diff --git a/README.md b/README.md index 0ace488f..cbc54866 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Terraform PHPIPAM provider - version 1.2.7 +# Terraform PHPIPAM provider - version 1.2.8 # ATTENTION!!! This repository is based on the original work of github user paybyphone. However, the version of the provider in this repo is updated and revised to support working with Terraform 12.x+ @@ -33,7 +33,7 @@ into your config. Check the [releases page][6] of this repo to get releases for Linux, OS X, and Windows. [5]: https://www.terraform.io/docs/plugins/basics.html -[6]: https://github.com/lord-kyron/terraform-provider-phpipam-0.3.1/releases +[6]: https://github.com/lord-kyron/terraform-provider-phpipam/releases Examle for CentOS 7: Build from repo: @@ -1271,4 +1271,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ``` -# terraform-provider-phpipam-1.2.7 +# terraform-provider-phpipam-1.2.8 diff --git a/docs/index.md b/docs/index.md index 0ace488f..cbc54866 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,4 +1,4 @@ -# Terraform PHPIPAM provider - version 1.2.7 +# Terraform PHPIPAM provider - version 1.2.8 # ATTENTION!!! This repository is based on the original work of github user paybyphone. However, the version of the provider in this repo is updated and revised to support working with Terraform 12.x+ @@ -33,7 +33,7 @@ into your config. Check the [releases page][6] of this repo to get releases for Linux, OS X, and Windows. [5]: https://www.terraform.io/docs/plugins/basics.html -[6]: https://github.com/lord-kyron/terraform-provider-phpipam-0.3.1/releases +[6]: https://github.com/lord-kyron/terraform-provider-phpipam/releases Examle for CentOS 7: Build from repo: @@ -1271,4 +1271,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ``` -# terraform-provider-phpipam-1.2.7 +# terraform-provider-phpipam-1.2.8 diff --git a/terraform-provider-phpipam b/terraform-provider-phpipam deleted file mode 100755 index d4add18c..00000000 Binary files a/terraform-provider-phpipam and /dev/null differ diff --git a/vendor/github.com/hashicorp/terraform-json/Makefile b/vendor/github.com/hashicorp/terraform-json/Makefile index 2d3f9a16..bb93c7f9 100644 --- a/vendor/github.com/hashicorp/terraform-json/Makefile +++ b/vendor/github.com/hashicorp/terraform-json/Makefile @@ -1,11 +1,11 @@ GOTOOLS = \ - gotest.tools/gotestsum + gotest.tools/gotestsum@latest test: tools gotestsum --format=short-verbose $(TEST) $(TESTARGS) generate: - cd test-fixtures && make generate + cd testdata && make generate modules: go mod download && go mod verify @@ -15,6 +15,7 @@ test-circle: gotestsum --format=short-verbose --junitfile test-results/terraform-json/results.xml tools: - go install $(GOTOOLS) + @echo $(GOTOOLS) | xargs -t -n1 go install + go mod tidy .PHONY: test generate modules test-circle tools diff --git a/vendor/github.com/hashicorp/terraform-json/README.md b/vendor/github.com/hashicorp/terraform-json/README.md index 78fd3bfa..fea0ba26 100644 --- a/vendor/github.com/hashicorp/terraform-json/README.md +++ b/vendor/github.com/hashicorp/terraform-json/README.md @@ -1,6 +1,6 @@ # terraform-json -[![CircleCI](https://circleci.com/gh/hashicorp/terraform-json/tree/master.svg?style=svg)](https://circleci.com/gh/hashicorp/terraform-json/tree/master) +[![CircleCI](https://circleci.com/gh/hashicorp/terraform-json/tree/main.svg?style=svg)](https://circleci.com/gh/hashicorp/terraform-json/tree/main) [![GoDoc](https://godoc.org/github.com/hashicorp/terraform-json?status.svg)](https://godoc.org/github.com/hashicorp/terraform-json) This repository houses data types designed to help parse the data produced by diff --git a/vendor/github.com/hashicorp/terraform-json/cmd/round-trip-dumper/README.md b/vendor/github.com/hashicorp/terraform-json/cmd/round-trip-dumper/README.md new file mode 100644 index 00000000..50f33f34 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/cmd/round-trip-dumper/README.md @@ -0,0 +1,13 @@ +# Round-Trip Dumper/Diagnostic Tool + +This directory contains a simple tool that will load a plan JSON file, and then +immediately dump it back out to stdout. It's helpful when troubleshooting large +parsing errors, which should (hopefully) be rare. + +`go build ./` in this directory to build the binary. `go run` also works if you +don't need the binary permanently. + +## Diffing + +The `-diff` flag will automatically diff the result for you. `colordiff` is used +if it's present, otherwise regular `diff` is used. diff --git a/vendor/github.com/hashicorp/terraform-json/cmd/round-trip-dumper/main.go b/vendor/github.com/hashicorp/terraform-json/cmd/round-trip-dumper/main.go new file mode 100644 index 00000000..b07e4048 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/cmd/round-trip-dumper/main.go @@ -0,0 +1,81 @@ +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "os" + "os/exec" + + tfjson "github.com/hashicorp/terraform-json" +) + +var ( + diff = flag.Bool("diff", false, "diff output instead of writing") + schema = flag.Bool("schema", false, "input is a schema, not a plan") +) + +func main() { + flag.Parse() + + if flag.NArg() < 1 { + fmt.Fprintf(os.Stderr, "usage: %s FILE\n\n", os.Args[0]) + os.Exit(1) + } + + path := flag.Arg(0) + + f, err := os.Open(path) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + defer f.Close() + + var parsed interface{} + if *schema { + parsed = &tfjson.ProviderSchemas{} + } else { + parsed = &tfjson.Plan{} + } + + dec := json.NewDecoder(f) + dec.DisallowUnknownFields() + if err = dec.Decode(parsed); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + out, err := json.MarshalIndent(parsed, "", " ") + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + out = append(out, byte('\n')) + + if *diff { + var diffCmd string + if _, err := exec.LookPath("colordiff"); err == nil { + diffCmd = "colordiff" + } else { + diffCmd = "diff" + } + + cmd := exec.Command(diffCmd, "-urN", path, "-") + cmd.Stdin = bytes.NewBuffer(out) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + if err.(*exec.ExitError).ProcessState.ExitCode() > 1 { + os.Exit(1) + } + } else { + fmt.Fprintln(os.Stderr, "[no diff]") + } + } else { + os.Stdout.Write(out) + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/config.go b/vendor/github.com/hashicorp/terraform-json/config.go index 3b8be511..e093cfa8 100644 --- a/vendor/github.com/hashicorp/terraform-json/config.go +++ b/vendor/github.com/hashicorp/terraform-json/config.go @@ -147,6 +147,9 @@ type ConfigVariable struct { // The defined text description of the variable. Description string `json:"description,omitempty"` + + // Whether the variable is marked as sensitive + Sensitive bool `json:"sensitive,omitempty"` } // ConfigProvisioner describes a provisioner declared in a resource @@ -181,4 +184,8 @@ type ModuleCall struct { // The version constraint for modules that come from the registry. VersionConstraint string `json:"version_constraint,omitempty"` + + // The explicit resource dependencies for the "depends_on" value. + // As it must be a slice of references, Expression is not used. + DependsOn []string `json:"depends_on,omitempty"` } diff --git a/vendor/github.com/hashicorp/terraform-json/config_test.go b/vendor/github.com/hashicorp/terraform-json/config_test.go new file mode 100644 index 00000000..1cd17fa2 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/config_test.go @@ -0,0 +1,24 @@ +package tfjson + +import ( + "encoding/json" + "os" + "testing" +) + +func TestConfigValidate(t *testing.T) { + f, err := os.Open("testdata/basic/plan.json") + if err != nil { + t.Fatal(err) + } + defer f.Close() + + var plan *Plan + if err := json.NewDecoder(f).Decode(&plan); err != nil { + t.Fatal(err) + } + + if err := plan.Config.Validate(); err != nil { + t.Fatal(err) + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/expression_test.go b/vendor/github.com/hashicorp/terraform-json/expression_test.go new file mode 100644 index 00000000..aea1eeaf --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/expression_test.go @@ -0,0 +1,132 @@ +package tfjson + +import ( + "encoding/json" + "reflect" + "testing" + + "github.com/davecgh/go-spew/spew" +) + +func TestUnmarshalExpressions(t *testing.T) { + cases := []struct { + name string + in string + expected *ConfigResource + }{ + { + name: "basic", + in: ` +{ + "address": "aws_instance.foo", + "mode": "managed", + "type": "aws_instance", + "name": "foo", + "provider_config_key": "aws", + "expressions": { + "ami": { + "constant_value": "ami-foobar" + }, + "ebs_block_device": [ + { + "device_name": { + "references": [ + "var.foo" + ] + } + } + ], + "instance_type": { + "constant_value": "t2.micro" + } + }, + "schema_version": 1 +} +`, + expected: &ConfigResource{ + Address: "aws_instance.foo", + Mode: ManagedResourceMode, + Type: "aws_instance", + Name: "foo", + ProviderConfigKey: "aws", + Expressions: map[string]*Expression{ + "ami": { + ExpressionData: &ExpressionData{ + ConstantValue: "ami-foobar", + }, + }, + "ebs_block_device": { + ExpressionData: &ExpressionData{ + NestedBlocks: []map[string]*Expression{ + { + "device_name": { + ExpressionData: &ExpressionData{ + ConstantValue: UnknownConstantValue, + References: []string{"var.foo"}, + }, + }, + }, + }, + }, + }, + "instance_type": { + ExpressionData: &ExpressionData{ + ConstantValue: "t2.micro", + }, + }, + }, + SchemaVersion: 1, + }, + }, + { + name: "explicit null in contstant value", + in: ` +{ + "address": "null_resource.foo", + "mode": "managed", + "type": "null_resource", + "name": "foo", + "provider_config_key": "null", + "expressions": { + "triggers": { + "constant_value": { + "foo": null + } + } + }, + "schema_version": 0 +} +`, + expected: &ConfigResource{ + Address: "null_resource.foo", + Mode: ManagedResourceMode, + Type: "null_resource", + Name: "foo", + ProviderConfigKey: "null", + Expressions: map[string]*Expression{ + "triggers": { + ExpressionData: &ExpressionData{ + ConstantValue: map[string]interface{}{ + "foo": nil, + }, + }, + }, + }, + SchemaVersion: 0, + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + var actual *ConfigResource + if err := json.Unmarshal([]byte(tc.in), &actual); err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(tc.expected, actual) { + t.Fatalf("expected:\n\n%s\n\ngot:\n\n%s\n\n", spew.Sdump(tc.expected), spew.Sdump(actual)) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/go.mod b/vendor/github.com/hashicorp/terraform-json/go.mod index 0e7ab50f..785dea4e 100644 --- a/vendor/github.com/hashicorp/terraform-json/go.mod +++ b/vendor/github.com/hashicorp/terraform-json/go.mod @@ -4,5 +4,8 @@ go 1.13 require ( github.com/davecgh/go-spew v1.1.1 + github.com/google/go-cmp v0.3.1 + github.com/mitchellh/copystructure v1.2.0 + github.com/sebdah/goldie v1.0.0 github.com/zclconf/go-cty v1.2.1 ) diff --git a/vendor/github.com/hashicorp/terraform-json/go.sum b/vendor/github.com/hashicorp/terraform-json/go.sum index 1bb69979..ae402451 100644 --- a/vendor/github.com/hashicorp/terraform-json/go.sum +++ b/vendor/github.com/hashicorp/terraform-json/go.sum @@ -1,4 +1,5 @@ github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -8,6 +9,17 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sebdah/goldie v1.0.0 h1:9GNhIat69MSlz/ndaBg48vl9dF5fI+NBB6kfOxgfkMc= +github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/zclconf/go-cty v1.2.1 h1:vGMsygfmeCl4Xb6OA5U5XVAaQZ69FvoG7X2jUtQujb8= github.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= diff --git a/vendor/github.com/hashicorp/terraform-json/parse_test.go b/vendor/github.com/hashicorp/terraform-json/parse_test.go new file mode 100644 index 00000000..f8751228 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/parse_test.go @@ -0,0 +1,103 @@ +package tfjson + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "path/filepath" + "reflect" + "strings" + "testing" +) + +const testFixtureDir = "testdata" +const testGoldenPlanFileName = "plan.json" +const testGoldenSchemasFileName = "schemas.json" + +func testParse(t *testing.T, filename string, typ reflect.Type) { + entries, err := ioutil.ReadDir(testFixtureDir) + if err != nil { + t.Fatalf("err: %s", err) + } + + for _, e := range entries { + if !e.IsDir() { + continue + } + + t.Run(e.Name(), func(t *testing.T) { + expected, err := ioutil.ReadFile(filepath.Join(testFixtureDir, e.Name(), filename)) + if err != nil { + t.Fatal(err) + } + + parsed := reflect.New(typ).Interface() + dec := json.NewDecoder(bytes.NewBuffer(expected)) + dec.DisallowUnknownFields() + if err = dec.Decode(parsed); err != nil { + t.Fatal(err) + } + + actual, err := json.Marshal(parsed) + if err != nil { + t.Fatal(err) + } + + // Add a newline at the end + actual = append(actual, byte('\n')) + + if err := testDiff(actual, expected); err != nil { + t.Fatal(err) + } + }) + } +} + +func TestParsePlan(t *testing.T) { + testParse(t, testGoldenPlanFileName, reflect.TypeOf(Plan{})) +} + +func TestParseSchemas(t *testing.T) { + testParse(t, testGoldenSchemasFileName, reflect.TypeOf(ProviderSchemas{})) +} + +func testDiff(out, gld []byte) error { + var b strings.Builder // holding long error message + + // compare lengths + if len(out) != len(gld) { + fmt.Fprintf(&b, "\nlength changed: len(output) = %d, len(golden) = %d", len(out), len(gld)) + } + + // compare contents + line := 1 + offs := 1 + for i := 0; i < len(out) && i < len(gld); i++ { + ch := out[i] + if ch != gld[i] { + fmt.Fprintf(&b, "\noutput:%d:%d: %s", line, i-offs+1, lineAt(out, offs)) + fmt.Fprintf(&b, "\ngolden:%d:%d: %s", line, i-offs+1, lineAt(gld, offs)) + fmt.Fprintf(&b, "\n\n") + break + } + if ch == '\n' { + line++ + offs = i + 1 + } + } + + if b.Len() > 0 { + return errors.New(b.String()) + } + return nil +} + +func lineAt(text []byte, offs int) []byte { + i := offs + for i < len(text) && text[i] != '\n' { + i++ + } + return text[offs:i] +} diff --git a/vendor/github.com/hashicorp/terraform-json/plan.go b/vendor/github.com/hashicorp/terraform-json/plan.go index 2a8ebd21..7e44c5c4 100644 --- a/vendor/github.com/hashicorp/terraform-json/plan.go +++ b/vendor/github.com/hashicorp/terraform-json/plan.go @@ -150,6 +150,14 @@ type Change struct { // If the value cannot be found in this map, then its value should // be available within After, so long as the operation supports it. AfterUnknown interface{} `json:"after_unknown,omitempty"` + + // BeforeSensitive and AfterSensitive are object values with similar + // structure to Before and After, but with all sensitive leaf values + // replaced with true, and all non-sensitive leaf values omitted. These + // objects should be combined with Before and After to prevent accidental + // display of sensitive values in user interfaces. + BeforeSensitive interface{} `json:"before_sensitive,omitempty"` + AfterSensitive interface{} `json:"after_sensitive,omitempty"` } // PlanVariable is a top-level variable in the Terraform plan. diff --git a/vendor/github.com/hashicorp/terraform-json/plan_test.go b/vendor/github.com/hashicorp/terraform-json/plan_test.go new file mode 100644 index 00000000..003e9661 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/plan_test.go @@ -0,0 +1,64 @@ +package tfjson + +import ( + "encoding/json" + "os" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestPlanValidate(t *testing.T) { + f, err := os.Open("testdata/basic/plan.json") + if err != nil { + t.Fatal(err) + } + defer f.Close() + + var plan *Plan + if err := json.NewDecoder(f).Decode(&plan); err != nil { + t.Fatal(err) + } + + if err := plan.Validate(); err != nil { + t.Fatal(err) + } +} + +func TestPlan_015(t *testing.T) { + f, err := os.Open("testdata/basic/plan-0.15.json") + if err != nil { + t.Fatal(err) + } + defer f.Close() + + var plan *Plan + if err := json.NewDecoder(f).Decode(&plan); err != nil { + t.Fatal(err) + } + + if err := plan.Validate(); err != nil { + t.Fatal(err) + } + + expectedChange := &Change{ + Actions: Actions{"create"}, + After: map[string]interface{}{"ami": "boop"}, + AfterUnknown: map[string]interface{}{"id": true}, + BeforeSensitive: false, + AfterSensitive: map[string]interface{}{"ami": true}, + } + if diff := cmp.Diff(expectedChange, plan.ResourceChanges[0].Change); diff != "" { + t.Fatalf("unexpected change: %s", diff) + } + + expectedVariable := map[string]*ConfigVariable{ + "test_var": { + Default: "boop", + Sensitive: true, + }, + } + if diff := cmp.Diff(expectedVariable, plan.Config.RootModule.Variables); diff != "" { + t.Fatalf("unexpected variables: %s", diff) + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/sanitize/copy.go b/vendor/github.com/hashicorp/terraform-json/sanitize/copy.go new file mode 100644 index 00000000..27991fde --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/sanitize/copy.go @@ -0,0 +1,74 @@ +package sanitize + +import ( + "reflect" + + tfjson "github.com/hashicorp/terraform-json" + "github.com/mitchellh/copystructure" +) + +// copyStructureCopy is an internal function that wraps copystructure.Copy with +// a shallow copier for unknown values. +// +// Performing the shallow copy of the unknown values is important +// here, as unknown values are parsed in with the main terraform-json +// package as singletons, and must continue to be comparable. +func copyStructureCopy(v interface{}) (interface{}, error) { + c := ©structure.Config{ + ShallowCopiers: map[reflect.Type]struct{}{ + reflect.TypeOf(tfjson.UnknownConstantValue): struct{}{}, + }, + } + + return c.Copy(v) +} + +// copyChange copies a Change value and returns the copy. +func copyChange(old *tfjson.Change) (*tfjson.Change, error) { + c, err := copyStructureCopy(old) + if err != nil { + return nil, err + } + + return c.(*tfjson.Change), nil +} + +// copyPlan copies a Plan value and returns the copy. +func copyPlan(old *tfjson.Plan) (*tfjson.Plan, error) { + c, err := copyStructureCopy(old) + if err != nil { + return nil, err + } + + return c.(*tfjson.Plan), nil +} + +// copyPlanVariable copies a PlanVariable value and returns the copy. +func copyPlanVariable(old *tfjson.PlanVariable) (*tfjson.PlanVariable, error) { + c, err := copyStructureCopy(old) + if err != nil { + return nil, err + } + + return c.(*tfjson.PlanVariable), nil +} + +// copyStateResource copies a StateResource value and returns the copy. +func copyStateResource(old *tfjson.StateResource) (*tfjson.StateResource, error) { + c, err := copyStructureCopy(old) + if err != nil { + return nil, err + } + + return c.(*tfjson.StateResource), nil +} + +// copyStateOutput copies a StateOutput value and returns the copy. +func copyStateOutputs(old map[string]*tfjson.StateOutput) (map[string]*tfjson.StateOutput, error) { + c, err := copystructure.Copy(old) + if err != nil { + return nil, err + } + + return c.(map[string]*tfjson.StateOutput), nil +} diff --git a/vendor/github.com/hashicorp/terraform-json/sanitize/copy_test.go b/vendor/github.com/hashicorp/terraform-json/sanitize/copy_test.go new file mode 100644 index 00000000..63bff6c5 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/sanitize/copy_test.go @@ -0,0 +1,19 @@ +package sanitize + +import ( + "testing" + + tfjson "github.com/hashicorp/terraform-json" +) + +func TestCopyStructureCopy(t *testing.T) { + in := tfjson.UnknownConstantValue + out, err := copyStructureCopy(in) + if err != nil { + t.Fatal(err) + } + + if in != out { + t.Fatal("did not shallow copy") + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_change.go b/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_change.go new file mode 100644 index 00000000..3396438f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_change.go @@ -0,0 +1,53 @@ +package sanitize + +import ( + tfjson "github.com/hashicorp/terraform-json" +) + +// SanitizeChange traverses a Change and replaces all values at +// the particular locations marked by BeforeSensitive AfterSensitive +// with the value supplied as replaceWith. +// +// A new change is issued. +func SanitizeChange(old *tfjson.Change, replaceWith interface{}) (*tfjson.Change, error) { + result, err := copyChange(old) + if err != nil { + return nil, err + } + + result.Before = sanitizeChangeValue(result.Before, result.BeforeSensitive, replaceWith) + result.After = sanitizeChangeValue(result.After, result.AfterSensitive, replaceWith) + + return result, nil +} + +func sanitizeChangeValue(old, sensitive, replaceWith interface{}) interface{} { + // Only expect deep types that we would normally see in JSON, so + // arrays and objects. + switch x := old.(type) { + case []interface{}: + if filterSlice, ok := sensitive.([]interface{}); ok { + for i := range filterSlice { + if i >= len(x) { + break + } + + x[i] = sanitizeChangeValue(x[i], filterSlice[i], replaceWith) + } + } + case map[string]interface{}: + if filterMap, ok := sensitive.(map[string]interface{}); ok { + for filterKey := range filterMap { + if value, ok := x[filterKey]; ok { + x[filterKey] = sanitizeChangeValue(value, filterMap[filterKey], replaceWith) + } + } + } + } + + if shouldFilter, ok := sensitive.(bool); ok && shouldFilter { + return replaceWith + } + + return old +} diff --git a/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_change_test.go b/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_change_test.go new file mode 100644 index 00000000..775a1f43 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_change_test.go @@ -0,0 +1,142 @@ +package sanitize + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + tfjson "github.com/hashicorp/terraform-json" +) + +type testChangeCase struct { + name string + old *tfjson.Change + expected *tfjson.Change +} + +func changeCases() []testChangeCase { + return []testChangeCase{ + { + name: "basic", + old: &tfjson.Change{ + Before: map[string]interface{}{ + "foo": map[string]interface{}{"a": "foo"}, + "bar": map[string]interface{}{"a": "foo"}, + "baz": map[string]interface{}{"a": "foo"}, + "qux": map[string]interface{}{ + "a": map[string]interface{}{ + "b": "foo", + }, + "c": "bar", + }, + "quxx": map[string]interface{}{ + "a": map[string]interface{}{ + "b": "foo", + }, + "c": "bar", + }, + }, + After: map[string]interface{}{ + "one": map[string]interface{}{"x": "one"}, + "two": map[string]interface{}{"x": "one"}, + "three": map[string]interface{}{"x": "one"}, + "four": map[string]interface{}{ + "x": map[string]interface{}{ + "y": "one", + }, + "z": "two", + }, + "five": map[string]interface{}{ + "x": map[string]interface{}{ + "y": "one", + }, + "z": "two", + }, + }, + BeforeSensitive: map[string]interface{}{ + "foo": map[string]interface{}{}, + "bar": true, + "baz": map[string]interface{}{"a": true}, + "qux": map[string]interface{}{}, + "quxx": map[string]interface{}{"c": true}, + }, + AfterSensitive: map[string]interface{}{ + "one": map[string]interface{}{}, + "two": true, + "three": map[string]interface{}{"x": true}, + "four": map[string]interface{}{}, + "five": map[string]interface{}{"z": true}, + }, + }, + expected: &tfjson.Change{ + Before: map[string]interface{}{ + "foo": map[string]interface{}{"a": "foo"}, + "bar": DefaultSensitiveValue, + "baz": map[string]interface{}{"a": DefaultSensitiveValue}, + "qux": map[string]interface{}{ + "a": map[string]interface{}{ + "b": "foo", + }, + "c": "bar", + }, + "quxx": map[string]interface{}{ + "a": map[string]interface{}{ + "b": "foo", + }, + "c": DefaultSensitiveValue, + }, + }, + After: map[string]interface{}{ + "one": map[string]interface{}{"x": "one"}, + "two": DefaultSensitiveValue, + "three": map[string]interface{}{"x": DefaultSensitiveValue}, + "four": map[string]interface{}{ + "x": map[string]interface{}{ + "y": "one", + }, + "z": "two", + }, + "five": map[string]interface{}{ + "x": map[string]interface{}{ + "y": "one", + }, + "z": DefaultSensitiveValue, + }, + }, + BeforeSensitive: map[string]interface{}{ + "foo": map[string]interface{}{}, + "bar": true, + "baz": map[string]interface{}{"a": true}, + "qux": map[string]interface{}{}, + "quxx": map[string]interface{}{"c": true}, + }, + AfterSensitive: map[string]interface{}{ + "one": map[string]interface{}{}, + "two": true, + "three": map[string]interface{}{"x": true}, + "four": map[string]interface{}{}, + "five": map[string]interface{}{"z": true}, + }, + }, + }, + } +} + +func TestSanitizeChange(t *testing.T) { + for i, tc := range changeCases() { + tc := tc + t.Run(tc.name, func(t *testing.T) { + actual, err := SanitizeChange(tc.old, DefaultSensitiveValue) + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(tc.expected, actual); diff != "" { + t.Errorf("SanitizeChange() mismatch (-expected +actual):\n%s", diff) + } + + if diff := cmp.Diff(changeCases()[i].old, tc.old); diff != "" { + t.Errorf("SanitizeChange() altered original (-expected +actual):\n%s", diff) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_plan.go b/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_plan.go new file mode 100644 index 00000000..6d009f08 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_plan.go @@ -0,0 +1,114 @@ +package sanitize + +import ( + "errors" + + tfjson "github.com/hashicorp/terraform-json" +) + +const DefaultSensitiveValue = "REDACTED_SENSITIVE" + +var NilPlanError = errors.New("nil plan supplied") + +// SanitizePlan sanitizes the entirety of a Plan, replacing sensitive +// values with the default value in DefaultSensitiveValue. +// +// See SanitizePlanWithValue for full detail on the where replacement +// takes place. +func SanitizePlan(old *tfjson.Plan) (*tfjson.Plan, error) { + return SanitizePlanWithValue(old, DefaultSensitiveValue) +} + +// SanitizePlanWithValue sanitizes the entirety of a Plan to the best +// of its ability, depending on the provided metadata on sensitive +// values. These are found in: +// +// * ResourceChanges: Sanitized based on BeforeSensitive and +// AfterSensitive fields. +// +// * Variables: Based on variable config data found in the root +// module of the Config. +// +// * PlannedValues: Sanitized based on the values found in +// AfterSensitive in ResourceChanges. Outputs are sanitized +// according to the appropriate sensitivity flags provided for the +// output. +// +// * PriorState: Sanitized based on the values found in +// BeforeSensitive in ResourceChanges. Outputs are sanitized according +// to the appropriate sensitivity flags provided for the output. +// +// * OutputChanges: Sanitized based on the values found in +// BeforeSensitive and AfterSensitive. This generally means that +// any sensitive output will have OutputChange fully obfuscated as +// the BeforeSensitive and AfterSensitive in outputs are opaquely the +// same. +// +// Sensitive values are replaced with the value supplied with +// replaceWith. A copy of the Plan is returned. +func SanitizePlanWithValue(old *tfjson.Plan, replaceWith interface{}) (*tfjson.Plan, error) { + if old == nil { + return nil, NilPlanError + } + + result, err := copyPlan(old) + if err != nil { + return nil, err + } + + // Sanitize ResourceChanges + for i := range result.ResourceChanges { + result.ResourceChanges[i].Change, err = SanitizeChange(result.ResourceChanges[i].Change, replaceWith) + if err != nil { + return nil, err + } + } + + // Sanitize Variables + result.Variables, err = SanitizePlanVariables(result.Variables, result.Config.RootModule.Variables, replaceWith) + if err != nil { + return nil, err + } + + // Sanitize PlannedValues + result.PlannedValues.RootModule, err = SanitizeStateModule( + result.PlannedValues.RootModule, + result.ResourceChanges, + SanitizeStateModuleChangeModeAfter, + replaceWith) + if err != nil { + return nil, err + } + + result.PlannedValues.Outputs, err = SanitizeStateOutputs(result.PlannedValues.Outputs, replaceWith) + if err != nil { + return nil, err + } + + // Sanitize PriorState + if result.PriorState != nil { + result.PriorState.Values.RootModule, err = SanitizeStateModule( + result.PriorState.Values.RootModule, + result.ResourceChanges, + SanitizeStateModuleChangeModeBefore, + replaceWith) + if err != nil { + return nil, err + } + + result.PriorState.Values.Outputs, err = SanitizeStateOutputs(result.PriorState.Values.Outputs, replaceWith) + if err != nil { + return nil, err + } + } + + // Sanitize OutputChanges + for k := range result.OutputChanges { + result.OutputChanges[k], err = SanitizeChange(result.OutputChanges[k], replaceWith) + if err != nil { + return nil, err + } + } + + return result, nil +} diff --git a/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_plan_test.go b/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_plan_test.go new file mode 100644 index 00000000..18ad5c34 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_plan_test.go @@ -0,0 +1,86 @@ +package sanitize + +import ( + "encoding/json" + "os" + "path/filepath" + "strings" + "testing" + + tfjson "github.com/hashicorp/terraform-json" + "github.com/sebdah/goldie" +) + +const testDataDir = "testdata" + +func TestSanitizePlanGolden(t *testing.T) { + cases, err := goldenCases() + if err != nil { + t.Fatal(err) + } + + for _, tc := range cases { + t.Run(tc.Name(), testSanitizePlanGoldenEntry(tc)) + } +} + +func testSanitizePlanGoldenEntry(c testGoldenCase) func(t *testing.T) { + return func(t *testing.T) { + p := new(tfjson.Plan) + err := json.Unmarshal(c.InputData, p) + if err != nil { + t.Fatal(err) + } + + p, err = SanitizePlan(p) + if err != nil { + t.Fatal(err) + } + + goldie.AssertJson(t, c.Name(), p) + } +} + +type testGoldenCase struct { + FileName string + InputData []byte +} + +func (c *testGoldenCase) Name() string { + return strings.TrimSuffix(c.FileName, filepath.Ext(c.FileName)) +} + +func goldenCases() ([]testGoldenCase, error) { + d, err := os.Open(testDataDir) + if err != nil { + return nil, err + } + + entries, err := d.ReadDir(0) + if err != nil { + return nil, err + } + + result := make([]testGoldenCase, 0) + for _, e := range entries { + if !e.Type().IsRegular() || !strings.HasSuffix(e.Name(), ".json") { + continue + } + + data, err := os.ReadFile(filepath.Join(testDataDir, e.Name())) + if err != nil { + return nil, err + } + + result = append(result, testGoldenCase{ + FileName: e.Name(), + InputData: data, + }) + } + + return result, err +} + +func init() { + goldie.FixtureDir = testDataDir +} diff --git a/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_plan_variables.go b/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_plan_variables.go new file mode 100644 index 00000000..7d86a124 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_plan_variables.go @@ -0,0 +1,46 @@ +package sanitize + +import ( + tfjson "github.com/hashicorp/terraform-json" +) + +// SanitizePlanVariables traverses a map of PlanVariable and replaces +// any sensitive values with the value supplied in replaceWith. +// configs should be the map of ConfigVariables from the root module +// (so Plan.Config.RootModule.Variables). +// +// A new copy of the PlanVariable map is returned. +func SanitizePlanVariables( + old map[string]*tfjson.PlanVariable, + configs map[string]*tfjson.ConfigVariable, + replaceWith interface{}, +) (map[string]*tfjson.PlanVariable, error) { + result := make(map[string]*tfjson.PlanVariable, len(old)) + for k := range old { + v, err := sanitizeVariable(old[k], configs[k], replaceWith) + if err != nil { + return nil, err + } + + result[k] = v + } + + return result, nil +} + +func sanitizeVariable( + old *tfjson.PlanVariable, + config *tfjson.ConfigVariable, + replaceWith interface{}, +) (*tfjson.PlanVariable, error) { + result, err := copyPlanVariable(old) + if err != nil { + return nil, err + } + + if config != nil && config.Sensitive { + result.Value = replaceWith + } + + return result, nil +} diff --git a/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_plan_variables_test.go b/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_plan_variables_test.go new file mode 100644 index 00000000..19164ce0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_plan_variables_test.go @@ -0,0 +1,67 @@ +package sanitize + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + tfjson "github.com/hashicorp/terraform-json" +) + +type testVariablesCase struct { + name string + old map[string]*tfjson.PlanVariable + configs map[string]*tfjson.ConfigVariable + expected map[string]*tfjson.PlanVariable +} + +func variablesCases() []testVariablesCase { + return []testVariablesCase{ + { + name: "basic", + old: map[string]*tfjson.PlanVariable{ + "foo": &tfjson.PlanVariable{ + Value: "test-foo", + }, + "bar": &tfjson.PlanVariable{ + Value: "test-bar", + }, + }, + configs: map[string]*tfjson.ConfigVariable{ + "foo": &tfjson.ConfigVariable{ + Sensitive: false, + }, + "bar": &tfjson.ConfigVariable{ + Sensitive: true, + }, + }, + expected: map[string]*tfjson.PlanVariable{ + "foo": &tfjson.PlanVariable{ + Value: "test-foo", + }, + "bar": &tfjson.PlanVariable{ + Value: DefaultSensitiveValue, + }, + }, + }, + } +} + +func TestSanitizePlanVariables(t *testing.T) { + for i, tc := range variablesCases() { + tc := tc + t.Run(tc.name, func(t *testing.T) { + actual, err := SanitizePlanVariables(tc.old, tc.configs, DefaultSensitiveValue) + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(tc.expected, actual); diff != "" { + t.Errorf("SanitizePlanVariables() mismatch (-expected +actual):\n%s", diff) + } + + if diff := cmp.Diff(variablesCases()[i].old, tc.old); diff != "" { + t.Errorf("SanitizePlanVariables() altered original (-expected +actual):\n%s", diff) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_state.go b/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_state.go new file mode 100644 index 00000000..3aba29bd --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_state.go @@ -0,0 +1,131 @@ +package sanitize + +import ( + "fmt" + + tfjson "github.com/hashicorp/terraform-json" +) + +type SanitizeStateModuleChangeMode string + +const ( + SanitizeStateModuleChangeModeBefore SanitizeStateModuleChangeMode = "before_sensitive" + SanitizeStateModuleChangeModeAfter SanitizeStateModuleChangeMode = "after_sensitive" +) + +// SanitizeStateModule traverses a StateModule, consulting the +// supplied ResourceChange set for resources to determine whether or +// not particular values should be obfuscated. +// +// Use mode to supply the SanitizeStateModuleChangeMode that +// represents what sensitive field should be consulted to determine +// whether or not the value should be obfuscated: +// +// * SanitizeStateModuleChangeModeBefore for before_sensitive +// * SanitizeStateModuleChangeModeAfter for after_sensitive +// +// Sensitive values are replaced with the supplied replaceWith value. +// A new state module tree is issued. +func SanitizeStateModule( + old *tfjson.StateModule, + resourceChanges []*tfjson.ResourceChange, + mode SanitizeStateModuleChangeMode, + replaceWith interface{}, +) (*tfjson.StateModule, error) { + result := &tfjson.StateModule{ + Resources: make([]*tfjson.StateResource, len(old.Resources)), + Address: old.Address, + ChildModules: make([]*tfjson.StateModule, len(old.ChildModules)), + } + + for i := range old.Resources { + var err error + result.Resources[i], err = sanitizeStateResource( + old.Resources[i], + findResourceChange(resourceChanges, old.Resources[i].Address), + mode, + replaceWith, + ) + if err != nil { + return nil, err + } + } + + for i := range old.ChildModules { + var err error + result.ChildModules[i], err = SanitizeStateModule( + old.ChildModules[i], + resourceChanges, + mode, + replaceWith, + ) + if err != nil { + return nil, err + } + } + + return result, nil +} + +func sanitizeStateResource( + old *tfjson.StateResource, + rc *tfjson.ResourceChange, + mode SanitizeStateModuleChangeMode, + replaceWith interface{}, +) (*tfjson.StateResource, error) { + result, err := copyStateResource(old) + if err != nil { + return nil, err + } + + if rc == nil { + return result, nil + } + + var sensitive interface{} + switch mode { + case SanitizeStateModuleChangeModeBefore: + sensitive = rc.Change.BeforeSensitive + + case SanitizeStateModuleChangeModeAfter: + sensitive = rc.Change.AfterSensitive + + default: + panic(fmt.Sprintf("invalid change mode %q", mode)) + } + + // We can re-use sanitizeChangeValue here to do the sanitization. + result.AttributeValues = sanitizeChangeValue(result.AttributeValues, sensitive, replaceWith).(map[string]interface{}) + return result, nil +} + +func findResourceChange(resourceChanges []*tfjson.ResourceChange, addr string) *tfjson.ResourceChange { + // Linear search here, unfortunately :P + for _, rc := range resourceChanges { + if rc.Address == addr { + return rc + } + } + + return nil +} + +// SanitizeStateOutputs scans the supplied map of StateOutputs and +// replaces any values of outputs marked as Sensitive with the value +// supplied in replaceWith. +// +// A new copy of StateOutputs is returned. +func SanitizeStateOutputs(old map[string]*tfjson.StateOutput, replaceWith interface{}) (map[string]*tfjson.StateOutput, error) { + result, err := copyStateOutputs(old) + if err != nil { + return nil, err + } + + for k := range result { + if result[k].Sensitive { + result[k].Value = replaceWith + } + } + + return result, nil +} diff --git a/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_state_test.go b/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_state_test.go new file mode 100644 index 00000000..f54d696c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/sanitize/sanitize_state_test.go @@ -0,0 +1,257 @@ +package sanitize + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + tfjson "github.com/hashicorp/terraform-json" +) + +type testStateCase struct { + name string + old *tfjson.StateModule + resourceChanges []*tfjson.ResourceChange + mode SanitizeStateModuleChangeMode + expected *tfjson.StateModule +} + +func stateCases() []testStateCase { + return []testStateCase{ + { + name: "before", + old: &tfjson.StateModule{ + Resources: []*tfjson.StateResource{ + { + Address: "null_resource.foo", + AttributeValues: map[string]interface{}{ + "foo": "bar", + "baz": "qux", + }, + }, + }, + Address: "", + ChildModules: []*tfjson.StateModule{ + &tfjson.StateModule{ + Resources: []*tfjson.StateResource{ + { + Address: "module.foo.null_resource.bar", + AttributeValues: map[string]interface{}{ + "a": "b", + "c": "d", + }, + }, + }, + Address: "module.foo", + ChildModules: []*tfjson.StateModule{}, + }, + }, + }, + resourceChanges: []*tfjson.ResourceChange{ + { + Address: "null_resource.foo", + Change: &tfjson.Change{ + BeforeSensitive: map[string]interface{}{ + "baz": true, + }, + AfterSensitive: map[string]interface{}{ + "foo": true, + }, + }, + }, + { + Address: "module.foo.null_resource.bar", + Change: &tfjson.Change{ + BeforeSensitive: map[string]interface{}{ + "a": true, + }, + AfterSensitive: map[string]interface{}{ + "c": true, + }, + }, + }, + }, + mode: SanitizeStateModuleChangeModeBefore, + expected: &tfjson.StateModule{ + Resources: []*tfjson.StateResource{ + { + Address: "null_resource.foo", + AttributeValues: map[string]interface{}{ + "foo": "bar", + "baz": DefaultSensitiveValue, + }, + }, + }, + Address: "", + ChildModules: []*tfjson.StateModule{ + &tfjson.StateModule{ + Resources: []*tfjson.StateResource{ + { + Address: "module.foo.null_resource.bar", + AttributeValues: map[string]interface{}{ + "a": DefaultSensitiveValue, + "c": "d", + }, + }, + }, + Address: "module.foo", + ChildModules: []*tfjson.StateModule{}, + }, + }, + }, + }, + { + name: "after", + old: &tfjson.StateModule{ + Resources: []*tfjson.StateResource{ + { + Address: "null_resource.foo", + AttributeValues: map[string]interface{}{ + "foo": "bar", + "baz": "qux", + }, + }, + }, + Address: "", + ChildModules: []*tfjson.StateModule{ + &tfjson.StateModule{ + Resources: []*tfjson.StateResource{ + { + Address: "module.foo.null_resource.bar", + AttributeValues: map[string]interface{}{ + "a": "b", + "c": "d", + }, + }, + }, + Address: "module.foo", + ChildModules: []*tfjson.StateModule{}, + }, + }, + }, + resourceChanges: []*tfjson.ResourceChange{ + { + Address: "null_resource.foo", + Change: &tfjson.Change{ + BeforeSensitive: map[string]interface{}{ + "baz": true, + }, + AfterSensitive: map[string]interface{}{ + "foo": true, + }, + }, + }, + { + Address: "module.foo.null_resource.bar", + Change: &tfjson.Change{ + BeforeSensitive: map[string]interface{}{ + "a": true, + }, + AfterSensitive: map[string]interface{}{ + "c": true, + }, + }, + }, + }, + mode: SanitizeStateModuleChangeModeAfter, + expected: &tfjson.StateModule{ + Resources: []*tfjson.StateResource{ + { + Address: "null_resource.foo", + AttributeValues: map[string]interface{}{ + "foo": DefaultSensitiveValue, + "baz": "qux", + }, + }, + }, + Address: "", + ChildModules: []*tfjson.StateModule{ + &tfjson.StateModule{ + Resources: []*tfjson.StateResource{ + { + Address: "module.foo.null_resource.bar", + AttributeValues: map[string]interface{}{ + "a": "b", + "c": DefaultSensitiveValue, + }, + }, + }, + Address: "module.foo", + ChildModules: []*tfjson.StateModule{}, + }, + }, + }, + }, + } +} + +func TestSanitizeStateModule(t *testing.T) { + for i, tc := range stateCases() { + tc := tc + t.Run(tc.name, func(t *testing.T) { + actual, err := SanitizeStateModule(tc.old, tc.resourceChanges, tc.mode, DefaultSensitiveValue) + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(tc.expected, actual); diff != "" { + t.Errorf("SanitizeStateModule() mismatch (-expected +actual):\n%s", diff) + } + + if diff := cmp.Diff(stateCases()[i].old, tc.old); diff != "" { + t.Errorf("SanitizeStateModule() altered original (-expected +actual):\n%s", diff) + } + }) + } +} + +type testOutputCase struct { + name string + old map[string]*tfjson.StateOutput + expected map[string]*tfjson.StateOutput +} + +func outputCases() []testOutputCase { + return []testOutputCase{ + { + name: "basic", + old: map[string]*tfjson.StateOutput{ + "foo": { + Value: "bar", + }, + "a": { + Value: "b", + Sensitive: true, + }, + }, + expected: map[string]*tfjson.StateOutput{ + "foo": { + Value: "bar", + }, + "a": { + Value: DefaultSensitiveValue, + Sensitive: true, + }, + }, + }, + } +} + +func TestSanitizeStateOutputs(t *testing.T) { + for i, tc := range outputCases() { + tc := tc + t.Run(tc.name, func(t *testing.T) { + actual, err := SanitizeStateOutputs(tc.old, DefaultSensitiveValue) + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(tc.expected, actual); diff != "" { + t.Errorf("SanitizeStateOutputs() mismatch (-expected +actual):\n%s", diff) + } + + if diff := cmp.Diff(outputCases()[i].old, tc.old); diff != "" { + t.Errorf("SanitizeStateOutputs() altered original (-expected +actual):\n%s", diff) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/sanitize/testdata/basic.golden b/vendor/github.com/hashicorp/terraform-json/sanitize/testdata/basic.golden new file mode 100644 index 00000000..f8209203 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/sanitize/testdata/basic.golden @@ -0,0 +1,371 @@ +{ + "format_version": "0.1", + "terraform_version": "0.15.1", + "variables": { + "foo": { + "value": "REDACTED_SENSITIVE" + }, + "toggle_sensitive": { + "value": true + } + }, + "planned_values": { + "outputs": { + "secret": { + "sensitive": true, + "value": "REDACTED_SENSITIVE" + } + }, + "root_module": { + "resources": [ + { + "address": "null_resource.bar", + "mode": "managed", + "type": "null_resource", + "name": "bar", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "4356817825751333663", + "triggers": "REDACTED_SENSITIVE" + } + }, + { + "address": "null_resource.baz", + "mode": "managed", + "type": "null_resource", + "name": "baz", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "2737458537890894185", + "triggers": { + "foo": "REDACTED_SENSITIVE" + } + } + }, + { + "address": "null_resource.foo", + "mode": "managed", + "type": "null_resource", + "name": "foo", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "7483449866618536328", + "triggers": { + "bar": "REDACTED_SENSITIVE", + "foo": "one" + } + } + }, + { + "address": "null_resource.qux", + "mode": "managed", + "type": "null_resource", + "name": "qux", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "389458145002419915", + "triggers": { + "foo": "REDACTED_SENSITIVE" + } + } + } + ] + } + }, + "resource_changes": [ + { + "address": "null_resource.bar", + "mode": "managed", + "type": "null_resource", + "name": "bar", + "provider_name": "registry.terraform.io/hashicorp/null", + "change": { + "actions": [ + "no-op" + ], + "before": { + "id": "4356817825751333663", + "triggers": "REDACTED_SENSITIVE" + }, + "after": { + "id": "4356817825751333663", + "triggers": "REDACTED_SENSITIVE" + }, + "after_unknown": {}, + "before_sensitive": { + "triggers": true + }, + "after_sensitive": { + "triggers": true + } + } + }, + { + "address": "null_resource.baz", + "mode": "managed", + "type": "null_resource", + "name": "baz", + "provider_name": "registry.terraform.io/hashicorp/null", + "change": { + "actions": [ + "no-op" + ], + "before": { + "id": "2737458537890894185", + "triggers": { + "foo": "REDACTED_SENSITIVE" + } + }, + "after": { + "id": "2737458537890894185", + "triggers": { + "foo": "REDACTED_SENSITIVE" + } + }, + "after_unknown": {}, + "before_sensitive": { + "triggers": { + "foo": true + } + }, + "after_sensitive": { + "triggers": { + "foo": true + } + } + } + }, + { + "address": "null_resource.foo", + "mode": "managed", + "type": "null_resource", + "name": "foo", + "provider_name": "registry.terraform.io/hashicorp/null", + "change": { + "actions": [ + "no-op" + ], + "before": { + "id": "7483449866618536328", + "triggers": { + "bar": "REDACTED_SENSITIVE", + "foo": "one" + } + }, + "after": { + "id": "7483449866618536328", + "triggers": { + "bar": "REDACTED_SENSITIVE", + "foo": "one" + } + }, + "after_unknown": {}, + "before_sensitive": { + "triggers": { + "bar": true + } + }, + "after_sensitive": { + "triggers": { + "bar": true + } + } + } + }, + { + "address": "null_resource.qux", + "mode": "managed", + "type": "null_resource", + "name": "qux", + "provider_name": "registry.terraform.io/hashicorp/null", + "change": { + "actions": [ + "update" + ], + "before": { + "id": "389458145002419915", + "triggers": { + "foo": "bar" + } + }, + "after": { + "id": "389458145002419915", + "triggers": { + "foo": "REDACTED_SENSITIVE" + } + }, + "after_unknown": {}, + "before_sensitive": { + "triggers": {} + }, + "after_sensitive": { + "triggers": { + "foo": true + } + } + } + } + ], + "output_changes": { + "secret": { + "actions": [ + "no-op" + ], + "before": "REDACTED_SENSITIVE", + "after": "REDACTED_SENSITIVE", + "after_unknown": false, + "before_sensitive": true, + "after_sensitive": true + } + }, + "prior_state": { + "format_version": "0.1", + "terraform_version": "0.15.1", + "values": { + "outputs": { + "secret": { + "sensitive": true, + "value": "REDACTED_SENSITIVE" + } + }, + "root_module": { + "resources": [ + { + "address": "null_resource.bar", + "mode": "managed", + "type": "null_resource", + "name": "bar", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "4356817825751333663", + "triggers": "REDACTED_SENSITIVE" + } + }, + { + "address": "null_resource.baz", + "mode": "managed", + "type": "null_resource", + "name": "baz", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "2737458537890894185", + "triggers": { + "foo": "REDACTED_SENSITIVE" + } + } + }, + { + "address": "null_resource.foo", + "mode": "managed", + "type": "null_resource", + "name": "foo", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "7483449866618536328", + "triggers": { + "bar": "REDACTED_SENSITIVE", + "foo": "one" + } + } + }, + { + "address": "null_resource.qux", + "mode": "managed", + "type": "null_resource", + "name": "qux", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "389458145002419915", + "triggers": { + "foo": "bar" + } + } + } + ] + } + } + }, + "configuration": { + "root_module": { + "outputs": { + "secret": { + "sensitive": true, + "expression": { + "references": [ + "null_resource.foo" + ] + } + } + }, + "resources": [ + { + "address": "null_resource.bar", + "mode": "managed", + "type": "null_resource", + "name": "bar", + "provider_config_key": "null", + "expressions": { + "triggers": {} + }, + "schema_version": 0 + }, + { + "address": "null_resource.baz", + "mode": "managed", + "type": "null_resource", + "name": "baz", + "provider_config_key": "null", + "expressions": { + "triggers": { + "references": [ + "var.foo" + ] + } + }, + "schema_version": 0 + }, + { + "address": "null_resource.foo", + "mode": "managed", + "type": "null_resource", + "name": "foo", + "provider_config_key": "null", + "expressions": { + "triggers": {} + }, + "schema_version": 0 + }, + { + "address": "null_resource.qux", + "mode": "managed", + "type": "null_resource", + "name": "qux", + "provider_config_key": "null", + "expressions": { + "triggers": { + "references": [ + "var.toggle_sensitive" + ] + } + }, + "schema_version": 0 + } + ], + "variables": { + "foo": { + "default": "bar", + "sensitive": true + }, + "toggle_sensitive": {} + } + } + } +} \ No newline at end of file diff --git a/vendor/github.com/hashicorp/terraform-json/sanitize/testdata/basic.json b/vendor/github.com/hashicorp/terraform-json/sanitize/testdata/basic.json new file mode 100644 index 00000000..c51e26c3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/sanitize/testdata/basic.json @@ -0,0 +1,383 @@ +{ + "format_version": "0.1", + "terraform_version": "0.15.1", + "variables": { + "foo": { + "value": "bar" + }, + "toggle_sensitive": { + "value": true + } + }, + "planned_values": { + "outputs": { + "secret": { + "sensitive": true, + "value": "7483449866618536328" + } + }, + "root_module": { + "resources": [ + { + "address": "null_resource.bar", + "mode": "managed", + "type": "null_resource", + "name": "bar", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "4356817825751333663", + "triggers": { + "bar": "two", + "foo": "one" + } + } + }, + { + "address": "null_resource.baz", + "mode": "managed", + "type": "null_resource", + "name": "baz", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "2737458537890894185", + "triggers": { + "foo": "bar" + } + } + }, + { + "address": "null_resource.foo", + "mode": "managed", + "type": "null_resource", + "name": "foo", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "7483449866618536328", + "triggers": { + "bar": "two", + "foo": "one" + } + } + }, + { + "address": "null_resource.qux", + "mode": "managed", + "type": "null_resource", + "name": "qux", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "389458145002419915", + "triggers": { + "foo": "bar" + } + } + } + ] + } + }, + "resource_changes": [ + { + "address": "null_resource.bar", + "mode": "managed", + "type": "null_resource", + "name": "bar", + "provider_name": "registry.terraform.io/hashicorp/null", + "change": { + "actions": [ + "no-op" + ], + "before": { + "id": "4356817825751333663", + "triggers": { + "bar": "two", + "foo": "one" + } + }, + "after": { + "id": "4356817825751333663", + "triggers": { + "bar": "two", + "foo": "one" + } + }, + "after_unknown": {}, + "before_sensitive": { + "triggers": true + }, + "after_sensitive": { + "triggers": true + } + } + }, + { + "address": "null_resource.baz", + "mode": "managed", + "type": "null_resource", + "name": "baz", + "provider_name": "registry.terraform.io/hashicorp/null", + "change": { + "actions": [ + "no-op" + ], + "before": { + "id": "2737458537890894185", + "triggers": { + "foo": "bar" + } + }, + "after": { + "id": "2737458537890894185", + "triggers": { + "foo": "bar" + } + }, + "after_unknown": {}, + "before_sensitive": { + "triggers": { + "foo": true + } + }, + "after_sensitive": { + "triggers": { + "foo": true + } + } + } + }, + { + "address": "null_resource.foo", + "mode": "managed", + "type": "null_resource", + "name": "foo", + "provider_name": "registry.terraform.io/hashicorp/null", + "change": { + "actions": [ + "no-op" + ], + "before": { + "id": "7483449866618536328", + "triggers": { + "bar": "two", + "foo": "one" + } + }, + "after": { + "id": "7483449866618536328", + "triggers": { + "bar": "two", + "foo": "one" + } + }, + "after_unknown": {}, + "before_sensitive": { + "triggers": { + "bar": true + } + }, + "after_sensitive": { + "triggers": { + "bar": true + } + } + } + }, + { + "address": "null_resource.qux", + "mode": "managed", + "type": "null_resource", + "name": "qux", + "provider_name": "registry.terraform.io/hashicorp/null", + "change": { + "actions": [ + "update" + ], + "before": { + "id": "389458145002419915", + "triggers": { + "foo": "bar" + } + }, + "after": { + "id": "389458145002419915", + "triggers": { + "foo": "bar" + } + }, + "after_unknown": {}, + "before_sensitive": { + "triggers": {} + }, + "after_sensitive": { + "triggers": { + "foo": true + } + } + } + } + ], + "output_changes": { + "secret": { + "actions": [ + "no-op" + ], + "before": "7483449866618536328", + "after": "7483449866618536328", + "after_unknown": false, + "before_sensitive": true, + "after_sensitive": true + } + }, + "prior_state": { + "format_version": "0.1", + "terraform_version": "0.15.1", + "values": { + "outputs": { + "secret": { + "sensitive": true, + "value": "7483449866618536328" + } + }, + "root_module": { + "resources": [ + { + "address": "null_resource.bar", + "mode": "managed", + "type": "null_resource", + "name": "bar", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "4356817825751333663", + "triggers": { + "bar": "two", + "foo": "one" + } + } + }, + { + "address": "null_resource.baz", + "mode": "managed", + "type": "null_resource", + "name": "baz", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "2737458537890894185", + "triggers": { + "foo": "bar" + } + } + }, + { + "address": "null_resource.foo", + "mode": "managed", + "type": "null_resource", + "name": "foo", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "7483449866618536328", + "triggers": { + "bar": "two", + "foo": "one" + } + } + }, + { + "address": "null_resource.qux", + "mode": "managed", + "type": "null_resource", + "name": "qux", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "389458145002419915", + "triggers": { + "foo": "bar" + } + } + } + ] + } + } + }, + "configuration": { + "root_module": { + "outputs": { + "secret": { + "sensitive": true, + "expression": { + "references": [ + "null_resource.foo" + ] + } + } + }, + "resources": [ + { + "address": "null_resource.bar", + "mode": "managed", + "type": "null_resource", + "name": "bar", + "provider_config_key": "null", + "expressions": { + "triggers": {} + }, + "schema_version": 0 + }, + { + "address": "null_resource.baz", + "mode": "managed", + "type": "null_resource", + "name": "baz", + "provider_config_key": "null", + "expressions": { + "triggers": { + "references": [ + "var.foo" + ] + } + }, + "schema_version": 0 + }, + { + "address": "null_resource.foo", + "mode": "managed", + "type": "null_resource", + "name": "foo", + "provider_config_key": "null", + "expressions": { + "triggers": {} + }, + "schema_version": 0 + }, + { + "address": "null_resource.qux", + "mode": "managed", + "type": "null_resource", + "name": "qux", + "provider_config_key": "null", + "expressions": { + "triggers": { + "references": [ + "var.toggle_sensitive" + ] + } + }, + "schema_version": 0 + } + ], + "variables": { + "foo": { + "default": "bar", + "sensitive": true + }, + "toggle_sensitive": {} + } + } + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/schemas.go b/vendor/github.com/hashicorp/terraform-json/schemas.go index e025fbe0..494c359f 100644 --- a/vendor/github.com/hashicorp/terraform-json/schemas.go +++ b/vendor/github.com/hashicorp/terraform-json/schemas.go @@ -10,7 +10,7 @@ import ( // ProviderSchemasFormatVersion is the version of the JSON provider // schema format that is supported by this package. -const ProviderSchemasFormatVersion = "0.1" +const ProviderSchemasFormatVersion = "0.2" // ProviderSchemas represents the schemas of all providers and // resources in use by the configuration. @@ -38,8 +38,10 @@ func (p *ProviderSchemas) Validate() error { return errors.New("unexpected provider schema data, format version is missing") } - if ProviderSchemasFormatVersion != p.FormatVersion { - return fmt.Errorf("unsupported provider schema data format version: expected %q, got %q", PlanFormatVersion, p.FormatVersion) + oldVersion := "0.1" + if p.FormatVersion != ProviderSchemasFormatVersion && p.FormatVersion != oldVersion { + return fmt.Errorf("unsupported provider schema data format version: expected %q or %q, got %q", + PlanFormatVersion, oldVersion, p.FormatVersion) } return nil @@ -83,6 +85,18 @@ type Schema struct { Block *SchemaBlock `json:"block,omitempty"` } +// SchemaDescriptionKind describes the format type for a particular description's field. +type SchemaDescriptionKind string + +const ( + // SchemaDescriptionKindPlain indicates a string in plain text format. + SchemaDescriptionKindPlain SchemaDescriptionKind = "plain" + + // SchemaDescriptionKindMarkdown indicates a Markdown string and may need to be + // processed prior to presentation. + SchemaDescriptionKindMarkdown SchemaDescriptionKind = "markdown" +) + // SchemaBlock represents a nested block within a particular schema. type SchemaBlock struct { // The attributes defined at the particular level of this block. @@ -90,6 +104,14 @@ type SchemaBlock struct { // Any nested blocks within this particular block. NestedBlocks map[string]*SchemaBlockType `json:"block_types,omitempty"` + + // The description for this block and format of the description. If + // no kind is provided, it can be assumed to be plain text. + Description string `json:"description,omitempty"` + DescriptionKind SchemaDescriptionKind `json:"description_kind,omitempty"` + + // If true, this block is deprecated. + Deprecated bool `json:"deprecated,omitempty"` } // SchemaNestingMode is the nesting mode for a particular nested @@ -103,6 +125,20 @@ const ( // with a single-element constraint. SchemaNestingModeSingle SchemaNestingMode = "single" + // SchemaNestingModeGroup is similar to SchemaNestingModeSingle in that it + // calls for only a single instance of a given block type with no labels, + // but it additonally guarantees that its result will never be null, + // even if the block is absent, and instead the nested attributes + // and blocks will be treated as absent in that case. + // + // This is useful for the situation where a remote API has a feature that + // is always enabled but has a group of settings related to that feature + // that themselves have default values. By using SchemaNestingModeGroup + // instead of SchemaNestingModeSingle in that case, generated plans will + // show the block as present even when not present in configuration, + // thus allowing any default values within to be displayed to the user. + SchemaNestingModeGroup SchemaNestingMode = "group" + // SchemaNestingModeList denotes list block nesting mode, which // allows an ordered list of blocks where duplicates are allowed. SchemaNestingModeList SchemaNestingMode = "list" @@ -142,11 +178,21 @@ type SchemaBlockType struct { // SchemaAttribute describes an attribute within a schema block. type SchemaAttribute struct { - // The attribute type. + // The attribute type + // Either AttributeType or AttributeNestedType is set, never both. AttributeType cty.Type `json:"type,omitempty"` - // The description field for this attribute. - Description string `json:"description,omitempty"` + // Details about a nested attribute type + // Either AttributeType or AttributeNestedType is set, never both. + AttributeNestedType *SchemaNestedAttributeType `json:"nested_type,omitempty"` + + // The description field for this attribute. If no kind is + // provided, it can be assumed to be plain text. + Description string `json:"description,omitempty"` + DescriptionKind SchemaDescriptionKind `json:"description_kind,omitempty"` + + // If true, this attribute is deprecated. + Deprecated bool `json:"deprecated,omitempty"` // If true, this attribute is required - it has to be entered in // configuration. @@ -166,3 +212,23 @@ type SchemaAttribute struct { // treat these values with greater care than non-sensitive fields. Sensitive bool `json:"sensitive,omitempty"` } + +// SchemaNestedAttributeType describes a nested attribute +// which could also be just expressed simply as cty.Object(...), +// cty.List(cty.Object(...)) etc. but this allows tracking additional +// metadata which can help interpreting or validating the data. +type SchemaNestedAttributeType struct { + // A map of nested attributes + Attributes map[string]*SchemaAttribute `json:"attributes,omitempty"` + + // The nesting mode for this attribute. + NestingMode SchemaNestingMode `json:"nesting_mode,omitempty"` + + // The lower limit on number of items that can be declared + // of this attribute type (not applicable to single nesting mode). + MinItems uint64 `json:"min_items,omitempty"` + + // The upper limit on number of items that can be declared + // of this attribute type (not applicable to single nesting mode). + MaxItems uint64 `json:"max_items,omitempty"` +} diff --git a/vendor/github.com/hashicorp/terraform-json/schemas_test.go b/vendor/github.com/hashicorp/terraform-json/schemas_test.go new file mode 100644 index 00000000..f6b5c937 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/schemas_test.go @@ -0,0 +1,24 @@ +package tfjson + +import ( + "encoding/json" + "os" + "testing" +) + +func TestProviderSchemasValidate(t *testing.T) { + f, err := os.Open("testdata/basic/schemas.json") + if err != nil { + t.Fatal(err) + } + defer f.Close() + + var schemas *ProviderSchemas + if err := json.NewDecoder(f).Decode(&schemas); err != nil { + t.Fatal(err) + } + + if err := schemas.Validate(); err != nil { + t.Fatal(err) + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/state.go b/vendor/github.com/hashicorp/terraform-json/state.go index a320bc82..e1a9149c 100644 --- a/vendor/github.com/hashicorp/terraform-json/state.go +++ b/vendor/github.com/hashicorp/terraform-json/state.go @@ -1,6 +1,7 @@ package tfjson import ( + "bytes" "encoding/json" "errors" "fmt" @@ -12,6 +13,12 @@ const StateFormatVersion = "0.1" // State is the top-level representation of a Terraform state. type State struct { + // useJSONNumber opts into the behavior of calling + // json.Decoder.UseNumber prior to decoding the state, which turns + // numbers into json.Numbers instead of float64s. Set it using + // State.UseJSONNumber. + useJSONNumber bool + // The version of the state format. This should always match the // StateFormatVersion constant in this package, or else am // unmarshal will be unstable. @@ -24,6 +31,14 @@ type State struct { Values *StateValues `json:"values,omitempty"` } +// UseJSONNumber controls whether the State will be decoded using the +// json.Number behavior or the float64 behavior. When b is true, the State will +// represent numbers in StateOutputs as json.Numbers. When b is false, the +// State will represent numbers in StateOutputs as float64s. +func (s *State) UseJSONNumber(b bool) { + s.useJSONNumber = b +} + // Validate checks to ensure that the state is present, and the // version matches the version supported by this library. func (s *State) Validate() error { @@ -46,7 +61,11 @@ func (s *State) UnmarshalJSON(b []byte) error { type rawState State var state rawState - err := json.Unmarshal(b, &state) + dec := json.NewDecoder(bytes.NewReader(b)) + if s.useJSONNumber { + dec.UseNumber() + } + err := dec.Decode(&state) if err != nil { return err } diff --git a/vendor/github.com/hashicorp/terraform-json/state_test.go b/vendor/github.com/hashicorp/terraform-json/state_test.go new file mode 100644 index 00000000..9201c473 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/state_test.go @@ -0,0 +1,85 @@ +package tfjson + +import ( + "encoding/json" + "io/ioutil" + "os" + "testing" +) + +func TestStateValidate_raw(t *testing.T) { + f, err := os.Open("testdata/no_changes/state.json") + if err != nil { + t.Fatal(err) + } + defer f.Close() + + var state State + if err := json.NewDecoder(f).Decode(&state); err != nil { + t.Fatal(err) + } + + if err := state.Validate(); err != nil { + t.Fatal(err) + } +} + +func TestStateUnmarshal_valid(t *testing.T) { + f, err := os.Open("testdata/no_changes/state.json") + if err != nil { + t.Fatal(err) + } + defer f.Close() + + b, err := ioutil.ReadAll(f) + if err != nil { + t.Fatal(err) + } + + var state State + err = json.Unmarshal(b, &state) + if err != nil { + t.Fatal(err) + } +} + +func TestStateUnmarshal_internalState(t *testing.T) { + f, err := os.Open("testdata/no_changes/terraform.tfstate") + if err != nil { + t.Fatal(err) + } + defer f.Close() + + b, err := ioutil.ReadAll(f) + if err != nil { + t.Fatal(err) + } + + var state State + err = json.Unmarshal(b, &state) + if err == nil { + t.Fatal("expected unmarshal to fail") + } + got := err.Error() + expected := "unexpected state input, format version is missing" + if expected != got { + t.Fatalf("error mismatch.\nexpected: %q\ngot: %q\n", expected, got) + } +} + +func TestStateValidate_fromPlan(t *testing.T) { + f, err := os.Open("testdata/no_changes/plan.json") + if err != nil { + t.Fatal(err) + } + defer f.Close() + + var plan *Plan + if err := json.NewDecoder(f).Decode(&plan); err != nil { + t.Fatal(err) + } + + if err := plan.PriorState.Validate(); err != nil { + t.Fatal(err) + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/013_module_depends_on/main.tf b/vendor/github.com/hashicorp/terraform-json/testdata/013_module_depends_on/main.tf new file mode 100644 index 00000000..5142ecfb --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/013_module_depends_on/main.tf @@ -0,0 +1,9 @@ +module "foo" { + source = "vancluever/module/null" + + depends_on = [ + null_resource.bar + ] +} + +resource "null_resource" "bar" {} \ No newline at end of file diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/013_module_depends_on/plan.json b/vendor/github.com/hashicorp/terraform-json/testdata/013_module_depends_on/plan.json new file mode 100644 index 00000000..33a2e67b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/013_module_depends_on/plan.json @@ -0,0 +1 @@ +{"format_version":"0.1","terraform_version":"0.13.1","planned_values":{"root_module":{"resources":[{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_name":"registry.terraform.io/hashicorp/null","schema_version":0,"values":{"triggers":null}}],"child_modules":[{"resources":[{"address":"module.foo.data.null_data_source.data","mode":"data","type":"null_data_source","name":"data","provider_name":"registry.terraform.io/hashicorp/null","schema_version":0,"values":{"inputs":{"key":"foo"}}},{"address":"module.foo.null_resource.resource","mode":"managed","type":"null_resource","name":"resource","provider_name":"registry.terraform.io/hashicorp/null","schema_version":0,"values":{"triggers":{"number":"one"}}}],"address":"module.foo"}]}},"resource_changes":[{"address":"module.foo.data.null_data_source.data","module_address":"module.foo","mode":"data","type":"null_data_source","name":"data","provider_name":"registry.terraform.io/hashicorp/null","change":{"actions":["read"],"before":null,"after":{"inputs":{"key":"foo"}},"after_unknown":{"has_computed_default":true,"id":true,"inputs":{},"outputs":true,"random":true}}},{"address":"module.foo.null_resource.resource","module_address":"module.foo","mode":"managed","type":"null_resource","name":"resource","provider_name":"registry.terraform.io/hashicorp/null","change":{"actions":["create"],"before":null,"after":{"triggers":{"number":"one"}},"after_unknown":{"id":true,"triggers":{}}}},{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_name":"registry.terraform.io/hashicorp/null","change":{"actions":["create"],"before":null,"after":{"triggers":null},"after_unknown":{"id":true}}}],"configuration":{"root_module":{"resources":[{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_config_key":"null","schema_version":0}],"module_calls":{"foo":{"source":"vancluever/module/null","module":{"outputs":{"null_data_source_id":{"expression":{"references":["data.null_data_source.data"]},"description":"The `id` of the `null_data_source` data source in this module."},"null_resource_id":{"expression":{"references":["null_resource.resource"]},"description":"The `id` of the `null_resource` resource in this module."}},"resources":[{"address":"null_resource.resource","mode":"managed","type":"null_resource","name":"resource","provider_config_key":"foo:null","expressions":{"triggers":{"references":["var.trigger"]}},"schema_version":0},{"address":"data.null_data_source.data","mode":"data","type":"null_data_source","name":"data","provider_config_key":"foo:null","expressions":{"inputs":{"references":["var.input"]}},"schema_version":0}],"variables":{"input":{"default":"foo","description":"The input value for the `null_data_source` data source in this module."},"trigger":{"default":"one","description":"The trigger value for the `null_resource` resource in this module."}}},"depends_on":["null_resource.bar"]}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/013_module_depends_on/schemas.json b/vendor/github.com/hashicorp/terraform-json/testdata/013_module_depends_on/schemas.json new file mode 100644 index 00000000..666f5c7e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/013_module_depends_on/schemas.json @@ -0,0 +1 @@ +{"format_version":"0.1","provider_schemas":{"registry.terraform.io/hashicorp/null":{"provider":{"version":0,"block":{"description_kind":"plain"}},"resource_schemas":{"null_resource":{"version":0,"block":{"attributes":{"id":{"type":"string","description_kind":"plain","optional":true,"computed":true},"triggers":{"type":["map","string"],"description_kind":"plain","optional":true}},"description_kind":"plain"}}},"data_source_schemas":{"null_data_source":{"version":0,"block":{"attributes":{"has_computed_default":{"type":"string","description_kind":"plain","optional":true,"computed":true},"id":{"type":"string","description_kind":"plain","optional":true,"computed":true},"inputs":{"type":["map","string"],"description_kind":"plain","optional":true},"outputs":{"type":["map","string"],"description_kind":"plain","computed":true},"random":{"type":"string","description_kind":"plain","computed":true}},"description_kind":"plain"}}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/Makefile b/vendor/github.com/hashicorp/terraform-json/testdata/Makefile new file mode 100644 index 00000000..6814855c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/Makefile @@ -0,0 +1,28 @@ +AWS_ENV_VARS=AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN AWS_REGION +TERRAFORM_DOCKER_CMD=docker run $(foreach var,$(AWS_ENV_VARS),--env $(var)) --rm --workdir "$$(pwd)" --volume "$$(pwd)":"$$(pwd)" + +TERRAFORM_012_VERSION=0.12.11 +TERRAFORM012=$(TERRAFORM_DOCKER_CMD) docker.mirror.hashicorp.services/hashicorp/terraform:$(TERRAFORM_012_VERSION) + +TERRAFORM_013_VERSION=0.13.1 +TERRAFORM013=$(TERRAFORM_DOCKER_CMD) docker.mirror.hashicorp.services/hashicorp/terraform:$(TERRAFORM_013_VERSION) + +FIXTURES ?= $(shell find * -maxdepth 0 -type d -not -name "013*") +013FIXTURES ?= $(shell find * -maxdepth 0 -type d -name "013*") + +.PHONY: $(FIXTURES) +$(FIXTURES): + cd $@/ && $(TERRAFORM012) init + cd $@/ && $(TERRAFORM012) plan -out=plan.tfplan + cd $@/ && $(TERRAFORM012) show -json plan.tfplan > plan.json + cd $@/ && $(TERRAFORM012) providers schema -json > schemas.json + +.PHONY: $(013FIXTURES) +$(013FIXTURES): + cd $@/ && $(TERRAFORM013) init + cd $@/ && $(TERRAFORM013) plan -out=plan.tfplan + cd $@/ && $(TERRAFORM013) show -json plan.tfplan > plan.json + cd $@/ && $(TERRAFORM013) providers schema -json > schemas.json + +.PHONY: generate +generate: $(FIXTURES) $(013FIXTURES) diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/basic/foo/main.tf b/vendor/github.com/hashicorp/terraform-json/testdata/basic/foo/main.tf new file mode 100644 index 00000000..8c48a854 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/basic/foo/main.tf @@ -0,0 +1,25 @@ +variable "bar" { + type = "string" +} + +variable "one" { + type = "string" +} + +provider "null" { + alias = "aliased" +} + +resource "null_resource" "foo" { + triggers = { + foo = "bar" + } +} + +resource "null_resource" "aliased" { + provider = "null.aliased" +} + +output "foo" { + value = "bar" +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/basic/module.tf b/vendor/github.com/hashicorp/terraform-json/testdata/basic/module.tf new file mode 100644 index 00000000..236fdac6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/basic/module.tf @@ -0,0 +1,6 @@ +module "foo" { + source = "./foo" + + bar = "baz" + one = "two" +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/basic/outputs.tf b/vendor/github.com/hashicorp/terraform-json/testdata/basic/outputs.tf new file mode 100644 index 00000000..1e2a4531 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/basic/outputs.tf @@ -0,0 +1,52 @@ +output "foo" { + sensitive = true + value = "bar" +} + +output "string" { + value = "foo" +} + +output "list" { + value = [ + "foo", + "bar", + ] +} + +output "map" { + value = { + foo = "bar" + number = 42 + } +} + +output "referenced" { + value = null_resource.foo.id +} + +output "interpolated" { + value = "${null_resource.foo.id}" +} + +output "referenced_deep" { + value = { + foo = "bar" + number = 42 + map = { + bar = "baz" + id = null_resource.foo.id + } + } +} + +output "interpolated_deep" { + value = { + foo = "bar" + number = 42 + map = { + bar = "baz" + id = "${null_resource.foo.id}" + } + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/basic/plan-0.15.json b/vendor/github.com/hashicorp/terraform-json/testdata/basic/plan-0.15.json new file mode 100644 index 00000000..51105382 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/basic/plan-0.15.json @@ -0,0 +1,117 @@ +{ + "format_version": "0.1", + "variables": { + "test_var": { + "value": "boop" + } + }, + "planned_values": { + "outputs": { + "test": { + "sensitive": true, + "value": "boop" + } + }, + "root_module": { + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "ami": "boop" + } + } + ] + } + }, + "resource_changes": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "provider_name": "registry.terraform.io/hashicorp/test", + "name": "test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "ami": "boop" + }, + "after_unknown": { + "id": true + }, + "after_sensitive": { + "ami": true + }, + "before_sensitive": false + } + } + ], + "output_changes": { + "test": { + "actions": [ + "create" + ], + "before": null, + "after": "boop", + "after_unknown": false, + "before_sensitive": true, + "after_sensitive": true + } + }, + "prior_state": { + "format_version": "0.1", + "values": { + "outputs": { + "test": { + "sensitive": true, + "value": "boop" + } + }, + "root_module": {} + } + }, + "configuration": { + "root_module": { + "outputs": { + "test": { + "expression": { + "references": [ + "test_instance.test" + ] + }, + "sensitive": true + } + }, + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_config_key": "test", + "schema_version": 0, + "expressions": { + "ami": { + "references": [ + "var.test_var" + ] + } + } + } + ], + "variables": { + "test_var": { + "default": "boop", + "sensitive": true + } + } + } + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/basic/plan.json b/vendor/github.com/hashicorp/terraform-json/testdata/basic/plan.json new file mode 100644 index 00000000..5c4bc03d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/basic/plan.json @@ -0,0 +1 @@ +{"format_version":"0.1","terraform_version":"0.12.11","variables":{"foo":{"value":"bar"},"map":{"value":{"foo":"bar","number":42}},"number":{"value":42}},"planned_values":{"outputs":{"foo":{"sensitive":true,"value":"bar"},"interpolated":{"sensitive":false},"interpolated_deep":{"sensitive":false},"list":{"sensitive":false,"value":["foo","bar"]},"map":{"sensitive":false,"value":{"foo":"bar","number":42}},"referenced":{"sensitive":false},"referenced_deep":{"sensitive":false},"string":{"sensitive":false,"value":"foo"}},"root_module":{"resources":[{"address":"data.null_data_source.baz","mode":"data","type":"null_data_source","name":"baz","provider_name":"null","schema_version":0,"values":{"inputs":{}}},{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_name":"null","schema_version":0},{"address":"null_resource.baz[0]","mode":"managed","type":"null_resource","name":"baz","index":0,"provider_name":"null","schema_version":0},{"address":"null_resource.baz[1]","mode":"managed","type":"null_resource","name":"baz","index":1,"provider_name":"null","schema_version":0},{"address":"null_resource.baz[2]","mode":"managed","type":"null_resource","name":"baz","index":2,"provider_name":"null","schema_version":0},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","schema_version":0,"values":{"triggers":{"foo":"bar"}}}],"child_modules":[{"resources":[{"address":"module.foo.null_resource.aliased","mode":"managed","type":"null_resource","name":"aliased","provider_name":"null.aliased","schema_version":0,"values":{"triggers":null}},{"address":"module.foo.null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","schema_version":0,"values":{"triggers":{"foo":"bar"}}}],"address":"module.foo"}]}},"resource_changes":[{"address":"data.null_data_source.baz","mode":"data","type":"null_data_source","name":"baz","provider_name":"null","change":{"actions":["read"],"before":null,"after":{"inputs":{}},"after_unknown":{"has_computed_default":true,"id":true,"inputs":{"bar_id":true,"foo_id":true},"outputs":true,"random":true}}},{"address":"module.foo.null_resource.aliased","module_address":"module.foo","mode":"managed","type":"null_resource","name":"aliased","provider_name":"null.aliased","change":{"actions":["create"],"before":null,"after":{"triggers":null},"after_unknown":{"id":true}}},{"address":"module.foo.null_resource.foo","module_address":"module.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","change":{"actions":["create"],"before":null,"after":{"triggers":{"foo":"bar"}},"after_unknown":{"id":true,"triggers":{}}}},{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_name":"null","change":{"actions":["create"],"before":null,"after":{},"after_unknown":{"id":true,"triggers":true}}},{"address":"null_resource.baz[0]","mode":"managed","type":"null_resource","name":"baz","index":0,"provider_name":"null","change":{"actions":["create"],"before":null,"after":{},"after_unknown":{"id":true,"triggers":true}}},{"address":"null_resource.baz[1]","mode":"managed","type":"null_resource","name":"baz","index":1,"provider_name":"null","change":{"actions":["create"],"before":null,"after":{},"after_unknown":{"id":true,"triggers":true}}},{"address":"null_resource.baz[2]","mode":"managed","type":"null_resource","name":"baz","index":2,"provider_name":"null","change":{"actions":["create"],"before":null,"after":{},"after_unknown":{"id":true,"triggers":true}}},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","change":{"actions":["create"],"before":null,"after":{"triggers":{"foo":"bar"}},"after_unknown":{"id":true,"triggers":{}}}}],"output_changes":{"foo":{"actions":["create"],"before":null,"after":"bar","after_unknown":false},"interpolated":{"actions":["create"],"before":null,"after_unknown":true},"interpolated_deep":{"actions":["create"],"before":null,"after_unknown":true},"list":{"actions":["create"],"before":null,"after":["foo","bar"],"after_unknown":false},"map":{"actions":["create"],"before":null,"after":{"foo":"bar","number":42},"after_unknown":false},"referenced":{"actions":["create"],"before":null,"after_unknown":true},"referenced_deep":{"actions":["create"],"before":null,"after_unknown":true},"string":{"actions":["create"],"before":null,"after":"foo","after_unknown":false}},"prior_state":{"format_version":"0.1","terraform_version":"0.12.11","values":{"outputs":{"foo":{"sensitive":true,"value":"bar"},"interpolated_deep":{"sensitive":false,"value":{"foo":"bar","map":{"bar":"baz","id":null},"number":42}},"list":{"sensitive":false,"value":["foo","bar"]},"map":{"sensitive":false,"value":{"foo":"bar","number":42}},"referenced_deep":{"sensitive":false,"value":{"foo":"bar","map":{"bar":"baz","id":null},"number":42}},"string":{"sensitive":false,"value":"foo"}},"root_module":{}}},"configuration":{"provider_config":{"aws":{"name":"aws","expressions":{"region":{"constant_value":"us-west-2"}}},"aws.east":{"name":"aws","alias":"east","expressions":{"region":{"constant_value":"us-east-1"}}},"foo:null.aliased":{"name":"null","alias":"aliased","module_address":"foo"},"null":{"name":"null"}},"root_module":{"outputs":{"foo":{"sensitive":true,"expression":{"constant_value":"bar"}},"interpolated":{"expression":{"references":["null_resource.foo"]}},"interpolated_deep":{"expression":{"references":["null_resource.foo"]}},"list":{"expression":{"constant_value":["foo","bar"]}},"map":{"expression":{"constant_value":{"foo":"bar","number":42}}},"referenced":{"expression":{"references":["null_resource.foo"]}},"referenced_deep":{"expression":{"references":["null_resource.foo"]}},"string":{"expression":{"constant_value":"foo"}}},"resources":[{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_config_key":"null","expressions":{"triggers":{"references":["null_resource.foo"]}},"schema_version":0},{"address":"null_resource.baz","mode":"managed","type":"null_resource","name":"baz","provider_config_key":"null","expressions":{"triggers":{"references":["null_resource.foo"]}},"schema_version":0,"count_expression":{"constant_value":3}},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_config_key":"null","provisioners":[{"type":"local-exec","expressions":{"command":{"constant_value":"echo hello"}}}],"expressions":{"triggers":{"constant_value":{"foo":"bar"}}},"schema_version":0},{"address":"data.null_data_source.baz","mode":"data","type":"null_data_source","name":"baz","provider_config_key":"null","expressions":{"inputs":{"references":["null_resource.foo","null_resource.bar"]}},"schema_version":0}],"module_calls":{"foo":{"source":"./foo","expressions":{"bar":{"constant_value":"baz"},"one":{"constant_value":"two"}},"module":{"outputs":{"foo":{"expression":{"constant_value":"bar"}}},"resources":[{"address":"null_resource.aliased","mode":"managed","type":"null_resource","name":"aliased","provider_config_key":"foo:null.aliased","schema_version":0},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_config_key":"foo:null","expressions":{"triggers":{"constant_value":{"foo":"bar"}}},"schema_version":0}],"variables":{"bar":{},"one":{}}}}},"variables":{"foo":{"default":"bar","description":"foobar"},"map":{"default":{"foo":"bar","number":42}},"number":{"default":42}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/basic/providers.tf b/vendor/github.com/hashicorp/terraform-json/testdata/basic/providers.tf new file mode 100644 index 00000000..4d481205 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/basic/providers.tf @@ -0,0 +1,10 @@ +provider "null" {} + +provider "aws" { + region = "us-west-2" +} + +provider "aws" { + alias = "east" + region = "us-east-1" +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/basic/resources.tf b/vendor/github.com/hashicorp/terraform-json/testdata/basic/resources.tf new file mode 100644 index 00000000..1d4a8e8f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/basic/resources.tf @@ -0,0 +1,30 @@ +resource "null_resource" "foo" { + triggers = { + foo = "bar" + } + + provisioner "local-exec" { + command = "echo hello" + } +} + +resource "null_resource" "bar" { + triggers = { + foo_id = "${null_resource.foo.id}" + } +} + +data "null_data_source" "baz" { + inputs = { + foo_id = "${null_resource.foo.id}" + bar_id = "${null_resource.bar.id}" + } +} + +resource "null_resource" "baz" { + count = 3 + + triggers = { + foo_id = "${null_resource.foo.id}" + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/basic/schemas.json b/vendor/github.com/hashicorp/terraform-json/testdata/basic/schemas.json new file mode 100644 index 00000000..8a610e2e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/basic/schemas.json @@ -0,0 +1 @@ +{"format_version":"0.1","provider_schemas":{"aws":{"provider":{"version":0,"block":{"attributes":{"access_key":{"type":"string","description":"The access key for API operations. You can retrieve this\nfrom the 'Security \u0026 Credentials' section of the AWS console.","optional":true},"allowed_account_ids":{"type":["set","string"],"optional":true},"forbidden_account_ids":{"type":["set","string"],"optional":true},"insecure":{"type":"bool","description":"Explicitly allow the provider to perform \"insecure\" SSL requests. If omitted,default value is `false`","optional":true},"max_retries":{"type":"number","description":"The maximum number of times an AWS API request is\nbeing executed. If the API request still fails, an error is\nthrown.","optional":true},"profile":{"type":"string","description":"The profile for API operations. If not set, the default profile\ncreated with `aws configure` will be used.","optional":true},"region":{"type":"string","description":"The region where AWS operations will take place. Examples\nare us-east-1, us-west-2, etc.","required":true},"s3_force_path_style":{"type":"bool","description":"Set this to true to force the request to use path-style addressing,\ni.e., http://s3.amazonaws.com/BUCKET/KEY. By default, the S3 client will\nuse virtual hosted bucket addressing when possible\n(http://BUCKET.s3.amazonaws.com/KEY). Specific to the Amazon S3 service.","optional":true},"secret_key":{"type":"string","description":"The secret key for API operations. You can retrieve this\nfrom the 'Security \u0026 Credentials' section of the AWS console.","optional":true},"shared_credentials_file":{"type":"string","description":"The path to the shared credentials file. If not set\nthis defaults to ~/.aws/credentials.","optional":true},"skip_credentials_validation":{"type":"bool","description":"Skip the credentials validation via STS API. Used for AWS API implementations that do not have STS available/implemented.","optional":true},"skip_get_ec2_platforms":{"type":"bool","description":"Skip getting the supported EC2 platforms. Used by users that don't have ec2:DescribeAccountAttributes permissions.","optional":true},"skip_metadata_api_check":{"type":"bool","optional":true},"skip_region_validation":{"type":"bool","description":"Skip static validation of region name. Used by users of alternative AWS-like APIs or users w/ access to regions that are not public (yet).","optional":true},"skip_requesting_account_id":{"type":"bool","description":"Skip requesting the account ID. Used for AWS API implementations that do not have IAM/STS API and/or metadata API.","optional":true},"token":{"type":"string","description":"session token. A session token is only required if you are\nusing temporary security credentials.","optional":true}},"block_types":{"assume_role":{"nesting_mode":"list","block":{"attributes":{"duration_seconds":{"type":"number","description":"Seconds to restrict the assume role session duration.","optional":true},"external_id":{"type":"string","description":"Unique identifier that might be required for assuming a role in another account.","optional":true},"policy":{"type":"string","description":"IAM Policy JSON describing further restricting permissions for the IAM Role being assumed.","optional":true},"policy_arns":{"type":["set","string"],"description":"Amazon Resource Names (ARNs) of IAM Policies describing further restricting permissions for the IAM Role being assumed.","optional":true},"role_arn":{"type":"string","description":"Amazon Resource Name of an IAM Role to assume prior to making API calls.","optional":true},"session_name":{"type":"string","description":"Identifier for the assumed role session.","optional":true},"tags":{"type":["map","string"],"description":"Assume role session tags.","optional":true},"transitive_tag_keys":{"type":["set","string"],"description":"Assume role session tag keys to pass to any subsequent sessions.","optional":true}}},"max_items":1},"endpoints":{"nesting_mode":"set","block":{"attributes":{"accessanalyzer":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"acm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"acmpca":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"amplify":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"apigateway":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"applicationautoscaling":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"applicationinsights":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"appmesh":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"appstream":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"appsync":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"athena":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"autoscaling":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"autoscalingplans":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"backup":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"batch":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"budgets":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloud9":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudformation":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudfront":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudhsm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudsearch":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudtrail":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudwatch":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudwatchevents":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudwatchlogs":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codeartifact":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codebuild":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codecommit":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codedeploy":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codepipeline":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cognitoidentity":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cognitoidp":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"configservice":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cur":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dataexchange":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"datapipeline":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"datasync":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dax":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"devicefarm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"directconnect":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dlm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dms":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"docdb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ds":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dynamodb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ec2":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ecr":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ecs":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"efs":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"eks":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"elasticache":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"elasticbeanstalk":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"elastictranscoder":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"elb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"emr":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"es":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"firehose":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"fms":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"forecast":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"fsx":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"gamelift":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"glacier":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"globalaccelerator":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"glue":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"greengrass":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"guardduty":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"iam":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"imagebuilder":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"inspector":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"iot":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"iotanalytics":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"iotevents":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kafka":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kinesis":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kinesisanalytics":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kinesisanalyticsv2":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kinesisvideo":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kms":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"lakeformation":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"lambda":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"lexmodels":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"licensemanager":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"lightsail":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"macie":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"managedblockchain":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"marketplacecatalog":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediaconnect":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediaconvert":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"medialive":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediapackage":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediastore":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediastoredata":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mq":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"neptune":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"networkmanager":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"opsworks":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"organizations":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"outposts":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"personalize":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"pinpoint":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"pricing":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"qldb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"quicksight":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ram":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"rds":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"redshift":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"resourcegroups":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"resourcegroupstaggingapi":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"route53":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"route53domains":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"route53resolver":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"s3":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"s3control":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sagemaker":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sdb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"secretsmanager":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"securityhub":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"serverlessrepo":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"servicecatalog":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"servicediscovery":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"servicequotas":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ses":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"shield":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sns":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sqs":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ssm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"stepfunctions":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"storagegateway":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sts":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"swf":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"synthetics":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"transfer":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"waf":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"wafregional":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"wafv2":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"worklink":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"workmail":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"workspaces":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"xray":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true}}}},"ignore_tags":{"nesting_mode":"list","block":{"attributes":{"key_prefixes":{"type":["set","string"],"description":"Resource tag key prefixes to ignore across all resources.","optional":true},"keys":{"type":["set","string"],"description":"Resource tag keys to ignore across all resources.","optional":true}}},"max_items":1}}}},"resource_schemas":{"aws_accessanalyzer_analyzer":{"version":0,"block":{"attributes":{"analyzer_name":{"type":"string","required":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true}}}},"aws_acm_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_authority_arn":{"type":"string","optional":true},"certificate_body":{"type":"string","optional":true},"certificate_chain":{"type":"string","optional":true},"domain_name":{"type":"string","optional":true,"computed":true},"domain_validation_options":{"type":["set",["object",{"domain_name":"string","resource_record_name":"string","resource_record_type":"string","resource_record_value":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"status":{"type":"string","computed":true},"subject_alternative_names":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"validation_emails":{"type":["list","string"],"computed":true},"validation_method":{"type":"string","optional":true,"computed":true}},"block_types":{"options":{"nesting_mode":"list","block":{"attributes":{"certificate_transparency_logging_preference":{"type":"string","optional":true}}},"max_items":1}}}},"aws_acm_certificate_validation":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"validation_record_fqdns":{"type":["set","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_acmpca_certificate_authority":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate":{"type":"string","computed":true},"certificate_chain":{"type":"string","computed":true},"certificate_signing_request":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"not_after":{"type":"string","computed":true},"not_before":{"type":"string","computed":true},"permanent_deletion_time_in_days":{"type":"number","optional":true},"serial":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true}},"block_types":{"certificate_authority_configuration":{"nesting_mode":"list","block":{"attributes":{"key_algorithm":{"type":"string","required":true},"signing_algorithm":{"type":"string","required":true}},"block_types":{"subject":{"nesting_mode":"list","block":{"attributes":{"common_name":{"type":"string","optional":true},"country":{"type":"string","optional":true},"distinguished_name_qualifier":{"type":"string","optional":true},"generation_qualifier":{"type":"string","optional":true},"given_name":{"type":"string","optional":true},"initials":{"type":"string","optional":true},"locality":{"type":"string","optional":true},"organization":{"type":"string","optional":true},"organizational_unit":{"type":"string","optional":true},"pseudonym":{"type":"string","optional":true},"state":{"type":"string","optional":true},"surname":{"type":"string","optional":true},"title":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"revocation_configuration":{"nesting_mode":"list","block":{"block_types":{"crl_configuration":{"nesting_mode":"list","block":{"attributes":{"custom_cname":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"expiration_in_days":{"type":"number","required":true},"s3_bucket_name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_alb":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"arn_suffix":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"drop_invalid_header_fields":{"type":"bool","optional":true},"enable_cross_zone_load_balancing":{"type":"bool","optional":true},"enable_deletion_protection":{"type":"bool","optional":true},"enable_http2":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","optional":true},"internal":{"type":"bool","optional":true,"computed":true},"ip_address_type":{"type":"string","optional":true,"computed":true},"load_balancer_type":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"subnets":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"zone_id":{"type":"string","computed":true}},"block_types":{"access_logs":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"prefix":{"type":"string","optional":true}}},"max_items":1},"subnet_mapping":{"nesting_mode":"set","block":{"attributes":{"allocation_id":{"type":"string","optional":true},"private_ipv4_address":{"type":"string","optional":true},"subnet_id":{"type":"string","required":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_alb_listener":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"load_balancer_arn":{"type":"string","required":true},"port":{"type":"number","required":true},"protocol":{"type":"string","optional":true},"ssl_policy":{"type":"string","optional":true,"computed":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"attributes":{"order":{"type":"number","optional":true,"computed":true},"target_group_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"authenticate_cognito":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"user_pool_arn":{"type":"string","required":true},"user_pool_client_id":{"type":"string","required":true},"user_pool_domain":{"type":"string","required":true}}},"max_items":1},"authenticate_oidc":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"authorization_endpoint":{"type":"string","required":true},"client_id":{"type":"string","required":true},"client_secret":{"type":"string","required":true,"sensitive":true},"issuer":{"type":"string","required":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"token_endpoint":{"type":"string","required":true},"user_info_endpoint":{"type":"string","required":true}}},"max_items":1},"fixed_response":{"nesting_mode":"list","block":{"attributes":{"content_type":{"type":"string","required":true},"message_body":{"type":"string","optional":true},"status_code":{"type":"string","optional":true,"computed":true}}},"max_items":1},"forward":{"nesting_mode":"list","block":{"block_types":{"stickiness":{"nesting_mode":"list","block":{"attributes":{"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1},"target_group":{"nesting_mode":"set","block":{"attributes":{"arn":{"type":"string","required":true},"weight":{"type":"number","optional":true}}},"min_items":2,"max_items":5}}},"max_items":1},"redirect":{"nesting_mode":"list","block":{"attributes":{"host":{"type":"string","optional":true},"path":{"type":"string","optional":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"query":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"read":{"type":"string","optional":true}}}}}}},"aws_alb_listener_certificate":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true}}}},"aws_alb_listener_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true},"priority":{"type":"number","optional":true,"computed":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"order":{"type":"number","optional":true,"computed":true},"target_group_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"authenticate_cognito":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"user_pool_arn":{"type":"string","required":true},"user_pool_client_id":{"type":"string","required":true},"user_pool_domain":{"type":"string","required":true}}},"max_items":1},"authenticate_oidc":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"authorization_endpoint":{"type":"string","required":true},"client_id":{"type":"string","required":true},"client_secret":{"type":"string","required":true,"sensitive":true},"issuer":{"type":"string","required":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"token_endpoint":{"type":"string","required":true},"user_info_endpoint":{"type":"string","required":true}}},"max_items":1},"fixed_response":{"nesting_mode":"list","block":{"attributes":{"content_type":{"type":"string","required":true},"message_body":{"type":"string","optional":true},"status_code":{"type":"string","optional":true,"computed":true}}},"max_items":1},"forward":{"nesting_mode":"list","block":{"block_types":{"stickiness":{"nesting_mode":"list","block":{"attributes":{"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1},"target_group":{"nesting_mode":"set","block":{"attributes":{"arn":{"type":"string","required":true},"weight":{"type":"number","optional":true}}},"min_items":2,"max_items":5}}},"max_items":1},"redirect":{"nesting_mode":"list","block":{"attributes":{"host":{"type":"string","optional":true},"path":{"type":"string","optional":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"query":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"condition":{"nesting_mode":"set","block":{"block_types":{"host_header":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"http_header":{"nesting_mode":"list","block":{"attributes":{"http_header_name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}},"max_items":1},"http_request_method":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"path_pattern":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"query_string":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"value":{"type":"string","required":true}}}},"source_ip":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1}}},"min_items":1}}}},"aws_alb_target_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"arn_suffix":{"type":"string","computed":true},"deregistration_delay":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"lambda_multi_value_headers_enabled":{"type":"bool","optional":true},"load_balancing_algorithm_type":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"port":{"type":"number","optional":true},"protocol":{"type":"string","optional":true},"proxy_protocol_v2":{"type":"bool","optional":true},"slow_start":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true},"target_type":{"type":"string","optional":true},"vpc_id":{"type":"string","optional":true}},"block_types":{"health_check":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"healthy_threshold":{"type":"number","optional":true},"interval":{"type":"number","optional":true},"matcher":{"type":"string","optional":true,"computed":true},"path":{"type":"string","optional":true,"computed":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"timeout":{"type":"number","optional":true,"computed":true},"unhealthy_threshold":{"type":"number","optional":true}}},"max_items":1},"stickiness":{"nesting_mode":"list","block":{"attributes":{"cookie_duration":{"type":"number","optional":true},"enabled":{"type":"bool","optional":true},"type":{"type":"string","required":true}}},"max_items":1}}}},"aws_alb_target_group_attachment":{"version":0,"block":{"attributes":{"availability_zone":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"target_group_arn":{"type":"string","required":true},"target_id":{"type":"string","required":true}}}},"aws_ami":{"version":0,"block":{"attributes":{"architecture":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"ena_support":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"image_location":{"type":"string","optional":true,"computed":true},"kernel_id":{"type":"string","optional":true},"manage_ebs_snapshots":{"type":"bool","computed":true},"name":{"type":"string","required":true},"ramdisk_id":{"type":"string","optional":true},"root_device_name":{"type":"string","optional":true},"root_snapshot_id":{"type":"string","computed":true},"sriov_net_support":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"virtualization_type":{"type":"string","optional":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"snapshot_id":{"type":"string","optional":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"virtual_name":{"type":"string","required":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_ami_copy":{"version":0,"block":{"attributes":{"architecture":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"ena_support":{"type":"bool","computed":true},"encrypted":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"image_location":{"type":"string","computed":true},"kernel_id":{"type":"string","computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"manage_ebs_snapshots":{"type":"bool","computed":true},"name":{"type":"string","required":true},"ramdisk_id":{"type":"string","computed":true},"root_device_name":{"type":"string","computed":true},"root_snapshot_id":{"type":"string","computed":true},"source_ami_id":{"type":"string","required":true},"source_ami_region":{"type":"string","required":true},"sriov_net_support":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"virtualization_type":{"type":"string","computed":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","computed":true},"device_name":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"iops":{"type":"number","computed":true},"snapshot_id":{"type":"string","computed":true},"volume_size":{"type":"number","computed":true},"volume_type":{"type":"string","computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","computed":true},"virtual_name":{"type":"string","computed":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_ami_from_instance":{"version":0,"block":{"attributes":{"architecture":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"ena_support":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"image_location":{"type":"string","computed":true},"kernel_id":{"type":"string","computed":true},"manage_ebs_snapshots":{"type":"bool","computed":true},"name":{"type":"string","required":true},"ramdisk_id":{"type":"string","computed":true},"root_device_name":{"type":"string","computed":true},"root_snapshot_id":{"type":"string","computed":true},"snapshot_without_reboot":{"type":"bool","optional":true},"source_instance_id":{"type":"string","required":true},"sriov_net_support":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"virtualization_type":{"type":"string","computed":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","computed":true},"device_name":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"iops":{"type":"number","computed":true},"snapshot_id":{"type":"string","computed":true},"volume_size":{"type":"number","computed":true},"volume_type":{"type":"string","computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","computed":true},"virtual_name":{"type":"string","computed":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_ami_launch_permission":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","required":true}}}},"aws_api_gateway_account":{"version":0,"block":{"attributes":{"cloudwatch_role_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"throttle_settings":{"type":["list",["object",{"burst_limit":"number","rate_limit":"number"}]],"computed":true}}}},"aws_api_gateway_api_key":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"value":{"type":"string","optional":true,"computed":true,"sensitive":true}}}},"aws_api_gateway_authorizer":{"version":0,"block":{"attributes":{"authorizer_credentials":{"type":"string","optional":true},"authorizer_result_ttl_in_seconds":{"type":"number","optional":true},"authorizer_uri":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"identity_source":{"type":"string","optional":true},"identity_validation_expression":{"type":"string","optional":true},"name":{"type":"string","required":true},"provider_arns":{"type":["set","string"],"optional":true},"rest_api_id":{"type":"string","required":true},"type":{"type":"string","optional":true}}}},"aws_api_gateway_base_path_mapping":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"base_path":{"type":"string","optional":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"stage_name":{"type":"string","optional":true}}}},"aws_api_gateway_client_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"expiration_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"pem_encoded_certificate":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_api_gateway_deployment":{"version":0,"block":{"attributes":{"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_url":{"type":"string","computed":true},"rest_api_id":{"type":"string","required":true},"stage_description":{"type":"string","optional":true},"stage_name":{"type":"string","optional":true},"triggers":{"type":["map","string"],"optional":true},"variables":{"type":["map","string"],"optional":true}}}},"aws_api_gateway_documentation_part":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"properties":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true}},"block_types":{"location":{"nesting_mode":"list","block":{"attributes":{"method":{"type":"string","optional":true},"name":{"type":"string","optional":true},"path":{"type":"string","optional":true},"status_code":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_api_gateway_documentation_version":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"rest_api_id":{"type":"string","required":true},"version":{"type":"string","required":true}}}},"aws_api_gateway_domain_name":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_arn":{"type":"string","optional":true},"certificate_body":{"type":"string","optional":true},"certificate_chain":{"type":"string","optional":true},"certificate_name":{"type":"string","optional":true},"certificate_private_key":{"type":"string","optional":true,"sensitive":true},"certificate_upload_date":{"type":"string","computed":true},"cloudfront_domain_name":{"type":"string","computed":true},"cloudfront_zone_id":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"regional_certificate_arn":{"type":"string","optional":true},"regional_certificate_name":{"type":"string","optional":true},"regional_domain_name":{"type":"string","computed":true},"regional_zone_id":{"type":"string","computed":true},"security_policy":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"endpoint_configuration":{"nesting_mode":"list","block":{"attributes":{"types":{"type":["list","string"],"required":true}}},"max_items":1}}}},"aws_api_gateway_gateway_response":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"response_parameters":{"type":["map","string"],"optional":true},"response_templates":{"type":["map","string"],"optional":true},"response_type":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"status_code":{"type":"string","optional":true}}}},"aws_api_gateway_integration":{"version":0,"block":{"attributes":{"cache_key_parameters":{"type":["set","string"],"optional":true},"cache_namespace":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","optional":true},"connection_type":{"type":"string","optional":true},"content_handling":{"type":"string","optional":true},"credentials":{"type":"string","optional":true},"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"integration_http_method":{"type":"string","optional":true},"passthrough_behavior":{"type":"string","optional":true,"computed":true},"request_parameters":{"type":["map","string"],"optional":true},"request_templates":{"type":["map","string"],"optional":true},"resource_id":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"timeout_milliseconds":{"type":"number","optional":true},"type":{"type":"string","required":true},"uri":{"type":"string","optional":true}}}},"aws_api_gateway_integration_response":{"version":0,"block":{"attributes":{"content_handling":{"type":"string","optional":true},"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"resource_id":{"type":"string","required":true},"response_parameters":{"type":["map","string"],"optional":true},"response_templates":{"type":["map","string"],"optional":true},"rest_api_id":{"type":"string","required":true},"selection_pattern":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}}},"aws_api_gateway_method":{"version":0,"block":{"attributes":{"api_key_required":{"type":"bool","optional":true},"authorization":{"type":"string","required":true},"authorization_scopes":{"type":["set","string"],"optional":true},"authorizer_id":{"type":"string","optional":true},"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"request_models":{"type":["map","string"],"optional":true},"request_parameters":{"type":["map","bool"],"optional":true},"request_validator_id":{"type":"string","optional":true},"resource_id":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true}}}},"aws_api_gateway_method_response":{"version":0,"block":{"attributes":{"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"resource_id":{"type":"string","required":true},"response_models":{"type":["map","string"],"optional":true},"response_parameters":{"type":["map","bool"],"optional":true},"rest_api_id":{"type":"string","required":true},"status_code":{"type":"string","required":true}}}},"aws_api_gateway_method_settings":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"method_path":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"stage_name":{"type":"string","required":true}},"block_types":{"settings":{"nesting_mode":"list","block":{"attributes":{"cache_data_encrypted":{"type":"bool","optional":true,"computed":true},"cache_ttl_in_seconds":{"type":"number","optional":true,"computed":true},"caching_enabled":{"type":"bool","optional":true,"computed":true},"data_trace_enabled":{"type":"bool","optional":true,"computed":true},"logging_level":{"type":"string","optional":true,"computed":true},"metrics_enabled":{"type":"bool","optional":true,"computed":true},"require_authorization_for_cache_control":{"type":"bool","optional":true,"computed":true},"throttling_burst_limit":{"type":"number","optional":true},"throttling_rate_limit":{"type":"number","optional":true},"unauthorized_cache_control_header_strategy":{"type":"string","optional":true,"computed":true}}},"min_items":1,"max_items":1}}}},"aws_api_gateway_model":{"version":0,"block":{"attributes":{"content_type":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"schema":{"type":"string","optional":true}}}},"aws_api_gateway_request_validator":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"validate_request_body":{"type":"bool","optional":true},"validate_request_parameters":{"type":"bool","optional":true}}}},"aws_api_gateway_resource":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"parent_id":{"type":"string","required":true},"path":{"type":"string","computed":true},"path_part":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true}}}},"aws_api_gateway_rest_api":{"version":0,"block":{"attributes":{"api_key_source":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"binary_media_types":{"type":["list","string"],"optional":true},"body":{"type":"string","optional":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"minimum_compression_size":{"type":"number","optional":true},"name":{"type":"string","required":true},"policy":{"type":"string","optional":true},"root_resource_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"endpoint_configuration":{"nesting_mode":"list","block":{"attributes":{"types":{"type":["list","string"],"required":true},"vpc_endpoint_ids":{"type":["set","string"],"optional":true}}},"max_items":1}}}},"aws_api_gateway_stage":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cache_cluster_enabled":{"type":"bool","optional":true},"cache_cluster_size":{"type":"string","optional":true},"client_certificate_id":{"type":"string","optional":true},"deployment_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"documentation_version":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_url":{"type":"string","computed":true},"rest_api_id":{"type":"string","required":true},"stage_name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"variables":{"type":["map","string"],"optional":true},"xray_tracing_enabled":{"type":"bool","optional":true}},"block_types":{"access_log_settings":{"nesting_mode":"list","block":{"attributes":{"destination_arn":{"type":"string","required":true},"format":{"type":"string","required":true}}},"max_items":1}}}},"aws_api_gateway_usage_plan":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"product_code":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"api_stages":{"nesting_mode":"list","block":{"attributes":{"api_id":{"type":"string","required":true},"stage":{"type":"string","required":true}}}},"quota_settings":{"nesting_mode":"list","block":{"attributes":{"limit":{"type":"number","required":true},"offset":{"type":"number","optional":true},"period":{"type":"string","required":true}}},"max_items":1},"throttle_settings":{"nesting_mode":"list","block":{"attributes":{"burst_limit":{"type":"number","optional":true},"rate_limit":{"type":"number","optional":true}}},"max_items":1}}}},"aws_api_gateway_usage_plan_key":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"key_type":{"type":"string","required":true},"name":{"type":"string","computed":true},"usage_plan_id":{"type":"string","required":true},"value":{"type":"string","computed":true}}}},"aws_api_gateway_vpc_link":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"target_arns":{"type":["list","string"],"required":true}}}},"aws_apigatewayv2_api":{"version":0,"block":{"attributes":{"api_endpoint":{"type":"string","computed":true},"api_key_selection_expression":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"credentials_arn":{"type":"string","optional":true},"description":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"protocol_type":{"type":"string","required":true},"route_key":{"type":"string","optional":true},"route_selection_expression":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"target":{"type":"string","optional":true},"version":{"type":"string","optional":true}},"block_types":{"cors_configuration":{"nesting_mode":"list","block":{"attributes":{"allow_credentials":{"type":"bool","optional":true},"allow_headers":{"type":["set","string"],"optional":true},"allow_methods":{"type":["set","string"],"optional":true},"allow_origins":{"type":["set","string"],"optional":true},"expose_headers":{"type":["set","string"],"optional":true},"max_age":{"type":"number","optional":true}}},"max_items":1}}}},"aws_apigatewayv2_api_mapping":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"api_mapping_key":{"type":"string","optional":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"stage":{"type":"string","required":true}}}},"aws_apigatewayv2_authorizer":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"authorizer_credentials_arn":{"type":"string","optional":true},"authorizer_type":{"type":"string","required":true},"authorizer_uri":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"identity_sources":{"type":["set","string"],"required":true},"name":{"type":"string","required":true}},"block_types":{"jwt_configuration":{"nesting_mode":"list","block":{"attributes":{"audience":{"type":["set","string"],"optional":true},"issuer":{"type":"string","optional":true}}},"max_items":1}}}},"aws_apigatewayv2_deployment":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"auto_deployed":{"type":"bool","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"triggers":{"type":["map","string"],"optional":true}}}},"aws_apigatewayv2_domain_name":{"version":0,"block":{"attributes":{"api_mapping_selection_expression":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"domain_name_configuration":{"nesting_mode":"list","block":{"attributes":{"certificate_arn":{"type":"string","required":true},"endpoint_type":{"type":"string","required":true},"hosted_zone_id":{"type":"string","computed":true},"security_policy":{"type":"string","required":true},"target_domain_name":{"type":"string","computed":true}}},"min_items":1,"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"update":{"type":"string","optional":true}}}}}}},"aws_apigatewayv2_integration":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"connection_id":{"type":"string","optional":true},"connection_type":{"type":"string","optional":true},"content_handling_strategy":{"type":"string","optional":true},"credentials_arn":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"integration_method":{"type":"string","optional":true},"integration_response_selection_expression":{"type":"string","computed":true},"integration_type":{"type":"string","required":true},"integration_uri":{"type":"string","optional":true},"passthrough_behavior":{"type":"string","optional":true},"payload_format_version":{"type":"string","optional":true},"request_parameters":{"type":["map","string"],"optional":true},"request_templates":{"type":["map","string"],"optional":true},"template_selection_expression":{"type":"string","optional":true},"timeout_milliseconds":{"type":"number","optional":true}},"block_types":{"tls_config":{"nesting_mode":"list","block":{"attributes":{"server_name_to_verify":{"type":"string","optional":true}}},"max_items":1}}}},"aws_apigatewayv2_integration_response":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"content_handling_strategy":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"integration_id":{"type":"string","required":true},"integration_response_key":{"type":"string","required":true},"response_templates":{"type":["map","string"],"optional":true},"template_selection_expression":{"type":"string","optional":true}}}},"aws_apigatewayv2_model":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"content_type":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"schema":{"type":"string","required":true}}}},"aws_apigatewayv2_route":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"api_key_required":{"type":"bool","optional":true},"authorization_scopes":{"type":["set","string"],"optional":true},"authorization_type":{"type":"string","optional":true},"authorizer_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"model_selection_expression":{"type":"string","optional":true},"operation_name":{"type":"string","optional":true},"request_models":{"type":["map","string"],"optional":true},"route_key":{"type":"string","required":true},"route_response_selection_expression":{"type":"string","optional":true},"target":{"type":"string","optional":true}}}},"aws_apigatewayv2_route_response":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"model_selection_expression":{"type":"string","optional":true},"response_models":{"type":["map","string"],"optional":true},"route_id":{"type":"string","required":true},"route_response_key":{"type":"string","required":true}}}},"aws_apigatewayv2_stage":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"auto_deploy":{"type":"bool","optional":true},"client_certificate_id":{"type":"string","optional":true},"deployment_id":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_url":{"type":"string","computed":true},"name":{"type":"string","required":true},"stage_variables":{"type":["map","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"access_log_settings":{"nesting_mode":"list","block":{"attributes":{"destination_arn":{"type":"string","required":true},"format":{"type":"string","required":true}}},"max_items":1},"default_route_settings":{"nesting_mode":"list","block":{"attributes":{"data_trace_enabled":{"type":"bool","optional":true},"detailed_metrics_enabled":{"type":"bool","optional":true},"logging_level":{"type":"string","optional":true,"computed":true},"throttling_burst_limit":{"type":"number","optional":true},"throttling_rate_limit":{"type":"number","optional":true}}},"max_items":1},"route_settings":{"nesting_mode":"set","block":{"attributes":{"data_trace_enabled":{"type":"bool","optional":true},"detailed_metrics_enabled":{"type":"bool","optional":true},"logging_level":{"type":"string","optional":true,"computed":true},"route_key":{"type":"string","required":true},"throttling_burst_limit":{"type":"number","optional":true},"throttling_rate_limit":{"type":"number","optional":true}}}}}}},"aws_apigatewayv2_vpc_link":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"security_group_ids":{"type":["set","string"],"required":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_app_cookie_stickiness_policy":{"version":0,"block":{"attributes":{"cookie_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"lb_port":{"type":"number","required":true},"load_balancer":{"type":"string","required":true},"name":{"type":"string","required":true}}}},"aws_appautoscaling_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"policy_type":{"type":"string","optional":true},"resource_id":{"type":"string","required":true},"scalable_dimension":{"type":"string","required":true},"service_namespace":{"type":"string","required":true}},"block_types":{"step_scaling_policy_configuration":{"nesting_mode":"list","block":{"attributes":{"adjustment_type":{"type":"string","optional":true},"cooldown":{"type":"number","optional":true},"metric_aggregation_type":{"type":"string","optional":true},"min_adjustment_magnitude":{"type":"number","optional":true}},"block_types":{"step_adjustment":{"nesting_mode":"set","block":{"attributes":{"metric_interval_lower_bound":{"type":"string","optional":true},"metric_interval_upper_bound":{"type":"string","optional":true},"scaling_adjustment":{"type":"number","required":true}}}}}},"max_items":1},"target_tracking_scaling_policy_configuration":{"nesting_mode":"list","block":{"attributes":{"disable_scale_in":{"type":"bool","optional":true},"scale_in_cooldown":{"type":"number","optional":true},"scale_out_cooldown":{"type":"number","optional":true},"target_value":{"type":"number","required":true}},"block_types":{"customized_metric_specification":{"nesting_mode":"list","block":{"attributes":{"metric_name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"statistic":{"type":"string","required":true},"unit":{"type":"string","optional":true}},"block_types":{"dimensions":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}},"max_items":1},"predefined_metric_specification":{"nesting_mode":"list","block":{"attributes":{"predefined_metric_type":{"type":"string","required":true},"resource_label":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_appautoscaling_scheduled_action":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"end_time":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"resource_id":{"type":"string","required":true},"scalable_dimension":{"type":"string","optional":true},"schedule":{"type":"string","optional":true},"service_namespace":{"type":"string","required":true},"start_time":{"type":"string","optional":true}},"block_types":{"scalable_target_action":{"nesting_mode":"list","block":{"attributes":{"max_capacity":{"type":"number","optional":true},"min_capacity":{"type":"number","optional":true}}},"max_items":1}}}},"aws_appautoscaling_target":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"max_capacity":{"type":"number","required":true},"min_capacity":{"type":"number","required":true},"resource_id":{"type":"string","required":true},"role_arn":{"type":"string","optional":true,"computed":true},"scalable_dimension":{"type":"string","required":true},"service_namespace":{"type":"string","required":true}}}},"aws_appmesh_mesh":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"block_types":{"egress_filter":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_appmesh_route":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"mesh_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"virtual_router_name":{"type":"string","required":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"attributes":{"priority":{"type":"number","optional":true}},"block_types":{"http_route":{"nesting_mode":"list","block":{"block_types":{"action":{"nesting_mode":"list","block":{"block_types":{"weighted_target":{"nesting_mode":"set","block":{"attributes":{"virtual_node":{"type":"string","required":true},"weight":{"type":"number","required":true}}},"min_items":1,"max_items":10}}},"min_items":1,"max_items":1},"match":{"nesting_mode":"list","block":{"attributes":{"method":{"type":"string","optional":true},"prefix":{"type":"string","required":true},"scheme":{"type":"string","optional":true}},"block_types":{"header":{"nesting_mode":"set","block":{"attributes":{"invert":{"type":"bool","optional":true},"name":{"type":"string","required":true}},"block_types":{"match":{"nesting_mode":"list","block":{"attributes":{"exact":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"regex":{"type":"string","optional":true},"suffix":{"type":"string","optional":true}},"block_types":{"range":{"nesting_mode":"list","block":{"attributes":{"end":{"type":"number","required":true},"start":{"type":"number","required":true}}},"max_items":1}}},"max_items":1}}},"max_items":10}}},"min_items":1,"max_items":1}}},"max_items":1},"tcp_route":{"nesting_mode":"list","block":{"block_types":{"action":{"nesting_mode":"list","block":{"block_types":{"weighted_target":{"nesting_mode":"set","block":{"attributes":{"virtual_node":{"type":"string","required":true},"weight":{"type":"number","required":true}}},"min_items":1,"max_items":10}}},"min_items":1,"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_appmesh_virtual_node":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"mesh_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"block_types":{"backend":{"nesting_mode":"set","block":{"block_types":{"virtual_service":{"nesting_mode":"list","block":{"attributes":{"virtual_service_name":{"type":"string","required":true}}},"max_items":1}}},"max_items":25},"listener":{"nesting_mode":"list","block":{"block_types":{"health_check":{"nesting_mode":"list","block":{"attributes":{"healthy_threshold":{"type":"number","required":true},"interval_millis":{"type":"number","required":true},"path":{"type":"string","optional":true},"port":{"type":"number","optional":true,"computed":true},"protocol":{"type":"string","required":true},"timeout_millis":{"type":"number","required":true},"unhealthy_threshold":{"type":"number","required":true}}},"max_items":1},"port_mapping":{"nesting_mode":"list","block":{"attributes":{"port":{"type":"number","required":true},"protocol":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"max_items":1},"logging":{"nesting_mode":"list","block":{"block_types":{"access_log":{"nesting_mode":"list","block":{"block_types":{"file":{"nesting_mode":"list","block":{"attributes":{"path":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"max_items":1},"service_discovery":{"nesting_mode":"list","block":{"block_types":{"aws_cloud_map":{"nesting_mode":"list","block":{"attributes":{"attributes":{"type":["map","string"],"optional":true},"namespace_name":{"type":"string","required":true},"service_name":{"type":"string","required":true}}},"max_items":1},"dns":{"nesting_mode":"list","block":{"attributes":{"hostname":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_appmesh_virtual_router":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"mesh_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"block_types":{"listener":{"nesting_mode":"list","block":{"block_types":{"port_mapping":{"nesting_mode":"list","block":{"attributes":{"port":{"type":"number","required":true},"protocol":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_appmesh_virtual_service":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"mesh_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"block_types":{"provider":{"nesting_mode":"list","block":{"block_types":{"virtual_node":{"nesting_mode":"list","block":{"attributes":{"virtual_node_name":{"type":"string","required":true}}},"max_items":1},"virtual_router":{"nesting_mode":"list","block":{"attributes":{"virtual_router_name":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_appsync_api_key":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"expires":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","computed":true,"sensitive":true}}}},"aws_appsync_datasource":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"service_role_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"dynamodb_config":{"nesting_mode":"list","block":{"attributes":{"region":{"type":"string","optional":true,"computed":true},"table_name":{"type":"string","required":true},"use_caller_credentials":{"type":"bool","optional":true}}},"max_items":1},"elasticsearch_config":{"nesting_mode":"list","block":{"attributes":{"endpoint":{"type":"string","required":true},"region":{"type":"string","optional":true,"computed":true}}},"max_items":1},"http_config":{"nesting_mode":"list","block":{"attributes":{"endpoint":{"type":"string","required":true}}},"max_items":1},"lambda_config":{"nesting_mode":"list","block":{"attributes":{"function_arn":{"type":"string","required":true}}},"max_items":1}}}},"aws_appsync_function":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"data_source":{"type":"string","required":true},"description":{"type":"string","optional":true},"function_id":{"type":"string","computed":true},"function_version":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"request_mapping_template":{"type":"string","required":true},"response_mapping_template":{"type":"string","required":true}}}},"aws_appsync_graphql_api":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"authentication_type":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"schema":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"uris":{"type":["map","string"],"computed":true},"xray_enabled":{"type":"bool","optional":true}},"block_types":{"additional_authentication_provider":{"nesting_mode":"list","block":{"attributes":{"authentication_type":{"type":"string","required":true}},"block_types":{"openid_connect_config":{"nesting_mode":"list","block":{"attributes":{"auth_ttl":{"type":"number","optional":true},"client_id":{"type":"string","optional":true},"iat_ttl":{"type":"number","optional":true},"issuer":{"type":"string","required":true}}},"max_items":1},"user_pool_config":{"nesting_mode":"list","block":{"attributes":{"app_id_client_regex":{"type":"string","optional":true},"aws_region":{"type":"string","optional":true,"computed":true},"user_pool_id":{"type":"string","required":true}}},"max_items":1}}}},"log_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_logs_role_arn":{"type":"string","required":true},"exclude_verbose_content":{"type":"bool","optional":true},"field_log_level":{"type":"string","required":true}}},"max_items":1},"openid_connect_config":{"nesting_mode":"list","block":{"attributes":{"auth_ttl":{"type":"number","optional":true},"client_id":{"type":"string","optional":true},"iat_ttl":{"type":"number","optional":true},"issuer":{"type":"string","required":true}}},"max_items":1},"user_pool_config":{"nesting_mode":"list","block":{"attributes":{"app_id_client_regex":{"type":"string","optional":true},"aws_region":{"type":"string","optional":true,"computed":true},"default_action":{"type":"string","required":true},"user_pool_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_appsync_resolver":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"data_source":{"type":"string","optional":true},"field":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"kind":{"type":"string","optional":true},"request_template":{"type":"string","required":true},"response_template":{"type":"string","required":true},"type":{"type":"string","required":true}},"block_types":{"caching_config":{"nesting_mode":"list","block":{"attributes":{"caching_keys":{"type":["set","string"],"optional":true},"ttl":{"type":"number","optional":true}}},"max_items":1},"pipeline_config":{"nesting_mode":"list","block":{"attributes":{"functions":{"type":["list","string"],"optional":true}}},"max_items":1}}}},"aws_athena_database":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"encryption_configuration":{"nesting_mode":"list","block":{"attributes":{"encryption_option":{"type":"string","required":true},"kms_key":{"type":"string","optional":true}}},"max_items":1}}}},"aws_athena_named_query":{"version":0,"block":{"attributes":{"database":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"query":{"type":"string","required":true},"workgroup":{"type":"string","optional":true}}}},"aws_athena_workgroup":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"state":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"configuration":{"nesting_mode":"list","block":{"attributes":{"bytes_scanned_cutoff_per_query":{"type":"number","optional":true},"enforce_workgroup_configuration":{"type":"bool","optional":true},"publish_cloudwatch_metrics_enabled":{"type":"bool","optional":true}},"block_types":{"result_configuration":{"nesting_mode":"list","block":{"attributes":{"output_location":{"type":"string","optional":true}},"block_types":{"encryption_configuration":{"nesting_mode":"list","block":{"attributes":{"encryption_option":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}},"max_items":1}}}},"aws_autoscaling_attachment":{"version":0,"block":{"attributes":{"alb_target_group_arn":{"type":"string","optional":true},"autoscaling_group_name":{"type":"string","required":true},"elb":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_autoscaling_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"default_cooldown":{"type":"number","optional":true,"computed":true},"desired_capacity":{"type":"number","optional":true,"computed":true},"enabled_metrics":{"type":["set","string"],"optional":true},"force_delete":{"type":"bool","optional":true},"health_check_grace_period":{"type":"number","optional":true},"health_check_type":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"launch_configuration":{"type":"string","optional":true},"load_balancers":{"type":["set","string"],"optional":true},"max_instance_lifetime":{"type":"number","optional":true},"max_size":{"type":"number","required":true},"metrics_granularity":{"type":"string","optional":true},"min_elb_capacity":{"type":"number","optional":true},"min_size":{"type":"number","required":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"placement_group":{"type":"string","optional":true},"protect_from_scale_in":{"type":"bool","optional":true},"service_linked_role_arn":{"type":"string","optional":true,"computed":true},"suspended_processes":{"type":["set","string"],"optional":true},"tags":{"type":["set",["map","string"]],"optional":true},"target_group_arns":{"type":["set","string"],"optional":true},"termination_policies":{"type":["list","string"],"optional":true},"vpc_zone_identifier":{"type":["set","string"],"optional":true,"computed":true},"wait_for_capacity_timeout":{"type":"string","optional":true},"wait_for_elb_capacity":{"type":"number","optional":true}},"block_types":{"initial_lifecycle_hook":{"nesting_mode":"set","block":{"attributes":{"default_result":{"type":"string","optional":true,"computed":true},"heartbeat_timeout":{"type":"number","optional":true},"lifecycle_transition":{"type":"string","required":true},"name":{"type":"string","required":true},"notification_metadata":{"type":"string","optional":true},"notification_target_arn":{"type":"string","optional":true},"role_arn":{"type":"string","optional":true}}}},"launch_template":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"version":{"type":"string","optional":true}}},"max_items":1},"mixed_instances_policy":{"nesting_mode":"list","block":{"block_types":{"instances_distribution":{"nesting_mode":"list","block":{"attributes":{"on_demand_allocation_strategy":{"type":"string","optional":true,"computed":true},"on_demand_base_capacity":{"type":"number","optional":true,"computed":true},"on_demand_percentage_above_base_capacity":{"type":"number","optional":true,"computed":true},"spot_allocation_strategy":{"type":"string","optional":true,"computed":true},"spot_instance_pools":{"type":"number","optional":true,"computed":true},"spot_max_price":{"type":"string","optional":true}}},"max_items":1},"launch_template":{"nesting_mode":"list","block":{"block_types":{"launch_template_specification":{"nesting_mode":"list","block":{"attributes":{"launch_template_id":{"type":"string","optional":true,"computed":true},"launch_template_name":{"type":"string","optional":true,"computed":true},"version":{"type":"string","optional":true}}},"min_items":1,"max_items":1},"override":{"nesting_mode":"list","block":{"attributes":{"instance_type":{"type":"string","optional":true},"weighted_capacity":{"type":"string","optional":true}}}}}},"min_items":1,"max_items":1}}},"max_items":1},"tag":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","required":true},"propagate_at_launch":{"type":"bool","required":true},"value":{"type":"string","required":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true}}}}}}},"aws_autoscaling_lifecycle_hook":{"version":0,"block":{"attributes":{"autoscaling_group_name":{"type":"string","required":true},"default_result":{"type":"string","optional":true,"computed":true},"heartbeat_timeout":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"lifecycle_transition":{"type":"string","required":true},"name":{"type":"string","required":true},"notification_metadata":{"type":"string","optional":true},"notification_target_arn":{"type":"string","optional":true},"role_arn":{"type":"string","optional":true}}}},"aws_autoscaling_notification":{"version":0,"block":{"attributes":{"group_names":{"type":["set","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"notifications":{"type":["set","string"],"required":true},"topic_arn":{"type":"string","required":true}}}},"aws_autoscaling_policy":{"version":0,"block":{"attributes":{"adjustment_type":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"autoscaling_group_name":{"type":"string","required":true},"cooldown":{"type":"number","optional":true},"estimated_instance_warmup":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"metric_aggregation_type":{"type":"string","optional":true,"computed":true},"min_adjustment_magnitude":{"type":"number","optional":true},"name":{"type":"string","required":true},"policy_type":{"type":"string","optional":true},"scaling_adjustment":{"type":"number","optional":true}},"block_types":{"step_adjustment":{"nesting_mode":"set","block":{"attributes":{"metric_interval_lower_bound":{"type":"string","optional":true},"metric_interval_upper_bound":{"type":"string","optional":true},"scaling_adjustment":{"type":"number","required":true}}}},"target_tracking_configuration":{"nesting_mode":"list","block":{"attributes":{"disable_scale_in":{"type":"bool","optional":true},"target_value":{"type":"number","required":true}},"block_types":{"customized_metric_specification":{"nesting_mode":"list","block":{"attributes":{"metric_name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"statistic":{"type":"string","required":true},"unit":{"type":"string","optional":true}},"block_types":{"metric_dimension":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}},"max_items":1},"predefined_metric_specification":{"nesting_mode":"list","block":{"attributes":{"predefined_metric_type":{"type":"string","required":true},"resource_label":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_autoscaling_schedule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"autoscaling_group_name":{"type":"string","required":true},"desired_capacity":{"type":"number","optional":true,"computed":true},"end_time":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"max_size":{"type":"number","optional":true,"computed":true},"min_size":{"type":"number","optional":true,"computed":true},"recurrence":{"type":"string","optional":true,"computed":true},"scheduled_action_name":{"type":"string","required":true},"start_time":{"type":"string","optional":true,"computed":true}}}},"aws_backup_plan":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","computed":true}},"block_types":{"rule":{"nesting_mode":"set","block":{"attributes":{"completion_window":{"type":"number","optional":true},"recovery_point_tags":{"type":["map","string"],"optional":true},"rule_name":{"type":"string","required":true},"schedule":{"type":"string","optional":true},"start_window":{"type":"number","optional":true},"target_vault_name":{"type":"string","required":true}},"block_types":{"copy_action":{"nesting_mode":"set","block":{"attributes":{"destination_vault_arn":{"type":"string","required":true}},"block_types":{"lifecycle":{"nesting_mode":"list","block":{"attributes":{"cold_storage_after":{"type":"number","optional":true},"delete_after":{"type":"number","optional":true}}},"max_items":1}}}},"lifecycle":{"nesting_mode":"list","block":{"attributes":{"cold_storage_after":{"type":"number","optional":true},"delete_after":{"type":"number","optional":true}}},"max_items":1}}},"min_items":1}}}},"aws_backup_selection":{"version":0,"block":{"attributes":{"iam_role_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"plan_id":{"type":"string","required":true},"resources":{"type":["set","string"],"optional":true}},"block_types":{"selection_tag":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","required":true},"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_backup_vault":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"recovery_points":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_batch_compute_environment":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compute_environment_name":{"type":"string","optional":true,"computed":true},"compute_environment_name_prefix":{"type":"string","optional":true},"ecs_cluster_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"service_role":{"type":"string","required":true},"state":{"type":"string","optional":true},"status":{"type":"string","computed":true},"status_reason":{"type":"string","computed":true},"type":{"type":"string","required":true}},"block_types":{"compute_resources":{"nesting_mode":"list","block":{"attributes":{"allocation_strategy":{"type":"string","optional":true},"bid_percentage":{"type":"number","optional":true},"desired_vcpus":{"type":"number","optional":true,"computed":true},"ec2_key_pair":{"type":"string","optional":true},"image_id":{"type":"string","optional":true},"instance_role":{"type":"string","required":true},"instance_type":{"type":["set","string"],"required":true},"max_vcpus":{"type":"number","required":true},"min_vcpus":{"type":"number","required":true},"security_group_ids":{"type":["set","string"],"required":true},"spot_iam_fleet_role":{"type":"string","optional":true},"subnets":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","required":true}},"block_types":{"launch_template":{"nesting_mode":"list","block":{"attributes":{"launch_template_id":{"type":"string","optional":true},"launch_template_name":{"type":"string","optional":true},"version":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_batch_job_definition":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"container_properties":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"parameters":{"type":["map","string"],"optional":true},"revision":{"type":"number","computed":true},"type":{"type":"string","required":true}},"block_types":{"retry_strategy":{"nesting_mode":"list","block":{"attributes":{"attempts":{"type":"number","optional":true}}},"max_items":1},"timeout":{"nesting_mode":"list","block":{"attributes":{"attempt_duration_seconds":{"type":"number","optional":true}}},"max_items":1}}}},"aws_batch_job_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compute_environments":{"type":["list","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"priority":{"type":"number","required":true},"state":{"type":"string","required":true}}}},"aws_budgets_budget":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"budget_type":{"type":"string","required":true},"cost_filters":{"type":["map","string"],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"limit_amount":{"type":"string","required":true},"limit_unit":{"type":"string","required":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"time_period_end":{"type":"string","optional":true},"time_period_start":{"type":"string","required":true},"time_unit":{"type":"string","required":true}},"block_types":{"cost_types":{"nesting_mode":"list","block":{"attributes":{"include_credit":{"type":"bool","optional":true},"include_discount":{"type":"bool","optional":true},"include_other_subscription":{"type":"bool","optional":true},"include_recurring":{"type":"bool","optional":true},"include_refund":{"type":"bool","optional":true},"include_subscription":{"type":"bool","optional":true},"include_support":{"type":"bool","optional":true},"include_tax":{"type":"bool","optional":true},"include_upfront":{"type":"bool","optional":true},"use_amortized":{"type":"bool","optional":true},"use_blended":{"type":"bool","optional":true}}},"max_items":1},"notification":{"nesting_mode":"set","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"notification_type":{"type":"string","required":true},"subscriber_email_addresses":{"type":["set","string"],"optional":true},"subscriber_sns_topic_arns":{"type":["set","string"],"optional":true},"threshold":{"type":"number","required":true},"threshold_type":{"type":"string","required":true}}}}}}},"aws_cloud9_environment_ec2":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"automatic_stop_time_minutes":{"type":"number","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","required":true},"name":{"type":"string","required":true},"owner_arn":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","computed":true}}}},"aws_cloudformation_stack":{"version":0,"block":{"attributes":{"capabilities":{"type":["set","string"],"optional":true},"disable_rollback":{"type":"bool","optional":true},"iam_role_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"notification_arns":{"type":["set","string"],"optional":true},"on_failure":{"type":"string","optional":true},"outputs":{"type":["map","string"],"computed":true},"parameters":{"type":["map","string"],"optional":true,"computed":true},"policy_body":{"type":"string","optional":true,"computed":true},"policy_url":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"template_body":{"type":"string","optional":true,"computed":true},"template_url":{"type":"string","optional":true},"timeout_in_minutes":{"type":"number","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cloudformation_stack_set":{"version":0,"block":{"attributes":{"administration_role_arn":{"type":"string","required":true},"arn":{"type":"string","computed":true},"capabilities":{"type":["set","string"],"optional":true},"description":{"type":"string","optional":true},"execution_role_name":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"parameters":{"type":["map","string"],"optional":true},"stack_set_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"template_body":{"type":"string","optional":true,"computed":true},"template_url":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"update":{"type":"string","optional":true}}}}}}},"aws_cloudformation_stack_set_instance":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"parameter_overrides":{"type":["map","string"],"optional":true},"region":{"type":"string","optional":true,"computed":true},"retain_stack":{"type":"bool","optional":true},"stack_id":{"type":"string","computed":true},"stack_set_name":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cloudfront_distribution":{"version":1,"block":{"attributes":{"aliases":{"type":["set","string"],"optional":true},"arn":{"type":"string","computed":true},"caller_reference":{"type":"string","computed":true},"comment":{"type":"string","optional":true},"default_root_object":{"type":"string","optional":true},"domain_name":{"type":"string","computed":true},"enabled":{"type":"bool","required":true},"etag":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"http_version":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"in_progress_validation_batches":{"type":"number","computed":true},"is_ipv6_enabled":{"type":"bool","optional":true},"last_modified_time":{"type":"string","computed":true},"price_class":{"type":"string","optional":true},"retain_on_delete":{"type":"bool","optional":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"trusted_signers":{"type":["list",["object",{"enabled":"bool","items":["list",["object",{"aws_account_number":"string","key_pair_ids":["set","string"]}]]}]],"computed":true},"wait_for_deployment":{"type":"bool","optional":true},"web_acl_id":{"type":"string","optional":true}},"block_types":{"custom_error_response":{"nesting_mode":"set","block":{"attributes":{"error_caching_min_ttl":{"type":"number","optional":true},"error_code":{"type":"number","required":true},"response_code":{"type":"number","optional":true},"response_page_path":{"type":"string","optional":true}}}},"default_cache_behavior":{"nesting_mode":"list","block":{"attributes":{"allowed_methods":{"type":["set","string"],"required":true},"cached_methods":{"type":["set","string"],"required":true},"compress":{"type":"bool","optional":true},"default_ttl":{"type":"number","optional":true},"field_level_encryption_id":{"type":"string","optional":true},"max_ttl":{"type":"number","optional":true},"min_ttl":{"type":"number","optional":true},"smooth_streaming":{"type":"bool","optional":true},"target_origin_id":{"type":"string","required":true},"trusted_signers":{"type":["list","string"],"optional":true},"viewer_protocol_policy":{"type":"string","required":true}},"block_types":{"forwarded_values":{"nesting_mode":"list","block":{"attributes":{"headers":{"type":["set","string"],"optional":true},"query_string":{"type":"bool","required":true},"query_string_cache_keys":{"type":["list","string"],"optional":true}},"block_types":{"cookies":{"nesting_mode":"list","block":{"attributes":{"forward":{"type":"string","required":true},"whitelisted_names":{"type":["set","string"],"optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"lambda_function_association":{"nesting_mode":"set","block":{"attributes":{"event_type":{"type":"string","required":true},"include_body":{"type":"bool","optional":true},"lambda_arn":{"type":"string","required":true}}},"max_items":4}}},"min_items":1,"max_items":1},"logging_config":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"include_cookies":{"type":"bool","optional":true},"prefix":{"type":"string","optional":true}}},"max_items":1},"ordered_cache_behavior":{"nesting_mode":"list","block":{"attributes":{"allowed_methods":{"type":["set","string"],"required":true},"cached_methods":{"type":["set","string"],"required":true},"compress":{"type":"bool","optional":true},"default_ttl":{"type":"number","optional":true},"field_level_encryption_id":{"type":"string","optional":true},"max_ttl":{"type":"number","optional":true},"min_ttl":{"type":"number","optional":true},"path_pattern":{"type":"string","required":true},"smooth_streaming":{"type":"bool","optional":true},"target_origin_id":{"type":"string","required":true},"trusted_signers":{"type":["list","string"],"optional":true},"viewer_protocol_policy":{"type":"string","required":true}},"block_types":{"forwarded_values":{"nesting_mode":"list","block":{"attributes":{"headers":{"type":["set","string"],"optional":true},"query_string":{"type":"bool","required":true},"query_string_cache_keys":{"type":["list","string"],"optional":true}},"block_types":{"cookies":{"nesting_mode":"list","block":{"attributes":{"forward":{"type":"string","required":true},"whitelisted_names":{"type":["set","string"],"optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"lambda_function_association":{"nesting_mode":"set","block":{"attributes":{"event_type":{"type":"string","required":true},"include_body":{"type":"bool","optional":true},"lambda_arn":{"type":"string","required":true}}},"max_items":4}}}},"origin":{"nesting_mode":"set","block":{"attributes":{"domain_name":{"type":"string","required":true},"origin_id":{"type":"string","required":true},"origin_path":{"type":"string","optional":true}},"block_types":{"custom_header":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}},"custom_origin_config":{"nesting_mode":"list","block":{"attributes":{"http_port":{"type":"number","required":true},"https_port":{"type":"number","required":true},"origin_keepalive_timeout":{"type":"number","optional":true},"origin_protocol_policy":{"type":"string","required":true},"origin_read_timeout":{"type":"number","optional":true},"origin_ssl_protocols":{"type":["set","string"],"required":true}}},"max_items":1},"s3_origin_config":{"nesting_mode":"list","block":{"attributes":{"origin_access_identity":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"origin_group":{"nesting_mode":"set","block":{"attributes":{"origin_id":{"type":"string","required":true}},"block_types":{"failover_criteria":{"nesting_mode":"list","block":{"attributes":{"status_codes":{"type":["set","number"],"required":true}}},"min_items":1,"max_items":1},"member":{"nesting_mode":"list","block":{"attributes":{"origin_id":{"type":"string","required":true}}},"min_items":2,"max_items":2}}}},"restrictions":{"nesting_mode":"list","block":{"block_types":{"geo_restriction":{"nesting_mode":"list","block":{"attributes":{"locations":{"type":["set","string"],"optional":true},"restriction_type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"viewer_certificate":{"nesting_mode":"list","block":{"attributes":{"acm_certificate_arn":{"type":"string","optional":true},"cloudfront_default_certificate":{"type":"bool","optional":true},"iam_certificate_id":{"type":"string","optional":true},"minimum_protocol_version":{"type":"string","optional":true},"ssl_support_method":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}}},"aws_cloudfront_origin_access_identity":{"version":0,"block":{"attributes":{"caller_reference":{"type":"string","computed":true},"cloudfront_access_identity_path":{"type":"string","computed":true},"comment":{"type":"string","optional":true},"etag":{"type":"string","computed":true},"iam_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"s3_canonical_user_id":{"type":"string","computed":true}}}},"aws_cloudfront_public_key":{"version":0,"block":{"attributes":{"caller_reference":{"type":"string","computed":true},"comment":{"type":"string","optional":true},"encoded_key":{"type":"string","required":true},"etag":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true}}}},"aws_cloudhsm_v2_cluster":{"version":0,"block":{"attributes":{"cluster_certificates":{"type":["list",["object",{"aws_hardware_certificate":"string","cluster_certificate":"string","cluster_csr":"string","hsm_certificate":"string","manufacturer_hardware_certificate":"string"}]],"computed":true},"cluster_id":{"type":"string","computed":true},"cluster_state":{"type":"string","computed":true},"hsm_type":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"security_group_id":{"type":"string","computed":true},"source_backup_identifier":{"type":"string","optional":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cloudhsm_v2_hsm":{"version":0,"block":{"attributes":{"availability_zone":{"type":"string","optional":true,"computed":true},"cluster_id":{"type":"string","required":true},"hsm_eni_id":{"type":"string","computed":true},"hsm_id":{"type":"string","computed":true},"hsm_state":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cloudtrail":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cloud_watch_logs_group_arn":{"type":"string","optional":true},"cloud_watch_logs_role_arn":{"type":"string","optional":true},"enable_log_file_validation":{"type":"bool","optional":true},"enable_logging":{"type":"bool","optional":true},"home_region":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"include_global_service_events":{"type":"bool","optional":true},"is_multi_region_trail":{"type":"bool","optional":true},"is_organization_trail":{"type":"bool","optional":true},"kms_key_id":{"type":"string","optional":true},"name":{"type":"string","required":true},"s3_bucket_name":{"type":"string","required":true},"s3_key_prefix":{"type":"string","optional":true},"sns_topic_name":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"event_selector":{"nesting_mode":"list","block":{"attributes":{"include_management_events":{"type":"bool","optional":true},"read_write_type":{"type":"string","optional":true}},"block_types":{"data_resource":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}},"max_items":5}}}},"aws_cloudwatch_dashboard":{"version":0,"block":{"attributes":{"dashboard_arn":{"type":"string","computed":true},"dashboard_body":{"type":"string","required":true},"dashboard_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_cloudwatch_event_permission":{"version":0,"block":{"attributes":{"action":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"principal":{"type":"string","required":true},"statement_id":{"type":"string","required":true}},"block_types":{"condition":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"type":{"type":"string","required":true},"value":{"type":"string","required":true}}},"max_items":1}}}},"aws_cloudwatch_event_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"event_pattern":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"is_enabled":{"type":"bool","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"role_arn":{"type":"string","optional":true},"schedule_expression":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_cloudwatch_event_target":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"input":{"type":"string","optional":true},"input_path":{"type":"string","optional":true},"role_arn":{"type":"string","optional":true},"rule":{"type":"string","required":true},"target_id":{"type":"string","optional":true,"computed":true}},"block_types":{"batch_target":{"nesting_mode":"list","block":{"attributes":{"array_size":{"type":"number","optional":true},"job_attempts":{"type":"number","optional":true},"job_definition":{"type":"string","required":true},"job_name":{"type":"string","required":true}}},"max_items":1},"ecs_target":{"nesting_mode":"list","block":{"attributes":{"group":{"type":"string","optional":true},"launch_type":{"type":"string","optional":true},"platform_version":{"type":"string","optional":true},"task_count":{"type":"number","optional":true},"task_definition_arn":{"type":"string","required":true}},"block_types":{"network_configuration":{"nesting_mode":"list","block":{"attributes":{"assign_public_ip":{"type":"bool","optional":true},"security_groups":{"type":["set","string"],"optional":true},"subnets":{"type":["set","string"],"required":true}}},"max_items":1}}},"max_items":1},"input_transformer":{"nesting_mode":"list","block":{"attributes":{"input_paths":{"type":["map","string"],"optional":true},"input_template":{"type":"string","required":true}}},"max_items":1},"kinesis_target":{"nesting_mode":"list","block":{"attributes":{"partition_key_path":{"type":"string","optional":true}}},"max_items":1},"run_command_targets":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"max_items":5},"sqs_target":{"nesting_mode":"list","block":{"attributes":{"message_group_id":{"type":"string","optional":true}}},"max_items":1}}}},"aws_cloudwatch_log_destination":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"target_arn":{"type":"string","required":true}}}},"aws_cloudwatch_log_destination_policy":{"version":0,"block":{"attributes":{"access_policy":{"type":"string","required":true},"destination_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_cloudwatch_log_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"retention_in_days":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_cloudwatch_log_metric_filter":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","required":true},"name":{"type":"string","required":true},"pattern":{"type":"string","required":true}},"block_types":{"metric_transformation":{"nesting_mode":"list","block":{"attributes":{"default_value":{"type":"string","optional":true},"name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"value":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_cloudwatch_log_resource_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy_document":{"type":"string","required":true},"policy_name":{"type":"string","required":true}}}},"aws_cloudwatch_log_stream":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","required":true},"name":{"type":"string","required":true}}}},"aws_cloudwatch_log_subscription_filter":{"version":0,"block":{"attributes":{"destination_arn":{"type":"string","required":true},"distribution":{"type":"string","optional":true},"filter_pattern":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","required":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","optional":true,"computed":true}}}},"aws_cloudwatch_metric_alarm":{"version":1,"block":{"attributes":{"actions_enabled":{"type":"bool","optional":true},"alarm_actions":{"type":["set","string"],"optional":true},"alarm_description":{"type":"string","optional":true},"alarm_name":{"type":"string","required":true},"arn":{"type":"string","computed":true},"comparison_operator":{"type":"string","required":true},"datapoints_to_alarm":{"type":"number","optional":true},"dimensions":{"type":["map","string"],"optional":true},"evaluate_low_sample_count_percentiles":{"type":"string","optional":true,"computed":true},"evaluation_periods":{"type":"number","required":true},"extended_statistic":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"insufficient_data_actions":{"type":["set","string"],"optional":true},"metric_name":{"type":"string","optional":true},"namespace":{"type":"string","optional":true},"ok_actions":{"type":["set","string"],"optional":true},"period":{"type":"number","optional":true},"statistic":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"threshold":{"type":"number","optional":true},"threshold_metric_id":{"type":"string","optional":true},"treat_missing_data":{"type":"string","optional":true},"unit":{"type":"string","optional":true}},"block_types":{"metric_query":{"nesting_mode":"set","block":{"attributes":{"expression":{"type":"string","optional":true},"id":{"type":"string","required":true},"label":{"type":"string","optional":true},"return_data":{"type":"bool","optional":true}},"block_types":{"metric":{"nesting_mode":"list","block":{"attributes":{"dimensions":{"type":["map","string"],"optional":true},"metric_name":{"type":"string","required":true},"namespace":{"type":"string","optional":true},"period":{"type":"number","required":true},"stat":{"type":"string","required":true},"unit":{"type":"string","optional":true}}},"max_items":1}}}}}}},"aws_codebuild_project":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"badge_enabled":{"type":"bool","optional":true},"badge_url":{"type":"string","computed":true},"build_timeout":{"type":"number","optional":true},"description":{"type":"string","optional":true,"computed":true},"encryption_key":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"queued_timeout":{"type":"number","optional":true},"service_role":{"type":"string","required":true},"source_version":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"artifacts":{"nesting_mode":"list","block":{"attributes":{"artifact_identifier":{"type":"string","optional":true},"encryption_disabled":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"name":{"type":"string","optional":true},"namespace_type":{"type":"string","optional":true},"override_artifact_name":{"type":"bool","optional":true},"packaging":{"type":"string","optional":true},"path":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1},"cache":{"nesting_mode":"list","block":{"attributes":{"location":{"type":"string","optional":true},"modes":{"type":["list","string"],"optional":true},"type":{"type":"string","optional":true}}},"max_items":1},"environment":{"nesting_mode":"list","block":{"attributes":{"certificate":{"type":"string","optional":true},"compute_type":{"type":"string","required":true},"image":{"type":"string","required":true},"image_pull_credentials_type":{"type":"string","optional":true},"privileged_mode":{"type":"bool","optional":true},"type":{"type":"string","required":true}},"block_types":{"environment_variable":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true},"type":{"type":"string","optional":true},"value":{"type":"string","required":true}}}},"registry_credential":{"nesting_mode":"list","block":{"attributes":{"credential":{"type":"string","required":true},"credential_provider":{"type":"string","required":true}}},"max_items":1}}},"min_items":1,"max_items":1},"logs_config":{"nesting_mode":"list","block":{"block_types":{"cloudwatch_logs":{"nesting_mode":"list","block":{"attributes":{"group_name":{"type":"string","optional":true},"status":{"type":"string","optional":true},"stream_name":{"type":"string","optional":true}}},"max_items":1},"s3_logs":{"nesting_mode":"list","block":{"attributes":{"encryption_disabled":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"status":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"secondary_artifacts":{"nesting_mode":"set","block":{"attributes":{"artifact_identifier":{"type":"string","required":true},"encryption_disabled":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"name":{"type":"string","optional":true},"namespace_type":{"type":"string","optional":true},"override_artifact_name":{"type":"bool","optional":true},"packaging":{"type":"string","optional":true},"path":{"type":"string","optional":true},"type":{"type":"string","required":true}}}},"secondary_sources":{"nesting_mode":"set","block":{"attributes":{"buildspec":{"type":"string","optional":true},"git_clone_depth":{"type":"number","optional":true},"insecure_ssl":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"report_build_status":{"type":"bool","optional":true},"source_identifier":{"type":"string","required":true},"type":{"type":"string","required":true}},"block_types":{"auth":{"nesting_mode":"list","block":{"attributes":{"resource":{"type":"string","optional":true,"sensitive":true},"type":{"type":"string","required":true}}},"max_items":1},"git_submodules_config":{"nesting_mode":"list","block":{"attributes":{"fetch_submodules":{"type":"bool","required":true}}},"max_items":1}}}},"source":{"nesting_mode":"list","block":{"attributes":{"buildspec":{"type":"string","optional":true},"git_clone_depth":{"type":"number","optional":true},"insecure_ssl":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"report_build_status":{"type":"bool","optional":true},"type":{"type":"string","required":true}},"block_types":{"auth":{"nesting_mode":"list","block":{"attributes":{"resource":{"type":"string","optional":true,"sensitive":true},"type":{"type":"string","required":true}}},"max_items":1},"git_submodules_config":{"nesting_mode":"list","block":{"attributes":{"fetch_submodules":{"type":"bool","required":true}}},"max_items":1}}},"min_items":1,"max_items":1},"vpc_config":{"nesting_mode":"list","block":{"attributes":{"security_group_ids":{"type":["set","string"],"required":true},"subnets":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_codebuild_source_credential":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auth_type":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"server_type":{"type":"string","required":true},"token":{"type":"string","required":true,"sensitive":true},"user_name":{"type":"string","optional":true}}}},"aws_codebuild_webhook":{"version":0,"block":{"attributes":{"branch_filter":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"payload_url":{"type":"string","computed":true},"project_name":{"type":"string","required":true},"secret":{"type":"string","computed":true,"sensitive":true},"url":{"type":"string","computed":true}},"block_types":{"filter_group":{"nesting_mode":"set","block":{"block_types":{"filter":{"nesting_mode":"list","block":{"attributes":{"exclude_matched_pattern":{"type":"bool","optional":true},"pattern":{"type":"string","required":true},"type":{"type":"string","required":true}}}}}}}}}},"aws_codecommit_repository":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"clone_url_http":{"type":"string","computed":true},"clone_url_ssh":{"type":"string","computed":true},"default_branch":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"repository_id":{"type":"string","computed":true},"repository_name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_codecommit_trigger":{"version":0,"block":{"attributes":{"configuration_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"repository_name":{"type":"string","required":true}},"block_types":{"trigger":{"nesting_mode":"set","block":{"attributes":{"branches":{"type":["list","string"],"optional":true},"custom_data":{"type":"string","optional":true},"destination_arn":{"type":"string","required":true},"events":{"type":["list","string"],"required":true},"name":{"type":"string","required":true}}},"min_items":1,"max_items":10}}}},"aws_codedeploy_app":{"version":0,"block":{"attributes":{"compute_platform":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"unique_id":{"type":"string","optional":true,"computed":true}}}},"aws_codedeploy_deployment_config":{"version":0,"block":{"attributes":{"compute_platform":{"type":"string","optional":true},"deployment_config_id":{"type":"string","computed":true},"deployment_config_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}},"block_types":{"minimum_healthy_hosts":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","optional":true},"value":{"type":"number","optional":true}}},"max_items":1},"traffic_routing_config":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","optional":true}},"block_types":{"time_based_canary":{"nesting_mode":"list","block":{"attributes":{"interval":{"type":"number","optional":true},"percentage":{"type":"number","optional":true}}},"max_items":1},"time_based_linear":{"nesting_mode":"list","block":{"attributes":{"interval":{"type":"number","optional":true},"percentage":{"type":"number","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_codedeploy_deployment_group":{"version":0,"block":{"attributes":{"app_name":{"type":"string","required":true},"autoscaling_groups":{"type":["set","string"],"optional":true},"deployment_config_name":{"type":"string","optional":true},"deployment_group_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"service_role_arn":{"type":"string","required":true}},"block_types":{"alarm_configuration":{"nesting_mode":"list","block":{"attributes":{"alarms":{"type":["set","string"],"optional":true},"enabled":{"type":"bool","optional":true},"ignore_poll_alarm_failure":{"type":"bool","optional":true}}},"max_items":1},"auto_rollback_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"events":{"type":["set","string"],"optional":true}}},"max_items":1},"blue_green_deployment_config":{"nesting_mode":"list","block":{"block_types":{"deployment_ready_option":{"nesting_mode":"list","block":{"attributes":{"action_on_timeout":{"type":"string","optional":true},"wait_time_in_minutes":{"type":"number","optional":true}}},"max_items":1},"green_fleet_provisioning_option":{"nesting_mode":"list","block":{"attributes":{"action":{"type":"string","optional":true}}},"max_items":1},"terminate_blue_instances_on_deployment_success":{"nesting_mode":"list","block":{"attributes":{"action":{"type":"string","optional":true},"termination_wait_time_in_minutes":{"type":"number","optional":true}}},"max_items":1}}},"max_items":1},"deployment_style":{"nesting_mode":"list","block":{"attributes":{"deployment_option":{"type":"string","optional":true},"deployment_type":{"type":"string","optional":true}}},"max_items":1},"ec2_tag_filter":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"type":{"type":"string","optional":true},"value":{"type":"string","optional":true}}}},"ec2_tag_set":{"nesting_mode":"set","block":{"block_types":{"ec2_tag_filter":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"type":{"type":"string","optional":true},"value":{"type":"string","optional":true}}}}}}},"ecs_service":{"nesting_mode":"list","block":{"attributes":{"cluster_name":{"type":"string","required":true},"service_name":{"type":"string","required":true}}},"max_items":1},"load_balancer_info":{"nesting_mode":"list","block":{"block_types":{"elb_info":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","optional":true}}}},"target_group_info":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","optional":true}}}},"target_group_pair_info":{"nesting_mode":"list","block":{"block_types":{"prod_traffic_route":{"nesting_mode":"list","block":{"attributes":{"listener_arns":{"type":["set","string"],"required":true}}},"min_items":1,"max_items":1},"target_group":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"min_items":1,"max_items":2},"test_traffic_route":{"nesting_mode":"list","block":{"attributes":{"listener_arns":{"type":["set","string"],"required":true}}},"max_items":1}}},"max_items":1}}},"max_items":1},"on_premises_instance_tag_filter":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"type":{"type":"string","optional":true},"value":{"type":"string","optional":true}}}},"trigger_configuration":{"nesting_mode":"set","block":{"attributes":{"trigger_events":{"type":["set","string"],"required":true},"trigger_name":{"type":"string","required":true},"trigger_target_arn":{"type":"string","required":true}}}}}}},"aws_codepipeline":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"artifact_store":{"nesting_mode":"set","block":{"attributes":{"location":{"type":"string","required":true},"region":{"type":"string","optional":true,"computed":true},"type":{"type":"string","required":true}},"block_types":{"encryption_key":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","required":true},"type":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"stage":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"category":{"type":"string","required":true},"configuration":{"type":["map","string"],"optional":true},"input_artifacts":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"namespace":{"type":"string","optional":true},"output_artifacts":{"type":["list","string"],"optional":true},"owner":{"type":"string","required":true},"provider":{"type":"string","required":true},"region":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","optional":true},"run_order":{"type":"number","optional":true,"computed":true},"version":{"type":"string","required":true}}},"min_items":1}}},"min_items":2}}}},"aws_codepipeline_webhook":{"version":0,"block":{"attributes":{"authentication":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"target_action":{"type":"string","required":true},"target_pipeline":{"type":"string","required":true},"url":{"type":"string","computed":true}},"block_types":{"authentication_configuration":{"nesting_mode":"list","block":{"attributes":{"allowed_ip_range":{"type":"string","optional":true},"secret_token":{"type":"string","optional":true,"sensitive":true}}},"max_items":1},"filter":{"nesting_mode":"set","block":{"attributes":{"json_path":{"type":"string","required":true},"match_equals":{"type":"string","required":true}}},"min_items":1}}}},"aws_codestarnotifications_notification_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"detail_type":{"type":"string","required":true},"event_type_ids":{"type":["set","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"resource":{"type":"string","required":true},"status":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"target":{"nesting_mode":"set","block":{"attributes":{"address":{"type":"string","required":true},"status":{"type":"string","computed":true},"type":{"type":"string","optional":true}}},"max_items":10}}}},"aws_cognito_identity_pool":{"version":0,"block":{"attributes":{"allow_unauthenticated_identities":{"type":"bool","optional":true},"arn":{"type":"string","computed":true},"developer_provider_name":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"identity_pool_name":{"type":"string","required":true},"openid_connect_provider_arns":{"type":["list","string"],"optional":true},"saml_provider_arns":{"type":["list","string"],"optional":true},"supported_login_providers":{"type":["map","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"cognito_identity_providers":{"nesting_mode":"set","block":{"attributes":{"client_id":{"type":"string","optional":true},"provider_name":{"type":"string","optional":true},"server_side_token_check":{"type":"bool","optional":true}}}}}}},"aws_cognito_identity_pool_roles_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"identity_pool_id":{"type":"string","required":true},"roles":{"type":["map","string"],"required":true}},"block_types":{"role_mapping":{"nesting_mode":"set","block":{"attributes":{"ambiguous_role_resolution":{"type":"string","optional":true},"identity_provider":{"type":"string","required":true},"type":{"type":"string","required":true}},"block_types":{"mapping_rule":{"nesting_mode":"list","block":{"attributes":{"claim":{"type":"string","required":true},"match_type":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"value":{"type":"string","required":true}}},"max_items":25}}}}}}},"aws_cognito_identity_provider":{"version":0,"block":{"attributes":{"attribute_mapping":{"type":["map","string"],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"idp_identifiers":{"type":["list","string"],"optional":true},"provider_details":{"type":["map","string"],"required":true},"provider_name":{"type":"string","required":true},"provider_type":{"type":"string","required":true},"user_pool_id":{"type":"string","required":true}}}},"aws_cognito_resource_server":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","required":true},"name":{"type":"string","required":true},"scope_identifiers":{"type":["list","string"],"computed":true},"user_pool_id":{"type":"string","required":true}},"block_types":{"scope":{"nesting_mode":"set","block":{"attributes":{"scope_description":{"type":"string","required":true},"scope_name":{"type":"string","required":true}}},"max_items":100}}}},"aws_cognito_user_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"precedence":{"type":"number","optional":true},"role_arn":{"type":"string","optional":true},"user_pool_id":{"type":"string","required":true}}}},"aws_cognito_user_pool":{"version":0,"block":{"attributes":{"alias_attributes":{"type":["set","string"],"optional":true},"arn":{"type":"string","computed":true},"auto_verified_attributes":{"type":["set","string"],"optional":true},"creation_date":{"type":"string","computed":true},"email_verification_message":{"type":"string","optional":true,"computed":true},"email_verification_subject":{"type":"string","optional":true,"computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_modified_date":{"type":"string","computed":true},"mfa_configuration":{"type":"string","optional":true},"name":{"type":"string","required":true},"sms_authentication_message":{"type":"string","optional":true},"sms_verification_message":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"username_attributes":{"type":["list","string"],"optional":true}},"block_types":{"admin_create_user_config":{"nesting_mode":"list","block":{"attributes":{"allow_admin_create_user_only":{"type":"bool","optional":true}},"block_types":{"invite_message_template":{"nesting_mode":"list","block":{"attributes":{"email_message":{"type":"string","optional":true},"email_subject":{"type":"string","optional":true},"sms_message":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"device_configuration":{"nesting_mode":"list","block":{"attributes":{"challenge_required_on_new_device":{"type":"bool","optional":true},"device_only_remembered_on_user_prompt":{"type":"bool","optional":true}}},"max_items":1},"email_configuration":{"nesting_mode":"list","block":{"attributes":{"email_sending_account":{"type":"string","optional":true},"from_email_address":{"type":"string","optional":true},"reply_to_email_address":{"type":"string","optional":true},"source_arn":{"type":"string","optional":true}}},"max_items":1},"lambda_config":{"nesting_mode":"list","block":{"attributes":{"create_auth_challenge":{"type":"string","optional":true},"custom_message":{"type":"string","optional":true},"define_auth_challenge":{"type":"string","optional":true},"post_authentication":{"type":"string","optional":true},"post_confirmation":{"type":"string","optional":true},"pre_authentication":{"type":"string","optional":true},"pre_sign_up":{"type":"string","optional":true},"pre_token_generation":{"type":"string","optional":true},"user_migration":{"type":"string","optional":true},"verify_auth_challenge_response":{"type":"string","optional":true}}},"max_items":1},"password_policy":{"nesting_mode":"list","block":{"attributes":{"minimum_length":{"type":"number","optional":true},"require_lowercase":{"type":"bool","optional":true},"require_numbers":{"type":"bool","optional":true},"require_symbols":{"type":"bool","optional":true},"require_uppercase":{"type":"bool","optional":true},"temporary_password_validity_days":{"type":"number","optional":true}}},"max_items":1},"schema":{"nesting_mode":"set","block":{"attributes":{"attribute_data_type":{"type":"string","required":true},"developer_only_attribute":{"type":"bool","optional":true},"mutable":{"type":"bool","optional":true},"name":{"type":"string","required":true},"required":{"type":"bool","optional":true}},"block_types":{"number_attribute_constraints":{"nesting_mode":"list","block":{"attributes":{"max_value":{"type":"string","optional":true},"min_value":{"type":"string","optional":true}}},"max_items":1},"string_attribute_constraints":{"nesting_mode":"list","block":{"attributes":{"max_length":{"type":"string","optional":true},"min_length":{"type":"string","optional":true}}},"max_items":1}}},"max_items":50},"sms_configuration":{"nesting_mode":"list","block":{"attributes":{"external_id":{"type":"string","required":true},"sns_caller_arn":{"type":"string","required":true}}},"max_items":1},"software_token_mfa_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true}}},"max_items":1},"user_pool_add_ons":{"nesting_mode":"list","block":{"attributes":{"advanced_security_mode":{"type":"string","required":true}}},"max_items":1},"username_configuration":{"nesting_mode":"list","block":{"attributes":{"case_sensitive":{"type":"bool","required":true}}},"max_items":1},"verification_message_template":{"nesting_mode":"list","block":{"attributes":{"default_email_option":{"type":"string","optional":true},"email_message":{"type":"string","optional":true,"computed":true},"email_message_by_link":{"type":"string","optional":true,"computed":true},"email_subject":{"type":"string","optional":true,"computed":true},"email_subject_by_link":{"type":"string","optional":true,"computed":true},"sms_message":{"type":"string","optional":true,"computed":true}}},"max_items":1}}}},"aws_cognito_user_pool_client":{"version":0,"block":{"attributes":{"allowed_oauth_flows":{"type":["set","string"],"optional":true},"allowed_oauth_flows_user_pool_client":{"type":"bool","optional":true},"allowed_oauth_scopes":{"type":["set","string"],"optional":true},"callback_urls":{"type":["set","string"],"optional":true},"client_secret":{"type":"string","computed":true,"sensitive":true},"default_redirect_uri":{"type":"string","optional":true},"explicit_auth_flows":{"type":["set","string"],"optional":true},"generate_secret":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"logout_urls":{"type":["set","string"],"optional":true},"name":{"type":"string","required":true},"prevent_user_existence_errors":{"type":"string","optional":true,"computed":true},"read_attributes":{"type":["set","string"],"optional":true},"refresh_token_validity":{"type":"number","optional":true},"supported_identity_providers":{"type":["set","string"],"optional":true},"user_pool_id":{"type":"string","required":true},"write_attributes":{"type":["set","string"],"optional":true}},"block_types":{"analytics_configuration":{"nesting_mode":"list","block":{"attributes":{"application_id":{"type":"string","required":true},"external_id":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"user_data_shared":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_cognito_user_pool_domain":{"version":0,"block":{"attributes":{"aws_account_id":{"type":"string","computed":true},"certificate_arn":{"type":"string","optional":true},"cloudfront_distribution_arn":{"type":"string","computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"s3_bucket":{"type":"string","computed":true},"user_pool_id":{"type":"string","required":true},"version":{"type":"string","computed":true}}}},"aws_config_aggregate_authorization":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_config_config_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"input_parameters":{"type":"string","optional":true},"maximum_execution_frequency":{"type":"string","optional":true},"name":{"type":"string","required":true},"rule_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"scope":{"nesting_mode":"list","block":{"attributes":{"compliance_resource_id":{"type":"string","optional":true},"compliance_resource_types":{"type":["set","string"],"optional":true},"tag_key":{"type":"string","optional":true},"tag_value":{"type":"string","optional":true}}},"max_items":1},"source":{"nesting_mode":"list","block":{"attributes":{"owner":{"type":"string","required":true},"source_identifier":{"type":"string","required":true}},"block_types":{"source_detail":{"nesting_mode":"set","block":{"attributes":{"event_source":{"type":"string","optional":true},"maximum_execution_frequency":{"type":"string","optional":true},"message_type":{"type":"string","optional":true}}},"max_items":25}}},"min_items":1,"max_items":1}}}},"aws_config_configuration_aggregator":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"account_aggregation_source":{"nesting_mode":"list","block":{"attributes":{"account_ids":{"type":["list","string"],"required":true},"all_regions":{"type":"bool","optional":true},"regions":{"type":["list","string"],"optional":true}}},"max_items":1},"organization_aggregation_source":{"nesting_mode":"list","block":{"attributes":{"all_regions":{"type":"bool","optional":true},"regions":{"type":["list","string"],"optional":true},"role_arn":{"type":"string","required":true}}},"max_items":1}}}},"aws_config_configuration_recorder":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}},"block_types":{"recording_group":{"nesting_mode":"list","block":{"attributes":{"all_supported":{"type":"bool","optional":true},"include_global_resource_types":{"type":"bool","optional":true},"resource_types":{"type":["set","string"],"optional":true}}},"max_items":1}}}},"aws_config_configuration_recorder_status":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"is_enabled":{"type":"bool","required":true},"name":{"type":"string","required":true}}}},"aws_config_delivery_channel":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"s3_bucket_name":{"type":"string","required":true},"s3_key_prefix":{"type":"string","optional":true},"sns_topic_arn":{"type":"string","optional":true}},"block_types":{"snapshot_delivery_properties":{"nesting_mode":"list","block":{"attributes":{"delivery_frequency":{"type":"string","optional":true}}},"max_items":1}}}},"aws_config_organization_custom_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"excluded_accounts":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"input_parameters":{"type":"string","optional":true},"lambda_function_arn":{"type":"string","required":true},"maximum_execution_frequency":{"type":"string","optional":true},"name":{"type":"string","required":true},"resource_id_scope":{"type":"string","optional":true},"resource_types_scope":{"type":["set","string"],"optional":true},"tag_key_scope":{"type":"string","optional":true},"tag_value_scope":{"type":"string","optional":true},"trigger_types":{"type":["set","string"],"required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_config_organization_managed_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"excluded_accounts":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"input_parameters":{"type":"string","optional":true},"maximum_execution_frequency":{"type":"string","optional":true},"name":{"type":"string","required":true},"resource_id_scope":{"type":"string","optional":true},"resource_types_scope":{"type":["set","string"],"optional":true},"rule_identifier":{"type":"string","required":true},"tag_key_scope":{"type":"string","optional":true},"tag_value_scope":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cur_report_definition":{"version":0,"block":{"attributes":{"additional_artifacts":{"type":["set","string"],"optional":true},"additional_schema_elements":{"type":["set","string"],"required":true},"compression":{"type":"string","required":true},"format":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"report_name":{"type":"string","required":true},"s3_bucket":{"type":"string","required":true},"s3_prefix":{"type":"string","optional":true},"s3_region":{"type":"string","required":true},"time_unit":{"type":"string","required":true}}}},"aws_customer_gateway":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bgp_asn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","required":true}}}},"aws_datapipeline_pipeline":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_datasync_agent":{"version":0,"block":{"attributes":{"activation_key":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_datasync_location_efs":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"efs_file_system_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"subdirectory":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"uri":{"type":"string","computed":true}},"block_types":{"ec2_config":{"nesting_mode":"list","block":{"attributes":{"security_group_arns":{"type":["set","string"],"required":true},"subnet_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_datasync_location_nfs":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"server_hostname":{"type":"string","required":true},"subdirectory":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"uri":{"type":"string","computed":true}},"block_types":{"on_prem_config":{"nesting_mode":"list","block":{"attributes":{"agent_arns":{"type":["set","string"],"required":true}}},"min_items":1,"max_items":1}}}},"aws_datasync_location_s3":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"s3_bucket_arn":{"type":"string","required":true},"subdirectory":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"uri":{"type":"string","computed":true}},"block_types":{"s3_config":{"nesting_mode":"list","block":{"attributes":{"bucket_access_role_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_datasync_location_smb":{"version":0,"block":{"attributes":{"agent_arns":{"type":["set","string"],"required":true},"arn":{"type":"string","computed":true},"domain":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"password":{"type":"string","required":true,"sensitive":true},"server_hostname":{"type":"string","required":true},"subdirectory":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"uri":{"type":"string","computed":true},"user":{"type":"string","required":true}},"block_types":{"mount_options":{"nesting_mode":"list","block":{"attributes":{"version":{"type":"string","optional":true}}},"max_items":1}}}},"aws_datasync_task":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cloudwatch_log_group_arn":{"type":"string","optional":true},"destination_location_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"source_location_arn":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"options":{"nesting_mode":"list","block":{"attributes":{"atime":{"type":"string","optional":true},"bytes_per_second":{"type":"number","optional":true},"gid":{"type":"string","optional":true},"mtime":{"type":"string","optional":true},"posix_permissions":{"type":"string","optional":true},"preserve_deleted_files":{"type":"string","optional":true},"preserve_devices":{"type":"string","optional":true},"uid":{"type":"string","optional":true},"verify_mode":{"type":"string","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_dax_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true},"cluster_address":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"configuration_endpoint":{"type":"string","computed":true},"description":{"type":"string","optional":true},"iam_role_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"maintenance_window":{"type":"string","optional":true,"computed":true},"node_type":{"type":"string","required":true},"nodes":{"type":["list",["object",{"address":"string","availability_zone":"string","id":"string","port":"number"}]],"computed":true},"notification_topic_arn":{"type":"string","optional":true},"parameter_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","computed":true},"replication_factor":{"type":"number","required":true},"security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"subnet_group_name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"server_side_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dax_parameter_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_dax_subnet_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","computed":true}}}},"aws_db_cluster_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zones":{"type":["list","string"],"computed":true},"db_cluster_identifier":{"type":"string","required":true},"db_cluster_snapshot_arn":{"type":"string","computed":true},"db_cluster_snapshot_identifier":{"type":"string","required":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_type":{"type":"string","computed":true},"source_db_cluster_snapshot_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_db_event_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_aws_id":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"event_categories":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"sns_topic":{"type":"string","required":true},"source_ids":{"type":["set","string"],"optional":true},"source_type":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_db_instance":{"version":1,"block":{"attributes":{"address":{"type":"string","computed":true},"allocated_storage":{"type":"number","optional":true,"computed":true},"allow_major_version_upgrade":{"type":"bool","optional":true},"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"backup_retention_period":{"type":"number","optional":true,"computed":true},"backup_window":{"type":"string","optional":true,"computed":true},"ca_cert_identifier":{"type":"string","optional":true,"computed":true},"character_set_name":{"type":"string","optional":true,"computed":true},"copy_tags_to_snapshot":{"type":"bool","optional":true},"db_subnet_group_name":{"type":"string","optional":true,"computed":true},"delete_automated_backups":{"type":"bool","optional":true},"deletion_protection":{"type":"bool","optional":true},"domain":{"type":"string","optional":true},"domain_iam_role_name":{"type":"string","optional":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"optional":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true,"computed":true},"engine_version":{"type":"string","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"hosted_zone_id":{"type":"string","computed":true},"iam_database_authentication_enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"instance_class":{"type":"string","required":true},"iops":{"type":"number","optional":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"license_model":{"type":"string","optional":true,"computed":true},"maintenance_window":{"type":"string","optional":true,"computed":true},"max_allocated_storage":{"type":"number","optional":true},"monitoring_interval":{"type":"number","optional":true},"monitoring_role_arn":{"type":"string","optional":true,"computed":true},"multi_az":{"type":"bool","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"option_group_name":{"type":"string","optional":true,"computed":true},"parameter_group_name":{"type":"string","optional":true,"computed":true},"password":{"type":"string","optional":true,"sensitive":true},"performance_insights_enabled":{"type":"bool","optional":true},"performance_insights_kms_key_id":{"type":"string","optional":true,"computed":true},"performance_insights_retention_period":{"type":"number","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"publicly_accessible":{"type":"bool","optional":true},"replicas":{"type":["list","string"],"computed":true},"replicate_source_db":{"type":"string","optional":true},"resource_id":{"type":"string","computed":true},"security_group_names":{"type":["set","string"],"optional":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_identifier":{"type":"string","optional":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","optional":true},"storage_type":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"timezone":{"type":"string","optional":true,"computed":true},"username":{"type":"string","optional":true,"computed":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"s3_import":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","required":true},"bucket_prefix":{"type":"string","optional":true},"ingestion_role":{"type":"string","required":true},"source_engine":{"type":"string","required":true},"source_engine_version":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_db_instance_role_association":{"version":0,"block":{"attributes":{"db_instance_identifier":{"type":"string","required":true},"feature_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","required":true}}}},"aws_db_option_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"engine_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"major_engine_version":{"type":"string","required":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"option_group_description":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"option":{"nesting_mode":"set","block":{"attributes":{"db_security_group_memberships":{"type":["set","string"],"optional":true},"option_name":{"type":"string","required":true},"port":{"type":"number","optional":true},"version":{"type":"string","optional":true},"vpc_security_group_memberships":{"type":["set","string"],"optional":true}},"block_types":{"option_settings":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true}}}}}}},"aws_db_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_db_security_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"ingress":{"nesting_mode":"set","block":{"attributes":{"cidr":{"type":"string","optional":true},"security_group_id":{"type":"string","optional":true,"computed":true},"security_group_name":{"type":"string","optional":true,"computed":true},"security_group_owner_id":{"type":"string","optional":true,"computed":true}}},"min_items":1}}}},"aws_db_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zone":{"type":"string","computed":true},"db_instance_identifier":{"type":"string","required":true},"db_snapshot_arn":{"type":"string","computed":true},"db_snapshot_identifier":{"type":"string","required":true},"encrypted":{"type":"bool","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"iops":{"type":"number","computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"option_group_name":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_type":{"type":"string","computed":true},"source_db_snapshot_identifier":{"type":"string","computed":true},"source_region":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_type":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"read":{"type":"string","optional":true}}}}}}},"aws_db_subnet_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_default_network_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"default_network_acl_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"egress":{"nesting_mode":"set","block":{"attributes":{"action":{"type":"string","required":true},"cidr_block":{"type":"string","optional":true},"from_port":{"type":"number","required":true},"icmp_code":{"type":"number","optional":true},"icmp_type":{"type":"number","optional":true},"ipv6_cidr_block":{"type":"string","optional":true},"protocol":{"type":"string","required":true},"rule_no":{"type":"number","required":true},"to_port":{"type":"number","required":true}}}},"ingress":{"nesting_mode":"set","block":{"attributes":{"action":{"type":"string","required":true},"cidr_block":{"type":"string","optional":true},"from_port":{"type":"number","required":true},"icmp_code":{"type":"number","optional":true},"icmp_type":{"type":"number","optional":true},"ipv6_cidr_block":{"type":"string","optional":true},"protocol":{"type":"string","required":true},"rule_no":{"type":"number","required":true},"to_port":{"type":"number","required":true}}}}}}},"aws_default_route_table":{"version":0,"block":{"attributes":{"default_route_table_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"propagating_vgws":{"type":["set","string"],"optional":true},"route":{"type":["set",["object",{"cidr_block":"string","egress_only_gateway_id":"string","gateway_id":"string","instance_id":"string","ipv6_cidr_block":"string","nat_gateway_id":"string","network_interface_id":"string","transit_gateway_id":"string","vpc_peering_connection_id":"string"}]],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}}}},"aws_default_security_group":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"egress":{"type":["set",["object",{"cidr_blocks":["list","string"],"description":"string","from_port":"number","ipv6_cidr_blocks":["list","string"],"prefix_list_ids":["list","string"],"protocol":"string","security_groups":["set","string"],"self":"bool","to_port":"number"}]],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ingress":{"type":["set",["object",{"cidr_blocks":["list","string"],"description":"string","from_port":"number","ipv6_cidr_blocks":["list","string"],"prefix_list_ids":["list","string"],"protocol":"string","security_groups":["set","string"],"self":"bool","to_port":"number"}]],"optional":true,"computed":true},"name":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"revoke_rules_on_delete":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","optional":true,"computed":true}}}},"aws_default_subnet":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_ipv6_address_on_creation":{"type":"bool","computed":true},"availability_zone":{"type":"string","required":true},"availability_zone_id":{"type":"string","computed":true},"cidr_block":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block":{"type":"string","computed":true},"ipv6_cidr_block_association_id":{"type":"string","computed":true},"map_public_ip_on_launch":{"type":"bool","optional":true,"computed":true},"outpost_arn":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_default_vpc":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_generated_ipv6_cidr_block":{"type":"bool","computed":true},"cidr_block":{"type":"string","computed":true},"default_network_acl_id":{"type":"string","computed":true},"default_route_table_id":{"type":"string","computed":true},"default_security_group_id":{"type":"string","computed":true},"dhcp_options_id":{"type":"string","computed":true},"enable_classiclink":{"type":"bool","optional":true,"computed":true},"enable_classiclink_dns_support":{"type":"bool","optional":true,"computed":true},"enable_dns_hostnames":{"type":"bool","optional":true,"computed":true},"enable_dns_support":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_tenancy":{"type":"string","computed":true},"ipv6_association_id":{"type":"string","computed":true},"ipv6_cidr_block":{"type":"string","computed":true},"main_route_table_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_default_vpc_dhcp_options":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","computed":true},"domain_name_servers":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"netbios_name_servers":{"type":["list","string"],"optional":true},"netbios_node_type":{"type":"string","optional":true},"ntp_servers":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_devicefarm_project":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_directory_service_conditional_forwarder":{"version":0,"block":{"attributes":{"directory_id":{"type":"string","required":true},"dns_ips":{"type":["list","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"remote_domain_name":{"type":"string","required":true}}}},"aws_directory_service_directory":{"version":0,"block":{"attributes":{"access_url":{"type":"string","computed":true},"alias":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"dns_ip_addresses":{"type":["set","string"],"computed":true},"edition":{"type":"string","optional":true,"computed":true},"enable_sso":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"password":{"type":"string","required":true,"sensitive":true},"security_group_id":{"type":"string","computed":true},"short_name":{"type":"string","optional":true,"computed":true},"size":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true}},"block_types":{"connect_settings":{"nesting_mode":"list","block":{"attributes":{"availability_zones":{"type":["set","string"],"computed":true},"connect_ips":{"type":["set","string"],"computed":true},"customer_dns_ips":{"type":["set","string"],"required":true},"customer_username":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","required":true}}},"max_items":1},"vpc_settings":{"nesting_mode":"list","block":{"attributes":{"availability_zones":{"type":["set","string"],"computed":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_directory_service_log_subscription":{"version":0,"block":{"attributes":{"directory_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","required":true}}}},"aws_dlm_lifecycle_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","required":true},"execution_role_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"state":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"policy_details":{"nesting_mode":"list","block":{"attributes":{"resource_types":{"type":["list","string"],"required":true},"target_tags":{"type":["map","string"],"required":true}},"block_types":{"schedule":{"nesting_mode":"list","block":{"attributes":{"copy_tags":{"type":"bool","optional":true,"computed":true},"name":{"type":"string","required":true},"tags_to_add":{"type":["map","string"],"optional":true}},"block_types":{"create_rule":{"nesting_mode":"list","block":{"attributes":{"interval":{"type":"number","required":true},"interval_unit":{"type":"string","optional":true},"times":{"type":["list","string"],"optional":true,"computed":true}}},"min_items":1,"max_items":1},"retain_rule":{"nesting_mode":"list","block":{"attributes":{"count":{"type":"number","required":true}}},"min_items":1,"max_items":1}}},"min_items":1}}},"min_items":1,"max_items":1}}}},"aws_dms_certificate":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","computed":true},"certificate_id":{"type":"string","required":true},"certificate_pem":{"type":"string","optional":true,"sensitive":true},"certificate_wallet":{"type":"string","optional":true,"sensitive":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_dms_endpoint":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","optional":true,"computed":true},"database_name":{"type":"string","optional":true},"endpoint_arn":{"type":"string","computed":true},"endpoint_id":{"type":"string","required":true},"endpoint_type":{"type":"string","required":true},"engine_name":{"type":"string","required":true},"extra_connection_attributes":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true,"computed":true},"password":{"type":"string","optional":true,"sensitive":true},"port":{"type":"number","optional":true},"server_name":{"type":"string","optional":true},"service_access_role":{"type":"string","optional":true},"ssl_mode":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"username":{"type":"string","optional":true}},"block_types":{"elasticsearch_settings":{"nesting_mode":"list","block":{"attributes":{"endpoint_uri":{"type":"string","required":true},"error_retry_duration":{"type":"number","optional":true},"full_load_error_percentage":{"type":"number","optional":true},"service_access_role_arn":{"type":"string","required":true}}},"max_items":1},"kafka_settings":{"nesting_mode":"list","block":{"attributes":{"broker":{"type":"string","required":true},"topic":{"type":"string","optional":true}}},"max_items":1},"kinesis_settings":{"nesting_mode":"list","block":{"attributes":{"message_format":{"type":"string","optional":true},"service_access_role_arn":{"type":"string","optional":true},"stream_arn":{"type":"string","optional":true}}},"max_items":1},"mongodb_settings":{"nesting_mode":"list","block":{"attributes":{"auth_mechanism":{"type":"string","optional":true},"auth_source":{"type":"string","optional":true},"auth_type":{"type":"string","optional":true},"docs_to_investigate":{"type":"string","optional":true},"extract_doc_id":{"type":"string","optional":true},"nesting_level":{"type":"string","optional":true}}},"max_items":1},"s3_settings":{"nesting_mode":"list","block":{"attributes":{"bucket_folder":{"type":"string","optional":true},"bucket_name":{"type":"string","optional":true},"compression_type":{"type":"string","optional":true},"csv_delimiter":{"type":"string","optional":true},"csv_row_delimiter":{"type":"string","optional":true},"external_table_definition":{"type":"string","optional":true},"service_access_role_arn":{"type":"string","optional":true}}},"max_items":1}}}},"aws_dms_event_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"event_categories":{"type":["set","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"sns_topic_arn":{"type":"string","required":true},"source_ids":{"type":["set","string"],"optional":true},"source_type":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dms_replication_instance":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","optional":true,"computed":true},"apply_immediately":{"type":"bool","optional":true},"auto_minor_version_upgrade":{"type":"bool","optional":true,"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true,"computed":true},"multi_az":{"type":"bool","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"publicly_accessible":{"type":"bool","optional":true,"computed":true},"replication_instance_arn":{"type":"string","computed":true},"replication_instance_class":{"type":"string","required":true},"replication_instance_id":{"type":"string","required":true},"replication_instance_private_ips":{"type":["list","string"],"computed":true},"replication_instance_public_ips":{"type":["list","string"],"computed":true},"replication_subnet_group_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dms_replication_subnet_group":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"replication_subnet_group_arn":{"type":"string","computed":true},"replication_subnet_group_description":{"type":"string","required":true},"replication_subnet_group_id":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}}}},"aws_dms_replication_task":{"version":0,"block":{"attributes":{"cdc_start_time":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"migration_type":{"type":"string","required":true},"replication_instance_arn":{"type":"string","required":true},"replication_task_arn":{"type":"string","computed":true},"replication_task_id":{"type":"string","required":true},"replication_task_settings":{"type":"string","optional":true},"source_endpoint_arn":{"type":"string","required":true},"table_mappings":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"target_endpoint_arn":{"type":"string","required":true}}}},"aws_docdb_cluster":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"backup_retention_period":{"type":"number","optional":true},"cluster_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier_prefix":{"type":"string","optional":true,"computed":true},"cluster_members":{"type":["set","string"],"optional":true,"computed":true},"cluster_resource_id":{"type":"string","computed":true},"db_cluster_parameter_group_name":{"type":"string","optional":true,"computed":true},"db_subnet_group_name":{"type":"string","optional":true,"computed":true},"deletion_protection":{"type":"bool","optional":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"optional":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"master_password":{"type":"string","optional":true,"sensitive":true},"master_username":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"reader_endpoint":{"type":"string","computed":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_identifier":{"type":"string","optional":true},"storage_encrypted":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_docdb_cluster_instance":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"ca_cert_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier":{"type":"string","required":true},"db_subnet_group_name":{"type":"string","computed":true},"dbi_resource_id":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"instance_class":{"type":"string","required":true},"kms_key_id":{"type":"string","computed":true},"port":{"type":"number","computed":true},"preferred_backup_window":{"type":"string","computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"promotion_tier":{"type":"number","optional":true},"publicly_accessible":{"type":"bool","computed":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true},"writer":{"type":"bool","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_docdb_cluster_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_docdb_cluster_snapshot":{"version":0,"block":{"attributes":{"availability_zones":{"type":["list","string"],"computed":true},"db_cluster_identifier":{"type":"string","required":true},"db_cluster_snapshot_arn":{"type":"string","computed":true},"db_cluster_snapshot_identifier":{"type":"string","required":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_type":{"type":"string","computed":true},"source_db_cluster_snapshot_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_docdb_subnet_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_dx_bgp_peer":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"bgp_peer_id":{"type":"string","computed":true},"bgp_status":{"type":"string","computed":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"virtual_interface_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_connection":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bandwidth":{"type":"string","required":true},"has_logical_redundancy":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"location":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_dx_connection_association":{"version":0,"block":{"attributes":{"connection_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"lag_id":{"type":"string","required":true}}}},"aws_dx_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_gateway_association":{"version":1,"block":{"attributes":{"allowed_prefixes":{"type":["set","string"],"optional":true,"computed":true},"associated_gateway_id":{"type":"string","optional":true,"computed":true},"associated_gateway_owner_account_id":{"type":"string","optional":true,"computed":true},"associated_gateway_type":{"type":"string","computed":true},"dx_gateway_association_id":{"type":"string","computed":true},"dx_gateway_id":{"type":"string","required":true},"dx_gateway_owner_account_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"proposal_id":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dx_gateway_association_proposal":{"version":0,"block":{"attributes":{"allowed_prefixes":{"type":["set","string"],"optional":true,"computed":true},"associated_gateway_id":{"type":"string","required":true},"associated_gateway_owner_account_id":{"type":"string","computed":true},"associated_gateway_type":{"type":"string","computed":true},"dx_gateway_id":{"type":"string","required":true},"dx_gateway_owner_account_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_dx_hosted_private_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"mtu":{"type":"number","optional":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","required":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_private_virtual_interface_accepter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"dx_gateway_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"virtual_interface_id":{"type":"string","required":true},"vpn_gateway_id":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_public_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","required":true},"route_filter_prefixes":{"type":["set","string"],"required":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_public_virtual_interface_accepter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"virtual_interface_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_transit_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"mtu":{"type":"number","optional":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","required":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_transit_virtual_interface_accepter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"dx_gateway_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"virtual_interface_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_lag":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"connections_bandwidth":{"type":"string","required":true},"force_destroy":{"type":"bool","optional":true},"has_logical_redundancy":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"location":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_dx_private_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"dx_gateway_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"mtu":{"type":"number","optional":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vlan":{"type":"number","required":true},"vpn_gateway_id":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dx_public_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"route_filter_prefixes":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_transit_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"dx_gateway_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"mtu":{"type":"number","optional":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dynamodb_global_table":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"replica":{"nesting_mode":"set","block":{"attributes":{"region_name":{"type":"string","required":true}}},"min_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dynamodb_table":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"billing_mode":{"type":"string","optional":true},"hash_key":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"range_key":{"type":"string","optional":true},"read_capacity":{"type":"number","optional":true},"stream_arn":{"type":"string","computed":true},"stream_enabled":{"type":"bool","optional":true},"stream_label":{"type":"string","computed":true},"stream_view_type":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"write_capacity":{"type":"number","optional":true}},"block_types":{"attribute":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"type":{"type":"string","required":true}}},"min_items":1},"global_secondary_index":{"nesting_mode":"set","block":{"attributes":{"hash_key":{"type":"string","required":true},"name":{"type":"string","required":true},"non_key_attributes":{"type":["list","string"],"optional":true},"projection_type":{"type":"string","required":true},"range_key":{"type":"string","optional":true},"read_capacity":{"type":"number","optional":true},"write_capacity":{"type":"number","optional":true}}}},"local_secondary_index":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"non_key_attributes":{"type":["list","string"],"optional":true},"projection_type":{"type":"string","required":true},"range_key":{"type":"string","required":true}}}},"point_in_time_recovery":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true}}},"max_items":1},"replica":{"nesting_mode":"set","block":{"attributes":{"region_name":{"type":"string","required":true}}}},"server_side_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true},"kms_key_arn":{"type":"string","optional":true,"computed":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}},"ttl":{"nesting_mode":"list","block":{"attributes":{"attribute_name":{"type":"string","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_dynamodb_table_item":{"version":0,"block":{"attributes":{"hash_key":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"item":{"type":"string","required":true},"range_key":{"type":"string","optional":true},"table_name":{"type":"string","required":true}}}},"aws_ebs_default_kms_key":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"key_arn":{"type":"string","required":true}}}},"aws_ebs_encryption_by_default":{"version":0,"block":{"attributes":{"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ebs_snapshot":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"data_encryption_key_id":{"type":"string","computed":true},"description":{"type":"string","optional":true},"encrypted":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"owner_alias":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"volume_id":{"type":"string","required":true},"volume_size":{"type":"number","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_ebs_snapshot_copy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"data_encryption_key_id":{"type":"string","computed":true},"description":{"type":"string","optional":true},"encrypted":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"owner_alias":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"source_region":{"type":"string","required":true},"source_snapshot_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","computed":true}}}},"aws_ebs_volume":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"multi_attach_enabled":{"type":"bool","optional":true},"outpost_arn":{"type":"string","optional":true},"size":{"type":"number","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true,"computed":true}}}},"aws_ec2_availability_zone_group":{"version":0,"block":{"attributes":{"group_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"opt_in_status":{"type":"string","required":true}}}},"aws_ec2_capacity_reservation":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","required":true},"ebs_optimized":{"type":"bool","optional":true},"end_date":{"type":"string","optional":true},"end_date_type":{"type":"string","optional":true},"ephemeral_storage":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_count":{"type":"number","required":true},"instance_match_criteria":{"type":"string","optional":true},"instance_platform":{"type":"string","required":true},"instance_type":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"tenancy":{"type":"string","optional":true}}}},"aws_ec2_client_vpn_authorization_rule":{"version":0,"block":{"attributes":{"access_group_id":{"type":"string","optional":true},"authorize_all_groups":{"type":"bool","optional":true},"client_vpn_endpoint_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"target_network_cidr":{"type":"string","required":true}}}},"aws_ec2_client_vpn_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"client_cidr_block":{"type":"string","required":true},"description":{"type":"string","optional":true},"dns_name":{"type":"string","computed":true},"dns_servers":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"server_certificate_arn":{"type":"string","required":true},"split_tunnel":{"type":"bool","optional":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"transport_protocol":{"type":"string","optional":true}},"block_types":{"authentication_options":{"nesting_mode":"list","block":{"attributes":{"active_directory_id":{"type":"string","optional":true},"root_certificate_chain_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":2},"connection_log_options":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_log_group":{"type":"string","optional":true},"cloudwatch_log_stream":{"type":"string","optional":true},"enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"aws_ec2_client_vpn_network_association":{"version":0,"block":{"attributes":{"client_vpn_endpoint_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"security_groups":{"type":["set","string"],"computed":true},"status":{"type":"string","computed":true},"subnet_id":{"type":"string","required":true},"vpc_id":{"type":"string","computed":true}}}},"aws_ec2_client_vpn_route":{"version":0,"block":{"attributes":{"client_vpn_endpoint_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"origin":{"type":"string","computed":true},"target_vpc_subnet_id":{"type":"string","required":true},"type":{"type":"string","computed":true}}}},"aws_ec2_fleet":{"version":0,"block":{"attributes":{"excess_capacity_termination_policy":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"replace_unhealthy_instances":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"terminate_instances":{"type":"bool","optional":true},"terminate_instances_with_expiration":{"type":"bool","optional":true},"type":{"type":"string","optional":true}},"block_types":{"launch_template_config":{"nesting_mode":"list","block":{"block_types":{"launch_template_specification":{"nesting_mode":"list","block":{"attributes":{"launch_template_id":{"type":"string","optional":true},"launch_template_name":{"type":"string","optional":true},"version":{"type":"string","required":true}}},"min_items":1,"max_items":1},"override":{"nesting_mode":"list","block":{"attributes":{"availability_zone":{"type":"string","optional":true},"instance_type":{"type":"string","optional":true},"max_price":{"type":"string","optional":true},"priority":{"type":"number","optional":true},"subnet_id":{"type":"string","optional":true},"weighted_capacity":{"type":"number","optional":true}}},"max_items":50}}},"min_items":1,"max_items":1},"on_demand_options":{"nesting_mode":"list","block":{"attributes":{"allocation_strategy":{"type":"string","optional":true}}},"max_items":1},"spot_options":{"nesting_mode":"list","block":{"attributes":{"allocation_strategy":{"type":"string","optional":true},"instance_interruption_behavior":{"type":"string","optional":true},"instance_pools_to_use_count":{"type":"number","optional":true}}},"max_items":1},"target_capacity_specification":{"nesting_mode":"list","block":{"attributes":{"default_target_capacity_type":{"type":"string","required":true},"on_demand_target_capacity":{"type":"number","optional":true},"spot_target_capacity":{"type":"number","optional":true},"total_target_capacity":{"type":"number","required":true}}},"min_items":1,"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_ec2_local_gateway_route":{"version":0,"block":{"attributes":{"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"local_gateway_route_table_id":{"type":"string","required":true},"local_gateway_virtual_interface_group_id":{"type":"string","required":true}}}},"aws_ec2_local_gateway_route_table_vpc_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_gateway_id":{"type":"string","computed":true},"local_gateway_route_table_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}}}},"aws_ec2_tag":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","required":true},"resource_id":{"type":"string","required":true},"value":{"type":"string","required":true}}}},"aws_ec2_traffic_mirror_filter":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"network_services":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_ec2_traffic_mirror_filter_rule":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"protocol":{"type":"number","optional":true},"rule_action":{"type":"string","required":true},"rule_number":{"type":"number","required":true},"source_cidr_block":{"type":"string","required":true},"traffic_direction":{"type":"string","required":true},"traffic_mirror_filter_id":{"type":"string","required":true}},"block_types":{"destination_port_range":{"nesting_mode":"list","block":{"attributes":{"from_port":{"type":"number","optional":true},"to_port":{"type":"number","optional":true}}},"max_items":1},"source_port_range":{"nesting_mode":"list","block":{"attributes":{"from_port":{"type":"number","optional":true},"to_port":{"type":"number","optional":true}}},"max_items":1}}}},"aws_ec2_traffic_mirror_session":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","required":true},"packet_length":{"type":"number","optional":true},"session_number":{"type":"number","required":true},"tags":{"type":["map","string"],"optional":true},"traffic_mirror_filter_id":{"type":"string","required":true},"traffic_mirror_target_id":{"type":"string","required":true},"virtual_network_id":{"type":"number","optional":true,"computed":true}}}},"aws_ec2_traffic_mirror_target":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","optional":true},"network_load_balancer_arn":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_ec2_transit_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"number","optional":true},"arn":{"type":"string","computed":true},"association_default_route_table_id":{"type":"string","computed":true},"auto_accept_shared_attachments":{"type":"string","optional":true},"default_route_table_association":{"type":"string","optional":true},"default_route_table_propagation":{"type":"string","optional":true},"description":{"type":"string","optional":true},"dns_support":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"propagation_default_route_table_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpn_ecmp_support":{"type":"string","optional":true}}}},"aws_ec2_transit_gateway_peering_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"peer_account_id":{"type":"string","optional":true,"computed":true},"peer_region":{"type":"string","required":true},"peer_transit_gateway_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_peering_attachment_accepter":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"peer_account_id":{"type":"string","computed":true},"peer_region":{"type":"string","computed":true},"peer_transit_gateway_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_attachment_id":{"type":"string","required":true},"transit_gateway_id":{"type":"string","computed":true}}}},"aws_ec2_transit_gateway_route":{"version":0,"block":{"attributes":{"blackhole":{"type":"bool","optional":true},"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"transit_gateway_attachment_id":{"type":"string","optional":true},"transit_gateway_route_table_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_route_table":{"version":0,"block":{"attributes":{"default_association_route_table":{"type":"bool","computed":true},"default_propagation_route_table":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_route_table_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_id":{"type":"string","computed":true},"resource_type":{"type":"string","computed":true},"transit_gateway_attachment_id":{"type":"string","required":true},"transit_gateway_route_table_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_route_table_propagation":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_id":{"type":"string","computed":true},"resource_type":{"type":"string","computed":true},"transit_gateway_attachment_id":{"type":"string","required":true},"transit_gateway_route_table_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_vpc_attachment":{"version":0,"block":{"attributes":{"dns_support":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_support":{"type":"string","optional":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_default_route_table_association":{"type":"bool","optional":true},"transit_gateway_default_route_table_propagation":{"type":"bool","optional":true},"transit_gateway_id":{"type":"string","required":true},"vpc_id":{"type":"string","required":true},"vpc_owner_id":{"type":"string","computed":true}}}},"aws_ec2_transit_gateway_vpc_attachment_accepter":{"version":0,"block":{"attributes":{"dns_support":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_support":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_attachment_id":{"type":"string","required":true},"transit_gateway_default_route_table_association":{"type":"bool","optional":true},"transit_gateway_default_route_table_propagation":{"type":"bool","optional":true},"transit_gateway_id":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true},"vpc_owner_id":{"type":"string","computed":true}}}},"aws_ecr_lifecycle_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true},"registry_id":{"type":"string","computed":true},"repository":{"type":"string","required":true}}}},"aws_ecr_repository":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"image_tag_mutability":{"type":"string","optional":true},"name":{"type":"string","required":true},"registry_id":{"type":"string","computed":true},"repository_url":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"encryption_configuration":{"nesting_mode":"list","block":{"attributes":{"encryption_type":{"type":"string","optional":true},"kms_key":{"type":"string","optional":true,"computed":true}}}},"image_scanning_configuration":{"nesting_mode":"list","block":{"attributes":{"scan_on_push":{"type":"bool","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true}}}}}}},"aws_ecr_repository_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true},"registry_id":{"type":"string","computed":true},"repository":{"type":"string","required":true}}}},"aws_ecs_capacity_provider":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"auto_scaling_group_provider":{"nesting_mode":"list","block":{"attributes":{"auto_scaling_group_arn":{"type":"string","required":true},"managed_termination_protection":{"type":"string","optional":true,"computed":true}},"block_types":{"managed_scaling":{"nesting_mode":"list","block":{"attributes":{"maximum_scaling_step_size":{"type":"number","optional":true,"computed":true},"minimum_scaling_step_size":{"type":"number","optional":true,"computed":true},"status":{"type":"string","optional":true,"computed":true},"target_capacity":{"type":"number","optional":true,"computed":true}}},"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_ecs_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"capacity_providers":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"default_capacity_provider_strategy":{"nesting_mode":"set","block":{"attributes":{"base":{"type":"number","optional":true},"capacity_provider":{"type":"string","required":true},"weight":{"type":"number","optional":true}}}},"setting":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_ecs_service":{"version":0,"block":{"attributes":{"cluster":{"type":"string","optional":true,"computed":true},"deployment_maximum_percent":{"type":"number","optional":true},"deployment_minimum_healthy_percent":{"type":"number","optional":true},"desired_count":{"type":"number","optional":true},"enable_ecs_managed_tags":{"type":"bool","optional":true},"force_new_deployment":{"type":"bool","optional":true},"health_check_grace_period_seconds":{"type":"number","optional":true},"iam_role":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"launch_type":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"platform_version":{"type":"string","optional":true,"computed":true},"propagate_tags":{"type":"string","optional":true},"scheduling_strategy":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"task_definition":{"type":"string","optional":true}},"block_types":{"capacity_provider_strategy":{"nesting_mode":"set","block":{"attributes":{"base":{"type":"number","optional":true},"capacity_provider":{"type":"string","required":true},"weight":{"type":"number","optional":true}}}},"deployment_controller":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","optional":true}}},"max_items":1},"load_balancer":{"nesting_mode":"set","block":{"attributes":{"container_name":{"type":"string","required":true},"container_port":{"type":"number","required":true},"elb_name":{"type":"string","optional":true},"target_group_arn":{"type":"string","optional":true}}}},"network_configuration":{"nesting_mode":"list","block":{"attributes":{"assign_public_ip":{"type":"bool","optional":true},"security_groups":{"type":["set","string"],"optional":true},"subnets":{"type":["set","string"],"required":true}}},"max_items":1},"ordered_placement_strategy":{"nesting_mode":"list","block":{"attributes":{"field":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"max_items":5},"placement_constraints":{"nesting_mode":"set","block":{"attributes":{"expression":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"max_items":10},"service_registries":{"nesting_mode":"list","block":{"attributes":{"container_name":{"type":"string","optional":true},"container_port":{"type":"number","optional":true},"port":{"type":"number","optional":true},"registry_arn":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true}}}}}}},"aws_ecs_task_definition":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"container_definitions":{"type":"string","required":true},"cpu":{"type":"string","optional":true},"execution_role_arn":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ipc_mode":{"type":"string","optional":true},"memory":{"type":"string","optional":true},"network_mode":{"type":"string","optional":true,"computed":true},"pid_mode":{"type":"string","optional":true},"requires_compatibilities":{"type":["set","string"],"optional":true},"revision":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true},"task_role_arn":{"type":"string","optional":true}},"block_types":{"inference_accelerator":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"device_type":{"type":"string","required":true}}}},"placement_constraints":{"nesting_mode":"set","block":{"attributes":{"expression":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"max_items":10},"proxy_configuration":{"nesting_mode":"list","block":{"attributes":{"container_name":{"type":"string","required":true},"properties":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true}}},"max_items":1},"volume":{"nesting_mode":"set","block":{"attributes":{"host_path":{"type":"string","optional":true},"name":{"type":"string","required":true}},"block_types":{"docker_volume_configuration":{"nesting_mode":"list","block":{"attributes":{"autoprovision":{"type":"bool","optional":true},"driver":{"type":"string","optional":true},"driver_opts":{"type":["map","string"],"optional":true},"labels":{"type":["map","string"],"optional":true},"scope":{"type":"string","optional":true,"computed":true}}},"max_items":1},"efs_volume_configuration":{"nesting_mode":"list","block":{"attributes":{"file_system_id":{"type":"string","required":true},"root_directory":{"type":"string","optional":true},"transit_encryption":{"type":"string","optional":true},"transit_encryption_port":{"type":"number","optional":true}},"block_types":{"authorization_config":{"nesting_mode":"list","block":{"attributes":{"access_point_id":{"type":"string","optional":true},"iam":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}}}}},"aws_efs_access_point":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"file_system_arn":{"type":"string","computed":true},"file_system_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"posix_user":{"nesting_mode":"list","block":{"attributes":{"gid":{"type":"number","required":true},"secondary_gids":{"type":["set","number"],"optional":true},"uid":{"type":"number","required":true}}},"max_items":1},"root_directory":{"nesting_mode":"list","block":{"attributes":{"path":{"type":"string","optional":true,"computed":true}},"block_types":{"creation_info":{"nesting_mode":"list","block":{"attributes":{"owner_gid":{"type":"number","required":true},"owner_uid":{"type":"number","required":true},"permissions":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}}},"aws_efs_file_system":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_token":{"type":"string","optional":true,"computed":true},"dns_name":{"type":"string","computed":true},"encrypted":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"performance_mode":{"type":"string","optional":true,"computed":true},"provisioned_throughput_in_mibps":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true},"throughput_mode":{"type":"string","optional":true}},"block_types":{"lifecycle_policy":{"nesting_mode":"list","block":{"attributes":{"transition_to_ia":{"type":"string","required":true}}},"max_items":1}}}},"aws_efs_file_system_policy":{"version":0,"block":{"attributes":{"file_system_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true}}}},"aws_efs_mount_target":{"version":0,"block":{"attributes":{"availability_zone_id":{"type":"string","computed":true},"availability_zone_name":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"file_system_arn":{"type":"string","computed":true},"file_system_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","optional":true,"computed":true},"mount_target_dns_name":{"type":"string","computed":true},"network_interface_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"subnet_id":{"type":"string","required":true}}}},"aws_egress_only_internet_gateway":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}}}},"aws_eip":{"version":0,"block":{"attributes":{"allocation_id":{"type":"string","computed":true},"associate_with_private_ip":{"type":"string","optional":true},"association_id":{"type":"string","computed":true},"customer_owned_ip":{"type":"string","computed":true},"customer_owned_ipv4_pool":{"type":"string","optional":true},"domain":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instance":{"type":"string","optional":true,"computed":true},"network_interface":{"type":"string","optional":true,"computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"public_ipv4_pool":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc":{"type":"bool","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true},"read":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_eip_association":{"version":0,"block":{"attributes":{"allocation_id":{"type":"string","optional":true,"computed":true},"allow_reassociation":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","optional":true,"computed":true},"private_ip_address":{"type":"string","optional":true,"computed":true},"public_ip":{"type":"string","optional":true,"computed":true}}}},"aws_eks_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_authority":{"type":["list",["object",{"data":"string"}]],"computed":true},"created_at":{"type":"string","computed":true},"enabled_cluster_log_types":{"type":["set","string"],"optional":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identity":{"type":["list",["object",{"oidc":["list",["object",{"issuer":"string"}]]}]],"computed":true},"name":{"type":"string","required":true},"platform_version":{"type":"string","computed":true},"role_arn":{"type":"string","required":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","optional":true,"computed":true}},"block_types":{"encryption_config":{"nesting_mode":"list","block":{"attributes":{"resources":{"type":["set","string"],"required":true}},"block_types":{"provider":{"nesting_mode":"list","block":{"attributes":{"key_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}},"vpc_config":{"nesting_mode":"list","block":{"attributes":{"cluster_security_group_id":{"type":"string","computed":true},"endpoint_private_access":{"type":"bool","optional":true},"endpoint_public_access":{"type":"bool","optional":true},"public_access_cidrs":{"type":["set","string"],"optional":true,"computed":true},"security_group_ids":{"type":["set","string"],"optional":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","computed":true}}},"min_items":1,"max_items":1}}}},"aws_eks_fargate_profile":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"fargate_profile_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"pod_execution_role_arn":{"type":"string","required":true},"status":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"selector":{"nesting_mode":"set","block":{"attributes":{"labels":{"type":["map","string"],"optional":true},"namespace":{"type":"string","required":true}}},"min_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_eks_node_group":{"version":0,"block":{"attributes":{"ami_type":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"disk_size":{"type":"number","optional":true,"computed":true},"force_update_version":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_types":{"type":["list","string"],"optional":true,"computed":true},"labels":{"type":["map","string"],"optional":true},"node_group_name":{"type":"string","required":true},"node_role_arn":{"type":"string","required":true},"release_version":{"type":"string","optional":true,"computed":true},"resources":{"type":["list",["object",{"autoscaling_groups":["list",["object",{"name":"string"}]],"remote_access_security_group_id":"string"}]],"computed":true},"status":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","optional":true,"computed":true}},"block_types":{"remote_access":{"nesting_mode":"list","block":{"attributes":{"ec2_ssh_key":{"type":"string","optional":true},"source_security_group_ids":{"type":["set","string"],"optional":true}}},"max_items":1},"scaling_config":{"nesting_mode":"list","block":{"attributes":{"desired_size":{"type":"number","required":true},"max_size":{"type":"number","required":true},"min_size":{"type":"number","required":true}}},"min_items":1,"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_elastic_beanstalk_application":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"appversion_lifecycle":{"nesting_mode":"list","block":{"attributes":{"delete_source_from_s3":{"type":"bool","optional":true},"max_age_in_days":{"type":"number","optional":true},"max_count":{"type":"number","optional":true},"service_role":{"type":"string","required":true}}},"max_items":1}}}},"aws_elastic_beanstalk_application_version":{"version":0,"block":{"attributes":{"application":{"type":"string","required":true},"arn":{"type":"string","computed":true},"bucket":{"type":"string","required":true},"description":{"type":"string","optional":true},"force_delete":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_elastic_beanstalk_configuration_template":{"version":0,"block":{"attributes":{"application":{"type":"string","required":true},"description":{"type":"string","optional":true},"environment_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"solution_stack_name":{"type":"string","optional":true}},"block_types":{"setting":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"resource":{"type":"string","optional":true},"value":{"type":"string","required":true}}}}}}},"aws_elastic_beanstalk_environment":{"version":1,"block":{"attributes":{"all_settings":{"type":["set",["object",{"name":"string","namespace":"string","resource":"string","value":"string"}]],"computed":true},"application":{"type":"string","required":true},"arn":{"type":"string","computed":true},"autoscaling_groups":{"type":["list","string"],"computed":true},"cname":{"type":"string","computed":true},"cname_prefix":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"endpoint_url":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instances":{"type":["list","string"],"computed":true},"launch_configurations":{"type":["list","string"],"computed":true},"load_balancers":{"type":["list","string"],"computed":true},"name":{"type":"string","required":true},"platform_arn":{"type":"string","optional":true,"computed":true},"poll_interval":{"type":"string","optional":true},"queues":{"type":["list","string"],"computed":true},"solution_stack_name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"template_name":{"type":"string","optional":true},"tier":{"type":"string","optional":true},"triggers":{"type":["list","string"],"computed":true},"version_label":{"type":"string","optional":true,"computed":true},"wait_for_ready_timeout":{"type":"string","optional":true}},"block_types":{"setting":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"resource":{"type":"string","optional":true},"value":{"type":"string","required":true}}}}}}},"aws_elasticache_cluster":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"az_mode":{"type":"string","optional":true,"computed":true},"cache_nodes":{"type":["list",["object",{"address":"string","availability_zone":"string","id":"string","port":"number"}]],"computed":true},"cluster_address":{"type":"string","computed":true},"cluster_id":{"type":"string","required":true},"configuration_endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true,"computed":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"maintenance_window":{"type":"string","optional":true,"computed":true},"node_type":{"type":"string","optional":true,"computed":true},"notification_topic_arn":{"type":"string","optional":true},"num_cache_nodes":{"type":"number","optional":true,"computed":true},"parameter_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"preferred_availability_zones":{"type":["list","string"],"optional":true},"replication_group_id":{"type":"string","optional":true,"computed":true},"security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"security_group_names":{"type":["set","string"],"optional":true,"computed":true},"snapshot_arns":{"type":["set","string"],"optional":true},"snapshot_name":{"type":"string","optional":true},"snapshot_retention_limit":{"type":"number","optional":true},"snapshot_window":{"type":"string","optional":true,"computed":true},"subnet_group_name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_elasticache_parameter_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_elasticache_replication_group":{"version":1,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"at_rest_encryption_enabled":{"type":"bool","optional":true},"auth_token":{"type":"string","optional":true,"sensitive":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"automatic_failover_enabled":{"type":"bool","optional":true},"availability_zones":{"type":["set","string"],"optional":true},"configuration_endpoint_address":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"maintenance_window":{"type":"string","optional":true,"computed":true},"member_clusters":{"type":["set","string"],"computed":true},"node_type":{"type":"string","optional":true,"computed":true},"notification_topic_arn":{"type":"string","optional":true},"number_cache_clusters":{"type":"number","optional":true,"computed":true},"parameter_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"primary_endpoint_address":{"type":"string","computed":true},"replication_group_description":{"type":"string","required":true},"replication_group_id":{"type":"string","required":true},"security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"security_group_names":{"type":["set","string"],"optional":true,"computed":true},"snapshot_arns":{"type":["set","string"],"optional":true},"snapshot_name":{"type":"string","optional":true},"snapshot_retention_limit":{"type":"number","optional":true},"snapshot_window":{"type":"string","optional":true,"computed":true},"subnet_group_name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"transit_encryption_enabled":{"type":"bool","optional":true}},"block_types":{"cluster_mode":{"nesting_mode":"list","block":{"attributes":{"num_node_groups":{"type":"number","required":true},"replicas_per_node_group":{"type":"number","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_elasticache_security_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"security_group_names":{"type":["set","string"],"required":true}}}},"aws_elasticache_subnet_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true}}}},"aws_elasticsearch_domain":{"version":0,"block":{"attributes":{"access_policies":{"type":"string","optional":true,"computed":true},"advanced_options":{"type":["map","string"],"optional":true,"computed":true},"arn":{"type":"string","computed":true},"domain_id":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"elasticsearch_version":{"type":"string","optional":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kibana_endpoint":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"advanced_security_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true},"internal_user_database_enabled":{"type":"bool","optional":true}},"block_types":{"master_user_options":{"nesting_mode":"list","block":{"attributes":{"master_user_arn":{"type":"string","optional":true},"master_user_name":{"type":"string","optional":true},"master_user_password":{"type":"string","optional":true,"sensitive":true}}},"max_items":1}}},"max_items":1},"cluster_config":{"nesting_mode":"list","block":{"attributes":{"dedicated_master_count":{"type":"number","optional":true},"dedicated_master_enabled":{"type":"bool","optional":true},"dedicated_master_type":{"type":"string","optional":true},"instance_count":{"type":"number","optional":true},"instance_type":{"type":"string","optional":true},"warm_count":{"type":"number","optional":true},"warm_enabled":{"type":"bool","optional":true},"warm_type":{"type":"string","optional":true},"zone_awareness_enabled":{"type":"bool","optional":true}},"block_types":{"zone_awareness_config":{"nesting_mode":"list","block":{"attributes":{"availability_zone_count":{"type":"number","optional":true}}},"max_items":1}}},"max_items":1},"cognito_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"identity_pool_id":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"user_pool_id":{"type":"string","required":true}}},"max_items":1},"domain_endpoint_options":{"nesting_mode":"list","block":{"attributes":{"enforce_https":{"type":"bool","required":true},"tls_security_policy":{"type":"string","optional":true,"computed":true}}},"max_items":1},"ebs_options":{"nesting_mode":"list","block":{"attributes":{"ebs_enabled":{"type":"bool","required":true},"iops":{"type":"number","optional":true},"volume_size":{"type":"number","optional":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1},"encrypt_at_rest":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true},"kms_key_id":{"type":"string","optional":true,"computed":true}}},"max_items":1},"log_publishing_options":{"nesting_mode":"set","block":{"attributes":{"cloudwatch_log_group_arn":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"log_type":{"type":"string","required":true}}}},"node_to_node_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true}}},"max_items":1},"snapshot_options":{"nesting_mode":"list","block":{"attributes":{"automated_snapshot_start_hour":{"type":"number","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"update":{"type":"string","optional":true}}}},"vpc_options":{"nesting_mode":"list","block":{"attributes":{"availability_zones":{"type":["set","string"],"computed":true},"security_group_ids":{"type":["set","string"],"optional":true},"subnet_ids":{"type":["set","string"],"optional":true},"vpc_id":{"type":"string","computed":true}}},"max_items":1}}}},"aws_elasticsearch_domain_policy":{"version":0,"block":{"attributes":{"access_policies":{"type":"string","required":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_elastictranscoder_pipeline":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_kms_key_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"input_bucket":{"type":"string","required":true},"name":{"type":"string","optional":true,"computed":true},"output_bucket":{"type":"string","optional":true,"computed":true},"role":{"type":"string","required":true}},"block_types":{"content_config":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","optional":true,"computed":true},"storage_class":{"type":"string","optional":true}}},"max_items":1},"content_config_permissions":{"nesting_mode":"set","block":{"attributes":{"access":{"type":["list","string"],"optional":true},"grantee":{"type":"string","optional":true},"grantee_type":{"type":"string","optional":true}}}},"notifications":{"nesting_mode":"list","block":{"attributes":{"completed":{"type":"string","optional":true},"error":{"type":"string","optional":true},"progressing":{"type":"string","optional":true},"warning":{"type":"string","optional":true}}},"max_items":1},"thumbnail_config":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","optional":true,"computed":true},"storage_class":{"type":"string","optional":true}}},"max_items":1},"thumbnail_config_permissions":{"nesting_mode":"set","block":{"attributes":{"access":{"type":["list","string"],"optional":true},"grantee":{"type":"string","optional":true},"grantee_type":{"type":"string","optional":true}}}}}}},"aws_elastictranscoder_preset":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"container":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"type":{"type":"string","optional":true,"computed":true},"video_codec_options":{"type":["map","string"],"optional":true}},"block_types":{"audio":{"nesting_mode":"list","block":{"attributes":{"audio_packing_mode":{"type":"string","optional":true},"bit_rate":{"type":"string","optional":true},"channels":{"type":"string","optional":true},"codec":{"type":"string","optional":true},"sample_rate":{"type":"string","optional":true}}},"max_items":1},"audio_codec_options":{"nesting_mode":"list","block":{"attributes":{"bit_depth":{"type":"string","optional":true},"bit_order":{"type":"string","optional":true},"profile":{"type":"string","optional":true},"signed":{"type":"string","optional":true}}},"max_items":1},"thumbnails":{"nesting_mode":"list","block":{"attributes":{"aspect_ratio":{"type":"string","optional":true},"format":{"type":"string","optional":true},"interval":{"type":"string","optional":true},"max_height":{"type":"string","optional":true},"max_width":{"type":"string","optional":true},"padding_policy":{"type":"string","optional":true},"resolution":{"type":"string","optional":true},"sizing_policy":{"type":"string","optional":true}}},"max_items":1},"video":{"nesting_mode":"list","block":{"attributes":{"aspect_ratio":{"type":"string","optional":true},"bit_rate":{"type":"string","optional":true},"codec":{"type":"string","optional":true},"display_aspect_ratio":{"type":"string","optional":true},"fixed_gop":{"type":"string","optional":true},"frame_rate":{"type":"string","optional":true},"keyframes_max_dist":{"type":"string","optional":true},"max_frame_rate":{"type":"string","optional":true,"computed":true},"max_height":{"type":"string","optional":true},"max_width":{"type":"string","optional":true},"padding_policy":{"type":"string","optional":true},"resolution":{"type":"string","optional":true},"sizing_policy":{"type":"string","optional":true}}},"max_items":1},"video_watermarks":{"nesting_mode":"set","block":{"attributes":{"horizontal_align":{"type":"string","optional":true},"horizontal_offset":{"type":"string","optional":true},"id":{"type":"string","optional":true},"max_height":{"type":"string","optional":true},"max_width":{"type":"string","optional":true},"opacity":{"type":"string","optional":true},"sizing_policy":{"type":"string","optional":true},"target":{"type":"string","optional":true},"vertical_align":{"type":"string","optional":true},"vertical_offset":{"type":"string","optional":true}}}}}}},"aws_elb":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"connection_draining":{"type":"bool","optional":true},"connection_draining_timeout":{"type":"number","optional":true},"cross_zone_load_balancing":{"type":"bool","optional":true},"dns_name":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","optional":true},"instances":{"type":["set","string"],"optional":true,"computed":true},"internal":{"type":"bool","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"source_security_group":{"type":"string","optional":true,"computed":true},"source_security_group_id":{"type":"string","computed":true},"subnets":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"zone_id":{"type":"string","computed":true}},"block_types":{"access_logs":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"bucket_prefix":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"interval":{"type":"number","optional":true}}},"max_items":1},"health_check":{"nesting_mode":"list","block":{"attributes":{"healthy_threshold":{"type":"number","required":true},"interval":{"type":"number","required":true},"target":{"type":"string","required":true},"timeout":{"type":"number","required":true},"unhealthy_threshold":{"type":"number","required":true}}},"max_items":1},"listener":{"nesting_mode":"set","block":{"attributes":{"instance_port":{"type":"number","required":true},"instance_protocol":{"type":"string","required":true},"lb_port":{"type":"number","required":true},"lb_protocol":{"type":"string","required":true},"ssl_certificate_id":{"type":"string","optional":true}}},"min_items":1}}}},"aws_elb_attachment":{"version":0,"block":{"attributes":{"elb":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instance":{"type":"string","required":true}}}},"aws_emr_cluster":{"version":0,"block":{"attributes":{"additional_info":{"type":"string","optional":true},"applications":{"type":["set","string"],"optional":true},"arn":{"type":"string","computed":true},"autoscaling_role":{"type":"string","optional":true},"cluster_state":{"type":"string","computed":true},"configurations":{"type":"string","optional":true},"configurations_json":{"type":"string","optional":true},"custom_ami_id":{"type":"string","optional":true},"ebs_root_volume_size":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"keep_job_flow_alive_when_no_steps":{"type":"bool","optional":true,"computed":true},"log_uri":{"type":"string","optional":true},"master_public_dns":{"type":"string","computed":true},"name":{"type":"string","required":true},"release_label":{"type":"string","required":true},"scale_down_behavior":{"type":"string","optional":true,"computed":true},"security_configuration":{"type":"string","optional":true},"service_role":{"type":"string","required":true},"step":{"type":["list",["object",{"action_on_failure":"string","hadoop_jar_step":["list",["object",{"args":["list","string"],"jar":"string","main_class":"string","properties":["map","string"]}]],"name":"string"}]],"optional":true,"computed":true},"step_concurrency_level":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true},"termination_protection":{"type":"bool","optional":true,"computed":true},"visible_to_all_users":{"type":"bool","optional":true}},"block_types":{"bootstrap_action":{"nesting_mode":"list","block":{"attributes":{"args":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"path":{"type":"string","required":true}}}},"core_instance_group":{"nesting_mode":"list","block":{"attributes":{"autoscaling_policy":{"type":"string","optional":true},"bid_price":{"type":"string","optional":true},"id":{"type":"string","computed":true},"instance_count":{"type":"number","optional":true},"instance_type":{"type":"string","required":true},"name":{"type":"string","optional":true}},"block_types":{"ebs_config":{"nesting_mode":"set","block":{"attributes":{"iops":{"type":"number","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","required":true},"volumes_per_instance":{"type":"number","optional":true}}}}}},"max_items":1},"ec2_attributes":{"nesting_mode":"list","block":{"attributes":{"additional_master_security_groups":{"type":"string","optional":true},"additional_slave_security_groups":{"type":"string","optional":true},"emr_managed_master_security_group":{"type":"string","optional":true,"computed":true},"emr_managed_slave_security_group":{"type":"string","optional":true,"computed":true},"instance_profile":{"type":"string","required":true},"key_name":{"type":"string","optional":true},"service_access_security_group":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true}}},"max_items":1},"kerberos_attributes":{"nesting_mode":"list","block":{"attributes":{"ad_domain_join_password":{"type":"string","optional":true,"sensitive":true},"ad_domain_join_user":{"type":"string","optional":true},"cross_realm_trust_principal_password":{"type":"string","optional":true,"sensitive":true},"kdc_admin_password":{"type":"string","required":true,"sensitive":true},"realm":{"type":"string","required":true}}},"max_items":1},"master_instance_group":{"nesting_mode":"list","block":{"attributes":{"bid_price":{"type":"string","optional":true},"id":{"type":"string","computed":true},"instance_count":{"type":"number","optional":true},"instance_type":{"type":"string","required":true},"name":{"type":"string","optional":true}},"block_types":{"ebs_config":{"nesting_mode":"set","block":{"attributes":{"iops":{"type":"number","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","required":true},"volumes_per_instance":{"type":"number","optional":true}}}}}},"max_items":1}}}},"aws_emr_instance_group":{"version":0,"block":{"attributes":{"autoscaling_policy":{"type":"string","optional":true},"bid_price":{"type":"string","optional":true},"cluster_id":{"type":"string","required":true},"configurations_json":{"type":"string","optional":true},"ebs_optimized":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_count":{"type":"number","optional":true},"instance_type":{"type":"string","required":true},"name":{"type":"string","optional":true},"running_instance_count":{"type":"number","computed":true},"status":{"type":"string","computed":true}},"block_types":{"ebs_config":{"nesting_mode":"set","block":{"attributes":{"iops":{"type":"number","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","required":true},"volumes_per_instance":{"type":"number","optional":true}}}}}}},"aws_emr_security_configuration":{"version":0,"block":{"attributes":{"configuration":{"type":"string","required":true},"creation_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true}}}},"aws_flow_log":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"eni_id":{"type":"string","optional":true},"iam_role_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"log_destination":{"type":"string","optional":true,"computed":true},"log_destination_type":{"type":"string","optional":true},"log_format":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","optional":true,"computed":true},"max_aggregation_interval":{"type":"number","optional":true},"subnet_id":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"traffic_type":{"type":"string","required":true},"vpc_id":{"type":"string","optional":true}}}},"aws_fms_admin_account":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_fsx_lustre_file_system":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deployment_type":{"type":"string","optional":true},"dns_name":{"type":"string","computed":true},"export_path":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"import_path":{"type":"string","optional":true},"imported_file_chunk_size":{"type":"number","optional":true,"computed":true},"network_interface_ids":{"type":["set","string"],"computed":true},"owner_id":{"type":"string","computed":true},"per_unit_storage_throughput":{"type":"number","optional":true},"security_group_ids":{"type":["set","string"],"optional":true},"storage_capacity":{"type":"number","required":true},"subnet_ids":{"type":["list","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"weekly_maintenance_start_time":{"type":"string","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_fsx_windows_file_system":{"version":0,"block":{"attributes":{"active_directory_id":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"automatic_backup_retention_days":{"type":"number","optional":true},"copy_tags_to_backups":{"type":"bool","optional":true},"daily_automatic_backup_start_time":{"type":"string","optional":true,"computed":true},"deployment_type":{"type":"string","optional":true},"dns_name":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"network_interface_ids":{"type":["set","string"],"computed":true},"owner_id":{"type":"string","computed":true},"preferred_file_server_ip":{"type":"string","computed":true},"preferred_subnet_id":{"type":"string","optional":true,"computed":true},"remote_administration_endpoint":{"type":"string","computed":true},"security_group_ids":{"type":["set","string"],"optional":true},"skip_final_backup":{"type":"bool","optional":true},"storage_capacity":{"type":"number","required":true},"storage_type":{"type":"string","optional":true},"subnet_ids":{"type":["list","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"throughput_capacity":{"type":"number","required":true},"vpc_id":{"type":"string","computed":true},"weekly_maintenance_start_time":{"type":"string","optional":true,"computed":true}},"block_types":{"self_managed_active_directory":{"nesting_mode":"list","block":{"attributes":{"dns_ips":{"type":["set","string"],"required":true},"domain_name":{"type":"string","required":true},"file_system_administrators_group":{"type":"string","optional":true},"organizational_unit_distinguished_name":{"type":"string","optional":true},"password":{"type":"string","required":true,"sensitive":true},"username":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_gamelift_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"routing_strategy":{"nesting_mode":"list","block":{"attributes":{"fleet_id":{"type":"string","optional":true},"message":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_gamelift_build":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"operating_system":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","optional":true}},"block_types":{"storage_location":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"key":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_gamelift_fleet":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"build_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"ec2_instance_type":{"type":"string","required":true},"fleet_type":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_role_arn":{"type":"string","optional":true},"log_paths":{"type":["list","string"],"computed":true},"metric_groups":{"type":["list","string"],"optional":true,"computed":true},"name":{"type":"string","required":true},"new_game_session_protection_policy":{"type":"string","optional":true},"operating_system":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"ec2_inbound_permission":{"nesting_mode":"list","block":{"attributes":{"from_port":{"type":"number","required":true},"ip_range":{"type":"string","required":true},"protocol":{"type":"string","required":true},"to_port":{"type":"number","required":true}}},"max_items":50},"resource_creation_limit_policy":{"nesting_mode":"list","block":{"attributes":{"new_game_sessions_per_creator":{"type":"number","optional":true},"policy_period_in_minutes":{"type":"number","optional":true}}},"max_items":1},"runtime_configuration":{"nesting_mode":"list","block":{"attributes":{"game_session_activation_timeout_seconds":{"type":"number","optional":true},"max_concurrent_game_session_activations":{"type":"number","optional":true}},"block_types":{"server_process":{"nesting_mode":"list","block":{"attributes":{"concurrent_executions":{"type":"number","required":true},"launch_path":{"type":"string","required":true},"parameters":{"type":"string","optional":true}}},"max_items":50}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_gamelift_game_session_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"destinations":{"type":["list","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"timeout_in_seconds":{"type":"number","optional":true}},"block_types":{"player_latency_policy":{"nesting_mode":"list","block":{"attributes":{"maximum_individual_player_latency_milliseconds":{"type":"number","required":true},"policy_duration_seconds":{"type":"number","optional":true}}}}}}},"aws_glacier_vault":{"version":0,"block":{"attributes":{"access_policy":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"location":{"type":"string","computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"notification":{"nesting_mode":"list","block":{"attributes":{"events":{"type":["set","string"],"required":true},"sns_topic":{"type":"string","required":true}}}}}}},"aws_glacier_vault_lock":{"version":0,"block":{"attributes":{"complete_lock":{"type":"bool","required":true},"id":{"type":"string","optional":true,"computed":true},"ignore_deletion_error":{"type":"bool","optional":true},"policy":{"type":"string","required":true},"vault_name":{"type":"string","required":true}}}},"aws_globalaccelerator_accelerator":{"version":0,"block":{"attributes":{"dns_name":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address_type":{"type":"string","optional":true},"ip_sets":{"type":["list",["object",{"ip_addresses":["list","string"],"ip_family":"string"}]],"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"attributes":{"nesting_mode":"list","block":{"attributes":{"flow_logs_enabled":{"type":"bool","optional":true},"flow_logs_s3_bucket":{"type":"string","optional":true},"flow_logs_s3_prefix":{"type":"string","optional":true}}},"max_items":1}}}},"aws_globalaccelerator_endpoint_group":{"version":0,"block":{"attributes":{"endpoint_group_region":{"type":"string","optional":true,"computed":true},"health_check_interval_seconds":{"type":"number","optional":true},"health_check_path":{"type":"string","optional":true},"health_check_port":{"type":"number","optional":true},"health_check_protocol":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true},"threshold_count":{"type":"number","optional":true},"traffic_dial_percentage":{"type":"number","optional":true}},"block_types":{"endpoint_configuration":{"nesting_mode":"set","block":{"attributes":{"endpoint_id":{"type":"string","optional":true},"weight":{"type":"number","optional":true}}},"max_items":10}}}},"aws_globalaccelerator_listener":{"version":0,"block":{"attributes":{"accelerator_arn":{"type":"string","required":true},"client_affinity":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"protocol":{"type":"string","required":true}},"block_types":{"port_range":{"nesting_mode":"set","block":{"attributes":{"from_port":{"type":"number","optional":true},"to_port":{"type":"number","optional":true}}},"min_items":1,"max_items":10}}}},"aws_glue_catalog_database":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"catalog_id":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"location_uri":{"type":"string","optional":true},"name":{"type":"string","required":true},"parameters":{"type":["map","string"],"optional":true}}}},"aws_glue_catalog_table":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"catalog_id":{"type":"string","optional":true,"computed":true},"database_name":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owner":{"type":"string","optional":true},"parameters":{"type":["map","string"],"optional":true},"retention":{"type":"number","optional":true},"table_type":{"type":"string","optional":true},"view_expanded_text":{"type":"string","optional":true},"view_original_text":{"type":"string","optional":true}},"block_types":{"partition_keys":{"nesting_mode":"list","block":{"attributes":{"comment":{"type":"string","optional":true},"name":{"type":"string","required":true},"type":{"type":"string","optional":true}}}},"storage_descriptor":{"nesting_mode":"list","block":{"attributes":{"bucket_columns":{"type":["list","string"],"optional":true},"compressed":{"type":"bool","optional":true},"input_format":{"type":"string","optional":true},"location":{"type":"string","optional":true},"number_of_buckets":{"type":"number","optional":true},"output_format":{"type":"string","optional":true},"parameters":{"type":["map","string"],"optional":true},"stored_as_sub_directories":{"type":"bool","optional":true}},"block_types":{"columns":{"nesting_mode":"list","block":{"attributes":{"comment":{"type":"string","optional":true},"name":{"type":"string","required":true},"type":{"type":"string","optional":true}}}},"ser_de_info":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","optional":true},"parameters":{"type":["map","string"],"optional":true},"serialization_library":{"type":"string","optional":true}}},"max_items":1},"skewed_info":{"nesting_mode":"list","block":{"attributes":{"skewed_column_names":{"type":["list","string"],"optional":true},"skewed_column_value_location_maps":{"type":["map","string"],"optional":true},"skewed_column_values":{"type":["list","string"],"optional":true}}},"max_items":1},"sort_columns":{"nesting_mode":"list","block":{"attributes":{"column":{"type":"string","required":true},"sort_order":{"type":"number","required":true}}}}}},"max_items":1}}}},"aws_glue_classifier":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"csv_classifier":{"nesting_mode":"list","block":{"attributes":{"allow_single_column":{"type":"bool","optional":true},"contains_header":{"type":"string","optional":true},"delimiter":{"type":"string","optional":true},"disable_value_trimming":{"type":"bool","optional":true},"header":{"type":["list","string"],"optional":true},"quote_symbol":{"type":"string","optional":true}}},"max_items":1},"grok_classifier":{"nesting_mode":"list","block":{"attributes":{"classification":{"type":"string","required":true},"custom_patterns":{"type":"string","optional":true},"grok_pattern":{"type":"string","required":true}}},"max_items":1},"json_classifier":{"nesting_mode":"list","block":{"attributes":{"json_path":{"type":"string","required":true}}},"max_items":1},"xml_classifier":{"nesting_mode":"list","block":{"attributes":{"classification":{"type":"string","required":true},"row_tag":{"type":"string","required":true}}},"max_items":1}}}},"aws_glue_connection":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"catalog_id":{"type":"string","optional":true,"computed":true},"connection_properties":{"type":["map","string"],"required":true,"sensitive":true},"connection_type":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"match_criteria":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true}},"block_types":{"physical_connection_requirements":{"nesting_mode":"list","block":{"attributes":{"availability_zone":{"type":"string","optional":true},"security_group_id_list":{"type":["list","string"],"optional":true},"subnet_id":{"type":"string","optional":true}}},"max_items":1}}}},"aws_glue_crawler":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"classifiers":{"type":["list","string"],"optional":true},"configuration":{"type":"string","optional":true},"database_name":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role":{"type":"string","required":true},"schedule":{"type":"string","optional":true},"security_configuration":{"type":"string","optional":true},"table_prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"catalog_target":{"nesting_mode":"list","block":{"attributes":{"database_name":{"type":"string","required":true},"tables":{"type":["list","string"],"required":true}}}},"dynamodb_target":{"nesting_mode":"list","block":{"attributes":{"path":{"type":"string","required":true}}}},"jdbc_target":{"nesting_mode":"list","block":{"attributes":{"connection_name":{"type":"string","required":true},"exclusions":{"type":["list","string"],"optional":true},"path":{"type":"string","required":true}}}},"s3_target":{"nesting_mode":"list","block":{"attributes":{"exclusions":{"type":["list","string"],"optional":true},"path":{"type":"string","required":true}}}},"schema_change_policy":{"nesting_mode":"list","block":{"attributes":{"delete_behavior":{"type":"string","optional":true},"update_behavior":{"type":"string","optional":true}}},"max_items":1}}}},"aws_glue_job":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"connections":{"type":["list","string"],"optional":true},"default_arguments":{"type":["map","string"],"optional":true},"description":{"type":"string","optional":true},"glue_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"max_capacity":{"type":"number","optional":true,"computed":true},"max_retries":{"type":"number","optional":true},"name":{"type":"string","required":true},"number_of_workers":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"security_configuration":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"timeout":{"type":"number","optional":true},"worker_type":{"type":"string","optional":true}},"block_types":{"command":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","optional":true},"python_version":{"type":"string","optional":true,"computed":true},"script_location":{"type":"string","required":true}}},"min_items":1,"max_items":1},"execution_property":{"nesting_mode":"list","block":{"attributes":{"max_concurrent_runs":{"type":"number","optional":true}}},"max_items":1},"notification_property":{"nesting_mode":"list","block":{"attributes":{"notify_delay_after":{"type":"number","optional":true}}},"max_items":1}}}},"aws_glue_security_configuration":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"encryption_configuration":{"nesting_mode":"list","block":{"block_types":{"cloudwatch_encryption":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_encryption_mode":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true}}},"min_items":1,"max_items":1},"job_bookmarks_encryption":{"nesting_mode":"list","block":{"attributes":{"job_bookmarks_encryption_mode":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true}}},"min_items":1,"max_items":1},"s3_encryption":{"nesting_mode":"list","block":{"attributes":{"kms_key_arn":{"type":"string","optional":true},"s3_encryption_mode":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_glue_trigger":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"schedule":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","required":true},"workflow_name":{"type":"string","optional":true}},"block_types":{"actions":{"nesting_mode":"list","block":{"attributes":{"arguments":{"type":["map","string"],"optional":true},"crawler_name":{"type":"string","optional":true},"job_name":{"type":"string","optional":true},"timeout":{"type":"number","optional":true}}},"min_items":1},"predicate":{"nesting_mode":"list","block":{"attributes":{"logical":{"type":"string","optional":true}},"block_types":{"conditions":{"nesting_mode":"list","block":{"attributes":{"crawl_state":{"type":"string","optional":true},"crawler_name":{"type":"string","optional":true},"job_name":{"type":"string","optional":true},"logical_operator":{"type":"string","optional":true},"state":{"type":"string","optional":true}}},"min_items":1}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_glue_workflow":{"version":0,"block":{"attributes":{"default_run_properties":{"type":["map","string"],"optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true}}}},"aws_guardduty_detector":{"version":0,"block":{"attributes":{"account_id":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"enable":{"type":"bool","optional":true},"finding_publishing_frequency":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_guardduty_invite_accepter":{"version":0,"block":{"attributes":{"detector_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"master_account_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_guardduty_ipset":{"version":0,"block":{"attributes":{"activate":{"type":"bool","required":true},"arn":{"type":"string","computed":true},"detector_id":{"type":"string","required":true},"format":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"location":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_guardduty_member":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"detector_id":{"type":"string","required":true},"disable_email_notification":{"type":"bool","optional":true},"email":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"invitation_message":{"type":"string","optional":true},"invite":{"type":"bool","optional":true},"relationship_status":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_guardduty_organization_admin_account":{"version":0,"block":{"attributes":{"admin_account_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_guardduty_organization_configuration":{"version":0,"block":{"attributes":{"auto_enable":{"type":"bool","required":true},"detector_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_guardduty_threatintelset":{"version":0,"block":{"attributes":{"activate":{"type":"bool","required":true},"arn":{"type":"string","computed":true},"detector_id":{"type":"string","required":true},"format":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"location":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_iam_access_key":{"version":0,"block":{"attributes":{"encrypted_secret":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key_fingerprint":{"type":"string","computed":true},"pgp_key":{"type":"string","optional":true},"secret":{"type":"string","computed":true,"sensitive":true},"ses_smtp_password_v4":{"type":"string","computed":true,"sensitive":true},"status":{"type":"string","optional":true,"computed":true},"user":{"type":"string","required":true}}}},"aws_iam_account_alias":{"version":0,"block":{"attributes":{"account_alias":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_iam_account_password_policy":{"version":0,"block":{"attributes":{"allow_users_to_change_password":{"type":"bool","optional":true},"expire_passwords":{"type":"bool","computed":true},"hard_expiry":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"max_password_age":{"type":"number","optional":true,"computed":true},"minimum_password_length":{"type":"number","optional":true},"password_reuse_prevention":{"type":"number","optional":true,"computed":true},"require_lowercase_characters":{"type":"bool","optional":true,"computed":true},"require_numbers":{"type":"bool","optional":true,"computed":true},"require_symbols":{"type":"bool","optional":true,"computed":true},"require_uppercase_characters":{"type":"bool","optional":true,"computed":true}}}},"aws_iam_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"path":{"type":"string","optional":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_group_membership":{"version":0,"block":{"attributes":{"group":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"users":{"type":["set","string"],"required":true}}}},"aws_iam_group_policy":{"version":0,"block":{"attributes":{"group":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","required":true}}}},"aws_iam_group_policy_attachment":{"version":0,"block":{"attributes":{"group":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy_arn":{"type":"string","required":true}}}},"aws_iam_instance_profile":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"create_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","optional":true},"role":{"type":"string","optional":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_openid_connect_provider":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"client_id_list":{"type":["list","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"thumbprint_list":{"type":["list","string"],"required":true},"url":{"type":"string","required":true}}}},"aws_iam_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","optional":true},"policy":{"type":"string","required":true}}}},"aws_iam_policy_attachment":{"version":0,"block":{"attributes":{"groups":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"policy_arn":{"type":"string","required":true},"roles":{"type":["set","string"],"optional":true},"users":{"type":["set","string"],"optional":true}}}},"aws_iam_role":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"assume_role_policy":{"type":"string","required":true},"create_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"force_detach_policies":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"max_session_duration":{"type":"number","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","optional":true},"permissions_boundary":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_role_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","required":true},"role":{"type":"string","required":true}}}},"aws_iam_role_policy_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy_arn":{"type":"string","required":true},"role":{"type":"string","required":true}}}},"aws_iam_saml_provider":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"saml_metadata_document":{"type":"string","required":true},"valid_until":{"type":"string","computed":true}}}},"aws_iam_server_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"certificate_body":{"type":"string","required":true},"certificate_chain":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","optional":true},"private_key":{"type":"string","required":true,"sensitive":true}}}},"aws_iam_service_linked_role":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_service_name":{"type":"string","required":true},"create_date":{"type":"string","computed":true},"custom_suffix":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"path":{"type":"string","computed":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_user":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"force_destroy":{"type":"bool","description":"Delete user even if it has non-Terraform-managed IAM access keys, login profile or MFA devices","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"path":{"type":"string","optional":true},"permissions_boundary":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_user_group_membership":{"version":0,"block":{"attributes":{"groups":{"type":["set","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"user":{"type":"string","required":true}}}},"aws_iam_user_login_profile":{"version":0,"block":{"attributes":{"encrypted_password":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key_fingerprint":{"type":"string","computed":true},"password_length":{"type":"number","optional":true},"password_reset_required":{"type":"bool","optional":true},"pgp_key":{"type":"string","required":true},"user":{"type":"string","required":true}}}},"aws_iam_user_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","required":true},"user":{"type":"string","required":true}}}},"aws_iam_user_policy_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy_arn":{"type":"string","required":true},"user":{"type":"string","required":true}}}},"aws_iam_user_ssh_key":{"version":0,"block":{"attributes":{"encoding":{"type":"string","required":true},"fingerprint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"public_key":{"type":"string","required":true},"ssh_public_key_id":{"type":"string","computed":true},"status":{"type":"string","optional":true,"computed":true},"username":{"type":"string","required":true}}}},"aws_inspector_assessment_target":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"resource_group_arn":{"type":"string","optional":true}}}},"aws_inspector_assessment_template":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"duration":{"type":"number","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"rules_package_arns":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"target_arn":{"type":"string","required":true}}}},"aws_inspector_resource_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"required":true}}}},"aws_instance":{"version":1,"block":{"attributes":{"ami":{"type":"string","required":true},"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","optional":true,"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"cpu_core_count":{"type":"number","optional":true,"computed":true},"cpu_threads_per_core":{"type":"number","optional":true,"computed":true},"disable_api_termination":{"type":"bool","optional":true},"ebs_optimized":{"type":"bool","optional":true},"get_password_data":{"type":"bool","optional":true},"hibernation":{"type":"bool","optional":true},"host_id":{"type":"string","optional":true,"computed":true},"iam_instance_profile":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_initiated_shutdown_behavior":{"type":"string","optional":true},"instance_state":{"type":"string","computed":true},"instance_type":{"type":"string","required":true},"ipv6_address_count":{"type":"number","optional":true,"computed":true},"ipv6_addresses":{"type":["list","string"],"optional":true,"computed":true},"key_name":{"type":"string","optional":true,"computed":true},"monitoring":{"type":"bool","optional":true},"outpost_arn":{"type":"string","computed":true},"password_data":{"type":"string","computed":true},"placement_group":{"type":"string","optional":true,"computed":true},"primary_network_interface_id":{"type":"string","computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","optional":true,"computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"secondary_private_ips":{"type":["set","string"],"optional":true,"computed":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"source_dest_check":{"type":"bool","optional":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"tenancy":{"type":"string","optional":true,"computed":true},"user_data":{"type":"string","optional":true},"user_data_base64":{"type":"string","optional":true},"volume_tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"credit_specification":{"nesting_mode":"list","block":{"attributes":{"cpu_credits":{"type":"string","optional":true}}},"max_items":1},"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"no_device":{"type":"bool","optional":true},"virtual_name":{"type":"string","optional":true}}}},"metadata_options":{"nesting_mode":"list","block":{"attributes":{"http_endpoint":{"type":"string","optional":true,"computed":true},"http_put_response_hop_limit":{"type":"number","optional":true,"computed":true},"http_tokens":{"type":"string","optional":true,"computed":true}}},"max_items":1},"network_interface":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_index":{"type":"number","required":true},"network_interface_id":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"list","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","computed":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_internet_gateway":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","optional":true}}}},"aws_iot_certificate":{"version":0,"block":{"attributes":{"active":{"type":"bool","required":true},"arn":{"type":"string","computed":true},"certificate_pem":{"type":"string","computed":true,"sensitive":true},"csr":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","computed":true,"sensitive":true},"public_key":{"type":"string","computed":true,"sensitive":true}}}},"aws_iot_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"default_version_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"policy":{"type":"string","required":true}}}},"aws_iot_policy_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true},"target":{"type":"string","required":true}}}},"aws_iot_role_alias":{"version":0,"block":{"attributes":{"alias":{"type":"string","required":true},"arn":{"type":"string","computed":true},"credential_duration":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","required":true}}}},"aws_iot_thing":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"attributes":{"type":["map","string"],"optional":true},"default_client_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"thing_type_name":{"type":"string","optional":true},"version":{"type":"number","computed":true}}}},"aws_iot_thing_principal_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"principal":{"type":"string","required":true},"thing":{"type":"string","required":true}}}},"aws_iot_thing_type":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deprecated":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"properties":{"nesting_mode":"list","block":{"attributes":{"description":{"type":"string","optional":true},"searchable_attributes":{"type":["set","string"],"optional":true,"computed":true}}},"max_items":1}}}},"aws_iot_topic_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"enabled":{"type":"bool","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"sql":{"type":"string","required":true},"sql_version":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"cloudwatch_alarm":{"nesting_mode":"set","block":{"attributes":{"alarm_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"state_reason":{"type":"string","required":true},"state_value":{"type":"string","required":true}}}},"cloudwatch_metric":{"nesting_mode":"set","block":{"attributes":{"metric_name":{"type":"string","required":true},"metric_namespace":{"type":"string","required":true},"metric_timestamp":{"type":"string","optional":true},"metric_unit":{"type":"string","required":true},"metric_value":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}}},"dynamodb":{"nesting_mode":"set","block":{"attributes":{"hash_key_field":{"type":"string","required":true},"hash_key_type":{"type":"string","optional":true},"hash_key_value":{"type":"string","required":true},"operation":{"type":"string","optional":true},"payload_field":{"type":"string","optional":true},"range_key_field":{"type":"string","optional":true},"range_key_type":{"type":"string","optional":true},"range_key_value":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"table_name":{"type":"string","required":true}}}},"dynamodbv2":{"nesting_mode":"set","block":{"attributes":{"role_arn":{"type":"string","required":true}},"block_types":{"put_item":{"nesting_mode":"list","block":{"attributes":{"table_name":{"type":"string","required":true}}},"max_items":1}}}},"elasticsearch":{"nesting_mode":"set","block":{"attributes":{"endpoint":{"type":"string","required":true},"id":{"type":"string","required":true},"index":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"type":{"type":"string","required":true}}}},"error_action":{"nesting_mode":"list","block":{"block_types":{"cloudwatch_alarm":{"nesting_mode":"list","block":{"attributes":{"alarm_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"state_reason":{"type":"string","required":true},"state_value":{"type":"string","required":true}}},"max_items":1},"cloudwatch_metric":{"nesting_mode":"list","block":{"attributes":{"metric_name":{"type":"string","required":true},"metric_namespace":{"type":"string","required":true},"metric_timestamp":{"type":"string","optional":true},"metric_unit":{"type":"string","required":true},"metric_value":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"dynamodb":{"nesting_mode":"list","block":{"attributes":{"hash_key_field":{"type":"string","required":true},"hash_key_type":{"type":"string","optional":true},"hash_key_value":{"type":"string","required":true},"operation":{"type":"string","optional":true},"payload_field":{"type":"string","optional":true},"range_key_field":{"type":"string","optional":true},"range_key_type":{"type":"string","optional":true},"range_key_value":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"table_name":{"type":"string","required":true}}},"max_items":1},"dynamodbv2":{"nesting_mode":"list","block":{"attributes":{"role_arn":{"type":"string","required":true}},"block_types":{"put_item":{"nesting_mode":"list","block":{"attributes":{"table_name":{"type":"string","required":true}}},"max_items":1}}},"max_items":1},"elasticsearch":{"nesting_mode":"list","block":{"attributes":{"endpoint":{"type":"string","required":true},"id":{"type":"string","required":true},"index":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"type":{"type":"string","required":true}}},"max_items":1},"firehose":{"nesting_mode":"list","block":{"attributes":{"delivery_stream_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"separator":{"type":"string","optional":true}}},"max_items":1},"iot_analytics":{"nesting_mode":"list","block":{"attributes":{"channel_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"iot_events":{"nesting_mode":"list","block":{"attributes":{"input_name":{"type":"string","required":true},"message_id":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"kinesis":{"nesting_mode":"list","block":{"attributes":{"partition_key":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"stream_name":{"type":"string","required":true}}},"max_items":1},"lambda":{"nesting_mode":"list","block":{"attributes":{"function_arn":{"type":"string","required":true}}},"max_items":1},"republish":{"nesting_mode":"list","block":{"attributes":{"qos":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"topic":{"type":"string","required":true}}},"max_items":1},"s3":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","required":true},"key":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"sns":{"nesting_mode":"list","block":{"attributes":{"message_format":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"target_arn":{"type":"string","required":true}}},"max_items":1},"sqs":{"nesting_mode":"list","block":{"attributes":{"queue_url":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"use_base64":{"type":"bool","required":true}}},"max_items":1},"step_functions":{"nesting_mode":"list","block":{"attributes":{"execution_name_prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"state_machine_name":{"type":"string","required":true}}},"max_items":1}}},"max_items":1},"firehose":{"nesting_mode":"set","block":{"attributes":{"delivery_stream_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"separator":{"type":"string","optional":true}}}},"iot_analytics":{"nesting_mode":"set","block":{"attributes":{"channel_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}}},"iot_events":{"nesting_mode":"set","block":{"attributes":{"input_name":{"type":"string","required":true},"message_id":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}}}},"kinesis":{"nesting_mode":"set","block":{"attributes":{"partition_key":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"stream_name":{"type":"string","required":true}}}},"lambda":{"nesting_mode":"set","block":{"attributes":{"function_arn":{"type":"string","required":true}}}},"republish":{"nesting_mode":"set","block":{"attributes":{"qos":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"topic":{"type":"string","required":true}}}},"s3":{"nesting_mode":"set","block":{"attributes":{"bucket_name":{"type":"string","required":true},"key":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}}},"sns":{"nesting_mode":"set","block":{"attributes":{"message_format":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"target_arn":{"type":"string","required":true}}}},"sqs":{"nesting_mode":"set","block":{"attributes":{"queue_url":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"use_base64":{"type":"bool","required":true}}}},"step_functions":{"nesting_mode":"set","block":{"attributes":{"execution_name_prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"state_machine_name":{"type":"string","required":true}}}}}}},"aws_key_pair":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"fingerprint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key_name":{"type":"string","optional":true,"computed":true},"key_name_prefix":{"type":"string","optional":true},"key_pair_id":{"type":"string","computed":true},"public_key":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_kinesis_analytics_application":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"code":{"type":"string","optional":true},"create_timestamp":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"last_update_timestamp":{"type":"string","computed":true},"name":{"type":"string","required":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"number","computed":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","computed":true},"log_stream_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"inputs":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","computed":true},"name_prefix":{"type":"string","required":true},"starting_position_configuration":{"type":["list",["object",{"starting_position":"string"}]],"computed":true},"stream_names":{"type":["set","string"],"computed":true}},"block_types":{"kinesis_firehose":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"kinesis_stream":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"parallelism":{"nesting_mode":"list","block":{"attributes":{"count":{"type":"number","required":true}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"block_types":{"lambda":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"max_items":1},"schema":{"nesting_mode":"list","block":{"attributes":{"record_encoding":{"type":"string","optional":true}},"block_types":{"record_columns":{"nesting_mode":"list","block":{"attributes":{"mapping":{"type":"string","optional":true},"name":{"type":"string","required":true},"sql_type":{"type":"string","required":true}}},"min_items":1},"record_format":{"nesting_mode":"list","block":{"attributes":{"record_format_type":{"type":"string","computed":true}},"block_types":{"mapping_parameters":{"nesting_mode":"list","block":{"block_types":{"csv":{"nesting_mode":"list","block":{"attributes":{"record_column_delimiter":{"type":"string","required":true},"record_row_delimiter":{"type":"string","required":true}}},"max_items":1},"json":{"nesting_mode":"list","block":{"attributes":{"record_row_path":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1},"outputs":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","computed":true},"name":{"type":"string","required":true}},"block_types":{"kinesis_firehose":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"kinesis_stream":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"lambda":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"schema":{"nesting_mode":"list","block":{"attributes":{"record_format_type":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"max_items":3},"reference_data_sources":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","computed":true},"table_name":{"type":"string","required":true}},"block_types":{"s3":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"file_key":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1},"schema":{"nesting_mode":"list","block":{"attributes":{"record_encoding":{"type":"string","optional":true}},"block_types":{"record_columns":{"nesting_mode":"list","block":{"attributes":{"mapping":{"type":"string","optional":true},"name":{"type":"string","required":true},"sql_type":{"type":"string","required":true}}},"min_items":1},"record_format":{"nesting_mode":"list","block":{"attributes":{"record_format_type":{"type":"string","computed":true}},"block_types":{"mapping_parameters":{"nesting_mode":"list","block":{"block_types":{"csv":{"nesting_mode":"list","block":{"attributes":{"record_column_delimiter":{"type":"string","required":true},"record_row_delimiter":{"type":"string","required":true}}},"max_items":1},"json":{"nesting_mode":"list","block":{"attributes":{"record_row_path":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1}}}},"aws_kinesis_firehose_delivery_stream":{"version":1,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"destination":{"type":"string","required":true},"destination_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"version_id":{"type":"string","optional":true,"computed":true}},"block_types":{"elasticsearch_configuration":{"nesting_mode":"list","block":{"attributes":{"buffering_interval":{"type":"number","optional":true},"buffering_size":{"type":"number","optional":true},"domain_arn":{"type":"string","required":true},"index_name":{"type":"string","required":true},"index_rotation_period":{"type":"string","optional":true},"retry_duration":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"s3_backup_mode":{"type":"string","optional":true},"type_name":{"type":"string","optional":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"processors":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"list","block":{"attributes":{"parameter_name":{"type":"string","required":true},"parameter_value":{"type":"string","required":true}}}}}}}}},"max_items":1}}},"max_items":1},"extended_s3_configuration":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"buffer_interval":{"type":"number","optional":true},"buffer_size":{"type":"number","optional":true},"compression_format":{"type":"string","optional":true},"error_output_prefix":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"s3_backup_mode":{"type":"string","optional":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1},"data_format_conversion_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"input_format_configuration":{"nesting_mode":"list","block":{"block_types":{"deserializer":{"nesting_mode":"list","block":{"block_types":{"hive_json_ser_de":{"nesting_mode":"list","block":{"attributes":{"timestamp_formats":{"type":["list","string"],"optional":true}}},"max_items":1},"open_x_json_ser_de":{"nesting_mode":"list","block":{"attributes":{"case_insensitive":{"type":"bool","optional":true},"column_to_json_key_mappings":{"type":["map","string"],"optional":true},"convert_dots_in_json_keys_to_underscores":{"type":"bool","optional":true}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"output_format_configuration":{"nesting_mode":"list","block":{"block_types":{"serializer":{"nesting_mode":"list","block":{"block_types":{"orc_ser_de":{"nesting_mode":"list","block":{"attributes":{"block_size_bytes":{"type":"number","optional":true},"bloom_filter_columns":{"type":["list","string"],"optional":true},"bloom_filter_false_positive_probability":{"type":"number","optional":true},"compression":{"type":"string","optional":true},"dictionary_key_threshold":{"type":"number","optional":true},"enable_padding":{"type":"bool","optional":true},"format_version":{"type":"string","optional":true},"padding_tolerance":{"type":"number","optional":true},"row_index_stride":{"type":"number","optional":true},"stripe_size_bytes":{"type":"number","optional":true}}},"max_items":1},"parquet_ser_de":{"nesting_mode":"list","block":{"attributes":{"block_size_bytes":{"type":"number","optional":true},"compression":{"type":"string","optional":true},"enable_dictionary_compression":{"type":"bool","optional":true},"max_padding_bytes":{"type":"number","optional":true},"page_size_bytes":{"type":"number","optional":true},"writer_version":{"type":"string","optional":true}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"schema_configuration":{"nesting_mode":"list","block":{"attributes":{"catalog_id":{"type":"string","optional":true,"computed":true},"database_name":{"type":"string","required":true},"region":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","required":true},"table_name":{"type":"string","required":true},"version_id":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"processors":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"list","block":{"attributes":{"parameter_name":{"type":"string","required":true},"parameter_value":{"type":"string","required":true}}}}}}}}},"max_items":1},"s3_backup_configuration":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"buffer_interval":{"type":"number","optional":true},"buffer_size":{"type":"number","optional":true},"compression_format":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}},"max_items":1},"kinesis_source_configuration":{"nesting_mode":"list","block":{"attributes":{"kinesis_stream_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"redshift_configuration":{"nesting_mode":"list","block":{"attributes":{"cluster_jdbcurl":{"type":"string","required":true},"copy_options":{"type":"string","optional":true},"data_table_columns":{"type":"string","optional":true},"data_table_name":{"type":"string","required":true},"password":{"type":"string","required":true,"sensitive":true},"retry_duration":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"s3_backup_mode":{"type":"string","optional":true},"username":{"type":"string","required":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"processors":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"list","block":{"attributes":{"parameter_name":{"type":"string","required":true},"parameter_value":{"type":"string","required":true}}}}}}}}},"max_items":1},"s3_backup_configuration":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"buffer_interval":{"type":"number","optional":true},"buffer_size":{"type":"number","optional":true},"compression_format":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}},"max_items":1},"s3_configuration":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"buffer_interval":{"type":"number","optional":true},"buffer_size":{"type":"number","optional":true},"compression_format":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"server_side_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}}},"max_items":1},"splunk_configuration":{"nesting_mode":"list","block":{"attributes":{"hec_acknowledgment_timeout":{"type":"number","optional":true},"hec_endpoint":{"type":"string","required":true},"hec_endpoint_type":{"type":"string","optional":true},"hec_token":{"type":"string","required":true},"retry_duration":{"type":"number","optional":true},"s3_backup_mode":{"type":"string","optional":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"processors":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"list","block":{"attributes":{"parameter_name":{"type":"string","required":true},"parameter_value":{"type":"string","required":true}}}}}}}}},"max_items":1}}},"max_items":1}}}},"aws_kinesis_stream":{"version":1,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"encryption_type":{"type":"string","optional":true},"enforce_consumer_deletion":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"name":{"type":"string","required":true},"retention_period":{"type":"number","optional":true},"shard_count":{"type":"number","required":true},"shard_level_metrics":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_kinesis_video_stream":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_time":{"type":"string","computed":true},"data_retention_in_hours":{"type":"number","optional":true},"device_name":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"media_type":{"type":"string","optional":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_kms_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"name_prefix":{"type":"string","optional":true},"target_key_arn":{"type":"string","computed":true},"target_key_id":{"type":"string","required":true}}}},"aws_kms_ciphertext":{"version":0,"block":{"attributes":{"ciphertext_blob":{"type":"string","computed":true},"context":{"type":["map","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"plaintext":{"type":"string","required":true,"sensitive":true}}}},"aws_kms_external_key":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deletion_window_in_days":{"type":"number","optional":true},"description":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true,"computed":true},"expiration_model":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key_material_base64":{"type":"string","optional":true,"sensitive":true},"key_state":{"type":"string","computed":true},"key_usage":{"type":"string","computed":true},"policy":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"valid_to":{"type":"string","optional":true}}}},"aws_kms_grant":{"version":0,"block":{"attributes":{"grant_creation_tokens":{"type":["set","string"],"optional":true},"grant_id":{"type":"string","computed":true},"grant_token":{"type":"string","computed":true},"grantee_principal":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"name":{"type":"string","optional":true},"operations":{"type":["set","string"],"required":true},"retire_on_delete":{"type":"bool","optional":true},"retiring_principal":{"type":"string","optional":true}},"block_types":{"constraints":{"nesting_mode":"set","block":{"attributes":{"encryption_context_equals":{"type":["map","string"],"optional":true},"encryption_context_subset":{"type":["map","string"],"optional":true}}}}}}},"aws_kms_key":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_master_key_spec":{"type":"string","optional":true},"deletion_window_in_days":{"type":"number","optional":true},"description":{"type":"string","optional":true,"computed":true},"enable_key_rotation":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"is_enabled":{"type":"bool","optional":true},"key_id":{"type":"string","computed":true},"key_usage":{"type":"string","optional":true},"policy":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_lambda_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"function_name":{"type":"string","required":true},"function_version":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"invoke_arn":{"type":"string","computed":true},"name":{"type":"string","required":true}},"block_types":{"routing_config":{"nesting_mode":"list","block":{"attributes":{"additional_version_weights":{"type":["map","number"],"optional":true}}},"max_items":1}}}},"aws_lambda_event_source_mapping":{"version":0,"block":{"attributes":{"batch_size":{"type":"number","optional":true},"bisect_batch_on_function_error":{"type":"bool","optional":true},"enabled":{"type":"bool","optional":true},"event_source_arn":{"type":"string","required":true},"function_arn":{"type":"string","computed":true},"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"last_modified":{"type":"string","computed":true},"last_processing_result":{"type":"string","computed":true},"maximum_batching_window_in_seconds":{"type":"number","optional":true},"maximum_record_age_in_seconds":{"type":"number","optional":true,"computed":true},"maximum_retry_attempts":{"type":"number","optional":true,"computed":true},"parallelization_factor":{"type":"number","optional":true,"computed":true},"starting_position":{"type":"string","optional":true},"starting_position_timestamp":{"type":"string","optional":true},"state":{"type":"string","computed":true},"state_transition_reason":{"type":"string","computed":true},"uuid":{"type":"string","computed":true}},"block_types":{"destination_config":{"nesting_mode":"list","block":{"block_types":{"on_failure":{"nesting_mode":"list","block":{"attributes":{"destination_arn":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}}},"aws_lambda_function":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"filename":{"type":"string","optional":true},"function_name":{"type":"string","required":true},"handler":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"invoke_arn":{"type":"string","computed":true},"kms_key_arn":{"type":"string","optional":true},"last_modified":{"type":"string","computed":true},"layers":{"type":["list","string"],"optional":true},"memory_size":{"type":"number","optional":true},"publish":{"type":"bool","optional":true},"qualified_arn":{"type":"string","computed":true},"reserved_concurrent_executions":{"type":"number","optional":true},"role":{"type":"string","required":true},"runtime":{"type":"string","required":true},"s3_bucket":{"type":"string","optional":true},"s3_key":{"type":"string","optional":true},"s3_object_version":{"type":"string","optional":true},"source_code_hash":{"type":"string","optional":true,"computed":true},"source_code_size":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true},"timeout":{"type":"number","optional":true},"version":{"type":"string","computed":true}},"block_types":{"dead_letter_config":{"nesting_mode":"list","block":{"attributes":{"target_arn":{"type":"string","required":true}}},"max_items":1},"environment":{"nesting_mode":"list","block":{"attributes":{"variables":{"type":["map","string"],"optional":true}}},"max_items":1},"file_system_config":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true},"local_mount_path":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}},"tracing_config":{"nesting_mode":"list","block":{"attributes":{"mode":{"type":"string","required":true}}},"max_items":1},"vpc_config":{"nesting_mode":"list","block":{"attributes":{"security_group_ids":{"type":["set","string"],"required":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","computed":true}}},"max_items":1}}}},"aws_lambda_function_event_invoke_config":{"version":0,"block":{"attributes":{"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"maximum_event_age_in_seconds":{"type":"number","optional":true},"maximum_retry_attempts":{"type":"number","optional":true},"qualifier":{"type":"string","optional":true}},"block_types":{"destination_config":{"nesting_mode":"list","block":{"block_types":{"on_failure":{"nesting_mode":"list","block":{"attributes":{"destination":{"type":"string","required":true}}},"max_items":1},"on_success":{"nesting_mode":"list","block":{"attributes":{"destination":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}}},"aws_lambda_layer_version":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compatible_runtimes":{"type":["set","string"],"optional":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"filename":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"layer_arn":{"type":"string","computed":true},"layer_name":{"type":"string","required":true},"license_info":{"type":"string","optional":true},"s3_bucket":{"type":"string","optional":true},"s3_key":{"type":"string","optional":true},"s3_object_version":{"type":"string","optional":true},"source_code_hash":{"type":"string","optional":true,"computed":true},"source_code_size":{"type":"number","computed":true},"version":{"type":"string","computed":true}}}},"aws_lambda_permission":{"version":0,"block":{"attributes":{"action":{"type":"string","required":true},"event_source_token":{"type":"string","optional":true},"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"principal":{"type":"string","required":true},"qualifier":{"type":"string","optional":true},"source_account":{"type":"string","optional":true},"source_arn":{"type":"string","optional":true},"statement_id":{"type":"string","optional":true,"computed":true},"statement_id_prefix":{"type":"string","optional":true}}}},"aws_lambda_provisioned_concurrency_config":{"version":0,"block":{"attributes":{"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"provisioned_concurrent_executions":{"type":"number","required":true},"qualifier":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_launch_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","optional":true},"ebs_optimized":{"type":"bool","optional":true,"computed":true},"enable_monitoring":{"type":"bool","optional":true},"iam_instance_profile":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","required":true},"instance_type":{"type":"string","required":true},"key_name":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"placement_tenancy":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true},"spot_price":{"type":"string","optional":true},"user_data":{"type":"string","optional":true},"user_data_base64":{"type":"string","optional":true},"vpc_classic_link_id":{"type":"string","optional":true},"vpc_classic_link_security_groups":{"type":["set","string"],"optional":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"no_device":{"type":"bool","optional":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"virtual_name":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"list","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1}}}},"aws_launch_template":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"default_version":{"type":"number","optional":true,"computed":true},"description":{"type":"string","optional":true},"disable_api_termination":{"type":"bool","optional":true},"ebs_optimized":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","optional":true},"instance_initiated_shutdown_behavior":{"type":"string","optional":true},"instance_type":{"type":"string","optional":true},"kernel_id":{"type":"string","optional":true},"key_name":{"type":"string","optional":true},"latest_version":{"type":"number","computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"ram_disk_id":{"type":"string","optional":true},"security_group_names":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"update_default_version":{"type":"bool","optional":true},"user_data":{"type":"string","optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true}},"block_types":{"block_device_mappings":{"nesting_mode":"list","block":{"attributes":{"device_name":{"type":"string","optional":true},"no_device":{"type":"string","optional":true},"virtual_name":{"type":"string","optional":true}},"block_types":{"ebs":{"nesting_mode":"list","block":{"attributes":{"delete_on_termination":{"type":"string","optional":true},"encrypted":{"type":"string","optional":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"snapshot_id":{"type":"string","optional":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1}}}},"capacity_reservation_specification":{"nesting_mode":"list","block":{"attributes":{"capacity_reservation_preference":{"type":"string","optional":true}},"block_types":{"capacity_reservation_target":{"nesting_mode":"list","block":{"attributes":{"capacity_reservation_id":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"cpu_options":{"nesting_mode":"list","block":{"attributes":{"core_count":{"type":"number","optional":true},"threads_per_core":{"type":"number","optional":true}}},"max_items":1},"credit_specification":{"nesting_mode":"list","block":{"attributes":{"cpu_credits":{"type":"string","optional":true}}},"max_items":1},"elastic_gpu_specifications":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}}},"elastic_inference_accelerator":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1},"hibernation_options":{"nesting_mode":"list","block":{"attributes":{"configured":{"type":"bool","required":true}}},"max_items":1},"iam_instance_profile":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","optional":true},"name":{"type":"string","optional":true}}},"max_items":1},"instance_market_options":{"nesting_mode":"list","block":{"attributes":{"market_type":{"type":"string","optional":true}},"block_types":{"spot_options":{"nesting_mode":"list","block":{"attributes":{"block_duration_minutes":{"type":"number","optional":true},"instance_interruption_behavior":{"type":"string","optional":true},"max_price":{"type":"string","optional":true},"spot_instance_type":{"type":"string","optional":true},"valid_until":{"type":"string","optional":true,"computed":true}}},"max_items":1}}},"max_items":1},"license_specification":{"nesting_mode":"set","block":{"attributes":{"license_configuration_arn":{"type":"string","required":true}}}},"metadata_options":{"nesting_mode":"list","block":{"attributes":{"http_endpoint":{"type":"string","optional":true,"computed":true},"http_put_response_hop_limit":{"type":"number","optional":true,"computed":true},"http_tokens":{"type":"string","optional":true,"computed":true}}},"max_items":1},"monitoring":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}}},"max_items":1},"network_interfaces":{"nesting_mode":"list","block":{"attributes":{"associate_public_ip_address":{"type":"string","optional":true},"delete_on_termination":{"type":"string","optional":true},"description":{"type":"string","optional":true},"device_index":{"type":"number","optional":true},"ipv4_address_count":{"type":"number","optional":true},"ipv4_addresses":{"type":["set","string"],"optional":true},"ipv6_address_count":{"type":"number","optional":true},"ipv6_addresses":{"type":["set","string"],"optional":true},"network_interface_id":{"type":"string","optional":true},"private_ip_address":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true},"subnet_id":{"type":"string","optional":true}}}},"placement":{"nesting_mode":"list","block":{"attributes":{"affinity":{"type":"string","optional":true},"availability_zone":{"type":"string","optional":true},"group_name":{"type":"string","optional":true},"host_id":{"type":"string","optional":true},"partition_number":{"type":"number","optional":true},"spread_domain":{"type":"string","optional":true},"tenancy":{"type":"string","optional":true}}},"max_items":1},"tag_specifications":{"nesting_mode":"list","block":{"attributes":{"resource_type":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}}}}},"aws_lb":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"arn_suffix":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"drop_invalid_header_fields":{"type":"bool","optional":true},"enable_cross_zone_load_balancing":{"type":"bool","optional":true},"enable_deletion_protection":{"type":"bool","optional":true},"enable_http2":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","optional":true},"internal":{"type":"bool","optional":true,"computed":true},"ip_address_type":{"type":"string","optional":true,"computed":true},"load_balancer_type":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"subnets":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"zone_id":{"type":"string","computed":true}},"block_types":{"access_logs":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"prefix":{"type":"string","optional":true}}},"max_items":1},"subnet_mapping":{"nesting_mode":"set","block":{"attributes":{"allocation_id":{"type":"string","optional":true},"private_ipv4_address":{"type":"string","optional":true},"subnet_id":{"type":"string","required":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_lb_cookie_stickiness_policy":{"version":0,"block":{"attributes":{"cookie_expiration_period":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"lb_port":{"type":"number","required":true},"load_balancer":{"type":"string","required":true},"name":{"type":"string","required":true}}}},"aws_lb_listener":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"load_balancer_arn":{"type":"string","required":true},"port":{"type":"number","required":true},"protocol":{"type":"string","optional":true},"ssl_policy":{"type":"string","optional":true,"computed":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"attributes":{"order":{"type":"number","optional":true,"computed":true},"target_group_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"authenticate_cognito":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"user_pool_arn":{"type":"string","required":true},"user_pool_client_id":{"type":"string","required":true},"user_pool_domain":{"type":"string","required":true}}},"max_items":1},"authenticate_oidc":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"authorization_endpoint":{"type":"string","required":true},"client_id":{"type":"string","required":true},"client_secret":{"type":"string","required":true,"sensitive":true},"issuer":{"type":"string","required":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"token_endpoint":{"type":"string","required":true},"user_info_endpoint":{"type":"string","required":true}}},"max_items":1},"fixed_response":{"nesting_mode":"list","block":{"attributes":{"content_type":{"type":"string","required":true},"message_body":{"type":"string","optional":true},"status_code":{"type":"string","optional":true,"computed":true}}},"max_items":1},"forward":{"nesting_mode":"list","block":{"block_types":{"stickiness":{"nesting_mode":"list","block":{"attributes":{"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1},"target_group":{"nesting_mode":"set","block":{"attributes":{"arn":{"type":"string","required":true},"weight":{"type":"number","optional":true}}},"min_items":2,"max_items":5}}},"max_items":1},"redirect":{"nesting_mode":"list","block":{"attributes":{"host":{"type":"string","optional":true},"path":{"type":"string","optional":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"query":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"read":{"type":"string","optional":true}}}}}}},"aws_lb_listener_certificate":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true}}}},"aws_lb_listener_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true},"priority":{"type":"number","optional":true,"computed":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"order":{"type":"number","optional":true,"computed":true},"target_group_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"authenticate_cognito":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"user_pool_arn":{"type":"string","required":true},"user_pool_client_id":{"type":"string","required":true},"user_pool_domain":{"type":"string","required":true}}},"max_items":1},"authenticate_oidc":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"authorization_endpoint":{"type":"string","required":true},"client_id":{"type":"string","required":true},"client_secret":{"type":"string","required":true,"sensitive":true},"issuer":{"type":"string","required":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"token_endpoint":{"type":"string","required":true},"user_info_endpoint":{"type":"string","required":true}}},"max_items":1},"fixed_response":{"nesting_mode":"list","block":{"attributes":{"content_type":{"type":"string","required":true},"message_body":{"type":"string","optional":true},"status_code":{"type":"string","optional":true,"computed":true}}},"max_items":1},"forward":{"nesting_mode":"list","block":{"block_types":{"stickiness":{"nesting_mode":"list","block":{"attributes":{"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1},"target_group":{"nesting_mode":"set","block":{"attributes":{"arn":{"type":"string","required":true},"weight":{"type":"number","optional":true}}},"min_items":2,"max_items":5}}},"max_items":1},"redirect":{"nesting_mode":"list","block":{"attributes":{"host":{"type":"string","optional":true},"path":{"type":"string","optional":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"query":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"condition":{"nesting_mode":"set","block":{"block_types":{"host_header":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"http_header":{"nesting_mode":"list","block":{"attributes":{"http_header_name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}},"max_items":1},"http_request_method":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"path_pattern":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"query_string":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"value":{"type":"string","required":true}}}},"source_ip":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1}}},"min_items":1}}}},"aws_lb_ssl_negotiation_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"lb_port":{"type":"number","required":true},"load_balancer":{"type":"string","required":true},"name":{"type":"string","required":true}},"block_types":{"attribute":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_lb_target_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"arn_suffix":{"type":"string","computed":true},"deregistration_delay":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"lambda_multi_value_headers_enabled":{"type":"bool","optional":true},"load_balancing_algorithm_type":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"port":{"type":"number","optional":true},"protocol":{"type":"string","optional":true},"proxy_protocol_v2":{"type":"bool","optional":true},"slow_start":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true},"target_type":{"type":"string","optional":true},"vpc_id":{"type":"string","optional":true}},"block_types":{"health_check":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"healthy_threshold":{"type":"number","optional":true},"interval":{"type":"number","optional":true},"matcher":{"type":"string","optional":true,"computed":true},"path":{"type":"string","optional":true,"computed":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"timeout":{"type":"number","optional":true,"computed":true},"unhealthy_threshold":{"type":"number","optional":true}}},"max_items":1},"stickiness":{"nesting_mode":"list","block":{"attributes":{"cookie_duration":{"type":"number","optional":true},"enabled":{"type":"bool","optional":true},"type":{"type":"string","required":true}}},"max_items":1}}}},"aws_lb_target_group_attachment":{"version":0,"block":{"attributes":{"availability_zone":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"target_group_arn":{"type":"string","required":true},"target_id":{"type":"string","required":true}}}},"aws_licensemanager_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"license_configuration_arn":{"type":"string","required":true},"resource_arn":{"type":"string","required":true}}}},"aws_licensemanager_license_configuration":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"license_count":{"type":"number","optional":true},"license_count_hard_limit":{"type":"bool","optional":true},"license_counting_type":{"type":"string","required":true},"license_rules":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_lightsail_domain":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_lightsail_instance":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","required":true},"blueprint_id":{"type":"string","required":true},"bundle_id":{"type":"string","required":true},"cpu_count":{"type":"number","computed":true},"created_at":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_address":{"type":"string","computed":true},"is_static_ip":{"type":"bool","computed":true},"key_pair_name":{"type":"string","optional":true},"name":{"type":"string","required":true},"private_ip_address":{"type":"string","computed":true},"public_ip_address":{"type":"string","computed":true},"ram_size":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true},"user_data":{"type":"string","optional":true},"username":{"type":"string","computed":true}}}},"aws_lightsail_key_pair":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"encrypted_fingerprint":{"type":"string","computed":true},"encrypted_private_key":{"type":"string","computed":true},"fingerprint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"pgp_key":{"type":"string","optional":true},"private_key":{"type":"string","computed":true},"public_key":{"type":"string","optional":true,"computed":true}}}},"aws_lightsail_static_ip":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","computed":true},"name":{"type":"string","required":true},"support_code":{"type":"string","computed":true}}}},"aws_lightsail_static_ip_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_name":{"type":"string","required":true},"ip_address":{"type":"string","computed":true},"static_ip_name":{"type":"string","required":true}}}},"aws_load_balancer_backend_server_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_port":{"type":"number","required":true},"load_balancer_name":{"type":"string","required":true},"policy_names":{"type":["set","string"],"optional":true}}}},"aws_load_balancer_listener_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"load_balancer_name":{"type":"string","required":true},"load_balancer_port":{"type":"number","required":true},"policy_names":{"type":["set","string"],"optional":true}}}},"aws_load_balancer_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"load_balancer_name":{"type":"string","required":true},"policy_name":{"type":"string","required":true},"policy_type_name":{"type":"string","required":true}},"block_types":{"policy_attribute":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","optional":true},"value":{"type":"string","optional":true}}}}}}},"aws_macie_member_account_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"member_account_id":{"type":"string","required":true}}}},"aws_macie_s3_bucket_association":{"version":0,"block":{"attributes":{"bucket_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"member_account_id":{"type":"string","optional":true},"prefix":{"type":"string","optional":true}},"block_types":{"classification_type":{"nesting_mode":"list","block":{"attributes":{"continuous":{"type":"string","optional":true},"one_time":{"type":"string","optional":true}}},"max_items":1}}}},"aws_main_route_table_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"original_route_table_id":{"type":"string","computed":true},"route_table_id":{"type":"string","required":true},"vpc_id":{"type":"string","required":true}}}},"aws_media_convert_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"pricing_plan":{"type":"string","optional":true},"status":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"reservation_plan_settings":{"nesting_mode":"list","block":{"attributes":{"commitment":{"type":"string","required":true},"renewal_type":{"type":"string","required":true},"reserved_slots":{"type":"number","required":true}}},"max_items":1}}}},"aws_media_package_channel":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"channel_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"hls_ingest":{"type":["list",["object",{"ingest_endpoints":["list",["object",{"password":"string","url":"string","username":"string"}]]}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_media_store_container":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_media_store_container_policy":{"version":0,"block":{"attributes":{"container_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true}}}},"aws_mq_broker":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"broker_name":{"type":"string","required":true},"deployment_mode":{"type":"string","optional":true},"engine_type":{"type":"string","required":true},"engine_version":{"type":"string","required":true},"host_instance_type":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instances":{"type":["list",["object",{"console_url":"string","endpoints":["list","string"],"ip_address":"string"}]],"computed":true},"publicly_accessible":{"type":"bool","optional":true},"security_groups":{"type":["set","string"],"required":true},"subnet_ids":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"configuration":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"revision":{"type":"number","optional":true,"computed":true}}},"max_items":1},"encryption_options":{"nesting_mode":"list","block":{"attributes":{"kms_key_id":{"type":"string","optional":true,"computed":true},"use_aws_owned_key":{"type":"bool","optional":true}}},"max_items":1},"logs":{"nesting_mode":"list","block":{"attributes":{"audit":{"type":"bool","optional":true},"general":{"type":"bool","optional":true}}},"max_items":1},"maintenance_window_start_time":{"nesting_mode":"list","block":{"attributes":{"day_of_week":{"type":"string","required":true},"time_of_day":{"type":"string","required":true},"time_zone":{"type":"string","required":true}}},"max_items":1},"user":{"nesting_mode":"set","block":{"attributes":{"console_access":{"type":"bool","optional":true},"groups":{"type":["set","string"],"optional":true},"password":{"type":"string","required":true,"sensitive":true},"username":{"type":"string","required":true}}},"min_items":1}}}},"aws_mq_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"data":{"type":"string","required":true},"description":{"type":"string","optional":true},"engine_type":{"type":"string","required":true},"engine_version":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"latest_revision":{"type":"number","computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_msk_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bootstrap_brokers":{"type":"string","computed":true},"bootstrap_brokers_tls":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"current_version":{"type":"string","computed":true},"enhanced_monitoring":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"kafka_version":{"type":"string","required":true},"number_of_broker_nodes":{"type":"number","required":true},"tags":{"type":["map","string"],"optional":true},"zookeeper_connect_string":{"type":"string","computed":true}},"block_types":{"broker_node_group_info":{"nesting_mode":"list","block":{"attributes":{"az_distribution":{"type":"string","optional":true},"client_subnets":{"type":["list","string"],"required":true},"ebs_volume_size":{"type":"number","required":true},"instance_type":{"type":"string","required":true},"security_groups":{"type":["list","string"],"required":true}}},"min_items":1,"max_items":1},"client_authentication":{"nesting_mode":"list","block":{"block_types":{"tls":{"nesting_mode":"list","block":{"attributes":{"certificate_authority_arns":{"type":["set","string"],"optional":true}}},"max_items":1}}},"max_items":1},"configuration_info":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true},"revision":{"type":"number","required":true}}},"max_items":1},"encryption_info":{"nesting_mode":"list","block":{"attributes":{"encryption_at_rest_kms_key_arn":{"type":"string","optional":true,"computed":true}},"block_types":{"encryption_in_transit":{"nesting_mode":"list","block":{"attributes":{"client_broker":{"type":"string","optional":true},"in_cluster":{"type":"bool","optional":true}}},"max_items":1}}},"max_items":1},"logging_info":{"nesting_mode":"list","block":{"block_types":{"broker_logs":{"nesting_mode":"list","block":{"block_types":{"cloudwatch_logs":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true},"log_group":{"type":"string","optional":true}}},"max_items":1},"firehose":{"nesting_mode":"list","block":{"attributes":{"delivery_stream":{"type":"string","optional":true},"enabled":{"type":"bool","required":true}}},"max_items":1},"s3":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","optional":true},"enabled":{"type":"bool","required":true},"prefix":{"type":"string","optional":true}}},"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1},"open_monitoring":{"nesting_mode":"list","block":{"block_types":{"prometheus":{"nesting_mode":"list","block":{"block_types":{"jmx_exporter":{"nesting_mode":"list","block":{"attributes":{"enabled_in_broker":{"type":"bool","required":true}}},"max_items":1},"node_exporter":{"nesting_mode":"list","block":{"attributes":{"enabled_in_broker":{"type":"bool","required":true}}},"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1}}}},"aws_msk_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"kafka_versions":{"type":["set","string"],"required":true},"latest_revision":{"type":"number","computed":true},"name":{"type":"string","required":true},"server_properties":{"type":"string","required":true}}}},"aws_nat_gateway":{"version":0,"block":{"attributes":{"allocation_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"subnet_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_neptune_cluster":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"backup_retention_period":{"type":"number","optional":true},"cluster_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier_prefix":{"type":"string","optional":true,"computed":true},"cluster_members":{"type":["set","string"],"computed":true},"cluster_resource_id":{"type":"string","computed":true},"deletion_protection":{"type":"bool","optional":true},"enable_cloudwatch_logs_exports":{"type":["set","string"],"optional":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"hosted_zone_id":{"type":"string","computed":true},"iam_database_authentication_enabled":{"type":"bool","optional":true},"iam_roles":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true,"computed":true},"neptune_cluster_parameter_group_name":{"type":"string","optional":true},"neptune_subnet_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"reader_endpoint":{"type":"string","computed":true},"replication_source_identifier":{"type":"string","optional":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_identifier":{"type":"string","optional":true},"storage_encrypted":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_neptune_cluster_instance":{"version":0,"block":{"attributes":{"address":{"type":"string","computed":true},"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"cluster_identifier":{"type":"string","required":true},"dbi_resource_id":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"instance_class":{"type":"string","required":true},"kms_key_arn":{"type":"string","computed":true},"neptune_parameter_group_name":{"type":"string","optional":true},"neptune_subnet_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"promotion_tier":{"type":"number","optional":true},"publicly_accessible":{"type":"bool","optional":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true},"writer":{"type":"bool","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_neptune_cluster_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_neptune_cluster_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zones":{"type":["list","string"],"computed":true},"db_cluster_identifier":{"type":"string","required":true},"db_cluster_snapshot_arn":{"type":"string","computed":true},"db_cluster_snapshot_identifier":{"type":"string","required":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_type":{"type":"string","computed":true},"source_db_cluster_snapshot_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_neptune_event_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_aws_id":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"event_categories":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"sns_topic_arn":{"type":"string","required":true},"source_ids":{"type":["set","string"],"optional":true},"source_type":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_neptune_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_neptune_subnet_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_network_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"egress":{"type":["set",["object",{"action":"string","cidr_block":"string","from_port":"number","icmp_code":"number","icmp_type":"number","ipv6_cidr_block":"string","protocol":"string","rule_no":"number","to_port":"number"}]],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ingress":{"type":["set",["object",{"action":"string","cidr_block":"string","from_port":"number","icmp_code":"number","icmp_type":"number","ipv6_cidr_block":"string","protocol":"string","rule_no":"number","to_port":"number"}]],"optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}}}},"aws_network_acl_rule":{"version":0,"block":{"attributes":{"cidr_block":{"type":"string","optional":true},"egress":{"type":"bool","optional":true},"from_port":{"type":"number","optional":true},"icmp_code":{"type":"string","optional":true},"icmp_type":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block":{"type":"string","optional":true},"network_acl_id":{"type":"string","required":true},"protocol":{"type":"string","required":true},"rule_action":{"type":"string","required":true},"rule_number":{"type":"number","required":true},"to_port":{"type":"number","optional":true}}}},"aws_network_interface":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"mac_address":{"type":"string","computed":true},"outpost_arn":{"type":"string","computed":true},"private_dns_name":{"type":"string","computed":true},"private_ip":{"type":"string","optional":true,"computed":true},"private_ips":{"type":["set","string"],"optional":true,"computed":true},"private_ips_count":{"type":"number","optional":true,"computed":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"source_dest_check":{"type":"bool","optional":true},"subnet_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"attachment":{"nesting_mode":"set","block":{"attributes":{"attachment_id":{"type":"string","computed":true},"device_index":{"type":"number","required":true},"instance":{"type":"string","required":true}}}}}}},"aws_network_interface_attachment":{"version":0,"block":{"attributes":{"attachment_id":{"type":"string","computed":true},"device_index":{"type":"number","required":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","required":true},"network_interface_id":{"type":"string","required":true},"status":{"type":"string","computed":true}}}},"aws_network_interface_sg_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","required":true},"security_group_id":{"type":"string","required":true}}}},"aws_opsworks_application":{"version":0,"block":{"attributes":{"auto_bundle_on_deploy":{"type":"string","optional":true},"aws_flow_ruby_settings":{"type":"string","optional":true},"data_source_arn":{"type":"string","optional":true},"data_source_database_name":{"type":"string","optional":true},"data_source_type":{"type":"string","optional":true},"description":{"type":"string","optional":true},"document_root":{"type":"string","optional":true},"domains":{"type":["list","string"],"optional":true},"enable_ssl":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"rails_env":{"type":"string","optional":true},"short_name":{"type":"string","optional":true,"computed":true},"stack_id":{"type":"string","required":true},"type":{"type":"string","required":true}},"block_types":{"app_source":{"nesting_mode":"list","block":{"attributes":{"password":{"type":"string","optional":true,"sensitive":true},"revision":{"type":"string","optional":true},"ssh_key":{"type":"string","optional":true,"sensitive":true},"type":{"type":"string","required":true},"url":{"type":"string","optional":true},"username":{"type":"string","optional":true}}}},"environment":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","required":true},"secure":{"type":"bool","optional":true},"value":{"type":"string","required":true}}}},"ssl_configuration":{"nesting_mode":"list","block":{"attributes":{"certificate":{"type":"string","required":true},"chain":{"type":"string","optional":true},"private_key":{"type":"string","required":true,"sensitive":true}}}}}}},"aws_opsworks_custom_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","required":true},"short_name":{"type":"string","required":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_ganglia_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"password":{"type":"string","required":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"url":{"type":"string","optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true},"username":{"type":"string","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_haproxy_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"healthcheck_method":{"type":"string","optional":true},"healthcheck_url":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"stats_enabled":{"type":"bool","optional":true},"stats_password":{"type":"string","required":true},"stats_url":{"type":"string","optional":true},"stats_user":{"type":"string","optional":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_instance":{"version":0,"block":{"attributes":{"agent_version":{"type":"string","optional":true},"ami_id":{"type":"string","optional":true,"computed":true},"architecture":{"type":"string","optional":true},"auto_scaling_type":{"type":"string","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"created_at":{"type":"string","optional":true,"computed":true},"delete_ebs":{"type":"bool","optional":true},"delete_eip":{"type":"bool","optional":true},"ebs_optimized":{"type":"bool","optional":true},"ec2_instance_id":{"type":"string","computed":true},"ecs_cluster_arn":{"type":"string","optional":true,"computed":true},"elastic_ip":{"type":"string","optional":true,"computed":true},"hostname":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"infrastructure_class":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_profile_arn":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","optional":true},"last_service_error_id":{"type":"string","optional":true,"computed":true},"layer_ids":{"type":["list","string"],"required":true},"os":{"type":"string","optional":true,"computed":true},"platform":{"type":"string","optional":true,"computed":true},"private_dns":{"type":"string","optional":true,"computed":true},"private_ip":{"type":"string","optional":true,"computed":true},"public_dns":{"type":"string","optional":true,"computed":true},"public_ip":{"type":"string","optional":true,"computed":true},"registered_by":{"type":"string","optional":true,"computed":true},"reported_agent_version":{"type":"string","optional":true,"computed":true},"reported_os_family":{"type":"string","optional":true,"computed":true},"reported_os_name":{"type":"string","optional":true,"computed":true},"reported_os_version":{"type":"string","optional":true,"computed":true},"root_device_type":{"type":"string","optional":true,"computed":true},"root_device_volume_id":{"type":"string","optional":true,"computed":true},"security_group_ids":{"type":["list","string"],"optional":true,"computed":true},"ssh_host_dsa_key_fingerprint":{"type":"string","optional":true,"computed":true},"ssh_host_rsa_key_fingerprint":{"type":"string","optional":true,"computed":true},"ssh_key_name":{"type":"string","optional":true,"computed":true},"stack_id":{"type":"string","required":true},"state":{"type":"string","optional":true},"status":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tenancy":{"type":"string","optional":true,"computed":true},"virtualization_type":{"type":"string","optional":true,"computed":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"iops":{"type":"number","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"virtual_name":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"iops":{"type":"number","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_opsworks_java_app_layer":{"version":0,"block":{"attributes":{"app_server":{"type":"string","optional":true},"app_server_version":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"jvm_options":{"type":"string","optional":true},"jvm_type":{"type":"string","optional":true},"jvm_version":{"type":"string","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_memcached_layer":{"version":0,"block":{"attributes":{"allocated_memory":{"type":"number","optional":true},"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_mysql_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"root_password":{"type":"string","optional":true},"root_password_on_all_instances":{"type":"bool","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_nodejs_app_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"nodejs_version":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_permission":{"version":0,"block":{"attributes":{"allow_ssh":{"type":"bool","optional":true,"computed":true},"allow_sudo":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"level":{"type":"string","optional":true,"computed":true},"stack_id":{"type":"string","optional":true,"computed":true},"user_arn":{"type":"string","required":true}}}},"aws_opsworks_php_app_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_rails_app_layer":{"version":0,"block":{"attributes":{"app_server":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"bundler_version":{"type":"string","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"manage_bundler":{"type":"bool","optional":true},"name":{"type":"string","optional":true},"passenger_version":{"type":"string","optional":true},"ruby_version":{"type":"string","optional":true},"rubygems_version":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_rds_db_instance":{"version":0,"block":{"attributes":{"db_password":{"type":"string","required":true,"sensitive":true},"db_user":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"rds_db_instance_arn":{"type":"string","required":true},"stack_id":{"type":"string","required":true}}}},"aws_opsworks_stack":{"version":0,"block":{"attributes":{"agent_version":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"berkshelf_version":{"type":"string","optional":true},"color":{"type":"string","optional":true},"configuration_manager_name":{"type":"string","optional":true},"configuration_manager_version":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"default_availability_zone":{"type":"string","optional":true,"computed":true},"default_instance_profile_arn":{"type":"string","required":true},"default_os":{"type":"string","optional":true},"default_root_device_type":{"type":"string","optional":true},"default_ssh_key_name":{"type":"string","optional":true},"default_subnet_id":{"type":"string","optional":true,"computed":true},"hostname_theme":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"manage_berkshelf":{"type":"bool","optional":true},"name":{"type":"string","required":true},"region":{"type":"string","required":true},"service_role_arn":{"type":"string","required":true},"stack_endpoint":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"use_custom_cookbooks":{"type":"bool","optional":true},"use_opsworks_security_groups":{"type":"bool","optional":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"custom_cookbooks_source":{"nesting_mode":"list","block":{"attributes":{"password":{"type":"string","optional":true,"sensitive":true},"revision":{"type":"string","optional":true},"ssh_key":{"type":"string","optional":true,"sensitive":true},"type":{"type":"string","required":true},"url":{"type":"string","required":true},"username":{"type":"string","optional":true}}}}}}},"aws_opsworks_static_web_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_user_profile":{"version":0,"block":{"attributes":{"allow_self_management":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"ssh_public_key":{"type":"string","optional":true},"ssh_username":{"type":"string","required":true},"user_arn":{"type":"string","required":true}}}},"aws_organizations_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"email":{"type":"string","required":true},"iam_user_access_to_billing":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"joined_method":{"type":"string","computed":true},"joined_timestamp":{"type":"string","computed":true},"name":{"type":"string","required":true},"parent_id":{"type":"string","optional":true,"computed":true},"role_name":{"type":"string","optional":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_organizations_organization":{"version":0,"block":{"attributes":{"accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string","status":"string"}]],"computed":true},"arn":{"type":"string","computed":true},"aws_service_access_principals":{"type":["set","string"],"optional":true},"enabled_policy_types":{"type":["set","string"],"optional":true},"feature_set":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"master_account_arn":{"type":"string","computed":true},"master_account_email":{"type":"string","computed":true},"master_account_id":{"type":"string","computed":true},"non_master_accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string","status":"string"}]],"computed":true},"roots":{"type":["list",["object",{"arn":"string","id":"string","name":"string","policy_types":["list",["object",{"status":"string","type":"string"}]]}]],"computed":true}}}},"aws_organizations_organizational_unit":{"version":0,"block":{"attributes":{"accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string"}]],"computed":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"parent_id":{"type":"string","required":true}}}},"aws_organizations_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"content":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"type":{"type":"string","optional":true}}}},"aws_organizations_policy_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy_id":{"type":"string","required":true},"target_id":{"type":"string","required":true}}}},"aws_pinpoint_adm_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"client_id":{"type":"string","required":true,"sensitive":true},"client_secret":{"type":"string","required":true,"sensitive":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_pinpoint_apns_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"bundle_id":{"type":"string","optional":true,"sensitive":true},"certificate":{"type":"string","optional":true,"sensitive":true},"default_authentication_method":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"team_id":{"type":"string","optional":true,"sensitive":true},"token_key":{"type":"string","optional":true,"sensitive":true},"token_key_id":{"type":"string","optional":true,"sensitive":true}}}},"aws_pinpoint_apns_sandbox_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"bundle_id":{"type":"string","optional":true,"sensitive":true},"certificate":{"type":"string","optional":true,"sensitive":true},"default_authentication_method":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"team_id":{"type":"string","optional":true,"sensitive":true},"token_key":{"type":"string","optional":true,"sensitive":true},"token_key_id":{"type":"string","optional":true,"sensitive":true}}}},"aws_pinpoint_apns_voip_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"bundle_id":{"type":"string","optional":true,"sensitive":true},"certificate":{"type":"string","optional":true,"sensitive":true},"default_authentication_method":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"team_id":{"type":"string","optional":true,"sensitive":true},"token_key":{"type":"string","optional":true,"sensitive":true},"token_key_id":{"type":"string","optional":true,"sensitive":true}}}},"aws_pinpoint_apns_voip_sandbox_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"bundle_id":{"type":"string","optional":true,"sensitive":true},"certificate":{"type":"string","optional":true,"sensitive":true},"default_authentication_method":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"team_id":{"type":"string","optional":true,"sensitive":true},"token_key":{"type":"string","optional":true,"sensitive":true},"token_key_id":{"type":"string","optional":true,"sensitive":true}}}},"aws_pinpoint_app":{"version":0,"block":{"attributes":{"application_id":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"campaign_hook":{"nesting_mode":"list","block":{"attributes":{"lambda_function_name":{"type":"string","optional":true},"mode":{"type":"string","optional":true},"web_url":{"type":"string","optional":true}}},"max_items":1},"limits":{"nesting_mode":"list","block":{"attributes":{"daily":{"type":"number","optional":true},"maximum_duration":{"type":"number","optional":true},"messages_per_second":{"type":"number","optional":true},"total":{"type":"number","optional":true}}},"max_items":1},"quiet_time":{"nesting_mode":"list","block":{"attributes":{"end":{"type":"string","optional":true},"start":{"type":"string","optional":true}}},"max_items":1}}}},"aws_pinpoint_baidu_channel":{"version":0,"block":{"attributes":{"api_key":{"type":"string","required":true,"sensitive":true},"application_id":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"secret_key":{"type":"string","required":true,"sensitive":true}}}},"aws_pinpoint_email_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"from_address":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"identity":{"type":"string","required":true},"messages_per_second":{"type":"number","computed":true},"role_arn":{"type":"string","required":true}}}},"aws_pinpoint_event_stream":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"destination_stream_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","required":true}}}},"aws_pinpoint_gcm_channel":{"version":0,"block":{"attributes":{"api_key":{"type":"string","required":true,"sensitive":true},"application_id":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_pinpoint_sms_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"promotional_messages_per_second":{"type":"number","computed":true},"sender_id":{"type":"string","optional":true},"short_code":{"type":"string","optional":true},"transactional_messages_per_second":{"type":"number","computed":true}}}},"aws_placement_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"placement_group_id":{"type":"string","computed":true},"strategy":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_proxy_protocol_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_ports":{"type":["set","string"],"required":true},"load_balancer":{"type":"string","required":true}}}},"aws_qldb_ledger":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deletion_protection":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_quicksight_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_account_id":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"group_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"namespace":{"type":"string","optional":true}}}},"aws_quicksight_user":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_account_id":{"type":"string","optional":true,"computed":true},"email":{"type":"string","required":true},"iam_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"identity_type":{"type":"string","required":true},"namespace":{"type":"string","optional":true},"session_name":{"type":"string","optional":true},"user_name":{"type":"string","optional":true},"user_role":{"type":"string","required":true}}}},"aws_ram_principal_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"principal":{"type":"string","required":true},"resource_share_arn":{"type":"string","required":true}}}},"aws_ram_resource_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_arn":{"type":"string","required":true},"resource_share_arn":{"type":"string","required":true}}}},"aws_ram_resource_share":{"version":0,"block":{"attributes":{"allow_external_principals":{"type":"bool","optional":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_ram_resource_share_accepter":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"invitation_arn":{"type":"string","computed":true},"receiver_account_id":{"type":"string","computed":true},"resources":{"type":["list","string"],"computed":true},"sender_account_id":{"type":"string","computed":true},"share_arn":{"type":"string","required":true},"share_id":{"type":"string","computed":true},"share_name":{"type":"string","computed":true},"status":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_rds_cluster":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"backtrack_window":{"type":"number","optional":true},"backup_retention_period":{"type":"number","optional":true},"cluster_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier_prefix":{"type":"string","optional":true,"computed":true},"cluster_members":{"type":["set","string"],"optional":true,"computed":true},"cluster_resource_id":{"type":"string","computed":true},"copy_tags_to_snapshot":{"type":"bool","optional":true},"database_name":{"type":"string","optional":true,"computed":true},"db_cluster_parameter_group_name":{"type":"string","optional":true,"computed":true},"db_subnet_group_name":{"type":"string","optional":true,"computed":true},"deletion_protection":{"type":"bool","optional":true},"enable_http_endpoint":{"type":"bool","optional":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"optional":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_mode":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"global_cluster_identifier":{"type":"string","optional":true},"hosted_zone_id":{"type":"string","computed":true},"iam_database_authentication_enabled":{"type":"bool","optional":true},"iam_roles":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"master_password":{"type":"string","optional":true,"sensitive":true},"master_username":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"reader_endpoint":{"type":"string","computed":true},"replication_source_identifier":{"type":"string","optional":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_identifier":{"type":"string","optional":true},"source_region":{"type":"string","optional":true},"storage_encrypted":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"s3_import":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","required":true},"bucket_prefix":{"type":"string","optional":true},"ingestion_role":{"type":"string","required":true},"source_engine":{"type":"string","required":true},"source_engine_version":{"type":"string","required":true}}},"max_items":1},"scaling_configuration":{"nesting_mode":"list","block":{"attributes":{"auto_pause":{"type":"bool","optional":true},"max_capacity":{"type":"number","optional":true},"min_capacity":{"type":"number","optional":true},"seconds_until_auto_pause":{"type":"number","optional":true},"timeout_action":{"type":"string","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_rds_cluster_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cluster_endpoint_identifier":{"type":"string","required":true},"cluster_identifier":{"type":"string","required":true},"custom_endpoint_type":{"type":"string","required":true},"endpoint":{"type":"string","computed":true},"excluded_members":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"static_members":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_rds_cluster_instance":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"ca_cert_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier":{"type":"string","required":true},"copy_tags_to_snapshot":{"type":"bool","optional":true},"db_parameter_group_name":{"type":"string","optional":true,"computed":true},"db_subnet_group_name":{"type":"string","optional":true,"computed":true},"dbi_resource_id":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"instance_class":{"type":"string","required":true},"kms_key_id":{"type":"string","computed":true},"monitoring_interval":{"type":"number","optional":true},"monitoring_role_arn":{"type":"string","optional":true,"computed":true},"performance_insights_enabled":{"type":"bool","optional":true,"computed":true},"performance_insights_kms_key_id":{"type":"string","optional":true,"computed":true},"port":{"type":"number","computed":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"promotion_tier":{"type":"number","optional":true},"publicly_accessible":{"type":"bool","optional":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true},"writer":{"type":"bool","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_rds_cluster_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_rds_global_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"database_name":{"type":"string","optional":true},"deletion_protection":{"type":"bool","optional":true},"engine":{"type":"string","optional":true,"computed":true},"engine_version":{"type":"string","optional":true,"computed":true},"force_destroy":{"type":"bool","optional":true},"global_cluster_identifier":{"type":"string","required":true},"global_cluster_members":{"type":["set",["object",{"db_cluster_arn":"string","is_writer":"bool"}]],"computed":true},"global_cluster_resource_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"source_db_cluster_identifier":{"type":"string","optional":true,"computed":true},"storage_encrypted":{"type":"bool","optional":true}}}},"aws_redshift_cluster":{"version":0,"block":{"attributes":{"allow_version_upgrade":{"type":"bool","optional":true},"arn":{"type":"string","computed":true},"automated_snapshot_retention_period":{"type":"number","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"cluster_identifier":{"type":"string","required":true},"cluster_parameter_group_name":{"type":"string","optional":true,"computed":true},"cluster_public_key":{"type":"string","optional":true,"computed":true},"cluster_revision_number":{"type":"string","optional":true,"computed":true},"cluster_security_groups":{"type":["set","string"],"optional":true,"computed":true},"cluster_subnet_group_name":{"type":"string","optional":true,"computed":true},"cluster_type":{"type":"string","optional":true,"computed":true},"cluster_version":{"type":"string","optional":true},"database_name":{"type":"string","optional":true,"computed":true},"dns_name":{"type":"string","computed":true},"elastic_ip":{"type":"string","optional":true},"encrypted":{"type":"bool","optional":true},"endpoint":{"type":"string","optional":true,"computed":true},"enhanced_vpc_routing":{"type":"bool","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"iam_roles":{"type":["set","string"],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"master_password":{"type":"string","optional":true,"sensitive":true},"master_username":{"type":"string","optional":true},"node_type":{"type":"string","required":true},"number_of_nodes":{"type":"number","optional":true},"owner_account":{"type":"string","optional":true},"port":{"type":"number","optional":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"publicly_accessible":{"type":"bool","optional":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_cluster_identifier":{"type":"string","optional":true},"snapshot_identifier":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"logging":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","optional":true,"computed":true},"enable":{"type":"bool","required":true},"s3_key_prefix":{"type":"string","optional":true,"computed":true}}},"max_items":1},"snapshot_copy":{"nesting_mode":"list","block":{"attributes":{"destination_region":{"type":"string","required":true},"grant_name":{"type":"string","optional":true},"retention_period":{"type":"number","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_redshift_event_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_aws_id":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"event_categories":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"severity":{"type":"string","optional":true},"sns_topic_arn":{"type":"string","required":true},"source_ids":{"type":["set","string"],"optional":true},"source_type":{"type":"string","optional":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_redshift_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_redshift_security_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"ingress":{"nesting_mode":"set","block":{"attributes":{"cidr":{"type":"string","optional":true},"security_group_name":{"type":"string","optional":true,"computed":true},"security_group_owner_id":{"type":"string","optional":true,"computed":true}}},"min_items":1}}}},"aws_redshift_snapshot_copy_grant":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"snapshot_copy_grant_name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_redshift_snapshot_schedule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"definitions":{"type":["set","string"],"required":true},"description":{"type":"string","optional":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_redshift_snapshot_schedule_association":{"version":0,"block":{"attributes":{"cluster_identifier":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"schedule_identifier":{"type":"string","required":true}}}},"aws_redshift_subnet_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_resourcegroups_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"resource_query":{"nesting_mode":"list","block":{"attributes":{"query":{"type":"string","required":true},"type":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}}},"aws_route":{"version":0,"block":{"attributes":{"destination_cidr_block":{"type":"string","optional":true},"destination_ipv6_cidr_block":{"type":"string","optional":true},"destination_prefix_list_id":{"type":"string","computed":true},"egress_only_gateway_id":{"type":"string","optional":true,"computed":true},"gateway_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true,"computed":true},"instance_owner_id":{"type":"string","computed":true},"nat_gateway_id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","optional":true,"computed":true},"origin":{"type":"string","computed":true},"route_table_id":{"type":"string","required":true},"state":{"type":"string","computed":true},"transit_gateway_id":{"type":"string","optional":true},"vpc_peering_connection_id":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_route53_delegation_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name_servers":{"type":["list","string"],"computed":true},"reference_name":{"type":"string","optional":true}}}},"aws_route53_health_check":{"version":0,"block":{"attributes":{"child_health_threshold":{"type":"number","optional":true},"child_healthchecks":{"type":["set","string"],"optional":true},"cloudwatch_alarm_name":{"type":"string","optional":true},"cloudwatch_alarm_region":{"type":"string","optional":true},"disabled":{"type":"bool","optional":true},"enable_sni":{"type":"bool","optional":true,"computed":true},"failure_threshold":{"type":"number","optional":true},"fqdn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"insufficient_data_health_status":{"type":"string","optional":true},"invert_healthcheck":{"type":"bool","optional":true},"ip_address":{"type":"string","optional":true},"measure_latency":{"type":"bool","optional":true},"port":{"type":"number","optional":true},"reference_name":{"type":"string","optional":true},"regions":{"type":["set","string"],"optional":true},"request_interval":{"type":"number","optional":true},"resource_path":{"type":"string","optional":true},"search_string":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","required":true}}}},"aws_route53_query_log":{"version":0,"block":{"attributes":{"cloudwatch_log_group_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","required":true}}}},"aws_route53_record":{"version":2,"block":{"attributes":{"allow_overwrite":{"type":"bool","optional":true,"computed":true},"fqdn":{"type":"string","computed":true},"health_check_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"multivalue_answer_routing_policy":{"type":"bool","optional":true},"name":{"type":"string","required":true},"records":{"type":["set","string"],"optional":true},"set_identifier":{"type":"string","optional":true},"ttl":{"type":"number","optional":true},"type":{"type":"string","required":true},"zone_id":{"type":"string","required":true}},"block_types":{"alias":{"nesting_mode":"set","block":{"attributes":{"evaluate_target_health":{"type":"bool","required":true},"name":{"type":"string","required":true},"zone_id":{"type":"string","required":true}}}},"failover_routing_policy":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}}},"geolocation_routing_policy":{"nesting_mode":"list","block":{"attributes":{"continent":{"type":"string","optional":true},"country":{"type":"string","optional":true},"subdivision":{"type":"string","optional":true}}}},"latency_routing_policy":{"nesting_mode":"list","block":{"attributes":{"region":{"type":"string","required":true}}}},"weighted_routing_policy":{"nesting_mode":"list","block":{"attributes":{"weight":{"type":"number","required":true}}}}}}},"aws_route53_resolver_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"direction":{"type":"string","required":true},"host_vpc_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"security_group_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"ip_address":{"nesting_mode":"set","block":{"attributes":{"ip":{"type":"string","optional":true,"computed":true},"ip_id":{"type":"string","computed":true},"subnet_id":{"type":"string","required":true}}},"min_items":2,"max_items":10},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_route53_resolver_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"resolver_endpoint_id":{"type":"string","optional":true},"rule_type":{"type":"string","required":true},"share_status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"target_ip":{"nesting_mode":"set","block":{"attributes":{"ip":{"type":"string","required":true},"port":{"type":"number","optional":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_route53_resolver_rule_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"resolver_rule_id":{"type":"string","required":true},"vpc_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_route53_vpc_association_authorization":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"vpc_id":{"type":"string","required":true},"vpc_region":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","required":true}}}},"aws_route53_zone":{"version":0,"block":{"attributes":{"comment":{"type":"string","optional":true},"delegation_set_id":{"type":"string","optional":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"name_servers":{"type":["list","string"],"computed":true},"tags":{"type":["map","string"],"optional":true},"zone_id":{"type":"string","computed":true}},"block_types":{"vpc":{"nesting_mode":"set","block":{"attributes":{"vpc_id":{"type":"string","required":true},"vpc_region":{"type":"string","optional":true,"computed":true}}}}}}},"aws_route53_zone_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"owning_account":{"type":"string","computed":true},"vpc_id":{"type":"string","required":true},"vpc_region":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","required":true}}}},"aws_route_table":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"propagating_vgws":{"type":["set","string"],"optional":true,"computed":true},"route":{"type":["set",["object",{"cidr_block":"string","egress_only_gateway_id":"string","gateway_id":"string","instance_id":"string","ipv6_cidr_block":"string","nat_gateway_id":"string","network_interface_id":"string","transit_gateway_id":"string","vpc_peering_connection_id":"string"}]],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}}}},"aws_route_table_association":{"version":0,"block":{"attributes":{"gateway_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"route_table_id":{"type":"string","required":true},"subnet_id":{"type":"string","optional":true}}}},"aws_s3_access_point":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"bucket":{"type":"string","required":true},"domain_name":{"type":"string","computed":true},"has_public_access_policy":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"network_origin":{"type":"string","computed":true},"policy":{"type":"string","optional":true}},"block_types":{"public_access_block_configuration":{"nesting_mode":"list","block":{"attributes":{"block_public_acls":{"type":"bool","optional":true},"block_public_policy":{"type":"bool","optional":true},"ignore_public_acls":{"type":"bool","optional":true},"restrict_public_buckets":{"type":"bool","optional":true}}},"max_items":1},"vpc_configuration":{"nesting_mode":"list","block":{"attributes":{"vpc_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_s3_account_public_access_block":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"block_public_acls":{"type":"bool","optional":true},"block_public_policy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"ignore_public_acls":{"type":"bool","optional":true},"restrict_public_buckets":{"type":"bool","optional":true}}}},"aws_s3_bucket":{"version":0,"block":{"attributes":{"acceleration_status":{"type":"string","optional":true,"computed":true},"acl":{"type":"string","optional":true},"arn":{"type":"string","optional":true,"computed":true},"bucket":{"type":"string","optional":true,"computed":true},"bucket_domain_name":{"type":"string","computed":true},"bucket_prefix":{"type":"string","optional":true},"bucket_regional_domain_name":{"type":"string","computed":true},"force_destroy":{"type":"bool","optional":true},"hosted_zone_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","optional":true},"region":{"type":"string","computed":true},"request_payer":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"website_domain":{"type":"string","optional":true,"computed":true},"website_endpoint":{"type":"string","optional":true,"computed":true}},"block_types":{"cors_rule":{"nesting_mode":"list","block":{"attributes":{"allowed_headers":{"type":["list","string"],"optional":true},"allowed_methods":{"type":["list","string"],"required":true},"allowed_origins":{"type":["list","string"],"required":true},"expose_headers":{"type":["list","string"],"optional":true},"max_age_seconds":{"type":"number","optional":true}}}},"grant":{"nesting_mode":"set","block":{"attributes":{"id":{"type":"string","optional":true},"permissions":{"type":["set","string"],"required":true},"type":{"type":"string","required":true},"uri":{"type":"string","optional":true}}}},"lifecycle_rule":{"nesting_mode":"list","block":{"attributes":{"abort_incomplete_multipart_upload_days":{"type":"number","optional":true},"enabled":{"type":"bool","required":true},"id":{"type":"string","optional":true,"computed":true},"prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"expiration":{"nesting_mode":"list","block":{"attributes":{"date":{"type":"string","optional":true},"days":{"type":"number","optional":true},"expired_object_delete_marker":{"type":"bool","optional":true}}},"max_items":1},"noncurrent_version_expiration":{"nesting_mode":"list","block":{"attributes":{"days":{"type":"number","optional":true}}},"max_items":1},"noncurrent_version_transition":{"nesting_mode":"set","block":{"attributes":{"days":{"type":"number","optional":true},"storage_class":{"type":"string","required":true}}}},"transition":{"nesting_mode":"set","block":{"attributes":{"date":{"type":"string","optional":true},"days":{"type":"number","optional":true},"storage_class":{"type":"string","required":true}}}}}}},"logging":{"nesting_mode":"set","block":{"attributes":{"target_bucket":{"type":"string","required":true},"target_prefix":{"type":"string","optional":true}}}},"object_lock_configuration":{"nesting_mode":"list","block":{"attributes":{"object_lock_enabled":{"type":"string","required":true}},"block_types":{"rule":{"nesting_mode":"list","block":{"block_types":{"default_retention":{"nesting_mode":"list","block":{"attributes":{"days":{"type":"number","optional":true},"mode":{"type":"string","required":true},"years":{"type":"number","optional":true}}},"min_items":1,"max_items":1}}},"max_items":1}}},"max_items":1},"replication_configuration":{"nesting_mode":"list","block":{"attributes":{"role":{"type":"string","required":true}},"block_types":{"rules":{"nesting_mode":"set","block":{"attributes":{"id":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"priority":{"type":"number","optional":true},"status":{"type":"string","required":true}},"block_types":{"destination":{"nesting_mode":"list","block":{"attributes":{"account_id":{"type":"string","optional":true},"bucket":{"type":"string","required":true},"replica_kms_key_id":{"type":"string","optional":true},"storage_class":{"type":"string","optional":true}},"block_types":{"access_control_translation":{"nesting_mode":"list","block":{"attributes":{"owner":{"type":"string","required":true}}},"max_items":1}}},"min_items":1,"max_items":1},"filter":{"nesting_mode":"list","block":{"attributes":{"prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}},"max_items":1},"source_selection_criteria":{"nesting_mode":"list","block":{"block_types":{"sse_kms_encrypted_objects":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"server_side_encryption_configuration":{"nesting_mode":"list","block":{"block_types":{"rule":{"nesting_mode":"list","block":{"block_types":{"apply_server_side_encryption_by_default":{"nesting_mode":"list","block":{"attributes":{"kms_master_key_id":{"type":"string","optional":true},"sse_algorithm":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1},"versioning":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"mfa_delete":{"type":"bool","optional":true}}},"max_items":1},"website":{"nesting_mode":"list","block":{"attributes":{"error_document":{"type":"string","optional":true},"index_document":{"type":"string","optional":true},"redirect_all_requests_to":{"type":"string","optional":true},"routing_rules":{"type":"string","optional":true}}},"max_items":1}}}},"aws_s3_bucket_analytics_configuration":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"filter":{"nesting_mode":"list","block":{"attributes":{"prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}},"max_items":1},"storage_class_analysis":{"nesting_mode":"list","block":{"block_types":{"data_export":{"nesting_mode":"list","block":{"attributes":{"output_schema_version":{"type":"string","optional":true}},"block_types":{"destination":{"nesting_mode":"list","block":{"block_types":{"s3_bucket_destination":{"nesting_mode":"list","block":{"attributes":{"bucket_account_id":{"type":"string","optional":true},"bucket_arn":{"type":"string","required":true},"format":{"type":"string","optional":true},"prefix":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1}}}},"aws_s3_bucket_inventory":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"included_object_versions":{"type":"string","required":true},"name":{"type":"string","required":true},"optional_fields":{"type":["set","string"],"optional":true}},"block_types":{"destination":{"nesting_mode":"list","block":{"block_types":{"bucket":{"nesting_mode":"list","block":{"attributes":{"account_id":{"type":"string","optional":true},"bucket_arn":{"type":"string","required":true},"format":{"type":"string","required":true},"prefix":{"type":"string","optional":true}},"block_types":{"encryption":{"nesting_mode":"list","block":{"block_types":{"sse_kms":{"nesting_mode":"list","block":{"attributes":{"key_id":{"type":"string","required":true}}},"max_items":1},"sse_s3":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"filter":{"nesting_mode":"list","block":{"attributes":{"prefix":{"type":"string","optional":true}}},"max_items":1},"schedule":{"nesting_mode":"list","block":{"attributes":{"frequency":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_s3_bucket_metric":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"filter":{"nesting_mode":"list","block":{"attributes":{"prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}},"max_items":1}}}},"aws_s3_bucket_notification":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}},"block_types":{"lambda_function":{"nesting_mode":"list","block":{"attributes":{"events":{"type":["set","string"],"required":true},"filter_prefix":{"type":"string","optional":true},"filter_suffix":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"lambda_function_arn":{"type":"string","optional":true}}}},"queue":{"nesting_mode":"list","block":{"attributes":{"events":{"type":["set","string"],"required":true},"filter_prefix":{"type":"string","optional":true},"filter_suffix":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"queue_arn":{"type":"string","required":true}}}},"topic":{"nesting_mode":"list","block":{"attributes":{"events":{"type":["set","string"],"required":true},"filter_prefix":{"type":"string","optional":true},"filter_suffix":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"topic_arn":{"type":"string","required":true}}}}}}},"aws_s3_bucket_object":{"version":0,"block":{"attributes":{"acl":{"type":"string","optional":true},"bucket":{"type":"string","required":true},"cache_control":{"type":"string","optional":true},"content":{"type":"string","optional":true},"content_base64":{"type":"string","optional":true},"content_disposition":{"type":"string","optional":true},"content_encoding":{"type":"string","optional":true},"content_language":{"type":"string","optional":true},"content_type":{"type":"string","optional":true,"computed":true},"etag":{"type":"string","optional":true,"computed":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","required":true},"kms_key_id":{"type":"string","optional":true},"metadata":{"type":["map","string"],"optional":true},"object_lock_legal_hold_status":{"type":"string","optional":true},"object_lock_mode":{"type":"string","optional":true},"object_lock_retain_until_date":{"type":"string","optional":true},"server_side_encryption":{"type":"string","optional":true,"computed":true},"source":{"type":"string","optional":true},"storage_class":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"version_id":{"type":"string","computed":true},"website_redirect":{"type":"string","optional":true}}}},"aws_s3_bucket_policy":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true}}}},"aws_s3_bucket_public_access_block":{"version":0,"block":{"attributes":{"block_public_acls":{"type":"bool","optional":true},"block_public_policy":{"type":"bool","optional":true},"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ignore_public_acls":{"type":"bool","optional":true},"restrict_public_buckets":{"type":"bool","optional":true}}}},"aws_sagemaker_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"endpoint_config_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_sagemaker_endpoint_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"production_variants":{"nesting_mode":"list","block":{"attributes":{"accelerator_type":{"type":"string","optional":true},"initial_instance_count":{"type":"number","required":true},"initial_variant_weight":{"type":"number","optional":true},"instance_type":{"type":"string","required":true},"model_name":{"type":"string","required":true},"variant_name":{"type":"string","optional":true,"computed":true}}},"min_items":1}}}},"aws_sagemaker_model":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"enable_network_isolation":{"type":"bool","optional":true},"execution_role_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"container":{"nesting_mode":"list","block":{"attributes":{"container_hostname":{"type":"string","optional":true},"environment":{"type":["map","string"],"optional":true},"image":{"type":"string","required":true},"model_data_url":{"type":"string","optional":true}}}},"primary_container":{"nesting_mode":"list","block":{"attributes":{"container_hostname":{"type":"string","optional":true},"environment":{"type":["map","string"],"optional":true},"image":{"type":"string","required":true},"model_data_url":{"type":"string","optional":true}}},"max_items":1},"vpc_config":{"nesting_mode":"list","block":{"attributes":{"security_group_ids":{"type":["set","string"],"required":true},"subnets":{"type":["set","string"],"required":true}}},"max_items":1}}}},"aws_sagemaker_notebook_instance":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"direct_internet_access":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","required":true},"kms_key_id":{"type":"string","optional":true},"lifecycle_config_name":{"type":"string","optional":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"subnet_id":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_sagemaker_notebook_instance_lifecycle_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"on_create":{"type":"string","optional":true},"on_start":{"type":"string","optional":true}}}},"aws_secretsmanager_secret":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","optional":true},"recovery_window_in_days":{"type":"number","optional":true},"rotation_enabled":{"type":"bool","computed":true},"rotation_lambda_arn":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"rotation_rules":{"nesting_mode":"list","block":{"attributes":{"automatically_after_days":{"type":"number","required":true}}},"max_items":1}}}},"aws_secretsmanager_secret_rotation":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"rotation_enabled":{"type":"bool","computed":true},"rotation_lambda_arn":{"type":"string","required":true},"secret_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"rotation_rules":{"nesting_mode":"list","block":{"attributes":{"automatically_after_days":{"type":"number","required":true}}},"min_items":1,"max_items":1}}}},"aws_secretsmanager_secret_version":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"secret_binary":{"type":"string","optional":true,"sensitive":true},"secret_id":{"type":"string","required":true},"secret_string":{"type":"string","optional":true,"sensitive":true},"version_id":{"type":"string","computed":true},"version_stages":{"type":["set","string"],"optional":true,"computed":true}}}},"aws_security_group":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"egress":{"type":["set",["object",{"cidr_blocks":["list","string"],"description":"string","from_port":"number","ipv6_cidr_blocks":["list","string"],"prefix_list_ids":["list","string"],"protocol":"string","security_groups":["set","string"],"self":"bool","to_port":"number"}]],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ingress":{"type":["set",["object",{"cidr_blocks":["list","string"],"description":"string","from_port":"number","ipv6_cidr_blocks":["list","string"],"prefix_list_ids":["list","string"],"protocol":"string","security_groups":["set","string"],"self":"bool","to_port":"number"}]],"optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"revoke_rules_on_delete":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_security_group_rule":{"version":2,"block":{"attributes":{"cidr_blocks":{"type":["list","string"],"optional":true},"description":{"type":"string","optional":true},"from_port":{"type":"number","required":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_blocks":{"type":["list","string"],"optional":true},"prefix_list_ids":{"type":["list","string"],"optional":true},"protocol":{"type":"string","required":true},"security_group_id":{"type":"string","required":true},"self":{"type":"bool","optional":true},"source_security_group_id":{"type":"string","optional":true,"computed":true},"to_port":{"type":"number","required":true},"type":{"type":"string","description":"Type of rule, ingress (inbound) or egress (outbound).","required":true}}}},"aws_securityhub_account":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true}}}},"aws_securityhub_member":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"email":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"invite":{"type":"bool","optional":true},"master_id":{"type":"string","computed":true},"member_status":{"type":"string","computed":true}}}},"aws_securityhub_product_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"product_arn":{"type":"string","required":true}}}},"aws_securityhub_standards_subscription":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"standards_arn":{"type":"string","required":true}}}},"aws_service_discovery_http_namespace":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_service_discovery_private_dns_namespace":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"hosted_zone":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vpc":{"type":"string","required":true}}}},"aws_service_discovery_public_dns_namespace":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"hosted_zone":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_service_discovery_service":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"namespace_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"dns_config":{"nesting_mode":"list","block":{"attributes":{"namespace_id":{"type":"string","required":true},"routing_policy":{"type":"string","optional":true}},"block_types":{"dns_records":{"nesting_mode":"list","block":{"attributes":{"ttl":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"health_check_config":{"nesting_mode":"list","block":{"attributes":{"failure_threshold":{"type":"number","optional":true},"resource_path":{"type":"string","optional":true},"type":{"type":"string","optional":true}}},"max_items":1},"health_check_custom_config":{"nesting_mode":"list","block":{"attributes":{"failure_threshold":{"type":"number","optional":true}}},"max_items":1}}}},"aws_servicecatalog_portfolio":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_time":{"type":"string","computed":true},"description":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"provider_name":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_servicequotas_service_quota":{"version":0,"block":{"attributes":{"adjustable":{"type":"bool","computed":true},"arn":{"type":"string","computed":true},"default_value":{"type":"number","computed":true},"id":{"type":"string","optional":true,"computed":true},"quota_code":{"type":"string","required":true},"quota_name":{"type":"string","computed":true},"request_id":{"type":"string","computed":true},"request_status":{"type":"string","computed":true},"service_code":{"type":"string","required":true},"service_name":{"type":"string","computed":true},"value":{"type":"number","required":true}}}},"aws_ses_active_receipt_rule_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"rule_set_name":{"type":"string","required":true}}}},"aws_ses_configuration_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_ses_domain_dkim":{"version":0,"block":{"attributes":{"dkim_tokens":{"type":["list","string"],"computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ses_domain_identity":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"verification_token":{"type":"string","computed":true}}}},"aws_ses_domain_identity_verification":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_ses_domain_mail_from":{"version":0,"block":{"attributes":{"behavior_on_mx_failure":{"type":"string","optional":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"mail_from_domain":{"type":"string","required":true}}}},"aws_ses_email_identity":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"email":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ses_event_destination":{"version":0,"block":{"attributes":{"configuration_set_name":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"matching_types":{"type":["set","string"],"required":true},"name":{"type":"string","required":true}},"block_types":{"cloudwatch_destination":{"nesting_mode":"set","block":{"attributes":{"default_value":{"type":"string","required":true},"dimension_name":{"type":"string","required":true},"value_source":{"type":"string","required":true}}}},"kinesis_destination":{"nesting_mode":"list","block":{"attributes":{"role_arn":{"type":"string","required":true},"stream_arn":{"type":"string","required":true}}},"max_items":1},"sns_destination":{"nesting_mode":"list","block":{"attributes":{"topic_arn":{"type":"string","required":true}}},"max_items":1}}}},"aws_ses_identity_notification_topic":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"identity":{"type":"string","required":true},"include_original_headers":{"type":"bool","optional":true},"notification_type":{"type":"string","required":true},"topic_arn":{"type":"string","optional":true}}}},"aws_ses_identity_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"identity":{"type":"string","required":true},"name":{"type":"string","required":true},"policy":{"type":"string","required":true}}}},"aws_ses_receipt_filter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cidr":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"policy":{"type":"string","required":true}}}},"aws_ses_receipt_rule":{"version":0,"block":{"attributes":{"after":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"recipients":{"type":["set","string"],"optional":true},"rule_set_name":{"type":"string","required":true},"scan_enabled":{"type":"bool","optional":true,"computed":true},"tls_policy":{"type":"string","optional":true,"computed":true}},"block_types":{"add_header_action":{"nesting_mode":"set","block":{"attributes":{"header_name":{"type":"string","required":true},"header_value":{"type":"string","required":true},"position":{"type":"number","required":true}}}},"bounce_action":{"nesting_mode":"set","block":{"attributes":{"message":{"type":"string","required":true},"position":{"type":"number","required":true},"sender":{"type":"string","required":true},"smtp_reply_code":{"type":"string","required":true},"status_code":{"type":"string","optional":true},"topic_arn":{"type":"string","optional":true}}}},"lambda_action":{"nesting_mode":"set","block":{"attributes":{"function_arn":{"type":"string","required":true},"invocation_type":{"type":"string","optional":true,"computed":true},"position":{"type":"number","required":true},"topic_arn":{"type":"string","optional":true}}}},"s3_action":{"nesting_mode":"set","block":{"attributes":{"bucket_name":{"type":"string","required":true},"kms_key_arn":{"type":"string","optional":true},"object_key_prefix":{"type":"string","optional":true},"position":{"type":"number","required":true},"topic_arn":{"type":"string","optional":true}}}},"sns_action":{"nesting_mode":"set","block":{"attributes":{"position":{"type":"number","required":true},"topic_arn":{"type":"string","required":true}}}},"stop_action":{"nesting_mode":"set","block":{"attributes":{"position":{"type":"number","required":true},"scope":{"type":"string","required":true},"topic_arn":{"type":"string","optional":true}}}},"workmail_action":{"nesting_mode":"set","block":{"attributes":{"organization_arn":{"type":"string","required":true},"position":{"type":"number","required":true},"topic_arn":{"type":"string","optional":true}}}}}}},"aws_ses_receipt_rule_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"rule_set_name":{"type":"string","required":true}}}},"aws_ses_template":{"version":0,"block":{"attributes":{"html":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"subject":{"type":"string","optional":true},"text":{"type":"string","optional":true}}}},"aws_sfn_activity":{"version":0,"block":{"attributes":{"creation_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_sfn_state_machine":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_date":{"type":"string","computed":true},"definition":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_shield_protection":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"resource_arn":{"type":"string","required":true}}}},"aws_simpledb_domain":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_snapshot_create_volume_permission":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"snapshot_id":{"type":"string","required":true}}}},"aws_sns_platform_application":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"event_delivery_failure_topic_arn":{"type":"string","optional":true},"event_endpoint_created_topic_arn":{"type":"string","optional":true},"event_endpoint_deleted_topic_arn":{"type":"string","optional":true},"event_endpoint_updated_topic_arn":{"type":"string","optional":true},"failure_feedback_role_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"platform":{"type":"string","required":true},"platform_credential":{"type":"string","required":true,"sensitive":true},"platform_principal":{"type":"string","optional":true,"sensitive":true},"success_feedback_role_arn":{"type":"string","optional":true},"success_feedback_sample_rate":{"type":"string","optional":true}}}},"aws_sns_sms_preferences":{"version":0,"block":{"attributes":{"default_sender_id":{"type":"string","optional":true},"default_sms_type":{"type":"string","optional":true},"delivery_status_iam_role_arn":{"type":"string","optional":true},"delivery_status_success_sampling_rate":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"monthly_spend_limit":{"type":"string","optional":true},"usage_report_s3_bucket":{"type":"string","optional":true}}}},"aws_sns_topic":{"version":0,"block":{"attributes":{"application_failure_feedback_role_arn":{"type":"string","optional":true},"application_success_feedback_role_arn":{"type":"string","optional":true},"application_success_feedback_sample_rate":{"type":"number","optional":true},"arn":{"type":"string","computed":true},"delivery_policy":{"type":"string","optional":true},"display_name":{"type":"string","optional":true},"http_failure_feedback_role_arn":{"type":"string","optional":true},"http_success_feedback_role_arn":{"type":"string","optional":true},"http_success_feedback_sample_rate":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_master_key_id":{"type":"string","optional":true},"lambda_failure_feedback_role_arn":{"type":"string","optional":true},"lambda_success_feedback_role_arn":{"type":"string","optional":true},"lambda_success_feedback_sample_rate":{"type":"number","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","optional":true,"computed":true},"sqs_failure_feedback_role_arn":{"type":"string","optional":true},"sqs_success_feedback_role_arn":{"type":"string","optional":true},"sqs_success_feedback_sample_rate":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_sns_topic_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true}}}},"aws_sns_topic_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"confirmation_timeout_in_minutes":{"type":"number","optional":true},"delivery_policy":{"type":"string","optional":true},"endpoint":{"type":"string","required":true},"endpoint_auto_confirms":{"type":"bool","optional":true},"filter_policy":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"protocol":{"type":"string","required":true},"raw_message_delivery":{"type":"bool","optional":true},"topic_arn":{"type":"string","required":true}}}},"aws_spot_datafeed_subscription":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"prefix":{"type":"string","optional":true}}}},"aws_spot_fleet_request":{"version":1,"block":{"attributes":{"allocation_strategy":{"type":"string","optional":true},"client_token":{"type":"string","computed":true},"excess_capacity_termination_policy":{"type":"string","optional":true},"fleet_type":{"type":"string","optional":true},"iam_fleet_role":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instance_interruption_behaviour":{"type":"string","optional":true},"instance_pools_to_use_count":{"type":"number","optional":true},"load_balancers":{"type":["set","string"],"optional":true,"computed":true},"replace_unhealthy_instances":{"type":"bool","optional":true},"spot_price":{"type":"string","optional":true},"spot_request_state":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"target_capacity":{"type":"number","required":true},"target_group_arns":{"type":["set","string"],"optional":true,"computed":true},"terminate_instances_with_expiration":{"type":"bool","optional":true},"valid_from":{"type":"string","optional":true},"valid_until":{"type":"string","optional":true},"wait_for_fulfillment":{"type":"bool","optional":true}},"block_types":{"launch_specification":{"nesting_mode":"set","block":{"attributes":{"ami":{"type":"string","required":true},"associate_public_ip_address":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"ebs_optimized":{"type":"bool","optional":true},"iam_instance_profile":{"type":"string","optional":true},"iam_instance_profile_arn":{"type":"string","optional":true},"instance_type":{"type":"string","required":true},"key_name":{"type":"string","optional":true,"computed":true},"monitoring":{"type":"bool","optional":true},"placement_group":{"type":"string","optional":true,"computed":true},"placement_tenancy":{"type":"string","optional":true},"spot_price":{"type":"string","optional":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"user_data":{"type":"string","optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"weighted_capacity":{"type":"string","optional":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"virtual_name":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}}}}},"launch_template_config":{"nesting_mode":"set","block":{"block_types":{"launch_template_specification":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","optional":true},"name":{"type":"string","optional":true},"version":{"type":"string","optional":true}}},"min_items":1,"max_items":1},"overrides":{"nesting_mode":"set","block":{"attributes":{"availability_zone":{"type":"string","optional":true},"instance_type":{"type":"string","optional":true},"priority":{"type":"number","optional":true,"computed":true},"spot_price":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true},"weighted_capacity":{"type":"number","optional":true,"computed":true}}}}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_spot_instance_request":{"version":0,"block":{"attributes":{"ami":{"type":"string","required":true},"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","optional":true,"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"block_duration_minutes":{"type":"number","optional":true},"cpu_core_count":{"type":"number","optional":true,"computed":true},"cpu_threads_per_core":{"type":"number","optional":true,"computed":true},"disable_api_termination":{"type":"bool","optional":true},"ebs_optimized":{"type":"bool","optional":true},"get_password_data":{"type":"bool","optional":true},"hibernation":{"type":"bool","optional":true},"host_id":{"type":"string","optional":true,"computed":true},"iam_instance_profile":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_initiated_shutdown_behavior":{"type":"string","optional":true},"instance_interruption_behaviour":{"type":"string","optional":true},"instance_state":{"type":"string","computed":true},"instance_type":{"type":"string","required":true},"ipv6_address_count":{"type":"number","optional":true,"computed":true},"ipv6_addresses":{"type":["list","string"],"optional":true,"computed":true},"key_name":{"type":"string","optional":true,"computed":true},"launch_group":{"type":"string","optional":true},"monitoring":{"type":"bool","optional":true},"outpost_arn":{"type":"string","computed":true},"password_data":{"type":"string","computed":true},"placement_group":{"type":"string","optional":true,"computed":true},"primary_network_interface_id":{"type":"string","computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","optional":true,"computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"secondary_private_ips":{"type":["set","string"],"optional":true,"computed":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"source_dest_check":{"type":"bool","optional":true},"spot_bid_status":{"type":"string","computed":true},"spot_instance_id":{"type":"string","computed":true},"spot_price":{"type":"string","optional":true},"spot_request_state":{"type":"string","computed":true},"spot_type":{"type":"string","optional":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"tenancy":{"type":"string","optional":true,"computed":true},"user_data":{"type":"string","optional":true},"user_data_base64":{"type":"string","optional":true},"valid_from":{"type":"string","optional":true,"computed":true},"valid_until":{"type":"string","optional":true,"computed":true},"volume_tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"wait_for_fulfillment":{"type":"bool","optional":true}},"block_types":{"credit_specification":{"nesting_mode":"list","block":{"attributes":{"cpu_credits":{"type":"string","optional":true}}},"max_items":1},"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"no_device":{"type":"bool","optional":true},"virtual_name":{"type":"string","optional":true}}}},"metadata_options":{"nesting_mode":"list","block":{"attributes":{"http_endpoint":{"type":"string","optional":true,"computed":true},"http_put_response_hop_limit":{"type":"number","optional":true,"computed":true},"http_tokens":{"type":"string","optional":true,"computed":true}}},"max_items":1},"network_interface":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_index":{"type":"number","required":true},"network_interface_id":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"list","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","computed":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_sqs_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"content_based_deduplication":{"type":"bool","optional":true},"delay_seconds":{"type":"number","optional":true},"fifo_queue":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_data_key_reuse_period_seconds":{"type":"number","optional":true,"computed":true},"kms_master_key_id":{"type":"string","optional":true},"max_message_size":{"type":"number","optional":true},"message_retention_seconds":{"type":"number","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","optional":true,"computed":true},"receive_wait_time_seconds":{"type":"number","optional":true},"redrive_policy":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"visibility_timeout_seconds":{"type":"number","optional":true}}}},"aws_sqs_queue_policy":{"version":1,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true},"queue_url":{"type":"string","required":true}}}},"aws_ssm_activation":{"version":0,"block":{"attributes":{"activation_code":{"type":"string","computed":true},"description":{"type":"string","optional":true},"expiration_date":{"type":"string","optional":true,"computed":true},"expired":{"type":"bool","computed":true},"iam_role":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"registration_count":{"type":"number","computed":true},"registration_limit":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_ssm_association":{"version":1,"block":{"attributes":{"association_id":{"type":"string","computed":true},"association_name":{"type":"string","optional":true},"automation_target_parameter_name":{"type":"string","optional":true},"compliance_severity":{"type":"string","optional":true},"document_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true},"max_concurrency":{"type":"string","optional":true},"max_errors":{"type":"string","optional":true},"name":{"type":"string","required":true},"parameters":{"type":["map","string"],"optional":true,"computed":true},"schedule_expression":{"type":"string","optional":true}},"block_types":{"output_location":{"nesting_mode":"list","block":{"attributes":{"s3_bucket_name":{"type":"string","required":true},"s3_key_prefix":{"type":"string","optional":true}}},"max_items":1},"targets":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"max_items":5}}}},"aws_ssm_document":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"content":{"type":"string","required":true},"created_date":{"type":"string","computed":true},"default_version":{"type":"string","computed":true},"description":{"type":"string","computed":true},"document_format":{"type":"string","optional":true},"document_type":{"type":"string","required":true},"document_version":{"type":"string","computed":true},"hash":{"type":"string","computed":true},"hash_type":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"latest_version":{"type":"string","computed":true},"name":{"type":"string","required":true},"owner":{"type":"string","computed":true},"parameter":{"type":["list",["object",{"default_value":"string","description":"string","name":"string","type":"string"}]],"computed":true},"permissions":{"type":["map","string"],"optional":true},"platform_types":{"type":["list","string"],"computed":true},"schema_version":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"target_type":{"type":"string","optional":true}},"block_types":{"attachments_source":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"name":{"type":"string","optional":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ssm_maintenance_window":{"version":0,"block":{"attributes":{"allow_unassociated_targets":{"type":"bool","optional":true},"cutoff":{"type":"number","required":true},"description":{"type":"string","optional":true},"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true},"end_date":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"schedule":{"type":"string","required":true},"schedule_timezone":{"type":"string","optional":true},"start_date":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_ssm_maintenance_window_target":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"owner_information":{"type":"string","optional":true},"resource_type":{"type":"string","required":true},"window_id":{"type":"string","required":true}},"block_types":{"targets":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"min_items":1,"max_items":5}}}},"aws_ssm_maintenance_window_task":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"max_concurrency":{"type":"string","required":true},"max_errors":{"type":"string","required":true},"name":{"type":"string","optional":true},"priority":{"type":"number","optional":true},"service_role_arn":{"type":"string","required":true},"task_arn":{"type":"string","required":true},"task_type":{"type":"string","required":true},"window_id":{"type":"string","required":true}},"block_types":{"targets":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"min_items":1},"task_invocation_parameters":{"nesting_mode":"list","block":{"block_types":{"automation_parameters":{"nesting_mode":"list","block":{"attributes":{"document_version":{"type":"string","optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}},"max_items":1},"lambda_parameters":{"nesting_mode":"list","block":{"attributes":{"client_context":{"type":"string","optional":true},"payload":{"type":"string","optional":true,"sensitive":true},"qualifier":{"type":"string","optional":true}}},"max_items":1},"run_command_parameters":{"nesting_mode":"list","block":{"attributes":{"comment":{"type":"string","optional":true},"document_hash":{"type":"string","optional":true},"document_hash_type":{"type":"string","optional":true},"output_s3_bucket":{"type":"string","optional":true},"output_s3_key_prefix":{"type":"string","optional":true},"service_role_arn":{"type":"string","optional":true},"timeout_seconds":{"type":"number","optional":true}},"block_types":{"notification_config":{"nesting_mode":"list","block":{"attributes":{"notification_arn":{"type":"string","optional":true},"notification_events":{"type":["list","string"],"optional":true},"notification_type":{"type":"string","optional":true}}},"max_items":1},"parameter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}},"max_items":1},"step_functions_parameters":{"nesting_mode":"list","block":{"attributes":{"input":{"type":"string","optional":true,"sensitive":true},"name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_ssm_parameter":{"version":0,"block":{"attributes":{"allowed_pattern":{"type":"string","optional":true},"arn":{"type":"string","optional":true,"computed":true},"data_type":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"overwrite":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"tier":{"type":"string","optional":true},"type":{"type":"string","required":true},"value":{"type":"string","required":true,"sensitive":true},"version":{"type":"number","computed":true}}}},"aws_ssm_patch_baseline":{"version":0,"block":{"attributes":{"approved_patches":{"type":["set","string"],"optional":true},"approved_patches_compliance_level":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"operating_system":{"type":"string","optional":true},"rejected_patches":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"approval_rule":{"nesting_mode":"list","block":{"attributes":{"approve_after_days":{"type":"number","required":true},"compliance_level":{"type":"string","optional":true},"enable_non_security":{"type":"bool","optional":true}},"block_types":{"patch_filter":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"min_items":1,"max_items":10}}}},"global_filter":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"max_items":4}}}},"aws_ssm_patch_group":{"version":0,"block":{"attributes":{"baseline_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"patch_group":{"type":"string","required":true}}}},"aws_ssm_resource_data_sync":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"s3_destination":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","required":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"region":{"type":"string","required":true},"sync_format":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}}},"aws_storagegateway_cache":{"version":0,"block":{"attributes":{"disk_id":{"type":"string","required":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_storagegateway_cached_iscsi_volume":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"chap_enabled":{"type":"bool","computed":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"lun_number":{"type":"number","computed":true},"network_interface_id":{"type":"string","required":true},"network_interface_port":{"type":"number","computed":true},"snapshot_id":{"type":"string","optional":true},"source_volume_arn":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"target_arn":{"type":"string","computed":true},"target_name":{"type":"string","required":true},"volume_arn":{"type":"string","computed":true},"volume_id":{"type":"string","computed":true},"volume_size_in_bytes":{"type":"number","required":true}}}},"aws_storagegateway_gateway":{"version":0,"block":{"attributes":{"activation_key":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"cloudwatch_log_group_arn":{"type":"string","optional":true},"gateway_id":{"type":"string","computed":true},"gateway_ip_address":{"type":"string","optional":true,"computed":true},"gateway_name":{"type":"string","required":true},"gateway_timezone":{"type":"string","required":true},"gateway_type":{"type":"string","optional":true},"gateway_vpc_endpoint":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"medium_changer_type":{"type":"string","optional":true},"smb_guest_password":{"type":"string","optional":true,"sensitive":true},"tags":{"type":["map","string"],"optional":true},"tape_drive_type":{"type":"string","optional":true}},"block_types":{"smb_active_directory_settings":{"nesting_mode":"list","block":{"attributes":{"domain_name":{"type":"string","required":true},"password":{"type":"string","required":true,"sensitive":true},"username":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_storagegateway_nfs_file_share":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"client_list":{"type":["set","string"],"required":true},"default_storage_class":{"type":"string","optional":true},"fileshare_id":{"type":"string","computed":true},"gateway_arn":{"type":"string","required":true},"guess_mime_type_enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_encrypted":{"type":"bool","optional":true},"kms_key_arn":{"type":"string","optional":true},"location_arn":{"type":"string","required":true},"object_acl":{"type":"string","optional":true},"path":{"type":"string","computed":true},"read_only":{"type":"bool","optional":true},"requester_pays":{"type":"bool","optional":true},"role_arn":{"type":"string","required":true},"squash":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"nfs_file_share_defaults":{"nesting_mode":"list","block":{"attributes":{"directory_mode":{"type":"string","optional":true},"file_mode":{"type":"string","optional":true},"group_id":{"type":"number","optional":true},"owner_id":{"type":"number","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_storagegateway_smb_file_share":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"authentication":{"type":"string","optional":true},"default_storage_class":{"type":"string","optional":true},"fileshare_id":{"type":"string","computed":true},"gateway_arn":{"type":"string","required":true},"guess_mime_type_enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"invalid_user_list":{"type":["set","string"],"optional":true},"kms_encrypted":{"type":"bool","optional":true},"kms_key_arn":{"type":"string","optional":true},"location_arn":{"type":"string","required":true},"object_acl":{"type":"string","optional":true},"path":{"type":"string","computed":true},"read_only":{"type":"bool","optional":true},"requester_pays":{"type":"bool","optional":true},"role_arn":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"valid_user_list":{"type":["set","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_storagegateway_upload_buffer":{"version":0,"block":{"attributes":{"disk_id":{"type":"string","required":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_storagegateway_working_storage":{"version":0,"block":{"attributes":{"disk_id":{"type":"string","required":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_subnet":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_ipv6_address_on_creation":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"availability_zone_id":{"type":"string","optional":true,"computed":true},"cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block_association_id":{"type":"string","computed":true},"map_public_ip_on_launch":{"type":"bool","optional":true},"outpost_arn":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_swf_domain":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"workflow_execution_retention_period_in_days":{"type":"string","required":true}}}},"aws_transfer_server":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"endpoint_type":{"type":"string","optional":true},"force_destroy":{"type":"bool","optional":true},"host_key":{"type":"string","optional":true,"sensitive":true},"host_key_fingerprint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identity_provider_type":{"type":"string","optional":true},"invocation_role":{"type":"string","optional":true},"logging_role":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"url":{"type":"string","optional":true}},"block_types":{"endpoint_details":{"nesting_mode":"list","block":{"attributes":{"vpc_endpoint_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_transfer_ssh_key":{"version":0,"block":{"attributes":{"body":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"server_id":{"type":"string","required":true},"user_name":{"type":"string","required":true}}}},"aws_transfer_user":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"home_directory":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","optional":true},"role":{"type":"string","required":true},"server_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"user_name":{"type":"string","required":true}}}},"aws_volume_attachment":{"version":0,"block":{"attributes":{"device_name":{"type":"string","required":true},"force_detach":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","required":true},"skip_destroy":{"type":"bool","optional":true},"volume_id":{"type":"string","required":true}}}},"aws_vpc":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_generated_ipv6_cidr_block":{"type":"bool","optional":true},"cidr_block":{"type":"string","required":true},"default_network_acl_id":{"type":"string","computed":true},"default_route_table_id":{"type":"string","computed":true},"default_security_group_id":{"type":"string","computed":true},"dhcp_options_id":{"type":"string","computed":true},"enable_classiclink":{"type":"bool","optional":true,"computed":true},"enable_classiclink_dns_support":{"type":"bool","optional":true,"computed":true},"enable_dns_hostnames":{"type":"bool","optional":true,"computed":true},"enable_dns_support":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_tenancy":{"type":"string","optional":true},"ipv6_association_id":{"type":"string","computed":true},"ipv6_cidr_block":{"type":"string","computed":true},"main_route_table_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_vpc_dhcp_options":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","optional":true},"domain_name_servers":{"type":["list","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"netbios_name_servers":{"type":["list","string"],"optional":true},"netbios_node_type":{"type":"string","optional":true},"ntp_servers":{"type":["list","string"],"optional":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_vpc_dhcp_options_association":{"version":0,"block":{"attributes":{"dhcp_options_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"vpc_id":{"type":"string","required":true}}}},"aws_vpc_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_accept":{"type":"bool","optional":true},"cidr_blocks":{"type":["list","string"],"computed":true},"dns_entry":{"type":["list",["object",{"dns_name":"string","hosted_zone_id":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_ids":{"type":["set","string"],"computed":true},"owner_id":{"type":"string","computed":true},"policy":{"type":"string","optional":true,"computed":true},"prefix_list_id":{"type":"string","computed":true},"private_dns_enabled":{"type":"bool","optional":true},"requester_managed":{"type":"bool","computed":true},"route_table_ids":{"type":["set","string"],"optional":true,"computed":true},"security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"service_name":{"type":"string","required":true},"state":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_endpoint_type":{"type":"string","optional":true},"vpc_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_vpc_endpoint_connection_notification":{"version":0,"block":{"attributes":{"connection_events":{"type":["set","string"],"required":true},"connection_notification_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"notification_type":{"type":"string","computed":true},"state":{"type":"string","computed":true},"vpc_endpoint_id":{"type":"string","optional":true},"vpc_endpoint_service_id":{"type":"string","optional":true}}}},"aws_vpc_endpoint_route_table_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"route_table_id":{"type":"string","required":true},"vpc_endpoint_id":{"type":"string","required":true}}}},"aws_vpc_endpoint_service":{"version":0,"block":{"attributes":{"acceptance_required":{"type":"bool","required":true},"allowed_principals":{"type":["set","string"],"optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"base_endpoint_dns_names":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"manages_vpc_endpoints":{"type":"bool","computed":true},"network_load_balancer_arns":{"type":["set","string"],"required":true},"private_dns_name":{"type":"string","computed":true},"service_name":{"type":"string","computed":true},"service_type":{"type":"string","computed":true},"state":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_vpc_endpoint_service_allowed_principal":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"principal_arn":{"type":"string","required":true},"vpc_endpoint_service_id":{"type":"string","required":true}}}},"aws_vpc_endpoint_subnet_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","required":true},"vpc_endpoint_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_vpc_ipv4_cidr_block_association":{"version":0,"block":{"attributes":{"cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"vpc_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_vpc_peering_connection":{"version":0,"block":{"attributes":{"accept_status":{"type":"string","computed":true},"auto_accept":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"peer_owner_id":{"type":"string","optional":true,"computed":true},"peer_region":{"type":"string","optional":true,"computed":true},"peer_vpc_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}},"block_types":{"accepter":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1},"requester":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_vpc_peering_connection_accepter":{"version":0,"block":{"attributes":{"accept_status":{"type":"string","computed":true},"auto_accept":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"peer_owner_id":{"type":"string","computed":true},"peer_region":{"type":"string","computed":true},"peer_vpc_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"vpc_peering_connection_id":{"type":"string","required":true}},"block_types":{"accepter":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1},"requester":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_vpc_peering_connection_options":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"vpc_peering_connection_id":{"type":"string","required":true}},"block_types":{"accepter":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1},"requester":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_vpn_connection":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_gateway_configuration":{"type":"string","computed":true},"customer_gateway_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"routes":{"type":["set",["object",{"destination_cidr_block":"string","source":"string","state":"string"}]],"computed":true},"static_routes_only":{"type":"bool","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_attachment_id":{"type":"string","computed":true},"transit_gateway_id":{"type":"string","optional":true},"tunnel1_address":{"type":"string","computed":true},"tunnel1_bgp_asn":{"type":"string","computed":true},"tunnel1_bgp_holdtime":{"type":"number","computed":true},"tunnel1_cgw_inside_address":{"type":"string","computed":true},"tunnel1_inside_cidr":{"type":"string","optional":true,"computed":true},"tunnel1_preshared_key":{"type":"string","optional":true,"computed":true,"sensitive":true},"tunnel1_vgw_inside_address":{"type":"string","computed":true},"tunnel2_address":{"type":"string","computed":true},"tunnel2_bgp_asn":{"type":"string","computed":true},"tunnel2_bgp_holdtime":{"type":"number","computed":true},"tunnel2_cgw_inside_address":{"type":"string","computed":true},"tunnel2_inside_cidr":{"type":"string","optional":true,"computed":true},"tunnel2_preshared_key":{"type":"string","optional":true,"computed":true,"sensitive":true},"tunnel2_vgw_inside_address":{"type":"string","computed":true},"type":{"type":"string","required":true},"vgw_telemetry":{"type":["set",["object",{"accepted_route_count":"number","last_status_change":"string","outside_ip_address":"string","status":"string","status_message":"string"}]],"computed":true},"vpn_gateway_id":{"type":"string","optional":true}}}},"aws_vpn_connection_route":{"version":0,"block":{"attributes":{"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"vpn_connection_id":{"type":"string","required":true}}}},"aws_vpn_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","optional":true,"computed":true}}}},"aws_vpn_gateway_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"vpc_id":{"type":"string","required":true},"vpn_gateway_id":{"type":"string","required":true}}}},"aws_vpn_gateway_route_propagation":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"route_table_id":{"type":"string","required":true},"vpn_gateway_id":{"type":"string","required":true}}}},"aws_waf_byte_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"byte_match_tuples":{"nesting_mode":"set","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"target_string":{"type":"string","optional":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_geo_match_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"geo_match_constraint":{"nesting_mode":"set","block":{"attributes":{"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_waf_ipset":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"ip_set_descriptors":{"nesting_mode":"set","block":{"attributes":{"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_waf_rate_based_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"rate_key":{"type":"string","required":true},"rate_limit":{"type":"number","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"predicates":{"nesting_mode":"set","block":{"attributes":{"data_id":{"type":"string","required":true},"negated":{"type":"bool","required":true},"type":{"type":"string","required":true}}}}}}},"aws_waf_regex_match_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"regex_match_tuple":{"nesting_mode":"set","block":{"attributes":{"regex_pattern_set_id":{"type":"string","required":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_regex_pattern_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"regex_pattern_strings":{"type":["set","string"],"optional":true}}}},"aws_waf_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"predicates":{"nesting_mode":"set","block":{"attributes":{"data_id":{"type":"string","required":true},"negated":{"type":"bool","required":true},"type":{"type":"string","required":true}}}}}}},"aws_waf_rule_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"activated_rule":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"rule_id":{"type":"string","required":true},"type":{"type":"string","optional":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_size_constraint_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"size_constraints":{"nesting_mode":"set","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_sql_injection_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"sql_injection_match_tuples":{"nesting_mode":"set","block":{"attributes":{"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_web_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"min_items":1,"max_items":1},"logging_configuration":{"nesting_mode":"list","block":{"attributes":{"log_destination":{"type":"string","required":true}},"block_types":{"redacted_fields":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"set","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"max_items":1},"rules":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"rule_id":{"type":"string","required":true},"type":{"type":"string","optional":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1},"override_action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1}}}}}}},"aws_waf_xss_match_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"xss_match_tuples":{"nesting_mode":"set","block":{"attributes":{"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_byte_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"byte_match_tuples":{"nesting_mode":"set","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"target_string":{"type":"string","optional":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_geo_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"geo_match_constraint":{"nesting_mode":"set","block":{"attributes":{"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_wafregional_ipset":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"ip_set_descriptor":{"nesting_mode":"set","block":{"attributes":{"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_wafregional_rate_based_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"rate_key":{"type":"string","required":true},"rate_limit":{"type":"number","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"predicate":{"nesting_mode":"set","block":{"attributes":{"data_id":{"type":"string","required":true},"negated":{"type":"bool","required":true},"type":{"type":"string","required":true}}}}}}},"aws_wafregional_regex_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"regex_match_tuple":{"nesting_mode":"set","block":{"attributes":{"regex_pattern_set_id":{"type":"string","required":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_regex_pattern_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"regex_pattern_strings":{"type":["set","string"],"optional":true}}}},"aws_wafregional_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"predicate":{"nesting_mode":"set","block":{"attributes":{"data_id":{"type":"string","required":true},"negated":{"type":"bool","required":true},"type":{"type":"string","required":true}}}}}}},"aws_wafregional_rule_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"activated_rule":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"rule_id":{"type":"string","required":true},"type":{"type":"string","optional":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_size_constraint_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"size_constraints":{"nesting_mode":"set","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_sql_injection_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"sql_injection_match_tuple":{"nesting_mode":"set","block":{"attributes":{"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_web_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"min_items":1,"max_items":1},"logging_configuration":{"nesting_mode":"list","block":{"attributes":{"log_destination":{"type":"string","required":true}},"block_types":{"redacted_fields":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"set","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"max_items":1},"rule":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"rule_id":{"type":"string","required":true},"type":{"type":"string","optional":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1},"override_action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1}}}}}}},"aws_wafregional_web_acl_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_arn":{"type":"string","required":true},"web_acl_id":{"type":"string","required":true}}}},"aws_wafregional_xss_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"xss_match_tuple":{"nesting_mode":"set","block":{"attributes":{"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafv2_ip_set":{"version":0,"block":{"attributes":{"addresses":{"type":["set","string"],"optional":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"ip_address_version":{"type":"string","required":true},"lock_token":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_wafv2_regex_pattern_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"lock_token":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"regular_expression":{"nesting_mode":"set","block":{"attributes":{"regex_string":{"type":"string","required":true}}},"max_items":10}}}},"aws_wafv2_rule_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"capacity":{"type":"number","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"lock_token":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"rule":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"priority":{"type":"number","required":true}},"block_types":{"action":{"nesting_mode":"list","block":{"block_types":{"allow":{"nesting_mode":"list","block":{},"max_items":1},"block":{"nesting_mode":"list","block":{},"max_items":1},"count":{"nesting_mode":"list","block":{},"max_items":1}}},"min_items":1,"max_items":1},"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1,"max_items":1},"visibility_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_metrics_enabled":{"type":"bool","required":true},"metric_name":{"type":"string","required":true},"sampled_requests_enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"visibility_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_metrics_enabled":{"type":"bool","required":true},"metric_name":{"type":"string","required":true},"sampled_requests_enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"aws_wafv2_web_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"capacity":{"type":"number","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"lock_token":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"block_types":{"allow":{"nesting_mode":"list","block":{},"max_items":1},"block":{"nesting_mode":"list","block":{},"max_items":1}}},"min_items":1,"max_items":1},"rule":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"priority":{"type":"number","required":true}},"block_types":{"action":{"nesting_mode":"list","block":{"block_types":{"allow":{"nesting_mode":"list","block":{},"max_items":1},"block":{"nesting_mode":"list","block":{},"max_items":1},"count":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"override_action":{"nesting_mode":"list","block":{"block_types":{"count":{"nesting_mode":"list","block":{},"max_items":1},"none":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"managed_rule_group_statement":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true},"vendor_name":{"type":"string","required":true}},"block_types":{"excluded_rule":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}}}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"rate_based_statement":{"nesting_mode":"list","block":{"attributes":{"aggregate_key_type":{"type":"string","optional":true},"limit":{"type":"number","required":true}},"block_types":{"scope_down_statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"max_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"rule_group_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"excluded_rule":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}}}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1,"max_items":1},"visibility_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_metrics_enabled":{"type":"bool","required":true},"metric_name":{"type":"string","required":true},"sampled_requests_enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"visibility_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_metrics_enabled":{"type":"bool","required":true},"metric_name":{"type":"string","required":true},"sampled_requests_enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"aws_wafv2_web_acl_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_arn":{"type":"string","required":true},"web_acl_arn":{"type":"string","required":true}}}},"aws_wafv2_web_acl_logging_configuration":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"log_destination_configs":{"type":["set","string"],"description":"AWS Kinesis Firehose Delivery Stream ARNs","required":true},"resource_arn":{"type":"string","description":"AWS WebACL ARN","required":true}},"block_types":{"redacted_fields":{"nesting_mode":"set","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":100}}}},"aws_worklink_fleet":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"audit_stream_arn":{"type":"string","optional":true},"company_code":{"type":"string","computed":true},"created_time":{"type":"string","computed":true},"device_ca_certificate":{"type":"string","optional":true},"display_name":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_time":{"type":"string","computed":true},"name":{"type":"string","required":true},"optimize_for_end_user_location":{"type":"bool","optional":true}},"block_types":{"identity_provider":{"nesting_mode":"list","block":{"attributes":{"saml_metadata":{"type":"string","required":true},"type":{"type":"string","required":true}}},"max_items":1},"network":{"nesting_mode":"list","block":{"attributes":{"security_group_ids":{"type":["set","string"],"required":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_worklink_website_certificate_authority_association":{"version":0,"block":{"attributes":{"certificate":{"type":"string","required":true},"display_name":{"type":"string","optional":true},"fleet_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"website_ca_id":{"type":"string","computed":true}}}},"aws_workspaces_directory":{"version":0,"block":{"attributes":{"alias":{"type":"string","computed":true},"customer_user_name":{"type":"string","computed":true},"directory_id":{"type":"string","required":true},"directory_name":{"type":"string","computed":true},"directory_type":{"type":"string","computed":true},"dns_ip_addresses":{"type":["set","string"],"computed":true},"iam_role_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_group_ids":{"type":["set","string"],"computed":true},"registration_code":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"workspace_security_group_id":{"type":"string","computed":true}},"block_types":{"self_service_permissions":{"nesting_mode":"list","block":{"attributes":{"change_compute_type":{"type":"bool","optional":true},"increase_volume_size":{"type":"bool","optional":true},"rebuild_workspace":{"type":"bool","optional":true},"restart_workspace":{"type":"bool","optional":true},"switch_running_mode":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_workspaces_ip_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"rules":{"nesting_mode":"set","block":{"attributes":{"description":{"type":"string","optional":true},"source":{"type":"string","required":true}}}}}}},"aws_workspaces_workspace":{"version":0,"block":{"attributes":{"bundle_id":{"type":"string","required":true},"computer_name":{"type":"string","computed":true},"directory_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","computed":true},"root_volume_encryption_enabled":{"type":"bool","optional":true},"state":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"user_name":{"type":"string","required":true},"user_volume_encryption_enabled":{"type":"bool","optional":true},"volume_encryption_key":{"type":"string","optional":true}},"block_types":{"workspace_properties":{"nesting_mode":"list","block":{"attributes":{"compute_type_name":{"type":"string","optional":true},"root_volume_size_gib":{"type":"number","optional":true},"running_mode":{"type":"string","optional":true},"running_mode_auto_stop_timeout_in_minutes":{"type":"number","optional":true,"computed":true},"user_volume_size_gib":{"type":"number","optional":true}}},"max_items":1}}}},"aws_xray_sampling_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"attributes":{"type":["map","string"],"optional":true},"fixed_rate":{"type":"number","required":true},"host":{"type":"string","required":true},"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"priority":{"type":"number","required":true},"reservoir_size":{"type":"number","required":true},"resource_arn":{"type":"string","required":true},"rule_name":{"type":"string","optional":true},"service_name":{"type":"string","required":true},"service_type":{"type":"string","required":true},"url_path":{"type":"string","required":true},"version":{"type":"number","required":true}}}}},"data_source_schemas":{"aws_acm_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"key_types":{"type":["set","string"],"optional":true},"most_recent":{"type":"bool","optional":true},"statuses":{"type":["list","string"],"optional":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"types":{"type":["list","string"],"optional":true}}}},"aws_acmpca_certificate_authority":{"version":1,"block":{"attributes":{"arn":{"type":"string","required":true},"certificate":{"type":"string","computed":true},"certificate_chain":{"type":"string","computed":true},"certificate_signing_request":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"not_after":{"type":"string","computed":true},"not_before":{"type":"string","computed":true},"serial":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"type":{"type":"string","computed":true}},"block_types":{"revocation_configuration":{"nesting_mode":"list","block":{"block_types":{"crl_configuration":{"nesting_mode":"list","block":{"attributes":{"custom_cname":{"type":"string","computed":true},"enabled":{"type":"bool","computed":true},"expiration_in_days":{"type":"number","computed":true},"s3_bucket_name":{"type":"string","computed":true}}}}}}}}}},"aws_alb":{"version":0,"block":{"attributes":{"access_logs":{"type":["list",["object",{"bucket":"string","enabled":"bool","prefix":"string"}]],"computed":true},"arn":{"type":"string","optional":true,"computed":true},"arn_suffix":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"drop_invalid_header_fields":{"type":"bool","computed":true},"enable_deletion_protection":{"type":"bool","computed":true},"enable_http2":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","computed":true},"internal":{"type":"bool","computed":true},"ip_address_type":{"type":"string","computed":true},"load_balancer_type":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_mapping":{"type":["set",["object",{"allocation_id":"string","private_ipv4_address":"string","subnet_id":"string"}]],"computed":true},"subnets":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","computed":true},"zone_id":{"type":"string","computed":true}}}},"aws_alb_listener":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"certificate_arn":{"type":"string","computed":true},"default_action":{"type":["list",["object",{"authenticate_cognito":["list",["object",{"authentication_request_extra_params":["map","string"],"on_unauthenticated_request":"string","scope":"string","session_cookie_name":"string","session_timeout":"number","user_pool_arn":"string","user_pool_client_id":"string","user_pool_domain":"string"}]],"authenticate_oidc":["list",["object",{"authentication_request_extra_params":["map","string"],"authorization_endpoint":"string","client_id":"string","client_secret":"string","issuer":"string","on_unauthenticated_request":"string","scope":"string","session_cookie_name":"string","session_timeout":"number","token_endpoint":"string","user_info_endpoint":"string"}]],"fixed_response":["list",["object",{"content_type":"string","message_body":"string","status_code":"string"}]],"order":"number","redirect":["list",["object",{"host":"string","path":"string","port":"string","protocol":"string","query":"string","status_code":"string"}]],"target_group_arn":"string","type":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"load_balancer_arn":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"protocol":{"type":"string","computed":true},"ssl_policy":{"type":"string","computed":true}}}},"aws_alb_target_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"arn_suffix":{"type":"string","computed":true},"deregistration_delay":{"type":"number","computed":true},"health_check":{"type":["list",["object",{"enabled":"bool","healthy_threshold":"number","interval":"number","matcher":"string","path":"string","port":"string","protocol":"string","timeout":"number","unhealthy_threshold":"number"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"lambda_multi_value_headers_enabled":{"type":"bool","computed":true},"load_balancing_algorithm_type":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","computed":true},"protocol":{"type":"string","computed":true},"proxy_protocol_v2":{"type":"bool","computed":true},"slow_start":{"type":"number","computed":true},"stickiness":{"type":["list",["object",{"cookie_duration":"number","enabled":"bool","type":"string"}]],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"target_type":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_ami":{"version":0,"block":{"attributes":{"architecture":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"block_device_mappings":{"type":["set",["object",{"device_name":"string","ebs":["map","string"],"no_device":"string","virtual_name":"string"}]],"computed":true},"creation_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"executable_users":{"type":["list","string"],"optional":true},"hypervisor":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","computed":true},"image_location":{"type":"string","computed":true},"image_owner_alias":{"type":"string","computed":true},"image_type":{"type":"string","computed":true},"kernel_id":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"name":{"type":"string","computed":true},"name_regex":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"owners":{"type":["list","string"],"required":true},"platform":{"type":"string","computed":true},"product_codes":{"type":["set",["object",{"product_code_id":"string","product_code_type":"string"}]],"computed":true},"public":{"type":"bool","computed":true},"ramdisk_id":{"type":"string","computed":true},"root_device_name":{"type":"string","computed":true},"root_device_type":{"type":"string","computed":true},"root_snapshot_id":{"type":"string","computed":true},"sriov_net_support":{"type":"string","computed":true},"state":{"type":"string","computed":true},"state_reason":{"type":["map","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"virtualization_type":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ami_ids":{"version":0,"block":{"attributes":{"executable_users":{"type":["list","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["list","string"],"computed":true},"name_regex":{"type":"string","optional":true},"owners":{"type":["list","string"],"required":true},"sort_ascending":{"type":"bool","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_api_gateway_api_key":{"version":0,"block":{"attributes":{"created_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"enabled":{"type":"bool","computed":true},"id":{"type":"string","required":true},"last_updated_date":{"type":"string","computed":true},"name":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"value":{"type":"string","computed":true,"sensitive":true}}}},"aws_api_gateway_resource":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"parent_id":{"type":"string","computed":true},"path":{"type":"string","required":true},"path_part":{"type":"string","computed":true},"rest_api_id":{"type":"string","required":true}}}},"aws_api_gateway_rest_api":{"version":0,"block":{"attributes":{"api_key_source":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"binary_media_types":{"type":["list","string"],"computed":true},"description":{"type":"string","computed":true},"endpoint_configuration":{"type":["list",["object",{"types":["list","string"],"vpc_endpoint_ids":["set","string"]}]],"computed":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"minimum_compression_size":{"type":"number","computed":true},"name":{"type":"string","required":true},"policy":{"type":"string","computed":true},"root_resource_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_api_gateway_vpc_link":{"version":0,"block":{"attributes":{"description":{"type":"string","computed":true},"id":{"type":"string","computed":true},"name":{"type":"string","required":true},"status":{"type":"string","computed":true},"status_message":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"target_arns":{"type":["set","string"],"computed":true}}}},"aws_arn":{"version":0,"block":{"attributes":{"account":{"type":"string","computed":true},"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"partition":{"type":"string","computed":true},"region":{"type":"string","computed":true},"resource":{"type":"string","computed":true},"service":{"type":"string","computed":true}}}},"aws_autoscaling_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"default_cooldown":{"type":"number","computed":true},"desired_capacity":{"type":"number","computed":true},"health_check_grace_period":{"type":"number","computed":true},"health_check_type":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"launch_configuration":{"type":"string","computed":true},"load_balancers":{"type":["set","string"],"computed":true},"max_size":{"type":"number","computed":true},"min_size":{"type":"number","computed":true},"name":{"type":"string","required":true},"new_instances_protected_from_scale_in":{"type":"bool","computed":true},"placement_group":{"type":"string","computed":true},"service_linked_role_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"target_group_arns":{"type":["set","string"],"computed":true},"termination_policies":{"type":["set","string"],"computed":true},"vpc_zone_identifier":{"type":"string","computed":true}}}},"aws_autoscaling_groups":{"version":0,"block":{"attributes":{"arns":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"names":{"type":["list","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_availability_zone":{"version":0,"block":{"attributes":{"all_availability_zones":{"type":"bool","optional":true},"group_name":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_suffix":{"type":"string","computed":true},"network_border_group":{"type":"string","computed":true},"opt_in_status":{"type":"string","computed":true},"region":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_availability_zones":{"version":0,"block":{"attributes":{"all_availability_zones":{"type":"bool","optional":true},"exclude_names":{"type":["set","string"],"optional":true},"exclude_zone_ids":{"type":["set","string"],"optional":true},"group_names":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"names":{"type":["list","string"],"computed":true},"state":{"type":"string","optional":true},"zone_ids":{"type":["list","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_backup_plan":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"plan_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"version":{"type":"string","computed":true}}}},"aws_backup_selection":{"version":0,"block":{"attributes":{"iam_role_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"plan_id":{"type":"string","required":true},"resources":{"type":["set","string"],"computed":true},"selection_id":{"type":"string","required":true}}}},"aws_backup_vault":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","computed":true},"name":{"type":"string","required":true},"recovery_points":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_batch_compute_environment":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compute_environment_name":{"type":"string","required":true},"ecs_cluster_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"service_role":{"type":"string","computed":true},"state":{"type":"string","computed":true},"status":{"type":"string","computed":true},"status_reason":{"type":"string","computed":true},"type":{"type":"string","computed":true}}}},"aws_batch_job_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compute_environment_order":{"type":["list",["object",{"compute_environment":"string","order":"number"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"priority":{"type":"number","computed":true},"state":{"type":"string","computed":true},"status":{"type":"string","computed":true},"status_reason":{"type":"string","computed":true}}}},"aws_billing_service_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_caller_identity":{"version":0,"block":{"attributes":{"account_id":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"user_id":{"type":"string","computed":true}}}},"aws_canonical_user_id":{"version":0,"block":{"attributes":{"display_name":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_cloudformation_export":{"version":0,"block":{"attributes":{"exporting_stack_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"value":{"type":"string","computed":true}}}},"aws_cloudformation_stack":{"version":0,"block":{"attributes":{"capabilities":{"type":["set","string"],"computed":true},"description":{"type":"string","computed":true},"disable_rollback":{"type":"bool","computed":true},"iam_role_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"notification_arns":{"type":["set","string"],"computed":true},"outputs":{"type":["map","string"],"computed":true},"parameters":{"type":["map","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"template_body":{"type":"string","computed":true},"timeout_in_minutes":{"type":"number","computed":true}}}},"aws_cloudfront_distribution":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","computed":true},"enabled":{"type":"bool","computed":true},"etag":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","required":true},"in_progress_validation_batches":{"type":"number","computed":true},"last_modified_time":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_cloudhsm_v2_cluster":{"version":0,"block":{"attributes":{"cluster_certificates":{"type":["list",["object",{"aws_hardware_certificate":"string","cluster_certificate":"string","cluster_csr":"string","hsm_certificate":"string","manufacturer_hardware_certificate":"string"}]],"computed":true},"cluster_id":{"type":"string","required":true},"cluster_state":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"security_group_id":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_cloudtrail_service_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_cloudwatch_log_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_time":{"type":"number","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"name":{"type":"string","required":true},"retention_in_days":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_codecommit_repository":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"clone_url_http":{"type":"string","computed":true},"clone_url_ssh":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"repository_id":{"type":"string","computed":true},"repository_name":{"type":"string","required":true}}}},"aws_cognito_user_pools":{"version":0,"block":{"attributes":{"arns":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"name":{"type":"string","required":true}}}},"aws_cur_report_definition":{"version":0,"block":{"attributes":{"additional_artifacts":{"type":["set","string"],"computed":true},"additional_schema_elements":{"type":["set","string"],"computed":true},"compression":{"type":"string","computed":true},"format":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"report_name":{"type":"string","required":true},"s3_bucket":{"type":"string","computed":true},"s3_prefix":{"type":"string","computed":true},"s3_region":{"type":"string","computed":true},"time_unit":{"type":"string","computed":true}}}},"aws_customer_gateway":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bgp_asn":{"type":"number","computed":true},"id":{"type":"string","optional":true},"ip_address":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"type":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_db_cluster_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zones":{"type":["list","string"],"computed":true},"db_cluster_identifier":{"type":"string","optional":true},"db_cluster_snapshot_arn":{"type":"string","computed":true},"db_cluster_snapshot_identifier":{"type":"string","optional":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"include_public":{"type":"bool","optional":true},"include_shared":{"type":"bool","optional":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"port":{"type":"number","computed":true},"snapshot_create_time":{"type":"string","computed":true},"snapshot_type":{"type":"string","optional":true},"source_db_cluster_snapshot_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_db_event_categories":{"version":0,"block":{"attributes":{"event_categories":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"source_type":{"type":"string","optional":true}}}},"aws_db_instance":{"version":1,"block":{"attributes":{"address":{"type":"string","computed":true},"allocated_storage":{"type":"number","computed":true},"auto_minor_version_upgrade":{"type":"bool","computed":true},"availability_zone":{"type":"string","computed":true},"backup_retention_period":{"type":"number","computed":true},"ca_cert_identifier":{"type":"string","computed":true},"db_cluster_identifier":{"type":"string","computed":true},"db_instance_arn":{"type":"string","computed":true},"db_instance_class":{"type":"string","computed":true},"db_instance_identifier":{"type":"string","required":true},"db_instance_port":{"type":"number","computed":true},"db_name":{"type":"string","computed":true},"db_parameter_groups":{"type":["list","string"],"computed":true},"db_security_groups":{"type":["list","string"],"computed":true},"db_subnet_group":{"type":"string","computed":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"iops":{"type":"number","computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"master_username":{"type":"string","computed":true},"monitoring_interval":{"type":"number","computed":true},"monitoring_role_arn":{"type":"string","computed":true},"multi_az":{"type":"bool","computed":true},"option_group_memberships":{"type":["list","string"],"computed":true},"port":{"type":"number","computed":true},"preferred_backup_window":{"type":"string","computed":true},"preferred_maintenance_window":{"type":"string","computed":true},"publicly_accessible":{"type":"bool","computed":true},"replicate_source_db":{"type":"string","computed":true},"resource_id":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"storage_type":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"timezone":{"type":"string","computed":true},"vpc_security_groups":{"type":["list","string"],"computed":true}}}},"aws_db_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zone":{"type":"string","computed":true},"db_instance_identifier":{"type":"string","optional":true},"db_snapshot_arn":{"type":"string","computed":true},"db_snapshot_identifier":{"type":"string","optional":true},"encrypted":{"type":"bool","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"include_public":{"type":"bool","optional":true},"include_shared":{"type":"bool","optional":true},"iops":{"type":"number","computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"option_group_name":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_create_time":{"type":"string","computed":true},"snapshot_type":{"type":"string","optional":true},"source_db_snapshot_identifier":{"type":"string","computed":true},"source_region":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_type":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_directory_service_directory":{"version":0,"block":{"attributes":{"access_url":{"type":"string","computed":true},"alias":{"type":"string","computed":true},"connect_settings":{"type":["list",["object",{"availability_zones":["set","string"],"connect_ips":["set","string"],"customer_dns_ips":["set","string"],"customer_username":"string","subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true},"description":{"type":"string","computed":true},"directory_id":{"type":"string","required":true},"dns_ip_addresses":{"type":["set","string"],"computed":true},"edition":{"type":"string","computed":true},"enable_sso":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"security_group_id":{"type":"string","computed":true},"short_name":{"type":"string","computed":true},"size":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","computed":true},"vpc_settings":{"type":["list",["object",{"availability_zones":["set","string"],"subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true}}}},"aws_dx_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","computed":true}}}},"aws_dynamodb_table":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"attribute":{"type":["set",["object",{"name":"string","type":"string"}]],"computed":true},"billing_mode":{"type":"string","computed":true},"global_secondary_index":{"type":["set",["object",{"hash_key":"string","name":"string","non_key_attributes":["list","string"],"projection_type":"string","range_key":"string","read_capacity":"number","write_capacity":"number"}]],"computed":true},"hash_key":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"local_secondary_index":{"type":["set",["object",{"name":"string","non_key_attributes":["list","string"],"projection_type":"string","range_key":"string"}]],"computed":true},"name":{"type":"string","required":true},"point_in_time_recovery":{"type":["list",["object",{"enabled":"bool"}]],"computed":true},"range_key":{"type":"string","computed":true},"read_capacity":{"type":"number","computed":true},"replica":{"type":["set",["object",{"region_name":"string"}]],"computed":true},"stream_arn":{"type":"string","computed":true},"stream_enabled":{"type":"bool","computed":true},"stream_label":{"type":"string","computed":true},"stream_view_type":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"ttl":{"type":["set",["object",{"attribute_name":"string","enabled":"bool"}]],"computed":true},"write_capacity":{"type":"number","computed":true}},"block_types":{"server_side_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","computed":true},"kms_key_arn":{"type":"string","computed":true}}},"max_items":1}}}},"aws_ebs_default_kms_key":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"key_arn":{"type":"string","computed":true}}}},"aws_ebs_encryption_by_default":{"version":0,"block":{"attributes":{"enabled":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ebs_snapshot":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"data_encryption_key_id":{"type":"string","computed":true},"description":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"owner_alias":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"owners":{"type":["list","string"],"optional":true},"restorable_by_user_ids":{"type":["list","string"],"optional":true},"snapshot_id":{"type":"string","computed":true},"snapshot_ids":{"type":["list","string"],"optional":true},"state":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ebs_snapshot_ids":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["list","string"],"computed":true},"owners":{"type":["list","string"],"optional":true},"restorable_by_user_ids":{"type":["list","string"],"optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ebs_volume":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"iops":{"type":"number","computed":true},"kms_key_id":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"multi_attach_enabled":{"type":"bool","computed":true},"outpost_arn":{"type":"string","computed":true},"size":{"type":"number","computed":true},"snapshot_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_type":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ebs_volumes":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_coip_pool":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_gateway_route_table_id":{"type":"string","optional":true,"computed":true},"pool_cidrs":{"type":["set","string"],"computed":true},"pool_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_coip_pools":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"pool_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_instance_type_offering":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","computed":true},"location_type":{"type":"string","optional":true},"preferred_instance_types":{"type":["list","string"],"optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_instance_type_offerings":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_types":{"type":["set","string"],"computed":true},"location_type":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_local_gateway":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"outpost_arn":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_route_table":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_gateway_id":{"type":"string","optional":true,"computed":true},"local_gateway_route_table_id":{"type":"string","optional":true,"computed":true},"outpost_arn":{"type":"string","optional":true,"computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_route_tables":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_virtual_interface":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_address":{"type":"string","computed":true},"local_bgp_asn":{"type":"number","computed":true},"local_gateway_id":{"type":"string","computed":true},"local_gateway_virtual_interface_ids":{"type":["set","string"],"computed":true},"peer_address":{"type":"string","computed":true},"peer_bgp_asn":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vlan":{"type":"number","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_virtual_interface_group":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_gateway_id":{"type":"string","optional":true,"computed":true},"local_gateway_virtual_interface_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_virtual_interface_groups":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"local_gateway_virtual_interface_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateways":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_spot_price":{"version":0,"block":{"attributes":{"availability_zone":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","optional":true},"spot_price":{"type":"string","computed":true},"spot_price_timestamp":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"number","computed":true},"arn":{"type":"string","computed":true},"association_default_route_table_id":{"type":"string","computed":true},"auto_accept_shared_attachments":{"type":"string","computed":true},"default_route_table_association":{"type":"string","computed":true},"default_route_table_propagation":{"type":"string","computed":true},"description":{"type":"string","computed":true},"dns_support":{"type":"string","computed":true},"id":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"propagation_default_route_table_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpn_ecmp_support":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_dx_gateway_attachment":{"version":0,"block":{"attributes":{"dx_gateway_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_peering_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true},"peer_account_id":{"type":"string","computed":true},"peer_region":{"type":"string","computed":true},"peer_transit_gateway_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_route_table":{"version":0,"block":{"attributes":{"default_association_route_table":{"type":"bool","computed":true},"default_propagation_route_table":{"type":"bool","computed":true},"id":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_vpc_attachment":{"version":0,"block":{"attributes":{"dns_support":{"type":"string","computed":true},"id":{"type":"string","optional":true},"ipv6_support":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true},"vpc_owner_id":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_vpn_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","optional":true},"vpn_connection_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ecr_authorization_token":{"version":0,"block":{"attributes":{"authorization_token":{"type":"string","computed":true,"sensitive":true},"expires_at":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"password":{"type":"string","computed":true,"sensitive":true},"proxy_endpoint":{"type":"string","computed":true},"registry_id":{"type":"string","optional":true},"user_name":{"type":"string","computed":true}}}},"aws_ecr_image":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"image_digest":{"type":"string","optional":true,"computed":true},"image_pushed_at":{"type":"number","computed":true},"image_size_in_bytes":{"type":"number","computed":true},"image_tag":{"type":"string","optional":true},"image_tags":{"type":["list","string"],"computed":true},"registry_id":{"type":"string","optional":true,"computed":true},"repository_name":{"type":"string","required":true}}}},"aws_ecr_repository":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"encryption_configuration":{"type":["list",["object",{"encryption_type":"string","kms_key":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"image_scanning_configuration":{"type":["list",["object",{"scan_on_push":"bool"}]],"computed":true},"image_tag_mutability":{"type":"string","computed":true},"name":{"type":"string","required":true},"registry_id":{"type":"string","optional":true,"computed":true},"repository_url":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_ecs_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"pending_tasks_count":{"type":"number","computed":true},"registered_container_instances_count":{"type":"number","computed":true},"running_tasks_count":{"type":"number","computed":true},"setting":{"type":["set",["object",{"name":"string","value":"string"}]],"computed":true},"status":{"type":"string","computed":true}}}},"aws_ecs_container_definition":{"version":0,"block":{"attributes":{"container_name":{"type":"string","required":true},"cpu":{"type":"number","computed":true},"disable_networking":{"type":"bool","computed":true},"docker_labels":{"type":["map","string"],"computed":true},"environment":{"type":["map","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"image":{"type":"string","computed":true},"image_digest":{"type":"string","computed":true},"memory":{"type":"number","computed":true},"memory_reservation":{"type":"number","computed":true},"task_definition":{"type":"string","required":true}}}},"aws_ecs_service":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cluster_arn":{"type":"string","required":true},"desired_count":{"type":"number","computed":true},"id":{"type":"string","optional":true,"computed":true},"launch_type":{"type":"string","computed":true},"scheduling_strategy":{"type":"string","computed":true},"service_name":{"type":"string","required":true},"task_definition":{"type":"string","computed":true}}}},"aws_ecs_task_definition":{"version":1,"block":{"attributes":{"family":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"network_mode":{"type":"string","computed":true},"revision":{"type":"number","computed":true},"status":{"type":"string","computed":true},"task_definition":{"type":"string","required":true},"task_role_arn":{"type":"string","computed":true}}}},"aws_efs_access_point":{"version":0,"block":{"attributes":{"access_point_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"file_system_arn":{"type":"string","computed":true},"file_system_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"posix_user":{"type":["list",["object",{"gid":"number","secondary_gids":["set","number"],"uid":"number"}]],"computed":true},"root_directory":{"type":["list",["object",{"creation_info":["list",["object",{"owner_gid":"number","owner_uid":"number","permissions":"string"}]],"path":"string"}]],"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_efs_access_points":{"version":0,"block":{"attributes":{"arns":{"type":["set","string"],"computed":true},"file_system_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true}}}},"aws_efs_file_system":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_token":{"type":"string","optional":true,"computed":true},"dns_name":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"file_system_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"lifecycle_policy":{"type":["list",["object",{"transition_to_ia":"string"}]],"computed":true},"performance_mode":{"type":"string","computed":true},"provisioned_throughput_in_mibps":{"type":"number","computed":true},"size_in_bytes":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"throughput_mode":{"type":"string","computed":true}}}},"aws_efs_mount_target":{"version":0,"block":{"attributes":{"availability_zone_id":{"type":"string","computed":true},"availability_zone_name":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"file_system_arn":{"type":"string","computed":true},"file_system_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","computed":true},"mount_target_dns_name":{"type":"string","computed":true},"mount_target_id":{"type":"string","required":true},"network_interface_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_id":{"type":"string","computed":true}}}},"aws_eip":{"version":0,"block":{"attributes":{"association_id":{"type":"string","computed":true},"customer_owned_ip":{"type":"string","computed":true},"customer_owned_ipv4_pool":{"type":"string","computed":true},"domain":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","computed":true},"network_interface_id":{"type":"string","computed":true},"network_interface_owner_id":{"type":"string","computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","optional":true,"computed":true},"public_ipv4_pool":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_eks_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_authority":{"type":["list",["object",{"data":"string"}]],"computed":true},"created_at":{"type":"string","computed":true},"enabled_cluster_log_types":{"type":["set","string"],"computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identity":{"type":["list",["object",{"oidc":["list",["object",{"issuer":"string"}]]}]],"computed":true},"name":{"type":"string","required":true},"platform_version":{"type":"string","computed":true},"role_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"version":{"type":"string","computed":true},"vpc_config":{"type":["list",["object",{"cluster_security_group_id":"string","endpoint_private_access":"bool","endpoint_public_access":"bool","public_access_cidrs":["set","string"],"security_group_ids":["set","string"],"subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true}}}},"aws_eks_cluster_auth":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"token":{"type":"string","computed":true,"sensitive":true}}}},"aws_elastic_beanstalk_application":{"version":0,"block":{"attributes":{"appversion_lifecycle":{"type":["list",["object",{"delete_source_from_s3":"bool","max_age_in_days":"number","max_count":"number","service_role":"string"}]],"computed":true},"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_elastic_beanstalk_hosted_zone":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_elastic_beanstalk_solution_stack":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"most_recent":{"type":"bool","optional":true},"name":{"type":"string","computed":true},"name_regex":{"type":"string","required":true}}}},"aws_elasticache_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","computed":true},"cache_nodes":{"type":["list",["object",{"address":"string","availability_zone":"string","id":"string","port":"number"}]],"computed":true},"cluster_address":{"type":"string","computed":true},"cluster_id":{"type":"string","required":true},"configuration_endpoint":{"type":"string","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"maintenance_window":{"type":"string","computed":true},"node_type":{"type":"string","computed":true},"notification_topic_arn":{"type":"string","computed":true},"num_cache_nodes":{"type":"number","computed":true},"parameter_group_name":{"type":"string","computed":true},"port":{"type":"number","computed":true},"replication_group_id":{"type":"string","computed":true},"security_group_ids":{"type":["set","string"],"computed":true},"security_group_names":{"type":["set","string"],"computed":true},"snapshot_retention_limit":{"type":"number","computed":true},"snapshot_window":{"type":"string","computed":true},"subnet_group_name":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_elasticache_replication_group":{"version":1,"block":{"attributes":{"auth_token_enabled":{"type":"bool","computed":true},"automatic_failover_enabled":{"type":"bool","computed":true},"configuration_endpoint_address":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"member_clusters":{"type":["set","string"],"computed":true},"node_type":{"type":"string","computed":true},"number_cache_clusters":{"type":"number","computed":true},"port":{"type":"number","computed":true},"primary_endpoint_address":{"type":"string","computed":true},"replication_group_description":{"type":"string","computed":true},"replication_group_id":{"type":"string","required":true},"snapshot_retention_limit":{"type":"number","computed":true},"snapshot_window":{"type":"string","computed":true}}}},"aws_elasticsearch_domain":{"version":0,"block":{"attributes":{"access_policies":{"type":"string","computed":true},"advanced_options":{"type":["map","string"],"computed":true},"advanced_security_options":{"type":["list",["object",{"enabled":"bool","internal_user_database_enabled":"bool"}]],"computed":true},"arn":{"type":"string","computed":true},"cluster_config":{"type":["list",["object",{"dedicated_master_count":"number","dedicated_master_enabled":"bool","dedicated_master_type":"string","instance_count":"number","instance_type":"string","warm_count":"number","warm_enabled":"bool","warm_type":"string","zone_awareness_config":["list",["object",{"availability_zone_count":"number"}]],"zone_awareness_enabled":"bool"}]],"computed":true},"cognito_options":{"type":["list",["object",{"enabled":"bool","identity_pool_id":"string","role_arn":"string","user_pool_id":"string"}]],"computed":true},"created":{"type":"bool","computed":true},"deleted":{"type":"bool","computed":true},"domain_id":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"ebs_options":{"type":["list",["object",{"ebs_enabled":"bool","iops":"number","volume_size":"number","volume_type":"string"}]],"computed":true},"elasticsearch_version":{"type":"string","computed":true},"encryption_at_rest":{"type":["list",["object",{"enabled":"bool","kms_key_id":"string"}]],"computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kibana_endpoint":{"type":"string","computed":true},"log_publishing_options":{"type":["set",["object",{"cloudwatch_log_group_arn":"string","enabled":"bool","log_type":"string"}]],"computed":true},"node_to_node_encryption":{"type":["list",["object",{"enabled":"bool"}]],"computed":true},"processing":{"type":"bool","computed":true},"snapshot_options":{"type":["list",["object",{"automated_snapshot_start_hour":"number"}]],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_options":{"type":["list",["object",{"availability_zones":["set","string"],"security_group_ids":["set","string"],"subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true}}}},"aws_elb":{"version":0,"block":{"attributes":{"access_logs":{"type":["list",["object",{"bucket":"string","bucket_prefix":"string","enabled":"bool","interval":"number"}]],"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"connection_draining":{"type":"bool","computed":true},"connection_draining_timeout":{"type":"number","computed":true},"cross_zone_load_balancing":{"type":"bool","computed":true},"dns_name":{"type":"string","computed":true},"health_check":{"type":["list",["object",{"healthy_threshold":"number","interval":"number","target":"string","timeout":"number","unhealthy_threshold":"number"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","computed":true},"instances":{"type":["set","string"],"computed":true},"internal":{"type":"bool","computed":true},"listener":{"type":["set",["object",{"instance_port":"number","instance_protocol":"string","lb_port":"number","lb_protocol":"string","ssl_certificate_id":"string"}]],"computed":true},"name":{"type":"string","required":true},"security_groups":{"type":["set","string"],"computed":true},"source_security_group":{"type":"string","computed":true},"source_security_group_id":{"type":"string","computed":true},"subnets":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"zone_id":{"type":"string","computed":true}}}},"aws_elb_hosted_zone_id":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_elb_service_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_glue_script":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"language":{"type":"string","optional":true},"python_script":{"type":"string","computed":true},"scala_code":{"type":"string","computed":true}},"block_types":{"dag_edge":{"nesting_mode":"list","block":{"attributes":{"source":{"type":"string","required":true},"target":{"type":"string","required":true},"target_parameter":{"type":"string","optional":true}}},"min_items":1},"dag_node":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","required":true},"line_number":{"type":"number","optional":true},"node_type":{"type":"string","required":true}},"block_types":{"args":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true},"param":{"type":"bool","optional":true},"value":{"type":"string","required":true}}},"min_items":1}}},"min_items":1}}}},"aws_guardduty_detector":{"version":0,"block":{"attributes":{"finding_publishing_frequency":{"type":"string","computed":true},"id":{"type":"string","optional":true},"service_role_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true}}}},"aws_iam_account_alias":{"version":0,"block":{"attributes":{"account_alias":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_iam_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"group_id":{"type":"string","computed":true},"group_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"path":{"type":"string","computed":true},"users":{"type":["list",["object",{"arn":"string","path":"string","user_id":"string","user_name":"string"}]],"computed":true}}}},"aws_iam_instance_profile":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"create_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"path":{"type":"string","computed":true},"role_arn":{"type":"string","computed":true},"role_id":{"type":"string","computed":true},"role_name":{"type":"string","computed":true}}}},"aws_iam_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"path":{"type":"string","computed":true},"policy":{"type":"string","computed":true}}}},"aws_iam_policy_document":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"json":{"type":"string","computed":true},"override_json":{"type":"string","optional":true},"policy_id":{"type":"string","optional":true},"source_json":{"type":"string","optional":true},"version":{"type":"string","optional":true}},"block_types":{"statement":{"nesting_mode":"list","block":{"attributes":{"actions":{"type":["set","string"],"optional":true},"effect":{"type":"string","optional":true},"not_actions":{"type":["set","string"],"optional":true},"not_resources":{"type":["set","string"],"optional":true},"resources":{"type":["set","string"],"optional":true},"sid":{"type":"string","optional":true}},"block_types":{"condition":{"nesting_mode":"set","block":{"attributes":{"test":{"type":"string","required":true},"values":{"type":["set","string"],"required":true},"variable":{"type":"string","required":true}}}},"not_principals":{"nesting_mode":"set","block":{"attributes":{"identifiers":{"type":["set","string"],"required":true},"type":{"type":"string","required":true}}}},"principals":{"nesting_mode":"set","block":{"attributes":{"identifiers":{"type":["set","string"],"required":true},"type":{"type":"string","required":true}}}}}}}}}},"aws_iam_role":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"assume_role_policy":{"type":"string","computed":true},"create_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"max_session_duration":{"type":"number","computed":true},"name":{"type":"string","required":true},"path":{"type":"string","computed":true},"permissions_boundary":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_server_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_body":{"type":"string","computed":true},"certificate_chain":{"type":"string","computed":true},"expiration_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"latest":{"type":"bool","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","computed":true},"path_prefix":{"type":"string","optional":true},"upload_date":{"type":"string","computed":true}}}},"aws_iam_user":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"path":{"type":"string","computed":true},"permissions_boundary":{"type":"string","computed":true},"user_id":{"type":"string","computed":true},"user_name":{"type":"string","required":true}}}},"aws_inspector_rules_packages":{"version":0,"block":{"attributes":{"arns":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_instance":{"version":1,"block":{"attributes":{"ami":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","computed":true},"availability_zone":{"type":"string","computed":true},"credit_specification":{"type":["list",["object",{"cpu_credits":"string"}]],"computed":true},"disable_api_termination":{"type":"bool","computed":true},"ebs_block_device":{"type":["set",["object",{"delete_on_termination":"bool","device_name":"string","encrypted":"bool","iops":"number","kms_key_id":"string","snapshot_id":"string","volume_id":"string","volume_size":"number","volume_type":"string"}]],"computed":true},"ebs_optimized":{"type":"bool","computed":true},"ephemeral_block_device":{"type":["list",["object",{"device_name":"string","no_device":"bool","virtual_name":"string"}]],"computed":true},"get_password_data":{"type":"bool","optional":true},"get_user_data":{"type":"bool","optional":true},"host_id":{"type":"string","computed":true},"iam_instance_profile":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true},"instance_state":{"type":"string","computed":true},"instance_tags":{"type":["map","string"],"optional":true,"computed":true},"instance_type":{"type":"string","computed":true},"key_name":{"type":"string","computed":true},"metadata_options":{"type":["list",["object",{"http_endpoint":"string","http_put_response_hop_limit":"number","http_tokens":"string"}]],"computed":true},"monitoring":{"type":"bool","computed":true},"network_interface_id":{"type":"string","computed":true},"outpost_arn":{"type":"string","computed":true},"password_data":{"type":"string","computed":true},"placement_group":{"type":"string","computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"root_block_device":{"type":["set",["object",{"delete_on_termination":"bool","device_name":"string","encrypted":"bool","iops":"number","kms_key_id":"string","volume_id":"string","volume_size":"number","volume_type":"string"}]],"computed":true},"secondary_private_ips":{"type":["set","string"],"computed":true},"security_groups":{"type":["set","string"],"computed":true},"source_dest_check":{"type":"bool","computed":true},"subnet_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"tenancy":{"type":"string","computed":true},"user_data":{"type":"string","computed":true},"user_data_base64":{"type":"string","computed":true},"vpc_security_group_ids":{"type":["set","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_instances":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["list","string"],"computed":true},"instance_state_names":{"type":["set","string"],"optional":true},"instance_tags":{"type":["map","string"],"optional":true,"computed":true},"private_ips":{"type":["list","string"],"computed":true},"public_ips":{"type":["list","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_internet_gateway":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"attachments":{"type":["list",["object",{"state":"string","vpc_id":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"internet_gateway_id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_iot_endpoint":{"version":0,"block":{"attributes":{"endpoint_address":{"type":"string","computed":true},"endpoint_type":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ip_ranges":{"version":0,"block":{"attributes":{"cidr_blocks":{"type":["list","string"],"computed":true},"create_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_blocks":{"type":["list","string"],"computed":true},"regions":{"type":["set","string"],"optional":true},"services":{"type":["set","string"],"required":true},"sync_token":{"type":"number","computed":true},"url":{"type":"string","optional":true}}}},"aws_kinesis_stream":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"closed_shards":{"type":["set","string"],"computed":true},"creation_timestamp":{"type":"number","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"open_shards":{"type":["set","string"],"computed":true},"retention_period":{"type":"number","computed":true},"shard_level_metrics":{"type":["set","string"],"computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_kms_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"target_key_arn":{"type":"string","computed":true},"target_key_id":{"type":"string","computed":true}}}},"aws_kms_ciphertext":{"version":0,"block":{"attributes":{"ciphertext_blob":{"type":"string","computed":true},"context":{"type":["map","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"plaintext":{"type":"string","required":true,"sensitive":true}}}},"aws_kms_key":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_account_id":{"type":"string","computed":true},"creation_date":{"type":"string","computed":true},"customer_master_key_spec":{"type":"string","computed":true},"deletion_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"enabled":{"type":"bool","computed":true},"expiration_model":{"type":"string","computed":true},"grant_tokens":{"type":["list","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"key_manager":{"type":"string","computed":true},"key_state":{"type":"string","computed":true},"key_usage":{"type":"string","computed":true},"origin":{"type":"string","computed":true},"valid_to":{"type":"string","computed":true}}}},"aws_kms_secret":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true}},"block_types":{"secret":{"nesting_mode":"set","block":{"attributes":{"context":{"type":["map","string"],"optional":true},"grant_tokens":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"payload":{"type":"string","required":true}}},"min_items":1}}}},"aws_kms_secrets":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"plaintext":{"type":["map","string"],"computed":true}},"block_types":{"secret":{"nesting_mode":"set","block":{"attributes":{"context":{"type":["map","string"],"optional":true},"grant_tokens":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"payload":{"type":"string","required":true}}},"min_items":1}}}},"aws_lambda_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"function_name":{"type":"string","required":true},"function_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_arn":{"type":"string","computed":true},"name":{"type":"string","required":true}}}},"aws_lambda_function":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"dead_letter_config":{"type":["list",["object",{"target_arn":"string"}]],"computed":true},"description":{"type":"string","computed":true},"environment":{"type":["list",["object",{"variables":["map","string"]}]],"computed":true},"file_system_config":{"type":["list",["object",{"arn":"string","local_mount_path":"string"}]],"computed":true},"function_name":{"type":"string","required":true},"handler":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_arn":{"type":"string","computed":true},"kms_key_arn":{"type":"string","computed":true},"last_modified":{"type":"string","computed":true},"layers":{"type":["list","string"],"computed":true},"memory_size":{"type":"number","computed":true},"qualified_arn":{"type":"string","computed":true},"qualifier":{"type":"string","optional":true},"reserved_concurrent_executions":{"type":"number","computed":true},"role":{"type":"string","computed":true},"runtime":{"type":"string","computed":true},"source_code_hash":{"type":"string","computed":true},"source_code_size":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"timeout":{"type":"number","computed":true},"tracing_config":{"type":["list",["object",{"mode":"string"}]],"computed":true},"version":{"type":"string","computed":true},"vpc_config":{"type":["list",["object",{"security_group_ids":["set","string"],"subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true}}}},"aws_lambda_invocation":{"version":0,"block":{"attributes":{"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"input":{"type":"string","required":true},"qualifier":{"type":"string","optional":true},"result":{"type":"string","computed":true}}}},"aws_lambda_layer_version":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compatible_runtime":{"type":"string","optional":true},"compatible_runtimes":{"type":["set","string"],"computed":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"layer_arn":{"type":"string","computed":true},"layer_name":{"type":"string","required":true},"license_info":{"type":"string","computed":true},"source_code_hash":{"type":"string","computed":true},"source_code_size":{"type":"number","computed":true},"version":{"type":"number","optional":true,"computed":true}}}},"aws_launch_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","computed":true},"ebs_block_device":{"type":["set",["object",{"delete_on_termination":"bool","device_name":"string","encrypted":"bool","iops":"number","no_device":"bool","snapshot_id":"string","volume_size":"number","volume_type":"string"}]],"computed":true},"ebs_optimized":{"type":"bool","computed":true},"enable_monitoring":{"type":"bool","computed":true},"ephemeral_block_device":{"type":["set",["object",{"device_name":"string","virtual_name":"string"}]],"computed":true},"iam_instance_profile":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","computed":true},"instance_type":{"type":"string","computed":true},"key_name":{"type":"string","computed":true},"name":{"type":"string","required":true},"placement_tenancy":{"type":"string","computed":true},"root_block_device":{"type":["list",["object",{"delete_on_termination":"bool","encrypted":"bool","iops":"number","volume_size":"number","volume_type":"string"}]],"computed":true},"security_groups":{"type":["set","string"],"computed":true},"spot_price":{"type":"string","computed":true},"user_data":{"type":"string","computed":true},"vpc_classic_link_id":{"type":"string","computed":true},"vpc_classic_link_security_groups":{"type":["set","string"],"computed":true}}}},"aws_launch_template":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"block_device_mappings":{"type":["list",["object",{"device_name":"string","ebs":["list",["object",{"delete_on_termination":"string","encrypted":"string","iops":"number","kms_key_id":"string","snapshot_id":"string","volume_size":"number","volume_type":"string"}]],"no_device":"string","virtual_name":"string"}]],"computed":true},"credit_specification":{"type":["list",["object",{"cpu_credits":"string"}]],"computed":true},"default_version":{"type":"number","computed":true},"description":{"type":"string","computed":true},"disable_api_termination":{"type":"bool","computed":true},"ebs_optimized":{"type":"string","computed":true},"elastic_gpu_specifications":{"type":["list",["object",{"type":"string"}]],"computed":true},"hibernation_options":{"type":["list",["object",{"configured":"bool"}]],"computed":true},"iam_instance_profile":{"type":["list",["object",{"arn":"string","name":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","computed":true},"instance_initiated_shutdown_behavior":{"type":"string","computed":true},"instance_market_options":{"type":["list",["object",{"market_type":"string","spot_options":["list",["object",{"block_duration_minutes":"number","instance_interruption_behavior":"string","max_price":"string","spot_instance_type":"string","valid_until":"string"}]]}]],"computed":true},"instance_type":{"type":"string","computed":true},"kernel_id":{"type":"string","computed":true},"key_name":{"type":"string","computed":true},"latest_version":{"type":"number","computed":true},"metadata_options":{"type":["list",["object",{"http_endpoint":"string","http_put_response_hop_limit":"number","http_tokens":"string"}]],"computed":true},"monitoring":{"type":["list",["object",{"enabled":"bool"}]],"computed":true},"name":{"type":"string","optional":true},"network_interfaces":{"type":["list",["object",{"associate_public_ip_address":"string","delete_on_termination":"string","description":"string","device_index":"number","ipv4_address_count":"number","ipv4_addresses":["set","string"],"ipv6_address_count":"number","ipv6_addresses":["set","string"],"network_interface_id":"string","private_ip_address":"string","security_groups":["set","string"],"subnet_id":"string"}]],"computed":true},"placement":{"type":["list",["object",{"affinity":"string","availability_zone":"string","group_name":"string","host_id":"string","partition_number":"number","spread_domain":"string","tenancy":"string"}]],"computed":true},"ram_disk_id":{"type":"string","computed":true},"security_group_names":{"type":["set","string"],"computed":true},"tag_specifications":{"type":["list",["object",{"resource_type":"string","tags":["map","string"]}]],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"user_data":{"type":"string","computed":true},"vpc_security_group_ids":{"type":["set","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_lb":{"version":0,"block":{"attributes":{"access_logs":{"type":["list",["object",{"bucket":"string","enabled":"bool","prefix":"string"}]],"computed":true},"arn":{"type":"string","optional":true,"computed":true},"arn_suffix":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"drop_invalid_header_fields":{"type":"bool","computed":true},"enable_deletion_protection":{"type":"bool","computed":true},"enable_http2":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","computed":true},"internal":{"type":"bool","computed":true},"ip_address_type":{"type":"string","computed":true},"load_balancer_type":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_mapping":{"type":["set",["object",{"allocation_id":"string","private_ipv4_address":"string","subnet_id":"string"}]],"computed":true},"subnets":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","computed":true},"zone_id":{"type":"string","computed":true}}}},"aws_lb_listener":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"certificate_arn":{"type":"string","computed":true},"default_action":{"type":["list",["object",{"authenticate_cognito":["list",["object",{"authentication_request_extra_params":["map","string"],"on_unauthenticated_request":"string","scope":"string","session_cookie_name":"string","session_timeout":"number","user_pool_arn":"string","user_pool_client_id":"string","user_pool_domain":"string"}]],"authenticate_oidc":["list",["object",{"authentication_request_extra_params":["map","string"],"authorization_endpoint":"string","client_id":"string","client_secret":"string","issuer":"string","on_unauthenticated_request":"string","scope":"string","session_cookie_name":"string","session_timeout":"number","token_endpoint":"string","user_info_endpoint":"string"}]],"fixed_response":["list",["object",{"content_type":"string","message_body":"string","status_code":"string"}]],"order":"number","redirect":["list",["object",{"host":"string","path":"string","port":"string","protocol":"string","query":"string","status_code":"string"}]],"target_group_arn":"string","type":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"load_balancer_arn":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"protocol":{"type":"string","computed":true},"ssl_policy":{"type":"string","computed":true}}}},"aws_lb_target_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"arn_suffix":{"type":"string","computed":true},"deregistration_delay":{"type":"number","computed":true},"health_check":{"type":["list",["object",{"enabled":"bool","healthy_threshold":"number","interval":"number","matcher":"string","path":"string","port":"string","protocol":"string","timeout":"number","unhealthy_threshold":"number"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"lambda_multi_value_headers_enabled":{"type":"bool","computed":true},"load_balancing_algorithm_type":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","computed":true},"protocol":{"type":"string","computed":true},"proxy_protocol_v2":{"type":"bool","computed":true},"slow_start":{"type":"number","computed":true},"stickiness":{"type":["list",["object",{"cookie_duration":"number","enabled":"bool","type":"string"}]],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"target_type":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_mq_broker":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","computed":true},"broker_id":{"type":"string","optional":true,"computed":true},"broker_name":{"type":"string","optional":true,"computed":true},"configuration":{"type":["list",["object",{"id":"string","revision":"number"}]],"computed":true},"deployment_mode":{"type":"string","computed":true},"encryption_options":{"type":["list",["object",{"kms_key_id":"string","use_aws_owned_key":"bool"}]],"computed":true},"engine_type":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"host_instance_type":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instances":{"type":["list",["object",{"console_url":"string","endpoints":["list","string"],"ip_address":"string"}]],"computed":true},"maintenance_window_start_time":{"type":["list",["object",{"day_of_week":"string","time_of_day":"string","time_zone":"string"}]],"computed":true},"publicly_accessible":{"type":"bool","computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"user":{"type":["set",["object",{"console_access":"bool","groups":["set","string"],"username":"string"}]],"computed":true}},"block_types":{"logs":{"nesting_mode":"list","block":{"attributes":{"audit":{"type":"bool","computed":true},"general":{"type":"bool","computed":true}}},"max_items":1}}}},"aws_msk_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bootstrap_brokers":{"type":"string","computed":true},"bootstrap_brokers_tls":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"kafka_version":{"type":"string","computed":true},"number_of_broker_nodes":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"zookeeper_connect_string":{"type":"string","computed":true}}}},"aws_msk_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kafka_versions":{"type":["set","string"],"computed":true},"latest_revision":{"type":"number","computed":true},"name":{"type":"string","required":true},"server_properties":{"type":"string","computed":true}}}},"aws_nat_gateway":{"version":0,"block":{"attributes":{"allocation_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_network_acls":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_network_interface":{"version":0,"block":{"attributes":{"association":{"type":["list",["object",{"allocation_id":"string","association_id":"string","ip_owner_id":"string","public_dns_name":"string","public_ip":"string"}]],"computed":true},"attachment":{"type":["list",["object",{"attachment_id":"string","device_index":"number","instance_id":"string","instance_owner_id":"string"}]],"computed":true},"availability_zone":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"interface_type":{"type":"string","computed":true},"ipv6_addresses":{"type":["set","string"],"computed":true},"mac_address":{"type":"string","computed":true},"outpost_arn":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"private_dns_name":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"private_ips":{"type":["list","string"],"computed":true},"requester_id":{"type":"string","computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_network_interfaces":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_organizations_organization":{"version":0,"block":{"attributes":{"accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string","status":"string"}]],"computed":true},"arn":{"type":"string","computed":true},"aws_service_access_principals":{"type":["set","string"],"computed":true},"enabled_policy_types":{"type":["set","string"],"computed":true},"feature_set":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"master_account_arn":{"type":"string","computed":true},"master_account_email":{"type":"string","computed":true},"master_account_id":{"type":"string","computed":true},"non_master_accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string","status":"string"}]],"computed":true},"roots":{"type":["list",["object",{"arn":"string","id":"string","name":"string","policy_types":["list",["object",{"status":"string","type":"string"}]]}]],"computed":true}}}},"aws_organizations_organizational_units":{"version":0,"block":{"attributes":{"children":{"type":["list",["object",{"arn":"string","id":"string","name":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"parent_id":{"type":"string","required":true}}}},"aws_outposts_outpost":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","computed":true},"availability_zone_id":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"site_id":{"type":"string","computed":true}}}},"aws_outposts_outpost_instance_type":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","optional":true,"computed":true},"preferred_instance_types":{"type":["list","string"],"optional":true}}}},"aws_outposts_outpost_instance_types":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instance_types":{"type":["set","string"],"computed":true}}}},"aws_outposts_outposts":{"version":0,"block":{"attributes":{"arns":{"type":["set","string"],"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"availability_zone_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"site_id":{"type":"string","optional":true,"computed":true}}}},"aws_outposts_site":{"version":0,"block":{"attributes":{"account_id":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true}}}},"aws_outposts_sites":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true}}}},"aws_partition":{"version":0,"block":{"attributes":{"dns_suffix":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"partition":{"type":"string","computed":true}}}},"aws_prefix_list":{"version":0,"block":{"attributes":{"cidr_blocks":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"prefix_list_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_pricing_product":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"result":{"type":"string","computed":true},"service_code":{"type":"string","required":true}},"block_types":{"filters":{"nesting_mode":"list","block":{"attributes":{"field":{"type":"string","required":true},"value":{"type":"string","required":true}}},"min_items":1}}}},"aws_qldb_ledger":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deletion_protection":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_ram_resource_share":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owning_account_id":{"type":"string","computed":true},"resource_owner":{"type":"string","required":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_rds_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"backtrack_window":{"type":"number","computed":true},"backup_retention_period":{"type":"number","computed":true},"cluster_identifier":{"type":"string","required":true},"cluster_members":{"type":["set","string"],"computed":true},"cluster_resource_id":{"type":"string","computed":true},"database_name":{"type":"string","computed":true},"db_cluster_parameter_group_name":{"type":"string","computed":true},"db_subnet_group_name":{"type":"string","computed":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"final_snapshot_identifier":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"iam_database_authentication_enabled":{"type":"bool","computed":true},"iam_roles":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"master_username":{"type":"string","computed":true},"port":{"type":"number","computed":true},"preferred_backup_window":{"type":"string","computed":true},"preferred_maintenance_window":{"type":"string","computed":true},"reader_endpoint":{"type":"string","computed":true},"replication_source_identifier":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_security_group_ids":{"type":["set","string"],"computed":true}}}},"aws_redshift_cluster":{"version":0,"block":{"attributes":{"allow_version_upgrade":{"type":"bool","computed":true},"automated_snapshot_retention_period":{"type":"number","computed":true},"availability_zone":{"type":"string","computed":true},"bucket_name":{"type":"string","computed":true},"cluster_identifier":{"type":"string","required":true},"cluster_parameter_group_name":{"type":"string","computed":true},"cluster_public_key":{"type":"string","computed":true},"cluster_revision_number":{"type":"string","computed":true},"cluster_security_groups":{"type":["list","string"],"computed":true},"cluster_subnet_group_name":{"type":"string","computed":true},"cluster_type":{"type":"string","computed":true},"cluster_version":{"type":"string","computed":true},"database_name":{"type":"string","computed":true},"elastic_ip":{"type":"string","computed":true},"enable_logging":{"type":"bool","computed":true},"encrypted":{"type":"bool","computed":true},"endpoint":{"type":"string","computed":true},"enhanced_vpc_routing":{"type":"bool","computed":true},"iam_roles":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"master_username":{"type":"string","computed":true},"node_type":{"type":"string","computed":true},"number_of_nodes":{"type":"number","computed":true},"port":{"type":"number","computed":true},"preferred_maintenance_window":{"type":"string","computed":true},"publicly_accessible":{"type":"bool","computed":true},"s3_key_prefix":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"vpc_security_group_ids":{"type":["list","string"],"computed":true}}}},"aws_redshift_service_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_region":{"version":0,"block":{"attributes":{"description":{"type":"string","computed":true},"endpoint":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true}}}},"aws_regions":{"version":0,"block":{"attributes":{"all_regions":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"names":{"type":["set","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_route":{"version":0,"block":{"attributes":{"destination_cidr_block":{"type":"string","optional":true,"computed":true},"destination_ipv6_cidr_block":{"type":"string","optional":true,"computed":true},"egress_only_gateway_id":{"type":"string","optional":true,"computed":true},"gateway_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true,"computed":true},"nat_gateway_id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","optional":true,"computed":true},"route_table_id":{"type":"string","required":true},"transit_gateway_id":{"type":"string","optional":true,"computed":true},"vpc_peering_connection_id":{"type":"string","optional":true,"computed":true}}}},"aws_route53_delegation_set":{"version":0,"block":{"attributes":{"caller_reference":{"type":"string","computed":true},"id":{"type":"string","required":true},"name_servers":{"type":["list","string"],"computed":true}}}},"aws_route53_resolver_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"resolver_endpoint_id":{"type":"string","optional":true,"computed":true},"resolver_rule_id":{"type":"string","optional":true,"computed":true},"rule_type":{"type":"string","optional":true,"computed":true},"share_status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_route53_resolver_rules":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","optional":true},"resolver_endpoint_id":{"type":"string","optional":true},"resolver_rule_ids":{"type":["set","string"],"computed":true},"rule_type":{"type":"string","optional":true},"share_status":{"type":"string","optional":true}}}},"aws_route53_zone":{"version":0,"block":{"attributes":{"caller_reference":{"type":"string","computed":true},"comment":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"linked_service_description":{"type":"string","computed":true},"linked_service_principal":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"name_servers":{"type":["list","string"],"computed":true},"private_zone":{"type":"bool","optional":true},"resource_record_set_count":{"type":"number","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","optional":true,"computed":true}}}},"aws_route_table":{"version":0,"block":{"attributes":{"associations":{"type":["list",["object",{"gateway_id":"string","main":"bool","route_table_association_id":"string","route_table_id":"string","subnet_id":"string"}]],"computed":true},"gateway_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"route_table_id":{"type":"string","optional":true,"computed":true},"routes":{"type":["list",["object",{"cidr_block":"string","egress_only_gateway_id":"string","gateway_id":"string","instance_id":"string","ipv6_cidr_block":"string","nat_gateway_id":"string","network_interface_id":"string","transit_gateway_id":"string","vpc_peering_connection_id":"string"}]],"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_route_tables":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_s3_bucket":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bucket":{"type":"string","required":true},"bucket_domain_name":{"type":"string","computed":true},"bucket_regional_domain_name":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","computed":true},"website_domain":{"type":"string","computed":true},"website_endpoint":{"type":"string","computed":true}}}},"aws_s3_bucket_object":{"version":0,"block":{"attributes":{"body":{"type":"string","computed":true},"bucket":{"type":"string","required":true},"cache_control":{"type":"string","computed":true},"content_disposition":{"type":"string","computed":true},"content_encoding":{"type":"string","computed":true},"content_language":{"type":"string","computed":true},"content_length":{"type":"number","computed":true},"content_type":{"type":"string","computed":true},"etag":{"type":"string","computed":true},"expiration":{"type":"string","computed":true},"expires":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","required":true},"last_modified":{"type":"string","computed":true},"metadata":{"type":["map","string"],"computed":true},"object_lock_legal_hold_status":{"type":"string","computed":true},"object_lock_mode":{"type":"string","computed":true},"object_lock_retain_until_date":{"type":"string","computed":true},"range":{"type":"string","optional":true},"server_side_encryption":{"type":"string","computed":true},"sse_kms_key_id":{"type":"string","computed":true},"storage_class":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"version_id":{"type":"string","optional":true,"computed":true},"website_redirect_location":{"type":"string","computed":true}}}},"aws_s3_bucket_objects":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"common_prefixes":{"type":["list","string"],"computed":true},"delimiter":{"type":"string","optional":true},"encoding_type":{"type":"string","optional":true},"fetch_owner":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"keys":{"type":["list","string"],"computed":true},"max_keys":{"type":"number","optional":true},"owners":{"type":["list","string"],"computed":true},"prefix":{"type":"string","optional":true},"start_after":{"type":"string","optional":true}}}},"aws_secretsmanager_secret":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","computed":true},"rotation_enabled":{"type":"bool","computed":true},"rotation_lambda_arn":{"type":"string","computed":true},"rotation_rules":{"type":["list",["object",{"automatically_after_days":"number"}]],"computed":true},"tags":{"type":["map","string"],"computed":true}}}},"aws_secretsmanager_secret_rotation":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"rotation_enabled":{"type":"bool","computed":true},"rotation_lambda_arn":{"type":"string","computed":true},"rotation_rules":{"type":["list",["object",{"automatically_after_days":"number"}]],"computed":true},"secret_id":{"type":"string","required":true}}}},"aws_secretsmanager_secret_version":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"secret_binary":{"type":"string","computed":true,"sensitive":true},"secret_id":{"type":"string","required":true},"secret_string":{"type":"string","computed":true,"sensitive":true},"version_id":{"type":"string","optional":true,"computed":true},"version_stage":{"type":"string","optional":true},"version_stages":{"type":["set","string"],"computed":true}}}},"aws_security_group":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_security_groups":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["list","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_ids":{"type":["list","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_servicequotas_service":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"service_code":{"type":"string","computed":true},"service_name":{"type":"string","required":true}}}},"aws_servicequotas_service_quota":{"version":0,"block":{"attributes":{"adjustable":{"type":"bool","computed":true},"arn":{"type":"string","computed":true},"default_value":{"type":"number","computed":true},"global_quota":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"quota_code":{"type":"string","optional":true,"computed":true},"quota_name":{"type":"string","optional":true,"computed":true},"service_code":{"type":"string","required":true},"service_name":{"type":"string","computed":true},"value":{"type":"number","computed":true}}}},"aws_sfn_activity":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"creation_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true}}}},"aws_sfn_state_machine":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_date":{"type":"string","computed":true},"definition":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true}}}},"aws_sns_topic":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_sqs_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"url":{"type":"string","computed":true}}}},"aws_ssm_document":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"content":{"type":"string","computed":true},"document_format":{"type":"string","optional":true},"document_type":{"type":"string","computed":true},"document_version":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_ssm_parameter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"type":{"type":"string","computed":true},"value":{"type":"string","computed":true,"sensitive":true},"version":{"type":"number","computed":true},"with_decryption":{"type":"bool","optional":true}}}},"aws_ssm_patch_baseline":{"version":0,"block":{"attributes":{"default_baseline":{"type":"bool","optional":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"name_prefix":{"type":"string","optional":true},"operating_system":{"type":"string","optional":true},"owner":{"type":"string","required":true}}}},"aws_storagegateway_local_disk":{"version":0,"block":{"attributes":{"disk_id":{"type":"string","computed":true},"disk_node":{"type":"string","optional":true},"disk_path":{"type":"string","optional":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_subnet":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_ipv6_address_on_creation":{"type":"bool","computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"availability_zone_id":{"type":"string","optional":true,"computed":true},"cidr_block":{"type":"string","optional":true,"computed":true},"default_for_az":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block_association_id":{"type":"string","computed":true},"map_public_ip_on_launch":{"type":"bool","computed":true},"outpost_arn":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_subnet_ids":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","required":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_transfer_server":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identity_provider_type":{"type":"string","computed":true},"invocation_role":{"type":"string","computed":true},"logging_role":{"type":"string","computed":true},"server_id":{"type":"string","required":true},"url":{"type":"string","computed":true}}}},"aws_vpc":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"cidr_block":{"type":"string","optional":true,"computed":true},"cidr_block_associations":{"type":["list",["object",{"association_id":"string","cidr_block":"string","state":"string"}]],"computed":true},"default":{"type":"bool","optional":true,"computed":true},"dhcp_options_id":{"type":"string","optional":true,"computed":true},"enable_dns_hostnames":{"type":"bool","computed":true},"enable_dns_support":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_tenancy":{"type":"string","computed":true},"ipv6_association_id":{"type":"string","computed":true},"ipv6_cidr_block":{"type":"string","computed":true},"main_route_table_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpc_dhcp_options":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"dhcp_options_id":{"type":"string","optional":true,"computed":true},"domain_name":{"type":"string","computed":true},"domain_name_servers":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"netbios_name_servers":{"type":["list","string"],"computed":true},"netbios_node_type":{"type":"string","computed":true},"ntp_servers":{"type":["list","string"],"computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpc_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cidr_blocks":{"type":["list","string"],"computed":true},"dns_entry":{"type":["list",["object",{"dns_name":"string","hosted_zone_id":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_ids":{"type":["set","string"],"computed":true},"owner_id":{"type":"string","computed":true},"policy":{"type":"string","computed":true},"prefix_list_id":{"type":"string","computed":true},"private_dns_enabled":{"type":"bool","computed":true},"requester_managed":{"type":"bool","computed":true},"route_table_ids":{"type":["set","string"],"computed":true},"security_group_ids":{"type":["set","string"],"computed":true},"service_name":{"type":"string","optional":true,"computed":true},"state":{"type":"string","optional":true,"computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_endpoint_type":{"type":"string","computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpc_endpoint_service":{"version":0,"block":{"attributes":{"acceptance_required":{"type":"bool","computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"base_endpoint_dns_names":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"manages_vpc_endpoints":{"type":"bool","computed":true},"owner":{"type":"string","computed":true},"private_dns_name":{"type":"string","computed":true},"service":{"type":"string","optional":true},"service_id":{"type":"string","computed":true},"service_name":{"type":"string","optional":true,"computed":true},"service_type":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_endpoint_policy_supported":{"type":"bool","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_vpc_peering_connection":{"version":0,"block":{"attributes":{"accepter":{"type":["map","bool"],"computed":true},"cidr_block":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","optional":true,"computed":true},"peer_cidr_block":{"type":"string","optional":true,"computed":true},"peer_owner_id":{"type":"string","optional":true,"computed":true},"peer_region":{"type":"string","optional":true,"computed":true},"peer_vpc_id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true,"computed":true},"requester":{"type":["map","bool"],"computed":true},"status":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpcs":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpn_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"attached_vpc_id":{"type":"string","optional":true,"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_waf_ipset":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_waf_rate_based_rule":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_waf_rule":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_waf_web_acl":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafregional_ipset":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafregional_rate_based_rule":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafregional_rule":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafregional_web_acl":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafv2_ip_set":{"version":0,"block":{"attributes":{"addresses":{"type":["set","string"],"computed":true},"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address_version":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true}}}},"aws_wafv2_regex_pattern_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"regular_expression":{"type":["set",["object",{"regex_string":"string"}]],"computed":true},"scope":{"type":"string","required":true}}}},"aws_wafv2_rule_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true}}}},"aws_wafv2_web_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true}}}},"aws_workspaces_bundle":{"version":0,"block":{"attributes":{"bundle_id":{"type":"string","optional":true},"compute_type":{"type":["list",["object",{"name":"string"}]],"computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"owner":{"type":"string","optional":true},"root_storage":{"type":["list",["object",{"capacity":"string"}]],"computed":true},"user_storage":{"type":["list",["object",{"capacity":"string"}]],"computed":true}}}},"aws_workspaces_directory":{"version":0,"block":{"attributes":{"alias":{"type":"string","computed":true},"customer_user_name":{"type":"string","computed":true},"directory_id":{"type":"string","required":true},"directory_name":{"type":"string","computed":true},"directory_type":{"type":"string","computed":true},"dns_ip_addresses":{"type":["set","string"],"computed":true},"iam_role_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_group_ids":{"type":["set","string"],"computed":true},"registration_code":{"type":"string","computed":true},"self_service_permissions":{"type":["list",["object",{"change_compute_type":"bool","increase_volume_size":"bool","rebuild_workspace":"bool","restart_workspace":"bool","switch_running_mode":"bool"}]],"computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true},"workspace_security_group_id":{"type":"string","computed":true}}}}}},"null":{"provider":{"version":0,"block":{}},"resource_schemas":{"null_resource":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"triggers":{"type":["map","string"],"optional":true}}}}},"data_source_schemas":{"null_data_source":{"version":0,"block":{"attributes":{"has_computed_default":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"inputs":{"type":["map","string"],"optional":true},"outputs":{"type":["map","string"],"computed":true},"random":{"type":"string","computed":true}}}}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/basic/variables.tf b/vendor/github.com/hashicorp/terraform-json/testdata/basic/variables.tf new file mode 100644 index 00000000..3964e7ac --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/basic/variables.tf @@ -0,0 +1,15 @@ +variable "foo" { + description = "foobar" + default = "bar" +} + +variable "number" { + default = 42 +} + +variable "map" { + default = { + foo = "bar" + number = 42 + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/config_resource_depends_on/main.tf b/vendor/github.com/hashicorp/terraform-json/testdata/config_resource_depends_on/main.tf new file mode 100644 index 00000000..4eede791 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/config_resource_depends_on/main.tf @@ -0,0 +1,5 @@ +resource "null_resource" "foo" {} + +resource "null_resource" "bar" { + depends_on = ["null_resource.foo"] +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/config_resource_depends_on/plan.json b/vendor/github.com/hashicorp/terraform-json/testdata/config_resource_depends_on/plan.json new file mode 100644 index 00000000..c1a6fb74 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/config_resource_depends_on/plan.json @@ -0,0 +1 @@ +{"format_version":"0.1","terraform_version":"0.12.11","planned_values":{"root_module":{"resources":[{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_name":"null","schema_version":0,"values":{"triggers":null}},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","schema_version":0,"values":{"id":"4379128789376396166","triggers":null}}]}},"resource_changes":[{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_name":"null","change":{"actions":["delete","create"],"before":{"id":"8595481736657951026","triggers":{"foo":"4379128789376396166"}},"after":{"triggers":null},"after_unknown":{"id":true}}},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","change":{"actions":["no-op"],"before":{"id":"4379128789376396166","triggers":null},"after":{"id":"4379128789376396166","triggers":null},"after_unknown":{}}}],"prior_state":{"format_version":"0.1","terraform_version":"0.12.11","values":{"root_module":{"resources":[{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_name":"null","schema_version":0,"values":{"id":"8595481736657951026","triggers":{"foo":"4379128789376396166"}},"depends_on":["null_resource.foo"]},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","schema_version":0,"values":{"id":"4379128789376396166","triggers":null}}]}}},"configuration":{"root_module":{"resources":[{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_config_key":"null","schema_version":0,"depends_on":["null_resource.foo"]},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_config_key":"null","schema_version":0}]}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/config_resource_depends_on/schemas.json b/vendor/github.com/hashicorp/terraform-json/testdata/config_resource_depends_on/schemas.json new file mode 100644 index 00000000..9c819e83 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/config_resource_depends_on/schemas.json @@ -0,0 +1 @@ +{"format_version":"0.1","provider_schemas":{"null":{"provider":{"version":0,"block":{}},"resource_schemas":{"null_resource":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"triggers":{"type":["map","string"],"optional":true}}}}},"data_source_schemas":{"null_data_source":{"version":0,"block":{"attributes":{"has_computed_default":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"inputs":{"type":["map","string"],"optional":true},"outputs":{"type":["map","string"],"computed":true},"random":{"type":"string","computed":true}}}}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/config_resource_depends_on/terraform.tfstate b/vendor/github.com/hashicorp/terraform-json/testdata/config_resource_depends_on/terraform.tfstate new file mode 100644 index 00000000..4af50fa4 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/config_resource_depends_on/terraform.tfstate @@ -0,0 +1,44 @@ +{ + "version": 4, + "terraform_version": "0.12.0", + "serial": 3, + "lineage": "af5a607f-9eae-15f8-afd8-9d32a8a9fc24", + "outputs": {}, + "resources": [ + { + "mode": "managed", + "type": "null_resource", + "name": "bar", + "provider": "provider.null", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "8595481736657951026", + "triggers": { + "foo": "4379128789376396166" + } + }, + "depends_on": [ + "null_resource.foo" + ] + } + ] + }, + { + "mode": "managed", + "type": "null_resource", + "name": "foo", + "provider": "provider.null", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "4379128789376396166", + "triggers": null + } + } + ] + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/deep_module/foo/bar/main.tf b/vendor/github.com/hashicorp/terraform-json/testdata/deep_module/foo/bar/main.tf new file mode 100644 index 00000000..87b7dc30 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/deep_module/foo/bar/main.tf @@ -0,0 +1 @@ +resource "null_resource" "baz" {} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/deep_module/foo/main.tf b/vendor/github.com/hashicorp/terraform-json/testdata/deep_module/foo/main.tf new file mode 100644 index 00000000..88d61ea3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/deep_module/foo/main.tf @@ -0,0 +1,3 @@ +module "bar" { + source = "./bar" +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/deep_module/main.tf b/vendor/github.com/hashicorp/terraform-json/testdata/deep_module/main.tf new file mode 100644 index 00000000..9ae1dbec --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/deep_module/main.tf @@ -0,0 +1,3 @@ +module "foo" { + source = "./foo" +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/deep_module/plan.json b/vendor/github.com/hashicorp/terraform-json/testdata/deep_module/plan.json new file mode 100644 index 00000000..a3f69288 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/deep_module/plan.json @@ -0,0 +1 @@ +{"format_version":"0.1","terraform_version":"0.12.11","planned_values":{"root_module":{"child_modules":[{"address":"module.foo","child_modules":[{"resources":[{"address":"module.foo.module.bar.null_resource.baz","mode":"managed","type":"null_resource","name":"baz","provider_name":"null","schema_version":0,"values":{"triggers":null}}],"address":"module.foo.module.bar"}]}]}},"resource_changes":[{"address":"module.foo.module.bar.null_resource.baz","module_address":"module.foo.module.bar","mode":"managed","type":"null_resource","name":"baz","provider_name":"null","change":{"actions":["create"],"before":null,"after":{"triggers":null},"after_unknown":{"id":true}}}],"configuration":{"root_module":{"module_calls":{"foo":{"source":"./foo","module":{"module_calls":{"bar":{"source":"./bar","module":{"resources":[{"address":"null_resource.baz","mode":"managed","type":"null_resource","name":"baz","provider_config_key":"bar:null","schema_version":0}]}}}}}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/deep_module/schemas.json b/vendor/github.com/hashicorp/terraform-json/testdata/deep_module/schemas.json new file mode 100644 index 00000000..9c819e83 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/deep_module/schemas.json @@ -0,0 +1 @@ +{"format_version":"0.1","provider_schemas":{"null":{"provider":{"version":0,"block":{}},"resource_schemas":{"null_resource":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"triggers":{"type":["map","string"],"optional":true}}}}},"data_source_schemas":{"null_data_source":{"version":0,"block":{"attributes":{"has_computed_default":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"inputs":{"type":["map","string"],"optional":true},"outputs":{"type":["map","string"],"computed":true},"random":{"type":"string","computed":true}}}}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/explicit_null/main.tf b/vendor/github.com/hashicorp/terraform-json/testdata/explicit_null/main.tf new file mode 100644 index 00000000..0970874e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/explicit_null/main.tf @@ -0,0 +1,22 @@ +variable "one" { + type = "string" +} + +resource "null_resource" "foo" { + triggers = { + foo = null + } +} + +resource "null_resource" "bar" { + triggers = { + foo = var.one + } +} + +resource "null_resource" "baz" { + triggers = { + foo = var.one + bar = null + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/explicit_null/plan.json b/vendor/github.com/hashicorp/terraform-json/testdata/explicit_null/plan.json new file mode 100644 index 00000000..123cbf42 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/explicit_null/plan.json @@ -0,0 +1 @@ +{"format_version":"0.1","terraform_version":"0.12.11","variables":{"one":{"value":"two"}},"planned_values":{"root_module":{"resources":[{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_name":"null","schema_version":0,"values":{"triggers":{"foo":"two"}}},{"address":"null_resource.baz","mode":"managed","type":"null_resource","name":"baz","provider_name":"null","schema_version":0,"values":{"triggers":{"foo":"two"}}},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","schema_version":0,"values":{"triggers":null}}]}},"resource_changes":[{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_name":"null","change":{"actions":["create"],"before":null,"after":{"triggers":{"foo":"two"}},"after_unknown":{"id":true,"triggers":{}}}},{"address":"null_resource.baz","mode":"managed","type":"null_resource","name":"baz","provider_name":"null","change":{"actions":["create"],"before":null,"after":{"triggers":{"foo":"two"}},"after_unknown":{"id":true,"triggers":{}}}},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","change":{"actions":["create"],"before":null,"after":{"triggers":null},"after_unknown":{"id":true}}}],"configuration":{"root_module":{"resources":[{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_config_key":"null","expressions":{"triggers":{"references":["var.one"]}},"schema_version":0},{"address":"null_resource.baz","mode":"managed","type":"null_resource","name":"baz","provider_config_key":"null","expressions":{"triggers":{"references":["var.one"]}},"schema_version":0},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_config_key":"null","expressions":{"triggers":{"constant_value":{"foo":null}}},"schema_version":0}],"variables":{"one":{}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/explicit_null/schemas.json b/vendor/github.com/hashicorp/terraform-json/testdata/explicit_null/schemas.json new file mode 100644 index 00000000..9c819e83 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/explicit_null/schemas.json @@ -0,0 +1 @@ +{"format_version":"0.1","provider_schemas":{"null":{"provider":{"version":0,"block":{}},"resource_schemas":{"null_resource":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"triggers":{"type":["map","string"],"optional":true}}}}},"data_source_schemas":{"null_data_source":{"version":0,"block":{"attributes":{"has_computed_default":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"inputs":{"type":["map","string"],"optional":true},"outputs":{"type":["map","string"],"computed":true},"random":{"type":"string","computed":true}}}}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/explicit_null/terraform.tfvars b/vendor/github.com/hashicorp/terraform-json/testdata/explicit_null/terraform.tfvars new file mode 100644 index 00000000..61806943 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/explicit_null/terraform.tfvars @@ -0,0 +1 @@ +one = "two" diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/foo/main.tf b/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/foo/main.tf new file mode 100644 index 00000000..f3d0b009 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/foo/main.tf @@ -0,0 +1,13 @@ +variable "bar" { + type = "string" +} + +variable "one" { + type = "string" +} + +resource "null_resource" "foo" { + triggers = { + foo = "bar" + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/module.tf b/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/module.tf new file mode 100644 index 00000000..236fdac6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/module.tf @@ -0,0 +1,6 @@ +module "foo" { + source = "./foo" + + bar = "baz" + one = "two" +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/outputs.tf b/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/outputs.tf new file mode 100644 index 00000000..1e2a4531 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/outputs.tf @@ -0,0 +1,52 @@ +output "foo" { + sensitive = true + value = "bar" +} + +output "string" { + value = "foo" +} + +output "list" { + value = [ + "foo", + "bar", + ] +} + +output "map" { + value = { + foo = "bar" + number = 42 + } +} + +output "referenced" { + value = null_resource.foo.id +} + +output "interpolated" { + value = "${null_resource.foo.id}" +} + +output "referenced_deep" { + value = { + foo = "bar" + number = 42 + map = { + bar = "baz" + id = null_resource.foo.id + } + } +} + +output "interpolated_deep" { + value = { + foo = "bar" + number = 42 + map = { + bar = "baz" + id = "${null_resource.foo.id}" + } + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/plan.json b/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/plan.json new file mode 100644 index 00000000..2e26384a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/plan.json @@ -0,0 +1 @@ +{"format_version":"0.1","terraform_version":"0.12.11","variables":{"foo":{"value":"bar"},"map":{"value":{"foo":"bar","number":42}},"number":{"value":42}},"planned_values":{"outputs":{"foo":{"sensitive":true,"value":"bar"},"interpolated":{"sensitive":false,"value":"424881806176056736"},"interpolated_deep":{"sensitive":false,"value":{"foo":"bar","map":{"bar":"baz","id":"424881806176056736"},"number":42}},"list":{"sensitive":false,"value":["foo","bar"]},"map":{"sensitive":false,"value":{"foo":"bar","number":42}},"referenced":{"sensitive":false,"value":"424881806176056736"},"referenced_deep":{"sensitive":false,"value":{"foo":"bar","map":{"bar":"baz","id":"424881806176056736"},"number":42}},"string":{"sensitive":false,"value":"foo"}},"root_module":{"resources":[{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_name":"null","schema_version":0,"values":{"id":"4347220156304926627","triggers":{"foo_id":"424881806176056736"}}},{"address":"null_resource.baz[0]","mode":"managed","type":"null_resource","name":"baz","index":0,"provider_name":"null","schema_version":0,"values":{"id":"751901236459396488","triggers":{"foo_id":"424881806176056736"}}},{"address":"null_resource.baz[1]","mode":"managed","type":"null_resource","name":"baz","index":1,"provider_name":"null","schema_version":0,"values":{"id":"2106740714798375541","triggers":{"foo_id":"424881806176056736"}}},{"address":"null_resource.baz[2]","mode":"managed","type":"null_resource","name":"baz","index":2,"provider_name":"null","schema_version":0,"values":{"id":"8665755682221598193","triggers":{"foo_id":"424881806176056736"}}},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","schema_version":0,"values":{"id":"424881806176056736","triggers":{"foo":"bar"}}}],"child_modules":[{"resources":[{"address":"module.foo.null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","schema_version":0,"values":{"id":"705267318028962447","triggers":{"foo":"bar"}}}],"address":"module.foo"}]}},"resource_changes":[{"address":"module.foo.null_resource.foo","module_address":"module.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","change":{"actions":["no-op"],"before":{"id":"705267318028962447","triggers":{"foo":"bar"}},"after":{"id":"705267318028962447","triggers":{"foo":"bar"}},"after_unknown":{}}},{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_name":"null","change":{"actions":["no-op"],"before":{"id":"4347220156304926627","triggers":{"foo_id":"424881806176056736"}},"after":{"id":"4347220156304926627","triggers":{"foo_id":"424881806176056736"}},"after_unknown":{}}},{"address":"null_resource.baz[0]","mode":"managed","type":"null_resource","name":"baz","index":0,"provider_name":"null","change":{"actions":["no-op"],"before":{"id":"751901236459396488","triggers":{"foo_id":"424881806176056736"}},"after":{"id":"751901236459396488","triggers":{"foo_id":"424881806176056736"}},"after_unknown":{}}},{"address":"null_resource.baz[1]","mode":"managed","type":"null_resource","name":"baz","index":1,"provider_name":"null","change":{"actions":["no-op"],"before":{"id":"2106740714798375541","triggers":{"foo_id":"424881806176056736"}},"after":{"id":"2106740714798375541","triggers":{"foo_id":"424881806176056736"}},"after_unknown":{}}},{"address":"null_resource.baz[2]","mode":"managed","type":"null_resource","name":"baz","index":2,"provider_name":"null","change":{"actions":["no-op"],"before":{"id":"8665755682221598193","triggers":{"foo_id":"424881806176056736"}},"after":{"id":"8665755682221598193","triggers":{"foo_id":"424881806176056736"}},"after_unknown":{}}},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","change":{"actions":["no-op"],"before":{"id":"424881806176056736","triggers":{"foo":"bar"}},"after":{"id":"424881806176056736","triggers":{"foo":"bar"}},"after_unknown":{}}}],"output_changes":{"foo":{"actions":["create"],"before":null,"after":"bar","after_unknown":false},"interpolated":{"actions":["create"],"before":null,"after":"424881806176056736","after_unknown":false},"interpolated_deep":{"actions":["create"],"before":null,"after":{"foo":"bar","map":{"bar":"baz","id":"424881806176056736"},"number":42},"after_unknown":false},"list":{"actions":["create"],"before":null,"after":["foo","bar"],"after_unknown":false},"map":{"actions":["create"],"before":null,"after":{"foo":"bar","number":42},"after_unknown":false},"referenced":{"actions":["create"],"before":null,"after":"424881806176056736","after_unknown":false},"referenced_deep":{"actions":["create"],"before":null,"after":{"foo":"bar","map":{"bar":"baz","id":"424881806176056736"},"number":42},"after_unknown":false},"string":{"actions":["create"],"before":null,"after":"foo","after_unknown":false}},"prior_state":{"format_version":"0.1","terraform_version":"0.12.11","values":{"outputs":{"foo":{"sensitive":true,"value":"bar"},"interpolated":{"sensitive":false,"value":"424881806176056736"},"interpolated_deep":{"sensitive":false,"value":{"foo":"bar","map":{"bar":"baz","id":"424881806176056736"},"number":42}},"list":{"sensitive":false,"value":["foo","bar"]},"map":{"sensitive":false,"value":{"foo":"bar","number":42}},"referenced":{"sensitive":false,"value":"424881806176056736"},"referenced_deep":{"sensitive":false,"value":{"foo":"bar","map":{"bar":"baz","id":"424881806176056736"},"number":42}},"string":{"sensitive":false,"value":"foo"}},"root_module":{"resources":[{"address":"data.null_data_source.baz","mode":"data","type":"null_data_source","name":"baz","provider_name":"null","schema_version":0,"values":{"has_computed_default":"default","id":"static","inputs":{"bar_id":"4347220156304926627","foo_id":"424881806176056736"},"outputs":{"bar_id":"4347220156304926627","foo_id":"424881806176056736"},"random":"3214461066155901416"},"depends_on":["null_resource.bar","null_resource.foo"]},{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_name":"null","schema_version":0,"values":{"id":"4347220156304926627","triggers":{"foo_id":"424881806176056736"}},"depends_on":["null_resource.foo"]},{"address":"null_resource.baz","mode":"managed","type":"null_resource","name":"baz","index":1,"provider_name":"null","schema_version":0,"values":{"id":"2106740714798375541","triggers":{"foo_id":"424881806176056736"}},"depends_on":["null_resource.foo"]},{"address":"null_resource.baz","mode":"managed","type":"null_resource","name":"baz","index":2,"provider_name":"null","schema_version":0,"values":{"id":"8665755682221598193","triggers":{"foo_id":"424881806176056736"}},"depends_on":["null_resource.foo"]},{"address":"null_resource.baz","mode":"managed","type":"null_resource","name":"baz","index":0,"provider_name":"null","schema_version":0,"values":{"id":"751901236459396488","triggers":{"foo_id":"424881806176056736"}},"depends_on":["null_resource.foo"]},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","schema_version":0,"values":{"id":"424881806176056736","triggers":{"foo":"bar"}}}],"child_modules":[{"resources":[{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","schema_version":0,"values":{"id":"705267318028962447","triggers":{"foo":"bar"}}}],"address":"module.foo"}]}}},"configuration":{"provider_config":{"aws":{"name":"aws","expressions":{"region":{"constant_value":"us-west-2"}}},"aws.east":{"name":"aws","alias":"east","expressions":{"region":{"constant_value":"us-east-1"}}},"null":{"name":"null"}},"root_module":{"outputs":{"foo":{"sensitive":true,"expression":{"constant_value":"bar"}},"interpolated":{"expression":{"references":["null_resource.foo"]}},"interpolated_deep":{"expression":{"references":["null_resource.foo"]}},"list":{"expression":{"constant_value":["foo","bar"]}},"map":{"expression":{"constant_value":{"foo":"bar","number":42}}},"referenced":{"expression":{"references":["null_resource.foo"]}},"referenced_deep":{"expression":{"references":["null_resource.foo"]}},"string":{"expression":{"constant_value":"foo"}}},"resources":[{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_config_key":"null","expressions":{"triggers":{"references":["null_resource.foo"]}},"schema_version":0},{"address":"null_resource.baz","mode":"managed","type":"null_resource","name":"baz","provider_config_key":"null","expressions":{"triggers":{"references":["null_resource.foo"]}},"schema_version":0,"count_expression":{"constant_value":3}},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_config_key":"null","provisioners":[{"type":"local-exec","expressions":{"command":{"constant_value":"echo hello"}}}],"expressions":{"triggers":{"constant_value":{"foo":"bar"}}},"schema_version":0},{"address":"data.null_data_source.baz","mode":"data","type":"null_data_source","name":"baz","provider_config_key":"null","expressions":{"inputs":{"references":["null_resource.foo","null_resource.bar"]}},"schema_version":0}],"module_calls":{"foo":{"source":"./foo","expressions":{"bar":{"constant_value":"baz"},"one":{"constant_value":"two"}},"module":{"resources":[{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_config_key":"foo:null","expressions":{"triggers":{"constant_value":{"foo":"bar"}}},"schema_version":0}],"variables":{"bar":{},"one":{}}}}},"variables":{"foo":{"default":"bar","description":"foobar"},"map":{"default":{"foo":"bar","number":42}},"number":{"default":42}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/providers.tf b/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/providers.tf new file mode 100644 index 00000000..4d481205 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/providers.tf @@ -0,0 +1,10 @@ +provider "null" {} + +provider "aws" { + region = "us-west-2" +} + +provider "aws" { + alias = "east" + region = "us-east-1" +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/resources.tf b/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/resources.tf new file mode 100644 index 00000000..1d4a8e8f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/resources.tf @@ -0,0 +1,30 @@ +resource "null_resource" "foo" { + triggers = { + foo = "bar" + } + + provisioner "local-exec" { + command = "echo hello" + } +} + +resource "null_resource" "bar" { + triggers = { + foo_id = "${null_resource.foo.id}" + } +} + +data "null_data_source" "baz" { + inputs = { + foo_id = "${null_resource.foo.id}" + bar_id = "${null_resource.bar.id}" + } +} + +resource "null_resource" "baz" { + count = 3 + + triggers = { + foo_id = "${null_resource.foo.id}" + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/schemas.json b/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/schemas.json new file mode 100644 index 00000000..8a610e2e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/schemas.json @@ -0,0 +1 @@ +{"format_version":"0.1","provider_schemas":{"aws":{"provider":{"version":0,"block":{"attributes":{"access_key":{"type":"string","description":"The access key for API operations. You can retrieve this\nfrom the 'Security \u0026 Credentials' section of the AWS console.","optional":true},"allowed_account_ids":{"type":["set","string"],"optional":true},"forbidden_account_ids":{"type":["set","string"],"optional":true},"insecure":{"type":"bool","description":"Explicitly allow the provider to perform \"insecure\" SSL requests. If omitted,default value is `false`","optional":true},"max_retries":{"type":"number","description":"The maximum number of times an AWS API request is\nbeing executed. If the API request still fails, an error is\nthrown.","optional":true},"profile":{"type":"string","description":"The profile for API operations. If not set, the default profile\ncreated with `aws configure` will be used.","optional":true},"region":{"type":"string","description":"The region where AWS operations will take place. Examples\nare us-east-1, us-west-2, etc.","required":true},"s3_force_path_style":{"type":"bool","description":"Set this to true to force the request to use path-style addressing,\ni.e., http://s3.amazonaws.com/BUCKET/KEY. By default, the S3 client will\nuse virtual hosted bucket addressing when possible\n(http://BUCKET.s3.amazonaws.com/KEY). Specific to the Amazon S3 service.","optional":true},"secret_key":{"type":"string","description":"The secret key for API operations. You can retrieve this\nfrom the 'Security \u0026 Credentials' section of the AWS console.","optional":true},"shared_credentials_file":{"type":"string","description":"The path to the shared credentials file. If not set\nthis defaults to ~/.aws/credentials.","optional":true},"skip_credentials_validation":{"type":"bool","description":"Skip the credentials validation via STS API. Used for AWS API implementations that do not have STS available/implemented.","optional":true},"skip_get_ec2_platforms":{"type":"bool","description":"Skip getting the supported EC2 platforms. Used by users that don't have ec2:DescribeAccountAttributes permissions.","optional":true},"skip_metadata_api_check":{"type":"bool","optional":true},"skip_region_validation":{"type":"bool","description":"Skip static validation of region name. Used by users of alternative AWS-like APIs or users w/ access to regions that are not public (yet).","optional":true},"skip_requesting_account_id":{"type":"bool","description":"Skip requesting the account ID. Used for AWS API implementations that do not have IAM/STS API and/or metadata API.","optional":true},"token":{"type":"string","description":"session token. A session token is only required if you are\nusing temporary security credentials.","optional":true}},"block_types":{"assume_role":{"nesting_mode":"list","block":{"attributes":{"duration_seconds":{"type":"number","description":"Seconds to restrict the assume role session duration.","optional":true},"external_id":{"type":"string","description":"Unique identifier that might be required for assuming a role in another account.","optional":true},"policy":{"type":"string","description":"IAM Policy JSON describing further restricting permissions for the IAM Role being assumed.","optional":true},"policy_arns":{"type":["set","string"],"description":"Amazon Resource Names (ARNs) of IAM Policies describing further restricting permissions for the IAM Role being assumed.","optional":true},"role_arn":{"type":"string","description":"Amazon Resource Name of an IAM Role to assume prior to making API calls.","optional":true},"session_name":{"type":"string","description":"Identifier for the assumed role session.","optional":true},"tags":{"type":["map","string"],"description":"Assume role session tags.","optional":true},"transitive_tag_keys":{"type":["set","string"],"description":"Assume role session tag keys to pass to any subsequent sessions.","optional":true}}},"max_items":1},"endpoints":{"nesting_mode":"set","block":{"attributes":{"accessanalyzer":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"acm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"acmpca":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"amplify":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"apigateway":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"applicationautoscaling":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"applicationinsights":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"appmesh":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"appstream":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"appsync":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"athena":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"autoscaling":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"autoscalingplans":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"backup":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"batch":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"budgets":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloud9":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudformation":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudfront":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudhsm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudsearch":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudtrail":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudwatch":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudwatchevents":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudwatchlogs":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codeartifact":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codebuild":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codecommit":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codedeploy":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codepipeline":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cognitoidentity":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cognitoidp":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"configservice":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cur":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dataexchange":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"datapipeline":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"datasync":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dax":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"devicefarm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"directconnect":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dlm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dms":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"docdb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ds":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dynamodb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ec2":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ecr":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ecs":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"efs":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"eks":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"elasticache":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"elasticbeanstalk":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"elastictranscoder":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"elb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"emr":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"es":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"firehose":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"fms":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"forecast":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"fsx":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"gamelift":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"glacier":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"globalaccelerator":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"glue":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"greengrass":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"guardduty":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"iam":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"imagebuilder":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"inspector":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"iot":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"iotanalytics":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"iotevents":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kafka":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kinesis":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kinesisanalytics":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kinesisanalyticsv2":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kinesisvideo":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kms":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"lakeformation":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"lambda":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"lexmodels":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"licensemanager":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"lightsail":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"macie":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"managedblockchain":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"marketplacecatalog":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediaconnect":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediaconvert":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"medialive":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediapackage":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediastore":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediastoredata":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mq":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"neptune":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"networkmanager":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"opsworks":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"organizations":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"outposts":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"personalize":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"pinpoint":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"pricing":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"qldb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"quicksight":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ram":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"rds":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"redshift":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"resourcegroups":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"resourcegroupstaggingapi":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"route53":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"route53domains":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"route53resolver":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"s3":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"s3control":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sagemaker":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sdb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"secretsmanager":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"securityhub":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"serverlessrepo":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"servicecatalog":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"servicediscovery":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"servicequotas":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ses":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"shield":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sns":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sqs":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ssm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"stepfunctions":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"storagegateway":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sts":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"swf":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"synthetics":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"transfer":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"waf":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"wafregional":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"wafv2":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"worklink":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"workmail":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"workspaces":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"xray":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true}}}},"ignore_tags":{"nesting_mode":"list","block":{"attributes":{"key_prefixes":{"type":["set","string"],"description":"Resource tag key prefixes to ignore across all resources.","optional":true},"keys":{"type":["set","string"],"description":"Resource tag keys to ignore across all resources.","optional":true}}},"max_items":1}}}},"resource_schemas":{"aws_accessanalyzer_analyzer":{"version":0,"block":{"attributes":{"analyzer_name":{"type":"string","required":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true}}}},"aws_acm_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_authority_arn":{"type":"string","optional":true},"certificate_body":{"type":"string","optional":true},"certificate_chain":{"type":"string","optional":true},"domain_name":{"type":"string","optional":true,"computed":true},"domain_validation_options":{"type":["set",["object",{"domain_name":"string","resource_record_name":"string","resource_record_type":"string","resource_record_value":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"status":{"type":"string","computed":true},"subject_alternative_names":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"validation_emails":{"type":["list","string"],"computed":true},"validation_method":{"type":"string","optional":true,"computed":true}},"block_types":{"options":{"nesting_mode":"list","block":{"attributes":{"certificate_transparency_logging_preference":{"type":"string","optional":true}}},"max_items":1}}}},"aws_acm_certificate_validation":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"validation_record_fqdns":{"type":["set","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_acmpca_certificate_authority":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate":{"type":"string","computed":true},"certificate_chain":{"type":"string","computed":true},"certificate_signing_request":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"not_after":{"type":"string","computed":true},"not_before":{"type":"string","computed":true},"permanent_deletion_time_in_days":{"type":"number","optional":true},"serial":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true}},"block_types":{"certificate_authority_configuration":{"nesting_mode":"list","block":{"attributes":{"key_algorithm":{"type":"string","required":true},"signing_algorithm":{"type":"string","required":true}},"block_types":{"subject":{"nesting_mode":"list","block":{"attributes":{"common_name":{"type":"string","optional":true},"country":{"type":"string","optional":true},"distinguished_name_qualifier":{"type":"string","optional":true},"generation_qualifier":{"type":"string","optional":true},"given_name":{"type":"string","optional":true},"initials":{"type":"string","optional":true},"locality":{"type":"string","optional":true},"organization":{"type":"string","optional":true},"organizational_unit":{"type":"string","optional":true},"pseudonym":{"type":"string","optional":true},"state":{"type":"string","optional":true},"surname":{"type":"string","optional":true},"title":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"revocation_configuration":{"nesting_mode":"list","block":{"block_types":{"crl_configuration":{"nesting_mode":"list","block":{"attributes":{"custom_cname":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"expiration_in_days":{"type":"number","required":true},"s3_bucket_name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_alb":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"arn_suffix":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"drop_invalid_header_fields":{"type":"bool","optional":true},"enable_cross_zone_load_balancing":{"type":"bool","optional":true},"enable_deletion_protection":{"type":"bool","optional":true},"enable_http2":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","optional":true},"internal":{"type":"bool","optional":true,"computed":true},"ip_address_type":{"type":"string","optional":true,"computed":true},"load_balancer_type":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"subnets":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"zone_id":{"type":"string","computed":true}},"block_types":{"access_logs":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"prefix":{"type":"string","optional":true}}},"max_items":1},"subnet_mapping":{"nesting_mode":"set","block":{"attributes":{"allocation_id":{"type":"string","optional":true},"private_ipv4_address":{"type":"string","optional":true},"subnet_id":{"type":"string","required":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_alb_listener":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"load_balancer_arn":{"type":"string","required":true},"port":{"type":"number","required":true},"protocol":{"type":"string","optional":true},"ssl_policy":{"type":"string","optional":true,"computed":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"attributes":{"order":{"type":"number","optional":true,"computed":true},"target_group_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"authenticate_cognito":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"user_pool_arn":{"type":"string","required":true},"user_pool_client_id":{"type":"string","required":true},"user_pool_domain":{"type":"string","required":true}}},"max_items":1},"authenticate_oidc":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"authorization_endpoint":{"type":"string","required":true},"client_id":{"type":"string","required":true},"client_secret":{"type":"string","required":true,"sensitive":true},"issuer":{"type":"string","required":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"token_endpoint":{"type":"string","required":true},"user_info_endpoint":{"type":"string","required":true}}},"max_items":1},"fixed_response":{"nesting_mode":"list","block":{"attributes":{"content_type":{"type":"string","required":true},"message_body":{"type":"string","optional":true},"status_code":{"type":"string","optional":true,"computed":true}}},"max_items":1},"forward":{"nesting_mode":"list","block":{"block_types":{"stickiness":{"nesting_mode":"list","block":{"attributes":{"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1},"target_group":{"nesting_mode":"set","block":{"attributes":{"arn":{"type":"string","required":true},"weight":{"type":"number","optional":true}}},"min_items":2,"max_items":5}}},"max_items":1},"redirect":{"nesting_mode":"list","block":{"attributes":{"host":{"type":"string","optional":true},"path":{"type":"string","optional":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"query":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"read":{"type":"string","optional":true}}}}}}},"aws_alb_listener_certificate":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true}}}},"aws_alb_listener_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true},"priority":{"type":"number","optional":true,"computed":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"order":{"type":"number","optional":true,"computed":true},"target_group_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"authenticate_cognito":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"user_pool_arn":{"type":"string","required":true},"user_pool_client_id":{"type":"string","required":true},"user_pool_domain":{"type":"string","required":true}}},"max_items":1},"authenticate_oidc":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"authorization_endpoint":{"type":"string","required":true},"client_id":{"type":"string","required":true},"client_secret":{"type":"string","required":true,"sensitive":true},"issuer":{"type":"string","required":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"token_endpoint":{"type":"string","required":true},"user_info_endpoint":{"type":"string","required":true}}},"max_items":1},"fixed_response":{"nesting_mode":"list","block":{"attributes":{"content_type":{"type":"string","required":true},"message_body":{"type":"string","optional":true},"status_code":{"type":"string","optional":true,"computed":true}}},"max_items":1},"forward":{"nesting_mode":"list","block":{"block_types":{"stickiness":{"nesting_mode":"list","block":{"attributes":{"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1},"target_group":{"nesting_mode":"set","block":{"attributes":{"arn":{"type":"string","required":true},"weight":{"type":"number","optional":true}}},"min_items":2,"max_items":5}}},"max_items":1},"redirect":{"nesting_mode":"list","block":{"attributes":{"host":{"type":"string","optional":true},"path":{"type":"string","optional":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"query":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"condition":{"nesting_mode":"set","block":{"block_types":{"host_header":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"http_header":{"nesting_mode":"list","block":{"attributes":{"http_header_name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}},"max_items":1},"http_request_method":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"path_pattern":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"query_string":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"value":{"type":"string","required":true}}}},"source_ip":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1}}},"min_items":1}}}},"aws_alb_target_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"arn_suffix":{"type":"string","computed":true},"deregistration_delay":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"lambda_multi_value_headers_enabled":{"type":"bool","optional":true},"load_balancing_algorithm_type":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"port":{"type":"number","optional":true},"protocol":{"type":"string","optional":true},"proxy_protocol_v2":{"type":"bool","optional":true},"slow_start":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true},"target_type":{"type":"string","optional":true},"vpc_id":{"type":"string","optional":true}},"block_types":{"health_check":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"healthy_threshold":{"type":"number","optional":true},"interval":{"type":"number","optional":true},"matcher":{"type":"string","optional":true,"computed":true},"path":{"type":"string","optional":true,"computed":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"timeout":{"type":"number","optional":true,"computed":true},"unhealthy_threshold":{"type":"number","optional":true}}},"max_items":1},"stickiness":{"nesting_mode":"list","block":{"attributes":{"cookie_duration":{"type":"number","optional":true},"enabled":{"type":"bool","optional":true},"type":{"type":"string","required":true}}},"max_items":1}}}},"aws_alb_target_group_attachment":{"version":0,"block":{"attributes":{"availability_zone":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"target_group_arn":{"type":"string","required":true},"target_id":{"type":"string","required":true}}}},"aws_ami":{"version":0,"block":{"attributes":{"architecture":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"ena_support":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"image_location":{"type":"string","optional":true,"computed":true},"kernel_id":{"type":"string","optional":true},"manage_ebs_snapshots":{"type":"bool","computed":true},"name":{"type":"string","required":true},"ramdisk_id":{"type":"string","optional":true},"root_device_name":{"type":"string","optional":true},"root_snapshot_id":{"type":"string","computed":true},"sriov_net_support":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"virtualization_type":{"type":"string","optional":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"snapshot_id":{"type":"string","optional":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"virtual_name":{"type":"string","required":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_ami_copy":{"version":0,"block":{"attributes":{"architecture":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"ena_support":{"type":"bool","computed":true},"encrypted":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"image_location":{"type":"string","computed":true},"kernel_id":{"type":"string","computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"manage_ebs_snapshots":{"type":"bool","computed":true},"name":{"type":"string","required":true},"ramdisk_id":{"type":"string","computed":true},"root_device_name":{"type":"string","computed":true},"root_snapshot_id":{"type":"string","computed":true},"source_ami_id":{"type":"string","required":true},"source_ami_region":{"type":"string","required":true},"sriov_net_support":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"virtualization_type":{"type":"string","computed":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","computed":true},"device_name":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"iops":{"type":"number","computed":true},"snapshot_id":{"type":"string","computed":true},"volume_size":{"type":"number","computed":true},"volume_type":{"type":"string","computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","computed":true},"virtual_name":{"type":"string","computed":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_ami_from_instance":{"version":0,"block":{"attributes":{"architecture":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"ena_support":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"image_location":{"type":"string","computed":true},"kernel_id":{"type":"string","computed":true},"manage_ebs_snapshots":{"type":"bool","computed":true},"name":{"type":"string","required":true},"ramdisk_id":{"type":"string","computed":true},"root_device_name":{"type":"string","computed":true},"root_snapshot_id":{"type":"string","computed":true},"snapshot_without_reboot":{"type":"bool","optional":true},"source_instance_id":{"type":"string","required":true},"sriov_net_support":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"virtualization_type":{"type":"string","computed":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","computed":true},"device_name":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"iops":{"type":"number","computed":true},"snapshot_id":{"type":"string","computed":true},"volume_size":{"type":"number","computed":true},"volume_type":{"type":"string","computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","computed":true},"virtual_name":{"type":"string","computed":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_ami_launch_permission":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","required":true}}}},"aws_api_gateway_account":{"version":0,"block":{"attributes":{"cloudwatch_role_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"throttle_settings":{"type":["list",["object",{"burst_limit":"number","rate_limit":"number"}]],"computed":true}}}},"aws_api_gateway_api_key":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"value":{"type":"string","optional":true,"computed":true,"sensitive":true}}}},"aws_api_gateway_authorizer":{"version":0,"block":{"attributes":{"authorizer_credentials":{"type":"string","optional":true},"authorizer_result_ttl_in_seconds":{"type":"number","optional":true},"authorizer_uri":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"identity_source":{"type":"string","optional":true},"identity_validation_expression":{"type":"string","optional":true},"name":{"type":"string","required":true},"provider_arns":{"type":["set","string"],"optional":true},"rest_api_id":{"type":"string","required":true},"type":{"type":"string","optional":true}}}},"aws_api_gateway_base_path_mapping":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"base_path":{"type":"string","optional":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"stage_name":{"type":"string","optional":true}}}},"aws_api_gateway_client_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"expiration_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"pem_encoded_certificate":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_api_gateway_deployment":{"version":0,"block":{"attributes":{"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_url":{"type":"string","computed":true},"rest_api_id":{"type":"string","required":true},"stage_description":{"type":"string","optional":true},"stage_name":{"type":"string","optional":true},"triggers":{"type":["map","string"],"optional":true},"variables":{"type":["map","string"],"optional":true}}}},"aws_api_gateway_documentation_part":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"properties":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true}},"block_types":{"location":{"nesting_mode":"list","block":{"attributes":{"method":{"type":"string","optional":true},"name":{"type":"string","optional":true},"path":{"type":"string","optional":true},"status_code":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_api_gateway_documentation_version":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"rest_api_id":{"type":"string","required":true},"version":{"type":"string","required":true}}}},"aws_api_gateway_domain_name":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_arn":{"type":"string","optional":true},"certificate_body":{"type":"string","optional":true},"certificate_chain":{"type":"string","optional":true},"certificate_name":{"type":"string","optional":true},"certificate_private_key":{"type":"string","optional":true,"sensitive":true},"certificate_upload_date":{"type":"string","computed":true},"cloudfront_domain_name":{"type":"string","computed":true},"cloudfront_zone_id":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"regional_certificate_arn":{"type":"string","optional":true},"regional_certificate_name":{"type":"string","optional":true},"regional_domain_name":{"type":"string","computed":true},"regional_zone_id":{"type":"string","computed":true},"security_policy":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"endpoint_configuration":{"nesting_mode":"list","block":{"attributes":{"types":{"type":["list","string"],"required":true}}},"max_items":1}}}},"aws_api_gateway_gateway_response":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"response_parameters":{"type":["map","string"],"optional":true},"response_templates":{"type":["map","string"],"optional":true},"response_type":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"status_code":{"type":"string","optional":true}}}},"aws_api_gateway_integration":{"version":0,"block":{"attributes":{"cache_key_parameters":{"type":["set","string"],"optional":true},"cache_namespace":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","optional":true},"connection_type":{"type":"string","optional":true},"content_handling":{"type":"string","optional":true},"credentials":{"type":"string","optional":true},"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"integration_http_method":{"type":"string","optional":true},"passthrough_behavior":{"type":"string","optional":true,"computed":true},"request_parameters":{"type":["map","string"],"optional":true},"request_templates":{"type":["map","string"],"optional":true},"resource_id":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"timeout_milliseconds":{"type":"number","optional":true},"type":{"type":"string","required":true},"uri":{"type":"string","optional":true}}}},"aws_api_gateway_integration_response":{"version":0,"block":{"attributes":{"content_handling":{"type":"string","optional":true},"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"resource_id":{"type":"string","required":true},"response_parameters":{"type":["map","string"],"optional":true},"response_templates":{"type":["map","string"],"optional":true},"rest_api_id":{"type":"string","required":true},"selection_pattern":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}}},"aws_api_gateway_method":{"version":0,"block":{"attributes":{"api_key_required":{"type":"bool","optional":true},"authorization":{"type":"string","required":true},"authorization_scopes":{"type":["set","string"],"optional":true},"authorizer_id":{"type":"string","optional":true},"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"request_models":{"type":["map","string"],"optional":true},"request_parameters":{"type":["map","bool"],"optional":true},"request_validator_id":{"type":"string","optional":true},"resource_id":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true}}}},"aws_api_gateway_method_response":{"version":0,"block":{"attributes":{"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"resource_id":{"type":"string","required":true},"response_models":{"type":["map","string"],"optional":true},"response_parameters":{"type":["map","bool"],"optional":true},"rest_api_id":{"type":"string","required":true},"status_code":{"type":"string","required":true}}}},"aws_api_gateway_method_settings":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"method_path":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"stage_name":{"type":"string","required":true}},"block_types":{"settings":{"nesting_mode":"list","block":{"attributes":{"cache_data_encrypted":{"type":"bool","optional":true,"computed":true},"cache_ttl_in_seconds":{"type":"number","optional":true,"computed":true},"caching_enabled":{"type":"bool","optional":true,"computed":true},"data_trace_enabled":{"type":"bool","optional":true,"computed":true},"logging_level":{"type":"string","optional":true,"computed":true},"metrics_enabled":{"type":"bool","optional":true,"computed":true},"require_authorization_for_cache_control":{"type":"bool","optional":true,"computed":true},"throttling_burst_limit":{"type":"number","optional":true},"throttling_rate_limit":{"type":"number","optional":true},"unauthorized_cache_control_header_strategy":{"type":"string","optional":true,"computed":true}}},"min_items":1,"max_items":1}}}},"aws_api_gateway_model":{"version":0,"block":{"attributes":{"content_type":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"schema":{"type":"string","optional":true}}}},"aws_api_gateway_request_validator":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"validate_request_body":{"type":"bool","optional":true},"validate_request_parameters":{"type":"bool","optional":true}}}},"aws_api_gateway_resource":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"parent_id":{"type":"string","required":true},"path":{"type":"string","computed":true},"path_part":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true}}}},"aws_api_gateway_rest_api":{"version":0,"block":{"attributes":{"api_key_source":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"binary_media_types":{"type":["list","string"],"optional":true},"body":{"type":"string","optional":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"minimum_compression_size":{"type":"number","optional":true},"name":{"type":"string","required":true},"policy":{"type":"string","optional":true},"root_resource_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"endpoint_configuration":{"nesting_mode":"list","block":{"attributes":{"types":{"type":["list","string"],"required":true},"vpc_endpoint_ids":{"type":["set","string"],"optional":true}}},"max_items":1}}}},"aws_api_gateway_stage":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cache_cluster_enabled":{"type":"bool","optional":true},"cache_cluster_size":{"type":"string","optional":true},"client_certificate_id":{"type":"string","optional":true},"deployment_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"documentation_version":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_url":{"type":"string","computed":true},"rest_api_id":{"type":"string","required":true},"stage_name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"variables":{"type":["map","string"],"optional":true},"xray_tracing_enabled":{"type":"bool","optional":true}},"block_types":{"access_log_settings":{"nesting_mode":"list","block":{"attributes":{"destination_arn":{"type":"string","required":true},"format":{"type":"string","required":true}}},"max_items":1}}}},"aws_api_gateway_usage_plan":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"product_code":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"api_stages":{"nesting_mode":"list","block":{"attributes":{"api_id":{"type":"string","required":true},"stage":{"type":"string","required":true}}}},"quota_settings":{"nesting_mode":"list","block":{"attributes":{"limit":{"type":"number","required":true},"offset":{"type":"number","optional":true},"period":{"type":"string","required":true}}},"max_items":1},"throttle_settings":{"nesting_mode":"list","block":{"attributes":{"burst_limit":{"type":"number","optional":true},"rate_limit":{"type":"number","optional":true}}},"max_items":1}}}},"aws_api_gateway_usage_plan_key":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"key_type":{"type":"string","required":true},"name":{"type":"string","computed":true},"usage_plan_id":{"type":"string","required":true},"value":{"type":"string","computed":true}}}},"aws_api_gateway_vpc_link":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"target_arns":{"type":["list","string"],"required":true}}}},"aws_apigatewayv2_api":{"version":0,"block":{"attributes":{"api_endpoint":{"type":"string","computed":true},"api_key_selection_expression":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"credentials_arn":{"type":"string","optional":true},"description":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"protocol_type":{"type":"string","required":true},"route_key":{"type":"string","optional":true},"route_selection_expression":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"target":{"type":"string","optional":true},"version":{"type":"string","optional":true}},"block_types":{"cors_configuration":{"nesting_mode":"list","block":{"attributes":{"allow_credentials":{"type":"bool","optional":true},"allow_headers":{"type":["set","string"],"optional":true},"allow_methods":{"type":["set","string"],"optional":true},"allow_origins":{"type":["set","string"],"optional":true},"expose_headers":{"type":["set","string"],"optional":true},"max_age":{"type":"number","optional":true}}},"max_items":1}}}},"aws_apigatewayv2_api_mapping":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"api_mapping_key":{"type":"string","optional":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"stage":{"type":"string","required":true}}}},"aws_apigatewayv2_authorizer":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"authorizer_credentials_arn":{"type":"string","optional":true},"authorizer_type":{"type":"string","required":true},"authorizer_uri":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"identity_sources":{"type":["set","string"],"required":true},"name":{"type":"string","required":true}},"block_types":{"jwt_configuration":{"nesting_mode":"list","block":{"attributes":{"audience":{"type":["set","string"],"optional":true},"issuer":{"type":"string","optional":true}}},"max_items":1}}}},"aws_apigatewayv2_deployment":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"auto_deployed":{"type":"bool","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"triggers":{"type":["map","string"],"optional":true}}}},"aws_apigatewayv2_domain_name":{"version":0,"block":{"attributes":{"api_mapping_selection_expression":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"domain_name_configuration":{"nesting_mode":"list","block":{"attributes":{"certificate_arn":{"type":"string","required":true},"endpoint_type":{"type":"string","required":true},"hosted_zone_id":{"type":"string","computed":true},"security_policy":{"type":"string","required":true},"target_domain_name":{"type":"string","computed":true}}},"min_items":1,"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"update":{"type":"string","optional":true}}}}}}},"aws_apigatewayv2_integration":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"connection_id":{"type":"string","optional":true},"connection_type":{"type":"string","optional":true},"content_handling_strategy":{"type":"string","optional":true},"credentials_arn":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"integration_method":{"type":"string","optional":true},"integration_response_selection_expression":{"type":"string","computed":true},"integration_type":{"type":"string","required":true},"integration_uri":{"type":"string","optional":true},"passthrough_behavior":{"type":"string","optional":true},"payload_format_version":{"type":"string","optional":true},"request_parameters":{"type":["map","string"],"optional":true},"request_templates":{"type":["map","string"],"optional":true},"template_selection_expression":{"type":"string","optional":true},"timeout_milliseconds":{"type":"number","optional":true}},"block_types":{"tls_config":{"nesting_mode":"list","block":{"attributes":{"server_name_to_verify":{"type":"string","optional":true}}},"max_items":1}}}},"aws_apigatewayv2_integration_response":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"content_handling_strategy":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"integration_id":{"type":"string","required":true},"integration_response_key":{"type":"string","required":true},"response_templates":{"type":["map","string"],"optional":true},"template_selection_expression":{"type":"string","optional":true}}}},"aws_apigatewayv2_model":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"content_type":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"schema":{"type":"string","required":true}}}},"aws_apigatewayv2_route":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"api_key_required":{"type":"bool","optional":true},"authorization_scopes":{"type":["set","string"],"optional":true},"authorization_type":{"type":"string","optional":true},"authorizer_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"model_selection_expression":{"type":"string","optional":true},"operation_name":{"type":"string","optional":true},"request_models":{"type":["map","string"],"optional":true},"route_key":{"type":"string","required":true},"route_response_selection_expression":{"type":"string","optional":true},"target":{"type":"string","optional":true}}}},"aws_apigatewayv2_route_response":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"model_selection_expression":{"type":"string","optional":true},"response_models":{"type":["map","string"],"optional":true},"route_id":{"type":"string","required":true},"route_response_key":{"type":"string","required":true}}}},"aws_apigatewayv2_stage":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"auto_deploy":{"type":"bool","optional":true},"client_certificate_id":{"type":"string","optional":true},"deployment_id":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_url":{"type":"string","computed":true},"name":{"type":"string","required":true},"stage_variables":{"type":["map","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"access_log_settings":{"nesting_mode":"list","block":{"attributes":{"destination_arn":{"type":"string","required":true},"format":{"type":"string","required":true}}},"max_items":1},"default_route_settings":{"nesting_mode":"list","block":{"attributes":{"data_trace_enabled":{"type":"bool","optional":true},"detailed_metrics_enabled":{"type":"bool","optional":true},"logging_level":{"type":"string","optional":true,"computed":true},"throttling_burst_limit":{"type":"number","optional":true},"throttling_rate_limit":{"type":"number","optional":true}}},"max_items":1},"route_settings":{"nesting_mode":"set","block":{"attributes":{"data_trace_enabled":{"type":"bool","optional":true},"detailed_metrics_enabled":{"type":"bool","optional":true},"logging_level":{"type":"string","optional":true,"computed":true},"route_key":{"type":"string","required":true},"throttling_burst_limit":{"type":"number","optional":true},"throttling_rate_limit":{"type":"number","optional":true}}}}}}},"aws_apigatewayv2_vpc_link":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"security_group_ids":{"type":["set","string"],"required":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_app_cookie_stickiness_policy":{"version":0,"block":{"attributes":{"cookie_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"lb_port":{"type":"number","required":true},"load_balancer":{"type":"string","required":true},"name":{"type":"string","required":true}}}},"aws_appautoscaling_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"policy_type":{"type":"string","optional":true},"resource_id":{"type":"string","required":true},"scalable_dimension":{"type":"string","required":true},"service_namespace":{"type":"string","required":true}},"block_types":{"step_scaling_policy_configuration":{"nesting_mode":"list","block":{"attributes":{"adjustment_type":{"type":"string","optional":true},"cooldown":{"type":"number","optional":true},"metric_aggregation_type":{"type":"string","optional":true},"min_adjustment_magnitude":{"type":"number","optional":true}},"block_types":{"step_adjustment":{"nesting_mode":"set","block":{"attributes":{"metric_interval_lower_bound":{"type":"string","optional":true},"metric_interval_upper_bound":{"type":"string","optional":true},"scaling_adjustment":{"type":"number","required":true}}}}}},"max_items":1},"target_tracking_scaling_policy_configuration":{"nesting_mode":"list","block":{"attributes":{"disable_scale_in":{"type":"bool","optional":true},"scale_in_cooldown":{"type":"number","optional":true},"scale_out_cooldown":{"type":"number","optional":true},"target_value":{"type":"number","required":true}},"block_types":{"customized_metric_specification":{"nesting_mode":"list","block":{"attributes":{"metric_name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"statistic":{"type":"string","required":true},"unit":{"type":"string","optional":true}},"block_types":{"dimensions":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}},"max_items":1},"predefined_metric_specification":{"nesting_mode":"list","block":{"attributes":{"predefined_metric_type":{"type":"string","required":true},"resource_label":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_appautoscaling_scheduled_action":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"end_time":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"resource_id":{"type":"string","required":true},"scalable_dimension":{"type":"string","optional":true},"schedule":{"type":"string","optional":true},"service_namespace":{"type":"string","required":true},"start_time":{"type":"string","optional":true}},"block_types":{"scalable_target_action":{"nesting_mode":"list","block":{"attributes":{"max_capacity":{"type":"number","optional":true},"min_capacity":{"type":"number","optional":true}}},"max_items":1}}}},"aws_appautoscaling_target":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"max_capacity":{"type":"number","required":true},"min_capacity":{"type":"number","required":true},"resource_id":{"type":"string","required":true},"role_arn":{"type":"string","optional":true,"computed":true},"scalable_dimension":{"type":"string","required":true},"service_namespace":{"type":"string","required":true}}}},"aws_appmesh_mesh":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"block_types":{"egress_filter":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_appmesh_route":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"mesh_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"virtual_router_name":{"type":"string","required":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"attributes":{"priority":{"type":"number","optional":true}},"block_types":{"http_route":{"nesting_mode":"list","block":{"block_types":{"action":{"nesting_mode":"list","block":{"block_types":{"weighted_target":{"nesting_mode":"set","block":{"attributes":{"virtual_node":{"type":"string","required":true},"weight":{"type":"number","required":true}}},"min_items":1,"max_items":10}}},"min_items":1,"max_items":1},"match":{"nesting_mode":"list","block":{"attributes":{"method":{"type":"string","optional":true},"prefix":{"type":"string","required":true},"scheme":{"type":"string","optional":true}},"block_types":{"header":{"nesting_mode":"set","block":{"attributes":{"invert":{"type":"bool","optional":true},"name":{"type":"string","required":true}},"block_types":{"match":{"nesting_mode":"list","block":{"attributes":{"exact":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"regex":{"type":"string","optional":true},"suffix":{"type":"string","optional":true}},"block_types":{"range":{"nesting_mode":"list","block":{"attributes":{"end":{"type":"number","required":true},"start":{"type":"number","required":true}}},"max_items":1}}},"max_items":1}}},"max_items":10}}},"min_items":1,"max_items":1}}},"max_items":1},"tcp_route":{"nesting_mode":"list","block":{"block_types":{"action":{"nesting_mode":"list","block":{"block_types":{"weighted_target":{"nesting_mode":"set","block":{"attributes":{"virtual_node":{"type":"string","required":true},"weight":{"type":"number","required":true}}},"min_items":1,"max_items":10}}},"min_items":1,"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_appmesh_virtual_node":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"mesh_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"block_types":{"backend":{"nesting_mode":"set","block":{"block_types":{"virtual_service":{"nesting_mode":"list","block":{"attributes":{"virtual_service_name":{"type":"string","required":true}}},"max_items":1}}},"max_items":25},"listener":{"nesting_mode":"list","block":{"block_types":{"health_check":{"nesting_mode":"list","block":{"attributes":{"healthy_threshold":{"type":"number","required":true},"interval_millis":{"type":"number","required":true},"path":{"type":"string","optional":true},"port":{"type":"number","optional":true,"computed":true},"protocol":{"type":"string","required":true},"timeout_millis":{"type":"number","required":true},"unhealthy_threshold":{"type":"number","required":true}}},"max_items":1},"port_mapping":{"nesting_mode":"list","block":{"attributes":{"port":{"type":"number","required":true},"protocol":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"max_items":1},"logging":{"nesting_mode":"list","block":{"block_types":{"access_log":{"nesting_mode":"list","block":{"block_types":{"file":{"nesting_mode":"list","block":{"attributes":{"path":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"max_items":1},"service_discovery":{"nesting_mode":"list","block":{"block_types":{"aws_cloud_map":{"nesting_mode":"list","block":{"attributes":{"attributes":{"type":["map","string"],"optional":true},"namespace_name":{"type":"string","required":true},"service_name":{"type":"string","required":true}}},"max_items":1},"dns":{"nesting_mode":"list","block":{"attributes":{"hostname":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_appmesh_virtual_router":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"mesh_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"block_types":{"listener":{"nesting_mode":"list","block":{"block_types":{"port_mapping":{"nesting_mode":"list","block":{"attributes":{"port":{"type":"number","required":true},"protocol":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_appmesh_virtual_service":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"mesh_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"block_types":{"provider":{"nesting_mode":"list","block":{"block_types":{"virtual_node":{"nesting_mode":"list","block":{"attributes":{"virtual_node_name":{"type":"string","required":true}}},"max_items":1},"virtual_router":{"nesting_mode":"list","block":{"attributes":{"virtual_router_name":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_appsync_api_key":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"expires":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","computed":true,"sensitive":true}}}},"aws_appsync_datasource":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"service_role_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"dynamodb_config":{"nesting_mode":"list","block":{"attributes":{"region":{"type":"string","optional":true,"computed":true},"table_name":{"type":"string","required":true},"use_caller_credentials":{"type":"bool","optional":true}}},"max_items":1},"elasticsearch_config":{"nesting_mode":"list","block":{"attributes":{"endpoint":{"type":"string","required":true},"region":{"type":"string","optional":true,"computed":true}}},"max_items":1},"http_config":{"nesting_mode":"list","block":{"attributes":{"endpoint":{"type":"string","required":true}}},"max_items":1},"lambda_config":{"nesting_mode":"list","block":{"attributes":{"function_arn":{"type":"string","required":true}}},"max_items":1}}}},"aws_appsync_function":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"data_source":{"type":"string","required":true},"description":{"type":"string","optional":true},"function_id":{"type":"string","computed":true},"function_version":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"request_mapping_template":{"type":"string","required":true},"response_mapping_template":{"type":"string","required":true}}}},"aws_appsync_graphql_api":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"authentication_type":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"schema":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"uris":{"type":["map","string"],"computed":true},"xray_enabled":{"type":"bool","optional":true}},"block_types":{"additional_authentication_provider":{"nesting_mode":"list","block":{"attributes":{"authentication_type":{"type":"string","required":true}},"block_types":{"openid_connect_config":{"nesting_mode":"list","block":{"attributes":{"auth_ttl":{"type":"number","optional":true},"client_id":{"type":"string","optional":true},"iat_ttl":{"type":"number","optional":true},"issuer":{"type":"string","required":true}}},"max_items":1},"user_pool_config":{"nesting_mode":"list","block":{"attributes":{"app_id_client_regex":{"type":"string","optional":true},"aws_region":{"type":"string","optional":true,"computed":true},"user_pool_id":{"type":"string","required":true}}},"max_items":1}}}},"log_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_logs_role_arn":{"type":"string","required":true},"exclude_verbose_content":{"type":"bool","optional":true},"field_log_level":{"type":"string","required":true}}},"max_items":1},"openid_connect_config":{"nesting_mode":"list","block":{"attributes":{"auth_ttl":{"type":"number","optional":true},"client_id":{"type":"string","optional":true},"iat_ttl":{"type":"number","optional":true},"issuer":{"type":"string","required":true}}},"max_items":1},"user_pool_config":{"nesting_mode":"list","block":{"attributes":{"app_id_client_regex":{"type":"string","optional":true},"aws_region":{"type":"string","optional":true,"computed":true},"default_action":{"type":"string","required":true},"user_pool_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_appsync_resolver":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"data_source":{"type":"string","optional":true},"field":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"kind":{"type":"string","optional":true},"request_template":{"type":"string","required":true},"response_template":{"type":"string","required":true},"type":{"type":"string","required":true}},"block_types":{"caching_config":{"nesting_mode":"list","block":{"attributes":{"caching_keys":{"type":["set","string"],"optional":true},"ttl":{"type":"number","optional":true}}},"max_items":1},"pipeline_config":{"nesting_mode":"list","block":{"attributes":{"functions":{"type":["list","string"],"optional":true}}},"max_items":1}}}},"aws_athena_database":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"encryption_configuration":{"nesting_mode":"list","block":{"attributes":{"encryption_option":{"type":"string","required":true},"kms_key":{"type":"string","optional":true}}},"max_items":1}}}},"aws_athena_named_query":{"version":0,"block":{"attributes":{"database":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"query":{"type":"string","required":true},"workgroup":{"type":"string","optional":true}}}},"aws_athena_workgroup":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"state":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"configuration":{"nesting_mode":"list","block":{"attributes":{"bytes_scanned_cutoff_per_query":{"type":"number","optional":true},"enforce_workgroup_configuration":{"type":"bool","optional":true},"publish_cloudwatch_metrics_enabled":{"type":"bool","optional":true}},"block_types":{"result_configuration":{"nesting_mode":"list","block":{"attributes":{"output_location":{"type":"string","optional":true}},"block_types":{"encryption_configuration":{"nesting_mode":"list","block":{"attributes":{"encryption_option":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}},"max_items":1}}}},"aws_autoscaling_attachment":{"version":0,"block":{"attributes":{"alb_target_group_arn":{"type":"string","optional":true},"autoscaling_group_name":{"type":"string","required":true},"elb":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_autoscaling_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"default_cooldown":{"type":"number","optional":true,"computed":true},"desired_capacity":{"type":"number","optional":true,"computed":true},"enabled_metrics":{"type":["set","string"],"optional":true},"force_delete":{"type":"bool","optional":true},"health_check_grace_period":{"type":"number","optional":true},"health_check_type":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"launch_configuration":{"type":"string","optional":true},"load_balancers":{"type":["set","string"],"optional":true},"max_instance_lifetime":{"type":"number","optional":true},"max_size":{"type":"number","required":true},"metrics_granularity":{"type":"string","optional":true},"min_elb_capacity":{"type":"number","optional":true},"min_size":{"type":"number","required":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"placement_group":{"type":"string","optional":true},"protect_from_scale_in":{"type":"bool","optional":true},"service_linked_role_arn":{"type":"string","optional":true,"computed":true},"suspended_processes":{"type":["set","string"],"optional":true},"tags":{"type":["set",["map","string"]],"optional":true},"target_group_arns":{"type":["set","string"],"optional":true},"termination_policies":{"type":["list","string"],"optional":true},"vpc_zone_identifier":{"type":["set","string"],"optional":true,"computed":true},"wait_for_capacity_timeout":{"type":"string","optional":true},"wait_for_elb_capacity":{"type":"number","optional":true}},"block_types":{"initial_lifecycle_hook":{"nesting_mode":"set","block":{"attributes":{"default_result":{"type":"string","optional":true,"computed":true},"heartbeat_timeout":{"type":"number","optional":true},"lifecycle_transition":{"type":"string","required":true},"name":{"type":"string","required":true},"notification_metadata":{"type":"string","optional":true},"notification_target_arn":{"type":"string","optional":true},"role_arn":{"type":"string","optional":true}}}},"launch_template":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"version":{"type":"string","optional":true}}},"max_items":1},"mixed_instances_policy":{"nesting_mode":"list","block":{"block_types":{"instances_distribution":{"nesting_mode":"list","block":{"attributes":{"on_demand_allocation_strategy":{"type":"string","optional":true,"computed":true},"on_demand_base_capacity":{"type":"number","optional":true,"computed":true},"on_demand_percentage_above_base_capacity":{"type":"number","optional":true,"computed":true},"spot_allocation_strategy":{"type":"string","optional":true,"computed":true},"spot_instance_pools":{"type":"number","optional":true,"computed":true},"spot_max_price":{"type":"string","optional":true}}},"max_items":1},"launch_template":{"nesting_mode":"list","block":{"block_types":{"launch_template_specification":{"nesting_mode":"list","block":{"attributes":{"launch_template_id":{"type":"string","optional":true,"computed":true},"launch_template_name":{"type":"string","optional":true,"computed":true},"version":{"type":"string","optional":true}}},"min_items":1,"max_items":1},"override":{"nesting_mode":"list","block":{"attributes":{"instance_type":{"type":"string","optional":true},"weighted_capacity":{"type":"string","optional":true}}}}}},"min_items":1,"max_items":1}}},"max_items":1},"tag":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","required":true},"propagate_at_launch":{"type":"bool","required":true},"value":{"type":"string","required":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true}}}}}}},"aws_autoscaling_lifecycle_hook":{"version":0,"block":{"attributes":{"autoscaling_group_name":{"type":"string","required":true},"default_result":{"type":"string","optional":true,"computed":true},"heartbeat_timeout":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"lifecycle_transition":{"type":"string","required":true},"name":{"type":"string","required":true},"notification_metadata":{"type":"string","optional":true},"notification_target_arn":{"type":"string","optional":true},"role_arn":{"type":"string","optional":true}}}},"aws_autoscaling_notification":{"version":0,"block":{"attributes":{"group_names":{"type":["set","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"notifications":{"type":["set","string"],"required":true},"topic_arn":{"type":"string","required":true}}}},"aws_autoscaling_policy":{"version":0,"block":{"attributes":{"adjustment_type":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"autoscaling_group_name":{"type":"string","required":true},"cooldown":{"type":"number","optional":true},"estimated_instance_warmup":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"metric_aggregation_type":{"type":"string","optional":true,"computed":true},"min_adjustment_magnitude":{"type":"number","optional":true},"name":{"type":"string","required":true},"policy_type":{"type":"string","optional":true},"scaling_adjustment":{"type":"number","optional":true}},"block_types":{"step_adjustment":{"nesting_mode":"set","block":{"attributes":{"metric_interval_lower_bound":{"type":"string","optional":true},"metric_interval_upper_bound":{"type":"string","optional":true},"scaling_adjustment":{"type":"number","required":true}}}},"target_tracking_configuration":{"nesting_mode":"list","block":{"attributes":{"disable_scale_in":{"type":"bool","optional":true},"target_value":{"type":"number","required":true}},"block_types":{"customized_metric_specification":{"nesting_mode":"list","block":{"attributes":{"metric_name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"statistic":{"type":"string","required":true},"unit":{"type":"string","optional":true}},"block_types":{"metric_dimension":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}},"max_items":1},"predefined_metric_specification":{"nesting_mode":"list","block":{"attributes":{"predefined_metric_type":{"type":"string","required":true},"resource_label":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_autoscaling_schedule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"autoscaling_group_name":{"type":"string","required":true},"desired_capacity":{"type":"number","optional":true,"computed":true},"end_time":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"max_size":{"type":"number","optional":true,"computed":true},"min_size":{"type":"number","optional":true,"computed":true},"recurrence":{"type":"string","optional":true,"computed":true},"scheduled_action_name":{"type":"string","required":true},"start_time":{"type":"string","optional":true,"computed":true}}}},"aws_backup_plan":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","computed":true}},"block_types":{"rule":{"nesting_mode":"set","block":{"attributes":{"completion_window":{"type":"number","optional":true},"recovery_point_tags":{"type":["map","string"],"optional":true},"rule_name":{"type":"string","required":true},"schedule":{"type":"string","optional":true},"start_window":{"type":"number","optional":true},"target_vault_name":{"type":"string","required":true}},"block_types":{"copy_action":{"nesting_mode":"set","block":{"attributes":{"destination_vault_arn":{"type":"string","required":true}},"block_types":{"lifecycle":{"nesting_mode":"list","block":{"attributes":{"cold_storage_after":{"type":"number","optional":true},"delete_after":{"type":"number","optional":true}}},"max_items":1}}}},"lifecycle":{"nesting_mode":"list","block":{"attributes":{"cold_storage_after":{"type":"number","optional":true},"delete_after":{"type":"number","optional":true}}},"max_items":1}}},"min_items":1}}}},"aws_backup_selection":{"version":0,"block":{"attributes":{"iam_role_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"plan_id":{"type":"string","required":true},"resources":{"type":["set","string"],"optional":true}},"block_types":{"selection_tag":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","required":true},"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_backup_vault":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"recovery_points":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_batch_compute_environment":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compute_environment_name":{"type":"string","optional":true,"computed":true},"compute_environment_name_prefix":{"type":"string","optional":true},"ecs_cluster_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"service_role":{"type":"string","required":true},"state":{"type":"string","optional":true},"status":{"type":"string","computed":true},"status_reason":{"type":"string","computed":true},"type":{"type":"string","required":true}},"block_types":{"compute_resources":{"nesting_mode":"list","block":{"attributes":{"allocation_strategy":{"type":"string","optional":true},"bid_percentage":{"type":"number","optional":true},"desired_vcpus":{"type":"number","optional":true,"computed":true},"ec2_key_pair":{"type":"string","optional":true},"image_id":{"type":"string","optional":true},"instance_role":{"type":"string","required":true},"instance_type":{"type":["set","string"],"required":true},"max_vcpus":{"type":"number","required":true},"min_vcpus":{"type":"number","required":true},"security_group_ids":{"type":["set","string"],"required":true},"spot_iam_fleet_role":{"type":"string","optional":true},"subnets":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","required":true}},"block_types":{"launch_template":{"nesting_mode":"list","block":{"attributes":{"launch_template_id":{"type":"string","optional":true},"launch_template_name":{"type":"string","optional":true},"version":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_batch_job_definition":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"container_properties":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"parameters":{"type":["map","string"],"optional":true},"revision":{"type":"number","computed":true},"type":{"type":"string","required":true}},"block_types":{"retry_strategy":{"nesting_mode":"list","block":{"attributes":{"attempts":{"type":"number","optional":true}}},"max_items":1},"timeout":{"nesting_mode":"list","block":{"attributes":{"attempt_duration_seconds":{"type":"number","optional":true}}},"max_items":1}}}},"aws_batch_job_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compute_environments":{"type":["list","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"priority":{"type":"number","required":true},"state":{"type":"string","required":true}}}},"aws_budgets_budget":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"budget_type":{"type":"string","required":true},"cost_filters":{"type":["map","string"],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"limit_amount":{"type":"string","required":true},"limit_unit":{"type":"string","required":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"time_period_end":{"type":"string","optional":true},"time_period_start":{"type":"string","required":true},"time_unit":{"type":"string","required":true}},"block_types":{"cost_types":{"nesting_mode":"list","block":{"attributes":{"include_credit":{"type":"bool","optional":true},"include_discount":{"type":"bool","optional":true},"include_other_subscription":{"type":"bool","optional":true},"include_recurring":{"type":"bool","optional":true},"include_refund":{"type":"bool","optional":true},"include_subscription":{"type":"bool","optional":true},"include_support":{"type":"bool","optional":true},"include_tax":{"type":"bool","optional":true},"include_upfront":{"type":"bool","optional":true},"use_amortized":{"type":"bool","optional":true},"use_blended":{"type":"bool","optional":true}}},"max_items":1},"notification":{"nesting_mode":"set","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"notification_type":{"type":"string","required":true},"subscriber_email_addresses":{"type":["set","string"],"optional":true},"subscriber_sns_topic_arns":{"type":["set","string"],"optional":true},"threshold":{"type":"number","required":true},"threshold_type":{"type":"string","required":true}}}}}}},"aws_cloud9_environment_ec2":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"automatic_stop_time_minutes":{"type":"number","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","required":true},"name":{"type":"string","required":true},"owner_arn":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","computed":true}}}},"aws_cloudformation_stack":{"version":0,"block":{"attributes":{"capabilities":{"type":["set","string"],"optional":true},"disable_rollback":{"type":"bool","optional":true},"iam_role_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"notification_arns":{"type":["set","string"],"optional":true},"on_failure":{"type":"string","optional":true},"outputs":{"type":["map","string"],"computed":true},"parameters":{"type":["map","string"],"optional":true,"computed":true},"policy_body":{"type":"string","optional":true,"computed":true},"policy_url":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"template_body":{"type":"string","optional":true,"computed":true},"template_url":{"type":"string","optional":true},"timeout_in_minutes":{"type":"number","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cloudformation_stack_set":{"version":0,"block":{"attributes":{"administration_role_arn":{"type":"string","required":true},"arn":{"type":"string","computed":true},"capabilities":{"type":["set","string"],"optional":true},"description":{"type":"string","optional":true},"execution_role_name":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"parameters":{"type":["map","string"],"optional":true},"stack_set_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"template_body":{"type":"string","optional":true,"computed":true},"template_url":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"update":{"type":"string","optional":true}}}}}}},"aws_cloudformation_stack_set_instance":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"parameter_overrides":{"type":["map","string"],"optional":true},"region":{"type":"string","optional":true,"computed":true},"retain_stack":{"type":"bool","optional":true},"stack_id":{"type":"string","computed":true},"stack_set_name":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cloudfront_distribution":{"version":1,"block":{"attributes":{"aliases":{"type":["set","string"],"optional":true},"arn":{"type":"string","computed":true},"caller_reference":{"type":"string","computed":true},"comment":{"type":"string","optional":true},"default_root_object":{"type":"string","optional":true},"domain_name":{"type":"string","computed":true},"enabled":{"type":"bool","required":true},"etag":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"http_version":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"in_progress_validation_batches":{"type":"number","computed":true},"is_ipv6_enabled":{"type":"bool","optional":true},"last_modified_time":{"type":"string","computed":true},"price_class":{"type":"string","optional":true},"retain_on_delete":{"type":"bool","optional":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"trusted_signers":{"type":["list",["object",{"enabled":"bool","items":["list",["object",{"aws_account_number":"string","key_pair_ids":["set","string"]}]]}]],"computed":true},"wait_for_deployment":{"type":"bool","optional":true},"web_acl_id":{"type":"string","optional":true}},"block_types":{"custom_error_response":{"nesting_mode":"set","block":{"attributes":{"error_caching_min_ttl":{"type":"number","optional":true},"error_code":{"type":"number","required":true},"response_code":{"type":"number","optional":true},"response_page_path":{"type":"string","optional":true}}}},"default_cache_behavior":{"nesting_mode":"list","block":{"attributes":{"allowed_methods":{"type":["set","string"],"required":true},"cached_methods":{"type":["set","string"],"required":true},"compress":{"type":"bool","optional":true},"default_ttl":{"type":"number","optional":true},"field_level_encryption_id":{"type":"string","optional":true},"max_ttl":{"type":"number","optional":true},"min_ttl":{"type":"number","optional":true},"smooth_streaming":{"type":"bool","optional":true},"target_origin_id":{"type":"string","required":true},"trusted_signers":{"type":["list","string"],"optional":true},"viewer_protocol_policy":{"type":"string","required":true}},"block_types":{"forwarded_values":{"nesting_mode":"list","block":{"attributes":{"headers":{"type":["set","string"],"optional":true},"query_string":{"type":"bool","required":true},"query_string_cache_keys":{"type":["list","string"],"optional":true}},"block_types":{"cookies":{"nesting_mode":"list","block":{"attributes":{"forward":{"type":"string","required":true},"whitelisted_names":{"type":["set","string"],"optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"lambda_function_association":{"nesting_mode":"set","block":{"attributes":{"event_type":{"type":"string","required":true},"include_body":{"type":"bool","optional":true},"lambda_arn":{"type":"string","required":true}}},"max_items":4}}},"min_items":1,"max_items":1},"logging_config":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"include_cookies":{"type":"bool","optional":true},"prefix":{"type":"string","optional":true}}},"max_items":1},"ordered_cache_behavior":{"nesting_mode":"list","block":{"attributes":{"allowed_methods":{"type":["set","string"],"required":true},"cached_methods":{"type":["set","string"],"required":true},"compress":{"type":"bool","optional":true},"default_ttl":{"type":"number","optional":true},"field_level_encryption_id":{"type":"string","optional":true},"max_ttl":{"type":"number","optional":true},"min_ttl":{"type":"number","optional":true},"path_pattern":{"type":"string","required":true},"smooth_streaming":{"type":"bool","optional":true},"target_origin_id":{"type":"string","required":true},"trusted_signers":{"type":["list","string"],"optional":true},"viewer_protocol_policy":{"type":"string","required":true}},"block_types":{"forwarded_values":{"nesting_mode":"list","block":{"attributes":{"headers":{"type":["set","string"],"optional":true},"query_string":{"type":"bool","required":true},"query_string_cache_keys":{"type":["list","string"],"optional":true}},"block_types":{"cookies":{"nesting_mode":"list","block":{"attributes":{"forward":{"type":"string","required":true},"whitelisted_names":{"type":["set","string"],"optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"lambda_function_association":{"nesting_mode":"set","block":{"attributes":{"event_type":{"type":"string","required":true},"include_body":{"type":"bool","optional":true},"lambda_arn":{"type":"string","required":true}}},"max_items":4}}}},"origin":{"nesting_mode":"set","block":{"attributes":{"domain_name":{"type":"string","required":true},"origin_id":{"type":"string","required":true},"origin_path":{"type":"string","optional":true}},"block_types":{"custom_header":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}},"custom_origin_config":{"nesting_mode":"list","block":{"attributes":{"http_port":{"type":"number","required":true},"https_port":{"type":"number","required":true},"origin_keepalive_timeout":{"type":"number","optional":true},"origin_protocol_policy":{"type":"string","required":true},"origin_read_timeout":{"type":"number","optional":true},"origin_ssl_protocols":{"type":["set","string"],"required":true}}},"max_items":1},"s3_origin_config":{"nesting_mode":"list","block":{"attributes":{"origin_access_identity":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"origin_group":{"nesting_mode":"set","block":{"attributes":{"origin_id":{"type":"string","required":true}},"block_types":{"failover_criteria":{"nesting_mode":"list","block":{"attributes":{"status_codes":{"type":["set","number"],"required":true}}},"min_items":1,"max_items":1},"member":{"nesting_mode":"list","block":{"attributes":{"origin_id":{"type":"string","required":true}}},"min_items":2,"max_items":2}}}},"restrictions":{"nesting_mode":"list","block":{"block_types":{"geo_restriction":{"nesting_mode":"list","block":{"attributes":{"locations":{"type":["set","string"],"optional":true},"restriction_type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"viewer_certificate":{"nesting_mode":"list","block":{"attributes":{"acm_certificate_arn":{"type":"string","optional":true},"cloudfront_default_certificate":{"type":"bool","optional":true},"iam_certificate_id":{"type":"string","optional":true},"minimum_protocol_version":{"type":"string","optional":true},"ssl_support_method":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}}},"aws_cloudfront_origin_access_identity":{"version":0,"block":{"attributes":{"caller_reference":{"type":"string","computed":true},"cloudfront_access_identity_path":{"type":"string","computed":true},"comment":{"type":"string","optional":true},"etag":{"type":"string","computed":true},"iam_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"s3_canonical_user_id":{"type":"string","computed":true}}}},"aws_cloudfront_public_key":{"version":0,"block":{"attributes":{"caller_reference":{"type":"string","computed":true},"comment":{"type":"string","optional":true},"encoded_key":{"type":"string","required":true},"etag":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true}}}},"aws_cloudhsm_v2_cluster":{"version":0,"block":{"attributes":{"cluster_certificates":{"type":["list",["object",{"aws_hardware_certificate":"string","cluster_certificate":"string","cluster_csr":"string","hsm_certificate":"string","manufacturer_hardware_certificate":"string"}]],"computed":true},"cluster_id":{"type":"string","computed":true},"cluster_state":{"type":"string","computed":true},"hsm_type":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"security_group_id":{"type":"string","computed":true},"source_backup_identifier":{"type":"string","optional":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cloudhsm_v2_hsm":{"version":0,"block":{"attributes":{"availability_zone":{"type":"string","optional":true,"computed":true},"cluster_id":{"type":"string","required":true},"hsm_eni_id":{"type":"string","computed":true},"hsm_id":{"type":"string","computed":true},"hsm_state":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cloudtrail":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cloud_watch_logs_group_arn":{"type":"string","optional":true},"cloud_watch_logs_role_arn":{"type":"string","optional":true},"enable_log_file_validation":{"type":"bool","optional":true},"enable_logging":{"type":"bool","optional":true},"home_region":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"include_global_service_events":{"type":"bool","optional":true},"is_multi_region_trail":{"type":"bool","optional":true},"is_organization_trail":{"type":"bool","optional":true},"kms_key_id":{"type":"string","optional":true},"name":{"type":"string","required":true},"s3_bucket_name":{"type":"string","required":true},"s3_key_prefix":{"type":"string","optional":true},"sns_topic_name":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"event_selector":{"nesting_mode":"list","block":{"attributes":{"include_management_events":{"type":"bool","optional":true},"read_write_type":{"type":"string","optional":true}},"block_types":{"data_resource":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}},"max_items":5}}}},"aws_cloudwatch_dashboard":{"version":0,"block":{"attributes":{"dashboard_arn":{"type":"string","computed":true},"dashboard_body":{"type":"string","required":true},"dashboard_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_cloudwatch_event_permission":{"version":0,"block":{"attributes":{"action":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"principal":{"type":"string","required":true},"statement_id":{"type":"string","required":true}},"block_types":{"condition":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"type":{"type":"string","required":true},"value":{"type":"string","required":true}}},"max_items":1}}}},"aws_cloudwatch_event_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"event_pattern":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"is_enabled":{"type":"bool","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"role_arn":{"type":"string","optional":true},"schedule_expression":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_cloudwatch_event_target":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"input":{"type":"string","optional":true},"input_path":{"type":"string","optional":true},"role_arn":{"type":"string","optional":true},"rule":{"type":"string","required":true},"target_id":{"type":"string","optional":true,"computed":true}},"block_types":{"batch_target":{"nesting_mode":"list","block":{"attributes":{"array_size":{"type":"number","optional":true},"job_attempts":{"type":"number","optional":true},"job_definition":{"type":"string","required":true},"job_name":{"type":"string","required":true}}},"max_items":1},"ecs_target":{"nesting_mode":"list","block":{"attributes":{"group":{"type":"string","optional":true},"launch_type":{"type":"string","optional":true},"platform_version":{"type":"string","optional":true},"task_count":{"type":"number","optional":true},"task_definition_arn":{"type":"string","required":true}},"block_types":{"network_configuration":{"nesting_mode":"list","block":{"attributes":{"assign_public_ip":{"type":"bool","optional":true},"security_groups":{"type":["set","string"],"optional":true},"subnets":{"type":["set","string"],"required":true}}},"max_items":1}}},"max_items":1},"input_transformer":{"nesting_mode":"list","block":{"attributes":{"input_paths":{"type":["map","string"],"optional":true},"input_template":{"type":"string","required":true}}},"max_items":1},"kinesis_target":{"nesting_mode":"list","block":{"attributes":{"partition_key_path":{"type":"string","optional":true}}},"max_items":1},"run_command_targets":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"max_items":5},"sqs_target":{"nesting_mode":"list","block":{"attributes":{"message_group_id":{"type":"string","optional":true}}},"max_items":1}}}},"aws_cloudwatch_log_destination":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"target_arn":{"type":"string","required":true}}}},"aws_cloudwatch_log_destination_policy":{"version":0,"block":{"attributes":{"access_policy":{"type":"string","required":true},"destination_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_cloudwatch_log_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"retention_in_days":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_cloudwatch_log_metric_filter":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","required":true},"name":{"type":"string","required":true},"pattern":{"type":"string","required":true}},"block_types":{"metric_transformation":{"nesting_mode":"list","block":{"attributes":{"default_value":{"type":"string","optional":true},"name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"value":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_cloudwatch_log_resource_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy_document":{"type":"string","required":true},"policy_name":{"type":"string","required":true}}}},"aws_cloudwatch_log_stream":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","required":true},"name":{"type":"string","required":true}}}},"aws_cloudwatch_log_subscription_filter":{"version":0,"block":{"attributes":{"destination_arn":{"type":"string","required":true},"distribution":{"type":"string","optional":true},"filter_pattern":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","required":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","optional":true,"computed":true}}}},"aws_cloudwatch_metric_alarm":{"version":1,"block":{"attributes":{"actions_enabled":{"type":"bool","optional":true},"alarm_actions":{"type":["set","string"],"optional":true},"alarm_description":{"type":"string","optional":true},"alarm_name":{"type":"string","required":true},"arn":{"type":"string","computed":true},"comparison_operator":{"type":"string","required":true},"datapoints_to_alarm":{"type":"number","optional":true},"dimensions":{"type":["map","string"],"optional":true},"evaluate_low_sample_count_percentiles":{"type":"string","optional":true,"computed":true},"evaluation_periods":{"type":"number","required":true},"extended_statistic":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"insufficient_data_actions":{"type":["set","string"],"optional":true},"metric_name":{"type":"string","optional":true},"namespace":{"type":"string","optional":true},"ok_actions":{"type":["set","string"],"optional":true},"period":{"type":"number","optional":true},"statistic":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"threshold":{"type":"number","optional":true},"threshold_metric_id":{"type":"string","optional":true},"treat_missing_data":{"type":"string","optional":true},"unit":{"type":"string","optional":true}},"block_types":{"metric_query":{"nesting_mode":"set","block":{"attributes":{"expression":{"type":"string","optional":true},"id":{"type":"string","required":true},"label":{"type":"string","optional":true},"return_data":{"type":"bool","optional":true}},"block_types":{"metric":{"nesting_mode":"list","block":{"attributes":{"dimensions":{"type":["map","string"],"optional":true},"metric_name":{"type":"string","required":true},"namespace":{"type":"string","optional":true},"period":{"type":"number","required":true},"stat":{"type":"string","required":true},"unit":{"type":"string","optional":true}}},"max_items":1}}}}}}},"aws_codebuild_project":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"badge_enabled":{"type":"bool","optional":true},"badge_url":{"type":"string","computed":true},"build_timeout":{"type":"number","optional":true},"description":{"type":"string","optional":true,"computed":true},"encryption_key":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"queued_timeout":{"type":"number","optional":true},"service_role":{"type":"string","required":true},"source_version":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"artifacts":{"nesting_mode":"list","block":{"attributes":{"artifact_identifier":{"type":"string","optional":true},"encryption_disabled":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"name":{"type":"string","optional":true},"namespace_type":{"type":"string","optional":true},"override_artifact_name":{"type":"bool","optional":true},"packaging":{"type":"string","optional":true},"path":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1},"cache":{"nesting_mode":"list","block":{"attributes":{"location":{"type":"string","optional":true},"modes":{"type":["list","string"],"optional":true},"type":{"type":"string","optional":true}}},"max_items":1},"environment":{"nesting_mode":"list","block":{"attributes":{"certificate":{"type":"string","optional":true},"compute_type":{"type":"string","required":true},"image":{"type":"string","required":true},"image_pull_credentials_type":{"type":"string","optional":true},"privileged_mode":{"type":"bool","optional":true},"type":{"type":"string","required":true}},"block_types":{"environment_variable":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true},"type":{"type":"string","optional":true},"value":{"type":"string","required":true}}}},"registry_credential":{"nesting_mode":"list","block":{"attributes":{"credential":{"type":"string","required":true},"credential_provider":{"type":"string","required":true}}},"max_items":1}}},"min_items":1,"max_items":1},"logs_config":{"nesting_mode":"list","block":{"block_types":{"cloudwatch_logs":{"nesting_mode":"list","block":{"attributes":{"group_name":{"type":"string","optional":true},"status":{"type":"string","optional":true},"stream_name":{"type":"string","optional":true}}},"max_items":1},"s3_logs":{"nesting_mode":"list","block":{"attributes":{"encryption_disabled":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"status":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"secondary_artifacts":{"nesting_mode":"set","block":{"attributes":{"artifact_identifier":{"type":"string","required":true},"encryption_disabled":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"name":{"type":"string","optional":true},"namespace_type":{"type":"string","optional":true},"override_artifact_name":{"type":"bool","optional":true},"packaging":{"type":"string","optional":true},"path":{"type":"string","optional":true},"type":{"type":"string","required":true}}}},"secondary_sources":{"nesting_mode":"set","block":{"attributes":{"buildspec":{"type":"string","optional":true},"git_clone_depth":{"type":"number","optional":true},"insecure_ssl":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"report_build_status":{"type":"bool","optional":true},"source_identifier":{"type":"string","required":true},"type":{"type":"string","required":true}},"block_types":{"auth":{"nesting_mode":"list","block":{"attributes":{"resource":{"type":"string","optional":true,"sensitive":true},"type":{"type":"string","required":true}}},"max_items":1},"git_submodules_config":{"nesting_mode":"list","block":{"attributes":{"fetch_submodules":{"type":"bool","required":true}}},"max_items":1}}}},"source":{"nesting_mode":"list","block":{"attributes":{"buildspec":{"type":"string","optional":true},"git_clone_depth":{"type":"number","optional":true},"insecure_ssl":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"report_build_status":{"type":"bool","optional":true},"type":{"type":"string","required":true}},"block_types":{"auth":{"nesting_mode":"list","block":{"attributes":{"resource":{"type":"string","optional":true,"sensitive":true},"type":{"type":"string","required":true}}},"max_items":1},"git_submodules_config":{"nesting_mode":"list","block":{"attributes":{"fetch_submodules":{"type":"bool","required":true}}},"max_items":1}}},"min_items":1,"max_items":1},"vpc_config":{"nesting_mode":"list","block":{"attributes":{"security_group_ids":{"type":["set","string"],"required":true},"subnets":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_codebuild_source_credential":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auth_type":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"server_type":{"type":"string","required":true},"token":{"type":"string","required":true,"sensitive":true},"user_name":{"type":"string","optional":true}}}},"aws_codebuild_webhook":{"version":0,"block":{"attributes":{"branch_filter":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"payload_url":{"type":"string","computed":true},"project_name":{"type":"string","required":true},"secret":{"type":"string","computed":true,"sensitive":true},"url":{"type":"string","computed":true}},"block_types":{"filter_group":{"nesting_mode":"set","block":{"block_types":{"filter":{"nesting_mode":"list","block":{"attributes":{"exclude_matched_pattern":{"type":"bool","optional":true},"pattern":{"type":"string","required":true},"type":{"type":"string","required":true}}}}}}}}}},"aws_codecommit_repository":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"clone_url_http":{"type":"string","computed":true},"clone_url_ssh":{"type":"string","computed":true},"default_branch":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"repository_id":{"type":"string","computed":true},"repository_name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_codecommit_trigger":{"version":0,"block":{"attributes":{"configuration_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"repository_name":{"type":"string","required":true}},"block_types":{"trigger":{"nesting_mode":"set","block":{"attributes":{"branches":{"type":["list","string"],"optional":true},"custom_data":{"type":"string","optional":true},"destination_arn":{"type":"string","required":true},"events":{"type":["list","string"],"required":true},"name":{"type":"string","required":true}}},"min_items":1,"max_items":10}}}},"aws_codedeploy_app":{"version":0,"block":{"attributes":{"compute_platform":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"unique_id":{"type":"string","optional":true,"computed":true}}}},"aws_codedeploy_deployment_config":{"version":0,"block":{"attributes":{"compute_platform":{"type":"string","optional":true},"deployment_config_id":{"type":"string","computed":true},"deployment_config_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}},"block_types":{"minimum_healthy_hosts":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","optional":true},"value":{"type":"number","optional":true}}},"max_items":1},"traffic_routing_config":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","optional":true}},"block_types":{"time_based_canary":{"nesting_mode":"list","block":{"attributes":{"interval":{"type":"number","optional":true},"percentage":{"type":"number","optional":true}}},"max_items":1},"time_based_linear":{"nesting_mode":"list","block":{"attributes":{"interval":{"type":"number","optional":true},"percentage":{"type":"number","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_codedeploy_deployment_group":{"version":0,"block":{"attributes":{"app_name":{"type":"string","required":true},"autoscaling_groups":{"type":["set","string"],"optional":true},"deployment_config_name":{"type":"string","optional":true},"deployment_group_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"service_role_arn":{"type":"string","required":true}},"block_types":{"alarm_configuration":{"nesting_mode":"list","block":{"attributes":{"alarms":{"type":["set","string"],"optional":true},"enabled":{"type":"bool","optional":true},"ignore_poll_alarm_failure":{"type":"bool","optional":true}}},"max_items":1},"auto_rollback_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"events":{"type":["set","string"],"optional":true}}},"max_items":1},"blue_green_deployment_config":{"nesting_mode":"list","block":{"block_types":{"deployment_ready_option":{"nesting_mode":"list","block":{"attributes":{"action_on_timeout":{"type":"string","optional":true},"wait_time_in_minutes":{"type":"number","optional":true}}},"max_items":1},"green_fleet_provisioning_option":{"nesting_mode":"list","block":{"attributes":{"action":{"type":"string","optional":true}}},"max_items":1},"terminate_blue_instances_on_deployment_success":{"nesting_mode":"list","block":{"attributes":{"action":{"type":"string","optional":true},"termination_wait_time_in_minutes":{"type":"number","optional":true}}},"max_items":1}}},"max_items":1},"deployment_style":{"nesting_mode":"list","block":{"attributes":{"deployment_option":{"type":"string","optional":true},"deployment_type":{"type":"string","optional":true}}},"max_items":1},"ec2_tag_filter":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"type":{"type":"string","optional":true},"value":{"type":"string","optional":true}}}},"ec2_tag_set":{"nesting_mode":"set","block":{"block_types":{"ec2_tag_filter":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"type":{"type":"string","optional":true},"value":{"type":"string","optional":true}}}}}}},"ecs_service":{"nesting_mode":"list","block":{"attributes":{"cluster_name":{"type":"string","required":true},"service_name":{"type":"string","required":true}}},"max_items":1},"load_balancer_info":{"nesting_mode":"list","block":{"block_types":{"elb_info":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","optional":true}}}},"target_group_info":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","optional":true}}}},"target_group_pair_info":{"nesting_mode":"list","block":{"block_types":{"prod_traffic_route":{"nesting_mode":"list","block":{"attributes":{"listener_arns":{"type":["set","string"],"required":true}}},"min_items":1,"max_items":1},"target_group":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"min_items":1,"max_items":2},"test_traffic_route":{"nesting_mode":"list","block":{"attributes":{"listener_arns":{"type":["set","string"],"required":true}}},"max_items":1}}},"max_items":1}}},"max_items":1},"on_premises_instance_tag_filter":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"type":{"type":"string","optional":true},"value":{"type":"string","optional":true}}}},"trigger_configuration":{"nesting_mode":"set","block":{"attributes":{"trigger_events":{"type":["set","string"],"required":true},"trigger_name":{"type":"string","required":true},"trigger_target_arn":{"type":"string","required":true}}}}}}},"aws_codepipeline":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"artifact_store":{"nesting_mode":"set","block":{"attributes":{"location":{"type":"string","required":true},"region":{"type":"string","optional":true,"computed":true},"type":{"type":"string","required":true}},"block_types":{"encryption_key":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","required":true},"type":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"stage":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"category":{"type":"string","required":true},"configuration":{"type":["map","string"],"optional":true},"input_artifacts":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"namespace":{"type":"string","optional":true},"output_artifacts":{"type":["list","string"],"optional":true},"owner":{"type":"string","required":true},"provider":{"type":"string","required":true},"region":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","optional":true},"run_order":{"type":"number","optional":true,"computed":true},"version":{"type":"string","required":true}}},"min_items":1}}},"min_items":2}}}},"aws_codepipeline_webhook":{"version":0,"block":{"attributes":{"authentication":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"target_action":{"type":"string","required":true},"target_pipeline":{"type":"string","required":true},"url":{"type":"string","computed":true}},"block_types":{"authentication_configuration":{"nesting_mode":"list","block":{"attributes":{"allowed_ip_range":{"type":"string","optional":true},"secret_token":{"type":"string","optional":true,"sensitive":true}}},"max_items":1},"filter":{"nesting_mode":"set","block":{"attributes":{"json_path":{"type":"string","required":true},"match_equals":{"type":"string","required":true}}},"min_items":1}}}},"aws_codestarnotifications_notification_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"detail_type":{"type":"string","required":true},"event_type_ids":{"type":["set","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"resource":{"type":"string","required":true},"status":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"target":{"nesting_mode":"set","block":{"attributes":{"address":{"type":"string","required":true},"status":{"type":"string","computed":true},"type":{"type":"string","optional":true}}},"max_items":10}}}},"aws_cognito_identity_pool":{"version":0,"block":{"attributes":{"allow_unauthenticated_identities":{"type":"bool","optional":true},"arn":{"type":"string","computed":true},"developer_provider_name":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"identity_pool_name":{"type":"string","required":true},"openid_connect_provider_arns":{"type":["list","string"],"optional":true},"saml_provider_arns":{"type":["list","string"],"optional":true},"supported_login_providers":{"type":["map","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"cognito_identity_providers":{"nesting_mode":"set","block":{"attributes":{"client_id":{"type":"string","optional":true},"provider_name":{"type":"string","optional":true},"server_side_token_check":{"type":"bool","optional":true}}}}}}},"aws_cognito_identity_pool_roles_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"identity_pool_id":{"type":"string","required":true},"roles":{"type":["map","string"],"required":true}},"block_types":{"role_mapping":{"nesting_mode":"set","block":{"attributes":{"ambiguous_role_resolution":{"type":"string","optional":true},"identity_provider":{"type":"string","required":true},"type":{"type":"string","required":true}},"block_types":{"mapping_rule":{"nesting_mode":"list","block":{"attributes":{"claim":{"type":"string","required":true},"match_type":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"value":{"type":"string","required":true}}},"max_items":25}}}}}}},"aws_cognito_identity_provider":{"version":0,"block":{"attributes":{"attribute_mapping":{"type":["map","string"],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"idp_identifiers":{"type":["list","string"],"optional":true},"provider_details":{"type":["map","string"],"required":true},"provider_name":{"type":"string","required":true},"provider_type":{"type":"string","required":true},"user_pool_id":{"type":"string","required":true}}}},"aws_cognito_resource_server":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","required":true},"name":{"type":"string","required":true},"scope_identifiers":{"type":["list","string"],"computed":true},"user_pool_id":{"type":"string","required":true}},"block_types":{"scope":{"nesting_mode":"set","block":{"attributes":{"scope_description":{"type":"string","required":true},"scope_name":{"type":"string","required":true}}},"max_items":100}}}},"aws_cognito_user_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"precedence":{"type":"number","optional":true},"role_arn":{"type":"string","optional":true},"user_pool_id":{"type":"string","required":true}}}},"aws_cognito_user_pool":{"version":0,"block":{"attributes":{"alias_attributes":{"type":["set","string"],"optional":true},"arn":{"type":"string","computed":true},"auto_verified_attributes":{"type":["set","string"],"optional":true},"creation_date":{"type":"string","computed":true},"email_verification_message":{"type":"string","optional":true,"computed":true},"email_verification_subject":{"type":"string","optional":true,"computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_modified_date":{"type":"string","computed":true},"mfa_configuration":{"type":"string","optional":true},"name":{"type":"string","required":true},"sms_authentication_message":{"type":"string","optional":true},"sms_verification_message":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"username_attributes":{"type":["list","string"],"optional":true}},"block_types":{"admin_create_user_config":{"nesting_mode":"list","block":{"attributes":{"allow_admin_create_user_only":{"type":"bool","optional":true}},"block_types":{"invite_message_template":{"nesting_mode":"list","block":{"attributes":{"email_message":{"type":"string","optional":true},"email_subject":{"type":"string","optional":true},"sms_message":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"device_configuration":{"nesting_mode":"list","block":{"attributes":{"challenge_required_on_new_device":{"type":"bool","optional":true},"device_only_remembered_on_user_prompt":{"type":"bool","optional":true}}},"max_items":1},"email_configuration":{"nesting_mode":"list","block":{"attributes":{"email_sending_account":{"type":"string","optional":true},"from_email_address":{"type":"string","optional":true},"reply_to_email_address":{"type":"string","optional":true},"source_arn":{"type":"string","optional":true}}},"max_items":1},"lambda_config":{"nesting_mode":"list","block":{"attributes":{"create_auth_challenge":{"type":"string","optional":true},"custom_message":{"type":"string","optional":true},"define_auth_challenge":{"type":"string","optional":true},"post_authentication":{"type":"string","optional":true},"post_confirmation":{"type":"string","optional":true},"pre_authentication":{"type":"string","optional":true},"pre_sign_up":{"type":"string","optional":true},"pre_token_generation":{"type":"string","optional":true},"user_migration":{"type":"string","optional":true},"verify_auth_challenge_response":{"type":"string","optional":true}}},"max_items":1},"password_policy":{"nesting_mode":"list","block":{"attributes":{"minimum_length":{"type":"number","optional":true},"require_lowercase":{"type":"bool","optional":true},"require_numbers":{"type":"bool","optional":true},"require_symbols":{"type":"bool","optional":true},"require_uppercase":{"type":"bool","optional":true},"temporary_password_validity_days":{"type":"number","optional":true}}},"max_items":1},"schema":{"nesting_mode":"set","block":{"attributes":{"attribute_data_type":{"type":"string","required":true},"developer_only_attribute":{"type":"bool","optional":true},"mutable":{"type":"bool","optional":true},"name":{"type":"string","required":true},"required":{"type":"bool","optional":true}},"block_types":{"number_attribute_constraints":{"nesting_mode":"list","block":{"attributes":{"max_value":{"type":"string","optional":true},"min_value":{"type":"string","optional":true}}},"max_items":1},"string_attribute_constraints":{"nesting_mode":"list","block":{"attributes":{"max_length":{"type":"string","optional":true},"min_length":{"type":"string","optional":true}}},"max_items":1}}},"max_items":50},"sms_configuration":{"nesting_mode":"list","block":{"attributes":{"external_id":{"type":"string","required":true},"sns_caller_arn":{"type":"string","required":true}}},"max_items":1},"software_token_mfa_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true}}},"max_items":1},"user_pool_add_ons":{"nesting_mode":"list","block":{"attributes":{"advanced_security_mode":{"type":"string","required":true}}},"max_items":1},"username_configuration":{"nesting_mode":"list","block":{"attributes":{"case_sensitive":{"type":"bool","required":true}}},"max_items":1},"verification_message_template":{"nesting_mode":"list","block":{"attributes":{"default_email_option":{"type":"string","optional":true},"email_message":{"type":"string","optional":true,"computed":true},"email_message_by_link":{"type":"string","optional":true,"computed":true},"email_subject":{"type":"string","optional":true,"computed":true},"email_subject_by_link":{"type":"string","optional":true,"computed":true},"sms_message":{"type":"string","optional":true,"computed":true}}},"max_items":1}}}},"aws_cognito_user_pool_client":{"version":0,"block":{"attributes":{"allowed_oauth_flows":{"type":["set","string"],"optional":true},"allowed_oauth_flows_user_pool_client":{"type":"bool","optional":true},"allowed_oauth_scopes":{"type":["set","string"],"optional":true},"callback_urls":{"type":["set","string"],"optional":true},"client_secret":{"type":"string","computed":true,"sensitive":true},"default_redirect_uri":{"type":"string","optional":true},"explicit_auth_flows":{"type":["set","string"],"optional":true},"generate_secret":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"logout_urls":{"type":["set","string"],"optional":true},"name":{"type":"string","required":true},"prevent_user_existence_errors":{"type":"string","optional":true,"computed":true},"read_attributes":{"type":["set","string"],"optional":true},"refresh_token_validity":{"type":"number","optional":true},"supported_identity_providers":{"type":["set","string"],"optional":true},"user_pool_id":{"type":"string","required":true},"write_attributes":{"type":["set","string"],"optional":true}},"block_types":{"analytics_configuration":{"nesting_mode":"list","block":{"attributes":{"application_id":{"type":"string","required":true},"external_id":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"user_data_shared":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_cognito_user_pool_domain":{"version":0,"block":{"attributes":{"aws_account_id":{"type":"string","computed":true},"certificate_arn":{"type":"string","optional":true},"cloudfront_distribution_arn":{"type":"string","computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"s3_bucket":{"type":"string","computed":true},"user_pool_id":{"type":"string","required":true},"version":{"type":"string","computed":true}}}},"aws_config_aggregate_authorization":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_config_config_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"input_parameters":{"type":"string","optional":true},"maximum_execution_frequency":{"type":"string","optional":true},"name":{"type":"string","required":true},"rule_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"scope":{"nesting_mode":"list","block":{"attributes":{"compliance_resource_id":{"type":"string","optional":true},"compliance_resource_types":{"type":["set","string"],"optional":true},"tag_key":{"type":"string","optional":true},"tag_value":{"type":"string","optional":true}}},"max_items":1},"source":{"nesting_mode":"list","block":{"attributes":{"owner":{"type":"string","required":true},"source_identifier":{"type":"string","required":true}},"block_types":{"source_detail":{"nesting_mode":"set","block":{"attributes":{"event_source":{"type":"string","optional":true},"maximum_execution_frequency":{"type":"string","optional":true},"message_type":{"type":"string","optional":true}}},"max_items":25}}},"min_items":1,"max_items":1}}}},"aws_config_configuration_aggregator":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"account_aggregation_source":{"nesting_mode":"list","block":{"attributes":{"account_ids":{"type":["list","string"],"required":true},"all_regions":{"type":"bool","optional":true},"regions":{"type":["list","string"],"optional":true}}},"max_items":1},"organization_aggregation_source":{"nesting_mode":"list","block":{"attributes":{"all_regions":{"type":"bool","optional":true},"regions":{"type":["list","string"],"optional":true},"role_arn":{"type":"string","required":true}}},"max_items":1}}}},"aws_config_configuration_recorder":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}},"block_types":{"recording_group":{"nesting_mode":"list","block":{"attributes":{"all_supported":{"type":"bool","optional":true},"include_global_resource_types":{"type":"bool","optional":true},"resource_types":{"type":["set","string"],"optional":true}}},"max_items":1}}}},"aws_config_configuration_recorder_status":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"is_enabled":{"type":"bool","required":true},"name":{"type":"string","required":true}}}},"aws_config_delivery_channel":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"s3_bucket_name":{"type":"string","required":true},"s3_key_prefix":{"type":"string","optional":true},"sns_topic_arn":{"type":"string","optional":true}},"block_types":{"snapshot_delivery_properties":{"nesting_mode":"list","block":{"attributes":{"delivery_frequency":{"type":"string","optional":true}}},"max_items":1}}}},"aws_config_organization_custom_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"excluded_accounts":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"input_parameters":{"type":"string","optional":true},"lambda_function_arn":{"type":"string","required":true},"maximum_execution_frequency":{"type":"string","optional":true},"name":{"type":"string","required":true},"resource_id_scope":{"type":"string","optional":true},"resource_types_scope":{"type":["set","string"],"optional":true},"tag_key_scope":{"type":"string","optional":true},"tag_value_scope":{"type":"string","optional":true},"trigger_types":{"type":["set","string"],"required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_config_organization_managed_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"excluded_accounts":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"input_parameters":{"type":"string","optional":true},"maximum_execution_frequency":{"type":"string","optional":true},"name":{"type":"string","required":true},"resource_id_scope":{"type":"string","optional":true},"resource_types_scope":{"type":["set","string"],"optional":true},"rule_identifier":{"type":"string","required":true},"tag_key_scope":{"type":"string","optional":true},"tag_value_scope":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cur_report_definition":{"version":0,"block":{"attributes":{"additional_artifacts":{"type":["set","string"],"optional":true},"additional_schema_elements":{"type":["set","string"],"required":true},"compression":{"type":"string","required":true},"format":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"report_name":{"type":"string","required":true},"s3_bucket":{"type":"string","required":true},"s3_prefix":{"type":"string","optional":true},"s3_region":{"type":"string","required":true},"time_unit":{"type":"string","required":true}}}},"aws_customer_gateway":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bgp_asn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","required":true}}}},"aws_datapipeline_pipeline":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_datasync_agent":{"version":0,"block":{"attributes":{"activation_key":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_datasync_location_efs":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"efs_file_system_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"subdirectory":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"uri":{"type":"string","computed":true}},"block_types":{"ec2_config":{"nesting_mode":"list","block":{"attributes":{"security_group_arns":{"type":["set","string"],"required":true},"subnet_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_datasync_location_nfs":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"server_hostname":{"type":"string","required":true},"subdirectory":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"uri":{"type":"string","computed":true}},"block_types":{"on_prem_config":{"nesting_mode":"list","block":{"attributes":{"agent_arns":{"type":["set","string"],"required":true}}},"min_items":1,"max_items":1}}}},"aws_datasync_location_s3":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"s3_bucket_arn":{"type":"string","required":true},"subdirectory":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"uri":{"type":"string","computed":true}},"block_types":{"s3_config":{"nesting_mode":"list","block":{"attributes":{"bucket_access_role_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_datasync_location_smb":{"version":0,"block":{"attributes":{"agent_arns":{"type":["set","string"],"required":true},"arn":{"type":"string","computed":true},"domain":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"password":{"type":"string","required":true,"sensitive":true},"server_hostname":{"type":"string","required":true},"subdirectory":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"uri":{"type":"string","computed":true},"user":{"type":"string","required":true}},"block_types":{"mount_options":{"nesting_mode":"list","block":{"attributes":{"version":{"type":"string","optional":true}}},"max_items":1}}}},"aws_datasync_task":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cloudwatch_log_group_arn":{"type":"string","optional":true},"destination_location_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"source_location_arn":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"options":{"nesting_mode":"list","block":{"attributes":{"atime":{"type":"string","optional":true},"bytes_per_second":{"type":"number","optional":true},"gid":{"type":"string","optional":true},"mtime":{"type":"string","optional":true},"posix_permissions":{"type":"string","optional":true},"preserve_deleted_files":{"type":"string","optional":true},"preserve_devices":{"type":"string","optional":true},"uid":{"type":"string","optional":true},"verify_mode":{"type":"string","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_dax_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true},"cluster_address":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"configuration_endpoint":{"type":"string","computed":true},"description":{"type":"string","optional":true},"iam_role_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"maintenance_window":{"type":"string","optional":true,"computed":true},"node_type":{"type":"string","required":true},"nodes":{"type":["list",["object",{"address":"string","availability_zone":"string","id":"string","port":"number"}]],"computed":true},"notification_topic_arn":{"type":"string","optional":true},"parameter_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","computed":true},"replication_factor":{"type":"number","required":true},"security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"subnet_group_name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"server_side_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dax_parameter_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_dax_subnet_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","computed":true}}}},"aws_db_cluster_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zones":{"type":["list","string"],"computed":true},"db_cluster_identifier":{"type":"string","required":true},"db_cluster_snapshot_arn":{"type":"string","computed":true},"db_cluster_snapshot_identifier":{"type":"string","required":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_type":{"type":"string","computed":true},"source_db_cluster_snapshot_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_db_event_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_aws_id":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"event_categories":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"sns_topic":{"type":"string","required":true},"source_ids":{"type":["set","string"],"optional":true},"source_type":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_db_instance":{"version":1,"block":{"attributes":{"address":{"type":"string","computed":true},"allocated_storage":{"type":"number","optional":true,"computed":true},"allow_major_version_upgrade":{"type":"bool","optional":true},"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"backup_retention_period":{"type":"number","optional":true,"computed":true},"backup_window":{"type":"string","optional":true,"computed":true},"ca_cert_identifier":{"type":"string","optional":true,"computed":true},"character_set_name":{"type":"string","optional":true,"computed":true},"copy_tags_to_snapshot":{"type":"bool","optional":true},"db_subnet_group_name":{"type":"string","optional":true,"computed":true},"delete_automated_backups":{"type":"bool","optional":true},"deletion_protection":{"type":"bool","optional":true},"domain":{"type":"string","optional":true},"domain_iam_role_name":{"type":"string","optional":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"optional":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true,"computed":true},"engine_version":{"type":"string","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"hosted_zone_id":{"type":"string","computed":true},"iam_database_authentication_enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"instance_class":{"type":"string","required":true},"iops":{"type":"number","optional":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"license_model":{"type":"string","optional":true,"computed":true},"maintenance_window":{"type":"string","optional":true,"computed":true},"max_allocated_storage":{"type":"number","optional":true},"monitoring_interval":{"type":"number","optional":true},"monitoring_role_arn":{"type":"string","optional":true,"computed":true},"multi_az":{"type":"bool","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"option_group_name":{"type":"string","optional":true,"computed":true},"parameter_group_name":{"type":"string","optional":true,"computed":true},"password":{"type":"string","optional":true,"sensitive":true},"performance_insights_enabled":{"type":"bool","optional":true},"performance_insights_kms_key_id":{"type":"string","optional":true,"computed":true},"performance_insights_retention_period":{"type":"number","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"publicly_accessible":{"type":"bool","optional":true},"replicas":{"type":["list","string"],"computed":true},"replicate_source_db":{"type":"string","optional":true},"resource_id":{"type":"string","computed":true},"security_group_names":{"type":["set","string"],"optional":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_identifier":{"type":"string","optional":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","optional":true},"storage_type":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"timezone":{"type":"string","optional":true,"computed":true},"username":{"type":"string","optional":true,"computed":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"s3_import":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","required":true},"bucket_prefix":{"type":"string","optional":true},"ingestion_role":{"type":"string","required":true},"source_engine":{"type":"string","required":true},"source_engine_version":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_db_instance_role_association":{"version":0,"block":{"attributes":{"db_instance_identifier":{"type":"string","required":true},"feature_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","required":true}}}},"aws_db_option_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"engine_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"major_engine_version":{"type":"string","required":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"option_group_description":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"option":{"nesting_mode":"set","block":{"attributes":{"db_security_group_memberships":{"type":["set","string"],"optional":true},"option_name":{"type":"string","required":true},"port":{"type":"number","optional":true},"version":{"type":"string","optional":true},"vpc_security_group_memberships":{"type":["set","string"],"optional":true}},"block_types":{"option_settings":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true}}}}}}},"aws_db_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_db_security_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"ingress":{"nesting_mode":"set","block":{"attributes":{"cidr":{"type":"string","optional":true},"security_group_id":{"type":"string","optional":true,"computed":true},"security_group_name":{"type":"string","optional":true,"computed":true},"security_group_owner_id":{"type":"string","optional":true,"computed":true}}},"min_items":1}}}},"aws_db_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zone":{"type":"string","computed":true},"db_instance_identifier":{"type":"string","required":true},"db_snapshot_arn":{"type":"string","computed":true},"db_snapshot_identifier":{"type":"string","required":true},"encrypted":{"type":"bool","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"iops":{"type":"number","computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"option_group_name":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_type":{"type":"string","computed":true},"source_db_snapshot_identifier":{"type":"string","computed":true},"source_region":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_type":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"read":{"type":"string","optional":true}}}}}}},"aws_db_subnet_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_default_network_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"default_network_acl_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"egress":{"nesting_mode":"set","block":{"attributes":{"action":{"type":"string","required":true},"cidr_block":{"type":"string","optional":true},"from_port":{"type":"number","required":true},"icmp_code":{"type":"number","optional":true},"icmp_type":{"type":"number","optional":true},"ipv6_cidr_block":{"type":"string","optional":true},"protocol":{"type":"string","required":true},"rule_no":{"type":"number","required":true},"to_port":{"type":"number","required":true}}}},"ingress":{"nesting_mode":"set","block":{"attributes":{"action":{"type":"string","required":true},"cidr_block":{"type":"string","optional":true},"from_port":{"type":"number","required":true},"icmp_code":{"type":"number","optional":true},"icmp_type":{"type":"number","optional":true},"ipv6_cidr_block":{"type":"string","optional":true},"protocol":{"type":"string","required":true},"rule_no":{"type":"number","required":true},"to_port":{"type":"number","required":true}}}}}}},"aws_default_route_table":{"version":0,"block":{"attributes":{"default_route_table_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"propagating_vgws":{"type":["set","string"],"optional":true},"route":{"type":["set",["object",{"cidr_block":"string","egress_only_gateway_id":"string","gateway_id":"string","instance_id":"string","ipv6_cidr_block":"string","nat_gateway_id":"string","network_interface_id":"string","transit_gateway_id":"string","vpc_peering_connection_id":"string"}]],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}}}},"aws_default_security_group":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"egress":{"type":["set",["object",{"cidr_blocks":["list","string"],"description":"string","from_port":"number","ipv6_cidr_blocks":["list","string"],"prefix_list_ids":["list","string"],"protocol":"string","security_groups":["set","string"],"self":"bool","to_port":"number"}]],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ingress":{"type":["set",["object",{"cidr_blocks":["list","string"],"description":"string","from_port":"number","ipv6_cidr_blocks":["list","string"],"prefix_list_ids":["list","string"],"protocol":"string","security_groups":["set","string"],"self":"bool","to_port":"number"}]],"optional":true,"computed":true},"name":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"revoke_rules_on_delete":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","optional":true,"computed":true}}}},"aws_default_subnet":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_ipv6_address_on_creation":{"type":"bool","computed":true},"availability_zone":{"type":"string","required":true},"availability_zone_id":{"type":"string","computed":true},"cidr_block":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block":{"type":"string","computed":true},"ipv6_cidr_block_association_id":{"type":"string","computed":true},"map_public_ip_on_launch":{"type":"bool","optional":true,"computed":true},"outpost_arn":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_default_vpc":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_generated_ipv6_cidr_block":{"type":"bool","computed":true},"cidr_block":{"type":"string","computed":true},"default_network_acl_id":{"type":"string","computed":true},"default_route_table_id":{"type":"string","computed":true},"default_security_group_id":{"type":"string","computed":true},"dhcp_options_id":{"type":"string","computed":true},"enable_classiclink":{"type":"bool","optional":true,"computed":true},"enable_classiclink_dns_support":{"type":"bool","optional":true,"computed":true},"enable_dns_hostnames":{"type":"bool","optional":true,"computed":true},"enable_dns_support":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_tenancy":{"type":"string","computed":true},"ipv6_association_id":{"type":"string","computed":true},"ipv6_cidr_block":{"type":"string","computed":true},"main_route_table_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_default_vpc_dhcp_options":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","computed":true},"domain_name_servers":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"netbios_name_servers":{"type":["list","string"],"optional":true},"netbios_node_type":{"type":"string","optional":true},"ntp_servers":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_devicefarm_project":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_directory_service_conditional_forwarder":{"version":0,"block":{"attributes":{"directory_id":{"type":"string","required":true},"dns_ips":{"type":["list","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"remote_domain_name":{"type":"string","required":true}}}},"aws_directory_service_directory":{"version":0,"block":{"attributes":{"access_url":{"type":"string","computed":true},"alias":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"dns_ip_addresses":{"type":["set","string"],"computed":true},"edition":{"type":"string","optional":true,"computed":true},"enable_sso":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"password":{"type":"string","required":true,"sensitive":true},"security_group_id":{"type":"string","computed":true},"short_name":{"type":"string","optional":true,"computed":true},"size":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true}},"block_types":{"connect_settings":{"nesting_mode":"list","block":{"attributes":{"availability_zones":{"type":["set","string"],"computed":true},"connect_ips":{"type":["set","string"],"computed":true},"customer_dns_ips":{"type":["set","string"],"required":true},"customer_username":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","required":true}}},"max_items":1},"vpc_settings":{"nesting_mode":"list","block":{"attributes":{"availability_zones":{"type":["set","string"],"computed":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_directory_service_log_subscription":{"version":0,"block":{"attributes":{"directory_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","required":true}}}},"aws_dlm_lifecycle_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","required":true},"execution_role_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"state":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"policy_details":{"nesting_mode":"list","block":{"attributes":{"resource_types":{"type":["list","string"],"required":true},"target_tags":{"type":["map","string"],"required":true}},"block_types":{"schedule":{"nesting_mode":"list","block":{"attributes":{"copy_tags":{"type":"bool","optional":true,"computed":true},"name":{"type":"string","required":true},"tags_to_add":{"type":["map","string"],"optional":true}},"block_types":{"create_rule":{"nesting_mode":"list","block":{"attributes":{"interval":{"type":"number","required":true},"interval_unit":{"type":"string","optional":true},"times":{"type":["list","string"],"optional":true,"computed":true}}},"min_items":1,"max_items":1},"retain_rule":{"nesting_mode":"list","block":{"attributes":{"count":{"type":"number","required":true}}},"min_items":1,"max_items":1}}},"min_items":1}}},"min_items":1,"max_items":1}}}},"aws_dms_certificate":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","computed":true},"certificate_id":{"type":"string","required":true},"certificate_pem":{"type":"string","optional":true,"sensitive":true},"certificate_wallet":{"type":"string","optional":true,"sensitive":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_dms_endpoint":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","optional":true,"computed":true},"database_name":{"type":"string","optional":true},"endpoint_arn":{"type":"string","computed":true},"endpoint_id":{"type":"string","required":true},"endpoint_type":{"type":"string","required":true},"engine_name":{"type":"string","required":true},"extra_connection_attributes":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true,"computed":true},"password":{"type":"string","optional":true,"sensitive":true},"port":{"type":"number","optional":true},"server_name":{"type":"string","optional":true},"service_access_role":{"type":"string","optional":true},"ssl_mode":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"username":{"type":"string","optional":true}},"block_types":{"elasticsearch_settings":{"nesting_mode":"list","block":{"attributes":{"endpoint_uri":{"type":"string","required":true},"error_retry_duration":{"type":"number","optional":true},"full_load_error_percentage":{"type":"number","optional":true},"service_access_role_arn":{"type":"string","required":true}}},"max_items":1},"kafka_settings":{"nesting_mode":"list","block":{"attributes":{"broker":{"type":"string","required":true},"topic":{"type":"string","optional":true}}},"max_items":1},"kinesis_settings":{"nesting_mode":"list","block":{"attributes":{"message_format":{"type":"string","optional":true},"service_access_role_arn":{"type":"string","optional":true},"stream_arn":{"type":"string","optional":true}}},"max_items":1},"mongodb_settings":{"nesting_mode":"list","block":{"attributes":{"auth_mechanism":{"type":"string","optional":true},"auth_source":{"type":"string","optional":true},"auth_type":{"type":"string","optional":true},"docs_to_investigate":{"type":"string","optional":true},"extract_doc_id":{"type":"string","optional":true},"nesting_level":{"type":"string","optional":true}}},"max_items":1},"s3_settings":{"nesting_mode":"list","block":{"attributes":{"bucket_folder":{"type":"string","optional":true},"bucket_name":{"type":"string","optional":true},"compression_type":{"type":"string","optional":true},"csv_delimiter":{"type":"string","optional":true},"csv_row_delimiter":{"type":"string","optional":true},"external_table_definition":{"type":"string","optional":true},"service_access_role_arn":{"type":"string","optional":true}}},"max_items":1}}}},"aws_dms_event_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"event_categories":{"type":["set","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"sns_topic_arn":{"type":"string","required":true},"source_ids":{"type":["set","string"],"optional":true},"source_type":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dms_replication_instance":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","optional":true,"computed":true},"apply_immediately":{"type":"bool","optional":true},"auto_minor_version_upgrade":{"type":"bool","optional":true,"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true,"computed":true},"multi_az":{"type":"bool","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"publicly_accessible":{"type":"bool","optional":true,"computed":true},"replication_instance_arn":{"type":"string","computed":true},"replication_instance_class":{"type":"string","required":true},"replication_instance_id":{"type":"string","required":true},"replication_instance_private_ips":{"type":["list","string"],"computed":true},"replication_instance_public_ips":{"type":["list","string"],"computed":true},"replication_subnet_group_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dms_replication_subnet_group":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"replication_subnet_group_arn":{"type":"string","computed":true},"replication_subnet_group_description":{"type":"string","required":true},"replication_subnet_group_id":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}}}},"aws_dms_replication_task":{"version":0,"block":{"attributes":{"cdc_start_time":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"migration_type":{"type":"string","required":true},"replication_instance_arn":{"type":"string","required":true},"replication_task_arn":{"type":"string","computed":true},"replication_task_id":{"type":"string","required":true},"replication_task_settings":{"type":"string","optional":true},"source_endpoint_arn":{"type":"string","required":true},"table_mappings":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"target_endpoint_arn":{"type":"string","required":true}}}},"aws_docdb_cluster":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"backup_retention_period":{"type":"number","optional":true},"cluster_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier_prefix":{"type":"string","optional":true,"computed":true},"cluster_members":{"type":["set","string"],"optional":true,"computed":true},"cluster_resource_id":{"type":"string","computed":true},"db_cluster_parameter_group_name":{"type":"string","optional":true,"computed":true},"db_subnet_group_name":{"type":"string","optional":true,"computed":true},"deletion_protection":{"type":"bool","optional":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"optional":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"master_password":{"type":"string","optional":true,"sensitive":true},"master_username":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"reader_endpoint":{"type":"string","computed":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_identifier":{"type":"string","optional":true},"storage_encrypted":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_docdb_cluster_instance":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"ca_cert_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier":{"type":"string","required":true},"db_subnet_group_name":{"type":"string","computed":true},"dbi_resource_id":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"instance_class":{"type":"string","required":true},"kms_key_id":{"type":"string","computed":true},"port":{"type":"number","computed":true},"preferred_backup_window":{"type":"string","computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"promotion_tier":{"type":"number","optional":true},"publicly_accessible":{"type":"bool","computed":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true},"writer":{"type":"bool","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_docdb_cluster_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_docdb_cluster_snapshot":{"version":0,"block":{"attributes":{"availability_zones":{"type":["list","string"],"computed":true},"db_cluster_identifier":{"type":"string","required":true},"db_cluster_snapshot_arn":{"type":"string","computed":true},"db_cluster_snapshot_identifier":{"type":"string","required":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_type":{"type":"string","computed":true},"source_db_cluster_snapshot_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_docdb_subnet_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_dx_bgp_peer":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"bgp_peer_id":{"type":"string","computed":true},"bgp_status":{"type":"string","computed":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"virtual_interface_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_connection":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bandwidth":{"type":"string","required":true},"has_logical_redundancy":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"location":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_dx_connection_association":{"version":0,"block":{"attributes":{"connection_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"lag_id":{"type":"string","required":true}}}},"aws_dx_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_gateway_association":{"version":1,"block":{"attributes":{"allowed_prefixes":{"type":["set","string"],"optional":true,"computed":true},"associated_gateway_id":{"type":"string","optional":true,"computed":true},"associated_gateway_owner_account_id":{"type":"string","optional":true,"computed":true},"associated_gateway_type":{"type":"string","computed":true},"dx_gateway_association_id":{"type":"string","computed":true},"dx_gateway_id":{"type":"string","required":true},"dx_gateway_owner_account_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"proposal_id":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dx_gateway_association_proposal":{"version":0,"block":{"attributes":{"allowed_prefixes":{"type":["set","string"],"optional":true,"computed":true},"associated_gateway_id":{"type":"string","required":true},"associated_gateway_owner_account_id":{"type":"string","computed":true},"associated_gateway_type":{"type":"string","computed":true},"dx_gateway_id":{"type":"string","required":true},"dx_gateway_owner_account_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_dx_hosted_private_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"mtu":{"type":"number","optional":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","required":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_private_virtual_interface_accepter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"dx_gateway_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"virtual_interface_id":{"type":"string","required":true},"vpn_gateway_id":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_public_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","required":true},"route_filter_prefixes":{"type":["set","string"],"required":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_public_virtual_interface_accepter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"virtual_interface_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_transit_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"mtu":{"type":"number","optional":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","required":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_transit_virtual_interface_accepter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"dx_gateway_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"virtual_interface_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_lag":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"connections_bandwidth":{"type":"string","required":true},"force_destroy":{"type":"bool","optional":true},"has_logical_redundancy":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"location":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_dx_private_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"dx_gateway_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"mtu":{"type":"number","optional":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vlan":{"type":"number","required":true},"vpn_gateway_id":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dx_public_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"route_filter_prefixes":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_transit_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"dx_gateway_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"mtu":{"type":"number","optional":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dynamodb_global_table":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"replica":{"nesting_mode":"set","block":{"attributes":{"region_name":{"type":"string","required":true}}},"min_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dynamodb_table":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"billing_mode":{"type":"string","optional":true},"hash_key":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"range_key":{"type":"string","optional":true},"read_capacity":{"type":"number","optional":true},"stream_arn":{"type":"string","computed":true},"stream_enabled":{"type":"bool","optional":true},"stream_label":{"type":"string","computed":true},"stream_view_type":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"write_capacity":{"type":"number","optional":true}},"block_types":{"attribute":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"type":{"type":"string","required":true}}},"min_items":1},"global_secondary_index":{"nesting_mode":"set","block":{"attributes":{"hash_key":{"type":"string","required":true},"name":{"type":"string","required":true},"non_key_attributes":{"type":["list","string"],"optional":true},"projection_type":{"type":"string","required":true},"range_key":{"type":"string","optional":true},"read_capacity":{"type":"number","optional":true},"write_capacity":{"type":"number","optional":true}}}},"local_secondary_index":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"non_key_attributes":{"type":["list","string"],"optional":true},"projection_type":{"type":"string","required":true},"range_key":{"type":"string","required":true}}}},"point_in_time_recovery":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true}}},"max_items":1},"replica":{"nesting_mode":"set","block":{"attributes":{"region_name":{"type":"string","required":true}}}},"server_side_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true},"kms_key_arn":{"type":"string","optional":true,"computed":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}},"ttl":{"nesting_mode":"list","block":{"attributes":{"attribute_name":{"type":"string","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_dynamodb_table_item":{"version":0,"block":{"attributes":{"hash_key":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"item":{"type":"string","required":true},"range_key":{"type":"string","optional":true},"table_name":{"type":"string","required":true}}}},"aws_ebs_default_kms_key":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"key_arn":{"type":"string","required":true}}}},"aws_ebs_encryption_by_default":{"version":0,"block":{"attributes":{"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ebs_snapshot":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"data_encryption_key_id":{"type":"string","computed":true},"description":{"type":"string","optional":true},"encrypted":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"owner_alias":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"volume_id":{"type":"string","required":true},"volume_size":{"type":"number","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_ebs_snapshot_copy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"data_encryption_key_id":{"type":"string","computed":true},"description":{"type":"string","optional":true},"encrypted":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"owner_alias":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"source_region":{"type":"string","required":true},"source_snapshot_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","computed":true}}}},"aws_ebs_volume":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"multi_attach_enabled":{"type":"bool","optional":true},"outpost_arn":{"type":"string","optional":true},"size":{"type":"number","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true,"computed":true}}}},"aws_ec2_availability_zone_group":{"version":0,"block":{"attributes":{"group_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"opt_in_status":{"type":"string","required":true}}}},"aws_ec2_capacity_reservation":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","required":true},"ebs_optimized":{"type":"bool","optional":true},"end_date":{"type":"string","optional":true},"end_date_type":{"type":"string","optional":true},"ephemeral_storage":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_count":{"type":"number","required":true},"instance_match_criteria":{"type":"string","optional":true},"instance_platform":{"type":"string","required":true},"instance_type":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"tenancy":{"type":"string","optional":true}}}},"aws_ec2_client_vpn_authorization_rule":{"version":0,"block":{"attributes":{"access_group_id":{"type":"string","optional":true},"authorize_all_groups":{"type":"bool","optional":true},"client_vpn_endpoint_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"target_network_cidr":{"type":"string","required":true}}}},"aws_ec2_client_vpn_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"client_cidr_block":{"type":"string","required":true},"description":{"type":"string","optional":true},"dns_name":{"type":"string","computed":true},"dns_servers":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"server_certificate_arn":{"type":"string","required":true},"split_tunnel":{"type":"bool","optional":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"transport_protocol":{"type":"string","optional":true}},"block_types":{"authentication_options":{"nesting_mode":"list","block":{"attributes":{"active_directory_id":{"type":"string","optional":true},"root_certificate_chain_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":2},"connection_log_options":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_log_group":{"type":"string","optional":true},"cloudwatch_log_stream":{"type":"string","optional":true},"enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"aws_ec2_client_vpn_network_association":{"version":0,"block":{"attributes":{"client_vpn_endpoint_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"security_groups":{"type":["set","string"],"computed":true},"status":{"type":"string","computed":true},"subnet_id":{"type":"string","required":true},"vpc_id":{"type":"string","computed":true}}}},"aws_ec2_client_vpn_route":{"version":0,"block":{"attributes":{"client_vpn_endpoint_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"origin":{"type":"string","computed":true},"target_vpc_subnet_id":{"type":"string","required":true},"type":{"type":"string","computed":true}}}},"aws_ec2_fleet":{"version":0,"block":{"attributes":{"excess_capacity_termination_policy":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"replace_unhealthy_instances":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"terminate_instances":{"type":"bool","optional":true},"terminate_instances_with_expiration":{"type":"bool","optional":true},"type":{"type":"string","optional":true}},"block_types":{"launch_template_config":{"nesting_mode":"list","block":{"block_types":{"launch_template_specification":{"nesting_mode":"list","block":{"attributes":{"launch_template_id":{"type":"string","optional":true},"launch_template_name":{"type":"string","optional":true},"version":{"type":"string","required":true}}},"min_items":1,"max_items":1},"override":{"nesting_mode":"list","block":{"attributes":{"availability_zone":{"type":"string","optional":true},"instance_type":{"type":"string","optional":true},"max_price":{"type":"string","optional":true},"priority":{"type":"number","optional":true},"subnet_id":{"type":"string","optional":true},"weighted_capacity":{"type":"number","optional":true}}},"max_items":50}}},"min_items":1,"max_items":1},"on_demand_options":{"nesting_mode":"list","block":{"attributes":{"allocation_strategy":{"type":"string","optional":true}}},"max_items":1},"spot_options":{"nesting_mode":"list","block":{"attributes":{"allocation_strategy":{"type":"string","optional":true},"instance_interruption_behavior":{"type":"string","optional":true},"instance_pools_to_use_count":{"type":"number","optional":true}}},"max_items":1},"target_capacity_specification":{"nesting_mode":"list","block":{"attributes":{"default_target_capacity_type":{"type":"string","required":true},"on_demand_target_capacity":{"type":"number","optional":true},"spot_target_capacity":{"type":"number","optional":true},"total_target_capacity":{"type":"number","required":true}}},"min_items":1,"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_ec2_local_gateway_route":{"version":0,"block":{"attributes":{"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"local_gateway_route_table_id":{"type":"string","required":true},"local_gateway_virtual_interface_group_id":{"type":"string","required":true}}}},"aws_ec2_local_gateway_route_table_vpc_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_gateway_id":{"type":"string","computed":true},"local_gateway_route_table_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}}}},"aws_ec2_tag":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","required":true},"resource_id":{"type":"string","required":true},"value":{"type":"string","required":true}}}},"aws_ec2_traffic_mirror_filter":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"network_services":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_ec2_traffic_mirror_filter_rule":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"protocol":{"type":"number","optional":true},"rule_action":{"type":"string","required":true},"rule_number":{"type":"number","required":true},"source_cidr_block":{"type":"string","required":true},"traffic_direction":{"type":"string","required":true},"traffic_mirror_filter_id":{"type":"string","required":true}},"block_types":{"destination_port_range":{"nesting_mode":"list","block":{"attributes":{"from_port":{"type":"number","optional":true},"to_port":{"type":"number","optional":true}}},"max_items":1},"source_port_range":{"nesting_mode":"list","block":{"attributes":{"from_port":{"type":"number","optional":true},"to_port":{"type":"number","optional":true}}},"max_items":1}}}},"aws_ec2_traffic_mirror_session":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","required":true},"packet_length":{"type":"number","optional":true},"session_number":{"type":"number","required":true},"tags":{"type":["map","string"],"optional":true},"traffic_mirror_filter_id":{"type":"string","required":true},"traffic_mirror_target_id":{"type":"string","required":true},"virtual_network_id":{"type":"number","optional":true,"computed":true}}}},"aws_ec2_traffic_mirror_target":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","optional":true},"network_load_balancer_arn":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_ec2_transit_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"number","optional":true},"arn":{"type":"string","computed":true},"association_default_route_table_id":{"type":"string","computed":true},"auto_accept_shared_attachments":{"type":"string","optional":true},"default_route_table_association":{"type":"string","optional":true},"default_route_table_propagation":{"type":"string","optional":true},"description":{"type":"string","optional":true},"dns_support":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"propagation_default_route_table_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpn_ecmp_support":{"type":"string","optional":true}}}},"aws_ec2_transit_gateway_peering_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"peer_account_id":{"type":"string","optional":true,"computed":true},"peer_region":{"type":"string","required":true},"peer_transit_gateway_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_peering_attachment_accepter":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"peer_account_id":{"type":"string","computed":true},"peer_region":{"type":"string","computed":true},"peer_transit_gateway_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_attachment_id":{"type":"string","required":true},"transit_gateway_id":{"type":"string","computed":true}}}},"aws_ec2_transit_gateway_route":{"version":0,"block":{"attributes":{"blackhole":{"type":"bool","optional":true},"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"transit_gateway_attachment_id":{"type":"string","optional":true},"transit_gateway_route_table_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_route_table":{"version":0,"block":{"attributes":{"default_association_route_table":{"type":"bool","computed":true},"default_propagation_route_table":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_route_table_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_id":{"type":"string","computed":true},"resource_type":{"type":"string","computed":true},"transit_gateway_attachment_id":{"type":"string","required":true},"transit_gateway_route_table_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_route_table_propagation":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_id":{"type":"string","computed":true},"resource_type":{"type":"string","computed":true},"transit_gateway_attachment_id":{"type":"string","required":true},"transit_gateway_route_table_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_vpc_attachment":{"version":0,"block":{"attributes":{"dns_support":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_support":{"type":"string","optional":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_default_route_table_association":{"type":"bool","optional":true},"transit_gateway_default_route_table_propagation":{"type":"bool","optional":true},"transit_gateway_id":{"type":"string","required":true},"vpc_id":{"type":"string","required":true},"vpc_owner_id":{"type":"string","computed":true}}}},"aws_ec2_transit_gateway_vpc_attachment_accepter":{"version":0,"block":{"attributes":{"dns_support":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_support":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_attachment_id":{"type":"string","required":true},"transit_gateway_default_route_table_association":{"type":"bool","optional":true},"transit_gateway_default_route_table_propagation":{"type":"bool","optional":true},"transit_gateway_id":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true},"vpc_owner_id":{"type":"string","computed":true}}}},"aws_ecr_lifecycle_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true},"registry_id":{"type":"string","computed":true},"repository":{"type":"string","required":true}}}},"aws_ecr_repository":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"image_tag_mutability":{"type":"string","optional":true},"name":{"type":"string","required":true},"registry_id":{"type":"string","computed":true},"repository_url":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"encryption_configuration":{"nesting_mode":"list","block":{"attributes":{"encryption_type":{"type":"string","optional":true},"kms_key":{"type":"string","optional":true,"computed":true}}}},"image_scanning_configuration":{"nesting_mode":"list","block":{"attributes":{"scan_on_push":{"type":"bool","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true}}}}}}},"aws_ecr_repository_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true},"registry_id":{"type":"string","computed":true},"repository":{"type":"string","required":true}}}},"aws_ecs_capacity_provider":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"auto_scaling_group_provider":{"nesting_mode":"list","block":{"attributes":{"auto_scaling_group_arn":{"type":"string","required":true},"managed_termination_protection":{"type":"string","optional":true,"computed":true}},"block_types":{"managed_scaling":{"nesting_mode":"list","block":{"attributes":{"maximum_scaling_step_size":{"type":"number","optional":true,"computed":true},"minimum_scaling_step_size":{"type":"number","optional":true,"computed":true},"status":{"type":"string","optional":true,"computed":true},"target_capacity":{"type":"number","optional":true,"computed":true}}},"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_ecs_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"capacity_providers":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"default_capacity_provider_strategy":{"nesting_mode":"set","block":{"attributes":{"base":{"type":"number","optional":true},"capacity_provider":{"type":"string","required":true},"weight":{"type":"number","optional":true}}}},"setting":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_ecs_service":{"version":0,"block":{"attributes":{"cluster":{"type":"string","optional":true,"computed":true},"deployment_maximum_percent":{"type":"number","optional":true},"deployment_minimum_healthy_percent":{"type":"number","optional":true},"desired_count":{"type":"number","optional":true},"enable_ecs_managed_tags":{"type":"bool","optional":true},"force_new_deployment":{"type":"bool","optional":true},"health_check_grace_period_seconds":{"type":"number","optional":true},"iam_role":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"launch_type":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"platform_version":{"type":"string","optional":true,"computed":true},"propagate_tags":{"type":"string","optional":true},"scheduling_strategy":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"task_definition":{"type":"string","optional":true}},"block_types":{"capacity_provider_strategy":{"nesting_mode":"set","block":{"attributes":{"base":{"type":"number","optional":true},"capacity_provider":{"type":"string","required":true},"weight":{"type":"number","optional":true}}}},"deployment_controller":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","optional":true}}},"max_items":1},"load_balancer":{"nesting_mode":"set","block":{"attributes":{"container_name":{"type":"string","required":true},"container_port":{"type":"number","required":true},"elb_name":{"type":"string","optional":true},"target_group_arn":{"type":"string","optional":true}}}},"network_configuration":{"nesting_mode":"list","block":{"attributes":{"assign_public_ip":{"type":"bool","optional":true},"security_groups":{"type":["set","string"],"optional":true},"subnets":{"type":["set","string"],"required":true}}},"max_items":1},"ordered_placement_strategy":{"nesting_mode":"list","block":{"attributes":{"field":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"max_items":5},"placement_constraints":{"nesting_mode":"set","block":{"attributes":{"expression":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"max_items":10},"service_registries":{"nesting_mode":"list","block":{"attributes":{"container_name":{"type":"string","optional":true},"container_port":{"type":"number","optional":true},"port":{"type":"number","optional":true},"registry_arn":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true}}}}}}},"aws_ecs_task_definition":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"container_definitions":{"type":"string","required":true},"cpu":{"type":"string","optional":true},"execution_role_arn":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ipc_mode":{"type":"string","optional":true},"memory":{"type":"string","optional":true},"network_mode":{"type":"string","optional":true,"computed":true},"pid_mode":{"type":"string","optional":true},"requires_compatibilities":{"type":["set","string"],"optional":true},"revision":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true},"task_role_arn":{"type":"string","optional":true}},"block_types":{"inference_accelerator":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"device_type":{"type":"string","required":true}}}},"placement_constraints":{"nesting_mode":"set","block":{"attributes":{"expression":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"max_items":10},"proxy_configuration":{"nesting_mode":"list","block":{"attributes":{"container_name":{"type":"string","required":true},"properties":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true}}},"max_items":1},"volume":{"nesting_mode":"set","block":{"attributes":{"host_path":{"type":"string","optional":true},"name":{"type":"string","required":true}},"block_types":{"docker_volume_configuration":{"nesting_mode":"list","block":{"attributes":{"autoprovision":{"type":"bool","optional":true},"driver":{"type":"string","optional":true},"driver_opts":{"type":["map","string"],"optional":true},"labels":{"type":["map","string"],"optional":true},"scope":{"type":"string","optional":true,"computed":true}}},"max_items":1},"efs_volume_configuration":{"nesting_mode":"list","block":{"attributes":{"file_system_id":{"type":"string","required":true},"root_directory":{"type":"string","optional":true},"transit_encryption":{"type":"string","optional":true},"transit_encryption_port":{"type":"number","optional":true}},"block_types":{"authorization_config":{"nesting_mode":"list","block":{"attributes":{"access_point_id":{"type":"string","optional":true},"iam":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}}}}},"aws_efs_access_point":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"file_system_arn":{"type":"string","computed":true},"file_system_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"posix_user":{"nesting_mode":"list","block":{"attributes":{"gid":{"type":"number","required":true},"secondary_gids":{"type":["set","number"],"optional":true},"uid":{"type":"number","required":true}}},"max_items":1},"root_directory":{"nesting_mode":"list","block":{"attributes":{"path":{"type":"string","optional":true,"computed":true}},"block_types":{"creation_info":{"nesting_mode":"list","block":{"attributes":{"owner_gid":{"type":"number","required":true},"owner_uid":{"type":"number","required":true},"permissions":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}}},"aws_efs_file_system":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_token":{"type":"string","optional":true,"computed":true},"dns_name":{"type":"string","computed":true},"encrypted":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"performance_mode":{"type":"string","optional":true,"computed":true},"provisioned_throughput_in_mibps":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true},"throughput_mode":{"type":"string","optional":true}},"block_types":{"lifecycle_policy":{"nesting_mode":"list","block":{"attributes":{"transition_to_ia":{"type":"string","required":true}}},"max_items":1}}}},"aws_efs_file_system_policy":{"version":0,"block":{"attributes":{"file_system_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true}}}},"aws_efs_mount_target":{"version":0,"block":{"attributes":{"availability_zone_id":{"type":"string","computed":true},"availability_zone_name":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"file_system_arn":{"type":"string","computed":true},"file_system_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","optional":true,"computed":true},"mount_target_dns_name":{"type":"string","computed":true},"network_interface_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"subnet_id":{"type":"string","required":true}}}},"aws_egress_only_internet_gateway":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}}}},"aws_eip":{"version":0,"block":{"attributes":{"allocation_id":{"type":"string","computed":true},"associate_with_private_ip":{"type":"string","optional":true},"association_id":{"type":"string","computed":true},"customer_owned_ip":{"type":"string","computed":true},"customer_owned_ipv4_pool":{"type":"string","optional":true},"domain":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instance":{"type":"string","optional":true,"computed":true},"network_interface":{"type":"string","optional":true,"computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"public_ipv4_pool":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc":{"type":"bool","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true},"read":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_eip_association":{"version":0,"block":{"attributes":{"allocation_id":{"type":"string","optional":true,"computed":true},"allow_reassociation":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","optional":true,"computed":true},"private_ip_address":{"type":"string","optional":true,"computed":true},"public_ip":{"type":"string","optional":true,"computed":true}}}},"aws_eks_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_authority":{"type":["list",["object",{"data":"string"}]],"computed":true},"created_at":{"type":"string","computed":true},"enabled_cluster_log_types":{"type":["set","string"],"optional":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identity":{"type":["list",["object",{"oidc":["list",["object",{"issuer":"string"}]]}]],"computed":true},"name":{"type":"string","required":true},"platform_version":{"type":"string","computed":true},"role_arn":{"type":"string","required":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","optional":true,"computed":true}},"block_types":{"encryption_config":{"nesting_mode":"list","block":{"attributes":{"resources":{"type":["set","string"],"required":true}},"block_types":{"provider":{"nesting_mode":"list","block":{"attributes":{"key_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}},"vpc_config":{"nesting_mode":"list","block":{"attributes":{"cluster_security_group_id":{"type":"string","computed":true},"endpoint_private_access":{"type":"bool","optional":true},"endpoint_public_access":{"type":"bool","optional":true},"public_access_cidrs":{"type":["set","string"],"optional":true,"computed":true},"security_group_ids":{"type":["set","string"],"optional":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","computed":true}}},"min_items":1,"max_items":1}}}},"aws_eks_fargate_profile":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"fargate_profile_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"pod_execution_role_arn":{"type":"string","required":true},"status":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"selector":{"nesting_mode":"set","block":{"attributes":{"labels":{"type":["map","string"],"optional":true},"namespace":{"type":"string","required":true}}},"min_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_eks_node_group":{"version":0,"block":{"attributes":{"ami_type":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"disk_size":{"type":"number","optional":true,"computed":true},"force_update_version":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_types":{"type":["list","string"],"optional":true,"computed":true},"labels":{"type":["map","string"],"optional":true},"node_group_name":{"type":"string","required":true},"node_role_arn":{"type":"string","required":true},"release_version":{"type":"string","optional":true,"computed":true},"resources":{"type":["list",["object",{"autoscaling_groups":["list",["object",{"name":"string"}]],"remote_access_security_group_id":"string"}]],"computed":true},"status":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","optional":true,"computed":true}},"block_types":{"remote_access":{"nesting_mode":"list","block":{"attributes":{"ec2_ssh_key":{"type":"string","optional":true},"source_security_group_ids":{"type":["set","string"],"optional":true}}},"max_items":1},"scaling_config":{"nesting_mode":"list","block":{"attributes":{"desired_size":{"type":"number","required":true},"max_size":{"type":"number","required":true},"min_size":{"type":"number","required":true}}},"min_items":1,"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_elastic_beanstalk_application":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"appversion_lifecycle":{"nesting_mode":"list","block":{"attributes":{"delete_source_from_s3":{"type":"bool","optional":true},"max_age_in_days":{"type":"number","optional":true},"max_count":{"type":"number","optional":true},"service_role":{"type":"string","required":true}}},"max_items":1}}}},"aws_elastic_beanstalk_application_version":{"version":0,"block":{"attributes":{"application":{"type":"string","required":true},"arn":{"type":"string","computed":true},"bucket":{"type":"string","required":true},"description":{"type":"string","optional":true},"force_delete":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_elastic_beanstalk_configuration_template":{"version":0,"block":{"attributes":{"application":{"type":"string","required":true},"description":{"type":"string","optional":true},"environment_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"solution_stack_name":{"type":"string","optional":true}},"block_types":{"setting":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"resource":{"type":"string","optional":true},"value":{"type":"string","required":true}}}}}}},"aws_elastic_beanstalk_environment":{"version":1,"block":{"attributes":{"all_settings":{"type":["set",["object",{"name":"string","namespace":"string","resource":"string","value":"string"}]],"computed":true},"application":{"type":"string","required":true},"arn":{"type":"string","computed":true},"autoscaling_groups":{"type":["list","string"],"computed":true},"cname":{"type":"string","computed":true},"cname_prefix":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"endpoint_url":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instances":{"type":["list","string"],"computed":true},"launch_configurations":{"type":["list","string"],"computed":true},"load_balancers":{"type":["list","string"],"computed":true},"name":{"type":"string","required":true},"platform_arn":{"type":"string","optional":true,"computed":true},"poll_interval":{"type":"string","optional":true},"queues":{"type":["list","string"],"computed":true},"solution_stack_name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"template_name":{"type":"string","optional":true},"tier":{"type":"string","optional":true},"triggers":{"type":["list","string"],"computed":true},"version_label":{"type":"string","optional":true,"computed":true},"wait_for_ready_timeout":{"type":"string","optional":true}},"block_types":{"setting":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"resource":{"type":"string","optional":true},"value":{"type":"string","required":true}}}}}}},"aws_elasticache_cluster":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"az_mode":{"type":"string","optional":true,"computed":true},"cache_nodes":{"type":["list",["object",{"address":"string","availability_zone":"string","id":"string","port":"number"}]],"computed":true},"cluster_address":{"type":"string","computed":true},"cluster_id":{"type":"string","required":true},"configuration_endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true,"computed":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"maintenance_window":{"type":"string","optional":true,"computed":true},"node_type":{"type":"string","optional":true,"computed":true},"notification_topic_arn":{"type":"string","optional":true},"num_cache_nodes":{"type":"number","optional":true,"computed":true},"parameter_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"preferred_availability_zones":{"type":["list","string"],"optional":true},"replication_group_id":{"type":"string","optional":true,"computed":true},"security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"security_group_names":{"type":["set","string"],"optional":true,"computed":true},"snapshot_arns":{"type":["set","string"],"optional":true},"snapshot_name":{"type":"string","optional":true},"snapshot_retention_limit":{"type":"number","optional":true},"snapshot_window":{"type":"string","optional":true,"computed":true},"subnet_group_name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_elasticache_parameter_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_elasticache_replication_group":{"version":1,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"at_rest_encryption_enabled":{"type":"bool","optional":true},"auth_token":{"type":"string","optional":true,"sensitive":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"automatic_failover_enabled":{"type":"bool","optional":true},"availability_zones":{"type":["set","string"],"optional":true},"configuration_endpoint_address":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"maintenance_window":{"type":"string","optional":true,"computed":true},"member_clusters":{"type":["set","string"],"computed":true},"node_type":{"type":"string","optional":true,"computed":true},"notification_topic_arn":{"type":"string","optional":true},"number_cache_clusters":{"type":"number","optional":true,"computed":true},"parameter_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"primary_endpoint_address":{"type":"string","computed":true},"replication_group_description":{"type":"string","required":true},"replication_group_id":{"type":"string","required":true},"security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"security_group_names":{"type":["set","string"],"optional":true,"computed":true},"snapshot_arns":{"type":["set","string"],"optional":true},"snapshot_name":{"type":"string","optional":true},"snapshot_retention_limit":{"type":"number","optional":true},"snapshot_window":{"type":"string","optional":true,"computed":true},"subnet_group_name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"transit_encryption_enabled":{"type":"bool","optional":true}},"block_types":{"cluster_mode":{"nesting_mode":"list","block":{"attributes":{"num_node_groups":{"type":"number","required":true},"replicas_per_node_group":{"type":"number","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_elasticache_security_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"security_group_names":{"type":["set","string"],"required":true}}}},"aws_elasticache_subnet_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true}}}},"aws_elasticsearch_domain":{"version":0,"block":{"attributes":{"access_policies":{"type":"string","optional":true,"computed":true},"advanced_options":{"type":["map","string"],"optional":true,"computed":true},"arn":{"type":"string","computed":true},"domain_id":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"elasticsearch_version":{"type":"string","optional":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kibana_endpoint":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"advanced_security_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true},"internal_user_database_enabled":{"type":"bool","optional":true}},"block_types":{"master_user_options":{"nesting_mode":"list","block":{"attributes":{"master_user_arn":{"type":"string","optional":true},"master_user_name":{"type":"string","optional":true},"master_user_password":{"type":"string","optional":true,"sensitive":true}}},"max_items":1}}},"max_items":1},"cluster_config":{"nesting_mode":"list","block":{"attributes":{"dedicated_master_count":{"type":"number","optional":true},"dedicated_master_enabled":{"type":"bool","optional":true},"dedicated_master_type":{"type":"string","optional":true},"instance_count":{"type":"number","optional":true},"instance_type":{"type":"string","optional":true},"warm_count":{"type":"number","optional":true},"warm_enabled":{"type":"bool","optional":true},"warm_type":{"type":"string","optional":true},"zone_awareness_enabled":{"type":"bool","optional":true}},"block_types":{"zone_awareness_config":{"nesting_mode":"list","block":{"attributes":{"availability_zone_count":{"type":"number","optional":true}}},"max_items":1}}},"max_items":1},"cognito_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"identity_pool_id":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"user_pool_id":{"type":"string","required":true}}},"max_items":1},"domain_endpoint_options":{"nesting_mode":"list","block":{"attributes":{"enforce_https":{"type":"bool","required":true},"tls_security_policy":{"type":"string","optional":true,"computed":true}}},"max_items":1},"ebs_options":{"nesting_mode":"list","block":{"attributes":{"ebs_enabled":{"type":"bool","required":true},"iops":{"type":"number","optional":true},"volume_size":{"type":"number","optional":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1},"encrypt_at_rest":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true},"kms_key_id":{"type":"string","optional":true,"computed":true}}},"max_items":1},"log_publishing_options":{"nesting_mode":"set","block":{"attributes":{"cloudwatch_log_group_arn":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"log_type":{"type":"string","required":true}}}},"node_to_node_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true}}},"max_items":1},"snapshot_options":{"nesting_mode":"list","block":{"attributes":{"automated_snapshot_start_hour":{"type":"number","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"update":{"type":"string","optional":true}}}},"vpc_options":{"nesting_mode":"list","block":{"attributes":{"availability_zones":{"type":["set","string"],"computed":true},"security_group_ids":{"type":["set","string"],"optional":true},"subnet_ids":{"type":["set","string"],"optional":true},"vpc_id":{"type":"string","computed":true}}},"max_items":1}}}},"aws_elasticsearch_domain_policy":{"version":0,"block":{"attributes":{"access_policies":{"type":"string","required":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_elastictranscoder_pipeline":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_kms_key_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"input_bucket":{"type":"string","required":true},"name":{"type":"string","optional":true,"computed":true},"output_bucket":{"type":"string","optional":true,"computed":true},"role":{"type":"string","required":true}},"block_types":{"content_config":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","optional":true,"computed":true},"storage_class":{"type":"string","optional":true}}},"max_items":1},"content_config_permissions":{"nesting_mode":"set","block":{"attributes":{"access":{"type":["list","string"],"optional":true},"grantee":{"type":"string","optional":true},"grantee_type":{"type":"string","optional":true}}}},"notifications":{"nesting_mode":"list","block":{"attributes":{"completed":{"type":"string","optional":true},"error":{"type":"string","optional":true},"progressing":{"type":"string","optional":true},"warning":{"type":"string","optional":true}}},"max_items":1},"thumbnail_config":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","optional":true,"computed":true},"storage_class":{"type":"string","optional":true}}},"max_items":1},"thumbnail_config_permissions":{"nesting_mode":"set","block":{"attributes":{"access":{"type":["list","string"],"optional":true},"grantee":{"type":"string","optional":true},"grantee_type":{"type":"string","optional":true}}}}}}},"aws_elastictranscoder_preset":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"container":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"type":{"type":"string","optional":true,"computed":true},"video_codec_options":{"type":["map","string"],"optional":true}},"block_types":{"audio":{"nesting_mode":"list","block":{"attributes":{"audio_packing_mode":{"type":"string","optional":true},"bit_rate":{"type":"string","optional":true},"channels":{"type":"string","optional":true},"codec":{"type":"string","optional":true},"sample_rate":{"type":"string","optional":true}}},"max_items":1},"audio_codec_options":{"nesting_mode":"list","block":{"attributes":{"bit_depth":{"type":"string","optional":true},"bit_order":{"type":"string","optional":true},"profile":{"type":"string","optional":true},"signed":{"type":"string","optional":true}}},"max_items":1},"thumbnails":{"nesting_mode":"list","block":{"attributes":{"aspect_ratio":{"type":"string","optional":true},"format":{"type":"string","optional":true},"interval":{"type":"string","optional":true},"max_height":{"type":"string","optional":true},"max_width":{"type":"string","optional":true},"padding_policy":{"type":"string","optional":true},"resolution":{"type":"string","optional":true},"sizing_policy":{"type":"string","optional":true}}},"max_items":1},"video":{"nesting_mode":"list","block":{"attributes":{"aspect_ratio":{"type":"string","optional":true},"bit_rate":{"type":"string","optional":true},"codec":{"type":"string","optional":true},"display_aspect_ratio":{"type":"string","optional":true},"fixed_gop":{"type":"string","optional":true},"frame_rate":{"type":"string","optional":true},"keyframes_max_dist":{"type":"string","optional":true},"max_frame_rate":{"type":"string","optional":true,"computed":true},"max_height":{"type":"string","optional":true},"max_width":{"type":"string","optional":true},"padding_policy":{"type":"string","optional":true},"resolution":{"type":"string","optional":true},"sizing_policy":{"type":"string","optional":true}}},"max_items":1},"video_watermarks":{"nesting_mode":"set","block":{"attributes":{"horizontal_align":{"type":"string","optional":true},"horizontal_offset":{"type":"string","optional":true},"id":{"type":"string","optional":true},"max_height":{"type":"string","optional":true},"max_width":{"type":"string","optional":true},"opacity":{"type":"string","optional":true},"sizing_policy":{"type":"string","optional":true},"target":{"type":"string","optional":true},"vertical_align":{"type":"string","optional":true},"vertical_offset":{"type":"string","optional":true}}}}}}},"aws_elb":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"connection_draining":{"type":"bool","optional":true},"connection_draining_timeout":{"type":"number","optional":true},"cross_zone_load_balancing":{"type":"bool","optional":true},"dns_name":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","optional":true},"instances":{"type":["set","string"],"optional":true,"computed":true},"internal":{"type":"bool","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"source_security_group":{"type":"string","optional":true,"computed":true},"source_security_group_id":{"type":"string","computed":true},"subnets":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"zone_id":{"type":"string","computed":true}},"block_types":{"access_logs":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"bucket_prefix":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"interval":{"type":"number","optional":true}}},"max_items":1},"health_check":{"nesting_mode":"list","block":{"attributes":{"healthy_threshold":{"type":"number","required":true},"interval":{"type":"number","required":true},"target":{"type":"string","required":true},"timeout":{"type":"number","required":true},"unhealthy_threshold":{"type":"number","required":true}}},"max_items":1},"listener":{"nesting_mode":"set","block":{"attributes":{"instance_port":{"type":"number","required":true},"instance_protocol":{"type":"string","required":true},"lb_port":{"type":"number","required":true},"lb_protocol":{"type":"string","required":true},"ssl_certificate_id":{"type":"string","optional":true}}},"min_items":1}}}},"aws_elb_attachment":{"version":0,"block":{"attributes":{"elb":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instance":{"type":"string","required":true}}}},"aws_emr_cluster":{"version":0,"block":{"attributes":{"additional_info":{"type":"string","optional":true},"applications":{"type":["set","string"],"optional":true},"arn":{"type":"string","computed":true},"autoscaling_role":{"type":"string","optional":true},"cluster_state":{"type":"string","computed":true},"configurations":{"type":"string","optional":true},"configurations_json":{"type":"string","optional":true},"custom_ami_id":{"type":"string","optional":true},"ebs_root_volume_size":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"keep_job_flow_alive_when_no_steps":{"type":"bool","optional":true,"computed":true},"log_uri":{"type":"string","optional":true},"master_public_dns":{"type":"string","computed":true},"name":{"type":"string","required":true},"release_label":{"type":"string","required":true},"scale_down_behavior":{"type":"string","optional":true,"computed":true},"security_configuration":{"type":"string","optional":true},"service_role":{"type":"string","required":true},"step":{"type":["list",["object",{"action_on_failure":"string","hadoop_jar_step":["list",["object",{"args":["list","string"],"jar":"string","main_class":"string","properties":["map","string"]}]],"name":"string"}]],"optional":true,"computed":true},"step_concurrency_level":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true},"termination_protection":{"type":"bool","optional":true,"computed":true},"visible_to_all_users":{"type":"bool","optional":true}},"block_types":{"bootstrap_action":{"nesting_mode":"list","block":{"attributes":{"args":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"path":{"type":"string","required":true}}}},"core_instance_group":{"nesting_mode":"list","block":{"attributes":{"autoscaling_policy":{"type":"string","optional":true},"bid_price":{"type":"string","optional":true},"id":{"type":"string","computed":true},"instance_count":{"type":"number","optional":true},"instance_type":{"type":"string","required":true},"name":{"type":"string","optional":true}},"block_types":{"ebs_config":{"nesting_mode":"set","block":{"attributes":{"iops":{"type":"number","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","required":true},"volumes_per_instance":{"type":"number","optional":true}}}}}},"max_items":1},"ec2_attributes":{"nesting_mode":"list","block":{"attributes":{"additional_master_security_groups":{"type":"string","optional":true},"additional_slave_security_groups":{"type":"string","optional":true},"emr_managed_master_security_group":{"type":"string","optional":true,"computed":true},"emr_managed_slave_security_group":{"type":"string","optional":true,"computed":true},"instance_profile":{"type":"string","required":true},"key_name":{"type":"string","optional":true},"service_access_security_group":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true}}},"max_items":1},"kerberos_attributes":{"nesting_mode":"list","block":{"attributes":{"ad_domain_join_password":{"type":"string","optional":true,"sensitive":true},"ad_domain_join_user":{"type":"string","optional":true},"cross_realm_trust_principal_password":{"type":"string","optional":true,"sensitive":true},"kdc_admin_password":{"type":"string","required":true,"sensitive":true},"realm":{"type":"string","required":true}}},"max_items":1},"master_instance_group":{"nesting_mode":"list","block":{"attributes":{"bid_price":{"type":"string","optional":true},"id":{"type":"string","computed":true},"instance_count":{"type":"number","optional":true},"instance_type":{"type":"string","required":true},"name":{"type":"string","optional":true}},"block_types":{"ebs_config":{"nesting_mode":"set","block":{"attributes":{"iops":{"type":"number","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","required":true},"volumes_per_instance":{"type":"number","optional":true}}}}}},"max_items":1}}}},"aws_emr_instance_group":{"version":0,"block":{"attributes":{"autoscaling_policy":{"type":"string","optional":true},"bid_price":{"type":"string","optional":true},"cluster_id":{"type":"string","required":true},"configurations_json":{"type":"string","optional":true},"ebs_optimized":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_count":{"type":"number","optional":true},"instance_type":{"type":"string","required":true},"name":{"type":"string","optional":true},"running_instance_count":{"type":"number","computed":true},"status":{"type":"string","computed":true}},"block_types":{"ebs_config":{"nesting_mode":"set","block":{"attributes":{"iops":{"type":"number","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","required":true},"volumes_per_instance":{"type":"number","optional":true}}}}}}},"aws_emr_security_configuration":{"version":0,"block":{"attributes":{"configuration":{"type":"string","required":true},"creation_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true}}}},"aws_flow_log":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"eni_id":{"type":"string","optional":true},"iam_role_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"log_destination":{"type":"string","optional":true,"computed":true},"log_destination_type":{"type":"string","optional":true},"log_format":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","optional":true,"computed":true},"max_aggregation_interval":{"type":"number","optional":true},"subnet_id":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"traffic_type":{"type":"string","required":true},"vpc_id":{"type":"string","optional":true}}}},"aws_fms_admin_account":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_fsx_lustre_file_system":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deployment_type":{"type":"string","optional":true},"dns_name":{"type":"string","computed":true},"export_path":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"import_path":{"type":"string","optional":true},"imported_file_chunk_size":{"type":"number","optional":true,"computed":true},"network_interface_ids":{"type":["set","string"],"computed":true},"owner_id":{"type":"string","computed":true},"per_unit_storage_throughput":{"type":"number","optional":true},"security_group_ids":{"type":["set","string"],"optional":true},"storage_capacity":{"type":"number","required":true},"subnet_ids":{"type":["list","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"weekly_maintenance_start_time":{"type":"string","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_fsx_windows_file_system":{"version":0,"block":{"attributes":{"active_directory_id":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"automatic_backup_retention_days":{"type":"number","optional":true},"copy_tags_to_backups":{"type":"bool","optional":true},"daily_automatic_backup_start_time":{"type":"string","optional":true,"computed":true},"deployment_type":{"type":"string","optional":true},"dns_name":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"network_interface_ids":{"type":["set","string"],"computed":true},"owner_id":{"type":"string","computed":true},"preferred_file_server_ip":{"type":"string","computed":true},"preferred_subnet_id":{"type":"string","optional":true,"computed":true},"remote_administration_endpoint":{"type":"string","computed":true},"security_group_ids":{"type":["set","string"],"optional":true},"skip_final_backup":{"type":"bool","optional":true},"storage_capacity":{"type":"number","required":true},"storage_type":{"type":"string","optional":true},"subnet_ids":{"type":["list","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"throughput_capacity":{"type":"number","required":true},"vpc_id":{"type":"string","computed":true},"weekly_maintenance_start_time":{"type":"string","optional":true,"computed":true}},"block_types":{"self_managed_active_directory":{"nesting_mode":"list","block":{"attributes":{"dns_ips":{"type":["set","string"],"required":true},"domain_name":{"type":"string","required":true},"file_system_administrators_group":{"type":"string","optional":true},"organizational_unit_distinguished_name":{"type":"string","optional":true},"password":{"type":"string","required":true,"sensitive":true},"username":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_gamelift_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"routing_strategy":{"nesting_mode":"list","block":{"attributes":{"fleet_id":{"type":"string","optional":true},"message":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_gamelift_build":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"operating_system":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","optional":true}},"block_types":{"storage_location":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"key":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_gamelift_fleet":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"build_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"ec2_instance_type":{"type":"string","required":true},"fleet_type":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_role_arn":{"type":"string","optional":true},"log_paths":{"type":["list","string"],"computed":true},"metric_groups":{"type":["list","string"],"optional":true,"computed":true},"name":{"type":"string","required":true},"new_game_session_protection_policy":{"type":"string","optional":true},"operating_system":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"ec2_inbound_permission":{"nesting_mode":"list","block":{"attributes":{"from_port":{"type":"number","required":true},"ip_range":{"type":"string","required":true},"protocol":{"type":"string","required":true},"to_port":{"type":"number","required":true}}},"max_items":50},"resource_creation_limit_policy":{"nesting_mode":"list","block":{"attributes":{"new_game_sessions_per_creator":{"type":"number","optional":true},"policy_period_in_minutes":{"type":"number","optional":true}}},"max_items":1},"runtime_configuration":{"nesting_mode":"list","block":{"attributes":{"game_session_activation_timeout_seconds":{"type":"number","optional":true},"max_concurrent_game_session_activations":{"type":"number","optional":true}},"block_types":{"server_process":{"nesting_mode":"list","block":{"attributes":{"concurrent_executions":{"type":"number","required":true},"launch_path":{"type":"string","required":true},"parameters":{"type":"string","optional":true}}},"max_items":50}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_gamelift_game_session_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"destinations":{"type":["list","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"timeout_in_seconds":{"type":"number","optional":true}},"block_types":{"player_latency_policy":{"nesting_mode":"list","block":{"attributes":{"maximum_individual_player_latency_milliseconds":{"type":"number","required":true},"policy_duration_seconds":{"type":"number","optional":true}}}}}}},"aws_glacier_vault":{"version":0,"block":{"attributes":{"access_policy":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"location":{"type":"string","computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"notification":{"nesting_mode":"list","block":{"attributes":{"events":{"type":["set","string"],"required":true},"sns_topic":{"type":"string","required":true}}}}}}},"aws_glacier_vault_lock":{"version":0,"block":{"attributes":{"complete_lock":{"type":"bool","required":true},"id":{"type":"string","optional":true,"computed":true},"ignore_deletion_error":{"type":"bool","optional":true},"policy":{"type":"string","required":true},"vault_name":{"type":"string","required":true}}}},"aws_globalaccelerator_accelerator":{"version":0,"block":{"attributes":{"dns_name":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address_type":{"type":"string","optional":true},"ip_sets":{"type":["list",["object",{"ip_addresses":["list","string"],"ip_family":"string"}]],"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"attributes":{"nesting_mode":"list","block":{"attributes":{"flow_logs_enabled":{"type":"bool","optional":true},"flow_logs_s3_bucket":{"type":"string","optional":true},"flow_logs_s3_prefix":{"type":"string","optional":true}}},"max_items":1}}}},"aws_globalaccelerator_endpoint_group":{"version":0,"block":{"attributes":{"endpoint_group_region":{"type":"string","optional":true,"computed":true},"health_check_interval_seconds":{"type":"number","optional":true},"health_check_path":{"type":"string","optional":true},"health_check_port":{"type":"number","optional":true},"health_check_protocol":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true},"threshold_count":{"type":"number","optional":true},"traffic_dial_percentage":{"type":"number","optional":true}},"block_types":{"endpoint_configuration":{"nesting_mode":"set","block":{"attributes":{"endpoint_id":{"type":"string","optional":true},"weight":{"type":"number","optional":true}}},"max_items":10}}}},"aws_globalaccelerator_listener":{"version":0,"block":{"attributes":{"accelerator_arn":{"type":"string","required":true},"client_affinity":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"protocol":{"type":"string","required":true}},"block_types":{"port_range":{"nesting_mode":"set","block":{"attributes":{"from_port":{"type":"number","optional":true},"to_port":{"type":"number","optional":true}}},"min_items":1,"max_items":10}}}},"aws_glue_catalog_database":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"catalog_id":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"location_uri":{"type":"string","optional":true},"name":{"type":"string","required":true},"parameters":{"type":["map","string"],"optional":true}}}},"aws_glue_catalog_table":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"catalog_id":{"type":"string","optional":true,"computed":true},"database_name":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owner":{"type":"string","optional":true},"parameters":{"type":["map","string"],"optional":true},"retention":{"type":"number","optional":true},"table_type":{"type":"string","optional":true},"view_expanded_text":{"type":"string","optional":true},"view_original_text":{"type":"string","optional":true}},"block_types":{"partition_keys":{"nesting_mode":"list","block":{"attributes":{"comment":{"type":"string","optional":true},"name":{"type":"string","required":true},"type":{"type":"string","optional":true}}}},"storage_descriptor":{"nesting_mode":"list","block":{"attributes":{"bucket_columns":{"type":["list","string"],"optional":true},"compressed":{"type":"bool","optional":true},"input_format":{"type":"string","optional":true},"location":{"type":"string","optional":true},"number_of_buckets":{"type":"number","optional":true},"output_format":{"type":"string","optional":true},"parameters":{"type":["map","string"],"optional":true},"stored_as_sub_directories":{"type":"bool","optional":true}},"block_types":{"columns":{"nesting_mode":"list","block":{"attributes":{"comment":{"type":"string","optional":true},"name":{"type":"string","required":true},"type":{"type":"string","optional":true}}}},"ser_de_info":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","optional":true},"parameters":{"type":["map","string"],"optional":true},"serialization_library":{"type":"string","optional":true}}},"max_items":1},"skewed_info":{"nesting_mode":"list","block":{"attributes":{"skewed_column_names":{"type":["list","string"],"optional":true},"skewed_column_value_location_maps":{"type":["map","string"],"optional":true},"skewed_column_values":{"type":["list","string"],"optional":true}}},"max_items":1},"sort_columns":{"nesting_mode":"list","block":{"attributes":{"column":{"type":"string","required":true},"sort_order":{"type":"number","required":true}}}}}},"max_items":1}}}},"aws_glue_classifier":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"csv_classifier":{"nesting_mode":"list","block":{"attributes":{"allow_single_column":{"type":"bool","optional":true},"contains_header":{"type":"string","optional":true},"delimiter":{"type":"string","optional":true},"disable_value_trimming":{"type":"bool","optional":true},"header":{"type":["list","string"],"optional":true},"quote_symbol":{"type":"string","optional":true}}},"max_items":1},"grok_classifier":{"nesting_mode":"list","block":{"attributes":{"classification":{"type":"string","required":true},"custom_patterns":{"type":"string","optional":true},"grok_pattern":{"type":"string","required":true}}},"max_items":1},"json_classifier":{"nesting_mode":"list","block":{"attributes":{"json_path":{"type":"string","required":true}}},"max_items":1},"xml_classifier":{"nesting_mode":"list","block":{"attributes":{"classification":{"type":"string","required":true},"row_tag":{"type":"string","required":true}}},"max_items":1}}}},"aws_glue_connection":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"catalog_id":{"type":"string","optional":true,"computed":true},"connection_properties":{"type":["map","string"],"required":true,"sensitive":true},"connection_type":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"match_criteria":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true}},"block_types":{"physical_connection_requirements":{"nesting_mode":"list","block":{"attributes":{"availability_zone":{"type":"string","optional":true},"security_group_id_list":{"type":["list","string"],"optional":true},"subnet_id":{"type":"string","optional":true}}},"max_items":1}}}},"aws_glue_crawler":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"classifiers":{"type":["list","string"],"optional":true},"configuration":{"type":"string","optional":true},"database_name":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role":{"type":"string","required":true},"schedule":{"type":"string","optional":true},"security_configuration":{"type":"string","optional":true},"table_prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"catalog_target":{"nesting_mode":"list","block":{"attributes":{"database_name":{"type":"string","required":true},"tables":{"type":["list","string"],"required":true}}}},"dynamodb_target":{"nesting_mode":"list","block":{"attributes":{"path":{"type":"string","required":true}}}},"jdbc_target":{"nesting_mode":"list","block":{"attributes":{"connection_name":{"type":"string","required":true},"exclusions":{"type":["list","string"],"optional":true},"path":{"type":"string","required":true}}}},"s3_target":{"nesting_mode":"list","block":{"attributes":{"exclusions":{"type":["list","string"],"optional":true},"path":{"type":"string","required":true}}}},"schema_change_policy":{"nesting_mode":"list","block":{"attributes":{"delete_behavior":{"type":"string","optional":true},"update_behavior":{"type":"string","optional":true}}},"max_items":1}}}},"aws_glue_job":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"connections":{"type":["list","string"],"optional":true},"default_arguments":{"type":["map","string"],"optional":true},"description":{"type":"string","optional":true},"glue_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"max_capacity":{"type":"number","optional":true,"computed":true},"max_retries":{"type":"number","optional":true},"name":{"type":"string","required":true},"number_of_workers":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"security_configuration":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"timeout":{"type":"number","optional":true},"worker_type":{"type":"string","optional":true}},"block_types":{"command":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","optional":true},"python_version":{"type":"string","optional":true,"computed":true},"script_location":{"type":"string","required":true}}},"min_items":1,"max_items":1},"execution_property":{"nesting_mode":"list","block":{"attributes":{"max_concurrent_runs":{"type":"number","optional":true}}},"max_items":1},"notification_property":{"nesting_mode":"list","block":{"attributes":{"notify_delay_after":{"type":"number","optional":true}}},"max_items":1}}}},"aws_glue_security_configuration":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"encryption_configuration":{"nesting_mode":"list","block":{"block_types":{"cloudwatch_encryption":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_encryption_mode":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true}}},"min_items":1,"max_items":1},"job_bookmarks_encryption":{"nesting_mode":"list","block":{"attributes":{"job_bookmarks_encryption_mode":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true}}},"min_items":1,"max_items":1},"s3_encryption":{"nesting_mode":"list","block":{"attributes":{"kms_key_arn":{"type":"string","optional":true},"s3_encryption_mode":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_glue_trigger":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"schedule":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","required":true},"workflow_name":{"type":"string","optional":true}},"block_types":{"actions":{"nesting_mode":"list","block":{"attributes":{"arguments":{"type":["map","string"],"optional":true},"crawler_name":{"type":"string","optional":true},"job_name":{"type":"string","optional":true},"timeout":{"type":"number","optional":true}}},"min_items":1},"predicate":{"nesting_mode":"list","block":{"attributes":{"logical":{"type":"string","optional":true}},"block_types":{"conditions":{"nesting_mode":"list","block":{"attributes":{"crawl_state":{"type":"string","optional":true},"crawler_name":{"type":"string","optional":true},"job_name":{"type":"string","optional":true},"logical_operator":{"type":"string","optional":true},"state":{"type":"string","optional":true}}},"min_items":1}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_glue_workflow":{"version":0,"block":{"attributes":{"default_run_properties":{"type":["map","string"],"optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true}}}},"aws_guardduty_detector":{"version":0,"block":{"attributes":{"account_id":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"enable":{"type":"bool","optional":true},"finding_publishing_frequency":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_guardduty_invite_accepter":{"version":0,"block":{"attributes":{"detector_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"master_account_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_guardduty_ipset":{"version":0,"block":{"attributes":{"activate":{"type":"bool","required":true},"arn":{"type":"string","computed":true},"detector_id":{"type":"string","required":true},"format":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"location":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_guardduty_member":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"detector_id":{"type":"string","required":true},"disable_email_notification":{"type":"bool","optional":true},"email":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"invitation_message":{"type":"string","optional":true},"invite":{"type":"bool","optional":true},"relationship_status":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_guardduty_organization_admin_account":{"version":0,"block":{"attributes":{"admin_account_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_guardduty_organization_configuration":{"version":0,"block":{"attributes":{"auto_enable":{"type":"bool","required":true},"detector_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_guardduty_threatintelset":{"version":0,"block":{"attributes":{"activate":{"type":"bool","required":true},"arn":{"type":"string","computed":true},"detector_id":{"type":"string","required":true},"format":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"location":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_iam_access_key":{"version":0,"block":{"attributes":{"encrypted_secret":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key_fingerprint":{"type":"string","computed":true},"pgp_key":{"type":"string","optional":true},"secret":{"type":"string","computed":true,"sensitive":true},"ses_smtp_password_v4":{"type":"string","computed":true,"sensitive":true},"status":{"type":"string","optional":true,"computed":true},"user":{"type":"string","required":true}}}},"aws_iam_account_alias":{"version":0,"block":{"attributes":{"account_alias":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_iam_account_password_policy":{"version":0,"block":{"attributes":{"allow_users_to_change_password":{"type":"bool","optional":true},"expire_passwords":{"type":"bool","computed":true},"hard_expiry":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"max_password_age":{"type":"number","optional":true,"computed":true},"minimum_password_length":{"type":"number","optional":true},"password_reuse_prevention":{"type":"number","optional":true,"computed":true},"require_lowercase_characters":{"type":"bool","optional":true,"computed":true},"require_numbers":{"type":"bool","optional":true,"computed":true},"require_symbols":{"type":"bool","optional":true,"computed":true},"require_uppercase_characters":{"type":"bool","optional":true,"computed":true}}}},"aws_iam_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"path":{"type":"string","optional":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_group_membership":{"version":0,"block":{"attributes":{"group":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"users":{"type":["set","string"],"required":true}}}},"aws_iam_group_policy":{"version":0,"block":{"attributes":{"group":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","required":true}}}},"aws_iam_group_policy_attachment":{"version":0,"block":{"attributes":{"group":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy_arn":{"type":"string","required":true}}}},"aws_iam_instance_profile":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"create_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","optional":true},"role":{"type":"string","optional":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_openid_connect_provider":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"client_id_list":{"type":["list","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"thumbprint_list":{"type":["list","string"],"required":true},"url":{"type":"string","required":true}}}},"aws_iam_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","optional":true},"policy":{"type":"string","required":true}}}},"aws_iam_policy_attachment":{"version":0,"block":{"attributes":{"groups":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"policy_arn":{"type":"string","required":true},"roles":{"type":["set","string"],"optional":true},"users":{"type":["set","string"],"optional":true}}}},"aws_iam_role":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"assume_role_policy":{"type":"string","required":true},"create_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"force_detach_policies":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"max_session_duration":{"type":"number","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","optional":true},"permissions_boundary":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_role_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","required":true},"role":{"type":"string","required":true}}}},"aws_iam_role_policy_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy_arn":{"type":"string","required":true},"role":{"type":"string","required":true}}}},"aws_iam_saml_provider":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"saml_metadata_document":{"type":"string","required":true},"valid_until":{"type":"string","computed":true}}}},"aws_iam_server_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"certificate_body":{"type":"string","required":true},"certificate_chain":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","optional":true},"private_key":{"type":"string","required":true,"sensitive":true}}}},"aws_iam_service_linked_role":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_service_name":{"type":"string","required":true},"create_date":{"type":"string","computed":true},"custom_suffix":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"path":{"type":"string","computed":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_user":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"force_destroy":{"type":"bool","description":"Delete user even if it has non-Terraform-managed IAM access keys, login profile or MFA devices","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"path":{"type":"string","optional":true},"permissions_boundary":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_user_group_membership":{"version":0,"block":{"attributes":{"groups":{"type":["set","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"user":{"type":"string","required":true}}}},"aws_iam_user_login_profile":{"version":0,"block":{"attributes":{"encrypted_password":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key_fingerprint":{"type":"string","computed":true},"password_length":{"type":"number","optional":true},"password_reset_required":{"type":"bool","optional":true},"pgp_key":{"type":"string","required":true},"user":{"type":"string","required":true}}}},"aws_iam_user_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","required":true},"user":{"type":"string","required":true}}}},"aws_iam_user_policy_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy_arn":{"type":"string","required":true},"user":{"type":"string","required":true}}}},"aws_iam_user_ssh_key":{"version":0,"block":{"attributes":{"encoding":{"type":"string","required":true},"fingerprint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"public_key":{"type":"string","required":true},"ssh_public_key_id":{"type":"string","computed":true},"status":{"type":"string","optional":true,"computed":true},"username":{"type":"string","required":true}}}},"aws_inspector_assessment_target":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"resource_group_arn":{"type":"string","optional":true}}}},"aws_inspector_assessment_template":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"duration":{"type":"number","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"rules_package_arns":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"target_arn":{"type":"string","required":true}}}},"aws_inspector_resource_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"required":true}}}},"aws_instance":{"version":1,"block":{"attributes":{"ami":{"type":"string","required":true},"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","optional":true,"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"cpu_core_count":{"type":"number","optional":true,"computed":true},"cpu_threads_per_core":{"type":"number","optional":true,"computed":true},"disable_api_termination":{"type":"bool","optional":true},"ebs_optimized":{"type":"bool","optional":true},"get_password_data":{"type":"bool","optional":true},"hibernation":{"type":"bool","optional":true},"host_id":{"type":"string","optional":true,"computed":true},"iam_instance_profile":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_initiated_shutdown_behavior":{"type":"string","optional":true},"instance_state":{"type":"string","computed":true},"instance_type":{"type":"string","required":true},"ipv6_address_count":{"type":"number","optional":true,"computed":true},"ipv6_addresses":{"type":["list","string"],"optional":true,"computed":true},"key_name":{"type":"string","optional":true,"computed":true},"monitoring":{"type":"bool","optional":true},"outpost_arn":{"type":"string","computed":true},"password_data":{"type":"string","computed":true},"placement_group":{"type":"string","optional":true,"computed":true},"primary_network_interface_id":{"type":"string","computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","optional":true,"computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"secondary_private_ips":{"type":["set","string"],"optional":true,"computed":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"source_dest_check":{"type":"bool","optional":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"tenancy":{"type":"string","optional":true,"computed":true},"user_data":{"type":"string","optional":true},"user_data_base64":{"type":"string","optional":true},"volume_tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"credit_specification":{"nesting_mode":"list","block":{"attributes":{"cpu_credits":{"type":"string","optional":true}}},"max_items":1},"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"no_device":{"type":"bool","optional":true},"virtual_name":{"type":"string","optional":true}}}},"metadata_options":{"nesting_mode":"list","block":{"attributes":{"http_endpoint":{"type":"string","optional":true,"computed":true},"http_put_response_hop_limit":{"type":"number","optional":true,"computed":true},"http_tokens":{"type":"string","optional":true,"computed":true}}},"max_items":1},"network_interface":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_index":{"type":"number","required":true},"network_interface_id":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"list","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","computed":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_internet_gateway":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","optional":true}}}},"aws_iot_certificate":{"version":0,"block":{"attributes":{"active":{"type":"bool","required":true},"arn":{"type":"string","computed":true},"certificate_pem":{"type":"string","computed":true,"sensitive":true},"csr":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","computed":true,"sensitive":true},"public_key":{"type":"string","computed":true,"sensitive":true}}}},"aws_iot_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"default_version_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"policy":{"type":"string","required":true}}}},"aws_iot_policy_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true},"target":{"type":"string","required":true}}}},"aws_iot_role_alias":{"version":0,"block":{"attributes":{"alias":{"type":"string","required":true},"arn":{"type":"string","computed":true},"credential_duration":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","required":true}}}},"aws_iot_thing":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"attributes":{"type":["map","string"],"optional":true},"default_client_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"thing_type_name":{"type":"string","optional":true},"version":{"type":"number","computed":true}}}},"aws_iot_thing_principal_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"principal":{"type":"string","required":true},"thing":{"type":"string","required":true}}}},"aws_iot_thing_type":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deprecated":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"properties":{"nesting_mode":"list","block":{"attributes":{"description":{"type":"string","optional":true},"searchable_attributes":{"type":["set","string"],"optional":true,"computed":true}}},"max_items":1}}}},"aws_iot_topic_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"enabled":{"type":"bool","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"sql":{"type":"string","required":true},"sql_version":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"cloudwatch_alarm":{"nesting_mode":"set","block":{"attributes":{"alarm_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"state_reason":{"type":"string","required":true},"state_value":{"type":"string","required":true}}}},"cloudwatch_metric":{"nesting_mode":"set","block":{"attributes":{"metric_name":{"type":"string","required":true},"metric_namespace":{"type":"string","required":true},"metric_timestamp":{"type":"string","optional":true},"metric_unit":{"type":"string","required":true},"metric_value":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}}},"dynamodb":{"nesting_mode":"set","block":{"attributes":{"hash_key_field":{"type":"string","required":true},"hash_key_type":{"type":"string","optional":true},"hash_key_value":{"type":"string","required":true},"operation":{"type":"string","optional":true},"payload_field":{"type":"string","optional":true},"range_key_field":{"type":"string","optional":true},"range_key_type":{"type":"string","optional":true},"range_key_value":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"table_name":{"type":"string","required":true}}}},"dynamodbv2":{"nesting_mode":"set","block":{"attributes":{"role_arn":{"type":"string","required":true}},"block_types":{"put_item":{"nesting_mode":"list","block":{"attributes":{"table_name":{"type":"string","required":true}}},"max_items":1}}}},"elasticsearch":{"nesting_mode":"set","block":{"attributes":{"endpoint":{"type":"string","required":true},"id":{"type":"string","required":true},"index":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"type":{"type":"string","required":true}}}},"error_action":{"nesting_mode":"list","block":{"block_types":{"cloudwatch_alarm":{"nesting_mode":"list","block":{"attributes":{"alarm_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"state_reason":{"type":"string","required":true},"state_value":{"type":"string","required":true}}},"max_items":1},"cloudwatch_metric":{"nesting_mode":"list","block":{"attributes":{"metric_name":{"type":"string","required":true},"metric_namespace":{"type":"string","required":true},"metric_timestamp":{"type":"string","optional":true},"metric_unit":{"type":"string","required":true},"metric_value":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"dynamodb":{"nesting_mode":"list","block":{"attributes":{"hash_key_field":{"type":"string","required":true},"hash_key_type":{"type":"string","optional":true},"hash_key_value":{"type":"string","required":true},"operation":{"type":"string","optional":true},"payload_field":{"type":"string","optional":true},"range_key_field":{"type":"string","optional":true},"range_key_type":{"type":"string","optional":true},"range_key_value":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"table_name":{"type":"string","required":true}}},"max_items":1},"dynamodbv2":{"nesting_mode":"list","block":{"attributes":{"role_arn":{"type":"string","required":true}},"block_types":{"put_item":{"nesting_mode":"list","block":{"attributes":{"table_name":{"type":"string","required":true}}},"max_items":1}}},"max_items":1},"elasticsearch":{"nesting_mode":"list","block":{"attributes":{"endpoint":{"type":"string","required":true},"id":{"type":"string","required":true},"index":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"type":{"type":"string","required":true}}},"max_items":1},"firehose":{"nesting_mode":"list","block":{"attributes":{"delivery_stream_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"separator":{"type":"string","optional":true}}},"max_items":1},"iot_analytics":{"nesting_mode":"list","block":{"attributes":{"channel_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"iot_events":{"nesting_mode":"list","block":{"attributes":{"input_name":{"type":"string","required":true},"message_id":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"kinesis":{"nesting_mode":"list","block":{"attributes":{"partition_key":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"stream_name":{"type":"string","required":true}}},"max_items":1},"lambda":{"nesting_mode":"list","block":{"attributes":{"function_arn":{"type":"string","required":true}}},"max_items":1},"republish":{"nesting_mode":"list","block":{"attributes":{"qos":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"topic":{"type":"string","required":true}}},"max_items":1},"s3":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","required":true},"key":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"sns":{"nesting_mode":"list","block":{"attributes":{"message_format":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"target_arn":{"type":"string","required":true}}},"max_items":1},"sqs":{"nesting_mode":"list","block":{"attributes":{"queue_url":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"use_base64":{"type":"bool","required":true}}},"max_items":1},"step_functions":{"nesting_mode":"list","block":{"attributes":{"execution_name_prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"state_machine_name":{"type":"string","required":true}}},"max_items":1}}},"max_items":1},"firehose":{"nesting_mode":"set","block":{"attributes":{"delivery_stream_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"separator":{"type":"string","optional":true}}}},"iot_analytics":{"nesting_mode":"set","block":{"attributes":{"channel_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}}},"iot_events":{"nesting_mode":"set","block":{"attributes":{"input_name":{"type":"string","required":true},"message_id":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}}}},"kinesis":{"nesting_mode":"set","block":{"attributes":{"partition_key":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"stream_name":{"type":"string","required":true}}}},"lambda":{"nesting_mode":"set","block":{"attributes":{"function_arn":{"type":"string","required":true}}}},"republish":{"nesting_mode":"set","block":{"attributes":{"qos":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"topic":{"type":"string","required":true}}}},"s3":{"nesting_mode":"set","block":{"attributes":{"bucket_name":{"type":"string","required":true},"key":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}}},"sns":{"nesting_mode":"set","block":{"attributes":{"message_format":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"target_arn":{"type":"string","required":true}}}},"sqs":{"nesting_mode":"set","block":{"attributes":{"queue_url":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"use_base64":{"type":"bool","required":true}}}},"step_functions":{"nesting_mode":"set","block":{"attributes":{"execution_name_prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"state_machine_name":{"type":"string","required":true}}}}}}},"aws_key_pair":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"fingerprint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key_name":{"type":"string","optional":true,"computed":true},"key_name_prefix":{"type":"string","optional":true},"key_pair_id":{"type":"string","computed":true},"public_key":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_kinesis_analytics_application":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"code":{"type":"string","optional":true},"create_timestamp":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"last_update_timestamp":{"type":"string","computed":true},"name":{"type":"string","required":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"number","computed":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","computed":true},"log_stream_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"inputs":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","computed":true},"name_prefix":{"type":"string","required":true},"starting_position_configuration":{"type":["list",["object",{"starting_position":"string"}]],"computed":true},"stream_names":{"type":["set","string"],"computed":true}},"block_types":{"kinesis_firehose":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"kinesis_stream":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"parallelism":{"nesting_mode":"list","block":{"attributes":{"count":{"type":"number","required":true}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"block_types":{"lambda":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"max_items":1},"schema":{"nesting_mode":"list","block":{"attributes":{"record_encoding":{"type":"string","optional":true}},"block_types":{"record_columns":{"nesting_mode":"list","block":{"attributes":{"mapping":{"type":"string","optional":true},"name":{"type":"string","required":true},"sql_type":{"type":"string","required":true}}},"min_items":1},"record_format":{"nesting_mode":"list","block":{"attributes":{"record_format_type":{"type":"string","computed":true}},"block_types":{"mapping_parameters":{"nesting_mode":"list","block":{"block_types":{"csv":{"nesting_mode":"list","block":{"attributes":{"record_column_delimiter":{"type":"string","required":true},"record_row_delimiter":{"type":"string","required":true}}},"max_items":1},"json":{"nesting_mode":"list","block":{"attributes":{"record_row_path":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1},"outputs":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","computed":true},"name":{"type":"string","required":true}},"block_types":{"kinesis_firehose":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"kinesis_stream":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"lambda":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"schema":{"nesting_mode":"list","block":{"attributes":{"record_format_type":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"max_items":3},"reference_data_sources":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","computed":true},"table_name":{"type":"string","required":true}},"block_types":{"s3":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"file_key":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1},"schema":{"nesting_mode":"list","block":{"attributes":{"record_encoding":{"type":"string","optional":true}},"block_types":{"record_columns":{"nesting_mode":"list","block":{"attributes":{"mapping":{"type":"string","optional":true},"name":{"type":"string","required":true},"sql_type":{"type":"string","required":true}}},"min_items":1},"record_format":{"nesting_mode":"list","block":{"attributes":{"record_format_type":{"type":"string","computed":true}},"block_types":{"mapping_parameters":{"nesting_mode":"list","block":{"block_types":{"csv":{"nesting_mode":"list","block":{"attributes":{"record_column_delimiter":{"type":"string","required":true},"record_row_delimiter":{"type":"string","required":true}}},"max_items":1},"json":{"nesting_mode":"list","block":{"attributes":{"record_row_path":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1}}}},"aws_kinesis_firehose_delivery_stream":{"version":1,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"destination":{"type":"string","required":true},"destination_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"version_id":{"type":"string","optional":true,"computed":true}},"block_types":{"elasticsearch_configuration":{"nesting_mode":"list","block":{"attributes":{"buffering_interval":{"type":"number","optional":true},"buffering_size":{"type":"number","optional":true},"domain_arn":{"type":"string","required":true},"index_name":{"type":"string","required":true},"index_rotation_period":{"type":"string","optional":true},"retry_duration":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"s3_backup_mode":{"type":"string","optional":true},"type_name":{"type":"string","optional":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"processors":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"list","block":{"attributes":{"parameter_name":{"type":"string","required":true},"parameter_value":{"type":"string","required":true}}}}}}}}},"max_items":1}}},"max_items":1},"extended_s3_configuration":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"buffer_interval":{"type":"number","optional":true},"buffer_size":{"type":"number","optional":true},"compression_format":{"type":"string","optional":true},"error_output_prefix":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"s3_backup_mode":{"type":"string","optional":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1},"data_format_conversion_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"input_format_configuration":{"nesting_mode":"list","block":{"block_types":{"deserializer":{"nesting_mode":"list","block":{"block_types":{"hive_json_ser_de":{"nesting_mode":"list","block":{"attributes":{"timestamp_formats":{"type":["list","string"],"optional":true}}},"max_items":1},"open_x_json_ser_de":{"nesting_mode":"list","block":{"attributes":{"case_insensitive":{"type":"bool","optional":true},"column_to_json_key_mappings":{"type":["map","string"],"optional":true},"convert_dots_in_json_keys_to_underscores":{"type":"bool","optional":true}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"output_format_configuration":{"nesting_mode":"list","block":{"block_types":{"serializer":{"nesting_mode":"list","block":{"block_types":{"orc_ser_de":{"nesting_mode":"list","block":{"attributes":{"block_size_bytes":{"type":"number","optional":true},"bloom_filter_columns":{"type":["list","string"],"optional":true},"bloom_filter_false_positive_probability":{"type":"number","optional":true},"compression":{"type":"string","optional":true},"dictionary_key_threshold":{"type":"number","optional":true},"enable_padding":{"type":"bool","optional":true},"format_version":{"type":"string","optional":true},"padding_tolerance":{"type":"number","optional":true},"row_index_stride":{"type":"number","optional":true},"stripe_size_bytes":{"type":"number","optional":true}}},"max_items":1},"parquet_ser_de":{"nesting_mode":"list","block":{"attributes":{"block_size_bytes":{"type":"number","optional":true},"compression":{"type":"string","optional":true},"enable_dictionary_compression":{"type":"bool","optional":true},"max_padding_bytes":{"type":"number","optional":true},"page_size_bytes":{"type":"number","optional":true},"writer_version":{"type":"string","optional":true}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"schema_configuration":{"nesting_mode":"list","block":{"attributes":{"catalog_id":{"type":"string","optional":true,"computed":true},"database_name":{"type":"string","required":true},"region":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","required":true},"table_name":{"type":"string","required":true},"version_id":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"processors":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"list","block":{"attributes":{"parameter_name":{"type":"string","required":true},"parameter_value":{"type":"string","required":true}}}}}}}}},"max_items":1},"s3_backup_configuration":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"buffer_interval":{"type":"number","optional":true},"buffer_size":{"type":"number","optional":true},"compression_format":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}},"max_items":1},"kinesis_source_configuration":{"nesting_mode":"list","block":{"attributes":{"kinesis_stream_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"redshift_configuration":{"nesting_mode":"list","block":{"attributes":{"cluster_jdbcurl":{"type":"string","required":true},"copy_options":{"type":"string","optional":true},"data_table_columns":{"type":"string","optional":true},"data_table_name":{"type":"string","required":true},"password":{"type":"string","required":true,"sensitive":true},"retry_duration":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"s3_backup_mode":{"type":"string","optional":true},"username":{"type":"string","required":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"processors":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"list","block":{"attributes":{"parameter_name":{"type":"string","required":true},"parameter_value":{"type":"string","required":true}}}}}}}}},"max_items":1},"s3_backup_configuration":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"buffer_interval":{"type":"number","optional":true},"buffer_size":{"type":"number","optional":true},"compression_format":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}},"max_items":1},"s3_configuration":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"buffer_interval":{"type":"number","optional":true},"buffer_size":{"type":"number","optional":true},"compression_format":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"server_side_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}}},"max_items":1},"splunk_configuration":{"nesting_mode":"list","block":{"attributes":{"hec_acknowledgment_timeout":{"type":"number","optional":true},"hec_endpoint":{"type":"string","required":true},"hec_endpoint_type":{"type":"string","optional":true},"hec_token":{"type":"string","required":true},"retry_duration":{"type":"number","optional":true},"s3_backup_mode":{"type":"string","optional":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"processors":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"list","block":{"attributes":{"parameter_name":{"type":"string","required":true},"parameter_value":{"type":"string","required":true}}}}}}}}},"max_items":1}}},"max_items":1}}}},"aws_kinesis_stream":{"version":1,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"encryption_type":{"type":"string","optional":true},"enforce_consumer_deletion":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"name":{"type":"string","required":true},"retention_period":{"type":"number","optional":true},"shard_count":{"type":"number","required":true},"shard_level_metrics":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_kinesis_video_stream":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_time":{"type":"string","computed":true},"data_retention_in_hours":{"type":"number","optional":true},"device_name":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"media_type":{"type":"string","optional":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_kms_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"name_prefix":{"type":"string","optional":true},"target_key_arn":{"type":"string","computed":true},"target_key_id":{"type":"string","required":true}}}},"aws_kms_ciphertext":{"version":0,"block":{"attributes":{"ciphertext_blob":{"type":"string","computed":true},"context":{"type":["map","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"plaintext":{"type":"string","required":true,"sensitive":true}}}},"aws_kms_external_key":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deletion_window_in_days":{"type":"number","optional":true},"description":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true,"computed":true},"expiration_model":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key_material_base64":{"type":"string","optional":true,"sensitive":true},"key_state":{"type":"string","computed":true},"key_usage":{"type":"string","computed":true},"policy":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"valid_to":{"type":"string","optional":true}}}},"aws_kms_grant":{"version":0,"block":{"attributes":{"grant_creation_tokens":{"type":["set","string"],"optional":true},"grant_id":{"type":"string","computed":true},"grant_token":{"type":"string","computed":true},"grantee_principal":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"name":{"type":"string","optional":true},"operations":{"type":["set","string"],"required":true},"retire_on_delete":{"type":"bool","optional":true},"retiring_principal":{"type":"string","optional":true}},"block_types":{"constraints":{"nesting_mode":"set","block":{"attributes":{"encryption_context_equals":{"type":["map","string"],"optional":true},"encryption_context_subset":{"type":["map","string"],"optional":true}}}}}}},"aws_kms_key":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_master_key_spec":{"type":"string","optional":true},"deletion_window_in_days":{"type":"number","optional":true},"description":{"type":"string","optional":true,"computed":true},"enable_key_rotation":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"is_enabled":{"type":"bool","optional":true},"key_id":{"type":"string","computed":true},"key_usage":{"type":"string","optional":true},"policy":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_lambda_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"function_name":{"type":"string","required":true},"function_version":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"invoke_arn":{"type":"string","computed":true},"name":{"type":"string","required":true}},"block_types":{"routing_config":{"nesting_mode":"list","block":{"attributes":{"additional_version_weights":{"type":["map","number"],"optional":true}}},"max_items":1}}}},"aws_lambda_event_source_mapping":{"version":0,"block":{"attributes":{"batch_size":{"type":"number","optional":true},"bisect_batch_on_function_error":{"type":"bool","optional":true},"enabled":{"type":"bool","optional":true},"event_source_arn":{"type":"string","required":true},"function_arn":{"type":"string","computed":true},"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"last_modified":{"type":"string","computed":true},"last_processing_result":{"type":"string","computed":true},"maximum_batching_window_in_seconds":{"type":"number","optional":true},"maximum_record_age_in_seconds":{"type":"number","optional":true,"computed":true},"maximum_retry_attempts":{"type":"number","optional":true,"computed":true},"parallelization_factor":{"type":"number","optional":true,"computed":true},"starting_position":{"type":"string","optional":true},"starting_position_timestamp":{"type":"string","optional":true},"state":{"type":"string","computed":true},"state_transition_reason":{"type":"string","computed":true},"uuid":{"type":"string","computed":true}},"block_types":{"destination_config":{"nesting_mode":"list","block":{"block_types":{"on_failure":{"nesting_mode":"list","block":{"attributes":{"destination_arn":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}}},"aws_lambda_function":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"filename":{"type":"string","optional":true},"function_name":{"type":"string","required":true},"handler":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"invoke_arn":{"type":"string","computed":true},"kms_key_arn":{"type":"string","optional":true},"last_modified":{"type":"string","computed":true},"layers":{"type":["list","string"],"optional":true},"memory_size":{"type":"number","optional":true},"publish":{"type":"bool","optional":true},"qualified_arn":{"type":"string","computed":true},"reserved_concurrent_executions":{"type":"number","optional":true},"role":{"type":"string","required":true},"runtime":{"type":"string","required":true},"s3_bucket":{"type":"string","optional":true},"s3_key":{"type":"string","optional":true},"s3_object_version":{"type":"string","optional":true},"source_code_hash":{"type":"string","optional":true,"computed":true},"source_code_size":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true},"timeout":{"type":"number","optional":true},"version":{"type":"string","computed":true}},"block_types":{"dead_letter_config":{"nesting_mode":"list","block":{"attributes":{"target_arn":{"type":"string","required":true}}},"max_items":1},"environment":{"nesting_mode":"list","block":{"attributes":{"variables":{"type":["map","string"],"optional":true}}},"max_items":1},"file_system_config":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true},"local_mount_path":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}},"tracing_config":{"nesting_mode":"list","block":{"attributes":{"mode":{"type":"string","required":true}}},"max_items":1},"vpc_config":{"nesting_mode":"list","block":{"attributes":{"security_group_ids":{"type":["set","string"],"required":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","computed":true}}},"max_items":1}}}},"aws_lambda_function_event_invoke_config":{"version":0,"block":{"attributes":{"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"maximum_event_age_in_seconds":{"type":"number","optional":true},"maximum_retry_attempts":{"type":"number","optional":true},"qualifier":{"type":"string","optional":true}},"block_types":{"destination_config":{"nesting_mode":"list","block":{"block_types":{"on_failure":{"nesting_mode":"list","block":{"attributes":{"destination":{"type":"string","required":true}}},"max_items":1},"on_success":{"nesting_mode":"list","block":{"attributes":{"destination":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}}},"aws_lambda_layer_version":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compatible_runtimes":{"type":["set","string"],"optional":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"filename":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"layer_arn":{"type":"string","computed":true},"layer_name":{"type":"string","required":true},"license_info":{"type":"string","optional":true},"s3_bucket":{"type":"string","optional":true},"s3_key":{"type":"string","optional":true},"s3_object_version":{"type":"string","optional":true},"source_code_hash":{"type":"string","optional":true,"computed":true},"source_code_size":{"type":"number","computed":true},"version":{"type":"string","computed":true}}}},"aws_lambda_permission":{"version":0,"block":{"attributes":{"action":{"type":"string","required":true},"event_source_token":{"type":"string","optional":true},"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"principal":{"type":"string","required":true},"qualifier":{"type":"string","optional":true},"source_account":{"type":"string","optional":true},"source_arn":{"type":"string","optional":true},"statement_id":{"type":"string","optional":true,"computed":true},"statement_id_prefix":{"type":"string","optional":true}}}},"aws_lambda_provisioned_concurrency_config":{"version":0,"block":{"attributes":{"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"provisioned_concurrent_executions":{"type":"number","required":true},"qualifier":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_launch_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","optional":true},"ebs_optimized":{"type":"bool","optional":true,"computed":true},"enable_monitoring":{"type":"bool","optional":true},"iam_instance_profile":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","required":true},"instance_type":{"type":"string","required":true},"key_name":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"placement_tenancy":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true},"spot_price":{"type":"string","optional":true},"user_data":{"type":"string","optional":true},"user_data_base64":{"type":"string","optional":true},"vpc_classic_link_id":{"type":"string","optional":true},"vpc_classic_link_security_groups":{"type":["set","string"],"optional":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"no_device":{"type":"bool","optional":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"virtual_name":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"list","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1}}}},"aws_launch_template":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"default_version":{"type":"number","optional":true,"computed":true},"description":{"type":"string","optional":true},"disable_api_termination":{"type":"bool","optional":true},"ebs_optimized":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","optional":true},"instance_initiated_shutdown_behavior":{"type":"string","optional":true},"instance_type":{"type":"string","optional":true},"kernel_id":{"type":"string","optional":true},"key_name":{"type":"string","optional":true},"latest_version":{"type":"number","computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"ram_disk_id":{"type":"string","optional":true},"security_group_names":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"update_default_version":{"type":"bool","optional":true},"user_data":{"type":"string","optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true}},"block_types":{"block_device_mappings":{"nesting_mode":"list","block":{"attributes":{"device_name":{"type":"string","optional":true},"no_device":{"type":"string","optional":true},"virtual_name":{"type":"string","optional":true}},"block_types":{"ebs":{"nesting_mode":"list","block":{"attributes":{"delete_on_termination":{"type":"string","optional":true},"encrypted":{"type":"string","optional":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"snapshot_id":{"type":"string","optional":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1}}}},"capacity_reservation_specification":{"nesting_mode":"list","block":{"attributes":{"capacity_reservation_preference":{"type":"string","optional":true}},"block_types":{"capacity_reservation_target":{"nesting_mode":"list","block":{"attributes":{"capacity_reservation_id":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"cpu_options":{"nesting_mode":"list","block":{"attributes":{"core_count":{"type":"number","optional":true},"threads_per_core":{"type":"number","optional":true}}},"max_items":1},"credit_specification":{"nesting_mode":"list","block":{"attributes":{"cpu_credits":{"type":"string","optional":true}}},"max_items":1},"elastic_gpu_specifications":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}}},"elastic_inference_accelerator":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1},"hibernation_options":{"nesting_mode":"list","block":{"attributes":{"configured":{"type":"bool","required":true}}},"max_items":1},"iam_instance_profile":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","optional":true},"name":{"type":"string","optional":true}}},"max_items":1},"instance_market_options":{"nesting_mode":"list","block":{"attributes":{"market_type":{"type":"string","optional":true}},"block_types":{"spot_options":{"nesting_mode":"list","block":{"attributes":{"block_duration_minutes":{"type":"number","optional":true},"instance_interruption_behavior":{"type":"string","optional":true},"max_price":{"type":"string","optional":true},"spot_instance_type":{"type":"string","optional":true},"valid_until":{"type":"string","optional":true,"computed":true}}},"max_items":1}}},"max_items":1},"license_specification":{"nesting_mode":"set","block":{"attributes":{"license_configuration_arn":{"type":"string","required":true}}}},"metadata_options":{"nesting_mode":"list","block":{"attributes":{"http_endpoint":{"type":"string","optional":true,"computed":true},"http_put_response_hop_limit":{"type":"number","optional":true,"computed":true},"http_tokens":{"type":"string","optional":true,"computed":true}}},"max_items":1},"monitoring":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}}},"max_items":1},"network_interfaces":{"nesting_mode":"list","block":{"attributes":{"associate_public_ip_address":{"type":"string","optional":true},"delete_on_termination":{"type":"string","optional":true},"description":{"type":"string","optional":true},"device_index":{"type":"number","optional":true},"ipv4_address_count":{"type":"number","optional":true},"ipv4_addresses":{"type":["set","string"],"optional":true},"ipv6_address_count":{"type":"number","optional":true},"ipv6_addresses":{"type":["set","string"],"optional":true},"network_interface_id":{"type":"string","optional":true},"private_ip_address":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true},"subnet_id":{"type":"string","optional":true}}}},"placement":{"nesting_mode":"list","block":{"attributes":{"affinity":{"type":"string","optional":true},"availability_zone":{"type":"string","optional":true},"group_name":{"type":"string","optional":true},"host_id":{"type":"string","optional":true},"partition_number":{"type":"number","optional":true},"spread_domain":{"type":"string","optional":true},"tenancy":{"type":"string","optional":true}}},"max_items":1},"tag_specifications":{"nesting_mode":"list","block":{"attributes":{"resource_type":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}}}}},"aws_lb":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"arn_suffix":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"drop_invalid_header_fields":{"type":"bool","optional":true},"enable_cross_zone_load_balancing":{"type":"bool","optional":true},"enable_deletion_protection":{"type":"bool","optional":true},"enable_http2":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","optional":true},"internal":{"type":"bool","optional":true,"computed":true},"ip_address_type":{"type":"string","optional":true,"computed":true},"load_balancer_type":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"subnets":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"zone_id":{"type":"string","computed":true}},"block_types":{"access_logs":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"prefix":{"type":"string","optional":true}}},"max_items":1},"subnet_mapping":{"nesting_mode":"set","block":{"attributes":{"allocation_id":{"type":"string","optional":true},"private_ipv4_address":{"type":"string","optional":true},"subnet_id":{"type":"string","required":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_lb_cookie_stickiness_policy":{"version":0,"block":{"attributes":{"cookie_expiration_period":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"lb_port":{"type":"number","required":true},"load_balancer":{"type":"string","required":true},"name":{"type":"string","required":true}}}},"aws_lb_listener":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"load_balancer_arn":{"type":"string","required":true},"port":{"type":"number","required":true},"protocol":{"type":"string","optional":true},"ssl_policy":{"type":"string","optional":true,"computed":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"attributes":{"order":{"type":"number","optional":true,"computed":true},"target_group_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"authenticate_cognito":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"user_pool_arn":{"type":"string","required":true},"user_pool_client_id":{"type":"string","required":true},"user_pool_domain":{"type":"string","required":true}}},"max_items":1},"authenticate_oidc":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"authorization_endpoint":{"type":"string","required":true},"client_id":{"type":"string","required":true},"client_secret":{"type":"string","required":true,"sensitive":true},"issuer":{"type":"string","required":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"token_endpoint":{"type":"string","required":true},"user_info_endpoint":{"type":"string","required":true}}},"max_items":1},"fixed_response":{"nesting_mode":"list","block":{"attributes":{"content_type":{"type":"string","required":true},"message_body":{"type":"string","optional":true},"status_code":{"type":"string","optional":true,"computed":true}}},"max_items":1},"forward":{"nesting_mode":"list","block":{"block_types":{"stickiness":{"nesting_mode":"list","block":{"attributes":{"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1},"target_group":{"nesting_mode":"set","block":{"attributes":{"arn":{"type":"string","required":true},"weight":{"type":"number","optional":true}}},"min_items":2,"max_items":5}}},"max_items":1},"redirect":{"nesting_mode":"list","block":{"attributes":{"host":{"type":"string","optional":true},"path":{"type":"string","optional":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"query":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"read":{"type":"string","optional":true}}}}}}},"aws_lb_listener_certificate":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true}}}},"aws_lb_listener_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true},"priority":{"type":"number","optional":true,"computed":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"order":{"type":"number","optional":true,"computed":true},"target_group_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"authenticate_cognito":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"user_pool_arn":{"type":"string","required":true},"user_pool_client_id":{"type":"string","required":true},"user_pool_domain":{"type":"string","required":true}}},"max_items":1},"authenticate_oidc":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"authorization_endpoint":{"type":"string","required":true},"client_id":{"type":"string","required":true},"client_secret":{"type":"string","required":true,"sensitive":true},"issuer":{"type":"string","required":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"token_endpoint":{"type":"string","required":true},"user_info_endpoint":{"type":"string","required":true}}},"max_items":1},"fixed_response":{"nesting_mode":"list","block":{"attributes":{"content_type":{"type":"string","required":true},"message_body":{"type":"string","optional":true},"status_code":{"type":"string","optional":true,"computed":true}}},"max_items":1},"forward":{"nesting_mode":"list","block":{"block_types":{"stickiness":{"nesting_mode":"list","block":{"attributes":{"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1},"target_group":{"nesting_mode":"set","block":{"attributes":{"arn":{"type":"string","required":true},"weight":{"type":"number","optional":true}}},"min_items":2,"max_items":5}}},"max_items":1},"redirect":{"nesting_mode":"list","block":{"attributes":{"host":{"type":"string","optional":true},"path":{"type":"string","optional":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"query":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"condition":{"nesting_mode":"set","block":{"block_types":{"host_header":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"http_header":{"nesting_mode":"list","block":{"attributes":{"http_header_name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}},"max_items":1},"http_request_method":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"path_pattern":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"query_string":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"value":{"type":"string","required":true}}}},"source_ip":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1}}},"min_items":1}}}},"aws_lb_ssl_negotiation_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"lb_port":{"type":"number","required":true},"load_balancer":{"type":"string","required":true},"name":{"type":"string","required":true}},"block_types":{"attribute":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_lb_target_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"arn_suffix":{"type":"string","computed":true},"deregistration_delay":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"lambda_multi_value_headers_enabled":{"type":"bool","optional":true},"load_balancing_algorithm_type":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"port":{"type":"number","optional":true},"protocol":{"type":"string","optional":true},"proxy_protocol_v2":{"type":"bool","optional":true},"slow_start":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true},"target_type":{"type":"string","optional":true},"vpc_id":{"type":"string","optional":true}},"block_types":{"health_check":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"healthy_threshold":{"type":"number","optional":true},"interval":{"type":"number","optional":true},"matcher":{"type":"string","optional":true,"computed":true},"path":{"type":"string","optional":true,"computed":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"timeout":{"type":"number","optional":true,"computed":true},"unhealthy_threshold":{"type":"number","optional":true}}},"max_items":1},"stickiness":{"nesting_mode":"list","block":{"attributes":{"cookie_duration":{"type":"number","optional":true},"enabled":{"type":"bool","optional":true},"type":{"type":"string","required":true}}},"max_items":1}}}},"aws_lb_target_group_attachment":{"version":0,"block":{"attributes":{"availability_zone":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"target_group_arn":{"type":"string","required":true},"target_id":{"type":"string","required":true}}}},"aws_licensemanager_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"license_configuration_arn":{"type":"string","required":true},"resource_arn":{"type":"string","required":true}}}},"aws_licensemanager_license_configuration":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"license_count":{"type":"number","optional":true},"license_count_hard_limit":{"type":"bool","optional":true},"license_counting_type":{"type":"string","required":true},"license_rules":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_lightsail_domain":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_lightsail_instance":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","required":true},"blueprint_id":{"type":"string","required":true},"bundle_id":{"type":"string","required":true},"cpu_count":{"type":"number","computed":true},"created_at":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_address":{"type":"string","computed":true},"is_static_ip":{"type":"bool","computed":true},"key_pair_name":{"type":"string","optional":true},"name":{"type":"string","required":true},"private_ip_address":{"type":"string","computed":true},"public_ip_address":{"type":"string","computed":true},"ram_size":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true},"user_data":{"type":"string","optional":true},"username":{"type":"string","computed":true}}}},"aws_lightsail_key_pair":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"encrypted_fingerprint":{"type":"string","computed":true},"encrypted_private_key":{"type":"string","computed":true},"fingerprint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"pgp_key":{"type":"string","optional":true},"private_key":{"type":"string","computed":true},"public_key":{"type":"string","optional":true,"computed":true}}}},"aws_lightsail_static_ip":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","computed":true},"name":{"type":"string","required":true},"support_code":{"type":"string","computed":true}}}},"aws_lightsail_static_ip_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_name":{"type":"string","required":true},"ip_address":{"type":"string","computed":true},"static_ip_name":{"type":"string","required":true}}}},"aws_load_balancer_backend_server_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_port":{"type":"number","required":true},"load_balancer_name":{"type":"string","required":true},"policy_names":{"type":["set","string"],"optional":true}}}},"aws_load_balancer_listener_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"load_balancer_name":{"type":"string","required":true},"load_balancer_port":{"type":"number","required":true},"policy_names":{"type":["set","string"],"optional":true}}}},"aws_load_balancer_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"load_balancer_name":{"type":"string","required":true},"policy_name":{"type":"string","required":true},"policy_type_name":{"type":"string","required":true}},"block_types":{"policy_attribute":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","optional":true},"value":{"type":"string","optional":true}}}}}}},"aws_macie_member_account_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"member_account_id":{"type":"string","required":true}}}},"aws_macie_s3_bucket_association":{"version":0,"block":{"attributes":{"bucket_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"member_account_id":{"type":"string","optional":true},"prefix":{"type":"string","optional":true}},"block_types":{"classification_type":{"nesting_mode":"list","block":{"attributes":{"continuous":{"type":"string","optional":true},"one_time":{"type":"string","optional":true}}},"max_items":1}}}},"aws_main_route_table_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"original_route_table_id":{"type":"string","computed":true},"route_table_id":{"type":"string","required":true},"vpc_id":{"type":"string","required":true}}}},"aws_media_convert_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"pricing_plan":{"type":"string","optional":true},"status":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"reservation_plan_settings":{"nesting_mode":"list","block":{"attributes":{"commitment":{"type":"string","required":true},"renewal_type":{"type":"string","required":true},"reserved_slots":{"type":"number","required":true}}},"max_items":1}}}},"aws_media_package_channel":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"channel_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"hls_ingest":{"type":["list",["object",{"ingest_endpoints":["list",["object",{"password":"string","url":"string","username":"string"}]]}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_media_store_container":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_media_store_container_policy":{"version":0,"block":{"attributes":{"container_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true}}}},"aws_mq_broker":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"broker_name":{"type":"string","required":true},"deployment_mode":{"type":"string","optional":true},"engine_type":{"type":"string","required":true},"engine_version":{"type":"string","required":true},"host_instance_type":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instances":{"type":["list",["object",{"console_url":"string","endpoints":["list","string"],"ip_address":"string"}]],"computed":true},"publicly_accessible":{"type":"bool","optional":true},"security_groups":{"type":["set","string"],"required":true},"subnet_ids":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"configuration":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"revision":{"type":"number","optional":true,"computed":true}}},"max_items":1},"encryption_options":{"nesting_mode":"list","block":{"attributes":{"kms_key_id":{"type":"string","optional":true,"computed":true},"use_aws_owned_key":{"type":"bool","optional":true}}},"max_items":1},"logs":{"nesting_mode":"list","block":{"attributes":{"audit":{"type":"bool","optional":true},"general":{"type":"bool","optional":true}}},"max_items":1},"maintenance_window_start_time":{"nesting_mode":"list","block":{"attributes":{"day_of_week":{"type":"string","required":true},"time_of_day":{"type":"string","required":true},"time_zone":{"type":"string","required":true}}},"max_items":1},"user":{"nesting_mode":"set","block":{"attributes":{"console_access":{"type":"bool","optional":true},"groups":{"type":["set","string"],"optional":true},"password":{"type":"string","required":true,"sensitive":true},"username":{"type":"string","required":true}}},"min_items":1}}}},"aws_mq_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"data":{"type":"string","required":true},"description":{"type":"string","optional":true},"engine_type":{"type":"string","required":true},"engine_version":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"latest_revision":{"type":"number","computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_msk_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bootstrap_brokers":{"type":"string","computed":true},"bootstrap_brokers_tls":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"current_version":{"type":"string","computed":true},"enhanced_monitoring":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"kafka_version":{"type":"string","required":true},"number_of_broker_nodes":{"type":"number","required":true},"tags":{"type":["map","string"],"optional":true},"zookeeper_connect_string":{"type":"string","computed":true}},"block_types":{"broker_node_group_info":{"nesting_mode":"list","block":{"attributes":{"az_distribution":{"type":"string","optional":true},"client_subnets":{"type":["list","string"],"required":true},"ebs_volume_size":{"type":"number","required":true},"instance_type":{"type":"string","required":true},"security_groups":{"type":["list","string"],"required":true}}},"min_items":1,"max_items":1},"client_authentication":{"nesting_mode":"list","block":{"block_types":{"tls":{"nesting_mode":"list","block":{"attributes":{"certificate_authority_arns":{"type":["set","string"],"optional":true}}},"max_items":1}}},"max_items":1},"configuration_info":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true},"revision":{"type":"number","required":true}}},"max_items":1},"encryption_info":{"nesting_mode":"list","block":{"attributes":{"encryption_at_rest_kms_key_arn":{"type":"string","optional":true,"computed":true}},"block_types":{"encryption_in_transit":{"nesting_mode":"list","block":{"attributes":{"client_broker":{"type":"string","optional":true},"in_cluster":{"type":"bool","optional":true}}},"max_items":1}}},"max_items":1},"logging_info":{"nesting_mode":"list","block":{"block_types":{"broker_logs":{"nesting_mode":"list","block":{"block_types":{"cloudwatch_logs":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true},"log_group":{"type":"string","optional":true}}},"max_items":1},"firehose":{"nesting_mode":"list","block":{"attributes":{"delivery_stream":{"type":"string","optional":true},"enabled":{"type":"bool","required":true}}},"max_items":1},"s3":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","optional":true},"enabled":{"type":"bool","required":true},"prefix":{"type":"string","optional":true}}},"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1},"open_monitoring":{"nesting_mode":"list","block":{"block_types":{"prometheus":{"nesting_mode":"list","block":{"block_types":{"jmx_exporter":{"nesting_mode":"list","block":{"attributes":{"enabled_in_broker":{"type":"bool","required":true}}},"max_items":1},"node_exporter":{"nesting_mode":"list","block":{"attributes":{"enabled_in_broker":{"type":"bool","required":true}}},"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1}}}},"aws_msk_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"kafka_versions":{"type":["set","string"],"required":true},"latest_revision":{"type":"number","computed":true},"name":{"type":"string","required":true},"server_properties":{"type":"string","required":true}}}},"aws_nat_gateway":{"version":0,"block":{"attributes":{"allocation_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"subnet_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_neptune_cluster":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"backup_retention_period":{"type":"number","optional":true},"cluster_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier_prefix":{"type":"string","optional":true,"computed":true},"cluster_members":{"type":["set","string"],"computed":true},"cluster_resource_id":{"type":"string","computed":true},"deletion_protection":{"type":"bool","optional":true},"enable_cloudwatch_logs_exports":{"type":["set","string"],"optional":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"hosted_zone_id":{"type":"string","computed":true},"iam_database_authentication_enabled":{"type":"bool","optional":true},"iam_roles":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true,"computed":true},"neptune_cluster_parameter_group_name":{"type":"string","optional":true},"neptune_subnet_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"reader_endpoint":{"type":"string","computed":true},"replication_source_identifier":{"type":"string","optional":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_identifier":{"type":"string","optional":true},"storage_encrypted":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_neptune_cluster_instance":{"version":0,"block":{"attributes":{"address":{"type":"string","computed":true},"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"cluster_identifier":{"type":"string","required":true},"dbi_resource_id":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"instance_class":{"type":"string","required":true},"kms_key_arn":{"type":"string","computed":true},"neptune_parameter_group_name":{"type":"string","optional":true},"neptune_subnet_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"promotion_tier":{"type":"number","optional":true},"publicly_accessible":{"type":"bool","optional":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true},"writer":{"type":"bool","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_neptune_cluster_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_neptune_cluster_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zones":{"type":["list","string"],"computed":true},"db_cluster_identifier":{"type":"string","required":true},"db_cluster_snapshot_arn":{"type":"string","computed":true},"db_cluster_snapshot_identifier":{"type":"string","required":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_type":{"type":"string","computed":true},"source_db_cluster_snapshot_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_neptune_event_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_aws_id":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"event_categories":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"sns_topic_arn":{"type":"string","required":true},"source_ids":{"type":["set","string"],"optional":true},"source_type":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_neptune_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_neptune_subnet_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_network_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"egress":{"type":["set",["object",{"action":"string","cidr_block":"string","from_port":"number","icmp_code":"number","icmp_type":"number","ipv6_cidr_block":"string","protocol":"string","rule_no":"number","to_port":"number"}]],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ingress":{"type":["set",["object",{"action":"string","cidr_block":"string","from_port":"number","icmp_code":"number","icmp_type":"number","ipv6_cidr_block":"string","protocol":"string","rule_no":"number","to_port":"number"}]],"optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}}}},"aws_network_acl_rule":{"version":0,"block":{"attributes":{"cidr_block":{"type":"string","optional":true},"egress":{"type":"bool","optional":true},"from_port":{"type":"number","optional":true},"icmp_code":{"type":"string","optional":true},"icmp_type":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block":{"type":"string","optional":true},"network_acl_id":{"type":"string","required":true},"protocol":{"type":"string","required":true},"rule_action":{"type":"string","required":true},"rule_number":{"type":"number","required":true},"to_port":{"type":"number","optional":true}}}},"aws_network_interface":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"mac_address":{"type":"string","computed":true},"outpost_arn":{"type":"string","computed":true},"private_dns_name":{"type":"string","computed":true},"private_ip":{"type":"string","optional":true,"computed":true},"private_ips":{"type":["set","string"],"optional":true,"computed":true},"private_ips_count":{"type":"number","optional":true,"computed":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"source_dest_check":{"type":"bool","optional":true},"subnet_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"attachment":{"nesting_mode":"set","block":{"attributes":{"attachment_id":{"type":"string","computed":true},"device_index":{"type":"number","required":true},"instance":{"type":"string","required":true}}}}}}},"aws_network_interface_attachment":{"version":0,"block":{"attributes":{"attachment_id":{"type":"string","computed":true},"device_index":{"type":"number","required":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","required":true},"network_interface_id":{"type":"string","required":true},"status":{"type":"string","computed":true}}}},"aws_network_interface_sg_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","required":true},"security_group_id":{"type":"string","required":true}}}},"aws_opsworks_application":{"version":0,"block":{"attributes":{"auto_bundle_on_deploy":{"type":"string","optional":true},"aws_flow_ruby_settings":{"type":"string","optional":true},"data_source_arn":{"type":"string","optional":true},"data_source_database_name":{"type":"string","optional":true},"data_source_type":{"type":"string","optional":true},"description":{"type":"string","optional":true},"document_root":{"type":"string","optional":true},"domains":{"type":["list","string"],"optional":true},"enable_ssl":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"rails_env":{"type":"string","optional":true},"short_name":{"type":"string","optional":true,"computed":true},"stack_id":{"type":"string","required":true},"type":{"type":"string","required":true}},"block_types":{"app_source":{"nesting_mode":"list","block":{"attributes":{"password":{"type":"string","optional":true,"sensitive":true},"revision":{"type":"string","optional":true},"ssh_key":{"type":"string","optional":true,"sensitive":true},"type":{"type":"string","required":true},"url":{"type":"string","optional":true},"username":{"type":"string","optional":true}}}},"environment":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","required":true},"secure":{"type":"bool","optional":true},"value":{"type":"string","required":true}}}},"ssl_configuration":{"nesting_mode":"list","block":{"attributes":{"certificate":{"type":"string","required":true},"chain":{"type":"string","optional":true},"private_key":{"type":"string","required":true,"sensitive":true}}}}}}},"aws_opsworks_custom_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","required":true},"short_name":{"type":"string","required":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_ganglia_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"password":{"type":"string","required":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"url":{"type":"string","optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true},"username":{"type":"string","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_haproxy_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"healthcheck_method":{"type":"string","optional":true},"healthcheck_url":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"stats_enabled":{"type":"bool","optional":true},"stats_password":{"type":"string","required":true},"stats_url":{"type":"string","optional":true},"stats_user":{"type":"string","optional":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_instance":{"version":0,"block":{"attributes":{"agent_version":{"type":"string","optional":true},"ami_id":{"type":"string","optional":true,"computed":true},"architecture":{"type":"string","optional":true},"auto_scaling_type":{"type":"string","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"created_at":{"type":"string","optional":true,"computed":true},"delete_ebs":{"type":"bool","optional":true},"delete_eip":{"type":"bool","optional":true},"ebs_optimized":{"type":"bool","optional":true},"ec2_instance_id":{"type":"string","computed":true},"ecs_cluster_arn":{"type":"string","optional":true,"computed":true},"elastic_ip":{"type":"string","optional":true,"computed":true},"hostname":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"infrastructure_class":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_profile_arn":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","optional":true},"last_service_error_id":{"type":"string","optional":true,"computed":true},"layer_ids":{"type":["list","string"],"required":true},"os":{"type":"string","optional":true,"computed":true},"platform":{"type":"string","optional":true,"computed":true},"private_dns":{"type":"string","optional":true,"computed":true},"private_ip":{"type":"string","optional":true,"computed":true},"public_dns":{"type":"string","optional":true,"computed":true},"public_ip":{"type":"string","optional":true,"computed":true},"registered_by":{"type":"string","optional":true,"computed":true},"reported_agent_version":{"type":"string","optional":true,"computed":true},"reported_os_family":{"type":"string","optional":true,"computed":true},"reported_os_name":{"type":"string","optional":true,"computed":true},"reported_os_version":{"type":"string","optional":true,"computed":true},"root_device_type":{"type":"string","optional":true,"computed":true},"root_device_volume_id":{"type":"string","optional":true,"computed":true},"security_group_ids":{"type":["list","string"],"optional":true,"computed":true},"ssh_host_dsa_key_fingerprint":{"type":"string","optional":true,"computed":true},"ssh_host_rsa_key_fingerprint":{"type":"string","optional":true,"computed":true},"ssh_key_name":{"type":"string","optional":true,"computed":true},"stack_id":{"type":"string","required":true},"state":{"type":"string","optional":true},"status":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tenancy":{"type":"string","optional":true,"computed":true},"virtualization_type":{"type":"string","optional":true,"computed":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"iops":{"type":"number","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"virtual_name":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"iops":{"type":"number","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_opsworks_java_app_layer":{"version":0,"block":{"attributes":{"app_server":{"type":"string","optional":true},"app_server_version":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"jvm_options":{"type":"string","optional":true},"jvm_type":{"type":"string","optional":true},"jvm_version":{"type":"string","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_memcached_layer":{"version":0,"block":{"attributes":{"allocated_memory":{"type":"number","optional":true},"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_mysql_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"root_password":{"type":"string","optional":true},"root_password_on_all_instances":{"type":"bool","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_nodejs_app_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"nodejs_version":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_permission":{"version":0,"block":{"attributes":{"allow_ssh":{"type":"bool","optional":true,"computed":true},"allow_sudo":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"level":{"type":"string","optional":true,"computed":true},"stack_id":{"type":"string","optional":true,"computed":true},"user_arn":{"type":"string","required":true}}}},"aws_opsworks_php_app_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_rails_app_layer":{"version":0,"block":{"attributes":{"app_server":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"bundler_version":{"type":"string","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"manage_bundler":{"type":"bool","optional":true},"name":{"type":"string","optional":true},"passenger_version":{"type":"string","optional":true},"ruby_version":{"type":"string","optional":true},"rubygems_version":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_rds_db_instance":{"version":0,"block":{"attributes":{"db_password":{"type":"string","required":true,"sensitive":true},"db_user":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"rds_db_instance_arn":{"type":"string","required":true},"stack_id":{"type":"string","required":true}}}},"aws_opsworks_stack":{"version":0,"block":{"attributes":{"agent_version":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"berkshelf_version":{"type":"string","optional":true},"color":{"type":"string","optional":true},"configuration_manager_name":{"type":"string","optional":true},"configuration_manager_version":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"default_availability_zone":{"type":"string","optional":true,"computed":true},"default_instance_profile_arn":{"type":"string","required":true},"default_os":{"type":"string","optional":true},"default_root_device_type":{"type":"string","optional":true},"default_ssh_key_name":{"type":"string","optional":true},"default_subnet_id":{"type":"string","optional":true,"computed":true},"hostname_theme":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"manage_berkshelf":{"type":"bool","optional":true},"name":{"type":"string","required":true},"region":{"type":"string","required":true},"service_role_arn":{"type":"string","required":true},"stack_endpoint":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"use_custom_cookbooks":{"type":"bool","optional":true},"use_opsworks_security_groups":{"type":"bool","optional":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"custom_cookbooks_source":{"nesting_mode":"list","block":{"attributes":{"password":{"type":"string","optional":true,"sensitive":true},"revision":{"type":"string","optional":true},"ssh_key":{"type":"string","optional":true,"sensitive":true},"type":{"type":"string","required":true},"url":{"type":"string","required":true},"username":{"type":"string","optional":true}}}}}}},"aws_opsworks_static_web_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_user_profile":{"version":0,"block":{"attributes":{"allow_self_management":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"ssh_public_key":{"type":"string","optional":true},"ssh_username":{"type":"string","required":true},"user_arn":{"type":"string","required":true}}}},"aws_organizations_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"email":{"type":"string","required":true},"iam_user_access_to_billing":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"joined_method":{"type":"string","computed":true},"joined_timestamp":{"type":"string","computed":true},"name":{"type":"string","required":true},"parent_id":{"type":"string","optional":true,"computed":true},"role_name":{"type":"string","optional":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_organizations_organization":{"version":0,"block":{"attributes":{"accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string","status":"string"}]],"computed":true},"arn":{"type":"string","computed":true},"aws_service_access_principals":{"type":["set","string"],"optional":true},"enabled_policy_types":{"type":["set","string"],"optional":true},"feature_set":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"master_account_arn":{"type":"string","computed":true},"master_account_email":{"type":"string","computed":true},"master_account_id":{"type":"string","computed":true},"non_master_accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string","status":"string"}]],"computed":true},"roots":{"type":["list",["object",{"arn":"string","id":"string","name":"string","policy_types":["list",["object",{"status":"string","type":"string"}]]}]],"computed":true}}}},"aws_organizations_organizational_unit":{"version":0,"block":{"attributes":{"accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string"}]],"computed":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"parent_id":{"type":"string","required":true}}}},"aws_organizations_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"content":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"type":{"type":"string","optional":true}}}},"aws_organizations_policy_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy_id":{"type":"string","required":true},"target_id":{"type":"string","required":true}}}},"aws_pinpoint_adm_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"client_id":{"type":"string","required":true,"sensitive":true},"client_secret":{"type":"string","required":true,"sensitive":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_pinpoint_apns_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"bundle_id":{"type":"string","optional":true,"sensitive":true},"certificate":{"type":"string","optional":true,"sensitive":true},"default_authentication_method":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"team_id":{"type":"string","optional":true,"sensitive":true},"token_key":{"type":"string","optional":true,"sensitive":true},"token_key_id":{"type":"string","optional":true,"sensitive":true}}}},"aws_pinpoint_apns_sandbox_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"bundle_id":{"type":"string","optional":true,"sensitive":true},"certificate":{"type":"string","optional":true,"sensitive":true},"default_authentication_method":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"team_id":{"type":"string","optional":true,"sensitive":true},"token_key":{"type":"string","optional":true,"sensitive":true},"token_key_id":{"type":"string","optional":true,"sensitive":true}}}},"aws_pinpoint_apns_voip_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"bundle_id":{"type":"string","optional":true,"sensitive":true},"certificate":{"type":"string","optional":true,"sensitive":true},"default_authentication_method":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"team_id":{"type":"string","optional":true,"sensitive":true},"token_key":{"type":"string","optional":true,"sensitive":true},"token_key_id":{"type":"string","optional":true,"sensitive":true}}}},"aws_pinpoint_apns_voip_sandbox_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"bundle_id":{"type":"string","optional":true,"sensitive":true},"certificate":{"type":"string","optional":true,"sensitive":true},"default_authentication_method":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"team_id":{"type":"string","optional":true,"sensitive":true},"token_key":{"type":"string","optional":true,"sensitive":true},"token_key_id":{"type":"string","optional":true,"sensitive":true}}}},"aws_pinpoint_app":{"version":0,"block":{"attributes":{"application_id":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"campaign_hook":{"nesting_mode":"list","block":{"attributes":{"lambda_function_name":{"type":"string","optional":true},"mode":{"type":"string","optional":true},"web_url":{"type":"string","optional":true}}},"max_items":1},"limits":{"nesting_mode":"list","block":{"attributes":{"daily":{"type":"number","optional":true},"maximum_duration":{"type":"number","optional":true},"messages_per_second":{"type":"number","optional":true},"total":{"type":"number","optional":true}}},"max_items":1},"quiet_time":{"nesting_mode":"list","block":{"attributes":{"end":{"type":"string","optional":true},"start":{"type":"string","optional":true}}},"max_items":1}}}},"aws_pinpoint_baidu_channel":{"version":0,"block":{"attributes":{"api_key":{"type":"string","required":true,"sensitive":true},"application_id":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"secret_key":{"type":"string","required":true,"sensitive":true}}}},"aws_pinpoint_email_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"from_address":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"identity":{"type":"string","required":true},"messages_per_second":{"type":"number","computed":true},"role_arn":{"type":"string","required":true}}}},"aws_pinpoint_event_stream":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"destination_stream_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","required":true}}}},"aws_pinpoint_gcm_channel":{"version":0,"block":{"attributes":{"api_key":{"type":"string","required":true,"sensitive":true},"application_id":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_pinpoint_sms_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"promotional_messages_per_second":{"type":"number","computed":true},"sender_id":{"type":"string","optional":true},"short_code":{"type":"string","optional":true},"transactional_messages_per_second":{"type":"number","computed":true}}}},"aws_placement_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"placement_group_id":{"type":"string","computed":true},"strategy":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_proxy_protocol_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_ports":{"type":["set","string"],"required":true},"load_balancer":{"type":"string","required":true}}}},"aws_qldb_ledger":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deletion_protection":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_quicksight_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_account_id":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"group_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"namespace":{"type":"string","optional":true}}}},"aws_quicksight_user":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_account_id":{"type":"string","optional":true,"computed":true},"email":{"type":"string","required":true},"iam_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"identity_type":{"type":"string","required":true},"namespace":{"type":"string","optional":true},"session_name":{"type":"string","optional":true},"user_name":{"type":"string","optional":true},"user_role":{"type":"string","required":true}}}},"aws_ram_principal_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"principal":{"type":"string","required":true},"resource_share_arn":{"type":"string","required":true}}}},"aws_ram_resource_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_arn":{"type":"string","required":true},"resource_share_arn":{"type":"string","required":true}}}},"aws_ram_resource_share":{"version":0,"block":{"attributes":{"allow_external_principals":{"type":"bool","optional":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_ram_resource_share_accepter":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"invitation_arn":{"type":"string","computed":true},"receiver_account_id":{"type":"string","computed":true},"resources":{"type":["list","string"],"computed":true},"sender_account_id":{"type":"string","computed":true},"share_arn":{"type":"string","required":true},"share_id":{"type":"string","computed":true},"share_name":{"type":"string","computed":true},"status":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_rds_cluster":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"backtrack_window":{"type":"number","optional":true},"backup_retention_period":{"type":"number","optional":true},"cluster_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier_prefix":{"type":"string","optional":true,"computed":true},"cluster_members":{"type":["set","string"],"optional":true,"computed":true},"cluster_resource_id":{"type":"string","computed":true},"copy_tags_to_snapshot":{"type":"bool","optional":true},"database_name":{"type":"string","optional":true,"computed":true},"db_cluster_parameter_group_name":{"type":"string","optional":true,"computed":true},"db_subnet_group_name":{"type":"string","optional":true,"computed":true},"deletion_protection":{"type":"bool","optional":true},"enable_http_endpoint":{"type":"bool","optional":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"optional":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_mode":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"global_cluster_identifier":{"type":"string","optional":true},"hosted_zone_id":{"type":"string","computed":true},"iam_database_authentication_enabled":{"type":"bool","optional":true},"iam_roles":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"master_password":{"type":"string","optional":true,"sensitive":true},"master_username":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"reader_endpoint":{"type":"string","computed":true},"replication_source_identifier":{"type":"string","optional":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_identifier":{"type":"string","optional":true},"source_region":{"type":"string","optional":true},"storage_encrypted":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"s3_import":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","required":true},"bucket_prefix":{"type":"string","optional":true},"ingestion_role":{"type":"string","required":true},"source_engine":{"type":"string","required":true},"source_engine_version":{"type":"string","required":true}}},"max_items":1},"scaling_configuration":{"nesting_mode":"list","block":{"attributes":{"auto_pause":{"type":"bool","optional":true},"max_capacity":{"type":"number","optional":true},"min_capacity":{"type":"number","optional":true},"seconds_until_auto_pause":{"type":"number","optional":true},"timeout_action":{"type":"string","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_rds_cluster_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cluster_endpoint_identifier":{"type":"string","required":true},"cluster_identifier":{"type":"string","required":true},"custom_endpoint_type":{"type":"string","required":true},"endpoint":{"type":"string","computed":true},"excluded_members":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"static_members":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_rds_cluster_instance":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"ca_cert_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier":{"type":"string","required":true},"copy_tags_to_snapshot":{"type":"bool","optional":true},"db_parameter_group_name":{"type":"string","optional":true,"computed":true},"db_subnet_group_name":{"type":"string","optional":true,"computed":true},"dbi_resource_id":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"instance_class":{"type":"string","required":true},"kms_key_id":{"type":"string","computed":true},"monitoring_interval":{"type":"number","optional":true},"monitoring_role_arn":{"type":"string","optional":true,"computed":true},"performance_insights_enabled":{"type":"bool","optional":true,"computed":true},"performance_insights_kms_key_id":{"type":"string","optional":true,"computed":true},"port":{"type":"number","computed":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"promotion_tier":{"type":"number","optional":true},"publicly_accessible":{"type":"bool","optional":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true},"writer":{"type":"bool","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_rds_cluster_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_rds_global_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"database_name":{"type":"string","optional":true},"deletion_protection":{"type":"bool","optional":true},"engine":{"type":"string","optional":true,"computed":true},"engine_version":{"type":"string","optional":true,"computed":true},"force_destroy":{"type":"bool","optional":true},"global_cluster_identifier":{"type":"string","required":true},"global_cluster_members":{"type":["set",["object",{"db_cluster_arn":"string","is_writer":"bool"}]],"computed":true},"global_cluster_resource_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"source_db_cluster_identifier":{"type":"string","optional":true,"computed":true},"storage_encrypted":{"type":"bool","optional":true}}}},"aws_redshift_cluster":{"version":0,"block":{"attributes":{"allow_version_upgrade":{"type":"bool","optional":true},"arn":{"type":"string","computed":true},"automated_snapshot_retention_period":{"type":"number","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"cluster_identifier":{"type":"string","required":true},"cluster_parameter_group_name":{"type":"string","optional":true,"computed":true},"cluster_public_key":{"type":"string","optional":true,"computed":true},"cluster_revision_number":{"type":"string","optional":true,"computed":true},"cluster_security_groups":{"type":["set","string"],"optional":true,"computed":true},"cluster_subnet_group_name":{"type":"string","optional":true,"computed":true},"cluster_type":{"type":"string","optional":true,"computed":true},"cluster_version":{"type":"string","optional":true},"database_name":{"type":"string","optional":true,"computed":true},"dns_name":{"type":"string","computed":true},"elastic_ip":{"type":"string","optional":true},"encrypted":{"type":"bool","optional":true},"endpoint":{"type":"string","optional":true,"computed":true},"enhanced_vpc_routing":{"type":"bool","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"iam_roles":{"type":["set","string"],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"master_password":{"type":"string","optional":true,"sensitive":true},"master_username":{"type":"string","optional":true},"node_type":{"type":"string","required":true},"number_of_nodes":{"type":"number","optional":true},"owner_account":{"type":"string","optional":true},"port":{"type":"number","optional":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"publicly_accessible":{"type":"bool","optional":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_cluster_identifier":{"type":"string","optional":true},"snapshot_identifier":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"logging":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","optional":true,"computed":true},"enable":{"type":"bool","required":true},"s3_key_prefix":{"type":"string","optional":true,"computed":true}}},"max_items":1},"snapshot_copy":{"nesting_mode":"list","block":{"attributes":{"destination_region":{"type":"string","required":true},"grant_name":{"type":"string","optional":true},"retention_period":{"type":"number","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_redshift_event_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_aws_id":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"event_categories":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"severity":{"type":"string","optional":true},"sns_topic_arn":{"type":"string","required":true},"source_ids":{"type":["set","string"],"optional":true},"source_type":{"type":"string","optional":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_redshift_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_redshift_security_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"ingress":{"nesting_mode":"set","block":{"attributes":{"cidr":{"type":"string","optional":true},"security_group_name":{"type":"string","optional":true,"computed":true},"security_group_owner_id":{"type":"string","optional":true,"computed":true}}},"min_items":1}}}},"aws_redshift_snapshot_copy_grant":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"snapshot_copy_grant_name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_redshift_snapshot_schedule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"definitions":{"type":["set","string"],"required":true},"description":{"type":"string","optional":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_redshift_snapshot_schedule_association":{"version":0,"block":{"attributes":{"cluster_identifier":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"schedule_identifier":{"type":"string","required":true}}}},"aws_redshift_subnet_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_resourcegroups_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"resource_query":{"nesting_mode":"list","block":{"attributes":{"query":{"type":"string","required":true},"type":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}}},"aws_route":{"version":0,"block":{"attributes":{"destination_cidr_block":{"type":"string","optional":true},"destination_ipv6_cidr_block":{"type":"string","optional":true},"destination_prefix_list_id":{"type":"string","computed":true},"egress_only_gateway_id":{"type":"string","optional":true,"computed":true},"gateway_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true,"computed":true},"instance_owner_id":{"type":"string","computed":true},"nat_gateway_id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","optional":true,"computed":true},"origin":{"type":"string","computed":true},"route_table_id":{"type":"string","required":true},"state":{"type":"string","computed":true},"transit_gateway_id":{"type":"string","optional":true},"vpc_peering_connection_id":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_route53_delegation_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name_servers":{"type":["list","string"],"computed":true},"reference_name":{"type":"string","optional":true}}}},"aws_route53_health_check":{"version":0,"block":{"attributes":{"child_health_threshold":{"type":"number","optional":true},"child_healthchecks":{"type":["set","string"],"optional":true},"cloudwatch_alarm_name":{"type":"string","optional":true},"cloudwatch_alarm_region":{"type":"string","optional":true},"disabled":{"type":"bool","optional":true},"enable_sni":{"type":"bool","optional":true,"computed":true},"failure_threshold":{"type":"number","optional":true},"fqdn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"insufficient_data_health_status":{"type":"string","optional":true},"invert_healthcheck":{"type":"bool","optional":true},"ip_address":{"type":"string","optional":true},"measure_latency":{"type":"bool","optional":true},"port":{"type":"number","optional":true},"reference_name":{"type":"string","optional":true},"regions":{"type":["set","string"],"optional":true},"request_interval":{"type":"number","optional":true},"resource_path":{"type":"string","optional":true},"search_string":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","required":true}}}},"aws_route53_query_log":{"version":0,"block":{"attributes":{"cloudwatch_log_group_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","required":true}}}},"aws_route53_record":{"version":2,"block":{"attributes":{"allow_overwrite":{"type":"bool","optional":true,"computed":true},"fqdn":{"type":"string","computed":true},"health_check_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"multivalue_answer_routing_policy":{"type":"bool","optional":true},"name":{"type":"string","required":true},"records":{"type":["set","string"],"optional":true},"set_identifier":{"type":"string","optional":true},"ttl":{"type":"number","optional":true},"type":{"type":"string","required":true},"zone_id":{"type":"string","required":true}},"block_types":{"alias":{"nesting_mode":"set","block":{"attributes":{"evaluate_target_health":{"type":"bool","required":true},"name":{"type":"string","required":true},"zone_id":{"type":"string","required":true}}}},"failover_routing_policy":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}}},"geolocation_routing_policy":{"nesting_mode":"list","block":{"attributes":{"continent":{"type":"string","optional":true},"country":{"type":"string","optional":true},"subdivision":{"type":"string","optional":true}}}},"latency_routing_policy":{"nesting_mode":"list","block":{"attributes":{"region":{"type":"string","required":true}}}},"weighted_routing_policy":{"nesting_mode":"list","block":{"attributes":{"weight":{"type":"number","required":true}}}}}}},"aws_route53_resolver_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"direction":{"type":"string","required":true},"host_vpc_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"security_group_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"ip_address":{"nesting_mode":"set","block":{"attributes":{"ip":{"type":"string","optional":true,"computed":true},"ip_id":{"type":"string","computed":true},"subnet_id":{"type":"string","required":true}}},"min_items":2,"max_items":10},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_route53_resolver_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"resolver_endpoint_id":{"type":"string","optional":true},"rule_type":{"type":"string","required":true},"share_status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"target_ip":{"nesting_mode":"set","block":{"attributes":{"ip":{"type":"string","required":true},"port":{"type":"number","optional":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_route53_resolver_rule_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"resolver_rule_id":{"type":"string","required":true},"vpc_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_route53_vpc_association_authorization":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"vpc_id":{"type":"string","required":true},"vpc_region":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","required":true}}}},"aws_route53_zone":{"version":0,"block":{"attributes":{"comment":{"type":"string","optional":true},"delegation_set_id":{"type":"string","optional":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"name_servers":{"type":["list","string"],"computed":true},"tags":{"type":["map","string"],"optional":true},"zone_id":{"type":"string","computed":true}},"block_types":{"vpc":{"nesting_mode":"set","block":{"attributes":{"vpc_id":{"type":"string","required":true},"vpc_region":{"type":"string","optional":true,"computed":true}}}}}}},"aws_route53_zone_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"owning_account":{"type":"string","computed":true},"vpc_id":{"type":"string","required":true},"vpc_region":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","required":true}}}},"aws_route_table":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"propagating_vgws":{"type":["set","string"],"optional":true,"computed":true},"route":{"type":["set",["object",{"cidr_block":"string","egress_only_gateway_id":"string","gateway_id":"string","instance_id":"string","ipv6_cidr_block":"string","nat_gateway_id":"string","network_interface_id":"string","transit_gateway_id":"string","vpc_peering_connection_id":"string"}]],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}}}},"aws_route_table_association":{"version":0,"block":{"attributes":{"gateway_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"route_table_id":{"type":"string","required":true},"subnet_id":{"type":"string","optional":true}}}},"aws_s3_access_point":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"bucket":{"type":"string","required":true},"domain_name":{"type":"string","computed":true},"has_public_access_policy":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"network_origin":{"type":"string","computed":true},"policy":{"type":"string","optional":true}},"block_types":{"public_access_block_configuration":{"nesting_mode":"list","block":{"attributes":{"block_public_acls":{"type":"bool","optional":true},"block_public_policy":{"type":"bool","optional":true},"ignore_public_acls":{"type":"bool","optional":true},"restrict_public_buckets":{"type":"bool","optional":true}}},"max_items":1},"vpc_configuration":{"nesting_mode":"list","block":{"attributes":{"vpc_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_s3_account_public_access_block":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"block_public_acls":{"type":"bool","optional":true},"block_public_policy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"ignore_public_acls":{"type":"bool","optional":true},"restrict_public_buckets":{"type":"bool","optional":true}}}},"aws_s3_bucket":{"version":0,"block":{"attributes":{"acceleration_status":{"type":"string","optional":true,"computed":true},"acl":{"type":"string","optional":true},"arn":{"type":"string","optional":true,"computed":true},"bucket":{"type":"string","optional":true,"computed":true},"bucket_domain_name":{"type":"string","computed":true},"bucket_prefix":{"type":"string","optional":true},"bucket_regional_domain_name":{"type":"string","computed":true},"force_destroy":{"type":"bool","optional":true},"hosted_zone_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","optional":true},"region":{"type":"string","computed":true},"request_payer":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"website_domain":{"type":"string","optional":true,"computed":true},"website_endpoint":{"type":"string","optional":true,"computed":true}},"block_types":{"cors_rule":{"nesting_mode":"list","block":{"attributes":{"allowed_headers":{"type":["list","string"],"optional":true},"allowed_methods":{"type":["list","string"],"required":true},"allowed_origins":{"type":["list","string"],"required":true},"expose_headers":{"type":["list","string"],"optional":true},"max_age_seconds":{"type":"number","optional":true}}}},"grant":{"nesting_mode":"set","block":{"attributes":{"id":{"type":"string","optional":true},"permissions":{"type":["set","string"],"required":true},"type":{"type":"string","required":true},"uri":{"type":"string","optional":true}}}},"lifecycle_rule":{"nesting_mode":"list","block":{"attributes":{"abort_incomplete_multipart_upload_days":{"type":"number","optional":true},"enabled":{"type":"bool","required":true},"id":{"type":"string","optional":true,"computed":true},"prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"expiration":{"nesting_mode":"list","block":{"attributes":{"date":{"type":"string","optional":true},"days":{"type":"number","optional":true},"expired_object_delete_marker":{"type":"bool","optional":true}}},"max_items":1},"noncurrent_version_expiration":{"nesting_mode":"list","block":{"attributes":{"days":{"type":"number","optional":true}}},"max_items":1},"noncurrent_version_transition":{"nesting_mode":"set","block":{"attributes":{"days":{"type":"number","optional":true},"storage_class":{"type":"string","required":true}}}},"transition":{"nesting_mode":"set","block":{"attributes":{"date":{"type":"string","optional":true},"days":{"type":"number","optional":true},"storage_class":{"type":"string","required":true}}}}}}},"logging":{"nesting_mode":"set","block":{"attributes":{"target_bucket":{"type":"string","required":true},"target_prefix":{"type":"string","optional":true}}}},"object_lock_configuration":{"nesting_mode":"list","block":{"attributes":{"object_lock_enabled":{"type":"string","required":true}},"block_types":{"rule":{"nesting_mode":"list","block":{"block_types":{"default_retention":{"nesting_mode":"list","block":{"attributes":{"days":{"type":"number","optional":true},"mode":{"type":"string","required":true},"years":{"type":"number","optional":true}}},"min_items":1,"max_items":1}}},"max_items":1}}},"max_items":1},"replication_configuration":{"nesting_mode":"list","block":{"attributes":{"role":{"type":"string","required":true}},"block_types":{"rules":{"nesting_mode":"set","block":{"attributes":{"id":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"priority":{"type":"number","optional":true},"status":{"type":"string","required":true}},"block_types":{"destination":{"nesting_mode":"list","block":{"attributes":{"account_id":{"type":"string","optional":true},"bucket":{"type":"string","required":true},"replica_kms_key_id":{"type":"string","optional":true},"storage_class":{"type":"string","optional":true}},"block_types":{"access_control_translation":{"nesting_mode":"list","block":{"attributes":{"owner":{"type":"string","required":true}}},"max_items":1}}},"min_items":1,"max_items":1},"filter":{"nesting_mode":"list","block":{"attributes":{"prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}},"max_items":1},"source_selection_criteria":{"nesting_mode":"list","block":{"block_types":{"sse_kms_encrypted_objects":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"server_side_encryption_configuration":{"nesting_mode":"list","block":{"block_types":{"rule":{"nesting_mode":"list","block":{"block_types":{"apply_server_side_encryption_by_default":{"nesting_mode":"list","block":{"attributes":{"kms_master_key_id":{"type":"string","optional":true},"sse_algorithm":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1},"versioning":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"mfa_delete":{"type":"bool","optional":true}}},"max_items":1},"website":{"nesting_mode":"list","block":{"attributes":{"error_document":{"type":"string","optional":true},"index_document":{"type":"string","optional":true},"redirect_all_requests_to":{"type":"string","optional":true},"routing_rules":{"type":"string","optional":true}}},"max_items":1}}}},"aws_s3_bucket_analytics_configuration":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"filter":{"nesting_mode":"list","block":{"attributes":{"prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}},"max_items":1},"storage_class_analysis":{"nesting_mode":"list","block":{"block_types":{"data_export":{"nesting_mode":"list","block":{"attributes":{"output_schema_version":{"type":"string","optional":true}},"block_types":{"destination":{"nesting_mode":"list","block":{"block_types":{"s3_bucket_destination":{"nesting_mode":"list","block":{"attributes":{"bucket_account_id":{"type":"string","optional":true},"bucket_arn":{"type":"string","required":true},"format":{"type":"string","optional":true},"prefix":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1}}}},"aws_s3_bucket_inventory":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"included_object_versions":{"type":"string","required":true},"name":{"type":"string","required":true},"optional_fields":{"type":["set","string"],"optional":true}},"block_types":{"destination":{"nesting_mode":"list","block":{"block_types":{"bucket":{"nesting_mode":"list","block":{"attributes":{"account_id":{"type":"string","optional":true},"bucket_arn":{"type":"string","required":true},"format":{"type":"string","required":true},"prefix":{"type":"string","optional":true}},"block_types":{"encryption":{"nesting_mode":"list","block":{"block_types":{"sse_kms":{"nesting_mode":"list","block":{"attributes":{"key_id":{"type":"string","required":true}}},"max_items":1},"sse_s3":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"filter":{"nesting_mode":"list","block":{"attributes":{"prefix":{"type":"string","optional":true}}},"max_items":1},"schedule":{"nesting_mode":"list","block":{"attributes":{"frequency":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_s3_bucket_metric":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"filter":{"nesting_mode":"list","block":{"attributes":{"prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}},"max_items":1}}}},"aws_s3_bucket_notification":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}},"block_types":{"lambda_function":{"nesting_mode":"list","block":{"attributes":{"events":{"type":["set","string"],"required":true},"filter_prefix":{"type":"string","optional":true},"filter_suffix":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"lambda_function_arn":{"type":"string","optional":true}}}},"queue":{"nesting_mode":"list","block":{"attributes":{"events":{"type":["set","string"],"required":true},"filter_prefix":{"type":"string","optional":true},"filter_suffix":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"queue_arn":{"type":"string","required":true}}}},"topic":{"nesting_mode":"list","block":{"attributes":{"events":{"type":["set","string"],"required":true},"filter_prefix":{"type":"string","optional":true},"filter_suffix":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"topic_arn":{"type":"string","required":true}}}}}}},"aws_s3_bucket_object":{"version":0,"block":{"attributes":{"acl":{"type":"string","optional":true},"bucket":{"type":"string","required":true},"cache_control":{"type":"string","optional":true},"content":{"type":"string","optional":true},"content_base64":{"type":"string","optional":true},"content_disposition":{"type":"string","optional":true},"content_encoding":{"type":"string","optional":true},"content_language":{"type":"string","optional":true},"content_type":{"type":"string","optional":true,"computed":true},"etag":{"type":"string","optional":true,"computed":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","required":true},"kms_key_id":{"type":"string","optional":true},"metadata":{"type":["map","string"],"optional":true},"object_lock_legal_hold_status":{"type":"string","optional":true},"object_lock_mode":{"type":"string","optional":true},"object_lock_retain_until_date":{"type":"string","optional":true},"server_side_encryption":{"type":"string","optional":true,"computed":true},"source":{"type":"string","optional":true},"storage_class":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"version_id":{"type":"string","computed":true},"website_redirect":{"type":"string","optional":true}}}},"aws_s3_bucket_policy":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true}}}},"aws_s3_bucket_public_access_block":{"version":0,"block":{"attributes":{"block_public_acls":{"type":"bool","optional":true},"block_public_policy":{"type":"bool","optional":true},"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ignore_public_acls":{"type":"bool","optional":true},"restrict_public_buckets":{"type":"bool","optional":true}}}},"aws_sagemaker_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"endpoint_config_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_sagemaker_endpoint_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"production_variants":{"nesting_mode":"list","block":{"attributes":{"accelerator_type":{"type":"string","optional":true},"initial_instance_count":{"type":"number","required":true},"initial_variant_weight":{"type":"number","optional":true},"instance_type":{"type":"string","required":true},"model_name":{"type":"string","required":true},"variant_name":{"type":"string","optional":true,"computed":true}}},"min_items":1}}}},"aws_sagemaker_model":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"enable_network_isolation":{"type":"bool","optional":true},"execution_role_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"container":{"nesting_mode":"list","block":{"attributes":{"container_hostname":{"type":"string","optional":true},"environment":{"type":["map","string"],"optional":true},"image":{"type":"string","required":true},"model_data_url":{"type":"string","optional":true}}}},"primary_container":{"nesting_mode":"list","block":{"attributes":{"container_hostname":{"type":"string","optional":true},"environment":{"type":["map","string"],"optional":true},"image":{"type":"string","required":true},"model_data_url":{"type":"string","optional":true}}},"max_items":1},"vpc_config":{"nesting_mode":"list","block":{"attributes":{"security_group_ids":{"type":["set","string"],"required":true},"subnets":{"type":["set","string"],"required":true}}},"max_items":1}}}},"aws_sagemaker_notebook_instance":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"direct_internet_access":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","required":true},"kms_key_id":{"type":"string","optional":true},"lifecycle_config_name":{"type":"string","optional":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"subnet_id":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_sagemaker_notebook_instance_lifecycle_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"on_create":{"type":"string","optional":true},"on_start":{"type":"string","optional":true}}}},"aws_secretsmanager_secret":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","optional":true},"recovery_window_in_days":{"type":"number","optional":true},"rotation_enabled":{"type":"bool","computed":true},"rotation_lambda_arn":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"rotation_rules":{"nesting_mode":"list","block":{"attributes":{"automatically_after_days":{"type":"number","required":true}}},"max_items":1}}}},"aws_secretsmanager_secret_rotation":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"rotation_enabled":{"type":"bool","computed":true},"rotation_lambda_arn":{"type":"string","required":true},"secret_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"rotation_rules":{"nesting_mode":"list","block":{"attributes":{"automatically_after_days":{"type":"number","required":true}}},"min_items":1,"max_items":1}}}},"aws_secretsmanager_secret_version":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"secret_binary":{"type":"string","optional":true,"sensitive":true},"secret_id":{"type":"string","required":true},"secret_string":{"type":"string","optional":true,"sensitive":true},"version_id":{"type":"string","computed":true},"version_stages":{"type":["set","string"],"optional":true,"computed":true}}}},"aws_security_group":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"egress":{"type":["set",["object",{"cidr_blocks":["list","string"],"description":"string","from_port":"number","ipv6_cidr_blocks":["list","string"],"prefix_list_ids":["list","string"],"protocol":"string","security_groups":["set","string"],"self":"bool","to_port":"number"}]],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ingress":{"type":["set",["object",{"cidr_blocks":["list","string"],"description":"string","from_port":"number","ipv6_cidr_blocks":["list","string"],"prefix_list_ids":["list","string"],"protocol":"string","security_groups":["set","string"],"self":"bool","to_port":"number"}]],"optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"revoke_rules_on_delete":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_security_group_rule":{"version":2,"block":{"attributes":{"cidr_blocks":{"type":["list","string"],"optional":true},"description":{"type":"string","optional":true},"from_port":{"type":"number","required":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_blocks":{"type":["list","string"],"optional":true},"prefix_list_ids":{"type":["list","string"],"optional":true},"protocol":{"type":"string","required":true},"security_group_id":{"type":"string","required":true},"self":{"type":"bool","optional":true},"source_security_group_id":{"type":"string","optional":true,"computed":true},"to_port":{"type":"number","required":true},"type":{"type":"string","description":"Type of rule, ingress (inbound) or egress (outbound).","required":true}}}},"aws_securityhub_account":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true}}}},"aws_securityhub_member":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"email":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"invite":{"type":"bool","optional":true},"master_id":{"type":"string","computed":true},"member_status":{"type":"string","computed":true}}}},"aws_securityhub_product_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"product_arn":{"type":"string","required":true}}}},"aws_securityhub_standards_subscription":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"standards_arn":{"type":"string","required":true}}}},"aws_service_discovery_http_namespace":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_service_discovery_private_dns_namespace":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"hosted_zone":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vpc":{"type":"string","required":true}}}},"aws_service_discovery_public_dns_namespace":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"hosted_zone":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_service_discovery_service":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"namespace_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"dns_config":{"nesting_mode":"list","block":{"attributes":{"namespace_id":{"type":"string","required":true},"routing_policy":{"type":"string","optional":true}},"block_types":{"dns_records":{"nesting_mode":"list","block":{"attributes":{"ttl":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"health_check_config":{"nesting_mode":"list","block":{"attributes":{"failure_threshold":{"type":"number","optional":true},"resource_path":{"type":"string","optional":true},"type":{"type":"string","optional":true}}},"max_items":1},"health_check_custom_config":{"nesting_mode":"list","block":{"attributes":{"failure_threshold":{"type":"number","optional":true}}},"max_items":1}}}},"aws_servicecatalog_portfolio":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_time":{"type":"string","computed":true},"description":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"provider_name":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_servicequotas_service_quota":{"version":0,"block":{"attributes":{"adjustable":{"type":"bool","computed":true},"arn":{"type":"string","computed":true},"default_value":{"type":"number","computed":true},"id":{"type":"string","optional":true,"computed":true},"quota_code":{"type":"string","required":true},"quota_name":{"type":"string","computed":true},"request_id":{"type":"string","computed":true},"request_status":{"type":"string","computed":true},"service_code":{"type":"string","required":true},"service_name":{"type":"string","computed":true},"value":{"type":"number","required":true}}}},"aws_ses_active_receipt_rule_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"rule_set_name":{"type":"string","required":true}}}},"aws_ses_configuration_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_ses_domain_dkim":{"version":0,"block":{"attributes":{"dkim_tokens":{"type":["list","string"],"computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ses_domain_identity":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"verification_token":{"type":"string","computed":true}}}},"aws_ses_domain_identity_verification":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_ses_domain_mail_from":{"version":0,"block":{"attributes":{"behavior_on_mx_failure":{"type":"string","optional":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"mail_from_domain":{"type":"string","required":true}}}},"aws_ses_email_identity":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"email":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ses_event_destination":{"version":0,"block":{"attributes":{"configuration_set_name":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"matching_types":{"type":["set","string"],"required":true},"name":{"type":"string","required":true}},"block_types":{"cloudwatch_destination":{"nesting_mode":"set","block":{"attributes":{"default_value":{"type":"string","required":true},"dimension_name":{"type":"string","required":true},"value_source":{"type":"string","required":true}}}},"kinesis_destination":{"nesting_mode":"list","block":{"attributes":{"role_arn":{"type":"string","required":true},"stream_arn":{"type":"string","required":true}}},"max_items":1},"sns_destination":{"nesting_mode":"list","block":{"attributes":{"topic_arn":{"type":"string","required":true}}},"max_items":1}}}},"aws_ses_identity_notification_topic":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"identity":{"type":"string","required":true},"include_original_headers":{"type":"bool","optional":true},"notification_type":{"type":"string","required":true},"topic_arn":{"type":"string","optional":true}}}},"aws_ses_identity_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"identity":{"type":"string","required":true},"name":{"type":"string","required":true},"policy":{"type":"string","required":true}}}},"aws_ses_receipt_filter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cidr":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"policy":{"type":"string","required":true}}}},"aws_ses_receipt_rule":{"version":0,"block":{"attributes":{"after":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"recipients":{"type":["set","string"],"optional":true},"rule_set_name":{"type":"string","required":true},"scan_enabled":{"type":"bool","optional":true,"computed":true},"tls_policy":{"type":"string","optional":true,"computed":true}},"block_types":{"add_header_action":{"nesting_mode":"set","block":{"attributes":{"header_name":{"type":"string","required":true},"header_value":{"type":"string","required":true},"position":{"type":"number","required":true}}}},"bounce_action":{"nesting_mode":"set","block":{"attributes":{"message":{"type":"string","required":true},"position":{"type":"number","required":true},"sender":{"type":"string","required":true},"smtp_reply_code":{"type":"string","required":true},"status_code":{"type":"string","optional":true},"topic_arn":{"type":"string","optional":true}}}},"lambda_action":{"nesting_mode":"set","block":{"attributes":{"function_arn":{"type":"string","required":true},"invocation_type":{"type":"string","optional":true,"computed":true},"position":{"type":"number","required":true},"topic_arn":{"type":"string","optional":true}}}},"s3_action":{"nesting_mode":"set","block":{"attributes":{"bucket_name":{"type":"string","required":true},"kms_key_arn":{"type":"string","optional":true},"object_key_prefix":{"type":"string","optional":true},"position":{"type":"number","required":true},"topic_arn":{"type":"string","optional":true}}}},"sns_action":{"nesting_mode":"set","block":{"attributes":{"position":{"type":"number","required":true},"topic_arn":{"type":"string","required":true}}}},"stop_action":{"nesting_mode":"set","block":{"attributes":{"position":{"type":"number","required":true},"scope":{"type":"string","required":true},"topic_arn":{"type":"string","optional":true}}}},"workmail_action":{"nesting_mode":"set","block":{"attributes":{"organization_arn":{"type":"string","required":true},"position":{"type":"number","required":true},"topic_arn":{"type":"string","optional":true}}}}}}},"aws_ses_receipt_rule_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"rule_set_name":{"type":"string","required":true}}}},"aws_ses_template":{"version":0,"block":{"attributes":{"html":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"subject":{"type":"string","optional":true},"text":{"type":"string","optional":true}}}},"aws_sfn_activity":{"version":0,"block":{"attributes":{"creation_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_sfn_state_machine":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_date":{"type":"string","computed":true},"definition":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_shield_protection":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"resource_arn":{"type":"string","required":true}}}},"aws_simpledb_domain":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_snapshot_create_volume_permission":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"snapshot_id":{"type":"string","required":true}}}},"aws_sns_platform_application":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"event_delivery_failure_topic_arn":{"type":"string","optional":true},"event_endpoint_created_topic_arn":{"type":"string","optional":true},"event_endpoint_deleted_topic_arn":{"type":"string","optional":true},"event_endpoint_updated_topic_arn":{"type":"string","optional":true},"failure_feedback_role_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"platform":{"type":"string","required":true},"platform_credential":{"type":"string","required":true,"sensitive":true},"platform_principal":{"type":"string","optional":true,"sensitive":true},"success_feedback_role_arn":{"type":"string","optional":true},"success_feedback_sample_rate":{"type":"string","optional":true}}}},"aws_sns_sms_preferences":{"version":0,"block":{"attributes":{"default_sender_id":{"type":"string","optional":true},"default_sms_type":{"type":"string","optional":true},"delivery_status_iam_role_arn":{"type":"string","optional":true},"delivery_status_success_sampling_rate":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"monthly_spend_limit":{"type":"string","optional":true},"usage_report_s3_bucket":{"type":"string","optional":true}}}},"aws_sns_topic":{"version":0,"block":{"attributes":{"application_failure_feedback_role_arn":{"type":"string","optional":true},"application_success_feedback_role_arn":{"type":"string","optional":true},"application_success_feedback_sample_rate":{"type":"number","optional":true},"arn":{"type":"string","computed":true},"delivery_policy":{"type":"string","optional":true},"display_name":{"type":"string","optional":true},"http_failure_feedback_role_arn":{"type":"string","optional":true},"http_success_feedback_role_arn":{"type":"string","optional":true},"http_success_feedback_sample_rate":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_master_key_id":{"type":"string","optional":true},"lambda_failure_feedback_role_arn":{"type":"string","optional":true},"lambda_success_feedback_role_arn":{"type":"string","optional":true},"lambda_success_feedback_sample_rate":{"type":"number","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","optional":true,"computed":true},"sqs_failure_feedback_role_arn":{"type":"string","optional":true},"sqs_success_feedback_role_arn":{"type":"string","optional":true},"sqs_success_feedback_sample_rate":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_sns_topic_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true}}}},"aws_sns_topic_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"confirmation_timeout_in_minutes":{"type":"number","optional":true},"delivery_policy":{"type":"string","optional":true},"endpoint":{"type":"string","required":true},"endpoint_auto_confirms":{"type":"bool","optional":true},"filter_policy":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"protocol":{"type":"string","required":true},"raw_message_delivery":{"type":"bool","optional":true},"topic_arn":{"type":"string","required":true}}}},"aws_spot_datafeed_subscription":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"prefix":{"type":"string","optional":true}}}},"aws_spot_fleet_request":{"version":1,"block":{"attributes":{"allocation_strategy":{"type":"string","optional":true},"client_token":{"type":"string","computed":true},"excess_capacity_termination_policy":{"type":"string","optional":true},"fleet_type":{"type":"string","optional":true},"iam_fleet_role":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instance_interruption_behaviour":{"type":"string","optional":true},"instance_pools_to_use_count":{"type":"number","optional":true},"load_balancers":{"type":["set","string"],"optional":true,"computed":true},"replace_unhealthy_instances":{"type":"bool","optional":true},"spot_price":{"type":"string","optional":true},"spot_request_state":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"target_capacity":{"type":"number","required":true},"target_group_arns":{"type":["set","string"],"optional":true,"computed":true},"terminate_instances_with_expiration":{"type":"bool","optional":true},"valid_from":{"type":"string","optional":true},"valid_until":{"type":"string","optional":true},"wait_for_fulfillment":{"type":"bool","optional":true}},"block_types":{"launch_specification":{"nesting_mode":"set","block":{"attributes":{"ami":{"type":"string","required":true},"associate_public_ip_address":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"ebs_optimized":{"type":"bool","optional":true},"iam_instance_profile":{"type":"string","optional":true},"iam_instance_profile_arn":{"type":"string","optional":true},"instance_type":{"type":"string","required":true},"key_name":{"type":"string","optional":true,"computed":true},"monitoring":{"type":"bool","optional":true},"placement_group":{"type":"string","optional":true,"computed":true},"placement_tenancy":{"type":"string","optional":true},"spot_price":{"type":"string","optional":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"user_data":{"type":"string","optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"weighted_capacity":{"type":"string","optional":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"virtual_name":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}}}}},"launch_template_config":{"nesting_mode":"set","block":{"block_types":{"launch_template_specification":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","optional":true},"name":{"type":"string","optional":true},"version":{"type":"string","optional":true}}},"min_items":1,"max_items":1},"overrides":{"nesting_mode":"set","block":{"attributes":{"availability_zone":{"type":"string","optional":true},"instance_type":{"type":"string","optional":true},"priority":{"type":"number","optional":true,"computed":true},"spot_price":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true},"weighted_capacity":{"type":"number","optional":true,"computed":true}}}}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_spot_instance_request":{"version":0,"block":{"attributes":{"ami":{"type":"string","required":true},"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","optional":true,"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"block_duration_minutes":{"type":"number","optional":true},"cpu_core_count":{"type":"number","optional":true,"computed":true},"cpu_threads_per_core":{"type":"number","optional":true,"computed":true},"disable_api_termination":{"type":"bool","optional":true},"ebs_optimized":{"type":"bool","optional":true},"get_password_data":{"type":"bool","optional":true},"hibernation":{"type":"bool","optional":true},"host_id":{"type":"string","optional":true,"computed":true},"iam_instance_profile":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_initiated_shutdown_behavior":{"type":"string","optional":true},"instance_interruption_behaviour":{"type":"string","optional":true},"instance_state":{"type":"string","computed":true},"instance_type":{"type":"string","required":true},"ipv6_address_count":{"type":"number","optional":true,"computed":true},"ipv6_addresses":{"type":["list","string"],"optional":true,"computed":true},"key_name":{"type":"string","optional":true,"computed":true},"launch_group":{"type":"string","optional":true},"monitoring":{"type":"bool","optional":true},"outpost_arn":{"type":"string","computed":true},"password_data":{"type":"string","computed":true},"placement_group":{"type":"string","optional":true,"computed":true},"primary_network_interface_id":{"type":"string","computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","optional":true,"computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"secondary_private_ips":{"type":["set","string"],"optional":true,"computed":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"source_dest_check":{"type":"bool","optional":true},"spot_bid_status":{"type":"string","computed":true},"spot_instance_id":{"type":"string","computed":true},"spot_price":{"type":"string","optional":true},"spot_request_state":{"type":"string","computed":true},"spot_type":{"type":"string","optional":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"tenancy":{"type":"string","optional":true,"computed":true},"user_data":{"type":"string","optional":true},"user_data_base64":{"type":"string","optional":true},"valid_from":{"type":"string","optional":true,"computed":true},"valid_until":{"type":"string","optional":true,"computed":true},"volume_tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"wait_for_fulfillment":{"type":"bool","optional":true}},"block_types":{"credit_specification":{"nesting_mode":"list","block":{"attributes":{"cpu_credits":{"type":"string","optional":true}}},"max_items":1},"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"no_device":{"type":"bool","optional":true},"virtual_name":{"type":"string","optional":true}}}},"metadata_options":{"nesting_mode":"list","block":{"attributes":{"http_endpoint":{"type":"string","optional":true,"computed":true},"http_put_response_hop_limit":{"type":"number","optional":true,"computed":true},"http_tokens":{"type":"string","optional":true,"computed":true}}},"max_items":1},"network_interface":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_index":{"type":"number","required":true},"network_interface_id":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"list","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","computed":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_sqs_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"content_based_deduplication":{"type":"bool","optional":true},"delay_seconds":{"type":"number","optional":true},"fifo_queue":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_data_key_reuse_period_seconds":{"type":"number","optional":true,"computed":true},"kms_master_key_id":{"type":"string","optional":true},"max_message_size":{"type":"number","optional":true},"message_retention_seconds":{"type":"number","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","optional":true,"computed":true},"receive_wait_time_seconds":{"type":"number","optional":true},"redrive_policy":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"visibility_timeout_seconds":{"type":"number","optional":true}}}},"aws_sqs_queue_policy":{"version":1,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true},"queue_url":{"type":"string","required":true}}}},"aws_ssm_activation":{"version":0,"block":{"attributes":{"activation_code":{"type":"string","computed":true},"description":{"type":"string","optional":true},"expiration_date":{"type":"string","optional":true,"computed":true},"expired":{"type":"bool","computed":true},"iam_role":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"registration_count":{"type":"number","computed":true},"registration_limit":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_ssm_association":{"version":1,"block":{"attributes":{"association_id":{"type":"string","computed":true},"association_name":{"type":"string","optional":true},"automation_target_parameter_name":{"type":"string","optional":true},"compliance_severity":{"type":"string","optional":true},"document_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true},"max_concurrency":{"type":"string","optional":true},"max_errors":{"type":"string","optional":true},"name":{"type":"string","required":true},"parameters":{"type":["map","string"],"optional":true,"computed":true},"schedule_expression":{"type":"string","optional":true}},"block_types":{"output_location":{"nesting_mode":"list","block":{"attributes":{"s3_bucket_name":{"type":"string","required":true},"s3_key_prefix":{"type":"string","optional":true}}},"max_items":1},"targets":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"max_items":5}}}},"aws_ssm_document":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"content":{"type":"string","required":true},"created_date":{"type":"string","computed":true},"default_version":{"type":"string","computed":true},"description":{"type":"string","computed":true},"document_format":{"type":"string","optional":true},"document_type":{"type":"string","required":true},"document_version":{"type":"string","computed":true},"hash":{"type":"string","computed":true},"hash_type":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"latest_version":{"type":"string","computed":true},"name":{"type":"string","required":true},"owner":{"type":"string","computed":true},"parameter":{"type":["list",["object",{"default_value":"string","description":"string","name":"string","type":"string"}]],"computed":true},"permissions":{"type":["map","string"],"optional":true},"platform_types":{"type":["list","string"],"computed":true},"schema_version":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"target_type":{"type":"string","optional":true}},"block_types":{"attachments_source":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"name":{"type":"string","optional":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ssm_maintenance_window":{"version":0,"block":{"attributes":{"allow_unassociated_targets":{"type":"bool","optional":true},"cutoff":{"type":"number","required":true},"description":{"type":"string","optional":true},"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true},"end_date":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"schedule":{"type":"string","required":true},"schedule_timezone":{"type":"string","optional":true},"start_date":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_ssm_maintenance_window_target":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"owner_information":{"type":"string","optional":true},"resource_type":{"type":"string","required":true},"window_id":{"type":"string","required":true}},"block_types":{"targets":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"min_items":1,"max_items":5}}}},"aws_ssm_maintenance_window_task":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"max_concurrency":{"type":"string","required":true},"max_errors":{"type":"string","required":true},"name":{"type":"string","optional":true},"priority":{"type":"number","optional":true},"service_role_arn":{"type":"string","required":true},"task_arn":{"type":"string","required":true},"task_type":{"type":"string","required":true},"window_id":{"type":"string","required":true}},"block_types":{"targets":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"min_items":1},"task_invocation_parameters":{"nesting_mode":"list","block":{"block_types":{"automation_parameters":{"nesting_mode":"list","block":{"attributes":{"document_version":{"type":"string","optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}},"max_items":1},"lambda_parameters":{"nesting_mode":"list","block":{"attributes":{"client_context":{"type":"string","optional":true},"payload":{"type":"string","optional":true,"sensitive":true},"qualifier":{"type":"string","optional":true}}},"max_items":1},"run_command_parameters":{"nesting_mode":"list","block":{"attributes":{"comment":{"type":"string","optional":true},"document_hash":{"type":"string","optional":true},"document_hash_type":{"type":"string","optional":true},"output_s3_bucket":{"type":"string","optional":true},"output_s3_key_prefix":{"type":"string","optional":true},"service_role_arn":{"type":"string","optional":true},"timeout_seconds":{"type":"number","optional":true}},"block_types":{"notification_config":{"nesting_mode":"list","block":{"attributes":{"notification_arn":{"type":"string","optional":true},"notification_events":{"type":["list","string"],"optional":true},"notification_type":{"type":"string","optional":true}}},"max_items":1},"parameter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}},"max_items":1},"step_functions_parameters":{"nesting_mode":"list","block":{"attributes":{"input":{"type":"string","optional":true,"sensitive":true},"name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_ssm_parameter":{"version":0,"block":{"attributes":{"allowed_pattern":{"type":"string","optional":true},"arn":{"type":"string","optional":true,"computed":true},"data_type":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"overwrite":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"tier":{"type":"string","optional":true},"type":{"type":"string","required":true},"value":{"type":"string","required":true,"sensitive":true},"version":{"type":"number","computed":true}}}},"aws_ssm_patch_baseline":{"version":0,"block":{"attributes":{"approved_patches":{"type":["set","string"],"optional":true},"approved_patches_compliance_level":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"operating_system":{"type":"string","optional":true},"rejected_patches":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"approval_rule":{"nesting_mode":"list","block":{"attributes":{"approve_after_days":{"type":"number","required":true},"compliance_level":{"type":"string","optional":true},"enable_non_security":{"type":"bool","optional":true}},"block_types":{"patch_filter":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"min_items":1,"max_items":10}}}},"global_filter":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"max_items":4}}}},"aws_ssm_patch_group":{"version":0,"block":{"attributes":{"baseline_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"patch_group":{"type":"string","required":true}}}},"aws_ssm_resource_data_sync":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"s3_destination":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","required":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"region":{"type":"string","required":true},"sync_format":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}}},"aws_storagegateway_cache":{"version":0,"block":{"attributes":{"disk_id":{"type":"string","required":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_storagegateway_cached_iscsi_volume":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"chap_enabled":{"type":"bool","computed":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"lun_number":{"type":"number","computed":true},"network_interface_id":{"type":"string","required":true},"network_interface_port":{"type":"number","computed":true},"snapshot_id":{"type":"string","optional":true},"source_volume_arn":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"target_arn":{"type":"string","computed":true},"target_name":{"type":"string","required":true},"volume_arn":{"type":"string","computed":true},"volume_id":{"type":"string","computed":true},"volume_size_in_bytes":{"type":"number","required":true}}}},"aws_storagegateway_gateway":{"version":0,"block":{"attributes":{"activation_key":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"cloudwatch_log_group_arn":{"type":"string","optional":true},"gateway_id":{"type":"string","computed":true},"gateway_ip_address":{"type":"string","optional":true,"computed":true},"gateway_name":{"type":"string","required":true},"gateway_timezone":{"type":"string","required":true},"gateway_type":{"type":"string","optional":true},"gateway_vpc_endpoint":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"medium_changer_type":{"type":"string","optional":true},"smb_guest_password":{"type":"string","optional":true,"sensitive":true},"tags":{"type":["map","string"],"optional":true},"tape_drive_type":{"type":"string","optional":true}},"block_types":{"smb_active_directory_settings":{"nesting_mode":"list","block":{"attributes":{"domain_name":{"type":"string","required":true},"password":{"type":"string","required":true,"sensitive":true},"username":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_storagegateway_nfs_file_share":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"client_list":{"type":["set","string"],"required":true},"default_storage_class":{"type":"string","optional":true},"fileshare_id":{"type":"string","computed":true},"gateway_arn":{"type":"string","required":true},"guess_mime_type_enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_encrypted":{"type":"bool","optional":true},"kms_key_arn":{"type":"string","optional":true},"location_arn":{"type":"string","required":true},"object_acl":{"type":"string","optional":true},"path":{"type":"string","computed":true},"read_only":{"type":"bool","optional":true},"requester_pays":{"type":"bool","optional":true},"role_arn":{"type":"string","required":true},"squash":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"nfs_file_share_defaults":{"nesting_mode":"list","block":{"attributes":{"directory_mode":{"type":"string","optional":true},"file_mode":{"type":"string","optional":true},"group_id":{"type":"number","optional":true},"owner_id":{"type":"number","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_storagegateway_smb_file_share":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"authentication":{"type":"string","optional":true},"default_storage_class":{"type":"string","optional":true},"fileshare_id":{"type":"string","computed":true},"gateway_arn":{"type":"string","required":true},"guess_mime_type_enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"invalid_user_list":{"type":["set","string"],"optional":true},"kms_encrypted":{"type":"bool","optional":true},"kms_key_arn":{"type":"string","optional":true},"location_arn":{"type":"string","required":true},"object_acl":{"type":"string","optional":true},"path":{"type":"string","computed":true},"read_only":{"type":"bool","optional":true},"requester_pays":{"type":"bool","optional":true},"role_arn":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"valid_user_list":{"type":["set","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_storagegateway_upload_buffer":{"version":0,"block":{"attributes":{"disk_id":{"type":"string","required":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_storagegateway_working_storage":{"version":0,"block":{"attributes":{"disk_id":{"type":"string","required":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_subnet":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_ipv6_address_on_creation":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"availability_zone_id":{"type":"string","optional":true,"computed":true},"cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block_association_id":{"type":"string","computed":true},"map_public_ip_on_launch":{"type":"bool","optional":true},"outpost_arn":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_swf_domain":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"workflow_execution_retention_period_in_days":{"type":"string","required":true}}}},"aws_transfer_server":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"endpoint_type":{"type":"string","optional":true},"force_destroy":{"type":"bool","optional":true},"host_key":{"type":"string","optional":true,"sensitive":true},"host_key_fingerprint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identity_provider_type":{"type":"string","optional":true},"invocation_role":{"type":"string","optional":true},"logging_role":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"url":{"type":"string","optional":true}},"block_types":{"endpoint_details":{"nesting_mode":"list","block":{"attributes":{"vpc_endpoint_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_transfer_ssh_key":{"version":0,"block":{"attributes":{"body":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"server_id":{"type":"string","required":true},"user_name":{"type":"string","required":true}}}},"aws_transfer_user":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"home_directory":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","optional":true},"role":{"type":"string","required":true},"server_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"user_name":{"type":"string","required":true}}}},"aws_volume_attachment":{"version":0,"block":{"attributes":{"device_name":{"type":"string","required":true},"force_detach":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","required":true},"skip_destroy":{"type":"bool","optional":true},"volume_id":{"type":"string","required":true}}}},"aws_vpc":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_generated_ipv6_cidr_block":{"type":"bool","optional":true},"cidr_block":{"type":"string","required":true},"default_network_acl_id":{"type":"string","computed":true},"default_route_table_id":{"type":"string","computed":true},"default_security_group_id":{"type":"string","computed":true},"dhcp_options_id":{"type":"string","computed":true},"enable_classiclink":{"type":"bool","optional":true,"computed":true},"enable_classiclink_dns_support":{"type":"bool","optional":true,"computed":true},"enable_dns_hostnames":{"type":"bool","optional":true,"computed":true},"enable_dns_support":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_tenancy":{"type":"string","optional":true},"ipv6_association_id":{"type":"string","computed":true},"ipv6_cidr_block":{"type":"string","computed":true},"main_route_table_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_vpc_dhcp_options":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","optional":true},"domain_name_servers":{"type":["list","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"netbios_name_servers":{"type":["list","string"],"optional":true},"netbios_node_type":{"type":"string","optional":true},"ntp_servers":{"type":["list","string"],"optional":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_vpc_dhcp_options_association":{"version":0,"block":{"attributes":{"dhcp_options_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"vpc_id":{"type":"string","required":true}}}},"aws_vpc_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_accept":{"type":"bool","optional":true},"cidr_blocks":{"type":["list","string"],"computed":true},"dns_entry":{"type":["list",["object",{"dns_name":"string","hosted_zone_id":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_ids":{"type":["set","string"],"computed":true},"owner_id":{"type":"string","computed":true},"policy":{"type":"string","optional":true,"computed":true},"prefix_list_id":{"type":"string","computed":true},"private_dns_enabled":{"type":"bool","optional":true},"requester_managed":{"type":"bool","computed":true},"route_table_ids":{"type":["set","string"],"optional":true,"computed":true},"security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"service_name":{"type":"string","required":true},"state":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_endpoint_type":{"type":"string","optional":true},"vpc_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_vpc_endpoint_connection_notification":{"version":0,"block":{"attributes":{"connection_events":{"type":["set","string"],"required":true},"connection_notification_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"notification_type":{"type":"string","computed":true},"state":{"type":"string","computed":true},"vpc_endpoint_id":{"type":"string","optional":true},"vpc_endpoint_service_id":{"type":"string","optional":true}}}},"aws_vpc_endpoint_route_table_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"route_table_id":{"type":"string","required":true},"vpc_endpoint_id":{"type":"string","required":true}}}},"aws_vpc_endpoint_service":{"version":0,"block":{"attributes":{"acceptance_required":{"type":"bool","required":true},"allowed_principals":{"type":["set","string"],"optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"base_endpoint_dns_names":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"manages_vpc_endpoints":{"type":"bool","computed":true},"network_load_balancer_arns":{"type":["set","string"],"required":true},"private_dns_name":{"type":"string","computed":true},"service_name":{"type":"string","computed":true},"service_type":{"type":"string","computed":true},"state":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_vpc_endpoint_service_allowed_principal":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"principal_arn":{"type":"string","required":true},"vpc_endpoint_service_id":{"type":"string","required":true}}}},"aws_vpc_endpoint_subnet_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","required":true},"vpc_endpoint_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_vpc_ipv4_cidr_block_association":{"version":0,"block":{"attributes":{"cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"vpc_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_vpc_peering_connection":{"version":0,"block":{"attributes":{"accept_status":{"type":"string","computed":true},"auto_accept":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"peer_owner_id":{"type":"string","optional":true,"computed":true},"peer_region":{"type":"string","optional":true,"computed":true},"peer_vpc_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}},"block_types":{"accepter":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1},"requester":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_vpc_peering_connection_accepter":{"version":0,"block":{"attributes":{"accept_status":{"type":"string","computed":true},"auto_accept":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"peer_owner_id":{"type":"string","computed":true},"peer_region":{"type":"string","computed":true},"peer_vpc_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"vpc_peering_connection_id":{"type":"string","required":true}},"block_types":{"accepter":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1},"requester":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_vpc_peering_connection_options":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"vpc_peering_connection_id":{"type":"string","required":true}},"block_types":{"accepter":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1},"requester":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_vpn_connection":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_gateway_configuration":{"type":"string","computed":true},"customer_gateway_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"routes":{"type":["set",["object",{"destination_cidr_block":"string","source":"string","state":"string"}]],"computed":true},"static_routes_only":{"type":"bool","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_attachment_id":{"type":"string","computed":true},"transit_gateway_id":{"type":"string","optional":true},"tunnel1_address":{"type":"string","computed":true},"tunnel1_bgp_asn":{"type":"string","computed":true},"tunnel1_bgp_holdtime":{"type":"number","computed":true},"tunnel1_cgw_inside_address":{"type":"string","computed":true},"tunnel1_inside_cidr":{"type":"string","optional":true,"computed":true},"tunnel1_preshared_key":{"type":"string","optional":true,"computed":true,"sensitive":true},"tunnel1_vgw_inside_address":{"type":"string","computed":true},"tunnel2_address":{"type":"string","computed":true},"tunnel2_bgp_asn":{"type":"string","computed":true},"tunnel2_bgp_holdtime":{"type":"number","computed":true},"tunnel2_cgw_inside_address":{"type":"string","computed":true},"tunnel2_inside_cidr":{"type":"string","optional":true,"computed":true},"tunnel2_preshared_key":{"type":"string","optional":true,"computed":true,"sensitive":true},"tunnel2_vgw_inside_address":{"type":"string","computed":true},"type":{"type":"string","required":true},"vgw_telemetry":{"type":["set",["object",{"accepted_route_count":"number","last_status_change":"string","outside_ip_address":"string","status":"string","status_message":"string"}]],"computed":true},"vpn_gateway_id":{"type":"string","optional":true}}}},"aws_vpn_connection_route":{"version":0,"block":{"attributes":{"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"vpn_connection_id":{"type":"string","required":true}}}},"aws_vpn_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","optional":true,"computed":true}}}},"aws_vpn_gateway_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"vpc_id":{"type":"string","required":true},"vpn_gateway_id":{"type":"string","required":true}}}},"aws_vpn_gateway_route_propagation":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"route_table_id":{"type":"string","required":true},"vpn_gateway_id":{"type":"string","required":true}}}},"aws_waf_byte_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"byte_match_tuples":{"nesting_mode":"set","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"target_string":{"type":"string","optional":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_geo_match_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"geo_match_constraint":{"nesting_mode":"set","block":{"attributes":{"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_waf_ipset":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"ip_set_descriptors":{"nesting_mode":"set","block":{"attributes":{"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_waf_rate_based_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"rate_key":{"type":"string","required":true},"rate_limit":{"type":"number","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"predicates":{"nesting_mode":"set","block":{"attributes":{"data_id":{"type":"string","required":true},"negated":{"type":"bool","required":true},"type":{"type":"string","required":true}}}}}}},"aws_waf_regex_match_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"regex_match_tuple":{"nesting_mode":"set","block":{"attributes":{"regex_pattern_set_id":{"type":"string","required":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_regex_pattern_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"regex_pattern_strings":{"type":["set","string"],"optional":true}}}},"aws_waf_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"predicates":{"nesting_mode":"set","block":{"attributes":{"data_id":{"type":"string","required":true},"negated":{"type":"bool","required":true},"type":{"type":"string","required":true}}}}}}},"aws_waf_rule_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"activated_rule":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"rule_id":{"type":"string","required":true},"type":{"type":"string","optional":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_size_constraint_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"size_constraints":{"nesting_mode":"set","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_sql_injection_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"sql_injection_match_tuples":{"nesting_mode":"set","block":{"attributes":{"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_web_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"min_items":1,"max_items":1},"logging_configuration":{"nesting_mode":"list","block":{"attributes":{"log_destination":{"type":"string","required":true}},"block_types":{"redacted_fields":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"set","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"max_items":1},"rules":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"rule_id":{"type":"string","required":true},"type":{"type":"string","optional":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1},"override_action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1}}}}}}},"aws_waf_xss_match_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"xss_match_tuples":{"nesting_mode":"set","block":{"attributes":{"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_byte_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"byte_match_tuples":{"nesting_mode":"set","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"target_string":{"type":"string","optional":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_geo_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"geo_match_constraint":{"nesting_mode":"set","block":{"attributes":{"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_wafregional_ipset":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"ip_set_descriptor":{"nesting_mode":"set","block":{"attributes":{"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_wafregional_rate_based_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"rate_key":{"type":"string","required":true},"rate_limit":{"type":"number","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"predicate":{"nesting_mode":"set","block":{"attributes":{"data_id":{"type":"string","required":true},"negated":{"type":"bool","required":true},"type":{"type":"string","required":true}}}}}}},"aws_wafregional_regex_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"regex_match_tuple":{"nesting_mode":"set","block":{"attributes":{"regex_pattern_set_id":{"type":"string","required":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_regex_pattern_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"regex_pattern_strings":{"type":["set","string"],"optional":true}}}},"aws_wafregional_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"predicate":{"nesting_mode":"set","block":{"attributes":{"data_id":{"type":"string","required":true},"negated":{"type":"bool","required":true},"type":{"type":"string","required":true}}}}}}},"aws_wafregional_rule_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"activated_rule":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"rule_id":{"type":"string","required":true},"type":{"type":"string","optional":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_size_constraint_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"size_constraints":{"nesting_mode":"set","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_sql_injection_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"sql_injection_match_tuple":{"nesting_mode":"set","block":{"attributes":{"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_web_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"min_items":1,"max_items":1},"logging_configuration":{"nesting_mode":"list","block":{"attributes":{"log_destination":{"type":"string","required":true}},"block_types":{"redacted_fields":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"set","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"max_items":1},"rule":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"rule_id":{"type":"string","required":true},"type":{"type":"string","optional":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1},"override_action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1}}}}}}},"aws_wafregional_web_acl_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_arn":{"type":"string","required":true},"web_acl_id":{"type":"string","required":true}}}},"aws_wafregional_xss_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"xss_match_tuple":{"nesting_mode":"set","block":{"attributes":{"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafv2_ip_set":{"version":0,"block":{"attributes":{"addresses":{"type":["set","string"],"optional":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"ip_address_version":{"type":"string","required":true},"lock_token":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_wafv2_regex_pattern_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"lock_token":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"regular_expression":{"nesting_mode":"set","block":{"attributes":{"regex_string":{"type":"string","required":true}}},"max_items":10}}}},"aws_wafv2_rule_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"capacity":{"type":"number","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"lock_token":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"rule":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"priority":{"type":"number","required":true}},"block_types":{"action":{"nesting_mode":"list","block":{"block_types":{"allow":{"nesting_mode":"list","block":{},"max_items":1},"block":{"nesting_mode":"list","block":{},"max_items":1},"count":{"nesting_mode":"list","block":{},"max_items":1}}},"min_items":1,"max_items":1},"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1,"max_items":1},"visibility_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_metrics_enabled":{"type":"bool","required":true},"metric_name":{"type":"string","required":true},"sampled_requests_enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"visibility_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_metrics_enabled":{"type":"bool","required":true},"metric_name":{"type":"string","required":true},"sampled_requests_enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"aws_wafv2_web_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"capacity":{"type":"number","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"lock_token":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"block_types":{"allow":{"nesting_mode":"list","block":{},"max_items":1},"block":{"nesting_mode":"list","block":{},"max_items":1}}},"min_items":1,"max_items":1},"rule":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"priority":{"type":"number","required":true}},"block_types":{"action":{"nesting_mode":"list","block":{"block_types":{"allow":{"nesting_mode":"list","block":{},"max_items":1},"block":{"nesting_mode":"list","block":{},"max_items":1},"count":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"override_action":{"nesting_mode":"list","block":{"block_types":{"count":{"nesting_mode":"list","block":{},"max_items":1},"none":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"managed_rule_group_statement":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true},"vendor_name":{"type":"string","required":true}},"block_types":{"excluded_rule":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}}}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"rate_based_statement":{"nesting_mode":"list","block":{"attributes":{"aggregate_key_type":{"type":"string","optional":true},"limit":{"type":"number","required":true}},"block_types":{"scope_down_statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"max_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"rule_group_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"excluded_rule":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}}}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1,"max_items":1},"visibility_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_metrics_enabled":{"type":"bool","required":true},"metric_name":{"type":"string","required":true},"sampled_requests_enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"visibility_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_metrics_enabled":{"type":"bool","required":true},"metric_name":{"type":"string","required":true},"sampled_requests_enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"aws_wafv2_web_acl_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_arn":{"type":"string","required":true},"web_acl_arn":{"type":"string","required":true}}}},"aws_wafv2_web_acl_logging_configuration":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"log_destination_configs":{"type":["set","string"],"description":"AWS Kinesis Firehose Delivery Stream ARNs","required":true},"resource_arn":{"type":"string","description":"AWS WebACL ARN","required":true}},"block_types":{"redacted_fields":{"nesting_mode":"set","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":100}}}},"aws_worklink_fleet":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"audit_stream_arn":{"type":"string","optional":true},"company_code":{"type":"string","computed":true},"created_time":{"type":"string","computed":true},"device_ca_certificate":{"type":"string","optional":true},"display_name":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_time":{"type":"string","computed":true},"name":{"type":"string","required":true},"optimize_for_end_user_location":{"type":"bool","optional":true}},"block_types":{"identity_provider":{"nesting_mode":"list","block":{"attributes":{"saml_metadata":{"type":"string","required":true},"type":{"type":"string","required":true}}},"max_items":1},"network":{"nesting_mode":"list","block":{"attributes":{"security_group_ids":{"type":["set","string"],"required":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_worklink_website_certificate_authority_association":{"version":0,"block":{"attributes":{"certificate":{"type":"string","required":true},"display_name":{"type":"string","optional":true},"fleet_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"website_ca_id":{"type":"string","computed":true}}}},"aws_workspaces_directory":{"version":0,"block":{"attributes":{"alias":{"type":"string","computed":true},"customer_user_name":{"type":"string","computed":true},"directory_id":{"type":"string","required":true},"directory_name":{"type":"string","computed":true},"directory_type":{"type":"string","computed":true},"dns_ip_addresses":{"type":["set","string"],"computed":true},"iam_role_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_group_ids":{"type":["set","string"],"computed":true},"registration_code":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"workspace_security_group_id":{"type":"string","computed":true}},"block_types":{"self_service_permissions":{"nesting_mode":"list","block":{"attributes":{"change_compute_type":{"type":"bool","optional":true},"increase_volume_size":{"type":"bool","optional":true},"rebuild_workspace":{"type":"bool","optional":true},"restart_workspace":{"type":"bool","optional":true},"switch_running_mode":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_workspaces_ip_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"rules":{"nesting_mode":"set","block":{"attributes":{"description":{"type":"string","optional":true},"source":{"type":"string","required":true}}}}}}},"aws_workspaces_workspace":{"version":0,"block":{"attributes":{"bundle_id":{"type":"string","required":true},"computer_name":{"type":"string","computed":true},"directory_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","computed":true},"root_volume_encryption_enabled":{"type":"bool","optional":true},"state":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"user_name":{"type":"string","required":true},"user_volume_encryption_enabled":{"type":"bool","optional":true},"volume_encryption_key":{"type":"string","optional":true}},"block_types":{"workspace_properties":{"nesting_mode":"list","block":{"attributes":{"compute_type_name":{"type":"string","optional":true},"root_volume_size_gib":{"type":"number","optional":true},"running_mode":{"type":"string","optional":true},"running_mode_auto_stop_timeout_in_minutes":{"type":"number","optional":true,"computed":true},"user_volume_size_gib":{"type":"number","optional":true}}},"max_items":1}}}},"aws_xray_sampling_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"attributes":{"type":["map","string"],"optional":true},"fixed_rate":{"type":"number","required":true},"host":{"type":"string","required":true},"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"priority":{"type":"number","required":true},"reservoir_size":{"type":"number","required":true},"resource_arn":{"type":"string","required":true},"rule_name":{"type":"string","optional":true},"service_name":{"type":"string","required":true},"service_type":{"type":"string","required":true},"url_path":{"type":"string","required":true},"version":{"type":"number","required":true}}}}},"data_source_schemas":{"aws_acm_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"key_types":{"type":["set","string"],"optional":true},"most_recent":{"type":"bool","optional":true},"statuses":{"type":["list","string"],"optional":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"types":{"type":["list","string"],"optional":true}}}},"aws_acmpca_certificate_authority":{"version":1,"block":{"attributes":{"arn":{"type":"string","required":true},"certificate":{"type":"string","computed":true},"certificate_chain":{"type":"string","computed":true},"certificate_signing_request":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"not_after":{"type":"string","computed":true},"not_before":{"type":"string","computed":true},"serial":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"type":{"type":"string","computed":true}},"block_types":{"revocation_configuration":{"nesting_mode":"list","block":{"block_types":{"crl_configuration":{"nesting_mode":"list","block":{"attributes":{"custom_cname":{"type":"string","computed":true},"enabled":{"type":"bool","computed":true},"expiration_in_days":{"type":"number","computed":true},"s3_bucket_name":{"type":"string","computed":true}}}}}}}}}},"aws_alb":{"version":0,"block":{"attributes":{"access_logs":{"type":["list",["object",{"bucket":"string","enabled":"bool","prefix":"string"}]],"computed":true},"arn":{"type":"string","optional":true,"computed":true},"arn_suffix":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"drop_invalid_header_fields":{"type":"bool","computed":true},"enable_deletion_protection":{"type":"bool","computed":true},"enable_http2":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","computed":true},"internal":{"type":"bool","computed":true},"ip_address_type":{"type":"string","computed":true},"load_balancer_type":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_mapping":{"type":["set",["object",{"allocation_id":"string","private_ipv4_address":"string","subnet_id":"string"}]],"computed":true},"subnets":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","computed":true},"zone_id":{"type":"string","computed":true}}}},"aws_alb_listener":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"certificate_arn":{"type":"string","computed":true},"default_action":{"type":["list",["object",{"authenticate_cognito":["list",["object",{"authentication_request_extra_params":["map","string"],"on_unauthenticated_request":"string","scope":"string","session_cookie_name":"string","session_timeout":"number","user_pool_arn":"string","user_pool_client_id":"string","user_pool_domain":"string"}]],"authenticate_oidc":["list",["object",{"authentication_request_extra_params":["map","string"],"authorization_endpoint":"string","client_id":"string","client_secret":"string","issuer":"string","on_unauthenticated_request":"string","scope":"string","session_cookie_name":"string","session_timeout":"number","token_endpoint":"string","user_info_endpoint":"string"}]],"fixed_response":["list",["object",{"content_type":"string","message_body":"string","status_code":"string"}]],"order":"number","redirect":["list",["object",{"host":"string","path":"string","port":"string","protocol":"string","query":"string","status_code":"string"}]],"target_group_arn":"string","type":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"load_balancer_arn":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"protocol":{"type":"string","computed":true},"ssl_policy":{"type":"string","computed":true}}}},"aws_alb_target_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"arn_suffix":{"type":"string","computed":true},"deregistration_delay":{"type":"number","computed":true},"health_check":{"type":["list",["object",{"enabled":"bool","healthy_threshold":"number","interval":"number","matcher":"string","path":"string","port":"string","protocol":"string","timeout":"number","unhealthy_threshold":"number"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"lambda_multi_value_headers_enabled":{"type":"bool","computed":true},"load_balancing_algorithm_type":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","computed":true},"protocol":{"type":"string","computed":true},"proxy_protocol_v2":{"type":"bool","computed":true},"slow_start":{"type":"number","computed":true},"stickiness":{"type":["list",["object",{"cookie_duration":"number","enabled":"bool","type":"string"}]],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"target_type":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_ami":{"version":0,"block":{"attributes":{"architecture":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"block_device_mappings":{"type":["set",["object",{"device_name":"string","ebs":["map","string"],"no_device":"string","virtual_name":"string"}]],"computed":true},"creation_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"executable_users":{"type":["list","string"],"optional":true},"hypervisor":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","computed":true},"image_location":{"type":"string","computed":true},"image_owner_alias":{"type":"string","computed":true},"image_type":{"type":"string","computed":true},"kernel_id":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"name":{"type":"string","computed":true},"name_regex":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"owners":{"type":["list","string"],"required":true},"platform":{"type":"string","computed":true},"product_codes":{"type":["set",["object",{"product_code_id":"string","product_code_type":"string"}]],"computed":true},"public":{"type":"bool","computed":true},"ramdisk_id":{"type":"string","computed":true},"root_device_name":{"type":"string","computed":true},"root_device_type":{"type":"string","computed":true},"root_snapshot_id":{"type":"string","computed":true},"sriov_net_support":{"type":"string","computed":true},"state":{"type":"string","computed":true},"state_reason":{"type":["map","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"virtualization_type":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ami_ids":{"version":0,"block":{"attributes":{"executable_users":{"type":["list","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["list","string"],"computed":true},"name_regex":{"type":"string","optional":true},"owners":{"type":["list","string"],"required":true},"sort_ascending":{"type":"bool","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_api_gateway_api_key":{"version":0,"block":{"attributes":{"created_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"enabled":{"type":"bool","computed":true},"id":{"type":"string","required":true},"last_updated_date":{"type":"string","computed":true},"name":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"value":{"type":"string","computed":true,"sensitive":true}}}},"aws_api_gateway_resource":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"parent_id":{"type":"string","computed":true},"path":{"type":"string","required":true},"path_part":{"type":"string","computed":true},"rest_api_id":{"type":"string","required":true}}}},"aws_api_gateway_rest_api":{"version":0,"block":{"attributes":{"api_key_source":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"binary_media_types":{"type":["list","string"],"computed":true},"description":{"type":"string","computed":true},"endpoint_configuration":{"type":["list",["object",{"types":["list","string"],"vpc_endpoint_ids":["set","string"]}]],"computed":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"minimum_compression_size":{"type":"number","computed":true},"name":{"type":"string","required":true},"policy":{"type":"string","computed":true},"root_resource_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_api_gateway_vpc_link":{"version":0,"block":{"attributes":{"description":{"type":"string","computed":true},"id":{"type":"string","computed":true},"name":{"type":"string","required":true},"status":{"type":"string","computed":true},"status_message":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"target_arns":{"type":["set","string"],"computed":true}}}},"aws_arn":{"version":0,"block":{"attributes":{"account":{"type":"string","computed":true},"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"partition":{"type":"string","computed":true},"region":{"type":"string","computed":true},"resource":{"type":"string","computed":true},"service":{"type":"string","computed":true}}}},"aws_autoscaling_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"default_cooldown":{"type":"number","computed":true},"desired_capacity":{"type":"number","computed":true},"health_check_grace_period":{"type":"number","computed":true},"health_check_type":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"launch_configuration":{"type":"string","computed":true},"load_balancers":{"type":["set","string"],"computed":true},"max_size":{"type":"number","computed":true},"min_size":{"type":"number","computed":true},"name":{"type":"string","required":true},"new_instances_protected_from_scale_in":{"type":"bool","computed":true},"placement_group":{"type":"string","computed":true},"service_linked_role_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"target_group_arns":{"type":["set","string"],"computed":true},"termination_policies":{"type":["set","string"],"computed":true},"vpc_zone_identifier":{"type":"string","computed":true}}}},"aws_autoscaling_groups":{"version":0,"block":{"attributes":{"arns":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"names":{"type":["list","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_availability_zone":{"version":0,"block":{"attributes":{"all_availability_zones":{"type":"bool","optional":true},"group_name":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_suffix":{"type":"string","computed":true},"network_border_group":{"type":"string","computed":true},"opt_in_status":{"type":"string","computed":true},"region":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_availability_zones":{"version":0,"block":{"attributes":{"all_availability_zones":{"type":"bool","optional":true},"exclude_names":{"type":["set","string"],"optional":true},"exclude_zone_ids":{"type":["set","string"],"optional":true},"group_names":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"names":{"type":["list","string"],"computed":true},"state":{"type":"string","optional":true},"zone_ids":{"type":["list","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_backup_plan":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"plan_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"version":{"type":"string","computed":true}}}},"aws_backup_selection":{"version":0,"block":{"attributes":{"iam_role_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"plan_id":{"type":"string","required":true},"resources":{"type":["set","string"],"computed":true},"selection_id":{"type":"string","required":true}}}},"aws_backup_vault":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","computed":true},"name":{"type":"string","required":true},"recovery_points":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_batch_compute_environment":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compute_environment_name":{"type":"string","required":true},"ecs_cluster_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"service_role":{"type":"string","computed":true},"state":{"type":"string","computed":true},"status":{"type":"string","computed":true},"status_reason":{"type":"string","computed":true},"type":{"type":"string","computed":true}}}},"aws_batch_job_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compute_environment_order":{"type":["list",["object",{"compute_environment":"string","order":"number"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"priority":{"type":"number","computed":true},"state":{"type":"string","computed":true},"status":{"type":"string","computed":true},"status_reason":{"type":"string","computed":true}}}},"aws_billing_service_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_caller_identity":{"version":0,"block":{"attributes":{"account_id":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"user_id":{"type":"string","computed":true}}}},"aws_canonical_user_id":{"version":0,"block":{"attributes":{"display_name":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_cloudformation_export":{"version":0,"block":{"attributes":{"exporting_stack_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"value":{"type":"string","computed":true}}}},"aws_cloudformation_stack":{"version":0,"block":{"attributes":{"capabilities":{"type":["set","string"],"computed":true},"description":{"type":"string","computed":true},"disable_rollback":{"type":"bool","computed":true},"iam_role_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"notification_arns":{"type":["set","string"],"computed":true},"outputs":{"type":["map","string"],"computed":true},"parameters":{"type":["map","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"template_body":{"type":"string","computed":true},"timeout_in_minutes":{"type":"number","computed":true}}}},"aws_cloudfront_distribution":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","computed":true},"enabled":{"type":"bool","computed":true},"etag":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","required":true},"in_progress_validation_batches":{"type":"number","computed":true},"last_modified_time":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_cloudhsm_v2_cluster":{"version":0,"block":{"attributes":{"cluster_certificates":{"type":["list",["object",{"aws_hardware_certificate":"string","cluster_certificate":"string","cluster_csr":"string","hsm_certificate":"string","manufacturer_hardware_certificate":"string"}]],"computed":true},"cluster_id":{"type":"string","required":true},"cluster_state":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"security_group_id":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_cloudtrail_service_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_cloudwatch_log_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_time":{"type":"number","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"name":{"type":"string","required":true},"retention_in_days":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_codecommit_repository":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"clone_url_http":{"type":"string","computed":true},"clone_url_ssh":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"repository_id":{"type":"string","computed":true},"repository_name":{"type":"string","required":true}}}},"aws_cognito_user_pools":{"version":0,"block":{"attributes":{"arns":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"name":{"type":"string","required":true}}}},"aws_cur_report_definition":{"version":0,"block":{"attributes":{"additional_artifacts":{"type":["set","string"],"computed":true},"additional_schema_elements":{"type":["set","string"],"computed":true},"compression":{"type":"string","computed":true},"format":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"report_name":{"type":"string","required":true},"s3_bucket":{"type":"string","computed":true},"s3_prefix":{"type":"string","computed":true},"s3_region":{"type":"string","computed":true},"time_unit":{"type":"string","computed":true}}}},"aws_customer_gateway":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bgp_asn":{"type":"number","computed":true},"id":{"type":"string","optional":true},"ip_address":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"type":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_db_cluster_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zones":{"type":["list","string"],"computed":true},"db_cluster_identifier":{"type":"string","optional":true},"db_cluster_snapshot_arn":{"type":"string","computed":true},"db_cluster_snapshot_identifier":{"type":"string","optional":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"include_public":{"type":"bool","optional":true},"include_shared":{"type":"bool","optional":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"port":{"type":"number","computed":true},"snapshot_create_time":{"type":"string","computed":true},"snapshot_type":{"type":"string","optional":true},"source_db_cluster_snapshot_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_db_event_categories":{"version":0,"block":{"attributes":{"event_categories":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"source_type":{"type":"string","optional":true}}}},"aws_db_instance":{"version":1,"block":{"attributes":{"address":{"type":"string","computed":true},"allocated_storage":{"type":"number","computed":true},"auto_minor_version_upgrade":{"type":"bool","computed":true},"availability_zone":{"type":"string","computed":true},"backup_retention_period":{"type":"number","computed":true},"ca_cert_identifier":{"type":"string","computed":true},"db_cluster_identifier":{"type":"string","computed":true},"db_instance_arn":{"type":"string","computed":true},"db_instance_class":{"type":"string","computed":true},"db_instance_identifier":{"type":"string","required":true},"db_instance_port":{"type":"number","computed":true},"db_name":{"type":"string","computed":true},"db_parameter_groups":{"type":["list","string"],"computed":true},"db_security_groups":{"type":["list","string"],"computed":true},"db_subnet_group":{"type":"string","computed":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"iops":{"type":"number","computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"master_username":{"type":"string","computed":true},"monitoring_interval":{"type":"number","computed":true},"monitoring_role_arn":{"type":"string","computed":true},"multi_az":{"type":"bool","computed":true},"option_group_memberships":{"type":["list","string"],"computed":true},"port":{"type":"number","computed":true},"preferred_backup_window":{"type":"string","computed":true},"preferred_maintenance_window":{"type":"string","computed":true},"publicly_accessible":{"type":"bool","computed":true},"replicate_source_db":{"type":"string","computed":true},"resource_id":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"storage_type":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"timezone":{"type":"string","computed":true},"vpc_security_groups":{"type":["list","string"],"computed":true}}}},"aws_db_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zone":{"type":"string","computed":true},"db_instance_identifier":{"type":"string","optional":true},"db_snapshot_arn":{"type":"string","computed":true},"db_snapshot_identifier":{"type":"string","optional":true},"encrypted":{"type":"bool","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"include_public":{"type":"bool","optional":true},"include_shared":{"type":"bool","optional":true},"iops":{"type":"number","computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"option_group_name":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_create_time":{"type":"string","computed":true},"snapshot_type":{"type":"string","optional":true},"source_db_snapshot_identifier":{"type":"string","computed":true},"source_region":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_type":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_directory_service_directory":{"version":0,"block":{"attributes":{"access_url":{"type":"string","computed":true},"alias":{"type":"string","computed":true},"connect_settings":{"type":["list",["object",{"availability_zones":["set","string"],"connect_ips":["set","string"],"customer_dns_ips":["set","string"],"customer_username":"string","subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true},"description":{"type":"string","computed":true},"directory_id":{"type":"string","required":true},"dns_ip_addresses":{"type":["set","string"],"computed":true},"edition":{"type":"string","computed":true},"enable_sso":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"security_group_id":{"type":"string","computed":true},"short_name":{"type":"string","computed":true},"size":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","computed":true},"vpc_settings":{"type":["list",["object",{"availability_zones":["set","string"],"subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true}}}},"aws_dx_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","computed":true}}}},"aws_dynamodb_table":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"attribute":{"type":["set",["object",{"name":"string","type":"string"}]],"computed":true},"billing_mode":{"type":"string","computed":true},"global_secondary_index":{"type":["set",["object",{"hash_key":"string","name":"string","non_key_attributes":["list","string"],"projection_type":"string","range_key":"string","read_capacity":"number","write_capacity":"number"}]],"computed":true},"hash_key":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"local_secondary_index":{"type":["set",["object",{"name":"string","non_key_attributes":["list","string"],"projection_type":"string","range_key":"string"}]],"computed":true},"name":{"type":"string","required":true},"point_in_time_recovery":{"type":["list",["object",{"enabled":"bool"}]],"computed":true},"range_key":{"type":"string","computed":true},"read_capacity":{"type":"number","computed":true},"replica":{"type":["set",["object",{"region_name":"string"}]],"computed":true},"stream_arn":{"type":"string","computed":true},"stream_enabled":{"type":"bool","computed":true},"stream_label":{"type":"string","computed":true},"stream_view_type":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"ttl":{"type":["set",["object",{"attribute_name":"string","enabled":"bool"}]],"computed":true},"write_capacity":{"type":"number","computed":true}},"block_types":{"server_side_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","computed":true},"kms_key_arn":{"type":"string","computed":true}}},"max_items":1}}}},"aws_ebs_default_kms_key":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"key_arn":{"type":"string","computed":true}}}},"aws_ebs_encryption_by_default":{"version":0,"block":{"attributes":{"enabled":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ebs_snapshot":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"data_encryption_key_id":{"type":"string","computed":true},"description":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"owner_alias":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"owners":{"type":["list","string"],"optional":true},"restorable_by_user_ids":{"type":["list","string"],"optional":true},"snapshot_id":{"type":"string","computed":true},"snapshot_ids":{"type":["list","string"],"optional":true},"state":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ebs_snapshot_ids":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["list","string"],"computed":true},"owners":{"type":["list","string"],"optional":true},"restorable_by_user_ids":{"type":["list","string"],"optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ebs_volume":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"iops":{"type":"number","computed":true},"kms_key_id":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"multi_attach_enabled":{"type":"bool","computed":true},"outpost_arn":{"type":"string","computed":true},"size":{"type":"number","computed":true},"snapshot_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_type":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ebs_volumes":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_coip_pool":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_gateway_route_table_id":{"type":"string","optional":true,"computed":true},"pool_cidrs":{"type":["set","string"],"computed":true},"pool_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_coip_pools":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"pool_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_instance_type_offering":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","computed":true},"location_type":{"type":"string","optional":true},"preferred_instance_types":{"type":["list","string"],"optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_instance_type_offerings":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_types":{"type":["set","string"],"computed":true},"location_type":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_local_gateway":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"outpost_arn":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_route_table":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_gateway_id":{"type":"string","optional":true,"computed":true},"local_gateway_route_table_id":{"type":"string","optional":true,"computed":true},"outpost_arn":{"type":"string","optional":true,"computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_route_tables":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_virtual_interface":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_address":{"type":"string","computed":true},"local_bgp_asn":{"type":"number","computed":true},"local_gateway_id":{"type":"string","computed":true},"local_gateway_virtual_interface_ids":{"type":["set","string"],"computed":true},"peer_address":{"type":"string","computed":true},"peer_bgp_asn":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vlan":{"type":"number","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_virtual_interface_group":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_gateway_id":{"type":"string","optional":true,"computed":true},"local_gateway_virtual_interface_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_virtual_interface_groups":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"local_gateway_virtual_interface_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateways":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_spot_price":{"version":0,"block":{"attributes":{"availability_zone":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","optional":true},"spot_price":{"type":"string","computed":true},"spot_price_timestamp":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"number","computed":true},"arn":{"type":"string","computed":true},"association_default_route_table_id":{"type":"string","computed":true},"auto_accept_shared_attachments":{"type":"string","computed":true},"default_route_table_association":{"type":"string","computed":true},"default_route_table_propagation":{"type":"string","computed":true},"description":{"type":"string","computed":true},"dns_support":{"type":"string","computed":true},"id":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"propagation_default_route_table_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpn_ecmp_support":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_dx_gateway_attachment":{"version":0,"block":{"attributes":{"dx_gateway_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_peering_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true},"peer_account_id":{"type":"string","computed":true},"peer_region":{"type":"string","computed":true},"peer_transit_gateway_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_route_table":{"version":0,"block":{"attributes":{"default_association_route_table":{"type":"bool","computed":true},"default_propagation_route_table":{"type":"bool","computed":true},"id":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_vpc_attachment":{"version":0,"block":{"attributes":{"dns_support":{"type":"string","computed":true},"id":{"type":"string","optional":true},"ipv6_support":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true},"vpc_owner_id":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_vpn_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","optional":true},"vpn_connection_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ecr_authorization_token":{"version":0,"block":{"attributes":{"authorization_token":{"type":"string","computed":true,"sensitive":true},"expires_at":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"password":{"type":"string","computed":true,"sensitive":true},"proxy_endpoint":{"type":"string","computed":true},"registry_id":{"type":"string","optional":true},"user_name":{"type":"string","computed":true}}}},"aws_ecr_image":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"image_digest":{"type":"string","optional":true,"computed":true},"image_pushed_at":{"type":"number","computed":true},"image_size_in_bytes":{"type":"number","computed":true},"image_tag":{"type":"string","optional":true},"image_tags":{"type":["list","string"],"computed":true},"registry_id":{"type":"string","optional":true,"computed":true},"repository_name":{"type":"string","required":true}}}},"aws_ecr_repository":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"encryption_configuration":{"type":["list",["object",{"encryption_type":"string","kms_key":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"image_scanning_configuration":{"type":["list",["object",{"scan_on_push":"bool"}]],"computed":true},"image_tag_mutability":{"type":"string","computed":true},"name":{"type":"string","required":true},"registry_id":{"type":"string","optional":true,"computed":true},"repository_url":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_ecs_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"pending_tasks_count":{"type":"number","computed":true},"registered_container_instances_count":{"type":"number","computed":true},"running_tasks_count":{"type":"number","computed":true},"setting":{"type":["set",["object",{"name":"string","value":"string"}]],"computed":true},"status":{"type":"string","computed":true}}}},"aws_ecs_container_definition":{"version":0,"block":{"attributes":{"container_name":{"type":"string","required":true},"cpu":{"type":"number","computed":true},"disable_networking":{"type":"bool","computed":true},"docker_labels":{"type":["map","string"],"computed":true},"environment":{"type":["map","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"image":{"type":"string","computed":true},"image_digest":{"type":"string","computed":true},"memory":{"type":"number","computed":true},"memory_reservation":{"type":"number","computed":true},"task_definition":{"type":"string","required":true}}}},"aws_ecs_service":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cluster_arn":{"type":"string","required":true},"desired_count":{"type":"number","computed":true},"id":{"type":"string","optional":true,"computed":true},"launch_type":{"type":"string","computed":true},"scheduling_strategy":{"type":"string","computed":true},"service_name":{"type":"string","required":true},"task_definition":{"type":"string","computed":true}}}},"aws_ecs_task_definition":{"version":1,"block":{"attributes":{"family":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"network_mode":{"type":"string","computed":true},"revision":{"type":"number","computed":true},"status":{"type":"string","computed":true},"task_definition":{"type":"string","required":true},"task_role_arn":{"type":"string","computed":true}}}},"aws_efs_access_point":{"version":0,"block":{"attributes":{"access_point_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"file_system_arn":{"type":"string","computed":true},"file_system_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"posix_user":{"type":["list",["object",{"gid":"number","secondary_gids":["set","number"],"uid":"number"}]],"computed":true},"root_directory":{"type":["list",["object",{"creation_info":["list",["object",{"owner_gid":"number","owner_uid":"number","permissions":"string"}]],"path":"string"}]],"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_efs_access_points":{"version":0,"block":{"attributes":{"arns":{"type":["set","string"],"computed":true},"file_system_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true}}}},"aws_efs_file_system":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_token":{"type":"string","optional":true,"computed":true},"dns_name":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"file_system_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"lifecycle_policy":{"type":["list",["object",{"transition_to_ia":"string"}]],"computed":true},"performance_mode":{"type":"string","computed":true},"provisioned_throughput_in_mibps":{"type":"number","computed":true},"size_in_bytes":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"throughput_mode":{"type":"string","computed":true}}}},"aws_efs_mount_target":{"version":0,"block":{"attributes":{"availability_zone_id":{"type":"string","computed":true},"availability_zone_name":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"file_system_arn":{"type":"string","computed":true},"file_system_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","computed":true},"mount_target_dns_name":{"type":"string","computed":true},"mount_target_id":{"type":"string","required":true},"network_interface_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_id":{"type":"string","computed":true}}}},"aws_eip":{"version":0,"block":{"attributes":{"association_id":{"type":"string","computed":true},"customer_owned_ip":{"type":"string","computed":true},"customer_owned_ipv4_pool":{"type":"string","computed":true},"domain":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","computed":true},"network_interface_id":{"type":"string","computed":true},"network_interface_owner_id":{"type":"string","computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","optional":true,"computed":true},"public_ipv4_pool":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_eks_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_authority":{"type":["list",["object",{"data":"string"}]],"computed":true},"created_at":{"type":"string","computed":true},"enabled_cluster_log_types":{"type":["set","string"],"computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identity":{"type":["list",["object",{"oidc":["list",["object",{"issuer":"string"}]]}]],"computed":true},"name":{"type":"string","required":true},"platform_version":{"type":"string","computed":true},"role_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"version":{"type":"string","computed":true},"vpc_config":{"type":["list",["object",{"cluster_security_group_id":"string","endpoint_private_access":"bool","endpoint_public_access":"bool","public_access_cidrs":["set","string"],"security_group_ids":["set","string"],"subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true}}}},"aws_eks_cluster_auth":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"token":{"type":"string","computed":true,"sensitive":true}}}},"aws_elastic_beanstalk_application":{"version":0,"block":{"attributes":{"appversion_lifecycle":{"type":["list",["object",{"delete_source_from_s3":"bool","max_age_in_days":"number","max_count":"number","service_role":"string"}]],"computed":true},"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_elastic_beanstalk_hosted_zone":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_elastic_beanstalk_solution_stack":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"most_recent":{"type":"bool","optional":true},"name":{"type":"string","computed":true},"name_regex":{"type":"string","required":true}}}},"aws_elasticache_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","computed":true},"cache_nodes":{"type":["list",["object",{"address":"string","availability_zone":"string","id":"string","port":"number"}]],"computed":true},"cluster_address":{"type":"string","computed":true},"cluster_id":{"type":"string","required":true},"configuration_endpoint":{"type":"string","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"maintenance_window":{"type":"string","computed":true},"node_type":{"type":"string","computed":true},"notification_topic_arn":{"type":"string","computed":true},"num_cache_nodes":{"type":"number","computed":true},"parameter_group_name":{"type":"string","computed":true},"port":{"type":"number","computed":true},"replication_group_id":{"type":"string","computed":true},"security_group_ids":{"type":["set","string"],"computed":true},"security_group_names":{"type":["set","string"],"computed":true},"snapshot_retention_limit":{"type":"number","computed":true},"snapshot_window":{"type":"string","computed":true},"subnet_group_name":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_elasticache_replication_group":{"version":1,"block":{"attributes":{"auth_token_enabled":{"type":"bool","computed":true},"automatic_failover_enabled":{"type":"bool","computed":true},"configuration_endpoint_address":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"member_clusters":{"type":["set","string"],"computed":true},"node_type":{"type":"string","computed":true},"number_cache_clusters":{"type":"number","computed":true},"port":{"type":"number","computed":true},"primary_endpoint_address":{"type":"string","computed":true},"replication_group_description":{"type":"string","computed":true},"replication_group_id":{"type":"string","required":true},"snapshot_retention_limit":{"type":"number","computed":true},"snapshot_window":{"type":"string","computed":true}}}},"aws_elasticsearch_domain":{"version":0,"block":{"attributes":{"access_policies":{"type":"string","computed":true},"advanced_options":{"type":["map","string"],"computed":true},"advanced_security_options":{"type":["list",["object",{"enabled":"bool","internal_user_database_enabled":"bool"}]],"computed":true},"arn":{"type":"string","computed":true},"cluster_config":{"type":["list",["object",{"dedicated_master_count":"number","dedicated_master_enabled":"bool","dedicated_master_type":"string","instance_count":"number","instance_type":"string","warm_count":"number","warm_enabled":"bool","warm_type":"string","zone_awareness_config":["list",["object",{"availability_zone_count":"number"}]],"zone_awareness_enabled":"bool"}]],"computed":true},"cognito_options":{"type":["list",["object",{"enabled":"bool","identity_pool_id":"string","role_arn":"string","user_pool_id":"string"}]],"computed":true},"created":{"type":"bool","computed":true},"deleted":{"type":"bool","computed":true},"domain_id":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"ebs_options":{"type":["list",["object",{"ebs_enabled":"bool","iops":"number","volume_size":"number","volume_type":"string"}]],"computed":true},"elasticsearch_version":{"type":"string","computed":true},"encryption_at_rest":{"type":["list",["object",{"enabled":"bool","kms_key_id":"string"}]],"computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kibana_endpoint":{"type":"string","computed":true},"log_publishing_options":{"type":["set",["object",{"cloudwatch_log_group_arn":"string","enabled":"bool","log_type":"string"}]],"computed":true},"node_to_node_encryption":{"type":["list",["object",{"enabled":"bool"}]],"computed":true},"processing":{"type":"bool","computed":true},"snapshot_options":{"type":["list",["object",{"automated_snapshot_start_hour":"number"}]],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_options":{"type":["list",["object",{"availability_zones":["set","string"],"security_group_ids":["set","string"],"subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true}}}},"aws_elb":{"version":0,"block":{"attributes":{"access_logs":{"type":["list",["object",{"bucket":"string","bucket_prefix":"string","enabled":"bool","interval":"number"}]],"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"connection_draining":{"type":"bool","computed":true},"connection_draining_timeout":{"type":"number","computed":true},"cross_zone_load_balancing":{"type":"bool","computed":true},"dns_name":{"type":"string","computed":true},"health_check":{"type":["list",["object",{"healthy_threshold":"number","interval":"number","target":"string","timeout":"number","unhealthy_threshold":"number"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","computed":true},"instances":{"type":["set","string"],"computed":true},"internal":{"type":"bool","computed":true},"listener":{"type":["set",["object",{"instance_port":"number","instance_protocol":"string","lb_port":"number","lb_protocol":"string","ssl_certificate_id":"string"}]],"computed":true},"name":{"type":"string","required":true},"security_groups":{"type":["set","string"],"computed":true},"source_security_group":{"type":"string","computed":true},"source_security_group_id":{"type":"string","computed":true},"subnets":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"zone_id":{"type":"string","computed":true}}}},"aws_elb_hosted_zone_id":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_elb_service_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_glue_script":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"language":{"type":"string","optional":true},"python_script":{"type":"string","computed":true},"scala_code":{"type":"string","computed":true}},"block_types":{"dag_edge":{"nesting_mode":"list","block":{"attributes":{"source":{"type":"string","required":true},"target":{"type":"string","required":true},"target_parameter":{"type":"string","optional":true}}},"min_items":1},"dag_node":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","required":true},"line_number":{"type":"number","optional":true},"node_type":{"type":"string","required":true}},"block_types":{"args":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true},"param":{"type":"bool","optional":true},"value":{"type":"string","required":true}}},"min_items":1}}},"min_items":1}}}},"aws_guardduty_detector":{"version":0,"block":{"attributes":{"finding_publishing_frequency":{"type":"string","computed":true},"id":{"type":"string","optional":true},"service_role_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true}}}},"aws_iam_account_alias":{"version":0,"block":{"attributes":{"account_alias":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_iam_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"group_id":{"type":"string","computed":true},"group_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"path":{"type":"string","computed":true},"users":{"type":["list",["object",{"arn":"string","path":"string","user_id":"string","user_name":"string"}]],"computed":true}}}},"aws_iam_instance_profile":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"create_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"path":{"type":"string","computed":true},"role_arn":{"type":"string","computed":true},"role_id":{"type":"string","computed":true},"role_name":{"type":"string","computed":true}}}},"aws_iam_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"path":{"type":"string","computed":true},"policy":{"type":"string","computed":true}}}},"aws_iam_policy_document":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"json":{"type":"string","computed":true},"override_json":{"type":"string","optional":true},"policy_id":{"type":"string","optional":true},"source_json":{"type":"string","optional":true},"version":{"type":"string","optional":true}},"block_types":{"statement":{"nesting_mode":"list","block":{"attributes":{"actions":{"type":["set","string"],"optional":true},"effect":{"type":"string","optional":true},"not_actions":{"type":["set","string"],"optional":true},"not_resources":{"type":["set","string"],"optional":true},"resources":{"type":["set","string"],"optional":true},"sid":{"type":"string","optional":true}},"block_types":{"condition":{"nesting_mode":"set","block":{"attributes":{"test":{"type":"string","required":true},"values":{"type":["set","string"],"required":true},"variable":{"type":"string","required":true}}}},"not_principals":{"nesting_mode":"set","block":{"attributes":{"identifiers":{"type":["set","string"],"required":true},"type":{"type":"string","required":true}}}},"principals":{"nesting_mode":"set","block":{"attributes":{"identifiers":{"type":["set","string"],"required":true},"type":{"type":"string","required":true}}}}}}}}}},"aws_iam_role":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"assume_role_policy":{"type":"string","computed":true},"create_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"max_session_duration":{"type":"number","computed":true},"name":{"type":"string","required":true},"path":{"type":"string","computed":true},"permissions_boundary":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_server_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_body":{"type":"string","computed":true},"certificate_chain":{"type":"string","computed":true},"expiration_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"latest":{"type":"bool","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","computed":true},"path_prefix":{"type":"string","optional":true},"upload_date":{"type":"string","computed":true}}}},"aws_iam_user":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"path":{"type":"string","computed":true},"permissions_boundary":{"type":"string","computed":true},"user_id":{"type":"string","computed":true},"user_name":{"type":"string","required":true}}}},"aws_inspector_rules_packages":{"version":0,"block":{"attributes":{"arns":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_instance":{"version":1,"block":{"attributes":{"ami":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","computed":true},"availability_zone":{"type":"string","computed":true},"credit_specification":{"type":["list",["object",{"cpu_credits":"string"}]],"computed":true},"disable_api_termination":{"type":"bool","computed":true},"ebs_block_device":{"type":["set",["object",{"delete_on_termination":"bool","device_name":"string","encrypted":"bool","iops":"number","kms_key_id":"string","snapshot_id":"string","volume_id":"string","volume_size":"number","volume_type":"string"}]],"computed":true},"ebs_optimized":{"type":"bool","computed":true},"ephemeral_block_device":{"type":["list",["object",{"device_name":"string","no_device":"bool","virtual_name":"string"}]],"computed":true},"get_password_data":{"type":"bool","optional":true},"get_user_data":{"type":"bool","optional":true},"host_id":{"type":"string","computed":true},"iam_instance_profile":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true},"instance_state":{"type":"string","computed":true},"instance_tags":{"type":["map","string"],"optional":true,"computed":true},"instance_type":{"type":"string","computed":true},"key_name":{"type":"string","computed":true},"metadata_options":{"type":["list",["object",{"http_endpoint":"string","http_put_response_hop_limit":"number","http_tokens":"string"}]],"computed":true},"monitoring":{"type":"bool","computed":true},"network_interface_id":{"type":"string","computed":true},"outpost_arn":{"type":"string","computed":true},"password_data":{"type":"string","computed":true},"placement_group":{"type":"string","computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"root_block_device":{"type":["set",["object",{"delete_on_termination":"bool","device_name":"string","encrypted":"bool","iops":"number","kms_key_id":"string","volume_id":"string","volume_size":"number","volume_type":"string"}]],"computed":true},"secondary_private_ips":{"type":["set","string"],"computed":true},"security_groups":{"type":["set","string"],"computed":true},"source_dest_check":{"type":"bool","computed":true},"subnet_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"tenancy":{"type":"string","computed":true},"user_data":{"type":"string","computed":true},"user_data_base64":{"type":"string","computed":true},"vpc_security_group_ids":{"type":["set","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_instances":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["list","string"],"computed":true},"instance_state_names":{"type":["set","string"],"optional":true},"instance_tags":{"type":["map","string"],"optional":true,"computed":true},"private_ips":{"type":["list","string"],"computed":true},"public_ips":{"type":["list","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_internet_gateway":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"attachments":{"type":["list",["object",{"state":"string","vpc_id":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"internet_gateway_id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_iot_endpoint":{"version":0,"block":{"attributes":{"endpoint_address":{"type":"string","computed":true},"endpoint_type":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ip_ranges":{"version":0,"block":{"attributes":{"cidr_blocks":{"type":["list","string"],"computed":true},"create_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_blocks":{"type":["list","string"],"computed":true},"regions":{"type":["set","string"],"optional":true},"services":{"type":["set","string"],"required":true},"sync_token":{"type":"number","computed":true},"url":{"type":"string","optional":true}}}},"aws_kinesis_stream":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"closed_shards":{"type":["set","string"],"computed":true},"creation_timestamp":{"type":"number","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"open_shards":{"type":["set","string"],"computed":true},"retention_period":{"type":"number","computed":true},"shard_level_metrics":{"type":["set","string"],"computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_kms_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"target_key_arn":{"type":"string","computed":true},"target_key_id":{"type":"string","computed":true}}}},"aws_kms_ciphertext":{"version":0,"block":{"attributes":{"ciphertext_blob":{"type":"string","computed":true},"context":{"type":["map","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"plaintext":{"type":"string","required":true,"sensitive":true}}}},"aws_kms_key":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_account_id":{"type":"string","computed":true},"creation_date":{"type":"string","computed":true},"customer_master_key_spec":{"type":"string","computed":true},"deletion_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"enabled":{"type":"bool","computed":true},"expiration_model":{"type":"string","computed":true},"grant_tokens":{"type":["list","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"key_manager":{"type":"string","computed":true},"key_state":{"type":"string","computed":true},"key_usage":{"type":"string","computed":true},"origin":{"type":"string","computed":true},"valid_to":{"type":"string","computed":true}}}},"aws_kms_secret":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true}},"block_types":{"secret":{"nesting_mode":"set","block":{"attributes":{"context":{"type":["map","string"],"optional":true},"grant_tokens":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"payload":{"type":"string","required":true}}},"min_items":1}}}},"aws_kms_secrets":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"plaintext":{"type":["map","string"],"computed":true}},"block_types":{"secret":{"nesting_mode":"set","block":{"attributes":{"context":{"type":["map","string"],"optional":true},"grant_tokens":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"payload":{"type":"string","required":true}}},"min_items":1}}}},"aws_lambda_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"function_name":{"type":"string","required":true},"function_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_arn":{"type":"string","computed":true},"name":{"type":"string","required":true}}}},"aws_lambda_function":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"dead_letter_config":{"type":["list",["object",{"target_arn":"string"}]],"computed":true},"description":{"type":"string","computed":true},"environment":{"type":["list",["object",{"variables":["map","string"]}]],"computed":true},"file_system_config":{"type":["list",["object",{"arn":"string","local_mount_path":"string"}]],"computed":true},"function_name":{"type":"string","required":true},"handler":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_arn":{"type":"string","computed":true},"kms_key_arn":{"type":"string","computed":true},"last_modified":{"type":"string","computed":true},"layers":{"type":["list","string"],"computed":true},"memory_size":{"type":"number","computed":true},"qualified_arn":{"type":"string","computed":true},"qualifier":{"type":"string","optional":true},"reserved_concurrent_executions":{"type":"number","computed":true},"role":{"type":"string","computed":true},"runtime":{"type":"string","computed":true},"source_code_hash":{"type":"string","computed":true},"source_code_size":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"timeout":{"type":"number","computed":true},"tracing_config":{"type":["list",["object",{"mode":"string"}]],"computed":true},"version":{"type":"string","computed":true},"vpc_config":{"type":["list",["object",{"security_group_ids":["set","string"],"subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true}}}},"aws_lambda_invocation":{"version":0,"block":{"attributes":{"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"input":{"type":"string","required":true},"qualifier":{"type":"string","optional":true},"result":{"type":"string","computed":true}}}},"aws_lambda_layer_version":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compatible_runtime":{"type":"string","optional":true},"compatible_runtimes":{"type":["set","string"],"computed":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"layer_arn":{"type":"string","computed":true},"layer_name":{"type":"string","required":true},"license_info":{"type":"string","computed":true},"source_code_hash":{"type":"string","computed":true},"source_code_size":{"type":"number","computed":true},"version":{"type":"number","optional":true,"computed":true}}}},"aws_launch_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","computed":true},"ebs_block_device":{"type":["set",["object",{"delete_on_termination":"bool","device_name":"string","encrypted":"bool","iops":"number","no_device":"bool","snapshot_id":"string","volume_size":"number","volume_type":"string"}]],"computed":true},"ebs_optimized":{"type":"bool","computed":true},"enable_monitoring":{"type":"bool","computed":true},"ephemeral_block_device":{"type":["set",["object",{"device_name":"string","virtual_name":"string"}]],"computed":true},"iam_instance_profile":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","computed":true},"instance_type":{"type":"string","computed":true},"key_name":{"type":"string","computed":true},"name":{"type":"string","required":true},"placement_tenancy":{"type":"string","computed":true},"root_block_device":{"type":["list",["object",{"delete_on_termination":"bool","encrypted":"bool","iops":"number","volume_size":"number","volume_type":"string"}]],"computed":true},"security_groups":{"type":["set","string"],"computed":true},"spot_price":{"type":"string","computed":true},"user_data":{"type":"string","computed":true},"vpc_classic_link_id":{"type":"string","computed":true},"vpc_classic_link_security_groups":{"type":["set","string"],"computed":true}}}},"aws_launch_template":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"block_device_mappings":{"type":["list",["object",{"device_name":"string","ebs":["list",["object",{"delete_on_termination":"string","encrypted":"string","iops":"number","kms_key_id":"string","snapshot_id":"string","volume_size":"number","volume_type":"string"}]],"no_device":"string","virtual_name":"string"}]],"computed":true},"credit_specification":{"type":["list",["object",{"cpu_credits":"string"}]],"computed":true},"default_version":{"type":"number","computed":true},"description":{"type":"string","computed":true},"disable_api_termination":{"type":"bool","computed":true},"ebs_optimized":{"type":"string","computed":true},"elastic_gpu_specifications":{"type":["list",["object",{"type":"string"}]],"computed":true},"hibernation_options":{"type":["list",["object",{"configured":"bool"}]],"computed":true},"iam_instance_profile":{"type":["list",["object",{"arn":"string","name":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","computed":true},"instance_initiated_shutdown_behavior":{"type":"string","computed":true},"instance_market_options":{"type":["list",["object",{"market_type":"string","spot_options":["list",["object",{"block_duration_minutes":"number","instance_interruption_behavior":"string","max_price":"string","spot_instance_type":"string","valid_until":"string"}]]}]],"computed":true},"instance_type":{"type":"string","computed":true},"kernel_id":{"type":"string","computed":true},"key_name":{"type":"string","computed":true},"latest_version":{"type":"number","computed":true},"metadata_options":{"type":["list",["object",{"http_endpoint":"string","http_put_response_hop_limit":"number","http_tokens":"string"}]],"computed":true},"monitoring":{"type":["list",["object",{"enabled":"bool"}]],"computed":true},"name":{"type":"string","optional":true},"network_interfaces":{"type":["list",["object",{"associate_public_ip_address":"string","delete_on_termination":"string","description":"string","device_index":"number","ipv4_address_count":"number","ipv4_addresses":["set","string"],"ipv6_address_count":"number","ipv6_addresses":["set","string"],"network_interface_id":"string","private_ip_address":"string","security_groups":["set","string"],"subnet_id":"string"}]],"computed":true},"placement":{"type":["list",["object",{"affinity":"string","availability_zone":"string","group_name":"string","host_id":"string","partition_number":"number","spread_domain":"string","tenancy":"string"}]],"computed":true},"ram_disk_id":{"type":"string","computed":true},"security_group_names":{"type":["set","string"],"computed":true},"tag_specifications":{"type":["list",["object",{"resource_type":"string","tags":["map","string"]}]],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"user_data":{"type":"string","computed":true},"vpc_security_group_ids":{"type":["set","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_lb":{"version":0,"block":{"attributes":{"access_logs":{"type":["list",["object",{"bucket":"string","enabled":"bool","prefix":"string"}]],"computed":true},"arn":{"type":"string","optional":true,"computed":true},"arn_suffix":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"drop_invalid_header_fields":{"type":"bool","computed":true},"enable_deletion_protection":{"type":"bool","computed":true},"enable_http2":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","computed":true},"internal":{"type":"bool","computed":true},"ip_address_type":{"type":"string","computed":true},"load_balancer_type":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_mapping":{"type":["set",["object",{"allocation_id":"string","private_ipv4_address":"string","subnet_id":"string"}]],"computed":true},"subnets":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","computed":true},"zone_id":{"type":"string","computed":true}}}},"aws_lb_listener":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"certificate_arn":{"type":"string","computed":true},"default_action":{"type":["list",["object",{"authenticate_cognito":["list",["object",{"authentication_request_extra_params":["map","string"],"on_unauthenticated_request":"string","scope":"string","session_cookie_name":"string","session_timeout":"number","user_pool_arn":"string","user_pool_client_id":"string","user_pool_domain":"string"}]],"authenticate_oidc":["list",["object",{"authentication_request_extra_params":["map","string"],"authorization_endpoint":"string","client_id":"string","client_secret":"string","issuer":"string","on_unauthenticated_request":"string","scope":"string","session_cookie_name":"string","session_timeout":"number","token_endpoint":"string","user_info_endpoint":"string"}]],"fixed_response":["list",["object",{"content_type":"string","message_body":"string","status_code":"string"}]],"order":"number","redirect":["list",["object",{"host":"string","path":"string","port":"string","protocol":"string","query":"string","status_code":"string"}]],"target_group_arn":"string","type":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"load_balancer_arn":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"protocol":{"type":"string","computed":true},"ssl_policy":{"type":"string","computed":true}}}},"aws_lb_target_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"arn_suffix":{"type":"string","computed":true},"deregistration_delay":{"type":"number","computed":true},"health_check":{"type":["list",["object",{"enabled":"bool","healthy_threshold":"number","interval":"number","matcher":"string","path":"string","port":"string","protocol":"string","timeout":"number","unhealthy_threshold":"number"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"lambda_multi_value_headers_enabled":{"type":"bool","computed":true},"load_balancing_algorithm_type":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","computed":true},"protocol":{"type":"string","computed":true},"proxy_protocol_v2":{"type":"bool","computed":true},"slow_start":{"type":"number","computed":true},"stickiness":{"type":["list",["object",{"cookie_duration":"number","enabled":"bool","type":"string"}]],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"target_type":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_mq_broker":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","computed":true},"broker_id":{"type":"string","optional":true,"computed":true},"broker_name":{"type":"string","optional":true,"computed":true},"configuration":{"type":["list",["object",{"id":"string","revision":"number"}]],"computed":true},"deployment_mode":{"type":"string","computed":true},"encryption_options":{"type":["list",["object",{"kms_key_id":"string","use_aws_owned_key":"bool"}]],"computed":true},"engine_type":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"host_instance_type":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instances":{"type":["list",["object",{"console_url":"string","endpoints":["list","string"],"ip_address":"string"}]],"computed":true},"maintenance_window_start_time":{"type":["list",["object",{"day_of_week":"string","time_of_day":"string","time_zone":"string"}]],"computed":true},"publicly_accessible":{"type":"bool","computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"user":{"type":["set",["object",{"console_access":"bool","groups":["set","string"],"username":"string"}]],"computed":true}},"block_types":{"logs":{"nesting_mode":"list","block":{"attributes":{"audit":{"type":"bool","computed":true},"general":{"type":"bool","computed":true}}},"max_items":1}}}},"aws_msk_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bootstrap_brokers":{"type":"string","computed":true},"bootstrap_brokers_tls":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"kafka_version":{"type":"string","computed":true},"number_of_broker_nodes":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"zookeeper_connect_string":{"type":"string","computed":true}}}},"aws_msk_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kafka_versions":{"type":["set","string"],"computed":true},"latest_revision":{"type":"number","computed":true},"name":{"type":"string","required":true},"server_properties":{"type":"string","computed":true}}}},"aws_nat_gateway":{"version":0,"block":{"attributes":{"allocation_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_network_acls":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_network_interface":{"version":0,"block":{"attributes":{"association":{"type":["list",["object",{"allocation_id":"string","association_id":"string","ip_owner_id":"string","public_dns_name":"string","public_ip":"string"}]],"computed":true},"attachment":{"type":["list",["object",{"attachment_id":"string","device_index":"number","instance_id":"string","instance_owner_id":"string"}]],"computed":true},"availability_zone":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"interface_type":{"type":"string","computed":true},"ipv6_addresses":{"type":["set","string"],"computed":true},"mac_address":{"type":"string","computed":true},"outpost_arn":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"private_dns_name":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"private_ips":{"type":["list","string"],"computed":true},"requester_id":{"type":"string","computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_network_interfaces":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_organizations_organization":{"version":0,"block":{"attributes":{"accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string","status":"string"}]],"computed":true},"arn":{"type":"string","computed":true},"aws_service_access_principals":{"type":["set","string"],"computed":true},"enabled_policy_types":{"type":["set","string"],"computed":true},"feature_set":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"master_account_arn":{"type":"string","computed":true},"master_account_email":{"type":"string","computed":true},"master_account_id":{"type":"string","computed":true},"non_master_accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string","status":"string"}]],"computed":true},"roots":{"type":["list",["object",{"arn":"string","id":"string","name":"string","policy_types":["list",["object",{"status":"string","type":"string"}]]}]],"computed":true}}}},"aws_organizations_organizational_units":{"version":0,"block":{"attributes":{"children":{"type":["list",["object",{"arn":"string","id":"string","name":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"parent_id":{"type":"string","required":true}}}},"aws_outposts_outpost":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","computed":true},"availability_zone_id":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"site_id":{"type":"string","computed":true}}}},"aws_outposts_outpost_instance_type":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","optional":true,"computed":true},"preferred_instance_types":{"type":["list","string"],"optional":true}}}},"aws_outposts_outpost_instance_types":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instance_types":{"type":["set","string"],"computed":true}}}},"aws_outposts_outposts":{"version":0,"block":{"attributes":{"arns":{"type":["set","string"],"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"availability_zone_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"site_id":{"type":"string","optional":true,"computed":true}}}},"aws_outposts_site":{"version":0,"block":{"attributes":{"account_id":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true}}}},"aws_outposts_sites":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true}}}},"aws_partition":{"version":0,"block":{"attributes":{"dns_suffix":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"partition":{"type":"string","computed":true}}}},"aws_prefix_list":{"version":0,"block":{"attributes":{"cidr_blocks":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"prefix_list_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_pricing_product":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"result":{"type":"string","computed":true},"service_code":{"type":"string","required":true}},"block_types":{"filters":{"nesting_mode":"list","block":{"attributes":{"field":{"type":"string","required":true},"value":{"type":"string","required":true}}},"min_items":1}}}},"aws_qldb_ledger":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deletion_protection":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_ram_resource_share":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owning_account_id":{"type":"string","computed":true},"resource_owner":{"type":"string","required":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_rds_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"backtrack_window":{"type":"number","computed":true},"backup_retention_period":{"type":"number","computed":true},"cluster_identifier":{"type":"string","required":true},"cluster_members":{"type":["set","string"],"computed":true},"cluster_resource_id":{"type":"string","computed":true},"database_name":{"type":"string","computed":true},"db_cluster_parameter_group_name":{"type":"string","computed":true},"db_subnet_group_name":{"type":"string","computed":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"final_snapshot_identifier":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"iam_database_authentication_enabled":{"type":"bool","computed":true},"iam_roles":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"master_username":{"type":"string","computed":true},"port":{"type":"number","computed":true},"preferred_backup_window":{"type":"string","computed":true},"preferred_maintenance_window":{"type":"string","computed":true},"reader_endpoint":{"type":"string","computed":true},"replication_source_identifier":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_security_group_ids":{"type":["set","string"],"computed":true}}}},"aws_redshift_cluster":{"version":0,"block":{"attributes":{"allow_version_upgrade":{"type":"bool","computed":true},"automated_snapshot_retention_period":{"type":"number","computed":true},"availability_zone":{"type":"string","computed":true},"bucket_name":{"type":"string","computed":true},"cluster_identifier":{"type":"string","required":true},"cluster_parameter_group_name":{"type":"string","computed":true},"cluster_public_key":{"type":"string","computed":true},"cluster_revision_number":{"type":"string","computed":true},"cluster_security_groups":{"type":["list","string"],"computed":true},"cluster_subnet_group_name":{"type":"string","computed":true},"cluster_type":{"type":"string","computed":true},"cluster_version":{"type":"string","computed":true},"database_name":{"type":"string","computed":true},"elastic_ip":{"type":"string","computed":true},"enable_logging":{"type":"bool","computed":true},"encrypted":{"type":"bool","computed":true},"endpoint":{"type":"string","computed":true},"enhanced_vpc_routing":{"type":"bool","computed":true},"iam_roles":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"master_username":{"type":"string","computed":true},"node_type":{"type":"string","computed":true},"number_of_nodes":{"type":"number","computed":true},"port":{"type":"number","computed":true},"preferred_maintenance_window":{"type":"string","computed":true},"publicly_accessible":{"type":"bool","computed":true},"s3_key_prefix":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"vpc_security_group_ids":{"type":["list","string"],"computed":true}}}},"aws_redshift_service_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_region":{"version":0,"block":{"attributes":{"description":{"type":"string","computed":true},"endpoint":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true}}}},"aws_regions":{"version":0,"block":{"attributes":{"all_regions":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"names":{"type":["set","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_route":{"version":0,"block":{"attributes":{"destination_cidr_block":{"type":"string","optional":true,"computed":true},"destination_ipv6_cidr_block":{"type":"string","optional":true,"computed":true},"egress_only_gateway_id":{"type":"string","optional":true,"computed":true},"gateway_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true,"computed":true},"nat_gateway_id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","optional":true,"computed":true},"route_table_id":{"type":"string","required":true},"transit_gateway_id":{"type":"string","optional":true,"computed":true},"vpc_peering_connection_id":{"type":"string","optional":true,"computed":true}}}},"aws_route53_delegation_set":{"version":0,"block":{"attributes":{"caller_reference":{"type":"string","computed":true},"id":{"type":"string","required":true},"name_servers":{"type":["list","string"],"computed":true}}}},"aws_route53_resolver_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"resolver_endpoint_id":{"type":"string","optional":true,"computed":true},"resolver_rule_id":{"type":"string","optional":true,"computed":true},"rule_type":{"type":"string","optional":true,"computed":true},"share_status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_route53_resolver_rules":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","optional":true},"resolver_endpoint_id":{"type":"string","optional":true},"resolver_rule_ids":{"type":["set","string"],"computed":true},"rule_type":{"type":"string","optional":true},"share_status":{"type":"string","optional":true}}}},"aws_route53_zone":{"version":0,"block":{"attributes":{"caller_reference":{"type":"string","computed":true},"comment":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"linked_service_description":{"type":"string","computed":true},"linked_service_principal":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"name_servers":{"type":["list","string"],"computed":true},"private_zone":{"type":"bool","optional":true},"resource_record_set_count":{"type":"number","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","optional":true,"computed":true}}}},"aws_route_table":{"version":0,"block":{"attributes":{"associations":{"type":["list",["object",{"gateway_id":"string","main":"bool","route_table_association_id":"string","route_table_id":"string","subnet_id":"string"}]],"computed":true},"gateway_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"route_table_id":{"type":"string","optional":true,"computed":true},"routes":{"type":["list",["object",{"cidr_block":"string","egress_only_gateway_id":"string","gateway_id":"string","instance_id":"string","ipv6_cidr_block":"string","nat_gateway_id":"string","network_interface_id":"string","transit_gateway_id":"string","vpc_peering_connection_id":"string"}]],"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_route_tables":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_s3_bucket":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bucket":{"type":"string","required":true},"bucket_domain_name":{"type":"string","computed":true},"bucket_regional_domain_name":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","computed":true},"website_domain":{"type":"string","computed":true},"website_endpoint":{"type":"string","computed":true}}}},"aws_s3_bucket_object":{"version":0,"block":{"attributes":{"body":{"type":"string","computed":true},"bucket":{"type":"string","required":true},"cache_control":{"type":"string","computed":true},"content_disposition":{"type":"string","computed":true},"content_encoding":{"type":"string","computed":true},"content_language":{"type":"string","computed":true},"content_length":{"type":"number","computed":true},"content_type":{"type":"string","computed":true},"etag":{"type":"string","computed":true},"expiration":{"type":"string","computed":true},"expires":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","required":true},"last_modified":{"type":"string","computed":true},"metadata":{"type":["map","string"],"computed":true},"object_lock_legal_hold_status":{"type":"string","computed":true},"object_lock_mode":{"type":"string","computed":true},"object_lock_retain_until_date":{"type":"string","computed":true},"range":{"type":"string","optional":true},"server_side_encryption":{"type":"string","computed":true},"sse_kms_key_id":{"type":"string","computed":true},"storage_class":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"version_id":{"type":"string","optional":true,"computed":true},"website_redirect_location":{"type":"string","computed":true}}}},"aws_s3_bucket_objects":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"common_prefixes":{"type":["list","string"],"computed":true},"delimiter":{"type":"string","optional":true},"encoding_type":{"type":"string","optional":true},"fetch_owner":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"keys":{"type":["list","string"],"computed":true},"max_keys":{"type":"number","optional":true},"owners":{"type":["list","string"],"computed":true},"prefix":{"type":"string","optional":true},"start_after":{"type":"string","optional":true}}}},"aws_secretsmanager_secret":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","computed":true},"rotation_enabled":{"type":"bool","computed":true},"rotation_lambda_arn":{"type":"string","computed":true},"rotation_rules":{"type":["list",["object",{"automatically_after_days":"number"}]],"computed":true},"tags":{"type":["map","string"],"computed":true}}}},"aws_secretsmanager_secret_rotation":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"rotation_enabled":{"type":"bool","computed":true},"rotation_lambda_arn":{"type":"string","computed":true},"rotation_rules":{"type":["list",["object",{"automatically_after_days":"number"}]],"computed":true},"secret_id":{"type":"string","required":true}}}},"aws_secretsmanager_secret_version":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"secret_binary":{"type":"string","computed":true,"sensitive":true},"secret_id":{"type":"string","required":true},"secret_string":{"type":"string","computed":true,"sensitive":true},"version_id":{"type":"string","optional":true,"computed":true},"version_stage":{"type":"string","optional":true},"version_stages":{"type":["set","string"],"computed":true}}}},"aws_security_group":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_security_groups":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["list","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_ids":{"type":["list","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_servicequotas_service":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"service_code":{"type":"string","computed":true},"service_name":{"type":"string","required":true}}}},"aws_servicequotas_service_quota":{"version":0,"block":{"attributes":{"adjustable":{"type":"bool","computed":true},"arn":{"type":"string","computed":true},"default_value":{"type":"number","computed":true},"global_quota":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"quota_code":{"type":"string","optional":true,"computed":true},"quota_name":{"type":"string","optional":true,"computed":true},"service_code":{"type":"string","required":true},"service_name":{"type":"string","computed":true},"value":{"type":"number","computed":true}}}},"aws_sfn_activity":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"creation_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true}}}},"aws_sfn_state_machine":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_date":{"type":"string","computed":true},"definition":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true}}}},"aws_sns_topic":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_sqs_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"url":{"type":"string","computed":true}}}},"aws_ssm_document":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"content":{"type":"string","computed":true},"document_format":{"type":"string","optional":true},"document_type":{"type":"string","computed":true},"document_version":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_ssm_parameter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"type":{"type":"string","computed":true},"value":{"type":"string","computed":true,"sensitive":true},"version":{"type":"number","computed":true},"with_decryption":{"type":"bool","optional":true}}}},"aws_ssm_patch_baseline":{"version":0,"block":{"attributes":{"default_baseline":{"type":"bool","optional":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"name_prefix":{"type":"string","optional":true},"operating_system":{"type":"string","optional":true},"owner":{"type":"string","required":true}}}},"aws_storagegateway_local_disk":{"version":0,"block":{"attributes":{"disk_id":{"type":"string","computed":true},"disk_node":{"type":"string","optional":true},"disk_path":{"type":"string","optional":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_subnet":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_ipv6_address_on_creation":{"type":"bool","computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"availability_zone_id":{"type":"string","optional":true,"computed":true},"cidr_block":{"type":"string","optional":true,"computed":true},"default_for_az":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block_association_id":{"type":"string","computed":true},"map_public_ip_on_launch":{"type":"bool","computed":true},"outpost_arn":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_subnet_ids":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","required":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_transfer_server":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identity_provider_type":{"type":"string","computed":true},"invocation_role":{"type":"string","computed":true},"logging_role":{"type":"string","computed":true},"server_id":{"type":"string","required":true},"url":{"type":"string","computed":true}}}},"aws_vpc":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"cidr_block":{"type":"string","optional":true,"computed":true},"cidr_block_associations":{"type":["list",["object",{"association_id":"string","cidr_block":"string","state":"string"}]],"computed":true},"default":{"type":"bool","optional":true,"computed":true},"dhcp_options_id":{"type":"string","optional":true,"computed":true},"enable_dns_hostnames":{"type":"bool","computed":true},"enable_dns_support":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_tenancy":{"type":"string","computed":true},"ipv6_association_id":{"type":"string","computed":true},"ipv6_cidr_block":{"type":"string","computed":true},"main_route_table_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpc_dhcp_options":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"dhcp_options_id":{"type":"string","optional":true,"computed":true},"domain_name":{"type":"string","computed":true},"domain_name_servers":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"netbios_name_servers":{"type":["list","string"],"computed":true},"netbios_node_type":{"type":"string","computed":true},"ntp_servers":{"type":["list","string"],"computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpc_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cidr_blocks":{"type":["list","string"],"computed":true},"dns_entry":{"type":["list",["object",{"dns_name":"string","hosted_zone_id":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_ids":{"type":["set","string"],"computed":true},"owner_id":{"type":"string","computed":true},"policy":{"type":"string","computed":true},"prefix_list_id":{"type":"string","computed":true},"private_dns_enabled":{"type":"bool","computed":true},"requester_managed":{"type":"bool","computed":true},"route_table_ids":{"type":["set","string"],"computed":true},"security_group_ids":{"type":["set","string"],"computed":true},"service_name":{"type":"string","optional":true,"computed":true},"state":{"type":"string","optional":true,"computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_endpoint_type":{"type":"string","computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpc_endpoint_service":{"version":0,"block":{"attributes":{"acceptance_required":{"type":"bool","computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"base_endpoint_dns_names":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"manages_vpc_endpoints":{"type":"bool","computed":true},"owner":{"type":"string","computed":true},"private_dns_name":{"type":"string","computed":true},"service":{"type":"string","optional":true},"service_id":{"type":"string","computed":true},"service_name":{"type":"string","optional":true,"computed":true},"service_type":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_endpoint_policy_supported":{"type":"bool","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_vpc_peering_connection":{"version":0,"block":{"attributes":{"accepter":{"type":["map","bool"],"computed":true},"cidr_block":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","optional":true,"computed":true},"peer_cidr_block":{"type":"string","optional":true,"computed":true},"peer_owner_id":{"type":"string","optional":true,"computed":true},"peer_region":{"type":"string","optional":true,"computed":true},"peer_vpc_id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true,"computed":true},"requester":{"type":["map","bool"],"computed":true},"status":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpcs":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpn_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"attached_vpc_id":{"type":"string","optional":true,"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_waf_ipset":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_waf_rate_based_rule":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_waf_rule":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_waf_web_acl":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafregional_ipset":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafregional_rate_based_rule":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafregional_rule":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafregional_web_acl":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafv2_ip_set":{"version":0,"block":{"attributes":{"addresses":{"type":["set","string"],"computed":true},"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address_version":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true}}}},"aws_wafv2_regex_pattern_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"regular_expression":{"type":["set",["object",{"regex_string":"string"}]],"computed":true},"scope":{"type":"string","required":true}}}},"aws_wafv2_rule_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true}}}},"aws_wafv2_web_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true}}}},"aws_workspaces_bundle":{"version":0,"block":{"attributes":{"bundle_id":{"type":"string","optional":true},"compute_type":{"type":["list",["object",{"name":"string"}]],"computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"owner":{"type":"string","optional":true},"root_storage":{"type":["list",["object",{"capacity":"string"}]],"computed":true},"user_storage":{"type":["list",["object",{"capacity":"string"}]],"computed":true}}}},"aws_workspaces_directory":{"version":0,"block":{"attributes":{"alias":{"type":"string","computed":true},"customer_user_name":{"type":"string","computed":true},"directory_id":{"type":"string","required":true},"directory_name":{"type":"string","computed":true},"directory_type":{"type":"string","computed":true},"dns_ip_addresses":{"type":["set","string"],"computed":true},"iam_role_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_group_ids":{"type":["set","string"],"computed":true},"registration_code":{"type":"string","computed":true},"self_service_permissions":{"type":["list",["object",{"change_compute_type":"bool","increase_volume_size":"bool","rebuild_workspace":"bool","restart_workspace":"bool","switch_running_mode":"bool"}]],"computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true},"workspace_security_group_id":{"type":"string","computed":true}}}}}},"null":{"provider":{"version":0,"block":{}},"resource_schemas":{"null_resource":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"triggers":{"type":["map","string"],"optional":true}}}}},"data_source_schemas":{"null_data_source":{"version":0,"block":{"attributes":{"has_computed_default":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"inputs":{"type":["map","string"],"optional":true},"outputs":{"type":["map","string"],"computed":true},"random":{"type":"string","computed":true}}}}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/terraform.tfstate b/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/terraform.tfstate new file mode 100644 index 00000000..3d7f472d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/terraform.tfstate @@ -0,0 +1,232 @@ +{ + "version": 4, + "terraform_version": "0.12.0", + "serial": 8, + "lineage": "491fd8f4-81b5-9890-520c-8a173c36e483", + "outputs": { + "foo": { + "value": "bar", + "type": "string", + "sensitive": true + }, + "interpolated": { + "value": "424881806176056736", + "type": "string" + }, + "interpolated_deep": { + "value": { + "foo": "bar", + "map": { + "bar": "baz", + "id": "424881806176056736" + }, + "number": 42 + }, + "type": [ + "object", + { + "foo": "string", + "map": [ + "object", + { + "bar": "string", + "id": "string" + } + ], + "number": "number" + } + ] + }, + "list": { + "value": [ + "foo", + "bar" + ], + "type": [ + "tuple", + [ + "string", + "string" + ] + ] + }, + "map": { + "value": { + "foo": "bar", + "number": 42 + }, + "type": [ + "object", + { + "foo": "string", + "number": "number" + } + ] + }, + "referenced": { + "value": "424881806176056736", + "type": "string" + }, + "referenced_deep": { + "value": { + "foo": "bar", + "map": { + "bar": "baz", + "id": "424881806176056736" + }, + "number": 42 + }, + "type": [ + "object", + { + "foo": "string", + "map": [ + "object", + { + "bar": "string", + "id": "string" + } + ], + "number": "number" + } + ] + }, + "string": { + "value": "foo", + "type": "string" + } + }, + "resources": [ + { + "mode": "data", + "type": "null_data_source", + "name": "baz", + "provider": "provider.null", + "instances": [ + { + "schema_version": 0, + "attributes": { + "has_computed_default": "default", + "id": "static", + "inputs": { + "bar_id": "4347220156304926627", + "foo_id": "424881806176056736" + }, + "outputs": { + "bar_id": "4347220156304926627", + "foo_id": "424881806176056736" + }, + "random": "1951353658349486401" + }, + "depends_on": [ + "null_resource.bar", + "null_resource.foo" + ] + } + ] + }, + { + "mode": "managed", + "type": "null_resource", + "name": "bar", + "provider": "provider.null", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "4347220156304926627", + "triggers": { + "foo_id": "424881806176056736" + } + }, + "depends_on": [ + "null_resource.foo" + ] + } + ] + }, + { + "mode": "managed", + "type": "null_resource", + "name": "baz", + "each": "list", + "provider": "provider.null", + "instances": [ + { + "index_key": 0, + "schema_version": 0, + "attributes": { + "id": "751901236459396488", + "triggers": { + "foo_id": "424881806176056736" + } + }, + "depends_on": [ + "null_resource.foo" + ] + }, + { + "index_key": 1, + "schema_version": 0, + "attributes": { + "id": "2106740714798375541", + "triggers": { + "foo_id": "424881806176056736" + } + }, + "depends_on": [ + "null_resource.foo" + ] + }, + { + "index_key": 2, + "schema_version": 0, + "attributes": { + "id": "8665755682221598193", + "triggers": { + "foo_id": "424881806176056736" + } + }, + "depends_on": [ + "null_resource.foo" + ] + } + ] + }, + { + "mode": "managed", + "type": "null_resource", + "name": "foo", + "provider": "provider.null", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "424881806176056736", + "triggers": { + "foo": "bar" + } + } + } + ] + }, + { + "module": "module.foo", + "mode": "managed", + "type": "null_resource", + "name": "foo", + "provider": "provider.null", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "705267318028962447", + "triggers": { + "foo": "bar" + } + } + } + ] + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/variables.tf b/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/variables.tf new file mode 100644 index 00000000..3964e7ac --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/has_changes/variables.tf @@ -0,0 +1,15 @@ +variable "foo" { + description = "foobar" + default = "bar" +} + +variable "number" { + default = 42 +} + +variable "map" { + default = { + foo = "bar" + number = 42 + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/nested_config_keys/main.tf b/vendor/github.com/hashicorp/terraform-json/testdata/nested_config_keys/main.tf new file mode 100644 index 00000000..4a43a15a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/nested_config_keys/main.tf @@ -0,0 +1,16 @@ +provider "aws" { + region = "us-east-1" +} + +variable "foo" { + default = "/dev/sda1" +} + +resource "aws_instance" "foo" { + ami = "ami-foobar" + instance_type = "t2.micro" + + ebs_block_device { + device_name = "${var.foo}" + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/nested_config_keys/plan.json b/vendor/github.com/hashicorp/terraform-json/testdata/nested_config_keys/plan.json new file mode 100644 index 00000000..54cdbd86 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/nested_config_keys/plan.json @@ -0,0 +1 @@ +{"format_version":"0.1","terraform_version":"0.12.11","variables":{"foo":{"value":"/dev/sda1"}},"planned_values":{"root_module":{"resources":[{"address":"aws_instance.foo","mode":"managed","type":"aws_instance","name":"foo","provider_name":"aws","schema_version":1,"values":{"ami":"ami-foobar","credit_specification":[],"disable_api_termination":null,"ebs_block_device":[{"delete_on_termination":true,"device_name":"/dev/sda1"}],"ebs_optimized":null,"get_password_data":false,"hibernation":null,"iam_instance_profile":null,"instance_initiated_shutdown_behavior":null,"instance_type":"t2.micro","monitoring":null,"source_dest_check":true,"tags":null,"timeouts":null,"user_data":null,"user_data_base64":null}}]}},"resource_changes":[{"address":"aws_instance.foo","mode":"managed","type":"aws_instance","name":"foo","provider_name":"aws","change":{"actions":["create"],"before":null,"after":{"ami":"ami-foobar","credit_specification":[],"disable_api_termination":null,"ebs_block_device":[{"delete_on_termination":true,"device_name":"/dev/sda1"}],"ebs_optimized":null,"get_password_data":false,"hibernation":null,"iam_instance_profile":null,"instance_initiated_shutdown_behavior":null,"instance_type":"t2.micro","monitoring":null,"source_dest_check":true,"tags":null,"timeouts":null,"user_data":null,"user_data_base64":null},"after_unknown":{"arn":true,"associate_public_ip_address":true,"availability_zone":true,"cpu_core_count":true,"cpu_threads_per_core":true,"credit_specification":[],"ebs_block_device":[{"encrypted":true,"iops":true,"kms_key_id":true,"snapshot_id":true,"volume_id":true,"volume_size":true,"volume_type":true}],"ephemeral_block_device":true,"host_id":true,"id":true,"instance_state":true,"ipv6_address_count":true,"ipv6_addresses":true,"key_name":true,"metadata_options":true,"network_interface":true,"outpost_arn":true,"password_data":true,"placement_group":true,"primary_network_interface_id":true,"private_dns":true,"private_ip":true,"public_dns":true,"public_ip":true,"root_block_device":true,"secondary_private_ips":true,"security_groups":true,"subnet_id":true,"tenancy":true,"volume_tags":true,"vpc_security_group_ids":true}}}],"configuration":{"provider_config":{"aws":{"name":"aws","expressions":{"region":{"constant_value":"us-east-1"}}}},"root_module":{"resources":[{"address":"aws_instance.foo","mode":"managed","type":"aws_instance","name":"foo","provider_config_key":"aws","expressions":{"ami":{"constant_value":"ami-foobar"},"ebs_block_device":[{"device_name":{"references":["var.foo"]}}],"instance_type":{"constant_value":"t2.micro"}},"schema_version":1}],"variables":{"foo":{"default":"/dev/sda1"}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/nested_config_keys/schemas.json b/vendor/github.com/hashicorp/terraform-json/testdata/nested_config_keys/schemas.json new file mode 100644 index 00000000..4d4124df --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/nested_config_keys/schemas.json @@ -0,0 +1 @@ +{"format_version":"0.1","provider_schemas":{"aws":{"provider":{"version":0,"block":{"attributes":{"access_key":{"type":"string","description":"The access key for API operations. You can retrieve this\nfrom the 'Security \u0026 Credentials' section of the AWS console.","optional":true},"allowed_account_ids":{"type":["set","string"],"optional":true},"forbidden_account_ids":{"type":["set","string"],"optional":true},"insecure":{"type":"bool","description":"Explicitly allow the provider to perform \"insecure\" SSL requests. If omitted,default value is `false`","optional":true},"max_retries":{"type":"number","description":"The maximum number of times an AWS API request is\nbeing executed. If the API request still fails, an error is\nthrown.","optional":true},"profile":{"type":"string","description":"The profile for API operations. If not set, the default profile\ncreated with `aws configure` will be used.","optional":true},"region":{"type":"string","description":"The region where AWS operations will take place. Examples\nare us-east-1, us-west-2, etc.","required":true},"s3_force_path_style":{"type":"bool","description":"Set this to true to force the request to use path-style addressing,\ni.e., http://s3.amazonaws.com/BUCKET/KEY. By default, the S3 client will\nuse virtual hosted bucket addressing when possible\n(http://BUCKET.s3.amazonaws.com/KEY). Specific to the Amazon S3 service.","optional":true},"secret_key":{"type":"string","description":"The secret key for API operations. You can retrieve this\nfrom the 'Security \u0026 Credentials' section of the AWS console.","optional":true},"shared_credentials_file":{"type":"string","description":"The path to the shared credentials file. If not set\nthis defaults to ~/.aws/credentials.","optional":true},"skip_credentials_validation":{"type":"bool","description":"Skip the credentials validation via STS API. Used for AWS API implementations that do not have STS available/implemented.","optional":true},"skip_get_ec2_platforms":{"type":"bool","description":"Skip getting the supported EC2 platforms. Used by users that don't have ec2:DescribeAccountAttributes permissions.","optional":true},"skip_metadata_api_check":{"type":"bool","optional":true},"skip_region_validation":{"type":"bool","description":"Skip static validation of region name. Used by users of alternative AWS-like APIs or users w/ access to regions that are not public (yet).","optional":true},"skip_requesting_account_id":{"type":"bool","description":"Skip requesting the account ID. Used for AWS API implementations that do not have IAM/STS API and/or metadata API.","optional":true},"token":{"type":"string","description":"session token. A session token is only required if you are\nusing temporary security credentials.","optional":true}},"block_types":{"assume_role":{"nesting_mode":"list","block":{"attributes":{"duration_seconds":{"type":"number","description":"Seconds to restrict the assume role session duration.","optional":true},"external_id":{"type":"string","description":"Unique identifier that might be required for assuming a role in another account.","optional":true},"policy":{"type":"string","description":"IAM Policy JSON describing further restricting permissions for the IAM Role being assumed.","optional":true},"policy_arns":{"type":["set","string"],"description":"Amazon Resource Names (ARNs) of IAM Policies describing further restricting permissions for the IAM Role being assumed.","optional":true},"role_arn":{"type":"string","description":"Amazon Resource Name of an IAM Role to assume prior to making API calls.","optional":true},"session_name":{"type":"string","description":"Identifier for the assumed role session.","optional":true},"tags":{"type":["map","string"],"description":"Assume role session tags.","optional":true},"transitive_tag_keys":{"type":["set","string"],"description":"Assume role session tag keys to pass to any subsequent sessions.","optional":true}}},"max_items":1},"endpoints":{"nesting_mode":"set","block":{"attributes":{"accessanalyzer":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"acm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"acmpca":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"amplify":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"apigateway":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"applicationautoscaling":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"applicationinsights":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"appmesh":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"appstream":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"appsync":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"athena":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"autoscaling":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"autoscalingplans":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"backup":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"batch":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"budgets":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloud9":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudformation":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudfront":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudhsm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudsearch":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudtrail":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudwatch":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudwatchevents":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudwatchlogs":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codeartifact":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codebuild":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codecommit":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codedeploy":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codepipeline":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cognitoidentity":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cognitoidp":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"configservice":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cur":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dataexchange":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"datapipeline":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"datasync":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dax":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"devicefarm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"directconnect":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dlm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dms":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"docdb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ds":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dynamodb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ec2":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ecr":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ecs":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"efs":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"eks":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"elasticache":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"elasticbeanstalk":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"elastictranscoder":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"elb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"emr":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"es":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"firehose":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"fms":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"forecast":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"fsx":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"gamelift":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"glacier":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"globalaccelerator":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"glue":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"greengrass":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"guardduty":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"iam":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"imagebuilder":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"inspector":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"iot":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"iotanalytics":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"iotevents":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kafka":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kinesis":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kinesisanalytics":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kinesisanalyticsv2":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kinesisvideo":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kms":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"lakeformation":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"lambda":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"lexmodels":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"licensemanager":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"lightsail":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"macie":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"managedblockchain":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"marketplacecatalog":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediaconnect":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediaconvert":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"medialive":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediapackage":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediastore":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediastoredata":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mq":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"neptune":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"networkmanager":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"opsworks":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"organizations":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"outposts":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"personalize":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"pinpoint":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"pricing":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"qldb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"quicksight":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ram":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"rds":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"redshift":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"resourcegroups":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"resourcegroupstaggingapi":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"route53":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"route53domains":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"route53resolver":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"s3":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"s3control":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sagemaker":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sdb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"secretsmanager":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"securityhub":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"serverlessrepo":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"servicecatalog":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"servicediscovery":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"servicequotas":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ses":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"shield":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sns":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sqs":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ssm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"stepfunctions":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"storagegateway":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sts":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"swf":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"synthetics":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"transfer":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"waf":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"wafregional":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"wafv2":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"worklink":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"workmail":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"workspaces":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"xray":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true}}}},"ignore_tags":{"nesting_mode":"list","block":{"attributes":{"key_prefixes":{"type":["set","string"],"description":"Resource tag key prefixes to ignore across all resources.","optional":true},"keys":{"type":["set","string"],"description":"Resource tag keys to ignore across all resources.","optional":true}}},"max_items":1}}}},"resource_schemas":{"aws_accessanalyzer_analyzer":{"version":0,"block":{"attributes":{"analyzer_name":{"type":"string","required":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true}}}},"aws_acm_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_authority_arn":{"type":"string","optional":true},"certificate_body":{"type":"string","optional":true},"certificate_chain":{"type":"string","optional":true},"domain_name":{"type":"string","optional":true,"computed":true},"domain_validation_options":{"type":["set",["object",{"domain_name":"string","resource_record_name":"string","resource_record_type":"string","resource_record_value":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"status":{"type":"string","computed":true},"subject_alternative_names":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"validation_emails":{"type":["list","string"],"computed":true},"validation_method":{"type":"string","optional":true,"computed":true}},"block_types":{"options":{"nesting_mode":"list","block":{"attributes":{"certificate_transparency_logging_preference":{"type":"string","optional":true}}},"max_items":1}}}},"aws_acm_certificate_validation":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"validation_record_fqdns":{"type":["set","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_acmpca_certificate_authority":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate":{"type":"string","computed":true},"certificate_chain":{"type":"string","computed":true},"certificate_signing_request":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"not_after":{"type":"string","computed":true},"not_before":{"type":"string","computed":true},"permanent_deletion_time_in_days":{"type":"number","optional":true},"serial":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true}},"block_types":{"certificate_authority_configuration":{"nesting_mode":"list","block":{"attributes":{"key_algorithm":{"type":"string","required":true},"signing_algorithm":{"type":"string","required":true}},"block_types":{"subject":{"nesting_mode":"list","block":{"attributes":{"common_name":{"type":"string","optional":true},"country":{"type":"string","optional":true},"distinguished_name_qualifier":{"type":"string","optional":true},"generation_qualifier":{"type":"string","optional":true},"given_name":{"type":"string","optional":true},"initials":{"type":"string","optional":true},"locality":{"type":"string","optional":true},"organization":{"type":"string","optional":true},"organizational_unit":{"type":"string","optional":true},"pseudonym":{"type":"string","optional":true},"state":{"type":"string","optional":true},"surname":{"type":"string","optional":true},"title":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"revocation_configuration":{"nesting_mode":"list","block":{"block_types":{"crl_configuration":{"nesting_mode":"list","block":{"attributes":{"custom_cname":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"expiration_in_days":{"type":"number","required":true},"s3_bucket_name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_alb":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"arn_suffix":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"drop_invalid_header_fields":{"type":"bool","optional":true},"enable_cross_zone_load_balancing":{"type":"bool","optional":true},"enable_deletion_protection":{"type":"bool","optional":true},"enable_http2":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","optional":true},"internal":{"type":"bool","optional":true,"computed":true},"ip_address_type":{"type":"string","optional":true,"computed":true},"load_balancer_type":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"subnets":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"zone_id":{"type":"string","computed":true}},"block_types":{"access_logs":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"prefix":{"type":"string","optional":true}}},"max_items":1},"subnet_mapping":{"nesting_mode":"set","block":{"attributes":{"allocation_id":{"type":"string","optional":true},"private_ipv4_address":{"type":"string","optional":true},"subnet_id":{"type":"string","required":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_alb_listener":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"load_balancer_arn":{"type":"string","required":true},"port":{"type":"number","required":true},"protocol":{"type":"string","optional":true},"ssl_policy":{"type":"string","optional":true,"computed":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"attributes":{"order":{"type":"number","optional":true,"computed":true},"target_group_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"authenticate_cognito":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"user_pool_arn":{"type":"string","required":true},"user_pool_client_id":{"type":"string","required":true},"user_pool_domain":{"type":"string","required":true}}},"max_items":1},"authenticate_oidc":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"authorization_endpoint":{"type":"string","required":true},"client_id":{"type":"string","required":true},"client_secret":{"type":"string","required":true,"sensitive":true},"issuer":{"type":"string","required":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"token_endpoint":{"type":"string","required":true},"user_info_endpoint":{"type":"string","required":true}}},"max_items":1},"fixed_response":{"nesting_mode":"list","block":{"attributes":{"content_type":{"type":"string","required":true},"message_body":{"type":"string","optional":true},"status_code":{"type":"string","optional":true,"computed":true}}},"max_items":1},"forward":{"nesting_mode":"list","block":{"block_types":{"stickiness":{"nesting_mode":"list","block":{"attributes":{"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1},"target_group":{"nesting_mode":"set","block":{"attributes":{"arn":{"type":"string","required":true},"weight":{"type":"number","optional":true}}},"min_items":2,"max_items":5}}},"max_items":1},"redirect":{"nesting_mode":"list","block":{"attributes":{"host":{"type":"string","optional":true},"path":{"type":"string","optional":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"query":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"read":{"type":"string","optional":true}}}}}}},"aws_alb_listener_certificate":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true}}}},"aws_alb_listener_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true},"priority":{"type":"number","optional":true,"computed":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"order":{"type":"number","optional":true,"computed":true},"target_group_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"authenticate_cognito":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"user_pool_arn":{"type":"string","required":true},"user_pool_client_id":{"type":"string","required":true},"user_pool_domain":{"type":"string","required":true}}},"max_items":1},"authenticate_oidc":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"authorization_endpoint":{"type":"string","required":true},"client_id":{"type":"string","required":true},"client_secret":{"type":"string","required":true,"sensitive":true},"issuer":{"type":"string","required":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"token_endpoint":{"type":"string","required":true},"user_info_endpoint":{"type":"string","required":true}}},"max_items":1},"fixed_response":{"nesting_mode":"list","block":{"attributes":{"content_type":{"type":"string","required":true},"message_body":{"type":"string","optional":true},"status_code":{"type":"string","optional":true,"computed":true}}},"max_items":1},"forward":{"nesting_mode":"list","block":{"block_types":{"stickiness":{"nesting_mode":"list","block":{"attributes":{"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1},"target_group":{"nesting_mode":"set","block":{"attributes":{"arn":{"type":"string","required":true},"weight":{"type":"number","optional":true}}},"min_items":2,"max_items":5}}},"max_items":1},"redirect":{"nesting_mode":"list","block":{"attributes":{"host":{"type":"string","optional":true},"path":{"type":"string","optional":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"query":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"condition":{"nesting_mode":"set","block":{"block_types":{"host_header":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"http_header":{"nesting_mode":"list","block":{"attributes":{"http_header_name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}},"max_items":1},"http_request_method":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"path_pattern":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"query_string":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"value":{"type":"string","required":true}}}},"source_ip":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1}}},"min_items":1}}}},"aws_alb_target_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"arn_suffix":{"type":"string","computed":true},"deregistration_delay":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"lambda_multi_value_headers_enabled":{"type":"bool","optional":true},"load_balancing_algorithm_type":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"port":{"type":"number","optional":true},"protocol":{"type":"string","optional":true},"proxy_protocol_v2":{"type":"bool","optional":true},"slow_start":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true},"target_type":{"type":"string","optional":true},"vpc_id":{"type":"string","optional":true}},"block_types":{"health_check":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"healthy_threshold":{"type":"number","optional":true},"interval":{"type":"number","optional":true},"matcher":{"type":"string","optional":true,"computed":true},"path":{"type":"string","optional":true,"computed":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"timeout":{"type":"number","optional":true,"computed":true},"unhealthy_threshold":{"type":"number","optional":true}}},"max_items":1},"stickiness":{"nesting_mode":"list","block":{"attributes":{"cookie_duration":{"type":"number","optional":true},"enabled":{"type":"bool","optional":true},"type":{"type":"string","required":true}}},"max_items":1}}}},"aws_alb_target_group_attachment":{"version":0,"block":{"attributes":{"availability_zone":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"target_group_arn":{"type":"string","required":true},"target_id":{"type":"string","required":true}}}},"aws_ami":{"version":0,"block":{"attributes":{"architecture":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"ena_support":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"image_location":{"type":"string","optional":true,"computed":true},"kernel_id":{"type":"string","optional":true},"manage_ebs_snapshots":{"type":"bool","computed":true},"name":{"type":"string","required":true},"ramdisk_id":{"type":"string","optional":true},"root_device_name":{"type":"string","optional":true},"root_snapshot_id":{"type":"string","computed":true},"sriov_net_support":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"virtualization_type":{"type":"string","optional":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"snapshot_id":{"type":"string","optional":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"virtual_name":{"type":"string","required":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_ami_copy":{"version":0,"block":{"attributes":{"architecture":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"ena_support":{"type":"bool","computed":true},"encrypted":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"image_location":{"type":"string","computed":true},"kernel_id":{"type":"string","computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"manage_ebs_snapshots":{"type":"bool","computed":true},"name":{"type":"string","required":true},"ramdisk_id":{"type":"string","computed":true},"root_device_name":{"type":"string","computed":true},"root_snapshot_id":{"type":"string","computed":true},"source_ami_id":{"type":"string","required":true},"source_ami_region":{"type":"string","required":true},"sriov_net_support":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"virtualization_type":{"type":"string","computed":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","computed":true},"device_name":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"iops":{"type":"number","computed":true},"snapshot_id":{"type":"string","computed":true},"volume_size":{"type":"number","computed":true},"volume_type":{"type":"string","computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","computed":true},"virtual_name":{"type":"string","computed":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_ami_from_instance":{"version":0,"block":{"attributes":{"architecture":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"ena_support":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"image_location":{"type":"string","computed":true},"kernel_id":{"type":"string","computed":true},"manage_ebs_snapshots":{"type":"bool","computed":true},"name":{"type":"string","required":true},"ramdisk_id":{"type":"string","computed":true},"root_device_name":{"type":"string","computed":true},"root_snapshot_id":{"type":"string","computed":true},"snapshot_without_reboot":{"type":"bool","optional":true},"source_instance_id":{"type":"string","required":true},"sriov_net_support":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"virtualization_type":{"type":"string","computed":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","computed":true},"device_name":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"iops":{"type":"number","computed":true},"snapshot_id":{"type":"string","computed":true},"volume_size":{"type":"number","computed":true},"volume_type":{"type":"string","computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","computed":true},"virtual_name":{"type":"string","computed":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_ami_launch_permission":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","required":true}}}},"aws_api_gateway_account":{"version":0,"block":{"attributes":{"cloudwatch_role_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"throttle_settings":{"type":["list",["object",{"burst_limit":"number","rate_limit":"number"}]],"computed":true}}}},"aws_api_gateway_api_key":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"value":{"type":"string","optional":true,"computed":true,"sensitive":true}}}},"aws_api_gateway_authorizer":{"version":0,"block":{"attributes":{"authorizer_credentials":{"type":"string","optional":true},"authorizer_result_ttl_in_seconds":{"type":"number","optional":true},"authorizer_uri":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"identity_source":{"type":"string","optional":true},"identity_validation_expression":{"type":"string","optional":true},"name":{"type":"string","required":true},"provider_arns":{"type":["set","string"],"optional":true},"rest_api_id":{"type":"string","required":true},"type":{"type":"string","optional":true}}}},"aws_api_gateway_base_path_mapping":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"base_path":{"type":"string","optional":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"stage_name":{"type":"string","optional":true}}}},"aws_api_gateway_client_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"expiration_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"pem_encoded_certificate":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_api_gateway_deployment":{"version":0,"block":{"attributes":{"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_url":{"type":"string","computed":true},"rest_api_id":{"type":"string","required":true},"stage_description":{"type":"string","optional":true},"stage_name":{"type":"string","optional":true},"triggers":{"type":["map","string"],"optional":true},"variables":{"type":["map","string"],"optional":true}}}},"aws_api_gateway_documentation_part":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"properties":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true}},"block_types":{"location":{"nesting_mode":"list","block":{"attributes":{"method":{"type":"string","optional":true},"name":{"type":"string","optional":true},"path":{"type":"string","optional":true},"status_code":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_api_gateway_documentation_version":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"rest_api_id":{"type":"string","required":true},"version":{"type":"string","required":true}}}},"aws_api_gateway_domain_name":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_arn":{"type":"string","optional":true},"certificate_body":{"type":"string","optional":true},"certificate_chain":{"type":"string","optional":true},"certificate_name":{"type":"string","optional":true},"certificate_private_key":{"type":"string","optional":true,"sensitive":true},"certificate_upload_date":{"type":"string","computed":true},"cloudfront_domain_name":{"type":"string","computed":true},"cloudfront_zone_id":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"regional_certificate_arn":{"type":"string","optional":true},"regional_certificate_name":{"type":"string","optional":true},"regional_domain_name":{"type":"string","computed":true},"regional_zone_id":{"type":"string","computed":true},"security_policy":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"endpoint_configuration":{"nesting_mode":"list","block":{"attributes":{"types":{"type":["list","string"],"required":true}}},"max_items":1}}}},"aws_api_gateway_gateway_response":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"response_parameters":{"type":["map","string"],"optional":true},"response_templates":{"type":["map","string"],"optional":true},"response_type":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"status_code":{"type":"string","optional":true}}}},"aws_api_gateway_integration":{"version":0,"block":{"attributes":{"cache_key_parameters":{"type":["set","string"],"optional":true},"cache_namespace":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","optional":true},"connection_type":{"type":"string","optional":true},"content_handling":{"type":"string","optional":true},"credentials":{"type":"string","optional":true},"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"integration_http_method":{"type":"string","optional":true},"passthrough_behavior":{"type":"string","optional":true,"computed":true},"request_parameters":{"type":["map","string"],"optional":true},"request_templates":{"type":["map","string"],"optional":true},"resource_id":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"timeout_milliseconds":{"type":"number","optional":true},"type":{"type":"string","required":true},"uri":{"type":"string","optional":true}}}},"aws_api_gateway_integration_response":{"version":0,"block":{"attributes":{"content_handling":{"type":"string","optional":true},"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"resource_id":{"type":"string","required":true},"response_parameters":{"type":["map","string"],"optional":true},"response_templates":{"type":["map","string"],"optional":true},"rest_api_id":{"type":"string","required":true},"selection_pattern":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}}},"aws_api_gateway_method":{"version":0,"block":{"attributes":{"api_key_required":{"type":"bool","optional":true},"authorization":{"type":"string","required":true},"authorization_scopes":{"type":["set","string"],"optional":true},"authorizer_id":{"type":"string","optional":true},"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"request_models":{"type":["map","string"],"optional":true},"request_parameters":{"type":["map","bool"],"optional":true},"request_validator_id":{"type":"string","optional":true},"resource_id":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true}}}},"aws_api_gateway_method_response":{"version":0,"block":{"attributes":{"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"resource_id":{"type":"string","required":true},"response_models":{"type":["map","string"],"optional":true},"response_parameters":{"type":["map","bool"],"optional":true},"rest_api_id":{"type":"string","required":true},"status_code":{"type":"string","required":true}}}},"aws_api_gateway_method_settings":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"method_path":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"stage_name":{"type":"string","required":true}},"block_types":{"settings":{"nesting_mode":"list","block":{"attributes":{"cache_data_encrypted":{"type":"bool","optional":true,"computed":true},"cache_ttl_in_seconds":{"type":"number","optional":true,"computed":true},"caching_enabled":{"type":"bool","optional":true,"computed":true},"data_trace_enabled":{"type":"bool","optional":true,"computed":true},"logging_level":{"type":"string","optional":true,"computed":true},"metrics_enabled":{"type":"bool","optional":true,"computed":true},"require_authorization_for_cache_control":{"type":"bool","optional":true,"computed":true},"throttling_burst_limit":{"type":"number","optional":true},"throttling_rate_limit":{"type":"number","optional":true},"unauthorized_cache_control_header_strategy":{"type":"string","optional":true,"computed":true}}},"min_items":1,"max_items":1}}}},"aws_api_gateway_model":{"version":0,"block":{"attributes":{"content_type":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"schema":{"type":"string","optional":true}}}},"aws_api_gateway_request_validator":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"validate_request_body":{"type":"bool","optional":true},"validate_request_parameters":{"type":"bool","optional":true}}}},"aws_api_gateway_resource":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"parent_id":{"type":"string","required":true},"path":{"type":"string","computed":true},"path_part":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true}}}},"aws_api_gateway_rest_api":{"version":0,"block":{"attributes":{"api_key_source":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"binary_media_types":{"type":["list","string"],"optional":true},"body":{"type":"string","optional":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"minimum_compression_size":{"type":"number","optional":true},"name":{"type":"string","required":true},"policy":{"type":"string","optional":true},"root_resource_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"endpoint_configuration":{"nesting_mode":"list","block":{"attributes":{"types":{"type":["list","string"],"required":true},"vpc_endpoint_ids":{"type":["set","string"],"optional":true}}},"max_items":1}}}},"aws_api_gateway_stage":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cache_cluster_enabled":{"type":"bool","optional":true},"cache_cluster_size":{"type":"string","optional":true},"client_certificate_id":{"type":"string","optional":true},"deployment_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"documentation_version":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_url":{"type":"string","computed":true},"rest_api_id":{"type":"string","required":true},"stage_name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"variables":{"type":["map","string"],"optional":true},"xray_tracing_enabled":{"type":"bool","optional":true}},"block_types":{"access_log_settings":{"nesting_mode":"list","block":{"attributes":{"destination_arn":{"type":"string","required":true},"format":{"type":"string","required":true}}},"max_items":1}}}},"aws_api_gateway_usage_plan":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"product_code":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"api_stages":{"nesting_mode":"list","block":{"attributes":{"api_id":{"type":"string","required":true},"stage":{"type":"string","required":true}}}},"quota_settings":{"nesting_mode":"list","block":{"attributes":{"limit":{"type":"number","required":true},"offset":{"type":"number","optional":true},"period":{"type":"string","required":true}}},"max_items":1},"throttle_settings":{"nesting_mode":"list","block":{"attributes":{"burst_limit":{"type":"number","optional":true},"rate_limit":{"type":"number","optional":true}}},"max_items":1}}}},"aws_api_gateway_usage_plan_key":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"key_type":{"type":"string","required":true},"name":{"type":"string","computed":true},"usage_plan_id":{"type":"string","required":true},"value":{"type":"string","computed":true}}}},"aws_api_gateway_vpc_link":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"target_arns":{"type":["list","string"],"required":true}}}},"aws_apigatewayv2_api":{"version":0,"block":{"attributes":{"api_endpoint":{"type":"string","computed":true},"api_key_selection_expression":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"credentials_arn":{"type":"string","optional":true},"description":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"protocol_type":{"type":"string","required":true},"route_key":{"type":"string","optional":true},"route_selection_expression":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"target":{"type":"string","optional":true},"version":{"type":"string","optional":true}},"block_types":{"cors_configuration":{"nesting_mode":"list","block":{"attributes":{"allow_credentials":{"type":"bool","optional":true},"allow_headers":{"type":["set","string"],"optional":true},"allow_methods":{"type":["set","string"],"optional":true},"allow_origins":{"type":["set","string"],"optional":true},"expose_headers":{"type":["set","string"],"optional":true},"max_age":{"type":"number","optional":true}}},"max_items":1}}}},"aws_apigatewayv2_api_mapping":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"api_mapping_key":{"type":"string","optional":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"stage":{"type":"string","required":true}}}},"aws_apigatewayv2_authorizer":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"authorizer_credentials_arn":{"type":"string","optional":true},"authorizer_type":{"type":"string","required":true},"authorizer_uri":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"identity_sources":{"type":["set","string"],"required":true},"name":{"type":"string","required":true}},"block_types":{"jwt_configuration":{"nesting_mode":"list","block":{"attributes":{"audience":{"type":["set","string"],"optional":true},"issuer":{"type":"string","optional":true}}},"max_items":1}}}},"aws_apigatewayv2_deployment":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"auto_deployed":{"type":"bool","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"triggers":{"type":["map","string"],"optional":true}}}},"aws_apigatewayv2_domain_name":{"version":0,"block":{"attributes":{"api_mapping_selection_expression":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"domain_name_configuration":{"nesting_mode":"list","block":{"attributes":{"certificate_arn":{"type":"string","required":true},"endpoint_type":{"type":"string","required":true},"hosted_zone_id":{"type":"string","computed":true},"security_policy":{"type":"string","required":true},"target_domain_name":{"type":"string","computed":true}}},"min_items":1,"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"update":{"type":"string","optional":true}}}}}}},"aws_apigatewayv2_integration":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"connection_id":{"type":"string","optional":true},"connection_type":{"type":"string","optional":true},"content_handling_strategy":{"type":"string","optional":true},"credentials_arn":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"integration_method":{"type":"string","optional":true},"integration_response_selection_expression":{"type":"string","computed":true},"integration_type":{"type":"string","required":true},"integration_uri":{"type":"string","optional":true},"passthrough_behavior":{"type":"string","optional":true},"payload_format_version":{"type":"string","optional":true},"request_parameters":{"type":["map","string"],"optional":true},"request_templates":{"type":["map","string"],"optional":true},"template_selection_expression":{"type":"string","optional":true},"timeout_milliseconds":{"type":"number","optional":true}},"block_types":{"tls_config":{"nesting_mode":"list","block":{"attributes":{"server_name_to_verify":{"type":"string","optional":true}}},"max_items":1}}}},"aws_apigatewayv2_integration_response":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"content_handling_strategy":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"integration_id":{"type":"string","required":true},"integration_response_key":{"type":"string","required":true},"response_templates":{"type":["map","string"],"optional":true},"template_selection_expression":{"type":"string","optional":true}}}},"aws_apigatewayv2_model":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"content_type":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"schema":{"type":"string","required":true}}}},"aws_apigatewayv2_route":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"api_key_required":{"type":"bool","optional":true},"authorization_scopes":{"type":["set","string"],"optional":true},"authorization_type":{"type":"string","optional":true},"authorizer_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"model_selection_expression":{"type":"string","optional":true},"operation_name":{"type":"string","optional":true},"request_models":{"type":["map","string"],"optional":true},"route_key":{"type":"string","required":true},"route_response_selection_expression":{"type":"string","optional":true},"target":{"type":"string","optional":true}}}},"aws_apigatewayv2_route_response":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"model_selection_expression":{"type":"string","optional":true},"response_models":{"type":["map","string"],"optional":true},"route_id":{"type":"string","required":true},"route_response_key":{"type":"string","required":true}}}},"aws_apigatewayv2_stage":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"auto_deploy":{"type":"bool","optional":true},"client_certificate_id":{"type":"string","optional":true},"deployment_id":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_url":{"type":"string","computed":true},"name":{"type":"string","required":true},"stage_variables":{"type":["map","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"access_log_settings":{"nesting_mode":"list","block":{"attributes":{"destination_arn":{"type":"string","required":true},"format":{"type":"string","required":true}}},"max_items":1},"default_route_settings":{"nesting_mode":"list","block":{"attributes":{"data_trace_enabled":{"type":"bool","optional":true},"detailed_metrics_enabled":{"type":"bool","optional":true},"logging_level":{"type":"string","optional":true,"computed":true},"throttling_burst_limit":{"type":"number","optional":true},"throttling_rate_limit":{"type":"number","optional":true}}},"max_items":1},"route_settings":{"nesting_mode":"set","block":{"attributes":{"data_trace_enabled":{"type":"bool","optional":true},"detailed_metrics_enabled":{"type":"bool","optional":true},"logging_level":{"type":"string","optional":true,"computed":true},"route_key":{"type":"string","required":true},"throttling_burst_limit":{"type":"number","optional":true},"throttling_rate_limit":{"type":"number","optional":true}}}}}}},"aws_apigatewayv2_vpc_link":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"security_group_ids":{"type":["set","string"],"required":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_app_cookie_stickiness_policy":{"version":0,"block":{"attributes":{"cookie_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"lb_port":{"type":"number","required":true},"load_balancer":{"type":"string","required":true},"name":{"type":"string","required":true}}}},"aws_appautoscaling_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"policy_type":{"type":"string","optional":true},"resource_id":{"type":"string","required":true},"scalable_dimension":{"type":"string","required":true},"service_namespace":{"type":"string","required":true}},"block_types":{"step_scaling_policy_configuration":{"nesting_mode":"list","block":{"attributes":{"adjustment_type":{"type":"string","optional":true},"cooldown":{"type":"number","optional":true},"metric_aggregation_type":{"type":"string","optional":true},"min_adjustment_magnitude":{"type":"number","optional":true}},"block_types":{"step_adjustment":{"nesting_mode":"set","block":{"attributes":{"metric_interval_lower_bound":{"type":"string","optional":true},"metric_interval_upper_bound":{"type":"string","optional":true},"scaling_adjustment":{"type":"number","required":true}}}}}},"max_items":1},"target_tracking_scaling_policy_configuration":{"nesting_mode":"list","block":{"attributes":{"disable_scale_in":{"type":"bool","optional":true},"scale_in_cooldown":{"type":"number","optional":true},"scale_out_cooldown":{"type":"number","optional":true},"target_value":{"type":"number","required":true}},"block_types":{"customized_metric_specification":{"nesting_mode":"list","block":{"attributes":{"metric_name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"statistic":{"type":"string","required":true},"unit":{"type":"string","optional":true}},"block_types":{"dimensions":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}},"max_items":1},"predefined_metric_specification":{"nesting_mode":"list","block":{"attributes":{"predefined_metric_type":{"type":"string","required":true},"resource_label":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_appautoscaling_scheduled_action":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"end_time":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"resource_id":{"type":"string","required":true},"scalable_dimension":{"type":"string","optional":true},"schedule":{"type":"string","optional":true},"service_namespace":{"type":"string","required":true},"start_time":{"type":"string","optional":true}},"block_types":{"scalable_target_action":{"nesting_mode":"list","block":{"attributes":{"max_capacity":{"type":"number","optional":true},"min_capacity":{"type":"number","optional":true}}},"max_items":1}}}},"aws_appautoscaling_target":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"max_capacity":{"type":"number","required":true},"min_capacity":{"type":"number","required":true},"resource_id":{"type":"string","required":true},"role_arn":{"type":"string","optional":true,"computed":true},"scalable_dimension":{"type":"string","required":true},"service_namespace":{"type":"string","required":true}}}},"aws_appmesh_mesh":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"block_types":{"egress_filter":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_appmesh_route":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"mesh_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"virtual_router_name":{"type":"string","required":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"attributes":{"priority":{"type":"number","optional":true}},"block_types":{"http_route":{"nesting_mode":"list","block":{"block_types":{"action":{"nesting_mode":"list","block":{"block_types":{"weighted_target":{"nesting_mode":"set","block":{"attributes":{"virtual_node":{"type":"string","required":true},"weight":{"type":"number","required":true}}},"min_items":1,"max_items":10}}},"min_items":1,"max_items":1},"match":{"nesting_mode":"list","block":{"attributes":{"method":{"type":"string","optional":true},"prefix":{"type":"string","required":true},"scheme":{"type":"string","optional":true}},"block_types":{"header":{"nesting_mode":"set","block":{"attributes":{"invert":{"type":"bool","optional":true},"name":{"type":"string","required":true}},"block_types":{"match":{"nesting_mode":"list","block":{"attributes":{"exact":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"regex":{"type":"string","optional":true},"suffix":{"type":"string","optional":true}},"block_types":{"range":{"nesting_mode":"list","block":{"attributes":{"end":{"type":"number","required":true},"start":{"type":"number","required":true}}},"max_items":1}}},"max_items":1}}},"max_items":10}}},"min_items":1,"max_items":1}}},"max_items":1},"tcp_route":{"nesting_mode":"list","block":{"block_types":{"action":{"nesting_mode":"list","block":{"block_types":{"weighted_target":{"nesting_mode":"set","block":{"attributes":{"virtual_node":{"type":"string","required":true},"weight":{"type":"number","required":true}}},"min_items":1,"max_items":10}}},"min_items":1,"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_appmesh_virtual_node":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"mesh_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"block_types":{"backend":{"nesting_mode":"set","block":{"block_types":{"virtual_service":{"nesting_mode":"list","block":{"attributes":{"virtual_service_name":{"type":"string","required":true}}},"max_items":1}}},"max_items":25},"listener":{"nesting_mode":"list","block":{"block_types":{"health_check":{"nesting_mode":"list","block":{"attributes":{"healthy_threshold":{"type":"number","required":true},"interval_millis":{"type":"number","required":true},"path":{"type":"string","optional":true},"port":{"type":"number","optional":true,"computed":true},"protocol":{"type":"string","required":true},"timeout_millis":{"type":"number","required":true},"unhealthy_threshold":{"type":"number","required":true}}},"max_items":1},"port_mapping":{"nesting_mode":"list","block":{"attributes":{"port":{"type":"number","required":true},"protocol":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"max_items":1},"logging":{"nesting_mode":"list","block":{"block_types":{"access_log":{"nesting_mode":"list","block":{"block_types":{"file":{"nesting_mode":"list","block":{"attributes":{"path":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"max_items":1},"service_discovery":{"nesting_mode":"list","block":{"block_types":{"aws_cloud_map":{"nesting_mode":"list","block":{"attributes":{"attributes":{"type":["map","string"],"optional":true},"namespace_name":{"type":"string","required":true},"service_name":{"type":"string","required":true}}},"max_items":1},"dns":{"nesting_mode":"list","block":{"attributes":{"hostname":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_appmesh_virtual_router":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"mesh_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"block_types":{"listener":{"nesting_mode":"list","block":{"block_types":{"port_mapping":{"nesting_mode":"list","block":{"attributes":{"port":{"type":"number","required":true},"protocol":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_appmesh_virtual_service":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"mesh_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"block_types":{"provider":{"nesting_mode":"list","block":{"block_types":{"virtual_node":{"nesting_mode":"list","block":{"attributes":{"virtual_node_name":{"type":"string","required":true}}},"max_items":1},"virtual_router":{"nesting_mode":"list","block":{"attributes":{"virtual_router_name":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_appsync_api_key":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"expires":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","computed":true,"sensitive":true}}}},"aws_appsync_datasource":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"service_role_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"dynamodb_config":{"nesting_mode":"list","block":{"attributes":{"region":{"type":"string","optional":true,"computed":true},"table_name":{"type":"string","required":true},"use_caller_credentials":{"type":"bool","optional":true}}},"max_items":1},"elasticsearch_config":{"nesting_mode":"list","block":{"attributes":{"endpoint":{"type":"string","required":true},"region":{"type":"string","optional":true,"computed":true}}},"max_items":1},"http_config":{"nesting_mode":"list","block":{"attributes":{"endpoint":{"type":"string","required":true}}},"max_items":1},"lambda_config":{"nesting_mode":"list","block":{"attributes":{"function_arn":{"type":"string","required":true}}},"max_items":1}}}},"aws_appsync_function":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"data_source":{"type":"string","required":true},"description":{"type":"string","optional":true},"function_id":{"type":"string","computed":true},"function_version":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"request_mapping_template":{"type":"string","required":true},"response_mapping_template":{"type":"string","required":true}}}},"aws_appsync_graphql_api":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"authentication_type":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"schema":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"uris":{"type":["map","string"],"computed":true},"xray_enabled":{"type":"bool","optional":true}},"block_types":{"additional_authentication_provider":{"nesting_mode":"list","block":{"attributes":{"authentication_type":{"type":"string","required":true}},"block_types":{"openid_connect_config":{"nesting_mode":"list","block":{"attributes":{"auth_ttl":{"type":"number","optional":true},"client_id":{"type":"string","optional":true},"iat_ttl":{"type":"number","optional":true},"issuer":{"type":"string","required":true}}},"max_items":1},"user_pool_config":{"nesting_mode":"list","block":{"attributes":{"app_id_client_regex":{"type":"string","optional":true},"aws_region":{"type":"string","optional":true,"computed":true},"user_pool_id":{"type":"string","required":true}}},"max_items":1}}}},"log_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_logs_role_arn":{"type":"string","required":true},"exclude_verbose_content":{"type":"bool","optional":true},"field_log_level":{"type":"string","required":true}}},"max_items":1},"openid_connect_config":{"nesting_mode":"list","block":{"attributes":{"auth_ttl":{"type":"number","optional":true},"client_id":{"type":"string","optional":true},"iat_ttl":{"type":"number","optional":true},"issuer":{"type":"string","required":true}}},"max_items":1},"user_pool_config":{"nesting_mode":"list","block":{"attributes":{"app_id_client_regex":{"type":"string","optional":true},"aws_region":{"type":"string","optional":true,"computed":true},"default_action":{"type":"string","required":true},"user_pool_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_appsync_resolver":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"data_source":{"type":"string","optional":true},"field":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"kind":{"type":"string","optional":true},"request_template":{"type":"string","required":true},"response_template":{"type":"string","required":true},"type":{"type":"string","required":true}},"block_types":{"caching_config":{"nesting_mode":"list","block":{"attributes":{"caching_keys":{"type":["set","string"],"optional":true},"ttl":{"type":"number","optional":true}}},"max_items":1},"pipeline_config":{"nesting_mode":"list","block":{"attributes":{"functions":{"type":["list","string"],"optional":true}}},"max_items":1}}}},"aws_athena_database":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"encryption_configuration":{"nesting_mode":"list","block":{"attributes":{"encryption_option":{"type":"string","required":true},"kms_key":{"type":"string","optional":true}}},"max_items":1}}}},"aws_athena_named_query":{"version":0,"block":{"attributes":{"database":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"query":{"type":"string","required":true},"workgroup":{"type":"string","optional":true}}}},"aws_athena_workgroup":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"state":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"configuration":{"nesting_mode":"list","block":{"attributes":{"bytes_scanned_cutoff_per_query":{"type":"number","optional":true},"enforce_workgroup_configuration":{"type":"bool","optional":true},"publish_cloudwatch_metrics_enabled":{"type":"bool","optional":true}},"block_types":{"result_configuration":{"nesting_mode":"list","block":{"attributes":{"output_location":{"type":"string","optional":true}},"block_types":{"encryption_configuration":{"nesting_mode":"list","block":{"attributes":{"encryption_option":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}},"max_items":1}}}},"aws_autoscaling_attachment":{"version":0,"block":{"attributes":{"alb_target_group_arn":{"type":"string","optional":true},"autoscaling_group_name":{"type":"string","required":true},"elb":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_autoscaling_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"default_cooldown":{"type":"number","optional":true,"computed":true},"desired_capacity":{"type":"number","optional":true,"computed":true},"enabled_metrics":{"type":["set","string"],"optional":true},"force_delete":{"type":"bool","optional":true},"health_check_grace_period":{"type":"number","optional":true},"health_check_type":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"launch_configuration":{"type":"string","optional":true},"load_balancers":{"type":["set","string"],"optional":true},"max_instance_lifetime":{"type":"number","optional":true},"max_size":{"type":"number","required":true},"metrics_granularity":{"type":"string","optional":true},"min_elb_capacity":{"type":"number","optional":true},"min_size":{"type":"number","required":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"placement_group":{"type":"string","optional":true},"protect_from_scale_in":{"type":"bool","optional":true},"service_linked_role_arn":{"type":"string","optional":true,"computed":true},"suspended_processes":{"type":["set","string"],"optional":true},"tags":{"type":["set",["map","string"]],"optional":true},"target_group_arns":{"type":["set","string"],"optional":true},"termination_policies":{"type":["list","string"],"optional":true},"vpc_zone_identifier":{"type":["set","string"],"optional":true,"computed":true},"wait_for_capacity_timeout":{"type":"string","optional":true},"wait_for_elb_capacity":{"type":"number","optional":true}},"block_types":{"initial_lifecycle_hook":{"nesting_mode":"set","block":{"attributes":{"default_result":{"type":"string","optional":true,"computed":true},"heartbeat_timeout":{"type":"number","optional":true},"lifecycle_transition":{"type":"string","required":true},"name":{"type":"string","required":true},"notification_metadata":{"type":"string","optional":true},"notification_target_arn":{"type":"string","optional":true},"role_arn":{"type":"string","optional":true}}}},"launch_template":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"version":{"type":"string","optional":true}}},"max_items":1},"mixed_instances_policy":{"nesting_mode":"list","block":{"block_types":{"instances_distribution":{"nesting_mode":"list","block":{"attributes":{"on_demand_allocation_strategy":{"type":"string","optional":true,"computed":true},"on_demand_base_capacity":{"type":"number","optional":true,"computed":true},"on_demand_percentage_above_base_capacity":{"type":"number","optional":true,"computed":true},"spot_allocation_strategy":{"type":"string","optional":true,"computed":true},"spot_instance_pools":{"type":"number","optional":true,"computed":true},"spot_max_price":{"type":"string","optional":true}}},"max_items":1},"launch_template":{"nesting_mode":"list","block":{"block_types":{"launch_template_specification":{"nesting_mode":"list","block":{"attributes":{"launch_template_id":{"type":"string","optional":true,"computed":true},"launch_template_name":{"type":"string","optional":true,"computed":true},"version":{"type":"string","optional":true}}},"min_items":1,"max_items":1},"override":{"nesting_mode":"list","block":{"attributes":{"instance_type":{"type":"string","optional":true},"weighted_capacity":{"type":"string","optional":true}}}}}},"min_items":1,"max_items":1}}},"max_items":1},"tag":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","required":true},"propagate_at_launch":{"type":"bool","required":true},"value":{"type":"string","required":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true}}}}}}},"aws_autoscaling_lifecycle_hook":{"version":0,"block":{"attributes":{"autoscaling_group_name":{"type":"string","required":true},"default_result":{"type":"string","optional":true,"computed":true},"heartbeat_timeout":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"lifecycle_transition":{"type":"string","required":true},"name":{"type":"string","required":true},"notification_metadata":{"type":"string","optional":true},"notification_target_arn":{"type":"string","optional":true},"role_arn":{"type":"string","optional":true}}}},"aws_autoscaling_notification":{"version":0,"block":{"attributes":{"group_names":{"type":["set","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"notifications":{"type":["set","string"],"required":true},"topic_arn":{"type":"string","required":true}}}},"aws_autoscaling_policy":{"version":0,"block":{"attributes":{"adjustment_type":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"autoscaling_group_name":{"type":"string","required":true},"cooldown":{"type":"number","optional":true},"estimated_instance_warmup":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"metric_aggregation_type":{"type":"string","optional":true,"computed":true},"min_adjustment_magnitude":{"type":"number","optional":true},"name":{"type":"string","required":true},"policy_type":{"type":"string","optional":true},"scaling_adjustment":{"type":"number","optional":true}},"block_types":{"step_adjustment":{"nesting_mode":"set","block":{"attributes":{"metric_interval_lower_bound":{"type":"string","optional":true},"metric_interval_upper_bound":{"type":"string","optional":true},"scaling_adjustment":{"type":"number","required":true}}}},"target_tracking_configuration":{"nesting_mode":"list","block":{"attributes":{"disable_scale_in":{"type":"bool","optional":true},"target_value":{"type":"number","required":true}},"block_types":{"customized_metric_specification":{"nesting_mode":"list","block":{"attributes":{"metric_name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"statistic":{"type":"string","required":true},"unit":{"type":"string","optional":true}},"block_types":{"metric_dimension":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}},"max_items":1},"predefined_metric_specification":{"nesting_mode":"list","block":{"attributes":{"predefined_metric_type":{"type":"string","required":true},"resource_label":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_autoscaling_schedule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"autoscaling_group_name":{"type":"string","required":true},"desired_capacity":{"type":"number","optional":true,"computed":true},"end_time":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"max_size":{"type":"number","optional":true,"computed":true},"min_size":{"type":"number","optional":true,"computed":true},"recurrence":{"type":"string","optional":true,"computed":true},"scheduled_action_name":{"type":"string","required":true},"start_time":{"type":"string","optional":true,"computed":true}}}},"aws_backup_plan":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","computed":true}},"block_types":{"rule":{"nesting_mode":"set","block":{"attributes":{"completion_window":{"type":"number","optional":true},"recovery_point_tags":{"type":["map","string"],"optional":true},"rule_name":{"type":"string","required":true},"schedule":{"type":"string","optional":true},"start_window":{"type":"number","optional":true},"target_vault_name":{"type":"string","required":true}},"block_types":{"copy_action":{"nesting_mode":"set","block":{"attributes":{"destination_vault_arn":{"type":"string","required":true}},"block_types":{"lifecycle":{"nesting_mode":"list","block":{"attributes":{"cold_storage_after":{"type":"number","optional":true},"delete_after":{"type":"number","optional":true}}},"max_items":1}}}},"lifecycle":{"nesting_mode":"list","block":{"attributes":{"cold_storage_after":{"type":"number","optional":true},"delete_after":{"type":"number","optional":true}}},"max_items":1}}},"min_items":1}}}},"aws_backup_selection":{"version":0,"block":{"attributes":{"iam_role_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"plan_id":{"type":"string","required":true},"resources":{"type":["set","string"],"optional":true}},"block_types":{"selection_tag":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","required":true},"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_backup_vault":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"recovery_points":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_batch_compute_environment":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compute_environment_name":{"type":"string","optional":true,"computed":true},"compute_environment_name_prefix":{"type":"string","optional":true},"ecs_cluster_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"service_role":{"type":"string","required":true},"state":{"type":"string","optional":true},"status":{"type":"string","computed":true},"status_reason":{"type":"string","computed":true},"type":{"type":"string","required":true}},"block_types":{"compute_resources":{"nesting_mode":"list","block":{"attributes":{"allocation_strategy":{"type":"string","optional":true},"bid_percentage":{"type":"number","optional":true},"desired_vcpus":{"type":"number","optional":true,"computed":true},"ec2_key_pair":{"type":"string","optional":true},"image_id":{"type":"string","optional":true},"instance_role":{"type":"string","required":true},"instance_type":{"type":["set","string"],"required":true},"max_vcpus":{"type":"number","required":true},"min_vcpus":{"type":"number","required":true},"security_group_ids":{"type":["set","string"],"required":true},"spot_iam_fleet_role":{"type":"string","optional":true},"subnets":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","required":true}},"block_types":{"launch_template":{"nesting_mode":"list","block":{"attributes":{"launch_template_id":{"type":"string","optional":true},"launch_template_name":{"type":"string","optional":true},"version":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_batch_job_definition":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"container_properties":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"parameters":{"type":["map","string"],"optional":true},"revision":{"type":"number","computed":true},"type":{"type":"string","required":true}},"block_types":{"retry_strategy":{"nesting_mode":"list","block":{"attributes":{"attempts":{"type":"number","optional":true}}},"max_items":1},"timeout":{"nesting_mode":"list","block":{"attributes":{"attempt_duration_seconds":{"type":"number","optional":true}}},"max_items":1}}}},"aws_batch_job_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compute_environments":{"type":["list","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"priority":{"type":"number","required":true},"state":{"type":"string","required":true}}}},"aws_budgets_budget":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"budget_type":{"type":"string","required":true},"cost_filters":{"type":["map","string"],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"limit_amount":{"type":"string","required":true},"limit_unit":{"type":"string","required":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"time_period_end":{"type":"string","optional":true},"time_period_start":{"type":"string","required":true},"time_unit":{"type":"string","required":true}},"block_types":{"cost_types":{"nesting_mode":"list","block":{"attributes":{"include_credit":{"type":"bool","optional":true},"include_discount":{"type":"bool","optional":true},"include_other_subscription":{"type":"bool","optional":true},"include_recurring":{"type":"bool","optional":true},"include_refund":{"type":"bool","optional":true},"include_subscription":{"type":"bool","optional":true},"include_support":{"type":"bool","optional":true},"include_tax":{"type":"bool","optional":true},"include_upfront":{"type":"bool","optional":true},"use_amortized":{"type":"bool","optional":true},"use_blended":{"type":"bool","optional":true}}},"max_items":1},"notification":{"nesting_mode":"set","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"notification_type":{"type":"string","required":true},"subscriber_email_addresses":{"type":["set","string"],"optional":true},"subscriber_sns_topic_arns":{"type":["set","string"],"optional":true},"threshold":{"type":"number","required":true},"threshold_type":{"type":"string","required":true}}}}}}},"aws_cloud9_environment_ec2":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"automatic_stop_time_minutes":{"type":"number","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","required":true},"name":{"type":"string","required":true},"owner_arn":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","computed":true}}}},"aws_cloudformation_stack":{"version":0,"block":{"attributes":{"capabilities":{"type":["set","string"],"optional":true},"disable_rollback":{"type":"bool","optional":true},"iam_role_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"notification_arns":{"type":["set","string"],"optional":true},"on_failure":{"type":"string","optional":true},"outputs":{"type":["map","string"],"computed":true},"parameters":{"type":["map","string"],"optional":true,"computed":true},"policy_body":{"type":"string","optional":true,"computed":true},"policy_url":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"template_body":{"type":"string","optional":true,"computed":true},"template_url":{"type":"string","optional":true},"timeout_in_minutes":{"type":"number","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cloudformation_stack_set":{"version":0,"block":{"attributes":{"administration_role_arn":{"type":"string","required":true},"arn":{"type":"string","computed":true},"capabilities":{"type":["set","string"],"optional":true},"description":{"type":"string","optional":true},"execution_role_name":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"parameters":{"type":["map","string"],"optional":true},"stack_set_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"template_body":{"type":"string","optional":true,"computed":true},"template_url":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"update":{"type":"string","optional":true}}}}}}},"aws_cloudformation_stack_set_instance":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"parameter_overrides":{"type":["map","string"],"optional":true},"region":{"type":"string","optional":true,"computed":true},"retain_stack":{"type":"bool","optional":true},"stack_id":{"type":"string","computed":true},"stack_set_name":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cloudfront_distribution":{"version":1,"block":{"attributes":{"aliases":{"type":["set","string"],"optional":true},"arn":{"type":"string","computed":true},"caller_reference":{"type":"string","computed":true},"comment":{"type":"string","optional":true},"default_root_object":{"type":"string","optional":true},"domain_name":{"type":"string","computed":true},"enabled":{"type":"bool","required":true},"etag":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"http_version":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"in_progress_validation_batches":{"type":"number","computed":true},"is_ipv6_enabled":{"type":"bool","optional":true},"last_modified_time":{"type":"string","computed":true},"price_class":{"type":"string","optional":true},"retain_on_delete":{"type":"bool","optional":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"trusted_signers":{"type":["list",["object",{"enabled":"bool","items":["list",["object",{"aws_account_number":"string","key_pair_ids":["set","string"]}]]}]],"computed":true},"wait_for_deployment":{"type":"bool","optional":true},"web_acl_id":{"type":"string","optional":true}},"block_types":{"custom_error_response":{"nesting_mode":"set","block":{"attributes":{"error_caching_min_ttl":{"type":"number","optional":true},"error_code":{"type":"number","required":true},"response_code":{"type":"number","optional":true},"response_page_path":{"type":"string","optional":true}}}},"default_cache_behavior":{"nesting_mode":"list","block":{"attributes":{"allowed_methods":{"type":["set","string"],"required":true},"cached_methods":{"type":["set","string"],"required":true},"compress":{"type":"bool","optional":true},"default_ttl":{"type":"number","optional":true},"field_level_encryption_id":{"type":"string","optional":true},"max_ttl":{"type":"number","optional":true},"min_ttl":{"type":"number","optional":true},"smooth_streaming":{"type":"bool","optional":true},"target_origin_id":{"type":"string","required":true},"trusted_signers":{"type":["list","string"],"optional":true},"viewer_protocol_policy":{"type":"string","required":true}},"block_types":{"forwarded_values":{"nesting_mode":"list","block":{"attributes":{"headers":{"type":["set","string"],"optional":true},"query_string":{"type":"bool","required":true},"query_string_cache_keys":{"type":["list","string"],"optional":true}},"block_types":{"cookies":{"nesting_mode":"list","block":{"attributes":{"forward":{"type":"string","required":true},"whitelisted_names":{"type":["set","string"],"optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"lambda_function_association":{"nesting_mode":"set","block":{"attributes":{"event_type":{"type":"string","required":true},"include_body":{"type":"bool","optional":true},"lambda_arn":{"type":"string","required":true}}},"max_items":4}}},"min_items":1,"max_items":1},"logging_config":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"include_cookies":{"type":"bool","optional":true},"prefix":{"type":"string","optional":true}}},"max_items":1},"ordered_cache_behavior":{"nesting_mode":"list","block":{"attributes":{"allowed_methods":{"type":["set","string"],"required":true},"cached_methods":{"type":["set","string"],"required":true},"compress":{"type":"bool","optional":true},"default_ttl":{"type":"number","optional":true},"field_level_encryption_id":{"type":"string","optional":true},"max_ttl":{"type":"number","optional":true},"min_ttl":{"type":"number","optional":true},"path_pattern":{"type":"string","required":true},"smooth_streaming":{"type":"bool","optional":true},"target_origin_id":{"type":"string","required":true},"trusted_signers":{"type":["list","string"],"optional":true},"viewer_protocol_policy":{"type":"string","required":true}},"block_types":{"forwarded_values":{"nesting_mode":"list","block":{"attributes":{"headers":{"type":["set","string"],"optional":true},"query_string":{"type":"bool","required":true},"query_string_cache_keys":{"type":["list","string"],"optional":true}},"block_types":{"cookies":{"nesting_mode":"list","block":{"attributes":{"forward":{"type":"string","required":true},"whitelisted_names":{"type":["set","string"],"optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"lambda_function_association":{"nesting_mode":"set","block":{"attributes":{"event_type":{"type":"string","required":true},"include_body":{"type":"bool","optional":true},"lambda_arn":{"type":"string","required":true}}},"max_items":4}}}},"origin":{"nesting_mode":"set","block":{"attributes":{"domain_name":{"type":"string","required":true},"origin_id":{"type":"string","required":true},"origin_path":{"type":"string","optional":true}},"block_types":{"custom_header":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}},"custom_origin_config":{"nesting_mode":"list","block":{"attributes":{"http_port":{"type":"number","required":true},"https_port":{"type":"number","required":true},"origin_keepalive_timeout":{"type":"number","optional":true},"origin_protocol_policy":{"type":"string","required":true},"origin_read_timeout":{"type":"number","optional":true},"origin_ssl_protocols":{"type":["set","string"],"required":true}}},"max_items":1},"s3_origin_config":{"nesting_mode":"list","block":{"attributes":{"origin_access_identity":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"origin_group":{"nesting_mode":"set","block":{"attributes":{"origin_id":{"type":"string","required":true}},"block_types":{"failover_criteria":{"nesting_mode":"list","block":{"attributes":{"status_codes":{"type":["set","number"],"required":true}}},"min_items":1,"max_items":1},"member":{"nesting_mode":"list","block":{"attributes":{"origin_id":{"type":"string","required":true}}},"min_items":2,"max_items":2}}}},"restrictions":{"nesting_mode":"list","block":{"block_types":{"geo_restriction":{"nesting_mode":"list","block":{"attributes":{"locations":{"type":["set","string"],"optional":true},"restriction_type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"viewer_certificate":{"nesting_mode":"list","block":{"attributes":{"acm_certificate_arn":{"type":"string","optional":true},"cloudfront_default_certificate":{"type":"bool","optional":true},"iam_certificate_id":{"type":"string","optional":true},"minimum_protocol_version":{"type":"string","optional":true},"ssl_support_method":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}}},"aws_cloudfront_origin_access_identity":{"version":0,"block":{"attributes":{"caller_reference":{"type":"string","computed":true},"cloudfront_access_identity_path":{"type":"string","computed":true},"comment":{"type":"string","optional":true},"etag":{"type":"string","computed":true},"iam_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"s3_canonical_user_id":{"type":"string","computed":true}}}},"aws_cloudfront_public_key":{"version":0,"block":{"attributes":{"caller_reference":{"type":"string","computed":true},"comment":{"type":"string","optional":true},"encoded_key":{"type":"string","required":true},"etag":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true}}}},"aws_cloudhsm_v2_cluster":{"version":0,"block":{"attributes":{"cluster_certificates":{"type":["list",["object",{"aws_hardware_certificate":"string","cluster_certificate":"string","cluster_csr":"string","hsm_certificate":"string","manufacturer_hardware_certificate":"string"}]],"computed":true},"cluster_id":{"type":"string","computed":true},"cluster_state":{"type":"string","computed":true},"hsm_type":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"security_group_id":{"type":"string","computed":true},"source_backup_identifier":{"type":"string","optional":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cloudhsm_v2_hsm":{"version":0,"block":{"attributes":{"availability_zone":{"type":"string","optional":true,"computed":true},"cluster_id":{"type":"string","required":true},"hsm_eni_id":{"type":"string","computed":true},"hsm_id":{"type":"string","computed":true},"hsm_state":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cloudtrail":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cloud_watch_logs_group_arn":{"type":"string","optional":true},"cloud_watch_logs_role_arn":{"type":"string","optional":true},"enable_log_file_validation":{"type":"bool","optional":true},"enable_logging":{"type":"bool","optional":true},"home_region":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"include_global_service_events":{"type":"bool","optional":true},"is_multi_region_trail":{"type":"bool","optional":true},"is_organization_trail":{"type":"bool","optional":true},"kms_key_id":{"type":"string","optional":true},"name":{"type":"string","required":true},"s3_bucket_name":{"type":"string","required":true},"s3_key_prefix":{"type":"string","optional":true},"sns_topic_name":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"event_selector":{"nesting_mode":"list","block":{"attributes":{"include_management_events":{"type":"bool","optional":true},"read_write_type":{"type":"string","optional":true}},"block_types":{"data_resource":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}},"max_items":5}}}},"aws_cloudwatch_dashboard":{"version":0,"block":{"attributes":{"dashboard_arn":{"type":"string","computed":true},"dashboard_body":{"type":"string","required":true},"dashboard_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_cloudwatch_event_permission":{"version":0,"block":{"attributes":{"action":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"principal":{"type":"string","required":true},"statement_id":{"type":"string","required":true}},"block_types":{"condition":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"type":{"type":"string","required":true},"value":{"type":"string","required":true}}},"max_items":1}}}},"aws_cloudwatch_event_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"event_pattern":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"is_enabled":{"type":"bool","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"role_arn":{"type":"string","optional":true},"schedule_expression":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_cloudwatch_event_target":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"input":{"type":"string","optional":true},"input_path":{"type":"string","optional":true},"role_arn":{"type":"string","optional":true},"rule":{"type":"string","required":true},"target_id":{"type":"string","optional":true,"computed":true}},"block_types":{"batch_target":{"nesting_mode":"list","block":{"attributes":{"array_size":{"type":"number","optional":true},"job_attempts":{"type":"number","optional":true},"job_definition":{"type":"string","required":true},"job_name":{"type":"string","required":true}}},"max_items":1},"ecs_target":{"nesting_mode":"list","block":{"attributes":{"group":{"type":"string","optional":true},"launch_type":{"type":"string","optional":true},"platform_version":{"type":"string","optional":true},"task_count":{"type":"number","optional":true},"task_definition_arn":{"type":"string","required":true}},"block_types":{"network_configuration":{"nesting_mode":"list","block":{"attributes":{"assign_public_ip":{"type":"bool","optional":true},"security_groups":{"type":["set","string"],"optional":true},"subnets":{"type":["set","string"],"required":true}}},"max_items":1}}},"max_items":1},"input_transformer":{"nesting_mode":"list","block":{"attributes":{"input_paths":{"type":["map","string"],"optional":true},"input_template":{"type":"string","required":true}}},"max_items":1},"kinesis_target":{"nesting_mode":"list","block":{"attributes":{"partition_key_path":{"type":"string","optional":true}}},"max_items":1},"run_command_targets":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"max_items":5},"sqs_target":{"nesting_mode":"list","block":{"attributes":{"message_group_id":{"type":"string","optional":true}}},"max_items":1}}}},"aws_cloudwatch_log_destination":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"target_arn":{"type":"string","required":true}}}},"aws_cloudwatch_log_destination_policy":{"version":0,"block":{"attributes":{"access_policy":{"type":"string","required":true},"destination_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_cloudwatch_log_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"retention_in_days":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_cloudwatch_log_metric_filter":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","required":true},"name":{"type":"string","required":true},"pattern":{"type":"string","required":true}},"block_types":{"metric_transformation":{"nesting_mode":"list","block":{"attributes":{"default_value":{"type":"string","optional":true},"name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"value":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_cloudwatch_log_resource_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy_document":{"type":"string","required":true},"policy_name":{"type":"string","required":true}}}},"aws_cloudwatch_log_stream":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","required":true},"name":{"type":"string","required":true}}}},"aws_cloudwatch_log_subscription_filter":{"version":0,"block":{"attributes":{"destination_arn":{"type":"string","required":true},"distribution":{"type":"string","optional":true},"filter_pattern":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","required":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","optional":true,"computed":true}}}},"aws_cloudwatch_metric_alarm":{"version":1,"block":{"attributes":{"actions_enabled":{"type":"bool","optional":true},"alarm_actions":{"type":["set","string"],"optional":true},"alarm_description":{"type":"string","optional":true},"alarm_name":{"type":"string","required":true},"arn":{"type":"string","computed":true},"comparison_operator":{"type":"string","required":true},"datapoints_to_alarm":{"type":"number","optional":true},"dimensions":{"type":["map","string"],"optional":true},"evaluate_low_sample_count_percentiles":{"type":"string","optional":true,"computed":true},"evaluation_periods":{"type":"number","required":true},"extended_statistic":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"insufficient_data_actions":{"type":["set","string"],"optional":true},"metric_name":{"type":"string","optional":true},"namespace":{"type":"string","optional":true},"ok_actions":{"type":["set","string"],"optional":true},"period":{"type":"number","optional":true},"statistic":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"threshold":{"type":"number","optional":true},"threshold_metric_id":{"type":"string","optional":true},"treat_missing_data":{"type":"string","optional":true},"unit":{"type":"string","optional":true}},"block_types":{"metric_query":{"nesting_mode":"set","block":{"attributes":{"expression":{"type":"string","optional":true},"id":{"type":"string","required":true},"label":{"type":"string","optional":true},"return_data":{"type":"bool","optional":true}},"block_types":{"metric":{"nesting_mode":"list","block":{"attributes":{"dimensions":{"type":["map","string"],"optional":true},"metric_name":{"type":"string","required":true},"namespace":{"type":"string","optional":true},"period":{"type":"number","required":true},"stat":{"type":"string","required":true},"unit":{"type":"string","optional":true}}},"max_items":1}}}}}}},"aws_codebuild_project":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"badge_enabled":{"type":"bool","optional":true},"badge_url":{"type":"string","computed":true},"build_timeout":{"type":"number","optional":true},"description":{"type":"string","optional":true,"computed":true},"encryption_key":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"queued_timeout":{"type":"number","optional":true},"service_role":{"type":"string","required":true},"source_version":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"artifacts":{"nesting_mode":"list","block":{"attributes":{"artifact_identifier":{"type":"string","optional":true},"encryption_disabled":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"name":{"type":"string","optional":true},"namespace_type":{"type":"string","optional":true},"override_artifact_name":{"type":"bool","optional":true},"packaging":{"type":"string","optional":true},"path":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1},"cache":{"nesting_mode":"list","block":{"attributes":{"location":{"type":"string","optional":true},"modes":{"type":["list","string"],"optional":true},"type":{"type":"string","optional":true}}},"max_items":1},"environment":{"nesting_mode":"list","block":{"attributes":{"certificate":{"type":"string","optional":true},"compute_type":{"type":"string","required":true},"image":{"type":"string","required":true},"image_pull_credentials_type":{"type":"string","optional":true},"privileged_mode":{"type":"bool","optional":true},"type":{"type":"string","required":true}},"block_types":{"environment_variable":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true},"type":{"type":"string","optional":true},"value":{"type":"string","required":true}}}},"registry_credential":{"nesting_mode":"list","block":{"attributes":{"credential":{"type":"string","required":true},"credential_provider":{"type":"string","required":true}}},"max_items":1}}},"min_items":1,"max_items":1},"logs_config":{"nesting_mode":"list","block":{"block_types":{"cloudwatch_logs":{"nesting_mode":"list","block":{"attributes":{"group_name":{"type":"string","optional":true},"status":{"type":"string","optional":true},"stream_name":{"type":"string","optional":true}}},"max_items":1},"s3_logs":{"nesting_mode":"list","block":{"attributes":{"encryption_disabled":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"status":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"secondary_artifacts":{"nesting_mode":"set","block":{"attributes":{"artifact_identifier":{"type":"string","required":true},"encryption_disabled":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"name":{"type":"string","optional":true},"namespace_type":{"type":"string","optional":true},"override_artifact_name":{"type":"bool","optional":true},"packaging":{"type":"string","optional":true},"path":{"type":"string","optional":true},"type":{"type":"string","required":true}}}},"secondary_sources":{"nesting_mode":"set","block":{"attributes":{"buildspec":{"type":"string","optional":true},"git_clone_depth":{"type":"number","optional":true},"insecure_ssl":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"report_build_status":{"type":"bool","optional":true},"source_identifier":{"type":"string","required":true},"type":{"type":"string","required":true}},"block_types":{"auth":{"nesting_mode":"list","block":{"attributes":{"resource":{"type":"string","optional":true,"sensitive":true},"type":{"type":"string","required":true}}},"max_items":1},"git_submodules_config":{"nesting_mode":"list","block":{"attributes":{"fetch_submodules":{"type":"bool","required":true}}},"max_items":1}}}},"source":{"nesting_mode":"list","block":{"attributes":{"buildspec":{"type":"string","optional":true},"git_clone_depth":{"type":"number","optional":true},"insecure_ssl":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"report_build_status":{"type":"bool","optional":true},"type":{"type":"string","required":true}},"block_types":{"auth":{"nesting_mode":"list","block":{"attributes":{"resource":{"type":"string","optional":true,"sensitive":true},"type":{"type":"string","required":true}}},"max_items":1},"git_submodules_config":{"nesting_mode":"list","block":{"attributes":{"fetch_submodules":{"type":"bool","required":true}}},"max_items":1}}},"min_items":1,"max_items":1},"vpc_config":{"nesting_mode":"list","block":{"attributes":{"security_group_ids":{"type":["set","string"],"required":true},"subnets":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_codebuild_source_credential":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auth_type":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"server_type":{"type":"string","required":true},"token":{"type":"string","required":true,"sensitive":true},"user_name":{"type":"string","optional":true}}}},"aws_codebuild_webhook":{"version":0,"block":{"attributes":{"branch_filter":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"payload_url":{"type":"string","computed":true},"project_name":{"type":"string","required":true},"secret":{"type":"string","computed":true,"sensitive":true},"url":{"type":"string","computed":true}},"block_types":{"filter_group":{"nesting_mode":"set","block":{"block_types":{"filter":{"nesting_mode":"list","block":{"attributes":{"exclude_matched_pattern":{"type":"bool","optional":true},"pattern":{"type":"string","required":true},"type":{"type":"string","required":true}}}}}}}}}},"aws_codecommit_repository":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"clone_url_http":{"type":"string","computed":true},"clone_url_ssh":{"type":"string","computed":true},"default_branch":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"repository_id":{"type":"string","computed":true},"repository_name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_codecommit_trigger":{"version":0,"block":{"attributes":{"configuration_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"repository_name":{"type":"string","required":true}},"block_types":{"trigger":{"nesting_mode":"set","block":{"attributes":{"branches":{"type":["list","string"],"optional":true},"custom_data":{"type":"string","optional":true},"destination_arn":{"type":"string","required":true},"events":{"type":["list","string"],"required":true},"name":{"type":"string","required":true}}},"min_items":1,"max_items":10}}}},"aws_codedeploy_app":{"version":0,"block":{"attributes":{"compute_platform":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"unique_id":{"type":"string","optional":true,"computed":true}}}},"aws_codedeploy_deployment_config":{"version":0,"block":{"attributes":{"compute_platform":{"type":"string","optional":true},"deployment_config_id":{"type":"string","computed":true},"deployment_config_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}},"block_types":{"minimum_healthy_hosts":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","optional":true},"value":{"type":"number","optional":true}}},"max_items":1},"traffic_routing_config":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","optional":true}},"block_types":{"time_based_canary":{"nesting_mode":"list","block":{"attributes":{"interval":{"type":"number","optional":true},"percentage":{"type":"number","optional":true}}},"max_items":1},"time_based_linear":{"nesting_mode":"list","block":{"attributes":{"interval":{"type":"number","optional":true},"percentage":{"type":"number","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_codedeploy_deployment_group":{"version":0,"block":{"attributes":{"app_name":{"type":"string","required":true},"autoscaling_groups":{"type":["set","string"],"optional":true},"deployment_config_name":{"type":"string","optional":true},"deployment_group_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"service_role_arn":{"type":"string","required":true}},"block_types":{"alarm_configuration":{"nesting_mode":"list","block":{"attributes":{"alarms":{"type":["set","string"],"optional":true},"enabled":{"type":"bool","optional":true},"ignore_poll_alarm_failure":{"type":"bool","optional":true}}},"max_items":1},"auto_rollback_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"events":{"type":["set","string"],"optional":true}}},"max_items":1},"blue_green_deployment_config":{"nesting_mode":"list","block":{"block_types":{"deployment_ready_option":{"nesting_mode":"list","block":{"attributes":{"action_on_timeout":{"type":"string","optional":true},"wait_time_in_minutes":{"type":"number","optional":true}}},"max_items":1},"green_fleet_provisioning_option":{"nesting_mode":"list","block":{"attributes":{"action":{"type":"string","optional":true}}},"max_items":1},"terminate_blue_instances_on_deployment_success":{"nesting_mode":"list","block":{"attributes":{"action":{"type":"string","optional":true},"termination_wait_time_in_minutes":{"type":"number","optional":true}}},"max_items":1}}},"max_items":1},"deployment_style":{"nesting_mode":"list","block":{"attributes":{"deployment_option":{"type":"string","optional":true},"deployment_type":{"type":"string","optional":true}}},"max_items":1},"ec2_tag_filter":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"type":{"type":"string","optional":true},"value":{"type":"string","optional":true}}}},"ec2_tag_set":{"nesting_mode":"set","block":{"block_types":{"ec2_tag_filter":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"type":{"type":"string","optional":true},"value":{"type":"string","optional":true}}}}}}},"ecs_service":{"nesting_mode":"list","block":{"attributes":{"cluster_name":{"type":"string","required":true},"service_name":{"type":"string","required":true}}},"max_items":1},"load_balancer_info":{"nesting_mode":"list","block":{"block_types":{"elb_info":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","optional":true}}}},"target_group_info":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","optional":true}}}},"target_group_pair_info":{"nesting_mode":"list","block":{"block_types":{"prod_traffic_route":{"nesting_mode":"list","block":{"attributes":{"listener_arns":{"type":["set","string"],"required":true}}},"min_items":1,"max_items":1},"target_group":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"min_items":1,"max_items":2},"test_traffic_route":{"nesting_mode":"list","block":{"attributes":{"listener_arns":{"type":["set","string"],"required":true}}},"max_items":1}}},"max_items":1}}},"max_items":1},"on_premises_instance_tag_filter":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"type":{"type":"string","optional":true},"value":{"type":"string","optional":true}}}},"trigger_configuration":{"nesting_mode":"set","block":{"attributes":{"trigger_events":{"type":["set","string"],"required":true},"trigger_name":{"type":"string","required":true},"trigger_target_arn":{"type":"string","required":true}}}}}}},"aws_codepipeline":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"artifact_store":{"nesting_mode":"set","block":{"attributes":{"location":{"type":"string","required":true},"region":{"type":"string","optional":true,"computed":true},"type":{"type":"string","required":true}},"block_types":{"encryption_key":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","required":true},"type":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"stage":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"category":{"type":"string","required":true},"configuration":{"type":["map","string"],"optional":true},"input_artifacts":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"namespace":{"type":"string","optional":true},"output_artifacts":{"type":["list","string"],"optional":true},"owner":{"type":"string","required":true},"provider":{"type":"string","required":true},"region":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","optional":true},"run_order":{"type":"number","optional":true,"computed":true},"version":{"type":"string","required":true}}},"min_items":1}}},"min_items":2}}}},"aws_codepipeline_webhook":{"version":0,"block":{"attributes":{"authentication":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"target_action":{"type":"string","required":true},"target_pipeline":{"type":"string","required":true},"url":{"type":"string","computed":true}},"block_types":{"authentication_configuration":{"nesting_mode":"list","block":{"attributes":{"allowed_ip_range":{"type":"string","optional":true},"secret_token":{"type":"string","optional":true,"sensitive":true}}},"max_items":1},"filter":{"nesting_mode":"set","block":{"attributes":{"json_path":{"type":"string","required":true},"match_equals":{"type":"string","required":true}}},"min_items":1}}}},"aws_codestarnotifications_notification_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"detail_type":{"type":"string","required":true},"event_type_ids":{"type":["set","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"resource":{"type":"string","required":true},"status":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"target":{"nesting_mode":"set","block":{"attributes":{"address":{"type":"string","required":true},"status":{"type":"string","computed":true},"type":{"type":"string","optional":true}}},"max_items":10}}}},"aws_cognito_identity_pool":{"version":0,"block":{"attributes":{"allow_unauthenticated_identities":{"type":"bool","optional":true},"arn":{"type":"string","computed":true},"developer_provider_name":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"identity_pool_name":{"type":"string","required":true},"openid_connect_provider_arns":{"type":["list","string"],"optional":true},"saml_provider_arns":{"type":["list","string"],"optional":true},"supported_login_providers":{"type":["map","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"cognito_identity_providers":{"nesting_mode":"set","block":{"attributes":{"client_id":{"type":"string","optional":true},"provider_name":{"type":"string","optional":true},"server_side_token_check":{"type":"bool","optional":true}}}}}}},"aws_cognito_identity_pool_roles_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"identity_pool_id":{"type":"string","required":true},"roles":{"type":["map","string"],"required":true}},"block_types":{"role_mapping":{"nesting_mode":"set","block":{"attributes":{"ambiguous_role_resolution":{"type":"string","optional":true},"identity_provider":{"type":"string","required":true},"type":{"type":"string","required":true}},"block_types":{"mapping_rule":{"nesting_mode":"list","block":{"attributes":{"claim":{"type":"string","required":true},"match_type":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"value":{"type":"string","required":true}}},"max_items":25}}}}}}},"aws_cognito_identity_provider":{"version":0,"block":{"attributes":{"attribute_mapping":{"type":["map","string"],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"idp_identifiers":{"type":["list","string"],"optional":true},"provider_details":{"type":["map","string"],"required":true},"provider_name":{"type":"string","required":true},"provider_type":{"type":"string","required":true},"user_pool_id":{"type":"string","required":true}}}},"aws_cognito_resource_server":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","required":true},"name":{"type":"string","required":true},"scope_identifiers":{"type":["list","string"],"computed":true},"user_pool_id":{"type":"string","required":true}},"block_types":{"scope":{"nesting_mode":"set","block":{"attributes":{"scope_description":{"type":"string","required":true},"scope_name":{"type":"string","required":true}}},"max_items":100}}}},"aws_cognito_user_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"precedence":{"type":"number","optional":true},"role_arn":{"type":"string","optional":true},"user_pool_id":{"type":"string","required":true}}}},"aws_cognito_user_pool":{"version":0,"block":{"attributes":{"alias_attributes":{"type":["set","string"],"optional":true},"arn":{"type":"string","computed":true},"auto_verified_attributes":{"type":["set","string"],"optional":true},"creation_date":{"type":"string","computed":true},"email_verification_message":{"type":"string","optional":true,"computed":true},"email_verification_subject":{"type":"string","optional":true,"computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_modified_date":{"type":"string","computed":true},"mfa_configuration":{"type":"string","optional":true},"name":{"type":"string","required":true},"sms_authentication_message":{"type":"string","optional":true},"sms_verification_message":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"username_attributes":{"type":["list","string"],"optional":true}},"block_types":{"admin_create_user_config":{"nesting_mode":"list","block":{"attributes":{"allow_admin_create_user_only":{"type":"bool","optional":true}},"block_types":{"invite_message_template":{"nesting_mode":"list","block":{"attributes":{"email_message":{"type":"string","optional":true},"email_subject":{"type":"string","optional":true},"sms_message":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"device_configuration":{"nesting_mode":"list","block":{"attributes":{"challenge_required_on_new_device":{"type":"bool","optional":true},"device_only_remembered_on_user_prompt":{"type":"bool","optional":true}}},"max_items":1},"email_configuration":{"nesting_mode":"list","block":{"attributes":{"email_sending_account":{"type":"string","optional":true},"from_email_address":{"type":"string","optional":true},"reply_to_email_address":{"type":"string","optional":true},"source_arn":{"type":"string","optional":true}}},"max_items":1},"lambda_config":{"nesting_mode":"list","block":{"attributes":{"create_auth_challenge":{"type":"string","optional":true},"custom_message":{"type":"string","optional":true},"define_auth_challenge":{"type":"string","optional":true},"post_authentication":{"type":"string","optional":true},"post_confirmation":{"type":"string","optional":true},"pre_authentication":{"type":"string","optional":true},"pre_sign_up":{"type":"string","optional":true},"pre_token_generation":{"type":"string","optional":true},"user_migration":{"type":"string","optional":true},"verify_auth_challenge_response":{"type":"string","optional":true}}},"max_items":1},"password_policy":{"nesting_mode":"list","block":{"attributes":{"minimum_length":{"type":"number","optional":true},"require_lowercase":{"type":"bool","optional":true},"require_numbers":{"type":"bool","optional":true},"require_symbols":{"type":"bool","optional":true},"require_uppercase":{"type":"bool","optional":true},"temporary_password_validity_days":{"type":"number","optional":true}}},"max_items":1},"schema":{"nesting_mode":"set","block":{"attributes":{"attribute_data_type":{"type":"string","required":true},"developer_only_attribute":{"type":"bool","optional":true},"mutable":{"type":"bool","optional":true},"name":{"type":"string","required":true},"required":{"type":"bool","optional":true}},"block_types":{"number_attribute_constraints":{"nesting_mode":"list","block":{"attributes":{"max_value":{"type":"string","optional":true},"min_value":{"type":"string","optional":true}}},"max_items":1},"string_attribute_constraints":{"nesting_mode":"list","block":{"attributes":{"max_length":{"type":"string","optional":true},"min_length":{"type":"string","optional":true}}},"max_items":1}}},"max_items":50},"sms_configuration":{"nesting_mode":"list","block":{"attributes":{"external_id":{"type":"string","required":true},"sns_caller_arn":{"type":"string","required":true}}},"max_items":1},"software_token_mfa_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true}}},"max_items":1},"user_pool_add_ons":{"nesting_mode":"list","block":{"attributes":{"advanced_security_mode":{"type":"string","required":true}}},"max_items":1},"username_configuration":{"nesting_mode":"list","block":{"attributes":{"case_sensitive":{"type":"bool","required":true}}},"max_items":1},"verification_message_template":{"nesting_mode":"list","block":{"attributes":{"default_email_option":{"type":"string","optional":true},"email_message":{"type":"string","optional":true,"computed":true},"email_message_by_link":{"type":"string","optional":true,"computed":true},"email_subject":{"type":"string","optional":true,"computed":true},"email_subject_by_link":{"type":"string","optional":true,"computed":true},"sms_message":{"type":"string","optional":true,"computed":true}}},"max_items":1}}}},"aws_cognito_user_pool_client":{"version":0,"block":{"attributes":{"allowed_oauth_flows":{"type":["set","string"],"optional":true},"allowed_oauth_flows_user_pool_client":{"type":"bool","optional":true},"allowed_oauth_scopes":{"type":["set","string"],"optional":true},"callback_urls":{"type":["set","string"],"optional":true},"client_secret":{"type":"string","computed":true,"sensitive":true},"default_redirect_uri":{"type":"string","optional":true},"explicit_auth_flows":{"type":["set","string"],"optional":true},"generate_secret":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"logout_urls":{"type":["set","string"],"optional":true},"name":{"type":"string","required":true},"prevent_user_existence_errors":{"type":"string","optional":true,"computed":true},"read_attributes":{"type":["set","string"],"optional":true},"refresh_token_validity":{"type":"number","optional":true},"supported_identity_providers":{"type":["set","string"],"optional":true},"user_pool_id":{"type":"string","required":true},"write_attributes":{"type":["set","string"],"optional":true}},"block_types":{"analytics_configuration":{"nesting_mode":"list","block":{"attributes":{"application_id":{"type":"string","required":true},"external_id":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"user_data_shared":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_cognito_user_pool_domain":{"version":0,"block":{"attributes":{"aws_account_id":{"type":"string","computed":true},"certificate_arn":{"type":"string","optional":true},"cloudfront_distribution_arn":{"type":"string","computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"s3_bucket":{"type":"string","computed":true},"user_pool_id":{"type":"string","required":true},"version":{"type":"string","computed":true}}}},"aws_config_aggregate_authorization":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_config_config_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"input_parameters":{"type":"string","optional":true},"maximum_execution_frequency":{"type":"string","optional":true},"name":{"type":"string","required":true},"rule_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"scope":{"nesting_mode":"list","block":{"attributes":{"compliance_resource_id":{"type":"string","optional":true},"compliance_resource_types":{"type":["set","string"],"optional":true},"tag_key":{"type":"string","optional":true},"tag_value":{"type":"string","optional":true}}},"max_items":1},"source":{"nesting_mode":"list","block":{"attributes":{"owner":{"type":"string","required":true},"source_identifier":{"type":"string","required":true}},"block_types":{"source_detail":{"nesting_mode":"set","block":{"attributes":{"event_source":{"type":"string","optional":true},"maximum_execution_frequency":{"type":"string","optional":true},"message_type":{"type":"string","optional":true}}},"max_items":25}}},"min_items":1,"max_items":1}}}},"aws_config_configuration_aggregator":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"account_aggregation_source":{"nesting_mode":"list","block":{"attributes":{"account_ids":{"type":["list","string"],"required":true},"all_regions":{"type":"bool","optional":true},"regions":{"type":["list","string"],"optional":true}}},"max_items":1},"organization_aggregation_source":{"nesting_mode":"list","block":{"attributes":{"all_regions":{"type":"bool","optional":true},"regions":{"type":["list","string"],"optional":true},"role_arn":{"type":"string","required":true}}},"max_items":1}}}},"aws_config_configuration_recorder":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}},"block_types":{"recording_group":{"nesting_mode":"list","block":{"attributes":{"all_supported":{"type":"bool","optional":true},"include_global_resource_types":{"type":"bool","optional":true},"resource_types":{"type":["set","string"],"optional":true}}},"max_items":1}}}},"aws_config_configuration_recorder_status":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"is_enabled":{"type":"bool","required":true},"name":{"type":"string","required":true}}}},"aws_config_delivery_channel":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"s3_bucket_name":{"type":"string","required":true},"s3_key_prefix":{"type":"string","optional":true},"sns_topic_arn":{"type":"string","optional":true}},"block_types":{"snapshot_delivery_properties":{"nesting_mode":"list","block":{"attributes":{"delivery_frequency":{"type":"string","optional":true}}},"max_items":1}}}},"aws_config_organization_custom_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"excluded_accounts":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"input_parameters":{"type":"string","optional":true},"lambda_function_arn":{"type":"string","required":true},"maximum_execution_frequency":{"type":"string","optional":true},"name":{"type":"string","required":true},"resource_id_scope":{"type":"string","optional":true},"resource_types_scope":{"type":["set","string"],"optional":true},"tag_key_scope":{"type":"string","optional":true},"tag_value_scope":{"type":"string","optional":true},"trigger_types":{"type":["set","string"],"required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_config_organization_managed_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"excluded_accounts":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"input_parameters":{"type":"string","optional":true},"maximum_execution_frequency":{"type":"string","optional":true},"name":{"type":"string","required":true},"resource_id_scope":{"type":"string","optional":true},"resource_types_scope":{"type":["set","string"],"optional":true},"rule_identifier":{"type":"string","required":true},"tag_key_scope":{"type":"string","optional":true},"tag_value_scope":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cur_report_definition":{"version":0,"block":{"attributes":{"additional_artifacts":{"type":["set","string"],"optional":true},"additional_schema_elements":{"type":["set","string"],"required":true},"compression":{"type":"string","required":true},"format":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"report_name":{"type":"string","required":true},"s3_bucket":{"type":"string","required":true},"s3_prefix":{"type":"string","optional":true},"s3_region":{"type":"string","required":true},"time_unit":{"type":"string","required":true}}}},"aws_customer_gateway":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bgp_asn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","required":true}}}},"aws_datapipeline_pipeline":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_datasync_agent":{"version":0,"block":{"attributes":{"activation_key":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_datasync_location_efs":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"efs_file_system_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"subdirectory":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"uri":{"type":"string","computed":true}},"block_types":{"ec2_config":{"nesting_mode":"list","block":{"attributes":{"security_group_arns":{"type":["set","string"],"required":true},"subnet_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_datasync_location_nfs":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"server_hostname":{"type":"string","required":true},"subdirectory":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"uri":{"type":"string","computed":true}},"block_types":{"on_prem_config":{"nesting_mode":"list","block":{"attributes":{"agent_arns":{"type":["set","string"],"required":true}}},"min_items":1,"max_items":1}}}},"aws_datasync_location_s3":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"s3_bucket_arn":{"type":"string","required":true},"subdirectory":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"uri":{"type":"string","computed":true}},"block_types":{"s3_config":{"nesting_mode":"list","block":{"attributes":{"bucket_access_role_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_datasync_location_smb":{"version":0,"block":{"attributes":{"agent_arns":{"type":["set","string"],"required":true},"arn":{"type":"string","computed":true},"domain":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"password":{"type":"string","required":true,"sensitive":true},"server_hostname":{"type":"string","required":true},"subdirectory":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"uri":{"type":"string","computed":true},"user":{"type":"string","required":true}},"block_types":{"mount_options":{"nesting_mode":"list","block":{"attributes":{"version":{"type":"string","optional":true}}},"max_items":1}}}},"aws_datasync_task":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cloudwatch_log_group_arn":{"type":"string","optional":true},"destination_location_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"source_location_arn":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"options":{"nesting_mode":"list","block":{"attributes":{"atime":{"type":"string","optional":true},"bytes_per_second":{"type":"number","optional":true},"gid":{"type":"string","optional":true},"mtime":{"type":"string","optional":true},"posix_permissions":{"type":"string","optional":true},"preserve_deleted_files":{"type":"string","optional":true},"preserve_devices":{"type":"string","optional":true},"uid":{"type":"string","optional":true},"verify_mode":{"type":"string","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_dax_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true},"cluster_address":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"configuration_endpoint":{"type":"string","computed":true},"description":{"type":"string","optional":true},"iam_role_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"maintenance_window":{"type":"string","optional":true,"computed":true},"node_type":{"type":"string","required":true},"nodes":{"type":["list",["object",{"address":"string","availability_zone":"string","id":"string","port":"number"}]],"computed":true},"notification_topic_arn":{"type":"string","optional":true},"parameter_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","computed":true},"replication_factor":{"type":"number","required":true},"security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"subnet_group_name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"server_side_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dax_parameter_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_dax_subnet_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","computed":true}}}},"aws_db_cluster_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zones":{"type":["list","string"],"computed":true},"db_cluster_identifier":{"type":"string","required":true},"db_cluster_snapshot_arn":{"type":"string","computed":true},"db_cluster_snapshot_identifier":{"type":"string","required":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_type":{"type":"string","computed":true},"source_db_cluster_snapshot_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_db_event_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_aws_id":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"event_categories":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"sns_topic":{"type":"string","required":true},"source_ids":{"type":["set","string"],"optional":true},"source_type":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_db_instance":{"version":1,"block":{"attributes":{"address":{"type":"string","computed":true},"allocated_storage":{"type":"number","optional":true,"computed":true},"allow_major_version_upgrade":{"type":"bool","optional":true},"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"backup_retention_period":{"type":"number","optional":true,"computed":true},"backup_window":{"type":"string","optional":true,"computed":true},"ca_cert_identifier":{"type":"string","optional":true,"computed":true},"character_set_name":{"type":"string","optional":true,"computed":true},"copy_tags_to_snapshot":{"type":"bool","optional":true},"db_subnet_group_name":{"type":"string","optional":true,"computed":true},"delete_automated_backups":{"type":"bool","optional":true},"deletion_protection":{"type":"bool","optional":true},"domain":{"type":"string","optional":true},"domain_iam_role_name":{"type":"string","optional":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"optional":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true,"computed":true},"engine_version":{"type":"string","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"hosted_zone_id":{"type":"string","computed":true},"iam_database_authentication_enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"instance_class":{"type":"string","required":true},"iops":{"type":"number","optional":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"license_model":{"type":"string","optional":true,"computed":true},"maintenance_window":{"type":"string","optional":true,"computed":true},"max_allocated_storage":{"type":"number","optional":true},"monitoring_interval":{"type":"number","optional":true},"monitoring_role_arn":{"type":"string","optional":true,"computed":true},"multi_az":{"type":"bool","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"option_group_name":{"type":"string","optional":true,"computed":true},"parameter_group_name":{"type":"string","optional":true,"computed":true},"password":{"type":"string","optional":true,"sensitive":true},"performance_insights_enabled":{"type":"bool","optional":true},"performance_insights_kms_key_id":{"type":"string","optional":true,"computed":true},"performance_insights_retention_period":{"type":"number","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"publicly_accessible":{"type":"bool","optional":true},"replicas":{"type":["list","string"],"computed":true},"replicate_source_db":{"type":"string","optional":true},"resource_id":{"type":"string","computed":true},"security_group_names":{"type":["set","string"],"optional":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_identifier":{"type":"string","optional":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","optional":true},"storage_type":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"timezone":{"type":"string","optional":true,"computed":true},"username":{"type":"string","optional":true,"computed":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"s3_import":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","required":true},"bucket_prefix":{"type":"string","optional":true},"ingestion_role":{"type":"string","required":true},"source_engine":{"type":"string","required":true},"source_engine_version":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_db_instance_role_association":{"version":0,"block":{"attributes":{"db_instance_identifier":{"type":"string","required":true},"feature_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","required":true}}}},"aws_db_option_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"engine_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"major_engine_version":{"type":"string","required":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"option_group_description":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"option":{"nesting_mode":"set","block":{"attributes":{"db_security_group_memberships":{"type":["set","string"],"optional":true},"option_name":{"type":"string","required":true},"port":{"type":"number","optional":true},"version":{"type":"string","optional":true},"vpc_security_group_memberships":{"type":["set","string"],"optional":true}},"block_types":{"option_settings":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true}}}}}}},"aws_db_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_db_security_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"ingress":{"nesting_mode":"set","block":{"attributes":{"cidr":{"type":"string","optional":true},"security_group_id":{"type":"string","optional":true,"computed":true},"security_group_name":{"type":"string","optional":true,"computed":true},"security_group_owner_id":{"type":"string","optional":true,"computed":true}}},"min_items":1}}}},"aws_db_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zone":{"type":"string","computed":true},"db_instance_identifier":{"type":"string","required":true},"db_snapshot_arn":{"type":"string","computed":true},"db_snapshot_identifier":{"type":"string","required":true},"encrypted":{"type":"bool","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"iops":{"type":"number","computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"option_group_name":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_type":{"type":"string","computed":true},"source_db_snapshot_identifier":{"type":"string","computed":true},"source_region":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_type":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"read":{"type":"string","optional":true}}}}}}},"aws_db_subnet_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_default_network_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"default_network_acl_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"egress":{"nesting_mode":"set","block":{"attributes":{"action":{"type":"string","required":true},"cidr_block":{"type":"string","optional":true},"from_port":{"type":"number","required":true},"icmp_code":{"type":"number","optional":true},"icmp_type":{"type":"number","optional":true},"ipv6_cidr_block":{"type":"string","optional":true},"protocol":{"type":"string","required":true},"rule_no":{"type":"number","required":true},"to_port":{"type":"number","required":true}}}},"ingress":{"nesting_mode":"set","block":{"attributes":{"action":{"type":"string","required":true},"cidr_block":{"type":"string","optional":true},"from_port":{"type":"number","required":true},"icmp_code":{"type":"number","optional":true},"icmp_type":{"type":"number","optional":true},"ipv6_cidr_block":{"type":"string","optional":true},"protocol":{"type":"string","required":true},"rule_no":{"type":"number","required":true},"to_port":{"type":"number","required":true}}}}}}},"aws_default_route_table":{"version":0,"block":{"attributes":{"default_route_table_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"propagating_vgws":{"type":["set","string"],"optional":true},"route":{"type":["set",["object",{"cidr_block":"string","egress_only_gateway_id":"string","gateway_id":"string","instance_id":"string","ipv6_cidr_block":"string","nat_gateway_id":"string","network_interface_id":"string","transit_gateway_id":"string","vpc_peering_connection_id":"string"}]],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}}}},"aws_default_security_group":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"egress":{"type":["set",["object",{"cidr_blocks":["list","string"],"description":"string","from_port":"number","ipv6_cidr_blocks":["list","string"],"prefix_list_ids":["list","string"],"protocol":"string","security_groups":["set","string"],"self":"bool","to_port":"number"}]],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ingress":{"type":["set",["object",{"cidr_blocks":["list","string"],"description":"string","from_port":"number","ipv6_cidr_blocks":["list","string"],"prefix_list_ids":["list","string"],"protocol":"string","security_groups":["set","string"],"self":"bool","to_port":"number"}]],"optional":true,"computed":true},"name":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"revoke_rules_on_delete":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","optional":true,"computed":true}}}},"aws_default_subnet":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_ipv6_address_on_creation":{"type":"bool","computed":true},"availability_zone":{"type":"string","required":true},"availability_zone_id":{"type":"string","computed":true},"cidr_block":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block":{"type":"string","computed":true},"ipv6_cidr_block_association_id":{"type":"string","computed":true},"map_public_ip_on_launch":{"type":"bool","optional":true,"computed":true},"outpost_arn":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_default_vpc":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_generated_ipv6_cidr_block":{"type":"bool","computed":true},"cidr_block":{"type":"string","computed":true},"default_network_acl_id":{"type":"string","computed":true},"default_route_table_id":{"type":"string","computed":true},"default_security_group_id":{"type":"string","computed":true},"dhcp_options_id":{"type":"string","computed":true},"enable_classiclink":{"type":"bool","optional":true,"computed":true},"enable_classiclink_dns_support":{"type":"bool","optional":true,"computed":true},"enable_dns_hostnames":{"type":"bool","optional":true,"computed":true},"enable_dns_support":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_tenancy":{"type":"string","computed":true},"ipv6_association_id":{"type":"string","computed":true},"ipv6_cidr_block":{"type":"string","computed":true},"main_route_table_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_default_vpc_dhcp_options":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","computed":true},"domain_name_servers":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"netbios_name_servers":{"type":["list","string"],"optional":true},"netbios_node_type":{"type":"string","optional":true},"ntp_servers":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_devicefarm_project":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_directory_service_conditional_forwarder":{"version":0,"block":{"attributes":{"directory_id":{"type":"string","required":true},"dns_ips":{"type":["list","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"remote_domain_name":{"type":"string","required":true}}}},"aws_directory_service_directory":{"version":0,"block":{"attributes":{"access_url":{"type":"string","computed":true},"alias":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"dns_ip_addresses":{"type":["set","string"],"computed":true},"edition":{"type":"string","optional":true,"computed":true},"enable_sso":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"password":{"type":"string","required":true,"sensitive":true},"security_group_id":{"type":"string","computed":true},"short_name":{"type":"string","optional":true,"computed":true},"size":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true}},"block_types":{"connect_settings":{"nesting_mode":"list","block":{"attributes":{"availability_zones":{"type":["set","string"],"computed":true},"connect_ips":{"type":["set","string"],"computed":true},"customer_dns_ips":{"type":["set","string"],"required":true},"customer_username":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","required":true}}},"max_items":1},"vpc_settings":{"nesting_mode":"list","block":{"attributes":{"availability_zones":{"type":["set","string"],"computed":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_directory_service_log_subscription":{"version":0,"block":{"attributes":{"directory_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","required":true}}}},"aws_dlm_lifecycle_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","required":true},"execution_role_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"state":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"policy_details":{"nesting_mode":"list","block":{"attributes":{"resource_types":{"type":["list","string"],"required":true},"target_tags":{"type":["map","string"],"required":true}},"block_types":{"schedule":{"nesting_mode":"list","block":{"attributes":{"copy_tags":{"type":"bool","optional":true,"computed":true},"name":{"type":"string","required":true},"tags_to_add":{"type":["map","string"],"optional":true}},"block_types":{"create_rule":{"nesting_mode":"list","block":{"attributes":{"interval":{"type":"number","required":true},"interval_unit":{"type":"string","optional":true},"times":{"type":["list","string"],"optional":true,"computed":true}}},"min_items":1,"max_items":1},"retain_rule":{"nesting_mode":"list","block":{"attributes":{"count":{"type":"number","required":true}}},"min_items":1,"max_items":1}}},"min_items":1}}},"min_items":1,"max_items":1}}}},"aws_dms_certificate":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","computed":true},"certificate_id":{"type":"string","required":true},"certificate_pem":{"type":"string","optional":true,"sensitive":true},"certificate_wallet":{"type":"string","optional":true,"sensitive":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_dms_endpoint":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","optional":true,"computed":true},"database_name":{"type":"string","optional":true},"endpoint_arn":{"type":"string","computed":true},"endpoint_id":{"type":"string","required":true},"endpoint_type":{"type":"string","required":true},"engine_name":{"type":"string","required":true},"extra_connection_attributes":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true,"computed":true},"password":{"type":"string","optional":true,"sensitive":true},"port":{"type":"number","optional":true},"server_name":{"type":"string","optional":true},"service_access_role":{"type":"string","optional":true},"ssl_mode":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"username":{"type":"string","optional":true}},"block_types":{"elasticsearch_settings":{"nesting_mode":"list","block":{"attributes":{"endpoint_uri":{"type":"string","required":true},"error_retry_duration":{"type":"number","optional":true},"full_load_error_percentage":{"type":"number","optional":true},"service_access_role_arn":{"type":"string","required":true}}},"max_items":1},"kafka_settings":{"nesting_mode":"list","block":{"attributes":{"broker":{"type":"string","required":true},"topic":{"type":"string","optional":true}}},"max_items":1},"kinesis_settings":{"nesting_mode":"list","block":{"attributes":{"message_format":{"type":"string","optional":true},"service_access_role_arn":{"type":"string","optional":true},"stream_arn":{"type":"string","optional":true}}},"max_items":1},"mongodb_settings":{"nesting_mode":"list","block":{"attributes":{"auth_mechanism":{"type":"string","optional":true},"auth_source":{"type":"string","optional":true},"auth_type":{"type":"string","optional":true},"docs_to_investigate":{"type":"string","optional":true},"extract_doc_id":{"type":"string","optional":true},"nesting_level":{"type":"string","optional":true}}},"max_items":1},"s3_settings":{"nesting_mode":"list","block":{"attributes":{"bucket_folder":{"type":"string","optional":true},"bucket_name":{"type":"string","optional":true},"compression_type":{"type":"string","optional":true},"csv_delimiter":{"type":"string","optional":true},"csv_row_delimiter":{"type":"string","optional":true},"external_table_definition":{"type":"string","optional":true},"service_access_role_arn":{"type":"string","optional":true}}},"max_items":1}}}},"aws_dms_event_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"event_categories":{"type":["set","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"sns_topic_arn":{"type":"string","required":true},"source_ids":{"type":["set","string"],"optional":true},"source_type":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dms_replication_instance":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","optional":true,"computed":true},"apply_immediately":{"type":"bool","optional":true},"auto_minor_version_upgrade":{"type":"bool","optional":true,"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true,"computed":true},"multi_az":{"type":"bool","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"publicly_accessible":{"type":"bool","optional":true,"computed":true},"replication_instance_arn":{"type":"string","computed":true},"replication_instance_class":{"type":"string","required":true},"replication_instance_id":{"type":"string","required":true},"replication_instance_private_ips":{"type":["list","string"],"computed":true},"replication_instance_public_ips":{"type":["list","string"],"computed":true},"replication_subnet_group_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dms_replication_subnet_group":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"replication_subnet_group_arn":{"type":"string","computed":true},"replication_subnet_group_description":{"type":"string","required":true},"replication_subnet_group_id":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}}}},"aws_dms_replication_task":{"version":0,"block":{"attributes":{"cdc_start_time":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"migration_type":{"type":"string","required":true},"replication_instance_arn":{"type":"string","required":true},"replication_task_arn":{"type":"string","computed":true},"replication_task_id":{"type":"string","required":true},"replication_task_settings":{"type":"string","optional":true},"source_endpoint_arn":{"type":"string","required":true},"table_mappings":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"target_endpoint_arn":{"type":"string","required":true}}}},"aws_docdb_cluster":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"backup_retention_period":{"type":"number","optional":true},"cluster_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier_prefix":{"type":"string","optional":true,"computed":true},"cluster_members":{"type":["set","string"],"optional":true,"computed":true},"cluster_resource_id":{"type":"string","computed":true},"db_cluster_parameter_group_name":{"type":"string","optional":true,"computed":true},"db_subnet_group_name":{"type":"string","optional":true,"computed":true},"deletion_protection":{"type":"bool","optional":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"optional":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"master_password":{"type":"string","optional":true,"sensitive":true},"master_username":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"reader_endpoint":{"type":"string","computed":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_identifier":{"type":"string","optional":true},"storage_encrypted":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_docdb_cluster_instance":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"ca_cert_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier":{"type":"string","required":true},"db_subnet_group_name":{"type":"string","computed":true},"dbi_resource_id":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"instance_class":{"type":"string","required":true},"kms_key_id":{"type":"string","computed":true},"port":{"type":"number","computed":true},"preferred_backup_window":{"type":"string","computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"promotion_tier":{"type":"number","optional":true},"publicly_accessible":{"type":"bool","computed":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true},"writer":{"type":"bool","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_docdb_cluster_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_docdb_cluster_snapshot":{"version":0,"block":{"attributes":{"availability_zones":{"type":["list","string"],"computed":true},"db_cluster_identifier":{"type":"string","required":true},"db_cluster_snapshot_arn":{"type":"string","computed":true},"db_cluster_snapshot_identifier":{"type":"string","required":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_type":{"type":"string","computed":true},"source_db_cluster_snapshot_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_docdb_subnet_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_dx_bgp_peer":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"bgp_peer_id":{"type":"string","computed":true},"bgp_status":{"type":"string","computed":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"virtual_interface_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_connection":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bandwidth":{"type":"string","required":true},"has_logical_redundancy":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"location":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_dx_connection_association":{"version":0,"block":{"attributes":{"connection_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"lag_id":{"type":"string","required":true}}}},"aws_dx_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_gateway_association":{"version":1,"block":{"attributes":{"allowed_prefixes":{"type":["set","string"],"optional":true,"computed":true},"associated_gateway_id":{"type":"string","optional":true,"computed":true},"associated_gateway_owner_account_id":{"type":"string","optional":true,"computed":true},"associated_gateway_type":{"type":"string","computed":true},"dx_gateway_association_id":{"type":"string","computed":true},"dx_gateway_id":{"type":"string","required":true},"dx_gateway_owner_account_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"proposal_id":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dx_gateway_association_proposal":{"version":0,"block":{"attributes":{"allowed_prefixes":{"type":["set","string"],"optional":true,"computed":true},"associated_gateway_id":{"type":"string","required":true},"associated_gateway_owner_account_id":{"type":"string","computed":true},"associated_gateway_type":{"type":"string","computed":true},"dx_gateway_id":{"type":"string","required":true},"dx_gateway_owner_account_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_dx_hosted_private_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"mtu":{"type":"number","optional":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","required":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_private_virtual_interface_accepter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"dx_gateway_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"virtual_interface_id":{"type":"string","required":true},"vpn_gateway_id":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_public_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","required":true},"route_filter_prefixes":{"type":["set","string"],"required":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_public_virtual_interface_accepter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"virtual_interface_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_transit_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"mtu":{"type":"number","optional":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","required":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_transit_virtual_interface_accepter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"dx_gateway_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"virtual_interface_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_lag":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"connections_bandwidth":{"type":"string","required":true},"force_destroy":{"type":"bool","optional":true},"has_logical_redundancy":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"location":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_dx_private_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"dx_gateway_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"mtu":{"type":"number","optional":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vlan":{"type":"number","required":true},"vpn_gateway_id":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dx_public_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"route_filter_prefixes":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_transit_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"dx_gateway_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"mtu":{"type":"number","optional":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dynamodb_global_table":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"replica":{"nesting_mode":"set","block":{"attributes":{"region_name":{"type":"string","required":true}}},"min_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dynamodb_table":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"billing_mode":{"type":"string","optional":true},"hash_key":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"range_key":{"type":"string","optional":true},"read_capacity":{"type":"number","optional":true},"stream_arn":{"type":"string","computed":true},"stream_enabled":{"type":"bool","optional":true},"stream_label":{"type":"string","computed":true},"stream_view_type":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"write_capacity":{"type":"number","optional":true}},"block_types":{"attribute":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"type":{"type":"string","required":true}}},"min_items":1},"global_secondary_index":{"nesting_mode":"set","block":{"attributes":{"hash_key":{"type":"string","required":true},"name":{"type":"string","required":true},"non_key_attributes":{"type":["list","string"],"optional":true},"projection_type":{"type":"string","required":true},"range_key":{"type":"string","optional":true},"read_capacity":{"type":"number","optional":true},"write_capacity":{"type":"number","optional":true}}}},"local_secondary_index":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"non_key_attributes":{"type":["list","string"],"optional":true},"projection_type":{"type":"string","required":true},"range_key":{"type":"string","required":true}}}},"point_in_time_recovery":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true}}},"max_items":1},"replica":{"nesting_mode":"set","block":{"attributes":{"region_name":{"type":"string","required":true}}}},"server_side_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true},"kms_key_arn":{"type":"string","optional":true,"computed":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}},"ttl":{"nesting_mode":"list","block":{"attributes":{"attribute_name":{"type":"string","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_dynamodb_table_item":{"version":0,"block":{"attributes":{"hash_key":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"item":{"type":"string","required":true},"range_key":{"type":"string","optional":true},"table_name":{"type":"string","required":true}}}},"aws_ebs_default_kms_key":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"key_arn":{"type":"string","required":true}}}},"aws_ebs_encryption_by_default":{"version":0,"block":{"attributes":{"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ebs_snapshot":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"data_encryption_key_id":{"type":"string","computed":true},"description":{"type":"string","optional":true},"encrypted":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"owner_alias":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"volume_id":{"type":"string","required":true},"volume_size":{"type":"number","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_ebs_snapshot_copy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"data_encryption_key_id":{"type":"string","computed":true},"description":{"type":"string","optional":true},"encrypted":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"owner_alias":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"source_region":{"type":"string","required":true},"source_snapshot_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","computed":true}}}},"aws_ebs_volume":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"multi_attach_enabled":{"type":"bool","optional":true},"outpost_arn":{"type":"string","optional":true},"size":{"type":"number","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true,"computed":true}}}},"aws_ec2_availability_zone_group":{"version":0,"block":{"attributes":{"group_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"opt_in_status":{"type":"string","required":true}}}},"aws_ec2_capacity_reservation":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","required":true},"ebs_optimized":{"type":"bool","optional":true},"end_date":{"type":"string","optional":true},"end_date_type":{"type":"string","optional":true},"ephemeral_storage":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_count":{"type":"number","required":true},"instance_match_criteria":{"type":"string","optional":true},"instance_platform":{"type":"string","required":true},"instance_type":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"tenancy":{"type":"string","optional":true}}}},"aws_ec2_client_vpn_authorization_rule":{"version":0,"block":{"attributes":{"access_group_id":{"type":"string","optional":true},"authorize_all_groups":{"type":"bool","optional":true},"client_vpn_endpoint_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"target_network_cidr":{"type":"string","required":true}}}},"aws_ec2_client_vpn_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"client_cidr_block":{"type":"string","required":true},"description":{"type":"string","optional":true},"dns_name":{"type":"string","computed":true},"dns_servers":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"server_certificate_arn":{"type":"string","required":true},"split_tunnel":{"type":"bool","optional":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"transport_protocol":{"type":"string","optional":true}},"block_types":{"authentication_options":{"nesting_mode":"list","block":{"attributes":{"active_directory_id":{"type":"string","optional":true},"root_certificate_chain_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":2},"connection_log_options":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_log_group":{"type":"string","optional":true},"cloudwatch_log_stream":{"type":"string","optional":true},"enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"aws_ec2_client_vpn_network_association":{"version":0,"block":{"attributes":{"client_vpn_endpoint_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"security_groups":{"type":["set","string"],"computed":true},"status":{"type":"string","computed":true},"subnet_id":{"type":"string","required":true},"vpc_id":{"type":"string","computed":true}}}},"aws_ec2_client_vpn_route":{"version":0,"block":{"attributes":{"client_vpn_endpoint_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"origin":{"type":"string","computed":true},"target_vpc_subnet_id":{"type":"string","required":true},"type":{"type":"string","computed":true}}}},"aws_ec2_fleet":{"version":0,"block":{"attributes":{"excess_capacity_termination_policy":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"replace_unhealthy_instances":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"terminate_instances":{"type":"bool","optional":true},"terminate_instances_with_expiration":{"type":"bool","optional":true},"type":{"type":"string","optional":true}},"block_types":{"launch_template_config":{"nesting_mode":"list","block":{"block_types":{"launch_template_specification":{"nesting_mode":"list","block":{"attributes":{"launch_template_id":{"type":"string","optional":true},"launch_template_name":{"type":"string","optional":true},"version":{"type":"string","required":true}}},"min_items":1,"max_items":1},"override":{"nesting_mode":"list","block":{"attributes":{"availability_zone":{"type":"string","optional":true},"instance_type":{"type":"string","optional":true},"max_price":{"type":"string","optional":true},"priority":{"type":"number","optional":true},"subnet_id":{"type":"string","optional":true},"weighted_capacity":{"type":"number","optional":true}}},"max_items":50}}},"min_items":1,"max_items":1},"on_demand_options":{"nesting_mode":"list","block":{"attributes":{"allocation_strategy":{"type":"string","optional":true}}},"max_items":1},"spot_options":{"nesting_mode":"list","block":{"attributes":{"allocation_strategy":{"type":"string","optional":true},"instance_interruption_behavior":{"type":"string","optional":true},"instance_pools_to_use_count":{"type":"number","optional":true}}},"max_items":1},"target_capacity_specification":{"nesting_mode":"list","block":{"attributes":{"default_target_capacity_type":{"type":"string","required":true},"on_demand_target_capacity":{"type":"number","optional":true},"spot_target_capacity":{"type":"number","optional":true},"total_target_capacity":{"type":"number","required":true}}},"min_items":1,"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_ec2_local_gateway_route":{"version":0,"block":{"attributes":{"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"local_gateway_route_table_id":{"type":"string","required":true},"local_gateway_virtual_interface_group_id":{"type":"string","required":true}}}},"aws_ec2_local_gateway_route_table_vpc_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_gateway_id":{"type":"string","computed":true},"local_gateway_route_table_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}}}},"aws_ec2_tag":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","required":true},"resource_id":{"type":"string","required":true},"value":{"type":"string","required":true}}}},"aws_ec2_traffic_mirror_filter":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"network_services":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_ec2_traffic_mirror_filter_rule":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"protocol":{"type":"number","optional":true},"rule_action":{"type":"string","required":true},"rule_number":{"type":"number","required":true},"source_cidr_block":{"type":"string","required":true},"traffic_direction":{"type":"string","required":true},"traffic_mirror_filter_id":{"type":"string","required":true}},"block_types":{"destination_port_range":{"nesting_mode":"list","block":{"attributes":{"from_port":{"type":"number","optional":true},"to_port":{"type":"number","optional":true}}},"max_items":1},"source_port_range":{"nesting_mode":"list","block":{"attributes":{"from_port":{"type":"number","optional":true},"to_port":{"type":"number","optional":true}}},"max_items":1}}}},"aws_ec2_traffic_mirror_session":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","required":true},"packet_length":{"type":"number","optional":true},"session_number":{"type":"number","required":true},"tags":{"type":["map","string"],"optional":true},"traffic_mirror_filter_id":{"type":"string","required":true},"traffic_mirror_target_id":{"type":"string","required":true},"virtual_network_id":{"type":"number","optional":true,"computed":true}}}},"aws_ec2_traffic_mirror_target":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","optional":true},"network_load_balancer_arn":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_ec2_transit_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"number","optional":true},"arn":{"type":"string","computed":true},"association_default_route_table_id":{"type":"string","computed":true},"auto_accept_shared_attachments":{"type":"string","optional":true},"default_route_table_association":{"type":"string","optional":true},"default_route_table_propagation":{"type":"string","optional":true},"description":{"type":"string","optional":true},"dns_support":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"propagation_default_route_table_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpn_ecmp_support":{"type":"string","optional":true}}}},"aws_ec2_transit_gateway_peering_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"peer_account_id":{"type":"string","optional":true,"computed":true},"peer_region":{"type":"string","required":true},"peer_transit_gateway_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_peering_attachment_accepter":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"peer_account_id":{"type":"string","computed":true},"peer_region":{"type":"string","computed":true},"peer_transit_gateway_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_attachment_id":{"type":"string","required":true},"transit_gateway_id":{"type":"string","computed":true}}}},"aws_ec2_transit_gateway_route":{"version":0,"block":{"attributes":{"blackhole":{"type":"bool","optional":true},"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"transit_gateway_attachment_id":{"type":"string","optional":true},"transit_gateway_route_table_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_route_table":{"version":0,"block":{"attributes":{"default_association_route_table":{"type":"bool","computed":true},"default_propagation_route_table":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_route_table_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_id":{"type":"string","computed":true},"resource_type":{"type":"string","computed":true},"transit_gateway_attachment_id":{"type":"string","required":true},"transit_gateway_route_table_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_route_table_propagation":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_id":{"type":"string","computed":true},"resource_type":{"type":"string","computed":true},"transit_gateway_attachment_id":{"type":"string","required":true},"transit_gateway_route_table_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_vpc_attachment":{"version":0,"block":{"attributes":{"dns_support":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_support":{"type":"string","optional":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_default_route_table_association":{"type":"bool","optional":true},"transit_gateway_default_route_table_propagation":{"type":"bool","optional":true},"transit_gateway_id":{"type":"string","required":true},"vpc_id":{"type":"string","required":true},"vpc_owner_id":{"type":"string","computed":true}}}},"aws_ec2_transit_gateway_vpc_attachment_accepter":{"version":0,"block":{"attributes":{"dns_support":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_support":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_attachment_id":{"type":"string","required":true},"transit_gateway_default_route_table_association":{"type":"bool","optional":true},"transit_gateway_default_route_table_propagation":{"type":"bool","optional":true},"transit_gateway_id":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true},"vpc_owner_id":{"type":"string","computed":true}}}},"aws_ecr_lifecycle_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true},"registry_id":{"type":"string","computed":true},"repository":{"type":"string","required":true}}}},"aws_ecr_repository":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"image_tag_mutability":{"type":"string","optional":true},"name":{"type":"string","required":true},"registry_id":{"type":"string","computed":true},"repository_url":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"encryption_configuration":{"nesting_mode":"list","block":{"attributes":{"encryption_type":{"type":"string","optional":true},"kms_key":{"type":"string","optional":true,"computed":true}}}},"image_scanning_configuration":{"nesting_mode":"list","block":{"attributes":{"scan_on_push":{"type":"bool","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true}}}}}}},"aws_ecr_repository_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true},"registry_id":{"type":"string","computed":true},"repository":{"type":"string","required":true}}}},"aws_ecs_capacity_provider":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"auto_scaling_group_provider":{"nesting_mode":"list","block":{"attributes":{"auto_scaling_group_arn":{"type":"string","required":true},"managed_termination_protection":{"type":"string","optional":true,"computed":true}},"block_types":{"managed_scaling":{"nesting_mode":"list","block":{"attributes":{"maximum_scaling_step_size":{"type":"number","optional":true,"computed":true},"minimum_scaling_step_size":{"type":"number","optional":true,"computed":true},"status":{"type":"string","optional":true,"computed":true},"target_capacity":{"type":"number","optional":true,"computed":true}}},"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_ecs_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"capacity_providers":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"default_capacity_provider_strategy":{"nesting_mode":"set","block":{"attributes":{"base":{"type":"number","optional":true},"capacity_provider":{"type":"string","required":true},"weight":{"type":"number","optional":true}}}},"setting":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_ecs_service":{"version":0,"block":{"attributes":{"cluster":{"type":"string","optional":true,"computed":true},"deployment_maximum_percent":{"type":"number","optional":true},"deployment_minimum_healthy_percent":{"type":"number","optional":true},"desired_count":{"type":"number","optional":true},"enable_ecs_managed_tags":{"type":"bool","optional":true},"force_new_deployment":{"type":"bool","optional":true},"health_check_grace_period_seconds":{"type":"number","optional":true},"iam_role":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"launch_type":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"platform_version":{"type":"string","optional":true,"computed":true},"propagate_tags":{"type":"string","optional":true},"scheduling_strategy":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"task_definition":{"type":"string","optional":true}},"block_types":{"capacity_provider_strategy":{"nesting_mode":"set","block":{"attributes":{"base":{"type":"number","optional":true},"capacity_provider":{"type":"string","required":true},"weight":{"type":"number","optional":true}}}},"deployment_controller":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","optional":true}}},"max_items":1},"load_balancer":{"nesting_mode":"set","block":{"attributes":{"container_name":{"type":"string","required":true},"container_port":{"type":"number","required":true},"elb_name":{"type":"string","optional":true},"target_group_arn":{"type":"string","optional":true}}}},"network_configuration":{"nesting_mode":"list","block":{"attributes":{"assign_public_ip":{"type":"bool","optional":true},"security_groups":{"type":["set","string"],"optional":true},"subnets":{"type":["set","string"],"required":true}}},"max_items":1},"ordered_placement_strategy":{"nesting_mode":"list","block":{"attributes":{"field":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"max_items":5},"placement_constraints":{"nesting_mode":"set","block":{"attributes":{"expression":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"max_items":10},"service_registries":{"nesting_mode":"list","block":{"attributes":{"container_name":{"type":"string","optional":true},"container_port":{"type":"number","optional":true},"port":{"type":"number","optional":true},"registry_arn":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true}}}}}}},"aws_ecs_task_definition":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"container_definitions":{"type":"string","required":true},"cpu":{"type":"string","optional":true},"execution_role_arn":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ipc_mode":{"type":"string","optional":true},"memory":{"type":"string","optional":true},"network_mode":{"type":"string","optional":true,"computed":true},"pid_mode":{"type":"string","optional":true},"requires_compatibilities":{"type":["set","string"],"optional":true},"revision":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true},"task_role_arn":{"type":"string","optional":true}},"block_types":{"inference_accelerator":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"device_type":{"type":"string","required":true}}}},"placement_constraints":{"nesting_mode":"set","block":{"attributes":{"expression":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"max_items":10},"proxy_configuration":{"nesting_mode":"list","block":{"attributes":{"container_name":{"type":"string","required":true},"properties":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true}}},"max_items":1},"volume":{"nesting_mode":"set","block":{"attributes":{"host_path":{"type":"string","optional":true},"name":{"type":"string","required":true}},"block_types":{"docker_volume_configuration":{"nesting_mode":"list","block":{"attributes":{"autoprovision":{"type":"bool","optional":true},"driver":{"type":"string","optional":true},"driver_opts":{"type":["map","string"],"optional":true},"labels":{"type":["map","string"],"optional":true},"scope":{"type":"string","optional":true,"computed":true}}},"max_items":1},"efs_volume_configuration":{"nesting_mode":"list","block":{"attributes":{"file_system_id":{"type":"string","required":true},"root_directory":{"type":"string","optional":true},"transit_encryption":{"type":"string","optional":true},"transit_encryption_port":{"type":"number","optional":true}},"block_types":{"authorization_config":{"nesting_mode":"list","block":{"attributes":{"access_point_id":{"type":"string","optional":true},"iam":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}}}}},"aws_efs_access_point":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"file_system_arn":{"type":"string","computed":true},"file_system_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"posix_user":{"nesting_mode":"list","block":{"attributes":{"gid":{"type":"number","required":true},"secondary_gids":{"type":["set","number"],"optional":true},"uid":{"type":"number","required":true}}},"max_items":1},"root_directory":{"nesting_mode":"list","block":{"attributes":{"path":{"type":"string","optional":true,"computed":true}},"block_types":{"creation_info":{"nesting_mode":"list","block":{"attributes":{"owner_gid":{"type":"number","required":true},"owner_uid":{"type":"number","required":true},"permissions":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}}},"aws_efs_file_system":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_token":{"type":"string","optional":true,"computed":true},"dns_name":{"type":"string","computed":true},"encrypted":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"performance_mode":{"type":"string","optional":true,"computed":true},"provisioned_throughput_in_mibps":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true},"throughput_mode":{"type":"string","optional":true}},"block_types":{"lifecycle_policy":{"nesting_mode":"list","block":{"attributes":{"transition_to_ia":{"type":"string","required":true}}},"max_items":1}}}},"aws_efs_file_system_policy":{"version":0,"block":{"attributes":{"file_system_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true}}}},"aws_efs_mount_target":{"version":0,"block":{"attributes":{"availability_zone_id":{"type":"string","computed":true},"availability_zone_name":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"file_system_arn":{"type":"string","computed":true},"file_system_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","optional":true,"computed":true},"mount_target_dns_name":{"type":"string","computed":true},"network_interface_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"subnet_id":{"type":"string","required":true}}}},"aws_egress_only_internet_gateway":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}}}},"aws_eip":{"version":0,"block":{"attributes":{"allocation_id":{"type":"string","computed":true},"associate_with_private_ip":{"type":"string","optional":true},"association_id":{"type":"string","computed":true},"customer_owned_ip":{"type":"string","computed":true},"customer_owned_ipv4_pool":{"type":"string","optional":true},"domain":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instance":{"type":"string","optional":true,"computed":true},"network_interface":{"type":"string","optional":true,"computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"public_ipv4_pool":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc":{"type":"bool","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true},"read":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_eip_association":{"version":0,"block":{"attributes":{"allocation_id":{"type":"string","optional":true,"computed":true},"allow_reassociation":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","optional":true,"computed":true},"private_ip_address":{"type":"string","optional":true,"computed":true},"public_ip":{"type":"string","optional":true,"computed":true}}}},"aws_eks_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_authority":{"type":["list",["object",{"data":"string"}]],"computed":true},"created_at":{"type":"string","computed":true},"enabled_cluster_log_types":{"type":["set","string"],"optional":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identity":{"type":["list",["object",{"oidc":["list",["object",{"issuer":"string"}]]}]],"computed":true},"name":{"type":"string","required":true},"platform_version":{"type":"string","computed":true},"role_arn":{"type":"string","required":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","optional":true,"computed":true}},"block_types":{"encryption_config":{"nesting_mode":"list","block":{"attributes":{"resources":{"type":["set","string"],"required":true}},"block_types":{"provider":{"nesting_mode":"list","block":{"attributes":{"key_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}},"vpc_config":{"nesting_mode":"list","block":{"attributes":{"cluster_security_group_id":{"type":"string","computed":true},"endpoint_private_access":{"type":"bool","optional":true},"endpoint_public_access":{"type":"bool","optional":true},"public_access_cidrs":{"type":["set","string"],"optional":true,"computed":true},"security_group_ids":{"type":["set","string"],"optional":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","computed":true}}},"min_items":1,"max_items":1}}}},"aws_eks_fargate_profile":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"fargate_profile_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"pod_execution_role_arn":{"type":"string","required":true},"status":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"selector":{"nesting_mode":"set","block":{"attributes":{"labels":{"type":["map","string"],"optional":true},"namespace":{"type":"string","required":true}}},"min_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_eks_node_group":{"version":0,"block":{"attributes":{"ami_type":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"disk_size":{"type":"number","optional":true,"computed":true},"force_update_version":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_types":{"type":["list","string"],"optional":true,"computed":true},"labels":{"type":["map","string"],"optional":true},"node_group_name":{"type":"string","required":true},"node_role_arn":{"type":"string","required":true},"release_version":{"type":"string","optional":true,"computed":true},"resources":{"type":["list",["object",{"autoscaling_groups":["list",["object",{"name":"string"}]],"remote_access_security_group_id":"string"}]],"computed":true},"status":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","optional":true,"computed":true}},"block_types":{"remote_access":{"nesting_mode":"list","block":{"attributes":{"ec2_ssh_key":{"type":"string","optional":true},"source_security_group_ids":{"type":["set","string"],"optional":true}}},"max_items":1},"scaling_config":{"nesting_mode":"list","block":{"attributes":{"desired_size":{"type":"number","required":true},"max_size":{"type":"number","required":true},"min_size":{"type":"number","required":true}}},"min_items":1,"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_elastic_beanstalk_application":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"appversion_lifecycle":{"nesting_mode":"list","block":{"attributes":{"delete_source_from_s3":{"type":"bool","optional":true},"max_age_in_days":{"type":"number","optional":true},"max_count":{"type":"number","optional":true},"service_role":{"type":"string","required":true}}},"max_items":1}}}},"aws_elastic_beanstalk_application_version":{"version":0,"block":{"attributes":{"application":{"type":"string","required":true},"arn":{"type":"string","computed":true},"bucket":{"type":"string","required":true},"description":{"type":"string","optional":true},"force_delete":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_elastic_beanstalk_configuration_template":{"version":0,"block":{"attributes":{"application":{"type":"string","required":true},"description":{"type":"string","optional":true},"environment_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"solution_stack_name":{"type":"string","optional":true}},"block_types":{"setting":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"resource":{"type":"string","optional":true},"value":{"type":"string","required":true}}}}}}},"aws_elastic_beanstalk_environment":{"version":1,"block":{"attributes":{"all_settings":{"type":["set",["object",{"name":"string","namespace":"string","resource":"string","value":"string"}]],"computed":true},"application":{"type":"string","required":true},"arn":{"type":"string","computed":true},"autoscaling_groups":{"type":["list","string"],"computed":true},"cname":{"type":"string","computed":true},"cname_prefix":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"endpoint_url":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instances":{"type":["list","string"],"computed":true},"launch_configurations":{"type":["list","string"],"computed":true},"load_balancers":{"type":["list","string"],"computed":true},"name":{"type":"string","required":true},"platform_arn":{"type":"string","optional":true,"computed":true},"poll_interval":{"type":"string","optional":true},"queues":{"type":["list","string"],"computed":true},"solution_stack_name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"template_name":{"type":"string","optional":true},"tier":{"type":"string","optional":true},"triggers":{"type":["list","string"],"computed":true},"version_label":{"type":"string","optional":true,"computed":true},"wait_for_ready_timeout":{"type":"string","optional":true}},"block_types":{"setting":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"resource":{"type":"string","optional":true},"value":{"type":"string","required":true}}}}}}},"aws_elasticache_cluster":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"az_mode":{"type":"string","optional":true,"computed":true},"cache_nodes":{"type":["list",["object",{"address":"string","availability_zone":"string","id":"string","port":"number"}]],"computed":true},"cluster_address":{"type":"string","computed":true},"cluster_id":{"type":"string","required":true},"configuration_endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true,"computed":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"maintenance_window":{"type":"string","optional":true,"computed":true},"node_type":{"type":"string","optional":true,"computed":true},"notification_topic_arn":{"type":"string","optional":true},"num_cache_nodes":{"type":"number","optional":true,"computed":true},"parameter_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"preferred_availability_zones":{"type":["list","string"],"optional":true},"replication_group_id":{"type":"string","optional":true,"computed":true},"security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"security_group_names":{"type":["set","string"],"optional":true,"computed":true},"snapshot_arns":{"type":["set","string"],"optional":true},"snapshot_name":{"type":"string","optional":true},"snapshot_retention_limit":{"type":"number","optional":true},"snapshot_window":{"type":"string","optional":true,"computed":true},"subnet_group_name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_elasticache_parameter_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_elasticache_replication_group":{"version":1,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"at_rest_encryption_enabled":{"type":"bool","optional":true},"auth_token":{"type":"string","optional":true,"sensitive":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"automatic_failover_enabled":{"type":"bool","optional":true},"availability_zones":{"type":["set","string"],"optional":true},"configuration_endpoint_address":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"maintenance_window":{"type":"string","optional":true,"computed":true},"member_clusters":{"type":["set","string"],"computed":true},"node_type":{"type":"string","optional":true,"computed":true},"notification_topic_arn":{"type":"string","optional":true},"number_cache_clusters":{"type":"number","optional":true,"computed":true},"parameter_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"primary_endpoint_address":{"type":"string","computed":true},"replication_group_description":{"type":"string","required":true},"replication_group_id":{"type":"string","required":true},"security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"security_group_names":{"type":["set","string"],"optional":true,"computed":true},"snapshot_arns":{"type":["set","string"],"optional":true},"snapshot_name":{"type":"string","optional":true},"snapshot_retention_limit":{"type":"number","optional":true},"snapshot_window":{"type":"string","optional":true,"computed":true},"subnet_group_name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"transit_encryption_enabled":{"type":"bool","optional":true}},"block_types":{"cluster_mode":{"nesting_mode":"list","block":{"attributes":{"num_node_groups":{"type":"number","required":true},"replicas_per_node_group":{"type":"number","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_elasticache_security_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"security_group_names":{"type":["set","string"],"required":true}}}},"aws_elasticache_subnet_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true}}}},"aws_elasticsearch_domain":{"version":0,"block":{"attributes":{"access_policies":{"type":"string","optional":true,"computed":true},"advanced_options":{"type":["map","string"],"optional":true,"computed":true},"arn":{"type":"string","computed":true},"domain_id":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"elasticsearch_version":{"type":"string","optional":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kibana_endpoint":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"advanced_security_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true},"internal_user_database_enabled":{"type":"bool","optional":true}},"block_types":{"master_user_options":{"nesting_mode":"list","block":{"attributes":{"master_user_arn":{"type":"string","optional":true},"master_user_name":{"type":"string","optional":true},"master_user_password":{"type":"string","optional":true,"sensitive":true}}},"max_items":1}}},"max_items":1},"cluster_config":{"nesting_mode":"list","block":{"attributes":{"dedicated_master_count":{"type":"number","optional":true},"dedicated_master_enabled":{"type":"bool","optional":true},"dedicated_master_type":{"type":"string","optional":true},"instance_count":{"type":"number","optional":true},"instance_type":{"type":"string","optional":true},"warm_count":{"type":"number","optional":true},"warm_enabled":{"type":"bool","optional":true},"warm_type":{"type":"string","optional":true},"zone_awareness_enabled":{"type":"bool","optional":true}},"block_types":{"zone_awareness_config":{"nesting_mode":"list","block":{"attributes":{"availability_zone_count":{"type":"number","optional":true}}},"max_items":1}}},"max_items":1},"cognito_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"identity_pool_id":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"user_pool_id":{"type":"string","required":true}}},"max_items":1},"domain_endpoint_options":{"nesting_mode":"list","block":{"attributes":{"enforce_https":{"type":"bool","required":true},"tls_security_policy":{"type":"string","optional":true,"computed":true}}},"max_items":1},"ebs_options":{"nesting_mode":"list","block":{"attributes":{"ebs_enabled":{"type":"bool","required":true},"iops":{"type":"number","optional":true},"volume_size":{"type":"number","optional":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1},"encrypt_at_rest":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true},"kms_key_id":{"type":"string","optional":true,"computed":true}}},"max_items":1},"log_publishing_options":{"nesting_mode":"set","block":{"attributes":{"cloudwatch_log_group_arn":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"log_type":{"type":"string","required":true}}}},"node_to_node_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true}}},"max_items":1},"snapshot_options":{"nesting_mode":"list","block":{"attributes":{"automated_snapshot_start_hour":{"type":"number","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"update":{"type":"string","optional":true}}}},"vpc_options":{"nesting_mode":"list","block":{"attributes":{"availability_zones":{"type":["set","string"],"computed":true},"security_group_ids":{"type":["set","string"],"optional":true},"subnet_ids":{"type":["set","string"],"optional":true},"vpc_id":{"type":"string","computed":true}}},"max_items":1}}}},"aws_elasticsearch_domain_policy":{"version":0,"block":{"attributes":{"access_policies":{"type":"string","required":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_elastictranscoder_pipeline":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_kms_key_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"input_bucket":{"type":"string","required":true},"name":{"type":"string","optional":true,"computed":true},"output_bucket":{"type":"string","optional":true,"computed":true},"role":{"type":"string","required":true}},"block_types":{"content_config":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","optional":true,"computed":true},"storage_class":{"type":"string","optional":true}}},"max_items":1},"content_config_permissions":{"nesting_mode":"set","block":{"attributes":{"access":{"type":["list","string"],"optional":true},"grantee":{"type":"string","optional":true},"grantee_type":{"type":"string","optional":true}}}},"notifications":{"nesting_mode":"list","block":{"attributes":{"completed":{"type":"string","optional":true},"error":{"type":"string","optional":true},"progressing":{"type":"string","optional":true},"warning":{"type":"string","optional":true}}},"max_items":1},"thumbnail_config":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","optional":true,"computed":true},"storage_class":{"type":"string","optional":true}}},"max_items":1},"thumbnail_config_permissions":{"nesting_mode":"set","block":{"attributes":{"access":{"type":["list","string"],"optional":true},"grantee":{"type":"string","optional":true},"grantee_type":{"type":"string","optional":true}}}}}}},"aws_elastictranscoder_preset":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"container":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"type":{"type":"string","optional":true,"computed":true},"video_codec_options":{"type":["map","string"],"optional":true}},"block_types":{"audio":{"nesting_mode":"list","block":{"attributes":{"audio_packing_mode":{"type":"string","optional":true},"bit_rate":{"type":"string","optional":true},"channels":{"type":"string","optional":true},"codec":{"type":"string","optional":true},"sample_rate":{"type":"string","optional":true}}},"max_items":1},"audio_codec_options":{"nesting_mode":"list","block":{"attributes":{"bit_depth":{"type":"string","optional":true},"bit_order":{"type":"string","optional":true},"profile":{"type":"string","optional":true},"signed":{"type":"string","optional":true}}},"max_items":1},"thumbnails":{"nesting_mode":"list","block":{"attributes":{"aspect_ratio":{"type":"string","optional":true},"format":{"type":"string","optional":true},"interval":{"type":"string","optional":true},"max_height":{"type":"string","optional":true},"max_width":{"type":"string","optional":true},"padding_policy":{"type":"string","optional":true},"resolution":{"type":"string","optional":true},"sizing_policy":{"type":"string","optional":true}}},"max_items":1},"video":{"nesting_mode":"list","block":{"attributes":{"aspect_ratio":{"type":"string","optional":true},"bit_rate":{"type":"string","optional":true},"codec":{"type":"string","optional":true},"display_aspect_ratio":{"type":"string","optional":true},"fixed_gop":{"type":"string","optional":true},"frame_rate":{"type":"string","optional":true},"keyframes_max_dist":{"type":"string","optional":true},"max_frame_rate":{"type":"string","optional":true,"computed":true},"max_height":{"type":"string","optional":true},"max_width":{"type":"string","optional":true},"padding_policy":{"type":"string","optional":true},"resolution":{"type":"string","optional":true},"sizing_policy":{"type":"string","optional":true}}},"max_items":1},"video_watermarks":{"nesting_mode":"set","block":{"attributes":{"horizontal_align":{"type":"string","optional":true},"horizontal_offset":{"type":"string","optional":true},"id":{"type":"string","optional":true},"max_height":{"type":"string","optional":true},"max_width":{"type":"string","optional":true},"opacity":{"type":"string","optional":true},"sizing_policy":{"type":"string","optional":true},"target":{"type":"string","optional":true},"vertical_align":{"type":"string","optional":true},"vertical_offset":{"type":"string","optional":true}}}}}}},"aws_elb":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"connection_draining":{"type":"bool","optional":true},"connection_draining_timeout":{"type":"number","optional":true},"cross_zone_load_balancing":{"type":"bool","optional":true},"dns_name":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","optional":true},"instances":{"type":["set","string"],"optional":true,"computed":true},"internal":{"type":"bool","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"source_security_group":{"type":"string","optional":true,"computed":true},"source_security_group_id":{"type":"string","computed":true},"subnets":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"zone_id":{"type":"string","computed":true}},"block_types":{"access_logs":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"bucket_prefix":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"interval":{"type":"number","optional":true}}},"max_items":1},"health_check":{"nesting_mode":"list","block":{"attributes":{"healthy_threshold":{"type":"number","required":true},"interval":{"type":"number","required":true},"target":{"type":"string","required":true},"timeout":{"type":"number","required":true},"unhealthy_threshold":{"type":"number","required":true}}},"max_items":1},"listener":{"nesting_mode":"set","block":{"attributes":{"instance_port":{"type":"number","required":true},"instance_protocol":{"type":"string","required":true},"lb_port":{"type":"number","required":true},"lb_protocol":{"type":"string","required":true},"ssl_certificate_id":{"type":"string","optional":true}}},"min_items":1}}}},"aws_elb_attachment":{"version":0,"block":{"attributes":{"elb":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instance":{"type":"string","required":true}}}},"aws_emr_cluster":{"version":0,"block":{"attributes":{"additional_info":{"type":"string","optional":true},"applications":{"type":["set","string"],"optional":true},"arn":{"type":"string","computed":true},"autoscaling_role":{"type":"string","optional":true},"cluster_state":{"type":"string","computed":true},"configurations":{"type":"string","optional":true},"configurations_json":{"type":"string","optional":true},"custom_ami_id":{"type":"string","optional":true},"ebs_root_volume_size":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"keep_job_flow_alive_when_no_steps":{"type":"bool","optional":true,"computed":true},"log_uri":{"type":"string","optional":true},"master_public_dns":{"type":"string","computed":true},"name":{"type":"string","required":true},"release_label":{"type":"string","required":true},"scale_down_behavior":{"type":"string","optional":true,"computed":true},"security_configuration":{"type":"string","optional":true},"service_role":{"type":"string","required":true},"step":{"type":["list",["object",{"action_on_failure":"string","hadoop_jar_step":["list",["object",{"args":["list","string"],"jar":"string","main_class":"string","properties":["map","string"]}]],"name":"string"}]],"optional":true,"computed":true},"step_concurrency_level":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true},"termination_protection":{"type":"bool","optional":true,"computed":true},"visible_to_all_users":{"type":"bool","optional":true}},"block_types":{"bootstrap_action":{"nesting_mode":"list","block":{"attributes":{"args":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"path":{"type":"string","required":true}}}},"core_instance_group":{"nesting_mode":"list","block":{"attributes":{"autoscaling_policy":{"type":"string","optional":true},"bid_price":{"type":"string","optional":true},"id":{"type":"string","computed":true},"instance_count":{"type":"number","optional":true},"instance_type":{"type":"string","required":true},"name":{"type":"string","optional":true}},"block_types":{"ebs_config":{"nesting_mode":"set","block":{"attributes":{"iops":{"type":"number","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","required":true},"volumes_per_instance":{"type":"number","optional":true}}}}}},"max_items":1},"ec2_attributes":{"nesting_mode":"list","block":{"attributes":{"additional_master_security_groups":{"type":"string","optional":true},"additional_slave_security_groups":{"type":"string","optional":true},"emr_managed_master_security_group":{"type":"string","optional":true,"computed":true},"emr_managed_slave_security_group":{"type":"string","optional":true,"computed":true},"instance_profile":{"type":"string","required":true},"key_name":{"type":"string","optional":true},"service_access_security_group":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true}}},"max_items":1},"kerberos_attributes":{"nesting_mode":"list","block":{"attributes":{"ad_domain_join_password":{"type":"string","optional":true,"sensitive":true},"ad_domain_join_user":{"type":"string","optional":true},"cross_realm_trust_principal_password":{"type":"string","optional":true,"sensitive":true},"kdc_admin_password":{"type":"string","required":true,"sensitive":true},"realm":{"type":"string","required":true}}},"max_items":1},"master_instance_group":{"nesting_mode":"list","block":{"attributes":{"bid_price":{"type":"string","optional":true},"id":{"type":"string","computed":true},"instance_count":{"type":"number","optional":true},"instance_type":{"type":"string","required":true},"name":{"type":"string","optional":true}},"block_types":{"ebs_config":{"nesting_mode":"set","block":{"attributes":{"iops":{"type":"number","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","required":true},"volumes_per_instance":{"type":"number","optional":true}}}}}},"max_items":1}}}},"aws_emr_instance_group":{"version":0,"block":{"attributes":{"autoscaling_policy":{"type":"string","optional":true},"bid_price":{"type":"string","optional":true},"cluster_id":{"type":"string","required":true},"configurations_json":{"type":"string","optional":true},"ebs_optimized":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_count":{"type":"number","optional":true},"instance_type":{"type":"string","required":true},"name":{"type":"string","optional":true},"running_instance_count":{"type":"number","computed":true},"status":{"type":"string","computed":true}},"block_types":{"ebs_config":{"nesting_mode":"set","block":{"attributes":{"iops":{"type":"number","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","required":true},"volumes_per_instance":{"type":"number","optional":true}}}}}}},"aws_emr_security_configuration":{"version":0,"block":{"attributes":{"configuration":{"type":"string","required":true},"creation_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true}}}},"aws_flow_log":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"eni_id":{"type":"string","optional":true},"iam_role_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"log_destination":{"type":"string","optional":true,"computed":true},"log_destination_type":{"type":"string","optional":true},"log_format":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","optional":true,"computed":true},"max_aggregation_interval":{"type":"number","optional":true},"subnet_id":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"traffic_type":{"type":"string","required":true},"vpc_id":{"type":"string","optional":true}}}},"aws_fms_admin_account":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_fsx_lustre_file_system":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deployment_type":{"type":"string","optional":true},"dns_name":{"type":"string","computed":true},"export_path":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"import_path":{"type":"string","optional":true},"imported_file_chunk_size":{"type":"number","optional":true,"computed":true},"network_interface_ids":{"type":["set","string"],"computed":true},"owner_id":{"type":"string","computed":true},"per_unit_storage_throughput":{"type":"number","optional":true},"security_group_ids":{"type":["set","string"],"optional":true},"storage_capacity":{"type":"number","required":true},"subnet_ids":{"type":["list","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"weekly_maintenance_start_time":{"type":"string","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_fsx_windows_file_system":{"version":0,"block":{"attributes":{"active_directory_id":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"automatic_backup_retention_days":{"type":"number","optional":true},"copy_tags_to_backups":{"type":"bool","optional":true},"daily_automatic_backup_start_time":{"type":"string","optional":true,"computed":true},"deployment_type":{"type":"string","optional":true},"dns_name":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"network_interface_ids":{"type":["set","string"],"computed":true},"owner_id":{"type":"string","computed":true},"preferred_file_server_ip":{"type":"string","computed":true},"preferred_subnet_id":{"type":"string","optional":true,"computed":true},"remote_administration_endpoint":{"type":"string","computed":true},"security_group_ids":{"type":["set","string"],"optional":true},"skip_final_backup":{"type":"bool","optional":true},"storage_capacity":{"type":"number","required":true},"storage_type":{"type":"string","optional":true},"subnet_ids":{"type":["list","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"throughput_capacity":{"type":"number","required":true},"vpc_id":{"type":"string","computed":true},"weekly_maintenance_start_time":{"type":"string","optional":true,"computed":true}},"block_types":{"self_managed_active_directory":{"nesting_mode":"list","block":{"attributes":{"dns_ips":{"type":["set","string"],"required":true},"domain_name":{"type":"string","required":true},"file_system_administrators_group":{"type":"string","optional":true},"organizational_unit_distinguished_name":{"type":"string","optional":true},"password":{"type":"string","required":true,"sensitive":true},"username":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_gamelift_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"routing_strategy":{"nesting_mode":"list","block":{"attributes":{"fleet_id":{"type":"string","optional":true},"message":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_gamelift_build":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"operating_system":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","optional":true}},"block_types":{"storage_location":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"key":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_gamelift_fleet":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"build_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"ec2_instance_type":{"type":"string","required":true},"fleet_type":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_role_arn":{"type":"string","optional":true},"log_paths":{"type":["list","string"],"computed":true},"metric_groups":{"type":["list","string"],"optional":true,"computed":true},"name":{"type":"string","required":true},"new_game_session_protection_policy":{"type":"string","optional":true},"operating_system":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"ec2_inbound_permission":{"nesting_mode":"list","block":{"attributes":{"from_port":{"type":"number","required":true},"ip_range":{"type":"string","required":true},"protocol":{"type":"string","required":true},"to_port":{"type":"number","required":true}}},"max_items":50},"resource_creation_limit_policy":{"nesting_mode":"list","block":{"attributes":{"new_game_sessions_per_creator":{"type":"number","optional":true},"policy_period_in_minutes":{"type":"number","optional":true}}},"max_items":1},"runtime_configuration":{"nesting_mode":"list","block":{"attributes":{"game_session_activation_timeout_seconds":{"type":"number","optional":true},"max_concurrent_game_session_activations":{"type":"number","optional":true}},"block_types":{"server_process":{"nesting_mode":"list","block":{"attributes":{"concurrent_executions":{"type":"number","required":true},"launch_path":{"type":"string","required":true},"parameters":{"type":"string","optional":true}}},"max_items":50}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_gamelift_game_session_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"destinations":{"type":["list","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"timeout_in_seconds":{"type":"number","optional":true}},"block_types":{"player_latency_policy":{"nesting_mode":"list","block":{"attributes":{"maximum_individual_player_latency_milliseconds":{"type":"number","required":true},"policy_duration_seconds":{"type":"number","optional":true}}}}}}},"aws_glacier_vault":{"version":0,"block":{"attributes":{"access_policy":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"location":{"type":"string","computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"notification":{"nesting_mode":"list","block":{"attributes":{"events":{"type":["set","string"],"required":true},"sns_topic":{"type":"string","required":true}}}}}}},"aws_glacier_vault_lock":{"version":0,"block":{"attributes":{"complete_lock":{"type":"bool","required":true},"id":{"type":"string","optional":true,"computed":true},"ignore_deletion_error":{"type":"bool","optional":true},"policy":{"type":"string","required":true},"vault_name":{"type":"string","required":true}}}},"aws_globalaccelerator_accelerator":{"version":0,"block":{"attributes":{"dns_name":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address_type":{"type":"string","optional":true},"ip_sets":{"type":["list",["object",{"ip_addresses":["list","string"],"ip_family":"string"}]],"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"attributes":{"nesting_mode":"list","block":{"attributes":{"flow_logs_enabled":{"type":"bool","optional":true},"flow_logs_s3_bucket":{"type":"string","optional":true},"flow_logs_s3_prefix":{"type":"string","optional":true}}},"max_items":1}}}},"aws_globalaccelerator_endpoint_group":{"version":0,"block":{"attributes":{"endpoint_group_region":{"type":"string","optional":true,"computed":true},"health_check_interval_seconds":{"type":"number","optional":true},"health_check_path":{"type":"string","optional":true},"health_check_port":{"type":"number","optional":true},"health_check_protocol":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true},"threshold_count":{"type":"number","optional":true},"traffic_dial_percentage":{"type":"number","optional":true}},"block_types":{"endpoint_configuration":{"nesting_mode":"set","block":{"attributes":{"endpoint_id":{"type":"string","optional":true},"weight":{"type":"number","optional":true}}},"max_items":10}}}},"aws_globalaccelerator_listener":{"version":0,"block":{"attributes":{"accelerator_arn":{"type":"string","required":true},"client_affinity":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"protocol":{"type":"string","required":true}},"block_types":{"port_range":{"nesting_mode":"set","block":{"attributes":{"from_port":{"type":"number","optional":true},"to_port":{"type":"number","optional":true}}},"min_items":1,"max_items":10}}}},"aws_glue_catalog_database":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"catalog_id":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"location_uri":{"type":"string","optional":true},"name":{"type":"string","required":true},"parameters":{"type":["map","string"],"optional":true}}}},"aws_glue_catalog_table":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"catalog_id":{"type":"string","optional":true,"computed":true},"database_name":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owner":{"type":"string","optional":true},"parameters":{"type":["map","string"],"optional":true},"retention":{"type":"number","optional":true},"table_type":{"type":"string","optional":true},"view_expanded_text":{"type":"string","optional":true},"view_original_text":{"type":"string","optional":true}},"block_types":{"partition_keys":{"nesting_mode":"list","block":{"attributes":{"comment":{"type":"string","optional":true},"name":{"type":"string","required":true},"type":{"type":"string","optional":true}}}},"storage_descriptor":{"nesting_mode":"list","block":{"attributes":{"bucket_columns":{"type":["list","string"],"optional":true},"compressed":{"type":"bool","optional":true},"input_format":{"type":"string","optional":true},"location":{"type":"string","optional":true},"number_of_buckets":{"type":"number","optional":true},"output_format":{"type":"string","optional":true},"parameters":{"type":["map","string"],"optional":true},"stored_as_sub_directories":{"type":"bool","optional":true}},"block_types":{"columns":{"nesting_mode":"list","block":{"attributes":{"comment":{"type":"string","optional":true},"name":{"type":"string","required":true},"type":{"type":"string","optional":true}}}},"ser_de_info":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","optional":true},"parameters":{"type":["map","string"],"optional":true},"serialization_library":{"type":"string","optional":true}}},"max_items":1},"skewed_info":{"nesting_mode":"list","block":{"attributes":{"skewed_column_names":{"type":["list","string"],"optional":true},"skewed_column_value_location_maps":{"type":["map","string"],"optional":true},"skewed_column_values":{"type":["list","string"],"optional":true}}},"max_items":1},"sort_columns":{"nesting_mode":"list","block":{"attributes":{"column":{"type":"string","required":true},"sort_order":{"type":"number","required":true}}}}}},"max_items":1}}}},"aws_glue_classifier":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"csv_classifier":{"nesting_mode":"list","block":{"attributes":{"allow_single_column":{"type":"bool","optional":true},"contains_header":{"type":"string","optional":true},"delimiter":{"type":"string","optional":true},"disable_value_trimming":{"type":"bool","optional":true},"header":{"type":["list","string"],"optional":true},"quote_symbol":{"type":"string","optional":true}}},"max_items":1},"grok_classifier":{"nesting_mode":"list","block":{"attributes":{"classification":{"type":"string","required":true},"custom_patterns":{"type":"string","optional":true},"grok_pattern":{"type":"string","required":true}}},"max_items":1},"json_classifier":{"nesting_mode":"list","block":{"attributes":{"json_path":{"type":"string","required":true}}},"max_items":1},"xml_classifier":{"nesting_mode":"list","block":{"attributes":{"classification":{"type":"string","required":true},"row_tag":{"type":"string","required":true}}},"max_items":1}}}},"aws_glue_connection":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"catalog_id":{"type":"string","optional":true,"computed":true},"connection_properties":{"type":["map","string"],"required":true,"sensitive":true},"connection_type":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"match_criteria":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true}},"block_types":{"physical_connection_requirements":{"nesting_mode":"list","block":{"attributes":{"availability_zone":{"type":"string","optional":true},"security_group_id_list":{"type":["list","string"],"optional":true},"subnet_id":{"type":"string","optional":true}}},"max_items":1}}}},"aws_glue_crawler":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"classifiers":{"type":["list","string"],"optional":true},"configuration":{"type":"string","optional":true},"database_name":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role":{"type":"string","required":true},"schedule":{"type":"string","optional":true},"security_configuration":{"type":"string","optional":true},"table_prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"catalog_target":{"nesting_mode":"list","block":{"attributes":{"database_name":{"type":"string","required":true},"tables":{"type":["list","string"],"required":true}}}},"dynamodb_target":{"nesting_mode":"list","block":{"attributes":{"path":{"type":"string","required":true}}}},"jdbc_target":{"nesting_mode":"list","block":{"attributes":{"connection_name":{"type":"string","required":true},"exclusions":{"type":["list","string"],"optional":true},"path":{"type":"string","required":true}}}},"s3_target":{"nesting_mode":"list","block":{"attributes":{"exclusions":{"type":["list","string"],"optional":true},"path":{"type":"string","required":true}}}},"schema_change_policy":{"nesting_mode":"list","block":{"attributes":{"delete_behavior":{"type":"string","optional":true},"update_behavior":{"type":"string","optional":true}}},"max_items":1}}}},"aws_glue_job":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"connections":{"type":["list","string"],"optional":true},"default_arguments":{"type":["map","string"],"optional":true},"description":{"type":"string","optional":true},"glue_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"max_capacity":{"type":"number","optional":true,"computed":true},"max_retries":{"type":"number","optional":true},"name":{"type":"string","required":true},"number_of_workers":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"security_configuration":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"timeout":{"type":"number","optional":true},"worker_type":{"type":"string","optional":true}},"block_types":{"command":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","optional":true},"python_version":{"type":"string","optional":true,"computed":true},"script_location":{"type":"string","required":true}}},"min_items":1,"max_items":1},"execution_property":{"nesting_mode":"list","block":{"attributes":{"max_concurrent_runs":{"type":"number","optional":true}}},"max_items":1},"notification_property":{"nesting_mode":"list","block":{"attributes":{"notify_delay_after":{"type":"number","optional":true}}},"max_items":1}}}},"aws_glue_security_configuration":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"encryption_configuration":{"nesting_mode":"list","block":{"block_types":{"cloudwatch_encryption":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_encryption_mode":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true}}},"min_items":1,"max_items":1},"job_bookmarks_encryption":{"nesting_mode":"list","block":{"attributes":{"job_bookmarks_encryption_mode":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true}}},"min_items":1,"max_items":1},"s3_encryption":{"nesting_mode":"list","block":{"attributes":{"kms_key_arn":{"type":"string","optional":true},"s3_encryption_mode":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_glue_trigger":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"schedule":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","required":true},"workflow_name":{"type":"string","optional":true}},"block_types":{"actions":{"nesting_mode":"list","block":{"attributes":{"arguments":{"type":["map","string"],"optional":true},"crawler_name":{"type":"string","optional":true},"job_name":{"type":"string","optional":true},"timeout":{"type":"number","optional":true}}},"min_items":1},"predicate":{"nesting_mode":"list","block":{"attributes":{"logical":{"type":"string","optional":true}},"block_types":{"conditions":{"nesting_mode":"list","block":{"attributes":{"crawl_state":{"type":"string","optional":true},"crawler_name":{"type":"string","optional":true},"job_name":{"type":"string","optional":true},"logical_operator":{"type":"string","optional":true},"state":{"type":"string","optional":true}}},"min_items":1}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_glue_workflow":{"version":0,"block":{"attributes":{"default_run_properties":{"type":["map","string"],"optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true}}}},"aws_guardduty_detector":{"version":0,"block":{"attributes":{"account_id":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"enable":{"type":"bool","optional":true},"finding_publishing_frequency":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_guardduty_invite_accepter":{"version":0,"block":{"attributes":{"detector_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"master_account_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_guardduty_ipset":{"version":0,"block":{"attributes":{"activate":{"type":"bool","required":true},"arn":{"type":"string","computed":true},"detector_id":{"type":"string","required":true},"format":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"location":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_guardduty_member":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"detector_id":{"type":"string","required":true},"disable_email_notification":{"type":"bool","optional":true},"email":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"invitation_message":{"type":"string","optional":true},"invite":{"type":"bool","optional":true},"relationship_status":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_guardduty_organization_admin_account":{"version":0,"block":{"attributes":{"admin_account_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_guardduty_organization_configuration":{"version":0,"block":{"attributes":{"auto_enable":{"type":"bool","required":true},"detector_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_guardduty_threatintelset":{"version":0,"block":{"attributes":{"activate":{"type":"bool","required":true},"arn":{"type":"string","computed":true},"detector_id":{"type":"string","required":true},"format":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"location":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_iam_access_key":{"version":0,"block":{"attributes":{"encrypted_secret":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key_fingerprint":{"type":"string","computed":true},"pgp_key":{"type":"string","optional":true},"secret":{"type":"string","computed":true,"sensitive":true},"ses_smtp_password_v4":{"type":"string","computed":true,"sensitive":true},"status":{"type":"string","optional":true,"computed":true},"user":{"type":"string","required":true}}}},"aws_iam_account_alias":{"version":0,"block":{"attributes":{"account_alias":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_iam_account_password_policy":{"version":0,"block":{"attributes":{"allow_users_to_change_password":{"type":"bool","optional":true},"expire_passwords":{"type":"bool","computed":true},"hard_expiry":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"max_password_age":{"type":"number","optional":true,"computed":true},"minimum_password_length":{"type":"number","optional":true},"password_reuse_prevention":{"type":"number","optional":true,"computed":true},"require_lowercase_characters":{"type":"bool","optional":true,"computed":true},"require_numbers":{"type":"bool","optional":true,"computed":true},"require_symbols":{"type":"bool","optional":true,"computed":true},"require_uppercase_characters":{"type":"bool","optional":true,"computed":true}}}},"aws_iam_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"path":{"type":"string","optional":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_group_membership":{"version":0,"block":{"attributes":{"group":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"users":{"type":["set","string"],"required":true}}}},"aws_iam_group_policy":{"version":0,"block":{"attributes":{"group":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","required":true}}}},"aws_iam_group_policy_attachment":{"version":0,"block":{"attributes":{"group":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy_arn":{"type":"string","required":true}}}},"aws_iam_instance_profile":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"create_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","optional":true},"role":{"type":"string","optional":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_openid_connect_provider":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"client_id_list":{"type":["list","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"thumbprint_list":{"type":["list","string"],"required":true},"url":{"type":"string","required":true}}}},"aws_iam_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","optional":true},"policy":{"type":"string","required":true}}}},"aws_iam_policy_attachment":{"version":0,"block":{"attributes":{"groups":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"policy_arn":{"type":"string","required":true},"roles":{"type":["set","string"],"optional":true},"users":{"type":["set","string"],"optional":true}}}},"aws_iam_role":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"assume_role_policy":{"type":"string","required":true},"create_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"force_detach_policies":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"max_session_duration":{"type":"number","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","optional":true},"permissions_boundary":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_role_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","required":true},"role":{"type":"string","required":true}}}},"aws_iam_role_policy_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy_arn":{"type":"string","required":true},"role":{"type":"string","required":true}}}},"aws_iam_saml_provider":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"saml_metadata_document":{"type":"string","required":true},"valid_until":{"type":"string","computed":true}}}},"aws_iam_server_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"certificate_body":{"type":"string","required":true},"certificate_chain":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","optional":true},"private_key":{"type":"string","required":true,"sensitive":true}}}},"aws_iam_service_linked_role":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_service_name":{"type":"string","required":true},"create_date":{"type":"string","computed":true},"custom_suffix":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"path":{"type":"string","computed":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_user":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"force_destroy":{"type":"bool","description":"Delete user even if it has non-Terraform-managed IAM access keys, login profile or MFA devices","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"path":{"type":"string","optional":true},"permissions_boundary":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_user_group_membership":{"version":0,"block":{"attributes":{"groups":{"type":["set","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"user":{"type":"string","required":true}}}},"aws_iam_user_login_profile":{"version":0,"block":{"attributes":{"encrypted_password":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key_fingerprint":{"type":"string","computed":true},"password_length":{"type":"number","optional":true},"password_reset_required":{"type":"bool","optional":true},"pgp_key":{"type":"string","required":true},"user":{"type":"string","required":true}}}},"aws_iam_user_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","required":true},"user":{"type":"string","required":true}}}},"aws_iam_user_policy_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy_arn":{"type":"string","required":true},"user":{"type":"string","required":true}}}},"aws_iam_user_ssh_key":{"version":0,"block":{"attributes":{"encoding":{"type":"string","required":true},"fingerprint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"public_key":{"type":"string","required":true},"ssh_public_key_id":{"type":"string","computed":true},"status":{"type":"string","optional":true,"computed":true},"username":{"type":"string","required":true}}}},"aws_inspector_assessment_target":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"resource_group_arn":{"type":"string","optional":true}}}},"aws_inspector_assessment_template":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"duration":{"type":"number","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"rules_package_arns":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"target_arn":{"type":"string","required":true}}}},"aws_inspector_resource_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"required":true}}}},"aws_instance":{"version":1,"block":{"attributes":{"ami":{"type":"string","required":true},"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","optional":true,"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"cpu_core_count":{"type":"number","optional":true,"computed":true},"cpu_threads_per_core":{"type":"number","optional":true,"computed":true},"disable_api_termination":{"type":"bool","optional":true},"ebs_optimized":{"type":"bool","optional":true},"get_password_data":{"type":"bool","optional":true},"hibernation":{"type":"bool","optional":true},"host_id":{"type":"string","optional":true,"computed":true},"iam_instance_profile":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_initiated_shutdown_behavior":{"type":"string","optional":true},"instance_state":{"type":"string","computed":true},"instance_type":{"type":"string","required":true},"ipv6_address_count":{"type":"number","optional":true,"computed":true},"ipv6_addresses":{"type":["list","string"],"optional":true,"computed":true},"key_name":{"type":"string","optional":true,"computed":true},"monitoring":{"type":"bool","optional":true},"outpost_arn":{"type":"string","computed":true},"password_data":{"type":"string","computed":true},"placement_group":{"type":"string","optional":true,"computed":true},"primary_network_interface_id":{"type":"string","computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","optional":true,"computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"secondary_private_ips":{"type":["set","string"],"optional":true,"computed":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"source_dest_check":{"type":"bool","optional":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"tenancy":{"type":"string","optional":true,"computed":true},"user_data":{"type":"string","optional":true},"user_data_base64":{"type":"string","optional":true},"volume_tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"credit_specification":{"nesting_mode":"list","block":{"attributes":{"cpu_credits":{"type":"string","optional":true}}},"max_items":1},"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"no_device":{"type":"bool","optional":true},"virtual_name":{"type":"string","optional":true}}}},"metadata_options":{"nesting_mode":"list","block":{"attributes":{"http_endpoint":{"type":"string","optional":true,"computed":true},"http_put_response_hop_limit":{"type":"number","optional":true,"computed":true},"http_tokens":{"type":"string","optional":true,"computed":true}}},"max_items":1},"network_interface":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_index":{"type":"number","required":true},"network_interface_id":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"list","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","computed":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_internet_gateway":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","optional":true}}}},"aws_iot_certificate":{"version":0,"block":{"attributes":{"active":{"type":"bool","required":true},"arn":{"type":"string","computed":true},"certificate_pem":{"type":"string","computed":true,"sensitive":true},"csr":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","computed":true,"sensitive":true},"public_key":{"type":"string","computed":true,"sensitive":true}}}},"aws_iot_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"default_version_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"policy":{"type":"string","required":true}}}},"aws_iot_policy_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true},"target":{"type":"string","required":true}}}},"aws_iot_role_alias":{"version":0,"block":{"attributes":{"alias":{"type":"string","required":true},"arn":{"type":"string","computed":true},"credential_duration":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","required":true}}}},"aws_iot_thing":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"attributes":{"type":["map","string"],"optional":true},"default_client_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"thing_type_name":{"type":"string","optional":true},"version":{"type":"number","computed":true}}}},"aws_iot_thing_principal_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"principal":{"type":"string","required":true},"thing":{"type":"string","required":true}}}},"aws_iot_thing_type":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deprecated":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"properties":{"nesting_mode":"list","block":{"attributes":{"description":{"type":"string","optional":true},"searchable_attributes":{"type":["set","string"],"optional":true,"computed":true}}},"max_items":1}}}},"aws_iot_topic_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"enabled":{"type":"bool","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"sql":{"type":"string","required":true},"sql_version":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"cloudwatch_alarm":{"nesting_mode":"set","block":{"attributes":{"alarm_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"state_reason":{"type":"string","required":true},"state_value":{"type":"string","required":true}}}},"cloudwatch_metric":{"nesting_mode":"set","block":{"attributes":{"metric_name":{"type":"string","required":true},"metric_namespace":{"type":"string","required":true},"metric_timestamp":{"type":"string","optional":true},"metric_unit":{"type":"string","required":true},"metric_value":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}}},"dynamodb":{"nesting_mode":"set","block":{"attributes":{"hash_key_field":{"type":"string","required":true},"hash_key_type":{"type":"string","optional":true},"hash_key_value":{"type":"string","required":true},"operation":{"type":"string","optional":true},"payload_field":{"type":"string","optional":true},"range_key_field":{"type":"string","optional":true},"range_key_type":{"type":"string","optional":true},"range_key_value":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"table_name":{"type":"string","required":true}}}},"dynamodbv2":{"nesting_mode":"set","block":{"attributes":{"role_arn":{"type":"string","required":true}},"block_types":{"put_item":{"nesting_mode":"list","block":{"attributes":{"table_name":{"type":"string","required":true}}},"max_items":1}}}},"elasticsearch":{"nesting_mode":"set","block":{"attributes":{"endpoint":{"type":"string","required":true},"id":{"type":"string","required":true},"index":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"type":{"type":"string","required":true}}}},"error_action":{"nesting_mode":"list","block":{"block_types":{"cloudwatch_alarm":{"nesting_mode":"list","block":{"attributes":{"alarm_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"state_reason":{"type":"string","required":true},"state_value":{"type":"string","required":true}}},"max_items":1},"cloudwatch_metric":{"nesting_mode":"list","block":{"attributes":{"metric_name":{"type":"string","required":true},"metric_namespace":{"type":"string","required":true},"metric_timestamp":{"type":"string","optional":true},"metric_unit":{"type":"string","required":true},"metric_value":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"dynamodb":{"nesting_mode":"list","block":{"attributes":{"hash_key_field":{"type":"string","required":true},"hash_key_type":{"type":"string","optional":true},"hash_key_value":{"type":"string","required":true},"operation":{"type":"string","optional":true},"payload_field":{"type":"string","optional":true},"range_key_field":{"type":"string","optional":true},"range_key_type":{"type":"string","optional":true},"range_key_value":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"table_name":{"type":"string","required":true}}},"max_items":1},"dynamodbv2":{"nesting_mode":"list","block":{"attributes":{"role_arn":{"type":"string","required":true}},"block_types":{"put_item":{"nesting_mode":"list","block":{"attributes":{"table_name":{"type":"string","required":true}}},"max_items":1}}},"max_items":1},"elasticsearch":{"nesting_mode":"list","block":{"attributes":{"endpoint":{"type":"string","required":true},"id":{"type":"string","required":true},"index":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"type":{"type":"string","required":true}}},"max_items":1},"firehose":{"nesting_mode":"list","block":{"attributes":{"delivery_stream_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"separator":{"type":"string","optional":true}}},"max_items":1},"iot_analytics":{"nesting_mode":"list","block":{"attributes":{"channel_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"iot_events":{"nesting_mode":"list","block":{"attributes":{"input_name":{"type":"string","required":true},"message_id":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"kinesis":{"nesting_mode":"list","block":{"attributes":{"partition_key":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"stream_name":{"type":"string","required":true}}},"max_items":1},"lambda":{"nesting_mode":"list","block":{"attributes":{"function_arn":{"type":"string","required":true}}},"max_items":1},"republish":{"nesting_mode":"list","block":{"attributes":{"qos":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"topic":{"type":"string","required":true}}},"max_items":1},"s3":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","required":true},"key":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"sns":{"nesting_mode":"list","block":{"attributes":{"message_format":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"target_arn":{"type":"string","required":true}}},"max_items":1},"sqs":{"nesting_mode":"list","block":{"attributes":{"queue_url":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"use_base64":{"type":"bool","required":true}}},"max_items":1},"step_functions":{"nesting_mode":"list","block":{"attributes":{"execution_name_prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"state_machine_name":{"type":"string","required":true}}},"max_items":1}}},"max_items":1},"firehose":{"nesting_mode":"set","block":{"attributes":{"delivery_stream_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"separator":{"type":"string","optional":true}}}},"iot_analytics":{"nesting_mode":"set","block":{"attributes":{"channel_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}}},"iot_events":{"nesting_mode":"set","block":{"attributes":{"input_name":{"type":"string","required":true},"message_id":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}}}},"kinesis":{"nesting_mode":"set","block":{"attributes":{"partition_key":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"stream_name":{"type":"string","required":true}}}},"lambda":{"nesting_mode":"set","block":{"attributes":{"function_arn":{"type":"string","required":true}}}},"republish":{"nesting_mode":"set","block":{"attributes":{"qos":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"topic":{"type":"string","required":true}}}},"s3":{"nesting_mode":"set","block":{"attributes":{"bucket_name":{"type":"string","required":true},"key":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}}},"sns":{"nesting_mode":"set","block":{"attributes":{"message_format":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"target_arn":{"type":"string","required":true}}}},"sqs":{"nesting_mode":"set","block":{"attributes":{"queue_url":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"use_base64":{"type":"bool","required":true}}}},"step_functions":{"nesting_mode":"set","block":{"attributes":{"execution_name_prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"state_machine_name":{"type":"string","required":true}}}}}}},"aws_key_pair":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"fingerprint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key_name":{"type":"string","optional":true,"computed":true},"key_name_prefix":{"type":"string","optional":true},"key_pair_id":{"type":"string","computed":true},"public_key":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_kinesis_analytics_application":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"code":{"type":"string","optional":true},"create_timestamp":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"last_update_timestamp":{"type":"string","computed":true},"name":{"type":"string","required":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"number","computed":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","computed":true},"log_stream_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"inputs":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","computed":true},"name_prefix":{"type":"string","required":true},"starting_position_configuration":{"type":["list",["object",{"starting_position":"string"}]],"computed":true},"stream_names":{"type":["set","string"],"computed":true}},"block_types":{"kinesis_firehose":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"kinesis_stream":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"parallelism":{"nesting_mode":"list","block":{"attributes":{"count":{"type":"number","required":true}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"block_types":{"lambda":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"max_items":1},"schema":{"nesting_mode":"list","block":{"attributes":{"record_encoding":{"type":"string","optional":true}},"block_types":{"record_columns":{"nesting_mode":"list","block":{"attributes":{"mapping":{"type":"string","optional":true},"name":{"type":"string","required":true},"sql_type":{"type":"string","required":true}}},"min_items":1},"record_format":{"nesting_mode":"list","block":{"attributes":{"record_format_type":{"type":"string","computed":true}},"block_types":{"mapping_parameters":{"nesting_mode":"list","block":{"block_types":{"csv":{"nesting_mode":"list","block":{"attributes":{"record_column_delimiter":{"type":"string","required":true},"record_row_delimiter":{"type":"string","required":true}}},"max_items":1},"json":{"nesting_mode":"list","block":{"attributes":{"record_row_path":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1},"outputs":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","computed":true},"name":{"type":"string","required":true}},"block_types":{"kinesis_firehose":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"kinesis_stream":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"lambda":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"schema":{"nesting_mode":"list","block":{"attributes":{"record_format_type":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"max_items":3},"reference_data_sources":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","computed":true},"table_name":{"type":"string","required":true}},"block_types":{"s3":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"file_key":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1},"schema":{"nesting_mode":"list","block":{"attributes":{"record_encoding":{"type":"string","optional":true}},"block_types":{"record_columns":{"nesting_mode":"list","block":{"attributes":{"mapping":{"type":"string","optional":true},"name":{"type":"string","required":true},"sql_type":{"type":"string","required":true}}},"min_items":1},"record_format":{"nesting_mode":"list","block":{"attributes":{"record_format_type":{"type":"string","computed":true}},"block_types":{"mapping_parameters":{"nesting_mode":"list","block":{"block_types":{"csv":{"nesting_mode":"list","block":{"attributes":{"record_column_delimiter":{"type":"string","required":true},"record_row_delimiter":{"type":"string","required":true}}},"max_items":1},"json":{"nesting_mode":"list","block":{"attributes":{"record_row_path":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1}}}},"aws_kinesis_firehose_delivery_stream":{"version":1,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"destination":{"type":"string","required":true},"destination_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"version_id":{"type":"string","optional":true,"computed":true}},"block_types":{"elasticsearch_configuration":{"nesting_mode":"list","block":{"attributes":{"buffering_interval":{"type":"number","optional":true},"buffering_size":{"type":"number","optional":true},"domain_arn":{"type":"string","required":true},"index_name":{"type":"string","required":true},"index_rotation_period":{"type":"string","optional":true},"retry_duration":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"s3_backup_mode":{"type":"string","optional":true},"type_name":{"type":"string","optional":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"processors":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"list","block":{"attributes":{"parameter_name":{"type":"string","required":true},"parameter_value":{"type":"string","required":true}}}}}}}}},"max_items":1}}},"max_items":1},"extended_s3_configuration":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"buffer_interval":{"type":"number","optional":true},"buffer_size":{"type":"number","optional":true},"compression_format":{"type":"string","optional":true},"error_output_prefix":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"s3_backup_mode":{"type":"string","optional":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1},"data_format_conversion_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"input_format_configuration":{"nesting_mode":"list","block":{"block_types":{"deserializer":{"nesting_mode":"list","block":{"block_types":{"hive_json_ser_de":{"nesting_mode":"list","block":{"attributes":{"timestamp_formats":{"type":["list","string"],"optional":true}}},"max_items":1},"open_x_json_ser_de":{"nesting_mode":"list","block":{"attributes":{"case_insensitive":{"type":"bool","optional":true},"column_to_json_key_mappings":{"type":["map","string"],"optional":true},"convert_dots_in_json_keys_to_underscores":{"type":"bool","optional":true}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"output_format_configuration":{"nesting_mode":"list","block":{"block_types":{"serializer":{"nesting_mode":"list","block":{"block_types":{"orc_ser_de":{"nesting_mode":"list","block":{"attributes":{"block_size_bytes":{"type":"number","optional":true},"bloom_filter_columns":{"type":["list","string"],"optional":true},"bloom_filter_false_positive_probability":{"type":"number","optional":true},"compression":{"type":"string","optional":true},"dictionary_key_threshold":{"type":"number","optional":true},"enable_padding":{"type":"bool","optional":true},"format_version":{"type":"string","optional":true},"padding_tolerance":{"type":"number","optional":true},"row_index_stride":{"type":"number","optional":true},"stripe_size_bytes":{"type":"number","optional":true}}},"max_items":1},"parquet_ser_de":{"nesting_mode":"list","block":{"attributes":{"block_size_bytes":{"type":"number","optional":true},"compression":{"type":"string","optional":true},"enable_dictionary_compression":{"type":"bool","optional":true},"max_padding_bytes":{"type":"number","optional":true},"page_size_bytes":{"type":"number","optional":true},"writer_version":{"type":"string","optional":true}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"schema_configuration":{"nesting_mode":"list","block":{"attributes":{"catalog_id":{"type":"string","optional":true,"computed":true},"database_name":{"type":"string","required":true},"region":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","required":true},"table_name":{"type":"string","required":true},"version_id":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"processors":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"list","block":{"attributes":{"parameter_name":{"type":"string","required":true},"parameter_value":{"type":"string","required":true}}}}}}}}},"max_items":1},"s3_backup_configuration":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"buffer_interval":{"type":"number","optional":true},"buffer_size":{"type":"number","optional":true},"compression_format":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}},"max_items":1},"kinesis_source_configuration":{"nesting_mode":"list","block":{"attributes":{"kinesis_stream_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"redshift_configuration":{"nesting_mode":"list","block":{"attributes":{"cluster_jdbcurl":{"type":"string","required":true},"copy_options":{"type":"string","optional":true},"data_table_columns":{"type":"string","optional":true},"data_table_name":{"type":"string","required":true},"password":{"type":"string","required":true,"sensitive":true},"retry_duration":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"s3_backup_mode":{"type":"string","optional":true},"username":{"type":"string","required":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"processors":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"list","block":{"attributes":{"parameter_name":{"type":"string","required":true},"parameter_value":{"type":"string","required":true}}}}}}}}},"max_items":1},"s3_backup_configuration":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"buffer_interval":{"type":"number","optional":true},"buffer_size":{"type":"number","optional":true},"compression_format":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}},"max_items":1},"s3_configuration":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"buffer_interval":{"type":"number","optional":true},"buffer_size":{"type":"number","optional":true},"compression_format":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"server_side_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}}},"max_items":1},"splunk_configuration":{"nesting_mode":"list","block":{"attributes":{"hec_acknowledgment_timeout":{"type":"number","optional":true},"hec_endpoint":{"type":"string","required":true},"hec_endpoint_type":{"type":"string","optional":true},"hec_token":{"type":"string","required":true},"retry_duration":{"type":"number","optional":true},"s3_backup_mode":{"type":"string","optional":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"processors":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"list","block":{"attributes":{"parameter_name":{"type":"string","required":true},"parameter_value":{"type":"string","required":true}}}}}}}}},"max_items":1}}},"max_items":1}}}},"aws_kinesis_stream":{"version":1,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"encryption_type":{"type":"string","optional":true},"enforce_consumer_deletion":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"name":{"type":"string","required":true},"retention_period":{"type":"number","optional":true},"shard_count":{"type":"number","required":true},"shard_level_metrics":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_kinesis_video_stream":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_time":{"type":"string","computed":true},"data_retention_in_hours":{"type":"number","optional":true},"device_name":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"media_type":{"type":"string","optional":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_kms_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"name_prefix":{"type":"string","optional":true},"target_key_arn":{"type":"string","computed":true},"target_key_id":{"type":"string","required":true}}}},"aws_kms_ciphertext":{"version":0,"block":{"attributes":{"ciphertext_blob":{"type":"string","computed":true},"context":{"type":["map","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"plaintext":{"type":"string","required":true,"sensitive":true}}}},"aws_kms_external_key":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deletion_window_in_days":{"type":"number","optional":true},"description":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true,"computed":true},"expiration_model":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key_material_base64":{"type":"string","optional":true,"sensitive":true},"key_state":{"type":"string","computed":true},"key_usage":{"type":"string","computed":true},"policy":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"valid_to":{"type":"string","optional":true}}}},"aws_kms_grant":{"version":0,"block":{"attributes":{"grant_creation_tokens":{"type":["set","string"],"optional":true},"grant_id":{"type":"string","computed":true},"grant_token":{"type":"string","computed":true},"grantee_principal":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"name":{"type":"string","optional":true},"operations":{"type":["set","string"],"required":true},"retire_on_delete":{"type":"bool","optional":true},"retiring_principal":{"type":"string","optional":true}},"block_types":{"constraints":{"nesting_mode":"set","block":{"attributes":{"encryption_context_equals":{"type":["map","string"],"optional":true},"encryption_context_subset":{"type":["map","string"],"optional":true}}}}}}},"aws_kms_key":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_master_key_spec":{"type":"string","optional":true},"deletion_window_in_days":{"type":"number","optional":true},"description":{"type":"string","optional":true,"computed":true},"enable_key_rotation":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"is_enabled":{"type":"bool","optional":true},"key_id":{"type":"string","computed":true},"key_usage":{"type":"string","optional":true},"policy":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_lambda_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"function_name":{"type":"string","required":true},"function_version":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"invoke_arn":{"type":"string","computed":true},"name":{"type":"string","required":true}},"block_types":{"routing_config":{"nesting_mode":"list","block":{"attributes":{"additional_version_weights":{"type":["map","number"],"optional":true}}},"max_items":1}}}},"aws_lambda_event_source_mapping":{"version":0,"block":{"attributes":{"batch_size":{"type":"number","optional":true},"bisect_batch_on_function_error":{"type":"bool","optional":true},"enabled":{"type":"bool","optional":true},"event_source_arn":{"type":"string","required":true},"function_arn":{"type":"string","computed":true},"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"last_modified":{"type":"string","computed":true},"last_processing_result":{"type":"string","computed":true},"maximum_batching_window_in_seconds":{"type":"number","optional":true},"maximum_record_age_in_seconds":{"type":"number","optional":true,"computed":true},"maximum_retry_attempts":{"type":"number","optional":true,"computed":true},"parallelization_factor":{"type":"number","optional":true,"computed":true},"starting_position":{"type":"string","optional":true},"starting_position_timestamp":{"type":"string","optional":true},"state":{"type":"string","computed":true},"state_transition_reason":{"type":"string","computed":true},"uuid":{"type":"string","computed":true}},"block_types":{"destination_config":{"nesting_mode":"list","block":{"block_types":{"on_failure":{"nesting_mode":"list","block":{"attributes":{"destination_arn":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}}},"aws_lambda_function":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"filename":{"type":"string","optional":true},"function_name":{"type":"string","required":true},"handler":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"invoke_arn":{"type":"string","computed":true},"kms_key_arn":{"type":"string","optional":true},"last_modified":{"type":"string","computed":true},"layers":{"type":["list","string"],"optional":true},"memory_size":{"type":"number","optional":true},"publish":{"type":"bool","optional":true},"qualified_arn":{"type":"string","computed":true},"reserved_concurrent_executions":{"type":"number","optional":true},"role":{"type":"string","required":true},"runtime":{"type":"string","required":true},"s3_bucket":{"type":"string","optional":true},"s3_key":{"type":"string","optional":true},"s3_object_version":{"type":"string","optional":true},"source_code_hash":{"type":"string","optional":true,"computed":true},"source_code_size":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true},"timeout":{"type":"number","optional":true},"version":{"type":"string","computed":true}},"block_types":{"dead_letter_config":{"nesting_mode":"list","block":{"attributes":{"target_arn":{"type":"string","required":true}}},"max_items":1},"environment":{"nesting_mode":"list","block":{"attributes":{"variables":{"type":["map","string"],"optional":true}}},"max_items":1},"file_system_config":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true},"local_mount_path":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}},"tracing_config":{"nesting_mode":"list","block":{"attributes":{"mode":{"type":"string","required":true}}},"max_items":1},"vpc_config":{"nesting_mode":"list","block":{"attributes":{"security_group_ids":{"type":["set","string"],"required":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","computed":true}}},"max_items":1}}}},"aws_lambda_function_event_invoke_config":{"version":0,"block":{"attributes":{"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"maximum_event_age_in_seconds":{"type":"number","optional":true},"maximum_retry_attempts":{"type":"number","optional":true},"qualifier":{"type":"string","optional":true}},"block_types":{"destination_config":{"nesting_mode":"list","block":{"block_types":{"on_failure":{"nesting_mode":"list","block":{"attributes":{"destination":{"type":"string","required":true}}},"max_items":1},"on_success":{"nesting_mode":"list","block":{"attributes":{"destination":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}}},"aws_lambda_layer_version":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compatible_runtimes":{"type":["set","string"],"optional":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"filename":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"layer_arn":{"type":"string","computed":true},"layer_name":{"type":"string","required":true},"license_info":{"type":"string","optional":true},"s3_bucket":{"type":"string","optional":true},"s3_key":{"type":"string","optional":true},"s3_object_version":{"type":"string","optional":true},"source_code_hash":{"type":"string","optional":true,"computed":true},"source_code_size":{"type":"number","computed":true},"version":{"type":"string","computed":true}}}},"aws_lambda_permission":{"version":0,"block":{"attributes":{"action":{"type":"string","required":true},"event_source_token":{"type":"string","optional":true},"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"principal":{"type":"string","required":true},"qualifier":{"type":"string","optional":true},"source_account":{"type":"string","optional":true},"source_arn":{"type":"string","optional":true},"statement_id":{"type":"string","optional":true,"computed":true},"statement_id_prefix":{"type":"string","optional":true}}}},"aws_lambda_provisioned_concurrency_config":{"version":0,"block":{"attributes":{"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"provisioned_concurrent_executions":{"type":"number","required":true},"qualifier":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_launch_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","optional":true},"ebs_optimized":{"type":"bool","optional":true,"computed":true},"enable_monitoring":{"type":"bool","optional":true},"iam_instance_profile":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","required":true},"instance_type":{"type":"string","required":true},"key_name":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"placement_tenancy":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true},"spot_price":{"type":"string","optional":true},"user_data":{"type":"string","optional":true},"user_data_base64":{"type":"string","optional":true},"vpc_classic_link_id":{"type":"string","optional":true},"vpc_classic_link_security_groups":{"type":["set","string"],"optional":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"no_device":{"type":"bool","optional":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"virtual_name":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"list","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1}}}},"aws_launch_template":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"default_version":{"type":"number","optional":true,"computed":true},"description":{"type":"string","optional":true},"disable_api_termination":{"type":"bool","optional":true},"ebs_optimized":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","optional":true},"instance_initiated_shutdown_behavior":{"type":"string","optional":true},"instance_type":{"type":"string","optional":true},"kernel_id":{"type":"string","optional":true},"key_name":{"type":"string","optional":true},"latest_version":{"type":"number","computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"ram_disk_id":{"type":"string","optional":true},"security_group_names":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"update_default_version":{"type":"bool","optional":true},"user_data":{"type":"string","optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true}},"block_types":{"block_device_mappings":{"nesting_mode":"list","block":{"attributes":{"device_name":{"type":"string","optional":true},"no_device":{"type":"string","optional":true},"virtual_name":{"type":"string","optional":true}},"block_types":{"ebs":{"nesting_mode":"list","block":{"attributes":{"delete_on_termination":{"type":"string","optional":true},"encrypted":{"type":"string","optional":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"snapshot_id":{"type":"string","optional":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1}}}},"capacity_reservation_specification":{"nesting_mode":"list","block":{"attributes":{"capacity_reservation_preference":{"type":"string","optional":true}},"block_types":{"capacity_reservation_target":{"nesting_mode":"list","block":{"attributes":{"capacity_reservation_id":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"cpu_options":{"nesting_mode":"list","block":{"attributes":{"core_count":{"type":"number","optional":true},"threads_per_core":{"type":"number","optional":true}}},"max_items":1},"credit_specification":{"nesting_mode":"list","block":{"attributes":{"cpu_credits":{"type":"string","optional":true}}},"max_items":1},"elastic_gpu_specifications":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}}},"elastic_inference_accelerator":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1},"hibernation_options":{"nesting_mode":"list","block":{"attributes":{"configured":{"type":"bool","required":true}}},"max_items":1},"iam_instance_profile":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","optional":true},"name":{"type":"string","optional":true}}},"max_items":1},"instance_market_options":{"nesting_mode":"list","block":{"attributes":{"market_type":{"type":"string","optional":true}},"block_types":{"spot_options":{"nesting_mode":"list","block":{"attributes":{"block_duration_minutes":{"type":"number","optional":true},"instance_interruption_behavior":{"type":"string","optional":true},"max_price":{"type":"string","optional":true},"spot_instance_type":{"type":"string","optional":true},"valid_until":{"type":"string","optional":true,"computed":true}}},"max_items":1}}},"max_items":1},"license_specification":{"nesting_mode":"set","block":{"attributes":{"license_configuration_arn":{"type":"string","required":true}}}},"metadata_options":{"nesting_mode":"list","block":{"attributes":{"http_endpoint":{"type":"string","optional":true,"computed":true},"http_put_response_hop_limit":{"type":"number","optional":true,"computed":true},"http_tokens":{"type":"string","optional":true,"computed":true}}},"max_items":1},"monitoring":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}}},"max_items":1},"network_interfaces":{"nesting_mode":"list","block":{"attributes":{"associate_public_ip_address":{"type":"string","optional":true},"delete_on_termination":{"type":"string","optional":true},"description":{"type":"string","optional":true},"device_index":{"type":"number","optional":true},"ipv4_address_count":{"type":"number","optional":true},"ipv4_addresses":{"type":["set","string"],"optional":true},"ipv6_address_count":{"type":"number","optional":true},"ipv6_addresses":{"type":["set","string"],"optional":true},"network_interface_id":{"type":"string","optional":true},"private_ip_address":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true},"subnet_id":{"type":"string","optional":true}}}},"placement":{"nesting_mode":"list","block":{"attributes":{"affinity":{"type":"string","optional":true},"availability_zone":{"type":"string","optional":true},"group_name":{"type":"string","optional":true},"host_id":{"type":"string","optional":true},"partition_number":{"type":"number","optional":true},"spread_domain":{"type":"string","optional":true},"tenancy":{"type":"string","optional":true}}},"max_items":1},"tag_specifications":{"nesting_mode":"list","block":{"attributes":{"resource_type":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}}}}},"aws_lb":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"arn_suffix":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"drop_invalid_header_fields":{"type":"bool","optional":true},"enable_cross_zone_load_balancing":{"type":"bool","optional":true},"enable_deletion_protection":{"type":"bool","optional":true},"enable_http2":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","optional":true},"internal":{"type":"bool","optional":true,"computed":true},"ip_address_type":{"type":"string","optional":true,"computed":true},"load_balancer_type":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"subnets":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"zone_id":{"type":"string","computed":true}},"block_types":{"access_logs":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"prefix":{"type":"string","optional":true}}},"max_items":1},"subnet_mapping":{"nesting_mode":"set","block":{"attributes":{"allocation_id":{"type":"string","optional":true},"private_ipv4_address":{"type":"string","optional":true},"subnet_id":{"type":"string","required":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_lb_cookie_stickiness_policy":{"version":0,"block":{"attributes":{"cookie_expiration_period":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"lb_port":{"type":"number","required":true},"load_balancer":{"type":"string","required":true},"name":{"type":"string","required":true}}}},"aws_lb_listener":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"load_balancer_arn":{"type":"string","required":true},"port":{"type":"number","required":true},"protocol":{"type":"string","optional":true},"ssl_policy":{"type":"string","optional":true,"computed":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"attributes":{"order":{"type":"number","optional":true,"computed":true},"target_group_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"authenticate_cognito":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"user_pool_arn":{"type":"string","required":true},"user_pool_client_id":{"type":"string","required":true},"user_pool_domain":{"type":"string","required":true}}},"max_items":1},"authenticate_oidc":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"authorization_endpoint":{"type":"string","required":true},"client_id":{"type":"string","required":true},"client_secret":{"type":"string","required":true,"sensitive":true},"issuer":{"type":"string","required":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"token_endpoint":{"type":"string","required":true},"user_info_endpoint":{"type":"string","required":true}}},"max_items":1},"fixed_response":{"nesting_mode":"list","block":{"attributes":{"content_type":{"type":"string","required":true},"message_body":{"type":"string","optional":true},"status_code":{"type":"string","optional":true,"computed":true}}},"max_items":1},"forward":{"nesting_mode":"list","block":{"block_types":{"stickiness":{"nesting_mode":"list","block":{"attributes":{"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1},"target_group":{"nesting_mode":"set","block":{"attributes":{"arn":{"type":"string","required":true},"weight":{"type":"number","optional":true}}},"min_items":2,"max_items":5}}},"max_items":1},"redirect":{"nesting_mode":"list","block":{"attributes":{"host":{"type":"string","optional":true},"path":{"type":"string","optional":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"query":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"read":{"type":"string","optional":true}}}}}}},"aws_lb_listener_certificate":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true}}}},"aws_lb_listener_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true},"priority":{"type":"number","optional":true,"computed":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"order":{"type":"number","optional":true,"computed":true},"target_group_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"authenticate_cognito":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"user_pool_arn":{"type":"string","required":true},"user_pool_client_id":{"type":"string","required":true},"user_pool_domain":{"type":"string","required":true}}},"max_items":1},"authenticate_oidc":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"authorization_endpoint":{"type":"string","required":true},"client_id":{"type":"string","required":true},"client_secret":{"type":"string","required":true,"sensitive":true},"issuer":{"type":"string","required":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"token_endpoint":{"type":"string","required":true},"user_info_endpoint":{"type":"string","required":true}}},"max_items":1},"fixed_response":{"nesting_mode":"list","block":{"attributes":{"content_type":{"type":"string","required":true},"message_body":{"type":"string","optional":true},"status_code":{"type":"string","optional":true,"computed":true}}},"max_items":1},"forward":{"nesting_mode":"list","block":{"block_types":{"stickiness":{"nesting_mode":"list","block":{"attributes":{"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1},"target_group":{"nesting_mode":"set","block":{"attributes":{"arn":{"type":"string","required":true},"weight":{"type":"number","optional":true}}},"min_items":2,"max_items":5}}},"max_items":1},"redirect":{"nesting_mode":"list","block":{"attributes":{"host":{"type":"string","optional":true},"path":{"type":"string","optional":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"query":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"condition":{"nesting_mode":"set","block":{"block_types":{"host_header":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"http_header":{"nesting_mode":"list","block":{"attributes":{"http_header_name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}},"max_items":1},"http_request_method":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"path_pattern":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"query_string":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"value":{"type":"string","required":true}}}},"source_ip":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1}}},"min_items":1}}}},"aws_lb_ssl_negotiation_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"lb_port":{"type":"number","required":true},"load_balancer":{"type":"string","required":true},"name":{"type":"string","required":true}},"block_types":{"attribute":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_lb_target_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"arn_suffix":{"type":"string","computed":true},"deregistration_delay":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"lambda_multi_value_headers_enabled":{"type":"bool","optional":true},"load_balancing_algorithm_type":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"port":{"type":"number","optional":true},"protocol":{"type":"string","optional":true},"proxy_protocol_v2":{"type":"bool","optional":true},"slow_start":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true},"target_type":{"type":"string","optional":true},"vpc_id":{"type":"string","optional":true}},"block_types":{"health_check":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"healthy_threshold":{"type":"number","optional":true},"interval":{"type":"number","optional":true},"matcher":{"type":"string","optional":true,"computed":true},"path":{"type":"string","optional":true,"computed":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"timeout":{"type":"number","optional":true,"computed":true},"unhealthy_threshold":{"type":"number","optional":true}}},"max_items":1},"stickiness":{"nesting_mode":"list","block":{"attributes":{"cookie_duration":{"type":"number","optional":true},"enabled":{"type":"bool","optional":true},"type":{"type":"string","required":true}}},"max_items":1}}}},"aws_lb_target_group_attachment":{"version":0,"block":{"attributes":{"availability_zone":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"target_group_arn":{"type":"string","required":true},"target_id":{"type":"string","required":true}}}},"aws_licensemanager_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"license_configuration_arn":{"type":"string","required":true},"resource_arn":{"type":"string","required":true}}}},"aws_licensemanager_license_configuration":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"license_count":{"type":"number","optional":true},"license_count_hard_limit":{"type":"bool","optional":true},"license_counting_type":{"type":"string","required":true},"license_rules":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_lightsail_domain":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_lightsail_instance":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","required":true},"blueprint_id":{"type":"string","required":true},"bundle_id":{"type":"string","required":true},"cpu_count":{"type":"number","computed":true},"created_at":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_address":{"type":"string","computed":true},"is_static_ip":{"type":"bool","computed":true},"key_pair_name":{"type":"string","optional":true},"name":{"type":"string","required":true},"private_ip_address":{"type":"string","computed":true},"public_ip_address":{"type":"string","computed":true},"ram_size":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true},"user_data":{"type":"string","optional":true},"username":{"type":"string","computed":true}}}},"aws_lightsail_key_pair":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"encrypted_fingerprint":{"type":"string","computed":true},"encrypted_private_key":{"type":"string","computed":true},"fingerprint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"pgp_key":{"type":"string","optional":true},"private_key":{"type":"string","computed":true},"public_key":{"type":"string","optional":true,"computed":true}}}},"aws_lightsail_static_ip":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","computed":true},"name":{"type":"string","required":true},"support_code":{"type":"string","computed":true}}}},"aws_lightsail_static_ip_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_name":{"type":"string","required":true},"ip_address":{"type":"string","computed":true},"static_ip_name":{"type":"string","required":true}}}},"aws_load_balancer_backend_server_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_port":{"type":"number","required":true},"load_balancer_name":{"type":"string","required":true},"policy_names":{"type":["set","string"],"optional":true}}}},"aws_load_balancer_listener_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"load_balancer_name":{"type":"string","required":true},"load_balancer_port":{"type":"number","required":true},"policy_names":{"type":["set","string"],"optional":true}}}},"aws_load_balancer_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"load_balancer_name":{"type":"string","required":true},"policy_name":{"type":"string","required":true},"policy_type_name":{"type":"string","required":true}},"block_types":{"policy_attribute":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","optional":true},"value":{"type":"string","optional":true}}}}}}},"aws_macie_member_account_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"member_account_id":{"type":"string","required":true}}}},"aws_macie_s3_bucket_association":{"version":0,"block":{"attributes":{"bucket_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"member_account_id":{"type":"string","optional":true},"prefix":{"type":"string","optional":true}},"block_types":{"classification_type":{"nesting_mode":"list","block":{"attributes":{"continuous":{"type":"string","optional":true},"one_time":{"type":"string","optional":true}}},"max_items":1}}}},"aws_main_route_table_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"original_route_table_id":{"type":"string","computed":true},"route_table_id":{"type":"string","required":true},"vpc_id":{"type":"string","required":true}}}},"aws_media_convert_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"pricing_plan":{"type":"string","optional":true},"status":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"reservation_plan_settings":{"nesting_mode":"list","block":{"attributes":{"commitment":{"type":"string","required":true},"renewal_type":{"type":"string","required":true},"reserved_slots":{"type":"number","required":true}}},"max_items":1}}}},"aws_media_package_channel":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"channel_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"hls_ingest":{"type":["list",["object",{"ingest_endpoints":["list",["object",{"password":"string","url":"string","username":"string"}]]}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_media_store_container":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_media_store_container_policy":{"version":0,"block":{"attributes":{"container_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true}}}},"aws_mq_broker":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"broker_name":{"type":"string","required":true},"deployment_mode":{"type":"string","optional":true},"engine_type":{"type":"string","required":true},"engine_version":{"type":"string","required":true},"host_instance_type":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instances":{"type":["list",["object",{"console_url":"string","endpoints":["list","string"],"ip_address":"string"}]],"computed":true},"publicly_accessible":{"type":"bool","optional":true},"security_groups":{"type":["set","string"],"required":true},"subnet_ids":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"configuration":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"revision":{"type":"number","optional":true,"computed":true}}},"max_items":1},"encryption_options":{"nesting_mode":"list","block":{"attributes":{"kms_key_id":{"type":"string","optional":true,"computed":true},"use_aws_owned_key":{"type":"bool","optional":true}}},"max_items":1},"logs":{"nesting_mode":"list","block":{"attributes":{"audit":{"type":"bool","optional":true},"general":{"type":"bool","optional":true}}},"max_items":1},"maintenance_window_start_time":{"nesting_mode":"list","block":{"attributes":{"day_of_week":{"type":"string","required":true},"time_of_day":{"type":"string","required":true},"time_zone":{"type":"string","required":true}}},"max_items":1},"user":{"nesting_mode":"set","block":{"attributes":{"console_access":{"type":"bool","optional":true},"groups":{"type":["set","string"],"optional":true},"password":{"type":"string","required":true,"sensitive":true},"username":{"type":"string","required":true}}},"min_items":1}}}},"aws_mq_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"data":{"type":"string","required":true},"description":{"type":"string","optional":true},"engine_type":{"type":"string","required":true},"engine_version":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"latest_revision":{"type":"number","computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_msk_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bootstrap_brokers":{"type":"string","computed":true},"bootstrap_brokers_tls":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"current_version":{"type":"string","computed":true},"enhanced_monitoring":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"kafka_version":{"type":"string","required":true},"number_of_broker_nodes":{"type":"number","required":true},"tags":{"type":["map","string"],"optional":true},"zookeeper_connect_string":{"type":"string","computed":true}},"block_types":{"broker_node_group_info":{"nesting_mode":"list","block":{"attributes":{"az_distribution":{"type":"string","optional":true},"client_subnets":{"type":["list","string"],"required":true},"ebs_volume_size":{"type":"number","required":true},"instance_type":{"type":"string","required":true},"security_groups":{"type":["list","string"],"required":true}}},"min_items":1,"max_items":1},"client_authentication":{"nesting_mode":"list","block":{"block_types":{"tls":{"nesting_mode":"list","block":{"attributes":{"certificate_authority_arns":{"type":["set","string"],"optional":true}}},"max_items":1}}},"max_items":1},"configuration_info":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true},"revision":{"type":"number","required":true}}},"max_items":1},"encryption_info":{"nesting_mode":"list","block":{"attributes":{"encryption_at_rest_kms_key_arn":{"type":"string","optional":true,"computed":true}},"block_types":{"encryption_in_transit":{"nesting_mode":"list","block":{"attributes":{"client_broker":{"type":"string","optional":true},"in_cluster":{"type":"bool","optional":true}}},"max_items":1}}},"max_items":1},"logging_info":{"nesting_mode":"list","block":{"block_types":{"broker_logs":{"nesting_mode":"list","block":{"block_types":{"cloudwatch_logs":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true},"log_group":{"type":"string","optional":true}}},"max_items":1},"firehose":{"nesting_mode":"list","block":{"attributes":{"delivery_stream":{"type":"string","optional":true},"enabled":{"type":"bool","required":true}}},"max_items":1},"s3":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","optional":true},"enabled":{"type":"bool","required":true},"prefix":{"type":"string","optional":true}}},"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1},"open_monitoring":{"nesting_mode":"list","block":{"block_types":{"prometheus":{"nesting_mode":"list","block":{"block_types":{"jmx_exporter":{"nesting_mode":"list","block":{"attributes":{"enabled_in_broker":{"type":"bool","required":true}}},"max_items":1},"node_exporter":{"nesting_mode":"list","block":{"attributes":{"enabled_in_broker":{"type":"bool","required":true}}},"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1}}}},"aws_msk_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"kafka_versions":{"type":["set","string"],"required":true},"latest_revision":{"type":"number","computed":true},"name":{"type":"string","required":true},"server_properties":{"type":"string","required":true}}}},"aws_nat_gateway":{"version":0,"block":{"attributes":{"allocation_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"subnet_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_neptune_cluster":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"backup_retention_period":{"type":"number","optional":true},"cluster_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier_prefix":{"type":"string","optional":true,"computed":true},"cluster_members":{"type":["set","string"],"computed":true},"cluster_resource_id":{"type":"string","computed":true},"deletion_protection":{"type":"bool","optional":true},"enable_cloudwatch_logs_exports":{"type":["set","string"],"optional":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"hosted_zone_id":{"type":"string","computed":true},"iam_database_authentication_enabled":{"type":"bool","optional":true},"iam_roles":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true,"computed":true},"neptune_cluster_parameter_group_name":{"type":"string","optional":true},"neptune_subnet_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"reader_endpoint":{"type":"string","computed":true},"replication_source_identifier":{"type":"string","optional":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_identifier":{"type":"string","optional":true},"storage_encrypted":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_neptune_cluster_instance":{"version":0,"block":{"attributes":{"address":{"type":"string","computed":true},"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"cluster_identifier":{"type":"string","required":true},"dbi_resource_id":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"instance_class":{"type":"string","required":true},"kms_key_arn":{"type":"string","computed":true},"neptune_parameter_group_name":{"type":"string","optional":true},"neptune_subnet_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"promotion_tier":{"type":"number","optional":true},"publicly_accessible":{"type":"bool","optional":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true},"writer":{"type":"bool","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_neptune_cluster_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_neptune_cluster_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zones":{"type":["list","string"],"computed":true},"db_cluster_identifier":{"type":"string","required":true},"db_cluster_snapshot_arn":{"type":"string","computed":true},"db_cluster_snapshot_identifier":{"type":"string","required":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_type":{"type":"string","computed":true},"source_db_cluster_snapshot_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_neptune_event_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_aws_id":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"event_categories":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"sns_topic_arn":{"type":"string","required":true},"source_ids":{"type":["set","string"],"optional":true},"source_type":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_neptune_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_neptune_subnet_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_network_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"egress":{"type":["set",["object",{"action":"string","cidr_block":"string","from_port":"number","icmp_code":"number","icmp_type":"number","ipv6_cidr_block":"string","protocol":"string","rule_no":"number","to_port":"number"}]],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ingress":{"type":["set",["object",{"action":"string","cidr_block":"string","from_port":"number","icmp_code":"number","icmp_type":"number","ipv6_cidr_block":"string","protocol":"string","rule_no":"number","to_port":"number"}]],"optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}}}},"aws_network_acl_rule":{"version":0,"block":{"attributes":{"cidr_block":{"type":"string","optional":true},"egress":{"type":"bool","optional":true},"from_port":{"type":"number","optional":true},"icmp_code":{"type":"string","optional":true},"icmp_type":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block":{"type":"string","optional":true},"network_acl_id":{"type":"string","required":true},"protocol":{"type":"string","required":true},"rule_action":{"type":"string","required":true},"rule_number":{"type":"number","required":true},"to_port":{"type":"number","optional":true}}}},"aws_network_interface":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"mac_address":{"type":"string","computed":true},"outpost_arn":{"type":"string","computed":true},"private_dns_name":{"type":"string","computed":true},"private_ip":{"type":"string","optional":true,"computed":true},"private_ips":{"type":["set","string"],"optional":true,"computed":true},"private_ips_count":{"type":"number","optional":true,"computed":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"source_dest_check":{"type":"bool","optional":true},"subnet_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"attachment":{"nesting_mode":"set","block":{"attributes":{"attachment_id":{"type":"string","computed":true},"device_index":{"type":"number","required":true},"instance":{"type":"string","required":true}}}}}}},"aws_network_interface_attachment":{"version":0,"block":{"attributes":{"attachment_id":{"type":"string","computed":true},"device_index":{"type":"number","required":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","required":true},"network_interface_id":{"type":"string","required":true},"status":{"type":"string","computed":true}}}},"aws_network_interface_sg_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","required":true},"security_group_id":{"type":"string","required":true}}}},"aws_opsworks_application":{"version":0,"block":{"attributes":{"auto_bundle_on_deploy":{"type":"string","optional":true},"aws_flow_ruby_settings":{"type":"string","optional":true},"data_source_arn":{"type":"string","optional":true},"data_source_database_name":{"type":"string","optional":true},"data_source_type":{"type":"string","optional":true},"description":{"type":"string","optional":true},"document_root":{"type":"string","optional":true},"domains":{"type":["list","string"],"optional":true},"enable_ssl":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"rails_env":{"type":"string","optional":true},"short_name":{"type":"string","optional":true,"computed":true},"stack_id":{"type":"string","required":true},"type":{"type":"string","required":true}},"block_types":{"app_source":{"nesting_mode":"list","block":{"attributes":{"password":{"type":"string","optional":true,"sensitive":true},"revision":{"type":"string","optional":true},"ssh_key":{"type":"string","optional":true,"sensitive":true},"type":{"type":"string","required":true},"url":{"type":"string","optional":true},"username":{"type":"string","optional":true}}}},"environment":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","required":true},"secure":{"type":"bool","optional":true},"value":{"type":"string","required":true}}}},"ssl_configuration":{"nesting_mode":"list","block":{"attributes":{"certificate":{"type":"string","required":true},"chain":{"type":"string","optional":true},"private_key":{"type":"string","required":true,"sensitive":true}}}}}}},"aws_opsworks_custom_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","required":true},"short_name":{"type":"string","required":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_ganglia_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"password":{"type":"string","required":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"url":{"type":"string","optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true},"username":{"type":"string","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_haproxy_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"healthcheck_method":{"type":"string","optional":true},"healthcheck_url":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"stats_enabled":{"type":"bool","optional":true},"stats_password":{"type":"string","required":true},"stats_url":{"type":"string","optional":true},"stats_user":{"type":"string","optional":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_instance":{"version":0,"block":{"attributes":{"agent_version":{"type":"string","optional":true},"ami_id":{"type":"string","optional":true,"computed":true},"architecture":{"type":"string","optional":true},"auto_scaling_type":{"type":"string","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"created_at":{"type":"string","optional":true,"computed":true},"delete_ebs":{"type":"bool","optional":true},"delete_eip":{"type":"bool","optional":true},"ebs_optimized":{"type":"bool","optional":true},"ec2_instance_id":{"type":"string","computed":true},"ecs_cluster_arn":{"type":"string","optional":true,"computed":true},"elastic_ip":{"type":"string","optional":true,"computed":true},"hostname":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"infrastructure_class":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_profile_arn":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","optional":true},"last_service_error_id":{"type":"string","optional":true,"computed":true},"layer_ids":{"type":["list","string"],"required":true},"os":{"type":"string","optional":true,"computed":true},"platform":{"type":"string","optional":true,"computed":true},"private_dns":{"type":"string","optional":true,"computed":true},"private_ip":{"type":"string","optional":true,"computed":true},"public_dns":{"type":"string","optional":true,"computed":true},"public_ip":{"type":"string","optional":true,"computed":true},"registered_by":{"type":"string","optional":true,"computed":true},"reported_agent_version":{"type":"string","optional":true,"computed":true},"reported_os_family":{"type":"string","optional":true,"computed":true},"reported_os_name":{"type":"string","optional":true,"computed":true},"reported_os_version":{"type":"string","optional":true,"computed":true},"root_device_type":{"type":"string","optional":true,"computed":true},"root_device_volume_id":{"type":"string","optional":true,"computed":true},"security_group_ids":{"type":["list","string"],"optional":true,"computed":true},"ssh_host_dsa_key_fingerprint":{"type":"string","optional":true,"computed":true},"ssh_host_rsa_key_fingerprint":{"type":"string","optional":true,"computed":true},"ssh_key_name":{"type":"string","optional":true,"computed":true},"stack_id":{"type":"string","required":true},"state":{"type":"string","optional":true},"status":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tenancy":{"type":"string","optional":true,"computed":true},"virtualization_type":{"type":"string","optional":true,"computed":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"iops":{"type":"number","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"virtual_name":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"iops":{"type":"number","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_opsworks_java_app_layer":{"version":0,"block":{"attributes":{"app_server":{"type":"string","optional":true},"app_server_version":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"jvm_options":{"type":"string","optional":true},"jvm_type":{"type":"string","optional":true},"jvm_version":{"type":"string","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_memcached_layer":{"version":0,"block":{"attributes":{"allocated_memory":{"type":"number","optional":true},"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_mysql_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"root_password":{"type":"string","optional":true},"root_password_on_all_instances":{"type":"bool","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_nodejs_app_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"nodejs_version":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_permission":{"version":0,"block":{"attributes":{"allow_ssh":{"type":"bool","optional":true,"computed":true},"allow_sudo":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"level":{"type":"string","optional":true,"computed":true},"stack_id":{"type":"string","optional":true,"computed":true},"user_arn":{"type":"string","required":true}}}},"aws_opsworks_php_app_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_rails_app_layer":{"version":0,"block":{"attributes":{"app_server":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"bundler_version":{"type":"string","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"manage_bundler":{"type":"bool","optional":true},"name":{"type":"string","optional":true},"passenger_version":{"type":"string","optional":true},"ruby_version":{"type":"string","optional":true},"rubygems_version":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_rds_db_instance":{"version":0,"block":{"attributes":{"db_password":{"type":"string","required":true,"sensitive":true},"db_user":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"rds_db_instance_arn":{"type":"string","required":true},"stack_id":{"type":"string","required":true}}}},"aws_opsworks_stack":{"version":0,"block":{"attributes":{"agent_version":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"berkshelf_version":{"type":"string","optional":true},"color":{"type":"string","optional":true},"configuration_manager_name":{"type":"string","optional":true},"configuration_manager_version":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"default_availability_zone":{"type":"string","optional":true,"computed":true},"default_instance_profile_arn":{"type":"string","required":true},"default_os":{"type":"string","optional":true},"default_root_device_type":{"type":"string","optional":true},"default_ssh_key_name":{"type":"string","optional":true},"default_subnet_id":{"type":"string","optional":true,"computed":true},"hostname_theme":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"manage_berkshelf":{"type":"bool","optional":true},"name":{"type":"string","required":true},"region":{"type":"string","required":true},"service_role_arn":{"type":"string","required":true},"stack_endpoint":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"use_custom_cookbooks":{"type":"bool","optional":true},"use_opsworks_security_groups":{"type":"bool","optional":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"custom_cookbooks_source":{"nesting_mode":"list","block":{"attributes":{"password":{"type":"string","optional":true,"sensitive":true},"revision":{"type":"string","optional":true},"ssh_key":{"type":"string","optional":true,"sensitive":true},"type":{"type":"string","required":true},"url":{"type":"string","required":true},"username":{"type":"string","optional":true}}}}}}},"aws_opsworks_static_web_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_user_profile":{"version":0,"block":{"attributes":{"allow_self_management":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"ssh_public_key":{"type":"string","optional":true},"ssh_username":{"type":"string","required":true},"user_arn":{"type":"string","required":true}}}},"aws_organizations_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"email":{"type":"string","required":true},"iam_user_access_to_billing":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"joined_method":{"type":"string","computed":true},"joined_timestamp":{"type":"string","computed":true},"name":{"type":"string","required":true},"parent_id":{"type":"string","optional":true,"computed":true},"role_name":{"type":"string","optional":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_organizations_organization":{"version":0,"block":{"attributes":{"accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string","status":"string"}]],"computed":true},"arn":{"type":"string","computed":true},"aws_service_access_principals":{"type":["set","string"],"optional":true},"enabled_policy_types":{"type":["set","string"],"optional":true},"feature_set":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"master_account_arn":{"type":"string","computed":true},"master_account_email":{"type":"string","computed":true},"master_account_id":{"type":"string","computed":true},"non_master_accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string","status":"string"}]],"computed":true},"roots":{"type":["list",["object",{"arn":"string","id":"string","name":"string","policy_types":["list",["object",{"status":"string","type":"string"}]]}]],"computed":true}}}},"aws_organizations_organizational_unit":{"version":0,"block":{"attributes":{"accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string"}]],"computed":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"parent_id":{"type":"string","required":true}}}},"aws_organizations_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"content":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"type":{"type":"string","optional":true}}}},"aws_organizations_policy_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy_id":{"type":"string","required":true},"target_id":{"type":"string","required":true}}}},"aws_pinpoint_adm_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"client_id":{"type":"string","required":true,"sensitive":true},"client_secret":{"type":"string","required":true,"sensitive":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_pinpoint_apns_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"bundle_id":{"type":"string","optional":true,"sensitive":true},"certificate":{"type":"string","optional":true,"sensitive":true},"default_authentication_method":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"team_id":{"type":"string","optional":true,"sensitive":true},"token_key":{"type":"string","optional":true,"sensitive":true},"token_key_id":{"type":"string","optional":true,"sensitive":true}}}},"aws_pinpoint_apns_sandbox_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"bundle_id":{"type":"string","optional":true,"sensitive":true},"certificate":{"type":"string","optional":true,"sensitive":true},"default_authentication_method":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"team_id":{"type":"string","optional":true,"sensitive":true},"token_key":{"type":"string","optional":true,"sensitive":true},"token_key_id":{"type":"string","optional":true,"sensitive":true}}}},"aws_pinpoint_apns_voip_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"bundle_id":{"type":"string","optional":true,"sensitive":true},"certificate":{"type":"string","optional":true,"sensitive":true},"default_authentication_method":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"team_id":{"type":"string","optional":true,"sensitive":true},"token_key":{"type":"string","optional":true,"sensitive":true},"token_key_id":{"type":"string","optional":true,"sensitive":true}}}},"aws_pinpoint_apns_voip_sandbox_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"bundle_id":{"type":"string","optional":true,"sensitive":true},"certificate":{"type":"string","optional":true,"sensitive":true},"default_authentication_method":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"team_id":{"type":"string","optional":true,"sensitive":true},"token_key":{"type":"string","optional":true,"sensitive":true},"token_key_id":{"type":"string","optional":true,"sensitive":true}}}},"aws_pinpoint_app":{"version":0,"block":{"attributes":{"application_id":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"campaign_hook":{"nesting_mode":"list","block":{"attributes":{"lambda_function_name":{"type":"string","optional":true},"mode":{"type":"string","optional":true},"web_url":{"type":"string","optional":true}}},"max_items":1},"limits":{"nesting_mode":"list","block":{"attributes":{"daily":{"type":"number","optional":true},"maximum_duration":{"type":"number","optional":true},"messages_per_second":{"type":"number","optional":true},"total":{"type":"number","optional":true}}},"max_items":1},"quiet_time":{"nesting_mode":"list","block":{"attributes":{"end":{"type":"string","optional":true},"start":{"type":"string","optional":true}}},"max_items":1}}}},"aws_pinpoint_baidu_channel":{"version":0,"block":{"attributes":{"api_key":{"type":"string","required":true,"sensitive":true},"application_id":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"secret_key":{"type":"string","required":true,"sensitive":true}}}},"aws_pinpoint_email_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"from_address":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"identity":{"type":"string","required":true},"messages_per_second":{"type":"number","computed":true},"role_arn":{"type":"string","required":true}}}},"aws_pinpoint_event_stream":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"destination_stream_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","required":true}}}},"aws_pinpoint_gcm_channel":{"version":0,"block":{"attributes":{"api_key":{"type":"string","required":true,"sensitive":true},"application_id":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_pinpoint_sms_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"promotional_messages_per_second":{"type":"number","computed":true},"sender_id":{"type":"string","optional":true},"short_code":{"type":"string","optional":true},"transactional_messages_per_second":{"type":"number","computed":true}}}},"aws_placement_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"placement_group_id":{"type":"string","computed":true},"strategy":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_proxy_protocol_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_ports":{"type":["set","string"],"required":true},"load_balancer":{"type":"string","required":true}}}},"aws_qldb_ledger":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deletion_protection":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_quicksight_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_account_id":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"group_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"namespace":{"type":"string","optional":true}}}},"aws_quicksight_user":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_account_id":{"type":"string","optional":true,"computed":true},"email":{"type":"string","required":true},"iam_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"identity_type":{"type":"string","required":true},"namespace":{"type":"string","optional":true},"session_name":{"type":"string","optional":true},"user_name":{"type":"string","optional":true},"user_role":{"type":"string","required":true}}}},"aws_ram_principal_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"principal":{"type":"string","required":true},"resource_share_arn":{"type":"string","required":true}}}},"aws_ram_resource_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_arn":{"type":"string","required":true},"resource_share_arn":{"type":"string","required":true}}}},"aws_ram_resource_share":{"version":0,"block":{"attributes":{"allow_external_principals":{"type":"bool","optional":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_ram_resource_share_accepter":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"invitation_arn":{"type":"string","computed":true},"receiver_account_id":{"type":"string","computed":true},"resources":{"type":["list","string"],"computed":true},"sender_account_id":{"type":"string","computed":true},"share_arn":{"type":"string","required":true},"share_id":{"type":"string","computed":true},"share_name":{"type":"string","computed":true},"status":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_rds_cluster":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"backtrack_window":{"type":"number","optional":true},"backup_retention_period":{"type":"number","optional":true},"cluster_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier_prefix":{"type":"string","optional":true,"computed":true},"cluster_members":{"type":["set","string"],"optional":true,"computed":true},"cluster_resource_id":{"type":"string","computed":true},"copy_tags_to_snapshot":{"type":"bool","optional":true},"database_name":{"type":"string","optional":true,"computed":true},"db_cluster_parameter_group_name":{"type":"string","optional":true,"computed":true},"db_subnet_group_name":{"type":"string","optional":true,"computed":true},"deletion_protection":{"type":"bool","optional":true},"enable_http_endpoint":{"type":"bool","optional":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"optional":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_mode":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"global_cluster_identifier":{"type":"string","optional":true},"hosted_zone_id":{"type":"string","computed":true},"iam_database_authentication_enabled":{"type":"bool","optional":true},"iam_roles":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"master_password":{"type":"string","optional":true,"sensitive":true},"master_username":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"reader_endpoint":{"type":"string","computed":true},"replication_source_identifier":{"type":"string","optional":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_identifier":{"type":"string","optional":true},"source_region":{"type":"string","optional":true},"storage_encrypted":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"s3_import":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","required":true},"bucket_prefix":{"type":"string","optional":true},"ingestion_role":{"type":"string","required":true},"source_engine":{"type":"string","required":true},"source_engine_version":{"type":"string","required":true}}},"max_items":1},"scaling_configuration":{"nesting_mode":"list","block":{"attributes":{"auto_pause":{"type":"bool","optional":true},"max_capacity":{"type":"number","optional":true},"min_capacity":{"type":"number","optional":true},"seconds_until_auto_pause":{"type":"number","optional":true},"timeout_action":{"type":"string","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_rds_cluster_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cluster_endpoint_identifier":{"type":"string","required":true},"cluster_identifier":{"type":"string","required":true},"custom_endpoint_type":{"type":"string","required":true},"endpoint":{"type":"string","computed":true},"excluded_members":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"static_members":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_rds_cluster_instance":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"ca_cert_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier":{"type":"string","required":true},"copy_tags_to_snapshot":{"type":"bool","optional":true},"db_parameter_group_name":{"type":"string","optional":true,"computed":true},"db_subnet_group_name":{"type":"string","optional":true,"computed":true},"dbi_resource_id":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"instance_class":{"type":"string","required":true},"kms_key_id":{"type":"string","computed":true},"monitoring_interval":{"type":"number","optional":true},"monitoring_role_arn":{"type":"string","optional":true,"computed":true},"performance_insights_enabled":{"type":"bool","optional":true,"computed":true},"performance_insights_kms_key_id":{"type":"string","optional":true,"computed":true},"port":{"type":"number","computed":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"promotion_tier":{"type":"number","optional":true},"publicly_accessible":{"type":"bool","optional":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true},"writer":{"type":"bool","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_rds_cluster_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_rds_global_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"database_name":{"type":"string","optional":true},"deletion_protection":{"type":"bool","optional":true},"engine":{"type":"string","optional":true,"computed":true},"engine_version":{"type":"string","optional":true,"computed":true},"force_destroy":{"type":"bool","optional":true},"global_cluster_identifier":{"type":"string","required":true},"global_cluster_members":{"type":["set",["object",{"db_cluster_arn":"string","is_writer":"bool"}]],"computed":true},"global_cluster_resource_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"source_db_cluster_identifier":{"type":"string","optional":true,"computed":true},"storage_encrypted":{"type":"bool","optional":true}}}},"aws_redshift_cluster":{"version":0,"block":{"attributes":{"allow_version_upgrade":{"type":"bool","optional":true},"arn":{"type":"string","computed":true},"automated_snapshot_retention_period":{"type":"number","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"cluster_identifier":{"type":"string","required":true},"cluster_parameter_group_name":{"type":"string","optional":true,"computed":true},"cluster_public_key":{"type":"string","optional":true,"computed":true},"cluster_revision_number":{"type":"string","optional":true,"computed":true},"cluster_security_groups":{"type":["set","string"],"optional":true,"computed":true},"cluster_subnet_group_name":{"type":"string","optional":true,"computed":true},"cluster_type":{"type":"string","optional":true,"computed":true},"cluster_version":{"type":"string","optional":true},"database_name":{"type":"string","optional":true,"computed":true},"dns_name":{"type":"string","computed":true},"elastic_ip":{"type":"string","optional":true},"encrypted":{"type":"bool","optional":true},"endpoint":{"type":"string","optional":true,"computed":true},"enhanced_vpc_routing":{"type":"bool","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"iam_roles":{"type":["set","string"],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"master_password":{"type":"string","optional":true,"sensitive":true},"master_username":{"type":"string","optional":true},"node_type":{"type":"string","required":true},"number_of_nodes":{"type":"number","optional":true},"owner_account":{"type":"string","optional":true},"port":{"type":"number","optional":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"publicly_accessible":{"type":"bool","optional":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_cluster_identifier":{"type":"string","optional":true},"snapshot_identifier":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"logging":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","optional":true,"computed":true},"enable":{"type":"bool","required":true},"s3_key_prefix":{"type":"string","optional":true,"computed":true}}},"max_items":1},"snapshot_copy":{"nesting_mode":"list","block":{"attributes":{"destination_region":{"type":"string","required":true},"grant_name":{"type":"string","optional":true},"retention_period":{"type":"number","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_redshift_event_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_aws_id":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"event_categories":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"severity":{"type":"string","optional":true},"sns_topic_arn":{"type":"string","required":true},"source_ids":{"type":["set","string"],"optional":true},"source_type":{"type":"string","optional":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_redshift_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_redshift_security_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"ingress":{"nesting_mode":"set","block":{"attributes":{"cidr":{"type":"string","optional":true},"security_group_name":{"type":"string","optional":true,"computed":true},"security_group_owner_id":{"type":"string","optional":true,"computed":true}}},"min_items":1}}}},"aws_redshift_snapshot_copy_grant":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"snapshot_copy_grant_name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_redshift_snapshot_schedule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"definitions":{"type":["set","string"],"required":true},"description":{"type":"string","optional":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_redshift_snapshot_schedule_association":{"version":0,"block":{"attributes":{"cluster_identifier":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"schedule_identifier":{"type":"string","required":true}}}},"aws_redshift_subnet_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_resourcegroups_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"resource_query":{"nesting_mode":"list","block":{"attributes":{"query":{"type":"string","required":true},"type":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}}},"aws_route":{"version":0,"block":{"attributes":{"destination_cidr_block":{"type":"string","optional":true},"destination_ipv6_cidr_block":{"type":"string","optional":true},"destination_prefix_list_id":{"type":"string","computed":true},"egress_only_gateway_id":{"type":"string","optional":true,"computed":true},"gateway_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true,"computed":true},"instance_owner_id":{"type":"string","computed":true},"nat_gateway_id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","optional":true,"computed":true},"origin":{"type":"string","computed":true},"route_table_id":{"type":"string","required":true},"state":{"type":"string","computed":true},"transit_gateway_id":{"type":"string","optional":true},"vpc_peering_connection_id":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_route53_delegation_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name_servers":{"type":["list","string"],"computed":true},"reference_name":{"type":"string","optional":true}}}},"aws_route53_health_check":{"version":0,"block":{"attributes":{"child_health_threshold":{"type":"number","optional":true},"child_healthchecks":{"type":["set","string"],"optional":true},"cloudwatch_alarm_name":{"type":"string","optional":true},"cloudwatch_alarm_region":{"type":"string","optional":true},"disabled":{"type":"bool","optional":true},"enable_sni":{"type":"bool","optional":true,"computed":true},"failure_threshold":{"type":"number","optional":true},"fqdn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"insufficient_data_health_status":{"type":"string","optional":true},"invert_healthcheck":{"type":"bool","optional":true},"ip_address":{"type":"string","optional":true},"measure_latency":{"type":"bool","optional":true},"port":{"type":"number","optional":true},"reference_name":{"type":"string","optional":true},"regions":{"type":["set","string"],"optional":true},"request_interval":{"type":"number","optional":true},"resource_path":{"type":"string","optional":true},"search_string":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","required":true}}}},"aws_route53_query_log":{"version":0,"block":{"attributes":{"cloudwatch_log_group_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","required":true}}}},"aws_route53_record":{"version":2,"block":{"attributes":{"allow_overwrite":{"type":"bool","optional":true,"computed":true},"fqdn":{"type":"string","computed":true},"health_check_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"multivalue_answer_routing_policy":{"type":"bool","optional":true},"name":{"type":"string","required":true},"records":{"type":["set","string"],"optional":true},"set_identifier":{"type":"string","optional":true},"ttl":{"type":"number","optional":true},"type":{"type":"string","required":true},"zone_id":{"type":"string","required":true}},"block_types":{"alias":{"nesting_mode":"set","block":{"attributes":{"evaluate_target_health":{"type":"bool","required":true},"name":{"type":"string","required":true},"zone_id":{"type":"string","required":true}}}},"failover_routing_policy":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}}},"geolocation_routing_policy":{"nesting_mode":"list","block":{"attributes":{"continent":{"type":"string","optional":true},"country":{"type":"string","optional":true},"subdivision":{"type":"string","optional":true}}}},"latency_routing_policy":{"nesting_mode":"list","block":{"attributes":{"region":{"type":"string","required":true}}}},"weighted_routing_policy":{"nesting_mode":"list","block":{"attributes":{"weight":{"type":"number","required":true}}}}}}},"aws_route53_resolver_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"direction":{"type":"string","required":true},"host_vpc_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"security_group_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"ip_address":{"nesting_mode":"set","block":{"attributes":{"ip":{"type":"string","optional":true,"computed":true},"ip_id":{"type":"string","computed":true},"subnet_id":{"type":"string","required":true}}},"min_items":2,"max_items":10},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_route53_resolver_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"resolver_endpoint_id":{"type":"string","optional":true},"rule_type":{"type":"string","required":true},"share_status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"target_ip":{"nesting_mode":"set","block":{"attributes":{"ip":{"type":"string","required":true},"port":{"type":"number","optional":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_route53_resolver_rule_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"resolver_rule_id":{"type":"string","required":true},"vpc_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_route53_vpc_association_authorization":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"vpc_id":{"type":"string","required":true},"vpc_region":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","required":true}}}},"aws_route53_zone":{"version":0,"block":{"attributes":{"comment":{"type":"string","optional":true},"delegation_set_id":{"type":"string","optional":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"name_servers":{"type":["list","string"],"computed":true},"tags":{"type":["map","string"],"optional":true},"zone_id":{"type":"string","computed":true}},"block_types":{"vpc":{"nesting_mode":"set","block":{"attributes":{"vpc_id":{"type":"string","required":true},"vpc_region":{"type":"string","optional":true,"computed":true}}}}}}},"aws_route53_zone_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"owning_account":{"type":"string","computed":true},"vpc_id":{"type":"string","required":true},"vpc_region":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","required":true}}}},"aws_route_table":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"propagating_vgws":{"type":["set","string"],"optional":true,"computed":true},"route":{"type":["set",["object",{"cidr_block":"string","egress_only_gateway_id":"string","gateway_id":"string","instance_id":"string","ipv6_cidr_block":"string","nat_gateway_id":"string","network_interface_id":"string","transit_gateway_id":"string","vpc_peering_connection_id":"string"}]],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}}}},"aws_route_table_association":{"version":0,"block":{"attributes":{"gateway_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"route_table_id":{"type":"string","required":true},"subnet_id":{"type":"string","optional":true}}}},"aws_s3_access_point":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"bucket":{"type":"string","required":true},"domain_name":{"type":"string","computed":true},"has_public_access_policy":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"network_origin":{"type":"string","computed":true},"policy":{"type":"string","optional":true}},"block_types":{"public_access_block_configuration":{"nesting_mode":"list","block":{"attributes":{"block_public_acls":{"type":"bool","optional":true},"block_public_policy":{"type":"bool","optional":true},"ignore_public_acls":{"type":"bool","optional":true},"restrict_public_buckets":{"type":"bool","optional":true}}},"max_items":1},"vpc_configuration":{"nesting_mode":"list","block":{"attributes":{"vpc_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_s3_account_public_access_block":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"block_public_acls":{"type":"bool","optional":true},"block_public_policy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"ignore_public_acls":{"type":"bool","optional":true},"restrict_public_buckets":{"type":"bool","optional":true}}}},"aws_s3_bucket":{"version":0,"block":{"attributes":{"acceleration_status":{"type":"string","optional":true,"computed":true},"acl":{"type":"string","optional":true},"arn":{"type":"string","optional":true,"computed":true},"bucket":{"type":"string","optional":true,"computed":true},"bucket_domain_name":{"type":"string","computed":true},"bucket_prefix":{"type":"string","optional":true},"bucket_regional_domain_name":{"type":"string","computed":true},"force_destroy":{"type":"bool","optional":true},"hosted_zone_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","optional":true},"region":{"type":"string","computed":true},"request_payer":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"website_domain":{"type":"string","optional":true,"computed":true},"website_endpoint":{"type":"string","optional":true,"computed":true}},"block_types":{"cors_rule":{"nesting_mode":"list","block":{"attributes":{"allowed_headers":{"type":["list","string"],"optional":true},"allowed_methods":{"type":["list","string"],"required":true},"allowed_origins":{"type":["list","string"],"required":true},"expose_headers":{"type":["list","string"],"optional":true},"max_age_seconds":{"type":"number","optional":true}}}},"grant":{"nesting_mode":"set","block":{"attributes":{"id":{"type":"string","optional":true},"permissions":{"type":["set","string"],"required":true},"type":{"type":"string","required":true},"uri":{"type":"string","optional":true}}}},"lifecycle_rule":{"nesting_mode":"list","block":{"attributes":{"abort_incomplete_multipart_upload_days":{"type":"number","optional":true},"enabled":{"type":"bool","required":true},"id":{"type":"string","optional":true,"computed":true},"prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"expiration":{"nesting_mode":"list","block":{"attributes":{"date":{"type":"string","optional":true},"days":{"type":"number","optional":true},"expired_object_delete_marker":{"type":"bool","optional":true}}},"max_items":1},"noncurrent_version_expiration":{"nesting_mode":"list","block":{"attributes":{"days":{"type":"number","optional":true}}},"max_items":1},"noncurrent_version_transition":{"nesting_mode":"set","block":{"attributes":{"days":{"type":"number","optional":true},"storage_class":{"type":"string","required":true}}}},"transition":{"nesting_mode":"set","block":{"attributes":{"date":{"type":"string","optional":true},"days":{"type":"number","optional":true},"storage_class":{"type":"string","required":true}}}}}}},"logging":{"nesting_mode":"set","block":{"attributes":{"target_bucket":{"type":"string","required":true},"target_prefix":{"type":"string","optional":true}}}},"object_lock_configuration":{"nesting_mode":"list","block":{"attributes":{"object_lock_enabled":{"type":"string","required":true}},"block_types":{"rule":{"nesting_mode":"list","block":{"block_types":{"default_retention":{"nesting_mode":"list","block":{"attributes":{"days":{"type":"number","optional":true},"mode":{"type":"string","required":true},"years":{"type":"number","optional":true}}},"min_items":1,"max_items":1}}},"max_items":1}}},"max_items":1},"replication_configuration":{"nesting_mode":"list","block":{"attributes":{"role":{"type":"string","required":true}},"block_types":{"rules":{"nesting_mode":"set","block":{"attributes":{"id":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"priority":{"type":"number","optional":true},"status":{"type":"string","required":true}},"block_types":{"destination":{"nesting_mode":"list","block":{"attributes":{"account_id":{"type":"string","optional":true},"bucket":{"type":"string","required":true},"replica_kms_key_id":{"type":"string","optional":true},"storage_class":{"type":"string","optional":true}},"block_types":{"access_control_translation":{"nesting_mode":"list","block":{"attributes":{"owner":{"type":"string","required":true}}},"max_items":1}}},"min_items":1,"max_items":1},"filter":{"nesting_mode":"list","block":{"attributes":{"prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}},"max_items":1},"source_selection_criteria":{"nesting_mode":"list","block":{"block_types":{"sse_kms_encrypted_objects":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"server_side_encryption_configuration":{"nesting_mode":"list","block":{"block_types":{"rule":{"nesting_mode":"list","block":{"block_types":{"apply_server_side_encryption_by_default":{"nesting_mode":"list","block":{"attributes":{"kms_master_key_id":{"type":"string","optional":true},"sse_algorithm":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1},"versioning":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"mfa_delete":{"type":"bool","optional":true}}},"max_items":1},"website":{"nesting_mode":"list","block":{"attributes":{"error_document":{"type":"string","optional":true},"index_document":{"type":"string","optional":true},"redirect_all_requests_to":{"type":"string","optional":true},"routing_rules":{"type":"string","optional":true}}},"max_items":1}}}},"aws_s3_bucket_analytics_configuration":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"filter":{"nesting_mode":"list","block":{"attributes":{"prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}},"max_items":1},"storage_class_analysis":{"nesting_mode":"list","block":{"block_types":{"data_export":{"nesting_mode":"list","block":{"attributes":{"output_schema_version":{"type":"string","optional":true}},"block_types":{"destination":{"nesting_mode":"list","block":{"block_types":{"s3_bucket_destination":{"nesting_mode":"list","block":{"attributes":{"bucket_account_id":{"type":"string","optional":true},"bucket_arn":{"type":"string","required":true},"format":{"type":"string","optional":true},"prefix":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1}}}},"aws_s3_bucket_inventory":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"included_object_versions":{"type":"string","required":true},"name":{"type":"string","required":true},"optional_fields":{"type":["set","string"],"optional":true}},"block_types":{"destination":{"nesting_mode":"list","block":{"block_types":{"bucket":{"nesting_mode":"list","block":{"attributes":{"account_id":{"type":"string","optional":true},"bucket_arn":{"type":"string","required":true},"format":{"type":"string","required":true},"prefix":{"type":"string","optional":true}},"block_types":{"encryption":{"nesting_mode":"list","block":{"block_types":{"sse_kms":{"nesting_mode":"list","block":{"attributes":{"key_id":{"type":"string","required":true}}},"max_items":1},"sse_s3":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"filter":{"nesting_mode":"list","block":{"attributes":{"prefix":{"type":"string","optional":true}}},"max_items":1},"schedule":{"nesting_mode":"list","block":{"attributes":{"frequency":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_s3_bucket_metric":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"filter":{"nesting_mode":"list","block":{"attributes":{"prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}},"max_items":1}}}},"aws_s3_bucket_notification":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}},"block_types":{"lambda_function":{"nesting_mode":"list","block":{"attributes":{"events":{"type":["set","string"],"required":true},"filter_prefix":{"type":"string","optional":true},"filter_suffix":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"lambda_function_arn":{"type":"string","optional":true}}}},"queue":{"nesting_mode":"list","block":{"attributes":{"events":{"type":["set","string"],"required":true},"filter_prefix":{"type":"string","optional":true},"filter_suffix":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"queue_arn":{"type":"string","required":true}}}},"topic":{"nesting_mode":"list","block":{"attributes":{"events":{"type":["set","string"],"required":true},"filter_prefix":{"type":"string","optional":true},"filter_suffix":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"topic_arn":{"type":"string","required":true}}}}}}},"aws_s3_bucket_object":{"version":0,"block":{"attributes":{"acl":{"type":"string","optional":true},"bucket":{"type":"string","required":true},"cache_control":{"type":"string","optional":true},"content":{"type":"string","optional":true},"content_base64":{"type":"string","optional":true},"content_disposition":{"type":"string","optional":true},"content_encoding":{"type":"string","optional":true},"content_language":{"type":"string","optional":true},"content_type":{"type":"string","optional":true,"computed":true},"etag":{"type":"string","optional":true,"computed":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","required":true},"kms_key_id":{"type":"string","optional":true},"metadata":{"type":["map","string"],"optional":true},"object_lock_legal_hold_status":{"type":"string","optional":true},"object_lock_mode":{"type":"string","optional":true},"object_lock_retain_until_date":{"type":"string","optional":true},"server_side_encryption":{"type":"string","optional":true,"computed":true},"source":{"type":"string","optional":true},"storage_class":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"version_id":{"type":"string","computed":true},"website_redirect":{"type":"string","optional":true}}}},"aws_s3_bucket_policy":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true}}}},"aws_s3_bucket_public_access_block":{"version":0,"block":{"attributes":{"block_public_acls":{"type":"bool","optional":true},"block_public_policy":{"type":"bool","optional":true},"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ignore_public_acls":{"type":"bool","optional":true},"restrict_public_buckets":{"type":"bool","optional":true}}}},"aws_sagemaker_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"endpoint_config_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_sagemaker_endpoint_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"production_variants":{"nesting_mode":"list","block":{"attributes":{"accelerator_type":{"type":"string","optional":true},"initial_instance_count":{"type":"number","required":true},"initial_variant_weight":{"type":"number","optional":true},"instance_type":{"type":"string","required":true},"model_name":{"type":"string","required":true},"variant_name":{"type":"string","optional":true,"computed":true}}},"min_items":1}}}},"aws_sagemaker_model":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"enable_network_isolation":{"type":"bool","optional":true},"execution_role_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"container":{"nesting_mode":"list","block":{"attributes":{"container_hostname":{"type":"string","optional":true},"environment":{"type":["map","string"],"optional":true},"image":{"type":"string","required":true},"model_data_url":{"type":"string","optional":true}}}},"primary_container":{"nesting_mode":"list","block":{"attributes":{"container_hostname":{"type":"string","optional":true},"environment":{"type":["map","string"],"optional":true},"image":{"type":"string","required":true},"model_data_url":{"type":"string","optional":true}}},"max_items":1},"vpc_config":{"nesting_mode":"list","block":{"attributes":{"security_group_ids":{"type":["set","string"],"required":true},"subnets":{"type":["set","string"],"required":true}}},"max_items":1}}}},"aws_sagemaker_notebook_instance":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"direct_internet_access":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","required":true},"kms_key_id":{"type":"string","optional":true},"lifecycle_config_name":{"type":"string","optional":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"subnet_id":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_sagemaker_notebook_instance_lifecycle_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"on_create":{"type":"string","optional":true},"on_start":{"type":"string","optional":true}}}},"aws_secretsmanager_secret":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","optional":true},"recovery_window_in_days":{"type":"number","optional":true},"rotation_enabled":{"type":"bool","computed":true},"rotation_lambda_arn":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"rotation_rules":{"nesting_mode":"list","block":{"attributes":{"automatically_after_days":{"type":"number","required":true}}},"max_items":1}}}},"aws_secretsmanager_secret_rotation":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"rotation_enabled":{"type":"bool","computed":true},"rotation_lambda_arn":{"type":"string","required":true},"secret_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"rotation_rules":{"nesting_mode":"list","block":{"attributes":{"automatically_after_days":{"type":"number","required":true}}},"min_items":1,"max_items":1}}}},"aws_secretsmanager_secret_version":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"secret_binary":{"type":"string","optional":true,"sensitive":true},"secret_id":{"type":"string","required":true},"secret_string":{"type":"string","optional":true,"sensitive":true},"version_id":{"type":"string","computed":true},"version_stages":{"type":["set","string"],"optional":true,"computed":true}}}},"aws_security_group":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"egress":{"type":["set",["object",{"cidr_blocks":["list","string"],"description":"string","from_port":"number","ipv6_cidr_blocks":["list","string"],"prefix_list_ids":["list","string"],"protocol":"string","security_groups":["set","string"],"self":"bool","to_port":"number"}]],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ingress":{"type":["set",["object",{"cidr_blocks":["list","string"],"description":"string","from_port":"number","ipv6_cidr_blocks":["list","string"],"prefix_list_ids":["list","string"],"protocol":"string","security_groups":["set","string"],"self":"bool","to_port":"number"}]],"optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"revoke_rules_on_delete":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_security_group_rule":{"version":2,"block":{"attributes":{"cidr_blocks":{"type":["list","string"],"optional":true},"description":{"type":"string","optional":true},"from_port":{"type":"number","required":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_blocks":{"type":["list","string"],"optional":true},"prefix_list_ids":{"type":["list","string"],"optional":true},"protocol":{"type":"string","required":true},"security_group_id":{"type":"string","required":true},"self":{"type":"bool","optional":true},"source_security_group_id":{"type":"string","optional":true,"computed":true},"to_port":{"type":"number","required":true},"type":{"type":"string","description":"Type of rule, ingress (inbound) or egress (outbound).","required":true}}}},"aws_securityhub_account":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true}}}},"aws_securityhub_member":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"email":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"invite":{"type":"bool","optional":true},"master_id":{"type":"string","computed":true},"member_status":{"type":"string","computed":true}}}},"aws_securityhub_product_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"product_arn":{"type":"string","required":true}}}},"aws_securityhub_standards_subscription":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"standards_arn":{"type":"string","required":true}}}},"aws_service_discovery_http_namespace":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_service_discovery_private_dns_namespace":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"hosted_zone":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vpc":{"type":"string","required":true}}}},"aws_service_discovery_public_dns_namespace":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"hosted_zone":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_service_discovery_service":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"namespace_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"dns_config":{"nesting_mode":"list","block":{"attributes":{"namespace_id":{"type":"string","required":true},"routing_policy":{"type":"string","optional":true}},"block_types":{"dns_records":{"nesting_mode":"list","block":{"attributes":{"ttl":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"health_check_config":{"nesting_mode":"list","block":{"attributes":{"failure_threshold":{"type":"number","optional":true},"resource_path":{"type":"string","optional":true},"type":{"type":"string","optional":true}}},"max_items":1},"health_check_custom_config":{"nesting_mode":"list","block":{"attributes":{"failure_threshold":{"type":"number","optional":true}}},"max_items":1}}}},"aws_servicecatalog_portfolio":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_time":{"type":"string","computed":true},"description":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"provider_name":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_servicequotas_service_quota":{"version":0,"block":{"attributes":{"adjustable":{"type":"bool","computed":true},"arn":{"type":"string","computed":true},"default_value":{"type":"number","computed":true},"id":{"type":"string","optional":true,"computed":true},"quota_code":{"type":"string","required":true},"quota_name":{"type":"string","computed":true},"request_id":{"type":"string","computed":true},"request_status":{"type":"string","computed":true},"service_code":{"type":"string","required":true},"service_name":{"type":"string","computed":true},"value":{"type":"number","required":true}}}},"aws_ses_active_receipt_rule_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"rule_set_name":{"type":"string","required":true}}}},"aws_ses_configuration_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_ses_domain_dkim":{"version":0,"block":{"attributes":{"dkim_tokens":{"type":["list","string"],"computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ses_domain_identity":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"verification_token":{"type":"string","computed":true}}}},"aws_ses_domain_identity_verification":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_ses_domain_mail_from":{"version":0,"block":{"attributes":{"behavior_on_mx_failure":{"type":"string","optional":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"mail_from_domain":{"type":"string","required":true}}}},"aws_ses_email_identity":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"email":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ses_event_destination":{"version":0,"block":{"attributes":{"configuration_set_name":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"matching_types":{"type":["set","string"],"required":true},"name":{"type":"string","required":true}},"block_types":{"cloudwatch_destination":{"nesting_mode":"set","block":{"attributes":{"default_value":{"type":"string","required":true},"dimension_name":{"type":"string","required":true},"value_source":{"type":"string","required":true}}}},"kinesis_destination":{"nesting_mode":"list","block":{"attributes":{"role_arn":{"type":"string","required":true},"stream_arn":{"type":"string","required":true}}},"max_items":1},"sns_destination":{"nesting_mode":"list","block":{"attributes":{"topic_arn":{"type":"string","required":true}}},"max_items":1}}}},"aws_ses_identity_notification_topic":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"identity":{"type":"string","required":true},"include_original_headers":{"type":"bool","optional":true},"notification_type":{"type":"string","required":true},"topic_arn":{"type":"string","optional":true}}}},"aws_ses_identity_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"identity":{"type":"string","required":true},"name":{"type":"string","required":true},"policy":{"type":"string","required":true}}}},"aws_ses_receipt_filter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cidr":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"policy":{"type":"string","required":true}}}},"aws_ses_receipt_rule":{"version":0,"block":{"attributes":{"after":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"recipients":{"type":["set","string"],"optional":true},"rule_set_name":{"type":"string","required":true},"scan_enabled":{"type":"bool","optional":true,"computed":true},"tls_policy":{"type":"string","optional":true,"computed":true}},"block_types":{"add_header_action":{"nesting_mode":"set","block":{"attributes":{"header_name":{"type":"string","required":true},"header_value":{"type":"string","required":true},"position":{"type":"number","required":true}}}},"bounce_action":{"nesting_mode":"set","block":{"attributes":{"message":{"type":"string","required":true},"position":{"type":"number","required":true},"sender":{"type":"string","required":true},"smtp_reply_code":{"type":"string","required":true},"status_code":{"type":"string","optional":true},"topic_arn":{"type":"string","optional":true}}}},"lambda_action":{"nesting_mode":"set","block":{"attributes":{"function_arn":{"type":"string","required":true},"invocation_type":{"type":"string","optional":true,"computed":true},"position":{"type":"number","required":true},"topic_arn":{"type":"string","optional":true}}}},"s3_action":{"nesting_mode":"set","block":{"attributes":{"bucket_name":{"type":"string","required":true},"kms_key_arn":{"type":"string","optional":true},"object_key_prefix":{"type":"string","optional":true},"position":{"type":"number","required":true},"topic_arn":{"type":"string","optional":true}}}},"sns_action":{"nesting_mode":"set","block":{"attributes":{"position":{"type":"number","required":true},"topic_arn":{"type":"string","required":true}}}},"stop_action":{"nesting_mode":"set","block":{"attributes":{"position":{"type":"number","required":true},"scope":{"type":"string","required":true},"topic_arn":{"type":"string","optional":true}}}},"workmail_action":{"nesting_mode":"set","block":{"attributes":{"organization_arn":{"type":"string","required":true},"position":{"type":"number","required":true},"topic_arn":{"type":"string","optional":true}}}}}}},"aws_ses_receipt_rule_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"rule_set_name":{"type":"string","required":true}}}},"aws_ses_template":{"version":0,"block":{"attributes":{"html":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"subject":{"type":"string","optional":true},"text":{"type":"string","optional":true}}}},"aws_sfn_activity":{"version":0,"block":{"attributes":{"creation_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_sfn_state_machine":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_date":{"type":"string","computed":true},"definition":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_shield_protection":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"resource_arn":{"type":"string","required":true}}}},"aws_simpledb_domain":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_snapshot_create_volume_permission":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"snapshot_id":{"type":"string","required":true}}}},"aws_sns_platform_application":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"event_delivery_failure_topic_arn":{"type":"string","optional":true},"event_endpoint_created_topic_arn":{"type":"string","optional":true},"event_endpoint_deleted_topic_arn":{"type":"string","optional":true},"event_endpoint_updated_topic_arn":{"type":"string","optional":true},"failure_feedback_role_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"platform":{"type":"string","required":true},"platform_credential":{"type":"string","required":true,"sensitive":true},"platform_principal":{"type":"string","optional":true,"sensitive":true},"success_feedback_role_arn":{"type":"string","optional":true},"success_feedback_sample_rate":{"type":"string","optional":true}}}},"aws_sns_sms_preferences":{"version":0,"block":{"attributes":{"default_sender_id":{"type":"string","optional":true},"default_sms_type":{"type":"string","optional":true},"delivery_status_iam_role_arn":{"type":"string","optional":true},"delivery_status_success_sampling_rate":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"monthly_spend_limit":{"type":"string","optional":true},"usage_report_s3_bucket":{"type":"string","optional":true}}}},"aws_sns_topic":{"version":0,"block":{"attributes":{"application_failure_feedback_role_arn":{"type":"string","optional":true},"application_success_feedback_role_arn":{"type":"string","optional":true},"application_success_feedback_sample_rate":{"type":"number","optional":true},"arn":{"type":"string","computed":true},"delivery_policy":{"type":"string","optional":true},"display_name":{"type":"string","optional":true},"http_failure_feedback_role_arn":{"type":"string","optional":true},"http_success_feedback_role_arn":{"type":"string","optional":true},"http_success_feedback_sample_rate":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_master_key_id":{"type":"string","optional":true},"lambda_failure_feedback_role_arn":{"type":"string","optional":true},"lambda_success_feedback_role_arn":{"type":"string","optional":true},"lambda_success_feedback_sample_rate":{"type":"number","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","optional":true,"computed":true},"sqs_failure_feedback_role_arn":{"type":"string","optional":true},"sqs_success_feedback_role_arn":{"type":"string","optional":true},"sqs_success_feedback_sample_rate":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_sns_topic_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true}}}},"aws_sns_topic_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"confirmation_timeout_in_minutes":{"type":"number","optional":true},"delivery_policy":{"type":"string","optional":true},"endpoint":{"type":"string","required":true},"endpoint_auto_confirms":{"type":"bool","optional":true},"filter_policy":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"protocol":{"type":"string","required":true},"raw_message_delivery":{"type":"bool","optional":true},"topic_arn":{"type":"string","required":true}}}},"aws_spot_datafeed_subscription":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"prefix":{"type":"string","optional":true}}}},"aws_spot_fleet_request":{"version":1,"block":{"attributes":{"allocation_strategy":{"type":"string","optional":true},"client_token":{"type":"string","computed":true},"excess_capacity_termination_policy":{"type":"string","optional":true},"fleet_type":{"type":"string","optional":true},"iam_fleet_role":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instance_interruption_behaviour":{"type":"string","optional":true},"instance_pools_to_use_count":{"type":"number","optional":true},"load_balancers":{"type":["set","string"],"optional":true,"computed":true},"replace_unhealthy_instances":{"type":"bool","optional":true},"spot_price":{"type":"string","optional":true},"spot_request_state":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"target_capacity":{"type":"number","required":true},"target_group_arns":{"type":["set","string"],"optional":true,"computed":true},"terminate_instances_with_expiration":{"type":"bool","optional":true},"valid_from":{"type":"string","optional":true},"valid_until":{"type":"string","optional":true},"wait_for_fulfillment":{"type":"bool","optional":true}},"block_types":{"launch_specification":{"nesting_mode":"set","block":{"attributes":{"ami":{"type":"string","required":true},"associate_public_ip_address":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"ebs_optimized":{"type":"bool","optional":true},"iam_instance_profile":{"type":"string","optional":true},"iam_instance_profile_arn":{"type":"string","optional":true},"instance_type":{"type":"string","required":true},"key_name":{"type":"string","optional":true,"computed":true},"monitoring":{"type":"bool","optional":true},"placement_group":{"type":"string","optional":true,"computed":true},"placement_tenancy":{"type":"string","optional":true},"spot_price":{"type":"string","optional":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"user_data":{"type":"string","optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"weighted_capacity":{"type":"string","optional":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"virtual_name":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}}}}},"launch_template_config":{"nesting_mode":"set","block":{"block_types":{"launch_template_specification":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","optional":true},"name":{"type":"string","optional":true},"version":{"type":"string","optional":true}}},"min_items":1,"max_items":1},"overrides":{"nesting_mode":"set","block":{"attributes":{"availability_zone":{"type":"string","optional":true},"instance_type":{"type":"string","optional":true},"priority":{"type":"number","optional":true,"computed":true},"spot_price":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true},"weighted_capacity":{"type":"number","optional":true,"computed":true}}}}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_spot_instance_request":{"version":0,"block":{"attributes":{"ami":{"type":"string","required":true},"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","optional":true,"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"block_duration_minutes":{"type":"number","optional":true},"cpu_core_count":{"type":"number","optional":true,"computed":true},"cpu_threads_per_core":{"type":"number","optional":true,"computed":true},"disable_api_termination":{"type":"bool","optional":true},"ebs_optimized":{"type":"bool","optional":true},"get_password_data":{"type":"bool","optional":true},"hibernation":{"type":"bool","optional":true},"host_id":{"type":"string","optional":true,"computed":true},"iam_instance_profile":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_initiated_shutdown_behavior":{"type":"string","optional":true},"instance_interruption_behaviour":{"type":"string","optional":true},"instance_state":{"type":"string","computed":true},"instance_type":{"type":"string","required":true},"ipv6_address_count":{"type":"number","optional":true,"computed":true},"ipv6_addresses":{"type":["list","string"],"optional":true,"computed":true},"key_name":{"type":"string","optional":true,"computed":true},"launch_group":{"type":"string","optional":true},"monitoring":{"type":"bool","optional":true},"outpost_arn":{"type":"string","computed":true},"password_data":{"type":"string","computed":true},"placement_group":{"type":"string","optional":true,"computed":true},"primary_network_interface_id":{"type":"string","computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","optional":true,"computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"secondary_private_ips":{"type":["set","string"],"optional":true,"computed":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"source_dest_check":{"type":"bool","optional":true},"spot_bid_status":{"type":"string","computed":true},"spot_instance_id":{"type":"string","computed":true},"spot_price":{"type":"string","optional":true},"spot_request_state":{"type":"string","computed":true},"spot_type":{"type":"string","optional":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"tenancy":{"type":"string","optional":true,"computed":true},"user_data":{"type":"string","optional":true},"user_data_base64":{"type":"string","optional":true},"valid_from":{"type":"string","optional":true,"computed":true},"valid_until":{"type":"string","optional":true,"computed":true},"volume_tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"wait_for_fulfillment":{"type":"bool","optional":true}},"block_types":{"credit_specification":{"nesting_mode":"list","block":{"attributes":{"cpu_credits":{"type":"string","optional":true}}},"max_items":1},"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"no_device":{"type":"bool","optional":true},"virtual_name":{"type":"string","optional":true}}}},"metadata_options":{"nesting_mode":"list","block":{"attributes":{"http_endpoint":{"type":"string","optional":true,"computed":true},"http_put_response_hop_limit":{"type":"number","optional":true,"computed":true},"http_tokens":{"type":"string","optional":true,"computed":true}}},"max_items":1},"network_interface":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_index":{"type":"number","required":true},"network_interface_id":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"list","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","computed":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_sqs_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"content_based_deduplication":{"type":"bool","optional":true},"delay_seconds":{"type":"number","optional":true},"fifo_queue":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_data_key_reuse_period_seconds":{"type":"number","optional":true,"computed":true},"kms_master_key_id":{"type":"string","optional":true},"max_message_size":{"type":"number","optional":true},"message_retention_seconds":{"type":"number","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","optional":true,"computed":true},"receive_wait_time_seconds":{"type":"number","optional":true},"redrive_policy":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"visibility_timeout_seconds":{"type":"number","optional":true}}}},"aws_sqs_queue_policy":{"version":1,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true},"queue_url":{"type":"string","required":true}}}},"aws_ssm_activation":{"version":0,"block":{"attributes":{"activation_code":{"type":"string","computed":true},"description":{"type":"string","optional":true},"expiration_date":{"type":"string","optional":true,"computed":true},"expired":{"type":"bool","computed":true},"iam_role":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"registration_count":{"type":"number","computed":true},"registration_limit":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_ssm_association":{"version":1,"block":{"attributes":{"association_id":{"type":"string","computed":true},"association_name":{"type":"string","optional":true},"automation_target_parameter_name":{"type":"string","optional":true},"compliance_severity":{"type":"string","optional":true},"document_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true},"max_concurrency":{"type":"string","optional":true},"max_errors":{"type":"string","optional":true},"name":{"type":"string","required":true},"parameters":{"type":["map","string"],"optional":true,"computed":true},"schedule_expression":{"type":"string","optional":true}},"block_types":{"output_location":{"nesting_mode":"list","block":{"attributes":{"s3_bucket_name":{"type":"string","required":true},"s3_key_prefix":{"type":"string","optional":true}}},"max_items":1},"targets":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"max_items":5}}}},"aws_ssm_document":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"content":{"type":"string","required":true},"created_date":{"type":"string","computed":true},"default_version":{"type":"string","computed":true},"description":{"type":"string","computed":true},"document_format":{"type":"string","optional":true},"document_type":{"type":"string","required":true},"document_version":{"type":"string","computed":true},"hash":{"type":"string","computed":true},"hash_type":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"latest_version":{"type":"string","computed":true},"name":{"type":"string","required":true},"owner":{"type":"string","computed":true},"parameter":{"type":["list",["object",{"default_value":"string","description":"string","name":"string","type":"string"}]],"computed":true},"permissions":{"type":["map","string"],"optional":true},"platform_types":{"type":["list","string"],"computed":true},"schema_version":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"target_type":{"type":"string","optional":true}},"block_types":{"attachments_source":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"name":{"type":"string","optional":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ssm_maintenance_window":{"version":0,"block":{"attributes":{"allow_unassociated_targets":{"type":"bool","optional":true},"cutoff":{"type":"number","required":true},"description":{"type":"string","optional":true},"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true},"end_date":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"schedule":{"type":"string","required":true},"schedule_timezone":{"type":"string","optional":true},"start_date":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_ssm_maintenance_window_target":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"owner_information":{"type":"string","optional":true},"resource_type":{"type":"string","required":true},"window_id":{"type":"string","required":true}},"block_types":{"targets":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"min_items":1,"max_items":5}}}},"aws_ssm_maintenance_window_task":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"max_concurrency":{"type":"string","required":true},"max_errors":{"type":"string","required":true},"name":{"type":"string","optional":true},"priority":{"type":"number","optional":true},"service_role_arn":{"type":"string","required":true},"task_arn":{"type":"string","required":true},"task_type":{"type":"string","required":true},"window_id":{"type":"string","required":true}},"block_types":{"targets":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"min_items":1},"task_invocation_parameters":{"nesting_mode":"list","block":{"block_types":{"automation_parameters":{"nesting_mode":"list","block":{"attributes":{"document_version":{"type":"string","optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}},"max_items":1},"lambda_parameters":{"nesting_mode":"list","block":{"attributes":{"client_context":{"type":"string","optional":true},"payload":{"type":"string","optional":true,"sensitive":true},"qualifier":{"type":"string","optional":true}}},"max_items":1},"run_command_parameters":{"nesting_mode":"list","block":{"attributes":{"comment":{"type":"string","optional":true},"document_hash":{"type":"string","optional":true},"document_hash_type":{"type":"string","optional":true},"output_s3_bucket":{"type":"string","optional":true},"output_s3_key_prefix":{"type":"string","optional":true},"service_role_arn":{"type":"string","optional":true},"timeout_seconds":{"type":"number","optional":true}},"block_types":{"notification_config":{"nesting_mode":"list","block":{"attributes":{"notification_arn":{"type":"string","optional":true},"notification_events":{"type":["list","string"],"optional":true},"notification_type":{"type":"string","optional":true}}},"max_items":1},"parameter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}},"max_items":1},"step_functions_parameters":{"nesting_mode":"list","block":{"attributes":{"input":{"type":"string","optional":true,"sensitive":true},"name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_ssm_parameter":{"version":0,"block":{"attributes":{"allowed_pattern":{"type":"string","optional":true},"arn":{"type":"string","optional":true,"computed":true},"data_type":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"overwrite":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"tier":{"type":"string","optional":true},"type":{"type":"string","required":true},"value":{"type":"string","required":true,"sensitive":true},"version":{"type":"number","computed":true}}}},"aws_ssm_patch_baseline":{"version":0,"block":{"attributes":{"approved_patches":{"type":["set","string"],"optional":true},"approved_patches_compliance_level":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"operating_system":{"type":"string","optional":true},"rejected_patches":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"approval_rule":{"nesting_mode":"list","block":{"attributes":{"approve_after_days":{"type":"number","required":true},"compliance_level":{"type":"string","optional":true},"enable_non_security":{"type":"bool","optional":true}},"block_types":{"patch_filter":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"min_items":1,"max_items":10}}}},"global_filter":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"max_items":4}}}},"aws_ssm_patch_group":{"version":0,"block":{"attributes":{"baseline_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"patch_group":{"type":"string","required":true}}}},"aws_ssm_resource_data_sync":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"s3_destination":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","required":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"region":{"type":"string","required":true},"sync_format":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}}},"aws_storagegateway_cache":{"version":0,"block":{"attributes":{"disk_id":{"type":"string","required":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_storagegateway_cached_iscsi_volume":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"chap_enabled":{"type":"bool","computed":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"lun_number":{"type":"number","computed":true},"network_interface_id":{"type":"string","required":true},"network_interface_port":{"type":"number","computed":true},"snapshot_id":{"type":"string","optional":true},"source_volume_arn":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"target_arn":{"type":"string","computed":true},"target_name":{"type":"string","required":true},"volume_arn":{"type":"string","computed":true},"volume_id":{"type":"string","computed":true},"volume_size_in_bytes":{"type":"number","required":true}}}},"aws_storagegateway_gateway":{"version":0,"block":{"attributes":{"activation_key":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"cloudwatch_log_group_arn":{"type":"string","optional":true},"gateway_id":{"type":"string","computed":true},"gateway_ip_address":{"type":"string","optional":true,"computed":true},"gateway_name":{"type":"string","required":true},"gateway_timezone":{"type":"string","required":true},"gateway_type":{"type":"string","optional":true},"gateway_vpc_endpoint":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"medium_changer_type":{"type":"string","optional":true},"smb_guest_password":{"type":"string","optional":true,"sensitive":true},"tags":{"type":["map","string"],"optional":true},"tape_drive_type":{"type":"string","optional":true}},"block_types":{"smb_active_directory_settings":{"nesting_mode":"list","block":{"attributes":{"domain_name":{"type":"string","required":true},"password":{"type":"string","required":true,"sensitive":true},"username":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_storagegateway_nfs_file_share":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"client_list":{"type":["set","string"],"required":true},"default_storage_class":{"type":"string","optional":true},"fileshare_id":{"type":"string","computed":true},"gateway_arn":{"type":"string","required":true},"guess_mime_type_enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_encrypted":{"type":"bool","optional":true},"kms_key_arn":{"type":"string","optional":true},"location_arn":{"type":"string","required":true},"object_acl":{"type":"string","optional":true},"path":{"type":"string","computed":true},"read_only":{"type":"bool","optional":true},"requester_pays":{"type":"bool","optional":true},"role_arn":{"type":"string","required":true},"squash":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"nfs_file_share_defaults":{"nesting_mode":"list","block":{"attributes":{"directory_mode":{"type":"string","optional":true},"file_mode":{"type":"string","optional":true},"group_id":{"type":"number","optional":true},"owner_id":{"type":"number","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_storagegateway_smb_file_share":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"authentication":{"type":"string","optional":true},"default_storage_class":{"type":"string","optional":true},"fileshare_id":{"type":"string","computed":true},"gateway_arn":{"type":"string","required":true},"guess_mime_type_enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"invalid_user_list":{"type":["set","string"],"optional":true},"kms_encrypted":{"type":"bool","optional":true},"kms_key_arn":{"type":"string","optional":true},"location_arn":{"type":"string","required":true},"object_acl":{"type":"string","optional":true},"path":{"type":"string","computed":true},"read_only":{"type":"bool","optional":true},"requester_pays":{"type":"bool","optional":true},"role_arn":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"valid_user_list":{"type":["set","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_storagegateway_upload_buffer":{"version":0,"block":{"attributes":{"disk_id":{"type":"string","required":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_storagegateway_working_storage":{"version":0,"block":{"attributes":{"disk_id":{"type":"string","required":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_subnet":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_ipv6_address_on_creation":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"availability_zone_id":{"type":"string","optional":true,"computed":true},"cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block_association_id":{"type":"string","computed":true},"map_public_ip_on_launch":{"type":"bool","optional":true},"outpost_arn":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_swf_domain":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"workflow_execution_retention_period_in_days":{"type":"string","required":true}}}},"aws_transfer_server":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"endpoint_type":{"type":"string","optional":true},"force_destroy":{"type":"bool","optional":true},"host_key":{"type":"string","optional":true,"sensitive":true},"host_key_fingerprint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identity_provider_type":{"type":"string","optional":true},"invocation_role":{"type":"string","optional":true},"logging_role":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"url":{"type":"string","optional":true}},"block_types":{"endpoint_details":{"nesting_mode":"list","block":{"attributes":{"vpc_endpoint_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_transfer_ssh_key":{"version":0,"block":{"attributes":{"body":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"server_id":{"type":"string","required":true},"user_name":{"type":"string","required":true}}}},"aws_transfer_user":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"home_directory":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","optional":true},"role":{"type":"string","required":true},"server_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"user_name":{"type":"string","required":true}}}},"aws_volume_attachment":{"version":0,"block":{"attributes":{"device_name":{"type":"string","required":true},"force_detach":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","required":true},"skip_destroy":{"type":"bool","optional":true},"volume_id":{"type":"string","required":true}}}},"aws_vpc":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_generated_ipv6_cidr_block":{"type":"bool","optional":true},"cidr_block":{"type":"string","required":true},"default_network_acl_id":{"type":"string","computed":true},"default_route_table_id":{"type":"string","computed":true},"default_security_group_id":{"type":"string","computed":true},"dhcp_options_id":{"type":"string","computed":true},"enable_classiclink":{"type":"bool","optional":true,"computed":true},"enable_classiclink_dns_support":{"type":"bool","optional":true,"computed":true},"enable_dns_hostnames":{"type":"bool","optional":true,"computed":true},"enable_dns_support":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_tenancy":{"type":"string","optional":true},"ipv6_association_id":{"type":"string","computed":true},"ipv6_cidr_block":{"type":"string","computed":true},"main_route_table_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_vpc_dhcp_options":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","optional":true},"domain_name_servers":{"type":["list","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"netbios_name_servers":{"type":["list","string"],"optional":true},"netbios_node_type":{"type":"string","optional":true},"ntp_servers":{"type":["list","string"],"optional":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_vpc_dhcp_options_association":{"version":0,"block":{"attributes":{"dhcp_options_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"vpc_id":{"type":"string","required":true}}}},"aws_vpc_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_accept":{"type":"bool","optional":true},"cidr_blocks":{"type":["list","string"],"computed":true},"dns_entry":{"type":["list",["object",{"dns_name":"string","hosted_zone_id":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_ids":{"type":["set","string"],"computed":true},"owner_id":{"type":"string","computed":true},"policy":{"type":"string","optional":true,"computed":true},"prefix_list_id":{"type":"string","computed":true},"private_dns_enabled":{"type":"bool","optional":true},"requester_managed":{"type":"bool","computed":true},"route_table_ids":{"type":["set","string"],"optional":true,"computed":true},"security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"service_name":{"type":"string","required":true},"state":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_endpoint_type":{"type":"string","optional":true},"vpc_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_vpc_endpoint_connection_notification":{"version":0,"block":{"attributes":{"connection_events":{"type":["set","string"],"required":true},"connection_notification_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"notification_type":{"type":"string","computed":true},"state":{"type":"string","computed":true},"vpc_endpoint_id":{"type":"string","optional":true},"vpc_endpoint_service_id":{"type":"string","optional":true}}}},"aws_vpc_endpoint_route_table_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"route_table_id":{"type":"string","required":true},"vpc_endpoint_id":{"type":"string","required":true}}}},"aws_vpc_endpoint_service":{"version":0,"block":{"attributes":{"acceptance_required":{"type":"bool","required":true},"allowed_principals":{"type":["set","string"],"optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"base_endpoint_dns_names":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"manages_vpc_endpoints":{"type":"bool","computed":true},"network_load_balancer_arns":{"type":["set","string"],"required":true},"private_dns_name":{"type":"string","computed":true},"service_name":{"type":"string","computed":true},"service_type":{"type":"string","computed":true},"state":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_vpc_endpoint_service_allowed_principal":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"principal_arn":{"type":"string","required":true},"vpc_endpoint_service_id":{"type":"string","required":true}}}},"aws_vpc_endpoint_subnet_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","required":true},"vpc_endpoint_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_vpc_ipv4_cidr_block_association":{"version":0,"block":{"attributes":{"cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"vpc_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_vpc_peering_connection":{"version":0,"block":{"attributes":{"accept_status":{"type":"string","computed":true},"auto_accept":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"peer_owner_id":{"type":"string","optional":true,"computed":true},"peer_region":{"type":"string","optional":true,"computed":true},"peer_vpc_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}},"block_types":{"accepter":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1},"requester":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_vpc_peering_connection_accepter":{"version":0,"block":{"attributes":{"accept_status":{"type":"string","computed":true},"auto_accept":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"peer_owner_id":{"type":"string","computed":true},"peer_region":{"type":"string","computed":true},"peer_vpc_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"vpc_peering_connection_id":{"type":"string","required":true}},"block_types":{"accepter":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1},"requester":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_vpc_peering_connection_options":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"vpc_peering_connection_id":{"type":"string","required":true}},"block_types":{"accepter":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1},"requester":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_vpn_connection":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_gateway_configuration":{"type":"string","computed":true},"customer_gateway_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"routes":{"type":["set",["object",{"destination_cidr_block":"string","source":"string","state":"string"}]],"computed":true},"static_routes_only":{"type":"bool","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_attachment_id":{"type":"string","computed":true},"transit_gateway_id":{"type":"string","optional":true},"tunnel1_address":{"type":"string","computed":true},"tunnel1_bgp_asn":{"type":"string","computed":true},"tunnel1_bgp_holdtime":{"type":"number","computed":true},"tunnel1_cgw_inside_address":{"type":"string","computed":true},"tunnel1_inside_cidr":{"type":"string","optional":true,"computed":true},"tunnel1_preshared_key":{"type":"string","optional":true,"computed":true,"sensitive":true},"tunnel1_vgw_inside_address":{"type":"string","computed":true},"tunnel2_address":{"type":"string","computed":true},"tunnel2_bgp_asn":{"type":"string","computed":true},"tunnel2_bgp_holdtime":{"type":"number","computed":true},"tunnel2_cgw_inside_address":{"type":"string","computed":true},"tunnel2_inside_cidr":{"type":"string","optional":true,"computed":true},"tunnel2_preshared_key":{"type":"string","optional":true,"computed":true,"sensitive":true},"tunnel2_vgw_inside_address":{"type":"string","computed":true},"type":{"type":"string","required":true},"vgw_telemetry":{"type":["set",["object",{"accepted_route_count":"number","last_status_change":"string","outside_ip_address":"string","status":"string","status_message":"string"}]],"computed":true},"vpn_gateway_id":{"type":"string","optional":true}}}},"aws_vpn_connection_route":{"version":0,"block":{"attributes":{"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"vpn_connection_id":{"type":"string","required":true}}}},"aws_vpn_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","optional":true,"computed":true}}}},"aws_vpn_gateway_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"vpc_id":{"type":"string","required":true},"vpn_gateway_id":{"type":"string","required":true}}}},"aws_vpn_gateway_route_propagation":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"route_table_id":{"type":"string","required":true},"vpn_gateway_id":{"type":"string","required":true}}}},"aws_waf_byte_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"byte_match_tuples":{"nesting_mode":"set","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"target_string":{"type":"string","optional":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_geo_match_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"geo_match_constraint":{"nesting_mode":"set","block":{"attributes":{"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_waf_ipset":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"ip_set_descriptors":{"nesting_mode":"set","block":{"attributes":{"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_waf_rate_based_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"rate_key":{"type":"string","required":true},"rate_limit":{"type":"number","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"predicates":{"nesting_mode":"set","block":{"attributes":{"data_id":{"type":"string","required":true},"negated":{"type":"bool","required":true},"type":{"type":"string","required":true}}}}}}},"aws_waf_regex_match_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"regex_match_tuple":{"nesting_mode":"set","block":{"attributes":{"regex_pattern_set_id":{"type":"string","required":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_regex_pattern_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"regex_pattern_strings":{"type":["set","string"],"optional":true}}}},"aws_waf_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"predicates":{"nesting_mode":"set","block":{"attributes":{"data_id":{"type":"string","required":true},"negated":{"type":"bool","required":true},"type":{"type":"string","required":true}}}}}}},"aws_waf_rule_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"activated_rule":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"rule_id":{"type":"string","required":true},"type":{"type":"string","optional":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_size_constraint_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"size_constraints":{"nesting_mode":"set","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_sql_injection_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"sql_injection_match_tuples":{"nesting_mode":"set","block":{"attributes":{"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_web_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"min_items":1,"max_items":1},"logging_configuration":{"nesting_mode":"list","block":{"attributes":{"log_destination":{"type":"string","required":true}},"block_types":{"redacted_fields":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"set","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"max_items":1},"rules":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"rule_id":{"type":"string","required":true},"type":{"type":"string","optional":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1},"override_action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1}}}}}}},"aws_waf_xss_match_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"xss_match_tuples":{"nesting_mode":"set","block":{"attributes":{"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_byte_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"byte_match_tuples":{"nesting_mode":"set","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"target_string":{"type":"string","optional":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_geo_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"geo_match_constraint":{"nesting_mode":"set","block":{"attributes":{"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_wafregional_ipset":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"ip_set_descriptor":{"nesting_mode":"set","block":{"attributes":{"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_wafregional_rate_based_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"rate_key":{"type":"string","required":true},"rate_limit":{"type":"number","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"predicate":{"nesting_mode":"set","block":{"attributes":{"data_id":{"type":"string","required":true},"negated":{"type":"bool","required":true},"type":{"type":"string","required":true}}}}}}},"aws_wafregional_regex_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"regex_match_tuple":{"nesting_mode":"set","block":{"attributes":{"regex_pattern_set_id":{"type":"string","required":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_regex_pattern_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"regex_pattern_strings":{"type":["set","string"],"optional":true}}}},"aws_wafregional_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"predicate":{"nesting_mode":"set","block":{"attributes":{"data_id":{"type":"string","required":true},"negated":{"type":"bool","required":true},"type":{"type":"string","required":true}}}}}}},"aws_wafregional_rule_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"activated_rule":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"rule_id":{"type":"string","required":true},"type":{"type":"string","optional":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_size_constraint_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"size_constraints":{"nesting_mode":"set","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_sql_injection_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"sql_injection_match_tuple":{"nesting_mode":"set","block":{"attributes":{"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_web_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"min_items":1,"max_items":1},"logging_configuration":{"nesting_mode":"list","block":{"attributes":{"log_destination":{"type":"string","required":true}},"block_types":{"redacted_fields":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"set","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"max_items":1},"rule":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"rule_id":{"type":"string","required":true},"type":{"type":"string","optional":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1},"override_action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1}}}}}}},"aws_wafregional_web_acl_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_arn":{"type":"string","required":true},"web_acl_id":{"type":"string","required":true}}}},"aws_wafregional_xss_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"xss_match_tuple":{"nesting_mode":"set","block":{"attributes":{"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafv2_ip_set":{"version":0,"block":{"attributes":{"addresses":{"type":["set","string"],"optional":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"ip_address_version":{"type":"string","required":true},"lock_token":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_wafv2_regex_pattern_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"lock_token":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"regular_expression":{"nesting_mode":"set","block":{"attributes":{"regex_string":{"type":"string","required":true}}},"max_items":10}}}},"aws_wafv2_rule_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"capacity":{"type":"number","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"lock_token":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"rule":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"priority":{"type":"number","required":true}},"block_types":{"action":{"nesting_mode":"list","block":{"block_types":{"allow":{"nesting_mode":"list","block":{},"max_items":1},"block":{"nesting_mode":"list","block":{},"max_items":1},"count":{"nesting_mode":"list","block":{},"max_items":1}}},"min_items":1,"max_items":1},"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1,"max_items":1},"visibility_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_metrics_enabled":{"type":"bool","required":true},"metric_name":{"type":"string","required":true},"sampled_requests_enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"visibility_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_metrics_enabled":{"type":"bool","required":true},"metric_name":{"type":"string","required":true},"sampled_requests_enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"aws_wafv2_web_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"capacity":{"type":"number","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"lock_token":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"block_types":{"allow":{"nesting_mode":"list","block":{},"max_items":1},"block":{"nesting_mode":"list","block":{},"max_items":1}}},"min_items":1,"max_items":1},"rule":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"priority":{"type":"number","required":true}},"block_types":{"action":{"nesting_mode":"list","block":{"block_types":{"allow":{"nesting_mode":"list","block":{},"max_items":1},"block":{"nesting_mode":"list","block":{},"max_items":1},"count":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"override_action":{"nesting_mode":"list","block":{"block_types":{"count":{"nesting_mode":"list","block":{},"max_items":1},"none":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"managed_rule_group_statement":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true},"vendor_name":{"type":"string","required":true}},"block_types":{"excluded_rule":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}}}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"rate_based_statement":{"nesting_mode":"list","block":{"attributes":{"aggregate_key_type":{"type":"string","optional":true},"limit":{"type":"number","required":true}},"block_types":{"scope_down_statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"max_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"rule_group_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"excluded_rule":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}}}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1,"max_items":1},"visibility_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_metrics_enabled":{"type":"bool","required":true},"metric_name":{"type":"string","required":true},"sampled_requests_enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"visibility_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_metrics_enabled":{"type":"bool","required":true},"metric_name":{"type":"string","required":true},"sampled_requests_enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"aws_wafv2_web_acl_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_arn":{"type":"string","required":true},"web_acl_arn":{"type":"string","required":true}}}},"aws_wafv2_web_acl_logging_configuration":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"log_destination_configs":{"type":["set","string"],"description":"AWS Kinesis Firehose Delivery Stream ARNs","required":true},"resource_arn":{"type":"string","description":"AWS WebACL ARN","required":true}},"block_types":{"redacted_fields":{"nesting_mode":"set","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":100}}}},"aws_worklink_fleet":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"audit_stream_arn":{"type":"string","optional":true},"company_code":{"type":"string","computed":true},"created_time":{"type":"string","computed":true},"device_ca_certificate":{"type":"string","optional":true},"display_name":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_time":{"type":"string","computed":true},"name":{"type":"string","required":true},"optimize_for_end_user_location":{"type":"bool","optional":true}},"block_types":{"identity_provider":{"nesting_mode":"list","block":{"attributes":{"saml_metadata":{"type":"string","required":true},"type":{"type":"string","required":true}}},"max_items":1},"network":{"nesting_mode":"list","block":{"attributes":{"security_group_ids":{"type":["set","string"],"required":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_worklink_website_certificate_authority_association":{"version":0,"block":{"attributes":{"certificate":{"type":"string","required":true},"display_name":{"type":"string","optional":true},"fleet_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"website_ca_id":{"type":"string","computed":true}}}},"aws_workspaces_directory":{"version":0,"block":{"attributes":{"alias":{"type":"string","computed":true},"customer_user_name":{"type":"string","computed":true},"directory_id":{"type":"string","required":true},"directory_name":{"type":"string","computed":true},"directory_type":{"type":"string","computed":true},"dns_ip_addresses":{"type":["set","string"],"computed":true},"iam_role_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_group_ids":{"type":["set","string"],"computed":true},"registration_code":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"workspace_security_group_id":{"type":"string","computed":true}},"block_types":{"self_service_permissions":{"nesting_mode":"list","block":{"attributes":{"change_compute_type":{"type":"bool","optional":true},"increase_volume_size":{"type":"bool","optional":true},"rebuild_workspace":{"type":"bool","optional":true},"restart_workspace":{"type":"bool","optional":true},"switch_running_mode":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_workspaces_ip_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"rules":{"nesting_mode":"set","block":{"attributes":{"description":{"type":"string","optional":true},"source":{"type":"string","required":true}}}}}}},"aws_workspaces_workspace":{"version":0,"block":{"attributes":{"bundle_id":{"type":"string","required":true},"computer_name":{"type":"string","computed":true},"directory_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","computed":true},"root_volume_encryption_enabled":{"type":"bool","optional":true},"state":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"user_name":{"type":"string","required":true},"user_volume_encryption_enabled":{"type":"bool","optional":true},"volume_encryption_key":{"type":"string","optional":true}},"block_types":{"workspace_properties":{"nesting_mode":"list","block":{"attributes":{"compute_type_name":{"type":"string","optional":true},"root_volume_size_gib":{"type":"number","optional":true},"running_mode":{"type":"string","optional":true},"running_mode_auto_stop_timeout_in_minutes":{"type":"number","optional":true,"computed":true},"user_volume_size_gib":{"type":"number","optional":true}}},"max_items":1}}}},"aws_xray_sampling_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"attributes":{"type":["map","string"],"optional":true},"fixed_rate":{"type":"number","required":true},"host":{"type":"string","required":true},"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"priority":{"type":"number","required":true},"reservoir_size":{"type":"number","required":true},"resource_arn":{"type":"string","required":true},"rule_name":{"type":"string","optional":true},"service_name":{"type":"string","required":true},"service_type":{"type":"string","required":true},"url_path":{"type":"string","required":true},"version":{"type":"number","required":true}}}}},"data_source_schemas":{"aws_acm_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"key_types":{"type":["set","string"],"optional":true},"most_recent":{"type":"bool","optional":true},"statuses":{"type":["list","string"],"optional":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"types":{"type":["list","string"],"optional":true}}}},"aws_acmpca_certificate_authority":{"version":1,"block":{"attributes":{"arn":{"type":"string","required":true},"certificate":{"type":"string","computed":true},"certificate_chain":{"type":"string","computed":true},"certificate_signing_request":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"not_after":{"type":"string","computed":true},"not_before":{"type":"string","computed":true},"serial":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"type":{"type":"string","computed":true}},"block_types":{"revocation_configuration":{"nesting_mode":"list","block":{"block_types":{"crl_configuration":{"nesting_mode":"list","block":{"attributes":{"custom_cname":{"type":"string","computed":true},"enabled":{"type":"bool","computed":true},"expiration_in_days":{"type":"number","computed":true},"s3_bucket_name":{"type":"string","computed":true}}}}}}}}}},"aws_alb":{"version":0,"block":{"attributes":{"access_logs":{"type":["list",["object",{"bucket":"string","enabled":"bool","prefix":"string"}]],"computed":true},"arn":{"type":"string","optional":true,"computed":true},"arn_suffix":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"drop_invalid_header_fields":{"type":"bool","computed":true},"enable_deletion_protection":{"type":"bool","computed":true},"enable_http2":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","computed":true},"internal":{"type":"bool","computed":true},"ip_address_type":{"type":"string","computed":true},"load_balancer_type":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_mapping":{"type":["set",["object",{"allocation_id":"string","private_ipv4_address":"string","subnet_id":"string"}]],"computed":true},"subnets":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","computed":true},"zone_id":{"type":"string","computed":true}}}},"aws_alb_listener":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"certificate_arn":{"type":"string","computed":true},"default_action":{"type":["list",["object",{"authenticate_cognito":["list",["object",{"authentication_request_extra_params":["map","string"],"on_unauthenticated_request":"string","scope":"string","session_cookie_name":"string","session_timeout":"number","user_pool_arn":"string","user_pool_client_id":"string","user_pool_domain":"string"}]],"authenticate_oidc":["list",["object",{"authentication_request_extra_params":["map","string"],"authorization_endpoint":"string","client_id":"string","client_secret":"string","issuer":"string","on_unauthenticated_request":"string","scope":"string","session_cookie_name":"string","session_timeout":"number","token_endpoint":"string","user_info_endpoint":"string"}]],"fixed_response":["list",["object",{"content_type":"string","message_body":"string","status_code":"string"}]],"order":"number","redirect":["list",["object",{"host":"string","path":"string","port":"string","protocol":"string","query":"string","status_code":"string"}]],"target_group_arn":"string","type":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"load_balancer_arn":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"protocol":{"type":"string","computed":true},"ssl_policy":{"type":"string","computed":true}}}},"aws_alb_target_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"arn_suffix":{"type":"string","computed":true},"deregistration_delay":{"type":"number","computed":true},"health_check":{"type":["list",["object",{"enabled":"bool","healthy_threshold":"number","interval":"number","matcher":"string","path":"string","port":"string","protocol":"string","timeout":"number","unhealthy_threshold":"number"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"lambda_multi_value_headers_enabled":{"type":"bool","computed":true},"load_balancing_algorithm_type":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","computed":true},"protocol":{"type":"string","computed":true},"proxy_protocol_v2":{"type":"bool","computed":true},"slow_start":{"type":"number","computed":true},"stickiness":{"type":["list",["object",{"cookie_duration":"number","enabled":"bool","type":"string"}]],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"target_type":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_ami":{"version":0,"block":{"attributes":{"architecture":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"block_device_mappings":{"type":["set",["object",{"device_name":"string","ebs":["map","string"],"no_device":"string","virtual_name":"string"}]],"computed":true},"creation_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"executable_users":{"type":["list","string"],"optional":true},"hypervisor":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","computed":true},"image_location":{"type":"string","computed":true},"image_owner_alias":{"type":"string","computed":true},"image_type":{"type":"string","computed":true},"kernel_id":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"name":{"type":"string","computed":true},"name_regex":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"owners":{"type":["list","string"],"required":true},"platform":{"type":"string","computed":true},"product_codes":{"type":["set",["object",{"product_code_id":"string","product_code_type":"string"}]],"computed":true},"public":{"type":"bool","computed":true},"ramdisk_id":{"type":"string","computed":true},"root_device_name":{"type":"string","computed":true},"root_device_type":{"type":"string","computed":true},"root_snapshot_id":{"type":"string","computed":true},"sriov_net_support":{"type":"string","computed":true},"state":{"type":"string","computed":true},"state_reason":{"type":["map","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"virtualization_type":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ami_ids":{"version":0,"block":{"attributes":{"executable_users":{"type":["list","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["list","string"],"computed":true},"name_regex":{"type":"string","optional":true},"owners":{"type":["list","string"],"required":true},"sort_ascending":{"type":"bool","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_api_gateway_api_key":{"version":0,"block":{"attributes":{"created_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"enabled":{"type":"bool","computed":true},"id":{"type":"string","required":true},"last_updated_date":{"type":"string","computed":true},"name":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"value":{"type":"string","computed":true,"sensitive":true}}}},"aws_api_gateway_resource":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"parent_id":{"type":"string","computed":true},"path":{"type":"string","required":true},"path_part":{"type":"string","computed":true},"rest_api_id":{"type":"string","required":true}}}},"aws_api_gateway_rest_api":{"version":0,"block":{"attributes":{"api_key_source":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"binary_media_types":{"type":["list","string"],"computed":true},"description":{"type":"string","computed":true},"endpoint_configuration":{"type":["list",["object",{"types":["list","string"],"vpc_endpoint_ids":["set","string"]}]],"computed":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"minimum_compression_size":{"type":"number","computed":true},"name":{"type":"string","required":true},"policy":{"type":"string","computed":true},"root_resource_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_api_gateway_vpc_link":{"version":0,"block":{"attributes":{"description":{"type":"string","computed":true},"id":{"type":"string","computed":true},"name":{"type":"string","required":true},"status":{"type":"string","computed":true},"status_message":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"target_arns":{"type":["set","string"],"computed":true}}}},"aws_arn":{"version":0,"block":{"attributes":{"account":{"type":"string","computed":true},"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"partition":{"type":"string","computed":true},"region":{"type":"string","computed":true},"resource":{"type":"string","computed":true},"service":{"type":"string","computed":true}}}},"aws_autoscaling_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"default_cooldown":{"type":"number","computed":true},"desired_capacity":{"type":"number","computed":true},"health_check_grace_period":{"type":"number","computed":true},"health_check_type":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"launch_configuration":{"type":"string","computed":true},"load_balancers":{"type":["set","string"],"computed":true},"max_size":{"type":"number","computed":true},"min_size":{"type":"number","computed":true},"name":{"type":"string","required":true},"new_instances_protected_from_scale_in":{"type":"bool","computed":true},"placement_group":{"type":"string","computed":true},"service_linked_role_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"target_group_arns":{"type":["set","string"],"computed":true},"termination_policies":{"type":["set","string"],"computed":true},"vpc_zone_identifier":{"type":"string","computed":true}}}},"aws_autoscaling_groups":{"version":0,"block":{"attributes":{"arns":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"names":{"type":["list","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_availability_zone":{"version":0,"block":{"attributes":{"all_availability_zones":{"type":"bool","optional":true},"group_name":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_suffix":{"type":"string","computed":true},"network_border_group":{"type":"string","computed":true},"opt_in_status":{"type":"string","computed":true},"region":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_availability_zones":{"version":0,"block":{"attributes":{"all_availability_zones":{"type":"bool","optional":true},"exclude_names":{"type":["set","string"],"optional":true},"exclude_zone_ids":{"type":["set","string"],"optional":true},"group_names":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"names":{"type":["list","string"],"computed":true},"state":{"type":"string","optional":true},"zone_ids":{"type":["list","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_backup_plan":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"plan_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"version":{"type":"string","computed":true}}}},"aws_backup_selection":{"version":0,"block":{"attributes":{"iam_role_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"plan_id":{"type":"string","required":true},"resources":{"type":["set","string"],"computed":true},"selection_id":{"type":"string","required":true}}}},"aws_backup_vault":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","computed":true},"name":{"type":"string","required":true},"recovery_points":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_batch_compute_environment":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compute_environment_name":{"type":"string","required":true},"ecs_cluster_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"service_role":{"type":"string","computed":true},"state":{"type":"string","computed":true},"status":{"type":"string","computed":true},"status_reason":{"type":"string","computed":true},"type":{"type":"string","computed":true}}}},"aws_batch_job_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compute_environment_order":{"type":["list",["object",{"compute_environment":"string","order":"number"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"priority":{"type":"number","computed":true},"state":{"type":"string","computed":true},"status":{"type":"string","computed":true},"status_reason":{"type":"string","computed":true}}}},"aws_billing_service_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_caller_identity":{"version":0,"block":{"attributes":{"account_id":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"user_id":{"type":"string","computed":true}}}},"aws_canonical_user_id":{"version":0,"block":{"attributes":{"display_name":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_cloudformation_export":{"version":0,"block":{"attributes":{"exporting_stack_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"value":{"type":"string","computed":true}}}},"aws_cloudformation_stack":{"version":0,"block":{"attributes":{"capabilities":{"type":["set","string"],"computed":true},"description":{"type":"string","computed":true},"disable_rollback":{"type":"bool","computed":true},"iam_role_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"notification_arns":{"type":["set","string"],"computed":true},"outputs":{"type":["map","string"],"computed":true},"parameters":{"type":["map","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"template_body":{"type":"string","computed":true},"timeout_in_minutes":{"type":"number","computed":true}}}},"aws_cloudfront_distribution":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","computed":true},"enabled":{"type":"bool","computed":true},"etag":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","required":true},"in_progress_validation_batches":{"type":"number","computed":true},"last_modified_time":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_cloudhsm_v2_cluster":{"version":0,"block":{"attributes":{"cluster_certificates":{"type":["list",["object",{"aws_hardware_certificate":"string","cluster_certificate":"string","cluster_csr":"string","hsm_certificate":"string","manufacturer_hardware_certificate":"string"}]],"computed":true},"cluster_id":{"type":"string","required":true},"cluster_state":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"security_group_id":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_cloudtrail_service_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_cloudwatch_log_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_time":{"type":"number","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"name":{"type":"string","required":true},"retention_in_days":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_codecommit_repository":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"clone_url_http":{"type":"string","computed":true},"clone_url_ssh":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"repository_id":{"type":"string","computed":true},"repository_name":{"type":"string","required":true}}}},"aws_cognito_user_pools":{"version":0,"block":{"attributes":{"arns":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"name":{"type":"string","required":true}}}},"aws_cur_report_definition":{"version":0,"block":{"attributes":{"additional_artifacts":{"type":["set","string"],"computed":true},"additional_schema_elements":{"type":["set","string"],"computed":true},"compression":{"type":"string","computed":true},"format":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"report_name":{"type":"string","required":true},"s3_bucket":{"type":"string","computed":true},"s3_prefix":{"type":"string","computed":true},"s3_region":{"type":"string","computed":true},"time_unit":{"type":"string","computed":true}}}},"aws_customer_gateway":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bgp_asn":{"type":"number","computed":true},"id":{"type":"string","optional":true},"ip_address":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"type":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_db_cluster_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zones":{"type":["list","string"],"computed":true},"db_cluster_identifier":{"type":"string","optional":true},"db_cluster_snapshot_arn":{"type":"string","computed":true},"db_cluster_snapshot_identifier":{"type":"string","optional":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"include_public":{"type":"bool","optional":true},"include_shared":{"type":"bool","optional":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"port":{"type":"number","computed":true},"snapshot_create_time":{"type":"string","computed":true},"snapshot_type":{"type":"string","optional":true},"source_db_cluster_snapshot_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_db_event_categories":{"version":0,"block":{"attributes":{"event_categories":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"source_type":{"type":"string","optional":true}}}},"aws_db_instance":{"version":1,"block":{"attributes":{"address":{"type":"string","computed":true},"allocated_storage":{"type":"number","computed":true},"auto_minor_version_upgrade":{"type":"bool","computed":true},"availability_zone":{"type":"string","computed":true},"backup_retention_period":{"type":"number","computed":true},"ca_cert_identifier":{"type":"string","computed":true},"db_cluster_identifier":{"type":"string","computed":true},"db_instance_arn":{"type":"string","computed":true},"db_instance_class":{"type":"string","computed":true},"db_instance_identifier":{"type":"string","required":true},"db_instance_port":{"type":"number","computed":true},"db_name":{"type":"string","computed":true},"db_parameter_groups":{"type":["list","string"],"computed":true},"db_security_groups":{"type":["list","string"],"computed":true},"db_subnet_group":{"type":"string","computed":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"iops":{"type":"number","computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"master_username":{"type":"string","computed":true},"monitoring_interval":{"type":"number","computed":true},"monitoring_role_arn":{"type":"string","computed":true},"multi_az":{"type":"bool","computed":true},"option_group_memberships":{"type":["list","string"],"computed":true},"port":{"type":"number","computed":true},"preferred_backup_window":{"type":"string","computed":true},"preferred_maintenance_window":{"type":"string","computed":true},"publicly_accessible":{"type":"bool","computed":true},"replicate_source_db":{"type":"string","computed":true},"resource_id":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"storage_type":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"timezone":{"type":"string","computed":true},"vpc_security_groups":{"type":["list","string"],"computed":true}}}},"aws_db_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zone":{"type":"string","computed":true},"db_instance_identifier":{"type":"string","optional":true},"db_snapshot_arn":{"type":"string","computed":true},"db_snapshot_identifier":{"type":"string","optional":true},"encrypted":{"type":"bool","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"include_public":{"type":"bool","optional":true},"include_shared":{"type":"bool","optional":true},"iops":{"type":"number","computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"option_group_name":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_create_time":{"type":"string","computed":true},"snapshot_type":{"type":"string","optional":true},"source_db_snapshot_identifier":{"type":"string","computed":true},"source_region":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_type":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_directory_service_directory":{"version":0,"block":{"attributes":{"access_url":{"type":"string","computed":true},"alias":{"type":"string","computed":true},"connect_settings":{"type":["list",["object",{"availability_zones":["set","string"],"connect_ips":["set","string"],"customer_dns_ips":["set","string"],"customer_username":"string","subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true},"description":{"type":"string","computed":true},"directory_id":{"type":"string","required":true},"dns_ip_addresses":{"type":["set","string"],"computed":true},"edition":{"type":"string","computed":true},"enable_sso":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"security_group_id":{"type":"string","computed":true},"short_name":{"type":"string","computed":true},"size":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","computed":true},"vpc_settings":{"type":["list",["object",{"availability_zones":["set","string"],"subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true}}}},"aws_dx_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","computed":true}}}},"aws_dynamodb_table":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"attribute":{"type":["set",["object",{"name":"string","type":"string"}]],"computed":true},"billing_mode":{"type":"string","computed":true},"global_secondary_index":{"type":["set",["object",{"hash_key":"string","name":"string","non_key_attributes":["list","string"],"projection_type":"string","range_key":"string","read_capacity":"number","write_capacity":"number"}]],"computed":true},"hash_key":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"local_secondary_index":{"type":["set",["object",{"name":"string","non_key_attributes":["list","string"],"projection_type":"string","range_key":"string"}]],"computed":true},"name":{"type":"string","required":true},"point_in_time_recovery":{"type":["list",["object",{"enabled":"bool"}]],"computed":true},"range_key":{"type":"string","computed":true},"read_capacity":{"type":"number","computed":true},"replica":{"type":["set",["object",{"region_name":"string"}]],"computed":true},"stream_arn":{"type":"string","computed":true},"stream_enabled":{"type":"bool","computed":true},"stream_label":{"type":"string","computed":true},"stream_view_type":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"ttl":{"type":["set",["object",{"attribute_name":"string","enabled":"bool"}]],"computed":true},"write_capacity":{"type":"number","computed":true}},"block_types":{"server_side_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","computed":true},"kms_key_arn":{"type":"string","computed":true}}},"max_items":1}}}},"aws_ebs_default_kms_key":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"key_arn":{"type":"string","computed":true}}}},"aws_ebs_encryption_by_default":{"version":0,"block":{"attributes":{"enabled":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ebs_snapshot":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"data_encryption_key_id":{"type":"string","computed":true},"description":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"owner_alias":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"owners":{"type":["list","string"],"optional":true},"restorable_by_user_ids":{"type":["list","string"],"optional":true},"snapshot_id":{"type":"string","computed":true},"snapshot_ids":{"type":["list","string"],"optional":true},"state":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ebs_snapshot_ids":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["list","string"],"computed":true},"owners":{"type":["list","string"],"optional":true},"restorable_by_user_ids":{"type":["list","string"],"optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ebs_volume":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"iops":{"type":"number","computed":true},"kms_key_id":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"multi_attach_enabled":{"type":"bool","computed":true},"outpost_arn":{"type":"string","computed":true},"size":{"type":"number","computed":true},"snapshot_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_type":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ebs_volumes":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_coip_pool":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_gateway_route_table_id":{"type":"string","optional":true,"computed":true},"pool_cidrs":{"type":["set","string"],"computed":true},"pool_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_coip_pools":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"pool_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_instance_type_offering":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","computed":true},"location_type":{"type":"string","optional":true},"preferred_instance_types":{"type":["list","string"],"optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_instance_type_offerings":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_types":{"type":["set","string"],"computed":true},"location_type":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_local_gateway":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"outpost_arn":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_route_table":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_gateway_id":{"type":"string","optional":true,"computed":true},"local_gateway_route_table_id":{"type":"string","optional":true,"computed":true},"outpost_arn":{"type":"string","optional":true,"computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_route_tables":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_virtual_interface":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_address":{"type":"string","computed":true},"local_bgp_asn":{"type":"number","computed":true},"local_gateway_id":{"type":"string","computed":true},"local_gateway_virtual_interface_ids":{"type":["set","string"],"computed":true},"peer_address":{"type":"string","computed":true},"peer_bgp_asn":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vlan":{"type":"number","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_virtual_interface_group":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_gateway_id":{"type":"string","optional":true,"computed":true},"local_gateway_virtual_interface_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_virtual_interface_groups":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"local_gateway_virtual_interface_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateways":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_spot_price":{"version":0,"block":{"attributes":{"availability_zone":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","optional":true},"spot_price":{"type":"string","computed":true},"spot_price_timestamp":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"number","computed":true},"arn":{"type":"string","computed":true},"association_default_route_table_id":{"type":"string","computed":true},"auto_accept_shared_attachments":{"type":"string","computed":true},"default_route_table_association":{"type":"string","computed":true},"default_route_table_propagation":{"type":"string","computed":true},"description":{"type":"string","computed":true},"dns_support":{"type":"string","computed":true},"id":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"propagation_default_route_table_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpn_ecmp_support":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_dx_gateway_attachment":{"version":0,"block":{"attributes":{"dx_gateway_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_peering_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true},"peer_account_id":{"type":"string","computed":true},"peer_region":{"type":"string","computed":true},"peer_transit_gateway_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_route_table":{"version":0,"block":{"attributes":{"default_association_route_table":{"type":"bool","computed":true},"default_propagation_route_table":{"type":"bool","computed":true},"id":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_vpc_attachment":{"version":0,"block":{"attributes":{"dns_support":{"type":"string","computed":true},"id":{"type":"string","optional":true},"ipv6_support":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true},"vpc_owner_id":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_vpn_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","optional":true},"vpn_connection_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ecr_authorization_token":{"version":0,"block":{"attributes":{"authorization_token":{"type":"string","computed":true,"sensitive":true},"expires_at":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"password":{"type":"string","computed":true,"sensitive":true},"proxy_endpoint":{"type":"string","computed":true},"registry_id":{"type":"string","optional":true},"user_name":{"type":"string","computed":true}}}},"aws_ecr_image":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"image_digest":{"type":"string","optional":true,"computed":true},"image_pushed_at":{"type":"number","computed":true},"image_size_in_bytes":{"type":"number","computed":true},"image_tag":{"type":"string","optional":true},"image_tags":{"type":["list","string"],"computed":true},"registry_id":{"type":"string","optional":true,"computed":true},"repository_name":{"type":"string","required":true}}}},"aws_ecr_repository":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"encryption_configuration":{"type":["list",["object",{"encryption_type":"string","kms_key":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"image_scanning_configuration":{"type":["list",["object",{"scan_on_push":"bool"}]],"computed":true},"image_tag_mutability":{"type":"string","computed":true},"name":{"type":"string","required":true},"registry_id":{"type":"string","optional":true,"computed":true},"repository_url":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_ecs_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"pending_tasks_count":{"type":"number","computed":true},"registered_container_instances_count":{"type":"number","computed":true},"running_tasks_count":{"type":"number","computed":true},"setting":{"type":["set",["object",{"name":"string","value":"string"}]],"computed":true},"status":{"type":"string","computed":true}}}},"aws_ecs_container_definition":{"version":0,"block":{"attributes":{"container_name":{"type":"string","required":true},"cpu":{"type":"number","computed":true},"disable_networking":{"type":"bool","computed":true},"docker_labels":{"type":["map","string"],"computed":true},"environment":{"type":["map","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"image":{"type":"string","computed":true},"image_digest":{"type":"string","computed":true},"memory":{"type":"number","computed":true},"memory_reservation":{"type":"number","computed":true},"task_definition":{"type":"string","required":true}}}},"aws_ecs_service":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cluster_arn":{"type":"string","required":true},"desired_count":{"type":"number","computed":true},"id":{"type":"string","optional":true,"computed":true},"launch_type":{"type":"string","computed":true},"scheduling_strategy":{"type":"string","computed":true},"service_name":{"type":"string","required":true},"task_definition":{"type":"string","computed":true}}}},"aws_ecs_task_definition":{"version":1,"block":{"attributes":{"family":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"network_mode":{"type":"string","computed":true},"revision":{"type":"number","computed":true},"status":{"type":"string","computed":true},"task_definition":{"type":"string","required":true},"task_role_arn":{"type":"string","computed":true}}}},"aws_efs_access_point":{"version":0,"block":{"attributes":{"access_point_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"file_system_arn":{"type":"string","computed":true},"file_system_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"posix_user":{"type":["list",["object",{"gid":"number","secondary_gids":["set","number"],"uid":"number"}]],"computed":true},"root_directory":{"type":["list",["object",{"creation_info":["list",["object",{"owner_gid":"number","owner_uid":"number","permissions":"string"}]],"path":"string"}]],"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_efs_access_points":{"version":0,"block":{"attributes":{"arns":{"type":["set","string"],"computed":true},"file_system_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true}}}},"aws_efs_file_system":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_token":{"type":"string","optional":true,"computed":true},"dns_name":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"file_system_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"lifecycle_policy":{"type":["list",["object",{"transition_to_ia":"string"}]],"computed":true},"performance_mode":{"type":"string","computed":true},"provisioned_throughput_in_mibps":{"type":"number","computed":true},"size_in_bytes":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"throughput_mode":{"type":"string","computed":true}}}},"aws_efs_mount_target":{"version":0,"block":{"attributes":{"availability_zone_id":{"type":"string","computed":true},"availability_zone_name":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"file_system_arn":{"type":"string","computed":true},"file_system_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","computed":true},"mount_target_dns_name":{"type":"string","computed":true},"mount_target_id":{"type":"string","required":true},"network_interface_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_id":{"type":"string","computed":true}}}},"aws_eip":{"version":0,"block":{"attributes":{"association_id":{"type":"string","computed":true},"customer_owned_ip":{"type":"string","computed":true},"customer_owned_ipv4_pool":{"type":"string","computed":true},"domain":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","computed":true},"network_interface_id":{"type":"string","computed":true},"network_interface_owner_id":{"type":"string","computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","optional":true,"computed":true},"public_ipv4_pool":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_eks_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_authority":{"type":["list",["object",{"data":"string"}]],"computed":true},"created_at":{"type":"string","computed":true},"enabled_cluster_log_types":{"type":["set","string"],"computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identity":{"type":["list",["object",{"oidc":["list",["object",{"issuer":"string"}]]}]],"computed":true},"name":{"type":"string","required":true},"platform_version":{"type":"string","computed":true},"role_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"version":{"type":"string","computed":true},"vpc_config":{"type":["list",["object",{"cluster_security_group_id":"string","endpoint_private_access":"bool","endpoint_public_access":"bool","public_access_cidrs":["set","string"],"security_group_ids":["set","string"],"subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true}}}},"aws_eks_cluster_auth":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"token":{"type":"string","computed":true,"sensitive":true}}}},"aws_elastic_beanstalk_application":{"version":0,"block":{"attributes":{"appversion_lifecycle":{"type":["list",["object",{"delete_source_from_s3":"bool","max_age_in_days":"number","max_count":"number","service_role":"string"}]],"computed":true},"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_elastic_beanstalk_hosted_zone":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_elastic_beanstalk_solution_stack":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"most_recent":{"type":"bool","optional":true},"name":{"type":"string","computed":true},"name_regex":{"type":"string","required":true}}}},"aws_elasticache_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","computed":true},"cache_nodes":{"type":["list",["object",{"address":"string","availability_zone":"string","id":"string","port":"number"}]],"computed":true},"cluster_address":{"type":"string","computed":true},"cluster_id":{"type":"string","required":true},"configuration_endpoint":{"type":"string","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"maintenance_window":{"type":"string","computed":true},"node_type":{"type":"string","computed":true},"notification_topic_arn":{"type":"string","computed":true},"num_cache_nodes":{"type":"number","computed":true},"parameter_group_name":{"type":"string","computed":true},"port":{"type":"number","computed":true},"replication_group_id":{"type":"string","computed":true},"security_group_ids":{"type":["set","string"],"computed":true},"security_group_names":{"type":["set","string"],"computed":true},"snapshot_retention_limit":{"type":"number","computed":true},"snapshot_window":{"type":"string","computed":true},"subnet_group_name":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_elasticache_replication_group":{"version":1,"block":{"attributes":{"auth_token_enabled":{"type":"bool","computed":true},"automatic_failover_enabled":{"type":"bool","computed":true},"configuration_endpoint_address":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"member_clusters":{"type":["set","string"],"computed":true},"node_type":{"type":"string","computed":true},"number_cache_clusters":{"type":"number","computed":true},"port":{"type":"number","computed":true},"primary_endpoint_address":{"type":"string","computed":true},"replication_group_description":{"type":"string","computed":true},"replication_group_id":{"type":"string","required":true},"snapshot_retention_limit":{"type":"number","computed":true},"snapshot_window":{"type":"string","computed":true}}}},"aws_elasticsearch_domain":{"version":0,"block":{"attributes":{"access_policies":{"type":"string","computed":true},"advanced_options":{"type":["map","string"],"computed":true},"advanced_security_options":{"type":["list",["object",{"enabled":"bool","internal_user_database_enabled":"bool"}]],"computed":true},"arn":{"type":"string","computed":true},"cluster_config":{"type":["list",["object",{"dedicated_master_count":"number","dedicated_master_enabled":"bool","dedicated_master_type":"string","instance_count":"number","instance_type":"string","warm_count":"number","warm_enabled":"bool","warm_type":"string","zone_awareness_config":["list",["object",{"availability_zone_count":"number"}]],"zone_awareness_enabled":"bool"}]],"computed":true},"cognito_options":{"type":["list",["object",{"enabled":"bool","identity_pool_id":"string","role_arn":"string","user_pool_id":"string"}]],"computed":true},"created":{"type":"bool","computed":true},"deleted":{"type":"bool","computed":true},"domain_id":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"ebs_options":{"type":["list",["object",{"ebs_enabled":"bool","iops":"number","volume_size":"number","volume_type":"string"}]],"computed":true},"elasticsearch_version":{"type":"string","computed":true},"encryption_at_rest":{"type":["list",["object",{"enabled":"bool","kms_key_id":"string"}]],"computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kibana_endpoint":{"type":"string","computed":true},"log_publishing_options":{"type":["set",["object",{"cloudwatch_log_group_arn":"string","enabled":"bool","log_type":"string"}]],"computed":true},"node_to_node_encryption":{"type":["list",["object",{"enabled":"bool"}]],"computed":true},"processing":{"type":"bool","computed":true},"snapshot_options":{"type":["list",["object",{"automated_snapshot_start_hour":"number"}]],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_options":{"type":["list",["object",{"availability_zones":["set","string"],"security_group_ids":["set","string"],"subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true}}}},"aws_elb":{"version":0,"block":{"attributes":{"access_logs":{"type":["list",["object",{"bucket":"string","bucket_prefix":"string","enabled":"bool","interval":"number"}]],"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"connection_draining":{"type":"bool","computed":true},"connection_draining_timeout":{"type":"number","computed":true},"cross_zone_load_balancing":{"type":"bool","computed":true},"dns_name":{"type":"string","computed":true},"health_check":{"type":["list",["object",{"healthy_threshold":"number","interval":"number","target":"string","timeout":"number","unhealthy_threshold":"number"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","computed":true},"instances":{"type":["set","string"],"computed":true},"internal":{"type":"bool","computed":true},"listener":{"type":["set",["object",{"instance_port":"number","instance_protocol":"string","lb_port":"number","lb_protocol":"string","ssl_certificate_id":"string"}]],"computed":true},"name":{"type":"string","required":true},"security_groups":{"type":["set","string"],"computed":true},"source_security_group":{"type":"string","computed":true},"source_security_group_id":{"type":"string","computed":true},"subnets":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"zone_id":{"type":"string","computed":true}}}},"aws_elb_hosted_zone_id":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_elb_service_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_glue_script":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"language":{"type":"string","optional":true},"python_script":{"type":"string","computed":true},"scala_code":{"type":"string","computed":true}},"block_types":{"dag_edge":{"nesting_mode":"list","block":{"attributes":{"source":{"type":"string","required":true},"target":{"type":"string","required":true},"target_parameter":{"type":"string","optional":true}}},"min_items":1},"dag_node":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","required":true},"line_number":{"type":"number","optional":true},"node_type":{"type":"string","required":true}},"block_types":{"args":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true},"param":{"type":"bool","optional":true},"value":{"type":"string","required":true}}},"min_items":1}}},"min_items":1}}}},"aws_guardduty_detector":{"version":0,"block":{"attributes":{"finding_publishing_frequency":{"type":"string","computed":true},"id":{"type":"string","optional":true},"service_role_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true}}}},"aws_iam_account_alias":{"version":0,"block":{"attributes":{"account_alias":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_iam_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"group_id":{"type":"string","computed":true},"group_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"path":{"type":"string","computed":true},"users":{"type":["list",["object",{"arn":"string","path":"string","user_id":"string","user_name":"string"}]],"computed":true}}}},"aws_iam_instance_profile":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"create_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"path":{"type":"string","computed":true},"role_arn":{"type":"string","computed":true},"role_id":{"type":"string","computed":true},"role_name":{"type":"string","computed":true}}}},"aws_iam_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"path":{"type":"string","computed":true},"policy":{"type":"string","computed":true}}}},"aws_iam_policy_document":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"json":{"type":"string","computed":true},"override_json":{"type":"string","optional":true},"policy_id":{"type":"string","optional":true},"source_json":{"type":"string","optional":true},"version":{"type":"string","optional":true}},"block_types":{"statement":{"nesting_mode":"list","block":{"attributes":{"actions":{"type":["set","string"],"optional":true},"effect":{"type":"string","optional":true},"not_actions":{"type":["set","string"],"optional":true},"not_resources":{"type":["set","string"],"optional":true},"resources":{"type":["set","string"],"optional":true},"sid":{"type":"string","optional":true}},"block_types":{"condition":{"nesting_mode":"set","block":{"attributes":{"test":{"type":"string","required":true},"values":{"type":["set","string"],"required":true},"variable":{"type":"string","required":true}}}},"not_principals":{"nesting_mode":"set","block":{"attributes":{"identifiers":{"type":["set","string"],"required":true},"type":{"type":"string","required":true}}}},"principals":{"nesting_mode":"set","block":{"attributes":{"identifiers":{"type":["set","string"],"required":true},"type":{"type":"string","required":true}}}}}}}}}},"aws_iam_role":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"assume_role_policy":{"type":"string","computed":true},"create_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"max_session_duration":{"type":"number","computed":true},"name":{"type":"string","required":true},"path":{"type":"string","computed":true},"permissions_boundary":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_server_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_body":{"type":"string","computed":true},"certificate_chain":{"type":"string","computed":true},"expiration_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"latest":{"type":"bool","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","computed":true},"path_prefix":{"type":"string","optional":true},"upload_date":{"type":"string","computed":true}}}},"aws_iam_user":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"path":{"type":"string","computed":true},"permissions_boundary":{"type":"string","computed":true},"user_id":{"type":"string","computed":true},"user_name":{"type":"string","required":true}}}},"aws_inspector_rules_packages":{"version":0,"block":{"attributes":{"arns":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_instance":{"version":1,"block":{"attributes":{"ami":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","computed":true},"availability_zone":{"type":"string","computed":true},"credit_specification":{"type":["list",["object",{"cpu_credits":"string"}]],"computed":true},"disable_api_termination":{"type":"bool","computed":true},"ebs_block_device":{"type":["set",["object",{"delete_on_termination":"bool","device_name":"string","encrypted":"bool","iops":"number","kms_key_id":"string","snapshot_id":"string","volume_id":"string","volume_size":"number","volume_type":"string"}]],"computed":true},"ebs_optimized":{"type":"bool","computed":true},"ephemeral_block_device":{"type":["list",["object",{"device_name":"string","no_device":"bool","virtual_name":"string"}]],"computed":true},"get_password_data":{"type":"bool","optional":true},"get_user_data":{"type":"bool","optional":true},"host_id":{"type":"string","computed":true},"iam_instance_profile":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true},"instance_state":{"type":"string","computed":true},"instance_tags":{"type":["map","string"],"optional":true,"computed":true},"instance_type":{"type":"string","computed":true},"key_name":{"type":"string","computed":true},"metadata_options":{"type":["list",["object",{"http_endpoint":"string","http_put_response_hop_limit":"number","http_tokens":"string"}]],"computed":true},"monitoring":{"type":"bool","computed":true},"network_interface_id":{"type":"string","computed":true},"outpost_arn":{"type":"string","computed":true},"password_data":{"type":"string","computed":true},"placement_group":{"type":"string","computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"root_block_device":{"type":["set",["object",{"delete_on_termination":"bool","device_name":"string","encrypted":"bool","iops":"number","kms_key_id":"string","volume_id":"string","volume_size":"number","volume_type":"string"}]],"computed":true},"secondary_private_ips":{"type":["set","string"],"computed":true},"security_groups":{"type":["set","string"],"computed":true},"source_dest_check":{"type":"bool","computed":true},"subnet_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"tenancy":{"type":"string","computed":true},"user_data":{"type":"string","computed":true},"user_data_base64":{"type":"string","computed":true},"vpc_security_group_ids":{"type":["set","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_instances":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["list","string"],"computed":true},"instance_state_names":{"type":["set","string"],"optional":true},"instance_tags":{"type":["map","string"],"optional":true,"computed":true},"private_ips":{"type":["list","string"],"computed":true},"public_ips":{"type":["list","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_internet_gateway":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"attachments":{"type":["list",["object",{"state":"string","vpc_id":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"internet_gateway_id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_iot_endpoint":{"version":0,"block":{"attributes":{"endpoint_address":{"type":"string","computed":true},"endpoint_type":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ip_ranges":{"version":0,"block":{"attributes":{"cidr_blocks":{"type":["list","string"],"computed":true},"create_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_blocks":{"type":["list","string"],"computed":true},"regions":{"type":["set","string"],"optional":true},"services":{"type":["set","string"],"required":true},"sync_token":{"type":"number","computed":true},"url":{"type":"string","optional":true}}}},"aws_kinesis_stream":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"closed_shards":{"type":["set","string"],"computed":true},"creation_timestamp":{"type":"number","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"open_shards":{"type":["set","string"],"computed":true},"retention_period":{"type":"number","computed":true},"shard_level_metrics":{"type":["set","string"],"computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_kms_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"target_key_arn":{"type":"string","computed":true},"target_key_id":{"type":"string","computed":true}}}},"aws_kms_ciphertext":{"version":0,"block":{"attributes":{"ciphertext_blob":{"type":"string","computed":true},"context":{"type":["map","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"plaintext":{"type":"string","required":true,"sensitive":true}}}},"aws_kms_key":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_account_id":{"type":"string","computed":true},"creation_date":{"type":"string","computed":true},"customer_master_key_spec":{"type":"string","computed":true},"deletion_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"enabled":{"type":"bool","computed":true},"expiration_model":{"type":"string","computed":true},"grant_tokens":{"type":["list","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"key_manager":{"type":"string","computed":true},"key_state":{"type":"string","computed":true},"key_usage":{"type":"string","computed":true},"origin":{"type":"string","computed":true},"valid_to":{"type":"string","computed":true}}}},"aws_kms_secret":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true}},"block_types":{"secret":{"nesting_mode":"set","block":{"attributes":{"context":{"type":["map","string"],"optional":true},"grant_tokens":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"payload":{"type":"string","required":true}}},"min_items":1}}}},"aws_kms_secrets":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"plaintext":{"type":["map","string"],"computed":true}},"block_types":{"secret":{"nesting_mode":"set","block":{"attributes":{"context":{"type":["map","string"],"optional":true},"grant_tokens":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"payload":{"type":"string","required":true}}},"min_items":1}}}},"aws_lambda_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"function_name":{"type":"string","required":true},"function_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_arn":{"type":"string","computed":true},"name":{"type":"string","required":true}}}},"aws_lambda_function":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"dead_letter_config":{"type":["list",["object",{"target_arn":"string"}]],"computed":true},"description":{"type":"string","computed":true},"environment":{"type":["list",["object",{"variables":["map","string"]}]],"computed":true},"file_system_config":{"type":["list",["object",{"arn":"string","local_mount_path":"string"}]],"computed":true},"function_name":{"type":"string","required":true},"handler":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_arn":{"type":"string","computed":true},"kms_key_arn":{"type":"string","computed":true},"last_modified":{"type":"string","computed":true},"layers":{"type":["list","string"],"computed":true},"memory_size":{"type":"number","computed":true},"qualified_arn":{"type":"string","computed":true},"qualifier":{"type":"string","optional":true},"reserved_concurrent_executions":{"type":"number","computed":true},"role":{"type":"string","computed":true},"runtime":{"type":"string","computed":true},"source_code_hash":{"type":"string","computed":true},"source_code_size":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"timeout":{"type":"number","computed":true},"tracing_config":{"type":["list",["object",{"mode":"string"}]],"computed":true},"version":{"type":"string","computed":true},"vpc_config":{"type":["list",["object",{"security_group_ids":["set","string"],"subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true}}}},"aws_lambda_invocation":{"version":0,"block":{"attributes":{"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"input":{"type":"string","required":true},"qualifier":{"type":"string","optional":true},"result":{"type":"string","computed":true}}}},"aws_lambda_layer_version":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compatible_runtime":{"type":"string","optional":true},"compatible_runtimes":{"type":["set","string"],"computed":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"layer_arn":{"type":"string","computed":true},"layer_name":{"type":"string","required":true},"license_info":{"type":"string","computed":true},"source_code_hash":{"type":"string","computed":true},"source_code_size":{"type":"number","computed":true},"version":{"type":"number","optional":true,"computed":true}}}},"aws_launch_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","computed":true},"ebs_block_device":{"type":["set",["object",{"delete_on_termination":"bool","device_name":"string","encrypted":"bool","iops":"number","no_device":"bool","snapshot_id":"string","volume_size":"number","volume_type":"string"}]],"computed":true},"ebs_optimized":{"type":"bool","computed":true},"enable_monitoring":{"type":"bool","computed":true},"ephemeral_block_device":{"type":["set",["object",{"device_name":"string","virtual_name":"string"}]],"computed":true},"iam_instance_profile":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","computed":true},"instance_type":{"type":"string","computed":true},"key_name":{"type":"string","computed":true},"name":{"type":"string","required":true},"placement_tenancy":{"type":"string","computed":true},"root_block_device":{"type":["list",["object",{"delete_on_termination":"bool","encrypted":"bool","iops":"number","volume_size":"number","volume_type":"string"}]],"computed":true},"security_groups":{"type":["set","string"],"computed":true},"spot_price":{"type":"string","computed":true},"user_data":{"type":"string","computed":true},"vpc_classic_link_id":{"type":"string","computed":true},"vpc_classic_link_security_groups":{"type":["set","string"],"computed":true}}}},"aws_launch_template":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"block_device_mappings":{"type":["list",["object",{"device_name":"string","ebs":["list",["object",{"delete_on_termination":"string","encrypted":"string","iops":"number","kms_key_id":"string","snapshot_id":"string","volume_size":"number","volume_type":"string"}]],"no_device":"string","virtual_name":"string"}]],"computed":true},"credit_specification":{"type":["list",["object",{"cpu_credits":"string"}]],"computed":true},"default_version":{"type":"number","computed":true},"description":{"type":"string","computed":true},"disable_api_termination":{"type":"bool","computed":true},"ebs_optimized":{"type":"string","computed":true},"elastic_gpu_specifications":{"type":["list",["object",{"type":"string"}]],"computed":true},"hibernation_options":{"type":["list",["object",{"configured":"bool"}]],"computed":true},"iam_instance_profile":{"type":["list",["object",{"arn":"string","name":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","computed":true},"instance_initiated_shutdown_behavior":{"type":"string","computed":true},"instance_market_options":{"type":["list",["object",{"market_type":"string","spot_options":["list",["object",{"block_duration_minutes":"number","instance_interruption_behavior":"string","max_price":"string","spot_instance_type":"string","valid_until":"string"}]]}]],"computed":true},"instance_type":{"type":"string","computed":true},"kernel_id":{"type":"string","computed":true},"key_name":{"type":"string","computed":true},"latest_version":{"type":"number","computed":true},"metadata_options":{"type":["list",["object",{"http_endpoint":"string","http_put_response_hop_limit":"number","http_tokens":"string"}]],"computed":true},"monitoring":{"type":["list",["object",{"enabled":"bool"}]],"computed":true},"name":{"type":"string","optional":true},"network_interfaces":{"type":["list",["object",{"associate_public_ip_address":"string","delete_on_termination":"string","description":"string","device_index":"number","ipv4_address_count":"number","ipv4_addresses":["set","string"],"ipv6_address_count":"number","ipv6_addresses":["set","string"],"network_interface_id":"string","private_ip_address":"string","security_groups":["set","string"],"subnet_id":"string"}]],"computed":true},"placement":{"type":["list",["object",{"affinity":"string","availability_zone":"string","group_name":"string","host_id":"string","partition_number":"number","spread_domain":"string","tenancy":"string"}]],"computed":true},"ram_disk_id":{"type":"string","computed":true},"security_group_names":{"type":["set","string"],"computed":true},"tag_specifications":{"type":["list",["object",{"resource_type":"string","tags":["map","string"]}]],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"user_data":{"type":"string","computed":true},"vpc_security_group_ids":{"type":["set","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_lb":{"version":0,"block":{"attributes":{"access_logs":{"type":["list",["object",{"bucket":"string","enabled":"bool","prefix":"string"}]],"computed":true},"arn":{"type":"string","optional":true,"computed":true},"arn_suffix":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"drop_invalid_header_fields":{"type":"bool","computed":true},"enable_deletion_protection":{"type":"bool","computed":true},"enable_http2":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","computed":true},"internal":{"type":"bool","computed":true},"ip_address_type":{"type":"string","computed":true},"load_balancer_type":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_mapping":{"type":["set",["object",{"allocation_id":"string","private_ipv4_address":"string","subnet_id":"string"}]],"computed":true},"subnets":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","computed":true},"zone_id":{"type":"string","computed":true}}}},"aws_lb_listener":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"certificate_arn":{"type":"string","computed":true},"default_action":{"type":["list",["object",{"authenticate_cognito":["list",["object",{"authentication_request_extra_params":["map","string"],"on_unauthenticated_request":"string","scope":"string","session_cookie_name":"string","session_timeout":"number","user_pool_arn":"string","user_pool_client_id":"string","user_pool_domain":"string"}]],"authenticate_oidc":["list",["object",{"authentication_request_extra_params":["map","string"],"authorization_endpoint":"string","client_id":"string","client_secret":"string","issuer":"string","on_unauthenticated_request":"string","scope":"string","session_cookie_name":"string","session_timeout":"number","token_endpoint":"string","user_info_endpoint":"string"}]],"fixed_response":["list",["object",{"content_type":"string","message_body":"string","status_code":"string"}]],"order":"number","redirect":["list",["object",{"host":"string","path":"string","port":"string","protocol":"string","query":"string","status_code":"string"}]],"target_group_arn":"string","type":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"load_balancer_arn":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"protocol":{"type":"string","computed":true},"ssl_policy":{"type":"string","computed":true}}}},"aws_lb_target_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"arn_suffix":{"type":"string","computed":true},"deregistration_delay":{"type":"number","computed":true},"health_check":{"type":["list",["object",{"enabled":"bool","healthy_threshold":"number","interval":"number","matcher":"string","path":"string","port":"string","protocol":"string","timeout":"number","unhealthy_threshold":"number"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"lambda_multi_value_headers_enabled":{"type":"bool","computed":true},"load_balancing_algorithm_type":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","computed":true},"protocol":{"type":"string","computed":true},"proxy_protocol_v2":{"type":"bool","computed":true},"slow_start":{"type":"number","computed":true},"stickiness":{"type":["list",["object",{"cookie_duration":"number","enabled":"bool","type":"string"}]],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"target_type":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_mq_broker":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","computed":true},"broker_id":{"type":"string","optional":true,"computed":true},"broker_name":{"type":"string","optional":true,"computed":true},"configuration":{"type":["list",["object",{"id":"string","revision":"number"}]],"computed":true},"deployment_mode":{"type":"string","computed":true},"encryption_options":{"type":["list",["object",{"kms_key_id":"string","use_aws_owned_key":"bool"}]],"computed":true},"engine_type":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"host_instance_type":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instances":{"type":["list",["object",{"console_url":"string","endpoints":["list","string"],"ip_address":"string"}]],"computed":true},"maintenance_window_start_time":{"type":["list",["object",{"day_of_week":"string","time_of_day":"string","time_zone":"string"}]],"computed":true},"publicly_accessible":{"type":"bool","computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"user":{"type":["set",["object",{"console_access":"bool","groups":["set","string"],"username":"string"}]],"computed":true}},"block_types":{"logs":{"nesting_mode":"list","block":{"attributes":{"audit":{"type":"bool","computed":true},"general":{"type":"bool","computed":true}}},"max_items":1}}}},"aws_msk_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bootstrap_brokers":{"type":"string","computed":true},"bootstrap_brokers_tls":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"kafka_version":{"type":"string","computed":true},"number_of_broker_nodes":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"zookeeper_connect_string":{"type":"string","computed":true}}}},"aws_msk_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kafka_versions":{"type":["set","string"],"computed":true},"latest_revision":{"type":"number","computed":true},"name":{"type":"string","required":true},"server_properties":{"type":"string","computed":true}}}},"aws_nat_gateway":{"version":0,"block":{"attributes":{"allocation_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_network_acls":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_network_interface":{"version":0,"block":{"attributes":{"association":{"type":["list",["object",{"allocation_id":"string","association_id":"string","ip_owner_id":"string","public_dns_name":"string","public_ip":"string"}]],"computed":true},"attachment":{"type":["list",["object",{"attachment_id":"string","device_index":"number","instance_id":"string","instance_owner_id":"string"}]],"computed":true},"availability_zone":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"interface_type":{"type":"string","computed":true},"ipv6_addresses":{"type":["set","string"],"computed":true},"mac_address":{"type":"string","computed":true},"outpost_arn":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"private_dns_name":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"private_ips":{"type":["list","string"],"computed":true},"requester_id":{"type":"string","computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_network_interfaces":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_organizations_organization":{"version":0,"block":{"attributes":{"accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string","status":"string"}]],"computed":true},"arn":{"type":"string","computed":true},"aws_service_access_principals":{"type":["set","string"],"computed":true},"enabled_policy_types":{"type":["set","string"],"computed":true},"feature_set":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"master_account_arn":{"type":"string","computed":true},"master_account_email":{"type":"string","computed":true},"master_account_id":{"type":"string","computed":true},"non_master_accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string","status":"string"}]],"computed":true},"roots":{"type":["list",["object",{"arn":"string","id":"string","name":"string","policy_types":["list",["object",{"status":"string","type":"string"}]]}]],"computed":true}}}},"aws_organizations_organizational_units":{"version":0,"block":{"attributes":{"children":{"type":["list",["object",{"arn":"string","id":"string","name":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"parent_id":{"type":"string","required":true}}}},"aws_outposts_outpost":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","computed":true},"availability_zone_id":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"site_id":{"type":"string","computed":true}}}},"aws_outposts_outpost_instance_type":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","optional":true,"computed":true},"preferred_instance_types":{"type":["list","string"],"optional":true}}}},"aws_outposts_outpost_instance_types":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instance_types":{"type":["set","string"],"computed":true}}}},"aws_outposts_outposts":{"version":0,"block":{"attributes":{"arns":{"type":["set","string"],"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"availability_zone_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"site_id":{"type":"string","optional":true,"computed":true}}}},"aws_outposts_site":{"version":0,"block":{"attributes":{"account_id":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true}}}},"aws_outposts_sites":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true}}}},"aws_partition":{"version":0,"block":{"attributes":{"dns_suffix":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"partition":{"type":"string","computed":true}}}},"aws_prefix_list":{"version":0,"block":{"attributes":{"cidr_blocks":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"prefix_list_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_pricing_product":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"result":{"type":"string","computed":true},"service_code":{"type":"string","required":true}},"block_types":{"filters":{"nesting_mode":"list","block":{"attributes":{"field":{"type":"string","required":true},"value":{"type":"string","required":true}}},"min_items":1}}}},"aws_qldb_ledger":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deletion_protection":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_ram_resource_share":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owning_account_id":{"type":"string","computed":true},"resource_owner":{"type":"string","required":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_rds_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"backtrack_window":{"type":"number","computed":true},"backup_retention_period":{"type":"number","computed":true},"cluster_identifier":{"type":"string","required":true},"cluster_members":{"type":["set","string"],"computed":true},"cluster_resource_id":{"type":"string","computed":true},"database_name":{"type":"string","computed":true},"db_cluster_parameter_group_name":{"type":"string","computed":true},"db_subnet_group_name":{"type":"string","computed":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"final_snapshot_identifier":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"iam_database_authentication_enabled":{"type":"bool","computed":true},"iam_roles":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"master_username":{"type":"string","computed":true},"port":{"type":"number","computed":true},"preferred_backup_window":{"type":"string","computed":true},"preferred_maintenance_window":{"type":"string","computed":true},"reader_endpoint":{"type":"string","computed":true},"replication_source_identifier":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_security_group_ids":{"type":["set","string"],"computed":true}}}},"aws_redshift_cluster":{"version":0,"block":{"attributes":{"allow_version_upgrade":{"type":"bool","computed":true},"automated_snapshot_retention_period":{"type":"number","computed":true},"availability_zone":{"type":"string","computed":true},"bucket_name":{"type":"string","computed":true},"cluster_identifier":{"type":"string","required":true},"cluster_parameter_group_name":{"type":"string","computed":true},"cluster_public_key":{"type":"string","computed":true},"cluster_revision_number":{"type":"string","computed":true},"cluster_security_groups":{"type":["list","string"],"computed":true},"cluster_subnet_group_name":{"type":"string","computed":true},"cluster_type":{"type":"string","computed":true},"cluster_version":{"type":"string","computed":true},"database_name":{"type":"string","computed":true},"elastic_ip":{"type":"string","computed":true},"enable_logging":{"type":"bool","computed":true},"encrypted":{"type":"bool","computed":true},"endpoint":{"type":"string","computed":true},"enhanced_vpc_routing":{"type":"bool","computed":true},"iam_roles":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"master_username":{"type":"string","computed":true},"node_type":{"type":"string","computed":true},"number_of_nodes":{"type":"number","computed":true},"port":{"type":"number","computed":true},"preferred_maintenance_window":{"type":"string","computed":true},"publicly_accessible":{"type":"bool","computed":true},"s3_key_prefix":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"vpc_security_group_ids":{"type":["list","string"],"computed":true}}}},"aws_redshift_service_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_region":{"version":0,"block":{"attributes":{"description":{"type":"string","computed":true},"endpoint":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true}}}},"aws_regions":{"version":0,"block":{"attributes":{"all_regions":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"names":{"type":["set","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_route":{"version":0,"block":{"attributes":{"destination_cidr_block":{"type":"string","optional":true,"computed":true},"destination_ipv6_cidr_block":{"type":"string","optional":true,"computed":true},"egress_only_gateway_id":{"type":"string","optional":true,"computed":true},"gateway_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true,"computed":true},"nat_gateway_id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","optional":true,"computed":true},"route_table_id":{"type":"string","required":true},"transit_gateway_id":{"type":"string","optional":true,"computed":true},"vpc_peering_connection_id":{"type":"string","optional":true,"computed":true}}}},"aws_route53_delegation_set":{"version":0,"block":{"attributes":{"caller_reference":{"type":"string","computed":true},"id":{"type":"string","required":true},"name_servers":{"type":["list","string"],"computed":true}}}},"aws_route53_resolver_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"resolver_endpoint_id":{"type":"string","optional":true,"computed":true},"resolver_rule_id":{"type":"string","optional":true,"computed":true},"rule_type":{"type":"string","optional":true,"computed":true},"share_status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_route53_resolver_rules":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","optional":true},"resolver_endpoint_id":{"type":"string","optional":true},"resolver_rule_ids":{"type":["set","string"],"computed":true},"rule_type":{"type":"string","optional":true},"share_status":{"type":"string","optional":true}}}},"aws_route53_zone":{"version":0,"block":{"attributes":{"caller_reference":{"type":"string","computed":true},"comment":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"linked_service_description":{"type":"string","computed":true},"linked_service_principal":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"name_servers":{"type":["list","string"],"computed":true},"private_zone":{"type":"bool","optional":true},"resource_record_set_count":{"type":"number","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","optional":true,"computed":true}}}},"aws_route_table":{"version":0,"block":{"attributes":{"associations":{"type":["list",["object",{"gateway_id":"string","main":"bool","route_table_association_id":"string","route_table_id":"string","subnet_id":"string"}]],"computed":true},"gateway_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"route_table_id":{"type":"string","optional":true,"computed":true},"routes":{"type":["list",["object",{"cidr_block":"string","egress_only_gateway_id":"string","gateway_id":"string","instance_id":"string","ipv6_cidr_block":"string","nat_gateway_id":"string","network_interface_id":"string","transit_gateway_id":"string","vpc_peering_connection_id":"string"}]],"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_route_tables":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_s3_bucket":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bucket":{"type":"string","required":true},"bucket_domain_name":{"type":"string","computed":true},"bucket_regional_domain_name":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","computed":true},"website_domain":{"type":"string","computed":true},"website_endpoint":{"type":"string","computed":true}}}},"aws_s3_bucket_object":{"version":0,"block":{"attributes":{"body":{"type":"string","computed":true},"bucket":{"type":"string","required":true},"cache_control":{"type":"string","computed":true},"content_disposition":{"type":"string","computed":true},"content_encoding":{"type":"string","computed":true},"content_language":{"type":"string","computed":true},"content_length":{"type":"number","computed":true},"content_type":{"type":"string","computed":true},"etag":{"type":"string","computed":true},"expiration":{"type":"string","computed":true},"expires":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","required":true},"last_modified":{"type":"string","computed":true},"metadata":{"type":["map","string"],"computed":true},"object_lock_legal_hold_status":{"type":"string","computed":true},"object_lock_mode":{"type":"string","computed":true},"object_lock_retain_until_date":{"type":"string","computed":true},"range":{"type":"string","optional":true},"server_side_encryption":{"type":"string","computed":true},"sse_kms_key_id":{"type":"string","computed":true},"storage_class":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"version_id":{"type":"string","optional":true,"computed":true},"website_redirect_location":{"type":"string","computed":true}}}},"aws_s3_bucket_objects":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"common_prefixes":{"type":["list","string"],"computed":true},"delimiter":{"type":"string","optional":true},"encoding_type":{"type":"string","optional":true},"fetch_owner":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"keys":{"type":["list","string"],"computed":true},"max_keys":{"type":"number","optional":true},"owners":{"type":["list","string"],"computed":true},"prefix":{"type":"string","optional":true},"start_after":{"type":"string","optional":true}}}},"aws_secretsmanager_secret":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","computed":true},"rotation_enabled":{"type":"bool","computed":true},"rotation_lambda_arn":{"type":"string","computed":true},"rotation_rules":{"type":["list",["object",{"automatically_after_days":"number"}]],"computed":true},"tags":{"type":["map","string"],"computed":true}}}},"aws_secretsmanager_secret_rotation":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"rotation_enabled":{"type":"bool","computed":true},"rotation_lambda_arn":{"type":"string","computed":true},"rotation_rules":{"type":["list",["object",{"automatically_after_days":"number"}]],"computed":true},"secret_id":{"type":"string","required":true}}}},"aws_secretsmanager_secret_version":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"secret_binary":{"type":"string","computed":true,"sensitive":true},"secret_id":{"type":"string","required":true},"secret_string":{"type":"string","computed":true,"sensitive":true},"version_id":{"type":"string","optional":true,"computed":true},"version_stage":{"type":"string","optional":true},"version_stages":{"type":["set","string"],"computed":true}}}},"aws_security_group":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_security_groups":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["list","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_ids":{"type":["list","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_servicequotas_service":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"service_code":{"type":"string","computed":true},"service_name":{"type":"string","required":true}}}},"aws_servicequotas_service_quota":{"version":0,"block":{"attributes":{"adjustable":{"type":"bool","computed":true},"arn":{"type":"string","computed":true},"default_value":{"type":"number","computed":true},"global_quota":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"quota_code":{"type":"string","optional":true,"computed":true},"quota_name":{"type":"string","optional":true,"computed":true},"service_code":{"type":"string","required":true},"service_name":{"type":"string","computed":true},"value":{"type":"number","computed":true}}}},"aws_sfn_activity":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"creation_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true}}}},"aws_sfn_state_machine":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_date":{"type":"string","computed":true},"definition":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true}}}},"aws_sns_topic":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_sqs_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"url":{"type":"string","computed":true}}}},"aws_ssm_document":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"content":{"type":"string","computed":true},"document_format":{"type":"string","optional":true},"document_type":{"type":"string","computed":true},"document_version":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_ssm_parameter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"type":{"type":"string","computed":true},"value":{"type":"string","computed":true,"sensitive":true},"version":{"type":"number","computed":true},"with_decryption":{"type":"bool","optional":true}}}},"aws_ssm_patch_baseline":{"version":0,"block":{"attributes":{"default_baseline":{"type":"bool","optional":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"name_prefix":{"type":"string","optional":true},"operating_system":{"type":"string","optional":true},"owner":{"type":"string","required":true}}}},"aws_storagegateway_local_disk":{"version":0,"block":{"attributes":{"disk_id":{"type":"string","computed":true},"disk_node":{"type":"string","optional":true},"disk_path":{"type":"string","optional":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_subnet":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_ipv6_address_on_creation":{"type":"bool","computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"availability_zone_id":{"type":"string","optional":true,"computed":true},"cidr_block":{"type":"string","optional":true,"computed":true},"default_for_az":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block_association_id":{"type":"string","computed":true},"map_public_ip_on_launch":{"type":"bool","computed":true},"outpost_arn":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_subnet_ids":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","required":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_transfer_server":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identity_provider_type":{"type":"string","computed":true},"invocation_role":{"type":"string","computed":true},"logging_role":{"type":"string","computed":true},"server_id":{"type":"string","required":true},"url":{"type":"string","computed":true}}}},"aws_vpc":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"cidr_block":{"type":"string","optional":true,"computed":true},"cidr_block_associations":{"type":["list",["object",{"association_id":"string","cidr_block":"string","state":"string"}]],"computed":true},"default":{"type":"bool","optional":true,"computed":true},"dhcp_options_id":{"type":"string","optional":true,"computed":true},"enable_dns_hostnames":{"type":"bool","computed":true},"enable_dns_support":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_tenancy":{"type":"string","computed":true},"ipv6_association_id":{"type":"string","computed":true},"ipv6_cidr_block":{"type":"string","computed":true},"main_route_table_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpc_dhcp_options":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"dhcp_options_id":{"type":"string","optional":true,"computed":true},"domain_name":{"type":"string","computed":true},"domain_name_servers":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"netbios_name_servers":{"type":["list","string"],"computed":true},"netbios_node_type":{"type":"string","computed":true},"ntp_servers":{"type":["list","string"],"computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpc_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cidr_blocks":{"type":["list","string"],"computed":true},"dns_entry":{"type":["list",["object",{"dns_name":"string","hosted_zone_id":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_ids":{"type":["set","string"],"computed":true},"owner_id":{"type":"string","computed":true},"policy":{"type":"string","computed":true},"prefix_list_id":{"type":"string","computed":true},"private_dns_enabled":{"type":"bool","computed":true},"requester_managed":{"type":"bool","computed":true},"route_table_ids":{"type":["set","string"],"computed":true},"security_group_ids":{"type":["set","string"],"computed":true},"service_name":{"type":"string","optional":true,"computed":true},"state":{"type":"string","optional":true,"computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_endpoint_type":{"type":"string","computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpc_endpoint_service":{"version":0,"block":{"attributes":{"acceptance_required":{"type":"bool","computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"base_endpoint_dns_names":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"manages_vpc_endpoints":{"type":"bool","computed":true},"owner":{"type":"string","computed":true},"private_dns_name":{"type":"string","computed":true},"service":{"type":"string","optional":true},"service_id":{"type":"string","computed":true},"service_name":{"type":"string","optional":true,"computed":true},"service_type":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_endpoint_policy_supported":{"type":"bool","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_vpc_peering_connection":{"version":0,"block":{"attributes":{"accepter":{"type":["map","bool"],"computed":true},"cidr_block":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","optional":true,"computed":true},"peer_cidr_block":{"type":"string","optional":true,"computed":true},"peer_owner_id":{"type":"string","optional":true,"computed":true},"peer_region":{"type":"string","optional":true,"computed":true},"peer_vpc_id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true,"computed":true},"requester":{"type":["map","bool"],"computed":true},"status":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpcs":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpn_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"attached_vpc_id":{"type":"string","optional":true,"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_waf_ipset":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_waf_rate_based_rule":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_waf_rule":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_waf_web_acl":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafregional_ipset":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafregional_rate_based_rule":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafregional_rule":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafregional_web_acl":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafv2_ip_set":{"version":0,"block":{"attributes":{"addresses":{"type":["set","string"],"computed":true},"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address_version":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true}}}},"aws_wafv2_regex_pattern_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"regular_expression":{"type":["set",["object",{"regex_string":"string"}]],"computed":true},"scope":{"type":"string","required":true}}}},"aws_wafv2_rule_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true}}}},"aws_wafv2_web_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true}}}},"aws_workspaces_bundle":{"version":0,"block":{"attributes":{"bundle_id":{"type":"string","optional":true},"compute_type":{"type":["list",["object",{"name":"string"}]],"computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"owner":{"type":"string","optional":true},"root_storage":{"type":["list",["object",{"capacity":"string"}]],"computed":true},"user_storage":{"type":["list",["object",{"capacity":"string"}]],"computed":true}}}},"aws_workspaces_directory":{"version":0,"block":{"attributes":{"alias":{"type":"string","computed":true},"customer_user_name":{"type":"string","computed":true},"directory_id":{"type":"string","required":true},"directory_name":{"type":"string","computed":true},"directory_type":{"type":"string","computed":true},"dns_ip_addresses":{"type":["set","string"],"computed":true},"iam_role_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_group_ids":{"type":["set","string"],"computed":true},"registration_code":{"type":"string","computed":true},"self_service_permissions":{"type":["list",["object",{"change_compute_type":"bool","increase_volume_size":"bool","rebuild_workspace":"bool","restart_workspace":"bool","switch_running_mode":"bool"}]],"computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true},"workspace_security_group_id":{"type":"string","computed":true}}}}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/foo/main.tf b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/foo/main.tf new file mode 100644 index 00000000..f3d0b009 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/foo/main.tf @@ -0,0 +1,13 @@ +variable "bar" { + type = "string" +} + +variable "one" { + type = "string" +} + +resource "null_resource" "foo" { + triggers = { + foo = "bar" + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/module.tf b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/module.tf new file mode 100644 index 00000000..236fdac6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/module.tf @@ -0,0 +1,6 @@ +module "foo" { + source = "./foo" + + bar = "baz" + one = "two" +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/outputs.tf b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/outputs.tf new file mode 100644 index 00000000..1e2a4531 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/outputs.tf @@ -0,0 +1,52 @@ +output "foo" { + sensitive = true + value = "bar" +} + +output "string" { + value = "foo" +} + +output "list" { + value = [ + "foo", + "bar", + ] +} + +output "map" { + value = { + foo = "bar" + number = 42 + } +} + +output "referenced" { + value = null_resource.foo.id +} + +output "interpolated" { + value = "${null_resource.foo.id}" +} + +output "referenced_deep" { + value = { + foo = "bar" + number = 42 + map = { + bar = "baz" + id = null_resource.foo.id + } + } +} + +output "interpolated_deep" { + value = { + foo = "bar" + number = 42 + map = { + bar = "baz" + id = "${null_resource.foo.id}" + } + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/plan.json b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/plan.json new file mode 100644 index 00000000..d97d39d1 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/plan.json @@ -0,0 +1 @@ +{"format_version":"0.1","terraform_version":"0.12.11","variables":{"foo":{"value":"bar"},"map":{"value":{"foo":"bar","number":42}},"number":{"value":42}},"planned_values":{"outputs":{"foo":{"sensitive":true,"value":"bar"},"interpolated":{"sensitive":false,"value":"424881806176056736"},"interpolated_deep":{"sensitive":false,"value":{"foo":"bar","map":{"bar":"baz","id":"424881806176056736"},"number":42}},"list":{"sensitive":false,"value":["foo","bar"]},"map":{"sensitive":false,"value":{"foo":"bar","number":42}},"referenced":{"sensitive":false,"value":"424881806176056736"},"referenced_deep":{"sensitive":false,"value":{"foo":"bar","map":{"bar":"baz","id":"424881806176056736"},"number":42}},"string":{"sensitive":false,"value":"foo"}},"root_module":{"resources":[{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_name":"null","schema_version":0,"values":{"id":"4347220156304926627","triggers":{"foo_id":"424881806176056736"}}},{"address":"null_resource.baz[0]","mode":"managed","type":"null_resource","name":"baz","index":0,"provider_name":"null","schema_version":0,"values":{"id":"751901236459396488","triggers":{"foo_id":"424881806176056736"}}},{"address":"null_resource.baz[1]","mode":"managed","type":"null_resource","name":"baz","index":1,"provider_name":"null","schema_version":0,"values":{"id":"2106740714798375541","triggers":{"foo_id":"424881806176056736"}}},{"address":"null_resource.baz[2]","mode":"managed","type":"null_resource","name":"baz","index":2,"provider_name":"null","schema_version":0,"values":{"id":"8665755682221598193","triggers":{"foo_id":"424881806176056736"}}},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","schema_version":0,"values":{"id":"424881806176056736","triggers":{"foo":"bar"}}}],"child_modules":[{"resources":[{"address":"module.foo.null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","schema_version":0,"values":{"id":"705267318028962447","triggers":{"foo":"bar"}}}],"address":"module.foo"}]}},"resource_changes":[{"address":"module.foo.null_resource.foo","module_address":"module.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","change":{"actions":["no-op"],"before":{"id":"705267318028962447","triggers":{"foo":"bar"}},"after":{"id":"705267318028962447","triggers":{"foo":"bar"}},"after_unknown":{}}},{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_name":"null","change":{"actions":["no-op"],"before":{"id":"4347220156304926627","triggers":{"foo_id":"424881806176056736"}},"after":{"id":"4347220156304926627","triggers":{"foo_id":"424881806176056736"}},"after_unknown":{}}},{"address":"null_resource.baz[0]","mode":"managed","type":"null_resource","name":"baz","index":0,"provider_name":"null","change":{"actions":["no-op"],"before":{"id":"751901236459396488","triggers":{"foo_id":"424881806176056736"}},"after":{"id":"751901236459396488","triggers":{"foo_id":"424881806176056736"}},"after_unknown":{}}},{"address":"null_resource.baz[1]","mode":"managed","type":"null_resource","name":"baz","index":1,"provider_name":"null","change":{"actions":["no-op"],"before":{"id":"2106740714798375541","triggers":{"foo_id":"424881806176056736"}},"after":{"id":"2106740714798375541","triggers":{"foo_id":"424881806176056736"}},"after_unknown":{}}},{"address":"null_resource.baz[2]","mode":"managed","type":"null_resource","name":"baz","index":2,"provider_name":"null","change":{"actions":["no-op"],"before":{"id":"8665755682221598193","triggers":{"foo_id":"424881806176056736"}},"after":{"id":"8665755682221598193","triggers":{"foo_id":"424881806176056736"}},"after_unknown":{}}},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","change":{"actions":["no-op"],"before":{"id":"424881806176056736","triggers":{"foo":"bar"}},"after":{"id":"424881806176056736","triggers":{"foo":"bar"}},"after_unknown":{}}}],"output_changes":{"foo":{"actions":["create"],"before":null,"after":"bar","after_unknown":false},"interpolated":{"actions":["create"],"before":null,"after":"424881806176056736","after_unknown":false},"interpolated_deep":{"actions":["create"],"before":null,"after":{"foo":"bar","map":{"bar":"baz","id":"424881806176056736"},"number":42},"after_unknown":false},"list":{"actions":["create"],"before":null,"after":["foo","bar"],"after_unknown":false},"map":{"actions":["create"],"before":null,"after":{"foo":"bar","number":42},"after_unknown":false},"referenced":{"actions":["create"],"before":null,"after":"424881806176056736","after_unknown":false},"referenced_deep":{"actions":["create"],"before":null,"after":{"foo":"bar","map":{"bar":"baz","id":"424881806176056736"},"number":42},"after_unknown":false},"string":{"actions":["create"],"before":null,"after":"foo","after_unknown":false}},"prior_state":{"format_version":"0.1","terraform_version":"0.12.11","values":{"outputs":{"foo":{"sensitive":true,"value":"bar"},"interpolated":{"sensitive":false,"value":"424881806176056736"},"interpolated_deep":{"sensitive":false,"value":{"foo":"bar","map":{"bar":"baz","id":"424881806176056736"},"number":42}},"list":{"sensitive":false,"value":["foo","bar"]},"map":{"sensitive":false,"value":{"foo":"bar","number":42}},"referenced":{"sensitive":false,"value":"424881806176056736"},"referenced_deep":{"sensitive":false,"value":{"foo":"bar","map":{"bar":"baz","id":"424881806176056736"},"number":42}},"string":{"sensitive":false,"value":"foo"}},"root_module":{"resources":[{"address":"data.null_data_source.baz","mode":"data","type":"null_data_source","name":"baz","provider_name":"null","schema_version":0,"values":{"has_computed_default":"default","id":"static","inputs":{"bar_id":"4347220156304926627","foo_id":"424881806176056736"},"outputs":{"bar_id":"4347220156304926627","foo_id":"424881806176056736"},"random":"8894697877806649938"},"depends_on":["null_resource.bar","null_resource.foo"]},{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_name":"null","schema_version":0,"values":{"id":"4347220156304926627","triggers":{"foo_id":"424881806176056736"}},"depends_on":["null_resource.foo"]},{"address":"null_resource.baz","mode":"managed","type":"null_resource","name":"baz","index":0,"provider_name":"null","schema_version":0,"values":{"id":"751901236459396488","triggers":{"foo_id":"424881806176056736"}},"depends_on":["null_resource.foo"]},{"address":"null_resource.baz","mode":"managed","type":"null_resource","name":"baz","index":1,"provider_name":"null","schema_version":0,"values":{"id":"2106740714798375541","triggers":{"foo_id":"424881806176056736"}},"depends_on":["null_resource.foo"]},{"address":"null_resource.baz","mode":"managed","type":"null_resource","name":"baz","index":2,"provider_name":"null","schema_version":0,"values":{"id":"8665755682221598193","triggers":{"foo_id":"424881806176056736"}},"depends_on":["null_resource.foo"]},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","schema_version":0,"values":{"id":"424881806176056736","triggers":{"foo":"bar"}}}],"child_modules":[{"resources":[{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","schema_version":0,"values":{"id":"705267318028962447","triggers":{"foo":"bar"}}}],"address":"module.foo"}]}}},"configuration":{"provider_config":{"aws":{"name":"aws","expressions":{"region":{"constant_value":"us-west-2"}}},"aws.east":{"name":"aws","alias":"east","expressions":{"region":{"constant_value":"us-east-1"}}},"null":{"name":"null"}},"root_module":{"outputs":{"foo":{"sensitive":true,"expression":{"constant_value":"bar"}},"interpolated":{"expression":{"references":["null_resource.foo"]}},"interpolated_deep":{"expression":{"references":["null_resource.foo"]}},"list":{"expression":{"constant_value":["foo","bar"]}},"map":{"expression":{"constant_value":{"foo":"bar","number":42}}},"referenced":{"expression":{"references":["null_resource.foo"]}},"referenced_deep":{"expression":{"references":["null_resource.foo"]}},"string":{"expression":{"constant_value":"foo"}}},"resources":[{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_config_key":"null","expressions":{"triggers":{"references":["null_resource.foo"]}},"schema_version":0},{"address":"null_resource.baz","mode":"managed","type":"null_resource","name":"baz","provider_config_key":"null","expressions":{"triggers":{"references":["null_resource.foo"]}},"schema_version":0,"count_expression":{"constant_value":3}},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_config_key":"null","provisioners":[{"type":"local-exec","expressions":{"command":{"constant_value":"echo hello"}}}],"expressions":{"triggers":{"constant_value":{"foo":"bar"}}},"schema_version":0},{"address":"data.null_data_source.baz","mode":"data","type":"null_data_source","name":"baz","provider_config_key":"null","expressions":{"inputs":{"references":["null_resource.foo","null_resource.bar"]}},"schema_version":0}],"module_calls":{"foo":{"source":"./foo","expressions":{"bar":{"constant_value":"baz"},"one":{"constant_value":"two"}},"module":{"resources":[{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_config_key":"foo:null","expressions":{"triggers":{"constant_value":{"foo":"bar"}}},"schema_version":0}],"variables":{"bar":{},"one":{}}}}},"variables":{"foo":{"default":"bar","description":"foobar"},"map":{"default":{"foo":"bar","number":42}},"number":{"default":42}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/providers.tf b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/providers.tf new file mode 100644 index 00000000..4d481205 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/providers.tf @@ -0,0 +1,10 @@ +provider "null" {} + +provider "aws" { + region = "us-west-2" +} + +provider "aws" { + alias = "east" + region = "us-east-1" +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/resources.tf b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/resources.tf new file mode 100644 index 00000000..1d4a8e8f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/resources.tf @@ -0,0 +1,30 @@ +resource "null_resource" "foo" { + triggers = { + foo = "bar" + } + + provisioner "local-exec" { + command = "echo hello" + } +} + +resource "null_resource" "bar" { + triggers = { + foo_id = "${null_resource.foo.id}" + } +} + +data "null_data_source" "baz" { + inputs = { + foo_id = "${null_resource.foo.id}" + bar_id = "${null_resource.bar.id}" + } +} + +resource "null_resource" "baz" { + count = 3 + + triggers = { + foo_id = "${null_resource.foo.id}" + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/schemas.json b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/schemas.json new file mode 100644 index 00000000..8a610e2e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/schemas.json @@ -0,0 +1 @@ +{"format_version":"0.1","provider_schemas":{"aws":{"provider":{"version":0,"block":{"attributes":{"access_key":{"type":"string","description":"The access key for API operations. You can retrieve this\nfrom the 'Security \u0026 Credentials' section of the AWS console.","optional":true},"allowed_account_ids":{"type":["set","string"],"optional":true},"forbidden_account_ids":{"type":["set","string"],"optional":true},"insecure":{"type":"bool","description":"Explicitly allow the provider to perform \"insecure\" SSL requests. If omitted,default value is `false`","optional":true},"max_retries":{"type":"number","description":"The maximum number of times an AWS API request is\nbeing executed. If the API request still fails, an error is\nthrown.","optional":true},"profile":{"type":"string","description":"The profile for API operations. If not set, the default profile\ncreated with `aws configure` will be used.","optional":true},"region":{"type":"string","description":"The region where AWS operations will take place. Examples\nare us-east-1, us-west-2, etc.","required":true},"s3_force_path_style":{"type":"bool","description":"Set this to true to force the request to use path-style addressing,\ni.e., http://s3.amazonaws.com/BUCKET/KEY. By default, the S3 client will\nuse virtual hosted bucket addressing when possible\n(http://BUCKET.s3.amazonaws.com/KEY). Specific to the Amazon S3 service.","optional":true},"secret_key":{"type":"string","description":"The secret key for API operations. You can retrieve this\nfrom the 'Security \u0026 Credentials' section of the AWS console.","optional":true},"shared_credentials_file":{"type":"string","description":"The path to the shared credentials file. If not set\nthis defaults to ~/.aws/credentials.","optional":true},"skip_credentials_validation":{"type":"bool","description":"Skip the credentials validation via STS API. Used for AWS API implementations that do not have STS available/implemented.","optional":true},"skip_get_ec2_platforms":{"type":"bool","description":"Skip getting the supported EC2 platforms. Used by users that don't have ec2:DescribeAccountAttributes permissions.","optional":true},"skip_metadata_api_check":{"type":"bool","optional":true},"skip_region_validation":{"type":"bool","description":"Skip static validation of region name. Used by users of alternative AWS-like APIs or users w/ access to regions that are not public (yet).","optional":true},"skip_requesting_account_id":{"type":"bool","description":"Skip requesting the account ID. Used for AWS API implementations that do not have IAM/STS API and/or metadata API.","optional":true},"token":{"type":"string","description":"session token. A session token is only required if you are\nusing temporary security credentials.","optional":true}},"block_types":{"assume_role":{"nesting_mode":"list","block":{"attributes":{"duration_seconds":{"type":"number","description":"Seconds to restrict the assume role session duration.","optional":true},"external_id":{"type":"string","description":"Unique identifier that might be required for assuming a role in another account.","optional":true},"policy":{"type":"string","description":"IAM Policy JSON describing further restricting permissions for the IAM Role being assumed.","optional":true},"policy_arns":{"type":["set","string"],"description":"Amazon Resource Names (ARNs) of IAM Policies describing further restricting permissions for the IAM Role being assumed.","optional":true},"role_arn":{"type":"string","description":"Amazon Resource Name of an IAM Role to assume prior to making API calls.","optional":true},"session_name":{"type":"string","description":"Identifier for the assumed role session.","optional":true},"tags":{"type":["map","string"],"description":"Assume role session tags.","optional":true},"transitive_tag_keys":{"type":["set","string"],"description":"Assume role session tag keys to pass to any subsequent sessions.","optional":true}}},"max_items":1},"endpoints":{"nesting_mode":"set","block":{"attributes":{"accessanalyzer":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"acm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"acmpca":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"amplify":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"apigateway":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"applicationautoscaling":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"applicationinsights":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"appmesh":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"appstream":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"appsync":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"athena":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"autoscaling":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"autoscalingplans":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"backup":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"batch":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"budgets":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloud9":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudformation":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudfront":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudhsm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudsearch":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudtrail":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudwatch":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudwatchevents":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cloudwatchlogs":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codeartifact":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codebuild":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codecommit":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codedeploy":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"codepipeline":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cognitoidentity":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cognitoidp":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"configservice":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"cur":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dataexchange":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"datapipeline":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"datasync":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dax":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"devicefarm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"directconnect":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dlm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dms":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"docdb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ds":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"dynamodb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ec2":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ecr":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ecs":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"efs":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"eks":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"elasticache":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"elasticbeanstalk":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"elastictranscoder":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"elb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"emr":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"es":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"firehose":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"fms":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"forecast":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"fsx":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"gamelift":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"glacier":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"globalaccelerator":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"glue":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"greengrass":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"guardduty":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"iam":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"imagebuilder":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"inspector":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"iot":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"iotanalytics":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"iotevents":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kafka":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kinesis":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kinesisanalytics":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kinesisanalyticsv2":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kinesisvideo":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"kms":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"lakeformation":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"lambda":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"lexmodels":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"licensemanager":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"lightsail":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"macie":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"managedblockchain":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"marketplacecatalog":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediaconnect":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediaconvert":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"medialive":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediapackage":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediastore":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mediastoredata":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"mq":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"neptune":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"networkmanager":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"opsworks":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"organizations":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"outposts":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"personalize":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"pinpoint":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"pricing":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"qldb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"quicksight":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ram":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"rds":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"redshift":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"resourcegroups":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"resourcegroupstaggingapi":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"route53":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"route53domains":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"route53resolver":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"s3":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"s3control":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sagemaker":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sdb":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"secretsmanager":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"securityhub":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"serverlessrepo":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"servicecatalog":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"servicediscovery":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"servicequotas":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ses":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"shield":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sns":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sqs":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"ssm":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"stepfunctions":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"storagegateway":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"sts":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"swf":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"synthetics":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"transfer":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"waf":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"wafregional":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"wafv2":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"worklink":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"workmail":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"workspaces":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true},"xray":{"type":"string","description":"Use this to override the default service endpoint URL","optional":true}}}},"ignore_tags":{"nesting_mode":"list","block":{"attributes":{"key_prefixes":{"type":["set","string"],"description":"Resource tag key prefixes to ignore across all resources.","optional":true},"keys":{"type":["set","string"],"description":"Resource tag keys to ignore across all resources.","optional":true}}},"max_items":1}}}},"resource_schemas":{"aws_accessanalyzer_analyzer":{"version":0,"block":{"attributes":{"analyzer_name":{"type":"string","required":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true}}}},"aws_acm_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_authority_arn":{"type":"string","optional":true},"certificate_body":{"type":"string","optional":true},"certificate_chain":{"type":"string","optional":true},"domain_name":{"type":"string","optional":true,"computed":true},"domain_validation_options":{"type":["set",["object",{"domain_name":"string","resource_record_name":"string","resource_record_type":"string","resource_record_value":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"status":{"type":"string","computed":true},"subject_alternative_names":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"validation_emails":{"type":["list","string"],"computed":true},"validation_method":{"type":"string","optional":true,"computed":true}},"block_types":{"options":{"nesting_mode":"list","block":{"attributes":{"certificate_transparency_logging_preference":{"type":"string","optional":true}}},"max_items":1}}}},"aws_acm_certificate_validation":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"validation_record_fqdns":{"type":["set","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_acmpca_certificate_authority":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate":{"type":"string","computed":true},"certificate_chain":{"type":"string","computed":true},"certificate_signing_request":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"not_after":{"type":"string","computed":true},"not_before":{"type":"string","computed":true},"permanent_deletion_time_in_days":{"type":"number","optional":true},"serial":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true}},"block_types":{"certificate_authority_configuration":{"nesting_mode":"list","block":{"attributes":{"key_algorithm":{"type":"string","required":true},"signing_algorithm":{"type":"string","required":true}},"block_types":{"subject":{"nesting_mode":"list","block":{"attributes":{"common_name":{"type":"string","optional":true},"country":{"type":"string","optional":true},"distinguished_name_qualifier":{"type":"string","optional":true},"generation_qualifier":{"type":"string","optional":true},"given_name":{"type":"string","optional":true},"initials":{"type":"string","optional":true},"locality":{"type":"string","optional":true},"organization":{"type":"string","optional":true},"organizational_unit":{"type":"string","optional":true},"pseudonym":{"type":"string","optional":true},"state":{"type":"string","optional":true},"surname":{"type":"string","optional":true},"title":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"revocation_configuration":{"nesting_mode":"list","block":{"block_types":{"crl_configuration":{"nesting_mode":"list","block":{"attributes":{"custom_cname":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"expiration_in_days":{"type":"number","required":true},"s3_bucket_name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_alb":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"arn_suffix":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"drop_invalid_header_fields":{"type":"bool","optional":true},"enable_cross_zone_load_balancing":{"type":"bool","optional":true},"enable_deletion_protection":{"type":"bool","optional":true},"enable_http2":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","optional":true},"internal":{"type":"bool","optional":true,"computed":true},"ip_address_type":{"type":"string","optional":true,"computed":true},"load_balancer_type":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"subnets":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"zone_id":{"type":"string","computed":true}},"block_types":{"access_logs":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"prefix":{"type":"string","optional":true}}},"max_items":1},"subnet_mapping":{"nesting_mode":"set","block":{"attributes":{"allocation_id":{"type":"string","optional":true},"private_ipv4_address":{"type":"string","optional":true},"subnet_id":{"type":"string","required":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_alb_listener":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"load_balancer_arn":{"type":"string","required":true},"port":{"type":"number","required":true},"protocol":{"type":"string","optional":true},"ssl_policy":{"type":"string","optional":true,"computed":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"attributes":{"order":{"type":"number","optional":true,"computed":true},"target_group_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"authenticate_cognito":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"user_pool_arn":{"type":"string","required":true},"user_pool_client_id":{"type":"string","required":true},"user_pool_domain":{"type":"string","required":true}}},"max_items":1},"authenticate_oidc":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"authorization_endpoint":{"type":"string","required":true},"client_id":{"type":"string","required":true},"client_secret":{"type":"string","required":true,"sensitive":true},"issuer":{"type":"string","required":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"token_endpoint":{"type":"string","required":true},"user_info_endpoint":{"type":"string","required":true}}},"max_items":1},"fixed_response":{"nesting_mode":"list","block":{"attributes":{"content_type":{"type":"string","required":true},"message_body":{"type":"string","optional":true},"status_code":{"type":"string","optional":true,"computed":true}}},"max_items":1},"forward":{"nesting_mode":"list","block":{"block_types":{"stickiness":{"nesting_mode":"list","block":{"attributes":{"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1},"target_group":{"nesting_mode":"set","block":{"attributes":{"arn":{"type":"string","required":true},"weight":{"type":"number","optional":true}}},"min_items":2,"max_items":5}}},"max_items":1},"redirect":{"nesting_mode":"list","block":{"attributes":{"host":{"type":"string","optional":true},"path":{"type":"string","optional":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"query":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"read":{"type":"string","optional":true}}}}}}},"aws_alb_listener_certificate":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true}}}},"aws_alb_listener_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true},"priority":{"type":"number","optional":true,"computed":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"order":{"type":"number","optional":true,"computed":true},"target_group_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"authenticate_cognito":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"user_pool_arn":{"type":"string","required":true},"user_pool_client_id":{"type":"string","required":true},"user_pool_domain":{"type":"string","required":true}}},"max_items":1},"authenticate_oidc":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"authorization_endpoint":{"type":"string","required":true},"client_id":{"type":"string","required":true},"client_secret":{"type":"string","required":true,"sensitive":true},"issuer":{"type":"string","required":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"token_endpoint":{"type":"string","required":true},"user_info_endpoint":{"type":"string","required":true}}},"max_items":1},"fixed_response":{"nesting_mode":"list","block":{"attributes":{"content_type":{"type":"string","required":true},"message_body":{"type":"string","optional":true},"status_code":{"type":"string","optional":true,"computed":true}}},"max_items":1},"forward":{"nesting_mode":"list","block":{"block_types":{"stickiness":{"nesting_mode":"list","block":{"attributes":{"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1},"target_group":{"nesting_mode":"set","block":{"attributes":{"arn":{"type":"string","required":true},"weight":{"type":"number","optional":true}}},"min_items":2,"max_items":5}}},"max_items":1},"redirect":{"nesting_mode":"list","block":{"attributes":{"host":{"type":"string","optional":true},"path":{"type":"string","optional":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"query":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"condition":{"nesting_mode":"set","block":{"block_types":{"host_header":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"http_header":{"nesting_mode":"list","block":{"attributes":{"http_header_name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}},"max_items":1},"http_request_method":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"path_pattern":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"query_string":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"value":{"type":"string","required":true}}}},"source_ip":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1}}},"min_items":1}}}},"aws_alb_target_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"arn_suffix":{"type":"string","computed":true},"deregistration_delay":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"lambda_multi_value_headers_enabled":{"type":"bool","optional":true},"load_balancing_algorithm_type":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"port":{"type":"number","optional":true},"protocol":{"type":"string","optional":true},"proxy_protocol_v2":{"type":"bool","optional":true},"slow_start":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true},"target_type":{"type":"string","optional":true},"vpc_id":{"type":"string","optional":true}},"block_types":{"health_check":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"healthy_threshold":{"type":"number","optional":true},"interval":{"type":"number","optional":true},"matcher":{"type":"string","optional":true,"computed":true},"path":{"type":"string","optional":true,"computed":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"timeout":{"type":"number","optional":true,"computed":true},"unhealthy_threshold":{"type":"number","optional":true}}},"max_items":1},"stickiness":{"nesting_mode":"list","block":{"attributes":{"cookie_duration":{"type":"number","optional":true},"enabled":{"type":"bool","optional":true},"type":{"type":"string","required":true}}},"max_items":1}}}},"aws_alb_target_group_attachment":{"version":0,"block":{"attributes":{"availability_zone":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"target_group_arn":{"type":"string","required":true},"target_id":{"type":"string","required":true}}}},"aws_ami":{"version":0,"block":{"attributes":{"architecture":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"ena_support":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"image_location":{"type":"string","optional":true,"computed":true},"kernel_id":{"type":"string","optional":true},"manage_ebs_snapshots":{"type":"bool","computed":true},"name":{"type":"string","required":true},"ramdisk_id":{"type":"string","optional":true},"root_device_name":{"type":"string","optional":true},"root_snapshot_id":{"type":"string","computed":true},"sriov_net_support":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"virtualization_type":{"type":"string","optional":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"snapshot_id":{"type":"string","optional":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"virtual_name":{"type":"string","required":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_ami_copy":{"version":0,"block":{"attributes":{"architecture":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"ena_support":{"type":"bool","computed":true},"encrypted":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"image_location":{"type":"string","computed":true},"kernel_id":{"type":"string","computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"manage_ebs_snapshots":{"type":"bool","computed":true},"name":{"type":"string","required":true},"ramdisk_id":{"type":"string","computed":true},"root_device_name":{"type":"string","computed":true},"root_snapshot_id":{"type":"string","computed":true},"source_ami_id":{"type":"string","required":true},"source_ami_region":{"type":"string","required":true},"sriov_net_support":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"virtualization_type":{"type":"string","computed":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","computed":true},"device_name":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"iops":{"type":"number","computed":true},"snapshot_id":{"type":"string","computed":true},"volume_size":{"type":"number","computed":true},"volume_type":{"type":"string","computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","computed":true},"virtual_name":{"type":"string","computed":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_ami_from_instance":{"version":0,"block":{"attributes":{"architecture":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"ena_support":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"image_location":{"type":"string","computed":true},"kernel_id":{"type":"string","computed":true},"manage_ebs_snapshots":{"type":"bool","computed":true},"name":{"type":"string","required":true},"ramdisk_id":{"type":"string","computed":true},"root_device_name":{"type":"string","computed":true},"root_snapshot_id":{"type":"string","computed":true},"snapshot_without_reboot":{"type":"bool","optional":true},"source_instance_id":{"type":"string","required":true},"sriov_net_support":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"virtualization_type":{"type":"string","computed":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","computed":true},"device_name":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"iops":{"type":"number","computed":true},"snapshot_id":{"type":"string","computed":true},"volume_size":{"type":"number","computed":true},"volume_type":{"type":"string","computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","computed":true},"virtual_name":{"type":"string","computed":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_ami_launch_permission":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","required":true}}}},"aws_api_gateway_account":{"version":0,"block":{"attributes":{"cloudwatch_role_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"throttle_settings":{"type":["list",["object",{"burst_limit":"number","rate_limit":"number"}]],"computed":true}}}},"aws_api_gateway_api_key":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"value":{"type":"string","optional":true,"computed":true,"sensitive":true}}}},"aws_api_gateway_authorizer":{"version":0,"block":{"attributes":{"authorizer_credentials":{"type":"string","optional":true},"authorizer_result_ttl_in_seconds":{"type":"number","optional":true},"authorizer_uri":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"identity_source":{"type":"string","optional":true},"identity_validation_expression":{"type":"string","optional":true},"name":{"type":"string","required":true},"provider_arns":{"type":["set","string"],"optional":true},"rest_api_id":{"type":"string","required":true},"type":{"type":"string","optional":true}}}},"aws_api_gateway_base_path_mapping":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"base_path":{"type":"string","optional":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"stage_name":{"type":"string","optional":true}}}},"aws_api_gateway_client_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"expiration_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"pem_encoded_certificate":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_api_gateway_deployment":{"version":0,"block":{"attributes":{"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_url":{"type":"string","computed":true},"rest_api_id":{"type":"string","required":true},"stage_description":{"type":"string","optional":true},"stage_name":{"type":"string","optional":true},"triggers":{"type":["map","string"],"optional":true},"variables":{"type":["map","string"],"optional":true}}}},"aws_api_gateway_documentation_part":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"properties":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true}},"block_types":{"location":{"nesting_mode":"list","block":{"attributes":{"method":{"type":"string","optional":true},"name":{"type":"string","optional":true},"path":{"type":"string","optional":true},"status_code":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_api_gateway_documentation_version":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"rest_api_id":{"type":"string","required":true},"version":{"type":"string","required":true}}}},"aws_api_gateway_domain_name":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_arn":{"type":"string","optional":true},"certificate_body":{"type":"string","optional":true},"certificate_chain":{"type":"string","optional":true},"certificate_name":{"type":"string","optional":true},"certificate_private_key":{"type":"string","optional":true,"sensitive":true},"certificate_upload_date":{"type":"string","computed":true},"cloudfront_domain_name":{"type":"string","computed":true},"cloudfront_zone_id":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"regional_certificate_arn":{"type":"string","optional":true},"regional_certificate_name":{"type":"string","optional":true},"regional_domain_name":{"type":"string","computed":true},"regional_zone_id":{"type":"string","computed":true},"security_policy":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"endpoint_configuration":{"nesting_mode":"list","block":{"attributes":{"types":{"type":["list","string"],"required":true}}},"max_items":1}}}},"aws_api_gateway_gateway_response":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"response_parameters":{"type":["map","string"],"optional":true},"response_templates":{"type":["map","string"],"optional":true},"response_type":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"status_code":{"type":"string","optional":true}}}},"aws_api_gateway_integration":{"version":0,"block":{"attributes":{"cache_key_parameters":{"type":["set","string"],"optional":true},"cache_namespace":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","optional":true},"connection_type":{"type":"string","optional":true},"content_handling":{"type":"string","optional":true},"credentials":{"type":"string","optional":true},"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"integration_http_method":{"type":"string","optional":true},"passthrough_behavior":{"type":"string","optional":true,"computed":true},"request_parameters":{"type":["map","string"],"optional":true},"request_templates":{"type":["map","string"],"optional":true},"resource_id":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"timeout_milliseconds":{"type":"number","optional":true},"type":{"type":"string","required":true},"uri":{"type":"string","optional":true}}}},"aws_api_gateway_integration_response":{"version":0,"block":{"attributes":{"content_handling":{"type":"string","optional":true},"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"resource_id":{"type":"string","required":true},"response_parameters":{"type":["map","string"],"optional":true},"response_templates":{"type":["map","string"],"optional":true},"rest_api_id":{"type":"string","required":true},"selection_pattern":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}}},"aws_api_gateway_method":{"version":0,"block":{"attributes":{"api_key_required":{"type":"bool","optional":true},"authorization":{"type":"string","required":true},"authorization_scopes":{"type":["set","string"],"optional":true},"authorizer_id":{"type":"string","optional":true},"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"request_models":{"type":["map","string"],"optional":true},"request_parameters":{"type":["map","bool"],"optional":true},"request_validator_id":{"type":"string","optional":true},"resource_id":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true}}}},"aws_api_gateway_method_response":{"version":0,"block":{"attributes":{"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"resource_id":{"type":"string","required":true},"response_models":{"type":["map","string"],"optional":true},"response_parameters":{"type":["map","bool"],"optional":true},"rest_api_id":{"type":"string","required":true},"status_code":{"type":"string","required":true}}}},"aws_api_gateway_method_settings":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"method_path":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"stage_name":{"type":"string","required":true}},"block_types":{"settings":{"nesting_mode":"list","block":{"attributes":{"cache_data_encrypted":{"type":"bool","optional":true,"computed":true},"cache_ttl_in_seconds":{"type":"number","optional":true,"computed":true},"caching_enabled":{"type":"bool","optional":true,"computed":true},"data_trace_enabled":{"type":"bool","optional":true,"computed":true},"logging_level":{"type":"string","optional":true,"computed":true},"metrics_enabled":{"type":"bool","optional":true,"computed":true},"require_authorization_for_cache_control":{"type":"bool","optional":true,"computed":true},"throttling_burst_limit":{"type":"number","optional":true},"throttling_rate_limit":{"type":"number","optional":true},"unauthorized_cache_control_header_strategy":{"type":"string","optional":true,"computed":true}}},"min_items":1,"max_items":1}}}},"aws_api_gateway_model":{"version":0,"block":{"attributes":{"content_type":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"schema":{"type":"string","optional":true}}}},"aws_api_gateway_request_validator":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true},"validate_request_body":{"type":"bool","optional":true},"validate_request_parameters":{"type":"bool","optional":true}}}},"aws_api_gateway_resource":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"parent_id":{"type":"string","required":true},"path":{"type":"string","computed":true},"path_part":{"type":"string","required":true},"rest_api_id":{"type":"string","required":true}}}},"aws_api_gateway_rest_api":{"version":0,"block":{"attributes":{"api_key_source":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"binary_media_types":{"type":["list","string"],"optional":true},"body":{"type":"string","optional":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"minimum_compression_size":{"type":"number","optional":true},"name":{"type":"string","required":true},"policy":{"type":"string","optional":true},"root_resource_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"endpoint_configuration":{"nesting_mode":"list","block":{"attributes":{"types":{"type":["list","string"],"required":true},"vpc_endpoint_ids":{"type":["set","string"],"optional":true}}},"max_items":1}}}},"aws_api_gateway_stage":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cache_cluster_enabled":{"type":"bool","optional":true},"cache_cluster_size":{"type":"string","optional":true},"client_certificate_id":{"type":"string","optional":true},"deployment_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"documentation_version":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_url":{"type":"string","computed":true},"rest_api_id":{"type":"string","required":true},"stage_name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"variables":{"type":["map","string"],"optional":true},"xray_tracing_enabled":{"type":"bool","optional":true}},"block_types":{"access_log_settings":{"nesting_mode":"list","block":{"attributes":{"destination_arn":{"type":"string","required":true},"format":{"type":"string","required":true}}},"max_items":1}}}},"aws_api_gateway_usage_plan":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"product_code":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"api_stages":{"nesting_mode":"list","block":{"attributes":{"api_id":{"type":"string","required":true},"stage":{"type":"string","required":true}}}},"quota_settings":{"nesting_mode":"list","block":{"attributes":{"limit":{"type":"number","required":true},"offset":{"type":"number","optional":true},"period":{"type":"string","required":true}}},"max_items":1},"throttle_settings":{"nesting_mode":"list","block":{"attributes":{"burst_limit":{"type":"number","optional":true},"rate_limit":{"type":"number","optional":true}}},"max_items":1}}}},"aws_api_gateway_usage_plan_key":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"key_type":{"type":"string","required":true},"name":{"type":"string","computed":true},"usage_plan_id":{"type":"string","required":true},"value":{"type":"string","computed":true}}}},"aws_api_gateway_vpc_link":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"target_arns":{"type":["list","string"],"required":true}}}},"aws_apigatewayv2_api":{"version":0,"block":{"attributes":{"api_endpoint":{"type":"string","computed":true},"api_key_selection_expression":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"credentials_arn":{"type":"string","optional":true},"description":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"protocol_type":{"type":"string","required":true},"route_key":{"type":"string","optional":true},"route_selection_expression":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"target":{"type":"string","optional":true},"version":{"type":"string","optional":true}},"block_types":{"cors_configuration":{"nesting_mode":"list","block":{"attributes":{"allow_credentials":{"type":"bool","optional":true},"allow_headers":{"type":["set","string"],"optional":true},"allow_methods":{"type":["set","string"],"optional":true},"allow_origins":{"type":["set","string"],"optional":true},"expose_headers":{"type":["set","string"],"optional":true},"max_age":{"type":"number","optional":true}}},"max_items":1}}}},"aws_apigatewayv2_api_mapping":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"api_mapping_key":{"type":"string","optional":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"stage":{"type":"string","required":true}}}},"aws_apigatewayv2_authorizer":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"authorizer_credentials_arn":{"type":"string","optional":true},"authorizer_type":{"type":"string","required":true},"authorizer_uri":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"identity_sources":{"type":["set","string"],"required":true},"name":{"type":"string","required":true}},"block_types":{"jwt_configuration":{"nesting_mode":"list","block":{"attributes":{"audience":{"type":["set","string"],"optional":true},"issuer":{"type":"string","optional":true}}},"max_items":1}}}},"aws_apigatewayv2_deployment":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"auto_deployed":{"type":"bool","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"triggers":{"type":["map","string"],"optional":true}}}},"aws_apigatewayv2_domain_name":{"version":0,"block":{"attributes":{"api_mapping_selection_expression":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"domain_name_configuration":{"nesting_mode":"list","block":{"attributes":{"certificate_arn":{"type":"string","required":true},"endpoint_type":{"type":"string","required":true},"hosted_zone_id":{"type":"string","computed":true},"security_policy":{"type":"string","required":true},"target_domain_name":{"type":"string","computed":true}}},"min_items":1,"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"update":{"type":"string","optional":true}}}}}}},"aws_apigatewayv2_integration":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"connection_id":{"type":"string","optional":true},"connection_type":{"type":"string","optional":true},"content_handling_strategy":{"type":"string","optional":true},"credentials_arn":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"integration_method":{"type":"string","optional":true},"integration_response_selection_expression":{"type":"string","computed":true},"integration_type":{"type":"string","required":true},"integration_uri":{"type":"string","optional":true},"passthrough_behavior":{"type":"string","optional":true},"payload_format_version":{"type":"string","optional":true},"request_parameters":{"type":["map","string"],"optional":true},"request_templates":{"type":["map","string"],"optional":true},"template_selection_expression":{"type":"string","optional":true},"timeout_milliseconds":{"type":"number","optional":true}},"block_types":{"tls_config":{"nesting_mode":"list","block":{"attributes":{"server_name_to_verify":{"type":"string","optional":true}}},"max_items":1}}}},"aws_apigatewayv2_integration_response":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"content_handling_strategy":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"integration_id":{"type":"string","required":true},"integration_response_key":{"type":"string","required":true},"response_templates":{"type":["map","string"],"optional":true},"template_selection_expression":{"type":"string","optional":true}}}},"aws_apigatewayv2_model":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"content_type":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"schema":{"type":"string","required":true}}}},"aws_apigatewayv2_route":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"api_key_required":{"type":"bool","optional":true},"authorization_scopes":{"type":["set","string"],"optional":true},"authorization_type":{"type":"string","optional":true},"authorizer_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"model_selection_expression":{"type":"string","optional":true},"operation_name":{"type":"string","optional":true},"request_models":{"type":["map","string"],"optional":true},"route_key":{"type":"string","required":true},"route_response_selection_expression":{"type":"string","optional":true},"target":{"type":"string","optional":true}}}},"aws_apigatewayv2_route_response":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"model_selection_expression":{"type":"string","optional":true},"response_models":{"type":["map","string"],"optional":true},"route_id":{"type":"string","required":true},"route_response_key":{"type":"string","required":true}}}},"aws_apigatewayv2_stage":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"auto_deploy":{"type":"bool","optional":true},"client_certificate_id":{"type":"string","optional":true},"deployment_id":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_url":{"type":"string","computed":true},"name":{"type":"string","required":true},"stage_variables":{"type":["map","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"access_log_settings":{"nesting_mode":"list","block":{"attributes":{"destination_arn":{"type":"string","required":true},"format":{"type":"string","required":true}}},"max_items":1},"default_route_settings":{"nesting_mode":"list","block":{"attributes":{"data_trace_enabled":{"type":"bool","optional":true},"detailed_metrics_enabled":{"type":"bool","optional":true},"logging_level":{"type":"string","optional":true,"computed":true},"throttling_burst_limit":{"type":"number","optional":true},"throttling_rate_limit":{"type":"number","optional":true}}},"max_items":1},"route_settings":{"nesting_mode":"set","block":{"attributes":{"data_trace_enabled":{"type":"bool","optional":true},"detailed_metrics_enabled":{"type":"bool","optional":true},"logging_level":{"type":"string","optional":true,"computed":true},"route_key":{"type":"string","required":true},"throttling_burst_limit":{"type":"number","optional":true},"throttling_rate_limit":{"type":"number","optional":true}}}}}}},"aws_apigatewayv2_vpc_link":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"security_group_ids":{"type":["set","string"],"required":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_app_cookie_stickiness_policy":{"version":0,"block":{"attributes":{"cookie_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"lb_port":{"type":"number","required":true},"load_balancer":{"type":"string","required":true},"name":{"type":"string","required":true}}}},"aws_appautoscaling_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"policy_type":{"type":"string","optional":true},"resource_id":{"type":"string","required":true},"scalable_dimension":{"type":"string","required":true},"service_namespace":{"type":"string","required":true}},"block_types":{"step_scaling_policy_configuration":{"nesting_mode":"list","block":{"attributes":{"adjustment_type":{"type":"string","optional":true},"cooldown":{"type":"number","optional":true},"metric_aggregation_type":{"type":"string","optional":true},"min_adjustment_magnitude":{"type":"number","optional":true}},"block_types":{"step_adjustment":{"nesting_mode":"set","block":{"attributes":{"metric_interval_lower_bound":{"type":"string","optional":true},"metric_interval_upper_bound":{"type":"string","optional":true},"scaling_adjustment":{"type":"number","required":true}}}}}},"max_items":1},"target_tracking_scaling_policy_configuration":{"nesting_mode":"list","block":{"attributes":{"disable_scale_in":{"type":"bool","optional":true},"scale_in_cooldown":{"type":"number","optional":true},"scale_out_cooldown":{"type":"number","optional":true},"target_value":{"type":"number","required":true}},"block_types":{"customized_metric_specification":{"nesting_mode":"list","block":{"attributes":{"metric_name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"statistic":{"type":"string","required":true},"unit":{"type":"string","optional":true}},"block_types":{"dimensions":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}},"max_items":1},"predefined_metric_specification":{"nesting_mode":"list","block":{"attributes":{"predefined_metric_type":{"type":"string","required":true},"resource_label":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_appautoscaling_scheduled_action":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"end_time":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"resource_id":{"type":"string","required":true},"scalable_dimension":{"type":"string","optional":true},"schedule":{"type":"string","optional":true},"service_namespace":{"type":"string","required":true},"start_time":{"type":"string","optional":true}},"block_types":{"scalable_target_action":{"nesting_mode":"list","block":{"attributes":{"max_capacity":{"type":"number","optional":true},"min_capacity":{"type":"number","optional":true}}},"max_items":1}}}},"aws_appautoscaling_target":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"max_capacity":{"type":"number","required":true},"min_capacity":{"type":"number","required":true},"resource_id":{"type":"string","required":true},"role_arn":{"type":"string","optional":true,"computed":true},"scalable_dimension":{"type":"string","required":true},"service_namespace":{"type":"string","required":true}}}},"aws_appmesh_mesh":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"block_types":{"egress_filter":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_appmesh_route":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"mesh_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"virtual_router_name":{"type":"string","required":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"attributes":{"priority":{"type":"number","optional":true}},"block_types":{"http_route":{"nesting_mode":"list","block":{"block_types":{"action":{"nesting_mode":"list","block":{"block_types":{"weighted_target":{"nesting_mode":"set","block":{"attributes":{"virtual_node":{"type":"string","required":true},"weight":{"type":"number","required":true}}},"min_items":1,"max_items":10}}},"min_items":1,"max_items":1},"match":{"nesting_mode":"list","block":{"attributes":{"method":{"type":"string","optional":true},"prefix":{"type":"string","required":true},"scheme":{"type":"string","optional":true}},"block_types":{"header":{"nesting_mode":"set","block":{"attributes":{"invert":{"type":"bool","optional":true},"name":{"type":"string","required":true}},"block_types":{"match":{"nesting_mode":"list","block":{"attributes":{"exact":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"regex":{"type":"string","optional":true},"suffix":{"type":"string","optional":true}},"block_types":{"range":{"nesting_mode":"list","block":{"attributes":{"end":{"type":"number","required":true},"start":{"type":"number","required":true}}},"max_items":1}}},"max_items":1}}},"max_items":10}}},"min_items":1,"max_items":1}}},"max_items":1},"tcp_route":{"nesting_mode":"list","block":{"block_types":{"action":{"nesting_mode":"list","block":{"block_types":{"weighted_target":{"nesting_mode":"set","block":{"attributes":{"virtual_node":{"type":"string","required":true},"weight":{"type":"number","required":true}}},"min_items":1,"max_items":10}}},"min_items":1,"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_appmesh_virtual_node":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"mesh_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"block_types":{"backend":{"nesting_mode":"set","block":{"block_types":{"virtual_service":{"nesting_mode":"list","block":{"attributes":{"virtual_service_name":{"type":"string","required":true}}},"max_items":1}}},"max_items":25},"listener":{"nesting_mode":"list","block":{"block_types":{"health_check":{"nesting_mode":"list","block":{"attributes":{"healthy_threshold":{"type":"number","required":true},"interval_millis":{"type":"number","required":true},"path":{"type":"string","optional":true},"port":{"type":"number","optional":true,"computed":true},"protocol":{"type":"string","required":true},"timeout_millis":{"type":"number","required":true},"unhealthy_threshold":{"type":"number","required":true}}},"max_items":1},"port_mapping":{"nesting_mode":"list","block":{"attributes":{"port":{"type":"number","required":true},"protocol":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"max_items":1},"logging":{"nesting_mode":"list","block":{"block_types":{"access_log":{"nesting_mode":"list","block":{"block_types":{"file":{"nesting_mode":"list","block":{"attributes":{"path":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"max_items":1},"service_discovery":{"nesting_mode":"list","block":{"block_types":{"aws_cloud_map":{"nesting_mode":"list","block":{"attributes":{"attributes":{"type":["map","string"],"optional":true},"namespace_name":{"type":"string","required":true},"service_name":{"type":"string","required":true}}},"max_items":1},"dns":{"nesting_mode":"list","block":{"attributes":{"hostname":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_appmesh_virtual_router":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"mesh_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"block_types":{"listener":{"nesting_mode":"list","block":{"block_types":{"port_mapping":{"nesting_mode":"list","block":{"attributes":{"port":{"type":"number","required":true},"protocol":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_appmesh_virtual_service":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_date":{"type":"string","computed":true},"mesh_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"spec":{"nesting_mode":"list","block":{"block_types":{"provider":{"nesting_mode":"list","block":{"block_types":{"virtual_node":{"nesting_mode":"list","block":{"attributes":{"virtual_node_name":{"type":"string","required":true}}},"max_items":1},"virtual_router":{"nesting_mode":"list","block":{"attributes":{"virtual_router_name":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_appsync_api_key":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"expires":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","computed":true,"sensitive":true}}}},"aws_appsync_datasource":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"service_role_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"dynamodb_config":{"nesting_mode":"list","block":{"attributes":{"region":{"type":"string","optional":true,"computed":true},"table_name":{"type":"string","required":true},"use_caller_credentials":{"type":"bool","optional":true}}},"max_items":1},"elasticsearch_config":{"nesting_mode":"list","block":{"attributes":{"endpoint":{"type":"string","required":true},"region":{"type":"string","optional":true,"computed":true}}},"max_items":1},"http_config":{"nesting_mode":"list","block":{"attributes":{"endpoint":{"type":"string","required":true}}},"max_items":1},"lambda_config":{"nesting_mode":"list","block":{"attributes":{"function_arn":{"type":"string","required":true}}},"max_items":1}}}},"aws_appsync_function":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"data_source":{"type":"string","required":true},"description":{"type":"string","optional":true},"function_id":{"type":"string","computed":true},"function_version":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"request_mapping_template":{"type":"string","required":true},"response_mapping_template":{"type":"string","required":true}}}},"aws_appsync_graphql_api":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"authentication_type":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"schema":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"uris":{"type":["map","string"],"computed":true},"xray_enabled":{"type":"bool","optional":true}},"block_types":{"additional_authentication_provider":{"nesting_mode":"list","block":{"attributes":{"authentication_type":{"type":"string","required":true}},"block_types":{"openid_connect_config":{"nesting_mode":"list","block":{"attributes":{"auth_ttl":{"type":"number","optional":true},"client_id":{"type":"string","optional":true},"iat_ttl":{"type":"number","optional":true},"issuer":{"type":"string","required":true}}},"max_items":1},"user_pool_config":{"nesting_mode":"list","block":{"attributes":{"app_id_client_regex":{"type":"string","optional":true},"aws_region":{"type":"string","optional":true,"computed":true},"user_pool_id":{"type":"string","required":true}}},"max_items":1}}}},"log_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_logs_role_arn":{"type":"string","required":true},"exclude_verbose_content":{"type":"bool","optional":true},"field_log_level":{"type":"string","required":true}}},"max_items":1},"openid_connect_config":{"nesting_mode":"list","block":{"attributes":{"auth_ttl":{"type":"number","optional":true},"client_id":{"type":"string","optional":true},"iat_ttl":{"type":"number","optional":true},"issuer":{"type":"string","required":true}}},"max_items":1},"user_pool_config":{"nesting_mode":"list","block":{"attributes":{"app_id_client_regex":{"type":"string","optional":true},"aws_region":{"type":"string","optional":true,"computed":true},"default_action":{"type":"string","required":true},"user_pool_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_appsync_resolver":{"version":0,"block":{"attributes":{"api_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"data_source":{"type":"string","optional":true},"field":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"kind":{"type":"string","optional":true},"request_template":{"type":"string","required":true},"response_template":{"type":"string","required":true},"type":{"type":"string","required":true}},"block_types":{"caching_config":{"nesting_mode":"list","block":{"attributes":{"caching_keys":{"type":["set","string"],"optional":true},"ttl":{"type":"number","optional":true}}},"max_items":1},"pipeline_config":{"nesting_mode":"list","block":{"attributes":{"functions":{"type":["list","string"],"optional":true}}},"max_items":1}}}},"aws_athena_database":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"encryption_configuration":{"nesting_mode":"list","block":{"attributes":{"encryption_option":{"type":"string","required":true},"kms_key":{"type":"string","optional":true}}},"max_items":1}}}},"aws_athena_named_query":{"version":0,"block":{"attributes":{"database":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"query":{"type":"string","required":true},"workgroup":{"type":"string","optional":true}}}},"aws_athena_workgroup":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"state":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"configuration":{"nesting_mode":"list","block":{"attributes":{"bytes_scanned_cutoff_per_query":{"type":"number","optional":true},"enforce_workgroup_configuration":{"type":"bool","optional":true},"publish_cloudwatch_metrics_enabled":{"type":"bool","optional":true}},"block_types":{"result_configuration":{"nesting_mode":"list","block":{"attributes":{"output_location":{"type":"string","optional":true}},"block_types":{"encryption_configuration":{"nesting_mode":"list","block":{"attributes":{"encryption_option":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}},"max_items":1}}}},"aws_autoscaling_attachment":{"version":0,"block":{"attributes":{"alb_target_group_arn":{"type":"string","optional":true},"autoscaling_group_name":{"type":"string","required":true},"elb":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_autoscaling_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"default_cooldown":{"type":"number","optional":true,"computed":true},"desired_capacity":{"type":"number","optional":true,"computed":true},"enabled_metrics":{"type":["set","string"],"optional":true},"force_delete":{"type":"bool","optional":true},"health_check_grace_period":{"type":"number","optional":true},"health_check_type":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"launch_configuration":{"type":"string","optional":true},"load_balancers":{"type":["set","string"],"optional":true},"max_instance_lifetime":{"type":"number","optional":true},"max_size":{"type":"number","required":true},"metrics_granularity":{"type":"string","optional":true},"min_elb_capacity":{"type":"number","optional":true},"min_size":{"type":"number","required":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"placement_group":{"type":"string","optional":true},"protect_from_scale_in":{"type":"bool","optional":true},"service_linked_role_arn":{"type":"string","optional":true,"computed":true},"suspended_processes":{"type":["set","string"],"optional":true},"tags":{"type":["set",["map","string"]],"optional":true},"target_group_arns":{"type":["set","string"],"optional":true},"termination_policies":{"type":["list","string"],"optional":true},"vpc_zone_identifier":{"type":["set","string"],"optional":true,"computed":true},"wait_for_capacity_timeout":{"type":"string","optional":true},"wait_for_elb_capacity":{"type":"number","optional":true}},"block_types":{"initial_lifecycle_hook":{"nesting_mode":"set","block":{"attributes":{"default_result":{"type":"string","optional":true,"computed":true},"heartbeat_timeout":{"type":"number","optional":true},"lifecycle_transition":{"type":"string","required":true},"name":{"type":"string","required":true},"notification_metadata":{"type":"string","optional":true},"notification_target_arn":{"type":"string","optional":true},"role_arn":{"type":"string","optional":true}}}},"launch_template":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"version":{"type":"string","optional":true}}},"max_items":1},"mixed_instances_policy":{"nesting_mode":"list","block":{"block_types":{"instances_distribution":{"nesting_mode":"list","block":{"attributes":{"on_demand_allocation_strategy":{"type":"string","optional":true,"computed":true},"on_demand_base_capacity":{"type":"number","optional":true,"computed":true},"on_demand_percentage_above_base_capacity":{"type":"number","optional":true,"computed":true},"spot_allocation_strategy":{"type":"string","optional":true,"computed":true},"spot_instance_pools":{"type":"number","optional":true,"computed":true},"spot_max_price":{"type":"string","optional":true}}},"max_items":1},"launch_template":{"nesting_mode":"list","block":{"block_types":{"launch_template_specification":{"nesting_mode":"list","block":{"attributes":{"launch_template_id":{"type":"string","optional":true,"computed":true},"launch_template_name":{"type":"string","optional":true,"computed":true},"version":{"type":"string","optional":true}}},"min_items":1,"max_items":1},"override":{"nesting_mode":"list","block":{"attributes":{"instance_type":{"type":"string","optional":true},"weighted_capacity":{"type":"string","optional":true}}}}}},"min_items":1,"max_items":1}}},"max_items":1},"tag":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","required":true},"propagate_at_launch":{"type":"bool","required":true},"value":{"type":"string","required":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true}}}}}}},"aws_autoscaling_lifecycle_hook":{"version":0,"block":{"attributes":{"autoscaling_group_name":{"type":"string","required":true},"default_result":{"type":"string","optional":true,"computed":true},"heartbeat_timeout":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"lifecycle_transition":{"type":"string","required":true},"name":{"type":"string","required":true},"notification_metadata":{"type":"string","optional":true},"notification_target_arn":{"type":"string","optional":true},"role_arn":{"type":"string","optional":true}}}},"aws_autoscaling_notification":{"version":0,"block":{"attributes":{"group_names":{"type":["set","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"notifications":{"type":["set","string"],"required":true},"topic_arn":{"type":"string","required":true}}}},"aws_autoscaling_policy":{"version":0,"block":{"attributes":{"adjustment_type":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"autoscaling_group_name":{"type":"string","required":true},"cooldown":{"type":"number","optional":true},"estimated_instance_warmup":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"metric_aggregation_type":{"type":"string","optional":true,"computed":true},"min_adjustment_magnitude":{"type":"number","optional":true},"name":{"type":"string","required":true},"policy_type":{"type":"string","optional":true},"scaling_adjustment":{"type":"number","optional":true}},"block_types":{"step_adjustment":{"nesting_mode":"set","block":{"attributes":{"metric_interval_lower_bound":{"type":"string","optional":true},"metric_interval_upper_bound":{"type":"string","optional":true},"scaling_adjustment":{"type":"number","required":true}}}},"target_tracking_configuration":{"nesting_mode":"list","block":{"attributes":{"disable_scale_in":{"type":"bool","optional":true},"target_value":{"type":"number","required":true}},"block_types":{"customized_metric_specification":{"nesting_mode":"list","block":{"attributes":{"metric_name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"statistic":{"type":"string","required":true},"unit":{"type":"string","optional":true}},"block_types":{"metric_dimension":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}},"max_items":1},"predefined_metric_specification":{"nesting_mode":"list","block":{"attributes":{"predefined_metric_type":{"type":"string","required":true},"resource_label":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_autoscaling_schedule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"autoscaling_group_name":{"type":"string","required":true},"desired_capacity":{"type":"number","optional":true,"computed":true},"end_time":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"max_size":{"type":"number","optional":true,"computed":true},"min_size":{"type":"number","optional":true,"computed":true},"recurrence":{"type":"string","optional":true,"computed":true},"scheduled_action_name":{"type":"string","required":true},"start_time":{"type":"string","optional":true,"computed":true}}}},"aws_backup_plan":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","computed":true}},"block_types":{"rule":{"nesting_mode":"set","block":{"attributes":{"completion_window":{"type":"number","optional":true},"recovery_point_tags":{"type":["map","string"],"optional":true},"rule_name":{"type":"string","required":true},"schedule":{"type":"string","optional":true},"start_window":{"type":"number","optional":true},"target_vault_name":{"type":"string","required":true}},"block_types":{"copy_action":{"nesting_mode":"set","block":{"attributes":{"destination_vault_arn":{"type":"string","required":true}},"block_types":{"lifecycle":{"nesting_mode":"list","block":{"attributes":{"cold_storage_after":{"type":"number","optional":true},"delete_after":{"type":"number","optional":true}}},"max_items":1}}}},"lifecycle":{"nesting_mode":"list","block":{"attributes":{"cold_storage_after":{"type":"number","optional":true},"delete_after":{"type":"number","optional":true}}},"max_items":1}}},"min_items":1}}}},"aws_backup_selection":{"version":0,"block":{"attributes":{"iam_role_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"plan_id":{"type":"string","required":true},"resources":{"type":["set","string"],"optional":true}},"block_types":{"selection_tag":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","required":true},"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_backup_vault":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"recovery_points":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_batch_compute_environment":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compute_environment_name":{"type":"string","optional":true,"computed":true},"compute_environment_name_prefix":{"type":"string","optional":true},"ecs_cluster_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"service_role":{"type":"string","required":true},"state":{"type":"string","optional":true},"status":{"type":"string","computed":true},"status_reason":{"type":"string","computed":true},"type":{"type":"string","required":true}},"block_types":{"compute_resources":{"nesting_mode":"list","block":{"attributes":{"allocation_strategy":{"type":"string","optional":true},"bid_percentage":{"type":"number","optional":true},"desired_vcpus":{"type":"number","optional":true,"computed":true},"ec2_key_pair":{"type":"string","optional":true},"image_id":{"type":"string","optional":true},"instance_role":{"type":"string","required":true},"instance_type":{"type":["set","string"],"required":true},"max_vcpus":{"type":"number","required":true},"min_vcpus":{"type":"number","required":true},"security_group_ids":{"type":["set","string"],"required":true},"spot_iam_fleet_role":{"type":"string","optional":true},"subnets":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","required":true}},"block_types":{"launch_template":{"nesting_mode":"list","block":{"attributes":{"launch_template_id":{"type":"string","optional":true},"launch_template_name":{"type":"string","optional":true},"version":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_batch_job_definition":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"container_properties":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"parameters":{"type":["map","string"],"optional":true},"revision":{"type":"number","computed":true},"type":{"type":"string","required":true}},"block_types":{"retry_strategy":{"nesting_mode":"list","block":{"attributes":{"attempts":{"type":"number","optional":true}}},"max_items":1},"timeout":{"nesting_mode":"list","block":{"attributes":{"attempt_duration_seconds":{"type":"number","optional":true}}},"max_items":1}}}},"aws_batch_job_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compute_environments":{"type":["list","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"priority":{"type":"number","required":true},"state":{"type":"string","required":true}}}},"aws_budgets_budget":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"budget_type":{"type":"string","required":true},"cost_filters":{"type":["map","string"],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"limit_amount":{"type":"string","required":true},"limit_unit":{"type":"string","required":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"time_period_end":{"type":"string","optional":true},"time_period_start":{"type":"string","required":true},"time_unit":{"type":"string","required":true}},"block_types":{"cost_types":{"nesting_mode":"list","block":{"attributes":{"include_credit":{"type":"bool","optional":true},"include_discount":{"type":"bool","optional":true},"include_other_subscription":{"type":"bool","optional":true},"include_recurring":{"type":"bool","optional":true},"include_refund":{"type":"bool","optional":true},"include_subscription":{"type":"bool","optional":true},"include_support":{"type":"bool","optional":true},"include_tax":{"type":"bool","optional":true},"include_upfront":{"type":"bool","optional":true},"use_amortized":{"type":"bool","optional":true},"use_blended":{"type":"bool","optional":true}}},"max_items":1},"notification":{"nesting_mode":"set","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"notification_type":{"type":"string","required":true},"subscriber_email_addresses":{"type":["set","string"],"optional":true},"subscriber_sns_topic_arns":{"type":["set","string"],"optional":true},"threshold":{"type":"number","required":true},"threshold_type":{"type":"string","required":true}}}}}}},"aws_cloud9_environment_ec2":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"automatic_stop_time_minutes":{"type":"number","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","required":true},"name":{"type":"string","required":true},"owner_arn":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","computed":true}}}},"aws_cloudformation_stack":{"version":0,"block":{"attributes":{"capabilities":{"type":["set","string"],"optional":true},"disable_rollback":{"type":"bool","optional":true},"iam_role_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"notification_arns":{"type":["set","string"],"optional":true},"on_failure":{"type":"string","optional":true},"outputs":{"type":["map","string"],"computed":true},"parameters":{"type":["map","string"],"optional":true,"computed":true},"policy_body":{"type":"string","optional":true,"computed":true},"policy_url":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"template_body":{"type":"string","optional":true,"computed":true},"template_url":{"type":"string","optional":true},"timeout_in_minutes":{"type":"number","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cloudformation_stack_set":{"version":0,"block":{"attributes":{"administration_role_arn":{"type":"string","required":true},"arn":{"type":"string","computed":true},"capabilities":{"type":["set","string"],"optional":true},"description":{"type":"string","optional":true},"execution_role_name":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"parameters":{"type":["map","string"],"optional":true},"stack_set_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"template_body":{"type":"string","optional":true,"computed":true},"template_url":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"update":{"type":"string","optional":true}}}}}}},"aws_cloudformation_stack_set_instance":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"parameter_overrides":{"type":["map","string"],"optional":true},"region":{"type":"string","optional":true,"computed":true},"retain_stack":{"type":"bool","optional":true},"stack_id":{"type":"string","computed":true},"stack_set_name":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cloudfront_distribution":{"version":1,"block":{"attributes":{"aliases":{"type":["set","string"],"optional":true},"arn":{"type":"string","computed":true},"caller_reference":{"type":"string","computed":true},"comment":{"type":"string","optional":true},"default_root_object":{"type":"string","optional":true},"domain_name":{"type":"string","computed":true},"enabled":{"type":"bool","required":true},"etag":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"http_version":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"in_progress_validation_batches":{"type":"number","computed":true},"is_ipv6_enabled":{"type":"bool","optional":true},"last_modified_time":{"type":"string","computed":true},"price_class":{"type":"string","optional":true},"retain_on_delete":{"type":"bool","optional":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"trusted_signers":{"type":["list",["object",{"enabled":"bool","items":["list",["object",{"aws_account_number":"string","key_pair_ids":["set","string"]}]]}]],"computed":true},"wait_for_deployment":{"type":"bool","optional":true},"web_acl_id":{"type":"string","optional":true}},"block_types":{"custom_error_response":{"nesting_mode":"set","block":{"attributes":{"error_caching_min_ttl":{"type":"number","optional":true},"error_code":{"type":"number","required":true},"response_code":{"type":"number","optional":true},"response_page_path":{"type":"string","optional":true}}}},"default_cache_behavior":{"nesting_mode":"list","block":{"attributes":{"allowed_methods":{"type":["set","string"],"required":true},"cached_methods":{"type":["set","string"],"required":true},"compress":{"type":"bool","optional":true},"default_ttl":{"type":"number","optional":true},"field_level_encryption_id":{"type":"string","optional":true},"max_ttl":{"type":"number","optional":true},"min_ttl":{"type":"number","optional":true},"smooth_streaming":{"type":"bool","optional":true},"target_origin_id":{"type":"string","required":true},"trusted_signers":{"type":["list","string"],"optional":true},"viewer_protocol_policy":{"type":"string","required":true}},"block_types":{"forwarded_values":{"nesting_mode":"list","block":{"attributes":{"headers":{"type":["set","string"],"optional":true},"query_string":{"type":"bool","required":true},"query_string_cache_keys":{"type":["list","string"],"optional":true}},"block_types":{"cookies":{"nesting_mode":"list","block":{"attributes":{"forward":{"type":"string","required":true},"whitelisted_names":{"type":["set","string"],"optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"lambda_function_association":{"nesting_mode":"set","block":{"attributes":{"event_type":{"type":"string","required":true},"include_body":{"type":"bool","optional":true},"lambda_arn":{"type":"string","required":true}}},"max_items":4}}},"min_items":1,"max_items":1},"logging_config":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"include_cookies":{"type":"bool","optional":true},"prefix":{"type":"string","optional":true}}},"max_items":1},"ordered_cache_behavior":{"nesting_mode":"list","block":{"attributes":{"allowed_methods":{"type":["set","string"],"required":true},"cached_methods":{"type":["set","string"],"required":true},"compress":{"type":"bool","optional":true},"default_ttl":{"type":"number","optional":true},"field_level_encryption_id":{"type":"string","optional":true},"max_ttl":{"type":"number","optional":true},"min_ttl":{"type":"number","optional":true},"path_pattern":{"type":"string","required":true},"smooth_streaming":{"type":"bool","optional":true},"target_origin_id":{"type":"string","required":true},"trusted_signers":{"type":["list","string"],"optional":true},"viewer_protocol_policy":{"type":"string","required":true}},"block_types":{"forwarded_values":{"nesting_mode":"list","block":{"attributes":{"headers":{"type":["set","string"],"optional":true},"query_string":{"type":"bool","required":true},"query_string_cache_keys":{"type":["list","string"],"optional":true}},"block_types":{"cookies":{"nesting_mode":"list","block":{"attributes":{"forward":{"type":"string","required":true},"whitelisted_names":{"type":["set","string"],"optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"lambda_function_association":{"nesting_mode":"set","block":{"attributes":{"event_type":{"type":"string","required":true},"include_body":{"type":"bool","optional":true},"lambda_arn":{"type":"string","required":true}}},"max_items":4}}}},"origin":{"nesting_mode":"set","block":{"attributes":{"domain_name":{"type":"string","required":true},"origin_id":{"type":"string","required":true},"origin_path":{"type":"string","optional":true}},"block_types":{"custom_header":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}},"custom_origin_config":{"nesting_mode":"list","block":{"attributes":{"http_port":{"type":"number","required":true},"https_port":{"type":"number","required":true},"origin_keepalive_timeout":{"type":"number","optional":true},"origin_protocol_policy":{"type":"string","required":true},"origin_read_timeout":{"type":"number","optional":true},"origin_ssl_protocols":{"type":["set","string"],"required":true}}},"max_items":1},"s3_origin_config":{"nesting_mode":"list","block":{"attributes":{"origin_access_identity":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"origin_group":{"nesting_mode":"set","block":{"attributes":{"origin_id":{"type":"string","required":true}},"block_types":{"failover_criteria":{"nesting_mode":"list","block":{"attributes":{"status_codes":{"type":["set","number"],"required":true}}},"min_items":1,"max_items":1},"member":{"nesting_mode":"list","block":{"attributes":{"origin_id":{"type":"string","required":true}}},"min_items":2,"max_items":2}}}},"restrictions":{"nesting_mode":"list","block":{"block_types":{"geo_restriction":{"nesting_mode":"list","block":{"attributes":{"locations":{"type":["set","string"],"optional":true},"restriction_type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"viewer_certificate":{"nesting_mode":"list","block":{"attributes":{"acm_certificate_arn":{"type":"string","optional":true},"cloudfront_default_certificate":{"type":"bool","optional":true},"iam_certificate_id":{"type":"string","optional":true},"minimum_protocol_version":{"type":"string","optional":true},"ssl_support_method":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}}},"aws_cloudfront_origin_access_identity":{"version":0,"block":{"attributes":{"caller_reference":{"type":"string","computed":true},"cloudfront_access_identity_path":{"type":"string","computed":true},"comment":{"type":"string","optional":true},"etag":{"type":"string","computed":true},"iam_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"s3_canonical_user_id":{"type":"string","computed":true}}}},"aws_cloudfront_public_key":{"version":0,"block":{"attributes":{"caller_reference":{"type":"string","computed":true},"comment":{"type":"string","optional":true},"encoded_key":{"type":"string","required":true},"etag":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true}}}},"aws_cloudhsm_v2_cluster":{"version":0,"block":{"attributes":{"cluster_certificates":{"type":["list",["object",{"aws_hardware_certificate":"string","cluster_certificate":"string","cluster_csr":"string","hsm_certificate":"string","manufacturer_hardware_certificate":"string"}]],"computed":true},"cluster_id":{"type":"string","computed":true},"cluster_state":{"type":"string","computed":true},"hsm_type":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"security_group_id":{"type":"string","computed":true},"source_backup_identifier":{"type":"string","optional":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cloudhsm_v2_hsm":{"version":0,"block":{"attributes":{"availability_zone":{"type":"string","optional":true,"computed":true},"cluster_id":{"type":"string","required":true},"hsm_eni_id":{"type":"string","computed":true},"hsm_id":{"type":"string","computed":true},"hsm_state":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cloudtrail":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cloud_watch_logs_group_arn":{"type":"string","optional":true},"cloud_watch_logs_role_arn":{"type":"string","optional":true},"enable_log_file_validation":{"type":"bool","optional":true},"enable_logging":{"type":"bool","optional":true},"home_region":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"include_global_service_events":{"type":"bool","optional":true},"is_multi_region_trail":{"type":"bool","optional":true},"is_organization_trail":{"type":"bool","optional":true},"kms_key_id":{"type":"string","optional":true},"name":{"type":"string","required":true},"s3_bucket_name":{"type":"string","required":true},"s3_key_prefix":{"type":"string","optional":true},"sns_topic_name":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"event_selector":{"nesting_mode":"list","block":{"attributes":{"include_management_events":{"type":"bool","optional":true},"read_write_type":{"type":"string","optional":true}},"block_types":{"data_resource":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}},"max_items":5}}}},"aws_cloudwatch_dashboard":{"version":0,"block":{"attributes":{"dashboard_arn":{"type":"string","computed":true},"dashboard_body":{"type":"string","required":true},"dashboard_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_cloudwatch_event_permission":{"version":0,"block":{"attributes":{"action":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"principal":{"type":"string","required":true},"statement_id":{"type":"string","required":true}},"block_types":{"condition":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"type":{"type":"string","required":true},"value":{"type":"string","required":true}}},"max_items":1}}}},"aws_cloudwatch_event_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"event_pattern":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"is_enabled":{"type":"bool","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"role_arn":{"type":"string","optional":true},"schedule_expression":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_cloudwatch_event_target":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"input":{"type":"string","optional":true},"input_path":{"type":"string","optional":true},"role_arn":{"type":"string","optional":true},"rule":{"type":"string","required":true},"target_id":{"type":"string","optional":true,"computed":true}},"block_types":{"batch_target":{"nesting_mode":"list","block":{"attributes":{"array_size":{"type":"number","optional":true},"job_attempts":{"type":"number","optional":true},"job_definition":{"type":"string","required":true},"job_name":{"type":"string","required":true}}},"max_items":1},"ecs_target":{"nesting_mode":"list","block":{"attributes":{"group":{"type":"string","optional":true},"launch_type":{"type":"string","optional":true},"platform_version":{"type":"string","optional":true},"task_count":{"type":"number","optional":true},"task_definition_arn":{"type":"string","required":true}},"block_types":{"network_configuration":{"nesting_mode":"list","block":{"attributes":{"assign_public_ip":{"type":"bool","optional":true},"security_groups":{"type":["set","string"],"optional":true},"subnets":{"type":["set","string"],"required":true}}},"max_items":1}}},"max_items":1},"input_transformer":{"nesting_mode":"list","block":{"attributes":{"input_paths":{"type":["map","string"],"optional":true},"input_template":{"type":"string","required":true}}},"max_items":1},"kinesis_target":{"nesting_mode":"list","block":{"attributes":{"partition_key_path":{"type":"string","optional":true}}},"max_items":1},"run_command_targets":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"max_items":5},"sqs_target":{"nesting_mode":"list","block":{"attributes":{"message_group_id":{"type":"string","optional":true}}},"max_items":1}}}},"aws_cloudwatch_log_destination":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"target_arn":{"type":"string","required":true}}}},"aws_cloudwatch_log_destination_policy":{"version":0,"block":{"attributes":{"access_policy":{"type":"string","required":true},"destination_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_cloudwatch_log_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"retention_in_days":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_cloudwatch_log_metric_filter":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","required":true},"name":{"type":"string","required":true},"pattern":{"type":"string","required":true}},"block_types":{"metric_transformation":{"nesting_mode":"list","block":{"attributes":{"default_value":{"type":"string","optional":true},"name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"value":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_cloudwatch_log_resource_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy_document":{"type":"string","required":true},"policy_name":{"type":"string","required":true}}}},"aws_cloudwatch_log_stream":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","required":true},"name":{"type":"string","required":true}}}},"aws_cloudwatch_log_subscription_filter":{"version":0,"block":{"attributes":{"destination_arn":{"type":"string","required":true},"distribution":{"type":"string","optional":true},"filter_pattern":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","required":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","optional":true,"computed":true}}}},"aws_cloudwatch_metric_alarm":{"version":1,"block":{"attributes":{"actions_enabled":{"type":"bool","optional":true},"alarm_actions":{"type":["set","string"],"optional":true},"alarm_description":{"type":"string","optional":true},"alarm_name":{"type":"string","required":true},"arn":{"type":"string","computed":true},"comparison_operator":{"type":"string","required":true},"datapoints_to_alarm":{"type":"number","optional":true},"dimensions":{"type":["map","string"],"optional":true},"evaluate_low_sample_count_percentiles":{"type":"string","optional":true,"computed":true},"evaluation_periods":{"type":"number","required":true},"extended_statistic":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"insufficient_data_actions":{"type":["set","string"],"optional":true},"metric_name":{"type":"string","optional":true},"namespace":{"type":"string","optional":true},"ok_actions":{"type":["set","string"],"optional":true},"period":{"type":"number","optional":true},"statistic":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"threshold":{"type":"number","optional":true},"threshold_metric_id":{"type":"string","optional":true},"treat_missing_data":{"type":"string","optional":true},"unit":{"type":"string","optional":true}},"block_types":{"metric_query":{"nesting_mode":"set","block":{"attributes":{"expression":{"type":"string","optional":true},"id":{"type":"string","required":true},"label":{"type":"string","optional":true},"return_data":{"type":"bool","optional":true}},"block_types":{"metric":{"nesting_mode":"list","block":{"attributes":{"dimensions":{"type":["map","string"],"optional":true},"metric_name":{"type":"string","required":true},"namespace":{"type":"string","optional":true},"period":{"type":"number","required":true},"stat":{"type":"string","required":true},"unit":{"type":"string","optional":true}}},"max_items":1}}}}}}},"aws_codebuild_project":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"badge_enabled":{"type":"bool","optional":true},"badge_url":{"type":"string","computed":true},"build_timeout":{"type":"number","optional":true},"description":{"type":"string","optional":true,"computed":true},"encryption_key":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"queued_timeout":{"type":"number","optional":true},"service_role":{"type":"string","required":true},"source_version":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"artifacts":{"nesting_mode":"list","block":{"attributes":{"artifact_identifier":{"type":"string","optional":true},"encryption_disabled":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"name":{"type":"string","optional":true},"namespace_type":{"type":"string","optional":true},"override_artifact_name":{"type":"bool","optional":true},"packaging":{"type":"string","optional":true},"path":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1},"cache":{"nesting_mode":"list","block":{"attributes":{"location":{"type":"string","optional":true},"modes":{"type":["list","string"],"optional":true},"type":{"type":"string","optional":true}}},"max_items":1},"environment":{"nesting_mode":"list","block":{"attributes":{"certificate":{"type":"string","optional":true},"compute_type":{"type":"string","required":true},"image":{"type":"string","required":true},"image_pull_credentials_type":{"type":"string","optional":true},"privileged_mode":{"type":"bool","optional":true},"type":{"type":"string","required":true}},"block_types":{"environment_variable":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true},"type":{"type":"string","optional":true},"value":{"type":"string","required":true}}}},"registry_credential":{"nesting_mode":"list","block":{"attributes":{"credential":{"type":"string","required":true},"credential_provider":{"type":"string","required":true}}},"max_items":1}}},"min_items":1,"max_items":1},"logs_config":{"nesting_mode":"list","block":{"block_types":{"cloudwatch_logs":{"nesting_mode":"list","block":{"attributes":{"group_name":{"type":"string","optional":true},"status":{"type":"string","optional":true},"stream_name":{"type":"string","optional":true}}},"max_items":1},"s3_logs":{"nesting_mode":"list","block":{"attributes":{"encryption_disabled":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"status":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"secondary_artifacts":{"nesting_mode":"set","block":{"attributes":{"artifact_identifier":{"type":"string","required":true},"encryption_disabled":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"name":{"type":"string","optional":true},"namespace_type":{"type":"string","optional":true},"override_artifact_name":{"type":"bool","optional":true},"packaging":{"type":"string","optional":true},"path":{"type":"string","optional":true},"type":{"type":"string","required":true}}}},"secondary_sources":{"nesting_mode":"set","block":{"attributes":{"buildspec":{"type":"string","optional":true},"git_clone_depth":{"type":"number","optional":true},"insecure_ssl":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"report_build_status":{"type":"bool","optional":true},"source_identifier":{"type":"string","required":true},"type":{"type":"string","required":true}},"block_types":{"auth":{"nesting_mode":"list","block":{"attributes":{"resource":{"type":"string","optional":true,"sensitive":true},"type":{"type":"string","required":true}}},"max_items":1},"git_submodules_config":{"nesting_mode":"list","block":{"attributes":{"fetch_submodules":{"type":"bool","required":true}}},"max_items":1}}}},"source":{"nesting_mode":"list","block":{"attributes":{"buildspec":{"type":"string","optional":true},"git_clone_depth":{"type":"number","optional":true},"insecure_ssl":{"type":"bool","optional":true},"location":{"type":"string","optional":true},"report_build_status":{"type":"bool","optional":true},"type":{"type":"string","required":true}},"block_types":{"auth":{"nesting_mode":"list","block":{"attributes":{"resource":{"type":"string","optional":true,"sensitive":true},"type":{"type":"string","required":true}}},"max_items":1},"git_submodules_config":{"nesting_mode":"list","block":{"attributes":{"fetch_submodules":{"type":"bool","required":true}}},"max_items":1}}},"min_items":1,"max_items":1},"vpc_config":{"nesting_mode":"list","block":{"attributes":{"security_group_ids":{"type":["set","string"],"required":true},"subnets":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_codebuild_source_credential":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auth_type":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"server_type":{"type":"string","required":true},"token":{"type":"string","required":true,"sensitive":true},"user_name":{"type":"string","optional":true}}}},"aws_codebuild_webhook":{"version":0,"block":{"attributes":{"branch_filter":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"payload_url":{"type":"string","computed":true},"project_name":{"type":"string","required":true},"secret":{"type":"string","computed":true,"sensitive":true},"url":{"type":"string","computed":true}},"block_types":{"filter_group":{"nesting_mode":"set","block":{"block_types":{"filter":{"nesting_mode":"list","block":{"attributes":{"exclude_matched_pattern":{"type":"bool","optional":true},"pattern":{"type":"string","required":true},"type":{"type":"string","required":true}}}}}}}}}},"aws_codecommit_repository":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"clone_url_http":{"type":"string","computed":true},"clone_url_ssh":{"type":"string","computed":true},"default_branch":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"repository_id":{"type":"string","computed":true},"repository_name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_codecommit_trigger":{"version":0,"block":{"attributes":{"configuration_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"repository_name":{"type":"string","required":true}},"block_types":{"trigger":{"nesting_mode":"set","block":{"attributes":{"branches":{"type":["list","string"],"optional":true},"custom_data":{"type":"string","optional":true},"destination_arn":{"type":"string","required":true},"events":{"type":["list","string"],"required":true},"name":{"type":"string","required":true}}},"min_items":1,"max_items":10}}}},"aws_codedeploy_app":{"version":0,"block":{"attributes":{"compute_platform":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"unique_id":{"type":"string","optional":true,"computed":true}}}},"aws_codedeploy_deployment_config":{"version":0,"block":{"attributes":{"compute_platform":{"type":"string","optional":true},"deployment_config_id":{"type":"string","computed":true},"deployment_config_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}},"block_types":{"minimum_healthy_hosts":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","optional":true},"value":{"type":"number","optional":true}}},"max_items":1},"traffic_routing_config":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","optional":true}},"block_types":{"time_based_canary":{"nesting_mode":"list","block":{"attributes":{"interval":{"type":"number","optional":true},"percentage":{"type":"number","optional":true}}},"max_items":1},"time_based_linear":{"nesting_mode":"list","block":{"attributes":{"interval":{"type":"number","optional":true},"percentage":{"type":"number","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_codedeploy_deployment_group":{"version":0,"block":{"attributes":{"app_name":{"type":"string","required":true},"autoscaling_groups":{"type":["set","string"],"optional":true},"deployment_config_name":{"type":"string","optional":true},"deployment_group_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"service_role_arn":{"type":"string","required":true}},"block_types":{"alarm_configuration":{"nesting_mode":"list","block":{"attributes":{"alarms":{"type":["set","string"],"optional":true},"enabled":{"type":"bool","optional":true},"ignore_poll_alarm_failure":{"type":"bool","optional":true}}},"max_items":1},"auto_rollback_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"events":{"type":["set","string"],"optional":true}}},"max_items":1},"blue_green_deployment_config":{"nesting_mode":"list","block":{"block_types":{"deployment_ready_option":{"nesting_mode":"list","block":{"attributes":{"action_on_timeout":{"type":"string","optional":true},"wait_time_in_minutes":{"type":"number","optional":true}}},"max_items":1},"green_fleet_provisioning_option":{"nesting_mode":"list","block":{"attributes":{"action":{"type":"string","optional":true}}},"max_items":1},"terminate_blue_instances_on_deployment_success":{"nesting_mode":"list","block":{"attributes":{"action":{"type":"string","optional":true},"termination_wait_time_in_minutes":{"type":"number","optional":true}}},"max_items":1}}},"max_items":1},"deployment_style":{"nesting_mode":"list","block":{"attributes":{"deployment_option":{"type":"string","optional":true},"deployment_type":{"type":"string","optional":true}}},"max_items":1},"ec2_tag_filter":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"type":{"type":"string","optional":true},"value":{"type":"string","optional":true}}}},"ec2_tag_set":{"nesting_mode":"set","block":{"block_types":{"ec2_tag_filter":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"type":{"type":"string","optional":true},"value":{"type":"string","optional":true}}}}}}},"ecs_service":{"nesting_mode":"list","block":{"attributes":{"cluster_name":{"type":"string","required":true},"service_name":{"type":"string","required":true}}},"max_items":1},"load_balancer_info":{"nesting_mode":"list","block":{"block_types":{"elb_info":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","optional":true}}}},"target_group_info":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","optional":true}}}},"target_group_pair_info":{"nesting_mode":"list","block":{"block_types":{"prod_traffic_route":{"nesting_mode":"list","block":{"attributes":{"listener_arns":{"type":["set","string"],"required":true}}},"min_items":1,"max_items":1},"target_group":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"min_items":1,"max_items":2},"test_traffic_route":{"nesting_mode":"list","block":{"attributes":{"listener_arns":{"type":["set","string"],"required":true}}},"max_items":1}}},"max_items":1}}},"max_items":1},"on_premises_instance_tag_filter":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"type":{"type":"string","optional":true},"value":{"type":"string","optional":true}}}},"trigger_configuration":{"nesting_mode":"set","block":{"attributes":{"trigger_events":{"type":["set","string"],"required":true},"trigger_name":{"type":"string","required":true},"trigger_target_arn":{"type":"string","required":true}}}}}}},"aws_codepipeline":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"artifact_store":{"nesting_mode":"set","block":{"attributes":{"location":{"type":"string","required":true},"region":{"type":"string","optional":true,"computed":true},"type":{"type":"string","required":true}},"block_types":{"encryption_key":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","required":true},"type":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"stage":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"category":{"type":"string","required":true},"configuration":{"type":["map","string"],"optional":true},"input_artifacts":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"namespace":{"type":"string","optional":true},"output_artifacts":{"type":["list","string"],"optional":true},"owner":{"type":"string","required":true},"provider":{"type":"string","required":true},"region":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","optional":true},"run_order":{"type":"number","optional":true,"computed":true},"version":{"type":"string","required":true}}},"min_items":1}}},"min_items":2}}}},"aws_codepipeline_webhook":{"version":0,"block":{"attributes":{"authentication":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"target_action":{"type":"string","required":true},"target_pipeline":{"type":"string","required":true},"url":{"type":"string","computed":true}},"block_types":{"authentication_configuration":{"nesting_mode":"list","block":{"attributes":{"allowed_ip_range":{"type":"string","optional":true},"secret_token":{"type":"string","optional":true,"sensitive":true}}},"max_items":1},"filter":{"nesting_mode":"set","block":{"attributes":{"json_path":{"type":"string","required":true},"match_equals":{"type":"string","required":true}}},"min_items":1}}}},"aws_codestarnotifications_notification_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"detail_type":{"type":"string","required":true},"event_type_ids":{"type":["set","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"resource":{"type":"string","required":true},"status":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"target":{"nesting_mode":"set","block":{"attributes":{"address":{"type":"string","required":true},"status":{"type":"string","computed":true},"type":{"type":"string","optional":true}}},"max_items":10}}}},"aws_cognito_identity_pool":{"version":0,"block":{"attributes":{"allow_unauthenticated_identities":{"type":"bool","optional":true},"arn":{"type":"string","computed":true},"developer_provider_name":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"identity_pool_name":{"type":"string","required":true},"openid_connect_provider_arns":{"type":["list","string"],"optional":true},"saml_provider_arns":{"type":["list","string"],"optional":true},"supported_login_providers":{"type":["map","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"cognito_identity_providers":{"nesting_mode":"set","block":{"attributes":{"client_id":{"type":"string","optional":true},"provider_name":{"type":"string","optional":true},"server_side_token_check":{"type":"bool","optional":true}}}}}}},"aws_cognito_identity_pool_roles_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"identity_pool_id":{"type":"string","required":true},"roles":{"type":["map","string"],"required":true}},"block_types":{"role_mapping":{"nesting_mode":"set","block":{"attributes":{"ambiguous_role_resolution":{"type":"string","optional":true},"identity_provider":{"type":"string","required":true},"type":{"type":"string","required":true}},"block_types":{"mapping_rule":{"nesting_mode":"list","block":{"attributes":{"claim":{"type":"string","required":true},"match_type":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"value":{"type":"string","required":true}}},"max_items":25}}}}}}},"aws_cognito_identity_provider":{"version":0,"block":{"attributes":{"attribute_mapping":{"type":["map","string"],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"idp_identifiers":{"type":["list","string"],"optional":true},"provider_details":{"type":["map","string"],"required":true},"provider_name":{"type":"string","required":true},"provider_type":{"type":"string","required":true},"user_pool_id":{"type":"string","required":true}}}},"aws_cognito_resource_server":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","required":true},"name":{"type":"string","required":true},"scope_identifiers":{"type":["list","string"],"computed":true},"user_pool_id":{"type":"string","required":true}},"block_types":{"scope":{"nesting_mode":"set","block":{"attributes":{"scope_description":{"type":"string","required":true},"scope_name":{"type":"string","required":true}}},"max_items":100}}}},"aws_cognito_user_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"precedence":{"type":"number","optional":true},"role_arn":{"type":"string","optional":true},"user_pool_id":{"type":"string","required":true}}}},"aws_cognito_user_pool":{"version":0,"block":{"attributes":{"alias_attributes":{"type":["set","string"],"optional":true},"arn":{"type":"string","computed":true},"auto_verified_attributes":{"type":["set","string"],"optional":true},"creation_date":{"type":"string","computed":true},"email_verification_message":{"type":"string","optional":true,"computed":true},"email_verification_subject":{"type":"string","optional":true,"computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"last_modified_date":{"type":"string","computed":true},"mfa_configuration":{"type":"string","optional":true},"name":{"type":"string","required":true},"sms_authentication_message":{"type":"string","optional":true},"sms_verification_message":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"username_attributes":{"type":["list","string"],"optional":true}},"block_types":{"admin_create_user_config":{"nesting_mode":"list","block":{"attributes":{"allow_admin_create_user_only":{"type":"bool","optional":true}},"block_types":{"invite_message_template":{"nesting_mode":"list","block":{"attributes":{"email_message":{"type":"string","optional":true},"email_subject":{"type":"string","optional":true},"sms_message":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"device_configuration":{"nesting_mode":"list","block":{"attributes":{"challenge_required_on_new_device":{"type":"bool","optional":true},"device_only_remembered_on_user_prompt":{"type":"bool","optional":true}}},"max_items":1},"email_configuration":{"nesting_mode":"list","block":{"attributes":{"email_sending_account":{"type":"string","optional":true},"from_email_address":{"type":"string","optional":true},"reply_to_email_address":{"type":"string","optional":true},"source_arn":{"type":"string","optional":true}}},"max_items":1},"lambda_config":{"nesting_mode":"list","block":{"attributes":{"create_auth_challenge":{"type":"string","optional":true},"custom_message":{"type":"string","optional":true},"define_auth_challenge":{"type":"string","optional":true},"post_authentication":{"type":"string","optional":true},"post_confirmation":{"type":"string","optional":true},"pre_authentication":{"type":"string","optional":true},"pre_sign_up":{"type":"string","optional":true},"pre_token_generation":{"type":"string","optional":true},"user_migration":{"type":"string","optional":true},"verify_auth_challenge_response":{"type":"string","optional":true}}},"max_items":1},"password_policy":{"nesting_mode":"list","block":{"attributes":{"minimum_length":{"type":"number","optional":true},"require_lowercase":{"type":"bool","optional":true},"require_numbers":{"type":"bool","optional":true},"require_symbols":{"type":"bool","optional":true},"require_uppercase":{"type":"bool","optional":true},"temporary_password_validity_days":{"type":"number","optional":true}}},"max_items":1},"schema":{"nesting_mode":"set","block":{"attributes":{"attribute_data_type":{"type":"string","required":true},"developer_only_attribute":{"type":"bool","optional":true},"mutable":{"type":"bool","optional":true},"name":{"type":"string","required":true},"required":{"type":"bool","optional":true}},"block_types":{"number_attribute_constraints":{"nesting_mode":"list","block":{"attributes":{"max_value":{"type":"string","optional":true},"min_value":{"type":"string","optional":true}}},"max_items":1},"string_attribute_constraints":{"nesting_mode":"list","block":{"attributes":{"max_length":{"type":"string","optional":true},"min_length":{"type":"string","optional":true}}},"max_items":1}}},"max_items":50},"sms_configuration":{"nesting_mode":"list","block":{"attributes":{"external_id":{"type":"string","required":true},"sns_caller_arn":{"type":"string","required":true}}},"max_items":1},"software_token_mfa_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true}}},"max_items":1},"user_pool_add_ons":{"nesting_mode":"list","block":{"attributes":{"advanced_security_mode":{"type":"string","required":true}}},"max_items":1},"username_configuration":{"nesting_mode":"list","block":{"attributes":{"case_sensitive":{"type":"bool","required":true}}},"max_items":1},"verification_message_template":{"nesting_mode":"list","block":{"attributes":{"default_email_option":{"type":"string","optional":true},"email_message":{"type":"string","optional":true,"computed":true},"email_message_by_link":{"type":"string","optional":true,"computed":true},"email_subject":{"type":"string","optional":true,"computed":true},"email_subject_by_link":{"type":"string","optional":true,"computed":true},"sms_message":{"type":"string","optional":true,"computed":true}}},"max_items":1}}}},"aws_cognito_user_pool_client":{"version":0,"block":{"attributes":{"allowed_oauth_flows":{"type":["set","string"],"optional":true},"allowed_oauth_flows_user_pool_client":{"type":"bool","optional":true},"allowed_oauth_scopes":{"type":["set","string"],"optional":true},"callback_urls":{"type":["set","string"],"optional":true},"client_secret":{"type":"string","computed":true,"sensitive":true},"default_redirect_uri":{"type":"string","optional":true},"explicit_auth_flows":{"type":["set","string"],"optional":true},"generate_secret":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"logout_urls":{"type":["set","string"],"optional":true},"name":{"type":"string","required":true},"prevent_user_existence_errors":{"type":"string","optional":true,"computed":true},"read_attributes":{"type":["set","string"],"optional":true},"refresh_token_validity":{"type":"number","optional":true},"supported_identity_providers":{"type":["set","string"],"optional":true},"user_pool_id":{"type":"string","required":true},"write_attributes":{"type":["set","string"],"optional":true}},"block_types":{"analytics_configuration":{"nesting_mode":"list","block":{"attributes":{"application_id":{"type":"string","required":true},"external_id":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"user_data_shared":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_cognito_user_pool_domain":{"version":0,"block":{"attributes":{"aws_account_id":{"type":"string","computed":true},"certificate_arn":{"type":"string","optional":true},"cloudfront_distribution_arn":{"type":"string","computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"s3_bucket":{"type":"string","computed":true},"user_pool_id":{"type":"string","required":true},"version":{"type":"string","computed":true}}}},"aws_config_aggregate_authorization":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_config_config_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"input_parameters":{"type":"string","optional":true},"maximum_execution_frequency":{"type":"string","optional":true},"name":{"type":"string","required":true},"rule_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"scope":{"nesting_mode":"list","block":{"attributes":{"compliance_resource_id":{"type":"string","optional":true},"compliance_resource_types":{"type":["set","string"],"optional":true},"tag_key":{"type":"string","optional":true},"tag_value":{"type":"string","optional":true}}},"max_items":1},"source":{"nesting_mode":"list","block":{"attributes":{"owner":{"type":"string","required":true},"source_identifier":{"type":"string","required":true}},"block_types":{"source_detail":{"nesting_mode":"set","block":{"attributes":{"event_source":{"type":"string","optional":true},"maximum_execution_frequency":{"type":"string","optional":true},"message_type":{"type":"string","optional":true}}},"max_items":25}}},"min_items":1,"max_items":1}}}},"aws_config_configuration_aggregator":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"account_aggregation_source":{"nesting_mode":"list","block":{"attributes":{"account_ids":{"type":["list","string"],"required":true},"all_regions":{"type":"bool","optional":true},"regions":{"type":["list","string"],"optional":true}}},"max_items":1},"organization_aggregation_source":{"nesting_mode":"list","block":{"attributes":{"all_regions":{"type":"bool","optional":true},"regions":{"type":["list","string"],"optional":true},"role_arn":{"type":"string","required":true}}},"max_items":1}}}},"aws_config_configuration_recorder":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}},"block_types":{"recording_group":{"nesting_mode":"list","block":{"attributes":{"all_supported":{"type":"bool","optional":true},"include_global_resource_types":{"type":"bool","optional":true},"resource_types":{"type":["set","string"],"optional":true}}},"max_items":1}}}},"aws_config_configuration_recorder_status":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"is_enabled":{"type":"bool","required":true},"name":{"type":"string","required":true}}}},"aws_config_delivery_channel":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"s3_bucket_name":{"type":"string","required":true},"s3_key_prefix":{"type":"string","optional":true},"sns_topic_arn":{"type":"string","optional":true}},"block_types":{"snapshot_delivery_properties":{"nesting_mode":"list","block":{"attributes":{"delivery_frequency":{"type":"string","optional":true}}},"max_items":1}}}},"aws_config_organization_custom_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"excluded_accounts":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"input_parameters":{"type":"string","optional":true},"lambda_function_arn":{"type":"string","required":true},"maximum_execution_frequency":{"type":"string","optional":true},"name":{"type":"string","required":true},"resource_id_scope":{"type":"string","optional":true},"resource_types_scope":{"type":["set","string"],"optional":true},"tag_key_scope":{"type":"string","optional":true},"tag_value_scope":{"type":"string","optional":true},"trigger_types":{"type":["set","string"],"required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_config_organization_managed_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"excluded_accounts":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"input_parameters":{"type":"string","optional":true},"maximum_execution_frequency":{"type":"string","optional":true},"name":{"type":"string","required":true},"resource_id_scope":{"type":"string","optional":true},"resource_types_scope":{"type":["set","string"],"optional":true},"rule_identifier":{"type":"string","required":true},"tag_key_scope":{"type":"string","optional":true},"tag_value_scope":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_cur_report_definition":{"version":0,"block":{"attributes":{"additional_artifacts":{"type":["set","string"],"optional":true},"additional_schema_elements":{"type":["set","string"],"required":true},"compression":{"type":"string","required":true},"format":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"report_name":{"type":"string","required":true},"s3_bucket":{"type":"string","required":true},"s3_prefix":{"type":"string","optional":true},"s3_region":{"type":"string","required":true},"time_unit":{"type":"string","required":true}}}},"aws_customer_gateway":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bgp_asn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","required":true}}}},"aws_datapipeline_pipeline":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_datasync_agent":{"version":0,"block":{"attributes":{"activation_key":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_datasync_location_efs":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"efs_file_system_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"subdirectory":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"uri":{"type":"string","computed":true}},"block_types":{"ec2_config":{"nesting_mode":"list","block":{"attributes":{"security_group_arns":{"type":["set","string"],"required":true},"subnet_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_datasync_location_nfs":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"server_hostname":{"type":"string","required":true},"subdirectory":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"uri":{"type":"string","computed":true}},"block_types":{"on_prem_config":{"nesting_mode":"list","block":{"attributes":{"agent_arns":{"type":["set","string"],"required":true}}},"min_items":1,"max_items":1}}}},"aws_datasync_location_s3":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"s3_bucket_arn":{"type":"string","required":true},"subdirectory":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"uri":{"type":"string","computed":true}},"block_types":{"s3_config":{"nesting_mode":"list","block":{"attributes":{"bucket_access_role_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_datasync_location_smb":{"version":0,"block":{"attributes":{"agent_arns":{"type":["set","string"],"required":true},"arn":{"type":"string","computed":true},"domain":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"password":{"type":"string","required":true,"sensitive":true},"server_hostname":{"type":"string","required":true},"subdirectory":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"uri":{"type":"string","computed":true},"user":{"type":"string","required":true}},"block_types":{"mount_options":{"nesting_mode":"list","block":{"attributes":{"version":{"type":"string","optional":true}}},"max_items":1}}}},"aws_datasync_task":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cloudwatch_log_group_arn":{"type":"string","optional":true},"destination_location_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"source_location_arn":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"options":{"nesting_mode":"list","block":{"attributes":{"atime":{"type":"string","optional":true},"bytes_per_second":{"type":"number","optional":true},"gid":{"type":"string","optional":true},"mtime":{"type":"string","optional":true},"posix_permissions":{"type":"string","optional":true},"preserve_deleted_files":{"type":"string","optional":true},"preserve_devices":{"type":"string","optional":true},"uid":{"type":"string","optional":true},"verify_mode":{"type":"string","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_dax_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true},"cluster_address":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"configuration_endpoint":{"type":"string","computed":true},"description":{"type":"string","optional":true},"iam_role_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"maintenance_window":{"type":"string","optional":true,"computed":true},"node_type":{"type":"string","required":true},"nodes":{"type":["list",["object",{"address":"string","availability_zone":"string","id":"string","port":"number"}]],"computed":true},"notification_topic_arn":{"type":"string","optional":true},"parameter_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","computed":true},"replication_factor":{"type":"number","required":true},"security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"subnet_group_name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"server_side_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dax_parameter_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_dax_subnet_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","computed":true}}}},"aws_db_cluster_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zones":{"type":["list","string"],"computed":true},"db_cluster_identifier":{"type":"string","required":true},"db_cluster_snapshot_arn":{"type":"string","computed":true},"db_cluster_snapshot_identifier":{"type":"string","required":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_type":{"type":"string","computed":true},"source_db_cluster_snapshot_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_db_event_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_aws_id":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"event_categories":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"sns_topic":{"type":"string","required":true},"source_ids":{"type":["set","string"],"optional":true},"source_type":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_db_instance":{"version":1,"block":{"attributes":{"address":{"type":"string","computed":true},"allocated_storage":{"type":"number","optional":true,"computed":true},"allow_major_version_upgrade":{"type":"bool","optional":true},"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"backup_retention_period":{"type":"number","optional":true,"computed":true},"backup_window":{"type":"string","optional":true,"computed":true},"ca_cert_identifier":{"type":"string","optional":true,"computed":true},"character_set_name":{"type":"string","optional":true,"computed":true},"copy_tags_to_snapshot":{"type":"bool","optional":true},"db_subnet_group_name":{"type":"string","optional":true,"computed":true},"delete_automated_backups":{"type":"bool","optional":true},"deletion_protection":{"type":"bool","optional":true},"domain":{"type":"string","optional":true},"domain_iam_role_name":{"type":"string","optional":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"optional":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true,"computed":true},"engine_version":{"type":"string","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"hosted_zone_id":{"type":"string","computed":true},"iam_database_authentication_enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"instance_class":{"type":"string","required":true},"iops":{"type":"number","optional":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"license_model":{"type":"string","optional":true,"computed":true},"maintenance_window":{"type":"string","optional":true,"computed":true},"max_allocated_storage":{"type":"number","optional":true},"monitoring_interval":{"type":"number","optional":true},"monitoring_role_arn":{"type":"string","optional":true,"computed":true},"multi_az":{"type":"bool","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"option_group_name":{"type":"string","optional":true,"computed":true},"parameter_group_name":{"type":"string","optional":true,"computed":true},"password":{"type":"string","optional":true,"sensitive":true},"performance_insights_enabled":{"type":"bool","optional":true},"performance_insights_kms_key_id":{"type":"string","optional":true,"computed":true},"performance_insights_retention_period":{"type":"number","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"publicly_accessible":{"type":"bool","optional":true},"replicas":{"type":["list","string"],"computed":true},"replicate_source_db":{"type":"string","optional":true},"resource_id":{"type":"string","computed":true},"security_group_names":{"type":["set","string"],"optional":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_identifier":{"type":"string","optional":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","optional":true},"storage_type":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"timezone":{"type":"string","optional":true,"computed":true},"username":{"type":"string","optional":true,"computed":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"s3_import":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","required":true},"bucket_prefix":{"type":"string","optional":true},"ingestion_role":{"type":"string","required":true},"source_engine":{"type":"string","required":true},"source_engine_version":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_db_instance_role_association":{"version":0,"block":{"attributes":{"db_instance_identifier":{"type":"string","required":true},"feature_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","required":true}}}},"aws_db_option_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"engine_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"major_engine_version":{"type":"string","required":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"option_group_description":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"option":{"nesting_mode":"set","block":{"attributes":{"db_security_group_memberships":{"type":["set","string"],"optional":true},"option_name":{"type":"string","required":true},"port":{"type":"number","optional":true},"version":{"type":"string","optional":true},"vpc_security_group_memberships":{"type":["set","string"],"optional":true}},"block_types":{"option_settings":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true}}}}}}},"aws_db_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_db_security_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"ingress":{"nesting_mode":"set","block":{"attributes":{"cidr":{"type":"string","optional":true},"security_group_id":{"type":"string","optional":true,"computed":true},"security_group_name":{"type":"string","optional":true,"computed":true},"security_group_owner_id":{"type":"string","optional":true,"computed":true}}},"min_items":1}}}},"aws_db_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zone":{"type":"string","computed":true},"db_instance_identifier":{"type":"string","required":true},"db_snapshot_arn":{"type":"string","computed":true},"db_snapshot_identifier":{"type":"string","required":true},"encrypted":{"type":"bool","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"iops":{"type":"number","computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"option_group_name":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_type":{"type":"string","computed":true},"source_db_snapshot_identifier":{"type":"string","computed":true},"source_region":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_type":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"read":{"type":"string","optional":true}}}}}}},"aws_db_subnet_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_default_network_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"default_network_acl_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"egress":{"nesting_mode":"set","block":{"attributes":{"action":{"type":"string","required":true},"cidr_block":{"type":"string","optional":true},"from_port":{"type":"number","required":true},"icmp_code":{"type":"number","optional":true},"icmp_type":{"type":"number","optional":true},"ipv6_cidr_block":{"type":"string","optional":true},"protocol":{"type":"string","required":true},"rule_no":{"type":"number","required":true},"to_port":{"type":"number","required":true}}}},"ingress":{"nesting_mode":"set","block":{"attributes":{"action":{"type":"string","required":true},"cidr_block":{"type":"string","optional":true},"from_port":{"type":"number","required":true},"icmp_code":{"type":"number","optional":true},"icmp_type":{"type":"number","optional":true},"ipv6_cidr_block":{"type":"string","optional":true},"protocol":{"type":"string","required":true},"rule_no":{"type":"number","required":true},"to_port":{"type":"number","required":true}}}}}}},"aws_default_route_table":{"version":0,"block":{"attributes":{"default_route_table_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"propagating_vgws":{"type":["set","string"],"optional":true},"route":{"type":["set",["object",{"cidr_block":"string","egress_only_gateway_id":"string","gateway_id":"string","instance_id":"string","ipv6_cidr_block":"string","nat_gateway_id":"string","network_interface_id":"string","transit_gateway_id":"string","vpc_peering_connection_id":"string"}]],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}}}},"aws_default_security_group":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"egress":{"type":["set",["object",{"cidr_blocks":["list","string"],"description":"string","from_port":"number","ipv6_cidr_blocks":["list","string"],"prefix_list_ids":["list","string"],"protocol":"string","security_groups":["set","string"],"self":"bool","to_port":"number"}]],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ingress":{"type":["set",["object",{"cidr_blocks":["list","string"],"description":"string","from_port":"number","ipv6_cidr_blocks":["list","string"],"prefix_list_ids":["list","string"],"protocol":"string","security_groups":["set","string"],"self":"bool","to_port":"number"}]],"optional":true,"computed":true},"name":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"revoke_rules_on_delete":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","optional":true,"computed":true}}}},"aws_default_subnet":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_ipv6_address_on_creation":{"type":"bool","computed":true},"availability_zone":{"type":"string","required":true},"availability_zone_id":{"type":"string","computed":true},"cidr_block":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block":{"type":"string","computed":true},"ipv6_cidr_block_association_id":{"type":"string","computed":true},"map_public_ip_on_launch":{"type":"bool","optional":true,"computed":true},"outpost_arn":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_default_vpc":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_generated_ipv6_cidr_block":{"type":"bool","computed":true},"cidr_block":{"type":"string","computed":true},"default_network_acl_id":{"type":"string","computed":true},"default_route_table_id":{"type":"string","computed":true},"default_security_group_id":{"type":"string","computed":true},"dhcp_options_id":{"type":"string","computed":true},"enable_classiclink":{"type":"bool","optional":true,"computed":true},"enable_classiclink_dns_support":{"type":"bool","optional":true,"computed":true},"enable_dns_hostnames":{"type":"bool","optional":true,"computed":true},"enable_dns_support":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_tenancy":{"type":"string","computed":true},"ipv6_association_id":{"type":"string","computed":true},"ipv6_cidr_block":{"type":"string","computed":true},"main_route_table_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_default_vpc_dhcp_options":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","computed":true},"domain_name_servers":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"netbios_name_servers":{"type":["list","string"],"optional":true},"netbios_node_type":{"type":"string","optional":true},"ntp_servers":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_devicefarm_project":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_directory_service_conditional_forwarder":{"version":0,"block":{"attributes":{"directory_id":{"type":"string","required":true},"dns_ips":{"type":["list","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"remote_domain_name":{"type":"string","required":true}}}},"aws_directory_service_directory":{"version":0,"block":{"attributes":{"access_url":{"type":"string","computed":true},"alias":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"dns_ip_addresses":{"type":["set","string"],"computed":true},"edition":{"type":"string","optional":true,"computed":true},"enable_sso":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"password":{"type":"string","required":true,"sensitive":true},"security_group_id":{"type":"string","computed":true},"short_name":{"type":"string","optional":true,"computed":true},"size":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true}},"block_types":{"connect_settings":{"nesting_mode":"list","block":{"attributes":{"availability_zones":{"type":["set","string"],"computed":true},"connect_ips":{"type":["set","string"],"computed":true},"customer_dns_ips":{"type":["set","string"],"required":true},"customer_username":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","required":true}}},"max_items":1},"vpc_settings":{"nesting_mode":"list","block":{"attributes":{"availability_zones":{"type":["set","string"],"computed":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_directory_service_log_subscription":{"version":0,"block":{"attributes":{"directory_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","required":true}}}},"aws_dlm_lifecycle_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","required":true},"execution_role_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"state":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"policy_details":{"nesting_mode":"list","block":{"attributes":{"resource_types":{"type":["list","string"],"required":true},"target_tags":{"type":["map","string"],"required":true}},"block_types":{"schedule":{"nesting_mode":"list","block":{"attributes":{"copy_tags":{"type":"bool","optional":true,"computed":true},"name":{"type":"string","required":true},"tags_to_add":{"type":["map","string"],"optional":true}},"block_types":{"create_rule":{"nesting_mode":"list","block":{"attributes":{"interval":{"type":"number","required":true},"interval_unit":{"type":"string","optional":true},"times":{"type":["list","string"],"optional":true,"computed":true}}},"min_items":1,"max_items":1},"retain_rule":{"nesting_mode":"list","block":{"attributes":{"count":{"type":"number","required":true}}},"min_items":1,"max_items":1}}},"min_items":1}}},"min_items":1,"max_items":1}}}},"aws_dms_certificate":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","computed":true},"certificate_id":{"type":"string","required":true},"certificate_pem":{"type":"string","optional":true,"sensitive":true},"certificate_wallet":{"type":"string","optional":true,"sensitive":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_dms_endpoint":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","optional":true,"computed":true},"database_name":{"type":"string","optional":true},"endpoint_arn":{"type":"string","computed":true},"endpoint_id":{"type":"string","required":true},"endpoint_type":{"type":"string","required":true},"engine_name":{"type":"string","required":true},"extra_connection_attributes":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true,"computed":true},"password":{"type":"string","optional":true,"sensitive":true},"port":{"type":"number","optional":true},"server_name":{"type":"string","optional":true},"service_access_role":{"type":"string","optional":true},"ssl_mode":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"username":{"type":"string","optional":true}},"block_types":{"elasticsearch_settings":{"nesting_mode":"list","block":{"attributes":{"endpoint_uri":{"type":"string","required":true},"error_retry_duration":{"type":"number","optional":true},"full_load_error_percentage":{"type":"number","optional":true},"service_access_role_arn":{"type":"string","required":true}}},"max_items":1},"kafka_settings":{"nesting_mode":"list","block":{"attributes":{"broker":{"type":"string","required":true},"topic":{"type":"string","optional":true}}},"max_items":1},"kinesis_settings":{"nesting_mode":"list","block":{"attributes":{"message_format":{"type":"string","optional":true},"service_access_role_arn":{"type":"string","optional":true},"stream_arn":{"type":"string","optional":true}}},"max_items":1},"mongodb_settings":{"nesting_mode":"list","block":{"attributes":{"auth_mechanism":{"type":"string","optional":true},"auth_source":{"type":"string","optional":true},"auth_type":{"type":"string","optional":true},"docs_to_investigate":{"type":"string","optional":true},"extract_doc_id":{"type":"string","optional":true},"nesting_level":{"type":"string","optional":true}}},"max_items":1},"s3_settings":{"nesting_mode":"list","block":{"attributes":{"bucket_folder":{"type":"string","optional":true},"bucket_name":{"type":"string","optional":true},"compression_type":{"type":"string","optional":true},"csv_delimiter":{"type":"string","optional":true},"csv_row_delimiter":{"type":"string","optional":true},"external_table_definition":{"type":"string","optional":true},"service_access_role_arn":{"type":"string","optional":true}}},"max_items":1}}}},"aws_dms_event_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"event_categories":{"type":["set","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"sns_topic_arn":{"type":"string","required":true},"source_ids":{"type":["set","string"],"optional":true},"source_type":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dms_replication_instance":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","optional":true,"computed":true},"apply_immediately":{"type":"bool","optional":true},"auto_minor_version_upgrade":{"type":"bool","optional":true,"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true,"computed":true},"multi_az":{"type":"bool","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"publicly_accessible":{"type":"bool","optional":true,"computed":true},"replication_instance_arn":{"type":"string","computed":true},"replication_instance_class":{"type":"string","required":true},"replication_instance_id":{"type":"string","required":true},"replication_instance_private_ips":{"type":["list","string"],"computed":true},"replication_instance_public_ips":{"type":["list","string"],"computed":true},"replication_subnet_group_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dms_replication_subnet_group":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"replication_subnet_group_arn":{"type":"string","computed":true},"replication_subnet_group_description":{"type":"string","required":true},"replication_subnet_group_id":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true}}}},"aws_dms_replication_task":{"version":0,"block":{"attributes":{"cdc_start_time":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"migration_type":{"type":"string","required":true},"replication_instance_arn":{"type":"string","required":true},"replication_task_arn":{"type":"string","computed":true},"replication_task_id":{"type":"string","required":true},"replication_task_settings":{"type":"string","optional":true},"source_endpoint_arn":{"type":"string","required":true},"table_mappings":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"target_endpoint_arn":{"type":"string","required":true}}}},"aws_docdb_cluster":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"backup_retention_period":{"type":"number","optional":true},"cluster_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier_prefix":{"type":"string","optional":true,"computed":true},"cluster_members":{"type":["set","string"],"optional":true,"computed":true},"cluster_resource_id":{"type":"string","computed":true},"db_cluster_parameter_group_name":{"type":"string","optional":true,"computed":true},"db_subnet_group_name":{"type":"string","optional":true,"computed":true},"deletion_protection":{"type":"bool","optional":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"optional":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"master_password":{"type":"string","optional":true,"sensitive":true},"master_username":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"reader_endpoint":{"type":"string","computed":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_identifier":{"type":"string","optional":true},"storage_encrypted":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_docdb_cluster_instance":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"ca_cert_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier":{"type":"string","required":true},"db_subnet_group_name":{"type":"string","computed":true},"dbi_resource_id":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"instance_class":{"type":"string","required":true},"kms_key_id":{"type":"string","computed":true},"port":{"type":"number","computed":true},"preferred_backup_window":{"type":"string","computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"promotion_tier":{"type":"number","optional":true},"publicly_accessible":{"type":"bool","computed":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true},"writer":{"type":"bool","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_docdb_cluster_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_docdb_cluster_snapshot":{"version":0,"block":{"attributes":{"availability_zones":{"type":["list","string"],"computed":true},"db_cluster_identifier":{"type":"string","required":true},"db_cluster_snapshot_arn":{"type":"string","computed":true},"db_cluster_snapshot_identifier":{"type":"string","required":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_type":{"type":"string","computed":true},"source_db_cluster_snapshot_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_docdb_subnet_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_dx_bgp_peer":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"bgp_peer_id":{"type":"string","computed":true},"bgp_status":{"type":"string","computed":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"virtual_interface_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_connection":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bandwidth":{"type":"string","required":true},"has_logical_redundancy":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"location":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_dx_connection_association":{"version":0,"block":{"attributes":{"connection_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"lag_id":{"type":"string","required":true}}}},"aws_dx_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_gateway_association":{"version":1,"block":{"attributes":{"allowed_prefixes":{"type":["set","string"],"optional":true,"computed":true},"associated_gateway_id":{"type":"string","optional":true,"computed":true},"associated_gateway_owner_account_id":{"type":"string","optional":true,"computed":true},"associated_gateway_type":{"type":"string","computed":true},"dx_gateway_association_id":{"type":"string","computed":true},"dx_gateway_id":{"type":"string","required":true},"dx_gateway_owner_account_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"proposal_id":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dx_gateway_association_proposal":{"version":0,"block":{"attributes":{"allowed_prefixes":{"type":["set","string"],"optional":true,"computed":true},"associated_gateway_id":{"type":"string","required":true},"associated_gateway_owner_account_id":{"type":"string","computed":true},"associated_gateway_type":{"type":"string","computed":true},"dx_gateway_id":{"type":"string","required":true},"dx_gateway_owner_account_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_dx_hosted_private_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"mtu":{"type":"number","optional":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","required":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_private_virtual_interface_accepter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"dx_gateway_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"virtual_interface_id":{"type":"string","required":true},"vpn_gateway_id":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_public_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","required":true},"route_filter_prefixes":{"type":["set","string"],"required":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_public_virtual_interface_accepter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"virtual_interface_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_transit_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"mtu":{"type":"number","optional":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","required":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dx_hosted_transit_virtual_interface_accepter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"dx_gateway_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"virtual_interface_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_lag":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"connections_bandwidth":{"type":"string","required":true},"force_destroy":{"type":"bool","optional":true},"has_logical_redundancy":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"location":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_dx_private_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"dx_gateway_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"mtu":{"type":"number","optional":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vlan":{"type":"number","required":true},"vpn_gateway_id":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dx_public_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"route_filter_prefixes":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_dx_transit_virtual_interface":{"version":0,"block":{"attributes":{"address_family":{"type":"string","required":true},"amazon_address":{"type":"string","optional":true,"computed":true},"amazon_side_asn":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"aws_device":{"type":"string","computed":true},"bgp_asn":{"type":"number","required":true},"bgp_auth_key":{"type":"string","optional":true,"computed":true},"connection_id":{"type":"string","required":true},"customer_address":{"type":"string","optional":true,"computed":true},"dx_gateway_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"jumbo_frame_capable":{"type":"bool","computed":true},"mtu":{"type":"number","optional":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vlan":{"type":"number","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dynamodb_global_table":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"replica":{"nesting_mode":"set","block":{"attributes":{"region_name":{"type":"string","required":true}}},"min_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_dynamodb_table":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"billing_mode":{"type":"string","optional":true},"hash_key":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"range_key":{"type":"string","optional":true},"read_capacity":{"type":"number","optional":true},"stream_arn":{"type":"string","computed":true},"stream_enabled":{"type":"bool","optional":true},"stream_label":{"type":"string","computed":true},"stream_view_type":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"write_capacity":{"type":"number","optional":true}},"block_types":{"attribute":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"type":{"type":"string","required":true}}},"min_items":1},"global_secondary_index":{"nesting_mode":"set","block":{"attributes":{"hash_key":{"type":"string","required":true},"name":{"type":"string","required":true},"non_key_attributes":{"type":["list","string"],"optional":true},"projection_type":{"type":"string","required":true},"range_key":{"type":"string","optional":true},"read_capacity":{"type":"number","optional":true},"write_capacity":{"type":"number","optional":true}}}},"local_secondary_index":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"non_key_attributes":{"type":["list","string"],"optional":true},"projection_type":{"type":"string","required":true},"range_key":{"type":"string","required":true}}}},"point_in_time_recovery":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true}}},"max_items":1},"replica":{"nesting_mode":"set","block":{"attributes":{"region_name":{"type":"string","required":true}}}},"server_side_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true},"kms_key_arn":{"type":"string","optional":true,"computed":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}},"ttl":{"nesting_mode":"list","block":{"attributes":{"attribute_name":{"type":"string","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_dynamodb_table_item":{"version":0,"block":{"attributes":{"hash_key":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"item":{"type":"string","required":true},"range_key":{"type":"string","optional":true},"table_name":{"type":"string","required":true}}}},"aws_ebs_default_kms_key":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"key_arn":{"type":"string","required":true}}}},"aws_ebs_encryption_by_default":{"version":0,"block":{"attributes":{"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ebs_snapshot":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"data_encryption_key_id":{"type":"string","computed":true},"description":{"type":"string","optional":true},"encrypted":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"owner_alias":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"volume_id":{"type":"string","required":true},"volume_size":{"type":"number","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_ebs_snapshot_copy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"data_encryption_key_id":{"type":"string","computed":true},"description":{"type":"string","optional":true},"encrypted":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"owner_alias":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"source_region":{"type":"string","required":true},"source_snapshot_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","computed":true}}}},"aws_ebs_volume":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"multi_attach_enabled":{"type":"bool","optional":true},"outpost_arn":{"type":"string","optional":true},"size":{"type":"number","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true,"computed":true}}}},"aws_ec2_availability_zone_group":{"version":0,"block":{"attributes":{"group_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"opt_in_status":{"type":"string","required":true}}}},"aws_ec2_capacity_reservation":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","required":true},"ebs_optimized":{"type":"bool","optional":true},"end_date":{"type":"string","optional":true},"end_date_type":{"type":"string","optional":true},"ephemeral_storage":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_count":{"type":"number","required":true},"instance_match_criteria":{"type":"string","optional":true},"instance_platform":{"type":"string","required":true},"instance_type":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"tenancy":{"type":"string","optional":true}}}},"aws_ec2_client_vpn_authorization_rule":{"version":0,"block":{"attributes":{"access_group_id":{"type":"string","optional":true},"authorize_all_groups":{"type":"bool","optional":true},"client_vpn_endpoint_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"target_network_cidr":{"type":"string","required":true}}}},"aws_ec2_client_vpn_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"client_cidr_block":{"type":"string","required":true},"description":{"type":"string","optional":true},"dns_name":{"type":"string","computed":true},"dns_servers":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"server_certificate_arn":{"type":"string","required":true},"split_tunnel":{"type":"bool","optional":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"transport_protocol":{"type":"string","optional":true}},"block_types":{"authentication_options":{"nesting_mode":"list","block":{"attributes":{"active_directory_id":{"type":"string","optional":true},"root_certificate_chain_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":2},"connection_log_options":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_log_group":{"type":"string","optional":true},"cloudwatch_log_stream":{"type":"string","optional":true},"enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"aws_ec2_client_vpn_network_association":{"version":0,"block":{"attributes":{"client_vpn_endpoint_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"security_groups":{"type":["set","string"],"computed":true},"status":{"type":"string","computed":true},"subnet_id":{"type":"string","required":true},"vpc_id":{"type":"string","computed":true}}}},"aws_ec2_client_vpn_route":{"version":0,"block":{"attributes":{"client_vpn_endpoint_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"origin":{"type":"string","computed":true},"target_vpc_subnet_id":{"type":"string","required":true},"type":{"type":"string","computed":true}}}},"aws_ec2_fleet":{"version":0,"block":{"attributes":{"excess_capacity_termination_policy":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"replace_unhealthy_instances":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"terminate_instances":{"type":"bool","optional":true},"terminate_instances_with_expiration":{"type":"bool","optional":true},"type":{"type":"string","optional":true}},"block_types":{"launch_template_config":{"nesting_mode":"list","block":{"block_types":{"launch_template_specification":{"nesting_mode":"list","block":{"attributes":{"launch_template_id":{"type":"string","optional":true},"launch_template_name":{"type":"string","optional":true},"version":{"type":"string","required":true}}},"min_items":1,"max_items":1},"override":{"nesting_mode":"list","block":{"attributes":{"availability_zone":{"type":"string","optional":true},"instance_type":{"type":"string","optional":true},"max_price":{"type":"string","optional":true},"priority":{"type":"number","optional":true},"subnet_id":{"type":"string","optional":true},"weighted_capacity":{"type":"number","optional":true}}},"max_items":50}}},"min_items":1,"max_items":1},"on_demand_options":{"nesting_mode":"list","block":{"attributes":{"allocation_strategy":{"type":"string","optional":true}}},"max_items":1},"spot_options":{"nesting_mode":"list","block":{"attributes":{"allocation_strategy":{"type":"string","optional":true},"instance_interruption_behavior":{"type":"string","optional":true},"instance_pools_to_use_count":{"type":"number","optional":true}}},"max_items":1},"target_capacity_specification":{"nesting_mode":"list","block":{"attributes":{"default_target_capacity_type":{"type":"string","required":true},"on_demand_target_capacity":{"type":"number","optional":true},"spot_target_capacity":{"type":"number","optional":true},"total_target_capacity":{"type":"number","required":true}}},"min_items":1,"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_ec2_local_gateway_route":{"version":0,"block":{"attributes":{"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"local_gateway_route_table_id":{"type":"string","required":true},"local_gateway_virtual_interface_group_id":{"type":"string","required":true}}}},"aws_ec2_local_gateway_route_table_vpc_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_gateway_id":{"type":"string","computed":true},"local_gateway_route_table_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}}}},"aws_ec2_tag":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","required":true},"resource_id":{"type":"string","required":true},"value":{"type":"string","required":true}}}},"aws_ec2_traffic_mirror_filter":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"network_services":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_ec2_traffic_mirror_filter_rule":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"protocol":{"type":"number","optional":true},"rule_action":{"type":"string","required":true},"rule_number":{"type":"number","required":true},"source_cidr_block":{"type":"string","required":true},"traffic_direction":{"type":"string","required":true},"traffic_mirror_filter_id":{"type":"string","required":true}},"block_types":{"destination_port_range":{"nesting_mode":"list","block":{"attributes":{"from_port":{"type":"number","optional":true},"to_port":{"type":"number","optional":true}}},"max_items":1},"source_port_range":{"nesting_mode":"list","block":{"attributes":{"from_port":{"type":"number","optional":true},"to_port":{"type":"number","optional":true}}},"max_items":1}}}},"aws_ec2_traffic_mirror_session":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","required":true},"packet_length":{"type":"number","optional":true},"session_number":{"type":"number","required":true},"tags":{"type":["map","string"],"optional":true},"traffic_mirror_filter_id":{"type":"string","required":true},"traffic_mirror_target_id":{"type":"string","required":true},"virtual_network_id":{"type":"number","optional":true,"computed":true}}}},"aws_ec2_traffic_mirror_target":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","optional":true},"network_load_balancer_arn":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_ec2_transit_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"number","optional":true},"arn":{"type":"string","computed":true},"association_default_route_table_id":{"type":"string","computed":true},"auto_accept_shared_attachments":{"type":"string","optional":true},"default_route_table_association":{"type":"string","optional":true},"default_route_table_propagation":{"type":"string","optional":true},"description":{"type":"string","optional":true},"dns_support":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"propagation_default_route_table_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpn_ecmp_support":{"type":"string","optional":true}}}},"aws_ec2_transit_gateway_peering_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"peer_account_id":{"type":"string","optional":true,"computed":true},"peer_region":{"type":"string","required":true},"peer_transit_gateway_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_peering_attachment_accepter":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"peer_account_id":{"type":"string","computed":true},"peer_region":{"type":"string","computed":true},"peer_transit_gateway_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_attachment_id":{"type":"string","required":true},"transit_gateway_id":{"type":"string","computed":true}}}},"aws_ec2_transit_gateway_route":{"version":0,"block":{"attributes":{"blackhole":{"type":"bool","optional":true},"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"transit_gateway_attachment_id":{"type":"string","optional":true},"transit_gateway_route_table_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_route_table":{"version":0,"block":{"attributes":{"default_association_route_table":{"type":"bool","computed":true},"default_propagation_route_table":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_route_table_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_id":{"type":"string","computed":true},"resource_type":{"type":"string","computed":true},"transit_gateway_attachment_id":{"type":"string","required":true},"transit_gateway_route_table_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_route_table_propagation":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_id":{"type":"string","computed":true},"resource_type":{"type":"string","computed":true},"transit_gateway_attachment_id":{"type":"string","required":true},"transit_gateway_route_table_id":{"type":"string","required":true}}}},"aws_ec2_transit_gateway_vpc_attachment":{"version":0,"block":{"attributes":{"dns_support":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_support":{"type":"string","optional":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_default_route_table_association":{"type":"bool","optional":true},"transit_gateway_default_route_table_propagation":{"type":"bool","optional":true},"transit_gateway_id":{"type":"string","required":true},"vpc_id":{"type":"string","required":true},"vpc_owner_id":{"type":"string","computed":true}}}},"aws_ec2_transit_gateway_vpc_attachment_accepter":{"version":0,"block":{"attributes":{"dns_support":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_support":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_attachment_id":{"type":"string","required":true},"transit_gateway_default_route_table_association":{"type":"bool","optional":true},"transit_gateway_default_route_table_propagation":{"type":"bool","optional":true},"transit_gateway_id":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true},"vpc_owner_id":{"type":"string","computed":true}}}},"aws_ecr_lifecycle_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true},"registry_id":{"type":"string","computed":true},"repository":{"type":"string","required":true}}}},"aws_ecr_repository":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"image_tag_mutability":{"type":"string","optional":true},"name":{"type":"string","required":true},"registry_id":{"type":"string","computed":true},"repository_url":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"encryption_configuration":{"nesting_mode":"list","block":{"attributes":{"encryption_type":{"type":"string","optional":true},"kms_key":{"type":"string","optional":true,"computed":true}}}},"image_scanning_configuration":{"nesting_mode":"list","block":{"attributes":{"scan_on_push":{"type":"bool","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true}}}}}}},"aws_ecr_repository_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true},"registry_id":{"type":"string","computed":true},"repository":{"type":"string","required":true}}}},"aws_ecs_capacity_provider":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"auto_scaling_group_provider":{"nesting_mode":"list","block":{"attributes":{"auto_scaling_group_arn":{"type":"string","required":true},"managed_termination_protection":{"type":"string","optional":true,"computed":true}},"block_types":{"managed_scaling":{"nesting_mode":"list","block":{"attributes":{"maximum_scaling_step_size":{"type":"number","optional":true,"computed":true},"minimum_scaling_step_size":{"type":"number","optional":true,"computed":true},"status":{"type":"string","optional":true,"computed":true},"target_capacity":{"type":"number","optional":true,"computed":true}}},"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_ecs_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"capacity_providers":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"default_capacity_provider_strategy":{"nesting_mode":"set","block":{"attributes":{"base":{"type":"number","optional":true},"capacity_provider":{"type":"string","required":true},"weight":{"type":"number","optional":true}}}},"setting":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_ecs_service":{"version":0,"block":{"attributes":{"cluster":{"type":"string","optional":true,"computed":true},"deployment_maximum_percent":{"type":"number","optional":true},"deployment_minimum_healthy_percent":{"type":"number","optional":true},"desired_count":{"type":"number","optional":true},"enable_ecs_managed_tags":{"type":"bool","optional":true},"force_new_deployment":{"type":"bool","optional":true},"health_check_grace_period_seconds":{"type":"number","optional":true},"iam_role":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"launch_type":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"platform_version":{"type":"string","optional":true,"computed":true},"propagate_tags":{"type":"string","optional":true},"scheduling_strategy":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"task_definition":{"type":"string","optional":true}},"block_types":{"capacity_provider_strategy":{"nesting_mode":"set","block":{"attributes":{"base":{"type":"number","optional":true},"capacity_provider":{"type":"string","required":true},"weight":{"type":"number","optional":true}}}},"deployment_controller":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","optional":true}}},"max_items":1},"load_balancer":{"nesting_mode":"set","block":{"attributes":{"container_name":{"type":"string","required":true},"container_port":{"type":"number","required":true},"elb_name":{"type":"string","optional":true},"target_group_arn":{"type":"string","optional":true}}}},"network_configuration":{"nesting_mode":"list","block":{"attributes":{"assign_public_ip":{"type":"bool","optional":true},"security_groups":{"type":["set","string"],"optional":true},"subnets":{"type":["set","string"],"required":true}}},"max_items":1},"ordered_placement_strategy":{"nesting_mode":"list","block":{"attributes":{"field":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"max_items":5},"placement_constraints":{"nesting_mode":"set","block":{"attributes":{"expression":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"max_items":10},"service_registries":{"nesting_mode":"list","block":{"attributes":{"container_name":{"type":"string","optional":true},"container_port":{"type":"number","optional":true},"port":{"type":"number","optional":true},"registry_arn":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true}}}}}}},"aws_ecs_task_definition":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"container_definitions":{"type":"string","required":true},"cpu":{"type":"string","optional":true},"execution_role_arn":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ipc_mode":{"type":"string","optional":true},"memory":{"type":"string","optional":true},"network_mode":{"type":"string","optional":true,"computed":true},"pid_mode":{"type":"string","optional":true},"requires_compatibilities":{"type":["set","string"],"optional":true},"revision":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true},"task_role_arn":{"type":"string","optional":true}},"block_types":{"inference_accelerator":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"device_type":{"type":"string","required":true}}}},"placement_constraints":{"nesting_mode":"set","block":{"attributes":{"expression":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"max_items":10},"proxy_configuration":{"nesting_mode":"list","block":{"attributes":{"container_name":{"type":"string","required":true},"properties":{"type":["map","string"],"optional":true},"type":{"type":"string","optional":true}}},"max_items":1},"volume":{"nesting_mode":"set","block":{"attributes":{"host_path":{"type":"string","optional":true},"name":{"type":"string","required":true}},"block_types":{"docker_volume_configuration":{"nesting_mode":"list","block":{"attributes":{"autoprovision":{"type":"bool","optional":true},"driver":{"type":"string","optional":true},"driver_opts":{"type":["map","string"],"optional":true},"labels":{"type":["map","string"],"optional":true},"scope":{"type":"string","optional":true,"computed":true}}},"max_items":1},"efs_volume_configuration":{"nesting_mode":"list","block":{"attributes":{"file_system_id":{"type":"string","required":true},"root_directory":{"type":"string","optional":true},"transit_encryption":{"type":"string","optional":true},"transit_encryption_port":{"type":"number","optional":true}},"block_types":{"authorization_config":{"nesting_mode":"list","block":{"attributes":{"access_point_id":{"type":"string","optional":true},"iam":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}}}}},"aws_efs_access_point":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"file_system_arn":{"type":"string","computed":true},"file_system_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"posix_user":{"nesting_mode":"list","block":{"attributes":{"gid":{"type":"number","required":true},"secondary_gids":{"type":["set","number"],"optional":true},"uid":{"type":"number","required":true}}},"max_items":1},"root_directory":{"nesting_mode":"list","block":{"attributes":{"path":{"type":"string","optional":true,"computed":true}},"block_types":{"creation_info":{"nesting_mode":"list","block":{"attributes":{"owner_gid":{"type":"number","required":true},"owner_uid":{"type":"number","required":true},"permissions":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}}},"aws_efs_file_system":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_token":{"type":"string","optional":true,"computed":true},"dns_name":{"type":"string","computed":true},"encrypted":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"performance_mode":{"type":"string","optional":true,"computed":true},"provisioned_throughput_in_mibps":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true},"throughput_mode":{"type":"string","optional":true}},"block_types":{"lifecycle_policy":{"nesting_mode":"list","block":{"attributes":{"transition_to_ia":{"type":"string","required":true}}},"max_items":1}}}},"aws_efs_file_system_policy":{"version":0,"block":{"attributes":{"file_system_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true}}}},"aws_efs_mount_target":{"version":0,"block":{"attributes":{"availability_zone_id":{"type":"string","computed":true},"availability_zone_name":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"file_system_arn":{"type":"string","computed":true},"file_system_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","optional":true,"computed":true},"mount_target_dns_name":{"type":"string","computed":true},"network_interface_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"subnet_id":{"type":"string","required":true}}}},"aws_egress_only_internet_gateway":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}}}},"aws_eip":{"version":0,"block":{"attributes":{"allocation_id":{"type":"string","computed":true},"associate_with_private_ip":{"type":"string","optional":true},"association_id":{"type":"string","computed":true},"customer_owned_ip":{"type":"string","computed":true},"customer_owned_ipv4_pool":{"type":"string","optional":true},"domain":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instance":{"type":"string","optional":true,"computed":true},"network_interface":{"type":"string","optional":true,"computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"public_ipv4_pool":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc":{"type":"bool","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"delete":{"type":"string","optional":true},"read":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_eip_association":{"version":0,"block":{"attributes":{"allocation_id":{"type":"string","optional":true,"computed":true},"allow_reassociation":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","optional":true,"computed":true},"private_ip_address":{"type":"string","optional":true,"computed":true},"public_ip":{"type":"string","optional":true,"computed":true}}}},"aws_eks_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_authority":{"type":["list",["object",{"data":"string"}]],"computed":true},"created_at":{"type":"string","computed":true},"enabled_cluster_log_types":{"type":["set","string"],"optional":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identity":{"type":["list",["object",{"oidc":["list",["object",{"issuer":"string"}]]}]],"computed":true},"name":{"type":"string","required":true},"platform_version":{"type":"string","computed":true},"role_arn":{"type":"string","required":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","optional":true,"computed":true}},"block_types":{"encryption_config":{"nesting_mode":"list","block":{"attributes":{"resources":{"type":["set","string"],"required":true}},"block_types":{"provider":{"nesting_mode":"list","block":{"attributes":{"key_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}},"vpc_config":{"nesting_mode":"list","block":{"attributes":{"cluster_security_group_id":{"type":"string","computed":true},"endpoint_private_access":{"type":"bool","optional":true},"endpoint_public_access":{"type":"bool","optional":true},"public_access_cidrs":{"type":["set","string"],"optional":true,"computed":true},"security_group_ids":{"type":["set","string"],"optional":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","computed":true}}},"min_items":1,"max_items":1}}}},"aws_eks_fargate_profile":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"fargate_profile_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"pod_execution_role_arn":{"type":"string","required":true},"status":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"selector":{"nesting_mode":"set","block":{"attributes":{"labels":{"type":["map","string"],"optional":true},"namespace":{"type":"string","required":true}}},"min_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_eks_node_group":{"version":0,"block":{"attributes":{"ami_type":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"disk_size":{"type":"number","optional":true,"computed":true},"force_update_version":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_types":{"type":["list","string"],"optional":true,"computed":true},"labels":{"type":["map","string"],"optional":true},"node_group_name":{"type":"string","required":true},"node_role_arn":{"type":"string","required":true},"release_version":{"type":"string","optional":true,"computed":true},"resources":{"type":["list",["object",{"autoscaling_groups":["list",["object",{"name":"string"}]],"remote_access_security_group_id":"string"}]],"computed":true},"status":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","optional":true,"computed":true}},"block_types":{"remote_access":{"nesting_mode":"list","block":{"attributes":{"ec2_ssh_key":{"type":"string","optional":true},"source_security_group_ids":{"type":["set","string"],"optional":true}}},"max_items":1},"scaling_config":{"nesting_mode":"list","block":{"attributes":{"desired_size":{"type":"number","required":true},"max_size":{"type":"number","required":true},"min_size":{"type":"number","required":true}}},"min_items":1,"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_elastic_beanstalk_application":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"appversion_lifecycle":{"nesting_mode":"list","block":{"attributes":{"delete_source_from_s3":{"type":"bool","optional":true},"max_age_in_days":{"type":"number","optional":true},"max_count":{"type":"number","optional":true},"service_role":{"type":"string","required":true}}},"max_items":1}}}},"aws_elastic_beanstalk_application_version":{"version":0,"block":{"attributes":{"application":{"type":"string","required":true},"arn":{"type":"string","computed":true},"bucket":{"type":"string","required":true},"description":{"type":"string","optional":true},"force_delete":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_elastic_beanstalk_configuration_template":{"version":0,"block":{"attributes":{"application":{"type":"string","required":true},"description":{"type":"string","optional":true},"environment_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"solution_stack_name":{"type":"string","optional":true}},"block_types":{"setting":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"resource":{"type":"string","optional":true},"value":{"type":"string","required":true}}}}}}},"aws_elastic_beanstalk_environment":{"version":1,"block":{"attributes":{"all_settings":{"type":["set",["object",{"name":"string","namespace":"string","resource":"string","value":"string"}]],"computed":true},"application":{"type":"string","required":true},"arn":{"type":"string","computed":true},"autoscaling_groups":{"type":["list","string"],"computed":true},"cname":{"type":"string","computed":true},"cname_prefix":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"endpoint_url":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instances":{"type":["list","string"],"computed":true},"launch_configurations":{"type":["list","string"],"computed":true},"load_balancers":{"type":["list","string"],"computed":true},"name":{"type":"string","required":true},"platform_arn":{"type":"string","optional":true,"computed":true},"poll_interval":{"type":"string","optional":true},"queues":{"type":["list","string"],"computed":true},"solution_stack_name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"template_name":{"type":"string","optional":true},"tier":{"type":"string","optional":true},"triggers":{"type":["list","string"],"computed":true},"version_label":{"type":"string","optional":true,"computed":true},"wait_for_ready_timeout":{"type":"string","optional":true}},"block_types":{"setting":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"namespace":{"type":"string","required":true},"resource":{"type":"string","optional":true},"value":{"type":"string","required":true}}}}}}},"aws_elasticache_cluster":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"az_mode":{"type":"string","optional":true,"computed":true},"cache_nodes":{"type":["list",["object",{"address":"string","availability_zone":"string","id":"string","port":"number"}]],"computed":true},"cluster_address":{"type":"string","computed":true},"cluster_id":{"type":"string","required":true},"configuration_endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true,"computed":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"maintenance_window":{"type":"string","optional":true,"computed":true},"node_type":{"type":"string","optional":true,"computed":true},"notification_topic_arn":{"type":"string","optional":true},"num_cache_nodes":{"type":"number","optional":true,"computed":true},"parameter_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"preferred_availability_zones":{"type":["list","string"],"optional":true},"replication_group_id":{"type":"string","optional":true,"computed":true},"security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"security_group_names":{"type":["set","string"],"optional":true,"computed":true},"snapshot_arns":{"type":["set","string"],"optional":true},"snapshot_name":{"type":"string","optional":true},"snapshot_retention_limit":{"type":"number","optional":true},"snapshot_window":{"type":"string","optional":true,"computed":true},"subnet_group_name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_elasticache_parameter_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_elasticache_replication_group":{"version":1,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"at_rest_encryption_enabled":{"type":"bool","optional":true},"auth_token":{"type":"string","optional":true,"sensitive":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"automatic_failover_enabled":{"type":"bool","optional":true},"availability_zones":{"type":["set","string"],"optional":true},"configuration_endpoint_address":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"maintenance_window":{"type":"string","optional":true,"computed":true},"member_clusters":{"type":["set","string"],"computed":true},"node_type":{"type":"string","optional":true,"computed":true},"notification_topic_arn":{"type":"string","optional":true},"number_cache_clusters":{"type":"number","optional":true,"computed":true},"parameter_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"primary_endpoint_address":{"type":"string","computed":true},"replication_group_description":{"type":"string","required":true},"replication_group_id":{"type":"string","required":true},"security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"security_group_names":{"type":["set","string"],"optional":true,"computed":true},"snapshot_arns":{"type":["set","string"],"optional":true},"snapshot_name":{"type":"string","optional":true},"snapshot_retention_limit":{"type":"number","optional":true},"snapshot_window":{"type":"string","optional":true,"computed":true},"subnet_group_name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"transit_encryption_enabled":{"type":"bool","optional":true}},"block_types":{"cluster_mode":{"nesting_mode":"list","block":{"attributes":{"num_node_groups":{"type":"number","required":true},"replicas_per_node_group":{"type":"number","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_elasticache_security_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"security_group_names":{"type":["set","string"],"required":true}}}},"aws_elasticache_subnet_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true}}}},"aws_elasticsearch_domain":{"version":0,"block":{"attributes":{"access_policies":{"type":"string","optional":true,"computed":true},"advanced_options":{"type":["map","string"],"optional":true,"computed":true},"arn":{"type":"string","computed":true},"domain_id":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"elasticsearch_version":{"type":"string","optional":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kibana_endpoint":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"advanced_security_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true},"internal_user_database_enabled":{"type":"bool","optional":true}},"block_types":{"master_user_options":{"nesting_mode":"list","block":{"attributes":{"master_user_arn":{"type":"string","optional":true},"master_user_name":{"type":"string","optional":true},"master_user_password":{"type":"string","optional":true,"sensitive":true}}},"max_items":1}}},"max_items":1},"cluster_config":{"nesting_mode":"list","block":{"attributes":{"dedicated_master_count":{"type":"number","optional":true},"dedicated_master_enabled":{"type":"bool","optional":true},"dedicated_master_type":{"type":"string","optional":true},"instance_count":{"type":"number","optional":true},"instance_type":{"type":"string","optional":true},"warm_count":{"type":"number","optional":true},"warm_enabled":{"type":"bool","optional":true},"warm_type":{"type":"string","optional":true},"zone_awareness_enabled":{"type":"bool","optional":true}},"block_types":{"zone_awareness_config":{"nesting_mode":"list","block":{"attributes":{"availability_zone_count":{"type":"number","optional":true}}},"max_items":1}}},"max_items":1},"cognito_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"identity_pool_id":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"user_pool_id":{"type":"string","required":true}}},"max_items":1},"domain_endpoint_options":{"nesting_mode":"list","block":{"attributes":{"enforce_https":{"type":"bool","required":true},"tls_security_policy":{"type":"string","optional":true,"computed":true}}},"max_items":1},"ebs_options":{"nesting_mode":"list","block":{"attributes":{"ebs_enabled":{"type":"bool","required":true},"iops":{"type":"number","optional":true},"volume_size":{"type":"number","optional":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1},"encrypt_at_rest":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true},"kms_key_id":{"type":"string","optional":true,"computed":true}}},"max_items":1},"log_publishing_options":{"nesting_mode":"set","block":{"attributes":{"cloudwatch_log_group_arn":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"log_type":{"type":"string","required":true}}}},"node_to_node_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true}}},"max_items":1},"snapshot_options":{"nesting_mode":"list","block":{"attributes":{"automated_snapshot_start_hour":{"type":"number","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"update":{"type":"string","optional":true}}}},"vpc_options":{"nesting_mode":"list","block":{"attributes":{"availability_zones":{"type":["set","string"],"computed":true},"security_group_ids":{"type":["set","string"],"optional":true},"subnet_ids":{"type":["set","string"],"optional":true},"vpc_id":{"type":"string","computed":true}}},"max_items":1}}}},"aws_elasticsearch_domain_policy":{"version":0,"block":{"attributes":{"access_policies":{"type":"string","required":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_elastictranscoder_pipeline":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_kms_key_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"input_bucket":{"type":"string","required":true},"name":{"type":"string","optional":true,"computed":true},"output_bucket":{"type":"string","optional":true,"computed":true},"role":{"type":"string","required":true}},"block_types":{"content_config":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","optional":true,"computed":true},"storage_class":{"type":"string","optional":true}}},"max_items":1},"content_config_permissions":{"nesting_mode":"set","block":{"attributes":{"access":{"type":["list","string"],"optional":true},"grantee":{"type":"string","optional":true},"grantee_type":{"type":"string","optional":true}}}},"notifications":{"nesting_mode":"list","block":{"attributes":{"completed":{"type":"string","optional":true},"error":{"type":"string","optional":true},"progressing":{"type":"string","optional":true},"warning":{"type":"string","optional":true}}},"max_items":1},"thumbnail_config":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","optional":true,"computed":true},"storage_class":{"type":"string","optional":true}}},"max_items":1},"thumbnail_config_permissions":{"nesting_mode":"set","block":{"attributes":{"access":{"type":["list","string"],"optional":true},"grantee":{"type":"string","optional":true},"grantee_type":{"type":"string","optional":true}}}}}}},"aws_elastictranscoder_preset":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"container":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"type":{"type":"string","optional":true,"computed":true},"video_codec_options":{"type":["map","string"],"optional":true}},"block_types":{"audio":{"nesting_mode":"list","block":{"attributes":{"audio_packing_mode":{"type":"string","optional":true},"bit_rate":{"type":"string","optional":true},"channels":{"type":"string","optional":true},"codec":{"type":"string","optional":true},"sample_rate":{"type":"string","optional":true}}},"max_items":1},"audio_codec_options":{"nesting_mode":"list","block":{"attributes":{"bit_depth":{"type":"string","optional":true},"bit_order":{"type":"string","optional":true},"profile":{"type":"string","optional":true},"signed":{"type":"string","optional":true}}},"max_items":1},"thumbnails":{"nesting_mode":"list","block":{"attributes":{"aspect_ratio":{"type":"string","optional":true},"format":{"type":"string","optional":true},"interval":{"type":"string","optional":true},"max_height":{"type":"string","optional":true},"max_width":{"type":"string","optional":true},"padding_policy":{"type":"string","optional":true},"resolution":{"type":"string","optional":true},"sizing_policy":{"type":"string","optional":true}}},"max_items":1},"video":{"nesting_mode":"list","block":{"attributes":{"aspect_ratio":{"type":"string","optional":true},"bit_rate":{"type":"string","optional":true},"codec":{"type":"string","optional":true},"display_aspect_ratio":{"type":"string","optional":true},"fixed_gop":{"type":"string","optional":true},"frame_rate":{"type":"string","optional":true},"keyframes_max_dist":{"type":"string","optional":true},"max_frame_rate":{"type":"string","optional":true,"computed":true},"max_height":{"type":"string","optional":true},"max_width":{"type":"string","optional":true},"padding_policy":{"type":"string","optional":true},"resolution":{"type":"string","optional":true},"sizing_policy":{"type":"string","optional":true}}},"max_items":1},"video_watermarks":{"nesting_mode":"set","block":{"attributes":{"horizontal_align":{"type":"string","optional":true},"horizontal_offset":{"type":"string","optional":true},"id":{"type":"string","optional":true},"max_height":{"type":"string","optional":true},"max_width":{"type":"string","optional":true},"opacity":{"type":"string","optional":true},"sizing_policy":{"type":"string","optional":true},"target":{"type":"string","optional":true},"vertical_align":{"type":"string","optional":true},"vertical_offset":{"type":"string","optional":true}}}}}}},"aws_elb":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"connection_draining":{"type":"bool","optional":true},"connection_draining_timeout":{"type":"number","optional":true},"cross_zone_load_balancing":{"type":"bool","optional":true},"dns_name":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","optional":true},"instances":{"type":["set","string"],"optional":true,"computed":true},"internal":{"type":"bool","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"source_security_group":{"type":"string","optional":true,"computed":true},"source_security_group_id":{"type":"string","computed":true},"subnets":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"zone_id":{"type":"string","computed":true}},"block_types":{"access_logs":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"bucket_prefix":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"interval":{"type":"number","optional":true}}},"max_items":1},"health_check":{"nesting_mode":"list","block":{"attributes":{"healthy_threshold":{"type":"number","required":true},"interval":{"type":"number","required":true},"target":{"type":"string","required":true},"timeout":{"type":"number","required":true},"unhealthy_threshold":{"type":"number","required":true}}},"max_items":1},"listener":{"nesting_mode":"set","block":{"attributes":{"instance_port":{"type":"number","required":true},"instance_protocol":{"type":"string","required":true},"lb_port":{"type":"number","required":true},"lb_protocol":{"type":"string","required":true},"ssl_certificate_id":{"type":"string","optional":true}}},"min_items":1}}}},"aws_elb_attachment":{"version":0,"block":{"attributes":{"elb":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instance":{"type":"string","required":true}}}},"aws_emr_cluster":{"version":0,"block":{"attributes":{"additional_info":{"type":"string","optional":true},"applications":{"type":["set","string"],"optional":true},"arn":{"type":"string","computed":true},"autoscaling_role":{"type":"string","optional":true},"cluster_state":{"type":"string","computed":true},"configurations":{"type":"string","optional":true},"configurations_json":{"type":"string","optional":true},"custom_ami_id":{"type":"string","optional":true},"ebs_root_volume_size":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"keep_job_flow_alive_when_no_steps":{"type":"bool","optional":true,"computed":true},"log_uri":{"type":"string","optional":true},"master_public_dns":{"type":"string","computed":true},"name":{"type":"string","required":true},"release_label":{"type":"string","required":true},"scale_down_behavior":{"type":"string","optional":true,"computed":true},"security_configuration":{"type":"string","optional":true},"service_role":{"type":"string","required":true},"step":{"type":["list",["object",{"action_on_failure":"string","hadoop_jar_step":["list",["object",{"args":["list","string"],"jar":"string","main_class":"string","properties":["map","string"]}]],"name":"string"}]],"optional":true,"computed":true},"step_concurrency_level":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true},"termination_protection":{"type":"bool","optional":true,"computed":true},"visible_to_all_users":{"type":"bool","optional":true}},"block_types":{"bootstrap_action":{"nesting_mode":"list","block":{"attributes":{"args":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"path":{"type":"string","required":true}}}},"core_instance_group":{"nesting_mode":"list","block":{"attributes":{"autoscaling_policy":{"type":"string","optional":true},"bid_price":{"type":"string","optional":true},"id":{"type":"string","computed":true},"instance_count":{"type":"number","optional":true},"instance_type":{"type":"string","required":true},"name":{"type":"string","optional":true}},"block_types":{"ebs_config":{"nesting_mode":"set","block":{"attributes":{"iops":{"type":"number","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","required":true},"volumes_per_instance":{"type":"number","optional":true}}}}}},"max_items":1},"ec2_attributes":{"nesting_mode":"list","block":{"attributes":{"additional_master_security_groups":{"type":"string","optional":true},"additional_slave_security_groups":{"type":"string","optional":true},"emr_managed_master_security_group":{"type":"string","optional":true,"computed":true},"emr_managed_slave_security_group":{"type":"string","optional":true,"computed":true},"instance_profile":{"type":"string","required":true},"key_name":{"type":"string","optional":true},"service_access_security_group":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true}}},"max_items":1},"kerberos_attributes":{"nesting_mode":"list","block":{"attributes":{"ad_domain_join_password":{"type":"string","optional":true,"sensitive":true},"ad_domain_join_user":{"type":"string","optional":true},"cross_realm_trust_principal_password":{"type":"string","optional":true,"sensitive":true},"kdc_admin_password":{"type":"string","required":true,"sensitive":true},"realm":{"type":"string","required":true}}},"max_items":1},"master_instance_group":{"nesting_mode":"list","block":{"attributes":{"bid_price":{"type":"string","optional":true},"id":{"type":"string","computed":true},"instance_count":{"type":"number","optional":true},"instance_type":{"type":"string","required":true},"name":{"type":"string","optional":true}},"block_types":{"ebs_config":{"nesting_mode":"set","block":{"attributes":{"iops":{"type":"number","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","required":true},"volumes_per_instance":{"type":"number","optional":true}}}}}},"max_items":1}}}},"aws_emr_instance_group":{"version":0,"block":{"attributes":{"autoscaling_policy":{"type":"string","optional":true},"bid_price":{"type":"string","optional":true},"cluster_id":{"type":"string","required":true},"configurations_json":{"type":"string","optional":true},"ebs_optimized":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_count":{"type":"number","optional":true},"instance_type":{"type":"string","required":true},"name":{"type":"string","optional":true},"running_instance_count":{"type":"number","computed":true},"status":{"type":"string","computed":true}},"block_types":{"ebs_config":{"nesting_mode":"set","block":{"attributes":{"iops":{"type":"number","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","required":true},"volumes_per_instance":{"type":"number","optional":true}}}}}}},"aws_emr_security_configuration":{"version":0,"block":{"attributes":{"configuration":{"type":"string","required":true},"creation_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true}}}},"aws_flow_log":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"eni_id":{"type":"string","optional":true},"iam_role_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"log_destination":{"type":"string","optional":true,"computed":true},"log_destination_type":{"type":"string","optional":true},"log_format":{"type":"string","optional":true,"computed":true},"log_group_name":{"type":"string","optional":true,"computed":true},"max_aggregation_interval":{"type":"number","optional":true},"subnet_id":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"traffic_type":{"type":"string","required":true},"vpc_id":{"type":"string","optional":true}}}},"aws_fms_admin_account":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_fsx_lustre_file_system":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deployment_type":{"type":"string","optional":true},"dns_name":{"type":"string","computed":true},"export_path":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"import_path":{"type":"string","optional":true},"imported_file_chunk_size":{"type":"number","optional":true,"computed":true},"network_interface_ids":{"type":["set","string"],"computed":true},"owner_id":{"type":"string","computed":true},"per_unit_storage_throughput":{"type":"number","optional":true},"security_group_ids":{"type":["set","string"],"optional":true},"storage_capacity":{"type":"number","required":true},"subnet_ids":{"type":["list","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"weekly_maintenance_start_time":{"type":"string","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_fsx_windows_file_system":{"version":0,"block":{"attributes":{"active_directory_id":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"automatic_backup_retention_days":{"type":"number","optional":true},"copy_tags_to_backups":{"type":"bool","optional":true},"daily_automatic_backup_start_time":{"type":"string","optional":true,"computed":true},"deployment_type":{"type":"string","optional":true},"dns_name":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"network_interface_ids":{"type":["set","string"],"computed":true},"owner_id":{"type":"string","computed":true},"preferred_file_server_ip":{"type":"string","computed":true},"preferred_subnet_id":{"type":"string","optional":true,"computed":true},"remote_administration_endpoint":{"type":"string","computed":true},"security_group_ids":{"type":["set","string"],"optional":true},"skip_final_backup":{"type":"bool","optional":true},"storage_capacity":{"type":"number","required":true},"storage_type":{"type":"string","optional":true},"subnet_ids":{"type":["list","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"throughput_capacity":{"type":"number","required":true},"vpc_id":{"type":"string","computed":true},"weekly_maintenance_start_time":{"type":"string","optional":true,"computed":true}},"block_types":{"self_managed_active_directory":{"nesting_mode":"list","block":{"attributes":{"dns_ips":{"type":["set","string"],"required":true},"domain_name":{"type":"string","required":true},"file_system_administrators_group":{"type":"string","optional":true},"organizational_unit_distinguished_name":{"type":"string","optional":true},"password":{"type":"string","required":true,"sensitive":true},"username":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_gamelift_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"routing_strategy":{"nesting_mode":"list","block":{"attributes":{"fleet_id":{"type":"string","optional":true},"message":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_gamelift_build":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"operating_system":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","optional":true}},"block_types":{"storage_location":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"key":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_gamelift_fleet":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"build_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"ec2_instance_type":{"type":"string","required":true},"fleet_type":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_role_arn":{"type":"string","optional":true},"log_paths":{"type":["list","string"],"computed":true},"metric_groups":{"type":["list","string"],"optional":true,"computed":true},"name":{"type":"string","required":true},"new_game_session_protection_policy":{"type":"string","optional":true},"operating_system":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"ec2_inbound_permission":{"nesting_mode":"list","block":{"attributes":{"from_port":{"type":"number","required":true},"ip_range":{"type":"string","required":true},"protocol":{"type":"string","required":true},"to_port":{"type":"number","required":true}}},"max_items":50},"resource_creation_limit_policy":{"nesting_mode":"list","block":{"attributes":{"new_game_sessions_per_creator":{"type":"number","optional":true},"policy_period_in_minutes":{"type":"number","optional":true}}},"max_items":1},"runtime_configuration":{"nesting_mode":"list","block":{"attributes":{"game_session_activation_timeout_seconds":{"type":"number","optional":true},"max_concurrent_game_session_activations":{"type":"number","optional":true}},"block_types":{"server_process":{"nesting_mode":"list","block":{"attributes":{"concurrent_executions":{"type":"number","required":true},"launch_path":{"type":"string","required":true},"parameters":{"type":"string","optional":true}}},"max_items":50}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_gamelift_game_session_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"destinations":{"type":["list","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"timeout_in_seconds":{"type":"number","optional":true}},"block_types":{"player_latency_policy":{"nesting_mode":"list","block":{"attributes":{"maximum_individual_player_latency_milliseconds":{"type":"number","required":true},"policy_duration_seconds":{"type":"number","optional":true}}}}}}},"aws_glacier_vault":{"version":0,"block":{"attributes":{"access_policy":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"location":{"type":"string","computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"notification":{"nesting_mode":"list","block":{"attributes":{"events":{"type":["set","string"],"required":true},"sns_topic":{"type":"string","required":true}}}}}}},"aws_glacier_vault_lock":{"version":0,"block":{"attributes":{"complete_lock":{"type":"bool","required":true},"id":{"type":"string","optional":true,"computed":true},"ignore_deletion_error":{"type":"bool","optional":true},"policy":{"type":"string","required":true},"vault_name":{"type":"string","required":true}}}},"aws_globalaccelerator_accelerator":{"version":0,"block":{"attributes":{"dns_name":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address_type":{"type":"string","optional":true},"ip_sets":{"type":["list",["object",{"ip_addresses":["list","string"],"ip_family":"string"}]],"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"attributes":{"nesting_mode":"list","block":{"attributes":{"flow_logs_enabled":{"type":"bool","optional":true},"flow_logs_s3_bucket":{"type":"string","optional":true},"flow_logs_s3_prefix":{"type":"string","optional":true}}},"max_items":1}}}},"aws_globalaccelerator_endpoint_group":{"version":0,"block":{"attributes":{"endpoint_group_region":{"type":"string","optional":true,"computed":true},"health_check_interval_seconds":{"type":"number","optional":true},"health_check_path":{"type":"string","optional":true},"health_check_port":{"type":"number","optional":true},"health_check_protocol":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true},"threshold_count":{"type":"number","optional":true},"traffic_dial_percentage":{"type":"number","optional":true}},"block_types":{"endpoint_configuration":{"nesting_mode":"set","block":{"attributes":{"endpoint_id":{"type":"string","optional":true},"weight":{"type":"number","optional":true}}},"max_items":10}}}},"aws_globalaccelerator_listener":{"version":0,"block":{"attributes":{"accelerator_arn":{"type":"string","required":true},"client_affinity":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"protocol":{"type":"string","required":true}},"block_types":{"port_range":{"nesting_mode":"set","block":{"attributes":{"from_port":{"type":"number","optional":true},"to_port":{"type":"number","optional":true}}},"min_items":1,"max_items":10}}}},"aws_glue_catalog_database":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"catalog_id":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"location_uri":{"type":"string","optional":true},"name":{"type":"string","required":true},"parameters":{"type":["map","string"],"optional":true}}}},"aws_glue_catalog_table":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"catalog_id":{"type":"string","optional":true,"computed":true},"database_name":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owner":{"type":"string","optional":true},"parameters":{"type":["map","string"],"optional":true},"retention":{"type":"number","optional":true},"table_type":{"type":"string","optional":true},"view_expanded_text":{"type":"string","optional":true},"view_original_text":{"type":"string","optional":true}},"block_types":{"partition_keys":{"nesting_mode":"list","block":{"attributes":{"comment":{"type":"string","optional":true},"name":{"type":"string","required":true},"type":{"type":"string","optional":true}}}},"storage_descriptor":{"nesting_mode":"list","block":{"attributes":{"bucket_columns":{"type":["list","string"],"optional":true},"compressed":{"type":"bool","optional":true},"input_format":{"type":"string","optional":true},"location":{"type":"string","optional":true},"number_of_buckets":{"type":"number","optional":true},"output_format":{"type":"string","optional":true},"parameters":{"type":["map","string"],"optional":true},"stored_as_sub_directories":{"type":"bool","optional":true}},"block_types":{"columns":{"nesting_mode":"list","block":{"attributes":{"comment":{"type":"string","optional":true},"name":{"type":"string","required":true},"type":{"type":"string","optional":true}}}},"ser_de_info":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","optional":true},"parameters":{"type":["map","string"],"optional":true},"serialization_library":{"type":"string","optional":true}}},"max_items":1},"skewed_info":{"nesting_mode":"list","block":{"attributes":{"skewed_column_names":{"type":["list","string"],"optional":true},"skewed_column_value_location_maps":{"type":["map","string"],"optional":true},"skewed_column_values":{"type":["list","string"],"optional":true}}},"max_items":1},"sort_columns":{"nesting_mode":"list","block":{"attributes":{"column":{"type":"string","required":true},"sort_order":{"type":"number","required":true}}}}}},"max_items":1}}}},"aws_glue_classifier":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"csv_classifier":{"nesting_mode":"list","block":{"attributes":{"allow_single_column":{"type":"bool","optional":true},"contains_header":{"type":"string","optional":true},"delimiter":{"type":"string","optional":true},"disable_value_trimming":{"type":"bool","optional":true},"header":{"type":["list","string"],"optional":true},"quote_symbol":{"type":"string","optional":true}}},"max_items":1},"grok_classifier":{"nesting_mode":"list","block":{"attributes":{"classification":{"type":"string","required":true},"custom_patterns":{"type":"string","optional":true},"grok_pattern":{"type":"string","required":true}}},"max_items":1},"json_classifier":{"nesting_mode":"list","block":{"attributes":{"json_path":{"type":"string","required":true}}},"max_items":1},"xml_classifier":{"nesting_mode":"list","block":{"attributes":{"classification":{"type":"string","required":true},"row_tag":{"type":"string","required":true}}},"max_items":1}}}},"aws_glue_connection":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"catalog_id":{"type":"string","optional":true,"computed":true},"connection_properties":{"type":["map","string"],"required":true,"sensitive":true},"connection_type":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"match_criteria":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true}},"block_types":{"physical_connection_requirements":{"nesting_mode":"list","block":{"attributes":{"availability_zone":{"type":"string","optional":true},"security_group_id_list":{"type":["list","string"],"optional":true},"subnet_id":{"type":"string","optional":true}}},"max_items":1}}}},"aws_glue_crawler":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"classifiers":{"type":["list","string"],"optional":true},"configuration":{"type":"string","optional":true},"database_name":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role":{"type":"string","required":true},"schedule":{"type":"string","optional":true},"security_configuration":{"type":"string","optional":true},"table_prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"catalog_target":{"nesting_mode":"list","block":{"attributes":{"database_name":{"type":"string","required":true},"tables":{"type":["list","string"],"required":true}}}},"dynamodb_target":{"nesting_mode":"list","block":{"attributes":{"path":{"type":"string","required":true}}}},"jdbc_target":{"nesting_mode":"list","block":{"attributes":{"connection_name":{"type":"string","required":true},"exclusions":{"type":["list","string"],"optional":true},"path":{"type":"string","required":true}}}},"s3_target":{"nesting_mode":"list","block":{"attributes":{"exclusions":{"type":["list","string"],"optional":true},"path":{"type":"string","required":true}}}},"schema_change_policy":{"nesting_mode":"list","block":{"attributes":{"delete_behavior":{"type":"string","optional":true},"update_behavior":{"type":"string","optional":true}}},"max_items":1}}}},"aws_glue_job":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"connections":{"type":["list","string"],"optional":true},"default_arguments":{"type":["map","string"],"optional":true},"description":{"type":"string","optional":true},"glue_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"max_capacity":{"type":"number","optional":true,"computed":true},"max_retries":{"type":"number","optional":true},"name":{"type":"string","required":true},"number_of_workers":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"security_configuration":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"timeout":{"type":"number","optional":true},"worker_type":{"type":"string","optional":true}},"block_types":{"command":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","optional":true},"python_version":{"type":"string","optional":true,"computed":true},"script_location":{"type":"string","required":true}}},"min_items":1,"max_items":1},"execution_property":{"nesting_mode":"list","block":{"attributes":{"max_concurrent_runs":{"type":"number","optional":true}}},"max_items":1},"notification_property":{"nesting_mode":"list","block":{"attributes":{"notify_delay_after":{"type":"number","optional":true}}},"max_items":1}}}},"aws_glue_security_configuration":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"encryption_configuration":{"nesting_mode":"list","block":{"block_types":{"cloudwatch_encryption":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_encryption_mode":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true}}},"min_items":1,"max_items":1},"job_bookmarks_encryption":{"nesting_mode":"list","block":{"attributes":{"job_bookmarks_encryption_mode":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true}}},"min_items":1,"max_items":1},"s3_encryption":{"nesting_mode":"list","block":{"attributes":{"kms_key_arn":{"type":"string","optional":true},"s3_encryption_mode":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}}},"aws_glue_trigger":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"schedule":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","required":true},"workflow_name":{"type":"string","optional":true}},"block_types":{"actions":{"nesting_mode":"list","block":{"attributes":{"arguments":{"type":["map","string"],"optional":true},"crawler_name":{"type":"string","optional":true},"job_name":{"type":"string","optional":true},"timeout":{"type":"number","optional":true}}},"min_items":1},"predicate":{"nesting_mode":"list","block":{"attributes":{"logical":{"type":"string","optional":true}},"block_types":{"conditions":{"nesting_mode":"list","block":{"attributes":{"crawl_state":{"type":"string","optional":true},"crawler_name":{"type":"string","optional":true},"job_name":{"type":"string","optional":true},"logical_operator":{"type":"string","optional":true},"state":{"type":"string","optional":true}}},"min_items":1}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_glue_workflow":{"version":0,"block":{"attributes":{"default_run_properties":{"type":["map","string"],"optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true}}}},"aws_guardduty_detector":{"version":0,"block":{"attributes":{"account_id":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"enable":{"type":"bool","optional":true},"finding_publishing_frequency":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_guardduty_invite_accepter":{"version":0,"block":{"attributes":{"detector_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"master_account_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_guardduty_ipset":{"version":0,"block":{"attributes":{"activate":{"type":"bool","required":true},"arn":{"type":"string","computed":true},"detector_id":{"type":"string","required":true},"format":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"location":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_guardduty_member":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"detector_id":{"type":"string","required":true},"disable_email_notification":{"type":"bool","optional":true},"email":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"invitation_message":{"type":"string","optional":true},"invite":{"type":"bool","optional":true},"relationship_status":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_guardduty_organization_admin_account":{"version":0,"block":{"attributes":{"admin_account_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_guardduty_organization_configuration":{"version":0,"block":{"attributes":{"auto_enable":{"type":"bool","required":true},"detector_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_guardduty_threatintelset":{"version":0,"block":{"attributes":{"activate":{"type":"bool","required":true},"arn":{"type":"string","computed":true},"detector_id":{"type":"string","required":true},"format":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"location":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_iam_access_key":{"version":0,"block":{"attributes":{"encrypted_secret":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key_fingerprint":{"type":"string","computed":true},"pgp_key":{"type":"string","optional":true},"secret":{"type":"string","computed":true,"sensitive":true},"ses_smtp_password_v4":{"type":"string","computed":true,"sensitive":true},"status":{"type":"string","optional":true,"computed":true},"user":{"type":"string","required":true}}}},"aws_iam_account_alias":{"version":0,"block":{"attributes":{"account_alias":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_iam_account_password_policy":{"version":0,"block":{"attributes":{"allow_users_to_change_password":{"type":"bool","optional":true},"expire_passwords":{"type":"bool","computed":true},"hard_expiry":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"max_password_age":{"type":"number","optional":true,"computed":true},"minimum_password_length":{"type":"number","optional":true},"password_reuse_prevention":{"type":"number","optional":true,"computed":true},"require_lowercase_characters":{"type":"bool","optional":true,"computed":true},"require_numbers":{"type":"bool","optional":true,"computed":true},"require_symbols":{"type":"bool","optional":true,"computed":true},"require_uppercase_characters":{"type":"bool","optional":true,"computed":true}}}},"aws_iam_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"path":{"type":"string","optional":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_group_membership":{"version":0,"block":{"attributes":{"group":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"users":{"type":["set","string"],"required":true}}}},"aws_iam_group_policy":{"version":0,"block":{"attributes":{"group":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","required":true}}}},"aws_iam_group_policy_attachment":{"version":0,"block":{"attributes":{"group":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy_arn":{"type":"string","required":true}}}},"aws_iam_instance_profile":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"create_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","optional":true},"role":{"type":"string","optional":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_openid_connect_provider":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"client_id_list":{"type":["list","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"thumbprint_list":{"type":["list","string"],"required":true},"url":{"type":"string","required":true}}}},"aws_iam_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","optional":true},"policy":{"type":"string","required":true}}}},"aws_iam_policy_attachment":{"version":0,"block":{"attributes":{"groups":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"policy_arn":{"type":"string","required":true},"roles":{"type":["set","string"],"optional":true},"users":{"type":["set","string"],"optional":true}}}},"aws_iam_role":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"assume_role_policy":{"type":"string","required":true},"create_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"force_detach_policies":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"max_session_duration":{"type":"number","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","optional":true},"permissions_boundary":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_role_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","required":true},"role":{"type":"string","required":true}}}},"aws_iam_role_policy_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy_arn":{"type":"string","required":true},"role":{"type":"string","required":true}}}},"aws_iam_saml_provider":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"saml_metadata_document":{"type":"string","required":true},"valid_until":{"type":"string","computed":true}}}},"aws_iam_server_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"certificate_body":{"type":"string","required":true},"certificate_chain":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","optional":true},"private_key":{"type":"string","required":true,"sensitive":true}}}},"aws_iam_service_linked_role":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_service_name":{"type":"string","required":true},"create_date":{"type":"string","computed":true},"custom_suffix":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"path":{"type":"string","computed":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_user":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"force_destroy":{"type":"bool","description":"Delete user even if it has non-Terraform-managed IAM access keys, login profile or MFA devices","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"path":{"type":"string","optional":true},"permissions_boundary":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_user_group_membership":{"version":0,"block":{"attributes":{"groups":{"type":["set","string"],"required":true},"id":{"type":"string","optional":true,"computed":true},"user":{"type":"string","required":true}}}},"aws_iam_user_login_profile":{"version":0,"block":{"attributes":{"encrypted_password":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key_fingerprint":{"type":"string","computed":true},"password_length":{"type":"number","optional":true},"password_reset_required":{"type":"bool","optional":true},"pgp_key":{"type":"string","required":true},"user":{"type":"string","required":true}}}},"aws_iam_user_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","required":true},"user":{"type":"string","required":true}}}},"aws_iam_user_policy_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy_arn":{"type":"string","required":true},"user":{"type":"string","required":true}}}},"aws_iam_user_ssh_key":{"version":0,"block":{"attributes":{"encoding":{"type":"string","required":true},"fingerprint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"public_key":{"type":"string","required":true},"ssh_public_key_id":{"type":"string","computed":true},"status":{"type":"string","optional":true,"computed":true},"username":{"type":"string","required":true}}}},"aws_inspector_assessment_target":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"resource_group_arn":{"type":"string","optional":true}}}},"aws_inspector_assessment_template":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"duration":{"type":"number","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"rules_package_arns":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true},"target_arn":{"type":"string","required":true}}}},"aws_inspector_resource_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"required":true}}}},"aws_instance":{"version":1,"block":{"attributes":{"ami":{"type":"string","required":true},"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","optional":true,"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"cpu_core_count":{"type":"number","optional":true,"computed":true},"cpu_threads_per_core":{"type":"number","optional":true,"computed":true},"disable_api_termination":{"type":"bool","optional":true},"ebs_optimized":{"type":"bool","optional":true},"get_password_data":{"type":"bool","optional":true},"hibernation":{"type":"bool","optional":true},"host_id":{"type":"string","optional":true,"computed":true},"iam_instance_profile":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_initiated_shutdown_behavior":{"type":"string","optional":true},"instance_state":{"type":"string","computed":true},"instance_type":{"type":"string","required":true},"ipv6_address_count":{"type":"number","optional":true,"computed":true},"ipv6_addresses":{"type":["list","string"],"optional":true,"computed":true},"key_name":{"type":"string","optional":true,"computed":true},"monitoring":{"type":"bool","optional":true},"outpost_arn":{"type":"string","computed":true},"password_data":{"type":"string","computed":true},"placement_group":{"type":"string","optional":true,"computed":true},"primary_network_interface_id":{"type":"string","computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","optional":true,"computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"secondary_private_ips":{"type":["set","string"],"optional":true,"computed":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"source_dest_check":{"type":"bool","optional":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"tenancy":{"type":"string","optional":true,"computed":true},"user_data":{"type":"string","optional":true},"user_data_base64":{"type":"string","optional":true},"volume_tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"credit_specification":{"nesting_mode":"list","block":{"attributes":{"cpu_credits":{"type":"string","optional":true}}},"max_items":1},"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"no_device":{"type":"bool","optional":true},"virtual_name":{"type":"string","optional":true}}}},"metadata_options":{"nesting_mode":"list","block":{"attributes":{"http_endpoint":{"type":"string","optional":true,"computed":true},"http_put_response_hop_limit":{"type":"number","optional":true,"computed":true},"http_tokens":{"type":"string","optional":true,"computed":true}}},"max_items":1},"network_interface":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_index":{"type":"number","required":true},"network_interface_id":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"list","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","computed":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_internet_gateway":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","optional":true}}}},"aws_iot_certificate":{"version":0,"block":{"attributes":{"active":{"type":"bool","required":true},"arn":{"type":"string","computed":true},"certificate_pem":{"type":"string","computed":true,"sensitive":true},"csr":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","computed":true,"sensitive":true},"public_key":{"type":"string","computed":true,"sensitive":true}}}},"aws_iot_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"default_version_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"policy":{"type":"string","required":true}}}},"aws_iot_policy_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true},"target":{"type":"string","required":true}}}},"aws_iot_role_alias":{"version":0,"block":{"attributes":{"alias":{"type":"string","required":true},"arn":{"type":"string","computed":true},"credential_duration":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","required":true}}}},"aws_iot_thing":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"attributes":{"type":["map","string"],"optional":true},"default_client_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"thing_type_name":{"type":"string","optional":true},"version":{"type":"number","computed":true}}}},"aws_iot_thing_principal_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"principal":{"type":"string","required":true},"thing":{"type":"string","required":true}}}},"aws_iot_thing_type":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deprecated":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"properties":{"nesting_mode":"list","block":{"attributes":{"description":{"type":"string","optional":true},"searchable_attributes":{"type":["set","string"],"optional":true,"computed":true}}},"max_items":1}}}},"aws_iot_topic_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"enabled":{"type":"bool","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"sql":{"type":"string","required":true},"sql_version":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"cloudwatch_alarm":{"nesting_mode":"set","block":{"attributes":{"alarm_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"state_reason":{"type":"string","required":true},"state_value":{"type":"string","required":true}}}},"cloudwatch_metric":{"nesting_mode":"set","block":{"attributes":{"metric_name":{"type":"string","required":true},"metric_namespace":{"type":"string","required":true},"metric_timestamp":{"type":"string","optional":true},"metric_unit":{"type":"string","required":true},"metric_value":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}}},"dynamodb":{"nesting_mode":"set","block":{"attributes":{"hash_key_field":{"type":"string","required":true},"hash_key_type":{"type":"string","optional":true},"hash_key_value":{"type":"string","required":true},"operation":{"type":"string","optional":true},"payload_field":{"type":"string","optional":true},"range_key_field":{"type":"string","optional":true},"range_key_type":{"type":"string","optional":true},"range_key_value":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"table_name":{"type":"string","required":true}}}},"dynamodbv2":{"nesting_mode":"set","block":{"attributes":{"role_arn":{"type":"string","required":true}},"block_types":{"put_item":{"nesting_mode":"list","block":{"attributes":{"table_name":{"type":"string","required":true}}},"max_items":1}}}},"elasticsearch":{"nesting_mode":"set","block":{"attributes":{"endpoint":{"type":"string","required":true},"id":{"type":"string","required":true},"index":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"type":{"type":"string","required":true}}}},"error_action":{"nesting_mode":"list","block":{"block_types":{"cloudwatch_alarm":{"nesting_mode":"list","block":{"attributes":{"alarm_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"state_reason":{"type":"string","required":true},"state_value":{"type":"string","required":true}}},"max_items":1},"cloudwatch_metric":{"nesting_mode":"list","block":{"attributes":{"metric_name":{"type":"string","required":true},"metric_namespace":{"type":"string","required":true},"metric_timestamp":{"type":"string","optional":true},"metric_unit":{"type":"string","required":true},"metric_value":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"dynamodb":{"nesting_mode":"list","block":{"attributes":{"hash_key_field":{"type":"string","required":true},"hash_key_type":{"type":"string","optional":true},"hash_key_value":{"type":"string","required":true},"operation":{"type":"string","optional":true},"payload_field":{"type":"string","optional":true},"range_key_field":{"type":"string","optional":true},"range_key_type":{"type":"string","optional":true},"range_key_value":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"table_name":{"type":"string","required":true}}},"max_items":1},"dynamodbv2":{"nesting_mode":"list","block":{"attributes":{"role_arn":{"type":"string","required":true}},"block_types":{"put_item":{"nesting_mode":"list","block":{"attributes":{"table_name":{"type":"string","required":true}}},"max_items":1}}},"max_items":1},"elasticsearch":{"nesting_mode":"list","block":{"attributes":{"endpoint":{"type":"string","required":true},"id":{"type":"string","required":true},"index":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"type":{"type":"string","required":true}}},"max_items":1},"firehose":{"nesting_mode":"list","block":{"attributes":{"delivery_stream_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"separator":{"type":"string","optional":true}}},"max_items":1},"iot_analytics":{"nesting_mode":"list","block":{"attributes":{"channel_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"iot_events":{"nesting_mode":"list","block":{"attributes":{"input_name":{"type":"string","required":true},"message_id":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"kinesis":{"nesting_mode":"list","block":{"attributes":{"partition_key":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"stream_name":{"type":"string","required":true}}},"max_items":1},"lambda":{"nesting_mode":"list","block":{"attributes":{"function_arn":{"type":"string","required":true}}},"max_items":1},"republish":{"nesting_mode":"list","block":{"attributes":{"qos":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"topic":{"type":"string","required":true}}},"max_items":1},"s3":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","required":true},"key":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"sns":{"nesting_mode":"list","block":{"attributes":{"message_format":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"target_arn":{"type":"string","required":true}}},"max_items":1},"sqs":{"nesting_mode":"list","block":{"attributes":{"queue_url":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"use_base64":{"type":"bool","required":true}}},"max_items":1},"step_functions":{"nesting_mode":"list","block":{"attributes":{"execution_name_prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"state_machine_name":{"type":"string","required":true}}},"max_items":1}}},"max_items":1},"firehose":{"nesting_mode":"set","block":{"attributes":{"delivery_stream_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"separator":{"type":"string","optional":true}}}},"iot_analytics":{"nesting_mode":"set","block":{"attributes":{"channel_name":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}}},"iot_events":{"nesting_mode":"set","block":{"attributes":{"input_name":{"type":"string","required":true},"message_id":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}}}},"kinesis":{"nesting_mode":"set","block":{"attributes":{"partition_key":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"stream_name":{"type":"string","required":true}}}},"lambda":{"nesting_mode":"set","block":{"attributes":{"function_arn":{"type":"string","required":true}}}},"republish":{"nesting_mode":"set","block":{"attributes":{"qos":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"topic":{"type":"string","required":true}}}},"s3":{"nesting_mode":"set","block":{"attributes":{"bucket_name":{"type":"string","required":true},"key":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}}},"sns":{"nesting_mode":"set","block":{"attributes":{"message_format":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"target_arn":{"type":"string","required":true}}}},"sqs":{"nesting_mode":"set","block":{"attributes":{"queue_url":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"use_base64":{"type":"bool","required":true}}}},"step_functions":{"nesting_mode":"set","block":{"attributes":{"execution_name_prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"state_machine_name":{"type":"string","required":true}}}}}}},"aws_key_pair":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"fingerprint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key_name":{"type":"string","optional":true,"computed":true},"key_name_prefix":{"type":"string","optional":true},"key_pair_id":{"type":"string","computed":true},"public_key":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_kinesis_analytics_application":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"code":{"type":"string","optional":true},"create_timestamp":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"last_update_timestamp":{"type":"string","computed":true},"name":{"type":"string","required":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"number","computed":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","computed":true},"log_stream_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"inputs":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","computed":true},"name_prefix":{"type":"string","required":true},"starting_position_configuration":{"type":["list",["object",{"starting_position":"string"}]],"computed":true},"stream_names":{"type":["set","string"],"computed":true}},"block_types":{"kinesis_firehose":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"kinesis_stream":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"parallelism":{"nesting_mode":"list","block":{"attributes":{"count":{"type":"number","required":true}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"block_types":{"lambda":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"max_items":1},"schema":{"nesting_mode":"list","block":{"attributes":{"record_encoding":{"type":"string","optional":true}},"block_types":{"record_columns":{"nesting_mode":"list","block":{"attributes":{"mapping":{"type":"string","optional":true},"name":{"type":"string","required":true},"sql_type":{"type":"string","required":true}}},"min_items":1},"record_format":{"nesting_mode":"list","block":{"attributes":{"record_format_type":{"type":"string","computed":true}},"block_types":{"mapping_parameters":{"nesting_mode":"list","block":{"block_types":{"csv":{"nesting_mode":"list","block":{"attributes":{"record_column_delimiter":{"type":"string","required":true},"record_row_delimiter":{"type":"string","required":true}}},"max_items":1},"json":{"nesting_mode":"list","block":{"attributes":{"record_row_path":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1},"outputs":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","computed":true},"name":{"type":"string","required":true}},"block_types":{"kinesis_firehose":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"kinesis_stream":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"lambda":{"nesting_mode":"list","block":{"attributes":{"resource_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"schema":{"nesting_mode":"list","block":{"attributes":{"record_format_type":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"max_items":3},"reference_data_sources":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","computed":true},"table_name":{"type":"string","required":true}},"block_types":{"s3":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"file_key":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"min_items":1,"max_items":1},"schema":{"nesting_mode":"list","block":{"attributes":{"record_encoding":{"type":"string","optional":true}},"block_types":{"record_columns":{"nesting_mode":"list","block":{"attributes":{"mapping":{"type":"string","optional":true},"name":{"type":"string","required":true},"sql_type":{"type":"string","required":true}}},"min_items":1},"record_format":{"nesting_mode":"list","block":{"attributes":{"record_format_type":{"type":"string","computed":true}},"block_types":{"mapping_parameters":{"nesting_mode":"list","block":{"block_types":{"csv":{"nesting_mode":"list","block":{"attributes":{"record_column_delimiter":{"type":"string","required":true},"record_row_delimiter":{"type":"string","required":true}}},"max_items":1},"json":{"nesting_mode":"list","block":{"attributes":{"record_row_path":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1}}}},"aws_kinesis_firehose_delivery_stream":{"version":1,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"destination":{"type":"string","required":true},"destination_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"version_id":{"type":"string","optional":true,"computed":true}},"block_types":{"elasticsearch_configuration":{"nesting_mode":"list","block":{"attributes":{"buffering_interval":{"type":"number","optional":true},"buffering_size":{"type":"number","optional":true},"domain_arn":{"type":"string","required":true},"index_name":{"type":"string","required":true},"index_rotation_period":{"type":"string","optional":true},"retry_duration":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"s3_backup_mode":{"type":"string","optional":true},"type_name":{"type":"string","optional":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"processors":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"list","block":{"attributes":{"parameter_name":{"type":"string","required":true},"parameter_value":{"type":"string","required":true}}}}}}}}},"max_items":1}}},"max_items":1},"extended_s3_configuration":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"buffer_interval":{"type":"number","optional":true},"buffer_size":{"type":"number","optional":true},"compression_format":{"type":"string","optional":true},"error_output_prefix":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true},"s3_backup_mode":{"type":"string","optional":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1},"data_format_conversion_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"input_format_configuration":{"nesting_mode":"list","block":{"block_types":{"deserializer":{"nesting_mode":"list","block":{"block_types":{"hive_json_ser_de":{"nesting_mode":"list","block":{"attributes":{"timestamp_formats":{"type":["list","string"],"optional":true}}},"max_items":1},"open_x_json_ser_de":{"nesting_mode":"list","block":{"attributes":{"case_insensitive":{"type":"bool","optional":true},"column_to_json_key_mappings":{"type":["map","string"],"optional":true},"convert_dots_in_json_keys_to_underscores":{"type":"bool","optional":true}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"output_format_configuration":{"nesting_mode":"list","block":{"block_types":{"serializer":{"nesting_mode":"list","block":{"block_types":{"orc_ser_de":{"nesting_mode":"list","block":{"attributes":{"block_size_bytes":{"type":"number","optional":true},"bloom_filter_columns":{"type":["list","string"],"optional":true},"bloom_filter_false_positive_probability":{"type":"number","optional":true},"compression":{"type":"string","optional":true},"dictionary_key_threshold":{"type":"number","optional":true},"enable_padding":{"type":"bool","optional":true},"format_version":{"type":"string","optional":true},"padding_tolerance":{"type":"number","optional":true},"row_index_stride":{"type":"number","optional":true},"stripe_size_bytes":{"type":"number","optional":true}}},"max_items":1},"parquet_ser_de":{"nesting_mode":"list","block":{"attributes":{"block_size_bytes":{"type":"number","optional":true},"compression":{"type":"string","optional":true},"enable_dictionary_compression":{"type":"bool","optional":true},"max_padding_bytes":{"type":"number","optional":true},"page_size_bytes":{"type":"number","optional":true},"writer_version":{"type":"string","optional":true}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"schema_configuration":{"nesting_mode":"list","block":{"attributes":{"catalog_id":{"type":"string","optional":true,"computed":true},"database_name":{"type":"string","required":true},"region":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","required":true},"table_name":{"type":"string","required":true},"version_id":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"processors":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"list","block":{"attributes":{"parameter_name":{"type":"string","required":true},"parameter_value":{"type":"string","required":true}}}}}}}}},"max_items":1},"s3_backup_configuration":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"buffer_interval":{"type":"number","optional":true},"buffer_size":{"type":"number","optional":true},"compression_format":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}},"max_items":1},"kinesis_source_configuration":{"nesting_mode":"list","block":{"attributes":{"kinesis_stream_arn":{"type":"string","required":true},"role_arn":{"type":"string","required":true}}},"max_items":1},"redshift_configuration":{"nesting_mode":"list","block":{"attributes":{"cluster_jdbcurl":{"type":"string","required":true},"copy_options":{"type":"string","optional":true},"data_table_columns":{"type":"string","optional":true},"data_table_name":{"type":"string","required":true},"password":{"type":"string","required":true,"sensitive":true},"retry_duration":{"type":"number","optional":true},"role_arn":{"type":"string","required":true},"s3_backup_mode":{"type":"string","optional":true},"username":{"type":"string","required":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"processors":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"list","block":{"attributes":{"parameter_name":{"type":"string","required":true},"parameter_value":{"type":"string","required":true}}}}}}}}},"max_items":1},"s3_backup_configuration":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"buffer_interval":{"type":"number","optional":true},"buffer_size":{"type":"number","optional":true},"compression_format":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}},"max_items":1},"s3_configuration":{"nesting_mode":"list","block":{"attributes":{"bucket_arn":{"type":"string","required":true},"buffer_interval":{"type":"number","optional":true},"buffer_size":{"type":"number","optional":true},"compression_format":{"type":"string","optional":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"role_arn":{"type":"string","required":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"server_side_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}}},"max_items":1},"splunk_configuration":{"nesting_mode":"list","block":{"attributes":{"hec_acknowledgment_timeout":{"type":"number","optional":true},"hec_endpoint":{"type":"string","required":true},"hec_endpoint_type":{"type":"string","optional":true},"hec_token":{"type":"string","required":true},"retry_duration":{"type":"number","optional":true},"s3_backup_mode":{"type":"string","optional":true}},"block_types":{"cloudwatch_logging_options":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"log_group_name":{"type":"string","optional":true},"log_stream_name":{"type":"string","optional":true}}},"max_items":1},"processing_configuration":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}},"block_types":{"processors":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}},"block_types":{"parameters":{"nesting_mode":"list","block":{"attributes":{"parameter_name":{"type":"string","required":true},"parameter_value":{"type":"string","required":true}}}}}}}}},"max_items":1}}},"max_items":1}}}},"aws_kinesis_stream":{"version":1,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"encryption_type":{"type":"string","optional":true},"enforce_consumer_deletion":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"name":{"type":"string","required":true},"retention_period":{"type":"number","optional":true},"shard_count":{"type":"number","required":true},"shard_level_metrics":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_kinesis_video_stream":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_time":{"type":"string","computed":true},"data_retention_in_hours":{"type":"number","optional":true},"device_name":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"media_type":{"type":"string","optional":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"version":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_kms_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"name_prefix":{"type":"string","optional":true},"target_key_arn":{"type":"string","computed":true},"target_key_id":{"type":"string","required":true}}}},"aws_kms_ciphertext":{"version":0,"block":{"attributes":{"ciphertext_blob":{"type":"string","computed":true},"context":{"type":["map","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"plaintext":{"type":"string","required":true,"sensitive":true}}}},"aws_kms_external_key":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deletion_window_in_days":{"type":"number","optional":true},"description":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true,"computed":true},"expiration_model":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key_material_base64":{"type":"string","optional":true,"sensitive":true},"key_state":{"type":"string","computed":true},"key_usage":{"type":"string","computed":true},"policy":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"valid_to":{"type":"string","optional":true}}}},"aws_kms_grant":{"version":0,"block":{"attributes":{"grant_creation_tokens":{"type":["set","string"],"optional":true},"grant_id":{"type":"string","computed":true},"grant_token":{"type":"string","computed":true},"grantee_principal":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"name":{"type":"string","optional":true},"operations":{"type":["set","string"],"required":true},"retire_on_delete":{"type":"bool","optional":true},"retiring_principal":{"type":"string","optional":true}},"block_types":{"constraints":{"nesting_mode":"set","block":{"attributes":{"encryption_context_equals":{"type":["map","string"],"optional":true},"encryption_context_subset":{"type":["map","string"],"optional":true}}}}}}},"aws_kms_key":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_master_key_spec":{"type":"string","optional":true},"deletion_window_in_days":{"type":"number","optional":true},"description":{"type":"string","optional":true,"computed":true},"enable_key_rotation":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"is_enabled":{"type":"bool","optional":true},"key_id":{"type":"string","computed":true},"key_usage":{"type":"string","optional":true},"policy":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_lambda_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"function_name":{"type":"string","required":true},"function_version":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"invoke_arn":{"type":"string","computed":true},"name":{"type":"string","required":true}},"block_types":{"routing_config":{"nesting_mode":"list","block":{"attributes":{"additional_version_weights":{"type":["map","number"],"optional":true}}},"max_items":1}}}},"aws_lambda_event_source_mapping":{"version":0,"block":{"attributes":{"batch_size":{"type":"number","optional":true},"bisect_batch_on_function_error":{"type":"bool","optional":true},"enabled":{"type":"bool","optional":true},"event_source_arn":{"type":"string","required":true},"function_arn":{"type":"string","computed":true},"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"last_modified":{"type":"string","computed":true},"last_processing_result":{"type":"string","computed":true},"maximum_batching_window_in_seconds":{"type":"number","optional":true},"maximum_record_age_in_seconds":{"type":"number","optional":true,"computed":true},"maximum_retry_attempts":{"type":"number","optional":true,"computed":true},"parallelization_factor":{"type":"number","optional":true,"computed":true},"starting_position":{"type":"string","optional":true},"starting_position_timestamp":{"type":"string","optional":true},"state":{"type":"string","computed":true},"state_transition_reason":{"type":"string","computed":true},"uuid":{"type":"string","computed":true}},"block_types":{"destination_config":{"nesting_mode":"list","block":{"block_types":{"on_failure":{"nesting_mode":"list","block":{"attributes":{"destination_arn":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}}},"aws_lambda_function":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"filename":{"type":"string","optional":true},"function_name":{"type":"string","required":true},"handler":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"invoke_arn":{"type":"string","computed":true},"kms_key_arn":{"type":"string","optional":true},"last_modified":{"type":"string","computed":true},"layers":{"type":["list","string"],"optional":true},"memory_size":{"type":"number","optional":true},"publish":{"type":"bool","optional":true},"qualified_arn":{"type":"string","computed":true},"reserved_concurrent_executions":{"type":"number","optional":true},"role":{"type":"string","required":true},"runtime":{"type":"string","required":true},"s3_bucket":{"type":"string","optional":true},"s3_key":{"type":"string","optional":true},"s3_object_version":{"type":"string","optional":true},"source_code_hash":{"type":"string","optional":true,"computed":true},"source_code_size":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true},"timeout":{"type":"number","optional":true},"version":{"type":"string","computed":true}},"block_types":{"dead_letter_config":{"nesting_mode":"list","block":{"attributes":{"target_arn":{"type":"string","required":true}}},"max_items":1},"environment":{"nesting_mode":"list","block":{"attributes":{"variables":{"type":["map","string"],"optional":true}}},"max_items":1},"file_system_config":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true},"local_mount_path":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}},"tracing_config":{"nesting_mode":"list","block":{"attributes":{"mode":{"type":"string","required":true}}},"max_items":1},"vpc_config":{"nesting_mode":"list","block":{"attributes":{"security_group_ids":{"type":["set","string"],"required":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","computed":true}}},"max_items":1}}}},"aws_lambda_function_event_invoke_config":{"version":0,"block":{"attributes":{"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"maximum_event_age_in_seconds":{"type":"number","optional":true},"maximum_retry_attempts":{"type":"number","optional":true},"qualifier":{"type":"string","optional":true}},"block_types":{"destination_config":{"nesting_mode":"list","block":{"block_types":{"on_failure":{"nesting_mode":"list","block":{"attributes":{"destination":{"type":"string","required":true}}},"max_items":1},"on_success":{"nesting_mode":"list","block":{"attributes":{"destination":{"type":"string","required":true}}},"max_items":1}}},"max_items":1}}}},"aws_lambda_layer_version":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compatible_runtimes":{"type":["set","string"],"optional":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","optional":true},"filename":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"layer_arn":{"type":"string","computed":true},"layer_name":{"type":"string","required":true},"license_info":{"type":"string","optional":true},"s3_bucket":{"type":"string","optional":true},"s3_key":{"type":"string","optional":true},"s3_object_version":{"type":"string","optional":true},"source_code_hash":{"type":"string","optional":true,"computed":true},"source_code_size":{"type":"number","computed":true},"version":{"type":"string","computed":true}}}},"aws_lambda_permission":{"version":0,"block":{"attributes":{"action":{"type":"string","required":true},"event_source_token":{"type":"string","optional":true},"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"principal":{"type":"string","required":true},"qualifier":{"type":"string","optional":true},"source_account":{"type":"string","optional":true},"source_arn":{"type":"string","optional":true},"statement_id":{"type":"string","optional":true,"computed":true},"statement_id_prefix":{"type":"string","optional":true}}}},"aws_lambda_provisioned_concurrency_config":{"version":0,"block":{"attributes":{"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"provisioned_concurrent_executions":{"type":"number","required":true},"qualifier":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_launch_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","optional":true},"ebs_optimized":{"type":"bool","optional":true,"computed":true},"enable_monitoring":{"type":"bool","optional":true},"iam_instance_profile":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","required":true},"instance_type":{"type":"string","required":true},"key_name":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"placement_tenancy":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true},"spot_price":{"type":"string","optional":true},"user_data":{"type":"string","optional":true},"user_data_base64":{"type":"string","optional":true},"vpc_classic_link_id":{"type":"string","optional":true},"vpc_classic_link_security_groups":{"type":["set","string"],"optional":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"no_device":{"type":"bool","optional":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"virtual_name":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"list","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1}}}},"aws_launch_template":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"default_version":{"type":"number","optional":true,"computed":true},"description":{"type":"string","optional":true},"disable_api_termination":{"type":"bool","optional":true},"ebs_optimized":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","optional":true},"instance_initiated_shutdown_behavior":{"type":"string","optional":true},"instance_type":{"type":"string","optional":true},"kernel_id":{"type":"string","optional":true},"key_name":{"type":"string","optional":true},"latest_version":{"type":"number","computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"ram_disk_id":{"type":"string","optional":true},"security_group_names":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"update_default_version":{"type":"bool","optional":true},"user_data":{"type":"string","optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true}},"block_types":{"block_device_mappings":{"nesting_mode":"list","block":{"attributes":{"device_name":{"type":"string","optional":true},"no_device":{"type":"string","optional":true},"virtual_name":{"type":"string","optional":true}},"block_types":{"ebs":{"nesting_mode":"list","block":{"attributes":{"delete_on_termination":{"type":"string","optional":true},"encrypted":{"type":"string","optional":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"snapshot_id":{"type":"string","optional":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1}}}},"capacity_reservation_specification":{"nesting_mode":"list","block":{"attributes":{"capacity_reservation_preference":{"type":"string","optional":true}},"block_types":{"capacity_reservation_target":{"nesting_mode":"list","block":{"attributes":{"capacity_reservation_id":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1},"cpu_options":{"nesting_mode":"list","block":{"attributes":{"core_count":{"type":"number","optional":true},"threads_per_core":{"type":"number","optional":true}}},"max_items":1},"credit_specification":{"nesting_mode":"list","block":{"attributes":{"cpu_credits":{"type":"string","optional":true}}},"max_items":1},"elastic_gpu_specifications":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}}},"elastic_inference_accelerator":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1},"hibernation_options":{"nesting_mode":"list","block":{"attributes":{"configured":{"type":"bool","required":true}}},"max_items":1},"iam_instance_profile":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","optional":true},"name":{"type":"string","optional":true}}},"max_items":1},"instance_market_options":{"nesting_mode":"list","block":{"attributes":{"market_type":{"type":"string","optional":true}},"block_types":{"spot_options":{"nesting_mode":"list","block":{"attributes":{"block_duration_minutes":{"type":"number","optional":true},"instance_interruption_behavior":{"type":"string","optional":true},"max_price":{"type":"string","optional":true},"spot_instance_type":{"type":"string","optional":true},"valid_until":{"type":"string","optional":true,"computed":true}}},"max_items":1}}},"max_items":1},"license_specification":{"nesting_mode":"set","block":{"attributes":{"license_configuration_arn":{"type":"string","required":true}}}},"metadata_options":{"nesting_mode":"list","block":{"attributes":{"http_endpoint":{"type":"string","optional":true,"computed":true},"http_put_response_hop_limit":{"type":"number","optional":true,"computed":true},"http_tokens":{"type":"string","optional":true,"computed":true}}},"max_items":1},"monitoring":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true}}},"max_items":1},"network_interfaces":{"nesting_mode":"list","block":{"attributes":{"associate_public_ip_address":{"type":"string","optional":true},"delete_on_termination":{"type":"string","optional":true},"description":{"type":"string","optional":true},"device_index":{"type":"number","optional":true},"ipv4_address_count":{"type":"number","optional":true},"ipv4_addresses":{"type":["set","string"],"optional":true},"ipv6_address_count":{"type":"number","optional":true},"ipv6_addresses":{"type":["set","string"],"optional":true},"network_interface_id":{"type":"string","optional":true},"private_ip_address":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true},"subnet_id":{"type":"string","optional":true}}}},"placement":{"nesting_mode":"list","block":{"attributes":{"affinity":{"type":"string","optional":true},"availability_zone":{"type":"string","optional":true},"group_name":{"type":"string","optional":true},"host_id":{"type":"string","optional":true},"partition_number":{"type":"number","optional":true},"spread_domain":{"type":"string","optional":true},"tenancy":{"type":"string","optional":true}}},"max_items":1},"tag_specifications":{"nesting_mode":"list","block":{"attributes":{"resource_type":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}}}}},"aws_lb":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"arn_suffix":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"drop_invalid_header_fields":{"type":"bool","optional":true},"enable_cross_zone_load_balancing":{"type":"bool","optional":true},"enable_deletion_protection":{"type":"bool","optional":true},"enable_http2":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","optional":true},"internal":{"type":"bool","optional":true,"computed":true},"ip_address_type":{"type":"string","optional":true,"computed":true},"load_balancer_type":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"subnets":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"zone_id":{"type":"string","computed":true}},"block_types":{"access_logs":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"prefix":{"type":"string","optional":true}}},"max_items":1},"subnet_mapping":{"nesting_mode":"set","block":{"attributes":{"allocation_id":{"type":"string","optional":true},"private_ipv4_address":{"type":"string","optional":true},"subnet_id":{"type":"string","required":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_lb_cookie_stickiness_policy":{"version":0,"block":{"attributes":{"cookie_expiration_period":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"lb_port":{"type":"number","required":true},"load_balancer":{"type":"string","required":true},"name":{"type":"string","required":true}}}},"aws_lb_listener":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"load_balancer_arn":{"type":"string","required":true},"port":{"type":"number","required":true},"protocol":{"type":"string","optional":true},"ssl_policy":{"type":"string","optional":true,"computed":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"attributes":{"order":{"type":"number","optional":true,"computed":true},"target_group_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"authenticate_cognito":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"user_pool_arn":{"type":"string","required":true},"user_pool_client_id":{"type":"string","required":true},"user_pool_domain":{"type":"string","required":true}}},"max_items":1},"authenticate_oidc":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"authorization_endpoint":{"type":"string","required":true},"client_id":{"type":"string","required":true},"client_secret":{"type":"string","required":true,"sensitive":true},"issuer":{"type":"string","required":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"token_endpoint":{"type":"string","required":true},"user_info_endpoint":{"type":"string","required":true}}},"max_items":1},"fixed_response":{"nesting_mode":"list","block":{"attributes":{"content_type":{"type":"string","required":true},"message_body":{"type":"string","optional":true},"status_code":{"type":"string","optional":true,"computed":true}}},"max_items":1},"forward":{"nesting_mode":"list","block":{"block_types":{"stickiness":{"nesting_mode":"list","block":{"attributes":{"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1},"target_group":{"nesting_mode":"set","block":{"attributes":{"arn":{"type":"string","required":true},"weight":{"type":"number","optional":true}}},"min_items":2,"max_items":5}}},"max_items":1},"redirect":{"nesting_mode":"list","block":{"attributes":{"host":{"type":"string","optional":true},"path":{"type":"string","optional":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"query":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"read":{"type":"string","optional":true}}}}}}},"aws_lb_listener_certificate":{"version":0,"block":{"attributes":{"certificate_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true}}}},"aws_lb_listener_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"listener_arn":{"type":"string","required":true},"priority":{"type":"number","optional":true,"computed":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"order":{"type":"number","optional":true,"computed":true},"target_group_arn":{"type":"string","optional":true},"type":{"type":"string","required":true}},"block_types":{"authenticate_cognito":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"user_pool_arn":{"type":"string","required":true},"user_pool_client_id":{"type":"string","required":true},"user_pool_domain":{"type":"string","required":true}}},"max_items":1},"authenticate_oidc":{"nesting_mode":"list","block":{"attributes":{"authentication_request_extra_params":{"type":["map","string"],"optional":true},"authorization_endpoint":{"type":"string","required":true},"client_id":{"type":"string","required":true},"client_secret":{"type":"string","required":true,"sensitive":true},"issuer":{"type":"string","required":true},"on_unauthenticated_request":{"type":"string","optional":true,"computed":true},"scope":{"type":"string","optional":true,"computed":true},"session_cookie_name":{"type":"string","optional":true,"computed":true},"session_timeout":{"type":"number","optional":true,"computed":true},"token_endpoint":{"type":"string","required":true},"user_info_endpoint":{"type":"string","required":true}}},"max_items":1},"fixed_response":{"nesting_mode":"list","block":{"attributes":{"content_type":{"type":"string","required":true},"message_body":{"type":"string","optional":true},"status_code":{"type":"string","optional":true,"computed":true}}},"max_items":1},"forward":{"nesting_mode":"list","block":{"block_types":{"stickiness":{"nesting_mode":"list","block":{"attributes":{"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true}}},"max_items":1},"target_group":{"nesting_mode":"set","block":{"attributes":{"arn":{"type":"string","required":true},"weight":{"type":"number","optional":true}}},"min_items":2,"max_items":5}}},"max_items":1},"redirect":{"nesting_mode":"list","block":{"attributes":{"host":{"type":"string","optional":true},"path":{"type":"string","optional":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"query":{"type":"string","optional":true},"status_code":{"type":"string","required":true}}},"max_items":1}}},"min_items":1},"condition":{"nesting_mode":"set","block":{"block_types":{"host_header":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"http_header":{"nesting_mode":"list","block":{"attributes":{"http_header_name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}},"max_items":1},"http_request_method":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"path_pattern":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1},"query_string":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","optional":true},"value":{"type":"string","required":true}}}},"source_ip":{"nesting_mode":"list","block":{"attributes":{"values":{"type":["set","string"],"required":true}}},"max_items":1}}},"min_items":1}}}},"aws_lb_ssl_negotiation_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"lb_port":{"type":"number","required":true},"load_balancer":{"type":"string","required":true},"name":{"type":"string","required":true}},"block_types":{"attribute":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_lb_target_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"arn_suffix":{"type":"string","computed":true},"deregistration_delay":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"lambda_multi_value_headers_enabled":{"type":"bool","optional":true},"load_balancing_algorithm_type":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"port":{"type":"number","optional":true},"protocol":{"type":"string","optional":true},"proxy_protocol_v2":{"type":"bool","optional":true},"slow_start":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true},"target_type":{"type":"string","optional":true},"vpc_id":{"type":"string","optional":true}},"block_types":{"health_check":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"healthy_threshold":{"type":"number","optional":true},"interval":{"type":"number","optional":true},"matcher":{"type":"string","optional":true,"computed":true},"path":{"type":"string","optional":true,"computed":true},"port":{"type":"string","optional":true},"protocol":{"type":"string","optional":true},"timeout":{"type":"number","optional":true,"computed":true},"unhealthy_threshold":{"type":"number","optional":true}}},"max_items":1},"stickiness":{"nesting_mode":"list","block":{"attributes":{"cookie_duration":{"type":"number","optional":true},"enabled":{"type":"bool","optional":true},"type":{"type":"string","required":true}}},"max_items":1}}}},"aws_lb_target_group_attachment":{"version":0,"block":{"attributes":{"availability_zone":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"target_group_arn":{"type":"string","required":true},"target_id":{"type":"string","required":true}}}},"aws_licensemanager_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"license_configuration_arn":{"type":"string","required":true},"resource_arn":{"type":"string","required":true}}}},"aws_licensemanager_license_configuration":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"license_count":{"type":"number","optional":true},"license_count_hard_limit":{"type":"bool","optional":true},"license_counting_type":{"type":"string","required":true},"license_rules":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_lightsail_domain":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_lightsail_instance":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","required":true},"blueprint_id":{"type":"string","required":true},"bundle_id":{"type":"string","required":true},"cpu_count":{"type":"number","computed":true},"created_at":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_address":{"type":"string","computed":true},"is_static_ip":{"type":"bool","computed":true},"key_pair_name":{"type":"string","optional":true},"name":{"type":"string","required":true},"private_ip_address":{"type":"string","computed":true},"public_ip_address":{"type":"string","computed":true},"ram_size":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true},"user_data":{"type":"string","optional":true},"username":{"type":"string","computed":true}}}},"aws_lightsail_key_pair":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"encrypted_fingerprint":{"type":"string","computed":true},"encrypted_private_key":{"type":"string","computed":true},"fingerprint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"pgp_key":{"type":"string","optional":true},"private_key":{"type":"string","computed":true},"public_key":{"type":"string","optional":true,"computed":true}}}},"aws_lightsail_static_ip":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","computed":true},"name":{"type":"string","required":true},"support_code":{"type":"string","computed":true}}}},"aws_lightsail_static_ip_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_name":{"type":"string","required":true},"ip_address":{"type":"string","computed":true},"static_ip_name":{"type":"string","required":true}}}},"aws_load_balancer_backend_server_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_port":{"type":"number","required":true},"load_balancer_name":{"type":"string","required":true},"policy_names":{"type":["set","string"],"optional":true}}}},"aws_load_balancer_listener_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"load_balancer_name":{"type":"string","required":true},"load_balancer_port":{"type":"number","required":true},"policy_names":{"type":["set","string"],"optional":true}}}},"aws_load_balancer_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"load_balancer_name":{"type":"string","required":true},"policy_name":{"type":"string","required":true},"policy_type_name":{"type":"string","required":true}},"block_types":{"policy_attribute":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","optional":true},"value":{"type":"string","optional":true}}}}}}},"aws_macie_member_account_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"member_account_id":{"type":"string","required":true}}}},"aws_macie_s3_bucket_association":{"version":0,"block":{"attributes":{"bucket_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"member_account_id":{"type":"string","optional":true},"prefix":{"type":"string","optional":true}},"block_types":{"classification_type":{"nesting_mode":"list","block":{"attributes":{"continuous":{"type":"string","optional":true},"one_time":{"type":"string","optional":true}}},"max_items":1}}}},"aws_main_route_table_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"original_route_table_id":{"type":"string","computed":true},"route_table_id":{"type":"string","required":true},"vpc_id":{"type":"string","required":true}}}},"aws_media_convert_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"pricing_plan":{"type":"string","optional":true},"status":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"reservation_plan_settings":{"nesting_mode":"list","block":{"attributes":{"commitment":{"type":"string","required":true},"renewal_type":{"type":"string","required":true},"reserved_slots":{"type":"number","required":true}}},"max_items":1}}}},"aws_media_package_channel":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"channel_id":{"type":"string","required":true},"description":{"type":"string","optional":true},"hls_ingest":{"type":["list",["object",{"ingest_endpoints":["list",["object",{"password":"string","url":"string","username":"string"}]]}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_media_store_container":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_media_store_container_policy":{"version":0,"block":{"attributes":{"container_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true}}}},"aws_mq_broker":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"broker_name":{"type":"string","required":true},"deployment_mode":{"type":"string","optional":true},"engine_type":{"type":"string","required":true},"engine_version":{"type":"string","required":true},"host_instance_type":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instances":{"type":["list",["object",{"console_url":"string","endpoints":["list","string"],"ip_address":"string"}]],"computed":true},"publicly_accessible":{"type":"bool","optional":true},"security_groups":{"type":["set","string"],"required":true},"subnet_ids":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"configuration":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"revision":{"type":"number","optional":true,"computed":true}}},"max_items":1},"encryption_options":{"nesting_mode":"list","block":{"attributes":{"kms_key_id":{"type":"string","optional":true,"computed":true},"use_aws_owned_key":{"type":"bool","optional":true}}},"max_items":1},"logs":{"nesting_mode":"list","block":{"attributes":{"audit":{"type":"bool","optional":true},"general":{"type":"bool","optional":true}}},"max_items":1},"maintenance_window_start_time":{"nesting_mode":"list","block":{"attributes":{"day_of_week":{"type":"string","required":true},"time_of_day":{"type":"string","required":true},"time_zone":{"type":"string","required":true}}},"max_items":1},"user":{"nesting_mode":"set","block":{"attributes":{"console_access":{"type":"bool","optional":true},"groups":{"type":["set","string"],"optional":true},"password":{"type":"string","required":true,"sensitive":true},"username":{"type":"string","required":true}}},"min_items":1}}}},"aws_mq_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"data":{"type":"string","required":true},"description":{"type":"string","optional":true},"engine_type":{"type":"string","required":true},"engine_version":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"latest_revision":{"type":"number","computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_msk_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bootstrap_brokers":{"type":"string","computed":true},"bootstrap_brokers_tls":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"current_version":{"type":"string","computed":true},"enhanced_monitoring":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"kafka_version":{"type":"string","required":true},"number_of_broker_nodes":{"type":"number","required":true},"tags":{"type":["map","string"],"optional":true},"zookeeper_connect_string":{"type":"string","computed":true}},"block_types":{"broker_node_group_info":{"nesting_mode":"list","block":{"attributes":{"az_distribution":{"type":"string","optional":true},"client_subnets":{"type":["list","string"],"required":true},"ebs_volume_size":{"type":"number","required":true},"instance_type":{"type":"string","required":true},"security_groups":{"type":["list","string"],"required":true}}},"min_items":1,"max_items":1},"client_authentication":{"nesting_mode":"list","block":{"block_types":{"tls":{"nesting_mode":"list","block":{"attributes":{"certificate_authority_arns":{"type":["set","string"],"optional":true}}},"max_items":1}}},"max_items":1},"configuration_info":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true},"revision":{"type":"number","required":true}}},"max_items":1},"encryption_info":{"nesting_mode":"list","block":{"attributes":{"encryption_at_rest_kms_key_arn":{"type":"string","optional":true,"computed":true}},"block_types":{"encryption_in_transit":{"nesting_mode":"list","block":{"attributes":{"client_broker":{"type":"string","optional":true},"in_cluster":{"type":"bool","optional":true}}},"max_items":1}}},"max_items":1},"logging_info":{"nesting_mode":"list","block":{"block_types":{"broker_logs":{"nesting_mode":"list","block":{"block_types":{"cloudwatch_logs":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true},"log_group":{"type":"string","optional":true}}},"max_items":1},"firehose":{"nesting_mode":"list","block":{"attributes":{"delivery_stream":{"type":"string","optional":true},"enabled":{"type":"bool","required":true}}},"max_items":1},"s3":{"nesting_mode":"list","block":{"attributes":{"bucket":{"type":"string","optional":true},"enabled":{"type":"bool","required":true},"prefix":{"type":"string","optional":true}}},"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1},"open_monitoring":{"nesting_mode":"list","block":{"block_types":{"prometheus":{"nesting_mode":"list","block":{"block_types":{"jmx_exporter":{"nesting_mode":"list","block":{"attributes":{"enabled_in_broker":{"type":"bool","required":true}}},"max_items":1},"node_exporter":{"nesting_mode":"list","block":{"attributes":{"enabled_in_broker":{"type":"bool","required":true}}},"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1}}}},"aws_msk_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"kafka_versions":{"type":["set","string"],"required":true},"latest_revision":{"type":"number","computed":true},"name":{"type":"string","required":true},"server_properties":{"type":"string","required":true}}}},"aws_nat_gateway":{"version":0,"block":{"attributes":{"allocation_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"subnet_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_neptune_cluster":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"backup_retention_period":{"type":"number","optional":true},"cluster_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier_prefix":{"type":"string","optional":true,"computed":true},"cluster_members":{"type":["set","string"],"computed":true},"cluster_resource_id":{"type":"string","computed":true},"deletion_protection":{"type":"bool","optional":true},"enable_cloudwatch_logs_exports":{"type":["set","string"],"optional":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"hosted_zone_id":{"type":"string","computed":true},"iam_database_authentication_enabled":{"type":"bool","optional":true},"iam_roles":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true,"computed":true},"neptune_cluster_parameter_group_name":{"type":"string","optional":true},"neptune_subnet_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"reader_endpoint":{"type":"string","computed":true},"replication_source_identifier":{"type":"string","optional":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_identifier":{"type":"string","optional":true},"storage_encrypted":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_neptune_cluster_instance":{"version":0,"block":{"attributes":{"address":{"type":"string","computed":true},"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"cluster_identifier":{"type":"string","required":true},"dbi_resource_id":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"instance_class":{"type":"string","required":true},"kms_key_arn":{"type":"string","computed":true},"neptune_parameter_group_name":{"type":"string","optional":true},"neptune_subnet_group_name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"promotion_tier":{"type":"number","optional":true},"publicly_accessible":{"type":"bool","optional":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true},"writer":{"type":"bool","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_neptune_cluster_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_neptune_cluster_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zones":{"type":["list","string"],"computed":true},"db_cluster_identifier":{"type":"string","required":true},"db_cluster_snapshot_arn":{"type":"string","computed":true},"db_cluster_snapshot_identifier":{"type":"string","required":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_type":{"type":"string","computed":true},"source_db_cluster_snapshot_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_neptune_event_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_aws_id":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"event_categories":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"sns_topic_arn":{"type":"string","required":true},"source_ids":{"type":["set","string"],"optional":true},"source_type":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_neptune_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_neptune_subnet_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_network_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"egress":{"type":["set",["object",{"action":"string","cidr_block":"string","from_port":"number","icmp_code":"number","icmp_type":"number","ipv6_cidr_block":"string","protocol":"string","rule_no":"number","to_port":"number"}]],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ingress":{"type":["set",["object",{"action":"string","cidr_block":"string","from_port":"number","icmp_code":"number","icmp_type":"number","ipv6_cidr_block":"string","protocol":"string","rule_no":"number","to_port":"number"}]],"optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}}}},"aws_network_acl_rule":{"version":0,"block":{"attributes":{"cidr_block":{"type":"string","optional":true},"egress":{"type":"bool","optional":true},"from_port":{"type":"number","optional":true},"icmp_code":{"type":"string","optional":true},"icmp_type":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block":{"type":"string","optional":true},"network_acl_id":{"type":"string","required":true},"protocol":{"type":"string","required":true},"rule_action":{"type":"string","required":true},"rule_number":{"type":"number","required":true},"to_port":{"type":"number","optional":true}}}},"aws_network_interface":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"mac_address":{"type":"string","computed":true},"outpost_arn":{"type":"string","computed":true},"private_dns_name":{"type":"string","computed":true},"private_ip":{"type":"string","optional":true,"computed":true},"private_ips":{"type":["set","string"],"optional":true,"computed":true},"private_ips_count":{"type":"number","optional":true,"computed":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"source_dest_check":{"type":"bool","optional":true},"subnet_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"attachment":{"nesting_mode":"set","block":{"attributes":{"attachment_id":{"type":"string","computed":true},"device_index":{"type":"number","required":true},"instance":{"type":"string","required":true}}}}}}},"aws_network_interface_attachment":{"version":0,"block":{"attributes":{"attachment_id":{"type":"string","computed":true},"device_index":{"type":"number","required":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","required":true},"network_interface_id":{"type":"string","required":true},"status":{"type":"string","computed":true}}}},"aws_network_interface_sg_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","required":true},"security_group_id":{"type":"string","required":true}}}},"aws_opsworks_application":{"version":0,"block":{"attributes":{"auto_bundle_on_deploy":{"type":"string","optional":true},"aws_flow_ruby_settings":{"type":"string","optional":true},"data_source_arn":{"type":"string","optional":true},"data_source_database_name":{"type":"string","optional":true},"data_source_type":{"type":"string","optional":true},"description":{"type":"string","optional":true},"document_root":{"type":"string","optional":true},"domains":{"type":["list","string"],"optional":true},"enable_ssl":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"rails_env":{"type":"string","optional":true},"short_name":{"type":"string","optional":true,"computed":true},"stack_id":{"type":"string","required":true},"type":{"type":"string","required":true}},"block_types":{"app_source":{"nesting_mode":"list","block":{"attributes":{"password":{"type":"string","optional":true,"sensitive":true},"revision":{"type":"string","optional":true},"ssh_key":{"type":"string","optional":true,"sensitive":true},"type":{"type":"string","required":true},"url":{"type":"string","optional":true},"username":{"type":"string","optional":true}}}},"environment":{"nesting_mode":"set","block":{"attributes":{"key":{"type":"string","required":true},"secure":{"type":"bool","optional":true},"value":{"type":"string","required":true}}}},"ssl_configuration":{"nesting_mode":"list","block":{"attributes":{"certificate":{"type":"string","required":true},"chain":{"type":"string","optional":true},"private_key":{"type":"string","required":true,"sensitive":true}}}}}}},"aws_opsworks_custom_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","required":true},"short_name":{"type":"string","required":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_ganglia_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"password":{"type":"string","required":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"url":{"type":"string","optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true},"username":{"type":"string","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_haproxy_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"healthcheck_method":{"type":"string","optional":true},"healthcheck_url":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"stats_enabled":{"type":"bool","optional":true},"stats_password":{"type":"string","required":true},"stats_url":{"type":"string","optional":true},"stats_user":{"type":"string","optional":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_instance":{"version":0,"block":{"attributes":{"agent_version":{"type":"string","optional":true},"ami_id":{"type":"string","optional":true,"computed":true},"architecture":{"type":"string","optional":true},"auto_scaling_type":{"type":"string","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"created_at":{"type":"string","optional":true,"computed":true},"delete_ebs":{"type":"bool","optional":true},"delete_eip":{"type":"bool","optional":true},"ebs_optimized":{"type":"bool","optional":true},"ec2_instance_id":{"type":"string","computed":true},"ecs_cluster_arn":{"type":"string","optional":true,"computed":true},"elastic_ip":{"type":"string","optional":true,"computed":true},"hostname":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"infrastructure_class":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_profile_arn":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","optional":true},"last_service_error_id":{"type":"string","optional":true,"computed":true},"layer_ids":{"type":["list","string"],"required":true},"os":{"type":"string","optional":true,"computed":true},"platform":{"type":"string","optional":true,"computed":true},"private_dns":{"type":"string","optional":true,"computed":true},"private_ip":{"type":"string","optional":true,"computed":true},"public_dns":{"type":"string","optional":true,"computed":true},"public_ip":{"type":"string","optional":true,"computed":true},"registered_by":{"type":"string","optional":true,"computed":true},"reported_agent_version":{"type":"string","optional":true,"computed":true},"reported_os_family":{"type":"string","optional":true,"computed":true},"reported_os_name":{"type":"string","optional":true,"computed":true},"reported_os_version":{"type":"string","optional":true,"computed":true},"root_device_type":{"type":"string","optional":true,"computed":true},"root_device_volume_id":{"type":"string","optional":true,"computed":true},"security_group_ids":{"type":["list","string"],"optional":true,"computed":true},"ssh_host_dsa_key_fingerprint":{"type":"string","optional":true,"computed":true},"ssh_host_rsa_key_fingerprint":{"type":"string","optional":true,"computed":true},"ssh_key_name":{"type":"string","optional":true,"computed":true},"stack_id":{"type":"string","required":true},"state":{"type":"string","optional":true},"status":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tenancy":{"type":"string","optional":true,"computed":true},"virtualization_type":{"type":"string","optional":true,"computed":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"iops":{"type":"number","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"virtual_name":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"iops":{"type":"number","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_opsworks_java_app_layer":{"version":0,"block":{"attributes":{"app_server":{"type":"string","optional":true},"app_server_version":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"jvm_options":{"type":"string","optional":true},"jvm_type":{"type":"string","optional":true},"jvm_version":{"type":"string","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_memcached_layer":{"version":0,"block":{"attributes":{"allocated_memory":{"type":"number","optional":true},"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_mysql_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"root_password":{"type":"string","optional":true},"root_password_on_all_instances":{"type":"bool","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_nodejs_app_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"nodejs_version":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_permission":{"version":0,"block":{"attributes":{"allow_ssh":{"type":"bool","optional":true,"computed":true},"allow_sudo":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"level":{"type":"string","optional":true,"computed":true},"stack_id":{"type":"string","optional":true,"computed":true},"user_arn":{"type":"string","required":true}}}},"aws_opsworks_php_app_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_rails_app_layer":{"version":0,"block":{"attributes":{"app_server":{"type":"string","optional":true},"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"bundler_version":{"type":"string","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"manage_bundler":{"type":"bool","optional":true},"name":{"type":"string","optional":true},"passenger_version":{"type":"string","optional":true},"ruby_version":{"type":"string","optional":true},"rubygems_version":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_rds_db_instance":{"version":0,"block":{"attributes":{"db_password":{"type":"string","required":true,"sensitive":true},"db_user":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"rds_db_instance_arn":{"type":"string","required":true},"stack_id":{"type":"string","required":true}}}},"aws_opsworks_stack":{"version":0,"block":{"attributes":{"agent_version":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"berkshelf_version":{"type":"string","optional":true},"color":{"type":"string","optional":true},"configuration_manager_name":{"type":"string","optional":true},"configuration_manager_version":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"default_availability_zone":{"type":"string","optional":true,"computed":true},"default_instance_profile_arn":{"type":"string","required":true},"default_os":{"type":"string","optional":true},"default_root_device_type":{"type":"string","optional":true},"default_ssh_key_name":{"type":"string","optional":true},"default_subnet_id":{"type":"string","optional":true,"computed":true},"hostname_theme":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"manage_berkshelf":{"type":"bool","optional":true},"name":{"type":"string","required":true},"region":{"type":"string","required":true},"service_role_arn":{"type":"string","required":true},"stack_endpoint":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"use_custom_cookbooks":{"type":"bool","optional":true},"use_opsworks_security_groups":{"type":"bool","optional":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"custom_cookbooks_source":{"nesting_mode":"list","block":{"attributes":{"password":{"type":"string","optional":true,"sensitive":true},"revision":{"type":"string","optional":true},"ssh_key":{"type":"string","optional":true,"sensitive":true},"type":{"type":"string","required":true},"url":{"type":"string","required":true},"username":{"type":"string","optional":true}}}}}}},"aws_opsworks_static_web_layer":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_assign_elastic_ips":{"type":"bool","optional":true},"auto_assign_public_ips":{"type":"bool","optional":true},"auto_healing":{"type":"bool","optional":true},"custom_configure_recipes":{"type":["list","string"],"optional":true},"custom_deploy_recipes":{"type":["list","string"],"optional":true},"custom_instance_profile_arn":{"type":"string","optional":true},"custom_json":{"type":"string","optional":true},"custom_security_group_ids":{"type":["set","string"],"optional":true},"custom_setup_recipes":{"type":["list","string"],"optional":true},"custom_shutdown_recipes":{"type":["list","string"],"optional":true},"custom_undeploy_recipes":{"type":["list","string"],"optional":true},"drain_elb_on_shutdown":{"type":"bool","optional":true},"elastic_load_balancer":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"install_updates_on_boot":{"type":"bool","optional":true},"instance_shutdown_timeout":{"type":"number","optional":true},"name":{"type":"string","optional":true},"stack_id":{"type":"string","required":true},"system_packages":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true},"use_ebs_optimized_instances":{"type":"bool","optional":true}},"block_types":{"ebs_volume":{"nesting_mode":"set","block":{"attributes":{"encrypted":{"type":"bool","optional":true},"iops":{"type":"number","optional":true},"mount_point":{"type":"string","required":true},"number_of_disks":{"type":"number","required":true},"raid_level":{"type":"string","optional":true},"size":{"type":"number","required":true},"type":{"type":"string","optional":true}}}}}}},"aws_opsworks_user_profile":{"version":0,"block":{"attributes":{"allow_self_management":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"ssh_public_key":{"type":"string","optional":true},"ssh_username":{"type":"string","required":true},"user_arn":{"type":"string","required":true}}}},"aws_organizations_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"email":{"type":"string","required":true},"iam_user_access_to_billing":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"joined_method":{"type":"string","computed":true},"joined_timestamp":{"type":"string","computed":true},"name":{"type":"string","required":true},"parent_id":{"type":"string","optional":true,"computed":true},"role_name":{"type":"string","optional":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_organizations_organization":{"version":0,"block":{"attributes":{"accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string","status":"string"}]],"computed":true},"arn":{"type":"string","computed":true},"aws_service_access_principals":{"type":["set","string"],"optional":true},"enabled_policy_types":{"type":["set","string"],"optional":true},"feature_set":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"master_account_arn":{"type":"string","computed":true},"master_account_email":{"type":"string","computed":true},"master_account_id":{"type":"string","computed":true},"non_master_accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string","status":"string"}]],"computed":true},"roots":{"type":["list",["object",{"arn":"string","id":"string","name":"string","policy_types":["list",["object",{"status":"string","type":"string"}]]}]],"computed":true}}}},"aws_organizations_organizational_unit":{"version":0,"block":{"attributes":{"accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string"}]],"computed":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"parent_id":{"type":"string","required":true}}}},"aws_organizations_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"content":{"type":"string","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"type":{"type":"string","optional":true}}}},"aws_organizations_policy_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy_id":{"type":"string","required":true},"target_id":{"type":"string","required":true}}}},"aws_pinpoint_adm_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"client_id":{"type":"string","required":true,"sensitive":true},"client_secret":{"type":"string","required":true,"sensitive":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_pinpoint_apns_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"bundle_id":{"type":"string","optional":true,"sensitive":true},"certificate":{"type":"string","optional":true,"sensitive":true},"default_authentication_method":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"team_id":{"type":"string","optional":true,"sensitive":true},"token_key":{"type":"string","optional":true,"sensitive":true},"token_key_id":{"type":"string","optional":true,"sensitive":true}}}},"aws_pinpoint_apns_sandbox_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"bundle_id":{"type":"string","optional":true,"sensitive":true},"certificate":{"type":"string","optional":true,"sensitive":true},"default_authentication_method":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"team_id":{"type":"string","optional":true,"sensitive":true},"token_key":{"type":"string","optional":true,"sensitive":true},"token_key_id":{"type":"string","optional":true,"sensitive":true}}}},"aws_pinpoint_apns_voip_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"bundle_id":{"type":"string","optional":true,"sensitive":true},"certificate":{"type":"string","optional":true,"sensitive":true},"default_authentication_method":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"team_id":{"type":"string","optional":true,"sensitive":true},"token_key":{"type":"string","optional":true,"sensitive":true},"token_key_id":{"type":"string","optional":true,"sensitive":true}}}},"aws_pinpoint_apns_voip_sandbox_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"bundle_id":{"type":"string","optional":true,"sensitive":true},"certificate":{"type":"string","optional":true,"sensitive":true},"default_authentication_method":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"private_key":{"type":"string","optional":true,"sensitive":true},"team_id":{"type":"string","optional":true,"sensitive":true},"token_key":{"type":"string","optional":true,"sensitive":true},"token_key_id":{"type":"string","optional":true,"sensitive":true}}}},"aws_pinpoint_app":{"version":0,"block":{"attributes":{"application_id":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"campaign_hook":{"nesting_mode":"list","block":{"attributes":{"lambda_function_name":{"type":"string","optional":true},"mode":{"type":"string","optional":true},"web_url":{"type":"string","optional":true}}},"max_items":1},"limits":{"nesting_mode":"list","block":{"attributes":{"daily":{"type":"number","optional":true},"maximum_duration":{"type":"number","optional":true},"messages_per_second":{"type":"number","optional":true},"total":{"type":"number","optional":true}}},"max_items":1},"quiet_time":{"nesting_mode":"list","block":{"attributes":{"end":{"type":"string","optional":true},"start":{"type":"string","optional":true}}},"max_items":1}}}},"aws_pinpoint_baidu_channel":{"version":0,"block":{"attributes":{"api_key":{"type":"string","required":true,"sensitive":true},"application_id":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"secret_key":{"type":"string","required":true,"sensitive":true}}}},"aws_pinpoint_email_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"from_address":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"identity":{"type":"string","required":true},"messages_per_second":{"type":"number","computed":true},"role_arn":{"type":"string","required":true}}}},"aws_pinpoint_event_stream":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"destination_stream_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"role_arn":{"type":"string","required":true}}}},"aws_pinpoint_gcm_channel":{"version":0,"block":{"attributes":{"api_key":{"type":"string","required":true,"sensitive":true},"application_id":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_pinpoint_sms_channel":{"version":0,"block":{"attributes":{"application_id":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"promotional_messages_per_second":{"type":"number","computed":true},"sender_id":{"type":"string","optional":true},"short_code":{"type":"string","optional":true},"transactional_messages_per_second":{"type":"number","computed":true}}}},"aws_placement_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"placement_group_id":{"type":"string","computed":true},"strategy":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_proxy_protocol_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_ports":{"type":["set","string"],"required":true},"load_balancer":{"type":"string","required":true}}}},"aws_qldb_ledger":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deletion_protection":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_quicksight_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_account_id":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"group_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"namespace":{"type":"string","optional":true}}}},"aws_quicksight_user":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_account_id":{"type":"string","optional":true,"computed":true},"email":{"type":"string","required":true},"iam_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"identity_type":{"type":"string","required":true},"namespace":{"type":"string","optional":true},"session_name":{"type":"string","optional":true},"user_name":{"type":"string","optional":true},"user_role":{"type":"string","required":true}}}},"aws_ram_principal_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"principal":{"type":"string","required":true},"resource_share_arn":{"type":"string","required":true}}}},"aws_ram_resource_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_arn":{"type":"string","required":true},"resource_share_arn":{"type":"string","required":true}}}},"aws_ram_resource_share":{"version":0,"block":{"attributes":{"allow_external_principals":{"type":"bool","optional":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_ram_resource_share_accepter":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"invitation_arn":{"type":"string","computed":true},"receiver_account_id":{"type":"string","computed":true},"resources":{"type":["list","string"],"computed":true},"sender_account_id":{"type":"string","computed":true},"share_arn":{"type":"string","required":true},"share_id":{"type":"string","computed":true},"share_name":{"type":"string","computed":true},"status":{"type":"string","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_rds_cluster":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"optional":true,"computed":true},"backtrack_window":{"type":"number","optional":true},"backup_retention_period":{"type":"number","optional":true},"cluster_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier_prefix":{"type":"string","optional":true,"computed":true},"cluster_members":{"type":["set","string"],"optional":true,"computed":true},"cluster_resource_id":{"type":"string","computed":true},"copy_tags_to_snapshot":{"type":"bool","optional":true},"database_name":{"type":"string","optional":true,"computed":true},"db_cluster_parameter_group_name":{"type":"string","optional":true,"computed":true},"db_subnet_group_name":{"type":"string","optional":true,"computed":true},"deletion_protection":{"type":"bool","optional":true},"enable_http_endpoint":{"type":"bool","optional":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"optional":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_mode":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"global_cluster_identifier":{"type":"string","optional":true},"hosted_zone_id":{"type":"string","computed":true},"iam_database_authentication_enabled":{"type":"bool","optional":true},"iam_roles":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"master_password":{"type":"string","optional":true,"sensitive":true},"master_username":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"reader_endpoint":{"type":"string","computed":true},"replication_source_identifier":{"type":"string","optional":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_identifier":{"type":"string","optional":true},"source_region":{"type":"string","optional":true},"storage_encrypted":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"s3_import":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","required":true},"bucket_prefix":{"type":"string","optional":true},"ingestion_role":{"type":"string","required":true},"source_engine":{"type":"string","required":true},"source_engine_version":{"type":"string","required":true}}},"max_items":1},"scaling_configuration":{"nesting_mode":"list","block":{"attributes":{"auto_pause":{"type":"bool","optional":true},"max_capacity":{"type":"number","optional":true},"min_capacity":{"type":"number","optional":true},"seconds_until_auto_pause":{"type":"number","optional":true},"timeout_action":{"type":"string","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_rds_cluster_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cluster_endpoint_identifier":{"type":"string","required":true},"cluster_identifier":{"type":"string","required":true},"custom_endpoint_type":{"type":"string","required":true},"endpoint":{"type":"string","computed":true},"excluded_members":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"static_members":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_rds_cluster_instance":{"version":0,"block":{"attributes":{"apply_immediately":{"type":"bool","optional":true,"computed":true},"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"ca_cert_identifier":{"type":"string","optional":true,"computed":true},"cluster_identifier":{"type":"string","required":true},"copy_tags_to_snapshot":{"type":"bool","optional":true},"db_parameter_group_name":{"type":"string","optional":true,"computed":true},"db_subnet_group_name":{"type":"string","optional":true,"computed":true},"dbi_resource_id":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","optional":true},"engine_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"instance_class":{"type":"string","required":true},"kms_key_id":{"type":"string","computed":true},"monitoring_interval":{"type":"number","optional":true},"monitoring_role_arn":{"type":"string","optional":true,"computed":true},"performance_insights_enabled":{"type":"bool","optional":true,"computed":true},"performance_insights_kms_key_id":{"type":"string","optional":true,"computed":true},"port":{"type":"number","computed":true},"preferred_backup_window":{"type":"string","optional":true,"computed":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"promotion_tier":{"type":"number","optional":true},"publicly_accessible":{"type":"bool","optional":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true},"writer":{"type":"bool","computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_rds_cluster_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"apply_method":{"type":"string","optional":true},"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_rds_global_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"database_name":{"type":"string","optional":true},"deletion_protection":{"type":"bool","optional":true},"engine":{"type":"string","optional":true,"computed":true},"engine_version":{"type":"string","optional":true,"computed":true},"force_destroy":{"type":"bool","optional":true},"global_cluster_identifier":{"type":"string","required":true},"global_cluster_members":{"type":["set",["object",{"db_cluster_arn":"string","is_writer":"bool"}]],"computed":true},"global_cluster_resource_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"source_db_cluster_identifier":{"type":"string","optional":true,"computed":true},"storage_encrypted":{"type":"bool","optional":true}}}},"aws_redshift_cluster":{"version":0,"block":{"attributes":{"allow_version_upgrade":{"type":"bool","optional":true},"arn":{"type":"string","computed":true},"automated_snapshot_retention_period":{"type":"number","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"cluster_identifier":{"type":"string","required":true},"cluster_parameter_group_name":{"type":"string","optional":true,"computed":true},"cluster_public_key":{"type":"string","optional":true,"computed":true},"cluster_revision_number":{"type":"string","optional":true,"computed":true},"cluster_security_groups":{"type":["set","string"],"optional":true,"computed":true},"cluster_subnet_group_name":{"type":"string","optional":true,"computed":true},"cluster_type":{"type":"string","optional":true,"computed":true},"cluster_version":{"type":"string","optional":true},"database_name":{"type":"string","optional":true,"computed":true},"dns_name":{"type":"string","computed":true},"elastic_ip":{"type":"string","optional":true},"encrypted":{"type":"bool","optional":true},"endpoint":{"type":"string","optional":true,"computed":true},"enhanced_vpc_routing":{"type":"bool","optional":true,"computed":true},"final_snapshot_identifier":{"type":"string","optional":true},"iam_roles":{"type":["set","string"],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"master_password":{"type":"string","optional":true,"sensitive":true},"master_username":{"type":"string","optional":true},"node_type":{"type":"string","required":true},"number_of_nodes":{"type":"number","optional":true},"owner_account":{"type":"string","optional":true},"port":{"type":"number","optional":true},"preferred_maintenance_window":{"type":"string","optional":true,"computed":true},"publicly_accessible":{"type":"bool","optional":true},"skip_final_snapshot":{"type":"bool","optional":true},"snapshot_cluster_identifier":{"type":"string","optional":true},"snapshot_identifier":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true}},"block_types":{"logging":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","optional":true,"computed":true},"enable":{"type":"bool","required":true},"s3_key_prefix":{"type":"string","optional":true,"computed":true}}},"max_items":1},"snapshot_copy":{"nesting_mode":"list","block":{"attributes":{"destination_region":{"type":"string","required":true},"grant_name":{"type":"string","optional":true},"retention_period":{"type":"number","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_redshift_event_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_aws_id":{"type":"string","computed":true},"enabled":{"type":"bool","optional":true},"event_categories":{"type":["set","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"severity":{"type":"string","optional":true},"sns_topic_arn":{"type":"string","required":true},"source_ids":{"type":["set","string"],"optional":true},"source_type":{"type":"string","optional":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_redshift_parameter_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"family":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_redshift_security_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"ingress":{"nesting_mode":"set","block":{"attributes":{"cidr":{"type":"string","optional":true},"security_group_name":{"type":"string","optional":true,"computed":true},"security_group_owner_id":{"type":"string","optional":true,"computed":true}}},"min_items":1}}}},"aws_redshift_snapshot_copy_grant":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"snapshot_copy_grant_name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_redshift_snapshot_schedule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"definitions":{"type":["set","string"],"required":true},"description":{"type":"string","optional":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"identifier":{"type":"string","optional":true,"computed":true},"identifier_prefix":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_redshift_snapshot_schedule_association":{"version":0,"block":{"attributes":{"cluster_identifier":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"schedule_identifier":{"type":"string","required":true}}}},"aws_redshift_subnet_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"subnet_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_resourcegroups_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"resource_query":{"nesting_mode":"list","block":{"attributes":{"query":{"type":"string","required":true},"type":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}}},"aws_route":{"version":0,"block":{"attributes":{"destination_cidr_block":{"type":"string","optional":true},"destination_ipv6_cidr_block":{"type":"string","optional":true},"destination_prefix_list_id":{"type":"string","computed":true},"egress_only_gateway_id":{"type":"string","optional":true,"computed":true},"gateway_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true,"computed":true},"instance_owner_id":{"type":"string","computed":true},"nat_gateway_id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","optional":true,"computed":true},"origin":{"type":"string","computed":true},"route_table_id":{"type":"string","required":true},"state":{"type":"string","computed":true},"transit_gateway_id":{"type":"string","optional":true},"vpc_peering_connection_id":{"type":"string","optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_route53_delegation_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name_servers":{"type":["list","string"],"computed":true},"reference_name":{"type":"string","optional":true}}}},"aws_route53_health_check":{"version":0,"block":{"attributes":{"child_health_threshold":{"type":"number","optional":true},"child_healthchecks":{"type":["set","string"],"optional":true},"cloudwatch_alarm_name":{"type":"string","optional":true},"cloudwatch_alarm_region":{"type":"string","optional":true},"disabled":{"type":"bool","optional":true},"enable_sni":{"type":"bool","optional":true,"computed":true},"failure_threshold":{"type":"number","optional":true},"fqdn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"insufficient_data_health_status":{"type":"string","optional":true},"invert_healthcheck":{"type":"bool","optional":true},"ip_address":{"type":"string","optional":true},"measure_latency":{"type":"bool","optional":true},"port":{"type":"number","optional":true},"reference_name":{"type":"string","optional":true},"regions":{"type":["set","string"],"optional":true},"request_interval":{"type":"number","optional":true},"resource_path":{"type":"string","optional":true},"search_string":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","required":true}}}},"aws_route53_query_log":{"version":0,"block":{"attributes":{"cloudwatch_log_group_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","required":true}}}},"aws_route53_record":{"version":2,"block":{"attributes":{"allow_overwrite":{"type":"bool","optional":true,"computed":true},"fqdn":{"type":"string","computed":true},"health_check_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"multivalue_answer_routing_policy":{"type":"bool","optional":true},"name":{"type":"string","required":true},"records":{"type":["set","string"],"optional":true},"set_identifier":{"type":"string","optional":true},"ttl":{"type":"number","optional":true},"type":{"type":"string","required":true},"zone_id":{"type":"string","required":true}},"block_types":{"alias":{"nesting_mode":"set","block":{"attributes":{"evaluate_target_health":{"type":"bool","required":true},"name":{"type":"string","required":true},"zone_id":{"type":"string","required":true}}}},"failover_routing_policy":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}}},"geolocation_routing_policy":{"nesting_mode":"list","block":{"attributes":{"continent":{"type":"string","optional":true},"country":{"type":"string","optional":true},"subdivision":{"type":"string","optional":true}}}},"latency_routing_policy":{"nesting_mode":"list","block":{"attributes":{"region":{"type":"string","required":true}}}},"weighted_routing_policy":{"nesting_mode":"list","block":{"attributes":{"weight":{"type":"number","required":true}}}}}}},"aws_route53_resolver_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"direction":{"type":"string","required":true},"host_vpc_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"security_group_ids":{"type":["set","string"],"required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"ip_address":{"nesting_mode":"set","block":{"attributes":{"ip":{"type":"string","optional":true,"computed":true},"ip_id":{"type":"string","computed":true},"subnet_id":{"type":"string","required":true}}},"min_items":2,"max_items":10},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_route53_resolver_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"resolver_endpoint_id":{"type":"string","optional":true},"rule_type":{"type":"string","required":true},"share_status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"target_ip":{"nesting_mode":"set","block":{"attributes":{"ip":{"type":"string","required":true},"port":{"type":"number","optional":true}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_route53_resolver_rule_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"resolver_rule_id":{"type":"string","required":true},"vpc_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_route53_vpc_association_authorization":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"vpc_id":{"type":"string","required":true},"vpc_region":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","required":true}}}},"aws_route53_zone":{"version":0,"block":{"attributes":{"comment":{"type":"string","optional":true},"delegation_set_id":{"type":"string","optional":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"name_servers":{"type":["list","string"],"computed":true},"tags":{"type":["map","string"],"optional":true},"zone_id":{"type":"string","computed":true}},"block_types":{"vpc":{"nesting_mode":"set","block":{"attributes":{"vpc_id":{"type":"string","required":true},"vpc_region":{"type":"string","optional":true,"computed":true}}}}}}},"aws_route53_zone_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"owning_account":{"type":"string","computed":true},"vpc_id":{"type":"string","required":true},"vpc_region":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","required":true}}}},"aws_route_table":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"propagating_vgws":{"type":["set","string"],"optional":true,"computed":true},"route":{"type":["set",["object",{"cidr_block":"string","egress_only_gateway_id":"string","gateway_id":"string","instance_id":"string","ipv6_cidr_block":"string","nat_gateway_id":"string","network_interface_id":"string","transit_gateway_id":"string","vpc_peering_connection_id":"string"}]],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}}}},"aws_route_table_association":{"version":0,"block":{"attributes":{"gateway_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"route_table_id":{"type":"string","required":true},"subnet_id":{"type":"string","optional":true}}}},"aws_s3_access_point":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"bucket":{"type":"string","required":true},"domain_name":{"type":"string","computed":true},"has_public_access_policy":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"network_origin":{"type":"string","computed":true},"policy":{"type":"string","optional":true}},"block_types":{"public_access_block_configuration":{"nesting_mode":"list","block":{"attributes":{"block_public_acls":{"type":"bool","optional":true},"block_public_policy":{"type":"bool","optional":true},"ignore_public_acls":{"type":"bool","optional":true},"restrict_public_buckets":{"type":"bool","optional":true}}},"max_items":1},"vpc_configuration":{"nesting_mode":"list","block":{"attributes":{"vpc_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_s3_account_public_access_block":{"version":0,"block":{"attributes":{"account_id":{"type":"string","optional":true,"computed":true},"block_public_acls":{"type":"bool","optional":true},"block_public_policy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"ignore_public_acls":{"type":"bool","optional":true},"restrict_public_buckets":{"type":"bool","optional":true}}}},"aws_s3_bucket":{"version":0,"block":{"attributes":{"acceleration_status":{"type":"string","optional":true,"computed":true},"acl":{"type":"string","optional":true},"arn":{"type":"string","optional":true,"computed":true},"bucket":{"type":"string","optional":true,"computed":true},"bucket_domain_name":{"type":"string","computed":true},"bucket_prefix":{"type":"string","optional":true},"bucket_regional_domain_name":{"type":"string","computed":true},"force_destroy":{"type":"bool","optional":true},"hosted_zone_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","optional":true},"region":{"type":"string","computed":true},"request_payer":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"website_domain":{"type":"string","optional":true,"computed":true},"website_endpoint":{"type":"string","optional":true,"computed":true}},"block_types":{"cors_rule":{"nesting_mode":"list","block":{"attributes":{"allowed_headers":{"type":["list","string"],"optional":true},"allowed_methods":{"type":["list","string"],"required":true},"allowed_origins":{"type":["list","string"],"required":true},"expose_headers":{"type":["list","string"],"optional":true},"max_age_seconds":{"type":"number","optional":true}}}},"grant":{"nesting_mode":"set","block":{"attributes":{"id":{"type":"string","optional":true},"permissions":{"type":["set","string"],"required":true},"type":{"type":"string","required":true},"uri":{"type":"string","optional":true}}}},"lifecycle_rule":{"nesting_mode":"list","block":{"attributes":{"abort_incomplete_multipart_upload_days":{"type":"number","optional":true},"enabled":{"type":"bool","required":true},"id":{"type":"string","optional":true,"computed":true},"prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"expiration":{"nesting_mode":"list","block":{"attributes":{"date":{"type":"string","optional":true},"days":{"type":"number","optional":true},"expired_object_delete_marker":{"type":"bool","optional":true}}},"max_items":1},"noncurrent_version_expiration":{"nesting_mode":"list","block":{"attributes":{"days":{"type":"number","optional":true}}},"max_items":1},"noncurrent_version_transition":{"nesting_mode":"set","block":{"attributes":{"days":{"type":"number","optional":true},"storage_class":{"type":"string","required":true}}}},"transition":{"nesting_mode":"set","block":{"attributes":{"date":{"type":"string","optional":true},"days":{"type":"number","optional":true},"storage_class":{"type":"string","required":true}}}}}}},"logging":{"nesting_mode":"set","block":{"attributes":{"target_bucket":{"type":"string","required":true},"target_prefix":{"type":"string","optional":true}}}},"object_lock_configuration":{"nesting_mode":"list","block":{"attributes":{"object_lock_enabled":{"type":"string","required":true}},"block_types":{"rule":{"nesting_mode":"list","block":{"block_types":{"default_retention":{"nesting_mode":"list","block":{"attributes":{"days":{"type":"number","optional":true},"mode":{"type":"string","required":true},"years":{"type":"number","optional":true}}},"min_items":1,"max_items":1}}},"max_items":1}}},"max_items":1},"replication_configuration":{"nesting_mode":"list","block":{"attributes":{"role":{"type":"string","required":true}},"block_types":{"rules":{"nesting_mode":"set","block":{"attributes":{"id":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"priority":{"type":"number","optional":true},"status":{"type":"string","required":true}},"block_types":{"destination":{"nesting_mode":"list","block":{"attributes":{"account_id":{"type":"string","optional":true},"bucket":{"type":"string","required":true},"replica_kms_key_id":{"type":"string","optional":true},"storage_class":{"type":"string","optional":true}},"block_types":{"access_control_translation":{"nesting_mode":"list","block":{"attributes":{"owner":{"type":"string","required":true}}},"max_items":1}}},"min_items":1,"max_items":1},"filter":{"nesting_mode":"list","block":{"attributes":{"prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}},"max_items":1},"source_selection_criteria":{"nesting_mode":"list","block":{"block_types":{"sse_kms_encrypted_objects":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","required":true}}},"max_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"server_side_encryption_configuration":{"nesting_mode":"list","block":{"block_types":{"rule":{"nesting_mode":"list","block":{"block_types":{"apply_server_side_encryption_by_default":{"nesting_mode":"list","block":{"attributes":{"kms_master_key_id":{"type":"string","optional":true},"sse_algorithm":{"type":"string","required":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1},"versioning":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","optional":true},"mfa_delete":{"type":"bool","optional":true}}},"max_items":1},"website":{"nesting_mode":"list","block":{"attributes":{"error_document":{"type":"string","optional":true},"index_document":{"type":"string","optional":true},"redirect_all_requests_to":{"type":"string","optional":true},"routing_rules":{"type":"string","optional":true}}},"max_items":1}}}},"aws_s3_bucket_analytics_configuration":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"filter":{"nesting_mode":"list","block":{"attributes":{"prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}},"max_items":1},"storage_class_analysis":{"nesting_mode":"list","block":{"block_types":{"data_export":{"nesting_mode":"list","block":{"attributes":{"output_schema_version":{"type":"string","optional":true}},"block_types":{"destination":{"nesting_mode":"list","block":{"block_types":{"s3_bucket_destination":{"nesting_mode":"list","block":{"attributes":{"bucket_account_id":{"type":"string","optional":true},"bucket_arn":{"type":"string","required":true},"format":{"type":"string","optional":true},"prefix":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1}}},"max_items":1}}}},"aws_s3_bucket_inventory":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"included_object_versions":{"type":"string","required":true},"name":{"type":"string","required":true},"optional_fields":{"type":["set","string"],"optional":true}},"block_types":{"destination":{"nesting_mode":"list","block":{"block_types":{"bucket":{"nesting_mode":"list","block":{"attributes":{"account_id":{"type":"string","optional":true},"bucket_arn":{"type":"string","required":true},"format":{"type":"string","required":true},"prefix":{"type":"string","optional":true}},"block_types":{"encryption":{"nesting_mode":"list","block":{"block_types":{"sse_kms":{"nesting_mode":"list","block":{"attributes":{"key_id":{"type":"string","required":true}}},"max_items":1},"sse_s3":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1}}},"min_items":1,"max_items":1}}},"min_items":1,"max_items":1},"filter":{"nesting_mode":"list","block":{"attributes":{"prefix":{"type":"string","optional":true}}},"max_items":1},"schedule":{"nesting_mode":"list","block":{"attributes":{"frequency":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}},"aws_s3_bucket_metric":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"filter":{"nesting_mode":"list","block":{"attributes":{"prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}},"max_items":1}}}},"aws_s3_bucket_notification":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}},"block_types":{"lambda_function":{"nesting_mode":"list","block":{"attributes":{"events":{"type":["set","string"],"required":true},"filter_prefix":{"type":"string","optional":true},"filter_suffix":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"lambda_function_arn":{"type":"string","optional":true}}}},"queue":{"nesting_mode":"list","block":{"attributes":{"events":{"type":["set","string"],"required":true},"filter_prefix":{"type":"string","optional":true},"filter_suffix":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"queue_arn":{"type":"string","required":true}}}},"topic":{"nesting_mode":"list","block":{"attributes":{"events":{"type":["set","string"],"required":true},"filter_prefix":{"type":"string","optional":true},"filter_suffix":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"topic_arn":{"type":"string","required":true}}}}}}},"aws_s3_bucket_object":{"version":0,"block":{"attributes":{"acl":{"type":"string","optional":true},"bucket":{"type":"string","required":true},"cache_control":{"type":"string","optional":true},"content":{"type":"string","optional":true},"content_base64":{"type":"string","optional":true},"content_disposition":{"type":"string","optional":true},"content_encoding":{"type":"string","optional":true},"content_language":{"type":"string","optional":true},"content_type":{"type":"string","optional":true,"computed":true},"etag":{"type":"string","optional":true,"computed":true},"force_destroy":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","required":true},"kms_key_id":{"type":"string","optional":true},"metadata":{"type":["map","string"],"optional":true},"object_lock_legal_hold_status":{"type":"string","optional":true},"object_lock_mode":{"type":"string","optional":true},"object_lock_retain_until_date":{"type":"string","optional":true},"server_side_encryption":{"type":"string","optional":true,"computed":true},"source":{"type":"string","optional":true},"storage_class":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"version_id":{"type":"string","computed":true},"website_redirect":{"type":"string","optional":true}}}},"aws_s3_bucket_policy":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true}}}},"aws_s3_bucket_public_access_block":{"version":0,"block":{"attributes":{"block_public_acls":{"type":"bool","optional":true},"block_public_policy":{"type":"bool","optional":true},"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ignore_public_acls":{"type":"bool","optional":true},"restrict_public_buckets":{"type":"bool","optional":true}}}},"aws_sagemaker_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"endpoint_config_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_sagemaker_endpoint_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"production_variants":{"nesting_mode":"list","block":{"attributes":{"accelerator_type":{"type":"string","optional":true},"initial_instance_count":{"type":"number","required":true},"initial_variant_weight":{"type":"number","optional":true},"instance_type":{"type":"string","required":true},"model_name":{"type":"string","required":true},"variant_name":{"type":"string","optional":true,"computed":true}}},"min_items":1}}}},"aws_sagemaker_model":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"enable_network_isolation":{"type":"bool","optional":true},"execution_role_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"container":{"nesting_mode":"list","block":{"attributes":{"container_hostname":{"type":"string","optional":true},"environment":{"type":["map","string"],"optional":true},"image":{"type":"string","required":true},"model_data_url":{"type":"string","optional":true}}}},"primary_container":{"nesting_mode":"list","block":{"attributes":{"container_hostname":{"type":"string","optional":true},"environment":{"type":["map","string"],"optional":true},"image":{"type":"string","required":true},"model_data_url":{"type":"string","optional":true}}},"max_items":1},"vpc_config":{"nesting_mode":"list","block":{"attributes":{"security_group_ids":{"type":["set","string"],"required":true},"subnets":{"type":["set","string"],"required":true}}},"max_items":1}}}},"aws_sagemaker_notebook_instance":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"direct_internet_access":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","required":true},"kms_key_id":{"type":"string","optional":true},"lifecycle_config_name":{"type":"string","optional":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"subnet_id":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_sagemaker_notebook_instance_lifecycle_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"on_create":{"type":"string","optional":true},"on_start":{"type":"string","optional":true}}}},"aws_secretsmanager_secret":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","optional":true},"recovery_window_in_days":{"type":"number","optional":true},"rotation_enabled":{"type":"bool","computed":true},"rotation_lambda_arn":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"rotation_rules":{"nesting_mode":"list","block":{"attributes":{"automatically_after_days":{"type":"number","required":true}}},"max_items":1}}}},"aws_secretsmanager_secret_rotation":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"rotation_enabled":{"type":"bool","computed":true},"rotation_lambda_arn":{"type":"string","required":true},"secret_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"rotation_rules":{"nesting_mode":"list","block":{"attributes":{"automatically_after_days":{"type":"number","required":true}}},"min_items":1,"max_items":1}}}},"aws_secretsmanager_secret_version":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"secret_binary":{"type":"string","optional":true,"sensitive":true},"secret_id":{"type":"string","required":true},"secret_string":{"type":"string","optional":true,"sensitive":true},"version_id":{"type":"string","computed":true},"version_stages":{"type":["set","string"],"optional":true,"computed":true}}}},"aws_security_group":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"egress":{"type":["set",["object",{"cidr_blocks":["list","string"],"description":"string","from_port":"number","ipv6_cidr_blocks":["list","string"],"prefix_list_ids":["list","string"],"protocol":"string","security_groups":["set","string"],"self":"bool","to_port":"number"}]],"optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ingress":{"type":["set",["object",{"cidr_blocks":["list","string"],"description":"string","from_port":"number","ipv6_cidr_blocks":["list","string"],"prefix_list_ids":["list","string"],"protocol":"string","security_groups":["set","string"],"self":"bool","to_port":"number"}]],"optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"revoke_rules_on_delete":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_security_group_rule":{"version":2,"block":{"attributes":{"cidr_blocks":{"type":["list","string"],"optional":true},"description":{"type":"string","optional":true},"from_port":{"type":"number","required":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_blocks":{"type":["list","string"],"optional":true},"prefix_list_ids":{"type":["list","string"],"optional":true},"protocol":{"type":"string","required":true},"security_group_id":{"type":"string","required":true},"self":{"type":"bool","optional":true},"source_security_group_id":{"type":"string","optional":true,"computed":true},"to_port":{"type":"number","required":true},"type":{"type":"string","description":"Type of rule, ingress (inbound) or egress (outbound).","required":true}}}},"aws_securityhub_account":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true}}}},"aws_securityhub_member":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"email":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"invite":{"type":"bool","optional":true},"master_id":{"type":"string","computed":true},"member_status":{"type":"string","computed":true}}}},"aws_securityhub_product_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"product_arn":{"type":"string","required":true}}}},"aws_securityhub_standards_subscription":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"standards_arn":{"type":"string","required":true}}}},"aws_service_discovery_http_namespace":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_service_discovery_private_dns_namespace":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"hosted_zone":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vpc":{"type":"string","required":true}}}},"aws_service_discovery_public_dns_namespace":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"hosted_zone":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_service_discovery_service":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"namespace_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"dns_config":{"nesting_mode":"list","block":{"attributes":{"namespace_id":{"type":"string","required":true},"routing_policy":{"type":"string","optional":true}},"block_types":{"dns_records":{"nesting_mode":"list","block":{"attributes":{"ttl":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"health_check_config":{"nesting_mode":"list","block":{"attributes":{"failure_threshold":{"type":"number","optional":true},"resource_path":{"type":"string","optional":true},"type":{"type":"string","optional":true}}},"max_items":1},"health_check_custom_config":{"nesting_mode":"list","block":{"attributes":{"failure_threshold":{"type":"number","optional":true}}},"max_items":1}}}},"aws_servicecatalog_portfolio":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"created_time":{"type":"string","computed":true},"description":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"provider_name":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_servicequotas_service_quota":{"version":0,"block":{"attributes":{"adjustable":{"type":"bool","computed":true},"arn":{"type":"string","computed":true},"default_value":{"type":"number","computed":true},"id":{"type":"string","optional":true,"computed":true},"quota_code":{"type":"string","required":true},"quota_name":{"type":"string","computed":true},"request_id":{"type":"string","computed":true},"request_status":{"type":"string","computed":true},"service_code":{"type":"string","required":true},"service_name":{"type":"string","computed":true},"value":{"type":"number","required":true}}}},"aws_ses_active_receipt_rule_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"rule_set_name":{"type":"string","required":true}}}},"aws_ses_configuration_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_ses_domain_dkim":{"version":0,"block":{"attributes":{"dkim_tokens":{"type":["list","string"],"computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ses_domain_identity":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"verification_token":{"type":"string","computed":true}}}},"aws_ses_domain_identity_verification":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_ses_domain_mail_from":{"version":0,"block":{"attributes":{"behavior_on_mx_failure":{"type":"string","optional":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"mail_from_domain":{"type":"string","required":true}}}},"aws_ses_email_identity":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"email":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ses_event_destination":{"version":0,"block":{"attributes":{"configuration_set_name":{"type":"string","required":true},"enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"matching_types":{"type":["set","string"],"required":true},"name":{"type":"string","required":true}},"block_types":{"cloudwatch_destination":{"nesting_mode":"set","block":{"attributes":{"default_value":{"type":"string","required":true},"dimension_name":{"type":"string","required":true},"value_source":{"type":"string","required":true}}}},"kinesis_destination":{"nesting_mode":"list","block":{"attributes":{"role_arn":{"type":"string","required":true},"stream_arn":{"type":"string","required":true}}},"max_items":1},"sns_destination":{"nesting_mode":"list","block":{"attributes":{"topic_arn":{"type":"string","required":true}}},"max_items":1}}}},"aws_ses_identity_notification_topic":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"identity":{"type":"string","required":true},"include_original_headers":{"type":"bool","optional":true},"notification_type":{"type":"string","required":true},"topic_arn":{"type":"string","optional":true}}}},"aws_ses_identity_policy":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"identity":{"type":"string","required":true},"name":{"type":"string","required":true},"policy":{"type":"string","required":true}}}},"aws_ses_receipt_filter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cidr":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"policy":{"type":"string","required":true}}}},"aws_ses_receipt_rule":{"version":0,"block":{"attributes":{"after":{"type":"string","optional":true},"enabled":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"recipients":{"type":["set","string"],"optional":true},"rule_set_name":{"type":"string","required":true},"scan_enabled":{"type":"bool","optional":true,"computed":true},"tls_policy":{"type":"string","optional":true,"computed":true}},"block_types":{"add_header_action":{"nesting_mode":"set","block":{"attributes":{"header_name":{"type":"string","required":true},"header_value":{"type":"string","required":true},"position":{"type":"number","required":true}}}},"bounce_action":{"nesting_mode":"set","block":{"attributes":{"message":{"type":"string","required":true},"position":{"type":"number","required":true},"sender":{"type":"string","required":true},"smtp_reply_code":{"type":"string","required":true},"status_code":{"type":"string","optional":true},"topic_arn":{"type":"string","optional":true}}}},"lambda_action":{"nesting_mode":"set","block":{"attributes":{"function_arn":{"type":"string","required":true},"invocation_type":{"type":"string","optional":true,"computed":true},"position":{"type":"number","required":true},"topic_arn":{"type":"string","optional":true}}}},"s3_action":{"nesting_mode":"set","block":{"attributes":{"bucket_name":{"type":"string","required":true},"kms_key_arn":{"type":"string","optional":true},"object_key_prefix":{"type":"string","optional":true},"position":{"type":"number","required":true},"topic_arn":{"type":"string","optional":true}}}},"sns_action":{"nesting_mode":"set","block":{"attributes":{"position":{"type":"number","required":true},"topic_arn":{"type":"string","required":true}}}},"stop_action":{"nesting_mode":"set","block":{"attributes":{"position":{"type":"number","required":true},"scope":{"type":"string","required":true},"topic_arn":{"type":"string","optional":true}}}},"workmail_action":{"nesting_mode":"set","block":{"attributes":{"organization_arn":{"type":"string","required":true},"position":{"type":"number","required":true},"topic_arn":{"type":"string","optional":true}}}}}}},"aws_ses_receipt_rule_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"rule_set_name":{"type":"string","required":true}}}},"aws_ses_template":{"version":0,"block":{"attributes":{"html":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"subject":{"type":"string","optional":true},"text":{"type":"string","optional":true}}}},"aws_sfn_activity":{"version":0,"block":{"attributes":{"creation_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_sfn_state_machine":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_date":{"type":"string","computed":true},"definition":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","required":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_shield_protection":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"resource_arn":{"type":"string","required":true}}}},"aws_simpledb_domain":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_snapshot_create_volume_permission":{"version":0,"block":{"attributes":{"account_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"snapshot_id":{"type":"string","required":true}}}},"aws_sns_platform_application":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"event_delivery_failure_topic_arn":{"type":"string","optional":true},"event_endpoint_created_topic_arn":{"type":"string","optional":true},"event_endpoint_deleted_topic_arn":{"type":"string","optional":true},"event_endpoint_updated_topic_arn":{"type":"string","optional":true},"failure_feedback_role_arn":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"platform":{"type":"string","required":true},"platform_credential":{"type":"string","required":true,"sensitive":true},"platform_principal":{"type":"string","optional":true,"sensitive":true},"success_feedback_role_arn":{"type":"string","optional":true},"success_feedback_sample_rate":{"type":"string","optional":true}}}},"aws_sns_sms_preferences":{"version":0,"block":{"attributes":{"default_sender_id":{"type":"string","optional":true},"default_sms_type":{"type":"string","optional":true},"delivery_status_iam_role_arn":{"type":"string","optional":true},"delivery_status_success_sampling_rate":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"monthly_spend_limit":{"type":"string","optional":true},"usage_report_s3_bucket":{"type":"string","optional":true}}}},"aws_sns_topic":{"version":0,"block":{"attributes":{"application_failure_feedback_role_arn":{"type":"string","optional":true},"application_success_feedback_role_arn":{"type":"string","optional":true},"application_success_feedback_sample_rate":{"type":"number","optional":true},"arn":{"type":"string","computed":true},"delivery_policy":{"type":"string","optional":true},"display_name":{"type":"string","optional":true},"http_failure_feedback_role_arn":{"type":"string","optional":true},"http_success_feedback_role_arn":{"type":"string","optional":true},"http_success_feedback_sample_rate":{"type":"number","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_master_key_id":{"type":"string","optional":true},"lambda_failure_feedback_role_arn":{"type":"string","optional":true},"lambda_success_feedback_role_arn":{"type":"string","optional":true},"lambda_success_feedback_sample_rate":{"type":"number","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","optional":true,"computed":true},"sqs_failure_feedback_role_arn":{"type":"string","optional":true},"sqs_success_feedback_role_arn":{"type":"string","optional":true},"sqs_success_feedback_sample_rate":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_sns_topic_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true}}}},"aws_sns_topic_subscription":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"confirmation_timeout_in_minutes":{"type":"number","optional":true},"delivery_policy":{"type":"string","optional":true},"endpoint":{"type":"string","required":true},"endpoint_auto_confirms":{"type":"bool","optional":true},"filter_policy":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"protocol":{"type":"string","required":true},"raw_message_delivery":{"type":"bool","optional":true},"topic_arn":{"type":"string","required":true}}}},"aws_spot_datafeed_subscription":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"prefix":{"type":"string","optional":true}}}},"aws_spot_fleet_request":{"version":1,"block":{"attributes":{"allocation_strategy":{"type":"string","optional":true},"client_token":{"type":"string","computed":true},"excess_capacity_termination_policy":{"type":"string","optional":true},"fleet_type":{"type":"string","optional":true},"iam_fleet_role":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instance_interruption_behaviour":{"type":"string","optional":true},"instance_pools_to_use_count":{"type":"number","optional":true},"load_balancers":{"type":["set","string"],"optional":true,"computed":true},"replace_unhealthy_instances":{"type":"bool","optional":true},"spot_price":{"type":"string","optional":true},"spot_request_state":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"target_capacity":{"type":"number","required":true},"target_group_arns":{"type":["set","string"],"optional":true,"computed":true},"terminate_instances_with_expiration":{"type":"bool","optional":true},"valid_from":{"type":"string","optional":true},"valid_until":{"type":"string","optional":true},"wait_for_fulfillment":{"type":"bool","optional":true}},"block_types":{"launch_specification":{"nesting_mode":"set","block":{"attributes":{"ami":{"type":"string","required":true},"associate_public_ip_address":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"ebs_optimized":{"type":"bool","optional":true},"iam_instance_profile":{"type":"string","optional":true},"iam_instance_profile_arn":{"type":"string","optional":true},"instance_type":{"type":"string","required":true},"key_name":{"type":"string","optional":true,"computed":true},"monitoring":{"type":"bool","optional":true},"placement_group":{"type":"string","optional":true,"computed":true},"placement_tenancy":{"type":"string","optional":true},"spot_price":{"type":"string","optional":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"user_data":{"type":"string","optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"weighted_capacity":{"type":"string","optional":true}},"block_types":{"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"virtual_name":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}}}}},"launch_template_config":{"nesting_mode":"set","block":{"block_types":{"launch_template_specification":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","optional":true},"name":{"type":"string","optional":true},"version":{"type":"string","optional":true}}},"min_items":1,"max_items":1},"overrides":{"nesting_mode":"set","block":{"attributes":{"availability_zone":{"type":"string","optional":true},"instance_type":{"type":"string","optional":true},"priority":{"type":"number","optional":true,"computed":true},"spot_price":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true},"weighted_capacity":{"type":"number","optional":true,"computed":true}}}}}}},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_spot_instance_request":{"version":0,"block":{"attributes":{"ami":{"type":"string","required":true},"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","optional":true,"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"block_duration_minutes":{"type":"number","optional":true},"cpu_core_count":{"type":"number","optional":true,"computed":true},"cpu_threads_per_core":{"type":"number","optional":true,"computed":true},"disable_api_termination":{"type":"bool","optional":true},"ebs_optimized":{"type":"bool","optional":true},"get_password_data":{"type":"bool","optional":true},"hibernation":{"type":"bool","optional":true},"host_id":{"type":"string","optional":true,"computed":true},"iam_instance_profile":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_initiated_shutdown_behavior":{"type":"string","optional":true},"instance_interruption_behaviour":{"type":"string","optional":true},"instance_state":{"type":"string","computed":true},"instance_type":{"type":"string","required":true},"ipv6_address_count":{"type":"number","optional":true,"computed":true},"ipv6_addresses":{"type":["list","string"],"optional":true,"computed":true},"key_name":{"type":"string","optional":true,"computed":true},"launch_group":{"type":"string","optional":true},"monitoring":{"type":"bool","optional":true},"outpost_arn":{"type":"string","computed":true},"password_data":{"type":"string","computed":true},"placement_group":{"type":"string","optional":true,"computed":true},"primary_network_interface_id":{"type":"string","computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","optional":true,"computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"secondary_private_ips":{"type":["set","string"],"optional":true,"computed":true},"security_groups":{"type":["set","string"],"optional":true,"computed":true},"source_dest_check":{"type":"bool","optional":true},"spot_bid_status":{"type":"string","computed":true},"spot_instance_id":{"type":"string","computed":true},"spot_price":{"type":"string","optional":true},"spot_request_state":{"type":"string","computed":true},"spot_type":{"type":"string","optional":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"tenancy":{"type":"string","optional":true,"computed":true},"user_data":{"type":"string","optional":true},"user_data_base64":{"type":"string","optional":true},"valid_from":{"type":"string","optional":true,"computed":true},"valid_until":{"type":"string","optional":true,"computed":true},"volume_tags":{"type":["map","string"],"optional":true},"vpc_security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"wait_for_fulfillment":{"type":"bool","optional":true}},"block_types":{"credit_specification":{"nesting_mode":"list","block":{"attributes":{"cpu_credits":{"type":"string","optional":true}}},"max_items":1},"ebs_block_device":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","required":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"snapshot_id":{"type":"string","optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}}},"ephemeral_block_device":{"nesting_mode":"set","block":{"attributes":{"device_name":{"type":"string","required":true},"no_device":{"type":"bool","optional":true},"virtual_name":{"type":"string","optional":true}}}},"metadata_options":{"nesting_mode":"list","block":{"attributes":{"http_endpoint":{"type":"string","optional":true,"computed":true},"http_put_response_hop_limit":{"type":"number","optional":true,"computed":true},"http_tokens":{"type":"string","optional":true,"computed":true}}},"max_items":1},"network_interface":{"nesting_mode":"set","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_index":{"type":"number","required":true},"network_interface_id":{"type":"string","required":true}}}},"root_block_device":{"nesting_mode":"list","block":{"attributes":{"delete_on_termination":{"type":"bool","optional":true},"device_name":{"type":"string","computed":true},"encrypted":{"type":"bool","optional":true,"computed":true},"iops":{"type":"number","optional":true,"computed":true},"kms_key_id":{"type":"string","optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","optional":true,"computed":true},"volume_type":{"type":"string","optional":true,"computed":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_sqs_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"content_based_deduplication":{"type":"bool","optional":true},"delay_seconds":{"type":"number","optional":true},"fifo_queue":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_data_key_reuse_period_seconds":{"type":"number","optional":true,"computed":true},"kms_master_key_id":{"type":"string","optional":true},"max_message_size":{"type":"number","optional":true},"message_retention_seconds":{"type":"number","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"policy":{"type":"string","optional":true,"computed":true},"receive_wait_time_seconds":{"type":"number","optional":true},"redrive_policy":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"visibility_timeout_seconds":{"type":"number","optional":true}}}},"aws_sqs_queue_policy":{"version":1,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","required":true},"queue_url":{"type":"string","required":true}}}},"aws_ssm_activation":{"version":0,"block":{"attributes":{"activation_code":{"type":"string","computed":true},"description":{"type":"string","optional":true},"expiration_date":{"type":"string","optional":true,"computed":true},"expired":{"type":"bool","computed":true},"iam_role":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"registration_count":{"type":"number","computed":true},"registration_limit":{"type":"number","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_ssm_association":{"version":1,"block":{"attributes":{"association_id":{"type":"string","computed":true},"association_name":{"type":"string","optional":true},"automation_target_parameter_name":{"type":"string","optional":true},"compliance_severity":{"type":"string","optional":true},"document_version":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true},"max_concurrency":{"type":"string","optional":true},"max_errors":{"type":"string","optional":true},"name":{"type":"string","required":true},"parameters":{"type":["map","string"],"optional":true,"computed":true},"schedule_expression":{"type":"string","optional":true}},"block_types":{"output_location":{"nesting_mode":"list","block":{"attributes":{"s3_bucket_name":{"type":"string","required":true},"s3_key_prefix":{"type":"string","optional":true}}},"max_items":1},"targets":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"max_items":5}}}},"aws_ssm_document":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"content":{"type":"string","required":true},"created_date":{"type":"string","computed":true},"default_version":{"type":"string","computed":true},"description":{"type":"string","computed":true},"document_format":{"type":"string","optional":true},"document_type":{"type":"string","required":true},"document_version":{"type":"string","computed":true},"hash":{"type":"string","computed":true},"hash_type":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"latest_version":{"type":"string","computed":true},"name":{"type":"string","required":true},"owner":{"type":"string","computed":true},"parameter":{"type":["list",["object",{"default_value":"string","description":"string","name":"string","type":"string"}]],"computed":true},"permissions":{"type":["map","string"],"optional":true},"platform_types":{"type":["list","string"],"computed":true},"schema_version":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"target_type":{"type":"string","optional":true}},"block_types":{"attachments_source":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"name":{"type":"string","optional":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ssm_maintenance_window":{"version":0,"block":{"attributes":{"allow_unassociated_targets":{"type":"bool","optional":true},"cutoff":{"type":"number","required":true},"description":{"type":"string","optional":true},"duration":{"type":"number","required":true},"enabled":{"type":"bool","optional":true},"end_date":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"schedule":{"type":"string","required":true},"schedule_timezone":{"type":"string","optional":true},"start_date":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_ssm_maintenance_window_target":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"owner_information":{"type":"string","optional":true},"resource_type":{"type":"string","required":true},"window_id":{"type":"string","required":true}},"block_types":{"targets":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"min_items":1,"max_items":5}}}},"aws_ssm_maintenance_window_task":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"max_concurrency":{"type":"string","required":true},"max_errors":{"type":"string","required":true},"name":{"type":"string","optional":true},"priority":{"type":"number","optional":true},"service_role_arn":{"type":"string","required":true},"task_arn":{"type":"string","required":true},"task_type":{"type":"string","required":true},"window_id":{"type":"string","required":true}},"block_types":{"targets":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"min_items":1},"task_invocation_parameters":{"nesting_mode":"list","block":{"block_types":{"automation_parameters":{"nesting_mode":"list","block":{"attributes":{"document_version":{"type":"string","optional":true}},"block_types":{"parameter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}},"max_items":1},"lambda_parameters":{"nesting_mode":"list","block":{"attributes":{"client_context":{"type":"string","optional":true},"payload":{"type":"string","optional":true,"sensitive":true},"qualifier":{"type":"string","optional":true}}},"max_items":1},"run_command_parameters":{"nesting_mode":"list","block":{"attributes":{"comment":{"type":"string","optional":true},"document_hash":{"type":"string","optional":true},"document_hash_type":{"type":"string","optional":true},"output_s3_bucket":{"type":"string","optional":true},"output_s3_key_prefix":{"type":"string","optional":true},"service_role_arn":{"type":"string","optional":true},"timeout_seconds":{"type":"number","optional":true}},"block_types":{"notification_config":{"nesting_mode":"list","block":{"attributes":{"notification_arn":{"type":"string","optional":true},"notification_events":{"type":["list","string"],"optional":true},"notification_type":{"type":"string","optional":true}}},"max_items":1},"parameter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}},"max_items":1},"step_functions_parameters":{"nesting_mode":"list","block":{"attributes":{"input":{"type":"string","optional":true,"sensitive":true},"name":{"type":"string","optional":true}}},"max_items":1}}},"max_items":1}}}},"aws_ssm_parameter":{"version":0,"block":{"attributes":{"allowed_pattern":{"type":"string","optional":true},"arn":{"type":"string","optional":true,"computed":true},"data_type":{"type":"string","optional":true,"computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"overwrite":{"type":"bool","optional":true},"tags":{"type":["map","string"],"optional":true},"tier":{"type":"string","optional":true},"type":{"type":"string","required":true},"value":{"type":"string","required":true,"sensitive":true},"version":{"type":"number","computed":true}}}},"aws_ssm_patch_baseline":{"version":0,"block":{"attributes":{"approved_patches":{"type":["set","string"],"optional":true},"approved_patches_compliance_level":{"type":"string","optional":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"operating_system":{"type":"string","optional":true},"rejected_patches":{"type":["set","string"],"optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"approval_rule":{"nesting_mode":"list","block":{"attributes":{"approve_after_days":{"type":"number","required":true},"compliance_level":{"type":"string","optional":true},"enable_non_security":{"type":"bool","optional":true}},"block_types":{"patch_filter":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"min_items":1,"max_items":10}}}},"global_filter":{"nesting_mode":"list","block":{"attributes":{"key":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}},"max_items":4}}}},"aws_ssm_patch_group":{"version":0,"block":{"attributes":{"baseline_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"patch_group":{"type":"string","required":true}}}},"aws_ssm_resource_data_sync":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"s3_destination":{"nesting_mode":"list","block":{"attributes":{"bucket_name":{"type":"string","required":true},"kms_key_arn":{"type":"string","optional":true},"prefix":{"type":"string","optional":true},"region":{"type":"string","required":true},"sync_format":{"type":"string","optional":true}}},"min_items":1,"max_items":1}}}},"aws_storagegateway_cache":{"version":0,"block":{"attributes":{"disk_id":{"type":"string","required":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_storagegateway_cached_iscsi_volume":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"chap_enabled":{"type":"bool","computed":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"lun_number":{"type":"number","computed":true},"network_interface_id":{"type":"string","required":true},"network_interface_port":{"type":"number","computed":true},"snapshot_id":{"type":"string","optional":true},"source_volume_arn":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"target_arn":{"type":"string","computed":true},"target_name":{"type":"string","required":true},"volume_arn":{"type":"string","computed":true},"volume_id":{"type":"string","computed":true},"volume_size_in_bytes":{"type":"number","required":true}}}},"aws_storagegateway_gateway":{"version":0,"block":{"attributes":{"activation_key":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"cloudwatch_log_group_arn":{"type":"string","optional":true},"gateway_id":{"type":"string","computed":true},"gateway_ip_address":{"type":"string","optional":true,"computed":true},"gateway_name":{"type":"string","required":true},"gateway_timezone":{"type":"string","required":true},"gateway_type":{"type":"string","optional":true},"gateway_vpc_endpoint":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"medium_changer_type":{"type":"string","optional":true},"smb_guest_password":{"type":"string","optional":true,"sensitive":true},"tags":{"type":["map","string"],"optional":true},"tape_drive_type":{"type":"string","optional":true}},"block_types":{"smb_active_directory_settings":{"nesting_mode":"list","block":{"attributes":{"domain_name":{"type":"string","required":true},"password":{"type":"string","required":true,"sensitive":true},"username":{"type":"string","required":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true}}}}}}},"aws_storagegateway_nfs_file_share":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"client_list":{"type":["set","string"],"required":true},"default_storage_class":{"type":"string","optional":true},"fileshare_id":{"type":"string","computed":true},"gateway_arn":{"type":"string","required":true},"guess_mime_type_enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"kms_encrypted":{"type":"bool","optional":true},"kms_key_arn":{"type":"string","optional":true},"location_arn":{"type":"string","required":true},"object_acl":{"type":"string","optional":true},"path":{"type":"string","computed":true},"read_only":{"type":"bool","optional":true},"requester_pays":{"type":"bool","optional":true},"role_arn":{"type":"string","required":true},"squash":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"nfs_file_share_defaults":{"nesting_mode":"list","block":{"attributes":{"directory_mode":{"type":"string","optional":true},"file_mode":{"type":"string","optional":true},"group_id":{"type":"number","optional":true},"owner_id":{"type":"number","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_storagegateway_smb_file_share":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"authentication":{"type":"string","optional":true},"default_storage_class":{"type":"string","optional":true},"fileshare_id":{"type":"string","computed":true},"gateway_arn":{"type":"string","required":true},"guess_mime_type_enabled":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"invalid_user_list":{"type":["set","string"],"optional":true},"kms_encrypted":{"type":"bool","optional":true},"kms_key_arn":{"type":"string","optional":true},"location_arn":{"type":"string","required":true},"object_acl":{"type":"string","optional":true},"path":{"type":"string","computed":true},"read_only":{"type":"bool","optional":true},"requester_pays":{"type":"bool","optional":true},"role_arn":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"valid_user_list":{"type":["set","string"],"optional":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_storagegateway_upload_buffer":{"version":0,"block":{"attributes":{"disk_id":{"type":"string","required":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_storagegateway_working_storage":{"version":0,"block":{"attributes":{"disk_id":{"type":"string","required":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_subnet":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_ipv6_address_on_creation":{"type":"bool","optional":true},"availability_zone":{"type":"string","optional":true,"computed":true},"availability_zone_id":{"type":"string","optional":true,"computed":true},"cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block_association_id":{"type":"string","computed":true},"map_public_ip_on_launch":{"type":"bool","optional":true},"outpost_arn":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_swf_domain":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"workflow_execution_retention_period_in_days":{"type":"string","required":true}}}},"aws_transfer_server":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"endpoint_type":{"type":"string","optional":true},"force_destroy":{"type":"bool","optional":true},"host_key":{"type":"string","optional":true,"sensitive":true},"host_key_fingerprint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identity_provider_type":{"type":"string","optional":true},"invocation_role":{"type":"string","optional":true},"logging_role":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true},"url":{"type":"string","optional":true}},"block_types":{"endpoint_details":{"nesting_mode":"list","block":{"attributes":{"vpc_endpoint_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_transfer_ssh_key":{"version":0,"block":{"attributes":{"body":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"server_id":{"type":"string","required":true},"user_name":{"type":"string","required":true}}}},"aws_transfer_user":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"home_directory":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","optional":true},"role":{"type":"string","required":true},"server_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"user_name":{"type":"string","required":true}}}},"aws_volume_attachment":{"version":0,"block":{"attributes":{"device_name":{"type":"string","required":true},"force_detach":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","required":true},"skip_destroy":{"type":"bool","optional":true},"volume_id":{"type":"string","required":true}}}},"aws_vpc":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_generated_ipv6_cidr_block":{"type":"bool","optional":true},"cidr_block":{"type":"string","required":true},"default_network_acl_id":{"type":"string","computed":true},"default_route_table_id":{"type":"string","computed":true},"default_security_group_id":{"type":"string","computed":true},"dhcp_options_id":{"type":"string","computed":true},"enable_classiclink":{"type":"bool","optional":true,"computed":true},"enable_classiclink_dns_support":{"type":"bool","optional":true,"computed":true},"enable_dns_hostnames":{"type":"bool","optional":true,"computed":true},"enable_dns_support":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_tenancy":{"type":"string","optional":true},"ipv6_association_id":{"type":"string","computed":true},"ipv6_cidr_block":{"type":"string","computed":true},"main_route_table_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_vpc_dhcp_options":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","optional":true},"domain_name_servers":{"type":["list","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"netbios_name_servers":{"type":["list","string"],"optional":true},"netbios_node_type":{"type":"string","optional":true},"ntp_servers":{"type":["list","string"],"optional":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_vpc_dhcp_options_association":{"version":0,"block":{"attributes":{"dhcp_options_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"vpc_id":{"type":"string","required":true}}}},"aws_vpc_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_accept":{"type":"bool","optional":true},"cidr_blocks":{"type":["list","string"],"computed":true},"dns_entry":{"type":["list",["object",{"dns_name":"string","hosted_zone_id":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_ids":{"type":["set","string"],"computed":true},"owner_id":{"type":"string","computed":true},"policy":{"type":"string","optional":true,"computed":true},"prefix_list_id":{"type":"string","computed":true},"private_dns_enabled":{"type":"bool","optional":true},"requester_managed":{"type":"bool","computed":true},"route_table_ids":{"type":["set","string"],"optional":true,"computed":true},"security_group_ids":{"type":["set","string"],"optional":true,"computed":true},"service_name":{"type":"string","required":true},"state":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_endpoint_type":{"type":"string","optional":true},"vpc_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_vpc_endpoint_connection_notification":{"version":0,"block":{"attributes":{"connection_events":{"type":["set","string"],"required":true},"connection_notification_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"notification_type":{"type":"string","computed":true},"state":{"type":"string","computed":true},"vpc_endpoint_id":{"type":"string","optional":true},"vpc_endpoint_service_id":{"type":"string","optional":true}}}},"aws_vpc_endpoint_route_table_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"route_table_id":{"type":"string","required":true},"vpc_endpoint_id":{"type":"string","required":true}}}},"aws_vpc_endpoint_service":{"version":0,"block":{"attributes":{"acceptance_required":{"type":"bool","required":true},"allowed_principals":{"type":["set","string"],"optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"base_endpoint_dns_names":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"manages_vpc_endpoints":{"type":"bool","computed":true},"network_load_balancer_arns":{"type":["set","string"],"required":true},"private_dns_name":{"type":"string","computed":true},"service_name":{"type":"string","computed":true},"service_type":{"type":"string","computed":true},"state":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_vpc_endpoint_service_allowed_principal":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"principal_arn":{"type":"string","required":true},"vpc_endpoint_service_id":{"type":"string","required":true}}}},"aws_vpc_endpoint_subnet_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","required":true},"vpc_endpoint_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_vpc_ipv4_cidr_block_association":{"version":0,"block":{"attributes":{"cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"vpc_id":{"type":"string","required":true}},"block_types":{"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true}}}}}}},"aws_vpc_peering_connection":{"version":0,"block":{"attributes":{"accept_status":{"type":"string","computed":true},"auto_accept":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"peer_owner_id":{"type":"string","optional":true,"computed":true},"peer_region":{"type":"string","optional":true,"computed":true},"peer_vpc_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","required":true}},"block_types":{"accepter":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1},"requester":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1},"timeouts":{"nesting_mode":"single","block":{"attributes":{"create":{"type":"string","optional":true},"delete":{"type":"string","optional":true},"update":{"type":"string","optional":true}}}}}}},"aws_vpc_peering_connection_accepter":{"version":0,"block":{"attributes":{"accept_status":{"type":"string","computed":true},"auto_accept":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"peer_owner_id":{"type":"string","computed":true},"peer_region":{"type":"string","computed":true},"peer_vpc_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"vpc_peering_connection_id":{"type":"string","required":true}},"block_types":{"accepter":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1},"requester":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_vpc_peering_connection_options":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"vpc_peering_connection_id":{"type":"string","required":true}},"block_types":{"accepter":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1},"requester":{"nesting_mode":"list","block":{"attributes":{"allow_classic_link_to_remote_vpc":{"type":"bool","optional":true},"allow_remote_vpc_dns_resolution":{"type":"bool","optional":true},"allow_vpc_to_remote_classic_link":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_vpn_connection":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"customer_gateway_configuration":{"type":"string","computed":true},"customer_gateway_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"routes":{"type":["set",["object",{"destination_cidr_block":"string","source":"string","state":"string"}]],"computed":true},"static_routes_only":{"type":"bool","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"transit_gateway_attachment_id":{"type":"string","computed":true},"transit_gateway_id":{"type":"string","optional":true},"tunnel1_address":{"type":"string","computed":true},"tunnel1_bgp_asn":{"type":"string","computed":true},"tunnel1_bgp_holdtime":{"type":"number","computed":true},"tunnel1_cgw_inside_address":{"type":"string","computed":true},"tunnel1_inside_cidr":{"type":"string","optional":true,"computed":true},"tunnel1_preshared_key":{"type":"string","optional":true,"computed":true,"sensitive":true},"tunnel1_vgw_inside_address":{"type":"string","computed":true},"tunnel2_address":{"type":"string","computed":true},"tunnel2_bgp_asn":{"type":"string","computed":true},"tunnel2_bgp_holdtime":{"type":"number","computed":true},"tunnel2_cgw_inside_address":{"type":"string","computed":true},"tunnel2_inside_cidr":{"type":"string","optional":true,"computed":true},"tunnel2_preshared_key":{"type":"string","optional":true,"computed":true,"sensitive":true},"tunnel2_vgw_inside_address":{"type":"string","computed":true},"type":{"type":"string","required":true},"vgw_telemetry":{"type":["set",["object",{"accepted_route_count":"number","last_status_change":"string","outside_ip_address":"string","status":"string","status_message":"string"}]],"computed":true},"vpn_gateway_id":{"type":"string","optional":true}}}},"aws_vpn_connection_route":{"version":0,"block":{"attributes":{"destination_cidr_block":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"vpn_connection_id":{"type":"string","required":true}}}},"aws_vpn_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","optional":true,"computed":true}}}},"aws_vpn_gateway_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"vpc_id":{"type":"string","required":true},"vpn_gateway_id":{"type":"string","required":true}}}},"aws_vpn_gateway_route_propagation":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"route_table_id":{"type":"string","required":true},"vpn_gateway_id":{"type":"string","required":true}}}},"aws_waf_byte_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"byte_match_tuples":{"nesting_mode":"set","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"target_string":{"type":"string","optional":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_geo_match_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"geo_match_constraint":{"nesting_mode":"set","block":{"attributes":{"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_waf_ipset":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"ip_set_descriptors":{"nesting_mode":"set","block":{"attributes":{"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_waf_rate_based_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"rate_key":{"type":"string","required":true},"rate_limit":{"type":"number","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"predicates":{"nesting_mode":"set","block":{"attributes":{"data_id":{"type":"string","required":true},"negated":{"type":"bool","required":true},"type":{"type":"string","required":true}}}}}}},"aws_waf_regex_match_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"regex_match_tuple":{"nesting_mode":"set","block":{"attributes":{"regex_pattern_set_id":{"type":"string","required":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_regex_pattern_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"regex_pattern_strings":{"type":["set","string"],"optional":true}}}},"aws_waf_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"predicates":{"nesting_mode":"set","block":{"attributes":{"data_id":{"type":"string","required":true},"negated":{"type":"bool","required":true},"type":{"type":"string","required":true}}}}}}},"aws_waf_rule_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"activated_rule":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"rule_id":{"type":"string","required":true},"type":{"type":"string","optional":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_size_constraint_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"size_constraints":{"nesting_mode":"set","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_sql_injection_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"sql_injection_match_tuples":{"nesting_mode":"set","block":{"attributes":{"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_waf_web_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"min_items":1,"max_items":1},"logging_configuration":{"nesting_mode":"list","block":{"attributes":{"log_destination":{"type":"string","required":true}},"block_types":{"redacted_fields":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"set","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"max_items":1},"rules":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"rule_id":{"type":"string","required":true},"type":{"type":"string","optional":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1},"override_action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1}}}}}}},"aws_waf_xss_match_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"xss_match_tuples":{"nesting_mode":"set","block":{"attributes":{"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_byte_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"byte_match_tuples":{"nesting_mode":"set","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"target_string":{"type":"string","optional":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_geo_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"geo_match_constraint":{"nesting_mode":"set","block":{"attributes":{"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_wafregional_ipset":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"ip_set_descriptor":{"nesting_mode":"set","block":{"attributes":{"type":{"type":"string","required":true},"value":{"type":"string","required":true}}}}}}},"aws_wafregional_rate_based_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"rate_key":{"type":"string","required":true},"rate_limit":{"type":"number","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"predicate":{"nesting_mode":"set","block":{"attributes":{"data_id":{"type":"string","required":true},"negated":{"type":"bool","required":true},"type":{"type":"string","required":true}}}}}}},"aws_wafregional_regex_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"regex_match_tuple":{"nesting_mode":"set","block":{"attributes":{"regex_pattern_set_id":{"type":"string","required":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_regex_pattern_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"regex_pattern_strings":{"type":["set","string"],"optional":true}}}},"aws_wafregional_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"predicate":{"nesting_mode":"set","block":{"attributes":{"data_id":{"type":"string","required":true},"negated":{"type":"bool","required":true},"type":{"type":"string","required":true}}}}}}},"aws_wafregional_rule_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"activated_rule":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"rule_id":{"type":"string","required":true},"type":{"type":"string","optional":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_size_constraint_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"size_constraints":{"nesting_mode":"set","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true},"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_sql_injection_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"sql_injection_match_tuple":{"nesting_mode":"set","block":{"attributes":{"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafregional_web_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"metric_name":{"type":"string","required":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"min_items":1,"max_items":1},"logging_configuration":{"nesting_mode":"list","block":{"attributes":{"log_destination":{"type":"string","required":true}},"block_types":{"redacted_fields":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"set","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"max_items":1},"rule":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"rule_id":{"type":"string","required":true},"type":{"type":"string","optional":true}},"block_types":{"action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1},"override_action":{"nesting_mode":"list","block":{"attributes":{"type":{"type":"string","required":true}}},"max_items":1}}}}}}},"aws_wafregional_web_acl_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_arn":{"type":"string","required":true},"web_acl_id":{"type":"string","required":true}}}},"aws_wafregional_xss_match_set":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}},"block_types":{"xss_match_tuple":{"nesting_mode":"set","block":{"attributes":{"text_transformation":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"attributes":{"data":{"type":"string","optional":true},"type":{"type":"string","required":true}}},"min_items":1,"max_items":1}}}}}}},"aws_wafv2_ip_set":{"version":0,"block":{"attributes":{"addresses":{"type":["set","string"],"optional":true},"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"ip_address_version":{"type":"string","required":true},"lock_token":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_wafv2_regex_pattern_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"lock_token":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"regular_expression":{"nesting_mode":"set","block":{"attributes":{"regex_string":{"type":"string","required":true}}},"max_items":10}}}},"aws_wafv2_rule_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"capacity":{"type":"number","required":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"lock_token":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"rule":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"priority":{"type":"number","required":true}},"block_types":{"action":{"nesting_mode":"list","block":{"block_types":{"allow":{"nesting_mode":"list","block":{},"max_items":1},"block":{"nesting_mode":"list","block":{},"max_items":1},"count":{"nesting_mode":"list","block":{},"max_items":1}}},"min_items":1,"max_items":1},"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1,"max_items":1},"visibility_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_metrics_enabled":{"type":"bool","required":true},"metric_name":{"type":"string","required":true},"sampled_requests_enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"visibility_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_metrics_enabled":{"type":"bool","required":true},"metric_name":{"type":"string","required":true},"sampled_requests_enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"aws_wafv2_web_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"capacity":{"type":"number","computed":true},"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"lock_token":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"default_action":{"nesting_mode":"list","block":{"block_types":{"allow":{"nesting_mode":"list","block":{},"max_items":1},"block":{"nesting_mode":"list","block":{},"max_items":1}}},"min_items":1,"max_items":1},"rule":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"priority":{"type":"number","required":true}},"block_types":{"action":{"nesting_mode":"list","block":{"block_types":{"allow":{"nesting_mode":"list","block":{},"max_items":1},"block":{"nesting_mode":"list","block":{},"max_items":1},"count":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"override_action":{"nesting_mode":"list","block":{"block_types":{"count":{"nesting_mode":"list","block":{},"max_items":1},"none":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"managed_rule_group_statement":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true},"vendor_name":{"type":"string","required":true}},"block_types":{"excluded_rule":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}}}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"rate_based_statement":{"nesting_mode":"list","block":{"attributes":{"aggregate_key_type":{"type":"string","optional":true},"limit":{"type":"number","required":true}},"block_types":{"scope_down_statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"and_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"not_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"or_statement":{"nesting_mode":"list","block":{"block_types":{"statement":{"nesting_mode":"list","block":{"block_types":{"byte_match_statement":{"nesting_mode":"list","block":{"attributes":{"positional_constraint":{"type":"string","required":true},"search_string":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"geo_match_statement":{"nesting_mode":"list","block":{"attributes":{"country_codes":{"type":["list","string"],"required":true}}},"max_items":1},"ip_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"max_items":1}}},"max_items":1},"regex_pattern_set_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"rule_group_reference_statement":{"nesting_mode":"list","block":{"attributes":{"arn":{"type":"string","required":true}},"block_types":{"excluded_rule":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}}}}},"max_items":1},"size_constraint_statement":{"nesting_mode":"list","block":{"attributes":{"comparison_operator":{"type":"string","required":true},"size":{"type":"number","required":true}},"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"sqli_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1},"xss_match_statement":{"nesting_mode":"list","block":{"block_types":{"field_to_match":{"nesting_mode":"list","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":1},"text_transformation":{"nesting_mode":"set","block":{"attributes":{"priority":{"type":"number","required":true},"type":{"type":"string","required":true}}},"min_items":1}}},"max_items":1}}},"min_items":1,"max_items":1},"visibility_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_metrics_enabled":{"type":"bool","required":true},"metric_name":{"type":"string","required":true},"sampled_requests_enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"visibility_config":{"nesting_mode":"list","block":{"attributes":{"cloudwatch_metrics_enabled":{"type":"bool","required":true},"metric_name":{"type":"string","required":true},"sampled_requests_enabled":{"type":"bool","required":true}}},"min_items":1,"max_items":1}}}},"aws_wafv2_web_acl_association":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"resource_arn":{"type":"string","required":true},"web_acl_arn":{"type":"string","required":true}}}},"aws_wafv2_web_acl_logging_configuration":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"log_destination_configs":{"type":["set","string"],"description":"AWS Kinesis Firehose Delivery Stream ARNs","required":true},"resource_arn":{"type":"string","description":"AWS WebACL ARN","required":true}},"block_types":{"redacted_fields":{"nesting_mode":"set","block":{"block_types":{"all_query_arguments":{"nesting_mode":"list","block":{},"max_items":1},"body":{"nesting_mode":"list","block":{},"max_items":1},"method":{"nesting_mode":"list","block":{},"max_items":1},"query_string":{"nesting_mode":"list","block":{},"max_items":1},"single_header":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"single_query_argument":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true}}},"max_items":1},"uri_path":{"nesting_mode":"list","block":{},"max_items":1}}},"max_items":100}}}},"aws_worklink_fleet":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"audit_stream_arn":{"type":"string","optional":true},"company_code":{"type":"string","computed":true},"created_time":{"type":"string","computed":true},"device_ca_certificate":{"type":"string","optional":true},"display_name":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"last_updated_time":{"type":"string","computed":true},"name":{"type":"string","required":true},"optimize_for_end_user_location":{"type":"bool","optional":true}},"block_types":{"identity_provider":{"nesting_mode":"list","block":{"attributes":{"saml_metadata":{"type":"string","required":true},"type":{"type":"string","required":true}}},"max_items":1},"network":{"nesting_mode":"list","block":{"attributes":{"security_group_ids":{"type":["set","string"],"required":true},"subnet_ids":{"type":["set","string"],"required":true},"vpc_id":{"type":"string","required":true}}},"max_items":1}}}},"aws_worklink_website_certificate_authority_association":{"version":0,"block":{"attributes":{"certificate":{"type":"string","required":true},"display_name":{"type":"string","optional":true},"fleet_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"website_ca_id":{"type":"string","computed":true}}}},"aws_workspaces_directory":{"version":0,"block":{"attributes":{"alias":{"type":"string","computed":true},"customer_user_name":{"type":"string","computed":true},"directory_id":{"type":"string","required":true},"directory_name":{"type":"string","computed":true},"directory_type":{"type":"string","computed":true},"dns_ip_addresses":{"type":["set","string"],"computed":true},"iam_role_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_group_ids":{"type":["set","string"],"computed":true},"registration_code":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true},"workspace_security_group_id":{"type":"string","computed":true}},"block_types":{"self_service_permissions":{"nesting_mode":"list","block":{"attributes":{"change_compute_type":{"type":"bool","optional":true},"increase_volume_size":{"type":"bool","optional":true},"rebuild_workspace":{"type":"bool","optional":true},"restart_workspace":{"type":"bool","optional":true},"switch_running_mode":{"type":"bool","optional":true}}},"max_items":1}}}},"aws_workspaces_ip_group":{"version":0,"block":{"attributes":{"description":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"rules":{"nesting_mode":"set","block":{"attributes":{"description":{"type":"string","optional":true},"source":{"type":"string","required":true}}}}}}},"aws_workspaces_workspace":{"version":0,"block":{"attributes":{"bundle_id":{"type":"string","required":true},"computer_name":{"type":"string","computed":true},"directory_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","computed":true},"root_volume_encryption_enabled":{"type":"bool","optional":true},"state":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"user_name":{"type":"string","required":true},"user_volume_encryption_enabled":{"type":"bool","optional":true},"volume_encryption_key":{"type":"string","optional":true}},"block_types":{"workspace_properties":{"nesting_mode":"list","block":{"attributes":{"compute_type_name":{"type":"string","optional":true},"root_volume_size_gib":{"type":"number","optional":true},"running_mode":{"type":"string","optional":true},"running_mode_auto_stop_timeout_in_minutes":{"type":"number","optional":true,"computed":true},"user_volume_size_gib":{"type":"number","optional":true}}},"max_items":1}}}},"aws_xray_sampling_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"attributes":{"type":["map","string"],"optional":true},"fixed_rate":{"type":"number","required":true},"host":{"type":"string","required":true},"http_method":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"priority":{"type":"number","required":true},"reservoir_size":{"type":"number","required":true},"resource_arn":{"type":"string","required":true},"rule_name":{"type":"string","optional":true},"service_name":{"type":"string","required":true},"service_type":{"type":"string","required":true},"url_path":{"type":"string","required":true},"version":{"type":"number","required":true}}}}},"data_source_schemas":{"aws_acm_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"key_types":{"type":["set","string"],"optional":true},"most_recent":{"type":"bool","optional":true},"statuses":{"type":["list","string"],"optional":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"types":{"type":["list","string"],"optional":true}}}},"aws_acmpca_certificate_authority":{"version":1,"block":{"attributes":{"arn":{"type":"string","required":true},"certificate":{"type":"string","computed":true},"certificate_chain":{"type":"string","computed":true},"certificate_signing_request":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"not_after":{"type":"string","computed":true},"not_before":{"type":"string","computed":true},"serial":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"type":{"type":"string","computed":true}},"block_types":{"revocation_configuration":{"nesting_mode":"list","block":{"block_types":{"crl_configuration":{"nesting_mode":"list","block":{"attributes":{"custom_cname":{"type":"string","computed":true},"enabled":{"type":"bool","computed":true},"expiration_in_days":{"type":"number","computed":true},"s3_bucket_name":{"type":"string","computed":true}}}}}}}}}},"aws_alb":{"version":0,"block":{"attributes":{"access_logs":{"type":["list",["object",{"bucket":"string","enabled":"bool","prefix":"string"}]],"computed":true},"arn":{"type":"string","optional":true,"computed":true},"arn_suffix":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"drop_invalid_header_fields":{"type":"bool","computed":true},"enable_deletion_protection":{"type":"bool","computed":true},"enable_http2":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","computed":true},"internal":{"type":"bool","computed":true},"ip_address_type":{"type":"string","computed":true},"load_balancer_type":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_mapping":{"type":["set",["object",{"allocation_id":"string","private_ipv4_address":"string","subnet_id":"string"}]],"computed":true},"subnets":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","computed":true},"zone_id":{"type":"string","computed":true}}}},"aws_alb_listener":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"certificate_arn":{"type":"string","computed":true},"default_action":{"type":["list",["object",{"authenticate_cognito":["list",["object",{"authentication_request_extra_params":["map","string"],"on_unauthenticated_request":"string","scope":"string","session_cookie_name":"string","session_timeout":"number","user_pool_arn":"string","user_pool_client_id":"string","user_pool_domain":"string"}]],"authenticate_oidc":["list",["object",{"authentication_request_extra_params":["map","string"],"authorization_endpoint":"string","client_id":"string","client_secret":"string","issuer":"string","on_unauthenticated_request":"string","scope":"string","session_cookie_name":"string","session_timeout":"number","token_endpoint":"string","user_info_endpoint":"string"}]],"fixed_response":["list",["object",{"content_type":"string","message_body":"string","status_code":"string"}]],"order":"number","redirect":["list",["object",{"host":"string","path":"string","port":"string","protocol":"string","query":"string","status_code":"string"}]],"target_group_arn":"string","type":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"load_balancer_arn":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"protocol":{"type":"string","computed":true},"ssl_policy":{"type":"string","computed":true}}}},"aws_alb_target_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"arn_suffix":{"type":"string","computed":true},"deregistration_delay":{"type":"number","computed":true},"health_check":{"type":["list",["object",{"enabled":"bool","healthy_threshold":"number","interval":"number","matcher":"string","path":"string","port":"string","protocol":"string","timeout":"number","unhealthy_threshold":"number"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"lambda_multi_value_headers_enabled":{"type":"bool","computed":true},"load_balancing_algorithm_type":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","computed":true},"protocol":{"type":"string","computed":true},"proxy_protocol_v2":{"type":"bool","computed":true},"slow_start":{"type":"number","computed":true},"stickiness":{"type":["list",["object",{"cookie_duration":"number","enabled":"bool","type":"string"}]],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"target_type":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_ami":{"version":0,"block":{"attributes":{"architecture":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"block_device_mappings":{"type":["set",["object",{"device_name":"string","ebs":["map","string"],"no_device":"string","virtual_name":"string"}]],"computed":true},"creation_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"executable_users":{"type":["list","string"],"optional":true},"hypervisor":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","computed":true},"image_location":{"type":"string","computed":true},"image_owner_alias":{"type":"string","computed":true},"image_type":{"type":"string","computed":true},"kernel_id":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"name":{"type":"string","computed":true},"name_regex":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"owners":{"type":["list","string"],"required":true},"platform":{"type":"string","computed":true},"product_codes":{"type":["set",["object",{"product_code_id":"string","product_code_type":"string"}]],"computed":true},"public":{"type":"bool","computed":true},"ramdisk_id":{"type":"string","computed":true},"root_device_name":{"type":"string","computed":true},"root_device_type":{"type":"string","computed":true},"root_snapshot_id":{"type":"string","computed":true},"sriov_net_support":{"type":"string","computed":true},"state":{"type":"string","computed":true},"state_reason":{"type":["map","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"virtualization_type":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ami_ids":{"version":0,"block":{"attributes":{"executable_users":{"type":["list","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["list","string"],"computed":true},"name_regex":{"type":"string","optional":true},"owners":{"type":["list","string"],"required":true},"sort_ascending":{"type":"bool","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_api_gateway_api_key":{"version":0,"block":{"attributes":{"created_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"enabled":{"type":"bool","computed":true},"id":{"type":"string","required":true},"last_updated_date":{"type":"string","computed":true},"name":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"value":{"type":"string","computed":true,"sensitive":true}}}},"aws_api_gateway_resource":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"parent_id":{"type":"string","computed":true},"path":{"type":"string","required":true},"path_part":{"type":"string","computed":true},"rest_api_id":{"type":"string","required":true}}}},"aws_api_gateway_rest_api":{"version":0,"block":{"attributes":{"api_key_source":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"binary_media_types":{"type":["list","string"],"computed":true},"description":{"type":"string","computed":true},"endpoint_configuration":{"type":["list",["object",{"types":["list","string"],"vpc_endpoint_ids":["set","string"]}]],"computed":true},"execution_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"minimum_compression_size":{"type":"number","computed":true},"name":{"type":"string","required":true},"policy":{"type":"string","computed":true},"root_resource_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_api_gateway_vpc_link":{"version":0,"block":{"attributes":{"description":{"type":"string","computed":true},"id":{"type":"string","computed":true},"name":{"type":"string","required":true},"status":{"type":"string","computed":true},"status_message":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"target_arns":{"type":["set","string"],"computed":true}}}},"aws_arn":{"version":0,"block":{"attributes":{"account":{"type":"string","computed":true},"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"partition":{"type":"string","computed":true},"region":{"type":"string","computed":true},"resource":{"type":"string","computed":true},"service":{"type":"string","computed":true}}}},"aws_autoscaling_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"default_cooldown":{"type":"number","computed":true},"desired_capacity":{"type":"number","computed":true},"health_check_grace_period":{"type":"number","computed":true},"health_check_type":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"launch_configuration":{"type":"string","computed":true},"load_balancers":{"type":["set","string"],"computed":true},"max_size":{"type":"number","computed":true},"min_size":{"type":"number","computed":true},"name":{"type":"string","required":true},"new_instances_protected_from_scale_in":{"type":"bool","computed":true},"placement_group":{"type":"string","computed":true},"service_linked_role_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"target_group_arns":{"type":["set","string"],"computed":true},"termination_policies":{"type":["set","string"],"computed":true},"vpc_zone_identifier":{"type":"string","computed":true}}}},"aws_autoscaling_groups":{"version":0,"block":{"attributes":{"arns":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"names":{"type":["list","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_availability_zone":{"version":0,"block":{"attributes":{"all_availability_zones":{"type":"bool","optional":true},"group_name":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"name_suffix":{"type":"string","computed":true},"network_border_group":{"type":"string","computed":true},"opt_in_status":{"type":"string","computed":true},"region":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_availability_zones":{"version":0,"block":{"attributes":{"all_availability_zones":{"type":"bool","optional":true},"exclude_names":{"type":["set","string"],"optional":true},"exclude_zone_ids":{"type":["set","string"],"optional":true},"group_names":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"names":{"type":["list","string"],"computed":true},"state":{"type":"string","optional":true},"zone_ids":{"type":["list","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_backup_plan":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"plan_id":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"version":{"type":"string","computed":true}}}},"aws_backup_selection":{"version":0,"block":{"attributes":{"iam_role_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"plan_id":{"type":"string","required":true},"resources":{"type":["set","string"],"computed":true},"selection_id":{"type":"string","required":true}}}},"aws_backup_vault":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_arn":{"type":"string","computed":true},"name":{"type":"string","required":true},"recovery_points":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_batch_compute_environment":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compute_environment_name":{"type":"string","required":true},"ecs_cluster_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"service_role":{"type":"string","computed":true},"state":{"type":"string","computed":true},"status":{"type":"string","computed":true},"status_reason":{"type":"string","computed":true},"type":{"type":"string","computed":true}}}},"aws_batch_job_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compute_environment_order":{"type":["list",["object",{"compute_environment":"string","order":"number"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"priority":{"type":"number","computed":true},"state":{"type":"string","computed":true},"status":{"type":"string","computed":true},"status_reason":{"type":"string","computed":true}}}},"aws_billing_service_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_caller_identity":{"version":0,"block":{"attributes":{"account_id":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"user_id":{"type":"string","computed":true}}}},"aws_canonical_user_id":{"version":0,"block":{"attributes":{"display_name":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_cloudformation_export":{"version":0,"block":{"attributes":{"exporting_stack_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"value":{"type":"string","computed":true}}}},"aws_cloudformation_stack":{"version":0,"block":{"attributes":{"capabilities":{"type":["set","string"],"computed":true},"description":{"type":"string","computed":true},"disable_rollback":{"type":"bool","computed":true},"iam_role_arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"notification_arns":{"type":["set","string"],"computed":true},"outputs":{"type":["map","string"],"computed":true},"parameters":{"type":["map","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"template_body":{"type":"string","computed":true},"timeout_in_minutes":{"type":"number","computed":true}}}},"aws_cloudfront_distribution":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","computed":true},"enabled":{"type":"bool","computed":true},"etag":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","required":true},"in_progress_validation_batches":{"type":"number","computed":true},"last_modified_time":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_cloudhsm_v2_cluster":{"version":0,"block":{"attributes":{"cluster_certificates":{"type":["list",["object",{"aws_hardware_certificate":"string","cluster_certificate":"string","cluster_csr":"string","hsm_certificate":"string","manufacturer_hardware_certificate":"string"}]],"computed":true},"cluster_id":{"type":"string","required":true},"cluster_state":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"security_group_id":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_cloudtrail_service_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_cloudwatch_log_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_time":{"type":"number","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"name":{"type":"string","required":true},"retention_in_days":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_codecommit_repository":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"clone_url_http":{"type":"string","computed":true},"clone_url_ssh":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"repository_id":{"type":"string","computed":true},"repository_name":{"type":"string","required":true}}}},"aws_cognito_user_pools":{"version":0,"block":{"attributes":{"arns":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"name":{"type":"string","required":true}}}},"aws_cur_report_definition":{"version":0,"block":{"attributes":{"additional_artifacts":{"type":["set","string"],"computed":true},"additional_schema_elements":{"type":["set","string"],"computed":true},"compression":{"type":"string","computed":true},"format":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"report_name":{"type":"string","required":true},"s3_bucket":{"type":"string","computed":true},"s3_prefix":{"type":"string","computed":true},"s3_region":{"type":"string","computed":true},"time_unit":{"type":"string","computed":true}}}},"aws_customer_gateway":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bgp_asn":{"type":"number","computed":true},"id":{"type":"string","optional":true},"ip_address":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"type":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_db_cluster_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zones":{"type":["list","string"],"computed":true},"db_cluster_identifier":{"type":"string","optional":true},"db_cluster_snapshot_arn":{"type":"string","computed":true},"db_cluster_snapshot_identifier":{"type":"string","optional":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"include_public":{"type":"bool","optional":true},"include_shared":{"type":"bool","optional":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"port":{"type":"number","computed":true},"snapshot_create_time":{"type":"string","computed":true},"snapshot_type":{"type":"string","optional":true},"source_db_cluster_snapshot_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_db_event_categories":{"version":0,"block":{"attributes":{"event_categories":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"source_type":{"type":"string","optional":true}}}},"aws_db_instance":{"version":1,"block":{"attributes":{"address":{"type":"string","computed":true},"allocated_storage":{"type":"number","computed":true},"auto_minor_version_upgrade":{"type":"bool","computed":true},"availability_zone":{"type":"string","computed":true},"backup_retention_period":{"type":"number","computed":true},"ca_cert_identifier":{"type":"string","computed":true},"db_cluster_identifier":{"type":"string","computed":true},"db_instance_arn":{"type":"string","computed":true},"db_instance_class":{"type":"string","computed":true},"db_instance_identifier":{"type":"string","required":true},"db_instance_port":{"type":"number","computed":true},"db_name":{"type":"string","computed":true},"db_parameter_groups":{"type":["list","string"],"computed":true},"db_security_groups":{"type":["list","string"],"computed":true},"db_subnet_group":{"type":"string","computed":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"iops":{"type":"number","computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"master_username":{"type":"string","computed":true},"monitoring_interval":{"type":"number","computed":true},"monitoring_role_arn":{"type":"string","computed":true},"multi_az":{"type":"bool","computed":true},"option_group_memberships":{"type":["list","string"],"computed":true},"port":{"type":"number","computed":true},"preferred_backup_window":{"type":"string","computed":true},"preferred_maintenance_window":{"type":"string","computed":true},"publicly_accessible":{"type":"bool","computed":true},"replicate_source_db":{"type":"string","computed":true},"resource_id":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"storage_type":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"timezone":{"type":"string","computed":true},"vpc_security_groups":{"type":["list","string"],"computed":true}}}},"aws_db_snapshot":{"version":0,"block":{"attributes":{"allocated_storage":{"type":"number","computed":true},"availability_zone":{"type":"string","computed":true},"db_instance_identifier":{"type":"string","optional":true},"db_snapshot_arn":{"type":"string","computed":true},"db_snapshot_identifier":{"type":"string","optional":true},"encrypted":{"type":"bool","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"include_public":{"type":"bool","optional":true},"include_shared":{"type":"bool","optional":true},"iops":{"type":"number","computed":true},"kms_key_id":{"type":"string","computed":true},"license_model":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"option_group_name":{"type":"string","computed":true},"port":{"type":"number","computed":true},"snapshot_create_time":{"type":"string","computed":true},"snapshot_type":{"type":"string","optional":true},"source_db_snapshot_identifier":{"type":"string","computed":true},"source_region":{"type":"string","computed":true},"status":{"type":"string","computed":true},"storage_type":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_directory_service_directory":{"version":0,"block":{"attributes":{"access_url":{"type":"string","computed":true},"alias":{"type":"string","computed":true},"connect_settings":{"type":["list",["object",{"availability_zones":["set","string"],"connect_ips":["set","string"],"customer_dns_ips":["set","string"],"customer_username":"string","subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true},"description":{"type":"string","computed":true},"directory_id":{"type":"string","required":true},"dns_ip_addresses":{"type":["set","string"],"computed":true},"edition":{"type":"string","computed":true},"enable_sso":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"security_group_id":{"type":"string","computed":true},"short_name":{"type":"string","computed":true},"size":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"type":{"type":"string","computed":true},"vpc_settings":{"type":["list",["object",{"availability_zones":["set","string"],"subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true}}}},"aws_dx_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owner_account_id":{"type":"string","computed":true}}}},"aws_dynamodb_table":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"attribute":{"type":["set",["object",{"name":"string","type":"string"}]],"computed":true},"billing_mode":{"type":"string","computed":true},"global_secondary_index":{"type":["set",["object",{"hash_key":"string","name":"string","non_key_attributes":["list","string"],"projection_type":"string","range_key":"string","read_capacity":"number","write_capacity":"number"}]],"computed":true},"hash_key":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"local_secondary_index":{"type":["set",["object",{"name":"string","non_key_attributes":["list","string"],"projection_type":"string","range_key":"string"}]],"computed":true},"name":{"type":"string","required":true},"point_in_time_recovery":{"type":["list",["object",{"enabled":"bool"}]],"computed":true},"range_key":{"type":"string","computed":true},"read_capacity":{"type":"number","computed":true},"replica":{"type":["set",["object",{"region_name":"string"}]],"computed":true},"stream_arn":{"type":"string","computed":true},"stream_enabled":{"type":"bool","computed":true},"stream_label":{"type":"string","computed":true},"stream_view_type":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"ttl":{"type":["set",["object",{"attribute_name":"string","enabled":"bool"}]],"computed":true},"write_capacity":{"type":"number","computed":true}},"block_types":{"server_side_encryption":{"nesting_mode":"list","block":{"attributes":{"enabled":{"type":"bool","computed":true},"kms_key_arn":{"type":"string","computed":true}}},"max_items":1}}}},"aws_ebs_default_kms_key":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"key_arn":{"type":"string","computed":true}}}},"aws_ebs_encryption_by_default":{"version":0,"block":{"attributes":{"enabled":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ebs_snapshot":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"data_encryption_key_id":{"type":"string","computed":true},"description":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"owner_alias":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"owners":{"type":["list","string"],"optional":true},"restorable_by_user_ids":{"type":["list","string"],"optional":true},"snapshot_id":{"type":"string","computed":true},"snapshot_ids":{"type":["list","string"],"optional":true},"state":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_size":{"type":"number","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ebs_snapshot_ids":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["list","string"],"computed":true},"owners":{"type":["list","string"],"optional":true},"restorable_by_user_ids":{"type":["list","string"],"optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ebs_volume":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"iops":{"type":"number","computed":true},"kms_key_id":{"type":"string","computed":true},"most_recent":{"type":"bool","optional":true},"multi_attach_enabled":{"type":"bool","computed":true},"outpost_arn":{"type":"string","computed":true},"size":{"type":"number","computed":true},"snapshot_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"volume_id":{"type":"string","computed":true},"volume_type":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ebs_volumes":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_coip_pool":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_gateway_route_table_id":{"type":"string","optional":true,"computed":true},"pool_cidrs":{"type":["set","string"],"computed":true},"pool_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_coip_pools":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"pool_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_instance_type_offering":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","computed":true},"location_type":{"type":"string","optional":true},"preferred_instance_types":{"type":["list","string"],"optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_instance_type_offerings":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"instance_types":{"type":["set","string"],"computed":true},"location_type":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_local_gateway":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"outpost_arn":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_route_table":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_gateway_id":{"type":"string","optional":true,"computed":true},"local_gateway_route_table_id":{"type":"string","optional":true,"computed":true},"outpost_arn":{"type":"string","optional":true,"computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_route_tables":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_virtual_interface":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_address":{"type":"string","computed":true},"local_bgp_asn":{"type":"number","computed":true},"local_gateway_id":{"type":"string","computed":true},"local_gateway_virtual_interface_ids":{"type":["set","string"],"computed":true},"peer_address":{"type":"string","computed":true},"peer_bgp_asn":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vlan":{"type":"number","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_virtual_interface_group":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"local_gateway_id":{"type":"string","optional":true,"computed":true},"local_gateway_virtual_interface_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateway_virtual_interface_groups":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"local_gateway_virtual_interface_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_local_gateways":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_spot_price":{"version":0,"block":{"attributes":{"availability_zone":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","optional":true},"spot_price":{"type":"string","computed":true},"spot_price_timestamp":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"number","computed":true},"arn":{"type":"string","computed":true},"association_default_route_table_id":{"type":"string","computed":true},"auto_accept_shared_attachments":{"type":"string","computed":true},"default_route_table_association":{"type":"string","computed":true},"default_route_table_propagation":{"type":"string","computed":true},"description":{"type":"string","computed":true},"dns_support":{"type":"string","computed":true},"id":{"type":"string","optional":true},"owner_id":{"type":"string","computed":true},"propagation_default_route_table_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpn_ecmp_support":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_dx_gateway_attachment":{"version":0,"block":{"attributes":{"dx_gateway_id":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_peering_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true},"peer_account_id":{"type":"string","computed":true},"peer_region":{"type":"string","computed":true},"peer_transit_gateway_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_route_table":{"version":0,"block":{"attributes":{"default_association_route_table":{"type":"bool","computed":true},"default_propagation_route_table":{"type":"bool","computed":true},"id":{"type":"string","optional":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_vpc_attachment":{"version":0,"block":{"attributes":{"dns_support":{"type":"string","computed":true},"id":{"type":"string","optional":true},"ipv6_support":{"type":"string","computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true},"vpc_owner_id":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ec2_transit_gateway_vpn_attachment":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"transit_gateway_id":{"type":"string","optional":true},"vpn_connection_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_ecr_authorization_token":{"version":0,"block":{"attributes":{"authorization_token":{"type":"string","computed":true,"sensitive":true},"expires_at":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"password":{"type":"string","computed":true,"sensitive":true},"proxy_endpoint":{"type":"string","computed":true},"registry_id":{"type":"string","optional":true},"user_name":{"type":"string","computed":true}}}},"aws_ecr_image":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"image_digest":{"type":"string","optional":true,"computed":true},"image_pushed_at":{"type":"number","computed":true},"image_size_in_bytes":{"type":"number","computed":true},"image_tag":{"type":"string","optional":true},"image_tags":{"type":["list","string"],"computed":true},"registry_id":{"type":"string","optional":true,"computed":true},"repository_name":{"type":"string","required":true}}}},"aws_ecr_repository":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"encryption_configuration":{"type":["list",["object",{"encryption_type":"string","kms_key":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"image_scanning_configuration":{"type":["list",["object",{"scan_on_push":"bool"}]],"computed":true},"image_tag_mutability":{"type":"string","computed":true},"name":{"type":"string","required":true},"registry_id":{"type":"string","optional":true,"computed":true},"repository_url":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_ecs_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"pending_tasks_count":{"type":"number","computed":true},"registered_container_instances_count":{"type":"number","computed":true},"running_tasks_count":{"type":"number","computed":true},"setting":{"type":["set",["object",{"name":"string","value":"string"}]],"computed":true},"status":{"type":"string","computed":true}}}},"aws_ecs_container_definition":{"version":0,"block":{"attributes":{"container_name":{"type":"string","required":true},"cpu":{"type":"number","computed":true},"disable_networking":{"type":"bool","computed":true},"docker_labels":{"type":["map","string"],"computed":true},"environment":{"type":["map","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"image":{"type":"string","computed":true},"image_digest":{"type":"string","computed":true},"memory":{"type":"number","computed":true},"memory_reservation":{"type":"number","computed":true},"task_definition":{"type":"string","required":true}}}},"aws_ecs_service":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cluster_arn":{"type":"string","required":true},"desired_count":{"type":"number","computed":true},"id":{"type":"string","optional":true,"computed":true},"launch_type":{"type":"string","computed":true},"scheduling_strategy":{"type":"string","computed":true},"service_name":{"type":"string","required":true},"task_definition":{"type":"string","computed":true}}}},"aws_ecs_task_definition":{"version":1,"block":{"attributes":{"family":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"network_mode":{"type":"string","computed":true},"revision":{"type":"number","computed":true},"status":{"type":"string","computed":true},"task_definition":{"type":"string","required":true},"task_role_arn":{"type":"string","computed":true}}}},"aws_efs_access_point":{"version":0,"block":{"attributes":{"access_point_id":{"type":"string","required":true},"arn":{"type":"string","computed":true},"file_system_arn":{"type":"string","computed":true},"file_system_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"posix_user":{"type":["list",["object",{"gid":"number","secondary_gids":["set","number"],"uid":"number"}]],"computed":true},"root_directory":{"type":["list",["object",{"creation_info":["list",["object",{"owner_gid":"number","owner_uid":"number","permissions":"string"}]],"path":"string"}]],"computed":true},"tags":{"type":["map","string"],"optional":true}}}},"aws_efs_access_points":{"version":0,"block":{"attributes":{"arns":{"type":["set","string"],"computed":true},"file_system_id":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true}}}},"aws_efs_file_system":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_token":{"type":"string","optional":true,"computed":true},"dns_name":{"type":"string","computed":true},"encrypted":{"type":"bool","computed":true},"file_system_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"lifecycle_policy":{"type":["list",["object",{"transition_to_ia":"string"}]],"computed":true},"performance_mode":{"type":"string","computed":true},"provisioned_throughput_in_mibps":{"type":"number","computed":true},"size_in_bytes":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"throughput_mode":{"type":"string","computed":true}}}},"aws_efs_mount_target":{"version":0,"block":{"attributes":{"availability_zone_id":{"type":"string","computed":true},"availability_zone_name":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"file_system_arn":{"type":"string","computed":true},"file_system_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address":{"type":"string","computed":true},"mount_target_dns_name":{"type":"string","computed":true},"mount_target_id":{"type":"string","required":true},"network_interface_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_id":{"type":"string","computed":true}}}},"aws_eip":{"version":0,"block":{"attributes":{"association_id":{"type":"string","computed":true},"customer_owned_ip":{"type":"string","computed":true},"customer_owned_ipv4_pool":{"type":"string","computed":true},"domain":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","computed":true},"network_interface_id":{"type":"string","computed":true},"network_interface_owner_id":{"type":"string","computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","optional":true,"computed":true},"public_ipv4_pool":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_eks_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_authority":{"type":["list",["object",{"data":"string"}]],"computed":true},"created_at":{"type":"string","computed":true},"enabled_cluster_log_types":{"type":["set","string"],"computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identity":{"type":["list",["object",{"oidc":["list",["object",{"issuer":"string"}]]}]],"computed":true},"name":{"type":"string","required":true},"platform_version":{"type":"string","computed":true},"role_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"version":{"type":"string","computed":true},"vpc_config":{"type":["list",["object",{"cluster_security_group_id":"string","endpoint_private_access":"bool","endpoint_public_access":"bool","public_access_cidrs":["set","string"],"security_group_ids":["set","string"],"subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true}}}},"aws_eks_cluster_auth":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"token":{"type":"string","computed":true,"sensitive":true}}}},"aws_elastic_beanstalk_application":{"version":0,"block":{"attributes":{"appversion_lifecycle":{"type":["list",["object",{"delete_source_from_s3":"bool","max_age_in_days":"number","max_count":"number","service_role":"string"}]],"computed":true},"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_elastic_beanstalk_hosted_zone":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_elastic_beanstalk_solution_stack":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"most_recent":{"type":"bool","optional":true},"name":{"type":"string","computed":true},"name_regex":{"type":"string","required":true}}}},"aws_elasticache_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","computed":true},"cache_nodes":{"type":["list",["object",{"address":"string","availability_zone":"string","id":"string","port":"number"}]],"computed":true},"cluster_address":{"type":"string","computed":true},"cluster_id":{"type":"string","required":true},"configuration_endpoint":{"type":"string","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"maintenance_window":{"type":"string","computed":true},"node_type":{"type":"string","computed":true},"notification_topic_arn":{"type":"string","computed":true},"num_cache_nodes":{"type":"number","computed":true},"parameter_group_name":{"type":"string","computed":true},"port":{"type":"number","computed":true},"replication_group_id":{"type":"string","computed":true},"security_group_ids":{"type":["set","string"],"computed":true},"security_group_names":{"type":["set","string"],"computed":true},"snapshot_retention_limit":{"type":"number","computed":true},"snapshot_window":{"type":"string","computed":true},"subnet_group_name":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_elasticache_replication_group":{"version":1,"block":{"attributes":{"auth_token_enabled":{"type":"bool","computed":true},"automatic_failover_enabled":{"type":"bool","computed":true},"configuration_endpoint_address":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"member_clusters":{"type":["set","string"],"computed":true},"node_type":{"type":"string","computed":true},"number_cache_clusters":{"type":"number","computed":true},"port":{"type":"number","computed":true},"primary_endpoint_address":{"type":"string","computed":true},"replication_group_description":{"type":"string","computed":true},"replication_group_id":{"type":"string","required":true},"snapshot_retention_limit":{"type":"number","computed":true},"snapshot_window":{"type":"string","computed":true}}}},"aws_elasticsearch_domain":{"version":0,"block":{"attributes":{"access_policies":{"type":"string","computed":true},"advanced_options":{"type":["map","string"],"computed":true},"advanced_security_options":{"type":["list",["object",{"enabled":"bool","internal_user_database_enabled":"bool"}]],"computed":true},"arn":{"type":"string","computed":true},"cluster_config":{"type":["list",["object",{"dedicated_master_count":"number","dedicated_master_enabled":"bool","dedicated_master_type":"string","instance_count":"number","instance_type":"string","warm_count":"number","warm_enabled":"bool","warm_type":"string","zone_awareness_config":["list",["object",{"availability_zone_count":"number"}]],"zone_awareness_enabled":"bool"}]],"computed":true},"cognito_options":{"type":["list",["object",{"enabled":"bool","identity_pool_id":"string","role_arn":"string","user_pool_id":"string"}]],"computed":true},"created":{"type":"bool","computed":true},"deleted":{"type":"bool","computed":true},"domain_id":{"type":"string","computed":true},"domain_name":{"type":"string","required":true},"ebs_options":{"type":["list",["object",{"ebs_enabled":"bool","iops":"number","volume_size":"number","volume_type":"string"}]],"computed":true},"elasticsearch_version":{"type":"string","computed":true},"encryption_at_rest":{"type":["list",["object",{"enabled":"bool","kms_key_id":"string"}]],"computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kibana_endpoint":{"type":"string","computed":true},"log_publishing_options":{"type":["set",["object",{"cloudwatch_log_group_arn":"string","enabled":"bool","log_type":"string"}]],"computed":true},"node_to_node_encryption":{"type":["list",["object",{"enabled":"bool"}]],"computed":true},"processing":{"type":"bool","computed":true},"snapshot_options":{"type":["list",["object",{"automated_snapshot_start_hour":"number"}]],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_options":{"type":["list",["object",{"availability_zones":["set","string"],"security_group_ids":["set","string"],"subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true}}}},"aws_elb":{"version":0,"block":{"attributes":{"access_logs":{"type":["list",["object",{"bucket":"string","bucket_prefix":"string","enabled":"bool","interval":"number"}]],"computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"connection_draining":{"type":"bool","computed":true},"connection_draining_timeout":{"type":"number","computed":true},"cross_zone_load_balancing":{"type":"bool","computed":true},"dns_name":{"type":"string","computed":true},"health_check":{"type":["list",["object",{"healthy_threshold":"number","interval":"number","target":"string","timeout":"number","unhealthy_threshold":"number"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","computed":true},"instances":{"type":["set","string"],"computed":true},"internal":{"type":"bool","computed":true},"listener":{"type":["set",["object",{"instance_port":"number","instance_protocol":"string","lb_port":"number","lb_protocol":"string","ssl_certificate_id":"string"}]],"computed":true},"name":{"type":"string","required":true},"security_groups":{"type":["set","string"],"computed":true},"source_security_group":{"type":"string","computed":true},"source_security_group_id":{"type":"string","computed":true},"subnets":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"zone_id":{"type":"string","computed":true}}}},"aws_elb_hosted_zone_id":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_elb_service_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_glue_script":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"language":{"type":"string","optional":true},"python_script":{"type":"string","computed":true},"scala_code":{"type":"string","computed":true}},"block_types":{"dag_edge":{"nesting_mode":"list","block":{"attributes":{"source":{"type":"string","required":true},"target":{"type":"string","required":true},"target_parameter":{"type":"string","optional":true}}},"min_items":1},"dag_node":{"nesting_mode":"list","block":{"attributes":{"id":{"type":"string","required":true},"line_number":{"type":"number","optional":true},"node_type":{"type":"string","required":true}},"block_types":{"args":{"nesting_mode":"list","block":{"attributes":{"name":{"type":"string","required":true},"param":{"type":"bool","optional":true},"value":{"type":"string","required":true}}},"min_items":1}}},"min_items":1}}}},"aws_guardduty_detector":{"version":0,"block":{"attributes":{"finding_publishing_frequency":{"type":"string","computed":true},"id":{"type":"string","optional":true},"service_role_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true}}}},"aws_iam_account_alias":{"version":0,"block":{"attributes":{"account_alias":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_iam_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"group_id":{"type":"string","computed":true},"group_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"path":{"type":"string","computed":true},"users":{"type":["list",["object",{"arn":"string","path":"string","user_id":"string","user_name":"string"}]],"computed":true}}}},"aws_iam_instance_profile":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"create_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"path":{"type":"string","computed":true},"role_arn":{"type":"string","computed":true},"role_id":{"type":"string","computed":true},"role_name":{"type":"string","computed":true}}}},"aws_iam_policy":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"path":{"type":"string","computed":true},"policy":{"type":"string","computed":true}}}},"aws_iam_policy_document":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"json":{"type":"string","computed":true},"override_json":{"type":"string","optional":true},"policy_id":{"type":"string","optional":true},"source_json":{"type":"string","optional":true},"version":{"type":"string","optional":true}},"block_types":{"statement":{"nesting_mode":"list","block":{"attributes":{"actions":{"type":["set","string"],"optional":true},"effect":{"type":"string","optional":true},"not_actions":{"type":["set","string"],"optional":true},"not_resources":{"type":["set","string"],"optional":true},"resources":{"type":["set","string"],"optional":true},"sid":{"type":"string","optional":true}},"block_types":{"condition":{"nesting_mode":"set","block":{"attributes":{"test":{"type":"string","required":true},"values":{"type":["set","string"],"required":true},"variable":{"type":"string","required":true}}}},"not_principals":{"nesting_mode":"set","block":{"attributes":{"identifiers":{"type":["set","string"],"required":true},"type":{"type":"string","required":true}}}},"principals":{"nesting_mode":"set","block":{"attributes":{"identifiers":{"type":["set","string"],"required":true},"type":{"type":"string","required":true}}}}}}}}}},"aws_iam_role":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"assume_role_policy":{"type":"string","computed":true},"create_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"max_session_duration":{"type":"number","computed":true},"name":{"type":"string","required":true},"path":{"type":"string","computed":true},"permissions_boundary":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"unique_id":{"type":"string","computed":true}}}},"aws_iam_server_certificate":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"certificate_body":{"type":"string","computed":true},"certificate_chain":{"type":"string","computed":true},"expiration_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"latest":{"type":"bool","optional":true},"name":{"type":"string","optional":true,"computed":true},"name_prefix":{"type":"string","optional":true},"path":{"type":"string","computed":true},"path_prefix":{"type":"string","optional":true},"upload_date":{"type":"string","computed":true}}}},"aws_iam_user":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"path":{"type":"string","computed":true},"permissions_boundary":{"type":"string","computed":true},"user_id":{"type":"string","computed":true},"user_name":{"type":"string","required":true}}}},"aws_inspector_rules_packages":{"version":0,"block":{"attributes":{"arns":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_instance":{"version":1,"block":{"attributes":{"ami":{"type":"string","computed":true},"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","computed":true},"availability_zone":{"type":"string","computed":true},"credit_specification":{"type":["list",["object",{"cpu_credits":"string"}]],"computed":true},"disable_api_termination":{"type":"bool","computed":true},"ebs_block_device":{"type":["set",["object",{"delete_on_termination":"bool","device_name":"string","encrypted":"bool","iops":"number","kms_key_id":"string","snapshot_id":"string","volume_id":"string","volume_size":"number","volume_type":"string"}]],"computed":true},"ebs_optimized":{"type":"bool","computed":true},"ephemeral_block_device":{"type":["list",["object",{"device_name":"string","no_device":"bool","virtual_name":"string"}]],"computed":true},"get_password_data":{"type":"bool","optional":true},"get_user_data":{"type":"bool","optional":true},"host_id":{"type":"string","computed":true},"iam_instance_profile":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true},"instance_state":{"type":"string","computed":true},"instance_tags":{"type":["map","string"],"optional":true,"computed":true},"instance_type":{"type":"string","computed":true},"key_name":{"type":"string","computed":true},"metadata_options":{"type":["list",["object",{"http_endpoint":"string","http_put_response_hop_limit":"number","http_tokens":"string"}]],"computed":true},"monitoring":{"type":"bool","computed":true},"network_interface_id":{"type":"string","computed":true},"outpost_arn":{"type":"string","computed":true},"password_data":{"type":"string","computed":true},"placement_group":{"type":"string","computed":true},"private_dns":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_dns":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"root_block_device":{"type":["set",["object",{"delete_on_termination":"bool","device_name":"string","encrypted":"bool","iops":"number","kms_key_id":"string","volume_id":"string","volume_size":"number","volume_type":"string"}]],"computed":true},"secondary_private_ips":{"type":["set","string"],"computed":true},"security_groups":{"type":["set","string"],"computed":true},"source_dest_check":{"type":"bool","computed":true},"subnet_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"tenancy":{"type":"string","computed":true},"user_data":{"type":"string","computed":true},"user_data_base64":{"type":"string","computed":true},"vpc_security_group_ids":{"type":["set","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_instances":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["list","string"],"computed":true},"instance_state_names":{"type":["set","string"],"optional":true},"instance_tags":{"type":["map","string"],"optional":true,"computed":true},"private_ips":{"type":["list","string"],"computed":true},"public_ips":{"type":["list","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_internet_gateway":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"attachments":{"type":["list",["object",{"state":"string","vpc_id":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"internet_gateway_id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_iot_endpoint":{"version":0,"block":{"attributes":{"endpoint_address":{"type":"string","computed":true},"endpoint_type":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_ip_ranges":{"version":0,"block":{"attributes":{"cidr_blocks":{"type":["list","string"],"computed":true},"create_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_blocks":{"type":["list","string"],"computed":true},"regions":{"type":["set","string"],"optional":true},"services":{"type":["set","string"],"required":true},"sync_token":{"type":"number","computed":true},"url":{"type":"string","optional":true}}}},"aws_kinesis_stream":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"closed_shards":{"type":["set","string"],"computed":true},"creation_timestamp":{"type":"number","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"open_shards":{"type":["set","string"],"computed":true},"retention_period":{"type":"number","computed":true},"shard_level_metrics":{"type":["set","string"],"computed":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_kms_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"target_key_arn":{"type":"string","computed":true},"target_key_id":{"type":"string","computed":true}}}},"aws_kms_ciphertext":{"version":0,"block":{"attributes":{"ciphertext_blob":{"type":"string","computed":true},"context":{"type":["map","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"plaintext":{"type":"string","required":true,"sensitive":true}}}},"aws_kms_key":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"aws_account_id":{"type":"string","computed":true},"creation_date":{"type":"string","computed":true},"customer_master_key_spec":{"type":"string","computed":true},"deletion_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"enabled":{"type":"bool","computed":true},"expiration_model":{"type":"string","computed":true},"grant_tokens":{"type":["list","string"],"optional":true},"id":{"type":"string","optional":true,"computed":true},"key_id":{"type":"string","required":true},"key_manager":{"type":"string","computed":true},"key_state":{"type":"string","computed":true},"key_usage":{"type":"string","computed":true},"origin":{"type":"string","computed":true},"valid_to":{"type":"string","computed":true}}}},"aws_kms_secret":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true}},"block_types":{"secret":{"nesting_mode":"set","block":{"attributes":{"context":{"type":["map","string"],"optional":true},"grant_tokens":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"payload":{"type":"string","required":true}}},"min_items":1}}}},"aws_kms_secrets":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"plaintext":{"type":["map","string"],"computed":true}},"block_types":{"secret":{"nesting_mode":"set","block":{"attributes":{"context":{"type":["map","string"],"optional":true},"grant_tokens":{"type":["list","string"],"optional":true},"name":{"type":"string","required":true},"payload":{"type":"string","required":true}}},"min_items":1}}}},"aws_lambda_alias":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"function_name":{"type":"string","required":true},"function_version":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_arn":{"type":"string","computed":true},"name":{"type":"string","required":true}}}},"aws_lambda_function":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"dead_letter_config":{"type":["list",["object",{"target_arn":"string"}]],"computed":true},"description":{"type":"string","computed":true},"environment":{"type":["list",["object",{"variables":["map","string"]}]],"computed":true},"file_system_config":{"type":["list",["object",{"arn":"string","local_mount_path":"string"}]],"computed":true},"function_name":{"type":"string","required":true},"handler":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"invoke_arn":{"type":"string","computed":true},"kms_key_arn":{"type":"string","computed":true},"last_modified":{"type":"string","computed":true},"layers":{"type":["list","string"],"computed":true},"memory_size":{"type":"number","computed":true},"qualified_arn":{"type":"string","computed":true},"qualifier":{"type":"string","optional":true},"reserved_concurrent_executions":{"type":"number","computed":true},"role":{"type":"string","computed":true},"runtime":{"type":"string","computed":true},"source_code_hash":{"type":"string","computed":true},"source_code_size":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"timeout":{"type":"number","computed":true},"tracing_config":{"type":["list",["object",{"mode":"string"}]],"computed":true},"version":{"type":"string","computed":true},"vpc_config":{"type":["list",["object",{"security_group_ids":["set","string"],"subnet_ids":["set","string"],"vpc_id":"string"}]],"computed":true}}}},"aws_lambda_invocation":{"version":0,"block":{"attributes":{"function_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"input":{"type":"string","required":true},"qualifier":{"type":"string","optional":true},"result":{"type":"string","computed":true}}}},"aws_lambda_layer_version":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"compatible_runtime":{"type":"string","optional":true},"compatible_runtimes":{"type":["set","string"],"computed":true},"created_date":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"layer_arn":{"type":"string","computed":true},"layer_name":{"type":"string","required":true},"license_info":{"type":"string","computed":true},"source_code_hash":{"type":"string","computed":true},"source_code_size":{"type":"number","computed":true},"version":{"type":"number","optional":true,"computed":true}}}},"aws_launch_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"associate_public_ip_address":{"type":"bool","computed":true},"ebs_block_device":{"type":["set",["object",{"delete_on_termination":"bool","device_name":"string","encrypted":"bool","iops":"number","no_device":"bool","snapshot_id":"string","volume_size":"number","volume_type":"string"}]],"computed":true},"ebs_optimized":{"type":"bool","computed":true},"enable_monitoring":{"type":"bool","computed":true},"ephemeral_block_device":{"type":["set",["object",{"device_name":"string","virtual_name":"string"}]],"computed":true},"iam_instance_profile":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","computed":true},"instance_type":{"type":"string","computed":true},"key_name":{"type":"string","computed":true},"name":{"type":"string","required":true},"placement_tenancy":{"type":"string","computed":true},"root_block_device":{"type":["list",["object",{"delete_on_termination":"bool","encrypted":"bool","iops":"number","volume_size":"number","volume_type":"string"}]],"computed":true},"security_groups":{"type":["set","string"],"computed":true},"spot_price":{"type":"string","computed":true},"user_data":{"type":"string","computed":true},"vpc_classic_link_id":{"type":"string","computed":true},"vpc_classic_link_security_groups":{"type":["set","string"],"computed":true}}}},"aws_launch_template":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"block_device_mappings":{"type":["list",["object",{"device_name":"string","ebs":["list",["object",{"delete_on_termination":"string","encrypted":"string","iops":"number","kms_key_id":"string","snapshot_id":"string","volume_size":"number","volume_type":"string"}]],"no_device":"string","virtual_name":"string"}]],"computed":true},"credit_specification":{"type":["list",["object",{"cpu_credits":"string"}]],"computed":true},"default_version":{"type":"number","computed":true},"description":{"type":"string","computed":true},"disable_api_termination":{"type":"bool","computed":true},"ebs_optimized":{"type":"string","computed":true},"elastic_gpu_specifications":{"type":["list",["object",{"type":"string"}]],"computed":true},"hibernation_options":{"type":["list",["object",{"configured":"bool"}]],"computed":true},"iam_instance_profile":{"type":["list",["object",{"arn":"string","name":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"image_id":{"type":"string","computed":true},"instance_initiated_shutdown_behavior":{"type":"string","computed":true},"instance_market_options":{"type":["list",["object",{"market_type":"string","spot_options":["list",["object",{"block_duration_minutes":"number","instance_interruption_behavior":"string","max_price":"string","spot_instance_type":"string","valid_until":"string"}]]}]],"computed":true},"instance_type":{"type":"string","computed":true},"kernel_id":{"type":"string","computed":true},"key_name":{"type":"string","computed":true},"latest_version":{"type":"number","computed":true},"metadata_options":{"type":["list",["object",{"http_endpoint":"string","http_put_response_hop_limit":"number","http_tokens":"string"}]],"computed":true},"monitoring":{"type":["list",["object",{"enabled":"bool"}]],"computed":true},"name":{"type":"string","optional":true},"network_interfaces":{"type":["list",["object",{"associate_public_ip_address":"string","delete_on_termination":"string","description":"string","device_index":"number","ipv4_address_count":"number","ipv4_addresses":["set","string"],"ipv6_address_count":"number","ipv6_addresses":["set","string"],"network_interface_id":"string","private_ip_address":"string","security_groups":["set","string"],"subnet_id":"string"}]],"computed":true},"placement":{"type":["list",["object",{"affinity":"string","availability_zone":"string","group_name":"string","host_id":"string","partition_number":"number","spread_domain":"string","tenancy":"string"}]],"computed":true},"ram_disk_id":{"type":"string","computed":true},"security_group_names":{"type":["set","string"],"computed":true},"tag_specifications":{"type":["list",["object",{"resource_type":"string","tags":["map","string"]}]],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"user_data":{"type":"string","computed":true},"vpc_security_group_ids":{"type":["set","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_lb":{"version":0,"block":{"attributes":{"access_logs":{"type":["list",["object",{"bucket":"string","enabled":"bool","prefix":"string"}]],"computed":true},"arn":{"type":"string","optional":true,"computed":true},"arn_suffix":{"type":"string","computed":true},"dns_name":{"type":"string","computed":true},"drop_invalid_header_fields":{"type":"bool","computed":true},"enable_deletion_protection":{"type":"bool","computed":true},"enable_http2":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"idle_timeout":{"type":"number","computed":true},"internal":{"type":"bool","computed":true},"ip_address_type":{"type":"string","computed":true},"load_balancer_type":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_mapping":{"type":["set",["object",{"allocation_id":"string","private_ipv4_address":"string","subnet_id":"string"}]],"computed":true},"subnets":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","computed":true},"zone_id":{"type":"string","computed":true}}}},"aws_lb_listener":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"certificate_arn":{"type":"string","computed":true},"default_action":{"type":["list",["object",{"authenticate_cognito":["list",["object",{"authentication_request_extra_params":["map","string"],"on_unauthenticated_request":"string","scope":"string","session_cookie_name":"string","session_timeout":"number","user_pool_arn":"string","user_pool_client_id":"string","user_pool_domain":"string"}]],"authenticate_oidc":["list",["object",{"authentication_request_extra_params":["map","string"],"authorization_endpoint":"string","client_id":"string","client_secret":"string","issuer":"string","on_unauthenticated_request":"string","scope":"string","session_cookie_name":"string","session_timeout":"number","token_endpoint":"string","user_info_endpoint":"string"}]],"fixed_response":["list",["object",{"content_type":"string","message_body":"string","status_code":"string"}]],"order":"number","redirect":["list",["object",{"host":"string","path":"string","port":"string","protocol":"string","query":"string","status_code":"string"}]],"target_group_arn":"string","type":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"load_balancer_arn":{"type":"string","optional":true,"computed":true},"port":{"type":"number","optional":true,"computed":true},"protocol":{"type":"string","computed":true},"ssl_policy":{"type":"string","computed":true}}}},"aws_lb_target_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"arn_suffix":{"type":"string","computed":true},"deregistration_delay":{"type":"number","computed":true},"health_check":{"type":["list",["object",{"enabled":"bool","healthy_threshold":"number","interval":"number","matcher":"string","path":"string","port":"string","protocol":"string","timeout":"number","unhealthy_threshold":"number"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"lambda_multi_value_headers_enabled":{"type":"bool","computed":true},"load_balancing_algorithm_type":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"port":{"type":"number","computed":true},"protocol":{"type":"string","computed":true},"proxy_protocol_v2":{"type":"bool","computed":true},"slow_start":{"type":"number","computed":true},"stickiness":{"type":["list",["object",{"cookie_duration":"number","enabled":"bool","type":"string"}]],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"target_type":{"type":"string","computed":true},"vpc_id":{"type":"string","computed":true}}}},"aws_mq_broker":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"auto_minor_version_upgrade":{"type":"bool","computed":true},"broker_id":{"type":"string","optional":true,"computed":true},"broker_name":{"type":"string","optional":true,"computed":true},"configuration":{"type":["list",["object",{"id":"string","revision":"number"}]],"computed":true},"deployment_mode":{"type":"string","computed":true},"encryption_options":{"type":["list",["object",{"kms_key_id":"string","use_aws_owned_key":"bool"}]],"computed":true},"engine_type":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"host_instance_type":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"instances":{"type":["list",["object",{"console_url":"string","endpoints":["list","string"],"ip_address":"string"}]],"computed":true},"maintenance_window_start_time":{"type":["list",["object",{"day_of_week":"string","time_of_day":"string","time_zone":"string"}]],"computed":true},"publicly_accessible":{"type":"bool","computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"user":{"type":["set",["object",{"console_access":"bool","groups":["set","string"],"username":"string"}]],"computed":true}},"block_types":{"logs":{"nesting_mode":"list","block":{"attributes":{"audit":{"type":"bool","computed":true},"general":{"type":"bool","computed":true}}},"max_items":1}}}},"aws_msk_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bootstrap_brokers":{"type":"string","computed":true},"bootstrap_brokers_tls":{"type":"string","computed":true},"cluster_name":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"kafka_version":{"type":"string","computed":true},"number_of_broker_nodes":{"type":"number","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"zookeeper_connect_string":{"type":"string","computed":true}}}},"aws_msk_configuration":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kafka_versions":{"type":["set","string"],"computed":true},"latest_revision":{"type":"number","computed":true},"name":{"type":"string","required":true},"server_properties":{"type":"string","computed":true}}}},"aws_nat_gateway":{"version":0,"block":{"attributes":{"allocation_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"public_ip":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_network_acls":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_network_interface":{"version":0,"block":{"attributes":{"association":{"type":["list",["object",{"allocation_id":"string","association_id":"string","ip_owner_id":"string","public_dns_name":"string","public_ip":"string"}]],"computed":true},"attachment":{"type":["list",["object",{"attachment_id":"string","device_index":"number","instance_id":"string","instance_owner_id":"string"}]],"computed":true},"availability_zone":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"interface_type":{"type":"string","computed":true},"ipv6_addresses":{"type":["set","string"],"computed":true},"mac_address":{"type":"string","computed":true},"outpost_arn":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"private_dns_name":{"type":"string","computed":true},"private_ip":{"type":"string","computed":true},"private_ips":{"type":["list","string"],"computed":true},"requester_id":{"type":"string","computed":true},"security_groups":{"type":["set","string"],"computed":true},"subnet_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_network_interfaces":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_organizations_organization":{"version":0,"block":{"attributes":{"accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string","status":"string"}]],"computed":true},"arn":{"type":"string","computed":true},"aws_service_access_principals":{"type":["set","string"],"computed":true},"enabled_policy_types":{"type":["set","string"],"computed":true},"feature_set":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"master_account_arn":{"type":"string","computed":true},"master_account_email":{"type":"string","computed":true},"master_account_id":{"type":"string","computed":true},"non_master_accounts":{"type":["list",["object",{"arn":"string","email":"string","id":"string","name":"string","status":"string"}]],"computed":true},"roots":{"type":["list",["object",{"arn":"string","id":"string","name":"string","policy_types":["list",["object",{"status":"string","type":"string"}]]}]],"computed":true}}}},"aws_organizations_organizational_units":{"version":0,"block":{"attributes":{"children":{"type":["list",["object",{"arn":"string","id":"string","name":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"parent_id":{"type":"string","required":true}}}},"aws_outposts_outpost":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zone":{"type":"string","computed":true},"availability_zone_id":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"site_id":{"type":"string","computed":true}}}},"aws_outposts_outpost_instance_type":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instance_type":{"type":"string","optional":true,"computed":true},"preferred_instance_types":{"type":["list","string"],"optional":true}}}},"aws_outposts_outpost_instance_types":{"version":0,"block":{"attributes":{"arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true},"instance_types":{"type":["set","string"],"computed":true}}}},"aws_outposts_outposts":{"version":0,"block":{"attributes":{"arns":{"type":["set","string"],"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"availability_zone_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"site_id":{"type":"string","optional":true,"computed":true}}}},"aws_outposts_site":{"version":0,"block":{"attributes":{"account_id":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true}}}},"aws_outposts_sites":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true}}}},"aws_partition":{"version":0,"block":{"attributes":{"dns_suffix":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"partition":{"type":"string","computed":true}}}},"aws_prefix_list":{"version":0,"block":{"attributes":{"cidr_blocks":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"prefix_list_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_pricing_product":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"result":{"type":"string","computed":true},"service_code":{"type":"string","required":true}},"block_types":{"filters":{"nesting_mode":"list","block":{"attributes":{"field":{"type":"string","required":true},"value":{"type":"string","required":true}}},"min_items":1}}}},"aws_qldb_ledger":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"deletion_protection":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_ram_resource_share":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"owning_account_id":{"type":"string","computed":true},"resource_owner":{"type":"string","required":true},"status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_rds_cluster":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"backtrack_window":{"type":"number","computed":true},"backup_retention_period":{"type":"number","computed":true},"cluster_identifier":{"type":"string","required":true},"cluster_members":{"type":["set","string"],"computed":true},"cluster_resource_id":{"type":"string","computed":true},"database_name":{"type":"string","computed":true},"db_cluster_parameter_group_name":{"type":"string","computed":true},"db_subnet_group_name":{"type":"string","computed":true},"enabled_cloudwatch_logs_exports":{"type":["list","string"],"computed":true},"endpoint":{"type":"string","computed":true},"engine":{"type":"string","computed":true},"engine_version":{"type":"string","computed":true},"final_snapshot_identifier":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"iam_database_authentication_enabled":{"type":"bool","computed":true},"iam_roles":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"master_username":{"type":"string","computed":true},"port":{"type":"number","computed":true},"preferred_backup_window":{"type":"string","computed":true},"preferred_maintenance_window":{"type":"string","computed":true},"reader_endpoint":{"type":"string","computed":true},"replication_source_identifier":{"type":"string","computed":true},"storage_encrypted":{"type":"bool","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_security_group_ids":{"type":["set","string"],"computed":true}}}},"aws_redshift_cluster":{"version":0,"block":{"attributes":{"allow_version_upgrade":{"type":"bool","computed":true},"automated_snapshot_retention_period":{"type":"number","computed":true},"availability_zone":{"type":"string","computed":true},"bucket_name":{"type":"string","computed":true},"cluster_identifier":{"type":"string","required":true},"cluster_parameter_group_name":{"type":"string","computed":true},"cluster_public_key":{"type":"string","computed":true},"cluster_revision_number":{"type":"string","computed":true},"cluster_security_groups":{"type":["list","string"],"computed":true},"cluster_subnet_group_name":{"type":"string","computed":true},"cluster_type":{"type":"string","computed":true},"cluster_version":{"type":"string","computed":true},"database_name":{"type":"string","computed":true},"elastic_ip":{"type":"string","computed":true},"enable_logging":{"type":"bool","computed":true},"encrypted":{"type":"bool","computed":true},"endpoint":{"type":"string","computed":true},"enhanced_vpc_routing":{"type":"bool","computed":true},"iam_roles":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"master_username":{"type":"string","computed":true},"node_type":{"type":"string","computed":true},"number_of_nodes":{"type":"number","computed":true},"port":{"type":"number","computed":true},"preferred_maintenance_window":{"type":"string","computed":true},"publicly_accessible":{"type":"bool","computed":true},"s3_key_prefix":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true},"vpc_id":{"type":"string","computed":true},"vpc_security_group_ids":{"type":["list","string"],"computed":true}}}},"aws_redshift_service_account":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true}}}},"aws_region":{"version":0,"block":{"attributes":{"description":{"type":"string","computed":true},"endpoint":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true}}}},"aws_regions":{"version":0,"block":{"attributes":{"all_regions":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"names":{"type":["set","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_route":{"version":0,"block":{"attributes":{"destination_cidr_block":{"type":"string","optional":true,"computed":true},"destination_ipv6_cidr_block":{"type":"string","optional":true,"computed":true},"egress_only_gateway_id":{"type":"string","optional":true,"computed":true},"gateway_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_id":{"type":"string","optional":true,"computed":true},"nat_gateway_id":{"type":"string","optional":true,"computed":true},"network_interface_id":{"type":"string","optional":true,"computed":true},"route_table_id":{"type":"string","required":true},"transit_gateway_id":{"type":"string","optional":true,"computed":true},"vpc_peering_connection_id":{"type":"string","optional":true,"computed":true}}}},"aws_route53_delegation_set":{"version":0,"block":{"attributes":{"caller_reference":{"type":"string","computed":true},"id":{"type":"string","required":true},"name_servers":{"type":["list","string"],"computed":true}}}},"aws_route53_resolver_rule":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"domain_name":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"resolver_endpoint_id":{"type":"string","optional":true,"computed":true},"resolver_rule_id":{"type":"string","optional":true,"computed":true},"rule_type":{"type":"string","optional":true,"computed":true},"share_status":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}}}},"aws_route53_resolver_rules":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","optional":true},"resolver_endpoint_id":{"type":"string","optional":true},"resolver_rule_ids":{"type":["set","string"],"computed":true},"rule_type":{"type":"string","optional":true},"share_status":{"type":"string","optional":true}}}},"aws_route53_zone":{"version":0,"block":{"attributes":{"caller_reference":{"type":"string","computed":true},"comment":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"linked_service_description":{"type":"string","computed":true},"linked_service_principal":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"name_servers":{"type":["list","string"],"computed":true},"private_zone":{"type":"bool","optional":true},"resource_record_set_count":{"type":"number","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true},"zone_id":{"type":"string","optional":true,"computed":true}}}},"aws_route_table":{"version":0,"block":{"attributes":{"associations":{"type":["list",["object",{"gateway_id":"string","main":"bool","route_table_association_id":"string","route_table_id":"string","subnet_id":"string"}]],"computed":true},"gateway_id":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","computed":true},"route_table_id":{"type":"string","optional":true,"computed":true},"routes":{"type":["list",["object",{"cidr_block":"string","egress_only_gateway_id":"string","gateway_id":"string","instance_id":"string","ipv6_cidr_block":"string","nat_gateway_id":"string","network_interface_id":"string","transit_gateway_id":"string","vpc_peering_connection_id":"string"}]],"computed":true},"subnet_id":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_route_tables":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_s3_bucket":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"bucket":{"type":"string","required":true},"bucket_domain_name":{"type":"string","computed":true},"bucket_regional_domain_name":{"type":"string","computed":true},"hosted_zone_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","computed":true},"website_domain":{"type":"string","computed":true},"website_endpoint":{"type":"string","computed":true}}}},"aws_s3_bucket_object":{"version":0,"block":{"attributes":{"body":{"type":"string","computed":true},"bucket":{"type":"string","required":true},"cache_control":{"type":"string","computed":true},"content_disposition":{"type":"string","computed":true},"content_encoding":{"type":"string","computed":true},"content_language":{"type":"string","computed":true},"content_length":{"type":"number","computed":true},"content_type":{"type":"string","computed":true},"etag":{"type":"string","computed":true},"expiration":{"type":"string","computed":true},"expires":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"key":{"type":"string","required":true},"last_modified":{"type":"string","computed":true},"metadata":{"type":["map","string"],"computed":true},"object_lock_legal_hold_status":{"type":"string","computed":true},"object_lock_mode":{"type":"string","computed":true},"object_lock_retain_until_date":{"type":"string","computed":true},"range":{"type":"string","optional":true},"server_side_encryption":{"type":"string","computed":true},"sse_kms_key_id":{"type":"string","computed":true},"storage_class":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"version_id":{"type":"string","optional":true,"computed":true},"website_redirect_location":{"type":"string","computed":true}}}},"aws_s3_bucket_objects":{"version":0,"block":{"attributes":{"bucket":{"type":"string","required":true},"common_prefixes":{"type":["list","string"],"computed":true},"delimiter":{"type":"string","optional":true},"encoding_type":{"type":"string","optional":true},"fetch_owner":{"type":"bool","optional":true},"id":{"type":"string","optional":true,"computed":true},"keys":{"type":["list","string"],"computed":true},"max_keys":{"type":"number","optional":true},"owners":{"type":["list","string"],"computed":true},"prefix":{"type":"string","optional":true},"start_after":{"type":"string","optional":true}}}},"aws_secretsmanager_secret":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"kms_key_id":{"type":"string","computed":true},"name":{"type":"string","optional":true,"computed":true},"policy":{"type":"string","computed":true},"rotation_enabled":{"type":"bool","computed":true},"rotation_lambda_arn":{"type":"string","computed":true},"rotation_rules":{"type":["list",["object",{"automatically_after_days":"number"}]],"computed":true},"tags":{"type":["map","string"],"computed":true}}}},"aws_secretsmanager_secret_rotation":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"rotation_enabled":{"type":"bool","computed":true},"rotation_lambda_arn":{"type":"string","computed":true},"rotation_rules":{"type":["list",["object",{"automatically_after_days":"number"}]],"computed":true},"secret_id":{"type":"string","required":true}}}},"aws_secretsmanager_secret_version":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"secret_binary":{"type":"string","computed":true,"sensitive":true},"secret_id":{"type":"string","required":true},"secret_string":{"type":"string","computed":true,"sensitive":true},"version_id":{"type":"string","optional":true,"computed":true},"version_stage":{"type":"string","optional":true},"version_stages":{"type":["set","string"],"computed":true}}}},"aws_security_group":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_security_groups":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["list","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_ids":{"type":["list","string"],"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_servicequotas_service":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"service_code":{"type":"string","computed":true},"service_name":{"type":"string","required":true}}}},"aws_servicequotas_service_quota":{"version":0,"block":{"attributes":{"adjustable":{"type":"bool","computed":true},"arn":{"type":"string","computed":true},"default_value":{"type":"number","computed":true},"global_quota":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"quota_code":{"type":"string","optional":true,"computed":true},"quota_name":{"type":"string","optional":true,"computed":true},"service_code":{"type":"string","required":true},"service_name":{"type":"string","computed":true},"value":{"type":"number","computed":true}}}},"aws_sfn_activity":{"version":0,"block":{"attributes":{"arn":{"type":"string","optional":true,"computed":true},"creation_date":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true,"computed":true}}}},"aws_sfn_state_machine":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"creation_date":{"type":"string","computed":true},"definition":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"role_arn":{"type":"string","computed":true},"status":{"type":"string","computed":true}}}},"aws_sns_topic":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_sqs_queue":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"url":{"type":"string","computed":true}}}},"aws_ssm_document":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"content":{"type":"string","computed":true},"document_format":{"type":"string","optional":true},"document_type":{"type":"string","computed":true},"document_version":{"type":"string","optional":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_ssm_parameter":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"type":{"type":"string","computed":true},"value":{"type":"string","computed":true,"sensitive":true},"version":{"type":"number","computed":true},"with_decryption":{"type":"bool","optional":true}}}},"aws_ssm_patch_baseline":{"version":0,"block":{"attributes":{"default_baseline":{"type":"bool","optional":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","computed":true},"name_prefix":{"type":"string","optional":true},"operating_system":{"type":"string","optional":true},"owner":{"type":"string","required":true}}}},"aws_storagegateway_local_disk":{"version":0,"block":{"attributes":{"disk_id":{"type":"string","computed":true},"disk_node":{"type":"string","optional":true},"disk_path":{"type":"string","optional":true},"gateway_arn":{"type":"string","required":true},"id":{"type":"string","optional":true,"computed":true}}}},"aws_subnet":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"assign_ipv6_address_on_creation":{"type":"bool","computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"availability_zone_id":{"type":"string","optional":true,"computed":true},"cidr_block":{"type":"string","optional":true,"computed":true},"default_for_az":{"type":"bool","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block":{"type":"string","optional":true,"computed":true},"ipv6_cidr_block_association_id":{"type":"string","computed":true},"map_public_ip_on_launch":{"type":"bool","computed":true},"outpost_arn":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_subnet_ids":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","required":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_transfer_server":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"endpoint":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"identity_provider_type":{"type":"string","computed":true},"invocation_role":{"type":"string","computed":true},"logging_role":{"type":"string","computed":true},"server_id":{"type":"string","required":true},"url":{"type":"string","computed":true}}}},"aws_vpc":{"version":1,"block":{"attributes":{"arn":{"type":"string","computed":true},"cidr_block":{"type":"string","optional":true,"computed":true},"cidr_block_associations":{"type":["list",["object",{"association_id":"string","cidr_block":"string","state":"string"}]],"computed":true},"default":{"type":"bool","optional":true,"computed":true},"dhcp_options_id":{"type":"string","optional":true,"computed":true},"enable_dns_hostnames":{"type":"bool","computed":true},"enable_dns_support":{"type":"bool","computed":true},"id":{"type":"string","optional":true,"computed":true},"instance_tenancy":{"type":"string","computed":true},"ipv6_association_id":{"type":"string","computed":true},"ipv6_cidr_block":{"type":"string","computed":true},"main_route_table_id":{"type":"string","computed":true},"owner_id":{"type":"string","computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpc_dhcp_options":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"dhcp_options_id":{"type":"string","optional":true,"computed":true},"domain_name":{"type":"string","computed":true},"domain_name_servers":{"type":["list","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"netbios_name_servers":{"type":["list","string"],"computed":true},"netbios_node_type":{"type":"string","computed":true},"ntp_servers":{"type":["list","string"],"computed":true},"owner_id":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpc_endpoint":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"cidr_blocks":{"type":["list","string"],"computed":true},"dns_entry":{"type":["list",["object",{"dns_name":"string","hosted_zone_id":"string"}]],"computed":true},"id":{"type":"string","optional":true,"computed":true},"network_interface_ids":{"type":["set","string"],"computed":true},"owner_id":{"type":"string","computed":true},"policy":{"type":"string","computed":true},"prefix_list_id":{"type":"string","computed":true},"private_dns_enabled":{"type":"bool","computed":true},"requester_managed":{"type":"bool","computed":true},"route_table_ids":{"type":["set","string"],"computed":true},"security_group_ids":{"type":["set","string"],"computed":true},"service_name":{"type":"string","optional":true,"computed":true},"state":{"type":"string","optional":true,"computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_endpoint_type":{"type":"string","computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpc_endpoint_service":{"version":0,"block":{"attributes":{"acceptance_required":{"type":"bool","computed":true},"arn":{"type":"string","computed":true},"availability_zones":{"type":["set","string"],"computed":true},"base_endpoint_dns_names":{"type":["set","string"],"computed":true},"id":{"type":"string","optional":true,"computed":true},"manages_vpc_endpoints":{"type":"bool","computed":true},"owner":{"type":"string","computed":true},"private_dns_name":{"type":"string","computed":true},"service":{"type":"string","optional":true},"service_id":{"type":"string","computed":true},"service_name":{"type":"string","optional":true,"computed":true},"service_type":{"type":"string","computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_endpoint_policy_supported":{"type":"bool","computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["list","string"],"required":true}}}}}}},"aws_vpc_peering_connection":{"version":0,"block":{"attributes":{"accepter":{"type":["map","bool"],"computed":true},"cidr_block":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"owner_id":{"type":"string","optional":true,"computed":true},"peer_cidr_block":{"type":"string","optional":true,"computed":true},"peer_owner_id":{"type":"string","optional":true,"computed":true},"peer_region":{"type":"string","optional":true,"computed":true},"peer_vpc_id":{"type":"string","optional":true,"computed":true},"region":{"type":"string","optional":true,"computed":true},"requester":{"type":["map","bool"],"computed":true},"status":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true},"vpc_id":{"type":"string","optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpcs":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_vpn_gateway":{"version":0,"block":{"attributes":{"amazon_side_asn":{"type":"string","optional":true,"computed":true},"arn":{"type":"string","computed":true},"attached_vpc_id":{"type":"string","optional":true,"computed":true},"availability_zone":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"state":{"type":"string","optional":true,"computed":true},"tags":{"type":["map","string"],"optional":true,"computed":true}},"block_types":{"filter":{"nesting_mode":"set","block":{"attributes":{"name":{"type":"string","required":true},"values":{"type":["set","string"],"required":true}}}}}}},"aws_waf_ipset":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_waf_rate_based_rule":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_waf_rule":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_waf_web_acl":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafregional_ipset":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafregional_rate_based_rule":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafregional_rule":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafregional_web_acl":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true}}}},"aws_wafv2_ip_set":{"version":0,"block":{"attributes":{"addresses":{"type":["set","string"],"computed":true},"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_address_version":{"type":"string","computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true}}}},"aws_wafv2_regex_pattern_set":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"regular_expression":{"type":["set",["object",{"regex_string":"string"}]],"computed":true},"scope":{"type":"string","required":true}}}},"aws_wafv2_rule_group":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true}}}},"aws_wafv2_web_acl":{"version":0,"block":{"attributes":{"arn":{"type":"string","computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","required":true},"scope":{"type":"string","required":true}}}},"aws_workspaces_bundle":{"version":0,"block":{"attributes":{"bundle_id":{"type":"string","optional":true},"compute_type":{"type":["list",["object",{"name":"string"}]],"computed":true},"description":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"name":{"type":"string","optional":true},"owner":{"type":"string","optional":true},"root_storage":{"type":["list",["object",{"capacity":"string"}]],"computed":true},"user_storage":{"type":["list",["object",{"capacity":"string"}]],"computed":true}}}},"aws_workspaces_directory":{"version":0,"block":{"attributes":{"alias":{"type":"string","computed":true},"customer_user_name":{"type":"string","computed":true},"directory_id":{"type":"string","required":true},"directory_name":{"type":"string","computed":true},"directory_type":{"type":"string","computed":true},"dns_ip_addresses":{"type":["set","string"],"computed":true},"iam_role_id":{"type":"string","computed":true},"id":{"type":"string","optional":true,"computed":true},"ip_group_ids":{"type":["set","string"],"computed":true},"registration_code":{"type":"string","computed":true},"self_service_permissions":{"type":["list",["object",{"change_compute_type":"bool","increase_volume_size":"bool","rebuild_workspace":"bool","restart_workspace":"bool","switch_running_mode":"bool"}]],"computed":true},"subnet_ids":{"type":["set","string"],"computed":true},"tags":{"type":["map","string"],"optional":true},"workspace_security_group_id":{"type":"string","computed":true}}}}}},"null":{"provider":{"version":0,"block":{}},"resource_schemas":{"null_resource":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"triggers":{"type":["map","string"],"optional":true}}}}},"data_source_schemas":{"null_data_source":{"version":0,"block":{"attributes":{"has_computed_default":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"inputs":{"type":["map","string"],"optional":true},"outputs":{"type":["map","string"],"computed":true},"random":{"type":"string","computed":true}}}}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/state.json b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/state.json new file mode 100644 index 00000000..447d9ffc --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/state.json @@ -0,0 +1 @@ +{"format_version":"0.1","terraform_version":"0.12.0","values":{"outputs":{"foo":{"sensitive":true,"value":"bar"},"interpolated":{"sensitive":false,"value":"424881806176056736"},"interpolated_deep":{"sensitive":false,"value":{"foo":"bar","map":{"bar":"baz","id":"424881806176056736"},"number":42}},"list":{"sensitive":false,"value":["foo","bar"]},"map":{"sensitive":false,"value":{"foo":"bar","number":42}},"referenced":{"sensitive":false,"value":"424881806176056736"},"referenced_deep":{"sensitive":false,"value":{"foo":"bar","map":{"bar":"baz","id":"424881806176056736"},"number":42}},"string":{"sensitive":false,"value":"foo"}},"root_module":{"resources":[{"address":"data.null_data_source.baz","mode":"data","type":"null_data_source","name":"baz","provider_name":"null","schema_version":0,"values":{"has_computed_default":"default","id":"static","inputs":{"bar_id":"4347220156304926627","foo_id":"424881806176056736"},"outputs":{"bar_id":"4347220156304926627","foo_id":"424881806176056736"},"random":"1951353658349486401"}},{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_name":"null","schema_version":0,"values":{"id":"4347220156304926627","triggers":{"foo_id":"424881806176056736"}}},{"address":"null_resource.baz","mode":"managed","type":"null_resource","name":"baz","index":0,"provider_name":"null","schema_version":0,"values":{"id":"751901236459396488","triggers":{"foo_id":"424881806176056736"}}},{"address":"null_resource.baz","mode":"managed","type":"null_resource","name":"baz","index":1,"provider_name":"null","schema_version":0,"values":{"id":"2106740714798375541","triggers":{"foo_id":"424881806176056736"}}},{"address":"null_resource.baz","mode":"managed","type":"null_resource","name":"baz","index":2,"provider_name":"null","schema_version":0,"values":{"id":"8665755682221598193","triggers":{"foo_id":"424881806176056736"}}},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","schema_version":0,"values":{"id":"424881806176056736","triggers":{"foo":"bar"}}}],"child_modules":[{"resources":[{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","schema_version":0,"values":{"id":"705267318028962447","triggers":{"foo":"bar"}}}],"address":"module.foo"}]}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/terraform.tfstate b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/terraform.tfstate new file mode 100644 index 00000000..3d7f472d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/terraform.tfstate @@ -0,0 +1,232 @@ +{ + "version": 4, + "terraform_version": "0.12.0", + "serial": 8, + "lineage": "491fd8f4-81b5-9890-520c-8a173c36e483", + "outputs": { + "foo": { + "value": "bar", + "type": "string", + "sensitive": true + }, + "interpolated": { + "value": "424881806176056736", + "type": "string" + }, + "interpolated_deep": { + "value": { + "foo": "bar", + "map": { + "bar": "baz", + "id": "424881806176056736" + }, + "number": 42 + }, + "type": [ + "object", + { + "foo": "string", + "map": [ + "object", + { + "bar": "string", + "id": "string" + } + ], + "number": "number" + } + ] + }, + "list": { + "value": [ + "foo", + "bar" + ], + "type": [ + "tuple", + [ + "string", + "string" + ] + ] + }, + "map": { + "value": { + "foo": "bar", + "number": 42 + }, + "type": [ + "object", + { + "foo": "string", + "number": "number" + } + ] + }, + "referenced": { + "value": "424881806176056736", + "type": "string" + }, + "referenced_deep": { + "value": { + "foo": "bar", + "map": { + "bar": "baz", + "id": "424881806176056736" + }, + "number": 42 + }, + "type": [ + "object", + { + "foo": "string", + "map": [ + "object", + { + "bar": "string", + "id": "string" + } + ], + "number": "number" + } + ] + }, + "string": { + "value": "foo", + "type": "string" + } + }, + "resources": [ + { + "mode": "data", + "type": "null_data_source", + "name": "baz", + "provider": "provider.null", + "instances": [ + { + "schema_version": 0, + "attributes": { + "has_computed_default": "default", + "id": "static", + "inputs": { + "bar_id": "4347220156304926627", + "foo_id": "424881806176056736" + }, + "outputs": { + "bar_id": "4347220156304926627", + "foo_id": "424881806176056736" + }, + "random": "1951353658349486401" + }, + "depends_on": [ + "null_resource.bar", + "null_resource.foo" + ] + } + ] + }, + { + "mode": "managed", + "type": "null_resource", + "name": "bar", + "provider": "provider.null", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "4347220156304926627", + "triggers": { + "foo_id": "424881806176056736" + } + }, + "depends_on": [ + "null_resource.foo" + ] + } + ] + }, + { + "mode": "managed", + "type": "null_resource", + "name": "baz", + "each": "list", + "provider": "provider.null", + "instances": [ + { + "index_key": 0, + "schema_version": 0, + "attributes": { + "id": "751901236459396488", + "triggers": { + "foo_id": "424881806176056736" + } + }, + "depends_on": [ + "null_resource.foo" + ] + }, + { + "index_key": 1, + "schema_version": 0, + "attributes": { + "id": "2106740714798375541", + "triggers": { + "foo_id": "424881806176056736" + } + }, + "depends_on": [ + "null_resource.foo" + ] + }, + { + "index_key": 2, + "schema_version": 0, + "attributes": { + "id": "8665755682221598193", + "triggers": { + "foo_id": "424881806176056736" + } + }, + "depends_on": [ + "null_resource.foo" + ] + } + ] + }, + { + "mode": "managed", + "type": "null_resource", + "name": "foo", + "provider": "provider.null", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "424881806176056736", + "triggers": { + "foo": "bar" + } + } + } + ] + }, + { + "module": "module.foo", + "mode": "managed", + "type": "null_resource", + "name": "foo", + "provider": "provider.null", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "705267318028962447", + "triggers": { + "foo": "bar" + } + } + } + ] + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/variables.tf b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/variables.tf new file mode 100644 index 00000000..3964e7ac --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/no_changes/variables.tf @@ -0,0 +1,15 @@ +variable "foo" { + description = "foobar" + default = "bar" +} + +variable "number" { + default = 42 +} + +variable "map" { + default = { + foo = "bar" + number = 42 + } +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/output_depends_on/main.tf b/vendor/github.com/hashicorp/terraform-json/testdata/output_depends_on/main.tf new file mode 100644 index 00000000..c3dc56c1 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/output_depends_on/main.tf @@ -0,0 +1,8 @@ +resource "null_resource" "foo" {} + +resource "null_resource" "bar" {} + +output "id" { + depends_on = ["null_resource.bar"] + value = "${null_resource.foo.id}" +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/output_depends_on/plan.json b/vendor/github.com/hashicorp/terraform-json/testdata/output_depends_on/plan.json new file mode 100644 index 00000000..72a6f627 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/output_depends_on/plan.json @@ -0,0 +1 @@ +{"format_version":"0.1","terraform_version":"0.12.11","planned_values":{"outputs":{"id":{"sensitive":false}},"root_module":{"resources":[{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_name":"null","schema_version":0,"values":{"triggers":null}},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","schema_version":0,"values":{"triggers":null}}]}},"resource_changes":[{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_name":"null","change":{"actions":["create"],"before":null,"after":{"triggers":null},"after_unknown":{"id":true}}},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","change":{"actions":["create"],"before":null,"after":{"triggers":null},"after_unknown":{"id":true}}}],"output_changes":{"id":{"actions":["create"],"before":null,"after_unknown":true}},"configuration":{"root_module":{"outputs":{"id":{"expression":{"references":["null_resource.foo"]},"depends_on":["null_resource.bar"]}},"resources":[{"address":"null_resource.bar","mode":"managed","type":"null_resource","name":"bar","provider_config_key":"null","schema_version":0},{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_config_key":"null","schema_version":0}]}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/output_depends_on/schemas.json b/vendor/github.com/hashicorp/terraform-json/testdata/output_depends_on/schemas.json new file mode 100644 index 00000000..9c819e83 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/output_depends_on/schemas.json @@ -0,0 +1 @@ +{"format_version":"0.1","provider_schemas":{"null":{"provider":{"version":0,"block":{}},"resource_schemas":{"null_resource":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"triggers":{"type":["map","string"],"optional":true}}}}},"data_source_schemas":{"null_data_source":{"version":0,"block":{"attributes":{"has_computed_default":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"inputs":{"type":["map","string"],"optional":true},"outputs":{"type":["map","string"],"computed":true},"random":{"type":"string","computed":true}}}}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/provider_version/main.tf b/vendor/github.com/hashicorp/terraform-json/testdata/provider_version/main.tf new file mode 100644 index 00000000..8200c58d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/provider_version/main.tf @@ -0,0 +1,5 @@ +provider "null" { + version = "~> 2.1" +} + +resource "null_resource" "foo" {} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/provider_version/plan.json b/vendor/github.com/hashicorp/terraform-json/testdata/provider_version/plan.json new file mode 100644 index 00000000..7f85082b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/provider_version/plan.json @@ -0,0 +1 @@ +{"format_version":"0.1","terraform_version":"0.12.11","planned_values":{"root_module":{"resources":[{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","schema_version":0,"values":{"triggers":null}}]}},"resource_changes":[{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_name":"null","change":{"actions":["create"],"before":null,"after":{"triggers":null},"after_unknown":{"id":true}}}],"configuration":{"provider_config":{"null":{"name":"null","version_constraint":"~\u003e 2.1"}},"root_module":{"resources":[{"address":"null_resource.foo","mode":"managed","type":"null_resource","name":"foo","provider_config_key":"null","schema_version":0}]}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/provider_version/schemas.json b/vendor/github.com/hashicorp/terraform-json/testdata/provider_version/schemas.json new file mode 100644 index 00000000..9c819e83 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/provider_version/schemas.json @@ -0,0 +1 @@ +{"format_version":"0.1","provider_schemas":{"null":{"provider":{"version":0,"block":{}},"resource_schemas":{"null_resource":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"triggers":{"type":["map","string"],"optional":true}}}}},"data_source_schemas":{"null_data_source":{"version":0,"block":{"attributes":{"has_computed_default":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"inputs":{"type":["map","string"],"optional":true},"outputs":{"type":["map","string"],"computed":true},"random":{"type":"string","computed":true}}}}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/registry_module/main.tf b/vendor/github.com/hashicorp/terraform-json/testdata/registry_module/main.tf new file mode 100644 index 00000000..f512d2c0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/registry_module/main.tf @@ -0,0 +1,4 @@ +module "module" { + source = "vancluever/module/null" + version = "~> 1.0.1" +} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/registry_module/plan.json b/vendor/github.com/hashicorp/terraform-json/testdata/registry_module/plan.json new file mode 100644 index 00000000..11893bae --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/registry_module/plan.json @@ -0,0 +1 @@ +{"format_version":"0.1","terraform_version":"0.12.11","planned_values":{"root_module":{"child_modules":[{"resources":[{"address":"module.module.null_resource.resource","mode":"managed","type":"null_resource","name":"resource","provider_name":"null","schema_version":0,"values":{"triggers":{"number":"one"}}}],"address":"module.module"}]}},"resource_changes":[{"address":"module.module.null_resource.resource","module_address":"module.module","mode":"managed","type":"null_resource","name":"resource","provider_name":"null","change":{"actions":["create"],"before":null,"after":{"triggers":{"number":"one"}},"after_unknown":{"id":true,"triggers":{}}}}],"prior_state":{"format_version":"0.1","terraform_version":"0.12.11","values":{"root_module":{"child_modules":[{"resources":[{"address":"data.null_data_source.data","mode":"data","type":"null_data_source","name":"data","provider_name":"null","schema_version":0,"values":{"has_computed_default":"default","id":"static","inputs":{"key":"foo"},"outputs":{"key":"foo"},"random":"7083402041033367103"}}],"address":"module.module"}]}}},"configuration":{"root_module":{"module_calls":{"module":{"source":"vancluever/module/null","module":{"outputs":{"null_data_source_id":{"expression":{"references":["data.null_data_source.data"]},"description":"The `id` of the `null_data_source` data source in this module."},"null_resource_id":{"expression":{"references":["null_resource.resource"]},"description":"The `id` of the `null_resource` resource in this module."}},"resources":[{"address":"null_resource.resource","mode":"managed","type":"null_resource","name":"resource","provider_config_key":"module:null","expressions":{"triggers":{"references":["var.trigger"]}},"schema_version":0},{"address":"data.null_data_source.data","mode":"data","type":"null_data_source","name":"data","provider_config_key":"module:null","expressions":{"inputs":{"references":["var.input"]}},"schema_version":0}],"variables":{"input":{"default":"foo","description":"The input value for the `null_data_source` data source in this module."},"trigger":{"default":"one","description":"The trigger value for the `null_resource` resource in this module."}}},"version_constraint":"~\u003e 1.0.1"}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/testdata/registry_module/schemas.json b/vendor/github.com/hashicorp/terraform-json/testdata/registry_module/schemas.json new file mode 100644 index 00000000..9c819e83 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/testdata/registry_module/schemas.json @@ -0,0 +1 @@ +{"format_version":"0.1","provider_schemas":{"null":{"provider":{"version":0,"block":{}},"resource_schemas":{"null_resource":{"version":0,"block":{"attributes":{"id":{"type":"string","optional":true,"computed":true},"triggers":{"type":["map","string"],"optional":true}}}}},"data_source_schemas":{"null_data_source":{"version":0,"block":{"attributes":{"has_computed_default":{"type":"string","optional":true,"computed":true},"id":{"type":"string","optional":true,"computed":true},"inputs":{"type":["map","string"],"optional":true},"outputs":{"type":["map","string"],"computed":true},"random":{"type":"string","computed":true}}}}}}}} diff --git a/vendor/github.com/hashicorp/terraform-json/validate.go b/vendor/github.com/hashicorp/terraform-json/validate.go new file mode 100644 index 00000000..db9db191 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/validate.go @@ -0,0 +1,134 @@ +package tfjson + +import ( + "encoding/json" + "errors" + "fmt" +) + +// Pos represents a position in a config file +type Pos struct { + Line int `json:"line"` + Column int `json:"column"` + Byte int `json:"byte"` +} + +// Range represents a range of bytes between two positions +type Range struct { + Filename string `json:"filename"` + Start Pos `json:"start"` + End Pos `json:"end"` +} + +type DiagnosticSeverity string + +// These severities map to the tfdiags.Severity values, plus an explicit +// unknown in case that enum grows without us noticing here. +const ( + DiagnosticSeverityUnknown DiagnosticSeverity = "unknown" + DiagnosticSeverityError DiagnosticSeverity = "error" + DiagnosticSeverityWarning DiagnosticSeverity = "warning" +) + +// Diagnostic represents information to be presented to a user about an +// error or anomaly in parsing or evaluating configuration +type Diagnostic struct { + Severity DiagnosticSeverity `json:"severity,omitempty"` + + Summary string `json:"summary,omitempty"` + Detail string `json:"detail,omitempty"` + Range *Range `json:"range,omitempty"` + + Snippet *DiagnosticSnippet `json:"snippet,omitempty"` +} + +// DiagnosticSnippet represents source code information about the diagnostic. +// It is possible for a diagnostic to have a source (and therefore a range) but +// no source code can be found. In this case, the range field will be present and +// the snippet field will not. +type DiagnosticSnippet struct { + // Context is derived from HCL's hcled.ContextString output. This gives a + // high-level summary of the root context of the diagnostic: for example, + // the resource block in which an expression causes an error. + Context *string `json:"context"` + + // Code is a possibly-multi-line string of Terraform configuration, which + // includes both the diagnostic source and any relevant context as defined + // by the diagnostic. + Code string `json:"code"` + + // StartLine is the line number in the source file for the first line of + // the snippet code block. This is not necessarily the same as the value of + // Range.Start.Line, as it is possible to have zero or more lines of + // context source code before the diagnostic range starts. + StartLine int `json:"start_line"` + + // HighlightStartOffset is the character offset into Code at which the + // diagnostic source range starts, which ought to be highlighted as such by + // the consumer of this data. + HighlightStartOffset int `json:"highlight_start_offset"` + + // HighlightEndOffset is the character offset into Code at which the + // diagnostic source range ends. + HighlightEndOffset int `json:"highlight_end_offset"` + + // Values is a sorted slice of expression values which may be useful in + // understanding the source of an error in a complex expression. + Values []DiagnosticExpressionValue `json:"values"` +} + +// DiagnosticExpressionValue represents an HCL traversal string (e.g. +// "var.foo") and a statement about its value while the expression was +// evaluated (e.g. "is a string", "will be known only after apply"). These are +// intended to help the consumer diagnose why an expression caused a diagnostic +// to be emitted. +type DiagnosticExpressionValue struct { + Traversal string `json:"traversal"` + Statement string `json:"statement"` +} + +// ValidateOutput represents JSON output from terraform validate +// (available from 0.12 onwards) +type ValidateOutput struct { + FormatVersion string `json:"format_version"` + + Valid bool `json:"valid"` + ErrorCount int `json:"error_count"` + WarningCount int `json:"warning_count"` + Diagnostics []Diagnostic `json:"diagnostics"` +} + +// Validate checks to ensure that data is present, and the +// version matches the version supported by this library. +func (vo *ValidateOutput) Validate() error { + if vo == nil { + return errors.New("validation output is nil") + } + + if vo.FormatVersion == "" { + // The format was not versioned in the past + return nil + } + + supportedVersion := "0.1" + if vo.FormatVersion != supportedVersion { + return fmt.Errorf("unsupported validation output format version: expected %q, got %q", + supportedVersion, vo.FormatVersion) + } + + return nil +} + +func (vo *ValidateOutput) UnmarshalJSON(b []byte) error { + type rawOutput ValidateOutput + var schemas rawOutput + + err := json.Unmarshal(b, &schemas) + if err != nil { + return err + } + + *vo = *(*ValidateOutput)(&schemas) + + return vo.Validate() +} diff --git a/vendor/github.com/hashicorp/terraform-json/validate_test.go b/vendor/github.com/hashicorp/terraform-json/validate_test.go new file mode 100644 index 00000000..463b9a21 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/validate_test.go @@ -0,0 +1,227 @@ +package tfjson + +import ( + "encoding/json" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestValidateOutput_error(t *testing.T) { + errOutput := `{ + "valid": false, + "error_count": 1, + "warning_count": 0, + "diagnostics": [ + { + "severity": "error", + "summary": "Could not load plugin", + "detail": "\nPlugin reinitialization required..." + } + ] +}` + var parsed ValidateOutput + if err := json.Unmarshal([]byte(errOutput), &parsed); err != nil { + t.Fatal(err) + } + + expected := &ValidateOutput{ + ErrorCount: 1, + Diagnostics: []Diagnostic{ + { + Severity: "error", + Summary: "Could not load plugin", + Detail: "\nPlugin reinitialization required...", + }, + }, + } + if diff := cmp.Diff(expected, &parsed); diff != "" { + t.Fatalf("output mismatch: %s", diff) + } +} + +func TestValidateOutput_basic(t *testing.T) { + errOutput := `{ + "valid": false, + "error_count": 1, + "warning_count": 1, + "diagnostics": [ + { + "severity": "warning", + "summary": "\"anonymous\": [DEPRECATED] For versions later than 3.0.0, absence of a token enables this mode" + }, + { + "severity": "error", + "summary": "Missing required argument", + "detail": "The argument \"name\" is required, but no definition was found.", + "range": { + "filename": "main.tf", + "start": { + "line": 14, + "column": 37, + "byte": 200 + }, + "end": { + "line": 14, + "column": 37, + "byte": 200 + } + } + } + ] +}` + var parsed ValidateOutput + if err := json.Unmarshal([]byte(errOutput), &parsed); err != nil { + t.Fatal(err) + } + + expected := &ValidateOutput{ + ErrorCount: 1, + WarningCount: 1, + Diagnostics: []Diagnostic{ + { + Severity: "warning", + Summary: "\"anonymous\": [DEPRECATED] For versions later than 3.0.0, absence of a token enables this mode", + }, + { + Severity: "error", + Summary: "Missing required argument", + Detail: "The argument \"name\" is required, but no definition was found.", + Range: &Range{ + Filename: "main.tf", + Start: Pos{ + Line: 14, + Column: 37, + Byte: 200, + }, + End: Pos{ + Line: 14, + Column: 37, + Byte: 200, + }, + }, + }, + }, + } + if diff := cmp.Diff(expected, &parsed); diff != "" { + t.Fatalf("output mismatch: %s", diff) + } +} + +func TestValidateOutput_versioned(t *testing.T) { + errOutput := `{ + "format_version": "0.1", + "valid": false, + "error_count": 1, + "warning_count": 1, + "diagnostics": [ + { + "severity": "warning", + "summary": "Deprecated Attribute", + "detail": "Deprecated in favor of project_id", + "range": { + "filename": "main.tf", + "start": { + "line": 21, + "column": 25, + "byte": 408 + }, + "end": { + "line": 21, + "column": 42, + "byte": 425 + } + }, + "snippet": { + "context": "resource \"google_project_access_approval_settings\" \"project_access_approval\"", + "code": " project = \"my-project-name\"", + "start_line": 21, + "highlight_start_offset": 24, + "highlight_end_offset": 41, + "values": [] + } + }, + { + "severity": "error", + "summary": "Missing required argument", + "detail": "The argument \"enrolled_services\" is required, but no definition was found.", + "range": { + "filename": "main.tf", + "start": { + "line": 19, + "column": 78, + "byte": 340 + }, + "end": { + "line": 19, + "column": 79, + "byte": 341 + } + }, + "snippet": { + "context": "resource \"google_project_access_approval_settings\" \"project_access_approval\"", + "code": "resource \"google_project_access_approval_settings\" \"project_access_approval\" {", + "start_line": 19, + "highlight_start_offset": 77, + "highlight_end_offset": 78, + "values": [] + } + } + ] +}` + var parsed ValidateOutput + if err := json.Unmarshal([]byte(errOutput), &parsed); err != nil { + t.Fatal(err) + } + + expected := &ValidateOutput{ + FormatVersion: "0.1", + ErrorCount: 1, + WarningCount: 1, + Diagnostics: []Diagnostic{ + { + Severity: "warning", + Summary: "Deprecated Attribute", + Detail: "Deprecated in favor of project_id", + Range: &Range{ + Filename: "main.tf", + Start: Pos{Line: 21, Column: 25, Byte: 408}, + End: Pos{Line: 21, Column: 42, Byte: 425}, + }, + Snippet: &DiagnosticSnippet{ + Context: ptrToString(`resource "google_project_access_approval_settings" "project_access_approval"`), + Code: ` project = "my-project-name"`, + StartLine: 21, + HighlightStartOffset: 24, + HighlightEndOffset: 41, + Values: []DiagnosticExpressionValue{}, + }, + }, + { + Severity: "error", + Summary: "Missing required argument", + Detail: `The argument "enrolled_services" is required, but no definition was found.`, + Range: &Range{ + Filename: "main.tf", + Start: Pos{Line: 19, Column: 78, Byte: 340}, + End: Pos{Line: 19, Column: 79, Byte: 341}, + }, + Snippet: &DiagnosticSnippet{ + Context: ptrToString(`resource "google_project_access_approval_settings" "project_access_approval"`), + Code: `resource "google_project_access_approval_settings" "project_access_approval" {`, + StartLine: 19, + HighlightStartOffset: 77, + HighlightEndOffset: 78, + Values: []DiagnosticExpressionValue{}, + }, + }, + }, + } + if diff := cmp.Diff(expected, &parsed); diff != "" { + t.Fatalf("output mismatch: %s", diff) + } +} + +func ptrToString(val string) *string { + return &val +} diff --git a/vendor/github.com/hashicorp/terraform-json/version.go b/vendor/github.com/hashicorp/terraform-json/version.go new file mode 100644 index 00000000..16f0a853 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/version.go @@ -0,0 +1,11 @@ +package tfjson + +// VersionOutput represents output from the version -json command +// added in v0.13 +type VersionOutput struct { + Version string `json:"terraform_version"` + Revision string `json:"terraform_revision"` + Platform string `json:"platform,omitempty"` + ProviderSelections map[string]string `json:"provider_selections"` + Outdated bool `json:"terraform_outdated"` +} diff --git a/vendor/github.com/hashicorp/terraform-json/version_test.go b/vendor/github.com/hashicorp/terraform-json/version_test.go new file mode 100644 index 00000000..dcd73179 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-json/version_test.go @@ -0,0 +1,66 @@ +package tfjson + +import ( + "encoding/json" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestVersionOutput_013(t *testing.T) { + errOutput := `{ + "terraform_version": "0.13.5", + "terraform_revision": "", + "provider_selections": { + "registry.terraform.io/hashicorp/github": "2.9.2", + "registry.terraform.io/hashicorp/random": "3.0.0" + }, + "terraform_outdated": true +}` + var parsed VersionOutput + if err := json.Unmarshal([]byte(errOutput), &parsed); err != nil { + t.Fatal(err) + } + + expected := &VersionOutput{ + Version: "0.13.5", + ProviderSelections: map[string]string{ + "registry.terraform.io/hashicorp/github": "2.9.2", + "registry.terraform.io/hashicorp/random": "3.0.0", + }, + Outdated: true, + } + if diff := cmp.Diff(expected, &parsed); diff != "" { + t.Fatalf("output mismatch: %s", diff) + } +} + +func TestVersionOutput_015(t *testing.T) { + errOutput := `{ + "terraform_version": "0.15.0-dev", + "terraform_revision": "ae025248cc0712bf53c675dc2fe77af4276dd5cc", + "platform": "darwin_amd64", + "provider_selections": { + "registry.terraform.io/hashicorp/github": "2.9.2", + "registry.terraform.io/hashicorp/random": "3.0.0" + }, + "terraform_outdated": false +}` + var parsed VersionOutput + if err := json.Unmarshal([]byte(errOutput), &parsed); err != nil { + t.Fatal(err) + } + + expected := &VersionOutput{ + Version: "0.15.0-dev", + Revision: "ae025248cc0712bf53c675dc2fe77af4276dd5cc", + Platform: "darwin_amd64", + ProviderSelections: map[string]string{ + "registry.terraform.io/hashicorp/github": "2.9.2", + "registry.terraform.io/hashicorp/random": "3.0.0", + }, + } + if diff := cmp.Diff(expected, &parsed); diff != "" { + t.Fatalf("output mismatch: %s", diff) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/.go-version b/vendor/github.com/hashicorp/terraform-plugin-sdk/.go-version new file mode 100644 index 00000000..98e863cd --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/.go-version @@ -0,0 +1 @@ +1.15.8 diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/.hashibot.hcl b/vendor/github.com/hashicorp/terraform-plugin-sdk/.hashibot.hcl new file mode 100644 index 00000000..022aaaf5 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/.hashibot.hcl @@ -0,0 +1,31 @@ +behavior "remove_labels_on_reply" "remove_stale" { + labels = ["waiting-response", "stale"] + only_non_maintainers = true +} + +poll "label_issue_migrater" "provider_migrater" { + schedule = "0 40 * * * *" + new_owner = env.PROVIDERS_OWNER + repo_prefix = "terraform-provider-" + label_prefix = "provider/" + issue_header = <<-EOF + _This issue was originally opened by @${var.user} as ${var.repository}#${var.issue_number}. The original body of the issue is below._ + +
+ + EOF + migrated_comment = "This issue has been automatically migrated to ${var.repository}#${var.issue_number} because it looks like an issue with that provider. If you believe this is _not_ an issue with the provider, please reply to ${var.repository}#${var.issue_number}." +} + +poll "closed_issue_locker" "locker" { + schedule = "0 50 1 * * *" + closed_for = "720h" # 30 days + max_issues = 500 + sleep_between_issues = "5s" + + message = <<-EOF + I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues. + + If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further. + EOF +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/CHANGELOG.md b/vendor/github.com/hashicorp/terraform-plugin-sdk/CHANGELOG.md new file mode 100644 index 00000000..84c1d761 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/CHANGELOG.md @@ -0,0 +1,636 @@ +# 2.7.0 (Unreleased) + +ENHANCEMENTS: + +* Made SDK-generated diagnostics clearer and more consistent [GH-755] + +# 2.6.1 (April 23, 2021) + +BUG FIXES: + +* Updated the GPG key used to verify Terraform installs in response to the [Terraform GPG key rotation](https://discuss.hashicorp.com/t/hcsec-2021-12-codecov-security-event-and-hashicorp-gpg-key-exposure/23512). ([#750](https://github.com/hashicorp/terraform-plugin-sdk/issues/750)) + +# 2.6.0 (April 21, 2021) + +ENHANCEMENTS: + +* Made TF_ACC_TERRAFORM_VERSION more permissive, accepting values in either vX.Y.Z or X.Y.Z formats. ([#731](https://github.com/hashicorp/terraform-plugin-sdk/issues/731)) +* Upgraded to use terraform-plugin-go v0.3.0 ([#739](https://github.com/hashicorp/terraform-plugin-sdk/issues/739)) + +# 2.5.0 (March 24, 2021) + +ENHANCEMENTS + +* Added the ability to opt out of context timeouts in CRUD functions ([#723](https://github.com/hashicorp/terraform-plugin-sdk/issues/723)) + +# 2.4.4 (February 24, 2021) + +NOTES + +As per our Go version support policy, we now require Go 1.15 or higher to use the SDK. + +BUG FIXES + +* Resolved bug where Diagnostics wouldn't get associated with their configuration context in user output. ([#696](https://github.com/hashicorp/terraform-plugin-sdk/issues/696)) + +# 2.4.3 (February 10, 2021) + +BUG FIXES + +* Make acceptance testing framework compatible with Terraform 0.15 ([#694](https://github.com/hashicorp/terraform-plugin-sdk/issues/694)) + +# 2.4.2 (January 27, 2021) + +BUG FIXES + +* Don't panic in very specific circumstances involving CustomizeDiff and empty strings in the config ([#686](https://github.com/hashicorp/terraform-plugin-sdk/issues/686)) + +# 2.4.1 (January 20, 2021) + +BUG FIXES + +* Don't panic during assertions when testing sets with varying levels of nesting ([#648](https://github.com/hashicorp/terraform-plugin-sdk/issues/648)) +* Prevent panics when sending Ctrl-C to Terraform ([#674](https://github.com/hashicorp/terraform-plugin-sdk/issues/674)) +* Make the error message when a "required" block is missing clearer, identifying the block in question ([#672](https://github.com/hashicorp/terraform-plugin-sdk/issues/672)) + +# 2.4.0 (December 19, 2020) + +ENHANCEMENTS + +* Support `Unwrap` on SDK errors ([#647](https://github.com/hashicorp/terraform-plugin-sdk/issues/647)) +* Allow for `nil` errors in `diag.FromErr` ([#623](https://github.com/hashicorp/terraform-plugin-sdk/issues/623)) +* Added `validation.ToDiagFunc` helper to translate legacy validation functions into Diagnostics-aware validation functions. ([#611](https://github.com/hashicorp/terraform-plugin-sdk/issues/611)) +* Disable Checkpoint network connections during acceptance testing unless a Terraform binary needs to be installed. ([#663](https://github.com/hashicorp/terraform-plugin-sdk/issues/663)) + +BUG FIXES + +* Check for `nil` errors prior to invoking `ErrorCheck` ([#646](https://github.com/hashicorp/terraform-plugin-sdk/issues/646)) +* More reliable handling of logging ([#639](https://github.com/hashicorp/terraform-plugin-sdk/issues/639)) +* Modified error text to make golint and go vet happy when a non-empty plan is found in testing and an empty plan was expected ([#596](https://github.com/hashicorp/terraform-plugin-sdk/issues/596)) +* Add `UseJSONNumber` to `helper/schema.Resource` to make it possible to represent large numbers precisely. Setting to `true` will make numbers appear as `json.Number` in `StateUpgrader`s instead of as `float64`. ([#662](https://github.com/hashicorp/terraform-plugin-sdk/issues/662)) +* Fix logs sometimes appearing in test output when running acceptance tests. ([#665](https://github.com/hashicorp/terraform-plugin-sdk/issues/665)) + +NOTES + +We have removed the deprecation of the non-diagnostic version of validation until the build-in validations are ported to the new format. + +# 2.3.0 (November 20, 2020) + +ENHANCEMENTS + +* `helper/schema.ResourceData` now has `HasChangeExcept` and `HasChangesExcept` methods to check if the resource has changes _besides_ a given key or list of keys. ([#558](https://github.com/hashicorp/terraform-plugin-sdk/issues/558)) +* `helper/resource.TestCase` now has an `ErrorCheck` property that can be set to a function, allowing the programmatic determination of whether to ignore an error or not. ([#592](https://github.com/hashicorp/terraform-plugin-sdk/issues/592)) + +# 2.2.0 (November 02, 2020) + +FEATURES +* Updated to use the new [`terraform-plugin-go`](https://github.com/hashicorp/terraform-plugin-go) library as a foundation for the SDK, enabling it to be used with [`terraform-plugin-mux`](https://github.com/hashicorp/terraform-plugin-mux) ([#630](https://github.com/hashicorp/terraform-plugin-sdk/issues/630)) +* Added the `TestCase.ProtoV5ProviderFactories` property to allow testing providers created with `terraform-plugin-go` with the `helper/resource` test framework. ([#630](https://github.com/hashicorp/terraform-plugin-sdk/issues/630)) + +# 2.1.0 (October 27, 2020) + +FEATURES +* Relaxed validation in `InternalValidate` for explicitly set `id` attributes ([#613](https://github.com/hashicorp/terraform-plugin-sdk/issues/613)) +* Ported TypeSet test check funcs essential for migrating to V2 of the SDK ([#614](https://github.com/hashicorp/terraform-plugin-sdk/issues/614)) +* Improved debug output for how to manually invoke the Terraform CLI ([#615](https://github.com/hashicorp/terraform-plugin-sdk/issues/615)) + +# 2.0.4 (October 06, 2020) + +BUG FIXES +* Fix a bug that would pass the post-destroy state to `helper/resource.TestCase.CheckDestroy` instead of the documented pre-destroy state ([#591](https://github.com/hashicorp/terraform-plugin-sdk/issues/591)) +* Clean up the final remaining places where test numbers or dangling resources warnings could be omitted from errors ([#578](https://github.com/hashicorp/terraform-plugin-sdk/issues/578)) +* Stop considering plans empty when they include data source changes ([#594](https://github.com/hashicorp/terraform-plugin-sdk/issues/594)) + +# 2.0.3 (September 15, 2020) + +BUG FIXES + +* Fixed a bug that would incorrectly mark tests using TestStep.ImportStateVerify as failed if they tested a resource with custom timeouts ([#576](https://github.com/hashicorp/terraform-plugin-sdk/issues/576)) +* Fixed a bug where errors destroying infrastructure after tests wouldn't be reported ([#581](https://github.com/hashicorp/terraform-plugin-sdk/issues/581)) +* Fixed a bug where test steps that expected a non-empty plan would fail because they had an empty plan, erroneously ([#580](https://github.com/hashicorp/terraform-plugin-sdk/issues/580)) +* Fixed a bug where the plan output shown when an unexpected diff was encountered during testing would be shown in JSON instead of a human-readable format ([#584](https://github.com/hashicorp/terraform-plugin-sdk/issues/584)) + +# 2.0.2 (September 10, 2020) + +BUG FIXES + +* Fixed bug where state is read from the wrong workspace during import tests. ([#552](https://github.com/hashicorp/terraform-plugin-sdk/issues/552)) +* Fixed bug where the resource could belong to another provider when finding the resource state to check during import tests ([#522](https://github.com/hashicorp/terraform-plugin-sdk/issues/522)) +* Removed excessive logging when ExpectNonEmptyPlan was successfully matched ([#556](https://github.com/hashicorp/terraform-plugin-sdk/issues/556)) +* Fixed bug where state from data sources, which can't be imported, would be surfaced during ImportStateVerify ([#555](https://github.com/hashicorp/terraform-plugin-sdk/issues/555)) +* Fixed bug that ignored ExpectError when testing state imports ([#550](https://github.com/hashicorp/terraform-plugin-sdk/issues/550)) +* Fixed bug that sometimes prevented TestStep numbers from appearing in error output ([#557](https://github.com/hashicorp/terraform-plugin-sdk/issues/557)) +* Fixed bug that would ignore `TestStep.Destroy` when running tests. ([#563](https://github.com/hashicorp/terraform-plugin-sdk/issues/563)) + +# 2.0.1 (August 10, 2020) + +BUG FIXES + +* Restored reporting of failed test step number ([#524](https://github.com/hashicorp/terraform-plugin-sdk/issues/524)) +* Restored output of a test that failed with unexpected diff to V1 style output ([#526](https://github.com/hashicorp/terraform-plugin-sdk/issues/526)) + +# 2.0.0 (July 30, 2020) + +FEATURES + +* Provide deprecated method for receiving a global context that receives stop cancellation. ([#502](https://github.com/hashicorp/terraform-plugin-sdk/issues/502)) +* Support multiple providers in reattach mode ([#512](https://github.com/hashicorp/terraform-plugin-sdk/issues/512)) +* Allow setting `ExternalProviders` in `resource.TestCase` to control what providers are downloaded with `terraform init` for a test. ([#516](https://github.com/hashicorp/terraform-plugin-sdk/issues/516)) +* Restore `resource.TestEnvVar` ([#519](https://github.com/hashicorp/terraform-plugin-sdk/issues/519)) + +BUG FIXES + +* Remove deprecation warnings which cause spam and crashes in provider acceptance tests. ([#503](https://github.com/hashicorp/terraform-plugin-sdk/issues/503)) +* Fixed a bug in the test driver that caused errors for Windows users on versions of Terraform below 0.13.0-beta2. ([#499](https://github.com/hashicorp/terraform-plugin-sdk/issues/499)) +* Fixed a bug in the test driver that caused timeouts when using the `IDRefreshName` on `resource.TestCase`s. ([#501](https://github.com/hashicorp/terraform-plugin-sdk/issues/501)) +* Fixed a bug where data sources would not always reflect changes in their configs in the same `resource.TestStep` that the config changed. ([#515](https://github.com/hashicorp/terraform-plugin-sdk/issues/515)) +* Fixed a bug that would prevent errors from being handled by ExpectError handlers during testing. ([#518](https://github.com/hashicorp/terraform-plugin-sdk/issues/518)) + +# 2.0.0-rc.2 (June 11, 2020) + +FEATURES + +* The test driver was reworked to allow for test coverage, race detection, and debugger support. ([#471](https://github.com/hashicorp/terraform-plugin-sdk/issues/471)) +* A new `plugin.Debug` function allows starting the provider in a standalone mode that's compatible with debuggers, and outputs information on how to drive the standalone provider with Terraform. ([#471](https://github.com/hashicorp/terraform-plugin-sdk/issues/471)) + +BREAKING CHANGES + +* Removed the `helper/mutexkv`, `helper/pathorcontents`, `httpclient`, and `helper/hashcode` packages. These packages can be easily replicated in plugin code if necessary or the v1 versions can be used side-by-side ([#438](https://github.com/hashicorp/terraform-plugin-sdk/issues/438)) +* Removed `schema.PanicOnErr/TF_SCHEMA_PANIC_ON_ERR` environment variable. `d.Set()` errors are now logged in production and panic during acceptance tests (`TF_ACC=1`). ([#462](https://github.com/hashicorp/terraform-plugin-sdk/issues/462)) +* Running provider tests now requires Terraform 0.12.26 or higher. ([#471](https://github.com/hashicorp/terraform-plugin-sdk/issues/471)) +* Removed `acctest` package, as it is no longer needed. The calls to `acctest.UseBinaryDriver` can be deleted; they're no longer necessary. ([#471](https://github.com/hashicorp/terraform-plugin-sdk/issues/471)) +* The `resource.TestCase.Providers` and `resource.TestCaseProviderFactories` maps must now have exactly one entry set between both of them, meaning one or the other should be used. Only the provider under test should be present in these maps. Providers that tests rely upon can be used by setting provider blocks in the test case, where `terraform init` will pick them up automatically. ([#471](https://github.com/hashicorp/terraform-plugin-sdk/issues/471)) +* The `TF_LOG_PATH_MASK` used to filter provider logs by test name when running tests has been removed. ([#473](https://github.com/hashicorp/terraform-plugin-sdk/issues/473)) + +ENHANCEMENTS + +* Added a `schema.Provider.UserAgent` method to generate a User-Agent string ([#474](https://github.com/hashicorp/terraform-plugin-sdk/issues/474)) +* Convenience methods were added to the `diag` package to simplify common error cases ([#449](https://github.com/hashicorp/terraform-plugin-sdk/issues/449)) + +BUG FIXES + +* Restored `d.Partial` and noted the edgecase it covers and odd Terraform behavior. ([#472](https://github.com/hashicorp/terraform-plugin-sdk/issues/472)) +* Provider log output now respects the `TF_LOG` and `TF_LOG_PATH` environment variables when running tests. ([#473](https://github.com/hashicorp/terraform-plugin-sdk/issues/473)) + +# 2.0.0-rc.1 (May 05, 2020) + +BREAKING CHANGES + +* The SDK no longer supports protocol 4 (Terraform 0.11 and below). Providers built on the SDK after v2 will need Terraform 0.12 to be used. +* The new, previously optional binary acceptance testing framework is now the default and only available mode for testing. Test code and provider code will no longer reside in the same process. Providers also will have their processes stopped and restarted multiple times during a test. This more accurately mirrors the behavior of providers in production. +* Updated type signatures for some functions to include context.Context support. These include helpers in the helper/customdiff package, the CustomizeDiffFunc type, and the StateUpgradeFunc type. ([#276](https://github.com/hashicorp/terraform-plugin-sdk/issues/276)) +* The Partial and SetPartial methods on schema.ResourceData have been removed, as they were rarely necessary and poorly understood. ([#318](https://github.com/hashicorp/terraform-plugin-sdk/issues/318)) +* The terraform.ResourceProvider interface has been removed. The *schema.Provider type should be used directly, instead. ([#316](https://github.com/hashicorp/terraform-plugin-sdk/issues/316)) +* Deprecated helper/validation functions have been removed. ([#333](https://github.com/hashicorp/terraform-plugin-sdk/issues/333)) +* PromoteSingle’s use is discouraged, and so it has been removed from helper/schema.Schema. ([#337](https://github.com/hashicorp/terraform-plugin-sdk/issues/337)) +* schema.UnsafeSetFieldRaw’s use is discouraged, and so it has been removed. ([#339](https://github.com/hashicorp/terraform-plugin-sdk/issues/339)) +* Calls to schema.ResourceData.Set that would return an error now panic by default. TF_SCHEMA_PANIC_ON_ERROR can be set to a falsey value to disable this behavior. +* schema.Resource.Refresh has been removed, as it is unused in protocol 5. ([#370](https://github.com/hashicorp/terraform-plugin-sdk/issues/370)) +* The Removed field has been removed from helper/schema.Schema, which means providers can no longer specify error messages when a recently removed field is used. This functionality had a lot of bugs and corner cases that worked in unexpected ways, and so was removed. ([#414](https://github.com/hashicorp/terraform-plugin-sdk/issues/414)) +* The helper/encryption package has been removed, following our [published guidance](https://www.terraform.io/docs/extend/best-practices/sensitive-state.html#don-39-t-encrypt-state). ([#436](https://github.com/hashicorp/terraform-plugin-sdk/issues/436)) +* In scenarios where the Go testing package was used, the github.com/mitchellh/go-testing-interface package may be required instead. ([#406](https://github.com/hashicorp/terraform-plugin-sdk/issues/406)) +*
A number of exported variables, functions, types, and interfaces that were not meant to be part of the SDK’s interface have been removed. Most plugins should not notice they are gone. + + The removals include: + * helper/acctest.RemoteTestPrecheck + * helper/acctest.SkipRemoteTestsEnvVar + * helper/resource.EnvLogPathMask + * helper/resource.GRPCTestProvider + * helper/resource.LogOutput + * helper/resource.Map + * helper/resource.TestEnvVar + * helper/resource.TestProvider + * helper/schema.MultiMapReader + * helper/schema.Provider.Input + * plugin.Client + * plugin.ClientConfig + * plugin.DefaultProtocolVersion + * plugin.GRPCProvider + * plugin.GRPCProviderPlugin + * plugin.GRPCProvisioner + * plugin.GRPCProvisionerPlugin + * plugin.HandShake.ProtocolVersion + * plugin.ResourceProvider + * plugin.ResourceProviderApplyArgs + * plugin.ResourceProviderApplyResponse + * plugin.ResourceProviderConfigureResponse + * plugin.ResourceProviderDiffArgs + * plugin.ResourceProviderDiffResponse + * plugin.ResourceProviderGetSchemaArgs + * plugin.ResourceProviderGetSchemaResponse + * plugin.ResourceProviderImportStateArgs + * plugin.ResourceProviderImportStateResponse + * plugin.ResourceProviderInputArgs + * plugin.ResourceProviderInputResponse + * plugin.ResourceProviderPlugin + * plugin.ResourceProviderReadDataApplyArgs + * plugin.ResourceProviderReadDataApplyResponse + * plugin.ResourceProviderReadDataDiffArgs + * plugin.ResourceProviderReadDataDiffResponse + * plugin.ResourceProviderRefreshArgs + * plugin.ResourceProviderRefreshResponse + * plugin.ResourceProviderServer + * plugin.ResourceProviderStopResponse + * plugin.ResourceProviderValidateArgs + * plugin.ResourceProviderValidateResourceArgs + * plugin.ResourceProviderValidateResourceResponse + * plugin.ResourceProviderValidateResponse + * plugin.UIInput + * plugin.UIInputInputResponse + * plugin.UIInputServer + * plugin.UIOutput + * plugin.UIOutputServer + * plugin.VersionedPlugins no longer has a "provisioner" key + * resource.RunNewTest + * schema.Backend + * schema.FromContextBackendConfig + * schema.SetProto5 + * terraform.ApplyGraphBuilder + * terraform.AttachResourceConfigTransformer + * terraform.AttachSchemaTransformer + * terraform.AttachStateTransformer + * terraform.BackendState.Config + * terraform.BackendState.Empty + * terraform.BackendState.ForPlan + * terraform.BackendState.SetConfig + * terraform.BasicGraphBuilder + * terraform.BuiltinEvalContext + * terraform.CallbackUIOutput + * terraform.CBDEdgeTransformer + * terraform.CheckCoreVersionRequirements + * terraform.CloseProviderEvalTree + * terraform.CloseProviderTransformer + * terraform.CloseProvisionerTransformer + * terraform.ConcreteProviderNodeFunc + * terraform.ConcreteResourceInstanceDeposedNodeFunc + * terraform.ConcreteResourceInstanceNodeFunc + * terraform.ConcreteResourceNodeFunc + * terraform.ConfigTransformer + * terraform.ConfigTreeDependencies + * terraform.ConnectionBlockSupersetSchema + * terraform.Context + * terraform.ContextGraphOpts + * terraform.ContextGraphWalker + * terraform.ContextMeta + * terraform.ContextOpts + * terraform.CountBoundaryTransformer + * terraform.DefaultVariableValues + * terraform.DestroyEdge + * terraform.DestroyEdgeTransformer + * terraform.DestroyOutputTransformer + * terraform.DestroyPlanGraphBuilder + * terraform.DestroyValueReferenceTransformer + * terraform.Diff (this was eventually cut) + * terraform.Diff.ModuleByPath + * terraform.Diff.RootModule + * terraform.DiffAttrInput + * terraform.DiffAttrOutput + * terraform.DiffAttrType + * terraform.DiffAttrUnknown + * terraform.DiffChangeType + * terraform.DiffCreate + * terraform.DiffDestroy + * terraform.DiffDestroyCreate + * terraform.DiffInvalid + * terraform.DiffNone + * terraform.DiffRefresh + * terraform.DiffTransformer + * terraform.DiffUpdate + * terraform.EphemeralState.DeepCopy + * terraform.ErrNoState + * terraform.Eval + * terraform.EvalApply + * terraform.EvalApplyPost + * terraform.EvalApplyPre + * terraform.EvalApplyProvisioners + * terraform.EvalCheckModuleRemoved + * terraform.EvalCheckPlannedChange + * terraform.EvalCheckPreventDestroy + * terraform.EvalCloseProvider + * terraform.EvalCloseProvisioner + * terraform.EvalConfigBlock + * terraform.EvalConfigExpr + * terraform.EvalConfigProvider + * terraform.EvalContext + * terraform.EvalCountFixZeroOneBoundaryGlobal + * terraform.EvalDataForInstanceKey + * terraform.EvalDataForNoInstanceKey + * terraform.EvalDeleteLocal + * terraform.EvalDeleteOutput + * terraform.EvalDeposeState + * terraform.EvalDiff + * terraform.EvalDiffDestroy + * terraform.EvalEarlyExitError + * terraform.EvalFilter + * terraform.EvalForgetResourceState + * terraform.EvalGetProvider + * terraform.EvalGetProvisioner + * terraform.EvalGraphBuilder + * terraform.EvalIf + * terraform.EvalImportState + * terraform.EvalImportStateVerify + * terraform.EvalInitProvider + * terraform.EvalInitProvisioner + * terraform.EvalLocal + * terraform.EvalMaybeRestoreDeposedObject + * terraform.EvalMaybeTainted + * terraform.EvalModuleCallArgument + * terraform.EvalNode + * terraform.EvalNodeFilterable + * terraform.EvalNodeFilterFunc + * terraform.EvalNodeFilterOp + * terraform.EvalNodeOpFilterable + * terraform.EvalNoop + * terraform.EvalOpFilter + * terraform.EvalRaw + * terraform.EvalReadData + * terraform.EvalReadDataApply + * terraform.EvalReadDiff + * terraform.EvalReadState + * terraform.EvalReadStateDeposed + * terraform.EvalReduceDiff + * terraform.EvalRefresh + * terraform.EvalRequireState + * terraform.EvalReturnError + * terraform.EvalSequence + * terraform.EvalSetModuleCallArguments + * terraform.Evaluator + * terraform.EvalUpdateStateHook + * terraform.EvalValidateCount + * terraform.EvalValidateProvider + * terraform.EvalValidateProvisioner + * terraform.EvalValidateResource + * terraform.EvalValidateSelfRef + * terraform.EvalWriteDiff + * terraform.EvalWriteOutput + * terraform.EvalWriteResourceState + * terraform.EvalWriteState + * terraform.EvalWriteStateDeposed + * terraform.ExpandTransform + * terraform.ForcedCBDTransformer + * terraform.Graph + * terraform.GraphBuilder + * terraform.GraphDot + * terraform.GraphNodeAttachDestroyer + * terraform.GraphNodeAttachProvider + * terraform.GraphNodeAttachProviderConfigSchema + * terraform.GraphNodeAttachProvisionerSchema + * terraform.GraphNodeAttachResourceConfig + * terraform.GraphNodeAttachResourceSchema + * terraform.GraphNodeAttachResourceState + * terraform.GraphNodeCloseProvider + * terraform.GraphNodeCloseProvisioner + * terraform.GraphNodeCreator + * terraform.GraphNodeDeposedResourceInstanceObject + * terraform.GraphNodeDeposer + * terraform.GraphNodeDestroyer + * terraform.GraphNodeDestroyerCBD + * terraform.GraphNodeDynamicExpandable + * terraform.GraphNodeEvalable + * terraform.GraphNodeExpandable + * terraform.GraphNodeProvider + * terraform.GraphNodeProviderConsumer + * terraform.GraphNodeProvisioner + * terraform.GraphNodeProvisionerConsumer + * terraform.GraphNodeReferenceable + * terraform.GraphNodeReferenceOutside + * terraform.GraphNodeReferencer + * terraform.GraphNodeResource + * terraform.GraphNodeResourceInstance + * terraform.GraphNodeSubgraph + * terraform.GraphNodeSubPath + * terraform.GraphNodeTargetable + * terraform.GraphNodeTargetDownstream + * terraform.GraphTransformer + * terraform.GraphTransformIf + * terraform.GraphTransformMulti + * terraform.GraphType + * terraform.GraphTypeApply + * terraform.GraphTypeEval + * terraform.GraphTypeInvalid + * terraform.GraphTypeLegacy + * terraform.GraphTypeMap + * terraform.GraphTypePlan + * terraform.GraphTypePlanDestroy + * terraform.GraphTypeRefresh + * terraform.GraphTypeValidate + * terraform.GraphVertexTransformer + * terraform.GraphWalker + * terraform.Hook + * terraform.HookAction + * terraform.HookActionContinue + * terraform.HookActionHalt + * terraform.ImportGraphBuilder + * terraform.ImportOpts + * terraform.ImportProviderValidateTransformer + * terraform.ImportStateTransformer + * terraform.ImportTarget + * terraform.InputMode + * terraform.InputModeProvider + * terraform.InputModeStd + * terraform.InputModeVar + * terraform.InputModeVarUnset + * terraform.InputOpts + * terraform.InputValue + * terraform.InputValues + * terraform.InputValuesFromCaller + * terraform.InstanceDiff.Copy + * terraform.InstanceDiff.DelAttribute + * terraform.InstanceDiff.GetAttributesLen + * terraform.InstanceDiff.SetAttribute + * terraform.InstanceDiff.SetDestroy + * terraform.InstanceDiff.SetDestroyDeposed + * terraform.InstanceDiff.SetTainted + * terraform.InstanceInfo.ResourceAddress + * terraform.InstanceKeyEvalData + * terraform.InstanceType + * terraform.LoadSchemas + * terraform.LocalTransformer + * terraform.MissingProviderTransformer + * terraform.MissingProvisionerTransformer + * terraform.MockEvalContext + * terraform.MockHook + * terraform.MockProvider + * terraform.MockProvisioner + * terraform.MockResourceProvider (this was removed) + * terraform.MockResourceProvider.Input + * terraform.MockResourceProvider.InputCalled + * terraform.MockResourceProvider.InputConfig + * terraform.MockResourceProvider.InputFn + * terraform.MockResourceProvider.InputInput + * terraform.MockResourceProvider.InputReturnConfig + * terraform.MockResourceProvider.InputReturnError + * terraform.MockResourceProvisioner + * terraform.MockUIInput + * terraform.MockUIOutput + * terraform.ModuleDiff (this was eventually cut) + * terraform.ModuleDiff.IsRoot + * terraform.ModuleState.Empty + * terraform.ModuleState.IsDescendent + * terraform.ModuleState.IsRoot + * terraform.ModuleState.Orphans + * terraform.ModuleState.RemovedOutputs + * terraform.ModuleState.View + * terraform.ModuleVariableTransformer + * terraform.MustShimLegacyState + * terraform.NewContext + * terraform.NewInstanceInfo + * terraform.NewLegacyResourceAddress + * terraform.NewLegacyResourceInstanceAddress + * terraform.NewNodeAbstractResource + * terraform.NewNodeAbstractResourceInstance + * terraform.NewReferenceMap + * terraform.NewResource + * terraform.NewSemaphore + * terraform.NilHook + * terraform.NodeAbstractProvider + * terraform.NodeAbstractResource + * terraform.NodeAbstractResourceInstance + * terraform.NodeApplyableModuleVariable + * terraform.NodeApplyableOutput + * terraform.NodeApplyableProvider + * terraform.NodeApplyableResource + * terraform.NodeApplyableResourceInstance + * terraform.NodeCountBoundary + * terraform.NodeDestroyableDataResourceInstance + * terraform.NodeDestroyableOutput + * terraform.NodeDestroyDeposedResourceInstanceObject + * terraform.NodeDestroyResource + * terraform.NodeDestroyResourceInstance + * terraform.NodeDisabledProvider + * terraform.NodeEvalableProvider + * terraform.NodeLocal + * terraform.NodeModuleRemoved + * terraform.NodeOutputOrphan + * terraform.NodePlanDeposedResourceInstanceObject + * terraform.NodePlanDestroyableResourceInstance + * terraform.NodePlannableResource + * terraform.NodePlannableResourceInstance + * terraform.NodePlannableResourceInstanceOrphan + * terraform.NodeProvisioner + * terraform.NodeRefreshableDataResource + * terraform.NodeRefreshableDataResourceInstance + * terraform.NodeRefreshableManagedResource + * terraform.NodeRefreshableManagedResourceInstance + * terraform.NodeRootVariable + * terraform.NodeValidatableResource + * terraform.NullGraphWalker + * terraform.OrphanOutputTransformer + * terraform.OrphanResourceCountTransformer + * terraform.OrphanResourceInstanceTransformer + * terraform.OrphanResourceTransformer + * terraform.OutputTransformer + * terraform.ParentProviderTransformer + * terraform.ParseInstanceType + * terraform.ParseResourceAddress + * terraform.ParseResourceAddressForInstanceDiff + * terraform.ParseResourceIndex + * terraform.ParseResourcePath + * terraform.ParseResourceStateKey + * terraform.PathObjectCacheKey + * terraform.Plan + * terraform.PlanGraphBuilder + * terraform.PrefixUIInput + * terraform.ProviderConfigTransformer + * terraform.ProviderEvalTree + * terraform.ProviderHasDataSource + * terraform.ProviderHasResource + * terraform.ProviderSchema.SchemaForResourceAddr + * terraform.ProviderSchema.SchemaForResourceType + * terraform.ProviderTransformer + * terraform.ProvisionerFactory + * terraform.ProvisionerTransformer + * terraform.ProvisionerUIOutput + * terraform.PruneProviderTransformer + * terraform.PruneUnusedValuesTransformer + * terraform.ReadPlan + * terraform.ReadState + * terraform.ReadStateV1 + * terraform.ReadStateV2 + * terraform.ReadStateV3 + * terraform.ReferenceMap + * terraform.ReferencesFromConfig + * terraform.ReferenceTransformer + * terraform.RefreshGraphBuilder + * terraform.RemoteState.Equals + * terraform.RemovableIfNotTargeted + * terraform.RemovedModuleTransformer + * terraform.Resource + * terraform.ResourceAddress + * terraform.ResourceAttrDiff.Empty + * terraform.ResourceConfig.CheckSet + * terraform.ResourceConfig.IsSet + * terraform.ResourceCountTransformer + * terraform.ResourceFlag + * terraform.ResourceProviderCloser (this was removed) + * terraform.ResourceProviderFactoryFixed (this was removed) + * terraform.ResourceProviderResolver + * terraform.ResourceProviderResolverFixed + * terraform.ResourceProviderResolverFunc + * terraform.ResourceProvisioner + * terraform.ResourceProvisionerCloser + * terraform.ResourceProvisionerFactory + * terraform.RootTransformer + * terraform.RootVariableTransformer + * terraform.Schemas + * terraform.Semaphore + * terraform.ShimLegacyState + * terraform.State.FromFutureTerraform + * terraform.State.MarshalEqual + * terraform.StateFilter + * terraform.StateFilterResult + * terraform.StateFilterResultSlice + * terraform.StateTransformer + * terraform.StateVersion + * terraform.TargetsTransformer + * terraform.TestStateFile + * terraform.TransformProviders + * terraform.TransitiveReductionTransformer + * terraform.TypeDeposed + * terraform.TypeInvalid + * terraform.TypePrimary + * terraform.TypeTainted + * terraform.UIInput + * terraform.UIOutput + * terraform.UpgradeResourceState + * terraform.ValidateGraphBuilder + * terraform.ValueFromAutoFile + * terraform.ValueFromCaller + * terraform.ValueFromCLIArg + * terraform.ValueFromConfig + * terraform.ValueFromEnvVar + * terraform.ValueFromInput + * terraform.ValueFromNamedFile + * terraform.ValueFromPlan + * terraform.ValueFromUnknown + * terraform.ValueSourceType + * terraform.VertexTransformer + * terraform.WritePlan + * terraform.WriteState +
+ +FEATURES +* Many functions in the SDK now have support for context.Context, including CreateContextFunc, ReadContextFunc, UpdateContextFunc, and DeleteContextFunc, analogs to the existing CreateFunc, ReadFunc, UpdateFunc, and DeleteFuncs. This offers more accurate cancellation and timeouts. ([#276](https://github.com/hashicorp/terraform-plugin-sdk/issues/276)) +* Many functions in the SDK now return a new diag.Diagnostics type, like the new CreateContextFunc, ReadContextFunc, UpdateContextFunc, DeleteContextFunc, and a new ValidateDiagFunc. When using these Diagnostics, Terraform will now indicate more precisely-scoped errors, and providers now have the ability to display warnings. +* A new feature, provider metadata, is shipping as part of Terraform 0.13. This feature allows module authors to give information to providers without the information being persisted to state, which is useful for indicating metadata about modules. This is experimental new functionality and its usage should be closely coordinated with the Terraform core team to ensure that limitations are understood. [See the PR in Terraform core for more information.](https://github.com/hashicorp/terraform/pull/22583) ([#405](https://github.com/hashicorp/terraform-plugin-sdk/issues/405)) + +DEPRECATIONS +* The ExistsFunc defined on a schema.Resource is now deprecated. This logic can be achieved in the ReadFunc for that schema.Resource instead, and often was duplicated unnecessarily. +* Functions that got context- or diagnostics-aware counterparts--like CreateFunc, ReadFunc, UpdateFunc, DeleteFunc, and ValidateFunc--are now deprecated in favor of their context- and/or diagnostics-aware counterparts. + +ENHANCEMENTS +* A number of new map validators that take advantage of the Diagnostics support have been added. ([#304](https://github.com/hashicorp/terraform-plugin-sdk/issues/304)) +* schema.Resource and schema.Schema now have optional Description fields, which will surface information for user-facing interfaces for the provider. These fields can hold plain text or markdown, depending on the global DescriptionKind setting. ([#349](https://github.com/hashicorp/terraform-plugin-sdk/issues/349)) + +BUG FIXES +* helper/acctest.RandIntRange will now correctly return an integer between min and max; previously, it would return an integer between 0 and max-min. ([#300](https://github.com/hashicorp/terraform-plugin-sdk/issues/300)) +* NonRetryableError and RetryableError will now throw an explicit error if they’re given a nil error. Before unspecified and confusing behavior would arise. ([#199](https://github.com/hashicorp/terraform-plugin-sdk/issues/199)) +* TypeSet hash values are no longer collapsed into a single value when they consist only of Computed attributes. ([#197](https://github.com/hashicorp/terraform-plugin-sdk/issues/197)) +* Computed attributes now have stronger validation around what properties can be set on their schema.Schema. ([#336](https://github.com/hashicorp/terraform-plugin-sdk/issues/336)) +* Using a schema.Resource as the Elem on a TypeMap now returns an error; previously, unspecified and confusing behavior was exhibited. ([#338](https://github.com/hashicorp/terraform-plugin-sdk/issues/338)) +* Using TestCheckResourceAttrPair to compare the same attribute on the same resource will now throw an error. ([#335](https://github.com/hashicorp/terraform-plugin-sdk/issues/335)) +* Test sweeping will now error if a dependency sweeper is specified but doesn’t exist. ([#398](https://github.com/hashicorp/terraform-plugin-sdk/issues/398)) + +--- + +For information on v1.x releases, please see [the v1 branch changelog](https://github.com/hashicorp/terraform-plugin-sdk/blob/v1-maint/CHANGELOG.md). diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/Makefile b/vendor/github.com/hashicorp/terraform-plugin-sdk/Makefile new file mode 100644 index 00000000..4ac66daf --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/Makefile @@ -0,0 +1,17 @@ +GOFMT_FILES?=$$(find . -name '*.go') + +default: test + +test: fmtcheck generate + go test ./... + +generate: + go generate ./... + +fmt: + gofmt -w $(GOFMT_FILES) + +fmtcheck: + @sh -c "'$(CURDIR)/scripts/gofmtcheck.sh'" + +.PHONY: default fmt fmtcheck generate test diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/README.md b/vendor/github.com/hashicorp/terraform-plugin-sdk/README.md new file mode 100644 index 00000000..8c4c237a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/README.md @@ -0,0 +1,72 @@ +[![PkgGoDev](https://pkg.go.dev/badge/github.com/hashicorp/terraform-plugin-sdk/v2)](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-sdk/v2) + +# Terraform Plugin SDK + +This SDK enables building Terraform plugin which allows Terraform's users to manage existing and popular service providers as well as custom in-house solutions. + +Terraform itself is a tool for building, changing, and versioning infrastructure safely and efficiently. You can find more about Terraform on its [website](https://www.terraform.io) and [its GitHub repository](https://github.com/hashicorp/terraform). + +## Terraform CLI Compatibility + +Terraform 0.12.0 or later is needed for version 2.0.0 and later of the Plugin SDK. + +When running provider tests, Terraform 0.12.26 or later is needed for version 2.0.0 and later of the Plugin SDK. Users can still use any version after 0.12.0. + +## Go Compatibility + +The Terraform Plugin SDK is built in Go, and uses the [support policy](https://golang.org/doc/devel/release.html#policy) of Go as its support policy. The two latest major releases of Go are supported by the SDK. + +Currently, that means Go **1.15** or later must be used when building a provider with the SDK. + +## Getting Started + +See the [Call APIs with Terraform Providers](https://learn.hashicorp.com/collections/terraform/providers) guide on [learn.hashicorp.com](https://learn.hashicorp.com) for a guided tour of provider development. + +## Documentation + +See [Extending Terraform](https://www.terraform.io/docs/extend/index.html) section on the website. + +## Scope (Providers VS Core) + +### Terraform Core + + - acts as gRPC _client_ + - interacts with the user + - parses (HCL/JSON) configuration + - manages state as whole, asks **Provider(s)** to mutate provider-specific parts of state + - handles backends & provisioners + - handles inputs, outputs, modules, and functions + - discovers **Provider(s)** and their versions per configuration + - manages **Provider(s)** lifecycle (i.e. spins up & tears down provider process) + - passes relevant parts of parsed (valid JSON/HCL) and interpolated configuration to **Provider(s)** + - decides ordering of (Create, Read, Update, Delete) operations on resources and data sources + - ... + +### Terraform Provider (via this SDK) + + - acts as gRPC _server_ + - executes any domain-specific logic based on received parsed configuration + - (Create, Read, Update, Delete, Import, Validate) a Resource + - Read a Data Source + - tests domain-specific logic via provided acceptance test framework + - provides **Core** updated state of a resource or data source and/or appropriate feedback in the form of validation or other errors + +## Migrating to SDK v1 from built-in SDK + +Migrating to the standalone SDK v1 is covered on the [Plugin SDK section](https://www.terraform.io/docs/extend/guides/v1-upgrade-guide.html) of the website. + +## Migrating to SDK v2 from SDK v1 + +Migrating to the v2 release of the SDK is covered in the [v2 Upgrade Guide](https://www.terraform.io/docs/extend/guides/v2-upgrade-guide.html) of the website. + +## Versioning + +The Terraform Plugin SDK is a [Go module](https://github.com/golang/go/wiki/Modules) versioned using [semantic versioning](https://semver.org/). See [SUPPORT.md](https://github.com/hashicorp/terraform-plugin-sdk/blob/main/SUPPORT.md) for information on our support policies. + +## Contributing + +See [`.github/CONTRIBUTING.md`](https://github.com/hashicorp/terraform-plugin-sdk/blob/main/.github/CONTRIBUTING.md) + +## License + +[Mozilla Public License v2.0](https://github.com/hashicorp/terraform-plugin-sdk/blob/main/LICENSE) diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/ROADMAP.md b/vendor/github.com/hashicorp/terraform-plugin-sdk/ROADMAP.md new file mode 100644 index 00000000..5dcdf19c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/ROADMAP.md @@ -0,0 +1,51 @@ +# Q1 2021 Roadmap + +Each quarter, the team will highlight areas of focus for our work and upcoming +research. + +Each release will include necessary tasks that lead to the completion of the +stated goals as well as community pull requests, enhancements, and features +that are not highlighted in the roadmap. This upcoming calendar quarter +(Jan-Mar 2021) we will be prioritizing the following areas of work: + +## Currently In Progress + +### v2.x Release + +Following the release of v2.0.0 we will continue to make improvements to the +SDK to ease the upgrade path for provider developers who have not yet migrated +and improve the functionality for those that have. + +### terraform-plugin-go / terraform-plugin-mux improvements + +Following the release of the +[terraform-plugin-go](https://github.com/hashicorp/terraform-plugin-go) and +[terraform-plugin-mux](https://github.com/hashicorp/terraform-plugin-mux) modules, we +will work to improve and refine these modules to make them more robust, easier +to use, and more approachable for developers. This includes creating [new +abstractions](https://github.com/hashicorp/terraform-plugin-go-contrib) for +terraform-plugin-go that developers may find useful and will help inform our +future design efforts. + +## Under Research + +### An HCL2-native framework + +With the release of terraform-plugin-go, we're researching, prototyping, and +designing a framework based on the same ideas and foundations, meant to mirror +terraform-plugin-sdk in approachability and ease of use while retaining +terraform-plugin-go's level of suitability for modern Terraform development. + +### A modernized testing framework + +Following the creation of our reattach-based binary test driver in v2.0.0 of +the SDK, we're investigating what a redesigned approach to testing Terraform +providers may look like and if we can aid developers in making and testing +clearer assertions about their providers. + +### More observable provider development + +We're currently researching and investigating ways in which we can surface more +information to provider developers about what is happening in their providers, +and how we can make the information we surface more accessible and easier to +digest. diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/SUPPORT.md b/vendor/github.com/hashicorp/terraform-plugin-sdk/SUPPORT.md new file mode 100644 index 00000000..606764a6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/SUPPORT.md @@ -0,0 +1,21 @@ +# Support Policy + +Version 2 of the Terraform Plugin SDK is considered **GA (generally +available)** and has the following support policy: + +- Critical bug fixes will be accepted and merged. New features and non-critical +bug fixes may be accepted at maintainers’ discretion, but our priority is +maintaining stability. +- We will not break the public interface exposed by the SDK in a minor or patch +release unless a critical security issue or bug demands it, and only then as a +last resort. +- We will continue testing version 2 of the SDK against new releases of Terraform +to ensure compatibility. +- We will not be backporting bug fixes to prior minor releases. Only the latest +minor release receives new patch versions. + +# Version 1 + +Please see the +[v1-maint](https://github.com/hashicorp/terraform-plugin-sdk/blob/v1-maint/SUPPORT.md) +branch for the support policy of version 1 of the SDK. diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/acctest/helper.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/acctest/helper.go deleted file mode 100644 index 050b9741..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/acctest/helper.go +++ /dev/null @@ -1,26 +0,0 @@ -package acctest - -import ( - "os" - - "github.com/hashicorp/terraform-plugin-sdk/plugin" - tftest "github.com/hashicorp/terraform-plugin-test" -) - -var TestHelper *tftest.Helper - -func UseBinaryDriver(name string, providerFunc plugin.ProviderFunc) { - sourceDir, err := os.Getwd() - if err != nil { - panic(err) - } - - if tftest.RunningAsPlugin() { - plugin.Serve(&plugin.ServeOpts{ - ProviderFunc: providerFunc, - }) - os.Exit(0) - } else { - TestHelper = tftest.AutoInitProviderHelper(name, sourceDir) - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/diag/diagnostic.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/diag/diagnostic.go new file mode 100644 index 00000000..3b1ced34 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/diag/diagnostic.go @@ -0,0 +1,104 @@ +package diag + +import ( + "errors" + "fmt" + + "github.com/hashicorp/go-cty/cty" +) + +// Diagnostics is a collection of Diagnostic. +// +// Developers should append and build the list of diagnostics up until a fatal +// error is reached, at which point they should return the Diagnostics to the +// SDK. +type Diagnostics []Diagnostic + +// HasError returns true is Diagnostics contains an instance of +// Severity == Error. +// +// This helper aims to mimic the go error practices of if err != nil. After any +// operation that returns Diagnostics, check that it HasError and bubble up the +// stack. +func (diags Diagnostics) HasError() bool { + for i := range diags { + if diags[i].Severity == Error { + return true + } + } + return false +} + +// Diagnostic is a contextual message intended at outlining problems in user +// configuration. +// +// It supports multiple levels of severity (Error or Warning), a short Summary +// of the problem, an optional longer Detail message that can assist the user in +// fixing the problem, as well as an AttributePath representation which +// Terraform uses to indicate where the issue took place in the user's +// configuration. +// +// A Diagnostic will typically be used to pinpoint a problem with user +// configuration, however it can still be used to present warnings or errors +// to the user without any AttributePath set. +type Diagnostic struct { + // Severity indicates the level of the Diagnostic. Currently can be set to + // either Error or Warning + Severity Severity + + // Summary is a short description of the problem, rendered above location + // information + Summary string + + // Detail is an optional second message rendered below location information + // typically used to communicate a potential fix to the user. + Detail string + + // AttributePath is a representation of the path starting from the root of + // block (resource, datasource, provider) under evaluation by the SDK, to + // the attribute that the Diagnostic should be associated to. Terraform will + // use this information to render information on where the problem took + // place in the user's configuration. + // + // It is represented with cty.Path, which is a list of steps of either + // cty.GetAttrStep (an actual attribute) or cty.IndexStep (a step with Key + // of cty.StringVal for map indexes, and cty.NumberVal for list indexes). + // + // PLEASE NOTE: While cty can support indexing into sets, the SDK and + // protocol currently do not. For any Diagnostic related to a schema.TypeSet + // or a child of that type, please terminate the path at the schema.TypeSet + // and opt for more verbose Summary and Detail to help guide the user. + // + // Validity of the AttributePath is currently the responsibility of the + // developer, Terraform should render the root block (provider, resource, + // datasource) in cases where the attribute path is invalid. + AttributePath cty.Path +} + +// Validate ensures a valid Severity and a non-empty Summary are set. +func (d Diagnostic) Validate() error { + var validSev bool + for _, sev := range severities { + if d.Severity == sev { + validSev = true + break + } + } + if !validSev { + return fmt.Errorf("invalid severity: %v", d.Severity) + } + if d.Summary == "" { + return errors.New("empty summary") + } + return nil +} + +// Severity is an enum type marking the severity level of a Diagnostic +type Severity int + +const ( + Error Severity = iota + Warning +) + +var severities = []Severity{Error, Warning} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/diag/helpers.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/diag/helpers.go new file mode 100644 index 00000000..d43eab32 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/diag/helpers.go @@ -0,0 +1,39 @@ +package diag + +import "fmt" + +// FromErr will convert an error into a Diagnostics. This returns Diagnostics +// as the most common use case in Go will be handling a single error +// returned from a function. +// +// if err != nil { +// return diag.FromErr(err) +// } +func FromErr(err error) Diagnostics { + if err == nil { + return nil + } + return Diagnostics{ + Diagnostic{ + Severity: Error, + Summary: err.Error(), + }, + } +} + +// Errorf creates a Diagnostics with a single Error level Diagnostic entry. +// The summary is populated by performing a fmt.Sprintf with the supplied +// values. This returns a single error in a Diagnostics as errors typically +// do not occur in multiples as warnings may. +// +// if unexpectedCondition { +// return diag.Errorf("unexpected: %s", someValue) +// } +func Errorf(format string, a ...interface{}) Diagnostics { + return Diagnostics{ + Diagnostic{ + Severity: Error, + Summary: fmt.Sprintf(format, a...), + }, + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/go.mod b/vendor/github.com/hashicorp/terraform-plugin-sdk/go.mod new file mode 100644 index 00000000..d7ed69af --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/go.mod @@ -0,0 +1,40 @@ +module github.com/hashicorp/terraform-plugin-sdk/v2 + +go 1.15 + +require ( + cloud.google.com/go v0.61.0 // indirect + github.com/agext/levenshtein v1.2.2 // indirect + github.com/apparentlymart/go-cidr v1.0.1 + github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 + github.com/aws/aws-sdk-go v1.25.3 // indirect + github.com/davecgh/go-spew v1.1.1 + github.com/go-test/deep v1.0.3 + github.com/golang/mock v1.4.3 + github.com/golang/snappy v0.0.1 + github.com/google/go-cmp v0.5.5 + github.com/hashicorp/errwrap v1.0.0 + github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 + github.com/hashicorp/go-hclog v0.15.0 + github.com/hashicorp/go-multierror v1.0.0 + github.com/hashicorp/go-plugin v1.4.0 + github.com/hashicorp/go-uuid v1.0.1 + github.com/hashicorp/go-version v1.3.0 + github.com/hashicorp/hcl/v2 v2.3.0 + github.com/hashicorp/logutils v1.0.0 + github.com/hashicorp/terraform-exec v0.13.3 + github.com/hashicorp/terraform-json v0.10.0 + github.com/hashicorp/terraform-plugin-go v0.3.0 + github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/mitchellh/copystructure v1.0.0 + github.com/mitchellh/go-testing-interface v1.0.4 + github.com/mitchellh/go-wordwrap v1.0.0 // indirect + github.com/mitchellh/mapstructure v1.1.2 + github.com/mitchellh/reflectwalk v1.0.1 + github.com/pierrec/lz4 v2.0.5+incompatible + github.com/zclconf/go-cty v1.8.2 + golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 + golang.org/x/tools v0.0.0-20200713011307-fd294ab11aed + google.golang.org/grpc v1.32.0 +) diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/go.sum b/vendor/github.com/hashicorp/terraform-plugin-sdk/go.sum new file mode 100644 index 00000000..c09d613b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/go.sum @@ -0,0 +1,593 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.61.0 h1:NLQf5e1OMspfNT1RAHOB3ublr1TW3YTXO8OiWwVjK2U= +cloud.google.com/go v0.61.0/go.mod h1:XukKJg4Y7QsUu0Hxg3qQKUWR4VuWivmyMK2+rUyxAqw= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= +github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/andybalholm/crlf v0.0.0-20171020200849-670099aa064f/go.mod h1:k8feO4+kXDxro6ErPXBRTJ/ro2mf0SsFG8s7doP9kJE= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/apparentlymart/go-cidr v1.0.1 h1:NmIwLZ/KdsjIUlhf+/Np40atNXm/+lZ5txfTJ/SpF+U= +github.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= +github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= +github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFUye+ZcSR6opIgz9Co7WcDx6ZcY+RjfFHoA0I= +github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= +github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0= +github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= +github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= +github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= +github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= +github.com/aws/aws-sdk-go v1.25.3 h1:uM16hIw9BotjZKMZlX05SN2EFtaWfi/NonPKIARiBLQ= +github.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.1.0 h1:4pl5BV4o7ZG/lterP4S6WzJ6xr49Ba5ET9ygheTYahk= +github.com/go-git/go-billy/v5 v5.1.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M= +github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= +github.com/go-git/go-git/v5 v5.3.0 h1:8WKMtJR2j8RntEXR/uvTKagfEt4GYlwQ7mntE4+0GWc= +github.com/go-git/go-git/v5 v5.3.0/go.mod h1:xdX4bWJ48aOrdhnl2XqHYstHbbp6+LFS4r4X+lNVprw= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= +github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= +github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= +github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= +github.com/hashicorp/go-getter v1.5.3 h1:NF5+zOlQegim+w/EUhSLh6QhXHmZMEeHLQzllkQ3ROU= +github.com/hashicorp/go-getter v1.5.3/go.mod h1:BrrV/1clo8cCYu6mxvboYg+KutTiFnXjMEgDD8+i7ZI= +github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.15.0 h1:qMuK0wxsoW4D0ddCCYwPSTm4KQv1X1ke3WmPWZ0Mvsk= +github.com/hashicorp/go-hclog v0.15.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-plugin v1.3.0/go.mod h1:F9eH4LrE/ZsRdbwhfjs9k9HoDUwAHnYtXdgmf1AVNs0= +github.com/hashicorp/go-plugin v1.4.0 h1:b0O7rs5uiJ99Iu9HugEzsM67afboErkHUWddUSpUO3A= +github.com/hashicorp/go-plugin v1.4.0/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= +github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= +github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= +github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl/v2 v2.3.0 h1:iRly8YaMwTBAKhn1Ybk7VSdzbnopghktCD031P8ggUE= +github.com/hashicorp/hcl/v2 v2.3.0/go.mod h1:d+FwDBbOLvpAM3Z6J7gPj/VoAGkNe/gm352ZhjJ/Zv8= +github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/terraform-exec v0.13.3 h1:R6L2mNpDGSEqtLrSONN8Xth0xYwNrnEVzDz6LF/oJPk= +github.com/hashicorp/terraform-exec v0.13.3/go.mod h1:SSg6lbUsVB3DmFyCPjBPklqf6EYGX0TlQ6QTxOlikDU= +github.com/hashicorp/terraform-json v0.10.0 h1:9syPD/Y5t+3uFjG8AiWVPu1bklJD8QB8iTCaJASc8oQ= +github.com/hashicorp/terraform-json v0.10.0/go.mod h1:3defM4kkMfttwiE7VakJDwCd4R+umhSQnvJwORXbprE= +github.com/hashicorp/terraform-plugin-go v0.3.0 h1:AJqYzP52JFYl9NABRI7smXI1pNjgR5Q/y2WyVJ/BOZA= +github.com/hashicorp/terraform-plugin-go v0.3.0/go.mod h1:dFHsQMaTLpON2gWhVWT96fvtlc/MF1vSy3OdMhWBzdM= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= +github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= +github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.11.2 h1:MiK62aErc3gIiVEtyzKfeOHgW7atJb5g/KNX5m3c2nQ= +github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= +github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.0.4 h1:ZU1VNC02qyufSZsjjs7+khruk2fKvbQ3TwRV/IBCeFA= +github.com/mitchellh/go-testing-interface v1.0.4/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= +github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k+Mg7cowZ8yv4Trqw9UsJby758= +github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= +github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= +github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= +github.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= +github.com/zclconf/go-cty v1.8.2 h1:u+xZfBKgpycDnTNjPhGiTEYZS5qS/Sb5MqSfm7vzcjg= +github.com/zclconf/go-cty v1.8.2/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= +github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492 h1:Paq34FxTluEPvVyayQqMPgHm+vTOrIifmcYxFBx9TLg= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200713011307-fd294ab11aed h1:+qzWo37K31KxduIYaBeMqJ8MUOyTayOQKpH9aDPLMSY= +golang.org/x/tools v0.0.0-20200713011307-fd294ab11aed/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0 h1:BaiDisFir8O4IJxvAabCGGkQ6yCJegNQqSVoYUNAnbk= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200711021454-869866162049 h1:YFTFpQhgvrLrmxtiIncJxFXeCyq84ixuKWVCaCAi9Oc= +google.golang.org/genproto v0.0.0-20200711021454-869866162049/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/acctest/random.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/acctest/random.go new file mode 100644 index 00000000..97422756 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/acctest/random.go @@ -0,0 +1,176 @@ +package acctest + +import ( + "bytes" + crand "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "math/rand" + "net" + "strings" + "time" + + "github.com/apparentlymart/go-cidr/cidr" + "golang.org/x/crypto/ssh" +) + +func init() { + rand.Seed(time.Now().UTC().UnixNano()) +} + +// Helpers for generating random tidbits for use in identifiers to prevent +// collisions in acceptance tests. + +// RandInt generates a random integer +func RandInt() int { + return rand.New(rand.NewSource(time.Now().UnixNano())).Int() +} + +// RandomWithPrefix is used to generate a unique name with a prefix, for +// randomizing names in acceptance tests +func RandomWithPrefix(name string) string { + return fmt.Sprintf("%s-%d", name, rand.New(rand.NewSource(time.Now().UnixNano())).Int()) +} + +// RandIntRange returns a random integer between min (inclusive) and max (exclusive) +func RandIntRange(min int, max int) int { + source := rand.New(rand.NewSource(time.Now().UnixNano())) + rangeMax := max - min + + return int(source.Int31n(int32(rangeMax))) + min +} + +// RandString generates a random alphanumeric string of the length specified +func RandString(strlen int) string { + return RandStringFromCharSet(strlen, CharSetAlphaNum) +} + +// RandStringFromCharSet generates a random string by selecting characters from +// the charset provided +func RandStringFromCharSet(strlen int, charSet string) string { + result := make([]byte, strlen) + for i := 0; i < strlen; i++ { + result[i] = charSet[rand.Intn(len(charSet))] + } + return string(result) +} + +// RandSSHKeyPair generates a public and private SSH key pair. The public key is +// returned in OpenSSH format, and the private key is PEM encoded. +func RandSSHKeyPair(comment string) (string, string, error) { + privateKey, privateKeyPEM, err := genPrivateKey() + if err != nil { + return "", "", err + } + + publicKey, err := ssh.NewPublicKey(&privateKey.PublicKey) + if err != nil { + return "", "", err + } + keyMaterial := strings.TrimSpace(string(ssh.MarshalAuthorizedKey(publicKey))) + return fmt.Sprintf("%s %s", keyMaterial, comment), privateKeyPEM, nil +} + +// RandTLSCert generates a self-signed TLS certificate with a newly created +// private key, and returns both the cert and the private key PEM encoded. +func RandTLSCert(orgName string) (string, string, error) { + template := &x509.Certificate{ + SerialNumber: big.NewInt(int64(RandInt())), + Subject: pkix.Name{ + Organization: []string{orgName}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().Add(24 * time.Hour), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + privateKey, privateKeyPEM, err := genPrivateKey() + if err != nil { + return "", "", err + } + + cert, err := x509.CreateCertificate(crand.Reader, template, template, &privateKey.PublicKey, privateKey) + if err != nil { + return "", "", err + } + + certPEM, err := pemEncode(cert, "CERTIFICATE") + if err != nil { + return "", "", err + } + + return certPEM, privateKeyPEM, nil +} + +// RandIpAddress returns a random IP address in the specified CIDR block. +// The prefix length must be less than 31. +func RandIpAddress(s string) (string, error) { + _, network, err := net.ParseCIDR(s) + if err != nil { + return "", err + } + + firstIp, lastIp := cidr.AddressRange(network) + first := &big.Int{} + first.SetBytes([]byte(firstIp)) + last := &big.Int{} + last.SetBytes([]byte(lastIp)) + r := &big.Int{} + r.Sub(last, first) + if len := r.BitLen(); len > 31 { + return "", fmt.Errorf("CIDR range is too large: %d", len) + } + + max := int(r.Int64()) + if max == 0 { + // panic: invalid argument to Int31n + return firstIp.String(), nil + } + + host, err := cidr.Host(network, RandIntRange(0, max)) + if err != nil { + return "", err + } + + return host.String(), nil +} + +func genPrivateKey() (*rsa.PrivateKey, string, error) { + privateKey, err := rsa.GenerateKey(crand.Reader, 1024) + if err != nil { + return nil, "", err + } + + privateKeyPEM, err := pemEncode(x509.MarshalPKCS1PrivateKey(privateKey), "RSA PRIVATE KEY") + if err != nil { + return nil, "", err + } + + return privateKey, privateKeyPEM, nil +} + +func pemEncode(b []byte, block string) (string, error) { + var buf bytes.Buffer + pb := &pem.Block{Type: block, Bytes: b} + if err := pem.Encode(&buf, pb); err != nil { + return "", err + } + + return buf.String(), nil +} + +const ( + // CharSetAlphaNum is the alphanumeric character set for use with + // RandStringFromCharSet + CharSetAlphaNum = "abcdefghijklmnopqrstuvwxyz012346789" + + // CharSetAlpha is the alphabetical character set for use with + // RandStringFromCharSet + CharSetAlpha = "abcdefghijklmnopqrstuvwxyz" +) diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/acctest/random_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/acctest/random_test.go new file mode 100644 index 00000000..f8dea92b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/acctest/random_test.go @@ -0,0 +1,65 @@ +package acctest + +import ( + "regexp" + "testing" +) + +func TestRandIntRange(t *testing.T) { + v := RandInt() + if vv := RandIntRange(v, v+1); vv != v { + t.Errorf("expected RandIntRange(%d, %d) to return %d, got %d", v, v+1, v, vv) + } +} + +func TestRandIpAddress(t *testing.T) { + testCases := []struct { + s string + expected *regexp.Regexp + expectedErr string + }{ + { + s: "1.1.1.1/32", + expected: regexp.MustCompile(`^1\.1\.1\.1$`), + }, + { + s: "10.0.0.0/8", + expected: regexp.MustCompile(`^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$`), + }, + { + s: "0.0.0.0/0", + expectedErr: "CIDR range is too large: 32", + }, + { + s: "449d:e5f1:14b1:ddf3:8525:7e9e:4a0d:4a82/128", + expected: regexp.MustCompile(`^449d:e5f1:14b1:ddf3:8525:7e9e:4a0d:4a82$`), + }, + { + s: "2001:db8::/112", + expected: regexp.MustCompile(`^2001:db8::[[:xdigit:]]{1,4}$`), + }, + { + s: "2001:db8::/64", + expectedErr: "CIDR range is too large: 64", + }, + { + s: "abcdefg", + expectedErr: "invalid CIDR address: abcdefg", + }, + } + + for i, tc := range testCases { + v, err := RandIpAddress(tc.s) + if err != nil { + msg := err.Error() + if tc.expectedErr == "" { + t.Fatalf("expected test case %d to succeed but got error %q, ", i, msg) + } + if msg != tc.expectedErr { + t.Fatalf("expected test case %d to fail with %q but got %q", i, tc.expectedErr, msg) + } + } else if !tc.expected.MatchString(v) { + t.Fatalf("expected test case %d to return %q but got %q", i, tc.expected, v) + } + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/compose.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/compose.go new file mode 100644 index 00000000..9b1b929d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/compose.go @@ -0,0 +1,75 @@ +package customdiff + +import ( + "context" + + "github.com/hashicorp/go-multierror" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// All returns a CustomizeDiffFunc that runs all of the given +// CustomizeDiffFuncs and returns all of the errors produced. +// +// If one function produces an error, functions after it are still run. +// If this is not desirable, use function Sequence instead. +// +// If multiple functions returns errors, the result is a multierror. +// +// For example: +// +// &schema.Resource{ +// // ... +// CustomizeDiff: customdiff.All( +// customdiff.ValidateChange("size", func (old, new, meta interface{}) error { +// // If we are increasing "size" then the new value must be +// // a multiple of the old value. +// if new.(int) <= old.(int) { +// return nil +// } +// if (new.(int) % old.(int)) != 0 { +// return fmt.Errorf("new size value must be an integer multiple of old value %d", old.(int)) +// } +// return nil +// }), +// customdiff.ForceNewIfChange("size", func (old, new, meta interface{}) bool { +// // "size" can only increase in-place, so we must create a new resource +// // if it is decreased. +// return new.(int) < old.(int) +// }), +// customdiff.ComputedIf("version_id", func (d *schema.ResourceDiff, meta interface{}) bool { +// // Any change to "content" causes a new "version_id" to be allocated. +// return d.HasChange("content") +// }), +// ), +// } +// +func All(funcs ...schema.CustomizeDiffFunc) schema.CustomizeDiffFunc { + return func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error { + var err error + for _, f := range funcs { + thisErr := f(ctx, d, meta) + if thisErr != nil { + err = multierror.Append(err, thisErr) + } + } + return err + } +} + +// Sequence returns a CustomizeDiffFunc that runs all of the given +// CustomizeDiffFuncs in sequence, stopping at the first one that returns +// an error and returning that error. +// +// If all functions succeed, the combined function also succeeds. +func Sequence(funcs ...schema.CustomizeDiffFunc) schema.CustomizeDiffFunc { + return func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error { + for _, f := range funcs { + err := f(ctx, d, meta) + if err != nil { + return err + } + } + return nil + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/compose_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/compose_test.go new file mode 100644 index 00000000..61b1accd --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/compose_test.go @@ -0,0 +1,111 @@ +package customdiff + +import ( + "context" + "errors" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func TestAll(t *testing.T) { + var aCalled, bCalled, cCalled bool + + provider := testProvider( + map[string]*schema.Schema{}, + All( + func(_ context.Context, d *schema.ResourceDiff, meta interface{}) error { + aCalled = true + return errors.New("A bad") + }, + func(_ context.Context, d *schema.ResourceDiff, meta interface{}) error { + bCalled = true + return nil + }, + func(_ context.Context, d *schema.ResourceDiff, meta interface{}) error { + cCalled = true + return errors.New("C bad") + }, + ), + ) + + _, err := testDiff( + provider, + map[string]string{ + "foo": "bar", + }, + map[string]string{ + "foo": "baz", + }, + ) + + if err == nil { + t.Fatal("Diff succeeded; want error") + } + if s, sub := err.Error(), "* A bad"; !strings.Contains(s, sub) { + t.Errorf("Missing substring %q in error message %q", sub, s) + } + if s, sub := err.Error(), "* C bad"; !strings.Contains(s, sub) { + t.Errorf("Missing substring %q in error message %q", sub, s) + } + + if !aCalled { + t.Error("customize callback A was not called") + } + if !bCalled { + t.Error("customize callback B was not called") + } + if !cCalled { + t.Error("customize callback C was not called") + } +} + +func TestSequence(t *testing.T) { + var aCalled, bCalled, cCalled bool + + provider := testProvider( + map[string]*schema.Schema{}, + Sequence( + func(_ context.Context, d *schema.ResourceDiff, meta interface{}) error { + aCalled = true + return nil + }, + func(_ context.Context, d *schema.ResourceDiff, meta interface{}) error { + bCalled = true + return errors.New("B bad") + }, + func(_ context.Context, d *schema.ResourceDiff, meta interface{}) error { + cCalled = true + return errors.New("C bad") + }, + ), + ) + + _, err := testDiff( + provider, + map[string]string{ + "foo": "bar", + }, + map[string]string{ + "foo": "baz", + }, + ) + + if err == nil { + t.Fatal("Diff succeeded; want error") + } + if got, want := err.Error(), "B bad"; got != want { + t.Errorf("Wrong error message %q; want %q", got, want) + } + + if !aCalled { + t.Error("customize callback A was not called") + } + if !bCalled { + t.Error("customize callback B was not called") + } + if cCalled { + t.Error("customize callback C was called (should not have been)") + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/computed.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/computed.go new file mode 100644 index 00000000..f47d11a4 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/computed.go @@ -0,0 +1,18 @@ +package customdiff + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// ComputedIf returns a CustomizeDiffFunc that sets the given key's new value +// as computed if the given condition function returns true. +func ComputedIf(key string, f ResourceConditionFunc) schema.CustomizeDiffFunc { + return func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error { + if f(ctx, d, meta) { + d.SetNewComputed(key) + } + return nil + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/computed_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/computed_test.go new file mode 100644 index 00000000..9350078d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/computed_test.go @@ -0,0 +1,127 @@ +package customdiff + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func TestComputedIf(t *testing.T) { + t.Run("true", func(t *testing.T) { + var condCalls int + var gotOld, gotNew string + + provider := testProvider( + map[string]*schema.Schema{ + "foo": { + Type: schema.TypeString, + Optional: true, + }, + "comp": { + Type: schema.TypeString, + Computed: true, + }, + }, + ComputedIf("comp", func(_ context.Context, d *schema.ResourceDiff, meta interface{}) bool { + // When we set "ForceNew", our CustomizeDiff function is actually + // called a second time to construct the "create" portion of + // the replace diff. On the second call, the old value is masked + // as "" to suggest that the object is being created rather than + // updated. + + condCalls++ + old, new := d.GetChange("foo") + gotOld = old.(string) + gotNew = new.(string) + + return true + }), + ) + + diff, err := testDiff( + provider, + map[string]string{ + "foo": "bar", + "comp": "old", + }, + map[string]string{ + "foo": "baz", + }, + ) + + if err != nil { + t.Fatalf("Diff failed with error: %s", err) + } + + if condCalls != 1 { + t.Fatalf("Wrong number of conditional callback calls %d; want %d", condCalls, 1) + } else { + if got, want := gotOld, "bar"; got != want { + t.Errorf("wrong old value %q on first call; want %q", got, want) + } + if got, want := gotNew, "baz"; got != want { + t.Errorf("wrong new value %q on first call; want %q", got, want) + } + } + + if !diff.Attributes["comp"].NewComputed { + t.Error("Attribute 'comp' is not marked as NewComputed") + } + }) + t.Run("false", func(t *testing.T) { + var condCalls int + var gotOld, gotNew string + + provider := testProvider( + map[string]*schema.Schema{ + "foo": { + Type: schema.TypeString, + Optional: true, + }, + "comp": { + Type: schema.TypeString, + Computed: true, + }, + }, + ComputedIf("comp", func(_ context.Context, d *schema.ResourceDiff, meta interface{}) bool { + condCalls++ + old, new := d.GetChange("foo") + gotOld = old.(string) + gotNew = new.(string) + + return false + }), + ) + + diff, err := testDiff( + provider, + map[string]string{ + "foo": "bar", + "comp": "old", + }, + map[string]string{ + "foo": "baz", + }, + ) + + if err != nil { + t.Fatalf("Diff failed with error: %s", err) + } + + if condCalls != 1 { + t.Fatalf("Wrong number of conditional callback calls %d; want %d", condCalls, 1) + } else { + if got, want := gotOld, "bar"; got != want { + t.Errorf("wrong old value %q on first call; want %q", got, want) + } + if got, want := gotNew, "baz"; got != want { + t.Errorf("wrong new value %q on first call; want %q", got, want) + } + } + + if diff.Attributes["comp"] != nil && diff.Attributes["comp"].NewComputed { + t.Error("Attribute 'foo' is marked as NewComputed, but should not be") + } + }) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/condition.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/condition.go new file mode 100644 index 00000000..e85cdc7a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/condition.go @@ -0,0 +1,62 @@ +package customdiff + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// ResourceConditionFunc is a function type that makes a boolean decision based +// on an entire resource diff. +type ResourceConditionFunc func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) bool + +// ValueChangeConditionFunc is a function type that makes a boolean decision +// by comparing two values. +type ValueChangeConditionFunc func(ctx context.Context, old, new, meta interface{}) bool + +// ValueConditionFunc is a function type that makes a boolean decision based +// on a given value. +type ValueConditionFunc func(ctx context.Context, value, meta interface{}) bool + +// If returns a CustomizeDiffFunc that calls the given condition +// function and then calls the given CustomizeDiffFunc only if the condition +// function returns true. +// +// This can be used to include conditional customizations when composing +// customizations using All and Sequence, but should generally be used only in +// simple scenarios. Prefer directly writing a CustomizeDiffFunc containing +// a conditional branch if the given CustomizeDiffFunc is already a +// locally-defined function, since this avoids obscuring the control flow. +func If(cond ResourceConditionFunc, f schema.CustomizeDiffFunc) schema.CustomizeDiffFunc { + return func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error { + if cond(ctx, d, meta) { + return f(ctx, d, meta) + } + return nil + } +} + +// IfValueChange returns a CustomizeDiffFunc that calls the given condition +// function with the old and new values of the given key and then calls the +// given CustomizeDiffFunc only if the condition function returns true. +func IfValueChange(key string, cond ValueChangeConditionFunc, f schema.CustomizeDiffFunc) schema.CustomizeDiffFunc { + return func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error { + old, new := d.GetChange(key) + if cond(ctx, old, new, meta) { + return f(ctx, d, meta) + } + return nil + } +} + +// IfValue returns a CustomizeDiffFunc that calls the given condition +// function with the new values of the given key and then calls the +// given CustomizeDiffFunc only if the condition function returns true. +func IfValue(key string, cond ValueConditionFunc, f schema.CustomizeDiffFunc) schema.CustomizeDiffFunc { + return func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error { + if cond(ctx, d.Get(key), meta) { + return f(ctx, d, meta) + } + return nil + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/condition_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/condition_test.go new file mode 100644 index 00000000..fcaa4a27 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/condition_test.go @@ -0,0 +1,349 @@ +package customdiff + +import ( + "context" + "errors" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func TestIf(t *testing.T) { + t.Run("true", func(t *testing.T) { + var condCalled, customCalled bool + var gotOld, gotNew string + + provider := testProvider( + map[string]*schema.Schema{ + "foo": { + Type: schema.TypeString, + Optional: true, + }, + }, + If( + func(_ context.Context, d *schema.ResourceDiff, meta interface{}) bool { + condCalled = true + old, new := d.GetChange("foo") + gotOld = old.(string) + gotNew = new.(string) + return true + }, + func(_ context.Context, d *schema.ResourceDiff, meta interface{}) error { + customCalled = true + return errors.New("bad") + }, + ), + ) + + _, err := testDiff( + provider, + map[string]string{ + "foo": "bar", + }, + map[string]string{ + "foo": "baz", + }, + ) + + if err == nil { + t.Fatal("Diff succeeded; want error") + } + if got, want := err.Error(), "bad"; got != want { + t.Fatalf("wrong error message %q; want %q", got, want) + } + + if !condCalled { + t.Error("condition callback was not called") + } else { + if got, want := gotOld, "bar"; got != want { + t.Errorf("wrong old value %q; want %q", got, want) + } + if got, want := gotNew, "baz"; got != want { + t.Errorf("wrong new value %q; want %q", got, want) + } + } + + if !customCalled { + t.Error("customize callback was not called") + } + }) + t.Run("false", func(t *testing.T) { + var condCalled, customCalled bool + var gotOld, gotNew string + + provider := testProvider( + map[string]*schema.Schema{ + "foo": { + Type: schema.TypeString, + Optional: true, + }, + }, + If( + func(_ context.Context, d *schema.ResourceDiff, meta interface{}) bool { + condCalled = true + old, new := d.GetChange("foo") + gotOld = old.(string) + gotNew = new.(string) + return false + }, + func(_ context.Context, d *schema.ResourceDiff, meta interface{}) error { + customCalled = true + return errors.New("bad") + }, + ), + ) + + _, err := testDiff( + provider, + map[string]string{ + "foo": "bar", + }, + map[string]string{ + "foo": "baz", + }, + ) + + if err != nil { + t.Fatalf("Diff error %q; want success", err.Error()) + } + + if !condCalled { + t.Error("condition callback was not called") + } else { + if got, want := gotOld, "bar"; got != want { + t.Errorf("wrong old value %q; want %q", got, want) + } + if got, want := gotNew, "baz"; got != want { + t.Errorf("wrong new value %q; want %q", got, want) + } + } + + if customCalled { + t.Error("customize callback was called (should not have been)") + } + }) +} + +func TestIfValueChange(t *testing.T) { + t.Run("true", func(t *testing.T) { + var condCalled, customCalled bool + var gotOld, gotNew string + + provider := testProvider( + map[string]*schema.Schema{ + "foo": { + Type: schema.TypeString, + Optional: true, + }, + }, + IfValueChange( + "foo", + func(_ context.Context, old, new, meta interface{}) bool { + condCalled = true + gotOld = old.(string) + gotNew = new.(string) + return true + }, + func(_ context.Context, d *schema.ResourceDiff, meta interface{}) error { + customCalled = true + return errors.New("bad") + }, + ), + ) + + _, err := testDiff( + provider, + map[string]string{ + "foo": "bar", + }, + map[string]string{ + "foo": "baz", + }, + ) + + if err == nil { + t.Fatal("Diff succeeded; want error") + } + if got, want := err.Error(), "bad"; got != want { + t.Fatalf("wrong error message %q; want %q", got, want) + } + + if !condCalled { + t.Error("condition callback was not called") + } else { + if got, want := gotOld, "bar"; got != want { + t.Errorf("wrong old value %q; want %q", got, want) + } + if got, want := gotNew, "baz"; got != want { + t.Errorf("wrong new value %q; want %q", got, want) + } + } + + if !customCalled { + t.Error("customize callback was not called") + } + }) + t.Run("false", func(t *testing.T) { + var condCalled, customCalled bool + var gotOld, gotNew string + + provider := testProvider( + map[string]*schema.Schema{ + "foo": { + Type: schema.TypeString, + Optional: true, + }, + }, + IfValueChange( + "foo", + func(_ context.Context, old, new, meta interface{}) bool { + condCalled = true + gotOld = old.(string) + gotNew = new.(string) + return false + }, + func(_ context.Context, d *schema.ResourceDiff, meta interface{}) error { + customCalled = true + return errors.New("bad") + }, + ), + ) + + _, err := testDiff( + provider, + map[string]string{ + "foo": "bar", + }, + map[string]string{ + "foo": "baz", + }, + ) + + if err != nil { + t.Fatalf("Diff error %q; want success", err.Error()) + } + + if !condCalled { + t.Error("condition callback was not called") + } else { + if got, want := gotOld, "bar"; got != want { + t.Errorf("wrong old value %q; want %q", got, want) + } + if got, want := gotNew, "baz"; got != want { + t.Errorf("wrong new value %q; want %q", got, want) + } + } + + if customCalled { + t.Error("customize callback was called (should not have been)") + } + }) +} + +func TestIfValue(t *testing.T) { + t.Run("true", func(t *testing.T) { + var condCalled, customCalled bool + var gotValue string + + provider := testProvider( + map[string]*schema.Schema{ + "foo": { + Type: schema.TypeString, + Optional: true, + }, + }, + IfValue( + "foo", + func(_ context.Context, value, meta interface{}) bool { + condCalled = true + gotValue = value.(string) + return true + }, + func(_ context.Context, d *schema.ResourceDiff, meta interface{}) error { + customCalled = true + return errors.New("bad") + }, + ), + ) + + _, err := testDiff( + provider, + map[string]string{ + "foo": "bar", + }, + map[string]string{ + "foo": "baz", + }, + ) + + if err == nil { + t.Fatal("Diff succeeded; want error") + } + if got, want := err.Error(), "bad"; got != want { + t.Fatalf("wrong error message %q; want %q", got, want) + } + + if !condCalled { + t.Error("condition callback was not called") + } else { + if got, want := gotValue, "baz"; got != want { + t.Errorf("wrong value %q; want %q", got, want) + } + } + + if !customCalled { + t.Error("customize callback was not called") + } + }) + t.Run("false", func(t *testing.T) { + var condCalled, customCalled bool + var gotValue string + + provider := testProvider( + map[string]*schema.Schema{ + "foo": { + Type: schema.TypeString, + Optional: true, + }, + }, + IfValue( + "foo", + func(_ context.Context, value, meta interface{}) bool { + condCalled = true + gotValue = value.(string) + return false + }, + func(_ context.Context, d *schema.ResourceDiff, meta interface{}) error { + customCalled = true + return errors.New("bad") + }, + ), + ) + + _, err := testDiff( + provider, + map[string]string{ + "foo": "bar", + }, + map[string]string{ + "foo": "baz", + }, + ) + + if err != nil { + t.Fatalf("Diff error %q; want success", err.Error()) + } + + if !condCalled { + t.Error("condition callback was not called") + } else { + if got, want := gotValue, "baz"; got != want { + t.Errorf("wrong value %q; want %q", got, want) + } + } + + if customCalled { + t.Error("customize callback was called (should not have been)") + } + }) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/doc.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/doc.go new file mode 100644 index 00000000..c6ad1199 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/doc.go @@ -0,0 +1,11 @@ +// Package customdiff provides a set of reusable and composable functions +// to enable more "declarative" use of the CustomizeDiff mechanism available +// for resources in package helper/schema. +// +// The intent of these helpers is to make the intent of a set of diff +// customizations easier to see, rather than lost in a sea of Go function +// boilerplate. They should _not_ be used in situations where they _obscure_ +// intent, e.g. by over-using the composition functions where a single +// function containing normal Go control flow statements would be more +// straightforward. +package customdiff diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/force_new.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/force_new.go new file mode 100644 index 00000000..0ffc0886 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/force_new.go @@ -0,0 +1,42 @@ +package customdiff + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// ForceNewIf returns a CustomizeDiffFunc that flags the given key as +// requiring a new resource if the given condition function returns true. +// +// The return value of the condition function is ignored if the old and new +// values of the field compare equal, since no attribute diff is generated in +// that case. +func ForceNewIf(key string, f ResourceConditionFunc) schema.CustomizeDiffFunc { + return func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error { + if f(ctx, d, meta) { + d.ForceNew(key) + } + return nil + } +} + +// ForceNewIfChange returns a CustomizeDiffFunc that flags the given key as +// requiring a new resource if the given condition function returns true. +// +// The return value of the condition function is ignored if the old and new +// values compare equal, since no attribute diff is generated in that case. +// +// This function is similar to ForceNewIf but provides the condition function +// only the old and new values of the given key, which leads to more compact +// and explicit code in the common case where the decision can be made with +// only the specific field value. +func ForceNewIfChange(key string, f ValueChangeConditionFunc) schema.CustomizeDiffFunc { + return func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error { + old, new := d.GetChange(key) + if f(ctx, old, new, meta) { + d.ForceNew(key) + } + return nil + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/force_new_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/force_new_test.go new file mode 100644 index 00000000..3742cc6d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/force_new_test.go @@ -0,0 +1,250 @@ +package customdiff + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func TestForceNewIf(t *testing.T) { + t.Run("true", func(t *testing.T) { + var condCalls int + var gotOld1, gotNew1, gotOld2, gotNew2 string + + provider := testProvider( + map[string]*schema.Schema{ + "foo": { + Type: schema.TypeString, + Optional: true, + }, + }, + ForceNewIf("foo", func(_ context.Context, d *schema.ResourceDiff, meta interface{}) bool { + // When we set "ForceNew", our CustomizeDiff function is actually + // called a second time to construct the "create" portion of + // the replace diff. On the second call, the old value is masked + // as "" to suggest that the object is being created rather than + // updated. + + condCalls++ + old, new := d.GetChange("foo") + + switch condCalls { + case 1: + gotOld1 = old.(string) + gotNew1 = new.(string) + case 2: + gotOld2 = old.(string) + gotNew2 = new.(string) + } + + return true + }), + ) + + diff, err := testDiff( + provider, + map[string]string{ + "foo": "bar", + }, + map[string]string{ + "foo": "baz", + }, + ) + + if err != nil { + t.Fatalf("Diff failed with error: %s", err) + } + + if condCalls != 2 { + t.Fatalf("Wrong number of conditional callback calls %d; want %d", condCalls, 2) + } else { + if got, want := gotOld1, "bar"; got != want { + t.Errorf("wrong old value %q on first call; want %q", got, want) + } + if got, want := gotNew1, "baz"; got != want { + t.Errorf("wrong new value %q on first call; want %q", got, want) + } + if got, want := gotOld2, ""; got != want { + t.Errorf("wrong old value %q on first call; want %q", got, want) + } + if got, want := gotNew2, "baz"; got != want { + t.Errorf("wrong new value %q on first call; want %q", got, want) + } + } + + if !diff.Attributes["foo"].RequiresNew { + t.Error("Attribute 'foo' is not marked as RequiresNew") + } + }) + t.Run("false", func(t *testing.T) { + var condCalls int + var gotOld, gotNew string + + provider := testProvider( + map[string]*schema.Schema{ + "foo": { + Type: schema.TypeString, + Optional: true, + }, + }, + ForceNewIf("foo", func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) bool { + condCalls++ + old, new := d.GetChange("foo") + gotOld = old.(string) + gotNew = new.(string) + + return false + }), + ) + + diff, err := testDiff( + provider, + map[string]string{ + "foo": "bar", + }, + map[string]string{ + "foo": "baz", + }, + ) + + if err != nil { + t.Fatalf("Diff failed with error: %s", err) + } + + if condCalls != 1 { + t.Fatalf("Wrong number of conditional callback calls %d; want %d", condCalls, 1) + } else { + if got, want := gotOld, "bar"; got != want { + t.Errorf("wrong old value %q on first call; want %q", got, want) + } + if got, want := gotNew, "baz"; got != want { + t.Errorf("wrong new value %q on first call; want %q", got, want) + } + } + + if diff.Attributes["foo"].RequiresNew { + t.Error("Attribute 'foo' is marked as RequiresNew, but should not be") + } + }) +} + +func TestForceNewIfChange(t *testing.T) { + t.Run("true", func(t *testing.T) { + var condCalls int + var gotOld1, gotNew1, gotOld2, gotNew2 string + + provider := testProvider( + map[string]*schema.Schema{ + "foo": { + Type: schema.TypeString, + Optional: true, + }, + }, + ForceNewIfChange("foo", func(_ context.Context, old, new, meta interface{}) bool { + // When we set "ForceNew", our CustomizeDiff function is actually + // called a second time to construct the "create" portion of + // the replace diff. On the second call, the old value is masked + // as "" to suggest that the object is being created rather than + // updated. + + condCalls++ + + switch condCalls { + case 1: + gotOld1 = old.(string) + gotNew1 = new.(string) + case 2: + gotOld2 = old.(string) + gotNew2 = new.(string) + } + + return true + }), + ) + + diff, err := testDiff( + provider, + map[string]string{ + "foo": "bar", + }, + map[string]string{ + "foo": "baz", + }, + ) + + if err != nil { + t.Fatalf("Diff failed with error: %s", err) + } + + if condCalls != 2 { + t.Fatalf("Wrong number of conditional callback calls %d; want %d", condCalls, 2) + } else { + if got, want := gotOld1, "bar"; got != want { + t.Errorf("wrong old value %q on first call; want %q", got, want) + } + if got, want := gotNew1, "baz"; got != want { + t.Errorf("wrong new value %q on first call; want %q", got, want) + } + if got, want := gotOld2, ""; got != want { + t.Errorf("wrong old value %q on first call; want %q", got, want) + } + if got, want := gotNew2, "baz"; got != want { + t.Errorf("wrong new value %q on first call; want %q", got, want) + } + } + + if !diff.Attributes["foo"].RequiresNew { + t.Error("Attribute 'foo' is not marked as RequiresNew") + } + }) + t.Run("false", func(t *testing.T) { + var condCalls int + var gotOld, gotNew string + + provider := testProvider( + map[string]*schema.Schema{ + "foo": { + Type: schema.TypeString, + Optional: true, + }, + }, + ForceNewIfChange("foo", func(_ context.Context, old, new, meta interface{}) bool { + condCalls++ + gotOld = old.(string) + gotNew = new.(string) + + return false + }), + ) + + diff, err := testDiff( + provider, + map[string]string{ + "foo": "bar", + }, + map[string]string{ + "foo": "baz", + }, + ) + + if err != nil { + t.Fatalf("Diff failed with error: %s", err) + } + + if condCalls != 1 { + t.Fatalf("Wrong number of conditional callback calls %d; want %d", condCalls, 1) + } else { + if got, want := gotOld, "bar"; got != want { + t.Errorf("wrong old value %q on first call; want %q", got, want) + } + if got, want := gotNew, "baz"; got != want { + t.Errorf("wrong new value %q on first call; want %q", got, want) + } + } + + if diff.Attributes["foo"].RequiresNew { + t.Error("Attribute 'foo' is marked as RequiresNew, but should not be") + } + }) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/testing_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/testing_test.go new file mode 100644 index 00000000..692bf85a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/testing_test.go @@ -0,0 +1,37 @@ +package customdiff + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func testProvider(s map[string]*schema.Schema, cd schema.CustomizeDiffFunc) *schema.Provider { + return &schema.Provider{ + ResourcesMap: map[string]*schema.Resource{ + "test": { + Schema: s, + CustomizeDiff: cd, + }, + }, + } +} + +func testDiff(provider *schema.Provider, old, new map[string]string) (*terraform.InstanceDiff, error) { + newI := make(map[string]interface{}, len(new)) + for k, v := range new { + newI[k] = v + } + + return provider.ResourcesMap["test"].Diff( + context.Background(), + &terraform.InstanceState{ + Attributes: old, + }, + &terraform.ResourceConfig{ + Config: newI, + }, + provider.Meta(), + ) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/validate.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/validate.go new file mode 100644 index 00000000..5b88e5e0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/validate.go @@ -0,0 +1,40 @@ +package customdiff + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// ValueChangeValidationFunc is a function type that validates the difference +// (or lack thereof) between two values, returning an error if the change +// is invalid. +type ValueChangeValidationFunc func(ctx context.Context, old, new, meta interface{}) error + +// ValueValidationFunc is a function type that validates a particular value, +// returning an error if the value is invalid. +type ValueValidationFunc func(ctx context.Context, value, meta interface{}) error + +// ValidateChange returns a CustomizeDiffFunc that applies the given validation +// function to the change for the given key, returning any error produced. +func ValidateChange(key string, f ValueChangeValidationFunc) schema.CustomizeDiffFunc { + return func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error { + old, new := d.GetChange(key) + return f(ctx, old, new, meta) + } +} + +// ValidateValue returns a CustomizeDiffFunc that applies the given validation +// function to value of the given key, returning any error produced. +// +// This should generally not be used since it is functionally equivalent to +// a validation function applied directly to the schema attribute in question, +// but is provided for situations where composing multiple CustomizeDiffFuncs +// together makes intent clearer than spreading that validation across the +// schema. +func ValidateValue(key string, f ValueValidationFunc) schema.CustomizeDiffFunc { + return func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error { + val := d.Get(key) + return f(ctx, val, meta) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/validate_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/validate_test.go new file mode 100644 index 00000000..07cfa52e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/customdiff/validate_test.go @@ -0,0 +1,99 @@ +package customdiff + +import ( + "context" + "errors" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func TestValidateChange(t *testing.T) { + var called bool + var gotOld, gotNew string + + provider := testProvider( + map[string]*schema.Schema{ + "foo": { + Type: schema.TypeString, + Optional: true, + }, + }, + ValidateChange("foo", func(_ context.Context, old, new, meta interface{}) error { + called = true + gotOld = old.(string) + gotNew = new.(string) + return errors.New("bad") + }), + ) + + _, err := testDiff( + provider, + map[string]string{ + "foo": "bar", + }, + map[string]string{ + "foo": "baz", + }, + ) + + if err == nil { + t.Fatalf("Diff succeeded; want error") + } + if got, want := err.Error(), "bad"; got != want { + t.Fatalf("wrong error message %q; want %q", got, want) + } + + if !called { + t.Fatal("ValidateChange callback was not called") + } + if got, want := gotOld, "bar"; got != want { + t.Errorf("wrong old value %q; want %q", got, want) + } + if got, want := gotNew, "baz"; got != want { + t.Errorf("wrong new value %q; want %q", got, want) + } +} + +func TestValidateValue(t *testing.T) { + var called bool + var gotValue string + + provider := testProvider( + map[string]*schema.Schema{ + "foo": { + Type: schema.TypeString, + Optional: true, + }, + }, + ValidateValue("foo", func(_ context.Context, value, meta interface{}) error { + called = true + gotValue = value.(string) + return errors.New("bad") + }), + ) + + _, err := testDiff( + provider, + map[string]string{ + "foo": "bar", + }, + map[string]string{ + "foo": "baz", + }, + ) + + if err == nil { + t.Fatalf("Diff succeeded; want error") + } + if got, want := err.Error(), "bad"; got != want { + t.Fatalf("wrong error message %q; want %q", got, want) + } + + if !called { + t.Fatal("ValidateValue callback was not called") + } + if got, want := gotValue, "baz"; got != want { + t.Errorf("wrong value %q; want %q", got, want) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/logging/logging.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/logging/logging.go index 6bd92f77..546c5e1c 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/logging/logging.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/logging/logging.go @@ -1,6 +1,7 @@ package logging import ( + "fmt" "io" "io/ioutil" "log" @@ -9,6 +10,7 @@ import ( "syscall" "github.com/hashicorp/logutils" + testing "github.com/mitchellh/go-testing-interface" ) // These are the environmental variables that determine if we log, and if @@ -16,12 +18,14 @@ import ( const ( EnvLog = "TF_LOG" // Set to True EnvLogFile = "TF_LOG_PATH" // Set to a file + // EnvLogPathMask splits test log files by name. + EnvLogPathMask = "TF_LOG_PATH_MASK" ) var ValidLevels = []logutils.LogLevel{"TRACE", "DEBUG", "INFO", "WARN", "ERROR"} // LogOutput determines where we should send logs (if anywhere) and the log level. -func LogOutput() (logOutput io.Writer, err error) { +func LogOutput(t testing.T) (logOutput io.Writer, err error) { logOutput = ioutil.Discard logLevel := LogLevel() @@ -38,6 +42,18 @@ func LogOutput() (logOutput io.Writer, err error) { } } + if logPathMask := os.Getenv(EnvLogPathMask); logPathMask != "" { + // Escape special characters which may appear if we have subtests + testName := strings.Replace(t.Name(), "/", "__", -1) + + logPath := fmt.Sprintf(logPathMask, testName) + var err error + logOutput, err = os.OpenFile(logPath, syscall.O_CREAT|syscall.O_RDWR|syscall.O_APPEND, 0666) + if err != nil { + return nil, err + } + } + // This was the default since the beginning logOutput = &logutils.LevelFilter{ Levels: ValidLevels, @@ -51,8 +67,8 @@ func LogOutput() (logOutput io.Writer, err error) { // SetOutput checks for a log destination with LogOutput, and calls // log.SetOutput with the result. If LogOutput returns nil, SetOutput uses // ioutil.Discard. Any error from LogOutout is fatal. -func SetOutput() { - out, err := LogOutput() +func SetOutput(t testing.T) { + out, err := LogOutput(t) if err != nil { log.Fatal(err) } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/error.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/error.go index 7ee21614..b406cbae 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/error.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/error.go @@ -26,6 +26,10 @@ func (e *NotFoundError) Error() string { return "couldn't find resource" } +func (e *NotFoundError) Unwrap() error { + return e.LastError +} + // UnexpectedStateError is returned when Refresh returns a state that's neither in Target nor Pending type UnexpectedStateError struct { LastError error @@ -42,6 +46,10 @@ func (e *UnexpectedStateError) Error() string { ) } +func (e *UnexpectedStateError) Unwrap() error { + return e.LastError +} + // TimeoutError is returned when WaitForState times out type TimeoutError struct { LastError error @@ -77,3 +85,7 @@ func (e *TimeoutError) Error() string { return fmt.Sprintf("timeout while waiting for %s%s", expectedState, suffix) } + +func (e *TimeoutError) Unwrap() error { + return e.LastError +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/grpc_test_provider.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/grpc_test_provider.go deleted file mode 100644 index db12cee2..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/grpc_test_provider.go +++ /dev/null @@ -1,43 +0,0 @@ -package resource - -import ( - "context" - "net" - "time" - - "github.com/hashicorp/terraform-plugin-sdk/internal/helper/plugin" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - proto "github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5" - tfplugin "github.com/hashicorp/terraform-plugin-sdk/plugin" - "github.com/hashicorp/terraform-plugin-sdk/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 -} diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/id_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/id_test.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/resource/id_test.go rename to vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/id_test.go diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/json.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/json.go new file mode 100644 index 00000000..345abf71 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/json.go @@ -0,0 +1,12 @@ +package resource + +import ( + "bytes" + "encoding/json" +) + +func unmarshalJSON(data []byte, v interface{}) error { + dec := json.NewDecoder(bytes.NewReader(data)) + dec.UseNumber() + return dec.Decode(v) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/map.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/map.go deleted file mode 100644 index 02a993d6..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/map.go +++ /dev/null @@ -1,140 +0,0 @@ -package resource - -import ( - "fmt" - "sort" - - "github.com/hashicorp/terraform-plugin-sdk/terraform" -) - -// Map is a map of resources that are supported, and provides helpers for -// more easily implementing a ResourceProvider. -type Map struct { - Mapping map[string]Resource -} - -func (m *Map) Validate( - t string, c *terraform.ResourceConfig) ([]string, []error) { - r, ok := m.Mapping[t] - if !ok { - return nil, []error{fmt.Errorf("Unknown resource type: %s", t)} - } - - // If there is no validator set, then it is valid - if r.ConfigValidator == nil { - return nil, nil - } - - return r.ConfigValidator.Validate(c) -} - -// Apply performs a create or update depending on the diff, and calls -// the proper function on the matching Resource. -func (m *Map) Apply( - info *terraform.InstanceInfo, - s *terraform.InstanceState, - d *terraform.InstanceDiff, - meta interface{}) (*terraform.InstanceState, error) { - r, ok := m.Mapping[info.Type] - if !ok { - return nil, fmt.Errorf("Unknown resource type: %s", info.Type) - } - - if d.Destroy || d.RequiresNew() { - if s.ID != "" { - // Destroy the resource if it is created - err := r.Destroy(s, meta) - if err != nil { - return s, err - } - - s.ID = "" - } - - // If we're only destroying, and not creating, then return now. - // Otherwise, we continue so that we can create a new resource. - if !d.RequiresNew() { - return nil, nil - } - } - - var result *terraform.InstanceState - var err error - if s.ID == "" { - result, err = r.Create(s, d, meta) - } else { - if r.Update == nil { - return s, fmt.Errorf( - "Resource type '%s' doesn't support update", - info.Type) - } - - result, err = r.Update(s, d, meta) - } - if result != nil { - if result.Attributes == nil { - result.Attributes = make(map[string]string) - } - - result.Attributes["id"] = result.ID - } - - return result, err -} - -// Diff performs a diff on the proper resource type. -func (m *Map) Diff( - info *terraform.InstanceInfo, - s *terraform.InstanceState, - c *terraform.ResourceConfig, - meta interface{}) (*terraform.InstanceDiff, error) { - r, ok := m.Mapping[info.Type] - if !ok { - return nil, fmt.Errorf("Unknown resource type: %s", info.Type) - } - - return r.Diff(s, c, meta) -} - -// Refresh performs a Refresh on the proper resource type. -// -// Refresh on the Resource won't be called if the state represents a -// non-created resource (ID is blank). -// -// An error is returned if the resource isn't registered. -func (m *Map) Refresh( - info *terraform.InstanceInfo, - s *terraform.InstanceState, - meta interface{}) (*terraform.InstanceState, error) { - // If the resource isn't created, don't refresh. - if s.ID == "" { - return s, nil - } - - r, ok := m.Mapping[info.Type] - if !ok { - return nil, fmt.Errorf("Unknown resource type: %s", info.Type) - } - - return r.Refresh(s, meta) -} - -// Resources returns all the resources that are supported by this -// resource map and can be used to satisfy the Resources method of -// a ResourceProvider. -func (m *Map) Resources() []terraform.ResourceType { - ks := make([]string, 0, len(m.Mapping)) - for k, _ := range m.Mapping { - ks = append(ks, k) - } - sort.Strings(ks) - - rs := make([]terraform.ResourceType, 0, len(m.Mapping)) - for _, k := range ks { - rs = append(rs, terraform.ResourceType{ - Name: k, - }) - } - - return rs -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/plugin.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/plugin.go new file mode 100644 index 00000000..bef3a442 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/plugin.go @@ -0,0 +1,234 @@ +package resource + +import ( + "context" + "fmt" + "io/ioutil" + "log" + "os" + "strings" + "sync" + + "github.com/hashicorp/go-hclog" + "github.com/hashicorp/terraform-exec/tfexec" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugintest" + "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" + testing "github.com/mitchellh/go-testing-interface" +) + +func runProviderCommand(t testing.T, f func() error, wd *plugintest.WorkingDir, factories map[string]func() (*schema.Provider, error), v5factories map[string]func() (tfprotov5.ProviderServer, error)) error { + // don't point to this as a test failure location + // point to whatever called it + t.Helper() + + // Run the providers in the same process as the test runner using the + // reattach behavior in Terraform. This ensures we get test coverage + // and enables the use of delve as a debugger. + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // this is needed so Terraform doesn't default to expecting protocol 4; + // we're skipping the handshake because Terraform didn't launch the + // plugins. + os.Setenv("PLUGIN_PROTOCOL_VERSIONS", "5") + + // Terraform doesn't need to reach out to Checkpoint during testing. + wd.Setenv("CHECKPOINT_DISABLE", "1") + + // Terraform 0.12.X and 0.13.X+ treat namespaceless providers + // differently in terms of what namespace they default to. So we're + // going to set both variations, as we don't know which version of + // Terraform we're talking to. We're also going to allow overriding + // the host or namespace using environment variables. + var namespaces []string + host := "registry.terraform.io" + if v := os.Getenv("TF_ACC_PROVIDER_NAMESPACE"); v != "" { + namespaces = append(namespaces, v) + } else { + namespaces = append(namespaces, "-", "hashicorp") + } + if v := os.Getenv("TF_ACC_PROVIDER_HOST"); v != "" { + host = v + } + + // Spin up gRPC servers for every provider factory, start a + // WaitGroup to listen for all of the close channels. + var wg sync.WaitGroup + reattachInfo := map[string]tfexec.ReattachConfig{} + for providerName, factory := range factories { + // providerName may be returned as terraform-provider-foo, and + // we need just foo. So let's fix that. + providerName = strings.TrimPrefix(providerName, "terraform-provider-") + + provider, err := factory() + if err != nil { + return fmt.Errorf("unable to create provider %q from factory: %w", providerName, err) + } + + // keep track of the running factory, so we can make sure it's + // shut down. + wg.Add(1) + + // configure the settings our plugin will be served with + // the GRPCProviderFunc wraps a non-gRPC provider server + // into a gRPC interface, and the logger just discards logs + // from go-plugin. + opts := &plugin.ServeOpts{ + GRPCProviderFunc: func() tfprotov5.ProviderServer { + return schema.NewGRPCProviderServer(provider) + }, + Logger: hclog.New(&hclog.LoggerOptions{ + Name: "plugintest", + Level: hclog.Trace, + Output: ioutil.Discard, + }), + NoLogOutputOverride: true, + } + + // let's actually start the provider server + config, closeCh, err := plugin.DebugServe(ctx, opts) + if err != nil { + return fmt.Errorf("unable to serve provider %q: %w", providerName, err) + } + tfexecConfig := tfexec.ReattachConfig{ + Protocol: config.Protocol, + Pid: config.Pid, + Test: config.Test, + Addr: tfexec.ReattachConfigAddr{ + Network: config.Addr.Network, + String: config.Addr.String, + }, + } + + // when the provider exits, remove one from the waitgroup + // so we can track when everything is done + go func(c <-chan struct{}) { + <-c + wg.Done() + }(closeCh) + + // set our provider's reattachinfo in our map, once + // for every namespace that different Terraform versions + // may expect. + for _, ns := range namespaces { + reattachInfo[strings.TrimSuffix(host, "/")+"/"+ + strings.TrimSuffix(ns, "/")+"/"+ + providerName] = tfexecConfig + } + } + + // Now spin up gRPC servers for every plugin-go provider factory + // in the same way. + for providerName, factory := range v5factories { + // providerName may be returned as terraform-provider-foo, and + // we need just foo. So let's fix that. + providerName = strings.TrimPrefix(providerName, "terraform-provider-") + + // If the user has supplied the same provider in both + // ProviderFactories and ProtoV5ProviderFactories, they made a + // mistake and we should exit early. + for _, ns := range namespaces { + reattachString := strings.TrimSuffix(host, "/") + "/" + + strings.TrimSuffix(ns, "/") + "/" + + providerName + if _, ok := reattachInfo[reattachString]; ok { + return fmt.Errorf("Provider %s registered in both TestCase.ProviderFactories and TestCase.ProtoV5ProviderFactories: please use one or the other, or supply a muxed provider to TestCase.ProtoV5ProviderFactories.", providerName) + } + } + + provider, err := factory() + if err != nil { + return fmt.Errorf("unable to create provider %q from factory: %w", providerName, err) + } + + // keep track of the running factory, so we can make sure it's + // shut down. + wg.Add(1) + + // configure the settings our plugin will be served with + // the GRPCProviderFunc wraps a non-gRPC provider server + // into a gRPC interface, and the logger just discards logs + // from go-plugin. + opts := &plugin.ServeOpts{ + GRPCProviderFunc: func() tfprotov5.ProviderServer { + return provider + }, + Logger: hclog.New(&hclog.LoggerOptions{ + Name: "plugintest", + Level: hclog.Trace, + Output: ioutil.Discard, + }), + NoLogOutputOverride: true, + } + + // let's actually start the provider server + config, closeCh, err := plugin.DebugServe(ctx, opts) + if err != nil { + return fmt.Errorf("unable to serve provider %q: %w", providerName, err) + } + tfexecConfig := tfexec.ReattachConfig{ + Protocol: config.Protocol, + Pid: config.Pid, + Test: config.Test, + Addr: tfexec.ReattachConfigAddr{ + Network: config.Addr.Network, + String: config.Addr.String, + }, + } + + // when the provider exits, remove one from the waitgroup + // so we can track when everything is done + go func(c <-chan struct{}) { + <-c + wg.Done() + }(closeCh) + + // set our provider's reattachinfo in our map, once + // for every namespace that different Terraform versions + // may expect. + for _, ns := range namespaces { + reattachString := strings.TrimSuffix(host, "/") + "/" + + strings.TrimSuffix(ns, "/") + "/" + + providerName + reattachInfo[reattachString] = tfexecConfig + } + } + + // set the working directory reattach info that will tell Terraform how to + // connect to our various running servers. + wd.SetReattachInfo(reattachInfo) + + // ok, let's call whatever Terraform command the test was trying to + // call, now that we know it'll attach back to those servers we just + // started. + err := f() + if err != nil { + log.Printf("[WARN] Got error running Terraform: %s", err) + } + + // cancel the servers so they'll return. Otherwise, this closeCh won't + // get closed, and we'll hang here. + cancel() + + // wait for the servers to actually shut down; it may take a moment for + // them to clean up, or whatever. + // TODO: add a timeout here? + // PC: do we need one? The test will time out automatically... + wg.Wait() + + // once we've run the Terraform command, let's remove the reattach + // information from the WorkingDir's environment. The WorkingDir will + // persist until the next call, but the server in the reattach info + // doesn't exist anymore at this point, so the reattach info is no + // longer valid. In theory it should be overwritten in the next call, + // but just to avoid any confusing bug reports, let's just unset the + // environment variable altogether. + wd.UnsetReattachInfo() + + // return any error returned from the orchestration code running + // Terraform commands + return err +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/resource.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/resource.go deleted file mode 100644 index 80782413..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/resource.go +++ /dev/null @@ -1,49 +0,0 @@ -package resource - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/helper/config" - "github.com/hashicorp/terraform-plugin-sdk/terraform" -) - -type Resource struct { - ConfigValidator *config.Validator - Create CreateFunc - Destroy DestroyFunc - Diff DiffFunc - Refresh RefreshFunc - Update UpdateFunc -} - -// CreateFunc is a function that creates a resource that didn't previously -// exist. -type CreateFunc func( - *terraform.InstanceState, - *terraform.InstanceDiff, - interface{}) (*terraform.InstanceState, error) - -// DestroyFunc is a function that destroys a resource that previously -// exists using the state. -type DestroyFunc func( - *terraform.InstanceState, - interface{}) error - -// DiffFunc is a function that performs a diff of a resource. -type DiffFunc func( - *terraform.InstanceState, - *terraform.ResourceConfig, - interface{}) (*terraform.InstanceDiff, error) - -// RefreshFunc is a function that performs a refresh of a specific type -// of resource. -type RefreshFunc func( - *terraform.InstanceState, - interface{}) (*terraform.InstanceState, error) - -// UpdateFunc is a function that is called to update a resource that -// previously existed. The difference between this and CreateFunc is that -// the diff is guaranteed to only contain attributes that don't require -// a new resource. -type UpdateFunc func( - *terraform.InstanceState, - *terraform.InstanceDiff, - interface{}) (*terraform.InstanceState, error) diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/state.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/state.go index 88a83966..6eda1993 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/state.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/state.go @@ -1,6 +1,7 @@ package resource import ( + "context" "log" "time" ) @@ -13,7 +14,7 @@ var refreshGracePeriod = 30 * time.Second // 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. +// it. A nil result represents not found. // // `state` is the latest state of that object. And `err` is any error that // may have happened while refreshing the state. @@ -28,13 +29,13 @@ type StateChangeConf struct { 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 + NotFoundChecks int // Number of times to allow not found (nil result from Refresh) // 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 +// WaitForStateContext 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. // @@ -48,7 +49,9 @@ type StateChangeConf struct { // // 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) { +// +// Cancellation from the passed in context will cancel the refresh loop +func (conf *StateChangeConf) WaitForStateContext(ctx context.Context) (interface{}, error) { log.Printf("[DEBUG] Waiting for state to become: %s", conf.Target) notfoundTick := 0 @@ -80,7 +83,11 @@ func (conf *StateChangeConf) WaitForState() (interface{}, error) { go func() { defer close(resCh) - time.Sleep(conf.Delay) + select { + case <-time.After(conf.Delay): + case <-cancelCh: + return + } // start with 0 delay for the first loop var wait time.Duration @@ -213,7 +220,9 @@ func (conf *StateChangeConf) WaitForState() (interface{}, error) { // still waiting, store the last result lastResult = r - + case <-ctx.Done(): + close(cancelCh) + return nil, ctx.Err() case <-timeout: log.Printf("[WARN] WaitForState timeout after %s", conf.Timeout) log.Printf("[WARN] WaitForState starting %s refresh grace period", refreshGracePeriod) @@ -242,6 +251,9 @@ func (conf *StateChangeConf) WaitForState() (interface{}, error) { // target state not reached, save the result for the // TimeoutError and wait for the channel to close lastResult = r + case <-ctx.Done(): + log.Println("[ERROR] Context cancelation detected, abandoning grace period") + break forSelect case <-timeout: log.Println("[ERROR] WaitForState exceeded refresh grace period") break forSelect @@ -257,3 +269,12 @@ func (conf *StateChangeConf) WaitForState() (interface{}, error) { } } } + +// 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. +// +// Deprecated: Please use WaitForStateContext to ensure proper plugin shutdown +func (conf *StateChangeConf) WaitForState() (interface{}, error) { + return conf.WaitForStateContext(context.Background()) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/state_shim.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/state_shim.go index 768b4847..7f8c2252 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/state_shim.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/state_shim.go @@ -6,187 +6,11 @@ import ( "strconv" tfjson "github.com/hashicorp/terraform-json" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - "github.com/hashicorp/terraform-plugin-sdk/terraform" - "github.com/zclconf/go-cty/cty" -) - -// 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.Type - providerType := res.ProviderConfig.ProviderConfig.Type - - resource := getResource(providers, providerType, res.Addr) - - for key, i := range res.Instances { - resState := &terraform.ResourceState{ - Type: resType, - Provider: res.ProviderConfig.String(), - } - - // 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 - } - - for _, dep := range i.Current.Dependencies { - resState.Dependencies = append(resState.Dependencies, dep.String()) - } - - // 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.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 -} + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/addrs" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/tfdiags" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) type shimmedState struct { state *terraform.State @@ -247,11 +71,11 @@ func shimOutputState(so *tfjson.StateOutput) (*terraform.OutputState, error) { elements[i] = el.(bool) } os.Value = elements - // unmarshalled number from JSON will always be float64 - case float64: + // unmarshalled number from JSON will always be json.Number + case json.Number: elements := make([]interface{}, len(v)) for i, el := range v { - elements[i] = el.(float64) + elements[i] = el.(json.Number) } os.Value = elements case []interface{}: @@ -270,10 +94,10 @@ func shimOutputState(so *tfjson.StateOutput) (*terraform.OutputState, error) { os.Type = "string" os.Value = strconv.FormatBool(v) return os, nil - // unmarshalled number from JSON will always be float64 - case float64: + // unmarshalled number from JSON will always be json.Number + case json.Number: os.Type = "string" - os.Value = strconv.FormatFloat(v, 'f', -1, 64) + os.Value = v.String() return os, nil } @@ -332,8 +156,13 @@ func shimResourceStateKey(res *tfjson.StateResource) (string, error) { var index int switch idx := res.Index.(type) { - case float64: - index = int(idx) + case json.Number: + i, err := idx.Int64() + if err != nil { + return "", fmt.Errorf("unexpected index value (%q) for %q, ", + idx, res.Address) + } + index = int(i) default: return "", fmt.Errorf("unexpected index type (%T) for %q, "+ "for_each is not supported", res.Index, res.Address) @@ -433,8 +262,8 @@ func (sf *shimmedFlatmap) AddEntry(key string, value interface{}) error { return nil case bool: sf.m[key] = strconv.FormatBool(el) - case float64: - sf.m[key] = strconv.FormatFloat(el, 'f', -1, 64) + case json.Number: + sf.m[key] = el.String() case string: sf.m[key] = el case map[string]interface{}: diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/state_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/state_test.go new file mode 100644 index 00000000..1d223263 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/state_test.go @@ -0,0 +1,373 @@ +package resource + +import ( + "context" + "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") + } +} + +func TestWaitForStateContext_cancel(t *testing.T) { + // make this refresh func block until we cancel it + ctx, cancel := context.WithCancel(context.Background()) + refresh := func() (interface{}, string, error) { + <-ctx.Done() + return nil, "pending", nil + } + conf := &StateChangeConf{ + Pending: []string{"pending", "incomplete"}, + Target: []string{"running"}, + Refresh: refresh, + Timeout: 10 * time.Second, + } + + var err error + + waitDone := make(chan struct{}) + go func() { + defer close(waitDone) + _, err = conf.WaitForStateContext(ctx) + }() + + // make sure WaitForState is blocked + select { + case <-waitDone: + t.Fatal("WaitForState returned too early") + case <-time.After(10 * time.Millisecond): + } + + // unlock the refresh function + cancel() + // make sure WaitForState returns + select { + case <-waitDone: + case <-time.After(time.Second): + t.Fatal("WaitForState didn't return after refresh finished") + } + + if err != context.Canceled { + t.Fatalf("Expected canceled context error, got: %s", err) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing.go index 8fa28d7b..fc9644b3 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing.go @@ -1,37 +1,24 @@ package resource import ( - "bytes" "errors" "flag" "fmt" - "io" - "io/ioutil" "log" "os" - "path/filepath" - "reflect" "regexp" + "strconv" "strings" - "syscall" - "testing" - "github.com/davecgh/go-spew/spew" - "github.com/hashicorp/errwrap" "github.com/hashicorp/go-multierror" - "github.com/hashicorp/logutils" - "github.com/hashicorp/terraform-plugin-sdk/acctest" - "github.com/hashicorp/terraform-plugin-sdk/helper/logging" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/command/format" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload" - "github.com/hashicorp/terraform-plugin-sdk/internal/initwd" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - "github.com/hashicorp/terraform-plugin-sdk/terraform" - "github.com/mitchellh/colorstring" + testing "github.com/mitchellh/go-testing-interface" + + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/logging" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/addrs" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugintest" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) // flagSweep is a flag available when running tests on the command line. It @@ -95,7 +82,9 @@ func AddTestSweepers(name string, s *Sweeper) { sweeperFuncs[name] = s } -func TestMain(m *testing.M) { +func TestMain(m interface { + Run() int +}) { flag.Parse() if *flagSweep != "" { // parse flagSweep contents for regions to run @@ -108,10 +97,8 @@ func TestMain(m *testing.M) { os.Exit(1) } } else { - if acctest.TestHelper != nil { - defer acctest.TestHelper.Close() - } - os.Exit(m.Run()) + exitCode := m.Run() + os.Exit(exitCode) } } @@ -219,20 +206,22 @@ func filterSweeperWithDependencies(name string, source map[string]*Sweeper) map[ // add the success/fail status to the sweeperRunList. func runSweeperWithRegion(region string, s *Sweeper, sweepers map[string]*Sweeper, sweeperRunList map[string]error, allowFailures bool) error { for _, dep := range s.Dependencies { - if depSweeper, ok := sweepers[dep]; ok { - log.Printf("[DEBUG] Sweeper (%s) has dependency (%s), running..", s.Name, dep) - err := runSweeperWithRegion(region, depSweeper, sweepers, sweeperRunList, allowFailures) + depSweeper, ok := sweepers[dep] - if err != nil { - if allowFailures { - log.Printf("[ERROR] Error running Sweeper (%s) in region (%s): %s", depSweeper.Name, region, err) - continue - } + if !ok { + return fmt.Errorf("sweeper (%s) has dependency (%s), but that sweeper was not found", s.Name, dep) + } + + log.Printf("[DEBUG] Sweeper (%s) has dependency (%s), running..", s.Name, dep) + err := runSweeperWithRegion(region, depSweeper, sweepers, sweeperRunList, allowFailures) - return err + if err != nil { + if allowFailures { + log.Printf("[ERROR] Error running Sweeper (%s) in region (%s): %s", depSweeper.Name, region, err) + continue } - } else { - log.Printf("[WARN] Sweeper (%s) has dependency (%s), but that sweeper was not found", s.Name, dep) + + return err } } @@ -256,13 +245,6 @@ func runSweeperWithRegion(region string, s *Sweeper, sweepers map[string]*Sweepe const TestEnvVar = "TF_ACC" -// TestProvider can be implemented by any ResourceProvider to provide custom -// reset functionality at the start of an acceptance test. -// The helper/schema Provider implements this interface. -type TestProvider interface { - TestReset() error -} - // TestCheckFunc is the callback type used with acceptance tests to check // the state of a resource. The state passed in is the latest state known, // or in the case of being after a destroy, it is the last known state when @@ -276,6 +258,9 @@ type ImportStateCheckFunc func([]*terraform.InstanceState) error // generation for ImportState tests. type ImportStateIdFunc func(*terraform.State) (string, error) +// ErrorCheckFunc is a function providers can use to handle errors. +type ErrorCheckFunc func(error) error + // TestCase is a single acceptance test case used to test the apply/destroy // lifecycle of a resource in a specific configuration. // @@ -295,15 +280,43 @@ type TestCase struct { // acceptance tests, such as verifying that keys are setup. PreCheck func() - // Providers is the ResourceProvider that will be under test. + // ProviderFactories can be specified for the providers that are valid. + // + // These are the providers that can be referenced within the test. Each key + // is an individually addressable provider. Typically you will only pass a + // single value here for the provider you are testing. Aliases are not + // supported by the test framework, so to use multiple provider instances, + // you should add additional copies to this map with unique names. To set + // their configuration, you would reference them similar to the following: + // + // provider "my_factory_key" { + // # ... + // } + // + // resource "my_resource" "mr" { + // provider = my_factory_key // - // Alternately, ProviderFactories can be specified for the providers - // that are valid. This takes priority over Providers. + // # ... + // } + ProviderFactories map[string]func() (*schema.Provider, error) + + // ProtoV5ProviderFactories serves the same purpose as ProviderFactories, + // but for protocol v5 providers defined using the terraform-plugin-go + // ProviderServer interface. + ProtoV5ProviderFactories map[string]func() (tfprotov5.ProviderServer, error) + + // Providers is the ResourceProvider that will be under test. // - // The end effect of each is the same: specifying the providers that - // are used within the tests. - Providers map[string]terraform.ResourceProvider - ProviderFactories map[string]terraform.ResourceProviderFactory + // Deprecated: Providers is deprecated, please use ProviderFactories + Providers map[string]*schema.Provider + + // ExternalProviders are providers the TestCase relies on that should + // be downloaded from the registry during init. This is only really + // necessary to set if you're using import, as providers in your config + // will be automatically retrieved during init. Import doesn't use a + // config, however, so we allow manually specifying them here to be + // downloaded for import tests. + ExternalProviders map[string]ExternalProvider // PreventPostDestroyRefresh can be set to true for cases where data sources // are tested alongside real resources @@ -313,6 +326,10 @@ type TestCase struct { // to allow the tester to test that the resource is truly gone. CheckDestroy TestCheckFunc + // ErrorCheck allows providers the option to handle errors such as skipping + // tests based on certain errors. + ErrorCheck ErrorCheckFunc + // Steps are the apply sequences done within the context of the // same state. Each step can have its own check to verify correctness. Steps []TestStep @@ -328,11 +345,13 @@ type TestCase struct { // IDRefreshIgnore is a list of configuration keys that will be ignored. IDRefreshName string IDRefreshIgnore []string +} - // DisableBinaryDriver forces this test case to run using the legacy test - // driver, even if the binary test driver has been enabled. - // This property will be removed in version 2.0.0 of the SDK. - DisableBinaryDriver bool +// ExternalProvider holds information about third-party providers that should +// be downloaded by Terraform as part of running the test step. +type ExternalProvider struct { + VersionConstraint string // the version constraint for the provider + Source string // the provider source } // TestStep is a single apply sequence of a test, done within the @@ -463,53 +482,6 @@ type TestStep struct { // fields that can't be refreshed and don't matter. ImportStateVerify bool ImportStateVerifyIgnore []string - - // provider s is used internally to maintain a reference to the - // underlying providers during the tests - providers map[string]terraform.ResourceProvider -} - -// Set to a file mask in sprintf format where %s is test name -const EnvLogPathMask = "TF_LOG_PATH_MASK" - -func LogOutput(t TestT) (logOutput io.Writer, err error) { - logOutput = ioutil.Discard - - logLevel := logging.LogLevel() - if logLevel == "" { - return - } - - logOutput = os.Stderr - - if logPath := os.Getenv(logging.EnvLogFile); logPath != "" { - var err error - logOutput, err = os.OpenFile(logPath, syscall.O_CREAT|syscall.O_RDWR|syscall.O_APPEND, 0666) - if err != nil { - return nil, err - } - } - - if logPathMask := os.Getenv(EnvLogPathMask); logPathMask != "" { - // Escape special characters which may appear if we have subtests - testName := strings.Replace(t.Name(), "/", "__", -1) - - logPath := fmt.Sprintf(logPathMask, testName) - var err error - logOutput, err = os.OpenFile(logPath, syscall.O_CREAT|syscall.O_RDWR|syscall.O_APPEND, 0666) - if err != nil { - return nil, err - } - } - - // This was the default since the beginning - logOutput = &logutils.LevelFilter{ - Levels: logging.ValidLevels, - MinLevel: logutils.LogLevel(logLevel), - Writer: logOutput, - } - - return } // ParallelTest performs an acceptance test on a resource, allowing concurrency @@ -518,7 +490,8 @@ func LogOutput(t TestT) (logOutput io.Writer, err error) { // Tests will fail if they do not properly handle conditions to allow multiple // tests to occur against the same resource or service (e.g. random naming). // All other requirements of the Test function also apply to this function. -func ParallelTest(t TestT, c TestCase) { +func ParallelTest(t testing.T, c TestCase) { + t.Helper() t.Parallel() Test(t, c) } @@ -533,7 +506,9 @@ func ParallelTest(t TestT, c TestCase) { // the "-test.v" flag) is set. Because some acceptance tests take quite // long, we require the verbose flag so users are able to see progress // output. -func Test(t TestT, c TestCase) { +func Test(t testing.T, c TestCase) { + t.Helper() + // We only run acceptance tests if an env var is set because they're // slow and generally require some outside configuration. You can opt out // of this with OverrideEnvVar on individual TestCases. @@ -544,400 +519,90 @@ func Test(t TestT, c TestCase) { return } - logWriter, err := LogOutput(t) - if err != nil { - t.Error(fmt.Errorf("error setting up logging: %s", err)) - } - log.SetOutput(logWriter) + logging.SetOutput(t) - // We require verbose mode so that the user knows what is going on. - if !testTesting && !testing.Verbose() && !c.IsUnitTest { - t.Fatal("Acceptance tests must be run with the -v flag on tests") - return - } + // Copy any explicitly passed providers to factories, this is for backwards compatibility. + if len(c.Providers) > 0 { + c.ProviderFactories = map[string]func() (*schema.Provider, error){} - // Run the PreCheck if we have it - if c.PreCheck != nil { - c.PreCheck() - } - - // get instances of all providers, so we can use the individual - // resources to shim the state during the tests. - providers := make(map[string]terraform.ResourceProvider) - for name, pf := range testProviderFactories(c) { - p, err := pf() - if err != nil { - t.Fatal(err) + for name, p := range c.Providers { + if _, ok := c.ProviderFactories[name]; ok { + t.Fatalf("ProviderFactory for %q already exists, cannot overwrite with Provider", name) + } + prov := p + c.ProviderFactories[name] = func() (*schema.Provider, error) { + return prov, nil + } } - providers[name] = p } - if acctest.TestHelper != nil && c.DisableBinaryDriver == false { - // inject providers for ImportStateVerify - RunNewTest(t.(*testing.T), c, providers) - return + // Run the PreCheck if we have it. + // This is done after the auto-configure to allow providers + // to override the default auto-configure parameters. + if c.PreCheck != nil { + c.PreCheck() } - providerResolver, err := testProviderResolver(c) + sourceDir, err := os.Getwd() if err != nil { - t.Fatal(err) + t.Fatalf("Error getting working dir: %s", err) } - - opts := terraform.ContextOpts{ProviderResolver: providerResolver} - - // A single state variable to track the lifecycle, starting with no state - var state *terraform.State - - // Go through each step and run it - var idRefreshCheck *terraform.ResourceState - idRefresh := c.IDRefreshName != "" - errored := false - for i, step := range c.Steps { - // insert the providers into the step so we can get the resources for - // shimming the state - step.providers = providers - - var err error - log.Printf("[DEBUG] Test: Executing step %d", i) - - if step.SkipFunc != nil { - skip, err := step.SkipFunc() - if err != nil { - t.Fatal(err) - } - if skip { - log.Printf("[WARN] Skipping step %d", i) - continue - } - } - - if step.Config == "" && !step.ImportState { - err = fmt.Errorf( - "unknown test mode for step. Please see TestStep docs\n\n%#v", - step) - } else { - if step.ImportState { - if step.Config == "" { - step.Config = testProviderConfig(c) - } - - // Can optionally set step.Config in addition to - // step.ImportState, to provide config for the import. - state, err = testStepImportState(opts, state, step) - } else { - state, err = testStepConfig(opts, state, step) - } - } - - // If we expected an error, but did not get one, fail - if err == nil && step.ExpectError != nil { - errored = true - t.Error(fmt.Sprintf( - "Step %d, no error received, but expected a match to:\n\n%s\n\n", - i, step.ExpectError)) - break - } - - // If there was an error, exit + helper := plugintest.AutoInitProviderHelper(sourceDir) + defer func(helper *plugintest.Helper) { + err := helper.Close() if err != nil { - // Perhaps we expected an error? Check if it matches - if step.ExpectError != nil { - if !step.ExpectError.MatchString(err.Error()) { - errored = true - t.Error(fmt.Sprintf( - "Step %d, expected error:\n\n%s\n\nTo match:\n\n%s\n\n", - i, err, step.ExpectError)) - break - } - } else { - errored = true - t.Error(fmt.Sprintf("Step %d error: %s", i, detailedErrorMessage(err))) - break - } + log.Printf("Error cleaning up temporary test files: %s", err) } + }(helper) - // If we've never checked an id-only refresh and our state isn't - // empty, find the first resource and test it. - if idRefresh && idRefreshCheck == nil && !state.Empty() { - // Find the first non-nil resource in the state - for _, m := range state.Modules { - if len(m.Resources) > 0 { - if v, ok := m.Resources[c.IDRefreshName]; ok { - idRefreshCheck = v - } - - break - } - } - - // If we have an instance to check for refreshes, do it - // immediately. We do it in the middle of another test - // because it shouldn't affect the overall state (refresh - // is read-only semantically) and we want to fail early if - // this fails. If refresh isn't read-only, then this will have - // caught a different bug. - if idRefreshCheck != nil { - log.Printf( - "[WARN] Test: Running ID-only refresh check on %s", - idRefreshCheck.Primary.ID) - if err := testIDOnlyRefresh(c, opts, step, idRefreshCheck); err != nil { - log.Printf("[ERROR] Test: ID-only test failed: %s", err) - t.Error(fmt.Sprintf( - "[ERROR] Test: ID-only test failed: %s", err)) - break - } - } - } - } - - // If we never checked an id-only refresh, it is a failure. - if idRefresh { - if !errored && len(c.Steps) > 0 && idRefreshCheck == nil { - t.Error("ID-only refresh check never ran.") - } - } - - // If we have a state, then run the destroy - if state != nil { - lastStep := c.Steps[len(c.Steps)-1] - destroyStep := TestStep{ - Config: lastStep.Config, - Check: c.CheckDestroy, - Destroy: true, - PreventDiskCleanup: lastStep.PreventDiskCleanup, - PreventPostDestroyRefresh: c.PreventPostDestroyRefresh, - providers: providers, - } - - log.Printf("[WARN] Test: Executing destroy step") - state, err := testStep(opts, state, destroyStep) - if err != nil { - t.Error(fmt.Sprintf( - "Error destroying resource! WARNING: Dangling resources\n"+ - "may exist. The full state and error is shown below.\n\n"+ - "Error: %s\n\nState: %s", - err, - state)) - } - } else { - log.Printf("[WARN] Skipping destroy test since there is no state.") - } + runNewTest(t, c, helper) } // testProviderConfig takes the list of Providers in a TestCase and returns a // config with only empty provider blocks. This is useful for Import, where no // config is provided, but the providers must be defined. -func testProviderConfig(c TestCase) string { +func testProviderConfig(c TestCase) (string, error) { var lines []string + var requiredProviders []string for p := range c.Providers { lines = append(lines, fmt.Sprintf("provider %q {}\n", p)) } - - return strings.Join(lines, "") -} - -// testProviderFactories combines the fixed Providers and -// ResourceProviderFactory functions into a single map of -// ResourceProviderFactory functions. -func testProviderFactories(c TestCase) map[string]terraform.ResourceProviderFactory { - ctxProviders := make(map[string]terraform.ResourceProviderFactory) - for k, pf := range c.ProviderFactories { - ctxProviders[k] = pf - } - - // add any fixed providers - for k, p := range c.Providers { - ctxProviders[k] = terraform.ResourceProviderFactoryFixed(p) - } - return ctxProviders -} - -// testProviderResolver is a helper to build a ResourceProviderResolver -// with pre instantiated ResourceProviders, so that we can reset them for the -// test, while only calling the factory function once. -// Any errors are stored so that they can be returned by the factory in -// terraform to match non-test behavior. -func testProviderResolver(c TestCase) (providers.Resolver, error) { - ctxProviders := testProviderFactories(c) - - // wrap the old provider factories in the test grpc server so they can be - // called from terraform. - newProviders := make(map[string]providers.Factory) - - for k, pf := range ctxProviders { - factory := pf // must copy to ensure each closure sees its own value - newProviders[k] = func() (providers.Interface, error) { - p, err := factory() - if err != nil { - return nil, err - } - - // The provider is wrapped in a GRPCTestProvider so that it can be - // passed back to terraform core as a providers.Interface, rather - // than the legacy ResourceProvider. - return GRPCTestProvider(p), nil + for p, v := range c.ExternalProviders { + if _, ok := c.Providers[p]; ok { + return "", fmt.Errorf("Provider %q set in both Providers and ExternalProviders for TestCase. Must be set in only one.", p) } - } - - return providers.ResolverFixed(newProviders), nil -} - -// UnitTest is a helper to force the acceptance testing harness to run in the -// normal unit test suite. This should only be used for resource that don't -// have any external dependencies. -func UnitTest(t TestT, c TestCase) { - c.IsUnitTest = true - Test(t, c) -} - -func testIDOnlyRefresh(c TestCase, opts terraform.ContextOpts, step TestStep, r *terraform.ResourceState) error { - // TODO: We guard by this right now so master doesn't explode. We - // need to remove this eventually to make this part of the normal tests. - if os.Getenv("TF_ACC_IDONLY") == "" { - return nil - } - - addr := addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: r.Type, - Name: "foo", - }.Instance(addrs.NoKey) - absAddr := addr.Absolute(addrs.RootModuleInstance) - - // Build the state. The state is just the resource with an ID. There - // are no attributes. We only set what is needed to perform a refresh. - state := states.NewState() - state.RootModule().SetResourceInstanceCurrent( - addr, - &states.ResourceInstanceObjectSrc{ - AttrsFlat: r.Primary.Attributes, - Status: states.ObjectReady, - }, - addrs.ProviderConfig{Type: "placeholder"}.Absolute(addrs.RootModuleInstance), - ) - - // Create the config module. We use the full config because Refresh - // doesn't have access to it and we may need things like provider - // configurations. The initial implementation of id-only checks used - // an empty config module, but that caused the aforementioned problems. - cfg, err := testConfig(opts, step) - if err != nil { - return err - } - - // Initialize the context - opts.Config = cfg - opts.State = state - ctx, ctxDiags := terraform.NewContext(&opts) - if ctxDiags.HasErrors() { - return ctxDiags.Err() - } - if diags := ctx.Validate(); len(diags) > 0 { - if diags.HasErrors() { - return errwrap.Wrapf("config is invalid: {{err}}", diags.Err()) + if _, ok := c.ProviderFactories[p]; ok { + return "", fmt.Errorf("Provider %q set in both ProviderFactories and ExternalProviders for TestCase. Must be set in only one.", p) } - - log.Printf("[WARN] Config warnings:\n%s", diags.Err().Error()) - } - - // Refresh! - state, refreshDiags := ctx.Refresh() - if refreshDiags.HasErrors() { - return refreshDiags.Err() - } - - // Verify attribute equivalence. - actualR := state.ResourceInstance(absAddr) - if actualR == nil { - return fmt.Errorf("Resource gone!") - } - if actualR.Current == nil { - return fmt.Errorf("Resource has no primary instance") - } - actual := actualR.Current.AttrsFlat - expected := r.Primary.Attributes - // Remove fields we're ignoring - for _, v := range c.IDRefreshIgnore { - for k, _ := range actual { - if strings.HasPrefix(k, v) { - delete(actual, k) - } + lines = append(lines, fmt.Sprintf("provider %q {}\n", p)) + var providerBlock string + if v.VersionConstraint != "" { + providerBlock = fmt.Sprintf("%s\nversion = %q", providerBlock, v.VersionConstraint) } - for k, _ := range expected { - if strings.HasPrefix(k, v) { - delete(expected, k) - } + if v.Source != "" { + providerBlock = fmt.Sprintf("%s\nsource = %q", providerBlock, v.Source) } - } - - 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) - } + if providerBlock != "" { + providerBlock = fmt.Sprintf("%s = {%s\n}\n", p, providerBlock) } - - spewConf := spew.NewDefaultConfig() - spewConf.SortKeys = true - return fmt.Errorf( - "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 nil -} - -func testConfig(opts terraform.ContextOpts, step TestStep) (*configs.Config, error) { - if step.PreConfig != nil { - step.PreConfig() + requiredProviders = append(requiredProviders, providerBlock) } - cfgPath, err := ioutil.TempDir("", "tf-test") - if err != nil { - return nil, fmt.Errorf("Error creating temporary directory for config: %s", err) + if len(requiredProviders) > 0 { + lines = append([]string{fmt.Sprintf("terraform {\nrequired_providers {\n%s}\n}\n\n", strings.Join(requiredProviders, ""))}, lines...) } - if step.PreventDiskCleanup { - log.Printf("[INFO] Skipping defer os.RemoveAll call") - } else { - defer os.RemoveAll(cfgPath) - } - - // Write the main configuration file - err = ioutil.WriteFile(filepath.Join(cfgPath, "main.tf"), []byte(step.Config), os.ModePerm) - if err != nil { - return nil, fmt.Errorf("Error creating temporary file for config: %s", err) - } - - // Create directory for our child modules, if any. - modulesDir := filepath.Join(cfgPath, ".modules") - err = os.Mkdir(modulesDir, os.ModePerm) - if err != nil { - return nil, fmt.Errorf("Error creating child modules directory: %s", err) - } - - inst := initwd.NewModuleInstaller(modulesDir, nil) - _, installDiags := inst.InstallModules(cfgPath, true, initwd.ModuleInstallHooksImpl{}) - if installDiags.HasErrors() { - return nil, installDiags.Err() - } - - loader, err := configload.NewLoader(&configload.Config{ - ModulesDir: modulesDir, - }) - if err != nil { - return nil, fmt.Errorf("failed to create config loader: %s", err) - } + return strings.Join(lines, ""), nil +} - config, configDiags := loader.LoadConfig(cfgPath) - if configDiags.HasErrors() { - return nil, configDiags - } +// UnitTest is a helper to force the acceptance testing harness to run in the +// normal unit test suite. This should only be used for resource that don't +// have any external dependencies. +func UnitTest(t testing.T, c TestCase) { + t.Helper() - return config, nil + c.IsUnitTest = true + Test(t, c) } func testResource(c TestStep, state *terraform.State) (*terraform.ResourceState, error) { @@ -1001,28 +666,28 @@ func ComposeAggregateTestCheckFunc(fs ...TestCheckFunc) TestCheckFunc { // testing that computed values were set, when it is not possible to // know ahead of time what the values will be. func TestCheckResourceAttrSet(name, key string) TestCheckFunc { - return func(s *terraform.State) error { + return checkIfIndexesIntoTypeSet(key, func(s *terraform.State) error { is, err := primaryInstanceState(s, name) if err != nil { return err } return testCheckResourceAttrSet(is, name, key) - } + }) } // TestCheckModuleResourceAttrSet - as per TestCheckResourceAttrSet but with // support for non-root modules func TestCheckModuleResourceAttrSet(mp []string, name string, key string) TestCheckFunc { mpt := addrs.Module(mp).UnkeyedInstanceShim() - return func(s *terraform.State) error { + return checkIfIndexesIntoTypeSet(key, func(s *terraform.State) error { is, err := modulePathPrimaryInstanceState(s, mpt, name) if err != nil { return err } return testCheckResourceAttrSet(is, name, key) - } + }) } func testCheckResourceAttrSet(is *terraform.InstanceState, name string, key string) error { @@ -1036,28 +701,28 @@ func testCheckResourceAttrSet(is *terraform.InstanceState, name string, key stri // TestCheckResourceAttr is a TestCheckFunc which validates // the value in state for the given name/key combination. func TestCheckResourceAttr(name, key, value string) TestCheckFunc { - return func(s *terraform.State) error { + return checkIfIndexesIntoTypeSet(key, func(s *terraform.State) error { is, err := primaryInstanceState(s, name) if err != nil { return err } return testCheckResourceAttr(is, name, key, value) - } + }) } // TestCheckModuleResourceAttr - as per TestCheckResourceAttr but with // support for non-root modules func TestCheckModuleResourceAttr(mp []string, name string, key string, value string) TestCheckFunc { mpt := addrs.Module(mp).UnkeyedInstanceShim() - return func(s *terraform.State) error { + return checkIfIndexesIntoTypeSet(key, func(s *terraform.State) error { is, err := modulePathPrimaryInstanceState(s, mpt, name) if err != nil { return err } return testCheckResourceAttr(is, name, key, value) - } + }) } func testCheckResourceAttr(is *terraform.InstanceState, name string, key string, value string) error { @@ -1091,28 +756,28 @@ func testCheckResourceAttr(is *terraform.InstanceState, name string, key string, // TestCheckNoResourceAttr is a TestCheckFunc which ensures that // NO value exists in state for the given name/key combination. func TestCheckNoResourceAttr(name, key string) TestCheckFunc { - return func(s *terraform.State) error { + return checkIfIndexesIntoTypeSet(key, func(s *terraform.State) error { is, err := primaryInstanceState(s, name) if err != nil { return err } return testCheckNoResourceAttr(is, name, key) - } + }) } // TestCheckModuleNoResourceAttr - as per TestCheckNoResourceAttr but with // support for non-root modules func TestCheckModuleNoResourceAttr(mp []string, name string, key string) TestCheckFunc { mpt := addrs.Module(mp).UnkeyedInstanceShim() - return func(s *terraform.State) error { + return checkIfIndexesIntoTypeSet(key, func(s *terraform.State) error { is, err := modulePathPrimaryInstanceState(s, mpt, name) if err != nil { return err } return testCheckNoResourceAttr(is, name, key) - } + }) } func testCheckNoResourceAttr(is *terraform.InstanceState, name string, key string) error { @@ -1139,28 +804,28 @@ func testCheckNoResourceAttr(is *terraform.InstanceState, name string, key strin // TestMatchResourceAttr is a TestCheckFunc which checks that the value // in state for the given name/key combination matches the given regex. func TestMatchResourceAttr(name, key string, r *regexp.Regexp) TestCheckFunc { - return func(s *terraform.State) error { + return checkIfIndexesIntoTypeSet(key, func(s *terraform.State) error { is, err := primaryInstanceState(s, name) if err != nil { return err } return testMatchResourceAttr(is, name, key, r) - } + }) } // TestModuleMatchResourceAttr - as per TestMatchResourceAttr but with // support for non-root modules func TestModuleMatchResourceAttr(mp []string, name string, key string, r *regexp.Regexp) TestCheckFunc { mpt := addrs.Module(mp).UnkeyedInstanceShim() - return func(s *terraform.State) error { + return checkIfIndexesIntoTypeSet(key, func(s *terraform.State) error { is, err := modulePathPrimaryInstanceState(s, mpt, name) if err != nil { return err } return testMatchResourceAttr(is, name, key, r) - } + }) } func testMatchResourceAttr(is *terraform.InstanceState, name string, key string, r *regexp.Regexp) error { @@ -1196,7 +861,7 @@ func TestCheckModuleResourceAttrPtr(mp []string, name string, key string, value // TestCheckResourceAttrPair is a TestCheckFunc which validates that the values // in state for a pair of name/key combinations are equal. func TestCheckResourceAttrPair(nameFirst, keyFirst, nameSecond, keySecond string) TestCheckFunc { - return func(s *terraform.State) error { + return checkIfIndexesIntoTypeSetPair(keyFirst, keySecond, func(s *terraform.State) error { isFirst, err := primaryInstanceState(s, nameFirst) if err != nil { return err @@ -1208,7 +873,7 @@ func TestCheckResourceAttrPair(nameFirst, keyFirst, nameSecond, keySecond string } return testCheckResourceAttrPair(isFirst, nameFirst, keyFirst, isSecond, nameSecond, keySecond) - } + }) } // TestCheckModuleResourceAttrPair - as per TestCheckResourceAttrPair but with @@ -1216,7 +881,7 @@ func TestCheckResourceAttrPair(nameFirst, keyFirst, nameSecond, keySecond string func TestCheckModuleResourceAttrPair(mpFirst []string, nameFirst string, keyFirst string, mpSecond []string, nameSecond string, keySecond string) TestCheckFunc { mptFirst := addrs.Module(mpFirst).UnkeyedInstanceShim() mptSecond := addrs.Module(mpSecond).UnkeyedInstanceShim() - return func(s *terraform.State) error { + return checkIfIndexesIntoTypeSetPair(keyFirst, keySecond, func(s *terraform.State) error { isFirst, err := modulePathPrimaryInstanceState(s, mptFirst, nameFirst) if err != nil { return err @@ -1228,10 +893,18 @@ func TestCheckModuleResourceAttrPair(mpFirst []string, nameFirst string, keyFirs } return testCheckResourceAttrPair(isFirst, nameFirst, keyFirst, isSecond, nameSecond, keySecond) - } + }) } func testCheckResourceAttrPair(isFirst *terraform.InstanceState, nameFirst string, keyFirst string, isSecond *terraform.InstanceState, nameSecond string, keySecond string) error { + if nameFirst == nameSecond && keyFirst == keySecond { + return fmt.Errorf( + "comparing self: resource %s attribute %s", + nameFirst, + keyFirst, + ) + } + vFirst, okFirst := isFirst.Attributes[keyFirst] vSecond, okSecond := isSecond.Attributes[keySecond] @@ -1313,20 +986,6 @@ func TestMatchOutput(name string, r *regexp.Regexp) TestCheckFunc { } } -// TestT is the interface used to handle the test lifecycle of a test. -// -// Users should just use a *testing.T object, which implements this. -type TestT interface { - Error(args ...interface{}) - Fatal(args ...interface{}) - Skip(args ...interface{}) - Name() string - Parallel() -} - -// This is set to true by unit tests to alter some behavior -var testTesting = false - // modulePrimaryInstanceState returns the instance state for the given resource // name in a ModuleState func modulePrimaryInstanceState(s *terraform.State, ms *terraform.ModuleState, name string) (*terraform.InstanceState, error) { @@ -1361,46 +1020,34 @@ func primaryInstanceState(s *terraform.State, name string) (*terraform.InstanceS return modulePrimaryInstanceState(s, ms, name) } -// operationError is a specialized implementation of error used to describe -// failures during one of the several operations performed for a particular -// test case. -type operationError struct { - OpName string - Diags tfdiags.Diagnostics -} - -func newOperationError(opName string, diags tfdiags.Diagnostics) error { - return operationError{opName, diags} -} - -// Error returns a terse error string containing just the basic diagnostic -// messages, for situations where normal Go error behavior is appropriate. -func (err operationError) Error() string { - return fmt.Sprintf("errors during %s: %s", err.OpName, err.Diags.Err().Error()) +// indexesIntoTypeSet is a heuristic to try and identify if a flatmap style +// string address uses a precalculated TypeSet hash, which are integers and +// typically are large and obviously not a list index +func indexesIntoTypeSet(key string) bool { + for _, part := range strings.Split(key, ".") { + if i, err := strconv.Atoi(part); err == nil && i > 100 { + return true + } + } + return false } -// ErrorDetail is like Error except it includes verbosely-rendered diagnostics -// similar to what would come from a normal Terraform run, which include -// additional context not included in Error(). -func (err operationError) ErrorDetail() string { - var buf bytes.Buffer - fmt.Fprintf(&buf, "errors during %s:", err.OpName) - clr := &colorstring.Colorize{Disable: true, Colors: colorstring.DefaultColors} - for _, diag := range err.Diags { - diagStr := format.Diagnostic(diag, nil, clr, 78) - buf.WriteByte('\n') - buf.WriteString(diagStr) +func checkIfIndexesIntoTypeSet(key string, f TestCheckFunc) TestCheckFunc { + return func(s *terraform.State) error { + err := f(s) + if err != nil && s.IsBinaryDrivenTest && indexesIntoTypeSet(key) { + return fmt.Errorf("Error in test check: %s\nTest check address %q likely indexes into TypeSet\nThis is currently not possible in the SDK", err, key) + } + return err } - return buf.String() } -// detailedErrorMessage is a helper for calling ErrorDetail on an error if -// it is an operationError or just taking Error otherwise. -func detailedErrorMessage(err error) string { - switch tErr := err.(type) { - case operationError: - return tErr.ErrorDetail() - default: - return err.Error() +func checkIfIndexesIntoTypeSetPair(keyFirst, keySecond string, f TestCheckFunc) TestCheckFunc { + return func(s *terraform.State) error { + err := f(s) + if err != nil && s.IsBinaryDrivenTest && (indexesIntoTypeSet(keyFirst) || indexesIntoTypeSet(keySecond)) { + return fmt.Errorf("Error in test check: %s\nTest check address %q or %q likely indexes into TypeSet\nThis is currently not possible in the SDK", err, keyFirst, keySecond) + } + return err } } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_config.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_config.go index e21525de..1e39294f 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_config.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_config.go @@ -1,392 +1,13 @@ package resource import ( - "bufio" - "bytes" "errors" "fmt" "log" - "sort" - "strings" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - - "github.com/hashicorp/errwrap" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) -// 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 = terraform.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) - } - - // Refresh! - newState, stepDiags := ctx.Refresh() - // 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("refresh", 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! - if p, stepDiags := ctx.Plan(); stepDiags.HasErrors() { - return state, newOperationError("plan", stepDiags) - } else { - 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. - var p *plans.Plan - if p, stepDiags = ctx.Plan(); stepDiags.HasErrors() { - return state, newOperationError("follow-up plan", stepDiags) - } - if !p.Changes.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)) - } - } - - // And another after a Refresh. - if !step.Destroy || (step.Destroy && !step.PreventPostDestroyRefresh) { - newState, stepDiags = ctx.Refresh() - if stepDiags.HasErrors() { - return state, newOperationError("follow-up refresh", stepDiags) - } - - state, err = shimNewState(newState, step.providers) - if err != nil { - return nil, err - } - } - if p, stepDiags = ctx.Plan(); stepDiags.HasErrors() { - return state, newOperationError("second follow-up refresh", stepDiags) - } - empty := p.Changes.Empty() - - // Data resources are tricky because they legitimately get instantiated - // during refresh so that they will be already populated during the - // plan walk. Because of this, if we have any data resources in the - // config we'll end up wanting to destroy them again here. This is - // acceptable and expected, and we'll treat it as "empty" for the - // sake of this testing. - if step.Destroy && !empty { - empty = true - for _, change := range p.Changes.Resources { - if change.Addr.Resource.Resource.Mode != addrs.DataResourceMode { - 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 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 = "" - } - // NOTE: we don't support 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() diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_import_state.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_import_state.go deleted file mode 100644 index 561873de..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_import_state.go +++ /dev/null @@ -1,233 +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-plugin-sdk/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/terraform" -) - -// testStepImportState runs an imort 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{ - // Set the module so that any provider config is loaded - Config: cfg, - - 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() { - providerType := providerAddr.ProviderConfig.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 -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_new.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_new.go index c393d9c2..c9d90dd2 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_new.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_new.go @@ -5,53 +5,90 @@ import ( "log" "reflect" "strings" - "testing" "github.com/davecgh/go-spew/spew" tfjson "github.com/hashicorp/terraform-json" - "github.com/hashicorp/terraform-plugin-sdk/acctest" - "github.com/hashicorp/terraform-plugin-sdk/terraform" - tftest "github.com/hashicorp/terraform-plugin-test" + testing "github.com/mitchellh/go-testing-interface" + + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugintest" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) -func getState(t *testing.T, wd *tftest.WorkingDir) *terraform.State { - jsonState := wd.RequireState(t) - state, err := shimStateFromJson(jsonState) +func runPostTestDestroy(t testing.T, c TestCase, wd *plugintest.WorkingDir, factories map[string]func() (*schema.Provider, error), v5factories map[string]func() (tfprotov5.ProviderServer, error), statePreDestroy *terraform.State) error { + t.Helper() + + err := runProviderCommand(t, func() error { + return wd.Destroy() + }, wd, factories, v5factories) if err != nil { - t.Fatal(err) + return err } - return state + + if c.CheckDestroy != nil { + if err := c.CheckDestroy(statePreDestroy); err != nil { + return err + } + } + + return nil } -func RunNewTest(t *testing.T, c TestCase, providers map[string]terraform.ResourceProvider) { +func runNewTest(t testing.T, c TestCase, helper *plugintest.Helper) { + t.Helper() + spewConf := spew.NewDefaultConfig() spewConf.SortKeys = true - wd := acctest.TestHelper.RequireNewWorkingDir(t) + wd := helper.RequireNewWorkingDir(t) defer func() { - wd.RequireDestroy(t) - - if c.CheckDestroy != nil { - statePostDestroy := getState(t, wd) + var statePreDestroy *terraform.State + var err error + err = runProviderCommand(t, func() error { + statePreDestroy, err = getState(t, wd) + if err != nil { + return err + } + return nil + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + t.Fatalf("Error retrieving state, there may be dangling resources: %s", err.Error()) + return + } - if err := c.CheckDestroy(statePostDestroy); err != nil { - t.Fatal(err) + if !stateIsEmpty(statePreDestroy) { + err := runPostTestDestroy(t, c, wd, c.ProviderFactories, c.ProtoV5ProviderFactories, statePreDestroy) + if err != nil { + t.Fatalf("Error running post-test destroy, there may be dangling resources: %s", err.Error()) } } + wd.Close() }() - providerCfg := testProviderConfig(c) + providerCfg, err := testProviderConfig(c) + if err != nil { + t.Fatal(err) + } - wd.RequireSetConfig(t, providerCfg) - wd.RequireInit(t) + err = wd.SetConfig(providerCfg) + if err != nil { + t.Fatalf("Error setting test config: %s", err) + } + err = runProviderCommand(t, func() error { + return wd.Init() + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + t.Fatalf("Error running init: %s", err.Error()) + return + } // use this to track last step succesfully applied // acts as default for import tests var appliedCfg string for i, step := range c.Steps { - if step.PreConfig != nil { step.PreConfig() } @@ -62,16 +99,27 @@ func RunNewTest(t *testing.T, c TestCase, providers map[string]terraform.Resourc t.Fatal(err) } if skip { - log.Printf("[WARN] Skipping step %d", i) + log.Printf("[WARN] Skipping step %d/%d", i+1, len(c.Steps)) continue } } if step.ImportState { - step.providers = providers - err := testStepNewImportState(t, c, wd, step, appliedCfg) - if err != nil { - t.Fatal(err) + err := testStepNewImportState(t, c, helper, wd, step, appliedCfg) + if step.ExpectError != nil { + if err == nil { + t.Fatalf("Step %d/%d error running import: expected an error but got none", i+1, len(c.Steps)) + } + if !step.ExpectError.MatchString(err.Error()) { + t.Fatalf("Step %d/%d error running import, expected an error with pattern (%s), no match on: %s", i+1, len(c.Steps), step.ExpectError.String(), err) + } + } else { + if err != nil && c.ErrorCheck != nil { + err = c.ErrorCheck(err) + } + if err != nil { + t.Fatalf("Step %d/%d error running import: %s", i+1, len(c.Steps), err) + } } continue } @@ -80,14 +128,17 @@ func RunNewTest(t *testing.T, c TestCase, providers map[string]terraform.Resourc err := testStepNewConfig(t, c, wd, step) if step.ExpectError != nil { if err == nil { - t.Fatal("Expected an error but got none") + t.Fatalf("Step %d/%d, expected an error but got none", i+1, len(c.Steps)) } if !step.ExpectError.MatchString(err.Error()) { - t.Fatalf("Expected an error with pattern, no match on: %s", err) + t.Fatalf("Step %d/%d, expected an error with pattern, no match on: %s", i+1, len(c.Steps), err) } } else { + if err != nil && c.ErrorCheck != nil { + err = c.ErrorCheck(err) + } if err != nil { - t.Fatal(err) + t.Fatalf("Step %d/%d error: %s", i+1, len(c.Steps), err) } } appliedCfg = step.Config @@ -98,14 +149,26 @@ func RunNewTest(t *testing.T, c TestCase, providers map[string]terraform.Resourc } } +func getState(t testing.T, wd *plugintest.WorkingDir) (*terraform.State, error) { + t.Helper() + + jsonState, err := wd.State() + if err != nil { + return nil, err + } + state, err := shimStateFromJson(jsonState) + if err != nil { + t.Fatal(err) + } + return state, nil +} + +func stateIsEmpty(state *terraform.State) bool { + return state.Empty() || !state.HasResources() +} + func planIsEmpty(plan *tfjson.Plan) bool { for _, rc := range plan.ResourceChanges { - if rc.Mode == tfjson.DataResourceMode { - // Skip data sources as the current implementation ignores - // existing state and they are all re-read every time - continue - } - for _, a := range rc.Change.Actions { if a != tfjson.ActionNoop { return false @@ -114,7 +177,10 @@ func planIsEmpty(plan *tfjson.Plan) bool { } return true } -func testIDRefresh(c TestCase, t *testing.T, wd *tftest.WorkingDir, step TestStep, r *terraform.ResourceState) error { + +func testIDRefresh(c TestCase, t testing.T, wd *plugintest.WorkingDir, step TestStep, r *terraform.ResourceState) error { + t.Helper() + spewConf := spew.NewDefaultConfig() spewConf.SortKeys = true @@ -126,13 +192,36 @@ func testIDRefresh(c TestCase, t *testing.T, wd *tftest.WorkingDir, step TestSte // Temporarily set the config to a minimal provider config for the refresh // test. After the refresh we can reset it. - cfg := testProviderConfig(c) - wd.RequireSetConfig(t, cfg) - defer wd.RequireSetConfig(t, step.Config) + cfg, err := testProviderConfig(c) + if err != nil { + return err + } + err = wd.SetConfig(cfg) + if err != nil { + t.Fatalf("Error setting import test config: %s", err) + } + defer func() { + err = wd.SetConfig(step.Config) + if err != nil { + t.Fatalf("Error resetting test config: %s", err) + } + }() // Refresh! - wd.RequireRefresh(t) - state = getState(t, wd) + err = runProviderCommand(t, func() error { + err = wd.Refresh() + if err != nil { + t.Fatalf("Error running terraform refresh: %s", err) + } + state, err = getState(t, wd) + if err != nil { + return err + } + return nil + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + return err + } // Verify attribute equivalence. actualR := state.RootModule().Resources[c.IDRefreshName] @@ -146,12 +235,12 @@ func testIDRefresh(c TestCase, t *testing.T, wd *tftest.WorkingDir, step TestSte expected := r.Primary.Attributes // Remove fields we're ignoring for _, v := range c.IDRefreshIgnore { - for k, _ := range actual { + for k := range actual { if strings.HasPrefix(k, v) { delete(actual, k) } } - for k, _ := range expected { + for k := range expected { if strings.HasPrefix(k, v) { delete(expected, k) } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_new_config.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_new_config.go index d2fbf29d..0e218f60 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_new_config.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_new_config.go @@ -1,39 +1,119 @@ package resource import ( - "testing" + "errors" + "fmt" - "github.com/davecgh/go-spew/spew" - "github.com/hashicorp/terraform-plugin-sdk/terraform" - tftest "github.com/hashicorp/terraform-plugin-test" + tfjson "github.com/hashicorp/terraform-json" + testing "github.com/mitchellh/go-testing-interface" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugintest" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) -func testStepNewConfig(t *testing.T, c TestCase, wd *tftest.WorkingDir, step TestStep) error { - spewConf := spew.NewDefaultConfig() - spewConf.SortKeys = true +func testStepNewConfig(t testing.T, c TestCase, wd *plugintest.WorkingDir, step TestStep) error { + t.Helper() var idRefreshCheck *terraform.ResourceState idRefresh := c.IDRefreshName != "" if !step.Destroy { - state := getState(t, wd) + var state *terraform.State + var err error + err = runProviderCommand(t, func() error { + state, err = getState(t, wd) + if err != nil { + return err + } + return nil + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + return err + } if err := testStepTaint(state, step); err != nil { - t.Fatalf("Error when tainting resources: %s", err) + return fmt.Errorf("Error when tainting resources: %s", err) } } - wd.RequireSetConfig(t, step.Config) + err := wd.SetConfig(step.Config) + if err != nil { + return fmt.Errorf("Error setting config: %w", err) + } + // require a refresh before applying + // failing to do this will result in data sources not being updated + err = runProviderCommand(t, func() error { + return wd.Refresh() + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + return fmt.Errorf("Error running pre-apply refresh: %w", err) + } + + // If this step is a PlanOnly step, skip over this first Plan and + // subsequent Apply, and use the follow-up Plan that checks for + // permadiffs if !step.PlanOnly { - err := wd.Apply() + // Plan! + err := runProviderCommand(t, func() error { + if step.Destroy { + return wd.CreateDestroyPlan() + } + return wd.CreatePlan() + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) if err != nil { - return err + return fmt.Errorf("Error running pre-apply plan: %w", err) + } + + // We need to keep a copy of the state prior to destroying such + // that the destroy steps can verify their behavior in the + // check function + var stateBeforeApplication *terraform.State + err = runProviderCommand(t, func() error { + stateBeforeApplication, err = getState(t, wd) + if err != nil { + return err + } + return nil + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + return fmt.Errorf("Error retrieving pre-apply state: %w", err) + } + + // Apply the diff, creating real resources + err = runProviderCommand(t, func() error { + return wd.Apply() + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + if step.Destroy { + return fmt.Errorf("Error running destroy: %w", err) + } + return fmt.Errorf("Error running apply: %w", err) + } + + // Get the new state + var state *terraform.State + err = runProviderCommand(t, func() error { + state, err = getState(t, wd) + if err != nil { + return err + } + return nil + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + return fmt.Errorf("Error retrieving state after apply: %w", err) } - state := getState(t, wd) + // Run any configured checks if step.Check != nil { - if err := step.Check(state); err != nil { - t.Fatal(err) + state.IsBinaryDrivenTest = true + if step.Destroy { + if err := step.Check(stateBeforeApplication); err != nil { + return fmt.Errorf("Check failed: %w", err) + } + } else { + if err := step.Check(state); err != nil { + return fmt.Errorf("Check failed: %w", err) + } } } } @@ -41,41 +121,99 @@ func testStepNewConfig(t *testing.T, c TestCase, wd *tftest.WorkingDir, step Tes // Test for perpetual diffs by performing a plan, a refresh, and another plan // do a plan - wd.RequireCreatePlan(t) - plan := wd.RequireSavedPlan(t) + err = runProviderCommand(t, func() error { + if step.Destroy { + return wd.CreateDestroyPlan() + } + return wd.CreatePlan() + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + return fmt.Errorf("Error running post-apply plan: %w", err) + } - if !planIsEmpty(plan) { - if step.ExpectNonEmptyPlan { - t.Log("[INFO] Got non-empty plan, as expected") - } else { + var plan *tfjson.Plan + err = runProviderCommand(t, func() error { + var err error + plan, err = wd.SavedPlan() + return err + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + return fmt.Errorf("Error retrieving post-apply plan: %w", err) + } - t.Fatalf("After applying this test step, the plan was not empty. %s", spewConf.Sdump(plan)) + if !planIsEmpty(plan) && !step.ExpectNonEmptyPlan { + var stdout string + err = runProviderCommand(t, func() error { + var err error + stdout, err = wd.SavedPlanRawStdout() + return err + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + return fmt.Errorf("Error retrieving formatted plan output: %w", err) } + return fmt.Errorf("After applying this test step, the plan was not empty.\nstdout:\n\n%s", stdout) } // do a refresh - if !c.PreventPostDestroyRefresh { - wd.RequireRefresh(t) + if !step.Destroy || (step.Destroy && !step.PreventPostDestroyRefresh) { + err := runProviderCommand(t, func() error { + return wd.Refresh() + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + return fmt.Errorf("Error running post-apply refresh: %w", err) + } } // do another plan - wd.RequireCreatePlan(t) - plan = wd.RequireSavedPlan(t) + err = runProviderCommand(t, func() error { + if step.Destroy { + return wd.CreateDestroyPlan() + } + return wd.CreatePlan() + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + return fmt.Errorf("Error running second post-apply plan: %w", err) + } - // check if plan is empty - if !planIsEmpty(plan) { - if step.ExpectNonEmptyPlan { - t.Log("[INFO] Got non-empty plan, as expected") - } else { + err = runProviderCommand(t, func() error { + var err error + plan, err = wd.SavedPlan() + return err + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + return fmt.Errorf("Error retrieving second post-apply plan: %w", err) + } - t.Fatalf("After applying this test step and performing a `terraform refresh`, the plan was not empty. %s", spewConf.Sdump(plan)) + // check if plan is empty + if !planIsEmpty(plan) && !step.ExpectNonEmptyPlan { + var stdout string + err = runProviderCommand(t, func() error { + var err error + stdout, err = wd.SavedPlanRawStdout() + return err + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + return fmt.Errorf("Error retrieving formatted second plan output: %w", err) } + return fmt.Errorf("After applying this test step and performing a `terraform refresh`, the plan was not empty.\nstdout\n\n%s", stdout) + } else if step.ExpectNonEmptyPlan && planIsEmpty(plan) { + return errors.New("Expected a non-empty plan, but got an empty plan") } // ID-ONLY REFRESH // If we've never checked an id-only refresh and our state isn't // empty, find the first resource and test it. - state := getState(t, wd) + var state *terraform.State + err = runProviderCommand(t, func() error { + state, err = getState(t, wd) + if err != nil { + return err + } + return nil + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + return err + } if idRefresh && idRefreshCheck == nil && !state.Empty() { // Find the first non-nil resource in the state for _, m := range state.Modules { @@ -96,7 +234,7 @@ func testStepNewConfig(t *testing.T, c TestCase, wd *tftest.WorkingDir, step Tes // caught a different bug. if idRefreshCheck != nil { if err := testIDRefresh(c, t, wd, step, idRefreshCheck); err != nil { - t.Fatalf( + return fmt.Errorf( "[ERROR] Test: ID-only test failed: %s", err) } } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_new_import_state.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_new_import_state.go index ec2f9916..52487ad8 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_new_import_state.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_new_import_state.go @@ -3,17 +3,17 @@ package resource import ( "reflect" "strings" - "testing" "github.com/davecgh/go-spew/spew" - "github.com/hashicorp/terraform-plugin-sdk/acctest" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/terraform" - tftest "github.com/hashicorp/terraform-plugin-test" + testing "github.com/mitchellh/go-testing-interface" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugintest" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) -func testStepNewImportState(t *testing.T, c TestCase, wd *tftest.WorkingDir, step TestStep, cfg string) error { +func testStepNewImportState(t testing.T, c TestCase, helper *plugintest.Helper, wd *plugintest.WorkingDir, step TestStep, cfg string) error { + t.Helper() + spewConf := spew.NewDefaultConfig() spewConf.SortKeys = true @@ -22,7 +22,18 @@ func testStepNewImportState(t *testing.T, c TestCase, wd *tftest.WorkingDir, ste } // get state from check sequence - state := getState(t, wd) + var state *terraform.State + var err error + err = runProviderCommand(t, func() error { + state, err = getState(t, wd) + if err != nil { + return err + } + return nil + }, wd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + t.Fatalf("Error getting state: %s", err) + } // Determine the ID to import var importId string @@ -51,12 +62,38 @@ func testStepNewImportState(t *testing.T, c TestCase, wd *tftest.WorkingDir, ste t.Fatal("Cannot import state with no specified config") } } - importWd := acctest.TestHelper.RequireNewWorkingDir(t) + importWd := helper.RequireNewWorkingDir(t) defer importWd.Close() - importWd.RequireSetConfig(t, step.Config) - importWd.RequireInit(t) - importWd.RequireImport(t, step.ResourceName, importId) - importState := getState(t, wd) + err = importWd.SetConfig(step.Config) + if err != nil { + t.Fatalf("Error setting test config: %s", err) + } + + err = runProviderCommand(t, func() error { + return importWd.Init() + }, importWd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + t.Fatalf("Error running init: %s", err) + } + + err = runProviderCommand(t, func() error { + return importWd.Import(step.ResourceName, importId) + }, importWd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + return err + } + + var importState *terraform.State + err = runProviderCommand(t, func() error { + importState, err = getState(t, importWd) + if err != nil { + return err + } + return nil + }, importWd, c.ProviderFactories, c.ProtoV5ProviderFactories) + if err != nil { + t.Fatalf("Error getting state: %s", err) + } // Go through the imported state and verify if step.ImportStateCheck != nil { @@ -81,8 +118,16 @@ func testStepNewImportState(t *testing.T, c TestCase, wd *tftest.WorkingDir, ste 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 { + for r2Key, r2 := range old { + // Ensure that we do not match against data sources as they + // cannot be imported and are not what we want to verify. + // Mode is not present in ResourceState so we use the + // stringified ResourceStateKey for comparison. + if strings.HasPrefix(r2Key, "data.") { + continue + } + + if r2.Primary != nil && r2.Primary.ID == r.Primary.ID && r2.Type == r.Type && r2.Provider == r.Provider { oldR = r2 break } @@ -93,25 +138,6 @@ func testStepNewImportState(t *testing.T, c TestCase, wd *tftest.WorkingDir, ste 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. - // KEM CHANGE FROM OLD FRAMEWORK: Fail test if this happens. - var rsrcSchema *schema.Resource - providerAddr, diags := addrs.ParseAbsProviderConfigStr("provider." + r.Provider + "." + r.Type) - if diags.HasErrors() { - t.Fatalf("Failed to find schema for resource with ID %s", r.Primary) - } - - providerType := providerAddr.ProviderConfig.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 { @@ -154,24 +180,23 @@ func testStepNewImportState(t *testing.T, c TestCase, wd *tftest.WorkingDir, ste } } - // 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 - } - } + // timeouts are only _sometimes_ added to state. To + // account for this, just don't compare timeouts at + // all. + for k := range actual { + if strings.HasPrefix(k, "timeouts.") { + delete(actual, k) } - for k := range expected { - for _, schema := range rsrcSchema.SchemasForFlatmapPath(k) { - if schema.Removed != "" { - delete(expected, k) - break - } - } + if k == "timeouts" { + delete(actual, k) + } + } + for k := range expected { + if strings.HasPrefix(k, "timeouts.") { + delete(expected, k) + } + if k == "timeouts" { + delete(expected, k) } } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_new_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_new_test.go new file mode 100644 index 00000000..cd574493 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_new_test.go @@ -0,0 +1,1120 @@ +package resource + +import ( + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/go-test/deep" + tfjson "github.com/hashicorp/terraform-json" + + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestShimState(t *testing.T) { + type expectedError struct { + Prefix string + } + + testCases := []struct { + Name string + RawState string + ExpectedState *terraform.State + ExpectedErr *expectedError + }{ + { + "empty", + `{ + "format_version": "0.1" +}`, + &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{}, + Dependencies: []string{}, + }, + }, + }, + nil, + }, + { + "simple outputs", + `{ + "format_version": "0.1", + "terraform_version": "0.12.18", + "values": { + "outputs": { + "bool": { + "sensitive": false, + "value": true + }, + "int": { + "sensitive": false, + "value": 42 + }, + "float": { + "sensitive": false, + "value": 1.4 + }, + "string": { + "sensitive": false, + "value": "test" + } + }, + "root_module": {} + } +}`, + &terraform.State{ + Version: 3, + TFVersion: "0.12.18", + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{ + "bool": { + Type: "string", + Value: "true", + Sensitive: false, + }, + "int": { + Type: "string", + Value: "42", + Sensitive: false, + }, + "float": { + Type: "string", + Value: "1.4", + Sensitive: false, + }, + "string": { + Type: "string", + Value: "test", + Sensitive: false, + }, + }, + Resources: map[string]*terraform.ResourceState{}, + Dependencies: []string{}, + }, + }, + }, + nil, + }, + { + "complex outputs", + `{ + "format_version": "0.1", + "terraform_version": "0.12.18", + "values": { + "outputs": { + "empty_list": { + "sensitive": false, + "value": [] + }, + "list_of_strings": { + "sensitive": false, + "value": ["first", "second", "third"] + }, + "map_of_strings": { + "sensitive": false, + "value": { + "hello": "world", + "foo": "bar" + } + }, + "list_of_int": { + "sensitive": false, + "value": [1, 4, 9] + }, + "list_of_float": { + "sensitive": false, + "value": [1.2, 4.2, 9.8] + }, + "list_of_bool": { + "sensitive": false, + "value": [true, false, true] + } + }, + "root_module": {} + } +}`, + &terraform.State{ + Version: 3, + TFVersion: "0.12.18", + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{ + "empty_list": { + Type: "list", + Value: []interface{}{}, + Sensitive: false, + }, + "list_of_strings": { + Type: "list", + Value: []interface{}{ + "first", "second", "third", + }, + Sensitive: false, + }, + "map_of_strings": { + Type: "map", + Value: map[string]interface{}{ + "hello": "world", + "foo": "bar", + }, + Sensitive: false, + }, + "list_of_int": { + Type: "list", + Value: []interface{}{ + json.Number("1"), + json.Number("4"), + json.Number("9"), + }, + Sensitive: false, + }, + "list_of_float": { + Type: "list", + Value: []interface{}{ + json.Number("1.2"), + json.Number("4.2"), + json.Number("9.8"), + }, + Sensitive: false, + }, + "list_of_bool": { + Type: "list", + Value: []interface{}{ + true, false, true, + }, + Sensitive: false, + }, + }, + Resources: map[string]*terraform.ResourceState{}, + Dependencies: []string{}, + }, + }, + }, + nil, + }, + { + "output with slice of slices", + `{ + "format_version": "0.1", + "terraform_version": "0.12.18", + "values": { + "outputs": { + "list_of_lists": { + "sensitive": false, + "value": [ + ["one", "two"], + ["blue", "green", "red"] + ] + } + }, + "root_module": {} + } +}`, + &terraform.State{ + Version: 3, + TFVersion: "0.12.18", + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{ + "list_of_lists": { + Type: "list", + Value: []interface{}{ + []interface{}{"one", "two"}, + []interface{}{"blue", "green", "red"}, + }, + Sensitive: false, + }, + }, + Resources: map[string]*terraform.ResourceState{}, + Dependencies: []string{}, + }, + }, + }, + nil, + }, + { + "output with slice of maps", + `{ + "format_version": "0.1", + "terraform_version": "0.12.18", + "values": { + "outputs": { + "list_of_maps": { + "sensitive": false, + "value": [ + { + "rule": "allow", + "port": 443, + "allow_bool": true + }, + { + "rule": "deny", + "port": 80, + "allow_bool": false + } + ] + } + }, + "root_module": {} + } +}`, + &terraform.State{ + Version: 3, + TFVersion: "0.12.18", + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{ + "list_of_maps": { + Type: "list", + Value: []interface{}{ + map[string]interface{}{ + "allow_bool": true, + "port": json.Number("443"), + "rule": "allow", + }, + map[string]interface{}{ + "allow_bool": false, + "port": json.Number("80"), + "rule": "deny", + }, + }, + Sensitive: false, + }, + }, + Resources: map[string]*terraform.ResourceState{}, + Dependencies: []string{}, + }, + }, + }, + nil, + }, + { + "output with nested map", + `{ + "format_version": "0.1", + "terraform_version": "0.12.18", + "values": { + "outputs": { + "map_of_maps": { + "sensitive": false, + "value": { + "hello": { + "whole": "world" + }, + "foo": "bar" + } + } + }, + "root_module": {} + } +}`, + &terraform.State{ + Version: 3, + TFVersion: "0.12.18", + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{ + "map_of_maps": { + Type: "map", + Value: map[string]interface{}{ + "hello": map[string]interface{}{ + "whole": "world", + }, + "foo": "bar", + }, + Sensitive: false, + }, + }, + Resources: map[string]*terraform.ResourceState{}, + Dependencies: []string{}, + }, + }, + }, + nil, + }, + { + "invalid address", + `{ + "format_version": "0.1", + "values": { + "root_module": { + "address": "blah" + } + } +}`, + nil, + &expectedError{Prefix: "Invalid module instance address"}, + }, + { + "resource with primitive attributes", + `{ + "format_version": "0.1", + "terraform_version": "0.12.18", + "values": { + "outputs": {}, + "root_module": { + "resources": [ + { + "address": "cloud_vpc.main", + "mode": "managed", + "type": "cloud_vpc", + "name": "main", + "provider_name": "cloud", + "schema_version": 1, + "values": { + "id": "123999", + "string_field": "hello world", + "bool_field_1": false, + "bool_field_2": true, + "null_field": null, + "empty_string": "", + "number_field": 42 + } + } + ] + } + } +}`, + &terraform.State{ + Version: 3, + TFVersion: "0.12.18", + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "cloud_vpc.main": { + Type: "cloud_vpc", + Provider: "cloud", + Primary: &terraform.InstanceState{ + ID: "123999", + Meta: map[string]interface{}{ + "schema_version": 1, + }, + Attributes: map[string]string{ + "%": "7", + "id": "123999", + + "bool_field_2": "true", + "string_field": "hello world", + "bool_field_1": "false", + "empty_string": "", + "number_field": "42", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + nil, + }, + { + "resource with nested slice", + `{ + "format_version": "0.1", + "terraform_version": "0.12.18", + "values": { + "outputs": {}, + "root_module": { + "resources": [ + { + "address": "cloud_vpc.main", + "mode": "managed", + "type": "cloud_vpc", + "name": "main", + "provider_name": "cloud", + "schema_version": 1, + "values": { + "id": "123999", + "list_of_lists": [ + ["one", "two", "three"], + ["red", "green", "blue"], + ["black", "white"] + ], + "list_of_maps": [ + { + "action": "allow", + "port": 443, + "allow_bool": true + }, + { + "action": "deny", + "port": 80, + "allow_bool": false + } + ] + } + } + ] + } + } +}`, + &terraform.State{ + Version: 3, + TFVersion: "0.12.18", + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "cloud_vpc.main": { + Type: "cloud_vpc", + Provider: "cloud", + Primary: &terraform.InstanceState{ + ID: "123999", + Meta: map[string]interface{}{ + "schema_version": 1, + }, + Attributes: map[string]string{ + "%": "3", + "id": "123999", + + "list_of_lists.#": "3", + "list_of_lists.0.#": "3", + "list_of_lists.0.0": "one", + "list_of_lists.0.1": "two", + "list_of_lists.0.2": "three", + "list_of_lists.1.#": "3", + "list_of_lists.1.0": "red", + "list_of_lists.1.1": "green", + "list_of_lists.1.2": "blue", + "list_of_lists.2.#": "2", + "list_of_lists.2.0": "black", + "list_of_lists.2.1": "white", + + "list_of_maps.#": "2", + "list_of_maps.0.%": "3", + "list_of_maps.0.action": "allow", + "list_of_maps.0.allow_bool": "true", + "list_of_maps.0.port": "443", + "list_of_maps.1.%": "3", + "list_of_maps.1.action": "deny", + "list_of_maps.1.allow_bool": "false", + "list_of_maps.1.port": "80", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + nil, + }, + { + "resource with nested map", + `{ + "format_version": "0.1", + "terraform_version": "0.12.18", + "values": { + "outputs": {}, + "root_module": { + "resources": [ + { + "address": "cloud_vpc.main", + "mode": "managed", + "type": "cloud_vpc", + "name": "main", + "provider_name": "cloud", + "schema_version": 1, + "values": { + "id": "123999", + "map_of_maps": { + "parent": { + "inner": "value" + }, + "second": { + "inner2": "value2" + } + }, + "map_of_lists": { + "parent": { + "inner": ["one", "two"] + }, + "second": { + "inner2": [1, 4, 9] + } + }, + "map_of_list_of_maps": { + "parent": [ + { + "action": "allow", + "port": 443, + "allow_bool": true + }, + { + "action": "deny", + "port": 80, + "allow_bool": false + } + ] + } + } + } + ] + } + } +}`, + &terraform.State{ + Version: 3, + TFVersion: "0.12.18", + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "cloud_vpc.main": { + Type: "cloud_vpc", + Provider: "cloud", + Primary: &terraform.InstanceState{ + ID: "123999", + Meta: map[string]interface{}{ + "schema_version": 1, + }, + Attributes: map[string]string{ + "%": "4", + "id": "123999", + + "map_of_maps.%": "2", + "map_of_maps.parent.%": "1", + "map_of_maps.parent.inner": "value", + "map_of_maps.second.%": "1", + "map_of_maps.second.inner2": "value2", + + "map_of_lists.%": "2", + "map_of_lists.parent.%": "1", + "map_of_lists.parent.inner.#": "2", + "map_of_lists.parent.inner.0": "one", + "map_of_lists.parent.inner.1": "two", + "map_of_lists.second.%": "1", + "map_of_lists.second.inner2.#": "3", + "map_of_lists.second.inner2.0": "1", + "map_of_lists.second.inner2.1": "4", + "map_of_lists.second.inner2.2": "9", + + "map_of_list_of_maps.%": "1", + "map_of_list_of_maps.parent.#": "2", + "map_of_list_of_maps.parent.0.%": "3", + "map_of_list_of_maps.parent.0.action": "allow", + "map_of_list_of_maps.parent.0.allow_bool": "true", + "map_of_list_of_maps.parent.0.port": "443", + "map_of_list_of_maps.parent.1.%": "3", + "map_of_list_of_maps.parent.1.action": "deny", + "map_of_list_of_maps.parent.1.allow_bool": "false", + "map_of_list_of_maps.parent.1.port": "80", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + nil, + }, + { + "data source", + `{ + "format_version": "0.1", + "terraform_version": "0.12.18", + "values": { + "outputs": {}, + "root_module": { + "resources": [ + { + "address": "data.cloud_vpc.main", + "mode": "data", + "type": "cloud_vpc", + "name": "main", + "provider_name": "cloud", + "schema_version": 0, + "values": { + "id": "123999", + "string_field": "hello world", + "bool_field_1": false, + "bool_field_2": true, + "null_field": null, + "empty_string": "", + "number_field": 42 + } + } + ] + } + } +}`, + &terraform.State{ + Version: 3, + TFVersion: "0.12.18", + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "data.cloud_vpc.main": { + Type: "cloud_vpc", + Provider: "cloud", + Primary: &terraform.InstanceState{ + ID: "123999", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "7", + "id": "123999", + + "bool_field_2": "true", + "string_field": "hello world", + "bool_field_1": "false", + "empty_string": "", + "number_field": "42", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + nil, + }, + { + "resource with complex attributes", + `{ + "format_version": "0.1", + "terraform_version": "0.12.18", + "values": { + "outputs": {}, + "root_module": { + "resources": [ + { + "address": "cloud_vpc.main", + "mode": "managed", + "type": "cloud_vpc", + "name": "main", + "provider_name": "cloud", + "schema_version": 1, + "values": { + "id": "123999", + "map_field": { + "key": "val", + "foo": "bar" + }, + "list_of_string": ["first", "second"], + "list_of_numbers": [1,2,3,4], + "list_of_bool": [true, false, true] + } + } + ] + } + } +}`, + &terraform.State{ + Version: 3, + TFVersion: "0.12.18", + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "cloud_vpc.main": { + Type: "cloud_vpc", + Provider: "cloud", + Primary: &terraform.InstanceState{ + ID: "123999", + Meta: map[string]interface{}{ + "schema_version": 1, + }, + Attributes: map[string]string{ + "%": "5", + "id": "123999", + + "map_field.%": "2", + "map_field.key": "val", + "map_field.foo": "bar", + + "list_of_string.#": "2", + "list_of_string.0": "first", + "list_of_string.1": "second", + + "list_of_numbers.#": "4", + "list_of_numbers.0": "1", + "list_of_numbers.1": "2", + "list_of_numbers.2": "3", + "list_of_numbers.3": "4", + + "list_of_bool.#": "3", + "list_of_bool.0": "true", + "list_of_bool.1": "false", + "list_of_bool.2": "true", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + nil, + }, + { + "indexed resource", + `{ + "format_version": "0.1", + "terraform_version": "0.12.18", + "values": { + "outputs": {}, + "root_module": { + "resources": [ + { + "address": "cloud_vpc.main", + "index": 0, + "mode": "managed", + "type": "cloud_vpc", + "name": "main", + "provider_name": "cloud", + "schema_version": 1, + "values": { + "id": "11111" + } + }, + { + "address": "cloud_vpc.main", + "index": 1, + "mode": "managed", + "type": "cloud_vpc", + "name": "main", + "provider_name": "cloud", + "schema_version": 1, + "values": { + "id": "22222" + } + } + ] + } + } +}`, + &terraform.State{ + Version: 3, + TFVersion: "0.12.18", + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "cloud_vpc.main.0": { + Type: "cloud_vpc", + Provider: "cloud", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 1, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + "cloud_vpc.main.1": { + Type: "cloud_vpc", + Provider: "cloud", + Primary: &terraform.InstanceState{ + ID: "22222", + Meta: map[string]interface{}{ + "schema_version": 1, + }, + Attributes: map[string]string{ + "%": "1", + "id": "22222", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + nil, + }, + { + "indexed data source", + `{ + "format_version": "0.1", + "terraform_version": "0.12.18", + "values": { + "outputs": {}, + "root_module": { + "resources": [ + { + "address": "data.cloud_vpc.main", + "index": 0, + "mode": "data", + "type": "cloud_vpc", + "name": "main", + "provider_name": "cloud", + "schema_version": 0, + "values": { + "id": "11111" + } + }, + { + "address": "data.cloud_vpc.main", + "index": 1, + "mode": "data", + "type": "cloud_vpc", + "name": "main", + "provider_name": "cloud", + "schema_version": 0, + "values": { + "id": "22222" + } + } + ] + } + } +}`, + &terraform.State{ + Version: 3, + TFVersion: "0.12.18", + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "data.cloud_vpc.main.0": { + Type: "cloud_vpc", + Provider: "cloud", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + "data.cloud_vpc.main.1": { + Type: "cloud_vpc", + Provider: "cloud", + Primary: &terraform.InstanceState{ + ID: "22222", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "22222", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + nil, + }, + { + "for_each", + `{ + "format_version": "0.1", + "terraform_version": "0.12.18", + "values": { + "outputs": {}, + "root_module": { + "resources": [ + { + "address": "cloud_vpc.main", + "index": "one", + "mode": "managed", + "type": "cloud_vpc", + "name": "main", + "provider_name": "cloud", + "schema_version": 1, + "values": { + "id": "11111" + } + }, + { + "address": "cloud_vpc.main", + "index": "two", + "mode": "managed", + "type": "cloud_vpc", + "name": "main", + "provider_name": "cloud", + "schema_version": 1, + "values": { + "id": "22222" + } + } + ] + } + } +}`, + nil, + &expectedError{Prefix: "unexpected index type (string)"}, + }, + { + "depends on", + `{ + "format_version": "0.1", + "terraform_version": "0.12.18", + "values": { + "outputs": {}, + "root_module": { + "resources": [ + { + "address": "cloud_vpc.primary", + "mode": "managed", + "type": "cloud_vpc", + "name": "primary", + "provider_name": "cloud", + "values": { + "id": "11111" + } + }, + { + "address": "cloud_vpc.secondary", + "mode": "managed", + "type": "cloud_vpc", + "name": "secondary", + "provider_name": "cloud", + "values": { + "id": "22222" + }, + "depends_on": [ + "cloud_vpc.primary" + ] + } + ] + } + } +}`, + &terraform.State{ + Version: 3, + TFVersion: "0.12.18", + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "cloud_vpc.primary": { + Type: "cloud_vpc", + Provider: "cloud", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + "cloud_vpc.secondary": { + Type: "cloud_vpc", + Provider: "cloud", + Primary: &terraform.InstanceState{ + ID: "22222", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "22222", + }, + }, + Dependencies: []string{ + "cloud_vpc.primary", + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + nil, + }, + { + "child modules", + `{ + "format_version": "0.1", + "terraform_version": "0.12.18", + "values": { + "root_module": { + "child_modules": [ + { + "resources": [ + { + "address": "cloud_vpc.primary", + "mode": "managed", + "type": "cloud_vpc", + "name": "primary", + "provider_name": "cloud", + "values": { + "id": "11111" + } + } + ] + } + ] + } + } +}`, + nil, + &expectedError{Prefix: "Modules are not supported."}, + }, + } + + for i, tc := range testCases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + var rawState tfjson.State + rawState.UseJSONNumber(true) + + err := unmarshalJSON([]byte(tc.RawState), &rawState) + if err != nil { + t.Fatal(err) + } + + shimmedState, err := shimStateFromJson(&rawState) + if tc.ExpectedErr != nil { + if err == nil { + t.Fatalf("Expected error with prefix: %q\nGot no error.", + tc.ExpectedErr.Prefix) + } + if strings.HasPrefix(err.Error(), tc.ExpectedErr.Prefix) { + return + } + t.Fatalf("Error mismatch.\nExpected prefix: %q\nGot: %q\n", + tc.ExpectedErr.Prefix, err.Error()) + } + if err != nil { + t.Fatal(err) + } + + // Lineage is randomly generated, so we wipe it to make comparing easier + shimmedState.Lineage = "" + + if diff := deep.Equal(tc.ExpectedState, shimmedState); diff != nil { + t.Fatalf("state mismatch:\n%s", strings.Join(diff, "\n")) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_sets.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_sets.go new file mode 100644 index 00000000..ada1d6e7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_sets.go @@ -0,0 +1,254 @@ +// These test helpers were developed by the AWS provider team at HashiCorp. + +package resource + +import ( + "fmt" + "regexp" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +const ( + sentinelIndex = "*" +) + +// TestCheckTypeSetElemNestedAttrs is a TestCheckFunc that accepts a resource +// name, an attribute path, which should use the sentinel value '*' for indexing +// into a TypeSet. The function verifies that an element matches the whole value +// map. +// +// You may check for unset keys, however this will also match keys set to empty +// string. Please provide a map with at least 1 non-empty value. +// +// map[string]string{ +// "key1": "value", +// "key2": "", +// } +// +// Use this function over SDK provided TestCheckFunctions when validating a +// TypeSet where its elements are a nested object with their own attrs/values. +// +// Please note, if the provided value map is not granular enough, there exists +// the possibility you match an element you were not intending to, in the TypeSet. +// Provide a full mapping of attributes to be sure the unique element exists. +func TestCheckTypeSetElemNestedAttrs(name, attr string, values map[string]string) TestCheckFunc { + return func(s *terraform.State) error { + is, err := primaryInstanceState(s, name) + if err != nil { + return err + } + + attrParts := strings.Split(attr, ".") + if attrParts[len(attrParts)-1] != sentinelIndex { + return fmt.Errorf("%q does not end with the special value %q", attr, sentinelIndex) + } + // account for cases where the user is trying to see if the value is unset/empty + // there may be ambiguous scenarios where a field was deliberately unset vs set + // to the empty string, this will match both, which may be a false positive. + var matchCount int + for _, v := range values { + if v != "" { + matchCount++ + } + } + if matchCount == 0 { + return fmt.Errorf("%#v has no non-empty values", values) + } + + if testCheckTypeSetElemNestedAttrsInState(is, attrParts, matchCount, values) { + return nil + } + return fmt.Errorf("%q no TypeSet element %q, with nested attrs %#v in state: %#v", name, attr, values, is.Attributes) + } +} + +// TestMatchTypeSetElemNestedAttrs is a TestCheckFunc similar to TestCheckTypeSetElemNestedAttrs +// with the exception that it verifies that an element matches a *regexp.Regexp. +// +// You may check for unset keys, however this will also match keys set to empty +// string. Please provide a map with at least 1 non-empty value e.g. +// +// map[string]*regexp.Regexp{ +// "key1": regexp.MustCompile("value"), +// "key2": regexp.MustCompile(""), +// } +// +func TestMatchTypeSetElemNestedAttrs(name, attr string, values map[string]*regexp.Regexp) TestCheckFunc { + return func(s *terraform.State) error { + is, err := primaryInstanceState(s, name) + if err != nil { + return err + } + + attrParts := strings.Split(attr, ".") + if attrParts[len(attrParts)-1] != sentinelIndex { + return fmt.Errorf("%q does not end with the special value %q", attr, sentinelIndex) + } + // account for cases where the user is trying to see if the value is unset/empty + // there may be ambiguous scenarios where a field was deliberately unset vs set + // to the empty string, this will match both, which may be a false positive. + var matchCount int + for _, v := range values { + if v != nil { + matchCount++ + } + } + if matchCount == 0 { + return fmt.Errorf("%#v has no non-empty values", values) + } + + if testCheckTypeSetElemNestedAttrsInState(is, attrParts, matchCount, values) { + return nil + } + return fmt.Errorf("%q no TypeSet element %q, with the regex provided, match in state: %#v", name, attr, is.Attributes) + } +} + +// TestCheckTypeSetElemAttr is a TestCheckFunc that accepts a resource +// name, an attribute path, which should use the sentinel value '*' for indexing +// into a TypeSet. The function verifies that an element matches the provided +// value. +// +// Use this function over SDK provided TestCheckFunctions when validating a +// TypeSet where its elements are a simple value +func TestCheckTypeSetElemAttr(name, attr, value string) TestCheckFunc { + return func(s *terraform.State) error { + is, err := primaryInstanceState(s, name) + if err != nil { + return err + } + + err = testCheckTypeSetElem(is, attr, value) + if err != nil { + return fmt.Errorf("%q error: %s", name, err) + } + + return nil + } +} + +// TestCheckTypeSetElemAttrPair is a TestCheckFunc that verifies a pair of name/key +// combinations are equal where the first uses the sentinel value to index into a +// TypeSet. +// +// E.g., TestCheckTypeSetElemAttrPair("aws_autoscaling_group.bar", "availability_zones.*", "data.aws_availability_zones.available", "names.0") +// E.g., TestCheckTypeSetElemAttrPair("aws_spot_fleet_request.bar", "launch_specification.*.instance_type", "data.data.aws_ec2_instance_type_offering.available", "instance_type") +func TestCheckTypeSetElemAttrPair(nameFirst, keyFirst, nameSecond, keySecond string) TestCheckFunc { + return func(s *terraform.State) error { + isFirst, err := primaryInstanceState(s, nameFirst) + if err != nil { + return err + } + + isSecond, err := primaryInstanceState(s, nameSecond) + if err != nil { + return err + } + + vSecond, okSecond := isSecond.Attributes[keySecond] + if !okSecond { + return fmt.Errorf("%s: Attribute %q not set, cannot be checked against TypeSet", nameSecond, keySecond) + } + + return testCheckTypeSetElemPair(isFirst, keyFirst, vSecond) + } +} + +func testCheckTypeSetElem(is *terraform.InstanceState, attr, value string) error { + attrParts := strings.Split(attr, ".") + if attrParts[len(attrParts)-1] != sentinelIndex { + return fmt.Errorf("%q does not end with the special value %q", attr, sentinelIndex) + } + for stateKey, stateValue := range is.Attributes { + if stateValue == value { + stateKeyParts := strings.Split(stateKey, ".") + if len(stateKeyParts) == len(attrParts) { + for i := range attrParts { + if attrParts[i] != stateKeyParts[i] && attrParts[i] != sentinelIndex { + break + } + if i == len(attrParts)-1 { + return nil + } + } + } + } + } + + return fmt.Errorf("no TypeSet element %q, with value %q in state: %#v", attr, value, is.Attributes) +} + +func testCheckTypeSetElemPair(is *terraform.InstanceState, attr, value string) error { + attrParts := strings.Split(attr, ".") + for stateKey, stateValue := range is.Attributes { + if stateValue == value { + stateKeyParts := strings.Split(stateKey, ".") + if len(stateKeyParts) == len(attrParts) { + for i := range attrParts { + if attrParts[i] != stateKeyParts[i] && attrParts[i] != sentinelIndex { + break + } + if i == len(attrParts)-1 { + return nil + } + } + } + } + } + + return fmt.Errorf("no TypeSet element %q, with value %q in state: %#v", attr, value, is.Attributes) +} + +// testCheckTypeSetElemNestedAttrsInState is a helper function +// to determine if nested attributes and their values are equal to those +// in the instance state. Currently, the function accepts a "values" param of type +// map[string]string or map[string]*regexp.Regexp. +// Returns true if all attributes match, else false. +func testCheckTypeSetElemNestedAttrsInState(is *terraform.InstanceState, attrParts []string, matchCount int, values interface{}) bool { + matches := make(map[string]int) + + for stateKey, stateValue := range is.Attributes { + stateKeyParts := strings.Split(stateKey, ".") + // a Set/List item with nested attrs would have a flatmap address of + // at least length 3 + // foo.0.name = "bar" + if len(stateKeyParts) < 3 || len(attrParts) > len(stateKeyParts) { + continue + } + var pathMatch bool + for i := range attrParts { + if attrParts[i] != stateKeyParts[i] && attrParts[i] != sentinelIndex { + break + } + if i == len(attrParts)-1 { + pathMatch = true + } + } + if !pathMatch { + continue + } + id := stateKeyParts[len(attrParts)-1] + nestedAttr := strings.Join(stateKeyParts[len(attrParts):], ".") + + var match bool + switch t := values.(type) { + case map[string]string: + if v, keyExists := t[nestedAttr]; keyExists && v == stateValue { + match = true + } + case map[string]*regexp.Regexp: + if v, keyExists := t[nestedAttr]; keyExists && v != nil && v.MatchString(stateValue) { + match = true + } + } + if match { + matches[id] = matches[id] + 1 + if matches[id] == matchCount { + return true + } + } + } + return false +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_sets_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_sets_test.go new file mode 100644 index 00000000..fc229003 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_sets_test.go @@ -0,0 +1,2594 @@ +package resource + +import ( + "regexp" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestTestCheckTypeSetElemAttr(t *testing.T) { + testCases := []struct { + Description string + ResourceAddress string + ResourceAttribute string + Value string + TerraformState *terraform.State + ExpectedError func(err error) bool + }{ + { + Description: "no resources", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Value: "", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{}, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "Not found: example_thing.test") + }, + }, + { + Description: "resource not found", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Value: "", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_other_thing.test": { + Type: "example_other_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "Not found: example_thing.test") + }, + }, + { + Description: "no primary instance", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Value: "", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Deposed: []*terraform.InstanceState{ + { + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "No primary instance: example_thing.test") + }, + }, + { + Description: "attribute path does not end with sentinel value", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test", + Value: "", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "does not end with the special value") + }, + }, + { + Description: "attribute not found", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Value: "", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" error: no TypeSet element \"test.*\"") + }, + }, + { + Description: "single root TypeSet attribute match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Value: "value1", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "3", + "id": "11111", + "test.%": "1", + "test.12345": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single root TypeSet attribute mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Value: "value2", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "3", + "id": "11111", + "test.%": "1", + "test.12345": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" error: no TypeSet element \"test.*\"") + }, + }, + { + Description: "multiple root TypeSet attribute match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Value: "value1", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "2", + "test.12345": "value2", + "test.67890": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "multiple root TypeSet attribute mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Value: "value3", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "2", + "test.12345": "value2", + "test.67890": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" error: no TypeSet element \"test.*\"") + }, + }, + { + Description: "single nested TypeSet attribute match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.0.nested_test.*", + Value: "value1", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "1", + "test.0.nested_test.12345": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single nested TypeSet attribute mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.0.nested_test.*", + Value: "value2", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "1", + "test.0.nested_test.12345": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" error: no TypeSet element \"test.0.nested_test.*\"") + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.Description, func(t *testing.T) { + err := TestCheckTypeSetElemAttr(testCase.ResourceAddress, testCase.ResourceAttribute, testCase.Value)(testCase.TerraformState) + + if err != nil { + if testCase.ExpectedError == nil { + t.Fatalf("expected no error, got error: %s", err) + } + + if !testCase.ExpectedError(err) { + t.Fatalf("unexpected error: %s", err) + } + + t.Logf("received expected error: %s", err) + return + } + + if err == nil && testCase.ExpectedError != nil { + t.Fatalf("expected error, got no error") + } + }) + } +} + +func TestTestCheckTypeSetElemAttrPair(t *testing.T) { + testCases := []struct { + Description string + FirstResourceAddress string + FirstResourceAttribute string + SecondResourceAddress string + SecondResourceAttribute string + TerraformState *terraform.State + ExpectedError func(err error) bool + }{ + { + Description: "first resource no primary instance", + FirstResourceAddress: "asg.bar", + FirstResourceAttribute: "az.*", + SecondResourceAddress: "data.az.available", + SecondResourceAttribute: "names.0", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "asg.bar": { + Type: "asg", + Provider: "example", + }, + "data.az.available": { + Type: "data.az", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "3579", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "3579", + "names.#": "3", + "names.0": "uswst3", + "names.1": "uswst2", + "names.3": "uswst1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "No primary instance") + }, + }, + { + Description: "second resource no primary instance", + FirstResourceAddress: "asg.bar", + FirstResourceAttribute: "az.*", + SecondResourceAddress: "data.az.available", + SecondResourceAttribute: "names.0", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "asg.bar": { + Type: "asg", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + "az.%": "2", + "az.12345": "uswst2", + "az.23456": "uswst3", + }, + }, + }, + "data.az.available": { + Type: "data.az", + Provider: "example", + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "No primary instance") + }, + }, + { + Description: "no resources", + FirstResourceAddress: "asg.bar", + FirstResourceAttribute: "az.*", + SecondResourceAddress: "data.az.available", + SecondResourceAttribute: "names.0", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{}, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "Not found: asg.bar") + }, + }, + { + Description: "first resource not found", + FirstResourceAddress: "asg.bar", + FirstResourceAttribute: "az.*", + SecondResourceAddress: "data.az.available", + SecondResourceAttribute: "names.0", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "data.az.available": { + Type: "data.az", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "3579", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "3579", + "names.#": "3", + "names.0": "uswst3", + "names.1": "uswst2", + "names.3": "uswst1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "Not found: asg.bar") + }, + }, + { + Description: "second resource not found", + FirstResourceAddress: "asg.bar", + FirstResourceAttribute: "az.*", + SecondResourceAddress: "data.az.available", + SecondResourceAttribute: "names.0", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "asg.bar": { + Type: "asg", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + "az.%": "2", + "az.12345": "uswst2", + "az.23456": "uswst3", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "Not found") + }, + }, + { + Description: "first resource attribute not found", + FirstResourceAddress: "asg.bar", + FirstResourceAttribute: "az.*", + SecondResourceAddress: "data.az.available", + SecondResourceAttribute: "names.0", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "asg.bar": { + Type: "asg", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + "data.az.available": { + Type: "data.az", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "3579", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "3579", + "names.#": "3", + "names.0": "uswst3", + "names.1": "uswst2", + "names.3": "uswst1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "no TypeSet element \"az.*\", with value \"uswst3\" in state") + }, + }, + { + Description: "second resource attribute not found", + FirstResourceAddress: "asg.bar", + FirstResourceAttribute: "az.*", + SecondResourceAddress: "data.az.available", + SecondResourceAttribute: "names.0", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "asg.bar": { + Type: "asg", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + "az.%": "2", + "az.12345": "uswst2", + "az.23456": "uswst3", + }, + }, + }, + "data.az.available": { + Type: "data.az", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "3579", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "3579", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), `Attribute "names.0" not set`) + }, + }, + { + Description: "first resource attribute does not end with sentinel", + FirstResourceAddress: "asg.bar", + FirstResourceAttribute: "az.34812", + SecondResourceAddress: "data.az.available", + SecondResourceAttribute: "names.0", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "asg.bar": { + Type: "asg", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + "az.%": "2", + "az.12345": "uswst2", + "az.23456": "uswst3", + }, + }, + }, + "data.az.available": { + Type: "data.az", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "3579", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "3579", + "names.#": "3", + "names.0": "uswst3", + "names.1": "uswst2", + "names.3": "uswst1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), `no TypeSet element "az.34812", with value "uswst3" in state`) + }, + }, + { + Description: "second resource attribute ends with sentinel", + FirstResourceAddress: "asg.bar", + FirstResourceAttribute: "az.*", + SecondResourceAddress: "data.az.available", + SecondResourceAttribute: "names.*", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "asg.bar": { + Type: "asg", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + "az.%": "2", + "az.12345": "uswst2", + "az.23456": "uswst3", + }, + }, + }, + "data.az.available": { + Type: "data.az", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "3579", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "3579", + "names.#": "3", + "names.0": "uswst3", + "names.1": "uswst2", + "names.3": "uswst1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), `data.az.available: Attribute "names.*" not set`) + }, + }, + { + Description: "match zero attribute", + FirstResourceAddress: "asg.bar", + FirstResourceAttribute: "az.*", + SecondResourceAddress: "data.az.available", + SecondResourceAttribute: "names.0", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "asg.bar": { + Type: "asg", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + "az.%": "2", + "az.12345": "uswst2", + "az.23456": "uswst3", + }, + }, + }, + "data.az.available": { + Type: "data.az", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "3579", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "3579", + "names.#": "3", + "names.0": "uswst3", + "names.1": "uswst2", + "names.3": "uswst1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "match non-zero attribute", + FirstResourceAddress: "asg.bar", + FirstResourceAttribute: "az.*", + SecondResourceAddress: "data.az.available", + SecondResourceAttribute: "names.2", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "asg.bar": { + Type: "asg", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + "az.%": "2", + "az.12345": "uswst1", + "az.23456": "uswst3", + }, + }, + }, + "data.az.available": { + Type: "data.az", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "3579", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "3579", + "names.#": "3", + "names.0": "uswst3", + "names.1": "uswst2", + "names.2": "uswst1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single nested TypeSet argument and root TypeSet argument", + FirstResourceAddress: "spot_fleet_request.bar", + FirstResourceAttribute: "launch_specification.*.instance_type", + SecondResourceAddress: "data.ec2_instances.available", + SecondResourceAttribute: "instance_type", + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "spot_fleet_request.bar": { + Type: "spot_fleet_request", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + "launch_specification.#": "1", + "launch_specification.12345.instance_type": "t2.micro", + }, + }, + }, + "data.ec2_instances.available": { + Type: "data.ec2_instances", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "3579", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "3579", + "instance_type": "t2.micro", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.Description, func(t *testing.T) { + err := TestCheckTypeSetElemAttrPair( + testCase.FirstResourceAddress, + testCase.FirstResourceAttribute, + testCase.SecondResourceAddress, + testCase.SecondResourceAttribute)(testCase.TerraformState) + + if err != nil { + if testCase.ExpectedError == nil { + t.Fatalf("expected no error, got error: %s", err) + } + + if !testCase.ExpectedError(err) { + t.Fatalf("unexpected error: %s", err) + } + + t.Logf("received expected error: %s", err) + return + } + + if err == nil && testCase.ExpectedError != nil { + t.Fatalf("expected error, got no error") + } + }) + } +} + +func TestTestMatchTypeSetElemNestedAttrs(t *testing.T) { + testCases := []struct { + Description string + ResourceAddress string + ResourceAttribute string + Values map[string]*regexp.Regexp + TerraformState *terraform.State + ExpectedError func(err error) bool + }{ + { + Description: "no resources", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]*regexp.Regexp{}, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{}, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "Not found: example_thing.test") + }, + }, + { + Description: "resource not found", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]*regexp.Regexp{}, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_other_thing.test": { + Type: "example_other_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "Not found: example_thing.test") + }, + }, + { + Description: "no primary instance", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]*regexp.Regexp{}, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Deposed: []*terraform.InstanceState{ + { + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "No primary instance: example_thing.test") + }, + }, + { + Description: "value map has no non-empty values", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]*regexp.Regexp{"key": nil}, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "has no non-empty values") + }, + }, + { + Description: "attribute path does not end with sentinel value", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test", + Values: map[string]*regexp.Regexp{}, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "does not end with the special value") + }, + }, + { + Description: "attribute not found", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]*regexp.Regexp{"key": regexp.MustCompile("value")}, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.*\"") + }, + }, + { + Description: "single root TypeSet attribute single value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]*regexp.Regexp{ + "key1": regexp.MustCompile("value"), + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "3", + "id": "11111", + "test.%": "1", + "test.12345.key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single root TypeSet attribute single value mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]*regexp.Regexp{ + "key1": regexp.MustCompile("2"), + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "3", + "id": "11111", + "test.%": "1", + "test.12345.key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.*\"") + }, + }, + { + Description: "single root TypeSet attribute single nested value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]*regexp.Regexp{ + "key1.0.nested_key1": regexp.MustCompile("value"), + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "3", + "id": "11111", + "test.%": "1", + "test.12345.key1.0.nested_key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single root TypeSet attribute single nested value mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]*regexp.Regexp{ + "key1.0.nested_key1": regexp.MustCompile("2"), + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "3", + "id": "11111", + "test.%": "1", + "test.12345.key1.0.nested_key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.*\"") + }, + }, + { + Description: "single root TypeSet attribute multiple value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]*regexp.Regexp{ + "key1": regexp.MustCompile("value"), + "key2": regexp.MustCompile("value"), + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "1", + "test.12345.key1": "value1", + "test.12345.key2": "value2", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single root TypeSet attribute unset/empty value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]*regexp.Regexp{ + "key1": regexp.MustCompile("value"), + "key2": nil, + "key3": nil, + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "1", + "test.12345.key1": "value1", + "test.12345.key2": "", + // key3 is unset + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single root TypeSet attribute multiple value mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]*regexp.Regexp{ + "key1": regexp.MustCompile("1"), + "key2": regexp.MustCompile("3"), + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "1", + "test.12345.key1": "value1", + "test.12345.key2": "value2", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.*\"") + }, + }, + { + Description: "multiple root TypeSet attribute single value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]*regexp.Regexp{ + "key1": regexp.MustCompile("value1"), + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "2", + "test.12345.key1": "value2", + "test.67890.key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "multiple root TypeSet attribute multiple value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]*regexp.Regexp{ + "key1": regexp.MustCompile("1"), + "key2": regexp.MustCompile("2"), + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "6", + "id": "11111", + "test.%": "2", + "test.12345.key1": "value2", + "test.12345.key2": "value3", + "test.67890.key1": "value1", + "test.67890.key2": "value2", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single nested TypeSet attribute single value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.0.nested_test.*", + Values: map[string]*regexp.Regexp{ + "key1": regexp.MustCompile("value"), + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "1", + "test.0.nested_test.%": "1", + "test.0.nested_test.12345.key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single nested TypeSet attribute single value mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.0.nested_test.*", + Values: map[string]*regexp.Regexp{ + "key1": regexp.MustCompile("2"), + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "1", + "test.0.nested_test.%": "1", + "test.0.nested_test.12345.key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.0.nested_test.*\"") + }, + }, + { + Description: "single nested TypeSet attribute single nested value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.0.nested_test.*", + Values: map[string]*regexp.Regexp{ + "key1.0.nested_key1": regexp.MustCompile("value"), + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "5", + "id": "11111", + "test.%": "1", + "test.0.nested_test.%": "1", + "test.0.nested_test.12345.key1.%": "1", + "test.0.nested_test.12345.key1.0.nested_key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single nested TypeSet attribute single nested value mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.0.nested_test.*", + Values: map[string]*regexp.Regexp{ + "key1.0.nested_key1": regexp.MustCompile("2"), + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "5", + "id": "11111", + "test.%": "1", + "test.0.nested_test.%": "1", + "test.0.nested_test.12345.key1.%": "1", + "test.0.nested_test.12345.key1.0.nested_key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.0.nested_test.*\"") + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.Description, func(t *testing.T) { + err := TestMatchTypeSetElemNestedAttrs(testCase.ResourceAddress, testCase.ResourceAttribute, testCase.Values)(testCase.TerraformState) + + if err != nil { + if testCase.ExpectedError == nil { + t.Fatalf("expected no error, got error: %s", err) + } + + if !testCase.ExpectedError(err) { + t.Fatalf("unexpected error: %s", err) + } + + t.Logf("received expected error: %s", err) + return + } + + if err == nil && testCase.ExpectedError != nil { + t.Fatalf("expected error, got no error") + } + }) + } +} + +func TestTestCheckTypeSetElemNestedAttrs(t *testing.T) { + testCases := []struct { + Description string + ResourceAddress string + ResourceAttribute string + Values map[string]string + TerraformState *terraform.State + ExpectedError func(err error) bool + }{ + { + Description: "no resources", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{}, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{}, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "Not found: example_thing.test") + }, + }, + { + Description: "resource not found", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{}, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_other_thing.test": { + Type: "example_other_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "Not found: example_thing.test") + }, + }, + { + Description: "no primary instance", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{}, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Deposed: []*terraform.InstanceState{ + { + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "No primary instance: example_thing.test") + }, + }, + { + Description: "value map has no non-empty values", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{"key": ""}, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "has no non-empty values") + }, + }, + { + Description: "attribute path does not end with sentinel value", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test", + Values: map[string]string{}, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "does not end with the special value") + }, + }, + { + Description: "attribute not found", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{"key": "value"}, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "1", + "id": "11111", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.*\"") + }, + }, + { + Description: "single root TypeSet attribute single value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{ + "key1": "value1", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "3", + "id": "11111", + "test.%": "1", + "test.12345.key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single root TypeSet attribute single value mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{ + "key1": "value2", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "3", + "id": "11111", + "test.%": "1", + "test.12345.key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.*\"") + }, + }, + { + Description: "single root TypeSet attribute single nested value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{ + "key1.0.nested_key1": "value1", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "3", + "id": "11111", + "test.%": "1", + "test.12345.key1.0.nested_key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single root TypeSet attribute single nested value match with varying depths", + ResourceAddress: "example_thing.test", + ResourceAttribute: "groups.*.groups.*.groups.*", + Values: map[string]string{ + "gid": "group ID 3", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "2", + "id": "resource ID", + "groups.%": "2", + "groups.#": "2", + "groups.0.%": "2", + "groups.0.gid": "group ID 0", + "groups.0.groups.#": "0", + "groups.1.%": "2", + "groups.1.gid": "group ID 1", + "groups.1.groups.#": "1", + "groups.1.groups.0.%": "2", + "groups.1.groups.0.gid": "group ID 2", + "groups.1.groups.0.groups.#": "1", + "groups.1.groups.0.groups.0.%": "2", + "groups.1.groups.0.groups.0.gid": "group ID 3", + "groups.1.groups.0.groups.0.groups.#": "0", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single root TypeSet attribute single nested value mismatch with varying depths", + ResourceAddress: "example_thing.test", + ResourceAttribute: "groups.*.groups.*.groups.*", + Values: map[string]string{ + "gid": "group ID 7", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "2", + "id": "resource ID", + "groups.%": "2", + "groups.#": "2", + "groups.0.%": "2", + "groups.0.gid": "group ID 0", + "groups.0.groups.#": "0", + "groups.1.%": "2", + "groups.1.gid": "group ID 1", + "groups.1.groups.#": "1", + "groups.1.groups.0.%": "2", + "groups.1.groups.0.gid": "group ID 2", + "groups.1.groups.0.groups.#": "1", + "groups.1.groups.0.groups.0.%": "2", + "groups.1.groups.0.groups.0.gid": "group ID 3", + "groups.1.groups.0.groups.0.groups.#": "0", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"groups.*.groups.*.groups.*\"") + }, + }, + { + Description: "single root TypeSet attribute single nested value mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{ + "key1.0.nested_key1": "value2", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "3", + "id": "11111", + "test.%": "1", + "test.12345.key1.0.nested_key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.*\"") + }, + }, + { + Description: "single root TypeSet attribute multiple value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "1", + "test.12345.key1": "value1", + "test.12345.key2": "value2", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single root TypeSet attribute unset/empty value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{ + "key1": "value1", + "key2": "", + "key3": "", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "1", + "test.12345.key1": "value1", + "test.12345.key2": "", + // key3 is unset + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single root TypeSet attribute multiple value mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{ + "key1": "value1", + "key2": "value3", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "1", + "test.12345.key1": "value1", + "test.12345.key2": "value2", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.*\"") + }, + }, + { + Description: "multiple root TypeSet attribute single value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{ + "key1": "value1", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "2", + "test.12345.key1": "value2", + "test.67890.key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "multiple root TypeSet attribute multiple value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.*", + Values: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "6", + "id": "11111", + "test.%": "2", + "test.12345.key1": "value2", + "test.12345.key2": "value3", + "test.67890.key1": "value1", + "test.67890.key2": "value2", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single nested TypeSet attribute single value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.0.nested_test.*", + Values: map[string]string{ + "key1": "value1", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "1", + "test.0.nested_test.%": "1", + "test.0.nested_test.12345.key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single nested TypeSet attribute single value mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.0.nested_test.*", + Values: map[string]string{ + "key1": "value2", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "4", + "id": "11111", + "test.%": "1", + "test.0.nested_test.%": "1", + "test.0.nested_test.12345.key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.0.nested_test.*\"") + }, + }, + { + Description: "single nested TypeSet attribute single nested value match", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.0.nested_test.*", + Values: map[string]string{ + "key1.0.nested_key1": "value1", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "5", + "id": "11111", + "test.%": "1", + "test.0.nested_test.%": "1", + "test.0.nested_test.12345.key1.%": "1", + "test.0.nested_test.12345.key1.0.nested_key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + }, + { + Description: "single nested TypeSet attribute single nested value mismatch", + ResourceAddress: "example_thing.test", + ResourceAttribute: "test.0.nested_test.*", + Values: map[string]string{ + "key1.0.nested_key1": "value2", + }, + TerraformState: &terraform.State{ + Version: 3, + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Outputs: map[string]*terraform.OutputState{}, + Resources: map[string]*terraform.ResourceState{ + "example_thing.test": { + Type: "example_thing", + Provider: "example", + Primary: &terraform.InstanceState{ + ID: "11111", + Meta: map[string]interface{}{ + "schema_version": 0, + }, + Attributes: map[string]string{ + "%": "5", + "id": "11111", + "test.%": "1", + "test.0.nested_test.%": "1", + "test.0.nested_test.12345.key1.%": "1", + "test.0.nested_test.12345.key1.0.nested_key1": "value1", + }, + }, + }, + }, + Dependencies: []string{}, + }, + }, + }, + ExpectedError: func(err error) bool { + return strings.Contains(err.Error(), "\"example_thing.test\" no TypeSet element \"test.0.nested_test.*\"") + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.Description, func(t *testing.T) { + err := TestCheckTypeSetElemNestedAttrs(testCase.ResourceAddress, testCase.ResourceAttribute, testCase.Values)(testCase.TerraformState) + + if err != nil { + if testCase.ExpectedError == nil { + t.Fatalf("expected no error, got error: %s", err) + } + + if !testCase.ExpectedError(err) { + t.Fatalf("unexpected error: %s", err) + } + + t.Logf("received expected error: %s", err) + return + } + + if err == nil && testCase.ExpectedError != nil { + t.Fatalf("expected error, got no error") + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_test.go new file mode 100644 index 00000000..0b2ae296 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/testing_test.go @@ -0,0 +1,1196 @@ +package resource + +import ( + "errors" + "flag" + "fmt" + "os" + "reflect" + "sort" + "strings" + "testing" + + "github.com/hashicorp/go-multierror" + testinginterface "github.com/mitchellh/go-testing-interface" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func init() { + if err := os.Setenv(TestEnvVar, "1"); err != nil { + panic(err) + } +} + +func TestParallelTest(t *testing.T) { + mt := new(mockT) + + ParallelTest(mt, TestCase{IsUnitTest: true}) + + if !mt.ParallelCalled { + t.Fatal("Parallel() not called") + } +} + +func TestTest_factoryError(t *testing.T) { + resourceFactoryError := fmt.Errorf("resource factory error") + + factory := func() (*schema.Provider, error) { + return nil, resourceFactoryError + } + mt := new(mockT) + recovered := false + + func() { + defer func() { + r := recover() + // this string is hardcoded in github.com/mitchellh/go-testing-interface + if s, ok := r.(string); !ok || !strings.HasPrefix(s, "testing.T failed, see logs for output") { + panic(r) + } + recovered = true + }() + Test(mt, TestCase{ + ProviderFactories: map[string]func() (*schema.Provider, error){ + "test": factory, + }, + IsUnitTest: true, + }) + }() + + if !recovered { + t.Fatalf("test should've failed fatally") + } +} + +func TestComposeAggregateTestCheckFunc(t *testing.T) { + check1 := func(s *terraform.State) error { + return errors.New("Error 1") + } + + check2 := func(s *terraform.State) error { + return errors.New("Error 2") + } + + f := ComposeAggregateTestCheckFunc(check1, check2) + err := f(nil) + if err == nil { + t.Fatalf("Expected errors") + } + + multi := err.(*multierror.Error) + if !strings.Contains(multi.Errors[0].Error(), "Error 1") { + t.Fatalf("Expected Error 1, Got %s", multi.Errors[0]) + } + if !strings.Contains(multi.Errors[1].Error(), "Error 2") { + t.Fatalf("Expected Error 2, Got %s", multi.Errors[1]) + } +} + +func TestComposeTestCheckFunc(t *testing.T) { + cases := []struct { + F []TestCheckFunc + Result string + }{ + { + F: []TestCheckFunc{ + func(*terraform.State) error { return nil }, + }, + Result: "", + }, + + { + F: []TestCheckFunc{ + func(*terraform.State) error { + return fmt.Errorf("error") + }, + func(*terraform.State) error { return nil }, + }, + Result: "Check 1/2 error: error", + }, + + { + F: []TestCheckFunc{ + func(*terraform.State) error { return nil }, + func(*terraform.State) error { + return fmt.Errorf("error") + }, + }, + Result: "Check 2/2 error: error", + }, + + { + F: []TestCheckFunc{ + func(*terraform.State) error { return nil }, + func(*terraform.State) error { return nil }, + }, + Result: "", + }, + } + + for i, tc := range cases { + f := ComposeTestCheckFunc(tc.F...) + err := f(nil) + if err == nil { + err = fmt.Errorf("") + } + if tc.Result != err.Error() { + t.Fatalf("Case %d bad: %s", i, err) + } + } +} + +// mockT implements TestT for testing +type mockT struct { + testinginterface.RuntimeT + + ParallelCalled bool +} + +func (t *mockT) Parallel() { + t.ParallelCalled = true +} + +func (t *mockT) Name() string { + return "MockedName" +} + +func TestTest_Main(t *testing.T) { + flag.Parse() + if *flagSweep == "" { + // Tests for the TestMain method used for Sweepers will panic without the -sweep + // flag specified. Mock the value for now + *flagSweep = "us-east-1" + } + + cases := []struct { + Name string + Sweepers map[string]*Sweeper + ExpectedRunList []string + SweepRun string + }{ + { + Name: "basic passing", + Sweepers: map[string]*Sweeper{ + "aws_dummy": { + Name: "aws_dummy", + F: mockSweeperFunc, + }, + }, + ExpectedRunList: []string{"aws_dummy"}, + }, + } + + for _, tc := range cases { + // reset sweepers + sweeperFuncs = map[string]*Sweeper{} + + t.Run(tc.Name, func(t *testing.T) { + for n, s := range tc.Sweepers { + AddTestSweepers(n, s) + } + *flagSweepRun = tc.SweepRun + + // Verify it does not exit/panic + TestMain(&testing.M{}) + }) + } +} + +func TestFilterSweepers(t *testing.T) { + cases := []struct { + Name string + Sweepers map[string]*Sweeper + Filter string + ExpectedSweepers []string + }{ + { + Name: "normal", + Sweepers: map[string]*Sweeper{ + "aws_dummy": { + Name: "aws_dummy", + F: mockSweeperFunc, + }, + }, + ExpectedSweepers: []string{"aws_dummy"}, + }, + { + Name: "with dep", + Sweepers: map[string]*Sweeper{ + "aws_dummy": { + Name: "aws_dummy", + F: mockSweeperFunc, + }, + "aws_top": { + Name: "aws_top", + Dependencies: []string{"aws_sub"}, + F: mockSweeperFunc, + }, + "aws_sub": { + Name: "aws_sub", + F: mockSweeperFunc, + }, + }, + ExpectedSweepers: []string{"aws_dummy", "aws_sub", "aws_top"}, + }, + { + Name: "with filter", + Sweepers: map[string]*Sweeper{ + "aws_dummy": { + Name: "aws_dummy", + F: mockSweeperFunc, + }, + "aws_top": { + Name: "aws_top", + Dependencies: []string{"aws_sub"}, + F: mockSweeperFunc, + }, + "aws_sub": { + Name: "aws_sub", + F: mockSweeperFunc, + }, + }, + ExpectedSweepers: []string{"aws_dummy"}, + Filter: "aws_dummy", + }, + { + Name: "with two filters", + Sweepers: map[string]*Sweeper{ + "aws_dummy": { + Name: "aws_dummy", + F: mockSweeperFunc, + }, + "aws_top": { + Name: "aws_top", + Dependencies: []string{"aws_sub"}, + F: mockSweeperFunc, + }, + "aws_sub": { + Name: "aws_sub", + F: mockSweeperFunc, + }, + }, + ExpectedSweepers: []string{"aws_dummy", "aws_sub"}, + Filter: "aws_dummy,aws_sub", + }, + { + Name: "with dep and filter", + Sweepers: map[string]*Sweeper{ + "aws_dummy": { + Name: "aws_dummy", + F: mockSweeperFunc, + }, + "aws_top": { + Name: "aws_top", + Dependencies: []string{"aws_sub"}, + F: mockSweeperFunc, + }, + "aws_sub": { + Name: "aws_sub", + F: mockSweeperFunc, + }, + }, + ExpectedSweepers: []string{"aws_sub", "aws_top"}, + Filter: "aws_top", + }, + { + Name: "with non-matching filter", + Sweepers: map[string]*Sweeper{ + "aws_dummy": { + Name: "aws_dummy", + F: mockSweeperFunc, + }, + "aws_top": { + Name: "aws_top", + Dependencies: []string{"aws_sub"}, + F: mockSweeperFunc, + }, + "aws_sub": { + Name: "aws_sub", + F: mockSweeperFunc, + }, + }, + Filter: "none", + }, + { + Name: "with nested depenencies and top level filter", + Sweepers: map[string]*Sweeper{ + "not_matching": { + Name: "not_matching", + F: mockSweeperFunc, + }, + "matching_level1": { + Name: "matching_level1", + Dependencies: []string{"matching_level2"}, + F: mockSweeperFunc, + }, + "matching_level2": { + Name: "matching_level2", + Dependencies: []string{"matching_level3"}, + F: mockSweeperFunc, + }, + "matching_level3": { + Name: "matching_level3", + F: mockSweeperFunc, + }, + }, + ExpectedSweepers: []string{"matching_level1", "matching_level2", "matching_level3"}, + Filter: "matching_level1", + }, + { + Name: "with nested depenencies and middle level filter", + Sweepers: map[string]*Sweeper{ + "not_matching": { + Name: "not_matching", + F: mockSweeperFunc, + }, + "matching_level1": { + Name: "matching_level1", + Dependencies: []string{"matching_level2"}, + F: mockSweeperFunc, + }, + "matching_level2": { + Name: "matching_level2", + Dependencies: []string{"matching_level3"}, + F: mockSweeperFunc, + }, + "matching_level3": { + Name: "matching_level3", + F: mockSweeperFunc, + }, + }, + ExpectedSweepers: []string{"matching_level2", "matching_level3"}, + Filter: "matching_level2", + }, + { + Name: "with nested depenencies and bottom level filter", + Sweepers: map[string]*Sweeper{ + "not_matching": { + Name: "not_matching", + F: mockSweeperFunc, + }, + "matching_level1": { + Name: "matching_level1", + Dependencies: []string{"matching_level2"}, + F: mockSweeperFunc, + }, + "matching_level2": { + Name: "matching_level2", + Dependencies: []string{"matching_level3"}, + F: mockSweeperFunc, + }, + "matching_level3": { + Name: "matching_level3", + F: mockSweeperFunc, + }, + }, + ExpectedSweepers: []string{"matching_level3"}, + Filter: "matching_level3", + }, + } + + for _, tc := range cases { + // reset sweepers + sweeperFuncs = map[string]*Sweeper{} + + t.Run(tc.Name, func(t *testing.T) { + actualSweepers := filterSweepers(tc.Filter, tc.Sweepers) + + var keys []string + for k := range actualSweepers { + keys = append(keys, k) + } + + sort.Strings(keys) + sort.Strings(tc.ExpectedSweepers) + if !reflect.DeepEqual(keys, tc.ExpectedSweepers) { + t.Fatalf("Expected keys mismatch, expected:\n%#v\ngot:\n%#v\n", tc.ExpectedSweepers, keys) + } + }) + } +} + +func TestFilterSweeperWithDependencies(t *testing.T) { + cases := []struct { + Name string + Sweepers map[string]*Sweeper + StartingSweeper string + ExpectedSweepers []string + }{ + { + Name: "no dependencies", + Sweepers: map[string]*Sweeper{ + "matching_level1": { + Name: "matching_level1", + F: mockSweeperFunc, + }, + "non_matching": { + Name: "non_matching", + F: mockSweeperFunc, + }, + }, + StartingSweeper: "matching_level1", + ExpectedSweepers: []string{"matching_level1"}, + }, + { + Name: "one level one dependency", + Sweepers: map[string]*Sweeper{ + "matching_level1": { + Name: "matching_level1", + Dependencies: []string{"matching_level2"}, + F: mockSweeperFunc, + }, + "matching_level2": { + Name: "matching_level2", + F: mockSweeperFunc, + }, + }, + StartingSweeper: "matching_level1", + ExpectedSweepers: []string{"matching_level1", "matching_level2"}, + }, + { + Name: "one level multiple dependencies", + Sweepers: map[string]*Sweeper{ + "matching_level1": { + Name: "matching_level1", + Dependencies: []string{"matching_level2a", "matching_level2b"}, + F: mockSweeperFunc, + }, + "matching_level2a": { + Name: "matching_level2a", + F: mockSweeperFunc, + }, + "matching_level2b": { + Name: "matching_level2b", + F: mockSweeperFunc, + }, + }, + StartingSweeper: "matching_level1", + ExpectedSweepers: []string{"matching_level1", "matching_level2a", "matching_level2b"}, + }, + { + Name: "multiple level one dependency", + Sweepers: map[string]*Sweeper{ + "matching_level1": { + Name: "matching_level1", + Dependencies: []string{"matching_level2"}, + F: mockSweeperFunc, + }, + "matching_level2": { + Name: "matching_level2", + Dependencies: []string{"matching_level3"}, + F: mockSweeperFunc, + }, + "matching_level3": { + Name: "matching_level3", + F: mockSweeperFunc, + }, + }, + StartingSweeper: "matching_level1", + ExpectedSweepers: []string{"matching_level1", "matching_level2", "matching_level3"}, + }, + { + Name: "multiple level multiple dependencies", + Sweepers: map[string]*Sweeper{ + "matching_level1": { + Name: "matching_level1", + Dependencies: []string{"matching_level2a", "matching_level2b"}, + F: mockSweeperFunc, + }, + "matching_level2a": { + Name: "matching_level2a", + Dependencies: []string{"matching_level3a", "matching_level3b"}, + F: mockSweeperFunc, + }, + "matching_level2b": { + Name: "matching_level2b", + Dependencies: []string{"matching_level3c", "matching_level3d"}, + F: mockSweeperFunc, + }, + "matching_level3a": { + Name: "matching_level3a", + F: mockSweeperFunc, + }, + "matching_level3b": { + Name: "matching_level3b", + F: mockSweeperFunc, + }, + "matching_level3c": { + Name: "matching_level3c", + F: mockSweeperFunc, + }, + "matching_level3d": { + Name: "matching_level3d", + F: mockSweeperFunc, + }, + }, + StartingSweeper: "matching_level1", + ExpectedSweepers: []string{"matching_level1", "matching_level2a", "matching_level2b", "matching_level3a", "matching_level3b", "matching_level3c", "matching_level3d"}, + }, + { + Name: "no parents one level", + Sweepers: map[string]*Sweeper{ + "matching_level1": { + Name: "matching_level1", + Dependencies: []string{"matching_level2"}, + F: mockSweeperFunc, + }, + "matching_level2": { + Name: "matching_level2", + Dependencies: []string{"matching_level3"}, + F: mockSweeperFunc, + }, + "matching_level3": { + Name: "matching_level3", + F: mockSweeperFunc, + }, + }, + StartingSweeper: "matching_level2", + ExpectedSweepers: []string{"matching_level2", "matching_level3"}, + }, + { + Name: "no parents multiple level", + Sweepers: map[string]*Sweeper{ + "matching_level1": { + Name: "matching_level1", + Dependencies: []string{"matching_level2"}, + F: mockSweeperFunc, + }, + "matching_level2": { + Name: "matching_level2", + Dependencies: []string{"matching_level3"}, + F: mockSweeperFunc, + }, + "matching_level3": { + Name: "matching_level3", + F: mockSweeperFunc, + }, + }, + StartingSweeper: "matching_level3", + ExpectedSweepers: []string{"matching_level3"}, + }, + { + Name: "one level missing dependency", + Sweepers: map[string]*Sweeper{ + "matching_level1": { + Name: "matching_level1", + Dependencies: []string{"matching_level2a", "matching_level2c"}, + F: mockSweeperFunc, + }, + "matching_level2a": { + Name: "matching_level2a", + F: mockSweeperFunc, + }, + "matching_level2b": { + Name: "matching_level2b", + F: mockSweeperFunc, + }, + }, + StartingSweeper: "matching_level1", + ExpectedSweepers: []string{"matching_level1", "matching_level2a"}, + }, + { + Name: "multiple level missing dependencies", + Sweepers: map[string]*Sweeper{ + "matching_level1": { + Name: "matching_level1", + Dependencies: []string{"matching_level2a", "matching_level2b", "matching_level2c"}, + F: mockSweeperFunc, + }, + "matching_level2a": { + Name: "matching_level2a", + Dependencies: []string{"matching_level3a", "matching_level3e"}, + F: mockSweeperFunc, + }, + "matching_level2b": { + Name: "matching_level2b", + Dependencies: []string{"matching_level3c", "matching_level3f"}, + F: mockSweeperFunc, + }, + "matching_level3a": { + Name: "matching_level3a", + F: mockSweeperFunc, + }, + "matching_level3b": { + Name: "matching_level3b", + F: mockSweeperFunc, + }, + "matching_level3c": { + Name: "matching_level3c", + F: mockSweeperFunc, + }, + "matching_level3d": { + Name: "matching_level3d", + F: mockSweeperFunc, + }, + }, + StartingSweeper: "matching_level1", + ExpectedSweepers: []string{"matching_level1", "matching_level2a", "matching_level2b", "matching_level3a", "matching_level3c"}, + }, + } + + for _, tc := range cases { + // reset sweepers + sweeperFuncs = map[string]*Sweeper{} + + t.Run(tc.Name, func(t *testing.T) { + actualSweepers := filterSweeperWithDependencies(tc.StartingSweeper, tc.Sweepers) + + var keys []string + for k := range actualSweepers { + keys = append(keys, k) + } + + sort.Strings(keys) + sort.Strings(tc.ExpectedSweepers) + if !reflect.DeepEqual(keys, tc.ExpectedSweepers) { + t.Fatalf("Expected keys mismatch, expected:\n%#v\ngot:\n%#v\n", tc.ExpectedSweepers, keys) + } + }) + } +} + +func TestRunSweepers(t *testing.T) { + cases := []struct { + Name string + Sweepers map[string]*Sweeper + ExpectedRunList []string + SweepRun string + AllowFailures bool + ExpectError bool + }{ + { + Name: "single", + Sweepers: map[string]*Sweeper{ + "aws_dummy": { + Name: "aws_dummy", + F: mockSweeperFunc, + }, + }, + ExpectedRunList: []string{"aws_dummy"}, + }, + { + Name: "multiple", + Sweepers: map[string]*Sweeper{ + "aws_one": { + Name: "aws_one", + F: mockSweeperFunc, + }, + "aws_two": { + Name: "aws_two", + F: mockSweeperFunc, + }, + }, + ExpectedRunList: []string{"aws_one", "aws_two"}, + }, + { + Name: "multiple with dep", + Sweepers: map[string]*Sweeper{ + "aws_dummy": { + Name: "aws_dummy", + F: mockSweeperFunc, + }, + "aws_top": { + Name: "aws_top", + Dependencies: []string{"aws_sub"}, + F: mockSweeperFunc, + }, + "aws_sub": { + Name: "aws_sub", + F: mockSweeperFunc, + }, + }, + ExpectedRunList: []string{"aws_dummy", "aws_sub", "aws_top"}, + }, + { + Name: "failing dep", + Sweepers: map[string]*Sweeper{ + "aws_top": { + Name: "aws_top", + Dependencies: []string{"aws_sub"}, + F: mockSweeperFunc, + }, + "aws_sub": { + Name: "aws_sub", + F: mockFailingSweeperFunc, + }, + }, + ExpectedRunList: []string{"aws_sub"}, + ExpectError: true, + }, + { + Name: "failing dep allow failures", + Sweepers: map[string]*Sweeper{ + "aws_top": { + Name: "aws_top", + Dependencies: []string{"aws_sub"}, + F: mockSweeperFunc, + }, + "aws_sub": { + Name: "aws_sub", + F: mockFailingSweeperFunc, + }, + }, + ExpectedRunList: []string{"aws_sub", "aws_top"}, + AllowFailures: true, + ExpectError: true, + }, + { + Name: "failing top", + Sweepers: map[string]*Sweeper{ + "aws_top": { + Name: "aws_top", + Dependencies: []string{"aws_sub"}, + F: mockFailingSweeperFunc, + }, + "aws_sub": { + Name: "aws_sub", + F: mockSweeperFunc, + }, + }, + ExpectedRunList: []string{"aws_sub", "aws_top"}, + ExpectError: true, + }, + { + Name: "failing top allow failures", + Sweepers: map[string]*Sweeper{ + "aws_top": { + Name: "aws_top", + Dependencies: []string{"aws_sub"}, + F: mockFailingSweeperFunc, + }, + "aws_sub": { + Name: "aws_sub", + F: mockSweeperFunc, + }, + }, + ExpectedRunList: []string{"aws_sub", "aws_top"}, + AllowFailures: true, + ExpectError: true, + }, + { + Name: "failing top and dep", + Sweepers: map[string]*Sweeper{ + "aws_top": { + Name: "aws_top", + Dependencies: []string{"aws_sub"}, + F: mockFailingSweeperFunc, + }, + "aws_sub": { + Name: "aws_sub", + F: mockFailingSweeperFunc, + }, + }, + ExpectedRunList: []string{"aws_sub"}, + ExpectError: true, + }, + { + Name: "failing top and dep allow failues", + Sweepers: map[string]*Sweeper{ + "aws_top": { + Name: "aws_top", + Dependencies: []string{"aws_sub"}, + F: mockFailingSweeperFunc, + }, + "aws_sub": { + Name: "aws_sub", + F: mockFailingSweeperFunc, + }, + }, + ExpectedRunList: []string{"aws_sub", "aws_top"}, + AllowFailures: true, + ExpectError: true, + }, + } + + for _, tc := range cases { + // reset sweepers + sweeperFuncs = map[string]*Sweeper{} + + t.Run(tc.Name, func(t *testing.T) { + sweeperRunList, err := runSweepers([]string{"test"}, tc.Sweepers, tc.AllowFailures) + fmt.Printf("sweeperRunList: %#v\n", sweeperRunList) + + if err == nil && tc.ExpectError { + t.Fatalf("expected error, did not receive error") + } + + if err != nil && !tc.ExpectError { + t.Fatalf("did not expect error, received error: %s", err) + } + + // get list of tests ran from sweeperRunList keys + var keys []string + for k := range sweeperRunList["test"] { + keys = append(keys, k) + } + + sort.Strings(keys) + sort.Strings(tc.ExpectedRunList) + if !reflect.DeepEqual(keys, tc.ExpectedRunList) { + t.Fatalf("Expected keys mismatch, expected:\n%#v\ngot:\n%#v\n", tc.ExpectedRunList, keys) + } + }) + } +} + +func mockFailingSweeperFunc(s string) error { + return errors.New("failing sweeper") +} + +func mockSweeperFunc(s string) error { + return nil +} + +func TestCheckResourceAttr_empty(t *testing.T) { + s := terraform.NewState() + s.AddModuleState(&terraform.ModuleState{ + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test_resource": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{ + "empty_list.#": "0", + "empty_map.%": "0", + }, + }, + }, + }, + }) + + for _, key := range []string{ + "empty_list.#", + "empty_map.%", + "missing_list.#", + "missing_map.%", + } { + t.Run(key, func(t *testing.T) { + check := TestCheckResourceAttr("test_resource", key, "0") + if err := check(s); err != nil { + t.Fatal(err) + } + }) + } +} + +func TestCheckNoResourceAttr_empty(t *testing.T) { + s := terraform.NewState() + s.AddModuleState(&terraform.ModuleState{ + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test_resource": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{ + "empty_list.#": "0", + "empty_map.%": "0", + }, + }, + }, + }, + }) + + for _, key := range []string{ + "empty_list.#", + "empty_map.%", + "missing_list.#", + "missing_map.%", + } { + t.Run(key, func(t *testing.T) { + check := TestCheckNoResourceAttr("test_resource", key) + if err := check(s); err != nil { + t.Fatal(err) + } + }) + } +} + +func TestTestCheckResourceAttrPair(t *testing.T) { + tests := map[string]struct { + nameFirst string + keyFirst string + nameSecond string + keySecond string + state *terraform.State + wantErr string + }{ + "self": { + nameFirst: "test.a", + keyFirst: "a", + nameSecond: "test.a", + keySecond: "a", + state: &terraform.State{ + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test.a": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{ + "a": "boop", + }, + }, + }, + "test.b": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{ + "b": "boop", + }, + }, + }, + }, + }, + }, + }, + wantErr: `comparing self: resource test.a attribute a`, + }, + "exist match": { + nameFirst: "test.a", + keyFirst: "a", + nameSecond: "test.b", + keySecond: "b", + state: &terraform.State{ + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test.a": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{ + "a": "boop", + }, + }, + }, + "test.b": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{ + "b": "boop", + }, + }, + }, + }, + }, + }, + }, + wantErr: ``, + }, + "nonexist match": { + nameFirst: "test.a", + keyFirst: "a", + nameSecond: "test.b", + keySecond: "b", + state: &terraform.State{ + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test.a": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{}, + }, + }, + "test.b": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{}, + }, + }, + }, + }, + }, + }, + wantErr: ``, + }, + "exist nonmatch": { + nameFirst: "test.a", + keyFirst: "a", + nameSecond: "test.b", + keySecond: "b", + state: &terraform.State{ + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test.a": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{ + "a": "beep", + }, + }, + }, + "test.b": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{ + "b": "boop", + }, + }, + }, + }, + }, + }, + }, + wantErr: `test.a: Attribute 'a' expected "boop", got "beep"`, + }, + "inconsistent exist a": { + nameFirst: "test.a", + keyFirst: "a", + nameSecond: "test.b", + keySecond: "b", + state: &terraform.State{ + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test.a": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{ + "a": "beep", + }, + }, + }, + "test.b": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{}, + }, + }, + }, + }, + }, + }, + wantErr: `test.a: Attribute "a" is "beep", but "b" is not set in test.b`, + }, + "inconsistent exist b": { + nameFirst: "test.a", + keyFirst: "a", + nameSecond: "test.b", + keySecond: "b", + state: &terraform.State{ + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test.a": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{}, + }, + }, + "test.b": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{ + "b": "boop", + }, + }, + }, + }, + }, + }, + }, + wantErr: `test.a: Attribute "a" not set, but "b" is set in test.b as "boop"`, + }, + "unset and 0 equal list": { + nameFirst: "test.a", + keyFirst: "a.#", + nameSecond: "test.b", + keySecond: "a.#", + state: &terraform.State{ + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test.a": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{ + "a.#": "0", + }, + }, + }, + "test.b": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{}, + }, + }, + }, + }, + }, + }, + wantErr: ``, + }, + "unset and 0 equal map": { + nameFirst: "test.a", + keyFirst: "a.%", + nameSecond: "test.b", + keySecond: "a.%", + state: &terraform.State{ + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test.a": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{ + "a.%": "0", + }, + }, + }, + "test.b": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{}, + }, + }, + }, + }, + }, + }, + wantErr: ``, + }, + "count equal": { + nameFirst: "test.a", + keyFirst: "a.%", + nameSecond: "test.b", + keySecond: "a.%", + state: &terraform.State{ + Modules: []*terraform.ModuleState{ + { + Path: []string{"root"}, + Resources: map[string]*terraform.ResourceState{ + "test.a": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{ + "a.%": "1", + }, + }, + }, + "test.b": { + Primary: &terraform.InstanceState{ + Attributes: map[string]string{ + "a.%": "1", + }}, + }, + }, + }, + }, + }, + wantErr: ``, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + fn := TestCheckResourceAttrPair(test.nameFirst, test.keyFirst, test.nameSecond, test.keySecond) + err := fn(test.state) + + if test.wantErr != "" { + if err == nil { + t.Fatalf("succeeded; want error\nwant: %s", test.wantErr) + } + if got, want := err.Error(), test.wantErr; got != want { + t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) + } + return + } + + if err != nil { + t.Fatalf("failed; want success\ngot: %s", err.Error()) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/wait.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/wait.go index e56a5155..6b7bdae8 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/wait.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/wait.go @@ -1,13 +1,18 @@ package resource import ( + "context" + "errors" "sync" "time" ) -// Retry is a basic wrapper around StateChangeConf that will just retry +// RetryContext 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 { +// +// Cancellation from the passed in context will propagate through to the +// underlying StateChangeConf +func RetryContext(ctx context.Context, 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 @@ -38,7 +43,7 @@ func Retry(timeout time.Duration, f RetryFunc) error { }, } - _, waitErr := c.WaitForState() + _, waitErr := c.WaitForStateContext(ctx) // Need to acquire the lock here to be able to avoid race using resultErr as // the return value @@ -55,6 +60,14 @@ func Retry(timeout time.Duration, f RetryFunc) error { return resultErr } +// Retry is a basic wrapper around StateChangeConf that will just retry +// a function until it no longer returns an error. +// +// Deprecated: Please use RetryContext to ensure proper plugin shutdown +func Retry(timeout time.Duration, f RetryFunc) error { + return RetryContext(context.Background(), timeout, f) +} + // RetryFunc is the function retried until it succeeds. type RetryFunc func() *RetryError @@ -65,20 +78,36 @@ type RetryError struct { Retryable bool } +func (e *RetryError) Unwrap() error { + return e.Err +} + // RetryableError is a helper to create a RetryError that's retryable from a -// given error. +// given error. To prevent logic errors, will return an error when passed a +// nil error. func RetryableError(err error) *RetryError { if err == nil { - return nil + return &RetryError{ + Err: errors.New("empty retryable error received. " + + "This is a bug with the Terraform provider and should be " + + "reported as a GitHub issue in the provider repository."), + Retryable: false, + } } return &RetryError{Err: err, Retryable: true} } // NonRetryableError is a helper to create a RetryError that's _not_ retryable -// from a given error. +// from a given error. To prevent logic errors, will return an error when +// passed a nil error. func NonRetryableError(err error) *RetryError { if err == nil { - return nil + return &RetryError{ + Err: errors.New("empty non-retryable error received. " + + "This is a bug with the Terraform provider and should be " + + "reported as a GitHub issue in the provider repository."), + Retryable: false, + } } return &RetryError{Err: err, Retryable: false} } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/wait_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/wait_test.go new file mode 100644 index 00000000..f678954e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/resource/wait_test.go @@ -0,0 +1,159 @@ +package resource + +import ( + "context" + "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") + } +} + +func TestRetry_nilNonRetryableError(t *testing.T) { + t.Parallel() + + f := func() *RetryError { + return NonRetryableError(nil) + } + + err := Retry(1*time.Second, f) + if err == nil { + t.Fatal("should error") + } +} + +func TestRetry_nilRetryableError(t *testing.T) { + t.Parallel() + + f := func() *RetryError { + return RetryableError(nil) + } + + err := Retry(1*time.Second, f) + if err == nil { + t.Fatal("should error") + } +} + +func TestRetryContext_cancel(t *testing.T) { + t.Parallel() + + waitForever := make(chan struct{}) + defer close(waitForever) + ctx, cancel := context.WithCancel(context.Background()) + + f := func() *RetryError { + <-waitForever + return nil + } + + cancel() + + if err := RetryContext(ctx, 10*time.Millisecond, f); err != context.Canceled { + t.Fatalf("Expected context.Canceled error, got: %s", err) + } +} + +func TestRetryContext_deadline(t *testing.T) { + t.Parallel() + + waitForever := make(chan struct{}) + defer close(waitForever) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond) + defer cancel() + + f := func() *RetryError { + <-waitForever + return nil + } + + if err := RetryContext(ctx, 10*time.Second, f); err != context.DeadlineExceeded { + t.Fatalf("Expected context.DeadlineExceeded error, got: %s", err) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/backend.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/backend.go deleted file mode 100644 index 609c208b..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/backend.go +++ /dev/null @@ -1,200 +0,0 @@ -package schema - -import ( - "context" - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim" - "github.com/hashicorp/terraform-plugin-sdk/terraform" - ctyconvert "github.com/zclconf/go-cty/cty/convert" -) - -// Backend represents a partial backend.Backend implementation and simplifies -// the creation of configuration loading and validation. -// -// Unlike other schema structs such as Provider, this struct is meant to be -// embedded within your actual implementation. It provides implementations -// only for Input and Configure and gives you a method for accessing the -// configuration in the form of a ResourceData that you're expected to call -// from the other implementation funcs. -type Backend struct { - // Schema is the schema for the configuration of this backend. If this - // Backend has no configuration this can be omitted. - Schema map[string]*Schema - - // ConfigureFunc is called to configure the backend. Use the - // FromContext* methods to extract information from the context. - // This can be nil, in which case nothing will be called but the - // config will still be stored. - ConfigureFunc func(context.Context) error - - config *ResourceData -} - -var ( - backendConfigKey = contextKey("backend config") -) - -// FromContextBackendConfig extracts a ResourceData with the configuration -// from the context. This should only be called by Backend functions. -func FromContextBackendConfig(ctx context.Context) *ResourceData { - return ctx.Value(backendConfigKey).(*ResourceData) -} - -func (b *Backend) ConfigSchema() *configschema.Block { - // This is an alias of CoreConfigSchema just to implement the - // backend.Backend interface. - return b.CoreConfigSchema() -} - -func (b *Backend) PrepareConfig(configVal cty.Value) (cty.Value, tfdiags.Diagnostics) { - if b == nil { - return configVal, nil - } - var diags tfdiags.Diagnostics - var err error - - // In order to use Transform below, this needs to be filled out completely - // according the schema. - configVal, err = b.CoreConfigSchema().CoerceValue(configVal) - if err != nil { - return configVal, diags.Append(err) - } - - // lookup any required, top-level attributes that are Null, and see if we - // have a Default value available. - configVal, err = cty.Transform(configVal, func(path cty.Path, val cty.Value) (cty.Value, error) { - // we're only looking for top-level attributes - if len(path) != 1 { - return val, nil - } - - // nothing to do if we already have a value - if !val.IsNull() { - return val, nil - } - - // get the Schema definition for this attribute - getAttr, ok := path[0].(cty.GetAttrStep) - // these should all exist, but just ignore anything strange - if !ok { - return val, nil - } - - attrSchema := b.Schema[getAttr.Name] - // continue to ignore anything that doesn't match - if attrSchema == nil { - return val, nil - } - - // this is deprecated, so don't set it - if attrSchema.Deprecated != "" || attrSchema.Removed != "" { - return val, nil - } - - // find a default value if it exists - def, err := attrSchema.DefaultValue() - if err != nil { - diags = diags.Append(fmt.Errorf("error getting default for %q: %s", getAttr.Name, err)) - return val, err - } - - // no default - if def == nil { - return val, nil - } - - // create a cty.Value and make sure it's the correct type - tmpVal := hcl2shim.HCL2ValueFromConfigValue(def) - - // helper/schema used to allow setting "" to a bool - if val.Type() == cty.Bool && tmpVal.RawEquals(cty.StringVal("")) { - // return a warning about the conversion - diags = diags.Append("provider set empty string as default value for bool " + getAttr.Name) - tmpVal = cty.False - } - - val, err = ctyconvert.Convert(tmpVal, val.Type()) - if err != nil { - diags = diags.Append(fmt.Errorf("error setting default for %q: %s", getAttr.Name, err)) - } - - return val, err - }) - if err != nil { - // any error here was already added to the diagnostics - return configVal, diags - } - - shimRC := b.shimConfig(configVal) - warns, errs := schemaMap(b.Schema).Validate(shimRC) - for _, warn := range warns { - diags = diags.Append(tfdiags.SimpleWarning(warn)) - } - for _, err := range errs { - diags = diags.Append(err) - } - return configVal, diags -} - -func (b *Backend) Configure(obj cty.Value) tfdiags.Diagnostics { - if b == nil { - return nil - } - - var diags tfdiags.Diagnostics - sm := schemaMap(b.Schema) - shimRC := b.shimConfig(obj) - - // Get a ResourceData for this configuration. To do this, we actually - // generate an intermediary "diff" although that is never exposed. - diff, err := sm.Diff(nil, shimRC, nil, nil, true) - if err != nil { - diags = diags.Append(err) - return diags - } - - data, err := sm.Data(nil, diff) - if err != nil { - diags = diags.Append(err) - return diags - } - b.config = data - - if b.ConfigureFunc != nil { - err = b.ConfigureFunc(context.WithValue( - context.Background(), backendConfigKey, data)) - if err != nil { - diags = diags.Append(err) - return diags - } - } - - return diags -} - -// shimConfig turns a new-style cty.Value configuration (which must be of -// an object type) into a minimal old-style *terraform.ResourceConfig object -// that should be populated enough to appease the not-yet-updated functionality -// in this package. This should be removed once everything is updated. -func (b *Backend) shimConfig(obj cty.Value) *terraform.ResourceConfig { - shimMap, ok := hcl2shim.ConfigValueFromHCL2(obj).(map[string]interface{}) - if !ok { - // If the configVal was nil, we still want a non-nil map here. - shimMap = map[string]interface{}{} - } - return &terraform.ResourceConfig{ - Config: shimMap, - Raw: shimMap, - } -} - -// Config returns the configuration. This is available after Configure is -// called. -func (b *Backend) Config() *ResourceData { - return b.config -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/context.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/context.go new file mode 100644 index 00000000..ed8d0964 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/context.go @@ -0,0 +1,7 @@ +package schema + +type Key string + +var ( + StopContextKey = Key("StopContext") +) diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/core_schema.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/core_schema.go index fa03d833..bb1fc1db 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/core_schema.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/core_schema.go @@ -3,8 +3,41 @@ package schema import ( "fmt" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/zclconf/go-cty/cty" + "github.com/hashicorp/go-cty/cty" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" +) + +// StringKind represents the format a string is in. +type StringKind configschema.StringKind + +const ( + // StringPlain indicates a string is plain-text and requires no processing for display. + StringPlain = StringKind(configschema.StringPlain) + + // StringMarkdown indicates a string is in markdown format and may + // require additional processing to display. + StringMarkdown = StringKind(configschema.StringMarkdown) +) + +var ( + // DescriptionKind is the default StringKind of descriptions in this provider. + // It defaults to StringPlain but can be globally switched to StringMarkdown. + DescriptionKind = StringPlain + + // SchemaDescriptionBuilder converts helper/schema.Schema Descriptions to configschema.Attribute + // and Block Descriptions. This method can be used to modify the description text prior to it + // being returned in the schema. + SchemaDescriptionBuilder = func(s *Schema) string { + return s.Description + } + + // ResourceDescriptionBuilder converts helper/schema.Resource Descriptions to configschema.Block + // Descriptions at the resource top level. This method can be used to modify the description prior + // to it being returned in the schema. + ResourceDescriptionBuilder = func(r *Resource) string { + return r.Description + } ) // The functions and methods in this file are concerned with the conversion @@ -115,13 +148,22 @@ func (s *Schema) coreConfigSchemaAttribute() *configschema.Attribute { } } + desc := SchemaDescriptionBuilder(s) + descKind := configschema.StringKind(DescriptionKind) + if desc == "" { + // fallback to plain text if empty + descKind = configschema.StringPlain + } + return &configschema.Attribute{ - Type: s.coreConfigSchemaType(), - Optional: opt, - Required: reqd, - Computed: s.Computed, - Sensitive: s.Sensitive, - Description: s.Description, + Type: s.coreConfigSchemaType(), + Optional: opt, + Required: reqd, + Computed: s.Computed, + Sensitive: s.Sensitive, + Description: desc, + DescriptionKind: descKind, + Deprecated: s.Deprecated != "", } } @@ -132,6 +174,17 @@ func (s *Schema) coreConfigSchemaBlock() *configschema.NestedBlock { ret := &configschema.NestedBlock{} if nested := s.Elem.(*Resource).coreConfigSchema(); nested != nil { ret.Block = *nested + + desc := SchemaDescriptionBuilder(s) + descKind := configschema.StringKind(DescriptionKind) + if desc == "" { + // fallback to plain text if empty + descKind = configschema.StringPlain + } + // set these on the block from the attribute Schema + ret.Block.Description = desc + ret.Block.DescriptionKind = descKind + ret.Block.Deprecated = s.Deprecated != "" } switch s.Type { case TypeList: @@ -231,6 +284,18 @@ func (s *Schema) coreConfigSchemaType() cty.Type { func (r *Resource) CoreConfigSchema() *configschema.Block { block := r.coreConfigSchema() + desc := ResourceDescriptionBuilder(r) + descKind := configschema.StringKind(DescriptionKind) + if desc == "" { + // fallback to plain text if empty + descKind = configschema.StringPlain + } + + // Only apply Resource Description, Kind, Deprecation at top level + block.Description = desc + block.DescriptionKind = descKind + block.Deprecated = r.DeprecationMessage != "" + if block.Attributes == nil { block.Attributes = map[string]*configschema.Attribute{} } @@ -301,9 +366,3 @@ func (r *Resource) CoreConfigSchema() *configschema.Block { func (r *Resource) coreConfigSchema() *configschema.Block { return schemaMap(r.Schema).CoreConfigSchema() } - -// CoreConfigSchema is a convenient shortcut for calling CoreConfigSchema -// on the backends's schema. -func (r *Backend) CoreConfigSchema() *configschema.Block { - return schemaMap(r.Schema).CoreConfigSchema() -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/core_schema_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/core_schema_test.go new file mode 100644 index 00000000..01aadc29 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/core_schema_test.go @@ -0,0 +1,468 @@ +package schema + +import ( + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/go-cty/cty" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" +) + +// add the implicit "id" attribute for test resources +func testResource(block *configschema.Block) *configschema.Block { + if block.Attributes == nil { + block.Attributes = make(map[string]*configschema.Attribute) + } + + if block.BlockTypes == nil { + block.BlockTypes = make(map[string]*configschema.NestedBlock) + } + + if block.Attributes["id"] == nil { + block.Attributes["id"] = &configschema.Attribute{ + Type: cty.String, + Optional: true, + Computed: true, + } + } + return block +} + +func TestSchemaMapCoreConfigSchema(t *testing.T) { + // these are global so if new tests are written we should probably employ a mutex + DescriptionKind = StringMarkdown + SchemaDescriptionBuilder = func(s *Schema) string { + if s.Required && s.Description != "" { + return fmt.Sprintf("**Required** %s", s.Description) + } + return s.Description + } + + tests := map[string]struct { + Schema map[string]*Schema + Want *configschema.Block + }{ + "empty": { + map[string]*Schema{}, + testResource(&configschema.Block{}), + }, + "primitives": { + map[string]*Schema{ + "int": { + Type: TypeInt, + Required: true, + Description: "foo bar baz", + }, + "float": { + Type: TypeFloat, + Optional: true, + }, + "bool": { + Type: TypeBool, + Computed: true, + }, + "string": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + testResource(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "int": { + Type: cty.Number, + Required: true, + Description: "**Required** foo bar baz", + DescriptionKind: configschema.StringMarkdown, + }, + "float": { + Type: cty.Number, + Optional: true, + }, + "bool": { + Type: cty.Bool, + Computed: true, + }, + "string": { + Type: cty.String, + Optional: true, + Computed: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{}, + }), + }, + "simple collections": { + map[string]*Schema{ + "list": { + Type: TypeList, + Required: true, + Elem: &Schema{ + Type: TypeInt, + }, + }, + "set": { + Type: TypeSet, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + "map": { + Type: TypeMap, + Optional: true, + Elem: &Schema{ + Type: TypeBool, + }, + }, + "map_default_type": { + Type: TypeMap, + Optional: true, + // Maps historically don't have elements because we + // assumed they would be strings, so this needs to work + // for pre-existing schemas. + }, + }, + testResource(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "list": { + Type: cty.List(cty.Number), + Required: true, + }, + "set": { + Type: cty.Set(cty.String), + Optional: true, + }, + "map": { + Type: cty.Map(cty.Bool), + Optional: true, + }, + "map_default_type": { + Type: cty.Map(cty.String), + Optional: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{}, + }), + }, + "incorrectly-specified collections": { + // Historically we tolerated setting a type directly as the Elem + // attribute, rather than a Schema object. This is common enough + // in existing provider code that we must support it as an alias + // for a schema object with the given type. + map[string]*Schema{ + "list": { + Type: TypeList, + Required: true, + Elem: TypeInt, + }, + "set": { + Type: TypeSet, + Optional: true, + Elem: TypeString, + }, + "map": { + Type: TypeMap, + Optional: true, + Elem: TypeBool, + }, + }, + testResource(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "list": { + Type: cty.List(cty.Number), + Required: true, + }, + "set": { + Type: cty.Set(cty.String), + Optional: true, + }, + "map": { + Type: cty.Map(cty.Bool), + Optional: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{}, + }), + }, + "sub-resource collections": { + map[string]*Schema{ + "list": { + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{}, + }, + MinItems: 1, + MaxItems: 2, + }, + "set": { + Type: TypeSet, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{}, + }, + }, + "map": { + Type: TypeMap, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{}, + }, + }, + }, + testResource(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + // This one becomes a string attribute because helper/schema + // doesn't actually support maps of resource. The given + // "Elem" is just ignored entirely here, which is important + // because that is also true of the helper/schema logic and + // existing providers rely on this being ignored for + // correct operation. + "map": { + Type: cty.Map(cty.String), + Optional: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "list": { + Nesting: configschema.NestingList, + Block: configschema.Block{}, + MinItems: 1, + MaxItems: 2, + }, + "set": { + Nesting: configschema.NestingSet, + Block: configschema.Block{}, + MinItems: 1, // because schema is Required + }, + }, + }), + }, + "sub-resource collections minitems+optional": { + // This particular case is an odd one where the provider gives + // conflicting information about whether a sub-resource is required, + // by marking it as optional but also requiring one item. + // Historically the optional-ness "won" here, and so we must + // honor that for compatibility with providers that relied on this + // undocumented interaction. + map[string]*Schema{ + "list": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{}, + }, + MinItems: 1, + MaxItems: 1, + }, + "set": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{}, + }, + MinItems: 1, + MaxItems: 1, + }, + }, + testResource(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{}, + BlockTypes: map[string]*configschema.NestedBlock{ + "list": { + Nesting: configschema.NestingList, + Block: configschema.Block{}, + MinItems: 0, + MaxItems: 1, + }, + "set": { + Nesting: configschema.NestingSet, + Block: configschema.Block{}, + MinItems: 0, + MaxItems: 1, + }, + }, + }), + }, + "sub-resource collections minitems+computed": { + map[string]*Schema{ + "list": { + Type: TypeList, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{}, + }, + MinItems: 1, + MaxItems: 1, + }, + "set": { + Type: TypeSet, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{}, + }, + MinItems: 1, + MaxItems: 1, + }, + }, + testResource(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "list": { + Type: cty.List(cty.EmptyObject), + Computed: true, + }, + "set": { + Type: cty.Set(cty.EmptyObject), + Computed: true, + }, + }, + }), + }, + "nested attributes and blocks": { + map[string]*Schema{ + "foo": { + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "bar": { + Type: TypeList, + Required: true, + Elem: &Schema{ + Type: TypeList, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + "baz": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{}, + }, + }, + }, + }, + }, + }, + testResource(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{}, + BlockTypes: map[string]*configschema.NestedBlock{ + "foo": { + Nesting: configschema.NestingList, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bar": { + Type: cty.List(cty.List(cty.String)), + Required: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "baz": { + Nesting: configschema.NestingSet, + Block: configschema.Block{}, + }, + }, + }, + MinItems: 1, // because schema is Required + }, + }, + }), + }, + "sensitive": { + map[string]*Schema{ + "string": { + Type: TypeString, + Optional: true, + Sensitive: true, + }, + }, + testResource(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "string": { + Type: cty.String, + Optional: true, + Sensitive: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{}, + }), + }, + "conditionally required on": { + map[string]*Schema{ + "string": { + Type: TypeString, + Required: true, + DefaultFunc: func() (interface{}, error) { + return nil, nil + }, + }, + }, + testResource(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "string": { + Type: cty.String, + Required: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{}, + }), + }, + "conditionally required off": { + map[string]*Schema{ + "string": { + Type: TypeString, + Required: true, + DefaultFunc: func() (interface{}, error) { + // If we return a non-nil default then this overrides + // the "Required: true" for the purpose of building + // the core schema, so that core will ignore it not + // being set and let the provider handle it. + return "boop", nil + }, + }, + }, + testResource(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "string": { + Type: cty.String, + Optional: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{}, + }), + }, + "conditionally required error": { + map[string]*Schema{ + "string": { + Type: TypeString, + Required: true, + DefaultFunc: func() (interface{}, error) { + return nil, fmt.Errorf("placeholder error") + }, + }, + }, + testResource(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "string": { + Type: cty.String, + Optional: true, // Just so we can progress to provider-driven validation and return the error there + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{}, + }), + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + got := (&Resource{Schema: test.Schema}).CoreConfigSchema() + if !cmp.Equal(got, test.Want, equateEmpty, typeComparer) { + t.Error(cmp.Diff(got, test.Want, equateEmpty, typeComparer)) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader.go index 2a66a068..c7721bcd 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader.go @@ -3,7 +3,6 @@ package schema import ( "fmt" "strconv" - "strings" ) // FieldReaders are responsible for decoding fields out of data into @@ -42,13 +41,6 @@ func (r *FieldReadResult) ValueOrZero(s *Schema) interface{} { return s.ZeroValue() } -// SchemasForFlatmapPath tries its best to find a sequence of schemas that -// the given dot-delimited attribute path traverses through. -func SchemasForFlatmapPath(path string, schemaMap map[string]*Schema) []*Schema { - parts := strings.Split(path, ".") - return addrToSchema(parts, schemaMap) -} - // addrToSchema finds the final element schema for the given address // and the given schema. It returns all the schemas that led to the final // schema. These are in order of the address (out to in). @@ -205,7 +197,7 @@ func readListField( // Go through each count, and get the item value out of it result := make([]interface{}, countResult.Value.(int)) - for i, _ := range result { + for i := range result { is := strconv.FormatInt(int64(i), 10) addrPadded[len(addrPadded)-1] = is rawResult, err := r.ReadField(addrPadded) diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_config.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_config.go index dc2ae1af..3f1f5e8a 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_config.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_config.go @@ -7,8 +7,9 @@ import ( "strings" "sync" - "github.com/hashicorp/terraform-plugin-sdk/terraform" "github.com/mitchellh/mapstructure" + + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) // ConfigFieldReader reads fields out of an untyped map[string]string to the @@ -81,31 +82,16 @@ func (r *ConfigFieldReader) readField( k := strings.Join(address, ".") schema := schemaList[len(schemaList)-1] - // If we're getting the single element of a promoted list, then - // check to see if we have a single element we need to promote. - if address[len(address)-1] == "0" && len(schemaList) > 1 { - lastSchema := schemaList[len(schemaList)-2] - if lastSchema.Type == TypeList && lastSchema.PromoteSingle { - k := strings.Join(address[:len(address)-1], ".") - result, err := r.readPrimitive(k, schema) - if err == nil { - return result, nil - } - } - } - - if protoVersion5 { - switch schema.Type { - case TypeList, TypeSet, TypeMap, typeObject: - // Check if the value itself is unknown. - // The new protocol shims will add unknown values to this list of - // ComputedKeys. This is the only way we have to indicate that a - // collection is unknown in the config - for _, unknown := range r.Config.ComputedKeys { - if k == unknown { - log.Printf("[DEBUG] setting computed for %q from ComputedKeys", k) - return FieldReadResult{Computed: true, Exists: true}, nil - } + switch schema.Type { + case TypeList, TypeSet, TypeMap, typeObject: + // Check if the value itself is unknown. + // The new protocol shims will add unknown values to this list of + // ComputedKeys. This is the only way we have to indicate that a + // collection is unknown in the config + for _, unknown := range r.Config.ComputedKeys { + if k == unknown { + log.Printf("[DEBUG] setting computed for %q from ComputedKeys", k) + return FieldReadResult{Computed: true, Exists: true}, nil } } } @@ -114,17 +100,6 @@ func (r *ConfigFieldReader) readField( case TypeBool, TypeFloat, TypeInt, TypeString: return r.readPrimitive(k, schema) case TypeList: - // If we support promotion then we first check if we have a lone - // value that we must promote. - // a value that is alone. - if schema.PromoteSingle { - result, err := r.readPrimitive(k, schema.Elem.(*Schema)) - if err == nil && result.Exists { - result.Value = []interface{}{result.Value} - return result, nil - } - } - return readListField(&nestedConfigFieldReader{r}, address, schema) case TypeMap: return r.readMap(k, schema) diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_config_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_config_test.go new file mode 100644 index 00000000..e0496f28 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_config_test.go @@ -0,0 +1,540 @@ +package schema + +import ( + "bytes" + "fmt" + "reflect" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/hcl2shim" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/helper/hashcode" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestConfigFieldReader_impl(t *testing.T) { + var _ FieldReader = new(ConfigFieldReader) +} + +func TestConfigFieldReader(t *testing.T) { + testFieldReader(t, func(s map[string]*Schema) FieldReader { + return &ConfigFieldReader{ + Schema: s, + + Config: testConfig(t, map[string]interface{}{ + "bool": true, + "float": 3.1415, + "int": 42, + "string": "string", + + "list": []interface{}{"foo", "bar"}, + + "listInt": []interface{}{21, 42}, + + "map": map[string]interface{}{ + "foo": "bar", + "bar": "baz", + }, + "mapInt": map[string]interface{}{ + "one": "1", + "two": "2", + }, + "mapIntNestedSchema": map[string]interface{}{ + "one": "1", + "two": "2", + }, + "mapFloat": map[string]interface{}{ + "oneDotTwo": "1.2", + }, + "mapBool": map[string]interface{}{ + "True": "true", + "False": "false", + }, + + "set": []interface{}{10, 50}, + "setDeep": []interface{}{ + map[string]interface{}{ + "index": 10, + "value": "foo", + }, + map[string]interface{}{ + "index": 50, + "value": "bar", + }, + }, + }), + } + }) +} + +// This contains custom table tests for our ConfigFieldReader +func TestConfigFieldReader_custom(t *testing.T) { + schema := map[string]*Schema{ + "bool": { + Type: TypeBool, + }, + } + + cases := map[string]struct { + Addr []string + Result FieldReadResult + Config *terraform.ResourceConfig + Err bool + }{ + "basic": { + []string{"bool"}, + FieldReadResult{ + Value: true, + Exists: true, + }, + testConfig(t, map[string]interface{}{ + "bool": true, + }), + false, + }, + + "computed": { + []string{"bool"}, + FieldReadResult{ + Exists: true, + Computed: true, + }, + testConfig(t, map[string]interface{}{ + "bool": hcl2shim.UnknownVariableValue, + }), + false, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + r := &ConfigFieldReader{ + Schema: schema, + Config: tc.Config, + } + out, err := r.ReadField(tc.Addr) + if err != nil != tc.Err { + t.Fatalf("%s: err: %s", name, err) + } + if s, ok := out.Value.(*Set); ok { + // If it is a set, convert to a list so its more easily checked. + out.Value = s.List() + } + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("%s: bad: %#v", name, out) + } + }) + } +} + +func TestConfigFieldReader_DefaultHandling(t *testing.T) { + schema := map[string]*Schema{ + "strWithDefault": { + Type: TypeString, + Default: "ImADefault", + }, + "strWithDefaultFunc": { + Type: TypeString, + DefaultFunc: func() (interface{}, error) { + return "FuncDefault", nil + }, + }, + } + + cases := map[string]struct { + Addr []string + Result FieldReadResult + Config *terraform.ResourceConfig + Err bool + }{ + "gets default value when no config set": { + []string{"strWithDefault"}, + FieldReadResult{ + Value: "ImADefault", + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{}), + false, + }, + "config overrides default value": { + []string{"strWithDefault"}, + FieldReadResult{ + Value: "fromConfig", + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "strWithDefault": "fromConfig", + }), + false, + }, + "gets default from function when no config set": { + []string{"strWithDefaultFunc"}, + FieldReadResult{ + Value: "FuncDefault", + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{}), + false, + }, + "config overrides default function": { + []string{"strWithDefaultFunc"}, + FieldReadResult{ + Value: "fromConfig", + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "strWithDefaultFunc": "fromConfig", + }), + false, + }, + } + + for name, tc := range cases { + r := &ConfigFieldReader{ + Schema: schema, + Config: tc.Config, + } + out, err := r.ReadField(tc.Addr) + if err != nil != tc.Err { + t.Fatalf("%s: err: %s", name, err) + } + if s, ok := out.Value.(*Set); ok { + // If it is a set, convert to a list so its more easily checked. + out.Value = s.List() + } + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("%s: bad: %#v", name, out) + } + } +} + +func TestConfigFieldReader_ComputedMap(t *testing.T) { + schema := map[string]*Schema{ + "map": { + Type: TypeMap, + Computed: true, + }, + "listmap": { + Type: TypeMap, + Computed: true, + Elem: TypeList, + }, + "maplist": { + Type: TypeList, + Computed: true, + Elem: TypeMap, + }, + } + + cases := []struct { + Name string + Addr []string + Result FieldReadResult + Config *terraform.ResourceConfig + Err bool + }{ + { + "set, normal", + []string{"map"}, + FieldReadResult{ + Value: map[string]interface{}{ + "foo": "bar", + }, + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "map": map[string]interface{}{ + "foo": "bar", + }, + }), + false, + }, + + { + "computed element", + []string{"map"}, + FieldReadResult{ + Exists: true, + Computed: true, + }, + testConfig(t, map[string]interface{}{ + "map": map[string]interface{}{ + "foo": hcl2shim.UnknownVariableValue, + }, + }), + false, + }, + + { + "native map", + []string{"map"}, + FieldReadResult{ + Value: map[string]interface{}{ + "bar": "baz", + "baz": "bar", + }, + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "map": map[string]interface{}{ + "bar": "baz", + "baz": "bar", + }, + }), + false, + }, + + { + "map-from-list-of-maps", + []string{"maplist", "0"}, + FieldReadResult{ + Value: map[string]interface{}{ + "key": "bar", + }, + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "maplist": []interface{}{ + map[string]interface{}{ + "key": "bar", + }, + }, + }), + false, + }, + + { + "value-from-list-of-maps", + []string{"maplist", "0", "key"}, + FieldReadResult{ + Value: "bar", + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "maplist": []interface{}{ + map[string]interface{}{ + "key": "bar", + }, + }, + }), + false, + }, + + { + "list-from-map-of-lists", + []string{"listmap", "key"}, + FieldReadResult{ + Value: []interface{}{"bar"}, + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "listmap": map[string]interface{}{ + "key": []interface{}{ + "bar", + }, + }, + }), + false, + }, + + { + "value-from-map-of-lists", + []string{"listmap", "key", "0"}, + FieldReadResult{ + Value: "bar", + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "listmap": map[string]interface{}{ + "key": []interface{}{ + "bar", + }, + }, + }), + false, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + r := &ConfigFieldReader{ + Schema: schema, + Config: tc.Config, + } + out, err := r.ReadField(tc.Addr) + if err != nil != tc.Err { + t.Fatal(err) + } + if s, ok := out.Value.(*Set); ok { + // If it is a set, convert to the raw map + out.Value = s.m + if len(s.m) == 0 { + out.Value = nil + } + } + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("\nexpected: %#v\ngot: %#v", tc.Result, out) + } + }) + } +} + +func TestConfigFieldReader_ComputedSet(t *testing.T) { + schema := map[string]*Schema{ + "strSet": { + Type: TypeSet, + Elem: &Schema{Type: TypeString}, + Set: HashString, + }, + } + + cases := map[string]struct { + Addr []string + Result FieldReadResult + Config *terraform.ResourceConfig + Err bool + }{ + "set, normal": { + []string{"strSet"}, + FieldReadResult{ + Value: map[string]interface{}{ + "2356372769": "foo", + }, + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "strSet": []interface{}{"foo"}, + }), + false, + }, + + "set, computed element": { + []string{"strSet"}, + FieldReadResult{ + Value: nil, + Exists: true, + Computed: true, + }, + testConfig(t, map[string]interface{}{ + "strSet": []interface{}{hcl2shim.UnknownVariableValue}, + }), + false, + }, + } + + for name, tc := range cases { + r := &ConfigFieldReader{ + Schema: schema, + Config: tc.Config, + } + out, err := r.ReadField(tc.Addr) + if err != nil != tc.Err { + t.Fatalf("%s: err: %s", name, err) + } + if s, ok := out.Value.(*Set); ok { + // If it is a set, convert to the raw map + out.Value = s.m + if len(s.m) == 0 { + out.Value = nil + } + } + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("%s: bad: %#v", name, out) + } + } +} + +func TestConfigFieldReader_computedComplexSet(t *testing.T) { + hashfunc := func(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["vhd_uri"].(string))) + return hashcode.String(buf.String()) + } + + schema := map[string]*Schema{ + "set": { + Type: TypeSet, + Elem: &Resource{ + Schema: map[string]*Schema{ + "name": { + Type: TypeString, + Required: true, + }, + + "vhd_uri": { + Type: TypeString, + Required: true, + }, + }, + }, + Set: hashfunc, + }, + } + + cases := map[string]struct { + Addr []string + Result FieldReadResult + Config *terraform.ResourceConfig + Err bool + }{ + "set, normal": { + []string{"set"}, + FieldReadResult{ + Value: map[string]interface{}{ + "532860136": map[string]interface{}{ + "name": "myosdisk1", + "vhd_uri": "bar", + }, + }, + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "set": []interface{}{ + map[string]interface{}{ + "name": "myosdisk1", + "vhd_uri": "bar", + }, + }, + }), + false, + }, + } + + for name, tc := range cases { + r := &ConfigFieldReader{ + Schema: schema, + Config: tc.Config, + } + out, err := r.ReadField(tc.Addr) + if err != nil != tc.Err { + t.Fatalf("%s: err: %s", name, err) + } + if s, ok := out.Value.(*Set); ok { + // If it is a set, convert to the raw map + out.Value = s.m + if len(s.m) == 0 { + out.Value = nil + } + } + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("%s: bad: %#v", name, out) + } + } +} + +func testConfig(t *testing.T, raw map[string]interface{}) *terraform.ResourceConfig { + return terraform.NewResourceConfigRaw(raw) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_diff.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_diff.go index c099029a..642e7f32 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_diff.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_diff.go @@ -4,8 +4,9 @@ import ( "fmt" "strings" - "github.com/hashicorp/terraform-plugin-sdk/terraform" "github.com/mitchellh/mapstructure" + + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) // DiffFieldReader reads fields out of a diff structures. diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_diff_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_diff_test.go new file mode 100644 index 00000000..5af63bb6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_diff_test.go @@ -0,0 +1,524 @@ +package schema + +import ( + "reflect" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestDiffFieldReader_impl(t *testing.T) { + var _ FieldReader = new(DiffFieldReader) +} + +func TestDiffFieldReader_NestedSetUpdate(t *testing.T) { + hashFn := func(a interface{}) int { + m := a.(map[string]interface{}) + return m["val"].(int) + } + + schema := map[string]*Schema{ + "list_of_sets_1": { + Type: TypeList, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_set": { + Type: TypeSet, + Elem: &Resource{ + Schema: map[string]*Schema{ + "val": { + Type: TypeInt, + }, + }, + }, + Set: hashFn, + }, + }, + }, + }, + "list_of_sets_2": { + Type: TypeList, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_set": { + Type: TypeSet, + Elem: &Resource{ + Schema: map[string]*Schema{ + "val": { + Type: TypeInt, + }, + }, + }, + Set: hashFn, + }, + }, + }, + }, + } + + r := &DiffFieldReader{ + Schema: schema, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "list_of_sets_1.0.nested_set.1.val": { + Old: "1", + New: "0", + NewRemoved: true, + }, + "list_of_sets_1.0.nested_set.2.val": { + New: "2", + }, + }, + }, + } + + r.Source = &MultiLevelFieldReader{ + Readers: map[string]FieldReader{ + "diff": r, + "set": &MapFieldReader{Schema: schema}, + "state": &MapFieldReader{ + Map: &BasicMapReader{ + "list_of_sets_1.#": "1", + "list_of_sets_1.0.nested_set.#": "1", + "list_of_sets_1.0.nested_set.1.val": "1", + "list_of_sets_2.#": "1", + "list_of_sets_2.0.nested_set.#": "1", + "list_of_sets_2.0.nested_set.1.val": "1", + }, + Schema: schema, + }, + }, + Levels: []string{"state", "config"}, + } + + out, err := r.ReadField([]string{"list_of_sets_2"}) + if err != nil { + t.Fatalf("err: %v", err) + } + + s := &Set{F: hashFn} + s.Add(map[string]interface{}{"val": 1}) + expected := s.List() + + l := out.Value.([]interface{}) + i := l[0].(map[string]interface{}) + actual := i["nested_set"].(*Set).List() + + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("bad: NestedSetUpdate\n\nexpected: %#v\n\ngot: %#v\n\n", expected, actual) + } +} + +// https://github.com/hashicorp/terraform-plugin-sdk/issues/914 +func TestDiffFieldReader_MapHandling(t *testing.T) { + schema := map[string]*Schema{ + "tags": { + Type: TypeMap, + }, + } + r := &DiffFieldReader{ + Schema: schema, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "tags.%": { + Old: "1", + New: "2", + }, + "tags.baz": { + Old: "", + New: "qux", + }, + }, + }, + Source: &MapFieldReader{ + Schema: schema, + Map: BasicMapReader(map[string]string{ + "tags.%": "1", + "tags.foo": "bar", + }), + }, + } + + result, err := r.ReadField([]string{"tags"}) + if err != nil { + t.Fatalf("ReadField failed: %#v", err) + } + + expected := map[string]interface{}{ + "foo": "bar", + "baz": "qux", + } + + if !reflect.DeepEqual(expected, result.Value) { + t.Fatalf("bad: DiffHandling\n\nexpected: %#v\n\ngot: %#v\n\n", expected, result.Value) + } +} + +func TestDiffFieldReader_extra(t *testing.T) { + schema := map[string]*Schema{ + "stringComputed": {Type: TypeString}, + + "listMap": { + Type: TypeList, + Elem: &Schema{ + Type: TypeMap, + }, + }, + + "mapRemove": {Type: TypeMap}, + + "setChange": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": { + Type: TypeInt, + Required: true, + }, + + "value": { + Type: TypeString, + Required: true, + }, + }, + }, + Set: func(a interface{}) int { + m := a.(map[string]interface{}) + return m["index"].(int) + }, + }, + + "setEmpty": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": { + Type: TypeInt, + Required: true, + }, + + "value": { + Type: TypeString, + Required: true, + }, + }, + }, + Set: func(a interface{}) int { + m := a.(map[string]interface{}) + return m["index"].(int) + }, + }, + } + + r := &DiffFieldReader{ + Schema: schema, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "stringComputed": { + Old: "foo", + New: "bar", + NewComputed: true, + }, + + "listMap.0.bar": { + NewRemoved: true, + }, + + "mapRemove.bar": { + NewRemoved: true, + }, + + "setChange.10.value": { + Old: "50", + New: "80", + }, + + "setEmpty.#": { + Old: "2", + New: "0", + }, + }, + }, + + Source: &MapFieldReader{ + Schema: schema, + Map: BasicMapReader(map[string]string{ + "listMap.#": "2", + "listMap.0.foo": "bar", + "listMap.0.bar": "baz", + "listMap.1.baz": "baz", + + "mapRemove.foo": "bar", + "mapRemove.bar": "bar", + + "setChange.#": "1", + "setChange.10.index": "10", + "setChange.10.value": "50", + + "setEmpty.#": "2", + "setEmpty.10.index": "10", + "setEmpty.10.value": "50", + "setEmpty.20.index": "20", + "setEmpty.20.value": "50", + }), + }, + } + + cases := map[string]struct { + Addr []string + Result FieldReadResult + Err bool + }{ + "stringComputed": { + []string{"stringComputed"}, + FieldReadResult{ + Value: "", + Exists: true, + Computed: true, + }, + false, + }, + + "listMapRemoval": { + []string{"listMap"}, + FieldReadResult{ + Value: []interface{}{ + map[string]interface{}{ + "foo": "bar", + }, + map[string]interface{}{ + "baz": "baz", + }, + }, + Exists: true, + }, + false, + }, + + "mapRemove": { + []string{"mapRemove"}, + FieldReadResult{ + Value: map[string]interface{}{ + "foo": "bar", + }, + Exists: true, + Computed: false, + }, + false, + }, + + "setChange": { + []string{"setChange"}, + FieldReadResult{ + Value: []interface{}{ + map[string]interface{}{ + "index": 10, + "value": "80", + }, + }, + Exists: true, + }, + false, + }, + + "setEmpty": { + []string{"setEmpty"}, + FieldReadResult{ + Value: []interface{}{}, + Exists: true, + }, + false, + }, + } + + for name, tc := range cases { + out, err := r.ReadField(tc.Addr) + if err != nil != tc.Err { + t.Fatalf("%s: err: %s", name, err) + } + if s, ok := out.Value.(*Set); ok { + // If it is a set, convert to a list so its more easily checked. + out.Value = s.List() + } + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("%s: bad: %#v", name, out) + } + } +} + +func TestDiffFieldReader(t *testing.T) { + testFieldReader(t, func(s map[string]*Schema) FieldReader { + return &DiffFieldReader{ + Schema: s, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "bool": { + Old: "", + New: "true", + }, + + "int": { + Old: "", + New: "42", + }, + + "float": { + Old: "", + New: "3.1415", + }, + + "string": { + Old: "", + New: "string", + }, + + "stringComputed": { + Old: "foo", + New: "bar", + NewComputed: true, + }, + + "list.#": { + Old: "0", + New: "2", + }, + + "list.0": { + Old: "", + New: "foo", + }, + + "list.1": { + Old: "", + New: "bar", + }, + + "listInt.#": { + Old: "0", + New: "2", + }, + + "listInt.0": { + Old: "", + New: "21", + }, + + "listInt.1": { + Old: "", + New: "42", + }, + + "map.foo": { + Old: "", + New: "bar", + }, + + "map.bar": { + Old: "", + New: "baz", + }, + + "mapInt.%": { + Old: "", + New: "2", + }, + "mapInt.one": { + Old: "", + New: "1", + }, + "mapInt.two": { + Old: "", + New: "2", + }, + + "mapIntNestedSchema.%": { + Old: "", + New: "2", + }, + "mapIntNestedSchema.one": { + Old: "", + New: "1", + }, + "mapIntNestedSchema.two": { + Old: "", + New: "2", + }, + + "mapFloat.%": { + Old: "", + New: "1", + }, + "mapFloat.oneDotTwo": { + Old: "", + New: "1.2", + }, + + "mapBool.%": { + Old: "", + New: "2", + }, + "mapBool.True": { + Old: "", + New: "true", + }, + "mapBool.False": { + Old: "", + New: "false", + }, + + "set.#": { + Old: "0", + New: "2", + }, + + "set.10": { + Old: "", + New: "10", + }, + + "set.50": { + Old: "", + New: "50", + }, + + "setDeep.#": { + Old: "0", + New: "2", + }, + + "setDeep.10.index": { + Old: "", + New: "10", + }, + + "setDeep.10.value": { + Old: "", + New: "foo", + }, + + "setDeep.50.index": { + Old: "", + New: "50", + }, + + "setDeep.50.value": { + Old: "", + New: "bar", + }, + }, + }, + + Source: &MapFieldReader{ + Schema: s, + Map: BasicMapReader(map[string]string{ + "listMap.#": "2", + "listMap.0.foo": "bar", + "listMap.0.bar": "baz", + "listMap.1.baz": "baz", + }), + }, + } + }) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_map.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_map.go index 53f73b71..092dd7f6 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_map.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_map.go @@ -200,36 +200,3 @@ func (r BasicMapReader) Range(f func(string, string) bool) bool { return true } - -// MultiMapReader reads over multiple maps, preferring keys that are -// founder earlier (lower number index) vs. later (higher number index) -type MultiMapReader []map[string]string - -func (r MultiMapReader) Access(k string) (string, bool) { - for _, m := range r { - if v, ok := m[k]; ok { - return v, ok - } - } - - return "", false -} - -func (r MultiMapReader) Range(f func(string, string) bool) bool { - done := make(map[string]struct{}) - for _, m := range r { - for k, v := range m { - if _, ok := done[k]; ok { - continue - } - - if cont := f(k, v); !cont { - return false - } - - done[k] = struct{}{} - } - } - - return true -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_map_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_map_test.go new file mode 100644 index 00000000..a0fff5cb --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_map_test.go @@ -0,0 +1,123 @@ +package schema + +import ( + "reflect" + "testing" +) + +func TestMapFieldReader_impl(t *testing.T) { + var _ FieldReader = new(MapFieldReader) +} + +func TestMapFieldReader(t *testing.T) { + testFieldReader(t, func(s map[string]*Schema) FieldReader { + return &MapFieldReader{ + Schema: s, + + Map: BasicMapReader(map[string]string{ + "bool": "true", + "int": "42", + "float": "3.1415", + "string": "string", + + "list.#": "2", + "list.0": "foo", + "list.1": "bar", + + "listInt.#": "2", + "listInt.0": "21", + "listInt.1": "42", + + "map.%": "2", + "map.foo": "bar", + "map.bar": "baz", + + "set.#": "2", + "set.10": "10", + "set.50": "50", + + "setDeep.#": "2", + "setDeep.10.index": "10", + "setDeep.10.value": "foo", + "setDeep.50.index": "50", + "setDeep.50.value": "bar", + + "mapInt.%": "2", + "mapInt.one": "1", + "mapInt.two": "2", + + "mapIntNestedSchema.%": "2", + "mapIntNestedSchema.one": "1", + "mapIntNestedSchema.two": "2", + + "mapFloat.%": "1", + "mapFloat.oneDotTwo": "1.2", + + "mapBool.%": "2", + "mapBool.True": "true", + "mapBool.False": "false", + }), + } + }) +} + +func TestMapFieldReader_extra(t *testing.T) { + r := &MapFieldReader{ + Schema: map[string]*Schema{ + "mapDel": {Type: TypeMap}, + "mapEmpty": {Type: TypeMap}, + }, + + Map: BasicMapReader(map[string]string{ + "mapDel": "", + + "mapEmpty.%": "0", + }), + } + + cases := map[string]struct { + Addr []string + Out interface{} + OutOk bool + OutComputed bool + OutErr bool + }{ + "mapDel": { + []string{"mapDel"}, + map[string]interface{}{}, + true, + false, + false, + }, + + "mapEmpty": { + []string{"mapEmpty"}, + map[string]interface{}{}, + true, + false, + false, + }, + } + + for name, tc := range cases { + out, err := r.ReadField(tc.Addr) + if err != nil != tc.OutErr { + t.Fatalf("%s: err: %s", name, err) + } + if out.Computed != tc.OutComputed { + t.Fatalf("%s: err: %#v", name, out.Computed) + } + + if s, ok := out.Value.(*Set); ok { + // If it is a set, convert to a list so its more easily checked. + out.Value = s.List() + } + + if !reflect.DeepEqual(out.Value, tc.Out) { + t.Fatalf("%s: out: %#v", name, out.Value) + } + if out.Exists != tc.OutOk { + t.Fatalf("%s: outOk: %#v", name, out.Exists) + } + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_multi_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_multi_test.go new file mode 100644 index 00000000..a4239c3a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_multi_test.go @@ -0,0 +1,270 @@ +package schema + +import ( + "reflect" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestMultiLevelFieldReaderReadFieldExact(t *testing.T) { + cases := map[string]struct { + Addr []string + Readers []FieldReader + Level string + Result FieldReadResult + }{ + "specific": { + Addr: []string{"foo"}, + + Readers: []FieldReader{ + &MapFieldReader{ + Schema: map[string]*Schema{ + "foo": {Type: TypeString}, + }, + Map: BasicMapReader(map[string]string{ + "foo": "bar", + }), + }, + &MapFieldReader{ + Schema: map[string]*Schema{ + "foo": {Type: TypeString}, + }, + Map: BasicMapReader(map[string]string{ + "foo": "baz", + }), + }, + &MapFieldReader{ + Schema: map[string]*Schema{ + "foo": {Type: TypeString}, + }, + Map: BasicMapReader(map[string]string{}), + }, + }, + + Level: "1", + Result: FieldReadResult{ + Value: "baz", + Exists: true, + }, + }, + } + + for name, tc := range cases { + readers := make(map[string]FieldReader) + levels := make([]string, len(tc.Readers)) + for i, r := range tc.Readers { + is := strconv.FormatInt(int64(i), 10) + readers[is] = r + levels[i] = is + } + + r := &MultiLevelFieldReader{ + Readers: readers, + Levels: levels, + } + + out, err := r.ReadFieldExact(tc.Addr, tc.Level) + if err != nil { + t.Fatalf("%s: err: %s", name, err) + } + + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("%s: bad: %#v", name, out) + } + } +} + +func TestMultiLevelFieldReaderReadFieldMerge(t *testing.T) { + cases := map[string]struct { + Addr []string + Readers []FieldReader + Result FieldReadResult + }{ + "stringInDiff": { + Addr: []string{"availability_zone"}, + + Readers: []FieldReader{ + &DiffFieldReader{ + Schema: map[string]*Schema{ + "availability_zone": {Type: TypeString}, + }, + + Source: &MapFieldReader{ + Schema: map[string]*Schema{ + "availability_zone": {Type: TypeString}, + }, + Map: BasicMapReader(map[string]string{ + "availability_zone": "foo", + }), + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "foo", + New: "bar", + RequiresNew: true, + }, + }, + }, + }, + }, + + Result: FieldReadResult{ + Value: "bar", + Exists: true, + }, + }, + + "lastLevelComputed": { + Addr: []string{"availability_zone"}, + + Readers: []FieldReader{ + &MapFieldReader{ + Schema: map[string]*Schema{ + "availability_zone": {Type: TypeString}, + }, + + Map: BasicMapReader(map[string]string{ + "availability_zone": "foo", + }), + }, + + &DiffFieldReader{ + Schema: map[string]*Schema{ + "availability_zone": {Type: TypeString}, + }, + + Source: &MapFieldReader{ + Schema: map[string]*Schema{ + "availability_zone": {Type: TypeString}, + }, + + Map: BasicMapReader(map[string]string{ + "availability_zone": "foo", + }), + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "foo", + New: "bar", + NewComputed: true, + }, + }, + }, + }, + }, + + Result: FieldReadResult{ + Value: "", + Exists: true, + Computed: true, + }, + }, + + "list of maps with removal in diff": { + Addr: []string{"config_vars"}, + + Readers: []FieldReader{ + &DiffFieldReader{ + Schema: map[string]*Schema{ + "config_vars": { + Type: TypeList, + Elem: &Schema{Type: TypeMap}, + }, + }, + + Source: &MapFieldReader{ + Schema: map[string]*Schema{ + "config_vars": { + Type: TypeList, + Elem: &Schema{Type: TypeMap}, + }, + }, + + Map: BasicMapReader(map[string]string{ + "config_vars.#": "2", + "config_vars.0.foo": "bar", + "config_vars.0.bar": "bar", + "config_vars.1.bar": "baz", + }), + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.0.bar": { + NewRemoved: true, + }, + }, + }, + }, + }, + + Result: FieldReadResult{ + Value: []interface{}{ + map[string]interface{}{ + "foo": "bar", + }, + map[string]interface{}{ + "bar": "baz", + }, + }, + Exists: true, + }, + }, + + "first level only": { + Addr: []string{"foo"}, + + Readers: []FieldReader{ + &MapFieldReader{ + Schema: map[string]*Schema{ + "foo": {Type: TypeString}, + }, + Map: BasicMapReader(map[string]string{ + "foo": "bar", + }), + }, + &MapFieldReader{ + Schema: map[string]*Schema{ + "foo": {Type: TypeString}, + }, + Map: BasicMapReader(map[string]string{}), + }, + }, + + Result: FieldReadResult{ + Value: "bar", + Exists: true, + }, + }, + } + + for name, tc := range cases { + readers := make(map[string]FieldReader) + levels := make([]string, len(tc.Readers)) + for i, r := range tc.Readers { + is := strconv.FormatInt(int64(i), 10) + readers[is] = r + levels[i] = is + } + + r := &MultiLevelFieldReader{ + Readers: readers, + Levels: levels, + } + + out, err := r.ReadFieldMerge(tc.Addr, levels[len(levels)-1]) + if err != nil { + t.Fatalf("%s: err: %s", name, err) + } + + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("%s: bad: %#v", name, out) + } + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_test.go new file mode 100644 index 00000000..cbaf0fd1 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_reader_test.go @@ -0,0 +1,471 @@ +package schema + +import ( + "reflect" + "testing" +) + +func TestAddrToSchema(t *testing.T) { + cases := map[string]struct { + Addr []string + Schema map[string]*Schema + Result []ValueType + }{ + "full object": { + []string{}, + map[string]*Schema{ + "list": { + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + }, + }, + []ValueType{typeObject}, + }, + + "list": { + []string{"list"}, + map[string]*Schema{ + "list": { + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + }, + }, + []ValueType{TypeList}, + }, + + "list.#": { + []string{"list", "#"}, + map[string]*Schema{ + "list": { + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + }, + }, + []ValueType{TypeList, TypeInt}, + }, + + "list.0": { + []string{"list", "0"}, + map[string]*Schema{ + "list": { + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + }, + }, + []ValueType{TypeList, TypeInt}, + }, + + "list.0 with resource": { + []string{"list", "0"}, + map[string]*Schema{ + "list": { + Type: TypeList, + Elem: &Resource{ + Schema: map[string]*Schema{ + "field": {Type: TypeString}, + }, + }, + }, + }, + []ValueType{TypeList, typeObject}, + }, + + "list.0.field": { + []string{"list", "0", "field"}, + map[string]*Schema{ + "list": { + Type: TypeList, + Elem: &Resource{ + Schema: map[string]*Schema{ + "field": {Type: TypeString}, + }, + }, + }, + }, + []ValueType{TypeList, typeObject, TypeString}, + }, + + "set": { + []string{"set"}, + map[string]*Schema{ + "set": { + Type: TypeSet, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + []ValueType{TypeSet}, + }, + + "set.#": { + []string{"set", "#"}, + map[string]*Schema{ + "set": { + Type: TypeSet, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + []ValueType{TypeSet, TypeInt}, + }, + + "set.0": { + []string{"set", "0"}, + map[string]*Schema{ + "set": { + Type: TypeSet, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + []ValueType{TypeSet, TypeInt}, + }, + + "set.0 with resource": { + []string{"set", "0"}, + map[string]*Schema{ + "set": { + Type: TypeSet, + Elem: &Resource{ + Schema: map[string]*Schema{ + "field": {Type: TypeString}, + }, + }, + }, + }, + []ValueType{TypeSet, typeObject}, + }, + + "mapElem": { + []string{"map", "foo"}, + map[string]*Schema{ + "map": {Type: TypeMap}, + }, + []ValueType{TypeMap, TypeString}, + }, + + "setDeep": { + []string{"set", "50", "index"}, + map[string]*Schema{ + "set": { + Type: TypeSet, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": {Type: TypeInt}, + "value": {Type: TypeString}, + }, + }, + Set: func(a interface{}) int { + return a.(map[string]interface{})["index"].(int) + }, + }, + }, + []ValueType{TypeSet, typeObject, TypeInt}, + }, + } + + for name, tc := range cases { + result := addrToSchema(tc.Addr, tc.Schema) + types := make([]ValueType, len(result)) + for i, v := range result { + types[i] = v.Type + } + + if !reflect.DeepEqual(types, tc.Result) { + t.Fatalf("%s: %#v", name, types) + } + } +} + +// testFieldReader is a helper that should be used to verify that +// a FieldReader behaves properly in all the common cases. +func testFieldReader(t *testing.T, f func(map[string]*Schema) FieldReader) { + schema := map[string]*Schema{ + // Primitives + "bool": {Type: TypeBool}, + "float": {Type: TypeFloat}, + "int": {Type: TypeInt}, + "string": {Type: TypeString}, + + // Lists + "list": { + Type: TypeList, + Elem: &Schema{Type: TypeString}, + }, + "listInt": { + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + }, + "listMap": { + Type: TypeList, + Elem: &Schema{ + Type: TypeMap, + }, + }, + + // Maps + "map": {Type: TypeMap}, + "mapInt": { + Type: TypeMap, + Elem: TypeInt, + }, + + // This is used to verify that the type of a Map can be specified using the + // same syntax as for lists (as a nested *Schema passed to Elem) + "mapIntNestedSchema": { + Type: TypeMap, + Elem: &Schema{Type: TypeInt}, + }, + "mapFloat": { + Type: TypeMap, + Elem: TypeFloat, + }, + "mapBool": { + Type: TypeMap, + Elem: TypeBool, + }, + + // Sets + "set": { + Type: TypeSet, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + "setDeep": { + Type: TypeSet, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": {Type: TypeInt}, + "value": {Type: TypeString}, + }, + }, + Set: func(a interface{}) int { + return a.(map[string]interface{})["index"].(int) + }, + }, + "setEmpty": { + Type: TypeSet, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + } + + cases := map[string]struct { + Addr []string + Result FieldReadResult + Err bool + }{ + "noexist": { + []string{"boolNOPE"}, + FieldReadResult{ + Value: nil, + Exists: false, + Computed: false, + }, + false, + }, + + "bool": { + []string{"bool"}, + FieldReadResult{ + Value: true, + Exists: true, + Computed: false, + }, + false, + }, + + "float": { + []string{"float"}, + FieldReadResult{ + Value: 3.1415, + Exists: true, + Computed: false, + }, + false, + }, + + "int": { + []string{"int"}, + FieldReadResult{ + Value: 42, + Exists: true, + Computed: false, + }, + false, + }, + + "string": { + []string{"string"}, + FieldReadResult{ + Value: "string", + Exists: true, + Computed: false, + }, + false, + }, + + "list": { + []string{"list"}, + FieldReadResult{ + Value: []interface{}{ + "foo", + "bar", + }, + Exists: true, + Computed: false, + }, + false, + }, + + "listInt": { + []string{"listInt"}, + FieldReadResult{ + Value: []interface{}{ + 21, + 42, + }, + Exists: true, + Computed: false, + }, + false, + }, + + "map": { + []string{"map"}, + FieldReadResult{ + Value: map[string]interface{}{ + "foo": "bar", + "bar": "baz", + }, + Exists: true, + Computed: false, + }, + false, + }, + + "mapInt": { + []string{"mapInt"}, + FieldReadResult{ + Value: map[string]interface{}{ + "one": 1, + "two": 2, + }, + Exists: true, + Computed: false, + }, + false, + }, + + "mapIntNestedSchema": { + []string{"mapIntNestedSchema"}, + FieldReadResult{ + Value: map[string]interface{}{ + "one": 1, + "two": 2, + }, + Exists: true, + Computed: false, + }, + false, + }, + + "mapFloat": { + []string{"mapFloat"}, + FieldReadResult{ + Value: map[string]interface{}{ + "oneDotTwo": 1.2, + }, + Exists: true, + Computed: false, + }, + false, + }, + + "mapBool": { + []string{"mapBool"}, + FieldReadResult{ + Value: map[string]interface{}{ + "True": true, + "False": false, + }, + Exists: true, + Computed: false, + }, + false, + }, + + "mapelem": { + []string{"map", "foo"}, + FieldReadResult{ + Value: "bar", + Exists: true, + Computed: false, + }, + false, + }, + + "set": { + []string{"set"}, + FieldReadResult{ + Value: []interface{}{10, 50}, + Exists: true, + Computed: false, + }, + false, + }, + + "setDeep": { + []string{"setDeep"}, + FieldReadResult{ + Value: []interface{}{ + map[string]interface{}{ + "index": 10, + "value": "foo", + }, + map[string]interface{}{ + "index": 50, + "value": "bar", + }, + }, + Exists: true, + Computed: false, + }, + false, + }, + + "setEmpty": { + []string{"setEmpty"}, + FieldReadResult{ + Value: []interface{}{}, + Exists: false, + }, + false, + }, + } + + for name, tc := range cases { + r := f(schema) + out, err := r.ReadField(tc.Addr) + if err != nil != tc.Err { + t.Fatalf("%s: err: %s", name, err) + } + if s, ok := out.Value.(*Set); ok { + // If it is a set, convert to a list so its more easily checked. + out.Value = s.List() + } + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("%s: bad: %#v", name, out) + } + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_writer_map.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_writer_map.go index c09358b1..85d05be4 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_writer_map.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_writer_map.go @@ -146,7 +146,7 @@ func (w *MapFieldWriter) setList( } } if err != nil { - for i, _ := range vs { + for i := range vs { is := strconv.FormatInt(int64(i), 10) setElement(is, nil) } @@ -227,7 +227,7 @@ func (w *MapFieldWriter) setObject( } } if err != nil { - for k1, _ := range v { + for k1 := range v { w.set(append(addrCopy, k1), nil) } } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_writer_map_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_writer_map_test.go new file mode 100644 index 00000000..5692e9cf --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/field_writer_map_test.go @@ -0,0 +1,556 @@ +package schema + +import ( + "reflect" + "testing" +) + +func TestMapFieldWriter_impl(t *testing.T) { + var _ FieldWriter = new(MapFieldWriter) +} + +func TestMapFieldWriter(t *testing.T) { + schema := map[string]*Schema{ + "bool": {Type: TypeBool}, + "int": {Type: TypeInt}, + "string": {Type: TypeString}, + "list": { + Type: TypeList, + Elem: &Schema{Type: TypeString}, + }, + "listInt": { + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + }, + "listResource": { + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "value": { + Type: TypeInt, + Optional: true, + }, + }, + }, + }, + "map": {Type: TypeMap}, + "set": { + Type: TypeSet, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + "setDeep": { + Type: TypeSet, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": {Type: TypeInt}, + "value": {Type: TypeString}, + }, + }, + Set: func(a interface{}) int { + return a.(map[string]interface{})["index"].(int) + }, + }, + } + + cases := map[string]struct { + Addr []string + Value interface{} + Err bool + Out map[string]string + }{ + "noexist": { + []string{"noexist"}, + 42, + true, + map[string]string{}, + }, + + "bool": { + []string{"bool"}, + false, + false, + map[string]string{ + "bool": "false", + }, + }, + + "int": { + []string{"int"}, + 42, + false, + map[string]string{ + "int": "42", + }, + }, + + "bigint": { + []string{"int"}, + 7227701560655103598, + false, + map[string]string{ + "int": "7227701560655103598", + }, + }, + + "string": { + []string{"string"}, + "42", + false, + map[string]string{ + "string": "42", + }, + }, + + "string nil": { + []string{"string"}, + nil, + false, + map[string]string{ + "string": "", + }, + }, + + "list of resources": { + []string{"listResource"}, + []interface{}{ + map[string]interface{}{ + "value": 80, + }, + }, + false, + map[string]string{ + "listResource.#": "1", + "listResource.0.value": "80", + }, + }, + + "list of resources empty": { + []string{"listResource"}, + []interface{}{}, + false, + map[string]string{ + "listResource.#": "0", + }, + }, + + "list of resources nil": { + []string{"listResource"}, + nil, + false, + map[string]string{ + "listResource.#": "0", + }, + }, + + "list of strings": { + []string{"list"}, + []interface{}{"foo", "bar"}, + false, + map[string]string{ + "list.#": "2", + "list.0": "foo", + "list.1": "bar", + }, + }, + + "list element": { + []string{"list", "0"}, + "string", + true, + map[string]string{}, + }, + + "map": { + []string{"map"}, + map[string]interface{}{"foo": "bar"}, + false, + map[string]string{ + "map.%": "1", + "map.foo": "bar", + }, + }, + + "map delete": { + []string{"map"}, + nil, + false, + map[string]string{ + "map": "", + }, + }, + + "map element": { + []string{"map", "foo"}, + "bar", + true, + map[string]string{}, + }, + + "set": { + []string{"set"}, + []interface{}{1, 2, 5}, + false, + map[string]string{ + "set.#": "3", + "set.1": "1", + "set.2": "2", + "set.5": "5", + }, + }, + + "set nil": { + []string{"set"}, + nil, + false, + map[string]string{ + "set.#": "0", + }, + }, + + "set typed nil": { + []string{"set"}, + func() *Set { return nil }(), + false, + map[string]string{ + "set.#": "0", + }, + }, + + "set resource": { + []string{"setDeep"}, + []interface{}{ + map[string]interface{}{ + "index": 10, + "value": "foo", + }, + map[string]interface{}{ + "index": 50, + "value": "bar", + }, + }, + false, + map[string]string{ + "setDeep.#": "2", + "setDeep.10.index": "10", + "setDeep.10.value": "foo", + "setDeep.50.index": "50", + "setDeep.50.value": "bar", + }, + }, + + "set element": { + []string{"set", "5"}, + 5, + true, + map[string]string{}, + }, + + "full object": { + nil, + map[string]interface{}{ + "string": "foo", + "list": []interface{}{"foo", "bar"}, + }, + false, + map[string]string{ + "string": "foo", + "list.#": "2", + "list.0": "foo", + "list.1": "bar", + }, + }, + } + + for name, tc := range cases { + w := &MapFieldWriter{Schema: schema} + err := w.WriteField(tc.Addr, tc.Value) + if err != nil != tc.Err { + t.Fatalf("%s: err: %s", name, err) + } + + actual := w.Map() + if !reflect.DeepEqual(actual, tc.Out) { + t.Fatalf("%s: bad: %#v", name, actual) + } + } +} + +func TestMapFieldWriterCleanSet(t *testing.T) { + schema := map[string]*Schema{ + "setDeep": { + Type: TypeSet, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": {Type: TypeInt}, + "value": {Type: TypeString}, + }, + }, + Set: func(a interface{}) int { + return a.(map[string]interface{})["index"].(int) + }, + }, + } + + values := []struct { + Addr []string + Value interface{} + Out map[string]string + }{ + { + []string{"setDeep"}, + []interface{}{ + map[string]interface{}{ + "index": 10, + "value": "foo", + }, + map[string]interface{}{ + "index": 50, + "value": "bar", + }, + }, + map[string]string{ + "setDeep.#": "2", + "setDeep.10.index": "10", + "setDeep.10.value": "foo", + "setDeep.50.index": "50", + "setDeep.50.value": "bar", + }, + }, + { + []string{"setDeep"}, + []interface{}{ + map[string]interface{}{ + "index": 20, + "value": "baz", + }, + map[string]interface{}{ + "index": 60, + "value": "qux", + }, + }, + map[string]string{ + "setDeep.#": "2", + "setDeep.20.index": "20", + "setDeep.20.value": "baz", + "setDeep.60.index": "60", + "setDeep.60.value": "qux", + }, + }, + { + []string{"setDeep"}, + []interface{}{ + map[string]interface{}{ + "index": 30, + "value": "one", + }, + map[string]interface{}{ + "index": 70, + "value": "two", + }, + }, + map[string]string{ + "setDeep.#": "2", + "setDeep.30.index": "30", + "setDeep.30.value": "one", + "setDeep.70.index": "70", + "setDeep.70.value": "two", + }, + }, + } + + w := &MapFieldWriter{Schema: schema} + + for n, tc := range values { + err := w.WriteField(tc.Addr, tc.Value) + if err != nil { + t.Fatalf("%d: err: %s", n, err) + } + + actual := w.Map() + if !reflect.DeepEqual(actual, tc.Out) { + t.Fatalf("%d: bad: %#v", n, actual) + } + } +} + +func TestMapFieldWriterCleanList(t *testing.T) { + schema := map[string]*Schema{ + "listDeep": { + Type: TypeList, + Elem: &Resource{ + Schema: map[string]*Schema{ + "thing1": {Type: TypeString}, + "thing2": {Type: TypeString}, + }, + }, + }, + } + + values := []struct { + Addr []string + Value interface{} + Out map[string]string + }{ + { + // Base list + []string{"listDeep"}, + []interface{}{ + map[string]interface{}{ + "thing1": "a", + "thing2": "b", + }, + map[string]interface{}{ + "thing1": "c", + "thing2": "d", + }, + map[string]interface{}{ + "thing1": "e", + "thing2": "f", + }, + map[string]interface{}{ + "thing1": "g", + "thing2": "h", + }, + }, + map[string]string{ + "listDeep.#": "4", + "listDeep.0.thing1": "a", + "listDeep.0.thing2": "b", + "listDeep.1.thing1": "c", + "listDeep.1.thing2": "d", + "listDeep.2.thing1": "e", + "listDeep.2.thing2": "f", + "listDeep.3.thing1": "g", + "listDeep.3.thing2": "h", + }, + }, + { + // Remove an element + []string{"listDeep"}, + []interface{}{ + map[string]interface{}{ + "thing1": "a", + "thing2": "b", + }, + map[string]interface{}{ + "thing1": "c", + "thing2": "d", + }, + map[string]interface{}{ + "thing1": "e", + "thing2": "f", + }, + }, + map[string]string{ + "listDeep.#": "3", + "listDeep.0.thing1": "a", + "listDeep.0.thing2": "b", + "listDeep.1.thing1": "c", + "listDeep.1.thing2": "d", + "listDeep.2.thing1": "e", + "listDeep.2.thing2": "f", + }, + }, + { + // Rewrite with missing keys. This should normally not be necessary, as + // hopefully the writers are writing zero values as necessary, but for + // brevity we want to make sure that what exists in the writer is exactly + // what the last write looked like coming from the provider. + []string{"listDeep"}, + []interface{}{ + map[string]interface{}{ + "thing1": "a", + }, + map[string]interface{}{ + "thing1": "c", + }, + map[string]interface{}{ + "thing1": "e", + }, + }, + map[string]string{ + "listDeep.#": "3", + "listDeep.0.thing1": "a", + "listDeep.1.thing1": "c", + "listDeep.2.thing1": "e", + }, + }, + } + + w := &MapFieldWriter{Schema: schema} + + for n, tc := range values { + err := w.WriteField(tc.Addr, tc.Value) + if err != nil { + t.Fatalf("%d: err: %s", n, err) + } + + actual := w.Map() + if !reflect.DeepEqual(actual, tc.Out) { + t.Fatalf("%d: bad: %#v", n, actual) + } + } +} + +func TestMapFieldWriterCleanMap(t *testing.T) { + schema := map[string]*Schema{ + "map": { + Type: TypeMap, + }, + } + + values := []struct { + Value interface{} + Out map[string]string + }{ + { + // Base map + map[string]interface{}{ + "thing1": "a", + "thing2": "b", + "thing3": "c", + "thing4": "d", + }, + map[string]string{ + "map.%": "4", + "map.thing1": "a", + "map.thing2": "b", + "map.thing3": "c", + "map.thing4": "d", + }, + }, + { + // Base map + map[string]interface{}{ + "thing1": "a", + "thing2": "b", + "thing4": "d", + }, + map[string]string{ + "map.%": "3", + "map.thing1": "a", + "map.thing2": "b", + "map.thing4": "d", + }, + }, + } + + w := &MapFieldWriter{Schema: schema} + + for n, tc := range values { + err := w.WriteField([]string{"map"}, tc.Value) + if err != nil { + t.Fatalf("%d: err: %s", n, err) + } + + actual := w.Map() + if !reflect.DeepEqual(actual, tc.Out) { + t.Fatalf("%d: bad: %#v", n, actual) + } + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/grpc_provider.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/grpc_provider.go new file mode 100644 index 00000000..49a541b4 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/grpc_provider.go @@ -0,0 +1,1464 @@ +package schema + +import ( + "context" + "encoding/json" + "fmt" + "log" + "strconv" + "sync" + + "github.com/hashicorp/go-cty/cty" + ctyconvert "github.com/hashicorp/go-cty/cty/convert" + "github.com/hashicorp/go-cty/cty/msgpack" + + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/hcl2shim" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/plans/objchange" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugin/convert" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +const newExtraKey = "_new_extra_shim" + +func NewGRPCProviderServer(p *Provider) *GRPCProviderServer { + return &GRPCProviderServer{ + provider: p, + stopCh: make(chan struct{}), + } +} + +// GRPCProviderServer handles the server, or plugin side of the rpc connection. +type GRPCProviderServer struct { + provider *Provider + stopCh chan struct{} + stopMu sync.Mutex +} + +// mergeStop is called in a goroutine and waits for the global stop signal +// and propagates cancellation to the passed in ctx/cancel func. The ctx is +// also passed to this function and waited upon so no goroutine leak is caused. +func mergeStop(ctx context.Context, cancel context.CancelFunc, stopCh chan struct{}) { + select { + case <-ctx.Done(): + return + case <-stopCh: + cancel() + } +} + +// StopContext derives a new context from the passed in grpc context. +// It creates a goroutine to wait for the server stop and propagates +// cancellation to the derived grpc context. +func (s *GRPCProviderServer) StopContext(ctx context.Context) context.Context { + s.stopMu.Lock() + defer s.stopMu.Unlock() + + stoppable, cancel := context.WithCancel(ctx) + go mergeStop(stoppable, cancel, s.stopCh) + return stoppable +} + +func (s *GRPCProviderServer) GetProviderSchema(_ context.Context, req *tfprotov5.GetProviderSchemaRequest) (*tfprotov5.GetProviderSchemaResponse, error) { + + resp := &tfprotov5.GetProviderSchemaResponse{ + ResourceSchemas: make(map[string]*tfprotov5.Schema), + DataSourceSchemas: make(map[string]*tfprotov5.Schema), + } + + resp.Provider = &tfprotov5.Schema{ + Block: convert.ConfigSchemaToProto(s.getProviderSchemaBlock()), + } + + resp.ProviderMeta = &tfprotov5.Schema{ + Block: convert.ConfigSchemaToProto(s.getProviderMetaSchemaBlock()), + } + + for typ, res := range s.provider.ResourcesMap { + resp.ResourceSchemas[typ] = &tfprotov5.Schema{ + Version: int64(res.SchemaVersion), + Block: convert.ConfigSchemaToProto(res.CoreConfigSchema()), + } + } + + for typ, dat := range s.provider.DataSourcesMap { + resp.DataSourceSchemas[typ] = &tfprotov5.Schema{ + Version: int64(dat.SchemaVersion), + Block: convert.ConfigSchemaToProto(dat.CoreConfigSchema()), + } + } + + return resp, nil +} + +func (s *GRPCProviderServer) getProviderSchemaBlock() *configschema.Block { + return InternalMap(s.provider.Schema).CoreConfigSchema() +} + +func (s *GRPCProviderServer) getProviderMetaSchemaBlock() *configschema.Block { + return InternalMap(s.provider.ProviderMetaSchema).CoreConfigSchema() +} + +func (s *GRPCProviderServer) getResourceSchemaBlock(name string) *configschema.Block { + res := s.provider.ResourcesMap[name] + return res.CoreConfigSchema() +} + +func (s *GRPCProviderServer) getDatasourceSchemaBlock(name string) *configschema.Block { + dat := s.provider.DataSourcesMap[name] + return dat.CoreConfigSchema() +} + +func (s *GRPCProviderServer) PrepareProviderConfig(_ context.Context, req *tfprotov5.PrepareProviderConfigRequest) (*tfprotov5.PrepareProviderConfigResponse, error) { + resp := &tfprotov5.PrepareProviderConfigResponse{} + + schemaBlock := s.getProviderSchemaBlock() + + configVal, err := msgpack.Unmarshal(req.Config.MsgPack, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + // lookup any required, top-level attributes that are Null, and see if we + // have a Default value available. + configVal, err = cty.Transform(configVal, func(path cty.Path, val cty.Value) (cty.Value, error) { + // we're only looking for top-level attributes + if len(path) != 1 { + return val, nil + } + + // nothing to do if we already have a value + if !val.IsNull() { + return val, nil + } + + // get the Schema definition for this attribute + getAttr, ok := path[0].(cty.GetAttrStep) + // these should all exist, but just ignore anything strange + if !ok { + return val, nil + } + + attrSchema := s.provider.Schema[getAttr.Name] + // continue to ignore anything that doesn't match + if attrSchema == nil { + return val, nil + } + + // this is deprecated, so don't set it + if attrSchema.Deprecated != "" { + return val, nil + } + + // find a default value if it exists + def, err := attrSchema.DefaultValue() + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, fmt.Errorf("error getting default for %q: %s", getAttr.Name, err)) + return val, err + } + + // no default + if def == nil { + return val, nil + } + + // create a cty.Value and make sure it's the correct type + tmpVal := hcl2shim.HCL2ValueFromConfigValue(def) + + // helper/schema used to allow setting "" to a bool + if val.Type() == cty.Bool && tmpVal.RawEquals(cty.StringVal("")) { + // return a warning about the conversion + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, "provider set empty string as default value for bool "+getAttr.Name) + tmpVal = cty.False + } + + val, err = ctyconvert.Convert(tmpVal, val.Type()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, fmt.Errorf("error setting default for %q: %s", getAttr.Name, err)) + } + + return val, err + }) + if err != nil { + // any error here was already added to the diagnostics + return resp, nil + } + + configVal, err = schemaBlock.CoerceValue(configVal) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + // Ensure there are no nulls that will cause helper/schema to panic. + if err := validateConfigNulls(configVal, nil); err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + config := terraform.NewResourceConfigShimmed(configVal, schemaBlock) + + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, s.provider.Validate(config)) + + preparedConfigMP, err := msgpack.Marshal(configVal, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + resp.PreparedConfig = &tfprotov5.DynamicValue{MsgPack: preparedConfigMP} + + return resp, nil +} + +func (s *GRPCProviderServer) ValidateResourceTypeConfig(_ context.Context, req *tfprotov5.ValidateResourceTypeConfigRequest) (*tfprotov5.ValidateResourceTypeConfigResponse, error) { + resp := &tfprotov5.ValidateResourceTypeConfigResponse{} + + schemaBlock := s.getResourceSchemaBlock(req.TypeName) + + configVal, err := msgpack.Unmarshal(req.Config.MsgPack, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + config := terraform.NewResourceConfigShimmed(configVal, schemaBlock) + + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, s.provider.ValidateResource(req.TypeName, config)) + + return resp, nil +} + +func (s *GRPCProviderServer) ValidateDataSourceConfig(_ context.Context, req *tfprotov5.ValidateDataSourceConfigRequest) (*tfprotov5.ValidateDataSourceConfigResponse, error) { + resp := &tfprotov5.ValidateDataSourceConfigResponse{} + + schemaBlock := s.getDatasourceSchemaBlock(req.TypeName) + + configVal, err := msgpack.Unmarshal(req.Config.MsgPack, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + // Ensure there are no nulls that will cause helper/schema to panic. + if err := validateConfigNulls(configVal, nil); err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + config := terraform.NewResourceConfigShimmed(configVal, schemaBlock) + + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, s.provider.ValidateDataSource(req.TypeName, config)) + + return resp, nil +} + +func (s *GRPCProviderServer) UpgradeResourceState(ctx context.Context, req *tfprotov5.UpgradeResourceStateRequest) (*tfprotov5.UpgradeResourceStateResponse, error) { + resp := &tfprotov5.UpgradeResourceStateResponse{} + + res, ok := s.provider.ResourcesMap[req.TypeName] + if !ok { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, fmt.Errorf("unknown resource type: %s", req.TypeName)) + return resp, nil + } + schemaBlock := s.getResourceSchemaBlock(req.TypeName) + + version := int(req.Version) + + jsonMap := map[string]interface{}{} + var err error + + switch { + // We first need to upgrade a flatmap state if it exists. + // There should never be both a JSON and Flatmap state in the request. + case len(req.RawState.Flatmap) > 0: + jsonMap, version, err = s.upgradeFlatmapState(ctx, version, req.RawState.Flatmap, res) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + // if there's a JSON state, we need to decode it. + case len(req.RawState.JSON) > 0: + if res.UseJSONNumber { + err = unmarshalJSON(req.RawState.JSON, &jsonMap) + } else { + err = json.Unmarshal(req.RawState.JSON, &jsonMap) + } + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + default: + log.Println("[DEBUG] no state provided to upgrade") + return resp, nil + } + + // complete the upgrade of the JSON states + jsonMap, err = s.upgradeJSONState(ctx, version, jsonMap, res) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + // The provider isn't required to clean out removed fields + s.removeAttributes(jsonMap, schemaBlock.ImpliedType()) + + // now we need to turn the state into the default json representation, so + // that it can be re-decoded using the actual schema. + val, err := JSONMapToStateValue(jsonMap, schemaBlock) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + // Now we need to make sure blocks are represented correctly, which means + // that missing blocks are empty collections, rather than null. + // First we need to CoerceValue to ensure that all object types match. + val, err = schemaBlock.CoerceValue(val) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + // Normalize the value and fill in any missing blocks. + val = objchange.NormalizeObjectFromLegacySDK(val, schemaBlock) + + // encode the final state to the expected msgpack format + newStateMP, err := msgpack.Marshal(val, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + resp.UpgradedState = &tfprotov5.DynamicValue{MsgPack: newStateMP} + return resp, nil +} + +// upgradeFlatmapState takes a legacy flatmap state, upgrades it using Migrate +// state if necessary, and converts it to the new JSON state format decoded as a +// map[string]interface{}. +// upgradeFlatmapState returns the json map along with the corresponding schema +// version. +func (s *GRPCProviderServer) upgradeFlatmapState(ctx context.Context, version int, m map[string]string, res *Resource) (map[string]interface{}, int, error) { + // this will be the version we've upgraded so, defaulting to the given + // version in case no migration was called. + upgradedVersion := version + + // first determine if we need to call the legacy MigrateState func + requiresMigrate := version < res.SchemaVersion + + schemaType := res.CoreConfigSchema().ImpliedType() + + // if there are any StateUpgraders, then we need to only compare + // against the first version there + if len(res.StateUpgraders) > 0 { + requiresMigrate = version < res.StateUpgraders[0].Version + } + + if requiresMigrate && res.MigrateState == nil { + // Providers were previously allowed to bump the version + // without declaring MigrateState. + // If there are further upgraders, then we've only updated that far. + if len(res.StateUpgraders) > 0 { + schemaType = res.StateUpgraders[0].Type + upgradedVersion = res.StateUpgraders[0].Version + } + } else if requiresMigrate { + is := &terraform.InstanceState{ + ID: m["id"], + Attributes: m, + Meta: map[string]interface{}{ + "schema_version": strconv.Itoa(version), + }, + } + is, err := res.MigrateState(version, is, s.provider.Meta()) + if err != nil { + return nil, 0, err + } + + // re-assign the map in case there was a copy made, making sure to keep + // the ID + m := is.Attributes + m["id"] = is.ID + + // if there are further upgraders, then we've only updated that far + if len(res.StateUpgraders) > 0 { + schemaType = res.StateUpgraders[0].Type + upgradedVersion = res.StateUpgraders[0].Version + } + } else { + // the schema version may be newer than the MigrateState functions + // handled and older than the current, but still stored in the flatmap + // form. If that's the case, we need to find the correct schema type to + // convert the state. + for _, upgrader := range res.StateUpgraders { + if upgrader.Version == version { + schemaType = upgrader.Type + break + } + } + } + + // now we know the state is up to the latest version that handled the + // flatmap format state. Now we can upgrade the format and continue from + // there. + newConfigVal, err := hcl2shim.HCL2ValueFromFlatmap(m, schemaType) + if err != nil { + return nil, 0, err + } + + jsonMap, err := stateValueToJSONMap(newConfigVal, schemaType, res.UseJSONNumber) + return jsonMap, upgradedVersion, err +} + +func (s *GRPCProviderServer) upgradeJSONState(ctx context.Context, version int, m map[string]interface{}, res *Resource) (map[string]interface{}, error) { + var err error + + for _, upgrader := range res.StateUpgraders { + if version != upgrader.Version { + continue + } + + m, err = upgrader.Upgrade(ctx, m, s.provider.Meta()) + if err != nil { + return nil, err + } + version++ + } + + return m, nil +} + +// Remove any attributes no longer present in the schema, so that the json can +// be correctly decoded. +func (s *GRPCProviderServer) removeAttributes(v interface{}, ty cty.Type) { + // we're only concerned with finding maps that corespond to object + // attributes + switch v := v.(type) { + case []interface{}: + // If these aren't blocks the next call will be a noop + if ty.IsListType() || ty.IsSetType() { + eTy := ty.ElementType() + for _, eV := range v { + s.removeAttributes(eV, eTy) + } + } + return + case map[string]interface{}: + // map blocks aren't yet supported, but handle this just in case + if ty.IsMapType() { + eTy := ty.ElementType() + for _, eV := range v { + s.removeAttributes(eV, eTy) + } + return + } + + if ty == cty.DynamicPseudoType { + log.Printf("[DEBUG] ignoring dynamic block: %#v\n", v) + return + } + + if !ty.IsObjectType() { + // This shouldn't happen, and will fail to decode further on, so + // there's no need to handle it here. + log.Printf("[WARN] unexpected type %#v for map in json state", ty) + return + } + + attrTypes := ty.AttributeTypes() + for attr, attrV := range v { + attrTy, ok := attrTypes[attr] + if !ok { + log.Printf("[DEBUG] attribute %q no longer present in schema", attr) + delete(v, attr) + continue + } + + s.removeAttributes(attrV, attrTy) + } + } +} + +func (s *GRPCProviderServer) StopProvider(_ context.Context, _ *tfprotov5.StopProviderRequest) (*tfprotov5.StopProviderResponse, error) { + s.stopMu.Lock() + defer s.stopMu.Unlock() + + // stop + close(s.stopCh) + // reset the stop signal + s.stopCh = make(chan struct{}) + + return &tfprotov5.StopProviderResponse{}, nil +} + +func (s *GRPCProviderServer) ConfigureProvider(ctx context.Context, req *tfprotov5.ConfigureProviderRequest) (*tfprotov5.ConfigureProviderResponse, error) { + resp := &tfprotov5.ConfigureProviderResponse{} + + schemaBlock := s.getProviderSchemaBlock() + + configVal, err := msgpack.Unmarshal(req.Config.MsgPack, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + s.provider.TerraformVersion = req.TerraformVersion + + // Ensure there are no nulls that will cause helper/schema to panic. + if err := validateConfigNulls(configVal, nil); err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + config := terraform.NewResourceConfigShimmed(configVal, schemaBlock) + // TODO: remove global stop context hack + // This attaches a global stop synchro'd context onto the provider.Configure + // request scoped context. This provides a substitute for the removed provider.StopContext() + // function. Ideally a provider should migrate to the context aware API that receives + // request scoped contexts, however this is a large undertaking for very large providers. + ctxHack := context.WithValue(ctx, StopContextKey, s.StopContext(context.Background())) + diags := s.provider.Configure(ctxHack, config) + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, diags) + + return resp, nil +} + +func (s *GRPCProviderServer) ReadResource(ctx context.Context, req *tfprotov5.ReadResourceRequest) (*tfprotov5.ReadResourceResponse, error) { + resp := &tfprotov5.ReadResourceResponse{ + // helper/schema did previously handle private data during refresh, but + // core is now going to expect this to be maintained in order to + // persist it in the state. + Private: req.Private, + } + + res, ok := s.provider.ResourcesMap[req.TypeName] + if !ok { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, fmt.Errorf("unknown resource type: %s", req.TypeName)) + return resp, nil + } + schemaBlock := s.getResourceSchemaBlock(req.TypeName) + + stateVal, err := msgpack.Unmarshal(req.CurrentState.MsgPack, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + instanceState, err := res.ShimInstanceStateFromValue(stateVal) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + private := make(map[string]interface{}) + if len(req.Private) > 0 { + if err := json.Unmarshal(req.Private, &private); err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + } + instanceState.Meta = private + + pmSchemaBlock := s.getProviderMetaSchemaBlock() + if pmSchemaBlock != nil && req.ProviderMeta != nil { + providerSchemaVal, err := msgpack.Unmarshal(req.ProviderMeta.MsgPack, pmSchemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + instanceState.ProviderMeta = providerSchemaVal + } + + newInstanceState, diags := res.RefreshWithoutUpgrade(ctx, instanceState, s.provider.Meta()) + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, diags) + if diags.HasError() { + return resp, nil + } + + if newInstanceState == nil || newInstanceState.ID == "" { + // The old provider API used an empty id to signal that the remote + // object appears to have been deleted, but our new protocol expects + // to see a null value (in the cty sense) in that case. + newStateMP, err := msgpack.Marshal(cty.NullVal(schemaBlock.ImpliedType()), schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + } + resp.NewState = &tfprotov5.DynamicValue{ + MsgPack: newStateMP, + } + return resp, nil + } + + // helper/schema should always copy the ID over, but do it again just to be safe + newInstanceState.Attributes["id"] = newInstanceState.ID + + newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(newInstanceState.Attributes, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + newStateVal = normalizeNullValues(newStateVal, stateVal, false) + newStateVal = copyTimeoutValues(newStateVal, stateVal) + + newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + resp.NewState = &tfprotov5.DynamicValue{ + MsgPack: newStateMP, + } + + return resp, nil +} + +func (s *GRPCProviderServer) PlanResourceChange(ctx context.Context, req *tfprotov5.PlanResourceChangeRequest) (*tfprotov5.PlanResourceChangeResponse, error) { + resp := &tfprotov5.PlanResourceChangeResponse{} + + // This is a signal to Terraform Core that we're doing the best we can to + // shim the legacy type system of the SDK onto the Terraform type system + // but we need it to cut us some slack. This setting should not be taken + // forward to any new SDK implementations, since setting it prevents us + // from catching certain classes of provider bug that can lead to + // confusing downstream errors. + resp.UnsafeToUseLegacyTypeSystem = true + + res, ok := s.provider.ResourcesMap[req.TypeName] + if !ok { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, fmt.Errorf("unknown resource type: %s", req.TypeName)) + return resp, nil + } + schemaBlock := s.getResourceSchemaBlock(req.TypeName) + + priorStateVal, err := msgpack.Unmarshal(req.PriorState.MsgPack, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + create := priorStateVal.IsNull() + + proposedNewStateVal, err := msgpack.Unmarshal(req.ProposedNewState.MsgPack, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + // We don't usually plan destroys, but this can return early in any case. + if proposedNewStateVal.IsNull() { + resp.PlannedState = req.ProposedNewState + resp.PlannedPrivate = req.PriorPrivate + return resp, nil + } + + priorState, err := res.ShimInstanceStateFromValue(priorStateVal) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + priorPrivate := make(map[string]interface{}) + if len(req.PriorPrivate) > 0 { + if err := json.Unmarshal(req.PriorPrivate, &priorPrivate); err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + } + + priorState.Meta = priorPrivate + + pmSchemaBlock := s.getProviderMetaSchemaBlock() + if pmSchemaBlock != nil && req.ProviderMeta != nil { + providerSchemaVal, err := msgpack.Unmarshal(req.ProviderMeta.MsgPack, pmSchemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + priorState.ProviderMeta = providerSchemaVal + } + + // Ensure there are no nulls that will cause helper/schema to panic. + if err := validateConfigNulls(proposedNewStateVal, nil); err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + // turn the proposed state into a legacy configuration + cfg := terraform.NewResourceConfigShimmed(proposedNewStateVal, schemaBlock) + + diff, err := res.SimpleDiff(ctx, priorState, cfg, s.provider.Meta()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + // if this is a new instance, we need to make sure ID is going to be computed + if create { + if diff == nil { + diff = terraform.NewInstanceDiff() + } + + diff.Attributes["id"] = &terraform.ResourceAttrDiff{ + NewComputed: true, + } + } + + if diff == nil || len(diff.Attributes) == 0 { + // schema.Provider.Diff returns nil if it ends up making a diff with no + // changes, but our new interface wants us to return an actual change + // description that _shows_ there are no changes. This is always the + // prior state, because we force a diff above if this is a new instance. + resp.PlannedState = req.PriorState + resp.PlannedPrivate = req.PriorPrivate + return resp, nil + } + + if priorState == nil { + priorState = &terraform.InstanceState{} + } + + // now we need to apply the diff to the prior state, so get the planned state + plannedAttrs, err := diff.Apply(priorState.Attributes, schemaBlock) + + plannedStateVal, err := hcl2shim.HCL2ValueFromFlatmap(plannedAttrs, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + plannedStateVal, err = schemaBlock.CoerceValue(plannedStateVal) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + plannedStateVal = normalizeNullValues(plannedStateVal, proposedNewStateVal, false) + + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + plannedStateVal = copyTimeoutValues(plannedStateVal, proposedNewStateVal) + + // The old SDK code has some imprecisions that cause it to sometimes + // generate differences that the SDK itself does not consider significant + // but Terraform Core would. To avoid producing weird do-nothing diffs + // in that case, we'll check if the provider as produced something we + // think is "equivalent" to the prior state and just return the prior state + // itself if so, thus ensuring that Terraform Core will treat this as + // a no-op. See the docs for ValuesSDKEquivalent for some caveats on its + // accuracy. + forceNoChanges := false + if hcl2shim.ValuesSDKEquivalent(priorStateVal, plannedStateVal) { + plannedStateVal = priorStateVal + forceNoChanges = true + } + + // if this was creating the resource, we need to set any remaining computed + // fields + if create { + plannedStateVal = SetUnknowns(plannedStateVal, schemaBlock) + } + + plannedMP, err := msgpack.Marshal(plannedStateVal, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + resp.PlannedState = &tfprotov5.DynamicValue{ + MsgPack: plannedMP, + } + + // encode any timeouts into the diff Meta + t := &ResourceTimeout{} + if err := t.ConfigDecode(res, cfg); err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + if err := t.DiffEncode(diff); err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + // Now we need to store any NewExtra values, which are where any actual + // StateFunc modified config fields are hidden. + privateMap := diff.Meta + if privateMap == nil { + privateMap = map[string]interface{}{} + } + + newExtra := map[string]interface{}{} + + for k, v := range diff.Attributes { + if v.NewExtra != nil { + newExtra[k] = v.NewExtra + } + } + privateMap[newExtraKey] = newExtra + + // the Meta field gets encoded into PlannedPrivate + plannedPrivate, err := json.Marshal(privateMap) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + resp.PlannedPrivate = plannedPrivate + + // collect the attributes that require instance replacement, and convert + // them to cty.Paths. + var requiresNew []string + if !forceNoChanges { + for attr, d := range diff.Attributes { + if d.RequiresNew { + requiresNew = append(requiresNew, attr) + } + } + } + + // If anything requires a new resource already, or the "id" field indicates + // that we will be creating a new resource, then we need to add that to + // RequiresReplace so that core can tell if the instance is being replaced + // even if changes are being suppressed via "ignore_changes". + id := plannedStateVal.GetAttr("id") + if len(requiresNew) > 0 || id.IsNull() || !id.IsKnown() { + requiresNew = append(requiresNew, "id") + } + + requiresReplace, err := hcl2shim.RequiresReplace(requiresNew, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + // convert these to the protocol structures + for _, p := range requiresReplace { + resp.RequiresReplace = append(resp.RequiresReplace, pathToAttributePath(p)) + } + + return resp, nil +} + +func (s *GRPCProviderServer) ApplyResourceChange(ctx context.Context, req *tfprotov5.ApplyResourceChangeRequest) (*tfprotov5.ApplyResourceChangeResponse, error) { + resp := &tfprotov5.ApplyResourceChangeResponse{ + // Start with the existing state as a fallback + NewState: req.PriorState, + } + + res, ok := s.provider.ResourcesMap[req.TypeName] + if !ok { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, fmt.Errorf("unknown resource type: %s", req.TypeName)) + return resp, nil + } + schemaBlock := s.getResourceSchemaBlock(req.TypeName) + + priorStateVal, err := msgpack.Unmarshal(req.PriorState.MsgPack, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + plannedStateVal, err := msgpack.Unmarshal(req.PlannedState.MsgPack, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + priorState, err := res.ShimInstanceStateFromValue(priorStateVal) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + private := make(map[string]interface{}) + if len(req.PlannedPrivate) > 0 { + if err := json.Unmarshal(req.PlannedPrivate, &private); err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + } + + var diff *terraform.InstanceDiff + destroy := false + + // a null state means we are destroying the instance + if plannedStateVal.IsNull() { + destroy = true + diff = &terraform.InstanceDiff{ + Attributes: make(map[string]*terraform.ResourceAttrDiff), + Meta: make(map[string]interface{}), + Destroy: true, + } + } else { + diff, err = DiffFromValues(ctx, priorStateVal, plannedStateVal, stripResourceModifiers(res)) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + } + + if diff == nil { + diff = &terraform.InstanceDiff{ + Attributes: make(map[string]*terraform.ResourceAttrDiff), + Meta: make(map[string]interface{}), + } + } + + // add NewExtra Fields that may have been stored in the private data + if newExtra := private[newExtraKey]; newExtra != nil { + for k, v := range newExtra.(map[string]interface{}) { + d := diff.Attributes[k] + + if d == nil { + d = &terraform.ResourceAttrDiff{} + } + + d.NewExtra = v + diff.Attributes[k] = d + } + } + + if private != nil { + diff.Meta = private + } + + for k, d := range diff.Attributes { + // We need to turn off any RequiresNew. There could be attributes + // without changes in here inserted by helper/schema, but if they have + // RequiresNew then the state will be dropped from the ResourceData. + d.RequiresNew = false + + // Check that any "removed" attributes that don't actually exist in the + // prior state, or helper/schema will confuse itself + if d.NewRemoved { + if _, ok := priorState.Attributes[k]; !ok { + delete(diff.Attributes, k) + } + } + } + + pmSchemaBlock := s.getProviderMetaSchemaBlock() + if pmSchemaBlock != nil && req.ProviderMeta != nil { + providerSchemaVal, err := msgpack.Unmarshal(req.ProviderMeta.MsgPack, pmSchemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + priorState.ProviderMeta = providerSchemaVal + } + + newInstanceState, diags := res.Apply(ctx, priorState, diff, s.provider.Meta()) + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, diags) + + newStateVal := cty.NullVal(schemaBlock.ImpliedType()) + + // Always return a null value for destroy. + // While this is usually indicated by a nil state, check for missing ID or + // attributes in the case of a provider failure. + if destroy || newInstanceState == nil || newInstanceState.Attributes == nil || newInstanceState.ID == "" { + newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + resp.NewState = &tfprotov5.DynamicValue{ + MsgPack: newStateMP, + } + return resp, nil + } + + // We keep the null val if we destroyed the resource, otherwise build the + // entire object, even if the new state was nil. + newStateVal, err = StateValueFromInstanceState(newInstanceState, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + newStateVal = normalizeNullValues(newStateVal, plannedStateVal, true) + + newStateVal = copyTimeoutValues(newStateVal, plannedStateVal) + + newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + resp.NewState = &tfprotov5.DynamicValue{ + MsgPack: newStateMP, + } + + meta, err := json.Marshal(newInstanceState.Meta) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + resp.Private = meta + + // This is a signal to Terraform Core that we're doing the best we can to + // shim the legacy type system of the SDK onto the Terraform type system + // but we need it to cut us some slack. This setting should not be taken + // forward to any new SDK implementations, since setting it prevents us + // from catching certain classes of provider bug that can lead to + // confusing downstream errors. + resp.UnsafeToUseLegacyTypeSystem = true + + return resp, nil +} + +func (s *GRPCProviderServer) ImportResourceState(ctx context.Context, req *tfprotov5.ImportResourceStateRequest) (*tfprotov5.ImportResourceStateResponse, error) { + resp := &tfprotov5.ImportResourceStateResponse{} + + info := &terraform.InstanceInfo{ + Type: req.TypeName, + } + + newInstanceStates, err := s.provider.ImportState(ctx, info, req.ID) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + for _, is := range newInstanceStates { + // copy the ID again just to be sure it wasn't missed + is.Attributes["id"] = is.ID + + resourceType := is.Ephemeral.Type + if resourceType == "" { + resourceType = req.TypeName + } + + schemaBlock := s.getResourceSchemaBlock(resourceType) + newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(is.Attributes, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + // Normalize the value and fill in any missing blocks. + newStateVal = objchange.NormalizeObjectFromLegacySDK(newStateVal, schemaBlock) + + newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + meta, err := json.Marshal(is.Meta) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + importedResource := &tfprotov5.ImportedResource{ + TypeName: resourceType, + State: &tfprotov5.DynamicValue{ + MsgPack: newStateMP, + }, + Private: meta, + } + + resp.ImportedResources = append(resp.ImportedResources, importedResource) + } + + return resp, nil +} + +func (s *GRPCProviderServer) ReadDataSource(ctx context.Context, req *tfprotov5.ReadDataSourceRequest) (*tfprotov5.ReadDataSourceResponse, error) { + resp := &tfprotov5.ReadDataSourceResponse{} + + schemaBlock := s.getDatasourceSchemaBlock(req.TypeName) + + configVal, err := msgpack.Unmarshal(req.Config.MsgPack, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + // Ensure there are no nulls that will cause helper/schema to panic. + if err := validateConfigNulls(configVal, nil); err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + config := terraform.NewResourceConfigShimmed(configVal, schemaBlock) + + // we need to still build the diff separately with the Read method to match + // the old behavior + res, ok := s.provider.DataSourcesMap[req.TypeName] + if !ok { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, fmt.Errorf("unknown data source: %s", req.TypeName)) + return resp, nil + } + diff, err := res.Diff(ctx, nil, config, s.provider.Meta()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + // now we can get the new complete data source + newInstanceState, diags := res.ReadDataApply(ctx, diff, s.provider.Meta()) + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, diags) + if diags.HasError() { + return resp, nil + } + + newStateVal, err := StateValueFromInstanceState(newInstanceState, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + newStateVal = copyTimeoutValues(newStateVal, configVal) + + newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType()) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + resp.State = &tfprotov5.DynamicValue{ + MsgPack: newStateMP, + } + return resp, nil +} + +func pathToAttributePath(path cty.Path) *tftypes.AttributePath { + var steps []tftypes.AttributePathStep + + for _, step := range path { + switch s := step.(type) { + case cty.GetAttrStep: + steps = append(steps, + tftypes.AttributeName(s.Name), + ) + case cty.IndexStep: + ty := s.Key.Type() + switch ty { + case cty.Number: + i, _ := s.Key.AsBigFloat().Int64() + steps = append(steps, + tftypes.ElementKeyInt(i), + ) + case cty.String: + steps = append(steps, + tftypes.ElementKeyString(s.Key.AsString()), + ) + } + } + } + + if len(steps) < 1 { + return nil + } + return tftypes.NewAttributePathWithSteps(steps) +} + +// helper/schema throws away timeout values from the config and stores them in +// the Private/Meta fields. we need to copy those values into the planned state +// so that core doesn't see a perpetual diff with the timeout block. +func copyTimeoutValues(to cty.Value, from cty.Value) cty.Value { + // if `to` is null we are planning to remove it altogether. + if to.IsNull() { + return to + } + toAttrs := to.AsValueMap() + // We need to remove the key since the hcl2shims will add a non-null block + // because we can't determine if a single block was null from the flatmapped + // values. This needs to conform to the correct schema for marshaling, so + // change the value to null rather than deleting it from the object map. + timeouts, ok := toAttrs[TimeoutsConfigKey] + if ok { + toAttrs[TimeoutsConfigKey] = cty.NullVal(timeouts.Type()) + } + + // if from is null then there are no timeouts to copy + if from.IsNull() { + return cty.ObjectVal(toAttrs) + } + + fromAttrs := from.AsValueMap() + timeouts, ok = fromAttrs[TimeoutsConfigKey] + + // timeouts shouldn't be unknown, but don't copy possibly invalid values either + if !ok || timeouts.IsNull() || !timeouts.IsWhollyKnown() { + // no timeouts block to copy + return cty.ObjectVal(toAttrs) + } + + toAttrs[TimeoutsConfigKey] = timeouts + + return cty.ObjectVal(toAttrs) +} + +// stripResourceModifiers takes a *schema.Resource and returns a deep copy with all +// StateFuncs and CustomizeDiffs removed. This will be used during apply to +// create a diff from a planned state where the diff modifications have already +// been applied. +func stripResourceModifiers(r *Resource) *Resource { + if r == nil { + return nil + } + // start with a shallow copy + newResource := new(Resource) + *newResource = *r + + newResource.CustomizeDiff = nil + newResource.Schema = map[string]*Schema{} + + for k, s := range r.Schema { + newResource.Schema[k] = stripSchema(s) + } + + return newResource +} + +func stripSchema(s *Schema) *Schema { + if s == nil { + return nil + } + // start with a shallow copy + newSchema := new(Schema) + *newSchema = *s + + newSchema.StateFunc = nil + + switch e := newSchema.Elem.(type) { + case *Schema: + newSchema.Elem = stripSchema(e) + case *Resource: + newSchema.Elem = stripResourceModifiers(e) + } + + return newSchema +} + +// Zero values and empty containers may be interchanged by the apply process. +// When there is a discrepency between src and dst value being null or empty, +// prefer the src value. This takes a little more liberty with set types, since +// we can't correlate modified set values. In the case of sets, if the src set +// was wholly known we assume the value was correctly applied and copy that +// entirely to the new value. +// While apply prefers the src value, during plan we prefer dst whenever there +// is an unknown or a set is involved, since the plan can alter the value +// however it sees fit. This however means that a CustomizeDiffFunction may not +// be able to change a null to an empty value or vice versa, but that should be +// very uncommon nor was it reliable before 0.12 either. +func normalizeNullValues(dst, src cty.Value, apply bool) cty.Value { + ty := dst.Type() + if !src.IsNull() && !src.IsKnown() { + // Return src during plan to retain unknown interpolated placeholders, + // which could be lost if we're only updating a resource. If this is a + // read scenario, then there shouldn't be any unknowns at all. + if dst.IsNull() && !apply { + return src + } + return dst + } + + // Handle null/empty changes for collections during apply. + // A change between null and empty values prefers src to make sure the state + // is consistent between plan and apply. + if ty.IsCollectionType() && apply { + dstEmpty := !dst.IsNull() && dst.IsKnown() && dst.LengthInt() == 0 + srcEmpty := !src.IsNull() && src.IsKnown() && src.LengthInt() == 0 + + if (src.IsNull() && dstEmpty) || (srcEmpty && dst.IsNull()) { + return src + } + } + + // check the invariants that we need below, to ensure we are working with + // non-null and known values. + if src.IsNull() || !src.IsKnown() || !dst.IsKnown() { + return dst + } + + switch { + case ty.IsMapType(), ty.IsObjectType(): + var dstMap map[string]cty.Value + if !dst.IsNull() { + dstMap = dst.AsValueMap() + } + if dstMap == nil { + dstMap = map[string]cty.Value{} + } + + srcMap := src.AsValueMap() + for key, v := range srcMap { + dstVal, ok := dstMap[key] + if !ok && apply && ty.IsMapType() { + // don't transfer old map values to dst during apply + continue + } + + if dstVal == cty.NilVal { + if !apply && ty.IsMapType() { + // let plan shape this map however it wants + continue + } + dstVal = cty.NullVal(v.Type()) + } + + dstMap[key] = normalizeNullValues(dstVal, v, apply) + } + + // you can't call MapVal/ObjectVal with empty maps, but nothing was + // copied in anyway. If the dst is nil, and the src is known, assume the + // src is correct. + if len(dstMap) == 0 { + if dst.IsNull() && src.IsWhollyKnown() && apply { + return src + } + return dst + } + + if ty.IsMapType() { + // helper/schema will populate an optional+computed map with + // unknowns which we have to fixup here. + // It would be preferable to simply prevent any known value from + // becoming unknown, but concessions have to be made to retain the + // broken legacy behavior when possible. + for k, srcVal := range srcMap { + if !srcVal.IsNull() && srcVal.IsKnown() { + dstVal, ok := dstMap[k] + if !ok { + continue + } + + if !dstVal.IsNull() && !dstVal.IsKnown() { + dstMap[k] = srcVal + } + } + } + + return cty.MapVal(dstMap) + } + + return cty.ObjectVal(dstMap) + + case ty.IsSetType(): + // If the original was wholly known, then we expect that is what the + // provider applied. The apply process loses too much information to + // reliably re-create the set. + if src.IsWhollyKnown() && apply { + return src + } + + case ty.IsListType(), ty.IsTupleType(): + // If the dst is null, and the src is known, then we lost an empty value + // so take the original. + if dst.IsNull() { + if src.IsWhollyKnown() && src.LengthInt() == 0 && apply { + return src + } + + // if dst is null and src only contains unknown values, then we lost + // those during a read or plan. + if !apply && !src.IsNull() { + allUnknown := true + for _, v := range src.AsValueSlice() { + if v.IsKnown() { + allUnknown = false + break + } + } + if allUnknown { + return src + } + } + + return dst + } + + // if the lengths are identical, then iterate over each element in succession. + srcLen := src.LengthInt() + dstLen := dst.LengthInt() + if srcLen == dstLen && srcLen > 0 { + srcs := src.AsValueSlice() + dsts := dst.AsValueSlice() + + for i := 0; i < srcLen; i++ { + dsts[i] = normalizeNullValues(dsts[i], srcs[i], apply) + } + + if ty.IsTupleType() { + return cty.TupleVal(dsts) + } + return cty.ListVal(dsts) + } + + case ty == cty.String: + // The legacy SDK should not be able to remove a value during plan or + // apply, however we are only going to overwrite this if the source was + // an empty string, since that is what is often equated with unset and + // lost in the diff process. + if dst.IsNull() && src.AsString() == "" { + return src + } + } + + return dst +} + +// validateConfigNulls checks a config value for unsupported nulls before +// attempting to shim the value. While null values can mostly be ignored in the +// configuration, since they're not supported in HCL1, the case where a null +// appears in a list-like attribute (list, set, tuple) will present a nil value +// to helper/schema which can panic. Return an error to the user in this case, +// indicating the attribute with the null value. +func validateConfigNulls(v cty.Value, path cty.Path) []*tfprotov5.Diagnostic { + var diags []*tfprotov5.Diagnostic + if v.IsNull() || !v.IsKnown() { + return diags + } + + switch { + case v.Type().IsListType() || v.Type().IsSetType() || v.Type().IsTupleType(): + it := v.ElementIterator() + for it.Next() { + kv, ev := it.Element() + if ev.IsNull() { + // if this is a set, the kv is also going to be null which + // isn't a valid path element, so we can't append it to the + // diagnostic. + p := path + if !kv.IsNull() { + p = append(p, cty.IndexStep{Key: kv}) + } + + diags = append(diags, &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "Null value found in list", + Detail: "Null values are not allowed for this attribute value.", + Attribute: convert.PathToAttributePath(p), + }) + continue + } + + d := validateConfigNulls(ev, append(path, cty.IndexStep{Key: kv})) + diags = convert.AppendProtoDiag(diags, d) + } + + case v.Type().IsMapType() || v.Type().IsObjectType(): + it := v.ElementIterator() + for it.Next() { + kv, ev := it.Element() + var step cty.PathStep + switch { + case v.Type().IsMapType(): + step = cty.IndexStep{Key: kv} + case v.Type().IsObjectType(): + step = cty.GetAttrStep{Name: kv.AsString()} + } + d := validateConfigNulls(ev, append(path, step)) + diags = convert.AppendProtoDiag(diags, d) + } + } + + return diags +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/grpc_provider_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/grpc_provider_test.go new file mode 100644 index 00000000..6dad0b31 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/grpc_provider_test.go @@ -0,0 +1,1962 @@ +package schema + +import ( + "context" + "fmt" + "math/big" + "strconv" + "strings" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/go-cty/cty/msgpack" + + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/plugin/convert" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +// The GRPCProviderServer will directly implement the go protobuf server +var _ tfprotov5.ProviderServer = (*GRPCProviderServer)(nil) + +func TestUpgradeState_jsonState(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "two": { + Type: TypeInt, + Optional: true, + }, + }, + } + + r.StateUpgraders = []StateUpgrader{ + { + Version: 0, + Type: cty.Object(map[string]cty.Type{ + "id": cty.String, + "zero": cty.Number, + }), + Upgrade: func(ctx context.Context, m map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + _, ok := m["zero"].(float64) + if !ok { + return nil, fmt.Errorf("zero not found in %#v", m) + } + m["one"] = float64(1) + delete(m, "zero") + return m, nil + }, + }, + { + Version: 1, + Type: cty.Object(map[string]cty.Type{ + "id": cty.String, + "one": cty.Number, + }), + Upgrade: func(ctx context.Context, m map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + _, ok := m["one"].(float64) + if !ok { + return nil, fmt.Errorf("one not found in %#v", m) + } + m["two"] = float64(2) + delete(m, "one") + return m, nil + }, + }, + } + + server := NewGRPCProviderServer(&Provider{ + ResourcesMap: map[string]*Resource{ + "test": r, + }, + }) + + req := &tfprotov5.UpgradeResourceStateRequest{ + TypeName: "test", + Version: 0, + RawState: &tfprotov5.RawState{ + JSON: []byte(`{"id":"bar","zero":0}`), + }, + } + + resp, err := server.UpgradeResourceState(nil, req) + if err != nil { + t.Fatal(err) + } + + if len(resp.Diagnostics) > 0 { + for _, d := range resp.Diagnostics { + t.Errorf("%#v", d) + } + t.Fatal("error") + } + + val, err := msgpack.Unmarshal(resp.UpgradedState.MsgPack, r.CoreConfigSchema().ImpliedType()) + if err != nil { + t.Fatal(err) + } + + expected := cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("bar"), + "two": cty.NumberIntVal(2), + }) + + if !cmp.Equal(expected, val, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(expected, val, valueComparer, equateEmpty)) + } +} + +func TestUpgradeState_jsonStateBigInt(t *testing.T) { + r := &Resource{ + UseJSONNumber: true, + SchemaVersion: 2, + Schema: map[string]*Schema{ + "int": { + Type: TypeInt, + Required: true, + }, + }, + } + + server := NewGRPCProviderServer(&Provider{ + ResourcesMap: map[string]*Resource{ + "test": r, + }, + }) + + req := &tfprotov5.UpgradeResourceStateRequest{ + TypeName: "test", + Version: 0, + RawState: &tfprotov5.RawState{ + JSON: []byte(`{"id":"bar","int":7227701560655103598}`), + }, + } + + resp, err := server.UpgradeResourceState(nil, req) + if err != nil { + t.Fatal(err) + } + + if len(resp.Diagnostics) > 0 { + for _, d := range resp.Diagnostics { + t.Errorf("%#v", d) + } + t.Fatal("error") + } + + val, err := msgpack.Unmarshal(resp.UpgradedState.MsgPack, r.CoreConfigSchema().ImpliedType()) + if err != nil { + t.Fatal(err) + } + + expected := cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("bar"), + "int": cty.NumberIntVal(7227701560655103598), + }) + + if !cmp.Equal(expected, val, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(expected, val, valueComparer, equateEmpty)) + } +} + +func TestUpgradeState_removedAttr(t *testing.T) { + r1 := &Resource{ + Schema: map[string]*Schema{ + "two": { + Type: TypeString, + Optional: true, + }, + }, + } + + r2 := &Resource{ + Schema: map[string]*Schema{ + "multi": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "set": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "required": { + Type: TypeString, + Required: true, + }, + }, + }, + }, + }, + }, + }, + }, + } + + r3 := &Resource{ + Schema: map[string]*Schema{ + "config_mode_attr": { + Type: TypeList, + ConfigMode: SchemaConfigModeAttr, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + }, + } + + p := &Provider{ + ResourcesMap: map[string]*Resource{ + "r1": r1, + "r2": r2, + "r3": r3, + }, + } + + server := NewGRPCProviderServer(p) + + for _, tc := range []struct { + name string + raw string + expected cty.Value + }{ + { + name: "r1", + raw: `{"id":"bar","removed":"removed","two":"2"}`, + expected: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("bar"), + "two": cty.StringVal("2"), + }), + }, + { + name: "r2", + raw: `{"id":"bar","multi":[{"set":[{"required":"ok","removed":"removed"}]}]}`, + expected: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("bar"), + "multi": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "set": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "required": cty.StringVal("ok"), + }), + }), + }), + }), + }), + }, + { + name: "r3", + raw: `{"id":"bar","config_mode_attr":[{"foo":"ok","removed":"removed"}]}`, + expected: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("bar"), + "config_mode_attr": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("ok"), + }), + }), + }), + }, + } { + t.Run(tc.name, func(t *testing.T) { + req := &tfprotov5.UpgradeResourceStateRequest{ + TypeName: tc.name, + Version: 0, + RawState: &tfprotov5.RawState{ + JSON: []byte(tc.raw), + }, + } + resp, err := server.UpgradeResourceState(nil, req) + if err != nil { + t.Fatal(err) + } + + if len(resp.Diagnostics) > 0 { + for _, d := range resp.Diagnostics { + t.Errorf("%#v", d) + } + t.Fatal("error") + } + val, err := msgpack.Unmarshal(resp.UpgradedState.MsgPack, p.ResourcesMap[tc.name].CoreConfigSchema().ImpliedType()) + if err != nil { + t.Fatal(err) + } + if !tc.expected.RawEquals(val) { + t.Fatalf("\nexpected: %#v\ngot: %#v\n", tc.expected, val) + } + }) + } + +} + +func TestUpgradeState_flatmapState(t *testing.T) { + r := &Resource{ + SchemaVersion: 4, + Schema: map[string]*Schema{ + "four": { + Type: TypeInt, + Required: true, + }, + "block": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + }, + // this MigrateState will take the state to version 2 + MigrateState: func(v int, is *terraform.InstanceState, _ interface{}) (*terraform.InstanceState, error) { + switch v { + case 0: + _, ok := is.Attributes["zero"] + if !ok { + return nil, fmt.Errorf("zero not found in %#v", is.Attributes) + } + is.Attributes["one"] = "1" + delete(is.Attributes, "zero") + fallthrough + case 1: + _, ok := is.Attributes["one"] + if !ok { + return nil, fmt.Errorf("one not found in %#v", is.Attributes) + } + is.Attributes["two"] = "2" + delete(is.Attributes, "one") + default: + return nil, fmt.Errorf("invalid schema version %d", v) + } + return is, nil + }, + } + + r.StateUpgraders = []StateUpgrader{ + { + Version: 2, + Type: cty.Object(map[string]cty.Type{ + "id": cty.String, + "two": cty.Number, + }), + Upgrade: func(ctx context.Context, m map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + _, ok := m["two"].(float64) + if !ok { + return nil, fmt.Errorf("two not found in %#v", m) + } + m["three"] = float64(3) + delete(m, "two") + return m, nil + }, + }, + { + Version: 3, + Type: cty.Object(map[string]cty.Type{ + "id": cty.String, + "three": cty.Number, + }), + Upgrade: func(ctx context.Context, m map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + _, ok := m["three"].(float64) + if !ok { + return nil, fmt.Errorf("three not found in %#v", m) + } + m["four"] = float64(4) + delete(m, "three") + return m, nil + }, + }, + } + + server := NewGRPCProviderServer(&Provider{ + ResourcesMap: map[string]*Resource{ + "test": r, + }, + }) + + testReqs := []*tfprotov5.UpgradeResourceStateRequest{ + { + TypeName: "test", + Version: 0, + RawState: &tfprotov5.RawState{ + Flatmap: map[string]string{ + "id": "bar", + "zero": "0", + }, + }, + }, + { + TypeName: "test", + Version: 1, + RawState: &tfprotov5.RawState{ + Flatmap: map[string]string{ + "id": "bar", + "one": "1", + }, + }, + }, + // two and up could be stored in flatmap or json states + { + TypeName: "test", + Version: 2, + RawState: &tfprotov5.RawState{ + Flatmap: map[string]string{ + "id": "bar", + "two": "2", + }, + }, + }, + { + TypeName: "test", + Version: 2, + RawState: &tfprotov5.RawState{ + JSON: []byte(`{"id":"bar","two":2}`), + }, + }, + { + TypeName: "test", + Version: 3, + RawState: &tfprotov5.RawState{ + Flatmap: map[string]string{ + "id": "bar", + "three": "3", + }, + }, + }, + { + TypeName: "test", + Version: 3, + RawState: &tfprotov5.RawState{ + JSON: []byte(`{"id":"bar","three":3}`), + }, + }, + { + TypeName: "test", + Version: 4, + RawState: &tfprotov5.RawState{ + Flatmap: map[string]string{ + "id": "bar", + "four": "4", + }, + }, + }, + { + TypeName: "test", + Version: 4, + RawState: &tfprotov5.RawState{ + JSON: []byte(`{"id":"bar","four":4}`), + }, + }, + } + + for i, req := range testReqs { + t.Run(fmt.Sprintf("%d-%d", i, req.Version), func(t *testing.T) { + resp, err := server.UpgradeResourceState(nil, req) + if err != nil { + t.Fatal(err) + } + + if len(resp.Diagnostics) > 0 { + for _, d := range resp.Diagnostics { + t.Errorf("%#v", d) + } + t.Fatal("error") + } + + val, err := msgpack.Unmarshal(resp.UpgradedState.MsgPack, r.CoreConfigSchema().ImpliedType()) + if err != nil { + t.Fatal(err) + } + + expected := cty.ObjectVal(map[string]cty.Value{ + "block": cty.ListValEmpty(cty.Object(map[string]cty.Type{"attr": cty.String})), + "id": cty.StringVal("bar"), + "four": cty.NumberIntVal(4), + }) + + if !cmp.Equal(expected, val, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(expected, val, valueComparer, equateEmpty)) + } + }) + } +} + +func TestUpgradeState_flatmapStateMissingMigrateState(t *testing.T) { + r := &Resource{ + SchemaVersion: 1, + Schema: map[string]*Schema{ + "one": { + Type: TypeInt, + Required: true, + }, + }, + } + + server := NewGRPCProviderServer(&Provider{ + ResourcesMap: map[string]*Resource{ + "test": r, + }, + }) + + testReqs := []*tfprotov5.UpgradeResourceStateRequest{ + { + TypeName: "test", + Version: 0, + RawState: &tfprotov5.RawState{ + Flatmap: map[string]string{ + "id": "bar", + "one": "1", + }, + }, + }, + { + TypeName: "test", + Version: 1, + RawState: &tfprotov5.RawState{ + Flatmap: map[string]string{ + "id": "bar", + "one": "1", + }, + }, + }, + { + TypeName: "test", + Version: 1, + RawState: &tfprotov5.RawState{ + JSON: []byte(`{"id":"bar","one":1}`), + }, + }, + } + + for i, req := range testReqs { + t.Run(fmt.Sprintf("%d-%d", i, req.Version), func(t *testing.T) { + resp, err := server.UpgradeResourceState(nil, req) + if err != nil { + t.Fatal(err) + } + + if len(resp.Diagnostics) > 0 { + for _, d := range resp.Diagnostics { + t.Errorf("%#v", d) + } + t.Fatal("error") + } + + val, err := msgpack.Unmarshal(resp.UpgradedState.MsgPack, r.CoreConfigSchema().ImpliedType()) + if err != nil { + t.Fatal(err) + } + + expected := cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("bar"), + "one": cty.NumberIntVal(1), + }) + + if !cmp.Equal(expected, val, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(expected, val, valueComparer, equateEmpty)) + } + }) + } +} + +func TestPlanResourceChange(t *testing.T) { + r := &Resource{ + SchemaVersion: 4, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + } + + server := NewGRPCProviderServer(&Provider{ + ResourcesMap: map[string]*Resource{ + "test": r, + }, + }) + + schema := r.CoreConfigSchema() + priorState, err := msgpack.Marshal(cty.NullVal(schema.ImpliedType()), schema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + // A propsed state with only the ID unknown will produce a nil diff, and + // should return the propsed state value. + proposedVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ + "id": cty.UnknownVal(cty.String), + })) + if err != nil { + t.Fatal(err) + } + proposedState, err := msgpack.Marshal(proposedVal, schema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + testReq := &tfprotov5.PlanResourceChangeRequest{ + TypeName: "test", + PriorState: &tfprotov5.DynamicValue{ + MsgPack: priorState, + }, + ProposedNewState: &tfprotov5.DynamicValue{ + MsgPack: proposedState, + }, + } + + resp, err := server.PlanResourceChange(context.Background(), testReq) + if err != nil { + t.Fatal(err) + } + + plannedStateVal, err := msgpack.Unmarshal(resp.PlannedState.MsgPack, schema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + if !cmp.Equal(proposedVal, plannedStateVal, valueComparer) { + t.Fatal(cmp.Diff(proposedVal, plannedStateVal, valueComparer)) + } +} + +func TestPlanResourceChange_bigint(t *testing.T) { + r := &Resource{ + UseJSONNumber: true, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Required: true, + }, + }, + } + + server := NewGRPCProviderServer(&Provider{ + ResourcesMap: map[string]*Resource{ + "test": r, + }, + }) + + schema := r.CoreConfigSchema() + priorState, err := msgpack.Marshal(cty.NullVal(schema.ImpliedType()), schema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + proposedVal := cty.ObjectVal(map[string]cty.Value{ + "id": cty.UnknownVal(cty.String), + "foo": cty.MustParseNumberVal("7227701560655103598"), + }) + proposedState, err := msgpack.Marshal(proposedVal, schema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + testReq := &tfprotov5.PlanResourceChangeRequest{ + TypeName: "test", + PriorState: &tfprotov5.DynamicValue{ + MsgPack: priorState, + }, + ProposedNewState: &tfprotov5.DynamicValue{ + MsgPack: proposedState, + }, + } + + resp, err := server.PlanResourceChange(context.Background(), testReq) + if err != nil { + t.Fatal(err) + } + + plannedStateVal, err := msgpack.Unmarshal(resp.PlannedState.MsgPack, schema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + if !cmp.Equal(proposedVal, plannedStateVal, valueComparer) { + t.Fatal(cmp.Diff(proposedVal, plannedStateVal, valueComparer)) + } + + plannedStateFoo, acc := plannedStateVal.GetAttr("foo").AsBigFloat().Int64() + if acc != big.Exact { + t.Fatalf("Expected exact accuracy, got %s", acc) + } + if plannedStateFoo != 7227701560655103598 { + t.Fatalf("Expected %d, got %d, this represents a loss of precision in planning large numbers", 7227701560655103598, plannedStateFoo) + } +} + +func TestApplyResourceChange(t *testing.T) { + testCases := []struct { + Description string + TestResource *Resource + }{ + { + Description: "Create", + TestResource: &Resource{ + SchemaVersion: 4, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + Create: func(rd *ResourceData, _ interface{}) error { + rd.SetId("bar") + return nil + }, + }, + }, + { + Description: "CreateContext", + TestResource: &Resource{ + SchemaVersion: 4, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + CreateContext: func(_ context.Context, rd *ResourceData, _ interface{}) diag.Diagnostics { + rd.SetId("bar") + return nil + }, + }, + }, + { + Description: "CreateWithoutTimeout", + TestResource: &Resource{ + SchemaVersion: 4, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + CreateWithoutTimeout: func(_ context.Context, rd *ResourceData, _ interface{}) diag.Diagnostics { + rd.SetId("bar") + return nil + }, + }, + }, + } + + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.Description, func(t *testing.T) { + server := NewGRPCProviderServer(&Provider{ + ResourcesMap: map[string]*Resource{ + "test": testCase.TestResource, + }, + }) + + schema := testCase.TestResource.CoreConfigSchema() + priorState, err := msgpack.Marshal(cty.NullVal(schema.ImpliedType()), schema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + // A proposed state with only the ID unknown will produce a nil diff, and + // should return the proposed state value. + plannedVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ + "id": cty.UnknownVal(cty.String), + })) + if err != nil { + t.Fatal(err) + } + plannedState, err := msgpack.Marshal(plannedVal, schema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + testReq := &tfprotov5.ApplyResourceChangeRequest{ + TypeName: "test", + PriorState: &tfprotov5.DynamicValue{ + MsgPack: priorState, + }, + PlannedState: &tfprotov5.DynamicValue{ + MsgPack: plannedState, + }, + } + + resp, err := server.ApplyResourceChange(context.Background(), testReq) + if err != nil { + t.Fatal(err) + } + + newStateVal, err := msgpack.Unmarshal(resp.NewState.MsgPack, schema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + id := newStateVal.GetAttr("id").AsString() + if id != "bar" { + t.Fatalf("incorrect final state: %#v\n", newStateVal) + } + }) + } +} + +func TestApplyResourceChange_bigint(t *testing.T) { + testCases := []struct { + Description string + TestResource *Resource + }{ + { + Description: "Create", + TestResource: &Resource{ + UseJSONNumber: true, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Required: true, + }, + }, + Create: func(rd *ResourceData, _ interface{}) error { + rd.SetId("bar") + return nil + }, + }, + }, + { + Description: "CreateContext", + TestResource: &Resource{ + UseJSONNumber: true, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Required: true, + }, + }, + CreateContext: func(_ context.Context, rd *ResourceData, _ interface{}) diag.Diagnostics { + rd.SetId("bar") + return nil + }, + }, + }, + { + Description: "CreateWithoutTimeout", + TestResource: &Resource{ + UseJSONNumber: true, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Required: true, + }, + }, + CreateWithoutTimeout: func(_ context.Context, rd *ResourceData, _ interface{}) diag.Diagnostics { + rd.SetId("bar") + return nil + }, + }, + }, + } + + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.Description, func(t *testing.T) { + server := NewGRPCProviderServer(&Provider{ + ResourcesMap: map[string]*Resource{ + "test": testCase.TestResource, + }, + }) + + schema := testCase.TestResource.CoreConfigSchema() + priorState, err := msgpack.Marshal(cty.NullVal(schema.ImpliedType()), schema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + plannedVal := cty.ObjectVal(map[string]cty.Value{ + "id": cty.UnknownVal(cty.String), + "foo": cty.MustParseNumberVal("7227701560655103598"), + }) + plannedState, err := msgpack.Marshal(plannedVal, schema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + testReq := &tfprotov5.ApplyResourceChangeRequest{ + TypeName: "test", + PriorState: &tfprotov5.DynamicValue{ + MsgPack: priorState, + }, + PlannedState: &tfprotov5.DynamicValue{ + MsgPack: plannedState, + }, + } + + resp, err := server.ApplyResourceChange(context.Background(), testReq) + if err != nil { + t.Fatal(err) + } + + newStateVal, err := msgpack.Unmarshal(resp.NewState.MsgPack, schema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + id := newStateVal.GetAttr("id").AsString() + if id != "bar" { + t.Fatalf("incorrect final state: %#v\n", newStateVal) + } + + foo, acc := newStateVal.GetAttr("foo").AsBigFloat().Int64() + if acc != big.Exact { + t.Fatalf("Expected exact accuracy, got %s", acc) + } + if foo != 7227701560655103598 { + t.Fatalf("Expected %d, got %d, this represents a loss of precision in applying large numbers", 7227701560655103598, foo) + } + }) + } +} + +func TestPrepareProviderConfig(t *testing.T) { + for _, tc := range []struct { + Name string + Schema map[string]*Schema + ConfigVal cty.Value + ExpectError string + ExpectConfig cty.Value + }{ + { + Name: "test prepare", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + }, + }, + ConfigVal: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + }), + ExpectConfig: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + }), + }, + { + Name: "test default", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + Default: "default", + }, + }, + ConfigVal: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.NullVal(cty.String), + }), + ExpectConfig: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("default"), + }), + }, + { + Name: "test defaultfunc", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + DefaultFunc: func() (interface{}, error) { + return "defaultfunc", nil + }, + }, + }, + ConfigVal: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.NullVal(cty.String), + }), + ExpectConfig: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("defaultfunc"), + }), + }, + { + Name: "test default required", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + DefaultFunc: func() (interface{}, error) { + return "defaultfunc", nil + }, + }, + }, + ConfigVal: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.NullVal(cty.String), + }), + ExpectConfig: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("defaultfunc"), + }), + }, + { + Name: "test incorrect type", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + }, + }, + ConfigVal: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.NumberIntVal(3), + }), + ExpectConfig: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("3"), + }), + }, + { + Name: "test incorrect default type", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + Default: true, + }, + }, + ConfigVal: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.NullVal(cty.String), + }), + ExpectConfig: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("true"), + }), + }, + { + Name: "test incorrect default bool type", + Schema: map[string]*Schema{ + "foo": { + Type: TypeBool, + Optional: true, + Default: "", + }, + }, + ConfigVal: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.NullVal(cty.Bool), + }), + ExpectConfig: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.False, + }), + }, + } { + t.Run(tc.Name, func(t *testing.T) { + server := NewGRPCProviderServer(&Provider{ + Schema: tc.Schema, + }) + + block := InternalMap(tc.Schema).CoreConfigSchema() + + rawConfig, err := msgpack.Marshal(tc.ConfigVal, block.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + testReq := &tfprotov5.PrepareProviderConfigRequest{ + Config: &tfprotov5.DynamicValue{ + MsgPack: rawConfig, + }, + } + + resp, err := server.PrepareProviderConfig(nil, testReq) + if err != nil { + t.Fatal(err) + } + + if tc.ExpectError != "" && len(resp.Diagnostics) > 0 { + for _, d := range resp.Diagnostics { + if !strings.Contains(d.Summary, tc.ExpectError) { + t.Fatalf("Unexpected error: %s/%s", d.Summary, d.Detail) + } + } + return + } + + // we should have no errors past this point + for _, d := range resp.Diagnostics { + if d.Severity == tfprotov5.DiagnosticSeverityError { + t.Fatal(resp.Diagnostics) + } + } + + val, err := msgpack.Unmarshal(resp.PreparedConfig.MsgPack, block.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + if tc.ExpectConfig.GoString() != val.GoString() { + t.Fatalf("\nexpected: %#v\ngot: %#v", tc.ExpectConfig, val) + } + }) + } +} + +func TestGetSchemaTimeouts(t *testing.T) { + r := &Resource{ + SchemaVersion: 4, + Timeouts: &ResourceTimeout{ + Create: DefaultTimeout(time.Second), + Read: DefaultTimeout(2 * time.Second), + Update: DefaultTimeout(3 * time.Second), + Default: DefaultTimeout(10 * time.Second), + }, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + } + + // verify that the timeouts appear in the schema as defined + block := r.CoreConfigSchema() + timeoutsBlock := block.BlockTypes["timeouts"] + if timeoutsBlock == nil { + t.Fatal("missing timeouts in schema") + } + + if timeoutsBlock.Attributes["create"] == nil { + t.Fatal("missing create timeout in schema") + } + if timeoutsBlock.Attributes["read"] == nil { + t.Fatal("missing read timeout in schema") + } + if timeoutsBlock.Attributes["update"] == nil { + t.Fatal("missing update timeout in schema") + } + if d := timeoutsBlock.Attributes["delete"]; d != nil { + t.Fatalf("unexpected delete timeout in schema: %#v", d) + } + if timeoutsBlock.Attributes["default"] == nil { + t.Fatal("missing default timeout in schema") + } +} + +func TestNormalizeNullValues(t *testing.T) { + for i, tc := range []struct { + Src, Dst, Expect cty.Value + Apply bool + }{ + { + // The known set value is copied over the null set value + Src: cty.ObjectVal(map[string]cty.Value{ + "set": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.NullVal(cty.String), + }), + }), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ + "foo": cty.String, + }))), + }), + Expect: cty.ObjectVal(map[string]cty.Value{ + "set": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.NullVal(cty.String), + }), + }), + }), + Apply: true, + }, + { + // A zero set value is kept + Src: cty.ObjectVal(map[string]cty.Value{ + "set": cty.SetValEmpty(cty.String), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "set": cty.SetValEmpty(cty.String), + }), + Expect: cty.ObjectVal(map[string]cty.Value{ + "set": cty.SetValEmpty(cty.String), + }), + }, + { + // The known set value is copied over the null set value + Src: cty.ObjectVal(map[string]cty.Value{ + "set": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.NullVal(cty.String), + }), + }), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ + "foo": cty.String, + }))), + }), + // If we're only in a plan, we can't compare sets at all + Expect: cty.ObjectVal(map[string]cty.Value{ + "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ + "foo": cty.String, + }))), + }), + }, + { + // The empty map is copied over the null map + Src: cty.ObjectVal(map[string]cty.Value{ + "map": cty.MapValEmpty(cty.String), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "map": cty.NullVal(cty.Map(cty.String)), + }), + Expect: cty.ObjectVal(map[string]cty.Value{ + "map": cty.MapValEmpty(cty.String), + }), + Apply: true, + }, + { + // A zero value primitive is copied over a null primitive + Src: cty.ObjectVal(map[string]cty.Value{ + "string": cty.StringVal(""), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "string": cty.NullVal(cty.String), + }), + Expect: cty.ObjectVal(map[string]cty.Value{ + "string": cty.StringVal(""), + }), + Apply: true, + }, + { + // Plan primitives are kept + Src: cty.ObjectVal(map[string]cty.Value{ + "string": cty.NumberIntVal(0), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "string": cty.NullVal(cty.Number), + }), + Expect: cty.ObjectVal(map[string]cty.Value{ + "string": cty.NullVal(cty.Number), + }), + }, + { + // Neither plan nor apply should remove empty strings + Src: cty.ObjectVal(map[string]cty.Value{ + "string": cty.StringVal(""), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "string": cty.NullVal(cty.String), + }), + Expect: cty.ObjectVal(map[string]cty.Value{ + "string": cty.StringVal(""), + }), + }, + { + // Neither plan nor apply should remove empty strings + Src: cty.ObjectVal(map[string]cty.Value{ + "string": cty.StringVal(""), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "string": cty.NullVal(cty.String), + }), + Expect: cty.ObjectVal(map[string]cty.Value{ + "string": cty.StringVal(""), + }), + Apply: true, + }, + { + // The null map is retained, because the src was unknown + Src: cty.ObjectVal(map[string]cty.Value{ + "map": cty.UnknownVal(cty.Map(cty.String)), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "map": cty.NullVal(cty.Map(cty.String)), + }), + Expect: cty.ObjectVal(map[string]cty.Value{ + "map": cty.NullVal(cty.Map(cty.String)), + }), + Apply: true, + }, + { + // the nul set is retained, because the src set contains an unknown value + Src: cty.ObjectVal(map[string]cty.Value{ + "set": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.UnknownVal(cty.String), + }), + }), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ + "foo": cty.String, + }))), + }), + Expect: cty.ObjectVal(map[string]cty.Value{ + "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ + "foo": cty.String, + }))), + }), + Apply: true, + }, + { + // Retain don't re-add unexpected planned values in a map + Src: cty.ObjectVal(map[string]cty.Value{ + "map": cty.MapVal(map[string]cty.Value{ + "a": cty.StringVal("a"), + "b": cty.StringVal(""), + }), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "map": cty.MapVal(map[string]cty.Value{ + "a": cty.StringVal("a"), + }), + }), + Expect: cty.ObjectVal(map[string]cty.Value{ + "map": cty.MapVal(map[string]cty.Value{ + "a": cty.StringVal("a"), + }), + }), + }, + { + // Remove extra values after apply + Src: cty.ObjectVal(map[string]cty.Value{ + "map": cty.MapVal(map[string]cty.Value{ + "a": cty.StringVal("a"), + "b": cty.StringVal("b"), + }), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "map": cty.MapVal(map[string]cty.Value{ + "a": cty.StringVal("a"), + }), + }), + Expect: cty.ObjectVal(map[string]cty.Value{ + "map": cty.MapVal(map[string]cty.Value{ + "a": cty.StringVal("a"), + }), + }), + Apply: true, + }, + { + Src: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("a"), + }), + Dst: cty.EmptyObjectVal, + Expect: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + }), + }, + + // a list in an object in a list, going from null to empty + { + Src: cty.ObjectVal(map[string]cty.Value{ + "network_interface": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "network_ip": cty.UnknownVal(cty.String), + "access_config": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String}))), + "address": cty.NullVal(cty.String), + "name": cty.StringVal("nic0"), + })}), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "network_interface": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "network_ip": cty.StringVal("10.128.0.64"), + "access_config": cty.ListValEmpty(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String})), + "address": cty.StringVal("address"), + "name": cty.StringVal("nic0"), + }), + }), + }), + Expect: cty.ObjectVal(map[string]cty.Value{ + "network_interface": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "network_ip": cty.StringVal("10.128.0.64"), + "access_config": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String}))), + "address": cty.StringVal("address"), + "name": cty.StringVal("nic0"), + }), + }), + }), + Apply: true, + }, + + // a list in an object in a list, going from empty to null + { + Src: cty.ObjectVal(map[string]cty.Value{ + "network_interface": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "network_ip": cty.UnknownVal(cty.String), + "access_config": cty.ListValEmpty(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String})), + "address": cty.NullVal(cty.String), + "name": cty.StringVal("nic0"), + })}), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "network_interface": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "network_ip": cty.StringVal("10.128.0.64"), + "access_config": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String}))), + "address": cty.StringVal("address"), + "name": cty.StringVal("nic0"), + }), + }), + }), + Expect: cty.ObjectVal(map[string]cty.Value{ + "network_interface": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "network_ip": cty.StringVal("10.128.0.64"), + "access_config": cty.ListValEmpty(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String})), + "address": cty.StringVal("address"), + "name": cty.StringVal("nic0"), + }), + }), + }), + Apply: true, + }, + // the empty list should be transferred, but the new unknown should not be overridden + { + Src: cty.ObjectVal(map[string]cty.Value{ + "network_interface": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "network_ip": cty.StringVal("10.128.0.64"), + "access_config": cty.ListValEmpty(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String})), + "address": cty.NullVal(cty.String), + "name": cty.StringVal("nic0"), + })}), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "network_interface": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "network_ip": cty.UnknownVal(cty.String), + "access_config": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String}))), + "address": cty.StringVal("address"), + "name": cty.StringVal("nic0"), + }), + }), + }), + Expect: cty.ObjectVal(map[string]cty.Value{ + "network_interface": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "network_ip": cty.UnknownVal(cty.String), + "access_config": cty.ListValEmpty(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String})), + "address": cty.StringVal("address"), + "name": cty.StringVal("nic0"), + }), + }), + }), + }, + { + // fix unknowns added to a map + Src: cty.ObjectVal(map[string]cty.Value{ + "map": cty.MapVal(map[string]cty.Value{ + "a": cty.StringVal("a"), + "b": cty.StringVal(""), + }), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "map": cty.MapVal(map[string]cty.Value{ + "a": cty.StringVal("a"), + "b": cty.UnknownVal(cty.String), + }), + }), + Expect: cty.ObjectVal(map[string]cty.Value{ + "map": cty.MapVal(map[string]cty.Value{ + "a": cty.StringVal("a"), + "b": cty.StringVal(""), + }), + }), + }, + { + // fix unknowns lost from a list + Src: cty.ObjectVal(map[string]cty.Value{ + "top": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "list": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "values": cty.ListVal([]cty.Value{cty.UnknownVal(cty.String)}), + }), + }), + }), + }), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "top": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "list": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "values": cty.NullVal(cty.List(cty.String)), + }), + }), + }), + }), + }), + Expect: cty.ObjectVal(map[string]cty.Value{ + "top": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "list": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "values": cty.ListVal([]cty.Value{cty.UnknownVal(cty.String)}), + }), + }), + }), + }), + }), + }, + { + Src: cty.ObjectVal(map[string]cty.Value{ + "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ + "list": cty.List(cty.String), + }))), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "set": cty.SetValEmpty(cty.Object(map[string]cty.Type{ + "list": cty.List(cty.String), + })), + }), + Expect: cty.ObjectVal(map[string]cty.Value{ + "set": cty.SetValEmpty(cty.Object(map[string]cty.Type{ + "list": cty.List(cty.String), + })), + }), + }, + { + Src: cty.ObjectVal(map[string]cty.Value{ + "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ + "list": cty.List(cty.String), + }))), + }), + Dst: cty.ObjectVal(map[string]cty.Value{ + "set": cty.SetValEmpty(cty.Object(map[string]cty.Type{ + "list": cty.List(cty.String), + })), + }), + Expect: cty.ObjectVal(map[string]cty.Value{ + "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ + "list": cty.List(cty.String), + }))), + }), + Apply: true, + }, + } { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + got := normalizeNullValues(tc.Dst, tc.Src, tc.Apply) + if !got.RawEquals(tc.Expect) { + t.Fatalf("\nexpected: %#v\ngot: %#v\n", tc.Expect, got) + } + }) + } +} + +func TestValidateNulls(t *testing.T) { + for i, tc := range []struct { + Cfg cty.Value + Err bool + }{ + { + Cfg: cty.ObjectVal(map[string]cty.Value{ + "list": cty.ListVal([]cty.Value{ + cty.StringVal("string"), + cty.NullVal(cty.String), + }), + }), + Err: true, + }, + { + Cfg: cty.ObjectVal(map[string]cty.Value{ + "map": cty.MapVal(map[string]cty.Value{ + "string": cty.StringVal("string"), + "null": cty.NullVal(cty.String), + }), + }), + Err: false, + }, + { + Cfg: cty.ObjectVal(map[string]cty.Value{ + "object": cty.ObjectVal(map[string]cty.Value{ + "list": cty.ListVal([]cty.Value{ + cty.StringVal("string"), + cty.NullVal(cty.String), + }), + }), + }), + Err: true, + }, + { + Cfg: cty.ObjectVal(map[string]cty.Value{ + "object": cty.ObjectVal(map[string]cty.Value{ + "list": cty.ListVal([]cty.Value{ + cty.StringVal("string"), + cty.NullVal(cty.String), + }), + "list2": cty.ListVal([]cty.Value{ + cty.StringVal("string"), + cty.NullVal(cty.String), + }), + }), + }), + Err: true, + }, + { + Cfg: cty.ObjectVal(map[string]cty.Value{ + "object": cty.ObjectVal(map[string]cty.Value{ + "list": cty.SetVal([]cty.Value{ + cty.StringVal("string"), + cty.NullVal(cty.String), + }), + }), + }), + Err: true, + }, + } { + t.Run(strconv.Itoa(i), func(t *testing.T) { + d := validateConfigNulls(tc.Cfg, nil) + diags := convert.ProtoToDiags(d) + switch { + case tc.Err: + if !diags.HasError() { + t.Fatal("expected error") + } + default: + for _, d := range diags { + if d.Severity == diag.Error { + t.Fatalf("unexpected error: %q", d) + } + } + } + }) + } +} + +func TestStopContext_grpc(t *testing.T) { + testCases := []struct { + Description string + TestResource *Resource + }{ + { + Description: "CreateContext", + TestResource: &Resource{ + SchemaVersion: 4, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + CreateContext: func(ctx context.Context, rd *ResourceData, _ interface{}) diag.Diagnostics { + <-ctx.Done() + rd.SetId("bar") + return nil + }, + }, + }, + { + Description: "CreateWithoutTimeout", + TestResource: &Resource{ + SchemaVersion: 4, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + CreateWithoutTimeout: func(ctx context.Context, rd *ResourceData, _ interface{}) diag.Diagnostics { + <-ctx.Done() + rd.SetId("bar") + return nil + }, + }, + }, + } + + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.Description, func(t *testing.T) { + server := NewGRPCProviderServer(&Provider{ + ResourcesMap: map[string]*Resource{ + "test": testCase.TestResource, + }, + }) + + schema := testCase.TestResource.CoreConfigSchema() + priorState, err := msgpack.Marshal(cty.NullVal(schema.ImpliedType()), schema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + plannedVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ + "id": cty.UnknownVal(cty.String), + })) + if err != nil { + t.Fatal(err) + } + plannedState, err := msgpack.Marshal(plannedVal, schema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + testReq := &tfprotov5.ApplyResourceChangeRequest{ + TypeName: "test", + PriorState: &tfprotov5.DynamicValue{ + MsgPack: priorState, + }, + PlannedState: &tfprotov5.DynamicValue{ + MsgPack: plannedState, + }, + } + ctx, cancel := context.WithCancel(context.Background()) + ctx = server.StopContext(ctx) + doneCh := make(chan struct{}) + errCh := make(chan error) + go func() { + if _, err := server.ApplyResourceChange(ctx, testReq); err != nil { + errCh <- err + } + close(doneCh) + }() + // GRPC request cancel + cancel() + select { + case <-doneCh: + case err := <-errCh: + if err != nil { + t.Fatal(err) + } + case <-time.After(5 * time.Second): + t.Fatal("context cancel did not propagate") + } + }) + } +} + +func TestStopContext_stop(t *testing.T) { + testCases := []struct { + Description string + TestResource *Resource + }{ + { + Description: "CreateContext", + TestResource: &Resource{ + SchemaVersion: 4, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + CreateContext: func(ctx context.Context, rd *ResourceData, _ interface{}) diag.Diagnostics { + <-ctx.Done() + rd.SetId("bar") + return nil + }, + }, + }, + { + Description: "CreateWithoutTimeout", + TestResource: &Resource{ + SchemaVersion: 4, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + CreateWithoutTimeout: func(ctx context.Context, rd *ResourceData, _ interface{}) diag.Diagnostics { + <-ctx.Done() + rd.SetId("bar") + return nil + }, + }, + }, + } + + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.Description, func(t *testing.T) { + server := NewGRPCProviderServer(&Provider{ + ResourcesMap: map[string]*Resource{ + "test": testCase.TestResource, + }, + }) + + schema := testCase.TestResource.CoreConfigSchema() + priorState, err := msgpack.Marshal(cty.NullVal(schema.ImpliedType()), schema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + plannedVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ + "id": cty.UnknownVal(cty.String), + })) + if err != nil { + t.Fatal(err) + } + plannedState, err := msgpack.Marshal(plannedVal, schema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + testReq := &tfprotov5.ApplyResourceChangeRequest{ + TypeName: "test", + PriorState: &tfprotov5.DynamicValue{ + MsgPack: priorState, + }, + PlannedState: &tfprotov5.DynamicValue{ + MsgPack: plannedState, + }, + } + + ctx := server.StopContext(context.Background()) + doneCh := make(chan struct{}) + errCh := make(chan error) + go func() { + if _, err := server.ApplyResourceChange(ctx, testReq); err != nil { + errCh <- err + } + close(doneCh) + }() + server.StopProvider(context.Background(), &tfprotov5.StopProviderRequest{}) + select { + case <-doneCh: + case err := <-errCh: + if err != nil { + t.Fatal(err) + } + case <-time.After(5 * time.Second): + t.Fatal("Stop message did not cancel request context") + } + }) + } +} + +func TestStopContext_stopReset(t *testing.T) { + testCases := []struct { + Description string + TestResource *Resource + }{ + { + Description: "CreateContext", + TestResource: &Resource{ + SchemaVersion: 4, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + CreateContext: func(ctx context.Context, rd *ResourceData, _ interface{}) diag.Diagnostics { + <-ctx.Done() + rd.SetId("bar") + return nil + }, + }, + }, + { + Description: "CreateWithoutTimeout", + TestResource: &Resource{ + SchemaVersion: 4, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + CreateWithoutTimeout: func(ctx context.Context, rd *ResourceData, _ interface{}) diag.Diagnostics { + <-ctx.Done() + rd.SetId("bar") + return nil + }, + }, + }, + } + + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.Description, func(t *testing.T) { + server := NewGRPCProviderServer(&Provider{ + ResourcesMap: map[string]*Resource{ + "test": testCase.TestResource, + }, + }) + + schema := testCase.TestResource.CoreConfigSchema() + priorState, err := msgpack.Marshal(cty.NullVal(schema.ImpliedType()), schema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + plannedVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ + "id": cty.UnknownVal(cty.String), + })) + if err != nil { + t.Fatal(err) + } + plannedState, err := msgpack.Marshal(plannedVal, schema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + testReq := &tfprotov5.ApplyResourceChangeRequest{ + TypeName: "test", + PriorState: &tfprotov5.DynamicValue{ + MsgPack: priorState, + }, + PlannedState: &tfprotov5.DynamicValue{ + MsgPack: plannedState, + }, + } + + // test first stop + ctx := server.StopContext(context.Background()) + if ctx.Err() != nil { + t.Fatal("StopContext does not produce a non-closed context") + } + doneCh := make(chan struct{}) + errCh := make(chan error) + go func(d chan struct{}) { + if _, err := server.ApplyResourceChange(ctx, testReq); err != nil { + errCh <- err + } + close(d) + }(doneCh) + server.StopProvider(context.Background(), &tfprotov5.StopProviderRequest{}) + select { + case <-doneCh: + case err := <-errCh: + if err != nil { + t.Fatal(err) + } + case <-time.After(5 * time.Second): + t.Fatal("Stop message did not cancel request context") + } + + // test internal stop synchronization was reset + ctx = server.StopContext(context.Background()) + if ctx.Err() != nil { + t.Fatal("StopContext does not produce a non-closed context") + } + doneCh = make(chan struct{}) + errCh = make(chan error) + go func(d chan struct{}) { + if _, err := server.ApplyResourceChange(ctx, testReq); err != nil { + errCh <- err + } + close(d) + }(doneCh) + server.StopProvider(context.Background(), &tfprotov5.StopProviderRequest{}) + select { + case <-doneCh: + case err := <-errCh: + if err != nil { + t.Fatal(err) + } + case <-time.After(5 * time.Second): + t.Fatal("Stop message did not cancel request context") + } + }) + } +} + +func Test_pathToAttributePath_noSteps(t *testing.T) { + res := pathToAttributePath(cty.Path{}) + if res != nil { + t.Errorf("Expected nil attribute path, got %+v", res) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/json.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/json.go new file mode 100644 index 00000000..265099a6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/json.go @@ -0,0 +1,12 @@ +package schema + +import ( + "bytes" + "encoding/json" +) + +func unmarshalJSON(data []byte, v interface{}) error { + dec := json.NewDecoder(bytes.NewReader(data)) + dec.UseNumber() + return dec.Decode(v) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/provider.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/provider.go index bbea5dbd..25148e17 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/provider.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/provider.go @@ -4,19 +4,39 @@ import ( "context" "errors" "fmt" + "log" + "os" "sort" - "sync" + "strings" "github.com/hashicorp/go-multierror" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/terraform" + + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" + "github.com/hashicorp/terraform-plugin-sdk/v2/meta" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) +const uaEnvVar = "TF_APPEND_USER_AGENT" + var ReservedProviderFields = []string{ "alias", "version", } +// StopContext returns a context safe for global use that will cancel +// when Terraform requests a stop. This function should only be called +// within a ConfigureContextFunc, passing in the request scoped context +// received in that method. +// +// Deprecated: The use of a global context is discouraged. Please use the new +// context aware CRUD methods. +func StopContext(ctx context.Context) (context.Context, bool) { + stopContext, ok := ctx.Value(StopContextKey).(context.Context) + return stopContext, ok +} + // Provider represents a resource provider in Terraform, and properly // implements all of the ResourceProvider API. // @@ -50,35 +70,43 @@ type Provider struct { // and must *not* implement Create, Update or Delete. DataSourcesMap map[string]*Resource + // ProviderMetaSchema is the schema for the configuration of the meta + // information for this provider. If this provider has no meta info, + // this can be omitted. This functionality is currently experimental + // and subject to change or break without warning; it should only be + // used by providers that are collaborating on its use with the + // Terraform team. + ProviderMetaSchema map[string]*Schema + // ConfigureFunc is a function for configuring the provider. If the // provider doesn't need to be configured, this can be omitted. // - // See the ConfigureFunc documentation for more information. + // Deprecated: Please use ConfigureContextFunc instead. ConfigureFunc ConfigureFunc - // MetaReset is called by TestReset to reset any state stored in the meta - // interface. This is especially important if the StopContext is stored by - // the provider. - MetaReset func() error + // ConfigureContextFunc is a function for configuring the provider. If the + // provider doesn't need to be configured, this can be omitted. This function + // receives a context.Context that will cancel when Terraform sends a + // cancellation signal. This function can yield Diagnostics. + ConfigureContextFunc ConfigureContextFunc meta interface{} - // a mutex is required because TestReset can directly replace the stopCtx - stopMu sync.Mutex - stopCtx context.Context - stopCtxCancel context.CancelFunc - stopOnce sync.Once - TerraformVersion string } // ConfigureFunc is the function used to configure a Provider. // +// Deprecated: Please use ConfigureContextFunc +type ConfigureFunc func(*ResourceData) (interface{}, error) + +// ConfigureContextFunc is the function used to configure a Provider. +// // The interface{} value returned by this function is stored and passed into // the subsequent resources as the meta parameter. This return value is // usually used to pass along a configured API client, a configuration // structure, etc. -type ConfigureFunc func(*ResourceData) (interface{}, error) +type ConfigureContextFunc func(context.Context, *ResourceData) (interface{}, diag.Diagnostics) // InternalValidate should be called to validate the structure // of the provider. @@ -91,6 +119,10 @@ func (p *Provider) InternalValidate() error { return errors.New("provider is nil") } + if p.ConfigureFunc != nil && p.ConfigureContextFunc != nil { + return errors.New("ConfigureFunc and ConfigureContextFunc must not both be set") + } + var validationErrors error sm := schemaMap(p.Schema) if err := sm.InternalValidate(sm); err != nil { @@ -98,7 +130,7 @@ func (p *Provider) InternalValidate() error { } // Provider-specific checks - for k, _ := range sm { + for k := range sm { if isReservedProviderFieldName(k) { return fmt.Errorf("%s is a reserved field name for a provider", k) } @@ -141,58 +173,13 @@ func (p *Provider) SetMeta(v interface{}) { p.meta = v } -// Stopped reports whether the provider has been stopped or not. -func (p *Provider) Stopped() bool { - ctx := p.StopContext() - select { - case <-ctx.Done(): - return true - default: - return false - } -} - -// StopCh returns a channel that is closed once the provider is stopped. -func (p *Provider) StopContext() context.Context { - p.stopOnce.Do(p.stopInit) - - p.stopMu.Lock() - defer p.stopMu.Unlock() - - return p.stopCtx -} - -func (p *Provider) stopInit() { - p.stopMu.Lock() - defer p.stopMu.Unlock() - - p.stopCtx, p.stopCtxCancel = context.WithCancel(context.Background()) -} - -// Stop implementation of terraform.ResourceProvider interface. -func (p *Provider) Stop() error { - p.stopOnce.Do(p.stopInit) - - p.stopMu.Lock() - defer p.stopMu.Unlock() - - p.stopCtxCancel() - return nil -} - -// TestReset resets any state stored in the Provider, and will call TestReset -// on Meta if it implements the TestProvider interface. -// This may be used to reset the schema.Provider at the start of a test, and is -// automatically called by resource.Test. -func (p *Provider) TestReset() error { - p.stopInit() - if p.MetaReset != nil { - return p.MetaReset() - } - return nil -} - -// GetSchema implementation of terraform.ResourceProvider interface +// GetSchema returns the config schema for the main provider +// configuration, as would appear in a "provider" block in the +// configuration files. +// +// Currently not all providers support schema. Callers must therefore +// first call Resources and DataSources and ensure that at least one +// resource or data source has the SchemaAvailable flag set. func (p *Provider) GetSchema(req *terraform.ProviderSchemaRequest) (*terraform.ProviderSchema, error) { resourceTypes := map[string]*configschema.Block{} dataSources := map[string]*configschema.Block{} @@ -215,41 +202,62 @@ func (p *Provider) GetSchema(req *terraform.ProviderSchemaRequest) (*terraform.P }, nil } -// Input implementation of terraform.ResourceProvider interface. -func (p *Provider) Input( - input terraform.UIInput, - c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) { - return schemaMap(p.Schema).Input(input, c) -} - -// Validate implementation of terraform.ResourceProvider interface. -func (p *Provider) Validate(c *terraform.ResourceConfig) ([]string, []error) { +// Validate is called once at the beginning with the raw configuration +// (no interpolation done) and can return diagnostics +// +// This is called once with the provider configuration only. It may not +// be called at all if no provider configuration is given. +// +// This should not assume that any values of the configurations are valid. +// The primary use case of this call is to check that required keys are +// set. +func (p *Provider) Validate(c *terraform.ResourceConfig) diag.Diagnostics { if err := p.InternalValidate(); err != nil { - return nil, []error{fmt.Errorf( - "Internal validation of the provider failed! This is always a bug\n"+ - "with the provider itself, and not a user issue. Please report\n"+ - "this bug:\n\n%s", err)} + return []diag.Diagnostic{ + { + Severity: diag.Error, + Summary: "InternalValidate", + Detail: fmt.Sprintf("Internal validation of the provider failed! This is always a bug\n"+ + "with the provider itself, and not a user issue. Please report\n"+ + "this bug:\n\n%s", err), + }, + } } return schemaMap(p.Schema).Validate(c) } -// ValidateResource implementation of terraform.ResourceProvider interface. +// ValidateResource is called once at the beginning with the raw +// configuration (no interpolation done) and can return diagnostics. +// +// This is called once per resource. +// +// This should not assume any of the values in the resource configuration +// are valid since it is possible they have to be interpolated still. +// The primary use case of this call is to check that the required keys +// are set and that the general structure is correct. func (p *Provider) ValidateResource( - t string, c *terraform.ResourceConfig) ([]string, []error) { + t string, c *terraform.ResourceConfig) diag.Diagnostics { r, ok := p.ResourcesMap[t] if !ok { - return nil, []error{fmt.Errorf( - "Provider doesn't support resource: %s", t)} + return []diag.Diagnostic{ + { + Severity: diag.Error, + Summary: fmt.Sprintf("Provider doesn't support resource: %s", t), + }, + } } return r.Validate(c) } -// Configure implementation of terraform.ResourceProvider interface. -func (p *Provider) Configure(c *terraform.ResourceConfig) error { +// Configure configures the provider itself with the configuration +// given. This is useful for setting things like access keys. +// +// This won't be called at all if no provider configuration is given. +func (p *Provider) Configure(ctx context.Context, c *terraform.ResourceConfig) diag.Diagnostics { // No configuration - if p.ConfigureFunc == nil { + if p.ConfigureFunc == nil && p.ConfigureContextFunc == nil { return nil } @@ -257,83 +265,36 @@ func (p *Provider) Configure(c *terraform.ResourceConfig) error { // Get a ResourceData for this configuration. To do this, we actually // generate an intermediary "diff" although that is never exposed. - diff, err := sm.Diff(nil, c, nil, p.meta, true) + diff, err := sm.Diff(ctx, nil, c, nil, p.meta, true) if err != nil { - return err + return diag.FromErr(err) } data, err := sm.Data(nil, diff) if err != nil { - return err + return diag.FromErr(err) } - if p.TerraformVersion == "" { - // Terraform 0.12 introduced this field to the protocol - // We can therefore assume that if it's unconfigured at this point, it's 0.10 or 0.11 - p.TerraformVersion = "0.11+compatible" + if p.ConfigureFunc != nil { + meta, err := p.ConfigureFunc(data) + if err != nil { + return diag.FromErr(err) + } + p.meta = meta } - meta, err := p.ConfigureFunc(data) - if err != nil { - return err + if p.ConfigureContextFunc != nil { + meta, diags := p.ConfigureContextFunc(ctx, data) + if diags.HasError() { + return diags + } + p.meta = meta } - p.meta = meta return nil } -// Apply implementation of terraform.ResourceProvider interface. -func (p *Provider) Apply( - info *terraform.InstanceInfo, - s *terraform.InstanceState, - d *terraform.InstanceDiff) (*terraform.InstanceState, error) { - r, ok := p.ResourcesMap[info.Type] - if !ok { - return nil, fmt.Errorf("unknown resource type: %s", info.Type) - } - - return r.Apply(s, d, p.meta) -} - -// Diff implementation of terraform.ResourceProvider interface. -func (p *Provider) Diff( - info *terraform.InstanceInfo, - s *terraform.InstanceState, - c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { - r, ok := p.ResourcesMap[info.Type] - if !ok { - return nil, fmt.Errorf("unknown resource type: %s", info.Type) - } - - return r.Diff(s, c, p.meta) -} - -// SimpleDiff is used by the new protocol wrappers to get a diff that doesn't -// attempt to calculate ignore_changes. -func (p *Provider) SimpleDiff( - info *terraform.InstanceInfo, - s *terraform.InstanceState, - c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { - r, ok := p.ResourcesMap[info.Type] - if !ok { - return nil, fmt.Errorf("unknown resource type: %s", info.Type) - } - - return r.simpleDiff(s, c, p.meta) -} - -// Refresh implementation of terraform.ResourceProvider interface. -func (p *Provider) Refresh( - info *terraform.InstanceInfo, - s *terraform.InstanceState) (*terraform.InstanceState, error) { - r, ok := p.ResourcesMap[info.Type] - if !ok { - return nil, fmt.Errorf("unknown resource type: %s", info.Type) - } - - return r.Refresh(s, p.meta) -} - -// Resources implementation of terraform.ResourceProvider interface. +// Resources returns all the available resource types that this provider +// knows how to manage. func (p *Provider) Resources() []terraform.ResourceType { keys := make([]string, 0, len(p.ResourcesMap)) for k := range p.ResourcesMap { @@ -364,7 +325,22 @@ func (p *Provider) Resources() []terraform.ResourceType { return result } +// ImportState requests that the given resource be imported. +// +// The returned InstanceState only requires ID be set. Importing +// will always call Refresh after the state to complete it. +// +// IMPORTANT: InstanceState doesn't have the resource type attached +// to it. A type must be specified on the state via the Ephemeral +// field on the state. +// +// This function can return multiple states. Normally, an import +// will map 1:1 to a physical resource. However, some resources map +// to multiple. For example, an AWS security group may contain many rules. +// Each rule is represented by a separate resource in Terraform, +// therefore multiple states are returned. func (p *Provider) ImportState( + ctx context.Context, info *terraform.InstanceInfo, id string) ([]*terraform.InstanceState, error) { // Find the resource @@ -385,9 +361,13 @@ func (p *Provider) ImportState( // Call the import function results := []*ResourceData{data} - if r.Importer.State != nil { + if r.Importer.State != nil || r.Importer.StateContext != nil { var err error - results, err = r.Importer.State(data, p.meta) + if r.Importer.StateContext != nil { + results, err = r.Importer.StateContext(ctx, data, p.meta) + } else { + results, err = r.Importer.State(data, p.meta) + } if err != nil { return nil, err } @@ -413,48 +393,35 @@ func (p *Provider) ImportState( return states, nil } -// ValidateDataSource implementation of terraform.ResourceProvider interface. +// ValidateDataSource is called once at the beginning with the raw +// configuration (no interpolation done) and can return diagnostics. +// +// This is called once per data source instance. +// +// This should not assume any of the values in the resource configuration +// are valid since it is possible they have to be interpolated still. +// The primary use case of this call is to check that the required keys +// are set and that the general structure is correct. func (p *Provider) ValidateDataSource( - t string, c *terraform.ResourceConfig) ([]string, []error) { + t string, c *terraform.ResourceConfig) diag.Diagnostics { r, ok := p.DataSourcesMap[t] if !ok { - return nil, []error{fmt.Errorf( - "Provider doesn't support data source: %s", t)} + return []diag.Diagnostic{ + { + Severity: diag.Error, + Summary: fmt.Sprintf("Provider doesn't support data source: %s", t), + }, + } } return r.Validate(c) } -// ReadDataDiff implementation of terraform.ResourceProvider interface. -func (p *Provider) ReadDataDiff( - info *terraform.InstanceInfo, - c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { - - r, ok := p.DataSourcesMap[info.Type] - if !ok { - return nil, fmt.Errorf("unknown data source: %s", info.Type) - } - - return r.Diff(nil, c, p.meta) -} - -// RefreshData implementation of terraform.ResourceProvider interface. -func (p *Provider) ReadDataApply( - info *terraform.InstanceInfo, - d *terraform.InstanceDiff) (*terraform.InstanceState, error) { - - r, ok := p.DataSourcesMap[info.Type] - if !ok { - return nil, fmt.Errorf("unknown data source: %s", info.Type) - } - - return r.ReadDataApply(d, p.meta) -} - -// DataSources implementation of terraform.ResourceProvider interface. +// DataSources returns all of the available data sources that this +// provider implements. func (p *Provider) DataSources() []terraform.DataSource { keys := make([]string, 0, len(p.DataSourcesMap)) - for k, _ := range p.DataSourcesMap { + for k := range p.DataSourcesMap { keys = append(keys, k) } sort.Strings(keys) @@ -472,3 +439,36 @@ func (p *Provider) DataSources() []terraform.DataSource { return result } + +// UserAgent returns a string suitable for use in the User-Agent header of +// requests generated by the provider. The generated string contains the +// version of Terraform, the Plugin SDK, and the provider used to generate the +// request. `name` should be the hyphen-separated reporting name of the +// provider, and `version` should be the version of the provider. +// +// If TF_APPEND_USER_AGENT is set, its value will be appended to the returned +// string. +func (p *Provider) UserAgent(name, version string) string { + ua := fmt.Sprintf("Terraform/%s (+https://www.terraform.io) Terraform-Plugin-SDK/%s", p.TerraformVersion, meta.SDKVersionString()) + if name != "" { + ua += " " + name + if version != "" { + ua += "/" + version + } + } + + if add := os.Getenv(uaEnvVar); add != "" { + add = strings.TrimSpace(add) + if len(add) > 0 { + ua += " " + add + log.Printf("[DEBUG] Using modified User-Agent: %s", ua) + } + } + + return ua +} + +// GRPCProvider returns a gRPC server, for use with terraform-plugin-mux. +func (p *Provider) GRPCProvider() tfprotov5.ProviderServer { + return NewGRPCProviderServer(p) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/provider_go115_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/provider_go115_test.go new file mode 100644 index 00000000..9f9a0863 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/provider_go115_test.go @@ -0,0 +1,5 @@ +// +build go1.15 + +package schema + +const invalidDurationErrMsg = "time: invalid duration \"invalid\"" diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/provider_prego115_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/provider_prego115_test.go new file mode 100644 index 00000000..50d9d3d0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/provider_prego115_test.go @@ -0,0 +1,5 @@ +// +build !go1.15 + +package schema + +const invalidDurationErrMsg = "time: invalid duration invalid" diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/provider_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/provider_test.go new file mode 100644 index 00000000..3279ee2f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/provider_test.go @@ -0,0 +1,828 @@ +package schema + +import ( + "context" + "fmt" + "os" + "reflect" + "strings" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/go-cty/cty" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/diagutils" + "github.com/hashicorp/terraform-plugin-sdk/v2/meta" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestProviderGetSchema(t *testing.T) { + // This functionality is already broadly tested in core_schema_test.go, + // so this is just to ensure that the call passes through correctly. + p := &Provider{ + Schema: map[string]*Schema{ + "bar": { + Type: TypeString, + Required: true, + }, + }, + ResourcesMap: map[string]*Resource{ + "foo": { + Schema: map[string]*Schema{ + "bar": { + Type: TypeString, + Required: true, + }, + }, + }, + }, + DataSourcesMap: map[string]*Resource{ + "baz": { + Schema: map[string]*Schema{ + "bur": { + Type: TypeString, + Required: true, + }, + }, + }, + }, + } + + want := &terraform.ProviderSchema{ + Provider: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bar": { + Type: cty.String, + Required: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{}, + }, + ResourceTypes: map[string]*configschema.Block{ + "foo": testResource(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bar": { + Type: cty.String, + Required: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{}, + }), + }, + DataSources: map[string]*configschema.Block{ + "baz": testResource(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bur": { + Type: cty.String, + Required: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{}, + }), + }, + } + got, err := p.GetSchema(&terraform.ProviderSchemaRequest{ + ResourceTypes: []string{"foo", "bar"}, + DataSources: []string{"baz", "bar"}, + }) + if err != nil { + t.Fatalf("unexpected error %s", err) + } + + if !cmp.Equal(got, want, equateEmpty, typeComparer) { + t.Error("wrong result:\n", cmp.Diff(got, want, equateEmpty, typeComparer)) + } +} + +func TestProviderConfigure(t *testing.T) { + cases := []struct { + P *Provider + Config map[string]interface{} + Err bool + }{ + { + P: &Provider{}, + Config: nil, + Err: false, + }, + + { + P: &Provider{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + + ConfigureFunc: func(d *ResourceData) (interface{}, error) { + if d.Get("foo").(int) == 42 { + return nil, nil + } + + return nil, fmt.Errorf("nope") + }, + }, + Config: map[string]interface{}{ + "foo": 42, + }, + Err: false, + }, + + { + P: &Provider{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + + ConfigureContextFunc: func(ctx context.Context, d *ResourceData) (interface{}, diag.Diagnostics) { + if d.Get("foo").(int) == 42 { + return nil, nil + } + + return nil, diag.Errorf("nope") + }, + }, + Config: map[string]interface{}{ + "foo": 42, + }, + Err: false, + }, + + { + P: &Provider{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + + ConfigureFunc: func(d *ResourceData) (interface{}, error) { + if d.Get("foo").(int) == 42 { + return nil, nil + } + + return nil, fmt.Errorf("nope") + }, + }, + Config: map[string]interface{}{ + "foo": 52, + }, + Err: true, + }, + } + + for i, tc := range cases { + c := terraform.NewResourceConfigRaw(tc.Config) + diags := tc.P.Configure(context.Background(), c) + if diags.HasError() != tc.Err { + t.Fatalf("%d: %s", i, diagutils.ErrorDiags(diags)) + } + } +} + +func TestProviderResources(t *testing.T) { + cases := []struct { + P *Provider + Result []terraform.ResourceType + }{ + { + P: &Provider{}, + Result: []terraform.ResourceType{}, + }, + + { + P: &Provider{ + ResourcesMap: map[string]*Resource{ + "foo": nil, + "bar": nil, + }, + }, + Result: []terraform.ResourceType{ + {Name: "bar", SchemaAvailable: true}, + {Name: "foo", SchemaAvailable: true}, + }, + }, + + { + P: &Provider{ + ResourcesMap: map[string]*Resource{ + "foo": nil, + "bar": {Importer: &ResourceImporter{}}, + "baz": nil, + }, + }, + Result: []terraform.ResourceType{ + {Name: "bar", Importable: true, SchemaAvailable: true}, + {Name: "baz", SchemaAvailable: true}, + {Name: "foo", SchemaAvailable: true}, + }, + }, + } + + for i, tc := range cases { + actual := tc.P.Resources() + if !reflect.DeepEqual(actual, tc.Result) { + t.Fatalf("%d: %#v", i, actual) + } + } +} + +func TestProviderDataSources(t *testing.T) { + cases := []struct { + P *Provider + Result []terraform.DataSource + }{ + { + P: &Provider{}, + Result: []terraform.DataSource{}, + }, + + { + P: &Provider{ + DataSourcesMap: map[string]*Resource{ + "foo": nil, + "bar": nil, + }, + }, + Result: []terraform.DataSource{ + {Name: "bar", SchemaAvailable: true}, + {Name: "foo", SchemaAvailable: true}, + }, + }, + } + + for i, tc := range cases { + actual := tc.P.DataSources() + if !reflect.DeepEqual(actual, tc.Result) { + t.Fatalf("%d: got %#v; want %#v", i, actual, tc.Result) + } + } +} + +func TestProviderValidate(t *testing.T) { + cases := []struct { + P *Provider + Config map[string]interface{} + Err bool + }{ + { + P: &Provider{ + Schema: map[string]*Schema{ + "foo": {}, + }, + }, + Config: nil, + Err: true, + }, + } + + for i, tc := range cases { + c := terraform.NewResourceConfigRaw(tc.Config) + diags := tc.P.Validate(c) + if diags.HasError() != tc.Err { + t.Fatalf("%d: %#v", i, diags) + } + } +} + +func TestProviderValidate_attributePath(t *testing.T) { + cases := []struct { + P *Provider + Config map[string]interface{} + ExpectedDiags diag.Diagnostics + }{ + { // legacy validate path automatically built, even across list + P: &Provider{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "bar": { + Type: TypeString, + Required: true, + ValidateFunc: func(v interface{}, k string) ([]string, []error) { + return []string{"warn"}, []error{fmt.Errorf("error")} + }, + }, + }, + }, + }, + }, + }, + Config: map[string]interface{}{ + "foo": []interface{}{ + map[string]interface{}{ + "bar": "baz", + }, + }, + }, + ExpectedDiags: diag.Diagnostics{ + { + Severity: diag.Warning, + AttributePath: cty.Path{cty.GetAttrStep{Name: "foo"}, cty.IndexStep{Key: cty.NumberIntVal(0)}, cty.GetAttrStep{Name: "bar"}}, + }, + { + Severity: diag.Error, + AttributePath: cty.Path{cty.GetAttrStep{Name: "foo"}, cty.IndexStep{Key: cty.NumberIntVal(0)}, cty.GetAttrStep{Name: "bar"}}, + }, + }, + }, + { // validate path automatically built, even across list + P: &Provider{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "bar": { + Type: TypeString, + Required: true, + ValidateDiagFunc: func(v interface{}, path cty.Path) diag.Diagnostics { + return diag.Diagnostics{{Severity: diag.Error}} + }, + }, + }, + }, + }, + }, + }, + Config: map[string]interface{}{ + "foo": []interface{}{ + map[string]interface{}{ + "bar": "baz", + }, + }, + }, + ExpectedDiags: diag.Diagnostics{ + { + Severity: diag.Error, + AttributePath: cty.Path{cty.GetAttrStep{Name: "foo"}, cty.IndexStep{Key: cty.NumberIntVal(0)}, cty.GetAttrStep{Name: "bar"}}, + }, + }, + }, + { // path is truncated at typeset + P: &Provider{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeSet, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "bar": { + Type: TypeString, + Required: true, + ValidateDiagFunc: func(v interface{}, path cty.Path) diag.Diagnostics { + return diag.Diagnostics{{Severity: diag.Error, AttributePath: cty.Path{cty.GetAttrStep{Name: "doesnotmatter"}}}} + }, + }, + }, + }, + }, + }, + }, + Config: map[string]interface{}{ + "foo": []interface{}{ + map[string]interface{}{ + "bar": "baz", + }, + }, + }, + ExpectedDiags: diag.Diagnostics{ + { + Severity: diag.Error, + AttributePath: cty.Path{cty.GetAttrStep{Name: "foo"}}, + }, + }, + }, + { // relative path is appended + P: &Provider{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "bar": { + Type: TypeMap, + Required: true, + ValidateDiagFunc: func(v interface{}, path cty.Path) diag.Diagnostics { + return diag.Diagnostics{{Severity: diag.Error, AttributePath: cty.Path{cty.IndexStep{Key: cty.StringVal("mapkey")}}}} + }, + }, + }, + }, + }, + }, + }, + Config: map[string]interface{}{ + "foo": []interface{}{ + map[string]interface{}{ + "bar": map[string]interface{}{ + "mapkey": "val", + }, + }, + }, + }, + ExpectedDiags: diag.Diagnostics{ + { + Severity: diag.Error, + AttributePath: cty.Path{cty.GetAttrStep{Name: "foo"}, cty.IndexStep{Key: cty.NumberIntVal(0)}, cty.GetAttrStep{Name: "bar"}, cty.IndexStep{Key: cty.StringVal("mapkey")}}, + }, + }, + }, + { // absolute path is not altered + P: &Provider{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "bar": { + Type: TypeMap, + Required: true, + ValidateDiagFunc: func(v interface{}, path cty.Path) diag.Diagnostics { + return diag.Diagnostics{{Severity: diag.Error, AttributePath: append(path, cty.IndexStep{Key: cty.StringVal("mapkey")})}} + }, + }, + }, + }, + }, + }, + }, + Config: map[string]interface{}{ + "foo": []interface{}{ + map[string]interface{}{ + "bar": map[string]interface{}{ + "mapkey": "val", + }, + }, + }, + }, + ExpectedDiags: diag.Diagnostics{ + { + Severity: diag.Error, + AttributePath: cty.Path{cty.GetAttrStep{Name: "foo"}, cty.IndexStep{Key: cty.NumberIntVal(0)}, cty.GetAttrStep{Name: "bar"}, cty.IndexStep{Key: cty.StringVal("mapkey")}}, + }, + }, + }, + } + + for i, tc := range cases { + c := terraform.NewResourceConfigRaw(tc.Config) + diags := tc.P.Validate(c) + if len(diags) != len(tc.ExpectedDiags) { + t.Fatalf("%d: wrong number of diags, expected %d, got %d", i, len(tc.ExpectedDiags), len(diags)) + } + for j := range diags { + if diags[j].Severity != tc.ExpectedDiags[j].Severity { + t.Fatalf("%d: expected severity %v, got %v", i, tc.ExpectedDiags[j].Severity, diags[j].Severity) + } + if !diags[j].AttributePath.Equals(tc.ExpectedDiags[j].AttributePath) { + t.Fatalf("%d: attribute paths do not match expected: %v, got %v", i, tc.ExpectedDiags[j].AttributePath, diags[j].AttributePath) + } + } + } +} + +func TestProviderDiff_legacyTimeoutType(t *testing.T) { + p := &Provider{ + ResourcesMap: map[string]*Resource{ + "blah": { + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + Timeouts: &ResourceTimeout{ + Create: DefaultTimeout(10 * time.Minute), + }, + }, + }, + } + + invalidCfg := map[string]interface{}{ + "foo": 42, + "timeouts": []interface{}{ + map[string]interface{}{ + "create": "40m", + }, + }, + } + ic := terraform.NewResourceConfigRaw(invalidCfg) + _, err := p.ResourcesMap["blah"].Diff( + context.Background(), + nil, + ic, + p.Meta(), + ) + if err != nil { + t.Fatal(err) + } +} + +func TestProviderDiff_timeoutInvalidValue(t *testing.T) { + p := &Provider{ + ResourcesMap: map[string]*Resource{ + "blah": { + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + Timeouts: &ResourceTimeout{ + Create: DefaultTimeout(10 * time.Minute), + }, + }, + }, + } + + invalidCfg := map[string]interface{}{ + "foo": 42, + "timeouts": map[string]interface{}{ + "create": "invalid", + }, + } + ic := terraform.NewResourceConfigRaw(invalidCfg) + _, err := p.ResourcesMap["blah"].Diff( + context.Background(), + nil, + ic, + p.Meta(), + ) + if err == nil { + t.Fatal("Expected provider.Diff to fail with invalid timeout value") + } + if !strings.Contains(err.Error(), invalidDurationErrMsg) { + t.Fatalf("Unexpected error message: %q\nExpected message to contain %q", + err.Error(), + invalidDurationErrMsg) + } +} + +func TestProviderValidateResource(t *testing.T) { + cases := []struct { + P *Provider + Type string + Config map[string]interface{} + Err bool + }{ + { + P: &Provider{}, + Type: "foo", + Config: nil, + Err: true, + }, + + { + P: &Provider{ + ResourcesMap: map[string]*Resource{ + "foo": {}, + }, + }, + Type: "foo", + Config: nil, + Err: false, + }, + } + + for i, tc := range cases { + c := terraform.NewResourceConfigRaw(tc.Config) + diags := tc.P.ValidateResource(tc.Type, c) + if diags.HasError() != tc.Err { + t.Fatalf("%d: %#v", i, diags) + } + } +} + +func TestProviderImportState_default(t *testing.T) { + p := &Provider{ + ResourcesMap: map[string]*Resource{ + "foo": { + Importer: &ResourceImporter{}, + }, + }, + } + + states, err := p.ImportState(context.Background(), &terraform.InstanceInfo{ + Type: "foo", + }, "bar") + if err != nil { + t.Fatalf("err: %s", err) + } + + if len(states) != 1 { + t.Fatalf("bad: %#v", states) + } + if states[0].ID != "bar" { + t.Fatalf("bad: %#v", states) + } +} + +func TestProviderImportState_setsId(t *testing.T) { + var val string + stateFunc := func(d *ResourceData, meta interface{}) ([]*ResourceData, error) { + val = d.Id() + return []*ResourceData{d}, nil + } + + p := &Provider{ + ResourcesMap: map[string]*Resource{ + "foo": { + Importer: &ResourceImporter{ + State: stateFunc, + }, + }, + }, + } + + _, err := p.ImportState(context.Background(), &terraform.InstanceInfo{ + Type: "foo", + }, "bar") + if err != nil { + t.Fatalf("err: %s", err) + } + + if val != "bar" { + t.Fatal("should set id") + } +} + +func TestProviderImportState_setsType(t *testing.T) { + var tVal string + stateFunc := func(d *ResourceData, meta interface{}) ([]*ResourceData, error) { + d.SetId("foo") + tVal = d.State().Ephemeral.Type + return []*ResourceData{d}, nil + } + + p := &Provider{ + ResourcesMap: map[string]*Resource{ + "foo": { + Importer: &ResourceImporter{ + State: stateFunc, + }, + }, + }, + } + + _, err := p.ImportState(context.Background(), &terraform.InstanceInfo{ + Type: "foo", + }, "bar") + if err != nil { + t.Fatalf("err: %s", err) + } + + if tVal != "foo" { + t.Fatal("should set type") + } +} + +func TestProviderMeta(t *testing.T) { + p := new(Provider) + if v := p.Meta(); v != nil { + t.Fatalf("bad: %#v", v) + } + + expected := 42 + p.SetMeta(42) + if v := p.Meta(); !reflect.DeepEqual(v, expected) { + t.Fatalf("bad: %#v", v) + } +} + +func TestProvider_InternalValidate(t *testing.T) { + cases := []struct { + P *Provider + ExpectedErr error + }{ + { + P: &Provider{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeBool, + Optional: true, + }, + }, + }, + ExpectedErr: nil, + }, + { // Reserved resource fields should be allowed in provider block + P: &Provider{ + Schema: map[string]*Schema{ + "provisioner": { + Type: TypeString, + Optional: true, + }, + "count": { + Type: TypeInt, + Optional: true, + }, + }, + }, + ExpectedErr: nil, + }, + { // Reserved provider fields should not be allowed + P: &Provider{ + Schema: map[string]*Schema{ + "alias": { + Type: TypeString, + Optional: true, + }, + }, + }, + ExpectedErr: fmt.Errorf("%s is a reserved field name for a provider", "alias"), + }, + { // ConfigureFunc and ConfigureContext cannot both be set + P: &Provider{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + }, + }, + ConfigureFunc: func(d *ResourceData) (interface{}, error) { + return nil, nil + }, + ConfigureContextFunc: func(ctx context.Context, d *ResourceData) (interface{}, diag.Diagnostics) { + return nil, nil + }, + }, + ExpectedErr: fmt.Errorf("ConfigureFunc and ConfigureContextFunc must not both be set"), + }, + } + + for i, tc := range cases { + err := tc.P.InternalValidate() + if tc.ExpectedErr == nil { + if err != nil { + t.Fatalf("%d: Error returned (expected no error): %s", i, err) + } + continue + } + if tc.ExpectedErr != nil && err == nil { + t.Fatalf("%d: Expected error (%s), but no error returned", i, tc.ExpectedErr) + } + if err.Error() != tc.ExpectedErr.Error() { + t.Fatalf("%d: Errors don't match. Expected: %#v Given: %#v", i, tc.ExpectedErr, err) + } + } +} + +func TestProviderUserAgentAppendViaEnvVar(t *testing.T) { + if oldenv, isSet := os.LookupEnv(uaEnvVar); isSet { + defer os.Setenv(uaEnvVar, oldenv) + } else { + defer os.Unsetenv(uaEnvVar) + } + + expectedBase := "Terraform/4.5.6 (+https://www.terraform.io) Terraform-Plugin-SDK/" + meta.SDKVersionString() + + testCases := []struct { + providerName string + providerVersion string + envVarValue string + expected string + }{ + {"", "", "", expectedBase}, + {"", "", " ", expectedBase}, + {"", "", " \n", expectedBase}, + {"", "", "test/1", expectedBase + " test/1"}, + {"", "", "test/1 (comment)", expectedBase + " test/1 (comment)"}, + {"My-Provider", "", "", expectedBase + " My-Provider"}, + {"My-Provider", "", " ", expectedBase + " My-Provider"}, + {"My-Provider", "", " \n", expectedBase + " My-Provider"}, + {"My-Provider", "", "test/1", expectedBase + " My-Provider test/1"}, + {"My-Provider", "", "test/1 (comment)", expectedBase + " My-Provider test/1 (comment)"}, + {"My-Provider", "1.2.3", "", expectedBase + " My-Provider/1.2.3"}, + {"My-Provider", "1.2.3", " ", expectedBase + " My-Provider/1.2.3"}, + {"My-Provider", "1.2.3", " \n", expectedBase + " My-Provider/1.2.3"}, + {"My-Provider", "1.2.3", "test/1", expectedBase + " My-Provider/1.2.3 test/1"}, + {"My-Provider", "1.2.3", "test/1 (comment)", expectedBase + " My-Provider/1.2.3 test/1 (comment)"}, + } + + for i, tc := range testCases { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + os.Unsetenv(uaEnvVar) + os.Setenv(uaEnvVar, tc.envVarValue) + p := &Provider{TerraformVersion: "4.5.6"} + givenUA := p.UserAgent(tc.providerName, tc.providerVersion) + if givenUA != tc.expected { + t.Fatalf("Expected User-Agent '%s' does not match '%s'", tc.expected, givenUA) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource.go index 406dcdf7..45dc9c2e 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource.go @@ -1,13 +1,16 @@ package schema import ( + "context" "errors" "fmt" "log" "strconv" - "github.com/hashicorp/terraform-plugin-sdk/terraform" - "github.com/zclconf/go-cty/cty" + "github.com/hashicorp/go-cty/cty" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) var ReservedDataSourceFields = []string{ @@ -23,7 +26,6 @@ var ReservedResourceFields = []string{ "connection", "count", "depends_on", - "id", "lifecycle", "provider", "provisioner", @@ -63,12 +65,6 @@ type Resource struct { // their Versioning at any integer >= 1 SchemaVersion int - // MigrateState is deprecated and any new changes to a resource's schema - // should be handled by StateUpgraders. Existing MigrateState implementations - // should remain for compatibility with existing state. MigrateState will - // still be called if the stored SchemaVersion is less than the - // first version of the StateUpgraders. - // // MigrateState is responsible for updating an InstanceState with an old // version to the format expected by the current version of the Schema. // @@ -79,6 +75,12 @@ type Resource struct { // the InstanceState that needs updating, as well as the configured // provider's configured meta interface{}, in case the migration process // needs to make any remote API calls. + // + // Deprecated: MigrateState is deprecated and any new changes to a resource's schema + // should be handled by StateUpgraders. Existing MigrateState implementations + // should remain for compatibility with existing state. MigrateState will + // still be called if the stored SchemaVersion is less than the + // first version of the StateUpgraders. MigrateState StateMigrateFunc // StateUpgraders contains the functions responsible for upgrading an @@ -95,8 +97,27 @@ type Resource struct { // The functions below are the CRUD operations for this resource. // - // The only optional operation is Update. If Update is not implemented, - // then updates will not be supported for this resource. + // Deprecated: Please use the context aware equivalents instead. Only one of + // the operations or context aware equivalent can be set, not both. + Create CreateFunc + Read ReadFunc + Update UpdateFunc + Delete DeleteFunc + + // Exists is a function that is called to check if a resource still + // exists. If this returns false, then this will affect the diff + // accordingly. If this function isn't set, it will not be called. You + // can also signal existence in the Read method by calling d.SetId("") + // if the Resource is no longer present and should be removed from state. + // The *ResourceData passed to Exists should _not_ be modified. + // + // Deprecated: ReadContext should be able to encapsulate the logic of Exists + Exists ExistsFunc + + // The functions below are the CRUD operations for this resource. + // + // The only optional operation is Update. If Update is not + // implemented, then updates will not be supported for this resource. // // The ResourceData parameter in the functions below are used to // query configuration and changes for the resource as well as to set @@ -107,21 +128,59 @@ type Resource struct { // a ConfigureFunc, this will be nil. This parameter should be used // to store API clients, configuration structures, etc. // - // If any errors occur during each of the operation, an error should be - // returned. If a resource was partially updated, be careful to enable - // partial state mode for ResourceData and use it accordingly. + // These functions are passed a context configured to timeout with whatever + // was set as the timeout for this operation. Useful for forwarding on to + // backend SDK's that accept context. The context will also cancel if + // Terraform sends a cancellation signal. // - // Exists is a function that is called to check if a resource still - // exists. If this returns false, then this will affect the diff - // accordingly. If this function isn't set, it will not be called. You - // can also signal existence in the Read method by calling d.SetId("") - // if the Resource is no longer present and should be removed from state. - // The *ResourceData passed to Exists should _not_ be modified. - Create CreateFunc - Read ReadFunc - Update UpdateFunc - Delete DeleteFunc - Exists ExistsFunc + // These functions return diagnostics, allowing developers to build + // a list of warnings and errors to be presented to the Terraform user. + // The AttributePath of those diagnostics should be built within these + // functions, please consult go-cty documentation for building a cty.Path + CreateContext CreateContextFunc + ReadContext ReadContextFunc + UpdateContext UpdateContextFunc + DeleteContext DeleteContextFunc + + // CreateWithoutTimeout is equivalent to CreateContext with no context timeout. + // + // Most resources should prefer CreateContext with properly implemented + // operation timeout values, however there are cases where operation + // synchronization across concurrent resources is necessary in the resource + // logic, such as a mutex, to prevent remote system errors. Since these + // operations would have an indeterminate timeout that scales with the + // number of resources, this allows resources to control timeout behavior. + CreateWithoutTimeout CreateContextFunc + + // ReadWithoutTimeout is equivalent to ReadContext with no context timeout. + // + // Most resources should prefer ReadContext with properly implemented + // operation timeout values, however there are cases where operation + // synchronization across concurrent resources is necessary in the resource + // logic, such as a mutex, to prevent remote system errors. Since these + // operations would have an indeterminate timeout that scales with the + // number of resources, this allows resources to control timeout behavior. + ReadWithoutTimeout ReadContextFunc + + // UpdateWithoutTimeout is equivalent to UpdateContext with no context timeout. + // + // Most resources should prefer UpdateContext with properly implemented + // operation timeout values, however there are cases where operation + // synchronization across concurrent resources is necessary in the resource + // logic, such as a mutex, to prevent remote system errors. Since these + // operations would have an indeterminate timeout that scales with the + // number of resources, this allows resources to control timeout behavior. + UpdateWithoutTimeout UpdateContextFunc + + // DeleteWithoutTimeout is equivalent to DeleteContext with no context timeout. + // + // Most resources should prefer DeleteContext with properly implemented + // operation timeout values, however there are cases where operation + // synchronization across concurrent resources is necessary in the resource + // logic, such as a mutex, to prevent remote system errors. Since these + // operations would have an indeterminate timeout that scales with the + // number of resources, this allows resources to control timeout behavior. + DeleteWithoutTimeout DeleteContextFunc // CustomizeDiff is a custom function for working with the diff that // Terraform has created for this resource - it can be used to customize the @@ -143,11 +202,6 @@ type Resource struct { // // This function needs to be resilient to support all scenarios. // - // If this function needs to access external API resources, remember to flag - // the RequiresRefresh attribute mentioned below to ensure that - // -refresh=false is blocked when running plan or apply, as this means that - // this resource requires refresh-like behaviour to work effectively. - // // For the most part, only computed fields can be customized by this // function. // @@ -172,6 +226,21 @@ type Resource struct { // actions (Create, Read, Update, Delete, Default) to the Resource struct, and // accessing them in the matching methods. Timeouts *ResourceTimeout + + // Description is used as the description for docs, the language server and + // other user facing usage. It can be plain-text or markdown depending on the + // global DescriptionKind setting. + Description string + + // UseJSONNumber should be set when state upgraders will expect + // json.Numbers instead of float64s for numbers. This is added as a + // toggle for backwards compatibility for type assertions, but should + // be used in all new resources to avoid bugs with sufficiently large + // user input. + // + // See github.com/hashicorp/terraform-plugin-sdk/issues/655 for more + // details. + UseJSONNumber bool } // ShimInstanceStateFromValue converts a cty.Value to a @@ -195,20 +264,26 @@ func (r *Resource) ShimInstanceStateFromValue(state cty.Value) (*terraform.Insta return s, nil } -// See Resource documentation. +// The following function types are of the legacy CRUD operations. +// +// Deprecated: Please use the context aware equivalents instead. type CreateFunc func(*ResourceData, interface{}) error +type ReadFunc func(*ResourceData, interface{}) error +type UpdateFunc func(*ResourceData, interface{}) error +type DeleteFunc func(*ResourceData, interface{}) error +type ExistsFunc func(*ResourceData, interface{}) (bool, error) // See Resource documentation. -type ReadFunc func(*ResourceData, interface{}) error +type CreateContextFunc func(context.Context, *ResourceData, interface{}) diag.Diagnostics // See Resource documentation. -type UpdateFunc func(*ResourceData, interface{}) error +type ReadContextFunc func(context.Context, *ResourceData, interface{}) diag.Diagnostics // See Resource documentation. -type DeleteFunc func(*ResourceData, interface{}) error +type UpdateContextFunc func(context.Context, *ResourceData, interface{}) diag.Diagnostics // See Resource documentation. -type ExistsFunc func(*ResourceData, interface{}) (bool, error) +type DeleteContextFunc func(context.Context, *ResourceData, interface{}) diag.Diagnostics // See Resource documentation. type StateMigrateFunc func( @@ -233,19 +308,92 @@ type StateUpgrader struct { } // See StateUpgrader -type StateUpgradeFunc func(rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) +type StateUpgradeFunc func(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) // See Resource documentation. -type CustomizeDiffFunc func(*ResourceDiff, interface{}) error +type CustomizeDiffFunc func(context.Context, *ResourceDiff, interface{}) error + +func (r *Resource) create(ctx context.Context, d *ResourceData, meta interface{}) diag.Diagnostics { + if r.Create != nil { + if err := r.Create(d, meta); err != nil { + return diag.FromErr(err) + } + return nil + } + + if r.CreateWithoutTimeout != nil { + return r.CreateWithoutTimeout(ctx, d, meta) + } + + ctx, cancel := context.WithTimeout(ctx, d.Timeout(TimeoutCreate)) + defer cancel() + return r.CreateContext(ctx, d, meta) +} + +func (r *Resource) read(ctx context.Context, d *ResourceData, meta interface{}) diag.Diagnostics { + if r.Read != nil { + if err := r.Read(d, meta); err != nil { + return diag.FromErr(err) + } + return nil + } + + if r.ReadWithoutTimeout != nil { + return r.ReadWithoutTimeout(ctx, d, meta) + } + + ctx, cancel := context.WithTimeout(ctx, d.Timeout(TimeoutRead)) + defer cancel() + return r.ReadContext(ctx, d, meta) +} + +func (r *Resource) update(ctx context.Context, d *ResourceData, meta interface{}) diag.Diagnostics { + if r.Update != nil { + if err := r.Update(d, meta); err != nil { + return diag.FromErr(err) + } + return nil + } + + if r.UpdateWithoutTimeout != nil { + return r.UpdateWithoutTimeout(ctx, d, meta) + } + + ctx, cancel := context.WithTimeout(ctx, d.Timeout(TimeoutUpdate)) + defer cancel() + return r.UpdateContext(ctx, d, meta) +} + +func (r *Resource) delete(ctx context.Context, d *ResourceData, meta interface{}) diag.Diagnostics { + if r.Delete != nil { + if err := r.Delete(d, meta); err != nil { + return diag.FromErr(err) + } + return nil + } + + if r.DeleteWithoutTimeout != nil { + return r.DeleteWithoutTimeout(ctx, d, meta) + } + + ctx, cancel := context.WithTimeout(ctx, d.Timeout(TimeoutDelete)) + defer cancel() + return r.DeleteContext(ctx, d, meta) +} // Apply creates, updates, and/or deletes a resource. func (r *Resource) Apply( + ctx context.Context, s *terraform.InstanceState, d *terraform.InstanceDiff, - meta interface{}) (*terraform.InstanceState, error) { + meta interface{}) (*terraform.InstanceState, diag.Diagnostics) { data, err := schemaMap(r.Schema).Data(s, d) if err != nil { - return s, err + return s, diag.FromErr(err) + } + + if s != nil && data != nil { + data.providerMeta = s.ProviderMeta } // Instance Diff shoould have the timeout info, need to copy it over to the @@ -272,11 +420,14 @@ func (r *Resource) Apply( s = new(terraform.InstanceState) } + var diags diag.Diagnostics + if d.Destroy || d.RequiresNew() { if s.ID != "" { // Destroy the resource since it is created - if err := r.Delete(data, meta); err != nil { - return r.recordCurrentSchemaVersion(data.State()), err + diags = append(diags, r.delete(ctx, data, meta)...) + if diags.HasError() { + return r.recordCurrentSchemaVersion(data.State()), diags } // Make sure the ID is gone. @@ -286,36 +437,39 @@ func (r *Resource) Apply( // If we're only destroying, and not creating, then return // now since we're done! if !d.RequiresNew() { - return nil, nil + return nil, diags } // Reset the data to be stateless since we just destroyed data, err = schemaMap(r.Schema).Data(nil, d) - // data was reset, need to re-apply the parsed timeouts - data.timeouts = &rt if err != nil { - return nil, err + return nil, append(diags, diag.FromErr(err)...) } + + // data was reset, need to re-apply the parsed timeouts + data.timeouts = &rt } - err = nil if data.Id() == "" { // We're creating, it is a new resource. data.MarkNewResource() - err = r.Create(data, meta) + diags = append(diags, r.create(ctx, data, meta)...) } else { - if r.Update == nil { - return s, fmt.Errorf("doesn't support update") + if !r.updateFuncSet() { + return s, append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "doesn't support update", + }) } - - err = r.Update(data, meta) + diags = append(diags, r.update(ctx, data, meta)...) } - return r.recordCurrentSchemaVersion(data.State()), err + return r.recordCurrentSchemaVersion(data.State()), diags } // Diff returns a diff of this resource. func (r *Resource) Diff( + ctx context.Context, s *terraform.InstanceState, c *terraform.ResourceConfig, meta interface{}) (*terraform.InstanceDiff, error) { @@ -327,7 +481,7 @@ func (r *Resource) Diff( return nil, fmt.Errorf("[ERR] Error decoding timeout: %s", err) } - instanceDiff, err := schemaMap(r.Schema).Diff(s, c, r.CustomizeDiff, meta, true) + instanceDiff, err := schemaMap(r.Schema).Diff(ctx, s, c, r.CustomizeDiff, meta, true) if err != nil { return instanceDiff, err } @@ -343,12 +497,13 @@ func (r *Resource) Diff( return instanceDiff, err } -func (r *Resource) simpleDiff( +func (r *Resource) SimpleDiff( + ctx context.Context, s *terraform.InstanceState, c *terraform.ResourceConfig, meta interface{}) (*terraform.InstanceDiff, error) { - instanceDiff, err := schemaMap(r.Schema).Diff(s, c, r.CustomizeDiff, meta, false) + instanceDiff, err := schemaMap(r.Schema).Diff(ctx, s, c, r.CustomizeDiff, meta, false) if err != nil { return instanceDiff, err } @@ -372,30 +527,35 @@ func (r *Resource) simpleDiff( } // Validate validates the resource configuration against the schema. -func (r *Resource) Validate(c *terraform.ResourceConfig) ([]string, []error) { - warns, errs := schemaMap(r.Schema).Validate(c) +func (r *Resource) Validate(c *terraform.ResourceConfig) diag.Diagnostics { + diags := schemaMap(r.Schema).Validate(c) if r.DeprecationMessage != "" { - warns = append(warns, r.DeprecationMessage) + diags = append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Deprecated Resource", + Detail: r.DeprecationMessage, + }) } - return warns, errs + return diags } // ReadDataApply loads the data for a data source, given a diff that // describes the configuration arguments and desired computed attributes. func (r *Resource) ReadDataApply( + ctx context.Context, d *terraform.InstanceDiff, meta interface{}, -) (*terraform.InstanceState, error) { +) (*terraform.InstanceState, diag.Diagnostics) { // Data sources are always built completely from scratch // on each read, so the source state is always nil. data, err := schemaMap(r.Schema).Data(nil, d) if err != nil { - return nil, err + return nil, diag.FromErr(err) } - err = r.Read(data, meta) + diags := r.read(ctx, data, meta) state := data.State() if state != nil && state.ID == "" { // Data sources can set an ID if they want, but they aren't @@ -405,7 +565,7 @@ func (r *Resource) ReadDataApply( state.ID = "-" } - return r.recordCurrentSchemaVersion(state), err + return r.recordCurrentSchemaVersion(state), diags } // RefreshWithoutUpgrade reads the instance state, but does not call @@ -413,8 +573,9 @@ func (r *Resource) ReadDataApply( // separate API call. // RefreshWithoutUpgrade is part of the new plugin shims. func (r *Resource) RefreshWithoutUpgrade( + ctx context.Context, s *terraform.InstanceState, - meta interface{}) (*terraform.InstanceState, error) { + meta interface{}) (*terraform.InstanceState, diag.Diagnostics) { // If the ID is already somehow blank, it doesn't exist if s.ID == "" { return nil, nil @@ -431,155 +592,58 @@ func (r *Resource) RefreshWithoutUpgrade( // Make a copy of data so that if it is modified it doesn't // affect our Read later. data, err := schemaMap(r.Schema).Data(s, nil) - data.timeouts = &rt - if err != nil { - return s, err - } - - exists, err := r.Exists(data, meta) - if err != nil { - return s, err - } - if !exists { - return nil, nil - } - } - - data, err := schemaMap(r.Schema).Data(s, nil) - data.timeouts = &rt - if err != nil { - return s, err - } - - err = r.Read(data, meta) - state := data.State() - if state != nil && state.ID == "" { - state = nil - } - - return r.recordCurrentSchemaVersion(state), err -} - -// Refresh refreshes the state of the resource. -func (r *Resource) Refresh( - s *terraform.InstanceState, - meta interface{}) (*terraform.InstanceState, error) { - // If the ID is already somehow blank, it doesn't exist - if s.ID == "" { - return nil, nil - } - - rt := ResourceTimeout{} - if _, ok := s.Meta[TimeoutKey]; ok { - if err := rt.StateDecode(s); err != nil { - log.Printf("[ERR] Error decoding ResourceTimeout: %s", err) + return s, diag.FromErr(err) } - } - - if r.Exists != nil { - // Make a copy of data so that if it is modified it doesn't - // affect our Read later. - data, err := schemaMap(r.Schema).Data(s, nil) data.timeouts = &rt - if err != nil { - return s, err + if s != nil { + data.providerMeta = s.ProviderMeta } exists, err := r.Exists(data, meta) if err != nil { - return s, err + return s, diag.FromErr(err) } + if !exists { return nil, nil } } - // there may be new StateUpgraders that need to be run - s, err := r.upgradeState(s, meta) + data, err := schemaMap(r.Schema).Data(s, nil) if err != nil { - return s, err + return s, diag.FromErr(err) } - - data, err := schemaMap(r.Schema).Data(s, nil) data.timeouts = &rt - if err != nil { - return s, err + + if s != nil { + data.providerMeta = s.ProviderMeta } - err = r.Read(data, meta) + diags := r.read(ctx, data, meta) state := data.State() if state != nil && state.ID == "" { state = nil } - return r.recordCurrentSchemaVersion(state), err + return r.recordCurrentSchemaVersion(state), diags } -func (r *Resource) upgradeState(s *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) { - var err error - - needsMigration, stateSchemaVersion := r.checkSchemaVersion(s) - migrate := needsMigration && r.MigrateState != nil - - if migrate { - s, err = r.MigrateState(stateSchemaVersion, s, meta) - if err != nil { - return s, err - } - } - - if len(r.StateUpgraders) == 0 { - return s, nil - } - - // If we ran MigrateState, then the stateSchemaVersion value is no longer - // correct. We can expect the first upgrade function to be the correct - // schema type version. - if migrate { - stateSchemaVersion = r.StateUpgraders[0].Version - } - - schemaType := r.CoreConfigSchema().ImpliedType() - // find the expected type to convert the state - for _, upgrader := range r.StateUpgraders { - if stateSchemaVersion == upgrader.Version { - schemaType = upgrader.Type - } - } - - // StateUpgraders only operate on the new JSON format state, so the state - // need to be converted. - stateVal, err := StateValueFromInstanceState(s, schemaType) - if err != nil { - return nil, err - } - - jsonState, err := StateValueToJSONMap(stateVal, schemaType) - if err != nil { - return nil, err - } - - for _, upgrader := range r.StateUpgraders { - if stateSchemaVersion != upgrader.Version { - continue - } +func (r *Resource) createFuncSet() bool { + return (r.Create != nil || r.CreateContext != nil || r.CreateWithoutTimeout != nil) +} - jsonState, err = upgrader.Upgrade(jsonState, meta) - if err != nil { - return nil, err - } - stateSchemaVersion++ - } +func (r *Resource) readFuncSet() bool { + return (r.Read != nil || r.ReadContext != nil || r.ReadWithoutTimeout != nil) +} - // now we need to re-flatmap the new state - stateVal, err = JSONMapToStateValue(jsonState, r.CoreConfigSchema()) - if err != nil { - return nil, err - } +func (r *Resource) updateFuncSet() bool { + return (r.Update != nil || r.UpdateContext != nil || r.UpdateWithoutTimeout != nil) +} - return r.ShimInstanceStateFromValue(stateVal) +func (r *Resource) deleteFuncSet() bool { + return (r.Delete != nil || r.DeleteContext != nil || r.DeleteWithoutTimeout != nil) } // InternalValidate should be called to validate the structure @@ -598,7 +662,7 @@ func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error } if !writable { - if r.Create != nil || r.Update != nil || r.Delete != nil { + if r.createFuncSet() || r.updateFuncSet() || r.deleteFuncSet() { return fmt.Errorf("must not implement Create, Update or Delete") } @@ -612,7 +676,7 @@ func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error if r.isTopLevel() && writable { // All non-Computed attributes must be ForceNew if Update is not defined - if r.Update == nil { + if !r.updateFuncSet() { nonForceNewAttrs := make([]string, 0) for k, v := range r.Schema { if !v.ForceNew && !v.Computed { @@ -640,10 +704,10 @@ func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error tsm = schemaMap(r.Schema) // Destroy, and Read are required - if r.Read == nil { + if !r.readFuncSet() { return fmt.Errorf("Read must be implemented") } - if r.Delete == nil { + if !r.deleteFuncSet() { return fmt.Errorf("Delete must be implemented") } @@ -654,6 +718,14 @@ func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error } } + if f, ok := tsm["id"]; ok { + // if there is an explicit ID, validate it... + err := validateResourceID(f) + if err != nil { + return err + } + } + for k, f := range tsm { if isReservedResourceFieldName(k, f) { return fmt.Errorf("%s is a reserved field name", k) @@ -689,13 +761,55 @@ func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error // Data source if r.isTopLevel() && !writable { tsm = schemaMap(r.Schema) - for k, _ := range tsm { + for k := range tsm { if isReservedDataSourceFieldName(k) { return fmt.Errorf("%s is a reserved field name", k) } } } + // check context funcs are not set alongside their nonctx counterparts + if r.CreateContext != nil && r.Create != nil { + return fmt.Errorf("CreateContext and Create should not both be set") + } + if r.ReadContext != nil && r.Read != nil { + return fmt.Errorf("ReadContext and Read should not both be set") + } + if r.UpdateContext != nil && r.Update != nil { + return fmt.Errorf("UpdateContext and Update should not both be set") + } + if r.DeleteContext != nil && r.Delete != nil { + return fmt.Errorf("DeleteContext and Delete should not both be set") + } + + // check context funcs are not set alongside their without timeout counterparts + if r.CreateContext != nil && r.CreateWithoutTimeout != nil { + return fmt.Errorf("CreateContext and CreateWithoutTimeout should not both be set") + } + if r.ReadContext != nil && r.ReadWithoutTimeout != nil { + return fmt.Errorf("ReadContext and ReadWithoutTimeout should not both be set") + } + if r.UpdateContext != nil && r.UpdateWithoutTimeout != nil { + return fmt.Errorf("UpdateContext and UpdateWithoutTimeout should not both be set") + } + if r.DeleteContext != nil && r.DeleteWithoutTimeout != nil { + return fmt.Errorf("DeleteContext and DeleteWithoutTimeout should not both be set") + } + + // check non-context funcs are not set alongside the context without timeout counterparts + if r.Create != nil && r.CreateWithoutTimeout != nil { + return fmt.Errorf("Create and CreateWithoutTimeout should not both be set") + } + if r.Read != nil && r.ReadWithoutTimeout != nil { + return fmt.Errorf("Read and ReadWithoutTimeout should not both be set") + } + if r.Update != nil && r.UpdateWithoutTimeout != nil { + return fmt.Errorf("Update and UpdateWithoutTimeout should not both be set") + } + if r.Delete != nil && r.DeleteWithoutTimeout != nil { + return fmt.Errorf("Delete and DeleteWithoutTimeout should not both be set") + } + return schemaMap(r.Schema).InternalValidate(tsm) } @@ -708,18 +822,30 @@ func isReservedDataSourceFieldName(name string) bool { return false } -func isReservedResourceFieldName(name string, s *Schema) bool { - // Allow phasing out "id" - // See https://github.com/terraform-providers/terraform-provider-aws/pull/1626#issuecomment-328881415 - if name == "id" && (s.Deprecated != "" || s.Removed != "") { - return false +func validateResourceID(s *Schema) error { + if s.Type != TypeString { + return fmt.Errorf(`the "id" attribute must be of TypeString`) + } + + if s.Required { + return fmt.Errorf(`the "id" attribute cannot be marked Required`) + } + + // ID should at least be computed. If unspecified it will be set to Computed and Optional, + // but Optional is unnecessary if undesired. + if s.Computed != true { + return fmt.Errorf(`the "id" attribute must be marked Computed`) } + return nil +} +func isReservedResourceFieldName(name string, s *Schema) bool { for _, reservedName := range ReservedResourceFields { if name == reservedName { return true } } + return false } @@ -762,47 +888,10 @@ func (r *Resource) TestResourceData() *ResourceData { } } -// SchemasForFlatmapPath tries its best to find a sequence of schemas that -// the given dot-delimited attribute path traverses through in the schema -// of the receiving Resource. -func (r *Resource) SchemasForFlatmapPath(path string) []*Schema { - return SchemasForFlatmapPath(path, r.Schema) -} - // Returns true if the resource is "top level" i.e. not a sub-resource. func (r *Resource) isTopLevel() bool { // TODO: This is a heuristic; replace with a definitive attribute? - return (r.Create != nil || r.Read != nil) -} - -// Determines if a given InstanceState needs to be migrated by checking the -// stored version number with the current SchemaVersion -func (r *Resource) checkSchemaVersion(is *terraform.InstanceState) (bool, int) { - // Get the raw interface{} value for the schema version. If it doesn't - // exist or is nil then set it to zero. - raw := is.Meta["schema_version"] - if raw == nil { - raw = "0" - } - - // Try to convert it to a string. If it isn't a string then we pretend - // that it isn't set at all. It should never not be a string unless it - // was manually tampered with. - rawString, ok := raw.(string) - if !ok { - rawString = "0" - } - - stateSchemaVersion, _ := strconv.Atoi(rawString) - - // Don't run MigrateState if the version is handled by a StateUpgrader, - // since StateMigrateFuncs are not required to handle unknown versions - maxVersion := r.SchemaVersion - if len(r.StateUpgraders) > 0 { - maxVersion = r.StateUpgraders[0].Version - } - - return stateSchemaVersion < maxVersion, stateSchemaVersion + return (r.createFuncSet() || r.readFuncSet()) } func (r *Resource) recordCurrentSchemaVersion( @@ -822,6 +911,12 @@ func Noop(*ResourceData, interface{}) error { return nil } +// NoopContext is a convenience implementation of context aware resource function which takes +// no action and returns no error. +func NoopContext(context.Context, *ResourceData, interface{}) diag.Diagnostics { + return nil +} + // RemoveFromState is a convenience implementation of a resource function // which sets the resource ID to empty string (to remove it from state) // and returns no error. diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_data.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_data.go index ad00b93d..d12e7546 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_data.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_data.go @@ -7,32 +7,33 @@ import ( "sync" "time" - "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/go-cty/cty/gocty" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) // ResourceData is used to query and set the attributes of a resource. // // ResourceData is the primary argument received for CRUD operations on // a resource as well as configuration of a provider. It is a powerful -// structure that can be used to not only query data, but check for changes, -// define partial state updates, etc. +// structure that can be used to not only query data, but also check for changes // -// The most relevant methods to take a look at are Get, Set, and Partial. +// The most relevant methods to take a look at are Get and Set. type ResourceData struct { // Settable (internally) - schema map[string]*Schema - config *terraform.ResourceConfig - state *terraform.InstanceState - diff *terraform.InstanceDiff - meta map[string]interface{} - timeouts *ResourceTimeout + schema map[string]*Schema + config *terraform.ResourceConfig + state *terraform.InstanceState + diff *terraform.InstanceDiff + meta map[string]interface{} + timeouts *ResourceTimeout + providerMeta cty.Value // Don't set multiReader *MultiLevelFieldReader setWriter *MapFieldWriter newState *terraform.InstanceState partial bool - partialMap map[string]struct{} once sync.Once isNew bool @@ -49,17 +50,6 @@ type getResult struct { Schema *Schema } -// UnsafeSetFieldRaw allows setting arbitrary values in state to arbitrary -// values, bypassing schema. This MUST NOT be used in normal circumstances - -// it exists only to support the remote_state data source. -// -// Deprecated: Fully define schema attributes and use Set() instead. -func (d *ResourceData) UnsafeSetFieldRaw(key string, value string) { - d.once.Do(d.init) - - d.setWriter.unsafeWriteField(key, value) -} - // Get returns the data for the given key, or nil if the key doesn't exist // in the schema. // @@ -108,16 +98,11 @@ func (d *ResourceData) GetOk(key string) (interface{}, bool) { return r.Value, exists } -// GetOkExists returns the data for a given key and whether or not the key -// has been set to a non-zero value. This is only useful for determining -// if boolean attributes have been set, if they are Optional but do not -// have a Default value. +// GetOkExists can check if TypeBool attributes that are Optional with +// no Default value have been set. // -// This is nearly the same function as GetOk, yet it does not check -// for the zero value of the attribute's type. This allows for attributes -// without a default, to fully check for a literal assignment, regardless -// of the zero-value for that type. -// This should only be used if absolutely required/needed. +// Deprecated: usage is discouraged due to undefined behaviors and may be +// removed in a future version of the SDK func (d *ResourceData) GetOkExists(key string) (interface{}, bool) { r := d.getRaw(key, getSourceSet) exists := r.Exists && !r.Computed @@ -143,6 +128,29 @@ func (d *ResourceData) HasChanges(keys ...string) bool { return false } +// HasChangesExcept returns whether any keys outside the given keys have been changed. +// +// This function only works with root attribute keys. +func (d *ResourceData) HasChangesExcept(keys ...string) bool { + for attr := range d.diff.Attributes { + rootAttr := strings.Split(attr, ".")[0] + var skipAttr bool + + for _, key := range keys { + if rootAttr == key { + skipAttr = true + break + } + } + + if !skipAttr && d.HasChange(rootAttr) { + return true + } + } + + return false +} + // HasChange returns whether or not the given key has been changed. func (d *ResourceData) HasChange(key string) bool { o, n := d.GetChange(key) @@ -157,23 +165,38 @@ func (d *ResourceData) HasChange(key string) bool { return !reflect.DeepEqual(o, n) } -// Partial turns partial state mode on/off. +// HasChangeExcept returns whether any keys outside the given key have been changed. // -// When partial state mode is enabled, then only key prefixes specified -// by SetPartial will be in the final state. This allows providers to return -// partial states for partially applied resources (when errors occur). +// This function only works with root attribute keys. +func (d *ResourceData) HasChangeExcept(key string) bool { + for attr := range d.diff.Attributes { + rootAttr := strings.Split(attr, ".")[0] + + if rootAttr == key { + continue + } + + if d.HasChange(rootAttr) { + return true + } + } + + return false +} + +// Partial is a legacy function that was used for capturing state of specific +// attributes if an update only partially worked. Enabling this flag without +// setting any specific keys with the now removed SetPartial has a useful side +// effect of preserving all of the resource's previous state. Although confusing, +// it has been discovered that during an update when an error is returned, the +// proposed config is set into state, even without any calls to d.Set. // -// Deprecated: Partial state has very limited benefit given Terraform refreshes -// before operations by default. +// In practice this default behavior goes mostly unnoticed since Terraform +// refreshes between operations by default. The state situation discussed is +// subject to further investigation and potential change. Until then, this +// function has been preserved for the specific usecase. func (d *ResourceData) Partial(on bool) { d.partial = on - if on { - if d.partialMap == nil { - d.partialMap = make(map[string]struct{}) - } - } else { - d.partialMap = nil - } } // Set sets the value for the given key. @@ -202,27 +225,16 @@ func (d *ResourceData) Set(key string, value interface{}) error { } err := d.setWriter.WriteField(strings.Split(key, "."), value) - if err != nil && d.panicOnError { - panic(err) + if err != nil { + if d.panicOnError { + panic(err) + } else { + log.Printf("[ERROR] setting state: %s", err) + } } return err } -// SetPartial adds the key to the final state output while -// in partial state mode. The key must be a root key in the schema (i.e. -// it cannot be "list.0"). -// -// If partial state mode is disabled, then this has no effect. Additionally, -// whenever partial state mode is toggled, the partial data is cleared. -// -// Deprecated: Partial state has very limited benefit given Terraform refreshes -// before operations by default. -func (d *ResourceData) SetPartial(k string) { - if d.partial { - d.partialMap[k] = struct{}{} - } -} - func (d *ResourceData) MarkNewResource() { d.isNew = true } @@ -320,7 +332,7 @@ func (d *ResourceData) State() *terraform.InstanceState { // integrity check of fields existing in the schema, allowing dynamic // keys to be created. hasDynamicAttributes := false - for k, _ := range d.schema { + for k := range d.schema { if k == "__has_dynamic_attributes" { hasDynamicAttributes = true log.Printf("[INFO] Resource %s has dynamic attributes", result.ID) @@ -335,11 +347,7 @@ func (d *ResourceData) State() *terraform.InstanceState { source := getSourceSet if d.partial { source = getSourceState - if _, ok := d.partialMap[k]; ok { - source = getSourceSet - } } - raw := d.get([]string{k}, source) if raw.Exists && !raw.Computed { rawMap[k] = raw.Value @@ -565,3 +573,10 @@ func (d *ResourceData) get(addr []string, source getSource) getResult { Schema: schema, } } + +func (d *ResourceData) GetProviderMeta(dst interface{}) error { + if d.providerMeta.IsNull() { + return nil + } + return gocty.FromCtyValue(d.providerMeta, &dst) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_data_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_data_test.go new file mode 100644 index 00000000..d3e06fa0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_data_test.go @@ -0,0 +1,3932 @@ +package schema + +import ( + "fmt" + "math" + "reflect" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestResourceDataGet(t *testing.T) { + cases := []struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Key string + Value interface{} + }{ + // #0 + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "foo", + New: "bar", + NewComputed: true, + }, + }, + }, + + Key: "availability_zone", + Value: "", + }, + + // #1 + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Key: "availability_zone", + + Value: "foo", + }, + + // #2 + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo!", + NewExtra: "foo", + }, + }, + }, + + Key: "availability_zone", + Value: "foo", + }, + + // #3 + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "bar", + }, + }, + + Diff: nil, + + Key: "availability_zone", + + Value: "bar", + }, + + // #4 + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "foo", + New: "bar", + NewComputed: true, + }, + }, + }, + + Key: "availability_zone", + Value: "", + }, + + // #5 + { + Schema: map[string]*Schema{ + "port": { + Type: TypeInt, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "port": "80", + }, + }, + + Diff: nil, + + Key: "port", + + Value: 80, + }, + + // #6 + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.0": "1", + "ports.1": "2", + "ports.2": "5", + }, + }, + + Key: "ports.1", + + Value: 2, + }, + + // #7 + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.0": "1", + "ports.1": "2", + "ports.2": "5", + }, + }, + + Key: "ports.#", + + Value: 3, + }, + + // #8 + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Key: "ports.#", + + Value: 0, + }, + + // #9 + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.0": "1", + "ports.1": "2", + "ports.2": "5", + }, + }, + + Key: "ports", + + Value: []interface{}{1, 2, 5}, + }, + + // #10 + { + Schema: map[string]*Schema{ + "ingress": { + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": { + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ingress.#": { + Old: "", + New: "1", + }, + "ingress.0.from": { + Old: "", + New: "8080", + }, + }, + }, + + Key: "ingress.0", + + Value: map[string]interface{}{ + "from": 8080, + }, + }, + + // #11 + { + Schema: map[string]*Schema{ + "ingress": { + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": { + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ingress.#": { + Old: "", + New: "1", + }, + "ingress.0.from": { + Old: "", + New: "8080", + }, + }, + }, + + Key: "ingress", + + Value: []interface{}{ + map[string]interface{}{ + "from": 8080, + }, + }, + }, + + // #12 Computed get + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + + Key: "availability_zone", + + Value: "foo", + }, + + // #13 Full object + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Key: "", + + Value: map[string]interface{}{ + "availability_zone": "foo", + }, + }, + + // #14 List of maps + { + Schema: map[string]*Schema{ + "config_vars": { + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{ + Type: TypeMap, + }, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.#": { + Old: "0", + New: "2", + }, + "config_vars.0.foo": { + Old: "", + New: "bar", + }, + "config_vars.1.bar": { + Old: "", + New: "baz", + }, + }, + }, + + Key: "config_vars", + + Value: []interface{}{ + map[string]interface{}{ + "foo": "bar", + }, + map[string]interface{}{ + "bar": "baz", + }, + }, + }, + + // #15 List of maps in state + { + Schema: map[string]*Schema{ + "config_vars": { + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{ + Type: TypeMap, + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.#": "2", + "config_vars.0.foo": "baz", + "config_vars.1.bar": "bar", + }, + }, + + Diff: nil, + + Key: "config_vars", + + Value: []interface{}{ + map[string]interface{}{ + "foo": "baz", + }, + map[string]interface{}{ + "bar": "bar", + }, + }, + }, + + // #16 List of maps with removal in diff + { + Schema: map[string]*Schema{ + "config_vars": { + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{ + Type: TypeMap, + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.#": "1", + "config_vars.0.FOO": "bar", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.#": { + Old: "1", + New: "0", + }, + "config_vars.0.FOO": { + Old: "bar", + NewRemoved: true, + }, + }, + }, + + Key: "config_vars", + + Value: []interface{}{}, + }, + + // #17 Sets + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "1", + "ports.80": "80", + }, + }, + + Diff: nil, + + Key: "ports", + + Value: []interface{}{80}, + }, + + // #18 + { + Schema: map[string]*Schema{ + "data": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": { + Type: TypeInt, + Required: true, + }, + + "value": { + Type: TypeString, + Required: true, + }, + }, + }, + Set: func(a interface{}) int { + m := a.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "data.#": "1", + "data.10.index": "10", + "data.10.value": "50", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "data.10.value": { + Old: "50", + New: "80", + }, + }, + }, + + Key: "data", + + Value: []interface{}{ + map[string]interface{}{ + "index": 10, + "value": "80", + }, + }, + }, + + // #19 Empty Set + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports", + + Value: []interface{}{}, + }, + + // #20 Float zero + { + Schema: map[string]*Schema{ + "ratio": { + Type: TypeFloat, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ratio", + + Value: 0.0, + }, + + // #21 Float given + { + Schema: map[string]*Schema{ + "ratio": { + Type: TypeFloat, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ratio": "0.5", + }, + }, + + Diff: nil, + + Key: "ratio", + + Value: 0.5, + }, + + // #22 Float diff + { + Schema: map[string]*Schema{ + "ratio": { + Type: TypeFloat, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ratio": "-0.5", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ratio": { + Old: "-0.5", + New: "33.0", + }, + }, + }, + + Key: "ratio", + + Value: 33.0, + }, + + // #23 Sets with removed elements + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "1", + "ports.80": "80", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "2", + New: "1", + }, + "ports.80": { + Old: "80", + New: "80", + }, + "ports.8080": { + Old: "8080", + New: "0", + NewRemoved: true, + }, + }, + }, + + Key: "ports", + + Value: []interface{}{80}, + }, + } + + for i, tc := range cases { + d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + v := d.Get(tc.Key) + if s, ok := v.(*Set); ok { + v = s.List() + } + + if !reflect.DeepEqual(v, tc.Value) { + t.Fatalf("Bad: %d\n\n%#v\n\nExpected: %#v", i, v, tc.Value) + } + } +} + +func TestResourceDataGetChange(t *testing.T) { + cases := []struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Key string + OldValue interface{} + NewValue interface{} + }{ + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Key: "availability_zone", + + OldValue: "", + NewValue: "foo", + }, + + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Key: "availability_zone", + + OldValue: "foo", + NewValue: "foo", + }, + } + + for i, tc := range cases { + d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + o, n := d.GetChange(tc.Key) + if !reflect.DeepEqual(o, tc.OldValue) { + t.Fatalf("Old Bad: %d\n\n%#v", i, o) + } + if !reflect.DeepEqual(n, tc.NewValue) { + t.Fatalf("New Bad: %d\n\n%#v", i, n) + } + } +} + +func TestResourceDataGetOk(t *testing.T) { + cases := []struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Key string + Value interface{} + Ok bool + }{ + /* + * Primitives + */ + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "", + }, + }, + }, + + Key: "availability_zone", + Value: "", + Ok: false, + }, + + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + + Key: "availability_zone", + Value: "", + Ok: false, + }, + + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "availability_zone", + Value: "", + Ok: false, + }, + + /* + * Lists + */ + + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports", + Value: []interface{}{}, + Ok: false, + }, + + /* + * Map + */ + + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeMap, + Optional: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports", + Value: map[string]interface{}{}, + Ok: false, + }, + + /* + * Set + */ + + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports", + Value: []interface{}{}, + Ok: false, + }, + + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports.0", + Value: 0, + Ok: false, + }, + + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "0", + New: "0", + }, + }, + }, + + Key: "ports", + Value: []interface{}{}, + Ok: false, + }, + + // Further illustrates and clarifiies the GetOk semantics from #933, and + // highlights the limitation that zero-value config is currently + // indistinguishable from unset config. + { + Schema: map[string]*Schema{ + "from_port": { + Type: TypeInt, + Optional: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "from_port": { + Old: "", + New: "0", + }, + }, + }, + + Key: "from_port", + Value: 0, + Ok: false, + }, + } + + for i, tc := range cases { + d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + v, ok := d.GetOk(tc.Key) + if s, ok := v.(*Set); ok { + v = s.List() + } + + if !reflect.DeepEqual(v, tc.Value) { + t.Fatalf("Bad: %d\n\n%#v", i, v) + } + if ok != tc.Ok { + t.Fatalf("%d: expected ok: %t, got: %t", i, tc.Ok, ok) + } + } +} + +func TestResourceDataGetOkExists(t *testing.T) { + cases := []struct { + Name string + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Key string + Value interface{} + Ok bool + }{ + /* + * Primitives + */ + { + Name: "string-literal-empty", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "", + }, + }, + }, + + Key: "availability_zone", + Value: "", + Ok: true, + }, + + { + Name: "string-computed-empty", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + + Key: "availability_zone", + Value: "", + Ok: false, + }, + + { + Name: "string-optional-computed-nil-diff", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "availability_zone", + Value: "", + Ok: false, + }, + + /* + * Lists + */ + + { + Name: "list-optional", + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports", + Value: []interface{}{}, + Ok: false, + }, + + /* + * Map + */ + + { + Name: "map-optional", + Schema: map[string]*Schema{ + "ports": { + Type: TypeMap, + Optional: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports", + Value: map[string]interface{}{}, + Ok: false, + }, + + /* + * Set + */ + + { + Name: "set-optional", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports", + Value: []interface{}{}, + Ok: false, + }, + + { + Name: "set-optional-key", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports.0", + Value: 0, + Ok: false, + }, + + { + Name: "bool-literal-empty", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeBool, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "", + }, + }, + }, + + Key: "availability_zone", + Value: false, + Ok: true, + }, + + { + Name: "bool-literal-set", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeBool, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + New: "true", + }, + }, + }, + + Key: "availability_zone", + Value: true, + Ok: true, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) + if err != nil { + t.Fatalf("%s err: %s", tc.Name, err) + } + + v, ok := d.GetOkExists(tc.Key) + if s, ok := v.(*Set); ok { + v = s.List() + } + + if !reflect.DeepEqual(v, tc.Value) { + t.Fatalf("Bad %s: \n%#v", tc.Name, v) + } + if ok != tc.Ok { + t.Fatalf("%s: expected ok: %t, got: %t", tc.Name, tc.Ok, ok) + } + }) + } +} + +func TestResourceDataTimeout(t *testing.T) { + cases := []struct { + Name string + Rd *ResourceData + Expected *ResourceTimeout + }{ + { + Name: "Basic example default", + Rd: &ResourceData{timeouts: timeoutForValues(10, 3, 0, 15, 0)}, + Expected: expectedTimeoutForValues(10, 3, 0, 15, 0), + }, + { + Name: "Resource and config match update, create", + Rd: &ResourceData{timeouts: timeoutForValues(10, 0, 3, 0, 0)}, + Expected: expectedTimeoutForValues(10, 0, 3, 0, 0), + }, + { + Name: "Resource provides default", + Rd: &ResourceData{timeouts: timeoutForValues(10, 0, 0, 0, 7)}, + Expected: expectedTimeoutForValues(10, 7, 7, 7, 7), + }, + { + Name: "Resource provides default and delete", + Rd: &ResourceData{timeouts: timeoutForValues(10, 0, 0, 15, 7)}, + Expected: expectedTimeoutForValues(10, 7, 7, 15, 7), + }, + { + Name: "Resource provides default, config overwrites other values", + Rd: &ResourceData{timeouts: timeoutForValues(10, 3, 0, 0, 13)}, + Expected: expectedTimeoutForValues(10, 3, 13, 13, 13), + }, + { + Name: "Resource has no config", + Rd: &ResourceData{}, + Expected: expectedTimeoutForValues(0, 0, 0, 0, 0), + }, + } + + keys := timeoutKeys() + for i, c := range cases { + t.Run(fmt.Sprintf("%d-%s", i, c.Name), func(t *testing.T) { + + for _, k := range keys { + got := c.Rd.Timeout(k) + var ex *time.Duration + switch k { + case TimeoutCreate: + ex = c.Expected.Create + case TimeoutRead: + ex = c.Expected.Read + case TimeoutUpdate: + ex = c.Expected.Update + case TimeoutDelete: + ex = c.Expected.Delete + case TimeoutDefault: + ex = c.Expected.Default + } + + if got > 0 && ex == nil { + t.Fatalf("Unexpected value in (%s), case %d check 1:\n\texpected: %#v\n\tgot: %#v", k, i, ex, got) + } + if got == 0 && ex != nil { + t.Fatalf("Unexpected value in (%s), case %d check 2:\n\texpected: %#v\n\tgot: %#v", k, i, *ex, got) + } + + // confirm values + if ex != nil { + if got != *ex { + t.Fatalf("Timeout %s case (%d) expected (%s), got (%s)", k, i, *ex, got) + } + } + } + + }) + } +} + +func TestResourceDataHasChanges(t *testing.T) { + cases := []struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Keys []string + Change bool + }{ + // empty call d.HasChanges() + { + Schema: map[string]*Schema{}, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + + Keys: []string{}, + + Change: false, + }, + // neither has change + { + Schema: map[string]*Schema{ + "a": { + Type: TypeString, + }, + "b": { + Type: TypeString, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "a": "foo", + "b": "foo", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "a": { + Old: "", + New: "foo", + }, + "b": { + Old: "", + New: "foo", + }, + }, + }, + + Keys: []string{"a", "b"}, + + Change: false, + }, + // one key has change + { + Schema: map[string]*Schema{ + "a": { + Type: TypeString, + }, + "b": { + Type: TypeString, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "a": "foo", + "b": "foo", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "a": { + Old: "", + New: "bar", + }, + "b": { + Old: "", + New: "foo", + }, + }, + }, + + Keys: []string{"a", "b"}, + + Change: true, + }, + } + + for i, tc := range cases { + d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + actual := d.HasChanges(tc.Keys...) + if actual != tc.Change { + t.Fatalf("Bad: %d %#v", i, actual) + } + } +} + +func TestResourceDataHasChangesExcept(t *testing.T) { + testCases := map[string]struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Keys []string + Expected bool + }{ + "single self string diff": { + Schema: map[string]*Schema{ + "self": { + Type: TypeString, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "self": "test1", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "self": { + Old: "test1", + New: "test2", + }, + }, + }, + + Keys: []string{"self"}, + + Expected: false, + }, + + "single self map diff": { + Schema: map[string]*Schema{ + "self": { + Type: TypeMap, + Optional: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "self.Name": { + Old: "foo", + New: "foo", + }, + }, + }, + + Keys: []string{"self"}, + + Expected: false, + }, + + "single self set diff": { + Schema: map[string]*Schema{ + "self": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "self.#": "1", + "self.80": "80", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "self.#": { + Old: "1", + New: "0", + }, + }, + }, + + Keys: []string{"self"}, + + Expected: false, + }, + + "single other string diff": { + Schema: map[string]*Schema{ + "other": { + Type: TypeString, + Optional: true, + }, + "self": { + Type: TypeString, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "self": "test", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "other": { + Old: "", + New: "foo", + }, + }, + }, + + Keys: []string{"self"}, + + Expected: true, + }, + + // https://github.com/hashicorp/terraform/issues/927 + "single other map diff": { + Schema: map[string]*Schema{ + "other": { + Type: TypeMap, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "self": { + Type: TypeString, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "self": "test", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "other.foo": { + Old: "", + New: "bar", + }, + }, + }, + + Keys: []string{"self"}, + + Expected: true, + }, + + "single other set diff": { + Schema: map[string]*Schema{ + "other": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + "self": { + Type: TypeString, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "other.#": "1", + "other.80": "80", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "other.#": { + Old: "1", + New: "0", + }, + }, + }, + + Keys: []string{"self"}, + + Expected: true, + }, + + "single other and self diff": { + Schema: map[string]*Schema{ + "other": { + Type: TypeString, + Optional: true, + }, + "self": { + Type: TypeString, + Optional: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "other": { + Old: "", + New: "test", + }, + "self": { + Old: "", + New: "test", + }, + }, + }, + + Keys: []string{"self"}, + + Expected: true, + }, + + "multiple only other diff": { + Schema: map[string]*Schema{ + "other": { + Type: TypeString, + Optional: true, + }, + "self1": { + Type: TypeString, + Optional: true, + }, + "self2": { + Type: TypeString, + Optional: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "other": { + Old: "", + New: "test", + }, + }, + }, + + Keys: []string{"self1", "self2"}, + + Expected: true, + }, + + "multiple only self diff": { + Schema: map[string]*Schema{ + "other": { + Type: TypeString, + Optional: true, + }, + "self1": { + Type: TypeString, + Optional: true, + }, + "self2": { + Type: TypeString, + Optional: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "self1": { + Old: "", + New: "test", + }, + "self2": { + Old: "", + New: "test", + }, + }, + }, + + Keys: []string{"self1", "self2"}, + + Expected: false, + }, + + "multiple partial self diff one of two": { + Schema: map[string]*Schema{ + "self1": { + Type: TypeString, + Optional: true, + }, + "self2": { + Type: TypeString, + Optional: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "self1": { + Old: "", + New: "test", + }, + }, + }, + + Keys: []string{"self1", "self2"}, + + Expected: false, + }, + + "multiple partial self diff one of three": { + Schema: map[string]*Schema{ + "self1": { + Type: TypeString, + Optional: true, + }, + "self2": { + Type: TypeString, + Optional: true, + }, + "self3": { + Type: TypeString, + Optional: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "self1": { + Old: "", + New: "test", + }, + }, + }, + + Keys: []string{"self1", "self2", "self3"}, + + Expected: false, + }, + + "multiple partial self diff two of three": { + Schema: map[string]*Schema{ + "self1": { + Type: TypeString, + Optional: true, + }, + "self2": { + Type: TypeString, + Optional: true, + }, + "self3": { + Type: TypeString, + Optional: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "self1": { + Old: "", + New: "test", + }, + "self2": { + Old: "", + New: "test", + }, + }, + }, + + Keys: []string{"self1", "self2", "self3"}, + + Expected: false, + }, + } + + for testName, testCase := range testCases { + t.Run(testName, func(t *testing.T) { + d, err := schemaMap(testCase.Schema).Data(testCase.State, testCase.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + actual := d.HasChangesExcept(testCase.Keys...) + if actual != testCase.Expected { + t.Errorf("expected: %t, got: %t", testCase.Expected, actual) + } + }) + } +} + +func TestResourceDataHasChange(t *testing.T) { + cases := []struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Key string + Change bool + }{ + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Key: "availability_zone", + + Change: true, + }, + + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Key: "availability_zone", + + Change: false, + }, + + { + Schema: map[string]*Schema{ + "tags": { + Type: TypeMap, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "tags.Name": { + Old: "foo", + New: "foo", + }, + }, + }, + + Key: "tags", + + Change: true, + }, + + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "1", + "ports.80": "80", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "1", + New: "0", + }, + }, + }, + + Key: "ports", + + Change: true, + }, + + // https://github.com/hashicorp/terraform/issues/927 + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "1", + "ports.80": "80", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "tags.foo": { + Old: "", + New: "bar", + }, + }, + }, + + Key: "ports", + + Change: false, + }, + } + + for i, tc := range cases { + d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + actual := d.HasChange(tc.Key) + if actual != tc.Change { + t.Fatalf("Bad: %d %#v", i, actual) + } + } +} + +func TestResourceDataHasChangeExcept(t *testing.T) { + testCases := map[string]struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Key string + Expected bool + }{ + "self string diff": { + Schema: map[string]*Schema{ + "self": { + Type: TypeString, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "self": "test1", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "self": { + Old: "test1", + New: "test2", + }, + }, + }, + + Key: "self", + + Expected: false, + }, + + "self map diff": { + Schema: map[string]*Schema{ + "self": { + Type: TypeMap, + Optional: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "self.Name": { + Old: "foo", + New: "foo", + }, + }, + }, + + Key: "self", + + Expected: false, + }, + + "self set diff": { + Schema: map[string]*Schema{ + "self": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "self.#": "1", + "self.80": "80", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "self.#": { + Old: "1", + New: "0", + }, + }, + }, + + Key: "self", + + Expected: false, + }, + + "other string diff": { + Schema: map[string]*Schema{ + "other": { + Type: TypeString, + Optional: true, + }, + "self": { + Type: TypeString, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "self": "test", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "other": { + Old: "", + New: "foo", + }, + }, + }, + + Key: "self", + + Expected: true, + }, + + // https://github.com/hashicorp/terraform/issues/927 + "other map diff": { + Schema: map[string]*Schema{ + "other": { + Type: TypeMap, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "self": { + Type: TypeString, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "self": "test", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "other.foo": { + Old: "", + New: "bar", + }, + }, + }, + + Key: "self", + + Expected: true, + }, + + "other set diff": { + Schema: map[string]*Schema{ + "other": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + "self": { + Type: TypeString, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "other.#": "1", + "other.80": "80", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "other.#": { + Old: "1", + New: "0", + }, + }, + }, + + Key: "self", + + Expected: true, + }, + + "other and self diff": { + Schema: map[string]*Schema{ + "other": { + Type: TypeString, + Optional: true, + }, + "self": { + Type: TypeString, + Optional: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "other": { + Old: "", + New: "test", + }, + "self": { + Old: "", + New: "test", + }, + }, + }, + + Key: "self", + + Expected: true, + }, + } + + for testName, testCase := range testCases { + t.Run(testName, func(t *testing.T) { + d, err := schemaMap(testCase.Schema).Data(testCase.State, testCase.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + actual := d.HasChangeExcept(testCase.Key) + if actual != testCase.Expected { + t.Errorf("expected: %t, got: %t", testCase.Expected, actual) + } + }) + } +} + +func TestResourceDataSet(t *testing.T) { + var testNilPtr *string + + cases := []struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Key string + Value interface{} + Err bool + GetKey string + GetValue interface{} + + // GetPreProcess can be set to munge the return value before being + // compared to GetValue + GetPreProcess func(interface{}) interface{} + }{ + // #0: Basic good + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "availability_zone", + Value: "foo", + + GetKey: "availability_zone", + GetValue: "foo", + }, + + // #1: Basic int + { + Schema: map[string]*Schema{ + "port": { + Type: TypeInt, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "port", + Value: 80, + + GetKey: "port", + GetValue: 80, + }, + + // #2: Basic bool + { + Schema: map[string]*Schema{ + "vpc": { + Type: TypeBool, + Optional: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "vpc", + Value: true, + + GetKey: "vpc", + GetValue: true, + }, + + // #3 + { + Schema: map[string]*Schema{ + "vpc": { + Type: TypeBool, + Optional: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "vpc", + Value: false, + + GetKey: "vpc", + GetValue: false, + }, + + // #4: Invalid type + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "availability_zone", + Value: 80, + Err: true, + + GetKey: "availability_zone", + GetValue: "", + }, + + // #5: List of primitives, set list + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Computed: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports", + Value: []int{1, 2, 5}, + + GetKey: "ports", + GetValue: []interface{}{1, 2, 5}, + }, + + // #6: List of primitives, set list with error + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Computed: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports", + Value: []interface{}{1, "NOPE", 5}, + Err: true, + + GetKey: "ports", + GetValue: []interface{}{}, + }, + + // #7: Set a list of maps + { + Schema: map[string]*Schema{ + "config_vars": { + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{ + Type: TypeMap, + }, + }, + }, + + State: nil, + + Diff: nil, + + Key: "config_vars", + Value: []interface{}{ + map[string]interface{}{ + "foo": "bar", + }, + map[string]interface{}{ + "bar": "baz", + }, + }, + Err: false, + + GetKey: "config_vars", + GetValue: []interface{}{ + map[string]interface{}{ + "foo": "bar", + }, + map[string]interface{}{ + "bar": "baz", + }, + }, + }, + + // #8: Set, with list + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.0": "100", + "ports.1": "80", + "ports.2": "80", + }, + }, + + Key: "ports", + Value: []interface{}{100, 125, 125}, + + GetKey: "ports", + GetValue: []interface{}{100, 125}, + }, + + // #9: Set, with Set + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.100": "100", + "ports.80": "80", + "ports.81": "81", + }, + }, + + Key: "ports", + Value: &Set{ + m: map[string]interface{}{ + "1": 1, + "2": 2, + }, + }, + + GetKey: "ports", + GetValue: []interface{}{1, 2}, + }, + + // #10: Set single item + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "2", + "ports.100": "100", + "ports.80": "80", + }, + }, + + Key: "ports.100", + Value: 256, + Err: true, + + GetKey: "ports", + GetValue: []interface{}{100, 80}, + }, + + // #11: Set with nested set + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Elem: &Resource{ + Schema: map[string]*Schema{ + "port": { + Type: TypeInt, + }, + + "set": { + Type: TypeSet, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + }, + Set: func(a interface{}) int { + return a.(map[string]interface{})["port"].(int) + }, + }, + }, + + State: nil, + + Key: "ports", + Value: []interface{}{ + map[string]interface{}{ + "port": 80, + }, + }, + + GetKey: "ports", + GetValue: []interface{}{ + map[string]interface{}{ + "port": 80, + "set": []interface{}{}, + }, + }, + + GetPreProcess: func(v interface{}) interface{} { + if v == nil { + return v + } + s, ok := v.([]interface{}) + if !ok { + return v + } + for _, v := range s { + m, ok := v.(map[string]interface{}) + if !ok { + continue + } + if m["set"] == nil { + continue + } + if s, ok := m["set"].(*Set); ok { + m["set"] = s.List() + } + } + + return v + }, + }, + + // #12: List of floats, set list + { + Schema: map[string]*Schema{ + "ratios": { + Type: TypeList, + Computed: true, + Elem: &Schema{Type: TypeFloat}, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ratios", + Value: []float64{1.0, 2.2, 5.5}, + + GetKey: "ratios", + GetValue: []interface{}{1.0, 2.2, 5.5}, + }, + + // #12: Set of floats, set list + { + Schema: map[string]*Schema{ + "ratios": { + Type: TypeSet, + Computed: true, + Elem: &Schema{Type: TypeFloat}, + Set: func(a interface{}) int { + return int(math.Float64bits(a.(float64))) + }, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ratios", + Value: []float64{1.0, 2.2, 5.5}, + + GetKey: "ratios", + GetValue: []interface{}{1.0, 2.2, 5.5}, + }, + + // #13: Basic pointer + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "availability_zone", + Value: testPtrTo("foo"), + + GetKey: "availability_zone", + GetValue: "foo", + }, + + // #14: Basic nil value + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "availability_zone", + Value: testPtrTo(nil), + + GetKey: "availability_zone", + GetValue: "", + }, + + // #15: Basic nil pointer + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "availability_zone", + Value: testNilPtr, + + GetKey: "availability_zone", + GetValue: "", + }, + + // #16: Set in a list + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Elem: &Resource{ + Schema: map[string]*Schema{ + "set": { + Type: TypeSet, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + }, + }, + }, + + State: nil, + + Key: "ports", + Value: []interface{}{ + map[string]interface{}{ + "set": []interface{}{ + 1, + }, + }, + }, + + GetKey: "ports", + GetValue: []interface{}{ + map[string]interface{}{ + "set": []interface{}{ + 1, + }, + }, + }, + GetPreProcess: func(v interface{}) interface{} { + if v == nil { + return v + } + s, ok := v.([]interface{}) + if !ok { + return v + } + for _, v := range s { + m, ok := v.(map[string]interface{}) + if !ok { + continue + } + if m["set"] == nil { + continue + } + if s, ok := m["set"].(*Set); ok { + m["set"] = s.List() + } + } + + return v + }, + }, + } + + for i, tc := range cases { + d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + err = d.Set(tc.Key, tc.Value) + if err != nil != tc.Err { + t.Fatalf("%d err: %s", i, err) + } + + v := d.Get(tc.GetKey) + if s, ok := v.(*Set); ok { + v = s.List() + } + + if tc.GetPreProcess != nil { + v = tc.GetPreProcess(v) + } + + if !reflect.DeepEqual(v, tc.GetValue) { + t.Fatalf("Get Bad: %d\n\n%#v", i, v) + } + } +} + +func TestResourceDataState_schema(t *testing.T) { + cases := []struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Set map[string]interface{} + Result *terraform.InstanceState + }{ + // #0 Basic primitive in diff + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + }, + + // #1 Basic primitive set override + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Set: map[string]interface{}{ + "availability_zone": "bar", + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "bar", + }, + }, + }, + + // #2 + { + Schema: map[string]*Schema{ + "vpc": { + Type: TypeBool, + Optional: true, + }, + }, + + State: nil, + + Diff: nil, + + Set: map[string]interface{}{ + "vpc": true, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "vpc": "true", + }, + }, + }, + + // #3 Basic primitive with StateFunc set + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + StateFunc: func(interface{}) string { return "" }, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + NewExtra: "foo!", + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + }, + + // #4 List + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "1", + "ports.0": "80", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "1", + New: "2", + }, + "ports.1": { + Old: "", + New: "100", + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "2", + "ports.0": "80", + "ports.1": "100", + }, + }, + }, + + // #5 List of resources + { + Schema: map[string]*Schema{ + "ingress": { + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": { + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ingress.#": "1", + "ingress.0.from": "80", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ingress.#": { + Old: "1", + New: "2", + }, + "ingress.0.from": { + Old: "80", + New: "150", + }, + "ingress.1.from": { + Old: "", + New: "100", + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ingress.#": "2", + "ingress.0.from": "150", + "ingress.1.from": "100", + }, + }, + }, + + // #6 List of maps + { + Schema: map[string]*Schema{ + "config_vars": { + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{ + Type: TypeMap, + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.#": "2", + "config_vars.0.%": "2", + "config_vars.0.foo": "bar", + "config_vars.0.bar": "bar", + "config_vars.1.%": "1", + "config_vars.1.bar": "baz", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.0.bar": { + NewRemoved: true, + }, + }, + }, + + Set: map[string]interface{}{ + "config_vars": []map[string]interface{}{ + { + "foo": "bar", + }, + { + "baz": "bang", + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.#": "2", + "config_vars.0.%": "1", + "config_vars.0.foo": "bar", + "config_vars.1.%": "1", + "config_vars.1.baz": "bang", + }, + }, + }, + + // #7 List of maps with removal in diff + { + Schema: map[string]*Schema{ + "config_vars": { + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{ + Type: TypeMap, + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.#": "1", + "config_vars.0.FOO": "bar", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.#": { + Old: "1", + New: "0", + }, + "config_vars.0.FOO": { + Old: "bar", + NewRemoved: true, + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.#": "0", + }, + }, + }, + + // #8 Basic state with other keys + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Result: &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + "availability_zone": "foo", + }, + }, + }, + + // #9 Sets + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.100": "100", + "ports.80": "80", + "ports.81": "81", + }, + }, + + Diff: nil, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.80": "80", + "ports.81": "81", + "ports.100": "100", + }, + }, + }, + + // #10 + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Diff: nil, + + Set: map[string]interface{}{ + "ports": []interface{}{100, 80}, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "2", + "ports.80": "80", + "ports.100": "100", + }, + }, + }, + + // #11 + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "order": { + Type: TypeInt, + }, + + "a": { + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + }, + + "b": { + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + }, + }, + }, + Set: func(a interface{}) int { + m := a.(map[string]interface{}) + return m["order"].(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "2", + "ports.10.order": "10", + "ports.10.a.#": "1", + "ports.10.a.0": "80", + "ports.20.order": "20", + "ports.20.b.#": "1", + "ports.20.b.0": "100", + }, + }, + + Set: map[string]interface{}{ + "ports": []interface{}{ + map[string]interface{}{ + "order": 20, + "b": []interface{}{100}, + }, + map[string]interface{}{ + "order": 10, + "a": []interface{}{80}, + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "2", + "ports.10.order": "10", + "ports.10.a.#": "1", + "ports.10.a.0": "80", + "ports.10.b.#": "0", + "ports.20.order": "20", + "ports.20.a.#": "0", + "ports.20.b.#": "1", + "ports.20.b.0": "100", + }, + }, + }, + + // #12 Maps + { + Schema: map[string]*Schema{ + "tags": { + Type: TypeMap, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "tags.Name": { + Old: "", + New: "foo", + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "tags.%": "1", + "tags.Name": "foo", + }, + }, + }, + + // #13 empty computed map + { + Schema: map[string]*Schema{ + "tags": { + Type: TypeMap, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "tags.Name": { + Old: "", + New: "foo", + }, + }, + }, + + Set: map[string]interface{}{ + "tags": map[string]string{}, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "tags.%": "0", + }, + }, + }, + + // #14 + { + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + NewComputed: true, + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{}, + }, + }, + + // #15 + { + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + NewComputed: true, + }, + }, + }, + + Set: map[string]interface{}{ + "foo": "bar", + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + }, + + // #16 Set of maps + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": {Type: TypeInt}, + "uuids": {Type: TypeMap}, + }, + }, + Set: func(a interface{}) int { + m := a.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.10.uuids.#": { + NewComputed: true, + }, + }, + }, + + Set: map[string]interface{}{ + "ports": []interface{}{ + map[string]interface{}{ + "index": 10, + "uuids": map[string]interface{}{ + "80": "value", + }, + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "1", + "ports.10.index": "10", + "ports.10.uuids.%": "1", + "ports.10.uuids.80": "value", + }, + }, + }, + + // #17 + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.100": "100", + "ports.80": "80", + "ports.81": "81", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "3", + New: "0", + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "0", + }, + }, + }, + + // #18 + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Diff: nil, + + Set: map[string]interface{}{ + "ports": []interface{}{}, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "0", + }, + }, + }, + + // #19 + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Diff: nil, + + Set: map[string]interface{}{ + "ports": []interface{}{}, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "0", + }, + }, + }, + + // #20 Set lists + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": {Type: TypeInt}, + "uuids": {Type: TypeMap}, + }, + }, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + NewComputed: true, + }, + }, + }, + + Set: map[string]interface{}{ + "ports": []interface{}{ + map[string]interface{}{ + "index": 10, + "uuids": map[string]interface{}{ + "80": "value", + }, + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "1", + "ports.0.index": "10", + "ports.0.uuids.%": "1", + "ports.0.uuids.80": "value", + }, + }, + }, + } + + for i, tc := range cases { + d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + for k, v := range tc.Set { + if err := d.Set(k, v); err != nil { + t.Fatalf("%d err: %s", i, err) + } + } + + // Set an ID so that the state returned is not nil + idSet := false + if d.Id() == "" { + idSet = true + d.SetId("foo") + } + + actual := d.State() + + // If we set an ID, then undo what we did so the comparison works + if actual != nil && idSet { + actual.ID = "" + delete(actual.Attributes, "id") + } + + if !reflect.DeepEqual(actual, tc.Result) { + t.Fatalf("Bad: %d\n\n%#v\n\nExpected:\n\n%#v", i, actual, tc.Result) + } + } +} + +func TestResourceData_nonStringValuesInMap(t *testing.T) { + cases := []struct { + Schema map[string]*Schema + Diff *terraform.InstanceDiff + MapFieldName string + ItemName string + ExpectedType string + }{ + { + Schema: map[string]*Schema{ + "boolMap": { + Type: TypeMap, + Elem: TypeBool, + Optional: true, + }, + }, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "boolMap.%": { + Old: "", + New: "1", + }, + "boolMap.boolField": { + Old: "", + New: "true", + }, + }, + }, + MapFieldName: "boolMap", + ItemName: "boolField", + ExpectedType: "bool", + }, + { + Schema: map[string]*Schema{ + "intMap": { + Type: TypeMap, + Elem: TypeInt, + Optional: true, + }, + }, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "intMap.%": { + Old: "", + New: "1", + }, + "intMap.intField": { + Old: "", + New: "8", + }, + }, + }, + MapFieldName: "intMap", + ItemName: "intField", + ExpectedType: "int", + }, + { + Schema: map[string]*Schema{ + "floatMap": { + Type: TypeMap, + Elem: TypeFloat, + Optional: true, + }, + }, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "floatMap.%": { + Old: "", + New: "1", + }, + "floatMap.floatField": { + Old: "", + New: "8.22", + }, + }, + }, + MapFieldName: "floatMap", + ItemName: "floatField", + ExpectedType: "float64", + }, + } + + for _, c := range cases { + d, err := schemaMap(c.Schema).Data(nil, c.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + m, ok := d.Get(c.MapFieldName).(map[string]interface{}) + if !ok { + t.Fatalf("expected %q to be castable to a map", c.MapFieldName) + } + field, ok := m[c.ItemName] + if !ok { + t.Fatalf("expected %q in the map", c.ItemName) + } + + typeName := reflect.TypeOf(field).Name() + if typeName != c.ExpectedType { + t.Fatalf("expected %q to be %q, it is %q.", + c.ItemName, c.ExpectedType, typeName) + } + } +} + +func TestResourceDataSetConnInfo(t *testing.T) { + d := &ResourceData{} + d.SetId("foo") + d.SetConnInfo(map[string]string{ + "foo": "bar", + }) + + expected := map[string]string{ + "foo": "bar", + } + + actual := d.State() + if !reflect.DeepEqual(actual.Ephemeral.ConnInfo, expected) { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceDataSetMeta_Timeouts(t *testing.T) { + d := &ResourceData{} + d.SetId("foo") + + rt := ResourceTimeout{ + Create: DefaultTimeout(7 * time.Minute), + } + + d.timeouts = &rt + + expected := expectedForValues(7, 0, 0, 0, 0) + + actual := d.State() + if !reflect.DeepEqual(actual.Meta[TimeoutKey], expected) { + t.Fatalf("Bad Meta_timeout match:\n\texpected: %#v\n\tgot: %#v", expected, actual.Meta[TimeoutKey]) + } +} + +func TestResourceDataSetId(t *testing.T) { + d := &ResourceData{ + state: &terraform.InstanceState{ + ID: "test", + Attributes: map[string]string{ + "id": "test", + }, + }, + } + d.SetId("foo") + + actual := d.State() + + // SetId should set both the ID field as well as the attribute, to aid in + // transitioning to the new type system. + if actual.ID != "foo" || actual.Attributes["id"] != "foo" { + t.Fatalf("bad: %#v", actual) + } + + d.SetId("") + actual = d.State() + if actual != nil { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceDataSetId_clear(t *testing.T) { + d := &ResourceData{ + state: &terraform.InstanceState{ID: "bar"}, + } + d.SetId("") + + actual := d.State() + if actual != nil { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceDataSetId_override(t *testing.T) { + d := &ResourceData{ + state: &terraform.InstanceState{ID: "bar"}, + } + d.SetId("foo") + + actual := d.State() + if actual.ID != "foo" { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceDataSetType(t *testing.T) { + d := &ResourceData{} + d.SetId("foo") + d.SetType("bar") + + actual := d.State() + if v := actual.Ephemeral.Type; v != "bar" { + t.Fatalf("bad: %#v", actual) + } +} + +func testPtrTo(raw interface{}) interface{} { + return &raw +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_diff.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_diff.go index f55a66e1..984929df 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_diff.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_diff.go @@ -7,7 +7,7 @@ import ( "strings" "sync" - "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) // newValueWriter is a minor re-implementation of MapFieldWriter to include diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_diff_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_diff_test.go new file mode 100644 index 00000000..5a0b0e0a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_diff_test.go @@ -0,0 +1,2046 @@ +package schema + +import ( + "fmt" + "reflect" + "sort" + "testing" + + "github.com/davecgh/go-spew/spew" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/hcl2shim" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +// testSetFunc is a very simple function we use to test a foo/bar complex set. +// Both "foo" and "bar" are int values. +// +// This is not foolproof as since it performs sums, you can run into +// collisions. Spec tests accordingly. :P +func testSetFunc(v interface{}) int { + m := v.(map[string]interface{}) + return m["foo"].(int) + m["bar"].(int) +} + +// resourceDiffTestCase provides a test case struct for SetNew and SetDiff. +type resourceDiffTestCase struct { + Name string + Schema map[string]*Schema + State *terraform.InstanceState + Config *terraform.ResourceConfig + Diff *terraform.InstanceDiff + Key string + OldValue interface{} + NewValue interface{} + Expected *terraform.InstanceDiff + ExpectedKeys []string + ExpectedError bool +} + +// testDiffCases produces a list of test cases for use with SetNew and SetDiff. +func testDiffCases(t *testing.T, oldPrefix string, oldOffset int, computed bool) []resourceDiffTestCase { + return []resourceDiffTestCase{ + { + Name: "basic primitive diff", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "baz", + }, + }, + }, + Key: "foo", + NewValue: "qux", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: func() string { + if computed { + return "" + } + return "qux" + }(), + NewComputed: computed, + }, + }, + }, + }, + { + Name: "basic set diff", + Schema: map[string]*Schema{ + "foo": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeString}, + Set: HashString, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo.#": "1", + "foo.1996459178": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": []interface{}{"baz"}, + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.1996459178": { + Old: "bar", + New: "", + NewRemoved: true, + }, + "foo.2015626392": { + Old: "", + New: "baz", + }, + }, + }, + Key: "foo", + NewValue: []interface{}{"qux"}, + Expected: &terraform.InstanceDiff{ + Attributes: func() map[string]*terraform.ResourceAttrDiff { + result := map[string]*terraform.ResourceAttrDiff{} + if computed { + result["foo.#"] = &terraform.ResourceAttrDiff{ + Old: "1", + New: "", + NewComputed: true, + } + } else { + result["foo.2800005064"] = &terraform.ResourceAttrDiff{ + Old: "", + New: "qux", + } + result["foo.1996459178"] = &terraform.ResourceAttrDiff{ + Old: "bar", + New: "", + NewRemoved: true, + } + } + return result + }(), + }, + }, + { + Name: "basic list diff", + Schema: map[string]*Schema{ + "foo": { + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeString}, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo.#": "1", + "foo.0": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": []interface{}{"baz"}, + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.0": { + Old: "bar", + New: "baz", + }, + }, + }, + Key: "foo", + NewValue: []interface{}{"qux"}, + Expected: &terraform.InstanceDiff{ + Attributes: func() map[string]*terraform.ResourceAttrDiff { + result := make(map[string]*terraform.ResourceAttrDiff) + if computed { + result["foo.#"] = &terraform.ResourceAttrDiff{ + Old: "1", + New: "", + NewComputed: true, + } + } else { + result["foo.0"] = &terraform.ResourceAttrDiff{ + Old: "bar", + New: "qux", + } + } + return result + }(), + }, + }, + { + Name: "basic map diff", + Schema: map[string]*Schema{ + "foo": { + Type: TypeMap, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo.%": "1", + "foo.bar": "baz", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": map[string]interface{}{"bar": "qux"}, + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.bar": { + Old: "baz", + New: "qux", + }, + }, + }, + Key: "foo", + NewValue: map[string]interface{}{"bar": "quux"}, + Expected: &terraform.InstanceDiff{ + Attributes: func() map[string]*terraform.ResourceAttrDiff { + result := make(map[string]*terraform.ResourceAttrDiff) + if computed { + result["foo.%"] = &terraform.ResourceAttrDiff{ + Old: "", + New: "", + NewComputed: true, + } + result["foo.bar"] = &terraform.ResourceAttrDiff{ + Old: "baz", + New: "", + NewRemoved: true, + } + } else { + result["foo.bar"] = &terraform.ResourceAttrDiff{ + Old: "baz", + New: "quux", + } + } + return result + }(), + }, + }, + { + Name: "additional diff with primitive", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + }, + "one": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + "one": "two", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "baz", + }, + }, + }, + Key: "one", + NewValue: "four", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "baz", + }, + "one": { + Old: "two", + New: func() string { + if computed { + return "" + } + return "four" + }(), + NewComputed: computed, + }, + }, + }, + }, + { + Name: "additional diff with primitive computed only", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + }, + "one": { + Type: TypeString, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + "one": "two", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "baz", + }, + }, + }, + Key: "one", + NewValue: "three", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "baz", + }, + "one": { + Old: "two", + New: func() string { + if computed { + return "" + } + return "three" + }(), + NewComputed: computed, + }, + }, + }, + }, + { + Name: "complex-ish set diff", + Schema: map[string]*Schema{ + "top": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + Computed: true, + }, + "bar": { + Type: TypeInt, + Optional: true, + Computed: true, + }, + }, + }, + Set: testSetFunc, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "top.#": "2", + "top.3.foo": "1", + "top.3.bar": "2", + "top.23.foo": "11", + "top.23.bar": "12", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "top": []interface{}{ + map[string]interface{}{ + "foo": 1, + "bar": 3, + }, + map[string]interface{}{ + "foo": 12, + "bar": 12, + }, + }, + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "top.4.foo": { + Old: "", + New: "1", + }, + "top.4.bar": { + Old: "", + New: "3", + }, + "top.24.foo": { + Old: "", + New: "12", + }, + "top.24.bar": { + Old: "", + New: "12", + }, + }, + }, + Key: "top", + NewValue: NewSet(testSetFunc, []interface{}{ + map[string]interface{}{ + "foo": 1, + "bar": 4, + }, + map[string]interface{}{ + "foo": 13, + "bar": 12, + }, + map[string]interface{}{ + "foo": 21, + "bar": 22, + }, + }), + Expected: &terraform.InstanceDiff{ + Attributes: func() map[string]*terraform.ResourceAttrDiff { + result := make(map[string]*terraform.ResourceAttrDiff) + if computed { + result["top.#"] = &terraform.ResourceAttrDiff{ + Old: "2", + New: "", + NewComputed: true, + } + } else { + result["top.#"] = &terraform.ResourceAttrDiff{ + Old: "2", + New: "3", + } + result["top.5.foo"] = &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + } + result["top.5.bar"] = &terraform.ResourceAttrDiff{ + Old: "", + New: "4", + } + result["top.25.foo"] = &terraform.ResourceAttrDiff{ + Old: "", + New: "13", + } + result["top.25.bar"] = &terraform.ResourceAttrDiff{ + Old: "", + New: "12", + } + result["top.43.foo"] = &terraform.ResourceAttrDiff{ + Old: "", + New: "21", + } + result["top.43.bar"] = &terraform.ResourceAttrDiff{ + Old: "", + New: "22", + } + } + return result + }(), + }, + }, + { + Name: "primitive, no diff, no refresh", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{}), + Diff: &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{}}, + Key: "foo", + NewValue: "baz", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: func() string { + if computed { + return "" + } + return "baz" + }(), + NewComputed: computed, + }, + }, + }, + }, + { + Name: "non-computed key, should error", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "baz", + }, + }, + }, + Key: "foo", + NewValue: "qux", + ExpectedError: true, + }, + { + Name: "bad key, should error", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "baz", + }, + }, + }, + Key: "bad", + NewValue: "qux", + ExpectedError: true, + }, + { + // NOTE: This case is technically impossible in the current + // implementation, because optional+computed values never show up in the + // diff, and we actually clear existing diffs when SetNew or + // SetNewComputed is run. This test is here to ensure that if either of + // these behaviors change that we don't introduce regressions. + Name: "NewRemoved in diff for Optional and Computed, should be fully overridden", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{}), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "", + NewRemoved: true, + }, + }, + }, + Key: "foo", + NewValue: "qux", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: func() string { + if computed { + return "" + } + return "qux" + }(), + NewComputed: computed, + }, + }, + }, + }, + { + Name: "NewComputed should always propagate", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "", + }, + ID: "pre-existing", + }, + Config: testConfig(t, map[string]interface{}{}), + Diff: &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{}}, + Key: "foo", + NewValue: "", + Expected: &terraform.InstanceDiff{ + Attributes: func() map[string]*terraform.ResourceAttrDiff { + if computed { + return map[string]*terraform.ResourceAttrDiff{ + "foo": { + NewComputed: computed, + }, + } + } + return map[string]*terraform.ResourceAttrDiff{} + }(), + }, + }, + } +} + +func TestSetNew(t *testing.T) { + testCases := testDiffCases(t, "", 0, false) + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + m := schemaMap(tc.Schema) + d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) + err := d.SetNew(tc.Key, tc.NewValue) + switch { + case err != nil && !tc.ExpectedError: + t.Fatalf("bad: %s", err) + case err == nil && tc.ExpectedError: + t.Fatalf("Expected error, got none") + case err != nil && tc.ExpectedError: + return + } + for _, k := range d.UpdatedKeys() { + if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { + t.Fatalf("bad: %s", err) + } + } + if !reflect.DeepEqual(tc.Expected, tc.Diff) { + t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff)) + } + }) + } +} + +func TestSetNewComputed(t *testing.T) { + testCases := testDiffCases(t, "", 0, true) + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + m := schemaMap(tc.Schema) + d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) + err := d.SetNewComputed(tc.Key) + switch { + case err != nil && !tc.ExpectedError: + t.Fatalf("bad: %s", err) + case err == nil && tc.ExpectedError: + t.Fatalf("Expected error, got none") + case err != nil && tc.ExpectedError: + return + } + for _, k := range d.UpdatedKeys() { + if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { + t.Fatalf("bad: %s", err) + } + } + if !reflect.DeepEqual(tc.Expected, tc.Diff) { + t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff)) + } + }) + } +} + +func TestForceNew(t *testing.T) { + cases := []resourceDiffTestCase{ + { + Name: "basic primitive diff", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "baz", + }, + }, + }, + Key: "foo", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "baz", + RequiresNew: true, + }, + }, + }, + }, + { + Name: "no change, should error", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "bar", + }), + ExpectedError: true, + }, + { + Name: "basic primitive, non-computed key", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "baz", + }, + }, + }, + Key: "foo", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "baz", + RequiresNew: true, + }, + }, + }, + }, + { + Name: "nested field", + Schema: map[string]*Schema{ + "foo": { + Type: TypeList, + Required: true, + MaxItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "bar": { + Type: TypeString, + Optional: true, + }, + "baz": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo.#": "1", + "foo.0.bar": "abc", + "foo.0.baz": "xyz", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": []interface{}{ + map[string]interface{}{ + "bar": "abcdefg", + "baz": "changed", + }, + }, + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.0.bar": { + Old: "abc", + New: "abcdefg", + }, + "foo.0.baz": { + Old: "xyz", + New: "changed", + }, + }, + }, + Key: "foo.0.baz", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.0.bar": { + Old: "abc", + New: "abcdefg", + }, + "foo.0.baz": { + Old: "xyz", + New: "changed", + RequiresNew: true, + }, + }, + }, + }, + { + Name: "preserve NewRemoved on existing diff", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{}), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "", + NewRemoved: true, + }, + }, + }, + Key: "foo", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "", + RequiresNew: true, + NewRemoved: true, + }, + }, + }, + }, + { + Name: "nested field, preserve original diff without zero values", + Schema: map[string]*Schema{ + "foo": { + Type: TypeList, + Required: true, + MaxItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "bar": { + Type: TypeString, + Optional: true, + }, + "baz": { + Type: TypeInt, + Optional: true, + }, + }, + }, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo.#": "1", + "foo.0.bar": "abc", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": []interface{}{ + map[string]interface{}{ + "bar": "abcdefg", + }, + }, + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.0.bar": { + Old: "abc", + New: "abcdefg", + }, + }, + }, + Key: "foo.0.bar", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.0.bar": { + Old: "abc", + New: "abcdefg", + RequiresNew: true, + }, + }, + }, + }, + } + for _, tc := range cases { + t.Run(tc.Name, func(t *testing.T) { + m := schemaMap(tc.Schema) + d := newResourceDiff(m, tc.Config, tc.State, tc.Diff) + err := d.ForceNew(tc.Key) + switch { + case err != nil && !tc.ExpectedError: + t.Fatalf("bad: %s", err) + case err == nil && tc.ExpectedError: + t.Fatalf("Expected error, got none") + case err != nil && tc.ExpectedError: + return + } + for _, k := range d.UpdatedKeys() { + if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { + t.Fatalf("bad: %s", err) + } + } + if !reflect.DeepEqual(tc.Expected, tc.Diff) { + t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff)) + } + }) + } +} + +func TestClear(t *testing.T) { + cases := []resourceDiffTestCase{ + { + Name: "basic primitive diff", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "baz", + }, + }, + }, + Key: "foo", + Expected: &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{}}, + }, + { + Name: "non-computed key, should error", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "baz", + }, + }, + }, + Key: "foo", + ExpectedError: true, + }, + { + Name: "multi-value, one removed", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + Computed: true, + }, + "one": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + "one": "two", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + "one": "three", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "baz", + }, + "one": { + Old: "two", + New: "three", + }, + }, + }, + Key: "one", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "baz", + }, + }, + }, + }, + { + Name: "basic sub-block diff", + Schema: map[string]*Schema{ + "foo": { + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "bar": { + Type: TypeString, + Optional: true, + Computed: true, + }, + "baz": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo.0.bar": "bar1", + "foo.0.baz": "baz1", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": []interface{}{ + map[string]interface{}{ + "bar": "bar2", + "baz": "baz1", + }, + }, + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.0.bar": { + Old: "bar1", + New: "bar2", + }, + }, + }, + Key: "foo.0.bar", + Expected: &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{}}, + }, + { + Name: "sub-block diff only partial clear", + Schema: map[string]*Schema{ + "foo": { + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "bar": { + Type: TypeString, + Optional: true, + Computed: true, + }, + "baz": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo.0.bar": "bar1", + "foo.0.baz": "baz1", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": []interface{}{ + map[string]interface{}{ + "bar": "bar2", + "baz": "baz2", + }, + }, + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.0.bar": { + Old: "bar1", + New: "bar2", + }, + "foo.0.baz": { + Old: "baz1", + New: "baz2", + }, + }, + }, + Key: "foo.0.bar", + Expected: &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.0.baz": { + Old: "baz1", + New: "baz2", + }, + }}, + }, + } + for _, tc := range cases { + t.Run(tc.Name, func(t *testing.T) { + m := schemaMap(tc.Schema) + d := newResourceDiff(m, tc.Config, tc.State, tc.Diff) + err := d.Clear(tc.Key) + switch { + case err != nil && !tc.ExpectedError: + t.Fatalf("bad: %s", err) + case err == nil && tc.ExpectedError: + t.Fatalf("Expected error, got none") + case err != nil && tc.ExpectedError: + return + } + for _, k := range d.UpdatedKeys() { + if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { + t.Fatalf("bad: %s", err) + } + } + if !reflect.DeepEqual(tc.Expected, tc.Diff) { + t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff)) + } + }) + } +} + +func TestGetChangedKeysPrefix(t *testing.T) { + cases := []resourceDiffTestCase{ + { + Name: "basic primitive diff", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "baz", + }, + }, + }, + Key: "foo", + ExpectedKeys: []string{ + "foo", + }, + }, + { + Name: "nested field filtering", + Schema: map[string]*Schema{ + "testfield": { + Type: TypeString, + Required: true, + }, + "foo": { + Type: TypeList, + Required: true, + MaxItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "bar": { + Type: TypeString, + Optional: true, + }, + "baz": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "testfield": "blablah", + "foo.#": "1", + "foo.0.bar": "abc", + "foo.0.baz": "xyz", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "testfield": "modified", + "foo": []interface{}{ + map[string]interface{}{ + "bar": "abcdefg", + "baz": "changed", + }, + }, + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "testfield": { + Old: "blablah", + New: "modified", + }, + "foo.0.bar": { + Old: "abc", + New: "abcdefg", + }, + "foo.0.baz": { + Old: "xyz", + New: "changed", + }, + }, + }, + Key: "foo", + ExpectedKeys: []string{ + "foo.0.bar", + "foo.0.baz", + }, + }, + } + for _, tc := range cases { + t.Run(tc.Name, func(t *testing.T) { + m := schemaMap(tc.Schema) + d := newResourceDiff(m, tc.Config, tc.State, tc.Diff) + keys := d.GetChangedKeysPrefix(tc.Key) + + for _, k := range d.UpdatedKeys() { + if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { + t.Fatalf("bad: %s", err) + } + } + + sort.Strings(keys) + + if !reflect.DeepEqual(tc.ExpectedKeys, keys) { + t.Fatalf("Expected %s, got %s", spew.Sdump(tc.ExpectedKeys), spew.Sdump(keys)) + } + }) + } +} + +func TestResourceDiffGetOkExists(t *testing.T) { + cases := []struct { + Name string + Schema map[string]*Schema + State *terraform.InstanceState + Config *terraform.ResourceConfig + Diff *terraform.InstanceDiff + Key string + Value interface{} + Ok bool + }{ + /* + * Primitives + */ + { + Name: "string-literal-empty", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "", + }, + }, + }, + + Key: "availability_zone", + Value: "", + Ok: true, + }, + + { + Name: "string-computed-empty", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + + Key: "availability_zone", + Value: "", + Ok: false, + }, + + { + Name: "string-optional-computed-nil-diff", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + Config: nil, + + Diff: nil, + + Key: "availability_zone", + Value: "", + Ok: false, + }, + + /* + * Lists + */ + + { + Name: "list-optional", + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + Config: nil, + + Diff: nil, + + Key: "ports", + Value: []interface{}{}, + Ok: false, + }, + + /* + * Map + */ + + { + Name: "map-optional", + Schema: map[string]*Schema{ + "ports": { + Type: TypeMap, + Optional: true, + }, + }, + + State: nil, + Config: nil, + + Diff: nil, + + Key: "ports", + Value: map[string]interface{}{}, + Ok: false, + }, + + /* + * Set + */ + + { + Name: "set-optional", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: nil, + Config: nil, + + Diff: nil, + + Key: "ports", + Value: []interface{}{}, + Ok: false, + }, + + { + Name: "set-optional-key", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: nil, + Config: nil, + + Diff: nil, + + Key: "ports.0", + Value: 0, + Ok: false, + }, + + { + Name: "bool-literal-empty", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeBool, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + Config: nil, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "", + }, + }, + }, + + Key: "availability_zone", + Value: false, + Ok: true, + }, + + { + Name: "bool-literal-set", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeBool, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + New: "true", + }, + }, + }, + + Key: "availability_zone", + Value: true, + Ok: true, + }, + { + Name: "value-in-config", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "availability_zone": "foo", + }), + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + + Key: "availability_zone", + Value: "foo", + Ok: true, + }, + { + Name: "new-value-in-config", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + }, + }, + + State: nil, + Config: testConfig(t, map[string]interface{}{ + "availability_zone": "foo", + }), + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + }, + }, + }, + + Key: "availability_zone", + Value: "foo", + Ok: true, + }, + { + Name: "optional-computed-value-in-config", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "availability_zone": "bar", + }), + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "foo", + New: "bar", + }, + }, + }, + + Key: "availability_zone", + Value: "bar", + Ok: true, + }, + { + Name: "removed-value", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig(t, map[string]interface{}{}), + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "foo", + New: "", + NewRemoved: true, + }, + }, + }, + + Key: "availability_zone", + Value: "", + Ok: true, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) + + v, ok := d.GetOkExists(tc.Key) + if s, ok := v.(*Set); ok { + v = s.List() + } + + if !reflect.DeepEqual(v, tc.Value) { + t.Fatalf("Bad %s: \n%#v", tc.Name, v) + } + if ok != tc.Ok { + t.Fatalf("%s: expected ok: %t, got: %t", tc.Name, tc.Ok, ok) + } + }) + } +} + +func TestResourceDiffGetOkExistsSetNew(t *testing.T) { + tc := struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Key string + Value interface{} + Ok bool + }{ + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + + Key: "availability_zone", + Value: "foobar", + Ok: true, + } + + d := newResourceDiff(tc.Schema, testConfig(t, map[string]interface{}{}), tc.State, tc.Diff) + d.SetNew(tc.Key, tc.Value) + + v, ok := d.GetOkExists(tc.Key) + if s, ok := v.(*Set); ok { + v = s.List() + } + + if !reflect.DeepEqual(v, tc.Value) { + t.Fatalf("Bad: \n%#v", v) + } + if ok != tc.Ok { + t.Fatalf("expected ok: %t, got: %t", tc.Ok, ok) + } +} + +func TestResourceDiffGetOkExistsSetNewComputed(t *testing.T) { + tc := struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Key string + Value interface{} + Ok bool + }{ + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + + Key: "availability_zone", + Value: "foobar", + Ok: false, + } + + d := newResourceDiff(tc.Schema, testConfig(t, map[string]interface{}{}), tc.State, tc.Diff) + d.SetNewComputed(tc.Key) + + _, ok := d.GetOkExists(tc.Key) + + if ok != tc.Ok { + t.Fatalf("expected ok: %t, got: %t", tc.Ok, ok) + } +} + +func TestResourceDiffNewValueKnown(t *testing.T) { + cases := []struct { + Name string + Schema map[string]*Schema + State *terraform.InstanceState + Config *terraform.ResourceConfig + Diff *terraform.InstanceDiff + Key string + Expected bool + }{ + { + Name: "in config, no state", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + }, + }, + State: nil, + Config: testConfig(t, map[string]interface{}{ + "availability_zone": "foo", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + }, + }, + }, + Key: "availability_zone", + Expected: true, + }, + { + Name: "in config, has state, no diff", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "availability_zone": "foo", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + Key: "availability_zone", + Expected: true, + }, + { + Name: "computed attribute, in state, no diff", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig(t, map[string]interface{}{}), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + Key: "availability_zone", + Expected: true, + }, + { + Name: "optional and computed attribute, in state, no config", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig(t, map[string]interface{}{}), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + Key: "availability_zone", + Expected: true, + }, + { + Name: "optional and computed attribute, in state, with config", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "availability_zone": "foo", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + Key: "availability_zone", + Expected: true, + }, + { + Name: "computed value, through config reader", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig( + t, + map[string]interface{}{ + "availability_zone": hcl2shim.UnknownVariableValue, + }, + ), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + Key: "availability_zone", + Expected: false, + }, + { + Name: "computed value, through diff reader", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig( + t, + map[string]interface{}{ + "availability_zone": hcl2shim.UnknownVariableValue, + }, + ), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "foo", + New: "", + NewComputed: true, + }, + }, + }, + Key: "availability_zone", + Expected: false, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) + + actual := d.NewValueKnown(tc.Key) + if tc.Expected != actual { + t.Fatalf("%s: expected ok: %t, got: %t", tc.Name, tc.Expected, actual) + } + }) + } +} + +func TestResourceDiffNewValueKnownSetNew(t *testing.T) { + tc := struct { + Schema map[string]*Schema + State *terraform.InstanceState + Config *terraform.ResourceConfig + Diff *terraform.InstanceDiff + Key string + Value interface{} + Expected bool + }{ + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig( + t, + map[string]interface{}{ + "availability_zone": hcl2shim.UnknownVariableValue, + }, + ), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "foo", + New: "", + NewComputed: true, + }, + }, + }, + Key: "availability_zone", + Value: "bar", + Expected: true, + } + + d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) + d.SetNew(tc.Key, tc.Value) + + actual := d.NewValueKnown(tc.Key) + if tc.Expected != actual { + t.Fatalf("expected ok: %t, got: %t", tc.Expected, actual) + } +} + +func TestResourceDiffNewValueKnownSetNewComputed(t *testing.T) { + tc := struct { + Schema map[string]*Schema + State *terraform.InstanceState + Config *terraform.ResourceConfig + Diff *terraform.InstanceDiff + Key string + Expected bool + }{ + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig(t, map[string]interface{}{}), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + Key: "availability_zone", + Expected: false, + } + + d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) + d.SetNewComputed(tc.Key) + + actual := d.NewValueKnown(tc.Key) + if tc.Expected != actual { + t.Fatalf("expected ok: %t, got: %t", tc.Expected, actual) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_importer.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_importer.go index 5dada3ca..3b17c8e9 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_importer.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_importer.go @@ -1,5 +1,10 @@ package schema +import ( + "context" + "errors" +) + // ResourceImporter defines how a resource is imported in Terraform. This // can be set onto a Resource struct to make it Importable. Not all resources // have to be importable; if a Resource doesn't have a ResourceImporter then @@ -9,15 +14,26 @@ package schema // resource and bringing it under Terraform management. This can include // updating Terraform state, generating Terraform configuration, etc. type ResourceImporter struct { - // The functions below must all be implemented for importing to work. - // State is called to convert an ID to one or more InstanceState to - // insert into the Terraform state. If this isn't specified, then - // the ID is passed straight through. + // insert into the Terraform state. + // + // Deprecated: State is deprecated in favor of StateContext. + // Only one of the two functions can bet set. State StateFunc + + // StateContext is called to convert an ID to one or more InstanceState to + // insert into the Terraform state. If this isn't specified, then + // the ID is passed straight through. This function receives a context + // that will cancel if Terraform sends a cancellation signal. + StateContext StateContextFunc } -// StateFunc is the function called to import a resource into the +// StateFunc is the function called to import a resource into the Terraform state. +// +// Deprecated: Please use the context aware equivalent StateContextFunc. +type StateFunc func(*ResourceData, interface{}) ([]*ResourceData, error) + +// StateContextFunc is the function called to import a resource into the // Terraform state. It is given a ResourceData with only ID set. This // ID is going to be an arbitrary value given by the user and may not map // directly to the ID format that the resource expects, so that should @@ -31,7 +47,7 @@ type ResourceImporter struct { // // To create the ResourceData structures for other resource types (if // you have to), instantiate your resource and call the Data function. -type StateFunc func(*ResourceData, interface{}) ([]*ResourceData, error) +type StateContextFunc func(context.Context, *ResourceData, interface{}) ([]*ResourceData, error) // InternalValidate should be called to validate the structure of this // importer. This should be called in a unit test. @@ -41,12 +57,23 @@ type StateFunc func(*ResourceData, interface{}) ([]*ResourceData, error) // automatically called by Provider.InternalValidate(), so you only need // to internal validate the provider. func (r *ResourceImporter) InternalValidate() error { + if r.State != nil && r.StateContext != nil { + return errors.New("Both State and StateContext cannot be set.") + } return nil } // ImportStatePassthrough is an implementation of StateFunc that can be +// used to simply pass the ID directly through. +// +// Deprecated: Please use the context aware ImportStatePassthroughContext instead +func ImportStatePassthrough(d *ResourceData, m interface{}) ([]*ResourceData, error) { + return []*ResourceData{d}, nil +} + +// ImportStatePassthroughContext is an implementation of StateContextFunc that can be // used to simply pass the ID directly through. This should be used only // in the case that an ID-only refresh is possible. -func ImportStatePassthrough(d *ResourceData, m interface{}) ([]*ResourceData, error) { +func ImportStatePassthroughContext(ctx context.Context, d *ResourceData, m interface{}) ([]*ResourceData, error) { return []*ResourceData{d}, nil } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_importer_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_importer_test.go new file mode 100644 index 00000000..bbdd4b88 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_importer_test.go @@ -0,0 +1,13 @@ +package schema + +import "testing" + +func TestInternalValidate(t *testing.T) { + r := &ResourceImporter{ + State: ImportStatePassthrough, + StateContext: ImportStatePassthroughContext, + } + if err := r.InternalValidate(); err == nil { + t.Fatal("ResourceImporter should not allow State and StateContext to be set") + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_test.go new file mode 100644 index 00000000..356daa9c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_test.go @@ -0,0 +1,1563 @@ +package schema + +import ( + "context" + "encoding/json" + "fmt" + "reflect" + "testing" + "time" + + "github.com/hashicorp/go-cty/cty" + ctyjson "github.com/hashicorp/go-cty/cty/json" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/hcl2shim" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/diagutils" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestResourceApply_create(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + } + + called := false + r.Create = func(d *ResourceData, m interface{}) error { + called = true + d.SetId("foo") + return nil + } + + var s *terraform.InstanceState = nil + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + New: "42", + }, + }, + } + + actual, diags := r.Apply(context.Background(), s, d, nil) + if diags.HasError() { + t.Fatalf("err: %s", diagutils.ErrorDiags(diags)) + } + + if !called { + t.Fatal("not called") + } + + expected := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "42", + }, + Meta: map[string]interface{}{ + "schema_version": "2", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceApply_Timeout_state(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + Timeouts: &ResourceTimeout{ + Create: DefaultTimeout(40 * time.Minute), + Update: DefaultTimeout(80 * time.Minute), + Delete: DefaultTimeout(40 * time.Minute), + }, + } + + called := false + r.Create = func(d *ResourceData, m interface{}) error { + called = true + d.SetId("foo") + return nil + } + + var s *terraform.InstanceState = nil + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + New: "42", + }, + }, + } + + diffTimeout := &ResourceTimeout{ + Create: DefaultTimeout(40 * time.Minute), + Update: DefaultTimeout(80 * time.Minute), + Delete: DefaultTimeout(40 * time.Minute), + } + + if err := diffTimeout.DiffEncode(d); err != nil { + t.Fatalf("Error encoding timeout to diff: %s", err) + } + + actual, diags := r.Apply(context.Background(), s, d, nil) + if diags.HasError() { + t.Fatalf("err: %s", diagutils.ErrorDiags(diags)) + } + + if !called { + t.Fatal("not called") + } + + expected := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "42", + }, + Meta: map[string]interface{}{ + "schema_version": "2", + TimeoutKey: expectedForValues(40, 0, 80, 40, 0), + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("Not equal in Timeout State:\n\texpected: %#v\n\tactual: %#v", expected.Meta, actual.Meta) + } +} + +// Regression test to ensure that the meta data is read from state, if a +// resource is destroyed and the timeout meta is no longer available from the +// config +func TestResourceApply_Timeout_destroy(t *testing.T) { + timeouts := &ResourceTimeout{ + Create: DefaultTimeout(40 * time.Minute), + Update: DefaultTimeout(80 * time.Minute), + Delete: DefaultTimeout(40 * time.Minute), + } + + r := &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + Timeouts: timeouts, + } + + called := false + var delTimeout time.Duration + r.Delete = func(d *ResourceData, m interface{}) error { + delTimeout = d.Timeout(TimeoutDelete) + called = true + return nil + } + + s := &terraform.InstanceState{ + ID: "bar", + } + + if err := timeouts.StateEncode(s); err != nil { + t.Fatalf("Error encoding to state: %s", err) + } + + d := &terraform.InstanceDiff{ + Destroy: true, + } + + actual, diags := r.Apply(context.Background(), s, d, nil) + if diags.HasError() { + t.Fatalf("err: %s", diagutils.ErrorDiags(diags)) + } + + if !called { + t.Fatal("delete not called") + } + + if *timeouts.Delete != delTimeout { + t.Fatalf("timeouts don't match, expected (%#v), got (%#v)", timeouts.Delete, delTimeout) + } + + if actual != nil { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceDiff_Timeout_diff(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + Timeouts: &ResourceTimeout{ + Create: DefaultTimeout(40 * time.Minute), + Update: DefaultTimeout(80 * time.Minute), + Delete: DefaultTimeout(40 * time.Minute), + }, + } + + r.Create = func(d *ResourceData, m interface{}) error { + d.SetId("foo") + return nil + } + + conf := terraform.NewResourceConfigRaw( + map[string]interface{}{ + "foo": 42, + TimeoutsConfigKey: map[string]interface{}{ + "create": "2h", + }, + }, + ) + var s *terraform.InstanceState + + actual, err := r.Diff(context.Background(), s, conf, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + New: "42", + }, + }, + } + + diffTimeout := &ResourceTimeout{ + Create: DefaultTimeout(120 * time.Minute), + Update: DefaultTimeout(80 * time.Minute), + Delete: DefaultTimeout(40 * time.Minute), + } + + if err := diffTimeout.DiffEncode(expected); err != nil { + t.Fatalf("Error encoding timeout to diff: %s", err) + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("Not equal Meta in Timeout Diff:\n\texpected: %#v\n\tactual: %#v", expected.Meta, actual.Meta) + } +} + +func TestResourceDiff_CustomizeFunc(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + } + + var called bool + + r.CustomizeDiff = func(_ context.Context, d *ResourceDiff, m interface{}) error { + called = true + return nil + } + + conf := terraform.NewResourceConfigRaw( + map[string]interface{}{ + "foo": 42, + }, + ) + + var s *terraform.InstanceState + + _, err := r.Diff(context.Background(), s, conf, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !called { + t.Fatalf("diff customization not called") + } +} + +func TestResourceApply_destroy(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + } + + called := false + r.Delete = func(d *ResourceData, m interface{}) error { + called = true + return nil + } + + s := &terraform.InstanceState{ + ID: "bar", + } + + d := &terraform.InstanceDiff{ + Destroy: true, + } + + actual, diags := r.Apply(context.Background(), s, d, nil) + if diags.HasError() { + t.Fatalf("err: %s", diagutils.ErrorDiags(diags)) + } + + if !called { + t.Fatal("delete not called") + } + + if actual != nil { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceApply_destroyCreate(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + + "tags": { + Type: TypeMap, + Optional: true, + Computed: true, + }, + }, + } + + change := false + r.Create = func(d *ResourceData, m interface{}) error { + change = d.HasChange("tags") + d.SetId("foo") + return nil + } + r.Delete = func(d *ResourceData, m interface{}) error { + return nil + } + + var s *terraform.InstanceState = &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "foo": "bar", + "tags.Name": "foo", + }, + } + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + New: "42", + RequiresNew: true, + }, + "tags.Name": { + Old: "foo", + New: "foo", + RequiresNew: true, + }, + }, + } + + actual, diags := r.Apply(context.Background(), s, d, nil) + if diags.HasError() { + t.Fatalf("err: %s", diagutils.ErrorDiags(diags)) + } + + if !change { + t.Fatal("should have change") + } + + expected := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "42", + "tags.%": "1", + "tags.Name": "foo", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceApply_destroyPartial(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + SchemaVersion: 3, + } + + r.Delete = func(d *ResourceData, m interface{}) error { + d.Set("foo", 42) + return fmt.Errorf("some error") + } + + s := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "foo": "12", + }, + } + + d := &terraform.InstanceDiff{ + Destroy: true, + } + + actual, err := r.Apply(context.Background(), s, d, nil) + if err == nil { + t.Fatal("should error") + } + + expected := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + "foo": "42", + }, + Meta: map[string]interface{}{ + "schema_version": "3", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("expected:\n%#v\n\ngot:\n%#v", expected, actual) + } +} + +func TestResourceApply_update(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Update = func(d *ResourceData, m interface{}) error { + d.Set("foo", 42) + return nil + } + + s := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "foo": "12", + }, + } + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + New: "13", + }, + }, + } + + actual, diags := r.Apply(context.Background(), s, d, nil) + if diags.HasError() { + t.Fatalf("err: %s", diagutils.ErrorDiags(diags)) + } + + expected := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "42", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceApply_updateNoCallback(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Update = nil + + s := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "foo": "12", + }, + } + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + New: "13", + }, + }, + } + + actual, err := r.Apply(context.Background(), s, d, nil) + if err == nil { + t.Fatal("should error") + } + + expected := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "foo": "12", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceApply_isNewResource(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + }, + }, + } + + updateFunc := func(d *ResourceData, m interface{}) error { + d.Set("foo", "updated") + if d.IsNewResource() { + d.Set("foo", "new-resource") + } + return nil + } + r.Create = func(d *ResourceData, m interface{}) error { + d.SetId("foo") + d.Set("foo", "created") + return updateFunc(d, m) + } + r.Update = updateFunc + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + New: "bla-blah", + }, + }, + } + + // positive test + var s *terraform.InstanceState = nil + + actual, diags := r.Apply(context.Background(), s, d, nil) + if diags.HasError() { + t.Fatalf("err: %s", diagutils.ErrorDiags(diags)) + } + + expected := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "new-resource", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("actual: %#v\nexpected: %#v", + actual, expected) + } + + // negative test + s = &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "new-resource", + }, + } + + actual, diags = r.Apply(context.Background(), s, d, nil) + if diags.HasError() { + t.Fatalf("err: %s", diagutils.ErrorDiags(diags)) + } + + expected = &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "updated", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("actual: %#v\nexpected: %#v", + actual, expected) + } +} + +func TestResourceInternalValidate(t *testing.T) { + cases := []struct { + In *Resource + Writable bool + Err bool + }{ + 0: { + nil, + true, + true, + }, + + // No optional and no required + 1: { + &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + Required: true, + }, + }, + }, + true, + true, + }, + + // Update undefined for non-ForceNew field + 2: { + &Resource{ + Create: Noop, + Schema: map[string]*Schema{ + "boo": { + Type: TypeInt, + Optional: true, + }, + }, + }, + true, + true, + }, + + // Update defined for ForceNew field + 3: { + &Resource{ + Create: Noop, + Update: Noop, + Schema: map[string]*Schema{ + "goo": { + Type: TypeInt, + Optional: true, + ForceNew: true, + }, + }, + }, + true, + true, + }, + + // non-writable doesn't need Update, Create or Delete + 4: { + &Resource{ + Schema: map[string]*Schema{ + "goo": { + Type: TypeInt, + Optional: true, + }, + }, + }, + false, + false, + }, + + // non-writable *must not* have Create + 5: { + &Resource{ + Create: Noop, + Schema: map[string]*Schema{ + "goo": { + Type: TypeInt, + Optional: true, + }, + }, + }, + false, + true, + }, + + // writable must have Read + 6: { + &Resource{ + Create: Noop, + Update: Noop, + Delete: Noop, + Schema: map[string]*Schema{ + "goo": { + Type: TypeInt, + Optional: true, + }, + }, + }, + true, + true, + }, + + // writable must have Delete + 7: { + &Resource{ + Create: Noop, + Read: Noop, + Update: Noop, + Schema: map[string]*Schema{ + "goo": { + Type: TypeInt, + Optional: true, + }, + }, + }, + true, + true, + }, + + 8: { // Reserved name at root should be disallowed + &Resource{ + Create: Noop, + Read: Noop, + Update: Noop, + Delete: Noop, + Schema: map[string]*Schema{ + "count": { + Type: TypeInt, + Optional: true, + }, + }, + }, + true, + true, + }, + + 9: { // Reserved name at nested levels should be allowed + &Resource{ + Create: Noop, + Read: Noop, + Update: Noop, + Delete: Noop, + Schema: map[string]*Schema{ + "parent_list": { + Type: TypeString, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "provisioner": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + }, + }, + true, + false, + }, + + 10: { // Provider reserved name should be allowed in resource + &Resource{ + Create: Noop, + Read: Noop, + Update: Noop, + Delete: Noop, + Schema: map[string]*Schema{ + "alias": { + Type: TypeString, + Optional: true, + }, + }, + }, + true, + false, + }, + + 11: { // ID should be allowed in data source + &Resource{ + Read: Noop, + Schema: map[string]*Schema{ + "id": { + Type: TypeString, + Optional: true, + }, + }, + }, + false, + false, + }, + + 12: { // Deprecated ID should be allowed in resource + &Resource{ + Create: Noop, + Read: Noop, + Update: Noop, + Delete: Noop, + Schema: map[string]*Schema{ + "id": { + Type: TypeString, + Computed: true, + Optional: true, + Deprecated: "Use x_id instead", + }, + }, + }, + true, + false, + }, + + 13: { // non-writable must not define CustomizeDiff + &Resource{ + Read: Noop, + Schema: map[string]*Schema{ + "goo": { + Type: TypeInt, + Optional: true, + }, + }, + CustomizeDiff: func(context.Context, *ResourceDiff, interface{}) error { return nil }, + }, + false, + true, + }, + 14: { // Deprecated resource + &Resource{ + Read: Noop, + Schema: map[string]*Schema{ + "goo": { + Type: TypeInt, + Optional: true, + }, + }, + DeprecationMessage: "This resource has been deprecated.", + }, + true, + true, + }, + 15: { // Create and CreateContext should not both be set + &Resource{ + Create: Noop, + CreateContext: NoopContext, + Read: Noop, + Update: Noop, + Delete: Noop, + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + }, + }, + }, + true, + true, + }, + 16: { // Read and ReadContext should not both be set + &Resource{ + Create: Noop, + Read: Noop, + ReadContext: NoopContext, + Update: Noop, + Delete: Noop, + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + }, + }, + }, + true, + true, + }, + 17: { // Update and UpdateContext should not both be set + &Resource{ + Create: Noop, + Read: Noop, + Update: Noop, + UpdateContext: NoopContext, + Delete: Noop, + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + }, + }, + }, + true, + true, + }, + 18: { // Delete and DeleteContext should not both be set + &Resource{ + Create: Noop, + Read: Noop, + Update: Noop, + Delete: Noop, + DeleteContext: NoopContext, + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + }, + }, + }, + true, + true, + }, + 19: { // Create and CreateWithoutTimeout should not both be set + &Resource{ + Create: Noop, + CreateWithoutTimeout: NoopContext, + Read: Noop, + Update: Noop, + Delete: Noop, + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + }, + }, + }, + true, + true, + }, + 20: { // Read and ReadWithoutTimeout should not both be set + &Resource{ + Create: Noop, + Read: Noop, + ReadWithoutTimeout: NoopContext, + Update: Noop, + Delete: Noop, + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + }, + }, + }, + true, + true, + }, + 21: { // Update and UpdateWithoutTimeout should not both be set + &Resource{ + Create: Noop, + Read: Noop, + Update: Noop, + UpdateWithoutTimeout: NoopContext, + Delete: Noop, + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + }, + }, + }, + true, + true, + }, + 22: { // Delete and DeleteWithoutTimeout should not both be set + &Resource{ + Create: Noop, + Read: Noop, + Update: Noop, + Delete: Noop, + DeleteWithoutTimeout: NoopContext, + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + }, + }, + }, + true, + true, + }, + 23: { // CreateContext and CreateWithoutTimeout should not both be set + &Resource{ + CreateContext: NoopContext, + CreateWithoutTimeout: NoopContext, + Read: Noop, + Update: Noop, + Delete: Noop, + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + }, + }, + }, + true, + true, + }, + 24: { // ReadContext and ReadWithoutTimeout should not both be set + &Resource{ + Create: Noop, + ReadContext: NoopContext, + ReadWithoutTimeout: NoopContext, + Update: Noop, + Delete: Noop, + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + }, + }, + }, + true, + true, + }, + 25: { // UpdateContext and UpdateWithoutTimeout should not both be set + &Resource{ + Create: Noop, + Read: Noop, + UpdateContext: NoopContext, + UpdateWithoutTimeout: NoopContext, + Delete: Noop, + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + }, + }, + }, + true, + true, + }, + 26: { // DeleteContext and DeleteWithoutTimeout should not both be set + &Resource{ + Create: Noop, + Read: Noop, + Update: Noop, + DeleteContext: NoopContext, + DeleteWithoutTimeout: NoopContext, + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + }, + }, + }, + true, + true, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + sm := schemaMap{} + if tc.In != nil { + sm = schemaMap(tc.In.Schema) + } + err := tc.In.InternalValidate(sm, tc.Writable) + if err != nil && !tc.Err { + t.Fatalf("%d: expected validation to pass: %s", i, err) + } + if err == nil && tc.Err { + t.Fatalf("%d: expected validation to fail", i) + } + }) + } +} + +func TestResourceRefresh(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Read = func(d *ResourceData, m interface{}) error { + if m != 42 { + return fmt.Errorf("meta not passed") + } + + return d.Set("foo", d.Get("foo").(int)+1) + } + + s := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "foo": "12", + }, + } + + expected := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + "foo": "13", + }, + Meta: map[string]interface{}{ + "schema_version": "2", + }, + } + + actual, diags := r.RefreshWithoutUpgrade(context.Background(), s, 42) + if diags.HasError() { + t.Fatalf("err: %s", diagutils.ErrorDiags(diags)) + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceRefresh_blankId(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Read = func(d *ResourceData, m interface{}) error { + d.SetId("foo") + return nil + } + + s := &terraform.InstanceState{ + ID: "", + Attributes: map[string]string{}, + } + + actual, diags := r.RefreshWithoutUpgrade(context.Background(), s, 42) + if diags.HasError() { + t.Fatalf("err: %s", diagutils.ErrorDiags(diags)) + } + if actual != nil { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceRefresh_delete(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Read = func(d *ResourceData, m interface{}) error { + d.SetId("") + return nil + } + + s := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "foo": "12", + }, + } + + actual, diags := r.RefreshWithoutUpgrade(context.Background(), s, 42) + if diags.HasError() { + t.Fatalf("err: %s", diagutils.ErrorDiags(diags)) + } + + if actual != nil { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceRefresh_existsError(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Exists = func(*ResourceData, interface{}) (bool, error) { + return false, fmt.Errorf("error") + } + + r.Read = func(d *ResourceData, m interface{}) error { + panic("shouldn't be called") + } + + s := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "foo": "12", + }, + } + + actual, err := r.RefreshWithoutUpgrade(context.Background(), s, 42) + if err == nil { + t.Fatalf("should error") + } + if !reflect.DeepEqual(actual, s) { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceRefresh_noExists(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Exists = func(*ResourceData, interface{}) (bool, error) { + return false, nil + } + + r.Read = func(d *ResourceData, m interface{}) error { + panic("shouldn't be called") + } + + s := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "foo": "12", + }, + } + + actual, diags := r.RefreshWithoutUpgrade(context.Background(), s, 42) + if diags.HasError() { + t.Fatalf("err: %s", diagutils.ErrorDiags(diags)) + } + if actual != nil { + t.Fatalf("should have no state") + } +} + +func TestResourceData(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + } + + state := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "42", + }, + } + + data := r.Data(state) + if data.Id() != "foo" { + t.Fatalf("err: %s", data.Id()) + } + if v := data.Get("foo"); v != 42 { + t.Fatalf("bad: %#v", v) + } + + // Set expectations + state.Meta = map[string]interface{}{ + "schema_version": "2", + } + + result := data.State() + if !reflect.DeepEqual(result, state) { + t.Fatalf("bad: %#v", result) + } +} + +func TestResourceData_blank(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + } + + data := r.Data(nil) + if data.Id() != "" { + t.Fatalf("err: %s", data.Id()) + } + if v := data.Get("foo"); v != 0 { + t.Fatalf("bad: %#v", v) + } +} + +func TestResourceData_timeouts(t *testing.T) { + one := 1 * time.Second + two := 2 * time.Second + three := 3 * time.Second + four := 4 * time.Second + five := 5 * time.Second + + timeouts := &ResourceTimeout{ + Create: &one, + Read: &two, + Update: &three, + Delete: &four, + Default: &five, + } + + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + Timeouts: timeouts, + } + + data := r.Data(nil) + if data.Id() != "" { + t.Fatalf("err: %s", data.Id()) + } + + if !reflect.DeepEqual(timeouts, data.timeouts) { + t.Fatalf("incorrect ResourceData timeouts: %#v\n", *data.timeouts) + } +} + +func TestResource_UpgradeState(t *testing.T) { + // While this really only calls itself and therefore doesn't test any of + // the Resource code directly, it still serves as an example of registering + // a StateUpgrader. + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "newfoo": { + Type: TypeInt, + Optional: true, + }, + }, + } + + r.StateUpgraders = []StateUpgrader{ + { + Version: 1, + Type: cty.Object(map[string]cty.Type{ + "id": cty.String, + "oldfoo": cty.Number, + }), + Upgrade: func(ctx context.Context, m map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + + oldfoo, ok := m["oldfoo"].(float64) + if !ok { + t.Fatalf("expected 1.2, got %#v", m["oldfoo"]) + } + m["newfoo"] = int(oldfoo * 10) + delete(m, "oldfoo") + + return m, nil + }, + }, + } + + oldStateAttrs := map[string]string{ + "id": "bar", + "oldfoo": "1.2", + } + + // convert the legacy flatmap state to the json equivalent + ty := r.StateUpgraders[0].Type + val, err := hcl2shim.HCL2ValueFromFlatmap(oldStateAttrs, ty) + if err != nil { + t.Fatal(err) + } + js, err := ctyjson.Marshal(val, ty) + if err != nil { + t.Fatal(err) + } + + // unmarshal the state using the json default types + var m map[string]interface{} + if err := json.Unmarshal(js, &m); err != nil { + t.Fatal(err) + } + + actual, err := r.StateUpgraders[0].Upgrade(context.Background(), m, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := map[string]interface{}{ + "id": "bar", + "newfoo": 12, + } + + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("expected: %#v\ngot: %#v\n", expected, actual) + } +} + +func TestResource_ValidateUpgradeState(t *testing.T) { + r := &Resource{ + SchemaVersion: 3, + Schema: map[string]*Schema{ + "newfoo": { + Type: TypeInt, + Optional: true, + }, + }, + } + + if err := r.InternalValidate(nil, true); err != nil { + t.Fatal(err) + } + + r.StateUpgraders = append(r.StateUpgraders, StateUpgrader{ + Version: 2, + Type: cty.Object(map[string]cty.Type{ + "id": cty.String, + }), + Upgrade: func(ctx context.Context, m map[string]interface{}, _ interface{}) (map[string]interface{}, error) { + return m, nil + }, + }) + if err := r.InternalValidate(nil, true); err != nil { + t.Fatal(err) + } + + // check for missing type + r.StateUpgraders[0].Type = cty.Type{} + if err := r.InternalValidate(nil, true); err == nil { + t.Fatal("StateUpgrader must have type") + } + r.StateUpgraders[0].Type = cty.Object(map[string]cty.Type{ + "id": cty.String, + }) + + // check for missing Upgrade func + r.StateUpgraders[0].Upgrade = nil + if err := r.InternalValidate(nil, true); err == nil { + t.Fatal("StateUpgrader must have an Upgrade func") + } + r.StateUpgraders[0].Upgrade = func(ctx context.Context, m map[string]interface{}, _ interface{}) (map[string]interface{}, error) { + return m, nil + } + + // check for skipped version + r.StateUpgraders[0].Version = 0 + r.StateUpgraders = append(r.StateUpgraders, StateUpgrader{ + Version: 2, + Type: cty.Object(map[string]cty.Type{ + "id": cty.String, + }), + Upgrade: func(ctx context.Context, m map[string]interface{}, _ interface{}) (map[string]interface{}, error) { + return m, nil + }, + }) + if err := r.InternalValidate(nil, true); err == nil { + t.Fatal("StateUpgraders cannot skip versions") + } + + // add the missing version, but fail because it's still out of order + r.StateUpgraders = append(r.StateUpgraders, StateUpgrader{ + Version: 1, + Type: cty.Object(map[string]cty.Type{ + "id": cty.String, + }), + Upgrade: func(ctx context.Context, m map[string]interface{}, _ interface{}) (map[string]interface{}, error) { + return m, nil + }, + }) + if err := r.InternalValidate(nil, true); err == nil { + t.Fatal("upgraders must be defined in order") + } + + r.StateUpgraders[1], r.StateUpgraders[2] = r.StateUpgraders[2], r.StateUpgraders[1] + if err := r.InternalValidate(nil, true); err != nil { + t.Fatal(err) + } + + // can't add an upgrader for a schema >= the current version + r.StateUpgraders = append(r.StateUpgraders, StateUpgrader{ + Version: 3, + Type: cty.Object(map[string]cty.Type{ + "id": cty.String, + }), + Upgrade: func(ctx context.Context, m map[string]interface{}, _ interface{}) (map[string]interface{}, error) { + return m, nil + }, + }) + if err := r.InternalValidate(nil, true); err == nil { + t.Fatal("StateUpgraders cannot have a version >= current SchemaVersion") + } +} + +func TestResource_ContextTimeout(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + Timeouts: &ResourceTimeout{ + Create: DefaultTimeout(40 * time.Minute), + }, + } + + var deadlineSet bool + r.CreateContext = func(ctx context.Context, d *ResourceData, m interface{}) diag.Diagnostics { + d.SetId("foo") + _, deadlineSet = ctx.Deadline() + return nil + } + + var s *terraform.InstanceState = nil + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + New: "42", + }, + }, + } + + if _, diags := r.Apply(context.Background(), s, d, nil); diags.HasError() { + t.Fatalf("err: %s", diagutils.ErrorDiags(diags)) + } + + if !deadlineSet { + t.Fatal("context does not have timeout") + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_timeout.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_timeout.go index f12bf725..cee0b678 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_timeout.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_timeout.go @@ -5,9 +5,10 @@ import ( "log" "time" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim" - "github.com/hashicorp/terraform-plugin-sdk/terraform" "github.com/mitchellh/copystructure" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/hcl2shim" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) const TimeoutKey = "e2bfb730-ecaa-11e6-8f88-34363bc7c4c0" diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_timeout_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_timeout_test.go new file mode 100644 index 00000000..b0ceb9ef --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/resource_timeout_test.go @@ -0,0 +1,376 @@ +package schema + +import ( + "fmt" + "reflect" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestResourceTimeout_ConfigDecode_badkey(t *testing.T) { + cases := []struct { + Name string + // what the resource has defined in source + ResourceDefaultTimeout *ResourceTimeout + // configuration provider by user in tf file + Config map[string]interface{} + // what we expect the parsed ResourceTimeout to be + Expected *ResourceTimeout + // Should we have an error (key not defined in source) + ShouldErr bool + }{ + { + Name: "Source does not define 'delete' key", + ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 0), + Config: expectedConfigForValues(2, 0, 0, 1, 0), + Expected: timeoutForValues(10, 0, 5, 0, 0), + ShouldErr: true, + }, + { + Name: "Config overrides create", + ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 0), + Config: expectedConfigForValues(2, 0, 7, 0, 0), + Expected: timeoutForValues(2, 0, 7, 0, 0), + ShouldErr: false, + }, + { + Name: "Config overrides create, default provided. Should still have zero values", + ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 3), + Config: expectedConfigForValues(2, 0, 7, 0, 0), + Expected: timeoutForValues(2, 0, 7, 0, 3), + ShouldErr: false, + }, + { + Name: "Use something besides 'minutes'", + ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 3), + Config: map[string]interface{}{ + "create": "2h", + }, + Expected: timeoutForValues(120, 0, 5, 0, 3), + ShouldErr: false, + }, + } + + for i, c := range cases { + t.Run(fmt.Sprintf("%d-%s", i, c.Name), func(t *testing.T) { + r := &Resource{ + Timeouts: c.ResourceDefaultTimeout, + } + + conf := terraform.NewResourceConfigRaw( + map[string]interface{}{ + "foo": "bar", + TimeoutsConfigKey: c.Config, + }, + ) + + timeout := &ResourceTimeout{} + decodeErr := timeout.ConfigDecode(r, conf) + if c.ShouldErr { + if decodeErr == nil { + t.Fatalf("ConfigDecode case (%d): Expected bad timeout key: %s", i, decodeErr) + } + // should error, err was not nil, continue + return + } else { + if decodeErr != nil { + // should not error, error was not nil, fatal + t.Fatalf("decodeError was not nil: %s", decodeErr) + } + } + + if !reflect.DeepEqual(c.Expected, timeout) { + t.Fatalf("ConfigDecode match error case (%d).\nExpected:\n%#v\nGot:\n%#v", i, c.Expected, timeout) + } + }) + } +} + +func TestResourceTimeout_ConfigDecode(t *testing.T) { + r := &Resource{ + Timeouts: &ResourceTimeout{ + Create: DefaultTimeout(10 * time.Minute), + Update: DefaultTimeout(5 * time.Minute), + }, + } + + c := terraform.NewResourceConfigRaw( + map[string]interface{}{ + "foo": "bar", + TimeoutsConfigKey: map[string]interface{}{ + "create": "2m", + "update": "1m", + }, + }, + ) + + timeout := &ResourceTimeout{} + err := timeout.ConfigDecode(r, c) + if err != nil { + t.Fatalf("Expected good timeout returned:, %s", err) + } + + expected := &ResourceTimeout{ + Create: DefaultTimeout(2 * time.Minute), + Update: DefaultTimeout(1 * time.Minute), + } + + if !reflect.DeepEqual(timeout, expected) { + t.Fatalf("bad timeout decode.\nExpected:\n%#v\nGot:\n%#v\n", expected, timeout) + } +} + +func TestResourceTimeout_legacyConfigDecode(t *testing.T) { + r := &Resource{ + Timeouts: &ResourceTimeout{ + Create: DefaultTimeout(10 * time.Minute), + Update: DefaultTimeout(5 * time.Minute), + }, + } + + c := terraform.NewResourceConfigRaw( + map[string]interface{}{ + "foo": "bar", + TimeoutsConfigKey: []interface{}{ + map[string]interface{}{ + "create": "2m", + "update": "1m", + }, + }, + }, + ) + + timeout := &ResourceTimeout{} + err := timeout.ConfigDecode(r, c) + if err != nil { + t.Fatalf("Expected good timeout returned:, %s", err) + } + + expected := &ResourceTimeout{ + Create: DefaultTimeout(2 * time.Minute), + Update: DefaultTimeout(1 * time.Minute), + } + + if !reflect.DeepEqual(timeout, expected) { + t.Fatalf("bad timeout decode.\nExpected:\n%#v\nGot:\n%#v\n", expected, timeout) + } +} + +func TestResourceTimeout_DiffEncode_basic(t *testing.T) { + cases := []struct { + Timeout *ResourceTimeout + Expected map[string]interface{} + // Not immediately clear when an error would hit + ShouldErr bool + }{ + // Two fields + { + Timeout: timeoutForValues(10, 0, 5, 0, 0), + Expected: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 5, 0, 0)}, + ShouldErr: false, + }, + // Two fields, one is Default + { + Timeout: timeoutForValues(10, 0, 0, 0, 7), + Expected: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 0, 0, 7)}, + ShouldErr: false, + }, + // All fields + { + Timeout: timeoutForValues(10, 3, 4, 1, 7), + Expected: map[string]interface{}{TimeoutKey: expectedForValues(10, 3, 4, 1, 7)}, + ShouldErr: false, + }, + // No fields + { + Timeout: &ResourceTimeout{}, + Expected: nil, + ShouldErr: false, + }, + } + + for _, c := range cases { + state := &terraform.InstanceDiff{} + err := c.Timeout.DiffEncode(state) + if err != nil && !c.ShouldErr { + t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, state.Meta) + } + + // should maybe just compare [TimeoutKey] but for now we're assuming only + // that in Meta + if !reflect.DeepEqual(state.Meta, c.Expected) { + t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, state.Meta) + } + } + // same test cases but for InstanceState + for _, c := range cases { + state := &terraform.InstanceState{} + err := c.Timeout.StateEncode(state) + if err != nil && !c.ShouldErr { + t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, state.Meta) + } + + // should maybe just compare [TimeoutKey] but for now we're assuming only + // that in Meta + if !reflect.DeepEqual(state.Meta, c.Expected) { + t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, state.Meta) + } + } +} + +func TestResourceTimeout_MetaDecode_basic(t *testing.T) { + cases := []struct { + State *terraform.InstanceDiff + Expected *ResourceTimeout + // Not immediately clear when an error would hit + ShouldErr bool + }{ + // Two fields + { + State: &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 5, 0, 0)}}, + Expected: timeoutForValues(10, 0, 5, 0, 0), + ShouldErr: false, + }, + // Two fields, one is Default + { + State: &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 0, 0, 7)}}, + Expected: timeoutForValues(10, 7, 7, 7, 7), + ShouldErr: false, + }, + // All fields + { + State: &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 3, 4, 1, 7)}}, + Expected: timeoutForValues(10, 3, 4, 1, 7), + ShouldErr: false, + }, + // No fields + { + State: &terraform.InstanceDiff{}, + Expected: &ResourceTimeout{}, + ShouldErr: false, + }, + } + + for _, c := range cases { + rt := &ResourceTimeout{} + err := rt.DiffDecode(c.State) + if err != nil && !c.ShouldErr { + t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, rt) + } + + // should maybe just compare [TimeoutKey] but for now we're assuming only + // that in Meta + if !reflect.DeepEqual(rt, c.Expected) { + t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, rt) + } + } +} + +func timeoutForValues(create, read, update, del, def int) *ResourceTimeout { + rt := ResourceTimeout{} + + if create != 0 { + rt.Create = DefaultTimeout(time.Duration(create) * time.Minute) + } + if read != 0 { + rt.Read = DefaultTimeout(time.Duration(read) * time.Minute) + } + if update != 0 { + rt.Update = DefaultTimeout(time.Duration(update) * time.Minute) + } + if del != 0 { + rt.Delete = DefaultTimeout(time.Duration(del) * time.Minute) + } + + if def != 0 { + rt.Default = DefaultTimeout(time.Duration(def) * time.Minute) + } + + return &rt +} + +// Generates a ResourceTimeout struct that should reflect the +// d.Timeout("key") results +func expectedTimeoutForValues(create, read, update, del, def int) *ResourceTimeout { + rt := ResourceTimeout{} + + defaultValues := []*int{&create, &read, &update, &del, &def} + for _, v := range defaultValues { + if *v == 0 { + *v = 20 + } + } + + if create != 0 { + rt.Create = DefaultTimeout(time.Duration(create) * time.Minute) + } + if read != 0 { + rt.Read = DefaultTimeout(time.Duration(read) * time.Minute) + } + if update != 0 { + rt.Update = DefaultTimeout(time.Duration(update) * time.Minute) + } + if del != 0 { + rt.Delete = DefaultTimeout(time.Duration(del) * time.Minute) + } + + if def != 0 { + rt.Default = DefaultTimeout(time.Duration(def) * time.Minute) + } + + return &rt +} + +func expectedForValues(create, read, update, del, def int) map[string]interface{} { + ex := make(map[string]interface{}) + + if create != 0 { + ex["create"] = DefaultTimeout(time.Duration(create) * time.Minute).Nanoseconds() + } + if read != 0 { + ex["read"] = DefaultTimeout(time.Duration(read) * time.Minute).Nanoseconds() + } + if update != 0 { + ex["update"] = DefaultTimeout(time.Duration(update) * time.Minute).Nanoseconds() + } + if del != 0 { + ex["delete"] = DefaultTimeout(time.Duration(del) * time.Minute).Nanoseconds() + } + + if def != 0 { + defNano := DefaultTimeout(time.Duration(def) * time.Minute).Nanoseconds() + ex["default"] = defNano + + for _, k := range timeoutKeys() { + if _, ok := ex[k]; !ok { + ex[k] = defNano + } + } + } + + return ex +} + +func expectedConfigForValues(create, read, update, delete, def int) map[string]interface{} { + ex := make(map[string]interface{}, 0) + + if create != 0 { + ex["create"] = fmt.Sprintf("%dm", create) + } + if read != 0 { + ex["read"] = fmt.Sprintf("%dm", read) + } + if update != 0 { + ex["update"] = fmt.Sprintf("%dm", update) + } + if delete != 0 { + ex["delete"] = fmt.Sprintf("%dm", delete) + } + + if def != 0 { + ex["default"] = fmt.Sprintf("%dm", def) + } + return ex +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/schema.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/schema.go index 0cd64635..10a9743c 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/schema.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/schema.go @@ -14,47 +14,23 @@ package schema import ( "context" "fmt" + "log" "os" "reflect" "regexp" "sort" "strconv" "strings" - "sync" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim" - "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/hashicorp/go-cty/cty" "github.com/mitchellh/copystructure" "github.com/mitchellh/mapstructure" -) - -// Name of ENV variable which (if not empty) prefers panic over error -const PanicOnErr = "TF_SCHEMA_PANIC_ON_ERROR" - -// type used for schema package context keys -type contextKey string -var ( - protoVersionMu sync.Mutex - protoVersion5 = false + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/hcl2shim" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) -func isProto5() bool { - protoVersionMu.Lock() - defer protoVersionMu.Unlock() - return protoVersion5 - -} - -// SetProto5 enables a feature flag for any internal changes required required -// to work with the new plugin protocol. This should not be called by -// provider. -func SetProto5() { - protoVersionMu.Lock() - defer protoVersionMu.Unlock() - protoVersion5 = true -} - // Schema is used to describe the structure of a value. // // Read the documentation of the struct elements for important details. @@ -138,9 +114,9 @@ type Schema struct { Default interface{} DefaultFunc SchemaDefaultFunc - // Description is used as the description for docs or asking for user - // input. It should be relatively short (a few sentences max) and should - // be formatted to fit a CLI. + // Description is used as the description for docs, the language server and + // other user facing usage. It can be plain-text or markdown depending on the + // global DescriptionKind setting. Description string // InputDefault is the default value to use for when inputs are requested. @@ -191,14 +167,6 @@ type Schema struct { MaxItems int MinItems int - // PromoteSingle originally allowed for a single element to be assigned - // where a primitive list was expected, but this no longer works from - // Terraform v0.12 onwards (Terraform Core will require a list to be set - // regardless of what this is set to) and so only applies to Terraform v0.11 - // and earlier, and so should be used only to retain this functionality - // for those still using v0.11 with a provider that formerly used this. - PromoteSingle bool - // The following fields are only valid for a TypeSet type. // // Set defines a function to determine the unique ID of an item so that @@ -223,9 +191,12 @@ type Schema struct { // // AtLeastOneOf is a set of schema keys that, when set, at least one of // the keys in that list must be specified. + // + // RequiredWith is a set of schema keys that must be set simultaneously. ConflictsWith []string ExactlyOneOf []string AtLeastOneOf []string + RequiredWith []string // When Deprecated is set, this attribute is deprecated. // @@ -234,14 +205,6 @@ type Schema struct { // how to address the deprecation. Deprecated string - // When Removed is set, this attribute has been removed from the schema - // - // Removed attributes can be left in the Schema to generate informative error - // messages for the user when they show up in resource configurations. - // This string is the message shown to the user with instructions on - // what do to about the removed attribute. - Removed string - // ValidateFunc allows individual fields to define arbitrary validation // logic. It is yielded the provided config value as an interface{} that is // guaranteed to be of the proper Schema type, and it can yield warnings or @@ -251,6 +214,29 @@ type Schema struct { // TypeFloat, TypeString, TypeBool, or TypeMap. It is ignored for all other types. ValidateFunc SchemaValidateFunc + // ValidateDiagFunc allows individual fields to define arbitrary validation + // logic. It is yielded the provided config value as an interface{} that is + // guaranteed to be of the proper Schema type, and it can yield diagnostics + // based on inspection of that value. + // + // ValidateDiagFunc is honored only when the schema's Type is set to TypeInt, + // TypeFloat, TypeString, TypeBool, or TypeMap. It is ignored for all other types. + // + // ValidateDiagFunc is also yielded the cty.Path the SDK has built up to this + // attribute. The SDK will automatically set the AttributePath of any returned + // Diagnostics to this path. Therefore the developer does not need to set + // the AttributePath for primitive types. + // + // In the case of TypeMap to provide the most precise information, please + // set an AttributePath with the additional cty.IndexStep: + // + // AttributePath: cty.IndexStringPath("key_name") + // + // Or alternatively use the passed in path to create the absolute path: + // + // AttributePath: append(path, cty.IndexStep{Key: cty.StringVal("key_name")}) + ValidateDiagFunc SchemaValidateDiagFunc + // Sensitive ensures that the attribute's value does not get displayed in // logs or regular output. It should be used for passwords or other // secret fields. Future versions of Terraform may encrypt these @@ -318,8 +304,14 @@ type SchemaStateFunc func(interface{}) string // SchemaValidateFunc is a function used to validate a single field in the // schema. +// +// Deprecated: please use SchemaValidateDiagFunc type SchemaValidateFunc func(interface{}, string) ([]string, []error) +// SchemaValidateDiagFunc is a function used to validate a single field in the +// schema and has Diagnostic support. +type SchemaValidateDiagFunc func(interface{}, cty.Path) diag.Diagnostics + func (s *Schema) GoString() string { return fmt.Sprintf("*%#v", *s) } @@ -416,6 +408,7 @@ func (s *Schema) finalizeDiff(d *terraform.ResourceAttrDiff, customized bool) *t if d.Old != "" && d.New == "" { // This is a computed value with an old value set already, // just let it go. + log.Println("[DEBUG] A computed value with the empty string as the new value and a non-empty old value was found. Interpreting the empty string as \"unset\" to align with legacy behavior.") return nil } } @@ -434,6 +427,37 @@ func (s *Schema) finalizeDiff(d *terraform.ResourceAttrDiff, customized bool) *t return d } +func (s *Schema) validateFunc(decoded interface{}, k string, path cty.Path) diag.Diagnostics { + var diags diag.Diagnostics + + if s.ValidateDiagFunc != nil { + diags = s.ValidateDiagFunc(decoded, path) + for i := range diags { + if !diags[i].AttributePath.HasPrefix(path) { + diags[i].AttributePath = append(path, diags[i].AttributePath...) + } + } + } else if s.ValidateFunc != nil { + ws, es := s.ValidateFunc(decoded, k) + for _, w := range ws { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: w, + AttributePath: path, + }) + } + for _, e := range es { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: e.Error(), + AttributePath: path, + }) + } + } + + return diags +} + // InternalMap is used to aid in the transition to the new schema types and // protocol. The name is not meant to convey any usefulness, as this is not to // be used directly by any providers. @@ -443,10 +467,7 @@ type InternalMap = schemaMap type schemaMap map[string]*Schema func (m schemaMap) panicOnError() bool { - if os.Getenv(PanicOnErr) != "" { - return true - } - return false + return os.Getenv("TF_ACC") != "" } // Data returns a ResourceData for the given schema, state, and diff. @@ -476,6 +497,7 @@ func (m *schemaMap) DeepCopy() schemaMap { // Diff returns the diff for a resource given the schema map, // state, and configuration. func (m schemaMap) Diff( + ctx context.Context, s *terraform.InstanceState, c *terraform.ResourceConfig, customizeDiff CustomizeDiffFunc, @@ -515,7 +537,7 @@ func (m schemaMap) Diff( if !result.DestroyTainted && customizeDiff != nil { mc := m.DeepCopy() rd := newResourceDiff(mc, c, s, result) - if err := customizeDiff(rd, meta); err != nil { + if err := customizeDiff(ctx, rd, meta); err != nil { return nil, err } for _, k := range rd.UpdatedKeys() { @@ -556,7 +578,7 @@ func (m schemaMap) Diff( if !result2.DestroyTainted && customizeDiff != nil { mc := m.DeepCopy() rd := newResourceDiff(mc, c, d.state, result2) - if err := customizeDiff(rd, meta); err != nil { + if err := customizeDiff(ctx, rd, meta); err != nil { return nil, err } for _, k := range rd.UpdatedKeys() { @@ -619,71 +641,9 @@ func (m schemaMap) Diff( return result, nil } -// Input implements the terraform.ResourceProvider method by asking -// for input for required configuration keys that don't have a value. -func (m schemaMap) Input( - input terraform.UIInput, - c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) { - keys := make([]string, 0, len(m)) - for k, _ := range m { - keys = append(keys, k) - } - sort.Strings(keys) - - for _, k := range keys { - v := m[k] - - // Skip things that don't require config, if that is even valid - // for a provider schema. - // Required XOR Optional must always be true to validate, so we only - // need to check one. - if v.Optional { - continue - } - - // Deprecated fields should never prompt - if v.Deprecated != "" { - continue - } - - // Skip things that have a value of some sort already - if _, ok := c.Raw[k]; ok { - continue - } - - // Skip if it has a default value - defaultValue, err := v.DefaultValue() - if err != nil { - return nil, fmt.Errorf("%s: error loading default: %s", k, err) - } - if defaultValue != nil { - continue - } - - var value interface{} - switch v.Type { - case TypeBool, TypeInt, TypeFloat, TypeSet, TypeList: - continue - case TypeString: - value, err = m.inputString(input, k, v) - default: - panic(fmt.Sprintf("Unknown type for input: %#v", v.Type)) - } - - if err != nil { - return nil, fmt.Errorf( - "%s: %s", k, err) - } - - c.Config[k] = value - } - - return c, nil -} - // Validate validates the configuration against this schema mapping. -func (m schemaMap) Validate(c *terraform.ResourceConfig) ([]string, []error) { - return m.validateObject("", m, c) +func (m schemaMap) Validate(c *terraform.ResourceConfig) diag.Diagnostics { + return m.validateObject("", m, c, cty.Path{}) } // InternalValidate validates the format of this schema. This should be called @@ -767,21 +727,28 @@ func (m schemaMap) internalValidate(topSchemaMap schemaMap, attrsOnly bool) erro } if len(v.ConflictsWith) > 0 { - err := checkKeysAgainstSchemaFlags(k, v.ConflictsWith, topSchemaMap) + err := checkKeysAgainstSchemaFlags(k, v.ConflictsWith, topSchemaMap, v, false) if err != nil { return fmt.Errorf("ConflictsWith: %+v", err) } } + if len(v.RequiredWith) > 0 { + err := checkKeysAgainstSchemaFlags(k, v.RequiredWith, topSchemaMap, v, true) + if err != nil { + return fmt.Errorf("RequiredWith: %+v", err) + } + } + if len(v.ExactlyOneOf) > 0 { - err := checkKeysAgainstSchemaFlags(k, v.ExactlyOneOf, topSchemaMap) + err := checkKeysAgainstSchemaFlags(k, v.ExactlyOneOf, topSchemaMap, v, true) if err != nil { return fmt.Errorf("ExactlyOneOf: %+v", err) } } if len(v.AtLeastOneOf) > 0 { - err := checkKeysAgainstSchemaFlags(k, v.AtLeastOneOf, topSchemaMap) + err := checkKeysAgainstSchemaFlags(k, v.AtLeastOneOf, topSchemaMap, v, true) if err != nil { return fmt.Errorf("AtLeastOneOf: %+v", err) } @@ -820,27 +787,78 @@ func (m schemaMap) internalValidate(topSchemaMap schemaMap, attrsOnly bool) erro } } - // Computed-only field - if v.Computed && !v.Optional { - if v.ValidateFunc != nil { - return fmt.Errorf("%s: ValidateFunc is for validating user input, "+ - "there's nothing to validate on computed-only field", k) + if v.Type == TypeMap && v.Elem != nil { + switch v.Elem.(type) { + case *Resource: + return fmt.Errorf("%s: TypeMap with Elem *Resource not supported,"+ + "use TypeList/TypeSet with Elem *Resource or TypeMap with Elem *Schema", k) + } + } + + if computedOnly { + if len(v.AtLeastOneOf) > 0 { + return fmt.Errorf("%s: AtLeastOneOf is for configurable attributes,"+ + "there's nothing to configure on computed-only field", k) + } + if len(v.ConflictsWith) > 0 { + return fmt.Errorf("%s: ConflictsWith is for configurable attributes,"+ + "there's nothing to configure on computed-only field", k) + } + if v.Default != nil { + return fmt.Errorf("%s: Default is for configurable attributes,"+ + "there's nothing to configure on computed-only field", k) + } + if v.DefaultFunc != nil { + return fmt.Errorf("%s: DefaultFunc is for configurable attributes,"+ + "there's nothing to configure on computed-only field", k) } if v.DiffSuppressFunc != nil { return fmt.Errorf("%s: DiffSuppressFunc is for suppressing differences"+ " between config and state representation. "+ "There is no config for computed-only field, nothing to compare.", k) } + if len(v.ExactlyOneOf) > 0 { + return fmt.Errorf("%s: ExactlyOneOf is for configurable attributes,"+ + "there's nothing to configure on computed-only field", k) + } + if v.InputDefault != "" { + return fmt.Errorf("%s: InputDefault is for configurable attributes,"+ + "there's nothing to configure on computed-only field", k) + } + if v.MaxItems > 0 { + return fmt.Errorf("%s: MaxItems is for configurable attributes,"+ + "there's nothing to configure on computed-only field", k) + } + if v.MinItems > 0 { + return fmt.Errorf("%s: MinItems is for configurable attributes,"+ + "there's nothing to configure on computed-only field", k) + } + if v.StateFunc != nil { + return fmt.Errorf("%s: StateFunc is extraneous, "+ + "value should just be changed before setting on computed-only field", k) + } + if v.ValidateFunc != nil { + return fmt.Errorf("%s: ValidateFunc is for validating user input, "+ + "there's nothing to validate on computed-only field", k) + } + if v.ValidateDiagFunc != nil { + return fmt.Errorf("%s: ValidateDiagFunc is for validating user input, "+ + "there's nothing to validate on computed-only field", k) + } } - if v.ValidateFunc != nil { + if v.ValidateFunc != nil || v.ValidateDiagFunc != nil { switch v.Type { case TypeList, TypeSet: - return fmt.Errorf("%s: ValidateFunc is not yet supported on lists or sets.", k) + return fmt.Errorf("%s: ValidateFunc and ValidateDiagFunc are not yet supported on lists or sets.", k) } } - if v.Deprecated == "" && v.Removed == "" { + if v.ValidateFunc != nil && v.ValidateDiagFunc != nil { + return fmt.Errorf("%s: ValidateFunc and ValidateDiagFunc cannot both be set", k) + } + + if v.Deprecated == "" { if !isValidFieldName(k) { return fmt.Errorf("%s: Field name may only contain lowercase alphanumeric characters & underscores.", k) } @@ -850,14 +868,20 @@ func (m schemaMap) internalValidate(topSchemaMap schemaMap, attrsOnly bool) erro return nil } -func checkKeysAgainstSchemaFlags(k string, keys []string, topSchemaMap schemaMap) error { +func checkKeysAgainstSchemaFlags(k string, keys []string, topSchemaMap schemaMap, self *Schema, allowSelfReference bool) error { for _, key := range keys { parts := strings.Split(key, ".") sm := topSchemaMap var target *Schema - for _, part := range parts { - // Skip index fields - if _, err := strconv.Atoi(part); err == nil { + for idx, part := range parts { + // Skip index fields if 0 + partInt, err := strconv.Atoi(part) + + if err == nil { + if partInt != 0 { + return fmt.Errorf("%s configuration block reference (%s) can only use the .0. index for TypeList and MaxItems: 1 configuration blocks", k, key) + } + continue } @@ -866,13 +890,28 @@ func checkKeysAgainstSchemaFlags(k string, keys []string, topSchemaMap schemaMap return fmt.Errorf("%s references unknown attribute (%s) at part (%s)", k, key, part) } - if subResource, ok := target.Elem.(*Resource); ok { - sm = schemaMap(subResource.Schema) + subResource, ok := target.Elem.(*Resource) + + if !ok { + continue + } + + // Skip Type/MaxItems check if not the last element + if (target.Type == TypeSet || target.MaxItems != 1) && idx+1 != len(parts) { + return fmt.Errorf("%s configuration block reference (%s) can only be used with TypeList and MaxItems: 1 configuration blocks", k, key) } + + sm = schemaMap(subResource.Schema) } + if target == nil { return fmt.Errorf("%s cannot find target attribute (%s), sm: %#v", k, key, sm) } + + if target == self && !allowSelfReference { + return fmt.Errorf("%s cannot reference self (%s)", k, key) + } + if target.Required { return fmt.Errorf("%s cannot contain Required attribute (%s)", k, key) } @@ -881,6 +920,7 @@ func checkKeysAgainstSchemaFlags(k string, keys []string, topSchemaMap schemaMap return fmt.Errorf("%s cannot contain Computed(When) attribute (%s)", k, key) } } + return nil } @@ -1022,13 +1062,18 @@ func (m schemaMap) diffList( oldStr = "" } - diff.Attributes[k+".#"] = countSchema.finalizeDiff( + finalizedAttr := countSchema.finalizeDiff( &terraform.ResourceAttrDiff{ Old: oldStr, New: newStr, }, customized, ) + if finalizedAttr != nil { + diff.Attributes[k+".#"] = finalizedAttr + } else { + delete(diff.Attributes, k+".#") + } } // Figure out the maximum @@ -1128,13 +1173,18 @@ func (m schemaMap) diffMap( oldStr = "" } - diff.Attributes[k+".%"] = countSchema.finalizeDiff( + finalizedAttr := countSchema.finalizeDiff( &terraform.ResourceAttrDiff{ Old: oldStr, New: newStr, }, customized, ) + if finalizedAttr != nil { + diff.Attributes[k+".%"] = finalizedAttr + } else { + delete(diff.Attributes, k+".%") + } } // If the new map is nil and we're computed, then ignore it. @@ -1151,22 +1201,28 @@ func (m schemaMap) diffMap( continue } - diff.Attributes[prefix+k] = schema.finalizeDiff( + finalizedAttr := schema.finalizeDiff( &terraform.ResourceAttrDiff{ Old: old, New: v, }, customized, ) + if finalizedAttr != nil { + diff.Attributes[prefix+k] = finalizedAttr + } } for k, v := range stateMap { - diff.Attributes[prefix+k] = schema.finalizeDiff( + finalizedAttr := schema.finalizeDiff( &terraform.ResourceAttrDiff{ Old: v, NewRemoved: true, }, customized, ) + if finalizedAttr != nil { + diff.Attributes[prefix+k] = finalizedAttr + } } return nil @@ -1238,26 +1294,32 @@ func (m schemaMap) diffSet( countStr = "" } - diff.Attributes[k+".#"] = countSchema.finalizeDiff( + finalizedAttr := countSchema.finalizeDiff( &terraform.ResourceAttrDiff{ Old: countStr, NewComputed: true, }, customized, ) + if finalizedAttr != nil { + diff.Attributes[k+".#"] = finalizedAttr + } return nil } // If the counts are not the same, then record that diff changed := oldLen != newLen if changed || all { - diff.Attributes[k+".#"] = countSchema.finalizeDiff( + finalizedAttr := countSchema.finalizeDiff( &terraform.ResourceAttrDiff{ Old: oldStr, New: newStr, }, customized, ) + if finalizedAttr != nil { + diff.Attributes[k+".#"] = finalizedAttr + } } // Build the list of codes that will make up our set. This is the @@ -1344,7 +1406,7 @@ func (m schemaMap) diffString( return nil } - diff.Attributes[k] = schema.finalizeDiff( + finalizedAttr := schema.finalizeDiff( &terraform.ResourceAttrDiff{ Old: os, New: ns, @@ -1354,36 +1416,33 @@ func (m schemaMap) diffString( }, customized, ) + if finalizedAttr != nil { + diff.Attributes[k] = finalizedAttr + } return nil } -func (m schemaMap) inputString( - input terraform.UIInput, - k string, - schema *Schema) (interface{}, error) { - result, err := input.Input(context.Background(), &terraform.InputOpts{ - Id: k, - Query: k, - Description: schema.Description, - Default: schema.InputDefault, - }) - - return result, err -} - func (m schemaMap) validate( k string, schema *Schema, - c *terraform.ResourceConfig) ([]string, []error) { + c *terraform.ResourceConfig, + path cty.Path) diag.Diagnostics { + + var diags diag.Diagnostics + raw, ok := c.Get(k) if !ok && schema.DefaultFunc != nil { // We have a dynamic default. Check if we have a value. var err error raw, err = schema.DefaultFunc() if err != nil { - return nil, []error{fmt.Errorf( - "%q, error loading default: %s", k, err)} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Failed to determine default value", + Detail: err.Error(), + AttributePath: path, + }) } // We're okay as long as we had a value set @@ -1392,26 +1451,54 @@ func (m schemaMap) validate( err := validateExactlyOneAttribute(k, schema, c) if err != nil { - return nil, []error{err} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Invalid combination of arguments", + Detail: err.Error(), + AttributePath: path, + }) } err = validateAtLeastOneAttribute(k, schema, c) if err != nil { - return nil, []error{err} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Missing required argument", + Detail: err.Error(), + AttributePath: path, + }) } if !ok { if schema.Required { - return nil, []error{fmt.Errorf( - "%q: required field is not set", k)} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Missing required argument", + Detail: fmt.Sprintf("The argument %q is required, but no definition was found.", k), + AttributePath: path, + }) } - return nil, nil + return diags } if !schema.Required && !schema.Optional { // This is a computed-only field - return nil, []error{fmt.Errorf( - "%q: this field cannot be set", k)} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Value for unconfigurable attribute", + Detail: fmt.Sprintf("Can't configure a value for %q: its value will be decided automatically based on the result of applying this configuration.", k), + AttributePath: path, + }) + } + + err = validateRequiredWithAttribute(k, schema, c) + if err != nil { + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Missing required argument", + Detail: err.Error(), + AttributePath: path, + }) } // If the value is unknown then we can't validate it yet. @@ -1421,17 +1508,27 @@ func (m schemaMap) validate( // Required fields set via an interpolated value are accepted. if !isWhollyKnown(raw) { if schema.Deprecated != "" { - return []string{fmt.Sprintf("%q: [DEPRECATED] %s", k, schema.Deprecated)}, nil + return append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Argument is deprecated", + Detail: schema.Deprecated, + AttributePath: path, + }) } - return nil, nil + return diags } err = validateConflictingAttributes(k, schema, c) if err != nil { - return nil, []error{err} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Conflicting configuration arguments", + Detail: err.Error(), + AttributePath: path, + }) } - return m.validateType(k, raw, schema, c) + return m.validateType(k, raw, schema, c, path) } // isWhollyKnown returns false if the argument contains an UnknownVariableValue @@ -1494,6 +1591,27 @@ func removeDuplicates(elements []string) []string { return result } +func validateRequiredWithAttribute( + k string, + schema *Schema, + c *terraform.ResourceConfig) error { + + if len(schema.RequiredWith) == 0 { + return nil + } + + allKeys := removeDuplicates(append(schema.RequiredWith, k)) + sort.Strings(allKeys) + + for _, key := range allKeys { + if _, ok := c.Get(key); !ok { + return fmt.Errorf("%q: all of `%s` must be specified", k, strings.Join(allKeys, ",")) + } + } + + return nil +} + func validateExactlyOneAttribute( k string, schema *Schema, @@ -1557,33 +1675,33 @@ func (m schemaMap) validateList( k string, raw interface{}, schema *Schema, - c *terraform.ResourceConfig) ([]string, []error) { + c *terraform.ResourceConfig, + path cty.Path) diag.Diagnostics { + + var diags diag.Diagnostics + // first check if the list is wholly unknown if s, ok := raw.(string); ok { if s == hcl2shim.UnknownVariableValue { - return nil, nil + return diags } } // schemaMap can't validate nil if raw == nil { - return nil, nil + return diags } // We use reflection to verify the slice because you can't // case to []interface{} unless the slice is exactly that type. rawV := reflect.ValueOf(raw) - // If we support promotion and the raw value isn't a slice, wrap - // it in []interface{} and check again. - if schema.PromoteSingle && rawV.Kind() != reflect.Slice { - raw = []interface{}{raw} - rawV = reflect.ValueOf(raw) - } - if rawV.Kind() != reflect.Slice { - return nil, []error{fmt.Errorf( - "%s: should be a list", k)} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Attribute must be a list", + AttributePath: path, + }) } // We can't validate list length if this came from a dynamic block. @@ -1591,29 +1709,35 @@ func (m schemaMap) validateList( // at this point, we're going to skip validation in the new protocol if // there are any unknowns. Validate will eventually be called again once // all values are known. - if isProto5() && !isWhollyKnown(raw) { - return nil, nil + if !isWhollyKnown(raw) { + return diags } // Validate length if schema.MaxItems > 0 && rawV.Len() > schema.MaxItems { - return nil, []error{fmt.Errorf( - "%s: attribute supports %d item maximum, config has %d declared", k, schema.MaxItems, rawV.Len())} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Too many list items", + Detail: fmt.Sprintf("Attribute supports %d item maximum, but config has %d declared.", schema.MaxItems, rawV.Len()), + AttributePath: path, + }) } if schema.MinItems > 0 && rawV.Len() < schema.MinItems { - return nil, []error{fmt.Errorf( - "%s: attribute supports %d item as a minimum, config has %d declared", k, schema.MinItems, rawV.Len())} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Not enough list items", + Detail: fmt.Sprintf("Attribute requires %d item minimum, but config has only %d declared.", schema.MinItems, rawV.Len()), + AttributePath: path, + }) } // Now build the []interface{} raws := make([]interface{}, rawV.Len()) - for i, _ := range raws { + for i := range raws { raws[i] = rawV.Index(i).Interface() } - var ws []string - var es []error for i, raw := range raws { key := fmt.Sprintf("%s.%d", k, i) @@ -1624,42 +1748,40 @@ func (m schemaMap) validateList( raw = r } - var ws2 []string - var es2 []error + p := append(path, cty.IndexStep{Key: cty.NumberIntVal(int64(i))}) + switch t := schema.Elem.(type) { case *Resource: // This is a sub-resource - ws2, es2 = m.validateObject(key, t.Schema, c) + diags = append(diags, m.validateObject(key, t.Schema, c, p)...) case *Schema: - ws2, es2 = m.validateType(key, raw, t, c) + diags = append(diags, m.validateType(key, raw, t, c, p)...) } - if len(ws2) > 0 { - ws = append(ws, ws2...) - } - if len(es2) > 0 { - es = append(es, es2...) - } } - return ws, es + return diags } func (m schemaMap) validateMap( k string, raw interface{}, schema *Schema, - c *terraform.ResourceConfig) ([]string, []error) { + c *terraform.ResourceConfig, + path cty.Path) diag.Diagnostics { + + var diags diag.Diagnostics + // first check if the list is wholly unknown if s, ok := raw.(string); ok { if s == hcl2shim.UnknownVariableValue { - return nil, nil + return diags } } // schemaMap can't validate nil if raw == nil { - return nil, nil + return diags } // We use reflection to verify the slice because you can't // case to []interface{} unless the slice is exactly that type. @@ -1670,93 +1792,124 @@ func (m schemaMap) validateMap( // be rejected. reified, reifiedOk := c.Get(k) if reifiedOk && raw == reified && !c.IsComputed(k) { - return nil, []error{fmt.Errorf("%s: should be a map", k)} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Attribute must be a map", + AttributePath: path, + }) } // Otherwise it's likely raw is an interpolation. - return nil, nil + return diags case reflect.Map: case reflect.Slice: default: - return nil, []error{fmt.Errorf("%s: should be a map", k)} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Attribute must be a map", + AttributePath: path, + }) } // If it is not a slice, validate directly if rawV.Kind() != reflect.Slice { mapIface := rawV.Interface() - if _, errs := validateMapValues(k, mapIface.(map[string]interface{}), schema); len(errs) > 0 { - return nil, errs - } - if schema.ValidateFunc != nil { - return schema.ValidateFunc(mapIface, k) + diags = append(diags, validateMapValues(k, mapIface.(map[string]interface{}), schema, path)...) + if diags.HasError() { + return diags } - return nil, nil + + return schema.validateFunc(mapIface, k, path) } // It is a slice, verify that all the elements are maps raws := make([]interface{}, rawV.Len()) - for i, _ := range raws { + for i := range raws { raws[i] = rawV.Index(i).Interface() } for _, raw := range raws { v := reflect.ValueOf(raw) if v.Kind() != reflect.Map { - return nil, []error{fmt.Errorf( - "%s: should be a map", k)} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Attribute must be a map", + AttributePath: path, + }) } mapIface := v.Interface() - if _, errs := validateMapValues(k, mapIface.(map[string]interface{}), schema); len(errs) > 0 { - return nil, errs + diags = append(diags, validateMapValues(k, mapIface.(map[string]interface{}), schema, path)...) + if diags.HasError() { + return diags } } - if schema.ValidateFunc != nil { - validatableMap := make(map[string]interface{}) - for _, raw := range raws { - for k, v := range raw.(map[string]interface{}) { - validatableMap[k] = v - } + validatableMap := make(map[string]interface{}) + for _, raw := range raws { + for k, v := range raw.(map[string]interface{}) { + validatableMap[k] = v } - - return schema.ValidateFunc(validatableMap, k) } - return nil, nil + return schema.validateFunc(validatableMap, k, path) } -func validateMapValues(k string, m map[string]interface{}, schema *Schema) ([]string, []error) { +func validateMapValues(k string, m map[string]interface{}, schema *Schema, path cty.Path) diag.Diagnostics { + + var diags diag.Diagnostics + for key, raw := range m { valueType, err := getValueType(k, schema) + p := append(path, cty.IndexStep{Key: cty.StringVal(key)}) if err != nil { - return nil, []error{err} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: err.Error(), + AttributePath: p, + }) } switch valueType { case TypeBool: var n bool if err := mapstructure.WeakDecode(raw, &n); err != nil { - return nil, []error{fmt.Errorf("%s (%s): %s", k, key, err)} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: err.Error(), + AttributePath: p, + }) } case TypeInt: var n int if err := mapstructure.WeakDecode(raw, &n); err != nil { - return nil, []error{fmt.Errorf("%s (%s): %s", k, key, err)} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: err.Error(), + AttributePath: p, + }) } case TypeFloat: var n float64 if err := mapstructure.WeakDecode(raw, &n); err != nil { - return nil, []error{fmt.Errorf("%s (%s): %s", k, key, err)} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: err.Error(), + AttributePath: p, + }) } case TypeString: var n string if err := mapstructure.WeakDecode(raw, &n); err != nil { - return nil, []error{fmt.Errorf("%s (%s): %s", k, key, err)} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: err.Error(), + AttributePath: p, + }) } default: panic(fmt.Sprintf("Unknown validation type: %#v", schema.Type)) } } - return nil, nil + return diags } func getValueType(k string, schema *Schema) (ValueType, error) { @@ -1785,64 +1938,68 @@ func getValueType(k string, schema *Schema) (ValueType, error) { func (m schemaMap) validateObject( k string, schema map[string]*Schema, - c *terraform.ResourceConfig) ([]string, []error) { + c *terraform.ResourceConfig, + path cty.Path) diag.Diagnostics { + + var diags diag.Diagnostics + raw, _ := c.Get(k) // schemaMap can't validate nil if raw == nil { - return nil, nil + return diags } if _, ok := raw.(map[string]interface{}); !ok && !c.IsComputed(k) { - return nil, []error{fmt.Errorf( - "%s: expected object, got %s", - k, reflect.ValueOf(raw).Kind())} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Expected Object Type", + Detail: fmt.Sprintf("Expected object, got %s", reflect.ValueOf(raw).Kind()), + AttributePath: path, + }) } - var ws []string - var es []error for subK, s := range schema { key := subK if k != "" { key = fmt.Sprintf("%s.%s", k, subK) } - - ws2, es2 := m.validate(key, s, c) - if len(ws2) > 0 { - ws = append(ws, ws2...) - } - if len(es2) > 0 { - es = append(es, es2...) - } + diags = append(diags, m.validate(key, s, c, append(path, cty.GetAttrStep{Name: subK}))...) } // Detect any extra/unknown keys and report those as errors. if m, ok := raw.(map[string]interface{}); ok { - for subk, _ := range m { + for subk := range m { if _, ok := schema[subk]; !ok { if subk == TimeoutsConfigKey { continue } - es = append(es, fmt.Errorf( - "%s: invalid or unknown key: %s", k, subk)) + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Invalid or unknown key", + AttributePath: append(path, cty.GetAttrStep{Name: subk}), + }) } } } - return ws, es + return diags } func (m schemaMap) validatePrimitive( k string, raw interface{}, schema *Schema, - c *terraform.ResourceConfig) ([]string, []error) { + c *terraform.ResourceConfig, + path cty.Path) diag.Diagnostics { + + var diags diag.Diagnostics // a nil value shouldn't happen in the old protocol, and in the new // protocol the types have already been validated. Either way, we can't // reflect on nil, so don't panic. if raw == nil { - return nil, nil + return diags } // Catch if the user gave a complex type where a primitive was @@ -1850,20 +2007,24 @@ func (m schemaMap) validatePrimitive( // doesn't contain Go type system terminology. switch reflect.ValueOf(raw).Type().Kind() { case reflect.Slice: - return nil, []error{ - fmt.Errorf("%s must be a single value, not a list", k), - } + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Attribute must be a single value, not a list", + AttributePath: path, + }) case reflect.Map: - return nil, []error{ - fmt.Errorf("%s must be a single value, not a map", k), - } + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Attribute must be a single value, not a map", + AttributePath: path, + }) default: // ok } if c.IsComputed(k) { // If the key is being computed, then it is not an error as // long as it's not a slice or map. - return nil, nil + return diags } var decoded interface{} @@ -1872,81 +2033,91 @@ func (m schemaMap) validatePrimitive( // Verify that we can parse this as the correct type var n bool if err := mapstructure.WeakDecode(raw, &n); err != nil { - return nil, []error{fmt.Errorf("%s: %s", k, err)} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: err.Error(), + AttributePath: path, + }) } decoded = n case TypeInt: - switch { - case isProto5(): - // We need to verify the type precisely, because WeakDecode will - // decode a float as an integer. - - // the config shims only use int for integral number values - if v, ok := raw.(int); ok { - decoded = v - } else { - return nil, []error{fmt.Errorf("%s: must be a whole number, got %v", k, raw)} - } - default: - // Verify that we can parse this as an int - var n int - if err := mapstructure.WeakDecode(raw, &n); err != nil { - return nil, []error{fmt.Errorf("%s: %s", k, err)} - } - decoded = n + // We need to verify the type precisely, because WeakDecode will + // decode a float as an integer. + + // the config shims only use int for integral number values + if v, ok := raw.(int); ok { + decoded = v + } else { + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: fmt.Sprintf("Attribute must be a whole number, got %v", raw), + AttributePath: path, + }) } case TypeFloat: // Verify that we can parse this as an int var n float64 if err := mapstructure.WeakDecode(raw, &n); err != nil { - return nil, []error{fmt.Errorf("%s: %s", k, err)} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: err.Error(), + AttributePath: path, + }) } decoded = n case TypeString: // Verify that we can parse this as a string var n string if err := mapstructure.WeakDecode(raw, &n); err != nil { - return nil, []error{fmt.Errorf("%s: %s", k, err)} + return append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: err.Error(), + AttributePath: path, + }) } decoded = n default: panic(fmt.Sprintf("Unknown validation type: %#v", schema.Type)) } - if schema.ValidateFunc != nil { - return schema.ValidateFunc(decoded, k) - } - - return nil, nil + return append(diags, schema.validateFunc(decoded, k, path)...) } func (m schemaMap) validateType( k string, raw interface{}, schema *Schema, - c *terraform.ResourceConfig) ([]string, []error) { - var ws []string - var es []error + c *terraform.ResourceConfig, + path cty.Path) diag.Diagnostics { + + var diags diag.Diagnostics switch schema.Type { - case TypeSet, TypeList: - ws, es = m.validateList(k, raw, schema, c) + case TypeList: + diags = m.validateList(k, raw, schema, c, path) + case TypeSet: + // indexing into sets is not representable in the current protocol + // best we can do is associate the path up to this attribute. + diags = m.validateList(k, raw, schema, c, path) + log.Printf("[WARN] Truncating attribute path of %d diagnostics for TypeSet", len(diags)) + for i := range diags { + diags[i].AttributePath = path + } case TypeMap: - ws, es = m.validateMap(k, raw, schema, c) + diags = m.validateMap(k, raw, schema, c, path) default: - ws, es = m.validatePrimitive(k, raw, schema, c) + diags = m.validatePrimitive(k, raw, schema, c, path) } if schema.Deprecated != "" { - ws = append(ws, fmt.Sprintf( - "%q: [DEPRECATED] %s", k, schema.Deprecated)) - } - - if schema.Removed != "" { - es = append(es, fmt.Errorf( - "%q: [REMOVED] %s", k, schema.Removed)) + diags = append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Argument is deprecated", + Detail: schema.Deprecated, + AttributePath: path, + }) } - return ws, es + return diags } // Zero returns the zero value for a type. diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/schema_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/schema_test.go new file mode 100644 index 00000000..47451b2a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/schema_test.go @@ -0,0 +1,8504 @@ +package schema + +import ( + "bytes" + "context" + "errors" + "fmt" + "os" + "reflect" + "sort" + "strconv" + "strings" + "testing" + + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/hcl2shim" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/diagutils" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/helper/hashcode" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestEnvDefaultFunc(t *testing.T) { + key := "TF_TEST_ENV_DEFAULT_FUNC" + defer os.Unsetenv(key) + + f := EnvDefaultFunc(key, "42") + if err := os.Setenv(key, "foo"); err != nil { + t.Fatalf("err: %s", err) + } + + actual, err := f() + if err != nil { + t.Fatalf("err: %s", err) + } + if actual != "foo" { + t.Fatalf("bad: %#v", actual) + } + + if err := os.Unsetenv(key); err != nil { + t.Fatalf("err: %s", err) + } + + actual, err = f() + if err != nil { + t.Fatalf("err: %s", err) + } + if actual != "42" { + t.Fatalf("bad: %#v", actual) + } +} + +func TestMultiEnvDefaultFunc(t *testing.T) { + keys := []string{ + "TF_TEST_MULTI_ENV_DEFAULT_FUNC1", + "TF_TEST_MULTI_ENV_DEFAULT_FUNC2", + } + defer func() { + for _, k := range keys { + os.Unsetenv(k) + } + }() + + // Test that the first key is returned first + f := MultiEnvDefaultFunc(keys, "42") + if err := os.Setenv(keys[0], "foo"); err != nil { + t.Fatalf("err: %s", err) + } + + actual, err := f() + if err != nil { + t.Fatalf("err: %s", err) + } + if actual != "foo" { + t.Fatalf("bad: %#v", actual) + } + + if err := os.Unsetenv(keys[0]); err != nil { + t.Fatalf("err: %s", err) + } + + // Test that the second key is returned if the first one is empty + f = MultiEnvDefaultFunc(keys, "42") + if err := os.Setenv(keys[1], "foo"); err != nil { + t.Fatalf("err: %s", err) + } + + actual, err = f() + if err != nil { + t.Fatalf("err: %s", err) + } + if actual != "foo" { + t.Fatalf("bad: %#v", actual) + } + + if err := os.Unsetenv(keys[1]); err != nil { + t.Fatalf("err: %s", err) + } + + // Test that the default value is returned when no keys are set + actual, err = f() + if err != nil { + t.Fatalf("err: %s", err) + } + if actual != "42" { + t.Fatalf("bad: %#v", actual) + } +} + +func TestValueType_Zero(t *testing.T) { + cases := []struct { + Type ValueType + Value interface{} + }{ + {TypeBool, false}, + {TypeInt, 0}, + {TypeFloat, 0.0}, + {TypeString, ""}, + {TypeList, []interface{}{}}, + {TypeMap, map[string]interface{}{}}, + {TypeSet, new(Set)}, + } + + for i, tc := range cases { + actual := tc.Type.Zero() + if !reflect.DeepEqual(actual, tc.Value) { + t.Fatalf("%d: %#v != %#v", i, actual, tc.Value) + } + } +} + +func TestSchemaMap_Diff(t *testing.T) { + cases := []struct { + Name string + Schema map[string]*Schema + State *terraform.InstanceState + Config map[string]interface{} + CustomizeDiff CustomizeDiffFunc + Diff *terraform.InstanceDiff + Err bool + }{ + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + NewComputed: true, + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "foo", + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Computed, but set in config", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + + Config: map[string]interface{}{ + "availability_zone": "bar", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "foo", + New: "bar", + }, + }, + }, + + Err: false, + }, + + { + Name: "Default", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Default: "foo", + }, + }, + + State: nil, + + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + }, + }, + }, + + Err: false, + }, + + { + Name: "DefaultFunc, value", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + DefaultFunc: func() (interface{}, error) { + return "foo", nil + }, + }, + }, + + State: nil, + + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + }, + }, + }, + + Err: false, + }, + + { + Name: "DefaultFunc, configuration set", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + DefaultFunc: func() (interface{}, error) { + return "foo", nil + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "bar", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "bar", + }, + }, + }, + + Err: false, + }, + + { + Name: "String with StateFunc", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + StateFunc: func(a interface{}) string { + return a.(string) + "!" + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo!", + NewExtra: "foo", + }, + }, + }, + + Err: false, + }, + + { + Name: "StateFunc not called with nil value", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + StateFunc: func(a interface{}) string { + t.Fatalf("should not get here!") + return "" + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Variable computed", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": hcl2shim.UnknownVariableValue, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: hcl2shim.UnknownVariableValue, + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Int decode", + Schema: map[string]*Schema{ + "port": { + Type: TypeInt, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "port": 27, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "port": { + Old: "", + New: "27", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "bool decode", + Schema: map[string]*Schema{ + "port": { + Type: TypeBool, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "port": false, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "port": { + Old: "", + New: "false", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Bool", + Schema: map[string]*Schema{ + "delete": { + Type: TypeBool, + Optional: true, + Default: false, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "delete": "false", + }, + }, + + Config: nil, + + Diff: nil, + + Err: false, + }, + + { + Name: "List decode", + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "0", + New: "3", + }, + "ports.0": { + Old: "", + New: "1", + }, + "ports.1": { + Old: "", + New: "2", + }, + "ports.2": { + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "0", + New: "3", + }, + "ports.0": { + Old: "", + New: "1", + }, + "ports.1": { + Old: "", + New: "2", + }, + "ports.2": { + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, hcl2shim.UnknownVariableValue, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "0", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.0": "1", + "ports.1": "2", + "ports.2": "5", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: nil, + + Err: false, + }, + + { + Name: "", + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "2", + "ports.0": "1", + "ports.1": "2", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "2", + New: "3", + }, + "ports.2": { + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "", + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "0", + New: "3", + RequiresNew: true, + }, + "ports.0": { + Old: "", + New: "1", + RequiresNew: true, + }, + "ports.1": { + Old: "", + New: "2", + RequiresNew: true, + }, + "ports.2": { + Old: "", + New: "5", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "", + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "List with computed set", + Schema: map[string]*Schema{ + "config": { + Type: TypeList, + Optional: true, + ForceNew: true, + MinItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "name": { + Type: TypeString, + Required: true, + }, + + "rules": { + Type: TypeSet, + Computed: true, + Elem: &Schema{Type: TypeString}, + Set: HashString, + }, + }, + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "config": []interface{}{ + map[string]interface{}{ + "name": "hello", + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config.#": { + Old: "0", + New: "1", + RequiresNew: true, + }, + + "config.0.name": { + Old: "", + New: "hello", + }, + + "config.0.rules.#": { + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{5, 2, 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "0", + New: "3", + }, + "ports.1": { + Old: "", + New: "1", + }, + "ports.2": { + Old: "", + New: "2", + }, + "ports.5": { + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Computed: true, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "0", + }, + }, + + Config: nil, + + Diff: nil, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{"2", "5", 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "0", + New: "3", + }, + "ports.1": { + Old: "", + New: "1", + }, + "ports.2": { + Old: "", + New: "2", + }, + "ports.5": { + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, hcl2shim.UnknownVariableValue, "5"}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "2", + "ports.1": "1", + "ports.2": "2", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{5, 2, 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "2", + New: "3", + }, + "ports.1": { + Old: "1", + New: "1", + }, + "ports.2": { + Old: "2", + New: "2", + }, + "ports.5": { + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "2", + "ports.1": "1", + "ports.2": "2", + }, + }, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "2", + New: "0", + }, + "ports.1": { + Old: "1", + New: "0", + NewRemoved: true, + }, + "ports.2": { + Old: "2", + New: "0", + NewRemoved: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "bar", + "ports.#": "1", + "ports.80": "80", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "ingress": { + Type: TypeSet, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + ps := m["ports"].([]interface{}) + result := 0 + for _, p := range ps { + result += p.(int) + } + return result + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ingress.#": "2", + "ingress.80.ports.#": "1", + "ingress.80.ports.0": "80", + "ingress.443.ports.#": "1", + "ingress.443.ports.0": "443", + }, + }, + + Config: map[string]interface{}{ + "ingress": []interface{}{ + map[string]interface{}{ + "ports": []interface{}{443}, + }, + map[string]interface{}{ + "ports": []interface{}{80}, + }, + }, + }, + + Diff: nil, + + Err: false, + }, + + { + Name: "List of structure decode", + Schema: map[string]*Schema{ + "ingress": { + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": { + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ingress": []interface{}{ + map[string]interface{}{ + "from": 8080, + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ingress.#": { + Old: "0", + New: "1", + }, + "ingress.0.from": { + Old: "", + New: "8080", + }, + }, + }, + + Err: false, + }, + + { + Name: "ComputedWhen", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Computed: true, + ComputedWhen: []string{"port"}, + }, + + "port": { + Type: TypeInt, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + "port": "80", + }, + }, + + Config: map[string]interface{}{ + "port": 80, + }, + + Diff: nil, + + Err: false, + }, + + { + Name: "", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Computed: true, + ComputedWhen: []string{"port"}, + }, + + "port": { + Type: TypeInt, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "port": "80", + }, + }, + + Config: map[string]interface{}{ + "port": 80, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + /* TODO + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Computed: true, + ComputedWhen: []string{"port"}, + }, + + "port": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + "port": "80", + }, + }, + + Config: map[string]interface{}{ + "port": 8080, + }, + + Diff: &terraform.ResourceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "foo", + NewComputed: true, + }, + "port": &terraform.ResourceAttrDiff{ + Old: "80", + New: "8080", + }, + }, + }, + + Err: false, + }, + */ + + { + Name: "Maps", + Schema: map[string]*Schema{ + "config_vars": { + Type: TypeMap, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "config_vars": []interface{}{ + map[string]interface{}{ + "bar": "baz", + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.%": { + Old: "0", + New: "1", + }, + + "config_vars.bar": { + Old: "", + New: "baz", + }, + }, + }, + + Err: false, + }, + + { + Name: "Maps", + Schema: map[string]*Schema{ + "config_vars": { + Type: TypeMap, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.foo": "bar", + }, + }, + + Config: map[string]interface{}{ + "config_vars": []interface{}{ + map[string]interface{}{ + "bar": "baz", + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.foo": { + Old: "bar", + NewRemoved: true, + }, + "config_vars.bar": { + Old: "", + New: "baz", + }, + }, + }, + + Err: false, + }, + + { + Name: "Maps", + Schema: map[string]*Schema{ + "vars": { + Type: TypeMap, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "vars.foo": "bar", + }, + }, + + Config: map[string]interface{}{ + "vars": []interface{}{ + map[string]interface{}{ + "bar": "baz", + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "vars.foo": { + Old: "bar", + New: "", + NewRemoved: true, + }, + "vars.bar": { + Old: "", + New: "baz", + }, + }, + }, + + Err: false, + }, + + { + Name: "Maps", + Schema: map[string]*Schema{ + "vars": { + Type: TypeMap, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "vars.foo": "bar", + }, + }, + + Config: nil, + + Diff: nil, + + Err: false, + }, + + { + Name: "Maps", + Schema: map[string]*Schema{ + "config_vars": { + Type: TypeList, + Elem: &Schema{Type: TypeMap}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.#": "1", + "config_vars.0.foo": "bar", + }, + }, + + Config: map[string]interface{}{ + "config_vars": []interface{}{ + map[string]interface{}{ + "bar": "baz", + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.0.foo": { + Old: "bar", + NewRemoved: true, + }, + "config_vars.0.bar": { + Old: "", + New: "baz", + }, + }, + }, + + Err: false, + }, + + { + Name: "Maps", + Schema: map[string]*Schema{ + "config_vars": { + Type: TypeList, + Elem: &Schema{Type: TypeMap}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.#": "1", + "config_vars.0.foo": "bar", + "config_vars.0.bar": "baz", + }, + }, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.#": { + Old: "1", + New: "0", + }, + "config_vars.0.%": { + Old: "2", + New: "0", + }, + "config_vars.0.foo": { + Old: "bar", + NewRemoved: true, + }, + "config_vars.0.bar": { + Old: "baz", + NewRemoved: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "ForceNews", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + ForceNew: true, + }, + + "address": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "bar", + "address": "foo", + }, + }, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "bar", + New: "foo", + RequiresNew: true, + }, + + "address": { + Old: "foo", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + ForceNew: true, + }, + + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "bar", + "ports.#": "1", + "ports.80": "80", + }, + }, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "bar", + New: "foo", + RequiresNew: true, + }, + + "ports.#": { + Old: "1", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "instances": { + Type: TypeSet, + Elem: &Schema{Type: TypeString}, + Optional: true, + Computed: true, + Set: func(v interface{}) int { + return len(v.(string)) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "instances.#": "0", + }, + }, + + Config: map[string]interface{}{ + "instances": []interface{}{hcl2shim.UnknownVariableValue}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "instances.#": { + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "route": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": { + Type: TypeInt, + Required: true, + }, + + "gateway": { + Type: TypeString, + Optional: true, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "route": []interface{}{ + map[string]interface{}{ + "index": "1", + "gateway": hcl2shim.UnknownVariableValue, + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "route.#": { + Old: "0", + New: "1", + }, + "route.~1.index": { + Old: "", + New: "1", + }, + "route.~1.gateway": { + Old: "", + New: hcl2shim.UnknownVariableValue, + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "route": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": { + Type: TypeInt, + Required: true, + }, + + "gateway": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "route": []interface{}{ + map[string]interface{}{ + "index": "1", + "gateway": []interface{}{ + hcl2shim.UnknownVariableValue, + }, + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "route.#": { + Old: "0", + New: "1", + }, + "route.~1.index": { + Old: "", + New: "1", + }, + "route.~1.gateway.#": { + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Computed maps", + Schema: map[string]*Schema{ + "vars": { + Type: TypeMap, + Computed: true, + }, + }, + + State: nil, + + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "vars.%": { + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Computed maps", + Schema: map[string]*Schema{ + "vars": { + Type: TypeMap, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "vars.%": "0", + }, + }, + + Config: map[string]interface{}{ + "vars": map[string]interface{}{ + "bar": hcl2shim.UnknownVariableValue, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "vars.%": { + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: " - Empty", + Schema: map[string]*Schema{}, + + State: &terraform.InstanceState{}, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Float", + Schema: map[string]*Schema{ + "some_threshold": { + Type: TypeFloat, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "some_threshold": "567.8", + }, + }, + + Config: map[string]interface{}{ + "some_threshold": 12.34, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "some_threshold": { + Old: "567.8", + New: "12.34", + }, + }, + }, + + Err: false, + }, + + { + Name: "https://github.com/hashicorp/terraform-plugin-sdk/issues/824", + Schema: map[string]*Schema{ + "block_device": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "device_name": { + Type: TypeString, + Required: true, + }, + "delete_on_termination": { + Type: TypeBool, + Optional: true, + Default: true, + }, + }, + }, + Set: func(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string))) + buf.WriteString(fmt.Sprintf("%t-", m["delete_on_termination"].(bool))) + return hashcode.String(buf.String()) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "block_device.#": "2", + "block_device.616397234.delete_on_termination": "true", + "block_device.616397234.device_name": "/dev/sda1", + "block_device.2801811477.delete_on_termination": "true", + "block_device.2801811477.device_name": "/dev/sdx", + }, + }, + + Config: map[string]interface{}{ + "block_device": []interface{}{ + map[string]interface{}{ + "device_name": "/dev/sda1", + }, + map[string]interface{}{ + "device_name": "/dev/sdx", + }, + }, + }, + Diff: nil, + Err: false, + }, + + { + Name: "Zero value in state shouldn't result in diff", + Schema: map[string]*Schema{ + "port": { + Type: TypeBool, + Optional: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "port": "false", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Same as prev, but for sets", + Schema: map[string]*Schema{ + "route": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": { + Type: TypeInt, + Required: true, + }, + + "gateway": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "route.#": "0", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "A set computed element shouldn't cause a diff", + Schema: map[string]*Schema{ + "active": { + Type: TypeBool, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "active": "true", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "An empty set should show up in the diff", + Schema: map[string]*Schema{ + "instances": { + Type: TypeSet, + Elem: &Schema{Type: TypeString}, + Optional: true, + ForceNew: true, + Set: func(v interface{}) int { + return len(v.(string)) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "instances.#": "1", + "instances.3": "foo", + }, + }, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "instances.#": { + Old: "1", + New: "0", + RequiresNew: true, + }, + "instances.3": { + Old: "foo", + New: "", + NewRemoved: true, + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Map with empty value", + Schema: map[string]*Schema{ + "vars": { + Type: TypeMap, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "vars": map[string]interface{}{ + "foo": "", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "vars.%": { + Old: "0", + New: "1", + }, + "vars.foo": { + Old: "", + New: "", + }, + }, + }, + + Err: false, + }, + + { + Name: "Unset bool, not in state", + Schema: map[string]*Schema{ + "force": { + Type: TypeBool, + Optional: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Unset set, not in state", + Schema: map[string]*Schema{ + "metadata_keys": { + Type: TypeSet, + Optional: true, + ForceNew: true, + Elem: &Schema{Type: TypeInt}, + Set: func(interface{}) int { return 0 }, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Unset list in state, should not show up computed", + Schema: map[string]*Schema{ + "metadata_keys": { + Type: TypeList, + Optional: true, + Computed: true, + ForceNew: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "metadata_keys.#": "0", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Set element computed element", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, hcl2shim.UnknownVariableValue}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Computed map without config that's known to be empty does not generate diff", + Schema: map[string]*Schema{ + "tags": { + Type: TypeMap, + Computed: true, + }, + }, + + Config: nil, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "tags.%": "0", + }, + }, + + Diff: nil, + + Err: false, + }, + + { + Name: "Set with hyphen keys", + Schema: map[string]*Schema{ + "route": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": { + Type: TypeInt, + Required: true, + }, + + "gateway-name": { + Type: TypeString, + Optional: true, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "route": []interface{}{ + map[string]interface{}{ + "index": "1", + "gateway-name": "hello", + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "route.#": { + Old: "0", + New: "1", + }, + "route.1.index": { + Old: "", + New: "1", + }, + "route.1.gateway-name": { + Old: "", + New: "hello", + }, + }, + }, + + Err: false, + }, + + { + Name: ": StateFunc in nested set (#1759)", + Schema: map[string]*Schema{ + "service_account": { + Type: TypeList, + Optional: true, + ForceNew: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "scopes": { + Type: TypeSet, + Required: true, + ForceNew: true, + Elem: &Schema{ + Type: TypeString, + StateFunc: func(v interface{}) string { + return v.(string) + "!" + }, + }, + Set: func(v interface{}) int { + i, err := strconv.Atoi(v.(string)) + if err != nil { + t.Fatalf("err: %s", err) + } + return i + }, + }, + }, + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "service_account": []interface{}{ + map[string]interface{}{ + "scopes": []interface{}{"123"}, + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "service_account.#": { + Old: "0", + New: "1", + RequiresNew: true, + }, + "service_account.0.scopes.#": { + Old: "0", + New: "1", + RequiresNew: true, + }, + "service_account.0.scopes.123": { + Old: "", + New: "123!", + NewExtra: "123", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Removing set elements", + Schema: map[string]*Schema{ + "instances": { + Type: TypeSet, + Elem: &Schema{Type: TypeString}, + Optional: true, + ForceNew: true, + Set: func(v interface{}) int { + return len(v.(string)) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "instances.#": "2", + "instances.3": "333", + "instances.2": "22", + }, + }, + + Config: map[string]interface{}{ + "instances": []interface{}{"333", "4444"}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "instances.#": { + Old: "2", + New: "2", + }, + "instances.2": { + Old: "22", + New: "", + NewRemoved: true, + RequiresNew: true, + }, + "instances.3": { + Old: "333", + New: "333", + }, + "instances.4": { + Old: "", + New: "4444", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Bools can be set with 0/1 in config, still get true/false", + Schema: map[string]*Schema{ + "one": { + Type: TypeBool, + Optional: true, + }, + "two": { + Type: TypeBool, + Optional: true, + }, + "three": { + Type: TypeBool, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "one": "false", + "two": "true", + "three": "true", + }, + }, + + Config: map[string]interface{}{ + "one": "1", + "two": "0", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "one": { + Old: "false", + New: "true", + }, + "two": { + Old: "true", + New: "false", + }, + "three": { + Old: "true", + New: "false", + NewRemoved: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "tainted in state w/ no attr changes is still a replacement", + Schema: map[string]*Schema{}, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "id": "someid", + }, + Tainted: true, + }, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + DestroyTainted: true, + }, + + Err: false, + }, + + { + Name: "Set ForceNew only marks the changing element as ForceNew", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Required: true, + ForceNew: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.1": "1", + "ports.2": "2", + "ports.4": "4", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{5, 2, 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "3", + New: "3", + }, + "ports.1": { + Old: "1", + New: "1", + }, + "ports.2": { + Old: "2", + New: "2", + }, + "ports.5": { + Old: "", + New: "5", + RequiresNew: true, + }, + "ports.4": { + Old: "4", + New: "0", + NewRemoved: true, + RequiresNew: true, + }, + }, + }, + }, + + { + Name: "removed optional items should trigger ForceNew", + Schema: map[string]*Schema{ + "description": { + Type: TypeString, + ForceNew: true, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "description": "foo", + }, + }, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "description": { + Old: "foo", + New: "", + RequiresNew: true, + NewRemoved: true, + }, + }, + }, + + Err: false, + }, + + // GH-7715 + { + Name: "computed value for boolean field", + Schema: map[string]*Schema{ + "foo": { + Type: TypeBool, + ForceNew: true, + Computed: true, + Optional: true, + }, + }, + + State: &terraform.InstanceState{}, + + Config: map[string]interface{}{ + "foo": hcl2shim.UnknownVariableValue, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "", + New: "false", + NewComputed: true, + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set ForceNew marks count as ForceNew if computed", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Required: true, + ForceNew: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.1": "1", + "ports.2": "2", + "ports.4": "4", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{hcl2shim.UnknownVariableValue, 2, 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "3", + New: "", + NewComputed: true, + RequiresNew: true, + }, + }, + }, + }, + + { + Name: "List with computed schema and ForceNew", + Schema: map[string]*Schema{ + "config": { + Type: TypeList, + Optional: true, + ForceNew: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "config.#": "2", + "config.0": "a", + "config.1": "b", + }, + }, + + Config: map[string]interface{}{ + "config": []interface{}{hcl2shim.UnknownVariableValue, hcl2shim.UnknownVariableValue}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config.#": { + Old: "2", + New: "", + RequiresNew: true, + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "overridden diff with a CustomizeDiff function, ForceNew not in schema", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + CustomizeDiff: func(_ context.Context, d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("availability_zone", "bar"); err != nil { + return err + } + if err := d.ForceNew("availability_zone"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "bar", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + // NOTE: This case is technically impossible in the current + // implementation, because optional+computed values never show up in the + // diff. In the event behavior changes this test should ensure that the + // intended diff still shows up. + Name: "overridden removed attribute diff with a CustomizeDiff function, ForceNew not in schema", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + CustomizeDiff: func(_ context.Context, d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("availability_zone", "bar"); err != nil { + return err + } + if err := d.ForceNew("availability_zone"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "bar", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + + Name: "overridden diff with a CustomizeDiff function, ForceNew in schema", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + CustomizeDiff: func(_ context.Context, d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("availability_zone", "bar"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "bar", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "required field with computed diff added with CustomizeDiff function", + Schema: map[string]*Schema{ + "ami_id": { + Type: TypeString, + Required: true, + }, + "instance_id": { + Type: TypeString, + Computed: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ami_id": "foo", + }, + + CustomizeDiff: func(_ context.Context, d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("instance_id", "bar"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ami_id": { + Old: "", + New: "foo", + }, + "instance_id": { + Old: "", + New: "bar", + }, + }, + }, + + Err: false, + }, + + { + Name: "Set ForceNew only marks the changing element as ForceNew - CustomizeDiffFunc edition", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.1": "1", + "ports.2": "2", + "ports.4": "4", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{5, 2, 6}, + }, + + CustomizeDiff: func(_ context.Context, d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("ports", []interface{}{5, 2, 1}); err != nil { + return err + } + if err := d.ForceNew("ports"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "3", + New: "3", + }, + "ports.1": { + Old: "1", + New: "1", + }, + "ports.2": { + Old: "2", + New: "2", + }, + "ports.5": { + Old: "", + New: "5", + RequiresNew: true, + }, + "ports.4": { + Old: "4", + New: "0", + NewRemoved: true, + RequiresNew: true, + }, + }, + }, + }, + + { + Name: "tainted resource does not run CustomizeDiffFunc", + Schema: map[string]*Schema{}, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "id": "someid", + }, + Tainted: true, + }, + + Config: map[string]interface{}{}, + + CustomizeDiff: func(_ context.Context, d *ResourceDiff, meta interface{}) error { + return errors.New("diff customization should not have run") + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + DestroyTainted: true, + }, + + Err: false, + }, + + { + Name: "NewComputed based on a conditional with CustomizeDiffFunc", + Schema: map[string]*Schema{ + "etag": { + Type: TypeString, + Optional: true, + Computed: true, + }, + "version_id": { + Type: TypeString, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "etag": "foo", + "version_id": "1", + }, + }, + + Config: map[string]interface{}{ + "etag": "bar", + }, + + CustomizeDiff: func(_ context.Context, d *ResourceDiff, meta interface{}) error { + if d.HasChange("etag") { + d.SetNewComputed("version_id") + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "etag": { + Old: "foo", + New: "bar", + }, + "version_id": { + Old: "1", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "NewComputed should always propagate with CustomizeDiff", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "", + }, + ID: "pre-existing", + }, + + Config: map[string]interface{}{}, + + CustomizeDiff: func(_ context.Context, d *ResourceDiff, meta interface{}) error { + d.SetNewComputed("foo") + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "vetoing a diff", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + + Config: map[string]interface{}{ + "foo": "baz", + }, + + CustomizeDiff: func(_ context.Context, d *ResourceDiff, meta interface{}) error { + return fmt.Errorf("diff vetoed") + }, + + Err: true, + }, + + // A lot of resources currently depended on using the empty string as a + // nil/unset value. + // FIXME: We want this to eventually produce a diff, since there + // technically is a new value in the config. + { + Name: "optional, computed, empty string", + Schema: map[string]*Schema{ + "attr": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "attr": "bar", + }, + }, + + Config: map[string]interface{}{ + "attr": "", + }, + }, + + { + Name: "optional, computed, empty string should not crash in CustomizeDiff", + Schema: map[string]*Schema{ + "unrelated_set": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "stream_enabled": { + Type: TypeBool, + Optional: true, + }, + "stream_view_type": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "unrelated_set.#": "0", + "stream_enabled": "true", + "stream_view_type": "KEYS_ONLY", + }, + }, + Config: map[string]interface{}{ + "stream_enabled": false, + "stream_view_type": "", + }, + CustomizeDiff: func(_ context.Context, diff *ResourceDiff, v interface{}) error { + v, ok := diff.GetOk("unrelated_set") + if ok { + return fmt.Errorf("Didn't expect unrelated_set: %#v", v) + } + return nil + }, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "stream_enabled": { + Old: "true", + New: "false", + }, + }, + }, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + c := terraform.NewResourceConfigRaw(tc.Config) + + d, err := schemaMap(tc.Schema).Diff(context.Background(), tc.State, c, tc.CustomizeDiff, nil, true) + if err != nil != tc.Err { + t.Fatalf("err: %s", err) + } + + if !reflect.DeepEqual(tc.Diff, d) { + t.Fatalf("expected:\n%#v\n\ngot:\n%#v", tc.Diff, d) + } + }) + } +} + +func TestSchemaMap_InternalValidate(t *testing.T) { + cases := map[string]struct { + In map[string]*Schema + Err bool + }{ + "nothing": { + nil, + false, + }, + + "Both optional and required": { + map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + Required: true, + }, + }, + true, + }, + + "No optional and no required": { + map[string]*Schema{ + "foo": { + Type: TypeInt, + }, + }, + true, + }, + + "Missing Type": { + map[string]*Schema{ + "foo": { + Required: true, + }, + }, + true, + }, + + "Required but computed": { + map[string]*Schema{ + "foo": { + Type: TypeInt, + Required: true, + Computed: true, + }, + }, + true, + }, + + "Looks good": { + map[string]*Schema{ + "foo": { + Type: TypeString, + Required: true, + }, + }, + false, + }, + + "Computed but has default": { + map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + Computed: true, + Default: "foo", + }, + }, + true, + }, + + "Required but has default": { + map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + Required: true, + Default: "foo", + }, + }, + true, + }, + + "List element not set": { + map[string]*Schema{ + "foo": { + Type: TypeList, + }, + }, + true, + }, + + "List default": { + map[string]*Schema{ + "foo": { + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + Default: "foo", + }, + }, + true, + }, + + "List element computed": { + map[string]*Schema{ + "foo": { + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeInt, + Computed: true, + }, + }, + }, + true, + }, + + "List element with Set set": { + map[string]*Schema{ + "foo": { + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + Set: func(interface{}) int { return 0 }, + Optional: true, + }, + }, + true, + }, + + "Set element with no Set set": { + map[string]*Schema{ + "foo": { + Type: TypeSet, + Elem: &Schema{Type: TypeInt}, + Optional: true, + }, + }, + false, + }, + + "Required but computedWhen": { + map[string]*Schema{ + "foo": { + Type: TypeInt, + Required: true, + ComputedWhen: []string{"foo"}, + }, + }, + true, + }, + + "Conflicting attributes cannot be required": { + map[string]*Schema{ + "blacklist": { + Type: TypeBool, + Required: true, + }, + "whitelist": { + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"blacklist"}, + }, + }, + true, + }, + + "Attribute with conflicts cannot be required": { + map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Required: true, + ConflictsWith: []string{"blacklist"}, + }, + }, + true, + }, + + "ConflictsWith cannot be used w/ ComputedWhen": { + map[string]*Schema{ + "blacklist": { + Type: TypeBool, + ComputedWhen: []string{"foor"}, + }, + "whitelist": { + Type: TypeBool, + Required: true, + ConflictsWith: []string{"blacklist"}, + }, + }, + true, + }, + + "AtLeastOneOf list index syntax with self reference": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + MaxItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + AtLeastOneOf: []string{"config_block_attr.0.nested_attr"}, + }, + }, + }, + }, + }, + false, + }, + + "AtLeastOneOf list index syntax with list configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + MaxItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"config_block_attr.0.nested_attr"}, + }, + }, + false, + }, + + "AtLeastOneOf list index syntax with list configuration block missing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "AtLeastOneOf list index syntax with list configuration block missing MaxItems": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "AtLeastOneOf list index syntax with set configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"config_block_attr.0.nested_attr"}, + }, + }, + true, + }, + + "AtLeastOneOf list index syntax with set configuration block missing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "AtLeastOneOf map key syntax with list configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"config_block_attr.nested_attr"}, + }, + }, + true, + }, + + "AtLeastOneOf map key syntax with list configuration block self reference": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + AtLeastOneOf: []string{"config_block_attr.nested_attr"}, + }, + }, + }, + }, + }, + true, + }, + + "AtLeastOneOf map key syntax with set configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"config_block_attr.nested_attr"}, + }, + }, + true, + }, + + "AtLeastOneOf map key syntax with set configuration block self reference": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + AtLeastOneOf: []string{"config_block_attr.nested_attr"}, + }, + }, + }, + }, + }, + true, + }, + + "AtLeastOneOf map key syntax with map attribute": { + map[string]*Schema{ + "map_attr": { + Type: TypeMap, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"map_attr.some_key"}, + }, + }, + true, + }, + + "AtLeastOneOf string syntax with list attribute": { + map[string]*Schema{ + "list_attr": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"list_attr"}, + }, + }, + false, + }, + + "AtLeastOneOf string syntax with list configuration block": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"config_block_attr"}, + }, + }, + false, + }, + + "AtLeastOneOf string syntax with map attribute": { + map[string]*Schema{ + "map_attr": { + Type: TypeMap, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"map_attr"}, + }, + }, + false, + }, + + "AtLeastOneOf string syntax with set attribute": { + map[string]*Schema{ + "set_attr": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"set_attr"}, + }, + }, + false, + }, + + "AtLeastOneOf string syntax with set configuration block": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"config_block_attr"}, + }, + }, + false, + }, + + "AtLeastOneOf string syntax with self reference": { + map[string]*Schema{ + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"test"}, + }, + }, + false, + }, + + "ConflictsWith list index syntax with self reference": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + ConflictsWith: []string{"config_block_attr.0.nested_attr"}, + }, + }, + }, + }, + }, + true, + }, + + "ConflictsWith list index syntax with list configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + MaxItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.0.nested_attr"}, + }, + }, + false, + }, + + "ConflictsWith list index syntax with list configuration block missing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "ConflictsWith list index syntax with list configuration block missing MaxItems": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "ConflictsWith list index syntax with set configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.0.nested_attr"}, + }, + }, + true, + }, + + "ConflictsWith list index syntax with set configuration block missing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "ConflictsWith map key syntax with list configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.nested_attr"}, + }, + }, + true, + }, + + "ConflictsWith map key syntax with list configuration block self reference": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + ConflictsWith: []string{"config_block_attr.nested_attr"}, + }, + }, + }, + }, + }, + true, + }, + + "ConflictsWith map key syntax with set configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.nested_attr"}, + }, + }, + true, + }, + + "ConflictsWith map key syntax with set configuration block self reference": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + ConflictsWith: []string{"config_block_attr.nested_attr"}, + }, + }, + }, + }, + }, + true, + }, + + "ConflictsWith map key syntax with map attribute": { + map[string]*Schema{ + "map_attr": { + Type: TypeMap, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"map_attr.some_key"}, + }, + }, + true, + }, + + "ConflictsWith string syntax with list attribute": { + map[string]*Schema{ + "list_attr": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"list_attr"}, + }, + }, + false, + }, + + "ConflictsWith string syntax with list configuration block": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr"}, + }, + }, + false, + }, + + "ConflictsWith string syntax with map attribute": { + map[string]*Schema{ + "map_attr": { + Type: TypeMap, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"map_attr"}, + }, + }, + false, + }, + + "ConflictsWith string syntax with set attribute": { + map[string]*Schema{ + "set_attr": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"set_attr"}, + }, + }, + false, + }, + + "ConflictsWith string syntax with set configuration block": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr"}, + }, + }, + false, + }, + + "ConflictsWith string syntax with self reference": { + map[string]*Schema{ + "test": { + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"test"}, + }, + }, + true, + }, + + "ExactlyOneOf list index syntax with self reference": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + MaxItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + ExactlyOneOf: []string{"config_block_attr.0.nested_attr"}, + }, + }, + }, + }, + }, + false, + }, + + "ExactlyOneOf list index syntax with list configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + MaxItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"config_block_attr.0.nested_attr"}, + }, + }, + false, + }, + + "ExactlyOneOf list index syntax with list configuration block missing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "ExactlyOneOf list index syntax with list configuration block missing MaxItems": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "ExactlyOneOf list index syntax with set configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"config_block_attr.0.nested_attr"}, + }, + }, + true, + }, + + "ExactlyOneOf list index syntax with set configuration block missing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "ExactlyOneOf map key syntax with list configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"config_block_attr.nested_attr"}, + }, + }, + true, + }, + + "ExactlyOneOf map key syntax with list configuration block self reference": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + ExactlyOneOf: []string{"config_block_attr.nested_attr"}, + }, + }, + }, + }, + }, + true, + }, + + "ExactlyOneOf map key syntax with set configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"config_block_attr.nested_attr"}, + }, + }, + true, + }, + + "ExactlyOneOf map key syntax with set configuration block self reference": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + ExactlyOneOf: []string{"config_block_attr.nested_attr"}, + }, + }, + }, + }, + }, + true, + }, + + "ExactlyOneOf map key syntax with map attribute": { + map[string]*Schema{ + "map_attr": { + Type: TypeMap, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"map_attr.some_key"}, + }, + }, + true, + }, + + "ExactlyOneOf string syntax with list attribute": { + map[string]*Schema{ + "list_attr": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"list_attr"}, + }, + }, + false, + }, + + "ExactlyOneOf string syntax with list configuration block": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"config_block_attr"}, + }, + }, + false, + }, + + "ExactlyOneOf string syntax with map attribute": { + map[string]*Schema{ + "map_attr": { + Type: TypeMap, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"map_attr"}, + }, + }, + false, + }, + + "ExactlyOneOf string syntax with set attribute": { + map[string]*Schema{ + "set_attr": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"set_attr"}, + }, + }, + false, + }, + + "ExactlyOneOf string syntax with set configuration block": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"config_block_attr"}, + }, + }, + false, + }, + + "ExactlyOneOf string syntax with self reference": { + map[string]*Schema{ + "test": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"test"}, + }, + }, + false, + }, + + "RequiredWith list index syntax with self reference": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + MaxItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + RequiredWith: []string{"config_block_attr.0.nested_attr"}, + }, + }, + }, + }, + }, + false, + }, + + "RequiredWith list index syntax with list configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + MaxItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"config_block_attr.0.nested_attr"}, + }, + }, + false, + }, + + "RequiredWith list index syntax with list configuration block missing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "RequiredWith list index syntax with list configuration block missing MaxItems": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "RequiredWith list index syntax with set configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"config_block_attr.0.nested_attr"}, + }, + }, + true, + }, + + "RequiredWith list index syntax with set configuration block missing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"config_block_attr.0.missing_attr"}, + }, + }, + true, + }, + + "RequiredWith map key syntax with list configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"config_block_attr.nested_attr"}, + }, + }, + true, + }, + + "RequiredWith map key syntax with list configuration block self reference": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + RequiredWith: []string{"config_block_attr.nested_attr"}, + }, + }, + }, + }, + }, + true, + }, + + "RequiredWith map key syntax with set configuration block existing attribute": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"config_block_attr.nested_attr"}, + }, + }, + true, + }, + + "RequiredWith map key syntax with set configuration block self reference": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + RequiredWith: []string{"config_block_attr.nested_attr"}, + }, + }, + }, + }, + }, + true, + }, + + "RequiredWith map key syntax with map attribute": { + map[string]*Schema{ + "map_attr": { + Type: TypeMap, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"map_attr.some_key"}, + }, + }, + true, + }, + + "RequiredWith string syntax with list attribute": { + map[string]*Schema{ + "list_attr": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"list_attr"}, + }, + }, + false, + }, + + "RequiredWith string syntax with list configuration block": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"config_block_attr"}, + }, + }, + false, + }, + + "RequiredWith string syntax with map attribute": { + map[string]*Schema{ + "map_attr": { + Type: TypeMap, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"map_attr"}, + }, + }, + false, + }, + + "RequiredWith string syntax with set attribute": { + map[string]*Schema{ + "set_attr": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"set_attr"}, + }, + }, + false, + }, + + "RequiredWith string syntax with set configuration block": { + map[string]*Schema{ + "config_block_attr": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_attr": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"config_block_attr"}, + }, + }, + false, + }, + + "RequiredWith string syntax with self reference": { + map[string]*Schema{ + "test": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"test"}, + }, + }, + false, + }, + + "Sub-resource invalid": { + map[string]*Schema{ + "foo": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "foo": new(Schema), + }, + }, + }, + }, + true, + }, + + "Sub-resource valid": { + map[string]*Schema{ + "foo": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + }, + }, + }, + false, + }, + + "ValidateFunc on non-primitive": { + map[string]*Schema{ + "foo": { + Type: TypeSet, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + return + }, + }, + }, + true, + }, + + "Computed-only with AtLeastOneOf": { + map[string]*Schema{ + "string_one": { + Type: TypeString, + Computed: true, + AtLeastOneOf: []string{ + "string_one", + "string_two", + }, + }, + "string_two": { + Type: TypeString, + Computed: true, + AtLeastOneOf: []string{ + "string_one", + "string_two", + }, + }, + }, + true, + }, + + "Computed-only with ConflictsWith": { + map[string]*Schema{ + "string_one": { + Type: TypeString, + Computed: true, + ConflictsWith: []string{ + "string_two", + }, + }, + "string_two": { + Type: TypeString, + Computed: true, + ConflictsWith: []string{ + "string_one", + }, + }, + }, + true, + }, + + "Computed-only with Default": { + map[string]*Schema{ + "string": { + Type: TypeString, + Computed: true, + Default: "test", + }, + }, + true, + }, + + "Computed-only with DefaultFunc": { + map[string]*Schema{ + "string": { + Type: TypeString, + Computed: true, + DefaultFunc: func() (interface{}, error) { return nil, nil }, + }, + }, + true, + }, + + "Computed-only with DiffSuppressFunc": { + map[string]*Schema{ + "string": { + Type: TypeString, + Computed: true, + DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool { return false }, + }, + }, + true, + }, + + "Computed-only with ExactlyOneOf": { + map[string]*Schema{ + "string_one": { + Type: TypeString, + Computed: true, + ExactlyOneOf: []string{ + "string_one", + "string_two", + }, + }, + "string_two": { + Type: TypeString, + Computed: true, + ExactlyOneOf: []string{ + "string_one", + "string_two", + }, + }, + }, + true, + }, + + "Computed-only with InputDefault": { + map[string]*Schema{ + "string": { + Type: TypeString, + Computed: true, + InputDefault: "test", + }, + }, + true, + }, + + "Computed-only with MaxItems": { + map[string]*Schema{ + "string": { + Type: TypeList, + Elem: &Schema{Type: TypeString}, + Computed: true, + MaxItems: 1, + }, + }, + true, + }, + + "Computed-only with MinItems": { + map[string]*Schema{ + "string": { + Type: TypeList, + Elem: &Schema{Type: TypeString}, + Computed: true, + MinItems: 1, + }, + }, + true, + }, + + "Computed-only with StateFunc": { + map[string]*Schema{ + "string": { + Type: TypeString, + Computed: true, + StateFunc: func(v interface{}) string { return "" }, + }, + }, + true, + }, + + "Computed-only with ValidateFunc": { + map[string]*Schema{ + "string": { + Type: TypeString, + Computed: true, + ValidateFunc: func(v interface{}, k string) ([]string, []error) { return nil, nil }, + }, + }, + true, + }, + + "invalid field name format #1": { + map[string]*Schema{ + "with space": { + Type: TypeString, + Optional: true, + }, + }, + true, + }, + + "invalid field name format #2": { + map[string]*Schema{ + "WithCapitals": { + Type: TypeString, + Optional: true, + }, + }, + true, + }, + + "invalid field name format of a Deprecated field": { + map[string]*Schema{ + "WithCapitals": { + Type: TypeString, + Optional: true, + Deprecated: "Use with_underscores instead", + }, + }, + false, + }, + + "ConfigModeBlock with Elem *Resource": { + map[string]*Schema{ + "block": { + Type: TypeList, + ConfigMode: SchemaConfigModeBlock, + Optional: true, + Elem: &Resource{}, + }, + }, + false, + }, + + "ConfigModeBlock Computed with Elem *Resource": { + map[string]*Schema{ + "block": { + Type: TypeList, + ConfigMode: SchemaConfigModeBlock, + Computed: true, + Elem: &Resource{}, + }, + }, + true, // ConfigMode of block cannot be used for computed schema + }, + + "ConfigModeBlock with Elem *Schema": { + map[string]*Schema{ + "block": { + Type: TypeList, + ConfigMode: SchemaConfigModeBlock, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + true, + }, + + "ConfigModeBlock with no Elem": { + map[string]*Schema{ + "block": { + Type: TypeString, + ConfigMode: SchemaConfigModeBlock, + Optional: true, + }, + }, + true, + }, + + "ConfigModeBlock inside ConfigModeAttr": { + map[string]*Schema{ + "block": { + Type: TypeList, + ConfigMode: SchemaConfigModeAttr, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "sub": { + Type: TypeList, + ConfigMode: SchemaConfigModeBlock, + Elem: &Resource{}, + }, + }, + }, + }, + }, + true, // ConfigMode of block cannot be used in child of schema with ConfigMode of attribute + }, + + "ConfigModeAuto with *Resource inside ConfigModeAttr": { + map[string]*Schema{ + "block": { + Type: TypeList, + ConfigMode: SchemaConfigModeAttr, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "sub": { + Type: TypeList, + Elem: &Resource{}, + }, + }, + }, + }, + }, + true, // in *schema.Resource with ConfigMode of attribute, so must also have ConfigMode of attribute + }, + + "TypeMap with Elem *Resource": { + map[string]*Schema{ + "map": { + Type: TypeMap, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "keynothandled": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + }, + true, + }, + + "ValidateFunc and ValidateDiagFunc cannot both be set": { + map[string]*Schema{ + "foo": { + Type: TypeInt, + Required: true, + ValidateFunc: func(interface{}, string) ([]string, []error) { + return nil, nil + }, + ValidateDiagFunc: func(interface{}, cty.Path) diag.Diagnostics { + return nil + }, + }, + }, + true, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + err := schemaMap(tc.In).InternalValidate(nil) + if err != nil != tc.Err { + if tc.Err { + t.Fatalf("%q: Expected error did not occur:\n\n%#v", tn, tc.In) + } + t.Fatalf("%q: Unexpected error occurred: %s\n\n%#v", tn, err, tc.In) + } + }) + } + +} + +func TestSchemaMap_DiffSuppress(t *testing.T) { + cases := map[string]struct { + Schema map[string]*Schema + State *terraform.InstanceState + Config map[string]interface{} + ExpectedDiff *terraform.InstanceDiff + Err bool + }{ + "#0 - Suppress otherwise valid diff by returning true": { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool { + // Always suppress any diff + return true + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + ExpectedDiff: nil, + + Err: false, + }, + + "#1 - Don't suppress diff by returning false": { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool { + // Always suppress any diff + return false + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + ExpectedDiff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + }, + }, + }, + + Err: false, + }, + + "Default with suppress makes no diff": { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Default: "foo", + DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool { + return true + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + ExpectedDiff: nil, + + Err: false, + }, + + "Default with false suppress makes diff": { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Default: "foo", + DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool { + return false + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + ExpectedDiff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + }, + }, + }, + + Err: false, + }, + + "Complex structure with set of computed string should mark root set as computed": { + Schema: map[string]*Schema{ + "outer": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "outer_str": { + Type: TypeString, + Optional: true, + }, + "inner": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "inner_str": { + Type: TypeString, + Optional: true, + }, + }, + }, + Set: func(v interface{}) int { + return 2 + }, + }, + }, + }, + Set: func(v interface{}) int { + return 1 + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "outer": []interface{}{ + map[string]interface{}{ + "outer_str": "foo", + "inner": []interface{}{ + map[string]interface{}{ + "inner_str": hcl2shim.UnknownVariableValue, + }, + }, + }, + }, + }, + + ExpectedDiff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "outer.#": { + Old: "0", + New: "1", + }, + "outer.~1.outer_str": { + Old: "", + New: "foo", + }, + "outer.~1.inner.#": { + Old: "0", + New: "1", + }, + "outer.~1.inner.~2.inner_str": { + Old: "", + New: hcl2shim.UnknownVariableValue, + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + "Complex structure with complex list of computed string should mark root set as computed": { + Schema: map[string]*Schema{ + "outer": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "outer_str": { + Type: TypeString, + Optional: true, + }, + "inner": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "inner_str": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + }, + }, + Set: func(v interface{}) int { + return 1 + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "outer": []interface{}{ + map[string]interface{}{ + "outer_str": "foo", + "inner": []interface{}{ + map[string]interface{}{ + "inner_str": hcl2shim.UnknownVariableValue, + }, + }, + }, + }, + }, + + ExpectedDiff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "outer.#": { + Old: "0", + New: "1", + }, + "outer.~1.outer_str": { + Old: "", + New: "foo", + }, + "outer.~1.inner.#": { + Old: "0", + New: "1", + }, + "outer.~1.inner.0.inner_str": { + Old: "", + New: hcl2shim.UnknownVariableValue, + NewComputed: true, + }, + }, + }, + + Err: false, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + c := terraform.NewResourceConfigRaw(tc.Config) + + d, err := schemaMap(tc.Schema).Diff(context.Background(), tc.State, c, nil, nil, true) + if err != nil != tc.Err { + t.Fatalf("#%q err: %s", tn, err) + } + + if !reflect.DeepEqual(tc.ExpectedDiff, d) { + t.Fatalf("#%q:\n\nexpected:\n%#v\n\ngot:\n%#v", tn, tc.ExpectedDiff, d) + } + }) + } +} + +func TestSchemaMap_Validate(t *testing.T) { + cases := map[string]struct { + Schema map[string]*Schema + Config map[string]interface{} + Err bool + Errors []error + Warnings []string + }{ + "Good": { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + }, + + "Good, because the var is not set and that error will come elsewhere": { + Schema: map[string]*Schema{ + "size": { + Type: TypeInt, + Required: true, + }, + }, + + Config: map[string]interface{}{ + "size": hcl2shim.UnknownVariableValue, + }, + }, + + "Required field not set": { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Required: true, + }, + }, + + Config: map[string]interface{}{}, + + Err: true, + }, + + "Invalid basic type": { + Schema: map[string]*Schema{ + "port": { + Type: TypeInt, + Required: true, + }, + }, + + Config: map[string]interface{}{ + "port": "I am invalid", + }, + + Err: true, + }, + + "Invalid complex type": { + Schema: map[string]*Schema{ + "user_data": { + Type: TypeString, + Optional: true, + }, + }, + + Config: map[string]interface{}{ + "user_data": []interface{}{ + map[string]interface{}{ + "foo": "bar", + }, + }, + }, + + Err: true, + }, + + "Bad type": { + Schema: map[string]*Schema{ + "size": { + Type: TypeInt, + Required: true, + }, + }, + + Config: map[string]interface{}{ + "size": "nope", + }, + + Err: true, + }, + + "Required but has DefaultFunc": { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Required: true, + DefaultFunc: func() (interface{}, error) { + return "foo", nil + }, + }, + }, + + Config: nil, + }, + + "Required but has DefaultFunc return nil": { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Required: true, + DefaultFunc: func() (interface{}, error) { + return nil, nil + }, + }, + }, + + Config: nil, + + Err: true, + }, + + "Optional sub-resource": { + Schema: map[string]*Schema{ + "ingress": { + Type: TypeList, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": { + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + Config: map[string]interface{}{}, + + Err: false, + }, + + "Sub-resource is the wrong type": { + Schema: map[string]*Schema{ + "ingress": { + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": { + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + Config: map[string]interface{}{ + "ingress": []interface{}{"foo"}, + }, + + Err: true, + }, + + "Not a list nested block": { + Schema: map[string]*Schema{ + "ingress": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": { + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + Config: map[string]interface{}{ + "ingress": "foo", + }, + + Err: true, + Errors: []error{ + fmt.Errorf("Error: Attribute must be a list"), + }, + }, + + "Not a list primitive": { + Schema: map[string]*Schema{ + "strings": { + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + + Config: map[string]interface{}{ + "strings": "foo", + }, + + Err: true, + Errors: []error{ + fmt.Errorf("Error: Attribute must be a list"), + }, + }, + + "Unknown list": { + Schema: map[string]*Schema{ + "strings": { + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + + Config: map[string]interface{}{ + "strings": hcl2shim.UnknownVariableValue, + }, + + Err: false, + }, + + "Unknown + Deprecation": { + Schema: map[string]*Schema{ + "old_news": { + Type: TypeString, + Optional: true, + Deprecated: "please use 'new_news' instead", + }, + }, + + Config: map[string]interface{}{ + "old_news": hcl2shim.UnknownVariableValue, + }, + + Warnings: []string{ + "Warning: Argument is deprecated: please use 'new_news' instead", + }, + }, + + "Required sub-resource field": { + Schema: map[string]*Schema{ + "ingress": { + Type: TypeList, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": { + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + Config: map[string]interface{}{ + "ingress": []interface{}{ + map[string]interface{}{}, + }, + }, + + Err: true, + }, + + "Good sub-resource": { + Schema: map[string]*Schema{ + "ingress": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": { + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + Config: map[string]interface{}{ + "ingress": []interface{}{ + map[string]interface{}{ + "from": 80, + }, + }, + }, + + Err: false, + }, + + "Good sub-resource, computed value": { + Schema: map[string]*Schema{ + "ingress": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": { + Type: TypeInt, + Optional: true, + }, + }, + }, + }, + }, + + Config: map[string]interface{}{ + "ingress": []interface{}{ + map[string]interface{}{ + "from": hcl2shim.UnknownVariableValue, + }, + }, + }, + + Err: false, + }, + + "Invalid/unknown field": { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + Config: map[string]interface{}{ + "foo": "bar", + }, + + Err: true, + }, + + "Invalid/unknown field with computed value": { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + Config: map[string]interface{}{ + "foo": hcl2shim.UnknownVariableValue, + }, + + Err: true, + }, + + "Computed field set": { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Computed: true, + }, + }, + + Config: map[string]interface{}{ + "availability_zone": "bar", + }, + + Err: true, + }, + + "Not a set": { + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + Config: map[string]interface{}{ + "ports": "foo", + }, + + Err: true, + }, + + "Maps": { + Schema: map[string]*Schema{ + "user_data": { + Type: TypeMap, + Optional: true, + }, + }, + + Config: map[string]interface{}{ + "user_data": "foo", + }, + + Err: true, + }, + + "Good map: data surrounded by extra slice": { + Schema: map[string]*Schema{ + "user_data": { + Type: TypeMap, + Optional: true, + }, + }, + + Config: map[string]interface{}{ + "user_data": []interface{}{ + map[string]interface{}{ + "foo": "bar", + }, + }, + }, + }, + + "Good map": { + Schema: map[string]*Schema{ + "user_data": { + Type: TypeMap, + Optional: true, + }, + }, + + Config: map[string]interface{}{ + "user_data": map[string]interface{}{ + "foo": "bar", + }, + }, + }, + + "Map with type specified as value type": { + Schema: map[string]*Schema{ + "user_data": { + Type: TypeMap, + Optional: true, + Elem: TypeBool, + }, + }, + + Config: map[string]interface{}{ + "user_data": map[string]interface{}{ + "foo": "not_a_bool", + }, + }, + + Err: true, + }, + + "Map with type specified as nested Schema": { + Schema: map[string]*Schema{ + "user_data": { + Type: TypeMap, + Optional: true, + Elem: &Schema{Type: TypeBool}, + }, + }, + + Config: map[string]interface{}{ + "user_data": map[string]interface{}{ + "foo": "not_a_bool", + }, + }, + + Err: true, + }, + + "Bad map: just a slice": { + Schema: map[string]*Schema{ + "user_data": { + Type: TypeMap, + Optional: true, + }, + }, + + Config: map[string]interface{}{ + "user_data": []interface{}{ + "foo", + }, + }, + + Err: true, + }, + + "Good set: config has slice with single interpolated value": { + Schema: map[string]*Schema{ + "security_groups": { + Type: TypeSet, + Optional: true, + Computed: true, + ForceNew: true, + Elem: &Schema{Type: TypeString}, + Set: func(v interface{}) int { + return len(v.(string)) + }, + }, + }, + + Config: map[string]interface{}{ + "security_groups": []interface{}{"${var.foo}"}, + }, + + Err: false, + }, + + "Bad set: config has single interpolated value": { + Schema: map[string]*Schema{ + "security_groups": { + Type: TypeSet, + Optional: true, + Computed: true, + ForceNew: true, + Elem: &Schema{Type: TypeString}, + }, + }, + + Config: map[string]interface{}{ + "security_groups": "${var.foo}", + }, + + Err: true, + }, + + "Bad, subresource should not allow unknown elements": { + Schema: map[string]*Schema{ + "ingress": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "port": { + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + Config: map[string]interface{}{ + "ingress": []interface{}{ + map[string]interface{}{ + "port": 80, + "other": "yes", + }, + }, + }, + + Err: true, + }, + + "Bad, subresource should not allow invalid types": { + Schema: map[string]*Schema{ + "ingress": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "port": { + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + Config: map[string]interface{}{ + "ingress": []interface{}{ + map[string]interface{}{ + "port": "bad", + }, + }, + }, + + Err: true, + }, + + "Bad, should not allow lists to be assigned to string attributes": { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Required: true, + }, + }, + + Config: map[string]interface{}{ + "availability_zone": []interface{}{"foo", "bar", "baz"}, + }, + + Err: true, + }, + + "Bad, should not allow maps to be assigned to string attributes": { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Required: true, + }, + }, + + Config: map[string]interface{}{ + "availability_zone": map[string]interface{}{"foo": "bar", "baz": "thing"}, + }, + + Err: true, + }, + + "Deprecated attribute usage generates warning, but not error": { + Schema: map[string]*Schema{ + "old_news": { + Type: TypeString, + Optional: true, + Deprecated: "please use 'new_news' instead", + }, + }, + + Config: map[string]interface{}{ + "old_news": "extra extra!", + }, + + Err: false, + + Warnings: []string{ + "Warning: Argument is deprecated: please use 'new_news' instead", + }, + }, + + "Deprecated generates no warnings if attr not used": { + Schema: map[string]*Schema{ + "old_news": { + Type: TypeString, + Optional: true, + Deprecated: "please use 'new_news' instead", + }, + }, + + Err: false, + + Warnings: nil, + }, + + "Conflicting attributes generate error": { + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeString, + Optional: true, + }, + "blacklist": { + Type: TypeString, + Optional: true, + ConflictsWith: []string{"whitelist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": "white-val", + "blacklist": "black-val", + }, + + Err: true, + Errors: []error{ + fmt.Errorf(`Error: Conflicting configuration arguments: "blacklist": conflicts with whitelist`), + }, + }, + + "Conflicting attributes okay when unknown 1": { + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeString, + Optional: true, + }, + "blacklist": { + Type: TypeString, + Optional: true, + ConflictsWith: []string{"whitelist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": "white-val", + "blacklist": hcl2shim.UnknownVariableValue, + }, + + Err: false, + }, + + "Conflicting list attributes okay when unknown 1": { + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "blacklist": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeString}, + ConflictsWith: []string{"whitelist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": []interface{}{"white-val"}, + "blacklist": []interface{}{hcl2shim.UnknownVariableValue}, + }, + + Err: false, + }, + + "Conflicting attributes okay when unknown 2": { + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeString, + Optional: true, + }, + "blacklist": { + Type: TypeString, + Optional: true, + ConflictsWith: []string{"whitelist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": hcl2shim.UnknownVariableValue, + "blacklist": "black-val", + }, + + Err: false, + }, + + "Conflicting attributes generate error even if one is unknown": { + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeString, + Optional: true, + ConflictsWith: []string{"blacklist", "greenlist"}, + }, + "blacklist": { + Type: TypeString, + Optional: true, + ConflictsWith: []string{"whitelist", "greenlist"}, + }, + "greenlist": { + Type: TypeString, + Optional: true, + ConflictsWith: []string{"whitelist", "blacklist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": hcl2shim.UnknownVariableValue, + "blacklist": "black-val", + "greenlist": "green-val", + }, + + Err: true, + Errors: []error{ + fmt.Errorf(`Error: Conflicting configuration arguments: "blacklist": conflicts with greenlist`), + fmt.Errorf(`Error: Conflicting configuration arguments: "greenlist": conflicts with blacklist`), + }, + }, + + "Required attribute & undefined conflicting optional are good": { + Schema: map[string]*Schema{ + "required_att": { + Type: TypeString, + Required: true, + }, + "optional_att": { + Type: TypeString, + Optional: true, + ConflictsWith: []string{"required_att"}, + }, + }, + + Config: map[string]interface{}{ + "required_att": "required-val", + }, + + Err: false, + }, + + "Required conflicting attribute & defined optional generate error": { + Schema: map[string]*Schema{ + "required_att": { + Type: TypeString, + Required: true, + }, + "optional_att": { + Type: TypeString, + Optional: true, + ConflictsWith: []string{"required_att"}, + }, + }, + + Config: map[string]interface{}{ + "required_att": "required-val", + "optional_att": "optional-val", + }, + + Err: true, + Errors: []error{ + fmt.Errorf(`Error: Conflicting configuration arguments: "optional_att": conflicts with required_att`), + }, + }, + + "Computed + Optional fields conflicting with each other": { + Schema: map[string]*Schema{ + "foo_att": { + Type: TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"bar_att"}, + }, + "bar_att": { + Type: TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"foo_att"}, + }, + }, + + Config: map[string]interface{}{ + "foo_att": "foo-val", + "bar_att": "bar-val", + }, + + Err: true, + Errors: []error{ + fmt.Errorf(`Error: Conflicting configuration arguments: "bar_att": conflicts with foo_att`), + fmt.Errorf(`Error: Conflicting configuration arguments: "foo_att": conflicts with bar_att`), + }, + }, + + "Computed + Optional fields NOT conflicting with each other": { + Schema: map[string]*Schema{ + "foo_att": { + Type: TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"bar_att"}, + }, + "bar_att": { + Type: TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"foo_att"}, + }, + }, + + Config: map[string]interface{}{ + "foo_att": "foo-val", + }, + + Err: false, + }, + + "Computed + Optional fields that conflict with none set": { + Schema: map[string]*Schema{ + "foo_att": { + Type: TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"bar_att"}, + }, + "bar_att": { + Type: TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"foo_att"}, + }, + }, + + Config: map[string]interface{}{}, + + Err: false, + }, + + "Good with ValidateFunc": { + Schema: map[string]*Schema{ + "validate_me": { + Type: TypeString, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + return + }, + }, + }, + Config: map[string]interface{}{ + "validate_me": "valid", + }, + Err: false, + }, + + "Bad with ValidateFunc": { + Schema: map[string]*Schema{ + "validate_me": { + Type: TypeString, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + es = append(es, fmt.Errorf("something is not right here")) + return + }, + }, + }, + Config: map[string]interface{}{ + "validate_me": "invalid", + }, + Err: true, + Errors: []error{ + fmt.Errorf(`Error: something is not right here`), + }, + }, + + "ValidateFunc not called when type does not match": { + Schema: map[string]*Schema{ + "number": { + Type: TypeInt, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + t.Fatalf("Should not have gotten validate call") + return + }, + }, + }, + Config: map[string]interface{}{ + "number": "NaN", + }, + Err: true, + }, + + "ValidateFunc gets decoded type": { + Schema: map[string]*Schema{ + "maybe": { + Type: TypeBool, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + if _, ok := v.(bool); !ok { + t.Fatalf("Expected bool, got: %#v", v) + } + return + }, + }, + }, + Config: map[string]interface{}{ + "maybe": "true", + }, + }, + + "ValidateFunc is not called with a computed value": { + Schema: map[string]*Schema{ + "validate_me": { + Type: TypeString, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + es = append(es, fmt.Errorf("something is not right here")) + return + }, + }, + }, + Config: map[string]interface{}{ + "validate_me": hcl2shim.UnknownVariableValue, + }, + + Err: false, + }, + + "special timeouts field": { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + Config: map[string]interface{}{ + TimeoutsConfigKey: "bar", + }, + + Err: false, + }, + + "invalid bool field": { + Schema: map[string]*Schema{ + "bool_field": { + Type: TypeBool, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "bool_field": "abcdef", + }, + Err: true, + }, + "invalid integer field": { + Schema: map[string]*Schema{ + "integer_field": { + Type: TypeInt, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "integer_field": "abcdef", + }, + Err: true, + }, + "invalid float field": { + Schema: map[string]*Schema{ + "float_field": { + Type: TypeFloat, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "float_field": "abcdef", + }, + Err: true, + }, + + // Invalid map values + "invalid bool map value": { + Schema: map[string]*Schema{ + "boolMap": { + Type: TypeMap, + Elem: TypeBool, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "boolMap": map[string]interface{}{ + "boolField": "notbool", + }, + }, + Err: true, + }, + "invalid int map value": { + Schema: map[string]*Schema{ + "intMap": { + Type: TypeMap, + Elem: TypeInt, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "intMap": map[string]interface{}{ + "intField": "notInt", + }, + }, + Err: true, + }, + "invalid float map value": { + Schema: map[string]*Schema{ + "floatMap": { + Type: TypeMap, + Elem: TypeFloat, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "floatMap": map[string]interface{}{ + "floatField": "notFloat", + }, + }, + Err: true, + }, + + "map with positive validate function": { + Schema: map[string]*Schema{ + "floatInt": { + Type: TypeMap, + Elem: TypeInt, + Optional: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + return + }, + }, + }, + Config: map[string]interface{}{ + "floatInt": map[string]interface{}{ + "rightAnswer": "42", + "tooMuch": "43", + }, + }, + Err: false, + }, + "map with negative validate function": { + Schema: map[string]*Schema{ + "floatInt": { + Type: TypeMap, + Elem: TypeInt, + Optional: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + es = append(es, fmt.Errorf("this is not fine")) + return + }, + }, + }, + Config: map[string]interface{}{ + "floatInt": map[string]interface{}{ + "rightAnswer": "42", + "tooMuch": "43", + }, + }, + Err: true, + }, + + // The Validation function should not see interpolation strings from + // non-computed values. + "set with partially computed list and map": { + Schema: map[string]*Schema{ + "outer": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "list": { + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + if strings.HasPrefix(v.(string), "${") { + es = append(es, fmt.Errorf("should not have interpolations")) + } + return + }, + }, + }, + }, + }, + }, + }, + Config: map[string]interface{}{ + "outer": []interface{}{ + map[string]interface{}{ + "list": []interface{}{"A", hcl2shim.UnknownVariableValue, "c"}, + }, + }, + }, + Err: false, + }, + "unexpected nils values": { + Schema: map[string]*Schema{ + "strings": { + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + "block": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "int": { + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + Config: map[string]interface{}{ + "strings": []interface{}{"1", nil}, + "block": []interface{}{map[string]interface{}{ + "int": nil, + }, + nil, + }, + }, + Err: true, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + c := terraform.NewResourceConfigRaw(tc.Config) + + diags := schemaMap(tc.Schema).Validate(c) + if diags.HasError() != tc.Err { + if !diags.HasError() { + t.Errorf("%q: no errors", tn) + } + + for _, e := range diagutils.ErrorDiags(diags).Errors() { + t.Errorf("%q: err: %s", tn, e) + } + + t.FailNow() + } + + ws := diagutils.WarningDiags(diags).Warnings() + if !reflect.DeepEqual(ws, tc.Warnings) { + t.Fatalf("%q: warnings:\n\ngot: %#v\nwant: %#v", tn, ws, tc.Warnings) + } + + es := diagutils.ErrorDiags(diags).Errors() + if tc.Errors != nil { + sort.Sort(errorSort(es)) + sort.Sort(errorSort(tc.Errors)) + + if !errorEquals(es, tc.Errors) { + t.Fatalf("%q: errors:\n\ngot: %q\nwant: %q", tn, es, tc.Errors) + } + } + }) + + } +} + +func errorEquals(a []error, b []error) bool { + if len(a) != len(b) { + return false + } + for i := 0; i < len(a); i++ { + if a[i].Error() != b[i].Error() { + return false + } + } + return true +} + +func TestSchemaSet_ValidateMaxItems(t *testing.T) { + cases := map[string]struct { + Schema map[string]*Schema + State *terraform.InstanceState + Config map[string]interface{} + ConfigVariables map[string]string + Diff *terraform.InstanceDiff + Err bool + Errors []error + }{ + "#0": { + Schema: map[string]*Schema{ + "aliases": { + Type: TypeSet, + Optional: true, + MaxItems: 1, + Elem: &Schema{Type: TypeString}, + }, + }, + State: nil, + Config: map[string]interface{}{ + "aliases": []interface{}{"foo", "bar"}, + }, + Diff: nil, + Err: true, + Errors: []error{ + fmt.Errorf("Error: Too many list items: Attribute supports 1 item maximum, but config has 2 declared."), + }, + }, + "#1": { + Schema: map[string]*Schema{ + "aliases": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + }, + State: nil, + Config: map[string]interface{}{ + "aliases": []interface{}{"foo", "bar"}, + }, + Diff: nil, + Err: false, + Errors: nil, + }, + "#2": { + Schema: map[string]*Schema{ + "aliases": { + Type: TypeSet, + Optional: true, + MaxItems: 1, + Elem: &Schema{Type: TypeString}, + }, + }, + State: nil, + Config: map[string]interface{}{ + "aliases": []interface{}{"foo"}, + }, + Diff: nil, + Err: false, + Errors: nil, + }, + } + + for tn, tc := range cases { + c := terraform.NewResourceConfigRaw(tc.Config) + diags := schemaMap(tc.Schema).Validate(c) + + if diags.HasError() != tc.Err { + if !diags.HasError() { + t.Errorf("%q: no errors", tn) + } + + for _, e := range diagutils.ErrorDiags(diags).Errors() { + t.Errorf("%q: err: %s", tn, e) + } + + t.FailNow() + } + + es := diagutils.ErrorDiags(diags).Errors() + if tc.Errors != nil { + if !errorEquals(es, tc.Errors) { + t.Fatalf("%q: expected: %q\ngot: %q", tn, tc.Errors, es) + } + } + } +} + +func TestSchemaSet_ValidateMinItems(t *testing.T) { + cases := map[string]struct { + Schema map[string]*Schema + State *terraform.InstanceState + Config map[string]interface{} + ConfigVariables map[string]string + Diff *terraform.InstanceDiff + Err bool + Errors []error + }{ + "#0": { + Schema: map[string]*Schema{ + "aliases": { + Type: TypeSet, + Optional: true, + MinItems: 2, + Elem: &Schema{Type: TypeString}, + }, + }, + State: nil, + Config: map[string]interface{}{ + "aliases": []interface{}{"foo", "bar"}, + }, + Diff: nil, + Err: false, + Errors: nil, + }, + "#1": { + Schema: map[string]*Schema{ + "aliases": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + }, + State: nil, + Config: map[string]interface{}{ + "aliases": []interface{}{"foo", "bar"}, + }, + Diff: nil, + Err: false, + Errors: nil, + }, + "#2": { + Schema: map[string]*Schema{ + "aliases": { + Type: TypeSet, + Optional: true, + MinItems: 2, + Elem: &Schema{Type: TypeString}, + }, + }, + State: nil, + Config: map[string]interface{}{ + "aliases": []interface{}{"foo"}, + }, + Diff: nil, + Err: true, + Errors: []error{ + fmt.Errorf("Error: Not enough list items: Attribute requires 2 item minimum, but config has only 1 declared."), + }, + }, + } + + for tn, tc := range cases { + c := terraform.NewResourceConfigRaw(tc.Config) + diags := schemaMap(tc.Schema).Validate(c) + + if diags.HasError() != tc.Err { + if !diags.HasError() { + t.Errorf("%q: no errors", tn) + } + + for _, e := range diagutils.ErrorDiags(diags).Errors() { + t.Errorf("%q: err: %s", tn, e) + } + + t.FailNow() + } + + es := diagutils.ErrorDiags(diags).Errors() + if tc.Errors != nil { + if !errorEquals(es, tc.Errors) { + t.Fatalf("%q: wrong errors\ngot: %q\nwant: %q", tn, es, tc.Errors) + } + } + } +} + +// errorSort implements sort.Interface to sort errors by their error message +type errorSort []error + +func (e errorSort) Len() int { return len(e) } +func (e errorSort) Swap(i, j int) { e[i], e[j] = e[j], e[i] } +func (e errorSort) Less(i, j int) bool { + return e[i].Error() < e[j].Error() +} + +func TestSchemaMapDeepCopy(t *testing.T) { + schema := map[string]*Schema{ + "foo": { + Type: TypeString, + }, + } + source := schemaMap(schema) + dest := source.DeepCopy() + dest["foo"].ForceNew = true + if reflect.DeepEqual(source, dest) { + t.Fatalf("source and dest should not match") + } +} + +func TestValidateConflictingAttributes(t *testing.T) { + cases := map[string]struct { + Key string + Schema *Schema + Config map[string]interface{} + Err bool + }{ + "root attribute self conflicting": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"self"}, + }, + Config: map[string]interface{}{ + "self": true, + }, + Err: true, + }, + + "root attribute conflicting unconfigured self unconfigured": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"root_attr"}, + }, + Config: map[string]interface{}{}, + Err: false, + }, + + "root attribute conflicting unconfigured self unknown": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"root_attr"}, + }, + Config: map[string]interface{}{ + "self": hcl2shim.UnknownVariableValue, + }, + Err: false, + }, + + "root attribute conflicting unconfigured self known": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"root_attr"}, + }, + Config: map[string]interface{}{ + "self": true, + }, + Err: false, + }, + + "root attribute conflicting unknown self unconfigured": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"root_attr"}, + }, + Config: map[string]interface{}{ + "root_attr": hcl2shim.UnknownVariableValue, + }, + Err: false, + }, + + "root attribute conflicting unknown self unknown": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"root_attr"}, + }, + Config: map[string]interface{}{ + "root_attr": hcl2shim.UnknownVariableValue, + "self": hcl2shim.UnknownVariableValue, + }, + Err: false, + }, + + "root attribute conflicting unknown self known": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"root_attr"}, + }, + Config: map[string]interface{}{ + "root_attr": hcl2shim.UnknownVariableValue, + "self": true, + }, + Err: false, + }, + + "root attribute conflicting known self unconfigured": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"root_attr"}, + }, + Config: map[string]interface{}{ + "root_attr": true, + }, + Err: true, + }, + + "root attribute conflicting known self unknown": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"root_attr"}, + }, + Config: map[string]interface{}{ + "root_attr": true, + "self": hcl2shim.UnknownVariableValue, + }, + Err: true, + }, + + "root attribute conflicting known self known": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"root_attr"}, + }, + Config: map[string]interface{}{ + "root_attr": true, + "self": true, + }, + Err: true, + }, + + "configuration block attribute list index syntax self conflicting": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.0.self"}, + }, + Config: map[string]interface{}{ + "config_block_attr": []interface{}{ + map[string]interface{}{ + "self": true, + }, + }, + }, + Err: true, + }, + + "configuration block attribute list index syntax conflicting unconfigured self unconfigured": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.0.nested_attr"}, + }, + Config: map[string]interface{}{}, + Err: false, + }, + + "configuration block attribute list index syntax conflicting unconfigured self unknown": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.0.nested_attr"}, + }, + Config: map[string]interface{}{ + "config_block_attr": []interface{}{ + map[string]interface{}{ + "self": hcl2shim.UnknownVariableValue, + }, + }, + }, + Err: false, + }, + + "configuration block attribute list index syntax conflicting unconfigured self known": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.0.nested_attr"}, + }, + Config: map[string]interface{}{ + "config_block_attr": []interface{}{ + map[string]interface{}{ + "self": true, + }, + }, + }, + Err: false, + }, + + "configuration block attribute list index syntax conflicting unknown self unconfigured": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.0.nested_attr"}, + }, + Config: map[string]interface{}{ + "config_block_attr": []interface{}{ + map[string]interface{}{ + "nested_attr": hcl2shim.UnknownVariableValue, + }, + }, + }, + Err: false, + }, + + "configuration block attribute list index syntax conflicting unknown self unknown": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.0.nested_attr"}, + }, + Config: map[string]interface{}{ + "config_block_attr": []interface{}{ + map[string]interface{}{ + "nested_attr": hcl2shim.UnknownVariableValue, + "self": hcl2shim.UnknownVariableValue, + }, + }, + }, + Err: false, + }, + + "configuration block attribute list index syntax conflicting unknown self known": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.0.nested_attr"}, + }, + Config: map[string]interface{}{ + "config_block_attr": []interface{}{ + map[string]interface{}{ + "nested_attr": hcl2shim.UnknownVariableValue, + "self": true, + }, + }, + }, + Err: false, + }, + + "configuration block attribute list index syntax conflicting known self unconfigured": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.0.nested_attr"}, + }, + Config: map[string]interface{}{ + "config_block_attr": []interface{}{ + map[string]interface{}{ + "nested_attr": true, + }, + }, + }, + Err: true, + }, + + "configuration block attribute list index syntax conflicting known self unknown": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.0.nested_attr"}, + }, + Config: map[string]interface{}{ + "config_block_attr": []interface{}{ + map[string]interface{}{ + "nested_attr": true, + "self": hcl2shim.UnknownVariableValue, + }, + }, + }, + Err: true, + }, + + "configuration block attribute list index syntax conflicting known self known": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.0.nested_attr"}, + }, + Config: map[string]interface{}{ + "config_block_attr": []interface{}{ + map[string]interface{}{ + "nested_attr": true, + "self": true, + }, + }, + }, + Err: true, + }, + + "configuration block attribute map key syntax self conflicting": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.self"}, + }, + Config: map[string]interface{}{ + "config_block_attr": []interface{}{ + map[string]interface{}{ + "self": true, + }, + }, + }, + Err: false, + }, + + "configuration block attribute map key syntax conflicting known self unconfigured": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.nested_attr"}, + }, + Config: map[string]interface{}{ + "config_block_attr": []interface{}{ + map[string]interface{}{ + "nested_attr": true, + }, + }, + }, + Err: false, + }, + + "configuration block attribute map key syntax conflicting known self unknown": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.nested_attr"}, + }, + Config: map[string]interface{}{ + "config_block_attr": []interface{}{ + map[string]interface{}{ + "nested_attr": true, + "self": hcl2shim.UnknownVariableValue, + }, + }, + }, + Err: false, + }, + + "configuration block attribute map key syntax conflicting known self known": { + Key: "self", + Schema: &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"config_block_attr.nested_attr"}, + }, + Config: map[string]interface{}{ + "config_block_attr": []interface{}{ + map[string]interface{}{ + "nested_attr": true, + "self": true, + }, + }, + }, + Err: false, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + c := terraform.NewResourceConfigRaw(tc.Config) + + err := validateConflictingAttributes(tc.Key, tc.Schema, c) + if err == nil && tc.Err { + t.Fatalf("expected error") + } + + if err != nil && !tc.Err { + t.Fatalf("didn't expect error, got error: %+v", err) + } + }) + } + +} + +func TestValidateExactlyOneOfAttributes(t *testing.T) { + cases := map[string]struct { + Key string + Schema map[string]*Schema + Config map[string]interface{} + Err bool + }{ + + "two attributes specified": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"blacklist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"whitelist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": true, + "blacklist": true, + }, + Err: true, + }, + + "one attributes specified": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"blacklist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"whitelist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": true, + }, + Err: false, + }, + + "two attributes of three specified": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"blacklist", "purplelist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"whitelist", "purplelist"}, + }, + "purplelist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"whitelist", "blacklist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": true, + "purplelist": true, + }, + Err: true, + }, + + "one attributes of three specified": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"blacklist", "purplelist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"whitelist", "purplelist"}, + }, + "purplelist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"whitelist", "blacklist"}, + }, + }, + + Config: map[string]interface{}{ + "purplelist": true, + }, + Err: false, + }, + + "no attributes of three specified": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"blacklist", "purplelist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"whitelist", "purplelist"}, + }, + "purplelist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"whitelist", "blacklist"}, + }, + }, + + Config: map[string]interface{}{}, + Err: true, + }, + + "Only Unknown Variable Value": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"blacklist", "purplelist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"whitelist", "purplelist"}, + }, + "purplelist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"whitelist", "blacklist"}, + }, + }, + + Config: map[string]interface{}{ + "purplelist": hcl2shim.UnknownVariableValue, + }, + Err: false, + }, + + "Unknown Variable Value and Known Value": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"blacklist", "purplelist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"whitelist", "purplelist"}, + }, + "purplelist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"whitelist", "blacklist"}, + }, + }, + + Config: map[string]interface{}{ + "purplelist": hcl2shim.UnknownVariableValue, + "whitelist": true, + }, + Err: false, + }, + + "Unknown Variable Value and 2 Known Value": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"blacklist", "purplelist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"whitelist", "purplelist"}, + }, + "purplelist": { + Type: TypeBool, + Optional: true, + ExactlyOneOf: []string{"whitelist", "blacklist"}, + }, + }, + + Config: map[string]interface{}{ + "purplelist": hcl2shim.UnknownVariableValue, + "whitelist": true, + "blacklist": true, + }, + Err: true, + }, + + "unknown list values": { + Key: "allow", + Schema: map[string]*Schema{ + "allow": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "protocol": { + Type: TypeString, + Required: true, + }, + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + }, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "deny": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "protocol": { + Type: TypeString, + Required: true, + }, + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + }, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "purplelist": { + Type: TypeString, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "allow": []interface{}{map[string]interface{}{ + "ports": hcl2shim.UnknownVariableValue, + "protocol": hcl2shim.UnknownVariableValue, + }}, + "deny": []interface{}{map[string]interface{}{ + "ports": hcl2shim.UnknownVariableValue, + "protocol": hcl2shim.UnknownVariableValue, + }}, + "purplelist": "blah", + }, + Err: false, + }, + + // This should probably fail, but we let it pass and rely on 2nd + // validation phase when unknowns become known, which will then fail. + "partially known list values": { + Key: "allow", + Schema: map[string]*Schema{ + "allow": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "protocol": { + Type: TypeString, + Required: true, + }, + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + }, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "deny": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "protocol": { + Type: TypeString, + Required: true, + }, + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + }, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "purplelist": { + Type: TypeString, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "allow": []interface{}{map[string]interface{}{ + "ports": hcl2shim.UnknownVariableValue, + "protocol": "TCP", + }}, + "deny": []interface{}{map[string]interface{}{ + "ports": hcl2shim.UnknownVariableValue, + "protocol": "TCP", + }}, + "purplelist": "blah", + }, + Err: false, + }, + + "known list values": { + Key: "allow", + Schema: map[string]*Schema{ + "allow": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "protocol": { + Type: TypeString, + Required: true, + }, + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + }, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "deny": { + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "protocol": { + Type: TypeString, + Required: true, + }, + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + }, + ExactlyOneOf: []string{"allow", "deny"}, + }, + }, + Config: map[string]interface{}{ + "allow": []interface{}{map[string]interface{}{ + "ports": []interface{}{"80"}, + "protocol": "TCP", + }}, + "deny": []interface{}{map[string]interface{}{ + "ports": []interface{}{"80"}, + "protocol": "TCP", + }}, + }, + Err: true, + }, + + "wholly unknown set values": { + Key: "allow", + Schema: map[string]*Schema{ + "allow": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "protocol": { + Type: TypeString, + Required: true, + }, + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + }, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "deny": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "protocol": { + Type: TypeString, + Required: true, + }, + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + }, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "purplelist": { + Type: TypeString, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "allow": []interface{}{map[string]interface{}{ + "ports": hcl2shim.UnknownVariableValue, + "protocol": hcl2shim.UnknownVariableValue, + }}, + "deny": []interface{}{map[string]interface{}{ + "ports": hcl2shim.UnknownVariableValue, + "protocol": hcl2shim.UnknownVariableValue, + }}, + "purplelist": "blah", + }, + Err: false, + }, + + // This should probably fail, but we let it pass and rely on 2nd + // validation phase when unknowns become known, which will then fail. + "partially known set values": { + Key: "allow", + Schema: map[string]*Schema{ + "allow": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "protocol": { + Type: TypeString, + Required: true, + }, + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + }, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "deny": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "protocol": { + Type: TypeString, + Required: true, + }, + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + }, + ExactlyOneOf: []string{"allow", "deny"}, + }, + }, + Config: map[string]interface{}{ + "allow": []interface{}{map[string]interface{}{ + "ports": hcl2shim.UnknownVariableValue, + "protocol": "TCP", + }}, + "deny": []interface{}{map[string]interface{}{ + "ports": hcl2shim.UnknownVariableValue, + "protocol": "UDP", + }}, + }, + Err: false, + }, + + "known set values": { + Key: "allow", + Schema: map[string]*Schema{ + "allow": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "protocol": { + Type: TypeString, + Required: true, + }, + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + }, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "deny": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "protocol": { + Type: TypeString, + Required: true, + }, + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + }, + ExactlyOneOf: []string{"allow", "deny"}, + }, + }, + Config: map[string]interface{}{ + "allow": []interface{}{map[string]interface{}{ + "ports": []interface{}{"80"}, + "protocol": "TCP", + }}, + "deny": []interface{}{map[string]interface{}{ + "ports": []interface{}{"80"}, + "protocol": "TCP", + }}, + }, + Err: true, + }, + + "wholly unknown simple lists": { + Key: "allow", + Schema: map[string]*Schema{ + "allow": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeString}, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "deny": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeString}, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "purplelist": { + Type: TypeString, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "allow": []interface{}{ + hcl2shim.UnknownVariableValue, + hcl2shim.UnknownVariableValue, + }, + "deny": []interface{}{ + hcl2shim.UnknownVariableValue, + hcl2shim.UnknownVariableValue, + }, + "purplelist": "blah", + }, + Err: false, + }, + + // This should probably fail, but we let it pass and rely on 2nd + // validation phase when unknowns become known, which will then fail. + "partially known simple lists": { + Key: "allow", + Schema: map[string]*Schema{ + "allow": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeString}, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "deny": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeString}, + ExactlyOneOf: []string{"allow", "deny"}, + }, + }, + Config: map[string]interface{}{ + "allow": []interface{}{ + hcl2shim.UnknownVariableValue, + "known", + }, + "deny": []interface{}{ + hcl2shim.UnknownVariableValue, + "known", + }, + }, + Err: false, + }, + + "known simple lists": { + Key: "allow", + Schema: map[string]*Schema{ + "allow": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeString}, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "deny": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeString}, + ExactlyOneOf: []string{"allow", "deny"}, + }, + }, + Config: map[string]interface{}{ + "allow": []interface{}{ + "blah", + "known", + }, + "deny": []interface{}{ + "known", + }, + }, + Err: true, + }, + + "wholly unknown map keys and values": { + Key: "allow", + Schema: map[string]*Schema{ + "allow": { + Type: TypeMap, + Optional: true, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "deny": { + Type: TypeList, + Optional: true, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "purplelist": { + Type: TypeString, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "allow": map[string]interface{}{ + hcl2shim.UnknownVariableValue: hcl2shim.UnknownVariableValue, + }, + "deny": map[string]interface{}{ + hcl2shim.UnknownVariableValue: hcl2shim.UnknownVariableValue, + }, + "purplelist": "blah", + }, + Err: false, + }, + + // This should probably fail, but we let it pass and rely on 2nd + // validation phase when unknowns become known, which will then fail. + "wholly unknown map values": { + Key: "allow", + Schema: map[string]*Schema{ + "allow": { + Type: TypeMap, + Optional: true, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "deny": { + Type: TypeList, + Optional: true, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "purplelist": { + Type: TypeString, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "allow": map[string]interface{}{ + "key": hcl2shim.UnknownVariableValue, + }, + "deny": map[string]interface{}{ + "key": hcl2shim.UnknownVariableValue, + }, + "purplelist": "blah", + }, + Err: false, + }, + + // This should probably fail, but we let it pass and rely on 2nd + // validation phase when unknowns become known, which will then fail. + "partially known maps": { + Key: "allow", + Schema: map[string]*Schema{ + "allow": { + Type: TypeMap, + Optional: true, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "deny": { + Type: TypeList, + Optional: true, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "purplelist": { + Type: TypeString, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "allow": map[string]interface{}{ + "first": "value", + "second": hcl2shim.UnknownVariableValue, + }, + "deny": map[string]interface{}{ + "first": "value", + "second": hcl2shim.UnknownVariableValue, + }, + "purplelist": "blah", + }, + Err: false, + }, + + "known maps": { + Key: "allow", + Schema: map[string]*Schema{ + "allow": { + Type: TypeMap, + Optional: true, + ExactlyOneOf: []string{"allow", "deny"}, + }, + "deny": { + Type: TypeList, + Optional: true, + ExactlyOneOf: []string{"allow", "deny"}, + }, + }, + Config: map[string]interface{}{ + "allow": map[string]interface{}{ + "first": "value", + "second": "blah", + }, + "deny": map[string]interface{}{ + "first": "value", + "second": "boo", + }, + }, + Err: true, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + c := terraform.NewResourceConfigRaw(tc.Config) + + err := validateExactlyOneAttribute(tc.Key, tc.Schema[tc.Key], c) + if err == nil && tc.Err { + t.Fatalf("expected error") + } + + if err != nil && !tc.Err { + t.Fatalf("didn't expect error, got error: %+v", err) + } + }) + } + +} + +func TestValidateAtLeastOneOfAttributes(t *testing.T) { + cases := map[string]struct { + Key string + Schema map[string]*Schema + Config map[string]interface{} + Err bool + }{ + + "two attributes specified": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"blacklist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"whitelist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": true, + "blacklist": true, + }, + Err: false, + }, + + "one attributes specified": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"blacklist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"whitelist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": true, + }, + Err: false, + }, + + "two attributes of three specified": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"blacklist", "purplelist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"whitelist", "purplelist"}, + }, + "purplelist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"whitelist", "blacklist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": true, + "purplelist": true, + }, + Err: false, + }, + + "three attributes of three specified": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"blacklist", "purplelist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"whitelist", "purplelist"}, + }, + "purplelist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"whitelist", "blacklist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": true, + "purplelist": true, + "blacklist": true, + }, + Err: false, + }, + + "one attributes of three specified": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"blacklist", "purplelist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"whitelist", "purplelist"}, + }, + "purplelist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"whitelist", "blacklist"}, + }, + }, + + Config: map[string]interface{}{ + "purplelist": true, + }, + Err: false, + }, + + "no attributes of three specified": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"whitelist", "blacklist", "purplelist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"whitelist", "blacklist", "purplelist"}, + }, + "purplelist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"whitelist", "blacklist", "purplelist"}, + }, + }, + + Config: map[string]interface{}{}, + Err: true, + }, + + "Only Unknown Variable Value": { + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"whitelist", "blacklist", "purplelist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"whitelist", "blacklist", "purplelist"}, + }, + "purplelist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"whitelist", "blacklist", "purplelist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": hcl2shim.UnknownVariableValue, + }, + + Err: false, + }, + + "only unknown list value": { + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeString}, + AtLeastOneOf: []string{"whitelist", "blacklist"}, + }, + "blacklist": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeString}, + AtLeastOneOf: []string{"whitelist", "blacklist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": []interface{}{hcl2shim.UnknownVariableValue}, + }, + + Err: false, + }, + + "Unknown Variable Value and Known Value": { + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"whitelist", "blacklist", "purplelist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"whitelist", "blacklist", "purplelist"}, + }, + "purplelist": { + Type: TypeBool, + Optional: true, + AtLeastOneOf: []string{"whitelist", "blacklist", "purplelist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": hcl2shim.UnknownVariableValue, + "blacklist": true, + }, + + Err: false, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + c := terraform.NewResourceConfigRaw(tc.Config) + diags := schemaMap(tc.Schema).Validate(c) + if diags.HasError() != tc.Err { + if !diags.HasError() { + t.Fatalf("expected error") + } + + for _, e := range diagutils.ErrorDiags(diags).Errors() { + t.Fatalf("didn't expect error, got error: %+v", e) + } + + t.FailNow() + } + }) + } +} + +func TestPanicOnErrorDefaultsFalse(t *testing.T) { + oldEnv := os.Getenv("TF_ACC") + + os.Setenv("TF_ACC", "") + if schemaMap(nil).panicOnError() { + t.Fatalf("panicOnError should be false when TF_ACC is empty") + } + + os.Setenv("TF_ACC", oldEnv) +} + +func TestPanicOnErrorTF_ACCSet(t *testing.T) { + oldEnv := os.Getenv("TF_ACC") + + os.Setenv("TF_ACC", "1") + if !schemaMap(nil).panicOnError() { + t.Fatalf("panicOnError should be true when TF_ACC is not empty") + } + + os.Setenv("TF_ACC", oldEnv) +} + +func TestValidateRequiredWithAttributes(t *testing.T) { + cases := map[string]struct { + Key string + Schema map[string]*Schema + Config map[string]interface{} + Err bool + }{ + + "two attributes specified": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"blacklist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"whitelist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": true, + "blacklist": true, + }, + Err: false, + }, + + "one attributes specified": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"blacklist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"whitelist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": true, + }, + Err: true, + }, + + "no attributes specified": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"blacklist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"whitelist"}, + }, + }, + + Config: map[string]interface{}{}, + Err: false, + }, + + "two attributes of three specified": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"purplelist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"whitelist", "purplelist"}, + }, + "purplelist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"whitelist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": true, + "purplelist": true, + }, + Err: false, + }, + + "three attributes of three specified": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"blacklist", "purplelist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"whitelist", "purplelist"}, + }, + "purplelist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"whitelist", "blacklist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": true, + "purplelist": true, + "blacklist": true, + }, + Err: false, + }, + + "one attributes of three specified": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"blacklist", "purplelist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"whitelist", "purplelist"}, + }, + "purplelist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"whitelist", "blacklist"}, + }, + }, + + Config: map[string]interface{}{ + "purplelist": true, + }, + Err: true, + }, + + "no attributes of three specified": { + Key: "whitelist", + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"whitelist", "blacklist", "purplelist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"whitelist", "blacklist", "purplelist"}, + }, + "purplelist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"whitelist", "blacklist", "purplelist"}, + }, + }, + + Config: map[string]interface{}{}, + Err: false, + }, + + "Only Unknown Variable Value": { + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"whitelist", "blacklist", "purplelist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"whitelist", "blacklist", "purplelist"}, + }, + "purplelist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"whitelist", "blacklist", "purplelist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": hcl2shim.UnknownVariableValue, + }, + + Err: true, + }, + + "only unknown list value": { + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeString}, + RequiredWith: []string{"whitelist", "blacklist"}, + }, + "blacklist": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeString}, + RequiredWith: []string{"whitelist", "blacklist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": []interface{}{hcl2shim.UnknownVariableValue}, + }, + + Err: true, + }, + + "Unknown Variable Value and Known Value": { + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"whitelist", "blacklist", "purplelist"}, + }, + "blacklist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"whitelist", "blacklist", "purplelist"}, + }, + "purplelist": { + Type: TypeBool, + Optional: true, + RequiredWith: []string{"whitelist", "blacklist", "purplelist"}, + }, + }, + + Config: map[string]interface{}{ + "whitelist": hcl2shim.UnknownVariableValue, + "blacklist": true, + }, + + Err: true, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + c := terraform.NewResourceConfigRaw(tc.Config) + diags := schemaMap(tc.Schema).Validate(c) + es := diagutils.ErrorDiags(diags).Errors() + if len(es) > 0 != tc.Err { + if len(es) == 0 { + t.Fatalf("expected error") + } + + for _, e := range es { + t.Fatalf("didn't expect error, got error: %+v", e) + } + + t.FailNow() + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/serialize.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/serialize.go index fe6d7504..0e0e3cca 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/serialize.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/serialize.go @@ -91,7 +91,12 @@ func SerializeResourceForHash(buf *bytes.Buffer, val interface{}, resource *Reso sm := resource.Schema m := val.(map[string]interface{}) var keys []string - for k := range sm { + allComputed := true + for k, v := range sm { + if v.Optional || v.Required { + allComputed = false + } + keys = append(keys, k) } sort.Strings(keys) @@ -100,7 +105,7 @@ func SerializeResourceForHash(buf *bytes.Buffer, val interface{}, resource *Reso // Skip attributes that are not user-provided. Computed attributes // do not contribute to the hash since their ultimate value cannot // be known at plan/diff time. - if !(innerSchema.Required || innerSchema.Optional) { + if !allComputed && !(innerSchema.Required || innerSchema.Optional) { continue } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/serialize_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/serialize_test.go new file mode 100644 index 00000000..efa01c95 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/serialize_test.go @@ -0,0 +1,258 @@ +package schema + +import ( + "bytes" + "testing" +) + +func TestSerializeForHash(t *testing.T) { + type testCase struct { + Schema interface{} + Value interface{} + Expected string + } + + tests := []testCase{ + { + Schema: &Schema{ + Type: TypeInt, + }, + Value: 0, + Expected: "0;", + }, + + { + Schema: &Schema{ + Type: TypeInt, + }, + Value: 200, + Expected: "200;", + }, + + { + Schema: &Schema{ + Type: TypeBool, + }, + Value: true, + Expected: "1;", + }, + + { + Schema: &Schema{ + Type: TypeBool, + }, + Value: false, + Expected: "0;", + }, + + { + Schema: &Schema{ + Type: TypeFloat, + }, + Value: 1.0, + Expected: "1;", + }, + + { + Schema: &Schema{ + Type: TypeFloat, + }, + Value: 1.54, + Expected: "1.54;", + }, + + { + Schema: &Schema{ + Type: TypeFloat, + }, + Value: 0.1, + Expected: "0.1;", + }, + + { + Schema: &Schema{ + Type: TypeString, + }, + Value: "hello", + Expected: "hello;", + }, + + { + Schema: &Schema{ + Type: TypeString, + }, + Value: "1", + Expected: "1;", + }, + + { + Schema: &Schema{ + Type: TypeList, + Elem: &Schema{ + Type: TypeString, + }, + }, + Value: []interface{}{}, + Expected: "();", + }, + + { + Schema: &Schema{ + Type: TypeList, + Elem: &Schema{ + Type: TypeString, + }, + }, + Value: []interface{}{"hello", "world"}, + Expected: "(hello;world;);", + }, + + { + Schema: &Schema{ + Type: TypeList, + Elem: &Resource{ + Schema: map[string]*Schema{ + "fo": { + Type: TypeString, + Required: true, + }, + "fum": { + Type: TypeString, + Required: true, + }, + }, + }, + }, + Value: []interface{}{ + map[string]interface{}{ + "fo": "bar", + }, + map[string]interface{}{ + "fo": "baz", + "fum": "boz", + }, + }, + Expected: "(;;);", + }, + + { + Schema: &Schema{ + Type: TypeSet, + Elem: &Schema{ + Type: TypeString, + }, + }, + Value: NewSet(func(i interface{}) int { return len(i.(string)) }, []interface{}{ + "hello", + "woo", + }), + Expected: "{woo;hello;};", + }, + + { + Schema: &Schema{ + Type: TypeMap, + Elem: &Schema{ + Type: TypeString, + }, + }, + Value: map[string]interface{}{ + "foo": "bar", + "baz": "foo", + }, + Expected: "[baz:foo;foo:bar;];", + }, + + { + Schema: &Resource{ + Schema: map[string]*Schema{ + "name": { + Type: TypeString, + Required: true, + }, + "size": { + Type: TypeInt, + Optional: true, + }, + "green": { + Type: TypeBool, + Optional: true, + Computed: true, + }, + "upside_down": { + Type: TypeBool, + Computed: true, + }, + }, + }, + Value: map[string]interface{}{ + "name": "my-fun-database", + "size": 12, + "green": true, + }, + Expected: "green:1;name:my-fun-database;size:12;", + }, + + // test TypeMap nested in Schema: GH-7091 + { + Schema: &Resource{ + Schema: map[string]*Schema{ + "outer": { + Type: TypeSet, + Required: true, + Elem: &Schema{ + Type: TypeMap, + Optional: true, + }, + }, + }, + }, + Value: map[string]interface{}{ + "outer": NewSet(func(i interface{}) int { return 42 }, []interface{}{ + map[string]interface{}{ + "foo": "bar", + "baz": "foo", + }, + }), + }, + Expected: "outer:{[baz:foo;foo:bar;];};", + }, + + { + Schema: &Resource{ + Schema: map[string]*Schema{ + "attr1": { + Type: TypeString, + Computed: true, + }, + "attr2": { + Type: TypeString, + Computed: true, + }, + }, + }, + Value: map[string]interface{}{ + "attr1": "value1", + "attr2": "value2", + }, + Expected: "attr1:value1;attr2:value2;", + }, + } + + for _, test := range tests { + var gotBuf bytes.Buffer + schema := test.Schema + + switch s := schema.(type) { + case *Schema: + SerializeValueForHash(&gotBuf, test.Value, s) + case *Resource: + SerializeResourceForHash(&gotBuf, test.Value, s) + } + + got := gotBuf.String() + if got != test.Expected { + t.Errorf("hash(%#v) got %#v, but want %#v", test.Value, got, test.Expected) + } + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/set.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/set.go index daa431dd..a510e60f 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/set.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/set.go @@ -8,7 +8,7 @@ import ( "strconv" "sync" - "github.com/hashicorp/terraform-plugin-sdk/helper/hashcode" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/helper/hashcode" ) // HashString hashes strings. If you want a Set of strings, this is the @@ -150,13 +150,46 @@ func (s *Set) Union(other *Set) *Set { return result } +func checkSetMapEqual(m1, m2 map[string]interface{}) bool { + if (m1 == nil) != (m2 == nil) { + return false + } + if len(m1) != len(m2) { + return false + } + for k := range m1 { + v1 := m1[k] + v2, ok := m2[k] + if !ok { + return false + } + switch v1.(type) { + case map[string]interface{}: + same := checkSetMapEqual(v1.(map[string]interface{}), v2.(map[string]interface{})) + if !same { + return false + } + case *Set: + same := v1.(*Set).Equal(v2) + if !same { + return false + } + default: + same := reflect.DeepEqual(v1, v2) + if !same { + return false + } + } + } + return true +} + func (s *Set) Equal(raw interface{}) bool { other, ok := raw.(*Set) if !ok { return false } - - return reflect.DeepEqual(s.m, other.m) + return checkSetMapEqual(s.m, other.m) } // HashEqual simply checks to the keys the top-level map to the keys in the @@ -198,16 +231,13 @@ func (s *Set) add(item interface{}, computed bool) string { code := s.hash(item) if computed { code = "~" + code - - if isProto5() { - tmpCode := code - count := 0 - for _, exists := s.m[tmpCode]; exists; _, exists = s.m[tmpCode] { - count++ - tmpCode = fmt.Sprintf("%s%d", code, count) - } - code = tmpCode + tmpCode := code + count := 0 + for _, exists := s.m[tmpCode]; exists; _, exists = s.m[tmpCode] { + count++ + tmpCode = fmt.Sprintf("%s%d", code, count) } + code = tmpCode } if _, ok := s.m[code]; !ok { diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/set_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/set_test.go new file mode 100644 index 00000000..9ed1219e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/set_test.go @@ -0,0 +1,247 @@ +package schema + +import ( + "reflect" + "testing" +) + +func TestSetAdd(t *testing.T) { + s := &Set{F: testSetInt} + s.Add(1) + s.Add(5) + s.Add(25) + + expected := []interface{}{1, 25, 5} + actual := s.List() + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } +} + +func TestSetAdd_negative(t *testing.T) { + // Since we don't allow negative hashes, this should just hash to the + // same thing... + s := &Set{F: testSetInt} + s.Add(-1) + s.Add(1) + + expected := []interface{}{-1} + actual := s.List() + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } +} + +func TestSetContains(t *testing.T) { + s := &Set{F: testSetInt} + s.Add(5) + s.Add(-5) + + if s.Contains(2) { + t.Fatal("should not contain") + } + if !s.Contains(5) { + t.Fatal("should contain") + } + if !s.Contains(-5) { + t.Fatal("should contain") + } +} + +func TestSetDifference(t *testing.T) { + s1 := &Set{F: testSetInt} + s2 := &Set{F: testSetInt} + + s1.Add(1) + s1.Add(5) + + s2.Add(5) + s2.Add(25) + + difference := s1.Difference(s2) + difference.Add(2) + + expected := []interface{}{1, 2} + actual := difference.List() + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } +} + +func TestSetIntersection(t *testing.T) { + s1 := &Set{F: testSetInt} + s2 := &Set{F: testSetInt} + + s1.Add(1) + s1.Add(5) + + s2.Add(5) + s2.Add(25) + + intersection := s1.Intersection(s2) + intersection.Add(2) + + expected := []interface{}{2, 5} + actual := intersection.List() + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } +} + +func TestSetUnion(t *testing.T) { + s1 := &Set{F: testSetInt} + s2 := &Set{F: testSetInt} + + s1.Add(1) + s1.Add(5) + + s2.Add(5) + s2.Add(25) + + union := s1.Union(s2) + union.Add(2) + + expected := []interface{}{1, 2, 25, 5} + actual := union.List() + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } +} + +func testSetInt(v interface{}) int { + return v.(int) +} + +// Same test function implementation but not same address +func testSetInt2(v interface{}) int { + return v.(int) +} + +func TestHashResource_nil(t *testing.T) { + resource := &Resource{ + Schema: map[string]*Schema{ + "name": { + Type: TypeString, + Optional: true, + }, + }, + } + f := HashResource(resource) + + idx := f(nil) + if idx != 0 { + t.Fatalf("Expected 0 when hashing nil, given: %d", idx) + } +} + +func TestHashEqual(t *testing.T) { + nested := &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + }, + }, + } + root := &Resource{ + Schema: map[string]*Schema{ + "bar": { + Type: TypeString, + Optional: true, + }, + "nested": { + Type: TypeSet, + Optional: true, + Elem: nested, + }, + }, + } + n1 := map[string]interface{}{"foo": "bar"} + n2 := map[string]interface{}{"foo": "baz"} + + r1 := map[string]interface{}{ + "bar": "baz", + "nested": NewSet(HashResource(nested), []interface{}{n1}), + } + r2 := map[string]interface{}{ + "bar": "qux", + "nested": NewSet(HashResource(nested), []interface{}{n2}), + } + r3 := map[string]interface{}{ + "bar": "baz", + "nested": NewSet(HashResource(nested), []interface{}{n2}), + } + r4 := map[string]interface{}{ + "bar": "qux", + "nested": NewSet(HashResource(nested), []interface{}{n1}), + } + s1 := NewSet(HashResource(root), []interface{}{r1}) + s2 := NewSet(HashResource(root), []interface{}{r2}) + s3 := NewSet(HashResource(root), []interface{}{r3}) + s4 := NewSet(HashResource(root), []interface{}{r4}) + + cases := []struct { + name string + set *Set + compare *Set + expected bool + }{ + { + name: "equal", + set: s1, + compare: s1, + expected: true, + }, + { + name: "not equal", + set: s1, + compare: s2, + expected: false, + }, + { + name: "outer equal, should still not be equal", + set: s1, + compare: s3, + expected: false, + }, + { + name: "inner equal, should still not be equal", + set: s1, + compare: s4, + expected: false, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + actual := tc.set.HashEqual(tc.compare) + if tc.expected != actual { + t.Fatalf("expected %t, got %t", tc.expected, actual) + } + }) + } +} + +func TestSetEqualNested(t *testing.T) { + s := &Set{F: testSetInt} + s.Add(1) + s.Add(5) + s.Add(25) + + s1 := &Set{F: testSetInt} + s1.Add(100) + s.m["dummy"] = s1 + + // Same nested structure but different function address + s2 := &Set{F: testSetInt2} + s2.Add(1) + s2.Add(5) + s2.Add(25) + + s3 := &Set{F: testSetInt2} + s3.Add(100) + s2.m["dummy"] = s3 + + if !s.Equal(s2) { + t.Fatalf("Nested Sets structures differ") + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/shims.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/shims.go index 93c601f8..e1575a0f 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/shims.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/shims.go @@ -1,29 +1,30 @@ package schema import ( + "context" "encoding/json" - "github.com/zclconf/go-cty/cty" - ctyjson "github.com/zclconf/go-cty/cty/json" + "github.com/hashicorp/go-cty/cty" + ctyjson "github.com/hashicorp/go-cty/cty/json" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim" - "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/hcl2shim" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) // DiffFromValues takes the current state and desired state as cty.Values and // derives a terraform.InstanceDiff to give to the legacy providers. This is // used to take the states provided by the new ApplyResourceChange method and // convert them to a state+diff required for the legacy Apply method. -func DiffFromValues(prior, planned cty.Value, res *Resource) (*terraform.InstanceDiff, error) { - return diffFromValues(prior, planned, res, nil) +func DiffFromValues(ctx context.Context, prior, planned cty.Value, res *Resource) (*terraform.InstanceDiff, error) { + return diffFromValues(ctx, prior, planned, res, nil) } // diffFromValues takes an additional CustomizeDiffFunc, so we can generate our // test fixtures from the legacy tests. In the new provider protocol the diff // only needs to be created for the apply operation, and any customizations // have already been done. -func diffFromValues(prior, planned cty.Value, res *Resource, cust CustomizeDiffFunc) (*terraform.InstanceDiff, error) { +func diffFromValues(ctx context.Context, prior, planned cty.Value, res *Resource, cust CustomizeDiffFunc) (*terraform.InstanceDiff, error) { instanceState, err := res.ShimInstanceStateFromValue(prior) if err != nil { return nil, err @@ -35,7 +36,7 @@ func diffFromValues(prior, planned cty.Value, res *Resource, cust CustomizeDiffF removeConfigUnknowns(cfg.Config) removeConfigUnknowns(cfg.Raw) - diff, err := schemaMap(res.Schema).Diff(instanceState, cfg, cust, nil, false) + diff, err := schemaMap(res.Schema).Diff(ctx, instanceState, cfg, cust, nil, false) if err != nil { return nil, err } @@ -76,14 +77,24 @@ func ApplyDiff(base cty.Value, d *terraform.InstanceDiff, schema *configschema.B // StateValueToJSONMap converts a cty.Value to generic JSON map via the cty JSON // encoding. func StateValueToJSONMap(val cty.Value, ty cty.Type) (map[string]interface{}, error) { + return stateValueToJSONMap(val, ty, false) +} + +func stateValueToJSONMap(val cty.Value, ty cty.Type, useJSONNumber bool) (map[string]interface{}, error) { js, err := ctyjson.Marshal(val, ty) if err != nil { return nil, err } var m map[string]interface{} - if err := json.Unmarshal(js, &m); err != nil { - return nil, err + if useJSONNumber { + if err := unmarshalJSON(js, &m); err != nil { + return nil, err + } + } else { + if err := json.Unmarshal(js, &m); err != nil { + return nil, err + } } return m, nil diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/shims_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/shims_test.go new file mode 100644 index 00000000..678bda3f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/shims_test.go @@ -0,0 +1,3491 @@ +package schema + +import ( + "bytes" + "context" + "errors" + "fmt" + "reflect" + "strconv" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/go-cty/cty" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/hcl2shim" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/diagutils" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/helper/hashcode" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/providers" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/tfdiags" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +var ( + typeComparer = cmp.Comparer(cty.Type.Equals) + valueComparer = cmp.Comparer(cty.Value.RawEquals) + equateEmpty = cmpopts.EquateEmpty() +) + +func testApplyDiff(t *testing.T, + resource *Resource, + state, expected *terraform.InstanceState, + diff *terraform.InstanceDiff) { + + testSchema := providers.Schema{ + Version: int64(resource.SchemaVersion), + Block: resourceSchemaToBlock(resource.Schema), + } + + stateVal, err := StateValueFromInstanceState(state, testSchema.Block.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + newState, err := ApplyDiff(stateVal, diff, testSchema.Block) + if err != nil { + t.Fatal(err) + } + + // verify that "id" is correct + id := newState.AsValueMap()["id"] + + switch { + case diff.Destroy || diff.DestroyDeposed || diff.DestroyTainted: + // there should be no id + if !id.IsNull() { + t.Fatalf("destroyed instance should have no id: %#v", id) + } + default: + // the "id" field always exists and is computed, so it must have a + // valid value or be unknown. + if id.IsNull() { + t.Fatal("new instance state cannot have a null id") + } + + if id.IsKnown() && id.AsString() == "" { + t.Fatal("new instance id cannot be an empty string") + } + } + + // Resource.Meta will be hanlded separately, so it's OK that we lose the + // timeout values here. + expectedState, err := StateValueFromInstanceState(expected, testSchema.Block.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + if !cmp.Equal(expectedState, newState, equateEmpty, typeComparer, valueComparer) { + t.Fatalf(cmp.Diff(expectedState, newState, equateEmpty, typeComparer, valueComparer)) + } +} + +func TestShimResourcePlan_destroyCreate(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + ForceNew: true, + }, + }, + } + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + RequiresNew: true, + Old: "3", + New: "42", + }, + }, + } + + state := &terraform.InstanceState{ + Attributes: map[string]string{"foo": "3"}, + } + + expected := &terraform.InstanceState{ + ID: hcl2shim.UnknownVariableValue, + Attributes: map[string]string{ + "id": hcl2shim.UnknownVariableValue, + "foo": "42", + }, + Meta: map[string]interface{}{ + "schema_version": "2", + }, + } + + testApplyDiff(t, r, state, expected, d) +} + +func TestShimResourceApply_create(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + } + + called := false + r.Create = func(d *ResourceData, m interface{}) error { + called = true + d.SetId("foo") + return nil + } + + var s *terraform.InstanceState = nil + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + New: "42", + }, + }, + } + + actual, diags := r.Apply(context.Background(), s, d, nil) + if diags.HasError() { + t.Fatalf("err: %s", diagutils.ErrorDiags(diags)) + } + + if !called { + t.Fatal("not called") + } + + expected := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "42", + }, + Meta: map[string]interface{}{ + "schema_version": "2", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } + + // Shim + // now that we have our diff and desired state, see if we can reproduce + // that with the shim + // we're not testing Resource.Create, so we need to start with the "created" state + createdState := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{"id": "foo"}, + } + + testApplyDiff(t, r, createdState, expected, d) +} + +func TestShimResourceApply_Timeout_state(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + Timeouts: &ResourceTimeout{ + Create: DefaultTimeout(40 * time.Minute), + Update: DefaultTimeout(80 * time.Minute), + Delete: DefaultTimeout(40 * time.Minute), + }, + } + + called := false + r.Create = func(d *ResourceData, m interface{}) error { + called = true + d.SetId("foo") + return nil + } + + var s *terraform.InstanceState = nil + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + New: "42", + }, + }, + } + + diffTimeout := &ResourceTimeout{ + Create: DefaultTimeout(40 * time.Minute), + Update: DefaultTimeout(80 * time.Minute), + Delete: DefaultTimeout(40 * time.Minute), + } + + if err := diffTimeout.DiffEncode(d); err != nil { + t.Fatalf("Error encoding timeout to diff: %s", err) + } + + actual, diags := r.Apply(context.Background(), s, d, nil) + if diags.HasError() { + t.Fatalf("err: %s", diagutils.ErrorDiags(diags)) + } + + if !called { + t.Fatal("not called") + } + + expected := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "42", + }, + Meta: map[string]interface{}{ + "schema_version": "2", + TimeoutKey: expectedForValues(40, 0, 80, 40, 0), + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("Not equal in Timeout State:\n\texpected: %#v\n\tactual: %#v", expected.Meta, actual.Meta) + } + + // Shim + // we're not testing Resource.Create, so we need to start with the "created" state + createdState := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{"id": "foo"}, + } + + testApplyDiff(t, r, createdState, expected, d) +} + +func TestShimResourceDiff_Timeout_diff(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + Timeouts: &ResourceTimeout{ + Create: DefaultTimeout(40 * time.Minute), + Update: DefaultTimeout(80 * time.Minute), + Delete: DefaultTimeout(40 * time.Minute), + }, + } + + r.Create = func(d *ResourceData, m interface{}) error { + d.SetId("foo") + return nil + } + + conf := terraform.NewResourceConfigRaw(map[string]interface{}{ + "foo": 42, + TimeoutsConfigKey: map[string]interface{}{ + "create": "2h", + }, + }) + var s *terraform.InstanceState + + actual, err := r.Diff(context.Background(), s, conf, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + New: "42", + }, + }, + } + + diffTimeout := &ResourceTimeout{ + Create: DefaultTimeout(120 * time.Minute), + Update: DefaultTimeout(80 * time.Minute), + Delete: DefaultTimeout(40 * time.Minute), + } + + if err := diffTimeout.DiffEncode(expected); err != nil { + t.Fatalf("Error encoding timeout to diff: %s", err) + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("Not equal in Timeout Diff:\n\texpected: %#v\n\tactual: %#v", expected.Meta, actual.Meta) + } + + // Shim + // apply this diff, so we have a state to compare + applied, diags := r.Apply(context.Background(), s, actual, nil) + if diags.HasError() { + t.Fatal(err) + } + + // we're not testing Resource.Create, so we need to start with the "created" state + createdState := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{"id": "foo"}, + } + + testSchema := providers.Schema{ + Version: int64(r.SchemaVersion), + Block: resourceSchemaToBlock(r.Schema), + } + + initialVal, err := StateValueFromInstanceState(createdState, testSchema.Block.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + appliedVal, err := StateValueFromInstanceState(applied, testSchema.Block.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + d, err := DiffFromValues(context.Background(), initialVal, appliedVal, r) + if err != nil { + t.Fatal(err) + } + if eq, _ := d.Same(expected); !eq { + t.Fatal(cmp.Diff(d, expected)) + } +} + +func TestShimResourceApply_destroy(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + } + + called := false + r.Delete = func(d *ResourceData, m interface{}) error { + called = true + return nil + } + + s := &terraform.InstanceState{ + ID: "bar", + } + + d := &terraform.InstanceDiff{ + Destroy: true, + } + + actual, diags := r.Apply(context.Background(), s, d, nil) + if diags.HasError() { + t.Fatalf("err: %s", diagutils.ErrorDiags(diags)) + } + + if !called { + t.Fatal("delete not called") + } + + if actual != nil { + t.Fatalf("bad: %#v", actual) + } + + // Shim + // now that we have our diff and desired state, see if we can reproduce + // that with the shim + testApplyDiff(t, r, s, actual, d) +} + +func TestShimResourceApply_destroyCreate(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + ForceNew: true, + }, + + "tags": { + Type: TypeMap, + Optional: true, + Computed: true, + }, + }, + } + + change := false + r.Create = func(d *ResourceData, m interface{}) error { + change = d.HasChange("tags") + d.SetId("foo") + return nil + } + r.Delete = func(d *ResourceData, m interface{}) error { + return nil + } + + var s *terraform.InstanceState = &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "foo": "7", + "tags.Name": "foo", + }, + } + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "id": { + New: "foo", + }, + "foo": { + Old: "7", + New: "42", + RequiresNew: true, + }, + "tags.Name": { + Old: "foo", + New: "foo", + RequiresNew: true, + }, + }, + } + + actual, diags := r.Apply(context.Background(), s, d, nil) + if diags.HasError() { + t.Fatalf("err: %s", diagutils.ErrorDiags(diags)) + } + + if !change { + t.Fatal("should have change") + } + + expected := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "42", + "tags.%": "1", + "tags.Name": "foo", + }, + } + + if !reflect.DeepEqual(actual, expected) { + cmp.Diff(actual, expected) + } + + // Shim + // now that we have our diff and desired state, see if we can reproduce + // that with the shim + // we're not testing Resource.Create, so we need to start with the "created" state + createdState := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "7", + "tags.%": "1", + "tags.Name": "foo", + }, + } + + testApplyDiff(t, r, createdState, expected, d) +} + +func TestShimSchemaMap_Diff(t *testing.T) { + cases := []struct { + Name string + Schema map[string]*Schema + State *terraform.InstanceState + Config map[string]interface{} + CustomizeDiff CustomizeDiffFunc + Diff *terraform.InstanceDiff + Err bool + }{ + { + Name: "diff-1", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "diff-2", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + NewComputed: true, + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "diff-3", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "foo", + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Computed, but set in config", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + + Config: map[string]interface{}{ + "availability_zone": "bar", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "foo", + New: "bar", + }, + }, + }, + + Err: false, + }, + + { + Name: "Default", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Default: "foo", + }, + }, + + State: nil, + + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + }, + }, + }, + + Err: false, + }, + + { + Name: "DefaultFunc, value", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + DefaultFunc: func() (interface{}, error) { + return "foo", nil + }, + }, + }, + + State: nil, + + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + }, + }, + }, + + Err: false, + }, + + { + Name: "DefaultFunc, configuration set", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + DefaultFunc: func() (interface{}, error) { + return "foo", nil + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "bar", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "bar", + }, + }, + }, + + Err: false, + }, + + { + Name: "String with StateFunc", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + StateFunc: func(a interface{}) string { + return a.(string) + "!" + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo!", + NewExtra: "foo", + }, + }, + }, + + Err: false, + }, + + { + Name: "StateFunc not called with nil value", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + StateFunc: func(a interface{}) string { + t.Error("should not get here!") + return "" + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Variable computed", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": hcl2shim.UnknownVariableValue, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: hcl2shim.UnknownVariableValue, + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Int decode", + Schema: map[string]*Schema{ + "port": { + Type: TypeInt, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "port": 27, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "port": { + Old: "", + New: "27", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "bool decode", + Schema: map[string]*Schema{ + "port": { + Type: TypeBool, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "port": false, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "port": { + Old: "", + New: "false", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Bool", + Schema: map[string]*Schema{ + "delete": { + Type: TypeBool, + Optional: true, + Default: false, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "delete": "false", + }, + }, + + Config: nil, + + Diff: nil, + + Err: false, + }, + + { + Name: "List decode", + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "0", + New: "3", + }, + "ports.0": { + Old: "", + New: "1", + }, + "ports.1": { + Old: "", + New: "2", + }, + "ports.2": { + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "0", + New: "3", + }, + "ports.0": { + Old: "", + New: "1", + }, + "ports.1": { + Old: "", + New: "2", + }, + "ports.2": { + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, hcl2shim.UnknownVariableValue, "5"}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "0", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "ports.#": "3", + "ports.0": "1", + "ports.1": "2", + "ports.2": "5", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: nil, + + Err: false, + }, + + { + Name: "", + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "ports.#": "2", + "ports.0": "1", + "ports.1": "2", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "2", + New: "3", + }, + "ports.2": { + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "", + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "0", + New: "3", + RequiresNew: true, + }, + "ports.0": { + Old: "", + New: "1", + RequiresNew: true, + }, + "ports.1": { + Old: "", + New: "2", + RequiresNew: true, + }, + "ports.2": { + Old: "", + New: "5", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "", + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "List with computed set", + Schema: map[string]*Schema{ + "config": { + Type: TypeList, + Optional: true, + ForceNew: true, + MinItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "name": { + Type: TypeString, + Required: true, + }, + + "rules": { + Type: TypeSet, + Computed: true, + Elem: &Schema{Type: TypeString}, + Set: HashString, + }, + }, + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "config": []interface{}{ + map[string]interface{}{ + "name": "hello", + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config.#": { + Old: "0", + New: "1", + RequiresNew: true, + }, + + "config.0.name": { + Old: "", + New: "hello", + }, + + "config.0.rules.#": { + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-1", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{5, 2, 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "0", + New: "3", + }, + "ports.1": { + Old: "", + New: "1", + }, + "ports.2": { + Old: "", + New: "2", + }, + "ports.5": { + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-2", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Computed: true, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "ports.#": "0", + }, + }, + + Config: nil, + + Diff: nil, + + Err: false, + }, + + { + Name: "Set-3", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-4", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{"2", "5", 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "0", + New: "3", + }, + "ports.1": { + Old: "", + New: "1", + }, + "ports.2": { + Old: "", + New: "2", + }, + "ports.5": { + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-5", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, hcl2shim.UnknownVariableValue, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-6", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "ports.#": "2", + "ports.1": "1", + "ports.2": "2", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{5, 2, 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + Old: "2", + New: "3", + }, + "ports.1": { + Old: "1", + New: "1", + }, + "ports.2": { + Old: "2", + New: "2", + }, + "ports.5": { + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-8", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "availability_zone": "bar", + "ports.#": "1", + "ports.80": "80", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Set-9", + Schema: map[string]*Schema{ + "ingress": { + Type: TypeSet, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + ps := m["ports"].([]interface{}) + result := 0 + for _, p := range ps { + result += p.(int) + } + return result + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "ingress.#": "2", + "ingress.80.ports.#": "1", + "ingress.80.ports.0": "80", + "ingress.443.ports.#": "1", + "ingress.443.ports.0": "443", + }, + }, + + Config: map[string]interface{}{ + "ingress": []interface{}{ + map[string]interface{}{ + "ports": []interface{}{443}, + }, + map[string]interface{}{ + "ports": []interface{}{80}, + }, + }, + }, + + Diff: nil, + + Err: false, + }, + + { + Name: "List of structure decode", + Schema: map[string]*Schema{ + "ingress": { + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": { + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ingress": []interface{}{ + map[string]interface{}{ + "from": 8080, + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ingress.#": { + Old: "0", + New: "1", + }, + "ingress.0.from": { + Old: "", + New: "8080", + }, + }, + }, + + Err: false, + }, + + { + Name: "ComputedWhen", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Computed: true, + ComputedWhen: []string{"port"}, + }, + + "port": { + Type: TypeInt, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "availability_zone": "foo", + "port": "80", + }, + }, + + Config: map[string]interface{}{ + "port": 80, + }, + + Diff: nil, + + Err: false, + }, + + { + Name: "computed", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Computed: true, + ComputedWhen: []string{"port"}, + }, + + "port": { + Type: TypeInt, + Optional: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "port": 80, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + NewComputed: true, + }, + "port": { + New: "80", + }, + }, + }, + + Err: false, + }, + + { + Name: "computed, exists", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Computed: true, + ComputedWhen: []string{"port"}, + }, + + "port": { + Type: TypeInt, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "port": "80", + }, + }, + + Config: map[string]interface{}{ + "port": 80, + }, + + // there is no computed diff when the instance exists already + Diff: nil, + + Err: false, + }, + + { + Name: "Maps-1", + Schema: map[string]*Schema{ + "config_vars": { + Type: TypeMap, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "config_vars": map[string]interface{}{ + "bar": "baz", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.%": { + Old: "0", + New: "1", + }, + + "config_vars.bar": { + Old: "", + New: "baz", + }, + }, + }, + + Err: false, + }, + + { + Name: "Maps-2", + Schema: map[string]*Schema{ + "config_vars": { + Type: TypeMap, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "config_vars.%": "1", + "config_vars.foo": "bar", + }, + }, + + Config: map[string]interface{}{ + "config_vars": map[string]interface{}{ + "bar": "baz", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.foo": { + Old: "bar", + NewRemoved: true, + }, + "config_vars.bar": { + Old: "", + New: "baz", + }, + }, + }, + + Err: false, + }, + + { + Name: "Maps-3", + Schema: map[string]*Schema{ + "vars": { + Type: TypeMap, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "vars.%": "1", + "vars.foo": "bar", + }, + }, + + Config: map[string]interface{}{ + "vars": map[string]interface{}{ + "bar": "baz", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "vars.foo": { + Old: "bar", + New: "", + NewRemoved: true, + }, + "vars.bar": { + Old: "", + New: "baz", + }, + }, + }, + + Err: false, + }, + + { + Name: "Maps-4", + Schema: map[string]*Schema{ + "vars": { + Type: TypeMap, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "vars.%": "1", + "vars.foo": "bar", + }, + }, + + Config: nil, + + Diff: nil, + + Err: false, + }, + + { + Name: "Maps-5", + Schema: map[string]*Schema{ + "config_vars": { + Type: TypeList, + Elem: &Schema{Type: TypeMap}, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "config_vars.#": "1", + "config_vars.0.%": "1", + "config_vars.0.foo": "bar", + }, + }, + + Config: map[string]interface{}{ + "config_vars": []interface{}{ + map[string]interface{}{ + "bar": "baz", + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.0.foo": { + Old: "bar", + NewRemoved: true, + }, + "config_vars.0.bar": { + Old: "", + New: "baz", + }, + }, + }, + + Err: false, + }, + + { + Name: "Maps-6", + Schema: map[string]*Schema{ + "config_vars": { + Type: TypeList, + Elem: &Schema{Type: TypeMap}, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "config_vars.#": "1", + "config_vars.0.%": "2", + "config_vars.0.foo": "bar", + "config_vars.0.bar": "baz", + }, + }, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.#": { + Old: "1", + New: "0", + }, + "config_vars.0.%": { + Old: "2", + New: "0", + }, + "config_vars.0.foo": { + Old: "bar", + NewRemoved: true, + }, + "config_vars.0.bar": { + Old: "baz", + NewRemoved: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "ForceNews", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + ForceNew: true, + }, + + "address": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "availability_zone": "bar", + "address": "foo", + }, + }, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "bar", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-10", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + ForceNew: true, + }, + + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "availability_zone": "bar", + "ports.#": "1", + "ports.80": "80", + }, + }, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "bar", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-11", + Schema: map[string]*Schema{ + "instances": { + Type: TypeSet, + Elem: &Schema{Type: TypeString}, + Optional: true, + Computed: true, + Set: func(v interface{}) int { + return len(v.(string)) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "instances.#": "0", + }, + }, + + Config: map[string]interface{}{ + "instances": []interface{}{hcl2shim.UnknownVariableValue}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "instances.#": { + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-12", + Schema: map[string]*Schema{ + "route": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": { + Type: TypeInt, + Required: true, + }, + + "gateway": { + Type: TypeString, + Optional: true, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "route": []interface{}{ + map[string]interface{}{ + "index": "1", + "gateway": hcl2shim.UnknownVariableValue, + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "route.#": { + Old: "0", + New: "1", + }, + "route.~1.index": { + Old: "", + New: "1", + }, + "route.~1.gateway": { + Old: "", + New: hcl2shim.UnknownVariableValue, + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-13", + Schema: map[string]*Schema{ + "route": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": { + Type: TypeInt, + Required: true, + }, + + "gateway": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "route": []interface{}{ + map[string]interface{}{ + "index": "1", + "gateway": []interface{}{ + hcl2shim.UnknownVariableValue, + }, + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "route.#": { + Old: "0", + New: "1", + }, + "route.~1.index": { + Old: "", + New: "1", + }, + "route.~1.gateway.#": { + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Computed maps", + Schema: map[string]*Schema{ + "vars": { + Type: TypeMap, + Computed: true, + }, + }, + + State: nil, + + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "vars.%": { + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Computed maps", + Schema: map[string]*Schema{ + "vars": { + Type: TypeMap, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "vars.%": "0", + }, + }, + + Config: map[string]interface{}{ + "vars": map[string]interface{}{ + "bar": hcl2shim.UnknownVariableValue, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "vars.%": { + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Empty", + Schema: map[string]*Schema{}, + + State: &terraform.InstanceState{}, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Float", + Schema: map[string]*Schema{ + "some_threshold": { + Type: TypeFloat, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "some_threshold": "567.8", + }, + }, + + Config: map[string]interface{}{ + "some_threshold": 12.34, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "some_threshold": { + Old: "567.8", + New: "12.34", + }, + }, + }, + + Err: false, + }, + + { + Name: "https://github.com/hashicorp/terraform-plugin-sdk/issues/824", + Schema: map[string]*Schema{ + "block_device": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "device_name": { + Type: TypeString, + Required: true, + }, + "delete_on_termination": { + Type: TypeBool, + Optional: true, + Default: true, + }, + }, + }, + Set: func(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string))) + buf.WriteString(fmt.Sprintf("%t-", m["delete_on_termination"].(bool))) + return hashcode.String(buf.String()) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "block_device.#": "2", + "block_device.616397234.delete_on_termination": "true", + "block_device.616397234.device_name": "/dev/sda1", + "block_device.2801811477.delete_on_termination": "true", + "block_device.2801811477.device_name": "/dev/sdx", + }, + }, + + Config: map[string]interface{}{ + "block_device": []interface{}{ + map[string]interface{}{ + "device_name": "/dev/sda1", + }, + map[string]interface{}{ + "device_name": "/dev/sdx", + }, + }, + }, + Diff: nil, + Err: false, + }, + + { + Name: "Zero value in state shouldn't result in diff", + Schema: map[string]*Schema{ + "port": { + Type: TypeBool, + Optional: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "port": "false", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Same as prev, but for sets", + Schema: map[string]*Schema{ + "route": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": { + Type: TypeInt, + Required: true, + }, + + "gateway": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "route.#": "0", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "A set computed element shouldn't cause a diff", + Schema: map[string]*Schema{ + "active": { + Type: TypeBool, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "active": "true", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "An empty set should show up in the diff", + Schema: map[string]*Schema{ + "instances": { + Type: TypeSet, + Elem: &Schema{Type: TypeString}, + Optional: true, + ForceNew: true, + Set: func(v interface{}) int { + return len(v.(string)) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "instances.#": "1", + "instances.3": "foo", + }, + }, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "instances.#": { + Old: "1", + New: "0", + RequiresNew: true, + }, + "instances.3": { + Old: "foo", + New: "", + NewRemoved: true, + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Map with empty value", + Schema: map[string]*Schema{ + "vars": { + Type: TypeMap, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "vars": map[string]interface{}{ + "foo": "", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "vars.%": { + Old: "0", + New: "1", + }, + "vars.foo": { + Old: "", + New: "", + }, + }, + }, + + Err: false, + }, + + { + Name: "Unset bool, not in state", + Schema: map[string]*Schema{ + "force": { + Type: TypeBool, + Optional: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Unset set, not in state", + Schema: map[string]*Schema{ + "metadata_keys": { + Type: TypeSet, + Optional: true, + ForceNew: true, + Elem: &Schema{Type: TypeInt}, + Set: func(interface{}) int { return 0 }, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Unset list in state, should not show up computed", + Schema: map[string]*Schema{ + "metadata_keys": { + Type: TypeList, + Optional: true, + Computed: true, + ForceNew: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "metadata_keys.#": "0", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Computed map without config that's known to be empty does not generate diff", + Schema: map[string]*Schema{ + "tags": { + Type: TypeMap, + Computed: true, + }, + }, + + Config: nil, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "tags.%": "0", + }, + }, + + Diff: nil, + + Err: false, + }, + + { + Name: "Set with hyphen keys", + Schema: map[string]*Schema{ + "route": { + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": { + Type: TypeInt, + Required: true, + }, + + "gateway-name": { + Type: TypeString, + Optional: true, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "route": []interface{}{ + map[string]interface{}{ + "index": "1", + "gateway-name": "hello", + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "route.#": { + Old: "0", + New: "1", + }, + "route.1.index": { + Old: "", + New: "1", + }, + "route.1.gateway-name": { + Old: "", + New: "hello", + }, + }, + }, + + Err: false, + }, + + { + Name: "StateFunc in nested set (#1759)", + Schema: map[string]*Schema{ + "service_account": { + Type: TypeList, + Optional: true, + ForceNew: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "scopes": { + Type: TypeSet, + Required: true, + ForceNew: true, + Elem: &Schema{ + Type: TypeString, + StateFunc: func(v interface{}) string { + return v.(string) + "!" + }, + }, + Set: func(v interface{}) int { + i, err := strconv.Atoi(v.(string)) + if err != nil { + t.Fatalf("err: %s", err) + } + return i + }, + }, + }, + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "service_account": []interface{}{ + map[string]interface{}{ + "scopes": []interface{}{"123"}, + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "service_account.#": { + Old: "0", + New: "1", + RequiresNew: true, + }, + "service_account.0.scopes.#": { + Old: "0", + New: "1", + RequiresNew: true, + }, + "service_account.0.scopes.123": { + Old: "", + New: "123!", + NewExtra: "123", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Removing set elements", + Schema: map[string]*Schema{ + "instances": { + Type: TypeSet, + Elem: &Schema{Type: TypeString}, + Optional: true, + ForceNew: true, + Set: func(v interface{}) int { + return len(v.(string)) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "instances.#": "2", + "instances.3": "333", + "instances.2": "22", + }, + }, + + Config: map[string]interface{}{ + "instances": []interface{}{"333", "4444"}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "instances.2": { + Old: "22", + New: "", + NewRemoved: true, + RequiresNew: true, + }, + "instances.3": { + Old: "333", + New: "333", + }, + "instances.4": { + Old: "", + New: "4444", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Bools can be set with 0/1 in config, still get true/false", + Schema: map[string]*Schema{ + "one": { + Type: TypeBool, + Optional: true, + }, + "two": { + Type: TypeBool, + Optional: true, + }, + "three": { + Type: TypeBool, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "one": "false", + "two": "true", + "three": "true", + }, + }, + + Config: map[string]interface{}{ + "one": "1", + "two": "0", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "one": { + Old: "false", + New: "true", + }, + "two": { + Old: "true", + New: "false", + }, + "three": { + Old: "true", + New: "false", + NewRemoved: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "tainted in state w/ no attr changes is still a replacement", + Schema: map[string]*Schema{}, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "id": "someid", + }, + Tainted: true, + }, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + DestroyTainted: true, + }, + }, + + { + Name: "Set ForceNew only marks the changing element as ForceNew", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Required: true, + ForceNew: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "ports.#": "3", + "ports.1": "1", + "ports.2": "2", + "ports.4": "4", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{5, 2, 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.1": { + Old: "1", + New: "1", + }, + "ports.2": { + Old: "2", + New: "2", + }, + "ports.5": { + Old: "", + New: "5", + RequiresNew: true, + }, + "ports.4": { + Old: "4", + New: "0", + NewRemoved: true, + RequiresNew: true, + }, + }, + }, + }, + + { + Name: "removed optional items should trigger ForceNew", + Schema: map[string]*Schema{ + "description": { + Type: TypeString, + ForceNew: true, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "description": "foo", + }, + }, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "description": { + Old: "foo", + New: "", + RequiresNew: true, + NewRemoved: true, + }, + }, + }, + + Err: false, + }, + + // GH-7715 + { + Name: "computed value for boolean field", + Schema: map[string]*Schema{ + "foo": { + Type: TypeBool, + ForceNew: true, + Computed: true, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + }, + + Config: map[string]interface{}{ + "foo": hcl2shim.UnknownVariableValue, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": { + Old: "", + New: "false", + NewComputed: true, + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set ForceNew marks count as ForceNew if computed", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Required: true, + ForceNew: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "ports.#": "3", + "ports.1": "1", + "ports.2": "2", + "ports.4": "4", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{hcl2shim.UnknownVariableValue, 2, 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": { + NewComputed: true, + RequiresNew: true, + }, + }, + }, + }, + + { + Name: "List with computed schema and ForceNew", + Schema: map[string]*Schema{ + "config": { + Type: TypeList, + Optional: true, + ForceNew: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "config.#": "2", + "config.0": "a", + "config.1": "b", + }, + }, + + Config: map[string]interface{}{ + "config": []interface{}{hcl2shim.UnknownVariableValue, hcl2shim.UnknownVariableValue}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config.#": { + Old: "2", + New: "", + RequiresNew: true, + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "overridden diff with a CustomizeDiff function, ForceNew not in schema", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + CustomizeDiff: func(_ context.Context, d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("availability_zone", "bar"); err != nil { + return err + } + if err := d.ForceNew("availability_zone"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "bar", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + // NOTE: This case is technically impossible in the current + // implementation, because optional+computed values never show up in the + // diff. In the event behavior changes this test should ensure that the + // intended diff still shows up. + Name: "overridden removed attribute diff with a CustomizeDiff function, ForceNew not in schema", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + CustomizeDiff: func(_ context.Context, d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("availability_zone", "bar"); err != nil { + return err + } + if err := d.ForceNew("availability_zone"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "bar", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + + Name: "overridden diff with a CustomizeDiff function, ForceNew in schema", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + CustomizeDiff: func(_ context.Context, d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("availability_zone", "bar"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "bar", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "required field with computed diff added with CustomizeDiff function", + Schema: map[string]*Schema{ + "ami_id": { + Type: TypeString, + Required: true, + }, + "instance_id": { + Type: TypeString, + Computed: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ami_id": "foo", + }, + + CustomizeDiff: func(_ context.Context, d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("instance_id", "bar"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ami_id": { + Old: "", + New: "foo", + }, + "instance_id": { + Old: "", + New: "bar", + }, + }, + }, + + Err: false, + }, + + { + Name: "Set ForceNew only marks the changing element as ForceNew - CustomizeDiffFunc edition", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "ports.#": "3", + "ports.1": "1", + "ports.2": "2", + "ports.4": "4", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{5, 2, 6}, + }, + + CustomizeDiff: func(_ context.Context, d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("ports", []interface{}{5, 2, 1}); err != nil { + return err + } + if err := d.ForceNew("ports"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.1": { + Old: "1", + New: "1", + }, + "ports.2": { + Old: "2", + New: "2", + }, + "ports.5": { + Old: "", + New: "5", + RequiresNew: true, + }, + "ports.4": { + Old: "4", + New: "0", + NewRemoved: true, + RequiresNew: true, + }, + }, + }, + }, + + { + Name: "tainted resource does not run CustomizeDiffFunc", + Schema: map[string]*Schema{}, + + State: &terraform.InstanceState{ + ID: "someid", + Attributes: map[string]string{ + "id": "someid", + }, + Tainted: true, + }, + + Config: map[string]interface{}{}, + + CustomizeDiff: func(_ context.Context, d *ResourceDiff, meta interface{}) error { + return errors.New("diff customization should not have run") + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + DestroyTainted: true, + }, + + Err: false, + }, + + { + Name: "NewComputed based on a conditional with CustomizeDiffFunc", + Schema: map[string]*Schema{ + "etag": { + Type: TypeString, + Optional: true, + Computed: true, + }, + "version_id": { + Type: TypeString, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "etag": "foo", + "version_id": "1", + }, + }, + + Config: map[string]interface{}{ + "etag": "bar", + }, + + CustomizeDiff: func(_ context.Context, d *ResourceDiff, meta interface{}) error { + if d.HasChange("etag") { + d.SetNewComputed("version_id") + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "etag": { + Old: "foo", + New: "bar", + }, + "version_id": { + Old: "1", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "vetoing a diff", + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "foo": "bar", + }, + }, + + Config: map[string]interface{}{ + "foo": "baz", + }, + + CustomizeDiff: func(_ context.Context, d *ResourceDiff, meta interface{}) error { + return fmt.Errorf("diff vetoed") + }, + + Err: true, + }, + + // A lot of resources currently depended on using the empty string as a + // nil/unset value. + { + Name: "optional, computed, empty string", + Schema: map[string]*Schema{ + "attr": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "attr": "bar", + }, + }, + + Config: map[string]interface{}{ + "attr": "", + }, + }, + + { + Name: "optional, computed, empty string should not crash in CustomizeDiff", + Schema: map[string]*Schema{ + "unrelated_set": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "stream_enabled": { + Type: TypeBool, + Optional: true, + }, + "stream_view_type": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "unrelated_set.#": "0", + "stream_enabled": "true", + "stream_view_type": "KEYS_ONLY", + }, + }, + Config: map[string]interface{}{ + "stream_enabled": false, + "stream_view_type": "", + }, + CustomizeDiff: func(_ context.Context, diff *ResourceDiff, v interface{}) error { + v, ok := diff.GetOk("unrelated_set") + if ok { + return fmt.Errorf("Didn't expect unrelated_set: %#v", v) + } + return nil + }, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "stream_enabled": { + Old: "true", + New: "false", + }, + }, + }, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + c := terraform.NewResourceConfigRaw(tc.Config) + + { + d, err := schemaMap(tc.Schema).Diff(context.Background(), tc.State, c, tc.CustomizeDiff, nil, false) + if err != nil != tc.Err { + t.Fatalf("err: %s", err) + } + if !cmp.Equal(d, tc.Diff, equateEmpty) { + t.Fatal(cmp.Diff(d, tc.Diff, equateEmpty)) + } + } + // up to here is already tested in helper/schema; we're just + // verify that we haven't broken any tests in transition. + + // create a schema from the schemaMap + testSchema := resourceSchemaToBlock(tc.Schema) + + // get our initial state cty.Value + stateVal, err := StateValueFromInstanceState(tc.State, testSchema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + // this is the desired cty.Value from the configuration + configVal := hcl2shim.HCL2ValueFromConfigValue(c.Config) + + // verify that we can round-trip the config + origConfig := hcl2shim.ConfigValueFromHCL2(configVal) + if !cmp.Equal(c.Config, origConfig, equateEmpty) { + t.Fatal(cmp.Diff(c.Config, origConfig, equateEmpty)) + } + + // make sure our config conforms precisely to the schema + configVal, err = testSchema.CoerceValue(configVal) + if err != nil { + t.Fatal(tfdiags.FormatError(err)) + } + + // The new API requires returning the desired state rather than a + // diff, so we need to verify that we can combine the state and + // diff and recreate a new state. + + // now verify that we can create diff, using the new config and state values + // customize isn't run on tainted resources + tainted := tc.State != nil && tc.State.Tainted + if tainted { + tc.CustomizeDiff = nil + } + + res := &Resource{Schema: tc.Schema} + + d, err := diffFromValues(context.Background(), stateVal, configVal, res, tc.CustomizeDiff) + if err != nil { + if !tc.Err { + t.Fatal(err) + } + } + + // In a real "apply" operation there would be no unknown values, + // so for tests containing unknowns we'll stop here: the steps + // after this point apply only to the apply phase. + if !configVal.IsWhollyKnown() { + return + } + + // our diff function can't set DestroyTainted, but match the + // expected value here for the test fixtures + if tainted { + if d == nil { + d = &terraform.InstanceDiff{} + } + d.DestroyTainted = true + } + + if eq, _ := d.Same(tc.Diff); !eq { + t.Fatal(cmp.Diff(d, tc.Diff)) + } + + }) + } +} + +func resourceSchemaToBlock(s map[string]*Schema) *configschema.Block { + return (&Resource{Schema: s}).CoreConfigSchema() +} + +func TestRemoveConfigUnknowns(t *testing.T) { + cfg := map[string]interface{}{ + "id": "74D93920-ED26-11E3-AC10-0800200C9A66", + "route_rules": []interface{}{ + map[string]interface{}{ + "cidr_block": "74D93920-ED26-11E3-AC10-0800200C9A66", + "destination": "0.0.0.0/0", + "destination_type": "CIDR_BLOCK", + "network_entity_id": "1", + }, + map[string]interface{}{ + "cidr_block": "74D93920-ED26-11E3-AC10-0800200C9A66", + "destination": "0.0.0.0/0", + "destination_type": "CIDR_BLOCK", + "sub_block": []interface{}{ + map[string]interface{}{ + "computed": "74D93920-ED26-11E3-AC10-0800200C9A66", + }, + }, + }, + }, + } + + expect := map[string]interface{}{ + "route_rules": []interface{}{ + map[string]interface{}{ + "destination": "0.0.0.0/0", + "destination_type": "CIDR_BLOCK", + "network_entity_id": "1", + }, + map[string]interface{}{ + "destination": "0.0.0.0/0", + "destination_type": "CIDR_BLOCK", + "sub_block": []interface{}{ + map[string]interface{}{}, + }, + }, + }, + } + + removeConfigUnknowns(cfg) + + if !reflect.DeepEqual(cfg, expect) { + t.Fatalf("\nexpected: %#v\ngot: %#v", expect, cfg) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/testing.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/testing.go index 4d0fd736..f345f832 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/testing.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/testing.go @@ -1,20 +1,21 @@ package schema import ( - "testing" + "context" - "github.com/hashicorp/terraform-plugin-sdk/terraform" + testing "github.com/mitchellh/go-testing-interface" + + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) // TestResourceDataRaw creates a ResourceData from a raw configuration map. -func TestResourceDataRaw( - t *testing.T, schema map[string]*Schema, raw map[string]interface{}) *ResourceData { +func TestResourceDataRaw(t testing.T, schema map[string]*Schema, raw map[string]interface{}) *ResourceData { t.Helper() c := terraform.NewResourceConfigRaw(raw) sm := schemaMap(schema) - diff, err := sm.Diff(nil, c, nil, nil, true) + diff, err := sm.Diff(context.Background(), nil, c, nil, nil, true) if err != nil { t.Fatalf("err: %s", err) } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/unknown.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/unknown.go new file mode 100644 index 00000000..c58d3648 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/unknown.go @@ -0,0 +1,132 @@ +package schema + +import ( + "fmt" + + "github.com/hashicorp/go-cty/cty" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" +) + +// SetUnknowns takes a cty.Value, and compares it to the schema setting any null +// values which are computed to unknown. +func SetUnknowns(val cty.Value, schema *configschema.Block) cty.Value { + if !val.IsKnown() { + return val + } + + // If the object was null, we still need to handle the top level attributes + // which might be computed, but we don't need to expand the blocks. + if val.IsNull() { + objMap := map[string]cty.Value{} + allNull := true + for name, attr := range schema.Attributes { + switch { + case attr.Computed: + objMap[name] = cty.UnknownVal(attr.Type) + allNull = false + default: + objMap[name] = cty.NullVal(attr.Type) + } + } + + // If this object has no unknown attributes, then we can leave it null. + if allNull { + return val + } + + return cty.ObjectVal(objMap) + } + + valMap := val.AsValueMap() + newVals := make(map[string]cty.Value) + + for name, attr := range schema.Attributes { + v := valMap[name] + + if attr.Computed && v.IsNull() { + newVals[name] = cty.UnknownVal(attr.Type) + continue + } + + newVals[name] = v + } + + for name, blockS := range schema.BlockTypes { + blockVal := valMap[name] + if blockVal.IsNull() || !blockVal.IsKnown() { + newVals[name] = blockVal + continue + } + + blockValType := blockVal.Type() + blockElementType := blockS.Block.ImpliedType() + + // This switches on the value type here, so we can correctly switch + // between Tuples/Lists and Maps/Objects. + switch { + case blockS.Nesting == configschema.NestingSingle || blockS.Nesting == configschema.NestingGroup: + // NestingSingle is the only exception here, where we treat the + // block directly as an object + newVals[name] = SetUnknowns(blockVal, &blockS.Block) + + case blockValType.IsSetType(), blockValType.IsListType(), blockValType.IsTupleType(): + listVals := blockVal.AsValueSlice() + newListVals := make([]cty.Value, 0, len(listVals)) + + for _, v := range listVals { + newListVals = append(newListVals, SetUnknowns(v, &blockS.Block)) + } + + switch { + case blockValType.IsSetType(): + switch len(newListVals) { + case 0: + newVals[name] = cty.SetValEmpty(blockElementType) + default: + newVals[name] = cty.SetVal(newListVals) + } + case blockValType.IsListType(): + switch len(newListVals) { + case 0: + newVals[name] = cty.ListValEmpty(blockElementType) + default: + newVals[name] = cty.ListVal(newListVals) + } + case blockValType.IsTupleType(): + newVals[name] = cty.TupleVal(newListVals) + } + + case blockValType.IsMapType(), blockValType.IsObjectType(): + mapVals := blockVal.AsValueMap() + newMapVals := make(map[string]cty.Value) + + for k, v := range mapVals { + newMapVals[k] = SetUnknowns(v, &blockS.Block) + } + + switch { + case blockValType.IsMapType(): + switch len(newMapVals) { + case 0: + newVals[name] = cty.MapValEmpty(blockElementType) + default: + newVals[name] = cty.MapVal(newMapVals) + } + case blockValType.IsObjectType(): + if len(newMapVals) == 0 { + // We need to populate empty values to make a valid object. + for attr, ty := range blockElementType.AttributeTypes() { + newMapVals[attr] = cty.NullVal(ty) + } + } + newVals[name] = cty.ObjectVal(newMapVals) + } + + default: + panic(fmt.Sprintf("failed to set unknown values for nested block %q:%#v", name, blockValType)) + } + } + + return cty.ObjectVal(newVals) +} diff --git a/vendor/github.com/hashicorp/terraform/helper/plugin/unknown_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/unknown_test.go similarity index 98% rename from vendor/github.com/hashicorp/terraform/helper/plugin/unknown_test.go rename to vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/unknown_test.go index 4214b184..be279117 100644 --- a/vendor/github.com/hashicorp/terraform/helper/plugin/unknown_test.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/schema/unknown_test.go @@ -1,10 +1,11 @@ -package plugin +package schema import ( "testing" - "github.com/hashicorp/terraform/configs/configschema" - "github.com/zclconf/go-cty/cty" + "github.com/hashicorp/go-cty/cty" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" ) func TestSetUnknowns(t *testing.T) { @@ -67,7 +68,7 @@ func TestSetUnknowns(t *testing.T) { // if the object has no computed attributes, it should stay null &configschema.Block{ Attributes: map[string]*configschema.Attribute{ - "foo": &configschema.Attribute{ + "foo": { Type: cty.String, }, }, @@ -103,7 +104,7 @@ func TestSetUnknowns(t *testing.T) { // the set value should remain null &configschema.Block{ Attributes: map[string]*configschema.Attribute{ - "foo": &configschema.Attribute{ + "foo": { Type: cty.String, Computed: true, }, diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/expand_json.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/expand_json.go new file mode 100644 index 00000000..b3eb90fd --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/expand_json.go @@ -0,0 +1,11 @@ +package structure + +import "encoding/json" + +func ExpandJsonFromString(jsonString string) (map[string]interface{}, error) { + var result map[string]interface{} + + err := json.Unmarshal([]byte(jsonString), &result) + + return result, err +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/expand_json_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/expand_json_test.go new file mode 100644 index 00000000..431cd19e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/expand_json_test.go @@ -0,0 +1,48 @@ +package structure + +import ( + "reflect" + "testing" +) + +func TestExpandJson_emptyString(t *testing.T) { + _, err := ExpandJsonFromString("") + if err == nil { + t.Fatal("Expected to throw an error while Expanding JSON") + } +} + +func TestExpandJson_singleItem(t *testing.T) { + input := `{ + "foo": "bar" + }` + expected := make(map[string]interface{}, 1) + expected["foo"] = "bar" + actual, err := ExpandJsonFromString(input) + if err != nil { + t.Fatalf("Expected not to throw an error while Expanding JSON, but got: %s", err) + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("Got:\n\n%+v\n\nExpected:\n\n%+v\n", actual, expected) + } +} + +func TestExpandJson_multipleItems(t *testing.T) { + input := `{ + "foo": "bar", + "hello": "world" + }` + expected := make(map[string]interface{}, 1) + expected["foo"] = "bar" + expected["hello"] = "world" + + actual, err := ExpandJsonFromString(input) + if err != nil { + t.Fatalf("Expected not to throw an error while Expanding JSON, but got: %s", err) + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("Got:\n\n%+v\n\nExpected:\n\n%+v\n", actual, expected) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/flatten_json.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/flatten_json.go new file mode 100644 index 00000000..578ad2ea --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/flatten_json.go @@ -0,0 +1,16 @@ +package structure + +import "encoding/json" + +func FlattenJsonToString(input map[string]interface{}) (string, error) { + if len(input) == 0 { + return "", nil + } + + result, err := json.Marshal(input) + if err != nil { + return "", err + } + + return string(result), nil +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/flatten_json_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/flatten_json_test.go new file mode 100644 index 00000000..fe131a9d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/flatten_json_test.go @@ -0,0 +1,47 @@ +package structure + +import ( + "testing" +) + +func TestFlattenJson_empty(t *testing.T) { + input := make(map[string]interface{}, 0) + expected := "" + actual, err := FlattenJsonToString(input) + if err != nil { + t.Fatalf("Expected not to throw an error while Flattening JSON, but got: %s", err) + } + + if expected != actual { + t.Fatalf("Got: `%+v`. Expected: `%+v`", actual, expected) + } +} + +func TestFlattenJson_singleItem(t *testing.T) { + input := make(map[string]interface{}, 1) + input["foo"] = "bar" + expected := `{"foo":"bar"}` + actual, err := FlattenJsonToString(input) + if err != nil { + t.Fatalf("Expected not to throw an error while Flattening JSON, but got: %s", err) + } + + if expected != actual { + t.Fatalf("Got: `%+v`. Expected: `%+v`", actual, expected) + } +} + +func TestFlattenJson_multipleItems(t *testing.T) { + input := make(map[string]interface{}, 1) + input["foo"] = "bar" + input["bar"] = "foo" + expected := `{"bar":"foo","foo":"bar"}` + actual, err := FlattenJsonToString(input) + if err != nil { + t.Fatalf("Expected not to throw an error while Flattening JSON, but got: %s", err) + } + + if expected != actual { + t.Fatalf("Got: `%+v`. Expected: `%+v`", actual, expected) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/normalize_json.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/normalize_json.go new file mode 100644 index 00000000..3256b476 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/normalize_json.go @@ -0,0 +1,24 @@ +package structure + +import "encoding/json" + +// Takes a value containing JSON string and passes it through +// the JSON parser to normalize it, returns either a parsing +// error or normalized JSON string. +func NormalizeJsonString(jsonString interface{}) (string, error) { + var j interface{} + + if jsonString == nil || jsonString.(string) == "" { + return "", nil + } + + s := jsonString.(string) + + err := json.Unmarshal([]byte(s), &j) + if err != nil { + return s, err + } + + bytes, _ := json.Marshal(j) + return string(bytes[:]), nil +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/normalize_json_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/normalize_json_test.go new file mode 100644 index 00000000..63f49cad --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/normalize_json_test.go @@ -0,0 +1,91 @@ +package structure + +import ( + "testing" +) + +func TestNormalizeJsonString_valid(t *testing.T) { + // Well formatted and valid. + validJson := `{ + "abc": { + "def": 123, + "xyz": [ + { + "a": "ホリネズミ" + }, + { + "b": "1\\n2" + } + ] + } +}` + expected := `{"abc":{"def":123,"xyz":[{"a":"ホリネズミ"},{"b":"1\\n2"}]}}` + + actual, err := NormalizeJsonString(validJson) + if err != nil { + t.Fatalf("Expected not to throw an error while parsing JSON, but got: %s", err) + } + + if actual != expected { + t.Fatalf("Got:\n\n%s\n\nExpected:\n\n%s\n", actual, expected) + } + + // Well formatted but not valid, + // missing closing square bracket. + invalidJson := `{ + "abc": { + "def": 123, + "xyz": [ + { + "a": "1" + } + } + } +}` + actual, err = NormalizeJsonString(invalidJson) + if err == nil { + t.Fatalf("Expected to throw an error while parsing JSON, but got: %s", err) + } + + // We expect the invalid JSON to be shown back to us again. + if actual != invalidJson { + t.Fatalf("Got:\n\n%s\n\nExpected:\n\n%s\n", actual, invalidJson) + } + + // Verify that it leaves strings alone + testString := "2016-07-28t04:07:02z\nsomething else" + expected = "2016-07-28t04:07:02z\nsomething else" + actual, err = NormalizeJsonString(testString) + if err == nil { + t.Fatalf("Expected to throw an error while parsing JSON, but got: %s", err) + } + + if actual != expected { + t.Fatalf("Got:\n\n%s\n\nExpected:\n\n%s\n", actual, expected) + } +} + +func TestNormalizeJsonString_invalid(t *testing.T) { + // Well formatted but not valid, + // missing closing squre bracket. + invalidJson := `{ + "abc": { + "def": 123, + "xyz": [ + { + "a": "1" + } + } + } +}` + expected := `{"abc":{"def":123,"xyz":[{"a":"ホリネズミ"},{"b":"1\\n2"}]}}` + actual, err := NormalizeJsonString(invalidJson) + if err == nil { + t.Fatalf("Expected to throw an error while parsing JSON, but got: %s", err) + } + + // We expect the invalid JSON to be shown back to us again. + if actual != invalidJson { + t.Fatalf("Got:\n\n%s\n\nExpected:\n\n%s\n", expected, invalidJson) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/suppress_json_diff.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/suppress_json_diff.go new file mode 100644 index 00000000..741ca0ac --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/suppress_json_diff.go @@ -0,0 +1,21 @@ +package structure + +import ( + "reflect" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func SuppressJsonDiff(k, old, new string, d *schema.ResourceData) bool { + oldMap, err := ExpandJsonFromString(old) + if err != nil { + return false + } + + newMap, err := ExpandJsonFromString(new) + if err != nil { + return false + } + + return reflect.DeepEqual(oldMap, newMap) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/suppress_json_diff_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/suppress_json_diff_test.go new file mode 100644 index 00000000..c017981b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/structure/suppress_json_diff_test.go @@ -0,0 +1,51 @@ +package structure + +import ( + "testing" +) + +func TestSuppressJsonDiff_same(t *testing.T) { + original := `{ "enabled": true }` + new := `{ "enabled": true }` + expected := true + + actual := SuppressJsonDiff("test", original, new, nil) + if actual != expected { + t.Fatal("[ERROR] Identical JSON values shouldn't cause a diff") + } +} + +func TestSuppressJsonDiff_sameWithWhitespace(t *testing.T) { + original := `{ + "enabled": true + }` + new := `{ "enabled": true }` + expected := true + + actual := SuppressJsonDiff("test", original, new, nil) + if actual != expected { + t.Fatal("[ERROR] Identical JSON values shouldn't cause a diff") + } +} + +func TestSuppressJsonDiff_differentValue(t *testing.T) { + original := `{ "enabled": true }` + new := `{ "enabled": false }` + expected := false + + actual := SuppressJsonDiff("test", original, new, nil) + if actual != expected { + t.Fatal("[ERROR] Different JSON values should cause a diff") + } +} + +func TestSuppressJsonDiff_newValue(t *testing.T) { + original := `{ "enabled": true }` + new := `{ "enabled": false, "world": "round" }` + expected := false + + actual := SuppressJsonDiff("test", original, new, nil) + if actual != expected { + t.Fatal("[ERROR] Different JSON values should cause a diff") + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/float.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/float.go new file mode 100644 index 00000000..05a30531 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/float.go @@ -0,0 +1,64 @@ +package validation + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// FloatBetween returns a SchemaValidateFunc which tests if the provided value +// is of type float64 and is between min and max (inclusive). +func FloatBetween(min, max float64) schema.SchemaValidateFunc { + return func(i interface{}, k string) (s []string, es []error) { + v, ok := i.(float64) + if !ok { + es = append(es, fmt.Errorf("expected type of %s to be float64", k)) + return + } + + if v < min || v > max { + es = append(es, fmt.Errorf("expected %s to be in the range (%f - %f), got %f", k, min, max, v)) + return + } + + return + } +} + +// FloatAtLeast returns a SchemaValidateFunc which tests if the provided value +// is of type float and is at least min (inclusive) +func FloatAtLeast(min float64) schema.SchemaValidateFunc { + return func(i interface{}, k string) (s []string, es []error) { + v, ok := i.(float64) + if !ok { + es = append(es, fmt.Errorf("expected type of %s to be float", k)) + return + } + + if v < min { + es = append(es, fmt.Errorf("expected %s to be at least (%f), got %f", k, min, v)) + return + } + + return + } +} + +// FloatAtMost returns a SchemaValidateFunc which tests if the provided value +// is of type float and is at most max (inclusive) +func FloatAtMost(max float64) schema.SchemaValidateFunc { + return func(i interface{}, k string) (s []string, es []error) { + v, ok := i.(float64) + if !ok { + es = append(es, fmt.Errorf("expected type of %s to be float", k)) + return + } + + if v > max { + es = append(es, fmt.Errorf("expected %s to be at most (%f), got %f", k, max, v)) + return + } + + return + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/float_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/float_test.go new file mode 100644 index 00000000..f268747a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/float_test.go @@ -0,0 +1,97 @@ +package validation + +import ( + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func TestValidateFloatBetween(t *testing.T) { + cases := map[string]struct { + Value interface{} + ValidateFunc schema.SchemaValidateFunc + ExpectValidationErrors bool + }{ + "accept valid value": { + Value: 1.5, + ValidateFunc: FloatBetween(1.0, 2.0), + ExpectValidationErrors: false, + }, + "accept valid value inclusive upper bound": { + Value: 1.0, + ValidateFunc: FloatBetween(0.0, 1.0), + ExpectValidationErrors: false, + }, + "accept valid value inclusive lower bound": { + Value: 0.0, + ValidateFunc: FloatBetween(0.0, 1.0), + ExpectValidationErrors: false, + }, + "reject out of range value": { + Value: -1.0, + ValidateFunc: FloatBetween(0.0, 1.0), + ExpectValidationErrors: true, + }, + "reject incorrectly typed value": { + Value: 1, + ValidateFunc: FloatBetween(0.0, 1.0), + ExpectValidationErrors: true, + }, + } + + for tn, tc := range cases { + _, errors := tc.ValidateFunc(tc.Value, tn) + if len(errors) > 0 && !tc.ExpectValidationErrors { + t.Errorf("%s: unexpected errors %s", tn, errors) + } else if len(errors) == 0 && tc.ExpectValidationErrors { + t.Errorf("%s: expected errors but got none", tn) + } + } +} + +func TestValidateFloatAtLeast(t *testing.T) { + runTestCases(t, []testCase{ + { + val: 2.5, + f: FloatAtLeast(1.5), + }, + { + val: -1.0, + f: FloatAtLeast(-1.5), + }, + { + val: 1.5, + f: FloatAtLeast(2.5), + expectedErr: regexp.MustCompile("expected [\\w]+ to be at least \\(2\\.5\\d*\\), got 1\\.5\\d*"), + }, + { + val: "2.5", + f: FloatAtLeast(1.5), + expectedErr: regexp.MustCompile("expected type of [\\w]+ to be float"), + }, + }) +} + +func TestValidateFloatAtMost(t *testing.T) { + runTestCases(t, []testCase{ + { + val: 2.5, + f: FloatAtMost(3.5), + }, + { + val: -1.0, + f: FloatAtMost(-0.5), + }, + { + val: 2.5, + f: FloatAtMost(1.5), + expectedErr: regexp.MustCompile("expected [\\w]+ to be at most \\(1\\.5\\d*\\), got 2\\.5\\d*"), + }, + { + val: "2.5", + f: FloatAtMost(3.5), + expectedErr: regexp.MustCompile("expected type of [\\w]+ to be float"), + }, + }) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/int.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/int.go new file mode 100644 index 00000000..f2738201 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/int.go @@ -0,0 +1,125 @@ +package validation + +import ( + "fmt" + "math" + + "github.com/hashicorp/terraform-plugin-sdk/v2/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) (warnings []string, errors []error) { + v, ok := i.(int) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be integer", k)) + return warnings, errors + } + + if v < min || v > max { + errors = append(errors, fmt.Errorf("expected %s to be in the range (%d - %d), got %d", k, min, max, v)) + return warnings, errors + } + + return warnings, errors + } +} + +// IntAtLeast returns a SchemaValidateFunc which tests if the provided value +// is of type int and is at least min (inclusive) +func IntAtLeast(min int) schema.SchemaValidateFunc { + return func(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(int) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be integer", k)) + return warnings, errors + } + + if v < min { + errors = append(errors, fmt.Errorf("expected %s to be at least (%d), got %d", k, min, v)) + return warnings, errors + } + + return warnings, errors + } +} + +// IntAtMost returns a SchemaValidateFunc which tests if the provided value +// is of type int and is at most max (inclusive) +func IntAtMost(max int) schema.SchemaValidateFunc { + return func(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(int) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be integer", k)) + return warnings, errors + } + + if v > max { + errors = append(errors, fmt.Errorf("expected %s to be at most (%d), got %d", k, max, v)) + return warnings, errors + } + + return warnings, errors + } +} + +// IntDivisibleBy returns a SchemaValidateFunc which tests if the provided value +// is of type int and is divisible by a given number +func IntDivisibleBy(divisor int) schema.SchemaValidateFunc { + return func(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(int) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be integer", k)) + return warnings, errors + } + + if math.Mod(float64(v), float64(divisor)) != 0 { + errors = append(errors, fmt.Errorf("expected %s to be divisible by %d, got: %v", k, divisor, i)) + return warnings, errors + } + + return warnings, errors + } +} + +// IntInSlice returns a SchemaValidateFunc which tests if the provided value +// is of type int and matches the value of an element in the valid slice +func IntInSlice(valid []int) schema.SchemaValidateFunc { + return func(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(int) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be integer", k)) + return warnings, errors + } + + for _, validInt := range valid { + if v == validInt { + return warnings, errors + } + } + + errors = append(errors, fmt.Errorf("expected %s to be one of %v, got %d", k, valid, v)) + return warnings, errors + } +} + +// IntNotInSlice returns a SchemaValidateFunc which tests if the provided value +// is of type int and matches the value of an element in the valid slice +func IntNotInSlice(valid []int) schema.SchemaValidateFunc { + return func(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(int) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be integer", k)) + return warnings, errors + } + + for _, validInt := range valid { + if v == validInt { + errors = append(errors, fmt.Errorf("expected %s to not be one of %v, got %d", k, valid, v)) + } + } + + return warnings, errors + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/int_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/int_test.go new file mode 100644 index 00000000..935da204 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/int_test.go @@ -0,0 +1,176 @@ +package validation + +import ( + "regexp" + "testing" +) + +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 integer"), + }, + }) +} + +func TestValidationIntAtLeast(t *testing.T) { + runTestCases(t, []testCase{ + { + val: 1, + f: IntAtLeast(1), + }, + { + val: 1, + f: IntAtLeast(0), + }, + { + val: 1, + f: IntAtLeast(2), + expectedErr: regexp.MustCompile("expected [\\w]+ to be at least \\(2\\), got 1"), + }, + { + val: "1", + f: IntAtLeast(2), + expectedErr: regexp.MustCompile("expected type of [\\w]+ to be integer"), + }, + }) +} + +func TestValidationIntAtMost(t *testing.T) { + runTestCases(t, []testCase{ + { + val: 1, + f: IntAtMost(1), + }, + { + val: 1, + f: IntAtMost(2), + }, + { + val: 1, + f: IntAtMost(0), + expectedErr: regexp.MustCompile("expected [\\w]+ to be at most \\(0\\), got 1"), + }, + { + val: "1", + f: IntAtMost(0), + expectedErr: regexp.MustCompile("expected type of [\\w]+ to be integer"), + }, + }) +} + +func TestValidationIntDivisibleBy(t *testing.T) { + cases := map[string]struct { + Value interface{} + Divisor int + Error bool + }{ + "NotInt": { + Value: "words", + Divisor: 2, + Error: true, + }, + "NotDivisible": { + Value: 15, + Divisor: 7, + Error: true, + }, + "Divisible": { + Value: 14, + Divisor: 7, + Error: false, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := IntDivisibleBy(tc.Divisor)(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("IntDivisibleBy(%v) produced an unexpected error for %v", tc.Divisor, tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("IntDivisibleBy(%v) did not error for %v", tc.Divisor, tc.Value) + } + }) + } +} + +func TestValidationIntInSlice(t *testing.T) { + runTestCases(t, []testCase{ + { + val: 42, + f: IntInSlice([]int{1, 42}), + }, + { + val: 42, + f: IntInSlice([]int{10, 20}), + expectedErr: regexp.MustCompile("expected [\\w]+ to be one of \\[10 20\\], got 42"), + }, + { + val: "InvalidValue", + f: IntInSlice([]int{10, 20}), + expectedErr: regexp.MustCompile("expected type of [\\w]+ to be integer"), + }, + }) +} + +func TestValidationIntNotInSlice(t *testing.T) { + cases := map[string]struct { + Value interface{} + Slice []int + Error bool + }{ + "NotInt": { + Value: "words", + Slice: []int{7, 77}, + Error: true, + }, + "NotInSlice": { + Value: 1, + Slice: []int{7, 77}, + Error: false, + }, + "InSlice": { + Value: 7, + Slice: []int{7, 77}, + Error: true, + }, + "InSliceOfOne": { + Value: 7, + Slice: []int{7}, + Error: true, + }, + "NotInSliceOfOne": { + Value: 1, + Slice: []int{7}, + Error: false, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := IntNotInSlice(tc.Slice)(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("IntNotInSlice(%v) produced an unexpected error for %v", tc.Slice, tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("IntNotInSlice(%v) did not error for %v", tc.Slice, tc.Value) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/list.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/list.go new file mode 100644 index 00000000..75702b5b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/list.go @@ -0,0 +1,32 @@ +package validation + +import "fmt" + +// ListOfUniqueStrings is a ValidateFunc that ensures a list has no +// duplicate items in it. It's useful for when a list is needed over a set +// because order matters, yet the items still need to be unique. +func ListOfUniqueStrings(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.([]interface{}) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be List", k)) + return warnings, errors + } + + for _, e := range v { + if _, eok := e.(string); !eok { + errors = append(errors, fmt.Errorf("expected %q to only contain string elements, found :%v", k, e)) + return warnings, errors + } + } + + for n1, i1 := range v { + for n2, i2 := range v { + if i1.(string) == i2.(string) && n1 != n2 { + errors = append(errors, fmt.Errorf("expected %q to not have duplicates: found 2 or more of %v", k, i1)) + return warnings, errors + } + } + } + + return warnings, errors +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/list_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/list_test.go new file mode 100644 index 00000000..3f15b984 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/list_test.go @@ -0,0 +1,41 @@ +package validation + +import ( + "testing" +) + +func TestValidationListOfUniqueStrings(t *testing.T) { + cases := map[string]struct { + Value interface{} + Error bool + }{ + "NotList": { + Value: "the list is a lie", + Error: true, + }, + "NotListOfString": { + Value: []interface{}{"seven", 7}, + Error: true, + }, + "NonUniqueStrings": { + Value: []interface{}{"kt", "is", "kt"}, + Error: true, + }, + "UniqueStrings": { + Value: []interface{}{"thanks", "for", "all", "the", "fish"}, + Error: false, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := ListOfUniqueStrings(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("ListOfUniqueStrings(%s) produced an unexpected error", tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("ListOfUniqueStrings(%s) did not error", tc.Value) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/map.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/map.go new file mode 100644 index 00000000..3e8068a8 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/map.go @@ -0,0 +1,155 @@ +package validation + +import ( + "fmt" + "regexp" + "sort" + + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// MapKeyLenBetween returns a SchemaValidateDiagFunc which tests if the provided value +// is of type map and the length of all keys are between min and max (inclusive) +func MapKeyLenBetween(min, max int) schema.SchemaValidateDiagFunc { + return func(v interface{}, path cty.Path) diag.Diagnostics { + var diags diag.Diagnostics + + for _, key := range sortedKeys(v.(map[string]interface{})) { + len := len(key) + if len < min || len > max { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Bad map key length", + Detail: fmt.Sprintf("Map key lengths should be in the range (%d - %d): %s (length = %d)", min, max, key, len), + AttributePath: append(path, cty.IndexStep{Key: cty.StringVal(key)}), + }) + } + } + + return diags + } +} + +// MapValueLenBetween returns a SchemaValidateDiagFunc which tests if the provided value +// is of type map and the length of all values are between min and max (inclusive) +func MapValueLenBetween(min, max int) schema.SchemaValidateDiagFunc { + return func(v interface{}, path cty.Path) diag.Diagnostics { + var diags diag.Diagnostics + + m := v.(map[string]interface{}) + + for _, key := range sortedKeys(m) { + val := m[key] + + if _, ok := val.(string); !ok { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Bad map value type", + Detail: fmt.Sprintf("Map values should be strings: %s => %v (type = %T)", key, val, val), + AttributePath: append(path, cty.IndexStep{Key: cty.StringVal(key)}), + }) + continue + } + + len := len(val.(string)) + if len < min || len > max { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Bad map value length", + Detail: fmt.Sprintf("Map value lengths should be in the range (%d - %d): %s => %v (length = %d)", min, max, key, val, len), + AttributePath: append(path, cty.IndexStep{Key: cty.StringVal(key)}), + }) + } + } + + return diags + } +} + +// MapKeyMatch returns a SchemaValidateDiagFunc which tests if the provided value +// is of type map and all keys match a given regexp. Optionally an error message +// can be provided to return something friendlier than "expected to match some globby regexp". +func MapKeyMatch(r *regexp.Regexp, message string) schema.SchemaValidateDiagFunc { + return func(v interface{}, path cty.Path) diag.Diagnostics { + var diags diag.Diagnostics + + for _, key := range sortedKeys(v.(map[string]interface{})) { + if ok := r.MatchString(key); !ok { + var detail string + if message == "" { + detail = fmt.Sprintf("Map key expected to match regular expression %q: %s", r, key) + } else { + detail = fmt.Sprintf("%s: %s", message, key) + } + + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Invalid map key", + Detail: detail, + AttributePath: append(path, cty.IndexStep{Key: cty.StringVal(key)}), + }) + } + } + + return diags + } +} + +// MapValueMatch returns a SchemaValidateDiagFunc which tests if the provided value +// is of type map and all values match a given regexp. Optionally an error message +// can be provided to return something friendlier than "expected to match some globby regexp". +func MapValueMatch(r *regexp.Regexp, message string) schema.SchemaValidateDiagFunc { + return func(v interface{}, path cty.Path) diag.Diagnostics { + var diags diag.Diagnostics + + m := v.(map[string]interface{}) + + for _, key := range sortedKeys(m) { + val := m[key] + + if _, ok := val.(string); !ok { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Bad map value type", + Detail: fmt.Sprintf("Map values should be strings: %s => %v (type = %T)", key, val, val), + AttributePath: append(path, cty.IndexStep{Key: cty.StringVal(key)}), + }) + continue + } + + if ok := r.MatchString(val.(string)); !ok { + var detail string + if message == "" { + detail = fmt.Sprintf("Map value expected to match regular expression %q: %s => %v", r, key, val) + } else { + detail = fmt.Sprintf("%s: %s => %v", message, key, val) + } + + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Invalid map value", + Detail: detail, + AttributePath: append(path, cty.IndexStep{Key: cty.StringVal(key)}), + }) + } + } + + return diags + } +} + +func sortedKeys(m map[string]interface{}) []string { + keys := make([]string, len(m)) + + i := 0 + for key := range m { + keys[i] = key + i++ + } + + sort.Strings(keys) + + return keys +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/map_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/map_test.go new file mode 100644 index 00000000..45e04264 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/map_test.go @@ -0,0 +1,269 @@ +package validation + +import ( + "regexp" + "testing" + + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" +) + +func TestValidationMapKeyLenBetween(t *testing.T) { + cases := map[string]struct { + Value interface{} + ExpectedDiags diag.Diagnostics + }{ + "TooLong": { + Value: map[string]interface{}{ + "ABC": "123", + "UVWXYZ": "123456", + }, + ExpectedDiags: diag.Diagnostics{ + { + Severity: diag.Error, + AttributePath: append(cty.Path{}, cty.IndexStep{Key: cty.StringVal("UVWXYZ")}), + }, + }, + }, + "TooShort": { + Value: map[string]interface{}{ + "ABC": "123", + "U": "1", + }, + ExpectedDiags: diag.Diagnostics{ + { + Severity: diag.Error, + AttributePath: append(cty.Path{}, cty.IndexStep{Key: cty.StringVal("U")}), + }, + }, + }, + "TooLongAndTooShort": { + Value: map[string]interface{}{ + "UVWXYZ": "123456", + "ABC": "123", + "U": "1", + }, + ExpectedDiags: diag.Diagnostics{ + { + Severity: diag.Error, + AttributePath: append(cty.Path{}, cty.IndexStep{Key: cty.StringVal("U")}), + }, + { + Severity: diag.Error, + AttributePath: append(cty.Path{}, cty.IndexStep{Key: cty.StringVal("UVWXYZ")}), + }, + }, + }, + "AllGood": { + Value: map[string]interface{}{ + "AB": "12", + "UVWXY": "12345", + }, + ExpectedDiags: nil, + }, + } + + fn := MapKeyLenBetween(2, 5) + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + diags := fn(tc.Value, cty.Path{}) + + checkDiagnostics(t, tn, diags, tc.ExpectedDiags) + }) + } +} + +func TestValidationMapValueLenBetween(t *testing.T) { + cases := map[string]struct { + Value interface{} + ExpectedDiags diag.Diagnostics + }{ + "NotStringValue": { + Value: map[string]interface{}{ + "ABC": "123", + "UVWXYZ": 123456, + }, + ExpectedDiags: diag.Diagnostics{ + { + Severity: diag.Error, + AttributePath: append(cty.Path{}, cty.IndexStep{Key: cty.StringVal("UVWXYZ")}), + }, + }, + }, + "TooLong": { + Value: map[string]interface{}{ + "ABC": "123", + "UVWXYZ": "123456", + }, + ExpectedDiags: diag.Diagnostics{ + { + Severity: diag.Error, + AttributePath: append(cty.Path{}, cty.IndexStep{Key: cty.StringVal("UVWXYZ")}), + }, + }, + }, + "TooShort": { + Value: map[string]interface{}{ + "ABC": "123", + "U": "1", + }, + ExpectedDiags: diag.Diagnostics{ + { + Severity: diag.Error, + AttributePath: append(cty.Path{}, cty.IndexStep{Key: cty.StringVal("U")}), + }, + }, + }, + "TooLongAndTooShort": { + Value: map[string]interface{}{ + "UVWXYZ": "123456", + "ABC": "123", + "U": "1", + }, + ExpectedDiags: diag.Diagnostics{ + { + Severity: diag.Error, + AttributePath: append(cty.Path{}, cty.IndexStep{Key: cty.StringVal("U")}), + }, + { + Severity: diag.Error, + AttributePath: append(cty.Path{}, cty.IndexStep{Key: cty.StringVal("UVWXYZ")}), + }, + }, + }, + "AllGood": { + Value: map[string]interface{}{ + "AB": "12", + "UVWXY": "12345", + }, + ExpectedDiags: nil, + }, + } + + fn := MapValueLenBetween(2, 5) + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + diags := fn(tc.Value, cty.Path{}) + + checkDiagnostics(t, tn, diags, tc.ExpectedDiags) + }) + } +} + +func TestValidationMapKeyMatch(t *testing.T) { + cases := map[string]struct { + Value interface{} + ExpectedDiags diag.Diagnostics + }{ + "NoMatch": { + Value: map[string]interface{}{ + "ABC": "123", + "UVWXYZ": "123456", + }, + ExpectedDiags: diag.Diagnostics{ + { + Severity: diag.Error, + AttributePath: append(cty.Path{}, cty.IndexStep{Key: cty.StringVal("UVWXYZ")}), + }, + }, + }, + "AllGood": { + Value: map[string]interface{}{ + "AB": "12", + "UVABY": "12345", + }, + ExpectedDiags: nil, + }, + } + + fn := MapKeyMatch(regexp.MustCompile(".*AB.*"), "") + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + diags := fn(tc.Value, cty.Path{}) + + checkDiagnostics(t, tn, diags, tc.ExpectedDiags) + }) + } +} + +func TestValidationValueKeyMatch(t *testing.T) { + cases := map[string]struct { + Value interface{} + ExpectedDiags diag.Diagnostics + }{ + "NotStringValue": { + Value: map[string]interface{}{ + "MNO": "ABC", + "UVWXYZ": 123456, + }, + ExpectedDiags: diag.Diagnostics{ + { + Severity: diag.Error, + AttributePath: append(cty.Path{}, cty.IndexStep{Key: cty.StringVal("UVWXYZ")}), + }, + }, + }, + "NoMatch": { + Value: map[string]interface{}{ + "MNO": "ABC", + "UVWXYZ": "UVWXYZ", + }, + ExpectedDiags: diag.Diagnostics{ + { + Severity: diag.Error, + AttributePath: append(cty.Path{}, cty.IndexStep{Key: cty.StringVal("UVWXYZ")}), + }, + }, + }, + "BothBad": { + Value: map[string]interface{}{ + "MNO": "123", + "UVWXYZ": 123456, + }, + ExpectedDiags: diag.Diagnostics{ + { + Severity: diag.Error, + AttributePath: append(cty.Path{}, cty.IndexStep{Key: cty.StringVal("MNO")}), + }, + { + Severity: diag.Error, + AttributePath: append(cty.Path{}, cty.IndexStep{Key: cty.StringVal("UVWXYZ")}), + }, + }, + }, + "AllGood": { + Value: map[string]interface{}{ + "MNO": "ABC", + "UVWXYZ": "UVABY", + }, + ExpectedDiags: nil, + }, + } + + fn := MapValueMatch(regexp.MustCompile(".*AB.*"), "") + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + diags := fn(tc.Value, cty.Path{}) + + checkDiagnostics(t, tn, diags, tc.ExpectedDiags) + }) + } +} + +func checkDiagnostics(t *testing.T, tn string, got, expected diag.Diagnostics) { + if len(got) != len(expected) { + t.Fatalf("%s: wrong number of diags, expected %d, got %d", tn, len(expected), len(got)) + } + for j := range got { + if got[j].Severity != expected[j].Severity { + t.Fatalf("%s: expected severity %v, got %v", tn, expected[j].Severity, got[j].Severity) + } + if !got[j].AttributePath.Equals(expected[j].AttributePath) { + t.Fatalf("%s: attribute paths do not match expected: %v, got %v", tn, expected[j].AttributePath, got[j].AttributePath) + } + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/meta.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/meta.go new file mode 100644 index 00000000..f1376c2d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/meta.go @@ -0,0 +1,88 @@ +package validation + +import ( + "fmt" + "reflect" + + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// NoZeroValues is a SchemaValidateFunc which tests if the provided value is +// not a zero value. It's useful in situations where you want to catch +// explicit zero values on things like required fields during validation. +func NoZeroValues(i interface{}, k string) (s []string, es []error) { + if reflect.ValueOf(i).Interface() == reflect.Zero(reflect.TypeOf(i)).Interface() { + switch reflect.TypeOf(i).Kind() { + case reflect.String: + es = append(es, fmt.Errorf("%s must not be empty, got %v", k, i)) + case reflect.Int, reflect.Float64: + es = append(es, fmt.Errorf("%s must not be zero, got %v", k, i)) + default: + // this validator should only ever be applied to TypeString, TypeInt and TypeFloat + panic(fmt.Errorf("can't use NoZeroValues with %T attribute %s", i, k)) + } + } + return +} + +// All returns a SchemaValidateFunc which tests if the provided value +// passes all provided SchemaValidateFunc +func All(validators ...schema.SchemaValidateFunc) schema.SchemaValidateFunc { + return func(i interface{}, k string) ([]string, []error) { + var allErrors []error + var allWarnings []string + for _, validator := range validators { + validatorWarnings, validatorErrors := validator(i, k) + allWarnings = append(allWarnings, validatorWarnings...) + allErrors = append(allErrors, validatorErrors...) + } + return allWarnings, allErrors + } +} + +// Any returns a SchemaValidateFunc which tests if the provided value +// passes any of the provided SchemaValidateFunc +func Any(validators ...schema.SchemaValidateFunc) schema.SchemaValidateFunc { + return func(i interface{}, k string) ([]string, []error) { + var allErrors []error + var allWarnings []string + for _, validator := range validators { + validatorWarnings, validatorErrors := validator(i, k) + if len(validatorWarnings) == 0 && len(validatorErrors) == 0 { + return []string{}, []error{} + } + allWarnings = append(allWarnings, validatorWarnings...) + allErrors = append(allErrors, validatorErrors...) + } + return allWarnings, allErrors + } +} + +// ToDiagFunc is a wrapper for legacy schema.SchemaValidateFunc +// converting it to schema.SchemaValidateDiagFunc +func ToDiagFunc(validator schema.SchemaValidateFunc) schema.SchemaValidateDiagFunc { + return func(i interface{}, p cty.Path) diag.Diagnostics { + var diags diag.Diagnostics + + attr := p[len(p)-1].(cty.GetAttrStep) + ws, es := validator(i, attr.Name) + + for _, w := range ws { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: w, + AttributePath: p, + }) + } + for _, e := range es { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: e.Error(), + AttributePath: p, + }) + } + return diags + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/meta_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/meta_test.go new file mode 100644 index 00000000..398dcb06 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/meta_test.go @@ -0,0 +1,136 @@ +package validation + +import ( + "regexp" + "testing" +) + +func TestValidationNoZeroValues(t *testing.T) { + runTestCases(t, []testCase{ + { + val: "foo", + f: NoZeroValues, + }, + { + val: 1, + f: NoZeroValues, + }, + { + val: float64(1), + f: NoZeroValues, + }, + { + val: "", + f: NoZeroValues, + expectedErr: regexp.MustCompile("must not be empty"), + }, + { + val: 0, + f: NoZeroValues, + expectedErr: regexp.MustCompile("must not be zero"), + }, + { + val: float64(0), + f: NoZeroValues, + expectedErr: regexp.MustCompile("must not be zero"), + }, + }) +} + +func TestValidationAll(t *testing.T) { + runTestCases(t, []testCase{ + { + val: "valid", + f: All( + StringLenBetween(5, 42), + StringMatch(regexp.MustCompile(`[a-zA-Z0-9]+`), "value must be alphanumeric"), + ), + }, + { + val: "foo", + f: All( + StringLenBetween(5, 42), + StringMatch(regexp.MustCompile(`[a-zA-Z0-9]+`), "value must be alphanumeric"), + ), + expectedErr: regexp.MustCompile("expected length of [\\w]+ to be in the range \\(5 - 42\\), got foo"), + }, + { + val: "!!!!!", + f: All( + StringLenBetween(5, 42), + StringMatch(regexp.MustCompile(`[a-zA-Z0-9]+`), "value must be alphanumeric"), + ), + expectedErr: regexp.MustCompile("value must be alphanumeric"), + }, + }) +} + +func TestValidationAny(t *testing.T) { + runTestCases(t, []testCase{ + { + val: 43, + f: Any( + IntAtLeast(42), + IntAtMost(5), + ), + }, + { + val: 4, + f: Any( + IntAtLeast(42), + IntAtMost(5), + ), + }, + { + val: 7, + f: Any( + IntAtLeast(42), + IntAtMost(5), + ), + expectedErr: regexp.MustCompile("expected [\\w]+ to be at least \\(42\\), got 7"), + }, + { + val: 7, + f: Any( + IntAtLeast(42), + IntAtMost(5), + ), + expectedErr: regexp.MustCompile("expected [\\w]+ to be at most \\(5\\), got 7"), + }, + }) +} + +func TestToDiagFunc(t *testing.T) { + runDiagTestCases(t, []diagTestCase{ + { + val: 43, + f: ToDiagFunc(Any( + IntAtLeast(42), + IntAtMost(5), + )), + }, + { + val: "foo", + f: ToDiagFunc(All( + StringLenBetween(1, 10), + StringIsNotWhiteSpace, + )), + }, + { + val: 7, + f: ToDiagFunc(Any( + IntAtLeast(42), + IntAtMost(5), + )), + expectedErr: regexp.MustCompile("expected [\\w]+ to be at least \\(42\\), got 7"), + }, + { + val: 7, + f: ToDiagFunc(Any( + IntAtLeast(42), + IntAtMost(5), + )), + expectedErr: regexp.MustCompile("expected [\\w]+ to be at most \\(5\\), got 7"), + }, + }) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/network.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/network.go new file mode 100644 index 00000000..8aa7139a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/network.go @@ -0,0 +1,171 @@ +package validation + +import ( + "bytes" + "fmt" + "net" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// IsIPAddress is a SchemaValidateFunc which tests if the provided value is of type string and is a single IP (v4 or v6) +func IsIPAddress(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return warnings, errors + } + + ip := net.ParseIP(v) + if ip == nil { + errors = append(errors, fmt.Errorf("expected %s to contain a valid IP, got: %s", k, v)) + } + + return warnings, errors +} + +// IsIPv6Address is a SchemaValidateFunc which tests if the provided value is of type string and a valid IPv6 address +func IsIPv6Address(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return warnings, errors + } + + ip := net.ParseIP(v) + if six := ip.To16(); six == nil { + errors = append(errors, fmt.Errorf("expected %s to contain a valid IPv6 address, got: %s", k, v)) + } + + return warnings, errors +} + +// IsIPv4Address is a SchemaValidateFunc which tests if the provided value is of type string and a valid IPv4 address +func IsIPv4Address(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return warnings, errors + } + + ip := net.ParseIP(v) + if four := ip.To4(); four == nil { + errors = append(errors, fmt.Errorf("expected %s to contain a valid IPv4 address, got: %s", k, v)) + } + + return warnings, errors +} + +// IsIPv4Range is a SchemaValidateFunc which tests if the provided value is of type string, and in valid IP range +func IsIPv4Range(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return warnings, errors + } + + ips := strings.Split(v, "-") + if len(ips) != 2 { + errors = append(errors, fmt.Errorf("expected %s to contain a valid IP range, got: %s", k, v)) + return warnings, errors + } + + ip1 := net.ParseIP(ips[0]) + ip2 := net.ParseIP(ips[1]) + if ip1 == nil || ip2 == nil || bytes.Compare(ip1, ip2) > 0 { + errors = append(errors, fmt.Errorf("expected %s to contain a valid IP range, got: %s", k, v)) + } + + return warnings, errors +} + +// IsCIDR is a SchemaValidateFunc which tests if the provided value is of type string and a valid CIDR +func IsCIDR(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return warnings, errors + } + + if _, _, err := net.ParseCIDR(v); err != nil { + errors = append(errors, fmt.Errorf("expected %q to be a valid IPv4 Value, got %v: %v", k, i, err)) + } + + return warnings, errors +} + +// IsCIDRNetwork returns a SchemaValidateFunc which tests if the provided value +// is of type string, is in valid Value network notation, and has significant bits between min and max (inclusive) +func IsCIDRNetwork(min, max int) schema.SchemaValidateFunc { + return func(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return warnings, errors + } + + _, ipnet, err := net.ParseCIDR(v) + if err != nil { + errors = append(errors, fmt.Errorf("expected %s to contain a valid Value, got: %s with err: %s", k, v, err)) + return warnings, errors + } + + if ipnet == nil || v != ipnet.String() { + errors = append(errors, fmt.Errorf("expected %s to contain a valid network Value, expected %s, got %s", + k, ipnet, v)) + } + + sigbits, _ := ipnet.Mask.Size() + if sigbits < min || sigbits > max { + errors = append(errors, fmt.Errorf("expected %q to contain a network Value with between %d and %d significant bits, got: %d", k, min, max, sigbits)) + } + + return warnings, errors + } +} + +// IsMACAddress is a SchemaValidateFunc which tests if the provided value is of type string and a valid MAC address +func IsMACAddress(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return warnings, errors + } + + if _, err := net.ParseMAC(v); err != nil { + errors = append(errors, fmt.Errorf("expected %q to be a valid MAC address, got %v: %v", k, i, err)) + } + + return warnings, errors +} + +// IsPortNumber is a SchemaValidateFunc which tests if the provided value is of type string and a valid TCP Port Number +func IsPortNumber(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(int) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be integer", k)) + return warnings, errors + } + + if 1 > v || v > 65535 { + errors = append(errors, fmt.Errorf("expected %q to be a valid port number, got: %v", k, v)) + } + + return warnings, errors +} + +// IsPortNumberOrZero is a SchemaValidateFunc which tests if the provided value is of type string and a valid TCP Port Number or zero +func IsPortNumberOrZero(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(int) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be integer", k)) + return warnings, errors + } + + if 0 > v || v > 65535 { + errors = append(errors, fmt.Errorf("expected %q to be a valid port number or 0, got: %v", k, v)) + } + + return warnings, errors +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/network_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/network_test.go new file mode 100644 index 00000000..d868ef7b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/network_test.go @@ -0,0 +1,461 @@ +package validation + +import ( + "testing" +) + +func TestValidateIsIPAddress(t *testing.T) { + cases := map[string]struct { + Value interface{} + Error bool + }{ + "NotString": { + Value: 777, + Error: true, + }, + "Empty": { + Value: "", + Error: true, + }, + "MissingOctet": { + Value: "1.2.3", + Error: true, + }, + "Chars": { + Value: "1.2.3.no", + Error: true, + }, + "Text": { + Value: "text", + Error: true, + }, + "256": { + Value: "256.1.1.1", + Error: true, + }, + "CIDR": { + Value: "1.1.1.0/20", + Error: true, + }, + "Zeros": { + Value: "0.0.0.0", + Error: false, + }, + "Valid": { + Value: "1.2.3.4", + Error: false, + }, + "Valid10s": { + Value: "12.34.43.21", + Error: false, + }, + "Valid100s": { + Value: "100.123.199.0", + Error: false, + }, + "Valid255": { + Value: "255.255.255.255", + Error: false, + }, + "ZeroIPv6": { + Value: "0:0:0:0:0:0:0:0", + Error: false, + }, + "ValidIPv6": { + Value: "2001:0db8:85a3:0:0:8a2e:0370:7334", + Error: false, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := IsIPAddress(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("IsIPAddress(%s) produced an unexpected error", tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("IsIPAddress(%s) did not error", tc.Value) + } + }) + } +} + +func TestValidateIsIPv4Address(t *testing.T) { + cases := map[string]struct { + Value interface{} + Error bool + }{ + "NotString": { + Value: 777, + Error: true, + }, + "Empty": { + Value: "", + Error: true, + }, + "Chars": { + Value: "1.2.3.no", + Error: true, + }, + "MissingOctet": { + Value: "1.2.3", + Error: true, + }, + "Text": { + Value: "text", + Error: true, + }, + "IPv6": { + Value: "2001:0db8:85a3:0:0:8a2e:0370:7334", + Error: true, + }, + "256": { + Value: "256.1.1.1", + Error: true, + }, + "CIDR": { + Value: "1.1.1.0/20", + Error: true, + }, + "Zeros": { + Value: "0.0.0.0", + Error: false, + }, + "Valid": { + Value: "1.2.3.4", + Error: false, + }, + "Valid10s": { + Value: "12.34.43.21", + Error: false, + }, + "Valid100s": { + Value: "100.123.199.0", + Error: false, + }, + "Valid255": { + Value: "255.255.255.255", + Error: false, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := IsIPv4Address(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("IsIPv4Address(%s) produced an unexpected error", tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("IsIPv4Address(%s) did not error", tc.Value) + } + }) + } +} + +func TestValidateIsIPv6Address(t *testing.T) { + cases := map[string]struct { + Value interface{} + Error bool + }{ + "NotString": { + Value: 777, + Error: true, + }, + "Empty": { + Value: "", + Error: true, + }, + "ZeroIpv4": { + Value: "0.0.0.0", + Error: false, + }, + "NotARealAddress": { + Value: "not:a:real:address:1:2:3:4", + Error: true, + }, + "Text": { + Value: "text", + Error: true, + }, + "IPv4": { + Value: "1.2.3.4", + Error: false, + }, + "Colons": { + Value: "::", + Error: false, + }, + "ZeroIPv6": { + Value: "0:0:0:0:0:0:0:0", + Error: false, + }, + "Valid1": { + Value: "2001:0db8:85a3:0:0:8a2e:0370:7334", + Error: false, + }, + "Valid2": { + Value: "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + Error: false, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := IsIPv6Address(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("IsIPv6Address(%s) produced an unexpected error", tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("IsIPv6Address(%s) did not error", tc.Value) + } + }) + } +} + +func TestValidateIsIPv4Range(t *testing.T) { + cases := map[string]struct { + Value interface{} + Error bool + }{ + "NotString": { + Value: 777, + Error: true, + }, + "Empty": { + Value: "", + Error: true, + }, + "Zeros": { + Value: "0.0.0.0", + Error: true, + }, + "CIDR": { + Value: "127.0.0.1/8", + Error: true, + }, + "SingleIP": { + Value: "127.0.0.1", + Error: true, + }, + "BackwardsRange": { + Value: "10.0.0.0-7.0.0.0", + Error: true, + }, + "ValidRange": { + Value: "10.0.0.1-70.0.0.0", + Error: false, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := IsIPv4Range(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("IsIPv4Range(%s) produced an unexpected error", tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("IsIPv4Range(%s) did not error", tc.Value) + } + }) + } +} + +func TestValidateIsCIDR(t *testing.T) { + cases := map[string]struct { + Value interface{} + Error bool + }{ + "NotString": { + Value: 777, + Error: true, + }, + "Empty": { + Value: "", + Error: true, + }, + "Zeros": { + Value: "0.0.0.0", + Error: true, + }, + "Slash8": { + Value: "127.0.0.1/8", + Error: false, + }, + "Slash33": { + Value: "127.0.0.1/33", + Error: true, + }, + "Slash-1": { + Value: "127.0.0.1/-1", + Error: true, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := IsCIDR(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("IsCIDR(%s) produced an unexpected error", tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("IsCIDR(%s) did not error", tc.Value) + } + }) + } +} + +func TestValidationIsMACAddress(t *testing.T) { + cases := map[string]struct { + Value interface{} + Error bool + }{ + "NotString": { + Value: 777, + Error: true, + }, + "Empty": { + Value: "", + Error: true, + }, + "Text": { + Value: "text d", + Error: true, + }, + "Gibberish": { + Value: "12:34:no", + Error: true, + }, + "InvalidOctetSize": { + Value: "123:34:56:78:90:ab", + Error: true, + }, + "InvalidOctetChars": { + Value: "12:34:56:78:90:NO", + Error: true, + }, + "ValidLowercase": { + Value: "12:34:56:78:90:ab", + Error: false, + }, + "ValidUppercase": { + Value: "ab:cd:ef:AB:CD:EF", + Error: false, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := IsMACAddress(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("IsMACAddress(%s) produced an unexpected error", tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("IsMACAddress(%s) did not error", tc.Value) + } + }) + } +} + +func TestValidationIsPortNumber(t *testing.T) { + cases := map[string]struct { + Value interface{} + Error bool + }{ + "NotInt": { + Value: "kt", + Error: true, + }, + "Negative": { + Value: -1, + Error: true, + }, + "Zero": { + Value: 0, + Error: true, + }, + "One": { + Value: 1, + Error: false, + }, + "Valid": { + Value: 8477, + Error: false, + }, + "MaxPort": { + Value: 65535, + Error: false, + }, + "OneToHigh": { + Value: 65536, + Error: true, + }, + "HugeNumber": { + Value: 7000000, + Error: true, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := IsPortNumber(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("IsPortNumber(%s) produced an unexpected error", tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("IsPortNumber(%s) did not error", tc.Value) + } + }) + } +} + +func TestValidationIsPortNumberOrZero(t *testing.T) { + cases := map[string]struct { + Value interface{} + Error bool + }{ + "NotInt": { + Value: "kt", + Error: true, + }, + "Negative": { + Value: -1, + Error: true, + }, + "Zero": { + Value: 0, + Error: false, + }, + "One": { + Value: 1, + Error: false, + }, + "Valid": { + Value: 8477, + Error: false, + }, + "MaxPort": { + Value: 65535, + Error: false, + }, + "OneToHigh": { + Value: 65536, + Error: true, + }, + "HugeNumber": { + Value: 7000000, + Error: true, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := IsPortNumberOrZero(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("IsPortNumberOrZero(%s) produced an unexpected error", tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("IsPortNumberOrZero(%s) did not error", tc.Value) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/strings.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/strings.go new file mode 100644 index 00000000..c0c17d01 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/strings.go @@ -0,0 +1,237 @@ +package validation + +import ( + "encoding/base64" + "fmt" + "regexp" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" +) + +// StringIsNotEmpty is a ValidateFunc that ensures a string is not empty +func StringIsNotEmpty(i interface{}, k string) ([]string, []error) { + v, ok := i.(string) + if !ok { + return nil, []error{fmt.Errorf("expected type of %q to be string", k)} + } + + if v == "" { + return nil, []error{fmt.Errorf("expected %q to not be an empty string, got %v", k, i)} + } + + return nil, nil +} + +// StringIsNotWhiteSpace is a ValidateFunc that ensures a string is not empty or consisting entirely of whitespace characters +func StringIsNotWhiteSpace(i interface{}, k string) ([]string, []error) { + v, ok := i.(string) + if !ok { + return nil, []error{fmt.Errorf("expected type of %q to be string", k)} + } + + if strings.TrimSpace(v) == "" { + return nil, []error{fmt.Errorf("expected %q to not be an empty string or whitespace", k)} + } + + return nil, nil +} + +// StringIsEmpty is a ValidateFunc that ensures a string has no characters +func StringIsEmpty(i interface{}, k string) ([]string, []error) { + v, ok := i.(string) + if !ok { + return nil, []error{fmt.Errorf("expected type of %q to be string", k)} + } + + if v != "" { + return nil, []error{fmt.Errorf("expected %q to be an empty string: got %v", k, v)} + } + + return nil, nil +} + +// StringIsWhiteSpace is a ValidateFunc that ensures a string is composed of entirely whitespace +func StringIsWhiteSpace(i interface{}, k string) ([]string, []error) { + v, ok := i.(string) + if !ok { + return nil, []error{fmt.Errorf("expected type of %q to be string", k)} + } + + if strings.TrimSpace(v) != "" { + return nil, []error{fmt.Errorf("expected %q to be an empty string or whitespace: got %v", k, v)} + } + + return nil, nil +} + +// StringLenBetween returns a SchemaValidateFunc which tests if the provided value +// is of type string and has length between min and max (inclusive) +func StringLenBetween(min, max int) schema.SchemaValidateFunc { + return func(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return warnings, errors + } + + if len(v) < min || len(v) > max { + errors = append(errors, fmt.Errorf("expected length of %s to be in the range (%d - %d), got %s", k, min, max, v)) + } + + return warnings, errors + } +} + +// StringMatch returns a SchemaValidateFunc which tests if the provided value +// matches a given regexp. Optionally an error message can be provided to +// return something friendlier than "must match some globby regexp". +func StringMatch(r *regexp.Regexp, message string) schema.SchemaValidateFunc { + return func(i interface{}, k string) ([]string, []error) { + v, ok := i.(string) + if !ok { + return nil, []error{fmt.Errorf("expected type of %s to be string", k)} + } + + if ok := r.MatchString(v); !ok { + if message != "" { + return nil, []error{fmt.Errorf("invalid value for %s (%s)", k, message)} + + } + return nil, []error{fmt.Errorf("expected value of %s to match regular expression %q, got %v", k, r, i)} + } + return nil, nil + } +} + +// StringDoesNotMatch returns a SchemaValidateFunc which tests if the provided value +// does not match a given regexp. Optionally an error message can be provided to +// return something friendlier than "must not match some globby regexp". +func StringDoesNotMatch(r *regexp.Regexp, message string) schema.SchemaValidateFunc { + return func(i interface{}, k string) ([]string, []error) { + v, ok := i.(string) + if !ok { + return nil, []error{fmt.Errorf("expected type of %s to be string", k)} + } + + if ok := r.MatchString(v); ok { + if message != "" { + return nil, []error{fmt.Errorf("invalid value for %s (%s)", k, message)} + + } + return nil, []error{fmt.Errorf("expected value of %s to not match regular expression %q, got %v", k, r, i)} + } + return nil, nil + } +} + +// 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) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return warnings, errors + } + + for _, str := range valid { + if v == str || (ignoreCase && strings.ToLower(v) == strings.ToLower(str)) { + return warnings, errors + } + } + + errors = append(errors, fmt.Errorf("expected %s to be one of %v, got %s", k, valid, v)) + return warnings, errors + } +} + +// StringNotInSlice returns a SchemaValidateFunc which tests if the provided value +// is of type string and does not match the value of any element in the invalid slice +// will test with in lower case if ignoreCase is true +func StringNotInSlice(invalid []string, ignoreCase bool) schema.SchemaValidateFunc { + return func(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return warnings, errors + } + + for _, str := range invalid { + if v == str || (ignoreCase && strings.ToLower(v) == strings.ToLower(str)) { + errors = append(errors, fmt.Errorf("expected %s to not be any of %v, got %s", k, invalid, v)) + return warnings, errors + } + } + + return warnings, errors + } +} + +// StringDoesNotContainAny returns a SchemaValidateFunc which validates that the +// provided value does not contain any of the specified Unicode code points in chars. +func StringDoesNotContainAny(chars string) schema.SchemaValidateFunc { + return func(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return warnings, errors + } + + if strings.ContainsAny(v, chars) { + errors = append(errors, fmt.Errorf("expected value of %s to not contain any of %q, got %v", k, chars, i)) + return warnings, errors + } + + return warnings, errors + } +} + +// StringIsBase64 is a ValidateFunc that ensures a string can be parsed as Base64 +func StringIsBase64(i interface{}, k string) (warnings []string, errors []error) { + // Empty string is not allowed + if warnings, errors = StringIsNotEmpty(i, k); len(errors) > 0 { + return + } + + // NoEmptyStrings checks it is a string + v, _ := i.(string) + + if _, err := base64.StdEncoding.DecodeString(v); err != nil { + errors = append(errors, fmt.Errorf("expected %q to be a base64 string, got %v", k, v)) + } + + return warnings, errors +} + +// StringIsJSON is a SchemaValidateFunc which tests to make sure the supplied string is valid JSON. +func StringIsJSON(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return warnings, errors + } + + if _, err := structure.NormalizeJsonString(v); err != nil { + errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err)) + } + + return warnings, errors +} + +// StringIsValidRegExp returns a SchemaValidateFunc which tests to make sure the supplied string is a valid regular expression. +func StringIsValidRegExp(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return warnings, errors + } + + if _, err := regexp.Compile(v); err != nil { + errors = append(errors, fmt.Errorf("%q: %s", k, err)) + } + + return warnings, errors +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/strings_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/strings_test.go new file mode 100644 index 00000000..f18c68aa --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/strings_test.go @@ -0,0 +1,471 @@ +package validation + +import ( + "regexp" + "testing" +) + +func TestValidationStringIsNotEmpty(t *testing.T) { + cases := map[string]struct { + Value interface{} + Error bool + }{ + "NotString": { + Value: 7, + Error: true, + }, + "Empty": { + Value: "", + Error: true, + }, + "SingleSpace": { + Value: " ", + Error: false, + }, + "MultipleSpaces": { + Value: " ", + Error: false, + }, + "NewLine": { + Value: "\n", + Error: false, + }, + "MultipleSymbols": { + Value: "-_-", + Error: false, + }, + "Sentence": { + Value: "Hello kt's sentence.", + Error: false, + }, + "StartsWithWhitespace": { + Value: " 7", + Error: false, + }, + "EndsWithWhitespace": { + Value: "7 ", + Error: false, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := StringIsNotEmpty(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("StringIsNotEmpty(%s) produced an unexpected error", tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("StringIsNotEmpty(%s) did not error", tc.Value) + } + }) + } +} + +func TestValidationStringIsNotWhitespace(t *testing.T) { + cases := map[string]struct { + Value interface{} + Error bool + }{ + "NotString": { + Value: 7, + Error: true, + }, + "Empty": { + Value: "", + Error: true, + }, + "SingleSpace": { + Value: " ", + Error: true, + }, + "MultipleSpaces": { + Value: " ", + Error: true, + }, + "CarriageReturn": { + Value: "\r", + Error: true, + }, + "NewLine": { + Value: "\n", + Error: true, + }, + "Tab": { + Value: "\t", + Error: true, + }, + "FormFeed": { + Value: "\f", + Error: true, + }, + "VerticalTab": { + Value: "\v", + Error: true, + }, + "SingleChar": { + Value: "\v", + Error: true, + }, + "MultipleChars": { + Value: "-_-", + Error: false, + }, + "Sentence": { + Value: "Hello kt's sentence.", + Error: false, + }, + + "StartsWithWhitespace": { + Value: " 7", + Error: false, + }, + "EndsWithWhitespace": { + Value: "7 ", + Error: false, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := StringIsNotWhiteSpace(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("StringIsNotWhiteSpace(%s) produced an unexpected error", tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("StringIsNotWhiteSpace(%s) did not error", tc.Value) + } + }) + } +} + +func TestValidationStringIsEmpty(t *testing.T) { + cases := map[string]struct { + Value interface{} + Error bool + }{ + "NotString": { + Value: 7, + Error: true, + }, + "Empty": { + Value: "", + Error: false, + }, + "SingleSpace": { + Value: " ", + Error: true, + }, + "MultipleSpaces": { + Value: " ", + Error: true, + }, + "Sentence": { + Value: "Hello kt's sentence.", + Error: true, + }, + + "StartsWithWhitespace": { + Value: " 7", + Error: true, + }, + "EndsWithWhitespace": { + Value: "7 ", + Error: true, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := StringIsEmpty(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("StringIsEmpty(%s) produced an unexpected error", tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("StringIsEmpty(%s) did not error", tc.Value) + } + }) + } +} + +func TestValidationStringIsWhiteSpace(t *testing.T) { + cases := map[string]struct { + Value interface{} + Error bool + }{ + "NotString": { + Value: 7, + Error: true, + }, + "Empty": { + Value: "", + Error: false, + }, + "SingleSpace": { + Value: " ", + Error: false, + }, + "MultipleSpaces": { + Value: " ", + Error: false, + }, + "MultipleWhitespace": { + Value: " \t\n\f ", + Error: false, + }, + "Sentence": { + Value: "Hello kt's sentence.", + Error: true, + }, + + "StartsWithWhitespace": { + Value: " 7", + Error: true, + }, + "EndsWithWhitespace": { + Value: "7 ", + Error: true, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := StringIsWhiteSpace(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("StringIsWhiteSpace(%s) produced an unexpected error", tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("StringIsWhiteSpace(%s) did not error", tc.Value) + } + }) + } +} + +func TestValidationStringIsBase64(t *testing.T) { + cases := map[string]struct { + Value interface{} + Error bool + }{ + "NotString": { + Value: 7, + Error: true, + }, + "Empty": { + Value: "", + Error: true, + }, + "NotBase64": { + Value: "Do'h!", + Error: true, + }, + "Base64": { + Value: "RG8naCE=", + Error: false, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := StringIsBase64(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("StringIsBase64(%s) produced an unexpected error", tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("StringIsBase64(%s) did not error", tc.Value) + } + }) + } +} + +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 TestValidationStringNotInSlice(t *testing.T) { + runTestCases(t, []testCase{ + { + val: "ValidValue", + f: StringNotInSlice([]string{"InvalidValue", "AnotherInvalidValue"}, false), + }, + // ignore case + { + val: "VALIDVALUE", + f: StringNotInSlice([]string{"InvalidValue", "AnotherInvalidValue"}, true), + }, + { + val: "AnotherInvalidValue", + f: StringNotInSlice([]string{"InvalidValue", "AnotherInvalidValue"}, false), + expectedErr: regexp.MustCompile("expected [\\w]+ to not be any of \\[InvalidValue AnotherInvalidValue\\], got AnotherInvalidValue"), + }, + // ignore case + { + val: "INVALIDVALUE", + f: StringNotInSlice([]string{"InvalidValue", "AnotherInvalidValue"}, true), + expectedErr: regexp.MustCompile("expected [\\w]+ to not be any of \\[InvalidValue AnotherInvalidValue\\], got INVALIDVALUE"), + }, + { + val: 1, + f: StringNotInSlice([]string{"InvalidValue", "AnotherInvalidValue"}, false), + expectedErr: regexp.MustCompile("expected type of [\\w]+ to be string"), + }, + }) +} + +func TestValidationStringMatch(t *testing.T) { + runTestCases(t, []testCase{ + { + val: "foobar", + f: StringMatch(regexp.MustCompile(".*foo.*"), ""), + }, + { + val: "bar", + f: StringMatch(regexp.MustCompile(".*foo.*"), ""), + expectedErr: regexp.MustCompile("expected value of [\\w]+ to match regular expression " + regexp.QuoteMeta(`".*foo.*"`)), + }, + { + val: "bar", + f: StringMatch(regexp.MustCompile(".*foo.*"), "value must contain foo"), + expectedErr: regexp.MustCompile("invalid value for [\\w]+ \\(value must contain foo\\)"), + }, + }) +} + +func TestValidationStringDoesNotMatch(t *testing.T) { + runTestCases(t, []testCase{ + { + val: "foobar", + f: StringDoesNotMatch(regexp.MustCompile(".*baz.*"), ""), + }, + { + val: "bar", + f: StringDoesNotMatch(regexp.MustCompile(".*bar.*"), ""), + expectedErr: regexp.MustCompile("expected value of [\\w]+ to not match regular expression " + regexp.QuoteMeta(`".*bar.*"`)), + }, + { + val: "bar", + f: StringDoesNotMatch(regexp.MustCompile(".*bar.*"), "value must not contain foo"), + expectedErr: regexp.MustCompile("invalid value for [\\w]+ \\(value must not contain foo\\)"), + }, + }) +} + +func TestStringIsJSON(t *testing.T) { + type testCases struct { + Value string + ErrCount int + } + + invalidCases := []testCases{ + { + Value: `{0:"1"}`, + ErrCount: 1, + }, + { + Value: `{'abc':1}`, + ErrCount: 1, + }, + { + Value: `{"def":}`, + ErrCount: 1, + }, + { + Value: `{"xyz":[}}`, + ErrCount: 1, + }, + } + + for _, tc := range invalidCases { + _, errors := StringIsJSON(tc.Value, "json") + if len(errors) != tc.ErrCount { + t.Fatalf("Expected %q to trigger a validation error.", tc.Value) + } + } + + validCases := []testCases{ + { + Value: ``, + ErrCount: 0, + }, + { + Value: `{}`, + ErrCount: 0, + }, + { + Value: `{"abc":["1","2"]}`, + ErrCount: 0, + }, + } + + for _, tc := range validCases { + _, errors := StringIsJSON(tc.Value, "json") + if len(errors) != tc.ErrCount { + t.Fatalf("Expected %q not to trigger a validation error.", tc.Value) + } + } +} + +func TestStringDoesNotContainAny(t *testing.T) { + chars := "|:/" + + validStrings := []string{ + "HelloWorld", + "ABC_*&^%123", + } + for _, v := range validStrings { + _, errors := StringDoesNotContainAny(chars)(v, "name") + if len(errors) != 0 { + t.Fatalf("%q should not contain any of %q", v, chars) + } + } + + invalidStrings := []string{ + "Hello/World", + "ABC|123", + "This will fail:", + chars, + } + for _, v := range invalidStrings { + _, errors := StringDoesNotContainAny(chars)(v, "name") + if len(errors) == 0 { + t.Fatalf("%q should contain one of %q", v, chars) + } + } +} + +func TestStringIsValidRegExp(t *testing.T) { + runTestCases(t, []testCase{ + { + val: ".*foo.*", + f: StringIsValidRegExp, + }, + { + val: "foo(bar", + f: StringIsValidRegExp, + expectedErr: regexp.MustCompile(regexp.QuoteMeta("error parsing regexp: missing closing ): `foo(bar`")), + }, + }) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/testing.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/testing.go new file mode 100644 index 00000000..596c5754 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/testing.go @@ -0,0 +1,85 @@ +package validation + +import ( + "regexp" + + testing "github.com/mitchellh/go-testing-interface" + + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +type testCase struct { + val interface{} + f schema.SchemaValidateFunc + expectedErr *regexp.Regexp +} + +type diagTestCase struct { + val interface{} + f schema.SchemaValidateDiagFunc + expectedErr *regexp.Regexp +} + +func runTestCases(t testing.T, cases []testCase) { + t.Helper() + + 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 !matchAnyError(errs, tc.expectedErr) { + t.Fatalf("expected test case %d to produce error matching \"%s\", got %v", i, tc.expectedErr, errs) + } + } +} + +func matchAnyError(errs []error, r *regexp.Regexp) bool { + // err must match one provided + for _, err := range errs { + if r.MatchString(err.Error()) { + return true + } + } + return false +} + +func runDiagTestCases(t testing.T, cases []diagTestCase) { + t.Helper() + + for i, tc := range cases { + p := cty.Path{ + cty.GetAttrStep{Name: "test_property"}, + } + diags := tc.f(tc.val, p) + + if !diags.HasError() && tc.expectedErr == nil { + continue + } + + if diags.HasError() && tc.expectedErr == nil { + t.Fatalf("expected test case %d to produce no errors, got %v", i, diags) + } + + if !matchAnyDiagSummary(diags, tc.expectedErr) { + t.Fatalf("expected test case %d to produce error matching \"%s\", got %v", i, tc.expectedErr, diags) + } + } +} + +func matchAnyDiagSummary(ds diag.Diagnostics, r *regexp.Regexp) bool { + for _, d := range ds { + if r.MatchString(d.Summary) { + return true + } + } + return false +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/time.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/time.go new file mode 100644 index 00000000..df3e2620 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/time.go @@ -0,0 +1,54 @@ +package validation + +import ( + "fmt" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// IsDayOfTheWeek id a SchemaValidateFunc which tests if the provided value is of type string and a valid english day of the week +func IsDayOfTheWeek(ignoreCase bool) schema.SchemaValidateFunc { + return StringInSlice([]string{ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday", + }, ignoreCase) +} + +// IsMonth id a SchemaValidateFunc which tests if the provided value is of type string and a valid english month +func IsMonth(ignoreCase bool) schema.SchemaValidateFunc { + return StringInSlice([]string{ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + }, ignoreCase) +} + +// IsRFC3339Time is a SchemaValidateFunc which tests if the provided value is of type string and a valid RFC33349Time +func IsRFC3339Time(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return warnings, errors + } + + if _, err := time.Parse(time.RFC3339, v); err != nil { + errors = append(errors, fmt.Errorf("expected %q to be a valid RFC3339 date, got %q: %+v", k, i, err)) + } + + return warnings, errors +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/time_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/time_test.go new file mode 100644 index 00000000..e453ee4c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/time_test.go @@ -0,0 +1,69 @@ +package validation + +import ( + "testing" +) + +func TestValidationIsRFC3339Time(t *testing.T) { + cases := map[string]struct { + Value interface{} + Error bool + }{ + "NotString": { + Value: 7, + Error: true, + }, + "ValidDate": { + Value: "2018-03-01T00:00:00Z", + Error: false, + }, + "ValidDateTime": { + Value: "2018-03-01T00:00:00-05:00", + Error: false, + }, + "ValidDateTime2": { + Value: "2018-03-01T00:00:00+05:00", + Error: false, + }, + "InvalidDateWithSlashes": { + Value: "03/01/2018", + Error: true, + }, + "InvalidDateWithDashes": { + Value: "03-01-2018", + Error: true, + }, + "InvalidDateWithDashes2": { + Value: "2018-03-01", + Error: true, + }, + "InvalidDateWithT": { + Value: "2018-03-01T", + Error: true, + }, + "DateTimeWithoutZone": { + Value: "2018-03-01T00:00:00", + Error: true, + }, + "DateTimeWithZZone": { + Value: "2018-03-01T00:00:00Z05:00", + Error: true, + }, + "DateTimeWithZZoneNeg": { + Value: "2018-03-01T00:00:00Z-05:00", + Error: true, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := IsRFC3339Time(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("IsRFC3339Time(%s) produced an unexpected error", tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("IsRFC3339Time(%s) did not error", tc.Value) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/uuid.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/uuid.go new file mode 100644 index 00000000..00783faf --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/uuid.go @@ -0,0 +1,22 @@ +package validation + +import ( + "fmt" + + "github.com/hashicorp/go-uuid" +) + +// IsUUID is a ValidateFunc that ensures a string can be parsed as UUID +func IsUUID(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return + } + + if _, err := uuid.ParseUUID(v); err != nil { + errors = append(errors, fmt.Errorf("expected %q to be a valid UUID, got %v", k, v)) + } + + return warnings, errors +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/uuid_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/uuid_test.go new file mode 100644 index 00000000..b8bb1505 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/uuid_test.go @@ -0,0 +1,45 @@ +package validation + +import ( + "testing" +) + +func TestValidationIsUUID(t *testing.T) { + cases := map[string]struct { + Value interface{} + Error bool + }{ + "NotString": { + Value: 7, + Error: true, + }, + "Empty": { + Value: "", + Error: true, + }, + "InvalidUuid": { + Value: "00000000-0000-123-0000-000000000000", + Error: true, + }, + "ValidUuidWithOutDashs": { + Value: "12345678123412341234123456789012", + Error: true, + }, + "ValidUuid": { + Value: "00000000-0000-0000-0000-000000000000", + Error: false, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := IsUUID(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("IsUUID(%s) produced an unexpected error", tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("IsUUID(%s) did not error", tc.Value) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/web.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/web.go new file mode 100644 index 00000000..615e0feb --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/web.go @@ -0,0 +1,55 @@ +package validation + +import ( + "fmt" + "net/url" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// IsURLWithHTTPS is a SchemaValidateFunc which tests if the provided value is of type string and a valid HTTPS URL +func IsURLWithHTTPS(i interface{}, k string) (_ []string, errors []error) { + return IsURLWithScheme([]string{"https"})(i, k) +} + +// IsURLWithHTTPorHTTPS is a SchemaValidateFunc which tests if the provided value is of type string and a valid HTTP or HTTPS URL +func IsURLWithHTTPorHTTPS(i interface{}, k string) (_ []string, errors []error) { + return IsURLWithScheme([]string{"http", "https"})(i, k) +} + +// IsURLWithScheme is a SchemaValidateFunc which tests if the provided value is of type string and a valid URL with the provided schemas +func IsURLWithScheme(validSchemes []string) schema.SchemaValidateFunc { + return func(i interface{}, k string) (_ []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return + } + + if v == "" { + errors = append(errors, fmt.Errorf("expected %q url to not be empty, got %v", k, i)) + return + } + + u, err := url.Parse(v) + if err != nil { + errors = append(errors, fmt.Errorf("expected %q to be a valid url, got %v: %+v", k, v, err)) + return + } + + if u.Host == "" { + errors = append(errors, fmt.Errorf("expected %q to have a host, got %v", k, v)) + return + } + + for _, s := range validSchemes { + if u.Scheme == s { + return //last check so just return + } + } + + errors = append(errors, fmt.Errorf("expected %q to have a url with schema of: %q, got %v", k, strings.Join(validSchemes, ","), v)) + return + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/web_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/web_test.go new file mode 100644 index 00000000..e729e4e9 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/validation/web_test.go @@ -0,0 +1,101 @@ +package validation + +import ( + "testing" +) + +func TestValidationIsURLWithHTTPS(t *testing.T) { + cases := map[string]struct { + Value interface{} + Error bool + }{ + "NotString": { + Value: 7, + Error: true, + }, + "Empty": { + Value: "", + Error: true, + }, + "NotUrl": { + Value: "this is not a url", + Error: true, + }, + "BareUrl": { + Value: "www.example.com", + Error: true, + }, + "FtpUrl": { + Value: "ftp://www.example.com", + Error: true, + }, + "HttpUrl": { + Value: "http://www.example.com", + Error: true, + }, + "HttpsUrl": { + Value: "https://www.example.com", + Error: false, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := IsURLWithHTTPS(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("IsURLWithHTTPS(%s) produced an unexpected error", tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("IsURLWithHTTPS(%s) did not error", tc.Value) + } + }) + } +} + +func TestValidationIsURLWithHTTPorHTTPS(t *testing.T) { + cases := map[string]struct { + Value interface{} + Error bool + }{ + "NotString": { + Value: 7, + Error: true, + }, + "Empty": { + Value: "", + Error: true, + }, + "NotUrl": { + Value: "this is not a url", + Error: true, + }, + "BareUrl": { + Value: "www.example.com", + Error: true, + }, + "FtpUrl": { + Value: "ftp://www.example.com", + Error: true, + }, + "HttpUrl": { + Value: "http://www.example.com", + Error: false, + }, + "HttpsUrl": { + Value: "https://www.example.com", + Error: false, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + _, errors := IsURLWithHTTPorHTTPS(tc.Value, tn) + + if len(errors) > 0 && !tc.Error { + t.Errorf("IsURLWithHTTPorHTTPS(%s) produced an unexpected error", tc.Value) + } else if len(errors) == 0 && tc.Error { + t.Errorf("IsURLWithHTTPorHTTPS(%s) did not error", tc.Value) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/httpclient/useragent.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/httpclient/useragent.go deleted file mode 100644 index 36b494c0..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/httpclient/useragent.go +++ /dev/null @@ -1,26 +0,0 @@ -package httpclient - -import ( - "fmt" - "log" - "os" - "strings" - - "github.com/hashicorp/terraform-plugin-sdk/meta" -) - -const uaEnvVar = "TF_APPEND_USER_AGENT" - -func TerraformUserAgent(version string) string { - ua := fmt.Sprintf("HashiCorp Terraform/%s (+https://www.terraform.io) Terraform Plugin SDK/%s", version, meta.SDKVersionString()) - - if add := os.Getenv(uaEnvVar); add != "" { - add = strings.TrimSpace(add) - if len(add) > 0 { - ua += " " + add - log.Printf("[DEBUG] Using modified User-Agent: %s", ua) - } - } - - return ua -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/count_attr.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/count_attr.go deleted file mode 100644 index 90a5faf0..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/count_attr.go +++ /dev/null @@ -1,12 +0,0 @@ -package addrs - -// CountAttr is the address of an attribute of the "count" object in -// the interpolation scope, like "count.index". -type CountAttr struct { - referenceable - Name string -} - -func (ca CountAttr) String() string { - return "count." + ca.Name -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/for_each_attr.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/for_each_attr.go deleted file mode 100644 index 7a638503..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/for_each_attr.go +++ /dev/null @@ -1,12 +0,0 @@ -package addrs - -// ForEachAttr is the address of an attribute referencing the current "for_each" object in -// the interpolation scope, addressed using the "each" keyword, ex. "each.key" and "each.value" -type ForEachAttr struct { - referenceable - Name string -} - -func (f ForEachAttr) String() string { - return "each." + f.Name -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/input_variable.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/input_variable.go deleted file mode 100644 index d2c046c1..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/input_variable.go +++ /dev/null @@ -1,41 +0,0 @@ -package addrs - -import ( - "fmt" -) - -// InputVariable is the address of an input variable. -type InputVariable struct { - referenceable - Name string -} - -func (v InputVariable) String() string { - return "var." + v.Name -} - -// AbsInputVariableInstance is the address of an input variable within a -// particular module instance. -type AbsInputVariableInstance struct { - Module ModuleInstance - Variable InputVariable -} - -// InputVariable returns the absolute address of the input variable of the -// given name inside the receiving module instance. -func (m ModuleInstance) InputVariable(name string) AbsInputVariableInstance { - return AbsInputVariableInstance{ - Module: m, - Variable: InputVariable{ - Name: name, - }, - } -} - -func (v AbsInputVariableInstance) String() string { - if len(v.Module) == 0 { - return v.String() - } - - return fmt.Sprintf("%s.%s", v.Module.String(), v.Variable.String()) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/instance_key.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/instance_key.go index cef8b279..064aeda2 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/instance_key.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/instance_key.go @@ -2,122 +2,46 @@ package addrs import ( "fmt" - - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/gocty" ) -// InstanceKey represents the key of an instance within an object that +// instanceKey represents the key of an instance within an object that // contains multiple instances due to using "count" or "for_each" arguments // in configuration. // -// IntKey and StringKey are the two implementations of this type. No other +// intKey and stringKey are the two implementations of this type. No other // implementations are allowed. The single instance of an object that _isn't_ // using "count" or "for_each" is represented by NoKey, which is a nil // InstanceKey. -type InstanceKey interface { +type instanceKey interface { instanceKeySigil() String() string } -// ParseInstanceKey returns the instance key corresponding to the given value, -// which must be known and non-null. -// -// If an unknown or null value is provided then this function will panic. This -// function is intended to deal with the values that would naturally be found -// in a hcl.TraverseIndex, which (when parsed from source, at least) can never -// contain unknown or null values. -func ParseInstanceKey(key cty.Value) (InstanceKey, error) { - switch key.Type() { - case cty.String: - return StringKey(key.AsString()), nil - case cty.Number: - var idx int - err := gocty.FromCtyValue(key, &idx) - return IntKey(idx), err - default: - return NoKey, fmt.Errorf("either a string or an integer is required") - } -} - -// NoKey represents the absense of an InstanceKey, for the single instance +// NoKey represents the absense of an instanceKey, for the single instance // of a configuration object that does not use "count" or "for_each" at all. -var NoKey InstanceKey +var NoKey instanceKey -// IntKey is the InstanceKey representation representing integer indices, as +// intKey is the InstanceKey representation representing integer indices, as // used when the "count" argument is specified or if for_each is used with // a sequence type. -type IntKey int +type intKey int -func (k IntKey) instanceKeySigil() { +func (k intKey) instanceKeySigil() { } -func (k IntKey) String() string { +func (k intKey) String() string { return fmt.Sprintf("[%d]", int(k)) } -// StringKey is the InstanceKey representation representing string indices, as +// stringKey is the InstanceKey representation representing string indices, as // used when the "for_each" argument is specified with a map or object type. -type StringKey string +type stringKey string -func (k StringKey) instanceKeySigil() { +func (k stringKey) instanceKeySigil() { } -func (k StringKey) String() string { +func (k stringKey) String() string { // FIXME: This isn't _quite_ right because Go's quoted string syntax is // slightly different than HCL's, but we'll accept it for now. return fmt.Sprintf("[%q]", string(k)) } - -// InstanceKeyLess returns true if the first given instance key i should sort -// before the second key j, and false otherwise. -func InstanceKeyLess(i, j InstanceKey) bool { - iTy := instanceKeyType(i) - jTy := instanceKeyType(j) - - switch { - case i == j: - return false - case i == NoKey: - return true - case j == NoKey: - return false - case iTy != jTy: - // The ordering here is arbitrary except that we want NoKeyType - // to sort before the others, so we'll just use the enum values - // of InstanceKeyType here (where NoKey is zero, sorting before - // any other). - return uint32(iTy) < uint32(jTy) - case iTy == IntKeyType: - return int(i.(IntKey)) < int(j.(IntKey)) - case iTy == StringKeyType: - return string(i.(StringKey)) < string(j.(StringKey)) - default: - // Shouldn't be possible to get down here in practice, since the - // above is exhaustive. - return false - } -} - -func instanceKeyType(k InstanceKey) InstanceKeyType { - if _, ok := k.(StringKey); ok { - return StringKeyType - } - if _, ok := k.(IntKey); ok { - return IntKeyType - } - return NoKeyType -} - -// InstanceKeyType represents the different types of instance key that are -// supported. Usually it is sufficient to simply type-assert an InstanceKey -// value to either IntKey or StringKey, but this type and its values can be -// used to represent the types themselves, rather than specific values -// of those types. -type InstanceKeyType rune - -const ( - NoKeyType InstanceKeyType = 0 - IntKeyType InstanceKeyType = 'I' - StringKeyType InstanceKeyType = 'S' -) diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/local_value.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/local_value.go deleted file mode 100644 index 61a07b9c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/local_value.go +++ /dev/null @@ -1,48 +0,0 @@ -package addrs - -import ( - "fmt" -) - -// LocalValue is the address of a local value. -type LocalValue struct { - referenceable - Name string -} - -func (v LocalValue) String() string { - return "local." + v.Name -} - -// Absolute converts the receiver into an absolute address within the given -// module instance. -func (v LocalValue) Absolute(m ModuleInstance) AbsLocalValue { - return AbsLocalValue{ - Module: m, - LocalValue: v, - } -} - -// AbsLocalValue is the absolute address of a local value within a module instance. -type AbsLocalValue struct { - Module ModuleInstance - LocalValue LocalValue -} - -// LocalValue returns the absolute address of a local value of the given -// name within the receiving module instance. -func (m ModuleInstance) LocalValue(name string) AbsLocalValue { - return AbsLocalValue{ - Module: m, - LocalValue: LocalValue{ - Name: name, - }, - } -} - -func (v AbsLocalValue) String() string { - if len(v.Module) == 0 { - return v.LocalValue.String() - } - return fmt.Sprintf("%s.%s", v.Module.String(), v.LocalValue.String()) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/module.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/module.go index 1533f853..98699f46 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/module.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/module.go @@ -1,9 +1,5 @@ package addrs -import ( - "strings" -) - // Module is an address for a module call within configuration. This is // the static counterpart of ModuleInstance, representing a traversal through // the static module call tree in configuration and does not take into account @@ -15,37 +11,3 @@ import ( // // Although Module is a slice, it should be treated as immutable after creation. type Module []string - -// IsRoot returns true if the receiver is the address of the root module, -// or false otherwise. -func (m Module) IsRoot() bool { - return len(m) == 0 -} - -func (m Module) String() string { - if len(m) == 0 { - return "" - } - return strings.Join([]string(m), ".") -} - -// Call returns the module call address that corresponds to the given module -// instance, along with the address of the module that contains it. -// -// There is no call for the root module, so this method will panic if called -// on the root module address. -// -// In practice, this just turns the last element of the receiver into a -// ModuleCall and then returns a slice of the receiever that excludes that -// last part. This is just a convenience for situations where a call address -// is required, such as when dealing with *Reference and Referencable values. -func (m Module) Call() (Module, ModuleCall) { - if len(m) == 0 { - panic("cannot produce ModuleCall for root module") - } - - caller, callName := m[:len(m)-1], m[len(m)-1] - return caller, ModuleCall{ - Name: callName, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/module_call.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/module_call.go deleted file mode 100644 index d138fade..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/module_call.go +++ /dev/null @@ -1,63 +0,0 @@ -package addrs - -import ( - "fmt" -) - -// ModuleCall is the address of a call from the current module to a child -// module. -// -// There is no "Abs" version of ModuleCall because an absolute module path -// is represented by ModuleInstance. -type ModuleCall struct { - referenceable - Name string -} - -func (c ModuleCall) String() string { - return "module." + c.Name -} - -// ModuleCallInstance is the address of one instance of a module created from -// a module call, which might create multiple instances using "count" or -// "for_each" arguments. -type ModuleCallInstance struct { - referenceable - Call ModuleCall - Key InstanceKey -} - -func (c ModuleCallInstance) String() string { - if c.Key == NoKey { - return c.Call.String() - } - return fmt.Sprintf("module.%s%s", c.Call.Name, c.Key) -} - -// ModuleInstance returns the address of the module instance that corresponds -// to the receiving call instance when resolved in the given calling module. -// In other words, it returns the child module instance that the receving -// call instance creates. -func (c ModuleCallInstance) ModuleInstance(caller ModuleInstance) ModuleInstance { - return caller.Child(c.Call.Name, c.Key) -} - -// ModuleCallOutput is the address of a particular named output produced by -// an instance of a module call. -type ModuleCallOutput struct { - referenceable - Call ModuleCallInstance - Name string -} - -func (co ModuleCallOutput) String() string { - return fmt.Sprintf("%s.%s", co.Call.String(), co.Name) -} - -// AbsOutputValue returns the absolute output value address that corresponds -// to the receving module call output address, once resolved in the given -// calling module. -func (co ModuleCallOutput) AbsOutputValue(caller ModuleInstance) AbsOutputValue { - moduleAddr := co.Call.ModuleInstance(caller) - return moduleAddr.OutputValue(co.Name) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/module_instance.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/module_instance.go index bb0901a2..f31d833d 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/module_instance.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/module_instance.go @@ -9,7 +9,7 @@ import ( "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/gocty" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/tfdiags" ) // ModuleInstance is an address for a particular module instance within the @@ -22,27 +22,21 @@ import ( // creation. type ModuleInstance []ModuleInstanceStep -var ( - _ Targetable = ModuleInstance(nil) -) - -func ParseModuleInstance(traversal hcl.Traversal) (ModuleInstance, tfdiags.Diagnostics) { +func parseModuleInstance(traversal hcl.Traversal) (ModuleInstance, tfdiags.Diagnostics) { mi, remain, diags := parseModuleInstancePrefix(traversal) if len(remain) != 0 { if len(remain) == len(traversal) { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid module instance address", - Detail: "A module instance address must begin with \"module.\".", - Subject: remain.SourceRange().Ptr(), - }) + diags = append(diags, tfdiags.Diag( + tfdiags.Error, + "Invalid module instance address", + "A module instance address must begin with \"module.\".", + )) } else { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid module instance address", - Detail: "The module instance address is followed by additional invalid content.", - Subject: remain.SourceRange().Ptr(), - }) + diags = append(diags, tfdiags.Diag( + tfdiags.Error, + "Invalid module instance address", + "The module instance address is followed by additional invalid content.", + )) } } return mi, diags @@ -67,13 +61,16 @@ func ParseModuleInstanceStr(str string) (ModuleInstance, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1}) - diags = diags.Append(parseDiags) + for _, err := range parseDiags.Errs() { + // ignore warnings, they don't matter in this case + diags = append(diags, tfdiags.FromError(err)) + } if parseDiags.HasErrors() { return nil, diags } - addr, addrDiags := ParseModuleInstance(traversal) - diags = diags.Append(addrDiags) + addr, addrDiags := parseModuleInstance(traversal) + diags = append(diags, addrDiags...) return addr, diags } @@ -90,12 +87,11 @@ func parseModuleInstancePrefix(traversal hcl.Traversal) (ModuleInstance, hcl.Tra case hcl.TraverseAttr: next = tt.Name default: - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid address operator", - Detail: "Module address prefix must be followed by dot and then a name.", - Subject: remain[0].SourceRange().Ptr(), - }) + diags = append(diags, tfdiags.Diag( + tfdiags.Error, + "Invalid address operator", + "Module address prefix must be followed by dot and then a name.", + )) break } @@ -103,18 +99,16 @@ func parseModuleInstancePrefix(traversal hcl.Traversal) (ModuleInstance, hcl.Tra break } - kwRange := remain[0].SourceRange() remain = remain[1:] // If we have the prefix "module" then we should be followed by an // module call name, as an attribute, and then optionally an index step // giving the instance key. if len(remain) == 0 { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid address operator", - Detail: "Prefix \"module.\" must be followed by a module name.", - Subject: &kwRange, - }) + diags = append(diags, tfdiags.Diag( + tfdiags.Error, + "Invalid address operator", + "Prefix \"module.\" must be followed by a module name.", + )) break } @@ -123,12 +117,11 @@ func parseModuleInstancePrefix(traversal hcl.Traversal) (ModuleInstance, hcl.Tra case hcl.TraverseAttr: moduleName = tt.Name default: - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid address operator", - Detail: "Prefix \"module.\" must be followed by a module name.", - Subject: remain[0].SourceRange().Ptr(), - }) + diags = append(diags, tfdiags.Diag( + tfdiags.Error, + "Invalid address operator", + "Prefix \"module.\" must be followed by a module name.", + )) break } remain = remain[1:] @@ -142,28 +135,26 @@ func parseModuleInstancePrefix(traversal hcl.Traversal) (ModuleInstance, hcl.Tra switch idx.Key.Type() { case cty.String: - step.InstanceKey = StringKey(idx.Key.AsString()) + step.InstanceKey = stringKey(idx.Key.AsString()) case cty.Number: var idxInt int err := gocty.FromCtyValue(idx.Key, &idxInt) if err == nil { - step.InstanceKey = IntKey(idxInt) + step.InstanceKey = intKey(idxInt) } else { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid address operator", - Detail: fmt.Sprintf("Invalid module index: %s.", err), - Subject: idx.SourceRange().Ptr(), - }) + diags = append(diags, tfdiags.Diag( + tfdiags.Error, + "Invalid address operator", + fmt.Sprintf("Invalid module index: %s.", err), + )) } default: // Should never happen, because no other types are allowed in traversal indices. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid address operator", - Detail: "Invalid module key: must be either a string or an integer.", - Subject: idx.SourceRange().Ptr(), - }) + diags = append(diags, tfdiags.Diag( + tfdiags.Error, + "Invalid address operator", + "Invalid module key: must be either a string or an integer.", + )) } } } @@ -212,22 +203,16 @@ func (m Module) UnkeyedInstanceShim() ModuleInstance { // tree. It is used only as part of ModuleInstance. type ModuleInstanceStep struct { Name string - InstanceKey InstanceKey + InstanceKey instanceKey } // RootModuleInstance is the module instance address representing the root // module, which is also the zero value of ModuleInstance. var RootModuleInstance ModuleInstance -// IsRoot returns true if the receiver is the address of the root module instance, -// or false otherwise. -func (m ModuleInstance) IsRoot() bool { - return len(m) == 0 -} - // Child returns the address of a child module instance of the receiver, // identified by the given name and key. -func (m ModuleInstance) Child(name string, key InstanceKey) ModuleInstance { +func (m ModuleInstance) Child(name string, key instanceKey) ModuleInstance { ret := make(ModuleInstance, 0, len(m)+1) ret = append(ret, m...) return append(ret, ModuleInstanceStep{ @@ -236,15 +221,6 @@ func (m ModuleInstance) Child(name string, key InstanceKey) ModuleInstance { }) } -// Parent returns the address of the parent module instance of the receiver, or -// the receiver itself if there is no parent (if it's the root module address). -func (m ModuleInstance) Parent() ModuleInstance { - if len(m) == 0 { - return m - } - return m[:len(m)-1] -} - // String returns a string representation of the receiver, in the format used // within e.g. user-provided resource addresses. // @@ -263,126 +239,3 @@ func (m ModuleInstance) String() string { } return buf.String() } - -// Less returns true if the receiver should sort before the given other value -// in a sorted list of addresses. -func (m ModuleInstance) Less(o ModuleInstance) bool { - if len(m) != len(o) { - // Shorter path sorts first. - return len(m) < len(o) - } - - for i := range m { - mS, oS := m[i], o[i] - switch { - case mS.Name != oS.Name: - return mS.Name < oS.Name - case mS.InstanceKey != oS.InstanceKey: - return InstanceKeyLess(mS.InstanceKey, oS.InstanceKey) - } - } - - return false -} - -// Ancestors returns a slice containing the receiver and all of its ancestor -// module instances, all the way up to (and including) the root module. -// The result is ordered by depth, with the root module always first. -// -// Since the result always includes the root module, a caller may choose to -// ignore it by slicing the result with [1:]. -func (m ModuleInstance) Ancestors() []ModuleInstance { - ret := make([]ModuleInstance, 0, len(m)+1) - for i := 0; i <= len(m); i++ { - ret = append(ret, m[:i]) - } - return ret -} - -// Call returns the module call address that corresponds to the given module -// instance, along with the address of the module instance that contains it. -// -// There is no call for the root module, so this method will panic if called -// on the root module address. -// -// A single module call can produce potentially many module instances, so the -// result discards any instance key that might be present on the last step -// of the instance. To retain this, use CallInstance instead. -// -// In practice, this just turns the last element of the receiver into a -// ModuleCall and then returns a slice of the receiever that excludes that -// last part. This is just a convenience for situations where a call address -// is required, such as when dealing with *Reference and Referencable values. -func (m ModuleInstance) Call() (ModuleInstance, ModuleCall) { - if len(m) == 0 { - panic("cannot produce ModuleCall for root module") - } - - inst, lastStep := m[:len(m)-1], m[len(m)-1] - return inst, ModuleCall{ - Name: lastStep.Name, - } -} - -// CallInstance returns the module call instance address that corresponds to -// the given module instance, along with the address of the module instance -// that contains it. -// -// There is no call for the root module, so this method will panic if called -// on the root module address. -// -// In practice, this just turns the last element of the receiver into a -// ModuleCallInstance and then returns a slice of the receiever that excludes -// that last part. This is just a convenience for situations where a call\ -// address is required, such as when dealing with *Reference and Referencable -// values. -func (m ModuleInstance) CallInstance() (ModuleInstance, ModuleCallInstance) { - if len(m) == 0 { - panic("cannot produce ModuleCallInstance for root module") - } - - inst, lastStep := m[:len(m)-1], m[len(m)-1] - return inst, ModuleCallInstance{ - Call: ModuleCall{ - Name: lastStep.Name, - }, - Key: lastStep.InstanceKey, - } -} - -// TargetContains implements Targetable by returning true if the given other -// address either matches the receiver, is a sub-module-instance of the -// receiver, or is a targetable absolute address within a module that -// is contained within the reciever. -func (m ModuleInstance) TargetContains(other Targetable) bool { - switch to := other.(type) { - - case ModuleInstance: - if len(to) < len(m) { - // Can't be contained if the path is shorter - return false - } - // Other is contained if its steps match for the length of our own path. - for i, ourStep := range m { - otherStep := to[i] - if ourStep != otherStep { - return false - } - } - // If we fall out here then the prefixed matched, so it's contained. - return true - - case AbsResource: - return m.TargetContains(to.Module) - - case AbsResourceInstance: - return m.TargetContains(to.Module) - - default: - return false - } -} - -func (m ModuleInstance) targetableSigil() { - // ModuleInstance is targetable -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/output_value.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/output_value.go deleted file mode 100644 index bcd923ac..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/output_value.go +++ /dev/null @@ -1,75 +0,0 @@ -package addrs - -import ( - "fmt" -) - -// OutputValue is the address of an output value, in the context of the module -// that is defining it. -// -// This is related to but separate from ModuleCallOutput, which represents -// a module output from the perspective of its parent module. Since output -// values cannot be represented from the module where they are defined, -// OutputValue is not Referenceable, while ModuleCallOutput is. -type OutputValue struct { - Name string -} - -func (v OutputValue) String() string { - return "output." + v.Name -} - -// Absolute converts the receiver into an absolute address within the given -// module instance. -func (v OutputValue) Absolute(m ModuleInstance) AbsOutputValue { - return AbsOutputValue{ - Module: m, - OutputValue: v, - } -} - -// AbsOutputValue is the absolute address of an output value within a module instance. -// -// This represents an output globally within the namespace of a particular -// configuration. It is related to but separate from ModuleCallOutput, which -// represents a module output from the perspective of its parent module. -type AbsOutputValue struct { - Module ModuleInstance - OutputValue OutputValue -} - -// OutputValue returns the absolute address of an output value of the given -// name within the receiving module instance. -func (m ModuleInstance) OutputValue(name string) AbsOutputValue { - return AbsOutputValue{ - Module: m, - OutputValue: OutputValue{ - Name: name, - }, - } -} - -func (v AbsOutputValue) String() string { - if v.Module.IsRoot() { - return v.OutputValue.String() - } - return fmt.Sprintf("%s.%s", v.Module.String(), v.OutputValue.String()) -} - -// ModuleCallOutput converts an AbsModuleOutput into a ModuleCallOutput, -// returning also the module instance that the ModuleCallOutput is relative -// to. -// -// The root module does not have a call, and so this method cannot be used -// with outputs in the root module, and will panic in that case. -func (v AbsOutputValue) ModuleCallOutput() (ModuleInstance, ModuleCallOutput) { - if v.Module.IsRoot() { - panic("ReferenceFromCall used with root module output") - } - - caller, call := v.Module.CallInstance() - return caller, ModuleCallOutput{ - Call: call, - Name: v.OutputValue.Name, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/parse_ref.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/parse_ref.go deleted file mode 100644 index a2ee1644..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/parse_ref.go +++ /dev/null @@ -1,346 +0,0 @@ -package addrs - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// Reference describes a reference to an address with source location -// information. -type Reference struct { - Subject Referenceable - SourceRange tfdiags.SourceRange - Remaining hcl.Traversal -} - -// ParseRef attempts to extract a referencable address from the prefix of the -// given traversal, which must be an absolute traversal or this function -// will panic. -// -// If no error diagnostics are returned, the returned reference includes the -// address that was extracted, the source range it was extracted from, and any -// remaining relative traversal that was not consumed as part of the -// reference. -// -// If error diagnostics are returned then the Reference value is invalid and -// must not be used. -func ParseRef(traversal hcl.Traversal) (*Reference, tfdiags.Diagnostics) { - ref, diags := parseRef(traversal) - - // Normalize a little to make life easier for callers. - if ref != nil { - if len(ref.Remaining) == 0 { - ref.Remaining = nil - } - } - - return ref, diags -} - -// ParseRefStr is a helper wrapper around ParseRef that takes a string -// and parses it with the HCL native syntax traversal parser before -// interpreting it. -// -// This should be used only in specialized situations since it will cause the -// created references to not have any meaningful source location information. -// If a reference string is coming from a source that should be identified in -// error messages then the caller should instead parse it directly using a -// suitable function from the HCL API and pass the traversal itself to -// ParseRef. -// -// Error diagnostics are returned if either the parsing fails or the analysis -// of the traversal fails. There is no way for the caller to distinguish the -// two kinds of diagnostics programmatically. If error diagnostics are returned -// the returned reference may be nil or incomplete. -func ParseRefStr(str string) (*Reference, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1}) - diags = diags.Append(parseDiags) - if parseDiags.HasErrors() { - return nil, diags - } - - ref, targetDiags := ParseRef(traversal) - diags = diags.Append(targetDiags) - return ref, diags -} - -func parseRef(traversal hcl.Traversal) (*Reference, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - root := traversal.RootName() - rootRange := traversal[0].SourceRange() - - switch root { - - case "count": - name, rng, remain, diags := parseSingleAttrRef(traversal) - return &Reference{ - Subject: CountAttr{Name: name}, - SourceRange: tfdiags.SourceRangeFromHCL(rng), - Remaining: remain, - }, diags - - case "each": - name, rng, remain, diags := parseSingleAttrRef(traversal) - return &Reference{ - Subject: ForEachAttr{Name: name}, - SourceRange: tfdiags.SourceRangeFromHCL(rng), - Remaining: remain, - }, diags - - case "data": - if len(traversal) < 3 { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid reference", - Detail: `The "data" object must be followed by two attribute names: the data source type and the resource name.`, - Subject: traversal.SourceRange().Ptr(), - }) - return nil, diags - } - remain := traversal[1:] // trim off "data" so we can use our shared resource reference parser - return parseResourceRef(DataResourceMode, rootRange, remain) - - case "local": - name, rng, remain, diags := parseSingleAttrRef(traversal) - return &Reference{ - Subject: LocalValue{Name: name}, - SourceRange: tfdiags.SourceRangeFromHCL(rng), - Remaining: remain, - }, diags - - case "module": - callName, callRange, remain, diags := parseSingleAttrRef(traversal) - if diags.HasErrors() { - return nil, diags - } - - // A traversal starting with "module" can either be a reference to - // an entire module instance or to a single output from a module - // instance, depending on what we find after this introducer. - - callInstance := ModuleCallInstance{ - Call: ModuleCall{ - Name: callName, - }, - Key: NoKey, - } - - if len(remain) == 0 { - // Reference to an entire module instance. Might alternatively - // be a reference to a collection of instances of a particular - // module, but the caller will need to deal with that ambiguity - // since we don't have enough context here. - return &Reference{ - Subject: callInstance, - SourceRange: tfdiags.SourceRangeFromHCL(callRange), - Remaining: remain, - }, diags - } - - if idxTrav, ok := remain[0].(hcl.TraverseIndex); ok { - var err error - callInstance.Key, err = ParseInstanceKey(idxTrav.Key) - if err != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid index key", - Detail: fmt.Sprintf("Invalid index for module instance: %s.", err), - Subject: &idxTrav.SrcRange, - }) - return nil, diags - } - remain = remain[1:] - - if len(remain) == 0 { - // Also a reference to an entire module instance, but we have a key - // now. - return &Reference{ - Subject: callInstance, - SourceRange: tfdiags.SourceRangeFromHCL(hcl.RangeBetween(callRange, idxTrav.SrcRange)), - Remaining: remain, - }, diags - } - } - - if attrTrav, ok := remain[0].(hcl.TraverseAttr); ok { - remain = remain[1:] - return &Reference{ - Subject: ModuleCallOutput{ - Name: attrTrav.Name, - Call: callInstance, - }, - SourceRange: tfdiags.SourceRangeFromHCL(hcl.RangeBetween(callRange, attrTrav.SrcRange)), - Remaining: remain, - }, diags - } - - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid reference", - Detail: "Module instance objects do not support this operation.", - Subject: remain[0].SourceRange().Ptr(), - }) - return nil, diags - - case "path": - name, rng, remain, diags := parseSingleAttrRef(traversal) - return &Reference{ - Subject: PathAttr{Name: name}, - SourceRange: tfdiags.SourceRangeFromHCL(rng), - Remaining: remain, - }, diags - - case "self": - return &Reference{ - Subject: Self, - SourceRange: tfdiags.SourceRangeFromHCL(rootRange), - Remaining: traversal[1:], - }, diags - - case "terraform": - name, rng, remain, diags := parseSingleAttrRef(traversal) - return &Reference{ - Subject: TerraformAttr{Name: name}, - SourceRange: tfdiags.SourceRangeFromHCL(rng), - Remaining: remain, - }, diags - - case "var": - name, rng, remain, diags := parseSingleAttrRef(traversal) - return &Reference{ - Subject: InputVariable{Name: name}, - SourceRange: tfdiags.SourceRangeFromHCL(rng), - Remaining: remain, - }, diags - - default: - return parseResourceRef(ManagedResourceMode, rootRange, traversal) - } -} - -func parseResourceRef(mode ResourceMode, startRange hcl.Range, traversal hcl.Traversal) (*Reference, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - if len(traversal) < 2 { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid reference", - Detail: `A reference to a resource type must be followed by at least one attribute access, specifying the resource name.`, - Subject: hcl.RangeBetween(traversal[0].SourceRange(), traversal[len(traversal)-1].SourceRange()).Ptr(), - }) - return nil, diags - } - - var typeName, name string - switch tt := traversal[0].(type) { // Could be either root or attr, depending on our resource mode - case hcl.TraverseRoot: - typeName = tt.Name - case hcl.TraverseAttr: - typeName = tt.Name - default: - // If it isn't a TraverseRoot then it must be a "data" reference. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid reference", - Detail: `The "data" object does not support this operation.`, - Subject: traversal[0].SourceRange().Ptr(), - }) - return nil, diags - } - - attrTrav, ok := traversal[1].(hcl.TraverseAttr) - if !ok { - var what string - switch mode { - case DataResourceMode: - what = "data source" - default: - what = "resource type" - } - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid reference", - Detail: fmt.Sprintf(`A reference to a %s must be followed by at least one attribute access, specifying the resource name.`, what), - Subject: traversal[1].SourceRange().Ptr(), - }) - return nil, diags - } - name = attrTrav.Name - rng := hcl.RangeBetween(startRange, attrTrav.SrcRange) - remain := traversal[2:] - - resourceAddr := Resource{ - Mode: mode, - Type: typeName, - Name: name, - } - resourceInstAddr := ResourceInstance{ - Resource: resourceAddr, - Key: NoKey, - } - - if len(remain) == 0 { - // This might actually be a reference to the collection of all instances - // of the resource, but we don't have enough context here to decide - // so we'll let the caller resolve that ambiguity. - return &Reference{ - Subject: resourceAddr, - SourceRange: tfdiags.SourceRangeFromHCL(rng), - }, diags - } - - if idxTrav, ok := remain[0].(hcl.TraverseIndex); ok { - var err error - resourceInstAddr.Key, err = ParseInstanceKey(idxTrav.Key) - if err != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid index key", - Detail: fmt.Sprintf("Invalid index for resource instance: %s.", err), - Subject: &idxTrav.SrcRange, - }) - return nil, diags - } - remain = remain[1:] - rng = hcl.RangeBetween(rng, idxTrav.SrcRange) - } - - return &Reference{ - Subject: resourceInstAddr, - SourceRange: tfdiags.SourceRangeFromHCL(rng), - Remaining: remain, - }, diags -} - -func parseSingleAttrRef(traversal hcl.Traversal) (string, hcl.Range, hcl.Traversal, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - root := traversal.RootName() - rootRange := traversal[0].SourceRange() - - if len(traversal) < 2 { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid reference", - Detail: fmt.Sprintf("The %q object cannot be accessed directly. Instead, access one of its attributes.", root), - Subject: &rootRange, - }) - return "", hcl.Range{}, nil, diags - } - if attrTrav, ok := traversal[1].(hcl.TraverseAttr); ok { - return attrTrav.Name, hcl.RangeBetween(rootRange, attrTrav.SrcRange), traversal[2:], diags - } - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid reference", - Detail: fmt.Sprintf("The %q object does not support this operation.", root), - Subject: traversal[1].SourceRange().Ptr(), - }) - return "", hcl.Range{}, nil, diags -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/parse_target.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/parse_target.go deleted file mode 100644 index 5b922e8b..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/parse_target.go +++ /dev/null @@ -1,240 +0,0 @@ -package addrs - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2/hclsyntax" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// Target describes a targeted address with source location information. -type Target struct { - Subject Targetable - SourceRange tfdiags.SourceRange -} - -// ParseTarget attempts to interpret the given traversal as a targetable -// address. The given traversal must be absolute, or this function will -// panic. -// -// If no error diagnostics are returned, the returned target includes the -// address that was extracted and the source range it was extracted from. -// -// If error diagnostics are returned then the Target value is invalid and -// must not be used. -func ParseTarget(traversal hcl.Traversal) (*Target, tfdiags.Diagnostics) { - path, remain, diags := parseModuleInstancePrefix(traversal) - if diags.HasErrors() { - return nil, diags - } - - rng := tfdiags.SourceRangeFromHCL(traversal.SourceRange()) - - if len(remain) == 0 { - return &Target{ - Subject: path, - SourceRange: rng, - }, diags - } - - mode := ManagedResourceMode - if remain.RootName() == "data" { - mode = DataResourceMode - remain = remain[1:] - } - - if len(remain) < 2 { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid address", - Detail: "Resource specification must include a resource type and name.", - Subject: remain.SourceRange().Ptr(), - }) - return nil, diags - } - - var typeName, name string - switch tt := remain[0].(type) { - case hcl.TraverseRoot: - typeName = tt.Name - case hcl.TraverseAttr: - typeName = tt.Name - default: - switch mode { - case ManagedResourceMode: - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid address", - Detail: "A resource type name is required.", - Subject: remain[0].SourceRange().Ptr(), - }) - case DataResourceMode: - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid address", - Detail: "A data source name is required.", - Subject: remain[0].SourceRange().Ptr(), - }) - default: - panic("unknown mode") - } - return nil, diags - } - - switch tt := remain[1].(type) { - case hcl.TraverseAttr: - name = tt.Name - default: - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid address", - Detail: "A resource name is required.", - Subject: remain[1].SourceRange().Ptr(), - }) - return nil, diags - } - - var subject Targetable - remain = remain[2:] - switch len(remain) { - case 0: - subject = path.Resource(mode, typeName, name) - case 1: - if tt, ok := remain[0].(hcl.TraverseIndex); ok { - key, err := ParseInstanceKey(tt.Key) - if err != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid address", - Detail: fmt.Sprintf("Invalid resource instance key: %s.", err), - Subject: remain[0].SourceRange().Ptr(), - }) - return nil, diags - } - - subject = path.ResourceInstance(mode, typeName, name, key) - } else { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid address", - Detail: "Resource instance key must be given in square brackets.", - Subject: remain[0].SourceRange().Ptr(), - }) - return nil, diags - } - default: - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid address", - Detail: "Unexpected extra operators after address.", - Subject: remain[1].SourceRange().Ptr(), - }) - return nil, diags - } - - return &Target{ - Subject: subject, - SourceRange: rng, - }, diags -} - -// ParseTargetStr is a helper wrapper around ParseTarget that takes a string -// and parses it with the HCL native syntax traversal parser before -// interpreting it. -// -// This should be used only in specialized situations since it will cause the -// created references to not have any meaningful source location information. -// If a target string is coming from a source that should be identified in -// error messages then the caller should instead parse it directly using a -// suitable function from the HCL API and pass the traversal itself to -// ParseTarget. -// -// Error diagnostics are returned if either the parsing fails or the analysis -// of the traversal fails. There is no way for the caller to distinguish the -// two kinds of diagnostics programmatically. If error diagnostics are returned -// the returned target may be nil or incomplete. -func ParseTargetStr(str string) (*Target, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1}) - diags = diags.Append(parseDiags) - if parseDiags.HasErrors() { - return nil, diags - } - - target, targetDiags := ParseTarget(traversal) - diags = diags.Append(targetDiags) - return target, diags -} - -// ParseAbsResourceInstance attempts to interpret the given traversal as an -// absolute resource instance address, using the same syntax as expected by -// ParseTarget. -// -// If no error diagnostics are returned, the returned target includes the -// address that was extracted and the source range it was extracted from. -// -// If error diagnostics are returned then the AbsResource value is invalid and -// must not be used. -func ParseAbsResourceInstance(traversal hcl.Traversal) (AbsResourceInstance, tfdiags.Diagnostics) { - addr, diags := ParseTarget(traversal) - if diags.HasErrors() { - return AbsResourceInstance{}, diags - } - - switch tt := addr.Subject.(type) { - - case AbsResource: - return tt.Instance(NoKey), diags - - case AbsResourceInstance: - return tt, diags - - case ModuleInstance: // Catch likely user error with specialized message - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid address", - Detail: "A resource instance address is required here. The module path must be followed by a resource instance specification.", - Subject: traversal.SourceRange().Ptr(), - }) - return AbsResourceInstance{}, diags - - default: // Generic message for other address types - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid address", - Detail: "A resource address is required here.", - Subject: traversal.SourceRange().Ptr(), - }) - return AbsResourceInstance{}, diags - - } -} - -// ParseAbsResourceInstanceStr is a helper wrapper around -// ParseAbsResourceInstance that takes a string and parses it with the HCL -// native syntax traversal parser before interpreting it. -// -// Error diagnostics are returned if either the parsing fails or the analysis -// of the traversal fails. There is no way for the caller to distinguish the -// two kinds of diagnostics programmatically. If error diagnostics are returned -// the returned address may be incomplete. -// -// Since this function has no context about the source of the given string, -// any returned diagnostics will not have meaningful source location -// information. -func ParseAbsResourceInstanceStr(str string) (AbsResourceInstance, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1}) - diags = diags.Append(parseDiags) - if parseDiags.HasErrors() { - return AbsResourceInstance{}, diags - } - - addr, addrDiags := ParseAbsResourceInstance(traversal) - diags = diags.Append(addrDiags) - return addr, diags -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/path_attr.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/path_attr.go deleted file mode 100644 index cfc13f4b..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/path_attr.go +++ /dev/null @@ -1,12 +0,0 @@ -package addrs - -// PathAttr is the address of an attribute of the "path" object in -// the interpolation scope, like "path.module". -type PathAttr struct { - referenceable - Name string -} - -func (pa PathAttr) String() string { - return "path." + pa.Name -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/provider_config.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/provider_config.go deleted file mode 100644 index c6fce1a5..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/provider_config.go +++ /dev/null @@ -1,289 +0,0 @@ -package addrs - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" -) - -// ProviderConfig is the address of a provider configuration. -type ProviderConfig struct { - Type string - - // If not empty, Alias identifies which non-default (aliased) provider - // configuration this address refers to. - Alias string -} - -// ParseProviderConfigCompact parses the given absolute traversal as a relative -// provider address in compact form. The following are examples of traversals -// that can be successfully parsed as compact relative provider configuration -// addresses: -// -// aws -// aws.foo -// -// This function will panic if given a relative traversal. -// -// If the returned diagnostics contains errors then the result value is invalid -// and must not be used. -func ParseProviderConfigCompact(traversal hcl.Traversal) (ProviderConfig, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - ret := ProviderConfig{ - Type: traversal.RootName(), - } - - if len(traversal) < 2 { - // Just a type name, then. - return ret, diags - } - - aliasStep := traversal[1] - switch ts := aliasStep.(type) { - case hcl.TraverseAttr: - ret.Alias = ts.Name - return ret, diags - default: - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid provider configuration address", - Detail: "The provider type name must either stand alone or be followed by an alias name separated with a dot.", - Subject: aliasStep.SourceRange().Ptr(), - }) - } - - if len(traversal) > 2 { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid provider configuration address", - Detail: "Extraneous extra operators after provider configuration address.", - Subject: traversal[2:].SourceRange().Ptr(), - }) - } - - return ret, diags -} - -// ParseProviderConfigCompactStr is a helper wrapper around ParseProviderConfigCompact -// that takes a string and parses it with the HCL native syntax traversal parser -// before interpreting it. -// -// This should be used only in specialized situations since it will cause the -// created references to not have any meaningful source location information. -// If a reference string is coming from a source that should be identified in -// error messages then the caller should instead parse it directly using a -// suitable function from the HCL API and pass the traversal itself to -// ParseProviderConfigCompact. -// -// Error diagnostics are returned if either the parsing fails or the analysis -// of the traversal fails. There is no way for the caller to distinguish the -// two kinds of diagnostics programmatically. If error diagnostics are returned -// then the returned address is invalid. -func ParseProviderConfigCompactStr(str string) (ProviderConfig, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1}) - diags = diags.Append(parseDiags) - if parseDiags.HasErrors() { - return ProviderConfig{}, diags - } - - addr, addrDiags := ParseProviderConfigCompact(traversal) - diags = diags.Append(addrDiags) - return addr, diags -} - -// Absolute returns an AbsProviderConfig from the receiver and the given module -// instance address. -func (pc ProviderConfig) Absolute(module ModuleInstance) AbsProviderConfig { - return AbsProviderConfig{ - Module: module, - ProviderConfig: pc, - } -} - -func (pc ProviderConfig) String() string { - if pc.Type == "" { - // Should never happen; always indicates a bug - return "provider." - } - - if pc.Alias != "" { - return fmt.Sprintf("provider.%s.%s", pc.Type, pc.Alias) - } - - return "provider." + pc.Type -} - -// StringCompact is an alternative to String that returns the form that can -// be parsed by ParseProviderConfigCompact, without the "provider." prefix. -func (pc ProviderConfig) StringCompact() string { - if pc.Alias != "" { - return fmt.Sprintf("%s.%s", pc.Type, pc.Alias) - } - return pc.Type -} - -// AbsProviderConfig is the absolute address of a provider configuration -// within a particular module instance. -type AbsProviderConfig struct { - Module ModuleInstance - ProviderConfig ProviderConfig -} - -// ParseAbsProviderConfig parses the given traversal as an absolute provider -// address. The following are examples of traversals that can be successfully -// parsed as absolute provider configuration addresses: -// -// provider.aws -// provider.aws.foo -// module.bar.provider.aws -// module.bar.module.baz.provider.aws.foo -// module.foo[1].provider.aws.foo -// -// This type of address is used, for example, to record the relationships -// between resources and provider configurations in the state structure. -// This type of address is not generally used in the UI, except in error -// messages that refer to provider configurations. -func ParseAbsProviderConfig(traversal hcl.Traversal) (AbsProviderConfig, tfdiags.Diagnostics) { - modInst, remain, diags := parseModuleInstancePrefix(traversal) - ret := AbsProviderConfig{ - Module: modInst, - } - if len(remain) < 2 || remain.RootName() != "provider" { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid provider configuration address", - Detail: "Provider address must begin with \"provider.\", followed by a provider type name.", - Subject: remain.SourceRange().Ptr(), - }) - return ret, diags - } - if len(remain) > 3 { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid provider configuration address", - Detail: "Extraneous operators after provider configuration alias.", - Subject: hcl.Traversal(remain[3:]).SourceRange().Ptr(), - }) - return ret, diags - } - - if tt, ok := remain[1].(hcl.TraverseAttr); ok { - ret.ProviderConfig.Type = tt.Name - } else { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid provider configuration address", - Detail: "The prefix \"provider.\" must be followed by a provider type name.", - Subject: remain[1].SourceRange().Ptr(), - }) - return ret, diags - } - - if len(remain) == 3 { - if tt, ok := remain[2].(hcl.TraverseAttr); ok { - ret.ProviderConfig.Alias = tt.Name - } else { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid provider configuration address", - Detail: "Provider type name must be followed by a configuration alias name.", - Subject: remain[2].SourceRange().Ptr(), - }) - return ret, diags - } - } - - return ret, diags -} - -// ParseAbsProviderConfigStr is a helper wrapper around ParseAbsProviderConfig -// that takes a string and parses it with the HCL native syntax traversal parser -// before interpreting it. -// -// This should be used only in specialized situations since it will cause the -// created references to not have any meaningful source location information. -// If a reference string is coming from a source that should be identified in -// error messages then the caller should instead parse it directly using a -// suitable function from the HCL API and pass the traversal itself to -// ParseAbsProviderConfig. -// -// Error diagnostics are returned if either the parsing fails or the analysis -// of the traversal fails. There is no way for the caller to distinguish the -// two kinds of diagnostics programmatically. If error diagnostics are returned -// the returned address is invalid. -func ParseAbsProviderConfigStr(str string) (AbsProviderConfig, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1}) - diags = diags.Append(parseDiags) - if parseDiags.HasErrors() { - return AbsProviderConfig{}, diags - } - - addr, addrDiags := ParseAbsProviderConfig(traversal) - diags = diags.Append(addrDiags) - return addr, diags -} - -// ProviderConfigDefault returns the address of the default provider config -// of the given type inside the recieving module instance. -func (m ModuleInstance) ProviderConfigDefault(name string) AbsProviderConfig { - return AbsProviderConfig{ - Module: m, - ProviderConfig: ProviderConfig{ - Type: name, - }, - } -} - -// ProviderConfigAliased returns the address of an aliased provider config -// of with given type and alias inside the recieving module instance. -func (m ModuleInstance) ProviderConfigAliased(name, alias string) AbsProviderConfig { - return AbsProviderConfig{ - Module: m, - ProviderConfig: ProviderConfig{ - Type: name, - Alias: alias, - }, - } -} - -// Inherited returns an address that the receiving configuration address might -// inherit from in a parent module. The second bool return value indicates if -// such inheritance is possible, and thus whether the returned address is valid. -// -// Inheritance is possible only for default (un-aliased) providers in modules -// other than the root module. Even if a valid address is returned, inheritence -// may not be performed for other reasons, such as if the calling module -// provided explicit provider configurations within the call for this module. -// The ProviderTransformer graph transform in the main terraform module has -// the authoritative logic for provider inheritance, and this method is here -// mainly just for its benefit. -func (pc AbsProviderConfig) Inherited() (AbsProviderConfig, bool) { - // Can't inherit if we're already in the root. - if len(pc.Module) == 0 { - return AbsProviderConfig{}, false - } - - // Can't inherit if we have an alias. - if pc.ProviderConfig.Alias != "" { - return AbsProviderConfig{}, false - } - - // Otherwise, we might inherit from a configuration with the same - // provider name in the parent module instance. - parentMod := pc.Module.Parent() - return pc.ProviderConfig.Absolute(parentMod), true -} - -func (pc AbsProviderConfig) String() string { - if len(pc.Module) == 0 { - return pc.ProviderConfig.String() - } - return fmt.Sprintf("%s.%s", pc.Module.String(), pc.ProviderConfig.String()) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/provider_type.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/provider_type.go deleted file mode 100644 index 64b8ac86..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/provider_type.go +++ /dev/null @@ -1,7 +0,0 @@ -package addrs - -// ProviderType encapsulates a single provider type. In the future this will be -// extended to include additional fields including Namespace and SourceHost -type ProviderType struct { - Name string -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/referenceable.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/referenceable.go deleted file mode 100644 index 211083a5..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/referenceable.go +++ /dev/null @@ -1,20 +0,0 @@ -package addrs - -// Referenceable is an interface implemented by all address types that can -// appear as references in configuration language expressions. -type Referenceable interface { - // All implementations of this interface must be covered by the type switch - // in lang.Scope.buildEvalContext. - referenceableSigil() - - // String produces a string representation of the address that could be - // parsed as a HCL traversal and passed to ParseRef to produce an identical - // result. - String() string -} - -type referenceable struct { -} - -func (r referenceable) referenceableSigil() { -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/resource.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/resource.go index 103f8a28..f56032b5 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/resource.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/resource.go @@ -2,20 +2,18 @@ package addrs import ( "fmt" - "strings" ) -// Resource is an address for a resource block within configuration, which +// resource is an address for a resource block within configuration, which // contains potentially-multiple resource instances if that configuration // block uses "count" or "for_each". -type Resource struct { - referenceable - Mode ResourceMode +type resource struct { + Mode resourceMode Type string Name string } -func (r Resource) String() string { +func (r resource) String() string { switch r.Mode { case ManagedResourceMode: return fmt.Sprintf("%s.%s", r.Type, r.Name) @@ -28,85 +26,36 @@ func (r Resource) String() string { } } -// Instance produces the address for a specific instance of the receiver -// that is idenfied by the given key. -func (r Resource) Instance(key InstanceKey) ResourceInstance { - return ResourceInstance{ - Resource: r, - Key: key, - } -} - -// Absolute returns an AbsResource from the receiver and the given module -// instance address. -func (r Resource) Absolute(module ModuleInstance) AbsResource { - return AbsResource{ - Module: module, - Resource: r, - } -} - -// DefaultProviderConfig returns the address of the provider configuration -// that should be used for the resource identified by the reciever if it -// does not have a provider configuration address explicitly set in -// configuration. -// -// This method is not able to verify that such a configuration exists, nor -// represent the behavior of automatically inheriting certain provider -// configurations from parent modules. It just does a static analysis of the -// receiving address and returns an address to start from, relative to the -// same module that contains the resource. -func (r Resource) DefaultProviderConfig() ProviderConfig { - typeName := r.Type - if under := strings.Index(typeName, "_"); under != -1 { - typeName = typeName[:under] - } - return ProviderConfig{ - Type: typeName, - } -} - -// ResourceInstance is an address for a specific instance of a resource. +// resourceInstance is an address for a specific instance of a resource. // When a resource is defined in configuration with "count" or "for_each" it // produces zero or more instances, which can be addressed using this type. -type ResourceInstance struct { - referenceable - Resource Resource - Key InstanceKey +type resourceInstance struct { + Resource resource + Key instanceKey } -func (r ResourceInstance) ContainingResource() Resource { +func (r resourceInstance) ContainingResource() resource { return r.Resource } -func (r ResourceInstance) String() string { +func (r resourceInstance) String() string { if r.Key == NoKey { return r.Resource.String() } return r.Resource.String() + r.Key.String() } -// Absolute returns an AbsResourceInstance from the receiver and the given module -// instance address. -func (r ResourceInstance) Absolute(module ModuleInstance) AbsResourceInstance { - return AbsResourceInstance{ - Module: module, - Resource: r, - } -} - -// AbsResource is an absolute address for a resource under a given module path. -type AbsResource struct { - targetable +// absResource is an absolute address for a resource under a given module path. +type absResource struct { Module ModuleInstance - Resource Resource + Resource resource } // Resource returns the address of a particular resource within the receiver. -func (m ModuleInstance) Resource(mode ResourceMode, typeName string, name string) AbsResource { - return AbsResource{ +func (m ModuleInstance) Resource(mode resourceMode, typeName string, name string) absResource { + return absResource{ Module: m, - Resource: Resource{ + Resource: resource{ Mode: mode, Type: typeName, Name: name, @@ -114,55 +63,26 @@ func (m ModuleInstance) Resource(mode ResourceMode, typeName string, name string } } -// Instance produces the address for a specific instance of the receiver -// that is idenfied by the given key. -func (r AbsResource) Instance(key InstanceKey) AbsResourceInstance { - return AbsResourceInstance{ - Module: r.Module, - Resource: r.Resource.Instance(key), - } -} - -// TargetContains implements Targetable by returning true if the given other -// address is either equal to the receiver or is an instance of the -// receiver. -func (r AbsResource) TargetContains(other Targetable) bool { - switch to := other.(type) { - - case AbsResource: - // We'll use our stringification as a cheat-ish way to test for equality. - return to.String() == r.String() - - case AbsResourceInstance: - return r.TargetContains(to.ContainingResource()) - - default: - return false - - } -} - -func (r AbsResource) String() string { +func (r absResource) String() string { if len(r.Module) == 0 { return r.Resource.String() } return fmt.Sprintf("%s.%s", r.Module.String(), r.Resource.String()) } -// AbsResourceInstance is an absolute address for a resource instance under a +// absResourceInstance is an absolute address for a resource instance under a // given module path. -type AbsResourceInstance struct { - targetable +type absResourceInstance struct { Module ModuleInstance - Resource ResourceInstance + Resource resourceInstance } // ResourceInstance returns the address of a particular resource instance within the receiver. -func (m ModuleInstance) ResourceInstance(mode ResourceMode, typeName string, name string, key InstanceKey) AbsResourceInstance { - return AbsResourceInstance{ +func (m ModuleInstance) ResourceInstance(mode resourceMode, typeName string, name string, key instanceKey) absResourceInstance { + return absResourceInstance{ Module: m, - Resource: ResourceInstance{ - Resource: Resource{ + Resource: resourceInstance{ + Resource: resource{ Mode: mode, Type: typeName, Name: name, @@ -174,81 +94,37 @@ func (m ModuleInstance) ResourceInstance(mode ResourceMode, typeName string, nam // ContainingResource returns the address of the resource that contains the // receving resource instance. In other words, it discards the key portion -// of the address to produce an AbsResource value. -func (r AbsResourceInstance) ContainingResource() AbsResource { - return AbsResource{ +// of the address to produce an absResource value. +func (r absResourceInstance) ContainingResource() absResource { + return absResource{ Module: r.Module, Resource: r.Resource.ContainingResource(), } } -// TargetContains implements Targetable by returning true if the given other -// address is equal to the receiver. -func (r AbsResourceInstance) TargetContains(other Targetable) bool { - switch to := other.(type) { - - case AbsResourceInstance: - // We'll use our stringification as a cheat-ish way to test for equality. - return to.String() == r.String() - - default: - return false - - } -} - -func (r AbsResourceInstance) String() string { +func (r absResourceInstance) String() string { if len(r.Module) == 0 { return r.Resource.String() } return fmt.Sprintf("%s.%s", r.Module.String(), r.Resource.String()) } -// Less returns true if the receiver should sort before the given other value -// in a sorted list of addresses. -func (r AbsResourceInstance) Less(o AbsResourceInstance) bool { - switch { - - case len(r.Module) != len(o.Module): - return len(r.Module) < len(o.Module) - - case r.Module.String() != o.Module.String(): - return r.Module.Less(o.Module) - - case r.Resource.Resource.Mode != o.Resource.Resource.Mode: - return r.Resource.Resource.Mode == DataResourceMode - - case r.Resource.Resource.Type != o.Resource.Resource.Type: - return r.Resource.Resource.Type < o.Resource.Resource.Type - - case r.Resource.Resource.Name != o.Resource.Resource.Name: - return r.Resource.Resource.Name < o.Resource.Resource.Name - - case r.Resource.Key != o.Resource.Key: - return InstanceKeyLess(r.Resource.Key, o.Resource.Key) - - default: - return false - - } -} - -// ResourceMode defines which lifecycle applies to a given resource. Each +// resourceMode defines which lifecycle applies to a given resource. Each // resource lifecycle has a slightly different address format. -type ResourceMode rune +type resourceMode rune -//go:generate go run golang.org/x/tools/cmd/stringer -type ResourceMode +//go:generate go run golang.org/x/tools/cmd/stringer -type resourceMode const ( // InvalidResourceMode is the zero value of ResourceMode and is not // a valid resource mode. - InvalidResourceMode ResourceMode = 0 + InvalidResourceMode resourceMode = 0 // ManagedResourceMode indicates a managed resource, as defined by // "resource" blocks in configuration. - ManagedResourceMode ResourceMode = 'M' + ManagedResourceMode resourceMode = 'M' // DataResourceMode indicates a data resource, as defined by // "data" blocks in configuration. - DataResourceMode ResourceMode = 'D' + DataResourceMode resourceMode = 'D' ) diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/resource_phase.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/resource_phase.go deleted file mode 100644 index 9bdbdc42..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/resource_phase.go +++ /dev/null @@ -1,105 +0,0 @@ -package addrs - -import "fmt" - -// ResourceInstancePhase is a special kind of reference used only internally -// during graph building to represent resource instances that are in a -// non-primary state. -// -// Graph nodes can declare themselves referenceable via an instance phase -// or can declare that they reference an instance phase in order to accomodate -// secondary graph nodes dealing with, for example, destroy actions. -// -// This special reference type cannot be accessed directly by end-users, and -// should never be shown in the UI. -type ResourceInstancePhase struct { - referenceable - ResourceInstance ResourceInstance - Phase ResourceInstancePhaseType -} - -var _ Referenceable = ResourceInstancePhase{} - -// Phase returns a special "phase address" for the receving instance. See the -// documentation of ResourceInstancePhase for the limited situations where this -// is intended to be used. -func (r ResourceInstance) Phase(rpt ResourceInstancePhaseType) ResourceInstancePhase { - return ResourceInstancePhase{ - ResourceInstance: r, - Phase: rpt, - } -} - -// ContainingResource returns an address for the same phase of the resource -// that this instance belongs to. -func (rp ResourceInstancePhase) ContainingResource() ResourcePhase { - return rp.ResourceInstance.Resource.Phase(rp.Phase) -} - -func (rp ResourceInstancePhase) String() string { - // We use a different separator here than usual to ensure that we'll - // never conflict with any non-phased resource instance string. This - // is intentionally something that would fail parsing with ParseRef, - // because this special address type should never be exposed in the UI. - return fmt.Sprintf("%s#%s", rp.ResourceInstance, rp.Phase) -} - -// ResourceInstancePhaseType is an enumeration used with ResourceInstancePhase. -type ResourceInstancePhaseType string - -const ( - // ResourceInstancePhaseDestroy represents the "destroy" phase of a - // resource instance. - ResourceInstancePhaseDestroy ResourceInstancePhaseType = "destroy" - - // ResourceInstancePhaseDestroyCBD is similar to ResourceInstancePhaseDestroy - // but is used for resources that have "create_before_destroy" set, thus - // requiring a different dependency ordering. - ResourceInstancePhaseDestroyCBD ResourceInstancePhaseType = "destroy-cbd" -) - -func (rpt ResourceInstancePhaseType) String() string { - return string(rpt) -} - -// ResourcePhase is a special kind of reference used only internally -// during graph building to represent resources that are in a -// non-primary state. -// -// Graph nodes can declare themselves referenceable via a resource phase -// or can declare that they reference a resource phase in order to accomodate -// secondary graph nodes dealing with, for example, destroy actions. -// -// Since resources (as opposed to instances) aren't actually phased, this -// address type is used only as an approximation during initial construction -// of the resource-oriented plan graph, under the assumption that resource -// instances with ResourceInstancePhase addresses will be created in dynamic -// subgraphs during the graph walk. -// -// This special reference type cannot be accessed directly by end-users, and -// should never be shown in the UI. -type ResourcePhase struct { - referenceable - Resource Resource - Phase ResourceInstancePhaseType -} - -var _ Referenceable = ResourcePhase{} - -// Phase returns a special "phase address" for the receving instance. See the -// documentation of ResourceInstancePhase for the limited situations where this -// is intended to be used. -func (r Resource) Phase(rpt ResourceInstancePhaseType) ResourcePhase { - return ResourcePhase{ - Resource: r, - Phase: rpt, - } -} - -func (rp ResourcePhase) String() string { - // We use a different separator here than usual to ensure that we'll - // never conflict with any non-phased resource instance string. This - // is intentionally something that would fail parsing with ParseRef, - // because this special address type should never be exposed in the UI. - return fmt.Sprintf("%s#%s", rp.Resource, rp.Phase) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/resourcemode_string.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/resourcemode_string.go index 0b5c33f8..3b2e65c3 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/resourcemode_string.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/resourcemode_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type ResourceMode"; DO NOT EDIT. +// Code generated by "stringer -type resourceMode"; DO NOT EDIT. package addrs @@ -14,20 +14,20 @@ func _() { } const ( - _ResourceMode_name_0 = "InvalidResourceMode" - _ResourceMode_name_1 = "DataResourceMode" - _ResourceMode_name_2 = "ManagedResourceMode" + _resourceMode_name_0 = "InvalidResourceMode" + _resourceMode_name_1 = "DataResourceMode" + _resourceMode_name_2 = "ManagedResourceMode" ) -func (i ResourceMode) String() string { +func (i resourceMode) String() string { switch { case i == 0: - return _ResourceMode_name_0 + return _resourceMode_name_0 case i == 68: - return _ResourceMode_name_1 + return _resourceMode_name_1 case i == 77: - return _ResourceMode_name_2 + return _resourceMode_name_2 default: - return "ResourceMode(" + strconv.FormatInt(int64(i), 10) + ")" + return "resourceMode(" + strconv.FormatInt(int64(i), 10) + ")" } } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/self.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/self.go deleted file mode 100644 index 7f24eaf0..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/self.go +++ /dev/null @@ -1,14 +0,0 @@ -package addrs - -// Self is the address of the special object "self" that behaves as an alias -// for a containing object currently in scope. -const Self selfT = 0 - -type selfT int - -func (s selfT) referenceableSigil() { -} - -func (s selfT) String() string { - return "self" -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/targetable.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/targetable.go deleted file mode 100644 index 16819a5a..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/targetable.go +++ /dev/null @@ -1,26 +0,0 @@ -package addrs - -// Targetable is an interface implemented by all address types that can be -// used as "targets" for selecting sub-graphs of a graph. -type Targetable interface { - targetableSigil() - - // TargetContains returns true if the receiver is considered to contain - // the given other address. Containment, for the purpose of targeting, - // means that if a container address is targeted then all of the - // addresses within it are also implicitly targeted. - // - // A targetable address always contains at least itself. - TargetContains(other Targetable) bool - - // String produces a string representation of the address that could be - // parsed as a HCL traversal and passed to ParseTarget to produce an - // identical result. - String() string -} - -type targetable struct { -} - -func (r targetable) targetableSigil() { -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/terraform_attr.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/terraform_attr.go deleted file mode 100644 index a880182a..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/addrs/terraform_attr.go +++ /dev/null @@ -1,12 +0,0 @@ -package addrs - -// TerraformAttr is the address of an attribute of the "terraform" object in -// the interpolation scope, like "terraform.workspace". -type TerraformAttr struct { - referenceable - Name string -} - -func (ta TerraformAttr) String() string { - return "terraform." + ta.Name -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/command/format/diagnostic.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/command/format/diagnostic.go deleted file mode 100644 index c054acf0..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/command/format/diagnostic.go +++ /dev/null @@ -1,295 +0,0 @@ -package format - -import ( - "bufio" - "bytes" - "fmt" - "sort" - "strings" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hcled" - "github.com/hashicorp/hcl/v2/hclparse" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - "github.com/mitchellh/colorstring" - wordwrap "github.com/mitchellh/go-wordwrap" - "github.com/zclconf/go-cty/cty" -) - -// Diagnostic formats a single diagnostic message. -// -// The width argument specifies at what column the diagnostic messages will -// be wrapped. If set to zero, messages will not be wrapped by this function -// at all. Although the long-form text parts of the message are wrapped, -// not all aspects of the message are guaranteed to fit within the specified -// terminal width. -func Diagnostic(diag tfdiags.Diagnostic, sources map[string][]byte, color *colorstring.Colorize, width int) string { - if diag == nil { - // No good reason to pass a nil diagnostic in here... - return "" - } - - var buf bytes.Buffer - - switch diag.Severity() { - case tfdiags.Error: - buf.WriteString(color.Color("\n[bold][red]Error: [reset]")) - case tfdiags.Warning: - buf.WriteString(color.Color("\n[bold][yellow]Warning: [reset]")) - default: - // Clear out any coloring that might be applied by Terraform's UI helper, - // so our result is not context-sensitive. - buf.WriteString(color.Color("\n[reset]")) - } - - desc := diag.Description() - sourceRefs := diag.Source() - - // We don't wrap the summary, since we expect it to be terse, and since - // this is where we put the text of a native Go error it may not always - // be pure text that lends itself well to word-wrapping. - fmt.Fprintf(&buf, color.Color("[bold]%s[reset]\n\n"), desc.Summary) - - if sourceRefs.Subject != nil { - // We'll borrow HCL's range implementation here, because it has some - // handy features to help us produce a nice source code snippet. - highlightRange := sourceRefs.Subject.ToHCL() - snippetRange := highlightRange - if sourceRefs.Context != nil { - snippetRange = sourceRefs.Context.ToHCL() - } - - // Make sure the snippet includes the highlight. This should be true - // for any reasonable diagnostic, but we'll make sure. - snippetRange = hcl.RangeOver(snippetRange, highlightRange) - if snippetRange.Empty() { - snippetRange.End.Byte++ - snippetRange.End.Column++ - } - if highlightRange.Empty() { - highlightRange.End.Byte++ - highlightRange.End.Column++ - } - - var src []byte - if sources != nil { - src = sources[snippetRange.Filename] - } - if src == nil { - // This should generally not happen, as long as sources are always - // loaded through the main loader. We may load things in other - // ways in weird cases, so we'll tolerate it at the expense of - // a not-so-helpful error message. - fmt.Fprintf(&buf, " on %s line %d:\n (source code not available)\n", highlightRange.Filename, highlightRange.Start.Line) - } else { - file, offset := parseRange(src, highlightRange) - - headerRange := highlightRange - - contextStr := hcled.ContextString(file, offset-1) - if contextStr != "" { - contextStr = ", in " + contextStr - } - - fmt.Fprintf(&buf, " on %s line %d%s:\n", headerRange.Filename, headerRange.Start.Line, contextStr) - - // Config snippet rendering - sc := hcl.NewRangeScanner(src, highlightRange.Filename, bufio.ScanLines) - for sc.Scan() { - lineRange := sc.Range() - if !lineRange.Overlaps(snippetRange) { - continue - } - beforeRange, highlightedRange, afterRange := lineRange.PartitionAround(highlightRange) - before := beforeRange.SliceBytes(src) - highlighted := highlightedRange.SliceBytes(src) - after := afterRange.SliceBytes(src) - fmt.Fprintf( - &buf, color.Color("%4d: %s[underline]%s[reset]%s\n"), - lineRange.Start.Line, - before, highlighted, after, - ) - } - - } - - if fromExpr := diag.FromExpr(); fromExpr != nil { - // We may also be able to generate information about the dynamic - // values of relevant variables at the point of evaluation, then. - // This is particularly useful for expressions that get evaluated - // multiple times with different values, such as blocks using - // "count" and "for_each", or within "for" expressions. - expr := fromExpr.Expression - ctx := fromExpr.EvalContext - vars := expr.Variables() - stmts := make([]string, 0, len(vars)) - seen := make(map[string]struct{}, len(vars)) - Traversals: - for _, traversal := range vars { - for len(traversal) > 1 { - val, diags := traversal.TraverseAbs(ctx) - if diags.HasErrors() { - // Skip anything that generates errors, since we probably - // already have the same error in our diagnostics set - // already. - traversal = traversal[:len(traversal)-1] - continue - } - - traversalStr := traversalStr(traversal) - if _, exists := seen[traversalStr]; exists { - continue Traversals // don't show duplicates when the same variable is referenced multiple times - } - switch { - case !val.IsKnown(): - // Can't say anything about this yet, then. - continue Traversals - case val.IsNull(): - stmts = append(stmts, fmt.Sprintf(color.Color("[bold]%s[reset] is null"), traversalStr)) - default: - stmts = append(stmts, fmt.Sprintf(color.Color("[bold]%s[reset] is %s"), traversalStr, compactValueStr(val))) - } - seen[traversalStr] = struct{}{} - } - } - - sort.Strings(stmts) // FIXME: Should maybe use a traversal-aware sort that can sort numeric indexes properly? - - if len(stmts) > 0 { - fmt.Fprint(&buf, color.Color(" [dark_gray]|----------------[reset]\n")) - } - for _, stmt := range stmts { - fmt.Fprintf(&buf, color.Color(" [dark_gray]|[reset] %s\n"), stmt) - } - } - - buf.WriteByte('\n') - } - - if desc.Detail != "" { - detail := desc.Detail - if width != 0 { - detail = wordwrap.WrapString(detail, uint(width)) - } - fmt.Fprintf(&buf, "%s\n", detail) - } - - return buf.String() -} - -func parseRange(src []byte, rng hcl.Range) (*hcl.File, int) { - filename := rng.Filename - offset := rng.Start.Byte - - // We need to re-parse here to get a *hcl.File we can interrogate. This - // is not awesome since we presumably already parsed the file earlier too, - // but this re-parsing is architecturally simpler than retaining all of - // the hcl.File objects and we only do this in the case of an error anyway - // so the overhead here is not a big problem. - parser := hclparse.NewParser() - var file *hcl.File - var diags hcl.Diagnostics - if strings.HasSuffix(filename, ".json") { - file, diags = parser.ParseJSON(src, filename) - } else { - file, diags = parser.ParseHCL(src, filename) - } - if diags.HasErrors() { - return file, offset - } - - return file, offset -} - -// traversalStr produces a representation of an HCL traversal that is compact, -// resembles HCL native syntax, and is suitable for display in the UI. -func traversalStr(traversal hcl.Traversal) string { - // This is a specialized subset of traversal rendering tailored to - // producing helpful contextual messages in diagnostics. It is not - // comprehensive nor intended to be used for other purposes. - - var buf bytes.Buffer - for _, step := range traversal { - switch tStep := step.(type) { - case hcl.TraverseRoot: - buf.WriteString(tStep.Name) - case hcl.TraverseAttr: - buf.WriteByte('.') - buf.WriteString(tStep.Name) - case hcl.TraverseIndex: - buf.WriteByte('[') - if keyTy := tStep.Key.Type(); keyTy.IsPrimitiveType() { - buf.WriteString(compactValueStr(tStep.Key)) - } else { - // We'll just use a placeholder for more complex values, - // since otherwise our result could grow ridiculously long. - buf.WriteString("...") - } - buf.WriteByte(']') - } - } - return buf.String() -} - -// compactValueStr produces a compact, single-line summary of a given value -// that is suitable for display in the UI. -// -// For primitives it returns a full representation, while for more complex -// types it instead summarizes the type, size, etc to produce something -// that is hopefully still somewhat useful but not as verbose as a rendering -// of the entire data structure. -func compactValueStr(val cty.Value) string { - // This is a specialized subset of value rendering tailored to producing - // helpful but concise messages in diagnostics. It is not comprehensive - // nor intended to be used for other purposes. - - ty := val.Type() - switch { - case val.IsNull(): - return "null" - case !val.IsKnown(): - // Should never happen here because we should filter before we get - // in here, but we'll do something reasonable rather than panic. - return "(not yet known)" - case ty == cty.Bool: - if val.True() { - return "true" - } - return "false" - case ty == cty.Number: - bf := val.AsBigFloat() - return bf.Text('g', 10) - case ty == cty.String: - // Go string syntax is not exactly the same as HCL native string syntax, - // but we'll accept the minor edge-cases where this is different here - // for now, just to get something reasonable here. - return fmt.Sprintf("%q", val.AsString()) - case ty.IsCollectionType() || ty.IsTupleType(): - l := val.LengthInt() - switch l { - case 0: - return "empty " + ty.FriendlyName() - case 1: - return ty.FriendlyName() + " with 1 element" - default: - return fmt.Sprintf("%s with %d elements", ty.FriendlyName(), l) - } - case ty.IsObjectType(): - atys := ty.AttributeTypes() - l := len(atys) - switch l { - case 0: - return "object with no attributes" - case 1: - var name string - for k := range atys { - name = k - } - return fmt.Sprintf("object with 1 attribute %q", name) - default: - return fmt.Sprintf("object with %d attributes", l) - } - default: - return ty.FriendlyName() - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/command/format/diff.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/command/format/diff.go deleted file mode 100644 index 0a2aa7d0..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/command/format/diff.go +++ /dev/null @@ -1,1192 +0,0 @@ -package format - -import ( - "bufio" - "bytes" - "fmt" - "sort" - "strings" - - "github.com/mitchellh/colorstring" - "github.com/zclconf/go-cty/cty" - ctyjson "github.com/zclconf/go-cty/cty/json" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// ResourceChange returns a string representation of a change to a particular -// resource, for inclusion in user-facing plan output. -// -// The resource schema must be provided along with the change so that the -// formatted change can reflect the configuration structure for the associated -// resource. -// -// If "color" is non-nil, it will be used to color the result. Otherwise, -// no color codes will be included. -func ResourceChange( - change *plans.ResourceInstanceChangeSrc, - tainted bool, - schema *configschema.Block, - color *colorstring.Colorize, -) string { - addr := change.Addr - var buf bytes.Buffer - - if color == nil { - color = &colorstring.Colorize{ - Colors: colorstring.DefaultColors, - Disable: true, - Reset: false, - } - } - - dispAddr := addr.String() - if change.DeposedKey != states.NotDeposed { - dispAddr = fmt.Sprintf("%s (deposed object %s)", dispAddr, change.DeposedKey) - } - - switch change.Action { - case plans.Create: - buf.WriteString(color.Color(fmt.Sprintf("[bold] # %s[reset] will be created", dispAddr))) - case plans.Read: - buf.WriteString(color.Color(fmt.Sprintf("[bold] # %s[reset] will be read during apply\n # (config refers to values not yet known)", dispAddr))) - case plans.Update: - buf.WriteString(color.Color(fmt.Sprintf("[bold] # %s[reset] will be updated in-place", dispAddr))) - case plans.CreateThenDelete, plans.DeleteThenCreate: - if tainted { - buf.WriteString(color.Color(fmt.Sprintf("[bold] # %s[reset] is tainted, so must be [bold][red]replaced", dispAddr))) - } else { - buf.WriteString(color.Color(fmt.Sprintf("[bold] # %s[reset] must be [bold][red]replaced", dispAddr))) - } - case plans.Delete: - buf.WriteString(color.Color(fmt.Sprintf("[bold] # %s[reset] will be [bold][red]destroyed", dispAddr))) - default: - // should never happen, since the above is exhaustive - buf.WriteString(fmt.Sprintf("%s has an action the plan renderer doesn't support (this is a bug)", dispAddr)) - } - buf.WriteString(color.Color("[reset]\n")) - - switch change.Action { - case plans.Create: - buf.WriteString(color.Color("[green] +[reset] ")) - case plans.Read: - buf.WriteString(color.Color("[cyan] <=[reset] ")) - case plans.Update: - buf.WriteString(color.Color("[yellow] ~[reset] ")) - case plans.DeleteThenCreate: - buf.WriteString(color.Color("[red]-[reset]/[green]+[reset] ")) - case plans.CreateThenDelete: - buf.WriteString(color.Color("[green]+[reset]/[red]-[reset] ")) - case plans.Delete: - buf.WriteString(color.Color("[red] -[reset] ")) - default: - buf.WriteString(color.Color("??? ")) - } - - switch addr.Resource.Resource.Mode { - case addrs.ManagedResourceMode: - buf.WriteString(fmt.Sprintf( - "resource %q %q", - addr.Resource.Resource.Type, - addr.Resource.Resource.Name, - )) - case addrs.DataResourceMode: - buf.WriteString(fmt.Sprintf( - "data %q %q ", - addr.Resource.Resource.Type, - addr.Resource.Resource.Name, - )) - default: - // should never happen, since the above is exhaustive - buf.WriteString(addr.String()) - } - - buf.WriteString(" {") - - p := blockBodyDiffPrinter{ - buf: &buf, - color: color, - action: change.Action, - requiredReplace: change.RequiredReplace, - } - - // Most commonly-used resources have nested blocks that result in us - // going at least three traversals deep while we recurse here, so we'll - // start with that much capacity and then grow as needed for deeper - // structures. - path := make(cty.Path, 0, 3) - - changeV, err := change.Decode(schema.ImpliedType()) - if err != nil { - // Should never happen in here, since we've already been through - // loads of layers of encode/decode of the planned changes before now. - panic(fmt.Sprintf("failed to decode plan for %s while rendering diff: %s", addr, err)) - } - - // We currently have an opt-out that permits the legacy SDK to return values - // that defy our usual conventions around handling of nesting blocks. To - // avoid the rendering code from needing to handle all of these, we'll - // normalize first. - // (Ideally we'd do this as part of the SDK opt-out implementation in core, - // but we've added it here for now to reduce risk of unexpected impacts - // on other code in core.) - changeV.Change.Before = objchange.NormalizeObjectFromLegacySDK(changeV.Change.Before, schema) - changeV.Change.After = objchange.NormalizeObjectFromLegacySDK(changeV.Change.After, schema) - - bodyWritten := p.writeBlockBodyDiff(schema, changeV.Before, changeV.After, 6, path) - if bodyWritten { - buf.WriteString("\n") - buf.WriteString(strings.Repeat(" ", 4)) - } - buf.WriteString("}\n") - - return buf.String() -} - -type blockBodyDiffPrinter struct { - buf *bytes.Buffer - color *colorstring.Colorize - action plans.Action - requiredReplace cty.PathSet -} - -const forcesNewResourceCaption = " [red]# forces replacement[reset]" - -// writeBlockBodyDiff writes attribute or block differences -// and returns true if any differences were found and written -func (p *blockBodyDiffPrinter) writeBlockBodyDiff(schema *configschema.Block, old, new cty.Value, indent int, path cty.Path) bool { - path = ctyEnsurePathCapacity(path, 1) - - bodyWritten := false - blankBeforeBlocks := false - { - attrNames := make([]string, 0, len(schema.Attributes)) - attrNameLen := 0 - for name := range schema.Attributes { - oldVal := ctyGetAttrMaybeNull(old, name) - newVal := ctyGetAttrMaybeNull(new, name) - if oldVal.IsNull() && newVal.IsNull() { - // Skip attributes where both old and new values are null - // (we do this early here so that we'll do our value alignment - // based on the longest attribute name that has a change, rather - // than the longest attribute name in the full set.) - continue - } - - attrNames = append(attrNames, name) - if len(name) > attrNameLen { - attrNameLen = len(name) - } - } - sort.Strings(attrNames) - if len(attrNames) > 0 { - blankBeforeBlocks = true - } - - for _, name := range attrNames { - attrS := schema.Attributes[name] - oldVal := ctyGetAttrMaybeNull(old, name) - newVal := ctyGetAttrMaybeNull(new, name) - - bodyWritten = true - p.writeAttrDiff(name, attrS, oldVal, newVal, attrNameLen, indent, path) - } - } - - { - blockTypeNames := make([]string, 0, len(schema.BlockTypes)) - for name := range schema.BlockTypes { - blockTypeNames = append(blockTypeNames, name) - } - sort.Strings(blockTypeNames) - - for _, name := range blockTypeNames { - blockS := schema.BlockTypes[name] - oldVal := ctyGetAttrMaybeNull(old, name) - newVal := ctyGetAttrMaybeNull(new, name) - - bodyWritten = true - p.writeNestedBlockDiffs(name, blockS, oldVal, newVal, blankBeforeBlocks, indent, path) - - // Always include a blank for any subsequent block types. - blankBeforeBlocks = true - } - } - - return bodyWritten -} - -func (p *blockBodyDiffPrinter) writeAttrDiff(name string, attrS *configschema.Attribute, old, new cty.Value, nameLen, indent int, path cty.Path) { - path = append(path, cty.GetAttrStep{Name: name}) - p.buf.WriteString("\n") - p.buf.WriteString(strings.Repeat(" ", indent)) - showJustNew := false - var action plans.Action - switch { - case old.IsNull(): - action = plans.Create - showJustNew = true - case new.IsNull(): - action = plans.Delete - case ctyEqualWithUnknown(old, new): - action = plans.NoOp - showJustNew = true - default: - action = plans.Update - } - - p.writeActionSymbol(action) - - p.buf.WriteString(p.color.Color("[bold]")) - p.buf.WriteString(name) - p.buf.WriteString(p.color.Color("[reset]")) - p.buf.WriteString(strings.Repeat(" ", nameLen-len(name))) - p.buf.WriteString(" = ") - - if attrS.Sensitive { - p.buf.WriteString("(sensitive value)") - } else { - switch { - case showJustNew: - p.writeValue(new, action, indent+2) - if p.pathForcesNewResource(path) { - p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) - } - default: - // We show new even if it is null to emphasize the fact - // that it is being unset, since otherwise it is easy to - // misunderstand that the value is still set to the old value. - p.writeValueDiff(old, new, indent+2, path) - } - } -} - -func (p *blockBodyDiffPrinter) writeNestedBlockDiffs(name string, blockS *configschema.NestedBlock, old, new cty.Value, blankBefore bool, indent int, path cty.Path) { - path = append(path, cty.GetAttrStep{Name: name}) - if old.IsNull() && new.IsNull() { - // Nothing to do if both old and new is null - return - } - - // Where old/new are collections representing a nesting mode other than - // NestingSingle, we assume the collection value can never be unknown - // since we always produce the container for the nested objects, even if - // the objects within are computed. - - switch blockS.Nesting { - case configschema.NestingSingle, configschema.NestingGroup: - var action plans.Action - eqV := new.Equals(old) - switch { - case old.IsNull(): - action = plans.Create - case new.IsNull(): - action = plans.Delete - case !new.IsWhollyKnown() || !old.IsWhollyKnown(): - // "old" should actually always be known due to our contract - // that old values must never be unknown, but we'll allow it - // anyway to be robust. - action = plans.Update - case !eqV.IsKnown() || !eqV.True(): - action = plans.Update - } - - if blankBefore { - p.buf.WriteRune('\n') - } - p.writeNestedBlockDiff(name, nil, &blockS.Block, action, old, new, indent, path) - case configschema.NestingList: - // For the sake of handling nested blocks, we'll treat a null list - // the same as an empty list since the config language doesn't - // distinguish these anyway. - old = ctyNullBlockListAsEmpty(old) - new = ctyNullBlockListAsEmpty(new) - - oldItems := ctyCollectionValues(old) - newItems := ctyCollectionValues(new) - - // Here we intentionally preserve the index-based correspondance - // between old and new, rather than trying to detect insertions - // and removals in the list, because this more accurately reflects - // how Terraform Core and providers will understand the change, - // particularly when the nested block contains computed attributes - // that will themselves maintain correspondance by index. - - // commonLen is number of elements that exist in both lists, which - // will be presented as updates (~). Any additional items in one - // of the lists will be presented as either creates (+) or deletes (-) - // depending on which list they belong to. - var commonLen int - switch { - case len(oldItems) < len(newItems): - commonLen = len(oldItems) - default: - commonLen = len(newItems) - } - - if blankBefore && (len(oldItems) > 0 || len(newItems) > 0) { - p.buf.WriteRune('\n') - } - - for i := 0; i < commonLen; i++ { - path := append(path, cty.IndexStep{Key: cty.NumberIntVal(int64(i))}) - oldItem := oldItems[i] - newItem := newItems[i] - action := plans.Update - if oldItem.RawEquals(newItem) { - action = plans.NoOp - } - p.writeNestedBlockDiff(name, nil, &blockS.Block, action, oldItem, newItem, indent, path) - } - for i := commonLen; i < len(oldItems); i++ { - path := append(path, cty.IndexStep{Key: cty.NumberIntVal(int64(i))}) - oldItem := oldItems[i] - newItem := cty.NullVal(oldItem.Type()) - p.writeNestedBlockDiff(name, nil, &blockS.Block, plans.Delete, oldItem, newItem, indent, path) - } - for i := commonLen; i < len(newItems); i++ { - path := append(path, cty.IndexStep{Key: cty.NumberIntVal(int64(i))}) - newItem := newItems[i] - oldItem := cty.NullVal(newItem.Type()) - p.writeNestedBlockDiff(name, nil, &blockS.Block, plans.Create, oldItem, newItem, indent, path) - } - case configschema.NestingSet: - // For the sake of handling nested blocks, we'll treat a null set - // the same as an empty set since the config language doesn't - // distinguish these anyway. - old = ctyNullBlockSetAsEmpty(old) - new = ctyNullBlockSetAsEmpty(new) - - oldItems := ctyCollectionValues(old) - newItems := ctyCollectionValues(new) - - if (len(oldItems) + len(newItems)) == 0 { - // Nothing to do if both sets are empty - return - } - - allItems := make([]cty.Value, 0, len(oldItems)+len(newItems)) - allItems = append(allItems, oldItems...) - allItems = append(allItems, newItems...) - all := cty.SetVal(allItems) - - if blankBefore { - p.buf.WriteRune('\n') - } - - for it := all.ElementIterator(); it.Next(); { - _, val := it.Element() - var action plans.Action - var oldValue, newValue cty.Value - switch { - case !val.IsKnown(): - action = plans.Update - newValue = val - case !old.HasElement(val).True(): - action = plans.Create - oldValue = cty.NullVal(val.Type()) - newValue = val - case !new.HasElement(val).True(): - action = plans.Delete - oldValue = val - newValue = cty.NullVal(val.Type()) - default: - action = plans.NoOp - oldValue = val - newValue = val - } - path := append(path, cty.IndexStep{Key: val}) - p.writeNestedBlockDiff(name, nil, &blockS.Block, action, oldValue, newValue, indent, path) - } - - case configschema.NestingMap: - // For the sake of handling nested blocks, we'll treat a null map - // the same as an empty map since the config language doesn't - // distinguish these anyway. - old = ctyNullBlockMapAsEmpty(old) - new = ctyNullBlockMapAsEmpty(new) - - oldItems := old.AsValueMap() - newItems := new.AsValueMap() - if (len(oldItems) + len(newItems)) == 0 { - // Nothing to do if both maps are empty - return - } - - allKeys := make(map[string]bool) - for k := range oldItems { - allKeys[k] = true - } - for k := range newItems { - allKeys[k] = true - } - allKeysOrder := make([]string, 0, len(allKeys)) - for k := range allKeys { - allKeysOrder = append(allKeysOrder, k) - } - sort.Strings(allKeysOrder) - - if blankBefore { - p.buf.WriteRune('\n') - } - - for _, k := range allKeysOrder { - var action plans.Action - oldValue := oldItems[k] - newValue := newItems[k] - switch { - case oldValue == cty.NilVal: - oldValue = cty.NullVal(newValue.Type()) - action = plans.Create - case newValue == cty.NilVal: - newValue = cty.NullVal(oldValue.Type()) - action = plans.Delete - case !newValue.RawEquals(oldValue): - action = plans.Update - default: - action = plans.NoOp - } - - path := append(path, cty.IndexStep{Key: cty.StringVal(k)}) - p.writeNestedBlockDiff(name, &k, &blockS.Block, action, oldValue, newValue, indent, path) - } - } -} - -func (p *blockBodyDiffPrinter) writeNestedBlockDiff(name string, label *string, blockS *configschema.Block, action plans.Action, old, new cty.Value, indent int, path cty.Path) { - p.buf.WriteString("\n") - p.buf.WriteString(strings.Repeat(" ", indent)) - p.writeActionSymbol(action) - - if label != nil { - fmt.Fprintf(p.buf, "%s %q {", name, *label) - } else { - fmt.Fprintf(p.buf, "%s {", name) - } - - if action != plans.NoOp && (p.pathForcesNewResource(path) || p.pathForcesNewResource(path[:len(path)-1])) { - p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) - } - - bodyWritten := p.writeBlockBodyDiff(blockS, old, new, indent+4, path) - if bodyWritten { - p.buf.WriteString("\n") - p.buf.WriteString(strings.Repeat(" ", indent+2)) - } - p.buf.WriteString("}") -} - -func (p *blockBodyDiffPrinter) writeValue(val cty.Value, action plans.Action, indent int) { - if !val.IsKnown() { - p.buf.WriteString("(known after apply)") - return - } - if val.IsNull() { - p.buf.WriteString(p.color.Color("[dark_gray]null[reset]")) - return - } - - ty := val.Type() - - switch { - case ty.IsPrimitiveType(): - switch ty { - case cty.String: - { - // Special behavior for JSON strings containing array or object - src := []byte(val.AsString()) - ty, err := ctyjson.ImpliedType(src) - // check for the special case of "null", which decodes to nil, - // and just allow it to be printed out directly - if err == nil && !ty.IsPrimitiveType() && val.AsString() != "null" { - jv, err := ctyjson.Unmarshal(src, ty) - if err == nil { - p.buf.WriteString("jsonencode(") - if jv.LengthInt() == 0 { - p.writeValue(jv, action, 0) - } else { - p.buf.WriteByte('\n') - p.buf.WriteString(strings.Repeat(" ", indent+4)) - p.writeValue(jv, action, indent+4) - p.buf.WriteByte('\n') - p.buf.WriteString(strings.Repeat(" ", indent)) - } - p.buf.WriteByte(')') - break // don't *also* do the normal behavior below - } - } - } - fmt.Fprintf(p.buf, "%q", val.AsString()) - case cty.Bool: - if val.True() { - p.buf.WriteString("true") - } else { - p.buf.WriteString("false") - } - case cty.Number: - bf := val.AsBigFloat() - p.buf.WriteString(bf.Text('f', -1)) - default: - // should never happen, since the above is exhaustive - fmt.Fprintf(p.buf, "%#v", val) - } - case ty.IsListType() || ty.IsSetType() || ty.IsTupleType(): - p.buf.WriteString("[") - - it := val.ElementIterator() - for it.Next() { - _, val := it.Element() - - p.buf.WriteString("\n") - p.buf.WriteString(strings.Repeat(" ", indent+2)) - p.writeActionSymbol(action) - p.writeValue(val, action, indent+4) - p.buf.WriteString(",") - } - - if val.LengthInt() > 0 { - p.buf.WriteString("\n") - p.buf.WriteString(strings.Repeat(" ", indent)) - } - p.buf.WriteString("]") - case ty.IsMapType(): - p.buf.WriteString("{") - - keyLen := 0 - for it := val.ElementIterator(); it.Next(); { - key, _ := it.Element() - if keyStr := key.AsString(); len(keyStr) > keyLen { - keyLen = len(keyStr) - } - } - - for it := val.ElementIterator(); it.Next(); { - key, val := it.Element() - - p.buf.WriteString("\n") - p.buf.WriteString(strings.Repeat(" ", indent+2)) - p.writeActionSymbol(action) - p.writeValue(key, action, indent+4) - p.buf.WriteString(strings.Repeat(" ", keyLen-len(key.AsString()))) - p.buf.WriteString(" = ") - p.writeValue(val, action, indent+4) - } - - if val.LengthInt() > 0 { - p.buf.WriteString("\n") - p.buf.WriteString(strings.Repeat(" ", indent)) - } - p.buf.WriteString("}") - case ty.IsObjectType(): - p.buf.WriteString("{") - - atys := ty.AttributeTypes() - attrNames := make([]string, 0, len(atys)) - nameLen := 0 - for attrName := range atys { - attrNames = append(attrNames, attrName) - if len(attrName) > nameLen { - nameLen = len(attrName) - } - } - sort.Strings(attrNames) - - for _, attrName := range attrNames { - val := val.GetAttr(attrName) - - p.buf.WriteString("\n") - p.buf.WriteString(strings.Repeat(" ", indent+2)) - p.writeActionSymbol(action) - p.buf.WriteString(attrName) - p.buf.WriteString(strings.Repeat(" ", nameLen-len(attrName))) - p.buf.WriteString(" = ") - p.writeValue(val, action, indent+4) - } - - if len(attrNames) > 0 { - p.buf.WriteString("\n") - p.buf.WriteString(strings.Repeat(" ", indent)) - } - p.buf.WriteString("}") - } -} - -func (p *blockBodyDiffPrinter) writeValueDiff(old, new cty.Value, indent int, path cty.Path) { - ty := old.Type() - typesEqual := ctyTypesEqual(ty, new.Type()) - - // We have some specialized diff implementations for certain complex - // values where it's useful to see a visualization of the diff of - // the nested elements rather than just showing the entire old and - // new values verbatim. - // However, these specialized implementations can apply only if both - // values are known and non-null. - if old.IsKnown() && new.IsKnown() && !old.IsNull() && !new.IsNull() && typesEqual { - switch { - case ty == cty.String: - // We have special behavior for both multi-line strings in general - // and for strings that can parse as JSON. For the JSON handling - // to apply, both old and new must be valid JSON. - // For single-line strings that don't parse as JSON we just fall - // out of this switch block and do the default old -> new rendering. - oldS := old.AsString() - newS := new.AsString() - - { - // Special behavior for JSON strings containing object or - // list values. - oldBytes := []byte(oldS) - newBytes := []byte(newS) - oldType, oldErr := ctyjson.ImpliedType(oldBytes) - newType, newErr := ctyjson.ImpliedType(newBytes) - if oldErr == nil && newErr == nil && !(oldType.IsPrimitiveType() && newType.IsPrimitiveType()) { - oldJV, oldErr := ctyjson.Unmarshal(oldBytes, oldType) - newJV, newErr := ctyjson.Unmarshal(newBytes, newType) - if oldErr == nil && newErr == nil { - if !oldJV.RawEquals(newJV) { // two JSON values may differ only in insignificant whitespace - p.buf.WriteString("jsonencode(") - p.buf.WriteByte('\n') - p.buf.WriteString(strings.Repeat(" ", indent+2)) - p.writeActionSymbol(plans.Update) - p.writeValueDiff(oldJV, newJV, indent+4, path) - p.buf.WriteByte('\n') - p.buf.WriteString(strings.Repeat(" ", indent)) - p.buf.WriteByte(')') - } else { - // if they differ only in insigificant whitespace - // then we'll note that but still expand out the - // effective value. - if p.pathForcesNewResource(path) { - p.buf.WriteString(p.color.Color("jsonencode( [red]# whitespace changes force replacement[reset]")) - } else { - p.buf.WriteString(p.color.Color("jsonencode( [dim]# whitespace changes[reset]")) - } - p.buf.WriteByte('\n') - p.buf.WriteString(strings.Repeat(" ", indent+4)) - p.writeValue(oldJV, plans.NoOp, indent+4) - p.buf.WriteByte('\n') - p.buf.WriteString(strings.Repeat(" ", indent)) - p.buf.WriteByte(')') - } - return - } - } - } - - if strings.Index(oldS, "\n") < 0 && strings.Index(newS, "\n") < 0 { - break - } - - p.buf.WriteString("<<~EOT") - if p.pathForcesNewResource(path) { - p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) - } - p.buf.WriteString("\n") - - var oldLines, newLines []cty.Value - { - r := strings.NewReader(oldS) - sc := bufio.NewScanner(r) - for sc.Scan() { - oldLines = append(oldLines, cty.StringVal(sc.Text())) - } - } - { - r := strings.NewReader(newS) - sc := bufio.NewScanner(r) - for sc.Scan() { - newLines = append(newLines, cty.StringVal(sc.Text())) - } - } - - diffLines := ctySequenceDiff(oldLines, newLines) - for _, diffLine := range diffLines { - p.buf.WriteString(strings.Repeat(" ", indent+2)) - p.writeActionSymbol(diffLine.Action) - - switch diffLine.Action { - case plans.NoOp, plans.Delete: - p.buf.WriteString(diffLine.Before.AsString()) - case plans.Create: - p.buf.WriteString(diffLine.After.AsString()) - default: - // Should never happen since the above covers all - // actions that ctySequenceDiff can return for strings - p.buf.WriteString(diffLine.After.AsString()) - - } - p.buf.WriteString("\n") - } - - p.buf.WriteString(strings.Repeat(" ", indent)) // +4 here because there's no symbol - p.buf.WriteString("EOT") - - return - - case ty.IsSetType(): - p.buf.WriteString("[") - if p.pathForcesNewResource(path) { - p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) - } - p.buf.WriteString("\n") - - var addedVals, removedVals, allVals []cty.Value - for it := old.ElementIterator(); it.Next(); { - _, val := it.Element() - allVals = append(allVals, val) - if new.HasElement(val).False() { - removedVals = append(removedVals, val) - } - } - for it := new.ElementIterator(); it.Next(); { - _, val := it.Element() - allVals = append(allVals, val) - if val.IsKnown() && old.HasElement(val).False() { - addedVals = append(addedVals, val) - } - } - - var all, added, removed cty.Value - if len(allVals) > 0 { - all = cty.SetVal(allVals) - } else { - all = cty.SetValEmpty(ty.ElementType()) - } - if len(addedVals) > 0 { - added = cty.SetVal(addedVals) - } else { - added = cty.SetValEmpty(ty.ElementType()) - } - if len(removedVals) > 0 { - removed = cty.SetVal(removedVals) - } else { - removed = cty.SetValEmpty(ty.ElementType()) - } - - for it := all.ElementIterator(); it.Next(); { - _, val := it.Element() - - p.buf.WriteString(strings.Repeat(" ", indent+2)) - - var action plans.Action - switch { - case !val.IsKnown(): - action = plans.Update - case added.HasElement(val).True(): - action = plans.Create - case removed.HasElement(val).True(): - action = plans.Delete - default: - action = plans.NoOp - } - - p.writeActionSymbol(action) - p.writeValue(val, action, indent+4) - p.buf.WriteString(",\n") - } - - p.buf.WriteString(strings.Repeat(" ", indent)) - p.buf.WriteString("]") - return - case ty.IsListType() || ty.IsTupleType(): - p.buf.WriteString("[") - if p.pathForcesNewResource(path) { - p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) - } - p.buf.WriteString("\n") - - elemDiffs := ctySequenceDiff(old.AsValueSlice(), new.AsValueSlice()) - for _, elemDiff := range elemDiffs { - p.buf.WriteString(strings.Repeat(" ", indent+2)) - p.writeActionSymbol(elemDiff.Action) - switch elemDiff.Action { - case plans.NoOp, plans.Delete: - p.writeValue(elemDiff.Before, elemDiff.Action, indent+4) - case plans.Update: - p.writeValueDiff(elemDiff.Before, elemDiff.After, indent+4, path) - case plans.Create: - p.writeValue(elemDiff.After, elemDiff.Action, indent+4) - default: - // Should never happen since the above covers all - // actions that ctySequenceDiff can return. - p.writeValue(elemDiff.After, elemDiff.Action, indent+4) - } - - p.buf.WriteString(",\n") - } - - p.buf.WriteString(strings.Repeat(" ", indent)) - p.buf.WriteString("]") - return - - case ty.IsMapType(): - p.buf.WriteString("{") - if p.pathForcesNewResource(path) { - p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) - } - p.buf.WriteString("\n") - - var allKeys []string - keyLen := 0 - for it := old.ElementIterator(); it.Next(); { - k, _ := it.Element() - keyStr := k.AsString() - allKeys = append(allKeys, keyStr) - if len(keyStr) > keyLen { - keyLen = len(keyStr) - } - } - for it := new.ElementIterator(); it.Next(); { - k, _ := it.Element() - keyStr := k.AsString() - allKeys = append(allKeys, keyStr) - if len(keyStr) > keyLen { - keyLen = len(keyStr) - } - } - - sort.Strings(allKeys) - - lastK := "" - for i, k := range allKeys { - if i > 0 && lastK == k { - continue // skip duplicates (list is sorted) - } - lastK = k - - p.buf.WriteString(strings.Repeat(" ", indent+2)) - kV := cty.StringVal(k) - var action plans.Action - if old.HasIndex(kV).False() { - action = plans.Create - } else if new.HasIndex(kV).False() { - action = plans.Delete - } else if eqV := old.Index(kV).Equals(new.Index(kV)); eqV.IsKnown() && eqV.True() { - action = plans.NoOp - } else { - action = plans.Update - } - - path := append(path, cty.IndexStep{Key: kV}) - - p.writeActionSymbol(action) - p.writeValue(kV, action, indent+4) - p.buf.WriteString(strings.Repeat(" ", keyLen-len(k))) - p.buf.WriteString(" = ") - switch action { - case plans.Create, plans.NoOp: - v := new.Index(kV) - p.writeValue(v, action, indent+4) - case plans.Delete: - oldV := old.Index(kV) - newV := cty.NullVal(oldV.Type()) - p.writeValueDiff(oldV, newV, indent+4, path) - default: - oldV := old.Index(kV) - newV := new.Index(kV) - p.writeValueDiff(oldV, newV, indent+4, path) - } - - p.buf.WriteByte('\n') - } - - p.buf.WriteString(strings.Repeat(" ", indent)) - p.buf.WriteString("}") - return - case ty.IsObjectType(): - p.buf.WriteString("{") - p.buf.WriteString("\n") - - forcesNewResource := p.pathForcesNewResource(path) - - var allKeys []string - keyLen := 0 - for it := old.ElementIterator(); it.Next(); { - k, _ := it.Element() - keyStr := k.AsString() - allKeys = append(allKeys, keyStr) - if len(keyStr) > keyLen { - keyLen = len(keyStr) - } - } - for it := new.ElementIterator(); it.Next(); { - k, _ := it.Element() - keyStr := k.AsString() - allKeys = append(allKeys, keyStr) - if len(keyStr) > keyLen { - keyLen = len(keyStr) - } - } - - sort.Strings(allKeys) - - lastK := "" - for i, k := range allKeys { - if i > 0 && lastK == k { - continue // skip duplicates (list is sorted) - } - lastK = k - - p.buf.WriteString(strings.Repeat(" ", indent+2)) - kV := k - var action plans.Action - if !old.Type().HasAttribute(kV) { - action = plans.Create - } else if !new.Type().HasAttribute(kV) { - action = plans.Delete - } else if eqV := old.GetAttr(kV).Equals(new.GetAttr(kV)); eqV.IsKnown() && eqV.True() { - action = plans.NoOp - } else { - action = plans.Update - } - - path := append(path, cty.GetAttrStep{Name: kV}) - - p.writeActionSymbol(action) - p.buf.WriteString(k) - p.buf.WriteString(strings.Repeat(" ", keyLen-len(k))) - p.buf.WriteString(" = ") - - switch action { - case plans.Create, plans.NoOp: - v := new.GetAttr(kV) - p.writeValue(v, action, indent+4) - case plans.Delete: - oldV := old.GetAttr(kV) - newV := cty.NullVal(oldV.Type()) - p.writeValueDiff(oldV, newV, indent+4, path) - default: - oldV := old.GetAttr(kV) - newV := new.GetAttr(kV) - p.writeValueDiff(oldV, newV, indent+4, path) - } - - p.buf.WriteString("\n") - } - - p.buf.WriteString(strings.Repeat(" ", indent)) - p.buf.WriteString("}") - - if forcesNewResource { - p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) - } - return - } - } - - // In all other cases, we just show the new and old values as-is - p.writeValue(old, plans.Delete, indent) - if new.IsNull() { - p.buf.WriteString(p.color.Color(" [dark_gray]->[reset] ")) - } else { - p.buf.WriteString(p.color.Color(" [yellow]->[reset] ")) - } - - p.writeValue(new, plans.Create, indent) - if p.pathForcesNewResource(path) { - p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) - } -} - -// writeActionSymbol writes a symbol to represent the given action, followed -// by a space. -// -// It only supports the actions that can be represented with a single character: -// Create, Delete, Update and NoAction. -func (p *blockBodyDiffPrinter) writeActionSymbol(action plans.Action) { - switch action { - case plans.Create: - p.buf.WriteString(p.color.Color("[green]+[reset] ")) - case plans.Delete: - p.buf.WriteString(p.color.Color("[red]-[reset] ")) - case plans.Update: - p.buf.WriteString(p.color.Color("[yellow]~[reset] ")) - case plans.NoOp: - p.buf.WriteString(" ") - default: - // Should never happen - p.buf.WriteString(p.color.Color("? ")) - } -} - -func (p *blockBodyDiffPrinter) pathForcesNewResource(path cty.Path) bool { - if !p.action.IsReplace() { - // "requiredReplace" only applies when the instance is being replaced - return false - } - return p.requiredReplace.Has(path) -} - -func ctyEmptyString(value cty.Value) bool { - if !value.IsNull() && value.IsKnown() { - valueType := value.Type() - if valueType == cty.String && value.AsString() == "" { - return true - } - } - return false -} - -func ctyGetAttrMaybeNull(val cty.Value, name string) cty.Value { - attrType := val.Type().AttributeType(name) - - if val.IsNull() { - return cty.NullVal(attrType) - } - - // We treat "" as null here - // as existing SDK doesn't support null yet. - // This allows us to avoid spurious diffs - // until we introduce null to the SDK. - attrValue := val.GetAttr(name) - if ctyEmptyString(attrValue) { - return cty.NullVal(attrType) - } - - return attrValue -} - -func ctyCollectionValues(val cty.Value) []cty.Value { - if !val.IsKnown() || val.IsNull() { - return nil - } - - ret := make([]cty.Value, 0, val.LengthInt()) - for it := val.ElementIterator(); it.Next(); { - _, value := it.Element() - ret = append(ret, value) - } - return ret -} - -// ctySequenceDiff returns differences between given sequences of cty.Value(s) -// in the form of Create, Delete, or Update actions (for objects). -func ctySequenceDiff(old, new []cty.Value) []*plans.Change { - var ret []*plans.Change - lcs := objchange.LongestCommonSubsequence(old, new) - var oldI, newI, lcsI int - for oldI < len(old) || newI < len(new) || lcsI < len(lcs) { - for oldI < len(old) && (lcsI >= len(lcs) || !old[oldI].RawEquals(lcs[lcsI])) { - isObjectDiff := old[oldI].Type().IsObjectType() && (newI >= len(new) || new[newI].Type().IsObjectType()) - if isObjectDiff && newI < len(new) { - ret = append(ret, &plans.Change{ - Action: plans.Update, - Before: old[oldI], - After: new[newI], - }) - oldI++ - newI++ // we also consume the next "new" in this case - continue - } - - ret = append(ret, &plans.Change{ - Action: plans.Delete, - Before: old[oldI], - After: cty.NullVal(old[oldI].Type()), - }) - oldI++ - } - for newI < len(new) && (lcsI >= len(lcs) || !new[newI].RawEquals(lcs[lcsI])) { - ret = append(ret, &plans.Change{ - Action: plans.Create, - Before: cty.NullVal(new[newI].Type()), - After: new[newI], - }) - newI++ - } - if lcsI < len(lcs) { - ret = append(ret, &plans.Change{ - Action: plans.NoOp, - Before: lcs[lcsI], - After: lcs[lcsI], - }) - - // All of our indexes advance together now, since the line - // is common to all three sequences. - lcsI++ - oldI++ - newI++ - } - } - return ret -} - -func ctyEqualWithUnknown(old, new cty.Value) bool { - if !old.IsWhollyKnown() || !new.IsWhollyKnown() { - return false - } - return old.Equals(new).True() -} - -// ctyTypesEqual checks equality of two types more loosely -// by avoiding checks of object/tuple elements -// as we render differences on element-by-element basis anyway -func ctyTypesEqual(oldT, newT cty.Type) bool { - if oldT.IsObjectType() && newT.IsObjectType() { - return true - } - if oldT.IsTupleType() && newT.IsTupleType() { - return true - } - return oldT.Equals(newT) -} - -func ctyEnsurePathCapacity(path cty.Path, minExtra int) cty.Path { - if cap(path)-len(path) >= minExtra { - return path - } - newCap := cap(path) * 2 - if newCap < (len(path) + minExtra) { - newCap = len(path) + minExtra - } - newPath := make(cty.Path, len(path), newCap) - copy(newPath, path) - return newPath -} - -// ctyNullBlockListAsEmpty either returns the given value verbatim if it is non-nil -// or returns an empty value of a suitable type to serve as a placeholder for it. -// -// In particular, this function handles the special situation where a "list" is -// actually represented as a tuple type where nested blocks contain -// dynamically-typed values. -func ctyNullBlockListAsEmpty(in cty.Value) cty.Value { - if !in.IsNull() { - return in - } - if ty := in.Type(); ty.IsListType() { - return cty.ListValEmpty(ty.ElementType()) - } - return cty.EmptyTupleVal // must need a tuple, then -} - -// ctyNullBlockMapAsEmpty either returns the given value verbatim if it is non-nil -// or returns an empty value of a suitable type to serve as a placeholder for it. -// -// In particular, this function handles the special situation where a "map" is -// actually represented as an object type where nested blocks contain -// dynamically-typed values. -func ctyNullBlockMapAsEmpty(in cty.Value) cty.Value { - if !in.IsNull() { - return in - } - if ty := in.Type(); ty.IsMapType() { - return cty.MapValEmpty(ty.ElementType()) - } - return cty.EmptyObjectVal // must need an object, then -} - -// ctyNullBlockSetAsEmpty either returns the given value verbatim if it is non-nil -// or returns an empty value of a suitable type to serve as a placeholder for it. -func ctyNullBlockSetAsEmpty(in cty.Value) cty.Value { - if !in.IsNull() { - return in - } - // Dynamically-typed attributes are not supported inside blocks backed by - // sets, so our result here is always a set. - return cty.SetValEmpty(in.Type().ElementType()) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/command/format/format.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/command/format/format.go deleted file mode 100644 index aa8d7deb..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/command/format/format.go +++ /dev/null @@ -1,8 +0,0 @@ -// Package format contains helpers for formatting various Terraform -// structures for human-readabout output. -// -// This package is used by the official Terraform CLI in formatting any -// output and is exported to encourage non-official frontends to mimic the -// output formatting as much as possible so that text formats of Terraform -// structures have a consistent look and feel. -package format diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/command/format/object_id.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/command/format/object_id.go deleted file mode 100644 index 85ebbfec..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/command/format/object_id.go +++ /dev/null @@ -1,123 +0,0 @@ -package format - -import ( - "github.com/zclconf/go-cty/cty" -) - -// ObjectValueID takes a value that is assumed to be an object representation -// of some resource instance object and attempts to heuristically find an -// attribute of it that is likely to be a unique identifier in the remote -// system that it belongs to which will be useful to the user. -// -// If such an attribute is found, its name and string value intended for -// display are returned. Both returned strings are empty if no such attribute -// exists, in which case the caller should assume that the resource instance -// address within the Terraform configuration is the best available identifier. -// -// This is only a best-effort sort of thing, relying on naming conventions in -// our resource type schemas. The result is not guaranteed to be unique, but -// should generally be suitable for display to an end-user anyway. -// -// This function will panic if the given value is not of an object type. -func ObjectValueID(obj cty.Value) (k, v string) { - if obj.IsNull() || !obj.IsKnown() { - return "", "" - } - - atys := obj.Type().AttributeTypes() - - switch { - - case atys["id"] == cty.String: - v := obj.GetAttr("id") - if v.IsKnown() && !v.IsNull() { - return "id", v.AsString() - } - - case atys["name"] == cty.String: - // "name" isn't always globally unique, but if there isn't also an - // "id" then it _often_ is, in practice. - v := obj.GetAttr("name") - if v.IsKnown() && !v.IsNull() { - return "name", v.AsString() - } - } - - return "", "" -} - -// ObjectValueName takes a value that is assumed to be an object representation -// of some resource instance object and attempts to heuristically find an -// attribute of it that is likely to be a human-friendly name in the remote -// system that it belongs to which will be useful to the user. -// -// If such an attribute is found, its name and string value intended for -// display are returned. Both returned strings are empty if no such attribute -// exists, in which case the caller should assume that the resource instance -// address within the Terraform configuration is the best available identifier. -// -// This is only a best-effort sort of thing, relying on naming conventions in -// our resource type schemas. The result is not guaranteed to be unique, but -// should generally be suitable for display to an end-user anyway. -// -// Callers that use both ObjectValueName and ObjectValueID at the same time -// should be prepared to get the same attribute key and value from both in -// some cases, since there is overlap betweek the id-extraction and -// name-extraction heuristics. -// -// This function will panic if the given value is not of an object type. -func ObjectValueName(obj cty.Value) (k, v string) { - if obj.IsNull() || !obj.IsKnown() { - return "", "" - } - - atys := obj.Type().AttributeTypes() - - switch { - - case atys["name"] == cty.String: - v := obj.GetAttr("name") - if v.IsKnown() && !v.IsNull() { - return "name", v.AsString() - } - - case atys["tags"].IsMapType() && atys["tags"].ElementType() == cty.String: - tags := obj.GetAttr("tags") - if tags.IsNull() || !tags.IsWhollyKnown() { - break - } - - switch { - case tags.HasIndex(cty.StringVal("name")).RawEquals(cty.True): - v := tags.Index(cty.StringVal("name")) - if v.IsKnown() && !v.IsNull() { - return "tags.name", v.AsString() - } - case tags.HasIndex(cty.StringVal("Name")).RawEquals(cty.True): - // AWS-style naming convention - v := tags.Index(cty.StringVal("Name")) - if v.IsKnown() && !v.IsNull() { - return "tags.Name", v.AsString() - } - } - } - - return "", "" -} - -// ObjectValueIDOrName is a convenience wrapper around both ObjectValueID -// and ObjectValueName (in that preference order) to try to extract some sort -// of human-friendly descriptive string value for an object as additional -// context about an object when it is being displayed in a compact way (where -// not all of the attributes are visible.) -// -// Just as with the two functions it wraps, it is a best-effort and may return -// two empty strings if no suitable attribute can be found for a given object. -func ObjectValueIDOrName(obj cty.Value) (k, v string) { - k, v = ObjectValueID(obj) - if k != "" { - return - } - k, v = ObjectValueName(obj) - return -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/command/format/state.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/command/format/state.go deleted file mode 100644 index 14869ad3..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/command/format/state.go +++ /dev/null @@ -1,208 +0,0 @@ -package format - -import ( - "bytes" - "fmt" - "sort" - "strings" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/terraform" - "github.com/mitchellh/colorstring" - "github.com/zclconf/go-cty/cty" -) - -// StateOpts are the options for formatting a state. -type StateOpts struct { - // State is the state to format. This is required. - State *states.State - - // Schemas are used to decode attributes. This is required. - Schemas *terraform.Schemas - - // Color is the colorizer. This is optional. - Color *colorstring.Colorize -} - -// State takes a state and returns a string -func State(opts *StateOpts) string { - if opts.Color == nil { - panic("colorize not given") - } - - if opts.Schemas == nil { - panic("schemas not given") - } - - s := opts.State - if len(s.Modules) == 0 { - return "The state file is empty. No resources are represented." - } - - buf := bytes.NewBufferString("[reset]") - p := blockBodyDiffPrinter{ - buf: buf, - color: opts.Color, - action: plans.NoOp, - } - - // Format all the modules - for _, m := range s.Modules { - formatStateModule(p, m, opts.Schemas) - } - - // Write the outputs for the root module - m := s.RootModule() - - if m.OutputValues != nil { - if len(m.OutputValues) > 0 { - p.buf.WriteString("Outputs:\n\n") - } - - // Sort the outputs - ks := make([]string, 0, len(m.OutputValues)) - for k := range m.OutputValues { - ks = append(ks, k) - } - sort.Strings(ks) - - // Output each output k/v pair - for _, k := range ks { - v := m.OutputValues[k] - p.buf.WriteString(fmt.Sprintf("%s = ", k)) - p.writeValue(v.Value, plans.NoOp, 0) - p.buf.WriteString("\n") - } - } - - trimmedOutput := strings.TrimSpace(p.buf.String()) - trimmedOutput += "[reset]" - - return opts.Color.Color(trimmedOutput) - -} - -func formatStateModule(p blockBodyDiffPrinter, m *states.Module, schemas *terraform.Schemas) { - // First get the names of all the resources so we can show them - // in alphabetical order. - names := make([]string, 0, len(m.Resources)) - for name := range m.Resources { - names = append(names, name) - } - sort.Strings(names) - - // Go through each resource and begin building up the output. - for _, key := range names { - for k, v := range m.Resources[key].Instances { - // keep these in order to keep the current object first, and - // provide deterministic output for the deposed objects - type obj struct { - header string - instance *states.ResourceInstanceObjectSrc - } - instances := []obj{} - - addr := m.Resources[key].Addr - - taintStr := "" - if v.Current != nil && v.Current.Status == 'T' { - taintStr = " (tainted)" - } - - instances = append(instances, - obj{fmt.Sprintf("# %s:%s\n", addr.Absolute(m.Addr).Instance(k), taintStr), v.Current}) - - for dk, v := range v.Deposed { - instances = append(instances, - obj{fmt.Sprintf("# %s: (deposed object %s)\n", addr.Absolute(m.Addr).Instance(k), dk), v}) - } - - // Sort the instances for consistent output. - // Starting the sort from the second index, so the current instance - // is always first. - sort.Slice(instances[1:], func(i, j int) bool { - return instances[i+1].header < instances[j+1].header - }) - - for _, obj := range instances { - header := obj.header - instance := obj.instance - p.buf.WriteString(header) - if instance == nil { - // this shouldn't happen, but there's nothing to do here so - // don't panic below. - continue - } - - var schema *configschema.Block - provider := m.Resources[key].ProviderConfig.ProviderConfig.StringCompact() - if _, exists := schemas.Providers[provider]; !exists { - // This should never happen in normal use because we should've - // loaded all of the schemas and checked things prior to this - // point. We can't return errors here, but since this is UI code - // we will try to do _something_ reasonable. - p.buf.WriteString(fmt.Sprintf("# missing schema for provider %q\n\n", provider)) - continue - } - - switch addr.Mode { - case addrs.ManagedResourceMode: - schema, _ = schemas.ResourceTypeConfig( - provider, - addr.Mode, - addr.Type, - ) - if schema == nil { - p.buf.WriteString(fmt.Sprintf( - "# missing schema for provider %q resource type %s\n\n", provider, addr.Type)) - continue - } - - p.buf.WriteString(fmt.Sprintf( - "resource %q %q {", - addr.Type, - addr.Name, - )) - case addrs.DataResourceMode: - schema, _ = schemas.ResourceTypeConfig( - provider, - addr.Mode, - addr.Type, - ) - if schema == nil { - p.buf.WriteString(fmt.Sprintf( - "# missing schema for provider %q data source %s\n\n", provider, addr.Type)) - continue - } - - p.buf.WriteString(fmt.Sprintf( - "data %q %q {", - addr.Type, - addr.Name, - )) - default: - // should never happen, since the above is exhaustive - p.buf.WriteString(addr.String()) - } - - val, err := instance.Decode(schema.ImpliedType()) - if err != nil { - fmt.Println(err.Error()) - break - } - - path := make(cty.Path, 0, 3) - bodyWritten := p.writeBlockBodyDiff(schema, val.Value, val.Value, 2, path) - if bodyWritten { - p.buf.WriteString("\n") - } - - p.buf.WriteString("}\n\n") - } - } - } - p.buf.WriteString("\n") -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/backend.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/backend.go deleted file mode 100644 index 76d161d7..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/backend.go +++ /dev/null @@ -1,24 +0,0 @@ -package configs - -import ( - "github.com/hashicorp/hcl/v2" -) - -// Backend represents a "backend" block inside a "terraform" block in a module -// or file. -type Backend struct { - Type string - Config hcl.Body - - TypeRange hcl.Range - DeclRange hcl.Range -} - -func decodeBackendBlock(block *hcl.Block) (*Backend, hcl.Diagnostics) { - return &Backend{ - Type: block.Labels[0], - TypeRange: block.LabelRanges[0], - Config: block.Body, - DeclRange: block.DefRange, - }, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/compat_shim.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/compat_shim.go deleted file mode 100644 index e594ebd4..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/compat_shim.go +++ /dev/null @@ -1,116 +0,0 @@ -package configs - -import ( - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" - "github.com/zclconf/go-cty/cty" -) - -// ------------------------------------------------------------------------- -// Functions in this file are compatibility shims intended to ease conversion -// from the old configuration loader. Any use of these functions that makes -// a change should generate a deprecation warning explaining to the user how -// to update their code for new patterns. -// -// Shims are particularly important for any patterns that have been widely -// documented in books, tutorials, etc. Users will still be starting from -// these examples and we want to help them adopt the latest patterns rather -// than leave them stranded. -// ------------------------------------------------------------------------- - -// shimTraversalInString takes any arbitrary expression and checks if it is -// a quoted string in the native syntax. If it _is_, then it is parsed as a -// traversal and re-wrapped into a synthetic traversal expression and a -// warning is generated. Otherwise, the given expression is just returned -// verbatim. -// -// This function has no effect on expressions from the JSON syntax, since -// traversals in strings are the required pattern in that syntax. -// -// If wantKeyword is set, the generated warning diagnostic will talk about -// keywords rather than references. The behavior is otherwise unchanged, and -// the caller remains responsible for checking that the result is indeed -// a keyword, e.g. using hcl.ExprAsKeyword. -func shimTraversalInString(expr hcl.Expression, wantKeyword bool) (hcl.Expression, hcl.Diagnostics) { - // ObjectConsKeyExpr is a special wrapper type used for keys on object - // constructors to deal with the fact that naked identifiers are normally - // handled as "bareword" strings rather than as variable references. Since - // we know we're interpreting as a traversal anyway (and thus it won't - // matter whether it's a string or an identifier) we can safely just unwrap - // here and then process whatever we find inside as normal. - if ocke, ok := expr.(*hclsyntax.ObjectConsKeyExpr); ok { - expr = ocke.Wrapped - } - - if !exprIsNativeQuotedString(expr) { - return expr, nil - } - - strVal, diags := expr.Value(nil) - if diags.HasErrors() || strVal.IsNull() || !strVal.IsKnown() { - // Since we're not even able to attempt a shim here, we'll discard - // the diagnostics we saw so far and let the caller's own error - // handling take care of reporting the invalid expression. - return expr, nil - } - - // The position handling here isn't _quite_ right because it won't - // take into account any escape sequences in the literal string, but - // it should be close enough for any error reporting to make sense. - srcRange := expr.Range() - startPos := srcRange.Start // copy - startPos.Column++ // skip initial quote - startPos.Byte++ // skip initial quote - - traversal, tDiags := hclsyntax.ParseTraversalAbs( - []byte(strVal.AsString()), - srcRange.Filename, - startPos, - ) - diags = append(diags, tDiags...) - - // For initial release our deprecation warnings are disabled to allow - // a period where modules can be compatible with both old and new - // conventions. - // FIXME: Re-enable these deprecation warnings in a release prior to - // Terraform 0.13 and then remove the shims altogether for 0.13. - /* - if wantKeyword { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagWarning, - Summary: "Quoted keywords are deprecated", - Detail: "In this context, keywords are expected literally rather than in quotes. Previous versions of Terraform required quotes, but that usage is now deprecated. Remove the quotes surrounding this keyword to silence this warning.", - Subject: &srcRange, - }) - } else { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagWarning, - Summary: "Quoted references are deprecated", - Detail: "In this context, references are expected literally rather than in quotes. Previous versions of Terraform required quotes, but that usage is now deprecated. Remove the quotes surrounding this reference to silence this warning.", - Subject: &srcRange, - }) - } - */ - - return &hclsyntax.ScopeTraversalExpr{ - Traversal: traversal, - SrcRange: srcRange, - }, diags -} - -// shimIsIgnoreChangesStar returns true if the given expression seems to be -// a string literal whose value is "*". This is used to support a legacy -// form of ignore_changes = all . -// -// This function does not itself emit any diagnostics, so it's the caller's -// responsibility to emit a warning diagnostic when this function returns true. -func shimIsIgnoreChangesStar(expr hcl.Expression) bool { - val, valDiags := expr.Value(nil) - if valDiags.HasErrors() { - return false - } - if val.Type() != cty.String || val.IsNull() || !val.IsKnown() { - return false - } - return val.AsString() == "*" -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/config.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/config.go deleted file mode 100644 index 82c88a10..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/config.go +++ /dev/null @@ -1,164 +0,0 @@ -package configs - -import ( - "sort" - - version "github.com/hashicorp/go-version" - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// A Config is a node in the tree of modules within a configuration. -// -// The module tree is constructed by following ModuleCall instances recursively -// through the root module transitively into descendent modules. -// -// A module tree described in *this* package represents the static tree -// represented by configuration. During evaluation a static ModuleNode may -// expand into zero or more module instances depending on the use of count and -// for_each configuration attributes within each call. -type Config struct { - // RootModule points to the Config for the root module within the same - // module tree as this module. If this module _is_ the root module then - // this is self-referential. - Root *Config - - // ParentModule points to the Config for the module that directly calls - // this module. If this is the root module then this field is nil. - Parent *Config - - // Path is a sequence of module logical names that traverse from the root - // module to this config. Path is empty for the root module. - // - // This should only be used to display paths to the end-user in rare cases - // where we are talking about the static module tree, before module calls - // have been resolved. In most cases, an addrs.ModuleInstance describing - // a node in the dynamic module tree is better, since it will then include - // any keys resulting from evaluating "count" and "for_each" arguments. - Path addrs.Module - - // ChildModules points to the Config for each of the direct child modules - // called from this module. The keys in this map match the keys in - // Module.ModuleCalls. - Children map[string]*Config - - // Module points to the object describing the configuration for the - // various elements (variables, resources, etc) defined by this module. - Module *Module - - // CallRange is the source range for the header of the module block that - // requested this module. - // - // This field is meaningless for the root module, where its contents are undefined. - CallRange hcl.Range - - // SourceAddr is the source address that the referenced module was requested - // from, as specified in configuration. - // - // This field is meaningless for the root module, where its contents are undefined. - SourceAddr string - - // SourceAddrRange is the location in the configuration source where the - // SourceAddr value was set, for use in diagnostic messages. - // - // This field is meaningless for the root module, where its contents are undefined. - SourceAddrRange hcl.Range - - // Version is the specific version that was selected for this module, - // based on version constraints given in configuration. - // - // This field is nil if the module was loaded from a non-registry source, - // since versions are not supported for other sources. - // - // This field is meaningless for the root module, where it will always - // be nil. - Version *version.Version -} - -// NewEmptyConfig constructs a single-node configuration tree with an empty -// root module. This is generally a pretty useless thing to do, so most callers -// should instead use BuildConfig. -func NewEmptyConfig() *Config { - ret := &Config{} - ret.Root = ret - ret.Children = make(map[string]*Config) - ret.Module = &Module{} - return ret -} - -// DeepEach calls the given function once for each module in the tree, starting -// with the receiver. -// -// A parent is always called before its children and children of a particular -// node are visited in lexicographic order by their names. -func (c *Config) DeepEach(cb func(c *Config)) { - cb(c) - - names := make([]string, 0, len(c.Children)) - for name := range c.Children { - names = append(names, name) - } - - for _, name := range names { - c.Children[name].DeepEach(cb) - } -} - -// DescendentForInstance is like Descendent except that it accepts a path -// to a particular module instance in the dynamic module graph, returning -// the node from the static module graph that corresponds to it. -// -// All instances created by a particular module call share the same -// configuration, so the keys within the given path are disregarded. -func (c *Config) DescendentForInstance(path addrs.ModuleInstance) *Config { - current := c - for _, step := range path { - current = current.Children[step.Name] - if current == nil { - return nil - } - } - return current -} - -// ProviderTypes returns the names of each distinct provider type referenced -// in the receiving configuration. -// -// This is a helper for easily determining which provider types are required -// to fully interpret the configuration, though it does not include version -// information and so callers are expected to have already dealt with -// provider version selection in an earlier step and have identified suitable -// versions for each provider. -func (c *Config) ProviderTypes() []string { - m := make(map[string]struct{}) - c.gatherProviderTypes(m) - - ret := make([]string, 0, len(m)) - for k := range m { - ret = append(ret, k) - } - sort.Strings(ret) - return ret -} -func (c *Config) gatherProviderTypes(m map[string]struct{}) { - if c == nil { - return - } - - for _, pc := range c.Module.ProviderConfigs { - m[pc.Name] = struct{}{} - } - for _, rc := range c.Module.ManagedResources { - providerAddr := rc.ProviderConfigAddr() - m[providerAddr.Type] = struct{}{} - } - for _, rc := range c.Module.DataResources { - providerAddr := rc.ProviderConfigAddr() - m[providerAddr.Type] = struct{}{} - } - - // Must also visit our child modules, recursively. - for _, cc := range c.Children { - cc.gatherProviderTypes(m) - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/config_build.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/config_build.go deleted file mode 100644 index cb46b65a..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/config_build.go +++ /dev/null @@ -1,160 +0,0 @@ -package configs - -import ( - "sort" - - version "github.com/hashicorp/go-version" - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// BuildConfig constructs a Config from a root module by loading all of its -// descendent modules via the given ModuleWalker. -// -// The result is a module tree that has so far only had basic module- and -// file-level invariants validated. If the returned diagnostics contains errors, -// the returned module tree may be incomplete but can still be used carefully -// for static analysis. -func BuildConfig(root *Module, walker ModuleWalker) (*Config, hcl.Diagnostics) { - var diags hcl.Diagnostics - cfg := &Config{ - Module: root, - } - cfg.Root = cfg // Root module is self-referential. - cfg.Children, diags = buildChildModules(cfg, walker) - return cfg, diags -} - -func buildChildModules(parent *Config, walker ModuleWalker) (map[string]*Config, hcl.Diagnostics) { - var diags hcl.Diagnostics - ret := map[string]*Config{} - - calls := parent.Module.ModuleCalls - - // We'll sort the calls by their local names so that they'll appear in a - // predictable order in any logging that's produced during the walk. - callNames := make([]string, 0, len(calls)) - for k := range calls { - callNames = append(callNames, k) - } - sort.Strings(callNames) - - for _, callName := range callNames { - call := calls[callName] - path := make([]string, len(parent.Path)+1) - copy(path, parent.Path) - path[len(path)-1] = call.Name - - req := ModuleRequest{ - Name: call.Name, - Path: path, - SourceAddr: call.SourceAddr, - SourceAddrRange: call.SourceAddrRange, - VersionConstraint: call.Version, - Parent: parent, - CallRange: call.DeclRange, - } - - mod, ver, modDiags := walker.LoadModule(&req) - diags = append(diags, modDiags...) - if mod == nil { - // nil can be returned if the source address was invalid and so - // nothing could be loaded whatsoever. LoadModule should've - // returned at least one error diagnostic in that case. - continue - } - - child := &Config{ - Parent: parent, - Root: parent.Root, - Path: path, - Module: mod, - CallRange: call.DeclRange, - SourceAddr: call.SourceAddr, - SourceAddrRange: call.SourceAddrRange, - Version: ver, - } - - child.Children, modDiags = buildChildModules(child, walker) - diags = append(diags, modDiags...) - - ret[call.Name] = child - } - - return ret, diags -} - -// A ModuleWalker knows how to find and load a child module given details about -// the module to be loaded and a reference to its partially-loaded parent -// Config. -type ModuleWalker interface { - // LoadModule finds and loads a requested child module. - // - // If errors are detected during loading, implementations should return them - // in the diagnostics object. If the diagnostics object contains any errors - // then the caller will tolerate the returned module being nil or incomplete. - // If no errors are returned, it should be non-nil and complete. - // - // Full validation need not have been performed but an implementation should - // ensure that the basic file- and module-validations performed by the - // LoadConfigDir function (valid syntax, no namespace collisions, etc) have - // been performed before returning a module. - LoadModule(req *ModuleRequest) (*Module, *version.Version, hcl.Diagnostics) -} - -// ModuleWalkerFunc is an implementation of ModuleWalker that directly wraps -// a callback function, for more convenient use of that interface. -type ModuleWalkerFunc func(req *ModuleRequest) (*Module, *version.Version, hcl.Diagnostics) - -// LoadModule implements ModuleWalker. -func (f ModuleWalkerFunc) LoadModule(req *ModuleRequest) (*Module, *version.Version, hcl.Diagnostics) { - return f(req) -} - -// ModuleRequest is used with the ModuleWalker interface to describe a child -// module that must be loaded. -type ModuleRequest struct { - // Name is the "logical name" of the module call within configuration. - // This is provided in case the name is used as part of a storage key - // for the module, but implementations must otherwise treat it as an - // opaque string. It is guaranteed to have already been validated as an - // HCL identifier and UTF-8 encoded. - Name string - - // Path is a list of logical names that traverse from the root module to - // this module. This can be used, for example, to form a lookup key for - // each distinct module call in a configuration, allowing for multiple - // calls with the same name at different points in the tree. - Path addrs.Module - - // SourceAddr is the source address string provided by the user in - // configuration. - SourceAddr string - - // SourceAddrRange is the source range for the SourceAddr value as it - // was provided in configuration. This can and should be used to generate - // diagnostics about the source address having invalid syntax, referring - // to a non-existent object, etc. - SourceAddrRange hcl.Range - - // VersionConstraint is the version constraint applied to the module in - // configuration. This data structure includes the source range for - // the constraint, which can and should be used to generate diagnostics - // about constraint-related issues, such as constraints that eliminate all - // available versions of a module whose source is otherwise valid. - VersionConstraint VersionConstraint - - // Parent is the partially-constructed module tree node that the loaded - // module will be added to. Callers may refer to any field of this - // structure except Children, which is still under construction when - // ModuleRequest objects are created and thus has undefined content. - // The main reason this is provided is so that full module paths can - // be constructed for uniqueness. - Parent *Config - - // CallRange is the source range for the header of the "module" block - // in configuration that prompted this request. This can be used as the - // subject of an error diagnostic that relates to the module call itself, - // rather than to either its source address or its version number. - CallRange hcl.Range -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/copy_dir.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/copy_dir.go deleted file mode 100644 index ebbeb3b6..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/copy_dir.go +++ /dev/null @@ -1,125 +0,0 @@ -package configload - -import ( - "io" - "os" - "path/filepath" - "strings" -) - -// copyDir copies the src directory contents into dst. Both directories -// should already exist. -func copyDir(dst, src string) error { - src, err := filepath.EvalSymlinks(src) - if err != nil { - return err - } - - walkFn := func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - - if path == src { - return nil - } - - if strings.HasPrefix(filepath.Base(path), ".") { - // Skip any dot files - if info.IsDir() { - return filepath.SkipDir - } else { - return nil - } - } - - // The "path" has the src prefixed to it. We need to join our - // destination with the path without the src on it. - dstPath := filepath.Join(dst, path[len(src):]) - - // we don't want to try and copy the same file over itself. - if eq, err := sameFile(path, dstPath); eq { - return nil - } else if err != nil { - return err - } - - // If we have a directory, make that subdirectory, then continue - // the walk. - if info.IsDir() { - if path == filepath.Join(src, dst) { - // dst is in src; don't walk it. - return nil - } - - if err := os.MkdirAll(dstPath, 0755); err != nil { - return err - } - - return nil - } - - // If the current path is a symlink, recreate the symlink relative to - // the dst directory - if info.Mode()&os.ModeSymlink == os.ModeSymlink { - target, err := os.Readlink(path) - if err != nil { - return err - } - - return os.Symlink(target, dstPath) - } - - // If we have a file, copy the contents. - srcF, err := os.Open(path) - if err != nil { - return err - } - defer srcF.Close() - - dstF, err := os.Create(dstPath) - if err != nil { - return err - } - defer dstF.Close() - - if _, err := io.Copy(dstF, srcF); err != nil { - return err - } - - // Chmod it - return os.Chmod(dstPath, info.Mode()) - } - - return filepath.Walk(src, walkFn) -} - -// sameFile tried to determine if to paths are the same file. -// If the paths don't match, we lookup the inode on supported systems. -func sameFile(a, b string) (bool, error) { - if a == b { - return true, nil - } - - aIno, err := inode(a) - if err != nil { - if os.IsNotExist(err) { - return false, nil - } - return false, err - } - - bIno, err := inode(b) - if err != nil { - if os.IsNotExist(err) { - return false, nil - } - return false, err - } - - if aIno > 0 && aIno == bIno { - return true, nil - } - - return false, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/doc.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/doc.go deleted file mode 100644 index 8b615f90..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// Package configload knows how to install modules into the .terraform/modules -// directory and to load modules from those installed locations. It is used -// in conjunction with the LoadConfig function in the parent package. -package configload diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/inode.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/inode.go deleted file mode 100644 index 57df0414..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/inode.go +++ /dev/null @@ -1,21 +0,0 @@ -// +build linux darwin openbsd netbsd solaris dragonfly - -package configload - -import ( - "fmt" - "os" - "syscall" -) - -// lookup the inode of a file on posix systems -func inode(path string) (uint64, error) { - stat, err := os.Stat(path) - if err != nil { - return 0, err - } - if st, ok := stat.Sys().(*syscall.Stat_t); ok { - return st.Ino, nil - } - return 0, fmt.Errorf("could not determine file inode") -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/inode_freebsd.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/inode_freebsd.go deleted file mode 100644 index 4dc28eaa..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/inode_freebsd.go +++ /dev/null @@ -1,21 +0,0 @@ -// +build freebsd - -package configload - -import ( - "fmt" - "os" - "syscall" -) - -// lookup the inode of a file on posix systems -func inode(path string) (uint64, error) { - stat, err := os.Stat(path) - if err != nil { - return 0, err - } - if st, ok := stat.Sys().(*syscall.Stat_t); ok { - return uint64(st.Ino), nil - } - return 0, fmt.Errorf("could not determine file inode") -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/inode_windows.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/inode_windows.go deleted file mode 100644 index 0d22e672..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/inode_windows.go +++ /dev/null @@ -1,8 +0,0 @@ -// +build windows - -package configload - -// no syscall.Stat_t on windows, return 0 for inodes -func inode(path string) (uint64, error) { - return 0, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/loader.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/loader.go deleted file mode 100644 index 0d12d7d2..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/loader.go +++ /dev/null @@ -1,101 +0,0 @@ -package configload - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/registry" - "github.com/hashicorp/terraform-svchost/disco" - "github.com/spf13/afero" -) - -// A Loader instance is the main entry-point for loading configurations via -// this package. -// -// It extends the general config-loading functionality in the parent package -// "configs" to support installation of modules from remote sources and -// loading full configurations using modules that were previously installed. -type Loader struct { - // parser is used to read configuration - parser *configs.Parser - - // modules is used to install and locate descendent modules that are - // referenced (directly or indirectly) from the root module. - modules moduleMgr -} - -// Config is used with NewLoader to specify configuration arguments for the -// loader. -type Config struct { - // ModulesDir is a path to a directory where descendent modules are - // (or should be) installed. (This is usually the - // .terraform/modules directory, in the common case where this package - // is being loaded from the main Terraform CLI package.) - ModulesDir string - - // Services is the service discovery client to use when locating remote - // module registry endpoints. If this is nil then registry sources are - // not supported, which should be true only in specialized circumstances - // such as in tests. - Services *disco.Disco -} - -// NewLoader creates and returns a loader that reads configuration from the -// real OS filesystem. -// -// The loader has some internal state about the modules that are currently -// installed, which is read from disk as part of this function. If that -// manifest cannot be read then an error will be returned. -func NewLoader(config *Config) (*Loader, error) { - fs := afero.NewOsFs() - parser := configs.NewParser(fs) - reg := registry.NewClient(config.Services, nil) - - ret := &Loader{ - parser: parser, - modules: moduleMgr{ - FS: afero.Afero{Fs: fs}, - CanInstall: true, - Dir: config.ModulesDir, - Services: config.Services, - Registry: reg, - }, - } - - err := ret.modules.readModuleManifestSnapshot() - if err != nil { - return nil, fmt.Errorf("failed to read module manifest: %s", err) - } - - return ret, nil -} - -// ModulesDir returns the path to the directory where the loader will look for -// the local cache of remote module packages. -func (l *Loader) ModulesDir() string { - return l.modules.Dir -} - -// RefreshModules updates the in-memory cache of the module manifest from the -// module manifest file on disk. This is not necessary in normal use because -// module installation and configuration loading are separate steps, but it -// can be useful in tests where module installation is done as a part of -// configuration loading by a helper function. -// -// Call this function after any module installation where an existing loader -// is already alive and may be used again later. -// -// An error is returned if the manifest file cannot be read. -func (l *Loader) RefreshModules() error { - if l == nil { - // Nothing to do, then. - return nil - } - return l.modules.readModuleManifestSnapshot() -} - -// Sources returns the source code cache for the underlying parser of this -// loader. This is a shorthand for l.Parser().Sources(). -func (l *Loader) Sources() map[string][]byte { - return l.parser.Sources() -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/loader_load.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/loader_load.go deleted file mode 100644 index bcfa733e..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/loader_load.go +++ /dev/null @@ -1,105 +0,0 @@ -package configload - -import ( - "fmt" - - version "github.com/hashicorp/go-version" - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" -) - -// LoadConfig reads the Terraform module in the given directory and uses it as the -// root module to build the static module tree that represents a configuration, -// assuming that all required descendent modules have already been installed. -// -// If error diagnostics are returned, the returned configuration may be either -// nil or incomplete. In the latter case, cautious static analysis is possible -// in spite of the errors. -// -// LoadConfig performs the basic syntax and uniqueness validations that are -// required to process the individual modules, and also detects -func (l *Loader) LoadConfig(rootDir string) (*configs.Config, hcl.Diagnostics) { - rootMod, diags := l.parser.LoadConfigDir(rootDir) - if rootMod == nil { - return nil, diags - } - - cfg, cDiags := configs.BuildConfig(rootMod, configs.ModuleWalkerFunc(l.moduleWalkerLoad)) - diags = append(diags, cDiags...) - - return cfg, diags -} - -// moduleWalkerLoad is a configs.ModuleWalkerFunc for loading modules that -// are presumed to have already been installed. A different function -// (moduleWalkerInstall) is used for installation. -func (l *Loader) moduleWalkerLoad(req *configs.ModuleRequest) (*configs.Module, *version.Version, hcl.Diagnostics) { - // Since we're just loading here, we expect that all referenced modules - // will be already installed and described in our manifest. However, we - // do verify that the manifest and the configuration are in agreement - // so that we can prompt the user to run "terraform init" if not. - - key := l.modules.manifest.ModuleKey(req.Path) - record, exists := l.modules.manifest[key] - - if !exists { - return nil, nil, hcl.Diagnostics{ - { - Severity: hcl.DiagError, - Summary: "Module not installed", - Detail: "This module is not yet installed. Run \"terraform init\" to install all modules required by this configuration.", - Subject: &req.CallRange, - }, - } - } - - var diags hcl.Diagnostics - - // Check for inconsistencies between manifest and config - if req.SourceAddr != record.SourceAddr { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Module source has changed", - Detail: "The source address was changed since this module was installed. Run \"terraform init\" to install all modules required by this configuration.", - Subject: &req.SourceAddrRange, - }) - } - if len(req.VersionConstraint.Required) > 0 && record.Version == nil { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Module version requirements have changed", - Detail: "The version requirements have changed since this module was installed and the installed version is no longer acceptable. Run \"terraform init\" to install all modules required by this configuration.", - Subject: &req.SourceAddrRange, - }) - } - if record.Version != nil && !req.VersionConstraint.Required.Check(record.Version) { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Module version requirements have changed", - Detail: fmt.Sprintf( - "The version requirements have changed since this module was installed and the installed version (%s) is no longer acceptable. Run \"terraform init\" to install all modules required by this configuration.", - record.Version, - ), - Subject: &req.SourceAddrRange, - }) - } - - mod, mDiags := l.parser.LoadConfigDir(record.Dir) - diags = append(diags, mDiags...) - if mod == nil { - // nil specifically indicates that the directory does not exist or - // cannot be read, so in this case we'll discard any generic diagnostics - // returned from LoadConfigDir and produce our own context-sensitive - // error message. - return nil, nil, hcl.Diagnostics{ - { - Severity: hcl.DiagError, - Summary: "Module not installed", - Detail: fmt.Sprintf("This module's local cache directory %s could not be read. Run \"terraform init\" to install all modules required by this configuration.", record.Dir), - Subject: &req.CallRange, - }, - } - } - - return mod, record.Version, diags -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/loader_snapshot.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/loader_snapshot.go deleted file mode 100644 index 0772edc7..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/loader_snapshot.go +++ /dev/null @@ -1,492 +0,0 @@ -package configload - -import ( - "fmt" - "io" - "os" - "path/filepath" - "sort" - "time" - - version "github.com/hashicorp/go-version" - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/modsdir" - "github.com/spf13/afero" -) - -// LoadConfigWithSnapshot is a variant of LoadConfig that also simultaneously -// creates an in-memory snapshot of the configuration files used, which can -// be later used to create a loader that may read only from this snapshot. -func (l *Loader) LoadConfigWithSnapshot(rootDir string) (*configs.Config, *Snapshot, hcl.Diagnostics) { - rootMod, diags := l.parser.LoadConfigDir(rootDir) - if rootMod == nil { - return nil, nil, diags - } - - snap := &Snapshot{ - Modules: map[string]*SnapshotModule{}, - } - walker := l.makeModuleWalkerSnapshot(snap) - cfg, cDiags := configs.BuildConfig(rootMod, walker) - diags = append(diags, cDiags...) - - addDiags := l.addModuleToSnapshot(snap, "", rootDir, "", nil) - diags = append(diags, addDiags...) - - return cfg, snap, diags -} - -// NewLoaderFromSnapshot creates a Loader that reads files only from the -// given snapshot. -// -// A snapshot-based loader cannot install modules, so calling InstallModules -// on the return value will cause a panic. -// -// A snapshot-based loader also has access only to configuration files. Its -// underlying parser does not have access to other files in the native -// filesystem, such as values files. For those, either use a normal loader -// (created by NewLoader) or use the configs.Parser API directly. -func NewLoaderFromSnapshot(snap *Snapshot) *Loader { - fs := snapshotFS{snap} - parser := configs.NewParser(fs) - - ret := &Loader{ - parser: parser, - modules: moduleMgr{ - FS: afero.Afero{Fs: fs}, - CanInstall: false, - manifest: snap.moduleManifest(), - }, - } - - return ret -} - -// Snapshot is an in-memory representation of the source files from a -// configuration, which can be used as an alternative configurations source -// for a loader with NewLoaderFromSnapshot. -// -// The primary purpose of a Snapshot is to build the configuration portion -// of a plan file (see ../../plans/planfile) so that it can later be reloaded -// and used to recover the exact configuration that the plan was built from. -type Snapshot struct { - // Modules is a map from opaque module keys (suitable for use as directory - // names on all supported operating systems) to the snapshot information - // about each module. - Modules map[string]*SnapshotModule -} - -// SnapshotModule represents a single module within a Snapshot. -type SnapshotModule struct { - // Dir is the path, relative to the root directory given when the - // snapshot was created, where the module appears in the snapshot's - // virtual filesystem. - Dir string - - // Files is a map from each configuration file filename for the - // module to a raw byte representation of the source file contents. - Files map[string][]byte - - // SourceAddr is the source address given for this module in configuration. - SourceAddr string `json:"Source"` - - // Version is the version of the module that is installed, or nil if - // the module is installed from a source that does not support versions. - Version *version.Version `json:"-"` -} - -// moduleManifest constructs a module manifest based on the contents of -// the receiving snapshot. -func (s *Snapshot) moduleManifest() modsdir.Manifest { - ret := make(modsdir.Manifest) - - for k, modSnap := range s.Modules { - ret[k] = modsdir.Record{ - Key: k, - Dir: modSnap.Dir, - SourceAddr: modSnap.SourceAddr, - Version: modSnap.Version, - } - } - - return ret -} - -// makeModuleWalkerSnapshot creates a configs.ModuleWalker that will exhibit -// the same lookup behaviors as l.moduleWalkerLoad but will additionally write -// source files from the referenced modules into the given snapshot. -func (l *Loader) makeModuleWalkerSnapshot(snap *Snapshot) configs.ModuleWalker { - return configs.ModuleWalkerFunc( - func(req *configs.ModuleRequest) (*configs.Module, *version.Version, hcl.Diagnostics) { - mod, v, diags := l.moduleWalkerLoad(req) - if diags.HasErrors() { - return mod, v, diags - } - - key := l.modules.manifest.ModuleKey(req.Path) - record, exists := l.modules.manifest[key] - - if !exists { - // Should never happen, since otherwise moduleWalkerLoader would've - // returned an error and we would've returned already. - panic(fmt.Sprintf("module %s is not present in manifest", key)) - } - - addDiags := l.addModuleToSnapshot(snap, key, record.Dir, record.SourceAddr, record.Version) - diags = append(diags, addDiags...) - - return mod, v, diags - }, - ) -} - -func (l *Loader) addModuleToSnapshot(snap *Snapshot, key string, dir string, sourceAddr string, v *version.Version) hcl.Diagnostics { - var diags hcl.Diagnostics - - primaryFiles, overrideFiles, moreDiags := l.parser.ConfigDirFiles(dir) - if moreDiags.HasErrors() { - // Any diagnostics we get here should be already present - // in diags, so it's weird if we get here but we'll allow it - // and return a general error message in that case. - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Failed to read directory for module", - Detail: fmt.Sprintf("The source directory %s could not be read", dir), - }) - return diags - } - - snapMod := &SnapshotModule{ - Dir: dir, - Files: map[string][]byte{}, - SourceAddr: sourceAddr, - Version: v, - } - - files := make([]string, 0, len(primaryFiles)+len(overrideFiles)) - files = append(files, primaryFiles...) - files = append(files, overrideFiles...) - sources := l.Sources() // should be populated with all the files we need by now - for _, filePath := range files { - filename := filepath.Base(filePath) - src, exists := sources[filePath] - if !exists { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Missing source file for snapshot", - Detail: fmt.Sprintf("The source code for file %s could not be found to produce a configuration snapshot.", filePath), - }) - continue - } - snapMod.Files[filepath.Clean(filename)] = src - } - - snap.Modules[key] = snapMod - - return diags -} - -// snapshotFS is an implementation of afero.Fs that reads from a snapshot. -// -// This is not intended as a general-purpose filesystem implementation. Instead, -// it just supports the minimal functionality required to support the -// configuration loader and parser as an implementation detail of creating -// a loader from a snapshot. -type snapshotFS struct { - snap *Snapshot -} - -var _ afero.Fs = snapshotFS{} - -func (fs snapshotFS) Create(name string) (afero.File, error) { - return nil, fmt.Errorf("cannot create file inside configuration snapshot") -} - -func (fs snapshotFS) Mkdir(name string, perm os.FileMode) error { - return fmt.Errorf("cannot create directory inside configuration snapshot") -} - -func (fs snapshotFS) MkdirAll(name string, perm os.FileMode) error { - return fmt.Errorf("cannot create directories inside configuration snapshot") -} - -func (fs snapshotFS) Open(name string) (afero.File, error) { - - // Our "filesystem" is sparsely populated only with the directories - // mentioned by modules in our snapshot, so the high-level process - // for opening a file is: - // - Find the module snapshot corresponding to the containing directory - // - Find the file within that snapshot - // - Wrap the resulting byte slice in a snapshotFile to return - // - // The other possibility handled here is if the given name is for the - // module directory itself, in which case we'll return a snapshotDir - // instead. - // - // This function doesn't try to be incredibly robust in supporting - // different permutations of paths, etc because in practice we only - // need to support the path forms that our own loader and parser will - // generate. - - dir := filepath.Dir(name) - fn := filepath.Base(name) - directDir := filepath.Clean(name) - - // First we'll check to see if this is an exact path for a module directory. - // We need to do this first (rather than as part of the next loop below) - // because a module in a child directory of another module can otherwise - // appear to be a file in that parent directory. - for _, candidate := range fs.snap.Modules { - modDir := filepath.Clean(candidate.Dir) - if modDir == directDir { - // We've matched the module directory itself - filenames := make([]string, 0, len(candidate.Files)) - for n := range candidate.Files { - filenames = append(filenames, n) - } - sort.Strings(filenames) - return snapshotDir{ - filenames: filenames, - }, nil - } - } - - // If we get here then the given path isn't a module directory exactly, so - // we'll treat it as a file path and try to find a module directory it - // could be located in. - var modSnap *SnapshotModule - for _, candidate := range fs.snap.Modules { - modDir := filepath.Clean(candidate.Dir) - if modDir == dir { - modSnap = candidate - break - } - } - if modSnap == nil { - return nil, os.ErrNotExist - } - - src, exists := modSnap.Files[fn] - if !exists { - return nil, os.ErrNotExist - } - - return &snapshotFile{ - src: src, - }, nil -} - -func (fs snapshotFS) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { - return fs.Open(name) -} - -func (fs snapshotFS) Remove(name string) error { - return fmt.Errorf("cannot remove file inside configuration snapshot") -} - -func (fs snapshotFS) RemoveAll(path string) error { - return fmt.Errorf("cannot remove files inside configuration snapshot") -} - -func (fs snapshotFS) Rename(old, new string) error { - return fmt.Errorf("cannot rename file inside configuration snapshot") -} - -func (fs snapshotFS) Stat(name string) (os.FileInfo, error) { - f, err := fs.Open(name) - if err != nil { - return nil, err - } - _, isDir := f.(snapshotDir) - return snapshotFileInfo{ - name: filepath.Base(name), - isDir: isDir, - }, nil -} - -func (fs snapshotFS) Name() string { - return "ConfigSnapshotFS" -} - -func (fs snapshotFS) Chmod(name string, mode os.FileMode) error { - return fmt.Errorf("cannot set file mode inside configuration snapshot") -} - -func (fs snapshotFS) Chtimes(name string, atime, mtime time.Time) error { - return fmt.Errorf("cannot set file times inside configuration snapshot") -} - -type snapshotFile struct { - snapshotFileStub - src []byte - at int64 -} - -var _ afero.File = (*snapshotFile)(nil) - -func (f *snapshotFile) Read(p []byte) (n int, err error) { - if len(p) > 0 && f.at == int64(len(f.src)) { - return 0, io.EOF - } - if f.at > int64(len(f.src)) { - return 0, io.ErrUnexpectedEOF - } - if int64(len(f.src))-f.at >= int64(len(p)) { - n = len(p) - } else { - n = int(int64(len(f.src)) - f.at) - } - copy(p, f.src[f.at:f.at+int64(n)]) - f.at += int64(n) - return -} - -func (f *snapshotFile) ReadAt(p []byte, off int64) (n int, err error) { - f.at = off - return f.Read(p) -} - -func (f *snapshotFile) Seek(offset int64, whence int) (int64, error) { - switch whence { - case 0: - f.at = offset - case 1: - f.at += offset - case 2: - f.at = int64(len(f.src)) + offset - } - return f.at, nil -} - -type snapshotDir struct { - snapshotFileStub - filenames []string - at int -} - -var _ afero.File = snapshotDir{} - -func (f snapshotDir) Readdir(count int) ([]os.FileInfo, error) { - names, err := f.Readdirnames(count) - if err != nil { - return nil, err - } - ret := make([]os.FileInfo, len(names)) - for i, name := range names { - ret[i] = snapshotFileInfo{ - name: name, - isDir: false, - } - } - return ret, nil -} - -func (f snapshotDir) Readdirnames(count int) ([]string, error) { - var outLen int - names := f.filenames[f.at:] - if count > 0 { - if len(names) < count { - outLen = len(names) - } else { - outLen = count - } - if len(names) == 0 { - return nil, io.EOF - } - } else { - outLen = len(names) - } - f.at += outLen - - return names[:outLen], nil -} - -// snapshotFileInfo is a minimal implementation of os.FileInfo to support our -// virtual filesystem from snapshots. -type snapshotFileInfo struct { - name string - isDir bool -} - -var _ os.FileInfo = snapshotFileInfo{} - -func (fi snapshotFileInfo) Name() string { - return fi.name -} - -func (fi snapshotFileInfo) Size() int64 { - // In practice, our parser and loader never call Size - return -1 -} - -func (fi snapshotFileInfo) Mode() os.FileMode { - return os.ModePerm -} - -func (fi snapshotFileInfo) ModTime() time.Time { - return time.Now() -} - -func (fi snapshotFileInfo) IsDir() bool { - return fi.isDir -} - -func (fi snapshotFileInfo) Sys() interface{} { - return nil -} - -type snapshotFileStub struct{} - -func (f snapshotFileStub) Close() error { - return nil -} - -func (f snapshotFileStub) Read(p []byte) (n int, err error) { - return 0, fmt.Errorf("cannot read") -} - -func (f snapshotFileStub) ReadAt(p []byte, off int64) (n int, err error) { - return 0, fmt.Errorf("cannot read") -} - -func (f snapshotFileStub) Seek(offset int64, whence int) (int64, error) { - return 0, fmt.Errorf("cannot seek") -} - -func (f snapshotFileStub) Write(p []byte) (n int, err error) { - return f.WriteAt(p, 0) -} - -func (f snapshotFileStub) WriteAt(p []byte, off int64) (n int, err error) { - return 0, fmt.Errorf("cannot write to file in snapshot") -} - -func (f snapshotFileStub) WriteString(s string) (n int, err error) { - return 0, fmt.Errorf("cannot write to file in snapshot") -} - -func (f snapshotFileStub) Name() string { - // in practice, the loader and parser never use this - return "" -} - -func (f snapshotFileStub) Readdir(count int) ([]os.FileInfo, error) { - return nil, fmt.Errorf("cannot use Readdir on a file") -} - -func (f snapshotFileStub) Readdirnames(count int) ([]string, error) { - return nil, fmt.Errorf("cannot use Readdir on a file") -} - -func (f snapshotFileStub) Stat() (os.FileInfo, error) { - return nil, fmt.Errorf("cannot stat") -} - -func (f snapshotFileStub) Sync() error { - return nil -} - -func (f snapshotFileStub) Truncate(size int64) error { - return fmt.Errorf("cannot write to file in snapshot") -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/module_mgr.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/module_mgr.go deleted file mode 100644 index 797f50d2..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/module_mgr.go +++ /dev/null @@ -1,62 +0,0 @@ -package configload - -import ( - "os" - "path/filepath" - - "github.com/hashicorp/terraform-plugin-sdk/internal/modsdir" - "github.com/hashicorp/terraform-plugin-sdk/internal/registry" - "github.com/hashicorp/terraform-svchost/disco" - "github.com/spf13/afero" -) - -type moduleMgr struct { - FS afero.Afero - - // CanInstall is true for a module manager that can support installation. - // - // This must be set only if FS is an afero.OsFs, because the installer - // (which uses go-getter) is not aware of the virtual filesystem - // abstraction and will always write into the "real" filesystem. - CanInstall bool - - // Dir is the path where descendent modules are (or will be) installed. - Dir string - - // Services is a service discovery client that will be used to find - // remote module registry endpoints. This object may be pre-loaded with - // cached discovery information. - Services *disco.Disco - - // Registry is a client for the module registry protocol, which is used - // when a module is requested from a registry source. - Registry *registry.Client - - // manifest tracks the currently-installed modules for this manager. - // - // The loader may read this. Only the installer may write to it, and - // after a set of updates are completed the installer must call - // writeModuleManifestSnapshot to persist a snapshot of the manifest - // to disk for use on subsequent runs. - manifest modsdir.Manifest -} - -func (m *moduleMgr) manifestSnapshotPath() string { - return filepath.Join(m.Dir, modsdir.ManifestSnapshotFilename) -} - -// readModuleManifestSnapshot loads a manifest snapshot from the filesystem. -func (m *moduleMgr) readModuleManifestSnapshot() error { - r, err := m.FS.Open(m.manifestSnapshotPath()) - if err != nil { - if os.IsNotExist(err) { - // We'll treat a missing file as an empty manifest - m.manifest = make(modsdir.Manifest) - return nil - } - return err - } - - m.manifest, err = modsdir.ReadManifestSnapshot(r) - return err -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/testing.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/testing.go deleted file mode 100644 index 86ca9d10..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configload/testing.go +++ /dev/null @@ -1,43 +0,0 @@ -package configload - -import ( - "io/ioutil" - "os" - "testing" -) - -// NewLoaderForTests is a variant of NewLoader that is intended to be more -// convenient for unit tests. -// -// The loader's modules directory is a separate temporary directory created -// for each call. Along with the created loader, this function returns a -// cleanup function that should be called before the test completes in order -// to remove that temporary directory. -// -// In the case of any errors, t.Fatal (or similar) will be called to halt -// execution of the test, so the calling test does not need to handle errors -// itself. -func NewLoaderForTests(t *testing.T) (*Loader, func()) { - t.Helper() - - modulesDir, err := ioutil.TempDir("", "tf-configs") - if err != nil { - t.Fatalf("failed to create temporary modules dir: %s", err) - return nil, func() {} - } - - cleanup := func() { - os.RemoveAll(modulesDir) - } - - loader, err := NewLoader(&Config{ - ModulesDir: modulesDir, - }) - if err != nil { - cleanup() - t.Fatalf("failed to create config loader: %s", err) - return nil, func() {} - } - - return loader, cleanup -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/coerce_value.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/coerce_value.go index 41a53374..48278abe 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/coerce_value.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/coerce_value.go @@ -3,8 +3,8 @@ package configschema import ( "fmt" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/go-cty/cty/convert" ) // CoerceValue attempts to force the given value to conform to the type diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/coerce_value_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/coerce_value_test.go new file mode 100644 index 00000000..68aa1fcf --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/coerce_value_test.go @@ -0,0 +1,573 @@ +package configschema + +import ( + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/hashicorp/go-cty/cty" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/tfdiags" +) + +func TestCoerceValue(t *testing.T) { + tests := map[string]struct { + Schema *Block + Input cty.Value + WantValue cty.Value + WantErr string + }{ + "empty schema and value": { + &Block{}, + cty.EmptyObjectVal, + cty.EmptyObjectVal, + ``, + }, + "attribute present": { + &Block{ + Attributes: map[string]*Attribute{ + "foo": { + Type: cty.String, + Optional: true, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.True, + }), + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("true"), + }), + ``, + }, + "single block present": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "foo": { + Block: Block{}, + Nesting: NestingSingle, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.EmptyObjectVal, + }), + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.EmptyObjectVal, + }), + ``, + }, + "single block wrong type": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "foo": { + Block: Block{}, + Nesting: NestingSingle, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.True, + }), + cty.DynamicVal, + `.foo: an object is required`, + }, + "list block with one item": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "foo": { + Block: Block{}, + Nesting: NestingList, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{cty.EmptyObjectVal}), + }), + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{cty.EmptyObjectVal}), + }), + ``, + }, + "set block with one item": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "foo": { + Block: Block{}, + Nesting: NestingSet, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{cty.EmptyObjectVal}), // can implicitly convert to set + }), + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.SetVal([]cty.Value{cty.EmptyObjectVal}), + }), + ``, + }, + "map block with one item": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "foo": { + Block: Block{}, + Nesting: NestingMap, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.MapVal(map[string]cty.Value{"foo": cty.EmptyObjectVal}), + }), + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.MapVal(map[string]cty.Value{"foo": cty.EmptyObjectVal}), + }), + ``, + }, + "list block with one item having an attribute": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "foo": { + Block: Block{ + Attributes: map[string]*Attribute{ + "bar": { + Type: cty.String, + Required: true, + }, + }, + }, + Nesting: NestingList, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("hello"), + })}), + }), + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("hello"), + })}), + }), + ``, + }, + "list block with one item having a missing attribute": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "foo": { + Block: Block{ + Attributes: map[string]*Attribute{ + "bar": { + Type: cty.String, + Required: true, + }, + }, + }, + Nesting: NestingList, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{cty.EmptyObjectVal}), + }), + cty.DynamicVal, + `.foo[0]: attribute "bar" is required`, + }, + "list block with one item having an extraneous attribute": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "foo": { + Block: Block{}, + Nesting: NestingList, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("hello"), + })}), + }), + cty.DynamicVal, + `.foo[0]: unexpected attribute "bar"`, + }, + "missing optional attribute": { + &Block{ + Attributes: map[string]*Attribute{ + "foo": { + Type: cty.String, + Optional: true, + }, + }, + }, + cty.EmptyObjectVal, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.NullVal(cty.String), + }), + ``, + }, + "missing optional single block": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "foo": { + Block: Block{}, + Nesting: NestingSingle, + }, + }, + }, + cty.EmptyObjectVal, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.NullVal(cty.EmptyObject), + }), + ``, + }, + "missing optional list block": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "foo": { + Block: Block{}, + Nesting: NestingList, + }, + }, + }, + cty.EmptyObjectVal, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListValEmpty(cty.EmptyObject), + }), + ``, + }, + "missing optional set block": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "foo": { + Block: Block{}, + Nesting: NestingSet, + }, + }, + }, + cty.EmptyObjectVal, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.SetValEmpty(cty.EmptyObject), + }), + ``, + }, + "missing optional map block": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "foo": { + Block: Block{}, + Nesting: NestingMap, + }, + }, + }, + cty.EmptyObjectVal, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.MapValEmpty(cty.EmptyObject), + }), + ``, + }, + "missing required attribute": { + &Block{ + Attributes: map[string]*Attribute{ + "foo": { + Type: cty.String, + Required: true, + }, + }, + }, + cty.EmptyObjectVal, + cty.DynamicVal, + `attribute "foo" is required`, + }, + "missing required single block": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "foo": { + Block: Block{}, + Nesting: NestingSingle, + MinItems: 1, + MaxItems: 1, + }, + }, + }, + cty.EmptyObjectVal, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.NullVal(cty.EmptyObject), + }), + ``, + }, + "unknown nested list": { + &Block{ + Attributes: map[string]*Attribute{ + "attr": { + Type: cty.String, + Required: true, + }, + }, + BlockTypes: map[string]*NestedBlock{ + "foo": { + Block: Block{}, + Nesting: NestingList, + MinItems: 2, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("test"), + "foo": cty.UnknownVal(cty.EmptyObject), + }), + cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("test"), + "foo": cty.UnknownVal(cty.List(cty.EmptyObject)), + }), + "", + }, + "unknowns in nested list": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "foo": { + Block: Block{ + Attributes: map[string]*Attribute{ + "attr": { + Type: cty.String, + Required: true, + }, + }, + }, + Nesting: NestingList, + MinItems: 2, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "attr": cty.UnknownVal(cty.String), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "attr": cty.UnknownVal(cty.String), + }), + }), + }), + "", + }, + "unknown nested set": { + &Block{ + Attributes: map[string]*Attribute{ + "attr": { + Type: cty.String, + Required: true, + }, + }, + BlockTypes: map[string]*NestedBlock{ + "foo": { + Block: Block{}, + Nesting: NestingSet, + MinItems: 1, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("test"), + "foo": cty.UnknownVal(cty.EmptyObject), + }), + cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("test"), + "foo": cty.UnknownVal(cty.Set(cty.EmptyObject)), + }), + "", + }, + "unknown nested map": { + &Block{ + Attributes: map[string]*Attribute{ + "attr": { + Type: cty.String, + Required: true, + }, + }, + BlockTypes: map[string]*NestedBlock{ + "foo": { + Block: Block{}, + Nesting: NestingMap, + MinItems: 1, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("test"), + "foo": cty.UnknownVal(cty.Map(cty.String)), + }), + cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("test"), + "foo": cty.UnknownVal(cty.Map(cty.EmptyObject)), + }), + "", + }, + "extraneous attribute": { + &Block{}, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + }), + cty.DynamicVal, + `unexpected attribute "foo"`, + }, + "wrong attribute type": { + &Block{ + Attributes: map[string]*Attribute{ + "foo": { + Type: cty.Number, + Required: true, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.False, + }), + cty.DynamicVal, + `.foo: number required`, + }, + "unset computed value": { + &Block{ + Attributes: map[string]*Attribute{ + "foo": { + Type: cty.String, + Optional: true, + Computed: true, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{}), + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.NullVal(cty.String), + }), + ``, + }, + "dynamic value attributes": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "foo": { + Nesting: NestingMap, + Block: Block{ + Attributes: map[string]*Attribute{ + "bar": { + Type: cty.String, + Optional: true, + Computed: true, + }, + "baz": { + Type: cty.DynamicPseudoType, + Optional: true, + Computed: true, + }, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("beep"), + }), + "b": cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("boop"), + "baz": cty.NumberIntVal(8), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("beep"), + "baz": cty.NullVal(cty.DynamicPseudoType), + }), + "b": cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("boop"), + "baz": cty.NumberIntVal(8), + }), + }), + }), + ``, + }, + "dynamic attributes in map": { + // Convert a block represented as a map to an object if a + // DynamicPseudoType causes the element types to mismatch. + &Block{ + BlockTypes: map[string]*NestedBlock{ + "foo": { + Nesting: NestingMap, + Block: Block{ + Attributes: map[string]*Attribute{ + "bar": { + Type: cty.String, + Optional: true, + Computed: true, + }, + "baz": { + Type: cty.DynamicPseudoType, + Optional: true, + Computed: true, + }, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.MapVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("beep"), + }), + "b": cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("boop"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("beep"), + "baz": cty.NullVal(cty.DynamicPseudoType), + }), + "b": cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("boop"), + "baz": cty.NullVal(cty.DynamicPseudoType), + }), + }), + }), + ``, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + gotValue, gotErrObj := test.Schema.CoerceValue(test.Input) + + if gotErrObj == nil { + if test.WantErr != "" { + t.Fatalf("coersion succeeded; want error: %q", test.WantErr) + } + } else { + gotErr := tfdiags.FormatError(gotErrObj) + if gotErr != test.WantErr { + t.Fatalf("wrong error\ngot: %s\nwant: %s", gotErr, test.WantErr) + } + return + } + + if !gotValue.RawEquals(test.WantValue) { + t.Errorf("wrong result\ninput: %#v\ngot: %#v\nwant: %#v", test.Input, gotValue, test.WantValue) + } + + // The coerced value must always conform to the implied type of + // the schema. + wantTy := test.Schema.ImpliedType() + gotTy := gotValue.Type() + if errs := gotTy.TestConformance(wantTy); len(errs) > 0 { + t.Errorf("empty value has incorrect type\ngot: %#v\nwant: %#v\nerrors: %s", gotTy, wantTy, spew.Sdump(errs)) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/decoder_spec.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/decoder_spec.go deleted file mode 100644 index 2c21ca5e..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/decoder_spec.go +++ /dev/null @@ -1,123 +0,0 @@ -package configschema - -import ( - "github.com/hashicorp/hcl/v2/hcldec" -) - -var mapLabelNames = []string{"key"} - -// DecoderSpec returns a hcldec.Spec that can be used to decode a HCL Body -// using the facilities in the hcldec package. -// -// The returned specification is guaranteed to return a value of the same type -// returned by method ImpliedType, but it may contain null values if any of the -// block attributes are defined as optional and/or computed respectively. -func (b *Block) DecoderSpec() hcldec.Spec { - ret := hcldec.ObjectSpec{} - if b == nil { - return ret - } - - for name, attrS := range b.Attributes { - ret[name] = attrS.decoderSpec(name) - } - - for name, blockS := range b.BlockTypes { - if _, exists := ret[name]; exists { - // This indicates an invalid schema, since it's not valid to - // define both an attribute and a block type of the same name. - // However, we don't raise this here since it's checked by - // InternalValidate. - continue - } - - childSpec := blockS.Block.DecoderSpec() - - // We can only validate 0 or 1 for MinItems, because a dynamic block - // may satisfy any number of min items while only having a single - // block in the config. We cannot validate MaxItems because a - // configuration may have any number of dynamic blocks - minItems := 0 - if blockS.MinItems > 1 { - minItems = 1 - } - - switch blockS.Nesting { - case NestingSingle, NestingGroup: - ret[name] = &hcldec.BlockSpec{ - TypeName: name, - Nested: childSpec, - Required: blockS.MinItems == 1, - } - if blockS.Nesting == NestingGroup { - ret[name] = &hcldec.DefaultSpec{ - Primary: ret[name], - Default: &hcldec.LiteralSpec{ - Value: blockS.EmptyValue(), - }, - } - } - case NestingList: - // We prefer to use a list where possible, since it makes our - // implied type more complete, but if there are any - // dynamically-typed attributes inside we must use a tuple - // instead, at the expense of our type then not being predictable. - if blockS.Block.ImpliedType().HasDynamicTypes() { - ret[name] = &hcldec.BlockTupleSpec{ - TypeName: name, - Nested: childSpec, - MinItems: minItems, - } - } else { - ret[name] = &hcldec.BlockListSpec{ - TypeName: name, - Nested: childSpec, - MinItems: minItems, - } - } - case NestingSet: - // We forbid dynamically-typed attributes inside NestingSet in - // InternalValidate, so we don't do anything special to handle - // that here. (There is no set analog to tuple and object types, - // because cty's set implementation depends on knowing the static - // type in order to properly compute its internal hashes.) - ret[name] = &hcldec.BlockSetSpec{ - TypeName: name, - Nested: childSpec, - MinItems: minItems, - } - case NestingMap: - // We prefer to use a list where possible, since it makes our - // implied type more complete, but if there are any - // dynamically-typed attributes inside we must use a tuple - // instead, at the expense of our type then not being predictable. - if blockS.Block.ImpliedType().HasDynamicTypes() { - ret[name] = &hcldec.BlockObjectSpec{ - TypeName: name, - Nested: childSpec, - LabelNames: mapLabelNames, - } - } else { - ret[name] = &hcldec.BlockMapSpec{ - TypeName: name, - Nested: childSpec, - LabelNames: mapLabelNames, - } - } - default: - // Invalid nesting type is just ignored. It's checked by - // InternalValidate. - continue - } - } - - return ret -} - -func (a *Attribute) decoderSpec(name string) hcldec.Spec { - return &hcldec.AttrSpec{ - Name: name, - Type: a.Type, - Required: a.Required, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/empty_value.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/empty_value.go index 005da56b..51b8c5d2 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/empty_value.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/empty_value.go @@ -1,7 +1,7 @@ package configschema import ( - "github.com/zclconf/go-cty/cty" + "github.com/hashicorp/go-cty/cty" ) // EmptyValue returns the "empty value" for the recieving block, which for diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/empty_value_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/empty_value_test.go new file mode 100644 index 00000000..5ff1f287 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/empty_value_test.go @@ -0,0 +1,178 @@ +package configschema + +import ( + "fmt" + "testing" + + "github.com/apparentlymart/go-dump/dump" + "github.com/davecgh/go-spew/spew" + "github.com/hashicorp/go-cty/cty" +) + +func TestBlockEmptyValue(t *testing.T) { + tests := []struct { + Schema *Block + Want cty.Value + }{ + { + &Block{}, + cty.EmptyObjectVal, + }, + { + &Block{ + Attributes: map[string]*Attribute{ + "str": {Type: cty.String, Required: true}, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "str": cty.NullVal(cty.String), + }), + }, + { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "single": { + Nesting: NestingSingle, + Block: Block{ + Attributes: map[string]*Attribute{ + "str": {Type: cty.String, Required: true}, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "single": cty.NullVal(cty.Object(map[string]cty.Type{ + "str": cty.String, + })), + }), + }, + { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "group": { + Nesting: NestingGroup, + Block: Block{ + Attributes: map[string]*Attribute{ + "str": {Type: cty.String, Required: true}, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "group": cty.ObjectVal(map[string]cty.Value{ + "str": cty.NullVal(cty.String), + }), + }), + }, + { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "list": { + Nesting: NestingList, + Block: Block{ + Attributes: map[string]*Attribute{ + "str": {Type: cty.String, Required: true}, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "list": cty.ListValEmpty(cty.Object(map[string]cty.Type{ + "str": cty.String, + })), + }), + }, + { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "list_dynamic": { + Nesting: NestingList, + Block: Block{ + Attributes: map[string]*Attribute{ + "str": {Type: cty.DynamicPseudoType, Required: true}, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "list_dynamic": cty.EmptyTupleVal, + }), + }, + { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "map": { + Nesting: NestingMap, + Block: Block{ + Attributes: map[string]*Attribute{ + "str": {Type: cty.String, Required: true}, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "map": cty.MapValEmpty(cty.Object(map[string]cty.Type{ + "str": cty.String, + })), + }), + }, + { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "map_dynamic": { + Nesting: NestingMap, + Block: Block{ + Attributes: map[string]*Attribute{ + "str": {Type: cty.DynamicPseudoType, Required: true}, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "map_dynamic": cty.EmptyObjectVal, + }), + }, + { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "set": { + Nesting: NestingSet, + Block: Block{ + Attributes: map[string]*Attribute{ + "str": {Type: cty.String, Required: true}, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "set": cty.SetValEmpty(cty.Object(map[string]cty.Type{ + "str": cty.String, + })), + }), + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%#v", test.Schema), func(t *testing.T) { + got := test.Schema.EmptyValue() + if !test.Want.RawEquals(got) { + t.Errorf("wrong result\nschema: %s\ngot: %s\nwant: %s", spew.Sdump(test.Schema), dump.Value(got), dump.Value(test.Want)) + } + + // The empty value must always conform to the implied type of + // the schema. + wantTy := test.Schema.ImpliedType() + gotTy := got.Type() + if errs := gotTy.TestConformance(wantTy); len(errs) > 0 { + t.Errorf("empty value has incorrect type\ngot: %#v\nwant: %#v\nerrors: %s", gotTy, wantTy, spew.Sdump(errs)) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/implied_type.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/implied_type.go index 51f51ceb..edc9dadc 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/implied_type.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/implied_type.go @@ -1,21 +1,68 @@ package configschema import ( - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/zclconf/go-cty/cty" + "github.com/hashicorp/go-cty/cty" ) // ImpliedType returns the cty.Type that would result from decoding a // configuration block using the receiving block schema. // // ImpliedType always returns a result, even if the given schema is -// inconsistent. Code that creates configschema.Block objects should be -// tested using the InternalValidate method to detect any inconsistencies -// that would cause this method to fall back on defaults and assumptions. +// inconsistent. func (b *Block) ImpliedType() cty.Type { if b == nil { return cty.EmptyObject } - return hcldec.ImpliedType(b.DecoderSpec()) + atys := make(map[string]cty.Type) + + for name, attrS := range b.Attributes { + atys[name] = attrS.Type + } + + for name, blockS := range b.BlockTypes { + if _, exists := atys[name]; exists { + panic("invalid schema, blocks and attributes cannot have the same name") + } + + childType := blockS.Block.ImpliedType() + + switch blockS.Nesting { + case NestingSingle, NestingGroup: + atys[name] = childType + case NestingList: + // We prefer to use a list where possible, since it makes our + // implied type more complete, but if there are any + // dynamically-typed attributes inside we must use a tuple + // instead, which means our type _constraint_ must be + // cty.DynamicPseudoType to allow the tuple type to be decided + // separately for each value. + if childType.HasDynamicTypes() { + atys[name] = cty.DynamicPseudoType + } else { + atys[name] = cty.List(childType) + } + case NestingSet: + if childType.HasDynamicTypes() { + panic("can't use cty.DynamicPseudoType inside a block type with NestingSet") + } + atys[name] = cty.Set(childType) + case NestingMap: + // We prefer to use a map where possible, since it makes our + // implied type more complete, but if there are any + // dynamically-typed attributes inside we must use an object + // instead, which means our type _constraint_ must be + // cty.DynamicPseudoType to allow the tuple type to be decided + // separately for each value. + if childType.HasDynamicTypes() { + atys[name] = cty.DynamicPseudoType + } else { + atys[name] = cty.Map(childType) + } + default: + panic("invalid nesting type") + } + } + + return cty.Object(atys) } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/implied_type_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/implied_type_test.go new file mode 100644 index 00000000..3f4fac52 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/implied_type_test.go @@ -0,0 +1,124 @@ +package configschema + +import ( + "testing" + + "github.com/hashicorp/go-cty/cty" +) + +func TestBlockImpliedType(t *testing.T) { + tests := map[string]struct { + Schema *Block + Want cty.Type + }{ + "nil": { + nil, + cty.EmptyObject, + }, + "empty": { + &Block{}, + cty.EmptyObject, + }, + "attributes": { + &Block{ + Attributes: map[string]*Attribute{ + "optional": { + Type: cty.String, + Optional: true, + }, + "required": { + Type: cty.Number, + Required: true, + }, + "computed": { + Type: cty.List(cty.Bool), + Computed: true, + }, + "optional_computed": { + Type: cty.Map(cty.Bool), + Optional: true, + }, + }, + }, + cty.Object(map[string]cty.Type{ + "optional": cty.String, + "required": cty.Number, + "computed": cty.List(cty.Bool), + "optional_computed": cty.Map(cty.Bool), + }), + }, + "blocks": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "single": { + Nesting: NestingSingle, + Block: Block{ + Attributes: map[string]*Attribute{ + "foo": { + Type: cty.DynamicPseudoType, + Required: true, + }, + }, + }, + }, + "list": { + Nesting: NestingList, + }, + "set": { + Nesting: NestingSet, + }, + "map": { + Nesting: NestingMap, + }, + }, + }, + cty.Object(map[string]cty.Type{ + "single": cty.Object(map[string]cty.Type{ + "foo": cty.DynamicPseudoType, + }), + "list": cty.List(cty.EmptyObject), + "set": cty.Set(cty.EmptyObject), + "map": cty.Map(cty.EmptyObject), + }), + }, + "deep block nesting": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "single": { + Nesting: NestingSingle, + Block: Block{ + BlockTypes: map[string]*NestedBlock{ + "list": { + Nesting: NestingList, + Block: Block{ + BlockTypes: map[string]*NestedBlock{ + "set": { + Nesting: NestingSet, + }, + }, + }, + }, + }, + }, + }, + }, + }, + cty.Object(map[string]cty.Type{ + "single": cty.Object(map[string]cty.Type{ + "list": cty.List(cty.Object(map[string]cty.Type{ + "set": cty.Set(cty.EmptyObject), + })), + }), + }), + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + got := test.Schema.ImpliedType() + if !got.Equals(test.Want) { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/internal_validate.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/internal_validate.go deleted file mode 100644 index ebf1abba..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/internal_validate.go +++ /dev/null @@ -1,105 +0,0 @@ -package configschema - -import ( - "fmt" - "regexp" - - "github.com/zclconf/go-cty/cty" - - multierror "github.com/hashicorp/go-multierror" -) - -var validName = regexp.MustCompile(`^[a-z0-9_]+$`) - -// InternalValidate returns an error if the receiving block and its child -// schema definitions have any consistencies with the documented rules for -// valid schema. -// -// This is intended to be used within unit tests to detect when a given -// schema is invalid. -func (b *Block) InternalValidate() error { - if b == nil { - return fmt.Errorf("top-level block schema is nil") - } - return b.internalValidate("", nil) - -} - -func (b *Block) internalValidate(prefix string, err error) error { - for name, attrS := range b.Attributes { - if attrS == nil { - err = multierror.Append(err, fmt.Errorf("%s%s: attribute schema is nil", prefix, name)) - continue - } - if !validName.MatchString(name) { - err = multierror.Append(err, fmt.Errorf("%s%s: name may contain only lowercase letters, digits and underscores", prefix, name)) - } - if attrS.Optional == false && attrS.Required == false && attrS.Computed == false { - err = multierror.Append(err, fmt.Errorf("%s%s: must set Optional, Required or Computed", prefix, name)) - } - if attrS.Optional && attrS.Required { - err = multierror.Append(err, fmt.Errorf("%s%s: cannot set both Optional and Required", prefix, name)) - } - if attrS.Computed && attrS.Required { - err = multierror.Append(err, fmt.Errorf("%s%s: cannot set both Computed and Required", prefix, name)) - } - if attrS.Type == cty.NilType { - err = multierror.Append(err, fmt.Errorf("%s%s: Type must be set to something other than cty.NilType", prefix, name)) - } - } - - for name, blockS := range b.BlockTypes { - if blockS == nil { - err = multierror.Append(err, fmt.Errorf("%s%s: block schema is nil", prefix, name)) - continue - } - - if _, isAttr := b.Attributes[name]; isAttr { - err = multierror.Append(err, fmt.Errorf("%s%s: name defined as both attribute and child block type", prefix, name)) - } else if !validName.MatchString(name) { - err = multierror.Append(err, fmt.Errorf("%s%s: name may contain only lowercase letters, digits and underscores", prefix, name)) - } - - if blockS.MinItems < 0 || blockS.MaxItems < 0 { - err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must both be greater than zero", prefix, name)) - } - - switch blockS.Nesting { - case NestingSingle: - switch { - case blockS.MinItems != blockS.MaxItems: - err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must match in NestingSingle mode", prefix, name)) - case blockS.MinItems < 0 || blockS.MinItems > 1: - err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must be set to either 0 or 1 in NestingSingle mode", prefix, name)) - } - case NestingGroup: - if blockS.MinItems != 0 || blockS.MaxItems != 0 { - err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems cannot be used in NestingGroup mode", prefix, name)) - } - case NestingList, NestingSet: - if blockS.MinItems > blockS.MaxItems && blockS.MaxItems != 0 { - err = multierror.Append(err, fmt.Errorf("%s%s: MinItems must be less than or equal to MaxItems in %s mode", prefix, name, blockS.Nesting)) - } - if blockS.Nesting == NestingSet { - ety := blockS.Block.ImpliedType() - if ety.HasDynamicTypes() { - // This is not permitted because the HCL (cty) set implementation - // needs to know the exact type of set elements in order to - // properly hash them, and so can't support mixed types. - err = multierror.Append(err, fmt.Errorf("%s%s: NestingSet blocks may not contain attributes of cty.DynamicPseudoType", prefix, name)) - } - } - case NestingMap: - if blockS.MinItems != 0 || blockS.MaxItems != 0 { - err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must both be 0 in NestingMap mode", prefix, name)) - } - default: - err = multierror.Append(err, fmt.Errorf("%s%s: invalid nesting mode %s", prefix, name, blockS.Nesting)) - } - - subPrefix := prefix + name + "." - err = blockS.Block.internalValidate(subPrefix, err) - } - - return err -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/none_required.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/none_required.go deleted file mode 100644 index 0be3b8fa..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/none_required.go +++ /dev/null @@ -1,38 +0,0 @@ -package configschema - -// NoneRequired returns a deep copy of the receiver with any required -// attributes translated to optional. -func (b *Block) NoneRequired() *Block { - ret := &Block{} - - if b.Attributes != nil { - ret.Attributes = make(map[string]*Attribute, len(b.Attributes)) - } - for name, attrS := range b.Attributes { - ret.Attributes[name] = attrS.forceOptional() - } - - if b.BlockTypes != nil { - ret.BlockTypes = make(map[string]*NestedBlock, len(b.BlockTypes)) - } - for name, blockS := range b.BlockTypes { - ret.BlockTypes[name] = blockS.noneRequired() - } - - return ret -} - -func (b *NestedBlock) noneRequired() *NestedBlock { - ret := *b - ret.Block = *(ret.Block.NoneRequired()) - ret.MinItems = 0 - ret.MaxItems = 0 - return &ret -} - -func (a *Attribute) forceOptional() *Attribute { - ret := *a - ret.Optional = true - ret.Required = false - return &ret -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/schema.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/schema.go index f4702d36..6cddb9c6 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/schema.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/schema.go @@ -1,7 +1,18 @@ package configschema import ( - "github.com/zclconf/go-cty/cty" + "github.com/hashicorp/go-cty/cty" +) + +// StringKind represents the format a string is in. +type StringKind int + +const ( + // StringPlain indicates a string is plain-text and requires no processing for display. + StringPlain StringKind = iota + // StringMarkdown indicates a string is in markdown format and may + // require additional processing to display. + StringMarkdown ) // Block represents a configuration block. @@ -21,6 +32,15 @@ type Block struct { // BlockTypes describes any nested block types that may appear directly // inside the block. BlockTypes map[string]*NestedBlock + + // Description and DescriptionKind contain a user facing description of the block + // and the format of that string. + Description string + DescriptionKind StringKind + + // Deprecated indicates whether the block has been marked as deprecated in the + // provider and usage should be discouraged. + Deprecated bool } // Attribute represents a configuration attribute, within a block. @@ -32,7 +52,8 @@ type Attribute struct { // usage of the attribute. A description should be concise and use only // one or two sentences, leaving full definition to longer-form // documentation defined elsewhere. - Description string + Description string + DescriptionKind StringKind // Required, if set to true, specifies that an omitted or null value is // not permitted. @@ -55,6 +76,10 @@ type Attribute struct { // future to help Terraform mask sensitive information. (Terraform // currently achieves this in a limited sense via other mechanisms.) Sensitive bool + + // Deprecated indicates whether the attribute has been marked as deprecated in the + // provider and usage should be discouraged. + Deprecated bool } // NestedBlock represents the embedding of one block within another. diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/validate_traversal.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/validate_traversal.go deleted file mode 100644 index 446705ba..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema/validate_traversal.go +++ /dev/null @@ -1,173 +0,0 @@ -package configschema - -import ( - "fmt" - "sort" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/helper/didyoumean" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// StaticValidateTraversal checks whether the given traversal (which must be -// relative) refers to a construct in the receiving schema, returning error -// diagnostics if any problems are found. -// -// This method is "optimistic" in that it will not return errors for possible -// problems that cannot be detected statically. It is possible that an -// traversal which passed static validation will still fail when evaluated. -func (b *Block) StaticValidateTraversal(traversal hcl.Traversal) tfdiags.Diagnostics { - if !traversal.IsRelative() { - panic("StaticValidateTraversal on absolute traversal") - } - if len(traversal) == 0 { - return nil - } - - var diags tfdiags.Diagnostics - - next := traversal[0] - after := traversal[1:] - - var name string - switch step := next.(type) { - case hcl.TraverseAttr: - name = step.Name - case hcl.TraverseIndex: - // No other traversal step types are allowed directly at a block. - // If it looks like the user was trying to use index syntax to - // access an attribute then we'll produce a specialized message. - key := step.Key - if key.Type() == cty.String && key.IsKnown() && !key.IsNull() { - maybeName := key.AsString() - if hclsyntax.ValidIdentifier(maybeName) { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Invalid index operation`, - Detail: fmt.Sprintf(`Only attribute access is allowed here. Did you mean to access attribute %q using the dot operator?`, maybeName), - Subject: &step.SrcRange, - }) - return diags - } - } - // If it looks like some other kind of index then we'll use a generic error. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Invalid index operation`, - Detail: `Only attribute access is allowed here, using the dot operator.`, - Subject: &step.SrcRange, - }) - return diags - default: - // No other traversal types should appear in a normal valid traversal, - // but we'll handle this with a generic error anyway to be robust. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Invalid operation`, - Detail: `Only attribute access is allowed here, using the dot operator.`, - Subject: next.SourceRange().Ptr(), - }) - return diags - } - - if attrS, exists := b.Attributes[name]; exists { - // For attribute validation we will just apply the rest of the - // traversal to an unknown value of the attribute type and pass - // through HCL's own errors, since we don't want to replicate all of - // HCL's type checking rules here. - val := cty.UnknownVal(attrS.Type) - _, hclDiags := after.TraverseRel(val) - diags = diags.Append(hclDiags) - return diags - } - - if blockS, exists := b.BlockTypes[name]; exists { - moreDiags := blockS.staticValidateTraversal(name, after) - diags = diags.Append(moreDiags) - return diags - } - - // If we get here then the name isn't valid at all. We'll collect up - // all of the names that _are_ valid to use as suggestions. - var suggestions []string - for name := range b.Attributes { - suggestions = append(suggestions, name) - } - for name := range b.BlockTypes { - suggestions = append(suggestions, name) - } - sort.Strings(suggestions) - suggestion := didyoumean.NameSuggestion(name, suggestions) - if suggestion != "" { - suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) - } - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Unsupported attribute`, - Detail: fmt.Sprintf(`This object has no argument, nested block, or exported attribute named %q.%s`, name, suggestion), - Subject: next.SourceRange().Ptr(), - }) - - return diags -} - -func (b *NestedBlock) staticValidateTraversal(typeName string, traversal hcl.Traversal) tfdiags.Diagnostics { - if b.Nesting == NestingSingle || b.Nesting == NestingGroup { - // Single blocks are easy: just pass right through. - return b.Block.StaticValidateTraversal(traversal) - } - - if len(traversal) == 0 { - // It's always valid to access a nested block's attribute directly. - return nil - } - - var diags tfdiags.Diagnostics - next := traversal[0] - after := traversal[1:] - - switch b.Nesting { - - case NestingSet: - // Can't traverse into a set at all, since it does not have any keys - // to index with. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Cannot index a set value`, - Detail: fmt.Sprintf(`Block type %q is represented by a set of objects, and set elements do not have addressable keys. To find elements matching specific criteria, use a "for" expression with an "if" clause.`, typeName), - Subject: next.SourceRange().Ptr(), - }) - return diags - - case NestingList: - if _, ok := next.(hcl.TraverseIndex); ok { - moreDiags := b.Block.StaticValidateTraversal(after) - diags = diags.Append(moreDiags) - } else { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Invalid operation`, - Detail: fmt.Sprintf(`Block type %q is represented by a list of objects, so it must be indexed using a numeric key, like .%s[0].`, typeName, typeName), - Subject: next.SourceRange().Ptr(), - }) - } - return diags - - case NestingMap: - // Both attribute and index steps are valid for maps, so we'll just - // pass through here and let normal evaluation catch an - // incorrectly-typed index key later, if present. - moreDiags := b.Block.StaticValidateTraversal(after) - diags = diags.Append(moreDiags) - return diags - - default: - // Invalid nesting type is just ignored. It's checked by - // InternalValidate. (Note that we handled NestingSingle separately - // back at the start of this function.) - return nil - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/depends_on.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/depends_on.go deleted file mode 100644 index 036c2d6c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/depends_on.go +++ /dev/null @@ -1,23 +0,0 @@ -package configs - -import ( - "github.com/hashicorp/hcl/v2" -) - -func decodeDependsOn(attr *hcl.Attribute) ([]hcl.Traversal, hcl.Diagnostics) { - var ret []hcl.Traversal - exprs, diags := hcl.ExprList(attr.Expr) - - for _, expr := range exprs { - expr, shimDiags := shimTraversalInString(expr, false) - diags = append(diags, shimDiags...) - - traversal, travDiags := hcl.AbsTraversalForExpr(expr) - diags = append(diags, travDiags...) - if len(traversal) != 0 { - ret = append(ret, traversal) - } - } - - return ret, diags -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/doc.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/doc.go deleted file mode 100644 index f01eb79f..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -// Package configs contains types that represent Terraform configurations and -// the different elements thereof. -// -// The functionality in this package can be used for some static analyses of -// Terraform configurations, but this package generally exposes representations -// of the configuration source code rather than the result of evaluating these -// objects. The sibling package "lang" deals with evaluation of structures -// and expressions in the configuration. -// -// Due to its close relationship with HCL, this package makes frequent use -// of types from the HCL API, including raw HCL diagnostic messages. Such -// diagnostics can be converted into Terraform-flavored diagnostics, if needed, -// using functions in the sibling package tfdiags. -// -// The Parser type is the main entry-point into this package. The LoadConfigDir -// method can be used to load a single module directory, and then a full -// configuration (including any descendent modules) can be produced using -// the top-level BuildConfig method. -package configs diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/flatmap.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/flatmap.go index bb4228d9..e620e76a 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/flatmap.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/flatmap.go @@ -5,9 +5,8 @@ import ( "strconv" "strings" - "github.com/zclconf/go-cty/cty/convert" - - "github.com/zclconf/go-cty/cty" + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/go-cty/cty/convert" ) // FlatmapValueFromHCL2 converts a value from HCL2 (really, from the cty dynamic diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/flatmap_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/flatmap_test.go new file mode 100644 index 00000000..7659d0dd --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/flatmap_test.go @@ -0,0 +1,751 @@ +package hcl2shim + +import ( + "fmt" + "testing" + + "github.com/go-test/deep" + "github.com/hashicorp/go-cty/cty" +) + +func TestFlatmapValueFromHCL2(t *testing.T) { + tests := []struct { + Value cty.Value + Want map[string]string + }{ + { + cty.EmptyObjectVal, + map[string]string{}, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("hello"), + }), + map[string]string{ + "foo": "hello", + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.UnknownVal(cty.Bool), + }), + map[string]string{ + "foo": UnknownVariableValue, + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.NumberIntVal(12), + }), + map[string]string{ + "foo": "12", + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.True, + "bar": cty.False, + }), + map[string]string{ + "foo": "true", + "bar": "false", + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("hello"), + "bar": cty.StringVal("world"), + "baz": cty.StringVal("whelp"), + }), + map[string]string{ + "foo": "hello", + "bar": "world", + "baz": "whelp", + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListValEmpty(cty.String), + }), + map[string]string{ + "foo.#": "0", + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.UnknownVal(cty.List(cty.String)), + }), + map[string]string{ + "foo.#": UnknownVariableValue, + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{ + cty.StringVal("hello"), + }), + }), + map[string]string{ + "foo.#": "1", + "foo.0": "hello", + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{ + cty.StringVal("hello"), + cty.StringVal("world"), + }), + }), + map[string]string{ + "foo.#": "2", + "foo.0": "hello", + "foo.1": "world", + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.MapVal(map[string]cty.Value{ + "hello": cty.NumberIntVal(12), + "hello.world": cty.NumberIntVal(10), + }), + }), + map[string]string{ + "foo.%": "2", + "foo.hello": "12", + "foo.hello.world": "10", + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.UnknownVal(cty.Map(cty.String)), + }), + map[string]string{ + "foo.%": UnknownVariableValue, + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.MapVal(map[string]cty.Value{ + "hello": cty.NumberIntVal(12), + "hello.world": cty.NumberIntVal(10), + }), + }), + map[string]string{ + "foo.%": "2", + "foo.hello": "12", + "foo.hello.world": "10", + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.SetVal([]cty.Value{ + cty.StringVal("hello"), + cty.StringVal("world"), + }), + }), + map[string]string{ + "foo.#": "2", + "foo.0": "hello", + "foo.1": "world", + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.UnknownVal(cty.Set(cty.Number)), + }), + map[string]string{ + "foo.#": UnknownVariableValue, + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("hello"), + "baz": cty.StringVal("world"), + }), + cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("bloo"), + "baz": cty.StringVal("blaa"), + }), + }), + }), + map[string]string{ + "foo.#": "2", + "foo.0.bar": "hello", + "foo.0.baz": "world", + "foo.1.bar": "bloo", + "foo.1.baz": "blaa", + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("hello"), + "baz": cty.ListVal([]cty.Value{ + cty.True, + cty.True, + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("bloo"), + "baz": cty.ListVal([]cty.Value{ + cty.False, + cty.True, + }), + }), + }), + }), + map[string]string{ + "foo.#": "2", + "foo.0.bar": "hello", + "foo.0.baz.#": "2", + "foo.0.baz.0": "true", + "foo.0.baz.1": "true", + "foo.1.bar": "bloo", + "foo.1.baz.#": "2", + "foo.1.baz.0": "false", + "foo.1.baz.1": "true", + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{ + cty.UnknownVal(cty.Object(map[string]cty.Type{ + "bar": cty.String, + "baz": cty.List(cty.Bool), + "bap": cty.Map(cty.Number), + })), + }), + }), + map[string]string{ + "foo.#": "1", + "foo.0.bar": UnknownVariableValue, + "foo.0.baz.#": UnknownVariableValue, + "foo.0.bap.%": UnknownVariableValue, + }, + }, + { + cty.NullVal(cty.Object(map[string]cty.Type{ + "foo": cty.Set(cty.Object(map[string]cty.Type{ + "bar": cty.String, + })), + })), + nil, + }, + } + + for _, test := range tests { + t.Run(test.Value.GoString(), func(t *testing.T) { + got := FlatmapValueFromHCL2(test.Value) + + for _, problem := range deep.Equal(got, test.Want) { + t.Error(problem) + } + }) + } +} + +func TestFlatmapValueFromHCL2FromFlatmap(t *testing.T) { + tests := []struct { + Name string + Map map[string]string + Type cty.Type + }{ + { + "empty flatmap with collections", + map[string]string{}, + cty.Object(map[string]cty.Type{ + "foo": cty.Map(cty.String), + "bar": cty.Set(cty.String), + }), + }, + { + "nil flatmap with collections", + nil, + cty.Object(map[string]cty.Type{ + "foo": cty.Map(cty.String), + "bar": cty.Set(cty.String), + }), + }, + { + "empty flatmap with nested collections", + map[string]string{}, + cty.Object(map[string]cty.Type{ + "foo": cty.Object( + map[string]cty.Type{ + "baz": cty.Map(cty.String), + }, + ), + "bar": cty.Set(cty.String), + }), + }, + { + "partial flatmap with nested collections", + map[string]string{ + "foo.baz.%": "1", + "foo.baz.key": "val", + }, + cty.Object(map[string]cty.Type{ + "foo": cty.Object( + map[string]cty.Type{ + "baz": cty.Map(cty.String), + "biz": cty.Map(cty.String), + }, + ), + "bar": cty.Set(cty.String), + }), + }, + } + + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + val, err := HCL2ValueFromFlatmap(test.Map, test.Type) + if err != nil { + t.Fatal(err) + } + + got := FlatmapValueFromHCL2(val) + + for _, problem := range deep.Equal(got, test.Map) { + t.Error(problem) + } + }) + } +} +func TestHCL2ValueFromFlatmap(t *testing.T) { + tests := []struct { + Flatmap map[string]string + Type cty.Type + Want cty.Value + WantErr string + }{ + { + Flatmap: map[string]string{}, + Type: cty.EmptyObject, + Want: cty.EmptyObjectVal, + }, + { + Flatmap: map[string]string{ + "ignored": "foo", + }, + Type: cty.EmptyObject, + Want: cty.EmptyObjectVal, + }, + { + Flatmap: map[string]string{ + "foo": "blah", + "bar": "true", + "baz": "12.5", + "unk": UnknownVariableValue, + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.String, + "bar": cty.Bool, + "baz": cty.Number, + "unk": cty.Bool, + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("blah"), + "bar": cty.True, + "baz": cty.NumberFloatVal(12.5), + "unk": cty.UnknownVal(cty.Bool), + }), + }, + { + Flatmap: map[string]string{ + "foo.#": "0", + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.List(cty.String), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListValEmpty(cty.String), + }), + }, + { + Flatmap: map[string]string{ + "foo.#": UnknownVariableValue, + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.List(cty.String), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.UnknownVal(cty.List(cty.String)), + }), + }, + { + Flatmap: map[string]string{ + "foo.#": "1", + "foo.0": "hello", + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.List(cty.String), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{ + cty.StringVal("hello"), + }), + }), + }, + { + Flatmap: map[string]string{ + "foo.#": "2", + "foo.0": "true", + "foo.1": "false", + "foo.2": "ignored", // (because the count is 2, so this is out of range) + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.List(cty.Bool), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{ + cty.True, + cty.False, + }), + }), + }, + { + Flatmap: map[string]string{ + "foo.#": "2", + "foo.0": "hello", + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Tuple([]cty.Type{ + cty.String, + cty.Bool, + }), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.TupleVal([]cty.Value{ + cty.StringVal("hello"), + cty.NullVal(cty.Bool), + }), + }), + }, + { + Flatmap: map[string]string{ + "foo.#": UnknownVariableValue, + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Tuple([]cty.Type{ + cty.String, + cty.Bool, + }), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.UnknownVal(cty.Tuple([]cty.Type{ + cty.String, + cty.Bool, + })), + }), + }, + { + Flatmap: map[string]string{ + "foo.#": "0", + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Set(cty.String), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.SetValEmpty(cty.String), + }), + }, + { + Flatmap: map[string]string{ + "foo.#": UnknownVariableValue, + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Set(cty.String), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.UnknownVal(cty.Set(cty.String)), + }), + }, + { + Flatmap: map[string]string{ + "foo.#": "1", + "foo.24534534": "hello", + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Set(cty.String), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.SetVal([]cty.Value{ + cty.StringVal("hello"), + }), + }), + }, + { + Flatmap: map[string]string{ + "foo.#": "1", + "foo.24534534": "true", + "foo.95645644": "true", + "foo.34533452": "false", + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Set(cty.Bool), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.SetVal([]cty.Value{ + cty.True, + cty.False, + }), + }), + }, + { + Flatmap: map[string]string{ + "foo.%": "0", + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Map(cty.String), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.MapValEmpty(cty.String), + }), + }, + { + Flatmap: map[string]string{ + "foo.%": "2", + "foo.baz": "true", + "foo.bar.baz": "false", + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Map(cty.Bool), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.MapVal(map[string]cty.Value{ + "baz": cty.True, + "bar.baz": cty.False, + }), + }), + }, + { + Flatmap: map[string]string{ + "foo.%": UnknownVariableValue, + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Map(cty.Bool), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.UnknownVal(cty.Map(cty.Bool)), + }), + }, + { + Flatmap: map[string]string{ + "foo.#": "2", + "foo.0.bar": "hello", + "foo.0.baz": "1", + "foo.1.bar": "world", + "foo.1.baz": "false", + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.List(cty.Object(map[string]cty.Type{ + "bar": cty.String, + "baz": cty.Bool, + })), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("hello"), + "baz": cty.True, + }), + cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("world"), + "baz": cty.False, + }), + }), + }), + }, + { + Flatmap: map[string]string{ + "foo.#": "2", + "foo.34534534.bar": "hello", + "foo.34534534.baz": "1", + "foo.93453345.bar": "world", + "foo.93453345.baz": "false", + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Set(cty.Object(map[string]cty.Type{ + "bar": cty.String, + "baz": cty.Bool, + })), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("hello"), + "baz": cty.True, + }), + cty.ObjectVal(map[string]cty.Value{ + "bar": cty.StringVal("world"), + "baz": cty.False, + }), + }), + }), + }, + { + Flatmap: map[string]string{ + "foo.#": "not-valid", + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.List(cty.String), + }), + WantErr: `invalid count value for "foo." in state: strconv.Atoi: parsing "not-valid": invalid syntax`, + }, + { + Flatmap: nil, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Set(cty.Object(map[string]cty.Type{ + "bar": cty.String, + })), + }), + Want: cty.NullVal(cty.Object(map[string]cty.Type{ + "foo": cty.Set(cty.Object(map[string]cty.Type{ + "bar": cty.String, + })), + })), + }, + { + Flatmap: map[string]string{ + "foo.#": "2", + "foo.0.%": "2", + "foo.0.a": "a", + "foo.0.b": "b", + "foo.1.%": "1", + "foo.1.a": "a", + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.List(cty.Map(cty.String)), + }), + + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{ + cty.MapVal(map[string]cty.Value{ + "a": cty.StringVal("a"), + "b": cty.StringVal("b"), + }), + cty.MapVal(map[string]cty.Value{ + "a": cty.StringVal("a"), + }), + }), + }), + }, + { + Flatmap: map[string]string{ + "single.#": "1", + "single.~1.value": "a", + "single.~1.optional": UnknownVariableValue, + "two.#": "2", + "two.~2381914684.value": "a", + "two.~2381914684.optional": UnknownVariableValue, + "two.~2798940671.value": "b", + "two.~2798940671.optional": UnknownVariableValue, + }, + Type: cty.Object(map[string]cty.Type{ + "single": cty.Set( + cty.Object(map[string]cty.Type{ + "value": cty.String, + "optional": cty.String, + }), + ), + "two": cty.Set( + cty.Object(map[string]cty.Type{ + "optional": cty.String, + "value": cty.String, + }), + ), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "single": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "value": cty.StringVal("a"), + "optional": cty.UnknownVal(cty.String), + }), + }), + "two": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "value": cty.StringVal("a"), + "optional": cty.UnknownVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "value": cty.StringVal("b"), + "optional": cty.UnknownVal(cty.String), + }), + }), + }), + }, + { + Flatmap: map[string]string{ + "foo.#": "1", + }, + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Set(cty.Object(map[string]cty.Type{ + "bar": cty.String, + })), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "bar": cty.NullVal(cty.String), + }), + }), + }), + }, + { + Flatmap: map[string]string{ + "multi.#": "1", + "multi.2.set.#": "1", + "multi.2.set.3.required": "val", + }, + Type: cty.Object(map[string]cty.Type{ + "multi": cty.Set(cty.Object(map[string]cty.Type{ + "set": cty.Set(cty.Object(map[string]cty.Type{ + "required": cty.String, + })), + })), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "multi": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "set": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "required": cty.StringVal("val"), + }), + }), + }), + }), + }), + }, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("%d %#v as %#v", i, test.Flatmap, test.Type), func(t *testing.T) { + got, err := HCL2ValueFromFlatmap(test.Flatmap, test.Type) + + if test.WantErr != "" { + if err == nil { + t.Fatalf("succeeded; want error: %s", test.WantErr) + } + if got, want := err.Error(), test.WantErr; got != want { + t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) + } + if got == cty.NilVal { + t.Fatalf("result is cty.NilVal; want valid placeholder value") + } + return + } else { + if err != nil { + t.Fatalf("unexpected error: %s", err.Error()) + } + } + + if !got.RawEquals(test.Want) { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/paths.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/paths.go index 3403c026..e557845a 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/paths.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/paths.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/zclconf/go-cty/cty" + "github.com/hashicorp/go-cty/cty" ) // RequiresReplace takes a list of flatmapped paths from a diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/paths_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/paths_test.go new file mode 100644 index 00000000..c1b8cf3e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/paths_test.go @@ -0,0 +1,406 @@ +package hcl2shim + +import ( + "fmt" + "reflect" + "strconv" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/go-cty/cty" +) + +var ( + ignoreUnexported = cmpopts.IgnoreUnexported(cty.GetAttrStep{}, cty.IndexStep{}) + valueComparer = cmp.Comparer(cty.Value.RawEquals) +) + +func TestPathFromFlatmap(t *testing.T) { + tests := []struct { + Flatmap string + Type cty.Type + Want cty.Path + WantErr string + }{ + { + Flatmap: "", + Type: cty.EmptyObject, + Want: nil, + }, + { + Flatmap: "attr", + Type: cty.EmptyObject, + Want: nil, + WantErr: `attribute "attr" not found`, + }, + { + Flatmap: "foo", + Type: cty.Object(map[string]cty.Type{ + "foo": cty.String, + }), + Want: cty.Path{ + cty.GetAttrStep{Name: "foo"}, + }, + }, + { + Flatmap: "foo.#", + Type: cty.Object(map[string]cty.Type{ + "foo": cty.List(cty.String), + }), + Want: cty.Path{ + cty.GetAttrStep{Name: "foo"}, + }, + }, + { + Flatmap: "foo.1", + Type: cty.Object(map[string]cty.Type{ + "foo": cty.List(cty.String), + }), + Want: cty.Path{ + cty.GetAttrStep{Name: "foo"}, + cty.IndexStep{Key: cty.NumberIntVal(1)}, + }, + }, + { + Flatmap: "foo.1", + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Tuple([]cty.Type{ + cty.String, + cty.Bool, + }), + }), + Want: cty.Path{ + cty.GetAttrStep{Name: "foo"}, + cty.IndexStep{Key: cty.NumberIntVal(1)}, + }, + }, + { + // a set index returns the set itself, since this being applied to + // a diff and the set is changing. + Flatmap: "foo.24534534", + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Set(cty.String), + }), + Want: cty.Path{ + cty.GetAttrStep{Name: "foo"}, + }, + }, + { + Flatmap: "foo.%", + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Map(cty.String), + }), + Want: cty.Path{ + cty.GetAttrStep{Name: "foo"}, + }, + }, + { + Flatmap: "foo.baz", + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Map(cty.Bool), + }), + Want: cty.Path{ + cty.GetAttrStep{Name: "foo"}, + cty.IndexStep{Key: cty.StringVal("baz")}, + }, + }, + { + Flatmap: "foo.bar.baz", + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Map( + cty.Map(cty.Bool), + ), + }), + Want: cty.Path{ + cty.GetAttrStep{Name: "foo"}, + cty.IndexStep{Key: cty.StringVal("bar")}, + cty.IndexStep{Key: cty.StringVal("baz")}, + }, + }, + { + Flatmap: "foo.bar.baz", + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Map( + cty.Object(map[string]cty.Type{ + "baz": cty.String, + }), + ), + }), + Want: cty.Path{ + cty.GetAttrStep{Name: "foo"}, + cty.IndexStep{Key: cty.StringVal("bar")}, + cty.GetAttrStep{Name: "baz"}, + }, + }, + { + Flatmap: "foo.0.bar", + Type: cty.Object(map[string]cty.Type{ + "foo": cty.List(cty.Object(map[string]cty.Type{ + "bar": cty.String, + "baz": cty.Bool, + })), + }), + Want: cty.Path{ + cty.GetAttrStep{Name: "foo"}, + cty.IndexStep{Key: cty.NumberIntVal(0)}, + cty.GetAttrStep{Name: "bar"}, + }, + }, + { + Flatmap: "foo.34534534.baz", + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Set(cty.Object(map[string]cty.Type{ + "bar": cty.String, + "baz": cty.Bool, + })), + }), + Want: cty.Path{ + cty.GetAttrStep{Name: "foo"}, + }, + }, + { + Flatmap: "foo.bar.bang", + Type: cty.Object(map[string]cty.Type{ + "foo": cty.String, + }), + WantErr: `invalid step "bar.bang"`, + }, + { + // there should not be any attribute names with dots + Flatmap: "foo.bar.bang", + Type: cty.Object(map[string]cty.Type{ + "foo.bar": cty.Map(cty.String), + }), + WantErr: `attribute "foo" not found`, + }, + { + // We can only handle key names with dots if the map elements are a + // primitive type. + Flatmap: "foo.bar.bop", + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Map(cty.String), + }), + Want: cty.Path{ + cty.GetAttrStep{Name: "foo"}, + cty.IndexStep{Key: cty.StringVal("bar.bop")}, + }, + }, + { + Flatmap: "foo.bar.0.baz", + Type: cty.Object(map[string]cty.Type{ + "foo": cty.Map( + cty.List( + cty.Map(cty.String), + ), + ), + }), + Want: cty.Path{ + cty.GetAttrStep{Name: "foo"}, + cty.IndexStep{Key: cty.StringVal("bar")}, + cty.IndexStep{Key: cty.NumberIntVal(0)}, + cty.IndexStep{Key: cty.StringVal("baz")}, + }, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%s as %#v", test.Flatmap, test.Type), func(t *testing.T) { + got, err := requiresReplacePath(test.Flatmap, test.Type) + + if test.WantErr != "" { + if err == nil { + t.Fatalf("succeeded; want error: %s", test.WantErr) + } + if got, want := err.Error(), test.WantErr; !strings.Contains(got, want) { + t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) + } + return + } else { + if err != nil { + t.Fatalf("unexpected error: %s", err.Error()) + } + } + + if !reflect.DeepEqual(got, test.Want) { + t.Fatalf("incorrect path\ngot: %#v\nwant: %#v\n", got, test.Want) + } + }) + } +} + +func TestRequiresReplace(t *testing.T) { + for _, tc := range []struct { + name string + attrs []string + expected []cty.Path + ty cty.Type + }{ + { + name: "basic", + attrs: []string{ + "foo", + }, + ty: cty.Object(map[string]cty.Type{ + "foo": cty.String, + }), + expected: []cty.Path{ + {cty.GetAttrStep{Name: "foo"}}, + }, + }, + { + name: "two", + attrs: []string{ + "foo", + "bar", + }, + ty: cty.Object(map[string]cty.Type{ + "foo": cty.String, + "bar": cty.String, + }), + expected: []cty.Path{ + {cty.GetAttrStep{Name: "foo"}}, + {cty.GetAttrStep{Name: "bar"}}, + }, + }, + { + name: "nested object", + attrs: []string{ + "foo.bar", + }, + ty: cty.Object(map[string]cty.Type{ + "foo": cty.Object(map[string]cty.Type{ + "bar": cty.String, + }), + }), + expected: []cty.Path{ + {cty.GetAttrStep{Name: "foo"}, cty.GetAttrStep{Name: "bar"}}, + }, + }, + { + name: "nested objects", + attrs: []string{ + "foo.bar.baz", + }, + ty: cty.Object(map[string]cty.Type{ + "foo": cty.Object(map[string]cty.Type{ + "bar": cty.Object(map[string]cty.Type{ + "baz": cty.String, + }), + }), + }), + expected: []cty.Path{ + {cty.GetAttrStep{Name: "foo"}, cty.GetAttrStep{Name: "bar"}, cty.GetAttrStep{Name: "baz"}}, + }, + }, + { + name: "nested map", + attrs: []string{ + "foo.%", + "foo.bar", + }, + ty: cty.Object(map[string]cty.Type{ + "foo": cty.Map(cty.String), + }), + expected: []cty.Path{ + {cty.GetAttrStep{Name: "foo"}}, + }, + }, + { + name: "nested list", + attrs: []string{ + "foo.#", + "foo.1", + }, + ty: cty.Object(map[string]cty.Type{ + "foo": cty.Map(cty.String), + }), + expected: []cty.Path{ + {cty.GetAttrStep{Name: "foo"}}, + }, + }, + { + name: "object in map", + attrs: []string{ + "foo.bar.baz", + }, + ty: cty.Object(map[string]cty.Type{ + "foo": cty.Map(cty.Object( + map[string]cty.Type{ + "baz": cty.String, + }, + )), + }), + expected: []cty.Path{ + {cty.GetAttrStep{Name: "foo"}, cty.IndexStep{Key: cty.StringVal("bar")}, cty.GetAttrStep{Name: "baz"}}, + }, + }, + { + name: "object in list", + attrs: []string{ + "foo.1.baz", + }, + ty: cty.Object(map[string]cty.Type{ + "foo": cty.List(cty.Object( + map[string]cty.Type{ + "baz": cty.String, + }, + )), + }), + expected: []cty.Path{ + {cty.GetAttrStep{Name: "foo"}, cty.IndexStep{Key: cty.NumberIntVal(1)}, cty.GetAttrStep{Name: "baz"}}, + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + rp, err := RequiresReplace(tc.attrs, tc.ty) + if err != nil { + t.Fatal(err) + } + if !cmp.Equal(tc.expected, rp, ignoreUnexported, valueComparer) { + t.Fatalf("\nexpected: %#v\ngot: %#v\n", tc.expected, rp) + } + }) + + } +} + +func TestFlatmapKeyFromPath(t *testing.T) { + for i, tc := range []struct { + path cty.Path + attr string + }{ + { + path: cty.Path{ + cty.GetAttrStep{Name: "force_new"}, + }, + attr: "force_new", + }, + { + path: cty.Path{ + cty.GetAttrStep{Name: "attr"}, + cty.IndexStep{Key: cty.NumberIntVal(0)}, + cty.GetAttrStep{Name: "force_new"}, + }, + attr: "attr.0.force_new", + }, + { + path: cty.Path{ + cty.GetAttrStep{Name: "attr"}, + cty.IndexStep{Key: cty.StringVal("key")}, + cty.GetAttrStep{Name: "obj_attr"}, + cty.IndexStep{Key: cty.NumberIntVal(0)}, + cty.GetAttrStep{Name: "force_new"}, + }, + attr: "attr.key.obj_attr.0.force_new", + }, + } { + t.Run(strconv.Itoa(i), func(t *testing.T) { + attr := FlatmapKeyFromPath(tc.path) + if attr != tc.attr { + t.Fatalf("expected:%q got:%q", tc.attr, attr) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/values.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/values.go index a074c749..91e91547 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/values.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/values.go @@ -4,9 +4,9 @@ import ( "fmt" "math/big" - "github.com/zclconf/go-cty/cty" + "github.com/hashicorp/go-cty/cty" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" ) // UnknownVariableValue is a sentinel value that can be used diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/values_equiv.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/values_equiv.go index 92f0213d..87638b4e 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/values_equiv.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/values_equiv.go @@ -1,7 +1,7 @@ package hcl2shim import ( - "github.com/zclconf/go-cty/cty" + "github.com/hashicorp/go-cty/cty" ) // ValuesSDKEquivalent returns true if both of the given values seem equivalent diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/values_equiv_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/values_equiv_test.go new file mode 100644 index 00000000..85bbbb76 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/values_equiv_test.go @@ -0,0 +1,429 @@ +package hcl2shim + +import ( + "fmt" + "math/big" + "testing" + + "github.com/hashicorp/go-cty/cty" +) + +func TestValuesSDKEquivalent(t *testing.T) { + piBig, _, err := big.ParseFloat("3.14159265358979323846264338327950288419716939937510582097494459", 10, 512, big.ToZero) + if err != nil { + t.Fatal(err) + } + pi64, _ := piBig.Float64() + + tests := []struct { + A, B cty.Value + Want bool + }{ + // Strings + { + cty.StringVal("hello"), + cty.StringVal("hello"), + true, + }, + { + cty.StringVal("hello"), + cty.StringVal("world"), + false, + }, + { + cty.StringVal("hello"), + cty.StringVal(""), + false, + }, + { + cty.NullVal(cty.String), + cty.StringVal(""), + true, + }, + + // Numbers + { + cty.NumberIntVal(1), + cty.NumberIntVal(1), + true, + }, + { + cty.NumberIntVal(1), + cty.NumberIntVal(2), + false, + }, + { + cty.NumberIntVal(1), + cty.Zero, + false, + }, + { + cty.NullVal(cty.Number), + cty.Zero, + true, + }, + { + cty.NumberVal(piBig), + cty.Zero, + false, + }, + { + cty.NumberFloatVal(pi64), + cty.Zero, + false, + }, + { + cty.NumberFloatVal(pi64), + cty.NumberVal(piBig), + true, + }, + + // Bools + { + cty.True, + cty.True, + true, + }, + { + cty.True, + cty.False, + false, + }, + { + cty.NullVal(cty.Bool), + cty.False, + true, + }, + + // Mixed primitives + { + cty.StringVal("hello"), + cty.False, + false, + }, + { + cty.StringVal(""), + cty.False, + true, + }, + { + cty.NumberIntVal(0), + cty.False, + true, + }, + { + cty.StringVal(""), + cty.NumberIntVal(0), + true, + }, + { + cty.NullVal(cty.Bool), + cty.NullVal(cty.Number), + true, + }, + { + cty.StringVal(""), + cty.NullVal(cty.Number), + true, + }, + + // Lists + { + cty.ListValEmpty(cty.String), + cty.ListValEmpty(cty.String), + true, + }, + { + cty.ListValEmpty(cty.String), + cty.NullVal(cty.List(cty.String)), + true, + }, + { + cty.ListVal([]cty.Value{cty.StringVal("hello")}), + cty.ListVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("hello")}), + false, + }, + { + cty.ListVal([]cty.Value{cty.StringVal("hello")}), + cty.ListValEmpty(cty.String), + false, + }, + { + cty.ListVal([]cty.Value{cty.StringVal("hello")}), + cty.ListVal([]cty.Value{cty.StringVal("hello")}), + true, + }, + { + cty.ListVal([]cty.Value{cty.StringVal("hello")}), + cty.ListVal([]cty.Value{cty.StringVal("world")}), + false, + }, + { + cty.ListVal([]cty.Value{cty.NullVal(cty.String)}), + cty.ListVal([]cty.Value{cty.StringVal("")}), + true, + }, + + // Tuples + { + cty.EmptyTupleVal, + cty.EmptyTupleVal, + true, + }, + { + cty.EmptyTupleVal, + cty.NullVal(cty.EmptyTuple), + true, + }, + { + cty.TupleVal([]cty.Value{cty.StringVal("hello")}), + cty.TupleVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("hello")}), + false, + }, + { + cty.TupleVal([]cty.Value{cty.StringVal("hello")}), + cty.EmptyTupleVal, + false, + }, + { + cty.TupleVal([]cty.Value{cty.StringVal("hello")}), + cty.TupleVal([]cty.Value{cty.StringVal("hello")}), + true, + }, + { + cty.TupleVal([]cty.Value{cty.StringVal("hello")}), + cty.TupleVal([]cty.Value{cty.StringVal("world")}), + false, + }, + { + cty.TupleVal([]cty.Value{cty.NullVal(cty.String)}), + cty.TupleVal([]cty.Value{cty.StringVal("")}), + true, + }, + + // Sets + { + cty.SetValEmpty(cty.String), + cty.SetValEmpty(cty.String), + true, + }, + { + cty.SetValEmpty(cty.String), + cty.NullVal(cty.Set(cty.String)), + true, + }, + { + cty.SetVal([]cty.Value{cty.StringVal("hello")}), + cty.SetValEmpty(cty.String), + false, + }, + { + cty.SetVal([]cty.Value{cty.StringVal("hello")}), + cty.SetVal([]cty.Value{cty.StringVal("hello")}), + true, + }, + { + cty.SetVal([]cty.Value{cty.StringVal("hello")}), + cty.SetVal([]cty.Value{cty.StringVal("world")}), + false, + }, + { + cty.SetVal([]cty.Value{cty.NullVal(cty.String)}), + cty.SetVal([]cty.Value{cty.StringVal("")}), + true, + }, + { + cty.SetVal([]cty.Value{ + cty.NullVal(cty.String), + cty.StringVal(""), + }), + cty.SetVal([]cty.Value{ + cty.NullVal(cty.String), + }), + false, // because the element count is different + }, + { + cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal(""), + "b": cty.StringVal(""), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + "b": cty.StringVal(""), + }), + }), + cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal(""), + "b": cty.StringVal(""), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal(""), + "b": cty.NullVal(cty.String), + }), + }), + true, + }, + { + cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("boop"), + "b": cty.StringVal(""), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + "b": cty.StringVal(""), + }), + }), + cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("beep"), + "b": cty.StringVal(""), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal(""), + "b": cty.NullVal(cty.String), + }), + }), + false, + }, + { + cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + "list": cty.ListValEmpty(cty.String), + "list_block": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "unused": cty.StringVal(""), + }), + }), + })}), + cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + "list": cty.ListValEmpty(cty.String), + "list_block": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "unused": cty.NullVal(cty.String), + }), + }), + })}), + true, + }, + + // Maps + { + cty.MapValEmpty(cty.String), + cty.MapValEmpty(cty.String), + true, + }, + { + cty.MapValEmpty(cty.String), + cty.NullVal(cty.Map(cty.String)), + true, + }, + { + cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), + cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("hello"), "hey": cty.StringVal("hello")}), + false, + }, + { + cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), + cty.MapValEmpty(cty.String), + false, + }, + { + cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), + cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), + true, + }, + { + cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), + cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("world")}), + false, + }, + { + cty.MapVal(map[string]cty.Value{"hi": cty.NullVal(cty.String)}), + cty.MapVal(map[string]cty.Value{"hi": cty.StringVal("")}), + true, + }, + + // Objects + { + cty.EmptyObjectVal, + cty.EmptyObjectVal, + true, + }, + { + cty.EmptyObjectVal, + cty.NullVal(cty.EmptyObject), + true, + }, + { + cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), + cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("hello"), "hey": cty.StringVal("hello")}), + false, + }, + { + cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), + cty.EmptyObjectVal, + false, + }, + { + cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), + cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), + true, + }, + { + cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("hello")}), + cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("world")}), + false, + }, + { + cty.ObjectVal(map[string]cty.Value{"hi": cty.NullVal(cty.String)}), + cty.ObjectVal(map[string]cty.Value{"hi": cty.StringVal("")}), + true, + }, + + // Unknown values + { + cty.UnknownVal(cty.String), + cty.UnknownVal(cty.String), + true, + }, + { + cty.StringVal("hello"), + cty.UnknownVal(cty.String), + false, + }, + { + cty.StringVal(""), + cty.UnknownVal(cty.String), + false, + }, + { + cty.NullVal(cty.String), + cty.UnknownVal(cty.String), + false, + }, + } + + run := func(t *testing.T, a, b cty.Value, want bool) { + got := ValuesSDKEquivalent(a, b) + + if got != want { + t.Errorf("wrong result\nfor: %#v ≈ %#v\ngot %#v, but want %#v", a, b, got, want) + } + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%#v ≈ %#v", test.A, test.B), func(t *testing.T) { + run(t, test.A, test.B, test.Want) + }) + // This function is symmetrical, so we'll also test in reverse so + // we don't need to manually copy all of the test cases. (But this does + // mean that one failure normally becomes two, of course!) + if !test.A.RawEquals(test.B) { + t.Run(fmt.Sprintf("%#v ≈ %#v", test.B, test.A), func(t *testing.T) { + run(t, test.B, test.A, test.Want) + }) + } + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/values_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/values_test.go new file mode 100644 index 00000000..ed14351d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim/values_test.go @@ -0,0 +1,416 @@ +package hcl2shim + +import ( + "fmt" + "reflect" + "testing" + + "github.com/hashicorp/go-cty/cty" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" +) + +func TestConfigValueFromHCL2Block(t *testing.T) { + tests := []struct { + Input cty.Value + Schema *configschema.Block + Want map[string]interface{} + }{ + { + cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("Ermintrude"), + "age": cty.NumberIntVal(19), + "address": cty.ObjectVal(map[string]cty.Value{ + "street": cty.ListVal([]cty.Value{cty.StringVal("421 Shoreham Loop")}), + "city": cty.StringVal("Fridgewater"), + "state": cty.StringVal("MA"), + "zip": cty.StringVal("91037"), + }), + }), + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "name": {Type: cty.String, Optional: true}, + "age": {Type: cty.Number, Optional: true}, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "address": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "street": {Type: cty.List(cty.String), Optional: true}, + "city": {Type: cty.String, Optional: true}, + "state": {Type: cty.String, Optional: true}, + "zip": {Type: cty.String, Optional: true}, + }, + }, + }, + }, + }, + map[string]interface{}{ + "name": "Ermintrude", + "age": int(19), + "address": map[string]interface{}{ + "street": []interface{}{"421 Shoreham Loop"}, + "city": "Fridgewater", + "state": "MA", + "zip": "91037", + }, + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("Ermintrude"), + "age": cty.NumberIntVal(19), + "address": cty.NullVal(cty.Object(map[string]cty.Type{ + "street": cty.List(cty.String), + "city": cty.String, + "state": cty.String, + "zip": cty.String, + })), + }), + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "name": {Type: cty.String, Optional: true}, + "age": {Type: cty.Number, Optional: true}, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "address": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "street": {Type: cty.List(cty.String), Optional: true}, + "city": {Type: cty.String, Optional: true}, + "state": {Type: cty.String, Optional: true}, + "zip": {Type: cty.String, Optional: true}, + }, + }, + }, + }, + }, + map[string]interface{}{ + "name": "Ermintrude", + "age": int(19), + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("Ermintrude"), + "age": cty.NumberIntVal(19), + "address": cty.ObjectVal(map[string]cty.Value{ + "street": cty.ListVal([]cty.Value{cty.StringVal("421 Shoreham Loop")}), + "city": cty.StringVal("Fridgewater"), + "state": cty.StringVal("MA"), + "zip": cty.NullVal(cty.String), // should be omitted altogether in result + }), + }), + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "name": {Type: cty.String, Optional: true}, + "age": {Type: cty.Number, Optional: true}, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "address": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "street": {Type: cty.List(cty.String), Optional: true}, + "city": {Type: cty.String, Optional: true}, + "state": {Type: cty.String, Optional: true}, + "zip": {Type: cty.String, Optional: true}, + }, + }, + }, + }, + }, + map[string]interface{}{ + "name": "Ermintrude", + "age": int(19), + "address": map[string]interface{}{ + "street": []interface{}{"421 Shoreham Loop"}, + "city": "Fridgewater", + "state": "MA", + }, + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "address": cty.ListVal([]cty.Value{cty.EmptyObjectVal}), + }), + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "address": { + Nesting: configschema.NestingList, + Block: configschema.Block{}, + }, + }, + }, + map[string]interface{}{ + "address": []interface{}{ + map[string]interface{}{}, + }, + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "address": cty.ListValEmpty(cty.EmptyObject), // should be omitted altogether in result + }), + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "address": { + Nesting: configschema.NestingList, + Block: configschema.Block{}, + }, + }, + }, + map[string]interface{}{}, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "address": cty.SetVal([]cty.Value{cty.EmptyObjectVal}), + }), + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "address": { + Nesting: configschema.NestingSet, + Block: configschema.Block{}, + }, + }, + }, + map[string]interface{}{ + "address": []interface{}{ + map[string]interface{}{}, + }, + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "address": cty.SetValEmpty(cty.EmptyObject), + }), + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "address": { + Nesting: configschema.NestingSet, + Block: configschema.Block{}, + }, + }, + }, + map[string]interface{}{}, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "address": cty.MapVal(map[string]cty.Value{"foo": cty.EmptyObjectVal}), + }), + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "address": { + Nesting: configschema.NestingMap, + Block: configschema.Block{}, + }, + }, + }, + map[string]interface{}{ + "address": map[string]interface{}{ + "foo": map[string]interface{}{}, + }, + }, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "address": cty.MapValEmpty(cty.EmptyObject), + }), + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "address": { + Nesting: configschema.NestingMap, + Block: configschema.Block{}, + }, + }, + }, + map[string]interface{}{}, + }, + { + cty.NullVal(cty.EmptyObject), + &configschema.Block{}, + nil, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%#v", test.Input), func(t *testing.T) { + got := ConfigValueFromHCL2Block(test.Input, test.Schema) + if !reflect.DeepEqual(got, test.Want) { + t.Errorf("wrong result\ninput: %#v\ngot: %#v\nwant: %#v", test.Input, got, test.Want) + } + }) + } +} + +func TestConfigValueFromHCL2(t *testing.T) { + tests := []struct { + Input cty.Value + Want interface{} + }{ + { + cty.True, + true, + }, + { + cty.False, + false, + }, + { + cty.NumberIntVal(12), + int(12), + }, + { + cty.NumberFloatVal(12.5), + float64(12.5), + }, + { + cty.StringVal("hello world"), + "hello world", + }, + { + cty.ObjectVal(map[string]cty.Value{ + "name": cty.StringVal("Ermintrude"), + "age": cty.NumberIntVal(19), + "address": cty.ObjectVal(map[string]cty.Value{ + "street": cty.ListVal([]cty.Value{cty.StringVal("421 Shoreham Loop")}), + "city": cty.StringVal("Fridgewater"), + "state": cty.StringVal("MA"), + "zip": cty.StringVal("91037"), + }), + }), + map[string]interface{}{ + "name": "Ermintrude", + "age": int(19), + "address": map[string]interface{}{ + "street": []interface{}{"421 Shoreham Loop"}, + "city": "Fridgewater", + "state": "MA", + "zip": "91037", + }, + }, + }, + { + cty.MapVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + "bar": cty.StringVal("baz"), + }), + map[string]interface{}{ + "foo": "bar", + "bar": "baz", + }, + }, + { + cty.TupleVal([]cty.Value{ + cty.StringVal("foo"), + cty.True, + }), + []interface{}{ + "foo", + true, + }, + }, + { + cty.NullVal(cty.String), + nil, + }, + { + cty.UnknownVal(cty.String), + UnknownVariableValue, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%#v", test.Input), func(t *testing.T) { + got := ConfigValueFromHCL2(test.Input) + if !reflect.DeepEqual(got, test.Want) { + t.Errorf("wrong result\ninput: %#v\ngot: %#v\nwant: %#v", test.Input, got, test.Want) + } + }) + } +} + +func TestHCL2ValueFromConfigValue(t *testing.T) { + tests := []struct { + Input interface{} + Want cty.Value + }{ + { + nil, + cty.NullVal(cty.DynamicPseudoType), + }, + { + UnknownVariableValue, + cty.DynamicVal, + }, + { + true, + cty.True, + }, + { + false, + cty.False, + }, + { + int(12), + cty.NumberIntVal(12), + }, + { + int(0), + cty.Zero, + }, + { + float64(12.5), + cty.NumberFloatVal(12.5), + }, + { + "hello world", + cty.StringVal("hello world"), + }, + { + "O\u0308", // decomposed letter + diacritic + cty.StringVal("\u00D6"), // NFC-normalized on entry into cty + }, + { + []interface{}{}, + cty.EmptyTupleVal, + }, + { + []interface{}(nil), + cty.EmptyTupleVal, + }, + { + []interface{}{"hello", "world"}, + cty.TupleVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world")}), + }, + { + map[string]interface{}{}, + cty.EmptyObjectVal, + }, + { + map[string]interface{}(nil), + cty.EmptyObjectVal, + }, + { + map[string]interface{}{ + "foo": "bar", + "bar": "baz", + }, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + "bar": cty.StringVal("baz"), + }), + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%#v", test.Input), func(t *testing.T) { + got := HCL2ValueFromConfigValue(test.Input) + if !got.RawEquals(test.Want) { + t.Errorf("wrong result\ninput: %#v\ngot: %#v\nwant: %#v", test.Input, got, test.Want) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/module.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/module.go deleted file mode 100644 index 78223c3b..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/module.go +++ /dev/null @@ -1,404 +0,0 @@ -package configs - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// Module is a container for a set of configuration constructs that are -// evaluated within a common namespace. -type Module struct { - // SourceDir is the filesystem directory that the module was loaded from. - // - // This is populated automatically only for configurations loaded with - // LoadConfigDir. If the parser is using a virtual filesystem then the - // path here will be in terms of that virtual filesystem. - - // Any other caller that constructs a module directly with NewModule may - // assign a suitable value to this attribute before using it for other - // purposes. It should be treated as immutable by all consumers of Module - // values. - SourceDir string - - CoreVersionConstraints []VersionConstraint - - Backend *Backend - ProviderConfigs map[string]*Provider - ProviderRequirements map[string][]VersionConstraint - - Variables map[string]*Variable - Locals map[string]*Local - Outputs map[string]*Output - - ModuleCalls map[string]*ModuleCall - - ManagedResources map[string]*Resource - DataResources map[string]*Resource -} - -// File describes the contents of a single configuration file. -// -// Individual files are not usually used alone, but rather combined together -// with other files (conventionally, those in the same directory) to produce -// a *Module, using NewModule. -// -// At the level of an individual file we represent directly the structural -// elements present in the file, without any attempt to detect conflicting -// declarations. A File object can therefore be used for some basic static -// analysis of individual elements, but must be built into a Module to detect -// duplicate declarations. -type File struct { - CoreVersionConstraints []VersionConstraint - - Backends []*Backend - ProviderConfigs []*Provider - ProviderRequirements []*ProviderRequirement - - Variables []*Variable - Locals []*Local - Outputs []*Output - - ModuleCalls []*ModuleCall - - ManagedResources []*Resource - DataResources []*Resource -} - -// NewModule takes a list of primary files and a list of override files and -// produces a *Module by combining the files together. -// -// If there are any conflicting declarations in the given files -- for example, -// if the same variable name is defined twice -- then the resulting module -// will be incomplete and error diagnostics will be returned. Careful static -// analysis of the returned Module is still possible in this case, but the -// module will probably not be semantically valid. -func NewModule(primaryFiles, overrideFiles []*File) (*Module, hcl.Diagnostics) { - var diags hcl.Diagnostics - mod := &Module{ - ProviderConfigs: map[string]*Provider{}, - ProviderRequirements: map[string][]VersionConstraint{}, - Variables: map[string]*Variable{}, - Locals: map[string]*Local{}, - Outputs: map[string]*Output{}, - ModuleCalls: map[string]*ModuleCall{}, - ManagedResources: map[string]*Resource{}, - DataResources: map[string]*Resource{}, - } - - for _, file := range primaryFiles { - fileDiags := mod.appendFile(file) - diags = append(diags, fileDiags...) - } - - for _, file := range overrideFiles { - fileDiags := mod.mergeFile(file) - diags = append(diags, fileDiags...) - } - - return mod, diags -} - -// ResourceByAddr returns the configuration for the resource with the given -// address, or nil if there is no such resource. -func (m *Module) ResourceByAddr(addr addrs.Resource) *Resource { - key := addr.String() - switch addr.Mode { - case addrs.ManagedResourceMode: - return m.ManagedResources[key] - case addrs.DataResourceMode: - return m.DataResources[key] - default: - return nil - } -} - -func (m *Module) appendFile(file *File) hcl.Diagnostics { - var diags hcl.Diagnostics - - for _, constraint := range file.CoreVersionConstraints { - // If there are any conflicting requirements then we'll catch them - // when we actually check these constraints. - m.CoreVersionConstraints = append(m.CoreVersionConstraints, constraint) - } - - for _, b := range file.Backends { - if m.Backend != nil { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Duplicate backend configuration", - Detail: fmt.Sprintf("A module may have only one backend configuration. The backend was previously configured at %s.", m.Backend.DeclRange), - Subject: &b.DeclRange, - }) - continue - } - m.Backend = b - } - - for _, pc := range file.ProviderConfigs { - key := pc.moduleUniqueKey() - if existing, exists := m.ProviderConfigs[key]; exists { - if existing.Alias == "" { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Duplicate provider configuration", - Detail: fmt.Sprintf("A default (non-aliased) provider configuration for %q was already given at %s. If multiple configurations are required, set the \"alias\" argument for alternative configurations.", existing.Name, existing.DeclRange), - Subject: &pc.DeclRange, - }) - } else { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Duplicate provider configuration", - Detail: fmt.Sprintf("A provider configuration for %q with alias %q was already given at %s. Each configuration for the same provider must have a distinct alias.", existing.Name, existing.Alias, existing.DeclRange), - Subject: &pc.DeclRange, - }) - } - continue - } - m.ProviderConfigs[key] = pc - } - - for _, reqd := range file.ProviderRequirements { - m.ProviderRequirements[reqd.Name] = append(m.ProviderRequirements[reqd.Name], reqd.Requirement) - } - - for _, v := range file.Variables { - if existing, exists := m.Variables[v.Name]; exists { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Duplicate variable declaration", - Detail: fmt.Sprintf("A variable named %q was already declared at %s. Variable names must be unique within a module.", existing.Name, existing.DeclRange), - Subject: &v.DeclRange, - }) - } - m.Variables[v.Name] = v - } - - for _, l := range file.Locals { - if existing, exists := m.Locals[l.Name]; exists { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Duplicate local value definition", - Detail: fmt.Sprintf("A local value named %q was already defined at %s. Local value names must be unique within a module.", existing.Name, existing.DeclRange), - Subject: &l.DeclRange, - }) - } - m.Locals[l.Name] = l - } - - for _, o := range file.Outputs { - if existing, exists := m.Outputs[o.Name]; exists { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Duplicate output definition", - Detail: fmt.Sprintf("An output named %q was already defined at %s. Output names must be unique within a module.", existing.Name, existing.DeclRange), - Subject: &o.DeclRange, - }) - } - m.Outputs[o.Name] = o - } - - for _, mc := range file.ModuleCalls { - if existing, exists := m.ModuleCalls[mc.Name]; exists { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Duplicate module call", - Detail: fmt.Sprintf("An module call named %q was already defined at %s. Module calls must have unique names within a module.", existing.Name, existing.DeclRange), - Subject: &mc.DeclRange, - }) - } - m.ModuleCalls[mc.Name] = mc - } - - for _, r := range file.ManagedResources { - key := r.moduleUniqueKey() - if existing, exists := m.ManagedResources[key]; exists { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: fmt.Sprintf("Duplicate resource %q configuration", existing.Type), - Detail: fmt.Sprintf("A %s resource named %q was already declared at %s. Resource names must be unique per type in each module.", existing.Type, existing.Name, existing.DeclRange), - Subject: &r.DeclRange, - }) - continue - } - m.ManagedResources[key] = r - } - - for _, r := range file.DataResources { - key := r.moduleUniqueKey() - if existing, exists := m.DataResources[key]; exists { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: fmt.Sprintf("Duplicate data %q configuration", existing.Type), - Detail: fmt.Sprintf("A %s data resource named %q was already declared at %s. Resource names must be unique per type in each module.", existing.Type, existing.Name, existing.DeclRange), - Subject: &r.DeclRange, - }) - continue - } - m.DataResources[key] = r - } - - return diags -} - -func (m *Module) mergeFile(file *File) hcl.Diagnostics { - var diags hcl.Diagnostics - - if len(file.CoreVersionConstraints) != 0 { - // This is a bit of a strange case for overriding since we normally - // would union together across multiple files anyway, but we'll - // allow it and have each override file clobber any existing list. - m.CoreVersionConstraints = nil - for _, constraint := range file.CoreVersionConstraints { - m.CoreVersionConstraints = append(m.CoreVersionConstraints, constraint) - } - } - - if len(file.Backends) != 0 { - switch len(file.Backends) { - case 1: - m.Backend = file.Backends[0] - default: - // An override file with multiple backends is still invalid, even - // though it can override backends from _other_ files. - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Duplicate backend configuration", - Detail: fmt.Sprintf("Each override file may have only one backend configuration. A backend was previously configured at %s.", file.Backends[0].DeclRange), - Subject: &file.Backends[1].DeclRange, - }) - } - } - - for _, pc := range file.ProviderConfigs { - key := pc.moduleUniqueKey() - existing, exists := m.ProviderConfigs[key] - if pc.Alias == "" { - // We allow overriding a non-existing _default_ provider configuration - // because the user model is that an absent provider configuration - // implies an empty provider configuration, which is what the user - // is therefore overriding here. - if exists { - mergeDiags := existing.merge(pc) - diags = append(diags, mergeDiags...) - } else { - m.ProviderConfigs[key] = pc - } - } else { - // For aliased providers, there must be a base configuration to - // override. This allows us to detect and report alias typos - // that might otherwise cause the override to not apply. - if !exists { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Missing base provider configuration for override", - Detail: fmt.Sprintf("There is no %s provider configuration with the alias %q. An override file can only override an aliased provider configuration that was already defined in a primary configuration file.", pc.Name, pc.Alias), - Subject: &pc.DeclRange, - }) - continue - } - mergeDiags := existing.merge(pc) - diags = append(diags, mergeDiags...) - } - } - - if len(file.ProviderRequirements) != 0 { - mergeProviderVersionConstraints(m.ProviderRequirements, file.ProviderRequirements) - } - - for _, v := range file.Variables { - existing, exists := m.Variables[v.Name] - if !exists { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Missing base variable declaration to override", - Detail: fmt.Sprintf("There is no variable named %q. An override file can only override a variable that was already declared in a primary configuration file.", v.Name), - Subject: &v.DeclRange, - }) - continue - } - mergeDiags := existing.merge(v) - diags = append(diags, mergeDiags...) - } - - for _, l := range file.Locals { - existing, exists := m.Locals[l.Name] - if !exists { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Missing base local value definition to override", - Detail: fmt.Sprintf("There is no local value named %q. An override file can only override a local value that was already defined in a primary configuration file.", l.Name), - Subject: &l.DeclRange, - }) - continue - } - mergeDiags := existing.merge(l) - diags = append(diags, mergeDiags...) - } - - for _, o := range file.Outputs { - existing, exists := m.Outputs[o.Name] - if !exists { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Missing base output definition to override", - Detail: fmt.Sprintf("There is no output named %q. An override file can only override an output that was already defined in a primary configuration file.", o.Name), - Subject: &o.DeclRange, - }) - continue - } - mergeDiags := existing.merge(o) - diags = append(diags, mergeDiags...) - } - - for _, mc := range file.ModuleCalls { - existing, exists := m.ModuleCalls[mc.Name] - if !exists { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Missing module call to override", - Detail: fmt.Sprintf("There is no module call named %q. An override file can only override a module call that was defined in a primary configuration file.", mc.Name), - Subject: &mc.DeclRange, - }) - continue - } - mergeDiags := existing.merge(mc) - diags = append(diags, mergeDiags...) - } - - for _, r := range file.ManagedResources { - key := r.moduleUniqueKey() - existing, exists := m.ManagedResources[key] - if !exists { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Missing resource to override", - Detail: fmt.Sprintf("There is no %s resource named %q. An override file can only override a resource block defined in a primary configuration file.", r.Type, r.Name), - Subject: &r.DeclRange, - }) - continue - } - mergeDiags := existing.merge(r) - diags = append(diags, mergeDiags...) - } - - for _, r := range file.DataResources { - key := r.moduleUniqueKey() - existing, exists := m.DataResources[key] - if !exists { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Missing data resource to override", - Detail: fmt.Sprintf("There is no %s data resource named %q. An override file can only override a data block defined in a primary configuration file.", r.Type, r.Name), - Subject: &r.DeclRange, - }) - continue - } - mergeDiags := existing.merge(r) - diags = append(diags, mergeDiags...) - } - - return diags -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/module_call.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/module_call.go deleted file mode 100644 index a484ffef..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/module_call.go +++ /dev/null @@ -1,188 +0,0 @@ -package configs - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/gohcl" - "github.com/hashicorp/hcl/v2/hclsyntax" -) - -// ModuleCall represents a "module" block in a module or file. -type ModuleCall struct { - Name string - - SourceAddr string - SourceAddrRange hcl.Range - SourceSet bool - - Config hcl.Body - - Version VersionConstraint - - Count hcl.Expression - ForEach hcl.Expression - - Providers []PassedProviderConfig - - DependsOn []hcl.Traversal - - DeclRange hcl.Range -} - -func decodeModuleBlock(block *hcl.Block, override bool) (*ModuleCall, hcl.Diagnostics) { - mc := &ModuleCall{ - Name: block.Labels[0], - DeclRange: block.DefRange, - } - - schema := moduleBlockSchema - if override { - schema = schemaForOverrides(schema) - } - - content, remain, diags := block.Body.PartialContent(schema) - mc.Config = remain - - if !hclsyntax.ValidIdentifier(mc.Name) { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid module instance name", - Detail: badIdentifierDetail, - Subject: &block.LabelRanges[0], - }) - } - - if attr, exists := content.Attributes["source"]; exists { - valDiags := gohcl.DecodeExpression(attr.Expr, nil, &mc.SourceAddr) - diags = append(diags, valDiags...) - mc.SourceAddrRange = attr.Expr.Range() - mc.SourceSet = true - } - - if attr, exists := content.Attributes["version"]; exists { - var versionDiags hcl.Diagnostics - mc.Version, versionDiags = decodeVersionConstraint(attr) - diags = append(diags, versionDiags...) - } - - if attr, exists := content.Attributes["count"]; exists { - mc.Count = attr.Expr - - // We currently parse this, but don't yet do anything with it. - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Reserved argument name in module block", - Detail: fmt.Sprintf("The name %q is reserved for use in a future version of Terraform.", attr.Name), - Subject: &attr.NameRange, - }) - } - - if attr, exists := content.Attributes["for_each"]; exists { - mc.ForEach = attr.Expr - - // We currently parse this, but don't yet do anything with it. - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Reserved argument name in module block", - Detail: fmt.Sprintf("The name %q is reserved for use in a future version of Terraform.", attr.Name), - Subject: &attr.NameRange, - }) - } - - if attr, exists := content.Attributes["depends_on"]; exists { - deps, depsDiags := decodeDependsOn(attr) - diags = append(diags, depsDiags...) - mc.DependsOn = append(mc.DependsOn, deps...) - - // We currently parse this, but don't yet do anything with it. - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Reserved argument name in module block", - Detail: fmt.Sprintf("The name %q is reserved for use in a future version of Terraform.", attr.Name), - Subject: &attr.NameRange, - }) - } - - if attr, exists := content.Attributes["providers"]; exists { - seen := make(map[string]hcl.Range) - pairs, pDiags := hcl.ExprMap(attr.Expr) - diags = append(diags, pDiags...) - for _, pair := range pairs { - key, keyDiags := decodeProviderConfigRef(pair.Key, "providers") - diags = append(diags, keyDiags...) - value, valueDiags := decodeProviderConfigRef(pair.Value, "providers") - diags = append(diags, valueDiags...) - if keyDiags.HasErrors() || valueDiags.HasErrors() { - continue - } - - matchKey := key.String() - if prev, exists := seen[matchKey]; exists { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Duplicate provider address", - Detail: fmt.Sprintf("A provider configuration was already passed to %s at %s. Each child provider configuration can be assigned only once.", matchKey, prev), - Subject: pair.Value.Range().Ptr(), - }) - continue - } - - rng := hcl.RangeBetween(pair.Key.Range(), pair.Value.Range()) - seen[matchKey] = rng - mc.Providers = append(mc.Providers, PassedProviderConfig{ - InChild: key, - InParent: value, - }) - } - } - - // Reserved block types (all of them) - for _, block := range content.Blocks { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Reserved block type name in module block", - Detail: fmt.Sprintf("The block type name %q is reserved for use by Terraform in a future version.", block.Type), - Subject: &block.TypeRange, - }) - } - - return mc, diags -} - -// PassedProviderConfig represents a provider config explicitly passed down to -// a child module, possibly giving it a new local address in the process. -type PassedProviderConfig struct { - InChild *ProviderConfigRef - InParent *ProviderConfigRef -} - -var moduleBlockSchema = &hcl.BodySchema{ - Attributes: []hcl.AttributeSchema{ - { - Name: "source", - Required: true, - }, - { - Name: "version", - }, - { - Name: "count", - }, - { - Name: "for_each", - }, - { - Name: "depends_on", - }, - { - Name: "providers", - }, - }, - Blocks: []hcl.BlockHeaderSchema{ - // These are all reserved for future use. - {Type: "lifecycle"}, - {Type: "locals"}, - {Type: "provider", LabelNames: []string{"type"}}, - }, -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/module_merge.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/module_merge.go deleted file mode 100644 index 6fb82acf..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/module_merge.go +++ /dev/null @@ -1,247 +0,0 @@ -package configs - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" -) - -// The methods in this file are used by Module.mergeFile to apply overrides -// to our different configuration elements. These methods all follow the -// pattern of mutating the receiver to incorporate settings from the parameter, -// returning error diagnostics if any aspect of the parameter cannot be merged -// into the receiver for some reason. -// -// User expectation is that anything _explicitly_ set in the given object -// should take precedence over the corresponding settings in the receiver, -// but that anything omitted in the given object should be left unchanged. -// In some cases it may be reasonable to do a "deep merge" of certain nested -// features, if it is possible to unambiguously correlate the nested elements -// and their behaviors are orthogonal to each other. - -func (p *Provider) merge(op *Provider) hcl.Diagnostics { - var diags hcl.Diagnostics - - if op.Version.Required != nil { - p.Version = op.Version - } - - p.Config = MergeBodies(p.Config, op.Config) - - return diags -} - -func mergeProviderVersionConstraints(recv map[string][]VersionConstraint, ovrd []*ProviderRequirement) { - // Any provider name that's mentioned in the override gets nilled out in - // our map so that we'll rebuild it below. Any provider not mentioned is - // left unchanged. - for _, reqd := range ovrd { - delete(recv, reqd.Name) - } - for _, reqd := range ovrd { - recv[reqd.Name] = append(recv[reqd.Name], reqd.Requirement) - } -} - -func (v *Variable) merge(ov *Variable) hcl.Diagnostics { - var diags hcl.Diagnostics - - if ov.DescriptionSet { - v.Description = ov.Description - v.DescriptionSet = ov.DescriptionSet - } - if ov.Default != cty.NilVal { - v.Default = ov.Default - } - if ov.Type != cty.NilType { - v.Type = ov.Type - } - if ov.ParsingMode != 0 { - v.ParsingMode = ov.ParsingMode - } - - // If the override file overrode type without default or vice-versa then - // it may have created an invalid situation, which we'll catch now by - // attempting to re-convert the value. - // - // Note that here we may be re-converting an already-converted base value - // from the base config. This will be a no-op if the type was not changed, - // but in particular might be user-observable in the edge case where the - // literal value in config could've been converted to the overridden type - // constraint but the converted value cannot. In practice, this situation - // should be rare since most of our conversions are interchangable. - if v.Default != cty.NilVal { - val, err := convert.Convert(v.Default, v.Type) - if err != nil { - // What exactly we'll say in the error message here depends on whether - // it was Default or Type that was overridden here. - switch { - case ov.Type != cty.NilType && ov.Default == cty.NilVal: - // If only the type was overridden - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid default value for variable", - Detail: fmt.Sprintf("Overriding this variable's type constraint has made its default value invalid: %s.", err), - Subject: &ov.DeclRange, - }) - case ov.Type == cty.NilType && ov.Default != cty.NilVal: - // Only the default was overridden - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid default value for variable", - Detail: fmt.Sprintf("The overridden default value for this variable is not compatible with the variable's type constraint: %s.", err), - Subject: &ov.DeclRange, - }) - default: - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid default value for variable", - Detail: fmt.Sprintf("This variable's default value is not compatible with its type constraint: %s.", err), - Subject: &ov.DeclRange, - }) - } - } else { - v.Default = val - } - } - - return diags -} - -func (l *Local) merge(ol *Local) hcl.Diagnostics { - var diags hcl.Diagnostics - - // Since a local is just a single expression in configuration, the - // override definition entirely replaces the base definition, including - // the source range so that we'll send the user to the right place if - // there is an error. - l.Expr = ol.Expr - l.DeclRange = ol.DeclRange - - return diags -} - -func (o *Output) merge(oo *Output) hcl.Diagnostics { - var diags hcl.Diagnostics - - if oo.Description != "" { - o.Description = oo.Description - } - if oo.Expr != nil { - o.Expr = oo.Expr - } - if oo.SensitiveSet { - o.Sensitive = oo.Sensitive - o.SensitiveSet = oo.SensitiveSet - } - - // We don't allow depends_on to be overridden because that is likely to - // cause confusing misbehavior. - if len(oo.DependsOn) != 0 { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Unsupported override", - Detail: "The depends_on argument may not be overridden.", - Subject: oo.DependsOn[0].SourceRange().Ptr(), // the first item is the closest range we have - }) - } - - return diags -} - -func (mc *ModuleCall) merge(omc *ModuleCall) hcl.Diagnostics { - var diags hcl.Diagnostics - - if omc.SourceSet { - mc.SourceAddr = omc.SourceAddr - mc.SourceAddrRange = omc.SourceAddrRange - mc.SourceSet = omc.SourceSet - } - - if omc.Count != nil { - mc.Count = omc.Count - } - - if omc.ForEach != nil { - mc.ForEach = omc.ForEach - } - - if len(omc.Version.Required) != 0 { - mc.Version = omc.Version - } - - mc.Config = MergeBodies(mc.Config, omc.Config) - - // We don't allow depends_on to be overridden because that is likely to - // cause confusing misbehavior. - if len(mc.DependsOn) != 0 { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Unsupported override", - Detail: "The depends_on argument may not be overridden.", - Subject: mc.DependsOn[0].SourceRange().Ptr(), // the first item is the closest range we have - }) - } - - return diags -} - -func (r *Resource) merge(or *Resource) hcl.Diagnostics { - var diags hcl.Diagnostics - - if r.Mode != or.Mode { - // This is always a programming error, since managed and data resources - // are kept in separate maps in the configuration structures. - panic(fmt.Errorf("can't merge %s into %s", or.Mode, r.Mode)) - } - - if or.Count != nil { - r.Count = or.Count - } - if or.ForEach != nil { - r.ForEach = or.ForEach - } - if or.ProviderConfigRef != nil { - r.ProviderConfigRef = or.ProviderConfigRef - } - if r.Mode == addrs.ManagedResourceMode { - // or.Managed is always non-nil for managed resource mode - - if or.Managed.Connection != nil { - r.Managed.Connection = or.Managed.Connection - } - if or.Managed.CreateBeforeDestroySet { - r.Managed.CreateBeforeDestroy = or.Managed.CreateBeforeDestroy - r.Managed.CreateBeforeDestroySet = or.Managed.CreateBeforeDestroySet - } - if len(or.Managed.IgnoreChanges) != 0 { - r.Managed.IgnoreChanges = or.Managed.IgnoreChanges - } - if or.Managed.PreventDestroySet { - r.Managed.PreventDestroy = or.Managed.PreventDestroy - r.Managed.PreventDestroySet = or.Managed.PreventDestroySet - } - if len(or.Managed.Provisioners) != 0 { - r.Managed.Provisioners = or.Managed.Provisioners - } - } - - r.Config = MergeBodies(r.Config, or.Config) - - // We don't allow depends_on to be overridden because that is likely to - // cause confusing misbehavior. - if len(or.DependsOn) != 0 { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Unsupported override", - Detail: "The depends_on argument may not be overridden.", - Subject: or.DependsOn[0].SourceRange().Ptr(), // the first item is the closest range we have - }) - } - - return diags -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/module_merge_body.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/module_merge_body.go deleted file mode 100644 index 7b51eae8..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/module_merge_body.go +++ /dev/null @@ -1,143 +0,0 @@ -package configs - -import ( - "github.com/hashicorp/hcl/v2" -) - -// MergeBodies creates a new HCL body that contains a combination of the -// given base and override bodies. Attributes and blocks defined in the -// override body take precedence over those of the same name defined in -// the base body. -// -// If any block of a particular type appears in "override" then it will -// replace _all_ of the blocks of the same type in "base" in the new -// body. -func MergeBodies(base, override hcl.Body) hcl.Body { - return mergeBody{ - Base: base, - Override: override, - } -} - -// mergeBody is a hcl.Body implementation that wraps a pair of other bodies -// and allows attributes and blocks within the override to take precedence -// over those defined in the base body. -// -// This is used to deal with dynamically-processed bodies in Module.mergeFile. -// It uses a shallow-only merging strategy where direct attributes defined -// in Override will override attributes of the same name in Base, while any -// blocks defined in Override will hide all blocks of the same type in Base. -// -// This cannot possibly "do the right thing" in all cases, because we don't -// have enough information about user intent. However, this behavior is intended -// to be reasonable for simple overriding use-cases. -type mergeBody struct { - Base hcl.Body - Override hcl.Body -} - -var _ hcl.Body = mergeBody{} - -func (b mergeBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) { - var diags hcl.Diagnostics - baseSchema := schemaWithDynamic(schema) - overrideSchema := schemaWithDynamic(schemaForOverrides(schema)) - - baseContent, _, cDiags := b.Base.PartialContent(baseSchema) - diags = append(diags, cDiags...) - overrideContent, _, cDiags := b.Override.PartialContent(overrideSchema) - diags = append(diags, cDiags...) - - content := b.prepareContent(baseContent, overrideContent) - - return content, diags -} - -func (b mergeBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) { - var diags hcl.Diagnostics - baseSchema := schemaWithDynamic(schema) - overrideSchema := schemaWithDynamic(schemaForOverrides(schema)) - - baseContent, baseRemain, cDiags := b.Base.PartialContent(baseSchema) - diags = append(diags, cDiags...) - overrideContent, overrideRemain, cDiags := b.Override.PartialContent(overrideSchema) - diags = append(diags, cDiags...) - - content := b.prepareContent(baseContent, overrideContent) - - remain := MergeBodies(baseRemain, overrideRemain) - - return content, remain, diags -} - -func (b mergeBody) prepareContent(base *hcl.BodyContent, override *hcl.BodyContent) *hcl.BodyContent { - content := &hcl.BodyContent{ - Attributes: make(hcl.Attributes), - } - - // For attributes we just assign from each map in turn and let the override - // map clobber any matching entries from base. - for k, a := range base.Attributes { - content.Attributes[k] = a - } - for k, a := range override.Attributes { - content.Attributes[k] = a - } - - // Things are a little more interesting for blocks because they arrive - // as a flat list. Our merging semantics call for us to suppress blocks - // from base if at least one block of the same type appears in override. - // We explicitly do not try to correlate and deeply merge nested blocks, - // since we don't have enough context here to infer user intent. - - overriddenBlockTypes := make(map[string]bool) - for _, block := range override.Blocks { - if block.Type == "dynamic" { - overriddenBlockTypes[block.Labels[0]] = true - continue - } - overriddenBlockTypes[block.Type] = true - } - for _, block := range base.Blocks { - // We skip over dynamic blocks whose type label is an overridden type - // but note that below we do still leave them as dynamic blocks in - // the result because expanding the dynamic blocks that are left is - // done much later during the core graph walks, where we can safely - // evaluate the expressions. - if block.Type == "dynamic" && overriddenBlockTypes[block.Labels[0]] { - continue - } - if overriddenBlockTypes[block.Type] { - continue - } - content.Blocks = append(content.Blocks, block) - } - for _, block := range override.Blocks { - content.Blocks = append(content.Blocks, block) - } - - return content -} - -func (b mergeBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) { - var diags hcl.Diagnostics - ret := make(hcl.Attributes) - - baseAttrs, aDiags := b.Base.JustAttributes() - diags = append(diags, aDiags...) - overrideAttrs, aDiags := b.Override.JustAttributes() - diags = append(diags, aDiags...) - - for k, a := range baseAttrs { - ret[k] = a - } - for k, a := range overrideAttrs { - ret[k] = a - } - - return ret, diags -} - -func (b mergeBody) MissingItemRange() hcl.Range { - return b.Base.MissingItemRange() -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/named_values.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/named_values.go deleted file mode 100644 index 8c8398e0..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/named_values.go +++ /dev/null @@ -1,354 +0,0 @@ -package configs - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/ext/typeexpr" - "github.com/hashicorp/hcl/v2/gohcl" - "github.com/hashicorp/hcl/v2/hclsyntax" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" -) - -// A consistent detail message for all "not a valid identifier" diagnostics. -const badIdentifierDetail = "A name must start with a letter and may contain only letters, digits, underscores, and dashes." - -// Variable represents a "variable" block in a module or file. -type Variable struct { - Name string - Description string - Default cty.Value - Type cty.Type - ParsingMode VariableParsingMode - - DescriptionSet bool - - DeclRange hcl.Range -} - -func decodeVariableBlock(block *hcl.Block, override bool) (*Variable, hcl.Diagnostics) { - v := &Variable{ - Name: block.Labels[0], - DeclRange: block.DefRange, - } - - // Unless we're building an override, we'll set some defaults - // which we might override with attributes below. We leave these - // as zero-value in the override case so we can recognize whether - // or not they are set when we merge. - if !override { - v.Type = cty.DynamicPseudoType - v.ParsingMode = VariableParseLiteral - } - - content, diags := block.Body.Content(variableBlockSchema) - - if !hclsyntax.ValidIdentifier(v.Name) { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid variable name", - Detail: badIdentifierDetail, - Subject: &block.LabelRanges[0], - }) - } - - // Don't allow declaration of variables that would conflict with the - // reserved attribute and block type names in a "module" block, since - // these won't be usable for child modules. - for _, attr := range moduleBlockSchema.Attributes { - if attr.Name == v.Name { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid variable name", - Detail: fmt.Sprintf("The variable name %q is reserved due to its special meaning inside module blocks.", attr.Name), - Subject: &block.LabelRanges[0], - }) - } - } - for _, blockS := range moduleBlockSchema.Blocks { - if blockS.Type == v.Name { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid variable name", - Detail: fmt.Sprintf("The variable name %q is reserved due to its special meaning inside module blocks.", blockS.Type), - Subject: &block.LabelRanges[0], - }) - } - } - - if attr, exists := content.Attributes["description"]; exists { - valDiags := gohcl.DecodeExpression(attr.Expr, nil, &v.Description) - diags = append(diags, valDiags...) - v.DescriptionSet = true - } - - if attr, exists := content.Attributes["type"]; exists { - ty, parseMode, tyDiags := decodeVariableType(attr.Expr) - diags = append(diags, tyDiags...) - v.Type = ty - v.ParsingMode = parseMode - } - - if attr, exists := content.Attributes["default"]; exists { - val, valDiags := attr.Expr.Value(nil) - diags = append(diags, valDiags...) - - // Convert the default to the expected type so we can catch invalid - // defaults early and allow later code to assume validity. - // Note that this depends on us having already processed any "type" - // attribute above. - // However, we can't do this if we're in an override file where - // the type might not be set; we'll catch that during merge. - if v.Type != cty.NilType { - var err error - val, err = convert.Convert(val, v.Type) - if err != nil { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid default value for variable", - Detail: fmt.Sprintf("This default value is not compatible with the variable's type constraint: %s.", err), - Subject: attr.Expr.Range().Ptr(), - }) - val = cty.DynamicVal - } - } - - v.Default = val - } - - return v, diags -} - -func decodeVariableType(expr hcl.Expression) (cty.Type, VariableParsingMode, hcl.Diagnostics) { - if exprIsNativeQuotedString(expr) { - // Here we're accepting the pre-0.12 form of variable type argument where - // the string values "string", "list" and "map" are accepted has a hint - // about the type used primarily for deciding how to parse values - // given on the command line and in environment variables. - // Only the native syntax ends up in this codepath; we handle the - // JSON syntax (which is, of course, quoted even in the new format) - // in the normal codepath below. - val, diags := expr.Value(nil) - if diags.HasErrors() { - return cty.DynamicPseudoType, VariableParseHCL, diags - } - str := val.AsString() - switch str { - case "string": - return cty.String, VariableParseLiteral, diags - case "list": - return cty.List(cty.DynamicPseudoType), VariableParseHCL, diags - case "map": - return cty.Map(cty.DynamicPseudoType), VariableParseHCL, diags - default: - return cty.DynamicPseudoType, VariableParseHCL, hcl.Diagnostics{{ - Severity: hcl.DiagError, - Summary: "Invalid legacy variable type hint", - Detail: `The legacy variable type hint form, using a quoted string, allows only the values "string", "list", and "map". To provide a full type expression, remove the surrounding quotes and give the type expression directly.`, - Subject: expr.Range().Ptr(), - }} - } - } - - // First we'll deal with some shorthand forms that the HCL-level type - // expression parser doesn't include. These both emulate pre-0.12 behavior - // of allowing a list or map of any element type as long as all of the - // elements are consistent. This is the same as list(any) or map(any). - switch hcl.ExprAsKeyword(expr) { - case "list": - return cty.List(cty.DynamicPseudoType), VariableParseHCL, nil - case "map": - return cty.Map(cty.DynamicPseudoType), VariableParseHCL, nil - } - - ty, diags := typeexpr.TypeConstraint(expr) - if diags.HasErrors() { - return cty.DynamicPseudoType, VariableParseHCL, diags - } - - switch { - case ty.IsPrimitiveType(): - // Primitive types use literal parsing. - return ty, VariableParseLiteral, diags - default: - // Everything else uses HCL parsing - return ty, VariableParseHCL, diags - } -} - -// VariableParsingMode defines how values of a particular variable given by -// text-only mechanisms (command line arguments and environment variables) -// should be parsed to produce the final value. -type VariableParsingMode rune - -// VariableParseLiteral is a variable parsing mode that just takes the given -// string directly as a cty.String value. -const VariableParseLiteral VariableParsingMode = 'L' - -// VariableParseHCL is a variable parsing mode that attempts to parse the given -// string as an HCL expression and returns the result. -const VariableParseHCL VariableParsingMode = 'H' - -// Parse uses the receiving parsing mode to process the given variable value -// string, returning the result along with any diagnostics. -// -// A VariableParsingMode does not know the expected type of the corresponding -// variable, so it's the caller's responsibility to attempt to convert the -// result to the appropriate type and return to the user any diagnostics that -// conversion may produce. -// -// The given name is used to create a synthetic filename in case any diagnostics -// must be generated about the given string value. This should be the name -// of the root module variable whose value will be populated from the given -// string. -// -// If the returned diagnostics has errors, the returned value may not be -// valid. -func (m VariableParsingMode) Parse(name, value string) (cty.Value, hcl.Diagnostics) { - switch m { - case VariableParseLiteral: - return cty.StringVal(value), nil - case VariableParseHCL: - fakeFilename := fmt.Sprintf("", name) - expr, diags := hclsyntax.ParseExpression([]byte(value), fakeFilename, hcl.Pos{Line: 1, Column: 1}) - if diags.HasErrors() { - return cty.DynamicVal, diags - } - val, valDiags := expr.Value(nil) - diags = append(diags, valDiags...) - return val, diags - default: - // Should never happen - panic(fmt.Errorf("Parse called on invalid VariableParsingMode %#v", m)) - } -} - -// Output represents an "output" block in a module or file. -type Output struct { - Name string - Description string - Expr hcl.Expression - DependsOn []hcl.Traversal - Sensitive bool - - DescriptionSet bool - SensitiveSet bool - - DeclRange hcl.Range -} - -func decodeOutputBlock(block *hcl.Block, override bool) (*Output, hcl.Diagnostics) { - o := &Output{ - Name: block.Labels[0], - DeclRange: block.DefRange, - } - - schema := outputBlockSchema - if override { - schema = schemaForOverrides(schema) - } - - content, diags := block.Body.Content(schema) - - if !hclsyntax.ValidIdentifier(o.Name) { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid output name", - Detail: badIdentifierDetail, - Subject: &block.LabelRanges[0], - }) - } - - if attr, exists := content.Attributes["description"]; exists { - valDiags := gohcl.DecodeExpression(attr.Expr, nil, &o.Description) - diags = append(diags, valDiags...) - o.DescriptionSet = true - } - - if attr, exists := content.Attributes["value"]; exists { - o.Expr = attr.Expr - } - - if attr, exists := content.Attributes["sensitive"]; exists { - valDiags := gohcl.DecodeExpression(attr.Expr, nil, &o.Sensitive) - diags = append(diags, valDiags...) - o.SensitiveSet = true - } - - if attr, exists := content.Attributes["depends_on"]; exists { - deps, depsDiags := decodeDependsOn(attr) - diags = append(diags, depsDiags...) - o.DependsOn = append(o.DependsOn, deps...) - } - - return o, diags -} - -// Local represents a single entry from a "locals" block in a module or file. -// The "locals" block itself is not represented, because it serves only to -// provide context for us to interpret its contents. -type Local struct { - Name string - Expr hcl.Expression - - DeclRange hcl.Range -} - -func decodeLocalsBlock(block *hcl.Block) ([]*Local, hcl.Diagnostics) { - attrs, diags := block.Body.JustAttributes() - if len(attrs) == 0 { - return nil, diags - } - - locals := make([]*Local, 0, len(attrs)) - for name, attr := range attrs { - if !hclsyntax.ValidIdentifier(name) { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid local value name", - Detail: badIdentifierDetail, - Subject: &attr.NameRange, - }) - } - - locals = append(locals, &Local{ - Name: name, - Expr: attr.Expr, - DeclRange: attr.Range, - }) - } - return locals, diags -} - -var variableBlockSchema = &hcl.BodySchema{ - Attributes: []hcl.AttributeSchema{ - { - Name: "description", - }, - { - Name: "default", - }, - { - Name: "type", - }, - }, -} - -var outputBlockSchema = &hcl.BodySchema{ - Attributes: []hcl.AttributeSchema{ - { - Name: "description", - }, - { - Name: "value", - Required: true, - }, - { - Name: "depends_on", - }, - { - Name: "sensitive", - }, - }, -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/parser.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/parser.go deleted file mode 100644 index 2a621b57..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/parser.go +++ /dev/null @@ -1,100 +0,0 @@ -package configs - -import ( - "fmt" - "strings" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclparse" - "github.com/spf13/afero" -) - -// Parser is the main interface to read configuration files and other related -// files from disk. -// -// It retains a cache of all files that are loaded so that they can be used -// to create source code snippets in diagnostics, etc. -type Parser struct { - fs afero.Afero - p *hclparse.Parser -} - -// NewParser creates and returns a new Parser that reads files from the given -// filesystem. If a nil filesystem is passed then the system's "real" filesystem -// will be used, via afero.OsFs. -func NewParser(fs afero.Fs) *Parser { - if fs == nil { - fs = afero.OsFs{} - } - - return &Parser{ - fs: afero.Afero{Fs: fs}, - p: hclparse.NewParser(), - } -} - -// LoadHCLFile is a low-level method that reads the file at the given path, -// parses it, and returns the hcl.Body representing its root. In many cases -// it is better to use one of the other Load*File methods on this type, -// which additionally decode the root body in some way and return a higher-level -// construct. -// -// If the file cannot be read at all -- e.g. because it does not exist -- then -// this method will return a nil body and error diagnostics. In this case -// callers may wish to ignore the provided error diagnostics and produce -// a more context-sensitive error instead. -// -// The file will be parsed using the HCL native syntax unless the filename -// ends with ".json", in which case the HCL JSON syntax will be used. -func (p *Parser) LoadHCLFile(path string) (hcl.Body, hcl.Diagnostics) { - src, err := p.fs.ReadFile(path) - - if err != nil { - return nil, hcl.Diagnostics{ - { - Severity: hcl.DiagError, - Summary: "Failed to read file", - Detail: fmt.Sprintf("The file %q could not be read.", path), - }, - } - } - - var file *hcl.File - var diags hcl.Diagnostics - switch { - case strings.HasSuffix(path, ".json"): - file, diags = p.p.ParseJSON(src, path) - default: - file, diags = p.p.ParseHCL(src, path) - } - - // If the returned file or body is nil, then we'll return a non-nil empty - // body so we'll meet our contract that nil means an error reading the file. - if file == nil || file.Body == nil { - return hcl.EmptyBody(), diags - } - - return file.Body, diags -} - -// Sources returns a map of the cached source buffers for all files that -// have been loaded through this parser, with source filenames (as requested -// when each file was opened) as the keys. -func (p *Parser) Sources() map[string][]byte { - return p.p.Sources() -} - -// ForceFileSource artificially adds source code to the cache of file sources, -// as if it had been loaded from the given filename. -// -// This should be used only in special situations where configuration is loaded -// some other way. Most callers should load configuration via methods of -// Parser, which will update the sources cache automatically. -func (p *Parser) ForceFileSource(filename string, src []byte) { - // We'll make a synthetic hcl.File here just so we can reuse the - // existing cache. - p.p.AddFile(filename, &hcl.File{ - Body: hcl.EmptyBody(), - Bytes: src, - }) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/parser_config.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/parser_config.go deleted file mode 100644 index d4cbc945..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/parser_config.go +++ /dev/null @@ -1,247 +0,0 @@ -package configs - -import ( - "github.com/hashicorp/hcl/v2" -) - -// LoadConfigFile reads the file at the given path and parses it as a config -// file. -// -// If the file cannot be read -- for example, if it does not exist -- then -// a nil *File will be returned along with error diagnostics. Callers may wish -// to disregard the returned diagnostics in this case and instead generate -// their own error message(s) with additional context. -// -// If the returned diagnostics has errors when a non-nil map is returned -// then the map may be incomplete but should be valid enough for careful -// static analysis. -// -// This method wraps LoadHCLFile, and so it inherits the syntax selection -// behaviors documented for that method. -func (p *Parser) LoadConfigFile(path string) (*File, hcl.Diagnostics) { - return p.loadConfigFile(path, false) -} - -// LoadConfigFileOverride is the same as LoadConfigFile except that it relaxes -// certain required attribute constraints in order to interpret the given -// file as an overrides file. -func (p *Parser) LoadConfigFileOverride(path string) (*File, hcl.Diagnostics) { - return p.loadConfigFile(path, true) -} - -func (p *Parser) loadConfigFile(path string, override bool) (*File, hcl.Diagnostics) { - - body, diags := p.LoadHCLFile(path) - if body == nil { - return nil, diags - } - - file := &File{} - - var reqDiags hcl.Diagnostics - file.CoreVersionConstraints, reqDiags = sniffCoreVersionRequirements(body) - diags = append(diags, reqDiags...) - - content, contentDiags := body.Content(configFileSchema) - diags = append(diags, contentDiags...) - - for _, block := range content.Blocks { - switch block.Type { - - case "terraform": - content, contentDiags := block.Body.Content(terraformBlockSchema) - diags = append(diags, contentDiags...) - - // We ignore the "terraform_version" attribute here because - // sniffCoreVersionRequirements already dealt with that above. - - for _, innerBlock := range content.Blocks { - switch innerBlock.Type { - - case "backend": - backendCfg, cfgDiags := decodeBackendBlock(innerBlock) - diags = append(diags, cfgDiags...) - if backendCfg != nil { - file.Backends = append(file.Backends, backendCfg) - } - - case "required_providers": - reqs, reqsDiags := decodeRequiredProvidersBlock(innerBlock) - diags = append(diags, reqsDiags...) - file.ProviderRequirements = append(file.ProviderRequirements, reqs...) - - default: - // Should never happen because the above cases should be exhaustive - // for all block type names in our schema. - continue - - } - } - - case "provider": - cfg, cfgDiags := decodeProviderBlock(block) - diags = append(diags, cfgDiags...) - if cfg != nil { - file.ProviderConfigs = append(file.ProviderConfigs, cfg) - } - - case "variable": - cfg, cfgDiags := decodeVariableBlock(block, override) - diags = append(diags, cfgDiags...) - if cfg != nil { - file.Variables = append(file.Variables, cfg) - } - - case "locals": - defs, defsDiags := decodeLocalsBlock(block) - diags = append(diags, defsDiags...) - file.Locals = append(file.Locals, defs...) - - case "output": - cfg, cfgDiags := decodeOutputBlock(block, override) - diags = append(diags, cfgDiags...) - if cfg != nil { - file.Outputs = append(file.Outputs, cfg) - } - - case "module": - cfg, cfgDiags := decodeModuleBlock(block, override) - diags = append(diags, cfgDiags...) - if cfg != nil { - file.ModuleCalls = append(file.ModuleCalls, cfg) - } - - case "resource": - cfg, cfgDiags := decodeResourceBlock(block) - diags = append(diags, cfgDiags...) - if cfg != nil { - file.ManagedResources = append(file.ManagedResources, cfg) - } - - case "data": - cfg, cfgDiags := decodeDataBlock(block) - diags = append(diags, cfgDiags...) - if cfg != nil { - file.DataResources = append(file.DataResources, cfg) - } - - default: - // Should never happen because the above cases should be exhaustive - // for all block type names in our schema. - continue - - } - } - - return file, diags -} - -// sniffCoreVersionRequirements does minimal parsing of the given body for -// "terraform" blocks with "required_version" attributes, returning the -// requirements found. -// -// This is intended to maximize the chance that we'll be able to read the -// requirements (syntax errors notwithstanding) even if the config file contains -// constructs that might've been added in future Terraform versions -// -// This is a "best effort" sort of method which will return constraints it is -// able to find, but may return no constraints at all if the given body is -// so invalid that it cannot be decoded at all. -func sniffCoreVersionRequirements(body hcl.Body) ([]VersionConstraint, hcl.Diagnostics) { - rootContent, _, diags := body.PartialContent(configFileVersionSniffRootSchema) - - var constraints []VersionConstraint - - for _, block := range rootContent.Blocks { - content, _, blockDiags := block.Body.PartialContent(configFileVersionSniffBlockSchema) - diags = append(diags, blockDiags...) - - attr, exists := content.Attributes["required_version"] - if !exists { - continue - } - - constraint, constraintDiags := decodeVersionConstraint(attr) - diags = append(diags, constraintDiags...) - if !constraintDiags.HasErrors() { - constraints = append(constraints, constraint) - } - } - - return constraints, diags -} - -// configFileSchema is the schema for the top-level of a config file. We use -// the low-level HCL API for this level so we can easily deal with each -// block type separately with its own decoding logic. -var configFileSchema = &hcl.BodySchema{ - Blocks: []hcl.BlockHeaderSchema{ - { - Type: "terraform", - }, - { - Type: "provider", - LabelNames: []string{"name"}, - }, - { - Type: "variable", - LabelNames: []string{"name"}, - }, - { - Type: "locals", - }, - { - Type: "output", - LabelNames: []string{"name"}, - }, - { - Type: "module", - LabelNames: []string{"name"}, - }, - { - Type: "resource", - LabelNames: []string{"type", "name"}, - }, - { - Type: "data", - LabelNames: []string{"type", "name"}, - }, - }, -} - -// terraformBlockSchema is the schema for a top-level "terraform" block in -// a configuration file. -var terraformBlockSchema = &hcl.BodySchema{ - Attributes: []hcl.AttributeSchema{ - { - Name: "required_version", - }, - }, - Blocks: []hcl.BlockHeaderSchema{ - { - Type: "backend", - LabelNames: []string{"type"}, - }, - { - Type: "required_providers", - }, - }, -} - -// configFileVersionSniffRootSchema is a schema for sniffCoreVersionRequirements -var configFileVersionSniffRootSchema = &hcl.BodySchema{ - Blocks: []hcl.BlockHeaderSchema{ - { - Type: "terraform", - }, - }, -} - -// configFileVersionSniffBlockSchema is a schema for sniffCoreVersionRequirements -var configFileVersionSniffBlockSchema = &hcl.BodySchema{ - Attributes: []hcl.AttributeSchema{ - { - Name: "required_version", - }, - }, -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/parser_config_dir.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/parser_config_dir.go deleted file mode 100644 index afdd6983..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/parser_config_dir.go +++ /dev/null @@ -1,163 +0,0 @@ -package configs - -import ( - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/hashicorp/hcl/v2" -) - -// LoadConfigDir reads the .tf and .tf.json files in the given directory -// as config files (using LoadConfigFile) and then combines these files into -// a single Module. -// -// If this method returns nil, that indicates that the given directory does not -// exist at all or could not be opened for some reason. Callers may wish to -// detect this case and ignore the returned diagnostics so that they can -// produce a more context-aware error message in that case. -// -// If this method returns a non-nil module while error diagnostics are returned -// then the module may be incomplete but can be used carefully for static -// analysis. -// -// This file does not consider a directory with no files to be an error, and -// will simply return an empty module in that case. Callers should first call -// Parser.IsConfigDir if they wish to recognize that situation. -// -// .tf files are parsed using the HCL native syntax while .tf.json files are -// parsed using the HCL JSON syntax. -func (p *Parser) LoadConfigDir(path string) (*Module, hcl.Diagnostics) { - primaryPaths, overridePaths, diags := p.dirFiles(path) - if diags.HasErrors() { - return nil, diags - } - - primary, fDiags := p.loadFiles(primaryPaths, false) - diags = append(diags, fDiags...) - override, fDiags := p.loadFiles(overridePaths, true) - diags = append(diags, fDiags...) - - mod, modDiags := NewModule(primary, override) - diags = append(diags, modDiags...) - - mod.SourceDir = path - - return mod, diags -} - -// ConfigDirFiles returns lists of the primary and override files configuration -// files in the given directory. -// -// If the given directory does not exist or cannot be read, error diagnostics -// are returned. If errors are returned, the resulting lists may be incomplete. -func (p Parser) ConfigDirFiles(dir string) (primary, override []string, diags hcl.Diagnostics) { - return p.dirFiles(dir) -} - -// IsConfigDir determines whether the given path refers to a directory that -// exists and contains at least one Terraform config file (with a .tf or -// .tf.json extension.) -func (p *Parser) IsConfigDir(path string) bool { - primaryPaths, overridePaths, _ := p.dirFiles(path) - return (len(primaryPaths) + len(overridePaths)) > 0 -} - -func (p *Parser) loadFiles(paths []string, override bool) ([]*File, hcl.Diagnostics) { - var files []*File - var diags hcl.Diagnostics - - for _, path := range paths { - var f *File - var fDiags hcl.Diagnostics - if override { - f, fDiags = p.LoadConfigFileOverride(path) - } else { - f, fDiags = p.LoadConfigFile(path) - } - diags = append(diags, fDiags...) - if f != nil { - files = append(files, f) - } - } - - return files, diags -} - -func (p *Parser) dirFiles(dir string) (primary, override []string, diags hcl.Diagnostics) { - infos, err := p.fs.ReadDir(dir) - if err != nil { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Failed to read module directory", - Detail: fmt.Sprintf("Module directory %s does not exist or cannot be read.", dir), - }) - return - } - - for _, info := range infos { - if info.IsDir() { - // We only care about files - continue - } - - name := info.Name() - ext := fileExt(name) - if ext == "" || IsIgnoredFile(name) { - continue - } - - baseName := name[:len(name)-len(ext)] // strip extension - isOverride := baseName == "override" || strings.HasSuffix(baseName, "_override") - - fullPath := filepath.Join(dir, name) - if isOverride { - override = append(override, fullPath) - } else { - primary = append(primary, fullPath) - } - } - - return -} - -// fileExt returns the Terraform configuration extension of the given -// path, or a blank string if it is not a recognized extension. -func fileExt(path string) string { - if strings.HasSuffix(path, ".tf") { - return ".tf" - } else if strings.HasSuffix(path, ".tf.json") { - return ".tf.json" - } else { - return "" - } -} - -// IsIgnoredFile returns true if the given filename (which must not have a -// directory path ahead of it) should be ignored as e.g. an editor swap file. -func IsIgnoredFile(name string) bool { - return strings.HasPrefix(name, ".") || // Unix-like hidden files - strings.HasSuffix(name, "~") || // vim - strings.HasPrefix(name, "#") && strings.HasSuffix(name, "#") // emacs -} - -// IsEmptyDir returns true if the given filesystem path contains no Terraform -// configuration files. -// -// Unlike the methods of the Parser type, this function always consults the -// real filesystem, and thus it isn't appropriate to use when working with -// configuration loaded from a plan file. -func IsEmptyDir(path string) (bool, error) { - if _, err := os.Stat(path); err != nil && os.IsNotExist(err) { - return true, nil - } - - p := NewParser(nil) - fs, os, err := p.dirFiles(path) - if err != nil { - return false, err - } - - return len(fs) == 0 && len(os) == 0, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/parser_values.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/parser_values.go deleted file mode 100644 index 10d98e5b..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/parser_values.go +++ /dev/null @@ -1,43 +0,0 @@ -package configs - -import ( - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" -) - -// LoadValuesFile reads the file at the given path and parses it as a "values -// file", which is an HCL config file whose top-level attributes are treated -// as arbitrary key.value pairs. -// -// If the file cannot be read -- for example, if it does not exist -- then -// a nil map will be returned along with error diagnostics. Callers may wish -// to disregard the returned diagnostics in this case and instead generate -// their own error message(s) with additional context. -// -// If the returned diagnostics has errors when a non-nil map is returned -// then the map may be incomplete but should be valid enough for careful -// static analysis. -// -// This method wraps LoadHCLFile, and so it inherits the syntax selection -// behaviors documented for that method. -func (p *Parser) LoadValuesFile(path string) (map[string]cty.Value, hcl.Diagnostics) { - body, diags := p.LoadHCLFile(path) - if body == nil { - return nil, diags - } - - vals := make(map[string]cty.Value) - attrs, attrDiags := body.JustAttributes() - diags = append(diags, attrDiags...) - if attrs == nil { - return vals, diags - } - - for name, attr := range attrs { - val, valDiags := attr.Expr.Value(nil) - diags = append(diags, valDiags...) - vals[name] = val - } - - return vals, diags -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/provider.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/provider.go deleted file mode 100644 index cb9ba1f3..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/provider.go +++ /dev/null @@ -1,144 +0,0 @@ -package configs - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/gohcl" - "github.com/hashicorp/hcl/v2/hclsyntax" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// Provider represents a "provider" block in a module or file. A provider -// block is a provider configuration, and there can be zero or more -// configurations for each actual provider. -type Provider struct { - Name string - NameRange hcl.Range - Alias string - AliasRange *hcl.Range // nil if no alias set - - Version VersionConstraint - - Config hcl.Body - - DeclRange hcl.Range -} - -func decodeProviderBlock(block *hcl.Block) (*Provider, hcl.Diagnostics) { - content, config, diags := block.Body.PartialContent(providerBlockSchema) - - provider := &Provider{ - Name: block.Labels[0], - NameRange: block.LabelRanges[0], - Config: config, - DeclRange: block.DefRange, - } - - if attr, exists := content.Attributes["alias"]; exists { - valDiags := gohcl.DecodeExpression(attr.Expr, nil, &provider.Alias) - diags = append(diags, valDiags...) - provider.AliasRange = attr.Expr.Range().Ptr() - - if !hclsyntax.ValidIdentifier(provider.Alias) { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid provider configuration alias", - Detail: fmt.Sprintf("An alias must be a valid name. %s", badIdentifierDetail), - }) - } - } - - if attr, exists := content.Attributes["version"]; exists { - var versionDiags hcl.Diagnostics - provider.Version, versionDiags = decodeVersionConstraint(attr) - diags = append(diags, versionDiags...) - } - - // Reserved attribute names - for _, name := range []string{"count", "depends_on", "for_each", "source"} { - if attr, exists := content.Attributes[name]; exists { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Reserved argument name in provider block", - Detail: fmt.Sprintf("The provider argument name %q is reserved for use by Terraform in a future version.", name), - Subject: &attr.NameRange, - }) - } - } - - // Reserved block types (all of them) - for _, block := range content.Blocks { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Reserved block type name in provider block", - Detail: fmt.Sprintf("The block type name %q is reserved for use by Terraform in a future version.", block.Type), - Subject: &block.TypeRange, - }) - } - - return provider, diags -} - -// Addr returns the address of the receiving provider configuration, relative -// to its containing module. -func (p *Provider) Addr() addrs.ProviderConfig { - return addrs.ProviderConfig{ - Type: p.Name, - Alias: p.Alias, - } -} - -func (p *Provider) moduleUniqueKey() string { - if p.Alias != "" { - return fmt.Sprintf("%s.%s", p.Name, p.Alias) - } - return p.Name -} - -// ProviderRequirement represents a declaration of a dependency on a particular -// provider version without actually configuring that provider. This is used in -// child modules that expect a provider to be passed in from their parent. -type ProviderRequirement struct { - Name string - Requirement VersionConstraint -} - -func decodeRequiredProvidersBlock(block *hcl.Block) ([]*ProviderRequirement, hcl.Diagnostics) { - attrs, diags := block.Body.JustAttributes() - var reqs []*ProviderRequirement - for name, attr := range attrs { - req, reqDiags := decodeVersionConstraint(attr) - diags = append(diags, reqDiags...) - if !diags.HasErrors() { - reqs = append(reqs, &ProviderRequirement{ - Name: name, - Requirement: req, - }) - } - } - return reqs, diags -} - -var providerBlockSchema = &hcl.BodySchema{ - Attributes: []hcl.AttributeSchema{ - { - Name: "alias", - }, - { - Name: "version", - }, - - // Attribute names reserved for future expansion. - {Name: "count"}, - {Name: "depends_on"}, - {Name: "for_each"}, - {Name: "source"}, - }, - Blocks: []hcl.BlockHeaderSchema{ - // _All_ of these are reserved for future expansion. - {Type: "lifecycle"}, - {Type: "locals"}, - }, -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/provisioner.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/provisioner.go deleted file mode 100644 index 47b65679..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/provisioner.go +++ /dev/null @@ -1,150 +0,0 @@ -package configs - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" -) - -// Provisioner represents a "provisioner" block when used within a -// "resource" block in a module or file. -type Provisioner struct { - Type string - Config hcl.Body - Connection *Connection - When ProvisionerWhen - OnFailure ProvisionerOnFailure - - DeclRange hcl.Range - TypeRange hcl.Range -} - -func decodeProvisionerBlock(block *hcl.Block) (*Provisioner, hcl.Diagnostics) { - pv := &Provisioner{ - Type: block.Labels[0], - TypeRange: block.LabelRanges[0], - DeclRange: block.DefRange, - When: ProvisionerWhenCreate, - OnFailure: ProvisionerOnFailureFail, - } - - content, config, diags := block.Body.PartialContent(provisionerBlockSchema) - pv.Config = config - - if attr, exists := content.Attributes["when"]; exists { - expr, shimDiags := shimTraversalInString(attr.Expr, true) - diags = append(diags, shimDiags...) - - switch hcl.ExprAsKeyword(expr) { - case "create": - pv.When = ProvisionerWhenCreate - case "destroy": - pv.When = ProvisionerWhenDestroy - default: - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid \"when\" keyword", - Detail: "The \"when\" argument requires one of the following keywords: create or destroy.", - Subject: expr.Range().Ptr(), - }) - } - } - - if attr, exists := content.Attributes["on_failure"]; exists { - expr, shimDiags := shimTraversalInString(attr.Expr, true) - diags = append(diags, shimDiags...) - - switch hcl.ExprAsKeyword(expr) { - case "continue": - pv.OnFailure = ProvisionerOnFailureContinue - case "fail": - pv.OnFailure = ProvisionerOnFailureFail - default: - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid \"on_failure\" keyword", - Detail: "The \"on_failure\" argument requires one of the following keywords: continue or fail.", - Subject: attr.Expr.Range().Ptr(), - }) - } - } - - var seenConnection *hcl.Block - for _, block := range content.Blocks { - switch block.Type { - - case "connection": - if seenConnection != nil { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Duplicate connection block", - Detail: fmt.Sprintf("This provisioner already has a connection block at %s.", seenConnection.DefRange), - Subject: &block.DefRange, - }) - continue - } - seenConnection = block - - //conn, connDiags := decodeConnectionBlock(block) - //diags = append(diags, connDiags...) - pv.Connection = &Connection{ - Config: block.Body, - DeclRange: block.DefRange, - } - - default: - // Any other block types are ones we've reserved for future use, - // so they get a generic message. - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Reserved block type name in provisioner block", - Detail: fmt.Sprintf("The block type name %q is reserved for use by Terraform in a future version.", block.Type), - Subject: &block.TypeRange, - }) - } - } - - return pv, diags -} - -// Connection represents a "connection" block when used within either a -// "resource" or "provisioner" block in a module or file. -type Connection struct { - Config hcl.Body - - DeclRange hcl.Range -} - -// ProvisionerWhen is an enum for valid values for when to run provisioners. -type ProvisionerWhen int - -//go:generate go run golang.org/x/tools/cmd/stringer -type ProvisionerWhen - -const ( - ProvisionerWhenInvalid ProvisionerWhen = iota - ProvisionerWhenCreate - ProvisionerWhenDestroy -) - -// ProvisionerOnFailure is an enum for valid values for on_failure options -// for provisioners. -type ProvisionerOnFailure int - -//go:generate go run golang.org/x/tools/cmd/stringer -type ProvisionerOnFailure - -const ( - ProvisionerOnFailureInvalid ProvisionerOnFailure = iota - ProvisionerOnFailureContinue - ProvisionerOnFailureFail -) - -var provisionerBlockSchema = &hcl.BodySchema{ - Attributes: []hcl.AttributeSchema{ - {Name: "when"}, - {Name: "on_failure"}, - }, - Blocks: []hcl.BlockHeaderSchema{ - {Type: "connection"}, - {Type: "lifecycle"}, // reserved for future use - }, -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/provisioneronfailure_string.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/provisioneronfailure_string.go deleted file mode 100644 index 7ff5a6e0..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/provisioneronfailure_string.go +++ /dev/null @@ -1,25 +0,0 @@ -// Code generated by "stringer -type ProvisionerOnFailure"; DO NOT EDIT. - -package configs - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[ProvisionerOnFailureInvalid-0] - _ = x[ProvisionerOnFailureContinue-1] - _ = x[ProvisionerOnFailureFail-2] -} - -const _ProvisionerOnFailure_name = "ProvisionerOnFailureInvalidProvisionerOnFailureContinueProvisionerOnFailureFail" - -var _ProvisionerOnFailure_index = [...]uint8{0, 27, 55, 79} - -func (i ProvisionerOnFailure) String() string { - if i < 0 || i >= ProvisionerOnFailure(len(_ProvisionerOnFailure_index)-1) { - return "ProvisionerOnFailure(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _ProvisionerOnFailure_name[_ProvisionerOnFailure_index[i]:_ProvisionerOnFailure_index[i+1]] -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/provisionerwhen_string.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/provisionerwhen_string.go deleted file mode 100644 index 9f21b3ac..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/provisionerwhen_string.go +++ /dev/null @@ -1,25 +0,0 @@ -// Code generated by "stringer -type ProvisionerWhen"; DO NOT EDIT. - -package configs - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[ProvisionerWhenInvalid-0] - _ = x[ProvisionerWhenCreate-1] - _ = x[ProvisionerWhenDestroy-2] -} - -const _ProvisionerWhen_name = "ProvisionerWhenInvalidProvisionerWhenCreateProvisionerWhenDestroy" - -var _ProvisionerWhen_index = [...]uint8{0, 22, 43, 65} - -func (i ProvisionerWhen) String() string { - if i < 0 || i >= ProvisionerWhen(len(_ProvisionerWhen_index)-1) { - return "ProvisionerWhen(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _ProvisionerWhen_name[_ProvisionerWhen_index[i]:_ProvisionerWhen_index[i+1]] -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/resource.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/resource.go deleted file mode 100644 index cd9991a3..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/resource.go +++ /dev/null @@ -1,490 +0,0 @@ -package configs - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/gohcl" - "github.com/hashicorp/hcl/v2/hclsyntax" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// Resource represents a "resource" or "data" block in a module or file. -type Resource struct { - Mode addrs.ResourceMode - Name string - Type string - Config hcl.Body - Count hcl.Expression - ForEach hcl.Expression - - ProviderConfigRef *ProviderConfigRef - - DependsOn []hcl.Traversal - - // Managed is populated only for Mode = addrs.ManagedResourceMode, - // containing the additional fields that apply to managed resources. - // For all other resource modes, this field is nil. - Managed *ManagedResource - - DeclRange hcl.Range - TypeRange hcl.Range -} - -// ManagedResource represents a "resource" block in a module or file. -type ManagedResource struct { - Connection *Connection - Provisioners []*Provisioner - - CreateBeforeDestroy bool - PreventDestroy bool - IgnoreChanges []hcl.Traversal - IgnoreAllChanges bool - - CreateBeforeDestroySet bool - PreventDestroySet bool -} - -func (r *Resource) moduleUniqueKey() string { - return r.Addr().String() -} - -// Addr returns a resource address for the receiver that is relative to the -// resource's containing module. -func (r *Resource) Addr() addrs.Resource { - return addrs.Resource{ - Mode: r.Mode, - Type: r.Type, - Name: r.Name, - } -} - -// ProviderConfigAddr returns the address for the provider configuration -// that should be used for this resource. This function implements the -// default behavior of extracting the type from the resource type name if -// an explicit "provider" argument was not provided. -func (r *Resource) ProviderConfigAddr() addrs.ProviderConfig { - if r.ProviderConfigRef == nil { - return r.Addr().DefaultProviderConfig() - } - - return addrs.ProviderConfig{ - Type: r.ProviderConfigRef.Name, - Alias: r.ProviderConfigRef.Alias, - } -} - -func decodeResourceBlock(block *hcl.Block) (*Resource, hcl.Diagnostics) { - r := &Resource{ - Mode: addrs.ManagedResourceMode, - Type: block.Labels[0], - Name: block.Labels[1], - DeclRange: block.DefRange, - TypeRange: block.LabelRanges[0], - Managed: &ManagedResource{}, - } - - content, remain, diags := block.Body.PartialContent(resourceBlockSchema) - r.Config = remain - - if !hclsyntax.ValidIdentifier(r.Type) { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid resource type name", - Detail: badIdentifierDetail, - Subject: &block.LabelRanges[0], - }) - } - if !hclsyntax.ValidIdentifier(r.Name) { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid resource name", - Detail: badIdentifierDetail, - Subject: &block.LabelRanges[1], - }) - } - - if attr, exists := content.Attributes["count"]; exists { - r.Count = attr.Expr - } - - if attr, exists := content.Attributes["for_each"]; exists { - r.ForEach = attr.Expr - // Cannot have count and for_each on the same resource block - if r.Count != nil { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Invalid combination of "count" and "for_each"`, - Detail: `The "count" and "for_each" meta-arguments are mutually-exclusive, only one should be used to be explicit about the number of resources to be created.`, - Subject: &attr.NameRange, - }) - } - } - - if attr, exists := content.Attributes["provider"]; exists { - var providerDiags hcl.Diagnostics - r.ProviderConfigRef, providerDiags = decodeProviderConfigRef(attr.Expr, "provider") - diags = append(diags, providerDiags...) - } - - if attr, exists := content.Attributes["depends_on"]; exists { - deps, depsDiags := decodeDependsOn(attr) - diags = append(diags, depsDiags...) - r.DependsOn = append(r.DependsOn, deps...) - } - - var seenLifecycle *hcl.Block - var seenConnection *hcl.Block - for _, block := range content.Blocks { - switch block.Type { - case "lifecycle": - if seenLifecycle != nil { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Duplicate lifecycle block", - Detail: fmt.Sprintf("This resource already has a lifecycle block at %s.", seenLifecycle.DefRange), - Subject: &block.DefRange, - }) - continue - } - seenLifecycle = block - - lcContent, lcDiags := block.Body.Content(resourceLifecycleBlockSchema) - diags = append(diags, lcDiags...) - - if attr, exists := lcContent.Attributes["create_before_destroy"]; exists { - valDiags := gohcl.DecodeExpression(attr.Expr, nil, &r.Managed.CreateBeforeDestroy) - diags = append(diags, valDiags...) - r.Managed.CreateBeforeDestroySet = true - } - - if attr, exists := lcContent.Attributes["prevent_destroy"]; exists { - valDiags := gohcl.DecodeExpression(attr.Expr, nil, &r.Managed.PreventDestroy) - diags = append(diags, valDiags...) - r.Managed.PreventDestroySet = true - } - - if attr, exists := lcContent.Attributes["ignore_changes"]; exists { - - // ignore_changes can either be a list of relative traversals - // or it can be just the keyword "all" to ignore changes to this - // resource entirely. - // ignore_changes = [ami, instance_type] - // ignore_changes = all - // We also allow two legacy forms for compatibility with earlier - // versions: - // ignore_changes = ["ami", "instance_type"] - // ignore_changes = ["*"] - - kw := hcl.ExprAsKeyword(attr.Expr) - - switch { - case kw == "all": - r.Managed.IgnoreAllChanges = true - default: - exprs, listDiags := hcl.ExprList(attr.Expr) - diags = append(diags, listDiags...) - - var ignoreAllRange hcl.Range - - for _, expr := range exprs { - - // our expr might be the literal string "*", which - // we accept as a deprecated way of saying "all". - if shimIsIgnoreChangesStar(expr) { - r.Managed.IgnoreAllChanges = true - ignoreAllRange = expr.Range() - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagWarning, - Summary: "Deprecated ignore_changes wildcard", - Detail: "The [\"*\"] form of ignore_changes wildcard is deprecated. Use \"ignore_changes = all\" to ignore changes to all attributes.", - Subject: attr.Expr.Range().Ptr(), - }) - continue - } - - expr, shimDiags := shimTraversalInString(expr, false) - diags = append(diags, shimDiags...) - - traversal, travDiags := hcl.RelTraversalForExpr(expr) - diags = append(diags, travDiags...) - if len(traversal) != 0 { - r.Managed.IgnoreChanges = append(r.Managed.IgnoreChanges, traversal) - } - } - - if r.Managed.IgnoreAllChanges && len(r.Managed.IgnoreChanges) != 0 { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid ignore_changes ruleset", - Detail: "Cannot mix wildcard string \"*\" with non-wildcard references.", - Subject: &ignoreAllRange, - Context: attr.Expr.Range().Ptr(), - }) - } - - } - - } - - case "connection": - if seenConnection != nil { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Duplicate connection block", - Detail: fmt.Sprintf("This resource already has a connection block at %s.", seenConnection.DefRange), - Subject: &block.DefRange, - }) - continue - } - seenConnection = block - - r.Managed.Connection = &Connection{ - Config: block.Body, - DeclRange: block.DefRange, - } - - case "provisioner": - pv, pvDiags := decodeProvisionerBlock(block) - diags = append(diags, pvDiags...) - if pv != nil { - r.Managed.Provisioners = append(r.Managed.Provisioners, pv) - } - - default: - // Any other block types are ones we've reserved for future use, - // so they get a generic message. - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Reserved block type name in resource block", - Detail: fmt.Sprintf("The block type name %q is reserved for use by Terraform in a future version.", block.Type), - Subject: &block.TypeRange, - }) - } - } - - return r, diags -} - -func decodeDataBlock(block *hcl.Block) (*Resource, hcl.Diagnostics) { - r := &Resource{ - Mode: addrs.DataResourceMode, - Type: block.Labels[0], - Name: block.Labels[1], - DeclRange: block.DefRange, - TypeRange: block.LabelRanges[0], - } - - content, remain, diags := block.Body.PartialContent(dataBlockSchema) - r.Config = remain - - if !hclsyntax.ValidIdentifier(r.Type) { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid data source name", - Detail: badIdentifierDetail, - Subject: &block.LabelRanges[0], - }) - } - if !hclsyntax.ValidIdentifier(r.Name) { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid data resource name", - Detail: badIdentifierDetail, - Subject: &block.LabelRanges[1], - }) - } - - if attr, exists := content.Attributes["count"]; exists { - r.Count = attr.Expr - } - - if attr, exists := content.Attributes["for_each"]; exists { - r.ForEach = attr.Expr - // Cannot have count and for_each on the same data block - if r.Count != nil { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Invalid combination of "count" and "for_each"`, - Detail: `The "count" and "for_each" meta-arguments are mutually-exclusive, only one should be used to be explicit about the number of resources to be created.`, - Subject: &attr.NameRange, - }) - } - } - - if attr, exists := content.Attributes["provider"]; exists { - var providerDiags hcl.Diagnostics - r.ProviderConfigRef, providerDiags = decodeProviderConfigRef(attr.Expr, "provider") - diags = append(diags, providerDiags...) - } - - if attr, exists := content.Attributes["depends_on"]; exists { - deps, depsDiags := decodeDependsOn(attr) - diags = append(diags, depsDiags...) - r.DependsOn = append(r.DependsOn, deps...) - } - - for _, block := range content.Blocks { - // All of the block types we accept are just reserved for future use, but some get a specialized error message. - switch block.Type { - case "lifecycle": - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Unsupported lifecycle block", - Detail: "Data resources do not have lifecycle settings, so a lifecycle block is not allowed.", - Subject: &block.DefRange, - }) - default: - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Reserved block type name in data block", - Detail: fmt.Sprintf("The block type name %q is reserved for use by Terraform in a future version.", block.Type), - Subject: &block.TypeRange, - }) - } - } - - return r, diags -} - -type ProviderConfigRef struct { - Name string - NameRange hcl.Range - Alias string - AliasRange *hcl.Range // nil if alias not set -} - -func decodeProviderConfigRef(expr hcl.Expression, argName string) (*ProviderConfigRef, hcl.Diagnostics) { - var diags hcl.Diagnostics - - var shimDiags hcl.Diagnostics - expr, shimDiags = shimTraversalInString(expr, false) - diags = append(diags, shimDiags...) - - traversal, travDiags := hcl.AbsTraversalForExpr(expr) - - // AbsTraversalForExpr produces only generic errors, so we'll discard - // the errors given and produce our own with extra context. If we didn't - // get any errors then we might still have warnings, though. - if !travDiags.HasErrors() { - diags = append(diags, travDiags...) - } - - if len(traversal) < 1 || len(traversal) > 2 { - // A provider reference was given as a string literal in the legacy - // configuration language and there are lots of examples out there - // showing that usage, so we'll sniff for that situation here and - // produce a specialized error message for it to help users find - // the new correct form. - if exprIsNativeQuotedString(expr) { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid provider configuration reference", - Detail: "A provider configuration reference must not be given in quotes.", - Subject: expr.Range().Ptr(), - }) - return nil, diags - } - - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid provider configuration reference", - Detail: fmt.Sprintf("The %s argument requires a provider type name, optionally followed by a period and then a configuration alias.", argName), - Subject: expr.Range().Ptr(), - }) - return nil, diags - } - - ret := &ProviderConfigRef{ - Name: traversal.RootName(), - NameRange: traversal[0].SourceRange(), - } - - if len(traversal) > 1 { - aliasStep, ok := traversal[1].(hcl.TraverseAttr) - if !ok { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid provider configuration reference", - Detail: "Provider name must either stand alone or be followed by a period and then a configuration alias.", - Subject: traversal[1].SourceRange().Ptr(), - }) - return ret, diags - } - - ret.Alias = aliasStep.Name - ret.AliasRange = aliasStep.SourceRange().Ptr() - } - - return ret, diags -} - -// Addr returns the provider config address corresponding to the receiving -// config reference. -// -// This is a trivial conversion, essentially just discarding the source -// location information and keeping just the addressing information. -func (r *ProviderConfigRef) Addr() addrs.ProviderConfig { - return addrs.ProviderConfig{ - Type: r.Name, - Alias: r.Alias, - } -} - -func (r *ProviderConfigRef) String() string { - if r == nil { - return "" - } - if r.Alias != "" { - return fmt.Sprintf("%s.%s", r.Name, r.Alias) - } - return r.Name -} - -var commonResourceAttributes = []hcl.AttributeSchema{ - { - Name: "count", - }, - { - Name: "for_each", - }, - { - Name: "provider", - }, - { - Name: "depends_on", - }, -} - -var resourceBlockSchema = &hcl.BodySchema{ - Attributes: commonResourceAttributes, - Blocks: []hcl.BlockHeaderSchema{ - {Type: "locals"}, // reserved for future use - {Type: "lifecycle"}, - {Type: "connection"}, - {Type: "provisioner", LabelNames: []string{"type"}}, - }, -} - -var dataBlockSchema = &hcl.BodySchema{ - Attributes: commonResourceAttributes, - Blocks: []hcl.BlockHeaderSchema{ - {Type: "lifecycle"}, // reserved for future use - {Type: "locals"}, // reserved for future use - }, -} - -var resourceLifecycleBlockSchema = &hcl.BodySchema{ - Attributes: []hcl.AttributeSchema{ - { - Name: "create_before_destroy", - }, - { - Name: "prevent_destroy", - }, - { - Name: "ignore_changes", - }, - }, -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/synth_body.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/synth_body.go deleted file mode 100644 index cd914e5d..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/synth_body.go +++ /dev/null @@ -1,118 +0,0 @@ -package configs - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" - "github.com/zclconf/go-cty/cty" -) - -// SynthBody produces a synthetic hcl.Body that behaves as if it had attributes -// corresponding to the elements given in the values map. -// -// This is useful in situations where, for example, values provided on the -// command line can override values given in configuration, using MergeBodies. -// -// The given filename is used in case any diagnostics are returned. Since -// the created body is synthetic, it is likely that this will not be a "real" -// filename. For example, if from a command line argument it could be -// a representation of that argument's name, such as "-var=...". -func SynthBody(filename string, values map[string]cty.Value) hcl.Body { - return synthBody{ - Filename: filename, - Values: values, - } -} - -type synthBody struct { - Filename string - Values map[string]cty.Value -} - -func (b synthBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) { - content, remain, diags := b.PartialContent(schema) - remainS := remain.(synthBody) - for name := range remainS.Values { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Unsupported attribute", - Detail: fmt.Sprintf("An attribute named %q is not expected here.", name), - Subject: b.synthRange().Ptr(), - }) - } - return content, diags -} - -func (b synthBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) { - var diags hcl.Diagnostics - content := &hcl.BodyContent{ - Attributes: make(hcl.Attributes), - MissingItemRange: b.synthRange(), - } - - remainValues := make(map[string]cty.Value) - for attrName, val := range b.Values { - remainValues[attrName] = val - } - - for _, attrS := range schema.Attributes { - delete(remainValues, attrS.Name) - val, defined := b.Values[attrS.Name] - if !defined { - if attrS.Required { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Missing required attribute", - Detail: fmt.Sprintf("The attribute %q is required, but no definition was found.", attrS.Name), - Subject: b.synthRange().Ptr(), - }) - } - continue - } - content.Attributes[attrS.Name] = b.synthAttribute(attrS.Name, val) - } - - // We just ignore blocks altogether, because this body type never has - // nested blocks. - - remain := synthBody{ - Filename: b.Filename, - Values: remainValues, - } - - return content, remain, diags -} - -func (b synthBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) { - ret := make(hcl.Attributes) - for name, val := range b.Values { - ret[name] = b.synthAttribute(name, val) - } - return ret, nil -} - -func (b synthBody) MissingItemRange() hcl.Range { - return b.synthRange() -} - -func (b synthBody) synthAttribute(name string, val cty.Value) *hcl.Attribute { - rng := b.synthRange() - return &hcl.Attribute{ - Name: name, - Expr: &hclsyntax.LiteralValueExpr{ - Val: val, - SrcRange: rng, - }, - NameRange: rng, - Range: rng, - } -} - -func (b synthBody) synthRange() hcl.Range { - return hcl.Range{ - Filename: b.Filename, - Start: hcl.Pos{Line: 1, Column: 1}, - End: hcl.Pos{Line: 1, Column: 1}, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/util.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/util.go deleted file mode 100644 index e135546f..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/util.go +++ /dev/null @@ -1,63 +0,0 @@ -package configs - -import ( - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" -) - -// exprIsNativeQuotedString determines whether the given expression looks like -// it's a quoted string in the HCL native syntax. -// -// This should be used sparingly only for situations where our legacy HCL -// decoding would've expected a keyword or reference in quotes but our new -// decoding expects the keyword or reference to be provided directly as -// an identifier-based expression. -func exprIsNativeQuotedString(expr hcl.Expression) bool { - _, ok := expr.(*hclsyntax.TemplateExpr) - return ok -} - -// schemaForOverrides takes a *hcl.BodySchema and produces a new one that is -// equivalent except that any required attributes are forced to not be required. -// -// This is useful for dealing with "override" config files, which are allowed -// to omit things that they don't wish to override from the main configuration. -// -// The returned schema may have some pointers in common with the given schema, -// so neither the given schema nor the returned schema should be modified after -// using this function in order to avoid confusion. -// -// Overrides are rarely used, so it's recommended to just create the override -// schema on the fly only when it's needed, rather than storing it in a global -// variable as we tend to do for a primary schema. -func schemaForOverrides(schema *hcl.BodySchema) *hcl.BodySchema { - ret := &hcl.BodySchema{ - Attributes: make([]hcl.AttributeSchema, len(schema.Attributes)), - Blocks: schema.Blocks, - } - - for i, attrS := range schema.Attributes { - ret.Attributes[i] = attrS - ret.Attributes[i].Required = false - } - - return ret -} - -// schemaWithDynamic takes a *hcl.BodySchema and produces a new one that -// is equivalent except that it accepts an additional block type "dynamic" with -// a single label, used to recognize usage of the HCL dynamic block extension. -func schemaWithDynamic(schema *hcl.BodySchema) *hcl.BodySchema { - ret := &hcl.BodySchema{ - Attributes: schema.Attributes, - Blocks: make([]hcl.BlockHeaderSchema, len(schema.Blocks), len(schema.Blocks)+1), - } - - copy(ret.Blocks, schema.Blocks) - ret.Blocks = append(ret.Blocks, hcl.BlockHeaderSchema{ - Type: "dynamic", - LabelNames: []string{"type"}, - }) - - return ret -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/variable_type_hint.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/variable_type_hint.go deleted file mode 100644 index c02ad4b5..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/variable_type_hint.go +++ /dev/null @@ -1,45 +0,0 @@ -package configs - -// VariableTypeHint is an enumeration used for the Variable.TypeHint field, -// which is an incompletely-specified type for the variable which is used -// as a hint for whether a value provided in an ambiguous context (on the -// command line or in an environment variable) should be taken literally as a -// string or parsed as an HCL expression to produce a data structure. -// -// The type hint is applied to runtime values as well, but since it does not -// accurately describe a precise type it is not fully-sufficient to infer -// the dynamic type of a value passed through a variable. -// -// These hints use inaccurate terminology for historical reasons. Full details -// are in the documentation for each constant in this enumeration, but in -// summary: -// -// TypeHintString requires a primitive type -// TypeHintList requires a type that could be converted to a tuple -// TypeHintMap requires a type that could be converted to an object -type VariableTypeHint rune - -//go:generate go run golang.org/x/tools/cmd/stringer -type VariableTypeHint - -// TypeHintNone indicates the absence of a type hint. Values specified in -// ambiguous contexts will be treated as literal strings, as if TypeHintString -// were selected, but no runtime value checks will be applied. This is reasonable -// type hint for a module that is never intended to be used at the top-level -// of a configuration, since descendent modules never receive values from -// ambiguous contexts. -const TypeHintNone VariableTypeHint = 0 - -// TypeHintString spec indicates that a value provided in an ambiguous context -// should be treated as a literal string, and additionally requires that the -// runtime value for the variable is of a primitive type (string, number, bool). -const TypeHintString VariableTypeHint = 'S' - -// TypeHintList indicates that a value provided in an ambiguous context should -// be treated as an HCL expression, and additionally requires that the -// runtime value for the variable is of an tuple, list, or set type. -const TypeHintList VariableTypeHint = 'L' - -// TypeHintMap indicates that a value provided in an ambiguous context should -// be treated as an HCL expression, and additionally requires that the -// runtime value for the variable is of an object or map type. -const TypeHintMap VariableTypeHint = 'M' diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/variabletypehint_string.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/variabletypehint_string.go deleted file mode 100644 index 2b50428c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/variabletypehint_string.go +++ /dev/null @@ -1,39 +0,0 @@ -// Code generated by "stringer -type VariableTypeHint"; DO NOT EDIT. - -package configs - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[TypeHintNone-0] - _ = x[TypeHintString-83] - _ = x[TypeHintList-76] - _ = x[TypeHintMap-77] -} - -const ( - _VariableTypeHint_name_0 = "TypeHintNone" - _VariableTypeHint_name_1 = "TypeHintListTypeHintMap" - _VariableTypeHint_name_2 = "TypeHintString" -) - -var ( - _VariableTypeHint_index_1 = [...]uint8{0, 12, 23} -) - -func (i VariableTypeHint) String() string { - switch { - case i == 0: - return _VariableTypeHint_name_0 - case 76 <= i && i <= 77: - i -= 76 - return _VariableTypeHint_name_1[_VariableTypeHint_index_1[i]:_VariableTypeHint_index_1[i+1]] - case i == 83: - return _VariableTypeHint_name_2 - default: - return "VariableTypeHint(" + strconv.FormatInt(int64(i), 10) + ")" - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/version_constraint.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/version_constraint.go deleted file mode 100644 index 0f541dc7..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/configs/version_constraint.go +++ /dev/null @@ -1,71 +0,0 @@ -package configs - -import ( - "fmt" - - version "github.com/hashicorp/go-version" - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" -) - -// VersionConstraint represents a version constraint on some resource -// (e.g. Terraform Core, a provider, a module, ...) that carries with it -// a source range so that a helpful diagnostic can be printed in the event -// that a particular constraint does not match. -type VersionConstraint struct { - Required version.Constraints - DeclRange hcl.Range -} - -func decodeVersionConstraint(attr *hcl.Attribute) (VersionConstraint, hcl.Diagnostics) { - ret := VersionConstraint{ - DeclRange: attr.Range, - } - - val, diags := attr.Expr.Value(nil) - if diags.HasErrors() { - return ret, diags - } - var err error - val, err = convert.Convert(val, cty.String) - if err != nil { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid version constraint", - Detail: fmt.Sprintf("A string value is required for %s.", attr.Name), - Subject: attr.Expr.Range().Ptr(), - }) - return ret, diags - } - - if val.IsNull() { - // A null version constraint is strange, but we'll just treat it - // like an empty constraint set. - return ret, diags - } - - if !val.IsWhollyKnown() { - // If there is a syntax error, HCL sets the value of the given attribute - // to cty.DynamicVal. A diagnostic for the syntax error will already - // bubble up, so we will move forward gracefully here. - return ret, diags - } - - constraintStr := val.AsString() - constraints, err := version.NewConstraint(constraintStr) - if err != nil { - // NewConstraint doesn't return user-friendly errors, so we'll just - // ignore the provided error and produce our own generic one. - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid version constraint", - Detail: "This string does not use correct version constraint syntax.", // Not very actionable :( - Subject: attr.Expr.Range().Ptr(), - }) - return ret, diags - } - - ret.Required = constraints - return ret, diags -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/dag.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/dag.go deleted file mode 100644 index a150af96..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/dag.go +++ /dev/null @@ -1,301 +0,0 @@ -package dag - -import ( - "fmt" - "sort" - "strings" - - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - - "github.com/hashicorp/go-multierror" -) - -// AcyclicGraph is a specialization of Graph that cannot have cycles. With -// this property, we get the property of sane graph traversal. -type AcyclicGraph struct { - Graph -} - -// WalkFunc is the callback used for walking the graph. -type WalkFunc func(Vertex) tfdiags.Diagnostics - -// DepthWalkFunc is a walk function that also receives the current depth of the -// walk as an argument -type DepthWalkFunc func(Vertex, int) error - -func (g *AcyclicGraph) DirectedGraph() Grapher { - return g -} - -// Returns a Set that includes every Vertex yielded by walking down from the -// provided starting Vertex v. -func (g *AcyclicGraph) Ancestors(v Vertex) (*Set, error) { - s := new(Set) - start := AsVertexList(g.DownEdges(v)) - memoFunc := func(v Vertex, d int) error { - s.Add(v) - return nil - } - - if err := g.DepthFirstWalk(start, memoFunc); err != nil { - return nil, err - } - - return s, nil -} - -// Returns a Set that includes every Vertex yielded by walking up from the -// provided starting Vertex v. -func (g *AcyclicGraph) Descendents(v Vertex) (*Set, error) { - s := new(Set) - start := AsVertexList(g.UpEdges(v)) - memoFunc := func(v Vertex, d int) error { - s.Add(v) - return nil - } - - if err := g.ReverseDepthFirstWalk(start, memoFunc); err != nil { - return nil, err - } - - return s, nil -} - -// Root returns the root of the DAG, or an error. -// -// Complexity: O(V) -func (g *AcyclicGraph) Root() (Vertex, error) { - roots := make([]Vertex, 0, 1) - for _, v := range g.Vertices() { - if g.UpEdges(v).Len() == 0 { - roots = append(roots, v) - } - } - - if len(roots) > 1 { - // TODO(mitchellh): make this error message a lot better - return nil, fmt.Errorf("multiple roots: %#v", roots) - } - - if len(roots) == 0 { - return nil, fmt.Errorf("no roots found") - } - - return roots[0], nil -} - -// TransitiveReduction performs the transitive reduction of graph g in place. -// The transitive reduction of a graph is a graph with as few edges as -// possible with the same reachability as the original graph. This means -// that if there are three nodes A => B => C, and A connects to both -// B and C, and B connects to C, then the transitive reduction is the -// same graph with only a single edge between A and B, and a single edge -// between B and C. -// -// The graph must be valid for this operation to behave properly. If -// Validate() returns an error, the behavior is undefined and the results -// will likely be unexpected. -// -// Complexity: O(V(V+E)), or asymptotically O(VE) -func (g *AcyclicGraph) TransitiveReduction() { - // For each vertex u in graph g, do a DFS starting from each vertex - // v such that the edge (u,v) exists (v is a direct descendant of u). - // - // For each v-prime reachable from v, remove the edge (u, v-prime). - defer g.debug.BeginOperation("TransitiveReduction", "").End("") - - for _, u := range g.Vertices() { - uTargets := g.DownEdges(u) - vs := AsVertexList(g.DownEdges(u)) - - g.depthFirstWalk(vs, false, func(v Vertex, d int) error { - shared := uTargets.Intersection(g.DownEdges(v)) - for _, vPrime := range AsVertexList(shared) { - g.RemoveEdge(BasicEdge(u, vPrime)) - } - - return nil - }) - } -} - -// Validate validates the DAG. A DAG is valid if it has a single root -// with no cycles. -func (g *AcyclicGraph) Validate() error { - if _, err := g.Root(); err != nil { - return err - } - - // Look for cycles of more than 1 component - var err error - cycles := g.Cycles() - if len(cycles) > 0 { - for _, cycle := range cycles { - cycleStr := make([]string, len(cycle)) - for j, vertex := range cycle { - cycleStr[j] = VertexName(vertex) - } - - err = multierror.Append(err, fmt.Errorf( - "Cycle: %s", strings.Join(cycleStr, ", "))) - } - } - - // Look for cycles to self - for _, e := range g.Edges() { - if e.Source() == e.Target() { - err = multierror.Append(err, fmt.Errorf( - "Self reference: %s", VertexName(e.Source()))) - } - } - - return err -} - -func (g *AcyclicGraph) Cycles() [][]Vertex { - var cycles [][]Vertex - for _, cycle := range StronglyConnected(&g.Graph) { - if len(cycle) > 1 { - cycles = append(cycles, cycle) - } - } - return cycles -} - -// Walk walks the graph, calling your callback as each node is visited. -// This will walk nodes in parallel if it can. The resulting diagnostics -// contains problems from all graphs visited, in no particular order. -func (g *AcyclicGraph) Walk(cb WalkFunc) tfdiags.Diagnostics { - defer g.debug.BeginOperation(typeWalk, "").End("") - - w := &Walker{Callback: cb, Reverse: true} - w.Update(g) - return w.Wait() -} - -// simple convenience helper for converting a dag.Set to a []Vertex -func AsVertexList(s *Set) []Vertex { - rawList := s.List() - vertexList := make([]Vertex, len(rawList)) - for i, raw := range rawList { - vertexList[i] = raw.(Vertex) - } - return vertexList -} - -type vertexAtDepth struct { - Vertex Vertex - Depth int -} - -// depthFirstWalk does a depth-first walk of the graph starting from -// the vertices in start. -func (g *AcyclicGraph) DepthFirstWalk(start []Vertex, f DepthWalkFunc) error { - return g.depthFirstWalk(start, true, f) -} - -// This internal method provides the option of not sorting the vertices during -// the walk, which we use for the Transitive reduction. -// Some configurations can lead to fully-connected subgraphs, which makes our -// transitive reduction algorithm O(n^3). This is still passable for the size -// of our graphs, but the additional n^2 sort operations would make this -// uncomputable in a reasonable amount of time. -func (g *AcyclicGraph) depthFirstWalk(start []Vertex, sorted bool, f DepthWalkFunc) error { - defer g.debug.BeginOperation(typeDepthFirstWalk, "").End("") - - seen := make(map[Vertex]struct{}) - frontier := make([]*vertexAtDepth, len(start)) - for i, v := range start { - frontier[i] = &vertexAtDepth{ - Vertex: v, - Depth: 0, - } - } - for len(frontier) > 0 { - // Pop the current vertex - n := len(frontier) - current := frontier[n-1] - frontier = frontier[:n-1] - - // Check if we've seen this already and return... - if _, ok := seen[current.Vertex]; ok { - continue - } - seen[current.Vertex] = struct{}{} - - // Visit the current node - if err := f(current.Vertex, current.Depth); err != nil { - return err - } - - // Visit targets of this in a consistent order. - targets := AsVertexList(g.DownEdges(current.Vertex)) - - if sorted { - sort.Sort(byVertexName(targets)) - } - - for _, t := range targets { - frontier = append(frontier, &vertexAtDepth{ - Vertex: t, - Depth: current.Depth + 1, - }) - } - } - - return nil -} - -// reverseDepthFirstWalk does a depth-first walk _up_ the graph starting from -// the vertices in start. -func (g *AcyclicGraph) ReverseDepthFirstWalk(start []Vertex, f DepthWalkFunc) error { - defer g.debug.BeginOperation(typeReverseDepthFirstWalk, "").End("") - - seen := make(map[Vertex]struct{}) - frontier := make([]*vertexAtDepth, len(start)) - for i, v := range start { - frontier[i] = &vertexAtDepth{ - Vertex: v, - Depth: 0, - } - } - for len(frontier) > 0 { - // Pop the current vertex - n := len(frontier) - current := frontier[n-1] - frontier = frontier[:n-1] - - // Check if we've seen this already and return... - if _, ok := seen[current.Vertex]; ok { - continue - } - seen[current.Vertex] = struct{}{} - - // Add next set of targets in a consistent order. - targets := AsVertexList(g.UpEdges(current.Vertex)) - sort.Sort(byVertexName(targets)) - for _, t := range targets { - frontier = append(frontier, &vertexAtDepth{ - Vertex: t, - Depth: current.Depth + 1, - }) - } - - // Visit the current node - if err := f(current.Vertex, current.Depth); err != nil { - return err - } - } - - return nil -} - -// byVertexName implements sort.Interface so a list of Vertices can be sorted -// consistently by their VertexName -type byVertexName []Vertex - -func (b byVertexName) Len() int { return len(b) } -func (b byVertexName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } -func (b byVertexName) Less(i, j int) bool { - return VertexName(b[i]) < VertexName(b[j]) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/dot.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/dot.go deleted file mode 100644 index 65a351b6..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/dot.go +++ /dev/null @@ -1,278 +0,0 @@ -package dag - -import ( - "bytes" - "fmt" - "sort" - "strings" -) - -// DotOpts are the options for generating a dot formatted Graph. -type DotOpts struct { - // Allows some nodes to decide to only show themselves when the user has - // requested the "verbose" graph. - Verbose bool - - // Highlight Cycles - DrawCycles bool - - // How many levels to expand modules as we draw - MaxDepth int - - // use this to keep the cluster_ naming convention from the previous dot writer - cluster bool -} - -// GraphNodeDotter can be implemented by a node to cause it to be included -// in the dot graph. The Dot method will be called which is expected to -// return a representation of this node. -type GraphNodeDotter interface { - // Dot is called to return the dot formatting for the node. - // The first parameter is the title of the node. - // The second parameter includes user-specified options that affect the dot - // graph. See GraphDotOpts below for details. - DotNode(string, *DotOpts) *DotNode -} - -// DotNode provides a structure for Vertices to return in order to specify their -// dot format. -type DotNode struct { - Name string - Attrs map[string]string -} - -// Returns the DOT representation of this Graph. -func (g *marshalGraph) Dot(opts *DotOpts) []byte { - if opts == nil { - opts = &DotOpts{ - DrawCycles: true, - MaxDepth: -1, - Verbose: true, - } - } - - var w indentWriter - w.WriteString("digraph {\n") - w.Indent() - - // some dot defaults - w.WriteString(`compound = "true"` + "\n") - w.WriteString(`newrank = "true"` + "\n") - - // the top level graph is written as the first subgraph - w.WriteString(`subgraph "root" {` + "\n") - g.writeBody(opts, &w) - - // cluster isn't really used other than for naming purposes in some graphs - opts.cluster = opts.MaxDepth != 0 - maxDepth := opts.MaxDepth - if maxDepth == 0 { - maxDepth = -1 - } - - for _, s := range g.Subgraphs { - g.writeSubgraph(s, opts, maxDepth, &w) - } - - w.Unindent() - w.WriteString("}\n") - return w.Bytes() -} - -func (v *marshalVertex) dot(g *marshalGraph, opts *DotOpts) []byte { - var buf bytes.Buffer - graphName := g.Name - if graphName == "" { - graphName = "root" - } - - name := v.Name - attrs := v.Attrs - if v.graphNodeDotter != nil { - node := v.graphNodeDotter.DotNode(name, opts) - if node == nil { - return []byte{} - } - - newAttrs := make(map[string]string) - for k, v := range attrs { - newAttrs[k] = v - } - for k, v := range node.Attrs { - newAttrs[k] = v - } - - name = node.Name - attrs = newAttrs - } - - buf.WriteString(fmt.Sprintf(`"[%s] %s"`, graphName, name)) - writeAttrs(&buf, attrs) - buf.WriteByte('\n') - - return buf.Bytes() -} - -func (e *marshalEdge) dot(g *marshalGraph) string { - var buf bytes.Buffer - graphName := g.Name - if graphName == "" { - graphName = "root" - } - - sourceName := g.vertexByID(e.Source).Name - targetName := g.vertexByID(e.Target).Name - s := fmt.Sprintf(`"[%s] %s" -> "[%s] %s"`, graphName, sourceName, graphName, targetName) - buf.WriteString(s) - writeAttrs(&buf, e.Attrs) - - return buf.String() -} - -func cycleDot(e *marshalEdge, g *marshalGraph) string { - return e.dot(g) + ` [color = "red", penwidth = "2.0"]` -} - -// Write the subgraph body. The is recursive, and the depth argument is used to -// record the current depth of iteration. -func (g *marshalGraph) writeSubgraph(sg *marshalGraph, opts *DotOpts, depth int, w *indentWriter) { - if depth == 0 { - return - } - depth-- - - name := sg.Name - if opts.cluster { - // we prefix with cluster_ to match the old dot output - name = "cluster_" + name - sg.Attrs["label"] = sg.Name - } - w.WriteString(fmt.Sprintf("subgraph %q {\n", name)) - sg.writeBody(opts, w) - - for _, sg := range sg.Subgraphs { - g.writeSubgraph(sg, opts, depth, w) - } -} - -func (g *marshalGraph) writeBody(opts *DotOpts, w *indentWriter) { - w.Indent() - - for _, as := range attrStrings(g.Attrs) { - w.WriteString(as + "\n") - } - - // list of Vertices that aren't to be included in the dot output - skip := map[string]bool{} - - for _, v := range g.Vertices { - if v.graphNodeDotter == nil { - skip[v.ID] = true - continue - } - - w.Write(v.dot(g, opts)) - } - - var dotEdges []string - - if opts.DrawCycles { - for _, c := range g.Cycles { - if len(c) < 2 { - continue - } - - for i, j := 0, 1; i < len(c); i, j = i+1, j+1 { - if j >= len(c) { - j = 0 - } - src := c[i] - tgt := c[j] - - if skip[src.ID] || skip[tgt.ID] { - continue - } - - e := &marshalEdge{ - Name: fmt.Sprintf("%s|%s", src.Name, tgt.Name), - Source: src.ID, - Target: tgt.ID, - Attrs: make(map[string]string), - } - - dotEdges = append(dotEdges, cycleDot(e, g)) - src = tgt - } - } - } - - for _, e := range g.Edges { - dotEdges = append(dotEdges, e.dot(g)) - } - - // srot these again to match the old output - sort.Strings(dotEdges) - - for _, e := range dotEdges { - w.WriteString(e + "\n") - } - - w.Unindent() - w.WriteString("}\n") -} - -func writeAttrs(buf *bytes.Buffer, attrs map[string]string) { - if len(attrs) > 0 { - buf.WriteString(" [") - buf.WriteString(strings.Join(attrStrings(attrs), ", ")) - buf.WriteString("]") - } -} - -func attrStrings(attrs map[string]string) []string { - strings := make([]string, 0, len(attrs)) - for k, v := range attrs { - strings = append(strings, fmt.Sprintf("%s = %q", k, v)) - } - sort.Strings(strings) - return strings -} - -// Provide a bytes.Buffer like structure, which will indent when starting a -// newline. -type indentWriter struct { - bytes.Buffer - level int -} - -func (w *indentWriter) indent() { - newline := []byte("\n") - if !bytes.HasSuffix(w.Bytes(), newline) { - return - } - for i := 0; i < w.level; i++ { - w.Buffer.WriteString("\t") - } -} - -// Indent increases indentation by 1 -func (w *indentWriter) Indent() { w.level++ } - -// Unindent decreases indentation by 1 -func (w *indentWriter) Unindent() { w.level-- } - -// the following methods intercecpt the byte.Buffer writes and insert the -// indentation when starting a new line. -func (w *indentWriter) Write(b []byte) (int, error) { - w.indent() - return w.Buffer.Write(b) -} - -func (w *indentWriter) WriteString(s string) (int, error) { - w.indent() - return w.Buffer.WriteString(s) -} -func (w *indentWriter) WriteByte(b byte) error { - w.indent() - return w.Buffer.WriteByte(b) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/edge.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/edge.go deleted file mode 100644 index f0d99ee3..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/edge.go +++ /dev/null @@ -1,37 +0,0 @@ -package dag - -import ( - "fmt" -) - -// Edge represents an edge in the graph, with a source and target vertex. -type Edge interface { - Source() Vertex - Target() Vertex - - Hashable -} - -// BasicEdge returns an Edge implementation that simply tracks the source -// and target given as-is. -func BasicEdge(source, target Vertex) Edge { - return &basicEdge{S: source, T: target} -} - -// basicEdge is a basic implementation of Edge that has the source and -// target vertex. -type basicEdge struct { - S, T Vertex -} - -func (e *basicEdge) Hashcode() interface{} { - return fmt.Sprintf("%p-%p", e.S, e.T) -} - -func (e *basicEdge) Source() Vertex { - return e.S -} - -func (e *basicEdge) Target() Vertex { - return e.T -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/graph.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/graph.go deleted file mode 100644 index e7517a20..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/graph.go +++ /dev/null @@ -1,391 +0,0 @@ -package dag - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "sort" -) - -// Graph is used to represent a dependency graph. -type Graph struct { - vertices *Set - edges *Set - downEdges map[interface{}]*Set - upEdges map[interface{}]*Set - - // JSON encoder for recording debug information - debug *encoder -} - -// Subgrapher allows a Vertex to be a Graph itself, by returning a Grapher. -type Subgrapher interface { - Subgraph() Grapher -} - -// A Grapher is any type that returns a Grapher, mainly used to identify -// dag.Graph and dag.AcyclicGraph. In the case of Graph and AcyclicGraph, they -// return themselves. -type Grapher interface { - DirectedGraph() Grapher -} - -// Vertex of the graph. -type Vertex interface{} - -// NamedVertex is an optional interface that can be implemented by Vertex -// to give it a human-friendly name that is used for outputting the graph. -type NamedVertex interface { - Vertex - Name() string -} - -func (g *Graph) DirectedGraph() Grapher { - return g -} - -// Vertices returns the list of all the vertices in the graph. -func (g *Graph) Vertices() []Vertex { - list := g.vertices.List() - result := make([]Vertex, len(list)) - for i, v := range list { - result[i] = v.(Vertex) - } - - return result -} - -// Edges returns the list of all the edges in the graph. -func (g *Graph) Edges() []Edge { - list := g.edges.List() - result := make([]Edge, len(list)) - for i, v := range list { - result[i] = v.(Edge) - } - - return result -} - -// EdgesFrom returns the list of edges from the given source. -func (g *Graph) EdgesFrom(v Vertex) []Edge { - var result []Edge - from := hashcode(v) - for _, e := range g.Edges() { - if hashcode(e.Source()) == from { - result = append(result, e) - } - } - - return result -} - -// EdgesTo returns the list of edges to the given target. -func (g *Graph) EdgesTo(v Vertex) []Edge { - var result []Edge - search := hashcode(v) - for _, e := range g.Edges() { - if hashcode(e.Target()) == search { - result = append(result, e) - } - } - - return result -} - -// HasVertex checks if the given Vertex is present in the graph. -func (g *Graph) HasVertex(v Vertex) bool { - return g.vertices.Include(v) -} - -// HasEdge checks if the given Edge is present in the graph. -func (g *Graph) HasEdge(e Edge) bool { - return g.edges.Include(e) -} - -// Add adds a vertex to the graph. This is safe to call multiple time with -// the same Vertex. -func (g *Graph) Add(v Vertex) Vertex { - g.init() - g.vertices.Add(v) - g.debug.Add(v) - return v -} - -// Remove removes a vertex from the graph. This will also remove any -// edges with this vertex as a source or target. -func (g *Graph) Remove(v Vertex) Vertex { - // Delete the vertex itself - g.vertices.Delete(v) - g.debug.Remove(v) - - // Delete the edges to non-existent things - for _, target := range g.DownEdges(v).List() { - g.RemoveEdge(BasicEdge(v, target)) - } - for _, source := range g.UpEdges(v).List() { - g.RemoveEdge(BasicEdge(source, v)) - } - - return nil -} - -// Replace replaces the original Vertex with replacement. If the original -// does not exist within the graph, then false is returned. Otherwise, true -// is returned. -func (g *Graph) Replace(original, replacement Vertex) bool { - // If we don't have the original, we can't do anything - if !g.vertices.Include(original) { - return false - } - - defer g.debug.BeginOperation("Replace", "").End("") - - // If they're the same, then don't do anything - if original == replacement { - return true - } - - // Add our new vertex, then copy all the edges - g.Add(replacement) - for _, target := range g.DownEdges(original).List() { - g.Connect(BasicEdge(replacement, target)) - } - for _, source := range g.UpEdges(original).List() { - g.Connect(BasicEdge(source, replacement)) - } - - // Remove our old vertex, which will also remove all the edges - g.Remove(original) - - return true -} - -// RemoveEdge removes an edge from the graph. -func (g *Graph) RemoveEdge(edge Edge) { - g.init() - g.debug.RemoveEdge(edge) - - // Delete the edge from the set - g.edges.Delete(edge) - - // Delete the up/down edges - if s, ok := g.downEdges[hashcode(edge.Source())]; ok { - s.Delete(edge.Target()) - } - if s, ok := g.upEdges[hashcode(edge.Target())]; ok { - s.Delete(edge.Source()) - } -} - -// DownEdges returns the outward edges from the source Vertex v. -func (g *Graph) DownEdges(v Vertex) *Set { - g.init() - return g.downEdges[hashcode(v)] -} - -// UpEdges returns the inward edges to the destination Vertex v. -func (g *Graph) UpEdges(v Vertex) *Set { - g.init() - return g.upEdges[hashcode(v)] -} - -// Connect adds an edge with the given source and target. This is safe to -// call multiple times with the same value. Note that the same value is -// verified through pointer equality of the vertices, not through the -// value of the edge itself. -func (g *Graph) Connect(edge Edge) { - g.init() - g.debug.Connect(edge) - - source := edge.Source() - target := edge.Target() - sourceCode := hashcode(source) - targetCode := hashcode(target) - - // Do we have this already? If so, don't add it again. - if s, ok := g.downEdges[sourceCode]; ok && s.Include(target) { - return - } - - // Add the edge to the set - g.edges.Add(edge) - - // Add the down edge - s, ok := g.downEdges[sourceCode] - if !ok { - s = new(Set) - g.downEdges[sourceCode] = s - } - s.Add(target) - - // Add the up edge - s, ok = g.upEdges[targetCode] - if !ok { - s = new(Set) - g.upEdges[targetCode] = s - } - s.Add(source) -} - -// String outputs some human-friendly output for the graph structure. -func (g *Graph) StringWithNodeTypes() string { - var buf bytes.Buffer - - // Build the list of node names and a mapping so that we can more - // easily alphabetize the output to remain deterministic. - vertices := g.Vertices() - names := make([]string, 0, len(vertices)) - mapping := make(map[string]Vertex, len(vertices)) - for _, v := range vertices { - name := VertexName(v) - names = append(names, name) - mapping[name] = v - } - sort.Strings(names) - - // Write each node in order... - for _, name := range names { - v := mapping[name] - targets := g.downEdges[hashcode(v)] - - buf.WriteString(fmt.Sprintf("%s - %T\n", name, v)) - - // Alphabetize dependencies - deps := make([]string, 0, targets.Len()) - targetNodes := make(map[string]Vertex) - for _, target := range targets.List() { - dep := VertexName(target) - deps = append(deps, dep) - targetNodes[dep] = target - } - sort.Strings(deps) - - // Write dependencies - for _, d := range deps { - buf.WriteString(fmt.Sprintf(" %s - %T\n", d, targetNodes[d])) - } - } - - return buf.String() -} - -// String outputs some human-friendly output for the graph structure. -func (g *Graph) String() string { - var buf bytes.Buffer - - // Build the list of node names and a mapping so that we can more - // easily alphabetize the output to remain deterministic. - vertices := g.Vertices() - names := make([]string, 0, len(vertices)) - mapping := make(map[string]Vertex, len(vertices)) - for _, v := range vertices { - name := VertexName(v) - names = append(names, name) - mapping[name] = v - } - sort.Strings(names) - - // Write each node in order... - for _, name := range names { - v := mapping[name] - targets := g.downEdges[hashcode(v)] - - buf.WriteString(fmt.Sprintf("%s\n", name)) - - // Alphabetize dependencies - deps := make([]string, 0, targets.Len()) - for _, target := range targets.List() { - deps = append(deps, VertexName(target)) - } - sort.Strings(deps) - - // Write dependencies - for _, d := range deps { - buf.WriteString(fmt.Sprintf(" %s\n", d)) - } - } - - return buf.String() -} - -func (g *Graph) init() { - if g.vertices == nil { - g.vertices = new(Set) - } - if g.edges == nil { - g.edges = new(Set) - } - if g.downEdges == nil { - g.downEdges = make(map[interface{}]*Set) - } - if g.upEdges == nil { - g.upEdges = make(map[interface{}]*Set) - } -} - -// Dot returns a dot-formatted representation of the Graph. -func (g *Graph) Dot(opts *DotOpts) []byte { - return newMarshalGraph("", g).Dot(opts) -} - -// MarshalJSON returns a JSON representation of the entire Graph. -func (g *Graph) MarshalJSON() ([]byte, error) { - dg := newMarshalGraph("root", g) - return json.MarshalIndent(dg, "", " ") -} - -// SetDebugWriter sets the io.Writer where the Graph will record debug -// information. After this is set, the graph will immediately encode itself to -// the stream, and continue to record all subsequent operations. -func (g *Graph) SetDebugWriter(w io.Writer) { - g.debug = &encoder{w: w} - g.debug.Encode(newMarshalGraph("root", g)) -} - -// DebugVertexInfo encodes arbitrary information about a vertex in the graph -// debug logs. -func (g *Graph) DebugVertexInfo(v Vertex, info string) { - va := newVertexInfo(typeVertexInfo, v, info) - g.debug.Encode(va) -} - -// DebugEdgeInfo encodes arbitrary information about an edge in the graph debug -// logs. -func (g *Graph) DebugEdgeInfo(e Edge, info string) { - ea := newEdgeInfo(typeEdgeInfo, e, info) - g.debug.Encode(ea) -} - -// DebugVisitInfo records a visit to a Vertex during a walk operation. -func (g *Graph) DebugVisitInfo(v Vertex, info string) { - vi := newVertexInfo(typeVisitInfo, v, info) - g.debug.Encode(vi) -} - -// DebugOperation marks the start of a set of graph transformations in -// the debug log, and returns a DebugOperationEnd func, which marks the end of -// the operation in the log. Additional information can be added to the log via -// the info parameter. -// -// The returned func's End method allows this method to be called from a single -// defer statement: -// defer g.DebugOperationBegin("OpName", "operating").End("") -// -// The returned function must be called to properly close the logical operation -// in the logs. -func (g *Graph) DebugOperation(operation string, info string) DebugOperationEnd { - return g.debug.BeginOperation(operation, info) -} - -// VertexName returns the name of a vertex. -func VertexName(raw Vertex) string { - switch v := raw.(type) { - case NamedVertex: - return v.Name() - case fmt.Stringer: - return fmt.Sprintf("%s", v) - default: - return fmt.Sprintf("%v", v) - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/marshal.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/marshal.go deleted file mode 100644 index 7b23ea9c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/marshal.go +++ /dev/null @@ -1,460 +0,0 @@ -package dag - -import ( - "encoding/json" - "fmt" - "io" - "log" - "reflect" - "sort" - "strconv" - "sync" -) - -const ( - typeOperation = "Operation" - typeTransform = "Transform" - typeWalk = "Walk" - typeDepthFirstWalk = "DepthFirstWalk" - typeReverseDepthFirstWalk = "ReverseDepthFirstWalk" - typeTransitiveReduction = "TransitiveReduction" - typeEdgeInfo = "EdgeInfo" - typeVertexInfo = "VertexInfo" - typeVisitInfo = "VisitInfo" -) - -// the marshal* structs are for serialization of the graph data. -type marshalGraph struct { - // Type is always "Graph", for identification as a top level object in the - // JSON stream. - Type string - - // Each marshal structure requires a unique ID so that it can be referenced - // by other structures. - ID string `json:",omitempty"` - - // Human readable name for this graph. - Name string `json:",omitempty"` - - // Arbitrary attributes that can be added to the output. - Attrs map[string]string `json:",omitempty"` - - // List of graph vertices, sorted by ID. - Vertices []*marshalVertex `json:",omitempty"` - - // List of edges, sorted by Source ID. - Edges []*marshalEdge `json:",omitempty"` - - // Any number of subgraphs. A subgraph itself is considered a vertex, and - // may be referenced by either end of an edge. - Subgraphs []*marshalGraph `json:",omitempty"` - - // Any lists of vertices that are included in cycles. - Cycles [][]*marshalVertex `json:",omitempty"` -} - -// The add, remove, connect, removeEdge methods mirror the basic Graph -// manipulations to reconstruct a marshalGraph from a debug log. -func (g *marshalGraph) add(v *marshalVertex) { - g.Vertices = append(g.Vertices, v) - sort.Sort(vertices(g.Vertices)) -} - -func (g *marshalGraph) remove(v *marshalVertex) { - for i, existing := range g.Vertices { - if v.ID == existing.ID { - g.Vertices = append(g.Vertices[:i], g.Vertices[i+1:]...) - return - } - } -} - -func (g *marshalGraph) connect(e *marshalEdge) { - g.Edges = append(g.Edges, e) - sort.Sort(edges(g.Edges)) -} - -func (g *marshalGraph) removeEdge(e *marshalEdge) { - for i, existing := range g.Edges { - if e.Source == existing.Source && e.Target == existing.Target { - g.Edges = append(g.Edges[:i], g.Edges[i+1:]...) - return - } - } -} - -func (g *marshalGraph) vertexByID(id string) *marshalVertex { - for _, v := range g.Vertices { - if id == v.ID { - return v - } - } - return nil -} - -type marshalVertex struct { - // Unique ID, used to reference this vertex from other structures. - ID string - - // Human readable name - Name string `json:",omitempty"` - - Attrs map[string]string `json:",omitempty"` - - // This is to help transition from the old Dot interfaces. We record if the - // node was a GraphNodeDotter here, so we can call it to get attributes. - graphNodeDotter GraphNodeDotter -} - -func newMarshalVertex(v Vertex) *marshalVertex { - dn, ok := v.(GraphNodeDotter) - if !ok { - dn = nil - } - - return &marshalVertex{ - ID: marshalVertexID(v), - Name: VertexName(v), - Attrs: make(map[string]string), - graphNodeDotter: dn, - } -} - -// vertices is a sort.Interface implementation for sorting vertices by ID -type vertices []*marshalVertex - -func (v vertices) Less(i, j int) bool { return v[i].Name < v[j].Name } -func (v vertices) Len() int { return len(v) } -func (v vertices) Swap(i, j int) { v[i], v[j] = v[j], v[i] } - -type marshalEdge struct { - // Human readable name - Name string - - // Source and Target Vertices by ID - Source string - Target string - - Attrs map[string]string `json:",omitempty"` -} - -func newMarshalEdge(e Edge) *marshalEdge { - return &marshalEdge{ - Name: fmt.Sprintf("%s|%s", VertexName(e.Source()), VertexName(e.Target())), - Source: marshalVertexID(e.Source()), - Target: marshalVertexID(e.Target()), - Attrs: make(map[string]string), - } -} - -// edges is a sort.Interface implementation for sorting edges by Source ID -type edges []*marshalEdge - -func (e edges) Less(i, j int) bool { return e[i].Name < e[j].Name } -func (e edges) Len() int { return len(e) } -func (e edges) Swap(i, j int) { e[i], e[j] = e[j], e[i] } - -// build a marshalGraph structure from a *Graph -func newMarshalGraph(name string, g *Graph) *marshalGraph { - mg := &marshalGraph{ - Type: "Graph", - Name: name, - Attrs: make(map[string]string), - } - - for _, v := range g.Vertices() { - id := marshalVertexID(v) - if sg, ok := marshalSubgrapher(v); ok { - smg := newMarshalGraph(VertexName(v), sg) - smg.ID = id - mg.Subgraphs = append(mg.Subgraphs, smg) - } - - mv := newMarshalVertex(v) - mg.Vertices = append(mg.Vertices, mv) - } - - sort.Sort(vertices(mg.Vertices)) - - for _, e := range g.Edges() { - mg.Edges = append(mg.Edges, newMarshalEdge(e)) - } - - sort.Sort(edges(mg.Edges)) - - for _, c := range (&AcyclicGraph{*g}).Cycles() { - var cycle []*marshalVertex - for _, v := range c { - mv := newMarshalVertex(v) - cycle = append(cycle, mv) - } - mg.Cycles = append(mg.Cycles, cycle) - } - - return mg -} - -// Attempt to return a unique ID for any vertex. -func marshalVertexID(v Vertex) string { - val := reflect.ValueOf(v) - switch val.Kind() { - case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer: - return strconv.Itoa(int(val.Pointer())) - case reflect.Interface: - return strconv.Itoa(int(val.InterfaceData()[1])) - } - - if v, ok := v.(Hashable); ok { - h := v.Hashcode() - if h, ok := h.(string); ok { - return h - } - } - - // fallback to a name, which we hope is unique. - return VertexName(v) - - // we could try harder by attempting to read the arbitrary value from the - // interface, but we shouldn't get here from terraform right now. -} - -// check for a Subgrapher, and return the underlying *Graph. -func marshalSubgrapher(v Vertex) (*Graph, bool) { - sg, ok := v.(Subgrapher) - if !ok { - return nil, false - } - - switch g := sg.Subgraph().DirectedGraph().(type) { - case *Graph: - return g, true - case *AcyclicGraph: - return &g.Graph, true - } - - return nil, false -} - -// The DebugOperationEnd func type provides a way to call an End function via a -// method call, allowing for the chaining of methods in a defer statement. -type DebugOperationEnd func(string) - -// End calls function e with the info parameter, marking the end of this -// operation in the logs. -func (e DebugOperationEnd) End(info string) { e(info) } - -// encoder provides methods to write debug data to an io.Writer, and is a noop -// when no writer is present -type encoder struct { - sync.Mutex - w io.Writer -} - -// Encode is analogous to json.Encoder.Encode -func (e *encoder) Encode(i interface{}) { - if e == nil || e.w == nil { - return - } - e.Lock() - defer e.Unlock() - - js, err := json.Marshal(i) - if err != nil { - log.Println("[ERROR] dag:", err) - return - } - js = append(js, '\n') - - _, err = e.w.Write(js) - if err != nil { - log.Println("[ERROR] dag:", err) - return - } -} - -func (e *encoder) Add(v Vertex) { - if e == nil { - return - } - e.Encode(marshalTransform{ - Type: typeTransform, - AddVertex: newMarshalVertex(v), - }) -} - -// Remove records the removal of Vertex v. -func (e *encoder) Remove(v Vertex) { - if e == nil { - return - } - e.Encode(marshalTransform{ - Type: typeTransform, - RemoveVertex: newMarshalVertex(v), - }) -} - -func (e *encoder) Connect(edge Edge) { - if e == nil { - return - } - e.Encode(marshalTransform{ - Type: typeTransform, - AddEdge: newMarshalEdge(edge), - }) -} - -func (e *encoder) RemoveEdge(edge Edge) { - if e == nil { - return - } - e.Encode(marshalTransform{ - Type: typeTransform, - RemoveEdge: newMarshalEdge(edge), - }) -} - -// BeginOperation marks the start of set of graph transformations, and returns -// an EndDebugOperation func to be called once the opration is complete. -func (e *encoder) BeginOperation(op string, info string) DebugOperationEnd { - if e == nil { - return func(string) {} - } - - e.Encode(marshalOperation{ - Type: typeOperation, - Begin: op, - Info: info, - }) - - return func(info string) { - e.Encode(marshalOperation{ - Type: typeOperation, - End: op, - Info: info, - }) - } -} - -// structure for recording graph transformations -type marshalTransform struct { - // Type: "Transform" - Type string - AddEdge *marshalEdge `json:",omitempty"` - RemoveEdge *marshalEdge `json:",omitempty"` - AddVertex *marshalVertex `json:",omitempty"` - RemoveVertex *marshalVertex `json:",omitempty"` -} - -func (t marshalTransform) Transform(g *marshalGraph) { - switch { - case t.AddEdge != nil: - g.connect(t.AddEdge) - case t.RemoveEdge != nil: - g.removeEdge(t.RemoveEdge) - case t.AddVertex != nil: - g.add(t.AddVertex) - case t.RemoveVertex != nil: - g.remove(t.RemoveVertex) - } -} - -// this structure allows us to decode any object in the json stream for -// inspection, then re-decode it into a proper struct if needed. -type streamDecode struct { - Type string - Map map[string]interface{} - JSON []byte -} - -func (s *streamDecode) UnmarshalJSON(d []byte) error { - s.JSON = d - err := json.Unmarshal(d, &s.Map) - if err != nil { - return err - } - - if t, ok := s.Map["Type"]; ok { - s.Type, _ = t.(string) - } - return nil -} - -// structure for recording the beginning and end of any multi-step -// transformations. These are informational, and not required to reproduce the -// graph state. -type marshalOperation struct { - Type string - Begin string `json:",omitempty"` - End string `json:",omitempty"` - Info string `json:",omitempty"` -} - -// decodeGraph decodes a marshalGraph from an encoded graph stream. -func decodeGraph(r io.Reader) (*marshalGraph, error) { - dec := json.NewDecoder(r) - - // a stream should always start with a graph - g := &marshalGraph{} - - err := dec.Decode(g) - if err != nil { - return nil, err - } - - // now replay any operations that occurred on the original graph - for dec.More() { - s := &streamDecode{} - err := dec.Decode(s) - if err != nil { - return g, err - } - - // the only Type we're concerned with here is Transform to complete the - // Graph - if s.Type != typeTransform { - continue - } - - t := &marshalTransform{} - err = json.Unmarshal(s.JSON, t) - if err != nil { - return g, err - } - t.Transform(g) - } - return g, nil -} - -// marshalVertexInfo allows encoding arbitrary information about the a single -// Vertex in the logs. These are accumulated for informational display while -// rebuilding the graph. -type marshalVertexInfo struct { - Type string - Vertex *marshalVertex - Info string -} - -func newVertexInfo(infoType string, v Vertex, info string) *marshalVertexInfo { - return &marshalVertexInfo{ - Type: infoType, - Vertex: newMarshalVertex(v), - Info: info, - } -} - -// marshalEdgeInfo allows encoding arbitrary information about the a single -// Edge in the logs. These are accumulated for informational display while -// rebuilding the graph. -type marshalEdgeInfo struct { - Type string - Edge *marshalEdge - Info string -} - -func newEdgeInfo(infoType string, e Edge, info string) *marshalEdgeInfo { - return &marshalEdgeInfo{ - Type: infoType, - Edge: newMarshalEdge(e), - Info: info, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/set.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/set.go deleted file mode 100644 index 92b42151..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/set.go +++ /dev/null @@ -1,123 +0,0 @@ -package dag - -import ( - "sync" -) - -// Set is a set data structure. -type Set struct { - m map[interface{}]interface{} - once sync.Once -} - -// Hashable is the interface used by set to get the hash code of a value. -// If this isn't given, then the value of the item being added to the set -// itself is used as the comparison value. -type Hashable interface { - Hashcode() interface{} -} - -// hashcode returns the hashcode used for set elements. -func hashcode(v interface{}) interface{} { - if h, ok := v.(Hashable); ok { - return h.Hashcode() - } - - return v -} - -// Add adds an item to the set -func (s *Set) Add(v interface{}) { - s.once.Do(s.init) - s.m[hashcode(v)] = v -} - -// Delete removes an item from the set. -func (s *Set) Delete(v interface{}) { - s.once.Do(s.init) - delete(s.m, hashcode(v)) -} - -// Include returns true/false of whether a value is in the set. -func (s *Set) Include(v interface{}) bool { - s.once.Do(s.init) - _, ok := s.m[hashcode(v)] - return ok -} - -// Intersection computes the set intersection with other. -func (s *Set) Intersection(other *Set) *Set { - result := new(Set) - if s == nil { - return result - } - if other != nil { - for _, v := range s.m { - if other.Include(v) { - result.Add(v) - } - } - } - - return result -} - -// Difference returns a set with the elements that s has but -// other doesn't. -func (s *Set) Difference(other *Set) *Set { - result := new(Set) - if s != nil { - for k, v := range s.m { - var ok bool - if other != nil { - _, ok = other.m[k] - } - if !ok { - result.Add(v) - } - } - } - - return result -} - -// Filter returns a set that contains the elements from the receiver -// where the given callback returns true. -func (s *Set) Filter(cb func(interface{}) bool) *Set { - result := new(Set) - - for _, v := range s.m { - if cb(v) { - result.Add(v) - } - } - - return result -} - -// Len is the number of items in the set. -func (s *Set) Len() int { - if s == nil { - return 0 - } - - return len(s.m) -} - -// List returns the list of set elements. -func (s *Set) List() []interface{} { - if s == nil { - return nil - } - - r := make([]interface{}, 0, len(s.m)) - for _, v := range s.m { - r = append(r, v) - } - - return r -} - -func (s *Set) init() { - s.m = make(map[interface{}]interface{}) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/tarjan.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/tarjan.go deleted file mode 100644 index 9d8b25ce..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/tarjan.go +++ /dev/null @@ -1,107 +0,0 @@ -package dag - -// StronglyConnected returns the list of strongly connected components -// within the Graph g. This information is primarily used by this package -// for cycle detection, but strongly connected components have widespread -// use. -func StronglyConnected(g *Graph) [][]Vertex { - vs := g.Vertices() - acct := sccAcct{ - NextIndex: 1, - VertexIndex: make(map[Vertex]int, len(vs)), - } - for _, v := range vs { - // Recurse on any non-visited nodes - if acct.VertexIndex[v] == 0 { - stronglyConnected(&acct, g, v) - } - } - return acct.SCC -} - -func stronglyConnected(acct *sccAcct, g *Graph, v Vertex) int { - // Initial vertex visit - index := acct.visit(v) - minIdx := index - - for _, raw := range g.DownEdges(v).List() { - target := raw.(Vertex) - targetIdx := acct.VertexIndex[target] - - // Recurse on successor if not yet visited - if targetIdx == 0 { - minIdx = min(minIdx, stronglyConnected(acct, g, target)) - } else if acct.inStack(target) { - // Check if the vertex is in the stack - minIdx = min(minIdx, targetIdx) - } - } - - // Pop the strongly connected components off the stack if - // this is a root vertex - if index == minIdx { - var scc []Vertex - for { - v2 := acct.pop() - scc = append(scc, v2) - if v2 == v { - break - } - } - - acct.SCC = append(acct.SCC, scc) - } - - return minIdx -} - -func min(a, b int) int { - if a <= b { - return a - } - return b -} - -// sccAcct is used ot pass around accounting information for -// the StronglyConnectedComponents algorithm -type sccAcct struct { - NextIndex int - VertexIndex map[Vertex]int - Stack []Vertex - SCC [][]Vertex -} - -// visit assigns an index and pushes a vertex onto the stack -func (s *sccAcct) visit(v Vertex) int { - idx := s.NextIndex - s.VertexIndex[v] = idx - s.NextIndex++ - s.push(v) - return idx -} - -// push adds a vertex to the stack -func (s *sccAcct) push(n Vertex) { - s.Stack = append(s.Stack, n) -} - -// pop removes a vertex from the stack -func (s *sccAcct) pop() Vertex { - n := len(s.Stack) - if n == 0 { - return nil - } - vertex := s.Stack[n-1] - s.Stack = s.Stack[:n-1] - return vertex -} - -// inStack checks if a vertex is in the stack -func (s *sccAcct) inStack(needle Vertex) bool { - for _, n := range s.Stack { - if n == needle { - return true - } - } - return false -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/walk.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/walk.go deleted file mode 100644 index 5ddf8ef3..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/dag/walk.go +++ /dev/null @@ -1,454 +0,0 @@ -package dag - -import ( - "errors" - "log" - "sync" - "time" - - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// Walker is used to walk every vertex of a graph in parallel. -// -// A vertex will only be walked when the dependencies of that vertex have -// been walked. If two vertices can be walked at the same time, they will be. -// -// Update can be called to update the graph. This can be called even during -// a walk, cahnging vertices/edges mid-walk. This should be done carefully. -// If a vertex is removed but has already been executed, the result of that -// execution (any error) is still returned by Wait. Changing or re-adding -// a vertex that has already executed has no effect. Changing edges of -// a vertex that has already executed has no effect. -// -// Non-parallelism can be enforced by introducing a lock in your callback -// function. However, the goroutine overhead of a walk will remain. -// Walker will create V*2 goroutines (one for each vertex, and dependency -// waiter for each vertex). In general this should be of no concern unless -// there are a huge number of vertices. -// -// The walk is depth first by default. This can be changed with the Reverse -// option. -// -// A single walker is only valid for one graph walk. After the walk is complete -// you must construct a new walker to walk again. State for the walk is never -// deleted in case vertices or edges are changed. -type Walker struct { - // Callback is what is called for each vertex - Callback WalkFunc - - // Reverse, if true, causes the source of an edge to depend on a target. - // When false (default), the target depends on the source. - Reverse bool - - // changeLock must be held to modify any of the fields below. Only Update - // should modify these fields. Modifying them outside of Update can cause - // serious problems. - changeLock sync.Mutex - vertices Set - edges Set - vertexMap map[Vertex]*walkerVertex - - // wait is done when all vertices have executed. It may become "undone" - // if new vertices are added. - wait sync.WaitGroup - - // diagsMap contains the diagnostics recorded so far for execution, - // and upstreamFailed contains all the vertices whose problems were - // caused by upstream failures, and thus whose diagnostics should be - // excluded from the final set. - // - // Readers and writers of either map must hold diagsLock. - diagsMap map[Vertex]tfdiags.Diagnostics - upstreamFailed map[Vertex]struct{} - diagsLock sync.Mutex -} - -type walkerVertex struct { - // These should only be set once on initialization and never written again. - // They are not protected by a lock since they don't need to be since - // they are write-once. - - // DoneCh is closed when this vertex has completed execution, regardless - // of success. - // - // CancelCh is closed when the vertex should cancel execution. If execution - // is already complete (DoneCh is closed), this has no effect. Otherwise, - // execution is cancelled as quickly as possible. - DoneCh chan struct{} - CancelCh chan struct{} - - // Dependency information. Any changes to any of these fields requires - // holding DepsLock. - // - // DepsCh is sent a single value that denotes whether the upstream deps - // were successful (no errors). Any value sent means that the upstream - // dependencies are complete. No other values will ever be sent again. - // - // DepsUpdateCh is closed when there is a new DepsCh set. - DepsCh chan bool - DepsUpdateCh chan struct{} - DepsLock sync.Mutex - - // Below is not safe to read/write in parallel. This behavior is - // enforced by changes only happening in Update. Nothing else should - // ever modify these. - deps map[Vertex]chan struct{} - depsCancelCh chan struct{} -} - -// Wait waits for the completion of the walk and returns diagnostics describing -// any problems that arose. Update should be called to populate the walk with -// vertices and edges prior to calling this. -// -// Wait will return as soon as all currently known vertices are complete. -// If you plan on calling Update with more vertices in the future, you -// should not call Wait until after this is done. -func (w *Walker) Wait() tfdiags.Diagnostics { - // Wait for completion - w.wait.Wait() - - var diags tfdiags.Diagnostics - w.diagsLock.Lock() - for v, vDiags := range w.diagsMap { - if _, upstream := w.upstreamFailed[v]; upstream { - // Ignore diagnostics for nodes that had failed upstreams, since - // the downstream diagnostics are likely to be redundant. - continue - } - diags = diags.Append(vDiags) - } - w.diagsLock.Unlock() - - return diags -} - -// Update updates the currently executing walk with the given graph. -// This will perform a diff of the vertices and edges and update the walker. -// Already completed vertices remain completed (including any errors during -// their execution). -// -// This returns immediately once the walker is updated; it does not wait -// for completion of the walk. -// -// Multiple Updates can be called in parallel. Update can be called at any -// time during a walk. -func (w *Walker) Update(g *AcyclicGraph) { - log.Print("[TRACE] dag/walk: updating graph") - var v, e *Set - if g != nil { - v, e = g.vertices, g.edges - } - - // Grab the change lock so no more updates happen but also so that - // no new vertices are executed during this time since we may be - // removing them. - w.changeLock.Lock() - defer w.changeLock.Unlock() - - // Initialize fields - if w.vertexMap == nil { - w.vertexMap = make(map[Vertex]*walkerVertex) - } - - // Calculate all our sets - newEdges := e.Difference(&w.edges) - oldEdges := w.edges.Difference(e) - newVerts := v.Difference(&w.vertices) - oldVerts := w.vertices.Difference(v) - - // Add the new vertices - for _, raw := range newVerts.List() { - v := raw.(Vertex) - - // Add to the waitgroup so our walk is not done until everything finishes - w.wait.Add(1) - - // Add to our own set so we know about it already - log.Printf("[TRACE] dag/walk: added new vertex: %q", VertexName(v)) - w.vertices.Add(raw) - - // Initialize the vertex info - info := &walkerVertex{ - DoneCh: make(chan struct{}), - CancelCh: make(chan struct{}), - deps: make(map[Vertex]chan struct{}), - } - - // Add it to the map and kick off the walk - w.vertexMap[v] = info - } - - // Remove the old vertices - for _, raw := range oldVerts.List() { - v := raw.(Vertex) - - // Get the vertex info so we can cancel it - info, ok := w.vertexMap[v] - if !ok { - // This vertex for some reason was never in our map. This - // shouldn't be possible. - continue - } - - // Cancel the vertex - close(info.CancelCh) - - // Delete it out of the map - delete(w.vertexMap, v) - - log.Printf("[TRACE] dag/walk: removed vertex: %q", VertexName(v)) - w.vertices.Delete(raw) - } - - // Add the new edges - var changedDeps Set - for _, raw := range newEdges.List() { - edge := raw.(Edge) - waiter, dep := w.edgeParts(edge) - - // Get the info for the waiter - waiterInfo, ok := w.vertexMap[waiter] - if !ok { - // Vertex doesn't exist... shouldn't be possible but ignore. - continue - } - - // Get the info for the dep - depInfo, ok := w.vertexMap[dep] - if !ok { - // Vertex doesn't exist... shouldn't be possible but ignore. - continue - } - - // Add the dependency to our waiter - waiterInfo.deps[dep] = depInfo.DoneCh - - // Record that the deps changed for this waiter - changedDeps.Add(waiter) - - log.Printf( - "[TRACE] dag/walk: added edge: %q waiting on %q", - VertexName(waiter), VertexName(dep)) - w.edges.Add(raw) - } - - // Process reoved edges - for _, raw := range oldEdges.List() { - edge := raw.(Edge) - waiter, dep := w.edgeParts(edge) - - // Get the info for the waiter - waiterInfo, ok := w.vertexMap[waiter] - if !ok { - // Vertex doesn't exist... shouldn't be possible but ignore. - continue - } - - // Delete the dependency from the waiter - delete(waiterInfo.deps, dep) - - // Record that the deps changed for this waiter - changedDeps.Add(waiter) - - log.Printf( - "[TRACE] dag/walk: removed edge: %q waiting on %q", - VertexName(waiter), VertexName(dep)) - w.edges.Delete(raw) - } - - // For each vertex with changed dependencies, we need to kick off - // a new waiter and notify the vertex of the changes. - for _, raw := range changedDeps.List() { - v := raw.(Vertex) - info, ok := w.vertexMap[v] - if !ok { - // Vertex doesn't exist... shouldn't be possible but ignore. - continue - } - - // Create a new done channel - doneCh := make(chan bool, 1) - - // Create the channel we close for cancellation - cancelCh := make(chan struct{}) - - // Build a new deps copy - deps := make(map[Vertex]<-chan struct{}) - for k, v := range info.deps { - deps[k] = v - } - - // Update the update channel - info.DepsLock.Lock() - if info.DepsUpdateCh != nil { - close(info.DepsUpdateCh) - } - info.DepsCh = doneCh - info.DepsUpdateCh = make(chan struct{}) - info.DepsLock.Unlock() - - // Cancel the older waiter - if info.depsCancelCh != nil { - close(info.depsCancelCh) - } - info.depsCancelCh = cancelCh - - log.Printf( - "[TRACE] dag/walk: dependencies changed for %q, sending new deps", - VertexName(v)) - - // Start the waiter - go w.waitDeps(v, deps, doneCh, cancelCh) - } - - // Start all the new vertices. We do this at the end so that all - // the edge waiters and changes are setup above. - for _, raw := range newVerts.List() { - v := raw.(Vertex) - go w.walkVertex(v, w.vertexMap[v]) - } -} - -// edgeParts returns the waiter and the dependency, in that order. -// The waiter is waiting on the dependency. -func (w *Walker) edgeParts(e Edge) (Vertex, Vertex) { - if w.Reverse { - return e.Source(), e.Target() - } - - return e.Target(), e.Source() -} - -// walkVertex walks a single vertex, waiting for any dependencies before -// executing the callback. -func (w *Walker) walkVertex(v Vertex, info *walkerVertex) { - // When we're done executing, lower the waitgroup count - defer w.wait.Done() - - // When we're done, always close our done channel - defer close(info.DoneCh) - - // Wait for our dependencies. We create a [closed] deps channel so - // that we can immediately fall through to load our actual DepsCh. - var depsSuccess bool - var depsUpdateCh chan struct{} - depsCh := make(chan bool, 1) - depsCh <- true - close(depsCh) - for { - select { - case <-info.CancelCh: - // Cancel - return - - case depsSuccess = <-depsCh: - // Deps complete! Mark as nil to trigger completion handling. - depsCh = nil - - case <-depsUpdateCh: - // New deps, reloop - } - - // Check if we have updated dependencies. This can happen if the - // dependencies were satisfied exactly prior to an Update occurring. - // In that case, we'd like to take into account new dependencies - // if possible. - info.DepsLock.Lock() - if info.DepsCh != nil { - depsCh = info.DepsCh - info.DepsCh = nil - } - if info.DepsUpdateCh != nil { - depsUpdateCh = info.DepsUpdateCh - } - info.DepsLock.Unlock() - - // If we still have no deps channel set, then we're done! - if depsCh == nil { - break - } - } - - // If we passed dependencies, we just want to check once more that - // we're not cancelled, since this can happen just as dependencies pass. - select { - case <-info.CancelCh: - // Cancelled during an update while dependencies completed. - return - default: - } - - // Run our callback or note that our upstream failed - var diags tfdiags.Diagnostics - var upstreamFailed bool - if depsSuccess { - log.Printf("[TRACE] dag/walk: visiting %q", VertexName(v)) - diags = w.Callback(v) - } else { - log.Printf("[TRACE] dag/walk: upstream of %q errored, so skipping", VertexName(v)) - // This won't be displayed to the user because we'll set upstreamFailed, - // but we need to ensure there's at least one error in here so that - // the failures will cascade downstream. - diags = diags.Append(errors.New("upstream dependencies failed")) - upstreamFailed = true - } - - // Record the result (we must do this after execution because we mustn't - // hold diagsLock while visiting a vertex.) - w.diagsLock.Lock() - if w.diagsMap == nil { - w.diagsMap = make(map[Vertex]tfdiags.Diagnostics) - } - w.diagsMap[v] = diags - if w.upstreamFailed == nil { - w.upstreamFailed = make(map[Vertex]struct{}) - } - if upstreamFailed { - w.upstreamFailed[v] = struct{}{} - } - w.diagsLock.Unlock() -} - -func (w *Walker) waitDeps( - v Vertex, - deps map[Vertex]<-chan struct{}, - doneCh chan<- bool, - cancelCh <-chan struct{}) { - - // For each dependency given to us, wait for it to complete - for dep, depCh := range deps { - DepSatisfied: - for { - select { - case <-depCh: - // Dependency satisfied! - break DepSatisfied - - case <-cancelCh: - // Wait cancelled. Note that we didn't satisfy dependencies - // so that anything waiting on us also doesn't run. - doneCh <- false - return - - case <-time.After(time.Second * 5): - log.Printf("[TRACE] dag/walk: vertex %q is waiting for %q", - VertexName(v), VertexName(dep)) - } - } - } - - // Dependencies satisfied! We need to check if any errored - w.diagsLock.Lock() - defer w.diagsLock.Unlock() - for dep := range deps { - if w.diagsMap[dep].HasErrors() { - // One of our dependencies failed, so return false - doneCh <- false - return - } - } - - // All dependencies satisfied and successful - doneCh <- true -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/diagutils/diagutils.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/diagutils/diagutils.go new file mode 100644 index 00000000..6b69e2f1 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/diagutils/diagutils.go @@ -0,0 +1,45 @@ +package diagutils + +import ( + "errors" + "fmt" + + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" +) + +type ErrorDiags diag.Diagnostics + +func (diags ErrorDiags) Errors() []error { + var es []error + for i := range diags { + if diags[i].Severity == diag.Error { + s := fmt.Sprintf("Error: %s", diags[i].Summary) + if diags[i].Detail != "" { + s = fmt.Sprintf("%s: %s", s, diags[i].Detail) + } + es = append(es, errors.New(s)) + } + } + return es +} + +func (diags ErrorDiags) Error() string { + return multierror.ListFormatFunc(diags.Errors()) +} + +type WarningDiags diag.Diagnostics + +func (diags WarningDiags) Warnings() []string { + var ws []string + for i := range diags { + if diags[i].Severity == diag.Warning { + s := fmt.Sprintf("Warning: %s", diags[i].Summary) + if diags[i].Detail != "" { + s = fmt.Sprintf("%s: %s", s, diags[i].Detail) + } + ws = append(ws, s) + } + } + return ws +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/earlyconfig/config.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/earlyconfig/config.go deleted file mode 100644 index b86bd792..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/earlyconfig/config.go +++ /dev/null @@ -1,63 +0,0 @@ -package earlyconfig - -import ( - version "github.com/hashicorp/go-version" - "github.com/hashicorp/terraform-config-inspect/tfconfig" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// A Config is a node in the tree of modules within a configuration. -// -// The module tree is constructed by following ModuleCall instances recursively -// through the root module transitively into descendent modules. -type Config struct { - // RootModule points to the Config for the root module within the same - // module tree as this module. If this module _is_ the root module then - // this is self-referential. - Root *Config - - // ParentModule points to the Config for the module that directly calls - // this module. If this is the root module then this field is nil. - Parent *Config - - // Path is a sequence of module logical names that traverse from the root - // module to this config. Path is empty for the root module. - // - // This should only be used to display paths to the end-user in rare cases - // where we are talking about the static module tree, before module calls - // have been resolved. In most cases, an addrs.ModuleInstance describing - // a node in the dynamic module tree is better, since it will then include - // any keys resulting from evaluating "count" and "for_each" arguments. - Path addrs.Module - - // ChildModules points to the Config for each of the direct child modules - // called from this module. The keys in this map match the keys in - // Module.ModuleCalls. - Children map[string]*Config - - // Module points to the object describing the configuration for the - // various elements (variables, resources, etc) defined by this module. - Module *tfconfig.Module - - // CallPos is the source position for the header of the module block that - // requested this module. - // - // This field is meaningless for the root module, where its contents are undefined. - CallPos tfconfig.SourcePos - - // SourceAddr is the source address that the referenced module was requested - // from, as specified in configuration. - // - // This field is meaningless for the root module, where its contents are undefined. - SourceAddr string - - // Version is the specific version that was selected for this module, - // based on version constraints given in configuration. - // - // This field is nil if the module was loaded from a non-registry source, - // since versions are not supported for other sources. - // - // This field is meaningless for the root module, where it will always - // be nil. - Version *version.Version -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/earlyconfig/config_build.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/earlyconfig/config_build.go deleted file mode 100644 index 3707f273..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/earlyconfig/config_build.go +++ /dev/null @@ -1,144 +0,0 @@ -package earlyconfig - -import ( - "fmt" - "sort" - "strings" - - version "github.com/hashicorp/go-version" - "github.com/hashicorp/terraform-config-inspect/tfconfig" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// BuildConfig constructs a Config from a root module by loading all of its -// descendent modules via the given ModuleWalker. -func BuildConfig(root *tfconfig.Module, walker ModuleWalker) (*Config, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - cfg := &Config{ - Module: root, - } - cfg.Root = cfg // Root module is self-referential. - cfg.Children, diags = buildChildModules(cfg, walker) - return cfg, diags -} - -func buildChildModules(parent *Config, walker ModuleWalker) (map[string]*Config, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - ret := map[string]*Config{} - calls := parent.Module.ModuleCalls - - // We'll sort the calls by their local names so that they'll appear in a - // predictable order in any logging that's produced during the walk. - callNames := make([]string, 0, len(calls)) - for k := range calls { - callNames = append(callNames, k) - } - sort.Strings(callNames) - - for _, callName := range callNames { - call := calls[callName] - path := make([]string, len(parent.Path)+1) - copy(path, parent.Path) - path[len(path)-1] = call.Name - - var vc version.Constraints - if strings.TrimSpace(call.Version) != "" { - var err error - vc, err = version.NewConstraint(call.Version) - if err != nil { - diags = diags.Append(wrapDiagnostic(tfconfig.Diagnostic{ - Severity: tfconfig.DiagError, - Summary: "Invalid version constraint", - Detail: fmt.Sprintf("Module %q (declared at %s line %d) has invalid version constraint %q: %s.", callName, call.Pos.Filename, call.Pos.Line, call.Version, err), - })) - continue - } - } - - req := ModuleRequest{ - Name: call.Name, - Path: path, - SourceAddr: call.Source, - VersionConstraints: vc, - Parent: parent, - CallPos: call.Pos, - } - - mod, ver, modDiags := walker.LoadModule(&req) - diags = append(diags, modDiags...) - if mod == nil { - // nil can be returned if the source address was invalid and so - // nothing could be loaded whatsoever. LoadModule should've - // returned at least one error diagnostic in that case. - continue - } - - child := &Config{ - Parent: parent, - Root: parent.Root, - Path: path, - Module: mod, - CallPos: call.Pos, - SourceAddr: call.Source, - Version: ver, - } - - child.Children, modDiags = buildChildModules(child, walker) - diags = diags.Append(modDiags) - - ret[call.Name] = child - } - - return ret, diags -} - -// ModuleRequest is used as part of the ModuleWalker interface used with -// function BuildConfig. -type ModuleRequest struct { - // Name is the "logical name" of the module call within configuration. - // This is provided in case the name is used as part of a storage key - // for the module, but implementations must otherwise treat it as an - // opaque string. It is guaranteed to have already been validated as an - // HCL identifier and UTF-8 encoded. - Name string - - // Path is a list of logical names that traverse from the root module to - // this module. This can be used, for example, to form a lookup key for - // each distinct module call in a configuration, allowing for multiple - // calls with the same name at different points in the tree. - Path addrs.Module - - // SourceAddr is the source address string provided by the user in - // configuration. - SourceAddr string - - // VersionConstraint is the version constraint applied to the module in - // configuration. - VersionConstraints version.Constraints - - // Parent is the partially-constructed module tree node that the loaded - // module will be added to. Callers may refer to any field of this - // structure except Children, which is still under construction when - // ModuleRequest objects are created and thus has undefined content. - // The main reason this is provided is so that full module paths can - // be constructed for uniqueness. - Parent *Config - - // CallRange is the source position for the header of the "module" block - // in configuration that prompted this request. - CallPos tfconfig.SourcePos -} - -// ModuleWalker is an interface used with BuildConfig. -type ModuleWalker interface { - LoadModule(req *ModuleRequest) (*tfconfig.Module, *version.Version, tfdiags.Diagnostics) -} - -// ModuleWalkerFunc is an implementation of ModuleWalker that directly wraps -// a callback function, for more convenient use of that interface. -type ModuleWalkerFunc func(req *ModuleRequest) (*tfconfig.Module, *version.Version, tfdiags.Diagnostics) - -func (f ModuleWalkerFunc) LoadModule(req *ModuleRequest) (*tfconfig.Module, *version.Version, tfdiags.Diagnostics) { - return f(req) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/earlyconfig/diagnostics.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/earlyconfig/diagnostics.go deleted file mode 100644 index b2e1807e..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/earlyconfig/diagnostics.go +++ /dev/null @@ -1,78 +0,0 @@ -package earlyconfig - -import ( - "fmt" - - "github.com/hashicorp/terraform-config-inspect/tfconfig" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -func wrapDiagnostics(diags tfconfig.Diagnostics) tfdiags.Diagnostics { - ret := make(tfdiags.Diagnostics, len(diags)) - for i, diag := range diags { - ret[i] = wrapDiagnostic(diag) - } - return ret -} - -func wrapDiagnostic(diag tfconfig.Diagnostic) tfdiags.Diagnostic { - return wrappedDiagnostic{ - d: diag, - } -} - -type wrappedDiagnostic struct { - d tfconfig.Diagnostic -} - -func (d wrappedDiagnostic) Severity() tfdiags.Severity { - switch d.d.Severity { - case tfconfig.DiagError: - return tfdiags.Error - case tfconfig.DiagWarning: - return tfdiags.Warning - default: - // Should never happen since there are no other severities - return 0 - } -} - -func (d wrappedDiagnostic) Description() tfdiags.Description { - // Since the inspect library doesn't produce precise source locations, - // we include the position information as part of the error message text. - // See the comment inside method "Source" for more information. - switch { - case d.d.Pos == nil: - return tfdiags.Description{ - Summary: d.d.Summary, - Detail: d.d.Detail, - } - case d.d.Detail != "": - return tfdiags.Description{ - Summary: d.d.Summary, - Detail: fmt.Sprintf("On %s line %d: %s", d.d.Pos.Filename, d.d.Pos.Line, d.d.Detail), - } - default: - return tfdiags.Description{ - Summary: fmt.Sprintf("%s (on %s line %d)", d.d.Summary, d.d.Pos.Filename, d.d.Pos.Line), - } - } -} - -func (d wrappedDiagnostic) Source() tfdiags.Source { - // Since the inspect library is constrained by the lowest common denominator - // between legacy HCL and modern HCL, it only returns ranges at whole-line - // granularity, and that isn't sufficient to populate a tfdiags.Source - // and so we'll just omit ranges altogether and include the line number in - // the Description text. - // - // Callers that want to return nicer errors should consider reacting to - // earlyconfig errors by attempting a follow-up parse with the normal - // config loader, which can produce more precise source location - // information. - return tfdiags.Source{} -} - -func (d wrappedDiagnostic) FromExpr() *tfdiags.FromExpr { - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/earlyconfig/doc.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/earlyconfig/doc.go deleted file mode 100644 index a9cf10f3..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/earlyconfig/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -// Package earlyconfig is a specialized alternative to the top-level "configs" -// package that does only shallow processing of configuration and is therefore -// able to be much more liberal than the full config loader in what it accepts. -// -// In particular, it can accept both current and legacy HCL syntax, and it -// ignores top-level blocks that it doesn't recognize. These two characteristics -// make this package ideal for dependency-checking use-cases so that we are -// more likely to be able to return an error message about an explicit -// incompatibility than to return a less-actionable message about a construct -// not being supported. -// -// However, its liberal approach also means it should be used sparingly. It -// exists primarily for "terraform init", so that it is able to detect -// incompatibilities more robustly when installing dependencies. For most -// other use-cases, use the "configs" and "configs/configload" packages. -// -// Package earlyconfig is a wrapper around the terraform-config-inspect -// codebase, adding to it just some helper functionality for Terraform's own -// use-cases. -package earlyconfig diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/earlyconfig/module.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/earlyconfig/module.go deleted file mode 100644 index 11eff2eb..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/earlyconfig/module.go +++ /dev/null @@ -1,13 +0,0 @@ -package earlyconfig - -import ( - "github.com/hashicorp/terraform-config-inspect/tfconfig" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// LoadModule loads some top-level metadata for the module in the given -// directory. -func LoadModule(dir string) (*tfconfig.Module, tfdiags.Diagnostics) { - mod, diags := tfconfig.LoadModule(dir) - return mod, wrapDiagnostics(diags) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/flatmap/expand.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/flatmap/expand.go deleted file mode 100644 index 1bb7b9f2..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/flatmap/expand.go +++ /dev/null @@ -1,152 +0,0 @@ -package flatmap - -import ( - "fmt" - "sort" - "strconv" - "strings" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim" -) - -// Expand takes a map and a key (prefix) and expands that value into -// a more complex structure. This is the reverse of the Flatten operation. -func Expand(m map[string]string, key string) interface{} { - // If the key is exactly a key in the map, just return it - if v, ok := m[key]; ok { - if v == "true" { - return true - } else if v == "false" { - return false - } - - return v - } - - // Check if the key is an array, and if so, expand the array - if v, ok := m[key+".#"]; ok { - // If the count of the key is unknown, then just put the unknown - // value in the value itself. This will be detected by Terraform - // core later. - if v == hcl2shim.UnknownVariableValue { - return v - } - - return expandArray(m, key) - } - - // Check if this is a prefix in the map - prefix := key + "." - for k := range m { - if strings.HasPrefix(k, prefix) { - return expandMap(m, prefix) - } - } - - return nil -} - -func expandArray(m map[string]string, prefix string) []interface{} { - num, err := strconv.ParseInt(m[prefix+".#"], 0, 0) - if err != nil { - panic(err) - } - - // If the number of elements in this array is 0, then return an - // empty slice as there is nothing to expand. Trying to expand it - // anyway could lead to crashes as any child maps, arrays or sets - // that no longer exist are still shown as empty with a count of 0. - if num == 0 { - return []interface{}{} - } - - // NOTE: "num" is not necessarily accurate, e.g. if a user tampers - // with state, so the following code should not crash when given a - // number of items more or less than what's given in num. The - // num key is mainly just a hint that this is a list or set. - - // The Schema "Set" type stores its values in an array format, but - // using numeric hash values instead of ordinal keys. Take the set - // of keys regardless of value, and expand them in numeric order. - // See GH-11042 for more details. - keySet := map[int]bool{} - computed := map[string]bool{} - for k := range m { - if !strings.HasPrefix(k, prefix+".") { - continue - } - - key := k[len(prefix)+1:] - idx := strings.Index(key, ".") - if idx != -1 { - key = key[:idx] - } - - // skip the count value - if key == "#" { - continue - } - - // strip the computed flag if there is one - if strings.HasPrefix(key, "~") { - key = key[1:] - computed[key] = true - } - - k, err := strconv.Atoi(key) - if err != nil { - panic(err) - } - keySet[int(k)] = true - } - - keysList := make([]int, 0, num) - for key := range keySet { - keysList = append(keysList, key) - } - sort.Ints(keysList) - - result := make([]interface{}, len(keysList)) - for i, key := range keysList { - keyString := strconv.Itoa(key) - if computed[keyString] { - keyString = "~" + keyString - } - result[i] = Expand(m, fmt.Sprintf("%s.%s", prefix, keyString)) - } - - return result -} - -func expandMap(m map[string]string, prefix string) map[string]interface{} { - // Submaps may not have a '%' key, so we can't count on this value being - // here. If we don't have a count, just proceed as if we have have a map. - if count, ok := m[prefix+"%"]; ok && count == "0" { - return map[string]interface{}{} - } - - result := make(map[string]interface{}) - for k := range m { - if !strings.HasPrefix(k, prefix) { - continue - } - - key := k[len(prefix):] - idx := strings.Index(key, ".") - if idx != -1 { - key = key[:idx] - } - if _, ok := result[key]; ok { - continue - } - - // skip the map count value - if key == "%" { - continue - } - - result[key] = Expand(m, k[:len(prefix)+len(key)]) - } - - return result -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/flatmap/flatten.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/flatmap/flatten.go deleted file mode 100644 index 9ff6e426..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/flatmap/flatten.go +++ /dev/null @@ -1,71 +0,0 @@ -package flatmap - -import ( - "fmt" - "reflect" -) - -// Flatten takes a structure and turns into a flat map[string]string. -// -// Within the "thing" parameter, only primitive values are allowed. Structs are -// not supported. Therefore, it can only be slices, maps, primitives, and -// any combination of those together. -// -// See the tests for examples of what inputs are turned into. -func Flatten(thing map[string]interface{}) Map { - result := make(map[string]string) - - for k, raw := range thing { - flatten(result, k, reflect.ValueOf(raw)) - } - - return Map(result) -} - -func flatten(result map[string]string, prefix string, v reflect.Value) { - if v.Kind() == reflect.Interface { - v = v.Elem() - } - - switch v.Kind() { - case reflect.Bool: - if v.Bool() { - result[prefix] = "true" - } else { - result[prefix] = "false" - } - case reflect.Int: - result[prefix] = fmt.Sprintf("%d", v.Int()) - case reflect.Map: - flattenMap(result, prefix, v) - case reflect.Slice: - flattenSlice(result, prefix, v) - case reflect.String: - result[prefix] = v.String() - default: - panic(fmt.Sprintf("Unknown: %s", v)) - } -} - -func flattenMap(result map[string]string, prefix string, v reflect.Value) { - for _, k := range v.MapKeys() { - if k.Kind() == reflect.Interface { - k = k.Elem() - } - - if k.Kind() != reflect.String { - panic(fmt.Sprintf("%s: map key is not string: %s", prefix, k)) - } - - flatten(result, fmt.Sprintf("%s.%s", prefix, k.String()), v.MapIndex(k)) - } -} - -func flattenSlice(result map[string]string, prefix string, v reflect.Value) { - prefix = prefix + "." - - result[prefix+"#"] = fmt.Sprintf("%d", v.Len()) - for i := 0; i < v.Len(); i++ { - flatten(result, fmt.Sprintf("%s%d", prefix, i), v.Index(i)) - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/flatmap/map.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/flatmap/map.go deleted file mode 100644 index 46b72c40..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/flatmap/map.go +++ /dev/null @@ -1,82 +0,0 @@ -package flatmap - -import ( - "strings" -) - -// Map is a wrapper around map[string]string that provides some helpers -// above it that assume the map is in the format that flatmap expects -// (the result of Flatten). -// -// All modifying functions such as Delete are done in-place unless -// otherwise noted. -type Map map[string]string - -// Contains returns true if the map contains the given key. -func (m Map) Contains(key string) bool { - for _, k := range m.Keys() { - if k == key { - return true - } - } - - return false -} - -// Delete deletes a key out of the map with the given prefix. -func (m Map) Delete(prefix string) { - for k, _ := range m { - match := k == prefix - if !match { - if !strings.HasPrefix(k, prefix) { - continue - } - - if k[len(prefix):len(prefix)+1] != "." { - continue - } - } - - delete(m, k) - } -} - -// Keys returns all of the top-level keys in this map -func (m Map) Keys() []string { - ks := make(map[string]struct{}) - for k, _ := range m { - idx := strings.Index(k, ".") - if idx == -1 { - idx = len(k) - } - - ks[k[:idx]] = struct{}{} - } - - result := make([]string, 0, len(ks)) - for k, _ := range ks { - result = append(result, k) - } - - return result -} - -// Merge merges the contents of the other Map into this one. -// -// This merge is smarter than a simple map iteration because it -// will fully replace arrays and other complex structures that -// are present in this map with the other map's. For example, if -// this map has a 3 element "foo" list, and m2 has a 2 element "foo" -// list, then the result will be that m has a 2 element "foo" -// list. -func (m Map) Merge(m2 Map) { - for _, prefix := range m2.Keys() { - m.Delete(prefix) - - for k, v := range m2 { - if strings.HasPrefix(k, prefix) { - m[k] = v - } - } - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/config/validator.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/config/validator.go deleted file mode 100644 index 35a3e7a4..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/config/validator.go +++ /dev/null @@ -1,214 +0,0 @@ -package config - -import ( - "fmt" - "strconv" - "strings" - - "github.com/hashicorp/terraform-plugin-sdk/internal/flatmap" - "github.com/hashicorp/terraform-plugin-sdk/terraform" -) - -// Validator is a helper that helps you validate the configuration -// of your resource, resource provider, etc. -// -// At the most basic level, set the Required and Optional lists to be -// specifiers of keys that are required or optional. If a key shows up -// that isn't in one of these two lists, then an error is generated. -// -// The "specifiers" allowed in this is a fairly rich syntax to help -// describe the format of your configuration: -// -// * Basic keys are just strings. For example: "foo" will match the -// "foo" key. -// -// * Nested structure keys can be matched by doing -// "listener.*.foo". This will verify that there is at least one -// listener element that has the "foo" key set. -// -// * The existence of a nested structure can be checked by simply -// doing "listener.*" which will verify that there is at least -// one element in the "listener" structure. This is NOT -// validating that "listener" is an array. It is validating -// that it is a nested structure in the configuration. -// -type Validator struct { - Required []string - Optional []string -} - -func (v *Validator) Validate( - c *terraform.ResourceConfig) (ws []string, es []error) { - // Flatten the configuration so it is easier to reason about - flat := flatmap.Flatten(c.Raw) - - keySet := make(map[string]validatorKey) - for i, vs := range [][]string{v.Required, v.Optional} { - req := i == 0 - for _, k := range vs { - vk, err := newValidatorKey(k, req) - if err != nil { - es = append(es, err) - continue - } - - keySet[k] = vk - } - } - - purged := make([]string, 0) - for _, kv := range keySet { - p, w, e := kv.Validate(flat) - if len(w) > 0 { - ws = append(ws, w...) - } - if len(e) > 0 { - es = append(es, e...) - } - - purged = append(purged, p...) - } - - // Delete all the keys we processed in order to find - // the unknown keys. - for _, p := range purged { - delete(flat, p) - } - - // The rest are unknown - for k, _ := range flat { - es = append(es, fmt.Errorf("Unknown configuration: %s", k)) - } - - return -} - -type validatorKey interface { - // Validate validates the given configuration and returns viewed keys, - // warnings, and errors. - Validate(map[string]string) ([]string, []string, []error) -} - -func newValidatorKey(k string, req bool) (validatorKey, error) { - var result validatorKey - - parts := strings.Split(k, ".") - if len(parts) > 1 && parts[1] == "*" { - result = &nestedValidatorKey{ - Parts: parts, - Required: req, - } - } else { - result = &basicValidatorKey{ - Key: k, - Required: req, - } - } - - return result, nil -} - -// basicValidatorKey validates keys that are basic such as "foo" -type basicValidatorKey struct { - Key string - Required bool -} - -func (v *basicValidatorKey) Validate( - m map[string]string) ([]string, []string, []error) { - for k, _ := range m { - // If we have the exact key its a match - if k == v.Key { - return []string{k}, nil, nil - } - } - - if !v.Required { - return nil, nil, nil - } - - return nil, nil, []error{fmt.Errorf( - "Key not found: %s", v.Key)} -} - -type nestedValidatorKey struct { - Parts []string - Required bool -} - -func (v *nestedValidatorKey) validate( - m map[string]string, - prefix string, - offset int) ([]string, []string, []error) { - if offset >= len(v.Parts) { - // We're at the end. Look for a specific key. - v2 := &basicValidatorKey{Key: prefix, Required: v.Required} - return v2.Validate(m) - } - - current := v.Parts[offset] - - // If we're at offset 0, special case to start at the next one. - if offset == 0 { - return v.validate(m, current, offset+1) - } - - // Determine if we're doing a "for all" or a specific key - if current != "*" { - // We're looking at a specific key, continue on. - return v.validate(m, prefix+"."+current, offset+1) - } - - // We're doing a "for all", so we loop over. - countStr, ok := m[prefix+".#"] - if !ok { - if !v.Required { - // It wasn't required, so its no problem. - return nil, nil, nil - } - - return nil, nil, []error{fmt.Errorf( - "Key not found: %s", prefix)} - } - - count, err := strconv.ParseInt(countStr, 0, 0) - if err != nil { - // This shouldn't happen if flatmap works properly - panic("invalid flatmap array") - } - - var e []error - var w []string - u := make([]string, 1, count+1) - u[0] = prefix + ".#" - for i := 0; i < int(count); i++ { - prefix := fmt.Sprintf("%s.%d", prefix, i) - - // Mark that we saw this specific key - u = append(u, prefix) - - // Mark all prefixes of this - for k, _ := range m { - if !strings.HasPrefix(k, prefix+".") { - continue - } - u = append(u, k) - } - - // If we have more parts, then validate deeper - if offset+1 < len(v.Parts) { - u2, w2, e2 := v.validate(m, prefix, offset+1) - - u = append(u, u2...) - w = append(w, w2...) - e = append(e, e2...) - } - } - - return u, w, e -} - -func (v *nestedValidatorKey) Validate( - m map[string]string) ([]string, []string, []error) { - return v.validate(m, "", 0) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/didyoumean/name_suggestion.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/didyoumean/name_suggestion.go deleted file mode 100644 index 54899bc6..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/didyoumean/name_suggestion.go +++ /dev/null @@ -1,24 +0,0 @@ -package didyoumean - -import ( - "github.com/agext/levenshtein" -) - -// NameSuggestion tries to find a name from the given slice of suggested names -// that is close to the given name and returns it if found. If no suggestion -// is close enough, returns the empty string. -// -// The suggestions are tried in order, so earlier suggestions take precedence -// if the given string is similar to two or more suggestions. -// -// This function is intended to be used with a relatively-small number of -// suggestions. It's not optimized for hundreds or thousands of them. -func NameSuggestion(given string, suggestions []string) string { - for _, suggestion := range suggestions { - dist := levenshtein.Distance(given, suggestion, nil) - if dist < 3 { // threshold determined experimentally - return suggestion - } - } - return "" -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/helper/hashcode/hashcode.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/hashcode/hashcode.go similarity index 100% rename from vendor/github.com/hashicorp/terraform-plugin-sdk/helper/hashcode/hashcode.go rename to vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/hashcode/hashcode.go diff --git a/vendor/github.com/hashicorp/terraform/helper/hashcode/hashcode_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/hashcode/hashcode_test.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/hashcode/hashcode_test.go rename to vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/hashcode/hashcode_test.go diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/plugin/doc.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/plugin/doc.go deleted file mode 100644 index 82b5937b..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/plugin/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// Package plugin contains types and functions to help Terraform plugins -// implement the plugin rpc interface. -// The primary Provider type will be responsible for converting from the grpc -// wire protocol to the types and methods known to the provider -// implementations. -package plugin diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/plugin/grpc_provider.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/plugin/grpc_provider.go deleted file mode 100644 index 388f1ed5..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/plugin/grpc_provider.go +++ /dev/null @@ -1,1398 +0,0 @@ -package plugin - -import ( - "encoding/json" - "fmt" - "log" - "strconv" - - "github.com/zclconf/go-cty/cty" - ctyconvert "github.com/zclconf/go-cty/cty/convert" - "github.com/zclconf/go-cty/cty/msgpack" - context "golang.org/x/net/context" - - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange" - "github.com/hashicorp/terraform-plugin-sdk/internal/plugin/convert" - proto "github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5" - "github.com/hashicorp/terraform-plugin-sdk/terraform" -) - -const newExtraKey = "_new_extra_shim" - -// NewGRPCProviderServerShim wraps a terraform.ResourceProvider in a -// proto.ProviderServer implementation. If the provided provider is not a -// *schema.Provider, this will return nil, -func NewGRPCProviderServerShim(p terraform.ResourceProvider) *GRPCProviderServer { - sp, ok := p.(*schema.Provider) - if !ok { - return nil - } - - return &GRPCProviderServer{ - provider: sp, - } -} - -// GRPCProviderServer handles the server, or plugin side of the rpc connection. -type GRPCProviderServer struct { - provider *schema.Provider -} - -func (s *GRPCProviderServer) GetSchema(_ context.Context, req *proto.GetProviderSchema_Request) (*proto.GetProviderSchema_Response, error) { - // Here we are certain that the provider is being called through grpc, so - // make sure the feature flag for helper/schema is set - schema.SetProto5() - - resp := &proto.GetProviderSchema_Response{ - ResourceSchemas: make(map[string]*proto.Schema), - DataSourceSchemas: make(map[string]*proto.Schema), - } - - resp.Provider = &proto.Schema{ - Block: convert.ConfigSchemaToProto(s.getProviderSchemaBlock()), - } - - for typ, res := range s.provider.ResourcesMap { - resp.ResourceSchemas[typ] = &proto.Schema{ - Version: int64(res.SchemaVersion), - Block: convert.ConfigSchemaToProto(res.CoreConfigSchema()), - } - } - - for typ, dat := range s.provider.DataSourcesMap { - resp.DataSourceSchemas[typ] = &proto.Schema{ - Version: int64(dat.SchemaVersion), - Block: convert.ConfigSchemaToProto(dat.CoreConfigSchema()), - } - } - - return resp, nil -} - -func (s *GRPCProviderServer) getProviderSchemaBlock() *configschema.Block { - return schema.InternalMap(s.provider.Schema).CoreConfigSchema() -} - -func (s *GRPCProviderServer) getResourceSchemaBlock(name string) *configschema.Block { - res := s.provider.ResourcesMap[name] - return res.CoreConfigSchema() -} - -func (s *GRPCProviderServer) getDatasourceSchemaBlock(name string) *configschema.Block { - dat := s.provider.DataSourcesMap[name] - return dat.CoreConfigSchema() -} - -func (s *GRPCProviderServer) PrepareProviderConfig(_ context.Context, req *proto.PrepareProviderConfig_Request) (*proto.PrepareProviderConfig_Response, error) { - resp := &proto.PrepareProviderConfig_Response{} - - schemaBlock := s.getProviderSchemaBlock() - - configVal, err := msgpack.Unmarshal(req.Config.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // lookup any required, top-level attributes that are Null, and see if we - // have a Default value available. - configVal, err = cty.Transform(configVal, func(path cty.Path, val cty.Value) (cty.Value, error) { - // we're only looking for top-level attributes - if len(path) != 1 { - return val, nil - } - - // nothing to do if we already have a value - if !val.IsNull() { - return val, nil - } - - // get the Schema definition for this attribute - getAttr, ok := path[0].(cty.GetAttrStep) - // these should all exist, but just ignore anything strange - if !ok { - return val, nil - } - - attrSchema := s.provider.Schema[getAttr.Name] - // continue to ignore anything that doesn't match - if attrSchema == nil { - return val, nil - } - - // this is deprecated, so don't set it - if attrSchema.Deprecated != "" || attrSchema.Removed != "" { - return val, nil - } - - // find a default value if it exists - def, err := attrSchema.DefaultValue() - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, fmt.Errorf("error getting default for %q: %s", getAttr.Name, err)) - return val, err - } - - // no default - if def == nil { - return val, nil - } - - // create a cty.Value and make sure it's the correct type - tmpVal := hcl2shim.HCL2ValueFromConfigValue(def) - - // helper/schema used to allow setting "" to a bool - if val.Type() == cty.Bool && tmpVal.RawEquals(cty.StringVal("")) { - // return a warning about the conversion - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, "provider set empty string as default value for bool "+getAttr.Name) - tmpVal = cty.False - } - - val, err = ctyconvert.Convert(tmpVal, val.Type()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, fmt.Errorf("error setting default for %q: %s", getAttr.Name, err)) - } - - return val, err - }) - if err != nil { - // any error here was already added to the diagnostics - return resp, nil - } - - configVal, err = schemaBlock.CoerceValue(configVal) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // Ensure there are no nulls that will cause helper/schema to panic. - if err := validateConfigNulls(configVal, nil); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - config := terraform.NewResourceConfigShimmed(configVal, schemaBlock) - - warns, errs := s.provider.Validate(config) - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs)) - - preparedConfigMP, err := msgpack.Marshal(configVal, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - resp.PreparedConfig = &proto.DynamicValue{Msgpack: preparedConfigMP} - - return resp, nil -} - -func (s *GRPCProviderServer) ValidateResourceTypeConfig(_ context.Context, req *proto.ValidateResourceTypeConfig_Request) (*proto.ValidateResourceTypeConfig_Response, error) { - resp := &proto.ValidateResourceTypeConfig_Response{} - - schemaBlock := s.getResourceSchemaBlock(req.TypeName) - - configVal, err := msgpack.Unmarshal(req.Config.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - config := terraform.NewResourceConfigShimmed(configVal, schemaBlock) - - warns, errs := s.provider.ValidateResource(req.TypeName, config) - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs)) - - return resp, nil -} - -func (s *GRPCProviderServer) ValidateDataSourceConfig(_ context.Context, req *proto.ValidateDataSourceConfig_Request) (*proto.ValidateDataSourceConfig_Response, error) { - resp := &proto.ValidateDataSourceConfig_Response{} - - schemaBlock := s.getDatasourceSchemaBlock(req.TypeName) - - configVal, err := msgpack.Unmarshal(req.Config.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // Ensure there are no nulls that will cause helper/schema to panic. - if err := validateConfigNulls(configVal, nil); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - config := terraform.NewResourceConfigShimmed(configVal, schemaBlock) - - warns, errs := s.provider.ValidateDataSource(req.TypeName, config) - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs)) - - return resp, nil -} - -func (s *GRPCProviderServer) UpgradeResourceState(_ context.Context, req *proto.UpgradeResourceState_Request) (*proto.UpgradeResourceState_Response, error) { - resp := &proto.UpgradeResourceState_Response{} - - res := s.provider.ResourcesMap[req.TypeName] - schemaBlock := s.getResourceSchemaBlock(req.TypeName) - - version := int(req.Version) - - jsonMap := map[string]interface{}{} - var err error - - switch { - // We first need to upgrade a flatmap state if it exists. - // There should never be both a JSON and Flatmap state in the request. - case len(req.RawState.Flatmap) > 0: - jsonMap, version, err = s.upgradeFlatmapState(version, req.RawState.Flatmap, res) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - // if there's a JSON state, we need to decode it. - case len(req.RawState.Json) > 0: - err = json.Unmarshal(req.RawState.Json, &jsonMap) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - default: - log.Println("[DEBUG] no state provided to upgrade") - return resp, nil - } - - // complete the upgrade of the JSON states - jsonMap, err = s.upgradeJSONState(version, jsonMap, res) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // The provider isn't required to clean out removed fields - s.removeAttributes(jsonMap, schemaBlock.ImpliedType()) - - // now we need to turn the state into the default json representation, so - // that it can be re-decoded using the actual schema. - val, err := schema.JSONMapToStateValue(jsonMap, schemaBlock) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // Now we need to make sure blocks are represented correctly, which means - // that missing blocks are empty collections, rather than null. - // First we need to CoerceValue to ensure that all object types match. - val, err = schemaBlock.CoerceValue(val) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - // Normalize the value and fill in any missing blocks. - val = objchange.NormalizeObjectFromLegacySDK(val, schemaBlock) - - // encode the final state to the expected msgpack format - newStateMP, err := msgpack.Marshal(val, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - resp.UpgradedState = &proto.DynamicValue{Msgpack: newStateMP} - return resp, nil -} - -// upgradeFlatmapState takes a legacy flatmap state, upgrades it using Migrate -// state if necessary, and converts it to the new JSON state format decoded as a -// map[string]interface{}. -// upgradeFlatmapState returns the json map along with the corresponding schema -// version. -func (s *GRPCProviderServer) upgradeFlatmapState(version int, m map[string]string, res *schema.Resource) (map[string]interface{}, int, error) { - // this will be the version we've upgraded so, defaulting to the given - // version in case no migration was called. - upgradedVersion := version - - // first determine if we need to call the legacy MigrateState func - requiresMigrate := version < res.SchemaVersion - - schemaType := res.CoreConfigSchema().ImpliedType() - - // if there are any StateUpgraders, then we need to only compare - // against the first version there - if len(res.StateUpgraders) > 0 { - requiresMigrate = version < res.StateUpgraders[0].Version - } - - if requiresMigrate && res.MigrateState == nil { - // Providers were previously allowed to bump the version - // without declaring MigrateState. - // If there are further upgraders, then we've only updated that far. - if len(res.StateUpgraders) > 0 { - schemaType = res.StateUpgraders[0].Type - upgradedVersion = res.StateUpgraders[0].Version - } - } else if requiresMigrate { - is := &terraform.InstanceState{ - ID: m["id"], - Attributes: m, - Meta: map[string]interface{}{ - "schema_version": strconv.Itoa(version), - }, - } - - is, err := res.MigrateState(version, is, s.provider.Meta()) - if err != nil { - return nil, 0, err - } - - // re-assign the map in case there was a copy made, making sure to keep - // the ID - m := is.Attributes - m["id"] = is.ID - - // if there are further upgraders, then we've only updated that far - if len(res.StateUpgraders) > 0 { - schemaType = res.StateUpgraders[0].Type - upgradedVersion = res.StateUpgraders[0].Version - } - } else { - // the schema version may be newer than the MigrateState functions - // handled and older than the current, but still stored in the flatmap - // form. If that's the case, we need to find the correct schema type to - // convert the state. - for _, upgrader := range res.StateUpgraders { - if upgrader.Version == version { - schemaType = upgrader.Type - break - } - } - } - - // now we know the state is up to the latest version that handled the - // flatmap format state. Now we can upgrade the format and continue from - // there. - newConfigVal, err := hcl2shim.HCL2ValueFromFlatmap(m, schemaType) - if err != nil { - return nil, 0, err - } - - jsonMap, err := schema.StateValueToJSONMap(newConfigVal, schemaType) - return jsonMap, upgradedVersion, err -} - -func (s *GRPCProviderServer) upgradeJSONState(version int, m map[string]interface{}, res *schema.Resource) (map[string]interface{}, error) { - var err error - - for _, upgrader := range res.StateUpgraders { - if version != upgrader.Version { - continue - } - - m, err = upgrader.Upgrade(m, s.provider.Meta()) - if err != nil { - return nil, err - } - version++ - } - - return m, nil -} - -// Remove any attributes no longer present in the schema, so that the json can -// be correctly decoded. -func (s *GRPCProviderServer) removeAttributes(v interface{}, ty cty.Type) { - // we're only concerned with finding maps that corespond to object - // attributes - switch v := v.(type) { - case []interface{}: - // If these aren't blocks the next call will be a noop - if ty.IsListType() || ty.IsSetType() { - eTy := ty.ElementType() - for _, eV := range v { - s.removeAttributes(eV, eTy) - } - } - return - case map[string]interface{}: - // map blocks aren't yet supported, but handle this just in case - if ty.IsMapType() { - eTy := ty.ElementType() - for _, eV := range v { - s.removeAttributes(eV, eTy) - } - return - } - - if ty == cty.DynamicPseudoType { - log.Printf("[DEBUG] ignoring dynamic block: %#v\n", v) - return - } - - if !ty.IsObjectType() { - // This shouldn't happen, and will fail to decode further on, so - // there's no need to handle it here. - log.Printf("[WARN] unexpected type %#v for map in json state", ty) - return - } - - attrTypes := ty.AttributeTypes() - for attr, attrV := range v { - attrTy, ok := attrTypes[attr] - if !ok { - log.Printf("[DEBUG] attribute %q no longer present in schema", attr) - delete(v, attr) - continue - } - - s.removeAttributes(attrV, attrTy) - } - } -} - -func (s *GRPCProviderServer) Stop(_ context.Context, _ *proto.Stop_Request) (*proto.Stop_Response, error) { - resp := &proto.Stop_Response{} - - err := s.provider.Stop() - if err != nil { - resp.Error = err.Error() - } - - return resp, nil -} - -func (s *GRPCProviderServer) Configure(_ context.Context, req *proto.Configure_Request) (*proto.Configure_Response, error) { - resp := &proto.Configure_Response{} - - schemaBlock := s.getProviderSchemaBlock() - - configVal, err := msgpack.Unmarshal(req.Config.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - s.provider.TerraformVersion = req.TerraformVersion - - // Ensure there are no nulls that will cause helper/schema to panic. - if err := validateConfigNulls(configVal, nil); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - config := terraform.NewResourceConfigShimmed(configVal, schemaBlock) - err = s.provider.Configure(config) - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - - return resp, nil -} - -func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadResource_Request) (*proto.ReadResource_Response, error) { - resp := &proto.ReadResource_Response{ - // helper/schema did previously handle private data during refresh, but - // core is now going to expect this to be maintained in order to - // persist it in the state. - Private: req.Private, - } - - res := s.provider.ResourcesMap[req.TypeName] - schemaBlock := s.getResourceSchemaBlock(req.TypeName) - - stateVal, err := msgpack.Unmarshal(req.CurrentState.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - instanceState, err := res.ShimInstanceStateFromValue(stateVal) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - private := make(map[string]interface{}) - if len(req.Private) > 0 { - if err := json.Unmarshal(req.Private, &private); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - } - instanceState.Meta = private - - newInstanceState, err := res.RefreshWithoutUpgrade(instanceState, s.provider.Meta()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - if newInstanceState == nil || newInstanceState.ID == "" { - // The old provider API used an empty id to signal that the remote - // object appears to have been deleted, but our new protocol expects - // to see a null value (in the cty sense) in that case. - newStateMP, err := msgpack.Marshal(cty.NullVal(schemaBlock.ImpliedType()), schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - } - resp.NewState = &proto.DynamicValue{ - Msgpack: newStateMP, - } - return resp, nil - } - - // helper/schema should always copy the ID over, but do it again just to be safe - newInstanceState.Attributes["id"] = newInstanceState.ID - - newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(newInstanceState.Attributes, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - newStateVal = normalizeNullValues(newStateVal, stateVal, false) - newStateVal = copyTimeoutValues(newStateVal, stateVal) - - newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - resp.NewState = &proto.DynamicValue{ - Msgpack: newStateMP, - } - - return resp, nil -} - -func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.PlanResourceChange_Request) (*proto.PlanResourceChange_Response, error) { - resp := &proto.PlanResourceChange_Response{} - - // This is a signal to Terraform Core that we're doing the best we can to - // shim the legacy type system of the SDK onto the Terraform type system - // but we need it to cut us some slack. This setting should not be taken - // forward to any new SDK implementations, since setting it prevents us - // from catching certain classes of provider bug that can lead to - // confusing downstream errors. - resp.LegacyTypeSystem = true - - res := s.provider.ResourcesMap[req.TypeName] - schemaBlock := s.getResourceSchemaBlock(req.TypeName) - - priorStateVal, err := msgpack.Unmarshal(req.PriorState.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - create := priorStateVal.IsNull() - - proposedNewStateVal, err := msgpack.Unmarshal(req.ProposedNewState.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // We don't usually plan destroys, but this can return early in any case. - if proposedNewStateVal.IsNull() { - resp.PlannedState = req.ProposedNewState - resp.PlannedPrivate = req.PriorPrivate - return resp, nil - } - - info := &terraform.InstanceInfo{ - Type: req.TypeName, - } - - priorState, err := res.ShimInstanceStateFromValue(priorStateVal) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - priorPrivate := make(map[string]interface{}) - if len(req.PriorPrivate) > 0 { - if err := json.Unmarshal(req.PriorPrivate, &priorPrivate); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - } - - priorState.Meta = priorPrivate - - // Ensure there are no nulls that will cause helper/schema to panic. - if err := validateConfigNulls(proposedNewStateVal, nil); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // turn the proposed state into a legacy configuration - cfg := terraform.NewResourceConfigShimmed(proposedNewStateVal, schemaBlock) - - diff, err := s.provider.SimpleDiff(info, priorState, cfg) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // if this is a new instance, we need to make sure ID is going to be computed - if create { - if diff == nil { - diff = terraform.NewInstanceDiff() - } - - diff.Attributes["id"] = &terraform.ResourceAttrDiff{ - NewComputed: true, - } - } - - if diff == nil || len(diff.Attributes) == 0 { - // schema.Provider.Diff returns nil if it ends up making a diff with no - // changes, but our new interface wants us to return an actual change - // description that _shows_ there are no changes. This is always the - // prior state, because we force a diff above if this is a new instance. - resp.PlannedState = req.PriorState - resp.PlannedPrivate = req.PriorPrivate - return resp, nil - } - - if priorState == nil { - priorState = &terraform.InstanceState{} - } - - // now we need to apply the diff to the prior state, so get the planned state - plannedAttrs, err := diff.Apply(priorState.Attributes, schemaBlock) - - plannedStateVal, err := hcl2shim.HCL2ValueFromFlatmap(plannedAttrs, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - plannedStateVal, err = schemaBlock.CoerceValue(plannedStateVal) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - plannedStateVal = normalizeNullValues(plannedStateVal, proposedNewStateVal, false) - - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - plannedStateVal = copyTimeoutValues(plannedStateVal, proposedNewStateVal) - - // The old SDK code has some imprecisions that cause it to sometimes - // generate differences that the SDK itself does not consider significant - // but Terraform Core would. To avoid producing weird do-nothing diffs - // in that case, we'll check if the provider as produced something we - // think is "equivalent" to the prior state and just return the prior state - // itself if so, thus ensuring that Terraform Core will treat this as - // a no-op. See the docs for ValuesSDKEquivalent for some caveats on its - // accuracy. - forceNoChanges := false - if hcl2shim.ValuesSDKEquivalent(priorStateVal, plannedStateVal) { - plannedStateVal = priorStateVal - forceNoChanges = true - } - - // if this was creating the resource, we need to set any remaining computed - // fields - if create { - plannedStateVal = SetUnknowns(plannedStateVal, schemaBlock) - } - - plannedMP, err := msgpack.Marshal(plannedStateVal, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - resp.PlannedState = &proto.DynamicValue{ - Msgpack: plannedMP, - } - - // encode any timeouts into the diff Meta - t := &schema.ResourceTimeout{} - if err := t.ConfigDecode(res, cfg); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - if err := t.DiffEncode(diff); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // Now we need to store any NewExtra values, which are where any actual - // StateFunc modified config fields are hidden. - privateMap := diff.Meta - if privateMap == nil { - privateMap = map[string]interface{}{} - } - - newExtra := map[string]interface{}{} - - for k, v := range diff.Attributes { - if v.NewExtra != nil { - newExtra[k] = v.NewExtra - } - } - privateMap[newExtraKey] = newExtra - - // the Meta field gets encoded into PlannedPrivate - plannedPrivate, err := json.Marshal(privateMap) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - resp.PlannedPrivate = plannedPrivate - - // collect the attributes that require instance replacement, and convert - // them to cty.Paths. - var requiresNew []string - if !forceNoChanges { - for attr, d := range diff.Attributes { - if d.RequiresNew { - requiresNew = append(requiresNew, attr) - } - } - } - - // If anything requires a new resource already, or the "id" field indicates - // that we will be creating a new resource, then we need to add that to - // RequiresReplace so that core can tell if the instance is being replaced - // even if changes are being suppressed via "ignore_changes". - id := plannedStateVal.GetAttr("id") - if len(requiresNew) > 0 || id.IsNull() || !id.IsKnown() { - requiresNew = append(requiresNew, "id") - } - - requiresReplace, err := hcl2shim.RequiresReplace(requiresNew, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // convert these to the protocol structures - for _, p := range requiresReplace { - resp.RequiresReplace = append(resp.RequiresReplace, pathToAttributePath(p)) - } - - return resp, nil -} - -func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.ApplyResourceChange_Request) (*proto.ApplyResourceChange_Response, error) { - resp := &proto.ApplyResourceChange_Response{ - // Start with the existing state as a fallback - NewState: req.PriorState, - } - - res := s.provider.ResourcesMap[req.TypeName] - schemaBlock := s.getResourceSchemaBlock(req.TypeName) - - priorStateVal, err := msgpack.Unmarshal(req.PriorState.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - plannedStateVal, err := msgpack.Unmarshal(req.PlannedState.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - info := &terraform.InstanceInfo{ - Type: req.TypeName, - } - - priorState, err := res.ShimInstanceStateFromValue(priorStateVal) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - private := make(map[string]interface{}) - if len(req.PlannedPrivate) > 0 { - if err := json.Unmarshal(req.PlannedPrivate, &private); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - } - - var diff *terraform.InstanceDiff - destroy := false - - // a null state means we are destroying the instance - if plannedStateVal.IsNull() { - destroy = true - diff = &terraform.InstanceDiff{ - Attributes: make(map[string]*terraform.ResourceAttrDiff), - Meta: make(map[string]interface{}), - Destroy: true, - } - } else { - diff, err = schema.DiffFromValues(priorStateVal, plannedStateVal, stripResourceModifiers(res)) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - } - - if diff == nil { - diff = &terraform.InstanceDiff{ - Attributes: make(map[string]*terraform.ResourceAttrDiff), - Meta: make(map[string]interface{}), - } - } - - // add NewExtra Fields that may have been stored in the private data - if newExtra := private[newExtraKey]; newExtra != nil { - for k, v := range newExtra.(map[string]interface{}) { - d := diff.Attributes[k] - - if d == nil { - d = &terraform.ResourceAttrDiff{} - } - - d.NewExtra = v - diff.Attributes[k] = d - } - } - - if private != nil { - diff.Meta = private - } - - for k, d := range diff.Attributes { - // We need to turn off any RequiresNew. There could be attributes - // without changes in here inserted by helper/schema, but if they have - // RequiresNew then the state will be dropped from the ResourceData. - d.RequiresNew = false - - // Check that any "removed" attributes that don't actually exist in the - // prior state, or helper/schema will confuse itself - if d.NewRemoved { - if _, ok := priorState.Attributes[k]; !ok { - delete(diff.Attributes, k) - } - } - } - - newInstanceState, err := s.provider.Apply(info, priorState, diff) - // we record the error here, but continue processing any returned state. - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - } - newStateVal := cty.NullVal(schemaBlock.ImpliedType()) - - // Always return a null value for destroy. - // While this is usually indicated by a nil state, check for missing ID or - // attributes in the case of a provider failure. - if destroy || newInstanceState == nil || newInstanceState.Attributes == nil || newInstanceState.ID == "" { - newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - resp.NewState = &proto.DynamicValue{ - Msgpack: newStateMP, - } - return resp, nil - } - - // We keep the null val if we destroyed the resource, otherwise build the - // entire object, even if the new state was nil. - newStateVal, err = schema.StateValueFromInstanceState(newInstanceState, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - newStateVal = normalizeNullValues(newStateVal, plannedStateVal, true) - - newStateVal = copyTimeoutValues(newStateVal, plannedStateVal) - - newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - resp.NewState = &proto.DynamicValue{ - Msgpack: newStateMP, - } - - meta, err := json.Marshal(newInstanceState.Meta) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - resp.Private = meta - - // This is a signal to Terraform Core that we're doing the best we can to - // shim the legacy type system of the SDK onto the Terraform type system - // but we need it to cut us some slack. This setting should not be taken - // forward to any new SDK implementations, since setting it prevents us - // from catching certain classes of provider bug that can lead to - // confusing downstream errors. - resp.LegacyTypeSystem = true - - return resp, nil -} - -func (s *GRPCProviderServer) ImportResourceState(_ context.Context, req *proto.ImportResourceState_Request) (*proto.ImportResourceState_Response, error) { - resp := &proto.ImportResourceState_Response{} - - info := &terraform.InstanceInfo{ - Type: req.TypeName, - } - - newInstanceStates, err := s.provider.ImportState(info, req.Id) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - for _, is := range newInstanceStates { - // copy the ID again just to be sure it wasn't missed - is.Attributes["id"] = is.ID - - resourceType := is.Ephemeral.Type - if resourceType == "" { - resourceType = req.TypeName - } - - schemaBlock := s.getResourceSchemaBlock(resourceType) - newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(is.Attributes, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // Normalize the value and fill in any missing blocks. - newStateVal = objchange.NormalizeObjectFromLegacySDK(newStateVal, schemaBlock) - - newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - meta, err := json.Marshal(is.Meta) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - importedResource := &proto.ImportResourceState_ImportedResource{ - TypeName: resourceType, - State: &proto.DynamicValue{ - Msgpack: newStateMP, - }, - Private: meta, - } - - resp.ImportedResources = append(resp.ImportedResources, importedResource) - } - - return resp, nil -} - -func (s *GRPCProviderServer) ReadDataSource(_ context.Context, req *proto.ReadDataSource_Request) (*proto.ReadDataSource_Response, error) { - resp := &proto.ReadDataSource_Response{} - - schemaBlock := s.getDatasourceSchemaBlock(req.TypeName) - - configVal, err := msgpack.Unmarshal(req.Config.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - info := &terraform.InstanceInfo{ - Type: req.TypeName, - } - - // Ensure there are no nulls that will cause helper/schema to panic. - if err := validateConfigNulls(configVal, nil); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - config := terraform.NewResourceConfigShimmed(configVal, schemaBlock) - - // we need to still build the diff separately with the Read method to match - // the old behavior - diff, err := s.provider.ReadDataDiff(info, config) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // now we can get the new complete data source - newInstanceState, err := s.provider.ReadDataApply(info, diff) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - newStateVal, err := schema.StateValueFromInstanceState(newInstanceState, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - newStateVal = copyTimeoutValues(newStateVal, configVal) - - newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - resp.State = &proto.DynamicValue{ - Msgpack: newStateMP, - } - return resp, nil -} - -func pathToAttributePath(path cty.Path) *proto.AttributePath { - var steps []*proto.AttributePath_Step - - for _, step := range path { - switch s := step.(type) { - case cty.GetAttrStep: - steps = append(steps, &proto.AttributePath_Step{ - Selector: &proto.AttributePath_Step_AttributeName{ - AttributeName: s.Name, - }, - }) - case cty.IndexStep: - ty := s.Key.Type() - switch ty { - case cty.Number: - i, _ := s.Key.AsBigFloat().Int64() - steps = append(steps, &proto.AttributePath_Step{ - Selector: &proto.AttributePath_Step_ElementKeyInt{ - ElementKeyInt: i, - }, - }) - case cty.String: - steps = append(steps, &proto.AttributePath_Step{ - Selector: &proto.AttributePath_Step_ElementKeyString{ - ElementKeyString: s.Key.AsString(), - }, - }) - } - } - } - - return &proto.AttributePath{Steps: steps} -} - -// helper/schema throws away timeout values from the config and stores them in -// the Private/Meta fields. we need to copy those values into the planned state -// so that core doesn't see a perpetual diff with the timeout block. -func copyTimeoutValues(to cty.Value, from cty.Value) cty.Value { - // if `to` is null we are planning to remove it altogether. - if to.IsNull() { - return to - } - toAttrs := to.AsValueMap() - // We need to remove the key since the hcl2shims will add a non-null block - // because we can't determine if a single block was null from the flatmapped - // values. This needs to conform to the correct schema for marshaling, so - // change the value to null rather than deleting it from the object map. - timeouts, ok := toAttrs[schema.TimeoutsConfigKey] - if ok { - toAttrs[schema.TimeoutsConfigKey] = cty.NullVal(timeouts.Type()) - } - - // if from is null then there are no timeouts to copy - if from.IsNull() { - return cty.ObjectVal(toAttrs) - } - - fromAttrs := from.AsValueMap() - timeouts, ok = fromAttrs[schema.TimeoutsConfigKey] - - // timeouts shouldn't be unknown, but don't copy possibly invalid values either - if !ok || timeouts.IsNull() || !timeouts.IsWhollyKnown() { - // no timeouts block to copy - return cty.ObjectVal(toAttrs) - } - - toAttrs[schema.TimeoutsConfigKey] = timeouts - - return cty.ObjectVal(toAttrs) -} - -// stripResourceModifiers takes a *schema.Resource and returns a deep copy with all -// StateFuncs and CustomizeDiffs removed. This will be used during apply to -// create a diff from a planned state where the diff modifications have already -// been applied. -func stripResourceModifiers(r *schema.Resource) *schema.Resource { - if r == nil { - return nil - } - // start with a shallow copy - newResource := new(schema.Resource) - *newResource = *r - - newResource.CustomizeDiff = nil - newResource.Schema = map[string]*schema.Schema{} - - for k, s := range r.Schema { - newResource.Schema[k] = stripSchema(s) - } - - return newResource -} - -func stripSchema(s *schema.Schema) *schema.Schema { - if s == nil { - return nil - } - // start with a shallow copy - newSchema := new(schema.Schema) - *newSchema = *s - - newSchema.StateFunc = nil - - switch e := newSchema.Elem.(type) { - case *schema.Schema: - newSchema.Elem = stripSchema(e) - case *schema.Resource: - newSchema.Elem = stripResourceModifiers(e) - } - - return newSchema -} - -// Zero values and empty containers may be interchanged by the apply process. -// When there is a discrepency between src and dst value being null or empty, -// prefer the src value. This takes a little more liberty with set types, since -// we can't correlate modified set values. In the case of sets, if the src set -// was wholly known we assume the value was correctly applied and copy that -// entirely to the new value. -// While apply prefers the src value, during plan we prefer dst whenever there -// is an unknown or a set is involved, since the plan can alter the value -// however it sees fit. This however means that a CustomizeDiffFunction may not -// be able to change a null to an empty value or vice versa, but that should be -// very uncommon nor was it reliable before 0.12 either. -func normalizeNullValues(dst, src cty.Value, apply bool) cty.Value { - ty := dst.Type() - if !src.IsNull() && !src.IsKnown() { - // Return src during plan to retain unknown interpolated placeholders, - // which could be lost if we're only updating a resource. If this is a - // read scenario, then there shouldn't be any unknowns at all. - if dst.IsNull() && !apply { - return src - } - return dst - } - - // Handle null/empty changes for collections during apply. - // A change between null and empty values prefers src to make sure the state - // is consistent between plan and apply. - if ty.IsCollectionType() && apply { - dstEmpty := !dst.IsNull() && dst.IsKnown() && dst.LengthInt() == 0 - srcEmpty := !src.IsNull() && src.IsKnown() && src.LengthInt() == 0 - - if (src.IsNull() && dstEmpty) || (srcEmpty && dst.IsNull()) { - return src - } - } - - // check the invariants that we need below, to ensure we are working with - // non-null and known values. - if src.IsNull() || !src.IsKnown() || !dst.IsKnown() { - return dst - } - - switch { - case ty.IsMapType(), ty.IsObjectType(): - var dstMap map[string]cty.Value - if !dst.IsNull() { - dstMap = dst.AsValueMap() - } - if dstMap == nil { - dstMap = map[string]cty.Value{} - } - - srcMap := src.AsValueMap() - for key, v := range srcMap { - dstVal, ok := dstMap[key] - if !ok && apply && ty.IsMapType() { - // don't transfer old map values to dst during apply - continue - } - - if dstVal == cty.NilVal { - if !apply && ty.IsMapType() { - // let plan shape this map however it wants - continue - } - dstVal = cty.NullVal(v.Type()) - } - - dstMap[key] = normalizeNullValues(dstVal, v, apply) - } - - // you can't call MapVal/ObjectVal with empty maps, but nothing was - // copied in anyway. If the dst is nil, and the src is known, assume the - // src is correct. - if len(dstMap) == 0 { - if dst.IsNull() && src.IsWhollyKnown() && apply { - return src - } - return dst - } - - if ty.IsMapType() { - // helper/schema will populate an optional+computed map with - // unknowns which we have to fixup here. - // It would be preferable to simply prevent any known value from - // becoming unknown, but concessions have to be made to retain the - // broken legacy behavior when possible. - for k, srcVal := range srcMap { - if !srcVal.IsNull() && srcVal.IsKnown() { - dstVal, ok := dstMap[k] - if !ok { - continue - } - - if !dstVal.IsNull() && !dstVal.IsKnown() { - dstMap[k] = srcVal - } - } - } - - return cty.MapVal(dstMap) - } - - return cty.ObjectVal(dstMap) - - case ty.IsSetType(): - // If the original was wholly known, then we expect that is what the - // provider applied. The apply process loses too much information to - // reliably re-create the set. - if src.IsWhollyKnown() && apply { - return src - } - - case ty.IsListType(), ty.IsTupleType(): - // If the dst is null, and the src is known, then we lost an empty value - // so take the original. - if dst.IsNull() { - if src.IsWhollyKnown() && src.LengthInt() == 0 && apply { - return src - } - - // if dst is null and src only contains unknown values, then we lost - // those during a read or plan. - if !apply && !src.IsNull() { - allUnknown := true - for _, v := range src.AsValueSlice() { - if v.IsKnown() { - allUnknown = false - break - } - } - if allUnknown { - return src - } - } - - return dst - } - - // if the lengths are identical, then iterate over each element in succession. - srcLen := src.LengthInt() - dstLen := dst.LengthInt() - if srcLen == dstLen && srcLen > 0 { - srcs := src.AsValueSlice() - dsts := dst.AsValueSlice() - - for i := 0; i < srcLen; i++ { - dsts[i] = normalizeNullValues(dsts[i], srcs[i], apply) - } - - if ty.IsTupleType() { - return cty.TupleVal(dsts) - } - return cty.ListVal(dsts) - } - - case ty == cty.String: - // The legacy SDK should not be able to remove a value during plan or - // apply, however we are only going to overwrite this if the source was - // an empty string, since that is what is often equated with unset and - // lost in the diff process. - if dst.IsNull() && src.AsString() == "" { - return src - } - } - - return dst -} - -// validateConfigNulls checks a config value for unsupported nulls before -// attempting to shim the value. While null values can mostly be ignored in the -// configuration, since they're not supported in HCL1, the case where a null -// appears in a list-like attribute (list, set, tuple) will present a nil value -// to helper/schema which can panic. Return an error to the user in this case, -// indicating the attribute with the null value. -func validateConfigNulls(v cty.Value, path cty.Path) []*proto.Diagnostic { - var diags []*proto.Diagnostic - if v.IsNull() || !v.IsKnown() { - return diags - } - - switch { - case v.Type().IsListType() || v.Type().IsSetType() || v.Type().IsTupleType(): - it := v.ElementIterator() - for it.Next() { - kv, ev := it.Element() - if ev.IsNull() { - // if this is a set, the kv is also going to be null which - // isn't a valid path element, so we can't append it to the - // diagnostic. - p := path - if !kv.IsNull() { - p = append(p, cty.IndexStep{Key: kv}) - } - - diags = append(diags, &proto.Diagnostic{ - Severity: proto.Diagnostic_ERROR, - Summary: "Null value found in list", - Detail: "Null values are not allowed for this attribute value.", - Attribute: convert.PathToAttributePath(p), - }) - continue - } - - d := validateConfigNulls(ev, append(path, cty.IndexStep{Key: kv})) - diags = convert.AppendProtoDiag(diags, d) - } - - case v.Type().IsMapType() || v.Type().IsObjectType(): - it := v.ElementIterator() - for it.Next() { - kv, ev := it.Element() - var step cty.PathStep - switch { - case v.Type().IsMapType(): - step = cty.IndexStep{Key: kv} - case v.Type().IsObjectType(): - step = cty.GetAttrStep{Name: kv.AsString()} - } - d := validateConfigNulls(ev, append(path, step)) - diags = convert.AppendProtoDiag(diags, d) - } - } - - return diags -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/plugin/unknown.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/plugin/unknown.go deleted file mode 100644 index a22a264f..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/helper/plugin/unknown.go +++ /dev/null @@ -1,131 +0,0 @@ -package plugin - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/zclconf/go-cty/cty" -) - -// SetUnknowns takes a cty.Value, and compares it to the schema setting any null -// values which are computed to unknown. -func SetUnknowns(val cty.Value, schema *configschema.Block) cty.Value { - if !val.IsKnown() { - return val - } - - // If the object was null, we still need to handle the top level attributes - // which might be computed, but we don't need to expand the blocks. - if val.IsNull() { - objMap := map[string]cty.Value{} - allNull := true - for name, attr := range schema.Attributes { - switch { - case attr.Computed: - objMap[name] = cty.UnknownVal(attr.Type) - allNull = false - default: - objMap[name] = cty.NullVal(attr.Type) - } - } - - // If this object has no unknown attributes, then we can leave it null. - if allNull { - return val - } - - return cty.ObjectVal(objMap) - } - - valMap := val.AsValueMap() - newVals := make(map[string]cty.Value) - - for name, attr := range schema.Attributes { - v := valMap[name] - - if attr.Computed && v.IsNull() { - newVals[name] = cty.UnknownVal(attr.Type) - continue - } - - newVals[name] = v - } - - for name, blockS := range schema.BlockTypes { - blockVal := valMap[name] - if blockVal.IsNull() || !blockVal.IsKnown() { - newVals[name] = blockVal - continue - } - - blockValType := blockVal.Type() - blockElementType := blockS.Block.ImpliedType() - - // This switches on the value type here, so we can correctly switch - // between Tuples/Lists and Maps/Objects. - switch { - case blockS.Nesting == configschema.NestingSingle || blockS.Nesting == configschema.NestingGroup: - // NestingSingle is the only exception here, where we treat the - // block directly as an object - newVals[name] = SetUnknowns(blockVal, &blockS.Block) - - case blockValType.IsSetType(), blockValType.IsListType(), blockValType.IsTupleType(): - listVals := blockVal.AsValueSlice() - newListVals := make([]cty.Value, 0, len(listVals)) - - for _, v := range listVals { - newListVals = append(newListVals, SetUnknowns(v, &blockS.Block)) - } - - switch { - case blockValType.IsSetType(): - switch len(newListVals) { - case 0: - newVals[name] = cty.SetValEmpty(blockElementType) - default: - newVals[name] = cty.SetVal(newListVals) - } - case blockValType.IsListType(): - switch len(newListVals) { - case 0: - newVals[name] = cty.ListValEmpty(blockElementType) - default: - newVals[name] = cty.ListVal(newListVals) - } - case blockValType.IsTupleType(): - newVals[name] = cty.TupleVal(newListVals) - } - - case blockValType.IsMapType(), blockValType.IsObjectType(): - mapVals := blockVal.AsValueMap() - newMapVals := make(map[string]cty.Value) - - for k, v := range mapVals { - newMapVals[k] = SetUnknowns(v, &blockS.Block) - } - - switch { - case blockValType.IsMapType(): - switch len(newMapVals) { - case 0: - newVals[name] = cty.MapValEmpty(blockElementType) - default: - newVals[name] = cty.MapVal(newMapVals) - } - case blockValType.IsObjectType(): - if len(newMapVals) == 0 { - // We need to populate empty values to make a valid object. - for attr, ty := range blockElementType.AttributeTypes() { - newMapVals[attr] = cty.NullVal(ty) - } - } - newVals[name] = cty.ObjectVal(newMapVals) - } - - default: - panic(fmt.Sprintf("failed to set unknown values for nested block %q:%#v", name, blockValType)) - } - } - - return cty.ObjectVal(newVals) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/httpclient/client.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/httpclient/client.go deleted file mode 100644 index ad8d626c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/httpclient/client.go +++ /dev/null @@ -1,53 +0,0 @@ -package httpclient - -import ( - "fmt" - "log" - "net/http" - "os" - "strings" - - cleanhttp "github.com/hashicorp/go-cleanhttp" - "github.com/hashicorp/terraform-plugin-sdk/internal/version" -) - -const uaEnvVar = "TF_APPEND_USER_AGENT" -const userAgentFormat = "Terraform/%s" - -// New returns the DefaultPooledClient from the cleanhttp -// package that will also send a Terraform User-Agent string. -func New() *http.Client { - cli := cleanhttp.DefaultPooledClient() - cli.Transport = &userAgentRoundTripper{ - userAgent: UserAgentString(), - inner: cli.Transport, - } - return cli -} - -type userAgentRoundTripper struct { - inner http.RoundTripper - userAgent string -} - -func (rt *userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - if _, ok := req.Header["User-Agent"]; !ok { - req.Header.Set("User-Agent", rt.userAgent) - } - log.Printf("[TRACE] HTTP client %s request to %s", req.Method, req.URL.String()) - return rt.inner.RoundTrip(req) -} - -func UserAgentString() string { - ua := fmt.Sprintf(userAgentFormat, version.Version) - - if add := os.Getenv(uaEnvVar); add != "" { - add = strings.TrimSpace(add) - if len(add) > 0 { - ua += " " + add - log.Printf("[DEBUG] Using modified User-Agent: %s", ua) - } - } - - return ua -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/copy_dir.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/copy_dir.go deleted file mode 100644 index 7096ff74..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/copy_dir.go +++ /dev/null @@ -1,125 +0,0 @@ -package initwd - -import ( - "io" - "os" - "path/filepath" - "strings" -) - -// copyDir copies the src directory contents into dst. Both directories -// should already exist. -func copyDir(dst, src string) error { - src, err := filepath.EvalSymlinks(src) - if err != nil { - return err - } - - walkFn := func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - - if path == src { - return nil - } - - if strings.HasPrefix(filepath.Base(path), ".") { - // Skip any dot files - if info.IsDir() { - return filepath.SkipDir - } else { - return nil - } - } - - // The "path" has the src prefixed to it. We need to join our - // destination with the path without the src on it. - dstPath := filepath.Join(dst, path[len(src):]) - - // we don't want to try and copy the same file over itself. - if eq, err := sameFile(path, dstPath); eq { - return nil - } else if err != nil { - return err - } - - // If we have a directory, make that subdirectory, then continue - // the walk. - if info.IsDir() { - if path == filepath.Join(src, dst) { - // dst is in src; don't walk it. - return nil - } - - if err := os.MkdirAll(dstPath, 0755); err != nil { - return err - } - - return nil - } - - // If the current path is a symlink, recreate the symlink relative to - // the dst directory - if info.Mode()&os.ModeSymlink == os.ModeSymlink { - target, err := os.Readlink(path) - if err != nil { - return err - } - - return os.Symlink(target, dstPath) - } - - // If we have a file, copy the contents. - srcF, err := os.Open(path) - if err != nil { - return err - } - defer srcF.Close() - - dstF, err := os.Create(dstPath) - if err != nil { - return err - } - defer dstF.Close() - - if _, err := io.Copy(dstF, srcF); err != nil { - return err - } - - // Chmod it - return os.Chmod(dstPath, info.Mode()) - } - - return filepath.Walk(src, walkFn) -} - -// sameFile tried to determine if to paths are the same file. -// If the paths don't match, we lookup the inode on supported systems. -func sameFile(a, b string) (bool, error) { - if a == b { - return true, nil - } - - aIno, err := inode(a) - if err != nil { - if os.IsNotExist(err) { - return false, nil - } - return false, err - } - - bIno, err := inode(b) - if err != nil { - if os.IsNotExist(err) { - return false, nil - } - return false, err - } - - if aIno > 0 && aIno == bIno { - return true, nil - } - - return false, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/doc.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/doc.go deleted file mode 100644 index b9d938db..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/doc.go +++ /dev/null @@ -1,7 +0,0 @@ -// Package initwd contains various helper functions used by the "terraform init" -// command to initialize a working directory. -// -// These functions may also be used from testing code to simulate the behaviors -// of "terraform init" against test fixtures, but should not be used elsewhere -// in the main code. -package initwd diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/from_module.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/from_module.go deleted file mode 100644 index 641e71de..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/from_module.go +++ /dev/null @@ -1,363 +0,0 @@ -package initwd - -import ( - "fmt" - "github.com/hashicorp/terraform-plugin-sdk/internal/earlyconfig" - "io/ioutil" - "log" - "os" - "path/filepath" - "sort" - "strings" - - version "github.com/hashicorp/go-version" - "github.com/hashicorp/terraform-config-inspect/tfconfig" - "github.com/hashicorp/terraform-plugin-sdk/internal/modsdir" - "github.com/hashicorp/terraform-plugin-sdk/internal/registry" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -const initFromModuleRootCallName = "root" -const initFromModuleRootKeyPrefix = initFromModuleRootCallName + "." - -// DirFromModule populates the given directory (which must exist and be -// empty) with the contents of the module at the given source address. -// -// It does this by installing the given module and all of its descendent -// modules in a temporary root directory and then copying the installed -// files into suitable locations. As a consequence, any diagnostics it -// generates will reveal the location of this temporary directory to the -// user. -// -// This rather roundabout installation approach is taken to ensure that -// installation proceeds in a manner identical to normal module installation. -// -// If the given source address specifies a sub-directory of the given -// package then only the sub-directory and its descendents will be copied -// into the given root directory, which will cause any relative module -// references using ../ from that module to be unresolvable. Error diagnostics -// are produced in that case, to prompt the user to rewrite the source strings -// to be absolute references to the original remote module. -func DirFromModule(rootDir, modulesDir, sourceAddr string, reg *registry.Client, hooks ModuleInstallHooks) tfdiags.Diagnostics { - var diags tfdiags.Diagnostics - - // The way this function works is pretty ugly, but we accept it because - // -from-module is a less important case than normal module installation - // and so it's better to keep this ugly complexity out here rather than - // adding even more complexity to the normal module installer. - - // The target directory must exist but be empty. - { - entries, err := ioutil.ReadDir(rootDir) - if err != nil { - if os.IsNotExist(err) { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Target directory does not exist", - fmt.Sprintf("Cannot initialize non-existent directory %s.", rootDir), - )) - } else { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to read target directory", - fmt.Sprintf("Error reading %s to ensure it is empty: %s.", rootDir, err), - )) - } - return diags - } - haveEntries := false - for _, entry := range entries { - if entry.Name() == "." || entry.Name() == ".." || entry.Name() == ".terraform" { - continue - } - haveEntries = true - } - if haveEntries { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Can't populate non-empty directory", - fmt.Sprintf("The target directory %s is not empty, so it cannot be initialized with the -from-module=... option.", rootDir), - )) - return diags - } - } - - instDir := filepath.Join(rootDir, ".terraform/init-from-module") - inst := NewModuleInstaller(instDir, reg) - log.Printf("[DEBUG] installing modules in %s to initialize working directory from %q", instDir, sourceAddr) - os.RemoveAll(instDir) // if this fails then we'll fail on MkdirAll below too - err := os.MkdirAll(instDir, os.ModePerm) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to create temporary directory", - fmt.Sprintf("Failed to create temporary directory %s: %s.", instDir, err), - )) - return diags - } - - instManifest := make(modsdir.Manifest) - retManifest := make(modsdir.Manifest) - - fakeFilename := fmt.Sprintf("-from-module=%q", sourceAddr) - fakePos := tfconfig.SourcePos{ - Filename: fakeFilename, - Line: 1, - } - - // -from-module allows relative paths but it's different than a normal - // module address where it'd be resolved relative to the module call - // (which is synthetic, here.) To address this, we'll just patch up any - // relative paths to be absolute paths before we run, ensuring we'll - // get the right result. This also, as an important side-effect, ensures - // that the result will be "downloaded" with go-getter (copied from the - // source location), rather than just recorded as a relative path. - { - maybePath := filepath.ToSlash(sourceAddr) - if maybePath == "." || strings.HasPrefix(maybePath, "./") || strings.HasPrefix(maybePath, "../") { - if wd, err := os.Getwd(); err == nil { - sourceAddr = filepath.Join(wd, sourceAddr) - log.Printf("[TRACE] -from-module relative path rewritten to absolute path %s", sourceAddr) - } - } - } - - // Now we need to create an artificial root module that will seed our - // installation process. - fakeRootModule := &tfconfig.Module{ - ModuleCalls: map[string]*tfconfig.ModuleCall{ - initFromModuleRootCallName: { - Name: initFromModuleRootCallName, - Source: sourceAddr, - Pos: fakePos, - }, - }, - } - - // wrapHooks filters hook notifications to only include Download calls - // and to trim off the initFromModuleRootCallName prefix. We'll produce - // our own Install notifications directly below. - wrapHooks := installHooksInitDir{ - Wrapped: hooks, - } - getter := reusingGetter{} - _, instDiags := inst.installDescendentModules(fakeRootModule, rootDir, instManifest, true, wrapHooks, getter) - diags = append(diags, instDiags...) - if instDiags.HasErrors() { - return diags - } - - // If all of that succeeded then we'll now migrate what was installed - // into the final directory structure. - err = os.MkdirAll(modulesDir, os.ModePerm) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to create local modules directory", - fmt.Sprintf("Failed to create modules directory %s: %s.", modulesDir, err), - )) - return diags - } - - recordKeys := make([]string, 0, len(instManifest)) - for k := range instManifest { - recordKeys = append(recordKeys, k) - } - sort.Strings(recordKeys) - - for _, recordKey := range recordKeys { - record := instManifest[recordKey] - - if record.Key == initFromModuleRootCallName { - // We've found the module the user requested, which we must - // now copy into rootDir so it can be used directly. - log.Printf("[TRACE] copying new root module from %s to %s", record.Dir, rootDir) - err := copyDir(rootDir, record.Dir) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to copy root module", - fmt.Sprintf("Error copying root module %q from %s to %s: %s.", sourceAddr, record.Dir, rootDir, err), - )) - continue - } - - // We'll try to load the newly-copied module here just so we can - // sniff for any module calls that ../ out of the root directory - // and must thus be rewritten to be absolute addresses again. - // For now we can't do this rewriting automatically, but we'll - // generate an error to help the user do it manually. - mod, _ := earlyconfig.LoadModule(rootDir) // ignore diagnostics since we're just doing value-add here anyway - if mod != nil { - for _, mc := range mod.ModuleCalls { - if pathTraversesUp(mc.Source) { - packageAddr, givenSubdir := splitAddrSubdir(sourceAddr) - newSubdir := filepath.Join(givenSubdir, mc.Source) - if pathTraversesUp(newSubdir) { - // This should never happen in any reasonable - // configuration since this suggests a path that - // traverses up out of the package root. We'll just - // ignore this, since we'll fail soon enough anyway - // trying to resolve this path when this module is - // loaded. - continue - } - - var newAddr = packageAddr - if newSubdir != "" { - newAddr = fmt.Sprintf("%s//%s", newAddr, filepath.ToSlash(newSubdir)) - } - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Root module references parent directory", - fmt.Sprintf("The requested module %q refers to a module via its parent directory. To use this as a new root module this source string must be rewritten as a remote source address, such as %q.", sourceAddr, newAddr), - )) - continue - } - } - } - - retManifest[""] = modsdir.Record{ - Key: "", - Dir: rootDir, - } - continue - } - - if !strings.HasPrefix(record.Key, initFromModuleRootKeyPrefix) { - // Ignore the *real* root module, whose key is empty, since - // we're only interested in the module named "root" and its - // descendents. - continue - } - - newKey := record.Key[len(initFromModuleRootKeyPrefix):] - instPath := filepath.Join(modulesDir, newKey) - tempPath := filepath.Join(instDir, record.Key) - - // tempPath won't be present for a module that was installed from - // a relative path, so in that case we just record the installation - // directory and assume it was already copied into place as part - // of its parent. - if _, err := os.Stat(tempPath); err != nil { - if !os.IsNotExist(err) { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to stat temporary module install directory", - fmt.Sprintf("Error from stat %s for module %s: %s.", instPath, newKey, err), - )) - continue - } - - var parentKey string - if lastDot := strings.LastIndexByte(newKey, '.'); lastDot != -1 { - parentKey = newKey[:lastDot] - } else { - parentKey = "" // parent is the root module - } - - parentOld := instManifest[initFromModuleRootKeyPrefix+parentKey] - parentNew := retManifest[parentKey] - - // We need to figure out which portion of our directory is the - // parent package path and which portion is the subdirectory - // under that. - baseDirRel, err := filepath.Rel(parentOld.Dir, record.Dir) - if err != nil { - // Should never happen, because we constructed both directories - // from the same base and so they must have a common prefix. - panic(err) - } - - newDir := filepath.Join(parentNew.Dir, baseDirRel) - log.Printf("[TRACE] relative reference for %s rewritten from %s to %s", newKey, record.Dir, newDir) - newRecord := record // shallow copy - newRecord.Dir = newDir - newRecord.Key = newKey - retManifest[newKey] = newRecord - hooks.Install(newRecord.Key, newRecord.Version, newRecord.Dir) - continue - } - - err = os.MkdirAll(instPath, os.ModePerm) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to create module install directory", - fmt.Sprintf("Error creating directory %s for module %s: %s.", instPath, newKey, err), - )) - continue - } - - // We copy rather than "rename" here because renaming between directories - // can be tricky in edge-cases like network filesystems, etc. - log.Printf("[TRACE] copying new module %s from %s to %s", newKey, record.Dir, instPath) - err := copyDir(instPath, tempPath) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to copy descendent module", - fmt.Sprintf("Error copying module %q from %s to %s: %s.", newKey, tempPath, rootDir, err), - )) - continue - } - - subDir, err := filepath.Rel(tempPath, record.Dir) - if err != nil { - // Should never happen, because we constructed both directories - // from the same base and so they must have a common prefix. - panic(err) - } - - newRecord := record // shallow copy - newRecord.Dir = filepath.Join(instPath, subDir) - newRecord.Key = newKey - retManifest[newKey] = newRecord - hooks.Install(newRecord.Key, newRecord.Version, newRecord.Dir) - } - - retManifest.WriteSnapshotToDir(modulesDir) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to write module manifest", - fmt.Sprintf("Error writing module manifest: %s.", err), - )) - } - - if !diags.HasErrors() { - // Try to clean up our temporary directory, but don't worry if we don't - // succeed since it shouldn't hurt anything. - os.RemoveAll(instDir) - } - - return diags -} - -func pathTraversesUp(path string) bool { - return strings.HasPrefix(filepath.ToSlash(path), "../") -} - -// installHooksInitDir is an adapter wrapper for an InstallHooks that -// does some fakery to make downloads look like they are happening in their -// final locations, rather than in the temporary loader we use. -// -// It also suppresses "Install" calls entirely, since InitDirFromModule -// does its own installation steps after the initial installation pass -// has completed. -type installHooksInitDir struct { - Wrapped ModuleInstallHooks - ModuleInstallHooksImpl -} - -func (h installHooksInitDir) Download(moduleAddr, packageAddr string, version *version.Version) { - if !strings.HasPrefix(moduleAddr, initFromModuleRootKeyPrefix) { - // We won't announce the root module, since hook implementations - // don't expect to see that and the caller will usually have produced - // its own user-facing notification about what it's doing anyway. - return - } - - trimAddr := moduleAddr[len(initFromModuleRootKeyPrefix):] - h.Wrapped.Download(trimAddr, packageAddr, version) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/getter.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/getter.go deleted file mode 100644 index 8dc0374b..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/getter.go +++ /dev/null @@ -1,204 +0,0 @@ -package initwd - -import ( - "fmt" - "log" - "os" - "path/filepath" - "strings" - - cleanhttp "github.com/hashicorp/go-cleanhttp" - getter "github.com/hashicorp/go-getter" - "github.com/hashicorp/terraform-plugin-sdk/internal/registry/regsrc" -) - -// We configure our own go-getter detector and getter sets here, because -// the set of sources we support is part of Terraform's documentation and -// so we don't want any new sources introduced in go-getter to sneak in here -// and work even though they aren't documented. This also insulates us from -// any meddling that might be done by other go-getter callers linked into our -// executable. - -var goGetterNoDetectors = []getter.Detector{} - -var goGetterDecompressors = map[string]getter.Decompressor{ - "bz2": new(getter.Bzip2Decompressor), - "gz": new(getter.GzipDecompressor), - "xz": new(getter.XzDecompressor), - "zip": new(getter.ZipDecompressor), - - "tar.bz2": new(getter.TarBzip2Decompressor), - "tar.tbz2": new(getter.TarBzip2Decompressor), - - "tar.gz": new(getter.TarGzipDecompressor), - "tgz": new(getter.TarGzipDecompressor), - - "tar.xz": new(getter.TarXzDecompressor), - "txz": new(getter.TarXzDecompressor), -} - -var goGetterGetters = map[string]getter.Getter{ - "file": new(getter.FileGetter), - "gcs": new(getter.GCSGetter), - "git": new(getter.GitGetter), - "hg": new(getter.HgGetter), - "s3": new(getter.S3Getter), - "http": getterHTTPGetter, - "https": getterHTTPGetter, -} - -var getterHTTPClient = cleanhttp.DefaultClient() - -var getterHTTPGetter = &getter.HttpGetter{ - Client: getterHTTPClient, - Netrc: true, -} - -// A reusingGetter is a helper for the module installer that remembers -// the final resolved addresses of all of the sources it has already been -// asked to install, and will copy from a prior installation directory if -// it has the same resolved source address. -// -// The keys in a reusingGetter are resolved and trimmed source addresses -// (with a scheme always present, and without any "subdir" component), -// and the values are the paths where each source was previously installed. -type reusingGetter map[string]string - -// getWithGoGetter retrieves the package referenced in the given address -// into the installation path and then returns the full path to any subdir -// indicated in the address. -// -// The errors returned by this function are those surfaced by the underlying -// go-getter library, which have very inconsistent quality as -// end-user-actionable error messages. At this time we do not have any -// reasonable way to improve these error messages at this layer because -// the underlying errors are not separately recognizable. -func (g reusingGetter) getWithGoGetter(instPath, addr string) (string, error) { - packageAddr, subDir := splitAddrSubdir(addr) - - log.Printf("[DEBUG] will download %q to %s", packageAddr, instPath) - - realAddr, err := getter.Detect(packageAddr, instPath, getter.Detectors) - if err != nil { - return "", err - } - - if isMaybeRelativeLocalPath(realAddr) { - return "", &MaybeRelativePathErr{addr} - } - - var realSubDir string - realAddr, realSubDir = splitAddrSubdir(realAddr) - if realSubDir != "" { - subDir = filepath.Join(realSubDir, subDir) - } - - if realAddr != packageAddr { - log.Printf("[TRACE] go-getter detectors rewrote %q to %q", packageAddr, realAddr) - } - - if prevDir, exists := g[realAddr]; exists { - log.Printf("[TRACE] copying previous install %s to %s", prevDir, instPath) - err := os.Mkdir(instPath, os.ModePerm) - if err != nil { - return "", fmt.Errorf("failed to create directory %s: %s", instPath, err) - } - err = copyDir(instPath, prevDir) - if err != nil { - return "", fmt.Errorf("failed to copy from %s to %s: %s", prevDir, instPath, err) - } - } else { - log.Printf("[TRACE] fetching %q to %q", realAddr, instPath) - client := getter.Client{ - Src: realAddr, - Dst: instPath, - Pwd: instPath, - - Mode: getter.ClientModeDir, - - Detectors: goGetterNoDetectors, // we already did detection above - Decompressors: goGetterDecompressors, - Getters: goGetterGetters, - } - err = client.Get() - if err != nil { - return "", err - } - // Remember where we installed this so we might reuse this directory - // on subsequent calls to avoid re-downloading. - g[realAddr] = instPath - } - - // Our subDir string can contain wildcards until this point, so that - // e.g. a subDir of * can expand to one top-level directory in a .tar.gz - // archive. Now that we've expanded the archive successfully we must - // resolve that into a concrete path. - var finalDir string - if subDir != "" { - finalDir, err = getter.SubdirGlob(instPath, subDir) - log.Printf("[TRACE] expanded %q to %q", subDir, finalDir) - if err != nil { - return "", err - } - } else { - finalDir = instPath - } - - // If we got this far then we have apparently succeeded in downloading - // the requested object! - return filepath.Clean(finalDir), nil -} - -// splitAddrSubdir splits the given address (which is assumed to be a -// registry address or go-getter-style address) into a package portion -// and a sub-directory portion. -// -// The package portion defines what should be downloaded and then the -// sub-directory portion, if present, specifies a sub-directory within -// the downloaded object (an archive, VCS repository, etc) that contains -// the module's configuration files. -// -// The subDir portion will be returned as empty if no subdir separator -// ("//") is present in the address. -func splitAddrSubdir(addr string) (packageAddr, subDir string) { - return getter.SourceDirSubdir(addr) -} - -var localSourcePrefixes = []string{ - "./", - "../", - ".\\", - "..\\", -} - -func isLocalSourceAddr(addr string) bool { - for _, prefix := range localSourcePrefixes { - if strings.HasPrefix(addr, prefix) { - return true - } - } - return false -} - -func isRegistrySourceAddr(addr string) bool { - _, err := regsrc.ParseModuleSource(addr) - return err == nil -} - -type MaybeRelativePathErr struct { - Addr string -} - -func (e *MaybeRelativePathErr) Error() string { - return fmt.Sprintf("Terraform cannot determine the module source for %s", e.Addr) -} - -func isMaybeRelativeLocalPath(addr string) bool { - if strings.HasPrefix(addr, "file://") { - _, err := os.Stat(addr[7:]) - if err != nil { - return true - } - } - return false -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/inode.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/inode.go deleted file mode 100644 index 1150b093..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/inode.go +++ /dev/null @@ -1,21 +0,0 @@ -// +build linux darwin openbsd netbsd solaris dragonfly - -package initwd - -import ( - "fmt" - "os" - "syscall" -) - -// lookup the inode of a file on posix systems -func inode(path string) (uint64, error) { - stat, err := os.Stat(path) - if err != nil { - return 0, err - } - if st, ok := stat.Sys().(*syscall.Stat_t); ok { - return st.Ino, nil - } - return 0, fmt.Errorf("could not determine file inode") -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/inode_freebsd.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/inode_freebsd.go deleted file mode 100644 index 30532f54..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/inode_freebsd.go +++ /dev/null @@ -1,21 +0,0 @@ -// +build freebsd - -package initwd - -import ( - "fmt" - "os" - "syscall" -) - -// lookup the inode of a file on posix systems -func inode(path string) (uint64, error) { - stat, err := os.Stat(path) - if err != nil { - return 0, err - } - if st, ok := stat.Sys().(*syscall.Stat_t); ok { - return uint64(st.Ino), nil - } - return 0, fmt.Errorf("could not determine file inode") -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/inode_windows.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/inode_windows.go deleted file mode 100644 index 3ed58e4b..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/inode_windows.go +++ /dev/null @@ -1,8 +0,0 @@ -// +build windows - -package initwd - -// no syscall.Stat_t on windows, return 0 for inodes -func inode(path string) (uint64, error) { - return 0, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/module_install.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/module_install.go deleted file mode 100644 index 8e055756..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/module_install.go +++ /dev/null @@ -1,558 +0,0 @@ -package initwd - -import ( - "fmt" - "log" - "os" - "path/filepath" - "strings" - - version "github.com/hashicorp/go-version" - "github.com/hashicorp/terraform-config-inspect/tfconfig" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/earlyconfig" - "github.com/hashicorp/terraform-plugin-sdk/internal/modsdir" - "github.com/hashicorp/terraform-plugin-sdk/internal/registry" - "github.com/hashicorp/terraform-plugin-sdk/internal/registry/regsrc" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -type ModuleInstaller struct { - modsDir string - reg *registry.Client -} - -func NewModuleInstaller(modsDir string, reg *registry.Client) *ModuleInstaller { - return &ModuleInstaller{ - modsDir: modsDir, - reg: reg, - } -} - -// InstallModules analyses the root module in the given directory and installs -// all of its direct and transitive dependencies into the given modules -// directory, which must already exist. -// -// Since InstallModules makes possibly-time-consuming calls to remote services, -// a hook interface is supported to allow the caller to be notified when -// each module is installed and, for remote modules, when downloading begins. -// LoadConfig guarantees that two hook calls will not happen concurrently but -// it does not guarantee any particular ordering of hook calls. This mechanism -// is for UI feedback only and does not give the caller any control over the -// process. -// -// If modules are already installed in the target directory, they will be -// skipped unless their source address or version have changed or unless -// the upgrade flag is set. -// -// InstallModules never deletes any directory, except in the case where it -// needs to replace a directory that is already present with a newly-extracted -// package. -// -// If the returned diagnostics contains errors then the module installation -// may have wholly or partially completed. Modules must be loaded in order -// to find their dependencies, so this function does many of the same checks -// as LoadConfig as a side-effect. -// -// If successful (the returned diagnostics contains no errors) then the -// first return value is the early configuration tree that was constructed by -// the installation process. -func (i *ModuleInstaller) InstallModules(rootDir string, upgrade bool, hooks ModuleInstallHooks) (*earlyconfig.Config, tfdiags.Diagnostics) { - log.Printf("[TRACE] ModuleInstaller: installing child modules for %s into %s", rootDir, i.modsDir) - - rootMod, diags := earlyconfig.LoadModule(rootDir) - if rootMod == nil { - return nil, diags - } - - manifest, err := modsdir.ReadManifestSnapshotForDir(i.modsDir) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to read modules manifest file", - fmt.Sprintf("Error reading manifest for %s: %s.", i.modsDir, err), - )) - return nil, diags - } - - getter := reusingGetter{} - cfg, instDiags := i.installDescendentModules(rootMod, rootDir, manifest, upgrade, hooks, getter) - diags = append(diags, instDiags...) - - return cfg, diags -} - -func (i *ModuleInstaller) installDescendentModules(rootMod *tfconfig.Module, rootDir string, manifest modsdir.Manifest, upgrade bool, hooks ModuleInstallHooks, getter reusingGetter) (*earlyconfig.Config, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - if hooks == nil { - // Use our no-op implementation as a placeholder - hooks = ModuleInstallHooksImpl{} - } - - // Create a manifest record for the root module. This will be used if - // there are any relative-pathed modules in the root. - manifest[""] = modsdir.Record{ - Key: "", - Dir: rootDir, - } - - cfg, cDiags := earlyconfig.BuildConfig(rootMod, earlyconfig.ModuleWalkerFunc( - func(req *earlyconfig.ModuleRequest) (*tfconfig.Module, *version.Version, tfdiags.Diagnostics) { - - key := manifest.ModuleKey(req.Path) - instPath := i.packageInstallPath(req.Path) - - log.Printf("[DEBUG] Module installer: begin %s", key) - - // First we'll check if we need to upgrade/replace an existing - // installed module, and delete it out of the way if so. - replace := upgrade - if !replace { - record, recorded := manifest[key] - switch { - case !recorded: - log.Printf("[TRACE] ModuleInstaller: %s is not yet installed", key) - replace = true - case record.SourceAddr != req.SourceAddr: - log.Printf("[TRACE] ModuleInstaller: %s source address has changed from %q to %q", key, record.SourceAddr, req.SourceAddr) - replace = true - case record.Version != nil && !req.VersionConstraints.Check(record.Version): - log.Printf("[TRACE] ModuleInstaller: %s version %s no longer compatible with constraints %s", key, record.Version, req.VersionConstraints) - replace = true - } - } - - // If we _are_ planning to replace this module, then we'll remove - // it now so our installation code below won't conflict with any - // existing remnants. - if replace { - if _, recorded := manifest[key]; recorded { - log.Printf("[TRACE] ModuleInstaller: discarding previous record of %s prior to reinstall", key) - } - delete(manifest, key) - // Deleting a module invalidates all of its descendent modules too. - keyPrefix := key + "." - for subKey := range manifest { - if strings.HasPrefix(subKey, keyPrefix) { - if _, recorded := manifest[subKey]; recorded { - log.Printf("[TRACE] ModuleInstaller: also discarding downstream %s", subKey) - } - delete(manifest, subKey) - } - } - } - - record, recorded := manifest[key] - if !recorded { - // Clean up any stale cache directory that might be present. - // If this is a local (relative) source then the dir will - // not exist, but we'll ignore that. - log.Printf("[TRACE] ModuleInstaller: cleaning directory %s prior to install of %s", instPath, key) - err := os.RemoveAll(instPath) - if err != nil && !os.IsNotExist(err) { - log.Printf("[TRACE] ModuleInstaller: failed to remove %s: %s", key, err) - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to remove local module cache", - fmt.Sprintf( - "Terraform tried to remove %s in order to reinstall this module, but encountered an error: %s", - instPath, err, - ), - )) - return nil, nil, diags - } - } else { - // If this module is already recorded and its root directory - // exists then we will just load what's already there and - // keep our existing record. - info, err := os.Stat(record.Dir) - if err == nil && info.IsDir() { - mod, mDiags := earlyconfig.LoadModule(record.Dir) - diags = diags.Append(mDiags) - - log.Printf("[TRACE] ModuleInstaller: Module installer: %s %s already installed in %s", key, record.Version, record.Dir) - return mod, record.Version, diags - } - } - - // If we get down here then it's finally time to actually install - // the module. There are some variants to this process depending - // on what type of module source address we have. - switch { - - case isLocalSourceAddr(req.SourceAddr): - log.Printf("[TRACE] ModuleInstaller: %s has local path %q", key, req.SourceAddr) - mod, mDiags := i.installLocalModule(req, key, manifest, hooks) - diags = append(diags, mDiags...) - return mod, nil, diags - - case isRegistrySourceAddr(req.SourceAddr): - addr, err := regsrc.ParseModuleSource(req.SourceAddr) - if err != nil { - // Should never happen because isRegistrySourceAddr already validated - panic(err) - } - log.Printf("[TRACE] ModuleInstaller: %s is a registry module at %s", key, addr) - - mod, v, mDiags := i.installRegistryModule(req, key, instPath, addr, manifest, hooks, getter) - diags = append(diags, mDiags...) - return mod, v, diags - - default: - log.Printf("[TRACE] ModuleInstaller: %s address %q will be handled by go-getter", key, req.SourceAddr) - - mod, mDiags := i.installGoGetterModule(req, key, instPath, manifest, hooks, getter) - diags = append(diags, mDiags...) - return mod, nil, diags - } - - }, - )) - diags = append(diags, cDiags...) - - err := manifest.WriteSnapshotToDir(i.modsDir) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to update module manifest", - fmt.Sprintf("Unable to write the module manifest file: %s", err), - )) - } - - return cfg, diags -} - -func (i *ModuleInstaller) installLocalModule(req *earlyconfig.ModuleRequest, key string, manifest modsdir.Manifest, hooks ModuleInstallHooks) (*tfconfig.Module, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - parentKey := manifest.ModuleKey(req.Parent.Path) - parentRecord, recorded := manifest[parentKey] - if !recorded { - // This is indicative of a bug rather than a user-actionable error - panic(fmt.Errorf("missing manifest record for parent module %s", parentKey)) - } - - if len(req.VersionConstraints) != 0 { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid version constraint", - fmt.Sprintf("Cannot apply a version constraint to module %q (at %s:%d) because it has a relative local path.", req.Name, req.CallPos.Filename, req.CallPos.Line), - )) - } - - // For local sources we don't actually need to modify the - // filesystem at all because the parent already wrote - // the files we need, and so we just load up what's already here. - newDir := filepath.Join(parentRecord.Dir, req.SourceAddr) - - log.Printf("[TRACE] ModuleInstaller: %s uses directory from parent: %s", key, newDir) - // it is possible that the local directory is a symlink - newDir, err := filepath.EvalSymlinks(newDir) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Unreadable module directory", - fmt.Sprintf("Unable to evaluate directory symlink: %s", err.Error()), - )) - } - - mod, mDiags := earlyconfig.LoadModule(newDir) - if mod == nil { - // nil indicates missing or unreadable directory, so we'll - // discard the returned diags and return a more specific - // error message here. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Unreadable module directory", - fmt.Sprintf("The directory %s could not be read for module %q at %s:%d.", newDir, req.Name, req.CallPos.Filename, req.CallPos.Line), - )) - } else { - diags = diags.Append(mDiags) - } - - // Note the local location in our manifest. - manifest[key] = modsdir.Record{ - Key: key, - Dir: newDir, - SourceAddr: req.SourceAddr, - } - log.Printf("[DEBUG] Module installer: %s installed at %s", key, newDir) - hooks.Install(key, nil, newDir) - - return mod, diags -} - -func (i *ModuleInstaller) installRegistryModule(req *earlyconfig.ModuleRequest, key string, instPath string, addr *regsrc.Module, manifest modsdir.Manifest, hooks ModuleInstallHooks, getter reusingGetter) (*tfconfig.Module, *version.Version, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - hostname, err := addr.SvcHost() - if err != nil { - // If it looks like the user was trying to use punycode then we'll generate - // a specialized error for that case. We require the unicode form of - // hostname so that hostnames are always human-readable in configuration - // and punycode can't be used to hide a malicious module hostname. - if strings.HasPrefix(addr.RawHost.Raw, "xn--") { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid module registry hostname", - fmt.Sprintf("The hostname portion of the module %q source address (at %s:%d) is not an acceptable hostname. Internationalized domain names must be given in unicode form rather than ASCII (\"punycode\") form.", req.Name, req.CallPos.Filename, req.CallPos.Line), - )) - } else { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid module registry hostname", - fmt.Sprintf("The hostname portion of the module %q source address (at %s:%d) is not a valid hostname.", req.Name, req.CallPos.Filename, req.CallPos.Line), - )) - } - return nil, nil, diags - } - - reg := i.reg - - log.Printf("[DEBUG] %s listing available versions of %s at %s", key, addr, hostname) - resp, err := reg.ModuleVersions(addr) - if err != nil { - if registry.IsModuleNotFound(err) { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Module not found", - fmt.Sprintf("Module %q (from %s:%d) cannot be found in the module registry at %s.", req.Name, req.CallPos.Filename, req.CallPos.Line, hostname), - )) - } else { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Error accessing remote module registry", - fmt.Sprintf("Failed to retrieve available versions for module %q (%s:%d) from %s: %s.", req.Name, req.CallPos.Filename, req.CallPos.Line, hostname, err), - )) - } - return nil, nil, diags - } - - // The response might contain information about dependencies to allow us - // to potentially optimize future requests, but we don't currently do that - // and so for now we'll just take the first item which is guaranteed to - // be the address we requested. - if len(resp.Modules) < 1 { - // Should never happen, but since this is a remote service that may - // be implemented by third-parties we will handle it gracefully. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid response from remote module registry", - fmt.Sprintf("The registry at %s returned an invalid response when Terraform requested available versions for module %q (%s:%d).", hostname, req.Name, req.CallPos.Filename, req.CallPos.Line), - )) - return nil, nil, diags - } - - modMeta := resp.Modules[0] - - var latestMatch *version.Version - var latestVersion *version.Version - for _, mv := range modMeta.Versions { - v, err := version.NewVersion(mv.Version) - if err != nil { - // Should never happen if the registry server is compliant with - // the protocol, but we'll warn if not to assist someone who - // might be developing a module registry server. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Warning, - "Invalid response from remote module registry", - fmt.Sprintf("The registry at %s returned an invalid version string %q for module %q (%s:%d), which Terraform ignored.", hostname, mv.Version, req.Name, req.CallPos.Filename, req.CallPos.Line), - )) - continue - } - - // If we've found a pre-release version then we'll ignore it unless - // it was exactly requested. - if v.Prerelease() != "" && req.VersionConstraints.String() != v.String() { - log.Printf("[TRACE] ModuleInstaller: %s ignoring %s because it is a pre-release and was not requested exactly", key, v) - continue - } - - if latestVersion == nil || v.GreaterThan(latestVersion) { - latestVersion = v - } - - if req.VersionConstraints.Check(v) { - if latestMatch == nil || v.GreaterThan(latestMatch) { - latestMatch = v - } - } - } - - if latestVersion == nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Module has no versions", - fmt.Sprintf("Module %q (%s:%d) has no versions available on %s.", addr, req.CallPos.Filename, req.CallPos.Line, hostname), - )) - return nil, nil, diags - } - - if latestMatch == nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Unresolvable module version constraint", - fmt.Sprintf("There is no available version of module %q (%s:%d) which matches the given version constraint. The newest available version is %s.", addr, req.CallPos.Filename, req.CallPos.Line, latestVersion), - )) - return nil, nil, diags - } - - // Report up to the caller that we're about to start downloading. - packageAddr, _ := splitAddrSubdir(req.SourceAddr) - hooks.Download(key, packageAddr, latestMatch) - - // If we manage to get down here then we've found a suitable version to - // install, so we need to ask the registry where we should download it from. - // The response to this is a go-getter-style address string. - dlAddr, err := reg.ModuleLocation(addr, latestMatch.String()) - if err != nil { - log.Printf("[ERROR] %s from %s %s: %s", key, addr, latestMatch, err) - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid response from remote module registry", - fmt.Sprintf("The remote registry at %s failed to return a download URL for %s %s.", hostname, addr, latestMatch), - )) - return nil, nil, diags - } - - log.Printf("[TRACE] ModuleInstaller: %s %s %s is available at %q", key, addr, latestMatch, dlAddr) - - modDir, err := getter.getWithGoGetter(instPath, dlAddr) - if err != nil { - // Errors returned by go-getter have very inconsistent quality as - // end-user error messages, but for now we're accepting that because - // we have no way to recognize any specific errors to improve them - // and masking the error entirely would hide valuable diagnostic - // information from the user. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to download module", - fmt.Sprintf("Could not download module %q (%s:%d) source code from %q: %s.", req.Name, req.CallPos.Filename, req.CallPos.Line, dlAddr, err), - )) - return nil, nil, diags - } - - log.Printf("[TRACE] ModuleInstaller: %s %q was downloaded to %s", key, dlAddr, modDir) - - if addr.RawSubmodule != "" { - // Append the user's requested subdirectory to any subdirectory that - // was implied by any of the nested layers we expanded within go-getter. - modDir = filepath.Join(modDir, addr.RawSubmodule) - } - - log.Printf("[TRACE] ModuleInstaller: %s should now be at %s", key, modDir) - - // Finally we are ready to try actually loading the module. - mod, mDiags := earlyconfig.LoadModule(modDir) - if mod == nil { - // nil indicates missing or unreadable directory, so we'll - // discard the returned diags and return a more specific - // error message here. For registry modules this actually - // indicates a bug in the code above, since it's not the - // user's responsibility to create the directory in this case. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Unreadable module directory", - fmt.Sprintf("The directory %s could not be read. This is a bug in Terraform and should be reported.", modDir), - )) - } else { - diags = append(diags, mDiags...) - } - - // Note the local location in our manifest. - manifest[key] = modsdir.Record{ - Key: key, - Version: latestMatch, - Dir: modDir, - SourceAddr: req.SourceAddr, - } - log.Printf("[DEBUG] Module installer: %s installed at %s", key, modDir) - hooks.Install(key, latestMatch, modDir) - - return mod, latestMatch, diags -} - -func (i *ModuleInstaller) installGoGetterModule(req *earlyconfig.ModuleRequest, key string, instPath string, manifest modsdir.Manifest, hooks ModuleInstallHooks, getter reusingGetter) (*tfconfig.Module, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - // Report up to the caller that we're about to start downloading. - packageAddr, _ := splitAddrSubdir(req.SourceAddr) - hooks.Download(key, packageAddr, nil) - - if len(req.VersionConstraints) != 0 { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid version constraint", - fmt.Sprintf("Cannot apply a version constraint to module %q (at %s:%d) because it has a non Registry URL.", req.Name, req.CallPos.Filename, req.CallPos.Line), - )) - return nil, diags - } - - modDir, err := getter.getWithGoGetter(instPath, req.SourceAddr) - if err != nil { - if _, ok := err.(*MaybeRelativePathErr); ok { - log.Printf( - "[TRACE] ModuleInstaller: %s looks like a local path but is missing ./ or ../", - req.SourceAddr, - ) - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Module not found", - fmt.Sprintf( - "The module address %q could not be resolved.\n\n"+ - "If you intended this as a path relative to the current "+ - "module, use \"./%s\" instead. The \"./\" prefix "+ - "indicates that the address is a relative filesystem path.", - req.SourceAddr, req.SourceAddr, - ), - )) - } else { - // Errors returned by go-getter have very inconsistent quality as - // end-user error messages, but for now we're accepting that because - // we have no way to recognize any specific errors to improve them - // and masking the error entirely would hide valuable diagnostic - // information from the user. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to download module", - fmt.Sprintf("Could not download module %q (%s:%d) source code from %q: %s", req.Name, req.CallPos.Filename, req.CallPos.Line, packageAddr, err), - )) - } - return nil, diags - - } - - log.Printf("[TRACE] ModuleInstaller: %s %q was downloaded to %s", key, req.SourceAddr, modDir) - - mod, mDiags := earlyconfig.LoadModule(modDir) - if mod == nil { - // nil indicates missing or unreadable directory, so we'll - // discard the returned diags and return a more specific - // error message here. For go-getter modules this actually - // indicates a bug in the code above, since it's not the - // user's responsibility to create the directory in this case. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Unreadable module directory", - fmt.Sprintf("The directory %s could not be read. This is a bug in Terraform and should be reported.", modDir), - )) - } else { - diags = append(diags, mDiags...) - } - - // Note the local location in our manifest. - manifest[key] = modsdir.Record{ - Key: key, - Dir: modDir, - SourceAddr: req.SourceAddr, - } - log.Printf("[DEBUG] Module installer: %s installed at %s", key, modDir) - hooks.Install(key, nil, modDir) - - return mod, diags -} - -func (i *ModuleInstaller) packageInstallPath(modulePath addrs.Module) string { - return filepath.Join(i.modsDir, strings.Join(modulePath, ".")) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/module_install_hooks.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/module_install_hooks.go deleted file mode 100644 index 817a6dc8..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/initwd/module_install_hooks.go +++ /dev/null @@ -1,36 +0,0 @@ -package initwd - -import ( - version "github.com/hashicorp/go-version" -) - -// ModuleInstallHooks is an interface used to provide notifications about the -// installation process being orchestrated by InstallModules. -// -// This interface may have new methods added in future, so implementers should -// embed InstallHooksImpl to get no-op implementations of any unimplemented -// methods. -type ModuleInstallHooks interface { - // Download is called for modules that are retrieved from a remote source - // before that download begins, to allow a caller to give feedback - // on progress through a possibly-long sequence of downloads. - Download(moduleAddr, packageAddr string, version *version.Version) - - // Install is called for each module that is installed, even if it did - // not need to be downloaded from a remote source. - Install(moduleAddr string, version *version.Version, localPath string) -} - -// ModuleInstallHooksImpl is a do-nothing implementation of InstallHooks that -// can be embedded in another implementation struct to allow only partial -// implementation of the interface. -type ModuleInstallHooksImpl struct { -} - -func (h ModuleInstallHooksImpl) Download(moduleAddr, packageAddr string, version *version.Version) { -} - -func (h ModuleInstallHooksImpl) Install(moduleAddr string, version *version.Version, localPath string) { -} - -var _ ModuleInstallHooks = ModuleInstallHooksImpl{} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/blocktoattr/doc.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/blocktoattr/doc.go deleted file mode 100644 index 8f89909c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/blocktoattr/doc.go +++ /dev/null @@ -1,5 +0,0 @@ -// Package blocktoattr includes some helper functions that can perform -// preprocessing on a HCL body where a configschema.Block schema is available -// in order to allow list and set attributes defined in the schema to be -// optionally written by the user as block syntax. -package blocktoattr diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/blocktoattr/fixup.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/blocktoattr/fixup.go deleted file mode 100644 index f782f6b7..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/blocktoattr/fixup.go +++ /dev/null @@ -1,187 +0,0 @@ -package blocktoattr - -import ( - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/zclconf/go-cty/cty" -) - -// FixUpBlockAttrs takes a raw HCL body and adds some additional normalization -// functionality to allow attributes that are specified as having list or set -// type in the schema to be written with HCL block syntax as multiple nested -// blocks with the attribute name as the block type. -// -// This partially restores some of the block/attribute confusion from HCL 1 -// so that existing patterns that depended on that confusion can continue to -// be used in the short term while we settle on a longer-term strategy. -// -// Most of the fixup work is actually done when the returned body is -// subsequently decoded, so while FixUpBlockAttrs always succeeds, the eventual -// decode of the body might not, if the content of the body is so ambiguous -// that there's no safe way to map it to the schema. -func FixUpBlockAttrs(body hcl.Body, schema *configschema.Block) hcl.Body { - // The schema should never be nil, but in practice it seems to be sometimes - // in the presence of poorly-configured test mocks, so we'll be robust - // by synthesizing an empty one. - if schema == nil { - schema = &configschema.Block{} - } - - return &fixupBody{ - original: body, - schema: schema, - names: ambiguousNames(schema), - } -} - -type fixupBody struct { - original hcl.Body - schema *configschema.Block - names map[string]struct{} -} - -// Content decodes content from the body. The given schema must be the lower-level -// representation of the same schema that was previously passed to FixUpBlockAttrs, -// or else the result is undefined. -func (b *fixupBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) { - schema = b.effectiveSchema(schema) - content, diags := b.original.Content(schema) - return b.fixupContent(content), diags -} - -func (b *fixupBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) { - schema = b.effectiveSchema(schema) - content, remain, diags := b.original.PartialContent(schema) - remain = &fixupBody{ - original: remain, - schema: b.schema, - names: b.names, - } - return b.fixupContent(content), remain, diags -} - -func (b *fixupBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) { - // FixUpBlockAttrs is not intended to be used in situations where we'd use - // JustAttributes, so we just pass this through verbatim to complete our - // implementation of hcl.Body. - return b.original.JustAttributes() -} - -func (b *fixupBody) MissingItemRange() hcl.Range { - return b.original.MissingItemRange() -} - -// effectiveSchema produces a derived *hcl.BodySchema by sniffing the body's -// content to determine whether the author has used attribute or block syntax -// for each of the ambigious attributes where both are permitted. -// -// The resulting schema will always contain all of the same names that are -// in the given schema, but some attribute schemas may instead be replaced by -// block header schemas. -func (b *fixupBody) effectiveSchema(given *hcl.BodySchema) *hcl.BodySchema { - return effectiveSchema(given, b.original, b.names, true) -} - -func (b *fixupBody) fixupContent(content *hcl.BodyContent) *hcl.BodyContent { - var ret hcl.BodyContent - ret.Attributes = make(hcl.Attributes) - for name, attr := range content.Attributes { - ret.Attributes[name] = attr - } - blockAttrVals := make(map[string][]*hcl.Block) - for _, block := range content.Blocks { - if _, exists := b.names[block.Type]; exists { - // If we get here then we've found a block type whose instances need - // to be re-interpreted as a list-of-objects attribute. We'll gather - // those up and fix them up below. - blockAttrVals[block.Type] = append(blockAttrVals[block.Type], block) - continue - } - - // We need to now re-wrap our inner body so it will be subject to the - // same attribute-as-block fixup when recursively decoded. - retBlock := *block // shallow copy - if blockS, ok := b.schema.BlockTypes[block.Type]; ok { - // Would be weird if not ok, but we'll allow it for robustness; body just won't be fixed up, then - retBlock.Body = FixUpBlockAttrs(retBlock.Body, &blockS.Block) - } - - ret.Blocks = append(ret.Blocks, &retBlock) - } - // No we'll install synthetic attributes for each of our fixups. We can't - // do this exactly because HCL's information model expects an attribute - // to be a single decl but we have multiple separate blocks. We'll - // approximate things, then, by using only our first block for the source - // location information. (We are guaranteed at least one by the above logic.) - for name, blocks := range blockAttrVals { - ret.Attributes[name] = &hcl.Attribute{ - Name: name, - Expr: &fixupBlocksExpr{ - blocks: blocks, - ety: b.schema.Attributes[name].Type.ElementType(), - }, - - Range: blocks[0].DefRange, - NameRange: blocks[0].TypeRange, - } - } - return &ret -} - -type fixupBlocksExpr struct { - blocks hcl.Blocks - ety cty.Type -} - -func (e *fixupBlocksExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { - // In order to produce a suitable value for our expression we need to - // now decode the whole descendent block structure under each of our block - // bodies. - // - // That requires us to do something rather strange: we must construct a - // synthetic block type schema derived from the element type of the - // attribute, thus inverting our usual direction of lowering a schema - // into an implied type. Because a type is less detailed than a schema, - // the result is imprecise and in particular will just consider all - // the attributes to be optional and let the provider eventually decide - // whether to return errors if they turn out to be null when required. - schema := SchemaForCtyElementType(e.ety) // this schema's ImpliedType will match e.ety - spec := schema.DecoderSpec() - - vals := make([]cty.Value, len(e.blocks)) - var diags hcl.Diagnostics - for i, block := range e.blocks { - body := FixUpBlockAttrs(block.Body, schema) - val, blockDiags := hcldec.Decode(body, spec, ctx) - diags = append(diags, blockDiags...) - if val == cty.NilVal { - val = cty.UnknownVal(e.ety) - } - vals[i] = val - } - if len(vals) == 0 { - return cty.ListValEmpty(e.ety), diags - } - return cty.ListVal(vals), diags -} - -func (e *fixupBlocksExpr) Variables() []hcl.Traversal { - var ret []hcl.Traversal - schema := SchemaForCtyElementType(e.ety) - spec := schema.DecoderSpec() - for _, block := range e.blocks { - ret = append(ret, hcldec.Variables(block.Body, spec)...) - } - return ret -} - -func (e *fixupBlocksExpr) Range() hcl.Range { - // This is not really an appropriate range for the expression but it's - // the best we can do from here. - return e.blocks[0].DefRange -} - -func (e *fixupBlocksExpr) StartRange() hcl.Range { - return e.blocks[0].DefRange -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/blocktoattr/schema.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/blocktoattr/schema.go deleted file mode 100644 index 129ee0e8..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/blocktoattr/schema.go +++ /dev/null @@ -1,119 +0,0 @@ -package blocktoattr - -import ( - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/zclconf/go-cty/cty" -) - -func ambiguousNames(schema *configschema.Block) map[string]struct{} { - if schema == nil { - return nil - } - ambiguousNames := make(map[string]struct{}) - for name, attrS := range schema.Attributes { - aty := attrS.Type - if (aty.IsListType() || aty.IsSetType()) && aty.ElementType().IsObjectType() { - ambiguousNames[name] = struct{}{} - } - } - return ambiguousNames -} - -func effectiveSchema(given *hcl.BodySchema, body hcl.Body, ambiguousNames map[string]struct{}, dynamicExpanded bool) *hcl.BodySchema { - ret := &hcl.BodySchema{} - - appearsAsBlock := make(map[string]struct{}) - { - // We'll construct some throwaway schemas here just to probe for - // whether each of our ambiguous names seems to be being used as - // an attribute or a block. We need to check both because in JSON - // syntax we rely on the schema to decide between attribute or block - // interpretation and so JSON will always answer yes to both of - // these questions and we want to prefer the attribute interpretation - // in that case. - var probeSchema hcl.BodySchema - - for name := range ambiguousNames { - probeSchema = hcl.BodySchema{ - Attributes: []hcl.AttributeSchema{ - { - Name: name, - }, - }, - } - content, _, _ := body.PartialContent(&probeSchema) - if _, exists := content.Attributes[name]; exists { - // Can decode as an attribute, so we'll go with that. - continue - } - probeSchema = hcl.BodySchema{ - Blocks: []hcl.BlockHeaderSchema{ - { - Type: name, - }, - }, - } - content, _, _ = body.PartialContent(&probeSchema) - if len(content.Blocks) > 0 || dynamicExpanded { - // A dynamic block with an empty iterator returns nothing. - // If there's no attribute and we have either a block or a - // dynamic expansion, we need to rewrite this one as a - // block for a successful result. - appearsAsBlock[name] = struct{}{} - } - } - if !dynamicExpanded { - // If we're deciding for a context where dynamic blocks haven't - // been expanded yet then we need to probe for those too. - probeSchema = hcl.BodySchema{ - Blocks: []hcl.BlockHeaderSchema{ - { - Type: "dynamic", - LabelNames: []string{"type"}, - }, - }, - } - content, _, _ := body.PartialContent(&probeSchema) - for _, block := range content.Blocks { - if _, exists := ambiguousNames[block.Labels[0]]; exists { - appearsAsBlock[block.Labels[0]] = struct{}{} - } - } - } - } - - for _, attrS := range given.Attributes { - if _, exists := appearsAsBlock[attrS.Name]; exists { - ret.Blocks = append(ret.Blocks, hcl.BlockHeaderSchema{ - Type: attrS.Name, - }) - } else { - ret.Attributes = append(ret.Attributes, attrS) - } - } - - // Anything that is specified as a block type in the input schema remains - // that way by just passing through verbatim. - ret.Blocks = append(ret.Blocks, given.Blocks...) - - return ret -} - -// SchemaForCtyElementType converts a cty object type into an -// approximately-equivalent configschema.Block representing the element of -// a list or set. If the given type is not an object type then this -// function will panic. -func SchemaForCtyElementType(ty cty.Type) *configschema.Block { - atys := ty.AttributeTypes() - ret := &configschema.Block{ - Attributes: make(map[string]*configschema.Attribute, len(atys)), - } - for name, aty := range atys { - ret.Attributes[name] = &configschema.Attribute{ - Type: aty, - Optional: true, - } - } - return ret -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/blocktoattr/variables.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/blocktoattr/variables.go deleted file mode 100644 index f5ed1c53..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/blocktoattr/variables.go +++ /dev/null @@ -1,45 +0,0 @@ -package blocktoattr - -import ( - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/ext/dynblock" - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" -) - -// ExpandedVariables finds all of the global variables referenced in the -// given body with the given schema while taking into account the possibilities -// both of "dynamic" blocks being expanded and the possibility of certain -// attributes being written instead as nested blocks as allowed by the -// FixUpBlockAttrs function. -// -// This function exists to allow variables to be analyzed prior to dynamic -// block expansion while also dealing with the fact that dynamic block expansion -// might in turn produce nested blocks that are subject to FixUpBlockAttrs. -// -// This is intended as a drop-in replacement for dynblock.VariablesHCLDec, -// which is itself a drop-in replacement for hcldec.Variables. -func ExpandedVariables(body hcl.Body, schema *configschema.Block) []hcl.Traversal { - rootNode := dynblock.WalkVariables(body) - return walkVariables(rootNode, body, schema) -} - -func walkVariables(node dynblock.WalkVariablesNode, body hcl.Body, schema *configschema.Block) []hcl.Traversal { - givenRawSchema := hcldec.ImpliedSchema(schema.DecoderSpec()) - ambiguousNames := ambiguousNames(schema) - effectiveRawSchema := effectiveSchema(givenRawSchema, body, ambiguousNames, false) - vars, children := node.Visit(effectiveRawSchema) - - for _, child := range children { - if blockS, exists := schema.BlockTypes[child.BlockTypeName]; exists { - vars = append(vars, walkVariables(child.Node, child.Body(), &blockS.Block)...) - } else if attrS, exists := schema.Attributes[child.BlockTypeName]; exists && attrS.Type.IsCollectionType() && attrS.Type.ElementType().IsObjectType() { - // ☝️Check for collection type before element type, because if this is a mis-placed reference, - // a panic here will prevent other useful diags from being elevated to show the user what to fix - synthSchema := SchemaForCtyElementType(attrS.Type.ElementType()) - vars = append(vars, walkVariables(child.Node, child.Body(), synthSchema)...) - } - } - - return vars -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/data.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/data.go deleted file mode 100644 index 13f7ed93..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/data.go +++ /dev/null @@ -1,34 +0,0 @@ -package lang - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - "github.com/zclconf/go-cty/cty" -) - -// Data is an interface whose implementations can provide cty.Value -// representations of objects identified by referenceable addresses from -// the addrs package. -// -// This interface will grow each time a new type of reference is added, and so -// implementations outside of the Terraform codebases are not advised. -// -// Each method returns a suitable value and optionally some diagnostics. If the -// returned diagnostics contains errors then the type of the returned value is -// used to construct an unknown value of the same type which is then used in -// place of the requested object so that type checking can still proceed. In -// cases where it's not possible to even determine a suitable result type, -// cty.DynamicVal is returned along with errors describing the problem. -type Data interface { - StaticValidateReferences(refs []*addrs.Reference, self addrs.Referenceable) tfdiags.Diagnostics - - GetCountAttr(addrs.CountAttr, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) - GetForEachAttr(addrs.ForEachAttr, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) - GetResource(addrs.Resource, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) - GetLocalValue(addrs.LocalValue, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) - GetModuleInstance(addrs.ModuleCallInstance, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) - GetModuleInstanceOutput(addrs.ModuleCallOutput, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) - GetPathAttr(addrs.PathAttr, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) - GetTerraformAttr(addrs.TerraformAttr, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) - GetInputVariable(addrs.InputVariable, tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/doc.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/doc.go deleted file mode 100644 index af5c5cac..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/doc.go +++ /dev/null @@ -1,5 +0,0 @@ -// Package lang deals with the runtime aspects of Terraform's configuration -// language, with concerns such as expression evaluation. It is closely related -// to sibling package "configs", which is responsible for configuration -// parsing and static validation. -package lang diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/eval.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/eval.go deleted file mode 100644 index ec48a873..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/eval.go +++ /dev/null @@ -1,473 +0,0 @@ -package lang - -import ( - "fmt" - "log" - "strconv" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/ext/dynblock" - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/lang/blocktoattr" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" -) - -// ExpandBlock expands any "dynamic" blocks present in the given body. The -// result is a body with those blocks expanded, ready to be evaluated with -// EvalBlock. -// -// If the returned diagnostics contains errors then the result may be -// incomplete or invalid. -func (s *Scope) ExpandBlock(body hcl.Body, schema *configschema.Block) (hcl.Body, tfdiags.Diagnostics) { - spec := schema.DecoderSpec() - - traversals := dynblock.ExpandVariablesHCLDec(body, spec) - refs, diags := References(traversals) - - ctx, ctxDiags := s.EvalContext(refs) - diags = diags.Append(ctxDiags) - - return dynblock.Expand(body, ctx), diags -} - -// EvalBlock evaluates the given body using the given block schema and returns -// a cty object value representing its contents. The type of the result conforms -// to the implied type of the given schema. -// -// This function does not automatically expand "dynamic" blocks within the -// body. If that is desired, first call the ExpandBlock method to obtain -// an expanded body to pass to this method. -// -// If the returned diagnostics contains errors then the result may be -// incomplete or invalid. -func (s *Scope) EvalBlock(body hcl.Body, schema *configschema.Block) (cty.Value, tfdiags.Diagnostics) { - spec := schema.DecoderSpec() - - refs, diags := ReferencesInBlock(body, schema) - - ctx, ctxDiags := s.EvalContext(refs) - diags = diags.Append(ctxDiags) - if diags.HasErrors() { - // We'll stop early if we found problems in the references, because - // it's likely evaluation will produce redundant copies of the same errors. - return cty.UnknownVal(schema.ImpliedType()), diags - } - - // HACK: In order to remain compatible with some assumptions made in - // Terraform v0.11 and earlier about the approximate equivalence of - // attribute vs. block syntax, we do a just-in-time fixup here to allow - // any attribute in the schema that has a list-of-objects or set-of-objects - // kind to potentially be populated instead by one or more nested blocks - // whose type is the attribute name. - body = blocktoattr.FixUpBlockAttrs(body, schema) - - val, evalDiags := hcldec.Decode(body, spec, ctx) - diags = diags.Append(evalDiags) - - return val, diags -} - -// EvalExpr evaluates a single expression in the receiving context and returns -// the resulting value. The value will be converted to the given type before -// it is returned if possible, or else an error diagnostic will be produced -// describing the conversion error. -// -// Pass an expected type of cty.DynamicPseudoType to skip automatic conversion -// and just obtain the returned value directly. -// -// If the returned diagnostics contains errors then the result may be -// incomplete, but will always be of the requested type. -func (s *Scope) EvalExpr(expr hcl.Expression, wantType cty.Type) (cty.Value, tfdiags.Diagnostics) { - refs, diags := ReferencesInExpr(expr) - - ctx, ctxDiags := s.EvalContext(refs) - diags = diags.Append(ctxDiags) - if diags.HasErrors() { - // We'll stop early if we found problems in the references, because - // it's likely evaluation will produce redundant copies of the same errors. - return cty.UnknownVal(wantType), diags - } - - val, evalDiags := expr.Value(ctx) - diags = diags.Append(evalDiags) - - if wantType != cty.DynamicPseudoType { - var convErr error - val, convErr = convert.Convert(val, wantType) - if convErr != nil { - val = cty.UnknownVal(wantType) - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Incorrect value type", - Detail: fmt.Sprintf("Invalid expression value: %s.", tfdiags.FormatError(convErr)), - Subject: expr.Range().Ptr(), - }) - } - } - - return val, diags -} - -// EvalReference evaluates the given reference in the receiving scope and -// returns the resulting value. The value will be converted to the given type before -// it is returned if possible, or else an error diagnostic will be produced -// describing the conversion error. -// -// Pass an expected type of cty.DynamicPseudoType to skip automatic conversion -// and just obtain the returned value directly. -// -// If the returned diagnostics contains errors then the result may be -// incomplete, but will always be of the requested type. -func (s *Scope) EvalReference(ref *addrs.Reference, wantType cty.Type) (cty.Value, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - // We cheat a bit here and just build an EvalContext for our requested - // reference with the "self" address overridden, and then pull the "self" - // result out of it to return. - ctx, ctxDiags := s.evalContext([]*addrs.Reference{ref}, ref.Subject) - diags = diags.Append(ctxDiags) - val := ctx.Variables["self"] - if val == cty.NilVal { - val = cty.DynamicVal - } - - var convErr error - val, convErr = convert.Convert(val, wantType) - if convErr != nil { - val = cty.UnknownVal(wantType) - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Incorrect value type", - Detail: fmt.Sprintf("Invalid expression value: %s.", tfdiags.FormatError(convErr)), - Subject: ref.SourceRange.ToHCL().Ptr(), - }) - } - - return val, diags -} - -// EvalContext constructs a HCL expression evaluation context whose variable -// scope contains sufficient values to satisfy the given set of references. -// -// Most callers should prefer to use the evaluation helper methods that -// this type offers, but this is here for less common situations where the -// caller will handle the evaluation calls itself. -func (s *Scope) EvalContext(refs []*addrs.Reference) (*hcl.EvalContext, tfdiags.Diagnostics) { - return s.evalContext(refs, s.SelfAddr) -} - -func (s *Scope) evalContext(refs []*addrs.Reference, selfAddr addrs.Referenceable) (*hcl.EvalContext, tfdiags.Diagnostics) { - if s == nil { - panic("attempt to construct EvalContext for nil Scope") - } - - var diags tfdiags.Diagnostics - vals := make(map[string]cty.Value) - funcs := s.Functions() - ctx := &hcl.EvalContext{ - Variables: vals, - Functions: funcs, - } - - if len(refs) == 0 { - // Easy path for common case where there are no references at all. - return ctx, diags - } - - // First we'll do static validation of the references. This catches things - // early that might otherwise not get caught due to unknown values being - // present in the scope during planning. - if staticDiags := s.Data.StaticValidateReferences(refs, selfAddr); staticDiags.HasErrors() { - diags = diags.Append(staticDiags) - return ctx, diags - } - - // The reference set we are given has not been de-duped, and so there can - // be redundant requests in it for two reasons: - // - The same item is referenced multiple times - // - Both an item and that item's container are separately referenced. - // We will still visit every reference here and ask our data source for - // it, since that allows us to gather a full set of any errors and - // warnings, but once we've gathered all the data we'll then skip anything - // that's redundant in the process of populating our values map. - dataResources := map[string]map[string]cty.Value{} - managedResources := map[string]map[string]cty.Value{} - wholeModules := map[string]map[addrs.InstanceKey]cty.Value{} - moduleOutputs := map[string]map[addrs.InstanceKey]map[string]cty.Value{} - inputVariables := map[string]cty.Value{} - localValues := map[string]cty.Value{} - pathAttrs := map[string]cty.Value{} - terraformAttrs := map[string]cty.Value{} - countAttrs := map[string]cty.Value{} - forEachAttrs := map[string]cty.Value{} - var self cty.Value - - for _, ref := range refs { - rng := ref.SourceRange - - rawSubj := ref.Subject - if rawSubj == addrs.Self { - if selfAddr == nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Invalid "self" reference`, - // This detail message mentions some current practice that - // this codepath doesn't really "know about". If the "self" - // object starts being supported in more contexts later then - // we'll need to adjust this message. - Detail: `The "self" object is not available in this context. This object can be used only in resource provisioner and connection blocks.`, - Subject: ref.SourceRange.ToHCL().Ptr(), - }) - continue - } - - if selfAddr == addrs.Self { - // Programming error: the self address cannot alias itself. - panic("scope SelfAddr attempting to alias itself") - } - - // self can only be used within a resource instance - subj := selfAddr.(addrs.ResourceInstance) - - val, valDiags := normalizeRefValue(s.Data.GetResource(subj.ContainingResource(), rng)) - - diags = diags.Append(valDiags) - - // Self is an exception in that it must always resolve to a - // particular instance. We will still insert the full resource into - // the context below. - switch k := subj.Key.(type) { - case addrs.IntKey: - self = val.Index(cty.NumberIntVal(int64(k))) - case addrs.StringKey: - self = val.Index(cty.StringVal(string(k))) - default: - self = val - } - - continue - } - - // This type switch must cover all of the "Referenceable" implementations - // in package addrs, however we are removing the possibility of - // ResourceInstance beforehand. - if addr, ok := rawSubj.(addrs.ResourceInstance); ok { - rawSubj = addr.ContainingResource() - } - - switch subj := rawSubj.(type) { - case addrs.Resource: - var into map[string]map[string]cty.Value - switch subj.Mode { - case addrs.ManagedResourceMode: - into = managedResources - case addrs.DataResourceMode: - into = dataResources - default: - panic(fmt.Errorf("unsupported ResourceMode %s", subj.Mode)) - } - - val, valDiags := normalizeRefValue(s.Data.GetResource(subj, rng)) - diags = diags.Append(valDiags) - - r := subj - if into[r.Type] == nil { - into[r.Type] = make(map[string]cty.Value) - } - into[r.Type][r.Name] = val - - case addrs.ModuleCallInstance: - val, valDiags := normalizeRefValue(s.Data.GetModuleInstance(subj, rng)) - diags = diags.Append(valDiags) - - if wholeModules[subj.Call.Name] == nil { - wholeModules[subj.Call.Name] = make(map[addrs.InstanceKey]cty.Value) - } - wholeModules[subj.Call.Name][subj.Key] = val - - case addrs.ModuleCallOutput: - val, valDiags := normalizeRefValue(s.Data.GetModuleInstanceOutput(subj, rng)) - diags = diags.Append(valDiags) - - callName := subj.Call.Call.Name - callKey := subj.Call.Key - if moduleOutputs[callName] == nil { - moduleOutputs[callName] = make(map[addrs.InstanceKey]map[string]cty.Value) - } - if moduleOutputs[callName][callKey] == nil { - moduleOutputs[callName][callKey] = make(map[string]cty.Value) - } - moduleOutputs[callName][callKey][subj.Name] = val - - case addrs.InputVariable: - val, valDiags := normalizeRefValue(s.Data.GetInputVariable(subj, rng)) - diags = diags.Append(valDiags) - inputVariables[subj.Name] = val - - case addrs.LocalValue: - val, valDiags := normalizeRefValue(s.Data.GetLocalValue(subj, rng)) - diags = diags.Append(valDiags) - localValues[subj.Name] = val - - case addrs.PathAttr: - val, valDiags := normalizeRefValue(s.Data.GetPathAttr(subj, rng)) - diags = diags.Append(valDiags) - pathAttrs[subj.Name] = val - - case addrs.TerraformAttr: - val, valDiags := normalizeRefValue(s.Data.GetTerraformAttr(subj, rng)) - diags = diags.Append(valDiags) - terraformAttrs[subj.Name] = val - - case addrs.CountAttr: - val, valDiags := normalizeRefValue(s.Data.GetCountAttr(subj, rng)) - diags = diags.Append(valDiags) - countAttrs[subj.Name] = val - - case addrs.ForEachAttr: - val, valDiags := normalizeRefValue(s.Data.GetForEachAttr(subj, rng)) - diags = diags.Append(valDiags) - forEachAttrs[subj.Name] = val - - default: - // Should never happen - panic(fmt.Errorf("Scope.buildEvalContext cannot handle address type %T", rawSubj)) - } - } - - for k, v := range buildResourceObjects(managedResources) { - vals[k] = v - } - vals["data"] = cty.ObjectVal(buildResourceObjects(dataResources)) - vals["module"] = cty.ObjectVal(buildModuleObjects(wholeModules, moduleOutputs)) - vals["var"] = cty.ObjectVal(inputVariables) - vals["local"] = cty.ObjectVal(localValues) - vals["path"] = cty.ObjectVal(pathAttrs) - vals["terraform"] = cty.ObjectVal(terraformAttrs) - vals["count"] = cty.ObjectVal(countAttrs) - vals["each"] = cty.ObjectVal(forEachAttrs) - if self != cty.NilVal { - vals["self"] = self - } - - return ctx, diags -} - -func buildResourceObjects(resources map[string]map[string]cty.Value) map[string]cty.Value { - vals := make(map[string]cty.Value) - for typeName, nameVals := range resources { - vals[typeName] = cty.ObjectVal(nameVals) - } - return vals -} - -func buildModuleObjects(wholeModules map[string]map[addrs.InstanceKey]cty.Value, moduleOutputs map[string]map[addrs.InstanceKey]map[string]cty.Value) map[string]cty.Value { - vals := make(map[string]cty.Value) - - for name, keys := range wholeModules { - vals[name] = buildInstanceObjects(keys) - } - - for name, keys := range moduleOutputs { - if _, exists := wholeModules[name]; exists { - // If we also have a whole module value for this name then we'll - // skip this since the individual outputs are embedded in that result. - continue - } - - // The shape of this collection isn't compatible with buildInstanceObjects, - // but rather than replicating most of the buildInstanceObjects logic - // here we'll instead first transform the structure to be what that - // function expects and then use it. This is a little wasteful, but - // we do not expect this these maps to be large and so the extra work - // here should not hurt too much. - flattened := make(map[addrs.InstanceKey]cty.Value, len(keys)) - for k, vals := range keys { - flattened[k] = cty.ObjectVal(vals) - } - vals[name] = buildInstanceObjects(flattened) - } - - return vals -} - -func buildInstanceObjects(keys map[addrs.InstanceKey]cty.Value) cty.Value { - if val, exists := keys[addrs.NoKey]; exists { - // If present, a "no key" value supersedes all other values, - // since they should be embedded inside it. - return val - } - - // If we only have individual values then we need to construct - // either a list or a map, depending on what sort of keys we - // have. - haveInt := false - haveString := false - maxInt := 0 - - for k := range keys { - switch tk := k.(type) { - case addrs.IntKey: - haveInt = true - if int(tk) > maxInt { - maxInt = int(tk) - } - case addrs.StringKey: - haveString = true - } - } - - // We should either have ints or strings and not both, but - // if we have both then we'll prefer strings and let the - // language interpreter try to convert the int keys into - // strings in a map. - switch { - case haveString: - vals := make(map[string]cty.Value) - for k, v := range keys { - switch tk := k.(type) { - case addrs.StringKey: - vals[string(tk)] = v - case addrs.IntKey: - sk := strconv.Itoa(int(tk)) - vals[sk] = v - } - } - return cty.ObjectVal(vals) - case haveInt: - // We'll make a tuple that is long enough for our maximum - // index value. It doesn't matter if we end up shorter than - // the number of instances because if length(...) were - // being evaluated we would've got a NoKey reference and - // thus not ended up in this codepath at all. - vals := make([]cty.Value, maxInt+1) - for i := range vals { - if v, exists := keys[addrs.IntKey(i)]; exists { - vals[i] = v - } else { - // Just a placeholder, since nothing will access this anyway - vals[i] = cty.DynamicVal - } - } - return cty.TupleVal(vals) - default: - // Should never happen because there are no other key types. - log.Printf("[ERROR] strange makeInstanceObjects call with no supported key types") - return cty.EmptyObjectVal - } -} - -func normalizeRefValue(val cty.Value, diags tfdiags.Diagnostics) (cty.Value, tfdiags.Diagnostics) { - if diags.HasErrors() { - // If there are errors then we will force an unknown result so that - // we can still evaluate and catch type errors but we'll avoid - // producing redundant re-statements of the same errors we've already - // dealt with here. - return cty.UnknownVal(val.Type()), diags - } - return val, diags -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/cidr.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/cidr.go deleted file mode 100644 index 8c075148..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/cidr.go +++ /dev/null @@ -1,218 +0,0 @@ -package funcs - -import ( - "fmt" - "net" - - "github.com/apparentlymart/go-cidr/cidr" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" - "github.com/zclconf/go-cty/cty/gocty" -) - -// CidrHostFunc contructs a function that calculates a full host IP address -// within a given IP network address prefix. -var CidrHostFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "prefix", - Type: cty.String, - }, - { - Name: "hostnum", - Type: cty.Number, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - var hostNum int - if err := gocty.FromCtyValue(args[1], &hostNum); err != nil { - return cty.UnknownVal(cty.String), err - } - _, network, err := net.ParseCIDR(args[0].AsString()) - if err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err) - } - - ip, err := cidr.Host(network, hostNum) - if err != nil { - return cty.UnknownVal(cty.String), err - } - - return cty.StringVal(ip.String()), nil - }, -}) - -// CidrNetmaskFunc contructs a function that converts an IPv4 address prefix given -// in CIDR notation into a subnet mask address. -var CidrNetmaskFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "prefix", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - _, network, err := net.ParseCIDR(args[0].AsString()) - if err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err) - } - - return cty.StringVal(net.IP(network.Mask).String()), nil - }, -}) - -// CidrSubnetFunc contructs a function that calculates a subnet address within -// a given IP network address prefix. -var CidrSubnetFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "prefix", - Type: cty.String, - }, - { - Name: "newbits", - Type: cty.Number, - }, - { - Name: "netnum", - Type: cty.Number, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - var newbits int - if err := gocty.FromCtyValue(args[1], &newbits); err != nil { - return cty.UnknownVal(cty.String), err - } - var netnum int - if err := gocty.FromCtyValue(args[2], &netnum); err != nil { - return cty.UnknownVal(cty.String), err - } - - _, network, err := net.ParseCIDR(args[0].AsString()) - if err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err) - } - - // For portability with 32-bit systems where the subnet number - // will be a 32-bit int, we only allow extension of 32 bits in - // one call even if we're running on a 64-bit machine. - // (Of course, this is significant only for IPv6.) - if newbits > 32 { - return cty.UnknownVal(cty.String), fmt.Errorf("may not extend prefix by more than 32 bits") - } - - newNetwork, err := cidr.Subnet(network, newbits, netnum) - if err != nil { - return cty.UnknownVal(cty.String), err - } - - return cty.StringVal(newNetwork.String()), nil - }, -}) - -// CidrSubnetsFunc is similar to CidrSubnetFunc but calculates many consecutive -// subnet addresses at once, rather than just a single subnet extension. -var CidrSubnetsFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "prefix", - Type: cty.String, - }, - }, - VarParam: &function.Parameter{ - Name: "newbits", - Type: cty.Number, - }, - Type: function.StaticReturnType(cty.List(cty.String)), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - _, network, err := net.ParseCIDR(args[0].AsString()) - if err != nil { - return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "invalid CIDR expression: %s", err) - } - startPrefixLen, _ := network.Mask.Size() - - prefixLengthArgs := args[1:] - if len(prefixLengthArgs) == 0 { - return cty.ListValEmpty(cty.String), nil - } - - var firstLength int - if err := gocty.FromCtyValue(prefixLengthArgs[0], &firstLength); err != nil { - return cty.UnknownVal(cty.String), function.NewArgError(1, err) - } - firstLength += startPrefixLen - - retVals := make([]cty.Value, len(prefixLengthArgs)) - - current, _ := cidr.PreviousSubnet(network, firstLength) - for i, lengthArg := range prefixLengthArgs { - var length int - if err := gocty.FromCtyValue(lengthArg, &length); err != nil { - return cty.UnknownVal(cty.String), function.NewArgError(i+1, err) - } - - if length < 1 { - return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "must extend prefix by at least one bit") - } - // For portability with 32-bit systems where the subnet number - // will be a 32-bit int, we only allow extension of 32 bits in - // one call even if we're running on a 64-bit machine. - // (Of course, this is significant only for IPv6.) - if length > 32 { - return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "may not extend prefix by more than 32 bits") - } - length += startPrefixLen - if length > (len(network.IP) * 8) { - protocol := "IP" - switch len(network.IP) * 8 { - case 32: - protocol = "IPv4" - case 128: - protocol = "IPv6" - } - return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "would extend prefix to %d bits, which is too long for an %s address", length, protocol) - } - - next, rollover := cidr.NextSubnet(current, length) - if rollover || !network.Contains(next.IP) { - // If we run out of suffix bits in the base CIDR prefix then - // NextSubnet will start incrementing the prefix bits, which - // we don't allow because it would then allocate addresses - // outside of the caller's given prefix. - return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "not enough remaining address space for a subnet with a prefix of %d bits after %s", length, current.String()) - } - - current = next - retVals[i] = cty.StringVal(current.String()) - } - - return cty.ListVal(retVals), nil - }, -}) - -// CidrHost calculates a full host IP address within a given IP network address prefix. -func CidrHost(prefix, hostnum cty.Value) (cty.Value, error) { - return CidrHostFunc.Call([]cty.Value{prefix, hostnum}) -} - -// CidrNetmask converts an IPv4 address prefix given in CIDR notation into a subnet mask address. -func CidrNetmask(prefix cty.Value) (cty.Value, error) { - return CidrNetmaskFunc.Call([]cty.Value{prefix}) -} - -// CidrSubnet calculates a subnet address within a given IP network address prefix. -func CidrSubnet(prefix, newbits, netnum cty.Value) (cty.Value, error) { - return CidrSubnetFunc.Call([]cty.Value{prefix, newbits, netnum}) -} - -// CidrSubnets calculates a sequence of consecutive subnet prefixes that may -// be of different prefix lengths under a common base prefix. -func CidrSubnets(prefix cty.Value, newbits ...cty.Value) (cty.Value, error) { - args := make([]cty.Value, len(newbits)+1) - args[0] = prefix - copy(args[1:], newbits) - return CidrSubnetsFunc.Call(args) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/collection.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/collection.go deleted file mode 100644 index e6898457..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/collection.go +++ /dev/null @@ -1,1519 +0,0 @@ -package funcs - -import ( - "errors" - "fmt" - "sort" - - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" - "github.com/zclconf/go-cty/cty/function" - "github.com/zclconf/go-cty/cty/function/stdlib" - "github.com/zclconf/go-cty/cty/gocty" -) - -var ElementFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "list", - Type: cty.DynamicPseudoType, - }, - { - Name: "index", - Type: cty.Number, - }, - }, - Type: func(args []cty.Value) (cty.Type, error) { - list := args[0] - listTy := list.Type() - switch { - case listTy.IsListType(): - return listTy.ElementType(), nil - case listTy.IsTupleType(): - if !args[1].IsKnown() { - // If the index isn't known yet then we can't predict the - // result type since each tuple element can have its own type. - return cty.DynamicPseudoType, nil - } - - etys := listTy.TupleElementTypes() - var index int - err := gocty.FromCtyValue(args[1], &index) - if err != nil { - // e.g. fractional number where whole number is required - return cty.DynamicPseudoType, fmt.Errorf("invalid index: %s", err) - } - if len(etys) == 0 { - return cty.DynamicPseudoType, errors.New("cannot use element function with an empty list") - } - index = index % len(etys) - return etys[index], nil - default: - return cty.DynamicPseudoType, fmt.Errorf("cannot read elements from %s", listTy.FriendlyName()) - } - }, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - var index int - err := gocty.FromCtyValue(args[1], &index) - if err != nil { - // can't happen because we checked this in the Type function above - return cty.DynamicVal, fmt.Errorf("invalid index: %s", err) - } - - if !args[0].IsKnown() { - return cty.UnknownVal(retType), nil - } - - l := args[0].LengthInt() - if l == 0 { - return cty.DynamicVal, errors.New("cannot use element function with an empty list") - } - index = index % l - - // We did all the necessary type checks in the type function above, - // so this is guaranteed not to fail. - return args[0].Index(cty.NumberIntVal(int64(index))), nil - }, -}) - -var LengthFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "value", - Type: cty.DynamicPseudoType, - AllowDynamicType: true, - AllowUnknown: true, - }, - }, - Type: func(args []cty.Value) (cty.Type, error) { - collTy := args[0].Type() - switch { - case collTy == cty.String || collTy.IsTupleType() || collTy.IsObjectType() || collTy.IsListType() || collTy.IsMapType() || collTy.IsSetType() || collTy == cty.DynamicPseudoType: - return cty.Number, nil - default: - return cty.Number, errors.New("argument must be a string, a collection type, or a structural type") - } - }, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - coll := args[0] - collTy := args[0].Type() - switch { - case collTy == cty.DynamicPseudoType: - return cty.UnknownVal(cty.Number), nil - case collTy.IsTupleType(): - l := len(collTy.TupleElementTypes()) - return cty.NumberIntVal(int64(l)), nil - case collTy.IsObjectType(): - l := len(collTy.AttributeTypes()) - return cty.NumberIntVal(int64(l)), nil - case collTy == cty.String: - // We'll delegate to the cty stdlib strlen function here, because - // it deals with all of the complexities of tokenizing unicode - // grapheme clusters. - return stdlib.Strlen(coll) - case collTy.IsListType() || collTy.IsSetType() || collTy.IsMapType(): - return coll.Length(), nil - default: - // Should never happen, because of the checks in our Type func above - return cty.UnknownVal(cty.Number), errors.New("impossible value type for length(...)") - } - }, -}) - -// CoalesceFunc constructs a function that takes any number of arguments and -// returns the first one that isn't empty. This function was copied from go-cty -// stdlib and modified so that it returns the first *non-empty* non-null element -// from a sequence, instead of merely the first non-null. -var CoalesceFunc = function.New(&function.Spec{ - Params: []function.Parameter{}, - VarParam: &function.Parameter{ - Name: "vals", - Type: cty.DynamicPseudoType, - AllowUnknown: true, - AllowDynamicType: true, - AllowNull: true, - }, - Type: func(args []cty.Value) (ret cty.Type, err error) { - argTypes := make([]cty.Type, len(args)) - for i, val := range args { - argTypes[i] = val.Type() - } - retType, _ := convert.UnifyUnsafe(argTypes) - if retType == cty.NilType { - return cty.NilType, errors.New("all arguments must have the same type") - } - return retType, nil - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - for _, argVal := range args { - // We already know this will succeed because of the checks in our Type func above - argVal, _ = convert.Convert(argVal, retType) - if !argVal.IsKnown() { - return cty.UnknownVal(retType), nil - } - if argVal.IsNull() { - continue - } - if retType == cty.String && argVal.RawEquals(cty.StringVal("")) { - continue - } - - return argVal, nil - } - return cty.NilVal, errors.New("no non-null, non-empty-string arguments") - }, -}) - -// CoalesceListFunc constructs a function that takes any number of list arguments -// and returns the first one that isn't empty. -var CoalesceListFunc = function.New(&function.Spec{ - Params: []function.Parameter{}, - VarParam: &function.Parameter{ - Name: "vals", - Type: cty.DynamicPseudoType, - AllowUnknown: true, - AllowDynamicType: true, - AllowNull: true, - }, - Type: func(args []cty.Value) (ret cty.Type, err error) { - if len(args) == 0 { - return cty.NilType, errors.New("at least one argument is required") - } - - argTypes := make([]cty.Type, len(args)) - - for i, arg := range args { - // if any argument is unknown, we can't be certain know which type we will return - if !arg.IsKnown() { - return cty.DynamicPseudoType, nil - } - ty := arg.Type() - - if !ty.IsListType() && !ty.IsTupleType() { - return cty.NilType, errors.New("coalescelist arguments must be lists or tuples") - } - - argTypes[i] = arg.Type() - } - - last := argTypes[0] - // If there are mixed types, we have to return a dynamic type. - for _, next := range argTypes[1:] { - if !next.Equals(last) { - return cty.DynamicPseudoType, nil - } - } - - return last, nil - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - for _, arg := range args { - if !arg.IsKnown() { - // If we run into an unknown list at some point, we can't - // predict the final result yet. (If there's a known, non-empty - // arg before this then we won't get here.) - return cty.UnknownVal(retType), nil - } - - if arg.LengthInt() > 0 { - return arg, nil - } - } - - return cty.NilVal, errors.New("no non-null arguments") - }, -}) - -// CompactFunc constructs a function that takes a list of strings and returns a new list -// with any empty string elements removed. -var CompactFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "list", - Type: cty.List(cty.String), - }, - }, - Type: function.StaticReturnType(cty.List(cty.String)), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - listVal := args[0] - if !listVal.IsWhollyKnown() { - // If some of the element values aren't known yet then we - // can't yet return a compacted list - return cty.UnknownVal(retType), nil - } - - var outputList []cty.Value - - for it := listVal.ElementIterator(); it.Next(); { - _, v := it.Element() - if v.IsNull() || v.AsString() == "" { - continue - } - outputList = append(outputList, v) - } - - if len(outputList) == 0 { - return cty.ListValEmpty(cty.String), nil - } - - return cty.ListVal(outputList), nil - }, -}) - -// ContainsFunc constructs a function that determines whether a given list or -// set contains a given single value as one of its elements. -var ContainsFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "list", - Type: cty.DynamicPseudoType, - }, - { - Name: "value", - Type: cty.DynamicPseudoType, - }, - }, - Type: function.StaticReturnType(cty.Bool), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - arg := args[0] - ty := arg.Type() - - if !ty.IsListType() && !ty.IsTupleType() && !ty.IsSetType() { - return cty.NilVal, errors.New("argument must be list, tuple, or set") - } - - _, err = Index(cty.TupleVal(arg.AsValueSlice()), args[1]) - if err != nil { - return cty.False, nil - } - - return cty.True, nil - }, -}) - -// IndexFunc constructs a function that finds the element index for a given value in a list. -var IndexFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "list", - Type: cty.DynamicPseudoType, - }, - { - Name: "value", - Type: cty.DynamicPseudoType, - }, - }, - Type: function.StaticReturnType(cty.Number), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - if !(args[0].Type().IsListType() || args[0].Type().IsTupleType()) { - return cty.NilVal, errors.New("argument must be a list or tuple") - } - - if !args[0].IsKnown() { - return cty.UnknownVal(cty.Number), nil - } - - if args[0].LengthInt() == 0 { // Easy path - return cty.NilVal, errors.New("cannot search an empty list") - } - - for it := args[0].ElementIterator(); it.Next(); { - i, v := it.Element() - eq, err := stdlib.Equal(v, args[1]) - if err != nil { - return cty.NilVal, err - } - if !eq.IsKnown() { - return cty.UnknownVal(cty.Number), nil - } - if eq.True() { - return i, nil - } - } - return cty.NilVal, errors.New("item not found") - - }, -}) - -// DistinctFunc constructs a function that takes a list and returns a new list -// with any duplicate elements removed. -var DistinctFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "list", - Type: cty.List(cty.DynamicPseudoType), - }, - }, - Type: func(args []cty.Value) (cty.Type, error) { - return args[0].Type(), nil - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - listVal := args[0] - - if !listVal.IsWhollyKnown() { - return cty.UnknownVal(retType), nil - } - var list []cty.Value - - for it := listVal.ElementIterator(); it.Next(); { - _, v := it.Element() - list, err = appendIfMissing(list, v) - if err != nil { - return cty.NilVal, err - } - } - - if len(list) == 0 { - return cty.ListValEmpty(retType.ElementType()), nil - } - return cty.ListVal(list), nil - }, -}) - -// ChunklistFunc constructs a function that splits a single list into fixed-size chunks, -// returning a list of lists. -var ChunklistFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "list", - Type: cty.List(cty.DynamicPseudoType), - }, - { - Name: "size", - Type: cty.Number, - }, - }, - Type: func(args []cty.Value) (cty.Type, error) { - return cty.List(args[0].Type()), nil - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - listVal := args[0] - if !listVal.IsKnown() { - return cty.UnknownVal(retType), nil - } - - if listVal.LengthInt() == 0 { - return cty.ListValEmpty(listVal.Type()), nil - } - - var size int - err = gocty.FromCtyValue(args[1], &size) - if err != nil { - return cty.NilVal, fmt.Errorf("invalid index: %s", err) - } - - if size < 0 { - return cty.NilVal, errors.New("the size argument must be positive") - } - - output := make([]cty.Value, 0) - - // if size is 0, returns a list made of the initial list - if size == 0 { - output = append(output, listVal) - return cty.ListVal(output), nil - } - - chunk := make([]cty.Value, 0) - - l := args[0].LengthInt() - i := 0 - - for it := listVal.ElementIterator(); it.Next(); { - _, v := it.Element() - chunk = append(chunk, v) - - // Chunk when index isn't 0, or when reaching the values's length - if (i+1)%size == 0 || (i+1) == l { - output = append(output, cty.ListVal(chunk)) - chunk = make([]cty.Value, 0) - } - i++ - } - - return cty.ListVal(output), nil - }, -}) - -// FlattenFunc constructs a function that takes a list and replaces any elements -// that are lists with a flattened sequence of the list contents. -var FlattenFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "list", - Type: cty.DynamicPseudoType, - }, - }, - Type: func(args []cty.Value) (cty.Type, error) { - if !args[0].IsWhollyKnown() { - return cty.DynamicPseudoType, nil - } - - argTy := args[0].Type() - if !argTy.IsListType() && !argTy.IsSetType() && !argTy.IsTupleType() { - return cty.NilType, errors.New("can only flatten lists, sets and tuples") - } - - retVal, known := flattener(args[0]) - if !known { - return cty.DynamicPseudoType, nil - } - - tys := make([]cty.Type, len(retVal)) - for i, ty := range retVal { - tys[i] = ty.Type() - } - return cty.Tuple(tys), nil - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - inputList := args[0] - if inputList.LengthInt() == 0 { - return cty.EmptyTupleVal, nil - } - - out, known := flattener(inputList) - if !known { - return cty.UnknownVal(retType), nil - } - - return cty.TupleVal(out), nil - }, -}) - -// Flatten until it's not a cty.List, and return whether the value is known. -// We can flatten lists with unknown values, as long as they are not -// lists themselves. -func flattener(flattenList cty.Value) ([]cty.Value, bool) { - out := make([]cty.Value, 0) - for it := flattenList.ElementIterator(); it.Next(); { - _, val := it.Element() - if val.Type().IsListType() || val.Type().IsSetType() || val.Type().IsTupleType() { - if !val.IsKnown() { - return out, false - } - - res, known := flattener(val) - if !known { - return res, known - } - out = append(out, res...) - } else { - out = append(out, val) - } - } - return out, true -} - -// KeysFunc constructs a function that takes a map and returns a sorted list of the map keys. -var KeysFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "inputMap", - Type: cty.DynamicPseudoType, - AllowUnknown: true, - }, - }, - Type: func(args []cty.Value) (cty.Type, error) { - ty := args[0].Type() - switch { - case ty.IsMapType(): - return cty.List(cty.String), nil - case ty.IsObjectType(): - atys := ty.AttributeTypes() - if len(atys) == 0 { - return cty.EmptyTuple, nil - } - // All of our result elements will be strings, and atys just - // decides how many there are. - etys := make([]cty.Type, len(atys)) - for i := range etys { - etys[i] = cty.String - } - return cty.Tuple(etys), nil - default: - return cty.DynamicPseudoType, function.NewArgErrorf(0, "must have map or object type") - } - }, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - m := args[0] - var keys []cty.Value - - switch { - case m.Type().IsObjectType(): - // In this case we allow unknown values so we must work only with - // the attribute _types_, not with the value itself. - var names []string - for name := range m.Type().AttributeTypes() { - names = append(names, name) - } - sort.Strings(names) // same ordering guaranteed by cty's ElementIterator - if len(names) == 0 { - return cty.EmptyTupleVal, nil - } - keys = make([]cty.Value, len(names)) - for i, name := range names { - keys[i] = cty.StringVal(name) - } - return cty.TupleVal(keys), nil - default: - if !m.IsKnown() { - return cty.UnknownVal(retType), nil - } - - // cty guarantees that ElementIterator will iterate in lexicographical - // order by key. - for it := args[0].ElementIterator(); it.Next(); { - k, _ := it.Element() - keys = append(keys, k) - } - if len(keys) == 0 { - return cty.ListValEmpty(cty.String), nil - } - return cty.ListVal(keys), nil - } - }, -}) - -// ListFunc constructs a function that takes an arbitrary number of arguments -// and returns a list containing those values in the same order. -// -// This function is deprecated in Terraform v0.12 -var ListFunc = function.New(&function.Spec{ - Params: []function.Parameter{}, - VarParam: &function.Parameter{ - Name: "vals", - Type: cty.DynamicPseudoType, - AllowUnknown: true, - AllowDynamicType: true, - AllowNull: true, - }, - Type: func(args []cty.Value) (ret cty.Type, err error) { - if len(args) == 0 { - return cty.NilType, errors.New("at least one argument is required") - } - - argTypes := make([]cty.Type, len(args)) - - for i, arg := range args { - argTypes[i] = arg.Type() - } - - retType, _ := convert.UnifyUnsafe(argTypes) - if retType == cty.NilType { - return cty.NilType, errors.New("all arguments must have the same type") - } - - return cty.List(retType), nil - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - newList := make([]cty.Value, 0, len(args)) - - for _, arg := range args { - // We already know this will succeed because of the checks in our Type func above - arg, _ = convert.Convert(arg, retType.ElementType()) - newList = append(newList, arg) - } - - return cty.ListVal(newList), nil - }, -}) - -// LookupFunc constructs a function that performs dynamic lookups of map types. -var LookupFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "inputMap", - Type: cty.DynamicPseudoType, - }, - { - Name: "key", - Type: cty.String, - }, - }, - VarParam: &function.Parameter{ - Name: "default", - Type: cty.DynamicPseudoType, - AllowUnknown: true, - AllowDynamicType: true, - AllowNull: true, - }, - Type: func(args []cty.Value) (ret cty.Type, err error) { - if len(args) < 1 || len(args) > 3 { - return cty.NilType, fmt.Errorf("lookup() takes two or three arguments, got %d", len(args)) - } - - ty := args[0].Type() - - switch { - case ty.IsObjectType(): - if !args[1].IsKnown() { - return cty.DynamicPseudoType, nil - } - - key := args[1].AsString() - if ty.HasAttribute(key) { - return args[0].GetAttr(key).Type(), nil - } else if len(args) == 3 { - // if the key isn't found but a default is provided, - // return the default type - return args[2].Type(), nil - } - return cty.DynamicPseudoType, function.NewArgErrorf(0, "the given object has no attribute %q", key) - case ty.IsMapType(): - if len(args) == 3 { - _, err = convert.Convert(args[2], ty.ElementType()) - if err != nil { - return cty.NilType, function.NewArgErrorf(2, "the default value must have the same type as the map elements") - } - } - return ty.ElementType(), nil - default: - return cty.NilType, function.NewArgErrorf(0, "lookup() requires a map as the first argument") - } - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - var defaultVal cty.Value - defaultValueSet := false - - if len(args) == 3 { - defaultVal = args[2] - defaultValueSet = true - } - - mapVar := args[0] - lookupKey := args[1].AsString() - - if !mapVar.IsWhollyKnown() { - return cty.UnknownVal(retType), nil - } - - if mapVar.Type().IsObjectType() { - if mapVar.Type().HasAttribute(lookupKey) { - return mapVar.GetAttr(lookupKey), nil - } - } else if mapVar.HasIndex(cty.StringVal(lookupKey)) == cty.True { - return mapVar.Index(cty.StringVal(lookupKey)), nil - } - - if defaultValueSet { - defaultVal, err = convert.Convert(defaultVal, retType) - if err != nil { - return cty.NilVal, err - } - return defaultVal, nil - } - - return cty.UnknownVal(cty.DynamicPseudoType), fmt.Errorf( - "lookup failed to find '%s'", lookupKey) - }, -}) - -// MapFunc constructs a function that takes an even number of arguments and -// returns a map whose elements are constructed from consecutive pairs of arguments. -// -// This function is deprecated in Terraform v0.12 -var MapFunc = function.New(&function.Spec{ - Params: []function.Parameter{}, - VarParam: &function.Parameter{ - Name: "vals", - Type: cty.DynamicPseudoType, - AllowUnknown: true, - AllowDynamicType: true, - AllowNull: true, - }, - Type: func(args []cty.Value) (ret cty.Type, err error) { - if len(args) < 2 || len(args)%2 != 0 { - return cty.NilType, fmt.Errorf("map requires an even number of two or more arguments, got %d", len(args)) - } - - argTypes := make([]cty.Type, len(args)/2) - index := 0 - - for i := 0; i < len(args); i += 2 { - argTypes[index] = args[i+1].Type() - index++ - } - - valType, _ := convert.UnifyUnsafe(argTypes) - if valType == cty.NilType { - return cty.NilType, errors.New("all arguments must have the same type") - } - - return cty.Map(valType), nil - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - for _, arg := range args { - if !arg.IsWhollyKnown() { - return cty.UnknownVal(retType), nil - } - } - - outputMap := make(map[string]cty.Value) - - for i := 0; i < len(args); i += 2 { - - key := args[i].AsString() - - err := gocty.FromCtyValue(args[i], &key) - if err != nil { - return cty.NilVal, err - } - - val := args[i+1] - - var variable cty.Value - err = gocty.FromCtyValue(val, &variable) - if err != nil { - return cty.NilVal, err - } - - // We already know this will succeed because of the checks in our Type func above - variable, _ = convert.Convert(variable, retType.ElementType()) - - // Check for duplicate keys - if _, ok := outputMap[key]; ok { - return cty.NilVal, fmt.Errorf("argument %d is a duplicate key: %q", i+1, key) - } - outputMap[key] = variable - } - - return cty.MapVal(outputMap), nil - }, -}) - -// MatchkeysFunc constructs a function that constructs a new list by taking a -// subset of elements from one list whose indexes match the corresponding -// indexes of values in another list. -var MatchkeysFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "values", - Type: cty.List(cty.DynamicPseudoType), - }, - { - Name: "keys", - Type: cty.List(cty.DynamicPseudoType), - }, - { - Name: "searchset", - Type: cty.List(cty.DynamicPseudoType), - }, - }, - Type: func(args []cty.Value) (cty.Type, error) { - ty, _ := convert.UnifyUnsafe([]cty.Type{args[1].Type(), args[2].Type()}) - if ty == cty.NilType { - return cty.NilType, errors.New("keys and searchset must be of the same type") - } - - // the return type is based on args[0] (values) - return args[0].Type(), nil - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - if !args[0].IsKnown() { - return cty.UnknownVal(cty.List(retType.ElementType())), nil - } - - if args[0].LengthInt() != args[1].LengthInt() { - return cty.ListValEmpty(retType.ElementType()), errors.New("length of keys and values should be equal") - } - - output := make([]cty.Value, 0) - values := args[0] - - // Keys and searchset must be the same type. - // We can skip error checking here because we've already verified that - // they can be unified in the Type function - ty, _ := convert.UnifyUnsafe([]cty.Type{args[1].Type(), args[2].Type()}) - keys, _ := convert.Convert(args[1], ty) - searchset, _ := convert.Convert(args[2], ty) - - // if searchset is empty, return an empty list. - if searchset.LengthInt() == 0 { - return cty.ListValEmpty(retType.ElementType()), nil - } - - if !values.IsWhollyKnown() || !keys.IsWhollyKnown() { - return cty.UnknownVal(retType), nil - } - - i := 0 - for it := keys.ElementIterator(); it.Next(); { - _, key := it.Element() - for iter := searchset.ElementIterator(); iter.Next(); { - _, search := iter.Element() - eq, err := stdlib.Equal(key, search) - if err != nil { - return cty.NilVal, err - } - if !eq.IsKnown() { - return cty.ListValEmpty(retType.ElementType()), nil - } - if eq.True() { - v := values.Index(cty.NumberIntVal(int64(i))) - output = append(output, v) - break - } - } - i++ - } - - // if we haven't matched any key, then output is an empty list. - if len(output) == 0 { - return cty.ListValEmpty(retType.ElementType()), nil - } - return cty.ListVal(output), nil - }, -}) - -// MergeFunc constructs a function that takes an arbitrary number of maps and -// returns a single map that contains a merged set of elements from all of the maps. -// -// If more than one given map defines the same key then the one that is later in -// the argument sequence takes precedence. -var MergeFunc = function.New(&function.Spec{ - Params: []function.Parameter{}, - VarParam: &function.Parameter{ - Name: "maps", - Type: cty.DynamicPseudoType, - AllowDynamicType: true, - }, - Type: function.StaticReturnType(cty.DynamicPseudoType), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - outputMap := make(map[string]cty.Value) - - for _, arg := range args { - if !arg.IsWhollyKnown() { - return cty.UnknownVal(retType), nil - } - if !arg.Type().IsObjectType() && !arg.Type().IsMapType() { - return cty.NilVal, fmt.Errorf("arguments must be maps or objects, got %#v", arg.Type().FriendlyName()) - } - for it := arg.ElementIterator(); it.Next(); { - k, v := it.Element() - outputMap[k.AsString()] = v - } - } - return cty.ObjectVal(outputMap), nil - }, -}) - -// ReverseFunc takes a sequence and produces a new sequence of the same length -// with all of the same elements as the given sequence but in reverse order. -var ReverseFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "list", - Type: cty.DynamicPseudoType, - }, - }, - Type: func(args []cty.Value) (cty.Type, error) { - argTy := args[0].Type() - switch { - case argTy.IsTupleType(): - argTys := argTy.TupleElementTypes() - retTys := make([]cty.Type, len(argTys)) - for i, ty := range argTys { - retTys[len(retTys)-i-1] = ty - } - return cty.Tuple(retTys), nil - case argTy.IsListType(), argTy.IsSetType(): // We accept sets here to mimic the usual behavior of auto-converting to list - return cty.List(argTy.ElementType()), nil - default: - return cty.NilType, function.NewArgErrorf(0, "can only reverse list or tuple values, not %s", argTy.FriendlyName()) - } - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - in := args[0].AsValueSlice() - outVals := make([]cty.Value, len(in)) - for i, v := range in { - outVals[len(outVals)-i-1] = v - } - switch { - case retType.IsTupleType(): - return cty.TupleVal(outVals), nil - default: - if len(outVals) == 0 { - return cty.ListValEmpty(retType.ElementType()), nil - } - return cty.ListVal(outVals), nil - } - }, -}) - -// SetProductFunc calculates the cartesian product of two or more sets or -// sequences. If the arguments are all lists then the result is a list of tuples, -// preserving the ordering of all of the input lists. Otherwise the result is a -// set of tuples. -var SetProductFunc = function.New(&function.Spec{ - Params: []function.Parameter{}, - VarParam: &function.Parameter{ - Name: "sets", - Type: cty.DynamicPseudoType, - }, - Type: func(args []cty.Value) (retType cty.Type, err error) { - if len(args) < 2 { - return cty.NilType, errors.New("at least two arguments are required") - } - - listCount := 0 - elemTys := make([]cty.Type, len(args)) - for i, arg := range args { - aty := arg.Type() - switch { - case aty.IsSetType(): - elemTys[i] = aty.ElementType() - case aty.IsListType(): - elemTys[i] = aty.ElementType() - listCount++ - case aty.IsTupleType(): - // We can accept a tuple type only if there's some common type - // that all of its elements can be converted to. - allEtys := aty.TupleElementTypes() - if len(allEtys) == 0 { - elemTys[i] = cty.DynamicPseudoType - listCount++ - break - } - ety, _ := convert.UnifyUnsafe(allEtys) - if ety == cty.NilType { - return cty.NilType, function.NewArgErrorf(i, "all elements must be of the same type") - } - elemTys[i] = ety - listCount++ - default: - return cty.NilType, function.NewArgErrorf(i, "a set or a list is required") - } - } - - if listCount == len(args) { - return cty.List(cty.Tuple(elemTys)), nil - } - return cty.Set(cty.Tuple(elemTys)), nil - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - ety := retType.ElementType() - - total := 1 - for _, arg := range args { - // Because of our type checking function, we are guaranteed that - // all of the arguments are known, non-null values of types that - // support LengthInt. - total *= arg.LengthInt() - } - - if total == 0 { - // If any of the arguments was an empty collection then our result - // is also an empty collection, which we'll short-circuit here. - if retType.IsListType() { - return cty.ListValEmpty(ety), nil - } - return cty.SetValEmpty(ety), nil - } - - subEtys := ety.TupleElementTypes() - product := make([][]cty.Value, total) - - b := make([]cty.Value, total*len(args)) - n := make([]int, len(args)) - s := 0 - argVals := make([][]cty.Value, len(args)) - for i, arg := range args { - argVals[i] = arg.AsValueSlice() - } - - for i := range product { - e := s + len(args) - pi := b[s:e] - product[i] = pi - s = e - - for j, n := range n { - val := argVals[j][n] - ty := subEtys[j] - if !val.Type().Equals(ty) { - var err error - val, err = convert.Convert(val, ty) - if err != nil { - // Should never happen since we checked this in our - // type-checking function. - return cty.NilVal, fmt.Errorf("failed to convert argVals[%d][%d] to %s; this is a bug in Terraform", j, n, ty.FriendlyName()) - } - } - pi[j] = val - } - - for j := len(n) - 1; j >= 0; j-- { - n[j]++ - if n[j] < len(argVals[j]) { - break - } - n[j] = 0 - } - } - - productVals := make([]cty.Value, total) - for i, vals := range product { - productVals[i] = cty.TupleVal(vals) - } - - if retType.IsListType() { - return cty.ListVal(productVals), nil - } - return cty.SetVal(productVals), nil - }, -}) - -// SliceFunc constructs a function that extracts some consecutive elements -// from within a list. -var SliceFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "list", - Type: cty.DynamicPseudoType, - }, - { - Name: "start_index", - Type: cty.Number, - }, - { - Name: "end_index", - Type: cty.Number, - }, - }, - Type: func(args []cty.Value) (cty.Type, error) { - arg := args[0] - argTy := arg.Type() - - if argTy.IsSetType() { - return cty.NilType, function.NewArgErrorf(0, "cannot slice a set, because its elements do not have indices; use the tolist function to force conversion to list if the ordering of the result is not important") - } - if !argTy.IsListType() && !argTy.IsTupleType() { - return cty.NilType, function.NewArgErrorf(0, "must be a list or tuple value") - } - - startIndex, endIndex, idxsKnown, err := sliceIndexes(args) - if err != nil { - return cty.NilType, err - } - - if argTy.IsListType() { - return argTy, nil - } - - if !idxsKnown { - // If we don't know our start/end indices then we can't predict - // the result type if we're planning to return a tuple. - return cty.DynamicPseudoType, nil - } - return cty.Tuple(argTy.TupleElementTypes()[startIndex:endIndex]), nil - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - inputList := args[0] - - if retType == cty.DynamicPseudoType { - return cty.DynamicVal, nil - } - - // we ignore idxsKnown return value here because the indices are always - // known here, or else the call would've short-circuited. - startIndex, endIndex, _, err := sliceIndexes(args) - if err != nil { - return cty.NilVal, err - } - - if endIndex-startIndex == 0 { - if retType.IsTupleType() { - return cty.EmptyTupleVal, nil - } - return cty.ListValEmpty(retType.ElementType()), nil - } - - outputList := inputList.AsValueSlice()[startIndex:endIndex] - - if retType.IsTupleType() { - return cty.TupleVal(outputList), nil - } - - return cty.ListVal(outputList), nil - }, -}) - -func sliceIndexes(args []cty.Value) (int, int, bool, error) { - var startIndex, endIndex, length int - var startKnown, endKnown, lengthKnown bool - - if args[0].Type().IsTupleType() || args[0].IsKnown() { // if it's a tuple then we always know the length by the type, but lists must be known - length = args[0].LengthInt() - lengthKnown = true - } - - if args[1].IsKnown() { - if err := gocty.FromCtyValue(args[1], &startIndex); err != nil { - return 0, 0, false, function.NewArgErrorf(1, "invalid start index: %s", err) - } - if startIndex < 0 { - return 0, 0, false, function.NewArgErrorf(1, "start index must not be less than zero") - } - if lengthKnown && startIndex > length { - return 0, 0, false, function.NewArgErrorf(1, "start index must not be greater than the length of the list") - } - startKnown = true - } - if args[2].IsKnown() { - if err := gocty.FromCtyValue(args[2], &endIndex); err != nil { - return 0, 0, false, function.NewArgErrorf(2, "invalid end index: %s", err) - } - if endIndex < 0 { - return 0, 0, false, function.NewArgErrorf(2, "end index must not be less than zero") - } - if lengthKnown && endIndex > length { - return 0, 0, false, function.NewArgErrorf(2, "end index must not be greater than the length of the list") - } - endKnown = true - } - if startKnown && endKnown { - if startIndex > endIndex { - return 0, 0, false, function.NewArgErrorf(1, "start index must not be greater than end index") - } - } - return startIndex, endIndex, startKnown && endKnown, nil -} - -// TransposeFunc contructs a function that takes a map of lists of strings and -// TransposeFunc constructs a function that takes a map of lists of strings and -// swaps the keys and values to produce a new map of lists of strings. -var TransposeFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "values", - Type: cty.Map(cty.List(cty.String)), - }, - }, - Type: function.StaticReturnType(cty.Map(cty.List(cty.String))), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - inputMap := args[0] - if !inputMap.IsWhollyKnown() { - return cty.UnknownVal(retType), nil - } - - outputMap := make(map[string]cty.Value) - tmpMap := make(map[string][]string) - - for it := inputMap.ElementIterator(); it.Next(); { - inKey, inVal := it.Element() - for iter := inVal.ElementIterator(); iter.Next(); { - _, val := iter.Element() - if !val.Type().Equals(cty.String) { - return cty.MapValEmpty(cty.List(cty.String)), errors.New("input must be a map of lists of strings") - } - - outKey := val.AsString() - if _, ok := tmpMap[outKey]; !ok { - tmpMap[outKey] = make([]string, 0) - } - outVal := tmpMap[outKey] - outVal = append(outVal, inKey.AsString()) - sort.Strings(outVal) - tmpMap[outKey] = outVal - } - } - - for outKey, outVal := range tmpMap { - values := make([]cty.Value, 0) - for _, v := range outVal { - values = append(values, cty.StringVal(v)) - } - outputMap[outKey] = cty.ListVal(values) - } - - return cty.MapVal(outputMap), nil - }, -}) - -// ValuesFunc constructs a function that returns a list of the map values, -// in the order of the sorted keys. -var ValuesFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "values", - Type: cty.DynamicPseudoType, - }, - }, - Type: func(args []cty.Value) (ret cty.Type, err error) { - ty := args[0].Type() - if ty.IsMapType() { - return cty.List(ty.ElementType()), nil - } else if ty.IsObjectType() { - // The result is a tuple type with all of the same types as our - // object type's attributes, sorted in lexicographical order by the - // keys. (This matches the sort order guaranteed by ElementIterator - // on a cty object value.) - atys := ty.AttributeTypes() - if len(atys) == 0 { - return cty.EmptyTuple, nil - } - attrNames := make([]string, 0, len(atys)) - for name := range atys { - attrNames = append(attrNames, name) - } - sort.Strings(attrNames) - - tys := make([]cty.Type, len(attrNames)) - for i, name := range attrNames { - tys[i] = atys[name] - } - return cty.Tuple(tys), nil - } - return cty.NilType, errors.New("values() requires a map as the first argument") - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - mapVar := args[0] - - // We can just iterate the map/object value here because cty guarantees - // that these types always iterate in key lexicographical order. - var values []cty.Value - for it := mapVar.ElementIterator(); it.Next(); { - _, val := it.Element() - values = append(values, val) - } - - if retType.IsTupleType() { - return cty.TupleVal(values), nil - } - if len(values) == 0 { - return cty.ListValEmpty(retType.ElementType()), nil - } - return cty.ListVal(values), nil - }, -}) - -// ZipmapFunc constructs a function that constructs a map from a list of keys -// and a corresponding list of values. -var ZipmapFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "keys", - Type: cty.List(cty.String), - }, - { - Name: "values", - Type: cty.DynamicPseudoType, - }, - }, - Type: func(args []cty.Value) (ret cty.Type, err error) { - keys := args[0] - values := args[1] - valuesTy := values.Type() - - switch { - case valuesTy.IsListType(): - return cty.Map(values.Type().ElementType()), nil - case valuesTy.IsTupleType(): - if !keys.IsWhollyKnown() { - // Since zipmap with a tuple produces an object, we need to know - // all of the key names before we can predict our result type. - return cty.DynamicPseudoType, nil - } - - keysRaw := keys.AsValueSlice() - valueTypesRaw := valuesTy.TupleElementTypes() - if len(keysRaw) != len(valueTypesRaw) { - return cty.NilType, fmt.Errorf("number of keys (%d) does not match number of values (%d)", len(keysRaw), len(valueTypesRaw)) - } - atys := make(map[string]cty.Type, len(valueTypesRaw)) - for i, keyVal := range keysRaw { - if keyVal.IsNull() { - return cty.NilType, fmt.Errorf("keys list has null value at index %d", i) - } - key := keyVal.AsString() - atys[key] = valueTypesRaw[i] - } - return cty.Object(atys), nil - - default: - return cty.NilType, errors.New("values argument must be a list or tuple value") - } - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - keys := args[0] - values := args[1] - - if !keys.IsWhollyKnown() { - // Unknown map keys and object attributes are not supported, so - // our entire result must be unknown in this case. - return cty.UnknownVal(retType), nil - } - - // both keys and values are guaranteed to be shallowly-known here, - // because our declared params above don't allow unknown or null values. - if keys.LengthInt() != values.LengthInt() { - return cty.NilVal, fmt.Errorf("number of keys (%d) does not match number of values (%d)", keys.LengthInt(), values.LengthInt()) - } - - output := make(map[string]cty.Value) - - i := 0 - for it := keys.ElementIterator(); it.Next(); { - _, v := it.Element() - val := values.Index(cty.NumberIntVal(int64(i))) - output[v.AsString()] = val - i++ - } - - switch { - case retType.IsMapType(): - if len(output) == 0 { - return cty.MapValEmpty(retType.ElementType()), nil - } - return cty.MapVal(output), nil - case retType.IsObjectType(): - return cty.ObjectVal(output), nil - default: - // Should never happen because the type-check function should've - // caught any other case. - return cty.NilVal, fmt.Errorf("internally selected incorrect result type %s (this is a bug)", retType.FriendlyName()) - } - }, -}) - -// helper function to add an element to a list, if it does not already exist -func appendIfMissing(slice []cty.Value, element cty.Value) ([]cty.Value, error) { - for _, ele := range slice { - eq, err := stdlib.Equal(ele, element) - if err != nil { - return slice, err - } - if eq.True() { - return slice, nil - } - } - return append(slice, element), nil -} - -// Element returns a single element from a given list at the given index. If -// index is greater than the length of the list then it is wrapped modulo -// the list length. -func Element(list, index cty.Value) (cty.Value, error) { - return ElementFunc.Call([]cty.Value{list, index}) -} - -// Length returns the number of elements in the given collection or number of -// Unicode characters in the given string. -func Length(collection cty.Value) (cty.Value, error) { - return LengthFunc.Call([]cty.Value{collection}) -} - -// Coalesce takes any number of arguments and returns the first one that isn't empty. -func Coalesce(args ...cty.Value) (cty.Value, error) { - return CoalesceFunc.Call(args) -} - -// CoalesceList takes any number of list arguments and returns the first one that isn't empty. -func CoalesceList(args ...cty.Value) (cty.Value, error) { - return CoalesceListFunc.Call(args) -} - -// Compact takes a list of strings and returns a new list -// with any empty string elements removed. -func Compact(list cty.Value) (cty.Value, error) { - return CompactFunc.Call([]cty.Value{list}) -} - -// Contains determines whether a given list contains a given single value -// as one of its elements. -func Contains(list, value cty.Value) (cty.Value, error) { - return ContainsFunc.Call([]cty.Value{list, value}) -} - -// Index finds the element index for a given value in a list. -func Index(list, value cty.Value) (cty.Value, error) { - return IndexFunc.Call([]cty.Value{list, value}) -} - -// Distinct takes a list and returns a new list with any duplicate elements removed. -func Distinct(list cty.Value) (cty.Value, error) { - return DistinctFunc.Call([]cty.Value{list}) -} - -// Chunklist splits a single list into fixed-size chunks, returning a list of lists. -func Chunklist(list, size cty.Value) (cty.Value, error) { - return ChunklistFunc.Call([]cty.Value{list, size}) -} - -// Flatten takes a list and replaces any elements that are lists with a flattened -// sequence of the list contents. -func Flatten(list cty.Value) (cty.Value, error) { - return FlattenFunc.Call([]cty.Value{list}) -} - -// Keys takes a map and returns a sorted list of the map keys. -func Keys(inputMap cty.Value) (cty.Value, error) { - return KeysFunc.Call([]cty.Value{inputMap}) -} - -// List takes any number of list arguments and returns a list containing those -// values in the same order. -func List(args ...cty.Value) (cty.Value, error) { - return ListFunc.Call(args) -} - -// Lookup performs a dynamic lookup into a map. -// There are two required arguments, map and key, plus an optional default, -// which is a value to return if no key is found in map. -func Lookup(args ...cty.Value) (cty.Value, error) { - return LookupFunc.Call(args) -} - -// Map takes an even number of arguments and returns a map whose elements are constructed -// from consecutive pairs of arguments. -func Map(args ...cty.Value) (cty.Value, error) { - return MapFunc.Call(args) -} - -// Matchkeys constructs a new list by taking a subset of elements from one list -// whose indexes match the corresponding indexes of values in another list. -func Matchkeys(values, keys, searchset cty.Value) (cty.Value, error) { - return MatchkeysFunc.Call([]cty.Value{values, keys, searchset}) -} - -// Merge takes an arbitrary number of maps and returns a single map that contains -// a merged set of elements from all of the maps. -// -// If more than one given map defines the same key then the one that is later in -// the argument sequence takes precedence. -func Merge(maps ...cty.Value) (cty.Value, error) { - return MergeFunc.Call(maps) -} - -// Reverse takes a sequence and produces a new sequence of the same length -// with all of the same elements as the given sequence but in reverse order. -func Reverse(list cty.Value) (cty.Value, error) { - return ReverseFunc.Call([]cty.Value{list}) -} - -// SetProduct computes the cartesian product of sets or sequences. -func SetProduct(sets ...cty.Value) (cty.Value, error) { - return SetProductFunc.Call(sets) -} - -// Slice extracts some consecutive elements from within a list. -func Slice(list, start, end cty.Value) (cty.Value, error) { - return SliceFunc.Call([]cty.Value{list, start, end}) -} - -// Transpose takes a map of lists of strings and swaps the keys and values to -// produce a new map of lists of strings. -func Transpose(values cty.Value) (cty.Value, error) { - return TransposeFunc.Call([]cty.Value{values}) -} - -// Values returns a list of the map values, in the order of the sorted keys. -// This function only works on flat maps. -func Values(values cty.Value) (cty.Value, error) { - return ValuesFunc.Call([]cty.Value{values}) -} - -// Zipmap constructs a map from a list of keys and a corresponding list of values. -func Zipmap(keys, values cty.Value) (cty.Value, error) { - return ZipmapFunc.Call([]cty.Value{keys, values}) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/conversion.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/conversion.go deleted file mode 100644 index 83f85979..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/conversion.go +++ /dev/null @@ -1,87 +0,0 @@ -package funcs - -import ( - "strconv" - - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" - "github.com/zclconf/go-cty/cty/function" -) - -// MakeToFunc constructs a "to..." function, like "tostring", which converts -// its argument to a specific type or type kind. -// -// The given type wantTy can be any type constraint that cty's "convert" package -// would accept. In particular, this means that you can pass -// cty.List(cty.DynamicPseudoType) to mean "list of any single type", which -// will then cause cty to attempt to unify all of the element types when given -// a tuple. -func MakeToFunc(wantTy cty.Type) function.Function { - return function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "v", - // We use DynamicPseudoType rather than wantTy here so that - // all values will pass through the function API verbatim and - // we can handle the conversion logic within the Type and - // Impl functions. This allows us to customize the error - // messages to be more appropriate for an explicit type - // conversion, whereas the cty function system produces - // messages aimed at _implicit_ type conversions. - Type: cty.DynamicPseudoType, - AllowNull: true, - }, - }, - Type: func(args []cty.Value) (cty.Type, error) { - gotTy := args[0].Type() - if gotTy.Equals(wantTy) { - return wantTy, nil - } - conv := convert.GetConversionUnsafe(args[0].Type(), wantTy) - if conv == nil { - // We'll use some specialized errors for some trickier cases, - // but most we can handle in a simple way. - switch { - case gotTy.IsTupleType() && wantTy.IsTupleType(): - return cty.NilType, function.NewArgErrorf(0, "incompatible tuple type for conversion: %s", convert.MismatchMessage(gotTy, wantTy)) - case gotTy.IsObjectType() && wantTy.IsObjectType(): - return cty.NilType, function.NewArgErrorf(0, "incompatible object type for conversion: %s", convert.MismatchMessage(gotTy, wantTy)) - default: - return cty.NilType, function.NewArgErrorf(0, "cannot convert %s to %s", gotTy.FriendlyName(), wantTy.FriendlyNameForConstraint()) - } - } - // If a conversion is available then everything is fine. - return wantTy, nil - }, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - // We didn't set "AllowUnknown" on our argument, so it is guaranteed - // to be known here but may still be null. - ret, err := convert.Convert(args[0], retType) - if err != nil { - // Because we used GetConversionUnsafe above, conversion can - // still potentially fail in here. For example, if the user - // asks to convert the string "a" to bool then we'll - // optimistically permit it during type checking but fail here - // once we note that the value isn't either "true" or "false". - gotTy := args[0].Type() - switch { - case gotTy == cty.String && wantTy == cty.Bool: - what := "string" - if !args[0].IsNull() { - what = strconv.Quote(args[0].AsString()) - } - return cty.NilVal, function.NewArgErrorf(0, `cannot convert %s to bool; only the strings "true" or "false" are allowed`, what) - case gotTy == cty.String && wantTy == cty.Number: - what := "string" - if !args[0].IsNull() { - what = strconv.Quote(args[0].AsString()) - } - return cty.NilVal, function.NewArgErrorf(0, `cannot convert %s to number; given string must be a decimal representation of a number`, what) - default: - return cty.NilVal, function.NewArgErrorf(0, "cannot convert %s to %s", gotTy.FriendlyName(), wantTy.FriendlyNameForConstraint()) - } - } - return ret, nil - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/crypto.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/crypto.go deleted file mode 100644 index 28074fb1..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/crypto.go +++ /dev/null @@ -1,325 +0,0 @@ -package funcs - -import ( - "crypto/md5" - "crypto/rsa" - "crypto/sha1" - "crypto/sha256" - "crypto/sha512" - "crypto/x509" - "encoding/base64" - "encoding/hex" - "encoding/pem" - "fmt" - "hash" - - uuidv5 "github.com/google/uuid" - uuid "github.com/hashicorp/go-uuid" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" - "github.com/zclconf/go-cty/cty/gocty" - "golang.org/x/crypto/bcrypt" -) - -var UUIDFunc = function.New(&function.Spec{ - Params: []function.Parameter{}, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - result, err := uuid.GenerateUUID() - if err != nil { - return cty.UnknownVal(cty.String), err - } - return cty.StringVal(result), nil - }, -}) - -var UUIDV5Func = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "namespace", - Type: cty.String, - }, - { - Name: "name", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - var namespace uuidv5.UUID - switch { - case args[0].AsString() == "dns": - namespace = uuidv5.NameSpaceDNS - case args[0].AsString() == "url": - namespace = uuidv5.NameSpaceURL - case args[0].AsString() == "oid": - namespace = uuidv5.NameSpaceOID - case args[0].AsString() == "x500": - namespace = uuidv5.NameSpaceX500 - default: - if namespace, err = uuidv5.Parse(args[0].AsString()); err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("uuidv5() doesn't support namespace %s (%v)", args[0].AsString(), err) - } - } - val := args[1].AsString() - return cty.StringVal(uuidv5.NewSHA1(namespace, []byte(val)).String()), nil - }, -}) - -// Base64Sha256Func constructs a function that computes the SHA256 hash of a given string -// and encodes it with Base64. -var Base64Sha256Func = makeStringHashFunction(sha256.New, base64.StdEncoding.EncodeToString) - -// MakeFileBase64Sha256Func constructs a function that is like Base64Sha256Func but reads the -// contents of a file rather than hashing a given literal string. -func MakeFileBase64Sha256Func(baseDir string) function.Function { - return makeFileHashFunction(baseDir, sha256.New, base64.StdEncoding.EncodeToString) -} - -// Base64Sha512Func constructs a function that computes the SHA256 hash of a given string -// and encodes it with Base64. -var Base64Sha512Func = makeStringHashFunction(sha512.New, base64.StdEncoding.EncodeToString) - -// MakeFileBase64Sha512Func constructs a function that is like Base64Sha512Func but reads the -// contents of a file rather than hashing a given literal string. -func MakeFileBase64Sha512Func(baseDir string) function.Function { - return makeFileHashFunction(baseDir, sha512.New, base64.StdEncoding.EncodeToString) -} - -// BcryptFunc constructs a function that computes a hash of the given string using the Blowfish cipher. -var BcryptFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - }, - VarParam: &function.Parameter{ - Name: "cost", - Type: cty.Number, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - defaultCost := 10 - - if len(args) > 1 { - var val int - if err := gocty.FromCtyValue(args[1], &val); err != nil { - return cty.UnknownVal(cty.String), err - } - defaultCost = val - } - - if len(args) > 2 { - return cty.UnknownVal(cty.String), fmt.Errorf("bcrypt() takes no more than two arguments") - } - - input := args[0].AsString() - out, err := bcrypt.GenerateFromPassword([]byte(input), defaultCost) - if err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("error occured generating password %s", err.Error()) - } - - return cty.StringVal(string(out)), nil - }, -}) - -// Md5Func constructs a function that computes the MD5 hash of a given string and encodes it with hexadecimal digits. -var Md5Func = makeStringHashFunction(md5.New, hex.EncodeToString) - -// MakeFileMd5Func constructs a function that is like Md5Func but reads the -// contents of a file rather than hashing a given literal string. -func MakeFileMd5Func(baseDir string) function.Function { - return makeFileHashFunction(baseDir, md5.New, hex.EncodeToString) -} - -// RsaDecryptFunc constructs a function that decrypts an RSA-encrypted ciphertext. -var RsaDecryptFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "ciphertext", - Type: cty.String, - }, - { - Name: "privatekey", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - s := args[0].AsString() - key := args[1].AsString() - - b, err := base64.StdEncoding.DecodeString(s) - if err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("failed to decode input %q: cipher text must be base64-encoded", s) - } - - block, _ := pem.Decode([]byte(key)) - if block == nil { - return cty.UnknownVal(cty.String), fmt.Errorf("failed to parse key: no key found") - } - if block.Headers["Proc-Type"] == "4,ENCRYPTED" { - return cty.UnknownVal(cty.String), fmt.Errorf( - "failed to parse key: password protected keys are not supported. Please decrypt the key prior to use", - ) - } - - x509Key, err := x509.ParsePKCS1PrivateKey(block.Bytes) - if err != nil { - return cty.UnknownVal(cty.String), err - } - - out, err := rsa.DecryptPKCS1v15(nil, x509Key, b) - if err != nil { - return cty.UnknownVal(cty.String), err - } - - return cty.StringVal(string(out)), nil - }, -}) - -// Sha1Func contructs a function that computes the SHA1 hash of a given string -// and encodes it with hexadecimal digits. -var Sha1Func = makeStringHashFunction(sha1.New, hex.EncodeToString) - -// MakeFileSha1Func constructs a function that is like Sha1Func but reads the -// contents of a file rather than hashing a given literal string. -func MakeFileSha1Func(baseDir string) function.Function { - return makeFileHashFunction(baseDir, sha1.New, hex.EncodeToString) -} - -// Sha256Func contructs a function that computes the SHA256 hash of a given string -// and encodes it with hexadecimal digits. -var Sha256Func = makeStringHashFunction(sha256.New, hex.EncodeToString) - -// MakeFileSha256Func constructs a function that is like Sha256Func but reads the -// contents of a file rather than hashing a given literal string. -func MakeFileSha256Func(baseDir string) function.Function { - return makeFileHashFunction(baseDir, sha256.New, hex.EncodeToString) -} - -// Sha512Func contructs a function that computes the SHA512 hash of a given string -// and encodes it with hexadecimal digits. -var Sha512Func = makeStringHashFunction(sha512.New, hex.EncodeToString) - -// MakeFileSha512Func constructs a function that is like Sha512Func but reads the -// contents of a file rather than hashing a given literal string. -func MakeFileSha512Func(baseDir string) function.Function { - return makeFileHashFunction(baseDir, sha512.New, hex.EncodeToString) -} - -func makeStringHashFunction(hf func() hash.Hash, enc func([]byte) string) function.Function { - return function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - s := args[0].AsString() - h := hf() - h.Write([]byte(s)) - rv := enc(h.Sum(nil)) - return cty.StringVal(rv), nil - }, - }) -} - -func makeFileHashFunction(baseDir string, hf func() hash.Hash, enc func([]byte) string) function.Function { - return function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "path", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - path := args[0].AsString() - src, err := readFileBytes(baseDir, path) - if err != nil { - return cty.UnknownVal(cty.String), err - } - - h := hf() - h.Write(src) - rv := enc(h.Sum(nil)) - return cty.StringVal(rv), nil - }, - }) -} - -// UUID generates and returns a Type-4 UUID in the standard hexadecimal string -// format. -// -// This is not a pure function: it will generate a different result for each -// call. It must therefore be registered as an impure function in the function -// table in the "lang" package. -func UUID() (cty.Value, error) { - return UUIDFunc.Call(nil) -} - -// UUIDV5 generates and returns a Type-5 UUID in the standard hexadecimal string -// format. -func UUIDV5(namespace cty.Value, name cty.Value) (cty.Value, error) { - return UUIDV5Func.Call([]cty.Value{namespace, name}) -} - -// Base64Sha256 computes the SHA256 hash of a given string and encodes it with -// Base64. -// -// The given string is first encoded as UTF-8 and then the SHA256 algorithm is applied -// as defined in RFC 4634. The raw hash is then encoded with Base64 before returning. -// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4. -func Base64Sha256(str cty.Value) (cty.Value, error) { - return Base64Sha256Func.Call([]cty.Value{str}) -} - -// Base64Sha512 computes the SHA512 hash of a given string and encodes it with -// Base64. -// -// The given string is first encoded as UTF-8 and then the SHA256 algorithm is applied -// as defined in RFC 4634. The raw hash is then encoded with Base64 before returning. -// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4 -func Base64Sha512(str cty.Value) (cty.Value, error) { - return Base64Sha512Func.Call([]cty.Value{str}) -} - -// Bcrypt computes a hash of the given string using the Blowfish cipher, -// returning a string in the Modular Crypt Format -// usually expected in the shadow password file on many Unix systems. -func Bcrypt(str cty.Value, cost ...cty.Value) (cty.Value, error) { - args := make([]cty.Value, len(cost)+1) - args[0] = str - copy(args[1:], cost) - return BcryptFunc.Call(args) -} - -// Md5 computes the MD5 hash of a given string and encodes it with hexadecimal digits. -func Md5(str cty.Value) (cty.Value, error) { - return Md5Func.Call([]cty.Value{str}) -} - -// RsaDecrypt decrypts an RSA-encrypted ciphertext, returning the corresponding -// cleartext. -func RsaDecrypt(ciphertext, privatekey cty.Value) (cty.Value, error) { - return RsaDecryptFunc.Call([]cty.Value{ciphertext, privatekey}) -} - -// Sha1 computes the SHA1 hash of a given string and encodes it with hexadecimal digits. -func Sha1(str cty.Value) (cty.Value, error) { - return Sha1Func.Call([]cty.Value{str}) -} - -// Sha256 computes the SHA256 hash of a given string and encodes it with hexadecimal digits. -func Sha256(str cty.Value) (cty.Value, error) { - return Sha256Func.Call([]cty.Value{str}) -} - -// Sha512 computes the SHA512 hash of a given string and encodes it with hexadecimal digits. -func Sha512(str cty.Value) (cty.Value, error) { - return Sha512Func.Call([]cty.Value{str}) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/datetime.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/datetime.go deleted file mode 100644 index 5dae1987..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/datetime.go +++ /dev/null @@ -1,70 +0,0 @@ -package funcs - -import ( - "time" - - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" -) - -// TimestampFunc constructs a function that returns a string representation of the current date and time. -var TimestampFunc = function.New(&function.Spec{ - Params: []function.Parameter{}, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - return cty.StringVal(time.Now().UTC().Format(time.RFC3339)), nil - }, -}) - -// TimeAddFunc constructs a function that adds a duration to a timestamp, returning a new timestamp. -var TimeAddFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "timestamp", - Type: cty.String, - }, - { - Name: "duration", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - ts, err := time.Parse(time.RFC3339, args[0].AsString()) - if err != nil { - return cty.UnknownVal(cty.String), err - } - duration, err := time.ParseDuration(args[1].AsString()) - if err != nil { - return cty.UnknownVal(cty.String), err - } - - return cty.StringVal(ts.Add(duration).Format(time.RFC3339)), nil - }, -}) - -// Timestamp returns a string representation of the current date and time. -// -// In the Terraform language, timestamps are conventionally represented as -// strings using RFC 3339 "Date and Time format" syntax, and so timestamp -// returns a string in this format. -func Timestamp() (cty.Value, error) { - return TimestampFunc.Call([]cty.Value{}) -} - -// TimeAdd adds a duration to a timestamp, returning a new timestamp. -// -// In the Terraform language, timestamps are conventionally represented as -// strings using RFC 3339 "Date and Time format" syntax. Timeadd requires -// the timestamp argument to be a string conforming to this syntax. -// -// `duration` is a string representation of a time difference, consisting of -// sequences of number and unit pairs, like `"1.5h"` or `1h30m`. The accepted -// units are `ns`, `us` (or `µs`), `"ms"`, `"s"`, `"m"`, and `"h"`. The first -// number may be negative to indicate a negative duration, like `"-2h5m"`. -// -// The result is a string, also in RFC 3339 format, representing the result -// of adding the given direction to the given timestamp. -func TimeAdd(timestamp cty.Value, duration cty.Value) (cty.Value, error) { - return TimeAddFunc.Call([]cty.Value{timestamp, duration}) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/encoding.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/encoding.go deleted file mode 100644 index af93f08d..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/encoding.go +++ /dev/null @@ -1,140 +0,0 @@ -package funcs - -import ( - "bytes" - "compress/gzip" - "encoding/base64" - "fmt" - "log" - "net/url" - "unicode/utf8" - - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" -) - -// Base64DecodeFunc constructs a function that decodes a string containing a base64 sequence. -var Base64DecodeFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - s := args[0].AsString() - sDec, err := base64.StdEncoding.DecodeString(s) - if err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("failed to decode base64 data '%s'", s) - } - if !utf8.Valid([]byte(sDec)) { - log.Printf("[DEBUG] the result of decoding the the provided string is not valid UTF-8: %s", sDec) - return cty.UnknownVal(cty.String), fmt.Errorf("the result of decoding the the provided string is not valid UTF-8") - } - return cty.StringVal(string(sDec)), nil - }, -}) - -// Base64EncodeFunc constructs a function that encodes a string to a base64 sequence. -var Base64EncodeFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - return cty.StringVal(base64.StdEncoding.EncodeToString([]byte(args[0].AsString()))), nil - }, -}) - -// Base64GzipFunc constructs a function that compresses a string with gzip and then encodes the result in -// Base64 encoding. -var Base64GzipFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - s := args[0].AsString() - - var b bytes.Buffer - gz := gzip.NewWriter(&b) - if _, err := gz.Write([]byte(s)); err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("failed to write gzip raw data: '%s'", s) - } - if err := gz.Flush(); err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("failed to flush gzip writer: '%s'", s) - } - if err := gz.Close(); err != nil { - return cty.UnknownVal(cty.String), fmt.Errorf("failed to close gzip writer: '%s'", s) - } - return cty.StringVal(base64.StdEncoding.EncodeToString(b.Bytes())), nil - }, -}) - -// URLEncodeFunc constructs a function that applies URL encoding to a given string. -var URLEncodeFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - return cty.StringVal(url.QueryEscape(args[0].AsString())), nil - }, -}) - -// Base64Decode decodes a string containing a base64 sequence. -// -// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4. -// -// Strings in the Terraform language are sequences of unicode characters rather -// than bytes, so this function will also interpret the resulting bytes as -// UTF-8. If the bytes after Base64 decoding are _not_ valid UTF-8, this function -// produces an error. -func Base64Decode(str cty.Value) (cty.Value, error) { - return Base64DecodeFunc.Call([]cty.Value{str}) -} - -// Base64Encode applies Base64 encoding to a string. -// -// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4. -// -// Strings in the Terraform language are sequences of unicode characters rather -// than bytes, so this function will first encode the characters from the string -// as UTF-8, and then apply Base64 encoding to the result. -func Base64Encode(str cty.Value) (cty.Value, error) { - return Base64EncodeFunc.Call([]cty.Value{str}) -} - -// Base64Gzip compresses a string with gzip and then encodes the result in -// Base64 encoding. -// -// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4. -// -// Strings in the Terraform language are sequences of unicode characters rather -// than bytes, so this function will first encode the characters from the string -// as UTF-8, then apply gzip compression, and then finally apply Base64 encoding. -func Base64Gzip(str cty.Value) (cty.Value, error) { - return Base64GzipFunc.Call([]cty.Value{str}) -} - -// URLEncode applies URL encoding to a given string. -// -// This function identifies characters in the given string that would have a -// special meaning when included as a query string argument in a URL and -// escapes them using RFC 3986 "percent encoding". -// -// If the given string contains non-ASCII characters, these are first encoded as -// UTF-8 and then percent encoding is applied separately to each UTF-8 byte. -func URLEncode(str cty.Value) (cty.Value, error) { - return URLEncodeFunc.Call([]cty.Value{str}) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/filesystem.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/filesystem.go deleted file mode 100644 index 786d3e74..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/filesystem.go +++ /dev/null @@ -1,360 +0,0 @@ -package funcs - -import ( - "encoding/base64" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "unicode/utf8" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" - homedir "github.com/mitchellh/go-homedir" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" -) - -// MakeFileFunc constructs a function that takes a file path and returns the -// contents of that file, either directly as a string (where valid UTF-8 is -// required) or as a string containing base64 bytes. -func MakeFileFunc(baseDir string, encBase64 bool) function.Function { - return function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "path", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - path := args[0].AsString() - src, err := readFileBytes(baseDir, path) - if err != nil { - return cty.UnknownVal(cty.String), err - } - - switch { - case encBase64: - enc := base64.StdEncoding.EncodeToString(src) - return cty.StringVal(enc), nil - default: - if !utf8.Valid(src) { - return cty.UnknownVal(cty.String), fmt.Errorf("contents of %s are not valid UTF-8; use the filebase64 function to obtain the Base64 encoded contents or the other file functions (e.g. filemd5, filesha256) to obtain file hashing results instead", path) - } - return cty.StringVal(string(src)), nil - } - }, - }) -} - -// MakeTemplateFileFunc constructs a function that takes a file path and -// an arbitrary object of named values and attempts to render the referenced -// file as a template using HCL template syntax. -// -// The template itself may recursively call other functions so a callback -// must be provided to get access to those functions. The template cannot, -// however, access any variables defined in the scope: it is restricted only to -// those variables provided in the second function argument, to ensure that all -// dependencies on other graph nodes can be seen before executing this function. -// -// As a special exception, a referenced template file may not recursively call -// the templatefile function, since that would risk the same file being -// included into itself indefinitely. -func MakeTemplateFileFunc(baseDir string, funcsCb func() map[string]function.Function) function.Function { - - params := []function.Parameter{ - { - Name: "path", - Type: cty.String, - }, - { - Name: "vars", - Type: cty.DynamicPseudoType, - }, - } - - loadTmpl := func(fn string) (hcl.Expression, error) { - // We re-use File here to ensure the same filename interpretation - // as it does, along with its other safety checks. - tmplVal, err := File(baseDir, cty.StringVal(fn)) - if err != nil { - return nil, err - } - - expr, diags := hclsyntax.ParseTemplate([]byte(tmplVal.AsString()), fn, hcl.Pos{Line: 1, Column: 1}) - if diags.HasErrors() { - return nil, diags - } - - return expr, nil - } - - renderTmpl := func(expr hcl.Expression, varsVal cty.Value) (cty.Value, error) { - if varsTy := varsVal.Type(); !(varsTy.IsMapType() || varsTy.IsObjectType()) { - return cty.DynamicVal, function.NewArgErrorf(1, "invalid vars value: must be a map") // or an object, but we don't strongly distinguish these most of the time - } - - ctx := &hcl.EvalContext{ - Variables: varsVal.AsValueMap(), - } - - // We'll pre-check references in the template here so we can give a - // more specialized error message than HCL would by default, so it's - // clearer that this problem is coming from a templatefile call. - for _, traversal := range expr.Variables() { - root := traversal.RootName() - if _, ok := ctx.Variables[root]; !ok { - return cty.DynamicVal, function.NewArgErrorf(1, "vars map does not contain key %q, referenced at %s", root, traversal[0].SourceRange()) - } - } - - givenFuncs := funcsCb() // this callback indirection is to avoid chicken/egg problems - funcs := make(map[string]function.Function, len(givenFuncs)) - for name, fn := range givenFuncs { - if name == "templatefile" { - // We stub this one out to prevent recursive calls. - funcs[name] = function.New(&function.Spec{ - Params: params, - Type: func(args []cty.Value) (cty.Type, error) { - return cty.NilType, fmt.Errorf("cannot recursively call templatefile from inside templatefile call") - }, - }) - continue - } - funcs[name] = fn - } - ctx.Functions = funcs - - val, diags := expr.Value(ctx) - if diags.HasErrors() { - return cty.DynamicVal, diags - } - return val, nil - } - - return function.New(&function.Spec{ - Params: params, - Type: func(args []cty.Value) (cty.Type, error) { - if !(args[0].IsKnown() && args[1].IsKnown()) { - return cty.DynamicPseudoType, nil - } - - // We'll render our template now to see what result type it produces. - // A template consisting only of a single interpolation an potentially - // return any type. - expr, err := loadTmpl(args[0].AsString()) - if err != nil { - return cty.DynamicPseudoType, err - } - - // This is safe even if args[1] contains unknowns because the HCL - // template renderer itself knows how to short-circuit those. - val, err := renderTmpl(expr, args[1]) - return val.Type(), err - }, - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - expr, err := loadTmpl(args[0].AsString()) - if err != nil { - return cty.DynamicVal, err - } - return renderTmpl(expr, args[1]) - }, - }) - -} - -// MakeFileExistsFunc constructs a function that takes a path -// and determines whether a file exists at that path -func MakeFileExistsFunc(baseDir string) function.Function { - return function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "path", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.Bool), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - path := args[0].AsString() - path, err := homedir.Expand(path) - if err != nil { - return cty.UnknownVal(cty.Bool), fmt.Errorf("failed to expand ~: %s", err) - } - - if !filepath.IsAbs(path) { - path = filepath.Join(baseDir, path) - } - - // Ensure that the path is canonical for the host OS - path = filepath.Clean(path) - - fi, err := os.Stat(path) - if err != nil { - if os.IsNotExist(err) { - return cty.False, nil - } - return cty.UnknownVal(cty.Bool), fmt.Errorf("failed to stat %s", path) - } - - if fi.Mode().IsRegular() { - return cty.True, nil - } - - return cty.False, fmt.Errorf("%s is not a regular file, but %q", - path, fi.Mode().String()) - }, - }) -} - -// BasenameFunc constructs a function that takes a string containing a filesystem path -// and removes all except the last portion from it. -var BasenameFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "path", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - return cty.StringVal(filepath.Base(args[0].AsString())), nil - }, -}) - -// DirnameFunc constructs a function that takes a string containing a filesystem path -// and removes the last portion from it. -var DirnameFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "path", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - return cty.StringVal(filepath.Dir(args[0].AsString())), nil - }, -}) - -// AbsPathFunc constructs a function that converts a filesystem path to an absolute path -var AbsPathFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "path", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - absPath, err := filepath.Abs(args[0].AsString()) - return cty.StringVal(filepath.ToSlash(absPath)), err - }, -}) - -// PathExpandFunc constructs a function that expands a leading ~ character to the current user's home directory. -var PathExpandFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "path", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - - homePath, err := homedir.Expand(args[0].AsString()) - return cty.StringVal(homePath), err - }, -}) - -func readFileBytes(baseDir, path string) ([]byte, error) { - path, err := homedir.Expand(path) - if err != nil { - return nil, fmt.Errorf("failed to expand ~: %s", err) - } - - if !filepath.IsAbs(path) { - path = filepath.Join(baseDir, path) - } - - // Ensure that the path is canonical for the host OS - path = filepath.Clean(path) - - src, err := ioutil.ReadFile(path) - if err != nil { - // ReadFile does not return Terraform-user-friendly error - // messages, so we'll provide our own. - if os.IsNotExist(err) { - return nil, fmt.Errorf("no file exists at %s", path) - } - return nil, fmt.Errorf("failed to read %s", path) - } - - return src, nil -} - -// File reads the contents of the file at the given path. -// -// The file must contain valid UTF-8 bytes, or this function will return an error. -// -// The underlying function implementation works relative to a particular base -// directory, so this wrapper takes a base directory string and uses it to -// construct the underlying function before calling it. -func File(baseDir string, path cty.Value) (cty.Value, error) { - fn := MakeFileFunc(baseDir, false) - return fn.Call([]cty.Value{path}) -} - -// FileExists determines whether a file exists at the given path. -// -// The underlying function implementation works relative to a particular base -// directory, so this wrapper takes a base directory string and uses it to -// construct the underlying function before calling it. -func FileExists(baseDir string, path cty.Value) (cty.Value, error) { - fn := MakeFileExistsFunc(baseDir) - return fn.Call([]cty.Value{path}) -} - -// FileBase64 reads the contents of the file at the given path. -// -// The bytes from the file are encoded as base64 before returning. -// -// The underlying function implementation works relative to a particular base -// directory, so this wrapper takes a base directory string and uses it to -// construct the underlying function before calling it. -func FileBase64(baseDir string, path cty.Value) (cty.Value, error) { - fn := MakeFileFunc(baseDir, true) - return fn.Call([]cty.Value{path}) -} - -// Basename takes a string containing a filesystem path and removes all except the last portion from it. -// -// The underlying function implementation works only with the path string and does not access the filesystem itself. -// It is therefore unable to take into account filesystem features such as symlinks. -// -// If the path is empty then the result is ".", representing the current working directory. -func Basename(path cty.Value) (cty.Value, error) { - return BasenameFunc.Call([]cty.Value{path}) -} - -// Dirname takes a string containing a filesystem path and removes the last portion from it. -// -// The underlying function implementation works only with the path string and does not access the filesystem itself. -// It is therefore unable to take into account filesystem features such as symlinks. -// -// If the path is empty then the result is ".", representing the current working directory. -func Dirname(path cty.Value) (cty.Value, error) { - return DirnameFunc.Call([]cty.Value{path}) -} - -// Pathexpand takes a string that might begin with a `~` segment, and if so it replaces that segment with -// the current user's home directory path. -// -// The underlying function implementation works only with the path string and does not access the filesystem itself. -// It is therefore unable to take into account filesystem features such as symlinks. -// -// If the leading segment in the path is not `~` then the given path is returned unmodified. -func Pathexpand(path cty.Value) (cty.Value, error) { - return PathExpandFunc.Call([]cty.Value{path}) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/number.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/number.go deleted file mode 100644 index c813f47b..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/number.go +++ /dev/null @@ -1,217 +0,0 @@ -package funcs - -import ( - "math" - "math/big" - - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" - "github.com/zclconf/go-cty/cty/gocty" -) - -// CeilFunc contructs a function that returns the closest whole number greater -// than or equal to the given value. -var CeilFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "num", - Type: cty.Number, - }, - }, - Type: function.StaticReturnType(cty.Number), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - var val float64 - if err := gocty.FromCtyValue(args[0], &val); err != nil { - return cty.UnknownVal(cty.String), err - } - return cty.NumberIntVal(int64(math.Ceil(val))), nil - }, -}) - -// FloorFunc contructs a function that returns the closest whole number lesser -// than or equal to the given value. -var FloorFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "num", - Type: cty.Number, - }, - }, - Type: function.StaticReturnType(cty.Number), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - var val float64 - if err := gocty.FromCtyValue(args[0], &val); err != nil { - return cty.UnknownVal(cty.String), err - } - return cty.NumberIntVal(int64(math.Floor(val))), nil - }, -}) - -// LogFunc contructs a function that returns the logarithm of a given number in a given base. -var LogFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "num", - Type: cty.Number, - }, - { - Name: "base", - Type: cty.Number, - }, - }, - Type: function.StaticReturnType(cty.Number), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - var num float64 - if err := gocty.FromCtyValue(args[0], &num); err != nil { - return cty.UnknownVal(cty.String), err - } - - var base float64 - if err := gocty.FromCtyValue(args[1], &base); err != nil { - return cty.UnknownVal(cty.String), err - } - - return cty.NumberFloatVal(math.Log(num) / math.Log(base)), nil - }, -}) - -// PowFunc contructs a function that returns the logarithm of a given number in a given base. -var PowFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "num", - Type: cty.Number, - }, - { - Name: "power", - Type: cty.Number, - }, - }, - Type: function.StaticReturnType(cty.Number), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - var num float64 - if err := gocty.FromCtyValue(args[0], &num); err != nil { - return cty.UnknownVal(cty.String), err - } - - var power float64 - if err := gocty.FromCtyValue(args[1], &power); err != nil { - return cty.UnknownVal(cty.String), err - } - - return cty.NumberFloatVal(math.Pow(num, power)), nil - }, -}) - -// SignumFunc contructs a function that returns the closest whole number greater -// than or equal to the given value. -var SignumFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "num", - Type: cty.Number, - }, - }, - Type: function.StaticReturnType(cty.Number), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - var num int - if err := gocty.FromCtyValue(args[0], &num); err != nil { - return cty.UnknownVal(cty.String), err - } - switch { - case num < 0: - return cty.NumberIntVal(-1), nil - case num > 0: - return cty.NumberIntVal(+1), nil - default: - return cty.NumberIntVal(0), nil - } - }, -}) - -// ParseIntFunc contructs a function that parses a string argument and returns an integer of the specified base. -var ParseIntFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "number", - Type: cty.DynamicPseudoType, - }, - { - Name: "base", - Type: cty.Number, - }, - }, - - Type: func(args []cty.Value) (cty.Type, error) { - if !args[0].Type().Equals(cty.String) { - return cty.Number, function.NewArgErrorf(0, "first argument must be a string, not %s", args[0].Type().FriendlyName()) - } - return cty.Number, nil - }, - - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - var numstr string - var base int - var err error - - if err = gocty.FromCtyValue(args[0], &numstr); err != nil { - return cty.UnknownVal(cty.String), function.NewArgError(0, err) - } - - if err = gocty.FromCtyValue(args[1], &base); err != nil { - return cty.UnknownVal(cty.Number), function.NewArgError(1, err) - } - - if base < 2 || base > 62 { - return cty.UnknownVal(cty.Number), function.NewArgErrorf( - 1, - "base must be a whole number between 2 and 62 inclusive", - ) - } - - num, ok := (&big.Int{}).SetString(numstr, base) - if !ok { - return cty.UnknownVal(cty.Number), function.NewArgErrorf( - 0, - "cannot parse %q as a base %d integer", - numstr, - base, - ) - } - - parsedNum := cty.NumberVal((&big.Float{}).SetInt(num)) - - return parsedNum, nil - }, -}) - -// Ceil returns the closest whole number greater than or equal to the given value. -func Ceil(num cty.Value) (cty.Value, error) { - return CeilFunc.Call([]cty.Value{num}) -} - -// Floor returns the closest whole number lesser than or equal to the given value. -func Floor(num cty.Value) (cty.Value, error) { - return FloorFunc.Call([]cty.Value{num}) -} - -// Log returns returns the logarithm of a given number in a given base. -func Log(num, base cty.Value) (cty.Value, error) { - return LogFunc.Call([]cty.Value{num, base}) -} - -// Pow returns the logarithm of a given number in a given base. -func Pow(num, power cty.Value) (cty.Value, error) { - return PowFunc.Call([]cty.Value{num, power}) -} - -// Signum determines the sign of a number, returning a number between -1 and -// 1 to represent the sign. -func Signum(num cty.Value) (cty.Value, error) { - return SignumFunc.Call([]cty.Value{num}) -} - -// ParseInt parses a string argument and returns an integer of the specified base. -func ParseInt(num cty.Value, base cty.Value) (cty.Value, error) { - return ParseIntFunc.Call([]cty.Value{num, base}) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/string.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/string.go deleted file mode 100644 index c9ddf19e..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs/string.go +++ /dev/null @@ -1,280 +0,0 @@ -package funcs - -import ( - "fmt" - "regexp" - "sort" - "strings" - - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" - "github.com/zclconf/go-cty/cty/gocty" -) - -var JoinFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "separator", - Type: cty.String, - }, - }, - VarParam: &function.Parameter{ - Name: "lists", - Type: cty.List(cty.String), - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - sep := args[0].AsString() - listVals := args[1:] - if len(listVals) < 1 { - return cty.UnknownVal(cty.String), fmt.Errorf("at least one list is required") - } - - l := 0 - for _, list := range listVals { - if !list.IsWhollyKnown() { - return cty.UnknownVal(cty.String), nil - } - l += list.LengthInt() - } - - items := make([]string, 0, l) - for ai, list := range listVals { - ei := 0 - for it := list.ElementIterator(); it.Next(); { - _, val := it.Element() - if val.IsNull() { - if len(listVals) > 1 { - return cty.UnknownVal(cty.String), function.NewArgErrorf(ai+1, "element %d of list %d is null; cannot concatenate null values", ei, ai+1) - } - return cty.UnknownVal(cty.String), function.NewArgErrorf(ai+1, "element %d is null; cannot concatenate null values", ei) - } - items = append(items, val.AsString()) - ei++ - } - } - - return cty.StringVal(strings.Join(items, sep)), nil - }, -}) - -var SortFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "list", - Type: cty.List(cty.String), - }, - }, - Type: function.StaticReturnType(cty.List(cty.String)), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - listVal := args[0] - - if !listVal.IsWhollyKnown() { - // If some of the element values aren't known yet then we - // can't yet preduct the order of the result. - return cty.UnknownVal(retType), nil - } - if listVal.LengthInt() == 0 { // Easy path - return listVal, nil - } - - list := make([]string, 0, listVal.LengthInt()) - for it := listVal.ElementIterator(); it.Next(); { - iv, v := it.Element() - if v.IsNull() { - return cty.UnknownVal(retType), fmt.Errorf("given list element %s is null; a null string cannot be sorted", iv.AsBigFloat().String()) - } - list = append(list, v.AsString()) - } - - sort.Strings(list) - retVals := make([]cty.Value, len(list)) - for i, s := range list { - retVals[i] = cty.StringVal(s) - } - return cty.ListVal(retVals), nil - }, -}) - -var SplitFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "separator", - Type: cty.String, - }, - { - Name: "str", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.List(cty.String)), - Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { - sep := args[0].AsString() - str := args[1].AsString() - elems := strings.Split(str, sep) - elemVals := make([]cty.Value, len(elems)) - for i, s := range elems { - elemVals[i] = cty.StringVal(s) - } - if len(elemVals) == 0 { - return cty.ListValEmpty(cty.String), nil - } - return cty.ListVal(elemVals), nil - }, -}) - -// ChompFunc constructions a function that removes newline characters at the end of a string. -var ChompFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - newlines := regexp.MustCompile(`(?:\r\n?|\n)*\z`) - return cty.StringVal(newlines.ReplaceAllString(args[0].AsString(), "")), nil - }, -}) - -// IndentFunc constructions a function that adds a given number of spaces to the -// beginnings of all but the first line in a given multi-line string. -var IndentFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "spaces", - Type: cty.Number, - }, - { - Name: "str", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - var spaces int - if err := gocty.FromCtyValue(args[0], &spaces); err != nil { - return cty.UnknownVal(cty.String), err - } - data := args[1].AsString() - pad := strings.Repeat(" ", spaces) - return cty.StringVal(strings.Replace(data, "\n", "\n"+pad, -1)), nil - }, -}) - -// ReplaceFunc constructions a function that searches a given string for another -// given substring, and replaces each occurence with a given replacement string. -var ReplaceFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - { - Name: "substr", - Type: cty.String, - }, - { - Name: "replace", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - str := args[0].AsString() - substr := args[1].AsString() - replace := args[2].AsString() - - // We search/replace using a regexp if the string is surrounded - // in forward slashes. - if len(substr) > 1 && substr[0] == '/' && substr[len(substr)-1] == '/' { - re, err := regexp.Compile(substr[1 : len(substr)-1]) - if err != nil { - return cty.UnknownVal(cty.String), err - } - - return cty.StringVal(re.ReplaceAllString(str, replace)), nil - } - - return cty.StringVal(strings.Replace(str, substr, replace, -1)), nil - }, -}) - -// TitleFunc constructions a function that converts the first letter of each word -// in the given string to uppercase. -var TitleFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - return cty.StringVal(strings.Title(args[0].AsString())), nil - }, -}) - -// TrimSpaceFunc constructions a function that removes any space characters from -// the start and end of the given string. -var TrimSpaceFunc = function.New(&function.Spec{ - Params: []function.Parameter{ - { - Name: "str", - Type: cty.String, - }, - }, - Type: function.StaticReturnType(cty.String), - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - return cty.StringVal(strings.TrimSpace(args[0].AsString())), nil - }, -}) - -// Join concatenates together the string elements of one or more lists with a -// given separator. -func Join(sep cty.Value, lists ...cty.Value) (cty.Value, error) { - args := make([]cty.Value, len(lists)+1) - args[0] = sep - copy(args[1:], lists) - return JoinFunc.Call(args) -} - -// Sort re-orders the elements of a given list of strings so that they are -// in ascending lexicographical order. -func Sort(list cty.Value) (cty.Value, error) { - return SortFunc.Call([]cty.Value{list}) -} - -// Split divides a given string by a given separator, returning a list of -// strings containing the characters between the separator sequences. -func Split(sep, str cty.Value) (cty.Value, error) { - return SplitFunc.Call([]cty.Value{sep, str}) -} - -// Chomp removes newline characters at the end of a string. -func Chomp(str cty.Value) (cty.Value, error) { - return ChompFunc.Call([]cty.Value{str}) -} - -// Indent adds a given number of spaces to the beginnings of all but the first -// line in a given multi-line string. -func Indent(spaces, str cty.Value) (cty.Value, error) { - return IndentFunc.Call([]cty.Value{spaces, str}) -} - -// Replace searches a given string for another given substring, -// and replaces all occurences with a given replacement string. -func Replace(str, substr, replace cty.Value) (cty.Value, error) { - return ReplaceFunc.Call([]cty.Value{str, substr, replace}) -} - -// Title converts the first letter of each word in the given string to uppercase. -func Title(str cty.Value) (cty.Value, error) { - return TitleFunc.Call([]cty.Value{str}) -} - -// TrimSpace removes any space characters from the start and end of the given string. -func TrimSpace(str cty.Value) (cty.Value, error) { - return TrimSpaceFunc.Call([]cty.Value{str}) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/functions.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/functions.go deleted file mode 100644 index a3c49066..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/functions.go +++ /dev/null @@ -1,146 +0,0 @@ -package lang - -import ( - ctyyaml "github.com/zclconf/go-cty-yaml" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" - "github.com/zclconf/go-cty/cty/function/stdlib" - - "github.com/hashicorp/terraform-plugin-sdk/internal/lang/funcs" -) - -var impureFunctions = []string{ - "bcrypt", - "timestamp", - "uuid", -} - -// Functions returns the set of functions that should be used to when evaluating -// expressions in the receiving scope. -func (s *Scope) Functions() map[string]function.Function { - s.funcsLock.Lock() - if s.funcs == nil { - // Some of our functions are just directly the cty stdlib functions. - // Others are implemented in the subdirectory "funcs" here in this - // repository. New functions should generally start out their lives - // in the "funcs" directory and potentially graduate to cty stdlib - // later if the functionality seems to be something domain-agnostic - // that would be useful to all applications using cty functions. - - s.funcs = map[string]function.Function{ - "abs": stdlib.AbsoluteFunc, - "abspath": funcs.AbsPathFunc, - "basename": funcs.BasenameFunc, - "base64decode": funcs.Base64DecodeFunc, - "base64encode": funcs.Base64EncodeFunc, - "base64gzip": funcs.Base64GzipFunc, - "base64sha256": funcs.Base64Sha256Func, - "base64sha512": funcs.Base64Sha512Func, - "bcrypt": funcs.BcryptFunc, - "ceil": funcs.CeilFunc, - "chomp": funcs.ChompFunc, - "cidrhost": funcs.CidrHostFunc, - "cidrnetmask": funcs.CidrNetmaskFunc, - "cidrsubnet": funcs.CidrSubnetFunc, - "cidrsubnets": funcs.CidrSubnetsFunc, - "coalesce": funcs.CoalesceFunc, - "coalescelist": funcs.CoalesceListFunc, - "compact": funcs.CompactFunc, - "concat": stdlib.ConcatFunc, - "contains": funcs.ContainsFunc, - "csvdecode": stdlib.CSVDecodeFunc, - "dirname": funcs.DirnameFunc, - "distinct": funcs.DistinctFunc, - "element": funcs.ElementFunc, - "chunklist": funcs.ChunklistFunc, - "file": funcs.MakeFileFunc(s.BaseDir, false), - "fileexists": funcs.MakeFileExistsFunc(s.BaseDir), - "filebase64": funcs.MakeFileFunc(s.BaseDir, true), - "filebase64sha256": funcs.MakeFileBase64Sha256Func(s.BaseDir), - "filebase64sha512": funcs.MakeFileBase64Sha512Func(s.BaseDir), - "filemd5": funcs.MakeFileMd5Func(s.BaseDir), - "filesha1": funcs.MakeFileSha1Func(s.BaseDir), - "filesha256": funcs.MakeFileSha256Func(s.BaseDir), - "filesha512": funcs.MakeFileSha512Func(s.BaseDir), - "flatten": funcs.FlattenFunc, - "floor": funcs.FloorFunc, - "format": stdlib.FormatFunc, - "formatdate": stdlib.FormatDateFunc, - "formatlist": stdlib.FormatListFunc, - "indent": funcs.IndentFunc, - "index": funcs.IndexFunc, - "join": funcs.JoinFunc, - "jsondecode": stdlib.JSONDecodeFunc, - "jsonencode": stdlib.JSONEncodeFunc, - "keys": funcs.KeysFunc, - "length": funcs.LengthFunc, - "list": funcs.ListFunc, - "log": funcs.LogFunc, - "lookup": funcs.LookupFunc, - "lower": stdlib.LowerFunc, - "map": funcs.MapFunc, - "matchkeys": funcs.MatchkeysFunc, - "max": stdlib.MaxFunc, - "md5": funcs.Md5Func, - "merge": funcs.MergeFunc, - "min": stdlib.MinFunc, - "parseint": funcs.ParseIntFunc, - "pathexpand": funcs.PathExpandFunc, - "pow": funcs.PowFunc, - "range": stdlib.RangeFunc, - "regex": stdlib.RegexFunc, - "regexall": stdlib.RegexAllFunc, - "replace": funcs.ReplaceFunc, - "reverse": funcs.ReverseFunc, - "rsadecrypt": funcs.RsaDecryptFunc, - "setintersection": stdlib.SetIntersectionFunc, - "setproduct": funcs.SetProductFunc, - "setunion": stdlib.SetUnionFunc, - "sha1": funcs.Sha1Func, - "sha256": funcs.Sha256Func, - "sha512": funcs.Sha512Func, - "signum": funcs.SignumFunc, - "slice": funcs.SliceFunc, - "sort": funcs.SortFunc, - "split": funcs.SplitFunc, - "strrev": stdlib.ReverseFunc, - "substr": stdlib.SubstrFunc, - "timestamp": funcs.TimestampFunc, - "timeadd": funcs.TimeAddFunc, - "title": funcs.TitleFunc, - "tostring": funcs.MakeToFunc(cty.String), - "tonumber": funcs.MakeToFunc(cty.Number), - "tobool": funcs.MakeToFunc(cty.Bool), - "toset": funcs.MakeToFunc(cty.Set(cty.DynamicPseudoType)), - "tolist": funcs.MakeToFunc(cty.List(cty.DynamicPseudoType)), - "tomap": funcs.MakeToFunc(cty.Map(cty.DynamicPseudoType)), - "transpose": funcs.TransposeFunc, - "trimspace": funcs.TrimSpaceFunc, - "upper": stdlib.UpperFunc, - "urlencode": funcs.URLEncodeFunc, - "uuid": funcs.UUIDFunc, - "uuidv5": funcs.UUIDV5Func, - "values": funcs.ValuesFunc, - "yamldecode": ctyyaml.YAMLDecodeFunc, - "yamlencode": ctyyaml.YAMLEncodeFunc, - "zipmap": funcs.ZipmapFunc, - } - - s.funcs["templatefile"] = funcs.MakeTemplateFileFunc(s.BaseDir, func() map[string]function.Function { - // The templatefile function prevents recursive calls to itself - // by copying this map and overwriting the "templatefile" entry. - return s.funcs - }) - - if s.PureOnly { - // Force our few impure functions to return unknown so that we - // can defer evaluating them until a later pass. - for _, name := range impureFunctions { - s.funcs[name] = function.Unpredictable(s.funcs[name]) - } - } - } - s.funcsLock.Unlock() - - return s.funcs -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/references.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/references.go deleted file mode 100644 index 7923d511..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/references.go +++ /dev/null @@ -1,81 +0,0 @@ -package lang - -import ( - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/lang/blocktoattr" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// References finds all of the references in the given set of traversals, -// returning diagnostics if any of the traversals cannot be interpreted as a -// reference. -// -// This function does not do any de-duplication of references, since references -// have source location information embedded in them and so any invalid -// references that are duplicated should have errors reported for each -// occurence. -// -// If the returned diagnostics contains errors then the result may be -// incomplete or invalid. Otherwise, the returned slice has one reference per -// given traversal, though it is not guaranteed that the references will -// appear in the same order as the given traversals. -func References(traversals []hcl.Traversal) ([]*addrs.Reference, tfdiags.Diagnostics) { - if len(traversals) == 0 { - return nil, nil - } - - var diags tfdiags.Diagnostics - refs := make([]*addrs.Reference, 0, len(traversals)) - - for _, traversal := range traversals { - ref, refDiags := addrs.ParseRef(traversal) - diags = diags.Append(refDiags) - if ref == nil { - continue - } - refs = append(refs, ref) - } - - return refs, diags -} - -// ReferencesInBlock is a helper wrapper around References that first searches -// the given body for traversals, before converting those traversals to -// references. -// -// A block schema must be provided so that this function can determine where in -// the body variables are expected. -func ReferencesInBlock(body hcl.Body, schema *configschema.Block) ([]*addrs.Reference, tfdiags.Diagnostics) { - if body == nil { - return nil, nil - } - - // We use blocktoattr.ExpandedVariables instead of hcldec.Variables or - // dynblock.VariablesHCLDec here because when we evaluate a block we'll - // first apply the dynamic block extension and _then_ the blocktoattr - // transform, and so blocktoattr.ExpandedVariables takes into account - // both of those transforms when it analyzes the body to ensure we find - // all of the references as if they'd already moved into their final - // locations, even though we can't expand dynamic blocks yet until we - // already know which variables are required. - // - // The set of cases we want to detect here is covered by the tests for - // the plan graph builder in the main 'terraform' package, since it's - // in a better position to test this due to having mock providers etc - // available. - traversals := blocktoattr.ExpandedVariables(body, schema) - return References(traversals) -} - -// ReferencesInExpr is a helper wrapper around References that first searches -// the given expression for traversals, before converting those traversals -// to references. -func ReferencesInExpr(expr hcl.Expression) ([]*addrs.Reference, tfdiags.Diagnostics) { - if expr == nil { - return nil, nil - } - traversals := expr.Variables() - return References(traversals) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/scope.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/scope.go deleted file mode 100644 index a720cca6..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/lang/scope.go +++ /dev/null @@ -1,34 +0,0 @@ -package lang - -import ( - "sync" - - "github.com/zclconf/go-cty/cty/function" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// Scope is the main type in this package, allowing dynamic evaluation of -// blocks and expressions based on some contextual information that informs -// which variables and functions will be available. -type Scope struct { - // Data is used to resolve references in expressions. - Data Data - - // SelfAddr is the address that the "self" object should be an alias of, - // or nil if the "self" object should not be available at all. - SelfAddr addrs.Referenceable - - // BaseDir is the base directory used by any interpolation functions that - // accept filesystem paths as arguments. - BaseDir string - - // PureOnly can be set to true to request that any non-pure functions - // produce unknown value results rather than actually executing. This is - // important during a plan phase to avoid generating results that could - // then differ during apply. - PureOnly bool - - funcs map[string]function.Function - funcsLock sync.Mutex -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/modsdir/doc.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/modsdir/doc.go deleted file mode 100644 index 0d7d664f..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/modsdir/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -// Package modsdir is an internal package containing the model types used to -// represent the manifest of modules in a local modules cache directory. -package modsdir diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/modsdir/manifest.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/modsdir/manifest.go deleted file mode 100644 index 2d45c852..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/modsdir/manifest.go +++ /dev/null @@ -1,138 +0,0 @@ -package modsdir - -import ( - "encoding/json" - "fmt" - "io" - "io/ioutil" - "log" - "os" - "path/filepath" - - version "github.com/hashicorp/go-version" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// Record represents some metadata about an installed module, as part -// of a ModuleManifest. -type Record struct { - // Key is a unique identifier for this particular module, based on its - // position within the static module tree. - Key string `json:"Key"` - - // SourceAddr is the source address given for this module in configuration. - // This is used only to detect if the source was changed in configuration - // since the module was last installed, which means that the installer - // must re-install it. - SourceAddr string `json:"Source"` - - // Version is the exact version of the module, which results from parsing - // VersionStr. nil for un-versioned modules. - Version *version.Version `json:"-"` - - // VersionStr is the version specifier string. This is used only for - // serialization in snapshots and should not be accessed or updated - // by any other codepaths; use "Version" instead. - VersionStr string `json:"Version,omitempty"` - - // Dir is the path to the local directory where the module is installed. - Dir string `json:"Dir"` -} - -// Manifest is a map used to keep track of the filesystem locations -// and other metadata about installed modules. -// -// The configuration loader refers to this, while the module installer updates -// it to reflect any changes to the installed modules. -type Manifest map[string]Record - -func (m Manifest) ModuleKey(path addrs.Module) string { - return path.String() -} - -// manifestSnapshotFile is an internal struct used only to assist in our JSON -// serialization of manifest snapshots. It should not be used for any other -// purpose. -type manifestSnapshotFile struct { - Records []Record `json:"Modules"` -} - -func ReadManifestSnapshot(r io.Reader) (Manifest, error) { - src, err := ioutil.ReadAll(r) - if err != nil { - return nil, err - } - - if len(src) == 0 { - // This should never happen, but we'll tolerate it as if it were - // a valid empty JSON object. - return make(Manifest), nil - } - - var read manifestSnapshotFile - err = json.Unmarshal(src, &read) - - new := make(Manifest) - for _, record := range read.Records { - if record.VersionStr != "" { - record.Version, err = version.NewVersion(record.VersionStr) - if err != nil { - return nil, fmt.Errorf("invalid version %q for %s: %s", record.VersionStr, record.Key, err) - } - } - if _, exists := new[record.Key]; exists { - // This should never happen in any valid file, so we'll catch it - // and report it to avoid confusing/undefined behavior if the - // snapshot file was edited incorrectly outside of Terraform. - return nil, fmt.Errorf("snapshot file contains two records for path %s", record.Key) - } - new[record.Key] = record - } - return new, nil -} - -func ReadManifestSnapshotForDir(dir string) (Manifest, error) { - fn := filepath.Join(dir, ManifestSnapshotFilename) - r, err := os.Open(fn) - if err != nil { - if os.IsNotExist(err) { - return make(Manifest), nil // missing file is okay and treated as empty - } - return nil, err - } - return ReadManifestSnapshot(r) -} - -func (m Manifest) WriteSnapshot(w io.Writer) error { - var write manifestSnapshotFile - - for _, record := range m { - // Make sure VersionStr is in sync with Version, since we encourage - // callers to manipulate Version and ignore VersionStr. - if record.Version != nil { - record.VersionStr = record.Version.String() - } else { - record.VersionStr = "" - } - write.Records = append(write.Records, record) - } - - src, err := json.Marshal(write) - if err != nil { - return err - } - - _, err = w.Write(src) - return err -} - -func (m Manifest) WriteSnapshotToDir(dir string) error { - fn := filepath.Join(dir, ManifestSnapshotFilename) - log.Printf("[TRACE] modsdir: writing modules manifest to %s", fn) - w, err := os.Create(fn) - if err != nil { - return err - } - return m.WriteSnapshot(w) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/modsdir/paths.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/modsdir/paths.go deleted file mode 100644 index 9ebb5243..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/modsdir/paths.go +++ /dev/null @@ -1,3 +0,0 @@ -package modsdir - -const ManifestSnapshotFilename = "modules.json" diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/moduledeps/dependencies.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/moduledeps/dependencies.go deleted file mode 100644 index c8058871..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/moduledeps/dependencies.go +++ /dev/null @@ -1,43 +0,0 @@ -package moduledeps - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery" -) - -// Providers describes a set of provider dependencies for a given module. -// -// Each named provider instance can have one version constraint. -type Providers map[ProviderInstance]ProviderDependency - -// ProviderDependency describes the dependency for a particular provider -// instance, including both the set of allowed versions and the reason for -// the dependency. -type ProviderDependency struct { - Constraints discovery.Constraints - Reason ProviderDependencyReason -} - -// ProviderDependencyReason is an enumeration of reasons why a dependency might be -// present. -type ProviderDependencyReason int - -const ( - // ProviderDependencyExplicit means that there is an explicit "provider" - // block in the configuration for this module. - ProviderDependencyExplicit ProviderDependencyReason = iota - - // ProviderDependencyImplicit means that there is no explicit "provider" - // block but there is at least one resource that uses this provider. - ProviderDependencyImplicit - - // ProviderDependencyInherited is a special case of - // ProviderDependencyImplicit where a parent module has defined a - // configuration for the provider that has been inherited by at least one - // resource in this module. - ProviderDependencyInherited - - // ProviderDependencyFromState means that this provider is not currently - // referenced by configuration at all, but some existing instances in - // the state still depend on it. - ProviderDependencyFromState -) diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/moduledeps/doc.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/moduledeps/doc.go deleted file mode 100644 index 7eff0831..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/moduledeps/doc.go +++ /dev/null @@ -1,7 +0,0 @@ -// Package moduledeps contains types that can be used to describe the -// providers required for all of the modules in a module tree. -// -// It does not itself contain the functionality for populating such -// data structures; that's in Terraform core, since this package intentionally -// does not depend on terraform core to avoid package dependency cycles. -package moduledeps diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/moduledeps/module.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/moduledeps/module.go deleted file mode 100644 index 5189acfc..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/moduledeps/module.go +++ /dev/null @@ -1,134 +0,0 @@ -package moduledeps - -import ( - "sort" - "strings" - - "github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery" -) - -// Module represents the dependencies of a single module, as well being -// a node in a tree of such structures representing the dependencies of -// an entire configuration. -type Module struct { - Name string - Providers Providers - Children []*Module -} - -// WalkFunc is a callback type for use with Module.WalkTree -type WalkFunc func(path []string, parent *Module, current *Module) error - -// WalkTree calls the given callback once for the receiver and then -// once for each descendent, in an order such that parents are called -// before their children and siblings are called in the order they -// appear in the Children slice. -// -// When calling the callback, parent will be nil for the first call -// for the receiving module, and then set to the direct parent of -// each module for the subsequent calls. -// -// The path given to the callback is valid only until the callback -// returns, after which it will be mutated and reused. Callbacks must -// therefore copy the path slice if they wish to retain it. -// -// If the given callback returns an error, the walk will be aborted at -// that point and that error returned to the caller. -// -// This function is not thread-safe for concurrent modifications of the -// data structure, so it's the caller's responsibility to arrange for that -// should it be needed. -// -// It is safe for a callback to modify the descendents of the "current" -// module, including the ordering of the Children slice itself, but the -// callback MUST NOT modify the parent module. -func (m *Module) WalkTree(cb WalkFunc) error { - return walkModuleTree(make([]string, 0, 1), nil, m, cb) -} - -func walkModuleTree(path []string, parent *Module, current *Module, cb WalkFunc) error { - path = append(path, current.Name) - err := cb(path, parent, current) - if err != nil { - return err - } - - for _, child := range current.Children { - err := walkModuleTree(path, current, child, cb) - if err != nil { - return err - } - } - return nil -} - -// SortChildren sorts the Children slice into lexicographic order by -// name, in-place. -// -// This is primarily useful prior to calling WalkTree so that the walk -// will proceed in a consistent order. -func (m *Module) SortChildren() { - sort.Sort(sortModules{m.Children}) -} - -type sortModules struct { - modules []*Module -} - -func (s sortModules) Len() int { - return len(s.modules) -} - -func (s sortModules) Less(i, j int) bool { - cmp := strings.Compare(s.modules[i].Name, s.modules[j].Name) - return cmp < 0 -} - -func (s sortModules) Swap(i, j int) { - s.modules[i], s.modules[j] = s.modules[j], s.modules[i] -} - -// PluginRequirements produces a PluginRequirements structure that can -// be used with discovery.PluginMetaSet.ConstrainVersions to identify -// suitable plugins to satisfy the module's provider dependencies. -// -// This method only considers the direct requirements of the receiver. -// Use AllPluginRequirements to flatten the dependencies for the -// entire tree of modules. -// -// Requirements returned by this method include only version constraints, -// and apply no particular SHA256 hash constraint. -func (m *Module) PluginRequirements() discovery.PluginRequirements { - ret := make(discovery.PluginRequirements) - for inst, dep := range m.Providers { - // m.Providers is keyed on provider names, such as "aws.foo". - // a PluginRequirements wants keys to be provider *types*, such - // as "aws". If there are multiple aliases for the same - // provider then we will flatten them into a single requirement - // by combining their constraint sets. - pty := inst.Type() - if existing, exists := ret[pty]; exists { - ret[pty].Versions = existing.Versions.Append(dep.Constraints) - } else { - ret[pty] = &discovery.PluginConstraints{ - Versions: dep.Constraints, - } - } - } - return ret -} - -// AllPluginRequirements calls PluginRequirements for the receiver and all -// of its descendents, and merges the result into a single PluginRequirements -// structure that would satisfy all of the modules together. -// -// Requirements returned by this method include only version constraints, -// and apply no particular SHA256 hash constraint. -func (m *Module) AllPluginRequirements() discovery.PluginRequirements { - var ret discovery.PluginRequirements - m.WalkTree(func(path []string, parent *Module, current *Module) error { - ret = ret.Merge(current.PluginRequirements()) - return nil - }) - return ret -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/moduledeps/provider.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/moduledeps/provider.go deleted file mode 100644 index 89ceefb2..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/moduledeps/provider.go +++ /dev/null @@ -1,30 +0,0 @@ -package moduledeps - -import ( - "strings" -) - -// ProviderInstance describes a particular provider instance by its full name, -// like "null" or "aws.foo". -type ProviderInstance string - -// Type returns the provider type of this instance. For example, for an instance -// named "aws.foo" the type is "aws". -func (p ProviderInstance) Type() string { - t := string(p) - if dotPos := strings.Index(t, "."); dotPos != -1 { - t = t[:dotPos] - } - return t -} - -// Alias returns the alias of this provider, if any. An instance named "aws.foo" -// has the alias "foo", while an instance named just "docker" has no alias, -// so the empty string would be returned. -func (p ProviderInstance) Alias() string { - t := string(p) - if dotPos := strings.Index(t, "."); dotPos != -1 { - return t[dotPos+1:] - } - return "" -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/action.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/action.go deleted file mode 100644 index c653b106..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/action.go +++ /dev/null @@ -1,22 +0,0 @@ -package plans - -type Action rune - -const ( - NoOp Action = 0 - Create Action = '+' - Read Action = '←' - Update Action = '~' - DeleteThenCreate Action = '∓' - CreateThenDelete Action = '±' - Delete Action = '-' -) - -//go:generate go run golang.org/x/tools/cmd/stringer -type Action - -// IsReplace returns true if the action is one of the two actions that -// represents replacing an existing object with a new object: -// DeleteThenCreate or CreateThenDelete. -func (a Action) IsReplace() bool { - return a == DeleteThenCreate || a == CreateThenDelete -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/action_string.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/action_string.go deleted file mode 100644 index be43ab17..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/action_string.go +++ /dev/null @@ -1,49 +0,0 @@ -// Code generated by "stringer -type Action"; DO NOT EDIT. - -package plans - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[NoOp-0] - _ = x[Create-43] - _ = x[Read-8592] - _ = x[Update-126] - _ = x[DeleteThenCreate-8723] - _ = x[CreateThenDelete-177] - _ = x[Delete-45] -} - -const ( - _Action_name_0 = "NoOp" - _Action_name_1 = "Create" - _Action_name_2 = "Delete" - _Action_name_3 = "Update" - _Action_name_4 = "CreateThenDelete" - _Action_name_5 = "Read" - _Action_name_6 = "DeleteThenCreate" -) - -func (i Action) String() string { - switch { - case i == 0: - return _Action_name_0 - case i == 43: - return _Action_name_1 - case i == 45: - return _Action_name_2 - case i == 126: - return _Action_name_3 - case i == 177: - return _Action_name_4 - case i == 8592: - return _Action_name_5 - case i == 8723: - return _Action_name_6 - default: - return "Action(" + strconv.FormatInt(int64(i), 10) + ")" - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/changes.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/changes.go deleted file mode 100644 index 5c2028c8..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/changes.go +++ /dev/null @@ -1,308 +0,0 @@ -package plans - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/zclconf/go-cty/cty" -) - -// Changes describes various actions that Terraform will attempt to take if -// the corresponding plan is applied. -// -// A Changes object can be rendered into a visual diff (by the caller, using -// code in another package) for display to the user. -type Changes struct { - // Resources tracks planned changes to resource instance objects. - Resources []*ResourceInstanceChangeSrc - - // Outputs tracks planned changes output values. - // - // Note that although an in-memory plan contains planned changes for - // outputs throughout the configuration, a plan serialized - // to disk retains only the root outputs because they are - // externally-visible, while other outputs are implementation details and - // can be easily re-calculated during the apply phase. Therefore only root - // module outputs will survive a round-trip through a plan file. - Outputs []*OutputChangeSrc -} - -// NewChanges returns a valid Changes object that describes no changes. -func NewChanges() *Changes { - return &Changes{} -} - -func (c *Changes) Empty() bool { - for _, res := range c.Resources { - if res.Action != NoOp { - return false - } - } - return true -} - -// ResourceInstance returns the planned change for the current object of the -// resource instance of the given address, if any. Returns nil if no change is -// planned. -func (c *Changes) ResourceInstance(addr addrs.AbsResourceInstance) *ResourceInstanceChangeSrc { - addrStr := addr.String() - for _, rc := range c.Resources { - if rc.Addr.String() == addrStr && rc.DeposedKey == states.NotDeposed { - return rc - } - } - - return nil -} - -// ResourceInstanceDeposed returns the plan change of a deposed object of -// the resource instance of the given address, if any. Returns nil if no change -// is planned. -func (c *Changes) ResourceInstanceDeposed(addr addrs.AbsResourceInstance, key states.DeposedKey) *ResourceInstanceChangeSrc { - addrStr := addr.String() - for _, rc := range c.Resources { - if rc.Addr.String() == addrStr && rc.DeposedKey == key { - return rc - } - } - - return nil -} - -// OutputValue returns the planned change for the output value with the -// given address, if any. Returns nil if no change is planned. -func (c *Changes) OutputValue(addr addrs.AbsOutputValue) *OutputChangeSrc { - addrStr := addr.String() - for _, oc := range c.Outputs { - if oc.Addr.String() == addrStr { - return oc - } - } - - return nil -} - -// SyncWrapper returns a wrapper object around the receiver that can be used -// to make certain changes to the receiver in a concurrency-safe way, as long -// as all callers share the same wrapper object. -func (c *Changes) SyncWrapper() *ChangesSync { - return &ChangesSync{ - changes: c, - } -} - -// ResourceInstanceChange describes a change to a particular resource instance -// object. -type ResourceInstanceChange struct { - // Addr is the absolute address of the resource instance that the change - // will apply to. - Addr addrs.AbsResourceInstance - - // DeposedKey is the identifier for a deposed object associated with the - // given instance, or states.NotDeposed if this change applies to the - // current object. - // - // A Replace change for a resource with create_before_destroy set will - // create a new DeposedKey temporarily during replacement. In that case, - // DeposedKey in the plan is always states.NotDeposed, representing that - // the current object is being replaced with the deposed. - DeposedKey states.DeposedKey - - // Provider is the address of the provider configuration that was used - // to plan this change, and thus the configuration that must also be - // used to apply it. - ProviderAddr addrs.AbsProviderConfig - - // Change is an embedded description of the change. - Change - - // RequiredReplace is a set of paths that caused the change action to be - // Replace rather than Update. Always nil if the change action is not - // Replace. - // - // This is retained only for UI-plan-rendering purposes and so it does not - // currently survive a round-trip through a saved plan file. - RequiredReplace cty.PathSet - - // Private allows a provider to stash any extra data that is opaque to - // Terraform that relates to this change. Terraform will save this - // byte-for-byte and return it to the provider in the apply call. - Private []byte -} - -// Encode produces a variant of the reciever that has its change values -// serialized so it can be written to a plan file. Pass the implied type of the -// corresponding resource type schema for correct operation. -func (rc *ResourceInstanceChange) Encode(ty cty.Type) (*ResourceInstanceChangeSrc, error) { - cs, err := rc.Change.Encode(ty) - if err != nil { - return nil, err - } - return &ResourceInstanceChangeSrc{ - Addr: rc.Addr, - DeposedKey: rc.DeposedKey, - ProviderAddr: rc.ProviderAddr, - ChangeSrc: *cs, - RequiredReplace: rc.RequiredReplace, - Private: rc.Private, - }, err -} - -// Simplify will, where possible, produce a change with a simpler action than -// the receiever given a flag indicating whether the caller is dealing with -// a normal apply or a destroy. This flag deals with the fact that Terraform -// Core uses a specialized graph node type for destroying; only that -// specialized node should set "destroying" to true. -// -// The following table shows the simplification behavior: -// -// Action Destroying? New Action -// --------+-------------+----------- -// Create true NoOp -// Delete false NoOp -// Replace true Delete -// Replace false Create -// -// For any combination not in the above table, the Simplify just returns the -// receiver as-is. -func (rc *ResourceInstanceChange) Simplify(destroying bool) *ResourceInstanceChange { - if destroying { - switch rc.Action { - case Delete: - // We'll fall out and just return rc verbatim, then. - case CreateThenDelete, DeleteThenCreate: - return &ResourceInstanceChange{ - Addr: rc.Addr, - DeposedKey: rc.DeposedKey, - Private: rc.Private, - ProviderAddr: rc.ProviderAddr, - Change: Change{ - Action: Delete, - Before: rc.Before, - After: cty.NullVal(rc.Before.Type()), - }, - } - default: - return &ResourceInstanceChange{ - Addr: rc.Addr, - DeposedKey: rc.DeposedKey, - Private: rc.Private, - ProviderAddr: rc.ProviderAddr, - Change: Change{ - Action: NoOp, - Before: rc.Before, - After: rc.Before, - }, - } - } - } else { - switch rc.Action { - case Delete: - return &ResourceInstanceChange{ - Addr: rc.Addr, - DeposedKey: rc.DeposedKey, - Private: rc.Private, - ProviderAddr: rc.ProviderAddr, - Change: Change{ - Action: NoOp, - Before: rc.Before, - After: rc.Before, - }, - } - case CreateThenDelete, DeleteThenCreate: - return &ResourceInstanceChange{ - Addr: rc.Addr, - DeposedKey: rc.DeposedKey, - Private: rc.Private, - ProviderAddr: rc.ProviderAddr, - Change: Change{ - Action: Create, - Before: cty.NullVal(rc.After.Type()), - After: rc.After, - }, - } - } - } - - // If we fall out here then our change is already simple enough. - return rc -} - -// OutputChange describes a change to an output value. -type OutputChange struct { - // Addr is the absolute address of the output value that the change - // will apply to. - Addr addrs.AbsOutputValue - - // Change is an embedded description of the change. - // - // For output value changes, the type constraint for the DynamicValue - // instances is always cty.DynamicPseudoType. - Change - - // Sensitive, if true, indicates that either the old or new value in the - // change is sensitive and so a rendered version of the plan in the UI - // should elide the actual values while still indicating the action of the - // change. - Sensitive bool -} - -// Encode produces a variant of the reciever that has its change values -// serialized so it can be written to a plan file. -func (oc *OutputChange) Encode() (*OutputChangeSrc, error) { - cs, err := oc.Change.Encode(cty.DynamicPseudoType) - if err != nil { - return nil, err - } - return &OutputChangeSrc{ - Addr: oc.Addr, - ChangeSrc: *cs, - Sensitive: oc.Sensitive, - }, err -} - -// Change describes a single change with a given action. -type Change struct { - // Action defines what kind of change is being made. - Action Action - - // Interpretation of Before and After depend on Action: - // - // NoOp Before and After are the same, unchanged value - // Create Before is nil, and After is the expected value after create. - // Read Before is any prior value (nil if no prior), and After is the - // value that was or will be read. - // Update Before is the value prior to update, and After is the expected - // value after update. - // Replace As with Update. - // Delete Before is the value prior to delete, and After is always nil. - // - // Unknown values may appear anywhere within the Before and After values, - // either as the values themselves or as nested elements within known - // collections/structures. - Before, After cty.Value -} - -// Encode produces a variant of the reciever that has its change values -// serialized so it can be written to a plan file. Pass the type constraint -// that the values are expected to conform to; to properly decode the values -// later an identical type constraint must be provided at that time. -// -// Where a Change is embedded in some other struct, it's generally better -// to call the corresponding Encode method of that struct rather than working -// directly with its embedded Change. -func (c *Change) Encode(ty cty.Type) (*ChangeSrc, error) { - beforeDV, err := NewDynamicValue(c.Before, ty) - if err != nil { - return nil, err - } - afterDV, err := NewDynamicValue(c.After, ty) - if err != nil { - return nil, err - } - - return &ChangeSrc{ - Action: c.Action, - Before: beforeDV, - After: afterDV, - }, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/changes_src.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/changes_src.go deleted file mode 100644 index 97bc8da7..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/changes_src.go +++ /dev/null @@ -1,190 +0,0 @@ -package plans - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/zclconf/go-cty/cty" -) - -// ResourceInstanceChangeSrc is a not-yet-decoded ResourceInstanceChange. -// Pass the associated resource type's schema type to method Decode to -// obtain a ResourceInstancChange. -type ResourceInstanceChangeSrc struct { - // Addr is the absolute address of the resource instance that the change - // will apply to. - Addr addrs.AbsResourceInstance - - // DeposedKey is the identifier for a deposed object associated with the - // given instance, or states.NotDeposed if this change applies to the - // current object. - // - // A Replace change for a resource with create_before_destroy set will - // create a new DeposedKey temporarily during replacement. In that case, - // DeposedKey in the plan is always states.NotDeposed, representing that - // the current object is being replaced with the deposed. - DeposedKey states.DeposedKey - - // Provider is the address of the provider configuration that was used - // to plan this change, and thus the configuration that must also be - // used to apply it. - ProviderAddr addrs.AbsProviderConfig - - // ChangeSrc is an embedded description of the not-yet-decoded change. - ChangeSrc - - // RequiredReplace is a set of paths that caused the change action to be - // Replace rather than Update. Always nil if the change action is not - // Replace. - // - // This is retained only for UI-plan-rendering purposes and so it does not - // currently survive a round-trip through a saved plan file. - RequiredReplace cty.PathSet - - // Private allows a provider to stash any extra data that is opaque to - // Terraform that relates to this change. Terraform will save this - // byte-for-byte and return it to the provider in the apply call. - Private []byte -} - -// Decode unmarshals the raw representation of the instance object being -// changed. Pass the implied type of the corresponding resource type schema -// for correct operation. -func (rcs *ResourceInstanceChangeSrc) Decode(ty cty.Type) (*ResourceInstanceChange, error) { - change, err := rcs.ChangeSrc.Decode(ty) - if err != nil { - return nil, err - } - return &ResourceInstanceChange{ - Addr: rcs.Addr, - DeposedKey: rcs.DeposedKey, - ProviderAddr: rcs.ProviderAddr, - Change: *change, - RequiredReplace: rcs.RequiredReplace, - Private: rcs.Private, - }, nil -} - -// DeepCopy creates a copy of the receiver where any pointers to nested mutable -// values are also copied, thus ensuring that future mutations of the receiver -// will not affect the copy. -// -// Some types used within a resource change are immutable by convention even -// though the Go language allows them to be mutated, such as the types from -// the addrs package. These are _not_ copied by this method, under the -// assumption that callers will behave themselves. -func (rcs *ResourceInstanceChangeSrc) DeepCopy() *ResourceInstanceChangeSrc { - if rcs == nil { - return nil - } - ret := *rcs - - ret.RequiredReplace = cty.NewPathSet(ret.RequiredReplace.List()...) - - if len(ret.Private) != 0 { - private := make([]byte, len(ret.Private)) - copy(private, ret.Private) - ret.Private = private - } - - ret.ChangeSrc.Before = ret.ChangeSrc.Before.Copy() - ret.ChangeSrc.After = ret.ChangeSrc.After.Copy() - - return &ret -} - -// OutputChangeSrc describes a change to an output value. -type OutputChangeSrc struct { - // Addr is the absolute address of the output value that the change - // will apply to. - Addr addrs.AbsOutputValue - - // ChangeSrc is an embedded description of the not-yet-decoded change. - // - // For output value changes, the type constraint for the DynamicValue - // instances is always cty.DynamicPseudoType. - ChangeSrc - - // Sensitive, if true, indicates that either the old or new value in the - // change is sensitive and so a rendered version of the plan in the UI - // should elide the actual values while still indicating the action of the - // change. - Sensitive bool -} - -// Decode unmarshals the raw representation of the output value being -// changed. -func (ocs *OutputChangeSrc) Decode() (*OutputChange, error) { - change, err := ocs.ChangeSrc.Decode(cty.DynamicPseudoType) - if err != nil { - return nil, err - } - return &OutputChange{ - Addr: ocs.Addr, - Change: *change, - Sensitive: ocs.Sensitive, - }, nil -} - -// DeepCopy creates a copy of the receiver where any pointers to nested mutable -// values are also copied, thus ensuring that future mutations of the receiver -// will not affect the copy. -// -// Some types used within a resource change are immutable by convention even -// though the Go language allows them to be mutated, such as the types from -// the addrs package. These are _not_ copied by this method, under the -// assumption that callers will behave themselves. -func (ocs *OutputChangeSrc) DeepCopy() *OutputChangeSrc { - if ocs == nil { - return nil - } - ret := *ocs - - ret.ChangeSrc.Before = ret.ChangeSrc.Before.Copy() - ret.ChangeSrc.After = ret.ChangeSrc.After.Copy() - - return &ret -} - -// ChangeSrc is a not-yet-decoded Change. -type ChangeSrc struct { - // Action defines what kind of change is being made. - Action Action - - // Before and After correspond to the fields of the same name in Change, - // but have not yet been decoded from the serialized value used for - // storage. - Before, After DynamicValue -} - -// Decode unmarshals the raw representations of the before and after values -// to produce a Change object. Pass the type constraint that the result must -// conform to. -// -// Where a ChangeSrc is embedded in some other struct, it's generally better -// to call the corresponding Decode method of that struct rather than working -// directly with its embedded Change. -func (cs *ChangeSrc) Decode(ty cty.Type) (*Change, error) { - var err error - before := cty.NullVal(ty) - after := cty.NullVal(ty) - - if len(cs.Before) > 0 { - before, err = cs.Before.Decode(ty) - if err != nil { - return nil, fmt.Errorf("error decoding 'before' value: %s", err) - } - } - if len(cs.After) > 0 { - after, err = cs.After.Decode(ty) - if err != nil { - return nil, fmt.Errorf("error decoding 'after' value: %s", err) - } - } - return &Change{ - Action: cs.Action, - Before: before, - After: after, - }, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/changes_sync.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/changes_sync.go deleted file mode 100644 index 89cc1ab2..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/changes_sync.go +++ /dev/null @@ -1,144 +0,0 @@ -package plans - -import ( - "fmt" - "sync" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// ChangesSync is a wrapper around a Changes that provides a concurrency-safe -// interface to insert new changes and retrieve copies of existing changes. -// -// Each ChangesSync is independent of all others, so all concurrent writers -// to a particular Changes must share a single ChangesSync. Behavior is -// undefined if any other caller makes changes to the underlying Changes -// object or its nested objects concurrently with any of the methods of a -// particular ChangesSync. -type ChangesSync struct { - lock sync.Mutex - changes *Changes -} - -// AppendResourceInstanceChange records the given resource instance change in -// the set of planned resource changes. -// -// The caller must ensure that there are no concurrent writes to the given -// change while this method is running, but it is safe to resume mutating -// it after this method returns without affecting the saved change. -func (cs *ChangesSync) AppendResourceInstanceChange(changeSrc *ResourceInstanceChangeSrc) { - if cs == nil { - panic("AppendResourceInstanceChange on nil ChangesSync") - } - cs.lock.Lock() - defer cs.lock.Unlock() - - s := changeSrc.DeepCopy() - cs.changes.Resources = append(cs.changes.Resources, s) -} - -// GetResourceInstanceChange searches the set of resource instance changes for -// one matching the given address and generation, returning it if it exists. -// -// If no such change exists, nil is returned. -// -// The returned object is a deep copy of the change recorded in the plan, so -// callers may mutate it although it's generally better (less confusing) to -// treat planned changes as immutable after they've been initially constructed. -func (cs *ChangesSync) GetResourceInstanceChange(addr addrs.AbsResourceInstance, gen states.Generation) *ResourceInstanceChangeSrc { - if cs == nil { - panic("GetResourceInstanceChange on nil ChangesSync") - } - cs.lock.Lock() - defer cs.lock.Unlock() - - if gen == states.CurrentGen { - return cs.changes.ResourceInstance(addr).DeepCopy() - } - if dk, ok := gen.(states.DeposedKey); ok { - return cs.changes.ResourceInstanceDeposed(addr, dk).DeepCopy() - } - panic(fmt.Sprintf("unsupported generation value %#v", gen)) -} - -// RemoveResourceInstanceChange searches the set of resource instance changes -// for one matching the given address and generation, and removes it from the -// set if it exists. -func (cs *ChangesSync) RemoveResourceInstanceChange(addr addrs.AbsResourceInstance, gen states.Generation) { - if cs == nil { - panic("RemoveResourceInstanceChange on nil ChangesSync") - } - cs.lock.Lock() - defer cs.lock.Unlock() - - dk := states.NotDeposed - if realDK, ok := gen.(states.DeposedKey); ok { - dk = realDK - } - - addrStr := addr.String() - for i, r := range cs.changes.Resources { - if r.Addr.String() != addrStr || r.DeposedKey != dk { - continue - } - copy(cs.changes.Resources[i:], cs.changes.Resources[i+1:]) - cs.changes.Resources = cs.changes.Resources[:len(cs.changes.Resources)-1] - return - } -} - -// AppendOutputChange records the given output value change in the set of -// planned value changes. -// -// The caller must ensure that there are no concurrent writes to the given -// change while this method is running, but it is safe to resume mutating -// it after this method returns without affecting the saved change. -func (cs *ChangesSync) AppendOutputChange(changeSrc *OutputChangeSrc) { - if cs == nil { - panic("AppendOutputChange on nil ChangesSync") - } - cs.lock.Lock() - defer cs.lock.Unlock() - - s := changeSrc.DeepCopy() - cs.changes.Outputs = append(cs.changes.Outputs, s) -} - -// GetOutputChange searches the set of output value changes for one matching -// the given address, returning it if it exists. -// -// If no such change exists, nil is returned. -// -// The returned object is a deep copy of the change recorded in the plan, so -// callers may mutate it although it's generally better (less confusing) to -// treat planned changes as immutable after they've been initially constructed. -func (cs *ChangesSync) GetOutputChange(addr addrs.AbsOutputValue) *OutputChangeSrc { - if cs == nil { - panic("GetOutputChange on nil ChangesSync") - } - cs.lock.Lock() - defer cs.lock.Unlock() - - return cs.changes.OutputValue(addr) -} - -// RemoveOutputChange searches the set of output value changes for one matching -// the given address, and removes it from the set if it exists. -func (cs *ChangesSync) RemoveOutputChange(addr addrs.AbsOutputValue) { - if cs == nil { - panic("RemoveOutputChange on nil ChangesSync") - } - cs.lock.Lock() - defer cs.lock.Unlock() - - addrStr := addr.String() - for i, o := range cs.changes.Outputs { - if o.Addr.String() != addrStr { - continue - } - copy(cs.changes.Outputs[i:], cs.changes.Outputs[i+1:]) - cs.changes.Outputs = cs.changes.Outputs[:len(cs.changes.Outputs)-1] - return - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/doc.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/doc.go deleted file mode 100644 index 01ca3892..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/doc.go +++ /dev/null @@ -1,5 +0,0 @@ -// Package plans contains the types that are used to represent Terraform plans. -// -// A plan describes a set of changes that Terraform will make to update remote -// objects to match with changes to the configuration. -package plans diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/dynamic_value.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/dynamic_value.go deleted file mode 100644 index 51fbb24c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/dynamic_value.go +++ /dev/null @@ -1,96 +0,0 @@ -package plans - -import ( - "github.com/zclconf/go-cty/cty" - ctymsgpack "github.com/zclconf/go-cty/cty/msgpack" -) - -// DynamicValue is the representation in the plan of a value whose type cannot -// be determined at compile time, such as because it comes from a schema -// defined in a plugin. -// -// This type is used as an indirection so that the overall plan structure can -// be decoded without schema available, and then the dynamic values accessed -// at a later time once the appropriate schema has been determined. -// -// Internally, DynamicValue is a serialized version of a cty.Value created -// against a particular type constraint. Callers should not access directly -// the serialized form, whose format may change in future. Values of this -// type must always be created by calling NewDynamicValue. -// -// The zero value of DynamicValue is nil, and represents the absense of a -// value within the Go type system. This is distinct from a cty.NullVal -// result, which represents the absense of a value within the cty type system. -type DynamicValue []byte - -// NewDynamicValue creates a DynamicValue by serializing the given value -// against the given type constraint. The value must conform to the type -// constraint, or the result is undefined. -// -// If the value to be encoded has no predefined schema (for example, for -// module output values and input variables), set the type constraint to -// cty.DynamicPseudoType in order to save type information as part of the -// value, and then also pass cty.DynamicPseudoType to method Decode to recover -// the original value. -// -// cty.NilVal can be used to represent the absense of a value, but callers -// must be careful to distinguish values that are absent at the Go layer -// (cty.NilVal) vs. values that are absent at the cty layer (cty.NullVal -// results). -func NewDynamicValue(val cty.Value, ty cty.Type) (DynamicValue, error) { - // If we're given cty.NilVal (the zero value of cty.Value, which is - // distinct from a typed null value created by cty.NullVal) then we'll - // assume the caller is trying to represent the _absense_ of a value, - // and so we'll return a nil DynamicValue. - if val == cty.NilVal { - return DynamicValue(nil), nil - } - - // Currently our internal encoding is msgpack, via ctymsgpack. - buf, err := ctymsgpack.Marshal(val, ty) - if err != nil { - return nil, err - } - - return DynamicValue(buf), nil -} - -// Decode retrieves the effective value from the receiever by interpreting the -// serialized form against the given type constraint. For correct results, -// the type constraint must match (or be consistent with) the one that was -// used to create the receiver. -// -// A nil DynamicValue decodes to cty.NilVal, which is not a valid value and -// instead represents the absense of a value. -func (v DynamicValue) Decode(ty cty.Type) (cty.Value, error) { - if v == nil { - return cty.NilVal, nil - } - - return ctymsgpack.Unmarshal([]byte(v), ty) -} - -// ImpliedType returns the type implied by the serialized structure of the -// receiving value. -// -// This will not necessarily be exactly the type that was given when the -// value was encoded, and in particular must not be used for values that -// were encoded with their static type given as cty.DynamicPseudoType. -// It is however safe to use this method for values that were encoded using -// their runtime type as the conforming type, with the result being -// semantically equivalent but with all lists and sets represented as tuples, -// and maps as objects, due to ambiguities of the serialization. -func (v DynamicValue) ImpliedType() (cty.Type, error) { - return ctymsgpack.ImpliedType([]byte(v)) -} - -// Copy produces a copy of the receiver with a distinct backing array. -func (v DynamicValue) Copy() DynamicValue { - if v == nil { - return nil - } - - ret := make(DynamicValue, len(v)) - copy(ret, v) - return ret -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/all_null.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/all_null.go deleted file mode 100644 index ba9cc961..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/all_null.go +++ /dev/null @@ -1,18 +0,0 @@ -package objchange - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/zclconf/go-cty/cty" -) - -// AllAttributesNull constructs a non-null cty.Value of the object type implied -// by the given schema that has all of its leaf attributes set to null and all -// of its nested block collections set to zero-length. -// -// This simulates what would result from decoding an empty configuration block -// with the given schema, except that it does not produce errors -func AllAttributesNull(schema *configschema.Block) cty.Value { - // "All attributes null" happens to be the definition of EmptyValue for - // a Block, so we can just delegate to that. - return schema.EmptyValue() -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/compatible.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/compatible.go deleted file mode 100644 index 36a7d496..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/compatible.go +++ /dev/null @@ -1,447 +0,0 @@ -package objchange - -import ( - "fmt" - "strconv" - - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" -) - -// AssertObjectCompatible checks whether the given "actual" value is a valid -// completion of the possibly-partially-unknown "planned" value. -// -// This means that any known leaf value in "planned" must be equal to the -// corresponding value in "actual", and various other similar constraints. -// -// Any inconsistencies are reported by returning a non-zero number of errors. -// These errors are usually (but not necessarily) cty.PathError values -// referring to a particular nested value within the "actual" value. -// -// The two values must have types that conform to the given schema's implied -// type, or this function will panic. -func AssertObjectCompatible(schema *configschema.Block, planned, actual cty.Value) []error { - return assertObjectCompatible(schema, planned, actual, nil) -} - -func assertObjectCompatible(schema *configschema.Block, planned, actual cty.Value, path cty.Path) []error { - var errs []error - if planned.IsNull() && !actual.IsNull() { - errs = append(errs, path.NewErrorf("was absent, but now present")) - return errs - } - if actual.IsNull() && !planned.IsNull() { - errs = append(errs, path.NewErrorf("was present, but now absent")) - return errs - } - if planned.IsNull() { - // No further checks possible if both values are null - return errs - } - - for name, attrS := range schema.Attributes { - plannedV := planned.GetAttr(name) - actualV := actual.GetAttr(name) - - path := append(path, cty.GetAttrStep{Name: name}) - moreErrs := assertValueCompatible(plannedV, actualV, path) - if attrS.Sensitive { - if len(moreErrs) > 0 { - // Use a vague placeholder message instead, to avoid disclosing - // sensitive information. - errs = append(errs, path.NewErrorf("inconsistent values for sensitive attribute")) - } - } else { - errs = append(errs, moreErrs...) - } - } - for name, blockS := range schema.BlockTypes { - plannedV := planned.GetAttr(name) - actualV := actual.GetAttr(name) - - // As a special case, if there were any blocks whose leaf attributes - // are all unknown then we assume (possibly incorrectly) that the - // HCL dynamic block extension is in use with an unknown for_each - // argument, and so we will do looser validation here that allows - // for those blocks to have expanded into a different number of blocks - // if the for_each value is now known. - maybeUnknownBlocks := couldHaveUnknownBlockPlaceholder(plannedV, blockS, false) - - path := append(path, cty.GetAttrStep{Name: name}) - switch blockS.Nesting { - case configschema.NestingSingle, configschema.NestingGroup: - // If an unknown block placeholder was present then the placeholder - // may have expanded out into zero blocks, which is okay. - if maybeUnknownBlocks && actualV.IsNull() { - continue - } - moreErrs := assertObjectCompatible(&blockS.Block, plannedV, actualV, path) - errs = append(errs, moreErrs...) - case configschema.NestingList: - // A NestingList might either be a list or a tuple, depending on - // whether there are dynamically-typed attributes inside. However, - // both support a similar-enough API that we can treat them the - // same for our purposes here. - if !plannedV.IsKnown() || !actualV.IsKnown() || plannedV.IsNull() || actualV.IsNull() { - continue - } - - if maybeUnknownBlocks { - // When unknown blocks are present the final blocks may be - // at different indices than the planned blocks, so unfortunately - // we can't do our usual checks in this case without generating - // false negatives. - continue - } - - plannedL := plannedV.LengthInt() - actualL := actualV.LengthInt() - if plannedL != actualL { - errs = append(errs, path.NewErrorf("block count changed from %d to %d", plannedL, actualL)) - continue - } - for it := plannedV.ElementIterator(); it.Next(); { - idx, plannedEV := it.Element() - if !actualV.HasIndex(idx).True() { - continue - } - actualEV := actualV.Index(idx) - moreErrs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.IndexStep{Key: idx})) - errs = append(errs, moreErrs...) - } - case configschema.NestingMap: - // A NestingMap might either be a map or an object, depending on - // whether there are dynamically-typed attributes inside, but - // that's decided statically and so both values will have the same - // kind. - if plannedV.Type().IsObjectType() { - plannedAtys := plannedV.Type().AttributeTypes() - actualAtys := actualV.Type().AttributeTypes() - for k := range plannedAtys { - if _, ok := actualAtys[k]; !ok { - errs = append(errs, path.NewErrorf("block key %q has vanished", k)) - continue - } - - plannedEV := plannedV.GetAttr(k) - actualEV := actualV.GetAttr(k) - moreErrs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.GetAttrStep{Name: k})) - errs = append(errs, moreErrs...) - } - if !maybeUnknownBlocks { // new blocks may appear if unknown blocks were present in the plan - for k := range actualAtys { - if _, ok := plannedAtys[k]; !ok { - errs = append(errs, path.NewErrorf("new block key %q has appeared", k)) - continue - } - } - } - } else { - if !plannedV.IsKnown() || plannedV.IsNull() || actualV.IsNull() { - continue - } - plannedL := plannedV.LengthInt() - actualL := actualV.LengthInt() - if plannedL != actualL && !maybeUnknownBlocks { // new blocks may appear if unknown blocks were persent in the plan - errs = append(errs, path.NewErrorf("block count changed from %d to %d", plannedL, actualL)) - continue - } - for it := plannedV.ElementIterator(); it.Next(); { - idx, plannedEV := it.Element() - if !actualV.HasIndex(idx).True() { - continue - } - actualEV := actualV.Index(idx) - moreErrs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.IndexStep{Key: idx})) - errs = append(errs, moreErrs...) - } - } - case configschema.NestingSet: - if !plannedV.IsKnown() || !actualV.IsKnown() || plannedV.IsNull() || actualV.IsNull() { - continue - } - - setErrs := assertSetValuesCompatible(plannedV, actualV, path, func(plannedEV, actualEV cty.Value) bool { - errs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.IndexStep{Key: actualEV})) - return len(errs) == 0 - }) - errs = append(errs, setErrs...) - - if maybeUnknownBlocks { - // When unknown blocks are present the final number of blocks - // may be different, either because the unknown set values - // become equal and are collapsed, or the count is unknown due - // a dynamic block. Unfortunately this means we can't do our - // usual checks in this case without generating false - // negatives. - continue - } - - // There can be fewer elements in a set after its elements are all - // known (values that turn out to be equal will coalesce) but the - // number of elements must never get larger. - plannedL := plannedV.LengthInt() - actualL := actualV.LengthInt() - if plannedL < actualL { - errs = append(errs, path.NewErrorf("block set length changed from %d to %d", plannedL, actualL)) - } - default: - panic(fmt.Sprintf("unsupported nesting mode %s", blockS.Nesting)) - } - } - return errs -} - -func assertValueCompatible(planned, actual cty.Value, path cty.Path) []error { - // NOTE: We don't normally use the GoString rendering of cty.Value in - // user-facing error messages as a rule, but we make an exception - // for this function because we expect the user to pass this message on - // verbatim to the provider development team and so more detail is better. - - var errs []error - if planned.Type() == cty.DynamicPseudoType { - // Anything goes, then - return errs - } - if problems := planned.Type().TestConformance(actual.Type()); len(problems) > 0 { - errs = append(errs, path.NewErrorf("wrong final value type: %s", convert.MismatchMessage(actual.Type(), planned.Type()))) - // If the types don't match then we can't do any other comparisons, - // so we bail early. - return errs - } - - if !planned.IsKnown() { - // We didn't know what were going to end up with during plan, so - // anything goes during apply. - return errs - } - - if actual.IsNull() { - if planned.IsNull() { - return nil - } - errs = append(errs, path.NewErrorf("was %#v, but now null", planned)) - return errs - } - if planned.IsNull() { - errs = append(errs, path.NewErrorf("was null, but now %#v", actual)) - return errs - } - - ty := planned.Type() - switch { - - case !actual.IsKnown(): - errs = append(errs, path.NewErrorf("was known, but now unknown")) - - case ty.IsPrimitiveType(): - if !actual.Equals(planned).True() { - errs = append(errs, path.NewErrorf("was %#v, but now %#v", planned, actual)) - } - - case ty.IsListType() || ty.IsMapType() || ty.IsTupleType(): - for it := planned.ElementIterator(); it.Next(); { - k, plannedV := it.Element() - if !actual.HasIndex(k).True() { - errs = append(errs, path.NewErrorf("element %s has vanished", indexStrForErrors(k))) - continue - } - - actualV := actual.Index(k) - moreErrs := assertValueCompatible(plannedV, actualV, append(path, cty.IndexStep{Key: k})) - errs = append(errs, moreErrs...) - } - - for it := actual.ElementIterator(); it.Next(); { - k, _ := it.Element() - if !planned.HasIndex(k).True() { - errs = append(errs, path.NewErrorf("new element %s has appeared", indexStrForErrors(k))) - } - } - - case ty.IsObjectType(): - atys := ty.AttributeTypes() - for name := range atys { - // Because we already tested that the two values have the same type, - // we can assume that the same attributes are present in both and - // focus just on testing their values. - plannedV := planned.GetAttr(name) - actualV := actual.GetAttr(name) - moreErrs := assertValueCompatible(plannedV, actualV, append(path, cty.GetAttrStep{Name: name})) - errs = append(errs, moreErrs...) - } - - case ty.IsSetType(): - // We can't really do anything useful for sets here because changing - // an unknown element to known changes the identity of the element, and - // so we can't correlate them properly. However, we will at least check - // to ensure that the number of elements is consistent, along with - // the general type-match checks we ran earlier in this function. - if planned.IsKnown() && !planned.IsNull() && !actual.IsNull() { - - setErrs := assertSetValuesCompatible(planned, actual, path, func(plannedV, actualV cty.Value) bool { - errs := assertValueCompatible(plannedV, actualV, append(path, cty.IndexStep{Key: actualV})) - return len(errs) == 0 - }) - errs = append(errs, setErrs...) - - // There can be fewer elements in a set after its elements are all - // known (values that turn out to be equal will coalesce) but the - // number of elements must never get larger. - - plannedL := planned.LengthInt() - actualL := actual.LengthInt() - if plannedL < actualL { - errs = append(errs, path.NewErrorf("length changed from %d to %d", plannedL, actualL)) - } - } - } - - return errs -} - -func indexStrForErrors(v cty.Value) string { - switch v.Type() { - case cty.Number: - return v.AsBigFloat().Text('f', -1) - case cty.String: - return strconv.Quote(v.AsString()) - default: - // Should be impossible, since no other index types are allowed! - return fmt.Sprintf("%#v", v) - } -} - -// couldHaveUnknownBlockPlaceholder is a heuristic that recognizes how the -// HCL dynamic block extension behaves when it's asked to expand a block whose -// for_each argument is unknown. In such cases, it generates a single placeholder -// block with all leaf attribute values unknown, and once the for_each -// expression becomes known the placeholder may be replaced with any number -// of blocks, so object compatibility checks would need to be more liberal. -// -// Set "nested" if testing a block that is nested inside a candidate block -// placeholder; this changes the interpretation of there being no blocks of -// a type to allow for there being zero nested blocks. -func couldHaveUnknownBlockPlaceholder(v cty.Value, blockS *configschema.NestedBlock, nested bool) bool { - switch blockS.Nesting { - case configschema.NestingSingle, configschema.NestingGroup: - if nested && v.IsNull() { - return true // for nested blocks, a single block being unset doesn't disqualify from being an unknown block placeholder - } - return couldBeUnknownBlockPlaceholderElement(v, &blockS.Block) - default: - // These situations should be impossible for correct providers, but - // we permit the legacy SDK to produce some incorrect outcomes - // for compatibility with its existing logic, and so we must be - // tolerant here. - if !v.IsKnown() { - return true - } - if v.IsNull() { - return false // treated as if the list were empty, so we would see zero iterations below - } - - // For all other nesting modes, our value should be something iterable. - for it := v.ElementIterator(); it.Next(); { - _, ev := it.Element() - if couldBeUnknownBlockPlaceholderElement(ev, &blockS.Block) { - return true - } - } - - // Our default changes depending on whether we're testing the candidate - // block itself or something nested inside of it: zero blocks of a type - // can never contain a dynamic block placeholder, but a dynamic block - // placeholder might contain zero blocks of one of its own nested block - // types, if none were set in the config at all. - return nested - } -} - -func couldBeUnknownBlockPlaceholderElement(v cty.Value, schema *configschema.Block) bool { - if v.IsNull() { - return false // null value can never be a placeholder element - } - if !v.IsKnown() { - return true // this should never happen for well-behaved providers, but can happen with the legacy SDK opt-outs - } - for name := range schema.Attributes { - av := v.GetAttr(name) - - // Unknown block placeholders contain only unknown or null attribute - // values, depending on whether or not a particular attribute was set - // explicitly inside the content block. Note that this is imprecise: - // non-placeholders can also match this, so this function can generate - // false positives. - if av.IsKnown() && !av.IsNull() { - return false - } - } - for name, blockS := range schema.BlockTypes { - if !couldHaveUnknownBlockPlaceholder(v.GetAttr(name), blockS, true) { - return false - } - } - return true -} - -// assertSetValuesCompatible checks that each of the elements in a can -// be correlated with at least one equivalent element in b and vice-versa, -// using the given correlation function. -// -// This allows the number of elements in the sets to change as long as all -// elements in both sets can be correlated, making this function safe to use -// with sets that may contain unknown values as long as the unknown case is -// addressed in some reasonable way in the callback function. -// -// The callback always recieves values from set a as its first argument and -// values from set b in its second argument, so it is safe to use with -// non-commutative functions. -// -// As with assertValueCompatible, we assume that the target audience of error -// messages here is a provider developer (via a bug report from a user) and so -// we intentionally violate our usual rule of keeping cty implementation -// details out of error messages. -func assertSetValuesCompatible(planned, actual cty.Value, path cty.Path, f func(aVal, bVal cty.Value) bool) []error { - a := planned - b := actual - - // Our methodology here is a little tricky, to deal with the fact that - // it's impossible to directly correlate two non-equal set elements because - // they don't have identities separate from their values. - // The approach is to count the number of equivalent elements each element - // of a has in b and vice-versa, and then return true only if each element - // in both sets has at least one equivalent. - as := a.AsValueSlice() - bs := b.AsValueSlice() - aeqs := make([]bool, len(as)) - beqs := make([]bool, len(bs)) - for ai, av := range as { - for bi, bv := range bs { - if f(av, bv) { - aeqs[ai] = true - beqs[bi] = true - } - } - } - - var errs []error - for i, eq := range aeqs { - if !eq { - errs = append(errs, path.NewErrorf("planned set element %#v does not correlate with any element in actual", as[i])) - } - } - if len(errs) > 0 { - // Exit early since otherwise we're likely to generate duplicate - // error messages from the other perspective in the subsequent loop. - return errs - } - for i, eq := range beqs { - if !eq { - errs = append(errs, path.NewErrorf("actual set element %#v does not correlate with any element in plan", bs[i])) - } - } - return errs -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/doc.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/doc.go deleted file mode 100644 index 2c18a010..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// Package objchange deals with the business logic of taking a prior state -// value and a config value and producing a proposed new merged value, along -// with other related rules in this domain. -package objchange diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/lcs.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/lcs.go deleted file mode 100644 index cbfefddd..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/lcs.go +++ /dev/null @@ -1,104 +0,0 @@ -package objchange - -import ( - "github.com/zclconf/go-cty/cty" -) - -// LongestCommonSubsequence finds a sequence of values that are common to both -// x and y, with the same relative ordering as in both collections. This result -// is useful as a first step towards computing a diff showing added/removed -// elements in a sequence. -// -// The approached used here is a "naive" one, assuming that both xs and ys will -// generally be small in most reasonable Terraform configurations. For larger -// lists the time/space usage may be sub-optimal. -// -// A pair of lists may have multiple longest common subsequences. In that -// case, the one selected by this function is undefined. -func LongestCommonSubsequence(xs, ys []cty.Value) []cty.Value { - if len(xs) == 0 || len(ys) == 0 { - return make([]cty.Value, 0) - } - - c := make([]int, len(xs)*len(ys)) - eqs := make([]bool, len(xs)*len(ys)) - w := len(xs) - - for y := 0; y < len(ys); y++ { - for x := 0; x < len(xs); x++ { - eqV := xs[x].Equals(ys[y]) - eq := false - if eqV.IsKnown() && eqV.True() { - eq = true - eqs[(w*y)+x] = true // equality tests can be expensive, so cache it - } - if eq { - // Sequence gets one longer than for the cell at top left, - // since we'd append a new item to the sequence here. - if x == 0 || y == 0 { - c[(w*y)+x] = 1 - } else { - c[(w*y)+x] = c[(w*(y-1))+(x-1)] + 1 - } - } else { - // We follow the longest of the sequence above and the sequence - // to the left of us in the matrix. - l := 0 - u := 0 - if x > 0 { - l = c[(w*y)+(x-1)] - } - if y > 0 { - u = c[(w*(y-1))+x] - } - if l > u { - c[(w*y)+x] = l - } else { - c[(w*y)+x] = u - } - } - } - } - - // The bottom right cell tells us how long our longest sequence will be - seq := make([]cty.Value, c[len(c)-1]) - - // Now we will walk back from the bottom right cell, finding again all - // of the equal pairs to construct our sequence. - x := len(xs) - 1 - y := len(ys) - 1 - i := len(seq) - 1 - - for x > -1 && y > -1 { - if eqs[(w*y)+x] { - // Add the value to our result list and then walk diagonally - // up and to the left. - seq[i] = xs[x] - x-- - y-- - i-- - } else { - // Take the path with the greatest sequence length in the matrix. - l := 0 - u := 0 - if x > 0 { - l = c[(w*y)+(x-1)] - } - if y > 0 { - u = c[(w*(y-1))+x] - } - if l > u { - x-- - } else { - y-- - } - } - } - - if i > -1 { - // should never happen if the matrix was constructed properly - panic("not enough elements in sequence") - } - - return seq -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/normalize_obj.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/normalize_obj.go index a8629046..d6aabbe3 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/normalize_obj.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/normalize_obj.go @@ -1,13 +1,14 @@ package objchange import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/zclconf/go-cty/cty" + "github.com/hashicorp/go-cty/cty" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" ) // NormalizeObjectFromLegacySDK takes an object that may have been generated // by the legacy Terraform SDK (i.e. returned from a provider with the -// LegacyTypeSystem opt-out set) and does its best to normalize it for the +// UnsafeToUseLegacyTypeSystem opt-out set) and does its best to normalize it for the // assumptions we would normally enforce if the provider had not opted out. // // In particular, this function guarantees that a value representing a nested diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/normalize_obj_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/normalize_obj_test.go new file mode 100644 index 00000000..a586ca38 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/normalize_obj_test.go @@ -0,0 +1,309 @@ +package objchange + +import ( + "testing" + + "github.com/apparentlymart/go-dump/dump" + "github.com/hashicorp/go-cty/cty" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" +) + +func TestNormalizeObjectFromLegacySDK(t *testing.T) { + tests := map[string]struct { + Schema *configschema.Block + Input cty.Value + Want cty.Value + }{ + "empty": { + &configschema.Block{}, + cty.EmptyObjectVal, + cty.EmptyObjectVal, + }, + "attributes only": { + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "a": {Type: cty.String, Required: true}, + "b": {Type: cty.String, Optional: true}, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("a value"), + "b": cty.StringVal("b value"), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("a value"), + "b": cty.StringVal("b value"), + }), + }, + "null block single": { + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "a": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "b": {Type: cty.String, Optional: true}, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.Object(map[string]cty.Type{ + "b": cty.String, + })), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.Object(map[string]cty.Type{ + "b": cty.String, + })), + }), + }, + "unknown block single": { + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "a": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "b": {Type: cty.String, Optional: true}, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "c": {Nesting: configschema.NestingSingle}, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.UnknownVal(cty.Object(map[string]cty.Type{ + "b": cty.String, + "c": cty.EmptyObject, + })), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "b": cty.UnknownVal(cty.String), + "c": cty.EmptyObjectVal, + }), + }), + }, + "null block list": { + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "a": { + Nesting: configschema.NestingList, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "b": {Type: cty.String, Optional: true}, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "c": {Nesting: configschema.NestingSingle}, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{ + "b": cty.String, + "c": cty.EmptyObject, + }))), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListValEmpty(cty.Object(map[string]cty.Type{ + "b": cty.String, + "c": cty.EmptyObject, + })), + }), + }, + "unknown block list": { + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "a": { + Nesting: configschema.NestingList, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "b": {Type: cty.String, Optional: true}, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.UnknownVal(cty.List(cty.Object(map[string]cty.Type{ + "b": cty.String, + }))), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.UnknownVal(cty.String), + }), + }), + }), + }, + "null block set": { + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "a": { + Nesting: configschema.NestingSet, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "b": {Type: cty.String, Optional: true}, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ + "b": cty.String, + }))), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.SetValEmpty(cty.Object(map[string]cty.Type{ + "b": cty.String, + })), + }), + }, + "unknown block set": { + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "a": { + Nesting: configschema.NestingSet, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "b": {Type: cty.String, Optional: true}, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.UnknownVal(cty.Set(cty.Object(map[string]cty.Type{ + "b": cty.String, + }))), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.UnknownVal(cty.String), + }), + }), + }), + }, + "map block passes through": { + // Legacy SDK doesn't use NestingMap, so we don't do any transforms + // related to it but we still need to verify that map blocks pass + // through unscathed. + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "a": { + Nesting: configschema.NestingMap, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "b": {Type: cty.String, Optional: true}, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "foo": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("b value"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "foo": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("b value"), + }), + }), + }), + }, + "block list with dynamic type": { + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "a": { + Nesting: configschema.NestingList, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "b": {Type: cty.DynamicPseudoType, Optional: true}, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.TupleVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.True, + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.TupleVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.True, + }), + }), + }), + }, + "block map with dynamic type": { + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "a": { + Nesting: configschema.NestingMap, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "b": {Type: cty.DynamicPseudoType, Optional: true}, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "one": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + "another": cty.ObjectVal(map[string]cty.Value{ + "b": cty.True, + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "one": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + "another": cty.ObjectVal(map[string]cty.Value{ + "b": cty.True, + }), + }), + }), + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + got := NormalizeObjectFromLegacySDK(test.Input, test.Schema) + if !got.RawEquals(test.Want) { + t.Errorf( + "wrong result\ngot: %s\nwant: %s", + dump.Value(got), dump.Value(test.Want), + ) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/objchange.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/objchange.go deleted file mode 100644 index 879fc93a..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/objchange.go +++ /dev/null @@ -1,390 +0,0 @@ -package objchange - -import ( - "fmt" - - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" -) - -// ProposedNewObject constructs a proposed new object value by combining the -// computed attribute values from "prior" with the configured attribute values -// from "config". -// -// Both value must conform to the given schema's implied type, or this function -// will panic. -// -// The prior value must be wholly known, but the config value may be unknown -// or have nested unknown values. -// -// The merging of the two objects includes the attributes of any nested blocks, -// which will be correlated in a manner appropriate for their nesting mode. -// Note in particular that the correlation for blocks backed by sets is a -// heuristic based on matching non-computed attribute values and so it may -// produce strange results with more "extreme" cases, such as a nested set -// block where _all_ attributes are computed. -func ProposedNewObject(schema *configschema.Block, prior, config cty.Value) cty.Value { - // If the config and prior are both null, return early here before - // populating the prior block. The prevents non-null blocks from appearing - // the proposed state value. - if config.IsNull() && prior.IsNull() { - return prior - } - - if prior.IsNull() { - // In this case, we will construct a synthetic prior value that is - // similar to the result of decoding an empty configuration block, - // which simplifies our handling of the top-level attributes/blocks - // below by giving us one non-null level of object to pull values from. - prior = AllAttributesNull(schema) - } - return proposedNewObject(schema, prior, config) -} - -// PlannedDataResourceObject is similar to ProposedNewObject but tailored for -// planning data resources in particular. Specifically, it replaces the values -// of any Computed attributes not set in the configuration with an unknown -// value, which serves as a placeholder for a value to be filled in by the -// provider when the data resource is finally read. -// -// Data resources are different because the planning of them is handled -// entirely within Terraform Core and not subject to customization by the -// provider. This function is, in effect, producing an equivalent result to -// passing the ProposedNewObject result into a provider's PlanResourceChange -// function, assuming a fixed implementation of PlanResourceChange that just -// fills in unknown values as needed. -func PlannedDataResourceObject(schema *configschema.Block, config cty.Value) cty.Value { - // Our trick here is to run the ProposedNewObject logic with an - // entirely-unknown prior value. Because of cty's unknown short-circuit - // behavior, any operation on prior returns another unknown, and so - // unknown values propagate into all of the parts of the resulting value - // that would normally be filled in by preserving the prior state. - prior := cty.UnknownVal(schema.ImpliedType()) - return proposedNewObject(schema, prior, config) -} - -func proposedNewObject(schema *configschema.Block, prior, config cty.Value) cty.Value { - if config.IsNull() || !config.IsKnown() { - // This is a weird situation, but we'll allow it anyway to free - // callers from needing to specifically check for these cases. - return prior - } - if (!prior.Type().IsObjectType()) || (!config.Type().IsObjectType()) { - panic("ProposedNewObject only supports object-typed values") - } - - // From this point onwards, we can assume that both values are non-null - // object types, and that the config value itself is known (though it - // may contain nested values that are unknown.) - - newAttrs := map[string]cty.Value{} - for name, attr := range schema.Attributes { - priorV := prior.GetAttr(name) - configV := config.GetAttr(name) - var newV cty.Value - switch { - case attr.Computed && attr.Optional: - // This is the trickiest scenario: we want to keep the prior value - // if the config isn't overriding it. Note that due to some - // ambiguity here, setting an optional+computed attribute from - // config and then later switching the config to null in a - // subsequent change causes the initial config value to be "sticky" - // unless the provider specifically overrides it during its own - // plan customization step. - if configV.IsNull() { - newV = priorV - } else { - newV = configV - } - case attr.Computed: - // configV will always be null in this case, by definition. - // priorV may also be null, but that's okay. - newV = priorV - default: - // For non-computed attributes, we always take the config value, - // even if it is null. If it's _required_ then null values - // should've been caught during an earlier validation step, and - // so we don't really care about that here. - newV = configV - } - newAttrs[name] = newV - } - - // Merging nested blocks is a little more complex, since we need to - // correlate blocks between both objects and then recursively propose - // a new object for each. The correlation logic depends on the nesting - // mode for each block type. - for name, blockType := range schema.BlockTypes { - priorV := prior.GetAttr(name) - configV := config.GetAttr(name) - var newV cty.Value - switch blockType.Nesting { - - case configschema.NestingSingle, configschema.NestingGroup: - newV = ProposedNewObject(&blockType.Block, priorV, configV) - - case configschema.NestingList: - // Nested blocks are correlated by index. - configVLen := 0 - if configV.IsKnown() && !configV.IsNull() { - configVLen = configV.LengthInt() - } - if configVLen > 0 { - newVals := make([]cty.Value, 0, configVLen) - for it := configV.ElementIterator(); it.Next(); { - idx, configEV := it.Element() - if priorV.IsKnown() && (priorV.IsNull() || !priorV.HasIndex(idx).True()) { - // If there is no corresponding prior element then - // we just take the config value as-is. - newVals = append(newVals, configEV) - continue - } - priorEV := priorV.Index(idx) - - newEV := ProposedNewObject(&blockType.Block, priorEV, configEV) - newVals = append(newVals, newEV) - } - // Despite the name, a NestingList might also be a tuple, if - // its nested schema contains dynamically-typed attributes. - if configV.Type().IsTupleType() { - newV = cty.TupleVal(newVals) - } else { - newV = cty.ListVal(newVals) - } - } else { - // Despite the name, a NestingList might also be a tuple, if - // its nested schema contains dynamically-typed attributes. - if configV.Type().IsTupleType() { - newV = cty.EmptyTupleVal - } else { - newV = cty.ListValEmpty(blockType.ImpliedType()) - } - } - - case configschema.NestingMap: - // Despite the name, a NestingMap may produce either a map or - // object value, depending on whether the nested schema contains - // dynamically-typed attributes. - if configV.Type().IsObjectType() { - // Nested blocks are correlated by key. - configVLen := 0 - if configV.IsKnown() && !configV.IsNull() { - configVLen = configV.LengthInt() - } - if configVLen > 0 { - newVals := make(map[string]cty.Value, configVLen) - atys := configV.Type().AttributeTypes() - for name := range atys { - configEV := configV.GetAttr(name) - if !priorV.IsKnown() || priorV.IsNull() || !priorV.Type().HasAttribute(name) { - // If there is no corresponding prior element then - // we just take the config value as-is. - newVals[name] = configEV - continue - } - priorEV := priorV.GetAttr(name) - - newEV := ProposedNewObject(&blockType.Block, priorEV, configEV) - newVals[name] = newEV - } - // Although we call the nesting mode "map", we actually use - // object values so that elements might have different types - // in case of dynamically-typed attributes. - newV = cty.ObjectVal(newVals) - } else { - newV = cty.EmptyObjectVal - } - } else { - configVLen := 0 - if configV.IsKnown() && !configV.IsNull() { - configVLen = configV.LengthInt() - } - if configVLen > 0 { - newVals := make(map[string]cty.Value, configVLen) - for it := configV.ElementIterator(); it.Next(); { - idx, configEV := it.Element() - k := idx.AsString() - if priorV.IsKnown() && (priorV.IsNull() || !priorV.HasIndex(idx).True()) { - // If there is no corresponding prior element then - // we just take the config value as-is. - newVals[k] = configEV - continue - } - priorEV := priorV.Index(idx) - - newEV := ProposedNewObject(&blockType.Block, priorEV, configEV) - newVals[k] = newEV - } - newV = cty.MapVal(newVals) - } else { - newV = cty.MapValEmpty(blockType.ImpliedType()) - } - } - - case configschema.NestingSet: - if !configV.Type().IsSetType() { - panic("configschema.NestingSet value is not a set as expected") - } - - // Nested blocks are correlated by comparing the element values - // after eliminating all of the computed attributes. In practice, - // this means that any config change produces an entirely new - // nested object, and we only propagate prior computed values - // if the non-computed attribute values are identical. - var cmpVals [][2]cty.Value - if priorV.IsKnown() && !priorV.IsNull() { - cmpVals = setElementCompareValues(&blockType.Block, priorV, false) - } - configVLen := 0 - if configV.IsKnown() && !configV.IsNull() { - configVLen = configV.LengthInt() - } - if configVLen > 0 { - used := make([]bool, len(cmpVals)) // track used elements in case multiple have the same compare value - newVals := make([]cty.Value, 0, configVLen) - for it := configV.ElementIterator(); it.Next(); { - _, configEV := it.Element() - var priorEV cty.Value - for i, cmp := range cmpVals { - if used[i] { - continue - } - if cmp[1].RawEquals(configEV) { - priorEV = cmp[0] - used[i] = true // we can't use this value on a future iteration - break - } - } - if priorEV == cty.NilVal { - priorEV = cty.NullVal(blockType.ImpliedType()) - } - - newEV := ProposedNewObject(&blockType.Block, priorEV, configEV) - newVals = append(newVals, newEV) - } - newV = cty.SetVal(newVals) - } else { - newV = cty.SetValEmpty(blockType.Block.ImpliedType()) - } - - default: - // Should never happen, since the above cases are comprehensive. - panic(fmt.Sprintf("unsupported block nesting mode %s", blockType.Nesting)) - } - - newAttrs[name] = newV - } - - return cty.ObjectVal(newAttrs) -} - -// setElementCompareValues takes a known, non-null value of a cty.Set type and -// returns a table -- constructed of two-element arrays -- that maps original -// set element values to corresponding values that have all of the computed -// values removed, making them suitable for comparison with values obtained -// from configuration. The element type of the set must conform to the implied -// type of the given schema, or this function will panic. -// -// In the resulting slice, the zeroth element of each array is the original -// value and the one-indexed element is the corresponding "compare value". -// -// This is intended to help correlate prior elements with configured elements -// in ProposedNewObject. The result is a heuristic rather than an exact science, -// since e.g. two separate elements may reduce to the same value through this -// process. The caller must therefore be ready to deal with duplicates. -func setElementCompareValues(schema *configschema.Block, set cty.Value, isConfig bool) [][2]cty.Value { - ret := make([][2]cty.Value, 0, set.LengthInt()) - for it := set.ElementIterator(); it.Next(); { - _, ev := it.Element() - ret = append(ret, [2]cty.Value{ev, setElementCompareValue(schema, ev, isConfig)}) - } - return ret -} - -// setElementCompareValue creates a new value that has all of the same -// non-computed attribute values as the one given but has all computed -// attribute values forced to null. -// -// If isConfig is true then non-null Optional+Computed attribute values will -// be preserved. Otherwise, they will also be set to null. -// -// The input value must conform to the schema's implied type, and the return -// value is guaranteed to conform to it. -func setElementCompareValue(schema *configschema.Block, v cty.Value, isConfig bool) cty.Value { - if v.IsNull() || !v.IsKnown() { - return v - } - - attrs := map[string]cty.Value{} - for name, attr := range schema.Attributes { - switch { - case attr.Computed && attr.Optional: - if isConfig { - attrs[name] = v.GetAttr(name) - } else { - attrs[name] = cty.NullVal(attr.Type) - } - case attr.Computed: - attrs[name] = cty.NullVal(attr.Type) - default: - attrs[name] = v.GetAttr(name) - } - } - - for name, blockType := range schema.BlockTypes { - switch blockType.Nesting { - - case configschema.NestingSingle, configschema.NestingGroup: - attrs[name] = setElementCompareValue(&blockType.Block, v.GetAttr(name), isConfig) - - case configschema.NestingList, configschema.NestingSet: - cv := v.GetAttr(name) - if cv.IsNull() || !cv.IsKnown() { - attrs[name] = cv - continue - } - if l := cv.LengthInt(); l > 0 { - elems := make([]cty.Value, 0, l) - for it := cv.ElementIterator(); it.Next(); { - _, ev := it.Element() - elems = append(elems, setElementCompareValue(&blockType.Block, ev, isConfig)) - } - if blockType.Nesting == configschema.NestingSet { - // SetValEmpty would panic if given elements that are not - // all of the same type, but that's guaranteed not to - // happen here because our input value was _already_ a - // set and we've not changed the types of any elements here. - attrs[name] = cty.SetVal(elems) - } else { - attrs[name] = cty.TupleVal(elems) - } - } else { - if blockType.Nesting == configschema.NestingSet { - attrs[name] = cty.SetValEmpty(blockType.Block.ImpliedType()) - } else { - attrs[name] = cty.EmptyTupleVal - } - } - - case configschema.NestingMap: - cv := v.GetAttr(name) - if cv.IsNull() || !cv.IsKnown() { - attrs[name] = cv - continue - } - elems := make(map[string]cty.Value) - for it := cv.ElementIterator(); it.Next(); { - kv, ev := it.Element() - elems[kv.AsString()] = setElementCompareValue(&blockType.Block, ev, isConfig) - } - attrs[name] = cty.ObjectVal(elems) - - default: - // Should never happen, since the above cases are comprehensive. - panic(fmt.Sprintf("unsupported block nesting mode %s", blockType.Nesting)) - } - } - - return cty.ObjectVal(attrs) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/plan_valid.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/plan_valid.go deleted file mode 100644 index 905a9114..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange/plan_valid.go +++ /dev/null @@ -1,267 +0,0 @@ -package objchange - -import ( - "fmt" - - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" -) - -// AssertPlanValid checks checks whether a planned new state returned by a -// provider's PlanResourceChange method is suitable to achieve a change -// from priorState to config. It returns a slice with nonzero length if -// any problems are detected. Because problems here indicate bugs in the -// provider that generated the plannedState, they are written with provider -// developers as an audience, rather than end-users. -// -// All of the given values must have the same type and must conform to the -// implied type of the given schema, or this function may panic or produce -// garbage results. -// -// During planning, a provider may only make changes to attributes that are -// null (unset) in the configuration and are marked as "computed" in the -// resource type schema, in order to insert any default values the provider -// may know about. If the default value cannot be determined until apply time, -// the provider can return an unknown value. Providers are forbidden from -// planning a change that disagrees with any non-null argument in the -// configuration. -// -// As a special exception, providers _are_ allowed to provide attribute values -// conflicting with configuration if and only if the planned value exactly -// matches the corresponding attribute value in the prior state. The provider -// can use this to signal that the new value is functionally equivalent to -// the old and thus no change is required. -func AssertPlanValid(schema *configschema.Block, priorState, config, plannedState cty.Value) []error { - return assertPlanValid(schema, priorState, config, plannedState, nil) -} - -func assertPlanValid(schema *configschema.Block, priorState, config, plannedState cty.Value, path cty.Path) []error { - var errs []error - if plannedState.IsNull() && !config.IsNull() { - errs = append(errs, path.NewErrorf("planned for absense but config wants existence")) - return errs - } - if config.IsNull() && !plannedState.IsNull() { - errs = append(errs, path.NewErrorf("planned for existence but config wants absense")) - return errs - } - if plannedState.IsNull() { - // No further checks possible if the planned value is null - return errs - } - - impTy := schema.ImpliedType() - - for name, attrS := range schema.Attributes { - plannedV := plannedState.GetAttr(name) - configV := config.GetAttr(name) - priorV := cty.NullVal(attrS.Type) - if !priorState.IsNull() { - priorV = priorState.GetAttr(name) - } - - path := append(path, cty.GetAttrStep{Name: name}) - moreErrs := assertPlannedValueValid(attrS, priorV, configV, plannedV, path) - errs = append(errs, moreErrs...) - } - for name, blockS := range schema.BlockTypes { - path := append(path, cty.GetAttrStep{Name: name}) - plannedV := plannedState.GetAttr(name) - configV := config.GetAttr(name) - priorV := cty.NullVal(impTy.AttributeType(name)) - if !priorState.IsNull() { - priorV = priorState.GetAttr(name) - } - if plannedV.RawEquals(configV) { - // Easy path: nothing has changed at all - continue - } - if !plannedV.IsKnown() { - errs = append(errs, path.NewErrorf("attribute representing nested block must not be unknown itself; set nested attribute values to unknown instead")) - continue - } - - switch blockS.Nesting { - case configschema.NestingSingle, configschema.NestingGroup: - moreErrs := assertPlanValid(&blockS.Block, priorV, configV, plannedV, path) - errs = append(errs, moreErrs...) - case configschema.NestingList: - // A NestingList might either be a list or a tuple, depending on - // whether there are dynamically-typed attributes inside. However, - // both support a similar-enough API that we can treat them the - // same for our purposes here. - if plannedV.IsNull() { - errs = append(errs, path.NewErrorf("attribute representing a list of nested blocks must be empty to indicate no blocks, not null")) - continue - } - - plannedL := plannedV.LengthInt() - configL := configV.LengthInt() - if plannedL != configL { - errs = append(errs, path.NewErrorf("block count in plan (%d) disagrees with count in config (%d)", plannedL, configL)) - continue - } - for it := plannedV.ElementIterator(); it.Next(); { - idx, plannedEV := it.Element() - path := append(path, cty.IndexStep{Key: idx}) - if !plannedEV.IsKnown() { - errs = append(errs, path.NewErrorf("element representing nested block must not be unknown itself; set nested attribute values to unknown instead")) - continue - } - if !configV.HasIndex(idx).True() { - continue // should never happen since we checked the lengths above - } - configEV := configV.Index(idx) - priorEV := cty.NullVal(blockS.ImpliedType()) - if !priorV.IsNull() && priorV.HasIndex(idx).True() { - priorEV = priorV.Index(idx) - } - - moreErrs := assertPlanValid(&blockS.Block, priorEV, configEV, plannedEV, path) - errs = append(errs, moreErrs...) - } - case configschema.NestingMap: - if plannedV.IsNull() { - errs = append(errs, path.NewErrorf("attribute representing a map of nested blocks must be empty to indicate no blocks, not null")) - continue - } - - // A NestingMap might either be a map or an object, depending on - // whether there are dynamically-typed attributes inside, but - // that's decided statically and so all values will have the same - // kind. - if plannedV.Type().IsObjectType() { - plannedAtys := plannedV.Type().AttributeTypes() - configAtys := configV.Type().AttributeTypes() - for k := range plannedAtys { - if _, ok := configAtys[k]; !ok { - errs = append(errs, path.NewErrorf("block key %q from plan is not present in config", k)) - continue - } - path := append(path, cty.GetAttrStep{Name: k}) - - plannedEV := plannedV.GetAttr(k) - if !plannedEV.IsKnown() { - errs = append(errs, path.NewErrorf("element representing nested block must not be unknown itself; set nested attribute values to unknown instead")) - continue - } - configEV := configV.GetAttr(k) - priorEV := cty.NullVal(blockS.ImpliedType()) - if !priorV.IsNull() && priorV.Type().HasAttribute(k) { - priorEV = priorV.GetAttr(k) - } - moreErrs := assertPlanValid(&blockS.Block, priorEV, configEV, plannedEV, path) - errs = append(errs, moreErrs...) - } - for k := range configAtys { - if _, ok := plannedAtys[k]; !ok { - errs = append(errs, path.NewErrorf("block key %q from config is not present in plan", k)) - continue - } - } - } else { - plannedL := plannedV.LengthInt() - configL := configV.LengthInt() - if plannedL != configL { - errs = append(errs, path.NewErrorf("block count in plan (%d) disagrees with count in config (%d)", plannedL, configL)) - continue - } - for it := plannedV.ElementIterator(); it.Next(); { - idx, plannedEV := it.Element() - path := append(path, cty.IndexStep{Key: idx}) - if !plannedEV.IsKnown() { - errs = append(errs, path.NewErrorf("element representing nested block must not be unknown itself; set nested attribute values to unknown instead")) - continue - } - k := idx.AsString() - if !configV.HasIndex(idx).True() { - errs = append(errs, path.NewErrorf("block key %q from plan is not present in config", k)) - continue - } - configEV := configV.Index(idx) - priorEV := cty.NullVal(blockS.ImpliedType()) - if !priorV.IsNull() && priorV.HasIndex(idx).True() { - priorEV = priorV.Index(idx) - } - moreErrs := assertPlanValid(&blockS.Block, priorEV, configEV, plannedEV, path) - errs = append(errs, moreErrs...) - } - for it := configV.ElementIterator(); it.Next(); { - idx, _ := it.Element() - if !plannedV.HasIndex(idx).True() { - errs = append(errs, path.NewErrorf("block key %q from config is not present in plan", idx.AsString())) - continue - } - } - } - case configschema.NestingSet: - if plannedV.IsNull() { - errs = append(errs, path.NewErrorf("attribute representing a set of nested blocks must be empty to indicate no blocks, not null")) - continue - } - - // Because set elements have no identifier with which to correlate - // them, we can't robustly validate the plan for a nested block - // backed by a set, and so unfortunately we need to just trust the - // provider to do the right thing. :( - // - // (In principle we could correlate elements by matching the - // subset of attributes explicitly set in config, except for the - // special diff suppression rule which allows for there to be a - // planned value that is constructed by mixing part of a prior - // value with part of a config value, creating an entirely new - // element that is not present in either prior nor config.) - for it := plannedV.ElementIterator(); it.Next(); { - idx, plannedEV := it.Element() - path := append(path, cty.IndexStep{Key: idx}) - if !plannedEV.IsKnown() { - errs = append(errs, path.NewErrorf("element representing nested block must not be unknown itself; set nested attribute values to unknown instead")) - continue - } - } - - default: - panic(fmt.Sprintf("unsupported nesting mode %s", blockS.Nesting)) - } - } - - return errs -} - -func assertPlannedValueValid(attrS *configschema.Attribute, priorV, configV, plannedV cty.Value, path cty.Path) []error { - var errs []error - if plannedV.RawEquals(configV) { - // This is the easy path: provider didn't change anything at all. - return errs - } - if plannedV.RawEquals(priorV) && !priorV.IsNull() { - // Also pretty easy: there is a prior value and the provider has - // returned it unchanged. This indicates that configV and plannedV - // are functionally equivalent and so the provider wishes to disregard - // the configuration value in favor of the prior. - return errs - } - if attrS.Computed && configV.IsNull() { - // The provider is allowed to change the value of any computed - // attribute that isn't explicitly set in the config. - return errs - } - - // If none of the above conditions match, the provider has made an invalid - // change to this attribute. - if priorV.IsNull() { - if attrS.Sensitive { - errs = append(errs, path.NewErrorf("sensitive planned value does not match config value")) - } else { - errs = append(errs, path.NewErrorf("planned value %#v does not match config value %#v", plannedV, configV)) - } - return errs - } - if attrS.Sensitive { - errs = append(errs, path.NewErrorf("sensitive planned value does not match config value nor prior value")) - } else { - errs = append(errs, path.NewErrorf("planned value %#v does not match config value %#v nor prior value %#v", plannedV, configV, priorV)) - } - return errs -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/plan.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/plan.go deleted file mode 100644 index 0abed56a..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plans/plan.go +++ /dev/null @@ -1,92 +0,0 @@ -package plans - -import ( - "sort" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/zclconf/go-cty/cty" -) - -// Plan is the top-level type representing a planned set of changes. -// -// A plan is a summary of the set of changes required to move from a current -// state to a goal state derived from configuration. The described changes -// are not applied directly, but contain an approximation of the final -// result that will be completed during apply by resolving any values that -// cannot be predicted. -// -// A plan must always be accompanied by the state and configuration it was -// built from, since the plan does not itself include all of the information -// required to make the changes indicated. -type Plan struct { - VariableValues map[string]DynamicValue - Changes *Changes - TargetAddrs []addrs.Targetable - ProviderSHA256s map[string][]byte - Backend Backend -} - -// Backend represents the backend-related configuration and other data as it -// existed when a plan was created. -type Backend struct { - // Type is the type of backend that the plan will apply against. - Type string - - // Config is the configuration of the backend, whose schema is decided by - // the backend Type. - Config DynamicValue - - // Workspace is the name of the workspace that was active when the plan - // was created. It is illegal to apply a plan created for one workspace - // to the state of another workspace. - // (This constraint is already enforced by the statefile lineage mechanism, - // but storing this explicitly allows us to return a better error message - // in the situation where the user has the wrong workspace selected.) - Workspace string -} - -func NewBackend(typeName string, config cty.Value, configSchema *configschema.Block, workspaceName string) (*Backend, error) { - dv, err := NewDynamicValue(config, configSchema.ImpliedType()) - if err != nil { - return nil, err - } - - return &Backend{ - Type: typeName, - Config: dv, - Workspace: workspaceName, - }, nil -} - -// ProviderAddrs returns a list of all of the provider configuration addresses -// referenced throughout the receiving plan. -// -// The result is de-duplicated so that each distinct address appears only once. -func (p *Plan) ProviderAddrs() []addrs.AbsProviderConfig { - if p == nil || p.Changes == nil { - return nil - } - - m := map[string]addrs.AbsProviderConfig{} - for _, rc := range p.Changes.Resources { - m[rc.ProviderAddr.String()] = rc.ProviderAddr - } - if len(m) == 0 { - return nil - } - - // This is mainly just so we'll get stable results for testing purposes. - keys := make([]string, 0, len(m)) - for k := range m { - keys = append(keys, k) - } - sort.Strings(keys) - - ret := make([]addrs.AbsProviderConfig, len(keys)) - for i, key := range keys { - ret[i] = m[key] - } - - return ret -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/convert/diagnostics.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/convert/diagnostics.go index f20f0507..babd0ecf 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/convert/diagnostics.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/convert/diagnostics.go @@ -1,126 +1,129 @@ package convert import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - proto "github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5" - "github.com/zclconf/go-cty/cty" -) + "fmt" -// WarnsAndErrorsToProto converts the warnings and errors return by the legacy -// provider to protobuf diagnostics. -func WarnsAndErrsToProto(warns []string, errs []error) (diags []*proto.Diagnostic) { - for _, w := range warns { - diags = AppendProtoDiag(diags, w) - } + "github.com/hashicorp/go-cty/cty" - for _, e := range errs { - diags = AppendProtoDiag(diags, e) - } - - return diags -} + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" +) // AppendProtoDiag appends a new diagnostic from a warning string or an error. // This panics if d is not a string or error. -func AppendProtoDiag(diags []*proto.Diagnostic, d interface{}) []*proto.Diagnostic { +func AppendProtoDiag(diags []*tfprotov5.Diagnostic, d interface{}) []*tfprotov5.Diagnostic { switch d := d.(type) { case cty.PathError: ap := PathToAttributePath(d.Path) - diags = append(diags, &proto.Diagnostic{ - Severity: proto.Diagnostic_ERROR, + diags = append(diags, &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityError, Summary: d.Error(), Attribute: ap, }) + case diag.Diagnostics: + diags = append(diags, DiagsToProto(d)...) case error: - diags = append(diags, &proto.Diagnostic{ - Severity: proto.Diagnostic_ERROR, + diags = append(diags, &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityError, Summary: d.Error(), }) case string: - diags = append(diags, &proto.Diagnostic{ - Severity: proto.Diagnostic_WARNING, + diags = append(diags, &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityWarning, Summary: d, }) - case *proto.Diagnostic: + case *tfprotov5.Diagnostic: diags = append(diags, d) - case []*proto.Diagnostic: + case []*tfprotov5.Diagnostic: diags = append(diags, d...) } return diags } -// ProtoToDiagnostics converts a list of proto.Diagnostics to a tf.Diagnostics. -func ProtoToDiagnostics(ds []*proto.Diagnostic) tfdiags.Diagnostics { - var diags tfdiags.Diagnostics +// ProtoToDiags converts a list of tfprotov5.Diagnostics to a diag.Diagnostics. +func ProtoToDiags(ds []*tfprotov5.Diagnostic) diag.Diagnostics { + var diags diag.Diagnostics for _, d := range ds { - var severity tfdiags.Severity + var severity diag.Severity switch d.Severity { - case proto.Diagnostic_ERROR: - severity = tfdiags.Error - case proto.Diagnostic_WARNING: - severity = tfdiags.Warning - } - - var newDiag tfdiags.Diagnostic - - // if there's an attribute path, we need to create a AttributeValue diagnostic - if d.Attribute != nil { - path := AttributePathToPath(d.Attribute) - newDiag = tfdiags.AttributeValue(severity, d.Summary, d.Detail, path) - } else { - newDiag = tfdiags.WholeContainingBody(severity, d.Summary, d.Detail) + case tfprotov5.DiagnosticSeverityError: + severity = diag.Error + case tfprotov5.DiagnosticSeverityWarning: + severity = diag.Warning } - diags = diags.Append(newDiag) + diags = append(diags, diag.Diagnostic{ + Severity: severity, + Summary: d.Summary, + Detail: d.Detail, + AttributePath: AttributePathToPath(d.Attribute), + }) } return diags } +func DiagsToProto(diags diag.Diagnostics) []*tfprotov5.Diagnostic { + var ds []*tfprotov5.Diagnostic + for _, d := range diags { + if err := d.Validate(); err != nil { + panic(fmt.Errorf("Invalid diagnostic: %s. This is always a bug in the provider implementation", err)) + } + protoDiag := &tfprotov5.Diagnostic{ + Summary: d.Summary, + Detail: d.Detail, + Attribute: PathToAttributePath(d.AttributePath), + } + if d.Severity == diag.Error { + protoDiag.Severity = tfprotov5.DiagnosticSeverityError + } else if d.Severity == diag.Warning { + protoDiag.Severity = tfprotov5.DiagnosticSeverityWarning + } + ds = append(ds, protoDiag) + } + return ds +} + // AttributePathToPath takes the proto encoded path and converts it to a cty.Path -func AttributePathToPath(ap *proto.AttributePath) cty.Path { +func AttributePathToPath(ap *tftypes.AttributePath) cty.Path { var p cty.Path - for _, step := range ap.Steps { - switch selector := step.Selector.(type) { - case *proto.AttributePath_Step_AttributeName: - p = p.GetAttr(selector.AttributeName) - case *proto.AttributePath_Step_ElementKeyString: - p = p.Index(cty.StringVal(selector.ElementKeyString)) - case *proto.AttributePath_Step_ElementKeyInt: - p = p.Index(cty.NumberIntVal(selector.ElementKeyInt)) + if ap == nil { + return p + } + for _, step := range ap.Steps() { + switch step.(type) { + case tftypes.AttributeName: + p = p.GetAttr(string(step.(tftypes.AttributeName))) + case tftypes.ElementKeyString: + p = p.Index(cty.StringVal(string(step.(tftypes.ElementKeyString)))) + case tftypes.ElementKeyInt: + p = p.Index(cty.NumberIntVal(int64(step.(tftypes.ElementKeyInt)))) } } return p } -// AttributePathToPath takes a cty.Path and converts it to a proto-encoded path. -func PathToAttributePath(p cty.Path) *proto.AttributePath { - ap := &proto.AttributePath{} +// PathToAttributePath takes a cty.Path and converts it to a proto-encoded path. +func PathToAttributePath(p cty.Path) *tftypes.AttributePath { + if p == nil || len(p) < 1 { + return nil + } + ap := tftypes.NewAttributePath() for _, step := range p { switch selector := step.(type) { case cty.GetAttrStep: - ap.Steps = append(ap.Steps, &proto.AttributePath_Step{ - Selector: &proto.AttributePath_Step_AttributeName{ - AttributeName: selector.Name, - }, - }) + ap = ap.WithAttributeName(selector.Name) + case cty.IndexStep: key := selector.Key switch key.Type() { case cty.String: - ap.Steps = append(ap.Steps, &proto.AttributePath_Step{ - Selector: &proto.AttributePath_Step_ElementKeyString{ - ElementKeyString: key.AsString(), - }, - }) + ap = ap.WithElementKeyString(key.AsString()) case cty.Number: v, _ := key.AsBigFloat().Int64() - ap.Steps = append(ap.Steps, &proto.AttributePath_Step{ - Selector: &proto.AttributePath_Step_ElementKeyInt{ - ElementKeyInt: v, - }, - }) + ap = ap.WithElementKeyInt(v) default: // We'll bail early if we encounter anything else, and just // return the valid prefix. diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/convert/diagnostics_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/convert/diagnostics_test.go new file mode 100644 index 00000000..ddf8de79 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/convert/diagnostics_test.go @@ -0,0 +1,292 @@ +package convert + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/go-cty/cty" + + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" +) + +func TestDiagnostics(t *testing.T) { + type diagFlat struct { + Severity diag.Severity + Attr []interface{} + Summary string + Detail string + } + + tests := map[string]struct { + Cons func([]*tfprotov5.Diagnostic) []*tfprotov5.Diagnostic + Want []diagFlat + }{ + "nil": { + func(diags []*tfprotov5.Diagnostic) []*tfprotov5.Diagnostic { + return diags + }, + nil, + }, + "error": { + func(diags []*tfprotov5.Diagnostic) []*tfprotov5.Diagnostic { + return append(diags, &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "simple error", + }) + }, + []diagFlat{ + { + Severity: diag.Error, + Summary: "simple error", + }, + }, + }, + "detailed error": { + func(diags []*tfprotov5.Diagnostic) []*tfprotov5.Diagnostic { + return append(diags, &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "simple error", + Detail: "detailed error", + }) + }, + []diagFlat{ + { + Severity: diag.Error, + Summary: "simple error", + Detail: "detailed error", + }, + }, + }, + "warning": { + func(diags []*tfprotov5.Diagnostic) []*tfprotov5.Diagnostic { + return append(diags, &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityWarning, + Summary: "simple warning", + }) + }, + []diagFlat{ + { + Severity: diag.Warning, + Summary: "simple warning", + }, + }, + }, + "detailed warning": { + func(diags []*tfprotov5.Diagnostic) []*tfprotov5.Diagnostic { + return append(diags, &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityWarning, + Summary: "simple warning", + Detail: "detailed warning", + }) + }, + []diagFlat{ + { + Severity: diag.Warning, + Summary: "simple warning", + Detail: "detailed warning", + }, + }, + }, + "multi error": { + func(diags []*tfprotov5.Diagnostic) []*tfprotov5.Diagnostic { + diags = append(diags, &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "first error", + }, &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "second error", + }) + return diags + }, + []diagFlat{ + { + Severity: diag.Error, + Summary: "first error", + }, + { + Severity: diag.Error, + Summary: "second error", + }, + }, + }, + "warning and error": { + func(diags []*tfprotov5.Diagnostic) []*tfprotov5.Diagnostic { + diags = append(diags, &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityWarning, + Summary: "warning", + }, &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "error", + }) + return diags + }, + []diagFlat{ + { + Severity: diag.Warning, + Summary: "warning", + }, + { + Severity: diag.Error, + Summary: "error", + }, + }, + }, + "attr error": { + func(diags []*tfprotov5.Diagnostic) []*tfprotov5.Diagnostic { + diags = append(diags, &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "error", + Detail: "error detail", + Attribute: tftypes.NewAttributePathWithSteps([]tftypes.AttributePathStep{ + tftypes.AttributeName("attribute_name"), + }), + }) + return diags + }, + []diagFlat{ + { + Severity: diag.Error, + Summary: "error", + Detail: "error detail", + Attr: []interface{}{"attribute_name"}, + }, + }, + }, + "multi attr": { + func(diags []*tfprotov5.Diagnostic) []*tfprotov5.Diagnostic { + diags = append(diags, + &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "error 1", + Detail: "error 1 detail", + Attribute: tftypes.NewAttributePathWithSteps([]tftypes.AttributePathStep{ + tftypes.AttributeName("attr"), + }), + }, + &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "error 2", + Detail: "error 2 detail", + Attribute: tftypes.NewAttributePathWithSteps([]tftypes.AttributePathStep{ + tftypes.AttributeName("attr"), + tftypes.AttributeName("sub"), + }), + }, + &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityWarning, + Summary: "warning", + Detail: "warning detail", + Attribute: tftypes.NewAttributePathWithSteps([]tftypes.AttributePathStep{ + tftypes.AttributeName("attr"), + tftypes.ElementKeyInt(1), + tftypes.AttributeName("sub"), + }), + }, + &tfprotov5.Diagnostic{ + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "error 3", + Detail: "error 3 detail", + Attribute: tftypes.NewAttributePathWithSteps([]tftypes.AttributePathStep{ + tftypes.AttributeName("attr"), + tftypes.ElementKeyString("idx"), + tftypes.AttributeName("sub"), + }), + }, + ) + + return diags + }, + []diagFlat{ + { + Severity: diag.Error, + Summary: "error 1", + Detail: "error 1 detail", + Attr: []interface{}{"attr"}, + }, + { + Severity: diag.Error, + Summary: "error 2", + Detail: "error 2 detail", + Attr: []interface{}{"attr", "sub"}, + }, + { + Severity: diag.Warning, + Summary: "warning", + Detail: "warning detail", + Attr: []interface{}{"attr", 1, "sub"}, + }, + { + Severity: diag.Error, + Summary: "error 3", + Detail: "error 3 detail", + Attr: []interface{}{"attr", "idx", "sub"}, + }, + }, + }, + } + + flattenDiags := func(ds diag.Diagnostics) []diagFlat { + var flat []diagFlat + for _, item := range ds { + + var attr []interface{} + + for _, a := range item.AttributePath { + switch step := a.(type) { + case cty.GetAttrStep: + attr = append(attr, step.Name) + case cty.IndexStep: + switch step.Key.Type() { + case cty.Number: + i, _ := step.Key.AsBigFloat().Int64() + attr = append(attr, int(i)) + case cty.String: + attr = append(attr, step.Key.AsString()) + } + } + } + + flat = append(flat, diagFlat{ + Severity: item.Severity, + Attr: attr, + Summary: item.Summary, + Detail: item.Detail, + }) + } + return flat + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + diags := ProtoToDiags(tc.Cons(nil)) + + flat := flattenDiags(diags) + + if !cmp.Equal(flat, tc.Want, typeComparer, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(flat, tc.Want, typeComparer, valueComparer, equateEmpty)) + } + }) + } +} + +func TestPathToAttributePath(t *testing.T) { + tests := map[string]struct { + path cty.Path + want *tftypes.AttributePath + }{ + "no steps": { + path: cty.Path{}, + want: nil, + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + got := PathToAttributePath(tc.path) + if diff := cmp.Diff(got, tc.want); diff != "" { + t.Errorf("Unexpected diff: %s", diff) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/convert/schema.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/convert/schema.go index 105c32c6..310dc638 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/convert/schema.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/convert/schema.go @@ -1,38 +1,160 @@ package convert import ( - "encoding/json" + "fmt" + "log" "reflect" "sort" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - proto "github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5" + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" ) +func tftypeFromCtyType(in cty.Type) (tftypes.Type, error) { + switch { + case in.Equals(cty.String): + return tftypes.String, nil + case in.Equals(cty.Number): + return tftypes.Number, nil + case in.Equals(cty.Bool): + return tftypes.Bool, nil + case in.Equals(cty.DynamicPseudoType): + return tftypes.DynamicPseudoType, nil + case in.IsSetType(): + elemType, err := tftypeFromCtyType(in.ElementType()) + if err != nil { + return nil, err + } + return tftypes.Set{ + ElementType: elemType, + }, nil + case in.IsListType(): + elemType, err := tftypeFromCtyType(in.ElementType()) + if err != nil { + return nil, err + } + return tftypes.List{ + ElementType: elemType, + }, nil + case in.IsTupleType(): + elemTypes := make([]tftypes.Type, 0, in.Length()) + for _, typ := range in.TupleElementTypes() { + elemType, err := tftypeFromCtyType(typ) + if err != nil { + return nil, err + } + elemTypes = append(elemTypes, elemType) + } + return tftypes.Tuple{ + ElementTypes: elemTypes, + }, nil + case in.IsMapType(): + elemType, err := tftypeFromCtyType(in.ElementType()) + if err != nil { + return nil, err + } + return tftypes.Map{ + AttributeType: elemType, + }, nil + case in.IsObjectType(): + attrTypes := make(map[string]tftypes.Type) + for key, typ := range in.AttributeTypes() { + attrType, err := tftypeFromCtyType(typ) + if err != nil { + return nil, err + } + attrTypes[key] = attrType + } + return tftypes.Object{ + AttributeTypes: attrTypes, + }, nil + } + return nil, fmt.Errorf("unknown cty type %s", in.GoString()) +} + +func ctyTypeFromTFType(in tftypes.Type) (cty.Type, error) { + switch { + case in.Is(tftypes.String): + return cty.String, nil + case in.Is(tftypes.Bool): + return cty.Bool, nil + case in.Is(tftypes.Number): + return cty.Number, nil + case in.Is(tftypes.DynamicPseudoType): + return cty.DynamicPseudoType, nil + case in.Is(tftypes.List{}): + elemType, err := ctyTypeFromTFType(in.(tftypes.List).ElementType) + if err != nil { + return cty.Type{}, err + } + return cty.List(elemType), nil + case in.Is(tftypes.Set{}): + elemType, err := ctyTypeFromTFType(in.(tftypes.Set).ElementType) + if err != nil { + return cty.Type{}, err + } + return cty.Set(elemType), nil + case in.Is(tftypes.Map{}): + elemType, err := ctyTypeFromTFType(in.(tftypes.Map).AttributeType) + if err != nil { + return cty.Type{}, err + } + return cty.Map(elemType), nil + case in.Is(tftypes.Tuple{}): + elemTypes := make([]cty.Type, 0, len(in.(tftypes.Tuple).ElementTypes)) + for _, typ := range in.(tftypes.Tuple).ElementTypes { + elemType, err := ctyTypeFromTFType(typ) + if err != nil { + return cty.Type{}, err + } + elemTypes = append(elemTypes, elemType) + } + return cty.Tuple(elemTypes), nil + case in.Is(tftypes.Object{}): + attrTypes := make(map[string]cty.Type, len(in.(tftypes.Object).AttributeTypes)) + for k, v := range in.(tftypes.Object).AttributeTypes { + attrType, err := ctyTypeFromTFType(v) + if err != nil { + return cty.Type{}, err + } + attrTypes[k] = attrType + } + return cty.Object(attrTypes), nil + } + return cty.Type{}, fmt.Errorf("unknown tftypes.Type %s", in) +} + // ConfigSchemaToProto takes a *configschema.Block and converts it to a -// proto.Schema_Block for a grpc response. -func ConfigSchemaToProto(b *configschema.Block) *proto.Schema_Block { - block := &proto.Schema_Block{} +// tfprotov5.SchemaBlock for a grpc response. +func ConfigSchemaToProto(b *configschema.Block) *tfprotov5.SchemaBlock { + block := &tfprotov5.SchemaBlock{ + Description: b.Description, + DescriptionKind: protoStringKind(b.DescriptionKind), + Deprecated: b.Deprecated, + } for _, name := range sortedKeys(b.Attributes) { a := b.Attributes[name] - attr := &proto.Schema_Attribute{ - Name: name, - Description: a.Description, - Optional: a.Optional, - Computed: a.Computed, - Required: a.Required, - Sensitive: a.Sensitive, + + attr := &tfprotov5.SchemaAttribute{ + Name: name, + Description: a.Description, + DescriptionKind: protoStringKind(a.DescriptionKind), + Optional: a.Optional, + Computed: a.Computed, + Required: a.Required, + Sensitive: a.Sensitive, + Deprecated: a.Deprecated, } - ty, err := json.Marshal(a.Type) + var err error + attr.Type, err = tftypeFromCtyType(a.Type) if err != nil { panic(err) } - attr.Type = ty - block.Attributes = append(block.Attributes, attr) } @@ -44,23 +166,35 @@ func ConfigSchemaToProto(b *configschema.Block) *proto.Schema_Block { return block } -func protoSchemaNestedBlock(name string, b *configschema.NestedBlock) *proto.Schema_NestedBlock { - var nesting proto.Schema_NestedBlock_NestingMode +func protoStringKind(k configschema.StringKind) tfprotov5.StringKind { + switch k { + default: + log.Printf("[TRACE] unexpected configschema.StringKind: %d", k) + return tfprotov5.StringKindPlain + case configschema.StringPlain: + return tfprotov5.StringKindPlain + case configschema.StringMarkdown: + return tfprotov5.StringKindMarkdown + } +} + +func protoSchemaNestedBlock(name string, b *configschema.NestedBlock) *tfprotov5.SchemaNestedBlock { + var nesting tfprotov5.SchemaNestedBlockNestingMode switch b.Nesting { case configschema.NestingSingle: - nesting = proto.Schema_NestedBlock_SINGLE + nesting = tfprotov5.SchemaNestedBlockNestingModeSingle case configschema.NestingGroup: - nesting = proto.Schema_NestedBlock_GROUP + nesting = tfprotov5.SchemaNestedBlockNestingModeGroup case configschema.NestingList: - nesting = proto.Schema_NestedBlock_LIST + nesting = tfprotov5.SchemaNestedBlockNestingModeList case configschema.NestingSet: - nesting = proto.Schema_NestedBlock_SET + nesting = tfprotov5.SchemaNestedBlockNestingModeSet case configschema.NestingMap: - nesting = proto.Schema_NestedBlock_MAP + nesting = tfprotov5.SchemaNestedBlockNestingModeMap default: - nesting = proto.Schema_NestedBlock_INVALID + nesting = tfprotov5.SchemaNestedBlockNestingModeInvalid } - return &proto.Schema_NestedBlock{ + return &tfprotov5.SchemaNestedBlock{ TypeName: name, Block: ConfigSchemaToProto(&b.Block), Nesting: nesting, @@ -69,32 +203,32 @@ func protoSchemaNestedBlock(name string, b *configschema.NestedBlock) *proto.Sch } } -// ProtoToProviderSchema takes a proto.Schema and converts it to a providers.Schema. -func ProtoToProviderSchema(s *proto.Schema) providers.Schema { - return providers.Schema{ - Version: s.Version, - Block: ProtoToConfigSchema(s.Block), - } -} - -// ProtoToConfigSchema takes the GetSchcema_Block from a grpc response and converts it +// ProtoToConfigSchema takes the GetSchema_Block from a grpc response and converts it // to a terraform *configschema.Block. -func ProtoToConfigSchema(b *proto.Schema_Block) *configschema.Block { +func ProtoToConfigSchema(b *tfprotov5.SchemaBlock) *configschema.Block { block := &configschema.Block{ Attributes: make(map[string]*configschema.Attribute), BlockTypes: make(map[string]*configschema.NestedBlock), + + Description: b.Description, + DescriptionKind: schemaStringKind(b.DescriptionKind), + Deprecated: b.Deprecated, } for _, a := range b.Attributes { attr := &configschema.Attribute{ - Description: a.Description, - Required: a.Required, - Optional: a.Optional, - Computed: a.Computed, - Sensitive: a.Sensitive, + Description: a.Description, + DescriptionKind: schemaStringKind(a.DescriptionKind), + Required: a.Required, + Optional: a.Optional, + Computed: a.Computed, + Sensitive: a.Sensitive, + Deprecated: a.Deprecated, } - if err := json.Unmarshal(a.Type, &attr.Type); err != nil { + var err error + attr.Type, err = ctyTypeFromTFType(a.Type) + if err != nil { panic(err) } @@ -108,18 +242,30 @@ func ProtoToConfigSchema(b *proto.Schema_Block) *configschema.Block { return block } -func schemaNestedBlock(b *proto.Schema_NestedBlock) *configschema.NestedBlock { +func schemaStringKind(k tfprotov5.StringKind) configschema.StringKind { + switch k { + default: + log.Printf("[TRACE] unexpected tfprotov5.StringKind: %d", k) + return configschema.StringPlain + case tfprotov5.StringKindPlain: + return configschema.StringPlain + case tfprotov5.StringKindMarkdown: + return configschema.StringMarkdown + } +} + +func schemaNestedBlock(b *tfprotov5.SchemaNestedBlock) *configschema.NestedBlock { var nesting configschema.NestingMode switch b.Nesting { - case proto.Schema_NestedBlock_SINGLE: + case tfprotov5.SchemaNestedBlockNestingModeSingle: nesting = configschema.NestingSingle - case proto.Schema_NestedBlock_GROUP: + case tfprotov5.SchemaNestedBlockNestingModeGroup: nesting = configschema.NestingGroup - case proto.Schema_NestedBlock_LIST: + case tfprotov5.SchemaNestedBlockNestingModeList: nesting = configschema.NestingList - case proto.Schema_NestedBlock_MAP: + case tfprotov5.SchemaNestedBlockNestingModeMap: nesting = configschema.NestingMap - case proto.Schema_NestedBlock_SET: + case tfprotov5.SchemaNestedBlockNestingModeSet: nesting = configschema.NestingSet default: // In all other cases we'll leave it as the zero value (invalid) and diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/convert/schema_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/convert/schema_test.go new file mode 100644 index 00000000..69cb1485 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/convert/schema_test.go @@ -0,0 +1,371 @@ +package convert + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/go-cty/cty" + + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" +) + +var ( + equateEmpty = cmpopts.EquateEmpty() + typeComparer = cmp.Comparer(cty.Type.Equals) + valueComparer = cmp.Comparer(cty.Value.RawEquals) +) + +// Test that we can convert configschema to protobuf types and back again. +func TestConvertSchemaBlocks(t *testing.T) { + tests := map[string]struct { + Block *tfprotov5.SchemaBlock + Want *configschema.Block + }{ + "attributes": { + &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "computed", + Type: tftypes.List{ + ElementType: tftypes.Bool, + }, + Computed: true, + }, + { + Name: "optional", + Type: tftypes.String, + Optional: true, + }, + { + Name: "optional_computed", + Type: tftypes.Map{ + AttributeType: tftypes.Bool, + }, + Optional: true, + Computed: true, + }, + { + Name: "required", + Type: tftypes.Number, + Required: true, + }, + }, + }, + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "computed": { + Type: cty.List(cty.Bool), + Computed: true, + }, + "optional": { + Type: cty.String, + Optional: true, + }, + "optional_computed": { + Type: cty.Map(cty.Bool), + Optional: true, + Computed: true, + }, + "required": { + Type: cty.Number, + Required: true, + }, + }, + }, + }, + "blocks": { + &tfprotov5.SchemaBlock{ + BlockTypes: []*tfprotov5.SchemaNestedBlock{ + { + TypeName: "list", + Nesting: tfprotov5.SchemaNestedBlockNestingModeList, + Block: &tfprotov5.SchemaBlock{}, + }, + { + TypeName: "map", + Nesting: tfprotov5.SchemaNestedBlockNestingModeMap, + Block: &tfprotov5.SchemaBlock{}, + }, + { + TypeName: "set", + Nesting: tfprotov5.SchemaNestedBlockNestingModeSet, + Block: &tfprotov5.SchemaBlock{}, + }, + { + TypeName: "single", + Nesting: tfprotov5.SchemaNestedBlockNestingModeSingle, + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "foo", + Type: tftypes.DynamicPseudoType, + Required: true, + }, + }, + }, + }, + }, + }, + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "list": { + Nesting: configschema.NestingList, + }, + "map": { + Nesting: configschema.NestingMap, + }, + "set": { + Nesting: configschema.NestingSet, + }, + "single": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.DynamicPseudoType, + Required: true, + }, + }, + }, + }, + }, + }, + }, + "deep block nesting": { + &tfprotov5.SchemaBlock{ + BlockTypes: []*tfprotov5.SchemaNestedBlock{ + { + TypeName: "single", + Nesting: tfprotov5.SchemaNestedBlockNestingModeSingle, + Block: &tfprotov5.SchemaBlock{ + BlockTypes: []*tfprotov5.SchemaNestedBlock{ + { + TypeName: "list", + Nesting: tfprotov5.SchemaNestedBlockNestingModeList, + Block: &tfprotov5.SchemaBlock{ + BlockTypes: []*tfprotov5.SchemaNestedBlock{ + { + TypeName: "set", + Nesting: tfprotov5.SchemaNestedBlockNestingModeSet, + Block: &tfprotov5.SchemaBlock{}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "single": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "list": { + Nesting: configschema.NestingList, + Block: configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "set": { + Nesting: configschema.NestingSet, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + converted := ProtoToConfigSchema(tc.Block) + if !cmp.Equal(converted, tc.Want, typeComparer, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, valueComparer, equateEmpty)) + } + }) + } +} + +// Test that we can convert configschema to protobuf types and back again. +func TestConvertProtoSchemaBlocks(t *testing.T) { + tests := map[string]struct { + Want *tfprotov5.SchemaBlock + Block *configschema.Block + }{ + "attributes": { + &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "computed", + Type: tftypes.List{ + ElementType: tftypes.Bool, + }, + Computed: true, + }, + { + Name: "optional", + Type: tftypes.String, + Optional: true, + }, + { + Name: "optional_computed", + Type: tftypes.Map{ + AttributeType: tftypes.Bool, + }, + Optional: true, + Computed: true, + }, + { + Name: "required", + Type: tftypes.Number, + Required: true, + }, + }, + }, + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "computed": { + Type: cty.List(cty.Bool), + Computed: true, + }, + "optional": { + Type: cty.String, + Optional: true, + }, + "optional_computed": { + Type: cty.Map(cty.Bool), + Optional: true, + Computed: true, + }, + "required": { + Type: cty.Number, + Required: true, + }, + }, + }, + }, + "blocks": { + &tfprotov5.SchemaBlock{ + BlockTypes: []*tfprotov5.SchemaNestedBlock{ + { + TypeName: "list", + Nesting: tfprotov5.SchemaNestedBlockNestingModeList, + Block: &tfprotov5.SchemaBlock{}, + }, + { + TypeName: "map", + Nesting: tfprotov5.SchemaNestedBlockNestingModeMap, + Block: &tfprotov5.SchemaBlock{}, + }, + { + TypeName: "set", + Nesting: tfprotov5.SchemaNestedBlockNestingModeSet, + Block: &tfprotov5.SchemaBlock{}, + }, + { + TypeName: "single", + Nesting: tfprotov5.SchemaNestedBlockNestingModeSingle, + Block: &tfprotov5.SchemaBlock{ + Attributes: []*tfprotov5.SchemaAttribute{ + { + Name: "foo", + Type: tftypes.DynamicPseudoType, + Required: true, + }, + }, + }, + }, + }, + }, + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "list": { + Nesting: configschema.NestingList, + }, + "map": { + Nesting: configschema.NestingMap, + }, + "set": { + Nesting: configschema.NestingSet, + }, + "single": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.DynamicPseudoType, + Required: true, + }, + }, + }, + }, + }, + }, + }, + "deep block nesting": { + &tfprotov5.SchemaBlock{ + BlockTypes: []*tfprotov5.SchemaNestedBlock{ + { + TypeName: "single", + Nesting: tfprotov5.SchemaNestedBlockNestingModeSingle, + Block: &tfprotov5.SchemaBlock{ + BlockTypes: []*tfprotov5.SchemaNestedBlock{ + { + TypeName: "list", + Nesting: tfprotov5.SchemaNestedBlockNestingModeList, + Block: &tfprotov5.SchemaBlock{ + BlockTypes: []*tfprotov5.SchemaNestedBlock{ + { + TypeName: "set", + Nesting: tfprotov5.SchemaNestedBlockNestingModeSet, + Block: &tfprotov5.SchemaBlock{}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "single": { + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "list": { + Nesting: configschema.NestingList, + Block: configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "set": { + Nesting: configschema.NestingSet, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + converted := ConfigSchemaToProto(tc.Block) + if !cmp.Equal(converted, tc.Want, typeComparer, equateEmpty) { + t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, equateEmpty)) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/error.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/error.go deleted file mode 100644 index 729e9709..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/error.go +++ /dev/null @@ -1,64 +0,0 @@ -package discovery - -// Error is a type used to describe situations that the caller must handle -// since they indicate some form of user error. -// -// The functions and methods that return these specialized errors indicate so -// in their documentation. The Error type should not itself be used directly, -// but rather errors should be compared using the == operator with the -// error constants in this package. -// -// Values of this type are _not_ used when the error being reported is an -// operational error (server unavailable, etc) or indicative of a bug in -// this package or its caller. -type Error string - -// ErrorNoSuitableVersion indicates that a suitable version (meeting given -// constraints) is not available. -const ErrorNoSuitableVersion = Error("no suitable version is available") - -// ErrorNoVersionCompatible indicates that all of the available versions -// that otherwise met constraints are not compatible with the current -// version of Terraform. -const ErrorNoVersionCompatible = Error("no available version is compatible with this version of Terraform") - -// ErrorVersionIncompatible indicates that all of the versions within the -// constraints are not compatible with the current version of Terrafrom, though -// there does exist a version outside of the constaints that is compatible. -const ErrorVersionIncompatible = Error("incompatible provider version") - -// ErrorNoSuchProvider indicates that no provider exists with a name given -const ErrorNoSuchProvider = Error("no provider exists with the given name") - -// ErrorNoVersionCompatibleWithPlatform indicates that all of the available -// versions that otherwise met constraints are not compatible with the -// requested platform -const ErrorNoVersionCompatibleWithPlatform = Error("no available version is compatible for the requested platform") - -// ErrorMissingChecksumVerification indicates that either the provider -// distribution is missing the SHA256SUMS file or the checksum file does -// not contain a checksum for the binary plugin -const ErrorMissingChecksumVerification = Error("unable to verify checksum") - -// ErrorChecksumVerification indicates that the current checksum of the -// provider plugin has changed since the initial release and is not trusted -// to download -const ErrorChecksumVerification = Error("unexpected plugin checksum") - -// ErrorSignatureVerification indicates that the digital signature for a -// provider distribution could not be verified for one of the following -// reasons: missing signature file, missing public key, or the signature -// was not signed by any known key for the publisher -const ErrorSignatureVerification = Error("unable to verify signature") - -// ErrorServiceUnreachable indicates that the network was unable to connect -// to the registry service -const ErrorServiceUnreachable = Error("registry service is unreachable") - -// ErrorPublicRegistryUnreachable indicates that the network was unable to connect -// to the public registry in particular, so we can show a link to the statuspage -const ErrorPublicRegistryUnreachable = Error("registry service is unreachable, check https://status.hashicorp.com/ for status updates") - -func (err Error) Error() string { - return string(err) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/find.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/find.go deleted file mode 100644 index f053312b..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/find.go +++ /dev/null @@ -1,191 +0,0 @@ -package discovery - -import ( - "io/ioutil" - "log" - "os" - "path/filepath" - "strings" -) - -// FindPlugins looks in the given directories for files whose filenames -// suggest that they are plugins of the given kind (e.g. "provider") and -// returns a PluginMetaSet representing the discovered potential-plugins. -// -// Currently this supports two different naming schemes. The current -// standard naming scheme is a subdirectory called $GOOS-$GOARCH containing -// files named terraform-$KIND-$NAME-V$VERSION. The legacy naming scheme is -// files directly in the given directory whose names are like -// terraform-$KIND-$NAME. -// -// Only one plugin will be returned for each unique plugin (name, version) -// pair, with preference given to files found in earlier directories. -// -// This is a convenience wrapper around FindPluginPaths and ResolvePluginsPaths. -func FindPlugins(kind string, dirs []string) PluginMetaSet { - return ResolvePluginPaths(FindPluginPaths(kind, dirs)) -} - -// FindPluginPaths looks in the given directories for files whose filenames -// suggest that they are plugins of the given kind (e.g. "provider"). -// -// The return value is a list of absolute paths that appear to refer to -// plugins in the given directories, based only on what can be inferred -// from the naming scheme. The paths returned are ordered such that files -// in later dirs appear after files in earlier dirs in the given directory -// list. Within the same directory plugins are returned in a consistent but -// undefined order. -func FindPluginPaths(kind string, dirs []string) []string { - // This is just a thin wrapper around findPluginPaths so that we can - // use the latter in tests with a fake machineName so we can use our - // test fixtures. - return findPluginPaths(kind, dirs) -} - -func findPluginPaths(kind string, dirs []string) []string { - prefix := "terraform-" + kind + "-" - - ret := make([]string, 0, len(dirs)) - - for _, dir := range dirs { - items, err := ioutil.ReadDir(dir) - if err != nil { - // Ignore missing dirs, non-dirs, etc - continue - } - - log.Printf("[DEBUG] checking for %s in %q", kind, dir) - - for _, item := range items { - fullName := item.Name() - - if !strings.HasPrefix(fullName, prefix) { - continue - } - - // New-style paths must have a version segment in filename - if strings.Contains(strings.ToLower(fullName), "_v") { - absPath, err := filepath.Abs(filepath.Join(dir, fullName)) - if err != nil { - log.Printf("[ERROR] plugin filepath error: %s", err) - continue - } - - // Check that the file we found is usable - if !pathIsFile(absPath) { - log.Printf("[ERROR] ignoring non-file %s", absPath) - continue - } - - log.Printf("[DEBUG] found %s %q", kind, fullName) - ret = append(ret, filepath.Clean(absPath)) - continue - } - - // Legacy style with files directly in the base directory - absPath, err := filepath.Abs(filepath.Join(dir, fullName)) - if err != nil { - log.Printf("[ERROR] plugin filepath error: %s", err) - continue - } - - // Check that the file we found is usable - if !pathIsFile(absPath) { - log.Printf("[ERROR] ignoring non-file %s", absPath) - continue - } - - log.Printf("[WARN] found legacy %s %q", kind, fullName) - - ret = append(ret, filepath.Clean(absPath)) - } - } - - return ret -} - -// Returns true if and only if the given path refers to a file or a symlink -// to a file. -func pathIsFile(path string) bool { - info, err := os.Stat(path) - if err != nil { - return false - } - - return !info.IsDir() -} - -// ResolvePluginPaths takes a list of paths to plugin executables (as returned -// by e.g. FindPluginPaths) and produces a PluginMetaSet describing the -// referenced plugins. -// -// If the same combination of plugin name and version appears multiple times, -// the earlier reference will be preferred. Several different versions of -// the same plugin name may be returned, in which case the methods of -// PluginMetaSet can be used to filter down. -func ResolvePluginPaths(paths []string) PluginMetaSet { - s := make(PluginMetaSet) - - type nameVersion struct { - Name string - Version string - } - found := make(map[nameVersion]struct{}) - - for _, path := range paths { - baseName := strings.ToLower(filepath.Base(path)) - if !strings.HasPrefix(baseName, "terraform-") { - // Should never happen with reasonable input - continue - } - - baseName = baseName[10:] - firstDash := strings.Index(baseName, "-") - if firstDash == -1 { - // Should never happen with reasonable input - continue - } - - baseName = baseName[firstDash+1:] - if baseName == "" { - // Should never happen with reasonable input - continue - } - - // Trim the .exe suffix used on Windows before we start wrangling - // the remainder of the path. - if strings.HasSuffix(baseName, ".exe") { - baseName = baseName[:len(baseName)-4] - } - - parts := strings.SplitN(baseName, "_v", 2) - name := parts[0] - version := VersionZero - if len(parts) == 2 { - version = parts[1] - } - - // Auto-installed plugins contain an extra name portion representing - // the expected plugin version, which we must trim off. - if underX := strings.Index(version, "_x"); underX != -1 { - version = version[:underX] - } - - if _, ok := found[nameVersion{name, version}]; ok { - // Skip duplicate versions of the same plugin - // (We do this during this step because after this we will be - // dealing with sets and thus lose our ordering with which to - // decide preference.) - continue - } - - s.Add(PluginMeta{ - Name: name, - Version: VersionStr(version), - Path: path, - }) - found[nameVersion{name, version}] = struct{}{} - } - - return s -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/get.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/get.go deleted file mode 100644 index 722bb28a..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/get.go +++ /dev/null @@ -1,669 +0,0 @@ -package discovery - -import ( - "errors" - "fmt" - "io" - "io/ioutil" - "log" - "net/http" - "os" - "path/filepath" - "runtime" - "strconv" - "strings" - - "github.com/hashicorp/errwrap" - getter "github.com/hashicorp/go-getter" - multierror "github.com/hashicorp/go-multierror" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/httpclient" - "github.com/hashicorp/terraform-plugin-sdk/internal/registry" - "github.com/hashicorp/terraform-plugin-sdk/internal/registry/regsrc" - "github.com/hashicorp/terraform-plugin-sdk/internal/registry/response" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - tfversion "github.com/hashicorp/terraform-plugin-sdk/internal/version" - "github.com/hashicorp/terraform-svchost/disco" - "github.com/mitchellh/cli" -) - -// Releases are located by querying the terraform registry. - -var httpClient *http.Client - -func init() { - httpClient = httpclient.New() - - httpGetter := &getter.HttpGetter{ - Client: httpClient, - Netrc: true, - } - - getter.Getters["http"] = httpGetter - getter.Getters["https"] = httpGetter -} - -// ProviderInstaller is an Installer implementation that knows how to -// download Terraform providers from the official HashiCorp releases service -// into a local directory. The files downloaded are compliant with the -// naming scheme expected by FindPlugins, so the target directory of a -// provider installer can be used as one of several plugin discovery sources. -type ProviderInstaller struct { - Dir string - - // Cache is used to access and update a local cache of plugins if non-nil. - // Can be nil to disable caching. - Cache PluginCache - - PluginProtocolVersion uint - - // OS and Arch specify the OS and architecture that should be used when - // installing plugins. These use the same labels as the runtime.GOOS and - // runtime.GOARCH variables respectively, and indeed the values of these - // are used as defaults if either of these is the empty string. - OS string - Arch string - - // Skip checksum and signature verification - SkipVerify bool - - Ui cli.Ui // Ui for output - - // Services is a required *disco.Disco, which may have services and - // credentials pre-loaded. - Services *disco.Disco - - // registry client - registry *registry.Client -} - -// Get is part of an implementation of type Installer, and attempts to download -// and install a Terraform provider matching the given constraints. -// -// This method may return one of a number of sentinel errors from this -// package to indicate issues that are likely to be resolvable via user action: -// -// ErrorNoSuchProvider: no provider with the given name exists in the repository. -// ErrorNoSuitableVersion: the provider exists but no available version matches constraints. -// ErrorNoVersionCompatible: a plugin was found within the constraints but it is -// incompatible with the current Terraform version. -// -// These errors should be recognized and handled as special cases by the caller -// to present a suitable user-oriented error message. -// -// All other errors indicate an internal problem that is likely _not_ solvable -// through user action, or at least not within Terraform's scope. Error messages -// are produced under the assumption that if presented to the user they will -// be presented alongside context about what is being installed, and thus the -// error messages do not redundantly include such information. -func (i *ProviderInstaller) Get(provider addrs.ProviderType, req Constraints) (PluginMeta, tfdiags.Diagnostics, error) { - var diags tfdiags.Diagnostics - - // a little bit of initialization. - if i.OS == "" { - i.OS = runtime.GOOS - } - if i.Arch == "" { - i.Arch = runtime.GOARCH - } - if i.registry == nil { - i.registry = registry.NewClient(i.Services, nil) - } - - // get a full listing of versions for the requested provider - allVersions, err := i.listProviderVersions(provider) - - // TODO: return multiple errors - if err != nil { - log.Printf("[DEBUG] %s", err) - if registry.IsServiceUnreachable(err) { - registryHost, err := i.hostname() - if err == nil && registryHost == regsrc.PublicRegistryHost.Raw { - return PluginMeta{}, diags, ErrorPublicRegistryUnreachable - } - return PluginMeta{}, diags, ErrorServiceUnreachable - } - if registry.IsServiceNotProvided(err) { - return PluginMeta{}, diags, err - } - return PluginMeta{}, diags, ErrorNoSuchProvider - } - - // Add any warnings from the response to diags - for _, warning := range allVersions.Warnings { - hostname, err := i.hostname() - if err != nil { - return PluginMeta{}, diags, err - } - diag := tfdiags.SimpleWarning(fmt.Sprintf("%s: %s", hostname, warning)) - diags = diags.Append(diag) - } - - if len(allVersions.Versions) == 0 { - return PluginMeta{}, diags, ErrorNoSuitableVersion - } - providerSource := allVersions.ID - - // Filter the list of plugin versions to those which meet the version constraints - versions := allowedVersions(allVersions, req) - if len(versions) == 0 { - return PluginMeta{}, diags, ErrorNoSuitableVersion - } - - // sort them newest to oldest. The newest version wins! - response.ProviderVersionCollection(versions).Sort() - - // if the chosen provider version does not support the requested platform, - // filter the list of acceptable versions to those that support that platform - if err := i.checkPlatformCompatibility(versions[0]); err != nil { - versions = i.platformCompatibleVersions(versions) - if len(versions) == 0 { - return PluginMeta{}, diags, ErrorNoVersionCompatibleWithPlatform - } - } - - // we now have a winning platform-compatible version - versionMeta := versions[0] - v := VersionStr(versionMeta.Version).MustParse() - - // check protocol compatibility - if err := i.checkPluginProtocol(versionMeta); err != nil { - closestMatch, err := i.findClosestProtocolCompatibleVersion(allVersions.Versions) - if err != nil { - // No operation here if we can't find a version with compatible protocol - return PluginMeta{}, diags, err - } - - // Prompt version suggestion to UI based on closest protocol match - var errMsg string - closestVersion := VersionStr(closestMatch.Version).MustParse() - if v.NewerThan(closestVersion) { - errMsg = providerProtocolTooNew - } else { - errMsg = providerProtocolTooOld - } - - constraintStr := req.String() - if constraintStr == "" { - constraintStr = "(any version)" - } - - return PluginMeta{}, diags, errwrap.Wrap(ErrorVersionIncompatible, fmt.Errorf(fmt.Sprintf( - errMsg, provider, v.String(), tfversion.String(), - closestVersion.String(), closestVersion.MinorUpgradeConstraintStr(), constraintStr))) - } - - downloadURLs, err := i.listProviderDownloadURLs(providerSource, versionMeta.Version) - if err != nil { - return PluginMeta{}, diags, err - } - providerURL := downloadURLs.DownloadURL - - if !i.SkipVerify { - // Terraform verifies the integrity of a provider release before downloading - // the plugin binary. The digital signature (SHA256SUMS.sig) on the - // release distribution (SHA256SUMS) is verified with the public key of the - // publisher provided in the Terraform Registry response, ensuring that - // everything is as intended by the publisher. The checksum of the provider - // plugin is expected in the SHA256SUMS file and is double checked to match - // the checksum of the original published release to the Registry. This - // enforces immutability of releases between the Registry and the plugin's - // host location. Lastly, the integrity of the binary is verified upon - // download matches the Registry and signed checksum. - sha256, err := i.getProviderChecksum(downloadURLs) - if err != nil { - return PluginMeta{}, diags, err - } - - // add the checksum parameter for go-getter to verify the download for us. - if sha256 != "" { - providerURL = providerURL + "?checksum=sha256:" + sha256 - } - } - - printedProviderName := fmt.Sprintf("%q (%s)", provider.Name, providerSource) - i.Ui.Info(fmt.Sprintf("- Downloading plugin for provider %s %s...", printedProviderName, versionMeta.Version)) - log.Printf("[DEBUG] getting provider %s version %q", printedProviderName, versionMeta.Version) - err = i.install(provider, v, providerURL) - if err != nil { - return PluginMeta{}, diags, err - } - - // Find what we just installed - // (This is weird, because go-getter doesn't directly return - // information about what was extracted, and we just extracted - // the archive directly into a shared dir here.) - log.Printf("[DEBUG] looking for the %s %s plugin we just installed", provider.Name, versionMeta.Version) - metas := FindPlugins("provider", []string{i.Dir}) - log.Printf("[DEBUG] all plugins found %#v", metas) - metas, _ = metas.ValidateVersions() - metas = metas.WithName(provider.Name).WithVersion(v) - log.Printf("[DEBUG] filtered plugins %#v", metas) - if metas.Count() == 0 { - // This should never happen. Suggests that the release archive - // contains an executable file whose name doesn't match the - // expected convention. - return PluginMeta{}, diags, fmt.Errorf( - "failed to find installed plugin version %s; this is a bug in Terraform and should be reported", - versionMeta.Version, - ) - } - - if metas.Count() > 1 { - // This should also never happen, and suggests that a - // particular version was re-released with a different - // executable filename. We consider releases as immutable, so - // this is an error. - return PluginMeta{}, diags, fmt.Errorf( - "multiple plugins installed for version %s; this is a bug in Terraform and should be reported", - versionMeta.Version, - ) - } - - // By now we know we have exactly one meta, and so "Newest" will - // return that one. - return metas.Newest(), diags, nil -} - -func (i *ProviderInstaller) install(provider addrs.ProviderType, version Version, url string) error { - if i.Cache != nil { - log.Printf("[DEBUG] looking for provider %s %s in plugin cache", provider.Name, version) - cached := i.Cache.CachedPluginPath("provider", provider.Name, version) - if cached == "" { - log.Printf("[DEBUG] %s %s not yet in cache, so downloading %s", provider.Name, version, url) - err := getter.Get(i.Cache.InstallDir(), url) - if err != nil { - return err - } - // should now be in cache - cached = i.Cache.CachedPluginPath("provider", provider.Name, version) - if cached == "" { - // should never happen if the getter is behaving properly - // and the plugins are packaged properly. - return fmt.Errorf("failed to find downloaded plugin in cache %s", i.Cache.InstallDir()) - } - } - - // Link or copy the cached binary into our install dir so the - // normal resolution machinery can find it. - filename := filepath.Base(cached) - targetPath := filepath.Join(i.Dir, filename) - // check if the target dir exists, and create it if not - var err error - if _, StatErr := os.Stat(i.Dir); os.IsNotExist(StatErr) { - err = os.MkdirAll(i.Dir, 0700) - } - if err != nil { - return err - } - - log.Printf("[DEBUG] installing %s %s to %s from local cache %s", provider.Name, version, targetPath, cached) - - // Delete if we can. If there's nothing there already then no harm done. - // This is important because we can't create a link if there's - // already a file of the same name present. - // (any other error here we'll catch below when we try to write here) - os.Remove(targetPath) - - // We don't attempt linking on Windows because links are not - // comprehensively supported by all tools/apps in Windows and - // so we choose to be conservative to avoid creating any - // weird issues for Windows users. - linkErr := errors.New("link not supported for Windows") // placeholder error, never actually returned - if runtime.GOOS != "windows" { - // Try hard linking first. Hard links are preferable because this - // creates a self-contained directory that doesn't depend on the - // cache after install. - linkErr = os.Link(cached, targetPath) - - // If that failed, try a symlink. This _does_ depend on the cache - // after install, so the user must manage the cache more carefully - // in this case, but avoids creating redundant copies of the - // plugins on disk. - if linkErr != nil { - linkErr = os.Symlink(cached, targetPath) - } - } - - // If we still have an error then we'll try a copy as a fallback. - // In this case either the OS is Windows or the target filesystem - // can't support symlinks. - if linkErr != nil { - srcFile, err := os.Open(cached) - if err != nil { - return fmt.Errorf("failed to open cached plugin %s: %s", cached, err) - } - defer srcFile.Close() - - destFile, err := os.OpenFile(targetPath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, os.ModePerm) - if err != nil { - return fmt.Errorf("failed to create %s: %s", targetPath, err) - } - - _, err = io.Copy(destFile, srcFile) - if err != nil { - destFile.Close() - return fmt.Errorf("failed to copy cached plugin from %s to %s: %s", cached, targetPath, err) - } - - err = destFile.Close() - if err != nil { - return fmt.Errorf("error creating %s: %s", targetPath, err) - } - } - - // One way or another, by the time we get here we should have either - // a link or a copy of the cached plugin within i.Dir, as expected. - } else { - log.Printf("[DEBUG] plugin cache is disabled, so downloading %s %s from %s", provider.Name, version, url) - err := getter.Get(i.Dir, url) - if err != nil { - return err - } - } - return nil -} - -func (i *ProviderInstaller) PurgeUnused(used map[string]PluginMeta) (PluginMetaSet, error) { - purge := make(PluginMetaSet) - - present := FindPlugins("provider", []string{i.Dir}) - for meta := range present { - chosen, ok := used[meta.Name] - if !ok { - purge.Add(meta) - } - if chosen.Path != meta.Path { - purge.Add(meta) - } - } - - removed := make(PluginMetaSet) - var errs error - for meta := range purge { - path := meta.Path - err := os.Remove(path) - if err != nil { - errs = multierror.Append(errs, fmt.Errorf( - "failed to remove unused provider plugin %s: %s", - path, err, - )) - } else { - removed.Add(meta) - } - } - - return removed, errs -} - -func (i *ProviderInstaller) getProviderChecksum(resp *response.TerraformProviderPlatformLocation) (string, error) { - // Get SHA256SUMS file. - shasums, err := getFile(resp.ShasumsURL) - if err != nil { - log.Printf("[ERROR] error fetching checksums from %q: %s", resp.ShasumsURL, err) - return "", ErrorMissingChecksumVerification - } - - // Get SHA256SUMS.sig file. - signature, err := getFile(resp.ShasumsSignatureURL) - if err != nil { - log.Printf("[ERROR] error fetching checksums signature from %q: %s", resp.ShasumsSignatureURL, err) - return "", ErrorSignatureVerification - } - - // Verify the GPG signature returned from the Registry. - asciiArmor := resp.SigningKeys.GPGASCIIArmor() - signer, err := verifySig(shasums, signature, asciiArmor) - if err != nil { - log.Printf("[ERROR] error verifying signature: %s", err) - return "", ErrorSignatureVerification - } - - // Also verify the GPG signature against the HashiCorp public key. This is - // a temporary additional check until a more robust key verification - // process is added in a future release. - _, err = verifySig(shasums, signature, HashicorpPublicKey) - if err != nil { - log.Printf("[ERROR] error verifying signature against HashiCorp public key: %s", err) - return "", ErrorSignatureVerification - } - - // Display identity for GPG key which succeeded verifying the signature. - // This could also be used to display to the user with i.Ui.Info(). - identities := []string{} - for k := range signer.Identities { - identities = append(identities, k) - } - identity := strings.Join(identities, ", ") - log.Printf("[DEBUG] verified GPG signature with key from %s", identity) - - // Extract checksum for this os/arch platform binary and verify against Registry - checksum := checksumForFile(shasums, resp.Filename) - if checksum == "" { - log.Printf("[ERROR] missing checksum for %s from source %s", resp.Filename, resp.ShasumsURL) - return "", ErrorMissingChecksumVerification - } else if checksum != resp.Shasum { - log.Printf("[ERROR] unexpected checksum for %s from source %q", resp.Filename, resp.ShasumsURL) - return "", ErrorChecksumVerification - } - - return checksum, nil -} - -func (i *ProviderInstaller) hostname() (string, error) { - provider := regsrc.NewTerraformProvider("", i.OS, i.Arch) - svchost, err := provider.SvcHost() - if err != nil { - return "", err - } - - return svchost.ForDisplay(), nil -} - -// list all versions available for the named provider -func (i *ProviderInstaller) listProviderVersions(provider addrs.ProviderType) (*response.TerraformProviderVersions, error) { - req := regsrc.NewTerraformProvider(provider.Name, i.OS, i.Arch) - versions, err := i.registry.TerraformProviderVersions(req) - return versions, err -} - -func (i *ProviderInstaller) listProviderDownloadURLs(name, version string) (*response.TerraformProviderPlatformLocation, error) { - urls, err := i.registry.TerraformProviderLocation(regsrc.NewTerraformProvider(name, i.OS, i.Arch), version) - if urls == nil { - return nil, fmt.Errorf("No download urls found for provider %s", name) - } - return urls, err -} - -// findClosestProtocolCompatibleVersion searches for the provider version with the closest protocol match. -// Prerelease versions are filtered. -func (i *ProviderInstaller) findClosestProtocolCompatibleVersion(versions []*response.TerraformProviderVersion) (*response.TerraformProviderVersion, error) { - // Loop through all the provider versions to find the earliest and latest - // versions that match the installer protocol to then select the closest of the two - var latest, earliest *response.TerraformProviderVersion - for _, version := range versions { - // Prereleases are filtered and will not be suggested - v, err := VersionStr(version.Version).Parse() - if err != nil || v.IsPrerelease() { - continue - } - - if err := i.checkPluginProtocol(version); err == nil { - if earliest == nil { - // Found the first provider version with compatible protocol - earliest = version - } - // Update the latest protocol compatible version - latest = version - } - } - if earliest == nil { - // No compatible protocol was found for any version - return nil, ErrorNoVersionCompatible - } - - // Convert protocols to comparable types - protoString := strconv.Itoa(int(i.PluginProtocolVersion)) - protocolVersion, err := VersionStr(protoString).Parse() - if err != nil { - return nil, fmt.Errorf("invalid plugin protocol version: %q", i.PluginProtocolVersion) - } - - earliestVersionProtocol, err := VersionStr(earliest.Protocols[0]).Parse() - if err != nil { - return nil, err - } - - // Compare installer protocol version with the first protocol listed of the earliest match - // [A, B] where A is assumed the earliest compatible major version of the protocol pair - if protocolVersion.NewerThan(earliestVersionProtocol) { - // Provider protocols are too old, the closest version is the earliest compatible version - return earliest, nil - } - - // Provider protocols are too new, the closest version is the latest compatible version - return latest, nil -} - -func (i *ProviderInstaller) checkPluginProtocol(versionMeta *response.TerraformProviderVersion) error { - // TODO: should this be a different error? We should probably differentiate between - // no compatible versions and no protocol versions listed at all - if len(versionMeta.Protocols) == 0 { - return fmt.Errorf("no plugin protocol versions listed") - } - - protoString := strconv.Itoa(int(i.PluginProtocolVersion)) - protocolVersion, err := VersionStr(protoString).Parse() - if err != nil { - return fmt.Errorf("invalid plugin protocol version: %q", i.PluginProtocolVersion) - } - protocolConstraint, err := protocolVersion.MinorUpgradeConstraintStr().Parse() - if err != nil { - // This should not fail if the preceding function succeeded. - return fmt.Errorf("invalid plugin protocol version: %q", protocolVersion.String()) - } - - for _, p := range versionMeta.Protocols { - proPro, err := VersionStr(p).Parse() - if err != nil { - // invalid protocol reported by the registry. Move along. - log.Printf("[WARN] invalid provider protocol version %q found in the registry", versionMeta.Version) - continue - } - // success! - if protocolConstraint.Allows(proPro) { - return nil - } - } - - return ErrorNoVersionCompatible -} - -// platformCompatibleVersions returns a list of provider versions that are -// compatible with the requested platform. -func (i *ProviderInstaller) platformCompatibleVersions(versions []*response.TerraformProviderVersion) []*response.TerraformProviderVersion { - var v []*response.TerraformProviderVersion - for _, version := range versions { - if err := i.checkPlatformCompatibility(version); err == nil { - v = append(v, version) - } - } - return v -} - -func (i *ProviderInstaller) checkPlatformCompatibility(versionMeta *response.TerraformProviderVersion) error { - if len(versionMeta.Platforms) == 0 { - return fmt.Errorf("no supported provider platforms listed") - } - for _, p := range versionMeta.Platforms { - if p.Arch == i.Arch && p.OS == i.OS { - return nil - } - } - return fmt.Errorf("version %s does not support the requested platform %s_%s", versionMeta.Version, i.OS, i.Arch) -} - -// take the list of available versions for a plugin, and filter out those that -// don't fit the constraints. -func allowedVersions(available *response.TerraformProviderVersions, required Constraints) []*response.TerraformProviderVersion { - var allowed []*response.TerraformProviderVersion - - for _, v := range available.Versions { - version, err := VersionStr(v.Version).Parse() - if err != nil { - log.Printf("[WARN] invalid version found for %q: %s", available.ID, err) - continue - } - if required.Allows(version) { - allowed = append(allowed, v) - } - } - return allowed -} - -func checksumForFile(sums []byte, name string) string { - for _, line := range strings.Split(string(sums), "\n") { - parts := strings.Fields(line) - if len(parts) > 1 && parts[1] == name { - return parts[0] - } - } - return "" -} - -func getFile(url string) ([]byte, error) { - resp, err := httpClient.Get(url) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%s", resp.Status) - } - - data, err := ioutil.ReadAll(resp.Body) - if err != nil { - return data, err - } - return data, nil -} - -// providerProtocolTooOld is a message sent to the CLI UI if the provider's -// supported protocol versions are too old for the user's version of terraform, -// but an older version of the provider is compatible. -const providerProtocolTooOld = ` -[reset][bold][red]Provider %q v%s is not compatible with Terraform %s.[reset][red] - -Provider version %s is the earliest compatible version. Select it with -the following version constraint: - - version = %q - -Terraform checked all of the plugin versions matching the given constraint: - %s - -Consult the documentation for this provider for more information on -compatibility between provider and Terraform versions. -` - -// providerProtocolTooNew is a message sent to the CLI UI if the provider's -// supported protocol versions are too new for the user's version of terraform, -// and the user could either upgrade terraform or choose an older version of the -// provider -const providerProtocolTooNew = ` -[reset][bold][red]Provider %q v%s is not compatible with Terraform %s.[reset][red] - -Provider version %s is the latest compatible version. Select it with -the following constraint: - - version = %q - -Terraform checked all of the plugin versions matching the given constraint: - %s - -Consult the documentation for this provider for more information on -compatibility between provider and Terraform versions. - -Alternatively, upgrade to the latest version of Terraform for compatibility with newer provider releases. -` diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/get_cache.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/get_cache.go deleted file mode 100644 index 1a100426..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/get_cache.go +++ /dev/null @@ -1,48 +0,0 @@ -package discovery - -// PluginCache is an interface implemented by objects that are able to maintain -// a cache of plugins. -type PluginCache interface { - // CachedPluginPath returns a path where the requested plugin is already - // cached, or an empty string if the requested plugin is not yet cached. - CachedPluginPath(kind string, name string, version Version) string - - // InstallDir returns the directory that new plugins should be installed into - // in order to populate the cache. This directory should be used as the - // first argument to getter.Get when downloading plugins with go-getter. - // - // After installing into this directory, use CachedPluginPath to obtain the - // path where the plugin was installed. - InstallDir() string -} - -// NewLocalPluginCache returns a PluginCache that caches plugins in a -// given local directory. -func NewLocalPluginCache(dir string) PluginCache { - return &pluginCache{ - Dir: dir, - } -} - -type pluginCache struct { - Dir string -} - -func (c *pluginCache) CachedPluginPath(kind string, name string, version Version) string { - allPlugins := FindPlugins(kind, []string{c.Dir}) - plugins := allPlugins.WithName(name).WithVersion(version) - - if plugins.Count() == 0 { - // nothing cached - return "" - } - - // There should generally be only one plugin here; if there's more than - // one match for some reason then we'll just choose one arbitrarily. - plugin := plugins.Newest() - return plugin.Path -} - -func (c *pluginCache) InstallDir() string { - return c.Dir -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/hashicorp.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/hashicorp.go deleted file mode 100644 index 4622ca05..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/hashicorp.go +++ /dev/null @@ -1,34 +0,0 @@ -package discovery - -// HashicorpPublicKey is the HashiCorp public key, also available at -// https://www.hashicorp.com/security -const HashicorpPublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v1 - -mQENBFMORM0BCADBRyKO1MhCirazOSVwcfTr1xUxjPvfxD3hjUwHtjsOy/bT6p9f -W2mRPfwnq2JB5As+paL3UGDsSRDnK9KAxQb0NNF4+eVhr/EJ18s3wwXXDMjpIifq -fIm2WyH3G+aRLTLPIpscUNKDyxFOUbsmgXAmJ46Re1fn8uKxKRHbfa39aeuEYWFA -3drdL1WoUngvED7f+RnKBK2G6ZEpO+LDovQk19xGjiMTtPJrjMjZJ3QXqPvx5wca -KSZLr4lMTuoTI/ZXyZy5bD4tShiZz6KcyX27cD70q2iRcEZ0poLKHyEIDAi3TM5k -SwbbWBFd5RNPOR0qzrb/0p9ksKK48IIfH2FvABEBAAG0K0hhc2hpQ29ycCBTZWN1 -cml0eSA8c2VjdXJpdHlAaGFzaGljb3JwLmNvbT6JATgEEwECACIFAlMORM0CGwMG -CwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEFGFLYc0j/xMyWIIAIPhcVqiQ59n -Jc07gjUX0SWBJAxEG1lKxfzS4Xp+57h2xxTpdotGQ1fZwsihaIqow337YHQI3q0i -SqV534Ms+j/tU7X8sq11xFJIeEVG8PASRCwmryUwghFKPlHETQ8jJ+Y8+1asRydi -psP3B/5Mjhqv/uOK+Vy3zAyIpyDOMtIpOVfjSpCplVRdtSTFWBu9Em7j5I2HMn1w -sJZnJgXKpybpibGiiTtmnFLOwibmprSu04rsnP4ncdC2XRD4wIjoyA+4PKgX3sCO -klEzKryWYBmLkJOMDdo52LttP3279s7XrkLEE7ia0fXa2c12EQ0f0DQ1tGUvyVEW -WmJVccm5bq25AQ0EUw5EzQEIANaPUY04/g7AmYkOMjaCZ6iTp9hB5Rsj/4ee/ln9 -wArzRO9+3eejLWh53FoN1rO+su7tiXJA5YAzVy6tuolrqjM8DBztPxdLBbEi4V+j -2tK0dATdBQBHEh3OJApO2UBtcjaZBT31zrG9K55D+CrcgIVEHAKY8Cb4kLBkb5wM -skn+DrASKU0BNIV1qRsxfiUdQHZfSqtp004nrql1lbFMLFEuiY8FZrkkQ9qduixo -mTT6f34/oiY+Jam3zCK7RDN/OjuWheIPGj/Qbx9JuNiwgX6yRj7OE1tjUx6d8g9y -0H1fmLJbb3WZZbuuGFnK6qrE3bGeY8+AWaJAZ37wpWh1p0cAEQEAAYkBHwQYAQIA -CQUCUw5EzQIbDAAKCRBRhS2HNI/8TJntCAClU7TOO/X053eKF1jqNW4A1qpxctVc -z8eTcY8Om5O4f6a/rfxfNFKn9Qyja/OG1xWNobETy7MiMXYjaa8uUx5iFy6kMVaP -0BXJ59NLZjMARGw6lVTYDTIvzqqqwLxgliSDfSnqUhubGwvykANPO+93BBx89MRG -unNoYGXtPlhNFrAsB1VR8+EyKLv2HQtGCPSFBhrjuzH3gxGibNDDdFQLxxuJWepJ -EK1UbTS4ms0NgZ2Uknqn1WRU1Ki7rE4sTy68iZtWpKQXZEJa0IGnuI2sSINGcXCJ -oEIgXTMyCILo34Fa/C6VCm2WBgz9zZO8/rHIiQm1J5zqz0DrDwKBUM9C -=LYpS ------END PGP PUBLIC KEY BLOCK-----` diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/meta.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/meta.go deleted file mode 100644 index bdcebcb9..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/meta.go +++ /dev/null @@ -1,41 +0,0 @@ -package discovery - -import ( - "crypto/sha256" - "io" - "os" -) - -// PluginMeta is metadata about a plugin, useful for launching the plugin -// and for understanding which plugins are available. -type PluginMeta struct { - // Name is the name of the plugin, e.g. as inferred from the plugin - // binary's filename, or by explicit configuration. - Name string - - // Version is the semver version of the plugin, expressed as a string - // that might not be semver-valid. - Version VersionStr - - // Path is the absolute path of the executable that can be launched - // to provide the RPC server for this plugin. - Path string -} - -// SHA256 returns a SHA256 hash of the content of the referenced executable -// file, or an error if the file's contents cannot be read. -func (m PluginMeta) SHA256() ([]byte, error) { - f, err := os.Open(m.Path) - if err != nil { - return nil, err - } - defer f.Close() - - h := sha256.New() - _, err = io.Copy(h, f) - if err != nil { - return nil, err - } - - return h.Sum(nil), nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/meta_set.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/meta_set.go deleted file mode 100644 index 3a992892..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/meta_set.go +++ /dev/null @@ -1,195 +0,0 @@ -package discovery - -// A PluginMetaSet is a set of PluginMeta objects meeting a certain criteria. -// -// Methods on this type allow filtering of the set to produce subsets that -// meet more restrictive criteria. -type PluginMetaSet map[PluginMeta]struct{} - -// Add inserts the given PluginMeta into the receiving set. This is a no-op -// if the given meta is already present. -func (s PluginMetaSet) Add(p PluginMeta) { - s[p] = struct{}{} -} - -// Remove removes the given PluginMeta from the receiving set. This is a no-op -// if the given meta is not already present. -func (s PluginMetaSet) Remove(p PluginMeta) { - delete(s, p) -} - -// Has returns true if the given meta is in the receiving set, or false -// otherwise. -func (s PluginMetaSet) Has(p PluginMeta) bool { - _, ok := s[p] - return ok -} - -// Count returns the number of metas in the set -func (s PluginMetaSet) Count() int { - return len(s) -} - -// ValidateVersions returns two new PluginMetaSets, separating those with -// versions that have syntax-valid semver versions from those that don't. -// -// Eliminating invalid versions from consideration (and possibly warning about -// them) is usually the first step of working with a meta set after discovery -// has completed. -func (s PluginMetaSet) ValidateVersions() (valid, invalid PluginMetaSet) { - valid = make(PluginMetaSet) - invalid = make(PluginMetaSet) - for p := range s { - if _, err := p.Version.Parse(); err == nil { - valid.Add(p) - } else { - invalid.Add(p) - } - } - return -} - -// WithName returns the subset of metas that have the given name. -func (s PluginMetaSet) WithName(name string) PluginMetaSet { - ns := make(PluginMetaSet) - for p := range s { - if p.Name == name { - ns.Add(p) - } - } - return ns -} - -// WithVersion returns the subset of metas that have the given version. -// -// This should be used only with the "valid" result from ValidateVersions; -// it will ignore any plugin metas that have invalid version strings. -func (s PluginMetaSet) WithVersion(version Version) PluginMetaSet { - ns := make(PluginMetaSet) - for p := range s { - gotVersion, err := p.Version.Parse() - if err != nil { - continue - } - if gotVersion.Equal(version) { - ns.Add(p) - } - } - return ns -} - -// ByName groups the metas in the set by their Names, returning a map. -func (s PluginMetaSet) ByName() map[string]PluginMetaSet { - ret := make(map[string]PluginMetaSet) - for p := range s { - if _, ok := ret[p.Name]; !ok { - ret[p.Name] = make(PluginMetaSet) - } - ret[p.Name].Add(p) - } - return ret -} - -// Newest returns the one item from the set that has the newest Version value. -// -// The result is meaningful only if the set is already filtered such that -// all of the metas have the same Name. -// -// If there isn't at least one meta in the set then this function will panic. -// Use Count() to ensure that there is at least one value before calling. -// -// If any of the metas have invalid version strings then this function will -// panic. Use ValidateVersions() first to filter out metas with invalid -// versions. -// -// If two metas have the same Version then one is arbitrarily chosen. This -// situation should be avoided by pre-filtering the set. -func (s PluginMetaSet) Newest() PluginMeta { - if len(s) == 0 { - panic("can't call NewestStable on empty PluginMetaSet") - } - - var first = true - var winner PluginMeta - var winnerVersion Version - for p := range s { - version, err := p.Version.Parse() - if err != nil { - panic(err) - } - - if first == true || version.NewerThan(winnerVersion) { - winner = p - winnerVersion = version - first = false - } - } - - return winner -} - -// ConstrainVersions takes a set of requirements and attempts to -// return a map from name to a set of metas that have the matching -// name and an appropriate version. -// -// If any of the given requirements match *no* plugins then its PluginMetaSet -// in the returned map will be empty. -// -// All viable metas are returned, so the caller can apply any desired filtering -// to reduce down to a single option. For example, calling Newest() to obtain -// the highest available version. -// -// If any of the metas in the set have invalid version strings then this -// function will panic. Use ValidateVersions() first to filter out metas with -// invalid versions. -func (s PluginMetaSet) ConstrainVersions(reqd PluginRequirements) map[string]PluginMetaSet { - ret := make(map[string]PluginMetaSet) - for p := range s { - name := p.Name - allowedVersions, ok := reqd[name] - if !ok { - continue - } - if _, ok := ret[p.Name]; !ok { - ret[p.Name] = make(PluginMetaSet) - } - version, err := p.Version.Parse() - if err != nil { - panic(err) - } - if allowedVersions.Allows(version) { - ret[p.Name].Add(p) - } - } - return ret -} - -// OverridePaths returns a new set where any existing plugins with the given -// names are removed and replaced with the single path given in the map. -// -// This is here only to continue to support the legacy way of overriding -// plugin binaries in the .terraformrc file. It treats all given plugins -// as pre-versioning (version 0.0.0). This mechanism will eventually be -// phased out, with vendor directories being the intended replacement. -func (s PluginMetaSet) OverridePaths(paths map[string]string) PluginMetaSet { - ret := make(PluginMetaSet) - for p := range s { - if _, ok := paths[p.Name]; ok { - // Skip plugins that we're overridding - continue - } - - ret.Add(p) - } - - // Now add the metadata for overriding plugins - for name, path := range paths { - ret.Add(PluginMeta{ - Name: name, - Version: VersionZero, - Path: path, - }) - } - - return ret -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/requirements.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/requirements.go deleted file mode 100644 index 75430fdd..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/requirements.go +++ /dev/null @@ -1,105 +0,0 @@ -package discovery - -import ( - "bytes" -) - -// PluginRequirements describes a set of plugins (assumed to be of a consistent -// kind) that are required to exist and have versions within the given -// corresponding sets. -type PluginRequirements map[string]*PluginConstraints - -// PluginConstraints represents an element of PluginRequirements describing -// the constraints for a single plugin. -type PluginConstraints struct { - // Specifies that the plugin's version must be within the given - // constraints. - Versions Constraints - - // If non-nil, the hash of the on-disk plugin executable must exactly - // match the SHA256 hash given here. - SHA256 []byte -} - -// Allows returns true if the given version is within the receiver's version -// constraints. -func (s *PluginConstraints) Allows(v Version) bool { - return s.Versions.Allows(v) -} - -// AcceptsSHA256 returns true if the given executable SHA256 hash is acceptable, -// either because it matches the constraint or because there is no such -// constraint. -func (s *PluginConstraints) AcceptsSHA256(digest []byte) bool { - if s.SHA256 == nil { - return true - } - return bytes.Equal(s.SHA256, digest) -} - -// Merge takes the contents of the receiver and the other given requirements -// object and merges them together into a single requirements structure -// that satisfies both sets of requirements. -// -// Note that it doesn't make sense to merge two PluginRequirements with -// differing required plugin SHA256 hashes, since the result will never -// match any plugin. -func (r PluginRequirements) Merge(other PluginRequirements) PluginRequirements { - ret := make(PluginRequirements) - for n, c := range r { - ret[n] = &PluginConstraints{ - Versions: Constraints{}.Append(c.Versions), - SHA256: c.SHA256, - } - } - for n, c := range other { - if existing, exists := ret[n]; exists { - ret[n].Versions = ret[n].Versions.Append(c.Versions) - - if existing.SHA256 != nil { - if c.SHA256 != nil && !bytes.Equal(c.SHA256, existing.SHA256) { - // If we've been asked to merge two constraints with - // different SHA256 hashes then we'll produce a dummy value - // that can never match anything. This is a silly edge case - // that no reasonable caller should hit. - ret[n].SHA256 = []byte(invalidProviderHash) - } - } else { - ret[n].SHA256 = c.SHA256 // might still be nil - } - } else { - ret[n] = &PluginConstraints{ - Versions: Constraints{}.Append(c.Versions), - SHA256: c.SHA256, - } - } - } - return ret -} - -// LockExecutables applies additional constraints to the receiver that -// require plugin executables with specific SHA256 digests. This modifies -// the receiver in-place, since it's intended to be applied after -// version constraints have been resolved. -// -// The given map must include a key for every plugin that is already -// required. If not, any missing keys will cause the corresponding plugin -// to never match, though the direct caller doesn't necessarily need to -// guarantee this as long as the downstream code _applying_ these constraints -// is able to deal with the non-match in some way. -func (r PluginRequirements) LockExecutables(sha256s map[string][]byte) { - for name, cons := range r { - digest := sha256s[name] - - if digest == nil { - // Prevent any match, which will then presumably cause the - // downstream consumer of this requirements to report an error. - cons.SHA256 = []byte(invalidProviderHash) - continue - } - - cons.SHA256 = digest - } -} - -const invalidProviderHash = "" diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/signature.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/signature.go deleted file mode 100644 index 7bbae50c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/signature.go +++ /dev/null @@ -1,19 +0,0 @@ -package discovery - -import ( - "bytes" - "strings" - - "golang.org/x/crypto/openpgp" -) - -// Verify the data using the provided openpgp detached signature and the -// embedded hashicorp public key. -func verifySig(data, sig []byte, armor string) (*openpgp.Entity, error) { - el, err := openpgp.ReadArmoredKeyRing(strings.NewReader(armor)) - if err != nil { - return nil, err - } - - return openpgp.CheckDetachedSignature(el, bytes.NewReader(data), bytes.NewReader(sig)) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/version.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/version.go deleted file mode 100644 index 4311d510..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/version.go +++ /dev/null @@ -1,77 +0,0 @@ -package discovery - -import ( - "fmt" - "sort" - - version "github.com/hashicorp/go-version" -) - -const VersionZero = "0.0.0" - -// A VersionStr is a string containing a possibly-invalid representation -// of a semver version number. Call Parse on it to obtain a real Version -// object, or discover that it is invalid. -type VersionStr string - -// Parse transforms a VersionStr into a Version if it is -// syntactically valid. If it isn't then an error is returned instead. -func (s VersionStr) Parse() (Version, error) { - raw, err := version.NewVersion(string(s)) - if err != nil { - return Version{}, err - } - return Version{raw}, nil -} - -// MustParse transforms a VersionStr into a Version if it is -// syntactically valid. If it isn't then it panics. -func (s VersionStr) MustParse() Version { - ret, err := s.Parse() - if err != nil { - panic(err) - } - return ret -} - -// Version represents a version number that has been parsed from -// a semver string and known to be valid. -type Version struct { - // We wrap this here just because it avoids a proliferation of - // direct go-version imports all over the place, and keeps the - // version-processing details within this package. - raw *version.Version -} - -func (v Version) String() string { - return v.raw.String() -} - -func (v Version) NewerThan(other Version) bool { - return v.raw.GreaterThan(other.raw) -} - -func (v Version) Equal(other Version) bool { - return v.raw.Equal(other.raw) -} - -// IsPrerelease determines if version is a prerelease -func (v Version) IsPrerelease() bool { - return v.raw.Prerelease() != "" -} - -// MinorUpgradeConstraintStr returns a ConstraintStr that would permit -// minor upgrades relative to the receiving version. -func (v Version) MinorUpgradeConstraintStr() ConstraintStr { - segments := v.raw.Segments() - return ConstraintStr(fmt.Sprintf("~> %d.%d", segments[0], segments[1])) -} - -type Versions []Version - -// Sort sorts version from newest to oldest. -func (v Versions) Sort() { - sort.Slice(v, func(i, j int) bool { - return v[i].NewerThan(v[j]) - }) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/version_set.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/version_set.go deleted file mode 100644 index fc8b6f8b..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery/version_set.go +++ /dev/null @@ -1,83 +0,0 @@ -package discovery - -import ( - "sort" - - version "github.com/hashicorp/go-version" -) - -// A ConstraintStr is a string containing a possibly-invalid representation -// of a version constraint provided in configuration. Call Parse on it to -// obtain a real Constraint object, or discover that it is invalid. -type ConstraintStr string - -// Parse transforms a ConstraintStr into a Constraints if it is -// syntactically valid. If it isn't then an error is returned instead. -func (s ConstraintStr) Parse() (Constraints, error) { - raw, err := version.NewConstraint(string(s)) - if err != nil { - return Constraints{}, err - } - return Constraints{raw}, nil -} - -// MustParse is like Parse but it panics if the constraint string is invalid. -func (s ConstraintStr) MustParse() Constraints { - ret, err := s.Parse() - if err != nil { - panic(err) - } - return ret -} - -// Constraints represents a set of versions which any given Version is either -// a member of or not. -type Constraints struct { - raw version.Constraints -} - -// NewConstraints creates a Constraints based on a version.Constraints. -func NewConstraints(c version.Constraints) Constraints { - return Constraints{c} -} - -// AllVersions is a Constraints containing all versions -var AllVersions Constraints - -func init() { - AllVersions = Constraints{ - raw: make(version.Constraints, 0), - } -} - -// Allows returns true if the given version permitted by the receiving -// constraints set. -func (s Constraints) Allows(v Version) bool { - return s.raw.Check(v.raw) -} - -// Append combines the receiving set with the given other set to produce -// a set that is the intersection of both sets, which is to say that resulting -// constraints contain only the versions that are members of both. -func (s Constraints) Append(other Constraints) Constraints { - raw := make(version.Constraints, 0, len(s.raw)+len(other.raw)) - - // Since "raw" is a list of constraints that remove versions from the set, - // "Intersection" is implemented by concatenating together those lists, - // thus leaving behind only the versions not removed by either list. - raw = append(raw, s.raw...) - raw = append(raw, other.raw...) - - // while the set is unordered, we sort these lexically for consistent output - sort.Slice(raw, func(i, j int) bool { - return raw[i].String() < raw[j].String() - }) - - return Constraints{raw} -} - -// String returns a string representation of the set members as a set -// of range constraints. -func (s Constraints) String() string { - return s.raw.String() -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugintest/config.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugintest/config.go new file mode 100644 index 00000000..9b042483 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugintest/config.go @@ -0,0 +1,55 @@ +package plugintest + +import ( + "context" + "fmt" + "io/ioutil" + "os" + "strings" + + "github.com/hashicorp/terraform-exec/tfinstall" +) + +// Config is used to configure the test helper. In most normal test programs +// the configuration is discovered automatically by an Init* function using +// DiscoverConfig, but this is exposed so that more complex scenarios can be +// implemented by direct configuration. +type Config struct { + SourceDir string + TerraformExec string + execTempDir string + PreviousPluginExec string +} + +// DiscoverConfig uses environment variables and other means to automatically +// discover a reasonable test helper configuration. +func DiscoverConfig(sourceDir string) (*Config, error) { + tfVersion := strings.TrimPrefix(os.Getenv("TF_ACC_TERRAFORM_VERSION"), "v") + tfPath := os.Getenv("TF_ACC_TERRAFORM_PATH") + + tempDir := os.Getenv("TF_ACC_TEMP_DIR") + tfDir, err := ioutil.TempDir(tempDir, "plugintest-terraform") + if err != nil { + return nil, fmt.Errorf("failed to create temp dir: %w", err) + } + + finders := []tfinstall.ExecPathFinder{} + switch { + case tfPath != "": + finders = append(finders, tfinstall.ExactPath(tfPath)) + case tfVersion != "": + finders = append(finders, tfinstall.ExactVersion(tfVersion, tfDir)) + default: + finders = append(finders, tfinstall.LookPath(), tfinstall.LatestVersion(tfDir, true)) + } + tfExec, err := tfinstall.Find(context.Background(), finders...) + if err != nil { + return nil, err + } + + return &Config{ + SourceDir: sourceDir, + TerraformExec: tfExec, + execTempDir: tfDir, + }, nil +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugintest/doc.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugintest/doc.go new file mode 100644 index 00000000..3f84c6a3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugintest/doc.go @@ -0,0 +1,7 @@ +// Package plugintest contains utilities to help with writing tests for +// Terraform plugins. +// +// This is not a package for testing configurations or modules written in the +// Terraform language. It is for testing the plugins that allow Terraform to +// manage various cloud services and other APIs. +package plugintest diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugintest/guard.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugintest/guard.go new file mode 100644 index 00000000..56eaa0c1 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugintest/guard.go @@ -0,0 +1,94 @@ +package plugintest + +import ( + "fmt" + "os" + "testing" +) + +// AcceptanceTest is a test guard that will produce a log and call SkipNow on +// the given TestControl if the environment variable TF_ACC isn't set to +// indicate that the caller wants to run acceptance tests. +// +// Call this immediately at the start of each acceptance test function to +// signal that it may cost money and thus requires this opt-in enviromment +// variable. +// +// For the purpose of this function, an "acceptance test" is any est that +// reaches out to services that are not directly controlled by the test program +// itself, particularly if those requests may lead to service charges. For any +// system where it is possible and realistic to run a local instance of the +// service for testing (e.g. in a daemon launched by the test program itself), +// prefer to do this and _don't_ call AcceptanceTest, thus allowing tests to be +// run more easily and without external cost by contributors. +func AcceptanceTest(t TestControl) { + t.Helper() + if os.Getenv("TF_ACC") != "" { + t.Log("TF_ACC is not set") + t.SkipNow() + } +} + +// LongTest is a test guard that will produce a log and call SkipNow on the +// given TestControl if the test harness is currently running in "short mode". +// +// What is considered a "long test" will always be pretty subjective, but test +// implementers should think of this in terms of what seems like it'd be +// inconvenient to run repeatedly for quick feedback while testing a new feature +// under development. +// +// When testing resource types that always take several minutes to complete +// operations, consider having a single general test that covers the basic +// functionality and then mark any other more specific tests as long tests so +// that developers can quickly smoke-test a particular feature when needed +// but can still run the full set of tests for a feature when needed. +func LongTest(t TestControl) { + t.Helper() + if testing.Short() { + t.Log("skipping long test because of short mode") + t.SkipNow() + } +} + +// TestControl is an interface requiring a subset of *testing.T which is used +// by the test guards and helpers in this package. Most callers can simply +// pass their *testing.T value here, but the interface allows other +// implementations to potentially be provided instead, for example to allow +// meta-testing (testing of the test utilities themselves). +// +// This interface also describes the subset of normal test functionality the +// guards and helpers can perform: they can only create log lines, fail tests, +// and skip tests. All other test control is the responsibility of the main +// test code. +type TestControl interface { + Helper() + Log(args ...interface{}) + FailNow() + SkipNow() +} + +// testingT wraps a TestControl to recover some of the convenience behaviors +// that would normally come from a real *testing.T, so we can keep TestControl +// small while still having these conveniences. This is an abstraction +// inversion, but accepted because it makes the public API more convenient +// without any considerable disadvantage. +type testingT struct { + TestControl +} + +func (t testingT) Logf(f string, args ...interface{}) { + t.Helper() + t.Log(fmt.Sprintf(f, args...)) +} + +func (t testingT) Fatalf(f string, args ...interface{}) { + t.Helper() + t.Log(fmt.Sprintf(f, args...)) + t.FailNow() +} + +func (t testingT) Skipf(f string, args ...interface{}) { + t.Helper() + t.Log(fmt.Sprintf(f, args...)) + t.SkipNow() +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugintest/helper.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugintest/helper.go new file mode 100644 index 00000000..4b130b49 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugintest/helper.go @@ -0,0 +1,151 @@ +package plugintest + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/hashicorp/terraform-exec/tfexec" +) + +const subprocessCurrentSigil = "4acd63807899403ca4859f5bb948d2c6" +const subprocessPreviousSigil = "2279afb8cf71423996be1fd65d32f13b" + +// AutoInitProviderHelper is the main entrypoint for testing provider plugins +// using this package. It is intended to be called during TestMain to prepare +// for provider testing. +// +// AutoInitProviderHelper will discover the location of a current Terraform CLI +// executable to test against, detect whether a prior version of the plugin is +// available for upgrade tests, and then will return an object containing the +// results of that initialization which can then be stored in a global variable +// for use in other tests. +func AutoInitProviderHelper(sourceDir string) *Helper { + helper, err := AutoInitHelper(sourceDir) + if err != nil { + fmt.Fprintf(os.Stderr, "cannot run Terraform provider tests: %s\n", err) + os.Exit(1) + } + return helper +} + +// Helper is intended as a per-package singleton created in TestMain which +// other tests in a package can use to create Terraform execution contexts +type Helper struct { + baseDir string + + // sourceDir is the dir containing the provider source code, needed + // for tests that use fixture files. + sourceDir string + terraformExec string + + // execTempDir is created during DiscoverConfig to store any downloaded + // binaries + execTempDir string +} + +// AutoInitHelper uses the auto-discovery behavior of DiscoverConfig to prepare +// a configuration and then calls InitHelper with it. This is a convenient +// way to get the standard init behavior based on environment variables, and +// callers should use this unless they have an unusual requirement that calls +// for constructing a config in a different way. +func AutoInitHelper(sourceDir string) (*Helper, error) { + config, err := DiscoverConfig(sourceDir) + if err != nil { + return nil, err + } + + return InitHelper(config) +} + +// InitHelper prepares a testing helper with the given configuration. +// +// For most callers it is sufficient to call AutoInitHelper instead, which +// will construct a configuration automatically based on certain environment +// variables. +// +// If this function returns an error then it may have left some temporary files +// behind in the system's temporary directory. There is currently no way to +// automatically clean those up. +func InitHelper(config *Config) (*Helper, error) { + tempDir := os.Getenv("TF_ACC_TEMP_DIR") + baseDir, err := ioutil.TempDir(tempDir, "plugintest") + if err != nil { + return nil, fmt.Errorf("failed to create temporary directory for test helper: %s", err) + } + + return &Helper{ + baseDir: baseDir, + sourceDir: config.SourceDir, + terraformExec: config.TerraformExec, + execTempDir: config.execTempDir, + }, nil +} + +// Close cleans up temporary files and directories created to support this +// helper, returning an error if any of the cleanup fails. +// +// Call this before returning from TestMain to minimize the amount of detritus +// left behind in the filesystem after the tests complete. +func (h *Helper) Close() error { + if h.execTempDir != "" { + err := os.RemoveAll(h.execTempDir) + if err != nil { + return err + } + } + return os.RemoveAll(h.baseDir) +} + +// NewWorkingDir creates a new working directory for use in the implementation +// of a single test, returning a WorkingDir object representing that directory. +// +// If the working directory object is not itself closed by the time the test +// program exits, the Close method on the helper itself will attempt to +// delete it. +func (h *Helper) NewWorkingDir() (*WorkingDir, error) { + dir, err := ioutil.TempDir(h.baseDir, "work") + if err != nil { + return nil, err + } + + // symlink the provider source files into the config directory + // e.g. testdata + err = symlinkDirectoriesOnly(h.sourceDir, dir) + if err != nil { + return nil, err + } + + tf, err := tfexec.NewTerraform(dir, h.terraformExec) + if err != nil { + return nil, err + } + + return &WorkingDir{ + h: h, + tf: tf, + baseDir: dir, + terraformExec: h.terraformExec, + }, nil +} + +// RequireNewWorkingDir is a variant of NewWorkingDir that takes a TestControl +// object and will immediately fail the running test if the creation of the +// working directory fails. +func (h *Helper) RequireNewWorkingDir(t TestControl) *WorkingDir { + t.Helper() + + wd, err := h.NewWorkingDir() + if err != nil { + t := testingT{t} + t.Fatalf("failed to create new working directory: %s", err) + return nil + } + return wd +} + +// TerraformExecPath returns the location of the Terraform CLI executable that +// should be used when running tests. +func (h *Helper) TerraformExecPath() string { + return h.terraformExec +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugintest/util.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugintest/util.go new file mode 100644 index 00000000..df1cb92f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugintest/util.go @@ -0,0 +1,95 @@ +package plugintest + +import ( + "os" + "path/filepath" +) + +func symlinkFile(src string, dest string) (err error) { + err = os.Symlink(src, dest) + if err == nil { + srcInfo, err := os.Stat(src) + if err != nil { + err = os.Chmod(dest, srcInfo.Mode()) + } + } + + return +} + +// symlinkDir is a simplistic function for recursively symlinking all files in a directory to a new path. +// It is intended only for limited internal use and does not cover all edge cases. +func symlinkDir(srcDir string, destDir string) (err error) { + srcInfo, err := os.Stat(srcDir) + if err != nil { + return err + } + + err = os.MkdirAll(destDir, srcInfo.Mode()) + if err != nil { + return err + } + + directory, _ := os.Open(srcDir) + defer directory.Close() + objects, err := directory.Readdir(-1) + + for _, obj := range objects { + srcPath := filepath.Join(srcDir, obj.Name()) + destPath := filepath.Join(destDir, obj.Name()) + + if obj.IsDir() { + err = symlinkDir(srcPath, destPath) + if err != nil { + return err + } + } else { + err = symlinkFile(srcPath, destPath) + if err != nil { + return err + } + } + + } + return +} + +// symlinkDirectoriesOnly finds only the first-level child directories in srcDir +// and symlinks them into destDir. +// Unlike symlinkDir, this is done non-recursively in order to limit the number +// of file descriptors used. +func symlinkDirectoriesOnly(srcDir string, destDir string) (err error) { + srcInfo, err := os.Stat(srcDir) + if err != nil { + return err + } + + err = os.MkdirAll(destDir, srcInfo.Mode()) + if err != nil { + return err + } + + directory, err := os.Open(srcDir) + if err != nil { + return err + } + defer directory.Close() + objects, err := directory.Readdir(-1) + if err != nil { + return err + } + + for _, obj := range objects { + srcPath := filepath.Join(srcDir, obj.Name()) + destPath := filepath.Join(destDir, obj.Name()) + + if obj.IsDir() { + err = symlinkFile(srcPath, destPath) + if err != nil { + return err + } + } + + } + return +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugintest/working_dir.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugintest/working_dir.go new file mode 100644 index 00000000..1faeb5c7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/plugintest/working_dir.go @@ -0,0 +1,252 @@ +package plugintest + +import ( + "bytes" + "context" + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "github.com/hashicorp/terraform-exec/tfexec" + tfjson "github.com/hashicorp/terraform-json" +) + +const ( + ConfigFileName = "terraform_plugin_test.tf" + PlanFileName = "tfplan" +) + +// WorkingDir represents a distinct working directory that can be used for +// running tests. Each test should construct its own WorkingDir by calling +// NewWorkingDir or RequireNewWorkingDir on its package's singleton +// plugintest.Helper. +type WorkingDir struct { + h *Helper + + // baseDir is the root of the working directory tree + baseDir string + + // baseArgs is arguments that should be appended to all commands + baseArgs []string + + // tf is the instance of tfexec.Terraform used for running Terraform commands + tf *tfexec.Terraform + + // terraformExec is a path to a terraform binary, inherited from Helper + terraformExec string + + // reattachInfo stores the gRPC socket info required for Terraform's + // plugin reattach functionality + reattachInfo tfexec.ReattachInfo + + env map[string]string +} + +// Close deletes the directories and files created to represent the receiving +// working directory. After this method is called, the working directory object +// is invalid and may no longer be used. +func (wd *WorkingDir) Close() error { + return os.RemoveAll(wd.baseDir) +} + +// Setenv sets an environment variable on the WorkingDir. +func (wd *WorkingDir) Setenv(envVar, val string) { + if wd.env == nil { + wd.env = map[string]string{} + } + wd.env[envVar] = val +} + +// Unsetenv removes an environment variable from the WorkingDir. +func (wd *WorkingDir) Unsetenv(envVar string) { + delete(wd.env, envVar) +} + +func (wd *WorkingDir) SetReattachInfo(reattachInfo tfexec.ReattachInfo) { + wd.reattachInfo = reattachInfo +} + +func (wd *WorkingDir) UnsetReattachInfo() { + wd.reattachInfo = nil +} + +// GetHelper returns the Helper set on the WorkingDir. +func (wd *WorkingDir) GetHelper() *Helper { + return wd.h +} + +// SetConfig sets a new configuration for the working directory. +// +// This must be called at least once before any call to Init, Plan, Apply, or +// Destroy to establish the configuration. Any previously-set configuration is +// discarded and any saved plan is cleared. +func (wd *WorkingDir) SetConfig(cfg string) error { + configFilename := filepath.Join(wd.baseDir, ConfigFileName) + err := ioutil.WriteFile(configFilename, []byte(cfg), 0700) + if err != nil { + return err + } + + var mismatch *tfexec.ErrVersionMismatch + err = wd.tf.SetDisablePluginTLS(true) + if err != nil && !errors.As(err, &mismatch) { + return err + } + err = wd.tf.SetSkipProviderVerify(true) + if err != nil && !errors.As(err, &mismatch) { + return err + } + + if p := os.Getenv("TF_ACC_LOG_PATH"); p != "" { + wd.tf.SetLogPath(p) + } + + // Changing configuration invalidates any saved plan. + err = wd.ClearPlan() + if err != nil { + return err + } + return nil +} + +// ClearState deletes any Terraform state present in the working directory. +// +// Any remote objects tracked by the state are not destroyed first, so this +// will leave them dangling in the remote system. +func (wd *WorkingDir) ClearState() error { + err := os.Remove(filepath.Join(wd.baseDir, "terraform.tfstate")) + if os.IsNotExist(err) { + return nil + } + return err +} + +// ClearPlan deletes any saved plan present in the working directory. +func (wd *WorkingDir) ClearPlan() error { + err := os.Remove(wd.planFilename()) + if os.IsNotExist(err) { + return nil + } + return err +} + +// Init runs "terraform init" for the given working directory, forcing Terraform +// to use the current version of the plugin under test. +func (wd *WorkingDir) Init() error { + if _, err := os.Stat(wd.configFilename()); err != nil { + return fmt.Errorf("must call SetConfig before Init") + } + + return wd.tf.Init(context.Background(), tfexec.Reattach(wd.reattachInfo)) +} + +func (wd *WorkingDir) configFilename() string { + return filepath.Join(wd.baseDir, ConfigFileName) +} + +func (wd *WorkingDir) planFilename() string { + return filepath.Join(wd.baseDir, PlanFileName) +} + +// CreatePlan runs "terraform plan" to create a saved plan file, which if successful +// will then be used for the next call to Apply. +func (wd *WorkingDir) CreatePlan() error { + _, err := wd.tf.Plan(context.Background(), tfexec.Reattach(wd.reattachInfo), tfexec.Refresh(false), tfexec.Out(PlanFileName)) + return err +} + +// CreateDestroyPlan runs "terraform plan -destroy" to create a saved plan +// file, which if successful will then be used for the next call to Apply. +func (wd *WorkingDir) CreateDestroyPlan() error { + _, err := wd.tf.Plan(context.Background(), tfexec.Reattach(wd.reattachInfo), tfexec.Refresh(false), tfexec.Out(PlanFileName), tfexec.Destroy(true)) + return err +} + +// Apply runs "terraform apply". If CreatePlan has previously completed +// successfully and the saved plan has not been cleared in the meantime then +// this will apply the saved plan. Otherwise, it will implicitly create a new +// plan and apply it. +func (wd *WorkingDir) Apply() error { + args := []tfexec.ApplyOption{tfexec.Reattach(wd.reattachInfo), tfexec.Refresh(false)} + if wd.HasSavedPlan() { + args = append(args, tfexec.DirOrPlan(PlanFileName)) + } + + return wd.tf.Apply(context.Background(), args...) +} + +// Destroy runs "terraform destroy". It does not consider or modify any saved +// plan, and is primarily for cleaning up at the end of a test run. +// +// If destroy fails then remote objects might still exist, and continue to +// exist after a particular test is concluded. +func (wd *WorkingDir) Destroy() error { + return wd.tf.Destroy(context.Background(), tfexec.Reattach(wd.reattachInfo), tfexec.Refresh(false)) +} + +// HasSavedPlan returns true if there is a saved plan in the working directory. If +// so, a subsequent call to Apply will apply that saved plan. +func (wd *WorkingDir) HasSavedPlan() bool { + _, err := os.Stat(wd.planFilename()) + return err == nil +} + +// SavedPlan returns an object describing the current saved plan file, if any. +// +// If no plan is saved or if the plan file cannot be read, SavedPlan returns +// an error. +func (wd *WorkingDir) SavedPlan() (*tfjson.Plan, error) { + if !wd.HasSavedPlan() { + return nil, fmt.Errorf("there is no current saved plan") + } + + return wd.tf.ShowPlanFile(context.Background(), wd.planFilename(), tfexec.Reattach(wd.reattachInfo)) +} + +// SavedPlanRawStdout returns a human readable stdout capture of the current saved plan file, if any. +// +// If no plan is saved or if the plan file cannot be read, SavedPlanRawStdout returns +// an error. +func (wd *WorkingDir) SavedPlanRawStdout() (string, error) { + if !wd.HasSavedPlan() { + return "", fmt.Errorf("there is no current saved plan") + } + + var ret bytes.Buffer + + wd.tf.SetStdout(&ret) + defer wd.tf.SetStdout(ioutil.Discard) + _, err := wd.tf.ShowPlanFileRaw(context.Background(), wd.planFilename(), tfexec.Reattach(wd.reattachInfo)) + if err != nil { + return "", err + } + + return ret.String(), nil +} + +// State returns an object describing the current state. +// + +// If the state cannot be read, State returns an error. +func (wd *WorkingDir) State() (*tfjson.State, error) { + return wd.tf.Show(context.Background(), tfexec.Reattach(wd.reattachInfo)) +} + +// Import runs terraform import +func (wd *WorkingDir) Import(resource, id string) error { + return wd.tf.Import(context.Background(), resource, id, tfexec.Config(wd.baseDir), tfexec.Reattach(wd.reattachInfo)) +} + +// Refresh runs terraform refresh +func (wd *WorkingDir) Refresh() error { + return wd.tf.Refresh(context.Background(), tfexec.Reattach(wd.reattachInfo), tfexec.State(filepath.Join(wd.baseDir, "terraform.tfstate"))) +} + +// Schemas returns an object describing the provider schemas. +// +// If the schemas cannot be read, Schemas returns an error. +func (wd *WorkingDir) Schemas() (*tfjson.ProviderSchemas, error) { + return wd.tf.ProvidersSchema(context.Background()) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/providers/addressed_types.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/providers/addressed_types.go deleted file mode 100644 index 0f48f244..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/providers/addressed_types.go +++ /dev/null @@ -1,47 +0,0 @@ -package providers - -import ( - "sort" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// AddressedTypes is a helper that extracts all of the distinct provider -// types from the given list of relative provider configuration addresses. -func AddressedTypes(providerAddrs []addrs.ProviderConfig) []string { - if len(providerAddrs) == 0 { - return nil - } - m := map[string]struct{}{} - for _, addr := range providerAddrs { - m[addr.Type] = struct{}{} - } - - names := make([]string, 0, len(m)) - for typeName := range m { - names = append(names, typeName) - } - - sort.Strings(names) // Stable result for tests - return names -} - -// AddressedTypesAbs is a helper that extracts all of the distinct provider -// types from the given list of absolute provider configuration addresses. -func AddressedTypesAbs(providerAddrs []addrs.AbsProviderConfig) []string { - if len(providerAddrs) == 0 { - return nil - } - m := map[string]struct{}{} - for _, addr := range providerAddrs { - m[addr.ProviderConfig.Type] = struct{}{} - } - - names := make([]string, 0, len(m)) - for typeName := range m { - names = append(names, typeName) - } - - sort.Strings(names) // Stable result for tests - return names -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/providers/doc.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/providers/doc.go deleted file mode 100644 index 39aa1de6..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/providers/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -// Package providers contains the interface and primary types required to -// implement a Terraform resource provider. -package providers diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/providers/provider.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/providers/provider.go index 3d0aa8ec..9bb22871 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/providers/provider.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/providers/provider.go @@ -1,359 +1,12 @@ package providers import ( - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" ) -// Interface represents the set of methods required for a complete resource -// provider plugin. -type Interface interface { - // GetSchema returns the complete schema for the provider. - GetSchema() GetSchemaResponse - - // PrepareProviderConfig allows the provider to validate the configuration - // values, and set or override any values with defaults. - PrepareProviderConfig(PrepareProviderConfigRequest) PrepareProviderConfigResponse - - // ValidateResourceTypeConfig allows the provider to validate the resource - // configuration values. - ValidateResourceTypeConfig(ValidateResourceTypeConfigRequest) ValidateResourceTypeConfigResponse - - // ValidateDataSource allows the provider to validate the data source - // configuration values. - ValidateDataSourceConfig(ValidateDataSourceConfigRequest) ValidateDataSourceConfigResponse - - // UpgradeResourceState is called when the state loader encounters an - // instance state whose schema version is less than the one reported by the - // currently-used version of the corresponding provider, and the upgraded - // result is used for any further processing. - UpgradeResourceState(UpgradeResourceStateRequest) UpgradeResourceStateResponse - - // Configure configures and initialized the provider. - Configure(ConfigureRequest) ConfigureResponse - - // Stop is called when the provider should halt any in-flight actions. - // - // Stop should not block waiting for in-flight actions to complete. It - // should take any action it wants and return immediately acknowledging it - // has received the stop request. Terraform will not make any further API - // calls to the provider after Stop is called. - // - // The error returned, if non-nil, is assumed to mean that signaling the - // stop somehow failed and that the user should expect potentially waiting - // a longer period of time. - Stop() error - - // ReadResource refreshes a resource and returns its current state. - ReadResource(ReadResourceRequest) ReadResourceResponse - - // PlanResourceChange takes the current state and proposed state of a - // resource, and returns the planned final state. - PlanResourceChange(PlanResourceChangeRequest) PlanResourceChangeResponse - - // ApplyResourceChange takes the planned state for a resource, which may - // yet contain unknown computed values, and applies the changes returning - // the final state. - ApplyResourceChange(ApplyResourceChangeRequest) ApplyResourceChangeResponse - - // ImportResourceState requests that the given resource be imported. - ImportResourceState(ImportResourceStateRequest) ImportResourceStateResponse - - // ReadDataSource returns the data source's current state. - ReadDataSource(ReadDataSourceRequest) ReadDataSourceResponse - - // Close shuts down the plugin process if applicable. - Close() error -} - -type GetSchemaResponse struct { - // Provider is the schema for the provider itself. - Provider Schema - - // ResourceTypes map the resource type name to that type's schema. - ResourceTypes map[string]Schema - - // DataSources maps the data source name to that data source's schema. - DataSources map[string]Schema - - // Diagnostics contains any warnings or errors from the method call. - Diagnostics tfdiags.Diagnostics -} - // Schema pairs a provider or resource schema with that schema's version. // This is used to be able to upgrade the schema in UpgradeResourceState. type Schema struct { Version int64 Block *configschema.Block } - -type PrepareProviderConfigRequest struct { - // Config is the raw configuration value for the provider. - Config cty.Value -} - -type PrepareProviderConfigResponse struct { - // PreparedConfig is the configuration as prepared by the provider. - PreparedConfig cty.Value - // Diagnostics contains any warnings or errors from the method call. - Diagnostics tfdiags.Diagnostics -} - -type ValidateResourceTypeConfigRequest struct { - // TypeName is the name of the resource type to validate. - TypeName string - - // Config is the configuration value to validate, which may contain unknown - // values. - Config cty.Value -} - -type ValidateResourceTypeConfigResponse struct { - // Diagnostics contains any warnings or errors from the method call. - Diagnostics tfdiags.Diagnostics -} - -type ValidateDataSourceConfigRequest struct { - // TypeName is the name of the data source type to validate. - TypeName string - - // Config is the configuration value to validate, which may contain unknown - // values. - Config cty.Value -} - -type ValidateDataSourceConfigResponse struct { - // Diagnostics contains any warnings or errors from the method call. - Diagnostics tfdiags.Diagnostics -} - -type UpgradeResourceStateRequest struct { - // TypeName is the name of the resource type being upgraded - TypeName string - - // Version is version of the schema that created the current state. - Version int64 - - // RawStateJSON and RawStateFlatmap contiain the state that needs to be - // upgraded to match the current schema version. Because the schema is - // unknown, this contains only the raw data as stored in the state. - // RawStateJSON is the current json state encoding. - // RawStateFlatmap is the legacy flatmap encoding. - // Only on of these fields may be set for the upgrade request. - RawStateJSON []byte - RawStateFlatmap map[string]string -} - -type UpgradeResourceStateResponse struct { - // UpgradedState is the newly upgraded resource state. - UpgradedState cty.Value - - // Diagnostics contains any warnings or errors from the method call. - Diagnostics tfdiags.Diagnostics -} - -type ConfigureRequest struct { - // Terraform version is the version string from the running instance of - // terraform. Providers can use TerraformVersion to verify compatibility, - // and to store for informational purposes. - TerraformVersion string - - // Config is the complete configuration value for the provider. - Config cty.Value -} - -type ConfigureResponse struct { - // Diagnostics contains any warnings or errors from the method call. - Diagnostics tfdiags.Diagnostics -} - -type ReadResourceRequest struct { - // TypeName is the name of the resource type being read. - TypeName string - - // PriorState contains the previously saved state value for this resource. - PriorState cty.Value - - // Private is an opaque blob that will be stored in state along with the - // resource. It is intended only for interpretation by the provider itself. - Private []byte -} - -type ReadResourceResponse struct { - // NewState contains the current state of the resource. - NewState cty.Value - - // Diagnostics contains any warnings or errors from the method call. - Diagnostics tfdiags.Diagnostics - - // Private is an opaque blob that will be stored in state along with the - // resource. It is intended only for interpretation by the provider itself. - Private []byte -} - -type PlanResourceChangeRequest struct { - // TypeName is the name of the resource type to plan. - TypeName string - - // PriorState is the previously saved state value for this resource. - PriorState cty.Value - - // ProposedNewState is the expected state after the new configuration is - // applied. This is created by directly applying the configuration to the - // PriorState. The provider is then responsible for applying any further - // changes required to create the proposed final state. - ProposedNewState cty.Value - - // Config is the resource configuration, before being merged with the - // PriorState. Any value not explicitly set in the configuration will be - // null. Config is supplied for reference, but Provider implementations - // should prefer the ProposedNewState in most circumstances. - Config cty.Value - - // PriorPrivate is the previously saved private data returned from the - // provider during the last apply. - PriorPrivate []byte -} - -type PlanResourceChangeResponse struct { - // PlannedState is the expected state of the resource once the current - // configuration is applied. - PlannedState cty.Value - - // RequiresReplace is the list of thee attributes that are requiring - // resource replacement. - RequiresReplace []cty.Path - - // PlannedPrivate is an opaque blob that is not interpreted by terraform - // core. This will be saved and relayed back to the provider during - // ApplyResourceChange. - PlannedPrivate []byte - - // Diagnostics contains any warnings or errors from the method call. - Diagnostics tfdiags.Diagnostics - - // LegacyTypeSystem is set only if the provider is using the legacy SDK - // whose type system cannot be precisely mapped into the Terraform type - // system. We use this to bypass certain consistency checks that would - // otherwise fail due to this imprecise mapping. No other provider or SDK - // implementation is permitted to set this. - LegacyTypeSystem bool -} - -type ApplyResourceChangeRequest struct { - // TypeName is the name of the resource type being applied. - TypeName string - - // PriorState is the current state of resource. - PriorState cty.Value - - // Planned state is the state returned from PlanResourceChange, and should - // represent the new state, minus any remaining computed attributes. - PlannedState cty.Value - - // Config is the resource configuration, before being merged with the - // PriorState. Any value not explicitly set in the configuration will be - // null. Config is supplied for reference, but Provider implementations - // should prefer the PlannedState in most circumstances. - Config cty.Value - - // PlannedPrivate is the same value as returned by PlanResourceChange. - PlannedPrivate []byte -} - -type ApplyResourceChangeResponse struct { - // NewState is the new complete state after applying the planned change. - // In the event of an error, NewState should represent the most recent - // known state of the resource, if it exists. - NewState cty.Value - - // Private is an opaque blob that will be stored in state along with the - // resource. It is intended only for interpretation by the provider itself. - Private []byte - - // Diagnostics contains any warnings or errors from the method call. - Diagnostics tfdiags.Diagnostics - - // LegacyTypeSystem is set only if the provider is using the legacy SDK - // whose type system cannot be precisely mapped into the Terraform type - // system. We use this to bypass certain consistency checks that would - // otherwise fail due to this imprecise mapping. No other provider or SDK - // implementation is permitted to set this. - LegacyTypeSystem bool -} - -type ImportResourceStateRequest struct { - // TypeName is the name of the resource type to be imported. - TypeName string - - // ID is a string with which the provider can identify the resource to be - // imported. - ID string -} - -type ImportResourceStateResponse struct { - // ImportedResources contains one or more state values related to the - // imported resource. It is not required that these be complete, only that - // there is enough identifying information for the provider to successfully - // update the states in ReadResource. - ImportedResources []ImportedResource - - // Diagnostics contains any warnings or errors from the method call. - Diagnostics tfdiags.Diagnostics -} - -// ImportedResource represents an object being imported into Terraform with the -// help of a provider. An ImportedObject is a RemoteObject that has been read -// by the provider's import handler but hasn't yet been committed to state. -type ImportedResource struct { - // TypeName is the name of the resource type associated with the - // returned state. It's possible for providers to import multiple related - // types with a single import request. - TypeName string - - // State is the state of the remote object being imported. This may not be - // complete, but must contain enough information to uniquely identify the - // resource. - State cty.Value - - // Private is an opaque blob that will be stored in state along with the - // resource. It is intended only for interpretation by the provider itself. - Private []byte -} - -// AsInstanceObject converts the receiving ImportedObject into a -// ResourceInstanceObject that has status ObjectReady. -// -// The returned object does not know its own resource type, so the caller must -// retain the ResourceType value from the source object if this information is -// needed. -// -// The returned object also has no dependency addresses, but the caller may -// freely modify the direct fields of the returned object without affecting -// the receiver. -func (ir ImportedResource) AsInstanceObject() *states.ResourceInstanceObject { - return &states.ResourceInstanceObject{ - Status: states.ObjectReady, - Value: ir.State, - Private: ir.Private, - } -} - -type ReadDataSourceRequest struct { - // TypeName is the name of the data source type to Read. - TypeName string - - // Config is the complete configuration for the requested data source. - Config cty.Value -} - -type ReadDataSourceResponse struct { - // State is the current state of the requested data source. - State cty.Value - - // Diagnostics contains any warnings or errors from the method call. - Diagnostics tfdiags.Diagnostics -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/providers/resolver.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/providers/resolver.go deleted file mode 100644 index b42e4920..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/providers/resolver.go +++ /dev/null @@ -1,68 +0,0 @@ -package providers - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery" -) - -// Resolver is an interface implemented by objects that are able to resolve -// a given set of resource provider version constraints into Factory -// callbacks. -type Resolver interface { - // Given a constraint map, return a Factory for each requested provider. - // If some or all of the constraints cannot be satisfied, return a non-nil - // slice of errors describing the problems. - ResolveProviders(reqd discovery.PluginRequirements) (map[string]Factory, []error) -} - -// ResolverFunc wraps a callback function and turns it into a Resolver -// implementation, for convenience in situations where a function and its -// associated closure are sufficient as a resolver implementation. -type ResolverFunc func(reqd discovery.PluginRequirements) (map[string]Factory, []error) - -// ResolveProviders implements Resolver by calling the -// wrapped function. -func (f ResolverFunc) ResolveProviders(reqd discovery.PluginRequirements) (map[string]Factory, []error) { - return f(reqd) -} - -// ResolverFixed returns a Resolver that has a fixed set of provider factories -// provided by the caller. The returned resolver ignores version constraints -// entirely and just returns the given factory for each requested provider -// name. -// -// This function is primarily used in tests, to provide mock providers or -// in-process providers under test. -func ResolverFixed(factories map[string]Factory) Resolver { - return ResolverFunc(func(reqd discovery.PluginRequirements) (map[string]Factory, []error) { - ret := make(map[string]Factory, len(reqd)) - var errs []error - for name := range reqd { - if factory, exists := factories[name]; exists { - ret[name] = factory - } else { - errs = append(errs, fmt.Errorf("provider %q is not available", name)) - } - } - return ret, errs - }) -} - -// Factory is a function type that creates a new instance of a resource -// provider, or returns an error if that is impossible. -type Factory func() (Interface, error) - -// FactoryFixed is a helper that creates a Factory that just returns some given -// single provider. -// -// Unlike usual factories, the exact same instance is returned for each call -// to the factory and so this must be used in only specialized situations where -// the caller can take care to either not mutate the given provider at all -// or to mutate it in ways that will not cause unexpected behavior for others -// holding the same reference. -func FactoryFixed(p Interface) Factory { - return func() (Interface, error) { - return p, nil - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/provisioners/doc.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/provisioners/doc.go deleted file mode 100644 index b03ba9a1..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/provisioners/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -// Package provisioners contains the interface and primary types to implement a -// Terraform resource provisioner. -package provisioners diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/provisioners/factory.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/provisioners/factory.go deleted file mode 100644 index 7a9dca0a..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/provisioners/factory.go +++ /dev/null @@ -1,5 +0,0 @@ -package provisioners - -// Factory is a function type that creates a new instance of a resource -// provisioner, or returns an error if that is impossible. -type Factory func() (Interface, error) diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/provisioners/provisioner.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/provisioners/provisioner.go deleted file mode 100644 index 7d8f4076..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/provisioners/provisioner.go +++ /dev/null @@ -1,82 +0,0 @@ -package provisioners - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - "github.com/zclconf/go-cty/cty" -) - -// Interface is the set of methods required for a resource provisioner plugin. -type Interface interface { - // GetSchema returns the schema for the provisioner configuration. - GetSchema() GetSchemaResponse - - // ValidateProvisionerConfig allows the provisioner to validate the - // configuration values. - ValidateProvisionerConfig(ValidateProvisionerConfigRequest) ValidateProvisionerConfigResponse - - // ProvisionResource runs the provisioner with provided configuration. - // ProvisionResource blocks until the execution is complete. - // If the returned diagnostics contain any errors, the resource will be - // left in a tainted state. - ProvisionResource(ProvisionResourceRequest) ProvisionResourceResponse - - // Stop is called to interrupt the provisioner. - // - // Stop should not block waiting for in-flight actions to complete. It - // should take any action it wants and return immediately acknowledging it - // has received the stop request. Terraform will not make any further API - // calls to the provisioner after Stop is called. - // - // The error returned, if non-nil, is assumed to mean that signaling the - // stop somehow failed and that the user should expect potentially waiting - // a longer period of time. - Stop() error - - // Close shuts down the plugin process if applicable. - Close() error -} - -type GetSchemaResponse struct { - // Provisioner contains the schema for this provisioner. - Provisioner *configschema.Block - - // Diagnostics contains any warnings or errors from the method call. - Diagnostics tfdiags.Diagnostics -} - -// UIOutput provides the Output method for resource provisioner -// plugins to write any output to the UI. -// -// Provisioners may call the Output method multiple times while Apply is in -// progress. It is invalid to call Output after Apply returns. -type UIOutput interface { - Output(string) -} - -type ValidateProvisionerConfigRequest struct { - // Config is the complete configuration to be used for the provisioner. - Config cty.Value -} - -type ValidateProvisionerConfigResponse struct { - // Diagnostics contains any warnings or errors from the method call. - Diagnostics tfdiags.Diagnostics -} - -type ProvisionResourceRequest struct { - // Config is the complete provisioner configuration. - Config cty.Value - - // Connection contains any information required to access the resource - // instance. - Connection cty.Value - - // UIOutput is used to return output during the Apply operation. - UIOutput UIOutput -} - -type ProvisionResourceResponse struct { - // Diagnostics contains any warnings or errors from the method call. - Diagnostics tfdiags.Diagnostics -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/client.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/client.go deleted file mode 100644 index 4ef22052..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/client.go +++ /dev/null @@ -1,346 +0,0 @@ -package registry - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" - "net/url" - "path" - "strings" - "time" - - "github.com/hashicorp/terraform-plugin-sdk/httpclient" - internalhttpclient "github.com/hashicorp/terraform-plugin-sdk/internal/httpclient" - "github.com/hashicorp/terraform-plugin-sdk/internal/registry/regsrc" - "github.com/hashicorp/terraform-plugin-sdk/internal/registry/response" - "github.com/hashicorp/terraform-plugin-sdk/internal/version" - "github.com/hashicorp/terraform-svchost" - "github.com/hashicorp/terraform-svchost/disco" -) - -const ( - xTerraformGet = "X-Terraform-Get" - xTerraformVersion = "X-Terraform-Version" - requestTimeout = 10 * time.Second - modulesServiceID = "modules.v1" - providersServiceID = "providers.v1" -) - -var tfVersion = version.String() - -// Client provides methods to query Terraform Registries. -type Client struct { - // this is the client to be used for all requests. - client *http.Client - - // services is a required *disco.Disco, which may have services and - // credentials pre-loaded. - services *disco.Disco -} - -// NewClient returns a new initialized registry client. -func NewClient(services *disco.Disco, client *http.Client) *Client { - if services == nil { - services = disco.New() - } - - if client == nil { - client = internalhttpclient.New() - client.Timeout = requestTimeout - } - - services.Transport = client.Transport - - services.SetUserAgent(httpclient.TerraformUserAgent(version.String())) - - return &Client{ - client: client, - services: services, - } -} - -// Discover queries the host, and returns the url for the registry. -func (c *Client) Discover(host svchost.Hostname, serviceID string) (*url.URL, error) { - service, err := c.services.DiscoverServiceURL(host, serviceID) - if err != nil { - return nil, &ServiceUnreachableError{err} - } - if !strings.HasSuffix(service.Path, "/") { - service.Path += "/" - } - return service, nil -} - -// ModuleVersions queries the registry for a module, and returns the available versions. -func (c *Client) ModuleVersions(module *regsrc.Module) (*response.ModuleVersions, error) { - host, err := module.SvcHost() - if err != nil { - return nil, err - } - - service, err := c.Discover(host, modulesServiceID) - if err != nil { - return nil, err - } - - p, err := url.Parse(path.Join(module.Module(), "versions")) - if err != nil { - return nil, err - } - - service = service.ResolveReference(p) - - log.Printf("[DEBUG] fetching module versions from %q", service) - - req, err := http.NewRequest("GET", service.String(), nil) - if err != nil { - return nil, err - } - - c.addRequestCreds(host, req) - req.Header.Set(xTerraformVersion, tfVersion) - - resp, err := c.client.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - switch resp.StatusCode { - case http.StatusOK: - // OK - case http.StatusNotFound: - return nil, &errModuleNotFound{addr: module} - default: - return nil, fmt.Errorf("error looking up module versions: %s", resp.Status) - } - - var versions response.ModuleVersions - - dec := json.NewDecoder(resp.Body) - if err := dec.Decode(&versions); err != nil { - return nil, err - } - - for _, mod := range versions.Modules { - for _, v := range mod.Versions { - log.Printf("[DEBUG] found available version %q for %s", v.Version, mod.Source) - } - } - - return &versions, nil -} - -func (c *Client) addRequestCreds(host svchost.Hostname, req *http.Request) { - creds, err := c.services.CredentialsForHost(host) - if err != nil { - log.Printf("[WARN] Failed to get credentials for %s: %s (ignoring)", host, err) - return - } - - if creds != nil { - creds.PrepareRequest(req) - } -} - -// ModuleLocation find the download location for a specific version module. -// This returns a string, because the final location may contain special go-getter syntax. -func (c *Client) ModuleLocation(module *regsrc.Module, version string) (string, error) { - host, err := module.SvcHost() - if err != nil { - return "", err - } - - service, err := c.Discover(host, modulesServiceID) - if err != nil { - return "", err - } - - var p *url.URL - if version == "" { - p, err = url.Parse(path.Join(module.Module(), "download")) - } else { - p, err = url.Parse(path.Join(module.Module(), version, "download")) - } - if err != nil { - return "", err - } - download := service.ResolveReference(p) - - log.Printf("[DEBUG] looking up module location from %q", download) - - req, err := http.NewRequest("GET", download.String(), nil) - if err != nil { - return "", err - } - - c.addRequestCreds(host, req) - req.Header.Set(xTerraformVersion, tfVersion) - - resp, err := c.client.Do(req) - if err != nil { - return "", err - } - defer resp.Body.Close() - - // there should be no body, but save it for logging - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return "", fmt.Errorf("error reading response body from registry: %s", err) - } - - switch resp.StatusCode { - case http.StatusOK, http.StatusNoContent: - // OK - case http.StatusNotFound: - return "", fmt.Errorf("module %q version %q not found", module, version) - default: - // anything else is an error: - return "", fmt.Errorf("error getting download location for %q: %s resp:%s", module, resp.Status, body) - } - - // the download location is in the X-Terraform-Get header - location := resp.Header.Get(xTerraformGet) - if location == "" { - return "", fmt.Errorf("failed to get download URL for %q: %s resp:%s", module, resp.Status, body) - } - - // If location looks like it's trying to be a relative URL, treat it as - // one. - // - // We don't do this for just _any_ location, since the X-Terraform-Get - // header is a go-getter location rather than a URL, and so not all - // possible values will parse reasonably as URLs.) - // - // When used in conjunction with go-getter we normally require this header - // to be an absolute URL, but we are more liberal here because third-party - // registry implementations may not "know" their own absolute URLs if - // e.g. they are running behind a reverse proxy frontend, or such. - if strings.HasPrefix(location, "/") || strings.HasPrefix(location, "./") || strings.HasPrefix(location, "../") { - locationURL, err := url.Parse(location) - if err != nil { - return "", fmt.Errorf("invalid relative URL for %q: %s", module, err) - } - locationURL = download.ResolveReference(locationURL) - location = locationURL.String() - } - - return location, nil -} - -// TerraformProviderVersions queries the registry for a provider, and returns the available versions. -func (c *Client) TerraformProviderVersions(provider *regsrc.TerraformProvider) (*response.TerraformProviderVersions, error) { - host, err := provider.SvcHost() - if err != nil { - return nil, err - } - - service, err := c.Discover(host, providersServiceID) - if err != nil { - return nil, err - } - - p, err := url.Parse(path.Join(provider.TerraformProvider(), "versions")) - if err != nil { - return nil, err - } - - service = service.ResolveReference(p) - - log.Printf("[DEBUG] fetching provider versions from %q", service) - - req, err := http.NewRequest("GET", service.String(), nil) - if err != nil { - return nil, err - } - - c.addRequestCreds(host, req) - req.Header.Set(xTerraformVersion, tfVersion) - - resp, err := c.client.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - switch resp.StatusCode { - case http.StatusOK: - // OK - case http.StatusNotFound: - return nil, &errProviderNotFound{addr: provider} - default: - return nil, fmt.Errorf("error looking up provider versions: %s", resp.Status) - } - - var versions response.TerraformProviderVersions - - dec := json.NewDecoder(resp.Body) - if err := dec.Decode(&versions); err != nil { - return nil, err - } - - return &versions, nil -} - -// TerraformProviderLocation queries the registry for a provider download metadata -func (c *Client) TerraformProviderLocation(provider *regsrc.TerraformProvider, version string) (*response.TerraformProviderPlatformLocation, error) { - host, err := provider.SvcHost() - if err != nil { - return nil, err - } - - service, err := c.Discover(host, providersServiceID) - if err != nil { - return nil, err - } - - p, err := url.Parse(path.Join( - provider.TerraformProvider(), - version, - "download", - provider.OS, - provider.Arch, - )) - if err != nil { - return nil, err - } - - service = service.ResolveReference(p) - - log.Printf("[DEBUG] fetching provider location from %q", service) - - req, err := http.NewRequest("GET", service.String(), nil) - if err != nil { - return nil, err - } - - c.addRequestCreds(host, req) - req.Header.Set(xTerraformVersion, tfVersion) - - resp, err := c.client.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - var loc response.TerraformProviderPlatformLocation - - dec := json.NewDecoder(resp.Body) - if err := dec.Decode(&loc); err != nil { - return nil, err - } - - switch resp.StatusCode { - case http.StatusOK, http.StatusNoContent: - // OK - case http.StatusNotFound: - return nil, fmt.Errorf("provider %q version %q not found", provider.TerraformProvider(), version) - default: - // anything else is an error: - return nil, fmt.Errorf("error getting download location for %q: %s", provider.TerraformProvider(), resp.Status) - } - - return &loc, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/errors.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/errors.go deleted file mode 100644 index b05438c4..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/errors.go +++ /dev/null @@ -1,55 +0,0 @@ -package registry - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/registry/regsrc" - "github.com/hashicorp/terraform-svchost/disco" -) - -type errModuleNotFound struct { - addr *regsrc.Module -} - -func (e *errModuleNotFound) Error() string { - return fmt.Sprintf("module %s not found", e.addr) -} - -// IsModuleNotFound returns true only if the given error is a "module not found" -// error. This allows callers to recognize this particular error condition -// as distinct from operational errors such as poor network connectivity. -func IsModuleNotFound(err error) bool { - _, ok := err.(*errModuleNotFound) - return ok -} - -type errProviderNotFound struct { - addr *regsrc.TerraformProvider -} - -func (e *errProviderNotFound) Error() string { - return fmt.Sprintf("provider %s not found", e.addr) -} - -// IsServiceNotProvided returns true only if the given error is a "service not provided" -// error. This allows callers to recognize this particular error condition -// as distinct from operational errors such as poor network connectivity. -func IsServiceNotProvided(err error) bool { - _, ok := err.(*disco.ErrServiceNotProvided) - return ok -} - -// ServiceUnreachableError Registry service is unreachable -type ServiceUnreachableError struct { - err error -} - -func (e *ServiceUnreachableError) Error() string { - return e.err.Error() -} - -// IsServiceUnreachable returns true if the registry/discovery service was unreachable -func IsServiceUnreachable(err error) bool { - _, ok := err.(*ServiceUnreachableError) - return ok -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/regsrc/friendly_host.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/regsrc/friendly_host.go deleted file mode 100644 index c9bc40be..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/regsrc/friendly_host.go +++ /dev/null @@ -1,140 +0,0 @@ -package regsrc - -import ( - "regexp" - "strings" - - "github.com/hashicorp/terraform-svchost" -) - -var ( - // InvalidHostString is a placeholder returned when a raw host can't be - // converted by IDNA spec. It will never be returned for any host for which - // Valid() is true. - InvalidHostString = "" - - // urlLabelEndSubRe is a sub-expression that matches any character that's - // allowed at the start or end of a URL label according to RFC1123. - urlLabelEndSubRe = "[0-9A-Za-z]" - - // urlLabelEndSubRe is a sub-expression that matches any character that's - // allowed at in a non-start or end of a URL label according to RFC1123. - urlLabelMidSubRe = "[0-9A-Za-z-]" - - // urlLabelUnicodeSubRe is a sub-expression that matches any non-ascii char - // in an IDN (Unicode) display URL. It's not strict - there are only ~15k - // valid Unicode points in IDN RFC (some with conditions). We are just going - // with being liberal with matching and then erroring if we fail to convert - // to punycode later (which validates chars fully). This at least ensures - // ascii chars dissalowed by the RC1123 parts above don't become legal - // again. - urlLabelUnicodeSubRe = "[^[:ascii:]]" - - // hostLabelSubRe is the sub-expression that matches a valid hostname label. - // It does not anchor the start or end so it can be composed into more - // complex RegExps below. Note that for sanity we don't handle disallowing - // raw punycode in this regexp (esp. since re2 doesn't support negative - // lookbehind, but we can capture it's presence here to check later). - hostLabelSubRe = "" + - // Match valid initial char, or unicode char - "(?:" + urlLabelEndSubRe + "|" + urlLabelUnicodeSubRe + ")" + - // Optionally, match 0 to 61 valid URL or Unicode chars, - // followed by one valid end char or unicode char - "(?:" + - "(?:" + urlLabelMidSubRe + "|" + urlLabelUnicodeSubRe + "){0,61}" + - "(?:" + urlLabelEndSubRe + "|" + urlLabelUnicodeSubRe + ")" + - ")?" - - // hostSubRe is the sub-expression that matches a valid host prefix. - // Allows custom port. - hostSubRe = hostLabelSubRe + "(?:\\." + hostLabelSubRe + ")+(?::\\d+)?" - - // hostRe is a regexp that matches a valid host prefix. Additional - // validation of unicode strings is needed for matches. - hostRe = regexp.MustCompile("^" + hostSubRe + "$") -) - -// FriendlyHost describes a registry instance identified in source strings by a -// simple bare hostname like registry.terraform.io. -type FriendlyHost struct { - Raw string -} - -func NewFriendlyHost(host string) *FriendlyHost { - return &FriendlyHost{Raw: host} -} - -// ParseFriendlyHost attempts to parse a valid "friendly host" prefix from the -// given string. If no valid prefix is found, host will be nil and rest will -// contain the full source string. The host prefix must terminate at the end of -// the input or at the first / character. If one or more characters exist after -// the first /, they will be returned as rest (without the / delimiter). -// Hostnames containing punycode WILL be parsed successfully since they may have -// come from an internal normalized source string, however should be considered -// invalid if the string came from a user directly. This must be checked -// explicitly for user-input strings by calling Valid() on the -// returned host. -func ParseFriendlyHost(source string) (host *FriendlyHost, rest string) { - parts := strings.SplitN(source, "/", 2) - - if hostRe.MatchString(parts[0]) { - host = &FriendlyHost{Raw: parts[0]} - if len(parts) == 2 { - rest = parts[1] - } - return - } - - // No match, return whole string as rest along with nil host - rest = source - return -} - -// Valid returns whether the host prefix is considered valid in any case. -// Example of invalid prefixes might include ones that don't conform to the host -// name specifications. Not that IDN prefixes containing punycode are not valid -// input which we expect to always be in user-input or normalised display form. -func (h *FriendlyHost) Valid() bool { - return svchost.IsValid(h.Raw) -} - -// Display returns the host formatted for display to the user in CLI or web -// output. -func (h *FriendlyHost) Display() string { - return svchost.ForDisplay(h.Raw) -} - -// Normalized returns the host formatted for internal reference or comparison. -func (h *FriendlyHost) Normalized() string { - host, err := svchost.ForComparison(h.Raw) - if err != nil { - return InvalidHostString - } - return string(host) -} - -// String returns the host formatted as the user originally typed it assuming it -// was parsed from user input. -func (h *FriendlyHost) String() string { - return h.Raw -} - -// Equal compares the FriendlyHost against another instance taking normalization -// into account. Invalid hosts cannot be compared and will always return false. -func (h *FriendlyHost) Equal(other *FriendlyHost) bool { - if other == nil { - return false - } - - otherHost, err := svchost.ForComparison(other.Raw) - if err != nil { - return false - } - - host, err := svchost.ForComparison(h.Raw) - if err != nil { - return false - } - - return otherHost == host -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/regsrc/module.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/regsrc/module.go deleted file mode 100644 index eb37481f..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/regsrc/module.go +++ /dev/null @@ -1,175 +0,0 @@ -package regsrc - -import ( - "errors" - "fmt" - "regexp" - "strings" - - svchost "github.com/hashicorp/terraform-svchost" -) - -var ( - ErrInvalidModuleSource = errors.New("not a valid registry module source") - - // nameSubRe is the sub-expression that matches a valid module namespace or - // name. It's strictly a super-set of what GitHub allows for user/org and - // repo names respectively, but more restrictive than our original repo-name - // regex which allowed periods but could cause ambiguity with hostname - // prefixes. It does not anchor the start or end so it can be composed into - // more complex RegExps below. Alphanumeric with - and _ allowed in non - // leading or trailing positions. Max length 64 chars. (GitHub username is - // 38 max.) - nameSubRe = "[0-9A-Za-z](?:[0-9A-Za-z-_]{0,62}[0-9A-Za-z])?" - - // providerSubRe is the sub-expression that matches a valid provider. It - // does not anchor the start or end so it can be composed into more complex - // RegExps below. Only lowercase chars and digits are supported in practice. - // Max length 64 chars. - providerSubRe = "[0-9a-z]{1,64}" - - // moduleSourceRe is a regular expression that matches the basic - // namespace/name/provider[//...] format for registry sources. It assumes - // any FriendlyHost prefix has already been removed if present. - moduleSourceRe = regexp.MustCompile( - fmt.Sprintf("^(%s)\\/(%s)\\/(%s)(?:\\/\\/(.*))?$", - nameSubRe, nameSubRe, providerSubRe)) - - // these hostnames are not allowed as registry sources, because they are - // already special case module sources in terraform. - disallowed = map[string]bool{ - "github.com": true, - "bitbucket.org": true, - } -) - -// Module describes a Terraform Registry Module source. -type Module struct { - // RawHost is the friendly host prefix if one was present. It might be nil - // if the original source had no host prefix which implies - // PublicRegistryHost but is distinct from having an actual pointer to - // PublicRegistryHost since it encodes the fact the original string didn't - // include a host prefix at all which is significant for recovering actual - // input not just normalized form. Most callers should access it with Host() - // which will return public registry host instance if it's nil. - RawHost *FriendlyHost - RawNamespace string - RawName string - RawProvider string - RawSubmodule string -} - -// ParseModuleSource attempts to parse source as a Terraform registry module -// source. If the string is not found to be in a valid format, -// ErrInvalidModuleSource is returned. Note that this can only be used on -// "input" strings, e.g. either ones supplied by the user or potentially -// normalised but in Display form (unicode). It will fail to parse a source with -// a punycoded domain since this is not permitted input from a user. If you have -// an already normalized string internally, you can compare it without parsing -// by comparing with the normalized version of the subject with the normal -// string equality operator. -func ParseModuleSource(source string) (*Module, error) { - // See if there is a friendly host prefix. - host, rest := ParseFriendlyHost(source) - if host != nil { - if !host.Valid() || disallowed[host.Display()] { - return nil, ErrInvalidModuleSource - } - } - - matches := moduleSourceRe.FindStringSubmatch(rest) - if len(matches) < 4 { - return nil, ErrInvalidModuleSource - } - - m := &Module{ - RawHost: host, - RawNamespace: matches[1], - RawName: matches[2], - RawProvider: matches[3], - } - - if len(matches) == 5 { - m.RawSubmodule = matches[4] - } - - return m, nil -} - -// Display returns the source formatted for display to the user in CLI or web -// output. -func (m *Module) Display() string { - return m.formatWithPrefix(m.normalizedHostPrefix(m.Host().Display()), false) -} - -// Normalized returns the source formatted for internal reference or comparison. -func (m *Module) Normalized() string { - return m.formatWithPrefix(m.normalizedHostPrefix(m.Host().Normalized()), false) -} - -// String returns the source formatted as the user originally typed it assuming -// it was parsed from user input. -func (m *Module) String() string { - // Don't normalize public registry hostname - leave it exactly like the user - // input it. - hostPrefix := "" - if m.RawHost != nil { - hostPrefix = m.RawHost.String() + "/" - } - return m.formatWithPrefix(hostPrefix, true) -} - -// Equal compares the module source against another instance taking -// normalization into account. -func (m *Module) Equal(other *Module) bool { - return m.Normalized() == other.Normalized() -} - -// Host returns the FriendlyHost object describing which registry this module is -// in. If the original source string had not host component this will return the -// PublicRegistryHost. -func (m *Module) Host() *FriendlyHost { - if m.RawHost == nil { - return PublicRegistryHost - } - return m.RawHost -} - -func (m *Module) normalizedHostPrefix(host string) string { - if m.Host().Equal(PublicRegistryHost) { - return "" - } - return host + "/" -} - -func (m *Module) formatWithPrefix(hostPrefix string, preserveCase bool) string { - suffix := "" - if m.RawSubmodule != "" { - suffix = "//" + m.RawSubmodule - } - str := fmt.Sprintf("%s%s/%s/%s%s", hostPrefix, m.RawNamespace, m.RawName, - m.RawProvider, suffix) - - // lower case by default - if !preserveCase { - return strings.ToLower(str) - } - return str -} - -// Module returns just the registry ID of the module, without a hostname or -// suffix. -func (m *Module) Module() string { - return fmt.Sprintf("%s/%s/%s", m.RawNamespace, m.RawName, m.RawProvider) -} - -// SvcHost returns the svchost.Hostname for this module. Since FriendlyHost may -// contain an invalid hostname, this also returns an error indicating if it -// could be converted to a svchost.Hostname. If no host is specified, the -// default PublicRegistryHost is returned. -func (m *Module) SvcHost() (svchost.Hostname, error) { - if m.RawHost == nil { - return svchost.ForComparison(PublicRegistryHost.Raw) - } - return svchost.ForComparison(m.RawHost.Raw) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/regsrc/regsrc.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/regsrc/regsrc.go deleted file mode 100644 index c430bf14..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/regsrc/regsrc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Package regsrc provides helpers for working with source strings that identify -// resources within a Terraform registry. -package regsrc - -var ( - // PublicRegistryHost is a FriendlyHost that represents the public registry. - PublicRegistryHost = NewFriendlyHost("registry.terraform.io") -) diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/regsrc/terraform_provider.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/regsrc/terraform_provider.go deleted file mode 100644 index 7205d03b..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/regsrc/terraform_provider.go +++ /dev/null @@ -1,60 +0,0 @@ -package regsrc - -import ( - "fmt" - "runtime" - "strings" - - "github.com/hashicorp/terraform-svchost" -) - -var ( - // DefaultProviderNamespace represents the namespace for canonical - // HashiCorp-controlled providers. - DefaultProviderNamespace = "-" -) - -// TerraformProvider describes a Terraform Registry Provider source. -type TerraformProvider struct { - RawHost *FriendlyHost - RawNamespace string - RawName string - OS string - Arch string -} - -// NewTerraformProvider constructs a new provider source. -func NewTerraformProvider(name, os, arch string) *TerraformProvider { - if os == "" { - os = runtime.GOOS - } - if arch == "" { - arch = runtime.GOARCH - } - - // separate namespace if included - namespace := DefaultProviderNamespace - if names := strings.SplitN(name, "/", 2); len(names) == 2 { - namespace, name = names[0], names[1] - } - p := &TerraformProvider{ - RawHost: PublicRegistryHost, - RawNamespace: namespace, - RawName: name, - OS: os, - Arch: arch, - } - - return p -} - -// Provider returns just the registry ID of the provider -func (p *TerraformProvider) TerraformProvider() string { - return fmt.Sprintf("%s/%s", p.RawNamespace, p.RawName) -} - -// SvcHost returns the svchost.Hostname for this provider. The -// default PublicRegistryHost is returned. -func (p *TerraformProvider) SvcHost() (svchost.Hostname, error) { - return svchost.ForComparison(PublicRegistryHost.Raw) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/response/module.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/response/module.go deleted file mode 100644 index 06163963..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/response/module.go +++ /dev/null @@ -1,46 +0,0 @@ -package response - -// ModuleSubmodule is the metadata about a specific submodule within -// a module. This includes the root module as a special case. -type ModuleSubmodule struct { - Path string `json:"path"` - Readme string `json:"readme"` - Empty bool `json:"empty"` - - Inputs []*ModuleInput `json:"inputs"` - Outputs []*ModuleOutput `json:"outputs"` - Dependencies []*ModuleDep `json:"dependencies"` - Resources []*ModuleResource `json:"resources"` -} - -// ModuleInput is an input for a module. -type ModuleInput struct { - Name string `json:"name"` - Description string `json:"description"` - Default string `json:"default"` -} - -// ModuleOutput is an output for a module. -type ModuleOutput struct { - Name string `json:"name"` - Description string `json:"description"` -} - -// ModuleDep is an output for a module. -type ModuleDep struct { - Name string `json:"name"` - Source string `json:"source"` - Version string `json:"version"` -} - -// ModuleProviderDep is the output for a provider dependency -type ModuleProviderDep struct { - Name string `json:"name"` - Version string `json:"version"` -} - -// ModuleResource is an output for a module. -type ModuleResource struct { - Name string `json:"name"` - Type string `json:"type"` -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/response/module_versions.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/response/module_versions.go deleted file mode 100644 index f69e9750..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/response/module_versions.go +++ /dev/null @@ -1,32 +0,0 @@ -package response - -// ModuleVersions is the response format that contains all metadata about module -// versions needed for terraform CLI to resolve version constraints. See RFC -// TF-042 for details on this format. -type ModuleVersions struct { - Modules []*ModuleProviderVersions `json:"modules"` -} - -// ModuleProviderVersions is the response format for a single module instance, -// containing metadata about all versions and their dependencies. -type ModuleProviderVersions struct { - Source string `json:"source"` - Versions []*ModuleVersion `json:"versions"` -} - -// ModuleVersion is the output metadata for a given version needed by CLI to -// resolve candidate versions to satisfy requirements. -type ModuleVersion struct { - Version string `json:"version"` - Root VersionSubmodule `json:"root"` - Submodules []*VersionSubmodule `json:"submodules"` -} - -// VersionSubmodule is the output metadata for a submodule within a given -// version needed by CLI to resolve candidate versions to satisfy requirements. -// When representing the Root in JSON the path is omitted. -type VersionSubmodule struct { - Path string `json:"path,omitempty"` - Providers []*ModuleProviderDep `json:"providers"` - Dependencies []*ModuleDep `json:"dependencies"` -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/response/pagination.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/response/pagination.go deleted file mode 100644 index 75a92549..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/response/pagination.go +++ /dev/null @@ -1,65 +0,0 @@ -package response - -import ( - "net/url" - "strconv" -) - -// PaginationMeta is a structure included in responses for pagination. -type PaginationMeta struct { - Limit int `json:"limit"` - CurrentOffset int `json:"current_offset"` - NextOffset *int `json:"next_offset,omitempty"` - PrevOffset *int `json:"prev_offset,omitempty"` - NextURL string `json:"next_url,omitempty"` - PrevURL string `json:"prev_url,omitempty"` -} - -// NewPaginationMeta populates pagination meta data from result parameters -func NewPaginationMeta(offset, limit int, hasMore bool, currentURL string) PaginationMeta { - pm := PaginationMeta{ - Limit: limit, - CurrentOffset: offset, - } - - // Calculate next/prev offsets, leave nil if not valid pages - nextOffset := offset + limit - if hasMore { - pm.NextOffset = &nextOffset - } - - prevOffset := offset - limit - if prevOffset < 0 { - prevOffset = 0 - } - if prevOffset < offset { - pm.PrevOffset = &prevOffset - } - - // If URL format provided, populate URLs. Intentionally swallow URL errors for now, API should - // catch missing URLs if we call with bad URL arg (and we care about them being present). - if currentURL != "" && pm.NextOffset != nil { - pm.NextURL, _ = setQueryParam(currentURL, "offset", *pm.NextOffset, 0) - } - if currentURL != "" && pm.PrevOffset != nil { - pm.PrevURL, _ = setQueryParam(currentURL, "offset", *pm.PrevOffset, 0) - } - - return pm -} - -func setQueryParam(baseURL, key string, val, defaultVal int) (string, error) { - u, err := url.Parse(baseURL) - if err != nil { - return "", err - } - q := u.Query() - if val == defaultVal { - // elide param if it's the default value - q.Del(key) - } else { - q.Set(key, strconv.Itoa(val)) - } - u.RawQuery = q.Encode() - return u.String(), nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/response/terraform_provider.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/response/terraform_provider.go deleted file mode 100644 index c2c333b0..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/registry/response/terraform_provider.go +++ /dev/null @@ -1,95 +0,0 @@ -package response - -import ( - "sort" - "strings" - - version "github.com/hashicorp/go-version" -) - -// TerraformProvider is the response structure for all required information for -// Terraform to choose a download URL. It must include all versions and all -// platforms for Terraform to perform version and os/arch constraint matching -// locally. -type TerraformProvider struct { - ID string `json:"id"` - - Versions []*TerraformProviderVersion `json:"versions"` -} - -// TerraformProviderVersion is the Terraform-specific response structure for a -// provider version. -type TerraformProviderVersion struct { - Version string `json:"version"` - Protocols []string `json:"protocols"` - - Platforms []*TerraformProviderPlatform `json:"platforms"` -} - -// TerraformProviderVersions is the Terraform-specific response structure for an -// array of provider versions -type TerraformProviderVersions struct { - ID string `json:"id"` - Versions []*TerraformProviderVersion `json:"versions"` - Warnings []string `json:"warnings"` -} - -// TerraformProviderPlatform is the Terraform-specific response structure for a -// provider platform. -type TerraformProviderPlatform struct { - OS string `json:"os"` - Arch string `json:"arch"` -} - -// TerraformProviderPlatformLocation is the Terraform-specific response -// structure for a provider platform with all details required to perform a -// download. -type TerraformProviderPlatformLocation struct { - Protocols []string `json:"protocols"` - OS string `json:"os"` - Arch string `json:"arch"` - Filename string `json:"filename"` - DownloadURL string `json:"download_url"` - ShasumsURL string `json:"shasums_url"` - ShasumsSignatureURL string `json:"shasums_signature_url"` - Shasum string `json:"shasum"` - - SigningKeys SigningKeyList `json:"signing_keys"` -} - -// SigningKeyList is the response structure for a list of signing keys. -type SigningKeyList struct { - GPGKeys []*GPGKey `json:"gpg_public_keys"` -} - -// GPGKey is the response structure for a GPG key. -type GPGKey struct { - ASCIIArmor string `json:"ascii_armor"` - Source string `json:"source"` - SourceURL *string `json:"source_url"` -} - -// Collection type for TerraformProviderVersion -type ProviderVersionCollection []*TerraformProviderVersion - -// GPGASCIIArmor returns an ASCII-armor-formatted string for all of the gpg -// keys in the response. -func (signingKeys *SigningKeyList) GPGASCIIArmor() string { - keys := []string{} - - for _, gpgKey := range signingKeys.GPGKeys { - keys = append(keys, gpgKey.ASCIIArmor) - } - - return strings.Join(keys, "\n") -} - -// Sort sorts versions from newest to oldest. -func (v ProviderVersionCollection) Sort() { - sort.Slice(v, func(i, j int) bool { - versionA, _ := version.NewVersion(v[i].Version) - versionB, _ := version.NewVersion(v[j].Version) - - return versionA.GreaterThan(versionB) - }) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/doc.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/doc.go deleted file mode 100644 index 7dd74ac7..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -// Package states contains the types that are used to represent Terraform -// states. -package states diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/eachmode_string.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/eachmode_string.go deleted file mode 100644 index 0dc73499..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/eachmode_string.go +++ /dev/null @@ -1,35 +0,0 @@ -// Code generated by "stringer -type EachMode"; DO NOT EDIT. - -package states - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[NoEach-0] - _ = x[EachList-76] - _ = x[EachMap-77] -} - -const ( - _EachMode_name_0 = "NoEach" - _EachMode_name_1 = "EachListEachMap" -) - -var ( - _EachMode_index_1 = [...]uint8{0, 8, 15} -) - -func (i EachMode) String() string { - switch { - case i == 0: - return _EachMode_name_0 - case 76 <= i && i <= 77: - i -= 76 - return _EachMode_name_1[_EachMode_index_1[i]:_EachMode_index_1[i+1]] - default: - return "EachMode(" + strconv.FormatInt(int64(i), 10) + ")" - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/instance_generation.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/instance_generation.go deleted file mode 100644 index 891adc00..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/instance_generation.go +++ /dev/null @@ -1,20 +0,0 @@ -package states - -// Generation is used to represent multiple objects in a succession of objects -// represented by a single resource instance address. A resource instance can -// have multiple generations over its lifetime due to object replacement -// (when a change can't be applied without destroying and re-creating), and -// multiple generations can exist at the same time when create_before_destroy -// is used. -// -// A Generation value can either be the value of the variable "CurrentGen" or -// a value of type DeposedKey. Generation values can be compared for equality -// using "==" and used as map keys. The zero value of Generation (nil) is not -// a valid generation and must not be used. -type Generation interface { - generation() -} - -// CurrentGen is the Generation representing the currently-active object for -// a resource instance. -var CurrentGen Generation diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/instance_object.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/instance_object.go deleted file mode 100644 index 3bb717d3..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/instance_object.go +++ /dev/null @@ -1,120 +0,0 @@ -package states - -import ( - "github.com/zclconf/go-cty/cty" - ctyjson "github.com/zclconf/go-cty/cty/json" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// ResourceInstanceObject is the local representation of a specific remote -// object associated with a resource instance. In practice not all remote -// objects are actually remote in the sense of being accessed over the network, -// but this is the most common case. -// -// It is not valid to mutate a ResourceInstanceObject once it has been created. -// Instead, create a new object and replace the existing one. -type ResourceInstanceObject struct { - // Value is the object-typed value representing the remote object within - // Terraform. - Value cty.Value - - // Private is an opaque value set by the provider when this object was - // last created or updated. Terraform Core does not use this value in - // any way and it is not exposed anywhere in the user interface, so - // a provider can use it for retaining any necessary private state. - Private []byte - - // Status represents the "readiness" of the object as of the last time - // it was updated. - Status ObjectStatus - - // Dependencies is a set of other addresses in the same module which - // this instance depended on when the given attributes were evaluated. - // This is used to construct the dependency relationships for an object - // whose configuration is no longer available, such as if it has been - // removed from configuration altogether, or is now deposed. - Dependencies []addrs.Referenceable -} - -// ObjectStatus represents the status of a RemoteObject. -type ObjectStatus rune - -//go:generate go run golang.org/x/tools/cmd/stringer -type ObjectStatus - -const ( - // ObjectReady is an object status for an object that is ready to use. - ObjectReady ObjectStatus = 'R' - - // ObjectTainted is an object status representing an object that is in - // an unrecoverable bad state due to a partial failure during a create, - // update, or delete operation. Since it cannot be moved into the - // ObjectRead state, a tainted object must be replaced. - ObjectTainted ObjectStatus = 'T' - - // ObjectPlanned is a special object status used only for the transient - // placeholder objects we place into state during the refresh and plan - // walks to stand in for objects that will be created during apply. - // - // Any object of this status must have a corresponding change recorded - // in the current plan, whose value must then be used in preference to - // the value stored in state when evaluating expressions. A planned - // object stored in state will be incomplete if any of its attributes are - // not yet known, and the plan must be consulted in order to "see" those - // unknown values, because the state is not able to represent them. - ObjectPlanned ObjectStatus = 'P' -) - -// Encode marshals the value within the receiver to produce a -// ResourceInstanceObjectSrc ready to be written to a state file. -// -// The given type must be the implied type of the resource type schema, and -// the given value must conform to it. It is important to pass the schema -// type and not the object's own type so that dynamically-typed attributes -// will be stored correctly. The caller must also provide the version number -// of the schema that the given type was derived from, which will be recorded -// in the source object so it can be used to detect when schema migration is -// required on read. -// -// The returned object may share internal references with the receiver and -// so the caller must not mutate the receiver any further once once this -// method is called. -func (o *ResourceInstanceObject) Encode(ty cty.Type, schemaVersion uint64) (*ResourceInstanceObjectSrc, error) { - // Our state serialization can't represent unknown values, so we convert - // them to nulls here. This is lossy, but nobody should be writing unknown - // values here and expecting to get them out again later. - // - // We get unknown values here while we're building out a "planned state" - // during the plan phase, but the value stored in the plan takes precedence - // for expression evaluation. The apply step should never produce unknown - // values, but if it does it's the responsibility of the caller to detect - // and raise an error about that. - val := cty.UnknownAsNull(o.Value) - - src, err := ctyjson.Marshal(val, ty) - if err != nil { - return nil, err - } - - return &ResourceInstanceObjectSrc{ - SchemaVersion: schemaVersion, - AttrsJSON: src, - Private: o.Private, - Status: o.Status, - Dependencies: o.Dependencies, - }, nil -} - -// AsTainted returns a deep copy of the receiver with the status updated to -// ObjectTainted. -func (o *ResourceInstanceObject) AsTainted() *ResourceInstanceObject { - if o == nil { - // A nil object can't be tainted, but we'll allow this anyway to - // avoid a crash, since we presumably intend to eventually record - // the object has having been deleted anyway. - return nil - } - ret := o.DeepCopy() - ret.Status = ObjectTainted - return ret -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/instance_object_src.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/instance_object_src.go deleted file mode 100644 index 728ad80d..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/instance_object_src.go +++ /dev/null @@ -1,113 +0,0 @@ -package states - -import ( - "github.com/zclconf/go-cty/cty" - ctyjson "github.com/zclconf/go-cty/cty/json" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim" -) - -// ResourceInstanceObjectSrc is a not-fully-decoded version of -// ResourceInstanceObject. Decoding of it can be completed by first handling -// any schema migration steps to get to the latest schema version and then -// calling method Decode with the implied type of the latest schema. -type ResourceInstanceObjectSrc struct { - // SchemaVersion is the resource-type-specific schema version number that - // was current when either AttrsJSON or AttrsFlat was encoded. Migration - // steps are required if this is less than the current version number - // reported by the corresponding provider. - SchemaVersion uint64 - - // AttrsJSON is a JSON-encoded representation of the object attributes, - // encoding the value (of the object type implied by the associated resource - // type schema) that represents this remote object in Terraform Language - // expressions, and is compared with configuration when producing a diff. - // - // This is retained in JSON format here because it may require preprocessing - // before decoding if, for example, the stored attributes are for an older - // schema version which the provider must upgrade before use. If the - // version is current, it is valid to simply decode this using the - // type implied by the current schema, without the need for the provider - // to perform an upgrade first. - // - // When writing a ResourceInstanceObject into the state, AttrsJSON should - // always be conformant to the current schema version and the current - // schema version should be recorded in the SchemaVersion field. - AttrsJSON []byte - - // AttrsFlat is a legacy form of attributes used in older state file - // formats, and in the new state format for objects that haven't yet been - // upgraded. This attribute is mutually exclusive with Attrs: for any - // ResourceInstanceObject, only one of these attributes may be populated - // and the other must be nil. - // - // An instance object with this field populated should be upgraded to use - // Attrs at the earliest opportunity, since this legacy flatmap-based - // format will be phased out over time. AttrsFlat should not be used when - // writing new or updated objects to state; instead, callers must follow - // the recommendations in the AttrsJSON documentation above. - AttrsFlat map[string]string - - // These fields all correspond to the fields of the same name on - // ResourceInstanceObject. - Private []byte - Status ObjectStatus - Dependencies []addrs.Referenceable -} - -// Decode unmarshals the raw representation of the object attributes. Pass the -// implied type of the corresponding resource type schema for correct operation. -// -// Before calling Decode, the caller must check that the SchemaVersion field -// exactly equals the version number of the schema whose implied type is being -// passed, or else the result is undefined. -// -// The returned object may share internal references with the receiver and -// so the caller must not mutate the receiver any further once once this -// method is called. -func (os *ResourceInstanceObjectSrc) Decode(ty cty.Type) (*ResourceInstanceObject, error) { - var val cty.Value - var err error - if os.AttrsFlat != nil { - // Legacy mode. We'll do our best to unpick this from the flatmap. - val, err = hcl2shim.HCL2ValueFromFlatmap(os.AttrsFlat, ty) - if err != nil { - return nil, err - } - } else { - val, err = ctyjson.Unmarshal(os.AttrsJSON, ty) - if err != nil { - return nil, err - } - } - - return &ResourceInstanceObject{ - Value: val, - Status: os.Status, - Dependencies: os.Dependencies, - Private: os.Private, - }, nil -} - -// CompleteUpgrade creates a new ResourceInstanceObjectSrc by copying the -// metadata from the receiver and writing in the given new schema version -// and attribute value that are presumed to have resulted from upgrading -// from an older schema version. -func (os *ResourceInstanceObjectSrc) CompleteUpgrade(newAttrs cty.Value, newType cty.Type, newSchemaVersion uint64) (*ResourceInstanceObjectSrc, error) { - new := os.DeepCopy() - new.AttrsFlat = nil // We always use JSON after an upgrade, even if the source used flatmap - - // This is the same principle as ResourceInstanceObject.Encode, but - // avoiding a decode/re-encode cycle because we don't have type info - // available for the "old" attributes. - newAttrs = cty.UnknownAsNull(newAttrs) - src, err := ctyjson.Marshal(newAttrs, newType) - if err != nil { - return nil, err - } - - new.AttrsJSON = src - new.SchemaVersion = newSchemaVersion - return new, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/module.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/module.go deleted file mode 100644 index 6b74cbfa..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/module.go +++ /dev/null @@ -1,268 +0,0 @@ -package states - -import ( - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// Module is a container for the states of objects within a particular module. -type Module struct { - Addr addrs.ModuleInstance - - // Resources contains the state for each resource. The keys in this map are - // an implementation detail and must not be used by outside callers. - Resources map[string]*Resource - - // OutputValues contains the state for each output value. The keys in this - // map are output value names. - OutputValues map[string]*OutputValue - - // LocalValues contains the value for each named output value. The keys - // in this map are local value names. - LocalValues map[string]cty.Value -} - -// NewModule constructs an empty module state for the given module address. -func NewModule(addr addrs.ModuleInstance) *Module { - return &Module{ - Addr: addr, - Resources: map[string]*Resource{}, - OutputValues: map[string]*OutputValue{}, - LocalValues: map[string]cty.Value{}, - } -} - -// Resource returns the state for the resource with the given address within -// the receiving module state, or nil if the requested resource is not tracked -// in the state. -func (ms *Module) Resource(addr addrs.Resource) *Resource { - return ms.Resources[addr.String()] -} - -// ResourceInstance returns the state for the resource instance with the given -// address within the receiving module state, or nil if the requested instance -// is not tracked in the state. -func (ms *Module) ResourceInstance(addr addrs.ResourceInstance) *ResourceInstance { - rs := ms.Resource(addr.Resource) - if rs == nil { - return nil - } - return rs.Instance(addr.Key) -} - -// SetResourceMeta updates the resource-level metadata for the resource -// with the given address, creating the resource state for it if it doesn't -// already exist. -func (ms *Module) SetResourceMeta(addr addrs.Resource, eachMode EachMode, provider addrs.AbsProviderConfig) { - rs := ms.Resource(addr) - if rs == nil { - rs = &Resource{ - Addr: addr, - Instances: map[addrs.InstanceKey]*ResourceInstance{}, - } - ms.Resources[addr.String()] = rs - } - - rs.EachMode = eachMode - rs.ProviderConfig = provider -} - -// RemoveResource removes the entire state for the given resource, taking with -// it any instances associated with the resource. This should generally be -// called only for resource objects whose instances have all been destroyed. -func (ms *Module) RemoveResource(addr addrs.Resource) { - delete(ms.Resources, addr.String()) -} - -// SetResourceInstanceCurrent saves the given instance object as the current -// generation of the resource instance with the given address, simulataneously -// updating the recorded provider configuration address, dependencies, and -// resource EachMode. -// -// Any existing current instance object for the given resource is overwritten. -// Set obj to nil to remove the primary generation object altogether. If there -// are no deposed objects then the instance will be removed altogether. -// -// The provider address and "each mode" are resource-wide settings and so they -// are updated for all other instances of the same resource as a side-effect of -// this call. -func (ms *Module) SetResourceInstanceCurrent(addr addrs.ResourceInstance, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) { - ms.SetResourceMeta(addr.Resource, eachModeForInstanceKey(addr.Key), provider) - - rs := ms.Resource(addr.Resource) - is := rs.EnsureInstance(addr.Key) - - is.Current = obj - - if !is.HasObjects() { - // If we have no objects at all then we'll clean up. - delete(rs.Instances, addr.Key) - } - if rs.EachMode == NoEach && len(rs.Instances) == 0 { - // Also clean up if we only expect to have one instance anyway - // and there are none. We leave the resource behind if an each mode - // is active because an empty list or map of instances is a valid state. - delete(ms.Resources, addr.Resource.String()) - } -} - -// SetResourceInstanceDeposed saves the given instance object as a deposed -// generation of the resource instance with the given address and deposed key. -// -// Call this method only for pre-existing deposed objects that already have -// a known DeposedKey. For example, this method is useful if reloading objects -// that were persisted to a state file. To mark the current object as deposed, -// use DeposeResourceInstanceObject instead. -// -// The resource that contains the given instance must already exist in the -// state, or this method will panic. Use Resource to check first if its -// presence is not already guaranteed. -// -// Any existing current instance object for the given resource and deposed key -// is overwritten. Set obj to nil to remove the deposed object altogether. If -// the instance is left with no objects after this operation then it will -// be removed from its containing resource altogether. -func (ms *Module) SetResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) { - ms.SetResourceMeta(addr.Resource, eachModeForInstanceKey(addr.Key), provider) - - rs := ms.Resource(addr.Resource) - is := rs.EnsureInstance(addr.Key) - if obj != nil { - is.Deposed[key] = obj - } else { - delete(is.Deposed, key) - } - - if !is.HasObjects() { - // If we have no objects at all then we'll clean up. - delete(rs.Instances, addr.Key) - } - if rs.EachMode == NoEach && len(rs.Instances) == 0 { - // Also clean up if we only expect to have one instance anyway - // and there are none. We leave the resource behind if an each mode - // is active because an empty list or map of instances is a valid state. - delete(ms.Resources, addr.Resource.String()) - } -} - -// ForgetResourceInstanceDeposed removes the record of the deposed object with -// the given address and key, if present. If not present, this is a no-op. -func (ms *Module) ForgetResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey) { - rs := ms.Resource(addr.Resource) - if rs == nil { - return - } - is := rs.Instance(addr.Key) - if is == nil { - return - } - delete(is.Deposed, key) - - if !is.HasObjects() { - // If we have no objects at all then we'll clean up. - delete(rs.Instances, addr.Key) - } - if rs.EachMode == NoEach && len(rs.Instances) == 0 { - // Also clean up if we only expect to have one instance anyway - // and there are none. We leave the resource behind if an each mode - // is active because an empty list or map of instances is a valid state. - delete(ms.Resources, addr.Resource.String()) - } -} - -// deposeResourceInstanceObject is the real implementation of -// SyncState.DeposeResourceInstanceObject. -func (ms *Module) deposeResourceInstanceObject(addr addrs.ResourceInstance, forceKey DeposedKey) DeposedKey { - is := ms.ResourceInstance(addr) - if is == nil { - return NotDeposed - } - return is.deposeCurrentObject(forceKey) -} - -// maybeRestoreResourceInstanceDeposed is the real implementation of -// SyncState.MaybeRestoreResourceInstanceDeposed. -func (ms *Module) maybeRestoreResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey) bool { - rs := ms.Resource(addr.Resource) - if rs == nil { - return false - } - is := rs.Instance(addr.Key) - if is == nil { - return false - } - if is.Current != nil { - return false - } - if len(is.Deposed) == 0 { - return false - } - is.Current = is.Deposed[key] - delete(is.Deposed, key) - return true -} - -// SetOutputValue writes an output value into the state, overwriting any -// existing value of the same name. -func (ms *Module) SetOutputValue(name string, value cty.Value, sensitive bool) *OutputValue { - os := &OutputValue{ - Value: value, - Sensitive: sensitive, - } - ms.OutputValues[name] = os - return os -} - -// RemoveOutputValue removes the output value of the given name from the state, -// if it exists. This method is a no-op if there is no value of the given -// name. -func (ms *Module) RemoveOutputValue(name string) { - delete(ms.OutputValues, name) -} - -// SetLocalValue writes a local value into the state, overwriting any -// existing value of the same name. -func (ms *Module) SetLocalValue(name string, value cty.Value) { - ms.LocalValues[name] = value -} - -// RemoveLocalValue removes the local value of the given name from the state, -// if it exists. This method is a no-op if there is no value of the given -// name. -func (ms *Module) RemoveLocalValue(name string) { - delete(ms.LocalValues, name) -} - -// PruneResourceHusks is a specialized method that will remove any Resource -// objects that do not contain any instances, even if they have an EachMode. -// -// You probably shouldn't call this! See the method of the same name on -// type State for more information on what this is for and the rare situations -// where it is safe to use. -func (ms *Module) PruneResourceHusks() { - for _, rs := range ms.Resources { - if len(rs.Instances) == 0 { - ms.RemoveResource(rs.Addr) - } - } -} - -// empty returns true if the receving module state is contributing nothing -// to the state. In other words, it returns true if the module could be -// removed from the state altogether without changing the meaning of the state. -// -// In practice a module containing no objects is the same as a non-existent -// module, and so we can opportunistically clean up once a module becomes -// empty on the assumption that it will be re-added if needed later. -func (ms *Module) empty() bool { - if ms == nil { - return true - } - - // This must be updated to cover any new collections added to Module - // in future. - return (len(ms.Resources) == 0 && - len(ms.OutputValues) == 0 && - len(ms.LocalValues) == 0) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/objectstatus_string.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/objectstatus_string.go deleted file mode 100644 index 96a6db2f..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/objectstatus_string.go +++ /dev/null @@ -1,33 +0,0 @@ -// Code generated by "stringer -type ObjectStatus"; DO NOT EDIT. - -package states - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[ObjectReady-82] - _ = x[ObjectTainted-84] - _ = x[ObjectPlanned-80] -} - -const ( - _ObjectStatus_name_0 = "ObjectPlanned" - _ObjectStatus_name_1 = "ObjectReady" - _ObjectStatus_name_2 = "ObjectTainted" -) - -func (i ObjectStatus) String() string { - switch { - case i == 80: - return _ObjectStatus_name_0 - case i == 82: - return _ObjectStatus_name_1 - case i == 84: - return _ObjectStatus_name_2 - default: - return "ObjectStatus(" + strconv.FormatInt(int64(i), 10) + ")" - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/output_value.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/output_value.go deleted file mode 100644 index d232b76d..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/output_value.go +++ /dev/null @@ -1,14 +0,0 @@ -package states - -import ( - "github.com/zclconf/go-cty/cty" -) - -// OutputValue represents the state of a particular output value. -// -// It is not valid to mutate an OutputValue object once it has been created. -// Instead, create an entirely new OutputValue to replace the previous one. -type OutputValue struct { - Value cty.Value - Sensitive bool -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/resource.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/resource.go deleted file mode 100644 index 32ea638a..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/resource.go +++ /dev/null @@ -1,233 +0,0 @@ -package states - -import ( - "fmt" - "math/rand" - "time" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// Resource represents the state of a resource. -type Resource struct { - // Addr is the module-relative address for the resource this state object - // belongs to. - Addr addrs.Resource - - // EachMode is the multi-instance mode currently in use for this resource, - // or NoEach if this is a single-instance resource. This dictates what - // type of value is returned when accessing this resource via expressions - // in the Terraform language. - EachMode EachMode - - // Instances contains the potentially-multiple instances associated with - // this resource. This map can contain a mixture of different key types, - // but only the ones of InstanceKeyType are considered current. - Instances map[addrs.InstanceKey]*ResourceInstance - - // ProviderConfig is the absolute address for the provider configuration that - // most recently managed this resource. This is used to connect a resource - // with a provider configuration when the resource configuration block is - // not available, such as if it has been removed from configuration - // altogether. - ProviderConfig addrs.AbsProviderConfig -} - -// Instance returns the state for the instance with the given key, or nil -// if no such instance is tracked within the state. -func (rs *Resource) Instance(key addrs.InstanceKey) *ResourceInstance { - return rs.Instances[key] -} - -// EnsureInstance returns the state for the instance with the given key, -// creating a new empty state for it if one doesn't already exist. -// -// Because this may create and save a new state, it is considered to be -// a write operation. -func (rs *Resource) EnsureInstance(key addrs.InstanceKey) *ResourceInstance { - ret := rs.Instance(key) - if ret == nil { - ret = NewResourceInstance() - rs.Instances[key] = ret - } - return ret -} - -// ResourceInstance represents the state of a particular instance of a resource. -type ResourceInstance struct { - // Current, if non-nil, is the remote object that is currently represented - // by the corresponding resource instance. - Current *ResourceInstanceObjectSrc - - // Deposed, if len > 0, contains any remote objects that were previously - // represented by the corresponding resource instance but have been - // replaced and are pending destruction due to the create_before_destroy - // lifecycle mode. - Deposed map[DeposedKey]*ResourceInstanceObjectSrc -} - -// NewResourceInstance constructs and returns a new ResourceInstance, ready to -// use. -func NewResourceInstance() *ResourceInstance { - return &ResourceInstance{ - Deposed: map[DeposedKey]*ResourceInstanceObjectSrc{}, - } -} - -// HasCurrent returns true if this resource instance has a "current"-generation -// object. Most instances do, but this can briefly be false during a -// create-before-destroy replace operation when the current has been deposed -// but its replacement has not yet been created. -func (i *ResourceInstance) HasCurrent() bool { - return i != nil && i.Current != nil -} - -// HasDeposed returns true if this resource instance has a deposed object -// with the given key. -func (i *ResourceInstance) HasDeposed(key DeposedKey) bool { - return i != nil && i.Deposed[key] != nil -} - -// HasObjects returns true if this resource has any objects at all, whether -// current or deposed. -func (i *ResourceInstance) HasObjects() bool { - return i.Current != nil || len(i.Deposed) != 0 -} - -// deposeCurrentObject is part of the real implementation of -// SyncState.DeposeResourceInstanceObject. The exported method uses a lock -// to ensure that we can safely allocate an unused deposed key without -// collision. -func (i *ResourceInstance) deposeCurrentObject(forceKey DeposedKey) DeposedKey { - if !i.HasCurrent() { - return NotDeposed - } - - key := forceKey - if key == NotDeposed { - key = i.findUnusedDeposedKey() - } else { - if _, exists := i.Deposed[key]; exists { - panic(fmt.Sprintf("forced key %s is already in use", forceKey)) - } - } - i.Deposed[key] = i.Current - i.Current = nil - return key -} - -// GetGeneration retrieves the object of the given generation from the -// ResourceInstance, or returns nil if there is no such object. -// -// If the given generation is nil or invalid, this method will panic. -func (i *ResourceInstance) GetGeneration(gen Generation) *ResourceInstanceObjectSrc { - if gen == CurrentGen { - return i.Current - } - if dk, ok := gen.(DeposedKey); ok { - return i.Deposed[dk] - } - if gen == nil { - panic(fmt.Sprintf("get with nil Generation")) - } - // Should never fall out here, since the above covers all possible - // Generation values. - panic(fmt.Sprintf("get invalid Generation %#v", gen)) -} - -// FindUnusedDeposedKey generates a unique DeposedKey that is guaranteed not to -// already be in use for this instance at the time of the call. -// -// Note that the validity of this result may change if new deposed keys are -// allocated before it is used. To avoid this risk, instead use the -// DeposeResourceInstanceObject method on the SyncState wrapper type, which -// allocates a key and uses it atomically. -func (i *ResourceInstance) FindUnusedDeposedKey() DeposedKey { - return i.findUnusedDeposedKey() -} - -// findUnusedDeposedKey generates a unique DeposedKey that is guaranteed not to -// already be in use for this instance. -func (i *ResourceInstance) findUnusedDeposedKey() DeposedKey { - for { - key := NewDeposedKey() - if _, exists := i.Deposed[key]; !exists { - return key - } - // Spin until we find a unique one. This shouldn't take long, because - // we have a 32-bit keyspace and there's rarely more than one deposed - // instance. - } -} - -// EachMode specifies the multi-instance mode for a resource. -type EachMode rune - -const ( - NoEach EachMode = 0 - EachList EachMode = 'L' - EachMap EachMode = 'M' -) - -//go:generate go run golang.org/x/tools/cmd/stringer -type EachMode - -func eachModeForInstanceKey(key addrs.InstanceKey) EachMode { - switch key.(type) { - case addrs.IntKey: - return EachList - case addrs.StringKey: - return EachMap - default: - if key == addrs.NoKey { - return NoEach - } - panic(fmt.Sprintf("don't know an each mode for instance key %#v", key)) - } -} - -// DeposedKey is a 8-character hex string used to uniquely identify deposed -// instance objects in the state. -type DeposedKey string - -// NotDeposed is a special invalid value of DeposedKey that is used to represent -// the absense of a deposed key. It must not be used as an actual deposed key. -const NotDeposed = DeposedKey("") - -var deposedKeyRand = rand.New(rand.NewSource(time.Now().UnixNano())) - -// NewDeposedKey generates a pseudo-random deposed key. Because of the short -// length of these keys, uniqueness is not a natural consequence and so the -// caller should test to see if the generated key is already in use and generate -// another if so, until a unique key is found. -func NewDeposedKey() DeposedKey { - v := deposedKeyRand.Uint32() - return DeposedKey(fmt.Sprintf("%08x", v)) -} - -func (k DeposedKey) String() string { - return string(k) -} - -func (k DeposedKey) GoString() string { - ks := string(k) - switch { - case ks == "": - return "states.NotDeposed" - default: - return fmt.Sprintf("states.DeposedKey(%s)", ks) - } -} - -// Generation is a helper method to convert a DeposedKey into a Generation. -// If the reciever is anything other than NotDeposed then the result is -// just the same value as a Generation. If the receiver is NotDeposed then -// the result is CurrentGen. -func (k DeposedKey) Generation() Generation { - if k == NotDeposed { - return CurrentGen - } - return k -} - -// generation is an implementation of Generation. -func (k DeposedKey) generation() {} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/state.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/state.go deleted file mode 100644 index 328dd53d..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/state.go +++ /dev/null @@ -1,229 +0,0 @@ -package states - -import ( - "sort" - - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// State is the top-level type of a Terraform state. -// -// A state should be mutated only via its accessor methods, to ensure that -// invariants are preserved. -// -// Access to State and the nested values within it is not concurrency-safe, -// so when accessing a State object concurrently it is the caller's -// responsibility to ensure that only one write is in progress at a time -// and that reads only occur when no write is in progress. The most common -// way to acheive this is to wrap the State in a SyncState and use the -// higher-level atomic operations supported by that type. -type State struct { - // Modules contains the state for each module. The keys in this map are - // an implementation detail and must not be used by outside callers. - Modules map[string]*Module -} - -// NewState constructs a minimal empty state, containing an empty root module. -func NewState() *State { - modules := map[string]*Module{} - modules[addrs.RootModuleInstance.String()] = NewModule(addrs.RootModuleInstance) - return &State{ - Modules: modules, - } -} - -// BuildState is a helper -- primarily intended for tests -- to build a state -// using imperative code against the StateSync type while still acting as -// an expression of type *State to assign into a containing struct. -func BuildState(cb func(*SyncState)) *State { - s := NewState() - cb(s.SyncWrapper()) - return s -} - -// Empty returns true if there are no resources or populated output values -// in the receiver. In other words, if this state could be safely replaced -// with the return value of NewState and be functionally equivalent. -func (s *State) Empty() bool { - if s == nil { - return true - } - for _, ms := range s.Modules { - if len(ms.Resources) != 0 { - return false - } - if len(ms.OutputValues) != 0 { - return false - } - } - return true -} - -// Module returns the state for the module with the given address, or nil if -// the requested module is not tracked in the state. -func (s *State) Module(addr addrs.ModuleInstance) *Module { - if s == nil { - panic("State.Module on nil *State") - } - return s.Modules[addr.String()] -} - -// RemoveModule removes the module with the given address from the state, -// unless it is the root module. The root module cannot be deleted, and so -// this method will panic if that is attempted. -// -// Removing a module implicitly discards all of the resources, outputs and -// local values within it, and so this should usually be done only for empty -// modules. For callers accessing the state through a SyncState wrapper, modules -// are automatically pruned if they are empty after one of their contained -// elements is removed. -func (s *State) RemoveModule(addr addrs.ModuleInstance) { - if addr.IsRoot() { - panic("attempted to remove root module") - } - - delete(s.Modules, addr.String()) -} - -// RootModule is a convenient alias for Module(addrs.RootModuleInstance). -func (s *State) RootModule() *Module { - if s == nil { - panic("RootModule called on nil State") - } - return s.Modules[addrs.RootModuleInstance.String()] -} - -// EnsureModule returns the state for the module with the given address, -// creating and adding a new one if necessary. -// -// Since this might modify the state to add a new instance, it is considered -// to be a write operation. -func (s *State) EnsureModule(addr addrs.ModuleInstance) *Module { - ms := s.Module(addr) - if ms == nil { - ms = NewModule(addr) - s.Modules[addr.String()] = ms - } - return ms -} - -// HasResources returns true if there is at least one resource (of any mode) -// present in the receiving state. -func (s *State) HasResources() bool { - if s == nil { - return false - } - for _, ms := range s.Modules { - if len(ms.Resources) > 0 { - return true - } - } - return false -} - -// Resource returns the state for the resource with the given address, or nil -// if no such resource is tracked in the state. -func (s *State) Resource(addr addrs.AbsResource) *Resource { - ms := s.Module(addr.Module) - if ms == nil { - return nil - } - return ms.Resource(addr.Resource) -} - -// ResourceInstance returns the state for the resource instance with the given -// address, or nil if no such resource is tracked in the state. -func (s *State) ResourceInstance(addr addrs.AbsResourceInstance) *ResourceInstance { - if s == nil { - panic("State.ResourceInstance on nil *State") - } - ms := s.Module(addr.Module) - if ms == nil { - return nil - } - return ms.ResourceInstance(addr.Resource) -} - -// OutputValue returns the state for the output value with the given address, -// or nil if no such output value is tracked in the state. -func (s *State) OutputValue(addr addrs.AbsOutputValue) *OutputValue { - ms := s.Module(addr.Module) - if ms == nil { - return nil - } - return ms.OutputValues[addr.OutputValue.Name] -} - -// LocalValue returns the value of the named local value with the given address, -// or cty.NilVal if no such value is tracked in the state. -func (s *State) LocalValue(addr addrs.AbsLocalValue) cty.Value { - ms := s.Module(addr.Module) - if ms == nil { - return cty.NilVal - } - return ms.LocalValues[addr.LocalValue.Name] -} - -// ProviderAddrs returns a list of all of the provider configuration addresses -// referenced throughout the receiving state. -// -// The result is de-duplicated so that each distinct address appears only once. -func (s *State) ProviderAddrs() []addrs.AbsProviderConfig { - if s == nil { - return nil - } - - m := map[string]addrs.AbsProviderConfig{} - for _, ms := range s.Modules { - for _, rc := range ms.Resources { - m[rc.ProviderConfig.String()] = rc.ProviderConfig - } - } - if len(m) == 0 { - return nil - } - - // This is mainly just so we'll get stable results for testing purposes. - keys := make([]string, 0, len(m)) - for k := range m { - keys = append(keys, k) - } - sort.Strings(keys) - - ret := make([]addrs.AbsProviderConfig, len(keys)) - for i, key := range keys { - ret[i] = m[key] - } - - return ret -} - -// PruneResourceHusks is a specialized method that will remove any Resource -// objects that do not contain any instances, even if they have an EachMode. -// -// This should generally be used only after a "terraform destroy" operation, -// to finalize the cleanup of the state. It is not correct to use this after -// other operations because if a resource has "count = 0" or "for_each" over -// an empty collection then we want to retain it in the state so that references -// to it, particularly in "strange" contexts like "terraform console", can be -// properly resolved. -// -// This method MUST NOT be called concurrently with other readers and writers -// of the receiving state. -func (s *State) PruneResourceHusks() { - for _, m := range s.Modules { - m.PruneResourceHusks() - if len(m.Resources) == 0 && !m.Addr.IsRoot() { - s.RemoveModule(m.Addr) - } - } -} - -// SyncWrapper returns a SyncState object wrapping the receiver. -func (s *State) SyncWrapper() *SyncState { - return &SyncState{ - state: s, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/state_deepcopy.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/state_deepcopy.go deleted file mode 100644 index 6266aca7..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/state_deepcopy.go +++ /dev/null @@ -1,221 +0,0 @@ -package states - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/zclconf/go-cty/cty" -) - -// Taking deep copies of states is an important operation because state is -// otherwise a mutable data structure that is challenging to share across -// many separate callers. It is important that the DeepCopy implementations -// in this file comprehensively copy all parts of the state data structure -// that could be mutated via pointers. - -// DeepCopy returns a new state that contains equivalent data to the reciever -// but shares no backing memory in common. -// -// As with all methods on State, this method is not safe to use concurrently -// with writing to any portion of the recieving data structure. It is the -// caller's responsibility to ensure mutual exclusion for the duration of the -// operation, but may then freely modify the receiver and the returned copy -// independently once this method returns. -func (s *State) DeepCopy() *State { - if s == nil { - return nil - } - - modules := make(map[string]*Module, len(s.Modules)) - for k, m := range s.Modules { - modules[k] = m.DeepCopy() - } - return &State{ - Modules: modules, - } -} - -// DeepCopy returns a new module state that contains equivalent data to the -// receiver but shares no backing memory in common. -// -// As with all methods on Module, this method is not safe to use concurrently -// with writing to any portion of the recieving data structure. It is the -// caller's responsibility to ensure mutual exclusion for the duration of the -// operation, but may then freely modify the receiver and the returned copy -// independently once this method returns. -func (ms *Module) DeepCopy() *Module { - if ms == nil { - return nil - } - - resources := make(map[string]*Resource, len(ms.Resources)) - for k, r := range ms.Resources { - resources[k] = r.DeepCopy() - } - outputValues := make(map[string]*OutputValue, len(ms.OutputValues)) - for k, v := range ms.OutputValues { - outputValues[k] = v.DeepCopy() - } - localValues := make(map[string]cty.Value, len(ms.LocalValues)) - for k, v := range ms.LocalValues { - // cty.Value is immutable, so we don't need to copy these. - localValues[k] = v - } - - return &Module{ - Addr: ms.Addr, // technically mutable, but immutable by convention - Resources: resources, - OutputValues: outputValues, - LocalValues: localValues, - } -} - -// DeepCopy returns a new resource state that contains equivalent data to the -// receiver but shares no backing memory in common. -// -// As with all methods on Resource, this method is not safe to use concurrently -// with writing to any portion of the recieving data structure. It is the -// caller's responsibility to ensure mutual exclusion for the duration of the -// operation, but may then freely modify the receiver and the returned copy -// independently once this method returns. -func (rs *Resource) DeepCopy() *Resource { - if rs == nil { - return nil - } - - instances := make(map[addrs.InstanceKey]*ResourceInstance, len(rs.Instances)) - for k, i := range rs.Instances { - instances[k] = i.DeepCopy() - } - - return &Resource{ - Addr: rs.Addr, - EachMode: rs.EachMode, - Instances: instances, - ProviderConfig: rs.ProviderConfig, // technically mutable, but immutable by convention - } -} - -// DeepCopy returns a new resource instance state that contains equivalent data -// to the receiver but shares no backing memory in common. -// -// As with all methods on ResourceInstance, this method is not safe to use -// concurrently with writing to any portion of the recieving data structure. It -// is the caller's responsibility to ensure mutual exclusion for the duration -// of the operation, but may then freely modify the receiver and the returned -// copy independently once this method returns. -func (is *ResourceInstance) DeepCopy() *ResourceInstance { - if is == nil { - return nil - } - - deposed := make(map[DeposedKey]*ResourceInstanceObjectSrc, len(is.Deposed)) - for k, obj := range is.Deposed { - deposed[k] = obj.DeepCopy() - } - - return &ResourceInstance{ - Current: is.Current.DeepCopy(), - Deposed: deposed, - } -} - -// DeepCopy returns a new resource instance object that contains equivalent data -// to the receiver but shares no backing memory in common. -// -// As with all methods on ResourceInstanceObjectSrc, this method is not safe to -// use concurrently with writing to any portion of the recieving data structure. -// It is the caller's responsibility to ensure mutual exclusion for the duration -// of the operation, but may then freely modify the receiver and the returned -// copy independently once this method returns. -func (obj *ResourceInstanceObjectSrc) DeepCopy() *ResourceInstanceObjectSrc { - if obj == nil { - return nil - } - - var attrsFlat map[string]string - if obj.AttrsFlat != nil { - attrsFlat = make(map[string]string, len(obj.AttrsFlat)) - for k, v := range obj.AttrsFlat { - attrsFlat[k] = v - } - } - - var attrsJSON []byte - if obj.AttrsJSON != nil { - attrsJSON = make([]byte, len(obj.AttrsJSON)) - copy(attrsJSON, obj.AttrsJSON) - } - - var private []byte - if obj.Private != nil { - private = make([]byte, len(obj.Private)) - copy(private, obj.Private) - } - - // Some addrs.Referencable implementations are technically mutable, but - // we treat them as immutable by convention and so we don't deep-copy here. - dependencies := make([]addrs.Referenceable, len(obj.Dependencies)) - copy(dependencies, obj.Dependencies) - - return &ResourceInstanceObjectSrc{ - Status: obj.Status, - SchemaVersion: obj.SchemaVersion, - Private: private, - AttrsFlat: attrsFlat, - AttrsJSON: attrsJSON, - Dependencies: dependencies, - } -} - -// DeepCopy returns a new resource instance object that contains equivalent data -// to the receiver but shares no backing memory in common. -// -// As with all methods on ResourceInstanceObject, this method is not safe to use -// concurrently with writing to any portion of the recieving data structure. It -// is the caller's responsibility to ensure mutual exclusion for the duration -// of the operation, but may then freely modify the receiver and the returned -// copy independently once this method returns. -func (obj *ResourceInstanceObject) DeepCopy() *ResourceInstanceObject { - if obj == nil { - return nil - } - - var private []byte - if obj.Private != nil { - private = make([]byte, len(obj.Private)) - copy(private, obj.Private) - } - - // Some addrs.Referenceable implementations are technically mutable, but - // we treat them as immutable by convention and so we don't deep-copy here. - var dependencies []addrs.Referenceable - if obj.Dependencies != nil { - dependencies = make([]addrs.Referenceable, len(obj.Dependencies)) - copy(dependencies, obj.Dependencies) - } - - return &ResourceInstanceObject{ - Value: obj.Value, - Status: obj.Status, - Private: private, - Dependencies: dependencies, - } -} - -// DeepCopy returns a new output value state that contains equivalent data -// to the receiver but shares no backing memory in common. -// -// As with all methods on OutputValue, this method is not safe to use -// concurrently with writing to any portion of the recieving data structure. It -// is the caller's responsibility to ensure mutual exclusion for the duration -// of the operation, but may then freely modify the receiver and the returned -// copy independently once this method returns. -func (os *OutputValue) DeepCopy() *OutputValue { - if os == nil { - return nil - } - - return &OutputValue{ - Value: os.Value, - Sensitive: os.Sensitive, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/state_equal.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/state_equal.go deleted file mode 100644 index ea20967e..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/state_equal.go +++ /dev/null @@ -1,18 +0,0 @@ -package states - -import ( - "reflect" -) - -// Equal returns true if the receiver is functionally equivalent to other, -// including any ephemeral portions of the state that would not be included -// if the state were saved to files. -// -// To test only the persistent portions of two states for equality, instead -// use statefile.StatesMarshalEqual. -func (s *State) Equal(other *State) bool { - // For the moment this is sufficient, but we may need to do something - // more elaborate in future if we have any portions of state that require - // more sophisticated comparisons. - return reflect.DeepEqual(s, other) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/state_string.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/state_string.go deleted file mode 100644 index dffd650d..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/state_string.go +++ /dev/null @@ -1,279 +0,0 @@ -package states - -import ( - "bufio" - "bytes" - "encoding/json" - "fmt" - "sort" - "strings" - - ctyjson "github.com/zclconf/go-cty/cty/json" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim" -) - -// String returns a rather-odd string representation of the entire state. -// -// This is intended to match the behavior of the older terraform.State.String -// method that is used in lots of existing tests. It should not be used in -// new tests: instead, use "cmp" to directly compare the state data structures -// and print out a diff if they do not match. -// -// This method should never be used in non-test code, whether directly by call -// or indirectly via a %s or %q verb in package fmt. -func (s *State) String() string { - if s == nil { - return "" - } - - // sort the modules by name for consistent output - modules := make([]string, 0, len(s.Modules)) - for m := range s.Modules { - modules = append(modules, m) - } - sort.Strings(modules) - - var buf bytes.Buffer - for _, name := range modules { - m := s.Modules[name] - mStr := m.testString() - - // If we're the root module, we just write the output directly. - if m.Addr.IsRoot() { - buf.WriteString(mStr + "\n") - continue - } - - // We need to build out a string that resembles the not-quite-standard - // format that terraform.State.String used to use, where there's a - // "module." prefix but then just a chain of all of the module names - // without any further "module." portions. - buf.WriteString("module") - for _, step := range m.Addr { - buf.WriteByte('.') - buf.WriteString(step.Name) - if step.InstanceKey != addrs.NoKey { - buf.WriteByte('[') - buf.WriteString(step.InstanceKey.String()) - buf.WriteByte(']') - } - } - buf.WriteString(":\n") - - s := bufio.NewScanner(strings.NewReader(mStr)) - for s.Scan() { - text := s.Text() - if text != "" { - text = " " + text - } - - buf.WriteString(fmt.Sprintf("%s\n", text)) - } - } - - return strings.TrimSpace(buf.String()) -} - -// testString is used to produce part of the output of State.String. It should -// never be used directly. -func (m *Module) testString() string { - var buf bytes.Buffer - - if len(m.Resources) == 0 { - buf.WriteString("") - } - - // We use AbsResourceInstance here, even though everything belongs to - // the same module, just because we have a sorting behavior defined - // for those but not for just ResourceInstance. - addrsOrder := make([]addrs.AbsResourceInstance, 0, len(m.Resources)) - for _, rs := range m.Resources { - for ik := range rs.Instances { - addrsOrder = append(addrsOrder, rs.Addr.Instance(ik).Absolute(addrs.RootModuleInstance)) - } - } - - sort.Slice(addrsOrder, func(i, j int) bool { - return addrsOrder[i].Less(addrsOrder[j]) - }) - - for _, fakeAbsAddr := range addrsOrder { - addr := fakeAbsAddr.Resource - rs := m.Resource(addr.ContainingResource()) - is := m.ResourceInstance(addr) - - // Here we need to fake up a legacy-style address as the old state - // types would've used, since that's what our tests against those - // old types expect. The significant difference is that instancekey - // is dot-separated rather than using index brackets. - k := addr.ContainingResource().String() - if addr.Key != addrs.NoKey { - switch tk := addr.Key.(type) { - case addrs.IntKey: - k = fmt.Sprintf("%s.%d", k, tk) - default: - // No other key types existed for the legacy types, so we - // can do whatever we want here. We'll just use our standard - // syntax for these. - k = k + tk.String() - } - } - - id := LegacyInstanceObjectID(is.Current) - - taintStr := "" - if is.Current != nil && is.Current.Status == ObjectTainted { - taintStr = " (tainted)" - } - - deposedStr := "" - if len(is.Deposed) > 0 { - deposedStr = fmt.Sprintf(" (%d deposed)", len(is.Deposed)) - } - - buf.WriteString(fmt.Sprintf("%s:%s%s\n", k, taintStr, deposedStr)) - buf.WriteString(fmt.Sprintf(" ID = %s\n", id)) - buf.WriteString(fmt.Sprintf(" provider = %s\n", rs.ProviderConfig.String())) - - // Attributes were a flatmap before, but are not anymore. To preserve - // our old output as closely as possible we need to do a conversion - // to flatmap. Normally we'd want to do this with schema for - // accuracy, but for our purposes here it only needs to be approximate. - // This should produce an identical result for most cases, though - // in particular will differ in a few cases: - // - The keys used for elements in a set will be different - // - Values for attributes of type cty.DynamicPseudoType will be - // misinterpreted (but these weren't possible in old world anyway) - var attributes map[string]string - if obj := is.Current; obj != nil { - switch { - case obj.AttrsFlat != nil: - // Easy (but increasingly unlikely) case: the state hasn't - // actually been upgraded to the new form yet. - attributes = obj.AttrsFlat - case obj.AttrsJSON != nil: - ty, err := ctyjson.ImpliedType(obj.AttrsJSON) - if err == nil { - val, err := ctyjson.Unmarshal(obj.AttrsJSON, ty) - if err == nil { - attributes = hcl2shim.FlatmapValueFromHCL2(val) - } - } - } - } - attrKeys := make([]string, 0, len(attributes)) - for ak, val := range attributes { - if ak == "id" { - continue - } - - // don't show empty containers in the output - if val == "0" && (strings.HasSuffix(ak, ".#") || strings.HasSuffix(ak, ".%")) { - continue - } - - attrKeys = append(attrKeys, ak) - } - - sort.Strings(attrKeys) - - for _, ak := range attrKeys { - av := attributes[ak] - buf.WriteString(fmt.Sprintf(" %s = %s\n", ak, av)) - } - - // CAUTION: Since deposed keys are now random strings instead of - // incrementing integers, this result will not be deterministic - // if there is more than one deposed object. - i := 1 - for _, t := range is.Deposed { - id := LegacyInstanceObjectID(t) - taintStr := "" - if t.Status == ObjectTainted { - taintStr = " (tainted)" - } - buf.WriteString(fmt.Sprintf(" Deposed ID %d = %s%s\n", i, id, taintStr)) - i++ - } - - if obj := is.Current; obj != nil && len(obj.Dependencies) > 0 { - buf.WriteString(fmt.Sprintf("\n Dependencies:\n")) - for _, dep := range obj.Dependencies { - buf.WriteString(fmt.Sprintf(" %s\n", dep.String())) - } - } - } - - if len(m.OutputValues) > 0 { - buf.WriteString("\nOutputs:\n\n") - - ks := make([]string, 0, len(m.OutputValues)) - for k := range m.OutputValues { - ks = append(ks, k) - } - sort.Strings(ks) - - for _, k := range ks { - v := m.OutputValues[k] - lv := hcl2shim.ConfigValueFromHCL2(v.Value) - switch vTyped := lv.(type) { - case string: - buf.WriteString(fmt.Sprintf("%s = %s\n", k, vTyped)) - case []interface{}: - buf.WriteString(fmt.Sprintf("%s = %s\n", k, vTyped)) - case map[string]interface{}: - var mapKeys []string - for key := range vTyped { - mapKeys = append(mapKeys, key) - } - sort.Strings(mapKeys) - - var mapBuf bytes.Buffer - mapBuf.WriteString("{") - for _, key := range mapKeys { - mapBuf.WriteString(fmt.Sprintf("%s:%s ", key, vTyped[key])) - } - mapBuf.WriteString("}") - - buf.WriteString(fmt.Sprintf("%s = %s\n", k, mapBuf.String())) - default: - buf.WriteString(fmt.Sprintf("%s = %#v\n", k, lv)) - } - } - } - - return buf.String() -} - -// LegacyInstanceObjectID is a helper for extracting an object id value from -// an instance object in a way that approximates how we used to do this -// for the old state types. ID is no longer first-class, so this is preserved -// only for compatibility with old tests that include the id as part of their -// expected value. -func LegacyInstanceObjectID(obj *ResourceInstanceObjectSrc) string { - if obj == nil { - return "" - } - - if obj.AttrsJSON != nil { - type WithID struct { - ID string `json:"id"` - } - var withID WithID - err := json.Unmarshal(obj.AttrsJSON, &withID) - if err == nil { - return withID.ID - } - } else if obj.AttrsFlat != nil { - if flatID, exists := obj.AttrsFlat["id"]; exists { - return flatID - } - } - - // For resource types created after we removed id as special there may - // not actually be one at all. This is okay because older tests won't - // encounter this, and new tests shouldn't be using ids. - return "" -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/diagnostics.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/diagnostics.go deleted file mode 100644 index 042ce51c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/diagnostics.go +++ /dev/null @@ -1,62 +0,0 @@ -package statefile - -import ( - "encoding/json" - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -const invalidFormat = "Invalid state file format" - -// jsonUnmarshalDiags is a helper that translates errors returned from -// json.Unmarshal into hopefully-more-helpful diagnostics messages. -func jsonUnmarshalDiags(err error) tfdiags.Diagnostics { - var diags tfdiags.Diagnostics - if err == nil { - return diags - } - - switch tErr := err.(type) { - case *json.SyntaxError: - // We've usually already successfully parsed a source file as JSON at - // least once before we'd use jsonUnmarshalDiags with it (to sniff - // the version number) so this particular error should not appear much - // in practice. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - invalidFormat, - fmt.Sprintf("The state file could not be parsed as JSON: syntax error at byte offset %d.", tErr.Offset), - )) - case *json.UnmarshalTypeError: - // This is likely to be the most common area, describing a - // non-conformance between the file and the expected file format - // at a semantic level. - if tErr.Field != "" { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - invalidFormat, - fmt.Sprintf("The state file field %q has invalid value %s", tErr.Field, tErr.Value), - )) - break - } else { - // Without a field name, we can't really say anything helpful. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - invalidFormat, - "The state file does not conform to the expected JSON data structure.", - )) - } - default: - // Fallback for all other types of errors. This can happen only for - // custom UnmarshalJSON implementations, so should be encountered - // only rarely. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - invalidFormat, - fmt.Sprintf("The state file does not conform to the expected JSON data structure: %s.", err.Error()), - )) - } - - return diags -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/doc.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/doc.go deleted file mode 100644 index 625d0cf4..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -// Package statefile deals with the file format used to serialize states for -// persistent storage and then deserialize them into memory again later. -package statefile diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/file.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/file.go deleted file mode 100644 index 70c8ba6c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/file.go +++ /dev/null @@ -1,31 +0,0 @@ -package statefile - -import ( - version "github.com/hashicorp/go-version" - - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// File is the in-memory representation of a state file. It includes the state -// itself along with various metadata used to track changing state files for -// the same configuration over time. -type File struct { - // TerraformVersion is the version of Terraform that wrote this state file. - TerraformVersion *version.Version - - // Serial is incremented on any operation that modifies - // the State file. It is used to detect potentially conflicting - // updates. - Serial uint64 - - // Lineage is set when a new, blank state file is created and then - // never updated. This allows us to determine whether the serials - // of two states can be meaningfully compared. - // Apart from the guarantee that collisions between two lineages - // are very unlikely, this value is opaque and external callers - // should only compare lineage strings byte-for-byte for equality. - Lineage string - - // State is the actual state represented by this file. - State *states.State -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/read.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/read.go deleted file mode 100644 index f1899cd2..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/read.go +++ /dev/null @@ -1,209 +0,0 @@ -package statefile - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "os" - - version "github.com/hashicorp/go-version" - - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - tfversion "github.com/hashicorp/terraform-plugin-sdk/internal/version" -) - -// ErrNoState is returned by ReadState when the state file is empty. -var ErrNoState = errors.New("no state") - -// Read reads a state from the given reader. -// -// Legacy state format versions 1 through 3 are supported, but the result will -// contain object attributes in the deprecated "flatmap" format and so must -// be upgraded by the caller before use. -// -// If the state file is empty, the special error value ErrNoState is returned. -// Otherwise, the returned error might be a wrapper around tfdiags.Diagnostics -// potentially describing multiple errors. -func Read(r io.Reader) (*File, error) { - // Some callers provide us a "typed nil" *os.File here, which would - // cause us to panic below if we tried to use it. - if f, ok := r.(*os.File); ok && f == nil { - return nil, ErrNoState - } - - var diags tfdiags.Diagnostics - - // We actually just buffer the whole thing in memory, because states are - // generally not huge and we need to do be able to sniff for a version - // number before full parsing. - src, err := ioutil.ReadAll(r) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to read state file", - fmt.Sprintf("The state file could not be read: %s", err), - )) - return nil, diags.Err() - } - - if len(src) == 0 { - return nil, ErrNoState - } - - state, diags := readState(src) - if diags.HasErrors() { - return nil, diags.Err() - } - - if state == nil { - // Should never happen - panic("readState returned nil state with no errors") - } - - if state.TerraformVersion != nil && state.TerraformVersion.GreaterThan(tfversion.SemVer) { - return state, fmt.Errorf( - "state snapshot was created by Terraform v%s, which is newer than current v%s; upgrade to Terraform v%s or greater to work with this state", - state.TerraformVersion, - tfversion.SemVer, - state.TerraformVersion, - ) - } - - return state, diags.Err() -} - -func readState(src []byte) (*File, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - if looksLikeVersion0(src) { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - unsupportedFormat, - "The state is stored in a legacy binary format that is not supported since Terraform v0.7. To continue, first upgrade the state using Terraform 0.6.16 or earlier.", - )) - return nil, diags - } - - version, versionDiags := sniffJSONStateVersion(src) - diags = diags.Append(versionDiags) - if versionDiags.HasErrors() { - return nil, diags - } - - switch version { - case 0: - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - unsupportedFormat, - "The state file uses JSON syntax but has a version number of zero. There was never a JSON-based state format zero, so this state file is invalid and cannot be processed.", - )) - return nil, diags - case 1: - return readStateV1(src) - case 2: - return readStateV2(src) - case 3: - return readStateV3(src) - case 4: - return readStateV4(src) - default: - thisVersion := tfversion.SemVer.String() - creatingVersion := sniffJSONStateTerraformVersion(src) - switch { - case creatingVersion != "": - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - unsupportedFormat, - fmt.Sprintf("The state file uses format version %d, which is not supported by Terraform %s. This state file was created by Terraform %s.", version, thisVersion, creatingVersion), - )) - default: - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - unsupportedFormat, - fmt.Sprintf("The state file uses format version %d, which is not supported by Terraform %s. This state file may have been created by a newer version of Terraform.", version, thisVersion), - )) - } - return nil, diags - } -} - -func sniffJSONStateVersion(src []byte) (uint64, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - type VersionSniff struct { - Version *uint64 `json:"version"` - } - var sniff VersionSniff - err := json.Unmarshal(src, &sniff) - if err != nil { - switch tErr := err.(type) { - case *json.SyntaxError: - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - unsupportedFormat, - fmt.Sprintf("The state file could not be parsed as JSON: syntax error at byte offset %d.", tErr.Offset), - )) - case *json.UnmarshalTypeError: - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - unsupportedFormat, - fmt.Sprintf("The version in the state file is %s. A positive whole number is required.", tErr.Value), - )) - default: - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - unsupportedFormat, - "The state file could not be parsed as JSON.", - )) - } - } - - if sniff.Version == nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - unsupportedFormat, - "The state file does not have a \"version\" attribute, which is required to identify the format version.", - )) - return 0, diags - } - - return *sniff.Version, diags -} - -// sniffJSONStateTerraformVersion attempts to sniff the Terraform version -// specification from the given state file source code. The result is either -// a version string or an empty string if no version number could be extracted. -// -// This is a best-effort function intended to produce nicer error messages. It -// should not be used for any real processing. -func sniffJSONStateTerraformVersion(src []byte) string { - type VersionSniff struct { - Version string `json:"terraform_version"` - } - var sniff VersionSniff - - err := json.Unmarshal(src, &sniff) - if err != nil { - return "" - } - - // Attempt to parse the string as a version so we won't report garbage - // as a version number. - _, err = version.NewVersion(sniff.Version) - if err != nil { - return "" - } - - return sniff.Version -} - -// unsupportedFormat is a diagnostic summary message for when the state file -// seems to not be a state file at all, or is not a supported version. -// -// Use invalidFormat instead for the subtly-different case of "this looks like -// it's intended to be a state file but it's not structured correctly". -const unsupportedFormat = "Unsupported state file format" - -const upgradeFailed = "State format upgrade failed" diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version0.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version0.go deleted file mode 100644 index 9b533317..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version0.go +++ /dev/null @@ -1,23 +0,0 @@ -package statefile - -// looksLikeVersion0 sniffs for the signature indicating a version 0 state -// file. -// -// Version 0 was the number retroactively assigned to Terraform's initial -// (unversioned) binary state file format, which was later superseded by the -// version 1 format in JSON. -// -// Version 0 is no longer supported, so this is used only to detect it and -// return a nice error to the user. -func looksLikeVersion0(src []byte) bool { - // Version 0 files begin with the magic prefix "tfstate". - const magic = "tfstate" - if len(src) < len(magic) { - // Not even long enough to have the magic prefix - return false - } - if string(src[0:len(magic)]) == magic { - return true - } - return false -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version1.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version1.go deleted file mode 100644 index 85b422ad..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version1.go +++ /dev/null @@ -1,167 +0,0 @@ -package statefile - -import ( - "encoding/json" - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -func readStateV1(src []byte) (*File, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - sV1 := &stateV1{} - err := json.Unmarshal(src, sV1) - if err != nil { - diags = diags.Append(jsonUnmarshalDiags(err)) - return nil, diags - } - - file, prepDiags := prepareStateV1(sV1) - diags = diags.Append(prepDiags) - return file, diags -} - -func prepareStateV1(sV1 *stateV1) (*File, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - sV2, err := upgradeStateV1ToV2(sV1) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - upgradeFailed, - fmt.Sprintf("Error upgrading state file format from version 1 to version 2: %s.", err), - )) - return nil, diags - } - - file, prepDiags := prepareStateV2(sV2) - diags = diags.Append(prepDiags) - return file, diags -} - -// stateV1 is a representation of the legacy JSON state format version 1. -// -// It is only used to read version 1 JSON files prior to upgrading them to -// the current format. -type stateV1 struct { - // Version is the protocol version. "1" for a StateV1. - Version int `json:"version"` - - // Serial is incremented on any operation that modifies - // the State file. It is used to detect potentially conflicting - // updates. - Serial int64 `json:"serial"` - - // Remote is used to track the metadata required to - // pull and push state files from a remote storage endpoint. - Remote *remoteStateV1 `json:"remote,omitempty"` - - // Modules contains all the modules in a breadth-first order - Modules []*moduleStateV1 `json:"modules"` -} - -type remoteStateV1 struct { - // Type controls the client we use for the remote state - Type string `json:"type"` - - // Config is used to store arbitrary configuration that - // is type specific - Config map[string]string `json:"config"` -} - -type moduleStateV1 struct { - // Path is the import path from the root module. Modules imports are - // always disjoint, so the path represents amodule tree - Path []string `json:"path"` - - // Outputs declared by the module and maintained for each module - // even though only the root module technically needs to be kept. - // This allows operators to inspect values at the boundaries. - Outputs map[string]string `json:"outputs"` - - // Resources is a mapping of the logically named resource to - // the state of the resource. Each resource may actually have - // N instances underneath, although a user only needs to think - // about the 1:1 case. - Resources map[string]*resourceStateV1 `json:"resources"` - - // Dependencies are a list of things that this module relies on - // existing to remain intact. For example: an module may depend - // on a VPC ID given by an aws_vpc resource. - // - // Terraform uses this information to build valid destruction - // orders and to warn the user if they're destroying a module that - // another resource depends on. - // - // Things can be put into this list that may not be managed by - // Terraform. If Terraform doesn't find a matching ID in the - // overall state, then it assumes it isn't managed and doesn't - // worry about it. - Dependencies []string `json:"depends_on,omitempty"` -} - -type resourceStateV1 struct { - // This is filled in and managed by Terraform, and is the resource - // type itself such as "mycloud_instance". If a resource provider sets - // this value, it won't be persisted. - Type string `json:"type"` - - // Dependencies are a list of things that this resource relies on - // existing to remain intact. For example: an AWS instance might - // depend on a subnet (which itself might depend on a VPC, and so - // on). - // - // Terraform uses this information to build valid destruction - // orders and to warn the user if they're destroying a resource that - // another resource depends on. - // - // Things can be put into this list that may not be managed by - // Terraform. If Terraform doesn't find a matching ID in the - // overall state, then it assumes it isn't managed and doesn't - // worry about it. - Dependencies []string `json:"depends_on,omitempty"` - - // Primary is the current active instance for this resource. - // It can be replaced but only after a successful creation. - // This is the instances on which providers will act. - Primary *instanceStateV1 `json:"primary"` - - // Tainted is used to track any underlying instances that - // have been created but are in a bad or unknown state and - // need to be cleaned up subsequently. In the - // standard case, there is only at most a single instance. - // However, in pathological cases, it is possible for the number - // of instances to accumulate. - Tainted []*instanceStateV1 `json:"tainted,omitempty"` - - // Deposed is used in the mechanics of CreateBeforeDestroy: the existing - // Primary is Deposed to get it out of the way for the replacement Primary to - // be created by Apply. If the replacement Primary creates successfully, the - // Deposed instance is cleaned up. If there were problems creating the - // replacement, the instance remains in the Deposed list so it can be - // destroyed in a future run. Functionally, Deposed instances are very - // similar to Tainted instances in that Terraform is only tracking them in - // order to remember to destroy them. - Deposed []*instanceStateV1 `json:"deposed,omitempty"` - - // Provider is used when a resource is connected to a provider with an alias. - // If this string is empty, the resource is connected to the default provider, - // e.g. "aws_instance" goes with the "aws" provider. - // If the resource block contained a "provider" key, that value will be set here. - Provider string `json:"provider,omitempty"` -} - -type instanceStateV1 struct { - // A unique ID for this resource. This is opaque to Terraform - // and is only meant as a lookup mechanism for the providers. - ID string `json:"id"` - - // Attributes are basic information about the resource. Any keys here - // are accessible in variable format within Terraform configurations: - // ${resourcetype.name.attribute}. - Attributes map[string]string `json:"attributes,omitempty"` - - // Meta is a simple K/V map that is persisted to the State but otherwise - // ignored by Terraform core. It's meant to be used for accounting by - // external client code. - Meta map[string]string `json:"meta,omitempty"` -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version1_upgrade.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version1_upgrade.go deleted file mode 100644 index 0b417e1c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version1_upgrade.go +++ /dev/null @@ -1,172 +0,0 @@ -package statefile - -import ( - "fmt" - "log" - - "github.com/mitchellh/copystructure" -) - -// upgradeStateV1ToV2 is used to upgrade a V1 state representation -// into a V2 state representation -func upgradeStateV1ToV2(old *stateV1) (*stateV2, error) { - log.Printf("[TRACE] statefile.Read: upgrading format from v1 to v2") - if old == nil { - return nil, nil - } - - remote, err := old.Remote.upgradeToV2() - if err != nil { - return nil, fmt.Errorf("Error upgrading State V1: %v", err) - } - - modules := make([]*moduleStateV2, len(old.Modules)) - for i, module := range old.Modules { - upgraded, err := module.upgradeToV2() - if err != nil { - return nil, fmt.Errorf("Error upgrading State V1: %v", err) - } - modules[i] = upgraded - } - if len(modules) == 0 { - modules = nil - } - - newState := &stateV2{ - Version: 2, - Serial: old.Serial, - Remote: remote, - Modules: modules, - } - - return newState, nil -} - -func (old *remoteStateV1) upgradeToV2() (*remoteStateV2, error) { - if old == nil { - return nil, nil - } - - config, err := copystructure.Copy(old.Config) - if err != nil { - return nil, fmt.Errorf("Error upgrading RemoteState V1: %v", err) - } - - return &remoteStateV2{ - Type: old.Type, - Config: config.(map[string]string), - }, nil -} - -func (old *moduleStateV1) upgradeToV2() (*moduleStateV2, error) { - if old == nil { - return nil, nil - } - - pathRaw, err := copystructure.Copy(old.Path) - if err != nil { - return nil, fmt.Errorf("Error upgrading ModuleState V1: %v", err) - } - path, ok := pathRaw.([]string) - if !ok { - return nil, fmt.Errorf("Error upgrading ModuleState V1: path is not a list of strings") - } - if len(path) == 0 { - // We found some V1 states with a nil path. Assume root. - path = []string{"root"} - } - - // Outputs needs upgrading to use the new structure - outputs := make(map[string]*outputStateV2) - for key, output := range old.Outputs { - outputs[key] = &outputStateV2{ - Type: "string", - Value: output, - Sensitive: false, - } - } - - resources := make(map[string]*resourceStateV2) - for key, oldResource := range old.Resources { - upgraded, err := oldResource.upgradeToV2() - if err != nil { - return nil, fmt.Errorf("Error upgrading ModuleState V1: %v", err) - } - resources[key] = upgraded - } - - dependencies, err := copystructure.Copy(old.Dependencies) - if err != nil { - return nil, fmt.Errorf("Error upgrading ModuleState V1: %v", err) - } - - return &moduleStateV2{ - Path: path, - Outputs: outputs, - Resources: resources, - Dependencies: dependencies.([]string), - }, nil -} - -func (old *resourceStateV1) upgradeToV2() (*resourceStateV2, error) { - if old == nil { - return nil, nil - } - - dependencies, err := copystructure.Copy(old.Dependencies) - if err != nil { - return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err) - } - - primary, err := old.Primary.upgradeToV2() - if err != nil { - return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err) - } - - deposed := make([]*instanceStateV2, len(old.Deposed)) - for i, v := range old.Deposed { - upgraded, err := v.upgradeToV2() - if err != nil { - return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err) - } - deposed[i] = upgraded - } - if len(deposed) == 0 { - deposed = nil - } - - return &resourceStateV2{ - Type: old.Type, - Dependencies: dependencies.([]string), - Primary: primary, - Deposed: deposed, - Provider: old.Provider, - }, nil -} - -func (old *instanceStateV1) upgradeToV2() (*instanceStateV2, error) { - if old == nil { - return nil, nil - } - - attributes, err := copystructure.Copy(old.Attributes) - if err != nil { - return nil, fmt.Errorf("Error upgrading InstanceState V1: %v", err) - } - - meta, err := copystructure.Copy(old.Meta) - if err != nil { - return nil, fmt.Errorf("Error upgrading InstanceState V1: %v", err) - } - - newMeta := make(map[string]interface{}) - for k, v := range meta.(map[string]string) { - newMeta[k] = v - } - - return &instanceStateV2{ - ID: old.ID, - Attributes: attributes.(map[string]string), - Meta: newMeta, - }, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version2.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version2.go deleted file mode 100644 index 6d10166b..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version2.go +++ /dev/null @@ -1,204 +0,0 @@ -package statefile - -import ( - "encoding/json" - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -func readStateV2(src []byte) (*File, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - sV2 := &stateV2{} - err := json.Unmarshal(src, sV2) - if err != nil { - diags = diags.Append(jsonUnmarshalDiags(err)) - return nil, diags - } - - file, prepDiags := prepareStateV2(sV2) - diags = diags.Append(prepDiags) - return file, diags -} - -func prepareStateV2(sV2 *stateV2) (*File, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - sV3, err := upgradeStateV2ToV3(sV2) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - upgradeFailed, - fmt.Sprintf("Error upgrading state file format from version 2 to version 3: %s.", err), - )) - return nil, diags - } - - file, prepDiags := prepareStateV3(sV3) - diags = diags.Append(prepDiags) - return file, diags -} - -// stateV2 is a representation of the legacy JSON state format version 2. -// -// It is only used to read version 2 JSON files prior to upgrading them to -// the current format. -type stateV2 struct { - // Version is the state file protocol version. - Version int `json:"version"` - - // TFVersion is the version of Terraform that wrote this state. - TFVersion string `json:"terraform_version,omitempty"` - - // Serial is incremented on any operation that modifies - // the State file. It is used to detect potentially conflicting - // updates. - Serial int64 `json:"serial"` - - // Lineage is set when a new, blank state is created and then - // never updated. This allows us to determine whether the serials - // of two states can be meaningfully compared. - // Apart from the guarantee that collisions between two lineages - // are very unlikely, this value is opaque and external callers - // should only compare lineage strings byte-for-byte for equality. - Lineage string `json:"lineage"` - - // Remote is used to track the metadata required to - // pull and push state files from a remote storage endpoint. - Remote *remoteStateV2 `json:"remote,omitempty"` - - // Backend tracks the configuration for the backend in use with - // this state. This is used to track any changes in the backend - // configuration. - Backend *backendStateV2 `json:"backend,omitempty"` - - // Modules contains all the modules in a breadth-first order - Modules []*moduleStateV2 `json:"modules"` -} - -type remoteStateV2 struct { - // Type controls the client we use for the remote state - Type string `json:"type"` - - // Config is used to store arbitrary configuration that - // is type specific - Config map[string]string `json:"config"` -} - -type outputStateV2 struct { - // Sensitive describes whether the output is considered sensitive, - // which may lead to masking the value on screen in some cases. - Sensitive bool `json:"sensitive"` - // Type describes the structure of Value. Valid values are "string", - // "map" and "list" - Type string `json:"type"` - // Value contains the value of the output, in the structure described - // by the Type field. - Value interface{} `json:"value"` -} - -type moduleStateV2 struct { - // Path is the import path from the root module. Modules imports are - // always disjoint, so the path represents amodule tree - Path []string `json:"path"` - - // Locals are kept only transiently in-memory, because we can always - // re-compute them. - Locals map[string]interface{} `json:"-"` - - // Outputs declared by the module and maintained for each module - // even though only the root module technically needs to be kept. - // This allows operators to inspect values at the boundaries. - Outputs map[string]*outputStateV2 `json:"outputs"` - - // Resources is a mapping of the logically named resource to - // the state of the resource. Each resource may actually have - // N instances underneath, although a user only needs to think - // about the 1:1 case. - Resources map[string]*resourceStateV2 `json:"resources"` - - // Dependencies are a list of things that this module relies on - // existing to remain intact. For example: an module may depend - // on a VPC ID given by an aws_vpc resource. - // - // Terraform uses this information to build valid destruction - // orders and to warn the user if they're destroying a module that - // another resource depends on. - // - // Things can be put into this list that may not be managed by - // Terraform. If Terraform doesn't find a matching ID in the - // overall state, then it assumes it isn't managed and doesn't - // worry about it. - Dependencies []string `json:"depends_on"` -} - -type resourceStateV2 struct { - // This is filled in and managed by Terraform, and is the resource - // type itself such as "mycloud_instance". If a resource provider sets - // this value, it won't be persisted. - Type string `json:"type"` - - // Dependencies are a list of things that this resource relies on - // existing to remain intact. For example: an AWS instance might - // depend on a subnet (which itself might depend on a VPC, and so - // on). - // - // Terraform uses this information to build valid destruction - // orders and to warn the user if they're destroying a resource that - // another resource depends on. - // - // Things can be put into this list that may not be managed by - // Terraform. If Terraform doesn't find a matching ID in the - // overall state, then it assumes it isn't managed and doesn't - // worry about it. - Dependencies []string `json:"depends_on"` - - // Primary is the current active instance for this resource. - // It can be replaced but only after a successful creation. - // This is the instances on which providers will act. - Primary *instanceStateV2 `json:"primary"` - - // Deposed is used in the mechanics of CreateBeforeDestroy: the existing - // Primary is Deposed to get it out of the way for the replacement Primary to - // be created by Apply. If the replacement Primary creates successfully, the - // Deposed instance is cleaned up. - // - // If there were problems creating the replacement Primary, the Deposed - // instance and the (now tainted) replacement Primary will be swapped so the - // tainted replacement will be cleaned up instead. - // - // An instance will remain in the Deposed list until it is successfully - // destroyed and purged. - Deposed []*instanceStateV2 `json:"deposed"` - - // Provider is used when a resource is connected to a provider with an alias. - // If this string is empty, the resource is connected to the default provider, - // e.g. "aws_instance" goes with the "aws" provider. - // If the resource block contained a "provider" key, that value will be set here. - Provider string `json:"provider"` -} - -type instanceStateV2 struct { - // A unique ID for this resource. This is opaque to Terraform - // and is only meant as a lookup mechanism for the providers. - ID string `json:"id"` - - // Attributes are basic information about the resource. Any keys here - // are accessible in variable format within Terraform configurations: - // ${resourcetype.name.attribute}. - Attributes map[string]string `json:"attributes"` - - // Meta is a simple K/V map that is persisted to the State but otherwise - // ignored by Terraform core. It's meant to be used for accounting by - // external client code. The value here must only contain Go primitives - // and collections. - Meta map[string]interface{} `json:"meta"` - - // Tainted is used to mark a resource for recreation. - Tainted bool `json:"tainted"` -} - -type backendStateV2 struct { - Type string `json:"type"` // Backend type - ConfigRaw json.RawMessage `json:"config"` // Backend raw config - Hash uint64 `json:"hash"` // Hash of portion of configuration from config files -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version2_upgrade.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version2_upgrade.go deleted file mode 100644 index 2d03c07c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version2_upgrade.go +++ /dev/null @@ -1,145 +0,0 @@ -package statefile - -import ( - "fmt" - "log" - "regexp" - "sort" - "strconv" - "strings" - - "github.com/mitchellh/copystructure" -) - -func upgradeStateV2ToV3(old *stateV2) (*stateV3, error) { - if old == nil { - return (*stateV3)(nil), nil - } - - var new *stateV3 - { - copy, err := copystructure.Config{Lock: true}.Copy(old) - if err != nil { - panic(err) - } - newWrongType := copy.(*stateV2) - newRightType := (stateV3)(*newWrongType) - new = &newRightType - } - - // Set the new version number - new.Version = 3 - - // Change the counts for things which look like maps to use the % - // syntax. Remove counts for empty collections - they will be added - // back in later. - for _, module := range new.Modules { - for _, resource := range module.Resources { - // Upgrade Primary - if resource.Primary != nil { - upgradeAttributesV2ToV3(resource.Primary) - } - - // Upgrade Deposed - for _, deposed := range resource.Deposed { - upgradeAttributesV2ToV3(deposed) - } - } - } - - return new, nil -} - -func upgradeAttributesV2ToV3(instanceState *instanceStateV2) error { - collectionKeyRegexp := regexp.MustCompile(`^(.*\.)#$`) - collectionSubkeyRegexp := regexp.MustCompile(`^([^\.]+)\..*`) - - // Identify the key prefix of anything which is a collection - var collectionKeyPrefixes []string - for key := range instanceState.Attributes { - if submatches := collectionKeyRegexp.FindAllStringSubmatch(key, -1); len(submatches) > 0 { - collectionKeyPrefixes = append(collectionKeyPrefixes, submatches[0][1]) - } - } - sort.Strings(collectionKeyPrefixes) - - log.Printf("[STATE UPGRADE] Detected the following collections in state: %v", collectionKeyPrefixes) - - // This could be rolled into fewer loops, but it is somewhat clearer this way, and will not - // run very often. - for _, prefix := range collectionKeyPrefixes { - // First get the actual keys that belong to this prefix - var potentialKeysMatching []string - for key := range instanceState.Attributes { - if strings.HasPrefix(key, prefix) { - potentialKeysMatching = append(potentialKeysMatching, strings.TrimPrefix(key, prefix)) - } - } - sort.Strings(potentialKeysMatching) - - var actualKeysMatching []string - for _, key := range potentialKeysMatching { - if submatches := collectionSubkeyRegexp.FindAllStringSubmatch(key, -1); len(submatches) > 0 { - actualKeysMatching = append(actualKeysMatching, submatches[0][1]) - } else { - if key != "#" { - actualKeysMatching = append(actualKeysMatching, key) - } - } - } - actualKeysMatching = uniqueSortedStrings(actualKeysMatching) - - // Now inspect the keys in order to determine whether this is most likely to be - // a map, list or set. There is room for error here, so we log in each case. If - // there is no method of telling, we remove the key from the InstanceState in - // order that it will be recreated. Again, this could be rolled into fewer loops - // but we prefer clarity. - - oldCountKey := fmt.Sprintf("%s#", prefix) - - // First, detect "obvious" maps - which have non-numeric keys (mostly). - hasNonNumericKeys := false - for _, key := range actualKeysMatching { - if _, err := strconv.Atoi(key); err != nil { - hasNonNumericKeys = true - } - } - if hasNonNumericKeys { - newCountKey := fmt.Sprintf("%s%%", prefix) - - instanceState.Attributes[newCountKey] = instanceState.Attributes[oldCountKey] - delete(instanceState.Attributes, oldCountKey) - log.Printf("[STATE UPGRADE] Detected %s as a map. Replaced count = %s", - strings.TrimSuffix(prefix, "."), instanceState.Attributes[newCountKey]) - } - - // Now detect empty collections and remove them from state. - if len(actualKeysMatching) == 0 { - delete(instanceState.Attributes, oldCountKey) - log.Printf("[STATE UPGRADE] Detected %s as an empty collection. Removed from state.", - strings.TrimSuffix(prefix, ".")) - } - } - - return nil -} - -// uniqueSortedStrings removes duplicates from a slice of strings and returns -// a sorted slice of the unique strings. -func uniqueSortedStrings(input []string) []string { - uniquemap := make(map[string]struct{}) - for _, str := range input { - uniquemap[str] = struct{}{} - } - - output := make([]string, len(uniquemap)) - - i := 0 - for key := range uniquemap { - output[i] = key - i = i + 1 - } - - sort.Strings(output) - return output -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version3.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version3.go deleted file mode 100644 index 1c81e716..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version3.go +++ /dev/null @@ -1,50 +0,0 @@ -package statefile - -import ( - "encoding/json" - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -func readStateV3(src []byte) (*File, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - sV3 := &stateV3{} - err := json.Unmarshal(src, sV3) - if err != nil { - diags = diags.Append(jsonUnmarshalDiags(err)) - return nil, diags - } - - file, prepDiags := prepareStateV3(sV3) - diags = diags.Append(prepDiags) - return file, diags -} - -func prepareStateV3(sV3 *stateV3) (*File, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - sV4, err := upgradeStateV3ToV4(sV3) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - upgradeFailed, - fmt.Sprintf("Error upgrading state file format from version 3 to version 4: %s.", err), - )) - return nil, diags - } - - file, prepDiags := prepareStateV4(sV4) - diags = diags.Append(prepDiags) - return file, diags -} - -// stateV2 is a representation of the legacy JSON state format version 3. -// -// It is only used to read version 3 JSON files prior to upgrading them to -// the current format. -// -// The differences between version 2 and version 3 are only in the data and -// not in the structure, so stateV3 actually shares the same structs as -// stateV2. Type stateV3 represents that the data within is formatted as -// expected by the V3 format, rather than the V2 format. -type stateV3 stateV2 diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version3_upgrade.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version3_upgrade.go deleted file mode 100644 index f08a62b2..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version3_upgrade.go +++ /dev/null @@ -1,444 +0,0 @@ -package statefile - -import ( - "encoding/json" - "fmt" - "strconv" - "strings" - - "github.com/zclconf/go-cty/cty" - ctyjson "github.com/zclconf/go-cty/cty/json" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -func upgradeStateV3ToV4(old *stateV3) (*stateV4, error) { - - if old.Serial < 0 { - // The new format is using uint64 here, which should be fine for any - // real state (we only used positive integers in practice) but we'll - // catch this explicitly here to avoid weird behavior if a state file - // has been tampered with in some way. - return nil, fmt.Errorf("state has serial less than zero, which is invalid") - } - - new := &stateV4{ - TerraformVersion: old.TFVersion, - Serial: uint64(old.Serial), - Lineage: old.Lineage, - RootOutputs: map[string]outputStateV4{}, - Resources: []resourceStateV4{}, - } - - if new.TerraformVersion == "" { - // Older formats considered this to be optional, but now it's required - // and so we'll stub it out with something that's definitely older - // than the version that really created this state. - new.TerraformVersion = "0.0.0" - } - - for _, msOld := range old.Modules { - if len(msOld.Path) < 1 || msOld.Path[0] != "root" { - return nil, fmt.Errorf("state contains invalid module path %#v", msOld.Path) - } - - // Convert legacy-style module address into our newer address type. - // Since these old formats are only generated by versions of Terraform - // that don't support count and for_each on modules, we can just assume - // all of the modules are unkeyed. - moduleAddr := make(addrs.ModuleInstance, len(msOld.Path)-1) - for i, name := range msOld.Path[1:] { - moduleAddr[i] = addrs.ModuleInstanceStep{ - Name: name, - InstanceKey: addrs.NoKey, - } - } - - // In a v3 state file, a "resource state" is actually an instance - // state, so we need to fill in a missing level of heirarchy here - // by lazily creating resource states as we encounter them. - // We'll track them in here, keyed on the string representation of - // the resource address. - resourceStates := map[string]*resourceStateV4{} - - for legacyAddr, rsOld := range msOld.Resources { - instAddr, err := parseLegacyResourceAddress(legacyAddr) - if err != nil { - return nil, err - } - - resAddr := instAddr.Resource - rs, exists := resourceStates[resAddr.String()] - if !exists { - var modeStr string - switch resAddr.Mode { - case addrs.ManagedResourceMode: - modeStr = "managed" - case addrs.DataResourceMode: - modeStr = "data" - default: - return nil, fmt.Errorf("state contains resource %s with an unsupported resource mode %#v", resAddr, resAddr.Mode) - } - - // In state versions prior to 4 we allowed each instance of a - // resource to have its own provider configuration address, - // which makes no real sense in practice because providers - // are associated with resources in the configuration. We - // elevate that to the resource level during this upgrade, - // implicitly taking the provider address of the first instance - // we encounter for each resource. While this is lossy in - // theory, in practice there is no reason for these values to - // differ between instances. - var providerAddr addrs.AbsProviderConfig - oldProviderAddr := rsOld.Provider - if strings.Contains(oldProviderAddr, "provider.") { - // Smells like a new-style provider address, but we'll test it. - var diags tfdiags.Diagnostics - providerAddr, diags = addrs.ParseAbsProviderConfigStr(oldProviderAddr) - if diags.HasErrors() { - return nil, fmt.Errorf("invalid provider config reference %q for %s: %s", oldProviderAddr, instAddr, diags.Err()) - } - } else { - // Smells like an old-style module-local provider address, - // which we'll need to migrate. We'll assume it's referring - // to the same module the resource is in, which might be - // incorrect but it'll get fixed up next time any updates - // are made to an instance. - if oldProviderAddr != "" { - localAddr, diags := addrs.ParseProviderConfigCompactStr(oldProviderAddr) - if diags.HasErrors() { - return nil, fmt.Errorf("invalid legacy provider config reference %q for %s: %s", oldProviderAddr, instAddr, diags.Err()) - } - providerAddr = localAddr.Absolute(moduleAddr) - } else { - providerAddr = resAddr.DefaultProviderConfig().Absolute(moduleAddr) - } - } - - rs = &resourceStateV4{ - Module: moduleAddr.String(), - Mode: modeStr, - Type: resAddr.Type, - Name: resAddr.Name, - Instances: []instanceObjectStateV4{}, - ProviderConfig: providerAddr.String(), - } - resourceStates[resAddr.String()] = rs - } - - // Now we'll deal with the instance itself, which may either be - // the first instance in a resource we just created or an additional - // instance for a resource added on a prior loop. - instKey := instAddr.Key - if isOld := rsOld.Primary; isOld != nil { - isNew, err := upgradeInstanceObjectV3ToV4(rsOld, isOld, instKey, states.NotDeposed) - if err != nil { - return nil, fmt.Errorf("failed to migrate primary generation of %s: %s", instAddr, err) - } - rs.Instances = append(rs.Instances, *isNew) - } - for i, isOld := range rsOld.Deposed { - // When we migrate old instances we'll use sequential deposed - // keys just so that the upgrade result is deterministic. New - // deposed keys allocated moving forward will be pseudorandomly - // selected, but we check for collisions and so these - // non-random ones won't hurt. - deposedKey := states.DeposedKey(fmt.Sprintf("%08x", i+1)) - isNew, err := upgradeInstanceObjectV3ToV4(rsOld, isOld, instKey, deposedKey) - if err != nil { - return nil, fmt.Errorf("failed to migrate deposed generation index %d of %s: %s", i, instAddr, err) - } - rs.Instances = append(rs.Instances, *isNew) - } - - if instKey != addrs.NoKey && rs.EachMode == "" { - rs.EachMode = "list" - } - } - - for _, rs := range resourceStates { - new.Resources = append(new.Resources, *rs) - } - - if len(msOld.Path) == 1 && msOld.Path[0] == "root" { - // We'll migrate the outputs for this module too, then. - for name, oldOS := range msOld.Outputs { - newOS := outputStateV4{ - Sensitive: oldOS.Sensitive, - } - - valRaw := oldOS.Value - valSrc, err := json.Marshal(valRaw) - if err != nil { - // Should never happen, because this value came from JSON - // in the first place and so we're just round-tripping here. - return nil, fmt.Errorf("failed to serialize output %q value as JSON: %s", name, err) - } - - // The "type" field in state V2 wasn't really that useful - // since it was only able to capture string vs. list vs. map. - // For this reason, during upgrade we'll just discard it - // altogether and use cty's idea of the implied type of - // turning our old value into JSON. - ty, err := ctyjson.ImpliedType(valSrc) - if err != nil { - // REALLY should never happen, because we literally just - // encoded this as JSON above! - return nil, fmt.Errorf("failed to parse output %q value from JSON: %s", name, err) - } - - // ImpliedType tends to produce structural types, but since older - // version of Terraform didn't support those a collection type - // is probably what was intended, so we'll see if we can - // interpret our value as one. - ty = simplifyImpliedValueType(ty) - - tySrc, err := ctyjson.MarshalType(ty) - if err != nil { - return nil, fmt.Errorf("failed to serialize output %q type as JSON: %s", name, err) - } - - newOS.ValueRaw = json.RawMessage(valSrc) - newOS.ValueTypeRaw = json.RawMessage(tySrc) - - new.RootOutputs[name] = newOS - } - } - } - - new.normalize() - - return new, nil -} - -func upgradeInstanceObjectV3ToV4(rsOld *resourceStateV2, isOld *instanceStateV2, instKey addrs.InstanceKey, deposedKey states.DeposedKey) (*instanceObjectStateV4, error) { - - // Schema versions were, in prior formats, a private concern of the provider - // SDK, and not a first-class concept in the state format. Here we're - // sniffing for the pre-0.12 SDK's way of representing schema versions - // and promoting it to our first-class field if we find it. We'll ignore - // it if it doesn't look like what the SDK would've written. If this - // sniffing fails then we'll assume schema version 0. - var schemaVersion uint64 - migratedSchemaVersion := false - if raw, exists := isOld.Meta["schema_version"]; exists { - switch tv := raw.(type) { - case string: - v, err := strconv.ParseUint(tv, 10, 64) - if err == nil { - schemaVersion = v - migratedSchemaVersion = true - } - case int: - schemaVersion = uint64(tv) - migratedSchemaVersion = true - case float64: - schemaVersion = uint64(tv) - migratedSchemaVersion = true - } - } - - private := map[string]interface{}{} - for k, v := range isOld.Meta { - if k == "schema_version" && migratedSchemaVersion { - // We're gonna promote this into our first-class schema version field - continue - } - private[k] = v - } - var privateJSON []byte - if len(private) != 0 { - var err error - privateJSON, err = json.Marshal(private) - if err != nil { - // This shouldn't happen, because the Meta values all came from JSON - // originally anyway. - return nil, fmt.Errorf("cannot serialize private instance object data: %s", err) - } - } - - var status string - if isOld.Tainted { - status = "tainted" - } - - var instKeyRaw interface{} - switch tk := instKey.(type) { - case addrs.IntKey: - instKeyRaw = int(tk) - case addrs.StringKey: - instKeyRaw = string(tk) - default: - if instKeyRaw != nil { - return nil, fmt.Errorf("unsupported instance key: %#v", instKey) - } - } - - var attributes map[string]string - if isOld.Attributes != nil { - attributes = make(map[string]string, len(isOld.Attributes)) - for k, v := range isOld.Attributes { - attributes[k] = v - } - } - if isOld.ID != "" { - // As a special case, if we don't already have an "id" attribute and - // yet there's a non-empty first-class ID on the old object then we'll - // create a synthetic id attribute to avoid losing that first-class id. - // In practice this generally arises only in tests where state literals - // are hand-written in a non-standard way; real code prior to 0.12 - // would always force the first-class ID to be copied into the - // id attribute before storing. - if attributes == nil { - attributes = make(map[string]string, len(isOld.Attributes)) - } - if idVal := attributes["id"]; idVal == "" { - attributes["id"] = isOld.ID - } - } - - dependencies := make([]string, len(rsOld.Dependencies)) - for i, v := range rsOld.Dependencies { - depStr, err := parseLegacyDependency(v) - if err != nil { - return nil, fmt.Errorf("invalid dependency reference %q: %s", v, err) - } - dependencies[i] = depStr - } - - return &instanceObjectStateV4{ - IndexKey: instKeyRaw, - Status: status, - Deposed: string(deposedKey), - AttributesFlat: attributes, - Dependencies: dependencies, - SchemaVersion: schemaVersion, - PrivateRaw: privateJSON, - }, nil -} - -// parseLegacyResourceAddress parses the different identifier format used -// state formats before version 4, like "instance.name.0". -func parseLegacyResourceAddress(s string) (addrs.ResourceInstance, error) { - var ret addrs.ResourceInstance - - // Split based on ".". Every resource address should have at least two - // elements (type and name). - parts := strings.Split(s, ".") - if len(parts) < 2 || len(parts) > 4 { - return ret, fmt.Errorf("invalid internal resource address format: %s", s) - } - - // Data resource if we have at least 3 parts and the first one is data - ret.Resource.Mode = addrs.ManagedResourceMode - if len(parts) > 2 && parts[0] == "data" { - ret.Resource.Mode = addrs.DataResourceMode - parts = parts[1:] - } - - // If we're not a data resource and we have more than 3, then it is an error - if len(parts) > 3 && ret.Resource.Mode != addrs.DataResourceMode { - return ret, fmt.Errorf("invalid internal resource address format: %s", s) - } - - // Build the parts of the resource address that are guaranteed to exist - ret.Resource.Type = parts[0] - ret.Resource.Name = parts[1] - ret.Key = addrs.NoKey - - // If we have more parts, then we have an index. Parse that. - if len(parts) > 2 { - idx, err := strconv.ParseInt(parts[2], 0, 0) - if err != nil { - return ret, fmt.Errorf("error parsing resource address %q: %s", s, err) - } - - ret.Key = addrs.IntKey(idx) - } - - return ret, nil -} - -// simplifyImpliedValueType attempts to heuristically simplify a value type -// derived from a legacy stored output value into something simpler that -// is closer to what would've fitted into the pre-v0.12 value type system. -func simplifyImpliedValueType(ty cty.Type) cty.Type { - switch { - case ty.IsTupleType(): - // If all of the element types are the same then we'll make this - // a list instead. This is very likely to be true, since prior versions - // of Terraform did not officially support mixed-type collections. - - if ty.Equals(cty.EmptyTuple) { - // Don't know what the element type would be, then. - return ty - } - - etys := ty.TupleElementTypes() - ety := etys[0] - for _, other := range etys[1:] { - if !other.Equals(ety) { - // inconsistent types - return ty - } - } - ety = simplifyImpliedValueType(ety) - return cty.List(ety) - - case ty.IsObjectType(): - // If all of the attribute types are the same then we'll make this - // a map instead. This is very likely to be true, since prior versions - // of Terraform did not officially support mixed-type collections. - - if ty.Equals(cty.EmptyObject) { - // Don't know what the element type would be, then. - return ty - } - - atys := ty.AttributeTypes() - var ety cty.Type - for _, other := range atys { - if ety == cty.NilType { - ety = other - continue - } - if !other.Equals(ety) { - // inconsistent types - return ty - } - } - ety = simplifyImpliedValueType(ety) - return cty.Map(ety) - - default: - // No other normalizations are possible - return ty - } -} - -func parseLegacyDependency(s string) (string, error) { - parts := strings.Split(s, ".") - ret := parts[0] - for _, part := range parts[1:] { - if part == "*" { - break - } - if i, err := strconv.Atoi(part); err == nil { - ret = ret + fmt.Sprintf("[%d]", i) - break - } - ret = ret + "." + part - } - - // The result must parse as a reference, or else we'll create an invalid - // state file. - var diags tfdiags.Diagnostics - _, diags = addrs.ParseRefStr(ret) - if diags.HasErrors() { - return "", diags.Err() - } - - return ret, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version4.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version4.go deleted file mode 100644 index 164b57f8..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/version4.go +++ /dev/null @@ -1,604 +0,0 @@ -package statefile - -import ( - "encoding/json" - "fmt" - "io" - "sort" - - version "github.com/hashicorp/go-version" - ctyjson "github.com/zclconf/go-cty/cty/json" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -func readStateV4(src []byte) (*File, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - sV4 := &stateV4{} - err := json.Unmarshal(src, sV4) - if err != nil { - diags = diags.Append(jsonUnmarshalDiags(err)) - return nil, diags - } - - file, prepDiags := prepareStateV4(sV4) - diags = diags.Append(prepDiags) - return file, diags -} - -func prepareStateV4(sV4 *stateV4) (*File, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - var tfVersion *version.Version - if sV4.TerraformVersion != "" { - var err error - tfVersion, err = version.NewVersion(sV4.TerraformVersion) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid Terraform version string", - fmt.Sprintf("State file claims to have been written by Terraform version %q, which is not a valid version string.", sV4.TerraformVersion), - )) - } - } - - file := &File{ - TerraformVersion: tfVersion, - Serial: sV4.Serial, - Lineage: sV4.Lineage, - } - - state := states.NewState() - - for _, rsV4 := range sV4.Resources { - rAddr := addrs.Resource{ - Type: rsV4.Type, - Name: rsV4.Name, - } - switch rsV4.Mode { - case "managed": - rAddr.Mode = addrs.ManagedResourceMode - case "data": - rAddr.Mode = addrs.DataResourceMode - default: - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid resource mode in state", - fmt.Sprintf("State contains a resource with mode %q (%q %q) which is not supported.", rsV4.Mode, rAddr.Type, rAddr.Name), - )) - continue - } - - moduleAddr := addrs.RootModuleInstance - if rsV4.Module != "" { - var addrDiags tfdiags.Diagnostics - moduleAddr, addrDiags = addrs.ParseModuleInstanceStr(rsV4.Module) - diags = diags.Append(addrDiags) - if addrDiags.HasErrors() { - continue - } - } - - providerAddr, addrDiags := addrs.ParseAbsProviderConfigStr(rsV4.ProviderConfig) - diags.Append(addrDiags) - if addrDiags.HasErrors() { - continue - } - - var eachMode states.EachMode - switch rsV4.EachMode { - case "": - eachMode = states.NoEach - case "list": - eachMode = states.EachList - case "map": - eachMode = states.EachMap - default: - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid resource metadata in state", - fmt.Sprintf("Resource %s has invalid \"each\" value %q in state.", rAddr.Absolute(moduleAddr), eachMode), - )) - continue - } - - ms := state.EnsureModule(moduleAddr) - - // Ensure the resource container object is present in the state. - ms.SetResourceMeta(rAddr, eachMode, providerAddr) - - for _, isV4 := range rsV4.Instances { - keyRaw := isV4.IndexKey - var key addrs.InstanceKey - switch tk := keyRaw.(type) { - case int: - key = addrs.IntKey(tk) - case float64: - // Since JSON only has one number type, reading from encoding/json - // gives us a float64 here even if the number is whole. - // float64 has a smaller integer range than int, but in practice - // we rarely have more than a few tens of instances and so - // it's unlikely that we'll exhaust the 52 bits in a float64. - key = addrs.IntKey(int(tk)) - case string: - key = addrs.StringKey(tk) - default: - if keyRaw != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid resource instance metadata in state", - fmt.Sprintf("Resource %s has an instance with the invalid instance key %#v.", rAddr.Absolute(moduleAddr), keyRaw), - )) - continue - } - key = addrs.NoKey - } - - instAddr := rAddr.Instance(key) - - obj := &states.ResourceInstanceObjectSrc{ - SchemaVersion: isV4.SchemaVersion, - } - - { - // Instance attributes - switch { - case isV4.AttributesRaw != nil: - obj.AttrsJSON = isV4.AttributesRaw - case isV4.AttributesFlat != nil: - obj.AttrsFlat = isV4.AttributesFlat - default: - // This is odd, but we'll accept it and just treat the - // object has being empty. In practice this should arise - // only from the contrived sort of state objects we tend - // to hand-write inline in tests. - obj.AttrsJSON = []byte{'{', '}'} - } - } - - { - // Status - raw := isV4.Status - switch raw { - case "": - obj.Status = states.ObjectReady - case "tainted": - obj.Status = states.ObjectTainted - default: - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid resource instance metadata in state", - fmt.Sprintf("Instance %s has invalid status %q.", instAddr.Absolute(moduleAddr), raw), - )) - continue - } - } - - if raw := isV4.PrivateRaw; len(raw) > 0 { - obj.Private = raw - } - - { - depsRaw := isV4.Dependencies - deps := make([]addrs.Referenceable, 0, len(depsRaw)) - for _, depRaw := range depsRaw { - ref, refDiags := addrs.ParseRefStr(depRaw) - diags = diags.Append(refDiags) - if refDiags.HasErrors() { - continue - } - if len(ref.Remaining) != 0 { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid resource instance metadata in state", - fmt.Sprintf("Instance %s declares dependency on %q, which is not a reference to a dependable object.", instAddr.Absolute(moduleAddr), depRaw), - )) - } - if ref.Subject == nil { - // Should never happen - panic(fmt.Sprintf("parsing dependency %q for instance %s returned a nil address", depRaw, instAddr.Absolute(moduleAddr))) - } - deps = append(deps, ref.Subject) - } - obj.Dependencies = deps - } - - switch { - case isV4.Deposed != "": - dk := states.DeposedKey(isV4.Deposed) - if len(dk) != 8 { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid resource instance metadata in state", - fmt.Sprintf("Instance %s has an object with deposed key %q, which is not correctly formatted.", instAddr.Absolute(moduleAddr), isV4.Deposed), - )) - continue - } - is := ms.ResourceInstance(instAddr) - if is.HasDeposed(dk) { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Duplicate resource instance in state", - fmt.Sprintf("Instance %s deposed object %q appears multiple times in the state file.", instAddr.Absolute(moduleAddr), dk), - )) - continue - } - - ms.SetResourceInstanceDeposed(instAddr, dk, obj, providerAddr) - default: - is := ms.ResourceInstance(instAddr) - if is.HasCurrent() { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Duplicate resource instance in state", - fmt.Sprintf("Instance %s appears multiple times in the state file.", instAddr.Absolute(moduleAddr)), - )) - continue - } - - ms.SetResourceInstanceCurrent(instAddr, obj, providerAddr) - } - } - - // We repeat this after creating the instances because - // SetResourceInstanceCurrent automatically resets this metadata based - // on the incoming objects. That behavior is useful when we're making - // piecemeal updates to the state during an apply, but when we're - // reading the state file we want to reflect its contents exactly. - ms.SetResourceMeta(rAddr, eachMode, providerAddr) - } - - // The root module is special in that we persist its attributes and thus - // need to reload them now. (For descendent modules we just re-calculate - // them based on the latest configuration on each run.) - { - rootModule := state.RootModule() - for name, fos := range sV4.RootOutputs { - os := &states.OutputValue{} - os.Sensitive = fos.Sensitive - - ty, err := ctyjson.UnmarshalType([]byte(fos.ValueTypeRaw)) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid output value type in state", - fmt.Sprintf("The state file has an invalid type specification for output %q: %s.", name, err), - )) - continue - } - - val, err := ctyjson.Unmarshal([]byte(fos.ValueRaw), ty) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid output value saved in state", - fmt.Sprintf("The state file has an invalid value for output %q: %s.", name, err), - )) - continue - } - - os.Value = val - rootModule.OutputValues[name] = os - } - } - - file.State = state - return file, diags -} - -func writeStateV4(file *File, w io.Writer) tfdiags.Diagnostics { - // Here we'll convert back from the "File" representation to our - // stateV4 struct representation and write that. - // - // While we support legacy state formats for reading, we only support the - // latest for writing and so if a V5 is added in future then this function - // should be deleted and replaced with a writeStateV5, even though the - // read/prepare V4 functions above would stick around. - - var diags tfdiags.Diagnostics - if file == nil || file.State == nil { - panic("attempt to write nil state to file") - } - - var terraformVersion string - if file.TerraformVersion != nil { - terraformVersion = file.TerraformVersion.String() - } - - sV4 := &stateV4{ - TerraformVersion: terraformVersion, - Serial: file.Serial, - Lineage: file.Lineage, - RootOutputs: map[string]outputStateV4{}, - Resources: []resourceStateV4{}, - } - - for name, os := range file.State.RootModule().OutputValues { - src, err := ctyjson.Marshal(os.Value, os.Value.Type()) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to serialize output value in state", - fmt.Sprintf("An error occured while serializing output value %q: %s.", name, err), - )) - continue - } - - typeSrc, err := ctyjson.MarshalType(os.Value.Type()) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to serialize output value in state", - fmt.Sprintf("An error occured while serializing the type of output value %q: %s.", name, err), - )) - continue - } - - sV4.RootOutputs[name] = outputStateV4{ - Sensitive: os.Sensitive, - ValueRaw: json.RawMessage(src), - ValueTypeRaw: json.RawMessage(typeSrc), - } - } - - for _, ms := range file.State.Modules { - moduleAddr := ms.Addr - for _, rs := range ms.Resources { - resourceAddr := rs.Addr - - var mode string - switch resourceAddr.Mode { - case addrs.ManagedResourceMode: - mode = "managed" - case addrs.DataResourceMode: - mode = "data" - default: - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to serialize resource in state", - fmt.Sprintf("Resource %s has mode %s, which cannot be serialized in state", resourceAddr.Absolute(moduleAddr), resourceAddr.Mode), - )) - continue - } - - var eachMode string - switch rs.EachMode { - case states.NoEach: - eachMode = "" - case states.EachList: - eachMode = "list" - case states.EachMap: - eachMode = "map" - default: - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to serialize resource in state", - fmt.Sprintf("Resource %s has \"each\" mode %s, which cannot be serialized in state", resourceAddr.Absolute(moduleAddr), rs.EachMode), - )) - continue - } - - sV4.Resources = append(sV4.Resources, resourceStateV4{ - Module: moduleAddr.String(), - Mode: mode, - Type: resourceAddr.Type, - Name: resourceAddr.Name, - EachMode: eachMode, - ProviderConfig: rs.ProviderConfig.String(), - Instances: []instanceObjectStateV4{}, - }) - rsV4 := &(sV4.Resources[len(sV4.Resources)-1]) - - for key, is := range rs.Instances { - if is.HasCurrent() { - var objDiags tfdiags.Diagnostics - rsV4.Instances, objDiags = appendInstanceObjectStateV4( - rs, is, key, is.Current, states.NotDeposed, - rsV4.Instances, - ) - diags = diags.Append(objDiags) - } - for dk, obj := range is.Deposed { - var objDiags tfdiags.Diagnostics - rsV4.Instances, objDiags = appendInstanceObjectStateV4( - rs, is, key, obj, dk, - rsV4.Instances, - ) - diags = diags.Append(objDiags) - } - } - } - } - - sV4.normalize() - - src, err := json.MarshalIndent(sV4, "", " ") - if err != nil { - // Shouldn't happen if we do our conversion to *stateV4 correctly above. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to serialize state", - fmt.Sprintf("An error occured while serializing the state to save it. This is a bug in Terraform and should be reported: %s.", err), - )) - return diags - } - src = append(src, '\n') - - _, err = w.Write(src) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to write state", - fmt.Sprintf("An error occured while writing the serialized state: %s.", err), - )) - return diags - } - - return diags -} - -func appendInstanceObjectStateV4(rs *states.Resource, is *states.ResourceInstance, key addrs.InstanceKey, obj *states.ResourceInstanceObjectSrc, deposed states.DeposedKey, isV4s []instanceObjectStateV4) ([]instanceObjectStateV4, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - var status string - switch obj.Status { - case states.ObjectReady: - status = "" - case states.ObjectTainted: - status = "tainted" - default: - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to serialize resource instance in state", - fmt.Sprintf("Instance %s has status %s, which cannot be saved in state.", rs.Addr.Instance(key), obj.Status), - )) - } - - var privateRaw []byte - if len(obj.Private) > 0 { - privateRaw = obj.Private - } - - deps := make([]string, len(obj.Dependencies)) - for i, depAddr := range obj.Dependencies { - deps[i] = depAddr.String() - } - - var rawKey interface{} - switch tk := key.(type) { - case addrs.IntKey: - rawKey = int(tk) - case addrs.StringKey: - rawKey = string(tk) - default: - if key != addrs.NoKey { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to serialize resource instance in state", - fmt.Sprintf("Instance %s has an unsupported instance key: %#v.", rs.Addr.Instance(key), key), - )) - } - } - - return append(isV4s, instanceObjectStateV4{ - IndexKey: rawKey, - Deposed: string(deposed), - Status: status, - SchemaVersion: obj.SchemaVersion, - AttributesFlat: obj.AttrsFlat, - AttributesRaw: obj.AttrsJSON, - PrivateRaw: privateRaw, - Dependencies: deps, - }), diags -} - -type stateV4 struct { - Version stateVersionV4 `json:"version"` - TerraformVersion string `json:"terraform_version"` - Serial uint64 `json:"serial"` - Lineage string `json:"lineage"` - RootOutputs map[string]outputStateV4 `json:"outputs"` - Resources []resourceStateV4 `json:"resources"` -} - -// normalize makes some in-place changes to normalize the way items are -// stored to ensure that two functionally-equivalent states will be stored -// identically. -func (s *stateV4) normalize() { - sort.Stable(sortResourcesV4(s.Resources)) - for _, rs := range s.Resources { - sort.Stable(sortInstancesV4(rs.Instances)) - } -} - -type outputStateV4 struct { - ValueRaw json.RawMessage `json:"value"` - ValueTypeRaw json.RawMessage `json:"type"` - Sensitive bool `json:"sensitive,omitempty"` -} - -type resourceStateV4 struct { - Module string `json:"module,omitempty"` - Mode string `json:"mode"` - Type string `json:"type"` - Name string `json:"name"` - EachMode string `json:"each,omitempty"` - ProviderConfig string `json:"provider"` - Instances []instanceObjectStateV4 `json:"instances"` -} - -type instanceObjectStateV4 struct { - IndexKey interface{} `json:"index_key,omitempty"` - Status string `json:"status,omitempty"` - Deposed string `json:"deposed,omitempty"` - - SchemaVersion uint64 `json:"schema_version"` - AttributesRaw json.RawMessage `json:"attributes,omitempty"` - AttributesFlat map[string]string `json:"attributes_flat,omitempty"` - - PrivateRaw []byte `json:"private,omitempty"` - - Dependencies []string `json:"depends_on,omitempty"` -} - -// stateVersionV4 is a weird special type we use to produce our hard-coded -// "version": 4 in the JSON serialization. -type stateVersionV4 struct{} - -func (sv stateVersionV4) MarshalJSON() ([]byte, error) { - return []byte{'4'}, nil -} - -func (sv stateVersionV4) UnmarshalJSON([]byte) error { - // Nothing to do: we already know we're version 4 - return nil -} - -type sortResourcesV4 []resourceStateV4 - -func (sr sortResourcesV4) Len() int { return len(sr) } -func (sr sortResourcesV4) Swap(i, j int) { sr[i], sr[j] = sr[j], sr[i] } -func (sr sortResourcesV4) Less(i, j int) bool { - switch { - case sr[i].Mode != sr[j].Mode: - return sr[i].Mode < sr[j].Mode - case sr[i].Type != sr[j].Type: - return sr[i].Type < sr[j].Type - case sr[i].Name != sr[j].Name: - return sr[i].Name < sr[j].Name - default: - return false - } -} - -type sortInstancesV4 []instanceObjectStateV4 - -func (si sortInstancesV4) Len() int { return len(si) } -func (si sortInstancesV4) Swap(i, j int) { si[i], si[j] = si[j], si[i] } -func (si sortInstancesV4) Less(i, j int) bool { - ki := si[i].IndexKey - kj := si[j].IndexKey - if ki != kj { - if (ki == nil) != (kj == nil) { - return ki == nil - } - if kii, isInt := ki.(int); isInt { - if kji, isInt := kj.(int); isInt { - return kii < kji - } - return true - } - if kis, isStr := ki.(string); isStr { - if kjs, isStr := kj.(string); isStr { - return kis < kjs - } - return true - } - } - if si[i].Deposed != si[j].Deposed { - return si[i].Deposed < si[j].Deposed - } - return false -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/write.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/write.go deleted file mode 100644 index 8fdca458..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile/write.go +++ /dev/null @@ -1,17 +0,0 @@ -package statefile - -import ( - "io" - - tfversion "github.com/hashicorp/terraform-plugin-sdk/internal/version" -) - -// Write writes the given state to the given writer in the current state -// serialization format. -func Write(s *File, w io.Writer) error { - // Always record the current terraform version in the state. - s.TerraformVersion = tfversion.SemVer - - diags := writeStateV4(s, w) - return diags.Err() -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/sync.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/sync.go deleted file mode 100644 index 6d236125..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/states/sync.go +++ /dev/null @@ -1,484 +0,0 @@ -package states - -import ( - "log" - "sync" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/zclconf/go-cty/cty" -) - -// SyncState is a wrapper around State that provides concurrency-safe access to -// various common operations that occur during a Terraform graph walk, or other -// similar concurrent contexts. -// -// When a SyncState wrapper is in use, no concurrent direct access to the -// underlying objects is permitted unless the caller first acquires an explicit -// lock, using the Lock and Unlock methods. Most callers should _not_ -// explicitly lock, and should instead use the other methods of this type that -// handle locking automatically. -// -// Since SyncState is able to safely consolidate multiple updates into a single -// atomic operation, many of its methods are at a higher level than those -// of the underlying types, and operate on the state as a whole rather than -// on individual sub-structures of the state. -// -// SyncState can only protect against races within its own methods. It cannot -// provide any guarantees about the order in which concurrent operations will -// be processed, so callers may still need to employ higher-level techniques -// for ensuring correct operation sequencing, such as building and walking -// a dependency graph. -type SyncState struct { - state *State - lock sync.RWMutex -} - -// Module returns a snapshot of the state of the module instance with the given -// address, or nil if no such module is tracked. -// -// The return value is a pointer to a copy of the module state, which the -// caller may then freely access and mutate. However, since the module state -// tends to be a large data structure with many child objects, where possible -// callers should prefer to use a more granular accessor to access a child -// module directly, and thus reduce the amount of copying required. -func (s *SyncState) Module(addr addrs.ModuleInstance) *Module { - s.lock.RLock() - ret := s.state.Module(addr).DeepCopy() - s.lock.RUnlock() - return ret -} - -// OutputValue returns a snapshot of the state of the output value with the -// given address, or nil if no such output value is tracked. -// -// The return value is a pointer to a copy of the output value state, which the -// caller may then freely access and mutate. -func (s *SyncState) OutputValue(addr addrs.AbsOutputValue) *OutputValue { - s.lock.RLock() - ret := s.state.OutputValue(addr).DeepCopy() - s.lock.RUnlock() - return ret -} - -// SetOutputValue writes a given output value into the state, overwriting -// any existing value of the same name. -// -// If the module containing the output is not yet tracked in state then it -// be added as a side-effect. -func (s *SyncState) SetOutputValue(addr addrs.AbsOutputValue, value cty.Value, sensitive bool) { - s.lock.Lock() - defer s.lock.Unlock() - - ms := s.state.EnsureModule(addr.Module) - ms.SetOutputValue(addr.OutputValue.Name, value, sensitive) -} - -// RemoveOutputValue removes the stored value for the output value with the -// given address. -// -// If this results in its containing module being empty, the module will be -// pruned from the state as a side-effect. -func (s *SyncState) RemoveOutputValue(addr addrs.AbsOutputValue) { - s.lock.Lock() - defer s.lock.Unlock() - - ms := s.state.Module(addr.Module) - if ms == nil { - return - } - ms.RemoveOutputValue(addr.OutputValue.Name) - s.maybePruneModule(addr.Module) -} - -// LocalValue returns the current value associated with the given local value -// address. -func (s *SyncState) LocalValue(addr addrs.AbsLocalValue) cty.Value { - s.lock.RLock() - // cty.Value is immutable, so we don't need any extra copying here. - ret := s.state.LocalValue(addr) - s.lock.RUnlock() - return ret -} - -// SetLocalValue writes a given output value into the state, overwriting -// any existing value of the same name. -// -// If the module containing the local value is not yet tracked in state then it -// will be added as a side-effect. -func (s *SyncState) SetLocalValue(addr addrs.AbsLocalValue, value cty.Value) { - s.lock.Lock() - defer s.lock.Unlock() - - ms := s.state.EnsureModule(addr.Module) - ms.SetLocalValue(addr.LocalValue.Name, value) -} - -// RemoveLocalValue removes the stored value for the local value with the -// given address. -// -// If this results in its containing module being empty, the module will be -// pruned from the state as a side-effect. -func (s *SyncState) RemoveLocalValue(addr addrs.AbsLocalValue) { - s.lock.Lock() - defer s.lock.Unlock() - - ms := s.state.Module(addr.Module) - if ms == nil { - return - } - ms.RemoveLocalValue(addr.LocalValue.Name) - s.maybePruneModule(addr.Module) -} - -// Resource returns a snapshot of the state of the resource with the given -// address, or nil if no such resource is tracked. -// -// The return value is a pointer to a copy of the resource state, which the -// caller may then freely access and mutate. -func (s *SyncState) Resource(addr addrs.AbsResource) *Resource { - s.lock.RLock() - ret := s.state.Resource(addr).DeepCopy() - s.lock.RUnlock() - return ret -} - -// ResourceInstance returns a snapshot of the state the resource instance with -// the given address, or nil if no such instance is tracked. -// -// The return value is a pointer to a copy of the instance state, which the -// caller may then freely access and mutate. -func (s *SyncState) ResourceInstance(addr addrs.AbsResourceInstance) *ResourceInstance { - s.lock.RLock() - ret := s.state.ResourceInstance(addr).DeepCopy() - s.lock.RUnlock() - return ret -} - -// ResourceInstanceObject returns a snapshot of the current instance object -// of the given generation belonging to the instance with the given address, -// or nil if no such object is tracked.. -// -// The return value is a pointer to a copy of the object, which the caller may -// then freely access and mutate. -func (s *SyncState) ResourceInstanceObject(addr addrs.AbsResourceInstance, gen Generation) *ResourceInstanceObjectSrc { - s.lock.RLock() - defer s.lock.RUnlock() - - inst := s.state.ResourceInstance(addr) - if inst == nil { - return nil - } - return inst.GetGeneration(gen).DeepCopy() -} - -// SetResourceMeta updates the resource-level metadata for the resource at -// the given address, creating the containing module state and resource state -// as a side-effect if not already present. -func (s *SyncState) SetResourceMeta(addr addrs.AbsResource, eachMode EachMode, provider addrs.AbsProviderConfig) { - s.lock.Lock() - defer s.lock.Unlock() - - ms := s.state.EnsureModule(addr.Module) - ms.SetResourceMeta(addr.Resource, eachMode, provider) -} - -// RemoveResourceIfEmpty is similar to RemoveResource but first checks to -// make sure there are no instances or objects left in the resource. -// -// Returns true if the resource was removed, or false if remaining child -// objects prevented its removal. Returns true also if the resource was -// already absent, and thus no action needed to be taken. -func (s *SyncState) RemoveResourceIfEmpty(addr addrs.AbsResource) bool { - s.lock.Lock() - defer s.lock.Unlock() - - ms := s.state.Module(addr.Module) - if ms == nil { - return true // nothing to do - } - rs := ms.Resource(addr.Resource) - if rs == nil { - return true // nothing to do - } - if len(rs.Instances) != 0 { - // We don't check here for the possibility of instances that exist - // but don't have any objects because it's the responsibility of the - // instance-mutation methods to prune those away automatically. - return false - } - ms.RemoveResource(addr.Resource) - s.maybePruneModule(addr.Module) - return true -} - -// MaybeFixUpResourceInstanceAddressForCount deals with the situation where a -// resource has changed from having "count" set to not set, or vice-versa, and -// so we need to rename the zeroth instance key to no key at all, or vice-versa. -// -// Set countEnabled to true if the resource has count set in its new -// configuration, or false if it does not. -// -// The state is modified in-place if necessary, moving a resource instance -// between the two addresses. The return value is true if a change was made, -// and false otherwise. -func (s *SyncState) MaybeFixUpResourceInstanceAddressForCount(addr addrs.AbsResource, countEnabled bool) bool { - s.lock.Lock() - defer s.lock.Unlock() - - ms := s.state.Module(addr.Module) - if ms == nil { - return false - } - - relAddr := addr.Resource - rs := ms.Resource(relAddr) - if rs == nil { - return false - } - huntKey := addrs.NoKey - replaceKey := addrs.InstanceKey(addrs.IntKey(0)) - if !countEnabled { - huntKey, replaceKey = replaceKey, huntKey - } - - is, exists := rs.Instances[huntKey] - if !exists { - return false - } - - if _, exists := rs.Instances[replaceKey]; exists { - // If the replacement key also exists then we'll do nothing and keep both. - return false - } - - // If we get here then we need to "rename" from hunt to replace - rs.Instances[replaceKey] = is - delete(rs.Instances, huntKey) - return true -} - -// SetResourceInstanceCurrent saves the given instance object as the current -// generation of the resource instance with the given address, simultaneously -// updating the recorded provider configuration address, dependencies, and -// resource EachMode. -// -// Any existing current instance object for the given resource is overwritten. -// Set obj to nil to remove the primary generation object altogether. If there -// are no deposed objects then the instance as a whole will be removed, which -// may in turn also remove the containing module if it becomes empty. -// -// The caller must ensure that the given ResourceInstanceObject is not -// concurrently mutated during this call, but may be freely used again once -// this function returns. -// -// The provider address and "each mode" are resource-wide settings and so they -// are updated for all other instances of the same resource as a side-effect of -// this call. -// -// If the containing module for this resource or the resource itself are not -// already tracked in state then they will be added as a side-effect. -func (s *SyncState) SetResourceInstanceCurrent(addr addrs.AbsResourceInstance, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) { - s.lock.Lock() - defer s.lock.Unlock() - - ms := s.state.EnsureModule(addr.Module) - ms.SetResourceInstanceCurrent(addr.Resource, obj.DeepCopy(), provider) - s.maybePruneModule(addr.Module) -} - -// SetResourceInstanceDeposed saves the given instance object as a deposed -// generation of the resource instance with the given address and deposed key. -// -// Call this method only for pre-existing deposed objects that already have -// a known DeposedKey. For example, this method is useful if reloading objects -// that were persisted to a state file. To mark the current object as deposed, -// use DeposeResourceInstanceObject instead. -// -// The caller must ensure that the given ResourceInstanceObject is not -// concurrently mutated during this call, but may be freely used again once -// this function returns. -// -// The resource that contains the given instance must already exist in the -// state, or this method will panic. Use Resource to check first if its -// presence is not already guaranteed. -// -// Any existing current instance object for the given resource and deposed key -// is overwritten. Set obj to nil to remove the deposed object altogether. If -// the instance is left with no objects after this operation then it will -// be removed from its containing resource altogether. -// -// If the containing module for this resource or the resource itself are not -// already tracked in state then they will be added as a side-effect. -func (s *SyncState) SetResourceInstanceDeposed(addr addrs.AbsResourceInstance, key DeposedKey, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) { - s.lock.Lock() - defer s.lock.Unlock() - - ms := s.state.EnsureModule(addr.Module) - ms.SetResourceInstanceDeposed(addr.Resource, key, obj.DeepCopy(), provider) - s.maybePruneModule(addr.Module) -} - -// DeposeResourceInstanceObject moves the current instance object for the -// given resource instance address into the deposed set, leaving the instance -// without a current object. -// -// The return value is the newly-allocated deposed key, or NotDeposed if the -// given instance is already lacking a current object. -// -// If the containing module for this resource or the resource itself are not -// already tracked in state then there cannot be a current object for the -// given instance, and so NotDeposed will be returned without modifying the -// state at all. -func (s *SyncState) DeposeResourceInstanceObject(addr addrs.AbsResourceInstance) DeposedKey { - s.lock.Lock() - defer s.lock.Unlock() - - ms := s.state.Module(addr.Module) - if ms == nil { - return NotDeposed - } - - return ms.deposeResourceInstanceObject(addr.Resource, NotDeposed) -} - -// DeposeResourceInstanceObjectForceKey is like DeposeResourceInstanceObject -// but uses a pre-allocated key. It's the caller's responsibility to ensure -// that there aren't any races to use a particular key; this method will panic -// if the given key is already in use. -func (s *SyncState) DeposeResourceInstanceObjectForceKey(addr addrs.AbsResourceInstance, forcedKey DeposedKey) { - s.lock.Lock() - defer s.lock.Unlock() - - if forcedKey == NotDeposed { - // Usage error: should use DeposeResourceInstanceObject in this case - panic("DeposeResourceInstanceObjectForceKey called without forced key") - } - - ms := s.state.Module(addr.Module) - if ms == nil { - return // Nothing to do, since there can't be any current object either. - } - - ms.deposeResourceInstanceObject(addr.Resource, forcedKey) -} - -// MaybeRestoreResourceInstanceDeposed will restore the deposed object with the -// given key on the specified resource as the current object for that instance -// if and only if that would not cause us to forget an existing current -// object for that instance. -// -// Returns true if the object was restored to current, or false if no change -// was made at all. -func (s *SyncState) MaybeRestoreResourceInstanceDeposed(addr addrs.AbsResourceInstance, key DeposedKey) bool { - s.lock.Lock() - defer s.lock.Unlock() - - if key == NotDeposed { - panic("MaybeRestoreResourceInstanceDeposed called without DeposedKey") - } - - ms := s.state.Module(addr.Module) - if ms == nil { - // Nothing to do, since the specified deposed object cannot exist. - return false - } - - return ms.maybeRestoreResourceInstanceDeposed(addr.Resource, key) -} - -// RemovePlannedResourceInstanceObjects removes from the state any resource -// instance objects that have the status ObjectPlanned, indiciating that they -// are just transient placeholders created during planning. -// -// Note that this does not restore any "ready" or "tainted" object that might -// have been present before the planned object was written. The only real use -// for this method is in preparing the state created during a refresh walk, -// where we run the planning step for certain instances just to create enough -// information to allow correct expression evaluation within provider and -// data resource blocks. Discarding planned instances in that case is okay -// because the refresh phase only creates planned objects to stand in for -// objects that don't exist yet, and thus the planned object must have been -// absent before by definition. -func (s *SyncState) RemovePlannedResourceInstanceObjects() { - // TODO: Merge together the refresh and plan phases into a single walk, - // so we can remove the need to create this "partial plan" during refresh - // that we then need to clean up before proceeding. - - s.lock.Lock() - defer s.lock.Unlock() - - for _, ms := range s.state.Modules { - moduleAddr := ms.Addr - - for _, rs := range ms.Resources { - resAddr := rs.Addr - - for ik, is := range rs.Instances { - instAddr := resAddr.Instance(ik) - - if is.Current != nil && is.Current.Status == ObjectPlanned { - // Setting the current instance to nil removes it from the - // state altogether if there are not also deposed instances. - ms.SetResourceInstanceCurrent(instAddr, nil, rs.ProviderConfig) - } - - for dk, obj := range is.Deposed { - // Deposed objects should never be "planned", but we'll - // do this anyway for the sake of completeness. - if obj.Status == ObjectPlanned { - ms.ForgetResourceInstanceDeposed(instAddr, dk) - } - } - } - } - - // We may have deleted some objects, which means that we may have - // left a module empty, and so we must prune to preserve the invariant - // that only the root module is allowed to be empty. - s.maybePruneModule(moduleAddr) - } -} - -// Lock acquires an explicit lock on the state, allowing direct read and write -// access to the returned state object. The caller must call Unlock once -// access is no longer needed, and then immediately discard the state pointer -// pointer. -// -// Most callers should not use this. Instead, use the concurrency-safe -// accessors and mutators provided directly on SyncState. -func (s *SyncState) Lock() *State { - s.lock.Lock() - return s.state -} - -// Unlock releases a lock previously acquired by Lock, at which point the -// caller must cease all use of the state pointer that was returned. -// -// Do not call this method except to end an explicit lock acquired by -// Lock. If a caller calls Unlock without first holding the lock, behavior -// is undefined. -func (s *SyncState) Unlock() { - s.lock.Unlock() -} - -// maybePruneModule will remove a module from the state altogether if it is -// empty, unless it's the root module which must always be present. -// -// This helper method is not concurrency-safe on its own, so must only be -// called while the caller is already holding the lock for writing. -func (s *SyncState) maybePruneModule(addr addrs.ModuleInstance) { - if addr.IsRoot() { - // We never prune the root. - return - } - - ms := s.state.Module(addr) - if ms == nil { - return - } - - if ms.empty() { - log.Printf("[TRACE] states.SyncState: pruning %s because it is empty", addr) - s.state.RemoveModule(addr) - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/config_traversals.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/config_traversals.go index 8e41f46e..2201d0c0 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/config_traversals.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/config_traversals.go @@ -5,7 +5,7 @@ import ( "fmt" "strconv" - "github.com/zclconf/go-cty/cty" + "github.com/hashicorp/go-cty/cty" ) // FormatCtyPath is a helper function to produce a user-friendly string @@ -54,15 +54,3 @@ func FormatError(err error) string { return fmt.Sprintf("%s: %s", FormatCtyPath(perr.Path), perr.Error()) } - -// FormatErrorPrefixed is like FormatError except that it presents any path -// information after the given prefix string, which is assumed to contain -// an HCL syntax representation of the value that errors are relative to. -func FormatErrorPrefixed(err error, prefix string) string { - perr, ok := err.(cty.PathError) - if !ok || len(perr.Path) == 0 { - return fmt.Sprintf("%s: %s", prefix, err.Error()) - } - - return fmt.Sprintf("%s%s: %s", prefix, FormatCtyPath(perr.Path), perr.Error()) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/contextual.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/contextual.go index 59c06b70..b063bc1d 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/contextual.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/contextual.go @@ -1,51 +1,9 @@ package tfdiags import ( - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/gocty" + "github.com/hashicorp/go-cty/cty" ) -// The "contextual" family of diagnostics are designed to allow separating -// the detection of a problem from placing that problem in context. For -// example, some code that is validating an object extracted from configuration -// may not have access to the configuration that generated it, but can still -// report problems within that object which the caller can then place in -// context by calling IsConfigBody on the returned diagnostics. -// -// When contextual diagnostics are used, the documentation for a method must -// be very explicit about what context is implied for any diagnostics returned, -// to help ensure the expected result. - -// contextualFromConfig is an interface type implemented by diagnostic types -// that can elaborate themselves when given information about the configuration -// body they are embedded in. -// -// Usually this entails extracting source location information in order to -// populate the "Subject" range. -type contextualFromConfigBody interface { - ElaborateFromConfigBody(hcl.Body) Diagnostic -} - -// InConfigBody returns a copy of the receiver with any config-contextual -// diagnostics elaborated in the context of the given body. -func (d Diagnostics) InConfigBody(body hcl.Body) Diagnostics { - if len(d) == 0 { - return nil - } - - ret := make(Diagnostics, len(d)) - for i, srcDiag := range d { - if cd, isCD := srcDiag.(contextualFromConfigBody); isCD { - ret[i] = cd.ElaborateFromConfigBody(body) - } else { - ret[i] = srcDiag - } - } - - return ret -} - // AttributeValue returns a diagnostic about an attribute value in an implied current // configuration context. This should be returned only from functions whose // interface specifies a clear configuration context that this will be @@ -96,236 +54,6 @@ func GetAttribute(d Diagnostic) cty.Path { type attributeDiagnostic struct { diagnosticBase attrPath cty.Path - subject *SourceRange // populated only after ElaborateFromConfigBody -} - -// ElaborateFromConfigBody finds the most accurate possible source location -// for a diagnostic's attribute path within the given body. -// -// Backing out from a path back to a source location is not always entirely -// possible because we lose some information in the decoding process, so -// if an exact position cannot be found then the returned diagnostic will -// refer to a position somewhere within the containing body, which is assumed -// to be better than no location at all. -// -// If possible it is generally better to report an error at a layer where -// source location information is still available, for more accuracy. This -// is not always possible due to system architecture, so this serves as a -// "best effort" fallback behavior for such situations. -func (d *attributeDiagnostic) ElaborateFromConfigBody(body hcl.Body) Diagnostic { - if len(d.attrPath) < 1 { - // Should never happen, but we'll allow it rather than crashing. - return d - } - - if d.subject != nil { - // Don't modify an already-elaborated diagnostic. - return d - } - - ret := *d - - // This function will often end up re-decoding values that were already - // decoded by an earlier step. This is non-ideal but is architecturally - // more convenient than arranging for source location information to be - // propagated to every place in Terraform, and this happens only in the - // presence of errors where performance isn't a concern. - - traverse := d.attrPath[:] - final := d.attrPath[len(d.attrPath)-1] - - // Index should never be the first step - // as indexing of top blocks (such as resources & data sources) - // is handled elsewhere - if _, isIdxStep := traverse[0].(cty.IndexStep); isIdxStep { - subject := SourceRangeFromHCL(body.MissingItemRange()) - ret.subject = &subject - return &ret - } - - // Process index separately - idxStep, hasIdx := final.(cty.IndexStep) - if hasIdx { - final = d.attrPath[len(d.attrPath)-2] - traverse = d.attrPath[:len(d.attrPath)-1] - } - - // If we have more than one step after removing index - // then we'll first try to traverse to a child body - // corresponding to the requested path. - if len(traverse) > 1 { - body = traversePathSteps(traverse, body) - } - - // Default is to indicate a missing item in the deepest body we reached - // while traversing. - subject := SourceRangeFromHCL(body.MissingItemRange()) - ret.subject = &subject - - // Once we get here, "final" should be a GetAttr step that maps to an - // attribute in our current body. - finalStep, isAttr := final.(cty.GetAttrStep) - if !isAttr { - return &ret - } - - content, _, contentDiags := body.PartialContent(&hcl.BodySchema{ - Attributes: []hcl.AttributeSchema{ - { - Name: finalStep.Name, - Required: true, - }, - }, - }) - if contentDiags.HasErrors() { - return &ret - } - - if attr, ok := content.Attributes[finalStep.Name]; ok { - hclRange := attr.Expr.Range() - if hasIdx { - // Try to be more precise by finding index range - hclRange = hclRangeFromIndexStepAndAttribute(idxStep, attr) - } - subject = SourceRangeFromHCL(hclRange) - ret.subject = &subject - } - - return &ret -} - -func traversePathSteps(traverse []cty.PathStep, body hcl.Body) hcl.Body { - for i := 0; i < len(traverse); i++ { - step := traverse[i] - - switch tStep := step.(type) { - case cty.GetAttrStep: - - var next cty.PathStep - if i < (len(traverse) - 1) { - next = traverse[i+1] - } - - // Will be indexing into our result here? - var indexType cty.Type - var indexVal cty.Value - if nextIndex, ok := next.(cty.IndexStep); ok { - indexVal = nextIndex.Key - indexType = indexVal.Type() - i++ // skip over the index on subsequent iterations - } - - var blockLabelNames []string - if indexType == cty.String { - // Map traversal means we expect one label for the key. - blockLabelNames = []string{"key"} - } - - // For intermediate steps we expect to be referring to a child - // block, so we'll attempt decoding under that assumption. - content, _, contentDiags := body.PartialContent(&hcl.BodySchema{ - Blocks: []hcl.BlockHeaderSchema{ - { - Type: tStep.Name, - LabelNames: blockLabelNames, - }, - }, - }) - if contentDiags.HasErrors() { - return body - } - filtered := make([]*hcl.Block, 0, len(content.Blocks)) - for _, block := range content.Blocks { - if block.Type == tStep.Name { - filtered = append(filtered, block) - } - } - if len(filtered) == 0 { - // Step doesn't refer to a block - continue - } - - switch indexType { - case cty.NilType: // no index at all - if len(filtered) != 1 { - return body - } - body = filtered[0].Body - case cty.Number: - var idx int - err := gocty.FromCtyValue(indexVal, &idx) - if err != nil || idx >= len(filtered) { - return body - } - body = filtered[idx].Body - case cty.String: - key := indexVal.AsString() - var block *hcl.Block - for _, candidate := range filtered { - if candidate.Labels[0] == key { - block = candidate - break - } - } - if block == nil { - // No block with this key, so we'll just indicate a - // missing item in the containing block. - return body - } - body = block.Body - default: - // Should never happen, because only string and numeric indices - // are supported by cty collections. - return body - } - - default: - // For any other kind of step, we'll just return our current body - // as the subject and accept that this is a little inaccurate. - return body - } - } - return body -} - -func hclRangeFromIndexStepAndAttribute(idxStep cty.IndexStep, attr *hcl.Attribute) hcl.Range { - switch idxStep.Key.Type() { - case cty.Number: - var idx int - err := gocty.FromCtyValue(idxStep.Key, &idx) - items, diags := hcl.ExprList(attr.Expr) - if diags.HasErrors() { - return attr.Expr.Range() - } - if err != nil || idx >= len(items) { - return attr.NameRange - } - return items[idx].Range() - case cty.String: - pairs, diags := hcl.ExprMap(attr.Expr) - if diags.HasErrors() { - return attr.Expr.Range() - } - stepKey := idxStep.Key.AsString() - for _, kvPair := range pairs { - key, err := kvPair.Key.Value(nil) - if err != nil { - return attr.Expr.Range() - } - if key.AsString() == stepKey { - startRng := kvPair.Value.StartRange() - return startRng - } - } - return attr.NameRange - } - return attr.Expr.Range() -} - -func (d *attributeDiagnostic) Source() Source { - return Source{ - Subject: d.subject, - } } // WholeContainingBody returns a diagnostic about the body that is an implied @@ -350,23 +78,4 @@ func WholeContainingBody(severity Severity, summary, detail string) Diagnostic { type wholeBodyDiagnostic struct { diagnosticBase - subject *SourceRange // populated only after ElaborateFromConfigBody -} - -func (d *wholeBodyDiagnostic) ElaborateFromConfigBody(body hcl.Body) Diagnostic { - if d.subject != nil { - // Don't modify an already-elaborated diagnostic. - return d - } - - ret := *d - rng := SourceRangeFromHCL(body.MissingItemRange()) - ret.subject = &rng - return &ret -} - -func (d *wholeBodyDiagnostic) Source() Source { - return Source{ - Subject: d.subject, - } } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/contextual_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/contextual_test.go new file mode 100644 index 00000000..e5eb6336 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/contextual_test.go @@ -0,0 +1,28 @@ +package tfdiags + +import ( + "reflect" + "testing" + + "github.com/hashicorp/go-cty/cty" +) + +func TestGetAttribute(t *testing.T) { + path := cty.Path{ + cty.GetAttrStep{Name: "foo"}, + cty.IndexStep{Key: cty.NumberIntVal(0)}, + cty.GetAttrStep{Name: "bar"}, + } + + d := AttributeValue( + Error, + "foo[0].bar", + "detail", + path, + ) + + p := GetAttribute(d) + if !reflect.DeepEqual(path, p) { + t.Fatalf("paths don't match:\nexpected: %#v\ngot: %#v", path, p) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/diagnostic.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/diagnostic.go index a7699cf0..a36da370 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/diagnostic.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/diagnostic.go @@ -1,18 +1,8 @@ package tfdiags -import ( - "github.com/hashicorp/hcl/v2" -) - type Diagnostic interface { Severity() Severity Description() Description - Source() Source - - // FromExpr returns the expression-related context for the diagnostic, if - // available. Returns nil if the diagnostic is not related to an - // expression evaluation. - FromExpr() *FromExpr } type Severity rune @@ -28,13 +18,3 @@ type Description struct { Summary string Detail string } - -type Source struct { - Subject *SourceRange - Context *SourceRange -} - -type FromExpr struct { - Expression hcl.Expression - EvalContext *hcl.EvalContext -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/diagnostic_base.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/diagnostic_base.go index 50bf9d8e..34816208 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/diagnostic_base.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/diagnostic_base.go @@ -2,7 +2,7 @@ package tfdiags // diagnosticBase can be embedded in other diagnostic structs to get // default implementations of Severity and Description. This type also -// has default implementations of Source and FromExpr that return no source +// has default implementations of Source that return no source // location or expression-related information, so embedders should generally // override those method to return more useful results where possible. type diagnosticBase struct { @@ -22,10 +22,10 @@ func (d diagnosticBase) Description() Description { } } -func (d diagnosticBase) Source() Source { - return Source{} -} - -func (d diagnosticBase) FromExpr() *FromExpr { - return nil +func Diag(sev Severity, summary, detail string) Diagnostic { + return &diagnosticBase{ + severity: sev, + summary: summary, + detail: detail, + } } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/diagnostics.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/diagnostics.go index a19fa80c..925c14fa 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/diagnostics.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/diagnostics.go @@ -3,13 +3,7 @@ package tfdiags import ( "bytes" "fmt" - "path/filepath" "sort" - "strings" - - "github.com/hashicorp/errwrap" - multierror "github.com/hashicorp/go-multierror" - "github.com/hashicorp/hcl/v2" ) // Diagnostics is a list of diagnostics. Diagnostics is intended to be used @@ -21,81 +15,6 @@ import ( // diagnostics to report at all. type Diagnostics []Diagnostic -// Append is the main interface for constructing Diagnostics lists, taking -// an existing list (which may be nil) and appending the new objects to it -// after normalizing them to be implementations of Diagnostic. -// -// The usual pattern for a function that natively "speaks" diagnostics is: -// -// // Create a nil Diagnostics at the start of the function -// var diags diag.Diagnostics -// -// // At later points, build on it if errors / warnings occur: -// foo, err := DoSomethingRisky() -// if err != nil { -// diags = diags.Append(err) -// } -// -// // Eventually return the result and diagnostics in place of error -// return result, diags -// -// Append accepts a variety of different diagnostic-like types, including -// native Go errors and HCL diagnostics. It also knows how to unwrap -// a multierror.Error into separate error diagnostics. It can be passed -// another Diagnostics to concatenate the two lists. If given something -// it cannot handle, this function will panic. -func (diags Diagnostics) Append(new ...interface{}) Diagnostics { - for _, item := range new { - if item == nil { - continue - } - - switch ti := item.(type) { - case Diagnostic: - diags = append(diags, ti) - case Diagnostics: - diags = append(diags, ti...) // flatten - case diagnosticsAsError: - diags = diags.Append(ti.Diagnostics) // unwrap - case NonFatalError: - diags = diags.Append(ti.Diagnostics) // unwrap - case hcl.Diagnostics: - for _, hclDiag := range ti { - diags = append(diags, hclDiagnostic{hclDiag}) - } - case *hcl.Diagnostic: - diags = append(diags, hclDiagnostic{ti}) - case *multierror.Error: - for _, err := range ti.Errors { - diags = append(diags, nativeError{err}) - } - case error: - switch { - case errwrap.ContainsType(ti, Diagnostics(nil)): - // If we have an errwrap wrapper with a Diagnostics hiding - // inside then we'll unpick it here to get access to the - // individual diagnostics. - diags = diags.Append(errwrap.GetType(ti, Diagnostics(nil))) - case errwrap.ContainsType(ti, hcl.Diagnostics(nil)): - // Likewise, if we have HCL diagnostics we'll unpick that too. - diags = diags.Append(errwrap.GetType(ti, hcl.Diagnostics(nil))) - default: - diags = append(diags, nativeError{ti}) - } - default: - panic(fmt.Errorf("can't construct diagnostic(s) from %T", item)) - } - } - - // Given the above, we should never end up with a non-nil empty slice - // here, but we'll make sure of that so callers can rely on empty == nil - if len(diags) == 0 { - return nil - } - - return diags -} - // HasErrors returns true if any of the diagnostics in the list have // a severity of Error. func (diags Diagnostics) HasErrors() bool { @@ -274,36 +193,10 @@ func (sd sortDiagnostics) Len() int { func (sd sortDiagnostics) Less(i, j int) bool { iD, jD := sd[i], sd[j] iSev, jSev := iD.Severity(), jD.Severity() - iSrc, jSrc := iD.Source(), jD.Source() switch { - case iSev != jSev: return iSev == Warning - - case (iSrc.Subject == nil) != (jSrc.Subject == nil): - return iSrc.Subject == nil - - case iSrc.Subject != nil && *iSrc.Subject != *jSrc.Subject: - iSubj := iSrc.Subject - jSubj := jSrc.Subject - switch { - case iSubj.Filename != jSubj.Filename: - // Path with fewer segments goes first if they are different lengths - sep := string(filepath.Separator) - iCount := strings.Count(iSubj.Filename, sep) - jCount := strings.Count(jSubj.Filename, sep) - if iCount != jCount { - return iCount < jCount - } - return iSubj.Filename < jSubj.Filename - case iSubj.Start.Byte != jSubj.Start.Byte: - return iSubj.Start.Byte < jSubj.Start.Byte - case iSubj.End.Byte != jSubj.End.Byte: - return iSubj.End.Byte < jSubj.End.Byte - } - fallthrough - default: // The remaining properties do not have a defined ordering, so // we'll leave it unspecified. Since we use sort.Stable in diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/diagnostics_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/diagnostics_test.go new file mode 100644 index 00000000..09761eca --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/diagnostics_test.go @@ -0,0 +1,234 @@ +package tfdiags + +import ( + "errors" + "strings" + "testing" +) + +func TestDiagnosticsErr(t *testing.T) { + t.Run("empty", func(t *testing.T) { + var diags Diagnostics + err := diags.Err() + if err != nil { + t.Errorf("got non-nil error %#v; want nil", err) + } + }) + t.Run("warning only", func(t *testing.T) { + var diags Diagnostics + diags = append(diags, SimpleWarning("bad")) + err := diags.Err() + if err != nil { + t.Errorf("got non-nil error %#v; want nil", err) + } + }) + t.Run("one error", func(t *testing.T) { + var diags Diagnostics + diags = append(diags, FromError(errors.New("didn't work"))) + err := diags.Err() + if err == nil { + t.Fatalf("got nil error %#v; want non-nil", err) + } + if got, want := err.Error(), "didn't work"; got != want { + t.Errorf("wrong error message\ngot: %s\nwant: %s", got, want) + } + }) + t.Run("two errors", func(t *testing.T) { + var diags Diagnostics + diags = append(diags, FromError(errors.New("didn't work"))) + diags = append(diags, FromError(errors.New("didn't work either"))) + err := diags.Err() + if err == nil { + t.Fatalf("got nil error %#v; want non-nil", err) + } + want := strings.TrimSpace(` +2 problems: + +- didn't work +- didn't work either +`) + if got := err.Error(); got != want { + t.Errorf("wrong error message\ngot: %s\nwant: %s", got, want) + } + }) + t.Run("error and warning", func(t *testing.T) { + var diags Diagnostics + diags = append(diags, FromError(errors.New("didn't work"))) + diags = append(diags, SimpleWarning("didn't work either")) + err := diags.Err() + if err == nil { + t.Fatalf("got nil error %#v; want non-nil", err) + } + // Since this "as error" mode is just a fallback for + // non-diagnostics-aware situations like tests, we don't actually + // distinguish warnings and errors here since the point is to just + // get the messages rendered. User-facing code should be printing + // each diagnostic separately, so won't enter this codepath, + want := strings.TrimSpace(` +2 problems: + +- didn't work +- didn't work either +`) + if got := err.Error(); got != want { + t.Errorf("wrong error message\ngot: %s\nwant: %s", got, want) + } + }) +} + +func TestDiagnosticsErrWithWarnings(t *testing.T) { + t.Run("empty", func(t *testing.T) { + var diags Diagnostics + err := diags.ErrWithWarnings() + if err != nil { + t.Errorf("got non-nil error %#v; want nil", err) + } + }) + t.Run("warning only", func(t *testing.T) { + var diags Diagnostics + diags = append(diags, SimpleWarning("bad")) + err := diags.ErrWithWarnings() + if err == nil { + t.Errorf("got nil error; want NonFatalError") + return + } + if _, ok := err.(NonFatalError); !ok { + t.Errorf("got %T; want NonFatalError", err) + } + }) + t.Run("one error", func(t *testing.T) { + var diags Diagnostics + diags = append(diags, FromError(errors.New("didn't work"))) + err := diags.ErrWithWarnings() + if err == nil { + t.Fatalf("got nil error %#v; want non-nil", err) + } + if got, want := err.Error(), "didn't work"; got != want { + t.Errorf("wrong error message\ngot: %s\nwant: %s", got, want) + } + }) + t.Run("two errors", func(t *testing.T) { + var diags Diagnostics + diags = append(diags, FromError(errors.New("didn't work"))) + diags = append(diags, FromError(errors.New("didn't work either"))) + err := diags.ErrWithWarnings() + if err == nil { + t.Fatalf("got nil error %#v; want non-nil", err) + } + want := strings.TrimSpace(` +2 problems: + +- didn't work +- didn't work either +`) + if got := err.Error(); got != want { + t.Errorf("wrong error message\ngot: %s\nwant: %s", got, want) + } + }) + t.Run("error and warning", func(t *testing.T) { + var diags Diagnostics + diags = append(diags, FromError(errors.New("didn't work"))) + diags = append(diags, SimpleWarning("didn't work either")) + err := diags.ErrWithWarnings() + if err == nil { + t.Fatalf("got nil error %#v; want non-nil", err) + } + // Since this "as error" mode is just a fallback for + // non-diagnostics-aware situations like tests, we don't actually + // distinguish warnings and errors here since the point is to just + // get the messages rendered. User-facing code should be printing + // each diagnostic separately, so won't enter this codepath, + want := strings.TrimSpace(` +2 problems: + +- didn't work +- didn't work either +`) + if got := err.Error(); got != want { + t.Errorf("wrong error message\ngot: %s\nwant: %s", got, want) + } + }) +} + +func TestDiagnosticsNonFatalErr(t *testing.T) { + t.Run("empty", func(t *testing.T) { + var diags Diagnostics + err := diags.NonFatalErr() + if err != nil { + t.Errorf("got non-nil error %#v; want nil", err) + } + }) + t.Run("warning only", func(t *testing.T) { + var diags Diagnostics + diags = append(diags, SimpleWarning("bad")) + err := diags.NonFatalErr() + if err == nil { + t.Errorf("got nil error; want NonFatalError") + return + } + if _, ok := err.(NonFatalError); !ok { + t.Errorf("got %T; want NonFatalError", err) + } + }) + t.Run("one error", func(t *testing.T) { + var diags Diagnostics + diags = append(diags, FromError(errors.New("didn't work"))) + err := diags.NonFatalErr() + if err == nil { + t.Fatalf("got nil error %#v; want non-nil", err) + } + if got, want := err.Error(), "didn't work"; got != want { + t.Errorf("wrong error message\ngot: %s\nwant: %s", got, want) + } + if _, ok := err.(NonFatalError); !ok { + t.Errorf("got %T; want NonFatalError", err) + } + }) + t.Run("two errors", func(t *testing.T) { + var diags Diagnostics + diags = append(diags, FromError(errors.New("didn't work"))) + diags = append(diags, FromError(errors.New("didn't work either"))) + err := diags.NonFatalErr() + if err == nil { + t.Fatalf("got nil error %#v; want non-nil", err) + } + want := strings.TrimSpace(` +2 problems: + +- didn't work +- didn't work either +`) + if got := err.Error(); got != want { + t.Errorf("wrong error message\ngot: %s\nwant: %s", got, want) + } + if _, ok := err.(NonFatalError); !ok { + t.Errorf("got %T; want NonFatalError", err) + } + }) + t.Run("error and warning", func(t *testing.T) { + var diags Diagnostics + diags = append(diags, FromError(errors.New("didn't work"))) + diags = append(diags, SimpleWarning("didn't work either")) + err := diags.NonFatalErr() + if err == nil { + t.Fatalf("got nil error %#v; want non-nil", err) + } + // Since this "as error" mode is just a fallback for + // non-diagnostics-aware situations like tests, we don't actually + // distinguish warnings and errors here since the point is to just + // get the messages rendered. User-facing code should be printing + // each diagnostic separately, so won't enter this codepath, + want := strings.TrimSpace(` +2 problems: + +- didn't work +- didn't work either +`) + if got := err.Error(); got != want { + t.Errorf("wrong error message\ngot: %s\nwant: %s", got, want) + } + if _, ok := err.(NonFatalError); !ok { + t.Errorf("got %T; want NonFatalError", err) + } + }) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/error.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/error.go index 13f7a714..b63c5d74 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/error.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/error.go @@ -17,12 +17,8 @@ func (e nativeError) Description() Description { } } -func (e nativeError) Source() Source { - // No source information available for a native error - return Source{} -} - -func (e nativeError) FromExpr() *FromExpr { - // Native errors are not expression-related - return nil +func FromError(err error) Diagnostic { + return &nativeError{ + err: err, + } } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/hcl.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/hcl.go deleted file mode 100644 index 8c781611..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/hcl.go +++ /dev/null @@ -1,87 +0,0 @@ -package tfdiags - -import ( - "github.com/hashicorp/hcl/v2" -) - -// hclDiagnostic is a Diagnostic implementation that wraps a HCL Diagnostic -type hclDiagnostic struct { - diag *hcl.Diagnostic -} - -var _ Diagnostic = hclDiagnostic{} - -func (d hclDiagnostic) Severity() Severity { - switch d.diag.Severity { - case hcl.DiagWarning: - return Warning - default: - return Error - } -} - -func (d hclDiagnostic) Description() Description { - return Description{ - Summary: d.diag.Summary, - Detail: d.diag.Detail, - } -} - -func (d hclDiagnostic) Source() Source { - var ret Source - if d.diag.Subject != nil { - rng := SourceRangeFromHCL(*d.diag.Subject) - ret.Subject = &rng - } - if d.diag.Context != nil { - rng := SourceRangeFromHCL(*d.diag.Context) - ret.Context = &rng - } - return ret -} - -func (d hclDiagnostic) FromExpr() *FromExpr { - if d.diag.Expression == nil || d.diag.EvalContext == nil { - return nil - } - return &FromExpr{ - Expression: d.diag.Expression, - EvalContext: d.diag.EvalContext, - } -} - -// SourceRangeFromHCL constructs a SourceRange from the corresponding range -// type within the HCL package. -func SourceRangeFromHCL(hclRange hcl.Range) SourceRange { - return SourceRange{ - Filename: hclRange.Filename, - Start: SourcePos{ - Line: hclRange.Start.Line, - Column: hclRange.Start.Column, - Byte: hclRange.Start.Byte, - }, - End: SourcePos{ - Line: hclRange.End.Line, - Column: hclRange.End.Column, - Byte: hclRange.End.Byte, - }, - } -} - -// ToHCL constructs a HCL Range from the receiving SourceRange. This is the -// opposite of SourceRangeFromHCL. -func (r SourceRange) ToHCL() hcl.Range { - return hcl.Range{ - Filename: r.Filename, - Start: hcl.Pos{ - Line: r.Start.Line, - Column: r.Start.Column, - Byte: r.Start.Byte, - }, - End: hcl.Pos{ - Line: r.End.Line, - Column: r.End.Column, - Byte: r.End.Byte, - }, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/rpc_friendly.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/rpc_friendly.go index 485063b0..1b78887e 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/rpc_friendly.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/rpc_friendly.go @@ -8,8 +8,6 @@ type rpcFriendlyDiag struct { Severity_ Severity Summary_ string Detail_ string - Subject_ *SourceRange - Context_ *SourceRange } // rpcFriendlyDiag transforms a given diagnostic so that is more friendly to @@ -20,13 +18,10 @@ type rpcFriendlyDiag struct { // serializations later. func makeRPCFriendlyDiag(diag Diagnostic) Diagnostic { desc := diag.Description() - source := diag.Source() return &rpcFriendlyDiag{ Severity_: diag.Severity(), Summary_: desc.Summary, Detail_: desc.Detail, - Subject_: source.Subject, - Context_: source.Context, } } @@ -41,19 +36,6 @@ func (d *rpcFriendlyDiag) Description() Description { } } -func (d *rpcFriendlyDiag) Source() Source { - return Source{ - Subject: d.Subject_, - Context: d.Context_, - } -} - -func (d rpcFriendlyDiag) FromExpr() *FromExpr { - // RPC-friendly diagnostics cannot preserve expression information because - // expressions themselves are not RPC-friendly. - return nil -} - func init() { gob.Register((*rpcFriendlyDiag)(nil)) } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/rpc_friendly_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/rpc_friendly_test.go new file mode 100644 index 00000000..ecaee858 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/rpc_friendly_test.go @@ -0,0 +1,48 @@ +package tfdiags + +import ( + "bytes" + "encoding/gob" + "fmt" + "reflect" + "testing" + + "github.com/davecgh/go-spew/spew" +) + +func TestDiagnosticsForRPC(t *testing.T) { + var diags Diagnostics + diags = append(diags, FromError(fmt.Errorf("bad"))) + diags = append(diags, SimpleWarning("less bad")) + + buf := bytes.Buffer{} + enc := gob.NewEncoder(&buf) + dec := gob.NewDecoder(&buf) + + rpcDiags := diags.ForRPC() + err := enc.Encode(rpcDiags) + if err != nil { + t.Fatalf("error on Encode: %s", err) + } + + var got Diagnostics + err = dec.Decode(&got) + if err != nil { + t.Fatalf("error on Decode: %s", err) + } + + want := Diagnostics{ + &rpcFriendlyDiag{ + Severity_: Error, + Summary_: "bad", + }, + &rpcFriendlyDiag{ + Severity_: Warning, + Summary_: "less bad", + }, + } + + if !reflect.DeepEqual(got, want) { + t.Errorf("wrong result\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(want)) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/simple_warning.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/simple_warning.go index b0f1ecd4..09191704 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/simple_warning.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/simple_warning.go @@ -18,13 +18,3 @@ func (e simpleWarning) Description() Description { Summary: string(e), } } - -func (e simpleWarning) Source() Source { - // No source information available for a simple warning - return Source{} -} - -func (e simpleWarning) FromExpr() *FromExpr { - // Simple warnings are not expression-related - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/source_range.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/source_range.go deleted file mode 100644 index 3031168d..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/source_range.go +++ /dev/null @@ -1,35 +0,0 @@ -package tfdiags - -import ( - "fmt" - "os" - "path/filepath" -) - -type SourceRange struct { - Filename string - Start, End SourcePos -} - -type SourcePos struct { - Line, Column, Byte int -} - -// StartString returns a string representation of the start of the range, -// including the filename and the line and column numbers. -func (r SourceRange) StartString() string { - filename := r.Filename - - // We'll try to relative-ize our filename here so it's less verbose - // in the common case of being in the current working directory. If not, - // we'll just show the full path. - wd, err := os.Getwd() - if err == nil { - relFn, err := filepath.Rel(wd, filename) - if err == nil { - filename = relFn - } - } - - return fmt.Sprintf("%s:%d,%d", filename, r.Start.Line, r.Start.Column) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/sourceless.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/sourceless.go deleted file mode 100644 index eaa27373..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags/sourceless.go +++ /dev/null @@ -1,13 +0,0 @@ -package tfdiags - -// Sourceless creates and returns a diagnostic with no source location -// information. This is generally used for operational-type errors that are -// caused by or relate to the environment where Terraform is running rather -// than to the provided configuration. -func Sourceless(severity Severity, summary, detail string) Diagnostic { - return diagnosticBase{ - severity: severity, - summary: summary, - detail: detail, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5/tfplugin5.pb.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5/tfplugin5.pb.go deleted file mode 100644 index 86fd21e4..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5/tfplugin5.pb.go +++ /dev/null @@ -1,3518 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: tfplugin5.proto - -package tfplugin5 - -import ( - context "context" - fmt "fmt" - proto "github.com/golang/protobuf/proto" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" - math "math" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type Diagnostic_Severity int32 - -const ( - Diagnostic_INVALID Diagnostic_Severity = 0 - Diagnostic_ERROR Diagnostic_Severity = 1 - Diagnostic_WARNING Diagnostic_Severity = 2 -) - -var Diagnostic_Severity_name = map[int32]string{ - 0: "INVALID", - 1: "ERROR", - 2: "WARNING", -} - -var Diagnostic_Severity_value = map[string]int32{ - "INVALID": 0, - "ERROR": 1, - "WARNING": 2, -} - -func (x Diagnostic_Severity) String() string { - return proto.EnumName(Diagnostic_Severity_name, int32(x)) -} - -func (Diagnostic_Severity) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{1, 0} -} - -type Schema_NestedBlock_NestingMode int32 - -const ( - Schema_NestedBlock_INVALID Schema_NestedBlock_NestingMode = 0 - Schema_NestedBlock_SINGLE Schema_NestedBlock_NestingMode = 1 - Schema_NestedBlock_LIST Schema_NestedBlock_NestingMode = 2 - Schema_NestedBlock_SET Schema_NestedBlock_NestingMode = 3 - Schema_NestedBlock_MAP Schema_NestedBlock_NestingMode = 4 - Schema_NestedBlock_GROUP Schema_NestedBlock_NestingMode = 5 -) - -var Schema_NestedBlock_NestingMode_name = map[int32]string{ - 0: "INVALID", - 1: "SINGLE", - 2: "LIST", - 3: "SET", - 4: "MAP", - 5: "GROUP", -} - -var Schema_NestedBlock_NestingMode_value = map[string]int32{ - "INVALID": 0, - "SINGLE": 1, - "LIST": 2, - "SET": 3, - "MAP": 4, - "GROUP": 5, -} - -func (x Schema_NestedBlock_NestingMode) String() string { - return proto.EnumName(Schema_NestedBlock_NestingMode_name, int32(x)) -} - -func (Schema_NestedBlock_NestingMode) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{5, 2, 0} -} - -// DynamicValue is an opaque encoding of terraform data, with the field name -// indicating the encoding scheme used. -type DynamicValue struct { - Msgpack []byte `protobuf:"bytes,1,opt,name=msgpack,proto3" json:"msgpack,omitempty"` - Json []byte `protobuf:"bytes,2,opt,name=json,proto3" json:"json,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *DynamicValue) Reset() { *m = DynamicValue{} } -func (m *DynamicValue) String() string { return proto.CompactTextString(m) } -func (*DynamicValue) ProtoMessage() {} -func (*DynamicValue) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{0} -} - -func (m *DynamicValue) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_DynamicValue.Unmarshal(m, b) -} -func (m *DynamicValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_DynamicValue.Marshal(b, m, deterministic) -} -func (m *DynamicValue) XXX_Merge(src proto.Message) { - xxx_messageInfo_DynamicValue.Merge(m, src) -} -func (m *DynamicValue) XXX_Size() int { - return xxx_messageInfo_DynamicValue.Size(m) -} -func (m *DynamicValue) XXX_DiscardUnknown() { - xxx_messageInfo_DynamicValue.DiscardUnknown(m) -} - -var xxx_messageInfo_DynamicValue proto.InternalMessageInfo - -func (m *DynamicValue) GetMsgpack() []byte { - if m != nil { - return m.Msgpack - } - return nil -} - -func (m *DynamicValue) GetJson() []byte { - if m != nil { - return m.Json - } - return nil -} - -type Diagnostic struct { - Severity Diagnostic_Severity `protobuf:"varint,1,opt,name=severity,proto3,enum=tfplugin5.Diagnostic_Severity" json:"severity,omitempty"` - Summary string `protobuf:"bytes,2,opt,name=summary,proto3" json:"summary,omitempty"` - Detail string `protobuf:"bytes,3,opt,name=detail,proto3" json:"detail,omitempty"` - Attribute *AttributePath `protobuf:"bytes,4,opt,name=attribute,proto3" json:"attribute,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Diagnostic) Reset() { *m = Diagnostic{} } -func (m *Diagnostic) String() string { return proto.CompactTextString(m) } -func (*Diagnostic) ProtoMessage() {} -func (*Diagnostic) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{1} -} - -func (m *Diagnostic) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Diagnostic.Unmarshal(m, b) -} -func (m *Diagnostic) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Diagnostic.Marshal(b, m, deterministic) -} -func (m *Diagnostic) XXX_Merge(src proto.Message) { - xxx_messageInfo_Diagnostic.Merge(m, src) -} -func (m *Diagnostic) XXX_Size() int { - return xxx_messageInfo_Diagnostic.Size(m) -} -func (m *Diagnostic) XXX_DiscardUnknown() { - xxx_messageInfo_Diagnostic.DiscardUnknown(m) -} - -var xxx_messageInfo_Diagnostic proto.InternalMessageInfo - -func (m *Diagnostic) GetSeverity() Diagnostic_Severity { - if m != nil { - return m.Severity - } - return Diagnostic_INVALID -} - -func (m *Diagnostic) GetSummary() string { - if m != nil { - return m.Summary - } - return "" -} - -func (m *Diagnostic) GetDetail() string { - if m != nil { - return m.Detail - } - return "" -} - -func (m *Diagnostic) GetAttribute() *AttributePath { - if m != nil { - return m.Attribute - } - return nil -} - -type AttributePath struct { - Steps []*AttributePath_Step `protobuf:"bytes,1,rep,name=steps,proto3" json:"steps,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *AttributePath) Reset() { *m = AttributePath{} } -func (m *AttributePath) String() string { return proto.CompactTextString(m) } -func (*AttributePath) ProtoMessage() {} -func (*AttributePath) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{2} -} - -func (m *AttributePath) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_AttributePath.Unmarshal(m, b) -} -func (m *AttributePath) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_AttributePath.Marshal(b, m, deterministic) -} -func (m *AttributePath) XXX_Merge(src proto.Message) { - xxx_messageInfo_AttributePath.Merge(m, src) -} -func (m *AttributePath) XXX_Size() int { - return xxx_messageInfo_AttributePath.Size(m) -} -func (m *AttributePath) XXX_DiscardUnknown() { - xxx_messageInfo_AttributePath.DiscardUnknown(m) -} - -var xxx_messageInfo_AttributePath proto.InternalMessageInfo - -func (m *AttributePath) GetSteps() []*AttributePath_Step { - if m != nil { - return m.Steps - } - return nil -} - -type AttributePath_Step struct { - // Types that are valid to be assigned to Selector: - // *AttributePath_Step_AttributeName - // *AttributePath_Step_ElementKeyString - // *AttributePath_Step_ElementKeyInt - Selector isAttributePath_Step_Selector `protobuf_oneof:"selector"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *AttributePath_Step) Reset() { *m = AttributePath_Step{} } -func (m *AttributePath_Step) String() string { return proto.CompactTextString(m) } -func (*AttributePath_Step) ProtoMessage() {} -func (*AttributePath_Step) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{2, 0} -} - -func (m *AttributePath_Step) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_AttributePath_Step.Unmarshal(m, b) -} -func (m *AttributePath_Step) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_AttributePath_Step.Marshal(b, m, deterministic) -} -func (m *AttributePath_Step) XXX_Merge(src proto.Message) { - xxx_messageInfo_AttributePath_Step.Merge(m, src) -} -func (m *AttributePath_Step) XXX_Size() int { - return xxx_messageInfo_AttributePath_Step.Size(m) -} -func (m *AttributePath_Step) XXX_DiscardUnknown() { - xxx_messageInfo_AttributePath_Step.DiscardUnknown(m) -} - -var xxx_messageInfo_AttributePath_Step proto.InternalMessageInfo - -type isAttributePath_Step_Selector interface { - isAttributePath_Step_Selector() -} - -type AttributePath_Step_AttributeName struct { - AttributeName string `protobuf:"bytes,1,opt,name=attribute_name,json=attributeName,proto3,oneof"` -} - -type AttributePath_Step_ElementKeyString struct { - ElementKeyString string `protobuf:"bytes,2,opt,name=element_key_string,json=elementKeyString,proto3,oneof"` -} - -type AttributePath_Step_ElementKeyInt struct { - ElementKeyInt int64 `protobuf:"varint,3,opt,name=element_key_int,json=elementKeyInt,proto3,oneof"` -} - -func (*AttributePath_Step_AttributeName) isAttributePath_Step_Selector() {} - -func (*AttributePath_Step_ElementKeyString) isAttributePath_Step_Selector() {} - -func (*AttributePath_Step_ElementKeyInt) isAttributePath_Step_Selector() {} - -func (m *AttributePath_Step) GetSelector() isAttributePath_Step_Selector { - if m != nil { - return m.Selector - } - return nil -} - -func (m *AttributePath_Step) GetAttributeName() string { - if x, ok := m.GetSelector().(*AttributePath_Step_AttributeName); ok { - return x.AttributeName - } - return "" -} - -func (m *AttributePath_Step) GetElementKeyString() string { - if x, ok := m.GetSelector().(*AttributePath_Step_ElementKeyString); ok { - return x.ElementKeyString - } - return "" -} - -func (m *AttributePath_Step) GetElementKeyInt() int64 { - if x, ok := m.GetSelector().(*AttributePath_Step_ElementKeyInt); ok { - return x.ElementKeyInt - } - return 0 -} - -// XXX_OneofWrappers is for the internal use of the proto package. -func (*AttributePath_Step) XXX_OneofWrappers() []interface{} { - return []interface{}{ - (*AttributePath_Step_AttributeName)(nil), - (*AttributePath_Step_ElementKeyString)(nil), - (*AttributePath_Step_ElementKeyInt)(nil), - } -} - -type Stop struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Stop) Reset() { *m = Stop{} } -func (m *Stop) String() string { return proto.CompactTextString(m) } -func (*Stop) ProtoMessage() {} -func (*Stop) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{3} -} - -func (m *Stop) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Stop.Unmarshal(m, b) -} -func (m *Stop) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Stop.Marshal(b, m, deterministic) -} -func (m *Stop) XXX_Merge(src proto.Message) { - xxx_messageInfo_Stop.Merge(m, src) -} -func (m *Stop) XXX_Size() int { - return xxx_messageInfo_Stop.Size(m) -} -func (m *Stop) XXX_DiscardUnknown() { - xxx_messageInfo_Stop.DiscardUnknown(m) -} - -var xxx_messageInfo_Stop proto.InternalMessageInfo - -type Stop_Request struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Stop_Request) Reset() { *m = Stop_Request{} } -func (m *Stop_Request) String() string { return proto.CompactTextString(m) } -func (*Stop_Request) ProtoMessage() {} -func (*Stop_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{3, 0} -} - -func (m *Stop_Request) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Stop_Request.Unmarshal(m, b) -} -func (m *Stop_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Stop_Request.Marshal(b, m, deterministic) -} -func (m *Stop_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_Stop_Request.Merge(m, src) -} -func (m *Stop_Request) XXX_Size() int { - return xxx_messageInfo_Stop_Request.Size(m) -} -func (m *Stop_Request) XXX_DiscardUnknown() { - xxx_messageInfo_Stop_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_Stop_Request proto.InternalMessageInfo - -type Stop_Response struct { - Error string `protobuf:"bytes,1,opt,name=Error,proto3" json:"Error,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Stop_Response) Reset() { *m = Stop_Response{} } -func (m *Stop_Response) String() string { return proto.CompactTextString(m) } -func (*Stop_Response) ProtoMessage() {} -func (*Stop_Response) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{3, 1} -} - -func (m *Stop_Response) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Stop_Response.Unmarshal(m, b) -} -func (m *Stop_Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Stop_Response.Marshal(b, m, deterministic) -} -func (m *Stop_Response) XXX_Merge(src proto.Message) { - xxx_messageInfo_Stop_Response.Merge(m, src) -} -func (m *Stop_Response) XXX_Size() int { - return xxx_messageInfo_Stop_Response.Size(m) -} -func (m *Stop_Response) XXX_DiscardUnknown() { - xxx_messageInfo_Stop_Response.DiscardUnknown(m) -} - -var xxx_messageInfo_Stop_Response proto.InternalMessageInfo - -func (m *Stop_Response) GetError() string { - if m != nil { - return m.Error - } - return "" -} - -// RawState holds the stored state for a resource to be upgraded by the -// provider. It can be in one of two formats, the current json encoded format -// in bytes, or the legacy flatmap format as a map of strings. -type RawState struct { - Json []byte `protobuf:"bytes,1,opt,name=json,proto3" json:"json,omitempty"` - Flatmap map[string]string `protobuf:"bytes,2,rep,name=flatmap,proto3" json:"flatmap,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *RawState) Reset() { *m = RawState{} } -func (m *RawState) String() string { return proto.CompactTextString(m) } -func (*RawState) ProtoMessage() {} -func (*RawState) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{4} -} - -func (m *RawState) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_RawState.Unmarshal(m, b) -} -func (m *RawState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_RawState.Marshal(b, m, deterministic) -} -func (m *RawState) XXX_Merge(src proto.Message) { - xxx_messageInfo_RawState.Merge(m, src) -} -func (m *RawState) XXX_Size() int { - return xxx_messageInfo_RawState.Size(m) -} -func (m *RawState) XXX_DiscardUnknown() { - xxx_messageInfo_RawState.DiscardUnknown(m) -} - -var xxx_messageInfo_RawState proto.InternalMessageInfo - -func (m *RawState) GetJson() []byte { - if m != nil { - return m.Json - } - return nil -} - -func (m *RawState) GetFlatmap() map[string]string { - if m != nil { - return m.Flatmap - } - return nil -} - -// Schema is the configuration schema for a Resource, Provider, or Provisioner. -type Schema struct { - // The version of the schema. - // Schemas are versioned, so that providers can upgrade a saved resource - // state when the schema is changed. - Version int64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - // Block is the top level configuration block for this schema. - Block *Schema_Block `protobuf:"bytes,2,opt,name=block,proto3" json:"block,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Schema) Reset() { *m = Schema{} } -func (m *Schema) String() string { return proto.CompactTextString(m) } -func (*Schema) ProtoMessage() {} -func (*Schema) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{5} -} - -func (m *Schema) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Schema.Unmarshal(m, b) -} -func (m *Schema) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Schema.Marshal(b, m, deterministic) -} -func (m *Schema) XXX_Merge(src proto.Message) { - xxx_messageInfo_Schema.Merge(m, src) -} -func (m *Schema) XXX_Size() int { - return xxx_messageInfo_Schema.Size(m) -} -func (m *Schema) XXX_DiscardUnknown() { - xxx_messageInfo_Schema.DiscardUnknown(m) -} - -var xxx_messageInfo_Schema proto.InternalMessageInfo - -func (m *Schema) GetVersion() int64 { - if m != nil { - return m.Version - } - return 0 -} - -func (m *Schema) GetBlock() *Schema_Block { - if m != nil { - return m.Block - } - return nil -} - -type Schema_Block struct { - Version int64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - Attributes []*Schema_Attribute `protobuf:"bytes,2,rep,name=attributes,proto3" json:"attributes,omitempty"` - BlockTypes []*Schema_NestedBlock `protobuf:"bytes,3,rep,name=block_types,json=blockTypes,proto3" json:"block_types,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Schema_Block) Reset() { *m = Schema_Block{} } -func (m *Schema_Block) String() string { return proto.CompactTextString(m) } -func (*Schema_Block) ProtoMessage() {} -func (*Schema_Block) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{5, 0} -} - -func (m *Schema_Block) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Schema_Block.Unmarshal(m, b) -} -func (m *Schema_Block) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Schema_Block.Marshal(b, m, deterministic) -} -func (m *Schema_Block) XXX_Merge(src proto.Message) { - xxx_messageInfo_Schema_Block.Merge(m, src) -} -func (m *Schema_Block) XXX_Size() int { - return xxx_messageInfo_Schema_Block.Size(m) -} -func (m *Schema_Block) XXX_DiscardUnknown() { - xxx_messageInfo_Schema_Block.DiscardUnknown(m) -} - -var xxx_messageInfo_Schema_Block proto.InternalMessageInfo - -func (m *Schema_Block) GetVersion() int64 { - if m != nil { - return m.Version - } - return 0 -} - -func (m *Schema_Block) GetAttributes() []*Schema_Attribute { - if m != nil { - return m.Attributes - } - return nil -} - -func (m *Schema_Block) GetBlockTypes() []*Schema_NestedBlock { - if m != nil { - return m.BlockTypes - } - return nil -} - -type Schema_Attribute struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Type []byte `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` - Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` - Required bool `protobuf:"varint,4,opt,name=required,proto3" json:"required,omitempty"` - Optional bool `protobuf:"varint,5,opt,name=optional,proto3" json:"optional,omitempty"` - Computed bool `protobuf:"varint,6,opt,name=computed,proto3" json:"computed,omitempty"` - Sensitive bool `protobuf:"varint,7,opt,name=sensitive,proto3" json:"sensitive,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Schema_Attribute) Reset() { *m = Schema_Attribute{} } -func (m *Schema_Attribute) String() string { return proto.CompactTextString(m) } -func (*Schema_Attribute) ProtoMessage() {} -func (*Schema_Attribute) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{5, 1} -} - -func (m *Schema_Attribute) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Schema_Attribute.Unmarshal(m, b) -} -func (m *Schema_Attribute) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Schema_Attribute.Marshal(b, m, deterministic) -} -func (m *Schema_Attribute) XXX_Merge(src proto.Message) { - xxx_messageInfo_Schema_Attribute.Merge(m, src) -} -func (m *Schema_Attribute) XXX_Size() int { - return xxx_messageInfo_Schema_Attribute.Size(m) -} -func (m *Schema_Attribute) XXX_DiscardUnknown() { - xxx_messageInfo_Schema_Attribute.DiscardUnknown(m) -} - -var xxx_messageInfo_Schema_Attribute proto.InternalMessageInfo - -func (m *Schema_Attribute) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -func (m *Schema_Attribute) GetType() []byte { - if m != nil { - return m.Type - } - return nil -} - -func (m *Schema_Attribute) GetDescription() string { - if m != nil { - return m.Description - } - return "" -} - -func (m *Schema_Attribute) GetRequired() bool { - if m != nil { - return m.Required - } - return false -} - -func (m *Schema_Attribute) GetOptional() bool { - if m != nil { - return m.Optional - } - return false -} - -func (m *Schema_Attribute) GetComputed() bool { - if m != nil { - return m.Computed - } - return false -} - -func (m *Schema_Attribute) GetSensitive() bool { - if m != nil { - return m.Sensitive - } - return false -} - -type Schema_NestedBlock struct { - TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` - Block *Schema_Block `protobuf:"bytes,2,opt,name=block,proto3" json:"block,omitempty"` - Nesting Schema_NestedBlock_NestingMode `protobuf:"varint,3,opt,name=nesting,proto3,enum=tfplugin5.Schema_NestedBlock_NestingMode" json:"nesting,omitempty"` - MinItems int64 `protobuf:"varint,4,opt,name=min_items,json=minItems,proto3" json:"min_items,omitempty"` - MaxItems int64 `protobuf:"varint,5,opt,name=max_items,json=maxItems,proto3" json:"max_items,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Schema_NestedBlock) Reset() { *m = Schema_NestedBlock{} } -func (m *Schema_NestedBlock) String() string { return proto.CompactTextString(m) } -func (*Schema_NestedBlock) ProtoMessage() {} -func (*Schema_NestedBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{5, 2} -} - -func (m *Schema_NestedBlock) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Schema_NestedBlock.Unmarshal(m, b) -} -func (m *Schema_NestedBlock) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Schema_NestedBlock.Marshal(b, m, deterministic) -} -func (m *Schema_NestedBlock) XXX_Merge(src proto.Message) { - xxx_messageInfo_Schema_NestedBlock.Merge(m, src) -} -func (m *Schema_NestedBlock) XXX_Size() int { - return xxx_messageInfo_Schema_NestedBlock.Size(m) -} -func (m *Schema_NestedBlock) XXX_DiscardUnknown() { - xxx_messageInfo_Schema_NestedBlock.DiscardUnknown(m) -} - -var xxx_messageInfo_Schema_NestedBlock proto.InternalMessageInfo - -func (m *Schema_NestedBlock) GetTypeName() string { - if m != nil { - return m.TypeName - } - return "" -} - -func (m *Schema_NestedBlock) GetBlock() *Schema_Block { - if m != nil { - return m.Block - } - return nil -} - -func (m *Schema_NestedBlock) GetNesting() Schema_NestedBlock_NestingMode { - if m != nil { - return m.Nesting - } - return Schema_NestedBlock_INVALID -} - -func (m *Schema_NestedBlock) GetMinItems() int64 { - if m != nil { - return m.MinItems - } - return 0 -} - -func (m *Schema_NestedBlock) GetMaxItems() int64 { - if m != nil { - return m.MaxItems - } - return 0 -} - -type GetProviderSchema struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetProviderSchema) Reset() { *m = GetProviderSchema{} } -func (m *GetProviderSchema) String() string { return proto.CompactTextString(m) } -func (*GetProviderSchema) ProtoMessage() {} -func (*GetProviderSchema) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{6} -} - -func (m *GetProviderSchema) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetProviderSchema.Unmarshal(m, b) -} -func (m *GetProviderSchema) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetProviderSchema.Marshal(b, m, deterministic) -} -func (m *GetProviderSchema) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetProviderSchema.Merge(m, src) -} -func (m *GetProviderSchema) XXX_Size() int { - return xxx_messageInfo_GetProviderSchema.Size(m) -} -func (m *GetProviderSchema) XXX_DiscardUnknown() { - xxx_messageInfo_GetProviderSchema.DiscardUnknown(m) -} - -var xxx_messageInfo_GetProviderSchema proto.InternalMessageInfo - -type GetProviderSchema_Request struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetProviderSchema_Request) Reset() { *m = GetProviderSchema_Request{} } -func (m *GetProviderSchema_Request) String() string { return proto.CompactTextString(m) } -func (*GetProviderSchema_Request) ProtoMessage() {} -func (*GetProviderSchema_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{6, 0} -} - -func (m *GetProviderSchema_Request) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetProviderSchema_Request.Unmarshal(m, b) -} -func (m *GetProviderSchema_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetProviderSchema_Request.Marshal(b, m, deterministic) -} -func (m *GetProviderSchema_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetProviderSchema_Request.Merge(m, src) -} -func (m *GetProviderSchema_Request) XXX_Size() int { - return xxx_messageInfo_GetProviderSchema_Request.Size(m) -} -func (m *GetProviderSchema_Request) XXX_DiscardUnknown() { - xxx_messageInfo_GetProviderSchema_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_GetProviderSchema_Request proto.InternalMessageInfo - -type GetProviderSchema_Response struct { - Provider *Schema `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` - ResourceSchemas map[string]*Schema `protobuf:"bytes,2,rep,name=resource_schemas,json=resourceSchemas,proto3" json:"resource_schemas,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - DataSourceSchemas map[string]*Schema `protobuf:"bytes,3,rep,name=data_source_schemas,json=dataSourceSchemas,proto3" json:"data_source_schemas,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Diagnostics []*Diagnostic `protobuf:"bytes,4,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetProviderSchema_Response) Reset() { *m = GetProviderSchema_Response{} } -func (m *GetProviderSchema_Response) String() string { return proto.CompactTextString(m) } -func (*GetProviderSchema_Response) ProtoMessage() {} -func (*GetProviderSchema_Response) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{6, 1} -} - -func (m *GetProviderSchema_Response) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetProviderSchema_Response.Unmarshal(m, b) -} -func (m *GetProviderSchema_Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetProviderSchema_Response.Marshal(b, m, deterministic) -} -func (m *GetProviderSchema_Response) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetProviderSchema_Response.Merge(m, src) -} -func (m *GetProviderSchema_Response) XXX_Size() int { - return xxx_messageInfo_GetProviderSchema_Response.Size(m) -} -func (m *GetProviderSchema_Response) XXX_DiscardUnknown() { - xxx_messageInfo_GetProviderSchema_Response.DiscardUnknown(m) -} - -var xxx_messageInfo_GetProviderSchema_Response proto.InternalMessageInfo - -func (m *GetProviderSchema_Response) GetProvider() *Schema { - if m != nil { - return m.Provider - } - return nil -} - -func (m *GetProviderSchema_Response) GetResourceSchemas() map[string]*Schema { - if m != nil { - return m.ResourceSchemas - } - return nil -} - -func (m *GetProviderSchema_Response) GetDataSourceSchemas() map[string]*Schema { - if m != nil { - return m.DataSourceSchemas - } - return nil -} - -func (m *GetProviderSchema_Response) GetDiagnostics() []*Diagnostic { - if m != nil { - return m.Diagnostics - } - return nil -} - -type PrepareProviderConfig struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PrepareProviderConfig) Reset() { *m = PrepareProviderConfig{} } -func (m *PrepareProviderConfig) String() string { return proto.CompactTextString(m) } -func (*PrepareProviderConfig) ProtoMessage() {} -func (*PrepareProviderConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{7} -} - -func (m *PrepareProviderConfig) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PrepareProviderConfig.Unmarshal(m, b) -} -func (m *PrepareProviderConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PrepareProviderConfig.Marshal(b, m, deterministic) -} -func (m *PrepareProviderConfig) XXX_Merge(src proto.Message) { - xxx_messageInfo_PrepareProviderConfig.Merge(m, src) -} -func (m *PrepareProviderConfig) XXX_Size() int { - return xxx_messageInfo_PrepareProviderConfig.Size(m) -} -func (m *PrepareProviderConfig) XXX_DiscardUnknown() { - xxx_messageInfo_PrepareProviderConfig.DiscardUnknown(m) -} - -var xxx_messageInfo_PrepareProviderConfig proto.InternalMessageInfo - -type PrepareProviderConfig_Request struct { - Config *DynamicValue `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PrepareProviderConfig_Request) Reset() { *m = PrepareProviderConfig_Request{} } -func (m *PrepareProviderConfig_Request) String() string { return proto.CompactTextString(m) } -func (*PrepareProviderConfig_Request) ProtoMessage() {} -func (*PrepareProviderConfig_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{7, 0} -} - -func (m *PrepareProviderConfig_Request) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PrepareProviderConfig_Request.Unmarshal(m, b) -} -func (m *PrepareProviderConfig_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PrepareProviderConfig_Request.Marshal(b, m, deterministic) -} -func (m *PrepareProviderConfig_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_PrepareProviderConfig_Request.Merge(m, src) -} -func (m *PrepareProviderConfig_Request) XXX_Size() int { - return xxx_messageInfo_PrepareProviderConfig_Request.Size(m) -} -func (m *PrepareProviderConfig_Request) XXX_DiscardUnknown() { - xxx_messageInfo_PrepareProviderConfig_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_PrepareProviderConfig_Request proto.InternalMessageInfo - -func (m *PrepareProviderConfig_Request) GetConfig() *DynamicValue { - if m != nil { - return m.Config - } - return nil -} - -type PrepareProviderConfig_Response struct { - PreparedConfig *DynamicValue `protobuf:"bytes,1,opt,name=prepared_config,json=preparedConfig,proto3" json:"prepared_config,omitempty"` - Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PrepareProviderConfig_Response) Reset() { *m = PrepareProviderConfig_Response{} } -func (m *PrepareProviderConfig_Response) String() string { return proto.CompactTextString(m) } -func (*PrepareProviderConfig_Response) ProtoMessage() {} -func (*PrepareProviderConfig_Response) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{7, 1} -} - -func (m *PrepareProviderConfig_Response) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PrepareProviderConfig_Response.Unmarshal(m, b) -} -func (m *PrepareProviderConfig_Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PrepareProviderConfig_Response.Marshal(b, m, deterministic) -} -func (m *PrepareProviderConfig_Response) XXX_Merge(src proto.Message) { - xxx_messageInfo_PrepareProviderConfig_Response.Merge(m, src) -} -func (m *PrepareProviderConfig_Response) XXX_Size() int { - return xxx_messageInfo_PrepareProviderConfig_Response.Size(m) -} -func (m *PrepareProviderConfig_Response) XXX_DiscardUnknown() { - xxx_messageInfo_PrepareProviderConfig_Response.DiscardUnknown(m) -} - -var xxx_messageInfo_PrepareProviderConfig_Response proto.InternalMessageInfo - -func (m *PrepareProviderConfig_Response) GetPreparedConfig() *DynamicValue { - if m != nil { - return m.PreparedConfig - } - return nil -} - -func (m *PrepareProviderConfig_Response) GetDiagnostics() []*Diagnostic { - if m != nil { - return m.Diagnostics - } - return nil -} - -type UpgradeResourceState struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpgradeResourceState) Reset() { *m = UpgradeResourceState{} } -func (m *UpgradeResourceState) String() string { return proto.CompactTextString(m) } -func (*UpgradeResourceState) ProtoMessage() {} -func (*UpgradeResourceState) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{8} -} - -func (m *UpgradeResourceState) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_UpgradeResourceState.Unmarshal(m, b) -} -func (m *UpgradeResourceState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_UpgradeResourceState.Marshal(b, m, deterministic) -} -func (m *UpgradeResourceState) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpgradeResourceState.Merge(m, src) -} -func (m *UpgradeResourceState) XXX_Size() int { - return xxx_messageInfo_UpgradeResourceState.Size(m) -} -func (m *UpgradeResourceState) XXX_DiscardUnknown() { - xxx_messageInfo_UpgradeResourceState.DiscardUnknown(m) -} - -var xxx_messageInfo_UpgradeResourceState proto.InternalMessageInfo - -type UpgradeResourceState_Request struct { - TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` - // version is the schema_version number recorded in the state file - Version int64 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` - // raw_state is the raw states as stored for the resource. Core does - // not have access to the schema of prior_version, so it's the - // provider's responsibility to interpret this value using the - // appropriate older schema. The raw_state will be the json encoded - // state, or a legacy flat-mapped format. - RawState *RawState `protobuf:"bytes,3,opt,name=raw_state,json=rawState,proto3" json:"raw_state,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpgradeResourceState_Request) Reset() { *m = UpgradeResourceState_Request{} } -func (m *UpgradeResourceState_Request) String() string { return proto.CompactTextString(m) } -func (*UpgradeResourceState_Request) ProtoMessage() {} -func (*UpgradeResourceState_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{8, 0} -} - -func (m *UpgradeResourceState_Request) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_UpgradeResourceState_Request.Unmarshal(m, b) -} -func (m *UpgradeResourceState_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_UpgradeResourceState_Request.Marshal(b, m, deterministic) -} -func (m *UpgradeResourceState_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpgradeResourceState_Request.Merge(m, src) -} -func (m *UpgradeResourceState_Request) XXX_Size() int { - return xxx_messageInfo_UpgradeResourceState_Request.Size(m) -} -func (m *UpgradeResourceState_Request) XXX_DiscardUnknown() { - xxx_messageInfo_UpgradeResourceState_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_UpgradeResourceState_Request proto.InternalMessageInfo - -func (m *UpgradeResourceState_Request) GetTypeName() string { - if m != nil { - return m.TypeName - } - return "" -} - -func (m *UpgradeResourceState_Request) GetVersion() int64 { - if m != nil { - return m.Version - } - return 0 -} - -func (m *UpgradeResourceState_Request) GetRawState() *RawState { - if m != nil { - return m.RawState - } - return nil -} - -type UpgradeResourceState_Response struct { - // new_state is a msgpack-encoded data structure that, when interpreted with - // the _current_ schema for this resource type, is functionally equivalent to - // that which was given in prior_state_raw. - UpgradedState *DynamicValue `protobuf:"bytes,1,opt,name=upgraded_state,json=upgradedState,proto3" json:"upgraded_state,omitempty"` - // diagnostics describes any errors encountered during migration that could not - // be safely resolved, and warnings about any possibly-risky assumptions made - // in the upgrade process. - Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpgradeResourceState_Response) Reset() { *m = UpgradeResourceState_Response{} } -func (m *UpgradeResourceState_Response) String() string { return proto.CompactTextString(m) } -func (*UpgradeResourceState_Response) ProtoMessage() {} -func (*UpgradeResourceState_Response) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{8, 1} -} - -func (m *UpgradeResourceState_Response) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_UpgradeResourceState_Response.Unmarshal(m, b) -} -func (m *UpgradeResourceState_Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_UpgradeResourceState_Response.Marshal(b, m, deterministic) -} -func (m *UpgradeResourceState_Response) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpgradeResourceState_Response.Merge(m, src) -} -func (m *UpgradeResourceState_Response) XXX_Size() int { - return xxx_messageInfo_UpgradeResourceState_Response.Size(m) -} -func (m *UpgradeResourceState_Response) XXX_DiscardUnknown() { - xxx_messageInfo_UpgradeResourceState_Response.DiscardUnknown(m) -} - -var xxx_messageInfo_UpgradeResourceState_Response proto.InternalMessageInfo - -func (m *UpgradeResourceState_Response) GetUpgradedState() *DynamicValue { - if m != nil { - return m.UpgradedState - } - return nil -} - -func (m *UpgradeResourceState_Response) GetDiagnostics() []*Diagnostic { - if m != nil { - return m.Diagnostics - } - return nil -} - -type ValidateResourceTypeConfig struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ValidateResourceTypeConfig) Reset() { *m = ValidateResourceTypeConfig{} } -func (m *ValidateResourceTypeConfig) String() string { return proto.CompactTextString(m) } -func (*ValidateResourceTypeConfig) ProtoMessage() {} -func (*ValidateResourceTypeConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{9} -} - -func (m *ValidateResourceTypeConfig) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ValidateResourceTypeConfig.Unmarshal(m, b) -} -func (m *ValidateResourceTypeConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ValidateResourceTypeConfig.Marshal(b, m, deterministic) -} -func (m *ValidateResourceTypeConfig) XXX_Merge(src proto.Message) { - xxx_messageInfo_ValidateResourceTypeConfig.Merge(m, src) -} -func (m *ValidateResourceTypeConfig) XXX_Size() int { - return xxx_messageInfo_ValidateResourceTypeConfig.Size(m) -} -func (m *ValidateResourceTypeConfig) XXX_DiscardUnknown() { - xxx_messageInfo_ValidateResourceTypeConfig.DiscardUnknown(m) -} - -var xxx_messageInfo_ValidateResourceTypeConfig proto.InternalMessageInfo - -type ValidateResourceTypeConfig_Request struct { - TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` - Config *DynamicValue `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ValidateResourceTypeConfig_Request) Reset() { *m = ValidateResourceTypeConfig_Request{} } -func (m *ValidateResourceTypeConfig_Request) String() string { return proto.CompactTextString(m) } -func (*ValidateResourceTypeConfig_Request) ProtoMessage() {} -func (*ValidateResourceTypeConfig_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{9, 0} -} - -func (m *ValidateResourceTypeConfig_Request) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ValidateResourceTypeConfig_Request.Unmarshal(m, b) -} -func (m *ValidateResourceTypeConfig_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ValidateResourceTypeConfig_Request.Marshal(b, m, deterministic) -} -func (m *ValidateResourceTypeConfig_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_ValidateResourceTypeConfig_Request.Merge(m, src) -} -func (m *ValidateResourceTypeConfig_Request) XXX_Size() int { - return xxx_messageInfo_ValidateResourceTypeConfig_Request.Size(m) -} -func (m *ValidateResourceTypeConfig_Request) XXX_DiscardUnknown() { - xxx_messageInfo_ValidateResourceTypeConfig_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_ValidateResourceTypeConfig_Request proto.InternalMessageInfo - -func (m *ValidateResourceTypeConfig_Request) GetTypeName() string { - if m != nil { - return m.TypeName - } - return "" -} - -func (m *ValidateResourceTypeConfig_Request) GetConfig() *DynamicValue { - if m != nil { - return m.Config - } - return nil -} - -type ValidateResourceTypeConfig_Response struct { - Diagnostics []*Diagnostic `protobuf:"bytes,1,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ValidateResourceTypeConfig_Response) Reset() { *m = ValidateResourceTypeConfig_Response{} } -func (m *ValidateResourceTypeConfig_Response) String() string { return proto.CompactTextString(m) } -func (*ValidateResourceTypeConfig_Response) ProtoMessage() {} -func (*ValidateResourceTypeConfig_Response) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{9, 1} -} - -func (m *ValidateResourceTypeConfig_Response) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ValidateResourceTypeConfig_Response.Unmarshal(m, b) -} -func (m *ValidateResourceTypeConfig_Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ValidateResourceTypeConfig_Response.Marshal(b, m, deterministic) -} -func (m *ValidateResourceTypeConfig_Response) XXX_Merge(src proto.Message) { - xxx_messageInfo_ValidateResourceTypeConfig_Response.Merge(m, src) -} -func (m *ValidateResourceTypeConfig_Response) XXX_Size() int { - return xxx_messageInfo_ValidateResourceTypeConfig_Response.Size(m) -} -func (m *ValidateResourceTypeConfig_Response) XXX_DiscardUnknown() { - xxx_messageInfo_ValidateResourceTypeConfig_Response.DiscardUnknown(m) -} - -var xxx_messageInfo_ValidateResourceTypeConfig_Response proto.InternalMessageInfo - -func (m *ValidateResourceTypeConfig_Response) GetDiagnostics() []*Diagnostic { - if m != nil { - return m.Diagnostics - } - return nil -} - -type ValidateDataSourceConfig struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ValidateDataSourceConfig) Reset() { *m = ValidateDataSourceConfig{} } -func (m *ValidateDataSourceConfig) String() string { return proto.CompactTextString(m) } -func (*ValidateDataSourceConfig) ProtoMessage() {} -func (*ValidateDataSourceConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{10} -} - -func (m *ValidateDataSourceConfig) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ValidateDataSourceConfig.Unmarshal(m, b) -} -func (m *ValidateDataSourceConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ValidateDataSourceConfig.Marshal(b, m, deterministic) -} -func (m *ValidateDataSourceConfig) XXX_Merge(src proto.Message) { - xxx_messageInfo_ValidateDataSourceConfig.Merge(m, src) -} -func (m *ValidateDataSourceConfig) XXX_Size() int { - return xxx_messageInfo_ValidateDataSourceConfig.Size(m) -} -func (m *ValidateDataSourceConfig) XXX_DiscardUnknown() { - xxx_messageInfo_ValidateDataSourceConfig.DiscardUnknown(m) -} - -var xxx_messageInfo_ValidateDataSourceConfig proto.InternalMessageInfo - -type ValidateDataSourceConfig_Request struct { - TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` - Config *DynamicValue `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ValidateDataSourceConfig_Request) Reset() { *m = ValidateDataSourceConfig_Request{} } -func (m *ValidateDataSourceConfig_Request) String() string { return proto.CompactTextString(m) } -func (*ValidateDataSourceConfig_Request) ProtoMessage() {} -func (*ValidateDataSourceConfig_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{10, 0} -} - -func (m *ValidateDataSourceConfig_Request) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ValidateDataSourceConfig_Request.Unmarshal(m, b) -} -func (m *ValidateDataSourceConfig_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ValidateDataSourceConfig_Request.Marshal(b, m, deterministic) -} -func (m *ValidateDataSourceConfig_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_ValidateDataSourceConfig_Request.Merge(m, src) -} -func (m *ValidateDataSourceConfig_Request) XXX_Size() int { - return xxx_messageInfo_ValidateDataSourceConfig_Request.Size(m) -} -func (m *ValidateDataSourceConfig_Request) XXX_DiscardUnknown() { - xxx_messageInfo_ValidateDataSourceConfig_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_ValidateDataSourceConfig_Request proto.InternalMessageInfo - -func (m *ValidateDataSourceConfig_Request) GetTypeName() string { - if m != nil { - return m.TypeName - } - return "" -} - -func (m *ValidateDataSourceConfig_Request) GetConfig() *DynamicValue { - if m != nil { - return m.Config - } - return nil -} - -type ValidateDataSourceConfig_Response struct { - Diagnostics []*Diagnostic `protobuf:"bytes,1,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ValidateDataSourceConfig_Response) Reset() { *m = ValidateDataSourceConfig_Response{} } -func (m *ValidateDataSourceConfig_Response) String() string { return proto.CompactTextString(m) } -func (*ValidateDataSourceConfig_Response) ProtoMessage() {} -func (*ValidateDataSourceConfig_Response) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{10, 1} -} - -func (m *ValidateDataSourceConfig_Response) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ValidateDataSourceConfig_Response.Unmarshal(m, b) -} -func (m *ValidateDataSourceConfig_Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ValidateDataSourceConfig_Response.Marshal(b, m, deterministic) -} -func (m *ValidateDataSourceConfig_Response) XXX_Merge(src proto.Message) { - xxx_messageInfo_ValidateDataSourceConfig_Response.Merge(m, src) -} -func (m *ValidateDataSourceConfig_Response) XXX_Size() int { - return xxx_messageInfo_ValidateDataSourceConfig_Response.Size(m) -} -func (m *ValidateDataSourceConfig_Response) XXX_DiscardUnknown() { - xxx_messageInfo_ValidateDataSourceConfig_Response.DiscardUnknown(m) -} - -var xxx_messageInfo_ValidateDataSourceConfig_Response proto.InternalMessageInfo - -func (m *ValidateDataSourceConfig_Response) GetDiagnostics() []*Diagnostic { - if m != nil { - return m.Diagnostics - } - return nil -} - -type Configure struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Configure) Reset() { *m = Configure{} } -func (m *Configure) String() string { return proto.CompactTextString(m) } -func (*Configure) ProtoMessage() {} -func (*Configure) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{11} -} - -func (m *Configure) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Configure.Unmarshal(m, b) -} -func (m *Configure) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Configure.Marshal(b, m, deterministic) -} -func (m *Configure) XXX_Merge(src proto.Message) { - xxx_messageInfo_Configure.Merge(m, src) -} -func (m *Configure) XXX_Size() int { - return xxx_messageInfo_Configure.Size(m) -} -func (m *Configure) XXX_DiscardUnknown() { - xxx_messageInfo_Configure.DiscardUnknown(m) -} - -var xxx_messageInfo_Configure proto.InternalMessageInfo - -type Configure_Request struct { - TerraformVersion string `protobuf:"bytes,1,opt,name=terraform_version,json=terraformVersion,proto3" json:"terraform_version,omitempty"` - Config *DynamicValue `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Configure_Request) Reset() { *m = Configure_Request{} } -func (m *Configure_Request) String() string { return proto.CompactTextString(m) } -func (*Configure_Request) ProtoMessage() {} -func (*Configure_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{11, 0} -} - -func (m *Configure_Request) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Configure_Request.Unmarshal(m, b) -} -func (m *Configure_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Configure_Request.Marshal(b, m, deterministic) -} -func (m *Configure_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_Configure_Request.Merge(m, src) -} -func (m *Configure_Request) XXX_Size() int { - return xxx_messageInfo_Configure_Request.Size(m) -} -func (m *Configure_Request) XXX_DiscardUnknown() { - xxx_messageInfo_Configure_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_Configure_Request proto.InternalMessageInfo - -func (m *Configure_Request) GetTerraformVersion() string { - if m != nil { - return m.TerraformVersion - } - return "" -} - -func (m *Configure_Request) GetConfig() *DynamicValue { - if m != nil { - return m.Config - } - return nil -} - -type Configure_Response struct { - Diagnostics []*Diagnostic `protobuf:"bytes,1,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Configure_Response) Reset() { *m = Configure_Response{} } -func (m *Configure_Response) String() string { return proto.CompactTextString(m) } -func (*Configure_Response) ProtoMessage() {} -func (*Configure_Response) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{11, 1} -} - -func (m *Configure_Response) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Configure_Response.Unmarshal(m, b) -} -func (m *Configure_Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Configure_Response.Marshal(b, m, deterministic) -} -func (m *Configure_Response) XXX_Merge(src proto.Message) { - xxx_messageInfo_Configure_Response.Merge(m, src) -} -func (m *Configure_Response) XXX_Size() int { - return xxx_messageInfo_Configure_Response.Size(m) -} -func (m *Configure_Response) XXX_DiscardUnknown() { - xxx_messageInfo_Configure_Response.DiscardUnknown(m) -} - -var xxx_messageInfo_Configure_Response proto.InternalMessageInfo - -func (m *Configure_Response) GetDiagnostics() []*Diagnostic { - if m != nil { - return m.Diagnostics - } - return nil -} - -type ReadResource struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ReadResource) Reset() { *m = ReadResource{} } -func (m *ReadResource) String() string { return proto.CompactTextString(m) } -func (*ReadResource) ProtoMessage() {} -func (*ReadResource) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{12} -} - -func (m *ReadResource) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ReadResource.Unmarshal(m, b) -} -func (m *ReadResource) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ReadResource.Marshal(b, m, deterministic) -} -func (m *ReadResource) XXX_Merge(src proto.Message) { - xxx_messageInfo_ReadResource.Merge(m, src) -} -func (m *ReadResource) XXX_Size() int { - return xxx_messageInfo_ReadResource.Size(m) -} -func (m *ReadResource) XXX_DiscardUnknown() { - xxx_messageInfo_ReadResource.DiscardUnknown(m) -} - -var xxx_messageInfo_ReadResource proto.InternalMessageInfo - -type ReadResource_Request struct { - TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` - CurrentState *DynamicValue `protobuf:"bytes,2,opt,name=current_state,json=currentState,proto3" json:"current_state,omitempty"` - Private []byte `protobuf:"bytes,3,opt,name=private,proto3" json:"private,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ReadResource_Request) Reset() { *m = ReadResource_Request{} } -func (m *ReadResource_Request) String() string { return proto.CompactTextString(m) } -func (*ReadResource_Request) ProtoMessage() {} -func (*ReadResource_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{12, 0} -} - -func (m *ReadResource_Request) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ReadResource_Request.Unmarshal(m, b) -} -func (m *ReadResource_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ReadResource_Request.Marshal(b, m, deterministic) -} -func (m *ReadResource_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_ReadResource_Request.Merge(m, src) -} -func (m *ReadResource_Request) XXX_Size() int { - return xxx_messageInfo_ReadResource_Request.Size(m) -} -func (m *ReadResource_Request) XXX_DiscardUnknown() { - xxx_messageInfo_ReadResource_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_ReadResource_Request proto.InternalMessageInfo - -func (m *ReadResource_Request) GetTypeName() string { - if m != nil { - return m.TypeName - } - return "" -} - -func (m *ReadResource_Request) GetCurrentState() *DynamicValue { - if m != nil { - return m.CurrentState - } - return nil -} - -func (m *ReadResource_Request) GetPrivate() []byte { - if m != nil { - return m.Private - } - return nil -} - -type ReadResource_Response struct { - NewState *DynamicValue `protobuf:"bytes,1,opt,name=new_state,json=newState,proto3" json:"new_state,omitempty"` - Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - Private []byte `protobuf:"bytes,3,opt,name=private,proto3" json:"private,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ReadResource_Response) Reset() { *m = ReadResource_Response{} } -func (m *ReadResource_Response) String() string { return proto.CompactTextString(m) } -func (*ReadResource_Response) ProtoMessage() {} -func (*ReadResource_Response) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{12, 1} -} - -func (m *ReadResource_Response) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ReadResource_Response.Unmarshal(m, b) -} -func (m *ReadResource_Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ReadResource_Response.Marshal(b, m, deterministic) -} -func (m *ReadResource_Response) XXX_Merge(src proto.Message) { - xxx_messageInfo_ReadResource_Response.Merge(m, src) -} -func (m *ReadResource_Response) XXX_Size() int { - return xxx_messageInfo_ReadResource_Response.Size(m) -} -func (m *ReadResource_Response) XXX_DiscardUnknown() { - xxx_messageInfo_ReadResource_Response.DiscardUnknown(m) -} - -var xxx_messageInfo_ReadResource_Response proto.InternalMessageInfo - -func (m *ReadResource_Response) GetNewState() *DynamicValue { - if m != nil { - return m.NewState - } - return nil -} - -func (m *ReadResource_Response) GetDiagnostics() []*Diagnostic { - if m != nil { - return m.Diagnostics - } - return nil -} - -func (m *ReadResource_Response) GetPrivate() []byte { - if m != nil { - return m.Private - } - return nil -} - -type PlanResourceChange struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PlanResourceChange) Reset() { *m = PlanResourceChange{} } -func (m *PlanResourceChange) String() string { return proto.CompactTextString(m) } -func (*PlanResourceChange) ProtoMessage() {} -func (*PlanResourceChange) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{13} -} - -func (m *PlanResourceChange) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PlanResourceChange.Unmarshal(m, b) -} -func (m *PlanResourceChange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PlanResourceChange.Marshal(b, m, deterministic) -} -func (m *PlanResourceChange) XXX_Merge(src proto.Message) { - xxx_messageInfo_PlanResourceChange.Merge(m, src) -} -func (m *PlanResourceChange) XXX_Size() int { - return xxx_messageInfo_PlanResourceChange.Size(m) -} -func (m *PlanResourceChange) XXX_DiscardUnknown() { - xxx_messageInfo_PlanResourceChange.DiscardUnknown(m) -} - -var xxx_messageInfo_PlanResourceChange proto.InternalMessageInfo - -type PlanResourceChange_Request struct { - TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` - PriorState *DynamicValue `protobuf:"bytes,2,opt,name=prior_state,json=priorState,proto3" json:"prior_state,omitempty"` - ProposedNewState *DynamicValue `protobuf:"bytes,3,opt,name=proposed_new_state,json=proposedNewState,proto3" json:"proposed_new_state,omitempty"` - Config *DynamicValue `protobuf:"bytes,4,opt,name=config,proto3" json:"config,omitempty"` - PriorPrivate []byte `protobuf:"bytes,5,opt,name=prior_private,json=priorPrivate,proto3" json:"prior_private,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PlanResourceChange_Request) Reset() { *m = PlanResourceChange_Request{} } -func (m *PlanResourceChange_Request) String() string { return proto.CompactTextString(m) } -func (*PlanResourceChange_Request) ProtoMessage() {} -func (*PlanResourceChange_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{13, 0} -} - -func (m *PlanResourceChange_Request) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PlanResourceChange_Request.Unmarshal(m, b) -} -func (m *PlanResourceChange_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PlanResourceChange_Request.Marshal(b, m, deterministic) -} -func (m *PlanResourceChange_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_PlanResourceChange_Request.Merge(m, src) -} -func (m *PlanResourceChange_Request) XXX_Size() int { - return xxx_messageInfo_PlanResourceChange_Request.Size(m) -} -func (m *PlanResourceChange_Request) XXX_DiscardUnknown() { - xxx_messageInfo_PlanResourceChange_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_PlanResourceChange_Request proto.InternalMessageInfo - -func (m *PlanResourceChange_Request) GetTypeName() string { - if m != nil { - return m.TypeName - } - return "" -} - -func (m *PlanResourceChange_Request) GetPriorState() *DynamicValue { - if m != nil { - return m.PriorState - } - return nil -} - -func (m *PlanResourceChange_Request) GetProposedNewState() *DynamicValue { - if m != nil { - return m.ProposedNewState - } - return nil -} - -func (m *PlanResourceChange_Request) GetConfig() *DynamicValue { - if m != nil { - return m.Config - } - return nil -} - -func (m *PlanResourceChange_Request) GetPriorPrivate() []byte { - if m != nil { - return m.PriorPrivate - } - return nil -} - -type PlanResourceChange_Response struct { - PlannedState *DynamicValue `protobuf:"bytes,1,opt,name=planned_state,json=plannedState,proto3" json:"planned_state,omitempty"` - RequiresReplace []*AttributePath `protobuf:"bytes,2,rep,name=requires_replace,json=requiresReplace,proto3" json:"requires_replace,omitempty"` - PlannedPrivate []byte `protobuf:"bytes,3,opt,name=planned_private,json=plannedPrivate,proto3" json:"planned_private,omitempty"` - Diagnostics []*Diagnostic `protobuf:"bytes,4,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - // This may be set only by the helper/schema "SDK" in the main Terraform - // repository, to request that Terraform Core >=0.12 permit additional - // inconsistencies that can result from the legacy SDK type system - // and its imprecise mapping to the >=0.12 type system. - // The change in behavior implied by this flag makes sense only for the - // specific details of the legacy SDK type system, and are not a general - // mechanism to avoid proper type handling in providers. - // - // ==== DO NOT USE THIS ==== - // ==== THIS MUST BE LEFT UNSET IN ALL OTHER SDKS ==== - // ==== DO NOT USE THIS ==== - LegacyTypeSystem bool `protobuf:"varint,5,opt,name=legacy_type_system,json=legacyTypeSystem,proto3" json:"legacy_type_system,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PlanResourceChange_Response) Reset() { *m = PlanResourceChange_Response{} } -func (m *PlanResourceChange_Response) String() string { return proto.CompactTextString(m) } -func (*PlanResourceChange_Response) ProtoMessage() {} -func (*PlanResourceChange_Response) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{13, 1} -} - -func (m *PlanResourceChange_Response) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PlanResourceChange_Response.Unmarshal(m, b) -} -func (m *PlanResourceChange_Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PlanResourceChange_Response.Marshal(b, m, deterministic) -} -func (m *PlanResourceChange_Response) XXX_Merge(src proto.Message) { - xxx_messageInfo_PlanResourceChange_Response.Merge(m, src) -} -func (m *PlanResourceChange_Response) XXX_Size() int { - return xxx_messageInfo_PlanResourceChange_Response.Size(m) -} -func (m *PlanResourceChange_Response) XXX_DiscardUnknown() { - xxx_messageInfo_PlanResourceChange_Response.DiscardUnknown(m) -} - -var xxx_messageInfo_PlanResourceChange_Response proto.InternalMessageInfo - -func (m *PlanResourceChange_Response) GetPlannedState() *DynamicValue { - if m != nil { - return m.PlannedState - } - return nil -} - -func (m *PlanResourceChange_Response) GetRequiresReplace() []*AttributePath { - if m != nil { - return m.RequiresReplace - } - return nil -} - -func (m *PlanResourceChange_Response) GetPlannedPrivate() []byte { - if m != nil { - return m.PlannedPrivate - } - return nil -} - -func (m *PlanResourceChange_Response) GetDiagnostics() []*Diagnostic { - if m != nil { - return m.Diagnostics - } - return nil -} - -func (m *PlanResourceChange_Response) GetLegacyTypeSystem() bool { - if m != nil { - return m.LegacyTypeSystem - } - return false -} - -type ApplyResourceChange struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ApplyResourceChange) Reset() { *m = ApplyResourceChange{} } -func (m *ApplyResourceChange) String() string { return proto.CompactTextString(m) } -func (*ApplyResourceChange) ProtoMessage() {} -func (*ApplyResourceChange) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{14} -} - -func (m *ApplyResourceChange) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ApplyResourceChange.Unmarshal(m, b) -} -func (m *ApplyResourceChange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ApplyResourceChange.Marshal(b, m, deterministic) -} -func (m *ApplyResourceChange) XXX_Merge(src proto.Message) { - xxx_messageInfo_ApplyResourceChange.Merge(m, src) -} -func (m *ApplyResourceChange) XXX_Size() int { - return xxx_messageInfo_ApplyResourceChange.Size(m) -} -func (m *ApplyResourceChange) XXX_DiscardUnknown() { - xxx_messageInfo_ApplyResourceChange.DiscardUnknown(m) -} - -var xxx_messageInfo_ApplyResourceChange proto.InternalMessageInfo - -type ApplyResourceChange_Request struct { - TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` - PriorState *DynamicValue `protobuf:"bytes,2,opt,name=prior_state,json=priorState,proto3" json:"prior_state,omitempty"` - PlannedState *DynamicValue `protobuf:"bytes,3,opt,name=planned_state,json=plannedState,proto3" json:"planned_state,omitempty"` - Config *DynamicValue `protobuf:"bytes,4,opt,name=config,proto3" json:"config,omitempty"` - PlannedPrivate []byte `protobuf:"bytes,5,opt,name=planned_private,json=plannedPrivate,proto3" json:"planned_private,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ApplyResourceChange_Request) Reset() { *m = ApplyResourceChange_Request{} } -func (m *ApplyResourceChange_Request) String() string { return proto.CompactTextString(m) } -func (*ApplyResourceChange_Request) ProtoMessage() {} -func (*ApplyResourceChange_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{14, 0} -} - -func (m *ApplyResourceChange_Request) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ApplyResourceChange_Request.Unmarshal(m, b) -} -func (m *ApplyResourceChange_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ApplyResourceChange_Request.Marshal(b, m, deterministic) -} -func (m *ApplyResourceChange_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_ApplyResourceChange_Request.Merge(m, src) -} -func (m *ApplyResourceChange_Request) XXX_Size() int { - return xxx_messageInfo_ApplyResourceChange_Request.Size(m) -} -func (m *ApplyResourceChange_Request) XXX_DiscardUnknown() { - xxx_messageInfo_ApplyResourceChange_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_ApplyResourceChange_Request proto.InternalMessageInfo - -func (m *ApplyResourceChange_Request) GetTypeName() string { - if m != nil { - return m.TypeName - } - return "" -} - -func (m *ApplyResourceChange_Request) GetPriorState() *DynamicValue { - if m != nil { - return m.PriorState - } - return nil -} - -func (m *ApplyResourceChange_Request) GetPlannedState() *DynamicValue { - if m != nil { - return m.PlannedState - } - return nil -} - -func (m *ApplyResourceChange_Request) GetConfig() *DynamicValue { - if m != nil { - return m.Config - } - return nil -} - -func (m *ApplyResourceChange_Request) GetPlannedPrivate() []byte { - if m != nil { - return m.PlannedPrivate - } - return nil -} - -type ApplyResourceChange_Response struct { - NewState *DynamicValue `protobuf:"bytes,1,opt,name=new_state,json=newState,proto3" json:"new_state,omitempty"` - Private []byte `protobuf:"bytes,2,opt,name=private,proto3" json:"private,omitempty"` - Diagnostics []*Diagnostic `protobuf:"bytes,3,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - // This may be set only by the helper/schema "SDK" in the main Terraform - // repository, to request that Terraform Core >=0.12 permit additional - // inconsistencies that can result from the legacy SDK type system - // and its imprecise mapping to the >=0.12 type system. - // The change in behavior implied by this flag makes sense only for the - // specific details of the legacy SDK type system, and are not a general - // mechanism to avoid proper type handling in providers. - // - // ==== DO NOT USE THIS ==== - // ==== THIS MUST BE LEFT UNSET IN ALL OTHER SDKS ==== - // ==== DO NOT USE THIS ==== - LegacyTypeSystem bool `protobuf:"varint,4,opt,name=legacy_type_system,json=legacyTypeSystem,proto3" json:"legacy_type_system,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ApplyResourceChange_Response) Reset() { *m = ApplyResourceChange_Response{} } -func (m *ApplyResourceChange_Response) String() string { return proto.CompactTextString(m) } -func (*ApplyResourceChange_Response) ProtoMessage() {} -func (*ApplyResourceChange_Response) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{14, 1} -} - -func (m *ApplyResourceChange_Response) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ApplyResourceChange_Response.Unmarshal(m, b) -} -func (m *ApplyResourceChange_Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ApplyResourceChange_Response.Marshal(b, m, deterministic) -} -func (m *ApplyResourceChange_Response) XXX_Merge(src proto.Message) { - xxx_messageInfo_ApplyResourceChange_Response.Merge(m, src) -} -func (m *ApplyResourceChange_Response) XXX_Size() int { - return xxx_messageInfo_ApplyResourceChange_Response.Size(m) -} -func (m *ApplyResourceChange_Response) XXX_DiscardUnknown() { - xxx_messageInfo_ApplyResourceChange_Response.DiscardUnknown(m) -} - -var xxx_messageInfo_ApplyResourceChange_Response proto.InternalMessageInfo - -func (m *ApplyResourceChange_Response) GetNewState() *DynamicValue { - if m != nil { - return m.NewState - } - return nil -} - -func (m *ApplyResourceChange_Response) GetPrivate() []byte { - if m != nil { - return m.Private - } - return nil -} - -func (m *ApplyResourceChange_Response) GetDiagnostics() []*Diagnostic { - if m != nil { - return m.Diagnostics - } - return nil -} - -func (m *ApplyResourceChange_Response) GetLegacyTypeSystem() bool { - if m != nil { - return m.LegacyTypeSystem - } - return false -} - -type ImportResourceState struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ImportResourceState) Reset() { *m = ImportResourceState{} } -func (m *ImportResourceState) String() string { return proto.CompactTextString(m) } -func (*ImportResourceState) ProtoMessage() {} -func (*ImportResourceState) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{15} -} - -func (m *ImportResourceState) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ImportResourceState.Unmarshal(m, b) -} -func (m *ImportResourceState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ImportResourceState.Marshal(b, m, deterministic) -} -func (m *ImportResourceState) XXX_Merge(src proto.Message) { - xxx_messageInfo_ImportResourceState.Merge(m, src) -} -func (m *ImportResourceState) XXX_Size() int { - return xxx_messageInfo_ImportResourceState.Size(m) -} -func (m *ImportResourceState) XXX_DiscardUnknown() { - xxx_messageInfo_ImportResourceState.DiscardUnknown(m) -} - -var xxx_messageInfo_ImportResourceState proto.InternalMessageInfo - -type ImportResourceState_Request struct { - TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ImportResourceState_Request) Reset() { *m = ImportResourceState_Request{} } -func (m *ImportResourceState_Request) String() string { return proto.CompactTextString(m) } -func (*ImportResourceState_Request) ProtoMessage() {} -func (*ImportResourceState_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{15, 0} -} - -func (m *ImportResourceState_Request) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ImportResourceState_Request.Unmarshal(m, b) -} -func (m *ImportResourceState_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ImportResourceState_Request.Marshal(b, m, deterministic) -} -func (m *ImportResourceState_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_ImportResourceState_Request.Merge(m, src) -} -func (m *ImportResourceState_Request) XXX_Size() int { - return xxx_messageInfo_ImportResourceState_Request.Size(m) -} -func (m *ImportResourceState_Request) XXX_DiscardUnknown() { - xxx_messageInfo_ImportResourceState_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_ImportResourceState_Request proto.InternalMessageInfo - -func (m *ImportResourceState_Request) GetTypeName() string { - if m != nil { - return m.TypeName - } - return "" -} - -func (m *ImportResourceState_Request) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -type ImportResourceState_ImportedResource struct { - TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` - State *DynamicValue `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` - Private []byte `protobuf:"bytes,3,opt,name=private,proto3" json:"private,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ImportResourceState_ImportedResource) Reset() { *m = ImportResourceState_ImportedResource{} } -func (m *ImportResourceState_ImportedResource) String() string { return proto.CompactTextString(m) } -func (*ImportResourceState_ImportedResource) ProtoMessage() {} -func (*ImportResourceState_ImportedResource) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{15, 1} -} - -func (m *ImportResourceState_ImportedResource) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ImportResourceState_ImportedResource.Unmarshal(m, b) -} -func (m *ImportResourceState_ImportedResource) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ImportResourceState_ImportedResource.Marshal(b, m, deterministic) -} -func (m *ImportResourceState_ImportedResource) XXX_Merge(src proto.Message) { - xxx_messageInfo_ImportResourceState_ImportedResource.Merge(m, src) -} -func (m *ImportResourceState_ImportedResource) XXX_Size() int { - return xxx_messageInfo_ImportResourceState_ImportedResource.Size(m) -} -func (m *ImportResourceState_ImportedResource) XXX_DiscardUnknown() { - xxx_messageInfo_ImportResourceState_ImportedResource.DiscardUnknown(m) -} - -var xxx_messageInfo_ImportResourceState_ImportedResource proto.InternalMessageInfo - -func (m *ImportResourceState_ImportedResource) GetTypeName() string { - if m != nil { - return m.TypeName - } - return "" -} - -func (m *ImportResourceState_ImportedResource) GetState() *DynamicValue { - if m != nil { - return m.State - } - return nil -} - -func (m *ImportResourceState_ImportedResource) GetPrivate() []byte { - if m != nil { - return m.Private - } - return nil -} - -type ImportResourceState_Response struct { - ImportedResources []*ImportResourceState_ImportedResource `protobuf:"bytes,1,rep,name=imported_resources,json=importedResources,proto3" json:"imported_resources,omitempty"` - Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ImportResourceState_Response) Reset() { *m = ImportResourceState_Response{} } -func (m *ImportResourceState_Response) String() string { return proto.CompactTextString(m) } -func (*ImportResourceState_Response) ProtoMessage() {} -func (*ImportResourceState_Response) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{15, 2} -} - -func (m *ImportResourceState_Response) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ImportResourceState_Response.Unmarshal(m, b) -} -func (m *ImportResourceState_Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ImportResourceState_Response.Marshal(b, m, deterministic) -} -func (m *ImportResourceState_Response) XXX_Merge(src proto.Message) { - xxx_messageInfo_ImportResourceState_Response.Merge(m, src) -} -func (m *ImportResourceState_Response) XXX_Size() int { - return xxx_messageInfo_ImportResourceState_Response.Size(m) -} -func (m *ImportResourceState_Response) XXX_DiscardUnknown() { - xxx_messageInfo_ImportResourceState_Response.DiscardUnknown(m) -} - -var xxx_messageInfo_ImportResourceState_Response proto.InternalMessageInfo - -func (m *ImportResourceState_Response) GetImportedResources() []*ImportResourceState_ImportedResource { - if m != nil { - return m.ImportedResources - } - return nil -} - -func (m *ImportResourceState_Response) GetDiagnostics() []*Diagnostic { - if m != nil { - return m.Diagnostics - } - return nil -} - -type ReadDataSource struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ReadDataSource) Reset() { *m = ReadDataSource{} } -func (m *ReadDataSource) String() string { return proto.CompactTextString(m) } -func (*ReadDataSource) ProtoMessage() {} -func (*ReadDataSource) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{16} -} - -func (m *ReadDataSource) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ReadDataSource.Unmarshal(m, b) -} -func (m *ReadDataSource) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ReadDataSource.Marshal(b, m, deterministic) -} -func (m *ReadDataSource) XXX_Merge(src proto.Message) { - xxx_messageInfo_ReadDataSource.Merge(m, src) -} -func (m *ReadDataSource) XXX_Size() int { - return xxx_messageInfo_ReadDataSource.Size(m) -} -func (m *ReadDataSource) XXX_DiscardUnknown() { - xxx_messageInfo_ReadDataSource.DiscardUnknown(m) -} - -var xxx_messageInfo_ReadDataSource proto.InternalMessageInfo - -type ReadDataSource_Request struct { - TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` - Config *DynamicValue `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ReadDataSource_Request) Reset() { *m = ReadDataSource_Request{} } -func (m *ReadDataSource_Request) String() string { return proto.CompactTextString(m) } -func (*ReadDataSource_Request) ProtoMessage() {} -func (*ReadDataSource_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{16, 0} -} - -func (m *ReadDataSource_Request) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ReadDataSource_Request.Unmarshal(m, b) -} -func (m *ReadDataSource_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ReadDataSource_Request.Marshal(b, m, deterministic) -} -func (m *ReadDataSource_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_ReadDataSource_Request.Merge(m, src) -} -func (m *ReadDataSource_Request) XXX_Size() int { - return xxx_messageInfo_ReadDataSource_Request.Size(m) -} -func (m *ReadDataSource_Request) XXX_DiscardUnknown() { - xxx_messageInfo_ReadDataSource_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_ReadDataSource_Request proto.InternalMessageInfo - -func (m *ReadDataSource_Request) GetTypeName() string { - if m != nil { - return m.TypeName - } - return "" -} - -func (m *ReadDataSource_Request) GetConfig() *DynamicValue { - if m != nil { - return m.Config - } - return nil -} - -type ReadDataSource_Response struct { - State *DynamicValue `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` - Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ReadDataSource_Response) Reset() { *m = ReadDataSource_Response{} } -func (m *ReadDataSource_Response) String() string { return proto.CompactTextString(m) } -func (*ReadDataSource_Response) ProtoMessage() {} -func (*ReadDataSource_Response) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{16, 1} -} - -func (m *ReadDataSource_Response) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ReadDataSource_Response.Unmarshal(m, b) -} -func (m *ReadDataSource_Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ReadDataSource_Response.Marshal(b, m, deterministic) -} -func (m *ReadDataSource_Response) XXX_Merge(src proto.Message) { - xxx_messageInfo_ReadDataSource_Response.Merge(m, src) -} -func (m *ReadDataSource_Response) XXX_Size() int { - return xxx_messageInfo_ReadDataSource_Response.Size(m) -} -func (m *ReadDataSource_Response) XXX_DiscardUnknown() { - xxx_messageInfo_ReadDataSource_Response.DiscardUnknown(m) -} - -var xxx_messageInfo_ReadDataSource_Response proto.InternalMessageInfo - -func (m *ReadDataSource_Response) GetState() *DynamicValue { - if m != nil { - return m.State - } - return nil -} - -func (m *ReadDataSource_Response) GetDiagnostics() []*Diagnostic { - if m != nil { - return m.Diagnostics - } - return nil -} - -type GetProvisionerSchema struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetProvisionerSchema) Reset() { *m = GetProvisionerSchema{} } -func (m *GetProvisionerSchema) String() string { return proto.CompactTextString(m) } -func (*GetProvisionerSchema) ProtoMessage() {} -func (*GetProvisionerSchema) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{17} -} - -func (m *GetProvisionerSchema) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetProvisionerSchema.Unmarshal(m, b) -} -func (m *GetProvisionerSchema) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetProvisionerSchema.Marshal(b, m, deterministic) -} -func (m *GetProvisionerSchema) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetProvisionerSchema.Merge(m, src) -} -func (m *GetProvisionerSchema) XXX_Size() int { - return xxx_messageInfo_GetProvisionerSchema.Size(m) -} -func (m *GetProvisionerSchema) XXX_DiscardUnknown() { - xxx_messageInfo_GetProvisionerSchema.DiscardUnknown(m) -} - -var xxx_messageInfo_GetProvisionerSchema proto.InternalMessageInfo - -type GetProvisionerSchema_Request struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetProvisionerSchema_Request) Reset() { *m = GetProvisionerSchema_Request{} } -func (m *GetProvisionerSchema_Request) String() string { return proto.CompactTextString(m) } -func (*GetProvisionerSchema_Request) ProtoMessage() {} -func (*GetProvisionerSchema_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{17, 0} -} - -func (m *GetProvisionerSchema_Request) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetProvisionerSchema_Request.Unmarshal(m, b) -} -func (m *GetProvisionerSchema_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetProvisionerSchema_Request.Marshal(b, m, deterministic) -} -func (m *GetProvisionerSchema_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetProvisionerSchema_Request.Merge(m, src) -} -func (m *GetProvisionerSchema_Request) XXX_Size() int { - return xxx_messageInfo_GetProvisionerSchema_Request.Size(m) -} -func (m *GetProvisionerSchema_Request) XXX_DiscardUnknown() { - xxx_messageInfo_GetProvisionerSchema_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_GetProvisionerSchema_Request proto.InternalMessageInfo - -type GetProvisionerSchema_Response struct { - Provisioner *Schema `protobuf:"bytes,1,opt,name=provisioner,proto3" json:"provisioner,omitempty"` - Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetProvisionerSchema_Response) Reset() { *m = GetProvisionerSchema_Response{} } -func (m *GetProvisionerSchema_Response) String() string { return proto.CompactTextString(m) } -func (*GetProvisionerSchema_Response) ProtoMessage() {} -func (*GetProvisionerSchema_Response) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{17, 1} -} - -func (m *GetProvisionerSchema_Response) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetProvisionerSchema_Response.Unmarshal(m, b) -} -func (m *GetProvisionerSchema_Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetProvisionerSchema_Response.Marshal(b, m, deterministic) -} -func (m *GetProvisionerSchema_Response) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetProvisionerSchema_Response.Merge(m, src) -} -func (m *GetProvisionerSchema_Response) XXX_Size() int { - return xxx_messageInfo_GetProvisionerSchema_Response.Size(m) -} -func (m *GetProvisionerSchema_Response) XXX_DiscardUnknown() { - xxx_messageInfo_GetProvisionerSchema_Response.DiscardUnknown(m) -} - -var xxx_messageInfo_GetProvisionerSchema_Response proto.InternalMessageInfo - -func (m *GetProvisionerSchema_Response) GetProvisioner() *Schema { - if m != nil { - return m.Provisioner - } - return nil -} - -func (m *GetProvisionerSchema_Response) GetDiagnostics() []*Diagnostic { - if m != nil { - return m.Diagnostics - } - return nil -} - -type ValidateProvisionerConfig struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ValidateProvisionerConfig) Reset() { *m = ValidateProvisionerConfig{} } -func (m *ValidateProvisionerConfig) String() string { return proto.CompactTextString(m) } -func (*ValidateProvisionerConfig) ProtoMessage() {} -func (*ValidateProvisionerConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{18} -} - -func (m *ValidateProvisionerConfig) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ValidateProvisionerConfig.Unmarshal(m, b) -} -func (m *ValidateProvisionerConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ValidateProvisionerConfig.Marshal(b, m, deterministic) -} -func (m *ValidateProvisionerConfig) XXX_Merge(src proto.Message) { - xxx_messageInfo_ValidateProvisionerConfig.Merge(m, src) -} -func (m *ValidateProvisionerConfig) XXX_Size() int { - return xxx_messageInfo_ValidateProvisionerConfig.Size(m) -} -func (m *ValidateProvisionerConfig) XXX_DiscardUnknown() { - xxx_messageInfo_ValidateProvisionerConfig.DiscardUnknown(m) -} - -var xxx_messageInfo_ValidateProvisionerConfig proto.InternalMessageInfo - -type ValidateProvisionerConfig_Request struct { - Config *DynamicValue `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ValidateProvisionerConfig_Request) Reset() { *m = ValidateProvisionerConfig_Request{} } -func (m *ValidateProvisionerConfig_Request) String() string { return proto.CompactTextString(m) } -func (*ValidateProvisionerConfig_Request) ProtoMessage() {} -func (*ValidateProvisionerConfig_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{18, 0} -} - -func (m *ValidateProvisionerConfig_Request) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ValidateProvisionerConfig_Request.Unmarshal(m, b) -} -func (m *ValidateProvisionerConfig_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ValidateProvisionerConfig_Request.Marshal(b, m, deterministic) -} -func (m *ValidateProvisionerConfig_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_ValidateProvisionerConfig_Request.Merge(m, src) -} -func (m *ValidateProvisionerConfig_Request) XXX_Size() int { - return xxx_messageInfo_ValidateProvisionerConfig_Request.Size(m) -} -func (m *ValidateProvisionerConfig_Request) XXX_DiscardUnknown() { - xxx_messageInfo_ValidateProvisionerConfig_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_ValidateProvisionerConfig_Request proto.InternalMessageInfo - -func (m *ValidateProvisionerConfig_Request) GetConfig() *DynamicValue { - if m != nil { - return m.Config - } - return nil -} - -type ValidateProvisionerConfig_Response struct { - Diagnostics []*Diagnostic `protobuf:"bytes,1,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ValidateProvisionerConfig_Response) Reset() { *m = ValidateProvisionerConfig_Response{} } -func (m *ValidateProvisionerConfig_Response) String() string { return proto.CompactTextString(m) } -func (*ValidateProvisionerConfig_Response) ProtoMessage() {} -func (*ValidateProvisionerConfig_Response) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{18, 1} -} - -func (m *ValidateProvisionerConfig_Response) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ValidateProvisionerConfig_Response.Unmarshal(m, b) -} -func (m *ValidateProvisionerConfig_Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ValidateProvisionerConfig_Response.Marshal(b, m, deterministic) -} -func (m *ValidateProvisionerConfig_Response) XXX_Merge(src proto.Message) { - xxx_messageInfo_ValidateProvisionerConfig_Response.Merge(m, src) -} -func (m *ValidateProvisionerConfig_Response) XXX_Size() int { - return xxx_messageInfo_ValidateProvisionerConfig_Response.Size(m) -} -func (m *ValidateProvisionerConfig_Response) XXX_DiscardUnknown() { - xxx_messageInfo_ValidateProvisionerConfig_Response.DiscardUnknown(m) -} - -var xxx_messageInfo_ValidateProvisionerConfig_Response proto.InternalMessageInfo - -func (m *ValidateProvisionerConfig_Response) GetDiagnostics() []*Diagnostic { - if m != nil { - return m.Diagnostics - } - return nil -} - -type ProvisionResource struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ProvisionResource) Reset() { *m = ProvisionResource{} } -func (m *ProvisionResource) String() string { return proto.CompactTextString(m) } -func (*ProvisionResource) ProtoMessage() {} -func (*ProvisionResource) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{19} -} - -func (m *ProvisionResource) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ProvisionResource.Unmarshal(m, b) -} -func (m *ProvisionResource) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ProvisionResource.Marshal(b, m, deterministic) -} -func (m *ProvisionResource) XXX_Merge(src proto.Message) { - xxx_messageInfo_ProvisionResource.Merge(m, src) -} -func (m *ProvisionResource) XXX_Size() int { - return xxx_messageInfo_ProvisionResource.Size(m) -} -func (m *ProvisionResource) XXX_DiscardUnknown() { - xxx_messageInfo_ProvisionResource.DiscardUnknown(m) -} - -var xxx_messageInfo_ProvisionResource proto.InternalMessageInfo - -type ProvisionResource_Request struct { - Config *DynamicValue `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` - Connection *DynamicValue `protobuf:"bytes,2,opt,name=connection,proto3" json:"connection,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ProvisionResource_Request) Reset() { *m = ProvisionResource_Request{} } -func (m *ProvisionResource_Request) String() string { return proto.CompactTextString(m) } -func (*ProvisionResource_Request) ProtoMessage() {} -func (*ProvisionResource_Request) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{19, 0} -} - -func (m *ProvisionResource_Request) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ProvisionResource_Request.Unmarshal(m, b) -} -func (m *ProvisionResource_Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ProvisionResource_Request.Marshal(b, m, deterministic) -} -func (m *ProvisionResource_Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_ProvisionResource_Request.Merge(m, src) -} -func (m *ProvisionResource_Request) XXX_Size() int { - return xxx_messageInfo_ProvisionResource_Request.Size(m) -} -func (m *ProvisionResource_Request) XXX_DiscardUnknown() { - xxx_messageInfo_ProvisionResource_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_ProvisionResource_Request proto.InternalMessageInfo - -func (m *ProvisionResource_Request) GetConfig() *DynamicValue { - if m != nil { - return m.Config - } - return nil -} - -func (m *ProvisionResource_Request) GetConnection() *DynamicValue { - if m != nil { - return m.Connection - } - return nil -} - -type ProvisionResource_Response struct { - Output string `protobuf:"bytes,1,opt,name=output,proto3" json:"output,omitempty"` - Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ProvisionResource_Response) Reset() { *m = ProvisionResource_Response{} } -func (m *ProvisionResource_Response) String() string { return proto.CompactTextString(m) } -func (*ProvisionResource_Response) ProtoMessage() {} -func (*ProvisionResource_Response) Descriptor() ([]byte, []int) { - return fileDescriptor_17ae6090ff270234, []int{19, 1} -} - -func (m *ProvisionResource_Response) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ProvisionResource_Response.Unmarshal(m, b) -} -func (m *ProvisionResource_Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ProvisionResource_Response.Marshal(b, m, deterministic) -} -func (m *ProvisionResource_Response) XXX_Merge(src proto.Message) { - xxx_messageInfo_ProvisionResource_Response.Merge(m, src) -} -func (m *ProvisionResource_Response) XXX_Size() int { - return xxx_messageInfo_ProvisionResource_Response.Size(m) -} -func (m *ProvisionResource_Response) XXX_DiscardUnknown() { - xxx_messageInfo_ProvisionResource_Response.DiscardUnknown(m) -} - -var xxx_messageInfo_ProvisionResource_Response proto.InternalMessageInfo - -func (m *ProvisionResource_Response) GetOutput() string { - if m != nil { - return m.Output - } - return "" -} - -func (m *ProvisionResource_Response) GetDiagnostics() []*Diagnostic { - if m != nil { - return m.Diagnostics - } - return nil -} - -func init() { - proto.RegisterEnum("tfplugin5.Diagnostic_Severity", Diagnostic_Severity_name, Diagnostic_Severity_value) - proto.RegisterEnum("tfplugin5.Schema_NestedBlock_NestingMode", Schema_NestedBlock_NestingMode_name, Schema_NestedBlock_NestingMode_value) - proto.RegisterType((*DynamicValue)(nil), "tfplugin5.DynamicValue") - proto.RegisterType((*Diagnostic)(nil), "tfplugin5.Diagnostic") - proto.RegisterType((*AttributePath)(nil), "tfplugin5.AttributePath") - proto.RegisterType((*AttributePath_Step)(nil), "tfplugin5.AttributePath.Step") - proto.RegisterType((*Stop)(nil), "tfplugin5.Stop") - proto.RegisterType((*Stop_Request)(nil), "tfplugin5.Stop.Request") - proto.RegisterType((*Stop_Response)(nil), "tfplugin5.Stop.Response") - proto.RegisterType((*RawState)(nil), "tfplugin5.RawState") - proto.RegisterMapType((map[string]string)(nil), "tfplugin5.RawState.FlatmapEntry") - proto.RegisterType((*Schema)(nil), "tfplugin5.Schema") - proto.RegisterType((*Schema_Block)(nil), "tfplugin5.Schema.Block") - proto.RegisterType((*Schema_Attribute)(nil), "tfplugin5.Schema.Attribute") - proto.RegisterType((*Schema_NestedBlock)(nil), "tfplugin5.Schema.NestedBlock") - proto.RegisterType((*GetProviderSchema)(nil), "tfplugin5.GetProviderSchema") - proto.RegisterType((*GetProviderSchema_Request)(nil), "tfplugin5.GetProviderSchema.Request") - proto.RegisterType((*GetProviderSchema_Response)(nil), "tfplugin5.GetProviderSchema.Response") - proto.RegisterMapType((map[string]*Schema)(nil), "tfplugin5.GetProviderSchema.Response.DataSourceSchemasEntry") - proto.RegisterMapType((map[string]*Schema)(nil), "tfplugin5.GetProviderSchema.Response.ResourceSchemasEntry") - proto.RegisterType((*PrepareProviderConfig)(nil), "tfplugin5.PrepareProviderConfig") - proto.RegisterType((*PrepareProviderConfig_Request)(nil), "tfplugin5.PrepareProviderConfig.Request") - proto.RegisterType((*PrepareProviderConfig_Response)(nil), "tfplugin5.PrepareProviderConfig.Response") - proto.RegisterType((*UpgradeResourceState)(nil), "tfplugin5.UpgradeResourceState") - proto.RegisterType((*UpgradeResourceState_Request)(nil), "tfplugin5.UpgradeResourceState.Request") - proto.RegisterType((*UpgradeResourceState_Response)(nil), "tfplugin5.UpgradeResourceState.Response") - proto.RegisterType((*ValidateResourceTypeConfig)(nil), "tfplugin5.ValidateResourceTypeConfig") - proto.RegisterType((*ValidateResourceTypeConfig_Request)(nil), "tfplugin5.ValidateResourceTypeConfig.Request") - proto.RegisterType((*ValidateResourceTypeConfig_Response)(nil), "tfplugin5.ValidateResourceTypeConfig.Response") - proto.RegisterType((*ValidateDataSourceConfig)(nil), "tfplugin5.ValidateDataSourceConfig") - proto.RegisterType((*ValidateDataSourceConfig_Request)(nil), "tfplugin5.ValidateDataSourceConfig.Request") - proto.RegisterType((*ValidateDataSourceConfig_Response)(nil), "tfplugin5.ValidateDataSourceConfig.Response") - proto.RegisterType((*Configure)(nil), "tfplugin5.Configure") - proto.RegisterType((*Configure_Request)(nil), "tfplugin5.Configure.Request") - proto.RegisterType((*Configure_Response)(nil), "tfplugin5.Configure.Response") - proto.RegisterType((*ReadResource)(nil), "tfplugin5.ReadResource") - proto.RegisterType((*ReadResource_Request)(nil), "tfplugin5.ReadResource.Request") - proto.RegisterType((*ReadResource_Response)(nil), "tfplugin5.ReadResource.Response") - proto.RegisterType((*PlanResourceChange)(nil), "tfplugin5.PlanResourceChange") - proto.RegisterType((*PlanResourceChange_Request)(nil), "tfplugin5.PlanResourceChange.Request") - proto.RegisterType((*PlanResourceChange_Response)(nil), "tfplugin5.PlanResourceChange.Response") - proto.RegisterType((*ApplyResourceChange)(nil), "tfplugin5.ApplyResourceChange") - proto.RegisterType((*ApplyResourceChange_Request)(nil), "tfplugin5.ApplyResourceChange.Request") - proto.RegisterType((*ApplyResourceChange_Response)(nil), "tfplugin5.ApplyResourceChange.Response") - proto.RegisterType((*ImportResourceState)(nil), "tfplugin5.ImportResourceState") - proto.RegisterType((*ImportResourceState_Request)(nil), "tfplugin5.ImportResourceState.Request") - proto.RegisterType((*ImportResourceState_ImportedResource)(nil), "tfplugin5.ImportResourceState.ImportedResource") - proto.RegisterType((*ImportResourceState_Response)(nil), "tfplugin5.ImportResourceState.Response") - proto.RegisterType((*ReadDataSource)(nil), "tfplugin5.ReadDataSource") - proto.RegisterType((*ReadDataSource_Request)(nil), "tfplugin5.ReadDataSource.Request") - proto.RegisterType((*ReadDataSource_Response)(nil), "tfplugin5.ReadDataSource.Response") - proto.RegisterType((*GetProvisionerSchema)(nil), "tfplugin5.GetProvisionerSchema") - proto.RegisterType((*GetProvisionerSchema_Request)(nil), "tfplugin5.GetProvisionerSchema.Request") - proto.RegisterType((*GetProvisionerSchema_Response)(nil), "tfplugin5.GetProvisionerSchema.Response") - proto.RegisterType((*ValidateProvisionerConfig)(nil), "tfplugin5.ValidateProvisionerConfig") - proto.RegisterType((*ValidateProvisionerConfig_Request)(nil), "tfplugin5.ValidateProvisionerConfig.Request") - proto.RegisterType((*ValidateProvisionerConfig_Response)(nil), "tfplugin5.ValidateProvisionerConfig.Response") - proto.RegisterType((*ProvisionResource)(nil), "tfplugin5.ProvisionResource") - proto.RegisterType((*ProvisionResource_Request)(nil), "tfplugin5.ProvisionResource.Request") - proto.RegisterType((*ProvisionResource_Response)(nil), "tfplugin5.ProvisionResource.Response") -} - -func init() { proto.RegisterFile("tfplugin5.proto", fileDescriptor_17ae6090ff270234) } - -var fileDescriptor_17ae6090ff270234 = []byte{ - // 1880 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x59, 0xcb, 0x6f, 0x23, 0x49, - 0x19, 0x9f, 0xf6, 0x23, 0xb1, 0x3f, 0xe7, 0xe1, 0xd4, 0xcc, 0x0e, 0xa6, 0x77, 0x17, 0x82, 0x79, - 0x24, 0xab, 0xdd, 0xf1, 0xac, 0x32, 0xb0, 0xbb, 0x84, 0xd1, 0x8a, 0x6c, 0x26, 0x64, 0x22, 0x66, - 0xb2, 0xa1, 0x3c, 0x0f, 0x24, 0xa4, 0xb5, 0x6a, 0xdc, 0x15, 0x4f, 0x33, 0x76, 0x77, 0x6f, 0x75, - 0x39, 0x89, 0x85, 0xc4, 0x05, 0xc1, 0x19, 0x09, 0xf1, 0x90, 0x78, 0x5c, 0x40, 0xe2, 0x1f, 0xe0, - 0x00, 0xdc, 0x38, 0xf1, 0x0f, 0x70, 0x03, 0x4e, 0x08, 0x6e, 0x9c, 0xe1, 0x82, 0x84, 0xea, 0xd5, - 0x5d, 0xb6, 0xdb, 0x4e, 0x4f, 0xb2, 0x23, 0xc4, 0xad, 0xab, 0xbe, 0x5f, 0x7d, 0xdf, 0x57, 0xdf, - 0xab, 0xbe, 0xcf, 0x86, 0x55, 0x7e, 0x1c, 0xf5, 0x87, 0x3d, 0x3f, 0xf8, 0x42, 0x2b, 0x62, 0x21, - 0x0f, 0x51, 0x35, 0xd9, 0x68, 0xde, 0x86, 0xa5, 0x3b, 0xa3, 0x80, 0x0c, 0xfc, 0xee, 0x23, 0xd2, - 0x1f, 0x52, 0xd4, 0x80, 0xc5, 0x41, 0xdc, 0x8b, 0x48, 0xf7, 0x59, 0xc3, 0x59, 0x77, 0x36, 0x97, - 0xb0, 0x59, 0x22, 0x04, 0xa5, 0x6f, 0xc6, 0x61, 0xd0, 0x28, 0xc8, 0x6d, 0xf9, 0xdd, 0xfc, 0x9b, - 0x03, 0x70, 0xc7, 0x27, 0xbd, 0x20, 0x8c, 0xb9, 0xdf, 0x45, 0xdb, 0x50, 0x89, 0xe9, 0x09, 0x65, - 0x3e, 0x1f, 0xc9, 0xd3, 0x2b, 0x5b, 0x9f, 0x68, 0xa5, 0xb2, 0x53, 0x60, 0xab, 0xad, 0x51, 0x38, - 0xc1, 0x0b, 0xc1, 0xf1, 0x70, 0x30, 0x20, 0x6c, 0x24, 0x25, 0x54, 0xb1, 0x59, 0xa2, 0xeb, 0xb0, - 0xe0, 0x51, 0x4e, 0xfc, 0x7e, 0xa3, 0x28, 0x09, 0x7a, 0x85, 0xde, 0x82, 0x2a, 0xe1, 0x9c, 0xf9, - 0x4f, 0x86, 0x9c, 0x36, 0x4a, 0xeb, 0xce, 0x66, 0x6d, 0xab, 0x61, 0x89, 0xdb, 0x31, 0xb4, 0x23, - 0xc2, 0x9f, 0xe2, 0x14, 0xda, 0xbc, 0x09, 0x15, 0x23, 0x1f, 0xd5, 0x60, 0xf1, 0xe0, 0xf0, 0xd1, - 0xce, 0xbd, 0x83, 0x3b, 0xf5, 0x2b, 0xa8, 0x0a, 0xe5, 0x3d, 0x8c, 0xdf, 0xc7, 0x75, 0x47, 0xec, - 0x3f, 0xde, 0xc1, 0x87, 0x07, 0x87, 0xfb, 0xf5, 0x42, 0xf3, 0x2f, 0x0e, 0x2c, 0x8f, 0x71, 0x43, - 0xb7, 0xa0, 0x1c, 0x73, 0x1a, 0xc5, 0x0d, 0x67, 0xbd, 0xb8, 0x59, 0xdb, 0x7a, 0x75, 0x96, 0xd8, - 0x56, 0x9b, 0xd3, 0x08, 0x2b, 0xac, 0xfb, 0x43, 0x07, 0x4a, 0x62, 0x8d, 0x36, 0x60, 0x25, 0xd1, - 0xa6, 0x13, 0x90, 0x01, 0x95, 0xc6, 0xaa, 0xde, 0xbd, 0x82, 0x97, 0x93, 0xfd, 0x43, 0x32, 0xa0, - 0xa8, 0x05, 0x88, 0xf6, 0xe9, 0x80, 0x06, 0xbc, 0xf3, 0x8c, 0x8e, 0x3a, 0x31, 0x67, 0x7e, 0xd0, - 0x53, 0xe6, 0xb9, 0x7b, 0x05, 0xd7, 0x35, 0xed, 0xab, 0x74, 0xd4, 0x96, 0x14, 0xb4, 0x09, 0xab, - 0x36, 0xde, 0x0f, 0xb8, 0x34, 0x59, 0x51, 0x70, 0x4e, 0xc1, 0x07, 0x01, 0x7f, 0x0f, 0x84, 0xa7, - 0xfa, 0xb4, 0xcb, 0x43, 0xd6, 0xbc, 0x25, 0xd4, 0x0a, 0x23, 0xb7, 0x0a, 0x8b, 0x98, 0x7e, 0x38, - 0xa4, 0x31, 0x77, 0xd7, 0xa1, 0x82, 0x69, 0x1c, 0x85, 0x41, 0x4c, 0xd1, 0x35, 0x28, 0xef, 0x31, - 0x16, 0x32, 0xa5, 0x24, 0x56, 0x8b, 0xe6, 0x8f, 0x1c, 0xa8, 0x60, 0x72, 0xda, 0xe6, 0x84, 0xd3, - 0x24, 0x34, 0x9c, 0x34, 0x34, 0xd0, 0x36, 0x2c, 0x1e, 0xf7, 0x09, 0x1f, 0x90, 0xa8, 0x51, 0x90, - 0x46, 0x5a, 0xb7, 0x8c, 0x64, 0x4e, 0xb6, 0xbe, 0xa2, 0x20, 0x7b, 0x01, 0x67, 0x23, 0x6c, 0x0e, - 0xb8, 0xdb, 0xb0, 0x64, 0x13, 0x50, 0x1d, 0x8a, 0xcf, 0xe8, 0x48, 0x2b, 0x20, 0x3e, 0x85, 0x52, - 0x27, 0x22, 0x5e, 0x75, 0xac, 0xa8, 0xc5, 0x76, 0xe1, 0x1d, 0xa7, 0xf9, 0x8f, 0x32, 0x2c, 0xb4, - 0xbb, 0x4f, 0xe9, 0x80, 0x88, 0x90, 0x3a, 0xa1, 0x2c, 0xf6, 0xb5, 0x66, 0x45, 0x6c, 0x96, 0xe8, - 0x06, 0x94, 0x9f, 0xf4, 0xc3, 0xee, 0x33, 0x79, 0xbc, 0xb6, 0xf5, 0x31, 0x4b, 0x35, 0x75, 0xb6, - 0xf5, 0x9e, 0x20, 0x63, 0x85, 0x72, 0x7f, 0xe1, 0x40, 0x59, 0x6e, 0xcc, 0x61, 0xf9, 0x25, 0x80, - 0xc4, 0x79, 0xb1, 0xbe, 0xf2, 0xcb, 0xd3, 0x7c, 0x93, 0xf0, 0xc0, 0x16, 0x1c, 0xbd, 0x0b, 0x35, - 0x29, 0xa9, 0xc3, 0x47, 0x11, 0x8d, 0x1b, 0xc5, 0xa9, 0xa8, 0xd2, 0xa7, 0x0f, 0x69, 0xcc, 0xa9, - 0xa7, 0x74, 0x03, 0x79, 0xe2, 0x81, 0x38, 0xe0, 0xfe, 0xd1, 0x81, 0x6a, 0xc2, 0x59, 0xb8, 0x23, - 0x8d, 0x2a, 0x2c, 0xbf, 0xc5, 0x9e, 0xe0, 0x6d, 0xb2, 0x57, 0x7c, 0xa3, 0x75, 0xa8, 0x79, 0x34, - 0xee, 0x32, 0x3f, 0xe2, 0xe2, 0x42, 0x2a, 0xbb, 0xec, 0x2d, 0xe4, 0x42, 0x85, 0xd1, 0x0f, 0x87, - 0x3e, 0xa3, 0x9e, 0xcc, 0xb0, 0x0a, 0x4e, 0xd6, 0x82, 0x16, 0x4a, 0x14, 0xe9, 0x37, 0xca, 0x8a, - 0x66, 0xd6, 0x82, 0xd6, 0x0d, 0x07, 0xd1, 0x90, 0x53, 0xaf, 0xb1, 0xa0, 0x68, 0x66, 0x8d, 0x5e, - 0x81, 0x6a, 0x4c, 0x83, 0xd8, 0xe7, 0xfe, 0x09, 0x6d, 0x2c, 0x4a, 0x62, 0xba, 0xe1, 0xfe, 0xba, - 0x00, 0x35, 0xeb, 0x96, 0xe8, 0x65, 0xa8, 0x0a, 0x5d, 0xad, 0x34, 0xc1, 0x15, 0xb1, 0x21, 0xf3, - 0xe3, 0xf9, 0xdc, 0x88, 0x76, 0x61, 0x31, 0xa0, 0x31, 0x17, 0x39, 0x54, 0x94, 0xd5, 0xe9, 0xb5, - 0xb9, 0x16, 0x96, 0xdf, 0x7e, 0xd0, 0xbb, 0x1f, 0x7a, 0x14, 0x9b, 0x93, 0x42, 0xa1, 0x81, 0x1f, - 0x74, 0x7c, 0x4e, 0x07, 0xb1, 0xb4, 0x49, 0x11, 0x57, 0x06, 0x7e, 0x70, 0x20, 0xd6, 0x92, 0x48, - 0xce, 0x34, 0xb1, 0xac, 0x89, 0xe4, 0x4c, 0x12, 0x9b, 0xf7, 0xd5, 0xcd, 0x34, 0xc7, 0xf1, 0xd2, - 0x03, 0xb0, 0xd0, 0x3e, 0x38, 0xdc, 0xbf, 0xb7, 0x57, 0x77, 0x50, 0x05, 0x4a, 0xf7, 0x0e, 0xda, - 0x0f, 0xea, 0x05, 0xb4, 0x08, 0xc5, 0xf6, 0xde, 0x83, 0x7a, 0x51, 0x7c, 0xdc, 0xdf, 0x39, 0xaa, - 0x97, 0x44, 0x89, 0xda, 0xc7, 0xef, 0x3f, 0x3c, 0xaa, 0x97, 0x9b, 0x3f, 0x29, 0xc1, 0xda, 0x3e, - 0xe5, 0x47, 0x2c, 0x3c, 0xf1, 0x3d, 0xca, 0x94, 0xfe, 0x76, 0x12, 0xff, 0xab, 0x68, 0x65, 0xf1, - 0x0d, 0xa8, 0x44, 0x1a, 0x29, 0xcd, 0x58, 0xdb, 0x5a, 0x9b, 0xba, 0x3c, 0x4e, 0x20, 0x88, 0x42, - 0x9d, 0xd1, 0x38, 0x1c, 0xb2, 0x2e, 0xed, 0xc4, 0x92, 0x68, 0x62, 0x7a, 0xdb, 0x3a, 0x36, 0x25, - 0xbe, 0x65, 0xe4, 0x89, 0x0f, 0x79, 0x5a, 0xed, 0xc7, 0x2a, 0xc1, 0x57, 0xd9, 0xf8, 0x2e, 0xea, - 0xc3, 0x55, 0x8f, 0x70, 0xd2, 0x99, 0x90, 0xa4, 0xe2, 0xff, 0x76, 0x3e, 0x49, 0x77, 0x08, 0x27, - 0xed, 0x69, 0x59, 0x6b, 0xde, 0xe4, 0x3e, 0x7a, 0x1b, 0x6a, 0x5e, 0xf2, 0x06, 0x09, 0xe7, 0x09, - 0x29, 0x2f, 0x65, 0xbe, 0x50, 0xd8, 0x46, 0xba, 0x0f, 0xe1, 0x5a, 0xd6, 0x7d, 0x32, 0xea, 0xd2, - 0x86, 0x5d, 0x97, 0x32, 0x6d, 0x9c, 0x96, 0x2a, 0xf7, 0x31, 0x5c, 0xcf, 0x56, 0xfe, 0x92, 0x8c, - 0x9b, 0x7f, 0x76, 0xe0, 0xa5, 0x23, 0x46, 0x23, 0xc2, 0xa8, 0xb1, 0xda, 0x6e, 0x18, 0x1c, 0xfb, - 0x3d, 0x77, 0x3b, 0x09, 0x0f, 0x74, 0x13, 0x16, 0xba, 0x72, 0x53, 0xc7, 0x83, 0x9d, 0x3d, 0x76, - 0x4b, 0x80, 0x35, 0xcc, 0xfd, 0xae, 0x63, 0xc5, 0xd3, 0x97, 0x61, 0x35, 0x52, 0x12, 0xbc, 0x4e, - 0x3e, 0x36, 0x2b, 0x06, 0xaf, 0x54, 0x99, 0xf4, 0x46, 0x21, 0xaf, 0x37, 0x9a, 0xdf, 0x2f, 0xc0, - 0xb5, 0x87, 0x51, 0x8f, 0x11, 0x8f, 0x26, 0x5e, 0x11, 0x8f, 0x89, 0xcb, 0xd2, 0xcb, 0xcd, 0x2d, - 0x1b, 0x56, 0x11, 0x2f, 0x8c, 0x17, 0xf1, 0x37, 0xa1, 0xca, 0xc8, 0x69, 0x27, 0x16, 0xec, 0x64, - 0x8d, 0xa8, 0x6d, 0x5d, 0xcd, 0x78, 0xb6, 0x70, 0x85, 0xe9, 0x2f, 0xf7, 0x3b, 0xb6, 0x51, 0xde, - 0x85, 0x95, 0xa1, 0x52, 0xcc, 0xd3, 0x3c, 0xce, 0xb1, 0xc9, 0xb2, 0x81, 0xab, 0x77, 0xf4, 0xc2, - 0x26, 0xf9, 0xbd, 0x03, 0xee, 0x23, 0xd2, 0xf7, 0x3d, 0xa1, 0x9c, 0xb6, 0x89, 0x78, 0x19, 0xb4, - 0xd7, 0x1f, 0xe7, 0x34, 0x4c, 0x1a, 0x12, 0x85, 0x7c, 0x21, 0xb1, 0x6b, 0x5d, 0x7e, 0x42, 0x79, - 0x27, 0xb7, 0xf2, 0xbf, 0x75, 0xa0, 0x61, 0x94, 0x4f, 0xf3, 0xe1, 0xff, 0x42, 0xf5, 0xdf, 0x39, - 0x50, 0x55, 0x8a, 0x0e, 0x19, 0x75, 0x7b, 0xa9, 0xae, 0xaf, 0xc3, 0x1a, 0xa7, 0x8c, 0x91, 0xe3, - 0x90, 0x0d, 0x3a, 0x76, 0xc7, 0x50, 0xc5, 0xf5, 0x84, 0xf0, 0x48, 0x47, 0xdd, 0xff, 0x46, 0xf7, - 0x5f, 0x15, 0x60, 0x09, 0x53, 0xe2, 0x99, 0x78, 0x71, 0xbf, 0x9d, 0xd3, 0xd4, 0xb7, 0x61, 0xb9, - 0x3b, 0x64, 0x4c, 0x74, 0x99, 0x2a, 0xc8, 0xcf, 0xd1, 0x7a, 0x49, 0xa3, 0x55, 0x8c, 0x37, 0x60, - 0x31, 0x62, 0xfe, 0x89, 0x49, 0xb0, 0x25, 0x6c, 0x96, 0xee, 0x0f, 0xec, 0x54, 0xfa, 0x3c, 0x54, - 0x03, 0x7a, 0x9a, 0x2f, 0x8b, 0x2a, 0x01, 0x3d, 0xbd, 0x5c, 0x02, 0xcd, 0xd6, 0xaa, 0xf9, 0x9b, - 0x12, 0xa0, 0xa3, 0x3e, 0x09, 0x8c, 0x99, 0x76, 0x9f, 0x92, 0xa0, 0x47, 0xdd, 0xff, 0x38, 0x39, - 0xad, 0xf5, 0x0e, 0xd4, 0x22, 0xe6, 0x87, 0x2c, 0x9f, 0xad, 0x40, 0x62, 0xd5, 0x65, 0xf6, 0x00, - 0x45, 0x2c, 0x8c, 0xc2, 0x98, 0x7a, 0x9d, 0xd4, 0x16, 0xc5, 0xf9, 0x0c, 0xea, 0xe6, 0xc8, 0xa1, - 0xb1, 0x49, 0x1a, 0x5d, 0xa5, 0x5c, 0xd1, 0x85, 0x3e, 0x0d, 0xcb, 0x4a, 0x63, 0x63, 0x91, 0xb2, - 0xb4, 0xc8, 0x92, 0xdc, 0x3c, 0xd2, 0xce, 0xfa, 0x79, 0xc1, 0x72, 0xd6, 0x6d, 0x58, 0x8e, 0xfa, - 0x24, 0x08, 0xf2, 0x96, 0xbd, 0x25, 0x8d, 0x56, 0x0a, 0xee, 0x8a, 0x5e, 0x43, 0x36, 0x95, 0x71, - 0x87, 0xd1, 0xa8, 0x4f, 0xba, 0x54, 0x7b, 0x6e, 0xf6, 0x38, 0xb7, 0x6a, 0x4e, 0x60, 0x75, 0x00, - 0x6d, 0xc0, 0xaa, 0x51, 0x61, 0xdc, 0x91, 0x2b, 0x7a, 0x5b, 0x2b, 0x7e, 0xe1, 0x26, 0x00, 0xbd, - 0x01, 0xa8, 0x4f, 0x7b, 0xa4, 0x3b, 0x92, 0x4d, 0x7a, 0x27, 0x1e, 0xc5, 0x9c, 0x0e, 0x74, 0xe7, - 0x5b, 0x57, 0x14, 0x51, 0x72, 0xdb, 0x72, 0xbf, 0xf9, 0xa7, 0x22, 0x5c, 0xdd, 0x89, 0xa2, 0xfe, - 0x68, 0x22, 0x6e, 0xfe, 0xfd, 0xe2, 0xe3, 0x66, 0xca, 0x1b, 0xc5, 0xe7, 0xf1, 0xc6, 0x73, 0x87, - 0x4b, 0x86, 0xe5, 0xcb, 0x59, 0x96, 0x77, 0xff, 0x70, 0xf9, 0xfc, 0xb6, 0xd2, 0xb4, 0x30, 0x96, - 0xa6, 0x93, 0x6e, 0x2d, 0x5e, 0xd2, 0xad, 0xa5, 0x19, 0x6e, 0xfd, 0x67, 0x01, 0xae, 0x1e, 0x0c, - 0xa2, 0x90, 0xf1, 0xf1, 0xd6, 0xe3, 0xad, 0x9c, 0x5e, 0x5d, 0x81, 0x82, 0xef, 0xe9, 0xa1, 0xb5, - 0xe0, 0x7b, 0xee, 0x19, 0xd4, 0x15, 0x3b, 0x9a, 0xd4, 0xe1, 0x73, 0x47, 0x9e, 0x5c, 0x01, 0xa1, - 0x50, 0x73, 0xaa, 0xed, 0x2f, 0x6d, 0x6f, 0x7c, 0x00, 0xc8, 0xd7, 0x6a, 0x74, 0x4c, 0x8f, 0x6e, - 0xde, 0x92, 0x9b, 0x96, 0x88, 0x8c, 0xab, 0xb7, 0x26, 0xf5, 0xc7, 0x6b, 0xfe, 0xc4, 0x4e, 0x7c, - 0xf1, 0xc6, 0xe6, 0xaf, 0x0e, 0xac, 0x88, 0x47, 0x2a, 0xed, 0x0b, 0x5e, 0x5c, 0x47, 0xc0, 0xc6, - 0xc6, 0xa5, 0x72, 0xae, 0xd0, 0xd4, 0x66, 0xbe, 0xf0, 0xfd, 0x7e, 0xea, 0xc0, 0x35, 0x33, 0xdb, - 0x88, 0x5e, 0x20, 0x6b, 0x8e, 0x3b, 0xb3, 0xf4, 0xba, 0x25, 0xaa, 0x42, 0x82, 0x9d, 0x3d, 0xc9, - 0xd9, 0xa8, 0x8b, 0x6b, 0xf7, 0x33, 0x07, 0x3e, 0x6e, 0x3a, 0x33, 0x4b, 0xc5, 0x8f, 0x60, 0x96, - 0xf8, 0x48, 0x3a, 0x98, 0xbf, 0x3b, 0xb0, 0x96, 0xa8, 0x95, 0xb4, 0x31, 0xf1, 0xc5, 0xd5, 0x42, - 0x6f, 0x03, 0x74, 0xc3, 0x20, 0xa0, 0x5d, 0x6e, 0x86, 0x83, 0x79, 0x35, 0x37, 0x85, 0xba, 0xdf, - 0xb0, 0xee, 0x73, 0x1d, 0x16, 0xc2, 0x21, 0x8f, 0x86, 0x5c, 0x87, 0xa4, 0x5e, 0x5d, 0xd8, 0x0d, - 0x5b, 0x3f, 0xae, 0x42, 0xc5, 0xcc, 0x71, 0xe8, 0xeb, 0x50, 0xdd, 0xa7, 0x5c, 0xff, 0xc2, 0xf5, - 0x99, 0x73, 0x46, 0x64, 0x15, 0x40, 0x9f, 0xcd, 0x35, 0x48, 0xa3, 0xfe, 0x8c, 0xa1, 0x11, 0x6d, - 0x5a, 0xe7, 0x33, 0x11, 0x89, 0xa4, 0xd7, 0x72, 0x20, 0xb5, 0xb4, 0x6f, 0xcd, 0x9b, 0x58, 0xd0, - 0x0d, 0x8b, 0xd1, 0x6c, 0x58, 0x22, 0xb7, 0x95, 0x17, 0xae, 0x85, 0x0f, 0x67, 0x4f, 0x1c, 0xe8, - 0xf5, 0x0c, 0x5e, 0x93, 0xa0, 0x44, 0xf0, 0x1b, 0xf9, 0xc0, 0x5a, 0xac, 0x9f, 0x3d, 0xb8, 0xa2, - 0x0d, 0x8b, 0x4b, 0x16, 0x20, 0x11, 0xb7, 0x79, 0x3e, 0x50, 0x8b, 0xba, 0x6b, 0x0d, 0x26, 0xe8, - 0x15, 0xeb, 0x58, 0xb2, 0x9b, 0x30, 0x7d, 0x75, 0x06, 0x55, 0x73, 0xfa, 0xda, 0xf8, 0x98, 0x80, - 0x3e, 0x69, 0x0f, 0xc4, 0x16, 0x21, 0xe1, 0xb7, 0x3e, 0x1b, 0xa0, 0x59, 0x76, 0xb3, 0x5a, 0x6a, - 0x64, 0x87, 0xe9, 0x34, 0x39, 0x61, 0xff, 0xb9, 0xf3, 0x60, 0x5a, 0xc8, 0x71, 0x66, 0x03, 0x86, - 0xec, 0xe3, 0x19, 0xf4, 0x44, 0xcc, 0xc6, 0xb9, 0xb8, 0x54, 0x4e, 0xc6, 0xb3, 0x38, 0x26, 0x27, - 0xeb, 0xd9, 0xcc, 0x92, 0x93, 0x8d, 0xd3, 0x72, 0x1e, 0x4f, 0xbe, 0x84, 0xe8, 0x53, 0x13, 0x86, - 0x4e, 0x49, 0x09, 0xf7, 0xe6, 0x3c, 0x88, 0x66, 0xfc, 0x45, 0xf5, 0xfb, 0x3f, 0x1a, 0xfb, 0xf9, - 0x94, 0x87, 0x51, 0xc2, 0xa4, 0x31, 0x4d, 0x50, 0x47, 0xb7, 0xbe, 0x57, 0x84, 0x9a, 0xf5, 0x30, - 0xa0, 0x0f, 0xec, 0xe2, 0xb4, 0x91, 0x51, 0x76, 0xec, 0x37, 0x2e, 0x33, 0xaa, 0x67, 0x00, 0xb5, - 0xaa, 0x67, 0x73, 0xde, 0x23, 0x94, 0x95, 0x8b, 0x53, 0xa8, 0x44, 0xe8, 0x8d, 0x9c, 0x68, 0x2d, - 0xf9, 0x49, 0xc6, 0x53, 0x33, 0x56, 0x7e, 0xa7, 0xa8, 0x99, 0xe5, 0x37, 0x0b, 0xa5, 0x24, 0xbc, - 0xe9, 0x5c, 0xc2, 0x11, 0x4f, 0x16, 0xe4, 0x1f, 0x7b, 0xb7, 0xfe, 0x1b, 0x00, 0x00, 0xff, 0xff, - 0x8a, 0x61, 0xfa, 0xcc, 0xeb, 0x1b, 0x00, 0x00, -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// ProviderClient is the client API for Provider service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type ProviderClient interface { - //////// Information about what a provider supports/expects - GetSchema(ctx context.Context, in *GetProviderSchema_Request, opts ...grpc.CallOption) (*GetProviderSchema_Response, error) - PrepareProviderConfig(ctx context.Context, in *PrepareProviderConfig_Request, opts ...grpc.CallOption) (*PrepareProviderConfig_Response, error) - ValidateResourceTypeConfig(ctx context.Context, in *ValidateResourceTypeConfig_Request, opts ...grpc.CallOption) (*ValidateResourceTypeConfig_Response, error) - ValidateDataSourceConfig(ctx context.Context, in *ValidateDataSourceConfig_Request, opts ...grpc.CallOption) (*ValidateDataSourceConfig_Response, error) - UpgradeResourceState(ctx context.Context, in *UpgradeResourceState_Request, opts ...grpc.CallOption) (*UpgradeResourceState_Response, error) - //////// One-time initialization, called before other functions below - Configure(ctx context.Context, in *Configure_Request, opts ...grpc.CallOption) (*Configure_Response, error) - //////// Managed Resource Lifecycle - ReadResource(ctx context.Context, in *ReadResource_Request, opts ...grpc.CallOption) (*ReadResource_Response, error) - PlanResourceChange(ctx context.Context, in *PlanResourceChange_Request, opts ...grpc.CallOption) (*PlanResourceChange_Response, error) - ApplyResourceChange(ctx context.Context, in *ApplyResourceChange_Request, opts ...grpc.CallOption) (*ApplyResourceChange_Response, error) - ImportResourceState(ctx context.Context, in *ImportResourceState_Request, opts ...grpc.CallOption) (*ImportResourceState_Response, error) - ReadDataSource(ctx context.Context, in *ReadDataSource_Request, opts ...grpc.CallOption) (*ReadDataSource_Response, error) - //////// Graceful Shutdown - Stop(ctx context.Context, in *Stop_Request, opts ...grpc.CallOption) (*Stop_Response, error) -} - -type providerClient struct { - cc *grpc.ClientConn -} - -func NewProviderClient(cc *grpc.ClientConn) ProviderClient { - return &providerClient{cc} -} - -func (c *providerClient) GetSchema(ctx context.Context, in *GetProviderSchema_Request, opts ...grpc.CallOption) (*GetProviderSchema_Response, error) { - out := new(GetProviderSchema_Response) - err := c.cc.Invoke(ctx, "/tfplugin5.Provider/GetSchema", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *providerClient) PrepareProviderConfig(ctx context.Context, in *PrepareProviderConfig_Request, opts ...grpc.CallOption) (*PrepareProviderConfig_Response, error) { - out := new(PrepareProviderConfig_Response) - err := c.cc.Invoke(ctx, "/tfplugin5.Provider/PrepareProviderConfig", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *providerClient) ValidateResourceTypeConfig(ctx context.Context, in *ValidateResourceTypeConfig_Request, opts ...grpc.CallOption) (*ValidateResourceTypeConfig_Response, error) { - out := new(ValidateResourceTypeConfig_Response) - err := c.cc.Invoke(ctx, "/tfplugin5.Provider/ValidateResourceTypeConfig", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *providerClient) ValidateDataSourceConfig(ctx context.Context, in *ValidateDataSourceConfig_Request, opts ...grpc.CallOption) (*ValidateDataSourceConfig_Response, error) { - out := new(ValidateDataSourceConfig_Response) - err := c.cc.Invoke(ctx, "/tfplugin5.Provider/ValidateDataSourceConfig", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *providerClient) UpgradeResourceState(ctx context.Context, in *UpgradeResourceState_Request, opts ...grpc.CallOption) (*UpgradeResourceState_Response, error) { - out := new(UpgradeResourceState_Response) - err := c.cc.Invoke(ctx, "/tfplugin5.Provider/UpgradeResourceState", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *providerClient) Configure(ctx context.Context, in *Configure_Request, opts ...grpc.CallOption) (*Configure_Response, error) { - out := new(Configure_Response) - err := c.cc.Invoke(ctx, "/tfplugin5.Provider/Configure", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *providerClient) ReadResource(ctx context.Context, in *ReadResource_Request, opts ...grpc.CallOption) (*ReadResource_Response, error) { - out := new(ReadResource_Response) - err := c.cc.Invoke(ctx, "/tfplugin5.Provider/ReadResource", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *providerClient) PlanResourceChange(ctx context.Context, in *PlanResourceChange_Request, opts ...grpc.CallOption) (*PlanResourceChange_Response, error) { - out := new(PlanResourceChange_Response) - err := c.cc.Invoke(ctx, "/tfplugin5.Provider/PlanResourceChange", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *providerClient) ApplyResourceChange(ctx context.Context, in *ApplyResourceChange_Request, opts ...grpc.CallOption) (*ApplyResourceChange_Response, error) { - out := new(ApplyResourceChange_Response) - err := c.cc.Invoke(ctx, "/tfplugin5.Provider/ApplyResourceChange", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *providerClient) ImportResourceState(ctx context.Context, in *ImportResourceState_Request, opts ...grpc.CallOption) (*ImportResourceState_Response, error) { - out := new(ImportResourceState_Response) - err := c.cc.Invoke(ctx, "/tfplugin5.Provider/ImportResourceState", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *providerClient) ReadDataSource(ctx context.Context, in *ReadDataSource_Request, opts ...grpc.CallOption) (*ReadDataSource_Response, error) { - out := new(ReadDataSource_Response) - err := c.cc.Invoke(ctx, "/tfplugin5.Provider/ReadDataSource", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *providerClient) Stop(ctx context.Context, in *Stop_Request, opts ...grpc.CallOption) (*Stop_Response, error) { - out := new(Stop_Response) - err := c.cc.Invoke(ctx, "/tfplugin5.Provider/Stop", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// ProviderServer is the server API for Provider service. -type ProviderServer interface { - //////// Information about what a provider supports/expects - GetSchema(context.Context, *GetProviderSchema_Request) (*GetProviderSchema_Response, error) - PrepareProviderConfig(context.Context, *PrepareProviderConfig_Request) (*PrepareProviderConfig_Response, error) - ValidateResourceTypeConfig(context.Context, *ValidateResourceTypeConfig_Request) (*ValidateResourceTypeConfig_Response, error) - ValidateDataSourceConfig(context.Context, *ValidateDataSourceConfig_Request) (*ValidateDataSourceConfig_Response, error) - UpgradeResourceState(context.Context, *UpgradeResourceState_Request) (*UpgradeResourceState_Response, error) - //////// One-time initialization, called before other functions below - Configure(context.Context, *Configure_Request) (*Configure_Response, error) - //////// Managed Resource Lifecycle - ReadResource(context.Context, *ReadResource_Request) (*ReadResource_Response, error) - PlanResourceChange(context.Context, *PlanResourceChange_Request) (*PlanResourceChange_Response, error) - ApplyResourceChange(context.Context, *ApplyResourceChange_Request) (*ApplyResourceChange_Response, error) - ImportResourceState(context.Context, *ImportResourceState_Request) (*ImportResourceState_Response, error) - ReadDataSource(context.Context, *ReadDataSource_Request) (*ReadDataSource_Response, error) - //////// Graceful Shutdown - Stop(context.Context, *Stop_Request) (*Stop_Response, error) -} - -// UnimplementedProviderServer can be embedded to have forward compatible implementations. -type UnimplementedProviderServer struct { -} - -func (*UnimplementedProviderServer) GetSchema(ctx context.Context, req *GetProviderSchema_Request) (*GetProviderSchema_Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetSchema not implemented") -} -func (*UnimplementedProviderServer) PrepareProviderConfig(ctx context.Context, req *PrepareProviderConfig_Request) (*PrepareProviderConfig_Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method PrepareProviderConfig not implemented") -} -func (*UnimplementedProviderServer) ValidateResourceTypeConfig(ctx context.Context, req *ValidateResourceTypeConfig_Request) (*ValidateResourceTypeConfig_Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method ValidateResourceTypeConfig not implemented") -} -func (*UnimplementedProviderServer) ValidateDataSourceConfig(ctx context.Context, req *ValidateDataSourceConfig_Request) (*ValidateDataSourceConfig_Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method ValidateDataSourceConfig not implemented") -} -func (*UnimplementedProviderServer) UpgradeResourceState(ctx context.Context, req *UpgradeResourceState_Request) (*UpgradeResourceState_Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method UpgradeResourceState not implemented") -} -func (*UnimplementedProviderServer) Configure(ctx context.Context, req *Configure_Request) (*Configure_Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method Configure not implemented") -} -func (*UnimplementedProviderServer) ReadResource(ctx context.Context, req *ReadResource_Request) (*ReadResource_Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method ReadResource not implemented") -} -func (*UnimplementedProviderServer) PlanResourceChange(ctx context.Context, req *PlanResourceChange_Request) (*PlanResourceChange_Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method PlanResourceChange not implemented") -} -func (*UnimplementedProviderServer) ApplyResourceChange(ctx context.Context, req *ApplyResourceChange_Request) (*ApplyResourceChange_Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method ApplyResourceChange not implemented") -} -func (*UnimplementedProviderServer) ImportResourceState(ctx context.Context, req *ImportResourceState_Request) (*ImportResourceState_Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method ImportResourceState not implemented") -} -func (*UnimplementedProviderServer) ReadDataSource(ctx context.Context, req *ReadDataSource_Request) (*ReadDataSource_Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method ReadDataSource not implemented") -} -func (*UnimplementedProviderServer) Stop(ctx context.Context, req *Stop_Request) (*Stop_Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method Stop not implemented") -} - -func RegisterProviderServer(s *grpc.Server, srv ProviderServer) { - s.RegisterService(&_Provider_serviceDesc, srv) -} - -func _Provider_GetSchema_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetProviderSchema_Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ProviderServer).GetSchema(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/tfplugin5.Provider/GetSchema", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProviderServer).GetSchema(ctx, req.(*GetProviderSchema_Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _Provider_PrepareProviderConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(PrepareProviderConfig_Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ProviderServer).PrepareProviderConfig(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/tfplugin5.Provider/PrepareProviderConfig", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProviderServer).PrepareProviderConfig(ctx, req.(*PrepareProviderConfig_Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _Provider_ValidateResourceTypeConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ValidateResourceTypeConfig_Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ProviderServer).ValidateResourceTypeConfig(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/tfplugin5.Provider/ValidateResourceTypeConfig", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProviderServer).ValidateResourceTypeConfig(ctx, req.(*ValidateResourceTypeConfig_Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _Provider_ValidateDataSourceConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ValidateDataSourceConfig_Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ProviderServer).ValidateDataSourceConfig(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/tfplugin5.Provider/ValidateDataSourceConfig", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProviderServer).ValidateDataSourceConfig(ctx, req.(*ValidateDataSourceConfig_Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _Provider_UpgradeResourceState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpgradeResourceState_Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ProviderServer).UpgradeResourceState(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/tfplugin5.Provider/UpgradeResourceState", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProviderServer).UpgradeResourceState(ctx, req.(*UpgradeResourceState_Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _Provider_Configure_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Configure_Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ProviderServer).Configure(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/tfplugin5.Provider/Configure", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProviderServer).Configure(ctx, req.(*Configure_Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _Provider_ReadResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ReadResource_Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ProviderServer).ReadResource(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/tfplugin5.Provider/ReadResource", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProviderServer).ReadResource(ctx, req.(*ReadResource_Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _Provider_PlanResourceChange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(PlanResourceChange_Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ProviderServer).PlanResourceChange(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/tfplugin5.Provider/PlanResourceChange", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProviderServer).PlanResourceChange(ctx, req.(*PlanResourceChange_Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _Provider_ApplyResourceChange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ApplyResourceChange_Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ProviderServer).ApplyResourceChange(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/tfplugin5.Provider/ApplyResourceChange", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProviderServer).ApplyResourceChange(ctx, req.(*ApplyResourceChange_Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _Provider_ImportResourceState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ImportResourceState_Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ProviderServer).ImportResourceState(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/tfplugin5.Provider/ImportResourceState", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProviderServer).ImportResourceState(ctx, req.(*ImportResourceState_Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _Provider_ReadDataSource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ReadDataSource_Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ProviderServer).ReadDataSource(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/tfplugin5.Provider/ReadDataSource", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProviderServer).ReadDataSource(ctx, req.(*ReadDataSource_Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _Provider_Stop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Stop_Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ProviderServer).Stop(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/tfplugin5.Provider/Stop", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProviderServer).Stop(ctx, req.(*Stop_Request)) - } - return interceptor(ctx, in, info, handler) -} - -var _Provider_serviceDesc = grpc.ServiceDesc{ - ServiceName: "tfplugin5.Provider", - HandlerType: (*ProviderServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "GetSchema", - Handler: _Provider_GetSchema_Handler, - }, - { - MethodName: "PrepareProviderConfig", - Handler: _Provider_PrepareProviderConfig_Handler, - }, - { - MethodName: "ValidateResourceTypeConfig", - Handler: _Provider_ValidateResourceTypeConfig_Handler, - }, - { - MethodName: "ValidateDataSourceConfig", - Handler: _Provider_ValidateDataSourceConfig_Handler, - }, - { - MethodName: "UpgradeResourceState", - Handler: _Provider_UpgradeResourceState_Handler, - }, - { - MethodName: "Configure", - Handler: _Provider_Configure_Handler, - }, - { - MethodName: "ReadResource", - Handler: _Provider_ReadResource_Handler, - }, - { - MethodName: "PlanResourceChange", - Handler: _Provider_PlanResourceChange_Handler, - }, - { - MethodName: "ApplyResourceChange", - Handler: _Provider_ApplyResourceChange_Handler, - }, - { - MethodName: "ImportResourceState", - Handler: _Provider_ImportResourceState_Handler, - }, - { - MethodName: "ReadDataSource", - Handler: _Provider_ReadDataSource_Handler, - }, - { - MethodName: "Stop", - Handler: _Provider_Stop_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "tfplugin5.proto", -} - -// ProvisionerClient is the client API for Provisioner service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type ProvisionerClient interface { - GetSchema(ctx context.Context, in *GetProvisionerSchema_Request, opts ...grpc.CallOption) (*GetProvisionerSchema_Response, error) - ValidateProvisionerConfig(ctx context.Context, in *ValidateProvisionerConfig_Request, opts ...grpc.CallOption) (*ValidateProvisionerConfig_Response, error) - ProvisionResource(ctx context.Context, in *ProvisionResource_Request, opts ...grpc.CallOption) (Provisioner_ProvisionResourceClient, error) - Stop(ctx context.Context, in *Stop_Request, opts ...grpc.CallOption) (*Stop_Response, error) -} - -type provisionerClient struct { - cc *grpc.ClientConn -} - -func NewProvisionerClient(cc *grpc.ClientConn) ProvisionerClient { - return &provisionerClient{cc} -} - -func (c *provisionerClient) GetSchema(ctx context.Context, in *GetProvisionerSchema_Request, opts ...grpc.CallOption) (*GetProvisionerSchema_Response, error) { - out := new(GetProvisionerSchema_Response) - err := c.cc.Invoke(ctx, "/tfplugin5.Provisioner/GetSchema", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *provisionerClient) ValidateProvisionerConfig(ctx context.Context, in *ValidateProvisionerConfig_Request, opts ...grpc.CallOption) (*ValidateProvisionerConfig_Response, error) { - out := new(ValidateProvisionerConfig_Response) - err := c.cc.Invoke(ctx, "/tfplugin5.Provisioner/ValidateProvisionerConfig", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *provisionerClient) ProvisionResource(ctx context.Context, in *ProvisionResource_Request, opts ...grpc.CallOption) (Provisioner_ProvisionResourceClient, error) { - stream, err := c.cc.NewStream(ctx, &_Provisioner_serviceDesc.Streams[0], "/tfplugin5.Provisioner/ProvisionResource", opts...) - if err != nil { - return nil, err - } - x := &provisionerProvisionResourceClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -type Provisioner_ProvisionResourceClient interface { - Recv() (*ProvisionResource_Response, error) - grpc.ClientStream -} - -type provisionerProvisionResourceClient struct { - grpc.ClientStream -} - -func (x *provisionerProvisionResourceClient) Recv() (*ProvisionResource_Response, error) { - m := new(ProvisionResource_Response) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func (c *provisionerClient) Stop(ctx context.Context, in *Stop_Request, opts ...grpc.CallOption) (*Stop_Response, error) { - out := new(Stop_Response) - err := c.cc.Invoke(ctx, "/tfplugin5.Provisioner/Stop", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// ProvisionerServer is the server API for Provisioner service. -type ProvisionerServer interface { - GetSchema(context.Context, *GetProvisionerSchema_Request) (*GetProvisionerSchema_Response, error) - ValidateProvisionerConfig(context.Context, *ValidateProvisionerConfig_Request) (*ValidateProvisionerConfig_Response, error) - ProvisionResource(*ProvisionResource_Request, Provisioner_ProvisionResourceServer) error - Stop(context.Context, *Stop_Request) (*Stop_Response, error) -} - -// UnimplementedProvisionerServer can be embedded to have forward compatible implementations. -type UnimplementedProvisionerServer struct { -} - -func (*UnimplementedProvisionerServer) GetSchema(ctx context.Context, req *GetProvisionerSchema_Request) (*GetProvisionerSchema_Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetSchema not implemented") -} -func (*UnimplementedProvisionerServer) ValidateProvisionerConfig(ctx context.Context, req *ValidateProvisionerConfig_Request) (*ValidateProvisionerConfig_Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method ValidateProvisionerConfig not implemented") -} -func (*UnimplementedProvisionerServer) ProvisionResource(req *ProvisionResource_Request, srv Provisioner_ProvisionResourceServer) error { - return status.Errorf(codes.Unimplemented, "method ProvisionResource not implemented") -} -func (*UnimplementedProvisionerServer) Stop(ctx context.Context, req *Stop_Request) (*Stop_Response, error) { - return nil, status.Errorf(codes.Unimplemented, "method Stop not implemented") -} - -func RegisterProvisionerServer(s *grpc.Server, srv ProvisionerServer) { - s.RegisterService(&_Provisioner_serviceDesc, srv) -} - -func _Provisioner_GetSchema_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetProvisionerSchema_Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ProvisionerServer).GetSchema(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/tfplugin5.Provisioner/GetSchema", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProvisionerServer).GetSchema(ctx, req.(*GetProvisionerSchema_Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _Provisioner_ValidateProvisionerConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ValidateProvisionerConfig_Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ProvisionerServer).ValidateProvisionerConfig(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/tfplugin5.Provisioner/ValidateProvisionerConfig", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProvisionerServer).ValidateProvisionerConfig(ctx, req.(*ValidateProvisionerConfig_Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _Provisioner_ProvisionResource_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(ProvisionResource_Request) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(ProvisionerServer).ProvisionResource(m, &provisionerProvisionResourceServer{stream}) -} - -type Provisioner_ProvisionResourceServer interface { - Send(*ProvisionResource_Response) error - grpc.ServerStream -} - -type provisionerProvisionResourceServer struct { - grpc.ServerStream -} - -func (x *provisionerProvisionResourceServer) Send(m *ProvisionResource_Response) error { - return x.ServerStream.SendMsg(m) -} - -func _Provisioner_Stop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Stop_Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ProvisionerServer).Stop(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/tfplugin5.Provisioner/Stop", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ProvisionerServer).Stop(ctx, req.(*Stop_Request)) - } - return interceptor(ctx, in, info, handler) -} - -var _Provisioner_serviceDesc = grpc.ServiceDesc{ - ServiceName: "tfplugin5.Provisioner", - HandlerType: (*ProvisionerServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "GetSchema", - Handler: _Provisioner_GetSchema_Handler, - }, - { - MethodName: "ValidateProvisionerConfig", - Handler: _Provisioner_ValidateProvisionerConfig_Handler, - }, - { - MethodName: "Stop", - Handler: _Provisioner_Stop_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "ProvisionResource", - Handler: _Provisioner_ProvisionResource_Handler, - ServerStreams: true, - }, - }, - Metadata: "tfplugin5.proto", -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5/tfplugin5.proto b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5/tfplugin5.proto deleted file mode 100644 index 9875d9ba..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5/tfplugin5.proto +++ /dev/null @@ -1,353 +0,0 @@ -// Terraform Plugin RPC protocol version 5.1 -// -// This file defines version 5.1 of the RPC protocol. To implement a plugin -// against this protocol, copy this definition into your own codebase and -// use protoc to generate stubs for your target language. -// -// This file will be updated in-place in the source Terraform repository for -// any minor versions of protocol 5, but later minor versions will always be -// backwards compatible. Breaking changes, if any are required, will come -// in a subsequent major version with its own separate proto definition. -// -// Note that only the proto files included in a release tag of Terraform are -// official protocol releases. Proto files taken from other commits may include -// incomplete changes or features that did not make it into a final release. -// In all reasonable cases, plugin developers should take the proto file from -// the tag of the most recent release of Terraform, and not from the master -// branch or any other development branch. -// -syntax = "proto3"; - -package tfplugin5; - -// DynamicValue is an opaque encoding of terraform data, with the field name -// indicating the encoding scheme used. -message DynamicValue { - bytes msgpack = 1; - bytes json = 2; -} - -message Diagnostic { - enum Severity { - INVALID = 0; - ERROR = 1; - WARNING = 2; - } - Severity severity = 1; - string summary = 2; - string detail = 3; - AttributePath attribute = 4; -} - -message AttributePath { - message Step { - oneof selector { - // Set "attribute_name" to represent looking up an attribute - // in the current object value. - string attribute_name = 1; - // Set "element_key_*" to represent looking up an element in - // an indexable collection type. - string element_key_string = 2; - int64 element_key_int = 3; - } - } - repeated Step steps = 1; -} - -message Stop { - message Request { - } - message Response { - string Error = 1; - } -} - -// RawState holds the stored state for a resource to be upgraded by the -// provider. It can be in one of two formats, the current json encoded format -// in bytes, or the legacy flatmap format as a map of strings. -message RawState { - bytes json = 1; - map flatmap = 2; -} - -// Schema is the configuration schema for a Resource, Provider, or Provisioner. -message Schema { - message Block { - int64 version = 1; - repeated Attribute attributes = 2; - repeated NestedBlock block_types = 3; - } - - message Attribute { - string name = 1; - bytes type = 2; - string description = 3; - bool required = 4; - bool optional = 5; - bool computed = 6; - bool sensitive = 7; - } - - message NestedBlock { - enum NestingMode { - INVALID = 0; - SINGLE = 1; - LIST = 2; - SET = 3; - MAP = 4; - GROUP = 5; - } - - string type_name = 1; - Block block = 2; - NestingMode nesting = 3; - int64 min_items = 4; - int64 max_items = 5; - } - - // The version of the schema. - // Schemas are versioned, so that providers can upgrade a saved resource - // state when the schema is changed. - int64 version = 1; - - // Block is the top level configuration block for this schema. - Block block = 2; -} - -service Provider { - //////// Information about what a provider supports/expects - rpc GetSchema(GetProviderSchema.Request) returns (GetProviderSchema.Response); - rpc PrepareProviderConfig(PrepareProviderConfig.Request) returns (PrepareProviderConfig.Response); - rpc ValidateResourceTypeConfig(ValidateResourceTypeConfig.Request) returns (ValidateResourceTypeConfig.Response); - rpc ValidateDataSourceConfig(ValidateDataSourceConfig.Request) returns (ValidateDataSourceConfig.Response); - rpc UpgradeResourceState(UpgradeResourceState.Request) returns (UpgradeResourceState.Response); - - //////// One-time initialization, called before other functions below - rpc Configure(Configure.Request) returns (Configure.Response); - - //////// Managed Resource Lifecycle - rpc ReadResource(ReadResource.Request) returns (ReadResource.Response); - rpc PlanResourceChange(PlanResourceChange.Request) returns (PlanResourceChange.Response); - rpc ApplyResourceChange(ApplyResourceChange.Request) returns (ApplyResourceChange.Response); - rpc ImportResourceState(ImportResourceState.Request) returns (ImportResourceState.Response); - - rpc ReadDataSource(ReadDataSource.Request) returns (ReadDataSource.Response); - - //////// Graceful Shutdown - rpc Stop(Stop.Request) returns (Stop.Response); -} - -message GetProviderSchema { - message Request { - } - message Response { - Schema provider = 1; - map resource_schemas = 2; - map data_source_schemas = 3; - repeated Diagnostic diagnostics = 4; - } -} - -message PrepareProviderConfig { - message Request { - DynamicValue config = 1; - } - message Response { - DynamicValue prepared_config = 1; - repeated Diagnostic diagnostics = 2; - } -} - -message UpgradeResourceState { - message Request { - string type_name = 1; - - // version is the schema_version number recorded in the state file - int64 version = 2; - - // raw_state is the raw states as stored for the resource. Core does - // not have access to the schema of prior_version, so it's the - // provider's responsibility to interpret this value using the - // appropriate older schema. The raw_state will be the json encoded - // state, or a legacy flat-mapped format. - RawState raw_state = 3; - } - message Response { - // new_state is a msgpack-encoded data structure that, when interpreted with - // the _current_ schema for this resource type, is functionally equivalent to - // that which was given in prior_state_raw. - DynamicValue upgraded_state = 1; - - // diagnostics describes any errors encountered during migration that could not - // be safely resolved, and warnings about any possibly-risky assumptions made - // in the upgrade process. - repeated Diagnostic diagnostics = 2; - } -} - -message ValidateResourceTypeConfig { - message Request { - string type_name = 1; - DynamicValue config = 2; - } - message Response { - repeated Diagnostic diagnostics = 1; - } -} - -message ValidateDataSourceConfig { - message Request { - string type_name = 1; - DynamicValue config = 2; - } - message Response { - repeated Diagnostic diagnostics = 1; - } -} - -message Configure { - message Request { - string terraform_version = 1; - DynamicValue config = 2; - } - message Response { - repeated Diagnostic diagnostics = 1; - } -} - -message ReadResource { - message Request { - string type_name = 1; - DynamicValue current_state = 2; - bytes private = 3; - } - message Response { - DynamicValue new_state = 1; - repeated Diagnostic diagnostics = 2; - bytes private = 3; - } -} - -message PlanResourceChange { - message Request { - string type_name = 1; - DynamicValue prior_state = 2; - DynamicValue proposed_new_state = 3; - DynamicValue config = 4; - bytes prior_private = 5; - } - - message Response { - DynamicValue planned_state = 1; - repeated AttributePath requires_replace = 2; - bytes planned_private = 3; - repeated Diagnostic diagnostics = 4; - - - // This may be set only by the helper/schema "SDK" in the main Terraform - // repository, to request that Terraform Core >=0.12 permit additional - // inconsistencies that can result from the legacy SDK type system - // and its imprecise mapping to the >=0.12 type system. - // The change in behavior implied by this flag makes sense only for the - // specific details of the legacy SDK type system, and are not a general - // mechanism to avoid proper type handling in providers. - // - // ==== DO NOT USE THIS ==== - // ==== THIS MUST BE LEFT UNSET IN ALL OTHER SDKS ==== - // ==== DO NOT USE THIS ==== - bool legacy_type_system = 5; - } -} - -message ApplyResourceChange { - message Request { - string type_name = 1; - DynamicValue prior_state = 2; - DynamicValue planned_state = 3; - DynamicValue config = 4; - bytes planned_private = 5; - } - message Response { - DynamicValue new_state = 1; - bytes private = 2; - repeated Diagnostic diagnostics = 3; - - // This may be set only by the helper/schema "SDK" in the main Terraform - // repository, to request that Terraform Core >=0.12 permit additional - // inconsistencies that can result from the legacy SDK type system - // and its imprecise mapping to the >=0.12 type system. - // The change in behavior implied by this flag makes sense only for the - // specific details of the legacy SDK type system, and are not a general - // mechanism to avoid proper type handling in providers. - // - // ==== DO NOT USE THIS ==== - // ==== THIS MUST BE LEFT UNSET IN ALL OTHER SDKS ==== - // ==== DO NOT USE THIS ==== - bool legacy_type_system = 4; - } -} - -message ImportResourceState { - message Request { - string type_name = 1; - string id = 2; - } - - message ImportedResource { - string type_name = 1; - DynamicValue state = 2; - bytes private = 3; - } - - message Response { - repeated ImportedResource imported_resources = 1; - repeated Diagnostic diagnostics = 2; - } -} - -message ReadDataSource { - message Request { - string type_name = 1; - DynamicValue config = 2; - } - message Response { - DynamicValue state = 1; - repeated Diagnostic diagnostics = 2; - } -} - -service Provisioner { - rpc GetSchema(GetProvisionerSchema.Request) returns (GetProvisionerSchema.Response); - rpc ValidateProvisionerConfig(ValidateProvisionerConfig.Request) returns (ValidateProvisionerConfig.Response); - rpc ProvisionResource(ProvisionResource.Request) returns (stream ProvisionResource.Response); - rpc Stop(Stop.Request) returns (Stop.Response); -} - -message GetProvisionerSchema { - message Request { - } - message Response { - Schema provisioner = 1; - repeated Diagnostic diagnostics = 2; - } -} - -message ValidateProvisionerConfig { - message Request { - DynamicValue config = 1; - } - message Response { - repeated Diagnostic diagnostics = 1; - } -} - -message ProvisionResource { - message Request { - DynamicValue config = 1; - DynamicValue connection = 2; - } - message Response { - string output = 1; - repeated Diagnostic diagnostics = 2; - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/vault/sdk/helper/compressutil/compress.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/vault/sdk/helper/compressutil/compress.go new file mode 100644 index 00000000..356d4548 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/vault/sdk/helper/compressutil/compress.go @@ -0,0 +1,207 @@ +package compressutil + +import ( + "bytes" + "compress/gzip" + "compress/lzw" + "fmt" + "io" + + "github.com/golang/snappy" + "github.com/hashicorp/errwrap" + "github.com/pierrec/lz4" +) + +const ( + // A byte value used as a canary prefix for the compressed information + // which is used to distinguish if a JSON input is compressed or not. + // The value of this constant should not be a first character of any + // valid JSON string. + + CompressionTypeGzip = "gzip" + CompressionCanaryGzip byte = 'G' + + CompressionTypeLZW = "lzw" + CompressionCanaryLZW byte = 'L' + + CompressionTypeSnappy = "snappy" + CompressionCanarySnappy byte = 'S' + + CompressionTypeLZ4 = "lz4" + CompressionCanaryLZ4 byte = '4' +) + +// SnappyReadCloser embeds the snappy reader which implements the io.Reader +// interface. The decompress procedure in this utility expects an +// io.ReadCloser. This type implements the io.Closer interface to retain the +// generic way of decompression. +type CompressUtilReadCloser struct { + io.Reader +} + +// Close is a noop method implemented only to satisfy the io.Closer interface +func (c *CompressUtilReadCloser) Close() error { + return nil +} + +// CompressionConfig is used to select a compression type to be performed by +// Compress and Decompress utilities. +// Supported types are: +// * CompressionTypeLZW +// * CompressionTypeGzip +// * CompressionTypeSnappy +// * CompressionTypeLZ4 +// +// When using CompressionTypeGzip, the compression levels can also be chosen: +// * gzip.DefaultCompression +// * gzip.BestSpeed +// * gzip.BestCompression +type CompressionConfig struct { + // Type of the compression algorithm to be used + Type string + + // When using Gzip format, the compression level to employ + GzipCompressionLevel int +} + +// Compress places the canary byte in a buffer and uses the same buffer to fill +// in the compressed information of the given input. The configuration supports +// two type of compression: LZW and Gzip. When using Gzip compression format, +// if GzipCompressionLevel is not specified, the 'gzip.DefaultCompression' will +// be assumed. +func Compress(data []byte, config *CompressionConfig) ([]byte, error) { + var buf bytes.Buffer + var writer io.WriteCloser + var err error + + if config == nil { + return nil, fmt.Errorf("config is nil") + } + + // Write the canary into the buffer and create writer to compress the + // input data based on the configured type + switch config.Type { + case CompressionTypeLZW: + buf.Write([]byte{CompressionCanaryLZW}) + writer = lzw.NewWriter(&buf, lzw.LSB, 8) + + case CompressionTypeGzip: + buf.Write([]byte{CompressionCanaryGzip}) + + switch { + case config.GzipCompressionLevel == gzip.BestCompression, + config.GzipCompressionLevel == gzip.BestSpeed, + config.GzipCompressionLevel == gzip.DefaultCompression: + // These are valid compression levels + default: + // If compression level is set to NoCompression or to + // any invalid value, fallback to Defaultcompression + config.GzipCompressionLevel = gzip.DefaultCompression + } + writer, err = gzip.NewWriterLevel(&buf, config.GzipCompressionLevel) + + case CompressionTypeSnappy: + buf.Write([]byte{CompressionCanarySnappy}) + writer = snappy.NewBufferedWriter(&buf) + + case CompressionTypeLZ4: + buf.Write([]byte{CompressionCanaryLZ4}) + writer = lz4.NewWriter(&buf) + + default: + return nil, fmt.Errorf("unsupported compression type") + } + + if err != nil { + return nil, errwrap.Wrapf("failed to create a compression writer: {{err}}", err) + } + + if writer == nil { + return nil, fmt.Errorf("failed to create a compression writer") + } + + // Compress the input and place it in the same buffer containing the + // canary byte. + if _, err = writer.Write(data); err != nil { + return nil, errwrap.Wrapf("failed to compress input data: err: {{err}}", err) + } + + // Close the io.WriteCloser + if err = writer.Close(); err != nil { + return nil, err + } + + // Return the compressed bytes with canary byte at the start + return buf.Bytes(), nil +} + +// Decompress checks if the first byte in the input matches the canary byte. +// If the first byte is a canary byte, then the input past the canary byte +// will be decompressed using the method specified in the given configuration. +// If the first byte isn't a canary byte, then the utility returns a boolean +// value indicating that the input was not compressed. +func Decompress(data []byte) ([]byte, bool, error) { + var err error + var reader io.ReadCloser + if data == nil || len(data) == 0 { + return nil, false, fmt.Errorf("'data' being decompressed is empty") + } + + canary := data[0] + cData := data[1:] + + switch canary { + // If the first byte matches the canary byte, remove the canary + // byte and try to decompress the data that is after the canary. + case CompressionCanaryGzip: + if len(data) < 2 { + return nil, false, fmt.Errorf("invalid 'data' after the canary") + } + reader, err = gzip.NewReader(bytes.NewReader(cData)) + + case CompressionCanaryLZW: + if len(data) < 2 { + return nil, false, fmt.Errorf("invalid 'data' after the canary") + } + reader = lzw.NewReader(bytes.NewReader(cData), lzw.LSB, 8) + + case CompressionCanarySnappy: + if len(data) < 2 { + return nil, false, fmt.Errorf("invalid 'data' after the canary") + } + reader = &CompressUtilReadCloser{ + Reader: snappy.NewReader(bytes.NewReader(cData)), + } + + case CompressionCanaryLZ4: + if len(data) < 2 { + return nil, false, fmt.Errorf("invalid 'data' after the canary") + } + reader = &CompressUtilReadCloser{ + Reader: lz4.NewReader(bytes.NewReader(cData)), + } + + default: + // If the first byte doesn't match the canary byte, it means + // that the content was not compressed at all. Indicate the + // caller that the input was not compressed. + return nil, true, nil + } + if err != nil { + return nil, false, errwrap.Wrapf("failed to create a compression reader: {{err}}", err) + } + if reader == nil { + return nil, false, fmt.Errorf("failed to create a compression reader") + } + + // Close the io.ReadCloser + defer reader.Close() + + // Read all the compressed data into a buffer + var buf bytes.Buffer + if _, err = io.Copy(&buf, reader); err != nil { + return nil, false, err + } + + return buf.Bytes(), false, nil +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/vault/sdk/helper/compressutil/compress_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/vault/sdk/helper/compressutil/compress_test.go new file mode 100644 index 00000000..6de3181f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/vault/sdk/helper/compressutil/compress_test.go @@ -0,0 +1,99 @@ +package compressutil + +import ( + "bytes" + "compress/gzip" + "testing" +) + +func TestCompressUtil_CompressDecompress(t *testing.T) { + t.Parallel() + + tests := []struct { + compressionType string + compressionConfig CompressionConfig + canary byte + }{ + {"GZIP default implicit", + CompressionConfig{Type: CompressionTypeGzip}, + CompressionCanaryGzip, + }, + {"GZIP default explicit", + CompressionConfig{Type: CompressionTypeGzip, GzipCompressionLevel: gzip.DefaultCompression}, + CompressionCanaryGzip, + }, + {"GZIP best speed", + CompressionConfig{Type: CompressionTypeGzip, GzipCompressionLevel: gzip.BestSpeed}, + CompressionCanaryGzip, + }, + {"GZIP best compression", + CompressionConfig{Type: CompressionTypeGzip, GzipCompressionLevel: gzip.BestCompression}, + CompressionCanaryGzip, + }, + {"Snappy", + CompressionConfig{Type: CompressionTypeSnappy}, + CompressionCanarySnappy, + }, + {"LZ4", + CompressionConfig{Type: CompressionTypeLZ4}, + CompressionCanaryLZ4, + }, + {"LZW", + CompressionConfig{Type: CompressionTypeLZW}, + CompressionCanaryLZW, + }, + } + + inputJSONBytes := []byte(`{"sample":"data","verification":"process"}`) + + for _, test := range tests { + // Compress the input + compressedJSONBytes, err := Compress(inputJSONBytes, &test.compressionConfig) + if err != nil { + t.Fatalf("compress error (%s): %s", test.compressionType, err) + } + if len(compressedJSONBytes) == 0 { + t.Fatalf("failed to compress data in %s format", test.compressionType) + } + + // Check the presence of the canary + if compressedJSONBytes[0] != test.canary { + t.Fatalf("bad (%s): compression canary: expected: %d actual: %d", test.compressionType, test.canary, compressedJSONBytes[0]) + } + + decompressedJSONBytes, wasNotCompressed, err := Decompress(compressedJSONBytes) + if err != nil { + t.Fatalf("decompress error (%s): %s", test.compressionType, err) + } + + // Check if the input for decompress was not compressed in the first place + if wasNotCompressed { + t.Fatalf("bad (%s): expected compressed bytes", test.compressionType) + } + + if len(decompressedJSONBytes) == 0 { + t.Fatalf("bad (%s): expected decompressed bytes", test.compressionType) + } + + // Compare the value after decompression + if !bytes.Equal(inputJSONBytes, decompressedJSONBytes) { + t.Fatalf("bad (%s): decompressed value;\nexpected: %q\nactual: %q", test.compressionType, string(inputJSONBytes), string(decompressedJSONBytes)) + } + } +} + +func TestCompressUtil_InvalidConfigurations(t *testing.T) { + t.Parallel() + + inputJSONBytes := []byte(`{"sample":"data","verification":"process"}`) + + // Test nil configuration + if _, err := Compress(inputJSONBytes, nil); err == nil { + t.Fatal("expected an error") + } + + // Test invalid configuration + if _, err := Compress(inputJSONBytes, &CompressionConfig{}); err == nil { + t.Fatal("expected an error") + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/vault/sdk/helper/jsonutil/json.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/vault/sdk/helper/jsonutil/json.go new file mode 100644 index 00000000..59bebb4d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/vault/sdk/helper/jsonutil/json.go @@ -0,0 +1,101 @@ +package jsonutil + +import ( + "bytes" + "compress/gzip" + "encoding/json" + "fmt" + "io" + + "github.com/hashicorp/errwrap" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/vault/sdk/helper/compressutil" +) + +// Encodes/Marshals the given object into JSON +func EncodeJSON(in interface{}) ([]byte, error) { + if in == nil { + return nil, fmt.Errorf("input for encoding is nil") + } + var buf bytes.Buffer + enc := json.NewEncoder(&buf) + if err := enc.Encode(in); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// EncodeJSONAndCompress encodes the given input into JSON and compresses the +// encoded value (using Gzip format BestCompression level, by default). A +// canary byte is placed at the beginning of the returned bytes for the logic +// in decompression method to identify compressed input. +func EncodeJSONAndCompress(in interface{}, config *compressutil.CompressionConfig) ([]byte, error) { + if in == nil { + return nil, fmt.Errorf("input for encoding is nil") + } + + // First JSON encode the given input + encodedBytes, err := EncodeJSON(in) + if err != nil { + return nil, err + } + + if config == nil { + config = &compressutil.CompressionConfig{ + Type: compressutil.CompressionTypeGzip, + GzipCompressionLevel: gzip.BestCompression, + } + } + + return compressutil.Compress(encodedBytes, config) +} + +// DecodeJSON tries to decompress the given data. The call to decompress, fails +// if the content was not compressed in the first place, which is identified by +// a canary byte before the compressed data. If the data is not compressed, it +// is JSON decoded directly. Otherwise the decompressed data will be JSON +// decoded. +func DecodeJSON(data []byte, out interface{}) error { + if data == nil || len(data) == 0 { + return fmt.Errorf("'data' being decoded is nil") + } + if out == nil { + return fmt.Errorf("output parameter 'out' is nil") + } + + // Decompress the data if it was compressed in the first place + decompressedBytes, uncompressed, err := compressutil.Decompress(data) + if err != nil { + return errwrap.Wrapf("failed to decompress JSON: {{err}}", err) + } + if !uncompressed && (decompressedBytes == nil || len(decompressedBytes) == 0) { + return fmt.Errorf("decompressed data being decoded is invalid") + } + + // If the input supplied failed to contain the compression canary, it + // will be notified by the compression utility. Decode the decompressed + // input. + if !uncompressed { + data = decompressedBytes + } + + return DecodeJSONFromReader(bytes.NewReader(data), out) +} + +// Decodes/Unmarshals the given io.Reader pointing to a JSON, into a desired object +func DecodeJSONFromReader(r io.Reader, out interface{}) error { + if r == nil { + return fmt.Errorf("'io.Reader' being decoded is nil") + } + if out == nil { + return fmt.Errorf("output parameter 'out' is nil") + } + + dec := json.NewDecoder(r) + + // While decoding JSON values, interpret the integer values as `json.Number`s instead of `float64`. + dec.UseNumber() + + // Since 'out' is an interface representing a pointer, pass it to the decoder without an '&' + return dec.Decode(out) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/vault/sdk/helper/jsonutil/json_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/vault/sdk/helper/jsonutil/json_test.go new file mode 100644 index 00000000..3bc6f5ed --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/vault/sdk/helper/jsonutil/json_test.go @@ -0,0 +1,141 @@ +package jsonutil + +import ( + "bytes" + "compress/gzip" + "fmt" + "reflect" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/vault/sdk/helper/compressutil" +) + +func TestJSONUtil_CompressDecompressJSON(t *testing.T) { + expected := map[string]interface{}{ + "test": "data", + "validation": "process", + } + + // Compress an object + compressedBytes, err := EncodeJSONAndCompress(expected, nil) + if err != nil { + t.Fatal(err) + } + if len(compressedBytes) == 0 { + t.Fatal("expected compressed data") + } + + // Check if canary is present in the compressed data + if compressedBytes[0] != compressutil.CompressionCanaryGzip { + t.Fatalf("canary missing in compressed data") + } + + // Decompress and decode the compressed information and verify the functional + // behavior + var actual map[string]interface{} + if err = DecodeJSON(compressedBytes, &actual); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("bad: expected: %#v\nactual: %#v", expected, actual) + } + for key := range actual { + delete(actual, key) + } + + // Test invalid data + if err = DecodeJSON([]byte{}, &actual); err == nil { + t.Fatalf("expected a failure") + } + + // Test invalid data after the canary byte + var buf bytes.Buffer + buf.Write([]byte{compressutil.CompressionCanaryGzip}) + if err = DecodeJSON(buf.Bytes(), &actual); err == nil { + t.Fatalf("expected a failure") + } + + // Compress an object + compressedBytes, err = EncodeJSONAndCompress(expected, &compressutil.CompressionConfig{ + Type: compressutil.CompressionTypeGzip, + GzipCompressionLevel: gzip.BestSpeed, + }) + if err != nil { + t.Fatal(err) + } + if len(compressedBytes) == 0 { + t.Fatal("expected compressed data") + } + + // Check if canary is present in the compressed data + if compressedBytes[0] != compressutil.CompressionCanaryGzip { + t.Fatalf("canary missing in compressed data") + } + + // Decompress and decode the compressed information and verify the functional + // behavior + if err = DecodeJSON(compressedBytes, &actual); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("bad: expected: %#v\nactual: %#v", expected, actual) + } +} + +func TestJSONUtil_EncodeJSON(t *testing.T) { + input := map[string]interface{}{ + "test": "data", + "validation": "process", + } + + actualBytes, err := EncodeJSON(input) + if err != nil { + t.Fatalf("failed to encode JSON: %v", err) + } + + actual := strings.TrimSpace(string(actualBytes)) + expected := `{"test":"data","validation":"process"}` + + if actual != expected { + t.Fatalf("bad: encoded JSON: expected:%s\nactual:%s\n", expected, string(actualBytes)) + } +} + +func TestJSONUtil_DecodeJSON(t *testing.T) { + input := `{"test":"data","validation":"process"}` + + var actual map[string]interface{} + + err := DecodeJSON([]byte(input), &actual) + if err != nil { + fmt.Printf("decoding err: %v\n", err) + } + + expected := map[string]interface{}{ + "test": "data", + "validation": "process", + } + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual) + } +} + +func TestJSONUtil_DecodeJSONFromReader(t *testing.T) { + input := `{"test":"data","validation":"process"}` + + var actual map[string]interface{} + + err := DecodeJSONFromReader(bytes.NewReader([]byte(input)), &actual) + if err != nil { + fmt.Printf("decoding err: %v\n", err) + } + + expected := map[string]interface{}{ + "test": "data", + "validation": "process", + } + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/version/version.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/version/version.go deleted file mode 100644 index 2d56dab6..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/version/version.go +++ /dev/null @@ -1,36 +0,0 @@ -// The version package provides a location to set the release versions for all -// packages to consume, without creating import cycles. -// -// This package should not import any other terraform packages. -package version - -import ( - "fmt" - - version "github.com/hashicorp/go-version" -) - -// The main version number that is being run at the moment. -var Version = "0.12.7" - -// A pre-release marker for the version. If this is "" (empty string) -// then it means that it is a final release. Otherwise, this is a pre-release -// such as "dev" (in development), "beta", "rc1", etc. -var Prerelease = "sdk" - -// SemVer is an instance of version.Version. This has the secondary -// benefit of verifying during tests and init time that our version is a -// proper semantic version, which should always be the case. -var SemVer *version.Version - -func init() { - SemVer = version.Must(version.NewVersion(Version)) -} - -// String returns the complete version string, including prerelease -func String() string { - if Prerelease != "" { - return fmt.Sprintf("%s-%s", Version, Prerelease) - } - return Version -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/meta/meta.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/meta/meta.go index aab60272..05dc625f 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/meta/meta.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/meta/meta.go @@ -11,7 +11,7 @@ import ( ) // The main version number that is being run at the moment. -var SDKVersion = "1.7.0" +var SDKVersion = "2.6.1" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/client.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/client.go deleted file mode 100644 index 5a99e900..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/client.go +++ /dev/null @@ -1,35 +0,0 @@ -package plugin - -import ( - "os" - "os/exec" - - hclog "github.com/hashicorp/go-hclog" - plugin "github.com/hashicorp/go-plugin" - "github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery" -) - -// ClientConfig returns a configuration object that can be used to instantiate -// a client for the plugin described by the given metadata. -func ClientConfig(m discovery.PluginMeta) *plugin.ClientConfig { - logger := hclog.New(&hclog.LoggerOptions{ - Name: "plugin", - Level: hclog.Trace, - Output: os.Stderr, - }) - - return &plugin.ClientConfig{ - Cmd: exec.Command(m.Path), - HandshakeConfig: Handshake, - VersionedPlugins: VersionedPlugins, - Managed: true, - Logger: logger, - AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, - AutoMTLS: true, - } -} - -// Client returns a plugin client for the plugin described by the given metadata. -func Client(m discovery.PluginMeta) *plugin.Client { - return plugin.NewClient(ClientConfig(m)) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/debug.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/debug.go new file mode 100644 index 00000000..fb4f8140 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/debug.go @@ -0,0 +1,116 @@ +package plugin + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "os" + "os/signal" + "runtime" + "strings" + "time" + + "github.com/hashicorp/go-plugin" +) + +// ReattachConfig holds the information Terraform needs to be able to attach +// itself to a provider process, so it can drive the process. +type ReattachConfig struct { + Protocol string + Pid int + Test bool + Addr ReattachConfigAddr +} + +// ReattachConfigAddr is a JSON-encoding friendly version of net.Addr. +type ReattachConfigAddr struct { + Network string + String string +} + +// DebugServe starts a plugin server in debug mode; this should only be used +// when the provider will manage its own lifecycle. It is not recommended for +// normal usage; Serve is the correct function for that. +func DebugServe(ctx context.Context, opts *ServeOpts) (ReattachConfig, <-chan struct{}, error) { + reattachCh := make(chan *plugin.ReattachConfig) + closeCh := make(chan struct{}) + + opts.TestConfig = &plugin.ServeTestConfig{ + Context: ctx, + ReattachConfigCh: reattachCh, + CloseCh: closeCh, + } + + go Serve(opts) + + var config *plugin.ReattachConfig + select { + case config = <-reattachCh: + case <-time.After(2 * time.Second): + return ReattachConfig{}, closeCh, errors.New("timeout waiting on reattach config") + } + + if config == nil { + return ReattachConfig{}, closeCh, errors.New("nil reattach config received") + } + + return ReattachConfig{ + Protocol: string(config.Protocol), + Pid: config.Pid, + Test: config.Test, + Addr: ReattachConfigAddr{ + Network: config.Addr.Network(), + String: config.Addr.String(), + }, + }, closeCh, nil +} + +// Debug starts a debug server and controls its lifecycle, printing the +// information needed for Terraform to connect to the provider to stdout. +// os.Interrupt will be captured and used to stop the server. +func Debug(ctx context.Context, providerAddr string, opts *ServeOpts) error { + ctx, cancel := context.WithCancel(ctx) + // Ctrl-C will stop the server + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, os.Interrupt) + defer func() { + signal.Stop(sigCh) + cancel() + }() + config, closeCh, err := DebugServe(ctx, opts) + if err != nil { + return fmt.Errorf("Error launching debug server: %w", err) + } + go func() { + select { + case <-sigCh: + cancel() + case <-ctx.Done(): + } + }() + reattachBytes, err := json.Marshal(map[string]ReattachConfig{ + providerAddr: config, + }) + if err != nil { + return fmt.Errorf("Error building reattach string: %w", err) + } + + reattachStr := string(reattachBytes) + + fmt.Printf("Provider started, to attach Terraform set the TF_REATTACH_PROVIDERS env var:\n\n") + switch runtime.GOOS { + case "windows": + fmt.Printf("\tCommand Prompt:\tset \"TF_REATTACH_PROVIDERS=%s\"\n", reattachStr) + fmt.Printf("\tPowerShell:\t$env:TF_REATTACH_PROVIDERS='%s'\n", strings.ReplaceAll(reattachStr, `'`, `''`)) + case "linux", "darwin": + fmt.Printf("\tTF_REATTACH_PROVIDERS='%s'\n", strings.ReplaceAll(reattachStr, `'`, `'"'"'`)) + default: + fmt.Println(reattachStr) + } + fmt.Println("") + + // wait for the server to be done + <-closeCh + return nil +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/grpc_provider.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/grpc_provider.go deleted file mode 100644 index e4520975..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/grpc_provider.go +++ /dev/null @@ -1,563 +0,0 @@ -package plugin - -import ( - "context" - "errors" - "log" - "sync" - - "github.com/zclconf/go-cty/cty" - - plugin "github.com/hashicorp/go-plugin" - "github.com/hashicorp/terraform-plugin-sdk/internal/plugin/convert" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - proto "github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5" - "github.com/zclconf/go-cty/cty/msgpack" - "google.golang.org/grpc" -) - -// GRPCProviderPlugin implements plugin.GRPCPlugin for the go-plugin package. -type GRPCProviderPlugin struct { - plugin.Plugin - GRPCProvider func() proto.ProviderServer -} - -func (p *GRPCProviderPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { - return &GRPCProvider{ - client: proto.NewProviderClient(c), - ctx: ctx, - }, nil -} - -func (p *GRPCProviderPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error { - proto.RegisterProviderServer(s, p.GRPCProvider()) - return nil -} - -// GRPCProvider handles the client, or core side of the plugin rpc connection. -// The GRPCProvider methods are mostly a translation layer between the -// terraform provioders types and the grpc proto types, directly converting -// between the two. -type GRPCProvider struct { - // PluginClient provides a reference to the plugin.Client which controls the plugin process. - // This allows the GRPCProvider a way to shutdown the plugin process. - PluginClient *plugin.Client - - // TestServer contains a grpc.Server to close when the GRPCProvider is being - // used in an end to end test of a provider. - TestServer *grpc.Server - - // Proto client use to make the grpc service calls. - client proto.ProviderClient - - // this context is created by the plugin package, and is canceled when the - // plugin process ends. - ctx context.Context - - // schema stores the schema for this provider. This is used to properly - // serialize the state for requests. - mu sync.Mutex - schemas providers.GetSchemaResponse -} - -// getSchema is used internally to get the saved provider schema. The schema -// should have already been fetched from the provider, but we have to -// synchronize access to avoid being called concurrently with GetSchema. -func (p *GRPCProvider) getSchema() providers.GetSchemaResponse { - p.mu.Lock() - // unlock inline in case GetSchema needs to be called - if p.schemas.Provider.Block != nil { - p.mu.Unlock() - return p.schemas - } - p.mu.Unlock() - - // the schema should have been fetched already, but give it another shot - // just in case things are being called out of order. This may happen for - // tests. - schemas := p.GetSchema() - if schemas.Diagnostics.HasErrors() { - panic(schemas.Diagnostics.Err()) - } - - return schemas -} - -// getResourceSchema is a helper to extract the schema for a resource, and -// panics if the schema is not available. -func (p *GRPCProvider) getResourceSchema(name string) providers.Schema { - schema := p.getSchema() - resSchema, ok := schema.ResourceTypes[name] - if !ok { - panic("unknown resource type " + name) - } - return resSchema -} - -// gettDatasourceSchema is a helper to extract the schema for a datasource, and -// panics if that schema is not available. -func (p *GRPCProvider) getDatasourceSchema(name string) providers.Schema { - schema := p.getSchema() - dataSchema, ok := schema.DataSources[name] - if !ok { - panic("unknown data source " + name) - } - return dataSchema -} - -func (p *GRPCProvider) GetSchema() (resp providers.GetSchemaResponse) { - log.Printf("[TRACE] GRPCProvider: GetSchema") - p.mu.Lock() - defer p.mu.Unlock() - - if p.schemas.Provider.Block != nil { - return p.schemas - } - - resp.ResourceTypes = make(map[string]providers.Schema) - resp.DataSources = make(map[string]providers.Schema) - - // Some providers may generate quite large schemas, and the internal default - // grpc response size limit is 4MB. 64MB should cover most any use case, and - // if we get providers nearing that we may want to consider a finer-grained - // API to fetch individual resource schemas. - // Note: this option is marked as EXPERIMENTAL in the grpc API. - const maxRecvSize = 64 << 20 - protoResp, err := p.client.GetSchema(p.ctx, new(proto.GetProviderSchema_Request), grpc.MaxRecvMsgSizeCallOption{MaxRecvMsgSize: maxRecvSize}) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - - resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) - - if protoResp.Provider == nil { - resp.Diagnostics = resp.Diagnostics.Append(errors.New("missing provider schema")) - return resp - } - - resp.Provider = convert.ProtoToProviderSchema(protoResp.Provider) - - for name, res := range protoResp.ResourceSchemas { - resp.ResourceTypes[name] = convert.ProtoToProviderSchema(res) - } - - for name, data := range protoResp.DataSourceSchemas { - resp.DataSources[name] = convert.ProtoToProviderSchema(data) - } - - p.schemas = resp - - return resp -} - -func (p *GRPCProvider) PrepareProviderConfig(r providers.PrepareProviderConfigRequest) (resp providers.PrepareProviderConfigResponse) { - log.Printf("[TRACE] GRPCProvider: PrepareProviderConfig") - - schema := p.getSchema() - ty := schema.Provider.Block.ImpliedType() - - mp, err := msgpack.Marshal(r.Config, ty) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - - protoReq := &proto.PrepareProviderConfig_Request{ - Config: &proto.DynamicValue{Msgpack: mp}, - } - - protoResp, err := p.client.PrepareProviderConfig(p.ctx, protoReq) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - - config := cty.NullVal(ty) - if protoResp.PreparedConfig != nil { - config, err = msgpack.Unmarshal(protoResp.PreparedConfig.Msgpack, ty) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - } - resp.PreparedConfig = config - - resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) - return resp -} - -func (p *GRPCProvider) ValidateResourceTypeConfig(r providers.ValidateResourceTypeConfigRequest) (resp providers.ValidateResourceTypeConfigResponse) { - log.Printf("[TRACE] GRPCProvider: ValidateResourceTypeConfig") - resourceSchema := p.getResourceSchema(r.TypeName) - - mp, err := msgpack.Marshal(r.Config, resourceSchema.Block.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - - protoReq := &proto.ValidateResourceTypeConfig_Request{ - TypeName: r.TypeName, - Config: &proto.DynamicValue{Msgpack: mp}, - } - - protoResp, err := p.client.ValidateResourceTypeConfig(p.ctx, protoReq) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - - resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) - return resp -} - -func (p *GRPCProvider) ValidateDataSourceConfig(r providers.ValidateDataSourceConfigRequest) (resp providers.ValidateDataSourceConfigResponse) { - log.Printf("[TRACE] GRPCProvider: ValidateDataSourceConfig") - - dataSchema := p.getDatasourceSchema(r.TypeName) - - mp, err := msgpack.Marshal(r.Config, dataSchema.Block.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - - protoReq := &proto.ValidateDataSourceConfig_Request{ - TypeName: r.TypeName, - Config: &proto.DynamicValue{Msgpack: mp}, - } - - protoResp, err := p.client.ValidateDataSourceConfig(p.ctx, protoReq) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) - return resp -} - -func (p *GRPCProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) { - log.Printf("[TRACE] GRPCProvider: UpgradeResourceState") - - resSchema := p.getResourceSchema(r.TypeName) - - protoReq := &proto.UpgradeResourceState_Request{ - TypeName: r.TypeName, - Version: int64(r.Version), - RawState: &proto.RawState{ - Json: r.RawStateJSON, - Flatmap: r.RawStateFlatmap, - }, - } - - protoResp, err := p.client.UpgradeResourceState(p.ctx, protoReq) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) - - state := cty.NullVal(resSchema.Block.ImpliedType()) - if protoResp.UpgradedState != nil { - state, err = msgpack.Unmarshal(protoResp.UpgradedState.Msgpack, resSchema.Block.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - } - - resp.UpgradedState = state - return resp -} - -func (p *GRPCProvider) Configure(r providers.ConfigureRequest) (resp providers.ConfigureResponse) { - log.Printf("[TRACE] GRPCProvider: Configure") - - schema := p.getSchema() - - var mp []byte - - // we don't have anything to marshal if there's no config - mp, err := msgpack.Marshal(r.Config, schema.Provider.Block.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - - protoReq := &proto.Configure_Request{ - TerraformVersion: r.TerraformVersion, - Config: &proto.DynamicValue{ - Msgpack: mp, - }, - } - - protoResp, err := p.client.Configure(p.ctx, protoReq) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) - return resp -} - -func (p *GRPCProvider) Stop() error { - log.Printf("[TRACE] GRPCProvider: Stop") - - resp, err := p.client.Stop(p.ctx, new(proto.Stop_Request)) - if err != nil { - return err - } - - if resp.Error != "" { - return errors.New(resp.Error) - } - return nil -} - -func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp providers.ReadResourceResponse) { - log.Printf("[TRACE] GRPCProvider: ReadResource") - - resSchema := p.getResourceSchema(r.TypeName) - - mp, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - - protoReq := &proto.ReadResource_Request{ - TypeName: r.TypeName, - CurrentState: &proto.DynamicValue{Msgpack: mp}, - Private: r.Private, - } - - protoResp, err := p.client.ReadResource(p.ctx, protoReq) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) - - state := cty.NullVal(resSchema.Block.ImpliedType()) - if protoResp.NewState != nil { - state, err = msgpack.Unmarshal(protoResp.NewState.Msgpack, resSchema.Block.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - } - resp.NewState = state - resp.Private = protoResp.Private - - return resp -} - -func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { - log.Printf("[TRACE] GRPCProvider: PlanResourceChange") - - resSchema := p.getResourceSchema(r.TypeName) - - priorMP, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - - configMP, err := msgpack.Marshal(r.Config, resSchema.Block.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - - propMP, err := msgpack.Marshal(r.ProposedNewState, resSchema.Block.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - - protoReq := &proto.PlanResourceChange_Request{ - TypeName: r.TypeName, - PriorState: &proto.DynamicValue{Msgpack: priorMP}, - Config: &proto.DynamicValue{Msgpack: configMP}, - ProposedNewState: &proto.DynamicValue{Msgpack: propMP}, - PriorPrivate: r.PriorPrivate, - } - - protoResp, err := p.client.PlanResourceChange(p.ctx, protoReq) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) - - state := cty.NullVal(resSchema.Block.ImpliedType()) - if protoResp.PlannedState != nil { - state, err = msgpack.Unmarshal(protoResp.PlannedState.Msgpack, resSchema.Block.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - } - resp.PlannedState = state - - for _, p := range protoResp.RequiresReplace { - resp.RequiresReplace = append(resp.RequiresReplace, convert.AttributePathToPath(p)) - } - - resp.PlannedPrivate = protoResp.PlannedPrivate - - resp.LegacyTypeSystem = protoResp.LegacyTypeSystem - - return resp -} - -func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { - log.Printf("[TRACE] GRPCProvider: ApplyResourceChange") - - resSchema := p.getResourceSchema(r.TypeName) - - priorMP, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - plannedMP, err := msgpack.Marshal(r.PlannedState, resSchema.Block.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - configMP, err := msgpack.Marshal(r.Config, resSchema.Block.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - - protoReq := &proto.ApplyResourceChange_Request{ - TypeName: r.TypeName, - PriorState: &proto.DynamicValue{Msgpack: priorMP}, - PlannedState: &proto.DynamicValue{Msgpack: plannedMP}, - Config: &proto.DynamicValue{Msgpack: configMP}, - PlannedPrivate: r.PlannedPrivate, - } - - protoResp, err := p.client.ApplyResourceChange(p.ctx, protoReq) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) - - resp.Private = protoResp.Private - - state := cty.NullVal(resSchema.Block.ImpliedType()) - if protoResp.NewState != nil { - state, err = msgpack.Unmarshal(protoResp.NewState.Msgpack, resSchema.Block.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - } - resp.NewState = state - - resp.LegacyTypeSystem = protoResp.LegacyTypeSystem - - return resp -} - -func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateRequest) (resp providers.ImportResourceStateResponse) { - log.Printf("[TRACE] GRPCProvider: ImportResourceState") - - protoReq := &proto.ImportResourceState_Request{ - TypeName: r.TypeName, - Id: r.ID, - } - - protoResp, err := p.client.ImportResourceState(p.ctx, protoReq) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) - - for _, imported := range protoResp.ImportedResources { - resource := providers.ImportedResource{ - TypeName: imported.TypeName, - Private: imported.Private, - } - - resSchema := p.getResourceSchema(resource.TypeName) - state := cty.NullVal(resSchema.Block.ImpliedType()) - if imported.State != nil { - state, err = msgpack.Unmarshal(imported.State.Msgpack, resSchema.Block.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - } - resource.State = state - resp.ImportedResources = append(resp.ImportedResources, resource) - } - - return resp -} - -func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { - log.Printf("[TRACE] GRPCProvider: ReadDataSource") - - dataSchema := p.getDatasourceSchema(r.TypeName) - - config, err := msgpack.Marshal(r.Config, dataSchema.Block.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - - protoReq := &proto.ReadDataSource_Request{ - TypeName: r.TypeName, - Config: &proto.DynamicValue{ - Msgpack: config, - }, - } - - protoResp, err := p.client.ReadDataSource(p.ctx, protoReq) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) - - state := cty.NullVal(dataSchema.Block.ImpliedType()) - if protoResp.State != nil { - state, err = msgpack.Unmarshal(protoResp.State.Msgpack, dataSchema.Block.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - } - resp.State = state - - return resp -} - -// closing the grpc connection is final, and terraform will call it at the end of every phase. -func (p *GRPCProvider) Close() error { - log.Printf("[TRACE] GRPCProvider: Close") - - // Make sure to stop the server if we're not running within go-plugin. - if p.TestServer != nil { - p.TestServer.Stop() - } - - // Check this since it's not automatically inserted during plugin creation. - // It's currently only inserted by the command package, because that is - // where the factory is built and is the only point with access to the - // plugin.Client. - if p.PluginClient == nil { - log.Println("[DEBUG] provider has no plugin.Client") - return nil - } - - p.PluginClient.Kill() - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/grpc_provisioner.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/grpc_provisioner.go deleted file mode 100644 index c0e6f549..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/grpc_provisioner.go +++ /dev/null @@ -1,178 +0,0 @@ -package plugin - -import ( - "context" - "errors" - "io" - "log" - "sync" - - plugin "github.com/hashicorp/go-plugin" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/plugin/convert" - "github.com/hashicorp/terraform-plugin-sdk/internal/provisioners" - proto "github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/msgpack" - "google.golang.org/grpc" -) - -// GRPCProvisionerPlugin is the plugin.GRPCPlugin implementation. -type GRPCProvisionerPlugin struct { - plugin.Plugin - GRPCProvisioner func() proto.ProvisionerServer -} - -func (p *GRPCProvisionerPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { - return &GRPCProvisioner{ - client: proto.NewProvisionerClient(c), - ctx: ctx, - }, nil -} - -func (p *GRPCProvisionerPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error { - proto.RegisterProvisionerServer(s, p.GRPCProvisioner()) - return nil -} - -// provisioners.Interface grpc implementation -type GRPCProvisioner struct { - // PluginClient provides a reference to the plugin.Client which controls the plugin process. - // This allows the GRPCProvider a way to shutdown the plugin process. - PluginClient *plugin.Client - - client proto.ProvisionerClient - ctx context.Context - - // Cache the schema since we need it for serialization in each method call. - mu sync.Mutex - schema *configschema.Block -} - -func (p *GRPCProvisioner) GetSchema() (resp provisioners.GetSchemaResponse) { - p.mu.Lock() - defer p.mu.Unlock() - - if p.schema != nil { - return provisioners.GetSchemaResponse{ - Provisioner: p.schema, - } - } - - protoResp, err := p.client.GetSchema(p.ctx, new(proto.GetProvisionerSchema_Request)) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) - - if protoResp.Provisioner == nil { - resp.Diagnostics = resp.Diagnostics.Append(errors.New("missing provisioner schema")) - return resp - } - - resp.Provisioner = convert.ProtoToConfigSchema(protoResp.Provisioner.Block) - - p.schema = resp.Provisioner - - return resp -} - -func (p *GRPCProvisioner) ValidateProvisionerConfig(r provisioners.ValidateProvisionerConfigRequest) (resp provisioners.ValidateProvisionerConfigResponse) { - schema := p.GetSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = resp.Diagnostics.Append(schema.Diagnostics) - return resp - } - - mp, err := msgpack.Marshal(r.Config, schema.Provisioner.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - - protoReq := &proto.ValidateProvisionerConfig_Request{ - Config: &proto.DynamicValue{Msgpack: mp}, - } - protoResp, err := p.client.ValidateProvisionerConfig(p.ctx, protoReq) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) - return resp -} - -func (p *GRPCProvisioner) ProvisionResource(r provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { - schema := p.GetSchema() - if schema.Diagnostics.HasErrors() { - resp.Diagnostics = resp.Diagnostics.Append(schema.Diagnostics) - return resp - } - - mp, err := msgpack.Marshal(r.Config, schema.Provisioner.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - - // connection is always assumed to be a simple string map - connMP, err := msgpack.Marshal(r.Connection, cty.Map(cty.String)) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - - protoReq := &proto.ProvisionResource_Request{ - Config: &proto.DynamicValue{Msgpack: mp}, - Connection: &proto.DynamicValue{Msgpack: connMP}, - } - - outputClient, err := p.client.ProvisionResource(p.ctx, protoReq) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - - for { - rcv, err := outputClient.Recv() - if rcv != nil { - r.UIOutput.Output(rcv.Output) - } - if err != nil { - if err != io.EOF { - resp.Diagnostics = resp.Diagnostics.Append(err) - } - break - } - - if len(rcv.Diagnostics) > 0 { - resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(rcv.Diagnostics)) - break - } - } - - return resp -} - -func (p *GRPCProvisioner) Stop() error { - protoResp, err := p.client.Stop(p.ctx, &proto.Stop_Request{}) - if err != nil { - return err - } - if protoResp.Error != "" { - return errors.New(protoResp.Error) - } - return nil -} - -func (p *GRPCProvisioner) Close() error { - // check this since it's not automatically inserted during plugin creation - if p.PluginClient == nil { - log.Println("[DEBUG] provider has no plugin.Client") - return nil - } - - p.PluginClient.Kill() - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/plugin.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/plugin.go deleted file mode 100644 index e4fb5776..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/plugin.go +++ /dev/null @@ -1,14 +0,0 @@ -package plugin - -import ( - "github.com/hashicorp/go-plugin" -) - -// See serve.go for serving plugins - -var VersionedPlugins = map[int]plugin.PluginSet{ - 5: { - "provider": &GRPCProviderPlugin{}, - "provisioner": &GRPCProvisionerPlugin{}, - }, -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/resource_provider.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/resource_provider.go deleted file mode 100644 index bfd62e2e..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/resource_provider.go +++ /dev/null @@ -1,620 +0,0 @@ -package plugin - -import ( - "net/rpc" - - "github.com/hashicorp/go-plugin" - "github.com/hashicorp/terraform-plugin-sdk/terraform" -) - -// ResourceProviderPlugin is the plugin.Plugin implementation. -type ResourceProviderPlugin struct { - ResourceProvider func() terraform.ResourceProvider -} - -func (p *ResourceProviderPlugin) Server(b *plugin.MuxBroker) (interface{}, error) { - return &ResourceProviderServer{ - Broker: b, - Provider: p.ResourceProvider(), - }, nil -} - -func (p *ResourceProviderPlugin) Client( - b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { - return &ResourceProvider{Broker: b, Client: c}, nil -} - -// ResourceProvider is an implementation of terraform.ResourceProvider -// that communicates over RPC. -type ResourceProvider struct { - Broker *plugin.MuxBroker - Client *rpc.Client -} - -func (p *ResourceProvider) Stop() error { - var resp ResourceProviderStopResponse - err := p.Client.Call("Plugin.Stop", new(interface{}), &resp) - if err != nil { - return err - } - if resp.Error != nil { - err = resp.Error - } - - return err -} - -func (p *ResourceProvider) GetSchema(req *terraform.ProviderSchemaRequest) (*terraform.ProviderSchema, error) { - var result ResourceProviderGetSchemaResponse - args := &ResourceProviderGetSchemaArgs{ - Req: req, - } - - err := p.Client.Call("Plugin.GetSchema", args, &result) - if err != nil { - return nil, err - } - - if result.Error != nil { - err = result.Error - } - - return result.Schema, err -} - -func (p *ResourceProvider) Input( - input terraform.UIInput, - c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) { - id := p.Broker.NextId() - go p.Broker.AcceptAndServe(id, &UIInputServer{ - UIInput: input, - }) - - var resp ResourceProviderInputResponse - args := ResourceProviderInputArgs{ - InputId: id, - Config: c, - } - - err := p.Client.Call("Plugin.Input", &args, &resp) - if err != nil { - return nil, err - } - if resp.Error != nil { - err = resp.Error - return nil, err - } - - return resp.Config, nil -} - -func (p *ResourceProvider) Validate(c *terraform.ResourceConfig) ([]string, []error) { - var resp ResourceProviderValidateResponse - args := ResourceProviderValidateArgs{ - Config: c, - } - - err := p.Client.Call("Plugin.Validate", &args, &resp) - if err != nil { - return nil, []error{err} - } - - var errs []error - if len(resp.Errors) > 0 { - errs = make([]error, len(resp.Errors)) - for i, err := range resp.Errors { - errs[i] = err - } - } - - return resp.Warnings, errs -} - -func (p *ResourceProvider) ValidateResource( - t string, c *terraform.ResourceConfig) ([]string, []error) { - var resp ResourceProviderValidateResourceResponse - args := ResourceProviderValidateResourceArgs{ - Config: c, - Type: t, - } - - err := p.Client.Call("Plugin.ValidateResource", &args, &resp) - if err != nil { - return nil, []error{err} - } - - var errs []error - if len(resp.Errors) > 0 { - errs = make([]error, len(resp.Errors)) - for i, err := range resp.Errors { - errs[i] = err - } - } - - return resp.Warnings, errs -} - -func (p *ResourceProvider) Configure(c *terraform.ResourceConfig) error { - var resp ResourceProviderConfigureResponse - err := p.Client.Call("Plugin.Configure", c, &resp) - if err != nil { - return err - } - if resp.Error != nil { - err = resp.Error - } - - return err -} - -func (p *ResourceProvider) Apply( - info *terraform.InstanceInfo, - s *terraform.InstanceState, - d *terraform.InstanceDiff) (*terraform.InstanceState, error) { - var resp ResourceProviderApplyResponse - args := &ResourceProviderApplyArgs{ - Info: info, - State: s, - Diff: d, - } - - err := p.Client.Call("Plugin.Apply", args, &resp) - if err != nil { - return nil, err - } - if resp.Error != nil { - err = resp.Error - } - - return resp.State, err -} - -func (p *ResourceProvider) Diff( - info *terraform.InstanceInfo, - s *terraform.InstanceState, - c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { - var resp ResourceProviderDiffResponse - args := &ResourceProviderDiffArgs{ - Info: info, - State: s, - Config: c, - } - err := p.Client.Call("Plugin.Diff", args, &resp) - if err != nil { - return nil, err - } - if resp.Error != nil { - err = resp.Error - } - - return resp.Diff, err -} - -func (p *ResourceProvider) ValidateDataSource( - t string, c *terraform.ResourceConfig) ([]string, []error) { - var resp ResourceProviderValidateResourceResponse - args := ResourceProviderValidateResourceArgs{ - Config: c, - Type: t, - } - - err := p.Client.Call("Plugin.ValidateDataSource", &args, &resp) - if err != nil { - return nil, []error{err} - } - - var errs []error - if len(resp.Errors) > 0 { - errs = make([]error, len(resp.Errors)) - for i, err := range resp.Errors { - errs[i] = err - } - } - - return resp.Warnings, errs -} - -func (p *ResourceProvider) Refresh( - info *terraform.InstanceInfo, - s *terraform.InstanceState) (*terraform.InstanceState, error) { - var resp ResourceProviderRefreshResponse - args := &ResourceProviderRefreshArgs{ - Info: info, - State: s, - } - - err := p.Client.Call("Plugin.Refresh", args, &resp) - if err != nil { - return nil, err - } - if resp.Error != nil { - err = resp.Error - } - - return resp.State, err -} - -func (p *ResourceProvider) ImportState( - info *terraform.InstanceInfo, - id string) ([]*terraform.InstanceState, error) { - var resp ResourceProviderImportStateResponse - args := &ResourceProviderImportStateArgs{ - Info: info, - Id: id, - } - - err := p.Client.Call("Plugin.ImportState", args, &resp) - if err != nil { - return nil, err - } - if resp.Error != nil { - err = resp.Error - } - - return resp.State, err -} - -func (p *ResourceProvider) Resources() []terraform.ResourceType { - var result []terraform.ResourceType - - err := p.Client.Call("Plugin.Resources", new(interface{}), &result) - if err != nil { - // TODO: panic, log, what? - return nil - } - - return result -} - -func (p *ResourceProvider) ReadDataDiff( - info *terraform.InstanceInfo, - c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { - var resp ResourceProviderReadDataDiffResponse - args := &ResourceProviderReadDataDiffArgs{ - Info: info, - Config: c, - } - - err := p.Client.Call("Plugin.ReadDataDiff", args, &resp) - if err != nil { - return nil, err - } - if resp.Error != nil { - err = resp.Error - } - - return resp.Diff, err -} - -func (p *ResourceProvider) ReadDataApply( - info *terraform.InstanceInfo, - d *terraform.InstanceDiff) (*terraform.InstanceState, error) { - var resp ResourceProviderReadDataApplyResponse - args := &ResourceProviderReadDataApplyArgs{ - Info: info, - Diff: d, - } - - err := p.Client.Call("Plugin.ReadDataApply", args, &resp) - if err != nil { - return nil, err - } - if resp.Error != nil { - err = resp.Error - } - - return resp.State, err -} - -func (p *ResourceProvider) DataSources() []terraform.DataSource { - var result []terraform.DataSource - - err := p.Client.Call("Plugin.DataSources", new(interface{}), &result) - if err != nil { - // TODO: panic, log, what? - return nil - } - - return result -} - -func (p *ResourceProvider) Close() error { - return p.Client.Close() -} - -// ResourceProviderServer is a net/rpc compatible structure for serving -// a ResourceProvider. This should not be used directly. -type ResourceProviderServer struct { - Broker *plugin.MuxBroker - Provider terraform.ResourceProvider -} - -type ResourceProviderStopResponse struct { - Error *plugin.BasicError -} - -type ResourceProviderGetSchemaArgs struct { - Req *terraform.ProviderSchemaRequest -} - -type ResourceProviderGetSchemaResponse struct { - Schema *terraform.ProviderSchema - Error *plugin.BasicError -} - -type ResourceProviderConfigureResponse struct { - Error *plugin.BasicError -} - -type ResourceProviderInputArgs struct { - InputId uint32 - Config *terraform.ResourceConfig -} - -type ResourceProviderInputResponse struct { - Config *terraform.ResourceConfig - Error *plugin.BasicError -} - -type ResourceProviderApplyArgs struct { - Info *terraform.InstanceInfo - State *terraform.InstanceState - Diff *terraform.InstanceDiff -} - -type ResourceProviderApplyResponse struct { - State *terraform.InstanceState - Error *plugin.BasicError -} - -type ResourceProviderDiffArgs struct { - Info *terraform.InstanceInfo - State *terraform.InstanceState - Config *terraform.ResourceConfig -} - -type ResourceProviderDiffResponse struct { - Diff *terraform.InstanceDiff - Error *plugin.BasicError -} - -type ResourceProviderRefreshArgs struct { - Info *terraform.InstanceInfo - State *terraform.InstanceState -} - -type ResourceProviderRefreshResponse struct { - State *terraform.InstanceState - Error *plugin.BasicError -} - -type ResourceProviderImportStateArgs struct { - Info *terraform.InstanceInfo - Id string -} - -type ResourceProviderImportStateResponse struct { - State []*terraform.InstanceState - Error *plugin.BasicError -} - -type ResourceProviderReadDataApplyArgs struct { - Info *terraform.InstanceInfo - Diff *terraform.InstanceDiff -} - -type ResourceProviderReadDataApplyResponse struct { - State *terraform.InstanceState - Error *plugin.BasicError -} - -type ResourceProviderReadDataDiffArgs struct { - Info *terraform.InstanceInfo - Config *terraform.ResourceConfig -} - -type ResourceProviderReadDataDiffResponse struct { - Diff *terraform.InstanceDiff - Error *plugin.BasicError -} - -type ResourceProviderValidateArgs struct { - Config *terraform.ResourceConfig -} - -type ResourceProviderValidateResponse struct { - Warnings []string - Errors []*plugin.BasicError -} - -type ResourceProviderValidateResourceArgs struct { - Config *terraform.ResourceConfig - Type string -} - -type ResourceProviderValidateResourceResponse struct { - Warnings []string - Errors []*plugin.BasicError -} - -func (s *ResourceProviderServer) Stop( - _ interface{}, - reply *ResourceProviderStopResponse) error { - err := s.Provider.Stop() - *reply = ResourceProviderStopResponse{ - Error: plugin.NewBasicError(err), - } - - return nil -} - -func (s *ResourceProviderServer) GetSchema( - args *ResourceProviderGetSchemaArgs, - result *ResourceProviderGetSchemaResponse, -) error { - schema, err := s.Provider.GetSchema(args.Req) - result.Schema = schema - if err != nil { - result.Error = plugin.NewBasicError(err) - } - return nil -} - -func (s *ResourceProviderServer) Input( - args *ResourceProviderInputArgs, - reply *ResourceProviderInputResponse) error { - conn, err := s.Broker.Dial(args.InputId) - if err != nil { - *reply = ResourceProviderInputResponse{ - Error: plugin.NewBasicError(err), - } - return nil - } - client := rpc.NewClient(conn) - defer client.Close() - - input := &UIInput{Client: client} - - config, err := s.Provider.Input(input, args.Config) - *reply = ResourceProviderInputResponse{ - Config: config, - Error: plugin.NewBasicError(err), - } - - return nil -} - -func (s *ResourceProviderServer) Validate( - args *ResourceProviderValidateArgs, - reply *ResourceProviderValidateResponse) error { - warns, errs := s.Provider.Validate(args.Config) - berrs := make([]*plugin.BasicError, len(errs)) - for i, err := range errs { - berrs[i] = plugin.NewBasicError(err) - } - *reply = ResourceProviderValidateResponse{ - Warnings: warns, - Errors: berrs, - } - return nil -} - -func (s *ResourceProviderServer) ValidateResource( - args *ResourceProviderValidateResourceArgs, - reply *ResourceProviderValidateResourceResponse) error { - warns, errs := s.Provider.ValidateResource(args.Type, args.Config) - berrs := make([]*plugin.BasicError, len(errs)) - for i, err := range errs { - berrs[i] = plugin.NewBasicError(err) - } - *reply = ResourceProviderValidateResourceResponse{ - Warnings: warns, - Errors: berrs, - } - return nil -} - -func (s *ResourceProviderServer) Configure( - config *terraform.ResourceConfig, - reply *ResourceProviderConfigureResponse) error { - err := s.Provider.Configure(config) - *reply = ResourceProviderConfigureResponse{ - Error: plugin.NewBasicError(err), - } - return nil -} - -func (s *ResourceProviderServer) Apply( - args *ResourceProviderApplyArgs, - result *ResourceProviderApplyResponse) error { - state, err := s.Provider.Apply(args.Info, args.State, args.Diff) - *result = ResourceProviderApplyResponse{ - State: state, - Error: plugin.NewBasicError(err), - } - return nil -} - -func (s *ResourceProviderServer) Diff( - args *ResourceProviderDiffArgs, - result *ResourceProviderDiffResponse) error { - diff, err := s.Provider.Diff(args.Info, args.State, args.Config) - *result = ResourceProviderDiffResponse{ - Diff: diff, - Error: plugin.NewBasicError(err), - } - return nil -} - -func (s *ResourceProviderServer) Refresh( - args *ResourceProviderRefreshArgs, - result *ResourceProviderRefreshResponse) error { - newState, err := s.Provider.Refresh(args.Info, args.State) - *result = ResourceProviderRefreshResponse{ - State: newState, - Error: plugin.NewBasicError(err), - } - return nil -} - -func (s *ResourceProviderServer) ImportState( - args *ResourceProviderImportStateArgs, - result *ResourceProviderImportStateResponse) error { - states, err := s.Provider.ImportState(args.Info, args.Id) - *result = ResourceProviderImportStateResponse{ - State: states, - Error: plugin.NewBasicError(err), - } - return nil -} - -func (s *ResourceProviderServer) Resources( - nothing interface{}, - result *[]terraform.ResourceType) error { - *result = s.Provider.Resources() - return nil -} - -func (s *ResourceProviderServer) ValidateDataSource( - args *ResourceProviderValidateResourceArgs, - reply *ResourceProviderValidateResourceResponse) error { - warns, errs := s.Provider.ValidateDataSource(args.Type, args.Config) - berrs := make([]*plugin.BasicError, len(errs)) - for i, err := range errs { - berrs[i] = plugin.NewBasicError(err) - } - *reply = ResourceProviderValidateResourceResponse{ - Warnings: warns, - Errors: berrs, - } - return nil -} - -func (s *ResourceProviderServer) ReadDataDiff( - args *ResourceProviderReadDataDiffArgs, - result *ResourceProviderReadDataDiffResponse) error { - diff, err := s.Provider.ReadDataDiff(args.Info, args.Config) - *result = ResourceProviderReadDataDiffResponse{ - Diff: diff, - Error: plugin.NewBasicError(err), - } - return nil -} - -func (s *ResourceProviderServer) ReadDataApply( - args *ResourceProviderReadDataApplyArgs, - result *ResourceProviderReadDataApplyResponse) error { - newState, err := s.Provider.ReadDataApply(args.Info, args.Diff) - *result = ResourceProviderReadDataApplyResponse{ - State: newState, - Error: plugin.NewBasicError(err), - } - return nil -} - -func (s *ResourceProviderServer) DataSources( - nothing interface{}, - result *[]terraform.DataSource) error { - *result = s.Provider.DataSources() - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/serve.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/serve.go index cbe9fc63..baaab2d1 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/serve.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/serve.go @@ -1,41 +1,32 @@ package plugin import ( + "log" + + hclog "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-plugin" - grpcplugin "github.com/hashicorp/terraform-plugin-sdk/internal/helper/plugin" - proto "github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5" - "github.com/hashicorp/terraform-plugin-sdk/terraform" + "google.golang.org/grpc" + + "github.com/hashicorp/terraform-plugin-go/tfprotov5" + tf5server "github.com/hashicorp/terraform-plugin-go/tfprotov5/server" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) const ( // The constants below are the names of the plugins that can be dispensed // from the plugin server. ProviderPluginName = "provider" - - // DefaultProtocolVersion is the protocol version assumed for legacy clients that don't specify - // a particular version during their handshake. This is the version used when Terraform 0.10 - // and 0.11 launch plugins that were built with support for both versions 4 and 5, and must - // stay unchanged at 4 until we intentionally build plugins that are not compatible with 0.10 and - // 0.11. - DefaultProtocolVersion = 4 ) // Handshake is the HandshakeConfig used to configure clients and servers. var Handshake = plugin.HandshakeConfig{ - // The ProtocolVersion is the version that must match between TF core - // and TF plugins. This should be bumped whenever a change happens in - // one or the other that makes it so that they can't safely communicate. - // This could be adding a new interface value, it could be how - // helper/schema computes diffs, etc. - ProtocolVersion: DefaultProtocolVersion, - // The magic cookie values should NEVER be changed. MagicCookieKey: "TF_PLUGIN_MAGIC_COOKIE", MagicCookieValue: "d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2", } -type ProviderFunc func() terraform.ResourceProvider -type GRPCProviderFunc func() proto.ProviderServer +type ProviderFunc func() *schema.Provider +type GRPCProviderFunc func() tfprotov5.ProviderServer // ServeOpts are the configurations to serve a plugin. type ServeOpts struct { @@ -44,57 +35,65 @@ type ServeOpts struct { // Wrapped versions of the above plugins will automatically shimmed and // added to the GRPC functions when possible. GRPCProviderFunc GRPCProviderFunc + + // Logger is the logger that go-plugin will use. + Logger hclog.Logger + + // TestConfig should only be set when the provider is being tested; it + // will opt out of go-plugin's lifecycle management and other features, + // and will use the supplied configuration options to control the + // plugin's lifecycle and communicate connection information. See the + // go-plugin GoDoc for more information. + TestConfig *plugin.ServeTestConfig + + // Set NoLogOutputOverride to not override the log output with an hclog + // adapter. This should only be used when running the plugin in + // acceptance tests. + NoLogOutputOverride bool } // Serve serves a plugin. This function never returns and should be the final // function called in the main function of the plugin. func Serve(opts *ServeOpts) { + if !opts.NoLogOutputOverride { + // In order to allow go-plugin to correctly pass log-levels through to + // terraform, we need to use an hclog.Logger with JSON output. We can + // inject this into the std `log` package here, so existing providers will + // make use of it automatically. + logger := hclog.New(&hclog.LoggerOptions{ + // We send all output to terraform. Go-plugin will take the output and + // pass it through another hclog.Logger on the client side where it can + // be filtered. + Level: hclog.Trace, + JSONFormat: true, + }) + log.SetOutput(logger.StandardWriter(&hclog.StandardLoggerOptions{InferLevels: true})) + } + // since the plugins may not yet be aware of the new protocol, we // automatically wrap the plugins in the grpc shims. if opts.GRPCProviderFunc == nil && opts.ProviderFunc != nil { - provider := grpcplugin.NewGRPCProviderServerShim(opts.ProviderFunc()) - // this is almost always going to be a *schema.Provider, but check that - // we got back a valid provider just in case. - if provider != nil { - opts.GRPCProviderFunc = func() proto.ProviderServer { - return provider - } + opts.GRPCProviderFunc = func() tfprotov5.ProviderServer { + return schema.NewGRPCProviderServer(opts.ProviderFunc()) } } + provider := opts.GRPCProviderFunc() plugin.Serve(&plugin.ServeConfig{ - HandshakeConfig: Handshake, - VersionedPlugins: pluginSet(opts), - GRPCServer: plugin.DefaultGRPCServer, - }) -} - -// pluginMap returns the legacy map[string]plugin.Plugin to use for configuring -// a plugin server or client. -func legacyPluginMap(opts *ServeOpts) map[string]plugin.Plugin { - return map[string]plugin.Plugin{ - "provider": &ResourceProviderPlugin{ - ResourceProvider: opts.ProviderFunc, + HandshakeConfig: Handshake, + VersionedPlugins: map[int]plugin.PluginSet{ + 5: { + ProviderPluginName: &tf5server.GRPCProviderPlugin{ + GRPCProvider: func() tfprotov5.ProviderServer { + return provider + }, + }, + }, }, - } -} - -func pluginSet(opts *ServeOpts) map[int]plugin.PluginSet { - // Set the legacy netrpc plugins at version 4. - // The oldest version is returned in when executed by a legacy go-plugin - // client. - plugins := map[int]plugin.PluginSet{ - 4: legacyPluginMap(opts), - } - - // add the new protocol versions if they're configured - if opts.GRPCProviderFunc != nil { - plugins[5] = plugin.PluginSet{} - if opts.GRPCProviderFunc != nil { - plugins[5]["provider"] = &GRPCProviderPlugin{ - GRPCProvider: opts.GRPCProviderFunc, - } - } - } - return plugins + GRPCServer: func(opts []grpc.ServerOption) *grpc.Server { + return grpc.NewServer(opts...) + }, + Logger: opts.Logger, + Test: opts.TestConfig, + }) } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/ui_input.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/ui_input.go deleted file mode 100644 index b24b03eb..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/ui_input.go +++ /dev/null @@ -1,52 +0,0 @@ -package plugin - -import ( - "context" - "net/rpc" - - "github.com/hashicorp/go-plugin" - "github.com/hashicorp/terraform-plugin-sdk/terraform" -) - -// UIInput is an implementation of terraform.UIInput that communicates -// over RPC. -type UIInput struct { - Client *rpc.Client -} - -func (i *UIInput) Input(ctx context.Context, opts *terraform.InputOpts) (string, error) { - var resp UIInputInputResponse - err := i.Client.Call("Plugin.Input", opts, &resp) - if err != nil { - return "", err - } - if resp.Error != nil { - err = resp.Error - return "", err - } - - return resp.Value, nil -} - -type UIInputInputResponse struct { - Value string - Error *plugin.BasicError -} - -// UIInputServer is a net/rpc compatible structure for serving -// a UIInputServer. This should not be used directly. -type UIInputServer struct { - UIInput terraform.UIInput -} - -func (s *UIInputServer) Input( - opts *terraform.InputOpts, - reply *UIInputInputResponse) error { - value, err := s.UIInput.Input(context.Background(), opts) - *reply = UIInputInputResponse{ - Value: value, - Error: plugin.NewBasicError(err), - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/ui_output.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/ui_output.go deleted file mode 100644 index 07c13d03..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/plugin/ui_output.go +++ /dev/null @@ -1,29 +0,0 @@ -package plugin - -import ( - "net/rpc" - - "github.com/hashicorp/terraform-plugin-sdk/terraform" -) - -// UIOutput is an implementatin of terraform.UIOutput that communicates -// over RPC. -type UIOutput struct { - Client *rpc.Client -} - -func (o *UIOutput) Output(v string) { - o.Client.Call("Plugin.Output", v, new(interface{})) -} - -// UIOutputServer is the RPC server for serving UIOutput. -type UIOutputServer struct { - UIOutput terraform.UIOutput -} - -func (s *UIOutputServer) Output( - v string, - reply *interface{}) error { - s.UIOutput.Output(v) - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/scripts/cherry_pick/IGNORE_FILES b/vendor/github.com/hashicorp/terraform-plugin-sdk/scripts/cherry_pick/IGNORE_FILES new file mode 100644 index 00000000..d4df91db --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/scripts/cherry_pick/IGNORE_FILES @@ -0,0 +1,7 @@ +^internal/httpclient/(testdata/.*|test-fixtures/.*|[^/]*)$ +^internal/helper/encryption/(testdata/.*|test-fixtures/.*|[^/]*)$ +^internal/version/(testdata/.*|test-fixtures/.*|[^/]*)$ +^website/.*$ +^vendor/.*$ +^go\.mod$ +^go\.sum$ \ No newline at end of file diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/scripts/cherry_pick/cherry_pick.sh b/vendor/github.com/hashicorp/terraform-plugin-sdk/scripts/cherry_pick/cherry_pick.sh new file mode 100755 index 00000000..7cf79880 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/scripts/cherry_pick/cherry_pick.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +if [ "$#" -ne 1 ]; then + echo "Usage: ./scripts/cherry_pick/cherry_pick.sh MERGE_COMMIT_HASH" +fi + +function pleaseUseGNUsed { + echo "Please install GNU sed to your PATH as 'sed'." + exit 1 +} +sed --version > /dev/null || pleaseUseGNUsed + +COMMIT_ID=$1 + +echo "Cherry-picking changes..." +git cherry-pick --no-commit --mainline 1 "$COMMIT_ID" + +echo "Unstaging files removed by us..." +git status --short | sed -n 's/^DU //p' | ifne xargs git rm + +echo "Unstaging added files that match the ignore list..." +git status --short | sed -n 's/^A //p' | grep -Ef ./scripts/cherry_pick/IGNORE_FILES | ifne xargs git rm -f + +echo "Unstaging files where SDK intentionally diverges from Core..." +for f in $(git diff --name-only --cached | grep -Ef ./scripts/cherry_pick/IGNORE_FILES) +do + git reset "$f" + git checkout -- "$f" +done + +echo "Committing changes. If this fails, you must resolve the merge conflict manually." +./scripts/cherry_pick/commit.sh $COMMIT_ID && echo "Success!" diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/scripts/cherry_pick/commit.sh b/vendor/github.com/hashicorp/terraform-plugin-sdk/scripts/cherry_pick/commit.sh new file mode 100755 index 00000000..1c028af5 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/scripts/cherry_pick/commit.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +if [ "$#" -ne 1 ]; then + echo "Usage: ./scripts/cherry_pick/commit.sh MERGE_COMMIT_HASH" +fi + +function pleaseUseGNUsed { + echo "Please install GNU sed to your PATH as 'sed'." + exit 1 +} +sed --version > /dev/null || pleaseUseGNUsed + +COMMIT_ID=$1 + +COMMIT_MSG=$(git log --format=%B -n 1 "$COMMIT_ID" | \ + sed -z -r 's/Merge pull request (#[0-9]+) from ([^\n]*\/[^\n]*)\n\n(.*$)/\3\nThis commit was generated from hashicorp\/terraform\1./g') + +git commit -C "$COMMIT_ID" && \ + # amend commit message afterwards to preserve authorship information + git commit --amend --message "${COMMIT_MSG}" diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/scripts/gofmtcheck.sh b/vendor/github.com/hashicorp/terraform-plugin-sdk/scripts/gofmtcheck.sh new file mode 100755 index 00000000..bad9ba41 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/scripts/gofmtcheck.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -e + +echo "==> Checking that code complies with gofmt requirements..." +gofmt_files=$(gofmt -s -l `find . -name '*.go'`) +if [[ -n ${gofmt_files} ]]; then + echo 'gofmt needs running on the following files:' + echo "${gofmt_files}" + echo "You can use the command: \`make fmt\` to reformat code." + exit 1 +fi + +exit 0 diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/scripts/release/changelog_links.sh b/vendor/github.com/hashicorp/terraform-plugin-sdk/scripts/release/changelog_links.sh new file mode 100755 index 00000000..e557fcb2 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/scripts/release/changelog_links.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# This script rewrites [GH-nnnn]-style references in the CHANGELOG.md file to +# be Markdown links to the given github issues. +# +# This is run during releases so that the issue references in all of the +# released items are presented as clickable links, but we can just use the +# easy [GH-nnnn] shorthand for quickly adding items to the "Unrelease" section +# while merging things between releases. + +set -e + +if [[ ! -f CHANGELOG.md ]]; then + echo "ERROR: CHANGELOG.md not found in pwd." + echo "Please run this from the root of the SDK repository" + exit 1 +fi + +if [[ `uname` == "Darwin" ]]; then + echo "Using BSD sed" + SED="sed -i.bak -E -e" +else + echo "Using GNU sed" + SED="sed -i.bak -r -e" +fi + +$SED 's/GH-([0-9]+)/\[#\1\]\(https:\/\/github.com\/hashicorp\/terraform-plugin-sdk\/issues\/\1\)/g' -e 's/\[\[#(.+)([0-9])\)]$/(\[#\1\2))/g' CHANGELOG.md + +rm CHANGELOG.md.bak diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/scripts/release/release.sh b/vendor/github.com/hashicorp/terraform-plugin-sdk/scripts/release/release.sh new file mode 100755 index 00000000..a0b0a779 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/scripts/release/release.sh @@ -0,0 +1,107 @@ +#!/bin/bash + +set -e +set -x + +# release.sh will: +# 1. Modify changelog +# 2. Run changelog links script +# 3. Modify version in meta/meta.go +# 4. Commit and push changes +# 5. Create a Git tag + +function pleaseUseGNUsed { + echo "Please install GNU sed to your PATH as 'sed'." + exit 1 +} + +function init { + sed --version > /dev/null || pleaseUseGNUsed + + DATE=`date '+%B %d, %Y'` + START_DIR=`pwd` + + if [ "$CI" = true ] ; then + GPG_KEY_ID=FBA4BCB482BC44B5 + gpg --import <(echo -e "${GPG_PUBLIC_KEY}") + gpg --import <(echo -e "${GPG_PRIVATE_KEY}") + git config --global user.email hashibot-feedback+tf-sdk-circleci@hashicorp.com + git config --global user.name "Terraform SDK CircleCI" + fi + + TARGET_VERSION="$(getTargetVersion)" + TARGET_VERSION_CORE="$(getVersionCore)" + TARGET_VERSION_PRERELEASE="$(getVersionPrerelease)" +} + +semverRegex='\([0-9]\+\.[0-9]\+\.[0-9]\+\)\(-\?\)\([0-9a-zA-Z.]\+\)\?' + +function getTargetVersion { + # parse target version from CHANGELOG + sed -n 's/^# '"$semverRegex"' (Unreleased)$/\1\2\3/p' CHANGELOG.md || \ + (echo "\nTarget version not found in changelog, exiting" && \ + exit 1) +} + +function getVersionCore { + # extract major.minor.patch version, e.g. 1.2.3 + echo "${TARGET_VERSION}" | sed -n 's/'"$semverRegex"'/\1/p' +} + +function getVersionPrerelease { + # extract prerelease version, e.g. rc.1 + echo "${TARGET_VERSION}" | sed -n 's/'"$semverRegex"'/\3/p' +} + +function modifyChangelog { + sed -i "s/$TARGET_VERSION (Unreleased)$/$TARGET_VERSION ($DATE)/" CHANGELOG.md +} + +function changelogLinks { + ./scripts/release/changelog_links.sh +} + +function changelogMain { + printf "Modifying Changelog..." + modifyChangelog + printf "ok!\n" + printf "Running Changelog Links..." + changelogLinks + printf "ok!\n" +} + +function modifyVersionFiles { + sed -i "s/var SDKVersion =.*/var SDKVersion = \"${TARGET_VERSION_CORE}\"/" meta/meta.go + sed -i "s/var SDKPrerelease =.*/var SDKPrerelease = \"${TARGET_VERSION_PRERELEASE}\"/" meta/meta.go +} + +function commitChanges { + git add CHANGELOG.md + modifyVersionFiles + git add meta/meta.go + + if [ "$CI" = true ] ; then + git commit --gpg-sign="${GPG_KEY_ID}" -m "v${TARGET_VERSION} [skip ci]" + git tag -a -m "v${TARGET_VERSION}" -s -u "${GPG_KEY_ID}" "v${TARGET_VERSION}" + else + git commit -m "v${TARGET_VERSION} [skip ci]" + git tag -a -m "v${TARGET_VERSION}" -s "v${TARGET_VERSION}" + fi + + git push origin "${CIRCLE_BRANCH}" + git push origin "v${TARGET_VERSION}" +} + +function commitMain { + printf "Committing Changes..." + commitChanges + printf "ok!\n" +} + +function main { + init + changelogMain + commitMain +} + +main diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/context.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/context.go deleted file mode 100644 index eb05c68a..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/context.go +++ /dev/null @@ -1,882 +0,0 @@ -package terraform - -import ( - "bytes" - "context" - "fmt" - "log" - "sync" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/lang" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/provisioners" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/states/statefile" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - "github.com/zclconf/go-cty/cty" -) - -// InputMode defines what sort of input will be asked for when Input -// is called on Context. -type InputMode byte - -const ( - // InputModeVar asks for all variables - InputModeVar InputMode = 1 << iota - - // InputModeVarUnset asks for variables which are not set yet. - // InputModeVar must be set for this to have an effect. - InputModeVarUnset - - // InputModeProvider asks for provider variables - InputModeProvider - - // InputModeStd is the standard operating mode and asks for both variables - // and providers. - InputModeStd = InputModeVar | InputModeProvider -) - -// ContextOpts are the user-configurable options to create a context with -// NewContext. -type ContextOpts struct { - Config *configs.Config - Changes *plans.Changes - State *states.State - Targets []addrs.Targetable - Variables InputValues - Meta *ContextMeta - Destroy bool - - Hooks []Hook - Parallelism int - ProviderResolver providers.Resolver - Provisioners map[string]ProvisionerFactory - - // If non-nil, will apply as additional constraints on the provider - // plugins that will be requested from the provider resolver. - ProviderSHA256s map[string][]byte - SkipProviderVerify bool - - UIInput UIInput -} - -// ContextMeta is metadata about the running context. This is information -// that this package or structure cannot determine on its own but exposes -// into Terraform in various ways. This must be provided by the Context -// initializer. -type ContextMeta struct { - Env string // Env is the state environment -} - -// Context represents all the context that Terraform needs in order to -// perform operations on infrastructure. This structure is built using -// NewContext. -type Context struct { - config *configs.Config - changes *plans.Changes - state *states.State - targets []addrs.Targetable - variables InputValues - meta *ContextMeta - destroy bool - - hooks []Hook - components contextComponentFactory - schemas *Schemas - sh *stopHook - uiInput UIInput - - l sync.Mutex // Lock acquired during any task - parallelSem Semaphore - providerInputConfig map[string]map[string]cty.Value - providerSHA256s map[string][]byte - runCond *sync.Cond - runContext context.Context - runContextCancel context.CancelFunc - shadowErr error -} - -// (additional methods on Context can be found in context_*.go files.) - -// NewContext creates a new Context structure. -// -// Once a Context is created, the caller must not access or mutate any of -// the objects referenced (directly or indirectly) by the ContextOpts fields. -// -// If the returned diagnostics contains errors then the resulting context is -// invalid and must not be used. -func NewContext(opts *ContextOpts) (*Context, tfdiags.Diagnostics) { - log.Printf("[TRACE] terraform.NewContext: starting") - diags := CheckCoreVersionRequirements(opts.Config) - // If version constraints are not met then we'll bail early since otherwise - // we're likely to just see a bunch of other errors related to - // incompatibilities, which could be overwhelming for the user. - if diags.HasErrors() { - return nil, diags - } - - // Copy all the hooks and add our stop hook. We don't append directly - // to the Config so that we're not modifying that in-place. - sh := new(stopHook) - hooks := make([]Hook, len(opts.Hooks)+1) - copy(hooks, opts.Hooks) - hooks[len(opts.Hooks)] = sh - - state := opts.State - if state == nil { - state = states.NewState() - } - - // Determine parallelism, default to 10. We do this both to limit - // CPU pressure but also to have an extra guard against rate throttling - // from providers. - par := opts.Parallelism - if par == 0 { - par = 10 - } - - // Set up the variables in the following sequence: - // 0 - Take default values from the configuration - // 1 - Take values from TF_VAR_x environment variables - // 2 - Take values specified in -var flags, overriding values - // set by environment variables if necessary. This includes - // values taken from -var-file in addition. - var variables InputValues - if opts.Config != nil { - // Default variables from the configuration seed our map. - variables = DefaultVariableValues(opts.Config.Module.Variables) - } - // Variables provided by the caller (from CLI, environment, etc) can - // override the defaults. - variables = variables.Override(opts.Variables) - - // Bind available provider plugins to the constraints in config - var providerFactories map[string]providers.Factory - if opts.ProviderResolver != nil { - deps := ConfigTreeDependencies(opts.Config, state) - reqd := deps.AllPluginRequirements() - if opts.ProviderSHA256s != nil && !opts.SkipProviderVerify { - reqd.LockExecutables(opts.ProviderSHA256s) - } - log.Printf("[TRACE] terraform.NewContext: resolving provider version selections") - - var providerDiags tfdiags.Diagnostics - providerFactories, providerDiags = resourceProviderFactories(opts.ProviderResolver, reqd) - diags = diags.Append(providerDiags) - - if diags.HasErrors() { - return nil, diags - } - } else { - providerFactories = make(map[string]providers.Factory) - } - - components := &basicComponentFactory{ - providers: providerFactories, - provisioners: opts.Provisioners, - } - - log.Printf("[TRACE] terraform.NewContext: loading provider schemas") - schemas, err := LoadSchemas(opts.Config, opts.State, components) - if err != nil { - diags = diags.Append(err) - return nil, diags - } - - changes := opts.Changes - if changes == nil { - changes = plans.NewChanges() - } - - config := opts.Config - if config == nil { - config = configs.NewEmptyConfig() - } - - log.Printf("[TRACE] terraform.NewContext: complete") - - return &Context{ - components: components, - schemas: schemas, - destroy: opts.Destroy, - changes: changes, - hooks: hooks, - meta: opts.Meta, - config: config, - state: state, - targets: opts.Targets, - uiInput: opts.UIInput, - variables: variables, - - parallelSem: NewSemaphore(par), - providerInputConfig: make(map[string]map[string]cty.Value), - providerSHA256s: opts.ProviderSHA256s, - sh: sh, - }, nil -} - -func (c *Context) Schemas() *Schemas { - return c.schemas -} - -type ContextGraphOpts struct { - // If true, validates the graph structure (checks for cycles). - Validate bool - - // Legacy graphs only: won't prune the graph - Verbose bool -} - -// Graph returns the graph used for the given operation type. -// -// The most extensive or complex graph type is GraphTypePlan. -func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, tfdiags.Diagnostics) { - if opts == nil { - opts = &ContextGraphOpts{Validate: true} - } - - log.Printf("[INFO] terraform: building graph: %s", typ) - switch typ { - case GraphTypeApply: - return (&ApplyGraphBuilder{ - Config: c.config, - Changes: c.changes, - State: c.state, - Components: c.components, - Schemas: c.schemas, - Targets: c.targets, - Destroy: c.destroy, - Validate: opts.Validate, - }).Build(addrs.RootModuleInstance) - - case GraphTypeValidate: - // The validate graph is just a slightly modified plan graph - fallthrough - case GraphTypePlan: - // Create the plan graph builder - p := &PlanGraphBuilder{ - Config: c.config, - State: c.state, - Components: c.components, - Schemas: c.schemas, - Targets: c.targets, - Validate: opts.Validate, - } - - // Some special cases for other graph types shared with plan currently - var b GraphBuilder = p - switch typ { - case GraphTypeValidate: - b = ValidateGraphBuilder(p) - } - - return b.Build(addrs.RootModuleInstance) - - case GraphTypePlanDestroy: - return (&DestroyPlanGraphBuilder{ - Config: c.config, - State: c.state, - Components: c.components, - Schemas: c.schemas, - Targets: c.targets, - Validate: opts.Validate, - }).Build(addrs.RootModuleInstance) - - case GraphTypeRefresh: - return (&RefreshGraphBuilder{ - Config: c.config, - State: c.state, - Components: c.components, - Schemas: c.schemas, - Targets: c.targets, - Validate: opts.Validate, - }).Build(addrs.RootModuleInstance) - - case GraphTypeEval: - return (&EvalGraphBuilder{ - Config: c.config, - State: c.state, - Components: c.components, - Schemas: c.schemas, - }).Build(addrs.RootModuleInstance) - - default: - // Should never happen, because the above is exhaustive for all graph types. - panic(fmt.Errorf("unsupported graph type %s", typ)) - } -} - -// ShadowError returns any errors caught during a shadow operation. -// -// A shadow operation is an operation run in parallel to a real operation -// that performs the same tasks using new logic on copied state. The results -// are compared to ensure that the new logic works the same as the old logic. -// The shadow never affects the real operation or return values. -// -// The result of the shadow operation are only available through this function -// call after a real operation is complete. -// -// For API consumers of Context, you can safely ignore this function -// completely if you have no interest in helping report experimental feature -// errors to Terraform maintainers. Otherwise, please call this function -// after every operation and report this to the user. -// -// IMPORTANT: Shadow errors are _never_ critical: they _never_ affect -// the real state or result of a real operation. They are purely informational -// to assist in future Terraform versions being more stable. Please message -// this effectively to the end user. -// -// This must be called only when no other operation is running (refresh, -// plan, etc.). The result can be used in parallel to any other operation -// running. -func (c *Context) ShadowError() error { - return c.shadowErr -} - -// State returns a copy of the current state associated with this context. -// -// This cannot safely be called in parallel with any other Context function. -func (c *Context) State() *states.State { - return c.state.DeepCopy() -} - -// Eval produces a scope in which expressions can be evaluated for -// the given module path. -// -// This method must first evaluate any ephemeral values (input variables, local -// values, and output values) in the configuration. These ephemeral values are -// not included in the persisted state, so they must be re-computed using other -// values in the state before they can be properly evaluated. The updated -// values are retained in the main state associated with the receiving context. -// -// This function takes no action against remote APIs but it does need access -// to all provider and provisioner instances in order to obtain their schemas -// for type checking. -// -// The result is an evaluation scope that can be used to resolve references -// against the root module. If the returned diagnostics contains errors then -// the returned scope may be nil. If it is not nil then it may still be used -// to attempt expression evaluation or other analysis, but some expressions -// may not behave as expected. -func (c *Context) Eval(path addrs.ModuleInstance) (*lang.Scope, tfdiags.Diagnostics) { - // This is intended for external callers such as the "terraform console" - // command. Internally, we create an evaluator in c.walk before walking - // the graph, and create scopes in ContextGraphWalker. - - var diags tfdiags.Diagnostics - defer c.acquireRun("eval")() - - // Start with a copy of state so that we don't affect any instances - // that other methods may have already returned. - c.state = c.state.DeepCopy() - var walker *ContextGraphWalker - - graph, graphDiags := c.Graph(GraphTypeEval, nil) - diags = diags.Append(graphDiags) - if !diags.HasErrors() { - var walkDiags tfdiags.Diagnostics - walker, walkDiags = c.walk(graph, walkEval) - diags = diags.Append(walker.NonFatalDiagnostics) - diags = diags.Append(walkDiags) - } - - if walker == nil { - // If we skipped walking the graph (due to errors) then we'll just - // use a placeholder graph walker here, which'll refer to the - // unmodified state. - walker = c.graphWalker(walkEval) - } - - // This is a bit weird since we don't normally evaluate outside of - // the context of a walk, but we'll "re-enter" our desired path here - // just to get hold of an EvalContext for it. GraphContextBuiltin - // caches its contexts, so we should get hold of the context that was - // previously used for evaluation here, unless we skipped walking. - evalCtx := walker.EnterPath(path) - return evalCtx.EvaluationScope(nil, EvalDataForNoInstanceKey), diags -} - -// Apply applies the changes represented by this context and returns -// the resulting state. -// -// Even in the case an error is returned, the state may be returned and will -// potentially be partially updated. In addition to returning the resulting -// state, this context is updated with the latest state. -// -// If the state is required after an error, the caller should call -// Context.State, rather than rely on the return value. -// -// TODO: Apply and Refresh should either always return a state, or rely on the -// State() method. Currently the helper/resource testing framework relies -// on the absence of a returned state to determine if Destroy can be -// called, so that will need to be refactored before this can be changed. -func (c *Context) Apply() (*states.State, tfdiags.Diagnostics) { - defer c.acquireRun("apply")() - - // Copy our own state - c.state = c.state.DeepCopy() - - // Build the graph. - graph, diags := c.Graph(GraphTypeApply, nil) - if diags.HasErrors() { - return nil, diags - } - - // Determine the operation - operation := walkApply - if c.destroy { - operation = walkDestroy - } - - // Walk the graph - walker, walkDiags := c.walk(graph, operation) - diags = diags.Append(walker.NonFatalDiagnostics) - diags = diags.Append(walkDiags) - - if c.destroy && !diags.HasErrors() { - // If we know we were trying to destroy objects anyway, and we - // completed without any errors, then we'll also prune out any - // leftover empty resource husks (left after all of the instances - // of a resource with "count" or "for_each" are destroyed) to - // help ensure we end up with an _actually_ empty state, assuming - // we weren't destroying with -target here. - // - // (This doesn't actually take into account -target, but that should - // be okay because it doesn't throw away anything we can't recompute - // on a subsequent "terraform plan" run, if the resources are still - // present in the configuration. However, this _will_ cause "count = 0" - // resources to read as unknown during the next refresh walk, which - // may cause some additional churn if used in a data resource or - // provider block, until we remove refreshing as a separate walk and - // just do it as part of the plan walk.) - c.state.PruneResourceHusks() - } - - if len(c.targets) > 0 { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Warning, - "Applied changes may be incomplete", - `The plan was created with the -target option in effect, so some changes requested in the configuration may have been ignored and the output values may not be fully updated. Run the following command to verify that no other changes are pending: - terraform plan - -Note that the -target option is not suitable for routine use, and is provided only for exceptional situations such as recovering from errors or mistakes, or when Terraform specifically suggests to use it as part of an error message.`, - )) - } - - return c.state, diags -} - -// Plan generates an execution plan for the given context. -// -// The execution plan encapsulates the context and can be stored -// in order to reinstantiate a context later for Apply. -// -// Plan also updates the diff of this context to be the diff generated -// by the plan, so Apply can be called after. -func (c *Context) Plan() (*plans.Plan, tfdiags.Diagnostics) { - defer c.acquireRun("plan")() - c.changes = plans.NewChanges() - - var diags tfdiags.Diagnostics - - if len(c.targets) > 0 { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Warning, - "Resource targeting is in effect", - `You are creating a plan with the -target option, which means that the result of this plan may not represent all of the changes requested by the current configuration. - -The -target option is not for routine use, and is provided only for exceptional situations such as recovering from errors or mistakes, or when Terraform specifically suggests to use it as part of an error message.`, - )) - } - - varVals := make(map[string]plans.DynamicValue, len(c.variables)) - for k, iv := range c.variables { - // We use cty.DynamicPseudoType here so that we'll save both the - // value _and_ its dynamic type in the plan, so we can recover - // exactly the same value later. - dv, err := plans.NewDynamicValue(iv.Value, cty.DynamicPseudoType) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to prepare variable value for plan", - fmt.Sprintf("The value for variable %q could not be serialized to store in the plan: %s.", k, err), - )) - continue - } - varVals[k] = dv - } - - p := &plans.Plan{ - VariableValues: varVals, - TargetAddrs: c.targets, - ProviderSHA256s: c.providerSHA256s, - } - - var operation walkOperation - if c.destroy { - operation = walkPlanDestroy - } else { - // Set our state to be something temporary. We do this so that - // the plan can update a fake state so that variables work, then - // we replace it back with our old state. - old := c.state - if old == nil { - c.state = states.NewState() - } else { - c.state = old.DeepCopy() - } - defer func() { - c.state = old - }() - - operation = walkPlan - } - - // Build the graph. - graphType := GraphTypePlan - if c.destroy { - graphType = GraphTypePlanDestroy - } - graph, graphDiags := c.Graph(graphType, nil) - diags = diags.Append(graphDiags) - if graphDiags.HasErrors() { - return nil, diags - } - - // Do the walk - walker, walkDiags := c.walk(graph, operation) - diags = diags.Append(walker.NonFatalDiagnostics) - diags = diags.Append(walkDiags) - if walkDiags.HasErrors() { - return nil, diags - } - p.Changes = c.changes - - return p, diags -} - -// Refresh goes through all the resources in the state and refreshes them -// to their latest state. This will update the state that this context -// works with, along with returning it. -// -// Even in the case an error is returned, the state may be returned and -// will potentially be partially updated. -func (c *Context) Refresh() (*states.State, tfdiags.Diagnostics) { - defer c.acquireRun("refresh")() - - // Copy our own state - c.state = c.state.DeepCopy() - - // Refresh builds a partial changeset as part of its work because it must - // create placeholder stubs for any resource instances that'll be created - // in subsequent plan so that provider configurations and data resources - // can interpolate from them. This plan is always thrown away after - // the operation completes, restoring any existing changeset. - oldChanges := c.changes - defer func() { c.changes = oldChanges }() - c.changes = plans.NewChanges() - - // Build the graph. - graph, diags := c.Graph(GraphTypeRefresh, nil) - if diags.HasErrors() { - return nil, diags - } - - // Do the walk - _, walkDiags := c.walk(graph, walkRefresh) - diags = diags.Append(walkDiags) - if walkDiags.HasErrors() { - return nil, diags - } - - // During our walk we will have created planned object placeholders in - // state for resource instances that are in configuration but not yet - // created. These were created only to allow expression evaluation to - // work properly in provider and data blocks during the walk and must - // now be discarded, since a subsequent plan walk is responsible for - // creating these "for real". - // TODO: Consolidate refresh and plan into a single walk, so that the - // refresh walk doesn't need to emulate various aspects of the plan - // walk in order to properly evaluate provider and data blocks. - c.state.SyncWrapper().RemovePlannedResourceInstanceObjects() - - return c.state, diags -} - -// Stop stops the running task. -// -// Stop will block until the task completes. -func (c *Context) Stop() { - log.Printf("[WARN] terraform: Stop called, initiating interrupt sequence") - - c.l.Lock() - defer c.l.Unlock() - - // If we're running, then stop - if c.runContextCancel != nil { - log.Printf("[WARN] terraform: run context exists, stopping") - - // Tell the hook we want to stop - c.sh.Stop() - - // Stop the context - c.runContextCancel() - c.runContextCancel = nil - } - - // Grab the condition var before we exit - if cond := c.runCond; cond != nil { - log.Printf("[INFO] terraform: waiting for graceful stop to complete") - cond.Wait() - } - - log.Printf("[WARN] terraform: stop complete") -} - -// Validate performs semantic validation of the configuration, and returning -// any warnings or errors. -// -// Syntax and structural checks are performed by the configuration loader, -// and so are not repeated here. -func (c *Context) Validate() tfdiags.Diagnostics { - defer c.acquireRun("validate")() - - var diags tfdiags.Diagnostics - - // Validate input variables. We do this only for the values supplied - // by the root module, since child module calls are validated when we - // visit their graph nodes. - if c.config != nil { - varDiags := checkInputVariables(c.config.Module.Variables, c.variables) - diags = diags.Append(varDiags) - } - - // If we have errors at this point then we probably won't be able to - // construct a graph without producing redundant errors, so we'll halt early. - if diags.HasErrors() { - return diags - } - - // Build the graph so we can walk it and run Validate on nodes. - // We also validate the graph generated here, but this graph doesn't - // necessarily match the graph that Plan will generate, so we'll validate the - // graph again later after Planning. - graph, graphDiags := c.Graph(GraphTypeValidate, nil) - diags = diags.Append(graphDiags) - if graphDiags.HasErrors() { - return diags - } - - // Walk - walker, walkDiags := c.walk(graph, walkValidate) - diags = diags.Append(walker.NonFatalDiagnostics) - diags = diags.Append(walkDiags) - if walkDiags.HasErrors() { - return diags - } - - return diags -} - -// Config returns the configuration tree associated with this context. -func (c *Context) Config() *configs.Config { - return c.config -} - -// Variables will return the mapping of variables that were defined -// for this Context. If Input was called, this mapping may be different -// than what was given. -func (c *Context) Variables() InputValues { - return c.variables -} - -// SetVariable sets a variable after a context has already been built. -func (c *Context) SetVariable(k string, v cty.Value) { - c.variables[k] = &InputValue{ - Value: v, - SourceType: ValueFromCaller, - } -} - -func (c *Context) acquireRun(phase string) func() { - // With the run lock held, grab the context lock to make changes - // to the run context. - c.l.Lock() - defer c.l.Unlock() - - // Wait until we're no longer running - for c.runCond != nil { - c.runCond.Wait() - } - - // Build our lock - c.runCond = sync.NewCond(&c.l) - - // Create a new run context - c.runContext, c.runContextCancel = context.WithCancel(context.Background()) - - // Reset the stop hook so we're not stopped - c.sh.Reset() - - // Reset the shadow errors - c.shadowErr = nil - - return c.releaseRun -} - -func (c *Context) releaseRun() { - // Grab the context lock so that we can make modifications to fields - c.l.Lock() - defer c.l.Unlock() - - // End our run. We check if runContext is non-nil because it can be - // set to nil if it was cancelled via Stop() - if c.runContextCancel != nil { - c.runContextCancel() - } - - // Unlock all waiting our condition - cond := c.runCond - c.runCond = nil - cond.Broadcast() - - // Unset the context - c.runContext = nil -} - -func (c *Context) walk(graph *Graph, operation walkOperation) (*ContextGraphWalker, tfdiags.Diagnostics) { - log.Printf("[DEBUG] Starting graph walk: %s", operation.String()) - - walker := c.graphWalker(operation) - - // Watch for a stop so we can call the provider Stop() API. - watchStop, watchWait := c.watchStop(walker) - - // Walk the real graph, this will block until it completes - diags := graph.Walk(walker) - - // Close the channel so the watcher stops, and wait for it to return. - close(watchStop) - <-watchWait - - return walker, diags -} - -func (c *Context) graphWalker(operation walkOperation) *ContextGraphWalker { - return &ContextGraphWalker{ - Context: c, - State: c.state.SyncWrapper(), - Changes: c.changes.SyncWrapper(), - Operation: operation, - StopContext: c.runContext, - RootVariableValues: c.variables, - } -} - -// watchStop immediately returns a `stop` and a `wait` chan after dispatching -// the watchStop goroutine. This will watch the runContext for cancellation and -// stop the providers accordingly. When the watch is no longer needed, the -// `stop` chan should be closed before waiting on the `wait` chan. -// The `wait` chan is important, because without synchronizing with the end of -// the watchStop goroutine, the runContext may also be closed during the select -// incorrectly causing providers to be stopped. Even if the graph walk is done -// at that point, stopping a provider permanently cancels its StopContext which -// can cause later actions to fail. -func (c *Context) watchStop(walker *ContextGraphWalker) (chan struct{}, <-chan struct{}) { - stop := make(chan struct{}) - wait := make(chan struct{}) - - // get the runContext cancellation channel now, because releaseRun will - // write to the runContext field. - done := c.runContext.Done() - - go func() { - defer close(wait) - // Wait for a stop or completion - select { - case <-done: - // done means the context was canceled, so we need to try and stop - // providers. - case <-stop: - // our own stop channel was closed. - return - } - - // If we're here, we're stopped, trigger the call. - log.Printf("[TRACE] Context: requesting providers and provisioners to gracefully stop") - - { - // Copy the providers so that a misbehaved blocking Stop doesn't - // completely hang Terraform. - walker.providerLock.Lock() - ps := make([]providers.Interface, 0, len(walker.providerCache)) - for _, p := range walker.providerCache { - ps = append(ps, p) - } - defer walker.providerLock.Unlock() - - for _, p := range ps { - // We ignore the error for now since there isn't any reasonable - // action to take if there is an error here, since the stop is still - // advisory: Terraform will exit once the graph node completes. - p.Stop() - } - } - - { - // Call stop on all the provisioners - walker.provisionerLock.Lock() - ps := make([]provisioners.Interface, 0, len(walker.provisionerCache)) - for _, p := range walker.provisionerCache { - ps = append(ps, p) - } - defer walker.provisionerLock.Unlock() - - for _, p := range ps { - // We ignore the error for now since there isn't any reasonable - // action to take if there is an error here, since the stop is still - // advisory: Terraform will exit once the graph node completes. - p.Stop() - } - } - }() - - return stop, wait -} - -// ShimLegacyState is a helper that takes the legacy state type and -// converts it to the new state type. -// -// This is implemented as a state file upgrade, so it will not preserve -// parts of the state structure that are not included in a serialized state, -// such as the resolved results of any local values, outputs in non-root -// modules, etc. -func ShimLegacyState(legacy *State) (*states.State, error) { - if legacy == nil { - return nil, nil - } - var buf bytes.Buffer - err := WriteState(legacy, &buf) - if err != nil { - return nil, err - } - f, err := statefile.Read(&buf) - if err != nil { - return nil, err - } - return f.State, err -} - -// MustShimLegacyState is a wrapper around ShimLegacyState that panics if -// the conversion does not succeed. This is primarily intended for tests where -// the given legacy state is an object constructed within the test. -func MustShimLegacyState(legacy *State) *states.State { - ret, err := ShimLegacyState(legacy) - if err != nil { - panic(err) - } - return ret -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/context_components.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/context_components.go deleted file mode 100644 index a627996e..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/context_components.go +++ /dev/null @@ -1,68 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/provisioners" -) - -// contextComponentFactory is the interface that Context uses -// to initialize various components such as providers and provisioners. -// This factory gets more information than the raw maps using to initialize -// a Context. This information is used for debugging. -type contextComponentFactory interface { - // ResourceProvider creates a new ResourceProvider with the given - // type. The "uid" is a unique identifier for this provider being - // initialized that can be used for internal tracking. - ResourceProvider(typ, uid string) (providers.Interface, error) - ResourceProviders() []string - - // ResourceProvisioner creates a new ResourceProvisioner with the - // given type. The "uid" is a unique identifier for this provisioner - // being initialized that can be used for internal tracking. - ResourceProvisioner(typ, uid string) (provisioners.Interface, error) - ResourceProvisioners() []string -} - -// basicComponentFactory just calls a factory from a map directly. -type basicComponentFactory struct { - providers map[string]providers.Factory - provisioners map[string]ProvisionerFactory -} - -func (c *basicComponentFactory) ResourceProviders() []string { - result := make([]string, len(c.providers)) - for k := range c.providers { - result = append(result, k) - } - - return result -} - -func (c *basicComponentFactory) ResourceProvisioners() []string { - result := make([]string, len(c.provisioners)) - for k := range c.provisioners { - result = append(result, k) - } - - return result -} - -func (c *basicComponentFactory) ResourceProvider(typ, uid string) (providers.Interface, error) { - f, ok := c.providers[typ] - if !ok { - return nil, fmt.Errorf("unknown provider %q", typ) - } - - return f() -} - -func (c *basicComponentFactory) ResourceProvisioner(typ, uid string) (provisioners.Interface, error) { - f, ok := c.provisioners[typ] - if !ok { - return nil, fmt.Errorf("unknown provisioner %q", typ) - } - - return f() -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/context_graph_type.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/context_graph_type.go deleted file mode 100644 index 4448d870..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/context_graph_type.go +++ /dev/null @@ -1,32 +0,0 @@ -package terraform - -//go:generate go run golang.org/x/tools/cmd/stringer -type=GraphType context_graph_type.go - -// GraphType is an enum of the type of graph to create with a Context. -// The values of the constants may change so they shouldn't be depended on; -// always use the constant name. -type GraphType byte - -const ( - GraphTypeInvalid GraphType = 0 - GraphTypeLegacy GraphType = iota - GraphTypeRefresh - GraphTypePlan - GraphTypePlanDestroy - GraphTypeApply - GraphTypeValidate - GraphTypeEval // only visits in-memory elements such as variables, locals, and outputs. -) - -// GraphTypeMap is a mapping of human-readable string to GraphType. This -// is useful to use as the mechanism for human input for configurable -// graph types. -var GraphTypeMap = map[string]GraphType{ - "apply": GraphTypeApply, - "plan": GraphTypePlan, - "plan-destroy": GraphTypePlanDestroy, - "refresh": GraphTypeRefresh, - "legacy": GraphTypeLegacy, - "validate": GraphTypeValidate, - "eval": GraphTypeEval, -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/context_import.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/context_import.go deleted file mode 100644 index 9a9cd962..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/context_import.go +++ /dev/null @@ -1,83 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// ImportOpts are used as the configuration for Import. -type ImportOpts struct { - // Targets are the targets to import - Targets []*ImportTarget - - // Config is optional, and specifies a config tree that will be loaded - // into the graph and evaluated. This is the source for provider - // configurations. - Config *configs.Config -} - -// ImportTarget is a single resource to import. -type ImportTarget struct { - // Addr is the address for the resource instance that the new object should - // be imported into. - Addr addrs.AbsResourceInstance - - // ID is the ID of the resource to import. This is resource-specific. - ID string - - // ProviderAddr is the address of the provider that should handle the import. - ProviderAddr addrs.AbsProviderConfig -} - -// Import takes already-created external resources and brings them -// under Terraform management. Import requires the exact type, name, and ID -// of the resources to import. -// -// This operation is idempotent. If the requested resource is already -// imported, no changes are made to the state. -// -// Further, this operation also gracefully handles partial state. If during -// an import there is a failure, all previously imported resources remain -// imported. -func (c *Context) Import(opts *ImportOpts) (*states.State, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - // Hold a lock since we can modify our own state here - defer c.acquireRun("import")() - - // Copy our own state - c.state = c.state.DeepCopy() - - // If no module is given, default to the module configured with - // the Context. - config := opts.Config - if config == nil { - config = c.config - } - - // Initialize our graph builder - builder := &ImportGraphBuilder{ - ImportTargets: opts.Targets, - Config: config, - Components: c.components, - Schemas: c.schemas, - } - - // Build the graph! - graph, graphDiags := builder.Build(addrs.RootModuleInstance) - diags = diags.Append(graphDiags) - if graphDiags.HasErrors() { - return c.state, diags - } - - // Walk it - _, walkDiags := c.walk(graph, walkImport) - diags = diags.Append(walkDiags) - if walkDiags.HasErrors() { - return c.state, diags - } - - return c.state, diags -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/context_input.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/context_input.go deleted file mode 100644 index b99f1afa..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/context_input.go +++ /dev/null @@ -1,251 +0,0 @@ -package terraform - -import ( - "context" - "fmt" - "log" - "sort" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// Input asks for input to fill variables and provider configurations. -// This modifies the configuration in-place, so asking for Input twice -// may result in different UI output showing different current values. -func (c *Context) Input(mode InputMode) tfdiags.Diagnostics { - var diags tfdiags.Diagnostics - defer c.acquireRun("input")() - - if c.uiInput == nil { - log.Printf("[TRACE] Context.Input: uiInput is nil, so skipping") - return diags - } - - ctx := context.Background() - - if mode&InputModeVar != 0 { - log.Printf("[TRACE] Context.Input: Prompting for variables") - - // Walk the variables first for the root module. We walk them in - // alphabetical order for UX reasons. - configs := c.config.Module.Variables - names := make([]string, 0, len(configs)) - for name := range configs { - names = append(names, name) - } - sort.Strings(names) - Variables: - for _, n := range names { - v := configs[n] - - // If we only care about unset variables, then we should set any - // variable that is already set. - if mode&InputModeVarUnset != 0 { - if _, isSet := c.variables[n]; isSet { - continue - } - } - - // this should only happen during tests - if c.uiInput == nil { - log.Println("[WARN] Context.uiInput is nil during input walk") - continue - } - - // Ask the user for a value for this variable - var rawValue string - retry := 0 - for { - var err error - rawValue, err = c.uiInput.Input(ctx, &InputOpts{ - Id: fmt.Sprintf("var.%s", n), - Query: fmt.Sprintf("var.%s", n), - Description: v.Description, - }) - if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to request interactive input", - fmt.Sprintf("Terraform attempted to request a value for var.%s interactively, but encountered an error: %s.", n, err), - )) - return diags - } - - if rawValue == "" && v.Default == cty.NilVal { - // Redo if it is required, but abort if we keep getting - // blank entries - if retry > 2 { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Required variable not assigned", - fmt.Sprintf("The variable %q is required, so Terraform cannot proceed without a defined value for it.", n), - )) - continue Variables - } - retry++ - continue - } - - break - } - - val, valDiags := v.ParsingMode.Parse(n, rawValue) - diags = diags.Append(valDiags) - if diags.HasErrors() { - continue - } - - c.variables[n] = &InputValue{ - Value: val, - SourceType: ValueFromInput, - } - } - } - - if mode&InputModeProvider != 0 { - log.Printf("[TRACE] Context.Input: Prompting for provider arguments") - - // We prompt for input only for provider configurations defined in - // the root module. At the time of writing that is an arbitrary - // restriction, but we have future plans to support "count" and - // "for_each" on modules that will then prevent us from supporting - // input for child module configurations anyway (since we'd need to - // dynamic-expand first), and provider configurations in child modules - // are not recommended since v0.11 anyway, so this restriction allows - // us to keep this relatively simple without significant hardship. - - pcs := make(map[string]*configs.Provider) - pas := make(map[string]addrs.ProviderConfig) - for _, pc := range c.config.Module.ProviderConfigs { - addr := pc.Addr() - pcs[addr.String()] = pc - pas[addr.String()] = addr - log.Printf("[TRACE] Context.Input: Provider %s declared at %s", addr, pc.DeclRange) - } - // We also need to detect _implied_ provider configs from resources. - // These won't have *configs.Provider objects, but they will still - // exist in the map and we'll just treat them as empty below. - for _, rc := range c.config.Module.ManagedResources { - pa := rc.ProviderConfigAddr() - if pa.Alias != "" { - continue // alias configurations cannot be implied - } - if _, exists := pcs[pa.String()]; !exists { - pcs[pa.String()] = nil - pas[pa.String()] = pa - log.Printf("[TRACE] Context.Input: Provider %s implied by resource block at %s", pa, rc.DeclRange) - } - } - for _, rc := range c.config.Module.DataResources { - pa := rc.ProviderConfigAddr() - if pa.Alias != "" { - continue // alias configurations cannot be implied - } - if _, exists := pcs[pa.String()]; !exists { - pcs[pa.String()] = nil - pas[pa.String()] = pa - log.Printf("[TRACE] Context.Input: Provider %s implied by data block at %s", pa, rc.DeclRange) - } - } - - for pk, pa := range pas { - pc := pcs[pk] // will be nil if this is an implied config - - // Wrap the input into a namespace - input := &PrefixUIInput{ - IdPrefix: pk, - QueryPrefix: pk + ".", - UIInput: c.uiInput, - } - - schema := c.schemas.ProviderConfig(pa.Type) - if schema == nil { - // Could either be an incorrect config or just an incomplete - // mock in tests. We'll let a later pass decide, and just - // ignore this for the purposes of gathering input. - log.Printf("[TRACE] Context.Input: No schema available for provider type %q", pa.Type) - continue - } - - // For our purposes here we just want to detect if attrbutes are - // set in config at all, so rather than doing a full decode - // (which would require us to prepare an evalcontext, etc) we'll - // use the low-level HCL API to process only the top-level - // structure. - var attrExprs hcl.Attributes // nil if there is no config - if pc != nil && pc.Config != nil { - lowLevelSchema := schemaForInputSniffing(hcldec.ImpliedSchema(schema.DecoderSpec())) - content, _, diags := pc.Config.PartialContent(lowLevelSchema) - if diags.HasErrors() { - log.Printf("[TRACE] Context.Input: %s has decode error, so ignoring: %s", pa, diags.Error()) - continue - } - attrExprs = content.Attributes - } - - keys := make([]string, 0, len(schema.Attributes)) - for key := range schema.Attributes { - keys = append(keys, key) - } - sort.Strings(keys) - - vals := map[string]cty.Value{} - for _, key := range keys { - attrS := schema.Attributes[key] - if attrS.Optional { - continue - } - if attrExprs != nil { - if _, exists := attrExprs[key]; exists { - continue - } - } - if !attrS.Type.Equals(cty.String) { - continue - } - - log.Printf("[TRACE] Context.Input: Prompting for %s argument %s", pa, key) - rawVal, err := input.Input(ctx, &InputOpts{ - Id: key, - Query: key, - Description: attrS.Description, - }) - if err != nil { - log.Printf("[TRACE] Context.Input: Failed to prompt for %s argument %s: %s", pa, key, err) - continue - } - - vals[key] = cty.StringVal(rawVal) - } - - c.providerInputConfig[pk] = vals - log.Printf("[TRACE] Context.Input: Input for %s: %#v", pk, vals) - } - } - - return diags -} - -// schemaForInputSniffing returns a transformed version of a given schema -// that marks all attributes as optional, which the Context.Input method can -// use to detect whether a required argument is set without missing arguments -// themselves generating errors. -func schemaForInputSniffing(schema *hcl.BodySchema) *hcl.BodySchema { - ret := &hcl.BodySchema{ - Attributes: make([]hcl.AttributeSchema, len(schema.Attributes)), - Blocks: schema.Blocks, - } - - for i, attrS := range schema.Attributes { - ret.Attributes[i] = attrS - ret.Attributes[i].Required = false - } - - return ret -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/diff.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/diff.go index e2f54883..c7d82cf3 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/diff.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/diff.go @@ -1,8 +1,6 @@ package terraform import ( - "bufio" - "bytes" "fmt" "log" "reflect" @@ -12,375 +10,27 @@ import ( "strings" "sync" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim" - "github.com/zclconf/go-cty/cty" + "github.com/hashicorp/go-cty/cty" - "github.com/mitchellh/copystructure" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/hcl2shim" ) -// DiffChangeType is an enum with the kind of changes a diff has planned. -type DiffChangeType byte +// diffChangeType is an enum with the kind of changes a diff has planned. +type diffChangeType byte const ( - DiffInvalid DiffChangeType = iota - DiffNone - DiffCreate - DiffUpdate - DiffDestroy - DiffDestroyCreate - - // DiffRefresh is only used in the UI for displaying diffs. - // Managed resource reads never appear in plan, and when data source - // reads appear they are represented as DiffCreate in core before - // transforming to DiffRefresh in the UI layer. - DiffRefresh // TODO: Actually use DiffRefresh in core too, for less confusion + diffInvalid diffChangeType = iota + diffNone + diffCreate + diffUpdate + diffDestroy + diffDestroyCreate ) // multiVal matches the index key to a flatmapped set, list or map var multiVal = regexp.MustCompile(`\.(#|%)$`) -// Diff tracks the changes that are necessary to apply a configuration -// to an existing infrastructure. -type Diff struct { - // Modules contains all the modules that have a diff - Modules []*ModuleDiff -} - -// Prune cleans out unused structures in the diff without affecting -// the behavior of the diff at all. -// -// This is not safe to call concurrently. This is safe to call on a -// nil Diff. -func (d *Diff) Prune() { - if d == nil { - return - } - - // Prune all empty modules - newModules := make([]*ModuleDiff, 0, len(d.Modules)) - for _, m := range d.Modules { - // If the module isn't empty, we keep it - if !m.Empty() { - newModules = append(newModules, m) - } - } - if len(newModules) == 0 { - newModules = nil - } - d.Modules = newModules -} - -// AddModule adds the module with the given path to the diff. -// -// This should be the preferred method to add module diffs since it -// allows us to optimize lookups later as well as control sorting. -func (d *Diff) AddModule(path addrs.ModuleInstance) *ModuleDiff { - // Lower the new-style address into a legacy-style address. - // This requires that none of the steps have instance keys, which is - // true for all addresses at the time of implementing this because - // "count" and "for_each" are not yet implemented for modules. - legacyPath := make([]string, len(path)) - for i, step := range path { - if step.InstanceKey != addrs.NoKey { - // FIXME: Once the rest of Terraform is ready to use count and - // for_each, remove all of this and just write the addrs.ModuleInstance - // value itself into the ModuleState. - panic("diff cannot represent modules with count or for_each keys") - } - - legacyPath[i] = step.Name - } - - m := &ModuleDiff{Path: legacyPath} - m.init() - d.Modules = append(d.Modules, m) - return m -} - -// ModuleByPath is used to lookup the module diff for the given path. -// This should be the preferred lookup mechanism as it allows for future -// lookup optimizations. -func (d *Diff) ModuleByPath(path addrs.ModuleInstance) *ModuleDiff { - if d == nil { - return nil - } - for _, mod := range d.Modules { - if mod.Path == nil { - panic("missing module path") - } - modPath := normalizeModulePath(mod.Path) - if modPath.String() == path.String() { - return mod - } - } - return nil -} - -// RootModule returns the ModuleState for the root module -func (d *Diff) RootModule() *ModuleDiff { - root := d.ModuleByPath(addrs.RootModuleInstance) - if root == nil { - panic("missing root module") - } - return root -} - -// Empty returns true if the diff has no changes. -func (d *Diff) Empty() bool { - if d == nil { - return true - } - - for _, m := range d.Modules { - if !m.Empty() { - return false - } - } - - return true -} - -// Equal compares two diffs for exact equality. -// -// This is different from the Same comparison that is supported which -// checks for operation equality taking into account computed values. Equal -// instead checks for exact equality. -func (d *Diff) Equal(d2 *Diff) bool { - // If one is nil, they must both be nil - if d == nil || d2 == nil { - return d == d2 - } - - // Sort the modules - sort.Sort(moduleDiffSort(d.Modules)) - sort.Sort(moduleDiffSort(d2.Modules)) - - // Copy since we have to modify the module destroy flag to false so - // we don't compare that. TODO: delete this when we get rid of the - // destroy flag on modules. - dCopy := d.DeepCopy() - d2Copy := d2.DeepCopy() - for _, m := range dCopy.Modules { - m.Destroy = false - } - for _, m := range d2Copy.Modules { - m.Destroy = false - } - - // Use DeepEqual - return reflect.DeepEqual(dCopy, d2Copy) -} - -// DeepCopy performs a deep copy of all parts of the Diff, making the -// resulting Diff safe to use without modifying this one. -func (d *Diff) DeepCopy() *Diff { - copy, err := copystructure.Config{Lock: true}.Copy(d) - if err != nil { - panic(err) - } - - return copy.(*Diff) -} - -func (d *Diff) String() string { - var buf bytes.Buffer - - keys := make([]string, 0, len(d.Modules)) - lookup := make(map[string]*ModuleDiff) - for _, m := range d.Modules { - addr := normalizeModulePath(m.Path) - key := addr.String() - keys = append(keys, key) - lookup[key] = m - } - sort.Strings(keys) - - for _, key := range keys { - m := lookup[key] - mStr := m.String() - - // If we're the root module, we just write the output directly. - if reflect.DeepEqual(m.Path, rootModulePath) { - buf.WriteString(mStr + "\n") - continue - } - - buf.WriteString(fmt.Sprintf("%s:\n", key)) - - s := bufio.NewScanner(strings.NewReader(mStr)) - for s.Scan() { - buf.WriteString(fmt.Sprintf(" %s\n", s.Text())) - } - } - - return strings.TrimSpace(buf.String()) -} - -// ModuleDiff tracks the differences between resources to apply within -// a single module. -type ModuleDiff struct { - Path []string - Resources map[string]*InstanceDiff - Destroy bool // Set only by the destroy plan -} - -func (d *ModuleDiff) init() { - if d.Resources == nil { - d.Resources = make(map[string]*InstanceDiff) - } - for _, r := range d.Resources { - r.init() - } -} - -// ChangeType returns the type of changes that the diff for this -// module includes. -// -// At a module level, this will only be DiffNone, DiffUpdate, DiffDestroy, or -// DiffCreate. If an instance within the module has a DiffDestroyCreate -// then this will register as a DiffCreate for a module. -func (d *ModuleDiff) ChangeType() DiffChangeType { - result := DiffNone - for _, r := range d.Resources { - change := r.ChangeType() - switch change { - case DiffCreate, DiffDestroy: - if result == DiffNone { - result = change - } - case DiffDestroyCreate, DiffUpdate: - result = DiffUpdate - } - } - - return result -} - -// Empty returns true if the diff has no changes within this module. -func (d *ModuleDiff) Empty() bool { - if d.Destroy { - return false - } - - if len(d.Resources) == 0 { - return true - } - - for _, rd := range d.Resources { - if !rd.Empty() { - return false - } - } - - return true -} - -// Instances returns the instance diffs for the id given. This can return -// multiple instance diffs if there are counts within the resource. -func (d *ModuleDiff) Instances(id string) []*InstanceDiff { - var result []*InstanceDiff - for k, diff := range d.Resources { - if k == id || strings.HasPrefix(k, id+".") { - if !diff.Empty() { - result = append(result, diff) - } - } - } - - return result -} - -// IsRoot says whether or not this module diff is for the root module. -func (d *ModuleDiff) IsRoot() bool { - return reflect.DeepEqual(d.Path, rootModulePath) -} - -// String outputs the diff in a long but command-line friendly output -// format that users can read to quickly inspect a diff. -func (d *ModuleDiff) String() string { - var buf bytes.Buffer - - names := make([]string, 0, len(d.Resources)) - for name, _ := range d.Resources { - names = append(names, name) - } - sort.Strings(names) - - for _, name := range names { - rdiff := d.Resources[name] - - crud := "UPDATE" - switch { - case rdiff.RequiresNew() && (rdiff.GetDestroy() || rdiff.GetDestroyTainted()): - crud = "DESTROY/CREATE" - case rdiff.GetDestroy() || rdiff.GetDestroyDeposed(): - crud = "DESTROY" - case rdiff.RequiresNew(): - crud = "CREATE" - } - - extra := "" - if !rdiff.GetDestroy() && rdiff.GetDestroyDeposed() { - extra = " (deposed only)" - } - - buf.WriteString(fmt.Sprintf( - "%s: %s%s\n", - crud, - name, - extra)) - - keyLen := 0 - rdiffAttrs := rdiff.CopyAttributes() - keys := make([]string, 0, len(rdiffAttrs)) - for key, _ := range rdiffAttrs { - if key == "id" { - continue - } - - keys = append(keys, key) - if len(key) > keyLen { - keyLen = len(key) - } - } - sort.Strings(keys) - - for _, attrK := range keys { - attrDiff, _ := rdiff.GetAttribute(attrK) - - v := attrDiff.New - u := attrDiff.Old - if attrDiff.NewComputed { - v = "" - } - - if attrDiff.Sensitive { - u = "" - v = "" - } - - updateMsg := "" - if attrDiff.RequiresNew { - updateMsg = " (forces new resource)" - } else if attrDiff.Sensitive { - updateMsg = " (attribute changed)" - } - - buf.WriteString(fmt.Sprintf( - " %s:%s %#v => %#v%s\n", - attrK, - strings.Repeat(" ", keyLen-len(attrK)), - u, - v, - updateMsg)) - } - } - - return buf.String() -} - // InstanceDiff is the diff of a resource from some state to another. type InstanceDiff struct { mu sync.Mutex @@ -955,12 +605,7 @@ type ResourceAttrDiff struct { NewExtra interface{} // Extra information for the provider RequiresNew bool // True if change requires new resource Sensitive bool // True if the data should not be displayed in UI output - Type DiffAttrType -} - -// Empty returns true if the diff for this attr is neutral -func (d *ResourceAttrDiff) Empty() bool { - return d.Old == d.New && !d.NewComputed && !d.NewRemoved + Type diffAttrType } func (d *ResourceAttrDiff) GoString() string { @@ -972,57 +617,32 @@ func (d *ResourceAttrDiff) GoString() string { // output attribute (comes as a result of applying the configuration). An // example input would be "ami" for AWS and an example output would be // "private_ip". -type DiffAttrType byte - -const ( - DiffAttrUnknown DiffAttrType = iota - DiffAttrInput - DiffAttrOutput -) - -func (d *InstanceDiff) init() { - if d.Attributes == nil { - d.Attributes = make(map[string]*ResourceAttrDiff) - } -} +type diffAttrType byte func NewInstanceDiff() *InstanceDiff { return &InstanceDiff{Attributes: make(map[string]*ResourceAttrDiff)} } -func (d *InstanceDiff) Copy() (*InstanceDiff, error) { - if d == nil { - return nil, nil - } - - dCopy, err := copystructure.Config{Lock: true}.Copy(d) - if err != nil { - return nil, err - } - - return dCopy.(*InstanceDiff), nil -} - -// ChangeType returns the DiffChangeType represented by the diff +// ChangeType returns the diffChangeType represented by the diff // for this single instance. -func (d *InstanceDiff) ChangeType() DiffChangeType { +func (d *InstanceDiff) ChangeType() diffChangeType { if d.Empty() { - return DiffNone + return diffNone } if d.RequiresNew() && (d.GetDestroy() || d.GetDestroyTainted()) { - return DiffDestroyCreate + return diffDestroyCreate } if d.GetDestroy() || d.GetDestroyDeposed() { - return DiffDestroy + return diffDestroy } if d.RequiresNew() { - return DiffCreate + return diffCreate } - return DiffUpdate + return diffUpdate } // Empty returns true if this diff encapsulates no changes. @@ -1044,6 +664,7 @@ func (d *InstanceDiff) Empty() bool { // This is different from the Same comparison that is supported which // checks for operation equality taking into account computed values. Equal // instead checks for exact equality. +// TODO: investigate why removing this unused method causes panic in tests func (d *InstanceDiff) Equal(d2 *InstanceDiff) bool { // If one is nil, they must both be nil if d == nil || d2 == nil { @@ -1054,16 +675,6 @@ func (d *InstanceDiff) Equal(d2 *InstanceDiff) bool { return reflect.DeepEqual(d, d2) } -// DeepCopy performs a deep copy of all parts of the InstanceDiff -func (d *InstanceDiff) DeepCopy() *InstanceDiff { - copy, err := copystructure.Config{Lock: true}.Copy(d) - if err != nil { - panic(err) - } - - return copy.(*InstanceDiff) -} - func (d *InstanceDiff) GoString() string { return fmt.Sprintf("*%#v", InstanceDiff{ Attributes: d.Attributes, @@ -1111,23 +722,6 @@ func (d *InstanceDiff) GetDestroyDeposed() bool { return d.DestroyDeposed } -func (d *InstanceDiff) SetDestroyDeposed(b bool) { - d.mu.Lock() - defer d.mu.Unlock() - - d.DestroyDeposed = b -} - -// These methods are properly locked, for use outside other InstanceDiff -// methods but everywhere else within the terraform package. -// TODO refactor the locking scheme -func (d *InstanceDiff) SetTainted(b bool) { - d.mu.Lock() - defer d.mu.Unlock() - - d.DestroyTainted = b -} - func (d *InstanceDiff) GetDestroyTainted() bool { d.mu.Lock() defer d.mu.Unlock() @@ -1135,13 +729,6 @@ func (d *InstanceDiff) GetDestroyTainted() bool { return d.DestroyTainted } -func (d *InstanceDiff) SetDestroy(b bool) { - d.mu.Lock() - defer d.mu.Unlock() - - d.Destroy = b -} - func (d *InstanceDiff) GetDestroy() bool { d.mu.Lock() defer d.mu.Unlock() @@ -1149,20 +736,6 @@ func (d *InstanceDiff) GetDestroy() bool { return d.Destroy } -func (d *InstanceDiff) SetAttribute(key string, attr *ResourceAttrDiff) { - d.mu.Lock() - defer d.mu.Unlock() - - d.Attributes[key] = attr -} - -func (d *InstanceDiff) DelAttribute(key string) { - d.mu.Lock() - defer d.mu.Unlock() - - delete(d.Attributes, key) -} - func (d *InstanceDiff) GetAttribute(key string) (*ResourceAttrDiff, bool) { d.mu.Lock() defer d.mu.Unlock() @@ -1170,12 +743,6 @@ func (d *InstanceDiff) GetAttribute(key string) (*ResourceAttrDiff, bool) { attr, ok := d.Attributes[key] return attr, ok } -func (d *InstanceDiff) GetAttributesLen() int { - d.mu.Lock() - defer d.mu.Unlock() - - return len(d.Attributes) -} // Safely copies the Attributes map func (d *InstanceDiff) CopyAttributes() map[string]*ResourceAttrDiff { @@ -1242,7 +809,7 @@ func (d *InstanceDiff) Same(d2 *InstanceDiff) (bool, string) { // Found it! Ignore all of these. The prefix here is stripping // off the "%" so it is just "k." prefix := k[:len(k)-1] - for k2, _ := range d.Attributes { + for k2 := range d.Attributes { if strings.HasPrefix(k2, prefix) { ignoreAttrs[k2] = struct{}{} } @@ -1282,17 +849,17 @@ func (d *InstanceDiff) Same(d2 *InstanceDiff) (bool, string) { // same attributes. To start, build up the check map to be all the keys. checkOld := make(map[string]struct{}) checkNew := make(map[string]struct{}) - for k, _ := range d.Attributes { + for k := range d.Attributes { checkOld[k] = struct{}{} } - for k, _ := range d2.CopyAttributes() { + for k := range d2.CopyAttributes() { checkNew[k] = struct{}{} } // Make an ordered list so we are sure the approximated hashes are left // to process at the end of the loop keys := make([]string, 0, len(d.Attributes)) - for k, _ := range d.Attributes { + for k := range d.Attributes { keys = append(keys, k) } sort.StringSlice(keys).Sort() @@ -1350,7 +917,7 @@ func (d *InstanceDiff) Same(d2 *InstanceDiff) (bool, string) { return false, fmt.Sprintf("regexp failed to compile; err: %#v", err) } - for k2, _ := range checkNew { + for k2 := range checkNew { if re.MatchString(k2) { delete(checkNew, k2) } @@ -1387,12 +954,12 @@ func (d *InstanceDiff) Same(d2 *InstanceDiff) (bool, string) { // This is a computed list, set, or map, so remove any keys with // this prefix from the check list. kprefix := k[:len(k)-matchLen] - for k2, _ := range checkOld { + for k2 := range checkOld { if strings.HasPrefix(k2, kprefix) { delete(checkOld, k2) } } - for k2, _ := range checkNew { + for k2 := range checkNew { if strings.HasPrefix(k2, kprefix) { delete(checkNew, k2) } @@ -1412,7 +979,7 @@ func (d *InstanceDiff) Same(d2 *InstanceDiff) (bool, string) { // Check for leftover attributes if len(checkNew) > 0 { extras := make([]string, 0, len(checkNew)) - for attr, _ := range checkNew { + for attr := range checkNew { extras = append(extras, attr) } return false, @@ -1421,21 +988,3 @@ func (d *InstanceDiff) Same(d2 *InstanceDiff) (bool, string) { return true, "" } - -// moduleDiffSort implements sort.Interface to sort module diffs by path. -type moduleDiffSort []*ModuleDiff - -func (s moduleDiffSort) Len() int { return len(s) } -func (s moduleDiffSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s moduleDiffSort) Less(i, j int) bool { - a := s[i] - b := s[j] - - // If the lengths are different, then the shorter one always wins - if len(a.Path) != len(b.Path) { - return len(a.Path) < len(b.Path) - } - - // Otherwise, compare lexically - return strings.Join(a.Path, ".") < strings.Join(b.Path, ".") -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/diff_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/diff_test.go new file mode 100644 index 00000000..81235f7c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/diff_test.go @@ -0,0 +1,858 @@ +package terraform + +import ( + "fmt" + "strconv" + "testing" +) + +func TestInstanceDiff_ChangeType(t *testing.T) { + cases := []struct { + diff *InstanceDiff + Result diffChangeType + }{ + { + &InstanceDiff{}, + diffNone, + }, + { + &InstanceDiff{Destroy: true}, + diffDestroy, + }, + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": { + Old: "", + New: "bar", + }, + }, + }, + diffUpdate, + }, + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": { + Old: "", + New: "bar", + RequiresNew: true, + }, + }, + }, + diffCreate, + }, + { + &InstanceDiff{ + Destroy: true, + Attributes: map[string]*ResourceAttrDiff{ + "foo": { + Old: "", + New: "bar", + RequiresNew: true, + }, + }, + }, + diffDestroyCreate, + }, + { + &InstanceDiff{ + DestroyTainted: true, + Attributes: map[string]*ResourceAttrDiff{ + "foo": { + Old: "", + New: "bar", + RequiresNew: true, + }, + }, + }, + diffDestroyCreate, + }, + } + + for i, tc := range cases { + actual := tc.diff.ChangeType() + if actual != tc.Result { + t.Fatalf("%d: %#v", i, actual) + } + } +} + +func TestInstanceDiff_Empty(t *testing.T) { + var rd *InstanceDiff + + if !rd.Empty() { + t.Fatal("should be empty") + } + + rd = new(InstanceDiff) + + if !rd.Empty() { + t.Fatal("should be empty") + } + + rd = &InstanceDiff{Destroy: true} + + if rd.Empty() { + t.Fatal("should not be empty") + } + + rd = &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": { + New: "bar", + }, + }, + } + + if rd.Empty() { + t.Fatal("should not be empty") + } +} + +func TestInstanceDiff_RequiresNew(t *testing.T) { + rd := &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": {}, + }, + } + + if rd.RequiresNew() { + t.Fatal("should not require new") + } + + rd.Attributes["foo"].RequiresNew = true + + if !rd.RequiresNew() { + t.Fatal("should require new") + } +} + +func TestInstanceDiff_RequiresNew_nil(t *testing.T) { + var rd *InstanceDiff + + if rd.RequiresNew() { + t.Fatal("should not require new") + } +} + +func TestInstanceDiffSame(t *testing.T) { + cases := []struct { + One, Two *InstanceDiff + Same bool + Reason string + }{ + { + &InstanceDiff{}, + &InstanceDiff{}, + true, + "", + }, + + { + nil, + nil, + true, + "", + }, + + { + &InstanceDiff{Destroy: false}, + &InstanceDiff{Destroy: true}, + false, + "diff: Destroy; old: false, new: true", + }, + + { + &InstanceDiff{Destroy: true}, + &InstanceDiff{Destroy: true}, + true, + "", + }, + + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": {}, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": {}, + }, + }, + true, + "", + }, + + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "bar": {}, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": {}, + }, + }, + false, + "attribute mismatch: bar", + }, + + // Extra attributes + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": {}, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": {}, + "bar": {}, + }, + }, + false, + "extra attributes: bar", + }, + + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": {RequiresNew: true}, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": {RequiresNew: false}, + }, + }, + false, + "diff RequiresNew; old: true, new: false", + }, + + // NewComputed on primitive + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": { + Old: "", + New: "${var.foo}", + NewComputed: true, + }, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": { + Old: "0", + New: "1", + }, + }, + }, + true, + "", + }, + + // NewComputed on primitive, removed + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": { + Old: "", + New: "${var.foo}", + NewComputed: true, + }, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{}, + }, + true, + "", + }, + + // NewComputed on set, removed + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.#": { + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.1": { + Old: "foo", + New: "", + NewRemoved: true, + }, + "foo.2": { + Old: "", + New: "bar", + }, + }, + }, + true, + "", + }, + + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.#": {NewComputed: true}, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.#": { + Old: "0", + New: "1", + }, + "foo.0": { + Old: "", + New: "12", + }, + }, + }, + true, + "", + }, + + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.#": { + Old: "0", + New: "1", + }, + "foo.~35964334.bar": { + Old: "", + New: "${var.foo}", + }, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.#": { + Old: "0", + New: "1", + }, + "foo.87654323.bar": { + Old: "", + New: "12", + }, + }, + }, + true, + "", + }, + + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.#": { + Old: "0", + NewComputed: true, + }, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{}, + }, + true, + "", + }, + + // Computed can change RequiresNew by removal, and that's okay + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.#": { + Old: "0", + NewComputed: true, + RequiresNew: true, + }, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{}, + }, + true, + "", + }, + + // Computed can change Destroy by removal, and that's okay + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.#": { + Old: "0", + NewComputed: true, + RequiresNew: true, + }, + }, + + Destroy: true, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{}, + }, + true, + "", + }, + + // Computed can change Destroy by elements + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.#": { + Old: "0", + NewComputed: true, + RequiresNew: true, + }, + }, + + Destroy: true, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.#": { + Old: "1", + New: "1", + }, + "foo.12": { + Old: "4", + New: "12", + RequiresNew: true, + }, + }, + + Destroy: true, + }, + true, + "", + }, + + // Computed sets may not contain all fields in the original diff, and + // because multiple entries for the same set can compute to the same + // hash before the values are computed or interpolated, the overall + // count can change as well. + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.#": { + Old: "0", + New: "1", + }, + "foo.~35964334.bar": { + Old: "", + New: "${var.foo}", + }, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.#": { + Old: "0", + New: "2", + }, + "foo.87654323.bar": { + Old: "", + New: "12", + }, + "foo.87654325.bar": { + Old: "", + New: "12", + }, + "foo.87654325.baz": { + Old: "", + New: "12", + }, + }, + }, + true, + "", + }, + + // Computed values in maps will fail the "Same" check as well + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.%": { + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.%": { + Old: "0", + New: "1", + NewComputed: false, + }, + "foo.val": { + Old: "", + New: "something", + }, + }, + }, + true, + "", + }, + + // In a DESTROY/CREATE scenario, the plan diff will be run against the + // state of the old instance, while the apply diff will be run against an + // empty state (because the state is cleared when the destroy runs.) + // For complex attributes, this can result in keys that seem to disappear + // between the two diffs, when in reality everything is working just fine. + // + // Same() needs to take into account this scenario by analyzing NewRemoved + // and treating as "Same" a diff that does indeed have that key removed. + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "somemap.oldkey": { + Old: "long ago", + New: "", + NewRemoved: true, + }, + "somemap.newkey": { + Old: "", + New: "brave new world", + }, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "somemap.newkey": { + Old: "", + New: "brave new world", + }, + }, + }, + true, + "", + }, + + // Another thing that can occur in DESTROY/CREATE scenarios is that list + // values that are going to zero have diffs that show up at plan time but + // are gone at apply time. The NewRemoved handling catches the fields and + // treats them as OK, but it also needs to treat the .# field itself as + // okay to be present in the old diff but not in the new one. + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "reqnew": { + Old: "old", + New: "new", + RequiresNew: true, + }, + "somemap.#": { + Old: "1", + New: "0", + }, + "somemap.oldkey": { + Old: "long ago", + New: "", + NewRemoved: true, + }, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "reqnew": { + Old: "", + New: "new", + RequiresNew: true, + }, + }, + }, + true, + "", + }, + + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "reqnew": { + Old: "old", + New: "new", + RequiresNew: true, + }, + "somemap.%": { + Old: "1", + New: "0", + }, + "somemap.oldkey": { + Old: "long ago", + New: "", + NewRemoved: true, + }, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "reqnew": { + Old: "", + New: "new", + RequiresNew: true, + }, + }, + }, + true, + "", + }, + + // Innner computed set should allow outer change in key + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.#": { + Old: "0", + New: "1", + }, + "foo.~1.outer_val": { + Old: "", + New: "foo", + }, + "foo.~1.inner.#": { + Old: "0", + New: "1", + }, + "foo.~1.inner.~2.value": { + Old: "", + New: "${var.bar}", + NewComputed: true, + }, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.#": { + Old: "0", + New: "1", + }, + "foo.12.outer_val": { + Old: "", + New: "foo", + }, + "foo.12.inner.#": { + Old: "0", + New: "1", + }, + "foo.12.inner.42.value": { + Old: "", + New: "baz", + }, + }, + }, + true, + "", + }, + + // Innner computed list should allow outer change in key + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.#": { + Old: "0", + New: "1", + }, + "foo.~1.outer_val": { + Old: "", + New: "foo", + }, + "foo.~1.inner.#": { + Old: "0", + New: "1", + }, + "foo.~1.inner.0.value": { + Old: "", + New: "${var.bar}", + NewComputed: true, + }, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.#": { + Old: "0", + New: "1", + }, + "foo.12.outer_val": { + Old: "", + New: "foo", + }, + "foo.12.inner.#": { + Old: "0", + New: "1", + }, + "foo.12.inner.0.value": { + Old: "", + New: "baz", + }, + }, + }, + true, + "", + }, + + // When removing all collection items, the diff is allowed to contain + // nothing when re-creating the resource. This should be the "Same" + // since we said we were going from 1 to 0. + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.%": { + Old: "1", + New: "0", + RequiresNew: true, + }, + "foo.bar": { + Old: "baz", + New: "", + NewRemoved: true, + RequiresNew: true, + }, + }, + }, + &InstanceDiff{}, + true, + "", + }, + + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo.#": { + Old: "1", + New: "0", + RequiresNew: true, + }, + "foo.0": { + Old: "baz", + New: "", + NewRemoved: true, + RequiresNew: true, + }, + }, + }, + &InstanceDiff{}, + true, + "", + }, + + // Make sure that DestroyTainted diffs pass as well, especially when diff + // two works off of no state. + { + &InstanceDiff{ + DestroyTainted: true, + Attributes: map[string]*ResourceAttrDiff{ + "foo": { + Old: "foo", + New: "foo", + }, + }, + }, + &InstanceDiff{ + DestroyTainted: true, + Attributes: map[string]*ResourceAttrDiff{ + "foo": { + Old: "", + New: "foo", + }, + }, + }, + true, + "", + }, + // RequiresNew in different attribute + { + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": { + Old: "foo", + New: "foo", + }, + "bar": { + Old: "bar", + New: "baz", + RequiresNew: true, + }, + }, + }, + &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": { + Old: "", + New: "foo", + }, + "bar": { + Old: "", + New: "baz", + RequiresNew: true, + }, + }, + }, + true, + "", + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + same, reason := tc.One.Same(tc.Two) + if same != tc.Same { + t.Fatalf("%d: expected same: %t, got %t (%s)\n\n one: %#v\n\ntwo: %#v", + i, tc.Same, same, reason, tc.One, tc.Two) + } + if reason != tc.Reason { + t.Fatalf( + "%d: bad reason\n\nexpected: %#v\n\ngot: %#v", i, tc.Reason, reason) + } + }) + } +} + +func TestCountFlatmapContainerValues(t *testing.T) { + for i, tc := range []struct { + attrs map[string]string + key string + count string + }{ + { + attrs: map[string]string{"set.2.list.#": "9999", "set.2.list.0": "x", "set.2.list.0.z": "y", "set.2.attr": "bar", "set.#": "9999"}, + key: "set.2.list.#", + count: "1", + }, + { + attrs: map[string]string{"set.2.list.#": "9999", "set.2.list.0": "x", "set.2.list.0.z": "y", "set.2.attr": "bar", "set.#": "9999"}, + key: "set.#", + count: "1", + }, + { + attrs: map[string]string{"set.2.list.0": "x", "set.2.list.0.z": "y", "set.2.attr": "bar", "set.#": "9999"}, + key: "set.#", + count: "1", + }, + { + attrs: map[string]string{"map.#": "3", "map.a": "b", "map.a.#": "0", "map.b": "4"}, + key: "map.#", + count: "2", + }, + } { + t.Run(strconv.Itoa(i), func(t *testing.T) { + count := countFlatmapContainerValues(tc.key, tc.attrs) + if count != tc.count { + t.Fatalf("expected %q, got %q", tc.count, count) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/edge_destroy.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/edge_destroy.go deleted file mode 100644 index 17464bc0..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/edge_destroy.go +++ /dev/null @@ -1,17 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" -) - -// DestroyEdge is an edge that represents a standard "destroy" relationship: -// Target depends on Source because Source is destroying. -type DestroyEdge struct { - S, T dag.Vertex -} - -func (e *DestroyEdge) Hashcode() interface{} { return fmt.Sprintf("%p-%p", e.S, e.T) } -func (e *DestroyEdge) Source() dag.Vertex { return e.S } -func (e *DestroyEdge) Target() dag.Vertex { return e.T } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval.go deleted file mode 100644 index c490c3bc..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval.go +++ /dev/null @@ -1,70 +0,0 @@ -package terraform - -import ( - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// EvalNode is the interface that must be implemented by graph nodes to -// evaluate/execute. -type EvalNode interface { - // Eval evaluates this node with the given context. The second parameter - // are the argument values. These will match in order and 1-1 with the - // results of the Args() return value. - Eval(EvalContext) (interface{}, error) -} - -// GraphNodeEvalable is the interface that graph nodes must implement -// to enable valuation. -type GraphNodeEvalable interface { - EvalTree() EvalNode -} - -// EvalEarlyExitError is a special error return value that can be returned -// by eval nodes that does an early exit. -type EvalEarlyExitError struct{} - -func (EvalEarlyExitError) Error() string { return "early exit" } - -// Eval evaluates the given EvalNode with the given context, properly -// evaluating all args in the correct order. -func Eval(n EvalNode, ctx EvalContext) (interface{}, error) { - // Call the lower level eval which doesn't understand early exit, - // and if we early exit, it isn't an error. - result, err := EvalRaw(n, ctx) - if err != nil { - if _, ok := err.(EvalEarlyExitError); ok { - return nil, nil - } - } - - return result, err -} - -// EvalRaw is like Eval except that it returns all errors, even if they -// signal something normal such as EvalEarlyExitError. -func EvalRaw(n EvalNode, ctx EvalContext) (interface{}, error) { - path := "unknown" - if ctx != nil { - path = ctx.Path().String() - } - if path == "" { - path = "" - } - - log.Printf("[TRACE] %s: eval: %T", path, n) - output, err := n.Eval(ctx) - if err != nil { - switch err.(type) { - case EvalEarlyExitError: - log.Printf("[TRACE] %s: eval: %T, early exit err: %s", path, n, err) - case tfdiags.NonFatalError: - log.Printf("[WARN] %s: eval: %T, non-fatal err: %s", path, n, err) - default: - log.Printf("[ERROR] %s: eval: %T, err: %s", path, n, err) - } - } - - return output, err -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_apply.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_apply.go deleted file mode 100644 index 6beeaea9..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_apply.go +++ /dev/null @@ -1,656 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - "strings" - - "github.com/hashicorp/go-multierror" - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/provisioners" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// EvalApply is an EvalNode implementation that writes the diff to -// the full diff. -type EvalApply struct { - Addr addrs.ResourceInstance - Config *configs.Resource - Dependencies []addrs.Referenceable - State **states.ResourceInstanceObject - Change **plans.ResourceInstanceChange - ProviderAddr addrs.AbsProviderConfig - Provider *providers.Interface - ProviderSchema **ProviderSchema - Output **states.ResourceInstanceObject - CreateNew *bool - Error *error -} - -// TODO: test -func (n *EvalApply) Eval(ctx EvalContext) (interface{}, error) { - var diags tfdiags.Diagnostics - - change := *n.Change - provider := *n.Provider - state := *n.State - absAddr := n.Addr.Absolute(ctx.Path()) - - if state == nil { - state = &states.ResourceInstanceObject{} - } - - schema, _ := (*n.ProviderSchema).SchemaForResourceType(n.Addr.Resource.Mode, n.Addr.Resource.Type) - if schema == nil { - // Should be caught during validation, so we don't bother with a pretty error here - return nil, fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Type) - } - - if n.CreateNew != nil { - *n.CreateNew = (change.Action == plans.Create || change.Action.IsReplace()) - } - - configVal := cty.NullVal(cty.DynamicPseudoType) - if n.Config != nil { - var configDiags tfdiags.Diagnostics - forEach, _ := evaluateResourceForEachExpression(n.Config.ForEach, ctx) - keyData := EvalDataForInstanceKey(n.Addr.Key, forEach) - configVal, _, configDiags = ctx.EvaluateBlock(n.Config.Config, schema, nil, keyData) - diags = diags.Append(configDiags) - if configDiags.HasErrors() { - return nil, diags.Err() - } - } - - if !configVal.IsWhollyKnown() { - return nil, fmt.Errorf( - "configuration for %s still contains unknown values during apply (this is a bug in Terraform; please report it!)", - absAddr, - ) - } - - log.Printf("[DEBUG] %s: applying the planned %s change", n.Addr.Absolute(ctx.Path()), change.Action) - resp := provider.ApplyResourceChange(providers.ApplyResourceChangeRequest{ - TypeName: n.Addr.Resource.Type, - PriorState: change.Before, - Config: configVal, - PlannedState: change.After, - PlannedPrivate: change.Private, - }) - applyDiags := resp.Diagnostics - if n.Config != nil { - applyDiags = applyDiags.InConfigBody(n.Config.Config) - } - diags = diags.Append(applyDiags) - - // Even if there are errors in the returned diagnostics, the provider may - // have returned a _partial_ state for an object that already exists but - // failed to fully configure, and so the remaining code must always run - // to completion but must be defensive against the new value being - // incomplete. - newVal := resp.NewState - - if newVal == cty.NilVal { - // Providers are supposed to return a partial new value even when errors - // occur, but sometimes they don't and so in that case we'll patch that up - // by just using the prior state, so we'll at least keep track of the - // object for the user to retry. - newVal = change.Before - - // As a special case, we'll set the new value to null if it looks like - // we were trying to execute a delete, because the provider in this case - // probably left the newVal unset intending it to be interpreted as "null". - if change.After.IsNull() { - newVal = cty.NullVal(schema.ImpliedType()) - } - - // Ideally we'd produce an error or warning here if newVal is nil and - // there are no errors in diags, because that indicates a buggy - // provider not properly reporting its result, but unfortunately many - // of our historical test mocks behave in this way and so producing - // a diagnostic here fails hundreds of tests. Instead, we must just - // silently retain the old value for now. Returning a nil value with - // no errors is still always considered a bug in the provider though, - // and should be fixed for any "real" providers that do it. - } - - var conformDiags tfdiags.Diagnostics - for _, err := range newVal.Type().TestConformance(schema.ImpliedType()) { - conformDiags = conformDiags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced invalid object", - fmt.Sprintf( - "Provider %q produced an invalid value after apply for %s. The result cannot not be saved in the Terraform state.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.ProviderConfig.Type, tfdiags.FormatErrorPrefixed(err, absAddr.String()), - ), - )) - } - diags = diags.Append(conformDiags) - if conformDiags.HasErrors() { - // Bail early in this particular case, because an object that doesn't - // conform to the schema can't be saved in the state anyway -- the - // serializer will reject it. - return nil, diags.Err() - } - - // After this point we have a type-conforming result object and so we - // must always run to completion to ensure it can be saved. If n.Error - // is set then we must not return a non-nil error, in order to allow - // evaluation to continue to a later point where our state object will - // be saved. - - // By this point there must not be any unknown values remaining in our - // object, because we've applied the change and we can't save unknowns - // in our persistent state. If any are present then we will indicate an - // error (which is always a bug in the provider) but we will also replace - // them with nulls so that we can successfully save the portions of the - // returned value that are known. - if !newVal.IsWhollyKnown() { - // To generate better error messages, we'll go for a walk through the - // value and make a separate diagnostic for each unknown value we - // find. - cty.Walk(newVal, func(path cty.Path, val cty.Value) (bool, error) { - if !val.IsKnown() { - pathStr := tfdiags.FormatCtyPath(path) - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider returned invalid result object after apply", - fmt.Sprintf( - "After the apply operation, the provider still indicated an unknown value for %s%s. All values must be known after apply, so this is always a bug in the provider and should be reported in the provider's own repository. Terraform will still save the other known object values in the state.", - n.Addr.Absolute(ctx.Path()), pathStr, - ), - )) - } - return true, nil - }) - - // NOTE: This operation can potentially be lossy if there are multiple - // elements in a set that differ only by unknown values: after - // replacing with null these will be merged together into a single set - // element. Since we can only get here in the presence of a provider - // bug, we accept this because storing a result here is always a - // best-effort sort of thing. - newVal = cty.UnknownAsNull(newVal) - } - - if change.Action != plans.Delete && !diags.HasErrors() { - // Only values that were marked as unknown in the planned value are allowed - // to change during the apply operation. (We do this after the unknown-ness - // check above so that we also catch anything that became unknown after - // being known during plan.) - // - // If we are returning other errors anyway then we'll give this - // a pass since the other errors are usually the explanation for - // this one and so it's more helpful to let the user focus on the - // root cause rather than distract with this extra problem. - if errs := objchange.AssertObjectCompatible(schema, change.After, newVal); len(errs) > 0 { - if resp.LegacyTypeSystem { - // The shimming of the old type system in the legacy SDK is not precise - // enough to pass this consistency check, so we'll give it a pass here, - // but we will generate a warning about it so that we are more likely - // to notice in the logs if an inconsistency beyond the type system - // leads to a downstream provider failure. - var buf strings.Builder - fmt.Fprintf(&buf, "[WARN] Provider %q produced an unexpected new value for %s, but we are tolerating it because it is using the legacy plugin SDK.\n The following problems may be the cause of any confusing errors from downstream operations:", n.ProviderAddr.ProviderConfig.Type, absAddr) - for _, err := range errs { - fmt.Fprintf(&buf, "\n - %s", tfdiags.FormatError(err)) - } - log.Print(buf.String()) - - // The sort of inconsistency we won't catch here is if a known value - // in the plan is changed during apply. That can cause downstream - // problems because a dependent resource would make its own plan based - // on the planned value, and thus get a different result during the - // apply phase. This will usually lead to a "Provider produced invalid plan" - // error that incorrectly blames the downstream resource for the change. - - } else { - for _, err := range errs { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced inconsistent result after apply", - fmt.Sprintf( - "When applying changes to %s, provider %q produced an unexpected new value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - absAddr, n.ProviderAddr.ProviderConfig.Type, tfdiags.FormatError(err), - ), - )) - } - } - } - } - - // If a provider returns a null or non-null object at the wrong time then - // we still want to save that but it often causes some confusing behaviors - // where it seems like Terraform is failing to take any action at all, - // so we'll generate some errors to draw attention to it. - if !diags.HasErrors() { - if change.Action == plans.Delete && !newVal.IsNull() { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider returned invalid result object after apply", - fmt.Sprintf( - "After applying a %s plan, the provider returned a non-null object for %s. Destroying should always produce a null value, so this is always a bug in the provider and should be reported in the provider's own repository. Terraform will still save this errant object in the state for debugging and recovery.", - change.Action, n.Addr.Absolute(ctx.Path()), - ), - )) - } - if change.Action != plans.Delete && newVal.IsNull() { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider returned invalid result object after apply", - fmt.Sprintf( - "After applying a %s plan, the provider returned a null object for %s. Only destroying should always produce a null value, so this is always a bug in the provider and should be reported in the provider's own repository.", - change.Action, n.Addr.Absolute(ctx.Path()), - ), - )) - } - } - - // Sometimes providers return a null value when an operation fails for some - // reason, but we'd rather keep the prior state so that the error can be - // corrected on a subsequent run. We must only do this for null new value - // though, or else we may discard partial updates the provider was able to - // complete. - if diags.HasErrors() && newVal.IsNull() { - // Otherwise, we'll continue but using the prior state as the new value, - // making this effectively a no-op. If the item really _has_ been - // deleted then our next refresh will detect that and fix it up. - // If change.Action is Create then change.Before will also be null, - // which is fine. - newVal = change.Before - } - - var newState *states.ResourceInstanceObject - if !newVal.IsNull() { // null value indicates that the object is deleted, so we won't set a new state in that case - newState = &states.ResourceInstanceObject{ - Status: states.ObjectReady, - Value: newVal, - Private: resp.Private, - Dependencies: n.Dependencies, // Should be populated by the caller from the StateDependencies method on the resource instance node - } - } - - // Write the final state - if n.Output != nil { - *n.Output = newState - } - - if diags.HasErrors() { - // If the caller provided an error pointer then they are expected to - // handle the error some other way and we treat our own result as - // success. - if n.Error != nil { - err := diags.Err() - *n.Error = err - log.Printf("[DEBUG] %s: apply errored, but we're indicating that via the Error pointer rather than returning it: %s", n.Addr.Absolute(ctx.Path()), err) - return nil, nil - } - } - - return nil, diags.ErrWithWarnings() -} - -// EvalApplyPre is an EvalNode implementation that does the pre-Apply work -type EvalApplyPre struct { - Addr addrs.ResourceInstance - Gen states.Generation - State **states.ResourceInstanceObject - Change **plans.ResourceInstanceChange -} - -// TODO: test -func (n *EvalApplyPre) Eval(ctx EvalContext) (interface{}, error) { - change := *n.Change - absAddr := n.Addr.Absolute(ctx.Path()) - - if change == nil { - panic(fmt.Sprintf("EvalApplyPre for %s called with nil Change", absAddr)) - } - - if resourceHasUserVisibleApply(n.Addr) { - priorState := change.Before - plannedNewState := change.After - - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PreApply(absAddr, n.Gen, change.Action, priorState, plannedNewState) - }) - if err != nil { - return nil, err - } - } - - return nil, nil -} - -// EvalApplyPost is an EvalNode implementation that does the post-Apply work -type EvalApplyPost struct { - Addr addrs.ResourceInstance - Gen states.Generation - State **states.ResourceInstanceObject - Error *error -} - -// TODO: test -func (n *EvalApplyPost) Eval(ctx EvalContext) (interface{}, error) { - state := *n.State - - if resourceHasUserVisibleApply(n.Addr) { - absAddr := n.Addr.Absolute(ctx.Path()) - var newState cty.Value - if state != nil { - newState = state.Value - } else { - newState = cty.NullVal(cty.DynamicPseudoType) - } - var err error - if n.Error != nil { - err = *n.Error - } - - hookErr := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostApply(absAddr, n.Gen, newState, err) - }) - if hookErr != nil { - return nil, hookErr - } - } - - return nil, *n.Error -} - -// EvalMaybeTainted is an EvalNode that takes the planned change, new value, -// and possible error from an apply operation and produces a new instance -// object marked as tainted if it appears that a create operation has failed. -// -// This EvalNode never returns an error, to ensure that a subsequent EvalNode -// can still record the possibly-tainted object in the state. -type EvalMaybeTainted struct { - Addr addrs.ResourceInstance - Gen states.Generation - Change **plans.ResourceInstanceChange - State **states.ResourceInstanceObject - Error *error - - // If StateOutput is not nil, its referent will be assigned either the same - // pointer as State or a new object with its status set as Tainted, - // depending on whether an error is given and if this was a create action. - StateOutput **states.ResourceInstanceObject -} - -// TODO: test -func (n *EvalMaybeTainted) Eval(ctx EvalContext) (interface{}, error) { - state := *n.State - change := *n.Change - err := *n.Error - - if state != nil && state.Status == states.ObjectTainted { - log.Printf("[TRACE] EvalMaybeTainted: %s was already tainted, so nothing to do", n.Addr.Absolute(ctx.Path())) - return nil, nil - } - - if n.StateOutput != nil { - if err != nil && change.Action == plans.Create { - // If there are errors during a _create_ then the object is - // in an undefined state, and so we'll mark it as tainted so - // we can try again on the next run. - // - // We don't do this for other change actions because errors - // during updates will often not change the remote object at all. - // If there _were_ changes prior to the error, it's the provider's - // responsibility to record the effect of those changes in the - // object value it returned. - log.Printf("[TRACE] EvalMaybeTainted: %s encountered an error during creation, so it is now marked as tainted", n.Addr.Absolute(ctx.Path())) - *n.StateOutput = state.AsTainted() - } else { - *n.StateOutput = state - } - } - - return nil, nil -} - -// resourceHasUserVisibleApply returns true if the given resource is one where -// apply actions should be exposed to the user. -// -// Certain resources do apply actions only as an implementation detail, so -// these should not be advertised to code outside of this package. -func resourceHasUserVisibleApply(addr addrs.ResourceInstance) bool { - // Only managed resources have user-visible apply actions. - // In particular, this excludes data resources since we "apply" these - // only as an implementation detail of removing them from state when - // they are destroyed. (When reading, they don't get here at all because - // we present them as "Refresh" actions.) - return addr.ContainingResource().Mode == addrs.ManagedResourceMode -} - -// EvalApplyProvisioners is an EvalNode implementation that executes -// the provisioners for a resource. -// -// TODO(mitchellh): This should probably be split up into a more fine-grained -// ApplyProvisioner (single) that is looped over. -type EvalApplyProvisioners struct { - Addr addrs.ResourceInstance - State **states.ResourceInstanceObject - ResourceConfig *configs.Resource - CreateNew *bool - Error *error - - // When is the type of provisioner to run at this point - When configs.ProvisionerWhen -} - -// TODO: test -func (n *EvalApplyProvisioners) Eval(ctx EvalContext) (interface{}, error) { - absAddr := n.Addr.Absolute(ctx.Path()) - state := *n.State - if state == nil { - log.Printf("[TRACE] EvalApplyProvisioners: %s has no state, so skipping provisioners", n.Addr) - return nil, nil - } - if n.When == configs.ProvisionerWhenCreate && n.CreateNew != nil && !*n.CreateNew { - // If we're not creating a new resource, then don't run provisioners - log.Printf("[TRACE] EvalApplyProvisioners: %s is not freshly-created, so no provisioning is required", n.Addr) - return nil, nil - } - if state.Status == states.ObjectTainted { - // No point in provisioning an object that is already tainted, since - // it's going to get recreated on the next apply anyway. - log.Printf("[TRACE] EvalApplyProvisioners: %s is tainted, so skipping provisioning", n.Addr) - return nil, nil - } - - provs := n.filterProvisioners() - if len(provs) == 0 { - // We have no provisioners, so don't do anything - return nil, nil - } - - if n.Error != nil && *n.Error != nil { - // We're already tainted, so just return out - return nil, nil - } - - { - // Call pre hook - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PreProvisionInstance(absAddr, state.Value) - }) - if err != nil { - return nil, err - } - } - - // If there are no errors, then we append it to our output error - // if we have one, otherwise we just output it. - err := n.apply(ctx, provs) - if err != nil { - *n.Error = multierror.Append(*n.Error, err) - if n.Error == nil { - return nil, err - } else { - log.Printf("[TRACE] EvalApplyProvisioners: %s provisioning failed, but we will continue anyway at the caller's request", absAddr) - return nil, nil - } - } - - { - // Call post hook - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostProvisionInstance(absAddr, state.Value) - }) - if err != nil { - return nil, err - } - } - - return nil, nil -} - -// filterProvisioners filters the provisioners on the resource to only -// the provisioners specified by the "when" option. -func (n *EvalApplyProvisioners) filterProvisioners() []*configs.Provisioner { - // Fast path the zero case - if n.ResourceConfig == nil || n.ResourceConfig.Managed == nil { - return nil - } - - if len(n.ResourceConfig.Managed.Provisioners) == 0 { - return nil - } - - result := make([]*configs.Provisioner, 0, len(n.ResourceConfig.Managed.Provisioners)) - for _, p := range n.ResourceConfig.Managed.Provisioners { - if p.When == n.When { - result = append(result, p) - } - } - - return result -} - -func (n *EvalApplyProvisioners) apply(ctx EvalContext, provs []*configs.Provisioner) error { - var diags tfdiags.Diagnostics - instanceAddr := n.Addr - absAddr := instanceAddr.Absolute(ctx.Path()) - - // If there's a connection block defined directly inside the resource block - // then it'll serve as a base connection configuration for all of the - // provisioners. - var baseConn hcl.Body - if n.ResourceConfig.Managed != nil && n.ResourceConfig.Managed.Connection != nil { - baseConn = n.ResourceConfig.Managed.Connection.Config - } - - for _, prov := range provs { - log.Printf("[TRACE] EvalApplyProvisioners: provisioning %s with %q", absAddr, prov.Type) - - // Get the provisioner - provisioner := ctx.Provisioner(prov.Type) - schema := ctx.ProvisionerSchema(prov.Type) - - forEach, forEachDiags := evaluateResourceForEachExpression(n.ResourceConfig.ForEach, ctx) - diags = diags.Append(forEachDiags) - keyData := EvalDataForInstanceKey(instanceAddr.Key, forEach) - - // Evaluate the main provisioner configuration. - config, _, configDiags := ctx.EvaluateBlock(prov.Config, schema, instanceAddr, keyData) - diags = diags.Append(configDiags) - - // If the provisioner block contains a connection block of its own then - // it can override the base connection configuration, if any. - var localConn hcl.Body - if prov.Connection != nil { - localConn = prov.Connection.Config - } - - var connBody hcl.Body - switch { - case baseConn != nil && localConn != nil: - // Our standard merging logic applies here, similar to what we do - // with _override.tf configuration files: arguments from the - // base connection block will be masked by any arguments of the - // same name in the local connection block. - connBody = configs.MergeBodies(baseConn, localConn) - case baseConn != nil: - connBody = baseConn - case localConn != nil: - connBody = localConn - } - - // start with an empty connInfo - connInfo := cty.NullVal(connectionBlockSupersetSchema.ImpliedType()) - - if connBody != nil { - var connInfoDiags tfdiags.Diagnostics - connInfo, _, connInfoDiags = ctx.EvaluateBlock(connBody, connectionBlockSupersetSchema, instanceAddr, keyData) - diags = diags.Append(connInfoDiags) - if diags.HasErrors() { - // "on failure continue" setting only applies to failures of the - // provisioner itself, not to invalid configuration. - return diags.Err() - } - } - - { - // Call pre hook - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PreProvisionInstanceStep(absAddr, prov.Type) - }) - if err != nil { - return err - } - } - - // The output function - outputFn := func(msg string) { - ctx.Hook(func(h Hook) (HookAction, error) { - h.ProvisionOutput(absAddr, prov.Type, msg) - return HookActionContinue, nil - }) - } - - output := CallbackUIOutput{OutputFn: outputFn} - resp := provisioner.ProvisionResource(provisioners.ProvisionResourceRequest{ - Config: config, - Connection: connInfo, - UIOutput: &output, - }) - applyDiags := resp.Diagnostics.InConfigBody(prov.Config) - - // Call post hook - hookErr := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostProvisionInstanceStep(absAddr, prov.Type, applyDiags.Err()) - }) - - switch prov.OnFailure { - case configs.ProvisionerOnFailureContinue: - if applyDiags.HasErrors() { - log.Printf("[WARN] Errors while provisioning %s with %q, but continuing as requested in configuration", n.Addr, prov.Type) - } else { - // Maybe there are warnings that we still want to see - diags = diags.Append(applyDiags) - } - default: - diags = diags.Append(applyDiags) - if applyDiags.HasErrors() { - log.Printf("[WARN] Errors while provisioning %s with %q, so aborting", n.Addr, prov.Type) - return diags.Err() - } - } - - // Deal with the hook - if hookErr != nil { - return hookErr - } - } - - return diags.ErrWithWarnings() -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_check_prevent_destroy.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_check_prevent_destroy.go deleted file mode 100644 index d13a9652..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_check_prevent_destroy.go +++ /dev/null @@ -1,47 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - - "github.com/hashicorp/hcl/v2" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// EvalPreventDestroy is an EvalNode implementation that returns an -// error if a resource has PreventDestroy configured and the diff -// would destroy the resource. -type EvalCheckPreventDestroy struct { - Addr addrs.ResourceInstance - Config *configs.Resource - Change **plans.ResourceInstanceChange -} - -func (n *EvalCheckPreventDestroy) Eval(ctx EvalContext) (interface{}, error) { - if n.Change == nil || *n.Change == nil || n.Config == nil || n.Config.Managed == nil { - return nil, nil - } - - change := *n.Change - preventDestroy := n.Config.Managed.PreventDestroy - - if (change.Action == plans.Delete || change.Action.IsReplace()) && preventDestroy { - var diags tfdiags.Diagnostics - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Instance cannot be destroyed", - Detail: fmt.Sprintf( - "Resource %s has lifecycle.prevent_destroy set, but the plan calls for this resource to be destroyed. To avoid this error and continue with the plan, either disable lifecycle.prevent_destroy or reduce the scope of the plan using the -target flag.", - n.Addr.Absolute(ctx.Path()).String(), - ), - Subject: &n.Config.DeclRange, - }) - return nil, diags.Err() - } - - return nil, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_context.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_context.go deleted file mode 100644 index 4fa011e2..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_context.go +++ /dev/null @@ -1,133 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/lang" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/provisioners" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - "github.com/zclconf/go-cty/cty" -) - -// EvalContext is the interface that is given to eval nodes to execute. -type EvalContext interface { - // Stopped returns a channel that is closed when evaluation is stopped - // via Terraform.Context.Stop() - Stopped() <-chan struct{} - - // Path is the current module path. - Path() addrs.ModuleInstance - - // Hook is used to call hook methods. The callback is called for each - // hook and should return the hook action to take and the error. - Hook(func(Hook) (HookAction, error)) error - - // Input is the UIInput object for interacting with the UI. - Input() UIInput - - // InitProvider initializes the provider with the given type and address, and - // returns the implementation of the resource provider or an error. - // - // It is an error to initialize the same provider more than once. - InitProvider(typ string, addr addrs.ProviderConfig) (providers.Interface, error) - - // Provider gets the provider instance with the given address (already - // initialized) or returns nil if the provider isn't initialized. - // - // This method expects an _absolute_ provider configuration address, since - // resources in one module are able to use providers from other modules. - // InitProvider must've been called on the EvalContext of the module - // that owns the given provider before calling this method. - Provider(addrs.AbsProviderConfig) providers.Interface - - // ProviderSchema retrieves the schema for a particular provider, which - // must have already been initialized with InitProvider. - // - // This method expects an _absolute_ provider configuration address, since - // resources in one module are able to use providers from other modules. - ProviderSchema(addrs.AbsProviderConfig) *ProviderSchema - - // CloseProvider closes provider connections that aren't needed anymore. - CloseProvider(addrs.ProviderConfig) error - - // ConfigureProvider configures the provider with the given - // configuration. This is a separate context call because this call - // is used to store the provider configuration for inheritance lookups - // with ParentProviderConfig(). - ConfigureProvider(addrs.ProviderConfig, cty.Value) tfdiags.Diagnostics - - // ProviderInput and SetProviderInput are used to configure providers - // from user input. - ProviderInput(addrs.ProviderConfig) map[string]cty.Value - SetProviderInput(addrs.ProviderConfig, map[string]cty.Value) - - // InitProvisioner initializes the provisioner with the given name and - // returns the implementation of the resource provisioner or an error. - // - // It is an error to initialize the same provisioner more than once. - InitProvisioner(string) (provisioners.Interface, error) - - // Provisioner gets the provisioner instance with the given name (already - // initialized) or returns nil if the provisioner isn't initialized. - Provisioner(string) provisioners.Interface - - // ProvisionerSchema retrieves the main configuration schema for a - // particular provisioner, which must have already been initialized with - // InitProvisioner. - ProvisionerSchema(string) *configschema.Block - - // CloseProvisioner closes provisioner connections that aren't needed - // anymore. - CloseProvisioner(string) error - - // EvaluateBlock takes the given raw configuration block and associated - // schema and evaluates it to produce a value of an object type that - // conforms to the implied type of the schema. - // - // The "self" argument is optional. If given, it is the referenceable - // address that the name "self" should behave as an alias for when - // evaluating. Set this to nil if the "self" object should not be available. - // - // The "key" argument is also optional. If given, it is the instance key - // of the current object within the multi-instance container it belongs - // to. For example, on a resource block with "count" set this should be - // set to a different addrs.IntKey for each instance created from that - // block. Set this to addrs.NoKey if not appropriate. - // - // The returned body is an expanded version of the given body, with any - // "dynamic" blocks replaced with zero or more static blocks. This can be - // used to extract correct source location information about attributes of - // the returned object value. - EvaluateBlock(body hcl.Body, schema *configschema.Block, self addrs.Referenceable, keyData InstanceKeyEvalData) (cty.Value, hcl.Body, tfdiags.Diagnostics) - - // EvaluateExpr takes the given HCL expression and evaluates it to produce - // a value. - // - // The "self" argument is optional. If given, it is the referenceable - // address that the name "self" should behave as an alias for when - // evaluating. Set this to nil if the "self" object should not be available. - EvaluateExpr(expr hcl.Expression, wantType cty.Type, self addrs.Referenceable) (cty.Value, tfdiags.Diagnostics) - - // EvaluationScope returns a scope that can be used to evaluate reference - // addresses in this context. - EvaluationScope(self addrs.Referenceable, keyData InstanceKeyEvalData) *lang.Scope - - // SetModuleCallArguments defines values for the variables of a particular - // child module call. - // - // Calling this function multiple times has merging behavior, keeping any - // previously-set keys that are not present in the new map. - SetModuleCallArguments(addrs.ModuleCallInstance, map[string]cty.Value) - - // Changes returns the writer object that can be used to write new proposed - // changes into the global changes set. - Changes() *plans.ChangesSync - - // State returns a wrapper object that provides safe concurrent access to - // the global state. - State() *states.SyncState -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_context_builtin.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_context_builtin.go deleted file mode 100644 index bd414a96..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_context_builtin.go +++ /dev/null @@ -1,329 +0,0 @@ -package terraform - -import ( - "context" - "fmt" - "log" - "sync" - - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/provisioners" - "github.com/hashicorp/terraform-plugin-sdk/internal/version" - - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/lang" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/zclconf/go-cty/cty" -) - -// BuiltinEvalContext is an EvalContext implementation that is used by -// Terraform by default. -type BuiltinEvalContext struct { - // StopContext is the context used to track whether we're complete - StopContext context.Context - - // PathValue is the Path that this context is operating within. - PathValue addrs.ModuleInstance - - // Evaluator is used for evaluating expressions within the scope of this - // eval context. - Evaluator *Evaluator - - // Schemas is a repository of all of the schemas we should need to - // decode configuration blocks and expressions. This must be constructed by - // the caller to include schemas for all of the providers, resource types, - // data sources and provisioners used by the given configuration and - // state. - // - // This must not be mutated during evaluation. - Schemas *Schemas - - // VariableValues contains the variable values across all modules. This - // structure is shared across the entire containing context, and so it - // may be accessed only when holding VariableValuesLock. - // The keys of the first level of VariableValues are the string - // representations of addrs.ModuleInstance values. The second-level keys - // are variable names within each module instance. - VariableValues map[string]map[string]cty.Value - VariableValuesLock *sync.Mutex - - Components contextComponentFactory - Hooks []Hook - InputValue UIInput - ProviderCache map[string]providers.Interface - ProviderInputConfig map[string]map[string]cty.Value - ProviderLock *sync.Mutex - ProvisionerCache map[string]provisioners.Interface - ProvisionerLock *sync.Mutex - ChangesValue *plans.ChangesSync - StateValue *states.SyncState - - once sync.Once -} - -// BuiltinEvalContext implements EvalContext -var _ EvalContext = (*BuiltinEvalContext)(nil) - -func (ctx *BuiltinEvalContext) Stopped() <-chan struct{} { - // This can happen during tests. During tests, we just block forever. - if ctx.StopContext == nil { - return nil - } - - return ctx.StopContext.Done() -} - -func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error { - for _, h := range ctx.Hooks { - action, err := fn(h) - if err != nil { - return err - } - - switch action { - case HookActionContinue: - continue - case HookActionHalt: - // Return an early exit error to trigger an early exit - log.Printf("[WARN] Early exit triggered by hook: %T", h) - return EvalEarlyExitError{} - } - } - - return nil -} - -func (ctx *BuiltinEvalContext) Input() UIInput { - return ctx.InputValue -} - -func (ctx *BuiltinEvalContext) InitProvider(typeName string, addr addrs.ProviderConfig) (providers.Interface, error) { - ctx.once.Do(ctx.init) - absAddr := addr.Absolute(ctx.Path()) - - // If we already initialized, it is an error - if p := ctx.Provider(absAddr); p != nil { - return nil, fmt.Errorf("%s is already initialized", addr) - } - - // Warning: make sure to acquire these locks AFTER the call to Provider - // above, since it also acquires locks. - ctx.ProviderLock.Lock() - defer ctx.ProviderLock.Unlock() - - key := absAddr.String() - - p, err := ctx.Components.ResourceProvider(typeName, key) - if err != nil { - return nil, err - } - - log.Printf("[TRACE] BuiltinEvalContext: Initialized %q provider for %s", typeName, absAddr) - ctx.ProviderCache[key] = p - - return p, nil -} - -func (ctx *BuiltinEvalContext) Provider(addr addrs.AbsProviderConfig) providers.Interface { - ctx.once.Do(ctx.init) - - ctx.ProviderLock.Lock() - defer ctx.ProviderLock.Unlock() - - return ctx.ProviderCache[addr.String()] -} - -func (ctx *BuiltinEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) *ProviderSchema { - ctx.once.Do(ctx.init) - - return ctx.Schemas.ProviderSchema(addr.ProviderConfig.Type) -} - -func (ctx *BuiltinEvalContext) CloseProvider(addr addrs.ProviderConfig) error { - ctx.once.Do(ctx.init) - - ctx.ProviderLock.Lock() - defer ctx.ProviderLock.Unlock() - - key := addr.Absolute(ctx.Path()).String() - provider := ctx.ProviderCache[key] - if provider != nil { - delete(ctx.ProviderCache, key) - return provider.Close() - } - - return nil -} - -func (ctx *BuiltinEvalContext) ConfigureProvider(addr addrs.ProviderConfig, cfg cty.Value) tfdiags.Diagnostics { - var diags tfdiags.Diagnostics - absAddr := addr.Absolute(ctx.Path()) - p := ctx.Provider(absAddr) - if p == nil { - diags = diags.Append(fmt.Errorf("%s not initialized", addr)) - return diags - } - - providerSchema := ctx.ProviderSchema(absAddr) - if providerSchema == nil { - diags = diags.Append(fmt.Errorf("schema for %s is not available", absAddr)) - return diags - } - - req := providers.ConfigureRequest{ - TerraformVersion: version.String(), - Config: cfg, - } - - resp := p.Configure(req) - return resp.Diagnostics -} - -func (ctx *BuiltinEvalContext) ProviderInput(pc addrs.ProviderConfig) map[string]cty.Value { - ctx.ProviderLock.Lock() - defer ctx.ProviderLock.Unlock() - - if !ctx.Path().IsRoot() { - // Only root module provider configurations can have input. - return nil - } - - return ctx.ProviderInputConfig[pc.String()] -} - -func (ctx *BuiltinEvalContext) SetProviderInput(pc addrs.ProviderConfig, c map[string]cty.Value) { - absProvider := pc.Absolute(ctx.Path()) - - if !ctx.Path().IsRoot() { - // Only root module provider configurations can have input. - log.Printf("[WARN] BuiltinEvalContext: attempt to SetProviderInput for non-root module") - return - } - - // Save the configuration - ctx.ProviderLock.Lock() - ctx.ProviderInputConfig[absProvider.String()] = c - ctx.ProviderLock.Unlock() -} - -func (ctx *BuiltinEvalContext) InitProvisioner(n string) (provisioners.Interface, error) { - ctx.once.Do(ctx.init) - - // If we already initialized, it is an error - if p := ctx.Provisioner(n); p != nil { - return nil, fmt.Errorf("Provisioner '%s' already initialized", n) - } - - // Warning: make sure to acquire these locks AFTER the call to Provisioner - // above, since it also acquires locks. - ctx.ProvisionerLock.Lock() - defer ctx.ProvisionerLock.Unlock() - - key := PathObjectCacheKey(ctx.Path(), n) - - p, err := ctx.Components.ResourceProvisioner(n, key) - if err != nil { - return nil, err - } - - ctx.ProvisionerCache[key] = p - - return p, nil -} - -func (ctx *BuiltinEvalContext) Provisioner(n string) provisioners.Interface { - ctx.once.Do(ctx.init) - - ctx.ProvisionerLock.Lock() - defer ctx.ProvisionerLock.Unlock() - - key := PathObjectCacheKey(ctx.Path(), n) - return ctx.ProvisionerCache[key] -} - -func (ctx *BuiltinEvalContext) ProvisionerSchema(n string) *configschema.Block { - ctx.once.Do(ctx.init) - - return ctx.Schemas.ProvisionerConfig(n) -} - -func (ctx *BuiltinEvalContext) CloseProvisioner(n string) error { - ctx.once.Do(ctx.init) - - ctx.ProvisionerLock.Lock() - defer ctx.ProvisionerLock.Unlock() - - key := PathObjectCacheKey(ctx.Path(), n) - - prov := ctx.ProvisionerCache[key] - if prov != nil { - return prov.Close() - } - - return nil -} - -func (ctx *BuiltinEvalContext) EvaluateBlock(body hcl.Body, schema *configschema.Block, self addrs.Referenceable, keyData InstanceKeyEvalData) (cty.Value, hcl.Body, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - scope := ctx.EvaluationScope(self, keyData) - body, evalDiags := scope.ExpandBlock(body, schema) - diags = diags.Append(evalDiags) - val, evalDiags := scope.EvalBlock(body, schema) - diags = diags.Append(evalDiags) - return val, body, diags -} - -func (ctx *BuiltinEvalContext) EvaluateExpr(expr hcl.Expression, wantType cty.Type, self addrs.Referenceable) (cty.Value, tfdiags.Diagnostics) { - scope := ctx.EvaluationScope(self, EvalDataForNoInstanceKey) - return scope.EvalExpr(expr, wantType) -} - -func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, keyData InstanceKeyEvalData) *lang.Scope { - data := &evaluationStateData{ - Evaluator: ctx.Evaluator, - ModulePath: ctx.PathValue, - InstanceKeyData: keyData, - Operation: ctx.Evaluator.Operation, - } - return ctx.Evaluator.Scope(data, self) -} - -func (ctx *BuiltinEvalContext) Path() addrs.ModuleInstance { - return ctx.PathValue -} - -func (ctx *BuiltinEvalContext) SetModuleCallArguments(n addrs.ModuleCallInstance, vals map[string]cty.Value) { - ctx.VariableValuesLock.Lock() - defer ctx.VariableValuesLock.Unlock() - - childPath := n.ModuleInstance(ctx.PathValue) - key := childPath.String() - - args := ctx.VariableValues[key] - if args == nil { - args = make(map[string]cty.Value) - ctx.VariableValues[key] = vals - return - } - - for k, v := range vals { - args[k] = v - } -} - -func (ctx *BuiltinEvalContext) Changes() *plans.ChangesSync { - return ctx.ChangesValue -} - -func (ctx *BuiltinEvalContext) State() *states.SyncState { - return ctx.StateValue -} - -func (ctx *BuiltinEvalContext) init() { -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_context_mock.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_context_mock.go deleted file mode 100644 index 786316fb..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_context_mock.go +++ /dev/null @@ -1,319 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/lang" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/provisioners" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" -) - -// MockEvalContext is a mock version of EvalContext that can be used -// for tests. -type MockEvalContext struct { - StoppedCalled bool - StoppedValue <-chan struct{} - - HookCalled bool - HookHook Hook - HookError error - - InputCalled bool - InputInput UIInput - - InitProviderCalled bool - InitProviderType string - InitProviderAddr addrs.ProviderConfig - InitProviderProvider providers.Interface - InitProviderError error - - ProviderCalled bool - ProviderAddr addrs.AbsProviderConfig - ProviderProvider providers.Interface - - ProviderSchemaCalled bool - ProviderSchemaAddr addrs.AbsProviderConfig - ProviderSchemaSchema *ProviderSchema - - CloseProviderCalled bool - CloseProviderAddr addrs.ProviderConfig - CloseProviderProvider providers.Interface - - ProviderInputCalled bool - ProviderInputAddr addrs.ProviderConfig - ProviderInputValues map[string]cty.Value - - SetProviderInputCalled bool - SetProviderInputAddr addrs.ProviderConfig - SetProviderInputValues map[string]cty.Value - - ConfigureProviderCalled bool - ConfigureProviderAddr addrs.ProviderConfig - ConfigureProviderConfig cty.Value - ConfigureProviderDiags tfdiags.Diagnostics - - InitProvisionerCalled bool - InitProvisionerName string - InitProvisionerProvisioner provisioners.Interface - InitProvisionerError error - - ProvisionerCalled bool - ProvisionerName string - ProvisionerProvisioner provisioners.Interface - - ProvisionerSchemaCalled bool - ProvisionerSchemaName string - ProvisionerSchemaSchema *configschema.Block - - CloseProvisionerCalled bool - CloseProvisionerName string - CloseProvisionerProvisioner provisioners.Interface - - EvaluateBlockCalled bool - EvaluateBlockBody hcl.Body - EvaluateBlockSchema *configschema.Block - EvaluateBlockSelf addrs.Referenceable - EvaluateBlockKeyData InstanceKeyEvalData - EvaluateBlockResultFunc func( - body hcl.Body, - schema *configschema.Block, - self addrs.Referenceable, - keyData InstanceKeyEvalData, - ) (cty.Value, hcl.Body, tfdiags.Diagnostics) // overrides the other values below, if set - EvaluateBlockResult cty.Value - EvaluateBlockExpandedBody hcl.Body - EvaluateBlockDiags tfdiags.Diagnostics - - EvaluateExprCalled bool - EvaluateExprExpr hcl.Expression - EvaluateExprWantType cty.Type - EvaluateExprSelf addrs.Referenceable - EvaluateExprResultFunc func( - expr hcl.Expression, - wantType cty.Type, - self addrs.Referenceable, - ) (cty.Value, tfdiags.Diagnostics) // overrides the other values below, if set - EvaluateExprResult cty.Value - EvaluateExprDiags tfdiags.Diagnostics - - EvaluationScopeCalled bool - EvaluationScopeSelf addrs.Referenceable - EvaluationScopeKeyData InstanceKeyEvalData - EvaluationScopeScope *lang.Scope - - PathCalled bool - PathPath addrs.ModuleInstance - - SetModuleCallArgumentsCalled bool - SetModuleCallArgumentsModule addrs.ModuleCallInstance - SetModuleCallArgumentsValues map[string]cty.Value - - ChangesCalled bool - ChangesChanges *plans.ChangesSync - - StateCalled bool - StateState *states.SyncState -} - -// MockEvalContext implements EvalContext -var _ EvalContext = (*MockEvalContext)(nil) - -func (c *MockEvalContext) Stopped() <-chan struct{} { - c.StoppedCalled = true - return c.StoppedValue -} - -func (c *MockEvalContext) Hook(fn func(Hook) (HookAction, error)) error { - c.HookCalled = true - if c.HookHook != nil { - if _, err := fn(c.HookHook); err != nil { - return err - } - } - - return c.HookError -} - -func (c *MockEvalContext) Input() UIInput { - c.InputCalled = true - return c.InputInput -} - -func (c *MockEvalContext) InitProvider(t string, addr addrs.ProviderConfig) (providers.Interface, error) { - c.InitProviderCalled = true - c.InitProviderType = t - c.InitProviderAddr = addr - return c.InitProviderProvider, c.InitProviderError -} - -func (c *MockEvalContext) Provider(addr addrs.AbsProviderConfig) providers.Interface { - c.ProviderCalled = true - c.ProviderAddr = addr - return c.ProviderProvider -} - -func (c *MockEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) *ProviderSchema { - c.ProviderSchemaCalled = true - c.ProviderSchemaAddr = addr - return c.ProviderSchemaSchema -} - -func (c *MockEvalContext) CloseProvider(addr addrs.ProviderConfig) error { - c.CloseProviderCalled = true - c.CloseProviderAddr = addr - return nil -} - -func (c *MockEvalContext) ConfigureProvider(addr addrs.ProviderConfig, cfg cty.Value) tfdiags.Diagnostics { - c.ConfigureProviderCalled = true - c.ConfigureProviderAddr = addr - c.ConfigureProviderConfig = cfg - return c.ConfigureProviderDiags -} - -func (c *MockEvalContext) ProviderInput(addr addrs.ProviderConfig) map[string]cty.Value { - c.ProviderInputCalled = true - c.ProviderInputAddr = addr - return c.ProviderInputValues -} - -func (c *MockEvalContext) SetProviderInput(addr addrs.ProviderConfig, vals map[string]cty.Value) { - c.SetProviderInputCalled = true - c.SetProviderInputAddr = addr - c.SetProviderInputValues = vals -} - -func (c *MockEvalContext) InitProvisioner(n string) (provisioners.Interface, error) { - c.InitProvisionerCalled = true - c.InitProvisionerName = n - return c.InitProvisionerProvisioner, c.InitProvisionerError -} - -func (c *MockEvalContext) Provisioner(n string) provisioners.Interface { - c.ProvisionerCalled = true - c.ProvisionerName = n - return c.ProvisionerProvisioner -} - -func (c *MockEvalContext) ProvisionerSchema(n string) *configschema.Block { - c.ProvisionerSchemaCalled = true - c.ProvisionerSchemaName = n - return c.ProvisionerSchemaSchema -} - -func (c *MockEvalContext) CloseProvisioner(n string) error { - c.CloseProvisionerCalled = true - c.CloseProvisionerName = n - return nil -} - -func (c *MockEvalContext) EvaluateBlock(body hcl.Body, schema *configschema.Block, self addrs.Referenceable, keyData InstanceKeyEvalData) (cty.Value, hcl.Body, tfdiags.Diagnostics) { - c.EvaluateBlockCalled = true - c.EvaluateBlockBody = body - c.EvaluateBlockSchema = schema - c.EvaluateBlockSelf = self - c.EvaluateBlockKeyData = keyData - if c.EvaluateBlockResultFunc != nil { - return c.EvaluateBlockResultFunc(body, schema, self, keyData) - } - return c.EvaluateBlockResult, c.EvaluateBlockExpandedBody, c.EvaluateBlockDiags -} - -func (c *MockEvalContext) EvaluateExpr(expr hcl.Expression, wantType cty.Type, self addrs.Referenceable) (cty.Value, tfdiags.Diagnostics) { - c.EvaluateExprCalled = true - c.EvaluateExprExpr = expr - c.EvaluateExprWantType = wantType - c.EvaluateExprSelf = self - if c.EvaluateExprResultFunc != nil { - return c.EvaluateExprResultFunc(expr, wantType, self) - } - return c.EvaluateExprResult, c.EvaluateExprDiags -} - -// installSimpleEval is a helper to install a simple mock implementation of -// both EvaluateBlock and EvaluateExpr into the receiver. -// -// These default implementations will either evaluate the given input against -// the scope in field EvaluationScopeScope or, if it is nil, with no eval -// context at all so that only constant values may be used. -// -// This function overwrites any existing functions installed in fields -// EvaluateBlockResultFunc and EvaluateExprResultFunc. -func (c *MockEvalContext) installSimpleEval() { - c.EvaluateBlockResultFunc = func(body hcl.Body, schema *configschema.Block, self addrs.Referenceable, keyData InstanceKeyEvalData) (cty.Value, hcl.Body, tfdiags.Diagnostics) { - if scope := c.EvaluationScopeScope; scope != nil { - // Fully-functional codepath. - var diags tfdiags.Diagnostics - body, diags = scope.ExpandBlock(body, schema) - if diags.HasErrors() { - return cty.DynamicVal, body, diags - } - val, evalDiags := c.EvaluationScopeScope.EvalBlock(body, schema) - diags = diags.Append(evalDiags) - if evalDiags.HasErrors() { - return cty.DynamicVal, body, diags - } - return val, body, diags - } - - // Fallback codepath supporting constant values only. - val, hclDiags := hcldec.Decode(body, schema.DecoderSpec(), nil) - return val, body, tfdiags.Diagnostics(nil).Append(hclDiags) - } - c.EvaluateExprResultFunc = func(expr hcl.Expression, wantType cty.Type, self addrs.Referenceable) (cty.Value, tfdiags.Diagnostics) { - if scope := c.EvaluationScopeScope; scope != nil { - // Fully-functional codepath. - return scope.EvalExpr(expr, wantType) - } - - // Fallback codepath supporting constant values only. - var diags tfdiags.Diagnostics - val, hclDiags := expr.Value(nil) - diags = diags.Append(hclDiags) - if hclDiags.HasErrors() { - return cty.DynamicVal, diags - } - var err error - val, err = convert.Convert(val, wantType) - if err != nil { - diags = diags.Append(err) - return cty.DynamicVal, diags - } - return val, diags - } -} - -func (c *MockEvalContext) EvaluationScope(self addrs.Referenceable, keyData InstanceKeyEvalData) *lang.Scope { - c.EvaluationScopeCalled = true - c.EvaluationScopeSelf = self - c.EvaluationScopeKeyData = keyData - return c.EvaluationScopeScope -} - -func (c *MockEvalContext) Path() addrs.ModuleInstance { - c.PathCalled = true - return c.PathPath -} - -func (c *MockEvalContext) SetModuleCallArguments(n addrs.ModuleCallInstance, values map[string]cty.Value) { - c.SetModuleCallArgumentsCalled = true - c.SetModuleCallArgumentsModule = n - c.SetModuleCallArgumentsValues = values -} - -func (c *MockEvalContext) Changes() *plans.ChangesSync { - c.ChangesCalled = true - return c.ChangesChanges -} - -func (c *MockEvalContext) State() *states.SyncState { - c.StateCalled = true - return c.StateState -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_count.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_count.go deleted file mode 100644 index 7d6fa491..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_count.go +++ /dev/null @@ -1,120 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/gocty" -) - -// evaluateResourceCountExpression is our standard mechanism for interpreting an -// expression given for a "count" argument on a resource. This should be called -// from the DynamicExpand of a node representing a resource in order to -// determine the final count value. -// -// If the result is zero or positive and no error diagnostics are returned, then -// the result is the literal count value to use. -// -// If the result is -1, this indicates that the given expression is nil and so -// the "count" behavior should not be enabled for this resource at all. -// -// If error diagnostics are returned then the result is always the meaningless -// placeholder value -1. -func evaluateResourceCountExpression(expr hcl.Expression, ctx EvalContext) (int, tfdiags.Diagnostics) { - count, known, diags := evaluateResourceCountExpressionKnown(expr, ctx) - if !known { - // Currently this is a rather bad outcome from a UX standpoint, since we have - // no real mechanism to deal with this situation and all we can do is produce - // an error message. - // FIXME: In future, implement a built-in mechanism for deferring changes that - // can't yet be predicted, and use it to guide the user through several - // plan/apply steps until the desired configuration is eventually reached. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count argument", - Detail: `The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the count depends on.`, - Subject: expr.Range().Ptr(), - }) - } - return count, diags -} - -// evaluateResourceCountExpressionKnown is like evaluateResourceCountExpression -// except that it handles an unknown result by returning count = 0 and -// a known = false, rather than by reporting the unknown value as an error -// diagnostic. -func evaluateResourceCountExpressionKnown(expr hcl.Expression, ctx EvalContext) (count int, known bool, diags tfdiags.Diagnostics) { - if expr == nil { - return -1, true, nil - } - - countVal, countDiags := ctx.EvaluateExpr(expr, cty.Number, nil) - diags = diags.Append(countDiags) - if diags.HasErrors() { - return -1, true, diags - } - - switch { - case countVal.IsNull(): - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count argument", - Detail: `The given "count" argument value is null. An integer is required.`, - Subject: expr.Range().Ptr(), - }) - return -1, true, diags - case !countVal.IsKnown(): - return 0, false, diags - } - - err := gocty.FromCtyValue(countVal, &count) - if err != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count argument", - Detail: fmt.Sprintf(`The given "count" argument value is unsuitable: %s.`, err), - Subject: expr.Range().Ptr(), - }) - return -1, true, diags - } - if count < 0 { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count argument", - Detail: `The given "count" argument value is unsuitable: negative numbers are not supported.`, - Subject: expr.Range().Ptr(), - }) - return -1, true, diags - } - - return count, true, diags -} - -// fixResourceCountSetTransition is a helper function to fix up the state when a -// resource transitions its "count" from being set to unset or vice-versa, -// treating a 0-key and a no-key instance as aliases for one another across -// the transition. -// -// The correct time to call this function is in the DynamicExpand method for -// a node representing a resource, just after evaluating the count with -// evaluateResourceCountExpression, and before any other analysis of the -// state such as orphan detection. -// -// This function calls methods on the given EvalContext to update the current -// state in-place, if necessary. It is a no-op if there is no count transition -// taking place. -// -// Since the state is modified in-place, this function must take a writer lock -// on the state. The caller must therefore not also be holding a state lock, -// or this function will block forever awaiting the lock. -func fixResourceCountSetTransition(ctx EvalContext, addr addrs.AbsResource, countEnabled bool) { - state := ctx.State() - changed := state.MaybeFixUpResourceInstanceAddressForCount(addr, countEnabled) - if changed { - log.Printf("[TRACE] renamed first %s instance in transient state due to count argument change", addr) - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_count_boundary.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_count_boundary.go deleted file mode 100644 index aac38063..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_count_boundary.go +++ /dev/null @@ -1,77 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" -) - -// EvalCountFixZeroOneBoundaryGlobal is an EvalNode that fixes up the state -// when there is a resource count with zero/one boundary, i.e. fixing -// a resource named "aws_instance.foo" to "aws_instance.foo.0" and vice-versa. -// -// This works on the global state. -type EvalCountFixZeroOneBoundaryGlobal struct { - Config *configs.Config -} - -// TODO: test -func (n *EvalCountFixZeroOneBoundaryGlobal) Eval(ctx EvalContext) (interface{}, error) { - // We'll temporarily lock the state to grab the modules, then work on each - // one separately while taking a lock again for each separate resource. - // This means that if another caller concurrently adds a module here while - // we're working then we won't update it, but that's no worse than the - // concurrent writer blocking for our entire fixup process and _then_ - // adding a new module, and in practice the graph node associated with - // this eval depends on everything else in the graph anyway, so there - // should not be concurrent writers. - state := ctx.State().Lock() - moduleAddrs := make([]addrs.ModuleInstance, 0, len(state.Modules)) - for _, m := range state.Modules { - moduleAddrs = append(moduleAddrs, m.Addr) - } - ctx.State().Unlock() - - for _, addr := range moduleAddrs { - cfg := n.Config.DescendentForInstance(addr) - if cfg == nil { - log.Printf("[WARN] Not fixing up EachModes for %s because it has no config", addr) - continue - } - if err := n.fixModule(ctx, addr); err != nil { - return nil, err - } - } - - return nil, nil -} - -func (n *EvalCountFixZeroOneBoundaryGlobal) fixModule(ctx EvalContext, moduleAddr addrs.ModuleInstance) error { - ms := ctx.State().Module(moduleAddr) - cfg := n.Config.DescendentForInstance(moduleAddr) - if ms == nil { - // Theoretically possible for a concurrent writer to delete a module - // while we're running, but in practice the graph node that called us - // depends on everything else in the graph and so there can never - // be a concurrent writer. - return fmt.Errorf("[WARN] no state found for %s while trying to fix up EachModes", moduleAddr) - } - if cfg == nil { - return fmt.Errorf("[WARN] no config found for %s while trying to fix up EachModes", moduleAddr) - } - - for _, r := range ms.Resources { - addr := r.Addr.Absolute(moduleAddr) - rCfg := cfg.Module.ResourceByAddr(r.Addr) - if rCfg == nil { - log.Printf("[WARN] Not fixing up EachModes for %s because it has no config", addr) - continue - } - hasCount := rCfg.Count != nil - fixResourceCountSetTransition(ctx, addr, hasCount) - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_diff.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_diff.go deleted file mode 100644 index d6f51c95..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_diff.go +++ /dev/null @@ -1,783 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - "strings" - - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// EvalCheckPlannedChange is an EvalNode implementation that produces errors -// if the _actual_ expected value is not compatible with what was recorded -// in the plan. -// -// Errors here are most often indicative of a bug in the provider, so our -// error messages will report with that in mind. It's also possible that -// there's a bug in Terraform's Core's own "proposed new value" code in -// EvalDiff. -type EvalCheckPlannedChange struct { - Addr addrs.ResourceInstance - ProviderAddr addrs.AbsProviderConfig - ProviderSchema **ProviderSchema - - // We take ResourceInstanceChange objects here just because that's what's - // convenient to pass in from the evaltree implementation, but we really - // only look at the "After" value of each change. - Planned, Actual **plans.ResourceInstanceChange -} - -func (n *EvalCheckPlannedChange) Eval(ctx EvalContext) (interface{}, error) { - providerSchema := *n.ProviderSchema - plannedChange := *n.Planned - actualChange := *n.Actual - - schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // Should be caught during validation, so we don't bother with a pretty error here - return nil, fmt.Errorf("provider does not support %q", n.Addr.Resource.Type) - } - - var diags tfdiags.Diagnostics - absAddr := n.Addr.Absolute(ctx.Path()) - - log.Printf("[TRACE] EvalCheckPlannedChange: Verifying that actual change (action %s) matches planned change (action %s)", actualChange.Action, plannedChange.Action) - - if plannedChange.Action != actualChange.Action { - switch { - case plannedChange.Action == plans.Update && actualChange.Action == plans.NoOp: - // It's okay for an update to become a NoOp once we've filled in - // all of the unknown values, since the final values might actually - // match what was there before after all. - log.Printf("[DEBUG] After incorporating new values learned so far during apply, %s change has become NoOp", absAddr) - default: - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced inconsistent final plan", - fmt.Sprintf( - "When expanding the plan for %s to include new values learned so far during apply, provider %q changed the planned action from %s to %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - absAddr, n.ProviderAddr.ProviderConfig.Type, - plannedChange.Action, actualChange.Action, - ), - )) - } - } - - errs := objchange.AssertObjectCompatible(schema, plannedChange.After, actualChange.After) - for _, err := range errs { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced inconsistent final plan", - fmt.Sprintf( - "When expanding the plan for %s to include new values learned so far during apply, provider %q produced an invalid new value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - absAddr, n.ProviderAddr.ProviderConfig.Type, tfdiags.FormatError(err), - ), - )) - } - return nil, diags.Err() -} - -// EvalDiff is an EvalNode implementation that detects changes for a given -// resource instance. -type EvalDiff struct { - Addr addrs.ResourceInstance - Config *configs.Resource - Provider *providers.Interface - ProviderAddr addrs.AbsProviderConfig - ProviderSchema **ProviderSchema - State **states.ResourceInstanceObject - PreviousDiff **plans.ResourceInstanceChange - - // CreateBeforeDestroy is set if either the resource's own config sets - // create_before_destroy explicitly or if dependencies have forced the - // resource to be handled as create_before_destroy in order to avoid - // a dependency cycle. - CreateBeforeDestroy bool - - OutputChange **plans.ResourceInstanceChange - OutputValue *cty.Value - OutputState **states.ResourceInstanceObject - - Stub bool -} - -// TODO: test -func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) { - state := *n.State - config := *n.Config - provider := *n.Provider - providerSchema := *n.ProviderSchema - - if providerSchema == nil { - return nil, fmt.Errorf("provider schema is unavailable for %s", n.Addr) - } - if n.ProviderAddr.ProviderConfig.Type == "" { - panic(fmt.Sprintf("EvalDiff for %s does not have ProviderAddr set", n.Addr.Absolute(ctx.Path()))) - } - - var diags tfdiags.Diagnostics - - // Evaluate the configuration - schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // Should be caught during validation, so we don't bother with a pretty error here - return nil, fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Type) - } - forEach, _ := evaluateResourceForEachExpression(n.Config.ForEach, ctx) - keyData := EvalDataForInstanceKey(n.Addr.Key, forEach) - configVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema, nil, keyData) - diags = diags.Append(configDiags) - if configDiags.HasErrors() { - return nil, diags.Err() - } - - absAddr := n.Addr.Absolute(ctx.Path()) - var priorVal cty.Value - var priorValTainted cty.Value - var priorPrivate []byte - if state != nil { - if state.Status != states.ObjectTainted { - priorVal = state.Value - priorPrivate = state.Private - } else { - // If the prior state is tainted then we'll proceed below like - // we're creating an entirely new object, but then turn it into - // a synthetic "Replace" change at the end, creating the same - // result as if the provider had marked at least one argument - // change as "requires replacement". - priorValTainted = state.Value - priorVal = cty.NullVal(schema.ImpliedType()) - } - } else { - priorVal = cty.NullVal(schema.ImpliedType()) - } - - proposedNewVal := objchange.ProposedNewObject(schema, priorVal, configVal) - - // Call pre-diff hook - if !n.Stub { - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PreDiff(absAddr, states.CurrentGen, priorVal, proposedNewVal) - }) - if err != nil { - return nil, err - } - } - - log.Printf("[TRACE] Re-validating config for %q", n.Addr.Absolute(ctx.Path())) - // Allow the provider to validate the final set of values. - // The config was statically validated early on, but there may have been - // unknown values which the provider could not validate at the time. - validateResp := provider.ValidateResourceTypeConfig( - providers.ValidateResourceTypeConfigRequest{ - TypeName: n.Addr.Resource.Type, - Config: configVal, - }, - ) - if validateResp.Diagnostics.HasErrors() { - return nil, validateResp.Diagnostics.InConfigBody(config.Config).Err() - } - - // The provider gets an opportunity to customize the proposed new value, - // which in turn produces the _planned_ new value. But before - // we send back this information, we need to process ignore_changes - // so that CustomizeDiff will not act on them - var ignoreChangeDiags tfdiags.Diagnostics - proposedNewVal, ignoreChangeDiags = n.processIgnoreChanges(priorVal, proposedNewVal) - diags = diags.Append(ignoreChangeDiags) - if ignoreChangeDiags.HasErrors() { - return nil, diags.Err() - } - - resp := provider.PlanResourceChange(providers.PlanResourceChangeRequest{ - TypeName: n.Addr.Resource.Type, - Config: configVal, - PriorState: priorVal, - ProposedNewState: proposedNewVal, - PriorPrivate: priorPrivate, - }) - diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config)) - if diags.HasErrors() { - return nil, diags.Err() - } - - plannedNewVal := resp.PlannedState - plannedPrivate := resp.PlannedPrivate - - if plannedNewVal == cty.NilVal { - // Should never happen. Since real-world providers return via RPC a nil - // is always a bug in the client-side stub. This is more likely caused - // by an incompletely-configured mock provider in tests, though. - panic(fmt.Sprintf("PlanResourceChange of %s produced nil value", absAddr.String())) - } - - // We allow the planned new value to disagree with configuration _values_ - // here, since that allows the provider to do special logic like a - // DiffSuppressFunc, but we still require that the provider produces - // a value whose type conforms to the schema. - for _, err := range plannedNewVal.Type().TestConformance(schema.ImpliedType()) { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced invalid plan", - fmt.Sprintf( - "Provider %q planned an invalid value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.ProviderConfig.Type, tfdiags.FormatErrorPrefixed(err, absAddr.String()), - ), - )) - } - if diags.HasErrors() { - return nil, diags.Err() - } - - if errs := objchange.AssertPlanValid(schema, priorVal, configVal, plannedNewVal); len(errs) > 0 { - if resp.LegacyTypeSystem { - // The shimming of the old type system in the legacy SDK is not precise - // enough to pass this consistency check, so we'll give it a pass here, - // but we will generate a warning about it so that we are more likely - // to notice in the logs if an inconsistency beyond the type system - // leads to a downstream provider failure. - var buf strings.Builder - fmt.Fprintf(&buf, "[WARN] Provider %q produced an invalid plan for %s, but we are tolerating it because it is using the legacy plugin SDK.\n The following problems may be the cause of any confusing errors from downstream operations:", n.ProviderAddr.ProviderConfig.Type, absAddr) - for _, err := range errs { - fmt.Fprintf(&buf, "\n - %s", tfdiags.FormatError(err)) - } - log.Print(buf.String()) - } else { - for _, err := range errs { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced invalid plan", - fmt.Sprintf( - "Provider %q planned an invalid value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.ProviderConfig.Type, tfdiags.FormatErrorPrefixed(err, absAddr.String()), - ), - )) - } - return nil, diags.Err() - } - } - - // TODO: We should be able to remove this repeat of processing ignored changes - // after the plan, which helps providers relying on old behavior "just work" - // in the next major version, such that we can be stricter about ignore_changes - // values - plannedNewVal, ignoreChangeDiags = n.processIgnoreChanges(priorVal, plannedNewVal) - diags = diags.Append(ignoreChangeDiags) - if ignoreChangeDiags.HasErrors() { - return nil, diags.Err() - } - - // The provider produces a list of paths to attributes whose changes mean - // that we must replace rather than update an existing remote object. - // However, we only need to do that if the identified attributes _have_ - // actually changed -- particularly after we may have undone some of the - // changes in processIgnoreChanges -- so now we'll filter that list to - // include only where changes are detected. - reqRep := cty.NewPathSet() - if len(resp.RequiresReplace) > 0 { - for _, path := range resp.RequiresReplace { - if priorVal.IsNull() { - // If prior is null then we don't expect any RequiresReplace at all, - // because this is a Create action. - continue - } - - priorChangedVal, priorPathDiags := hcl.ApplyPath(priorVal, path, nil) - plannedChangedVal, plannedPathDiags := hcl.ApplyPath(plannedNewVal, path, nil) - if plannedPathDiags.HasErrors() && priorPathDiags.HasErrors() { - // This means the path was invalid in both the prior and new - // values, which is an error with the provider itself. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced invalid plan", - fmt.Sprintf( - "Provider %q has indicated \"requires replacement\" on %s for a non-existent attribute path %#v.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.ProviderConfig.Type, absAddr, path, - ), - )) - continue - } - - // Make sure we have valid Values for both values. - // Note: if the opposing value was of the type - // cty.DynamicPseudoType, the type assigned here may not exactly - // match the schema. This is fine here, since we're only going to - // check for equality, but if the NullVal is to be used, we need to - // check the schema for th true type. - switch { - case priorChangedVal == cty.NilVal && plannedChangedVal == cty.NilVal: - // this should never happen without ApplyPath errors above - panic("requires replace path returned 2 nil values") - case priorChangedVal == cty.NilVal: - priorChangedVal = cty.NullVal(plannedChangedVal.Type()) - case plannedChangedVal == cty.NilVal: - plannedChangedVal = cty.NullVal(priorChangedVal.Type()) - } - - eqV := plannedChangedVal.Equals(priorChangedVal) - if !eqV.IsKnown() || eqV.False() { - reqRep.Add(path) - } - } - if diags.HasErrors() { - return nil, diags.Err() - } - } - - eqV := plannedNewVal.Equals(priorVal) - eq := eqV.IsKnown() && eqV.True() - - var action plans.Action - switch { - case priorVal.IsNull(): - action = plans.Create - case eq: - action = plans.NoOp - case !reqRep.Empty(): - // If there are any "requires replace" paths left _after our filtering - // above_ then this is a replace action. - if n.CreateBeforeDestroy { - action = plans.CreateThenDelete - } else { - action = plans.DeleteThenCreate - } - default: - action = plans.Update - // "Delete" is never chosen here, because deletion plans are always - // created more directly elsewhere, such as in "orphan" handling. - } - - if action.IsReplace() { - // In this strange situation we want to produce a change object that - // shows our real prior object but has a _new_ object that is built - // from a null prior object, since we're going to delete the one - // that has all the computed values on it. - // - // Therefore we'll ask the provider to plan again here, giving it - // a null object for the prior, and then we'll meld that with the - // _actual_ prior state to produce a correctly-shaped replace change. - // The resulting change should show any computed attributes changing - // from known prior values to unknown values, unless the provider is - // able to predict new values for any of these computed attributes. - nullPriorVal := cty.NullVal(schema.ImpliedType()) - - // create a new proposed value from the null state and the config - proposedNewVal = objchange.ProposedNewObject(schema, nullPriorVal, configVal) - - resp = provider.PlanResourceChange(providers.PlanResourceChangeRequest{ - TypeName: n.Addr.Resource.Type, - Config: configVal, - PriorState: nullPriorVal, - ProposedNewState: proposedNewVal, - PriorPrivate: plannedPrivate, - }) - // We need to tread carefully here, since if there are any warnings - // in here they probably also came out of our previous call to - // PlanResourceChange above, and so we don't want to repeat them. - // Consequently, we break from the usual pattern here and only - // append these new diagnostics if there's at least one error inside. - if resp.Diagnostics.HasErrors() { - diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config)) - return nil, diags.Err() - } - plannedNewVal = resp.PlannedState - plannedPrivate = resp.PlannedPrivate - for _, err := range plannedNewVal.Type().TestConformance(schema.ImpliedType()) { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced invalid plan", - fmt.Sprintf( - "Provider %q planned an invalid value for %s%s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.ProviderConfig.Type, absAddr, tfdiags.FormatError(err), - ), - )) - } - if diags.HasErrors() { - return nil, diags.Err() - } - } - - // If our prior value was tainted then we actually want this to appear - // as a replace change, even though so far we've been treating it as a - // create. - if action == plans.Create && priorValTainted != cty.NilVal { - if n.CreateBeforeDestroy { - action = plans.CreateThenDelete - } else { - action = plans.DeleteThenCreate - } - priorVal = priorValTainted - } - - // As a special case, if we have a previous diff (presumably from the plan - // phases, whereas we're now in the apply phase) and it was for a replace, - // we've already deleted the original object from state by the time we - // get here and so we would've ended up with a _create_ action this time, - // which we now need to paper over to get a result consistent with what - // we originally intended. - if n.PreviousDiff != nil { - prevChange := *n.PreviousDiff - if prevChange.Action.IsReplace() && action == plans.Create { - log.Printf("[TRACE] EvalDiff: %s treating Create change as %s change to match with earlier plan", absAddr, prevChange.Action) - action = prevChange.Action - priorVal = prevChange.Before - } - } - - // Call post-refresh hook - if !n.Stub { - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostDiff(absAddr, states.CurrentGen, action, priorVal, plannedNewVal) - }) - if err != nil { - return nil, err - } - } - - // Update our output if we care - if n.OutputChange != nil { - *n.OutputChange = &plans.ResourceInstanceChange{ - Addr: absAddr, - Private: plannedPrivate, - ProviderAddr: n.ProviderAddr, - Change: plans.Change{ - Action: action, - Before: priorVal, - After: plannedNewVal, - }, - RequiredReplace: reqRep, - } - } - - if n.OutputValue != nil { - *n.OutputValue = configVal - } - - // Update the state if we care - if n.OutputState != nil { - *n.OutputState = &states.ResourceInstanceObject{ - // We use the special "planned" status here to note that this - // object's value is not yet complete. Objects with this status - // cannot be used during expression evaluation, so the caller - // must _also_ record the returned change in the active plan, - // which the expression evaluator will use in preference to this - // incomplete value recorded in the state. - Status: states.ObjectPlanned, - Value: plannedNewVal, - Private: plannedPrivate, - } - } - - return nil, nil -} - -func (n *EvalDiff) processIgnoreChanges(prior, proposed cty.Value) (cty.Value, tfdiags.Diagnostics) { - // ignore_changes only applies when an object already exists, since we - // can't ignore changes to a thing we've not created yet. - if prior.IsNull() { - return proposed, nil - } - - ignoreChanges := n.Config.Managed.IgnoreChanges - ignoreAll := n.Config.Managed.IgnoreAllChanges - - if len(ignoreChanges) == 0 && !ignoreAll { - return proposed, nil - } - if ignoreAll { - return prior, nil - } - if prior.IsNull() || proposed.IsNull() { - // Ignore changes doesn't apply when we're creating for the first time. - // Proposed should never be null here, but if it is then we'll just let it be. - return proposed, nil - } - - return processIgnoreChangesIndividual(prior, proposed, ignoreChanges) -} - -func processIgnoreChangesIndividual(prior, proposed cty.Value, ignoreChanges []hcl.Traversal) (cty.Value, tfdiags.Diagnostics) { - // When we walk below we will be using cty.Path values for comparison, so - // we'll convert our traversals here so we can compare more easily. - ignoreChangesPath := make([]cty.Path, len(ignoreChanges)) - for i, traversal := range ignoreChanges { - path := make(cty.Path, len(traversal)) - for si, step := range traversal { - switch ts := step.(type) { - case hcl.TraverseRoot: - path[si] = cty.GetAttrStep{ - Name: ts.Name, - } - case hcl.TraverseAttr: - path[si] = cty.GetAttrStep{ - Name: ts.Name, - } - case hcl.TraverseIndex: - path[si] = cty.IndexStep{ - Key: ts.Key, - } - default: - panic(fmt.Sprintf("unsupported traversal step %#v", step)) - } - } - ignoreChangesPath[i] = path - } - - var diags tfdiags.Diagnostics - ret, _ := cty.Transform(proposed, func(path cty.Path, v cty.Value) (cty.Value, error) { - // First we must see if this is a path that's being ignored at all. - // We're looking for an exact match here because this walk will visit - // leaf values first and then their containers, and we want to do - // the "ignore" transform once we reach the point indicated, throwing - // away any deeper values we already produced at that point. - var ignoreTraversal hcl.Traversal - for i, candidate := range ignoreChangesPath { - if path.Equals(candidate) { - ignoreTraversal = ignoreChanges[i] - } - } - if ignoreTraversal == nil { - return v, nil - } - - // If we're able to follow the same path through the prior value, - // we'll take the value there instead, effectively undoing the - // change that was planned. - priorV, diags := hcl.ApplyPath(prior, path, nil) - if diags.HasErrors() { - // We just ignore the errors and move on here, since we assume it's - // just because the prior value was a slightly-different shape. - // It could potentially also be that the traversal doesn't match - // the schema, but we should've caught that during the validate - // walk if so. - return v, nil - } - return priorV, nil - }) - return ret, diags -} - -// EvalDiffDestroy is an EvalNode implementation that returns a plain -// destroy diff. -type EvalDiffDestroy struct { - Addr addrs.ResourceInstance - DeposedKey states.DeposedKey - State **states.ResourceInstanceObject - ProviderAddr addrs.AbsProviderConfig - - Output **plans.ResourceInstanceChange - OutputState **states.ResourceInstanceObject -} - -// TODO: test -func (n *EvalDiffDestroy) Eval(ctx EvalContext) (interface{}, error) { - absAddr := n.Addr.Absolute(ctx.Path()) - state := *n.State - - if n.ProviderAddr.ProviderConfig.Type == "" { - if n.DeposedKey == "" { - panic(fmt.Sprintf("EvalDiffDestroy for %s does not have ProviderAddr set", absAddr)) - } else { - panic(fmt.Sprintf("EvalDiffDestroy for %s (deposed %s) does not have ProviderAddr set", absAddr, n.DeposedKey)) - } - } - - // If there is no state or our attributes object is null then we're already - // destroyed. - if state == nil || state.Value.IsNull() { - return nil, nil - } - - // Call pre-diff hook - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PreDiff( - absAddr, n.DeposedKey.Generation(), - state.Value, - cty.NullVal(cty.DynamicPseudoType), - ) - }) - if err != nil { - return nil, err - } - - // Change is always the same for a destroy. We don't need the provider's - // help for this one. - // TODO: Should we give the provider an opportunity to veto this? - change := &plans.ResourceInstanceChange{ - Addr: absAddr, - DeposedKey: n.DeposedKey, - Change: plans.Change{ - Action: plans.Delete, - Before: state.Value, - After: cty.NullVal(cty.DynamicPseudoType), - }, - Private: state.Private, - ProviderAddr: n.ProviderAddr, - } - - // Call post-diff hook - err = ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostDiff( - absAddr, - n.DeposedKey.Generation(), - change.Action, - change.Before, - change.After, - ) - }) - if err != nil { - return nil, err - } - - // Update our output - *n.Output = change - - if n.OutputState != nil { - // Record our proposed new state, which is nil because we're destroying. - *n.OutputState = nil - } - - return nil, nil -} - -// EvalReduceDiff is an EvalNode implementation that takes a planned resource -// instance change as might be produced by EvalDiff or EvalDiffDestroy and -// "simplifies" it to a single atomic action to be performed by a specific -// graph node. -// -// Callers must specify whether they are a destroy node or a regular apply -// node. If the result is NoOp then the given change requires no action for -// the specific graph node calling this and so evaluation of the that graph -// node should exit early and take no action. -// -// The object written to OutChange may either be identical to InChange or -// a new change object derived from InChange. Because of the former case, the -// caller must not mutate the object returned in OutChange. -type EvalReduceDiff struct { - Addr addrs.ResourceInstance - InChange **plans.ResourceInstanceChange - Destroy bool - OutChange **plans.ResourceInstanceChange -} - -// TODO: test -func (n *EvalReduceDiff) Eval(ctx EvalContext) (interface{}, error) { - in := *n.InChange - out := in.Simplify(n.Destroy) - if n.OutChange != nil { - *n.OutChange = out - } - if out.Action != in.Action { - if n.Destroy { - log.Printf("[TRACE] EvalReduceDiff: %s change simplified from %s to %s for destroy node", n.Addr, in.Action, out.Action) - } else { - log.Printf("[TRACE] EvalReduceDiff: %s change simplified from %s to %s for apply node", n.Addr, in.Action, out.Action) - } - } - return nil, nil -} - -// EvalReadDiff is an EvalNode implementation that retrieves the planned -// change for a particular resource instance object. -type EvalReadDiff struct { - Addr addrs.ResourceInstance - DeposedKey states.DeposedKey - ProviderSchema **ProviderSchema - Change **plans.ResourceInstanceChange -} - -func (n *EvalReadDiff) Eval(ctx EvalContext) (interface{}, error) { - providerSchema := *n.ProviderSchema - changes := ctx.Changes() - addr := n.Addr.Absolute(ctx.Path()) - - schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // Should be caught during validation, so we don't bother with a pretty error here - return nil, fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Type) - } - - gen := states.CurrentGen - if n.DeposedKey != states.NotDeposed { - gen = n.DeposedKey - } - csrc := changes.GetResourceInstanceChange(addr, gen) - if csrc == nil { - log.Printf("[TRACE] EvalReadDiff: No planned change recorded for %s", addr) - return nil, nil - } - - change, err := csrc.Decode(schema.ImpliedType()) - if err != nil { - return nil, fmt.Errorf("failed to decode planned changes for %s: %s", addr, err) - } - if n.Change != nil { - *n.Change = change - } - - log.Printf("[TRACE] EvalReadDiff: Read %s change from plan for %s", change.Action, addr) - - return nil, nil -} - -// EvalWriteDiff is an EvalNode implementation that saves a planned change -// for an instance object into the set of global planned changes. -type EvalWriteDiff struct { - Addr addrs.ResourceInstance - DeposedKey states.DeposedKey - ProviderSchema **ProviderSchema - Change **plans.ResourceInstanceChange -} - -// TODO: test -func (n *EvalWriteDiff) Eval(ctx EvalContext) (interface{}, error) { - changes := ctx.Changes() - addr := n.Addr.Absolute(ctx.Path()) - if n.Change == nil || *n.Change == nil { - // Caller sets nil to indicate that we need to remove a change from - // the set of changes. - gen := states.CurrentGen - if n.DeposedKey != states.NotDeposed { - gen = n.DeposedKey - } - changes.RemoveResourceInstanceChange(addr, gen) - return nil, nil - } - - providerSchema := *n.ProviderSchema - change := *n.Change - - if change.Addr.String() != addr.String() || change.DeposedKey != n.DeposedKey { - // Should never happen, and indicates a bug in the caller. - panic("inconsistent address and/or deposed key in EvalWriteDiff") - } - - schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // Should be caught during validation, so we don't bother with a pretty error here - return nil, fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Type) - } - - csrc, err := change.Encode(schema.ImpliedType()) - if err != nil { - return nil, fmt.Errorf("failed to encode planned changes for %s: %s", addr, err) - } - - changes.AppendResourceInstanceChange(csrc) - if n.DeposedKey == states.NotDeposed { - log.Printf("[TRACE] EvalWriteDiff: recorded %s change for %s", change.Action, addr) - } else { - log.Printf("[TRACE] EvalWriteDiff: recorded %s change for %s deposed object %s", change.Action, addr, n.DeposedKey) - } - - return nil, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_error.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_error.go deleted file mode 100644 index 470f798b..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_error.go +++ /dev/null @@ -1,20 +0,0 @@ -package terraform - -// EvalReturnError is an EvalNode implementation that returns an -// error if it is present. -// -// This is useful for scenarios where an error has been captured by -// another EvalNode (like EvalApply) for special EvalTree-based error -// handling, and that handling has completed, so the error should be -// returned normally. -type EvalReturnError struct { - Error *error -} - -func (n *EvalReturnError) Eval(ctx EvalContext) (interface{}, error) { - if n.Error == nil { - return nil, nil - } - - return nil, *n.Error -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_filter.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_filter.go deleted file mode 100644 index 711c625c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_filter.go +++ /dev/null @@ -1,25 +0,0 @@ -package terraform - -// EvalNodeFilterFunc is the callback used to replace a node with -// another to node. To not do the replacement, just return the input node. -type EvalNodeFilterFunc func(EvalNode) EvalNode - -// EvalNodeFilterable is an interface that can be implemented by -// EvalNodes to allow filtering of sub-elements. Note that this isn't -// a common thing to implement and you probably don't need it. -type EvalNodeFilterable interface { - EvalNode - Filter(EvalNodeFilterFunc) -} - -// EvalFilter runs the filter on the given node and returns the -// final filtered value. This should be called rather than checking -// the EvalNode directly since this will properly handle EvalNodeFilterables. -func EvalFilter(node EvalNode, fn EvalNodeFilterFunc) EvalNode { - if f, ok := node.(EvalNodeFilterable); ok { - f.Filter(fn) - return node - } - - return fn(node) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_filter_operation.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_filter_operation.go deleted file mode 100644 index 1a55f024..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_filter_operation.go +++ /dev/null @@ -1,49 +0,0 @@ -package terraform - -// EvalNodeOpFilterable is an interface that EvalNodes can implement -// to be filterable by the operation that is being run on Terraform. -type EvalNodeOpFilterable interface { - IncludeInOp(walkOperation) bool -} - -// EvalNodeFilterOp returns a filter function that filters nodes that -// include themselves in specific operations. -func EvalNodeFilterOp(op walkOperation) EvalNodeFilterFunc { - return func(n EvalNode) EvalNode { - include := true - if of, ok := n.(EvalNodeOpFilterable); ok { - include = of.IncludeInOp(op) - } - if include { - return n - } - - return EvalNoop{} - } -} - -// EvalOpFilter is an EvalNode implementation that is a proxy to -// another node but filters based on the operation. -type EvalOpFilter struct { - // Ops is the list of operations to include this node in. - Ops []walkOperation - - // Node is the node to execute - Node EvalNode -} - -// TODO: test -func (n *EvalOpFilter) Eval(ctx EvalContext) (interface{}, error) { - return EvalRaw(n.Node, ctx) -} - -// EvalNodeOpFilterable impl. -func (n *EvalOpFilter) IncludeInOp(op walkOperation) bool { - for _, v := range n.Ops { - if v == op { - return true - } - } - - return false -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_for_each.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_for_each.go deleted file mode 100644 index a63389a9..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_for_each.go +++ /dev/null @@ -1,95 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - "github.com/zclconf/go-cty/cty" -) - -// evaluateResourceForEachExpression interprets a "for_each" argument on a resource. -// -// Returns a cty.Value map, and diagnostics if necessary. It will return nil if -// the expression is nil, and is used to distinguish between an unset for_each and an -// empty map -func evaluateResourceForEachExpression(expr hcl.Expression, ctx EvalContext) (forEach map[string]cty.Value, diags tfdiags.Diagnostics) { - forEachMap, known, diags := evaluateResourceForEachExpressionKnown(expr, ctx) - if !known { - // Attach a diag as we do with count, with the same downsides - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid for_each argument", - Detail: `The "for_each" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the for_each depends on.`, - Subject: expr.Range().Ptr(), - }) - } - return forEachMap, diags -} - -// evaluateResourceForEachExpressionKnown is like evaluateResourceForEachExpression -// except that it handles an unknown result by returning an empty map and -// a known = false, rather than by reporting the unknown value as an error -// diagnostic. -func evaluateResourceForEachExpressionKnown(expr hcl.Expression, ctx EvalContext) (forEach map[string]cty.Value, known bool, diags tfdiags.Diagnostics) { - if expr == nil { - return nil, true, nil - } - - forEachVal, forEachDiags := ctx.EvaluateExpr(expr, cty.DynamicPseudoType, nil) - diags = diags.Append(forEachDiags) - if diags.HasErrors() { - return nil, true, diags - } - - switch { - case forEachVal.IsNull(): - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid for_each argument", - Detail: `The given "for_each" argument value is unsuitable: the given "for_each" argument value is null. A map, or set of strings is allowed.`, - Subject: expr.Range().Ptr(), - }) - return nil, true, diags - case !forEachVal.IsKnown(): - return map[string]cty.Value{}, false, diags - } - - if !forEachVal.CanIterateElements() || forEachVal.Type().IsListType() || forEachVal.Type().IsTupleType() { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid for_each argument", - Detail: fmt.Sprintf(`The given "for_each" argument value is unsuitable: the "for_each" argument must be a map, or set of strings, and you have provided a value of type %s.`, forEachVal.Type().FriendlyName()), - Subject: expr.Range().Ptr(), - }) - return nil, true, diags - } - - // If the map is empty ({}), return an empty map, because cty will return nil when representing {} AsValueMap - // This also covers an empty set (toset([])) - if forEachVal.LengthInt() == 0 { - return map[string]cty.Value{}, true, diags - } - - if forEachVal.Type().IsSetType() { - if forEachVal.Type().ElementType() != cty.String { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid for_each set argument", - Detail: fmt.Sprintf(`The given "for_each" argument value is unsuitable: "for_each" supports maps and sets of strings, but you have provided a set containing type %s.`, forEachVal.Type().ElementType().FriendlyName()), - Subject: expr.Range().Ptr(), - }) - return nil, true, diags - } - - // A set may contain unknown values that must be - // discovered by checking with IsWhollyKnown (which iterates through the - // structure), while for maps in cty, keys can never be unknown or null, - // thus the earlier IsKnown check suffices for maps - if !forEachVal.IsWhollyKnown() { - return map[string]cty.Value{}, false, diags - } - } - - return forEachVal.AsValueMap(), true, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_if.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_if.go deleted file mode 100644 index d6b46a1f..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_if.go +++ /dev/null @@ -1,26 +0,0 @@ -package terraform - -// EvalIf is an EvalNode that is a conditional. -type EvalIf struct { - If func(EvalContext) (bool, error) - Then EvalNode - Else EvalNode -} - -// TODO: test -func (n *EvalIf) Eval(ctx EvalContext) (interface{}, error) { - yes, err := n.If(ctx) - if err != nil { - return nil, err - } - - if yes { - return EvalRaw(n.Then, ctx) - } else { - if n.Else != nil { - return EvalRaw(n.Else, ctx) - } - } - - return nil, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_import_state.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_import_state.go deleted file mode 100644 index 25a2aae0..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_import_state.go +++ /dev/null @@ -1,95 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// EvalImportState is an EvalNode implementation that performs an -// ImportState operation on a provider. This will return the imported -// states but won't modify any actual state. -type EvalImportState struct { - Addr addrs.ResourceInstance - Provider *providers.Interface - ID string - Output *[]providers.ImportedResource -} - -// TODO: test -func (n *EvalImportState) Eval(ctx EvalContext) (interface{}, error) { - absAddr := n.Addr.Absolute(ctx.Path()) - provider := *n.Provider - var diags tfdiags.Diagnostics - - { - // Call pre-import hook - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PreImportState(absAddr, n.ID) - }) - if err != nil { - return nil, err - } - } - - resp := provider.ImportResourceState(providers.ImportResourceStateRequest{ - TypeName: n.Addr.Resource.Type, - ID: n.ID, - }) - diags = diags.Append(resp.Diagnostics) - if diags.HasErrors() { - return nil, diags.Err() - } - - imported := resp.ImportedResources - - for _, obj := range imported { - log.Printf("[TRACE] EvalImportState: import %s %q produced instance object of type %s", absAddr.String(), n.ID, obj.TypeName) - } - - if n.Output != nil { - *n.Output = imported - } - - { - // Call post-import hook - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostImportState(absAddr, imported) - }) - if err != nil { - return nil, err - } - } - - return nil, nil -} - -// EvalImportStateVerify verifies the state after ImportState and -// after the refresh to make sure it is non-nil and valid. -type EvalImportStateVerify struct { - Addr addrs.ResourceInstance - State **states.ResourceInstanceObject -} - -// TODO: test -func (n *EvalImportStateVerify) Eval(ctx EvalContext) (interface{}, error) { - var diags tfdiags.Diagnostics - - state := *n.State - if state.Value.IsNull() { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Cannot import non-existent remote object", - fmt.Sprintf( - "While attempting to import an existing object to %s, the provider detected that no object exists with the given id. Only pre-existing objects can be imported; check that the id is correct and that it is associated with the provider's configured region or endpoint, or use \"terraform apply\" to create a new remote object for this resource.", - n.Addr.String(), - ), - )) - } - - return nil, diags.ErrWithWarnings() -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_lang.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_lang.go deleted file mode 100644 index 5ab6b44f..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_lang.go +++ /dev/null @@ -1,61 +0,0 @@ -package terraform - -import ( - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/zclconf/go-cty/cty" -) - -// EvalConfigBlock is an EvalNode implementation that takes a raw -// configuration block and evaluates any expressions within it. -// -// ExpandedConfig is populated with the result of expanding any "dynamic" -// blocks in the given body, which can be useful for extracting correct source -// location information for specific attributes in the result. -type EvalConfigBlock struct { - Config *hcl.Body - Schema *configschema.Block - SelfAddr addrs.Referenceable - Output *cty.Value - ExpandedConfig *hcl.Body - ContinueOnErr bool -} - -func (n *EvalConfigBlock) Eval(ctx EvalContext) (interface{}, error) { - val, body, diags := ctx.EvaluateBlock(*n.Config, n.Schema, n.SelfAddr, EvalDataForNoInstanceKey) - if diags.HasErrors() && n.ContinueOnErr { - log.Printf("[WARN] Block evaluation failed: %s", diags.Err()) - return nil, EvalEarlyExitError{} - } - - if n.Output != nil { - *n.Output = val - } - if n.ExpandedConfig != nil { - *n.ExpandedConfig = body - } - - return nil, diags.ErrWithWarnings() -} - -// EvalConfigExpr is an EvalNode implementation that takes a raw configuration -// expression and evaluates it. -type EvalConfigExpr struct { - Expr hcl.Expression - SelfAddr addrs.Referenceable - Output *cty.Value -} - -func (n *EvalConfigExpr) Eval(ctx EvalContext) (interface{}, error) { - val, diags := ctx.EvaluateExpr(n.Expr, cty.DynamicPseudoType, n.SelfAddr) - - if n.Output != nil { - *n.Output = val - } - - return nil, diags.ErrWithWarnings() -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_local.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_local.go deleted file mode 100644 index 03101938..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_local.go +++ /dev/null @@ -1,74 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/lang" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// EvalLocal is an EvalNode implementation that evaluates the -// expression for a local value and writes it into a transient part of -// the state. -type EvalLocal struct { - Addr addrs.LocalValue - Expr hcl.Expression -} - -func (n *EvalLocal) Eval(ctx EvalContext) (interface{}, error) { - var diags tfdiags.Diagnostics - - // We ignore diags here because any problems we might find will be found - // again in EvaluateExpr below. - refs, _ := lang.ReferencesInExpr(n.Expr) - for _, ref := range refs { - if ref.Subject == n.Addr { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Self-referencing local value", - Detail: fmt.Sprintf("Local value %s cannot use its own result as part of its expression.", n.Addr), - Subject: ref.SourceRange.ToHCL().Ptr(), - Context: n.Expr.Range().Ptr(), - }) - } - } - if diags.HasErrors() { - return nil, diags.Err() - } - - val, moreDiags := ctx.EvaluateExpr(n.Expr, cty.DynamicPseudoType, nil) - diags = diags.Append(moreDiags) - if moreDiags.HasErrors() { - return nil, diags.Err() - } - - state := ctx.State() - if state == nil { - return nil, fmt.Errorf("cannot write local value to nil state") - } - - state.SetLocalValue(n.Addr.Absolute(ctx.Path()), val) - - return nil, nil -} - -// EvalDeleteLocal is an EvalNode implementation that deletes a Local value -// from the state. Locals aren't persisted, but we don't need to evaluate them -// during destroy. -type EvalDeleteLocal struct { - Addr addrs.LocalValue -} - -func (n *EvalDeleteLocal) Eval(ctx EvalContext) (interface{}, error) { - state := ctx.State() - if state == nil { - return nil, nil - } - - state.RemoveLocalValue(n.Addr.Absolute(ctx.Path())) - return nil, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_noop.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_noop.go deleted file mode 100644 index f4bc8225..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_noop.go +++ /dev/null @@ -1,8 +0,0 @@ -package terraform - -// EvalNoop is an EvalNode that does nothing. -type EvalNoop struct{} - -func (EvalNoop) Eval(EvalContext) (interface{}, error) { - return nil, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_output.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_output.go deleted file mode 100644 index 9f71e92f..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_output.go +++ /dev/null @@ -1,135 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// EvalDeleteOutput is an EvalNode implementation that deletes an output -// from the state. -type EvalDeleteOutput struct { - Addr addrs.OutputValue -} - -// TODO: test -func (n *EvalDeleteOutput) Eval(ctx EvalContext) (interface{}, error) { - state := ctx.State() - if state == nil { - return nil, nil - } - - state.RemoveOutputValue(n.Addr.Absolute(ctx.Path())) - return nil, nil -} - -// EvalWriteOutput is an EvalNode implementation that writes the output -// for the given name to the current state. -type EvalWriteOutput struct { - Addr addrs.OutputValue - Sensitive bool - Expr hcl.Expression - // ContinueOnErr allows interpolation to fail during Input - ContinueOnErr bool -} - -// TODO: test -func (n *EvalWriteOutput) Eval(ctx EvalContext) (interface{}, error) { - addr := n.Addr.Absolute(ctx.Path()) - - // This has to run before we have a state lock, since evaluation also - // reads the state - val, diags := ctx.EvaluateExpr(n.Expr, cty.DynamicPseudoType, nil) - // We'll handle errors below, after we have loaded the module. - - state := ctx.State() - if state == nil { - return nil, nil - } - - changes := ctx.Changes() // may be nil, if we're not working on a changeset - - // handling the interpolation error - if diags.HasErrors() { - if n.ContinueOnErr || flagWarnOutputErrors { - log.Printf("[ERROR] Output interpolation %q failed: %s", n.Addr.Name, diags.Err()) - // if we're continuing, make sure the output is included, and - // marked as unknown. If the evaluator was able to find a type - // for the value in spite of the error then we'll use it. - n.setValue(addr, state, changes, cty.UnknownVal(val.Type())) - return nil, EvalEarlyExitError{} - } - return nil, diags.Err() - } - - n.setValue(addr, state, changes, val) - - return nil, nil -} - -func (n *EvalWriteOutput) setValue(addr addrs.AbsOutputValue, state *states.SyncState, changes *plans.ChangesSync, val cty.Value) { - if val.IsKnown() && !val.IsNull() { - // The state itself doesn't represent unknown values, so we null them - // out here and then we'll save the real unknown value in the planned - // changeset below, if we have one on this graph walk. - log.Printf("[TRACE] EvalWriteOutput: Saving value for %s in state", addr) - stateVal := cty.UnknownAsNull(val) - state.SetOutputValue(addr, stateVal, n.Sensitive) - } else { - log.Printf("[TRACE] EvalWriteOutput: Removing %s from state (it is now null)", addr) - state.RemoveOutputValue(addr) - } - - // If we also have an active changeset then we'll replicate the value in - // there. This is used in preference to the state where present, since it - // *is* able to represent unknowns, while the state cannot. - if changes != nil { - // For the moment we are not properly tracking changes to output - // values, and just marking them always as "Create" or "Destroy" - // actions. A future release will rework the output lifecycle so we - // can track their changes properly, in a similar way to how we work - // with resource instances. - - var change *plans.OutputChange - if !val.IsNull() { - change = &plans.OutputChange{ - Addr: addr, - Sensitive: n.Sensitive, - Change: plans.Change{ - Action: plans.Create, - Before: cty.NullVal(cty.DynamicPseudoType), - After: val, - }, - } - } else { - change = &plans.OutputChange{ - Addr: addr, - Sensitive: n.Sensitive, - Change: plans.Change{ - // This is just a weird placeholder delete action since - // we don't have an actual prior value to indicate. - // FIXME: Generate real planned changes for output values - // that include the old values. - Action: plans.Delete, - Before: cty.NullVal(cty.DynamicPseudoType), - After: cty.NullVal(cty.DynamicPseudoType), - }, - } - } - - cs, err := change.Encode() - if err != nil { - // Should never happen, since we just constructed this right above - panic(fmt.Sprintf("planned change for %s could not be encoded: %s", addr, err)) - } - log.Printf("[TRACE] EvalWriteOutput: Saving %s change for %s in changeset", change.Action, addr) - changes.RemoveOutputChange(addr) // remove any existing planned change, if present - changes.AppendOutputChange(cs) // add the new planned change - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_provider.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_provider.go deleted file mode 100644 index 7440cff7..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_provider.go +++ /dev/null @@ -1,147 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/hcl/v2" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -func buildProviderConfig(ctx EvalContext, addr addrs.ProviderConfig, config *configs.Provider) hcl.Body { - var configBody hcl.Body - if config != nil { - configBody = config.Config - } - - var inputBody hcl.Body - inputConfig := ctx.ProviderInput(addr) - if len(inputConfig) > 0 { - inputBody = configs.SynthBody("", inputConfig) - } - - switch { - case configBody != nil && inputBody != nil: - log.Printf("[TRACE] buildProviderConfig for %s: merging explicit config and input", addr) - // Note that the inputBody is the _base_ here, because configs.MergeBodies - // expects the base have all of the required fields, while these are - // forced to be optional for the override. The input process should - // guarantee that we have a value for each of the required arguments and - // that in practice the sets of attributes in each body will be - // disjoint. - return configs.MergeBodies(inputBody, configBody) - case configBody != nil: - log.Printf("[TRACE] buildProviderConfig for %s: using explicit config only", addr) - return configBody - case inputBody != nil: - log.Printf("[TRACE] buildProviderConfig for %s: using input only", addr) - return inputBody - default: - log.Printf("[TRACE] buildProviderConfig for %s: no configuration at all", addr) - return hcl.EmptyBody() - } -} - -// EvalConfigProvider is an EvalNode implementation that configures -// a provider that is already initialized and retrieved. -type EvalConfigProvider struct { - Addr addrs.ProviderConfig - Provider *providers.Interface - Config *configs.Provider -} - -func (n *EvalConfigProvider) Eval(ctx EvalContext) (interface{}, error) { - if n.Provider == nil { - return nil, fmt.Errorf("EvalConfigProvider Provider is nil") - } - - var diags tfdiags.Diagnostics - provider := *n.Provider - config := n.Config - - configBody := buildProviderConfig(ctx, n.Addr, config) - - resp := provider.GetSchema() - diags = diags.Append(resp.Diagnostics) - if diags.HasErrors() { - return nil, diags.NonFatalErr() - } - - configSchema := resp.Provider.Block - configVal, configBody, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, EvalDataForNoInstanceKey) - diags = diags.Append(evalDiags) - if evalDiags.HasErrors() { - return nil, diags.NonFatalErr() - } - - configDiags := ctx.ConfigureProvider(n.Addr, configVal) - configDiags = configDiags.InConfigBody(configBody) - - return nil, configDiags.ErrWithWarnings() -} - -// EvalInitProvider is an EvalNode implementation that initializes a provider -// and returns nothing. The provider can be retrieved again with the -// EvalGetProvider node. -type EvalInitProvider struct { - TypeName string - Addr addrs.ProviderConfig -} - -func (n *EvalInitProvider) Eval(ctx EvalContext) (interface{}, error) { - return ctx.InitProvider(n.TypeName, n.Addr) -} - -// EvalCloseProvider is an EvalNode implementation that closes provider -// connections that aren't needed anymore. -type EvalCloseProvider struct { - Addr addrs.ProviderConfig -} - -func (n *EvalCloseProvider) Eval(ctx EvalContext) (interface{}, error) { - ctx.CloseProvider(n.Addr) - return nil, nil -} - -// EvalGetProvider is an EvalNode implementation that retrieves an already -// initialized provider instance for the given name. -// -// Unlike most eval nodes, this takes an _absolute_ provider configuration, -// because providers can be passed into and inherited between modules. -// Resource nodes must therefore know the absolute path of the provider they -// will use, which is usually accomplished by implementing -// interface GraphNodeProviderConsumer. -type EvalGetProvider struct { - Addr addrs.AbsProviderConfig - Output *providers.Interface - - // If non-nil, Schema will be updated after eval to refer to the - // schema of the provider. - Schema **ProviderSchema -} - -func (n *EvalGetProvider) Eval(ctx EvalContext) (interface{}, error) { - if n.Addr.ProviderConfig.Type == "" { - // Should never happen - panic("EvalGetProvider used with uninitialized provider configuration address") - } - - result := ctx.Provider(n.Addr) - if result == nil { - return nil, fmt.Errorf("provider %s not initialized", n.Addr) - } - - if n.Output != nil { - *n.Output = result - } - - if n.Schema != nil { - *n.Schema = ctx.ProviderSchema(n.Addr) - } - - return nil, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_provisioner.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_provisioner.go deleted file mode 100644 index 405ce9d0..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_provisioner.go +++ /dev/null @@ -1,55 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/provisioners" -) - -// EvalInitProvisioner is an EvalNode implementation that initializes a provisioner -// and returns nothing. The provisioner can be retrieved again with the -// EvalGetProvisioner node. -type EvalInitProvisioner struct { - Name string -} - -func (n *EvalInitProvisioner) Eval(ctx EvalContext) (interface{}, error) { - return ctx.InitProvisioner(n.Name) -} - -// EvalCloseProvisioner is an EvalNode implementation that closes provisioner -// connections that aren't needed anymore. -type EvalCloseProvisioner struct { - Name string -} - -func (n *EvalCloseProvisioner) Eval(ctx EvalContext) (interface{}, error) { - ctx.CloseProvisioner(n.Name) - return nil, nil -} - -// EvalGetProvisioner is an EvalNode implementation that retrieves an already -// initialized provisioner instance for the given name. -type EvalGetProvisioner struct { - Name string - Output *provisioners.Interface - Schema **configschema.Block -} - -func (n *EvalGetProvisioner) Eval(ctx EvalContext) (interface{}, error) { - result := ctx.Provisioner(n.Name) - if result == nil { - return nil, fmt.Errorf("provisioner %s not initialized", n.Name) - } - - if n.Output != nil { - *n.Output = result - } - - if n.Schema != nil { - *n.Schema = ctx.ProvisionerSchema(n.Name) - } - - return result, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_read_data.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_read_data.go deleted file mode 100644 index 0b734b79..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_read_data.go +++ /dev/null @@ -1,395 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans/objchange" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// EvalReadData is an EvalNode implementation that deals with the main part -// of the data resource lifecycle: either actually reading from the data source -// or generating a plan to do so. -type EvalReadData struct { - Addr addrs.ResourceInstance - Config *configs.Resource - Dependencies []addrs.Referenceable - Provider *providers.Interface - ProviderAddr addrs.AbsProviderConfig - ProviderSchema **ProviderSchema - - // Planned is set when dealing with data resources that were deferred to - // the apply walk, to let us see what was planned. If this is set, the - // evaluation of the config is required to produce a wholly-known - // configuration which is consistent with the partial object included - // in this planned change. - Planned **plans.ResourceInstanceChange - - // ForcePlanRead, if true, overrides the usual behavior of immediately - // reading from the data source where possible, instead forcing us to - // _always_ generate a plan. This is used during the plan walk, since we - // mustn't actually apply anything there. (The resulting state doesn't - // get persisted) - ForcePlanRead bool - - // The result from this EvalNode has a few different possibilities - // depending on the input: - // - If Planned is nil then we assume we're aiming to _produce_ the plan, - // and so the following two outcomes are possible: - // - OutputChange.Action is plans.NoOp and OutputState is the complete - // result of reading from the data source. This is the easy path. - // - OutputChange.Action is plans.Read and OutputState is a planned - // object placeholder (states.ObjectPlanned). In this case, the - // returned change must be recorded in the overral changeset and - // eventually passed to another instance of this struct during the - // apply walk. - // - If Planned is non-nil then we assume we're aiming to complete a - // planned read from an earlier plan walk. In this case the only possible - // non-error outcome is to set Output.Action (if non-nil) to a plans.NoOp - // change and put the complete resulting state in OutputState, ready to - // be saved in the overall state and used for expression evaluation. - OutputChange **plans.ResourceInstanceChange - OutputValue *cty.Value - OutputConfigValue *cty.Value - OutputState **states.ResourceInstanceObject -} - -func (n *EvalReadData) Eval(ctx EvalContext) (interface{}, error) { - absAddr := n.Addr.Absolute(ctx.Path()) - log.Printf("[TRACE] EvalReadData: working on %s", absAddr) - - if n.ProviderSchema == nil || *n.ProviderSchema == nil { - return nil, fmt.Errorf("provider schema not available for %s", n.Addr) - } - - var diags tfdiags.Diagnostics - var change *plans.ResourceInstanceChange - var configVal cty.Value - - // TODO: Do we need to handle Delete changes here? EvalReadDataDiff and - // EvalReadDataApply did, but it seems like we should handle that via a - // separate mechanism since it boils down to just deleting the object from - // the state... and we do that on every plan anyway, forcing the data - // resource to re-read. - - config := *n.Config - provider := *n.Provider - providerSchema := *n.ProviderSchema - schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // Should be caught during validation, so we don't bother with a pretty error here - return nil, fmt.Errorf("provider %q does not support data source %q", n.ProviderAddr.ProviderConfig.Type, n.Addr.Resource.Type) - } - - // We'll always start by evaluating the configuration. What we do after - // that will depend on the evaluation result along with what other inputs - // we were given. - objTy := schema.ImpliedType() - priorVal := cty.NullVal(objTy) // for data resources, prior is always null because we start fresh every time - - forEach, _ := evaluateResourceForEachExpression(n.Config.ForEach, ctx) - keyData := EvalDataForInstanceKey(n.Addr.Key, forEach) - - var configDiags tfdiags.Diagnostics - configVal, _, configDiags = ctx.EvaluateBlock(config.Config, schema, nil, keyData) - diags = diags.Append(configDiags) - if configDiags.HasErrors() { - return nil, diags.Err() - } - - proposedNewVal := objchange.PlannedDataResourceObject(schema, configVal) - - // If our configuration contains any unknown values then we must defer the - // read to the apply phase by producing a "Read" change for this resource, - // and a placeholder value for it in the state. - if n.ForcePlanRead || !configVal.IsWhollyKnown() { - // If the configuration is still unknown when we're applying a planned - // change then that indicates a bug in Terraform, since we should have - // everything resolved by now. - if n.Planned != nil && *n.Planned != nil { - return nil, fmt.Errorf( - "configuration for %s still contains unknown values during apply (this is a bug in Terraform; please report it!)", - absAddr, - ) - } - if n.ForcePlanRead { - log.Printf("[TRACE] EvalReadData: %s configuration is fully known, but we're forcing a read plan to be created", absAddr) - } else { - log.Printf("[TRACE] EvalReadData: %s configuration not fully known yet, so deferring to apply phase", absAddr) - } - - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PreDiff(absAddr, states.CurrentGen, priorVal, proposedNewVal) - }) - if err != nil { - return nil, err - } - - change = &plans.ResourceInstanceChange{ - Addr: absAddr, - ProviderAddr: n.ProviderAddr, - Change: plans.Change{ - Action: plans.Read, - Before: priorVal, - After: proposedNewVal, - }, - } - - err = ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostDiff(absAddr, states.CurrentGen, change.Action, priorVal, proposedNewVal) - }) - if err != nil { - return nil, err - } - - if n.OutputChange != nil { - *n.OutputChange = change - } - if n.OutputValue != nil { - *n.OutputValue = change.After - } - if n.OutputConfigValue != nil { - *n.OutputConfigValue = configVal - } - if n.OutputState != nil { - state := &states.ResourceInstanceObject{ - Value: change.After, - Status: states.ObjectPlanned, // because the partial value in the plan must be used for now - Dependencies: n.Dependencies, - } - *n.OutputState = state - } - - return nil, diags.ErrWithWarnings() - } - - if n.Planned != nil && *n.Planned != nil && (*n.Planned).Action != plans.Read { - // If any other action gets in here then that's always a bug; this - // EvalNode only deals with reading. - return nil, fmt.Errorf( - "invalid action %s for %s: only Read is supported (this is a bug in Terraform; please report it!)", - (*n.Planned).Action, absAddr, - ) - } - - log.Printf("[TRACE] Re-validating config for %s", absAddr) - validateResp := provider.ValidateDataSourceConfig( - providers.ValidateDataSourceConfigRequest{ - TypeName: n.Addr.Resource.Type, - Config: configVal, - }, - ) - if validateResp.Diagnostics.HasErrors() { - return nil, validateResp.Diagnostics.InConfigBody(n.Config.Config).Err() - } - - // If we get down here then our configuration is complete and we're read - // to actually call the provider to read the data. - log.Printf("[TRACE] EvalReadData: %s configuration is complete, so reading from provider", absAddr) - - err := ctx.Hook(func(h Hook) (HookAction, error) { - // We don't have a state yet, so we'll just give the hook an - // empty one to work with. - return h.PreRefresh(absAddr, states.CurrentGen, cty.NullVal(cty.DynamicPseudoType)) - }) - if err != nil { - return nil, err - } - - resp := provider.ReadDataSource(providers.ReadDataSourceRequest{ - TypeName: n.Addr.Resource.Type, - Config: configVal, - }) - diags = diags.Append(resp.Diagnostics.InConfigBody(n.Config.Config)) - if diags.HasErrors() { - return nil, diags.Err() - } - newVal := resp.State - if newVal == cty.NilVal { - // This can happen with incompletely-configured mocks. We'll allow it - // and treat it as an alias for a properly-typed null value. - newVal = cty.NullVal(schema.ImpliedType()) - } - - for _, err := range newVal.Type().TestConformance(schema.ImpliedType()) { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced invalid object", - fmt.Sprintf( - "Provider %q produced an invalid value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.ProviderConfig.Type, tfdiags.FormatErrorPrefixed(err, absAddr.String()), - ), - )) - } - if diags.HasErrors() { - return nil, diags.Err() - } - - if newVal.IsNull() { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced null object", - fmt.Sprintf( - "Provider %q produced a null value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.ProviderConfig.Type, absAddr, - ), - )) - } - if !newVal.IsWhollyKnown() { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced invalid object", - fmt.Sprintf( - "Provider %q produced a value for %s that is not wholly known.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.ProviderConfig.Type, absAddr, - ), - )) - - // We'll still save the object, but we need to eliminate any unknown - // values first because we can't serialize them in the state file. - // Note that this may cause set elements to be coalesced if they - // differed only by having unknown values, but we don't worry about - // that here because we're saving the value only for inspection - // purposes; the error we added above will halt the graph walk. - newVal = cty.UnknownAsNull(newVal) - } - - // Since we've completed the read, we actually have no change to make, but - // we'll produce a NoOp one anyway to preserve the usual flow of the - // plan phase and allow it to produce a complete plan. - change = &plans.ResourceInstanceChange{ - Addr: absAddr, - ProviderAddr: n.ProviderAddr, - Change: plans.Change{ - Action: plans.NoOp, - Before: newVal, - After: newVal, - }, - } - state := &states.ResourceInstanceObject{ - Value: change.After, - Status: states.ObjectReady, // because we completed the read from the provider - Dependencies: n.Dependencies, - } - - err = ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostRefresh(absAddr, states.CurrentGen, change.Before, newVal) - }) - if err != nil { - return nil, err - } - - if n.OutputChange != nil { - *n.OutputChange = change - } - if n.OutputValue != nil { - *n.OutputValue = change.After - } - if n.OutputConfigValue != nil { - *n.OutputConfigValue = configVal - } - if n.OutputState != nil { - *n.OutputState = state - } - - return nil, diags.ErrWithWarnings() -} - -// EvalReadDataApply is an EvalNode implementation that executes a data -// resource's ReadDataApply method to read data from the data source. -type EvalReadDataApply struct { - Addr addrs.ResourceInstance - Provider *providers.Interface - ProviderAddr addrs.AbsProviderConfig - ProviderSchema **ProviderSchema - Output **states.ResourceInstanceObject - Config *configs.Resource - Change **plans.ResourceInstanceChange - StateReferences []addrs.Referenceable -} - -func (n *EvalReadDataApply) Eval(ctx EvalContext) (interface{}, error) { - provider := *n.Provider - change := *n.Change - providerSchema := *n.ProviderSchema - absAddr := n.Addr.Absolute(ctx.Path()) - - var diags tfdiags.Diagnostics - - // If the diff is for *destroying* this resource then we'll - // just drop its state and move on, since data resources don't - // support an actual "destroy" action. - if change != nil && change.Action == plans.Delete { - if n.Output != nil { - *n.Output = nil - } - return nil, nil - } - - // For the purpose of external hooks we present a data apply as a - // "Refresh" rather than an "Apply" because creating a data source - // is presented to users/callers as a "read" operation. - err := ctx.Hook(func(h Hook) (HookAction, error) { - // We don't have a state yet, so we'll just give the hook an - // empty one to work with. - return h.PreRefresh(absAddr, states.CurrentGen, cty.NullVal(cty.DynamicPseudoType)) - }) - if err != nil { - return nil, err - } - - resp := provider.ReadDataSource(providers.ReadDataSourceRequest{ - TypeName: n.Addr.Resource.Type, - Config: change.After, - }) - diags = diags.Append(resp.Diagnostics.InConfigBody(n.Config.Config)) - if diags.HasErrors() { - return nil, diags.Err() - } - - schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // Should be caught during validation, so we don't bother with a pretty error here - return nil, fmt.Errorf("provider does not support data source %q", n.Addr.Resource.Type) - } - - newVal := resp.State - for _, err := range newVal.Type().TestConformance(schema.ImpliedType()) { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced invalid object", - fmt.Sprintf( - "Provider %q planned an invalid value for %s. The result could not be saved.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.ProviderConfig.Type, tfdiags.FormatErrorPrefixed(err, absAddr.String()), - ), - )) - } - if diags.HasErrors() { - return nil, diags.Err() - } - - err = ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostRefresh(absAddr, states.CurrentGen, change.Before, newVal) - }) - if err != nil { - return nil, err - } - - if n.Output != nil { - *n.Output = &states.ResourceInstanceObject{ - Value: newVal, - Status: states.ObjectReady, - Dependencies: n.StateReferences, - } - } - - return nil, diags.ErrWithWarnings() -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_refresh.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_refresh.go deleted file mode 100644 index 6a834445..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_refresh.go +++ /dev/null @@ -1,106 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// EvalRefresh is an EvalNode implementation that does a refresh for -// a resource. -type EvalRefresh struct { - Addr addrs.ResourceInstance - ProviderAddr addrs.AbsProviderConfig - Provider *providers.Interface - ProviderSchema **ProviderSchema - State **states.ResourceInstanceObject - Output **states.ResourceInstanceObject -} - -// TODO: test -func (n *EvalRefresh) Eval(ctx EvalContext) (interface{}, error) { - state := *n.State - absAddr := n.Addr.Absolute(ctx.Path()) - - var diags tfdiags.Diagnostics - - // If we have no state, we don't do any refreshing - if state == nil { - log.Printf("[DEBUG] refresh: %s: no state, so not refreshing", n.Addr.Absolute(ctx.Path())) - return nil, diags.ErrWithWarnings() - } - - schema, _ := (*n.ProviderSchema).SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // Should be caught during validation, so we don't bother with a pretty error here - return nil, fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Type) - } - - // Call pre-refresh hook - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PreRefresh(absAddr, states.CurrentGen, state.Value) - }) - if err != nil { - return nil, diags.ErrWithWarnings() - } - - // Refresh! - priorVal := state.Value - req := providers.ReadResourceRequest{ - TypeName: n.Addr.Resource.Type, - PriorState: priorVal, - Private: state.Private, - } - - provider := *n.Provider - resp := provider.ReadResource(req) - diags = diags.Append(resp.Diagnostics) - if diags.HasErrors() { - return nil, diags.Err() - } - - if resp.NewState == cty.NilVal { - // This ought not to happen in real cases since it's not possible to - // send NilVal over the plugin RPC channel, but it can come up in - // tests due to sloppy mocking. - panic("new state is cty.NilVal") - } - - for _, err := range resp.NewState.Type().TestConformance(schema.ImpliedType()) { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced invalid object", - fmt.Sprintf( - "Provider %q planned an invalid value for %s during refresh: %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.ProviderConfig.Type, absAddr, tfdiags.FormatError(err), - ), - )) - } - if diags.HasErrors() { - return nil, diags.Err() - } - - newState := state.DeepCopy() - newState.Value = resp.NewState - newState.Private = resp.Private - - // Call post-refresh hook - err = ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostRefresh(absAddr, states.CurrentGen, priorVal, newState.Value) - }) - if err != nil { - return nil, err - } - - if n.Output != nil { - *n.Output = newState - } - - return nil, diags.ErrWithWarnings() -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_sequence.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_sequence.go deleted file mode 100644 index 7d6bb660..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_sequence.go +++ /dev/null @@ -1,42 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// EvalSequence is an EvalNode that evaluates in sequence. -type EvalSequence struct { - Nodes []EvalNode -} - -func (n *EvalSequence) Eval(ctx EvalContext) (interface{}, error) { - var diags tfdiags.Diagnostics - - for _, n := range n.Nodes { - if n == nil { - continue - } - - if _, err := EvalRaw(n, ctx); err != nil { - if _, isEarlyExit := err.(EvalEarlyExitError); isEarlyExit { - // In this path we abort early, losing any non-error - // diagnostics we saw earlier. - return nil, err - } - diags = diags.Append(err) - if diags.HasErrors() { - // Halt if we get some errors, but warnings are okay. - break - } - } - } - - return nil, diags.ErrWithWarnings() -} - -// EvalNodeFilterable impl. -func (n *EvalSequence) Filter(fn EvalNodeFilterFunc) { - for i, node := range n.Nodes { - n.Nodes[i] = fn(node) - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_state.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_state.go deleted file mode 100644 index 70a72bbd..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_state.go +++ /dev/null @@ -1,475 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// EvalReadState is an EvalNode implementation that reads the -// current object for a specific instance in the state. -type EvalReadState struct { - // Addr is the address of the instance to read state for. - Addr addrs.ResourceInstance - - // ProviderSchema is the schema for the provider given in Provider. - ProviderSchema **ProviderSchema - - // Provider is the provider that will subsequently perform actions on - // the the state object. This is used to perform any schema upgrades - // that might be required to prepare the stored data for use. - Provider *providers.Interface - - // Output will be written with a pointer to the retrieved object. - Output **states.ResourceInstanceObject -} - -func (n *EvalReadState) Eval(ctx EvalContext) (interface{}, error) { - if n.Provider == nil || *n.Provider == nil { - panic("EvalReadState used with no Provider object") - } - if n.ProviderSchema == nil || *n.ProviderSchema == nil { - panic("EvalReadState used with no ProviderSchema object") - } - - absAddr := n.Addr.Absolute(ctx.Path()) - log.Printf("[TRACE] EvalReadState: reading state for %s", absAddr) - - src := ctx.State().ResourceInstanceObject(absAddr, states.CurrentGen) - if src == nil { - // Presumably we only have deposed objects, then. - log.Printf("[TRACE] EvalReadState: no state present for %s", absAddr) - return nil, nil - } - - schema, currentVersion := (*n.ProviderSchema).SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // Shouldn't happen since we should've failed long ago if no schema is present - return nil, fmt.Errorf("no schema available for %s while reading state; this is a bug in Terraform and should be reported", absAddr) - } - var diags tfdiags.Diagnostics - src, diags = UpgradeResourceState(absAddr, *n.Provider, src, schema, currentVersion) - if diags.HasErrors() { - // Note that we don't have any channel to return warnings here. We'll - // accept that for now since warnings during a schema upgrade would - // be pretty weird anyway, since this operation is supposed to seem - // invisible to the user. - return nil, diags.Err() - } - - obj, err := src.Decode(schema.ImpliedType()) - if err != nil { - return nil, err - } - - if n.Output != nil { - *n.Output = obj - } - return obj, nil -} - -// EvalReadStateDeposed is an EvalNode implementation that reads the -// deposed InstanceState for a specific resource out of the state -type EvalReadStateDeposed struct { - // Addr is the address of the instance to read state for. - Addr addrs.ResourceInstance - - // Key identifies which deposed object we will read. - Key states.DeposedKey - - // ProviderSchema is the schema for the provider given in Provider. - ProviderSchema **ProviderSchema - - // Provider is the provider that will subsequently perform actions on - // the the state object. This is used to perform any schema upgrades - // that might be required to prepare the stored data for use. - Provider *providers.Interface - - // Output will be written with a pointer to the retrieved object. - Output **states.ResourceInstanceObject -} - -func (n *EvalReadStateDeposed) Eval(ctx EvalContext) (interface{}, error) { - if n.Provider == nil || *n.Provider == nil { - panic("EvalReadStateDeposed used with no Provider object") - } - if n.ProviderSchema == nil || *n.ProviderSchema == nil { - panic("EvalReadStateDeposed used with no ProviderSchema object") - } - - key := n.Key - if key == states.NotDeposed { - return nil, fmt.Errorf("EvalReadStateDeposed used with no instance key; this is a bug in Terraform and should be reported") - } - absAddr := n.Addr.Absolute(ctx.Path()) - log.Printf("[TRACE] EvalReadStateDeposed: reading state for %s deposed object %s", absAddr, n.Key) - - src := ctx.State().ResourceInstanceObject(absAddr, key) - if src == nil { - // Presumably we only have deposed objects, then. - log.Printf("[TRACE] EvalReadStateDeposed: no state present for %s deposed object %s", absAddr, n.Key) - return nil, nil - } - - schema, currentVersion := (*n.ProviderSchema).SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // Shouldn't happen since we should've failed long ago if no schema is present - return nil, fmt.Errorf("no schema available for %s while reading state; this is a bug in Terraform and should be reported", absAddr) - } - var diags tfdiags.Diagnostics - src, diags = UpgradeResourceState(absAddr, *n.Provider, src, schema, currentVersion) - if diags.HasErrors() { - // Note that we don't have any channel to return warnings here. We'll - // accept that for now since warnings during a schema upgrade would - // be pretty weird anyway, since this operation is supposed to seem - // invisible to the user. - return nil, diags.Err() - } - - obj, err := src.Decode(schema.ImpliedType()) - if err != nil { - return nil, err - } - if n.Output != nil { - *n.Output = obj - } - return obj, nil -} - -// EvalRequireState is an EvalNode implementation that exits early if the given -// object is null. -type EvalRequireState struct { - State **states.ResourceInstanceObject -} - -func (n *EvalRequireState) Eval(ctx EvalContext) (interface{}, error) { - if n.State == nil { - return nil, EvalEarlyExitError{} - } - - state := *n.State - if state == nil || state.Value.IsNull() { - return nil, EvalEarlyExitError{} - } - - return nil, nil -} - -// EvalUpdateStateHook is an EvalNode implementation that calls the -// PostStateUpdate hook with the current state. -type EvalUpdateStateHook struct{} - -func (n *EvalUpdateStateHook) Eval(ctx EvalContext) (interface{}, error) { - // In principle we could grab the lock here just long enough to take a - // deep copy and then pass that to our hooks below, but we'll instead - // hold the hook for the duration to avoid the potential confusing - // situation of us racing to call PostStateUpdate concurrently with - // different state snapshots. - stateSync := ctx.State() - state := stateSync.Lock().DeepCopy() - defer stateSync.Unlock() - - // Call the hook - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostStateUpdate(state) - }) - if err != nil { - return nil, err - } - - return nil, nil -} - -// EvalWriteState is an EvalNode implementation that saves the given object -// as the current object for the selected resource instance. -type EvalWriteState struct { - // Addr is the address of the instance to read state for. - Addr addrs.ResourceInstance - - // State is the object state to save. - State **states.ResourceInstanceObject - - // ProviderSchema is the schema for the provider given in ProviderAddr. - ProviderSchema **ProviderSchema - - // ProviderAddr is the address of the provider configuration that - // produced the given object. - ProviderAddr addrs.AbsProviderConfig -} - -func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) { - if n.State == nil { - // Note that a pointer _to_ nil is valid here, indicating the total - // absense of an object as we'd see during destroy. - panic("EvalWriteState used with no ResourceInstanceObject") - } - - absAddr := n.Addr.Absolute(ctx.Path()) - state := ctx.State() - - if n.ProviderAddr.ProviderConfig.Type == "" { - return nil, fmt.Errorf("failed to write state for %s, missing provider type", absAddr) - } - - obj := *n.State - if obj == nil || obj.Value.IsNull() { - // No need to encode anything: we'll just write it directly. - state.SetResourceInstanceCurrent(absAddr, nil, n.ProviderAddr) - log.Printf("[TRACE] EvalWriteState: removing state object for %s", absAddr) - return nil, nil - } - if n.ProviderSchema == nil || *n.ProviderSchema == nil { - // Should never happen, unless our state object is nil - panic("EvalWriteState used with pointer to nil ProviderSchema object") - } - - if obj != nil { - log.Printf("[TRACE] EvalWriteState: writing current state object for %s", absAddr) - } else { - log.Printf("[TRACE] EvalWriteState: removing current state object for %s", absAddr) - } - - schema, currentVersion := (*n.ProviderSchema).SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // It shouldn't be possible to get this far in any real scenario - // without a schema, but we might end up here in contrived tests that - // fail to set up their world properly. - return nil, fmt.Errorf("failed to encode %s in state: no resource type schema available", absAddr) - } - src, err := obj.Encode(schema.ImpliedType(), currentVersion) - if err != nil { - return nil, fmt.Errorf("failed to encode %s in state: %s", absAddr, err) - } - - state.SetResourceInstanceCurrent(absAddr, src, n.ProviderAddr) - return nil, nil -} - -// EvalWriteStateDeposed is an EvalNode implementation that writes -// an InstanceState out to the Deposed list of a resource in the state. -type EvalWriteStateDeposed struct { - // Addr is the address of the instance to read state for. - Addr addrs.ResourceInstance - - // Key indicates which deposed object to write to. - Key states.DeposedKey - - // State is the object state to save. - State **states.ResourceInstanceObject - - // ProviderSchema is the schema for the provider given in ProviderAddr. - ProviderSchema **ProviderSchema - - // ProviderAddr is the address of the provider configuration that - // produced the given object. - ProviderAddr addrs.AbsProviderConfig -} - -func (n *EvalWriteStateDeposed) Eval(ctx EvalContext) (interface{}, error) { - if n.State == nil { - // Note that a pointer _to_ nil is valid here, indicating the total - // absense of an object as we'd see during destroy. - panic("EvalWriteStateDeposed used with no ResourceInstanceObject") - } - - absAddr := n.Addr.Absolute(ctx.Path()) - key := n.Key - state := ctx.State() - - if key == states.NotDeposed { - // should never happen - return nil, fmt.Errorf("can't save deposed object for %s without a deposed key; this is a bug in Terraform that should be reported", absAddr) - } - - obj := *n.State - if obj == nil { - // No need to encode anything: we'll just write it directly. - state.SetResourceInstanceDeposed(absAddr, key, nil, n.ProviderAddr) - log.Printf("[TRACE] EvalWriteStateDeposed: removing state object for %s deposed %s", absAddr, key) - return nil, nil - } - if n.ProviderSchema == nil || *n.ProviderSchema == nil { - // Should never happen, unless our state object is nil - panic("EvalWriteStateDeposed used with no ProviderSchema object") - } - - schema, currentVersion := (*n.ProviderSchema).SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // It shouldn't be possible to get this far in any real scenario - // without a schema, but we might end up here in contrived tests that - // fail to set up their world properly. - return nil, fmt.Errorf("failed to encode %s in state: no resource type schema available", absAddr) - } - src, err := obj.Encode(schema.ImpliedType(), currentVersion) - if err != nil { - return nil, fmt.Errorf("failed to encode %s in state: %s", absAddr, err) - } - - log.Printf("[TRACE] EvalWriteStateDeposed: writing state object for %s deposed %s", absAddr, key) - state.SetResourceInstanceDeposed(absAddr, key, src, n.ProviderAddr) - return nil, nil -} - -// EvalDeposeState is an EvalNode implementation that moves the current object -// for the given instance to instead be a deposed object, leaving the instance -// with no current object. -// This is used at the beginning of a create-before-destroy replace action so -// that the create can create while preserving the old state of the -// to-be-destroyed object. -type EvalDeposeState struct { - Addr addrs.ResourceInstance - - // ForceKey, if a value other than states.NotDeposed, will be used as the - // key for the newly-created deposed object that results from this action. - // If set to states.NotDeposed (the zero value), a new unique key will be - // allocated. - ForceKey states.DeposedKey - - // OutputKey, if non-nil, will be written with the deposed object key that - // was generated for the object. This can then be passed to - // EvalUndeposeState.Key so it knows which deposed instance to forget. - OutputKey *states.DeposedKey -} - -// TODO: test -func (n *EvalDeposeState) Eval(ctx EvalContext) (interface{}, error) { - absAddr := n.Addr.Absolute(ctx.Path()) - state := ctx.State() - - var key states.DeposedKey - if n.ForceKey == states.NotDeposed { - key = state.DeposeResourceInstanceObject(absAddr) - } else { - key = n.ForceKey - state.DeposeResourceInstanceObjectForceKey(absAddr, key) - } - log.Printf("[TRACE] EvalDeposeState: prior object for %s now deposed with key %s", absAddr, key) - - if n.OutputKey != nil { - *n.OutputKey = key - } - - return nil, nil -} - -// EvalMaybeRestoreDeposedObject is an EvalNode implementation that will -// restore a particular deposed object of the specified resource instance -// to be the "current" object if and only if the instance doesn't currently -// have a current object. -// -// This is intended for use when the create leg of a create before destroy -// fails with no partial new object: if we didn't take any action, the user -// would be left in the unfortunate situation of having no current object -// and the previously-workign object now deposed. This EvalNode causes a -// better outcome by restoring things to how they were before the replace -// operation began. -// -// The create operation may have produced a partial result even though it -// failed and it's important that we don't "forget" that state, so in that -// situation the prior object remains deposed and the partial new object -// remains the current object, allowing the situation to hopefully be -// improved in a subsequent run. -type EvalMaybeRestoreDeposedObject struct { - Addr addrs.ResourceInstance - - // Key is a pointer to the deposed object key that should be forgotten - // from the state, which must be non-nil. - Key *states.DeposedKey -} - -// TODO: test -func (n *EvalMaybeRestoreDeposedObject) Eval(ctx EvalContext) (interface{}, error) { - absAddr := n.Addr.Absolute(ctx.Path()) - dk := *n.Key - state := ctx.State() - - restored := state.MaybeRestoreResourceInstanceDeposed(absAddr, dk) - if restored { - log.Printf("[TRACE] EvalMaybeRestoreDeposedObject: %s deposed object %s was restored as the current object", absAddr, dk) - } else { - log.Printf("[TRACE] EvalMaybeRestoreDeposedObject: %s deposed object %s remains deposed", absAddr, dk) - } - - return nil, nil -} - -// EvalWriteResourceState is an EvalNode implementation that ensures that -// a suitable resource-level state record is present in the state, if that's -// required for the "each mode" of that resource. -// -// This is important primarily for the situation where count = 0, since this -// eval is the only change we get to set the resource "each mode" to list -// in that case, allowing expression evaluation to see it as a zero-element -// list rather than as not set at all. -type EvalWriteResourceState struct { - Addr addrs.Resource - Config *configs.Resource - ProviderAddr addrs.AbsProviderConfig -} - -// TODO: test -func (n *EvalWriteResourceState) Eval(ctx EvalContext) (interface{}, error) { - var diags tfdiags.Diagnostics - absAddr := n.Addr.Absolute(ctx.Path()) - state := ctx.State() - - count, countDiags := evaluateResourceCountExpression(n.Config.Count, ctx) - diags = diags.Append(countDiags) - if countDiags.HasErrors() { - return nil, diags.Err() - } - - eachMode := states.NoEach - if count >= 0 { // -1 signals "count not set" - eachMode = states.EachList - } - - forEach, forEachDiags := evaluateResourceForEachExpression(n.Config.ForEach, ctx) - diags = diags.Append(forEachDiags) - if forEachDiags.HasErrors() { - return nil, diags.Err() - } - - if forEach != nil { - eachMode = states.EachMap - } - - // This method takes care of all of the business logic of updating this - // while ensuring that any existing instances are preserved, etc. - state.SetResourceMeta(absAddr, eachMode, n.ProviderAddr) - - return nil, nil -} - -// EvalForgetResourceState is an EvalNode implementation that prunes out an -// empty resource-level state for a given resource address, or produces an -// error if it isn't empty after all. -// -// This should be the last action taken for a resource that has been removed -// from the configuration altogether, to clean up the leftover husk of the -// resource in the state after other EvalNodes have destroyed and removed -// all of the instances and instance objects beneath it. -type EvalForgetResourceState struct { - Addr addrs.Resource -} - -func (n *EvalForgetResourceState) Eval(ctx EvalContext) (interface{}, error) { - absAddr := n.Addr.Absolute(ctx.Path()) - state := ctx.State() - - pruned := state.RemoveResourceIfEmpty(absAddr) - if !pruned { - // If this produces an error, it indicates a bug elsewhere in Terraform - // -- probably missing graph nodes, graph edges, or - // incorrectly-implemented evaluation steps. - return nil, fmt.Errorf("orphan resource %s still has a non-empty state after apply; this is a bug in Terraform", absAddr) - } - log.Printf("[TRACE] EvalForgetResourceState: Pruned husk of %s from state", absAddr) - - return nil, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_state_upgrade.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_state_upgrade.go deleted file mode 100644 index 27d5f212..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_state_upgrade.go +++ /dev/null @@ -1,106 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// UpgradeResourceState will, if necessary, run the provider-defined upgrade -// logic against the given state object to make it compliant with the -// current schema version. This is a no-op if the given state object is -// already at the latest version. -// -// If any errors occur during upgrade, error diagnostics are returned. In that -// case it is not safe to proceed with using the original state object. -func UpgradeResourceState(addr addrs.AbsResourceInstance, provider providers.Interface, src *states.ResourceInstanceObjectSrc, currentSchema *configschema.Block, currentVersion uint64) (*states.ResourceInstanceObjectSrc, tfdiags.Diagnostics) { - if addr.Resource.Resource.Mode != addrs.ManagedResourceMode { - // We only do state upgrading for managed resources. - return src, nil - } - - stateIsFlatmap := len(src.AttrsJSON) == 0 - - providerType := addr.Resource.Resource.DefaultProviderConfig().Type - if src.SchemaVersion > currentVersion { - log.Printf("[TRACE] UpgradeResourceState: can't downgrade state for %s from version %d to %d", addr, src.SchemaVersion, currentVersion) - var diags tfdiags.Diagnostics - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Resource instance managed by newer provider version", - // This is not a very good error message, but we don't retain enough - // information in state to give good feedback on what provider - // version might be required here. :( - fmt.Sprintf("The current state of %s was created by a newer provider version than is currently selected. Upgrade the %s provider to work with this state.", addr, providerType), - )) - return nil, diags - } - - // If we get down here then we need to upgrade the state, with the - // provider's help. - // If this state was originally created by a version of Terraform prior to - // v0.12, this also includes translating from legacy flatmap to new-style - // representation, since only the provider has enough information to - // understand a flatmap built against an older schema. - if src.SchemaVersion != currentVersion { - log.Printf("[TRACE] UpgradeResourceState: upgrading state for %s from version %d to %d using provider %q", addr, src.SchemaVersion, currentVersion, providerType) - } else { - log.Printf("[TRACE] UpgradeResourceState: schema version of %s is still %d; calling provider %q for any other minor fixups", addr, currentVersion, providerType) - } - - req := providers.UpgradeResourceStateRequest{ - TypeName: addr.Resource.Resource.Type, - - // TODO: The internal schema version representations are all using - // uint64 instead of int64, but unsigned integers aren't friendly - // to all protobuf target languages so in practice we use int64 - // on the wire. In future we will change all of our internal - // representations to int64 too. - Version: int64(src.SchemaVersion), - } - - if stateIsFlatmap { - req.RawStateFlatmap = src.AttrsFlat - } else { - req.RawStateJSON = src.AttrsJSON - } - - resp := provider.UpgradeResourceState(req) - diags := resp.Diagnostics - if diags.HasErrors() { - return nil, diags - } - - // After upgrading, the new value must conform to the current schema. When - // going over RPC this is actually already ensured by the - // marshaling/unmarshaling of the new value, but we'll check it here - // anyway for robustness, e.g. for in-process providers. - newValue := resp.UpgradedState - if errs := newValue.Type().TestConformance(currentSchema.ImpliedType()); len(errs) > 0 { - for _, err := range errs { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid resource state upgrade", - fmt.Sprintf("The %s provider upgraded the state for %s from a previous version, but produced an invalid result: %s.", providerType, addr, tfdiags.FormatError(err)), - )) - } - return nil, diags - } - - new, err := src.CompleteUpgrade(newValue, currentSchema.ImpliedType(), uint64(currentVersion)) - if err != nil { - // We already checked for type conformance above, so getting into this - // codepath should be rare and is probably a bug somewhere under CompleteUpgrade. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to encode result of resource state upgrade", - fmt.Sprintf("Failed to encode state for %s after resource schema upgrade: %s.", addr, tfdiags.FormatError(err)), - )) - } - return new, diags -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_validate.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_validate.go deleted file mode 100644 index a4f28bd9..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_validate.go +++ /dev/null @@ -1,588 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/provisioners" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" - "github.com/zclconf/go-cty/cty/gocty" -) - -// EvalValidateCount is an EvalNode implementation that validates -// the count of a resource. -type EvalValidateCount struct { - Resource *configs.Resource -} - -// TODO: test -func (n *EvalValidateCount) Eval(ctx EvalContext) (interface{}, error) { - var diags tfdiags.Diagnostics - var count int - var err error - - val, valDiags := ctx.EvaluateExpr(n.Resource.Count, cty.Number, nil) - diags = diags.Append(valDiags) - if valDiags.HasErrors() { - goto RETURN - } - if val.IsNull() || !val.IsKnown() { - goto RETURN - } - - err = gocty.FromCtyValue(val, &count) - if err != nil { - // The EvaluateExpr call above already guaranteed us a number value, - // so if we end up here then we have something that is out of range - // for an int, and the error message will include a description of - // the valid range. - rawVal := val.AsBigFloat() - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count value", - Detail: fmt.Sprintf("The number %s is not a valid count value: %s.", rawVal, err), - Subject: n.Resource.Count.Range().Ptr(), - }) - } else if count < 0 { - rawVal := val.AsBigFloat() - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count value", - Detail: fmt.Sprintf("The number %s is not a valid count value: count must not be negative.", rawVal), - Subject: n.Resource.Count.Range().Ptr(), - }) - } - -RETURN: - return nil, diags.NonFatalErr() -} - -// EvalValidateProvider is an EvalNode implementation that validates -// a provider configuration. -type EvalValidateProvider struct { - Addr addrs.ProviderConfig - Provider *providers.Interface - Config *configs.Provider -} - -func (n *EvalValidateProvider) Eval(ctx EvalContext) (interface{}, error) { - var diags tfdiags.Diagnostics - provider := *n.Provider - - configBody := buildProviderConfig(ctx, n.Addr, n.Config) - - resp := provider.GetSchema() - diags = diags.Append(resp.Diagnostics) - if diags.HasErrors() { - return nil, diags.NonFatalErr() - } - - configSchema := resp.Provider.Block - if configSchema == nil { - // Should never happen in real code, but often comes up in tests where - // mock schemas are being used that tend to be incomplete. - log.Printf("[WARN] EvalValidateProvider: no config schema is available for %s, so using empty schema", n.Addr) - configSchema = &configschema.Block{} - } - - configVal, configBody, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, EvalDataForNoInstanceKey) - diags = diags.Append(evalDiags) - if evalDiags.HasErrors() { - return nil, diags.NonFatalErr() - } - - req := providers.PrepareProviderConfigRequest{ - Config: configVal, - } - - validateResp := provider.PrepareProviderConfig(req) - diags = diags.Append(validateResp.Diagnostics) - - return nil, diags.NonFatalErr() -} - -// EvalValidateProvisioner is an EvalNode implementation that validates -// the configuration of a provisioner belonging to a resource. The provisioner -// config is expected to contain the merged connection configurations. -type EvalValidateProvisioner struct { - ResourceAddr addrs.Resource - Provisioner *provisioners.Interface - Schema **configschema.Block - Config *configs.Provisioner - ResourceHasCount bool - ResourceHasForEach bool -} - -func (n *EvalValidateProvisioner) Eval(ctx EvalContext) (interface{}, error) { - provisioner := *n.Provisioner - config := *n.Config - schema := *n.Schema - - var diags tfdiags.Diagnostics - - { - // Validate the provisioner's own config first - - configVal, _, configDiags := n.evaluateBlock(ctx, config.Config, schema) - diags = diags.Append(configDiags) - if configDiags.HasErrors() { - return nil, diags.Err() - } - - if configVal == cty.NilVal { - // Should never happen for a well-behaved EvaluateBlock implementation - return nil, fmt.Errorf("EvaluateBlock returned nil value") - } - - req := provisioners.ValidateProvisionerConfigRequest{ - Config: configVal, - } - - resp := provisioner.ValidateProvisionerConfig(req) - diags = diags.Append(resp.Diagnostics) - } - - { - // Now validate the connection config, which contains the merged bodies - // of the resource and provisioner connection blocks. - connDiags := n.validateConnConfig(ctx, config.Connection, n.ResourceAddr) - diags = diags.Append(connDiags) - } - - return nil, diags.NonFatalErr() -} - -func (n *EvalValidateProvisioner) validateConnConfig(ctx EvalContext, config *configs.Connection, self addrs.Referenceable) tfdiags.Diagnostics { - // We can't comprehensively validate the connection config since its - // final structure is decided by the communicator and we can't instantiate - // that until we have a complete instance state. However, we *can* catch - // configuration keys that are not valid for *any* communicator, catching - // typos early rather than waiting until we actually try to run one of - // the resource's provisioners. - - var diags tfdiags.Diagnostics - - if config == nil || config.Config == nil { - // No block to validate - return diags - } - - // We evaluate here just by evaluating the block and returning any - // diagnostics we get, since evaluation alone is enough to check for - // extraneous arguments and incorrectly-typed arguments. - _, _, configDiags := n.evaluateBlock(ctx, config.Config, connectionBlockSupersetSchema) - diags = diags.Append(configDiags) - - return diags -} - -func (n *EvalValidateProvisioner) evaluateBlock(ctx EvalContext, body hcl.Body, schema *configschema.Block) (cty.Value, hcl.Body, tfdiags.Diagnostics) { - keyData := EvalDataForNoInstanceKey - selfAddr := n.ResourceAddr.Instance(addrs.NoKey) - - if n.ResourceHasCount { - // For a resource that has count, we allow count.index but don't - // know at this stage what it will return. - keyData = InstanceKeyEvalData{ - CountIndex: cty.UnknownVal(cty.Number), - } - - // "self" can't point to an unknown key, but we'll force it to be - // key 0 here, which should return an unknown value of the - // expected type since none of these elements are known at this - // point anyway. - selfAddr = n.ResourceAddr.Instance(addrs.IntKey(0)) - } else if n.ResourceHasForEach { - // For a resource that has for_each, we allow each.value and each.key - // but don't know at this stage what it will return. - keyData = InstanceKeyEvalData{ - EachKey: cty.UnknownVal(cty.String), - EachValue: cty.DynamicVal, - } - - // "self" can't point to an unknown key, but we'll force it to be - // key "" here, which should return an unknown value of the - // expected type since none of these elements are known at - // this point anyway. - selfAddr = n.ResourceAddr.Instance(addrs.StringKey("")) - } - - return ctx.EvaluateBlock(body, schema, selfAddr, keyData) -} - -// connectionBlockSupersetSchema is a schema representing the superset of all -// possible arguments for "connection" blocks across all supported connection -// types. -// -// This currently lives here because we've not yet updated our communicator -// subsystem to be aware of schema itself. Once that is done, we can remove -// this and use a type-specific schema from the communicator to validate -// exactly what is expected for a given connection type. -var connectionBlockSupersetSchema = &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - // NOTE: "type" is not included here because it's treated special - // by the config loader and stored away in a separate field. - - // Common attributes for both connection types - "host": { - Type: cty.String, - Required: true, - }, - "type": { - Type: cty.String, - Optional: true, - }, - "user": { - Type: cty.String, - Optional: true, - }, - "password": { - Type: cty.String, - Optional: true, - }, - "port": { - Type: cty.String, - Optional: true, - }, - "timeout": { - Type: cty.String, - Optional: true, - }, - "script_path": { - Type: cty.String, - Optional: true, - }, - - // For type=ssh only (enforced in ssh communicator) - "private_key": { - Type: cty.String, - Optional: true, - }, - "certificate": { - Type: cty.String, - Optional: true, - }, - "host_key": { - Type: cty.String, - Optional: true, - }, - "agent": { - Type: cty.Bool, - Optional: true, - }, - "agent_identity": { - Type: cty.String, - Optional: true, - }, - "bastion_host": { - Type: cty.String, - Optional: true, - }, - "bastion_host_key": { - Type: cty.String, - Optional: true, - }, - "bastion_port": { - Type: cty.Number, - Optional: true, - }, - "bastion_user": { - Type: cty.String, - Optional: true, - }, - "bastion_password": { - Type: cty.String, - Optional: true, - }, - "bastion_private_key": { - Type: cty.String, - Optional: true, - }, - "bastion_certificate": { - Type: cty.String, - Optional: true, - }, - - // For type=winrm only (enforced in winrm communicator) - "https": { - Type: cty.Bool, - Optional: true, - }, - "insecure": { - Type: cty.Bool, - Optional: true, - }, - "cacert": { - Type: cty.String, - Optional: true, - }, - "use_ntlm": { - Type: cty.Bool, - Optional: true, - }, - }, -} - -// connectionBlockSupersetSchema is a schema representing the superset of all -// possible arguments for "connection" blocks across all supported connection -// types. -// -// This currently lives here because we've not yet updated our communicator -// subsystem to be aware of schema itself. It's exported only for use in the -// configs/configupgrade package and should not be used from anywhere else. -// The caller may not modify any part of the returned schema data structure. -func ConnectionBlockSupersetSchema() *configschema.Block { - return connectionBlockSupersetSchema -} - -// EvalValidateResource is an EvalNode implementation that validates -// the configuration of a resource. -type EvalValidateResource struct { - Addr addrs.Resource - Provider *providers.Interface - ProviderSchema **ProviderSchema - Config *configs.Resource - - // IgnoreWarnings means that warnings will not be passed through. This allows - // "just-in-time" passes of validation to continue execution through warnings. - IgnoreWarnings bool - - // ConfigVal, if non-nil, will be updated with the value resulting from - // evaluating the given configuration body. Since validation is performed - // very early, this value is likely to contain lots of unknown values, - // but its type will conform to the schema of the resource type associated - // with the resource instance being validated. - ConfigVal *cty.Value -} - -func (n *EvalValidateResource) Eval(ctx EvalContext) (interface{}, error) { - if n.ProviderSchema == nil || *n.ProviderSchema == nil { - return nil, fmt.Errorf("EvalValidateResource has nil schema for %s", n.Addr) - } - - var diags tfdiags.Diagnostics - provider := *n.Provider - cfg := *n.Config - schema := *n.ProviderSchema - mode := cfg.Mode - - keyData := EvalDataForNoInstanceKey - if n.Config.Count != nil { - // If the config block has count, we'll evaluate with an unknown - // number as count.index so we can still type check even though - // we won't expand count until the plan phase. - keyData = InstanceKeyEvalData{ - CountIndex: cty.UnknownVal(cty.Number), - } - - // Basic type-checking of the count argument. More complete validation - // of this will happen when we DynamicExpand during the plan walk. - countDiags := n.validateCount(ctx, n.Config.Count) - diags = diags.Append(countDiags) - } - - if n.Config.ForEach != nil { - keyData = InstanceKeyEvalData{ - EachKey: cty.UnknownVal(cty.String), - EachValue: cty.UnknownVal(cty.DynamicPseudoType), - } - - // Evaluate the for_each expression here so we can expose the diagnostics - forEachDiags := n.validateForEach(ctx, n.Config.ForEach) - diags = diags.Append(forEachDiags) - } - - for _, traversal := range n.Config.DependsOn { - ref, refDiags := addrs.ParseRef(traversal) - diags = diags.Append(refDiags) - if !refDiags.HasErrors() && len(ref.Remaining) != 0 { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid depends_on reference", - Detail: "References in depends_on must be to a whole object (resource, etc), not to an attribute of an object.", - Subject: ref.Remaining.SourceRange().Ptr(), - }) - } - - // The ref must also refer to something that exists. To test that, - // we'll just eval it and count on the fact that our evaluator will - // detect references to non-existent objects. - if !diags.HasErrors() { - scope := ctx.EvaluationScope(nil, EvalDataForNoInstanceKey) - if scope != nil { // sometimes nil in tests, due to incomplete mocks - _, refDiags = scope.EvalReference(ref, cty.DynamicPseudoType) - diags = diags.Append(refDiags) - } - } - } - - // Provider entry point varies depending on resource mode, because - // managed resources and data resources are two distinct concepts - // in the provider abstraction. - switch mode { - case addrs.ManagedResourceMode: - schema, _ := schema.SchemaForResourceType(mode, cfg.Type) - if schema == nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid resource type", - Detail: fmt.Sprintf("The provider %s does not support resource type %q.", cfg.ProviderConfigAddr(), cfg.Type), - Subject: &cfg.TypeRange, - }) - return nil, diags.Err() - } - - configVal, _, valDiags := ctx.EvaluateBlock(cfg.Config, schema, nil, keyData) - diags = diags.Append(valDiags) - if valDiags.HasErrors() { - return nil, diags.Err() - } - - if cfg.Managed != nil { // can be nil only in tests with poorly-configured mocks - for _, traversal := range cfg.Managed.IgnoreChanges { - moreDiags := schema.StaticValidateTraversal(traversal) - diags = diags.Append(moreDiags) - } - } - - req := providers.ValidateResourceTypeConfigRequest{ - TypeName: cfg.Type, - Config: configVal, - } - - resp := provider.ValidateResourceTypeConfig(req) - diags = diags.Append(resp.Diagnostics.InConfigBody(cfg.Config)) - - if n.ConfigVal != nil { - *n.ConfigVal = configVal - } - - case addrs.DataResourceMode: - schema, _ := schema.SchemaForResourceType(mode, cfg.Type) - if schema == nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid data source", - Detail: fmt.Sprintf("The provider %s does not support data source %q.", cfg.ProviderConfigAddr(), cfg.Type), - Subject: &cfg.TypeRange, - }) - return nil, diags.Err() - } - - configVal, _, valDiags := ctx.EvaluateBlock(cfg.Config, schema, nil, keyData) - diags = diags.Append(valDiags) - if valDiags.HasErrors() { - return nil, diags.Err() - } - - req := providers.ValidateDataSourceConfigRequest{ - TypeName: cfg.Type, - Config: configVal, - } - - resp := provider.ValidateDataSourceConfig(req) - diags = diags.Append(resp.Diagnostics.InConfigBody(cfg.Config)) - } - - if n.IgnoreWarnings { - // If we _only_ have warnings then we'll return nil. - if diags.HasErrors() { - return nil, diags.NonFatalErr() - } - return nil, nil - } else { - // We'll return an error if there are any diagnostics at all, even if - // some of them are warnings. - return nil, diags.NonFatalErr() - } -} - -func (n *EvalValidateResource) validateCount(ctx EvalContext, expr hcl.Expression) tfdiags.Diagnostics { - if expr == nil { - return nil - } - - var diags tfdiags.Diagnostics - - countVal, countDiags := ctx.EvaluateExpr(expr, cty.Number, nil) - diags = diags.Append(countDiags) - if diags.HasErrors() { - return diags - } - - if countVal.IsNull() { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count argument", - Detail: `The given "count" argument value is null. An integer is required.`, - Subject: expr.Range().Ptr(), - }) - return diags - } - - var err error - countVal, err = convert.Convert(countVal, cty.Number) - if err != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count argument", - Detail: fmt.Sprintf(`The given "count" argument value is unsuitable: %s.`, err), - Subject: expr.Range().Ptr(), - }) - return diags - } - - // If the value isn't known then that's the best we can do for now, but - // we'll check more thoroughly during the plan walk. - if !countVal.IsKnown() { - return diags - } - - // If we _do_ know the value, then we can do a few more checks here. - var count int - err = gocty.FromCtyValue(countVal, &count) - if err != nil { - // Isn't a whole number, etc. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count argument", - Detail: fmt.Sprintf(`The given "count" argument value is unsuitable: %s.`, err), - Subject: expr.Range().Ptr(), - }) - return diags - } - - if count < 0 { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count argument", - Detail: `The given "count" argument value is unsuitable: count cannot be negative.`, - Subject: expr.Range().Ptr(), - }) - return diags - } - - return diags -} - -func (n *EvalValidateResource) validateForEach(ctx EvalContext, expr hcl.Expression) (diags tfdiags.Diagnostics) { - _, known, forEachDiags := evaluateResourceForEachExpressionKnown(expr, ctx) - // If the value isn't known then that's the best we can do for now, but - // we'll check more thoroughly during the plan walk - if !known { - return diags - } - - if forEachDiags.HasErrors() { - diags = diags.Append(forEachDiags) - } - - return diags -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_validate_selfref.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_validate_selfref.go deleted file mode 100644 index c9cc0e6d..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_validate_selfref.go +++ /dev/null @@ -1,67 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/lang" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// EvalValidateSelfRef is an EvalNode implementation that checks to ensure that -// expressions within a particular referencable block do not reference that -// same block. -type EvalValidateSelfRef struct { - Addr addrs.Referenceable - Config hcl.Body - ProviderSchema **ProviderSchema -} - -func (n *EvalValidateSelfRef) Eval(ctx EvalContext) (interface{}, error) { - var diags tfdiags.Diagnostics - addr := n.Addr - - addrStrs := make([]string, 0, 1) - addrStrs = append(addrStrs, addr.String()) - switch tAddr := addr.(type) { - case addrs.ResourceInstance: - // A resource instance may not refer to its containing resource either. - addrStrs = append(addrStrs, tAddr.ContainingResource().String()) - } - - if n.ProviderSchema == nil || *n.ProviderSchema == nil { - return nil, fmt.Errorf("provider schema unavailable while validating %s for self-references; this is a bug in Terraform and should be reported", addr) - } - - providerSchema := *n.ProviderSchema - var schema *configschema.Block - switch tAddr := addr.(type) { - case addrs.Resource: - schema, _ = providerSchema.SchemaForResourceAddr(tAddr) - case addrs.ResourceInstance: - schema, _ = providerSchema.SchemaForResourceAddr(tAddr.ContainingResource()) - } - - if schema == nil { - return nil, fmt.Errorf("no schema available for %s to validate for self-references; this is a bug in Terraform and should be reported", addr) - } - - refs, _ := lang.ReferencesInBlock(n.Config, schema) - for _, ref := range refs { - for _, addrStr := range addrStrs { - if ref.Subject.String() == addrStr { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Self-referential block", - Detail: fmt.Sprintf("Configuration for %s may not refer to itself.", addrStr), - Subject: ref.SourceRange.ToHCL().Ptr(), - }) - } - } - } - - return nil, diags.NonFatalErr() -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_variable.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_variable.go deleted file mode 100644 index 79f44b3f..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/eval_variable.go +++ /dev/null @@ -1,96 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" -) - -// EvalSetModuleCallArguments is an EvalNode implementation that sets values -// for arguments of a child module call, for later retrieval during -// expression evaluation. -type EvalSetModuleCallArguments struct { - Module addrs.ModuleCallInstance - Values map[string]cty.Value -} - -// TODO: test -func (n *EvalSetModuleCallArguments) Eval(ctx EvalContext) (interface{}, error) { - ctx.SetModuleCallArguments(n.Module, n.Values) - return nil, nil -} - -// EvalModuleCallArgument is an EvalNode implementation that produces the value -// for a particular variable as will be used by a child module instance. -// -// The result is written into the map given in Values, with its key -// set to the local name of the variable, disregarding the module instance -// address. Any existing values in that map are deleted first. This weird -// interface is a result of trying to be convenient for use with -// EvalContext.SetModuleCallArguments, which expects a map to merge in with -// any existing arguments. -type EvalModuleCallArgument struct { - Addr addrs.InputVariable - Config *configs.Variable - Expr hcl.Expression - - // If this flag is set, any diagnostics are discarded and this operation - // will always succeed, though may produce an unknown value in the - // event of an error. - IgnoreDiagnostics bool - - Values map[string]cty.Value -} - -func (n *EvalModuleCallArgument) Eval(ctx EvalContext) (interface{}, error) { - // Clear out the existing mapping - for k := range n.Values { - delete(n.Values, k) - } - - wantType := n.Config.Type - name := n.Addr.Name - expr := n.Expr - - if expr == nil { - // Should never happen, but we'll bail out early here rather than - // crash in case it does. We set no value at all in this case, - // making a subsequent call to EvalContext.SetModuleCallArguments - // a no-op. - log.Printf("[ERROR] attempt to evaluate %s with nil expression", n.Addr.String()) - return nil, nil - } - - val, diags := ctx.EvaluateExpr(expr, cty.DynamicPseudoType, nil) - - // We intentionally passed DynamicPseudoType to EvaluateExpr above because - // now we can do our own local type conversion and produce an error message - // with better context if it fails. - var convErr error - val, convErr = convert.Convert(val, wantType) - if convErr != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid value for module argument", - Detail: fmt.Sprintf( - "The given value is not suitable for child module variable %q defined at %s: %s.", - name, n.Config.DeclRange.String(), convErr, - ), - Subject: expr.Range().Ptr(), - }) - // We'll return a placeholder unknown value to avoid producing - // redundant downstream errors. - val = cty.UnknownVal(wantType) - } - - n.Values[name] = val - if n.IgnoreDiagnostics { - return nil, nil - } - return nil, diags.ErrWithWarnings() -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/evaltree_provider.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/evaltree_provider.go deleted file mode 100644 index d4a8d3cf..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/evaltree_provider.go +++ /dev/null @@ -1,88 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" -) - -// ProviderEvalTree returns the evaluation tree for initializing and -// configuring providers. -func ProviderEvalTree(n *NodeApplyableProvider, config *configs.Provider) EvalNode { - var provider providers.Interface - - addr := n.Addr - relAddr := addr.ProviderConfig - - seq := make([]EvalNode, 0, 5) - seq = append(seq, &EvalInitProvider{ - TypeName: relAddr.Type, - Addr: addr.ProviderConfig, - }) - - // Input stuff - seq = append(seq, &EvalOpFilter{ - Ops: []walkOperation{walkImport}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: addr, - Output: &provider, - }, - }, - }, - }) - - seq = append(seq, &EvalOpFilter{ - Ops: []walkOperation{walkValidate}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: addr, - Output: &provider, - }, - &EvalValidateProvider{ - Addr: relAddr, - Provider: &provider, - Config: config, - }, - }, - }, - }) - - // Apply stuff - seq = append(seq, &EvalOpFilter{ - Ops: []walkOperation{walkRefresh, walkPlan, walkApply, walkDestroy, walkImport}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: addr, - Output: &provider, - }, - }, - }, - }) - - // We configure on everything but validate, since validate may - // not have access to all the variables. - seq = append(seq, &EvalOpFilter{ - Ops: []walkOperation{walkRefresh, walkPlan, walkApply, walkDestroy, walkImport}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - &EvalConfigProvider{ - Addr: relAddr, - Provider: &provider, - Config: config, - }, - }, - }, - }) - - return &EvalSequence{Nodes: seq} -} - -// CloseProviderEvalTree returns the evaluation tree for closing -// provider connections that aren't needed anymore. -func CloseProviderEvalTree(addr addrs.AbsProviderConfig) EvalNode { - return &EvalCloseProvider{Addr: addr.ProviderConfig} -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/evaluate.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/evaluate.go deleted file mode 100644 index 2d3eabd4..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/evaluate.go +++ /dev/null @@ -1,838 +0,0 @@ -package terraform - -import ( - "fmt" - "os" - "path/filepath" - "sync" - - "github.com/agext/levenshtein" - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/lang" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// Evaluator provides the necessary contextual data for evaluating expressions -// for a particular walk operation. -type Evaluator struct { - // Operation defines what type of operation this evaluator is being used - // for. - Operation walkOperation - - // Meta is contextual metadata about the current operation. - Meta *ContextMeta - - // Config is the root node in the configuration tree. - Config *configs.Config - - // VariableValues is a map from variable names to their associated values, - // within the module indicated by ModulePath. VariableValues is modified - // concurrently, and so it must be accessed only while holding - // VariableValuesLock. - // - // The first map level is string representations of addr.ModuleInstance - // values, while the second level is variable names. - VariableValues map[string]map[string]cty.Value - VariableValuesLock *sync.Mutex - - // Schemas is a repository of all of the schemas we should need to - // evaluate expressions. This must be constructed by the caller to - // include schemas for all of the providers, resource types, data sources - // and provisioners used by the given configuration and state. - // - // This must not be mutated during evaluation. - Schemas *Schemas - - // State is the current state, embedded in a wrapper that ensures that - // it can be safely accessed and modified concurrently. - State *states.SyncState - - // Changes is the set of proposed changes, embedded in a wrapper that - // ensures they can be safely accessed and modified concurrently. - Changes *plans.ChangesSync -} - -// Scope creates an evaluation scope for the given module path and optional -// resource. -// -// If the "self" argument is nil then the "self" object is not available -// in evaluated expressions. Otherwise, it behaves as an alias for the given -// address. -func (e *Evaluator) Scope(data lang.Data, self addrs.Referenceable) *lang.Scope { - return &lang.Scope{ - Data: data, - SelfAddr: self, - PureOnly: e.Operation != walkApply && e.Operation != walkDestroy, - BaseDir: ".", // Always current working directory for now. - } -} - -// evaluationStateData is an implementation of lang.Data that resolves -// references primarily (but not exclusively) using information from a State. -type evaluationStateData struct { - Evaluator *Evaluator - - // ModulePath is the path through the dynamic module tree to the module - // that references will be resolved relative to. - ModulePath addrs.ModuleInstance - - // InstanceKeyData describes the values, if any, that are accessible due - // to repetition of a containing object using "count" or "for_each" - // arguments. (It is _not_ used for the for_each inside "dynamic" blocks, - // since the user specifies in that case which variable name to locally - // shadow.) - InstanceKeyData InstanceKeyEvalData - - // Operation records the type of walk the evaluationStateData is being used - // for. - Operation walkOperation -} - -// InstanceKeyEvalData is used during evaluation to specify which values, -// if any, should be produced for count.index, each.key, and each.value. -type InstanceKeyEvalData struct { - // CountIndex is the value for count.index, or cty.NilVal if evaluating - // in a context where the "count" argument is not active. - // - // For correct operation, this should always be of type cty.Number if not - // nil. - CountIndex cty.Value - - // EachKey and EachValue are the values for each.key and each.value - // respectively, or cty.NilVal if evaluating in a context where the - // "for_each" argument is not active. These must either both be set - // or neither set. - // - // For correct operation, EachKey must always be either of type cty.String - // or cty.Number if not nil. - EachKey, EachValue cty.Value -} - -// EvalDataForInstanceKey constructs a suitable InstanceKeyEvalData for -// evaluating in a context that has the given instance key. -func EvalDataForInstanceKey(key addrs.InstanceKey, forEachMap map[string]cty.Value) InstanceKeyEvalData { - var countIdx cty.Value - var eachKey cty.Value - var eachVal cty.Value - - if intKey, ok := key.(addrs.IntKey); ok { - countIdx = cty.NumberIntVal(int64(intKey)) - } - - if stringKey, ok := key.(addrs.StringKey); ok { - eachKey = cty.StringVal(string(stringKey)) - eachVal = forEachMap[string(stringKey)] - } - - return InstanceKeyEvalData{ - CountIndex: countIdx, - EachKey: eachKey, - EachValue: eachVal, - } -} - -// EvalDataForNoInstanceKey is a value of InstanceKeyData that sets no instance -// key values at all, suitable for use in contexts where no keyed instance -// is relevant. -var EvalDataForNoInstanceKey = InstanceKeyEvalData{} - -// evaluationStateData must implement lang.Data -var _ lang.Data = (*evaluationStateData)(nil) - -func (d *evaluationStateData) GetCountAttr(addr addrs.CountAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - switch addr.Name { - - case "index": - idxVal := d.InstanceKeyData.CountIndex - if idxVal == cty.NilVal { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Reference to "count" in non-counted context`, - Detail: fmt.Sprintf(`The "count" object can be used only in "resource" and "data" blocks, and only when the "count" argument is set.`), - Subject: rng.ToHCL().Ptr(), - }) - return cty.UnknownVal(cty.Number), diags - } - return idxVal, diags - - default: - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Invalid "count" attribute`, - Detail: fmt.Sprintf(`The "count" object does not have an attribute named %q. The only supported attribute is count.index, which is the index of each instance of a resource block that has the "count" argument set.`, addr.Name), - Subject: rng.ToHCL().Ptr(), - }) - return cty.DynamicVal, diags - } -} - -func (d *evaluationStateData) GetForEachAttr(addr addrs.ForEachAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - var returnVal cty.Value - switch addr.Name { - - case "key": - returnVal = d.InstanceKeyData.EachKey - case "value": - returnVal = d.InstanceKeyData.EachValue - default: - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Invalid "each" attribute`, - Detail: fmt.Sprintf(`The "each" object does not have an attribute named %q. The supported attributes are each.key and each.value, the current key and value pair of the "for_each" attribute set.`, addr.Name), - Subject: rng.ToHCL().Ptr(), - }) - return cty.DynamicVal, diags - } - - if returnVal == cty.NilVal { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Reference to "each" in context without for_each`, - Detail: fmt.Sprintf(`The "each" object can be used only in "resource" blocks, and only when the "for_each" argument is set.`), - Subject: rng.ToHCL().Ptr(), - }) - return cty.UnknownVal(cty.DynamicPseudoType), diags - } - return returnVal, diags -} - -func (d *evaluationStateData) GetInputVariable(addr addrs.InputVariable, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - // First we'll make sure the requested value is declared in configuration, - // so we can produce a nice message if not. - moduleConfig := d.Evaluator.Config.DescendentForInstance(d.ModulePath) - if moduleConfig == nil { - // should never happen, since we can't be evaluating in a module - // that wasn't mentioned in configuration. - panic(fmt.Sprintf("input variable read from %s, which has no configuration", d.ModulePath)) - } - - config := moduleConfig.Module.Variables[addr.Name] - if config == nil { - var suggestions []string - for k := range moduleConfig.Module.Variables { - suggestions = append(suggestions, k) - } - suggestion := nameSuggestion(addr.Name, suggestions) - if suggestion != "" { - suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) - } else { - suggestion = fmt.Sprintf(" This variable can be declared with a variable %q {} block.", addr.Name) - } - - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Reference to undeclared input variable`, - Detail: fmt.Sprintf(`An input variable with the name %q has not been declared.%s`, addr.Name, suggestion), - Subject: rng.ToHCL().Ptr(), - }) - return cty.DynamicVal, diags - } - - wantType := cty.DynamicPseudoType - if config.Type != cty.NilType { - wantType = config.Type - } - - d.Evaluator.VariableValuesLock.Lock() - defer d.Evaluator.VariableValuesLock.Unlock() - - // During the validate walk, input variables are always unknown so - // that we are validating the configuration for all possible input values - // rather than for a specific set. Checking against a specific set of - // input values then happens during the plan walk. - // - // This is important because otherwise the validation walk will tend to be - // overly strict, requiring expressions throughout the configuration to - // be complicated to accommodate all possible inputs, whereas returning - // known here allows for simpler patterns like using input values as - // guards to broadly enable/disable resources, avoid processing things - // that are disabled, etc. Terraform's static validation leans towards - // being liberal in what it accepts because the subsequent plan walk has - // more information available and so can be more conservative. - if d.Operation == walkValidate { - return cty.UnknownVal(wantType), diags - } - - moduleAddrStr := d.ModulePath.String() - vals := d.Evaluator.VariableValues[moduleAddrStr] - if vals == nil { - return cty.UnknownVal(wantType), diags - } - - val, isSet := vals[addr.Name] - if !isSet { - if config.Default != cty.NilVal { - return config.Default, diags - } - return cty.UnknownVal(wantType), diags - } - - var err error - val, err = convert.Convert(val, wantType) - if err != nil { - // We should never get here because this problem should've been caught - // during earlier validation, but we'll do something reasonable anyway. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Incorrect variable type`, - Detail: fmt.Sprintf(`The resolved value of variable %q is not appropriate: %s.`, addr.Name, err), - Subject: &config.DeclRange, - }) - // Stub out our return value so that the semantic checker doesn't - // produce redundant downstream errors. - val = cty.UnknownVal(wantType) - } - - return val, diags -} - -func (d *evaluationStateData) GetLocalValue(addr addrs.LocalValue, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - // First we'll make sure the requested value is declared in configuration, - // so we can produce a nice message if not. - moduleConfig := d.Evaluator.Config.DescendentForInstance(d.ModulePath) - if moduleConfig == nil { - // should never happen, since we can't be evaluating in a module - // that wasn't mentioned in configuration. - panic(fmt.Sprintf("local value read from %s, which has no configuration", d.ModulePath)) - } - - config := moduleConfig.Module.Locals[addr.Name] - if config == nil { - var suggestions []string - for k := range moduleConfig.Module.Locals { - suggestions = append(suggestions, k) - } - suggestion := nameSuggestion(addr.Name, suggestions) - if suggestion != "" { - suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) - } - - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Reference to undeclared local value`, - Detail: fmt.Sprintf(`A local value with the name %q has not been declared.%s`, addr.Name, suggestion), - Subject: rng.ToHCL().Ptr(), - }) - return cty.DynamicVal, diags - } - - val := d.Evaluator.State.LocalValue(addr.Absolute(d.ModulePath)) - if val == cty.NilVal { - // Not evaluated yet? - val = cty.DynamicVal - } - - return val, diags -} - -func (d *evaluationStateData) GetModuleInstance(addr addrs.ModuleCallInstance, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - // Output results live in the module that declares them, which is one of - // the child module instances of our current module path. - moduleAddr := addr.ModuleInstance(d.ModulePath) - - // We'll consult the configuration to see what output names we are - // expecting, so we can ensure the resulting object is of the expected - // type even if our data is incomplete for some reason. - moduleConfig := d.Evaluator.Config.DescendentForInstance(moduleAddr) - if moduleConfig == nil { - // should never happen, since this should've been caught during - // static validation. - panic(fmt.Sprintf("output value read from %s, which has no configuration", moduleAddr)) - } - outputConfigs := moduleConfig.Module.Outputs - - vals := map[string]cty.Value{} - for n := range outputConfigs { - addr := addrs.OutputValue{Name: n}.Absolute(moduleAddr) - - // If a pending change is present in our current changeset then its value - // takes priority over what's in state. (It will usually be the same but - // will differ if the new value is unknown during planning.) - if changeSrc := d.Evaluator.Changes.GetOutputChange(addr); changeSrc != nil { - change, err := changeSrc.Decode() - if err != nil { - // This should happen only if someone has tampered with a plan - // file, so we won't bother with a pretty error for it. - diags = diags.Append(fmt.Errorf("planned change for %s could not be decoded: %s", addr, err)) - vals[n] = cty.DynamicVal - continue - } - // We care only about the "after" value, which is the value this output - // will take on after the plan is applied. - vals[n] = change.After - } else { - os := d.Evaluator.State.OutputValue(addr) - if os == nil { - // Not evaluated yet? - vals[n] = cty.DynamicVal - continue - } - vals[n] = os.Value - } - } - return cty.ObjectVal(vals), diags -} - -func (d *evaluationStateData) GetModuleInstanceOutput(addr addrs.ModuleCallOutput, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - // Output results live in the module that declares them, which is one of - // the child module instances of our current module path. - absAddr := addr.AbsOutputValue(d.ModulePath) - moduleAddr := absAddr.Module - - // First we'll consult the configuration to see if an output of this - // name is declared at all. - moduleConfig := d.Evaluator.Config.DescendentForInstance(moduleAddr) - if moduleConfig == nil { - // this doesn't happen in normal circumstances due to our validation - // pass, but it can turn up in some unusual situations, like in the - // "terraform console" repl where arbitrary expressions can be - // evaluated. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Reference to undeclared module`, - Detail: fmt.Sprintf(`The configuration contains no %s.`, moduleAddr), - Subject: rng.ToHCL().Ptr(), - }) - return cty.DynamicVal, diags - } - - config := moduleConfig.Module.Outputs[addr.Name] - if config == nil { - var suggestions []string - for k := range moduleConfig.Module.Outputs { - suggestions = append(suggestions, k) - } - suggestion := nameSuggestion(addr.Name, suggestions) - if suggestion != "" { - suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) - } - - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Reference to undeclared output value`, - Detail: fmt.Sprintf(`An output value with the name %q has not been declared in %s.%s`, addr.Name, moduleDisplayAddr(moduleAddr), suggestion), - Subject: rng.ToHCL().Ptr(), - }) - return cty.DynamicVal, diags - } - - // If a pending change is present in our current changeset then its value - // takes priority over what's in state. (It will usually be the same but - // will differ if the new value is unknown during planning.) - if changeSrc := d.Evaluator.Changes.GetOutputChange(absAddr); changeSrc != nil { - change, err := changeSrc.Decode() - if err != nil { - // This should happen only if someone has tampered with a plan - // file, so we won't bother with a pretty error for it. - diags = diags.Append(fmt.Errorf("planned change for %s could not be decoded: %s", absAddr, err)) - return cty.DynamicVal, diags - } - // We care only about the "after" value, which is the value this output - // will take on after the plan is applied. - return change.After, diags - } - - os := d.Evaluator.State.OutputValue(absAddr) - if os == nil { - // Not evaluated yet? - return cty.DynamicVal, diags - } - - return os.Value, diags -} - -func (d *evaluationStateData) GetPathAttr(addr addrs.PathAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - switch addr.Name { - - case "cwd": - wd, err := os.Getwd() - if err != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Failed to get working directory`, - Detail: fmt.Sprintf(`The value for path.cwd cannot be determined due to a system error: %s`, err), - Subject: rng.ToHCL().Ptr(), - }) - return cty.DynamicVal, diags - } - return cty.StringVal(filepath.ToSlash(wd)), diags - - case "module": - moduleConfig := d.Evaluator.Config.DescendentForInstance(d.ModulePath) - if moduleConfig == nil { - // should never happen, since we can't be evaluating in a module - // that wasn't mentioned in configuration. - panic(fmt.Sprintf("module.path read from module %s, which has no configuration", d.ModulePath)) - } - sourceDir := moduleConfig.Module.SourceDir - return cty.StringVal(filepath.ToSlash(sourceDir)), diags - - case "root": - sourceDir := d.Evaluator.Config.Module.SourceDir - return cty.StringVal(filepath.ToSlash(sourceDir)), diags - - default: - suggestion := nameSuggestion(addr.Name, []string{"cwd", "module", "root"}) - if suggestion != "" { - suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) - } - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Invalid "path" attribute`, - Detail: fmt.Sprintf(`The "path" object does not have an attribute named %q.%s`, addr.Name, suggestion), - Subject: rng.ToHCL().Ptr(), - }) - return cty.DynamicVal, diags - } -} - -func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - // First we'll consult the configuration to see if an resource of this - // name is declared at all. - moduleAddr := d.ModulePath - moduleConfig := d.Evaluator.Config.DescendentForInstance(moduleAddr) - if moduleConfig == nil { - // should never happen, since we can't be evaluating in a module - // that wasn't mentioned in configuration. - panic(fmt.Sprintf("resource value read from %s, which has no configuration", moduleAddr)) - } - - config := moduleConfig.Module.ResourceByAddr(addr) - if config == nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Reference to undeclared resource`, - Detail: fmt.Sprintf(`A resource %q %q has not been declared in %s`, addr.Type, addr.Name, moduleDisplayAddr(moduleAddr)), - Subject: rng.ToHCL().Ptr(), - }) - return cty.DynamicVal, diags - } - - rs := d.Evaluator.State.Resource(addr.Absolute(d.ModulePath)) - - if rs == nil { - // we must return DynamicVal so that both interpretations - // can proceed without generating errors, and we'll deal with this - // in a later step where more information is gathered. - // (In practice we should only end up here during the validate walk, - // since later walks should have at least partial states populated - // for all resources in the configuration.) - return cty.DynamicVal, diags - } - - // Break out early during validation, because resource may not be expanded - // yet and indexed references may show up as invalid. - if d.Operation == walkValidate { - return cty.DynamicVal, diags - } - - return d.getResourceInstancesAll(addr, rng, config, rs, rs.ProviderConfig) -} - -func (d *evaluationStateData) getResourceInstancesAll(addr addrs.Resource, rng tfdiags.SourceRange, config *configs.Resource, rs *states.Resource, providerAddr addrs.AbsProviderConfig) (cty.Value, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - instAddr := addrs.ResourceInstance{Resource: addr, Key: addrs.NoKey} - - schema := d.getResourceSchema(addr, providerAddr) - if schema == nil { - // This shouldn't happen, since validation before we get here should've - // taken care of it, but we'll show a reasonable error message anyway. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Missing resource type schema`, - Detail: fmt.Sprintf("No schema is available for %s in %s. This is a bug in Terraform and should be reported.", addr, providerAddr), - Subject: rng.ToHCL().Ptr(), - }) - return cty.DynamicVal, diags - } - - switch rs.EachMode { - case states.NoEach: - ty := schema.ImpliedType() - is := rs.Instances[addrs.NoKey] - if is == nil || is.Current == nil { - // Assume we're dealing with an instance that hasn't been created yet. - return cty.UnknownVal(ty), diags - } - - if is.Current.Status == states.ObjectPlanned { - // If there's a pending change for this instance in our plan, we'll prefer - // that. This is important because the state can't represent unknown values - // and so its data is inaccurate when changes are pending. - if change := d.Evaluator.Changes.GetResourceInstanceChange(instAddr.Absolute(d.ModulePath), states.CurrentGen); change != nil { - val, err := change.After.Decode(ty) - if err != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid resource instance data in plan", - Detail: fmt.Sprintf("Instance %s data could not be decoded from the plan: %s.", addr.Absolute(d.ModulePath), err), - Subject: &config.DeclRange, - }) - return cty.UnknownVal(ty), diags - } - return val, diags - } else { - // If the object is in planned status then we should not - // get here, since we should've found a pending value - // in the plan above instead. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Missing pending object in plan", - Detail: fmt.Sprintf("Instance %s is marked as having a change pending but that change is not recorded in the plan. This is a bug in Terraform; please report it.", addr), - Subject: &config.DeclRange, - }) - return cty.UnknownVal(ty), diags - } - } - - ios, err := is.Current.Decode(ty) - if err != nil { - // This shouldn't happen, since by the time we get here - // we should've upgraded the state data already. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid resource instance data in state", - Detail: fmt.Sprintf("Instance %s data could not be decoded from the state: %s.", addr.Absolute(d.ModulePath), err), - Subject: &config.DeclRange, - }) - return cty.UnknownVal(ty), diags - } - - return ios.Value, diags - - case states.EachList: - // We need to infer the length of our resulting tuple by searching - // for the max IntKey in our instances map. - length := 0 - for k := range rs.Instances { - if ik, ok := k.(addrs.IntKey); ok { - if int(ik) >= length { - length = int(ik) + 1 - } - } - } - - vals := make([]cty.Value, length) - for i := 0; i < length; i++ { - ty := schema.ImpliedType() - key := addrs.IntKey(i) - is, exists := rs.Instances[key] - if exists && is.Current != nil { - instAddr := addr.Instance(key).Absolute(d.ModulePath) - - // Prefer pending value in plan if present. See getResourceInstanceSingle - // comment for the rationale. - if is.Current.Status == states.ObjectPlanned { - if change := d.Evaluator.Changes.GetResourceInstanceChange(instAddr, states.CurrentGen); change != nil { - val, err := change.After.Decode(ty) - if err != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid resource instance data in plan", - Detail: fmt.Sprintf("Instance %s data could not be decoded from the plan: %s.", instAddr, err), - Subject: &config.DeclRange, - }) - continue - } - vals[i] = val - continue - } else { - // If the object is in planned status then we should not - // get here, since we should've found a pending value - // in the plan above instead. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Missing pending object in plan", - Detail: fmt.Sprintf("Instance %s is marked as having a change pending but that change is not recorded in the plan. This is a bug in Terraform; please report it.", instAddr), - Subject: &config.DeclRange, - }) - continue - } - } - - ios, err := is.Current.Decode(ty) - if err != nil { - // This shouldn't happen, since by the time we get here - // we should've upgraded the state data already. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid resource instance data in state", - Detail: fmt.Sprintf("Instance %s data could not be decoded from the state: %s.", instAddr, err), - Subject: &config.DeclRange, - }) - continue - } - vals[i] = ios.Value - } else { - // There shouldn't normally be "gaps" in our list but we'll - // allow it under the assumption that we're in a weird situation - // where e.g. someone has run "terraform state mv" to reorder - // a list and left a hole behind. - vals[i] = cty.UnknownVal(schema.ImpliedType()) - } - } - - // We use a tuple rather than a list here because resource schemas may - // include dynamically-typed attributes, which will then cause each - // instance to potentially have a different runtime type even though - // they all conform to the static schema. - return cty.TupleVal(vals), diags - - case states.EachMap: - ty := schema.ImpliedType() - vals := make(map[string]cty.Value, len(rs.Instances)) - for k, is := range rs.Instances { - if sk, ok := k.(addrs.StringKey); ok { - instAddr := addr.Instance(k).Absolute(d.ModulePath) - - // Prefer pending value in plan if present. See getResourceInstanceSingle - // comment for the rationale. - // Prefer pending value in plan if present. See getResourceInstanceSingle - // comment for the rationale. - if is.Current.Status == states.ObjectPlanned { - if change := d.Evaluator.Changes.GetResourceInstanceChange(instAddr, states.CurrentGen); change != nil { - val, err := change.After.Decode(ty) - if err != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid resource instance data in plan", - Detail: fmt.Sprintf("Instance %s data could not be decoded from the plan: %s.", instAddr, err), - Subject: &config.DeclRange, - }) - continue - } - vals[string(sk)] = val - continue - } else { - // If the object is in planned status then we should not - // get here, since we should've found a pending value - // in the plan above instead. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Missing pending object in plan", - Detail: fmt.Sprintf("Instance %s is marked as having a change pending but that change is not recorded in the plan. This is a bug in Terraform; please report it.", instAddr), - Subject: &config.DeclRange, - }) - continue - } - } - - ios, err := is.Current.Decode(ty) - if err != nil { - // This shouldn't happen, since by the time we get here - // we should've upgraded the state data already. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid resource instance data in state", - Detail: fmt.Sprintf("Instance %s data could not be decoded from the state: %s.", instAddr, err), - Subject: &config.DeclRange, - }) - continue - } - vals[string(sk)] = ios.Value - } - } - - // We use an object rather than a map here because resource schemas may - // include dynamically-typed attributes, which will then cause each - // instance to potentially have a different runtime type even though - // they all conform to the static schema. - return cty.ObjectVal(vals), diags - - default: - // Should never happen since caller should deal with other modes - panic(fmt.Sprintf("unsupported EachMode %s", rs.EachMode)) - } -} - -func (d *evaluationStateData) getResourceSchema(addr addrs.Resource, providerAddr addrs.AbsProviderConfig) *configschema.Block { - providerType := providerAddr.ProviderConfig.Type - schemas := d.Evaluator.Schemas - schema, _ := schemas.ResourceTypeConfig(providerType, addr.Mode, addr.Type) - return schema -} - -func (d *evaluationStateData) GetTerraformAttr(addr addrs.TerraformAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - switch addr.Name { - - case "workspace": - workspaceName := d.Evaluator.Meta.Env - return cty.StringVal(workspaceName), diags - - case "env": - // Prior to Terraform 0.12 there was an attribute "env", which was - // an alias name for "workspace". This was deprecated and is now - // removed. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Invalid "terraform" attribute`, - Detail: `The terraform.env attribute was deprecated in v0.10 and removed in v0.12. The "state environment" concept was rename to "workspace" in v0.12, and so the workspace name can now be accessed using the terraform.workspace attribute.`, - Subject: rng.ToHCL().Ptr(), - }) - return cty.DynamicVal, diags - - default: - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Invalid "terraform" attribute`, - Detail: fmt.Sprintf(`The "terraform" object does not have an attribute named %q. The only supported attribute is terraform.workspace, the name of the currently-selected workspace.`, addr.Name), - Subject: rng.ToHCL().Ptr(), - }) - return cty.DynamicVal, diags - } -} - -// nameSuggestion tries to find a name from the given slice of suggested names -// that is close to the given name and returns it if found. If no suggestion -// is close enough, returns the empty string. -// -// The suggestions are tried in order, so earlier suggestions take precedence -// if the given string is similar to two or more suggestions. -// -// This function is intended to be used with a relatively-small number of -// suggestions. It's not optimized for hundreds or thousands of them. -func nameSuggestion(given string, suggestions []string) string { - for _, suggestion := range suggestions { - dist := levenshtein.Distance(given, suggestion, nil) - if dist < 3 { // threshold determined experimentally - return suggestion - } - } - return "" -} - -// moduleDisplayAddr returns a string describing the given module instance -// address that is appropriate for returning to users in situations where the -// root module is possible. Specifically, it returns "the root module" if the -// root module instance is given, or a string representation of the module -// address otherwise. -func moduleDisplayAddr(addr addrs.ModuleInstance) string { - switch { - case addr.IsRoot(): - return "the root module" - default: - return addr.String() - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/evaluate_valid.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/evaluate_valid.go deleted file mode 100644 index 35a8be0c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/evaluate_valid.go +++ /dev/null @@ -1,299 +0,0 @@ -package terraform - -import ( - "fmt" - "sort" - - "github.com/hashicorp/hcl/v2" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/helper/didyoumean" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// StaticValidateReferences checks the given references against schemas and -// other statically-checkable rules, producing error diagnostics if any -// problems are found. -// -// If this method returns errors for a particular reference then evaluating -// that reference is likely to generate a very similar error, so callers should -// not run this method and then also evaluate the source expression(s) and -// merge the two sets of diagnostics together, since this will result in -// confusing redundant errors. -// -// This method can find more errors than can be found by evaluating an -// expression with a partially-populated scope, since it checks the referenced -// names directly against the schema rather than relying on evaluation errors. -// -// The result may include warning diagnostics if, for example, deprecated -// features are referenced. -func (d *evaluationStateData) StaticValidateReferences(refs []*addrs.Reference, self addrs.Referenceable) tfdiags.Diagnostics { - var diags tfdiags.Diagnostics - for _, ref := range refs { - moreDiags := d.staticValidateReference(ref, self) - diags = diags.Append(moreDiags) - } - return diags -} - -func (d *evaluationStateData) staticValidateReference(ref *addrs.Reference, self addrs.Referenceable) tfdiags.Diagnostics { - modCfg := d.Evaluator.Config.DescendentForInstance(d.ModulePath) - if modCfg == nil { - // This is a bug in the caller rather than a problem with the - // reference, but rather than crashing out here in an unhelpful way - // we'll just ignore it and trust a different layer to catch it. - return nil - } - - if ref.Subject == addrs.Self { - // The "self" address is a special alias for the address given as - // our self parameter here, if present. - if self == nil { - var diags tfdiags.Diagnostics - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Invalid "self" reference`, - // This detail message mentions some current practice that - // this codepath doesn't really "know about". If the "self" - // object starts being supported in more contexts later then - // we'll need to adjust this message. - Detail: `The "self" object is not available in this context. This object can be used only in resource provisioner and connection blocks.`, - Subject: ref.SourceRange.ToHCL().Ptr(), - }) - return diags - } - - synthRef := *ref // shallow copy - synthRef.Subject = self - ref = &synthRef - } - - switch addr := ref.Subject.(type) { - - // For static validation we validate both resource and resource instance references the same way. - // We mostly disregard the index, though we do some simple validation of - // its _presence_ in staticValidateSingleResourceReference and - // staticValidateMultiResourceReference respectively. - case addrs.Resource: - var diags tfdiags.Diagnostics - diags = diags.Append(d.staticValidateSingleResourceReference(modCfg, addr, ref.Remaining, ref.SourceRange)) - diags = diags.Append(d.staticValidateResourceReference(modCfg, addr, ref.Remaining, ref.SourceRange)) - return diags - case addrs.ResourceInstance: - var diags tfdiags.Diagnostics - diags = diags.Append(d.staticValidateMultiResourceReference(modCfg, addr, ref.Remaining, ref.SourceRange)) - diags = diags.Append(d.staticValidateResourceReference(modCfg, addr.ContainingResource(), ref.Remaining, ref.SourceRange)) - return diags - - // We also handle all module call references the same way, disregarding index. - case addrs.ModuleCall: - return d.staticValidateModuleCallReference(modCfg, addr, ref.Remaining, ref.SourceRange) - case addrs.ModuleCallInstance: - return d.staticValidateModuleCallReference(modCfg, addr.Call, ref.Remaining, ref.SourceRange) - case addrs.ModuleCallOutput: - // This one is a funny one because we will take the output name referenced - // and use it to fake up a "remaining" that would make sense for the - // module call itself, rather than for the specific output, and then - // we can just re-use our static module call validation logic. - remain := make(hcl.Traversal, len(ref.Remaining)+1) - copy(remain[1:], ref.Remaining) - remain[0] = hcl.TraverseAttr{ - Name: addr.Name, - - // Using the whole reference as the source range here doesn't exactly - // match how HCL would normally generate an attribute traversal, - // but is close enough for our purposes. - SrcRange: ref.SourceRange.ToHCL(), - } - return d.staticValidateModuleCallReference(modCfg, addr.Call.Call, remain, ref.SourceRange) - - default: - // Anything else we'll just permit through without any static validation - // and let it be caught during dynamic evaluation, in evaluate.go . - return nil - } -} - -func (d *evaluationStateData) staticValidateSingleResourceReference(modCfg *configs.Config, addr addrs.Resource, remain hcl.Traversal, rng tfdiags.SourceRange) tfdiags.Diagnostics { - // If we have at least one step in "remain" and this resource has - // "count" set then we know for sure this in invalid because we have - // something like: - // aws_instance.foo.bar - // ...when we really need - // aws_instance.foo[count.index].bar - - // It is _not_ safe to do this check when remain is empty, because that - // would also match aws_instance.foo[count.index].bar due to `count.index` - // not being statically-resolvable as part of a reference, and match - // direct references to the whole aws_instance.foo tuple. - if len(remain) == 0 { - return nil - } - - var diags tfdiags.Diagnostics - - cfg := modCfg.Module.ResourceByAddr(addr) - if cfg == nil { - // We'll just bail out here and catch this in our subsequent call to - // staticValidateResourceReference, then. - return diags - } - - if cfg.Count != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Missing resource instance key`, - Detail: fmt.Sprintf("Because %s has \"count\" set, its attributes must be accessed on specific instances.\n\nFor example, to correlate with indices of a referring resource, use:\n %s[count.index]", addr, addr), - Subject: rng.ToHCL().Ptr(), - }) - } - if cfg.ForEach != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Missing resource instance key`, - Detail: fmt.Sprintf("Because %s has \"for_each\" set, its attributes must be accessed on specific instances.\n\nFor example, to correlate with indices of a referring resource, use:\n %s[each.key]", addr, addr), - Subject: rng.ToHCL().Ptr(), - }) - } - - return diags -} - -func (d *evaluationStateData) staticValidateMultiResourceReference(modCfg *configs.Config, addr addrs.ResourceInstance, remain hcl.Traversal, rng tfdiags.SourceRange) tfdiags.Diagnostics { - var diags tfdiags.Diagnostics - - cfg := modCfg.Module.ResourceByAddr(addr.ContainingResource()) - if cfg == nil { - // We'll just bail out here and catch this in our subsequent call to - // staticValidateResourceReference, then. - return diags - } - - if addr.Key == addrs.NoKey { - // This is a different path into staticValidateSingleResourceReference - return d.staticValidateSingleResourceReference(modCfg, addr.ContainingResource(), remain, rng) - } else { - if cfg.Count == nil && cfg.ForEach == nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Unexpected resource instance key`, - Detail: fmt.Sprintf(`Because %s does not have "count" or "for_each" set, references to it must not include an index key. Remove the bracketed index to refer to the single instance of this resource.`, addr.ContainingResource()), - Subject: rng.ToHCL().Ptr(), - }) - } - } - - return diags -} - -func (d *evaluationStateData) staticValidateResourceReference(modCfg *configs.Config, addr addrs.Resource, remain hcl.Traversal, rng tfdiags.SourceRange) tfdiags.Diagnostics { - var diags tfdiags.Diagnostics - - var modeAdjective string - switch addr.Mode { - case addrs.ManagedResourceMode: - modeAdjective = "managed" - case addrs.DataResourceMode: - modeAdjective = "data" - default: - // should never happen - modeAdjective = "" - } - - cfg := modCfg.Module.ResourceByAddr(addr) - if cfg == nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Reference to undeclared resource`, - Detail: fmt.Sprintf(`A %s resource %q %q has not been declared in %s.`, modeAdjective, addr.Type, addr.Name, moduleConfigDisplayAddr(modCfg.Path)), - Subject: rng.ToHCL().Ptr(), - }) - return diags - } - - // Normally accessing this directly is wrong because it doesn't take into - // account provider inheritance, etc but it's okay here because we're only - // paying attention to the type anyway. - providerType := cfg.ProviderConfigAddr().Type - schema, _ := d.Evaluator.Schemas.ResourceTypeConfig(providerType, addr.Mode, addr.Type) - - if schema == nil { - // Prior validation should've taken care of a resource block with an - // unsupported type, so we should never get here but we'll handle it - // here anyway for robustness. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Invalid resource type`, - Detail: fmt.Sprintf(`A %s resource type %q is not supported by provider %q.`, modeAdjective, addr.Type, providerType), - Subject: rng.ToHCL().Ptr(), - }) - return diags - } - - // As a special case we'll detect attempts to access an attribute called - // "count" and produce a special error for it, since versions of Terraform - // prior to v0.12 offered this as a weird special case that we can no - // longer support. - if len(remain) > 0 { - if step, ok := remain[0].(hcl.TraverseAttr); ok && step.Name == "count" { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Invalid resource count attribute`, - Detail: fmt.Sprintf(`The special "count" attribute is no longer supported after Terraform v0.12. Instead, use length(%s) to count resource instances.`, addr), - Subject: rng.ToHCL().Ptr(), - }) - return diags - } - } - - // If we got this far then we'll try to validate the remaining traversal - // steps against our schema. - moreDiags := schema.StaticValidateTraversal(remain) - diags = diags.Append(moreDiags) - - return diags -} - -func (d *evaluationStateData) staticValidateModuleCallReference(modCfg *configs.Config, addr addrs.ModuleCall, remain hcl.Traversal, rng tfdiags.SourceRange) tfdiags.Diagnostics { - var diags tfdiags.Diagnostics - - // For now, our focus here is just in testing that the referenced module - // call exists. All other validation is deferred until evaluation time. - _, exists := modCfg.Module.ModuleCalls[addr.Name] - if !exists { - var suggestions []string - for name := range modCfg.Module.ModuleCalls { - suggestions = append(suggestions, name) - } - sort.Strings(suggestions) - suggestion := didyoumean.NameSuggestion(addr.Name, suggestions) - if suggestion != "" { - suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) - } - - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: `Reference to undeclared module`, - Detail: fmt.Sprintf(`No module call named %q is declared in %s.%s`, addr.Name, moduleConfigDisplayAddr(modCfg.Path), suggestion), - Subject: rng.ToHCL().Ptr(), - }) - return diags - } - - return diags -} - -// moduleConfigDisplayAddr returns a string describing the given module -// address that is appropriate for returning to users in situations where the -// root module is possible. Specifically, it returns "the root module" if the -// root module instance is given, or a string representation of the module -// address otherwise. -func moduleConfigDisplayAddr(addr addrs.Module) string { - switch { - case addr.IsRoot(): - return "the root module" - default: - return addr.String() - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph.go deleted file mode 100644 index 36e295b6..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph.go +++ /dev/null @@ -1,141 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" -) - -// Graph represents the graph that Terraform uses to represent resources -// and their dependencies. -type Graph struct { - // Graph is the actual DAG. This is embedded so you can call the DAG - // methods directly. - dag.AcyclicGraph - - // Path is the path in the module tree that this Graph represents. - Path addrs.ModuleInstance - - // debugName is a name for reference in the debug output. This is usually - // to indicate what topmost builder was, and if this graph is a shadow or - // not. - debugName string -} - -func (g *Graph) DirectedGraph() dag.Grapher { - return &g.AcyclicGraph -} - -// Walk walks the graph with the given walker for callbacks. The graph -// will be walked with full parallelism, so the walker should expect -// to be called in concurrently. -func (g *Graph) Walk(walker GraphWalker) tfdiags.Diagnostics { - return g.walk(walker) -} - -func (g *Graph) walk(walker GraphWalker) tfdiags.Diagnostics { - // The callbacks for enter/exiting a graph - ctx := walker.EnterPath(g.Path) - defer walker.ExitPath(g.Path) - - // Get the path for logs - path := ctx.Path().String() - - debugName := "walk-graph.json" - if g.debugName != "" { - debugName = g.debugName + "-" + debugName - } - - // Walk the graph. - var walkFn dag.WalkFunc - walkFn = func(v dag.Vertex) (diags tfdiags.Diagnostics) { - log.Printf("[TRACE] vertex %q: starting visit (%T)", dag.VertexName(v), v) - g.DebugVisitInfo(v, g.debugName) - - defer func() { - log.Printf("[TRACE] vertex %q: visit complete", dag.VertexName(v)) - }() - - walker.EnterVertex(v) - defer walker.ExitVertex(v, diags) - - // vertexCtx is the context that we use when evaluating. This - // is normally the context of our graph but can be overridden - // with a GraphNodeSubPath impl. - vertexCtx := ctx - if pn, ok := v.(GraphNodeSubPath); ok && len(pn.Path()) > 0 { - vertexCtx = walker.EnterPath(pn.Path()) - defer walker.ExitPath(pn.Path()) - } - - // If the node is eval-able, then evaluate it. - if ev, ok := v.(GraphNodeEvalable); ok { - tree := ev.EvalTree() - if tree == nil { - panic(fmt.Sprintf("%q (%T): nil eval tree", dag.VertexName(v), v)) - } - - // Allow the walker to change our tree if needed. Eval, - // then callback with the output. - log.Printf("[TRACE] vertex %q: evaluating", dag.VertexName(v)) - - g.DebugVertexInfo(v, fmt.Sprintf("evaluating %T(%s)", v, path)) - - tree = walker.EnterEvalTree(v, tree) - output, err := Eval(tree, vertexCtx) - diags = diags.Append(walker.ExitEvalTree(v, output, err)) - if diags.HasErrors() { - return - } - } - - // If the node is dynamically expanded, then expand it - if ev, ok := v.(GraphNodeDynamicExpandable); ok { - log.Printf("[TRACE] vertex %q: expanding dynamic subgraph", dag.VertexName(v)) - - g.DebugVertexInfo(v, fmt.Sprintf("expanding %T(%s)", v, path)) - - g, err := ev.DynamicExpand(vertexCtx) - if err != nil { - diags = diags.Append(err) - return - } - if g != nil { - // Walk the subgraph - log.Printf("[TRACE] vertex %q: entering dynamic subgraph", dag.VertexName(v)) - subDiags := g.walk(walker) - diags = diags.Append(subDiags) - if subDiags.HasErrors() { - log.Printf("[TRACE] vertex %q: dynamic subgraph encountered errors", dag.VertexName(v)) - return - } - log.Printf("[TRACE] vertex %q: dynamic subgraph completed successfully", dag.VertexName(v)) - } else { - log.Printf("[TRACE] vertex %q: produced no dynamic subgraph", dag.VertexName(v)) - } - } - - // If the node has a subgraph, then walk the subgraph - if sn, ok := v.(GraphNodeSubgraph); ok { - log.Printf("[TRACE] vertex %q: entering static subgraph", dag.VertexName(v)) - - g.DebugVertexInfo(v, fmt.Sprintf("subgraph: %T(%s)", v, path)) - - subDiags := sn.Subgraph().(*Graph).walk(walker) - if subDiags.HasErrors() { - log.Printf("[TRACE] vertex %q: static subgraph encountered errors", dag.VertexName(v)) - return - } - log.Printf("[TRACE] vertex %q: static subgraph completed successfully", dag.VertexName(v)) - } - - return - } - - return g.AcyclicGraph.Walk(walkFn) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder.go deleted file mode 100644 index ee2c5857..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder.go +++ /dev/null @@ -1,85 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - "strings" - - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// GraphBuilder is an interface that can be implemented and used with -// Terraform to build the graph that Terraform walks. -type GraphBuilder interface { - // Build builds the graph for the given module path. It is up to - // the interface implementation whether this build should expand - // the graph or not. - Build(addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) -} - -// BasicGraphBuilder is a GraphBuilder that builds a graph out of a -// series of transforms and (optionally) validates the graph is a valid -// structure. -type BasicGraphBuilder struct { - Steps []GraphTransformer - Validate bool - // Optional name to add to the graph debug log - Name string -} - -func (b *BasicGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - g := &Graph{Path: path} - - var lastStepStr string - for _, step := range b.Steps { - if step == nil { - continue - } - log.Printf("[TRACE] Executing graph transform %T", step) - - stepName := fmt.Sprintf("%T", step) - dot := strings.LastIndex(stepName, ".") - if dot >= 0 { - stepName = stepName[dot+1:] - } - - debugOp := g.DebugOperation(stepName, "") - err := step.Transform(g) - - errMsg := "" - if err != nil { - errMsg = err.Error() - } - debugOp.End(errMsg) - - if thisStepStr := g.StringWithNodeTypes(); thisStepStr != lastStepStr { - log.Printf("[TRACE] Completed graph transform %T with new graph:\n%s------", step, thisStepStr) - lastStepStr = thisStepStr - } else { - log.Printf("[TRACE] Completed graph transform %T (no changes)", step) - } - - if err != nil { - if nf, isNF := err.(tfdiags.NonFatalError); isNF { - diags = diags.Append(nf.Diagnostics) - } else { - diags = diags.Append(err) - return g, diags - } - } - } - - // Validate the graph structure - if b.Validate { - if err := g.Validate(); err != nil { - log.Printf("[ERROR] Graph validation failed. Graph:\n\n%s", g.String()) - diags = diags.Append(err) - return nil, diags - } - } - - return g, diags -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_apply.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_apply.go deleted file mode 100644 index 91898761..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_apply.go +++ /dev/null @@ -1,211 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// ApplyGraphBuilder implements GraphBuilder and is responsible for building -// a graph for applying a Terraform diff. -// -// Because the graph is built from the diff (vs. the config or state), -// this helps ensure that the apply-time graph doesn't modify any resources -// that aren't explicitly in the diff. There are other scenarios where the -// diff can be deviated, so this is just one layer of protection. -type ApplyGraphBuilder struct { - // Config is the configuration tree that the diff was built from. - Config *configs.Config - - // Changes describes the changes that we need apply. - Changes *plans.Changes - - // State is the current state - State *states.State - - // Components is a factory for the plug-in components (providers and - // provisioners) available for use. - Components contextComponentFactory - - // Schemas is the repository of schemas we will draw from to analyse - // the configuration. - Schemas *Schemas - - // Targets are resources to target. This is only required to make sure - // unnecessary outputs aren't included in the apply graph. The plan - // builder successfully handles targeting resources. In the future, - // outputs should go into the diff so that this is unnecessary. - Targets []addrs.Targetable - - // DisableReduce, if true, will not reduce the graph. Great for testing. - DisableReduce bool - - // Destroy, if true, represents a pure destroy operation - Destroy bool - - // Validate will do structural validation of the graph. - Validate bool -} - -// See GraphBuilder -func (b *ApplyGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) { - return (&BasicGraphBuilder{ - Steps: b.Steps(), - Validate: b.Validate, - Name: "ApplyGraphBuilder", - }).Build(path) -} - -// See GraphBuilder -func (b *ApplyGraphBuilder) Steps() []GraphTransformer { - // Custom factory for creating providers. - concreteProvider := func(a *NodeAbstractProvider) dag.Vertex { - return &NodeApplyableProvider{ - NodeAbstractProvider: a, - } - } - - concreteResource := func(a *NodeAbstractResource) dag.Vertex { - return &NodeApplyableResource{ - NodeAbstractResource: a, - } - } - - concreteOrphanResource := func(a *NodeAbstractResource) dag.Vertex { - return &NodeDestroyResource{ - NodeAbstractResource: a, - } - } - - concreteResourceInstance := func(a *NodeAbstractResourceInstance) dag.Vertex { - return &NodeApplyableResourceInstance{ - NodeAbstractResourceInstance: a, - } - } - - steps := []GraphTransformer{ - // Creates all the resources represented in the config. During apply, - // we use this just to ensure that the whole-resource metadata is - // updated to reflect things such as whether the count argument is - // set in config, or which provider configuration manages each resource. - &ConfigTransformer{ - Concrete: concreteResource, - Config: b.Config, - }, - - // Creates all the resource instances represented in the diff, along - // with dependency edges against the whole-resource nodes added by - // ConfigTransformer above. - &DiffTransformer{ - Concrete: concreteResourceInstance, - State: b.State, - Changes: b.Changes, - }, - - // Creates extra cleanup nodes for any entire resources that are - // no longer present in config, so we can make sure we clean up the - // leftover empty resource states after the instances have been - // destroyed. - // (We don't track this particular type of change in the plan because - // it's just cleanup of our own state object, and so doesn't effect - // any real remote objects or consumable outputs.) - &OrphanResourceTransformer{ - Concrete: concreteOrphanResource, - Config: b.Config, - State: b.State, - }, - - // Create orphan output nodes - &OrphanOutputTransformer{Config: b.Config, State: b.State}, - - // Attach the configuration to any resources - &AttachResourceConfigTransformer{Config: b.Config}, - - // Attach the state - &AttachStateTransformer{State: b.State}, - - // Provisioner-related transformations - &MissingProvisionerTransformer{Provisioners: b.Components.ResourceProvisioners()}, - &ProvisionerTransformer{}, - - // Add root variables - &RootVariableTransformer{Config: b.Config}, - - // Add the local values - &LocalTransformer{Config: b.Config}, - - // Add the outputs - &OutputTransformer{Config: b.Config}, - - // Add module variables - &ModuleVariableTransformer{Config: b.Config}, - - // add providers - TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config), - - // Remove modules no longer present in the config - &RemovedModuleTransformer{Config: b.Config, State: b.State}, - - // Must attach schemas before ReferenceTransformer so that we can - // analyze the configuration to find references. - &AttachSchemaTransformer{Schemas: b.Schemas}, - - // Connect references so ordering is correct - &ReferenceTransformer{}, - - // Destruction ordering - &DestroyEdgeTransformer{ - Config: b.Config, - State: b.State, - Schemas: b.Schemas, - }, - - &CBDEdgeTransformer{ - Config: b.Config, - State: b.State, - Schemas: b.Schemas, - Destroy: b.Destroy, - }, - - // Handle destroy time transformations for output and local values. - // Reverse the edges from outputs and locals, so that - // interpolations don't fail during destroy. - // Create a destroy node for outputs to remove them from the state. - // Prune unreferenced values, which may have interpolations that can't - // be resolved. - GraphTransformIf( - func() bool { return b.Destroy }, - GraphTransformMulti( - &DestroyValueReferenceTransformer{}, - &DestroyOutputTransformer{}, - &PruneUnusedValuesTransformer{}, - ), - ), - - // Add the node to fix the state count boundaries - &CountBoundaryTransformer{ - Config: b.Config, - }, - - // Target - &TargetsTransformer{Targets: b.Targets}, - - // Close opened plugin connections - &CloseProviderTransformer{}, - &CloseProvisionerTransformer{}, - - // Single root - &RootTransformer{}, - } - - if !b.DisableReduce { - // Perform the transitive reduction to make our graph a bit - // more sane if possible (it usually is possible). - steps = append(steps, &TransitiveReductionTransformer{}) - } - - return steps -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_destroy_plan.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_destroy_plan.go deleted file mode 100644 index 32fe5f97..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_destroy_plan.go +++ /dev/null @@ -1,97 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// DestroyPlanGraphBuilder implements GraphBuilder and is responsible for -// planning a pure-destroy. -// -// Planning a pure destroy operation is simple because we can ignore most -// ordering configuration and simply reverse the state. -type DestroyPlanGraphBuilder struct { - // Config is the configuration tree to build the plan from. - Config *configs.Config - - // State is the current state - State *states.State - - // Components is a factory for the plug-in components (providers and - // provisioners) available for use. - Components contextComponentFactory - - // Schemas is the repository of schemas we will draw from to analyse - // the configuration. - Schemas *Schemas - - // Targets are resources to target - Targets []addrs.Targetable - - // Validate will do structural validation of the graph. - Validate bool -} - -// See GraphBuilder -func (b *DestroyPlanGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) { - return (&BasicGraphBuilder{ - Steps: b.Steps(), - Validate: b.Validate, - Name: "DestroyPlanGraphBuilder", - }).Build(path) -} - -// See GraphBuilder -func (b *DestroyPlanGraphBuilder) Steps() []GraphTransformer { - concreteResourceInstance := func(a *NodeAbstractResourceInstance) dag.Vertex { - return &NodePlanDestroyableResourceInstance{ - NodeAbstractResourceInstance: a, - } - } - concreteResourceInstanceDeposed := func(a *NodeAbstractResourceInstance, key states.DeposedKey) dag.Vertex { - return &NodePlanDeposedResourceInstanceObject{ - NodeAbstractResourceInstance: a, - DeposedKey: key, - } - } - - concreteProvider := func(a *NodeAbstractProvider) dag.Vertex { - return &NodeApplyableProvider{ - NodeAbstractProvider: a, - } - } - - steps := []GraphTransformer{ - // Creates nodes for the resource instances tracked in the state. - &StateTransformer{ - ConcreteCurrent: concreteResourceInstance, - ConcreteDeposed: concreteResourceInstanceDeposed, - State: b.State, - }, - - // Attach the configuration to any resources - &AttachResourceConfigTransformer{Config: b.Config}, - - TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config), - - // Destruction ordering. We require this only so that - // targeting below will prune the correct things. - &DestroyEdgeTransformer{ - Config: b.Config, - State: b.State, - Schemas: b.Schemas, - }, - - // Target. Note we don't set "Destroy: true" here since we already - // created proper destroy ordering. - &TargetsTransformer{Targets: b.Targets}, - - // Single root - &RootTransformer{}, - } - - return steps -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_eval.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_eval.go deleted file mode 100644 index 8a0bcf5b..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_eval.go +++ /dev/null @@ -1,108 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// EvalGraphBuilder implements GraphBuilder and constructs a graph suitable -// for evaluating in-memory values (input variables, local values, output -// values) in the state without any other side-effects. -// -// This graph is used only in weird cases, such as the "terraform console" -// CLI command, where we need to evaluate expressions against the state -// without taking any other actions. -// -// The generated graph will include nodes for providers, resources, etc -// just to allow indirect dependencies to be resolved, but these nodes will -// not take any actions themselves since we assume that their parts of the -// state, if any, are already complete. -// -// Although the providers are never configured, they must still be available -// in order to obtain schema information used for type checking, etc. -type EvalGraphBuilder struct { - // Config is the configuration tree. - Config *configs.Config - - // State is the current state - State *states.State - - // Components is a factory for the plug-in components (providers and - // provisioners) available for use. - Components contextComponentFactory - - // Schemas is the repository of schemas we will draw from to analyse - // the configuration. - Schemas *Schemas -} - -// See GraphBuilder -func (b *EvalGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) { - return (&BasicGraphBuilder{ - Steps: b.Steps(), - Validate: true, - Name: "EvalGraphBuilder", - }).Build(path) -} - -// See GraphBuilder -func (b *EvalGraphBuilder) Steps() []GraphTransformer { - concreteProvider := func(a *NodeAbstractProvider) dag.Vertex { - return &NodeEvalableProvider{ - NodeAbstractProvider: a, - } - } - - steps := []GraphTransformer{ - // Creates all the data resources that aren't in the state. This will also - // add any orphans from scaling in as destroy nodes. - &ConfigTransformer{ - Concrete: nil, // just use the abstract type - Config: b.Config, - Unique: true, - }, - - // Attach the state - &AttachStateTransformer{State: b.State}, - - // Attach the configuration to any resources - &AttachResourceConfigTransformer{Config: b.Config}, - - // Add root variables - &RootVariableTransformer{Config: b.Config}, - - // Add the local values - &LocalTransformer{Config: b.Config}, - - // Add the outputs - &OutputTransformer{Config: b.Config}, - - // Add module variables - &ModuleVariableTransformer{Config: b.Config}, - - TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config), - - // Must attach schemas before ReferenceTransformer so that we can - // analyze the configuration to find references. - &AttachSchemaTransformer{Schemas: b.Schemas}, - - // Connect so that the references are ready for targeting. We'll - // have to connect again later for providers and so on. - &ReferenceTransformer{}, - - // Although we don't configure providers, we do still start them up - // to get their schemas, and so we must shut them down again here. - &CloseProviderTransformer{}, - - // Single root - &RootTransformer{}, - - // Remove redundant edges to simplify the graph. - &TransitiveReductionTransformer{}, - } - - return steps -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_import.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_import.go deleted file mode 100644 index dcbb10e6..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_import.go +++ /dev/null @@ -1,100 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// ImportGraphBuilder implements GraphBuilder and is responsible for building -// a graph for importing resources into Terraform. This is a much, much -// simpler graph than a normal configuration graph. -type ImportGraphBuilder struct { - // ImportTargets are the list of resources to import. - ImportTargets []*ImportTarget - - // Module is a configuration to build the graph from. See ImportOpts.Config. - Config *configs.Config - - // Components is the factory for our available plugin components. - Components contextComponentFactory - - // Schemas is the repository of schemas we will draw from to analyse - // the configuration. - Schemas *Schemas -} - -// Build builds the graph according to the steps returned by Steps. -func (b *ImportGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) { - return (&BasicGraphBuilder{ - Steps: b.Steps(), - Validate: true, - Name: "ImportGraphBuilder", - }).Build(path) -} - -// Steps returns the ordered list of GraphTransformers that must be executed -// to build a complete graph. -func (b *ImportGraphBuilder) Steps() []GraphTransformer { - // Get the module. If we don't have one, we just use an empty tree - // so that the transform still works but does nothing. - config := b.Config - if config == nil { - config = configs.NewEmptyConfig() - } - - // Custom factory for creating providers. - concreteProvider := func(a *NodeAbstractProvider) dag.Vertex { - return &NodeApplyableProvider{ - NodeAbstractProvider: a, - } - } - - steps := []GraphTransformer{ - // Create all our resources from the configuration and state - &ConfigTransformer{Config: config}, - - // Attach the configuration to any resources - &AttachResourceConfigTransformer{Config: b.Config}, - - // Add the import steps - &ImportStateTransformer{Targets: b.ImportTargets}, - - // Add root variables - &RootVariableTransformer{Config: b.Config}, - - TransformProviders(b.Components.ResourceProviders(), concreteProvider, config), - - // This validates that the providers only depend on variables - &ImportProviderValidateTransformer{}, - - // Add the local values - &LocalTransformer{Config: b.Config}, - - // Add the outputs - &OutputTransformer{Config: b.Config}, - - // Add module variables - &ModuleVariableTransformer{Config: b.Config}, - - // Must attach schemas before ReferenceTransformer so that we can - // analyze the configuration to find references. - &AttachSchemaTransformer{Schemas: b.Schemas}, - - // Connect so that the references are ready for targeting. We'll - // have to connect again later for providers and so on. - &ReferenceTransformer{}, - - // Close opened plugin connections - &CloseProviderTransformer{}, - - // Single root - &RootTransformer{}, - - // Optimize - &TransitiveReductionTransformer{}, - } - - return steps -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_plan.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_plan.go deleted file mode 100644 index bcd119b3..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_plan.go +++ /dev/null @@ -1,204 +0,0 @@ -package terraform - -import ( - "sync" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// PlanGraphBuilder implements GraphBuilder and is responsible for building -// a graph for planning (creating a Terraform Diff). -// -// The primary difference between this graph and others: -// -// * Based on the config since it represents the target state -// -// * Ignores lifecycle options since no lifecycle events occur here. This -// simplifies the graph significantly since complex transforms such as -// create-before-destroy can be completely ignored. -// -type PlanGraphBuilder struct { - // Config is the configuration tree to build a plan from. - Config *configs.Config - - // State is the current state - State *states.State - - // Components is a factory for the plug-in components (providers and - // provisioners) available for use. - Components contextComponentFactory - - // Schemas is the repository of schemas we will draw from to analyse - // the configuration. - Schemas *Schemas - - // Targets are resources to target - Targets []addrs.Targetable - - // DisableReduce, if true, will not reduce the graph. Great for testing. - DisableReduce bool - - // Validate will do structural validation of the graph. - Validate bool - - // CustomConcrete can be set to customize the node types created - // for various parts of the plan. This is useful in order to customize - // the plan behavior. - CustomConcrete bool - ConcreteProvider ConcreteProviderNodeFunc - ConcreteResource ConcreteResourceNodeFunc - ConcreteResourceOrphan ConcreteResourceInstanceNodeFunc - - once sync.Once -} - -// See GraphBuilder -func (b *PlanGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) { - return (&BasicGraphBuilder{ - Steps: b.Steps(), - Validate: b.Validate, - Name: "PlanGraphBuilder", - }).Build(path) -} - -// See GraphBuilder -func (b *PlanGraphBuilder) Steps() []GraphTransformer { - b.once.Do(b.init) - - concreteResourceInstanceDeposed := func(a *NodeAbstractResourceInstance, key states.DeposedKey) dag.Vertex { - return &NodePlanDeposedResourceInstanceObject{ - NodeAbstractResourceInstance: a, - DeposedKey: key, - } - } - - steps := []GraphTransformer{ - // Creates all the resources represented in the config - &ConfigTransformer{ - Concrete: b.ConcreteResource, - Config: b.Config, - }, - - // Add the local values - &LocalTransformer{Config: b.Config}, - - // Add the outputs - &OutputTransformer{Config: b.Config}, - - // Add orphan resources - &OrphanResourceInstanceTransformer{ - Concrete: b.ConcreteResourceOrphan, - State: b.State, - Config: b.Config, - }, - - // We also need nodes for any deposed instance objects present in the - // state, so we can plan to destroy them. (This intentionally - // skips creating nodes for _current_ objects, since ConfigTransformer - // created nodes that will do that during DynamicExpand.) - &StateTransformer{ - ConcreteDeposed: concreteResourceInstanceDeposed, - State: b.State, - }, - - // Create orphan output nodes - &OrphanOutputTransformer{ - Config: b.Config, - State: b.State, - }, - - // Attach the configuration to any resources - &AttachResourceConfigTransformer{Config: b.Config}, - - // Attach the state - &AttachStateTransformer{State: b.State}, - - // Add root variables - &RootVariableTransformer{Config: b.Config}, - - &MissingProvisionerTransformer{Provisioners: b.Components.ResourceProvisioners()}, - &ProvisionerTransformer{}, - - // Add module variables - &ModuleVariableTransformer{ - Config: b.Config, - }, - - TransformProviders(b.Components.ResourceProviders(), b.ConcreteProvider, b.Config), - - // Remove modules no longer present in the config - &RemovedModuleTransformer{Config: b.Config, State: b.State}, - - // Must attach schemas before ReferenceTransformer so that we can - // analyze the configuration to find references. - &AttachSchemaTransformer{Schemas: b.Schemas}, - - // Connect so that the references are ready for targeting. We'll - // have to connect again later for providers and so on. - &ReferenceTransformer{}, - - // Add the node to fix the state count boundaries - &CountBoundaryTransformer{ - Config: b.Config, - }, - - // Target - &TargetsTransformer{ - Targets: b.Targets, - - // Resource nodes from config have not yet been expanded for - // "count", so we must apply targeting without indices. Exact - // targeting will be dealt with later when these resources - // DynamicExpand. - IgnoreIndices: true, - }, - - // Detect when create_before_destroy must be forced on for a particular - // node due to dependency edges, to avoid graph cycles during apply. - &ForcedCBDTransformer{}, - - // Close opened plugin connections - &CloseProviderTransformer{}, - &CloseProvisionerTransformer{}, - - // Single root - &RootTransformer{}, - } - - if !b.DisableReduce { - // Perform the transitive reduction to make our graph a bit - // more sane if possible (it usually is possible). - steps = append(steps, &TransitiveReductionTransformer{}) - } - - return steps -} - -func (b *PlanGraphBuilder) init() { - // Do nothing if the user requests customizing the fields - if b.CustomConcrete { - return - } - - b.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex { - return &NodeApplyableProvider{ - NodeAbstractProvider: a, - } - } - - b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex { - return &NodePlannableResource{ - NodeAbstractResource: a, - } - } - - b.ConcreteResourceOrphan = func(a *NodeAbstractResourceInstance) dag.Vertex { - return &NodePlannableResourceInstanceOrphan{ - NodeAbstractResourceInstance: a, - } - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_refresh.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_refresh.go deleted file mode 100644 index fad7bf16..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_refresh.go +++ /dev/null @@ -1,194 +0,0 @@ -package terraform - -import ( - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" -) - -// RefreshGraphBuilder implements GraphBuilder and is responsible for building -// a graph for refreshing (updating the Terraform state). -// -// The primary difference between this graph and others: -// -// * Based on the state since it represents the only resources that -// need to be refreshed. -// -// * Ignores lifecycle options since no lifecycle events occur here. This -// simplifies the graph significantly since complex transforms such as -// create-before-destroy can be completely ignored. -// -type RefreshGraphBuilder struct { - // Config is the configuration tree. - Config *configs.Config - - // State is the prior state - State *states.State - - // Components is a factory for the plug-in components (providers and - // provisioners) available for use. - Components contextComponentFactory - - // Schemas is the repository of schemas we will draw from to analyse - // the configuration. - Schemas *Schemas - - // Targets are resources to target - Targets []addrs.Targetable - - // DisableReduce, if true, will not reduce the graph. Great for testing. - DisableReduce bool - - // Validate will do structural validation of the graph. - Validate bool -} - -// See GraphBuilder -func (b *RefreshGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) { - return (&BasicGraphBuilder{ - Steps: b.Steps(), - Validate: b.Validate, - Name: "RefreshGraphBuilder", - }).Build(path) -} - -// See GraphBuilder -func (b *RefreshGraphBuilder) Steps() []GraphTransformer { - // Custom factory for creating providers. - concreteProvider := func(a *NodeAbstractProvider) dag.Vertex { - return &NodeApplyableProvider{ - NodeAbstractProvider: a, - } - } - - concreteManagedResource := func(a *NodeAbstractResource) dag.Vertex { - return &NodeRefreshableManagedResource{ - NodeAbstractResource: a, - } - } - - concreteManagedResourceInstance := func(a *NodeAbstractResourceInstance) dag.Vertex { - return &NodeRefreshableManagedResourceInstance{ - NodeAbstractResourceInstance: a, - } - } - - concreteResourceInstanceDeposed := func(a *NodeAbstractResourceInstance, key states.DeposedKey) dag.Vertex { - // The "Plan" node type also handles refreshing behavior. - return &NodePlanDeposedResourceInstanceObject{ - NodeAbstractResourceInstance: a, - DeposedKey: key, - } - } - - concreteDataResource := func(a *NodeAbstractResource) dag.Vertex { - return &NodeRefreshableDataResource{ - NodeAbstractResource: a, - } - } - - steps := []GraphTransformer{ - // Creates all the managed resources that aren't in the state, but only if - // we have a state already. No resources in state means there's not - // anything to refresh. - func() GraphTransformer { - if b.State.HasResources() { - return &ConfigTransformer{ - Concrete: concreteManagedResource, - Config: b.Config, - Unique: true, - ModeFilter: true, - Mode: addrs.ManagedResourceMode, - } - } - log.Println("[TRACE] No managed resources in state during refresh; skipping managed resource transformer") - return nil - }(), - - // Creates all the data resources that aren't in the state. This will also - // add any orphans from scaling in as destroy nodes. - &ConfigTransformer{ - Concrete: concreteDataResource, - Config: b.Config, - Unique: true, - ModeFilter: true, - Mode: addrs.DataResourceMode, - }, - - // Add any fully-orphaned resources from config (ones that have been - // removed completely, not ones that are just orphaned due to a scaled-in - // count. - &OrphanResourceInstanceTransformer{ - Concrete: concreteManagedResourceInstance, - State: b.State, - Config: b.Config, - }, - - // We also need nodes for any deposed instance objects present in the - // state, so we can check if they still exist. (This intentionally - // skips creating nodes for _current_ objects, since ConfigTransformer - // created nodes that will do that during DynamicExpand.) - &StateTransformer{ - ConcreteDeposed: concreteResourceInstanceDeposed, - State: b.State, - }, - - // Attach the state - &AttachStateTransformer{State: b.State}, - - // Attach the configuration to any resources - &AttachResourceConfigTransformer{Config: b.Config}, - - // Add root variables - &RootVariableTransformer{Config: b.Config}, - - // Add the local values - &LocalTransformer{Config: b.Config}, - - // Add the outputs - &OutputTransformer{Config: b.Config}, - - // Add module variables - &ModuleVariableTransformer{Config: b.Config}, - - TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config), - - // Must attach schemas before ReferenceTransformer so that we can - // analyze the configuration to find references. - &AttachSchemaTransformer{Schemas: b.Schemas}, - - // Connect so that the references are ready for targeting. We'll - // have to connect again later for providers and so on. - &ReferenceTransformer{}, - - // Target - &TargetsTransformer{ - Targets: b.Targets, - - // Resource nodes from config have not yet been expanded for - // "count", so we must apply targeting without indices. Exact - // targeting will be dealt with later when these resources - // DynamicExpand. - IgnoreIndices: true, - }, - - // Close opened plugin connections - &CloseProviderTransformer{}, - - // Single root - &RootTransformer{}, - } - - if !b.DisableReduce { - // Perform the transitive reduction to make our graph a bit - // more sane if possible (it usually is possible). - steps = append(steps, &TransitiveReductionTransformer{}) - } - - return steps -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_validate.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_validate.go deleted file mode 100644 index 0aa8b915..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_builder_validate.go +++ /dev/null @@ -1,34 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" -) - -// ValidateGraphBuilder creates the graph for the validate operation. -// -// ValidateGraphBuilder is based on the PlanGraphBuilder. We do this so that -// we only have to validate what we'd normally plan anyways. The -// PlanGraphBuilder given will be modified so it shouldn't be used for anything -// else after calling this function. -func ValidateGraphBuilder(p *PlanGraphBuilder) GraphBuilder { - // We're going to customize the concrete functions - p.CustomConcrete = true - - // Set the provider to the normal provider. This will ask for input. - p.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex { - return &NodeApplyableProvider{ - NodeAbstractProvider: a, - } - } - - p.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex { - return &NodeValidatableResource{ - NodeAbstractResource: a, - } - } - - // We purposely don't set any other concrete types since they don't - // require validation. - - return p -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_dot.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_dot.go deleted file mode 100644 index 5dbf415f..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_dot.go +++ /dev/null @@ -1,9 +0,0 @@ -package terraform - -import "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - -// GraphDot returns the dot formatting of a visual representation of -// the given Terraform graph. -func GraphDot(g *Graph, opts *dag.DotOpts) (string, error) { - return string(g.Dot(opts)), nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_interface_subgraph.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_interface_subgraph.go deleted file mode 100644 index a005ea5a..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_interface_subgraph.go +++ /dev/null @@ -1,11 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// GraphNodeSubPath says that a node is part of a graph with a -// different path, and the context should be adjusted accordingly. -type GraphNodeSubPath interface { - Path() addrs.ModuleInstance -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_walk.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_walk.go deleted file mode 100644 index d699376f..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_walk.go +++ /dev/null @@ -1,32 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// GraphWalker is an interface that can be implemented that when used -// with Graph.Walk will invoke the given callbacks under certain events. -type GraphWalker interface { - EnterPath(addrs.ModuleInstance) EvalContext - ExitPath(addrs.ModuleInstance) - EnterVertex(dag.Vertex) - ExitVertex(dag.Vertex, tfdiags.Diagnostics) - EnterEvalTree(dag.Vertex, EvalNode) EvalNode - ExitEvalTree(dag.Vertex, interface{}, error) tfdiags.Diagnostics -} - -// NullGraphWalker is a GraphWalker implementation that does nothing. -// This can be embedded within other GraphWalker implementations for easily -// implementing all the required functions. -type NullGraphWalker struct{} - -func (NullGraphWalker) EnterPath(addrs.ModuleInstance) EvalContext { return new(MockEvalContext) } -func (NullGraphWalker) ExitPath(addrs.ModuleInstance) {} -func (NullGraphWalker) EnterVertex(dag.Vertex) {} -func (NullGraphWalker) ExitVertex(dag.Vertex, tfdiags.Diagnostics) {} -func (NullGraphWalker) EnterEvalTree(v dag.Vertex, n EvalNode) EvalNode { return n } -func (NullGraphWalker) ExitEvalTree(dag.Vertex, interface{}, error) tfdiags.Diagnostics { - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_walk_context.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_walk_context.go deleted file mode 100644 index 11fb2fd0..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_walk_context.go +++ /dev/null @@ -1,157 +0,0 @@ -package terraform - -import ( - "context" - "log" - "sync" - - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/provisioners" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// ContextGraphWalker is the GraphWalker implementation used with the -// Context struct to walk and evaluate the graph. -type ContextGraphWalker struct { - NullGraphWalker - - // Configurable values - Context *Context - State *states.SyncState // Used for safe concurrent access to state - Changes *plans.ChangesSync // Used for safe concurrent writes to changes - Operation walkOperation - StopContext context.Context - RootVariableValues InputValues - - // This is an output. Do not set this, nor read it while a graph walk - // is in progress. - NonFatalDiagnostics tfdiags.Diagnostics - - errorLock sync.Mutex - once sync.Once - contexts map[string]*BuiltinEvalContext - contextLock sync.Mutex - variableValues map[string]map[string]cty.Value - variableValuesLock sync.Mutex - providerCache map[string]providers.Interface - providerSchemas map[string]*ProviderSchema - providerLock sync.Mutex - provisionerCache map[string]provisioners.Interface - provisionerSchemas map[string]*configschema.Block - provisionerLock sync.Mutex -} - -func (w *ContextGraphWalker) EnterPath(path addrs.ModuleInstance) EvalContext { - w.once.Do(w.init) - - w.contextLock.Lock() - defer w.contextLock.Unlock() - - // If we already have a context for this path cached, use that - key := path.String() - if ctx, ok := w.contexts[key]; ok { - return ctx - } - - // Our evaluator shares some locks with the main context and the walker - // so that we can safely run multiple evaluations at once across - // different modules. - evaluator := &Evaluator{ - Meta: w.Context.meta, - Config: w.Context.config, - Operation: w.Operation, - State: w.State, - Changes: w.Changes, - Schemas: w.Context.schemas, - VariableValues: w.variableValues, - VariableValuesLock: &w.variableValuesLock, - } - - ctx := &BuiltinEvalContext{ - StopContext: w.StopContext, - PathValue: path, - Hooks: w.Context.hooks, - InputValue: w.Context.uiInput, - Components: w.Context.components, - Schemas: w.Context.schemas, - ProviderCache: w.providerCache, - ProviderInputConfig: w.Context.providerInputConfig, - ProviderLock: &w.providerLock, - ProvisionerCache: w.provisionerCache, - ProvisionerLock: &w.provisionerLock, - ChangesValue: w.Changes, - StateValue: w.State, - Evaluator: evaluator, - VariableValues: w.variableValues, - VariableValuesLock: &w.variableValuesLock, - } - - w.contexts[key] = ctx - return ctx -} - -func (w *ContextGraphWalker) EnterEvalTree(v dag.Vertex, n EvalNode) EvalNode { - log.Printf("[TRACE] [%s] Entering eval tree: %s", w.Operation, dag.VertexName(v)) - - // Acquire a lock on the semaphore - w.Context.parallelSem.Acquire() - - // We want to filter the evaluation tree to only include operations - // that belong in this operation. - return EvalFilter(n, EvalNodeFilterOp(w.Operation)) -} - -func (w *ContextGraphWalker) ExitEvalTree(v dag.Vertex, output interface{}, err error) tfdiags.Diagnostics { - log.Printf("[TRACE] [%s] Exiting eval tree: %s", w.Operation, dag.VertexName(v)) - - // Release the semaphore - w.Context.parallelSem.Release() - - if err == nil { - return nil - } - - // Acquire the lock because anything is going to require a lock. - w.errorLock.Lock() - defer w.errorLock.Unlock() - - // If the error is non-fatal then we'll accumulate its diagnostics in our - // non-fatal list, rather than returning it directly, so that the graph - // walk can continue. - if nferr, ok := err.(tfdiags.NonFatalError); ok { - log.Printf("[WARN] %s: %s", dag.VertexName(v), nferr) - w.NonFatalDiagnostics = w.NonFatalDiagnostics.Append(nferr.Diagnostics) - return nil - } - - // Otherwise, we'll let our usual diagnostics machinery figure out how to - // unpack this as one or more diagnostic messages and return that. If we - // get down here then the returned diagnostics will contain at least one - // error, causing the graph walk to halt. - var diags tfdiags.Diagnostics - diags = diags.Append(err) - return diags -} - -func (w *ContextGraphWalker) init() { - w.contexts = make(map[string]*BuiltinEvalContext) - w.providerCache = make(map[string]providers.Interface) - w.providerSchemas = make(map[string]*ProviderSchema) - w.provisionerCache = make(map[string]provisioners.Interface) - w.provisionerSchemas = make(map[string]*configschema.Block) - w.variableValues = make(map[string]map[string]cty.Value) - - // Populate root module variable values. Other modules will be populated - // during the graph walk. - w.variableValues[""] = make(map[string]cty.Value) - for k, iv := range w.RootVariableValues { - w.variableValues[""][k] = iv.Value - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_walk_operation.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_walk_operation.go deleted file mode 100644 index 859f6fb1..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graph_walk_operation.go +++ /dev/null @@ -1,18 +0,0 @@ -package terraform - -//go:generate go run golang.org/x/tools/cmd/stringer -type=walkOperation graph_walk_operation.go - -// walkOperation is an enum which tells the walkContext what to do. -type walkOperation byte - -const ( - walkInvalid walkOperation = iota - walkApply - walkPlan - walkPlanDestroy - walkRefresh - walkValidate - walkDestroy - walkImport - walkEval // used just to prepare EvalContext for expression evaluation, with no other actions -) diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graphtype_string.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graphtype_string.go deleted file mode 100644 index b51e1a26..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/graphtype_string.go +++ /dev/null @@ -1,30 +0,0 @@ -// Code generated by "stringer -type=GraphType context_graph_type.go"; DO NOT EDIT. - -package terraform - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[GraphTypeInvalid-0] - _ = x[GraphTypeLegacy-1] - _ = x[GraphTypeRefresh-2] - _ = x[GraphTypePlan-3] - _ = x[GraphTypePlanDestroy-4] - _ = x[GraphTypeApply-5] - _ = x[GraphTypeValidate-6] - _ = x[GraphTypeEval-7] -} - -const _GraphType_name = "GraphTypeInvalidGraphTypeLegacyGraphTypeRefreshGraphTypePlanGraphTypePlanDestroyGraphTypeApplyGraphTypeValidateGraphTypeEval" - -var _GraphType_index = [...]uint8{0, 16, 31, 47, 60, 80, 94, 111, 124} - -func (i GraphType) String() string { - if i >= GraphType(len(_GraphType_index)-1) { - return "GraphType(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _GraphType_name[_GraphType_index[i]:_GraphType_index[i+1]] -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/hook.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/hook.go deleted file mode 100644 index b5be9482..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/hook.go +++ /dev/null @@ -1,145 +0,0 @@ -package terraform - -import ( - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// HookAction is an enum of actions that can be taken as a result of a hook -// callback. This allows you to modify the behavior of Terraform at runtime. -type HookAction byte - -const ( - // HookActionContinue continues with processing as usual. - HookActionContinue HookAction = iota - - // HookActionHalt halts immediately: no more hooks are processed - // and the action that Terraform was about to take is cancelled. - HookActionHalt -) - -// Hook is the interface that must be implemented to hook into various -// parts of Terraform, allowing you to inspect or change behavior at runtime. -// -// There are MANY hook points into Terraform. If you only want to implement -// some hook points, but not all (which is the likely case), then embed the -// NilHook into your struct, which implements all of the interface but does -// nothing. Then, override only the functions you want to implement. -type Hook interface { - // PreApply and PostApply are called before and after an action for a - // single instance is applied. The error argument in PostApply is the - // error, if any, that was returned from the provider Apply call itself. - PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (HookAction, error) - PostApply(addr addrs.AbsResourceInstance, gen states.Generation, newState cty.Value, err error) (HookAction, error) - - // PreDiff and PostDiff are called before and after a provider is given - // the opportunity to customize the proposed new state to produce the - // planned new state. - PreDiff(addr addrs.AbsResourceInstance, gen states.Generation, priorState, proposedNewState cty.Value) (HookAction, error) - PostDiff(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (HookAction, error) - - // The provisioning hooks signal both the overall start end end of - // provisioning for a particular instance and of each of the individual - // configured provisioners for each instance. The sequence of these - // for a given instance might look something like this: - // - // PreProvisionInstance(aws_instance.foo[1], ...) - // PreProvisionInstanceStep(aws_instance.foo[1], "file") - // PostProvisionInstanceStep(aws_instance.foo[1], "file", nil) - // PreProvisionInstanceStep(aws_instance.foo[1], "remote-exec") - // ProvisionOutput(aws_instance.foo[1], "remote-exec", "Installing foo...") - // ProvisionOutput(aws_instance.foo[1], "remote-exec", "Configuring bar...") - // PostProvisionInstanceStep(aws_instance.foo[1], "remote-exec", nil) - // PostProvisionInstance(aws_instance.foo[1], ...) - // - // ProvisionOutput is called with output sent back by the provisioners. - // This will be called multiple times as output comes in, with each call - // representing one line of output. It cannot control whether the - // provisioner continues running. - PreProvisionInstance(addr addrs.AbsResourceInstance, state cty.Value) (HookAction, error) - PostProvisionInstance(addr addrs.AbsResourceInstance, state cty.Value) (HookAction, error) - PreProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string) (HookAction, error) - PostProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string, err error) (HookAction, error) - ProvisionOutput(addr addrs.AbsResourceInstance, typeName string, line string) - - // PreRefresh and PostRefresh are called before and after a single - // resource state is refreshed, respectively. - PreRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value) (HookAction, error) - PostRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value, newState cty.Value) (HookAction, error) - - // PreImportState and PostImportState are called before and after - // (respectively) each state import operation for a given resource address. - PreImportState(addr addrs.AbsResourceInstance, importID string) (HookAction, error) - PostImportState(addr addrs.AbsResourceInstance, imported []providers.ImportedResource) (HookAction, error) - - // PostStateUpdate is called each time the state is updated. It receives - // a deep copy of the state, which it may therefore access freely without - // any need for locks to protect from concurrent writes from the caller. - PostStateUpdate(new *states.State) (HookAction, error) -} - -// NilHook is a Hook implementation that does nothing. It exists only to -// simplify implementing hooks. You can embed this into your Hook implementation -// and only implement the functions you are interested in. -type NilHook struct{} - -var _ Hook = (*NilHook)(nil) - -func (*NilHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (HookAction, error) { - return HookActionContinue, nil -} - -func (*NilHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generation, newState cty.Value, err error) (HookAction, error) { - return HookActionContinue, nil -} - -func (*NilHook) PreDiff(addr addrs.AbsResourceInstance, gen states.Generation, priorState, proposedNewState cty.Value) (HookAction, error) { - return HookActionContinue, nil -} - -func (*NilHook) PostDiff(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (HookAction, error) { - return HookActionContinue, nil -} - -func (*NilHook) PreProvisionInstance(addr addrs.AbsResourceInstance, state cty.Value) (HookAction, error) { - return HookActionContinue, nil -} - -func (*NilHook) PostProvisionInstance(addr addrs.AbsResourceInstance, state cty.Value) (HookAction, error) { - return HookActionContinue, nil -} - -func (*NilHook) PreProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string) (HookAction, error) { - return HookActionContinue, nil -} - -func (*NilHook) PostProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string, err error) (HookAction, error) { - return HookActionContinue, nil -} - -func (*NilHook) ProvisionOutput(addr addrs.AbsResourceInstance, typeName string, line string) { -} - -func (*NilHook) PreRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value) (HookAction, error) { - return HookActionContinue, nil -} - -func (*NilHook) PostRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value, newState cty.Value) (HookAction, error) { - return HookActionContinue, nil -} - -func (*NilHook) PreImportState(addr addrs.AbsResourceInstance, importID string) (HookAction, error) { - return HookActionContinue, nil -} - -func (*NilHook) PostImportState(addr addrs.AbsResourceInstance, imported []providers.ImportedResource) (HookAction, error) { - return HookActionContinue, nil -} - -func (*NilHook) PostStateUpdate(new *states.State) (HookAction, error) { - return HookActionContinue, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/hook_mock.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/hook_mock.go deleted file mode 100644 index 74a29bde..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/hook_mock.go +++ /dev/null @@ -1,274 +0,0 @@ -package terraform - -import ( - "sync" - - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// MockHook is an implementation of Hook that can be used for tests. -// It records all of its function calls. -type MockHook struct { - sync.Mutex - - PreApplyCalled bool - PreApplyAddr addrs.AbsResourceInstance - PreApplyGen states.Generation - PreApplyAction plans.Action - PreApplyPriorState cty.Value - PreApplyPlannedState cty.Value - PreApplyReturn HookAction - PreApplyError error - - PostApplyCalled bool - PostApplyAddr addrs.AbsResourceInstance - PostApplyGen states.Generation - PostApplyNewState cty.Value - PostApplyError error - PostApplyReturn HookAction - PostApplyReturnError error - PostApplyFn func(addrs.AbsResourceInstance, states.Generation, cty.Value, error) (HookAction, error) - - PreDiffCalled bool - PreDiffAddr addrs.AbsResourceInstance - PreDiffGen states.Generation - PreDiffPriorState cty.Value - PreDiffProposedState cty.Value - PreDiffReturn HookAction - PreDiffError error - - PostDiffCalled bool - PostDiffAddr addrs.AbsResourceInstance - PostDiffGen states.Generation - PostDiffAction plans.Action - PostDiffPriorState cty.Value - PostDiffPlannedState cty.Value - PostDiffReturn HookAction - PostDiffError error - - PreProvisionInstanceCalled bool - PreProvisionInstanceAddr addrs.AbsResourceInstance - PreProvisionInstanceState cty.Value - PreProvisionInstanceReturn HookAction - PreProvisionInstanceError error - - PostProvisionInstanceCalled bool - PostProvisionInstanceAddr addrs.AbsResourceInstance - PostProvisionInstanceState cty.Value - PostProvisionInstanceReturn HookAction - PostProvisionInstanceError error - - PreProvisionInstanceStepCalled bool - PreProvisionInstanceStepAddr addrs.AbsResourceInstance - PreProvisionInstanceStepProvisionerType string - PreProvisionInstanceStepReturn HookAction - PreProvisionInstanceStepError error - - PostProvisionInstanceStepCalled bool - PostProvisionInstanceStepAddr addrs.AbsResourceInstance - PostProvisionInstanceStepProvisionerType string - PostProvisionInstanceStepErrorArg error - PostProvisionInstanceStepReturn HookAction - PostProvisionInstanceStepError error - - ProvisionOutputCalled bool - ProvisionOutputAddr addrs.AbsResourceInstance - ProvisionOutputProvisionerType string - ProvisionOutputMessage string - - PreRefreshCalled bool - PreRefreshAddr addrs.AbsResourceInstance - PreRefreshGen states.Generation - PreRefreshPriorState cty.Value - PreRefreshReturn HookAction - PreRefreshError error - - PostRefreshCalled bool - PostRefreshAddr addrs.AbsResourceInstance - PostRefreshGen states.Generation - PostRefreshPriorState cty.Value - PostRefreshNewState cty.Value - PostRefreshReturn HookAction - PostRefreshError error - - PreImportStateCalled bool - PreImportStateAddr addrs.AbsResourceInstance - PreImportStateID string - PreImportStateReturn HookAction - PreImportStateError error - - PostImportStateCalled bool - PostImportStateAddr addrs.AbsResourceInstance - PostImportStateNewStates []providers.ImportedResource - PostImportStateReturn HookAction - PostImportStateError error - - PostStateUpdateCalled bool - PostStateUpdateState *states.State - PostStateUpdateReturn HookAction - PostStateUpdateError error -} - -var _ Hook = (*MockHook)(nil) - -func (h *MockHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (HookAction, error) { - h.Lock() - defer h.Unlock() - - h.PreApplyCalled = true - h.PreApplyAddr = addr - h.PreApplyGen = gen - h.PreApplyAction = action - h.PreApplyPriorState = priorState - h.PreApplyPlannedState = plannedNewState - return h.PreApplyReturn, h.PreApplyError -} - -func (h *MockHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generation, newState cty.Value, err error) (HookAction, error) { - h.Lock() - defer h.Unlock() - - h.PostApplyCalled = true - h.PostApplyAddr = addr - h.PostApplyGen = gen - h.PostApplyNewState = newState - h.PostApplyError = err - - if h.PostApplyFn != nil { - return h.PostApplyFn(addr, gen, newState, err) - } - - return h.PostApplyReturn, h.PostApplyReturnError -} - -func (h *MockHook) PreDiff(addr addrs.AbsResourceInstance, gen states.Generation, priorState, proposedNewState cty.Value) (HookAction, error) { - h.Lock() - defer h.Unlock() - - h.PreDiffCalled = true - h.PreDiffAddr = addr - h.PreDiffGen = gen - h.PreDiffPriorState = priorState - h.PreDiffProposedState = proposedNewState - return h.PreDiffReturn, h.PreDiffError -} - -func (h *MockHook) PostDiff(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (HookAction, error) { - h.Lock() - defer h.Unlock() - - h.PostDiffCalled = true - h.PostDiffAddr = addr - h.PostDiffGen = gen - h.PostDiffAction = action - h.PostDiffPriorState = priorState - h.PostDiffPlannedState = plannedNewState - return h.PostDiffReturn, h.PostDiffError -} - -func (h *MockHook) PreProvisionInstance(addr addrs.AbsResourceInstance, state cty.Value) (HookAction, error) { - h.Lock() - defer h.Unlock() - - h.PreProvisionInstanceCalled = true - h.PreProvisionInstanceAddr = addr - h.PreProvisionInstanceState = state - return h.PreProvisionInstanceReturn, h.PreProvisionInstanceError -} - -func (h *MockHook) PostProvisionInstance(addr addrs.AbsResourceInstance, state cty.Value) (HookAction, error) { - h.Lock() - defer h.Unlock() - - h.PostProvisionInstanceCalled = true - h.PostProvisionInstanceAddr = addr - h.PostProvisionInstanceState = state - return h.PostProvisionInstanceReturn, h.PostProvisionInstanceError -} - -func (h *MockHook) PreProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string) (HookAction, error) { - h.Lock() - defer h.Unlock() - - h.PreProvisionInstanceStepCalled = true - h.PreProvisionInstanceStepAddr = addr - h.PreProvisionInstanceStepProvisionerType = typeName - return h.PreProvisionInstanceStepReturn, h.PreProvisionInstanceStepError -} - -func (h *MockHook) PostProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string, err error) (HookAction, error) { - h.Lock() - defer h.Unlock() - - h.PostProvisionInstanceStepCalled = true - h.PostProvisionInstanceStepAddr = addr - h.PostProvisionInstanceStepProvisionerType = typeName - h.PostProvisionInstanceStepErrorArg = err - return h.PostProvisionInstanceStepReturn, h.PostProvisionInstanceStepError -} - -func (h *MockHook) ProvisionOutput(addr addrs.AbsResourceInstance, typeName string, line string) { - h.Lock() - defer h.Unlock() - - h.ProvisionOutputCalled = true - h.ProvisionOutputAddr = addr - h.ProvisionOutputProvisionerType = typeName - h.ProvisionOutputMessage = line -} - -func (h *MockHook) PreRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value) (HookAction, error) { - h.Lock() - defer h.Unlock() - - h.PreRefreshCalled = true - h.PreRefreshAddr = addr - h.PreRefreshGen = gen - h.PreRefreshPriorState = priorState - return h.PreRefreshReturn, h.PreRefreshError -} - -func (h *MockHook) PostRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value, newState cty.Value) (HookAction, error) { - h.Lock() - defer h.Unlock() - - h.PostRefreshCalled = true - h.PostRefreshAddr = addr - h.PostRefreshPriorState = priorState - h.PostRefreshNewState = newState - return h.PostRefreshReturn, h.PostRefreshError -} - -func (h *MockHook) PreImportState(addr addrs.AbsResourceInstance, importID string) (HookAction, error) { - h.Lock() - defer h.Unlock() - - h.PreImportStateCalled = true - h.PreImportStateAddr = addr - h.PreImportStateID = importID - return h.PreImportStateReturn, h.PreImportStateError -} - -func (h *MockHook) PostImportState(addr addrs.AbsResourceInstance, imported []providers.ImportedResource) (HookAction, error) { - h.Lock() - defer h.Unlock() - - h.PostImportStateCalled = true - h.PostImportStateAddr = addr - h.PostImportStateNewStates = imported - return h.PostImportStateReturn, h.PostImportStateError -} - -func (h *MockHook) PostStateUpdate(new *states.State) (HookAction, error) { - h.Lock() - defer h.Unlock() - - h.PostStateUpdateCalled = true - h.PostStateUpdateState = new - return h.PostStateUpdateReturn, h.PostStateUpdateError -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/hook_stop.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/hook_stop.go deleted file mode 100644 index 42c3d20c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/hook_stop.go +++ /dev/null @@ -1,100 +0,0 @@ -package terraform - -import ( - "sync/atomic" - - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// stopHook is a private Hook implementation that Terraform uses to -// signal when to stop or cancel actions. -type stopHook struct { - stop uint32 -} - -var _ Hook = (*stopHook)(nil) - -func (h *stopHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (HookAction, error) { - return h.hook() -} - -func (h *stopHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generation, newState cty.Value, err error) (HookAction, error) { - return h.hook() -} - -func (h *stopHook) PreDiff(addr addrs.AbsResourceInstance, gen states.Generation, priorState, proposedNewState cty.Value) (HookAction, error) { - return h.hook() -} - -func (h *stopHook) PostDiff(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (HookAction, error) { - return h.hook() -} - -func (h *stopHook) PreProvisionInstance(addr addrs.AbsResourceInstance, state cty.Value) (HookAction, error) { - return h.hook() -} - -func (h *stopHook) PostProvisionInstance(addr addrs.AbsResourceInstance, state cty.Value) (HookAction, error) { - return h.hook() -} - -func (h *stopHook) PreProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string) (HookAction, error) { - return h.hook() -} - -func (h *stopHook) PostProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string, err error) (HookAction, error) { - return h.hook() -} - -func (h *stopHook) ProvisionOutput(addr addrs.AbsResourceInstance, typeName string, line string) { -} - -func (h *stopHook) PreRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value) (HookAction, error) { - return h.hook() -} - -func (h *stopHook) PostRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value, newState cty.Value) (HookAction, error) { - return h.hook() -} - -func (h *stopHook) PreImportState(addr addrs.AbsResourceInstance, importID string) (HookAction, error) { - return h.hook() -} - -func (h *stopHook) PostImportState(addr addrs.AbsResourceInstance, imported []providers.ImportedResource) (HookAction, error) { - return h.hook() -} - -func (h *stopHook) PostStateUpdate(new *states.State) (HookAction, error) { - return h.hook() -} - -func (h *stopHook) hook() (HookAction, error) { - if h.Stopped() { - // FIXME: This should really return an error since stopping partway - // through is not a successful run-to-completion, but we'll need to - // introduce that cautiously since existing automation solutions may - // be depending on this behavior. - return HookActionHalt, nil - } - - return HookActionContinue, nil -} - -// reset should be called within the lock context -func (h *stopHook) Reset() { - atomic.StoreUint32(&h.stop, 0) -} - -func (h *stopHook) Stop() { - atomic.StoreUint32(&h.stop, 1) -} - -func (h *stopHook) Stopped() bool { - return atomic.LoadUint32(&h.stop) == 1 -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/instancetype.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/instancetype.go index 375a8638..b01e5a48 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/instancetype.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/instancetype.go @@ -1,13 +1,13 @@ package terraform -//go:generate go run golang.org/x/tools/cmd/stringer -type=InstanceType instancetype.go +//go:generate go run golang.org/x/tools/cmd/stringer -type=instanceType instancetype.go -// InstanceType is an enum of the various types of instances store in the State -type InstanceType int +// instanceType is an enum of the various types of instances store in the State +type instanceType int const ( - TypeInvalid InstanceType = iota - TypePrimary - TypeTainted - TypeDeposed + typeInvalid instanceType = iota + typePrimary + typeTainted + typeDeposed ) diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/instancetype_string.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/instancetype_string.go index 95b7a980..782ef90c 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/instancetype_string.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/instancetype_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=InstanceType instancetype.go"; DO NOT EDIT. +// Code generated by "stringer -type=instanceType instancetype.go"; DO NOT EDIT. package terraform @@ -8,19 +8,19 @@ func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} - _ = x[TypeInvalid-0] - _ = x[TypePrimary-1] - _ = x[TypeTainted-2] - _ = x[TypeDeposed-3] + _ = x[typeInvalid-0] + _ = x[typePrimary-1] + _ = x[typeTainted-2] + _ = x[typeDeposed-3] } -const _InstanceType_name = "TypeInvalidTypePrimaryTypeTaintedTypeDeposed" +const _instanceType_name = "typeInvalidtypePrimarytypeTaintedtypeDeposed" -var _InstanceType_index = [...]uint8{0, 11, 22, 33, 44} +var _instanceType_index = [...]uint8{0, 11, 22, 33, 44} -func (i InstanceType) String() string { - if i < 0 || i >= InstanceType(len(_InstanceType_index)-1) { - return "InstanceType(" + strconv.FormatInt(int64(i), 10) + ")" +func (i instanceType) String() string { + if i < 0 || i >= instanceType(len(_instanceType_index)-1) { + return "instanceType(" + strconv.FormatInt(int64(i), 10) + ")" } - return _InstanceType_name[_InstanceType_index[i]:_InstanceType_index[i+1]] + return _instanceType_name[_instanceType_index[i]:_instanceType_index[i+1]] } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/module_dependencies.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/module_dependencies.go deleted file mode 100644 index f1434e62..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/module_dependencies.go +++ /dev/null @@ -1,202 +0,0 @@ -package terraform - -import ( - version "github.com/hashicorp/go-version" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/moduledeps" - "github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// ConfigTreeDependencies returns the dependencies of the tree of modules -// described by the given configuration and state. -// -// Both configuration and state are required because there can be resources -// implied by instances in the state that no longer exist in config. -func ConfigTreeDependencies(root *configs.Config, state *states.State) *moduledeps.Module { - // First we walk the configuration tree to build the overall structure - // and capture the explicit/implicit/inherited provider dependencies. - deps := configTreeConfigDependencies(root, nil) - - // Next we walk over the resources in the state to catch any additional - // dependencies created by existing resources that are no longer in config. - // Most things we find in state will already be present in 'deps', but - // we're interested in the rare thing that isn't. - configTreeMergeStateDependencies(deps, state) - - return deps -} - -func configTreeConfigDependencies(root *configs.Config, inheritProviders map[string]*configs.Provider) *moduledeps.Module { - if root == nil { - // If no config is provided, we'll make a synthetic root. - // This isn't necessarily correct if we're called with a nil that - // *isn't* at the root, but in practice that can never happen. - return &moduledeps.Module{ - Name: "root", - Providers: make(moduledeps.Providers), - } - } - - name := "root" - if len(root.Path) != 0 { - name = root.Path[len(root.Path)-1] - } - - ret := &moduledeps.Module{ - Name: name, - } - - module := root.Module - - // Provider dependencies - { - providers := make(moduledeps.Providers) - - // The main way to declare a provider dependency is explicitly inside - // the "terraform" block, which allows declaring a requirement without - // also creating a configuration. - for fullName, constraints := range module.ProviderRequirements { - inst := moduledeps.ProviderInstance(fullName) - - // The handling here is a bit fiddly because the moduledeps package - // was designed around the legacy (pre-0.12) configuration model - // and hasn't yet been revised to handle the new model. As a result, - // we need to do some translation here. - // FIXME: Eventually we should adjust the underlying model so we - // can also retain the source location of each constraint, for - // more informative output from the "terraform providers" command. - var rawConstraints version.Constraints - for _, constraint := range constraints { - rawConstraints = append(rawConstraints, constraint.Required...) - } - discoConstraints := discovery.NewConstraints(rawConstraints) - - providers[inst] = moduledeps.ProviderDependency{ - Constraints: discoConstraints, - Reason: moduledeps.ProviderDependencyExplicit, - } - } - - // Provider configurations can also include version constraints, - // allowing for more terse declaration in situations where both a - // configuration and a constraint are defined in the same module. - for fullName, pCfg := range module.ProviderConfigs { - inst := moduledeps.ProviderInstance(fullName) - discoConstraints := discovery.AllVersions - if pCfg.Version.Required != nil { - discoConstraints = discovery.NewConstraints(pCfg.Version.Required) - } - if existing, exists := providers[inst]; exists { - existing.Constraints = existing.Constraints.Append(discoConstraints) - } else { - providers[inst] = moduledeps.ProviderDependency{ - Constraints: discoConstraints, - Reason: moduledeps.ProviderDependencyExplicit, - } - } - } - - // Each resource in the configuration creates an *implicit* provider - // dependency, though we'll only record it if there isn't already - // an explicit dependency on the same provider. - for _, rc := range module.ManagedResources { - addr := rc.ProviderConfigAddr() - inst := moduledeps.ProviderInstance(addr.StringCompact()) - if _, exists := providers[inst]; exists { - // Explicit dependency already present - continue - } - - reason := moduledeps.ProviderDependencyImplicit - if _, inherited := inheritProviders[addr.StringCompact()]; inherited { - reason = moduledeps.ProviderDependencyInherited - } - - providers[inst] = moduledeps.ProviderDependency{ - Constraints: discovery.AllVersions, - Reason: reason, - } - } - for _, rc := range module.DataResources { - addr := rc.ProviderConfigAddr() - inst := moduledeps.ProviderInstance(addr.StringCompact()) - if _, exists := providers[inst]; exists { - // Explicit dependency already present - continue - } - - reason := moduledeps.ProviderDependencyImplicit - if _, inherited := inheritProviders[addr.String()]; inherited { - reason = moduledeps.ProviderDependencyInherited - } - - providers[inst] = moduledeps.ProviderDependency{ - Constraints: discovery.AllVersions, - Reason: reason, - } - } - - ret.Providers = providers - } - - childInherit := make(map[string]*configs.Provider) - for k, v := range inheritProviders { - childInherit[k] = v - } - for k, v := range module.ProviderConfigs { - childInherit[k] = v - } - for _, c := range root.Children { - ret.Children = append(ret.Children, configTreeConfigDependencies(c, childInherit)) - } - - return ret -} - -func configTreeMergeStateDependencies(root *moduledeps.Module, state *states.State) { - if state == nil { - return - } - - findModule := func(path addrs.ModuleInstance) *moduledeps.Module { - module := root - for _, step := range path { - var next *moduledeps.Module - for _, cm := range module.Children { - if cm.Name == step.Name { - next = cm - break - } - } - - if next == nil { - // If we didn't find a next node, we'll need to make one - next = &moduledeps.Module{ - Name: step.Name, - Providers: make(moduledeps.Providers), - } - module.Children = append(module.Children, next) - } - - module = next - } - return module - } - - for _, ms := range state.Modules { - module := findModule(ms.Addr) - - for _, rs := range ms.Resources { - inst := moduledeps.ProviderInstance(rs.ProviderConfig.ProviderConfig.StringCompact()) - if _, exists := module.Providers[inst]; !exists { - module.Providers[inst] = moduledeps.ProviderDependency{ - Constraints: discovery.AllVersions, - Reason: moduledeps.ProviderDependencyFromState, - } - } - } - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_count_boundary.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_count_boundary.go deleted file mode 100644 index acd8262b..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_count_boundary.go +++ /dev/null @@ -1,22 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" -) - -// NodeCountBoundary fixes up any transitions between "each modes" in objects -// saved in state, such as switching from NoEach to EachInt. -type NodeCountBoundary struct { - Config *configs.Config -} - -func (n *NodeCountBoundary) Name() string { - return "meta.count-boundary (EachMode fixup)" -} - -// GraphNodeEvalable -func (n *NodeCountBoundary) EvalTree() EvalNode { - return &EvalCountFixZeroOneBoundaryGlobal{ - Config: n.Config, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_data_destroy.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_data_destroy.go deleted file mode 100644 index 56a33bce..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_data_destroy.go +++ /dev/null @@ -1,40 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// NodeDestroyableDataResourceInstance represents a resource that is "destroyable": -// it is ready to be destroyed. -type NodeDestroyableDataResourceInstance struct { - *NodeAbstractResourceInstance -} - -// GraphNodeEvalable -func (n *NodeDestroyableDataResourceInstance) EvalTree() EvalNode { - addr := n.ResourceInstanceAddr() - - var providerSchema *ProviderSchema - // We don't need the provider, but we're calling EvalGetProvider to load the - // schema. - var provider providers.Interface - - // Just destroy it. - var state *states.ResourceInstanceObject - return &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: n.ResolvedProvider, - Output: &provider, - Schema: &providerSchema, - }, - &EvalWriteState{ - Addr: addr.Resource, - State: &state, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - }, - }, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_data_refresh.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_data_refresh.go deleted file mode 100644 index 56283c0a..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_data_refresh.go +++ /dev/null @@ -1,229 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - "github.com/zclconf/go-cty/cty" -) - -// NodeRefreshableDataResource represents a resource that is "refreshable". -type NodeRefreshableDataResource struct { - *NodeAbstractResource -} - -var ( - _ GraphNodeSubPath = (*NodeRefreshableDataResource)(nil) - _ GraphNodeDynamicExpandable = (*NodeRefreshableDataResource)(nil) - _ GraphNodeReferenceable = (*NodeRefreshableDataResource)(nil) - _ GraphNodeReferencer = (*NodeRefreshableDataResource)(nil) - _ GraphNodeResource = (*NodeRefreshableDataResource)(nil) - _ GraphNodeAttachResourceConfig = (*NodeRefreshableDataResource)(nil) -) - -// GraphNodeDynamicExpandable -func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, error) { - var diags tfdiags.Diagnostics - - count, countKnown, countDiags := evaluateResourceCountExpressionKnown(n.Config.Count, ctx) - diags = diags.Append(countDiags) - if countDiags.HasErrors() { - return nil, diags.Err() - } - if !countKnown { - // If the count isn't known yet, we'll skip refreshing and try expansion - // again during the plan walk. - return nil, nil - } - - forEachMap, forEachKnown, forEachDiags := evaluateResourceForEachExpressionKnown(n.Config.ForEach, ctx) - diags = diags.Append(forEachDiags) - if forEachDiags.HasErrors() { - return nil, diags.Err() - } - if !forEachKnown { - // If the for_each isn't known yet, we'll skip refreshing and try expansion - // again during the plan walk. - return nil, nil - } - - // Next we need to potentially rename an instance address in the state - // if we're transitioning whether "count" is set at all. - fixResourceCountSetTransition(ctx, n.ResourceAddr(), count != -1) - - // Our graph transformers require access to the full state, so we'll - // temporarily lock it while we work on this. - state := ctx.State().Lock() - defer ctx.State().Unlock() - - // The concrete resource factory we'll use - concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex { - // Add the config and state since we don't do that via transforms - a.Config = n.Config - a.ResolvedProvider = n.ResolvedProvider - - return &NodeRefreshableDataResourceInstance{ - NodeAbstractResourceInstance: a, - } - } - - // We also need a destroyable resource for orphans that are a result of a - // scaled-in count. - concreteResourceDestroyable := func(a *NodeAbstractResourceInstance) dag.Vertex { - // Add the config and provider since we don't do that via transforms - a.Config = n.Config - a.ResolvedProvider = n.ResolvedProvider - - return &NodeDestroyableDataResourceInstance{ - NodeAbstractResourceInstance: a, - } - } - - // Start creating the steps - steps := []GraphTransformer{ - // Expand the count. - &ResourceCountTransformer{ - Concrete: concreteResource, - Schema: n.Schema, - Count: count, - ForEach: forEachMap, - Addr: n.ResourceAddr(), - }, - - // Add the count orphans. As these are orphaned refresh nodes, we add them - // directly as NodeDestroyableDataResource. - &OrphanResourceCountTransformer{ - Concrete: concreteResourceDestroyable, - Count: count, - ForEach: forEachMap, - Addr: n.ResourceAddr(), - State: state, - }, - - // Attach the state - &AttachStateTransformer{State: state}, - - // Targeting - &TargetsTransformer{Targets: n.Targets}, - - // Connect references so ordering is correct - &ReferenceTransformer{}, - - // Make sure there is a single root - &RootTransformer{}, - } - - // Build the graph - b := &BasicGraphBuilder{ - Steps: steps, - Validate: true, - Name: "NodeRefreshableDataResource", - } - - graph, diags := b.Build(ctx.Path()) - return graph, diags.ErrWithWarnings() -} - -// NodeRefreshableDataResourceInstance represents a single resource instance -// that is refreshable. -type NodeRefreshableDataResourceInstance struct { - *NodeAbstractResourceInstance -} - -// GraphNodeEvalable -func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode { - addr := n.ResourceInstanceAddr() - - // These variables are the state for the eval sequence below, and are - // updated through pointers. - var provider providers.Interface - var providerSchema *ProviderSchema - var change *plans.ResourceInstanceChange - var state *states.ResourceInstanceObject - var configVal cty.Value - - return &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: n.ResolvedProvider, - Output: &provider, - Schema: &providerSchema, - }, - - // Always destroy the existing state first, since we must - // make sure that values from a previous read will not - // get interpolated if we end up needing to defer our - // loading until apply time. - &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - State: &state, // a pointer to nil, here - ProviderSchema: &providerSchema, - }, - - // EvalReadData will _attempt_ to read the data source, but may - // generate an incomplete planned object if the configuration - // includes values that won't be known until apply. - &EvalReadData{ - Addr: addr.Resource, - Config: n.Config, - Dependencies: n.StateReferences(), - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - OutputChange: &change, - OutputConfigValue: &configVal, - OutputState: &state, - // If the config explicitly has a depends_on for this data - // source, assume the intention is to prevent refreshing ahead - // of that dependency, and therefore we need to deal with this - // resource during the apply phase. We do that by forcing this - // read to result in a plan. - ForcePlanRead: len(n.Config.DependsOn) > 0, - }, - - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - return (*state).Status != states.ObjectPlanned, nil - }, - Then: &EvalSequence{ - Nodes: []EvalNode{ - &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - State: &state, - ProviderSchema: &providerSchema, - }, - &EvalUpdateStateHook{}, - }, - }, - Else: &EvalSequence{ - // We can't deal with this yet, so we'll repeat this step - // during the plan walk to produce a planned change to read - // this during the apply walk. However, we do still need to - // save the generated change and partial state so that - // results from it can be included in other data resources - // or provider configurations during the refresh walk. - // (The planned object we save in the state here will be - // pruned out at the end of the refresh walk, returning - // it back to being unset again for subsequent walks.) - Nodes: []EvalNode{ - &EvalWriteDiff{ - Addr: addr.Resource, - Change: &change, - ProviderSchema: &providerSchema, - }, - &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - State: &state, - ProviderSchema: &providerSchema, - }, - }, - }, - }, - }, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_local.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_local.go deleted file mode 100644 index 38681d83..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_local.go +++ /dev/null @@ -1,70 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/lang" -) - -// NodeLocal represents a named local value in a particular module. -// -// Local value nodes only have one operation, common to all walk types: -// evaluate the result and place it in state. -type NodeLocal struct { - Addr addrs.AbsLocalValue - Config *configs.Local -} - -var ( - _ GraphNodeSubPath = (*NodeLocal)(nil) - _ RemovableIfNotTargeted = (*NodeLocal)(nil) - _ GraphNodeReferenceable = (*NodeLocal)(nil) - _ GraphNodeReferencer = (*NodeLocal)(nil) - _ GraphNodeEvalable = (*NodeLocal)(nil) - _ dag.GraphNodeDotter = (*NodeLocal)(nil) -) - -func (n *NodeLocal) Name() string { - return n.Addr.String() -} - -// GraphNodeSubPath -func (n *NodeLocal) Path() addrs.ModuleInstance { - return n.Addr.Module -} - -// RemovableIfNotTargeted -func (n *NodeLocal) RemoveIfNotTargeted() bool { - return true -} - -// GraphNodeReferenceable -func (n *NodeLocal) ReferenceableAddrs() []addrs.Referenceable { - return []addrs.Referenceable{n.Addr.LocalValue} -} - -// GraphNodeReferencer -func (n *NodeLocal) References() []*addrs.Reference { - refs, _ := lang.ReferencesInExpr(n.Config.Expr) - return appendResourceDestroyReferences(refs) -} - -// GraphNodeEvalable -func (n *NodeLocal) EvalTree() EvalNode { - return &EvalLocal{ - Addr: n.Addr.LocalValue, - Expr: n.Config.Expr, - } -} - -// dag.GraphNodeDotter impl. -func (n *NodeLocal) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { - return &dag.DotNode{ - Name: name, - Attrs: map[string]string{ - "label": n.Name(), - "shape": "note", - }, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_module_removed.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_module_removed.go deleted file mode 100644 index 6e3cb41d..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_module_removed.go +++ /dev/null @@ -1,89 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// NodeModuleRemoved represents a module that is no longer in the -// config. -type NodeModuleRemoved struct { - Addr addrs.ModuleInstance -} - -var ( - _ GraphNodeSubPath = (*NodeModuleRemoved)(nil) - _ RemovableIfNotTargeted = (*NodeModuleRemoved)(nil) - _ GraphNodeEvalable = (*NodeModuleRemoved)(nil) - _ GraphNodeReferencer = (*NodeModuleRemoved)(nil) - _ GraphNodeReferenceOutside = (*NodeModuleRemoved)(nil) -) - -func (n *NodeModuleRemoved) Name() string { - return fmt.Sprintf("%s (removed)", n.Addr.String()) -} - -// GraphNodeSubPath -func (n *NodeModuleRemoved) Path() addrs.ModuleInstance { - return n.Addr -} - -// GraphNodeEvalable -func (n *NodeModuleRemoved) EvalTree() EvalNode { - return &EvalOpFilter{ - Ops: []walkOperation{walkRefresh, walkApply, walkDestroy}, - Node: &EvalCheckModuleRemoved{ - Addr: n.Addr, - }, - } -} - -func (n *NodeModuleRemoved) ReferenceOutside() (selfPath, referencePath addrs.ModuleInstance) { - // Our "References" implementation indicates that this node depends on - // the call to the module it represents, which implicitly depends on - // everything inside the module. That reference must therefore be - // interpreted in terms of our parent module. - return n.Addr, n.Addr.Parent() -} - -func (n *NodeModuleRemoved) References() []*addrs.Reference { - // We depend on the call to the module we represent, because that - // implicitly then depends on everything inside that module. - // Our ReferenceOutside implementation causes this to be interpreted - // within the parent module. - - _, call := n.Addr.CallInstance() - return []*addrs.Reference{ - { - Subject: call, - - // No source range here, because there's nothing reasonable for - // us to return. - }, - } -} - -// RemovableIfNotTargeted -func (n *NodeModuleRemoved) RemoveIfNotTargeted() bool { - // We need to add this so that this node will be removed if - // it isn't targeted or a dependency of a target. - return true -} - -// EvalCheckModuleRemoved is an EvalNode implementation that verifies that -// a module has been removed from the state as expected. -type EvalCheckModuleRemoved struct { - Addr addrs.ModuleInstance -} - -func (n *EvalCheckModuleRemoved) Eval(ctx EvalContext) (interface{}, error) { - mod := ctx.State().Module(n.Addr) - if mod != nil { - // If we get here then that indicates a bug either in the states - // module or in an earlier step of the graph walk, since we should've - // pruned out the module when the last resource was removed from it. - return nil, fmt.Errorf("leftover module %s in state that should have been removed; this is a bug in Terraform and should be reported", n.Addr) - } - return nil, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_module_variable.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_module_variable.go deleted file mode 100644 index 76311a56..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_module_variable.go +++ /dev/null @@ -1,142 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/lang" - "github.com/zclconf/go-cty/cty" -) - -// NodeApplyableModuleVariable represents a module variable input during -// the apply step. -type NodeApplyableModuleVariable struct { - Addr addrs.AbsInputVariableInstance - Config *configs.Variable // Config is the var in the config - Expr hcl.Expression // Expr is the value expression given in the call -} - -// Ensure that we are implementing all of the interfaces we think we are -// implementing. -var ( - _ GraphNodeSubPath = (*NodeApplyableModuleVariable)(nil) - _ RemovableIfNotTargeted = (*NodeApplyableModuleVariable)(nil) - _ GraphNodeReferenceOutside = (*NodeApplyableModuleVariable)(nil) - _ GraphNodeReferenceable = (*NodeApplyableModuleVariable)(nil) - _ GraphNodeReferencer = (*NodeApplyableModuleVariable)(nil) - _ GraphNodeEvalable = (*NodeApplyableModuleVariable)(nil) - _ dag.GraphNodeDotter = (*NodeApplyableModuleVariable)(nil) -) - -func (n *NodeApplyableModuleVariable) Name() string { - return n.Addr.String() -} - -// GraphNodeSubPath -func (n *NodeApplyableModuleVariable) Path() addrs.ModuleInstance { - // We execute in the parent scope (above our own module) because - // expressions in our value are resolved in that context. - return n.Addr.Module.Parent() -} - -// RemovableIfNotTargeted -func (n *NodeApplyableModuleVariable) RemoveIfNotTargeted() bool { - // We need to add this so that this node will be removed if - // it isn't targeted or a dependency of a target. - return true -} - -// GraphNodeReferenceOutside implementation -func (n *NodeApplyableModuleVariable) ReferenceOutside() (selfPath, referencePath addrs.ModuleInstance) { - - // Module input variables have their value expressions defined in the - // context of their calling (parent) module, and so references from - // a node of this type should be resolved in the parent module instance. - referencePath = n.Addr.Module.Parent() - - // Input variables are _referenced_ from their own module, though. - selfPath = n.Addr.Module - - return // uses named return values -} - -// GraphNodeReferenceable -func (n *NodeApplyableModuleVariable) ReferenceableAddrs() []addrs.Referenceable { - return []addrs.Referenceable{n.Addr.Variable} -} - -// GraphNodeReferencer -func (n *NodeApplyableModuleVariable) References() []*addrs.Reference { - - // If we have no value expression, we cannot depend on anything. - if n.Expr == nil { - return nil - } - - // Variables in the root don't depend on anything, because their values - // are gathered prior to the graph walk and recorded in the context. - if len(n.Addr.Module) == 0 { - return nil - } - - // Otherwise, we depend on anything referenced by our value expression. - // We ignore diagnostics here under the assumption that we'll re-eval - // all these things later and catch them then; for our purposes here, - // we only care about valid references. - // - // Due to our GraphNodeReferenceOutside implementation, the addresses - // returned by this function are interpreted in the _parent_ module from - // where our associated variable was declared, which is correct because - // our value expression is assigned within a "module" block in the parent - // module. - refs, _ := lang.ReferencesInExpr(n.Expr) - return refs -} - -// GraphNodeEvalable -func (n *NodeApplyableModuleVariable) EvalTree() EvalNode { - // If we have no value, do nothing - if n.Expr == nil { - return &EvalNoop{} - } - - // Otherwise, interpolate the value of this variable and set it - // within the variables mapping. - vals := make(map[string]cty.Value) - - _, call := n.Addr.Module.CallInstance() - - return &EvalSequence{ - Nodes: []EvalNode{ - &EvalOpFilter{ - Ops: []walkOperation{walkRefresh, walkPlan, walkApply, - walkDestroy, walkValidate}, - Node: &EvalModuleCallArgument{ - Addr: n.Addr.Variable, - Config: n.Config, - Expr: n.Expr, - Values: vals, - - IgnoreDiagnostics: false, - }, - }, - - &EvalSetModuleCallArguments{ - Module: call, - Values: vals, - }, - }, - } -} - -// dag.GraphNodeDotter impl. -func (n *NodeApplyableModuleVariable) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { - return &dag.DotNode{ - Name: name, - Attrs: map[string]string{ - "label": n.Name(), - "shape": "note", - }, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_output.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_output.go deleted file mode 100644 index 75305712..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_output.go +++ /dev/null @@ -1,200 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/lang" -) - -// NodeApplyableOutput represents an output that is "applyable": -// it is ready to be applied. -type NodeApplyableOutput struct { - Addr addrs.AbsOutputValue - Config *configs.Output // Config is the output in the config -} - -var ( - _ GraphNodeSubPath = (*NodeApplyableOutput)(nil) - _ RemovableIfNotTargeted = (*NodeApplyableOutput)(nil) - _ GraphNodeTargetDownstream = (*NodeApplyableOutput)(nil) - _ GraphNodeReferenceable = (*NodeApplyableOutput)(nil) - _ GraphNodeReferencer = (*NodeApplyableOutput)(nil) - _ GraphNodeReferenceOutside = (*NodeApplyableOutput)(nil) - _ GraphNodeEvalable = (*NodeApplyableOutput)(nil) - _ dag.GraphNodeDotter = (*NodeApplyableOutput)(nil) -) - -func (n *NodeApplyableOutput) Name() string { - return n.Addr.String() -} - -// GraphNodeSubPath -func (n *NodeApplyableOutput) Path() addrs.ModuleInstance { - return n.Addr.Module -} - -// RemovableIfNotTargeted -func (n *NodeApplyableOutput) RemoveIfNotTargeted() bool { - // We need to add this so that this node will be removed if - // it isn't targeted or a dependency of a target. - return true -} - -// GraphNodeTargetDownstream -func (n *NodeApplyableOutput) TargetDownstream(targetedDeps, untargetedDeps *dag.Set) bool { - // If any of the direct dependencies of an output are targeted then - // the output must always be targeted as well, so its value will always - // be up-to-date at the completion of an apply walk. - return true -} - -func referenceOutsideForOutput(addr addrs.AbsOutputValue) (selfPath, referencePath addrs.ModuleInstance) { - - // Output values have their expressions resolved in the context of the - // module where they are defined. - referencePath = addr.Module - - // ...but they are referenced in the context of their calling module. - selfPath = addr.Module.Parent() - - return // uses named return values - -} - -// GraphNodeReferenceOutside implementation -func (n *NodeApplyableOutput) ReferenceOutside() (selfPath, referencePath addrs.ModuleInstance) { - return referenceOutsideForOutput(n.Addr) -} - -func referenceableAddrsForOutput(addr addrs.AbsOutputValue) []addrs.Referenceable { - // An output in the root module can't be referenced at all. - if addr.Module.IsRoot() { - return nil - } - - // Otherwise, we can be referenced via a reference to our output name - // on the parent module's call, or via a reference to the entire call. - // e.g. module.foo.bar or just module.foo . - // Note that our ReferenceOutside method causes these addresses to be - // relative to the calling module, not the module where the output - // was declared. - _, outp := addr.ModuleCallOutput() - _, call := addr.Module.CallInstance() - return []addrs.Referenceable{outp, call} - -} - -// GraphNodeReferenceable -func (n *NodeApplyableOutput) ReferenceableAddrs() []addrs.Referenceable { - return referenceableAddrsForOutput(n.Addr) -} - -func referencesForOutput(c *configs.Output) []*addrs.Reference { - impRefs, _ := lang.ReferencesInExpr(c.Expr) - expRefs, _ := lang.References(c.DependsOn) - l := len(impRefs) + len(expRefs) - if l == 0 { - return nil - } - refs := make([]*addrs.Reference, 0, l) - refs = append(refs, impRefs...) - refs = append(refs, expRefs...) - return refs - -} - -// GraphNodeReferencer -func (n *NodeApplyableOutput) References() []*addrs.Reference { - return appendResourceDestroyReferences(referencesForOutput(n.Config)) -} - -// GraphNodeEvalable -func (n *NodeApplyableOutput) EvalTree() EvalNode { - return &EvalSequence{ - Nodes: []EvalNode{ - &EvalOpFilter{ - Ops: []walkOperation{walkRefresh, walkPlan, walkApply, walkValidate, walkDestroy, walkPlanDestroy}, - Node: &EvalWriteOutput{ - Addr: n.Addr.OutputValue, - Sensitive: n.Config.Sensitive, - Expr: n.Config.Expr, - }, - }, - }, - } -} - -// dag.GraphNodeDotter impl. -func (n *NodeApplyableOutput) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { - return &dag.DotNode{ - Name: name, - Attrs: map[string]string{ - "label": n.Name(), - "shape": "note", - }, - } -} - -// NodeDestroyableOutput represents an output that is "destroybale": -// its application will remove the output from the state. -type NodeDestroyableOutput struct { - Addr addrs.AbsOutputValue - Config *configs.Output // Config is the output in the config -} - -var ( - _ GraphNodeSubPath = (*NodeDestroyableOutput)(nil) - _ RemovableIfNotTargeted = (*NodeDestroyableOutput)(nil) - _ GraphNodeTargetDownstream = (*NodeDestroyableOutput)(nil) - _ GraphNodeReferencer = (*NodeDestroyableOutput)(nil) - _ GraphNodeEvalable = (*NodeDestroyableOutput)(nil) - _ dag.GraphNodeDotter = (*NodeDestroyableOutput)(nil) -) - -func (n *NodeDestroyableOutput) Name() string { - return fmt.Sprintf("%s (destroy)", n.Addr.String()) -} - -// GraphNodeSubPath -func (n *NodeDestroyableOutput) Path() addrs.ModuleInstance { - return n.Addr.Module -} - -// RemovableIfNotTargeted -func (n *NodeDestroyableOutput) RemoveIfNotTargeted() bool { - // We need to add this so that this node will be removed if - // it isn't targeted or a dependency of a target. - return true -} - -// This will keep the destroy node in the graph if its corresponding output -// node is also in the destroy graph. -func (n *NodeDestroyableOutput) TargetDownstream(targetedDeps, untargetedDeps *dag.Set) bool { - return true -} - -// GraphNodeReferencer -func (n *NodeDestroyableOutput) References() []*addrs.Reference { - return referencesForOutput(n.Config) -} - -// GraphNodeEvalable -func (n *NodeDestroyableOutput) EvalTree() EvalNode { - return &EvalDeleteOutput{ - Addr: n.Addr.OutputValue, - } -} - -// dag.GraphNodeDotter impl. -func (n *NodeDestroyableOutput) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { - return &dag.DotNode{ - Name: name, - Attrs: map[string]string{ - "label": n.Name(), - "shape": "note", - }, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_output_orphan.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_output_orphan.go deleted file mode 100644 index a76d1742..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_output_orphan.go +++ /dev/null @@ -1,48 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// NodeOutputOrphan represents an output that is an orphan. -type NodeOutputOrphan struct { - Addr addrs.AbsOutputValue -} - -var ( - _ GraphNodeSubPath = (*NodeOutputOrphan)(nil) - _ GraphNodeReferenceable = (*NodeOutputOrphan)(nil) - _ GraphNodeReferenceOutside = (*NodeOutputOrphan)(nil) - _ GraphNodeEvalable = (*NodeOutputOrphan)(nil) -) - -func (n *NodeOutputOrphan) Name() string { - return fmt.Sprintf("%s (orphan)", n.Addr.String()) -} - -// GraphNodeReferenceOutside implementation -func (n *NodeOutputOrphan) ReferenceOutside() (selfPath, referencePath addrs.ModuleInstance) { - return referenceOutsideForOutput(n.Addr) -} - -// GraphNodeReferenceable -func (n *NodeOutputOrphan) ReferenceableAddrs() []addrs.Referenceable { - return referenceableAddrsForOutput(n.Addr) -} - -// GraphNodeSubPath -func (n *NodeOutputOrphan) Path() addrs.ModuleInstance { - return n.Addr.Module -} - -// GraphNodeEvalable -func (n *NodeOutputOrphan) EvalTree() EvalNode { - return &EvalOpFilter{ - Ops: []walkOperation{walkRefresh, walkApply, walkDestroy}, - Node: &EvalDeleteOutput{ - Addr: n.Addr.OutputValue, - }, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_provider.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_provider.go deleted file mode 100644 index 2071ab16..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_provider.go +++ /dev/null @@ -1,11 +0,0 @@ -package terraform - -// NodeApplyableProvider represents a provider during an apply. -type NodeApplyableProvider struct { - *NodeAbstractProvider -} - -// GraphNodeEvalable -func (n *NodeApplyableProvider) EvalTree() EvalNode { - return ProviderEvalTree(n, n.ProviderConfig()) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_provider_abstract.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_provider_abstract.go deleted file mode 100644 index afdd4741..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_provider_abstract.go +++ /dev/null @@ -1,96 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" -) - -// ConcreteProviderNodeFunc is a callback type used to convert an -// abstract provider to a concrete one of some type. -type ConcreteProviderNodeFunc func(*NodeAbstractProvider) dag.Vertex - -// NodeAbstractProvider represents a provider that has no associated operations. -// It registers all the common interfaces across operations for providers. -type NodeAbstractProvider struct { - Addr addrs.AbsProviderConfig - - // The fields below will be automatically set using the Attach - // interfaces if you're running those transforms, but also be explicitly - // set if you already have that information. - - Config *configs.Provider - Schema *configschema.Block -} - -var ( - _ GraphNodeSubPath = (*NodeAbstractProvider)(nil) - _ RemovableIfNotTargeted = (*NodeAbstractProvider)(nil) - _ GraphNodeReferencer = (*NodeAbstractProvider)(nil) - _ GraphNodeProvider = (*NodeAbstractProvider)(nil) - _ GraphNodeAttachProvider = (*NodeAbstractProvider)(nil) - _ GraphNodeAttachProviderConfigSchema = (*NodeAbstractProvider)(nil) - _ dag.GraphNodeDotter = (*NodeAbstractProvider)(nil) -) - -func (n *NodeAbstractProvider) Name() string { - return n.Addr.String() -} - -// GraphNodeSubPath -func (n *NodeAbstractProvider) Path() addrs.ModuleInstance { - return n.Addr.Module -} - -// RemovableIfNotTargeted -func (n *NodeAbstractProvider) RemoveIfNotTargeted() bool { - // We need to add this so that this node will be removed if - // it isn't targeted or a dependency of a target. - return true -} - -// GraphNodeReferencer -func (n *NodeAbstractProvider) References() []*addrs.Reference { - if n.Config == nil || n.Schema == nil { - return nil - } - - return ReferencesFromConfig(n.Config.Config, n.Schema) -} - -// GraphNodeProvider -func (n *NodeAbstractProvider) ProviderAddr() addrs.AbsProviderConfig { - return n.Addr -} - -// GraphNodeProvider -func (n *NodeAbstractProvider) ProviderConfig() *configs.Provider { - if n.Config == nil { - return nil - } - - return n.Config -} - -// GraphNodeAttachProvider -func (n *NodeAbstractProvider) AttachProvider(c *configs.Provider) { - n.Config = c -} - -// GraphNodeAttachProviderConfigSchema impl. -func (n *NodeAbstractProvider) AttachProviderConfigSchema(schema *configschema.Block) { - n.Schema = schema -} - -// GraphNodeDotter impl. -func (n *NodeAbstractProvider) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { - return &dag.DotNode{ - Name: name, - Attrs: map[string]string{ - "label": n.Name(), - "shape": "diamond", - }, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_provider_disabled.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_provider_disabled.go deleted file mode 100644 index 51335654..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_provider_disabled.go +++ /dev/null @@ -1,27 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" -) - -// NodeDisabledProvider represents a provider that is disabled. A disabled -// provider does nothing. It exists to properly set inheritance information -// for child providers. -type NodeDisabledProvider struct { - *NodeAbstractProvider -} - -var ( - _ GraphNodeSubPath = (*NodeDisabledProvider)(nil) - _ RemovableIfNotTargeted = (*NodeDisabledProvider)(nil) - _ GraphNodeReferencer = (*NodeDisabledProvider)(nil) - _ GraphNodeProvider = (*NodeDisabledProvider)(nil) - _ GraphNodeAttachProvider = (*NodeDisabledProvider)(nil) - _ dag.GraphNodeDotter = (*NodeDisabledProvider)(nil) -) - -func (n *NodeDisabledProvider) Name() string { - return fmt.Sprintf("%s (disabled)", n.NodeAbstractProvider.Name()) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_provider_eval.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_provider_eval.go deleted file mode 100644 index 580e60cb..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_provider_eval.go +++ /dev/null @@ -1,20 +0,0 @@ -package terraform - -// NodeEvalableProvider represents a provider during an "eval" walk. -// This special provider node type just initializes a provider and -// fetches its schema, without configuring it or otherwise interacting -// with it. -type NodeEvalableProvider struct { - *NodeAbstractProvider -} - -// GraphNodeEvalable -func (n *NodeEvalableProvider) EvalTree() EvalNode { - addr := n.Addr - relAddr := addr.ProviderConfig - - return &EvalInitProvider{ - TypeName: relAddr.Type, - Addr: addr.ProviderConfig, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_provisioner.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_provisioner.go deleted file mode 100644 index 573f030d..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_provisioner.go +++ /dev/null @@ -1,44 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// NodeProvisioner represents a provider that has no associated operations. -// It registers all the common interfaces across operations for providers. -type NodeProvisioner struct { - NameValue string - PathValue addrs.ModuleInstance -} - -var ( - _ GraphNodeSubPath = (*NodeProvisioner)(nil) - _ GraphNodeProvisioner = (*NodeProvisioner)(nil) - _ GraphNodeEvalable = (*NodeProvisioner)(nil) -) - -func (n *NodeProvisioner) Name() string { - result := fmt.Sprintf("provisioner.%s", n.NameValue) - if len(n.PathValue) > 0 { - result = fmt.Sprintf("%s.%s", n.PathValue.String(), result) - } - - return result -} - -// GraphNodeSubPath -func (n *NodeProvisioner) Path() addrs.ModuleInstance { - return n.PathValue -} - -// GraphNodeProvisioner -func (n *NodeProvisioner) ProvisionerName() string { - return n.NameValue -} - -// GraphNodeEvalable impl. -func (n *NodeProvisioner) EvalTree() EvalNode { - return &EvalInitProvisioner{Name: n.NameValue} -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_abstract.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_abstract.go deleted file mode 100644 index c7b0e3c8..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_abstract.go +++ /dev/null @@ -1,446 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - "sort" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/lang" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// ConcreteResourceNodeFunc is a callback type used to convert an -// abstract resource to a concrete one of some type. -type ConcreteResourceNodeFunc func(*NodeAbstractResource) dag.Vertex - -// GraphNodeResource is implemented by any nodes that represent a resource. -// The type of operation cannot be assumed, only that this node represents -// the given resource. -type GraphNodeResource interface { - ResourceAddr() addrs.AbsResource -} - -// ConcreteResourceInstanceNodeFunc is a callback type used to convert an -// abstract resource instance to a concrete one of some type. -type ConcreteResourceInstanceNodeFunc func(*NodeAbstractResourceInstance) dag.Vertex - -// GraphNodeResourceInstance is implemented by any nodes that represent -// a resource instance. A single resource may have multiple instances if, -// for example, the "count" or "for_each" argument is used for it in -// configuration. -type GraphNodeResourceInstance interface { - ResourceInstanceAddr() addrs.AbsResourceInstance -} - -// NodeAbstractResource represents a resource that has no associated -// operations. It registers all the interfaces for a resource that common -// across multiple operation types. -type NodeAbstractResource struct { - Addr addrs.AbsResource // Addr is the address for this resource - - // The fields below will be automatically set using the Attach - // interfaces if you're running those transforms, but also be explicitly - // set if you already have that information. - - Schema *configschema.Block // Schema for processing the configuration body - SchemaVersion uint64 // Schema version of "Schema", as decided by the provider - Config *configs.Resource // Config is the resource in the config - - ProvisionerSchemas map[string]*configschema.Block - - Targets []addrs.Targetable // Set from GraphNodeTargetable - - // The address of the provider this resource will use - ResolvedProvider addrs.AbsProviderConfig -} - -var ( - _ GraphNodeSubPath = (*NodeAbstractResource)(nil) - _ GraphNodeReferenceable = (*NodeAbstractResource)(nil) - _ GraphNodeReferencer = (*NodeAbstractResource)(nil) - _ GraphNodeProviderConsumer = (*NodeAbstractResource)(nil) - _ GraphNodeProvisionerConsumer = (*NodeAbstractResource)(nil) - _ GraphNodeResource = (*NodeAbstractResource)(nil) - _ GraphNodeAttachResourceConfig = (*NodeAbstractResource)(nil) - _ GraphNodeAttachResourceSchema = (*NodeAbstractResource)(nil) - _ GraphNodeAttachProvisionerSchema = (*NodeAbstractResource)(nil) - _ GraphNodeTargetable = (*NodeAbstractResource)(nil) - _ dag.GraphNodeDotter = (*NodeAbstractResource)(nil) -) - -// NewNodeAbstractResource creates an abstract resource graph node for -// the given absolute resource address. -func NewNodeAbstractResource(addr addrs.AbsResource) *NodeAbstractResource { - return &NodeAbstractResource{ - Addr: addr, - } -} - -// NodeAbstractResourceInstance represents a resource instance with no -// associated operations. It embeds NodeAbstractResource but additionally -// contains an instance key, used to identify one of potentially many -// instances that were created from a resource in configuration, e.g. using -// the "count" or "for_each" arguments. -type NodeAbstractResourceInstance struct { - NodeAbstractResource - InstanceKey addrs.InstanceKey - - // The fields below will be automatically set using the Attach - // interfaces if you're running those transforms, but also be explicitly - // set if you already have that information. - - ResourceState *states.Resource -} - -var ( - _ GraphNodeSubPath = (*NodeAbstractResourceInstance)(nil) - _ GraphNodeReferenceable = (*NodeAbstractResourceInstance)(nil) - _ GraphNodeReferencer = (*NodeAbstractResourceInstance)(nil) - _ GraphNodeProviderConsumer = (*NodeAbstractResourceInstance)(nil) - _ GraphNodeProvisionerConsumer = (*NodeAbstractResourceInstance)(nil) - _ GraphNodeResource = (*NodeAbstractResourceInstance)(nil) - _ GraphNodeResourceInstance = (*NodeAbstractResourceInstance)(nil) - _ GraphNodeAttachResourceState = (*NodeAbstractResourceInstance)(nil) - _ GraphNodeAttachResourceConfig = (*NodeAbstractResourceInstance)(nil) - _ GraphNodeAttachResourceSchema = (*NodeAbstractResourceInstance)(nil) - _ GraphNodeAttachProvisionerSchema = (*NodeAbstractResourceInstance)(nil) - _ GraphNodeTargetable = (*NodeAbstractResourceInstance)(nil) - _ dag.GraphNodeDotter = (*NodeAbstractResourceInstance)(nil) -) - -// NewNodeAbstractResourceInstance creates an abstract resource instance graph -// node for the given absolute resource instance address. -func NewNodeAbstractResourceInstance(addr addrs.AbsResourceInstance) *NodeAbstractResourceInstance { - // Due to the fact that we embed NodeAbstractResource, the given address - // actually ends up split between the resource address in the embedded - // object and the InstanceKey field in our own struct. The - // ResourceInstanceAddr method will stick these back together again on - // request. - return &NodeAbstractResourceInstance{ - NodeAbstractResource: NodeAbstractResource{ - Addr: addr.ContainingResource(), - }, - InstanceKey: addr.Resource.Key, - } -} - -func (n *NodeAbstractResource) Name() string { - return n.ResourceAddr().String() -} - -func (n *NodeAbstractResourceInstance) Name() string { - return n.ResourceInstanceAddr().String() -} - -// GraphNodeSubPath -func (n *NodeAbstractResource) Path() addrs.ModuleInstance { - return n.Addr.Module -} - -// GraphNodeReferenceable -func (n *NodeAbstractResource) ReferenceableAddrs() []addrs.Referenceable { - return []addrs.Referenceable{n.Addr.Resource} -} - -// GraphNodeReferenceable -func (n *NodeAbstractResourceInstance) ReferenceableAddrs() []addrs.Referenceable { - addr := n.ResourceInstanceAddr() - return []addrs.Referenceable{ - addr.Resource, - - // A resource instance can also be referenced by the address of its - // containing resource, so that e.g. a reference to aws_instance.foo - // would match both aws_instance.foo[0] and aws_instance.foo[1]. - addr.ContainingResource().Resource, - } -} - -// GraphNodeReferencer -func (n *NodeAbstractResource) References() []*addrs.Reference { - // If we have a config then we prefer to use that. - if c := n.Config; c != nil { - var result []*addrs.Reference - - for _, traversal := range c.DependsOn { - ref, err := addrs.ParseRef(traversal) - if err != nil { - // We ignore this here, because this isn't a suitable place to return - // errors. This situation should be caught and rejected during - // validation. - log.Printf("[ERROR] Can't parse %#v from depends_on as reference: %s", traversal, err) - continue - } - - result = append(result, ref) - } - - if n.Schema == nil { - // Should never happens, but we'll log if it does so that we can - // see this easily when debugging. - log.Printf("[WARN] no schema is attached to %s, so config references cannot be detected", n.Name()) - } - - refs, _ := lang.ReferencesInExpr(c.Count) - result = append(result, refs...) - refs, _ = lang.ReferencesInExpr(c.ForEach) - result = append(result, refs...) - refs, _ = lang.ReferencesInBlock(c.Config, n.Schema) - result = append(result, refs...) - if c.Managed != nil { - for _, p := range c.Managed.Provisioners { - if p.When != configs.ProvisionerWhenCreate { - continue - } - if p.Connection != nil { - refs, _ = lang.ReferencesInBlock(p.Connection.Config, connectionBlockSupersetSchema) - result = append(result, refs...) - } - - schema := n.ProvisionerSchemas[p.Type] - if schema == nil { - log.Printf("[WARN] no schema for provisioner %q is attached to %s, so provisioner block references cannot be detected", p.Type, n.Name()) - } - refs, _ = lang.ReferencesInBlock(p.Config, schema) - result = append(result, refs...) - } - } - return result - } - - // Otherwise, we have no references. - return nil -} - -// GraphNodeReferencer -func (n *NodeAbstractResourceInstance) References() []*addrs.Reference { - // If we have a configuration attached then we'll delegate to our - // embedded abstract resource, which knows how to extract dependencies - // from configuration. - if n.Config != nil { - if n.Schema == nil { - // We'll produce a log message about this out here so that - // we can include the full instance address, since the equivalent - // message in NodeAbstractResource.References cannot see it. - log.Printf("[WARN] no schema is attached to %s, so config references cannot be detected", n.Name()) - return nil - } - return n.NodeAbstractResource.References() - } - - // Otherwise, if we have state then we'll use the values stored in state - // as a fallback. - if rs := n.ResourceState; rs != nil { - if s := rs.Instance(n.InstanceKey); s != nil { - // State is still storing dependencies as old-style strings, so we'll - // need to do a little work here to massage this to the form we now - // want. - var result []*addrs.Reference - - // It is (apparently) possible for s.Current to be nil. This proved - // difficult to reproduce, so we will fix the symptom here and hope - // to find the root cause another time. - // - // https://github.com/hashicorp/terraform-plugin-sdk/issues/21407 - if s.Current == nil { - log.Printf("[WARN] no current state found for %s", n.Name()) - } else { - for _, addr := range s.Current.Dependencies { - if addr == nil { - // Should never happen; indicates a bug in the state loader - panic(fmt.Sprintf("dependencies for current object on %s contains nil address", n.ResourceInstanceAddr())) - } - - // This is a little weird: we need to manufacture an addrs.Reference - // with a fake range here because the state isn't something we can - // make source references into. - result = append(result, &addrs.Reference{ - Subject: addr, - SourceRange: tfdiags.SourceRange{ - Filename: "(state file)", - }, - }) - } - } - return result - } - } - - // If we have neither config nor state then we have no references. - return nil -} - -// StateReferences returns the dependencies to put into the state for -// this resource. -func (n *NodeAbstractResourceInstance) StateReferences() []addrs.Referenceable { - selfAddrs := n.ReferenceableAddrs() - - // Since we don't include the source location references in our - // results from this method, we'll also filter out duplicates: - // there's no point in listing the same object twice without - // that additional context. - seen := map[string]struct{}{} - - // Pretend that we've already "seen" all of our own addresses so that we - // won't record self-references in the state. This can arise if, for - // example, a provisioner for a resource refers to the resource itself, - // which is valid (since provisioners always run after apply) but should - // not create an explicit dependency edge. - for _, selfAddr := range selfAddrs { - seen[selfAddr.String()] = struct{}{} - if riAddr, ok := selfAddr.(addrs.ResourceInstance); ok { - seen[riAddr.ContainingResource().String()] = struct{}{} - } - } - - depsRaw := n.References() - deps := make([]addrs.Referenceable, 0, len(depsRaw)) - for _, d := range depsRaw { - subj := d.Subject - if mco, isOutput := subj.(addrs.ModuleCallOutput); isOutput { - // For state dependencies, we simplify outputs to just refer - // to the module as a whole. It's not really clear why we do this, - // but this logic is preserved from before the 0.12 rewrite of - // this function. - subj = mco.Call - } - - k := subj.String() - if _, exists := seen[k]; exists { - continue - } - seen[k] = struct{}{} - switch tr := subj.(type) { - case addrs.ResourceInstance: - deps = append(deps, tr) - case addrs.Resource: - deps = append(deps, tr) - case addrs.ModuleCallInstance: - deps = append(deps, tr) - default: - // No other reference types are recorded in the state. - } - } - - // We'll also sort them, since that'll avoid creating changes in the - // serialized state that make no semantic difference. - sort.Slice(deps, func(i, j int) bool { - // Simple string-based sort because we just care about consistency, - // not user-friendliness. - return deps[i].String() < deps[j].String() - }) - - return deps -} - -func (n *NodeAbstractResource) SetProvider(p addrs.AbsProviderConfig) { - n.ResolvedProvider = p -} - -// GraphNodeProviderConsumer -func (n *NodeAbstractResource) ProvidedBy() (addrs.AbsProviderConfig, bool) { - // If we have a config we prefer that above all else - if n.Config != nil { - relAddr := n.Config.ProviderConfigAddr() - return relAddr.Absolute(n.Path()), false - } - - // Use our type and containing module path to guess a provider configuration address - return n.Addr.Resource.DefaultProviderConfig().Absolute(n.Addr.Module), false -} - -// GraphNodeProviderConsumer -func (n *NodeAbstractResourceInstance) ProvidedBy() (addrs.AbsProviderConfig, bool) { - // If we have a config we prefer that above all else - if n.Config != nil { - relAddr := n.Config.ProviderConfigAddr() - return relAddr.Absolute(n.Path()), false - } - - // If we have state, then we will use the provider from there - if n.ResourceState != nil { - // An address from the state must match exactly, since we must ensure - // we refresh/destroy a resource with the same provider configuration - // that created it. - return n.ResourceState.ProviderConfig, true - } - - // Use our type and containing module path to guess a provider configuration address - return n.Addr.Resource.DefaultProviderConfig().Absolute(n.Path()), false -} - -// GraphNodeProvisionerConsumer -func (n *NodeAbstractResource) ProvisionedBy() []string { - // If we have no configuration, then we have no provisioners - if n.Config == nil || n.Config.Managed == nil { - return nil - } - - // Build the list of provisioners we need based on the configuration. - // It is okay to have duplicates here. - result := make([]string, len(n.Config.Managed.Provisioners)) - for i, p := range n.Config.Managed.Provisioners { - result[i] = p.Type - } - - return result -} - -// GraphNodeProvisionerConsumer -func (n *NodeAbstractResource) AttachProvisionerSchema(name string, schema *configschema.Block) { - if n.ProvisionerSchemas == nil { - n.ProvisionerSchemas = make(map[string]*configschema.Block) - } - n.ProvisionerSchemas[name] = schema -} - -// GraphNodeResource -func (n *NodeAbstractResource) ResourceAddr() addrs.AbsResource { - return n.Addr -} - -// GraphNodeResourceInstance -func (n *NodeAbstractResourceInstance) ResourceInstanceAddr() addrs.AbsResourceInstance { - return n.NodeAbstractResource.Addr.Instance(n.InstanceKey) -} - -// GraphNodeAddressable, TODO: remove, used by target, should unify -func (n *NodeAbstractResource) ResourceAddress() *ResourceAddress { - return NewLegacyResourceAddress(n.Addr) -} - -// GraphNodeTargetable -func (n *NodeAbstractResource) SetTargets(targets []addrs.Targetable) { - n.Targets = targets -} - -// GraphNodeAttachResourceState -func (n *NodeAbstractResourceInstance) AttachResourceState(s *states.Resource) { - n.ResourceState = s -} - -// GraphNodeAttachResourceConfig -func (n *NodeAbstractResource) AttachResourceConfig(c *configs.Resource) { - n.Config = c -} - -// GraphNodeAttachResourceSchema impl -func (n *NodeAbstractResource) AttachResourceSchema(schema *configschema.Block, version uint64) { - n.Schema = schema - n.SchemaVersion = version -} - -// GraphNodeDotter impl. -func (n *NodeAbstractResource) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { - return &dag.DotNode{ - Name: name, - Attrs: map[string]string{ - "label": n.Name(), - "shape": "box", - }, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_apply.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_apply.go deleted file mode 100644 index 68d438d7..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_apply.go +++ /dev/null @@ -1,71 +0,0 @@ -package terraform - -import ( - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/lang" -) - -// NodeApplyableResource represents a resource that is "applyable": -// it may need to have its record in the state adjusted to match configuration. -// -// Unlike in the plan walk, this resource node does not DynamicExpand. Instead, -// it should be inserted into the same graph as any instances of the nodes -// with dependency edges ensuring that the resource is evaluated before any -// of its instances, which will turn ensure that the whole-resource record -// in the state is suitably prepared to receive any updates to instances. -type NodeApplyableResource struct { - *NodeAbstractResource -} - -var ( - _ GraphNodeResource = (*NodeApplyableResource)(nil) - _ GraphNodeEvalable = (*NodeApplyableResource)(nil) - _ GraphNodeProviderConsumer = (*NodeApplyableResource)(nil) - _ GraphNodeAttachResourceConfig = (*NodeApplyableResource)(nil) - _ GraphNodeReferencer = (*NodeApplyableResource)(nil) -) - -func (n *NodeApplyableResource) Name() string { - return n.NodeAbstractResource.Name() + " (prepare state)" -} - -func (n *NodeApplyableResource) References() []*addrs.Reference { - if n.Config == nil { - log.Printf("[WARN] NodeApplyableResource %q: no configuration, so can't determine References", dag.VertexName(n)) - return nil - } - - var result []*addrs.Reference - - // Since this node type only updates resource-level metadata, we only - // need to worry about the parts of the configuration that affect - // our "each mode": the count and for_each meta-arguments. - refs, _ := lang.ReferencesInExpr(n.Config.Count) - result = append(result, refs...) - refs, _ = lang.ReferencesInExpr(n.Config.ForEach) - result = append(result, refs...) - - return result -} - -// GraphNodeEvalable -func (n *NodeApplyableResource) EvalTree() EvalNode { - addr := n.ResourceAddr() - config := n.Config - providerAddr := n.ResolvedProvider - - if config == nil { - // Nothing to do, then. - log.Printf("[TRACE] NodeApplyableResource: no configuration present for %s", addr) - return &EvalNoop{} - } - - return &EvalWriteResourceState{ - Addr: addr.Resource, - Config: config, - ProviderAddr: providerAddr, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_apply_instance.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_apply_instance.go deleted file mode 100644 index acdda45e..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_apply_instance.go +++ /dev/null @@ -1,426 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// NodeApplyableResourceInstance represents a resource instance that is -// "applyable": it is ready to be applied and is represented by a diff. -// -// This node is for a specific instance of a resource. It will usually be -// accompanied in the graph by a NodeApplyableResource representing its -// containing resource, and should depend on that node to ensure that the -// state is properly prepared to receive changes to instances. -type NodeApplyableResourceInstance struct { - *NodeAbstractResourceInstance - - destroyNode GraphNodeDestroyerCBD - graphNodeDeposer // implementation of GraphNodeDeposer -} - -var ( - _ GraphNodeResource = (*NodeApplyableResourceInstance)(nil) - _ GraphNodeResourceInstance = (*NodeApplyableResourceInstance)(nil) - _ GraphNodeCreator = (*NodeApplyableResourceInstance)(nil) - _ GraphNodeReferencer = (*NodeApplyableResourceInstance)(nil) - _ GraphNodeDeposer = (*NodeApplyableResourceInstance)(nil) - _ GraphNodeEvalable = (*NodeApplyableResourceInstance)(nil) -) - -// GraphNodeAttachDestroyer -func (n *NodeApplyableResourceInstance) AttachDestroyNode(d GraphNodeDestroyerCBD) { - n.destroyNode = d -} - -// createBeforeDestroy checks this nodes config status and the status af any -// companion destroy node for CreateBeforeDestroy. -func (n *NodeApplyableResourceInstance) createBeforeDestroy() bool { - cbd := false - - if n.Config != nil && n.Config.Managed != nil { - cbd = n.Config.Managed.CreateBeforeDestroy - } - - if n.destroyNode != nil { - cbd = cbd || n.destroyNode.CreateBeforeDestroy() - } - - return cbd -} - -// GraphNodeCreator -func (n *NodeApplyableResourceInstance) CreateAddr() *addrs.AbsResourceInstance { - addr := n.ResourceInstanceAddr() - return &addr -} - -// GraphNodeReferencer, overriding NodeAbstractResourceInstance -func (n *NodeApplyableResourceInstance) References() []*addrs.Reference { - // Start with the usual resource instance implementation - ret := n.NodeAbstractResourceInstance.References() - - // Applying a resource must also depend on the destruction of any of its - // dependencies, since this may for example affect the outcome of - // evaluating an entire list of resources with "count" set (by reducing - // the count). - // - // However, we can't do this in create_before_destroy mode because that - // would create a dependency cycle. We make a compromise here of requiring - // changes to be updated across two applies in this case, since the first - // plan will use the old values. - if !n.createBeforeDestroy() { - for _, ref := range ret { - switch tr := ref.Subject.(type) { - case addrs.ResourceInstance: - newRef := *ref // shallow copy so we can mutate - newRef.Subject = tr.Phase(addrs.ResourceInstancePhaseDestroy) - newRef.Remaining = nil // can't access attributes of something being destroyed - ret = append(ret, &newRef) - case addrs.Resource: - newRef := *ref // shallow copy so we can mutate - newRef.Subject = tr.Phase(addrs.ResourceInstancePhaseDestroy) - newRef.Remaining = nil // can't access attributes of something being destroyed - ret = append(ret, &newRef) - } - } - } - - return ret -} - -// GraphNodeEvalable -func (n *NodeApplyableResourceInstance) EvalTree() EvalNode { - addr := n.ResourceInstanceAddr() - - if n.Config == nil { - // This should not be possible, but we've got here in at least one - // case as discussed in the following issue: - // https://github.com/hashicorp/terraform-plugin-sdk/issues/21258 - // To avoid an outright crash here, we'll instead return an explicit - // error. - var diags tfdiags.Diagnostics - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Resource node has no configuration attached", - fmt.Sprintf( - "The graph node for %s has no configuration attached to it. This suggests a bug in Terraform's apply graph builder; please report it!", - addr, - ), - )) - err := diags.Err() - return &EvalReturnError{ - Error: &err, - } - } - - // Eval info is different depending on what kind of resource this is - switch n.Config.Mode { - case addrs.ManagedResourceMode: - return n.evalTreeManagedResource(addr) - case addrs.DataResourceMode: - return n.evalTreeDataResource(addr) - default: - panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode)) - } -} - -func (n *NodeApplyableResourceInstance) evalTreeDataResource(addr addrs.AbsResourceInstance) EvalNode { - var provider providers.Interface - var providerSchema *ProviderSchema - var change *plans.ResourceInstanceChange - var state *states.ResourceInstanceObject - - return &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: n.ResolvedProvider, - Output: &provider, - Schema: &providerSchema, - }, - - // Get the saved diff for apply - &EvalReadDiff{ - Addr: addr.Resource, - ProviderSchema: &providerSchema, - Change: &change, - }, - - // Stop early if we don't actually have a diff - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - if change == nil { - return true, EvalEarlyExitError{} - } - return true, nil - }, - Then: EvalNoop{}, - }, - - // In this particular call to EvalReadData we include our planned - // change, which signals that we expect this read to complete fully - // with no unknown values; it'll produce an error if not. - &EvalReadData{ - Addr: addr.Resource, - Config: n.Config, - Dependencies: n.StateReferences(), - Planned: &change, // setting this indicates that the result must be complete - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - OutputState: &state, - }, - - &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - }, - - // Clear the diff now that we've applied it, so - // later nodes won't see a diff that's now a no-op. - &EvalWriteDiff{ - Addr: addr.Resource, - ProviderSchema: &providerSchema, - Change: nil, - }, - - &EvalUpdateStateHook{}, - }, - } -} - -func (n *NodeApplyableResourceInstance) evalTreeManagedResource(addr addrs.AbsResourceInstance) EvalNode { - // Declare a bunch of variables that are used for state during - // evaluation. Most of this are written to by-address below. - var provider providers.Interface - var providerSchema *ProviderSchema - var diff, diffApply *plans.ResourceInstanceChange - var state *states.ResourceInstanceObject - var err error - var createNew bool - var createBeforeDestroyEnabled bool - var configVal cty.Value - var deposedKey states.DeposedKey - - return &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: n.ResolvedProvider, - Output: &provider, - Schema: &providerSchema, - }, - - // Get the saved diff for apply - &EvalReadDiff{ - Addr: addr.Resource, - ProviderSchema: &providerSchema, - Change: &diffApply, - }, - - // We don't want to do any destroys - // (these are handled by NodeDestroyResourceInstance instead) - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - if diffApply == nil { - return true, EvalEarlyExitError{} - } - if diffApply.Action == plans.Delete { - return true, EvalEarlyExitError{} - } - return true, nil - }, - Then: EvalNoop{}, - }, - - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - destroy := false - if diffApply != nil { - destroy = (diffApply.Action == plans.Delete || diffApply.Action.IsReplace()) - } - if destroy && n.createBeforeDestroy() { - createBeforeDestroyEnabled = true - } - return createBeforeDestroyEnabled, nil - }, - Then: &EvalDeposeState{ - Addr: addr.Resource, - ForceKey: n.PreallocatedDeposedKey, - OutputKey: &deposedKey, - }, - }, - - &EvalReadState{ - Addr: addr.Resource, - Provider: &provider, - ProviderSchema: &providerSchema, - - Output: &state, - }, - - // Get the saved diff - &EvalReadDiff{ - Addr: addr.Resource, - ProviderSchema: &providerSchema, - Change: &diff, - }, - - // Make a new diff, in case we've learned new values in the state - // during apply which we can now incorporate. - &EvalDiff{ - Addr: addr.Resource, - Config: n.Config, - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - PreviousDiff: &diff, - OutputChange: &diffApply, - OutputValue: &configVal, - OutputState: &state, - }, - - // Compare the diffs - &EvalCheckPlannedChange{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - Planned: &diff, - Actual: &diffApply, - }, - - &EvalGetProvider{ - Addr: n.ResolvedProvider, - Output: &provider, - Schema: &providerSchema, - }, - &EvalReadState{ - Addr: addr.Resource, - Provider: &provider, - ProviderSchema: &providerSchema, - - Output: &state, - }, - - &EvalReduceDiff{ - Addr: addr.Resource, - InChange: &diffApply, - Destroy: false, - OutChange: &diffApply, - }, - - // EvalReduceDiff may have simplified our planned change - // into a NoOp if it only requires destroying, since destroying - // is handled by NodeDestroyResourceInstance. - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - if diffApply == nil || diffApply.Action == plans.NoOp { - return true, EvalEarlyExitError{} - } - return true, nil - }, - Then: EvalNoop{}, - }, - - // Call pre-apply hook - &EvalApplyPre{ - Addr: addr.Resource, - State: &state, - Change: &diffApply, - }, - &EvalApply{ - Addr: addr.Resource, - Config: n.Config, - Dependencies: n.StateReferences(), - State: &state, - Change: &diffApply, - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - Output: &state, - Error: &err, - CreateNew: &createNew, - }, - &EvalMaybeTainted{ - Addr: addr.Resource, - State: &state, - Change: &diffApply, - Error: &err, - StateOutput: &state, - }, - &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - }, - &EvalApplyProvisioners{ - Addr: addr.Resource, - State: &state, // EvalApplyProvisioners will skip if already tainted - ResourceConfig: n.Config, - CreateNew: &createNew, - Error: &err, - When: configs.ProvisionerWhenCreate, - }, - &EvalMaybeTainted{ - Addr: addr.Resource, - State: &state, - Change: &diffApply, - Error: &err, - StateOutput: &state, - }, - &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - }, - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - return createBeforeDestroyEnabled && err != nil, nil - }, - Then: &EvalMaybeRestoreDeposedObject{ - Addr: addr.Resource, - Key: &deposedKey, - }, - }, - - // We clear the diff out here so that future nodes - // don't see a diff that is already complete. There - // is no longer a diff! - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - if !diff.Action.IsReplace() { - return true, nil - } - if !n.createBeforeDestroy() { - return true, nil - } - return false, nil - }, - Then: &EvalWriteDiff{ - Addr: addr.Resource, - ProviderSchema: &providerSchema, - Change: nil, - }, - }, - - &EvalApplyPost{ - Addr: addr.Resource, - State: &state, - Error: &err, - }, - &EvalUpdateStateHook{}, - }, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_destroy.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_destroy.go deleted file mode 100644 index 049e5e99..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_destroy.go +++ /dev/null @@ -1,321 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// NodeDestroyResourceInstance represents a resource instance that is to be -// destroyed. -type NodeDestroyResourceInstance struct { - *NodeAbstractResourceInstance - - // If DeposedKey is set to anything other than states.NotDeposed then - // this node destroys a deposed object of the associated instance - // rather than its current object. - DeposedKey states.DeposedKey - - CreateBeforeDestroyOverride *bool -} - -var ( - _ GraphNodeResource = (*NodeDestroyResourceInstance)(nil) - _ GraphNodeResourceInstance = (*NodeDestroyResourceInstance)(nil) - _ GraphNodeDestroyer = (*NodeDestroyResourceInstance)(nil) - _ GraphNodeDestroyerCBD = (*NodeDestroyResourceInstance)(nil) - _ GraphNodeReferenceable = (*NodeDestroyResourceInstance)(nil) - _ GraphNodeReferencer = (*NodeDestroyResourceInstance)(nil) - _ GraphNodeEvalable = (*NodeDestroyResourceInstance)(nil) - _ GraphNodeProviderConsumer = (*NodeDestroyResourceInstance)(nil) - _ GraphNodeProvisionerConsumer = (*NodeDestroyResourceInstance)(nil) -) - -func (n *NodeDestroyResourceInstance) Name() string { - if n.DeposedKey != states.NotDeposed { - return fmt.Sprintf("%s (destroy deposed %s)", n.ResourceInstanceAddr(), n.DeposedKey) - } - return n.ResourceInstanceAddr().String() + " (destroy)" -} - -// GraphNodeDestroyer -func (n *NodeDestroyResourceInstance) DestroyAddr() *addrs.AbsResourceInstance { - addr := n.ResourceInstanceAddr() - return &addr -} - -// GraphNodeDestroyerCBD -func (n *NodeDestroyResourceInstance) CreateBeforeDestroy() bool { - if n.CreateBeforeDestroyOverride != nil { - return *n.CreateBeforeDestroyOverride - } - - // If we have no config, we just assume no - if n.Config == nil || n.Config.Managed == nil { - return false - } - - return n.Config.Managed.CreateBeforeDestroy -} - -// GraphNodeDestroyerCBD -func (n *NodeDestroyResourceInstance) ModifyCreateBeforeDestroy(v bool) error { - n.CreateBeforeDestroyOverride = &v - return nil -} - -// GraphNodeReferenceable, overriding NodeAbstractResource -func (n *NodeDestroyResourceInstance) ReferenceableAddrs() []addrs.Referenceable { - normalAddrs := n.NodeAbstractResourceInstance.ReferenceableAddrs() - destroyAddrs := make([]addrs.Referenceable, len(normalAddrs)) - - phaseType := addrs.ResourceInstancePhaseDestroy - if n.CreateBeforeDestroy() { - phaseType = addrs.ResourceInstancePhaseDestroyCBD - } - - for i, normalAddr := range normalAddrs { - switch ta := normalAddr.(type) { - case addrs.Resource: - destroyAddrs[i] = ta.Phase(phaseType) - case addrs.ResourceInstance: - destroyAddrs[i] = ta.Phase(phaseType) - default: - destroyAddrs[i] = normalAddr - } - } - - return destroyAddrs -} - -// GraphNodeReferencer, overriding NodeAbstractResource -func (n *NodeDestroyResourceInstance) References() []*addrs.Reference { - // If we have a config, then we need to include destroy-time dependencies - if c := n.Config; c != nil && c.Managed != nil { - var result []*addrs.Reference - - // We include conn info and config for destroy time provisioners - // as dependencies that we have. - for _, p := range c.Managed.Provisioners { - schema := n.ProvisionerSchemas[p.Type] - - if p.When == configs.ProvisionerWhenDestroy { - if p.Connection != nil { - result = append(result, ReferencesFromConfig(p.Connection.Config, connectionBlockSupersetSchema)...) - } - result = append(result, ReferencesFromConfig(p.Config, schema)...) - } - } - - return result - } - - return nil -} - -// GraphNodeEvalable -func (n *NodeDestroyResourceInstance) EvalTree() EvalNode { - addr := n.ResourceInstanceAddr() - - // Get our state - rs := n.ResourceState - var is *states.ResourceInstance - if rs != nil { - is = rs.Instance(n.InstanceKey) - } - if is == nil { - log.Printf("[WARN] NodeDestroyResourceInstance for %s with no state", addr) - } - - var changeApply *plans.ResourceInstanceChange - var provider providers.Interface - var providerSchema *ProviderSchema - var state *states.ResourceInstanceObject - var err error - return &EvalOpFilter{ - Ops: []walkOperation{walkApply, walkDestroy}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: n.ResolvedProvider, - Output: &provider, - Schema: &providerSchema, - }, - - // Get the saved diff for apply - &EvalReadDiff{ - Addr: addr.Resource, - ProviderSchema: &providerSchema, - Change: &changeApply, - }, - - &EvalReduceDiff{ - Addr: addr.Resource, - InChange: &changeApply, - Destroy: true, - OutChange: &changeApply, - }, - - // EvalReduceDiff may have simplified our planned change - // into a NoOp if it does not require destroying. - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - if changeApply == nil || changeApply.Action == plans.NoOp { - return true, EvalEarlyExitError{} - } - return true, nil - }, - Then: EvalNoop{}, - }, - - &EvalReadState{ - Addr: addr.Resource, - Output: &state, - Provider: &provider, - ProviderSchema: &providerSchema, - }, - &EvalRequireState{ - State: &state, - }, - - // Call pre-apply hook - &EvalApplyPre{ - Addr: addr.Resource, - State: &state, - Change: &changeApply, - }, - - // Run destroy provisioners if not tainted - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - if state != nil && state.Status == states.ObjectTainted { - return false, nil - } - - return true, nil - }, - - Then: &EvalApplyProvisioners{ - Addr: addr.Resource, - State: &state, - ResourceConfig: n.Config, - Error: &err, - When: configs.ProvisionerWhenDestroy, - }, - }, - - // If we have a provisioning error, then we just call - // the post-apply hook now. - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - return err != nil, nil - }, - - Then: &EvalApplyPost{ - Addr: addr.Resource, - State: &state, - Error: &err, - }, - }, - - // Make sure we handle data sources properly. - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - return addr.Resource.Resource.Mode == addrs.DataResourceMode, nil - }, - - Then: &EvalReadDataApply{ - Addr: addr.Resource, - Config: n.Config, - Change: &changeApply, - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - Output: &state, - }, - Else: &EvalApply{ - Addr: addr.Resource, - Config: nil, // No configuration because we are destroying - State: &state, - Change: &changeApply, - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - Output: &state, - Error: &err, - }, - }, - &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - }, - &EvalApplyPost{ - Addr: addr.Resource, - State: &state, - Error: &err, - }, - &EvalUpdateStateHook{}, - }, - }, - } -} - -// NodeDestroyResourceInstance represents a resource that is to be destroyed. -// -// Destroying a resource is a state-only operation: it is the individual -// instances being destroyed that affects remote objects. During graph -// construction, NodeDestroyResource should always depend on any other node -// related to the given resource, since it's just a final cleanup to avoid -// leaving skeleton resource objects in state after their instances have -// all been destroyed. -type NodeDestroyResource struct { - *NodeAbstractResource -} - -var ( - _ GraphNodeResource = (*NodeDestroyResource)(nil) - _ GraphNodeReferenceable = (*NodeDestroyResource)(nil) - _ GraphNodeReferencer = (*NodeDestroyResource)(nil) - _ GraphNodeEvalable = (*NodeDestroyResource)(nil) -) - -func (n *NodeDestroyResource) Name() string { - return n.ResourceAddr().String() + " (clean up state)" -} - -// GraphNodeReferenceable, overriding NodeAbstractResource -func (n *NodeDestroyResource) ReferenceableAddrs() []addrs.Referenceable { - // NodeDestroyResource doesn't participate in references: the graph - // builder that created it should ensure directly that it already depends - // on every other node related to its resource, without relying on - // references. - return nil -} - -// GraphNodeReferencer, overriding NodeAbstractResource -func (n *NodeDestroyResource) References() []*addrs.Reference { - // NodeDestroyResource doesn't participate in references: the graph - // builder that created it should ensure directly that it already depends - // on every other node related to its resource, without relying on - // references. - return nil -} - -// GraphNodeEvalable -func (n *NodeDestroyResource) EvalTree() EvalNode { - // This EvalNode will produce an error if the resource isn't already - // empty by the time it is called, since it should just be pruning the - // leftover husk of a resource in state after all of the child instances - // and their objects were destroyed. - return &EvalForgetResourceState{ - Addr: n.ResourceAddr().Resource, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_destroy_deposed.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_destroy_deposed.go deleted file mode 100644 index 269c7980..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_destroy_deposed.go +++ /dev/null @@ -1,313 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// ConcreteResourceInstanceDeposedNodeFunc is a callback type used to convert -// an abstract resource instance to a concrete one of some type that has -// an associated deposed object key. -type ConcreteResourceInstanceDeposedNodeFunc func(*NodeAbstractResourceInstance, states.DeposedKey) dag.Vertex - -type GraphNodeDeposedResourceInstanceObject interface { - DeposedInstanceObjectKey() states.DeposedKey -} - -// NodePlanDeposedResourceInstanceObject represents deposed resource -// instance objects during plan. These are distinct from the primary object -// for each resource instance since the only valid operation to do with them -// is to destroy them. -// -// This node type is also used during the refresh walk to ensure that the -// record of a deposed object is up-to-date before we plan to destroy it. -type NodePlanDeposedResourceInstanceObject struct { - *NodeAbstractResourceInstance - DeposedKey states.DeposedKey -} - -var ( - _ GraphNodeDeposedResourceInstanceObject = (*NodePlanDeposedResourceInstanceObject)(nil) - _ GraphNodeResource = (*NodePlanDeposedResourceInstanceObject)(nil) - _ GraphNodeResourceInstance = (*NodePlanDeposedResourceInstanceObject)(nil) - _ GraphNodeReferenceable = (*NodePlanDeposedResourceInstanceObject)(nil) - _ GraphNodeReferencer = (*NodePlanDeposedResourceInstanceObject)(nil) - _ GraphNodeEvalable = (*NodePlanDeposedResourceInstanceObject)(nil) - _ GraphNodeProviderConsumer = (*NodePlanDeposedResourceInstanceObject)(nil) - _ GraphNodeProvisionerConsumer = (*NodePlanDeposedResourceInstanceObject)(nil) -) - -func (n *NodePlanDeposedResourceInstanceObject) Name() string { - return fmt.Sprintf("%s (deposed %s)", n.ResourceInstanceAddr().String(), n.DeposedKey) -} - -func (n *NodePlanDeposedResourceInstanceObject) DeposedInstanceObjectKey() states.DeposedKey { - return n.DeposedKey -} - -// GraphNodeReferenceable implementation, overriding the one from NodeAbstractResourceInstance -func (n *NodePlanDeposedResourceInstanceObject) ReferenceableAddrs() []addrs.Referenceable { - // Deposed objects don't participate in references. - return nil -} - -// GraphNodeReferencer implementation, overriding the one from NodeAbstractResourceInstance -func (n *NodePlanDeposedResourceInstanceObject) References() []*addrs.Reference { - // We don't evaluate configuration for deposed objects, so they effectively - // make no references. - return nil -} - -// GraphNodeEvalable impl. -func (n *NodePlanDeposedResourceInstanceObject) EvalTree() EvalNode { - addr := n.ResourceInstanceAddr() - - var provider providers.Interface - var providerSchema *ProviderSchema - var state *states.ResourceInstanceObject - - seq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)} - - // During the refresh walk we will ensure that our record of the deposed - // object is up-to-date. If it was already deleted outside of Terraform - // then this will remove it from state and thus avoid us planning a - // destroy for it during the subsequent plan walk. - seq.Nodes = append(seq.Nodes, &EvalOpFilter{ - Ops: []walkOperation{walkRefresh}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: n.ResolvedProvider, - Output: &provider, - Schema: &providerSchema, - }, - &EvalReadStateDeposed{ - Addr: addr.Resource, - Provider: &provider, - ProviderSchema: &providerSchema, - Key: n.DeposedKey, - Output: &state, - }, - &EvalRefresh{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - Provider: &provider, - ProviderSchema: &providerSchema, - State: &state, - Output: &state, - }, - &EvalWriteStateDeposed{ - Addr: addr.Resource, - Key: n.DeposedKey, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - }, - }, - }, - }) - - // During the plan walk we always produce a planned destroy change, because - // destroying is the only supported action for deposed objects. - var change *plans.ResourceInstanceChange - seq.Nodes = append(seq.Nodes, &EvalOpFilter{ - Ops: []walkOperation{walkPlan, walkPlanDestroy}, - Node: &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: n.ResolvedProvider, - Output: &provider, - Schema: &providerSchema, - }, - &EvalReadStateDeposed{ - Addr: addr.Resource, - Output: &state, - Key: n.DeposedKey, - Provider: &provider, - ProviderSchema: &providerSchema, - }, - &EvalDiffDestroy{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - DeposedKey: n.DeposedKey, - State: &state, - Output: &change, - }, - &EvalWriteDiff{ - Addr: addr.Resource, - DeposedKey: n.DeposedKey, - ProviderSchema: &providerSchema, - Change: &change, - }, - // Since deposed objects cannot be referenced by expressions - // elsewhere, we don't need to also record the planned new - // state in this case. - }, - }, - }) - - return seq -} - -// NodeDestroyDeposedResourceInstanceObject represents deposed resource -// instance objects during apply. Nodes of this type are inserted by -// DiffTransformer when the planned changeset contains "delete" changes for -// deposed instance objects, and its only supported operation is to destroy -// and then forget the associated object. -type NodeDestroyDeposedResourceInstanceObject struct { - *NodeAbstractResourceInstance - DeposedKey states.DeposedKey -} - -var ( - _ GraphNodeDeposedResourceInstanceObject = (*NodeDestroyDeposedResourceInstanceObject)(nil) - _ GraphNodeResource = (*NodeDestroyDeposedResourceInstanceObject)(nil) - _ GraphNodeResourceInstance = (*NodeDestroyDeposedResourceInstanceObject)(nil) - _ GraphNodeDestroyer = (*NodeDestroyDeposedResourceInstanceObject)(nil) - _ GraphNodeDestroyerCBD = (*NodeDestroyDeposedResourceInstanceObject)(nil) - _ GraphNodeReferenceable = (*NodeDestroyDeposedResourceInstanceObject)(nil) - _ GraphNodeReferencer = (*NodeDestroyDeposedResourceInstanceObject)(nil) - _ GraphNodeEvalable = (*NodeDestroyDeposedResourceInstanceObject)(nil) - _ GraphNodeProviderConsumer = (*NodeDestroyDeposedResourceInstanceObject)(nil) - _ GraphNodeProvisionerConsumer = (*NodeDestroyDeposedResourceInstanceObject)(nil) -) - -func (n *NodeDestroyDeposedResourceInstanceObject) Name() string { - return fmt.Sprintf("%s (destroy deposed %s)", n.Addr.String(), n.DeposedKey) -} - -func (n *NodeDestroyDeposedResourceInstanceObject) DeposedInstanceObjectKey() states.DeposedKey { - return n.DeposedKey -} - -// GraphNodeReferenceable implementation, overriding the one from NodeAbstractResourceInstance -func (n *NodeDestroyDeposedResourceInstanceObject) ReferenceableAddrs() []addrs.Referenceable { - // Deposed objects don't participate in references. - return nil -} - -// GraphNodeReferencer implementation, overriding the one from NodeAbstractResourceInstance -func (n *NodeDestroyDeposedResourceInstanceObject) References() []*addrs.Reference { - // We don't evaluate configuration for deposed objects, so they effectively - // make no references. - return nil -} - -// GraphNodeDestroyer -func (n *NodeDestroyDeposedResourceInstanceObject) DestroyAddr() *addrs.AbsResourceInstance { - addr := n.ResourceInstanceAddr() - return &addr -} - -// GraphNodeDestroyerCBD -func (n *NodeDestroyDeposedResourceInstanceObject) CreateBeforeDestroy() bool { - // A deposed instance is always CreateBeforeDestroy by definition, since - // we use deposed only to handle create-before-destroy. - return true -} - -// GraphNodeDestroyerCBD -func (n *NodeDestroyDeposedResourceInstanceObject) ModifyCreateBeforeDestroy(v bool) error { - if !v { - // Should never happen: deposed instances are _always_ create_before_destroy. - return fmt.Errorf("can't deactivate create_before_destroy for a deposed instance") - } - return nil -} - -// GraphNodeEvalable impl. -func (n *NodeDestroyDeposedResourceInstanceObject) EvalTree() EvalNode { - addr := n.ResourceInstanceAddr() - - var provider providers.Interface - var providerSchema *ProviderSchema - var state *states.ResourceInstanceObject - var change *plans.ResourceInstanceChange - var err error - - return &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: n.ResolvedProvider, - Output: &provider, - Schema: &providerSchema, - }, - &EvalReadStateDeposed{ - Addr: addr.Resource, - Output: &state, - Key: n.DeposedKey, - Provider: &provider, - ProviderSchema: &providerSchema, - }, - &EvalDiffDestroy{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - State: &state, - Output: &change, - }, - // Call pre-apply hook - &EvalApplyPre{ - Addr: addr.Resource, - State: &state, - Change: &change, - }, - &EvalApply{ - Addr: addr.Resource, - Config: nil, // No configuration because we are destroying - State: &state, - Change: &change, - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - Output: &state, - Error: &err, - }, - // Always write the resource back to the state deposed... if it - // was successfully destroyed it will be pruned. If it was not, it will - // be caught on the next run. - &EvalWriteStateDeposed{ - Addr: addr.Resource, - Key: n.DeposedKey, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - }, - &EvalApplyPost{ - Addr: addr.Resource, - State: &state, - Error: &err, - }, - &EvalReturnError{ - Error: &err, - }, - &EvalUpdateStateHook{}, - }, - } -} - -// GraphNodeDeposer is an optional interface implemented by graph nodes that -// might create a single new deposed object for a specific associated resource -// instance, allowing a caller to optionally pre-allocate a DeposedKey for -// it. -type GraphNodeDeposer interface { - // SetPreallocatedDeposedKey will be called during graph construction - // if a particular node must use a pre-allocated deposed key if/when it - // "deposes" the current object of its associated resource instance. - SetPreallocatedDeposedKey(key states.DeposedKey) -} - -// graphNodeDeposer is an embeddable implementation of GraphNodeDeposer. -// Embed it in a node type to get automatic support for it, and then access -// the field PreallocatedDeposedKey to access any pre-allocated key. -type graphNodeDeposer struct { - PreallocatedDeposedKey states.DeposedKey -} - -func (n *graphNodeDeposer) SetPreallocatedDeposedKey(key states.DeposedKey) { - n.PreallocatedDeposedKey = key -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_plan.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_plan.go deleted file mode 100644 index 2dc0df90..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_plan.go +++ /dev/null @@ -1,166 +0,0 @@ -package terraform - -import ( - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// NodePlannableResource represents a resource that is "plannable": -// it is ready to be planned in order to create a diff. -type NodePlannableResource struct { - *NodeAbstractResource - - // ForceCreateBeforeDestroy might be set via our GraphNodeDestroyerCBD - // during graph construction, if dependencies require us to force this - // on regardless of what the configuration says. - ForceCreateBeforeDestroy *bool -} - -var ( - _ GraphNodeSubPath = (*NodePlannableResource)(nil) - _ GraphNodeDestroyerCBD = (*NodePlannableResource)(nil) - _ GraphNodeDynamicExpandable = (*NodePlannableResource)(nil) - _ GraphNodeReferenceable = (*NodePlannableResource)(nil) - _ GraphNodeReferencer = (*NodePlannableResource)(nil) - _ GraphNodeResource = (*NodePlannableResource)(nil) - _ GraphNodeAttachResourceConfig = (*NodePlannableResource)(nil) -) - -// GraphNodeEvalable -func (n *NodePlannableResource) EvalTree() EvalNode { - addr := n.ResourceAddr() - config := n.Config - - if config == nil { - // Nothing to do, then. - log.Printf("[TRACE] NodeApplyableResource: no configuration present for %s", addr) - return &EvalNoop{} - } - - // this ensures we can reference the resource even if the count is 0 - return &EvalWriteResourceState{ - Addr: addr.Resource, - Config: config, - ProviderAddr: n.ResolvedProvider, - } -} - -// GraphNodeDestroyerCBD -func (n *NodePlannableResource) CreateBeforeDestroy() bool { - if n.ForceCreateBeforeDestroy != nil { - return *n.ForceCreateBeforeDestroy - } - - // If we have no config, we just assume no - if n.Config == nil || n.Config.Managed == nil { - return false - } - - return n.Config.Managed.CreateBeforeDestroy -} - -// GraphNodeDestroyerCBD -func (n *NodePlannableResource) ModifyCreateBeforeDestroy(v bool) error { - n.ForceCreateBeforeDestroy = &v - return nil -} - -// GraphNodeDynamicExpandable -func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) { - var diags tfdiags.Diagnostics - - count, countDiags := evaluateResourceCountExpression(n.Config.Count, ctx) - diags = diags.Append(countDiags) - if countDiags.HasErrors() { - return nil, diags.Err() - } - - forEachMap, forEachDiags := evaluateResourceForEachExpression(n.Config.ForEach, ctx) - if forEachDiags.HasErrors() { - return nil, diags.Err() - } - - // Next we need to potentially rename an instance address in the state - // if we're transitioning whether "count" is set at all. - fixResourceCountSetTransition(ctx, n.ResourceAddr(), count != -1) - - // Our graph transformers require access to the full state, so we'll - // temporarily lock it while we work on this. - state := ctx.State().Lock() - defer ctx.State().Unlock() - - // The concrete resource factory we'll use - concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex { - // Add the config and state since we don't do that via transforms - a.Config = n.Config - a.ResolvedProvider = n.ResolvedProvider - a.Schema = n.Schema - a.ProvisionerSchemas = n.ProvisionerSchemas - - return &NodePlannableResourceInstance{ - NodeAbstractResourceInstance: a, - - // By the time we're walking, we've figured out whether we need - // to force on CreateBeforeDestroy due to dependencies on other - // nodes that have it. - ForceCreateBeforeDestroy: n.CreateBeforeDestroy(), - } - } - - // The concrete resource factory we'll use for orphans - concreteResourceOrphan := func(a *NodeAbstractResourceInstance) dag.Vertex { - // Add the config and state since we don't do that via transforms - a.Config = n.Config - a.ResolvedProvider = n.ResolvedProvider - a.Schema = n.Schema - a.ProvisionerSchemas = n.ProvisionerSchemas - - return &NodePlannableResourceInstanceOrphan{ - NodeAbstractResourceInstance: a, - } - } - - // Start creating the steps - steps := []GraphTransformer{ - // Expand the count or for_each (if present) - &ResourceCountTransformer{ - Concrete: concreteResource, - Schema: n.Schema, - Count: count, - ForEach: forEachMap, - Addr: n.ResourceAddr(), - }, - - // Add the count/for_each orphans - &OrphanResourceCountTransformer{ - Concrete: concreteResourceOrphan, - Count: count, - ForEach: forEachMap, - Addr: n.ResourceAddr(), - State: state, - }, - - // Attach the state - &AttachStateTransformer{State: state}, - - // Targeting - &TargetsTransformer{Targets: n.Targets}, - - // Connect references so ordering is correct - &ReferenceTransformer{}, - - // Make sure there is a single root - &RootTransformer{}, - } - - // Build the graph - b := &BasicGraphBuilder{ - Steps: steps, - Validate: true, - Name: "NodePlannableResource", - } - graph, diags := b.Build(ctx.Path()) - return graph, diags.ErrWithWarnings() -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_plan_destroy.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_plan_destroy.go deleted file mode 100644 index 2c3a7012..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_plan_destroy.go +++ /dev/null @@ -1,88 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// NodePlanDestroyableResourceInstance represents a resource that is ready -// to be planned for destruction. -type NodePlanDestroyableResourceInstance struct { - *NodeAbstractResourceInstance -} - -var ( - _ GraphNodeSubPath = (*NodePlanDestroyableResourceInstance)(nil) - _ GraphNodeReferenceable = (*NodePlanDestroyableResourceInstance)(nil) - _ GraphNodeReferencer = (*NodePlanDestroyableResourceInstance)(nil) - _ GraphNodeDestroyer = (*NodePlanDestroyableResourceInstance)(nil) - _ GraphNodeResource = (*NodePlanDestroyableResourceInstance)(nil) - _ GraphNodeResourceInstance = (*NodePlanDestroyableResourceInstance)(nil) - _ GraphNodeAttachResourceConfig = (*NodePlanDestroyableResourceInstance)(nil) - _ GraphNodeAttachResourceState = (*NodePlanDestroyableResourceInstance)(nil) - _ GraphNodeEvalable = (*NodePlanDestroyableResourceInstance)(nil) - _ GraphNodeProviderConsumer = (*NodePlanDestroyableResourceInstance)(nil) -) - -// GraphNodeDestroyer -func (n *NodePlanDestroyableResourceInstance) DestroyAddr() *addrs.AbsResourceInstance { - addr := n.ResourceInstanceAddr() - return &addr -} - -// GraphNodeEvalable -func (n *NodePlanDestroyableResourceInstance) EvalTree() EvalNode { - addr := n.ResourceInstanceAddr() - - // Declare a bunch of variables that are used for state during - // evaluation. These are written to by address in the EvalNodes we - // declare below. - var provider providers.Interface - var providerSchema *ProviderSchema - var change *plans.ResourceInstanceChange - var state *states.ResourceInstanceObject - - if n.ResolvedProvider.ProviderConfig.Type == "" { - // Should never happen; indicates that the graph was not constructed - // correctly since we didn't get our provider attached. - panic(fmt.Sprintf("%T %q was not assigned a resolved provider", n, dag.VertexName(n))) - } - - return &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: n.ResolvedProvider, - Output: &provider, - Schema: &providerSchema, - }, - &EvalReadState{ - Addr: addr.Resource, - Provider: &provider, - ProviderSchema: &providerSchema, - - Output: &state, - }, - &EvalDiffDestroy{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - State: &state, - Output: &change, - }, - &EvalCheckPreventDestroy{ - Addr: addr.Resource, - Config: n.Config, - Change: &change, - }, - &EvalWriteDiff{ - Addr: addr.Resource, - ProviderSchema: &providerSchema, - Change: &change, - }, - }, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_plan_instance.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_plan_instance.go deleted file mode 100644 index ac4b24cf..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_plan_instance.go +++ /dev/null @@ -1,201 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/zclconf/go-cty/cty" -) - -// NodePlannableResourceInstance represents a _single_ resource -// instance that is plannable. This means this represents a single -// count index, for example. -type NodePlannableResourceInstance struct { - *NodeAbstractResourceInstance - ForceCreateBeforeDestroy bool -} - -var ( - _ GraphNodeSubPath = (*NodePlannableResourceInstance)(nil) - _ GraphNodeReferenceable = (*NodePlannableResourceInstance)(nil) - _ GraphNodeReferencer = (*NodePlannableResourceInstance)(nil) - _ GraphNodeResource = (*NodePlannableResourceInstance)(nil) - _ GraphNodeResourceInstance = (*NodePlannableResourceInstance)(nil) - _ GraphNodeAttachResourceConfig = (*NodePlannableResourceInstance)(nil) - _ GraphNodeAttachResourceState = (*NodePlannableResourceInstance)(nil) - _ GraphNodeEvalable = (*NodePlannableResourceInstance)(nil) -) - -// GraphNodeEvalable -func (n *NodePlannableResourceInstance) EvalTree() EvalNode { - addr := n.ResourceInstanceAddr() - - // Eval info is different depending on what kind of resource this is - switch addr.Resource.Resource.Mode { - case addrs.ManagedResourceMode: - return n.evalTreeManagedResource(addr) - case addrs.DataResourceMode: - return n.evalTreeDataResource(addr) - default: - panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode)) - } -} - -func (n *NodePlannableResourceInstance) evalTreeDataResource(addr addrs.AbsResourceInstance) EvalNode { - config := n.Config - var provider providers.Interface - var providerSchema *ProviderSchema - var change *plans.ResourceInstanceChange - var state *states.ResourceInstanceObject - var configVal cty.Value - - return &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: n.ResolvedProvider, - Output: &provider, - Schema: &providerSchema, - }, - - &EvalReadState{ - Addr: addr.Resource, - Provider: &provider, - ProviderSchema: &providerSchema, - - Output: &state, - }, - - // If we already have a non-planned state then we already dealt - // with this during the refresh walk and so we have nothing to do - // here. - &EvalIf{ - If: func(ctx EvalContext) (bool, error) { - depChanges := false - - // Check and see if any of our dependencies have changes. - changes := ctx.Changes() - for _, d := range n.StateReferences() { - ri, ok := d.(addrs.ResourceInstance) - if !ok { - continue - } - change := changes.GetResourceInstanceChange(ri.Absolute(ctx.Path()), states.CurrentGen) - if change != nil && change.Action != plans.NoOp { - depChanges = true - break - } - } - - refreshed := state != nil && state.Status != states.ObjectPlanned - - // If there are no dependency changes, and it's not a forced - // read because we there was no Refresh, then we don't need - // to re-read. If any dependencies have changes, it means - // our config may also have changes and we need to Read the - // data source again. - if !depChanges && refreshed { - return false, EvalEarlyExitError{} - } - return true, nil - }, - Then: EvalNoop{}, - }, - - &EvalValidateSelfRef{ - Addr: addr.Resource, - Config: config.Config, - ProviderSchema: &providerSchema, - }, - - &EvalReadData{ - Addr: addr.Resource, - Config: n.Config, - Dependencies: n.StateReferences(), - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - ForcePlanRead: true, // _always_ produce a Read change, even if the config seems ready - OutputChange: &change, - OutputValue: &configVal, - OutputState: &state, - }, - - &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - }, - - &EvalWriteDiff{ - Addr: addr.Resource, - ProviderSchema: &providerSchema, - Change: &change, - }, - }, - } -} - -func (n *NodePlannableResourceInstance) evalTreeManagedResource(addr addrs.AbsResourceInstance) EvalNode { - config := n.Config - var provider providers.Interface - var providerSchema *ProviderSchema - var change *plans.ResourceInstanceChange - var state *states.ResourceInstanceObject - - return &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: n.ResolvedProvider, - Output: &provider, - Schema: &providerSchema, - }, - - &EvalReadState{ - Addr: addr.Resource, - Provider: &provider, - ProviderSchema: &providerSchema, - - Output: &state, - }, - - &EvalValidateSelfRef{ - Addr: addr.Resource, - Config: config.Config, - ProviderSchema: &providerSchema, - }, - - &EvalDiff{ - Addr: addr.Resource, - Config: n.Config, - CreateBeforeDestroy: n.ForceCreateBeforeDestroy, - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - OutputChange: &change, - OutputState: &state, - }, - &EvalCheckPreventDestroy{ - Addr: addr.Resource, - Config: n.Config, - Change: &change, - }, - &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - State: &state, - ProviderSchema: &providerSchema, - }, - &EvalWriteDiff{ - Addr: addr.Resource, - ProviderSchema: &providerSchema, - Change: &change, - }, - }, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_plan_orphan.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_plan_orphan.go deleted file mode 100644 index 8e4f7148..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_plan_orphan.go +++ /dev/null @@ -1,84 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// NodePlannableResourceInstanceOrphan represents a resource that is "applyable": -// it is ready to be applied and is represented by a diff. -type NodePlannableResourceInstanceOrphan struct { - *NodeAbstractResourceInstance -} - -var ( - _ GraphNodeSubPath = (*NodePlannableResourceInstanceOrphan)(nil) - _ GraphNodeReferenceable = (*NodePlannableResourceInstanceOrphan)(nil) - _ GraphNodeReferencer = (*NodePlannableResourceInstanceOrphan)(nil) - _ GraphNodeResource = (*NodePlannableResourceInstanceOrphan)(nil) - _ GraphNodeResourceInstance = (*NodePlannableResourceInstanceOrphan)(nil) - _ GraphNodeAttachResourceConfig = (*NodePlannableResourceInstanceOrphan)(nil) - _ GraphNodeAttachResourceState = (*NodePlannableResourceInstanceOrphan)(nil) - _ GraphNodeEvalable = (*NodePlannableResourceInstanceOrphan)(nil) -) - -var ( - _ GraphNodeEvalable = (*NodePlannableResourceInstanceOrphan)(nil) -) - -func (n *NodePlannableResourceInstanceOrphan) Name() string { - return n.ResourceInstanceAddr().String() + " (orphan)" -} - -// GraphNodeEvalable -func (n *NodePlannableResourceInstanceOrphan) EvalTree() EvalNode { - addr := n.ResourceInstanceAddr() - - // Declare a bunch of variables that are used for state during - // evaluation. Most of this are written to by-address below. - var change *plans.ResourceInstanceChange - var state *states.ResourceInstanceObject - var provider providers.Interface - var providerSchema *ProviderSchema - - return &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: n.ResolvedProvider, - Output: &provider, - Schema: &providerSchema, - }, - &EvalReadState{ - Addr: addr.Resource, - Provider: &provider, - ProviderSchema: &providerSchema, - - Output: &state, - }, - &EvalDiffDestroy{ - Addr: addr.Resource, - State: &state, - ProviderAddr: n.ResolvedProvider, - Output: &change, - OutputState: &state, // Will point to a nil state after this complete, signalling destroyed - }, - &EvalCheckPreventDestroy{ - Addr: addr.Resource, - Config: n.Config, - Change: &change, - }, - &EvalWriteDiff{ - Addr: addr.Resource, - ProviderSchema: &providerSchema, - Change: &change, - }, - &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - }, - }, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_refresh.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_refresh.go deleted file mode 100644 index dcab3727..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_refresh.go +++ /dev/null @@ -1,296 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// NodeRefreshableManagedResource represents a resource that is expanabled into -// NodeRefreshableManagedResourceInstance. Resource count orphans are also added. -type NodeRefreshableManagedResource struct { - *NodeAbstractResource -} - -var ( - _ GraphNodeSubPath = (*NodeRefreshableManagedResource)(nil) - _ GraphNodeDynamicExpandable = (*NodeRefreshableManagedResource)(nil) - _ GraphNodeReferenceable = (*NodeRefreshableManagedResource)(nil) - _ GraphNodeReferencer = (*NodeRefreshableManagedResource)(nil) - _ GraphNodeResource = (*NodeRefreshableManagedResource)(nil) - _ GraphNodeAttachResourceConfig = (*NodeRefreshableManagedResource)(nil) -) - -// GraphNodeDynamicExpandable -func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph, error) { - var diags tfdiags.Diagnostics - - count, countDiags := evaluateResourceCountExpression(n.Config.Count, ctx) - diags = diags.Append(countDiags) - if countDiags.HasErrors() { - return nil, diags.Err() - } - - forEachMap, forEachDiags := evaluateResourceForEachExpression(n.Config.ForEach, ctx) - if forEachDiags.HasErrors() { - return nil, diags.Err() - } - - // Next we need to potentially rename an instance address in the state - // if we're transitioning whether "count" is set at all. - fixResourceCountSetTransition(ctx, n.ResourceAddr(), count != -1) - - // Our graph transformers require access to the full state, so we'll - // temporarily lock it while we work on this. - state := ctx.State().Lock() - defer ctx.State().Unlock() - - // The concrete resource factory we'll use - concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex { - // Add the config and state since we don't do that via transforms - a.Config = n.Config - a.ResolvedProvider = n.ResolvedProvider - - return &NodeRefreshableManagedResourceInstance{ - NodeAbstractResourceInstance: a, - } - } - - // Start creating the steps - steps := []GraphTransformer{ - // Expand the count. - &ResourceCountTransformer{ - Concrete: concreteResource, - Schema: n.Schema, - Count: count, - ForEach: forEachMap, - Addr: n.ResourceAddr(), - }, - - // Add the count orphans to make sure these resources are accounted for - // during a scale in. - &OrphanResourceCountTransformer{ - Concrete: concreteResource, - Count: count, - ForEach: forEachMap, - Addr: n.ResourceAddr(), - State: state, - }, - - // Attach the state - &AttachStateTransformer{State: state}, - - // Targeting - &TargetsTransformer{Targets: n.Targets}, - - // Connect references so ordering is correct - &ReferenceTransformer{}, - - // Make sure there is a single root - &RootTransformer{}, - } - - // Build the graph - b := &BasicGraphBuilder{ - Steps: steps, - Validate: true, - Name: "NodeRefreshableManagedResource", - } - - graph, diags := b.Build(ctx.Path()) - return graph, diags.ErrWithWarnings() -} - -// NodeRefreshableManagedResourceInstance represents a resource that is "applyable": -// it is ready to be applied and is represented by a diff. -type NodeRefreshableManagedResourceInstance struct { - *NodeAbstractResourceInstance -} - -var ( - _ GraphNodeSubPath = (*NodeRefreshableManagedResourceInstance)(nil) - _ GraphNodeReferenceable = (*NodeRefreshableManagedResourceInstance)(nil) - _ GraphNodeReferencer = (*NodeRefreshableManagedResourceInstance)(nil) - _ GraphNodeDestroyer = (*NodeRefreshableManagedResourceInstance)(nil) - _ GraphNodeResource = (*NodeRefreshableManagedResourceInstance)(nil) - _ GraphNodeResourceInstance = (*NodeRefreshableManagedResourceInstance)(nil) - _ GraphNodeAttachResourceConfig = (*NodeRefreshableManagedResourceInstance)(nil) - _ GraphNodeAttachResourceState = (*NodeRefreshableManagedResourceInstance)(nil) - _ GraphNodeEvalable = (*NodeRefreshableManagedResourceInstance)(nil) -) - -// GraphNodeDestroyer -func (n *NodeRefreshableManagedResourceInstance) DestroyAddr() *addrs.AbsResourceInstance { - addr := n.ResourceInstanceAddr() - return &addr -} - -// GraphNodeEvalable -func (n *NodeRefreshableManagedResourceInstance) EvalTree() EvalNode { - addr := n.ResourceInstanceAddr() - - // Eval info is different depending on what kind of resource this is - switch addr.Resource.Resource.Mode { - case addrs.ManagedResourceMode: - if n.ResourceState == nil { - log.Printf("[TRACE] NodeRefreshableManagedResourceInstance: %s has no existing state to refresh", addr) - return n.evalTreeManagedResourceNoState() - } - log.Printf("[TRACE] NodeRefreshableManagedResourceInstance: %s will be refreshed", addr) - return n.evalTreeManagedResource() - - case addrs.DataResourceMode: - // Get the data source node. If we don't have a configuration - // then it is an orphan so we destroy it (remove it from the state). - var dn GraphNodeEvalable - if n.Config != nil { - dn = &NodeRefreshableDataResourceInstance{ - NodeAbstractResourceInstance: n.NodeAbstractResourceInstance, - } - } else { - dn = &NodeDestroyableDataResourceInstance{ - NodeAbstractResourceInstance: n.NodeAbstractResourceInstance, - } - } - - return dn.EvalTree() - default: - panic(fmt.Errorf("unsupported resource mode %s", addr.Resource.Resource.Mode)) - } -} - -func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalNode { - addr := n.ResourceInstanceAddr() - - // Declare a bunch of variables that are used for state during - // evaluation. Most of this are written to by-address below. - var provider providers.Interface - var providerSchema *ProviderSchema - var state *states.ResourceInstanceObject - - // This happened during initial development. All known cases were - // fixed and tested but as a sanity check let's assert here. - if n.ResourceState == nil { - err := fmt.Errorf( - "No resource state attached for addr: %s\n\n"+ - "This is a bug. Please report this to Terraform with your configuration\n"+ - "and state attached. Please be careful to scrub any sensitive information.", - addr) - return &EvalReturnError{Error: &err} - } - - return &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: n.ResolvedProvider, - Output: &provider, - Schema: &providerSchema, - }, - - &EvalReadState{ - Addr: addr.Resource, - Provider: &provider, - ProviderSchema: &providerSchema, - - Output: &state, - }, - - &EvalRefresh{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - Provider: &provider, - ProviderSchema: &providerSchema, - State: &state, - Output: &state, - }, - - &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - }, - }, - } -} - -// evalTreeManagedResourceNoState produces an EvalSequence for refresh resource -// nodes that don't have state attached. An example of where this functionality -// is useful is when a resource that already exists in state is being scaled -// out, ie: has its resource count increased. In this case, the scaled out node -// needs to be available to other nodes (namely data sources) that may depend -// on it for proper interpolation, or confusing "index out of range" errors can -// occur. -// -// The steps in this sequence are very similar to the steps carried out in -// plan, but nothing is done with the diff after it is created - it is dropped, -// and its changes are not counted in the UI. -func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResourceNoState() EvalNode { - addr := n.ResourceInstanceAddr() - - // Declare a bunch of variables that are used for state during - // evaluation. Most of this are written to by-address below. - var provider providers.Interface - var providerSchema *ProviderSchema - var change *plans.ResourceInstanceChange - var state *states.ResourceInstanceObject - - return &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: n.ResolvedProvider, - Output: &provider, - Schema: &providerSchema, - }, - - &EvalReadState{ - Addr: addr.Resource, - Provider: &provider, - ProviderSchema: &providerSchema, - - Output: &state, - }, - - &EvalDiff{ - Addr: addr.Resource, - Config: n.Config, - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - OutputChange: &change, - OutputState: &state, - Stub: true, - }, - - &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - }, - - // We must also save the planned change, so that expressions in - // other nodes, such as provider configurations and data resources, - // can work with the planned new value. - // - // This depends on the fact that Context.Refresh creates a - // temporary new empty changeset for the duration of its graph - // walk, and so this recorded change will be discarded immediately - // after the refresh walk completes. - &EvalWriteDiff{ - Addr: addr.Resource, - Change: &change, - ProviderSchema: &providerSchema, - }, - }, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_validate.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_validate.go deleted file mode 100644 index f0eb18a0..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_resource_validate.go +++ /dev/null @@ -1,90 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/provisioners" - "github.com/zclconf/go-cty/cty" -) - -// NodeValidatableResource represents a resource that is used for validation -// only. -type NodeValidatableResource struct { - *NodeAbstractResource -} - -var ( - _ GraphNodeSubPath = (*NodeValidatableResource)(nil) - _ GraphNodeEvalable = (*NodeValidatableResource)(nil) - _ GraphNodeReferenceable = (*NodeValidatableResource)(nil) - _ GraphNodeReferencer = (*NodeValidatableResource)(nil) - _ GraphNodeResource = (*NodeValidatableResource)(nil) - _ GraphNodeAttachResourceConfig = (*NodeValidatableResource)(nil) -) - -// GraphNodeEvalable -func (n *NodeValidatableResource) EvalTree() EvalNode { - addr := n.ResourceAddr() - config := n.Config - - // Declare the variables will be used are used to pass values along - // the evaluation sequence below. These are written to via pointers - // passed to the EvalNodes. - var provider providers.Interface - var providerSchema *ProviderSchema - var configVal cty.Value - - seq := &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: n.ResolvedProvider, - Output: &provider, - Schema: &providerSchema, - }, - &EvalValidateResource{ - Addr: addr.Resource, - Provider: &provider, - ProviderSchema: &providerSchema, - Config: config, - ConfigVal: &configVal, - }, - }, - } - - if managed := n.Config.Managed; managed != nil { - hasCount := n.Config.Count != nil - hasForEach := n.Config.ForEach != nil - - // Validate all the provisioners - for _, p := range managed.Provisioners { - var provisioner provisioners.Interface - var provisionerSchema *configschema.Block - - if p.Connection == nil { - p.Connection = config.Managed.Connection - } else if config.Managed.Connection != nil { - p.Connection.Config = configs.MergeBodies(config.Managed.Connection.Config, p.Connection.Config) - } - - seq.Nodes = append( - seq.Nodes, - &EvalGetProvisioner{ - Name: p.Type, - Output: &provisioner, - Schema: &provisionerSchema, - }, - &EvalValidateProvisioner{ - ResourceAddr: addr.Resource, - Provisioner: &provisioner, - Schema: &provisionerSchema, - Config: p, - ResourceHasCount: hasCount, - ResourceHasForEach: hasForEach, - }, - ) - } - } - - return seq -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_root_variable.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_root_variable.go deleted file mode 100644 index 844d060c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/node_root_variable.go +++ /dev/null @@ -1,44 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" -) - -// NodeRootVariable represents a root variable input. -type NodeRootVariable struct { - Addr addrs.InputVariable - Config *configs.Variable -} - -var ( - _ GraphNodeSubPath = (*NodeRootVariable)(nil) - _ GraphNodeReferenceable = (*NodeRootVariable)(nil) - _ dag.GraphNodeDotter = (*NodeApplyableModuleVariable)(nil) -) - -func (n *NodeRootVariable) Name() string { - return n.Addr.String() -} - -// GraphNodeSubPath -func (n *NodeRootVariable) Path() addrs.ModuleInstance { - return addrs.RootModuleInstance -} - -// GraphNodeReferenceable -func (n *NodeRootVariable) ReferenceableAddrs() []addrs.Referenceable { - return []addrs.Referenceable{n.Addr} -} - -// dag.GraphNodeDotter impl. -func (n *NodeRootVariable) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { - return &dag.DotNode{ - Name: name, - Attrs: map[string]string{ - "label": n.Name(), - "shape": "note", - }, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/path.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/path.go deleted file mode 100644 index 19e3469c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/path.go +++ /dev/null @@ -1,17 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// PathObjectCacheKey is like PathCacheKey but includes an additional name -// to be included in the key, for module-namespaced objects. -// -// The result of this function is guaranteed unique for any distinct pair -// of path and name, but is not guaranteed to be in any particular format -// and in particular should never be shown to end-users. -func PathObjectCacheKey(path addrs.ModuleInstance, objectName string) string { - return fmt.Sprintf("%s|%s", path.String(), objectName) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/plan.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/plan.go deleted file mode 100644 index 5c19f6e7..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/plan.go +++ /dev/null @@ -1,94 +0,0 @@ -package terraform - -import ( - "bytes" - "encoding/gob" - "fmt" - "io" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/zclconf/go-cty/cty" -) - -func init() { - gob.Register(make([]interface{}, 0)) - gob.Register(make([]map[string]interface{}, 0)) - gob.Register(make(map[string]interface{})) - gob.Register(make(map[string]string)) -} - -// Plan represents a single Terraform execution plan, which contains -// all the information necessary to make an infrastructure change. -// -// A plan has to contain basically the entire state of the world -// necessary to make a change: the state, diff, config, backend config, etc. -// This is so that it can run alone without any other data. -type Plan struct { - // Diff describes the resource actions that must be taken when this - // plan is applied. - Diff *Diff - - // Config represents the entire configuration that was present when this - // plan was created. - Config *configs.Config - - // State is the Terraform state that was current when this plan was - // created. - // - // It is not allowed to apply a plan that has a stale state, since its - // diff could be outdated. - State *State - - // Vars retains the variables that were set when creating the plan, so - // that the same variables can be applied during apply. - Vars map[string]cty.Value - - // Targets, if non-empty, contains a set of resource address strings that - // identify graph nodes that were selected as targets for plan. - // - // When targets are set, any graph node that is not directly targeted or - // indirectly targeted via dependencies is excluded from the graph. - Targets []string - - // TerraformVersion is the version of Terraform that was used to create - // this plan. - // - // It is not allowed to apply a plan created with a different version of - // Terraform, since the other fields of this structure may be interpreted - // in different ways between versions. - TerraformVersion string - - // ProviderSHA256s is a map giving the SHA256 hashes of the exact binaries - // used as plugins for each provider during plan. - // - // These must match between plan and apply to ensure that the diff is - // correctly interpreted, since different provider versions may have - // different attributes or attribute value constraints. - ProviderSHA256s map[string][]byte - - // Backend is the backend that this plan should use and store data with. - Backend *BackendState - - // Destroy indicates that this plan was created for a full destroy operation - Destroy bool -} - -func (p *Plan) String() string { - buf := new(bytes.Buffer) - buf.WriteString("DIFF:\n\n") - buf.WriteString(p.Diff.String()) - buf.WriteString("\n\nSTATE:\n\n") - buf.WriteString(p.State.String()) - return buf.String() -} - -// ReadPlan reads a plan structure out of a reader in the format that -// was written by WritePlan. -func ReadPlan(src io.Reader) (*Plan, error) { - return nil, fmt.Errorf("terraform.ReadPlan is no longer in use; use planfile.Open instead") -} - -// WritePlan writes a plan somewhere in a binary format. -func WritePlan(d *Plan, dst io.Writer) error { - return fmt.Errorf("terraform.WritePlan is no longer in use; use planfile.Create instead") -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/provider_mock.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/provider_mock.go deleted file mode 100644 index 7e401f33..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/provider_mock.go +++ /dev/null @@ -1,521 +0,0 @@ -package terraform - -import ( - "encoding/json" - "fmt" - "sync" - - "github.com/zclconf/go-cty/cty" - ctyjson "github.com/zclconf/go-cty/cty/json" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -var _ providers.Interface = (*MockProvider)(nil) - -// MockProvider implements providers.Interface but mocks out all the -// calls for testing purposes. -type MockProvider struct { - sync.Mutex - - // Anything you want, in case you need to store extra data with the mock. - Meta interface{} - - GetSchemaCalled bool - GetSchemaReturn *ProviderSchema // This is using ProviderSchema directly rather than providers.GetSchemaResponse for compatibility with old tests - - PrepareProviderConfigCalled bool - PrepareProviderConfigResponse providers.PrepareProviderConfigResponse - PrepareProviderConfigRequest providers.PrepareProviderConfigRequest - PrepareProviderConfigFn func(providers.PrepareProviderConfigRequest) providers.PrepareProviderConfigResponse - - ValidateResourceTypeConfigCalled bool - ValidateResourceTypeConfigTypeName string - ValidateResourceTypeConfigResponse providers.ValidateResourceTypeConfigResponse - ValidateResourceTypeConfigRequest providers.ValidateResourceTypeConfigRequest - ValidateResourceTypeConfigFn func(providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse - - ValidateDataSourceConfigCalled bool - ValidateDataSourceConfigTypeName string - ValidateDataSourceConfigResponse providers.ValidateDataSourceConfigResponse - ValidateDataSourceConfigRequest providers.ValidateDataSourceConfigRequest - ValidateDataSourceConfigFn func(providers.ValidateDataSourceConfigRequest) providers.ValidateDataSourceConfigResponse - - UpgradeResourceStateCalled bool - UpgradeResourceStateTypeName string - UpgradeResourceStateResponse providers.UpgradeResourceStateResponse - UpgradeResourceStateRequest providers.UpgradeResourceStateRequest - UpgradeResourceStateFn func(providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse - - ConfigureCalled bool - ConfigureResponse providers.ConfigureResponse - ConfigureRequest providers.ConfigureRequest - ConfigureNewFn func(providers.ConfigureRequest) providers.ConfigureResponse // Named ConfigureNewFn so we can still have the legacy ConfigureFn declared below - - StopCalled bool - StopFn func() error - StopResponse error - - ReadResourceCalled bool - ReadResourceResponse providers.ReadResourceResponse - ReadResourceRequest providers.ReadResourceRequest - ReadResourceFn func(providers.ReadResourceRequest) providers.ReadResourceResponse - - PlanResourceChangeCalled bool - PlanResourceChangeResponse providers.PlanResourceChangeResponse - PlanResourceChangeRequest providers.PlanResourceChangeRequest - PlanResourceChangeFn func(providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse - - ApplyResourceChangeCalled bool - ApplyResourceChangeResponse providers.ApplyResourceChangeResponse - ApplyResourceChangeRequest providers.ApplyResourceChangeRequest - ApplyResourceChangeFn func(providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse - - ImportResourceStateCalled bool - ImportResourceStateResponse providers.ImportResourceStateResponse - ImportResourceStateRequest providers.ImportResourceStateRequest - ImportResourceStateFn func(providers.ImportResourceStateRequest) providers.ImportResourceStateResponse - // Legacy return type for existing tests, which will be shimmed into an - // ImportResourceStateResponse if set - ImportStateReturn []*InstanceState - - ReadDataSourceCalled bool - ReadDataSourceResponse providers.ReadDataSourceResponse - ReadDataSourceRequest providers.ReadDataSourceRequest - ReadDataSourceFn func(providers.ReadDataSourceRequest) providers.ReadDataSourceResponse - - CloseCalled bool - CloseError error - - // Legacy callbacks: if these are set, we will shim incoming calls for - // new-style methods to these old-fashioned terraform.ResourceProvider - // mock callbacks, for the benefit of older tests that were written against - // the old mock API. - ValidateFn func(c *ResourceConfig) (ws []string, es []error) - ConfigureFn func(c *ResourceConfig) error - DiffFn func(info *InstanceInfo, s *InstanceState, c *ResourceConfig) (*InstanceDiff, error) - ApplyFn func(info *InstanceInfo, s *InstanceState, d *InstanceDiff) (*InstanceState, error) -} - -func (p *MockProvider) GetSchema() providers.GetSchemaResponse { - p.Lock() - defer p.Unlock() - p.GetSchemaCalled = true - return p.getSchema() -} - -func (p *MockProvider) getSchema() providers.GetSchemaResponse { - // This version of getSchema doesn't do any locking, so it's suitable to - // call from other methods of this mock as long as they are already - // holding the lock. - - ret := providers.GetSchemaResponse{ - Provider: providers.Schema{}, - DataSources: map[string]providers.Schema{}, - ResourceTypes: map[string]providers.Schema{}, - } - if p.GetSchemaReturn != nil { - ret.Provider.Block = p.GetSchemaReturn.Provider - for n, s := range p.GetSchemaReturn.DataSources { - ret.DataSources[n] = providers.Schema{ - Block: s, - } - } - for n, s := range p.GetSchemaReturn.ResourceTypes { - ret.ResourceTypes[n] = providers.Schema{ - Version: int64(p.GetSchemaReturn.ResourceTypeSchemaVersions[n]), - Block: s, - } - } - } - - return ret -} - -func (p *MockProvider) PrepareProviderConfig(r providers.PrepareProviderConfigRequest) providers.PrepareProviderConfigResponse { - p.Lock() - defer p.Unlock() - - p.PrepareProviderConfigCalled = true - p.PrepareProviderConfigRequest = r - if p.PrepareProviderConfigFn != nil { - return p.PrepareProviderConfigFn(r) - } - return p.PrepareProviderConfigResponse -} - -func (p *MockProvider) ValidateResourceTypeConfig(r providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse { - p.Lock() - defer p.Unlock() - - p.ValidateResourceTypeConfigCalled = true - p.ValidateResourceTypeConfigRequest = r - - if p.ValidateFn != nil { - resp := p.getSchema() - schema := resp.Provider.Block - rc := NewResourceConfigShimmed(r.Config, schema) - warns, errs := p.ValidateFn(rc) - ret := providers.ValidateResourceTypeConfigResponse{} - for _, warn := range warns { - ret.Diagnostics = ret.Diagnostics.Append(tfdiags.SimpleWarning(warn)) - } - for _, err := range errs { - ret.Diagnostics = ret.Diagnostics.Append(err) - } - } - if p.ValidateResourceTypeConfigFn != nil { - return p.ValidateResourceTypeConfigFn(r) - } - - return p.ValidateResourceTypeConfigResponse -} - -func (p *MockProvider) ValidateDataSourceConfig(r providers.ValidateDataSourceConfigRequest) providers.ValidateDataSourceConfigResponse { - p.Lock() - defer p.Unlock() - - p.ValidateDataSourceConfigCalled = true - p.ValidateDataSourceConfigRequest = r - - if p.ValidateDataSourceConfigFn != nil { - return p.ValidateDataSourceConfigFn(r) - } - - return p.ValidateDataSourceConfigResponse -} - -func (p *MockProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse { - p.Lock() - defer p.Unlock() - - schemas := p.getSchema() - schema := schemas.ResourceTypes[r.TypeName] - schemaType := schema.Block.ImpliedType() - - p.UpgradeResourceStateCalled = true - p.UpgradeResourceStateRequest = r - - if p.UpgradeResourceStateFn != nil { - return p.UpgradeResourceStateFn(r) - } - - resp := p.UpgradeResourceStateResponse - - if resp.UpgradedState == cty.NilVal { - switch { - case r.RawStateFlatmap != nil: - v, err := hcl2shim.HCL2ValueFromFlatmap(r.RawStateFlatmap, schemaType) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - resp.UpgradedState = v - case len(r.RawStateJSON) > 0: - v, err := ctyjson.Unmarshal(r.RawStateJSON, schemaType) - - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - resp.UpgradedState = v - } - } - return resp -} - -func (p *MockProvider) Configure(r providers.ConfigureRequest) providers.ConfigureResponse { - p.Lock() - defer p.Unlock() - - p.ConfigureCalled = true - p.ConfigureRequest = r - - if p.ConfigureFn != nil { - resp := p.getSchema() - schema := resp.Provider.Block - rc := NewResourceConfigShimmed(r.Config, schema) - ret := providers.ConfigureResponse{} - - err := p.ConfigureFn(rc) - if err != nil { - ret.Diagnostics = ret.Diagnostics.Append(err) - } - return ret - } - if p.ConfigureNewFn != nil { - return p.ConfigureNewFn(r) - } - - return p.ConfigureResponse -} - -func (p *MockProvider) Stop() error { - // We intentionally don't lock in this one because the whole point of this - // method is to be called concurrently with another operation that can - // be cancelled. The provider itself is responsible for handling - // any concurrency concerns in this case. - - p.StopCalled = true - if p.StopFn != nil { - return p.StopFn() - } - - return p.StopResponse -} - -func (p *MockProvider) ReadResource(r providers.ReadResourceRequest) providers.ReadResourceResponse { - p.Lock() - defer p.Unlock() - - p.ReadResourceCalled = true - p.ReadResourceRequest = r - - if p.ReadResourceFn != nil { - return p.ReadResourceFn(r) - } - - // make sure the NewState fits the schema - newState, err := p.GetSchemaReturn.ResourceTypes[r.TypeName].CoerceValue(p.ReadResourceResponse.NewState) - if err != nil { - panic(err) - } - resp := p.ReadResourceResponse - resp.NewState = newState - - return resp -} - -func (p *MockProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { - p.Lock() - defer p.Unlock() - - p.PlanResourceChangeCalled = true - p.PlanResourceChangeRequest = r - - if p.DiffFn != nil { - ps := p.getSchema() - if ps.ResourceTypes == nil || ps.ResourceTypes[r.TypeName].Block == nil { - return providers.PlanResourceChangeResponse{ - Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Printf("mock provider has no schema for resource type %s", r.TypeName)), - } - } - schema := ps.ResourceTypes[r.TypeName].Block - info := &InstanceInfo{ - Type: r.TypeName, - } - priorState := NewInstanceStateShimmedFromValue(r.PriorState, 0) - cfg := NewResourceConfigShimmed(r.Config, schema) - - legacyDiff, err := p.DiffFn(info, priorState, cfg) - - var res providers.PlanResourceChangeResponse - res.PlannedState = r.ProposedNewState - if err != nil { - res.Diagnostics = res.Diagnostics.Append(err) - } - if legacyDiff != nil { - newVal, err := legacyDiff.ApplyToValue(r.PriorState, schema) - if err != nil { - res.Diagnostics = res.Diagnostics.Append(err) - } - - res.PlannedState = newVal - - var requiresNew []string - for attr, d := range legacyDiff.Attributes { - if d.RequiresNew { - requiresNew = append(requiresNew, attr) - } - } - requiresReplace, err := hcl2shim.RequiresReplace(requiresNew, schema.ImpliedType()) - if err != nil { - res.Diagnostics = res.Diagnostics.Append(err) - } - res.RequiresReplace = requiresReplace - } - return res - } - if p.PlanResourceChangeFn != nil { - return p.PlanResourceChangeFn(r) - } - - return p.PlanResourceChangeResponse -} - -func (p *MockProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { - p.Lock() - p.ApplyResourceChangeCalled = true - p.ApplyResourceChangeRequest = r - p.Unlock() - - if p.ApplyFn != nil { - // ApplyFn is a special callback fashioned after our old provider - // interface, which expected to be given an actual diff rather than - // separate old/new values to apply. Therefore we need to approximate - // a diff here well enough that _most_ of our legacy ApplyFns in old - // tests still see the behavior they are expecting. New tests should - // not use this, and should instead use ApplyResourceChangeFn directly. - providerSchema := p.getSchema() - schema, ok := providerSchema.ResourceTypes[r.TypeName] - if !ok { - return providers.ApplyResourceChangeResponse{ - Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("no mocked schema available for resource type %s", r.TypeName)), - } - } - - info := &InstanceInfo{ - Type: r.TypeName, - } - - priorVal := r.PriorState - plannedVal := r.PlannedState - priorMap := hcl2shim.FlatmapValueFromHCL2(priorVal) - plannedMap := hcl2shim.FlatmapValueFromHCL2(plannedVal) - s := NewInstanceStateShimmedFromValue(priorVal, 0) - d := &InstanceDiff{ - Attributes: make(map[string]*ResourceAttrDiff), - } - if plannedMap == nil { // destroying, then - d.Destroy = true - // Destroy diffs don't have any attribute diffs - } else { - if priorMap == nil { // creating, then - // We'll just make an empty prior map to make things easier below. - priorMap = make(map[string]string) - } - - for k, new := range plannedMap { - old := priorMap[k] - newComputed := false - if new == hcl2shim.UnknownVariableValue { - new = "" - newComputed = true - } - d.Attributes[k] = &ResourceAttrDiff{ - Old: old, - New: new, - NewComputed: newComputed, - Type: DiffAttrInput, // not generally used in tests, so just hard-coded - } - } - // Also need any attributes that were removed in "planned" - for k, old := range priorMap { - if _, ok := plannedMap[k]; ok { - continue - } - d.Attributes[k] = &ResourceAttrDiff{ - Old: old, - NewRemoved: true, - Type: DiffAttrInput, - } - } - } - newState, err := p.ApplyFn(info, s, d) - resp := providers.ApplyResourceChangeResponse{} - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - } - if newState != nil { - var newVal cty.Value - if newState != nil { - var err error - newVal, err = newState.AttrsAsObjectValue(schema.Block.ImpliedType()) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - } - } else { - // If apply returned a nil new state then that's the old way to - // indicate that the object was destroyed. Our new interface calls - // for that to be signalled as a null value. - newVal = cty.NullVal(schema.Block.ImpliedType()) - } - resp.NewState = newVal - } - - return resp - } - if p.ApplyResourceChangeFn != nil { - return p.ApplyResourceChangeFn(r) - } - - return p.ApplyResourceChangeResponse -} - -func (p *MockProvider) ImportResourceState(r providers.ImportResourceStateRequest) providers.ImportResourceStateResponse { - p.Lock() - defer p.Unlock() - - if p.ImportStateReturn != nil { - for _, is := range p.ImportStateReturn { - if is.Attributes == nil { - is.Attributes = make(map[string]string) - } - is.Attributes["id"] = is.ID - - typeName := is.Ephemeral.Type - // Use the requested type if the resource has no type of it's own. - // We still return the empty type, which will error, but this prevents a panic. - if typeName == "" { - typeName = r.TypeName - } - - schema := p.GetSchemaReturn.ResourceTypes[typeName] - if schema == nil { - panic("no schema found for " + typeName) - } - - private, err := json.Marshal(is.Meta) - if err != nil { - panic(err) - } - - state, err := hcl2shim.HCL2ValueFromFlatmap(is.Attributes, schema.ImpliedType()) - if err != nil { - panic(err) - } - - state, err = schema.CoerceValue(state) - if err != nil { - panic(err) - } - - p.ImportResourceStateResponse.ImportedResources = append( - p.ImportResourceStateResponse.ImportedResources, - providers.ImportedResource{ - TypeName: is.Ephemeral.Type, - State: state, - Private: private, - }) - } - } - - p.ImportResourceStateCalled = true - p.ImportResourceStateRequest = r - if p.ImportResourceStateFn != nil { - return p.ImportResourceStateFn(r) - } - - return p.ImportResourceStateResponse -} - -func (p *MockProvider) ReadDataSource(r providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { - p.Lock() - defer p.Unlock() - - p.ReadDataSourceCalled = true - p.ReadDataSourceRequest = r - - if p.ReadDataSourceFn != nil { - return p.ReadDataSourceFn(r) - } - - return p.ReadDataSourceResponse -} - -func (p *MockProvider) Close() error { - p.CloseCalled = true - return p.CloseError -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/provisioner_mock.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/provisioner_mock.go deleted file mode 100644 index 93b19be5..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/provisioner_mock.go +++ /dev/null @@ -1,154 +0,0 @@ -package terraform - -import ( - "fmt" - "sync" - - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" - - "github.com/hashicorp/terraform-plugin-sdk/internal/provisioners" -) - -var _ provisioners.Interface = (*MockProvisioner)(nil) - -// MockProvisioner implements provisioners.Interface but mocks out all the -// calls for testing purposes. -type MockProvisioner struct { - sync.Mutex - // Anything you want, in case you need to store extra data with the mock. - Meta interface{} - - GetSchemaCalled bool - GetSchemaResponse provisioners.GetSchemaResponse - - ValidateProvisionerConfigCalled bool - ValidateProvisionerConfigRequest provisioners.ValidateProvisionerConfigRequest - ValidateProvisionerConfigResponse provisioners.ValidateProvisionerConfigResponse - ValidateProvisionerConfigFn func(provisioners.ValidateProvisionerConfigRequest) provisioners.ValidateProvisionerConfigResponse - - ProvisionResourceCalled bool - ProvisionResourceRequest provisioners.ProvisionResourceRequest - ProvisionResourceResponse provisioners.ProvisionResourceResponse - ProvisionResourceFn func(provisioners.ProvisionResourceRequest) provisioners.ProvisionResourceResponse - - StopCalled bool - StopResponse error - StopFn func() error - - CloseCalled bool - CloseResponse error - CloseFn func() error - - // Legacy callbacks: if these are set, we will shim incoming calls for - // new-style methods to these old-fashioned terraform.ResourceProvider - // mock callbacks, for the benefit of older tests that were written against - // the old mock API. - ApplyFn func(rs *InstanceState, c *ResourceConfig) error -} - -func (p *MockProvisioner) GetSchema() provisioners.GetSchemaResponse { - p.Lock() - defer p.Unlock() - - p.GetSchemaCalled = true - return p.getSchema() -} - -// getSchema is the implementation of GetSchema, which can be called from other -// methods on MockProvisioner that may already be holding the lock. -func (p *MockProvisioner) getSchema() provisioners.GetSchemaResponse { - return p.GetSchemaResponse -} - -func (p *MockProvisioner) ValidateProvisionerConfig(r provisioners.ValidateProvisionerConfigRequest) provisioners.ValidateProvisionerConfigResponse { - p.Lock() - defer p.Unlock() - - p.ValidateProvisionerConfigCalled = true - p.ValidateProvisionerConfigRequest = r - if p.ValidateProvisionerConfigFn != nil { - return p.ValidateProvisionerConfigFn(r) - } - return p.ValidateProvisionerConfigResponse -} - -func (p *MockProvisioner) ProvisionResource(r provisioners.ProvisionResourceRequest) provisioners.ProvisionResourceResponse { - p.Lock() - defer p.Unlock() - - p.ProvisionResourceCalled = true - p.ProvisionResourceRequest = r - if p.ApplyFn != nil { - if !r.Config.IsKnown() { - panic(fmt.Sprintf("cannot provision with unknown value: %#v", r.Config)) - } - - schema := p.getSchema() - rc := NewResourceConfigShimmed(r.Config, schema.Provisioner) - connVal := r.Connection - connMap := map[string]string{} - - if !connVal.IsNull() && connVal.IsKnown() { - for it := connVal.ElementIterator(); it.Next(); { - ak, av := it.Element() - name := ak.AsString() - - if !av.IsKnown() || av.IsNull() { - continue - } - - av, _ = convert.Convert(av, cty.String) - connMap[name] = av.AsString() - } - } - - // We no longer pass the full instance state to a provisioner, so we'll - // construct a partial one that should be good enough for what existing - // test mocks need. - is := &InstanceState{ - Ephemeral: EphemeralState{ - ConnInfo: connMap, - }, - } - var resp provisioners.ProvisionResourceResponse - err := p.ApplyFn(is, rc) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - } - return resp - } - if p.ProvisionResourceFn != nil { - fn := p.ProvisionResourceFn - p.Unlock() - return fn(r) - } - - return p.ProvisionResourceResponse -} - -func (p *MockProvisioner) Stop() error { - // We intentionally don't lock in this one because the whole point of this - // method is to be called concurrently with another operation that can - // be cancelled. The provisioner itself is responsible for handling - // any concurrency concerns in this case. - - p.StopCalled = true - if p.StopFn != nil { - return p.StopFn() - } - - return p.StopResponse -} - -func (p *MockProvisioner) Close() error { - p.Lock() - defer p.Unlock() - - p.CloseCalled = true - if p.CloseFn != nil { - return p.CloseFn() - } - - return p.CloseResponse -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource.go index bd577460..11b63de8 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource.go @@ -7,66 +7,14 @@ import ( "strconv" "strings" + "github.com/hashicorp/go-cty/cty" "github.com/mitchellh/copystructure" "github.com/mitchellh/reflectwalk" - "github.com/zclconf/go-cty/cty" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/hcl2shim" ) -// Resource is a legacy way to identify a particular resource instance. -// -// New code should use addrs.ResourceInstance instead. This is still here -// only for codepaths that haven't been updated yet. -type Resource struct { - // These are all used by the new EvalNode stuff. - Name string - Type string - CountIndex int - - // These aren't really used anymore anywhere, but we keep them around - // since we haven't done a proper cleanup yet. - Id string - Info *InstanceInfo - Config *ResourceConfig - Dependencies []string - Diff *InstanceDiff - Provider ResourceProvider - State *InstanceState - Flags ResourceFlag -} - -// NewResource constructs a legacy Resource object from an -// addrs.ResourceInstance value. -// -// This is provided to shim to old codepaths that haven't been updated away -// from this type yet. Since this old type is not able to represent instances -// that have string keys, this function will panic if given a resource address -// that has a string key. -func NewResource(addr addrs.ResourceInstance) *Resource { - ret := &Resource{ - Name: addr.Resource.Name, - Type: addr.Resource.Type, - } - - if addr.Key != addrs.NoKey { - switch tk := addr.Key.(type) { - case addrs.IntKey: - ret.CountIndex = int(tk) - default: - panic(fmt.Errorf("resource instance with key %#v is not supported", addr.Key)) - } - } - - return ret -} - -// ResourceKind specifies what kind of instance we're working with, whether -// its a primary instance, a tainted instance, or an orphan. -type ResourceFlag byte - // InstanceInfo is used to hold information about the instance and/or // resource being modified. type InstanceInfo struct { @@ -82,95 +30,6 @@ type InstanceInfo struct { Type string } -// NewInstanceInfo constructs an InstanceInfo from an addrs.AbsResourceInstance. -// -// InstanceInfo is a legacy type, and uses of it should be gradually replaced -// by direct use of addrs.AbsResource or addrs.AbsResourceInstance as -// appropriate. -// -// The legacy InstanceInfo type cannot represent module instances with instance -// keys, so this function will panic if given such a path. Uses of this type -// should all be removed or replaced before implementing "count" and "for_each" -// arguments on modules in order to avoid such panics. -// -// This legacy type also cannot represent resource instances with string -// instance keys. It will panic if the given key is not either NoKey or an -// IntKey. -func NewInstanceInfo(addr addrs.AbsResourceInstance) *InstanceInfo { - // We need an old-style []string module path for InstanceInfo. - path := make([]string, len(addr.Module)) - for i, step := range addr.Module { - if step.InstanceKey != addrs.NoKey { - panic("NewInstanceInfo cannot convert module instance with key") - } - path[i] = step.Name - } - - // This is a funny old meaning of "id" that is no longer current. It should - // not be used for anything users might see. Note that it does not include - // a representation of the resource mode, and so it's impossible to - // determine from an InstanceInfo alone whether it is a managed or data - // resource that is being referred to. - id := fmt.Sprintf("%s.%s", addr.Resource.Resource.Type, addr.Resource.Resource.Name) - if addr.Resource.Resource.Mode == addrs.DataResourceMode { - id = "data." + id - } - if addr.Resource.Key != addrs.NoKey { - switch k := addr.Resource.Key.(type) { - case addrs.IntKey: - id = id + fmt.Sprintf(".%d", int(k)) - default: - panic(fmt.Sprintf("NewInstanceInfo cannot convert resource instance with %T instance key", addr.Resource.Key)) - } - } - - return &InstanceInfo{ - Id: id, - ModulePath: path, - Type: addr.Resource.Resource.Type, - } -} - -// ResourceAddress returns the address of the resource that the receiver is describing. -func (i *InstanceInfo) ResourceAddress() *ResourceAddress { - // GROSS: for tainted and deposed instances, their status gets appended - // to i.Id to create a unique id for the graph node. Historically these - // ids were displayed to the user, so it's designed to be human-readable: - // "aws_instance.bar.0 (deposed #0)" - // - // So here we detect such suffixes and try to interpret them back to - // their original meaning so we can then produce a ResourceAddress - // with a suitable InstanceType. - id := i.Id - instanceType := TypeInvalid - if idx := strings.Index(id, " ("); idx != -1 { - remain := id[idx:] - id = id[:idx] - - switch { - case strings.Contains(remain, "tainted"): - instanceType = TypeTainted - case strings.Contains(remain, "deposed"): - instanceType = TypeDeposed - } - } - - addr, err := parseResourceAddressInternal(id) - if err != nil { - // should never happen, since that would indicate a bug in the - // code that constructed this InstanceInfo. - panic(fmt.Errorf("InstanceInfo has invalid Id %s", id)) - } - if len(i.ModulePath) > 1 { - addr.Path = i.ModulePath[1:] // trim off "root" prefix, which is implied - } - if instanceType != TypeInvalid { - addr.InstanceTypeSet = true - addr.InstanceType = instanceType - } - return addr -} - // ResourceConfig is a legacy type that was formerly used to represent // interpolatable configuration blocks. It is now only used to shim to old // APIs that still use this type, via NewResourceConfigShimmed. @@ -335,22 +194,6 @@ func (c *ResourceConfig) Equal(c2 *ResourceConfig) bool { return true } -// CheckSet checks that the given list of configuration keys is -// properly set. If not, errors are returned for each unset key. -// -// This is useful to be called in the Validate method of a ResourceProvider. -func (c *ResourceConfig) CheckSet(keys []string) []error { - var errs []error - - for _, k := range keys { - if !c.IsSet(k) { - errs = append(errs, fmt.Errorf("%s must be set", k)) - } - } - - return errs -} - // Get looks up a configuration value by key and returns the value. // // The second return value is true if the get was successful. Get will @@ -399,28 +242,6 @@ func (c *ResourceConfig) IsComputed(k string) bool { return w.Unknown } -// IsSet checks if the key in the configuration is set. A key is set if -// it has a value or the value is being computed (is unknown currently). -// -// This function should be used rather than checking the keys of the -// raw configuration itself, since a key may be omitted from the raw -// configuration if it is being computed. -func (c *ResourceConfig) IsSet(k string) bool { - if c == nil { - return false - } - - if c.IsComputed(k) { - return true - } - - if _, ok := c.Get(k); ok { - return true - } - - return false -} - func (c *ResourceConfig) get( k string, raw map[string]interface{}) (interface{}, bool) { parts := strings.Split(k, ".") @@ -501,6 +322,8 @@ type unknownCheckWalker struct { Unknown bool } +// TODO: investigate why deleting this causes odd runtime test failures +// must be some kind of interface implementation func (w *unknownCheckWalker) Primitive(v reflect.Value) error { if v.Interface() == hcl2shim.UnknownVariableValue { w.Unknown = true diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_address.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_address.go index 8a683012..ec2665d3 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_address.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_address.go @@ -6,14 +6,11 @@ import ( "regexp" "strconv" "strings" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" ) -// ResourceAddress is a way of identifying an individual resource (or, +// resourceAddress is a way of identifying an individual resource (or, // eventually, a subset of resources) within the state. It is used for Targets. -type ResourceAddress struct { +type resourceAddress struct { // Addresses a resource falling somewhere in the module path // When specified alone, addresses all resources within a module path Path []string @@ -21,35 +18,15 @@ type ResourceAddress struct { // Addresses a specific resource that occurs in a list Index int - InstanceType InstanceType + InstanceType instanceType InstanceTypeSet bool Name string Type string Mode ResourceMode // significant only if InstanceTypeSet } -// Copy returns a copy of this ResourceAddress -func (r *ResourceAddress) Copy() *ResourceAddress { - if r == nil { - return nil - } - - n := &ResourceAddress{ - Path: make([]string, 0, len(r.Path)), - Index: r.Index, - InstanceType: r.InstanceType, - Name: r.Name, - Type: r.Type, - Mode: r.Mode, - } - - n.Path = append(n.Path, r.Path...) - - return n -} - // String outputs the address that parses into this address. -func (r *ResourceAddress) String() string { +func (r *resourceAddress) String() string { var result []string for _, p := range r.Path { result = append(result, "module", p) @@ -72,11 +49,11 @@ func (r *ResourceAddress) String() string { name := r.Name if r.InstanceTypeSet { switch r.InstanceType { - case TypePrimary: + case typePrimary: name += ".primary" - case TypeDeposed: + case typeDeposed: name += ".deposed" - case TypeTainted: + case typeTainted: name += ".tainted" } } @@ -90,133 +67,7 @@ func (r *ResourceAddress) String() string { return strings.Join(result, ".") } -// HasResourceSpec returns true if the address has a resource spec, as -// defined in the documentation: -// https://www.terraform.io/docs/internals/resource-addressing.html -// In particular, this returns false if the address contains only -// a module path, thus addressing the entire module. -func (r *ResourceAddress) HasResourceSpec() bool { - return r.Type != "" && r.Name != "" -} - -// WholeModuleAddress returns the resource address that refers to all -// resources in the same module as the receiver address. -func (r *ResourceAddress) WholeModuleAddress() *ResourceAddress { - return &ResourceAddress{ - Path: r.Path, - Index: -1, - InstanceTypeSet: false, - } -} - -// MatchesResourceConfig returns true if the receiver matches the given -// configuration resource within the given _static_ module path. Note that -// the module path in a resource address is a _dynamic_ module path, and -// multiple dynamic resource paths may map to a single static path if -// count and for_each are in use on module calls. -// -// Since resource configuration blocks represent all of the instances of -// a multi-instance resource, the index of the address (if any) is not -// considered. -func (r *ResourceAddress) MatchesResourceConfig(path addrs.Module, rc *configs.Resource) bool { - if r.HasResourceSpec() { - // FIXME: Some ugliness while we are between worlds. Functionality - // in "addrs" should eventually replace this ResourceAddress idea - // completely, but for now we'll need to translate to the old - // way of representing resource modes. - switch r.Mode { - case ManagedResourceMode: - if rc.Mode != addrs.ManagedResourceMode { - return false - } - case DataResourceMode: - if rc.Mode != addrs.DataResourceMode { - return false - } - } - if r.Type != rc.Type || r.Name != rc.Name { - return false - } - } - - addrPath := r.Path - - // normalize - if len(addrPath) == 0 { - addrPath = nil - } - if len(path) == 0 { - path = nil - } - rawPath := []string(path) - return reflect.DeepEqual(addrPath, rawPath) -} - -// stateId returns the ID that this resource should be entered with -// in the state. This is also used for diffs. In the future, we'd like to -// move away from this string field so I don't export this. -func (r *ResourceAddress) stateId() string { - result := fmt.Sprintf("%s.%s", r.Type, r.Name) - switch r.Mode { - case ManagedResourceMode: - // Done - case DataResourceMode: - result = fmt.Sprintf("data.%s", result) - default: - panic(fmt.Errorf("unknown resource mode: %s", r.Mode)) - } - if r.Index >= 0 { - result += fmt.Sprintf(".%d", r.Index) - } - - return result -} - -// parseResourceAddressInternal parses the somewhat bespoke resource -// identifier used in states and diffs, such as "instance.name.0". -func parseResourceAddressInternal(s string) (*ResourceAddress, error) { - // Split based on ".". Every resource address should have at least two - // elements (type and name). - parts := strings.Split(s, ".") - if len(parts) < 2 || len(parts) > 4 { - return nil, fmt.Errorf("Invalid internal resource address format: %s", s) - } - - // Data resource if we have at least 3 parts and the first one is data - mode := ManagedResourceMode - if len(parts) > 2 && parts[0] == "data" { - mode = DataResourceMode - parts = parts[1:] - } - - // If we're not a data resource and we have more than 3, then it is an error - if len(parts) > 3 && mode != DataResourceMode { - return nil, fmt.Errorf("Invalid internal resource address format: %s", s) - } - - // Build the parts of the resource address that are guaranteed to exist - addr := &ResourceAddress{ - Type: parts[0], - Name: parts[1], - Index: -1, - InstanceType: TypePrimary, - Mode: mode, - } - - // If we have more parts, then we have an index. Parse that. - if len(parts) > 2 { - idx, err := strconv.ParseInt(parts[2], 0, 0) - if err != nil { - return nil, fmt.Errorf("Error parsing resource address %q: %s", s, err) - } - - addr.Index = int(idx) - } - - return addr, nil -} - -func ParseResourceAddress(s string) (*ResourceAddress, error) { +func parseResourceAddress(s string) (*resourceAddress, error) { matches, err := tokenizeResourceAddress(s) if err != nil { return nil, err @@ -225,15 +76,15 @@ func ParseResourceAddress(s string) (*ResourceAddress, error) { if matches["data_prefix"] != "" { mode = DataResourceMode } - resourceIndex, err := ParseResourceIndex(matches["index"]) + resourceIndex, err := parseResourceIndex(matches["index"]) if err != nil { return nil, err } - instanceType, err := ParseInstanceType(matches["instance_type"]) + instanceType, err := parseInstanceType(matches["instance_type"]) if err != nil { return nil, err } - path := ParseResourcePath(matches["path"]) + path := parseResourcePath(matches["path"]) // not allowed to say "data." without a type following if mode == DataResourceMode && matches["type"] == "" { @@ -243,7 +94,7 @@ func ParseResourceAddress(s string) (*ResourceAddress, error) { ) } - return &ResourceAddress{ + return &resourceAddress{ Path: path, Index: resourceIndex, InstanceType: instanceType, @@ -254,249 +105,6 @@ func ParseResourceAddress(s string) (*ResourceAddress, error) { }, nil } -// ParseResourceAddressForInstanceDiff creates a ResourceAddress for a -// resource name as described in a module diff. -// -// For historical reasons a different addressing format is used in this -// context. The internal format should not be shown in the UI and instead -// this function should be used to translate to a ResourceAddress and -// then, where appropriate, use the String method to produce a canonical -// resource address string for display in the UI. -// -// The given path slice must be empty (or nil) for the root module, and -// otherwise consist of a sequence of module names traversing down into -// the module tree. If a non-nil path is provided, the caller must not -// modify its underlying array after passing it to this function. -func ParseResourceAddressForInstanceDiff(path []string, key string) (*ResourceAddress, error) { - addr, err := parseResourceAddressInternal(key) - if err != nil { - return nil, err - } - addr.Path = path - return addr, nil -} - -// NewLegacyResourceAddress creates a ResourceAddress from a new-style -// addrs.AbsResource value. -// -// This is provided for shimming purposes so that we can still easily call into -// older functions that expect the ResourceAddress type. -func NewLegacyResourceAddress(addr addrs.AbsResource) *ResourceAddress { - ret := &ResourceAddress{ - Type: addr.Resource.Type, - Name: addr.Resource.Name, - } - - switch addr.Resource.Mode { - case addrs.ManagedResourceMode: - ret.Mode = ManagedResourceMode - case addrs.DataResourceMode: - ret.Mode = DataResourceMode - default: - panic(fmt.Errorf("cannot shim %s to legacy ResourceMode value", addr.Resource.Mode)) - } - - path := make([]string, len(addr.Module)) - for i, step := range addr.Module { - if step.InstanceKey != addrs.NoKey { - // At the time of writing this can't happen because we don't - // ket generate keyed module instances. This legacy codepath must - // be removed before we can support "count" and "for_each" for - // modules. - panic(fmt.Errorf("cannot shim module instance step with key %#v to legacy ResourceAddress.Path", step.InstanceKey)) - } - - path[i] = step.Name - } - ret.Path = path - ret.Index = -1 - - return ret -} - -// NewLegacyResourceInstanceAddress creates a ResourceAddress from a new-style -// addrs.AbsResource value. -// -// This is provided for shimming purposes so that we can still easily call into -// older functions that expect the ResourceAddress type. -func NewLegacyResourceInstanceAddress(addr addrs.AbsResourceInstance) *ResourceAddress { - ret := &ResourceAddress{ - Type: addr.Resource.Resource.Type, - Name: addr.Resource.Resource.Name, - } - - switch addr.Resource.Resource.Mode { - case addrs.ManagedResourceMode: - ret.Mode = ManagedResourceMode - case addrs.DataResourceMode: - ret.Mode = DataResourceMode - default: - panic(fmt.Errorf("cannot shim %s to legacy ResourceMode value", addr.Resource.Resource.Mode)) - } - - path := make([]string, len(addr.Module)) - for i, step := range addr.Module { - if step.InstanceKey != addrs.NoKey { - // At the time of writing this can't happen because we don't - // ket generate keyed module instances. This legacy codepath must - // be removed before we can support "count" and "for_each" for - // modules. - panic(fmt.Errorf("cannot shim module instance step with key %#v to legacy ResourceAddress.Path", step.InstanceKey)) - } - - path[i] = step.Name - } - ret.Path = path - - if addr.Resource.Key == addrs.NoKey { - ret.Index = -1 - } else if ik, ok := addr.Resource.Key.(addrs.IntKey); ok { - ret.Index = int(ik) - } else if _, ok := addr.Resource.Key.(addrs.StringKey); ok { - ret.Index = -1 - } else { - panic(fmt.Errorf("cannot shim resource instance with key %#v to legacy ResourceAddress.Index", addr.Resource.Key)) - } - - return ret -} - -// AbsResourceInstanceAddr converts the receiver, a legacy resource address, to -// the new resource address type addrs.AbsResourceInstance. -// -// This method can be used only on an address that has a resource specification. -// It will panic if called on a module-path-only ResourceAddress. Use -// method HasResourceSpec to check before calling, in contexts where it is -// unclear. -// -// addrs.AbsResourceInstance does not represent the "tainted" and "deposed" -// states, and so if these are present on the receiver then they are discarded. -// -// This is provided for shimming purposes so that we can easily adapt functions -// that are returning the legacy ResourceAddress type, for situations where -// the new type is required. -func (addr *ResourceAddress) AbsResourceInstanceAddr() addrs.AbsResourceInstance { - if !addr.HasResourceSpec() { - panic("AbsResourceInstanceAddr called on ResourceAddress with no resource spec") - } - - ret := addrs.AbsResourceInstance{ - Module: addr.ModuleInstanceAddr(), - Resource: addrs.ResourceInstance{ - Resource: addrs.Resource{ - Type: addr.Type, - Name: addr.Name, - }, - }, - } - - switch addr.Mode { - case ManagedResourceMode: - ret.Resource.Resource.Mode = addrs.ManagedResourceMode - case DataResourceMode: - ret.Resource.Resource.Mode = addrs.DataResourceMode - default: - panic(fmt.Errorf("cannot shim %s to addrs.ResourceMode value", addr.Mode)) - } - - if addr.Index != -1 { - ret.Resource.Key = addrs.IntKey(addr.Index) - } - - return ret -} - -// ModuleInstanceAddr returns the module path portion of the receiver as a -// addrs.ModuleInstance value. -func (addr *ResourceAddress) ModuleInstanceAddr() addrs.ModuleInstance { - path := make(addrs.ModuleInstance, len(addr.Path)) - for i, name := range addr.Path { - path[i] = addrs.ModuleInstanceStep{Name: name} - } - return path -} - -// Contains returns true if and only if the given node is contained within -// the receiver. -// -// Containment is defined in terms of the module and resource heirarchy: -// a resource is contained within its module and any ancestor modules, -// an indexed resource instance is contained with the unindexed resource, etc. -func (addr *ResourceAddress) Contains(other *ResourceAddress) bool { - ourPath := addr.Path - givenPath := other.Path - if len(givenPath) < len(ourPath) { - return false - } - for i := range ourPath { - if ourPath[i] != givenPath[i] { - return false - } - } - - // If the receiver is a whole-module address then the path prefix - // matching is all we need. - if !addr.HasResourceSpec() { - return true - } - - if addr.Type != other.Type || addr.Name != other.Name || addr.Mode != other.Mode { - return false - } - - if addr.Index != -1 && addr.Index != other.Index { - return false - } - - if addr.InstanceTypeSet && (addr.InstanceTypeSet != other.InstanceTypeSet || addr.InstanceType != other.InstanceType) { - return false - } - - return true -} - -// Equals returns true if the receiver matches the given address. -// -// The name of this method is a misnomer, since it doesn't test for exact -// equality. Instead, it tests that the _specified_ parts of each -// address match, treating any unspecified parts as wildcards. -// -// See also Contains, which takes a more heirarchical approach to comparing -// addresses. -func (addr *ResourceAddress) Equals(raw interface{}) bool { - other, ok := raw.(*ResourceAddress) - if !ok { - return false - } - - pathMatch := len(addr.Path) == 0 && len(other.Path) == 0 || - reflect.DeepEqual(addr.Path, other.Path) - - indexMatch := addr.Index == -1 || - other.Index == -1 || - addr.Index == other.Index - - nameMatch := addr.Name == "" || - other.Name == "" || - addr.Name == other.Name - - typeMatch := addr.Type == "" || - other.Type == "" || - addr.Type == other.Type - - // mode is significant only when type is set - modeMatch := addr.Type == "" || - other.Type == "" || - addr.Mode == other.Mode - - return pathMatch && - indexMatch && - addr.InstanceType == other.InstanceType && - nameMatch && - typeMatch && - modeMatch -} - // Less returns true if and only if the receiver should be sorted before // the given address when presenting a list of resource addresses to // an end-user. @@ -504,7 +112,7 @@ func (addr *ResourceAddress) Equals(raw interface{}) bool { // This sort uses lexicographic sorting for most components, but uses // numeric sort for indices, thus causing index 10 to sort after // index 9, rather than after index 1. -func (addr *ResourceAddress) Less(other *ResourceAddress) bool { +func (addr *resourceAddress) Less(other *resourceAddress) bool { switch { @@ -550,14 +158,14 @@ func (addr *ResourceAddress) Less(other *ResourceAddress) bool { } } -func ParseResourceIndex(s string) (int, error) { +func parseResourceIndex(s string) (int, error) { if s == "" { return -1, nil } return strconv.Atoi(s) } -func ParseResourcePath(s string) []string { +func parseResourcePath(s string) []string { if s == "" { return nil } @@ -574,16 +182,16 @@ func ParseResourcePath(s string) []string { return path } -func ParseInstanceType(s string) (InstanceType, error) { +func parseInstanceType(s string) (instanceType, error) { switch s { case "", "primary": - return TypePrimary, nil + return typePrimary, nil case "deposed": - return TypeDeposed, nil + return typeDeposed, nil case "tainted": - return TypeTainted, nil + return typeTainted, nil default: - return TypeInvalid, fmt.Errorf("Unexpected value for InstanceType field: %q", s) + return typeInvalid, fmt.Errorf("Unexpected value for instanceType field: %q", s) } } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_address_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_address_test.go new file mode 100644 index 00000000..170f8a3c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_address_test.go @@ -0,0 +1,314 @@ +package terraform + +import ( + "fmt" + "reflect" + "testing" +) + +func TestParseResourceAddress(t *testing.T) { + cases := map[string]struct { + Input string + Expected *resourceAddress + Output string + Err bool + }{ + "implicit primary managed instance, no specific index": { + "aws_instance.foo", + &resourceAddress{ + Mode: ManagedResourceMode, + Type: "aws_instance", + Name: "foo", + InstanceType: typePrimary, + Index: -1, + }, + "", + false, + }, + "implicit primary data instance, no specific index": { + "data.aws_instance.foo", + &resourceAddress{ + Mode: DataResourceMode, + Type: "aws_instance", + Name: "foo", + InstanceType: typePrimary, + Index: -1, + }, + "", + false, + }, + "implicit primary, explicit index": { + "aws_instance.foo[2]", + &resourceAddress{ + Mode: ManagedResourceMode, + Type: "aws_instance", + Name: "foo", + InstanceType: typePrimary, + Index: 2, + }, + "", + false, + }, + "implicit primary, explicit index over ten": { + "aws_instance.foo[12]", + &resourceAddress{ + Mode: ManagedResourceMode, + Type: "aws_instance", + Name: "foo", + InstanceType: typePrimary, + Index: 12, + }, + "", + false, + }, + "explicit primary, explicit index": { + "aws_instance.foo.primary[2]", + &resourceAddress{ + Mode: ManagedResourceMode, + Type: "aws_instance", + Name: "foo", + InstanceType: typePrimary, + InstanceTypeSet: true, + Index: 2, + }, + "", + false, + }, + "tainted": { + "aws_instance.foo.tainted", + &resourceAddress{ + Mode: ManagedResourceMode, + Type: "aws_instance", + Name: "foo", + InstanceType: typeTainted, + InstanceTypeSet: true, + Index: -1, + }, + "", + false, + }, + "deposed": { + "aws_instance.foo.deposed", + &resourceAddress{ + Mode: ManagedResourceMode, + Type: "aws_instance", + Name: "foo", + InstanceType: typeDeposed, + InstanceTypeSet: true, + Index: -1, + }, + "", + false, + }, + "with a hyphen": { + "aws_instance.foo-bar", + &resourceAddress{ + Mode: ManagedResourceMode, + Type: "aws_instance", + Name: "foo-bar", + InstanceType: typePrimary, + Index: -1, + }, + "", + false, + }, + "managed in a module": { + "module.child.aws_instance.foo", + &resourceAddress{ + Path: []string{"child"}, + Mode: ManagedResourceMode, + Type: "aws_instance", + Name: "foo", + InstanceType: typePrimary, + Index: -1, + }, + "", + false, + }, + "data in a module": { + "module.child.data.aws_instance.foo", + &resourceAddress{ + Path: []string{"child"}, + Mode: DataResourceMode, + Type: "aws_instance", + Name: "foo", + InstanceType: typePrimary, + Index: -1, + }, + "", + false, + }, + "nested modules": { + "module.a.module.b.module.forever.aws_instance.foo", + &resourceAddress{ + Path: []string{"a", "b", "forever"}, + Mode: ManagedResourceMode, + Type: "aws_instance", + Name: "foo", + InstanceType: typePrimary, + Index: -1, + }, + "", + false, + }, + "just a module": { + "module.a", + &resourceAddress{ + Path: []string{"a"}, + Type: "", + Name: "", + InstanceType: typePrimary, + Index: -1, + }, + "", + false, + }, + "just a nested module": { + "module.a.module.b", + &resourceAddress{ + Path: []string{"a", "b"}, + Type: "", + Name: "", + InstanceType: typePrimary, + Index: -1, + }, + "", + false, + }, + "module missing resource type": { + "module.name.foo", + nil, + "", + true, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + out, err := parseResourceAddress(tc.Input) + if (err != nil) != tc.Err { + t.Fatalf("%s: unexpected err: %#v", tn, err) + } + if tc.Err { + return + } + + if !reflect.DeepEqual(out, tc.Expected) { + t.Fatalf("bad: %q\n\nexpected:\n%#v\n\ngot:\n%#v", tn, tc.Expected, out) + } + + expected := tc.Input + if tc.Output != "" { + expected = tc.Output + } + if out.String() != expected { + t.Fatalf("bad: %q\n\nexpected: %s\n\ngot: %s", tn, expected, out) + } + }) + } +} + +func TestResourceAddressLess(t *testing.T) { + tests := []struct { + A string + B string + Want bool + }{ + { + "foo.bar", + "module.baz.foo.bar", + true, + }, + { + "module.baz.foo.bar", + "zzz.bar", // would sort after "module" in lexicographical sort + false, + }, + { + "module.baz.foo.bar", + "module.baz.foo.bar", + false, + }, + { + "module.baz.foo.bar", + "module.boz.foo.bar", + true, + }, + { + "module.boz.foo.bar", + "module.baz.foo.bar", + false, + }, + { + "a.b", + "b.c", + true, + }, + { + "a.b", + "a.c", + true, + }, + { + "c.b", + "b.c", + false, + }, + { + "a.b[9]", + "a.b[10]", + true, + }, + { + "b.b[9]", + "a.b[10]", + false, + }, + { + "a.b", + "a.b.deposed", + true, + }, + { + "a.b.tainted", + "a.b.deposed", + true, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%s < %s", test.A, test.B), func(t *testing.T) { + addrA, err := parseResourceAddress(test.A) + if err != nil { + t.Fatal(err) + } + addrB, err := parseResourceAddress(test.B) + if err != nil { + t.Fatal(err) + } + got := addrA.Less(addrB) + invGot := addrB.Less(addrA) + if got != test.Want { + t.Errorf( + "wrong result\ntest: %s < %s\ngot: %#v\nwant: %#v", + test.A, test.B, got, test.Want, + ) + } + if test.A != test.B { // inverse test doesn't apply when equal + if invGot != !test.Want { + t.Errorf( + "wrong inverse result\ntest: %s < %s\ngot: %#v\nwant: %#v", + test.B, test.A, invGot, !test.Want, + ) + } + } else { + if invGot != test.Want { + t.Errorf( + "wrong inverse result\ntest: %s < %s\ngot: %#v\nwant: %#v", + test.B, test.A, invGot, test.Want, + ) + } + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_provider.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_provider.go index fec45967..ece8fc66 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_provider.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_provider.go @@ -1,182 +1,5 @@ package terraform -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - - "github.com/hashicorp/terraform-plugin-sdk/internal/plugin/discovery" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" -) - -// ResourceProvider is an interface that must be implemented by any -// resource provider: the thing that creates and manages the resources in -// a Terraform configuration. -// -// Important implementation note: All returned pointers, such as -// *ResourceConfig, *InstanceState, *InstanceDiff, etc. must not point to -// shared data. Terraform is highly parallel and assumes that this data is safe -// to read/write in parallel so it must be unique references. Note that it is -// safe to return arguments as results, however. -type ResourceProvider interface { - /********************************************************************* - * Functions related to the provider - *********************************************************************/ - - // GetSchema returns the config schema for the main provider - // configuration, as would appear in a "provider" block in the - // configuration files. - // - // Currently not all providers support schema. Callers must therefore - // first call Resources and DataSources and ensure that at least one - // resource or data source has the SchemaAvailable flag set. - GetSchema(*ProviderSchemaRequest) (*ProviderSchema, error) - - // Input was used prior to v0.12 to ask the provider to prompt the user - // for input to complete the configuration. - // - // From v0.12 onwards this method is never called because Terraform Core - // is able to handle the necessary input logic itself based on the - // schema returned from GetSchema. - Input(UIInput, *ResourceConfig) (*ResourceConfig, error) - - // Validate is called once at the beginning with the raw configuration - // (no interpolation done) and can return a list of warnings and/or - // errors. - // - // This is called once with the provider configuration only. It may not - // be called at all if no provider configuration is given. - // - // This should not assume that any values of the configurations are valid. - // The primary use case of this call is to check that required keys are - // set. - Validate(*ResourceConfig) ([]string, []error) - - // Configure configures the provider itself with the configuration - // given. This is useful for setting things like access keys. - // - // This won't be called at all if no provider configuration is given. - // - // Configure returns an error if it occurred. - Configure(*ResourceConfig) error - - // Resources returns all the available resource types that this provider - // knows how to manage. - Resources() []ResourceType - - // Stop is called when the provider should halt any in-flight actions. - // - // This can be used to make a nicer Ctrl-C experience for Terraform. - // Even if this isn't implemented to do anything (just returns nil), - // Terraform will still cleanly stop after the currently executing - // graph node is complete. However, this API can be used to make more - // efficient halts. - // - // Stop doesn't have to and shouldn't block waiting for in-flight actions - // to complete. It should take any action it wants and return immediately - // acknowledging it has received the stop request. Terraform core will - // automatically not make any further API calls to the provider soon - // after Stop is called (technically exactly once the currently executing - // graph nodes are complete). - // - // The error returned, if non-nil, is assumed to mean that signaling the - // stop somehow failed and that the user should expect potentially waiting - // a longer period of time. - Stop() error - - /********************************************************************* - * Functions related to individual resources - *********************************************************************/ - - // ValidateResource is called once at the beginning with the raw - // configuration (no interpolation done) and can return a list of warnings - // and/or errors. - // - // This is called once per resource. - // - // This should not assume any of the values in the resource configuration - // are valid since it is possible they have to be interpolated still. - // The primary use case of this call is to check that the required keys - // are set and that the general structure is correct. - ValidateResource(string, *ResourceConfig) ([]string, []error) - - // Apply applies a diff to a specific resource and returns the new - // resource state along with an error. - // - // If the resource state given has an empty ID, then a new resource - // is expected to be created. - Apply( - *InstanceInfo, - *InstanceState, - *InstanceDiff) (*InstanceState, error) - - // Diff diffs a resource versus a desired state and returns - // a diff. - Diff( - *InstanceInfo, - *InstanceState, - *ResourceConfig) (*InstanceDiff, error) - - // Refresh refreshes a resource and updates all of its attributes - // with the latest information. - Refresh(*InstanceInfo, *InstanceState) (*InstanceState, error) - - /********************************************************************* - * Functions related to importing - *********************************************************************/ - - // ImportState requests that the given resource be imported. - // - // The returned InstanceState only requires ID be set. Importing - // will always call Refresh after the state to complete it. - // - // IMPORTANT: InstanceState doesn't have the resource type attached - // to it. A type must be specified on the state via the Ephemeral - // field on the state. - // - // This function can return multiple states. Normally, an import - // will map 1:1 to a physical resource. However, some resources map - // to multiple. For example, an AWS security group may contain many rules. - // Each rule is represented by a separate resource in Terraform, - // therefore multiple states are returned. - ImportState(*InstanceInfo, string) ([]*InstanceState, error) - - /********************************************************************* - * Functions related to data resources - *********************************************************************/ - - // ValidateDataSource is called once at the beginning with the raw - // configuration (no interpolation done) and can return a list of warnings - // and/or errors. - // - // This is called once per data source instance. - // - // This should not assume any of the values in the resource configuration - // are valid since it is possible they have to be interpolated still. - // The primary use case of this call is to check that the required keys - // are set and that the general structure is correct. - ValidateDataSource(string, *ResourceConfig) ([]string, []error) - - // DataSources returns all of the available data sources that this - // provider implements. - DataSources() []DataSource - - // ReadDataDiff produces a diff that represents the state that will - // be produced when the given data source is read using a later call - // to ReadDataApply. - ReadDataDiff(*InstanceInfo, *ResourceConfig) (*InstanceDiff, error) - - // ReadDataApply initializes a data instance using the configuration - // in a diff produced by ReadDataDiff. - ReadDataApply(*InstanceInfo, *InstanceDiff) (*InstanceState, error) -} - -// ResourceProviderCloser is an interface that providers that can close -// connections that aren't needed anymore must implement. -type ResourceProviderCloser interface { - Close() error -} - // ResourceType is a type of resource that a resource provider can manage. type ResourceType struct { Name string // Name of the resource, example "instance" (no provider prefix) @@ -201,119 +24,3 @@ type DataSource struct { // the plugin protocol. SchemaAvailable bool } - -// ResourceProviderResolver is an interface implemented by objects that are -// able to resolve a given set of resource provider version constraints -// into ResourceProviderFactory callbacks. -type ResourceProviderResolver interface { - // Given a constraint map, return a ResourceProviderFactory for each - // requested provider. If some or all of the constraints cannot be - // satisfied, return a non-nil slice of errors describing the problems. - ResolveProviders(reqd discovery.PluginRequirements) (map[string]ResourceProviderFactory, []error) -} - -// ResourceProviderResolverFunc wraps a callback function and turns it into -// a ResourceProviderResolver implementation, for convenience in situations -// where a function and its associated closure are sufficient as a resolver -// implementation. -type ResourceProviderResolverFunc func(reqd discovery.PluginRequirements) (map[string]ResourceProviderFactory, []error) - -// ResolveProviders implements ResourceProviderResolver by calling the -// wrapped function. -func (f ResourceProviderResolverFunc) ResolveProviders(reqd discovery.PluginRequirements) (map[string]ResourceProviderFactory, []error) { - return f(reqd) -} - -// ResourceProviderResolverFixed returns a ResourceProviderResolver that -// has a fixed set of provider factories provided by the caller. The returned -// resolver ignores version constraints entirely and just returns the given -// factory for each requested provider name. -// -// This function is primarily used in tests, to provide mock providers or -// in-process providers under test. -func ResourceProviderResolverFixed(factories map[string]ResourceProviderFactory) ResourceProviderResolver { - return ResourceProviderResolverFunc(func(reqd discovery.PluginRequirements) (map[string]ResourceProviderFactory, []error) { - ret := make(map[string]ResourceProviderFactory, len(reqd)) - var errs []error - for name := range reqd { - if factory, exists := factories[name]; exists { - ret[name] = factory - } else { - errs = append(errs, fmt.Errorf("provider %q is not available", name)) - } - } - return ret, errs - }) -} - -// ResourceProviderFactory is a function type that creates a new instance -// of a resource provider. -type ResourceProviderFactory func() (ResourceProvider, error) - -// ResourceProviderFactoryFixed is a helper that creates a -// ResourceProviderFactory that just returns some fixed provider. -func ResourceProviderFactoryFixed(p ResourceProvider) ResourceProviderFactory { - return func() (ResourceProvider, error) { - return p, nil - } -} - -func ProviderHasResource(p ResourceProvider, n string) bool { - for _, rt := range p.Resources() { - if rt.Name == n { - return true - } - } - - return false -} - -func ProviderHasDataSource(p ResourceProvider, n string) bool { - for _, rt := range p.DataSources() { - if rt.Name == n { - return true - } - } - - return false -} - -// resourceProviderFactories matches available plugins to the given version -// requirements to produce a map of compatible provider plugins if possible, -// or an error if the currently-available plugins are insufficient. -// -// This should be called only with configurations that have passed calls -// to config.Validate(), which ensures that all of the given version -// constraints are valid. It will panic if any invalid constraints are present. -func resourceProviderFactories(resolver providers.Resolver, reqd discovery.PluginRequirements) (map[string]providers.Factory, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - ret, errs := resolver.ResolveProviders(reqd) - if errs != nil { - diags = diags.Append( - tfdiags.Sourceless(tfdiags.Error, - "Could not satisfy plugin requirements", - errPluginInit, - ), - ) - - for _, err := range errs { - diags = diags.Append(err) - } - - return nil, diags - } - - return ret, nil -} - -const errPluginInit = ` -Plugin reinitialization required. Please run "terraform init". - -Plugins are external binaries that Terraform uses to access and manipulate -resources. The configuration provided requires plugins which can't be located, -don't satisfy the version constraints, or are otherwise incompatible. - -Terraform automatically discovers provider requirements from your -configuration, including providers used in child modules. To see the -requirements and constraints from each module, run "terraform providers". -` diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_provider_mock.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_provider_mock.go deleted file mode 100644 index 4000e3d2..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_provider_mock.go +++ /dev/null @@ -1,315 +0,0 @@ -package terraform - -import ( - "sync" -) - -// MockResourceProvider implements ResourceProvider but mocks out all the -// calls for testing purposes. -type MockResourceProvider struct { - sync.Mutex - - // Anything you want, in case you need to store extra data with the mock. - Meta interface{} - - CloseCalled bool - CloseError error - GetSchemaCalled bool - GetSchemaRequest *ProviderSchemaRequest - GetSchemaReturn *ProviderSchema - GetSchemaReturnError error - InputCalled bool - InputInput UIInput - InputConfig *ResourceConfig - InputReturnConfig *ResourceConfig - InputReturnError error - InputFn func(UIInput, *ResourceConfig) (*ResourceConfig, error) - ApplyCalled bool - ApplyInfo *InstanceInfo - ApplyState *InstanceState - ApplyDiff *InstanceDiff - ApplyFn func(*InstanceInfo, *InstanceState, *InstanceDiff) (*InstanceState, error) - ApplyReturn *InstanceState - ApplyReturnError error - ConfigureCalled bool - ConfigureConfig *ResourceConfig - ConfigureFn func(*ResourceConfig) error - ConfigureReturnError error - DiffCalled bool - DiffInfo *InstanceInfo - DiffState *InstanceState - DiffDesired *ResourceConfig - DiffFn func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) - DiffReturn *InstanceDiff - DiffReturnError error - RefreshCalled bool - RefreshInfo *InstanceInfo - RefreshState *InstanceState - RefreshFn func(*InstanceInfo, *InstanceState) (*InstanceState, error) - RefreshReturn *InstanceState - RefreshReturnError error - ResourcesCalled bool - ResourcesReturn []ResourceType - ReadDataApplyCalled bool - ReadDataApplyInfo *InstanceInfo - ReadDataApplyDiff *InstanceDiff - ReadDataApplyFn func(*InstanceInfo, *InstanceDiff) (*InstanceState, error) - ReadDataApplyReturn *InstanceState - ReadDataApplyReturnError error - ReadDataDiffCalled bool - ReadDataDiffInfo *InstanceInfo - ReadDataDiffDesired *ResourceConfig - ReadDataDiffFn func(*InstanceInfo, *ResourceConfig) (*InstanceDiff, error) - ReadDataDiffReturn *InstanceDiff - ReadDataDiffReturnError error - StopCalled bool - StopFn func() error - StopReturnError error - DataSourcesCalled bool - DataSourcesReturn []DataSource - ValidateCalled bool - ValidateConfig *ResourceConfig - ValidateFn func(*ResourceConfig) ([]string, []error) - ValidateReturnWarns []string - ValidateReturnErrors []error - ValidateResourceFn func(string, *ResourceConfig) ([]string, []error) - ValidateResourceCalled bool - ValidateResourceType string - ValidateResourceConfig *ResourceConfig - ValidateResourceReturnWarns []string - ValidateResourceReturnErrors []error - ValidateDataSourceFn func(string, *ResourceConfig) ([]string, []error) - ValidateDataSourceCalled bool - ValidateDataSourceType string - ValidateDataSourceConfig *ResourceConfig - ValidateDataSourceReturnWarns []string - ValidateDataSourceReturnErrors []error - - ImportStateCalled bool - ImportStateInfo *InstanceInfo - ImportStateID string - ImportStateReturn []*InstanceState - ImportStateReturnError error - ImportStateFn func(*InstanceInfo, string) ([]*InstanceState, error) -} - -func (p *MockResourceProvider) Close() error { - p.CloseCalled = true - return p.CloseError -} - -func (p *MockResourceProvider) GetSchema(req *ProviderSchemaRequest) (*ProviderSchema, error) { - p.Lock() - defer p.Unlock() - - p.GetSchemaCalled = true - p.GetSchemaRequest = req - return p.GetSchemaReturn, p.GetSchemaReturnError -} - -func (p *MockResourceProvider) Input( - input UIInput, c *ResourceConfig) (*ResourceConfig, error) { - p.Lock() - defer p.Unlock() - p.InputCalled = true - p.InputInput = input - p.InputConfig = c - if p.InputFn != nil { - return p.InputFn(input, c) - } - return p.InputReturnConfig, p.InputReturnError -} - -func (p *MockResourceProvider) Validate(c *ResourceConfig) ([]string, []error) { - p.Lock() - defer p.Unlock() - - p.ValidateCalled = true - p.ValidateConfig = c - if p.ValidateFn != nil { - return p.ValidateFn(c) - } - return p.ValidateReturnWarns, p.ValidateReturnErrors -} - -func (p *MockResourceProvider) ValidateResource(t string, c *ResourceConfig) ([]string, []error) { - p.Lock() - defer p.Unlock() - - p.ValidateResourceCalled = true - p.ValidateResourceType = t - p.ValidateResourceConfig = c - - if p.ValidateResourceFn != nil { - return p.ValidateResourceFn(t, c) - } - - return p.ValidateResourceReturnWarns, p.ValidateResourceReturnErrors -} - -func (p *MockResourceProvider) Configure(c *ResourceConfig) error { - p.Lock() - defer p.Unlock() - - p.ConfigureCalled = true - p.ConfigureConfig = c - - if p.ConfigureFn != nil { - return p.ConfigureFn(c) - } - - return p.ConfigureReturnError -} - -func (p *MockResourceProvider) Stop() error { - p.Lock() - defer p.Unlock() - - p.StopCalled = true - if p.StopFn != nil { - return p.StopFn() - } - - return p.StopReturnError -} - -func (p *MockResourceProvider) Apply( - info *InstanceInfo, - state *InstanceState, - diff *InstanceDiff) (*InstanceState, error) { - // We only lock while writing data. Reading is fine - p.Lock() - p.ApplyCalled = true - p.ApplyInfo = info - p.ApplyState = state - p.ApplyDiff = diff - p.Unlock() - - if p.ApplyFn != nil { - return p.ApplyFn(info, state, diff) - } - - return p.ApplyReturn.DeepCopy(), p.ApplyReturnError -} - -func (p *MockResourceProvider) Diff( - info *InstanceInfo, - state *InstanceState, - desired *ResourceConfig) (*InstanceDiff, error) { - p.Lock() - defer p.Unlock() - - p.DiffCalled = true - p.DiffInfo = info - p.DiffState = state - p.DiffDesired = desired - - if p.DiffFn != nil { - return p.DiffFn(info, state, desired) - } - - return p.DiffReturn.DeepCopy(), p.DiffReturnError -} - -func (p *MockResourceProvider) Refresh( - info *InstanceInfo, - s *InstanceState) (*InstanceState, error) { - p.Lock() - defer p.Unlock() - - p.RefreshCalled = true - p.RefreshInfo = info - p.RefreshState = s - - if p.RefreshFn != nil { - return p.RefreshFn(info, s) - } - - return p.RefreshReturn.DeepCopy(), p.RefreshReturnError -} - -func (p *MockResourceProvider) Resources() []ResourceType { - p.Lock() - defer p.Unlock() - - p.ResourcesCalled = true - return p.ResourcesReturn -} - -func (p *MockResourceProvider) ImportState(info *InstanceInfo, id string) ([]*InstanceState, error) { - p.Lock() - defer p.Unlock() - - p.ImportStateCalled = true - p.ImportStateInfo = info - p.ImportStateID = id - if p.ImportStateFn != nil { - return p.ImportStateFn(info, id) - } - - var result []*InstanceState - if p.ImportStateReturn != nil { - result = make([]*InstanceState, len(p.ImportStateReturn)) - for i, v := range p.ImportStateReturn { - result[i] = v.DeepCopy() - } - } - - return result, p.ImportStateReturnError -} - -func (p *MockResourceProvider) ValidateDataSource(t string, c *ResourceConfig) ([]string, []error) { - p.Lock() - defer p.Unlock() - - p.ValidateDataSourceCalled = true - p.ValidateDataSourceType = t - p.ValidateDataSourceConfig = c - - if p.ValidateDataSourceFn != nil { - return p.ValidateDataSourceFn(t, c) - } - - return p.ValidateDataSourceReturnWarns, p.ValidateDataSourceReturnErrors -} - -func (p *MockResourceProvider) ReadDataDiff( - info *InstanceInfo, - desired *ResourceConfig) (*InstanceDiff, error) { - p.Lock() - defer p.Unlock() - - p.ReadDataDiffCalled = true - p.ReadDataDiffInfo = info - p.ReadDataDiffDesired = desired - if p.ReadDataDiffFn != nil { - return p.ReadDataDiffFn(info, desired) - } - - return p.ReadDataDiffReturn.DeepCopy(), p.ReadDataDiffReturnError -} - -func (p *MockResourceProvider) ReadDataApply( - info *InstanceInfo, - d *InstanceDiff) (*InstanceState, error) { - p.Lock() - defer p.Unlock() - - p.ReadDataApplyCalled = true - p.ReadDataApplyInfo = info - p.ReadDataApplyDiff = d - - if p.ReadDataApplyFn != nil { - return p.ReadDataApplyFn(info, d) - } - - return p.ReadDataApplyReturn.DeepCopy(), p.ReadDataApplyReturnError -} - -func (p *MockResourceProvider) DataSources() []DataSource { - p.Lock() - defer p.Unlock() - - p.DataSourcesCalled = true - return p.DataSourcesReturn -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_provisioner.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_provisioner.go deleted file mode 100644 index 74ee2a94..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_provisioner.go +++ /dev/null @@ -1,70 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/provisioners" -) - -// ResourceProvisioner is an interface that must be implemented by any -// resource provisioner: the thing that initializes resources in -// a Terraform configuration. -type ResourceProvisioner interface { - // GetConfigSchema returns the schema for the provisioner type's main - // configuration block. This is called prior to Validate to enable some - // basic structural validation to be performed automatically and to allow - // the configuration to be properly extracted from potentially-ambiguous - // configuration file formats. - GetConfigSchema() (*configschema.Block, error) - - // Validate is called once at the beginning with the raw - // configuration (no interpolation done) and can return a list of warnings - // and/or errors. - // - // This is called once per resource. - // - // This should not assume any of the values in the resource configuration - // are valid since it is possible they have to be interpolated still. - // The primary use case of this call is to check that the required keys - // are set and that the general structure is correct. - Validate(*ResourceConfig) ([]string, []error) - - // Apply runs the provisioner on a specific resource and returns the new - // resource state along with an error. Instead of a diff, the ResourceConfig - // is provided since provisioners only run after a resource has been - // newly created. - Apply(UIOutput, *InstanceState, *ResourceConfig) error - - // Stop is called when the provisioner should halt any in-flight actions. - // - // This can be used to make a nicer Ctrl-C experience for Terraform. - // Even if this isn't implemented to do anything (just returns nil), - // Terraform will still cleanly stop after the currently executing - // graph node is complete. However, this API can be used to make more - // efficient halts. - // - // Stop doesn't have to and shouldn't block waiting for in-flight actions - // to complete. It should take any action it wants and return immediately - // acknowledging it has received the stop request. Terraform core will - // automatically not make any further API calls to the provider soon - // after Stop is called (technically exactly once the currently executing - // graph nodes are complete). - // - // The error returned, if non-nil, is assumed to mean that signaling the - // stop somehow failed and that the user should expect potentially waiting - // a longer period of time. - Stop() error -} - -// ResourceProvisionerCloser is an interface that provisioners that can close -// connections that aren't needed anymore must implement. -type ResourceProvisionerCloser interface { - Close() error -} - -// ResourceProvisionerFactory is a function type that creates a new instance -// of a resource provisioner. -type ResourceProvisionerFactory func() (ResourceProvisioner, error) - -// ProvisionerFactory is a function type that creates a new instance -// of a provisioners.Interface. -type ProvisionerFactory = provisioners.Factory diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_provisioner_mock.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_provisioner_mock.go deleted file mode 100644 index ed6f241b..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_provisioner_mock.go +++ /dev/null @@ -1,87 +0,0 @@ -package terraform - -import ( - "sync" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" -) - -// MockResourceProvisioner implements ResourceProvisioner but mocks out all the -// calls for testing purposes. -type MockResourceProvisioner struct { - sync.Mutex - // Anything you want, in case you need to store extra data with the mock. - Meta interface{} - - GetConfigSchemaCalled bool - GetConfigSchemaReturnSchema *configschema.Block - GetConfigSchemaReturnError error - - ApplyCalled bool - ApplyOutput UIOutput - ApplyState *InstanceState - ApplyConfig *ResourceConfig - ApplyFn func(*InstanceState, *ResourceConfig) error - ApplyReturnError error - - ValidateCalled bool - ValidateConfig *ResourceConfig - ValidateFn func(c *ResourceConfig) ([]string, []error) - ValidateReturnWarns []string - ValidateReturnErrors []error - - StopCalled bool - StopFn func() error - StopReturnError error -} - -var _ ResourceProvisioner = (*MockResourceProvisioner)(nil) - -func (p *MockResourceProvisioner) GetConfigSchema() (*configschema.Block, error) { - p.GetConfigSchemaCalled = true - return p.GetConfigSchemaReturnSchema, p.GetConfigSchemaReturnError -} - -func (p *MockResourceProvisioner) Validate(c *ResourceConfig) ([]string, []error) { - p.Lock() - defer p.Unlock() - - p.ValidateCalled = true - p.ValidateConfig = c - if p.ValidateFn != nil { - return p.ValidateFn(c) - } - return p.ValidateReturnWarns, p.ValidateReturnErrors -} - -func (p *MockResourceProvisioner) Apply( - output UIOutput, - state *InstanceState, - c *ResourceConfig) error { - p.Lock() - - p.ApplyCalled = true - p.ApplyOutput = output - p.ApplyState = state - p.ApplyConfig = c - if p.ApplyFn != nil { - fn := p.ApplyFn - p.Unlock() - return fn(state, c) - } - - defer p.Unlock() - return p.ApplyReturnError -} - -func (p *MockResourceProvisioner) Stop() error { - p.Lock() - defer p.Unlock() - - p.StopCalled = true - if p.StopFn != nil { - return p.StopFn() - } - - return p.StopReturnError -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_test.go new file mode 100644 index 00000000..35a90b66 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/resource_test.go @@ -0,0 +1,674 @@ +package terraform + +import ( + "fmt" + "reflect" + "testing" + + "github.com/hashicorp/go-cty/cty" + "github.com/mitchellh/reflectwalk" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/hcl2shim" +) + +func TestResourceConfigGet(t *testing.T) { + fooStringSchema := &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, + } + fooListSchema := &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.List(cty.Number), Optional: true}, + }, + } + + cases := []struct { + Config cty.Value + Schema *configschema.Block + Key string + Value interface{} + }{ + { + Config: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + }), + Schema: fooStringSchema, + Key: "foo", + Value: "bar", + }, + + { + Config: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.UnknownVal(cty.String), + }), + Schema: fooStringSchema, + Key: "foo", + Value: hcl2shim.UnknownVariableValue, + }, + + { + Config: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{ + cty.NumberIntVal(1), + cty.NumberIntVal(2), + cty.NumberIntVal(5), + }), + }), + Schema: fooListSchema, + Key: "foo.0", + Value: 1, + }, + + { + Config: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{ + cty.NumberIntVal(1), + cty.NumberIntVal(2), + cty.NumberIntVal(5), + }), + }), + Schema: fooListSchema, + Key: "foo.5", + Value: nil, + }, + + { + Config: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{ + cty.NumberIntVal(1), + cty.NumberIntVal(2), + cty.NumberIntVal(5), + }), + }), + Schema: fooListSchema, + Key: "foo.-1", + Value: nil, + }, + + // get from map + { + Config: cty.ObjectVal(map[string]cty.Value{ + "mapname": cty.ListVal([]cty.Value{ + cty.MapVal(map[string]cty.Value{ + "key": cty.NumberIntVal(1), + }), + }), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "mapname": {Type: cty.List(cty.Map(cty.Number)), Optional: true}, + }, + }, + Key: "mapname.0.key", + Value: 1, + }, + + // get from map with dot in key + { + Config: cty.ObjectVal(map[string]cty.Value{ + "mapname": cty.ListVal([]cty.Value{ + cty.MapVal(map[string]cty.Value{ + "key.name": cty.NumberIntVal(1), + }), + }), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "mapname": {Type: cty.List(cty.Map(cty.Number)), Optional: true}, + }, + }, + Key: "mapname.0.key.name", + Value: 1, + }, + + // get from map with overlapping key names + { + Config: cty.ObjectVal(map[string]cty.Value{ + "mapname": cty.ListVal([]cty.Value{ + cty.MapVal(map[string]cty.Value{ + "key.name": cty.NumberIntVal(1), + "key.name.2": cty.NumberIntVal(2), + }), + }), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "mapname": {Type: cty.List(cty.Map(cty.Number)), Optional: true}, + }, + }, + Key: "mapname.0.key.name.2", + Value: 2, + }, + { + Config: cty.ObjectVal(map[string]cty.Value{ + "mapname": cty.ListVal([]cty.Value{ + cty.MapVal(map[string]cty.Value{ + "key.name": cty.NumberIntVal(1), + "key.name.foo": cty.NumberIntVal(2), + }), + }), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "mapname": {Type: cty.List(cty.Map(cty.Number)), Optional: true}, + }, + }, + Key: "mapname.0.key.name", + Value: 1, + }, + { + Config: cty.ObjectVal(map[string]cty.Value{ + "mapname": cty.ListVal([]cty.Value{ + cty.MapVal(map[string]cty.Value{ + "listkey": cty.ListVal([]cty.Value{ + cty.MapVal(map[string]cty.Value{ + "key": cty.NumberIntVal(3), + }), + }), + }), + }), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "mapname": {Type: cty.List(cty.Map(cty.List(cty.Map(cty.Number)))), Optional: true}, + }, + }, + Key: "mapname.0.listkey.0.key", + Value: 3, + }, + } + + for i, tc := range cases { + rc := NewResourceConfigShimmed(tc.Config, tc.Schema) + + // Test getting a key + t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) { + v, ok := rc.Get(tc.Key) + if ok && v == nil { + t.Fatal("(nil, true) returned from Get") + } + + if !reflect.DeepEqual(v, tc.Value) { + t.Fatalf("%d bad: %#v", i, v) + } + }) + + // Test copying and equality + t.Run(fmt.Sprintf("copy-and-equal-%d", i), func(t *testing.T) { + copy := rc.DeepCopy() + if !reflect.DeepEqual(copy, rc) { + t.Fatalf("bad:\n\n%#v\n\n%#v", copy, rc) + } + + if !copy.Equal(rc) { + t.Fatalf("copy != rc:\n\n%#v\n\n%#v", copy, rc) + } + if !rc.Equal(copy) { + t.Fatalf("rc != copy:\n\n%#v\n\n%#v", copy, rc) + } + }) + } +} + +func TestResourceConfigDeepCopy_nil(t *testing.T) { + var nilRc *ResourceConfig + actual := nilRc.DeepCopy() + if actual != nil { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceConfigDeepCopy_nilComputed(t *testing.T) { + rc := &ResourceConfig{} + actual := rc.DeepCopy() + if actual.ComputedKeys != nil { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceConfigEqual_nil(t *testing.T) { + var nilRc *ResourceConfig + notNil := NewResourceConfigShimmed(cty.EmptyObjectVal, &configschema.Block{}) + + if nilRc.Equal(notNil) { + t.Fatal("should not be equal") + } + + if notNil.Equal(nilRc) { + t.Fatal("should not be equal") + } +} + +func TestResourceConfigEqual_computedKeyOrder(t *testing.T) { + v := cty.ObjectVal(map[string]cty.Value{ + "foo": cty.UnknownVal(cty.String), + }) + schema := &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, + } + rc := NewResourceConfigShimmed(v, schema) + rc2 := NewResourceConfigShimmed(v, schema) + + // Set the computed keys manually to force ordering to differ + rc.ComputedKeys = []string{"foo", "bar"} + rc2.ComputedKeys = []string{"bar", "foo"} + + if !rc.Equal(rc2) { + t.Fatal("should be equal") + } +} + +func TestUnknownCheckWalker(t *testing.T) { + cases := []struct { + Name string + Input interface{} + Result bool + }{ + { + "primitive", + 42, + false, + }, + + { + "primitive computed", + hcl2shim.UnknownVariableValue, + true, + }, + + { + "list", + []interface{}{"foo", hcl2shim.UnknownVariableValue}, + true, + }, + + { + "nested list", + []interface{}{ + "foo", + []interface{}{hcl2shim.UnknownVariableValue}, + }, + true, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + var w unknownCheckWalker + if err := reflectwalk.Walk(tc.Input, &w); err != nil { + t.Fatalf("err: %s", err) + } + + if w.Unknown != tc.Result { + t.Fatalf("bad: %v", w.Unknown) + } + }) + } +} + +func TestNewResourceConfigShimmed(t *testing.T) { + for _, tc := range []struct { + Name string + Val cty.Value + Schema *configschema.Block + Expected *ResourceConfig + }{ + { + Name: "empty object", + Val: cty.NullVal(cty.EmptyObject), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Optional: true, + }, + }, + }, + Expected: &ResourceConfig{ + Raw: map[string]interface{}{}, + Config: map[string]interface{}{}, + }, + }, + { + Name: "basic", + Val: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Optional: true, + }, + }, + }, + Expected: &ResourceConfig{ + Raw: map[string]interface{}{ + "foo": "bar", + }, + Config: map[string]interface{}{ + "foo": "bar", + }, + }, + }, + { + Name: "null string", + Val: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.NullVal(cty.String), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Optional: true, + }, + }, + }, + Expected: &ResourceConfig{ + Raw: map[string]interface{}{}, + Config: map[string]interface{}{}, + }, + }, + { + Name: "unknown string", + Val: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.UnknownVal(cty.String), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Optional: true, + }, + }, + }, + Expected: &ResourceConfig{ + ComputedKeys: []string{"foo"}, + Raw: map[string]interface{}{ + "foo": hcl2shim.UnknownVariableValue, + }, + Config: map[string]interface{}{ + "foo": hcl2shim.UnknownVariableValue, + }, + }, + }, + { + Name: "unknown collections", + Val: cty.ObjectVal(map[string]cty.Value{ + "bar": cty.UnknownVal(cty.Map(cty.String)), + "baz": cty.UnknownVal(cty.List(cty.String)), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bar": { + Type: cty.Map(cty.String), + Required: true, + }, + "baz": { + Type: cty.List(cty.String), + Optional: true, + }, + }, + }, + Expected: &ResourceConfig{ + ComputedKeys: []string{"bar", "baz"}, + Raw: map[string]interface{}{ + "bar": hcl2shim.UnknownVariableValue, + "baz": hcl2shim.UnknownVariableValue, + }, + Config: map[string]interface{}{ + "bar": hcl2shim.UnknownVariableValue, + "baz": hcl2shim.UnknownVariableValue, + }, + }, + }, + { + Name: "null collections", + Val: cty.ObjectVal(map[string]cty.Value{ + "bar": cty.NullVal(cty.Map(cty.String)), + "baz": cty.NullVal(cty.List(cty.String)), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bar": { + Type: cty.Map(cty.String), + Required: true, + }, + "baz": { + Type: cty.List(cty.String), + Optional: true, + }, + }, + }, + Expected: &ResourceConfig{ + Raw: map[string]interface{}{}, + Config: map[string]interface{}{}, + }, + }, + { + Name: "unknown blocks", + Val: cty.ObjectVal(map[string]cty.Value{ + "bar": cty.UnknownVal(cty.Map(cty.String)), + "baz": cty.UnknownVal(cty.List(cty.String)), + }), + Schema: &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "bar": { + Block: configschema.Block{}, + Nesting: configschema.NestingList, + }, + "baz": { + Block: configschema.Block{}, + Nesting: configschema.NestingSet, + }, + }, + }, + Expected: &ResourceConfig{ + ComputedKeys: []string{"bar", "baz"}, + Raw: map[string]interface{}{ + "bar": hcl2shim.UnknownVariableValue, + "baz": hcl2shim.UnknownVariableValue, + }, + Config: map[string]interface{}{ + "bar": hcl2shim.UnknownVariableValue, + "baz": hcl2shim.UnknownVariableValue, + }, + }, + }, + { + Name: "unknown in nested blocks", + Val: cty.ObjectVal(map[string]cty.Value{ + "bar": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "baz": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "list": cty.UnknownVal(cty.List(cty.String)), + }), + }), + }), + }), + }), + Schema: &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "bar": { + Block: configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "baz": { + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "list": {Type: cty.List(cty.String), + Optional: true, + }, + }, + }, + Nesting: configschema.NestingList, + }, + }, + }, + Nesting: configschema.NestingList, + }, + }, + }, + Expected: &ResourceConfig{ + ComputedKeys: []string{"bar.0.baz.0.list"}, + Raw: map[string]interface{}{ + "bar": []interface{}{map[string]interface{}{ + "baz": []interface{}{map[string]interface{}{ + "list": "74D93920-ED26-11E3-AC10-0800200C9A66", + }}, + }}, + }, + Config: map[string]interface{}{ + "bar": []interface{}{map[string]interface{}{ + "baz": []interface{}{map[string]interface{}{ + "list": "74D93920-ED26-11E3-AC10-0800200C9A66", + }}, + }}, + }, + }, + }, + { + Name: "unknown in set", + Val: cty.ObjectVal(map[string]cty.Value{ + "bar": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "val": cty.UnknownVal(cty.String), + }), + }), + }), + Schema: &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "bar": { + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "val": { + Type: cty.String, + Optional: true, + }, + }, + }, + Nesting: configschema.NestingSet, + }, + }, + }, + Expected: &ResourceConfig{ + ComputedKeys: []string{"bar.0.val"}, + Raw: map[string]interface{}{ + "bar": []interface{}{map[string]interface{}{ + "val": "74D93920-ED26-11E3-AC10-0800200C9A66", + }}, + }, + Config: map[string]interface{}{ + "bar": []interface{}{map[string]interface{}{ + "val": "74D93920-ED26-11E3-AC10-0800200C9A66", + }}, + }, + }, + }, + { + Name: "unknown in attribute sets", + Val: cty.ObjectVal(map[string]cty.Value{ + "bar": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "val": cty.UnknownVal(cty.String), + }), + }), + "baz": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "obj": cty.UnknownVal(cty.Object(map[string]cty.Type{ + "attr": cty.List(cty.String), + })), + }), + cty.ObjectVal(map[string]cty.Value{ + "obj": cty.ObjectVal(map[string]cty.Value{ + "attr": cty.UnknownVal(cty.List(cty.String)), + }), + }), + }), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bar": { + Type: cty.Set(cty.Object(map[string]cty.Type{ + "val": cty.String, + })), + }, + "baz": { + Type: cty.Set(cty.Object(map[string]cty.Type{ + "obj": cty.Object(map[string]cty.Type{ + "attr": cty.List(cty.String), + }), + })), + }, + }, + }, + Expected: &ResourceConfig{ + ComputedKeys: []string{"bar.0.val", "baz.0.obj.attr", "baz.1.obj"}, + Raw: map[string]interface{}{ + "bar": []interface{}{map[string]interface{}{ + "val": "74D93920-ED26-11E3-AC10-0800200C9A66", + }}, + "baz": []interface{}{ + map[string]interface{}{ + "obj": map[string]interface{}{ + "attr": "74D93920-ED26-11E3-AC10-0800200C9A66", + }, + }, + map[string]interface{}{ + "obj": "74D93920-ED26-11E3-AC10-0800200C9A66", + }, + }, + }, + Config: map[string]interface{}{ + "bar": []interface{}{map[string]interface{}{ + "val": "74D93920-ED26-11E3-AC10-0800200C9A66", + }}, + "baz": []interface{}{ + map[string]interface{}{ + "obj": map[string]interface{}{ + "attr": "74D93920-ED26-11E3-AC10-0800200C9A66", + }, + }, + map[string]interface{}{ + "obj": "74D93920-ED26-11E3-AC10-0800200C9A66", + }, + }, + }, + }, + }, + { + Name: "null blocks", + Val: cty.ObjectVal(map[string]cty.Value{ + "bar": cty.NullVal(cty.Map(cty.String)), + "baz": cty.NullVal(cty.List(cty.String)), + }), + Schema: &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "bar": { + Block: configschema.Block{}, + Nesting: configschema.NestingMap, + }, + "baz": { + Block: configschema.Block{}, + Nesting: configschema.NestingSingle, + }, + }, + }, + Expected: &ResourceConfig{ + Raw: map[string]interface{}{}, + Config: map[string]interface{}{}, + }, + }, + } { + t.Run(tc.Name, func(*testing.T) { + cfg := NewResourceConfigShimmed(tc.Val, tc.Schema) + if !tc.Expected.Equal(cfg) { + t.Fatalf("expected:\n%#v\ngot:\n%#v", tc.Expected, cfg) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/schemas.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/schemas.go index 8bc3b017..07e5a84f 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/schemas.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/schemas.go @@ -1,240 +1,9 @@ package terraform import ( - "fmt" - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/configschema" ) -// Schemas is a container for various kinds of schema that Terraform needs -// during processing. -type Schemas struct { - Providers map[string]*ProviderSchema - Provisioners map[string]*configschema.Block -} - -// ProviderSchema returns the entire ProviderSchema object that was produced -// by the plugin for the given provider, or nil if no such schema is available. -// -// It's usually better to go use the more precise methods offered by type -// Schemas to handle this detail automatically. -func (ss *Schemas) ProviderSchema(typeName string) *ProviderSchema { - if ss.Providers == nil { - return nil - } - return ss.Providers[typeName] -} - -// ProviderConfig returns the schema for the provider configuration of the -// given provider type, or nil if no such schema is available. -func (ss *Schemas) ProviderConfig(typeName string) *configschema.Block { - ps := ss.ProviderSchema(typeName) - if ps == nil { - return nil - } - return ps.Provider -} - -// ResourceTypeConfig returns the schema for the configuration of a given -// resource type belonging to a given provider type, or nil of no such -// schema is available. -// -// In many cases the provider type is inferrable from the resource type name, -// but this is not always true because users can override the provider for -// a resource using the "provider" meta-argument. Therefore it's important to -// always pass the correct provider name, even though it many cases it feels -// redundant. -func (ss *Schemas) ResourceTypeConfig(providerType string, resourceMode addrs.ResourceMode, resourceType string) (block *configschema.Block, schemaVersion uint64) { - ps := ss.ProviderSchema(providerType) - if ps == nil || ps.ResourceTypes == nil { - return nil, 0 - } - return ps.SchemaForResourceType(resourceMode, resourceType) -} - -// ProvisionerConfig returns the schema for the configuration of a given -// provisioner, or nil of no such schema is available. -func (ss *Schemas) ProvisionerConfig(name string) *configschema.Block { - return ss.Provisioners[name] -} - -// LoadSchemas searches the given configuration, state and plan (any of which -// may be nil) for constructs that have an associated schema, requests the -// necessary schemas from the given component factory (which must _not_ be nil), -// and returns a single object representing all of the necessary schemas. -// -// If an error is returned, it may be a wrapped tfdiags.Diagnostics describing -// errors across multiple separate objects. Errors here will usually indicate -// either misbehavior on the part of one of the providers or of the provider -// protocol itself. When returned with errors, the returned schemas object is -// still valid but may be incomplete. -func LoadSchemas(config *configs.Config, state *states.State, components contextComponentFactory) (*Schemas, error) { - schemas := &Schemas{ - Providers: map[string]*ProviderSchema{}, - Provisioners: map[string]*configschema.Block{}, - } - var diags tfdiags.Diagnostics - - newDiags := loadProviderSchemas(schemas.Providers, config, state, components) - diags = diags.Append(newDiags) - newDiags = loadProvisionerSchemas(schemas.Provisioners, config, components) - diags = diags.Append(newDiags) - - return schemas, diags.Err() -} - -func loadProviderSchemas(schemas map[string]*ProviderSchema, config *configs.Config, state *states.State, components contextComponentFactory) tfdiags.Diagnostics { - var diags tfdiags.Diagnostics - - ensure := func(typeName string) { - if _, exists := schemas[typeName]; exists { - return - } - - log.Printf("[TRACE] LoadSchemas: retrieving schema for provider type %q", typeName) - provider, err := components.ResourceProvider(typeName, "early/"+typeName) - if err != nil { - // We'll put a stub in the map so we won't re-attempt this on - // future calls. - schemas[typeName] = &ProviderSchema{} - diags = diags.Append( - fmt.Errorf("Failed to instantiate provider %q to obtain schema: %s", typeName, err), - ) - return - } - defer func() { - provider.Close() - }() - - resp := provider.GetSchema() - if resp.Diagnostics.HasErrors() { - // We'll put a stub in the map so we won't re-attempt this on - // future calls. - schemas[typeName] = &ProviderSchema{} - diags = diags.Append( - fmt.Errorf("Failed to retrieve schema from provider %q: %s", typeName, resp.Diagnostics.Err()), - ) - return - } - - s := &ProviderSchema{ - Provider: resp.Provider.Block, - ResourceTypes: make(map[string]*configschema.Block), - DataSources: make(map[string]*configschema.Block), - - ResourceTypeSchemaVersions: make(map[string]uint64), - } - - if resp.Provider.Version < 0 { - // We're not using the version numbers here yet, but we'll check - // for validity anyway in case we start using them in future. - diags = diags.Append( - fmt.Errorf("invalid negative schema version provider configuration for provider %q", typeName), - ) - } - - for t, r := range resp.ResourceTypes { - s.ResourceTypes[t] = r.Block - s.ResourceTypeSchemaVersions[t] = uint64(r.Version) - if r.Version < 0 { - diags = diags.Append( - fmt.Errorf("invalid negative schema version for resource type %s in provider %q", t, typeName), - ) - } - } - - for t, d := range resp.DataSources { - s.DataSources[t] = d.Block - if d.Version < 0 { - // We're not using the version numbers here yet, but we'll check - // for validity anyway in case we start using them in future. - diags = diags.Append( - fmt.Errorf("invalid negative schema version for data source %s in provider %q", t, typeName), - ) - } - } - - schemas[typeName] = s - } - - if config != nil { - for _, typeName := range config.ProviderTypes() { - ensure(typeName) - } - } - - if state != nil { - needed := providers.AddressedTypesAbs(state.ProviderAddrs()) - for _, typeName := range needed { - ensure(typeName) - } - } - - return diags -} - -func loadProvisionerSchemas(schemas map[string]*configschema.Block, config *configs.Config, components contextComponentFactory) tfdiags.Diagnostics { - var diags tfdiags.Diagnostics - - ensure := func(name string) { - if _, exists := schemas[name]; exists { - return - } - - log.Printf("[TRACE] LoadSchemas: retrieving schema for provisioner %q", name) - provisioner, err := components.ResourceProvisioner(name, "early/"+name) - if err != nil { - // We'll put a stub in the map so we won't re-attempt this on - // future calls. - schemas[name] = &configschema.Block{} - diags = diags.Append( - fmt.Errorf("Failed to instantiate provisioner %q to obtain schema: %s", name, err), - ) - return - } - defer func() { - if closer, ok := provisioner.(ResourceProvisionerCloser); ok { - closer.Close() - } - }() - - resp := provisioner.GetSchema() - if resp.Diagnostics.HasErrors() { - // We'll put a stub in the map so we won't re-attempt this on - // future calls. - schemas[name] = &configschema.Block{} - diags = diags.Append( - fmt.Errorf("Failed to retrieve schema from provisioner %q: %s", name, resp.Diagnostics.Err()), - ) - return - } - - schemas[name] = resp.Provisioner - } - - if config != nil { - for _, rc := range config.Module.ManagedResources { - for _, pc := range rc.Managed.Provisioners { - ensure(pc.Type) - } - } - - // Must also visit our child modules, recursively. - for _, cc := range config.Children { - childDiags := loadProvisionerSchemas(schemas, cc, components) - diags = diags.Append(childDiags) - } - } - - return diags -} - // ProviderSchema represents the schema for a provider's own configuration // and the configuration for some or all of its resources and data sources. // @@ -249,27 +18,6 @@ type ProviderSchema struct { ResourceTypeSchemaVersions map[string]uint64 } -// SchemaForResourceType attempts to find a schema for the given mode and type. -// Returns nil if no such schema is available. -func (ps *ProviderSchema) SchemaForResourceType(mode addrs.ResourceMode, typeName string) (schema *configschema.Block, version uint64) { - switch mode { - case addrs.ManagedResourceMode: - return ps.ResourceTypes[typeName], ps.ResourceTypeSchemaVersions[typeName] - case addrs.DataResourceMode: - // Data resources don't have schema versions right now, since state is discarded for each refresh - return ps.DataSources[typeName], 0 - default: - // Shouldn't happen, because the above cases are comprehensive. - return nil, 0 - } -} - -// SchemaForResourceAddr attempts to find a schema for the mode and type from -// the given resource address. Returns nil if no such schema is available. -func (ps *ProviderSchema) SchemaForResourceAddr(addr addrs.Resource) (schema *configschema.Block, version uint64) { - return ps.SchemaForResourceType(addr.Mode, addr.Type) -} - // ProviderSchemaRequest is used to describe to a ResourceProvider which // aspects of schema are required, when calling the GetSchema method. type ProviderSchemaRequest struct { diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/state.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/state.go index 1d742c2f..87f7610a 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/state.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/state.go @@ -4,10 +4,7 @@ import ( "bufio" "bytes" "encoding/json" - "errors" "fmt" - "io" - "io/ioutil" "log" "os" "reflect" @@ -16,27 +13,18 @@ import ( "strings" "sync" - "github.com/hashicorp/errwrap" + "github.com/hashicorp/go-cty/cty" multierror "github.com/hashicorp/go-multierror" uuid "github.com/hashicorp/go-uuid" - version "github.com/hashicorp/go-version" - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - tfversion "github.com/hashicorp/terraform-plugin-sdk/internal/version" "github.com/mitchellh/copystructure" - "github.com/zclconf/go-cty/cty" - ctyjson "github.com/zclconf/go-cty/cty/json" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/addrs" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/hcl2shim" ) const ( // StateVersion is the current version for our state file - StateVersion = 3 + stateVersion = 3 ) // rootModulePath is the path of the root module @@ -112,6 +100,10 @@ type State struct { Modules []*ModuleState `json:"modules"` mu sync.Mutex + + // IsBinaryDrivenTest is a special flag that assists with a binary driver + // heuristic, it should not be set externally + IsBinaryDrivenTest bool } func (s *State) Lock() { s.mu.Lock() } @@ -337,8 +329,8 @@ func (s *State) Remove(addr ...string) error { defer s.Unlock() // Filter out what we need to delete - filter := &StateFilter{State: s} - results, err := filter.Filter(addr...) + filter := &stateFilter{State: s} + results, err := filter.filter(addr...) if err != nil { return err } @@ -496,43 +488,6 @@ func (s *State) equal(other *State) bool { return true } -// MarshalEqual is similar to Equal but provides a stronger definition of -// "equal", where two states are equal if and only if their serialized form -// is byte-for-byte identical. -// -// This is primarily useful for callers that are trying to save snapshots -// of state to persistent storage, allowing them to detect when a new -// snapshot must be taken. -// -// Note that the serial number and lineage are included in the serialized form, -// so it's the caller's responsibility to properly manage these attributes -// so that this method is only called on two states that have the same -// serial and lineage, unless detecting such differences is desired. -func (s *State) MarshalEqual(other *State) bool { - if s == nil && other == nil { - return true - } else if s == nil || other == nil { - return false - } - - recvBuf := &bytes.Buffer{} - otherBuf := &bytes.Buffer{} - - err := WriteState(s, recvBuf) - if err != nil { - // should never happen, since we're writing to a buffer - panic(err) - } - - err = WriteState(other, otherBuf) - if err != nil { - // should never happen, since we're writing to a buffer - panic(err) - } - - return bytes.Equal(recvBuf.Bytes(), otherBuf.Bytes()) -} - type StateAgeComparison int const ( @@ -615,21 +570,6 @@ func (s *State) DeepCopy() *State { return copy.(*State) } -// FromFutureTerraform checks if this state was written by a Terraform -// version from the future. -func (s *State) FromFutureTerraform() bool { - s.Lock() - defer s.Unlock() - - // No TF version means it is certainly from the past - if s.TFVersion == "" { - return false - } - - v := version.Must(version.NewVersion(s.TFVersion)) - return tfversion.SemVer.LessThan(v) -} - func (s *State) Init() { s.Lock() defer s.Unlock() @@ -638,7 +578,7 @@ func (s *State) Init() { func (s *State) init() { if s.Version == 0 { - s.Version = StateVersion + s.Version = stateVersion } if s.moduleByPath(addrs.RootModuleInstance) == nil { @@ -672,9 +612,13 @@ func (s *State) ensureHasLineage() { panic(fmt.Errorf("Failed to generate lineage: %v", err)) } s.Lineage = lineage - log.Printf("[DEBUG] New state was assigned lineage %q\n", s.Lineage) + if os.Getenv("TF_ACC") == "" || os.Getenv("TF_ACC_STATE_LINEAGE") == "1" { + log.Printf("[DEBUG] New state was assigned lineage %q\n", s.Lineage) + } } else { - log.Printf("[TRACE] Preserving existing state lineage %q\n", s.Lineage) + if os.Getenv("TF_ACC") == "" || os.Getenv("TF_ACC_STATE_LINEAGE") == "1" { + log.Printf("[TRACE] Preserving existing state lineage %q\n", s.Lineage) + } } } @@ -776,57 +720,6 @@ type BackendState struct { Hash uint64 `json:"hash"` // Hash of portion of configuration from config files } -// Empty returns true if BackendState has no state. -func (s *BackendState) Empty() bool { - return s == nil || s.Type == "" -} - -// Config decodes the type-specific configuration object using the provided -// schema and returns the result as a cty.Value. -// -// An error is returned if the stored configuration does not conform to the -// given schema. -func (s *BackendState) Config(schema *configschema.Block) (cty.Value, error) { - ty := schema.ImpliedType() - if s == nil { - return cty.NullVal(ty), nil - } - return ctyjson.Unmarshal(s.ConfigRaw, ty) -} - -// SetConfig replaces (in-place) the type-specific configuration object using -// the provided value and associated schema. -// -// An error is returned if the given value does not conform to the implied -// type of the schema. -func (s *BackendState) SetConfig(val cty.Value, schema *configschema.Block) error { - ty := schema.ImpliedType() - buf, err := ctyjson.Marshal(val, ty) - if err != nil { - return err - } - s.ConfigRaw = buf - return nil -} - -// ForPlan produces an alternative representation of the reciever that is -// suitable for storing in a plan. The current workspace must additionally -// be provided, to be stored alongside the backend configuration. -// -// The backend configuration schema is required in order to properly -// encode the backend-specific configuration settings. -func (s *BackendState) ForPlan(schema *configschema.Block, workspaceName string) (*plans.Backend, error) { - if s == nil { - return nil, nil - } - - configVal, err := s.Config(schema) - if err != nil { - return nil, errwrap.Wrapf("failed to decode backend config: {{err}}", err) - } - return plans.NewBackend(s.Type, configVal, schema, workspaceName) -} - // RemoteState is used to track the information about a remote // state store that we push/pull state to. type RemoteState struct { @@ -862,24 +755,6 @@ func (r *RemoteState) Empty() bool { return r.Type == "" } -func (r *RemoteState) Equals(other *RemoteState) bool { - r.Lock() - defer r.Unlock() - - if r.Type != other.Type { - return false - } - if len(r.Config) != len(other.Config) { - return false - } - for k, v := range r.Config { - if other.Config[k] != v { - return false - } - } - return true -} - // OutputState is used to track the state relevant to a single output. type OutputState struct { // Sensitive describes whether the output is considered sensitive, @@ -1024,103 +899,6 @@ func (m *ModuleState) Equal(other *ModuleState) bool { return true } -// IsRoot says whether or not this module diff is for the root module. -func (m *ModuleState) IsRoot() bool { - m.Lock() - defer m.Unlock() - return reflect.DeepEqual(m.Path, rootModulePath) -} - -// IsDescendent returns true if other is a descendent of this module. -func (m *ModuleState) IsDescendent(other *ModuleState) bool { - m.Lock() - defer m.Unlock() - - i := len(m.Path) - return len(other.Path) > i && reflect.DeepEqual(other.Path[:i], m.Path) -} - -// Orphans returns a list of keys of resources that are in the State -// but aren't present in the configuration itself. Hence, these keys -// represent the state of resources that are orphans. -func (m *ModuleState) Orphans(c *configs.Module) []addrs.ResourceInstance { - m.Lock() - defer m.Unlock() - - inConfig := make(map[string]struct{}) - if c != nil { - for _, r := range c.ManagedResources { - inConfig[r.Addr().String()] = struct{}{} - } - for _, r := range c.DataResources { - inConfig[r.Addr().String()] = struct{}{} - } - } - - var result []addrs.ResourceInstance - for k := range m.Resources { - // Since we've not yet updated state to use our new address format, - // we need to do some shimming here. - legacyAddr, err := parseResourceAddressInternal(k) - if err != nil { - // Suggests that the user tampered with the state, since we always - // generate valid internal addresses. - log.Printf("ModuleState has invalid resource key %q. Ignoring.", k) - continue - } - - addr := legacyAddr.AbsResourceInstanceAddr().Resource - compareKey := addr.Resource.String() // compare by resource address, ignoring instance key - if _, exists := inConfig[compareKey]; !exists { - result = append(result, addr) - } - } - return result -} - -// RemovedOutputs returns a list of outputs that are in the State but aren't -// present in the configuration itself. -func (s *ModuleState) RemovedOutputs(outputs map[string]*configs.Output) []addrs.OutputValue { - if outputs == nil { - // If we got no output map at all then we'll just treat our set of - // configured outputs as empty, since that suggests that they've all - // been removed by removing their containing module. - outputs = make(map[string]*configs.Output) - } - - s.Lock() - defer s.Unlock() - - var ret []addrs.OutputValue - for n := range s.Outputs { - if _, declared := outputs[n]; !declared { - ret = append(ret, addrs.OutputValue{ - Name: n, - }) - } - } - - return ret -} - -// View returns a view with the given resource prefix. -func (m *ModuleState) View(id string) *ModuleState { - if m == nil { - return m - } - - r := m.deepcopy() - for k, _ := range r.Resources { - if id == k || strings.HasPrefix(k, id+".") { - continue - } - - delete(r.Resources, k) - } - - return r -} - func (m *ModuleState) init() { m.Lock() defer m.Unlock() @@ -1144,19 +922,6 @@ func (m *ModuleState) init() { } } -func (m *ModuleState) deepcopy() *ModuleState { - if m == nil { - return nil - } - - stateCopy, err := copystructure.Config{Lock: true}.Copy(m) - if err != nil { - panic(err) - } - - return stateCopy.(*ModuleState) -} - // prune is used to remove any resources that are no longer required func (m *ModuleState) prune() { m.Lock() @@ -1197,7 +962,7 @@ func (m *ModuleState) String() string { } names := make([]string, 0, len(m.Resources)) - for name, _ := range m.Resources { + for name := range m.Resources { names = append(names, name) } @@ -1234,7 +999,7 @@ func (m *ModuleState) String() string { attributes = rs.Primary.Attributes } attrKeys := make([]string, 0, len(attributes)) - for ak, _ := range attributes { + for ak := range attributes { if ak == "id" { continue } @@ -1269,7 +1034,7 @@ func (m *ModuleState) String() string { buf.WriteString("\nOutputs:\n\n") ks := make([]string, 0, len(m.Outputs)) - for k, _ := range m.Outputs { + for k := range m.Outputs { ks = append(ks, k) } @@ -1284,7 +1049,7 @@ func (m *ModuleState) String() string { buf.WriteString(fmt.Sprintf("%s = %s\n", k, vTyped)) case map[string]interface{}: var mapKeys []string - for key, _ := range vTyped { + for key := range vTyped { mapKeys = append(mapKeys, key) } sort.Strings(mapKeys) @@ -1304,10 +1069,6 @@ func (m *ModuleState) String() string { return buf.String() } -func (m *ModuleState) Empty() bool { - return len(m.Locals) == 0 && len(m.Outputs) == 0 && len(m.Resources) == 0 -} - // ResourceStateKey is a structured representation of the key used for the // ModuleState.Resources mapping type ResourceStateKey struct { @@ -1360,7 +1121,7 @@ func (rsk *ResourceStateKey) String() string { // ModuleState.Resources and returns a resource name and resource index. In the // state, a resource has the format "type.name.index" or "type.name". In the // latter case, the index is returned as -1. -func ParseResourceStateKey(k string) (*ResourceStateKey, error) { +func parseResourceStateKey(k string) (*ResourceStateKey, error) { parts := strings.Split(k, ".") mode := ManagedResourceMode if len(parts) > 0 && parts[0] == "data" { @@ -1503,24 +1264,6 @@ func (s *ResourceState) Untaint() { } } -// ProviderAddr returns the provider address for the receiver, by parsing the -// string representation saved in state. An error can be returned if the -// value in state is corrupt. -func (s *ResourceState) ProviderAddr() (addrs.AbsProviderConfig, error) { - var diags tfdiags.Diagnostics - - str := s.Provider - traversal, travDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1}) - diags = diags.Append(travDiags) - if travDiags.HasErrors() { - return addrs.AbsProviderConfig{}, diags.Err() - } - - addr, addrDiags := addrs.ParseAbsProviderConfig(traversal) - diags = diags.Append(addrDiags) - return addr, diags.Err() -} - func (s *ResourceState) init() { s.Lock() defer s.Unlock() @@ -1598,6 +1341,8 @@ type InstanceState struct { // and collections. Meta map[string]interface{} `json:"meta"` + ProviderMeta cty.Value + // Tainted is used to mark a resource for recreation. Tainted bool `json:"tainted"` @@ -1820,7 +1565,7 @@ func (s *InstanceState) String() string { attributes := s.Attributes attrKeys := make([]string, 0, len(attributes)) - for ak, _ := range attributes { + for ak := range attributes { if ak == "id" { continue } @@ -1859,283 +1604,6 @@ func (e *EphemeralState) init() { } } -func (e *EphemeralState) DeepCopy() *EphemeralState { - copy, err := copystructure.Config{Lock: true}.Copy(e) - if err != nil { - panic(err) - } - - return copy.(*EphemeralState) -} - -type jsonStateVersionIdentifier struct { - Version int `json:"version"` -} - -// Check if this is a V0 format - the magic bytes at the start of the file -// should be "tfstate" if so. We no longer support upgrading this type of -// state but return an error message explaining to a user how they can -// upgrade via the 0.6.x series. -func testForV0State(buf *bufio.Reader) error { - start, err := buf.Peek(len("tfstate")) - if err != nil { - return fmt.Errorf("Failed to check for magic bytes: %v", err) - } - if string(start) == "tfstate" { - return fmt.Errorf("Terraform 0.7 no longer supports upgrading the binary state\n" + - "format which was used prior to Terraform 0.3. Please upgrade\n" + - "this state file using Terraform 0.6.16 prior to using it with\n" + - "Terraform 0.7.") - } - - return nil -} - -// ErrNoState is returned by ReadState when the io.Reader contains no data -var ErrNoState = errors.New("no state") - -// ReadState reads a state structure out of a reader in the format that -// was written by WriteState. -func ReadState(src io.Reader) (*State, error) { - // check for a nil file specifically, since that produces a platform - // specific error if we try to use it in a bufio.Reader. - if f, ok := src.(*os.File); ok && f == nil { - return nil, ErrNoState - } - - buf := bufio.NewReader(src) - - if _, err := buf.Peek(1); err != nil { - if err == io.EOF { - return nil, ErrNoState - } - return nil, err - } - - if err := testForV0State(buf); err != nil { - return nil, err - } - - // If we are JSON we buffer the whole thing in memory so we can read it twice. - // This is suboptimal, but will work for now. - jsonBytes, err := ioutil.ReadAll(buf) - if err != nil { - return nil, fmt.Errorf("Reading state file failed: %v", err) - } - - versionIdentifier := &jsonStateVersionIdentifier{} - if err := json.Unmarshal(jsonBytes, versionIdentifier); err != nil { - return nil, fmt.Errorf("Decoding state file version failed: %v", err) - } - - var result *State - switch versionIdentifier.Version { - case 0: - return nil, fmt.Errorf("State version 0 is not supported as JSON.") - case 1: - v1State, err := ReadStateV1(jsonBytes) - if err != nil { - return nil, err - } - - v2State, err := upgradeStateV1ToV2(v1State) - if err != nil { - return nil, err - } - - v3State, err := upgradeStateV2ToV3(v2State) - if err != nil { - return nil, err - } - - // increment the Serial whenever we upgrade state - v3State.Serial++ - result = v3State - case 2: - v2State, err := ReadStateV2(jsonBytes) - if err != nil { - return nil, err - } - v3State, err := upgradeStateV2ToV3(v2State) - if err != nil { - return nil, err - } - - v3State.Serial++ - result = v3State - case 3: - v3State, err := ReadStateV3(jsonBytes) - if err != nil { - return nil, err - } - - result = v3State - default: - return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.", - tfversion.SemVer.String(), versionIdentifier.Version) - } - - // If we reached this place we must have a result set - if result == nil { - panic("resulting state in load not set, assertion failed") - } - - // Prune the state when read it. Its possible to write unpruned states or - // for a user to make a state unpruned (nil-ing a module state for example). - result.prune() - - // Validate the state file is valid - if err := result.Validate(); err != nil { - return nil, err - } - - return result, nil -} - -func ReadStateV1(jsonBytes []byte) (*stateV1, error) { - v1State := &stateV1{} - if err := json.Unmarshal(jsonBytes, v1State); err != nil { - return nil, fmt.Errorf("Decoding state file failed: %v", err) - } - - if v1State.Version != 1 { - return nil, fmt.Errorf("Decoded state version did not match the decoder selection: "+ - "read %d, expected 1", v1State.Version) - } - - return v1State, nil -} - -func ReadStateV2(jsonBytes []byte) (*State, error) { - state := &State{} - if err := json.Unmarshal(jsonBytes, state); err != nil { - return nil, fmt.Errorf("Decoding state file failed: %v", err) - } - - // Check the version, this to ensure we don't read a future - // version that we don't understand - if state.Version > StateVersion { - return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.", - tfversion.SemVer.String(), state.Version) - } - - // Make sure the version is semantic - if state.TFVersion != "" { - if _, err := version.NewVersion(state.TFVersion); err != nil { - return nil, fmt.Errorf( - "State contains invalid version: %s\n\n"+ - "Terraform validates the version format prior to writing it. This\n"+ - "means that this is invalid of the state becoming corrupted through\n"+ - "some external means. Please manually modify the Terraform version\n"+ - "field to be a proper semantic version.", - state.TFVersion) - } - } - - // catch any unitialized fields in the state - state.init() - - // Sort it - state.sort() - - return state, nil -} - -func ReadStateV3(jsonBytes []byte) (*State, error) { - state := &State{} - if err := json.Unmarshal(jsonBytes, state); err != nil { - return nil, fmt.Errorf("Decoding state file failed: %v", err) - } - - // Check the version, this to ensure we don't read a future - // version that we don't understand - if state.Version > StateVersion { - return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.", - tfversion.SemVer.String(), state.Version) - } - - // Make sure the version is semantic - if state.TFVersion != "" { - if _, err := version.NewVersion(state.TFVersion); err != nil { - return nil, fmt.Errorf( - "State contains invalid version: %s\n\n"+ - "Terraform validates the version format prior to writing it. This\n"+ - "means that this is invalid of the state becoming corrupted through\n"+ - "some external means. Please manually modify the Terraform version\n"+ - "field to be a proper semantic version.", - state.TFVersion) - } - } - - // catch any unitialized fields in the state - state.init() - - // Sort it - state.sort() - - // Now we write the state back out to detect any changes in normaliztion. - // If our state is now written out differently, bump the serial number to - // prevent conflicts. - var buf bytes.Buffer - err := WriteState(state, &buf) - if err != nil { - return nil, err - } - - if !bytes.Equal(jsonBytes, buf.Bytes()) { - log.Println("[INFO] state modified during read or write. incrementing serial number") - state.Serial++ - } - - return state, nil -} - -// WriteState writes a state somewhere in a binary format. -func WriteState(d *State, dst io.Writer) error { - // writing a nil state is a noop. - if d == nil { - return nil - } - - // make sure we have no uninitialized fields - d.init() - - // Make sure it is sorted - d.sort() - - // Ensure the version is set - d.Version = StateVersion - - // If the TFVersion is set, verify it. We used to just set the version - // here, but this isn't safe since it changes the MD5 sum on some remote - // state storage backends such as Atlas. We now leave it be if needed. - if d.TFVersion != "" { - if _, err := version.NewVersion(d.TFVersion); err != nil { - return fmt.Errorf( - "Error writing state, invalid version: %s\n\n"+ - "The Terraform version when writing the state must be a semantic\n"+ - "version.", - d.TFVersion) - } - } - - // Encode the data in a human-friendly way - data, err := json.MarshalIndent(d, "", " ") - if err != nil { - return fmt.Errorf("Failed to encode state: %s", err) - } - - // We append a newline to the data because MarshalIndent doesn't - data = append(data, '\n') - - // Write the data out to the dst - if _, err := io.Copy(dst, bytes.NewReader(data)); err != nil { - return fmt.Errorf("Failed to write state: %v", err) - } - - return nil -} - // resourceNameSort implements the sort.Interface to sort name parts lexically for // strings and numerically for integer indexes. type resourceNameSort []string diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/state_filter.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/state_filter.go index 2dcb11b7..01d03927 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/state_filter.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/state_filter.go @@ -5,7 +5,7 @@ import ( "sort" ) -// StateFilter is responsible for filtering and searching a state. +// stateFilter is responsible for filtering and searching a state. // // This is a separate struct from State rather than a method on State // because StateFilter might create sidecar data structures to optimize @@ -15,18 +15,18 @@ import ( // Reset should be called or a new one should be allocated. StateFilter // will not watch State for changes and do this for you. If you filter after // changing the State without calling Reset, the behavior is not defined. -type StateFilter struct { +type stateFilter struct { State *State } // Filter takes the addresses specified by fs and finds all the matches. // The values of fs are resource addressing syntax that can be parsed by -// ParseResourceAddress. -func (f *StateFilter) Filter(fs ...string) ([]*StateFilterResult, error) { +// parseResourceAddress. +func (f *stateFilter) filter(fs ...string) ([]*stateFilterResult, error) { // Parse all the addresses - as := make([]*ResourceAddress, len(fs)) + as := make([]*resourceAddress, len(fs)) for i, v := range fs { - a, err := ParseResourceAddress(v) + a, err := parseResourceAddress(v) if err != nil { return nil, fmt.Errorf("Error parsing address '%s': %s", v, err) } @@ -36,12 +36,12 @@ func (f *StateFilter) Filter(fs ...string) ([]*StateFilterResult, error) { // If we weren't given any filters, then we list all if len(fs) == 0 { - as = append(as, &ResourceAddress{Index: -1}) + as = append(as, &resourceAddress{Index: -1}) } // Filter each of the address. We keep track of this in a map to // strip duplicates. - resultSet := make(map[string]*StateFilterResult) + resultSet := make(map[string]*stateFilterResult) for _, a := range as { for _, r := range f.filterSingle(a) { resultSet[r.String()] = r @@ -49,19 +49,19 @@ func (f *StateFilter) Filter(fs ...string) ([]*StateFilterResult, error) { } // Make the result list - results := make([]*StateFilterResult, 0, len(resultSet)) + results := make([]*stateFilterResult, 0, len(resultSet)) for _, v := range resultSet { results = append(results, v) } // Sort them and return - sort.Sort(StateFilterResultSlice(results)) + sort.Sort(stateFilterResultSlice(results)) return results, nil } -func (f *StateFilter) filterSingle(a *ResourceAddress) []*StateFilterResult { +func (f *stateFilter) filterSingle(a *resourceAddress) []*stateFilterResult { // The slice to keep track of results - var results []*StateFilterResult + var results []*stateFilterResult // Go through modules first. modules := make([]*ModuleState, 0, len(f.State.Modules)) @@ -72,9 +72,9 @@ func (f *StateFilter) filterSingle(a *ResourceAddress) []*StateFilterResult { // Only add the module to the results if we haven't specified a type. // We also ignore the root module. if a.Type == "" && len(m.Path) > 1 { - results = append(results, &StateFilterResult{ + results = append(results, &stateFilterResult{ Path: m.Path[1:], - Address: (&ResourceAddress{Path: m.Path[1:]}).String(), + Address: (&resourceAddress{Path: m.Path[1:]}).String(), Value: m, }) } @@ -86,7 +86,7 @@ func (f *StateFilter) filterSingle(a *ResourceAddress) []*StateFilterResult { for _, m := range modules { for n, r := range m.Resources { // The name in the state contains valuable information. Parse. - key, err := ParseResourceStateKey(n) + key, err := parseResourceStateKey(n) if err != nil { // If we get an error parsing, then just ignore it // out of the state. @@ -116,7 +116,7 @@ func (f *StateFilter) filterSingle(a *ResourceAddress) []*StateFilterResult { } // Build the address for this resource - addr := &ResourceAddress{ + addr := &resourceAddress{ Path: m.Path[1:], Name: key.Name, Type: key.Type, @@ -124,7 +124,7 @@ func (f *StateFilter) filterSingle(a *ResourceAddress) []*StateFilterResult { } // Add the resource level result - resourceResult := &StateFilterResult{ + resourceResult := &stateFilterResult{ Path: addr.Path, Address: addr.String(), Value: r, @@ -135,9 +135,9 @@ func (f *StateFilter) filterSingle(a *ResourceAddress) []*StateFilterResult { // Add the instances if r.Primary != nil { - addr.InstanceType = TypePrimary + addr.InstanceType = typePrimary addr.InstanceTypeSet = false - results = append(results, &StateFilterResult{ + results = append(results, &stateFilterResult{ Path: addr.Path, Address: addr.String(), Parent: resourceResult, @@ -147,9 +147,9 @@ func (f *StateFilter) filterSingle(a *ResourceAddress) []*StateFilterResult { for _, instance := range r.Deposed { if f.relevant(a, instance) { - addr.InstanceType = TypeDeposed + addr.InstanceType = typeDeposed addr.InstanceTypeSet = true - results = append(results, &StateFilterResult{ + results = append(results, &stateFilterResult{ Path: addr.Path, Address: addr.String(), Parent: resourceResult, @@ -165,7 +165,7 @@ func (f *StateFilter) filterSingle(a *ResourceAddress) []*StateFilterResult { } // relevant checks for relevance of this address against the given value. -func (f *StateFilter) relevant(addr *ResourceAddress, raw interface{}) bool { +func (f *stateFilter) relevant(addr *resourceAddress, raw interface{}) bool { switch v := raw.(type) { case *ModuleState: path := v.Path[1:] @@ -202,10 +202,10 @@ func (f *StateFilter) relevant(addr *ResourceAddress, raw interface{}) bool { } } -// StateFilterResult is a single result from a filter operation. Filter +// stateFilterResult is a single result from a filter operation. Filter // can match multiple things within a state (module, resource, instance, etc.) // and this unifies that. -type StateFilterResult struct { +type stateFilterResult struct { // Module path of the result Path []string @@ -215,7 +215,7 @@ type StateFilterResult struct { // Parent, if non-nil, is a parent of this result. For instances, the // parent would be a resource. For resources, the parent would be // a module. For modules, this is currently nil. - Parent *StateFilterResult + Parent *stateFilterResult // Value is the actual value. This must be type switched on. It can be // any data structures that `State` can hold: `ModuleState`, @@ -223,11 +223,11 @@ type StateFilterResult struct { Value interface{} } -func (r *StateFilterResult) String() string { +func (r *stateFilterResult) String() string { return fmt.Sprintf("%T: %s", r.Value, r.Address) } -func (r *StateFilterResult) sortedType() int { +func (r *stateFilterResult) sortedType() int { switch r.Value.(type) { case *ModuleState: return 0 @@ -240,19 +240,19 @@ func (r *StateFilterResult) sortedType() int { } } -// StateFilterResultSlice is a slice of results that implements +// stateFilterResultSlice is a slice of results that implements // sort.Interface. The sorting goal is what is most appealing to // human output. -type StateFilterResultSlice []*StateFilterResult +type stateFilterResultSlice []*stateFilterResult -func (s StateFilterResultSlice) Len() int { return len(s) } -func (s StateFilterResultSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s StateFilterResultSlice) Less(i, j int) bool { +func (s stateFilterResultSlice) Len() int { return len(s) } +func (s stateFilterResultSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s stateFilterResultSlice) Less(i, j int) bool { a, b := s[i], s[j] // if these address contain an index, we want to sort by index rather than name - addrA, errA := ParseResourceAddress(a.Address) - addrB, errB := ParseResourceAddress(b.Address) + addrA, errA := parseResourceAddress(a.Address) + addrB, errB := parseResourceAddress(b.Address) if errA == nil && errB == nil && addrA.Name == addrB.Name && addrA.Index != addrB.Index { return addrA.Index < addrB.Index } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/state_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/state_test.go new file mode 100644 index 00000000..48ce025b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/state_test.go @@ -0,0 +1,1424 @@ +package terraform + +import ( + "fmt" + "reflect" + "sort" + "testing" + + "github.com/davecgh/go-spew/spew" + + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/addrs" + "github.com/hashicorp/terraform-plugin-sdk/v2/internal/configs/hcl2shim" +) + +func TestStateValidate(t *testing.T) { + cases := map[string]struct { + In *State + Err bool + }{ + "empty state": { + &State{}, + false, + }, + + "multiple modules": { + &State{ + Modules: []*ModuleState{ + { + Path: []string{"root", "foo"}, + }, + { + Path: []string{"root", "foo"}, + }, + }, + }, + true, + }, + } + + for name, tc := range cases { + // Init the state + tc.In.init() + + err := tc.In.Validate() + if (err != nil) != tc.Err { + t.Fatalf("%s: err: %s", name, err) + } + } +} + +func TestStateAddModule(t *testing.T) { + cases := []struct { + In []addrs.ModuleInstance + Out [][]string + }{ + { + []addrs.ModuleInstance{ + addrs.RootModuleInstance, + addrs.RootModuleInstance.Child("child", addrs.NoKey), + }, + [][]string{ + {"root"}, + {"root", "child"}, + }, + }, + + { + []addrs.ModuleInstance{ + addrs.RootModuleInstance.Child("foo", addrs.NoKey).Child("bar", addrs.NoKey), + addrs.RootModuleInstance.Child("foo", addrs.NoKey), + addrs.RootModuleInstance, + addrs.RootModuleInstance.Child("bar", addrs.NoKey), + }, + [][]string{ + {"root"}, + {"root", "bar"}, + {"root", "foo"}, + {"root", "foo", "bar"}, + }, + }, + // Same last element, different middle element + { + []addrs.ModuleInstance{ + addrs.RootModuleInstance.Child("foo", addrs.NoKey).Child("bar", addrs.NoKey), // This one should sort after... + addrs.RootModuleInstance.Child("foo", addrs.NoKey), + addrs.RootModuleInstance, + addrs.RootModuleInstance.Child("bar", addrs.NoKey).Child("bar", addrs.NoKey), // ...this one. + addrs.RootModuleInstance.Child("bar", addrs.NoKey), + }, + [][]string{ + {"root"}, + {"root", "bar"}, + {"root", "foo"}, + {"root", "bar", "bar"}, + {"root", "foo", "bar"}, + }, + }, + } + + for _, tc := range cases { + s := new(State) + for _, p := range tc.In { + s.AddModule(p) + } + + actual := make([][]string, 0, len(tc.In)) + for _, m := range s.Modules { + actual = append(actual, m.Path) + } + + if !reflect.DeepEqual(actual, tc.Out) { + t.Fatalf("wrong result\ninput: %sgot: %#v\nwant: %#v", spew.Sdump(tc.In), actual, tc.Out) + } + } +} + +func TestStateDeepCopy(t *testing.T) { + cases := []struct { + State *State + }{ + // Nil + {nil}, + + // Version + { + &State{Version: 5}, + }, + // TFVersion + { + &State{TFVersion: "5"}, + }, + // Modules + { + &State{ + Version: 6, + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "test_instance.foo": { + Primary: &InstanceState{ + Meta: map[string]interface{}{}, + }, + }, + }, + }, + }, + }, + }, + // Deposed + // The nil values shouldn't be there if the State was properly init'ed, + // but the Copy should still work anyway. + { + &State{ + Version: 6, + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "test_instance.foo": { + Primary: &InstanceState{ + Meta: map[string]interface{}{}, + }, + Deposed: []*InstanceState{ + {ID: "test"}, + nil, + }, + }, + }, + }, + }, + }, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("copy-%d", i), func(t *testing.T) { + actual := tc.State.DeepCopy() + expected := tc.State + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("Expected: %#v\nRecevied: %#v\n", expected, actual) + } + }) + } +} + +func TestStateEqual(t *testing.T) { + cases := []struct { + Name string + Result bool + One, Two *State + }{ + // Nils + { + "one nil", + false, + nil, + &State{Version: 2}, + }, + + { + "both nil", + true, + nil, + nil, + }, + + // Different versions + { + "different state versions", + false, + &State{Version: 5}, + &State{Version: 2}, + }, + + // Different modules + { + "different module states", + false, + &State{ + Modules: []*ModuleState{ + { + Path: []string{"root"}, + }, + }, + }, + &State{}, + }, + + { + "same module states", + true, + &State{ + Modules: []*ModuleState{ + { + Path: []string{"root"}, + }, + }, + }, + &State{ + Modules: []*ModuleState{ + { + Path: []string{"root"}, + }, + }, + }, + }, + + // Meta differs + { + "differing meta values with primitives", + false, + &State{ + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "test_instance.foo": { + Primary: &InstanceState{ + Meta: map[string]interface{}{ + "schema_version": "1", + }, + }, + }, + }, + }, + }, + }, + &State{ + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "test_instance.foo": { + Primary: &InstanceState{ + Meta: map[string]interface{}{ + "schema_version": "2", + }, + }, + }, + }, + }, + }, + }, + }, + + // Meta with complex types + { + "same meta with complex types", + true, + &State{ + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "test_instance.foo": { + Primary: &InstanceState{ + Meta: map[string]interface{}{ + "timeouts": map[string]interface{}{ + "create": 42, + "read": "27", + }, + }, + }, + }, + }, + }, + }, + }, + &State{ + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "test_instance.foo": { + Primary: &InstanceState{ + Meta: map[string]interface{}{ + "timeouts": map[string]interface{}{ + "create": 42, + "read": "27", + }, + }, + }, + }, + }, + }, + }, + }, + }, + + // Meta with complex types that have been altered during serialization + { + "same meta with complex types that have been json-ified", + true, + &State{ + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "test_instance.foo": { + Primary: &InstanceState{ + Meta: map[string]interface{}{ + "timeouts": map[string]interface{}{ + "create": int(42), + "read": "27", + }, + }, + }, + }, + }, + }, + }, + }, + &State{ + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "test_instance.foo": { + Primary: &InstanceState{ + Meta: map[string]interface{}{ + "timeouts": map[string]interface{}{ + "create": float64(42), + "read": "27", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + if tc.One.Equal(tc.Two) != tc.Result { + t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) + } + if tc.Two.Equal(tc.One) != tc.Result { + t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) + } + }) + } +} + +func TestStateCompareAges(t *testing.T) { + cases := []struct { + Result StateAgeComparison + Err bool + One, Two *State + }{ + { + StateAgeEqual, false, + &State{ + Lineage: "1", + Serial: 2, + }, + &State{ + Lineage: "1", + Serial: 2, + }, + }, + { + StateAgeReceiverOlder, false, + &State{ + Lineage: "1", + Serial: 2, + }, + &State{ + Lineage: "1", + Serial: 3, + }, + }, + { + StateAgeReceiverNewer, false, + &State{ + Lineage: "1", + Serial: 3, + }, + &State{ + Lineage: "1", + Serial: 2, + }, + }, + { + StateAgeEqual, true, + &State{ + Lineage: "1", + Serial: 2, + }, + &State{ + Lineage: "2", + Serial: 2, + }, + }, + { + StateAgeEqual, true, + &State{ + Lineage: "1", + Serial: 3, + }, + &State{ + Lineage: "2", + Serial: 2, + }, + }, + } + + for i, tc := range cases { + result, err := tc.One.CompareAges(tc.Two) + + if err != nil && !tc.Err { + t.Errorf( + "%d: got error, but want success\n\n%s\n\n%s", + i, tc.One, tc.Two, + ) + continue + } + + if err == nil && tc.Err { + t.Errorf( + "%d: got success, but want error\n\n%s\n\n%s", + i, tc.One, tc.Two, + ) + continue + } + + if result != tc.Result { + t.Errorf( + "%d: got result %d, but want %d\n\n%s\n\n%s", + i, result, tc.Result, tc.One, tc.Two, + ) + continue + } + } +} + +func TestStateSameLineage(t *testing.T) { + cases := []struct { + Result bool + One, Two *State + }{ + { + true, + &State{ + Lineage: "1", + }, + &State{ + Lineage: "1", + }, + }, + { + // Empty lineage is compatible with all + true, + &State{ + Lineage: "", + }, + &State{ + Lineage: "1", + }, + }, + { + // Empty lineage is compatible with all + true, + &State{ + Lineage: "1", + }, + &State{ + Lineage: "", + }, + }, + { + false, + &State{ + Lineage: "1", + }, + &State{ + Lineage: "2", + }, + }, + } + + for i, tc := range cases { + result := tc.One.SameLineage(tc.Two) + + if result != tc.Result { + t.Errorf( + "%d: got %v, but want %v\n\n%s\n\n%s", + i, result, tc.Result, tc.One, tc.Two, + ) + continue + } + } +} + +func TestStateRemove(t *testing.T) { + cases := map[string]struct { + Address string + One, Two *State + }{ + "simple resource": { + "test_instance.foo", + &State{ + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "test_instance.foo": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + + "test_instance.bar": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + }, + }, + }, + }, + &State{ + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "test_instance.bar": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + }, + }, + }, + }, + }, + + "single instance": { + "test_instance.foo.primary", + &State{ + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "test_instance.foo": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + }, + }, + }, + }, + &State{ + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{}, + }, + }, + }, + }, + + "single instance in multi-count": { + "test_instance.foo[0]", + &State{ + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "test_instance.foo.0": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + + "test_instance.foo.1": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + }, + }, + }, + }, + &State{ + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "test_instance.foo.1": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + }, + }, + }, + }, + }, + + "single resource, multi-count": { + "test_instance.foo", + &State{ + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "test_instance.foo.0": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + + "test_instance.foo.1": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + }, + }, + }, + }, + &State{ + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{}, + }, + }, + }, + }, + + "full module": { + "module.foo", + &State{ + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "test_instance.foo": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + }, + }, + + { + Path: []string{"root", "foo"}, + Resources: map[string]*ResourceState{ + "test_instance.foo": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + + "test_instance.bar": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + }, + }, + }, + }, + &State{ + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "test_instance.foo": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + }, + }, + }, + }, + }, + + "module and children": { + "module.foo", + &State{ + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "test_instance.foo": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + }, + }, + + { + Path: []string{"root", "foo"}, + Resources: map[string]*ResourceState{ + "test_instance.foo": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + + "test_instance.bar": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + }, + }, + + { + Path: []string{"root", "foo", "bar"}, + Resources: map[string]*ResourceState{ + "test_instance.foo": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + + "test_instance.bar": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + }, + }, + }, + }, + &State{ + Modules: []*ModuleState{ + { + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "test_instance.foo": { + Type: "test_instance", + Primary: &InstanceState{ + ID: "foo", + }, + }, + }, + }, + }, + }, + }, + } + + for k, tc := range cases { + if err := tc.One.Remove(tc.Address); err != nil { + t.Fatalf("bad: %s\n\n%s", k, err) + } + + if !tc.One.Equal(tc.Two) { + t.Fatalf("Bad: %s\n\n%s\n\n%s", k, tc.One.String(), tc.Two.String()) + } + } +} + +func TestResourceStateEqual(t *testing.T) { + cases := []struct { + Result bool + One, Two *ResourceState + }{ + // Different types + { + false, + &ResourceState{Type: "foo"}, + &ResourceState{Type: "bar"}, + }, + + // Different dependencies + { + false, + &ResourceState{Dependencies: []string{"foo"}}, + &ResourceState{Dependencies: []string{"bar"}}, + }, + + { + false, + &ResourceState{Dependencies: []string{"foo", "bar"}}, + &ResourceState{Dependencies: []string{"foo"}}, + }, + + { + true, + &ResourceState{Dependencies: []string{"bar", "foo"}}, + &ResourceState{Dependencies: []string{"foo", "bar"}}, + }, + + // Different primaries + { + false, + &ResourceState{Primary: nil}, + &ResourceState{Primary: &InstanceState{ID: "foo"}}, + }, + + { + true, + &ResourceState{Primary: &InstanceState{ID: "foo"}}, + &ResourceState{Primary: &InstanceState{ID: "foo"}}, + }, + + // Different tainted + { + false, + &ResourceState{ + Primary: &InstanceState{ + ID: "foo", + }, + }, + &ResourceState{ + Primary: &InstanceState{ + ID: "foo", + Tainted: true, + }, + }, + }, + + { + true, + &ResourceState{ + Primary: &InstanceState{ + ID: "foo", + Tainted: true, + }, + }, + &ResourceState{ + Primary: &InstanceState{ + ID: "foo", + Tainted: true, + }, + }, + }, + } + + for i, tc := range cases { + if tc.One.Equal(tc.Two) != tc.Result { + t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) + } + if tc.Two.Equal(tc.One) != tc.Result { + t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) + } + } +} + +func TestResourceStateTaint(t *testing.T) { + cases := map[string]struct { + Input *ResourceState + Output *ResourceState + }{ + "no primary": { + &ResourceState{}, + &ResourceState{}, + }, + + "primary, not tainted": { + &ResourceState{ + Primary: &InstanceState{ID: "foo"}, + }, + &ResourceState{ + Primary: &InstanceState{ + ID: "foo", + Tainted: true, + }, + }, + }, + + "primary, tainted": { + &ResourceState{ + Primary: &InstanceState{ + ID: "foo", + Tainted: true, + }, + }, + &ResourceState{ + Primary: &InstanceState{ + ID: "foo", + Tainted: true, + }, + }, + }, + } + + for k, tc := range cases { + tc.Input.Taint() + if !reflect.DeepEqual(tc.Input, tc.Output) { + t.Fatalf( + "Failure: %s\n\nExpected: %#v\n\nGot: %#v", + k, tc.Output, tc.Input) + } + } +} + +func TestResourceStateUntaint(t *testing.T) { + cases := map[string]struct { + Input *ResourceState + ExpectedOutput *ResourceState + }{ + "no primary, err": { + Input: &ResourceState{}, + ExpectedOutput: &ResourceState{}, + }, + + "primary, not tainted": { + Input: &ResourceState{ + Primary: &InstanceState{ID: "foo"}, + }, + ExpectedOutput: &ResourceState{ + Primary: &InstanceState{ID: "foo"}, + }, + }, + "primary, tainted": { + Input: &ResourceState{ + Primary: &InstanceState{ + ID: "foo", + Tainted: true, + }, + }, + ExpectedOutput: &ResourceState{ + Primary: &InstanceState{ID: "foo"}, + }, + }, + } + + for k, tc := range cases { + tc.Input.Untaint() + if !reflect.DeepEqual(tc.Input, tc.ExpectedOutput) { + t.Fatalf( + "Failure: %s\n\nExpected: %#v\n\nGot: %#v", + k, tc.ExpectedOutput, tc.Input) + } + } +} + +func TestInstanceStateEmpty(t *testing.T) { + cases := map[string]struct { + In *InstanceState + Result bool + }{ + "nil is empty": { + nil, + true, + }, + "non-nil but without ID is empty": { + &InstanceState{}, + true, + }, + "with ID is not empty": { + &InstanceState{ + ID: "i-abc123", + }, + false, + }, + } + + for tn, tc := range cases { + if tc.In.Empty() != tc.Result { + t.Fatalf("%q expected %#v to be empty: %#v", tn, tc.In, tc.Result) + } + } +} + +func TestInstanceStateEqual(t *testing.T) { + cases := []struct { + Result bool + One, Two *InstanceState + }{ + // Nils + { + false, + nil, + &InstanceState{}, + }, + + { + false, + &InstanceState{}, + nil, + }, + + // Different IDs + { + false, + &InstanceState{ID: "foo"}, + &InstanceState{ID: "bar"}, + }, + + // Different Attributes + { + false, + &InstanceState{Attributes: map[string]string{"foo": "bar"}}, + &InstanceState{Attributes: map[string]string{"foo": "baz"}}, + }, + + // Different Attribute keys + { + false, + &InstanceState{Attributes: map[string]string{"foo": "bar"}}, + &InstanceState{Attributes: map[string]string{"bar": "baz"}}, + }, + + { + false, + &InstanceState{Attributes: map[string]string{"bar": "baz"}}, + &InstanceState{Attributes: map[string]string{"foo": "bar"}}, + }, + } + + for i, tc := range cases { + if tc.One.Equal(tc.Two) != tc.Result { + t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String()) + } + } +} + +func TestStateEmpty(t *testing.T) { + cases := []struct { + In *State + Result bool + }{ + { + nil, + true, + }, + { + &State{}, + true, + }, + { + &State{ + Remote: &RemoteState{Type: "foo"}, + }, + true, + }, + { + &State{ + Modules: []*ModuleState{ + {}, + }, + }, + false, + }, + } + + for i, tc := range cases { + if tc.In.Empty() != tc.Result { + t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In) + } + } +} + +func TestStateHasResources(t *testing.T) { + cases := []struct { + In *State + Result bool + }{ + { + nil, + false, + }, + { + &State{}, + false, + }, + { + &State{ + Remote: &RemoteState{Type: "foo"}, + }, + false, + }, + { + &State{ + Modules: []*ModuleState{ + {}, + }, + }, + false, + }, + { + &State{ + Modules: []*ModuleState{ + {}, + {}, + }, + }, + false, + }, + { + &State{ + Modules: []*ModuleState{ + {}, + { + Resources: map[string]*ResourceState{ + "foo.foo": {}, + }, + }, + }, + }, + true, + }, + } + + for i, tc := range cases { + if tc.In.HasResources() != tc.Result { + t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In) + } + } +} + +func TestStateIsRemote(t *testing.T) { + cases := []struct { + In *State + Result bool + }{ + { + nil, + false, + }, + { + &State{}, + false, + }, + { + &State{ + Remote: &RemoteState{Type: "foo"}, + }, + true, + }, + } + + for i, tc := range cases { + if tc.In.IsRemote() != tc.Result { + t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In) + } + } +} + +func TestInstanceState_MergeDiff(t *testing.T) { + is := InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "foo": "bar", + "port": "8000", + }, + } + + diff := &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": { + Old: "bar", + New: "baz", + }, + "bar": { + Old: "", + New: "foo", + }, + "baz": { + Old: "", + New: "foo", + NewComputed: true, + }, + "port": { + NewRemoved: true, + }, + }, + } + + is2 := is.MergeDiff(diff) + + expected := map[string]string{ + "foo": "baz", + "bar": "foo", + "baz": hcl2shim.UnknownVariableValue, + } + + if !reflect.DeepEqual(expected, is2.Attributes) { + t.Fatalf("bad: %#v", is2.Attributes) + } +} + +// GH-12183. This tests that a list with a computed set generates the +// right partial state. This never failed but is put here for completion +// of the test case for GH-12183. +func TestInstanceState_MergeDiff_computedSet(t *testing.T) { + is := InstanceState{} + + diff := &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "config.#": { + Old: "0", + New: "1", + RequiresNew: true, + }, + + "config.0.name": { + Old: "", + New: "hello", + }, + + "config.0.rules.#": { + Old: "", + NewComputed: true, + }, + }, + } + + is2 := is.MergeDiff(diff) + + expected := map[string]string{ + "config.#": "1", + "config.0.name": "hello", + "config.0.rules.#": hcl2shim.UnknownVariableValue, + } + + if !reflect.DeepEqual(expected, is2.Attributes) { + t.Fatalf("bad: %#v", is2.Attributes) + } +} + +func TestInstanceState_MergeDiff_nil(t *testing.T) { + var is *InstanceState + + diff := &InstanceDiff{ + Attributes: map[string]*ResourceAttrDiff{ + "foo": { + Old: "", + New: "baz", + }, + }, + } + + is2 := is.MergeDiff(diff) + + expected := map[string]string{ + "foo": "baz", + } + + if !reflect.DeepEqual(expected, is2.Attributes) { + t.Fatalf("bad: %#v", is2.Attributes) + } +} + +func TestInstanceState_MergeDiff_nilDiff(t *testing.T) { + is := InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "foo": "bar", + }, + } + + is2 := is.MergeDiff(nil) + + expected := map[string]string{ + "foo": "bar", + } + + if !reflect.DeepEqual(expected, is2.Attributes) { + t.Fatalf("bad: %#v", is2.Attributes) + } +} + +func TestParseResourceStateKey(t *testing.T) { + cases := []struct { + Input string + Expected *ResourceStateKey + ExpectedErr bool + }{ + { + Input: "aws_instance.foo.3", + Expected: &ResourceStateKey{ + Mode: ManagedResourceMode, + Type: "aws_instance", + Name: "foo", + Index: 3, + }, + }, + { + Input: "aws_instance.foo.0", + Expected: &ResourceStateKey{ + Mode: ManagedResourceMode, + Type: "aws_instance", + Name: "foo", + Index: 0, + }, + }, + { + Input: "aws_instance.foo", + Expected: &ResourceStateKey{ + Mode: ManagedResourceMode, + Type: "aws_instance", + Name: "foo", + Index: -1, + }, + }, + { + Input: "data.aws_ami.foo", + Expected: &ResourceStateKey{ + Mode: DataResourceMode, + Type: "aws_ami", + Name: "foo", + Index: -1, + }, + }, + { + Input: "aws_instance.foo.malformed", + ExpectedErr: true, + }, + { + Input: "aws_instance.foo.malformedwithnumber.123", + ExpectedErr: true, + }, + { + Input: "malformed", + ExpectedErr: true, + }, + } + for _, tc := range cases { + rsk, err := parseResourceStateKey(tc.Input) + if rsk != nil && tc.Expected != nil && !rsk.Equal(tc.Expected) { + t.Fatalf("%s: expected %s, got %s", tc.Input, tc.Expected, rsk) + } + if (err != nil) != tc.ExpectedErr { + t.Fatalf("%s: expected err: %t, got %s", tc.Input, tc.ExpectedErr, err) + } + } +} + +func TestResourceNameSort(t *testing.T) { + names := []string{ + "a", + "b", + "a.0", + "a.c", + "a.d", + "c", + "a.b.0", + "a.b.1", + "a.b.10", + "a.b.2", + } + + sort.Sort(resourceNameSort(names)) + + expected := []string{ + "a", + "a.0", + "a.b.0", + "a.b.1", + "a.b.2", + "a.b.10", + "a.c", + "a.d", + "b", + "c", + } + + if !reflect.DeepEqual(names, expected) { + t.Fatalf("got: %q\nexpected: %q\n", names, expected) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform.go deleted file mode 100644 index f9559f41..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform.go +++ /dev/null @@ -1,62 +0,0 @@ -package terraform - -import ( - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" -) - -// GraphTransformer is the interface that transformers implement. This -// interface is only for transforms that need entire graph visibility. -type GraphTransformer interface { - Transform(*Graph) error -} - -// GraphVertexTransformer is an interface that transforms a single -// Vertex within with graph. This is a specialization of GraphTransformer -// that makes it easy to do vertex replacement. -// -// The GraphTransformer that runs through the GraphVertexTransformers is -// VertexTransformer. -type GraphVertexTransformer interface { - Transform(dag.Vertex) (dag.Vertex, error) -} - -// GraphTransformIf is a helper function that conditionally returns a -// GraphTransformer given. This is useful for calling inline a sequence -// of transforms without having to split it up into multiple append() calls. -func GraphTransformIf(f func() bool, then GraphTransformer) GraphTransformer { - if f() { - return then - } - - return nil -} - -type graphTransformerMulti struct { - Transforms []GraphTransformer -} - -func (t *graphTransformerMulti) Transform(g *Graph) error { - var lastStepStr string - for _, t := range t.Transforms { - log.Printf("[TRACE] (graphTransformerMulti) Executing graph transform %T", t) - if err := t.Transform(g); err != nil { - return err - } - if thisStepStr := g.StringWithNodeTypes(); thisStepStr != lastStepStr { - log.Printf("[TRACE] (graphTransformerMulti) Completed graph transform %T with new graph:\n%s------", t, thisStepStr) - lastStepStr = thisStepStr - } else { - log.Printf("[TRACE] (graphTransformerMulti) Completed graph transform %T (no changes)", t) - } - } - - return nil -} - -// GraphTransformMulti combines multiple graph transformers into a single -// GraphTransformer that runs all the individual graph transformers. -func GraphTransformMulti(ts ...GraphTransformer) GraphTransformer { - return &graphTransformerMulti{Transforms: ts} -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_attach_config_provider.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_attach_config_provider.go deleted file mode 100644 index cbac1338..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_attach_config_provider.go +++ /dev/null @@ -1,19 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" -) - -// GraphNodeAttachProvider is an interface that must be implemented by nodes -// that want provider configurations attached. -type GraphNodeAttachProvider interface { - // Must be implemented to determine the path for the configuration - GraphNodeSubPath - - // ProviderName with no module prefix. Example: "aws". - ProviderAddr() addrs.AbsProviderConfig - - // Sets the configuration - AttachProvider(*configs.Provider) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_attach_config_resource.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_attach_config_resource.go deleted file mode 100644 index 23578c78..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_attach_config_resource.go +++ /dev/null @@ -1,74 +0,0 @@ -package terraform - -import ( - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" -) - -// GraphNodeAttachResourceConfig is an interface that must be implemented by nodes -// that want resource configurations attached. -type GraphNodeAttachResourceConfig interface { - GraphNodeResource - - // Sets the configuration - AttachResourceConfig(*configs.Resource) -} - -// AttachResourceConfigTransformer goes through the graph and attaches -// resource configuration structures to nodes that implement -// GraphNodeAttachManagedResourceConfig or GraphNodeAttachDataResourceConfig. -// -// The attached configuration structures are directly from the configuration. -// If they're going to be modified, a copy should be made. -type AttachResourceConfigTransformer struct { - Config *configs.Config // Config is the root node in the config tree -} - -func (t *AttachResourceConfigTransformer) Transform(g *Graph) error { - - // Go through and find GraphNodeAttachResource - for _, v := range g.Vertices() { - // Only care about GraphNodeAttachResource implementations - arn, ok := v.(GraphNodeAttachResourceConfig) - if !ok { - continue - } - - // Determine what we're looking for - addr := arn.ResourceAddr() - - // Get the configuration. - config := t.Config.DescendentForInstance(addr.Module) - if config == nil { - log.Printf("[TRACE] AttachResourceConfigTransformer: %q (%T) has no configuration available", dag.VertexName(v), v) - continue - } - - for _, r := range config.Module.ManagedResources { - rAddr := r.Addr() - - if rAddr != addr.Resource { - // Not the same resource - continue - } - - log.Printf("[TRACE] AttachResourceConfigTransformer: attaching to %q (%T) config from %s", dag.VertexName(v), v, r.DeclRange) - arn.AttachResourceConfig(r) - } - for _, r := range config.Module.DataResources { - rAddr := r.Addr() - - if rAddr != addr.Resource { - // Not the same resource - continue - } - - log.Printf("[TRACE] AttachResourceConfigTransformer: attaching to %q (%T) config from %#v", dag.VertexName(v), v, r.DeclRange) - arn.AttachResourceConfig(r) - } - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_attach_schema.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_attach_schema.go deleted file mode 100644 index fee220b5..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_attach_schema.go +++ /dev/null @@ -1,99 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" -) - -// GraphNodeAttachResourceSchema is an interface implemented by node types -// that need a resource schema attached. -type GraphNodeAttachResourceSchema interface { - GraphNodeResource - GraphNodeProviderConsumer - - AttachResourceSchema(schema *configschema.Block, version uint64) -} - -// GraphNodeAttachProviderConfigSchema is an interface implemented by node types -// that need a provider configuration schema attached. -type GraphNodeAttachProviderConfigSchema interface { - GraphNodeProvider - - AttachProviderConfigSchema(*configschema.Block) -} - -// GraphNodeAttachProvisionerSchema is an interface implemented by node types -// that need one or more provisioner schemas attached. -type GraphNodeAttachProvisionerSchema interface { - ProvisionedBy() []string - - // SetProvisionerSchema is called during transform for each provisioner - // type returned from ProvisionedBy, providing the configuration schema - // for each provisioner in turn. The implementer should save these for - // later use in evaluating provisioner configuration blocks. - AttachProvisionerSchema(name string, schema *configschema.Block) -} - -// AttachSchemaTransformer finds nodes that implement -// GraphNodeAttachResourceSchema, GraphNodeAttachProviderConfigSchema, or -// GraphNodeAttachProvisionerSchema, looks up the needed schemas for each -// and then passes them to a method implemented by the node. -type AttachSchemaTransformer struct { - Schemas *Schemas -} - -func (t *AttachSchemaTransformer) Transform(g *Graph) error { - if t.Schemas == nil { - // Should never happen with a reasonable caller, but we'll return a - // proper error here anyway so that we'll fail gracefully. - return fmt.Errorf("AttachSchemaTransformer used with nil Schemas") - } - - for _, v := range g.Vertices() { - - if tv, ok := v.(GraphNodeAttachResourceSchema); ok { - addr := tv.ResourceAddr() - mode := addr.Resource.Mode - typeName := addr.Resource.Type - providerAddr, _ := tv.ProvidedBy() - providerType := providerAddr.ProviderConfig.Type - - schema, version := t.Schemas.ResourceTypeConfig(providerType, mode, typeName) - if schema == nil { - log.Printf("[ERROR] AttachSchemaTransformer: No resource schema available for %s", addr) - continue - } - log.Printf("[TRACE] AttachSchemaTransformer: attaching resource schema to %s", dag.VertexName(v)) - tv.AttachResourceSchema(schema, version) - } - - if tv, ok := v.(GraphNodeAttachProviderConfigSchema); ok { - providerAddr := tv.ProviderAddr() - schema := t.Schemas.ProviderConfig(providerAddr.ProviderConfig.Type) - if schema == nil { - log.Printf("[ERROR] AttachSchemaTransformer: No provider config schema available for %s", providerAddr) - continue - } - log.Printf("[TRACE] AttachSchemaTransformer: attaching provider config schema to %s", dag.VertexName(v)) - tv.AttachProviderConfigSchema(schema) - } - - if tv, ok := v.(GraphNodeAttachProvisionerSchema); ok { - names := tv.ProvisionedBy() - for _, name := range names { - schema := t.Schemas.ProvisionerConfig(name) - if schema == nil { - log.Printf("[ERROR] AttachSchemaTransformer: No schema available for provisioner %q on %q", name, dag.VertexName(v)) - continue - } - log.Printf("[TRACE] AttachSchemaTransformer: attaching provisioner %q config schema to %s", name, dag.VertexName(v)) - tv.AttachProvisionerSchema(name, schema) - } - } - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_attach_state.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_attach_state.go deleted file mode 100644 index f8749487..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_attach_state.go +++ /dev/null @@ -1,68 +0,0 @@ -package terraform - -import ( - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// GraphNodeAttachResourceState is an interface that can be implemented -// to request that a ResourceState is attached to the node. -// -// Due to a historical naming inconsistency, the type ResourceState actually -// represents the state for a particular _instance_, while InstanceState -// represents the values for that instance during a particular phase -// (e.g. primary vs. deposed). Consequently, GraphNodeAttachResourceState -// is supported only for nodes that represent resource instances, even though -// the name might suggest it is for containing resources. -type GraphNodeAttachResourceState interface { - GraphNodeResourceInstance - - // Sets the state - AttachResourceState(*states.Resource) -} - -// AttachStateTransformer goes through the graph and attaches -// state to nodes that implement the interfaces above. -type AttachStateTransformer struct { - State *states.State // State is the root state -} - -func (t *AttachStateTransformer) Transform(g *Graph) error { - // If no state, then nothing to do - if t.State == nil { - log.Printf("[DEBUG] Not attaching any node states: overall state is nil") - return nil - } - - for _, v := range g.Vertices() { - // Nodes implement this interface to request state attachment. - an, ok := v.(GraphNodeAttachResourceState) - if !ok { - continue - } - addr := an.ResourceInstanceAddr() - - rs := t.State.Resource(addr.ContainingResource()) - if rs == nil { - log.Printf("[DEBUG] Resource state not found for node %q, instance %s", dag.VertexName(v), addr) - continue - } - - is := rs.Instance(addr.Resource.Key) - if is == nil { - // We don't actually need this here, since we'll attach the whole - // resource state, but we still check because it'd be weird - // for the specific instance we're attaching to not to exist. - log.Printf("[DEBUG] Resource instance state not found for node %q, instance %s", dag.VertexName(v), addr) - continue - } - - // make sure to attach a copy of the state, so instances can modify the - // same ResourceState. - an.AttachResourceState(rs.DeepCopy()) - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_config.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_config.go deleted file mode 100644 index 8920761e..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_config.go +++ /dev/null @@ -1,133 +0,0 @@ -package terraform - -import ( - "log" - "sync" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" -) - -// ConfigTransformer is a GraphTransformer that adds all the resources -// from the configuration to the graph. -// -// The module used to configure this transformer must be the root module. -// -// Only resources are added to the graph. Variables, outputs, and -// providers must be added via other transforms. -// -// Unlike ConfigTransformerOld, this transformer creates a graph with -// all resources including module resources, rather than creating module -// nodes that are then "flattened". -type ConfigTransformer struct { - Concrete ConcreteResourceNodeFunc - - // Module is the module to add resources from. - Config *configs.Config - - // Unique will only add resources that aren't already present in the graph. - Unique bool - - // Mode will only add resources that match the given mode - ModeFilter bool - Mode addrs.ResourceMode - - l sync.Mutex - uniqueMap map[string]struct{} -} - -func (t *ConfigTransformer) Transform(g *Graph) error { - // Lock since we use some internal state - t.l.Lock() - defer t.l.Unlock() - - // If no configuration is available, we don't do anything - if t.Config == nil { - return nil - } - - // Reset the uniqueness map. If we're tracking uniques, then populate - // it with addresses. - t.uniqueMap = make(map[string]struct{}) - defer func() { t.uniqueMap = nil }() - if t.Unique { - for _, v := range g.Vertices() { - if rn, ok := v.(GraphNodeResource); ok { - t.uniqueMap[rn.ResourceAddr().String()] = struct{}{} - } - } - } - - // Start the transformation process - return t.transform(g, t.Config) -} - -func (t *ConfigTransformer) transform(g *Graph, config *configs.Config) error { - // If no config, do nothing - if config == nil { - return nil - } - - // Add our resources - if err := t.transformSingle(g, config); err != nil { - return err - } - - // Transform all the children. - for _, c := range config.Children { - if err := t.transform(g, c); err != nil { - return err - } - } - - return nil -} - -func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config) error { - path := config.Path - module := config.Module - log.Printf("[TRACE] ConfigTransformer: Starting for path: %v", path) - - // For now we assume that each module call produces only one module - // instance with no key, since we don't yet support "count" and "for_each" - // on modules. - // FIXME: As part of supporting "count" and "for_each" on modules, rework - // this so that we'll "expand" the module call first and then create graph - // nodes for each module instance separately. - instPath := path.UnkeyedInstanceShim() - - allResources := make([]*configs.Resource, 0, len(module.ManagedResources)+len(module.DataResources)) - for _, r := range module.ManagedResources { - allResources = append(allResources, r) - } - for _, r := range module.DataResources { - allResources = append(allResources, r) - } - - for _, r := range allResources { - relAddr := r.Addr() - - if t.ModeFilter && relAddr.Mode != t.Mode { - // Skip non-matching modes - continue - } - - addr := relAddr.Absolute(instPath) - if _, ok := t.uniqueMap[addr.String()]; ok { - // We've already seen a resource with this address. This should - // never happen, because we enforce uniqueness in the config loader. - continue - } - - abstract := &NodeAbstractResource{Addr: addr} - var node dag.Vertex = abstract - if f := t.Concrete; f != nil { - node = f(abstract) - } - - g.Add(node) - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_count_boundary.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_count_boundary.go deleted file mode 100644 index 892f75ec..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_count_boundary.go +++ /dev/null @@ -1,33 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" -) - -// CountBoundaryTransformer adds a node that depends on everything else -// so that it runs last in order to clean up the state for nodes that -// are on the "count boundary": "foo.0" when only one exists becomes "foo" -type CountBoundaryTransformer struct { - Config *configs.Config -} - -func (t *CountBoundaryTransformer) Transform(g *Graph) error { - node := &NodeCountBoundary{ - Config: t.Config, - } - g.Add(node) - - // Depends on everything - for _, v := range g.Vertices() { - // Don't connect to ourselves - if v == node { - continue - } - - // Connect! - g.Connect(dag.BasicEdge(node, v)) - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_destroy_cbd.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_destroy_cbd.go deleted file mode 100644 index 98e088ee..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_destroy_cbd.go +++ /dev/null @@ -1,297 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// GraphNodeDestroyerCBD must be implemented by nodes that might be -// create-before-destroy destroyers, or might plan a create-before-destroy -// action. -type GraphNodeDestroyerCBD interface { - // CreateBeforeDestroy returns true if this node represents a node - // that is doing a CBD. - CreateBeforeDestroy() bool - - // ModifyCreateBeforeDestroy is called when the CBD state of a node - // is changed dynamically. This can return an error if this isn't - // allowed. - ModifyCreateBeforeDestroy(bool) error -} - -// GraphNodeAttachDestroyer is implemented by applyable nodes that have a -// companion destroy node. This allows the creation node to look up the status -// of the destroy node and determine if it needs to depose the existing state, -// or replace it. -// If a node is not marked as create-before-destroy in the configuration, but a -// dependency forces that status, only the destroy node will be aware of that -// status. -type GraphNodeAttachDestroyer interface { - // AttachDestroyNode takes a destroy node and saves a reference to that - // node in the receiver, so it can later check the status of - // CreateBeforeDestroy(). - AttachDestroyNode(n GraphNodeDestroyerCBD) -} - -// ForcedCBDTransformer detects when a particular CBD-able graph node has -// dependencies with another that has create_before_destroy set that require -// it to be forced on, and forces it on. -// -// This must be used in the plan graph builder to ensure that -// create_before_destroy settings are properly propagated before constructing -// the planned changes. This requires that the plannable resource nodes -// implement GraphNodeDestroyerCBD. -type ForcedCBDTransformer struct { -} - -func (t *ForcedCBDTransformer) Transform(g *Graph) error { - for _, v := range g.Vertices() { - dn, ok := v.(GraphNodeDestroyerCBD) - if !ok { - continue - } - - if !dn.CreateBeforeDestroy() { - // If there are no CBD decendent (dependent nodes), then we - // do nothing here. - if !t.hasCBDDescendent(g, v) { - log.Printf("[TRACE] ForcedCBDTransformer: %q (%T) has no CBD descendent, so skipping", dag.VertexName(v), v) - continue - } - - // If this isn't naturally a CBD node, this means that an descendent is - // and we need to auto-upgrade this node to CBD. We do this because - // a CBD node depending on non-CBD will result in cycles. To avoid this, - // we always attempt to upgrade it. - log.Printf("[TRACE] ForcedCBDTransformer: forcing create_before_destroy on for %q (%T)", dag.VertexName(v), v) - if err := dn.ModifyCreateBeforeDestroy(true); err != nil { - return fmt.Errorf( - "%s: must have create before destroy enabled because "+ - "a dependent resource has CBD enabled. However, when "+ - "attempting to automatically do this, an error occurred: %s", - dag.VertexName(v), err) - } - } else { - log.Printf("[TRACE] ForcedCBDTransformer: %q (%T) already has create_before_destroy set", dag.VertexName(v), v) - } - } - return nil -} - -// hasCBDDescendent returns true if any descendent (node that depends on this) -// has CBD set. -func (t *ForcedCBDTransformer) hasCBDDescendent(g *Graph, v dag.Vertex) bool { - s, _ := g.Descendents(v) - if s == nil { - return true - } - - for _, ov := range s.List() { - dn, ok := ov.(GraphNodeDestroyerCBD) - if !ok { - continue - } - - if dn.CreateBeforeDestroy() { - // some descendent is CreateBeforeDestroy, so we need to follow suit - log.Printf("[TRACE] ForcedCBDTransformer: %q has CBD descendent %q", dag.VertexName(v), dag.VertexName(ov)) - return true - } - } - - return false -} - -// CBDEdgeTransformer modifies the edges of CBD nodes that went through -// the DestroyEdgeTransformer to have the right dependencies. There are -// two real tasks here: -// -// 1. With CBD, the destroy edge is inverted: the destroy depends on -// the creation. -// -// 2. A_d must depend on resources that depend on A. This is to enable -// the destroy to only happen once nodes that depend on A successfully -// update to A. Example: adding a web server updates the load balancer -// before deleting the old web server. -// -// This transformer requires that a previous transformer has already forced -// create_before_destroy on for nodes that are depended on by explicit CBD -// nodes. This is the logic in ForcedCBDTransformer, though in practice we -// will get here by recording the CBD-ness of each change in the plan during -// the plan walk and then forcing the nodes into the appropriate setting during -// DiffTransformer when building the apply graph. -type CBDEdgeTransformer struct { - // Module and State are only needed to look up dependencies in - // any way possible. Either can be nil if not availabile. - Config *configs.Config - State *states.State - - // If configuration is present then Schemas is required in order to - // obtain schema information from providers and provisioners so we can - // properly resolve implicit dependencies. - Schemas *Schemas - - // If the operation is a simple destroy, no transformation is done. - Destroy bool -} - -func (t *CBDEdgeTransformer) Transform(g *Graph) error { - if t.Destroy { - return nil - } - - // Go through and reverse any destroy edges - destroyMap := make(map[string][]dag.Vertex) - for _, v := range g.Vertices() { - dn, ok := v.(GraphNodeDestroyerCBD) - if !ok { - continue - } - dern, ok := v.(GraphNodeDestroyer) - if !ok { - continue - } - - if !dn.CreateBeforeDestroy() { - continue - } - - // Find the destroy edge. There should only be one. - for _, e := range g.EdgesTo(v) { - // Not a destroy edge, ignore it - de, ok := e.(*DestroyEdge) - if !ok { - continue - } - - log.Printf("[TRACE] CBDEdgeTransformer: inverting edge: %s => %s", - dag.VertexName(de.Source()), dag.VertexName(de.Target())) - - // Found it! Invert. - g.RemoveEdge(de) - applyNode := de.Source() - destroyNode := de.Target() - g.Connect(&DestroyEdge{S: destroyNode, T: applyNode}) - break - } - - // If the address has an index, we strip that. Our depMap creation - // graph doesn't expand counts so we don't currently get _exact_ - // dependencies. One day when we limit dependencies more exactly - // this will have to change. We have a test case covering this - // (depNonCBDCountBoth) so it'll be caught. - addr := dern.DestroyAddr() - key := addr.ContainingResource().String() - - // Add this to the list of nodes that we need to fix up - // the edges for (step 2 above in the docs). - destroyMap[key] = append(destroyMap[key], v) - } - - // If we have no CBD nodes, then our work here is done - if len(destroyMap) == 0 { - return nil - } - - // We have CBD nodes. We now have to move on to the much more difficult - // task of connecting dependencies of the creation side of the destroy - // to the destruction node. The easiest way to explain this is an example: - // - // Given a pre-destroy dependence of: A => B - // And A has CBD set. - // - // The resulting graph should be: A => B => A_d - // - // They key here is that B happens before A is destroyed. This is to - // facilitate the primary purpose for CBD: making sure that downstreams - // are properly updated to avoid downtime before the resource is destroyed. - depMap, err := t.depMap(g, destroyMap) - if err != nil { - return err - } - - // We now have the mapping of resource addresses to the destroy - // nodes they need to depend on. We now go through our own vertices to - // find any matching these addresses and make the connection. - for _, v := range g.Vertices() { - // We're looking for creators - rn, ok := v.(GraphNodeCreator) - if !ok { - continue - } - - // Get the address - addr := rn.CreateAddr() - - // If the address has an index, we strip that. Our depMap creation - // graph doesn't expand counts so we don't currently get _exact_ - // dependencies. One day when we limit dependencies more exactly - // this will have to change. We have a test case covering this - // (depNonCBDCount) so it'll be caught. - key := addr.ContainingResource().String() - - // If there is nothing this resource should depend on, ignore it - dns, ok := depMap[key] - if !ok { - continue - } - - // We have nodes! Make the connection - for _, dn := range dns { - log.Printf("[TRACE] CBDEdgeTransformer: destroy depends on dependence: %s => %s", - dag.VertexName(dn), dag.VertexName(v)) - g.Connect(dag.BasicEdge(dn, v)) - } - } - - return nil -} - -func (t *CBDEdgeTransformer) depMap(g *Graph, destroyMap map[string][]dag.Vertex) (map[string][]dag.Vertex, error) { - // Build the list of destroy nodes that each resource address should depend - // on. For example, when we find B, we map the address of B to A_d in the - // "depMap" variable below. - depMap := make(map[string][]dag.Vertex) - for _, v := range g.Vertices() { - // We're looking for resources. - rn, ok := v.(GraphNodeResource) - if !ok { - continue - } - - // Get the address - addr := rn.ResourceAddr() - key := addr.String() - - // Get the destroy nodes that are destroying this resource. - // If there aren't any, then we don't need to worry about - // any connections. - dns, ok := destroyMap[key] - if !ok { - continue - } - - // Get the nodes that depend on this on. In the example above: - // finding B in A => B. Since dependencies can span modules, walk all - // descendents of the resource. - des, _ := g.Descendents(v) - for _, v := range des.List() { - // We're looking for resources. - rn, ok := v.(GraphNodeResource) - if !ok { - continue - } - - // Keep track of the destroy nodes that this address - // needs to depend on. - key := rn.ResourceAddr().String() - depMap[key] = append(depMap[key], dns...) - } - } - - return depMap, nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_destroy_edge.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_destroy_edge.go deleted file mode 100644 index 1d211570..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_destroy_edge.go +++ /dev/null @@ -1,323 +0,0 @@ -package terraform - -import ( - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" -) - -// GraphNodeDestroyer must be implemented by nodes that destroy resources. -type GraphNodeDestroyer interface { - dag.Vertex - - // DestroyAddr is the address of the resource that is being - // destroyed by this node. If this returns nil, then this node - // is not destroying anything. - DestroyAddr() *addrs.AbsResourceInstance -} - -// GraphNodeCreator must be implemented by nodes that create OR update resources. -type GraphNodeCreator interface { - // CreateAddr is the address of the resource being created or updated - CreateAddr() *addrs.AbsResourceInstance -} - -// DestroyEdgeTransformer is a GraphTransformer that creates the proper -// references for destroy resources. Destroy resources are more complex -// in that they must be depend on the destruction of resources that -// in turn depend on the CREATION of the node being destroy. -// -// That is complicated. Visually: -// -// B_d -> A_d -> A -> B -// -// Notice that A destroy depends on B destroy, while B create depends on -// A create. They're inverted. This must be done for example because often -// dependent resources will block parent resources from deleting. Concrete -// example: VPC with subnets, the VPC can't be deleted while there are -// still subnets. -type DestroyEdgeTransformer struct { - // These are needed to properly build the graph of dependencies - // to determine what a destroy node depends on. Any of these can be nil. - Config *configs.Config - State *states.State - - // If configuration is present then Schemas is required in order to - // obtain schema information from providers and provisioners in order - // to properly resolve implicit dependencies. - Schemas *Schemas -} - -func (t *DestroyEdgeTransformer) Transform(g *Graph) error { - // Build a map of what is being destroyed (by address string) to - // the list of destroyers. Usually there will be at most one destroyer - // per node, but we allow multiple if present for completeness. - destroyers := make(map[string][]GraphNodeDestroyer) - destroyerAddrs := make(map[string]addrs.AbsResourceInstance) - for _, v := range g.Vertices() { - dn, ok := v.(GraphNodeDestroyer) - if !ok { - continue - } - - addrP := dn.DestroyAddr() - if addrP == nil { - continue - } - addr := *addrP - - key := addr.String() - log.Printf("[TRACE] DestroyEdgeTransformer: %q (%T) destroys %s", dag.VertexName(dn), v, key) - destroyers[key] = append(destroyers[key], dn) - destroyerAddrs[key] = addr - } - - // If we aren't destroying anything, there will be no edges to make - // so just exit early and avoid future work. - if len(destroyers) == 0 { - return nil - } - - // Go through and connect creators to destroyers. Going along with - // our example, this makes: A_d => A - for _, v := range g.Vertices() { - cn, ok := v.(GraphNodeCreator) - if !ok { - continue - } - - addr := cn.CreateAddr() - if addr == nil { - continue - } - - key := addr.String() - ds := destroyers[key] - if len(ds) == 0 { - continue - } - - for _, d := range ds { - // For illustrating our example - a_d := d.(dag.Vertex) - a := v - - log.Printf( - "[TRACE] DestroyEdgeTransformer: connecting creator %q with destroyer %q", - dag.VertexName(a), dag.VertexName(a_d)) - - g.Connect(&DestroyEdge{S: a, T: a_d}) - - // Attach the destroy node to the creator - // There really shouldn't be more than one destroyer, but even if - // there are, any of them will represent the correct - // CreateBeforeDestroy status. - if n, ok := cn.(GraphNodeAttachDestroyer); ok { - if d, ok := d.(GraphNodeDestroyerCBD); ok { - n.AttachDestroyNode(d) - } - } - } - } - - // This is strange but is the easiest way to get the dependencies - // of a node that is being destroyed. We use another graph to make sure - // the resource is in the graph and ask for references. We have to do this - // because the node that is being destroyed may NOT be in the graph. - // - // Example: resource A is force new, then destroy A AND create A are - // in the graph. BUT if resource A is just pure destroy, then only - // destroy A is in the graph, and create A is not. - providerFn := func(a *NodeAbstractProvider) dag.Vertex { - return &NodeApplyableProvider{NodeAbstractProvider: a} - } - steps := []GraphTransformer{ - // Add the local values - &LocalTransformer{Config: t.Config}, - - // Add outputs and metadata - &OutputTransformer{Config: t.Config}, - &AttachResourceConfigTransformer{Config: t.Config}, - &AttachStateTransformer{State: t.State}, - - // Add all the variables. We can depend on resources through - // variables due to module parameters, and we need to properly - // determine that. - &RootVariableTransformer{Config: t.Config}, - &ModuleVariableTransformer{Config: t.Config}, - - TransformProviders(nil, providerFn, t.Config), - - // Must attach schemas before ReferenceTransformer so that we can - // analyze the configuration to find references. - &AttachSchemaTransformer{Schemas: t.Schemas}, - - &ReferenceTransformer{}, - } - - // Go through all the nodes being destroyed and create a graph. - // The resulting graph is only of things being CREATED. For example, - // following our example, the resulting graph would be: - // - // A, B (with no edges) - // - var tempG Graph - var tempDestroyed []dag.Vertex - for d := range destroyers { - // d is the string key for the resource being destroyed. We actually - // want the address value, which we stashed earlier. - addr := destroyerAddrs[d] - - // This part is a little bit weird but is the best way to - // find the dependencies we need to: build a graph and use the - // attach config and state transformers then ask for references. - abstract := NewNodeAbstractResourceInstance(addr) - tempG.Add(abstract) - tempDestroyed = append(tempDestroyed, abstract) - - // We also add the destroy version here since the destroy can - // depend on things that the creation doesn't (destroy provisioners). - destroy := &NodeDestroyResourceInstance{NodeAbstractResourceInstance: abstract} - tempG.Add(destroy) - tempDestroyed = append(tempDestroyed, destroy) - } - - // Run the graph transforms so we have the information we need to - // build references. - log.Printf("[TRACE] DestroyEdgeTransformer: constructing temporary graph for analysis of references, starting from:\n%s", tempG.StringWithNodeTypes()) - for _, s := range steps { - log.Printf("[TRACE] DestroyEdgeTransformer: running %T on temporary graph", s) - if err := s.Transform(&tempG); err != nil { - log.Printf("[TRACE] DestroyEdgeTransformer: %T failed: %s", s, err) - return err - } - } - log.Printf("[TRACE] DestroyEdgeTransformer: temporary reference graph:\n%s", tempG.String()) - - // Go through all the nodes in the graph and determine what they - // depend on. - for _, v := range tempDestroyed { - // Find all ancestors of this to determine the edges we'll depend on - vs, err := tempG.Ancestors(v) - if err != nil { - return err - } - - refs := make([]dag.Vertex, 0, vs.Len()) - for _, raw := range vs.List() { - refs = append(refs, raw.(dag.Vertex)) - } - - refNames := make([]string, len(refs)) - for i, ref := range refs { - refNames[i] = dag.VertexName(ref) - } - log.Printf( - "[TRACE] DestroyEdgeTransformer: creation node %q references %s", - dag.VertexName(v), refNames) - - // If we have no references, then we won't need to do anything - if len(refs) == 0 { - continue - } - - // Get the destroy node for this. In the example of our struct, - // we are currently at B and we're looking for B_d. - rn, ok := v.(GraphNodeResourceInstance) - if !ok { - log.Printf("[TRACE] DestroyEdgeTransformer: skipping %s, since it's not a resource", dag.VertexName(v)) - continue - } - - addr := rn.ResourceInstanceAddr() - dns := destroyers[addr.String()] - - // We have dependencies, check if any are being destroyed - // to build the list of things that we must depend on! - // - // In the example of the struct, if we have: - // - // B_d => A_d => A => B - // - // Then at this point in the algorithm we started with B_d, - // we built B (to get dependencies), and we found A. We're now looking - // to see if A_d exists. - var depDestroyers []dag.Vertex - for _, v := range refs { - rn, ok := v.(GraphNodeResourceInstance) - if !ok { - continue - } - - addr := rn.ResourceInstanceAddr() - key := addr.String() - if ds, ok := destroyers[key]; ok { - for _, d := range ds { - depDestroyers = append(depDestroyers, d.(dag.Vertex)) - log.Printf( - "[TRACE] DestroyEdgeTransformer: destruction of %q depends on %s", - key, dag.VertexName(d)) - } - } - } - - // Go through and make the connections. Use the variable - // names "a_d" and "b_d" to reference our example. - for _, a_d := range dns { - for _, b_d := range depDestroyers { - if b_d != a_d { - log.Printf("[TRACE] DestroyEdgeTransformer: %q depends on %q", dag.VertexName(b_d), dag.VertexName(a_d)) - g.Connect(dag.BasicEdge(b_d, a_d)) - } - } - } - } - - return t.pruneResources(g) -} - -// If there are only destroy instances for a particular resource, there's no -// reason for the resource node to prepare the state. Remove Resource nodes so -// that they don't fail by trying to evaluate a resource that is only being -// destroyed along with its dependencies. -func (t *DestroyEdgeTransformer) pruneResources(g *Graph) error { - for _, v := range g.Vertices() { - n, ok := v.(*NodeApplyableResource) - if !ok { - continue - } - - // if there are only destroy dependencies, we don't need this node - des, err := g.Descendents(n) - if err != nil { - return err - } - - descendents := des.List() - nonDestroyInstanceFound := false - for _, v := range descendents { - if _, ok := v.(*NodeApplyableResourceInstance); ok { - nonDestroyInstanceFound = true - break - } - } - - if nonDestroyInstanceFound { - continue - } - - // connect all the through-edges, then delete the node - for _, d := range g.DownEdges(n).List() { - for _, u := range g.UpEdges(n).List() { - g.Connect(dag.BasicEdge(u, d)) - } - } - log.Printf("DestroyEdgeTransformer: pruning unused resource node %s", dag.VertexName(n)) - g.Remove(n) - } - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_diff.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_diff.go deleted file mode 100644 index b7a237fc..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_diff.go +++ /dev/null @@ -1,184 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/plans" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// DiffTransformer is a GraphTransformer that adds graph nodes representing -// each of the resource changes described in the given Changes object. -type DiffTransformer struct { - Concrete ConcreteResourceInstanceNodeFunc - State *states.State - Changes *plans.Changes -} - -func (t *DiffTransformer) Transform(g *Graph) error { - if t.Changes == nil || len(t.Changes.Resources) == 0 { - // Nothing to do! - return nil - } - - // Go through all the modules in the diff. - log.Printf("[TRACE] DiffTransformer starting") - - var diags tfdiags.Diagnostics - state := t.State - changes := t.Changes - - // DiffTransformer creates resource _instance_ nodes. If there are any - // whole-resource nodes already in the graph, we must ensure that they - // get evaluated before any of the corresponding instances by creating - // dependency edges, so we'll do some prep work here to ensure we'll only - // create connections to nodes that existed before we started here. - resourceNodes := map[string][]GraphNodeResource{} - for _, node := range g.Vertices() { - rn, ok := node.(GraphNodeResource) - if !ok { - continue - } - // We ignore any instances that _also_ implement - // GraphNodeResourceInstance, since in the unlikely event that they - // do exist we'd probably end up creating cycles by connecting them. - if _, ok := node.(GraphNodeResourceInstance); ok { - continue - } - - addr := rn.ResourceAddr().String() - resourceNodes[addr] = append(resourceNodes[addr], rn) - } - - for _, rc := range changes.Resources { - addr := rc.Addr - dk := rc.DeposedKey - - log.Printf("[TRACE] DiffTransformer: found %s change for %s %s", rc.Action, addr, dk) - - // Depending on the action we'll need some different combinations of - // nodes, because destroying uses a special node type separate from - // other actions. - var update, delete, createBeforeDestroy bool - switch rc.Action { - case plans.NoOp: - continue - case plans.Delete: - delete = true - case plans.DeleteThenCreate, plans.CreateThenDelete: - update = true - delete = true - createBeforeDestroy = (rc.Action == plans.CreateThenDelete) - default: - update = true - } - - if dk != states.NotDeposed && update { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid planned change for deposed object", - fmt.Sprintf("The plan contains a non-delete change for %s deposed object %s. The only valid action for a deposed object is to destroy it, so this is a bug in Terraform.", addr, dk), - )) - continue - } - - // If we're going to do a create_before_destroy Replace operation then - // we need to allocate a DeposedKey to use to retain the - // not-yet-destroyed prior object, so that the delete node can destroy - // _that_ rather than the newly-created node, which will be current - // by the time the delete node is visited. - if update && delete && createBeforeDestroy { - // In this case, variable dk will be the _pre-assigned_ DeposedKey - // that must be used if the update graph node deposes the current - // instance, which will then align with the same key we pass - // into the destroy node to ensure we destroy exactly the deposed - // object we expect. - if state != nil { - ris := state.ResourceInstance(addr) - if ris == nil { - // Should never happen, since we don't plan to replace an - // instance that doesn't exist yet. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid planned change", - fmt.Sprintf("The plan contains a replace change for %s, which doesn't exist yet. This is a bug in Terraform.", addr), - )) - continue - } - - // Allocating a deposed key separately from using it can be racy - // in general, but we assume here that nothing except the apply - // node we instantiate below will actually make new deposed objects - // in practice, and so the set of already-used keys will not change - // between now and then. - dk = ris.FindUnusedDeposedKey() - } else { - // If we have no state at all yet then we can use _any_ - // DeposedKey. - dk = states.NewDeposedKey() - } - } - - if update { - // All actions except destroying the node type chosen by t.Concrete - abstract := NewNodeAbstractResourceInstance(addr) - var node dag.Vertex = abstract - if f := t.Concrete; f != nil { - node = f(abstract) - } - - if createBeforeDestroy { - // We'll attach our pre-allocated DeposedKey to the node if - // it supports that. NodeApplyableResourceInstance is the - // specific concrete node type we are looking for here really, - // since that's the only node type that might depose objects. - if dn, ok := node.(GraphNodeDeposer); ok { - dn.SetPreallocatedDeposedKey(dk) - } - log.Printf("[TRACE] DiffTransformer: %s will be represented by %s, deposing prior object to %s", addr, dag.VertexName(node), dk) - } else { - log.Printf("[TRACE] DiffTransformer: %s will be represented by %s", addr, dag.VertexName(node)) - } - - g.Add(node) - rsrcAddr := addr.ContainingResource().String() - for _, rsrcNode := range resourceNodes[rsrcAddr] { - g.Connect(dag.BasicEdge(node, rsrcNode)) - } - } - - if delete { - // Destroying always uses a destroy-specific node type, though - // which one depends on whether we're destroying a current object - // or a deposed object. - var node GraphNodeResourceInstance - abstract := NewNodeAbstractResourceInstance(addr) - if dk == states.NotDeposed { - node = &NodeDestroyResourceInstance{ - NodeAbstractResourceInstance: abstract, - DeposedKey: dk, - } - node.(*NodeDestroyResourceInstance).ModifyCreateBeforeDestroy(createBeforeDestroy) - } else { - node = &NodeDestroyDeposedResourceInstanceObject{ - NodeAbstractResourceInstance: abstract, - DeposedKey: dk, - } - } - if dk == states.NotDeposed { - log.Printf("[TRACE] DiffTransformer: %s will be represented for destruction by %s", addr, dag.VertexName(node)) - } else { - log.Printf("[TRACE] DiffTransformer: %s deposed object %s will be represented for destruction by %s", addr, dk, dag.VertexName(node)) - } - g.Add(node) - } - - } - - log.Printf("[TRACE] DiffTransformer complete") - - return diags.Err() -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_expand.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_expand.go deleted file mode 100644 index 03eac685..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_expand.go +++ /dev/null @@ -1,48 +0,0 @@ -package terraform - -import ( - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" -) - -// GraphNodeExapndable is an interface that nodes can implement to -// signal that they can be expanded. Expanded nodes turn into -// GraphNodeSubgraph nodes within the graph. -type GraphNodeExpandable interface { - Expand(GraphBuilder) (GraphNodeSubgraph, error) -} - -// GraphNodeDynamicExpandable is an interface that nodes can implement -// to signal that they can be expanded at eval-time (hence dynamic). -// These nodes are given the eval context and are expected to return -// a new subgraph. -type GraphNodeDynamicExpandable interface { - DynamicExpand(EvalContext) (*Graph, error) -} - -// GraphNodeSubgraph is an interface a node can implement if it has -// a larger subgraph that should be walked. -type GraphNodeSubgraph interface { - Subgraph() dag.Grapher -} - -// ExpandTransform is a transformer that does a subgraph expansion -// at graph transform time (vs. at eval time). The benefit of earlier -// subgraph expansion is that errors with the graph build can be detected -// at an earlier stage. -type ExpandTransform struct { - Builder GraphBuilder -} - -func (t *ExpandTransform) Transform(v dag.Vertex) (dag.Vertex, error) { - ev, ok := v.(GraphNodeExpandable) - if !ok { - // This isn't an expandable vertex, so just ignore it. - return v, nil - } - - // Expand the subgraph! - log.Printf("[DEBUG] vertex %q: static expanding", dag.VertexName(ev)) - return ev.Expand(t.Builder) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_import_provider.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_import_provider.go deleted file mode 100644 index 2ce23ddb..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_import_provider.go +++ /dev/null @@ -1,44 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// ImportProviderValidateTransformer is a GraphTransformer that goes through -// the providers in the graph and validates that they only depend on variables. -type ImportProviderValidateTransformer struct{} - -func (t *ImportProviderValidateTransformer) Transform(g *Graph) error { - var diags tfdiags.Diagnostics - - for _, v := range g.Vertices() { - // We only care about providers - pv, ok := v.(GraphNodeProvider) - if !ok { - continue - } - - // We only care about providers that reference things - rn, ok := pv.(GraphNodeReferencer) - if !ok { - continue - } - - for _, ref := range rn.References() { - if _, ok := ref.Subject.(addrs.InputVariable); !ok { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid provider dependency for import", - Detail: fmt.Sprintf("The configuration for %s depends on %s. Providers used with import must either have literal configuration or refer only to input variables.", pv.ProviderAddr(), ref.Subject.String()), - Subject: ref.SourceRange.ToHCL().Ptr(), - }) - } - } - } - - return diags.Err() -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_import_state.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_import_state.go deleted file mode 100644 index 7dd2c487..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_import_state.go +++ /dev/null @@ -1,239 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/providers" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// ImportStateTransformer is a GraphTransformer that adds nodes to the -// graph to represent the imports we want to do for resources. -type ImportStateTransformer struct { - Targets []*ImportTarget -} - -func (t *ImportStateTransformer) Transform(g *Graph) error { - for _, target := range t.Targets { - // The ProviderAddr may not be supplied for non-aliased providers. - // This will be populated if the targets come from the cli, but tests - // may not specify implied provider addresses. - providerAddr := target.ProviderAddr - if providerAddr.ProviderConfig.Type == "" { - providerAddr = target.Addr.Resource.Resource.DefaultProviderConfig().Absolute(target.Addr.Module) - } - - node := &graphNodeImportState{ - Addr: target.Addr, - ID: target.ID, - ProviderAddr: providerAddr, - } - g.Add(node) - } - return nil -} - -type graphNodeImportState struct { - Addr addrs.AbsResourceInstance // Addr is the resource address to import into - ID string // ID is the ID to import as - ProviderAddr addrs.AbsProviderConfig // Provider address given by the user, or implied by the resource type - ResolvedProvider addrs.AbsProviderConfig // provider node address after resolution - - states []providers.ImportedResource -} - -var ( - _ GraphNodeSubPath = (*graphNodeImportState)(nil) - _ GraphNodeEvalable = (*graphNodeImportState)(nil) - _ GraphNodeProviderConsumer = (*graphNodeImportState)(nil) - _ GraphNodeDynamicExpandable = (*graphNodeImportState)(nil) -) - -func (n *graphNodeImportState) Name() string { - return fmt.Sprintf("%s (import id %q)", n.Addr, n.ID) -} - -// GraphNodeProviderConsumer -func (n *graphNodeImportState) ProvidedBy() (addrs.AbsProviderConfig, bool) { - // We assume that n.ProviderAddr has been properly populated here. - // It's the responsibility of the code creating a graphNodeImportState - // to populate this, possibly by calling DefaultProviderConfig() on the - // resource address to infer an implied provider from the resource type - // name. - return n.ProviderAddr, false -} - -// GraphNodeProviderConsumer -func (n *graphNodeImportState) SetProvider(addr addrs.AbsProviderConfig) { - n.ResolvedProvider = addr -} - -// GraphNodeSubPath -func (n *graphNodeImportState) Path() addrs.ModuleInstance { - return n.Addr.Module -} - -// GraphNodeEvalable impl. -func (n *graphNodeImportState) EvalTree() EvalNode { - var provider providers.Interface - - // Reset our states - n.states = nil - - // Return our sequence - return &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: n.ResolvedProvider, - Output: &provider, - }, - &EvalImportState{ - Addr: n.Addr.Resource, - Provider: &provider, - ID: n.ID, - Output: &n.states, - }, - }, - } -} - -// GraphNodeDynamicExpandable impl. -// -// We use DynamicExpand as a way to generate the subgraph of refreshes -// and state inserts we need to do for our import state. Since they're new -// resources they don't depend on anything else and refreshes are isolated -// so this is nearly a perfect use case for dynamic expand. -func (n *graphNodeImportState) DynamicExpand(ctx EvalContext) (*Graph, error) { - var diags tfdiags.Diagnostics - - g := &Graph{Path: ctx.Path()} - - // nameCounter is used to de-dup names in the state. - nameCounter := make(map[string]int) - - // Compile the list of addresses that we'll be inserting into the state. - // We do this ahead of time so we can verify that we aren't importing - // something that already exists. - addrs := make([]addrs.AbsResourceInstance, len(n.states)) - for i, state := range n.states { - addr := n.Addr - if t := state.TypeName; t != "" { - addr.Resource.Resource.Type = t - } - - // Determine if we need to suffix the name to de-dup - key := addr.String() - count, ok := nameCounter[key] - if ok { - count++ - addr.Resource.Resource.Name += fmt.Sprintf("-%d", count) - } - nameCounter[key] = count - - // Add it to our list - addrs[i] = addr - } - - // Verify that all the addresses are clear - state := ctx.State() - for _, addr := range addrs { - existing := state.ResourceInstance(addr) - if existing != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Resource already managed by Terraform", - fmt.Sprintf("Terraform is already managing a remote object for %s. To import to this address you must first remove the existing object from the state.", addr), - )) - continue - } - } - if diags.HasErrors() { - // Bail out early, then. - return nil, diags.Err() - } - - // For each of the states, we add a node to handle the refresh/add to state. - // "n.states" is populated by our own EvalTree with the result of - // ImportState. Since DynamicExpand is always called after EvalTree, this - // is safe. - for i, state := range n.states { - g.Add(&graphNodeImportStateSub{ - TargetAddr: addrs[i], - State: state, - ResolvedProvider: n.ResolvedProvider, - }) - } - - // Root transform for a single root - t := &RootTransformer{} - if err := t.Transform(g); err != nil { - return nil, err - } - - // Done! - return g, diags.Err() -} - -// graphNodeImportStateSub is the sub-node of graphNodeImportState -// and is part of the subgraph. This node is responsible for refreshing -// and adding a resource to the state once it is imported. -type graphNodeImportStateSub struct { - TargetAddr addrs.AbsResourceInstance - State providers.ImportedResource - ResolvedProvider addrs.AbsProviderConfig -} - -var ( - _ GraphNodeSubPath = (*graphNodeImportStateSub)(nil) - _ GraphNodeEvalable = (*graphNodeImportStateSub)(nil) -) - -func (n *graphNodeImportStateSub) Name() string { - return fmt.Sprintf("import %s result", n.TargetAddr) -} - -func (n *graphNodeImportStateSub) Path() addrs.ModuleInstance { - return n.TargetAddr.Module -} - -// GraphNodeEvalable impl. -func (n *graphNodeImportStateSub) EvalTree() EvalNode { - // If the Ephemeral type isn't set, then it is an error - if n.State.TypeName == "" { - err := fmt.Errorf("import of %s didn't set type", n.TargetAddr.String()) - return &EvalReturnError{Error: &err} - } - - state := n.State.AsInstanceObject() - - var provider providers.Interface - var providerSchema *ProviderSchema - return &EvalSequence{ - Nodes: []EvalNode{ - &EvalGetProvider{ - Addr: n.ResolvedProvider, - Output: &provider, - Schema: &providerSchema, - }, - &EvalRefresh{ - Addr: n.TargetAddr.Resource, - ProviderAddr: n.ResolvedProvider, - Provider: &provider, - ProviderSchema: &providerSchema, - State: &state, - Output: &state, - }, - &EvalImportStateVerify{ - Addr: n.TargetAddr.Resource, - State: &state, - }, - &EvalWriteState{ - Addr: n.TargetAddr.Resource, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - }, - }, - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_local.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_local.go deleted file mode 100644 index b97dea2a..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_local.go +++ /dev/null @@ -1,48 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" -) - -// LocalTransformer is a GraphTransformer that adds all the local values -// from the configuration to the graph. -type LocalTransformer struct { - Config *configs.Config -} - -func (t *LocalTransformer) Transform(g *Graph) error { - return t.transformModule(g, t.Config) -} - -func (t *LocalTransformer) transformModule(g *Graph, c *configs.Config) error { - if c == nil { - // Can't have any locals if there's no config - return nil - } - - // Our addressing system distinguishes between modules and module instances, - // but we're not yet ready to make that distinction here (since we don't - // support "count"/"for_each" on modules) and so we just do a naive - // transform of the module path into a module instance path, assuming that - // no keys are in use. This should be removed when "count" and "for_each" - // are implemented for modules. - path := c.Path.UnkeyedInstanceShim() - - for _, local := range c.Module.Locals { - addr := path.LocalValue(local.Name) - node := &NodeLocal{ - Addr: addr, - Config: local, - } - g.Add(node) - } - - // Also populate locals for child modules - for _, cc := range c.Children { - if err := t.transformModule(g, cc); err != nil { - return err - } - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_module_variable.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_module_variable.go deleted file mode 100644 index caa4b6a6..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_module_variable.go +++ /dev/null @@ -1,126 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2/hclsyntax" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" -) - -// ModuleVariableTransformer is a GraphTransformer that adds all the variables -// in the configuration to the graph. -// -// Any "variable" block present in any non-root module is included here, even -// if a particular variable is not referenced from anywhere. -// -// The transform will produce errors if a call to a module does not conform -// to the expected set of arguments, but this transformer is not in a good -// position to return errors and so the validate walk should include specific -// steps for validating module blocks, separate from this transform. -type ModuleVariableTransformer struct { - Config *configs.Config -} - -func (t *ModuleVariableTransformer) Transform(g *Graph) error { - return t.transform(g, nil, t.Config) -} - -func (t *ModuleVariableTransformer) transform(g *Graph, parent, c *configs.Config) error { - // We can have no variables if we have no configuration. - if c == nil { - return nil - } - - // Transform all the children first. - for _, cc := range c.Children { - if err := t.transform(g, c, cc); err != nil { - return err - } - } - - // If we're processing anything other than the root module then we'll - // add graph nodes for variables defined inside. (Variables for the root - // module are dealt with in RootVariableTransformer). - // If we have a parent, we can determine if a module variable is being - // used, so we transform this. - if parent != nil { - if err := t.transformSingle(g, parent, c); err != nil { - return err - } - } - - return nil -} - -func (t *ModuleVariableTransformer) transformSingle(g *Graph, parent, c *configs.Config) error { - - // Our addressing system distinguishes between modules and module instances, - // but we're not yet ready to make that distinction here (since we don't - // support "count"/"for_each" on modules) and so we just do a naive - // transform of the module path into a module instance path, assuming that - // no keys are in use. This should be removed when "count" and "for_each" - // are implemented for modules. - path := c.Path.UnkeyedInstanceShim() - _, call := path.Call() - - // Find the call in the parent module configuration, so we can get the - // expressions given for each input variable at the call site. - callConfig, exists := parent.Module.ModuleCalls[call.Name] - if !exists { - // This should never happen, since it indicates an improperly-constructed - // configuration tree. - panic(fmt.Errorf("no module call block found for %s", path)) - } - - // We need to construct a schema for the expected call arguments based on - // the configured variables in our config, which we can then use to - // decode the content of the call block. - schema := &hcl.BodySchema{} - for _, v := range c.Module.Variables { - schema.Attributes = append(schema.Attributes, hcl.AttributeSchema{ - Name: v.Name, - Required: v.Default == cty.NilVal, - }) - } - - content, contentDiags := callConfig.Config.Content(schema) - if contentDiags.HasErrors() { - // Validation code elsewhere should deal with any errors before we - // get in here, but we'll report them out here just in case, to - // avoid crashes. - var diags tfdiags.Diagnostics - diags = diags.Append(contentDiags) - return diags.Err() - } - - for _, v := range c.Module.Variables { - var expr hcl.Expression - if attr := content.Attributes[v.Name]; attr != nil { - expr = attr.Expr - } else { - // No expression provided for this variable, so we'll make a - // synthetic one using the variable's default value. - expr = &hclsyntax.LiteralValueExpr{ - Val: v.Default, - SrcRange: v.DeclRange, // This is not exact, but close enough - } - } - - // For now we treat all module variables as "applyable", even though - // such nodes are valid to use on other walks too. We may specialize - // this in future if we find reasons to employ different behaviors - // in different scenarios. - node := &NodeApplyableModuleVariable{ - Addr: path.InputVariable(v.Name), - Config: v, - Expr: expr, - } - g.Add(node) - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_orphan_count.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_orphan_count.go deleted file mode 100644 index 4d1323fb..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_orphan_count.go +++ /dev/null @@ -1,175 +0,0 @@ -package terraform - -import ( - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" - "github.com/zclconf/go-cty/cty" -) - -// OrphanResourceCountTransformer is a GraphTransformer that adds orphans -// for an expanded count to the graph. The determination of this depends -// on the count argument given. -// -// Orphans are found by comparing the count to what is found in the state. -// This transform assumes that if an element in the state is within the count -// bounds given, that it is not an orphan. -type OrphanResourceCountTransformer struct { - Concrete ConcreteResourceInstanceNodeFunc - - Count int // Actual count of the resource, or -1 if count is not set at all - ForEach map[string]cty.Value // The ForEach map on the resource - Addr addrs.AbsResource // Addr of the resource to look for orphans - State *states.State // Full global state -} - -func (t *OrphanResourceCountTransformer) Transform(g *Graph) error { - rs := t.State.Resource(t.Addr) - if rs == nil { - return nil // Resource doesn't exist in state, so nothing to do! - } - - haveKeys := make(map[addrs.InstanceKey]struct{}) - for key := range rs.Instances { - haveKeys[key] = struct{}{} - } - - // if for_each is set, use that transformer - if t.ForEach != nil { - return t.transformForEach(haveKeys, g) - } - if t.Count < 0 { - return t.transformNoCount(haveKeys, g) - } - if t.Count == 0 { - return t.transformZeroCount(haveKeys, g) - } - return t.transformCount(haveKeys, g) -} - -func (t *OrphanResourceCountTransformer) transformForEach(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error { - // If there is a NoKey node, add this to the graph first, - // so that we can create edges to it in subsequent (StringKey) nodes. - // This is because the last item determines the resource mode for the whole resource, - // (see SetResourceInstanceCurrent for more information) and we need to evaluate - // an orphaned (NoKey) resource before the in-memory state is updated - // to deal with a new for_each resource - _, hasNoKeyNode := haveKeys[addrs.NoKey] - var noKeyNode dag.Vertex - if hasNoKeyNode { - abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(addrs.NoKey)) - noKeyNode = abstract - if f := t.Concrete; f != nil { - noKeyNode = f(abstract) - } - g.Add(noKeyNode) - } - - for key := range haveKeys { - // If the key is no-key, we have already added it, so skip - if key == addrs.NoKey { - continue - } - - s, _ := key.(addrs.StringKey) - // If the key is present in our current for_each, carry on - if _, ok := t.ForEach[string(s)]; ok { - continue - } - - abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key)) - var node dag.Vertex = abstract - if f := t.Concrete; f != nil { - node = f(abstract) - } - log.Printf("[TRACE] OrphanResourceCount(non-zero): adding %s as %T", t.Addr, node) - g.Add(node) - - // Add edge to noKeyNode if it exists - if hasNoKeyNode { - g.Connect(dag.BasicEdge(node, noKeyNode)) - } - } - return nil -} - -func (t *OrphanResourceCountTransformer) transformCount(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error { - // Due to the logic in Transform, we only get in here if our count is - // at least one. - - _, have0Key := haveKeys[addrs.IntKey(0)] - - for key := range haveKeys { - if key == addrs.NoKey && !have0Key { - // If we have no 0-key then we will accept a no-key instance - // as an alias for it. - continue - } - - i, isInt := key.(addrs.IntKey) - if isInt && int(i) < t.Count { - continue - } - - abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key)) - var node dag.Vertex = abstract - if f := t.Concrete; f != nil { - node = f(abstract) - } - log.Printf("[TRACE] OrphanResourceCount(non-zero): adding %s as %T", t.Addr, node) - g.Add(node) - } - - return nil -} - -func (t *OrphanResourceCountTransformer) transformZeroCount(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error { - // This case is easy: we need to orphan any keys we have at all. - - for key := range haveKeys { - abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key)) - var node dag.Vertex = abstract - if f := t.Concrete; f != nil { - node = f(abstract) - } - log.Printf("[TRACE] OrphanResourceCount(zero): adding %s as %T", t.Addr, node) - g.Add(node) - } - - return nil -} - -func (t *OrphanResourceCountTransformer) transformNoCount(haveKeys map[addrs.InstanceKey]struct{}, g *Graph) error { - // Negative count indicates that count is not set at all, in which - // case we expect to have a single instance with no key set at all. - // However, we'll also accept an instance with key 0 set as an alias - // for it, in case the user has just deleted the "count" argument and - // so wants to keep the first instance in the set. - - _, haveNoKey := haveKeys[addrs.NoKey] - _, have0Key := haveKeys[addrs.IntKey(0)] - keepKey := addrs.NoKey - if have0Key && !haveNoKey { - // If we don't have a no-key instance then we can use the 0-key instance - // instead. - keepKey = addrs.IntKey(0) - } - - for key := range haveKeys { - if key == keepKey { - continue - } - - abstract := NewNodeAbstractResourceInstance(t.Addr.Instance(key)) - var node dag.Vertex = abstract - if f := t.Concrete; f != nil { - node = f(abstract) - } - log.Printf("[TRACE] OrphanResourceCount(no-count): adding %s as %T", t.Addr, node) - g.Add(node) - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_orphan_output.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_orphan_output.go deleted file mode 100644 index cab10da1..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_orphan_output.go +++ /dev/null @@ -1,60 +0,0 @@ -package terraform - -import ( - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// OrphanOutputTransformer finds the outputs that aren't present -// in the given config that are in the state and adds them to the graph -// for deletion. -type OrphanOutputTransformer struct { - Config *configs.Config // Root of config tree - State *states.State // State is the root state -} - -func (t *OrphanOutputTransformer) Transform(g *Graph) error { - if t.State == nil { - log.Printf("[DEBUG] No state, no orphan outputs") - return nil - } - - for _, ms := range t.State.Modules { - if err := t.transform(g, ms); err != nil { - return err - } - } - return nil -} - -func (t *OrphanOutputTransformer) transform(g *Graph, ms *states.Module) error { - if ms == nil { - return nil - } - - moduleAddr := ms.Addr - - // Get the config for this path, which is nil if the entire module has been - // removed. - var outputs map[string]*configs.Output - if c := t.Config.DescendentForInstance(moduleAddr); c != nil { - outputs = c.Module.Outputs - } - - // An output is "orphaned" if it's present in the state but not declared - // in the configuration. - for name := range ms.OutputValues { - if _, exists := outputs[name]; exists { - continue - } - - g.Add(&NodeOutputOrphan{ - Addr: addrs.OutputValue{Name: name}.Absolute(moduleAddr), - }) - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_orphan_resource.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_orphan_resource.go deleted file mode 100644 index f927b108..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_orphan_resource.go +++ /dev/null @@ -1,179 +0,0 @@ -package terraform - -import ( - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// OrphanResourceInstanceTransformer is a GraphTransformer that adds orphaned -// resource instances to the graph. An "orphan" is an instance that is present -// in the state but belongs to a resource that is no longer present in the -// configuration. -// -// This is not the transformer that deals with "count orphans" (instances that -// are no longer covered by a resource's "count" or "for_each" setting); that's -// handled instead by OrphanResourceCountTransformer. -type OrphanResourceInstanceTransformer struct { - Concrete ConcreteResourceInstanceNodeFunc - - // State is the global state. We require the global state to - // properly find module orphans at our path. - State *states.State - - // Config is the root node in the configuration tree. We'll look up - // the appropriate note in this tree using the path in each node. - Config *configs.Config -} - -func (t *OrphanResourceInstanceTransformer) Transform(g *Graph) error { - if t.State == nil { - // If the entire state is nil, there can't be any orphans - return nil - } - if t.Config == nil { - // Should never happen: we can't be doing any Terraform operations - // without at least an empty configuration. - panic("OrphanResourceInstanceTransformer used without setting Config") - } - - // Go through the modules and for each module transform in order - // to add the orphan. - for _, ms := range t.State.Modules { - if err := t.transform(g, ms); err != nil { - return err - } - } - - return nil -} - -func (t *OrphanResourceInstanceTransformer) transform(g *Graph, ms *states.Module) error { - if ms == nil { - return nil - } - - moduleAddr := ms.Addr - - // Get the configuration for this module. The configuration might be - // nil if the module was removed from the configuration. This is okay, - // this just means that every resource is an orphan. - var m *configs.Module - if c := t.Config.DescendentForInstance(moduleAddr); c != nil { - m = c.Module - } - - // An "orphan" is a resource that is in the state but not the configuration, - // so we'll walk the state resources and try to correlate each of them - // with a configuration block. Each orphan gets a node in the graph whose - // type is decided by t.Concrete. - // - // We don't handle orphans related to changes in the "count" and "for_each" - // pseudo-arguments here. They are handled by OrphanResourceCountTransformer. - for _, rs := range ms.Resources { - if m != nil { - if r := m.ResourceByAddr(rs.Addr); r != nil { - continue - } - } - - for key := range rs.Instances { - addr := rs.Addr.Instance(key).Absolute(moduleAddr) - abstract := NewNodeAbstractResourceInstance(addr) - var node dag.Vertex = abstract - if f := t.Concrete; f != nil { - node = f(abstract) - } - log.Printf("[TRACE] OrphanResourceInstanceTransformer: adding single-instance orphan node for %s", addr) - g.Add(node) - } - } - - return nil -} - -// OrphanResourceTransformer is a GraphTransformer that adds orphaned -// resources to the graph. An "orphan" is a resource that is present in -// the state but no longer present in the config. -// -// This is separate to OrphanResourceInstanceTransformer in that it deals with -// whole resources, rather than individual instances of resources. Orphan -// resource nodes are only used during apply to clean up leftover empty -// resource state skeletons, after all of the instances inside have been -// removed. -// -// This transformer will also create edges in the graph to any pre-existing -// node that creates or destroys the entire orphaned resource or any of its -// instances, to ensure that the "orphan-ness" of a resource is always dealt -// with after all other aspects of it. -type OrphanResourceTransformer struct { - Concrete ConcreteResourceNodeFunc - - // State is the global state. - State *states.State - - // Config is the root node in the configuration tree. - Config *configs.Config -} - -func (t *OrphanResourceTransformer) Transform(g *Graph) error { - if t.State == nil { - // If the entire state is nil, there can't be any orphans - return nil - } - if t.Config == nil { - // Should never happen: we can't be doing any Terraform operations - // without at least an empty configuration. - panic("OrphanResourceTransformer used without setting Config") - } - - // We'll first collect up the existing nodes for each resource so we can - // create dependency edges for any new nodes we create. - deps := map[string][]dag.Vertex{} - for _, v := range g.Vertices() { - switch tv := v.(type) { - case GraphNodeResourceInstance: - k := tv.ResourceInstanceAddr().ContainingResource().String() - deps[k] = append(deps[k], v) - case GraphNodeResource: - k := tv.ResourceAddr().String() - deps[k] = append(deps[k], v) - case GraphNodeDestroyer: - k := tv.DestroyAddr().ContainingResource().String() - deps[k] = append(deps[k], v) - } - } - - for _, ms := range t.State.Modules { - moduleAddr := ms.Addr - - mc := t.Config.DescendentForInstance(moduleAddr) // might be nil if whole module has been removed - - for _, rs := range ms.Resources { - if mc != nil { - if r := mc.Module.ResourceByAddr(rs.Addr); r != nil { - // It's in the config, so nothing to do for this one. - continue - } - } - - addr := rs.Addr.Absolute(moduleAddr) - abstract := NewNodeAbstractResource(addr) - var node dag.Vertex = abstract - if f := t.Concrete; f != nil { - node = f(abstract) - } - log.Printf("[TRACE] OrphanResourceTransformer: adding whole-resource orphan node for %s", addr) - g.Add(node) - for _, dn := range deps[addr.String()] { - log.Printf("[TRACE] OrphanResourceTransformer: node %q depends on %q", dag.VertexName(node), dag.VertexName(dn)) - g.Connect(dag.BasicEdge(node, dn)) - } - } - } - - return nil - -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_output.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_output.go deleted file mode 100644 index e2979ac5..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_output.go +++ /dev/null @@ -1,95 +0,0 @@ -package terraform - -import ( - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" -) - -// OutputTransformer is a GraphTransformer that adds all the outputs -// in the configuration to the graph. -// -// This is done for the apply graph builder even if dependent nodes -// aren't changing since there is no downside: the state will be available -// even if the dependent items aren't changing. -type OutputTransformer struct { - Config *configs.Config -} - -func (t *OutputTransformer) Transform(g *Graph) error { - return t.transform(g, t.Config) -} - -func (t *OutputTransformer) transform(g *Graph, c *configs.Config) error { - // If we have no config then there can be no outputs. - if c == nil { - return nil - } - - // Transform all the children. We must do this first because - // we can reference module outputs and they must show up in the - // reference map. - for _, cc := range c.Children { - if err := t.transform(g, cc); err != nil { - return err - } - } - - // Our addressing system distinguishes between modules and module instances, - // but we're not yet ready to make that distinction here (since we don't - // support "count"/"for_each" on modules) and so we just do a naive - // transform of the module path into a module instance path, assuming that - // no keys are in use. This should be removed when "count" and "for_each" - // are implemented for modules. - path := c.Path.UnkeyedInstanceShim() - - for _, o := range c.Module.Outputs { - addr := path.OutputValue(o.Name) - node := &NodeApplyableOutput{ - Addr: addr, - Config: o, - } - g.Add(node) - } - - return nil -} - -// DestroyOutputTransformer is a GraphTransformer that adds nodes to delete -// outputs during destroy. We need to do this to ensure that no stale outputs -// are ever left in the state. -type DestroyOutputTransformer struct { -} - -func (t *DestroyOutputTransformer) Transform(g *Graph) error { - for _, v := range g.Vertices() { - output, ok := v.(*NodeApplyableOutput) - if !ok { - continue - } - - // create the destroy node for this output - node := &NodeDestroyableOutput{ - Addr: output.Addr, - Config: output.Config, - } - - log.Printf("[TRACE] creating %s", node.Name()) - g.Add(node) - - deps, err := g.Descendents(v) - if err != nil { - return err - } - - // the destroy node must depend on the eval node - deps.Add(v) - - for _, d := range deps.List() { - log.Printf("[TRACE] %s depends on %s", node.Name(), dag.VertexName(d)) - g.Connect(dag.BasicEdge(node, d)) - } - } - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_provider.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_provider.go deleted file mode 100644 index 9c8966fa..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_provider.go +++ /dev/null @@ -1,705 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -func TransformProviders(providers []string, concrete ConcreteProviderNodeFunc, config *configs.Config) GraphTransformer { - return GraphTransformMulti( - // Add providers from the config - &ProviderConfigTransformer{ - Config: config, - Providers: providers, - Concrete: concrete, - }, - // Add any remaining missing providers - &MissingProviderTransformer{ - Providers: providers, - Concrete: concrete, - }, - // Connect the providers - &ProviderTransformer{ - Config: config, - }, - // Remove unused providers and proxies - &PruneProviderTransformer{}, - // Connect provider to their parent provider nodes - &ParentProviderTransformer{}, - ) -} - -// GraphNodeProvider is an interface that nodes that can be a provider -// must implement. -// -// ProviderAddr returns the address of the provider configuration this -// satisfies, which is relative to the path returned by method Path(). -// -// Name returns the full name of the provider in the config. -type GraphNodeProvider interface { - GraphNodeSubPath - ProviderAddr() addrs.AbsProviderConfig - Name() string -} - -// GraphNodeCloseProvider is an interface that nodes that can be a close -// provider must implement. The CloseProviderName returned is the name of -// the provider they satisfy. -type GraphNodeCloseProvider interface { - GraphNodeSubPath - CloseProviderAddr() addrs.AbsProviderConfig -} - -// GraphNodeProviderConsumer is an interface that nodes that require -// a provider must implement. ProvidedBy must return the address of the provider -// to use, which will be resolved to a configuration either in the same module -// or in an ancestor module, with the resulting absolute address passed to -// SetProvider. -type GraphNodeProviderConsumer interface { - // ProvidedBy returns the address of the provider configuration the node - // refers to. If the returned "exact" value is true, this address will - // be taken exactly. If "exact" is false, a provider configuration from - // an ancestor module may be selected instead. - ProvidedBy() (addr addrs.AbsProviderConfig, exact bool) - // Set the resolved provider address for this resource. - SetProvider(addrs.AbsProviderConfig) -} - -// ProviderTransformer is a GraphTransformer that maps resources to -// providers within the graph. This will error if there are any resources -// that don't map to proper resources. -type ProviderTransformer struct { - Config *configs.Config -} - -func (t *ProviderTransformer) Transform(g *Graph) error { - // We need to find a provider configuration address for each resource - // either directly represented by a node or referenced by a node in - // the graph, and then create graph edges from provider to provider user - // so that the providers will get initialized first. - - var diags tfdiags.Diagnostics - - // To start, we'll collect the _requested_ provider addresses for each - // node, which we'll then resolve (handling provider inheritence, etc) in - // the next step. - // Our "requested" map is from graph vertices to string representations of - // provider config addresses (for deduping) to requests. - type ProviderRequest struct { - Addr addrs.AbsProviderConfig - Exact bool // If true, inheritence from parent modules is not attempted - } - requested := map[dag.Vertex]map[string]ProviderRequest{} - needConfigured := map[string]addrs.AbsProviderConfig{} - for _, v := range g.Vertices() { - - // Does the vertex _directly_ use a provider? - if pv, ok := v.(GraphNodeProviderConsumer); ok { - requested[v] = make(map[string]ProviderRequest) - - p, exact := pv.ProvidedBy() - if exact { - log.Printf("[TRACE] ProviderTransformer: %s is provided by %s exactly", dag.VertexName(v), p) - } else { - log.Printf("[TRACE] ProviderTransformer: %s is provided by %s or inherited equivalent", dag.VertexName(v), p) - } - - requested[v][p.String()] = ProviderRequest{ - Addr: p, - Exact: exact, - } - - // Direct references need the provider configured as well as initialized - needConfigured[p.String()] = p - } - } - - // Now we'll go through all the requested addresses we just collected and - // figure out which _actual_ config address each belongs to, after resolving - // for provider inheritance and passing. - m := providerVertexMap(g) - for v, reqs := range requested { - for key, req := range reqs { - p := req.Addr - target := m[key] - - _, ok := v.(GraphNodeSubPath) - if !ok && target == nil { - // No target and no path to traverse up from - diags = diags.Append(fmt.Errorf("%s: provider %s couldn't be found", dag.VertexName(v), p)) - continue - } - - if target != nil { - log.Printf("[TRACE] ProviderTransformer: exact match for %s serving %s", p, dag.VertexName(v)) - } - - // if we don't have a provider at this level, walk up the path looking for one, - // unless we were told to be exact. - if target == nil && !req.Exact { - for pp, ok := p.Inherited(); ok; pp, ok = pp.Inherited() { - key := pp.String() - target = m[key] - if target != nil { - log.Printf("[TRACE] ProviderTransformer: %s uses inherited configuration %s", dag.VertexName(v), pp) - break - } - log.Printf("[TRACE] ProviderTransformer: looking for %s to serve %s", pp, dag.VertexName(v)) - } - } - - // If this provider doesn't need to be configured then we can just - // stub it out with an init-only provider node, which will just - // start up the provider and fetch its schema. - if _, exists := needConfigured[key]; target == nil && !exists { - stubAddr := p.ProviderConfig.Absolute(addrs.RootModuleInstance) - stub := &NodeEvalableProvider{ - &NodeAbstractProvider{ - Addr: stubAddr, - }, - } - m[stubAddr.String()] = stub - log.Printf("[TRACE] ProviderTransformer: creating init-only node for %s", stubAddr) - target = stub - g.Add(target) - } - - if target == nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider configuration not present", - fmt.Sprintf( - "To work with %s its original provider configuration at %s is required, but it has been removed. This occurs when a provider configuration is removed while objects created by that provider still exist in the state. Re-add the provider configuration to destroy %s, after which you can remove the provider configuration again.", - dag.VertexName(v), p, dag.VertexName(v), - ), - )) - break - } - - // see if this in an inherited provider - if p, ok := target.(*graphNodeProxyProvider); ok { - g.Remove(p) - target = p.Target() - key = target.(GraphNodeProvider).ProviderAddr().String() - } - - log.Printf("[DEBUG] ProviderTransformer: %q (%T) needs %s", dag.VertexName(v), v, dag.VertexName(target)) - if pv, ok := v.(GraphNodeProviderConsumer); ok { - pv.SetProvider(target.ProviderAddr()) - } - g.Connect(dag.BasicEdge(v, target)) - } - } - - return diags.Err() -} - -// CloseProviderTransformer is a GraphTransformer that adds nodes to the -// graph that will close open provider connections that aren't needed anymore. -// A provider connection is not needed anymore once all depended resources -// in the graph are evaluated. -type CloseProviderTransformer struct{} - -func (t *CloseProviderTransformer) Transform(g *Graph) error { - pm := providerVertexMap(g) - cpm := make(map[string]*graphNodeCloseProvider) - var err error - - for _, v := range pm { - p := v.(GraphNodeProvider) - key := p.ProviderAddr().String() - - // get the close provider of this type if we alread created it - closer := cpm[key] - - if closer == nil { - // create a closer for this provider type - closer = &graphNodeCloseProvider{Addr: p.ProviderAddr()} - g.Add(closer) - cpm[key] = closer - } - - // Close node depends on the provider itself - // this is added unconditionally, so it will connect to all instances - // of the provider. Extra edges will be removed by transitive - // reduction. - g.Connect(dag.BasicEdge(closer, p)) - - // connect all the provider's resources to the close node - for _, s := range g.UpEdges(p).List() { - if _, ok := s.(GraphNodeProviderConsumer); ok { - g.Connect(dag.BasicEdge(closer, s)) - } - } - } - - return err -} - -// MissingProviderTransformer is a GraphTransformer that adds to the graph -// a node for each default provider configuration that is referenced by another -// node but not already present in the graph. -// -// These "default" nodes are always added to the root module, regardless of -// where they are requested. This is important because our inheritance -// resolution behavior in ProviderTransformer will then treat these as a -// last-ditch fallback after walking up the tree, rather than preferring them -// as it would if they were placed in the same module as the requester. -// -// This transformer may create extra nodes that are not needed in practice, -// due to overriding provider configurations in child modules. -// PruneProviderTransformer can then remove these once ProviderTransformer -// has resolved all of the inheritence, etc. -type MissingProviderTransformer struct { - // Providers is the list of providers we support. - Providers []string - - // Concrete, if set, overrides how the providers are made. - Concrete ConcreteProviderNodeFunc -} - -func (t *MissingProviderTransformer) Transform(g *Graph) error { - // Initialize factory - if t.Concrete == nil { - t.Concrete = func(a *NodeAbstractProvider) dag.Vertex { - return a - } - } - - var err error - m := providerVertexMap(g) - for _, v := range g.Vertices() { - pv, ok := v.(GraphNodeProviderConsumer) - if !ok { - continue - } - - // For our work here we actually care only about the provider type and - // we plan to place all default providers in the root module, and so - // it's safe for us to rely on ProvidedBy here rather than waiting for - // the later proper resolution of provider inheritance done by - // ProviderTransformer. - p, _ := pv.ProvidedBy() - if p.ProviderConfig.Alias != "" { - // We do not create default aliased configurations. - log.Println("[TRACE] MissingProviderTransformer: skipping implication of aliased config", p) - continue - } - - // We're going to create an implicit _default_ configuration for the - // referenced provider type in the _root_ module, ignoring all other - // aspects of the resource's declared provider address. - defaultAddr := addrs.RootModuleInstance.ProviderConfigDefault(p.ProviderConfig.Type) - key := defaultAddr.String() - provider := m[key] - - if provider != nil { - // There's already an explicit default configuration for this - // provider type in the root module, so we have nothing to do. - continue - } - - log.Printf("[DEBUG] adding implicit provider configuration %s, implied first by %s", defaultAddr, dag.VertexName(v)) - - // create the missing top-level provider - provider = t.Concrete(&NodeAbstractProvider{ - Addr: defaultAddr, - }).(GraphNodeProvider) - - g.Add(provider) - m[key] = provider - } - - return err -} - -// ParentProviderTransformer connects provider nodes to their parents. -// -// This works by finding nodes that are both GraphNodeProviders and -// GraphNodeSubPath. It then connects the providers to their parent -// path. The parent provider is always at the root level. -type ParentProviderTransformer struct{} - -func (t *ParentProviderTransformer) Transform(g *Graph) error { - pm := providerVertexMap(g) - for _, v := range g.Vertices() { - // Only care about providers - pn, ok := v.(GraphNodeProvider) - if !ok { - continue - } - - // Also require non-empty path, since otherwise we're in the root - // module and so cannot have a parent. - if len(pn.Path()) <= 1 { - continue - } - - // this provider may be disabled, but we can only get it's name from - // the ProviderName string - addr := pn.ProviderAddr() - parentAddr, ok := addr.Inherited() - if ok { - parent := pm[parentAddr.String()] - if parent != nil { - g.Connect(dag.BasicEdge(v, parent)) - } - } - } - return nil -} - -// PruneProviderTransformer removes any providers that are not actually used by -// anything, and provider proxies. This avoids the provider being initialized -// and configured. This both saves resources but also avoids errors since -// configuration may imply initialization which may require auth. -type PruneProviderTransformer struct{} - -func (t *PruneProviderTransformer) Transform(g *Graph) error { - for _, v := range g.Vertices() { - // We only care about providers - _, ok := v.(GraphNodeProvider) - if !ok { - continue - } - - // ProxyProviders will have up edges, but we're now done with them in the graph - if _, ok := v.(*graphNodeProxyProvider); ok { - log.Printf("[DEBUG] pruning proxy %s", dag.VertexName(v)) - g.Remove(v) - } - - // Remove providers with no dependencies. - if g.UpEdges(v).Len() == 0 { - log.Printf("[DEBUG] pruning unused %s", dag.VertexName(v)) - g.Remove(v) - } - } - - return nil -} - -func providerVertexMap(g *Graph) map[string]GraphNodeProvider { - m := make(map[string]GraphNodeProvider) - for _, v := range g.Vertices() { - if pv, ok := v.(GraphNodeProvider); ok { - addr := pv.ProviderAddr() - m[addr.String()] = pv - } - } - - return m -} - -type graphNodeCloseProvider struct { - Addr addrs.AbsProviderConfig -} - -var ( - _ GraphNodeCloseProvider = (*graphNodeCloseProvider)(nil) -) - -func (n *graphNodeCloseProvider) Name() string { - return n.Addr.String() + " (close)" -} - -// GraphNodeSubPath impl. -func (n *graphNodeCloseProvider) Path() addrs.ModuleInstance { - return n.Addr.Module -} - -// GraphNodeEvalable impl. -func (n *graphNodeCloseProvider) EvalTree() EvalNode { - return CloseProviderEvalTree(n.Addr) -} - -// GraphNodeDependable impl. -func (n *graphNodeCloseProvider) DependableName() []string { - return []string{n.Name()} -} - -func (n *graphNodeCloseProvider) CloseProviderAddr() addrs.AbsProviderConfig { - return n.Addr -} - -// GraphNodeDotter impl. -func (n *graphNodeCloseProvider) DotNode(name string, opts *dag.DotOpts) *dag.DotNode { - if !opts.Verbose { - return nil - } - return &dag.DotNode{ - Name: name, - Attrs: map[string]string{ - "label": n.Name(), - "shape": "diamond", - }, - } -} - -// RemovableIfNotTargeted -func (n *graphNodeCloseProvider) RemoveIfNotTargeted() bool { - // We need to add this so that this node will be removed if - // it isn't targeted or a dependency of a target. - return true -} - -// graphNodeProxyProvider is a GraphNodeProvider implementation that is used to -// store the name and value of a provider node for inheritance between modules. -// These nodes are only used to store the data while loading the provider -// configurations, and are removed after all the resources have been connected -// to their providers. -type graphNodeProxyProvider struct { - addr addrs.AbsProviderConfig - target GraphNodeProvider -} - -var ( - _ GraphNodeProvider = (*graphNodeProxyProvider)(nil) -) - -func (n *graphNodeProxyProvider) ProviderAddr() addrs.AbsProviderConfig { - return n.addr -} - -func (n *graphNodeProxyProvider) Path() addrs.ModuleInstance { - return n.addr.Module -} - -func (n *graphNodeProxyProvider) Name() string { - return n.addr.String() + " (proxy)" -} - -// find the concrete provider instance -func (n *graphNodeProxyProvider) Target() GraphNodeProvider { - switch t := n.target.(type) { - case *graphNodeProxyProvider: - return t.Target() - default: - return n.target - } -} - -// ProviderConfigTransformer adds all provider nodes from the configuration and -// attaches the configs. -type ProviderConfigTransformer struct { - Providers []string - Concrete ConcreteProviderNodeFunc - - // each provider node is stored here so that the proxy nodes can look up - // their targets by name. - providers map[string]GraphNodeProvider - // record providers that can be overriden with a proxy - proxiable map[string]bool - - // Config is the root node of the configuration tree to add providers from. - Config *configs.Config -} - -func (t *ProviderConfigTransformer) Transform(g *Graph) error { - // If no configuration is given, we don't do anything - if t.Config == nil { - return nil - } - - t.providers = make(map[string]GraphNodeProvider) - t.proxiable = make(map[string]bool) - - // Start the transformation process - if err := t.transform(g, t.Config); err != nil { - return err - } - - // finally attach the configs to the new nodes - return t.attachProviderConfigs(g) -} - -func (t *ProviderConfigTransformer) transform(g *Graph, c *configs.Config) error { - // If no config, do nothing - if c == nil { - return nil - } - - // Add our resources - if err := t.transformSingle(g, c); err != nil { - return err - } - - // Transform all the children. - for _, cc := range c.Children { - if err := t.transform(g, cc); err != nil { - return err - } - } - return nil -} - -func (t *ProviderConfigTransformer) transformSingle(g *Graph, c *configs.Config) error { - // Get the module associated with this configuration tree node - mod := c.Module - staticPath := c.Path - - // We actually need a dynamic module path here, but we've not yet updated - // our graph builders enough to support expansion of module calls with - // "count" and "for_each" set, so for now we'll shim this by converting to - // a dynamic path with no keys. At the time of writing this is the only - // possible kind of dynamic path anyway. - path := make(addrs.ModuleInstance, len(staticPath)) - for i, name := range staticPath { - path[i] = addrs.ModuleInstanceStep{ - Name: name, - } - } - - // add all providers from the configuration - for _, p := range mod.ProviderConfigs { - relAddr := p.Addr() - addr := relAddr.Absolute(path) - - abstract := &NodeAbstractProvider{ - Addr: addr, - } - var v dag.Vertex - if t.Concrete != nil { - v = t.Concrete(abstract) - } else { - v = abstract - } - - // Add it to the graph - g.Add(v) - key := addr.String() - t.providers[key] = v.(GraphNodeProvider) - - // A provider configuration is "proxyable" if its configuration is - // entirely empty. This means it's standing in for a provider - // configuration that must be passed in from the parent module. - // We decide this by evaluating the config with an empty schema; - // if this succeeds, then we know there's nothing in the body. - _, diags := p.Config.Content(&hcl.BodySchema{}) - t.proxiable[key] = !diags.HasErrors() - } - - // Now replace the provider nodes with proxy nodes if a provider was being - // passed in, and create implicit proxies if there was no config. Any extra - // proxies will be removed in the prune step. - return t.addProxyProviders(g, c) -} - -func (t *ProviderConfigTransformer) addProxyProviders(g *Graph, c *configs.Config) error { - path := c.Path - - // can't add proxies at the root - if len(path) == 0 { - return nil - } - - parentPath, callAddr := path.Call() - parent := c.Parent - if parent == nil { - return nil - } - - callName := callAddr.Name - var parentCfg *configs.ModuleCall - for name, mod := range parent.Module.ModuleCalls { - if name == callName { - parentCfg = mod - break - } - } - - // We currently don't support count/for_each for modules and so we must - // shim our path and parentPath into module instances here so that the - // rest of Terraform can behave as if we do. This shimming should be - // removed later as part of implementing count/for_each for modules. - instPath := make(addrs.ModuleInstance, len(path)) - for i, name := range path { - instPath[i] = addrs.ModuleInstanceStep{Name: name} - } - parentInstPath := make(addrs.ModuleInstance, len(parentPath)) - for i, name := range parentPath { - parentInstPath[i] = addrs.ModuleInstanceStep{Name: name} - } - - if parentCfg == nil { - // this can't really happen during normal execution. - return fmt.Errorf("parent module config not found for %s", c.Path.String()) - } - - // Go through all the providers the parent is passing in, and add proxies to - // the parent provider nodes. - for _, pair := range parentCfg.Providers { - fullAddr := pair.InChild.Addr().Absolute(instPath) - fullParentAddr := pair.InParent.Addr().Absolute(parentInstPath) - fullName := fullAddr.String() - fullParentName := fullParentAddr.String() - - parentProvider := t.providers[fullParentName] - - if parentProvider == nil { - return fmt.Errorf("missing provider %s", fullParentName) - } - - proxy := &graphNodeProxyProvider{ - addr: fullAddr, - target: parentProvider, - } - - concreteProvider := t.providers[fullName] - - // replace the concrete node with the provider passed in - if concreteProvider != nil && t.proxiable[fullName] { - g.Replace(concreteProvider, proxy) - t.providers[fullName] = proxy - continue - } - - // aliased configurations can't be implicitly passed in - if fullAddr.ProviderConfig.Alias != "" { - continue - } - - // There was no concrete provider, so add this as an implicit provider. - // The extra proxy will be pruned later if it's unused. - g.Add(proxy) - t.providers[fullName] = proxy - } - return nil -} - -func (t *ProviderConfigTransformer) attachProviderConfigs(g *Graph) error { - for _, v := range g.Vertices() { - // Only care about GraphNodeAttachProvider implementations - apn, ok := v.(GraphNodeAttachProvider) - if !ok { - continue - } - - // Determine what we're looking for - addr := apn.ProviderAddr() - - // Get the configuration. - mc := t.Config.DescendentForInstance(addr.Module) - if mc == nil { - log.Printf("[TRACE] ProviderConfigTransformer: no configuration available for %s", addr.String()) - continue - } - - // Go through the provider configs to find the matching config - for _, p := range mc.Module.ProviderConfigs { - if p.Name == addr.ProviderConfig.Type && p.Alias == addr.ProviderConfig.Alias { - log.Printf("[TRACE] ProviderConfigTransformer: attaching to %q provider configuration from %s", dag.VertexName(v), p.DeclRange) - apn.AttachProvider(p) - break - } - } - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_provisioner.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_provisioner.go deleted file mode 100644 index e6fe25da..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_provisioner.go +++ /dev/null @@ -1,205 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - - "github.com/hashicorp/go-multierror" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" -) - -// GraphNodeProvisioner is an interface that nodes that can be a provisioner -// must implement. The ProvisionerName returned is the name of the provisioner -// they satisfy. -type GraphNodeProvisioner interface { - ProvisionerName() string -} - -// GraphNodeCloseProvisioner is an interface that nodes that can be a close -// provisioner must implement. The CloseProvisionerName returned is the name -// of the provisioner they satisfy. -type GraphNodeCloseProvisioner interface { - CloseProvisionerName() string -} - -// GraphNodeProvisionerConsumer is an interface that nodes that require -// a provisioner must implement. ProvisionedBy must return the names of the -// provisioners to use. -type GraphNodeProvisionerConsumer interface { - ProvisionedBy() []string -} - -// ProvisionerTransformer is a GraphTransformer that maps resources to -// provisioners within the graph. This will error if there are any resources -// that don't map to proper resources. -type ProvisionerTransformer struct{} - -func (t *ProvisionerTransformer) Transform(g *Graph) error { - // Go through the other nodes and match them to provisioners they need - var err error - m := provisionerVertexMap(g) - for _, v := range g.Vertices() { - if pv, ok := v.(GraphNodeProvisionerConsumer); ok { - for _, p := range pv.ProvisionedBy() { - key := provisionerMapKey(p, pv) - if m[key] == nil { - err = multierror.Append(err, fmt.Errorf( - "%s: provisioner %s couldn't be found", - dag.VertexName(v), p)) - continue - } - - log.Printf("[TRACE] ProvisionerTransformer: %s is provisioned by %s (%q)", dag.VertexName(v), key, dag.VertexName(m[key])) - g.Connect(dag.BasicEdge(v, m[key])) - } - } - } - - return err -} - -// MissingProvisionerTransformer is a GraphTransformer that adds nodes -// for missing provisioners into the graph. -type MissingProvisionerTransformer struct { - // Provisioners is the list of provisioners we support. - Provisioners []string -} - -func (t *MissingProvisionerTransformer) Transform(g *Graph) error { - // Create a set of our supported provisioners - supported := make(map[string]struct{}, len(t.Provisioners)) - for _, v := range t.Provisioners { - supported[v] = struct{}{} - } - - // Get the map of provisioners we already have in our graph - m := provisionerVertexMap(g) - - // Go through all the provisioner consumers and make sure we add - // that provisioner if it is missing. - for _, v := range g.Vertices() { - pv, ok := v.(GraphNodeProvisionerConsumer) - if !ok { - continue - } - - // If this node has a subpath, then we use that as a prefix - // into our map to check for an existing provider. - path := addrs.RootModuleInstance - if sp, ok := pv.(GraphNodeSubPath); ok { - path = sp.Path() - } - - for _, p := range pv.ProvisionedBy() { - // Build the key for storing in the map - key := provisionerMapKey(p, pv) - - if _, ok := m[key]; ok { - // This provisioner already exists as a configure node - continue - } - - if _, ok := supported[p]; !ok { - // If we don't support the provisioner type, we skip it. - // Validation later will catch this as an error. - continue - } - - // Build the vertex - var newV dag.Vertex = &NodeProvisioner{ - NameValue: p, - PathValue: path, - } - - // Add the missing provisioner node to the graph - m[key] = g.Add(newV) - log.Printf("[TRACE] MissingProviderTransformer: added implicit provisioner %s, first implied by %s", key, dag.VertexName(v)) - } - } - - return nil -} - -// CloseProvisionerTransformer is a GraphTransformer that adds nodes to the -// graph that will close open provisioner connections that aren't needed -// anymore. A provisioner connection is not needed anymore once all depended -// resources in the graph are evaluated. -type CloseProvisionerTransformer struct{} - -func (t *CloseProvisionerTransformer) Transform(g *Graph) error { - m := closeProvisionerVertexMap(g) - for _, v := range g.Vertices() { - if pv, ok := v.(GraphNodeProvisionerConsumer); ok { - for _, p := range pv.ProvisionedBy() { - source := m[p] - - if source == nil { - // Create a new graphNodeCloseProvisioner and add it to the graph - source = &graphNodeCloseProvisioner{ProvisionerNameValue: p} - g.Add(source) - - // Make sure we also add the new graphNodeCloseProvisioner to the map - // so we don't create and add any duplicate graphNodeCloseProvisioners. - m[p] = source - } - - g.Connect(dag.BasicEdge(source, v)) - } - } - } - - return nil -} - -// provisionerMapKey is a helper that gives us the key to use for the -// maps returned by things such as provisionerVertexMap. -func provisionerMapKey(k string, v dag.Vertex) string { - pathPrefix := "" - if sp, ok := v.(GraphNodeSubPath); ok { - pathPrefix = sp.Path().String() + "." - } - - return pathPrefix + k -} - -func provisionerVertexMap(g *Graph) map[string]dag.Vertex { - m := make(map[string]dag.Vertex) - for _, v := range g.Vertices() { - if pv, ok := v.(GraphNodeProvisioner); ok { - key := provisionerMapKey(pv.ProvisionerName(), v) - m[key] = v - } - } - - return m -} - -func closeProvisionerVertexMap(g *Graph) map[string]dag.Vertex { - m := make(map[string]dag.Vertex) - for _, v := range g.Vertices() { - if pv, ok := v.(GraphNodeCloseProvisioner); ok { - m[pv.CloseProvisionerName()] = v - } - } - - return m -} - -type graphNodeCloseProvisioner struct { - ProvisionerNameValue string -} - -func (n *graphNodeCloseProvisioner) Name() string { - return fmt.Sprintf("provisioner.%s (close)", n.ProvisionerNameValue) -} - -// GraphNodeEvalable impl. -func (n *graphNodeCloseProvisioner) EvalTree() EvalNode { - return &EvalCloseProvisioner{Name: n.ProvisionerNameValue} -} - -func (n *graphNodeCloseProvisioner) CloseProvisionerName() string { - return n.ProvisionerNameValue -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_reference.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_reference.go deleted file mode 100644 index 54f9829c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_reference.go +++ /dev/null @@ -1,446 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/hashicorp/terraform-plugin-sdk/internal/lang" -) - -// GraphNodeReferenceable must be implemented by any node that represents -// a Terraform thing that can be referenced (resource, module, etc.). -// -// Even if the thing has no name, this should return an empty list. By -// implementing this and returning a non-nil result, you say that this CAN -// be referenced and other methods of referencing may still be possible (such -// as by path!) -type GraphNodeReferenceable interface { - GraphNodeSubPath - - // ReferenceableAddrs returns a list of addresses through which this can be - // referenced. - ReferenceableAddrs() []addrs.Referenceable -} - -// GraphNodeReferencer must be implemented by nodes that reference other -// Terraform items and therefore depend on them. -type GraphNodeReferencer interface { - GraphNodeSubPath - - // References returns a list of references made by this node, which - // include both a referenced address and source location information for - // the reference. - References() []*addrs.Reference -} - -// GraphNodeReferenceOutside is an interface that can optionally be implemented. -// A node that implements it can specify that its own referenceable addresses -// and/or the addresses it references are in a different module than the -// node itself. -// -// Any referenceable addresses returned by ReferenceableAddrs are interpreted -// relative to the returned selfPath. -// -// Any references returned by References are interpreted relative to the -// returned referencePath. -// -// It is valid but not required for either of these paths to match what is -// returned by method Path, though if both match the main Path then there -// is no reason to implement this method. -// -// The primary use-case for this is the nodes representing module input -// variables, since their expressions are resolved in terms of their calling -// module, but they are still referenced from their own module. -type GraphNodeReferenceOutside interface { - // ReferenceOutside returns a path in which any references from this node - // are resolved. - ReferenceOutside() (selfPath, referencePath addrs.ModuleInstance) -} - -// ReferenceTransformer is a GraphTransformer that connects all the -// nodes that reference each other in order to form the proper ordering. -type ReferenceTransformer struct{} - -func (t *ReferenceTransformer) Transform(g *Graph) error { - // Build a reference map so we can efficiently look up the references - vs := g.Vertices() - m := NewReferenceMap(vs) - - // Find the things that reference things and connect them - for _, v := range vs { - parents, _ := m.References(v) - parentsDbg := make([]string, len(parents)) - for i, v := range parents { - parentsDbg[i] = dag.VertexName(v) - } - log.Printf( - "[DEBUG] ReferenceTransformer: %q references: %v", - dag.VertexName(v), parentsDbg) - - for _, parent := range parents { - g.Connect(dag.BasicEdge(v, parent)) - } - } - - return nil -} - -// DestroyReferenceTransformer is a GraphTransformer that reverses the edges -// for locals and outputs that depend on other nodes which will be -// removed during destroy. If a destroy node is evaluated before the local or -// output value, it will be removed from the state, and the later interpolation -// will fail. -type DestroyValueReferenceTransformer struct{} - -func (t *DestroyValueReferenceTransformer) Transform(g *Graph) error { - vs := g.Vertices() - for _, v := range vs { - switch v.(type) { - case *NodeApplyableOutput, *NodeLocal: - // OK - default: - continue - } - - // reverse any outgoing edges so that the value is evaluated first. - for _, e := range g.EdgesFrom(v) { - target := e.Target() - - // only destroy nodes will be evaluated in reverse - if _, ok := target.(GraphNodeDestroyer); !ok { - continue - } - - log.Printf("[TRACE] output dep: %s", dag.VertexName(target)) - - g.RemoveEdge(e) - g.Connect(&DestroyEdge{S: target, T: v}) - } - } - - return nil -} - -// PruneUnusedValuesTransformer is s GraphTransformer that removes local and -// output values which are not referenced in the graph. Since outputs and -// locals always need to be evaluated, if they reference a resource that is not -// available in the state the interpolation could fail. -type PruneUnusedValuesTransformer struct{} - -func (t *PruneUnusedValuesTransformer) Transform(g *Graph) error { - // this might need multiple runs in order to ensure that pruning a value - // doesn't effect a previously checked value. - for removed := 0; ; removed = 0 { - for _, v := range g.Vertices() { - switch v.(type) { - case *NodeApplyableOutput, *NodeLocal: - // OK - default: - continue - } - - dependants := g.UpEdges(v) - - switch dependants.Len() { - case 0: - // nothing at all depends on this - g.Remove(v) - removed++ - case 1: - // because an output's destroy node always depends on the output, - // we need to check for the case of a single destroy node. - d := dependants.List()[0] - if _, ok := d.(*NodeDestroyableOutput); ok { - g.Remove(v) - removed++ - } - } - } - if removed == 0 { - break - } - } - - return nil -} - -// ReferenceMap is a structure that can be used to efficiently check -// for references on a graph. -type ReferenceMap struct { - // vertices is a map from internal reference keys (as produced by the - // mapKey method) to one or more vertices that are identified by each key. - // - // A particular reference key might actually identify multiple vertices, - // e.g. in situations where one object is contained inside another. - vertices map[string][]dag.Vertex - - // edges is a map whose keys are a subset of the internal reference keys - // from "vertices", and whose values are the nodes that refer to each - // key. The values in this map are the referrers, while values in - // "verticies" are the referents. The keys in both cases are referents. - edges map[string][]dag.Vertex -} - -// References returns the set of vertices that the given vertex refers to, -// and any referenced addresses that do not have corresponding vertices. -func (m *ReferenceMap) References(v dag.Vertex) ([]dag.Vertex, []addrs.Referenceable) { - rn, ok := v.(GraphNodeReferencer) - if !ok { - return nil, nil - } - if _, ok := v.(GraphNodeSubPath); !ok { - return nil, nil - } - - var matches []dag.Vertex - var missing []addrs.Referenceable - - for _, ref := range rn.References() { - subject := ref.Subject - - key := m.referenceMapKey(v, subject) - if _, exists := m.vertices[key]; !exists { - // If what we were looking for was a ResourceInstance then we - // might be in a resource-oriented graph rather than an - // instance-oriented graph, and so we'll see if we have the - // resource itself instead. - switch ri := subject.(type) { - case addrs.ResourceInstance: - subject = ri.ContainingResource() - case addrs.ResourceInstancePhase: - subject = ri.ContainingResource() - } - key = m.referenceMapKey(v, subject) - } - - vertices := m.vertices[key] - for _, rv := range vertices { - // don't include self-references - if rv == v { - continue - } - matches = append(matches, rv) - } - if len(vertices) == 0 { - missing = append(missing, ref.Subject) - } - } - - return matches, missing -} - -// Referrers returns the set of vertices that refer to the given vertex. -func (m *ReferenceMap) Referrers(v dag.Vertex) []dag.Vertex { - rn, ok := v.(GraphNodeReferenceable) - if !ok { - return nil - } - sp, ok := v.(GraphNodeSubPath) - if !ok { - return nil - } - - var matches []dag.Vertex - for _, addr := range rn.ReferenceableAddrs() { - key := m.mapKey(sp.Path(), addr) - referrers, ok := m.edges[key] - if !ok { - continue - } - - // If the referrer set includes our own given vertex then we skip, - // since we don't want to return self-references. - selfRef := false - for _, p := range referrers { - if p == v { - selfRef = true - break - } - } - if selfRef { - continue - } - - matches = append(matches, referrers...) - } - - return matches -} - -func (m *ReferenceMap) mapKey(path addrs.ModuleInstance, addr addrs.Referenceable) string { - return fmt.Sprintf("%s|%s", path.String(), addr.String()) -} - -// vertexReferenceablePath returns the path in which the given vertex can be -// referenced. This is the path that its results from ReferenceableAddrs -// are considered to be relative to. -// -// Only GraphNodeSubPath implementations can be referenced, so this method will -// panic if the given vertex does not implement that interface. -func (m *ReferenceMap) vertexReferenceablePath(v dag.Vertex) addrs.ModuleInstance { - sp, ok := v.(GraphNodeSubPath) - if !ok { - // Only nodes with paths can participate in a reference map. - panic(fmt.Errorf("vertexMapKey on vertex type %T which doesn't implement GraphNodeSubPath", sp)) - } - - if outside, ok := v.(GraphNodeReferenceOutside); ok { - // Vertex is referenced from a different module than where it was - // declared. - path, _ := outside.ReferenceOutside() - return path - } - - // Vertex is referenced from the same module as where it was declared. - return sp.Path() -} - -// vertexReferencePath returns the path in which references _from_ the given -// vertex must be interpreted. -// -// Only GraphNodeSubPath implementations can have references, so this method -// will panic if the given vertex does not implement that interface. -func vertexReferencePath(referrer dag.Vertex) addrs.ModuleInstance { - sp, ok := referrer.(GraphNodeSubPath) - if !ok { - // Only nodes with paths can participate in a reference map. - panic(fmt.Errorf("vertexReferencePath on vertex type %T which doesn't implement GraphNodeSubPath", sp)) - } - - var path addrs.ModuleInstance - if outside, ok := referrer.(GraphNodeReferenceOutside); ok { - // Vertex makes references to objects in a different module than where - // it was declared. - _, path = outside.ReferenceOutside() - return path - } - - // Vertex makes references to objects in the same module as where it - // was declared. - return sp.Path() -} - -// referenceMapKey produces keys for the "edges" map. "referrer" is the vertex -// that the reference is from, and "addr" is the address of the object being -// referenced. -// -// The result is an opaque string that includes both the address of the given -// object and the address of the module instance that object belongs to. -// -// Only GraphNodeSubPath implementations can be referrers, so this method will -// panic if the given vertex does not implement that interface. -func (m *ReferenceMap) referenceMapKey(referrer dag.Vertex, addr addrs.Referenceable) string { - path := vertexReferencePath(referrer) - return m.mapKey(path, addr) -} - -// NewReferenceMap is used to create a new reference map for the -// given set of vertices. -func NewReferenceMap(vs []dag.Vertex) *ReferenceMap { - var m ReferenceMap - - // Build the lookup table - vertices := make(map[string][]dag.Vertex) - for _, v := range vs { - _, ok := v.(GraphNodeSubPath) - if !ok { - // Only nodes with paths can participate in a reference map. - continue - } - - // We're only looking for referenceable nodes - rn, ok := v.(GraphNodeReferenceable) - if !ok { - continue - } - - path := m.vertexReferenceablePath(v) - - // Go through and cache them - for _, addr := range rn.ReferenceableAddrs() { - key := m.mapKey(path, addr) - vertices[key] = append(vertices[key], v) - } - - // Any node can be referenced by the address of the module it belongs - // to or any of that module's ancestors. - for _, addr := range path.Ancestors()[1:] { - // Can be referenced either as the specific call instance (with - // an instance key) or as the bare module call itself (the "module" - // block in the parent module that created the instance). - callPath, call := addr.Call() - callInstPath, callInst := addr.CallInstance() - callKey := m.mapKey(callPath, call) - callInstKey := m.mapKey(callInstPath, callInst) - vertices[callKey] = append(vertices[callKey], v) - vertices[callInstKey] = append(vertices[callInstKey], v) - } - } - - // Build the lookup table for referenced by - edges := make(map[string][]dag.Vertex) - for _, v := range vs { - _, ok := v.(GraphNodeSubPath) - if !ok { - // Only nodes with paths can participate in a reference map. - continue - } - - rn, ok := v.(GraphNodeReferencer) - if !ok { - // We're only looking for referenceable nodes - continue - } - - // Go through and cache them - for _, ref := range rn.References() { - if ref.Subject == nil { - // Should never happen - panic(fmt.Sprintf("%T.References returned reference with nil subject", rn)) - } - key := m.referenceMapKey(v, ref.Subject) - edges[key] = append(edges[key], v) - } - } - - m.vertices = vertices - m.edges = edges - return &m -} - -// ReferencesFromConfig returns the references that a configuration has -// based on the interpolated variables in a configuration. -func ReferencesFromConfig(body hcl.Body, schema *configschema.Block) []*addrs.Reference { - if body == nil { - return nil - } - refs, _ := lang.ReferencesInBlock(body, schema) - return refs -} - -// appendResourceDestroyReferences identifies resource and resource instance -// references in the given slice and appends to it the "destroy-phase" -// equivalents of those references, returning the result. -// -// This can be used in the References implementation for a node which must also -// depend on the destruction of anything it references. -func appendResourceDestroyReferences(refs []*addrs.Reference) []*addrs.Reference { - given := refs - for _, ref := range given { - switch tr := ref.Subject.(type) { - case addrs.Resource: - newRef := *ref // shallow copy - newRef.Subject = tr.Phase(addrs.ResourceInstancePhaseDestroy) - refs = append(refs, &newRef) - case addrs.ResourceInstance: - newRef := *ref // shallow copy - newRef.Subject = tr.Phase(addrs.ResourceInstancePhaseDestroy) - refs = append(refs, &newRef) - } - } - return refs -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_removed_modules.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_removed_modules.go deleted file mode 100644 index 327950d8..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_removed_modules.go +++ /dev/null @@ -1,33 +0,0 @@ -package terraform - -import ( - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// RemovedModuleTransformer implements GraphTransformer to add nodes indicating -// when a module was removed from the configuration. -type RemovedModuleTransformer struct { - Config *configs.Config // root node in the config tree - State *states.State -} - -func (t *RemovedModuleTransformer) Transform(g *Graph) error { - // nothing to remove if there's no state! - if t.State == nil { - return nil - } - - for _, m := range t.State.Modules { - cc := t.Config.DescendentForInstance(m.Addr) - if cc != nil { - continue - } - - log.Printf("[DEBUG] %s is no longer in configuration\n", m.Addr) - g.Add(&NodeModuleRemoved{Addr: m.Addr}) - } - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_resource_count.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_resource_count.go deleted file mode 100644 index 51d9466a..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_resource_count.go +++ /dev/null @@ -1,71 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - "github.com/zclconf/go-cty/cty" -) - -// ResourceCountTransformer is a GraphTransformer that expands the count -// out for a specific resource. -// -// This assumes that the count is already interpolated. -type ResourceCountTransformer struct { - Concrete ConcreteResourceInstanceNodeFunc - Schema *configschema.Block - - // Count is either the number of indexed instances to create, or -1 to - // indicate that count is not set at all and thus a no-key instance should - // be created. - Count int - ForEach map[string]cty.Value - Addr addrs.AbsResource -} - -func (t *ResourceCountTransformer) Transform(g *Graph) error { - if t.Count < 0 && t.ForEach == nil { - // Negative count indicates that count is not set at all. - addr := t.Addr.Instance(addrs.NoKey) - - abstract := NewNodeAbstractResourceInstance(addr) - abstract.Schema = t.Schema - var node dag.Vertex = abstract - if f := t.Concrete; f != nil { - node = f(abstract) - } - - g.Add(node) - return nil - } - - // Add nodes related to the for_each expression - for key := range t.ForEach { - addr := t.Addr.Instance(addrs.StringKey(key)) - abstract := NewNodeAbstractResourceInstance(addr) - abstract.Schema = t.Schema - var node dag.Vertex = abstract - if f := t.Concrete; f != nil { - node = f(abstract) - } - - g.Add(node) - } - - // For each count, build and add the node - for i := 0; i < t.Count; i++ { - key := addrs.IntKey(i) - addr := t.Addr.Instance(key) - - abstract := NewNodeAbstractResourceInstance(addr) - abstract.Schema = t.Schema - var node dag.Vertex = abstract - if f := t.Concrete; f != nil { - node = f(abstract) - } - - g.Add(node) - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_root.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_root.go deleted file mode 100644 index 485c1c8a..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_root.go +++ /dev/null @@ -1,38 +0,0 @@ -package terraform - -import "github.com/hashicorp/terraform-plugin-sdk/internal/dag" - -const rootNodeName = "root" - -// RootTransformer is a GraphTransformer that adds a root to the graph. -type RootTransformer struct{} - -func (t *RootTransformer) Transform(g *Graph) error { - // If we already have a good root, we're done - if _, err := g.Root(); err == nil { - return nil - } - - // Add a root - var root graphNodeRoot - g.Add(root) - - // Connect the root to all the edges that need it - for _, v := range g.Vertices() { - if v == root { - continue - } - - if g.UpEdges(v).Len() == 0 { - g.Connect(dag.BasicEdge(root, v)) - } - } - - return nil -} - -type graphNodeRoot struct{} - -func (n graphNodeRoot) Name() string { - return rootNodeName -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_state.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_state.go deleted file mode 100644 index e7d95be9..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_state.go +++ /dev/null @@ -1,74 +0,0 @@ -package terraform - -import ( - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/states" -) - -// StateTransformer is a GraphTransformer that adds the elements of -// the state to the graph. -// -// This transform is used for example by the DestroyPlanGraphBuilder to ensure -// that only resources that are in the state are represented in the graph. -type StateTransformer struct { - // ConcreteCurrent and ConcreteDeposed are used to specialize the abstract - // resource instance nodes that this transformer will create. - // - // If either of these is nil, the objects of that type will be skipped and - // not added to the graph at all. It doesn't make sense to use this - // transformer without setting at least one of these, since that would - // skip everything and thus be a no-op. - ConcreteCurrent ConcreteResourceInstanceNodeFunc - ConcreteDeposed ConcreteResourceInstanceDeposedNodeFunc - - State *states.State -} - -func (t *StateTransformer) Transform(g *Graph) error { - if !t.State.HasResources() { - log.Printf("[TRACE] StateTransformer: state is empty, so nothing to do") - return nil - } - - switch { - case t.ConcreteCurrent != nil && t.ConcreteDeposed != nil: - log.Printf("[TRACE] StateTransformer: creating nodes for both current and deposed instance objects") - case t.ConcreteCurrent != nil: - log.Printf("[TRACE] StateTransformer: creating nodes for current instance objects only") - case t.ConcreteDeposed != nil: - log.Printf("[TRACE] StateTransformer: creating nodes for deposed instance objects only") - default: - log.Printf("[TRACE] StateTransformer: pointless no-op call, creating no nodes at all") - } - - for _, ms := range t.State.Modules { - moduleAddr := ms.Addr - - for _, rs := range ms.Resources { - resourceAddr := rs.Addr.Absolute(moduleAddr) - - for key, is := range rs.Instances { - addr := resourceAddr.Instance(key) - - if obj := is.Current; obj != nil && t.ConcreteCurrent != nil { - abstract := NewNodeAbstractResourceInstance(addr) - node := t.ConcreteCurrent(abstract) - g.Add(node) - log.Printf("[TRACE] StateTransformer: added %T for %s current object", node, addr) - } - - if t.ConcreteDeposed != nil { - for dk := range is.Deposed { - abstract := NewNodeAbstractResourceInstance(addr) - node := t.ConcreteDeposed(abstract, dk) - g.Add(node) - log.Printf("[TRACE] StateTransformer: added %T for %s deposed object %s", node, addr, dk) - } - } - } - } - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_targets.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_targets.go deleted file mode 100644 index beb1eed9..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_targets.go +++ /dev/null @@ -1,267 +0,0 @@ -package terraform - -import ( - "log" - - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" -) - -// GraphNodeTargetable is an interface for graph nodes to implement when they -// need to be told about incoming targets. This is useful for nodes that need -// to respect targets as they dynamically expand. Note that the list of targets -// provided will contain every target provided, and each implementing graph -// node must filter this list to targets considered relevant. -type GraphNodeTargetable interface { - SetTargets([]addrs.Targetable) -} - -// GraphNodeTargetDownstream is an interface for graph nodes that need to -// be remain present under targeting if any of their dependencies are targeted. -// TargetDownstream is called with the set of vertices that are direct -// dependencies for the node, and it should return true if the node must remain -// in the graph in support of those dependencies. -// -// This is used in situations where the dependency edges are representing an -// ordering relationship but the dependency must still be visited if its -// dependencies are visited. This is true for outputs, for example, since -// they must get updated if any of their dependent resources get updated, -// which would not normally be true if one of their dependencies were targeted. -type GraphNodeTargetDownstream interface { - TargetDownstream(targeted, untargeted *dag.Set) bool -} - -// TargetsTransformer is a GraphTransformer that, when the user specifies a -// list of resources to target, limits the graph to only those resources and -// their dependencies. -type TargetsTransformer struct { - // List of targeted resource names specified by the user - Targets []addrs.Targetable - - // If set, the index portions of resource addresses will be ignored - // for comparison. This is used when transforming a graph where - // counted resources have not yet been expanded, since otherwise - // the unexpanded nodes (which never have indices) would not match. - IgnoreIndices bool - - // Set to true when we're in a `terraform destroy` or a - // `terraform plan -destroy` - Destroy bool -} - -func (t *TargetsTransformer) Transform(g *Graph) error { - if len(t.Targets) > 0 { - targetedNodes, err := t.selectTargetedNodes(g, t.Targets) - if err != nil { - return err - } - - for _, v := range g.Vertices() { - removable := false - if _, ok := v.(GraphNodeResource); ok { - removable = true - } - - if vr, ok := v.(RemovableIfNotTargeted); ok { - removable = vr.RemoveIfNotTargeted() - } - - if removable && !targetedNodes.Include(v) { - log.Printf("[DEBUG] Removing %q, filtered by targeting.", dag.VertexName(v)) - g.Remove(v) - } - } - } - - return nil -} - -// Returns a set of targeted nodes. A targeted node is either addressed -// directly, address indirectly via its container, or it's a dependency of a -// targeted node. Destroy mode keeps dependents instead of dependencies. -func (t *TargetsTransformer) selectTargetedNodes(g *Graph, addrs []addrs.Targetable) (*dag.Set, error) { - targetedNodes := new(dag.Set) - - vertices := g.Vertices() - - for _, v := range vertices { - if t.nodeIsTarget(v, addrs) { - targetedNodes.Add(v) - - // We inform nodes that ask about the list of targets - helps for nodes - // that need to dynamically expand. Note that this only occurs for nodes - // that are already directly targeted. - if tn, ok := v.(GraphNodeTargetable); ok { - tn.SetTargets(addrs) - } - - var deps *dag.Set - var err error - if t.Destroy { - deps, err = g.Descendents(v) - } else { - deps, err = g.Ancestors(v) - } - if err != nil { - return nil, err - } - - for _, d := range deps.List() { - targetedNodes.Add(d) - } - } - } - return t.addDependencies(targetedNodes, g) -} - -func (t *TargetsTransformer) addDependencies(targetedNodes *dag.Set, g *Graph) (*dag.Set, error) { - // Handle nodes that need to be included if their dependencies are included. - // This requires multiple passes since we need to catch transitive - // dependencies if and only if they are via other nodes that also - // support TargetDownstream. For example: - // output -> output -> targeted-resource: both outputs need to be targeted - // output -> non-targeted-resource -> targeted-resource: output not targeted - // - // We'll keep looping until we stop targeting more nodes. - queue := targetedNodes.List() - for len(queue) > 0 { - vertices := queue - queue = nil // ready to append for next iteration if neccessary - for _, v := range vertices { - // providers don't cause transitive dependencies, so don't target - // downstream from them. - if _, ok := v.(GraphNodeProvider); ok { - continue - } - - dependers := g.UpEdges(v) - if dependers == nil { - // indicates that there are no up edges for this node, so - // we have nothing to do here. - continue - } - - dependers = dependers.Filter(func(dv interface{}) bool { - _, ok := dv.(GraphNodeTargetDownstream) - return ok - }) - - if dependers.Len() == 0 { - continue - } - - for _, dv := range dependers.List() { - if targetedNodes.Include(dv) { - // Already present, so nothing to do - continue - } - - // We'll give the node some information about what it's - // depending on in case that informs its decision about whether - // it is safe to be targeted. - deps := g.DownEdges(v) - - depsTargeted := deps.Intersection(targetedNodes) - depsUntargeted := deps.Difference(depsTargeted) - - if dv.(GraphNodeTargetDownstream).TargetDownstream(depsTargeted, depsUntargeted) { - targetedNodes.Add(dv) - // Need to visit this node on the next pass to see if it - // has any transitive dependers. - queue = append(queue, dv) - } - } - } - } - - return targetedNodes.Filter(func(dv interface{}) bool { - return filterPartialOutputs(dv, targetedNodes, g) - }), nil -} - -// Outputs may have been included transitively, but if any of their -// dependencies have been pruned they won't be resolvable. -// If nothing depends on the output, and the output is missing any -// dependencies, remove it from the graph. -// This essentially maintains the previous behavior where interpolation in -// outputs would fail silently, but can now surface errors where the output -// is required. -func filterPartialOutputs(v interface{}, targetedNodes *dag.Set, g *Graph) bool { - // should this just be done with TargetDownstream? - if _, ok := v.(*NodeApplyableOutput); !ok { - return true - } - - dependers := g.UpEdges(v) - for _, d := range dependers.List() { - if _, ok := d.(*NodeCountBoundary); ok { - continue - } - - if !targetedNodes.Include(d) { - // this one is going to be removed, so it doesn't count - continue - } - - // as soon as we see a real dependency, we mark this as - // non-removable - return true - } - - depends := g.DownEdges(v) - - for _, d := range depends.List() { - if !targetedNodes.Include(d) { - log.Printf("[WARN] %s missing targeted dependency %s, removing from the graph", - dag.VertexName(v), dag.VertexName(d)) - return false - } - } - return true -} - -func (t *TargetsTransformer) nodeIsTarget(v dag.Vertex, targets []addrs.Targetable) bool { - var vertexAddr addrs.Targetable - switch r := v.(type) { - case GraphNodeResourceInstance: - vertexAddr = r.ResourceInstanceAddr() - case GraphNodeResource: - vertexAddr = r.ResourceAddr() - default: - // Only resource and resource instance nodes can be targeted. - return false - } - _, ok := v.(GraphNodeResource) - if !ok { - return false - } - - for _, targetAddr := range targets { - if t.IgnoreIndices { - // If we're ignoring indices then we'll convert any resource instance - // addresses into resource addresses. We don't need to convert - // vertexAddr because instance addresses are contained within - // their associated resources, and so .TargetContains will take - // care of this for us. - if instance, isInstance := targetAddr.(addrs.AbsResourceInstance); isInstance { - targetAddr = instance.ContainingResource() - } - } - if targetAddr.TargetContains(vertexAddr) { - return true - } - } - - return false -} - -// RemovableIfNotTargeted is a special interface for graph nodes that -// aren't directly addressable, but need to be removed from the graph when they -// are not targeted. (Nodes that are not directly targeted end up in the set of -// targeted nodes because something that _is_ targeted depends on them.) The -// initial use case for this interface is GraphNodeConfigVariable, which was -// having trouble interpolating for module variables in targeted scenarios that -// filtered out the resource node being referenced. -type RemovableIfNotTargeted interface { - RemoveIfNotTargeted() bool -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_transitive_reduction.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_transitive_reduction.go deleted file mode 100644 index 21842789..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_transitive_reduction.go +++ /dev/null @@ -1,20 +0,0 @@ -package terraform - -// TransitiveReductionTransformer is a GraphTransformer that performs -// finds the transitive reduction of the graph. For a definition of -// transitive reduction, see Wikipedia. -type TransitiveReductionTransformer struct{} - -func (t *TransitiveReductionTransformer) Transform(g *Graph) error { - // If the graph isn't valid, skip the transitive reduction. - // We don't error here because Terraform itself handles graph - // validation in a better way, or we assume it does. - if err := g.Validate(); err != nil { - return nil - } - - // Do it - g.TransitiveReduction() - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_variable.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_variable.go deleted file mode 100644 index 3afce566..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_variable.go +++ /dev/null @@ -1,40 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" -) - -// RootVariableTransformer is a GraphTransformer that adds all the root -// variables to the graph. -// -// Root variables are currently no-ops but they must be added to the -// graph since downstream things that depend on them must be able to -// reach them. -type RootVariableTransformer struct { - Config *configs.Config -} - -func (t *RootVariableTransformer) Transform(g *Graph) error { - // We can have no variables if we have no config. - if t.Config == nil { - return nil - } - - // We're only considering root module variables here, since child - // module variables are handled by ModuleVariableTransformer. - vars := t.Config.Module.Variables - - // Add all variables here - for _, v := range vars { - node := &NodeRootVariable{ - Addr: addrs.InputVariable{ - Name: v.Name, - }, - Config: v, - } - g.Add(node) - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_vertex.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_vertex.go deleted file mode 100644 index 6b3c62d1..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/transform_vertex.go +++ /dev/null @@ -1,44 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform-plugin-sdk/internal/dag" -) - -// VertexTransformer is a GraphTransformer that transforms vertices -// using the GraphVertexTransformers. The Transforms are run in sequential -// order. If a transform replaces a vertex then the next transform will see -// the new vertex. -type VertexTransformer struct { - Transforms []GraphVertexTransformer -} - -func (t *VertexTransformer) Transform(g *Graph) error { - for _, v := range g.Vertices() { - for _, vt := range t.Transforms { - newV, err := vt.Transform(v) - if err != nil { - return err - } - - // If the vertex didn't change, then don't do anything more - if newV == v { - continue - } - - // Vertex changed, replace it within the graph - if ok := g.Replace(v, newV); !ok { - // This should never happen, big problem - return fmt.Errorf( - "Failed to replace %s with %s!\n\nSource: %#v\n\nTarget: %#v", - dag.VertexName(v), dag.VertexName(newV), v, newV) - } - - // Replace v so that future transforms use the proper vertex - v = newV - } - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/ui_input.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/ui_input.go deleted file mode 100644 index f6790d9e..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/ui_input.go +++ /dev/null @@ -1,28 +0,0 @@ -package terraform - -import "context" - -// UIInput is the interface that must be implemented to ask for input -// from this user. This should forward the request to wherever the user -// inputs things to ask for values. -type UIInput interface { - Input(context.Context, *InputOpts) (string, error) -} - -// InputOpts are options for asking for input. -type InputOpts struct { - // Id is a unique ID for the question being asked that might be - // used for logging or to look up a prior answered question. - Id string - - // Query is a human-friendly question for inputting this value. - Query string - - // Description is a description about what this option is. Be wary - // that this will probably be in a terminal so split lines as you see - // necessary. - Description string - - // Default will be the value returned if no data is entered. - Default string -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/ui_output_provisioner.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/ui_output_provisioner.go deleted file mode 100644 index 0d7d4ce0..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/ui_output_provisioner.go +++ /dev/null @@ -1,19 +0,0 @@ -package terraform - -import ( - "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" -) - -// ProvisionerUIOutput is an implementation of UIOutput that calls a hook -// for the output so that the hooks can handle it. -type ProvisionerUIOutput struct { - InstanceAddr addrs.AbsResourceInstance - ProvisionerType string - Hooks []Hook -} - -func (o *ProvisionerUIOutput) Output(msg string) { - for _, h := range o.Hooks { - h.ProvisionOutput(o.InstanceAddr, o.ProvisionerType, msg) - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/util.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/util.go index 5428cd5a..01ac810f 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/util.go +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/util.go @@ -4,59 +4,6 @@ import ( "sort" ) -// Semaphore is a wrapper around a channel to provide -// utility methods to clarify that we are treating the -// channel as a semaphore -type Semaphore chan struct{} - -// NewSemaphore creates a semaphore that allows up -// to a given limit of simultaneous acquisitions -func NewSemaphore(n int) Semaphore { - if n == 0 { - panic("semaphore with limit 0") - } - ch := make(chan struct{}, n) - return Semaphore(ch) -} - -// Acquire is used to acquire an available slot. -// Blocks until available. -func (s Semaphore) Acquire() { - s <- struct{}{} -} - -// TryAcquire is used to do a non-blocking acquire. -// Returns a bool indicating success -func (s Semaphore) TryAcquire() bool { - select { - case s <- struct{}{}: - return true - default: - return false - } -} - -// Release is used to return a slot. Acquire must -// be called as a pre-condition. -func (s Semaphore) Release() { - select { - case <-s: - default: - panic("release without an acquire") - } -} - -// strSliceContains checks if a given string is contained in a slice -// When anybody asks why Go needs generics, here you go. -func strSliceContains(haystack []string, needle string) bool { - for _, s := range haystack { - if s == needle { - return true - } - } - return false -} - // deduplicate a slice of strings func uniqueStrings(s []string) []string { if len(s) < 2 { diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/util_test.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/util_test.go new file mode 100644 index 00000000..220d73cc --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/util_test.go @@ -0,0 +1,48 @@ +package terraform + +import ( + "fmt" + "reflect" + "testing" +) + +func TestUniqueStrings(t *testing.T) { + cases := []struct { + Input []string + Expected []string + }{ + { + []string{}, + []string{}, + }, + { + []string{"x"}, + []string{"x"}, + }, + { + []string{"a", "b", "c"}, + []string{"a", "b", "c"}, + }, + { + []string{"a", "a", "a"}, + []string{"a"}, + }, + { + []string{"a", "b", "a", "b", "a", "a"}, + []string{"a", "b"}, + }, + { + []string{"c", "b", "a", "c", "b"}, + []string{"a", "b", "c"}, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("unique-%d", i), func(t *testing.T) { + actual := uniqueStrings(tc.Input) + if !reflect.DeepEqual(tc.Expected, actual) { + t.Fatalf("Expected: %q\nGot: %q", tc.Expected, actual) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/valuesourcetype_string.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/valuesourcetype_string.go deleted file mode 100644 index 627593d7..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/valuesourcetype_string.go +++ /dev/null @@ -1,59 +0,0 @@ -// Code generated by "stringer -type ValueSourceType"; DO NOT EDIT. - -package terraform - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[ValueFromUnknown-0] - _ = x[ValueFromConfig-67] - _ = x[ValueFromAutoFile-70] - _ = x[ValueFromNamedFile-78] - _ = x[ValueFromCLIArg-65] - _ = x[ValueFromEnvVar-69] - _ = x[ValueFromInput-73] - _ = x[ValueFromPlan-80] - _ = x[ValueFromCaller-83] -} - -const ( - _ValueSourceType_name_0 = "ValueFromUnknown" - _ValueSourceType_name_1 = "ValueFromCLIArg" - _ValueSourceType_name_2 = "ValueFromConfig" - _ValueSourceType_name_3 = "ValueFromEnvVarValueFromAutoFile" - _ValueSourceType_name_4 = "ValueFromInput" - _ValueSourceType_name_5 = "ValueFromNamedFile" - _ValueSourceType_name_6 = "ValueFromPlan" - _ValueSourceType_name_7 = "ValueFromCaller" -) - -var ( - _ValueSourceType_index_3 = [...]uint8{0, 15, 32} -) - -func (i ValueSourceType) String() string { - switch { - case i == 0: - return _ValueSourceType_name_0 - case i == 65: - return _ValueSourceType_name_1 - case i == 67: - return _ValueSourceType_name_2 - case 69 <= i && i <= 70: - i -= 69 - return _ValueSourceType_name_3[_ValueSourceType_index_3[i]:_ValueSourceType_index_3[i+1]] - case i == 73: - return _ValueSourceType_name_4 - case i == 78: - return _ValueSourceType_name_5 - case i == 80: - return _ValueSourceType_name_6 - case i == 83: - return _ValueSourceType_name_7 - default: - return "ValueSourceType(" + strconv.FormatInt(int64(i), 10) + ")" - } -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/variables.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/variables.go deleted file mode 100644 index 4ae9c92c..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/variables.go +++ /dev/null @@ -1,313 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" - - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" -) - -// InputValue represents a value for a variable in the root module, provided -// as part of the definition of an operation. -type InputValue struct { - Value cty.Value - SourceType ValueSourceType - - // SourceRange provides source location information for values whose - // SourceType is either ValueFromConfig or ValueFromFile. It is not - // populated for other source types, and so should not be used. - SourceRange tfdiags.SourceRange -} - -// ValueSourceType describes what broad category of source location provided -// a particular value. -type ValueSourceType rune - -const ( - // ValueFromUnknown is the zero value of ValueSourceType and is not valid. - ValueFromUnknown ValueSourceType = 0 - - // ValueFromConfig indicates that a value came from a .tf or .tf.json file, - // e.g. the default value defined for a variable. - ValueFromConfig ValueSourceType = 'C' - - // ValueFromAutoFile indicates that a value came from a "values file", like - // a .tfvars file, that was implicitly loaded by naming convention. - ValueFromAutoFile ValueSourceType = 'F' - - // ValueFromNamedFile indicates that a value came from a named "values file", - // like a .tfvars file, that was passed explicitly on the command line (e.g. - // -var-file=foo.tfvars). - ValueFromNamedFile ValueSourceType = 'N' - - // ValueFromCLIArg indicates that the value was provided directly in - // a CLI argument. The name of this argument is not recorded and so it must - // be inferred from context. - ValueFromCLIArg ValueSourceType = 'A' - - // ValueFromEnvVar indicates that the value was provided via an environment - // variable. The name of the variable is not recorded and so it must be - // inferred from context. - ValueFromEnvVar ValueSourceType = 'E' - - // ValueFromInput indicates that the value was provided at an interactive - // input prompt. - ValueFromInput ValueSourceType = 'I' - - // ValueFromPlan indicates that the value was retrieved from a stored plan. - ValueFromPlan ValueSourceType = 'P' - - // ValueFromCaller indicates that the value was explicitly overridden by - // a caller to Context.SetVariable after the context was constructed. - ValueFromCaller ValueSourceType = 'S' -) - -func (v *InputValue) GoString() string { - if (v.SourceRange != tfdiags.SourceRange{}) { - return fmt.Sprintf("&terraform.InputValue{Value: %#v, SourceType: %#v, SourceRange: %#v}", v.Value, v.SourceType, v.SourceRange) - } else { - return fmt.Sprintf("&terraform.InputValue{Value: %#v, SourceType: %#v}", v.Value, v.SourceType) - } -} - -func (v ValueSourceType) GoString() string { - return fmt.Sprintf("terraform.%s", v) -} - -//go:generate go run golang.org/x/tools/cmd/stringer -type ValueSourceType - -// InputValues is a map of InputValue instances. -type InputValues map[string]*InputValue - -// InputValuesFromCaller turns the given map of naked values into an -// InputValues that attributes each value to "a caller", using the source -// type ValueFromCaller. This is primarily useful for testing purposes. -// -// This should not be used as a general way to convert map[string]cty.Value -// into InputValues, since in most real cases we want to set a suitable -// other SourceType and possibly SourceRange value. -func InputValuesFromCaller(vals map[string]cty.Value) InputValues { - ret := make(InputValues, len(vals)) - for k, v := range vals { - ret[k] = &InputValue{ - Value: v, - SourceType: ValueFromCaller, - } - } - return ret -} - -// Override merges the given value maps with the receiver, overriding any -// conflicting keys so that the latest definition wins. -func (vv InputValues) Override(others ...InputValues) InputValues { - // FIXME: This should check to see if any of the values are maps and - // merge them if so, in order to preserve the behavior from prior to - // Terraform 0.12. - ret := make(InputValues) - for k, v := range vv { - ret[k] = v - } - for _, other := range others { - for k, v := range other { - ret[k] = v - } - } - return ret -} - -// JustValues returns a map that just includes the values, discarding the -// source information. -func (vv InputValues) JustValues() map[string]cty.Value { - ret := make(map[string]cty.Value, len(vv)) - for k, v := range vv { - ret[k] = v.Value - } - return ret -} - -// DefaultVariableValues returns an InputValues map representing the default -// values specified for variables in the given configuration map. -func DefaultVariableValues(configs map[string]*configs.Variable) InputValues { - ret := make(InputValues) - for k, c := range configs { - if c.Default == cty.NilVal { - continue - } - ret[k] = &InputValue{ - Value: c.Default, - SourceType: ValueFromConfig, - SourceRange: tfdiags.SourceRangeFromHCL(c.DeclRange), - } - } - return ret -} - -// SameValues returns true if the given InputValues has the same values as -// the receiever, disregarding the source types and source ranges. -// -// Values are compared using the cty "RawEquals" method, which means that -// unknown values can be considered equal to one another if they are of the -// same type. -func (vv InputValues) SameValues(other InputValues) bool { - if len(vv) != len(other) { - return false - } - - for k, v := range vv { - ov, exists := other[k] - if !exists { - return false - } - if !v.Value.RawEquals(ov.Value) { - return false - } - } - - return true -} - -// HasValues returns true if the reciever has the same values as in the given -// map, disregarding the source types and source ranges. -// -// Values are compared using the cty "RawEquals" method, which means that -// unknown values can be considered equal to one another if they are of the -// same type. -func (vv InputValues) HasValues(vals map[string]cty.Value) bool { - if len(vv) != len(vals) { - return false - } - - for k, v := range vv { - oVal, exists := vals[k] - if !exists { - return false - } - if !v.Value.RawEquals(oVal) { - return false - } - } - - return true -} - -// Identical returns true if the given InputValues has the same values, -// source types, and source ranges as the receiver. -// -// Values are compared using the cty "RawEquals" method, which means that -// unknown values can be considered equal to one another if they are of the -// same type. -// -// This method is primarily for testing. For most practical purposes, it's -// better to use SameValues or HasValues. -func (vv InputValues) Identical(other InputValues) bool { - if len(vv) != len(other) { - return false - } - - for k, v := range vv { - ov, exists := other[k] - if !exists { - return false - } - if !v.Value.RawEquals(ov.Value) { - return false - } - if v.SourceType != ov.SourceType { - return false - } - if v.SourceRange != ov.SourceRange { - return false - } - } - - return true -} - -// checkInputVariables ensures that variable values supplied at the UI conform -// to their corresponding declarations in configuration. -// -// The set of values is considered valid only if the returned diagnostics -// does not contain errors. A valid set of values may still produce warnings, -// which should be returned to the user. -func checkInputVariables(vcs map[string]*configs.Variable, vs InputValues) tfdiags.Diagnostics { - var diags tfdiags.Diagnostics - - for name, vc := range vcs { - val, isSet := vs[name] - if !isSet { - // Always an error, since the caller should already have included - // default values from the configuration in the values map. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Unassigned variable", - fmt.Sprintf("The input variable %q has not been assigned a value. This is a bug in Terraform; please report it in a GitHub issue.", name), - )) - continue - } - - wantType := vc.Type - - // A given value is valid if it can convert to the desired type. - _, err := convert.Convert(val.Value, wantType) - if err != nil { - switch val.SourceType { - case ValueFromConfig, ValueFromAutoFile, ValueFromNamedFile: - // We have source location information for these. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid value for input variable", - Detail: fmt.Sprintf("The given value is not valid for variable %q: %s.", name, err), - Subject: val.SourceRange.ToHCL().Ptr(), - }) - case ValueFromEnvVar: - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid value for input variable", - fmt.Sprintf("The environment variable TF_VAR_%s does not contain a valid value for variable %q: %s.", name, name, err), - )) - case ValueFromCLIArg: - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid value for input variable", - fmt.Sprintf("The argument -var=\"%s=...\" does not contain a valid value for variable %q: %s.", name, name, err), - )) - case ValueFromInput: - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid value for input variable", - fmt.Sprintf("The value entered for variable %q is not valid: %s.", name, err), - )) - default: - // The above gets us good coverage for the situations users - // are likely to encounter with their own inputs. The other - // cases are generally implementation bugs, so we'll just - // use a generic error for these. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid value for input variable", - fmt.Sprintf("The value provided for variable %q is not valid: %s.", name, err), - )) - } - } - } - - // Check for any variables that are assigned without being configured. - // This is always an implementation error in the caller, because we - // expect undefined variables to be caught during context construction - // where there is better context to report it well. - for name := range vs { - if _, defined := vcs[name]; !defined { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Value assigned to undeclared variable", - fmt.Sprintf("A value was assigned to an undeclared input variable %q.", name), - )) - } - } - - return diags -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/walkoperation_string.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/walkoperation_string.go deleted file mode 100644 index 0666aa5f..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/walkoperation_string.go +++ /dev/null @@ -1,31 +0,0 @@ -// Code generated by "stringer -type=walkOperation graph_walk_operation.go"; DO NOT EDIT. - -package terraform - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[walkInvalid-0] - _ = x[walkApply-1] - _ = x[walkPlan-2] - _ = x[walkPlanDestroy-3] - _ = x[walkRefresh-4] - _ = x[walkValidate-5] - _ = x[walkDestroy-6] - _ = x[walkImport-7] - _ = x[walkEval-8] -} - -const _walkOperation_name = "walkInvalidwalkApplywalkPlanwalkPlanDestroywalkRefreshwalkValidatewalkDestroywalkImportwalkEval" - -var _walkOperation_index = [...]uint8{0, 11, 20, 28, 43, 54, 66, 77, 87, 95} - -func (i walkOperation) String() string { - if i >= walkOperation(len(_walkOperation_index)-1) { - return "walkOperation(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _walkOperation_name[_walkOperation_index[i]:_walkOperation_index[i+1]] -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/tools/tools.go b/vendor/github.com/hashicorp/terraform-plugin-sdk/tools/tools.go new file mode 100644 index 00000000..48135a7e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-sdk/tools/tools.go @@ -0,0 +1,9 @@ +// +build tools + +package tools + +import ( + _ "github.com/golang/mock/mockgen" + _ "golang.org/x/tools/cmd/cover" + _ "golang.org/x/tools/cmd/stringer" +) diff --git a/vendor/github.com/hashicorp/terraform-plugin-test/CHANGELOG.md b/vendor/github.com/hashicorp/terraform-plugin-test/CHANGELOG.md new file mode 100644 index 00000000..513e76a3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-plugin-test/CHANGELOG.md @@ -0,0 +1,74 @@ +# 2.2.1 (April 27, 2021) + +SECURITY: + + - Upgraded to terraform-exec v0.13.3 to address GPG key rotation. See [terraform-exec's CHANGELOG](https://github.com/hashicorp/terraform-exec/blob/main/CHANGELOG.md#0133-april-23-2021). + +# 2.2.0 (April 01, 2021) + +NOTES: + +In this release, we upgraded to a version of terraform-exec that surfaces numbers in state as json.Number instead of float64. You may need to update your type assertions against numbers in state. + +ENHANCEMENTS: + + - Added support for Terraform 0.15 ([#45](https://github.com/hashicorp/terraform-plugin-test/pull/45)) + +# 2.1.3 (February 22, 2021) + +BUG FIXES: + + - Fix compilation error from go-getter ([#44](https://github.com/hashicorp/terraform-plugin-test/pull/44)) + +# 2.1.2 (September 15, 2020) + +BUG FIXES: + + - Fix plan output to be in a human-friendly format ([#40](https://github.com/hashicorp/terraform-plugin-test/pull/40)) + +# 2.1.1 (September 9, 2020) + +BUG FIXES: + + - Fix propagation of plugin reattach information ([#38](https://github.com/hashicorp/terraform-plugin-test/pull/38)) + +# 2.1.0 (September 2, 2020) + +FEATURES: + + - Added the ability to create destroy plans. ([#37](https://github.com/hashicorp/terraform-plugin-test/pull/37)) + +ENHANCEMENTS: + + - Normalised internal Terraform CLI commands using github.com/hashicorp/terraform-exec module. ([#35](https://github.com/hashicorp/terraform-plugin-test/pull/35)) + +# 2.0.0 (August 10, 2020) + +FEATURES: + + - Simplified API signatures to reflect no longer needing provider name ([#32](https://github.com/hashicorp/terraform-plugin-test/pull/32)) + - Implement SavedPlanStdout which captures a non-json stdout run of `terraform show` of a planfile ([#34](https://github.com/hashicorp/terraform-plugin-test/pull/34)) + +# 1.4.4 (July 10, 2020) + +BUG FIXES: + + - Fix Windows bug in versions of Terraform below 0.13.0-beta2 ([#30](https://github.com/hashicorp/terraform-plugin-test/pull/30)) + +# 1.4.3 (July 7, 2020) + +DEPENDENCIES: + + - `github.com/hashicorp/go-getter@v1.4.0` ([#29](https://github.com/hashicorp/terraform-plugin-test/pull/29)) + +# 1.4.2 (July 7, 2020) + +DEPENDENCIES: + + - `github.com/hashicorp/terraform-exec@v0.1.1` ([#28](https://github.com/hashicorp/terraform-plugin-test/pull/28)) + +# 1.4.1 (July 7, 2020) + +BUG FIXES: + + - Fix auto-install Terraform feature ([#26](https://github.com/hashicorp/terraform-plugin-test/pull/26)) diff --git a/vendor/github.com/hashicorp/terraform-plugin-test/README.md b/vendor/github.com/hashicorp/terraform-plugin-test/README.md index 48abe71e..4f1d5487 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-test/README.md +++ b/vendor/github.com/hashicorp/terraform-plugin-test/README.md @@ -1,3 +1,6 @@ +**ARCHIVED: This project has been merged into [terraform-plugin-sdk](github.com/hashicorp/terraform-plugin-sdk) as the `plugintest` package.** + + # Terraform Plugin Test Helper Library This is an **experimental** library for testing Terraform plugins in their diff --git a/vendor/github.com/hashicorp/terraform-plugin-test/config.go b/vendor/github.com/hashicorp/terraform-plugin-test/config.go index a40460e9..e9c4d58c 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-test/config.go +++ b/vendor/github.com/hashicorp/terraform-plugin-test/config.go @@ -1,9 +1,12 @@ package tftest import ( + "context" "fmt" + "io/ioutil" "os" - "path/filepath" + + "github.com/hashicorp/terraform-exec/tfinstall" ) // Config is used to configure the test helper. In most normal test programs @@ -11,50 +14,41 @@ import ( // DiscoverConfig, but this is exposed so that more complex scenarios can be // implemented by direct configuration. type Config struct { - PluginName string SourceDir string TerraformExec string - CurrentPluginExec string + execTempDir string PreviousPluginExec string } // DiscoverConfig uses environment variables and other means to automatically // discover a reasonable test helper configuration. -func DiscoverConfig(pluginName string, sourceDir string) (*Config, error) { - var tfExec string - var err error +func DiscoverConfig(sourceDir string) (*Config, error) { tfVersion := os.Getenv("TF_ACC_TERRAFORM_VERSION") - if tfVersion == "" { - tfExec = FindTerraform() - if tfExec == "" { - return nil, fmt.Errorf("unable to find 'terraform' executable for testing; either place it in PATH or set TF_ACC_TERRAFORM_PATH explicitly to a direct executable path") - } - } else { - tfExec, err = InstallTerraform(tfVersion) - if err != nil { - return nil, fmt.Errorf("could not install Terraform version %s: %s", tfVersion, err) - } - } + tfPath := os.Getenv("TF_ACC_TERRAFORM_PATH") - prevExec := os.Getenv("TF_ACC_PREVIOUS_EXEC") - if prevExec != "" { - if info, err := os.Stat(prevExec); err != nil { - return nil, fmt.Errorf("TF_ACC_PREVIOUS_EXEC of %s cannot be used: %s", prevExec, err) - } else if info.IsDir() { - return nil, fmt.Errorf("TF_ACC_PREVIOUS_EXEC of %s is directory, not file", prevExec) - } + tempDir := os.Getenv("TF_ACC_TEMP_DIR") + tfDir, err := ioutil.TempDir(tempDir, "tftest-terraform") + if err != nil { + return nil, fmt.Errorf("failed to create temp dir: %w", err) } - absPluginExecPath, err := filepath.Abs(os.Args[0]) + finders := []tfinstall.ExecPathFinder{} + switch { + case tfPath != "": + finders = append(finders, tfinstall.ExactPath(tfPath)) + case tfVersion != "": + finders = append(finders, tfinstall.ExactVersion(tfVersion, tfDir)) + default: + finders = append(finders, tfinstall.LookPath(), tfinstall.LatestVersion(tfDir, true)) + } + tfExec, err := tfinstall.Find(context.Background(), finders...) if err != nil { - return nil, fmt.Errorf("could not resolve plugin exec path %s: %s", os.Args[0], err) + return nil, err } return &Config{ - PluginName: pluginName, - SourceDir: sourceDir, - TerraformExec: tfExec, - CurrentPluginExec: absPluginExecPath, - PreviousPluginExec: os.Getenv("TF_ACC_PREVIOUS_EXEC"), + SourceDir: sourceDir, + TerraformExec: tfExec, + execTempDir: tfDir, }, nil } diff --git a/vendor/github.com/hashicorp/terraform-plugin-test/go.mod b/vendor/github.com/hashicorp/terraform-plugin-test/go.mod index 1c3c29a1..53934907 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-test/go.mod +++ b/vendor/github.com/hashicorp/terraform-plugin-test/go.mod @@ -1,8 +1,9 @@ -module github.com/hashicorp/terraform-plugin-test +module github.com/hashicorp/terraform-plugin-test/v2 go 1.12 require ( - github.com/hashicorp/go-getter v1.4.0 - github.com/hashicorp/terraform-json v0.4.0 + github.com/hashicorp/go-getter v1.5.3 + github.com/hashicorp/terraform-exec v0.13.3 + github.com/hashicorp/terraform-json v0.10.0 ) diff --git a/vendor/github.com/hashicorp/terraform-plugin-test/go.sum b/vendor/github.com/hashicorp/terraform-plugin-test/go.sum index 9d69283a..2a318eb1 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-test/go.sum +++ b/vendor/github.com/hashicorp/terraform-plugin-test/go.sum @@ -9,16 +9,49 @@ cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbf cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/andybalholm/crlf v0.0.0-20171020200849-670099aa064f/go.mod h1:k8feO4+kXDxro6ErPXBRTJ/ro2mf0SsFG8s7doP9kJE= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= +github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v1.15.78 h1:LaXy6lWR0YK7LKyuU0QWy2ws/LWTPfYV/UgfiBu4tvY= github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.1.0 h1:4pl5BV4o7ZG/lterP4S6WzJ6xr49Ba5ET9ygheTYahk= +github.com/go-git/go-billy/v5 v5.1.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M= +github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= +github.com/go-git/go-git/v5 v5.3.0 h1:8WKMtJR2j8RntEXR/uvTKagfEt4GYlwQ7mntE4+0GWc= +github.com/go-git/go-git/v5 v5.3.0/go.mod h1:xdX4bWJ48aOrdhnl2XqHYstHbbp6+LFS4r4X+lNVprw= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -27,63 +60,122 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= +github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-getter v1.4.0 h1:ENHNi8494porjD0ZhIrjlAHnveSFhY7hvOJrV/fsKkw= -github.com/hashicorp/go-getter v1.4.0/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-getter v1.5.3 h1:NF5+zOlQegim+w/EUhSLh6QhXHmZMEeHLQzllkQ3ROU= +github.com/hashicorp/go-getter v1.5.3/go.mod h1:BrrV/1clo8cCYu6mxvboYg+KutTiFnXjMEgDD8+i7ZI= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= -github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0= +github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= +github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/terraform-json v0.4.0 h1:KNh29iNxozP5adfUFBJ4/fWd0Cu3taGgjHB38JYqOF4= -github.com/hashicorp/terraform-json v0.4.0/go.mod h1:eAbqb4w0pSlRmdvl8fOyHAi/+8jnkVYN28gJkSJrLhU= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/terraform-exec v0.13.3 h1:R6L2mNpDGSEqtLrSONN8Xth0xYwNrnEVzDz6LF/oJPk= +github.com/hashicorp/terraform-exec v0.13.3/go.mod h1:SSg6lbUsVB3DmFyCPjBPklqf6EYGX0TlQ6QTxOlikDU= +github.com/hashicorp/terraform-json v0.10.0 h1:9syPD/Y5t+3uFjG8AiWVPu1bklJD8QB8iTCaJASc8oQ= +github.com/hashicorp/terraform-json v0.10.0/go.mod h1:3defM4kkMfttwiE7VakJDwCd4R+umhSQnvJwORXbprE= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/klauspost/compress v1.11.2 h1:MiK62aErc3gIiVEtyzKfeOHgW7atJb5g/KNX5m3c2nQ= +github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= +github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= -github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= +github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/zclconf/go-cty v1.2.1 h1:vGMsygfmeCl4Xb6OA5U5XVAaQZ69FvoG7X2jUtQujb8= +github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= github.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= +github.com/zclconf/go-cty v1.8.2 h1:u+xZfBKgpycDnTNjPhGiTEYZS5qS/Sb5MqSfm7vzcjg= +github.com/zclconf/go-cty v1.8.2/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= +github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= @@ -103,8 +195,11 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= @@ -115,19 +210,28 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492 h1:Paq34FxTluEPvVyayQqMPgHm+vTOrIifmcYxFBx9TLg= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -140,6 +244,8 @@ golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -148,8 +254,9 @@ google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEn google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -162,8 +269,21 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/vendor/github.com/hashicorp/terraform-plugin-test/guard.go b/vendor/github.com/hashicorp/terraform-plugin-test/guard.go index a8fe5630..819937b3 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-test/guard.go +++ b/vendor/github.com/hashicorp/terraform-plugin-test/guard.go @@ -50,22 +50,6 @@ func LongTest(t TestControl) { } } -// RequirePreviousVersion is a test guard that will produce a log and call -// SkipNow on the given TestControl if the receiving Helper does not have a -// previous plugin version to test against. -// -// Call this immediately at the start of any "upgrade test" that expects to -// be able to run some operations with a previous version of the plugin before -// "upgrading" to the current version under test to continue with other -// operations. -func (h *Helper) RequirePreviousVersion(t TestControl) { - t.Helper() - if !h.HasPreviousVersion() { - t.Log("no previous plugin version available") - t.SkipNow() - } -} - // TestControl is an interface requiring a subset of *testing.T which is used // by the test guards and helpers in this package. Most callers can simply // pass their *testing.T value here, but the interface allows other diff --git a/vendor/github.com/hashicorp/terraform-plugin-test/helper.go b/vendor/github.com/hashicorp/terraform-plugin-test/helper.go index d4de4468..06b8f76b 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-test/helper.go +++ b/vendor/github.com/hashicorp/terraform-plugin-test/helper.go @@ -9,6 +9,7 @@ import ( "strings" getter "github.com/hashicorp/go-getter" + "github.com/hashicorp/terraform-exec/tfexec" ) const subprocessCurrentSigil = "4acd63807899403ca4859f5bb948d2c6" @@ -23,8 +24,8 @@ const subprocessPreviousSigil = "2279afb8cf71423996be1fd65d32f13b" // available for upgrade tests, and then will return an object containing the // results of that initialization which can then be stored in a global variable // for use in other tests. -func AutoInitProviderHelper(name string, sourceDir string) *Helper { - helper, err := AutoInitHelper("terraform-provider-"+name, sourceDir) +func AutoInitProviderHelper(sourceDir string) *Helper { + helper, err := AutoInitHelper(sourceDir) if err != nil { fmt.Fprintf(os.Stderr, "cannot run Terraform provider tests: %s\n", err) os.Exit(1) @@ -39,10 +40,12 @@ type Helper struct { // sourceDir is the dir containing the provider source code, needed // for tests that use fixture files. - sourceDir string - pluginName string - terraformExec string - thisPluginDir, prevPluginDir string + sourceDir string + terraformExec string + + // execTempDir is created during DiscoverConfig to store any downloaded + // binaries + execTempDir string } // AutoInitHelper uses the auto-discovery behavior of DiscoverConfig to prepare @@ -50,8 +53,8 @@ type Helper struct { // way to get the standard init behavior based on environment variables, and // callers should use this unless they have an unusual requirement that calls // for constructing a config in a different way. -func AutoInitHelper(pluginName string, sourceDir string) (*Helper, error) { - config, err := DiscoverConfig(pluginName, sourceDir) +func AutoInitHelper(sourceDir string) (*Helper, error) { + config, err := DiscoverConfig(sourceDir) if err != nil { return nil, err } @@ -70,54 +73,16 @@ func AutoInitHelper(pluginName string, sourceDir string) (*Helper, error) { // automatically clean those up. func InitHelper(config *Config) (*Helper, error) { tempDir := os.Getenv("TF_ACC_TEMP_DIR") - baseDir, err := ioutil.TempDir(tempDir, "tftest-"+config.PluginName) + baseDir, err := ioutil.TempDir(tempDir, "tftest") if err != nil { return nil, fmt.Errorf("failed to create temporary directory for test helper: %s", err) } - var thisPluginDir, prevPluginDir string - if config.CurrentPluginExec != "" { - thisPluginDir, err = ioutil.TempDir(baseDir, "plugins-current") - if err != nil { - return nil, fmt.Errorf("failed to create temporary directory for -plugin-dir: %s", err) - } - currentExecPath := filepath.Join(thisPluginDir, config.PluginName) - err = symlinkFile(config.CurrentPluginExec, currentExecPath) - if err != nil { - return nil, fmt.Errorf("failed to create symlink at %s to %s: %s", currentExecPath, config.CurrentPluginExec, err) - } - - err = symlinkAuxiliaryProviders(thisPluginDir) - if err != nil { - return nil, fmt.Errorf("failed to symlink auxiliary providers: %s", err) - } - } else { - return nil, fmt.Errorf("CurrentPluginExec is not set") - } - if config.PreviousPluginExec != "" { - prevPluginDir, err = ioutil.TempDir(baseDir, "plugins-previous") - if err != nil { - return nil, fmt.Errorf("failed to create temporary directory for previous -plugin-dir: %s", err) - } - prevExecPath := filepath.Join(prevPluginDir, config.PluginName) - err = symlinkFile(config.PreviousPluginExec, prevExecPath) - if err != nil { - return nil, fmt.Errorf("failed to create symlink at %s to %s: %s", prevExecPath, config.PreviousPluginExec, err) - } - - err = symlinkAuxiliaryProviders(prevPluginDir) - if err != nil { - return nil, fmt.Errorf("failed to symlink auxiliary providers: %s", err) - } - } - return &Helper{ baseDir: baseDir, sourceDir: config.SourceDir, - pluginName: config.PluginName, terraformExec: config.TerraformExec, - thisPluginDir: thisPluginDir, - prevPluginDir: prevPluginDir, + execTempDir: config.execTempDir, }, nil } @@ -181,7 +146,7 @@ func symlinkAuxiliaryProviders(pluginDir string) error { if filenameExt == ".zip" { _, err = os.Stat(path) if os.IsNotExist(err) { - zipDecompressor.Decompress(path, filepath.Join(auxiliaryProviderDir, filename), false) + zipDecompressor.Decompress(path, filepath.Join(auxiliaryProviderDir, filename), false, 0) } else if err != nil { return fmt.Errorf("Unexpected error: %s", err) } @@ -202,6 +167,12 @@ func symlinkAuxiliaryProviders(pluginDir string) error { // Call this before returning from TestMain to minimize the amount of detritus // left behind in the filesystem after the tests complete. func (h *Helper) Close() error { + if h.execTempDir != "" { + err := os.RemoveAll(h.execTempDir) + if err != nil { + return err + } + } return os.RemoveAll(h.baseDir) } @@ -217,22 +188,23 @@ func (h *Helper) NewWorkingDir() (*WorkingDir, error) { return nil, err } - // symlink the provider source files into the base directory - err = symlinkDir(h.sourceDir, dir) + // symlink the provider source files into the config directory + // e.g. testdata + err = symlinkDirectoriesOnly(h.sourceDir, dir) if err != nil { return nil, err } - // symlink the provider binaries into the base directory - err = symlinkDir(h.thisPluginDir, dir) + tf, err := tfexec.NewTerraform(dir, h.terraformExec) if err != nil { return nil, err } return &WorkingDir{ - h: h, - baseArgs: []string{"-no-color"}, - baseDir: dir, + h: h, + tf: tf, + baseDir: dir, + terraformExec: h.terraformExec, }, nil } @@ -251,35 +223,8 @@ func (h *Helper) RequireNewWorkingDir(t TestControl) *WorkingDir { return wd } -// HasPreviousVersion returns true if and only if the receiving helper has a -// previous plugin version available for use in tests. -func (h *Helper) HasPreviousVersion() bool { - return h.prevPluginDir != "" -} - // TerraformExecPath returns the location of the Terraform CLI executable that // should be used when running tests. func (h *Helper) TerraformExecPath() string { return h.terraformExec } - -// PluginDir returns the directory that should be used as the -plugin-dir when -// running "terraform init" in order to make Terraform detect the current -// version of the plugin. -func (h *Helper) PluginDir() string { - return h.thisPluginDir -} - -// PreviousPluginDir returns the directory that should be used as the -plugin-dir -// when running "terraform init" in order to make Terraform detect the previous -// version of the plugin, if available. -// -// If no previous version is available, this method will panic. Use -// RequirePreviousVersion or HasPreviousVersion to ensure a previous version is -// available before calling this. -func (h *Helper) PreviousPluginDir() string { - if h.prevPluginDir != "" { - panic("PreviousPluginDir not available") - } - return h.prevPluginDir -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-test/terraform.go b/vendor/github.com/hashicorp/terraform-plugin-test/terraform.go deleted file mode 100644 index d2f118e1..00000000 --- a/vendor/github.com/hashicorp/terraform-plugin-test/terraform.go +++ /dev/null @@ -1,163 +0,0 @@ -package tftest - -import ( - "bytes" - "encoding/json" - "fmt" - getter "github.com/hashicorp/go-getter" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "runtime" - "strings" -) - -const releaseHost = "https://releases.hashicorp.com" - -// FindTerraform attempts to find a Terraform CLI executable for plugin testing. -// -// As a first preference it will look for the environment variable -// TF_ACC_TERRAFORM_PATH and return its value. If that variable is not set, it will -// look in PATH for a program named "terraform" and, if one is found, return -// its absolute path. -// -// If no Terraform executable can be found, the result is the empty string. In -// that case, the test program will usually fail outright. -func FindTerraform() string { - if p := os.Getenv("TF_ACC_TERRAFORM_PATH"); p != "" { - return p - } - p, err := exec.LookPath("terraform") - if err != nil { - return "" - } - return p -} - -func tfURL(version, osName, archName string) string { - return fmt.Sprintf( - "%s/terraform/%s/terraform_%s_%s_%s.zip", - releaseHost, version, version, osName, archName, - ) -} - -// InstallTerraform downloads and decompresses a Terraform CLI executable with -// the specified version, downloaded from the HashiCorp releases page over HTTP. -// -// The version string must match an existing Terraform release semver version, -// e.g. 0.12.5. -// -// The terraform executable is installed to a temporary folder. -// -// FIXME: Temporary folder should be cleaned up after tests have finished. -func InstallTerraform(tfVersion string) (string, error) { - osName := runtime.GOOS - archName := runtime.GOARCH - - var tfDir string - var err error - - tempDir := os.Getenv("TF_ACC_TEMP_DIR") - tfDir, err = ioutil.TempDir(tempDir, "tftest-terraform") - if err != nil { - return "", fmt.Errorf("failed to create temp dir: %s", err) - } - - url := tfURL(tfVersion, osName, archName) - - client := getter.Client{ - Src: url, - Dst: tfDir, - - Mode: getter.ClientModeDir, - } - - err = client.Get() - if err != nil { - return "", fmt.Errorf("failed to download terraform from %s: %s", url, err) - } - - return filepath.Join(tfDir, "terraform"), nil -} - -// getTerraformEnv returns the appropriate Env for the Terraform command. -func getTerraformEnv() []string { - var env []string - for _, e := range os.Environ() { - env = append(env, e) - } - - env = append(env, "TF_DISABLE_PLUGIN_TLS=1") - env = append(env, "TF_SKIP_PROVIDER_VERIFY=1") - - // FIXME: Ideally in testing.Verbose mode we'd turn on Terraform DEBUG - // logging, perhaps redirected to a separate fd other than stderr to avoid - // polluting it, and then propagate the log lines out into t.Log so that - // they are visible to the person running the test. Currently though, - // Terraform CLI is able to send logs only to either an on-disk file or - // to stderr. - env = append(env, "TF_LOG=") // so logging can't pollute our stderr output - env = append(env, "TF_INPUT=0") - - if p := os.Getenv("TF_ACC_LOG_PATH"); p != "" { - env = append(env, "TF_LOG=TRACE") - env = append(env, "TF_LOG_PATH="+p) - } - return env -} - -// RunTerraform runs the configured Terraform CLI executable with the given -// arguments, returning an error if it produces a non-successful exit status. -func (wd *WorkingDir) runTerraform(args ...string) error { - allArgs := []string{"terraform"} - allArgs = append(allArgs, args...) - - env := getTerraformEnv() - - var errBuf strings.Builder - - cmd := &exec.Cmd{ - Path: wd.h.TerraformExecPath(), - Args: allArgs, - Dir: wd.baseDir, - Stderr: &errBuf, - Env: env, - } - err := cmd.Run() - if tErr, ok := err.(*exec.ExitError); ok { - err = fmt.Errorf("terraform failed: %s\n\nstderr:\n%s", tErr.ProcessState.String(), errBuf.String()) - } - return err -} - -// runTerraformJSON runs the configured Terraform CLI executable with the given -// arguments and tries to decode its stdout into the given target value (which -// must be a non-nil pointer) as JSON. -func (wd *WorkingDir) runTerraformJSON(target interface{}, args ...string) error { - allArgs := []string{"terraform"} - allArgs = append(allArgs, args...) - - env := getTerraformEnv() - - var outBuf bytes.Buffer - var errBuf strings.Builder - - cmd := &exec.Cmd{ - Path: wd.h.TerraformExecPath(), - Args: allArgs, - Dir: wd.baseDir, - Stderr: &errBuf, - Stdout: &outBuf, - Env: env, - } - err := cmd.Run() - if err != nil { - if tErr, ok := err.(*exec.ExitError); ok { - err = fmt.Errorf("terraform failed: %s\n\nstderr:\n%s", tErr.ProcessState.String(), errBuf.String()) - } - return err - } - - return json.Unmarshal(outBuf.Bytes(), target) -} diff --git a/vendor/github.com/hashicorp/terraform-plugin-test/util.go b/vendor/github.com/hashicorp/terraform-plugin-test/util.go index 0732c82d..57bc84f2 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-test/util.go +++ b/vendor/github.com/hashicorp/terraform-plugin-test/util.go @@ -53,3 +53,43 @@ func symlinkDir(srcDir string, destDir string) (err error) { } return } + +// symlinkDirectoriesOnly finds only the first-level child directories in srcDir +// and symlinks them into destDir. +// Unlike symlinkDir, this is done non-recursively in order to limit the number +// of file descriptors used. +func symlinkDirectoriesOnly(srcDir string, destDir string) (err error) { + srcInfo, err := os.Stat(srcDir) + if err != nil { + return err + } + + err = os.MkdirAll(destDir, srcInfo.Mode()) + if err != nil { + return err + } + + directory, err := os.Open(srcDir) + if err != nil { + return err + } + defer directory.Close() + objects, err := directory.Readdir(-1) + if err != nil { + return err + } + + for _, obj := range objects { + srcPath := filepath.Join(srcDir, obj.Name()) + destPath := filepath.Join(destDir, obj.Name()) + + if obj.IsDir() { + err = symlinkFile(srcPath, destPath) + if err != nil { + return err + } + } + + } + return +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-test/working_dir.go b/vendor/github.com/hashicorp/terraform-plugin-test/working_dir.go index 7f64706f..bcf4c5a0 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-test/working_dir.go +++ b/vendor/github.com/hashicorp/terraform-plugin-test/working_dir.go @@ -1,14 +1,23 @@ package tftest import ( + "bytes" + "context" + "errors" "fmt" "io/ioutil" "os" "path/filepath" + "github.com/hashicorp/terraform-exec/tfexec" tfjson "github.com/hashicorp/terraform-json" ) +const ( + ConfigFileName = "terraform_plugin_test.tf" + PlanFileName = "tfplan" +) + // WorkingDir represents a distinct working directory that can be used for // running tests. Each test should construct its own WorkingDir by calling // NewWorkingDir or RequireNewWorkingDir on its package's singleton @@ -22,8 +31,17 @@ type WorkingDir struct { // baseArgs is arguments that should be appended to all commands baseArgs []string - // configDir contains the singular config file generated for each test - configDir string + // tf is the instance of tfexec.Terraform used for running Terraform commands + tf *tfexec.Terraform + + // terraformExec is a path to a terraform binary, inherited from Helper + terraformExec string + + // reattachInfo stores the gRPC socket info required for Terraform's + // plugin reattach functionality + reattachInfo tfexec.ReattachInfo + + env map[string]string } // Close deletes the directories and files created to represent the receiving @@ -33,26 +51,57 @@ func (wd *WorkingDir) Close() error { return os.RemoveAll(wd.baseDir) } +// Setenv sets an environment variable on the WorkingDir. +func (wd *WorkingDir) Setenv(envVar, val string) { + if wd.env == nil { + wd.env = map[string]string{} + } + wd.env[envVar] = val +} + +// Unsetenv removes an environment variable from the WorkingDir. +func (wd *WorkingDir) Unsetenv(envVar string) { + delete(wd.env, envVar) +} + +func (wd *WorkingDir) SetReattachInfo(reattachInfo tfexec.ReattachInfo) { + wd.reattachInfo = reattachInfo +} + +func (wd *WorkingDir) UnsetReattachInfo() { + wd.reattachInfo = nil +} + +// GetHelper returns the Helper set on the WorkingDir. +func (wd *WorkingDir) GetHelper() *Helper { + return wd.h +} + // SetConfig sets a new configuration for the working directory. // // This must be called at least once before any call to Init, Plan, Apply, or // Destroy to establish the configuration. Any previously-set configuration is // discarded and any saved plan is cleared. func (wd *WorkingDir) SetConfig(cfg string) error { - // Each call to SetConfig creates a new directory under our baseDir. - // We create them within so that our final cleanup step will delete them - // automatically without any additional tracking. - configDir, err := ioutil.TempDir(wd.baseDir, "config") + configFilename := filepath.Join(wd.baseDir, ConfigFileName) + err := ioutil.WriteFile(configFilename, []byte(cfg), 0700) if err != nil { return err } - configFilename := filepath.Join(configDir, "terraform_plugin_test.tf") - err = ioutil.WriteFile(configFilename, []byte(cfg), 0700) - if err != nil { + + var mismatch *tfexec.ErrVersionMismatch + err = wd.tf.SetDisablePluginTLS(true) + if err != nil && !errors.As(err, &mismatch) { + return err + } + err = wd.tf.SetSkipProviderVerify(true) + if err != nil && !errors.As(err, &mismatch) { return err } - wd.configDir = configDir + if p := os.Getenv("TF_ACC_LOG_PATH"); p != "" { + wd.tf.SetLogPath(p) + } // Changing configuration invalidates any saved plan. err = wd.ClearPlan() @@ -113,19 +162,18 @@ func (wd *WorkingDir) RequireClearPlan(t TestControl) { } } -func (wd *WorkingDir) init(pluginDir string) error { - args := []string{"init"} - args = append(args, wd.baseArgs...) - return wd.runTerraform(args...) -} - // Init runs "terraform init" for the given working directory, forcing Terraform // to use the current version of the plugin under test. func (wd *WorkingDir) Init() error { - if wd.configDir == "" { + if _, err := os.Stat(wd.configFilename()); err != nil { return fmt.Errorf("must call SetConfig before Init") } - return wd.init(wd.h.PluginDir()) + + return wd.tf.Init(context.Background(), tfexec.Reattach(wd.reattachInfo)) +} + +func (wd *WorkingDir) configFilename() string { + return filepath.Join(wd.baseDir, ConfigFileName) } // RequireInit is a variant of Init that will fail the test via the given @@ -138,40 +186,15 @@ func (wd *WorkingDir) RequireInit(t TestControl) { } } -// InitPrevious runs "terraform init" for the given working directory, forcing -// Terraform to use the previous version of the plugin under test. -// -// This method will panic if no previous plugin version is available. Use -// HasPreviousVersion or RequirePreviousVersion on the test helper singleton -// to check this first. -func (wd *WorkingDir) InitPrevious() error { - if wd.configDir == "" { - return fmt.Errorf("must call SetConfig before InitPrevious") - } - return wd.init(wd.h.PreviousPluginDir()) -} - -// RequireInitPrevious is a variant of InitPrevious that will fail the test -// via the given TestControl if init fails. -func (wd *WorkingDir) RequireInitPrevious(t TestControl) { - t.Helper() - if err := wd.InitPrevious(); err != nil { - t := testingT{t} - t.Fatalf("init failed: %s", err) - } -} - func (wd *WorkingDir) planFilename() string { - return filepath.Join(wd.baseDir, "tfplan") + return filepath.Join(wd.baseDir, PlanFileName) } // CreatePlan runs "terraform plan" to create a saved plan file, which if successful // will then be used for the next call to Apply. func (wd *WorkingDir) CreatePlan() error { - args := []string{"plan", "-refresh=false"} - args = append(args, wd.baseArgs...) - args = append(args, "-out=tfplan", wd.configDir) - return wd.runTerraform(args...) + _, err := wd.tf.Plan(context.Background(), tfexec.Reattach(wd.reattachInfo), tfexec.Refresh(false), tfexec.Out(PlanFileName)) + return err } // RequireCreatePlan is a variant of CreatePlan that will fail the test via @@ -184,22 +207,24 @@ func (wd *WorkingDir) RequireCreatePlan(t TestControl) { } } +// CreateDestroyPlan runs "terraform plan -destroy" to create a saved plan +// file, which if successful will then be used for the next call to Apply. +func (wd *WorkingDir) CreateDestroyPlan() error { + _, err := wd.tf.Plan(context.Background(), tfexec.Reattach(wd.reattachInfo), tfexec.Refresh(false), tfexec.Out(PlanFileName), tfexec.Destroy(true)) + return err +} + // Apply runs "terraform apply". If CreatePlan has previously completed // successfully and the saved plan has not been cleared in the meantime then // this will apply the saved plan. Otherwise, it will implicitly create a new // plan and apply it. func (wd *WorkingDir) Apply() error { - args := []string{"apply", "-refresh=false"} - args = append(args, wd.baseArgs...) - + args := []tfexec.ApplyOption{tfexec.Reattach(wd.reattachInfo), tfexec.Refresh(false)} if wd.HasSavedPlan() { - args = append(args, "tfplan") - } else { - args = append(args, "-auto-approve") - args = append(args, wd.configDir) + args = append(args, tfexec.DirOrPlan(PlanFileName)) } - return wd.runTerraform(args...) + return wd.tf.Apply(context.Background(), args...) } // RequireApply is a variant of Apply that will fail the test via @@ -218,11 +243,7 @@ func (wd *WorkingDir) RequireApply(t TestControl) { // If destroy fails then remote objects might still exist, and continue to // exist after a particular test is concluded. func (wd *WorkingDir) Destroy() error { - args := []string{"destroy", "-refresh=false"} - args = append(args, wd.baseArgs...) - - args = append(args, "-auto-approve", wd.configDir) - return wd.runTerraform(args...) + return wd.tf.Destroy(context.Background(), tfexec.Reattach(wd.reattachInfo), tfexec.Refresh(false)) } // RequireDestroy is a variant of Destroy that will fail the test via @@ -255,18 +276,7 @@ func (wd *WorkingDir) SavedPlan() (*tfjson.Plan, error) { return nil, fmt.Errorf("there is no current saved plan") } - var ret tfjson.Plan - - args := []string{"show"} - args = append(args, wd.baseArgs...) - args = append(args, "-json", wd.planFilename()) - - err := wd.runTerraformJSON(&ret, args...) - if err != nil { - return nil, err - } - - return &ret, nil + return wd.tf.ShowPlanFile(context.Background(), wd.planFilename(), tfexec.Reattach(wd.reattachInfo)) } // RequireSavedPlan is a variant of SavedPlan that will fail the test via @@ -281,22 +291,44 @@ func (wd *WorkingDir) RequireSavedPlan(t TestControl) *tfjson.Plan { return ret } -// State returns an object describing the current state. +// SavedPlanStdout returns a stdout capture of the current saved plan file, if any. // -// If the state cannot be read, State returns an error. -func (wd *WorkingDir) State() (*tfjson.State, error) { - var ret tfjson.State +// If no plan is saved or if the plan file cannot be read, SavedPlanStdout returns +// an error. +func (wd *WorkingDir) SavedPlanStdout() (string, error) { + if !wd.HasSavedPlan() { + return "", fmt.Errorf("there is no current saved plan") + } + + var ret bytes.Buffer - args := []string{"show"} - args = append(args, wd.baseArgs...) - args = append(args, "-json") + wd.tf.SetStdout(&ret) + defer wd.tf.SetStdout(ioutil.Discard) + _, err := wd.tf.ShowPlanFileRaw(context.Background(), wd.planFilename(), tfexec.Reattach(wd.reattachInfo)) + if err != nil { + return "", err + } - err := wd.runTerraformJSON(&ret, args...) + return ret.String(), nil +} + +// RequireSavedPlanStdout is a variant of SavedPlanStdout that will fail the test via +// the given TestControl if the plan cannot be read. +func (wd *WorkingDir) RequireSavedPlanStdout(t TestControl) string { + t.Helper() + ret, err := wd.SavedPlanStdout() if err != nil { - return nil, err + t := testingT{t} + t.Fatalf("failed to read saved plan: %s", err) } + return ret +} - return &ret, nil +// State returns an object describing the current state. +// +// If the state cannot be read, State returns an error. +func (wd *WorkingDir) State() (*tfjson.State, error) { + return wd.tf.Show(context.Background(), tfexec.Reattach(wd.reattachInfo)) } // RequireState is a variant of State that will fail the test via @@ -313,10 +345,7 @@ func (wd *WorkingDir) RequireState(t TestControl) *tfjson.State { // Import runs terraform import func (wd *WorkingDir) Import(resource, id string) error { - args := []string{"import"} - args = append(args, wd.baseArgs...) - args = append(args, "-config="+wd.configDir, resource, id) - return wd.runTerraform(args...) + return wd.tf.Import(context.Background(), resource, id, tfexec.Config(wd.baseDir), tfexec.Reattach(wd.reattachInfo)) } // RequireImport is a variant of Import that will fail the test via @@ -331,11 +360,7 @@ func (wd *WorkingDir) RequireImport(t TestControl, resource, id string) { // Refresh runs terraform refresh func (wd *WorkingDir) Refresh() error { - args := []string{"refresh"} - args = append(args, wd.baseArgs...) - args = append(args, "-state="+filepath.Join(wd.baseDir, "terraform.tfstate")) - args = append(args, wd.configDir) - return wd.runTerraform(args...) + return wd.tf.Refresh(context.Background(), tfexec.Reattach(wd.reattachInfo), tfexec.State(filepath.Join(wd.baseDir, "terraform.tfstate"))) } // RequireRefresh is a variant of Refresh that will fail the test via @@ -352,15 +377,7 @@ func (wd *WorkingDir) RequireRefresh(t TestControl) { // // If the schemas cannot be read, Schemas returns an error. func (wd *WorkingDir) Schemas() (*tfjson.ProviderSchemas, error) { - args := []string{"providers", wd.configDir, "schema"} - - var ret tfjson.ProviderSchemas - err := wd.runTerraformJSON(&ret, args...) - if err != nil { - return nil, err - } - - return &ret, nil + return wd.tf.ProvidersSchema(context.Background()) } // RequireSchemas is a variant of Schemas that will fail the test via diff --git a/vendor/github.com/hashicorp/terraform-svchost/LICENSE b/vendor/github.com/hashicorp/terraform-svchost/LICENSE new file mode 100644 index 00000000..82b4de97 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-svchost/LICENSE @@ -0,0 +1,353 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control” means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible + With Secondary Licenses”, as defined by + the Mozilla Public License, v. 2.0. diff --git a/vendor/github.com/hashicorp/terraform-svchost/auth/helper_program_test.go b/vendor/github.com/hashicorp/terraform-svchost/auth/helper_program_test.go new file mode 100644 index 00000000..65ce49e4 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-svchost/auth/helper_program_test.go @@ -0,0 +1,83 @@ +package auth + +import ( + "os" + "path/filepath" + "testing" + + "github.com/hashicorp/terraform-svchost" +) + +func TestHelperProgramCredentialsSource(t *testing.T) { + wd, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + + program := filepath.Join(wd, "testdata/test-helper") + t.Logf("testing with helper at %s", program) + + src := HelperProgramCredentialsSource(program) + + t.Run("happy path", func(t *testing.T) { + creds, err := src.ForHost(svchost.Hostname("example.com")) + if err != nil { + t.Fatal(err) + } + if tokCreds, isTok := creds.(HostCredentialsToken); isTok { + if got, want := string(tokCreds), "example-token"; got != want { + t.Errorf("wrong token %q; want %q", got, want) + } + } else { + t.Errorf("wrong type of credentials %T", creds) + } + }) + t.Run("no credentials", func(t *testing.T) { + creds, err := src.ForHost(svchost.Hostname("nothing.example.com")) + if err != nil { + t.Fatal(err) + } + if creds != nil { + t.Errorf("got credentials; want nil") + } + }) + t.Run("unsupported credentials type", func(t *testing.T) { + creds, err := src.ForHost(svchost.Hostname("other-cred-type.example.com")) + if err != nil { + t.Fatal(err) + } + if creds != nil { + t.Errorf("got credentials; want nil") + } + }) + t.Run("lookup error", func(t *testing.T) { + _, err := src.ForHost(svchost.Hostname("fail.example.com")) + if err == nil { + t.Error("completed successfully; want error") + } + }) + t.Run("store happy path", func(t *testing.T) { + err := src.StoreForHost(svchost.Hostname("example.com"), HostCredentialsToken("example-token")) + if err != nil { + t.Fatal(err) + } + }) + t.Run("store error", func(t *testing.T) { + err := src.StoreForHost(svchost.Hostname("fail.example.com"), HostCredentialsToken("example-token")) + if err == nil { + t.Error("completed successfully; want error") + } + }) + t.Run("forget happy path", func(t *testing.T) { + err := src.ForgetForHost(svchost.Hostname("example.com")) + if err != nil { + t.Fatal(err) + } + }) + t.Run("forget error", func(t *testing.T) { + err := src.ForgetForHost(svchost.Hostname("fail.example.com")) + if err == nil { + t.Error("completed successfully; want error") + } + }) +} diff --git a/vendor/github.com/hashicorp/terraform-svchost/auth/static_test.go b/vendor/github.com/hashicorp/terraform-svchost/auth/static_test.go new file mode 100644 index 00000000..026a9abf --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-svchost/auth/static_test.go @@ -0,0 +1,38 @@ +package auth + +import ( + "testing" + + "github.com/hashicorp/terraform-svchost" +) + +func TestStaticCredentialsSource(t *testing.T) { + src := StaticCredentialsSource(map[svchost.Hostname]map[string]interface{}{ + svchost.Hostname("example.com"): map[string]interface{}{ + "token": "abc123", + }, + }) + + t.Run("exists", func(t *testing.T) { + creds, err := src.ForHost(svchost.Hostname("example.com")) + if err != nil { + t.Fatal(err) + } + if tokCreds, isToken := creds.(HostCredentialsToken); isToken { + if got, want := string(tokCreds), "abc123"; got != want { + t.Errorf("wrong token %q; want %q", got, want) + } + } else { + t.Errorf("creds is %#v; want HostCredentialsToken", creds) + } + }) + t.Run("does not exist", func(t *testing.T) { + creds, err := src.ForHost(svchost.Hostname("example.net")) + if err != nil { + t.Fatal(err) + } + if creds != nil { + t.Errorf("creds is %#v; want nil", creds) + } + }) +} diff --git a/vendor/github.com/hashicorp/terraform-svchost/auth/testdata/.gitignore b/vendor/github.com/hashicorp/terraform-svchost/auth/testdata/.gitignore new file mode 100644 index 00000000..ba2906d0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-svchost/auth/testdata/.gitignore @@ -0,0 +1 @@ +main diff --git a/vendor/github.com/hashicorp/terraform-svchost/auth/testdata/main.go b/vendor/github.com/hashicorp/terraform-svchost/auth/testdata/main.go new file mode 100644 index 00000000..1abd5fc4 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-svchost/auth/testdata/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" +) + +// This is a simple program that implements the "helper program" protocol +// for the svchost/auth package for unit testing purposes. + +func main() { + args := os.Args + + if len(args) < 3 { + die("not enough arguments\n") + } + + host := args[2] + switch args[1] { + case "get": + switch host { + case "example.com": + fmt.Print(`{"token":"example-token"}`) + case "other-cred-type.example.com": + fmt.Print(`{"username":"alfred"}`) // unrecognized by main program + case "fail.example.com": + die("failing because you told me to fail\n") + default: + fmt.Print("{}") // no credentials available + } + case "store": + dataSrc, err := ioutil.ReadAll(os.Stdin) + if err != nil { + die("invalid input: %s", err) + } + var data map[string]interface{} + err = json.Unmarshal(dataSrc, &data) + + switch host { + case "example.com": + if data["token"] != "example-token" { + die("incorrect token value to store") + } + default: + die("can't store credentials for %s", host) + } + case "forget": + switch host { + case "example.com": + // okay! + default: + die("can't forget credentials for %s", host) + } + default: + die("unknown subcommand %q\n", args[1]) + } +} + +func die(f string, args ...interface{}) { + fmt.Fprintf(os.Stderr, fmt.Sprintf(f, args...)) + os.Exit(1) +} diff --git a/vendor/github.com/hashicorp/terraform-svchost/auth/testdata/test-helper b/vendor/github.com/hashicorp/terraform-svchost/auth/testdata/test-helper new file mode 100755 index 00000000..0ed3396c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-svchost/auth/testdata/test-helper @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -eu + +cd "$( dirname "${BASH_SOURCE[0]}" )" +[ -x main ] || go build -o main . +exec ./main "$@" diff --git a/vendor/github.com/hashicorp/terraform-svchost/auth/token_credentials_test.go b/vendor/github.com/hashicorp/terraform-svchost/auth/token_credentials_test.go new file mode 100644 index 00000000..61f2c9bd --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-svchost/auth/token_credentials_test.go @@ -0,0 +1,31 @@ +package auth + +import ( + "net/http" + "testing" + + "github.com/zclconf/go-cty/cty" +) + +func TestHostCredentialsToken(t *testing.T) { + creds := HostCredentialsToken("foo-bar") + + { + req := &http.Request{} + creds.PrepareRequest(req) + authStr := req.Header.Get("authorization") + if got, want := authStr, "Bearer foo-bar"; got != want { + t.Errorf("wrong Authorization header value %q; want %q", got, want) + } + } + + { + got := creds.ToStore() + want := cty.ObjectVal(map[string]cty.Value{ + "token": cty.StringVal("foo-bar"), + }) + if !want.RawEquals(got) { + t.Errorf("wrong storable object value\ngot: %#v\nwant: %#v", got, want) + } + } +} diff --git a/vendor/github.com/hashicorp/terraform-svchost/disco/disco_test.go b/vendor/github.com/hashicorp/terraform-svchost/disco/disco_test.go new file mode 100644 index 00000000..f1cb7ecf --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-svchost/disco/disco_test.go @@ -0,0 +1,357 @@ +package disco + +import ( + "crypto/tls" + "net/http" + "net/http/httptest" + "net/url" + "os" + "strconv" + "testing" + + "github.com/hashicorp/terraform-svchost" + "github.com/hashicorp/terraform-svchost/auth" +) + +func TestMain(m *testing.M) { + // During all tests we override the HTTP transport we use for discovery + // so it'll tolerate the locally-generated TLS certificates we use + // for test URLs. + httpTransport = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + + os.Exit(m.Run()) +} + +func TestDiscover(t *testing.T) { + t.Run("happy path", func(t *testing.T) { + portStr, close := testServer(func(w http.ResponseWriter, r *http.Request) { + resp := []byte(` +{ +"thingy.v1": "http://example.com/foo", +"wotsit.v2": "http://example.net/bar" +} +`) + w.Header().Add("Content-Type", "application/json") + w.Header().Add("Content-Length", strconv.Itoa(len(resp))) + w.Write(resp) + }) + defer close() + + givenHost := "localhost" + portStr + host, err := svchost.ForComparison(givenHost) + if err != nil { + t.Fatalf("test server hostname is invalid: %s", err) + } + + d := New() + discovered, err := d.Discover(host) + if err != nil { + t.Fatalf("unexpected discovery error: %s", err) + } + + gotURL, err := discovered.ServiceURL("thingy.v1") + if err != nil { + t.Fatalf("unexpected service URL error: %s", err) + } + if gotURL == nil { + t.Fatalf("found no URL for thingy.v1") + } + if got, want := gotURL.String(), "http://example.com/foo"; got != want { + t.Fatalf("wrong result %q; want %q", got, want) + } + }) + t.Run("chunked encoding", func(t *testing.T) { + portStr, close := testServer(func(w http.ResponseWriter, r *http.Request) { + resp := []byte(` +{ +"thingy.v1": "http://example.com/foo", +"wotsit.v2": "http://example.net/bar" +} +`) + w.Header().Add("Content-Type", "application/json") + // We're going to force chunked encoding here -- and thus prevent + // the server from predicting the length -- so we can make sure + // our client is tolerant of servers using this encoding. + w.Write(resp[:5]) + w.(http.Flusher).Flush() + w.Write(resp[5:]) + w.(http.Flusher).Flush() + }) + defer close() + + givenHost := "localhost" + portStr + host, err := svchost.ForComparison(givenHost) + if err != nil { + t.Fatalf("test server hostname is invalid: %s", err) + } + + d := New() + discovered, err := d.Discover(host) + if err != nil { + t.Fatalf("unexpected discovery error: %s", err) + } + + gotURL, err := discovered.ServiceURL("wotsit.v2") + if err != nil { + t.Fatalf("unexpected service URL error: %s", err) + } + if gotURL == nil { + t.Fatalf("found no URL for wotsit.v2") + } + if got, want := gotURL.String(), "http://example.net/bar"; got != want { + t.Fatalf("wrong result %q; want %q", got, want) + } + }) + t.Run("with credentials", func(t *testing.T) { + var authHeaderText string + portStr, close := testServer(func(w http.ResponseWriter, r *http.Request) { + resp := []byte(`{}`) + authHeaderText = r.Header.Get("Authorization") + w.Header().Add("Content-Type", "application/json") + w.Header().Add("Content-Length", strconv.Itoa(len(resp))) + w.Write(resp) + }) + defer close() + + givenHost := "localhost" + portStr + host, err := svchost.ForComparison(givenHost) + if err != nil { + t.Fatalf("test server hostname is invalid: %s", err) + } + + d := New() + d.SetCredentialsSource(auth.StaticCredentialsSource(map[svchost.Hostname]map[string]interface{}{ + host: map[string]interface{}{ + "token": "abc123", + }, + })) + d.Discover(host) + if got, want := authHeaderText, "Bearer abc123"; got != want { + t.Fatalf("wrong Authorization header\ngot: %s\nwant: %s", got, want) + } + }) + t.Run("forced services override", func(t *testing.T) { + forced := map[string]interface{}{ + "thingy.v1": "http://example.net/foo", + "wotsit.v2": "/foo", + } + + d := New() + d.ForceHostServices(svchost.Hostname("example.com"), forced) + + givenHost := "example.com" + host, err := svchost.ForComparison(givenHost) + if err != nil { + t.Fatalf("test server hostname is invalid: %s", err) + } + + discovered, err := d.Discover(host) + if err != nil { + t.Fatalf("unexpected discovery error: %s", err) + } + { + gotURL, err := discovered.ServiceURL("thingy.v1") + if err != nil { + t.Fatalf("unexpected service URL error: %s", err) + } + if gotURL == nil { + t.Fatalf("found no URL for thingy.v1") + } + if got, want := gotURL.String(), "http://example.net/foo"; got != want { + t.Fatalf("wrong result %q; want %q", got, want) + } + } + { + gotURL, err := discovered.ServiceURL("wotsit.v2") + if err != nil { + t.Fatalf("unexpected service URL error: %s", err) + } + if gotURL == nil { + t.Fatalf("found no URL for wotsit.v2") + } + if got, want := gotURL.String(), "https://example.com/foo"; got != want { + t.Fatalf("wrong result %q; want %q", got, want) + } + } + }) + t.Run("not JSON", func(t *testing.T) { + portStr, close := testServer(func(w http.ResponseWriter, r *http.Request) { + resp := []byte(`{"thingy.v1": "http://example.com/foo"}`) + w.Header().Add("Content-Type", "application/octet-stream") + w.Write(resp) + }) + defer close() + + givenHost := "localhost" + portStr + host, err := svchost.ForComparison(givenHost) + if err != nil { + t.Fatalf("test server hostname is invalid: %s", err) + } + + d := New() + discovered, err := d.Discover(host) + if err == nil { + t.Fatalf("expected a discovery error") + } + + // Returned discovered should be nil. + if discovered != nil { + t.Errorf("discovered not nil; should be") + } + }) + t.Run("malformed JSON", func(t *testing.T) { + portStr, close := testServer(func(w http.ResponseWriter, r *http.Request) { + resp := []byte(`{"thingy.v1": "htt`) // truncated, for example... + w.Header().Add("Content-Type", "application/json") + w.Write(resp) + }) + defer close() + + givenHost := "localhost" + portStr + host, err := svchost.ForComparison(givenHost) + if err != nil { + t.Fatalf("test server hostname is invalid: %s", err) + } + + d := New() + discovered, err := d.Discover(host) + if err == nil { + t.Fatalf("expected a discovery error") + } + + // Returned discovered should be nil. + if discovered != nil { + t.Errorf("discovered not nil; should be") + } + }) + t.Run("JSON with redundant charset", func(t *testing.T) { + // The JSON RFC defines no parameters for the application/json + // MIME type, but some servers have a weird tendency to just add + // "charset" to everything, so we'll make sure we ignore it successfully. + // (JSON uses content sniffing for encoding detection, not media type params.) + portStr, close := testServer(func(w http.ResponseWriter, r *http.Request) { + resp := []byte(`{"thingy.v1": "http://example.com/foo"}`) + w.Header().Add("Content-Type", "application/json; charset=latin-1") + w.Write(resp) + }) + defer close() + + givenHost := "localhost" + portStr + host, err := svchost.ForComparison(givenHost) + if err != nil { + t.Fatalf("test server hostname is invalid: %s", err) + } + + d := New() + discovered, err := d.Discover(host) + if err != nil { + t.Fatalf("unexpected discovery error: %s", err) + } + + if discovered.services == nil { + t.Errorf("response is empty; shouldn't be") + } + }) + t.Run("no discovery doc", func(t *testing.T) { + portStr, close := testServer(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(404) + }) + defer close() + + givenHost := "localhost" + portStr + host, err := svchost.ForComparison(givenHost) + if err != nil { + t.Fatalf("test server hostname is invalid: %s", err) + } + + d := New() + discovered, err := d.Discover(host) + if err != nil { + t.Fatalf("unexpected discovery error: %s", err) + } + + // Returned discovered.services should be nil (empty). + if discovered.services != nil { + t.Errorf("discovered.services not nil (empty); should be") + } + }) + t.Run("redirect", func(t *testing.T) { + // For this test, we have two servers and one redirects to the other + portStr1, close1 := testServer(func(w http.ResponseWriter, r *http.Request) { + // This server is the one that returns a real response. + resp := []byte(`{"thingy.v1": "http://example.com/foo"}`) + w.Header().Add("Content-Type", "application/json") + w.Header().Add("Content-Length", strconv.Itoa(len(resp))) + w.Write(resp) + }) + portStr2, close2 := testServer(func(w http.ResponseWriter, r *http.Request) { + // This server is the one that redirects. + http.Redirect(w, r, "https://127.0.0.1"+portStr1+"/.well-known/terraform.json", 302) + }) + defer close1() + defer close2() + + givenHost := "localhost" + portStr2 + host, err := svchost.ForComparison(givenHost) + if err != nil { + t.Fatalf("test server hostname is invalid: %s", err) + } + + d := New() + discovered, err := d.Discover(host) + if err != nil { + t.Fatalf("unexpected discovery error: %s", err) + } + + gotURL, err := discovered.ServiceURL("thingy.v1") + if err != nil { + t.Fatalf("unexpected service URL error: %s", err) + } + if gotURL == nil { + t.Fatalf("found no URL for thingy.v1") + } + if got, want := gotURL.String(), "http://example.com/foo"; got != want { + t.Fatalf("wrong result %q; want %q", got, want) + } + + // The base URL for the host object should be the URL we redirected to, + // rather than the we redirected _from_. + gotBaseURL := discovered.discoURL.String() + wantBaseURL := "https://127.0.0.1" + portStr1 + "/.well-known/terraform.json" + if gotBaseURL != wantBaseURL { + t.Errorf("incorrect base url %s; want %s", gotBaseURL, wantBaseURL) + } + + }) +} + +func testServer(h func(w http.ResponseWriter, r *http.Request)) (portStr string, close func()) { + server := httptest.NewTLSServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + // Test server always returns 404 if the URL isn't what we expect + if r.URL.Path != "/.well-known/terraform.json" { + w.WriteHeader(404) + w.Write([]byte("not found")) + return + } + + // If the URL is correct then the given hander decides the response + h(w, r) + }, + )) + + serverURL, _ := url.Parse(server.URL) + + portStr = serverURL.Port() + if portStr != "" { + portStr = ":" + portStr + } + + close = func() { + server.Close() + } + + return portStr, close +} diff --git a/vendor/github.com/hashicorp/terraform-svchost/disco/host.go b/vendor/github.com/hashicorp/terraform-svchost/disco/host.go index 2d0fc9f1..d0ec8ee6 100644 --- a/vendor/github.com/hashicorp/terraform-svchost/disco/host.go +++ b/vendor/github.com/hashicorp/terraform-svchost/disco/host.go @@ -255,6 +255,17 @@ func (h *Host) ServiceOAuthClient(id string) (*OAuthClient, error) { ret.MinPort = 1024 ret.MaxPort = 65535 } + if scopesRaw, ok := raw["scopes"].([]interface{}); ok { + var scopes []string + for _, scopeI := range scopesRaw { + scope, ok := scopeI.(string) + if !ok { + return nil, fmt.Errorf("Invalid \"scopes\" for service %s: all scopes must be strings", id) + } + scopes = append(scopes, scope) + } + ret.Scopes = scopes + } return ret, nil } diff --git a/vendor/github.com/hashicorp/terraform-svchost/disco/host_test.go b/vendor/github.com/hashicorp/terraform-svchost/disco/host_test.go new file mode 100644 index 00000000..ad18f1bc --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-svchost/disco/host_test.go @@ -0,0 +1,576 @@ +package disco + +import ( + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path" + "reflect" + "strconv" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestHostServiceURL(t *testing.T) { + baseURL, _ := url.Parse("https://example.com/disco/foo.json") + host := Host{ + discoURL: baseURL, + hostname: "test-server", + services: map[string]interface{}{ + "absolute.v1": "http://example.net/foo/bar", + "absolutewithport.v1": "http://example.net:8080/foo/bar", + "relative.v1": "./stu/", + "rootrelative.v1": "/baz", + "protorelative.v1": "//example.net/", + "withfragment.v1": "http://example.org/#foo", + "querystring.v1": "https://example.net/baz?foo=bar", + "nothttp.v1": "ftp://127.0.0.1/pub/", + "invalid.v1": "***not A URL at all!:/<@@@@>***", + }, + } + + tests := []struct { + ID string + want string + err string + }{ + {"absolute.v1", "http://example.net/foo/bar", ""}, + {"absolutewithport.v1", "http://example.net:8080/foo/bar", ""}, + {"relative.v1", "https://example.com/disco/stu/", ""}, + {"rootrelative.v1", "https://example.com/baz", ""}, + {"protorelative.v1", "https://example.net/", ""}, + {"withfragment.v1", "http://example.org/", ""}, + {"querystring.v1", "https://example.net/baz?foo=bar", ""}, + {"nothttp.v1", "", "unsupported scheme"}, + {"invalid.v1", "", "Failed to parse service URL"}, + } + + for _, test := range tests { + t.Run(test.ID, func(t *testing.T) { + url, err := host.ServiceURL(test.ID) + if (err != nil || test.err != "") && + (err == nil || !strings.Contains(err.Error(), test.err)) { + t.Fatalf("unexpected service URL error: %s", err) + } + + var got string + if url != nil { + got = url.String() + } else { + got = "" + } + + if got != test.want { + t.Errorf("wrong result\ngot: %s\nwant: %s", got, test.want) + } + }) + } +} + +func TestHostServiceOAuthClient(t *testing.T) { + baseURL, _ := url.Parse("https://example.com/disco/foo.json") + host := Host{ + discoURL: baseURL, + hostname: "test-server", + services: map[string]interface{}{ + "explicitgranttype.v1": map[string]interface{}{ + "client": "explicitgranttype", + "authz": "./authz", + "token": "./token", + "grant_types": []interface{}{"authz_code", "password", "tbd"}, + }, + "customports.v1": map[string]interface{}{ + "client": "customports", + "authz": "./authz", + "token": "./token", + "ports": []interface{}{1025, 1026}, + }, + "invalidports.v1": map[string]interface{}{ + "client": "invalidports", + "authz": "./authz", + "token": "./token", + "ports": []interface{}{1, 65535}, + }, + "missingauthz.v1": map[string]interface{}{ + "client": "missingauthz", + "token": "./token", + }, + "missingtoken.v1": map[string]interface{}{ + "client": "missingtoken", + "authz": "./authz", + }, + "passwordmissingauthz.v1": map[string]interface{}{ + "client": "passwordmissingauthz", + "token": "./token", + "grant_types": []interface{}{"password"}, + }, + "absolute.v1": map[string]interface{}{ + "client": "absolute", + "authz": "http://example.net/foo/authz", + "token": "http://example.net/foo/token", + }, + "absolutewithport.v1": map[string]interface{}{ + "client": "absolutewithport", + "authz": "http://example.net:8000/foo/authz", + "token": "http://example.net:8000/foo/token", + }, + "relative.v1": map[string]interface{}{ + "client": "relative", + "authz": "./authz", + "token": "./token", + }, + "rootrelative.v1": map[string]interface{}{ + "client": "rootrelative", + "authz": "/authz", + "token": "/token", + }, + "protorelative.v1": map[string]interface{}{ + "client": "protorelative", + "authz": "//example.net/authz", + "token": "//example.net/token", + }, + "nothttp.v1": map[string]interface{}{ + "client": "nothttp", + "authz": "ftp://127.0.0.1/pub/authz", + "token": "ftp://127.0.0.1/pub/token", + }, + "invalidauthz.v1": map[string]interface{}{ + "client": "invalidauthz", + "authz": "***not A URL at all!:/<@@@@>***", + "token": "/foo", + }, + "invalidtoken.v1": map[string]interface{}{ + "client": "invalidauthz", + "authz": "/foo", + "token": "***not A URL at all!:/<@@@@>***", + }, + "scopesincluded.v1": map[string]interface{}{ + "client": "scopesincluded", + "authz": "/auth", + "token": "/token", + "scopes": []interface{}{"app1.full_access", "app2.read_only"}, + }, + "scopesempty.v1": map[string]interface{}{ + "client": "scopesempty", + "authz": "/auth", + "token": "/token", + "scopes": []interface{}{}, + }, + "scopesbad.v1": map[string]interface{}{ + "client": "scopesbad", + "authz": "/auth", + "token": "/token", + "scopes": []interface{}{"app1.full_access", 42}, + }, + }, + } + + mustURL := func(t *testing.T, s string) *url.URL { + t.Helper() + u, err := url.Parse(s) + if err != nil { + t.Fatalf("invalid wanted URL %s in test case: %s", s, err) + } + return u + } + + tests := []struct { + ID string + want *OAuthClient + err string + }{ + { + "explicitgranttype.v1", + &OAuthClient{ + ID: "explicitgranttype", + AuthorizationURL: mustURL(t, "https://example.com/disco/authz"), + TokenURL: mustURL(t, "https://example.com/disco/token"), + MinPort: 1024, + MaxPort: 65535, + SupportedGrantTypes: NewOAuthGrantTypeSet("authz_code", "password", "tbd"), + }, + "", + }, + { + "customports.v1", + &OAuthClient{ + ID: "customports", + AuthorizationURL: mustURL(t, "https://example.com/disco/authz"), + TokenURL: mustURL(t, "https://example.com/disco/token"), + MinPort: 1025, + MaxPort: 1026, + SupportedGrantTypes: NewOAuthGrantTypeSet("authz_code"), + }, + "", + }, + { + "invalidports.v1", + nil, + `Invalid "ports" definition for service invalidports.v1: both ports must be whole numbers between 1024 and 65535`, + }, + { + "missingauthz.v1", + nil, + `Service missingauthz.v1 definition is missing required property "authz"`, + }, + { + "missingtoken.v1", + nil, + `Service missingtoken.v1 definition is missing required property "token"`, + }, + { + "passwordmissingauthz.v1", + &OAuthClient{ + ID: "passwordmissingauthz", + TokenURL: mustURL(t, "https://example.com/disco/token"), + MinPort: 1024, + MaxPort: 65535, + SupportedGrantTypes: NewOAuthGrantTypeSet("password"), + }, + "", + }, + { + "absolute.v1", + &OAuthClient{ + ID: "absolute", + AuthorizationURL: mustURL(t, "http://example.net/foo/authz"), + TokenURL: mustURL(t, "http://example.net/foo/token"), + MinPort: 1024, + MaxPort: 65535, + SupportedGrantTypes: NewOAuthGrantTypeSet("authz_code"), + }, + "", + }, + { + "absolutewithport.v1", + &OAuthClient{ + ID: "absolutewithport", + AuthorizationURL: mustURL(t, "http://example.net:8000/foo/authz"), + TokenURL: mustURL(t, "http://example.net:8000/foo/token"), + MinPort: 1024, + MaxPort: 65535, + SupportedGrantTypes: NewOAuthGrantTypeSet("authz_code"), + }, + "", + }, + { + "relative.v1", + &OAuthClient{ + ID: "relative", + AuthorizationURL: mustURL(t, "https://example.com/disco/authz"), + TokenURL: mustURL(t, "https://example.com/disco/token"), + MinPort: 1024, + MaxPort: 65535, + SupportedGrantTypes: NewOAuthGrantTypeSet("authz_code"), + }, + "", + }, + { + "rootrelative.v1", + &OAuthClient{ + ID: "rootrelative", + AuthorizationURL: mustURL(t, "https://example.com/authz"), + TokenURL: mustURL(t, "https://example.com/token"), + MinPort: 1024, + MaxPort: 65535, + SupportedGrantTypes: NewOAuthGrantTypeSet("authz_code"), + }, + "", + }, + { + "protorelative.v1", + &OAuthClient{ + ID: "protorelative", + AuthorizationURL: mustURL(t, "https://example.net/authz"), + TokenURL: mustURL(t, "https://example.net/token"), + MinPort: 1024, + MaxPort: 65535, + SupportedGrantTypes: NewOAuthGrantTypeSet("authz_code"), + }, + "", + }, + { + "nothttp.v1", + nil, + "Failed to parse authorization URL: unsupported scheme ftp", + }, + { + "invalidauthz.v1", + nil, + "Failed to parse authorization URL: parse \"***not A URL at all!:/<@@@@>***\": first path segment in URL cannot contain colon", + }, + { + "invalidtoken.v1", + nil, + "Failed to parse token URL: parse \"***not A URL at all!:/<@@@@>***\": first path segment in URL cannot contain colon", + }, + { + "scopesincluded.v1", + &OAuthClient{ + ID: "scopesincluded", + AuthorizationURL: mustURL(t, "https://example.com/auth"), + TokenURL: mustURL(t, "https://example.com/token"), + MinPort: 1024, + MaxPort: 65535, + SupportedGrantTypes: NewOAuthGrantTypeSet("authz_code"), + Scopes: []string{"app1.full_access", "app2.read_only"}, + }, + "", + }, + { + "scopesempty.v1", + &OAuthClient{ + ID: "scopesempty", + AuthorizationURL: mustURL(t, "https://example.com/auth"), + TokenURL: mustURL(t, "https://example.com/token"), + MinPort: 1024, + MaxPort: 65535, + SupportedGrantTypes: NewOAuthGrantTypeSet("authz_code"), + }, + "", + }, + { + "scopesbad.v1", + nil, + `Invalid "scopes" for service scopesbad.v1: all scopes must be strings`, + }, + } + + for _, test := range tests { + t.Run(test.ID, func(t *testing.T) { + got, err := host.ServiceOAuthClient(test.ID) + if (err != nil || test.err != "") && + (err == nil || !strings.Contains(err.Error(), test.err)) { + t.Fatalf("unexpected service URL error: %s", err) + } + + if diff := cmp.Diff(test.want, got); diff != "" { + t.Errorf("wrong result\n%s", diff) + } + }) + } +} + +func TestVersionConstrains(t *testing.T) { + baseURL, _ := url.Parse("https://example.com/disco/foo.json") + + t.Run("exact service version is provided", func(t *testing.T) { + portStr, close := testVersionsServer(func(w http.ResponseWriter, r *http.Request) { + resp := []byte(` +{ + "service": "%s", + "product": "%s", + "minimum": "0.11.8", + "maximum": "0.12.0" +}`) + // Add the requested service and product to the response. + service := path.Base(r.URL.Path) + product := r.URL.Query().Get("product") + resp = []byte(fmt.Sprintf(string(resp), service, product)) + + w.Header().Add("Content-Type", "application/json") + w.Header().Add("Content-Length", strconv.Itoa(len(resp))) + w.Write(resp) + }) + defer close() + + host := Host{ + discoURL: baseURL, + hostname: "test-server", + transport: httpTransport, + services: map[string]interface{}{ + "thingy.v1": "/api/v1/", + "thingy.v2": "/api/v2/", + "versions.v1": "https://localhost" + portStr + "/v1/versions/", + }, + } + + expected := &Constraints{ + Service: "thingy.v1", + Product: "terraform", + Minimum: "0.11.8", + Maximum: "0.12.0", + } + + actual, err := host.VersionConstraints("thingy.v1", "terraform") + if err != nil { + t.Fatalf("unexpected version constraints error: %s", err) + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("expected %#v, got: %#v", expected, actual) + } + }) + + t.Run("service provided with different versions", func(t *testing.T) { + portStr, close := testVersionsServer(func(w http.ResponseWriter, r *http.Request) { + resp := []byte(` +{ + "service": "%s", + "product": "%s", + "minimum": "0.11.8", + "maximum": "0.12.0" +}`) + // Add the requested service and product to the response. + service := path.Base(r.URL.Path) + product := r.URL.Query().Get("product") + resp = []byte(fmt.Sprintf(string(resp), service, product)) + + w.Header().Add("Content-Type", "application/json") + w.Header().Add("Content-Length", strconv.Itoa(len(resp))) + w.Write(resp) + }) + defer close() + + host := Host{ + discoURL: baseURL, + hostname: "test-server", + transport: httpTransport, + services: map[string]interface{}{ + "thingy.v2": "/api/v2/", + "thingy.v3": "/api/v3/", + "versions.v1": "https://localhost" + portStr + "/v1/versions/", + }, + } + + expected := &Constraints{ + Service: "thingy.v3", + Product: "terraform", + Minimum: "0.11.8", + Maximum: "0.12.0", + } + + actual, err := host.VersionConstraints("thingy.v1", "terraform") + if err != nil { + t.Fatalf("unexpected version constraints error: %s", err) + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("expected %#v, got: %#v", expected, actual) + } + }) + + t.Run("service not provided", func(t *testing.T) { + host := Host{ + discoURL: baseURL, + hostname: "test-server", + transport: httpTransport, + services: map[string]interface{}{ + "versions.v1": "https://localhost/v1/versions/", + }, + } + + _, err := host.VersionConstraints("thingy.v1", "terraform") + if _, ok := err.(*ErrServiceNotProvided); !ok { + t.Fatalf("expected service not provided error, got: %v", err) + } + }) + + t.Run("versions service returns a 404", func(t *testing.T) { + portStr, close := testVersionsServer(nil) + defer close() + + host := Host{ + discoURL: baseURL, + hostname: "test-server", + transport: httpTransport, + services: map[string]interface{}{ + "thingy.v1": "/api/v1/", + "versions.v1": "https://localhost" + portStr + "/v1/non-existent/", + }, + } + + _, err := host.VersionConstraints("thingy.v1", "terraform") + if _, ok := err.(*ErrNoVersionConstraints); !ok { + t.Fatalf("expected service not provided error, got: %v", err) + } + }) + + t.Run("checkpoint is disabled", func(t *testing.T) { + if err := os.Setenv("CHECKPOINT_DISABLE", "1"); err != nil { + t.Fatalf("unexpected error: %v", err) + } + defer os.Unsetenv("CHECKPOINT_DISABLE") + + host := Host{ + discoURL: baseURL, + hostname: "test-server", + transport: httpTransport, + services: map[string]interface{}{ + "thingy.v1": "/api/v1/", + "versions.v1": "https://localhost/v1/versions/", + }, + } + + _, err := host.VersionConstraints("thingy.v1", "terraform") + if _, ok := err.(*ErrNoVersionConstraints); !ok { + t.Fatalf("expected service not provided error, got: %v", err) + } + }) + + t.Run("versions service not discovered", func(t *testing.T) { + host := Host{ + discoURL: baseURL, + hostname: "test-server", + transport: httpTransport, + services: map[string]interface{}{ + "thingy.v1": "/api/v1/", + }, + } + + _, err := host.VersionConstraints("thingy.v1", "terraform") + if _, ok := err.(*ErrServiceNotProvided); !ok { + t.Fatalf("expected service not provided error, got: %v", err) + } + }) + + t.Run("versions service version not discovered", func(t *testing.T) { + host := Host{ + discoURL: baseURL, + hostname: "test-server", + transport: httpTransport, + services: map[string]interface{}{ + "thingy.v1": "/api/v1/", + "versions.v2": "https://localhost/v2/versions/", + }, + } + + _, err := host.VersionConstraints("thingy.v1", "terraform") + if _, ok := err.(*ErrVersionNotSupported); !ok { + t.Fatalf("expected service not provided error, got: %v", err) + } + }) +} + +func testVersionsServer(h func(w http.ResponseWriter, r *http.Request)) (portStr string, close func()) { + server := httptest.NewTLSServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + // Test server always returns 404 if the URL isn't what we expect + if !strings.HasPrefix(r.URL.Path, "/v1/versions/") { + w.WriteHeader(404) + w.Write([]byte("not found")) + return + } + + // If the URL is correct then the given hander decides the response + h(w, r) + }, + )) + + serverURL, _ := url.Parse(server.URL) + + portStr = serverURL.Port() + if portStr != "" { + portStr = ":" + portStr + } + + close = func() { + server.Close() + } + + return portStr, close +} diff --git a/vendor/github.com/hashicorp/terraform-svchost/disco/oauth_client.go b/vendor/github.com/hashicorp/terraform-svchost/disco/oauth_client.go index 9308bbf7..9df16ebc 100644 --- a/vendor/github.com/hashicorp/terraform-svchost/disco/oauth_client.go +++ b/vendor/github.com/hashicorp/terraform-svchost/disco/oauth_client.go @@ -46,6 +46,11 @@ type OAuthClient struct { // by the server, even if a particular keyword is not supported by the // current version of Terraform. SupportedGrantTypes OAuthGrantTypeSet + + // Oauth2 does not require scopes for the authorization endpoint, however + // OIDC does. Optional list of scopes to include in auth code and token + // requests. + Scopes []string } // Endpoint returns an oauth2.Endpoint value ready to be used with the oauth2 diff --git a/vendor/github.com/hashicorp/terraform-svchost/svchost_test.go b/vendor/github.com/hashicorp/terraform-svchost/svchost_test.go new file mode 100644 index 00000000..2eda3acb --- /dev/null +++ b/vendor/github.com/hashicorp/terraform-svchost/svchost_test.go @@ -0,0 +1,218 @@ +package svchost + +import "testing" + +func TestForDisplay(t *testing.T) { + tests := []struct { + Input string + Want string + }{ + { + "", + "", + }, + { + "example.com", + "example.com", + }, + { + "invalid", + "invalid", + }, + { + "localhost", + "localhost", + }, + { + "localhost:1211", + "localhost:1211", + }, + { + "HashiCorp.com", + "hashicorp.com", + }, + { + "Испытание.com", + "испытание.com", + }, + { + "münchen.de", // this is a precomposed u with diaeresis + "münchen.de", // this is a precomposed u with diaeresis + }, + { + "münchen.de", // this is a separate u and combining diaeresis + "münchen.de", // this is a precomposed u with diaeresis + }, + { + "example.com:443", + "example.com", + }, + { + "example.com:81", + "example.com:81", + }, + { + "example.com:boo", + "example.com:boo", // invalid, but tolerated for display purposes + }, + { + "example.com:boo:boo", + "example.com:boo:boo", // invalid, but tolerated for display purposes + }, + { + "example.com:081", + "example.com:81", + }, + } + + for _, test := range tests { + t.Run(test.Input, func(t *testing.T) { + got := ForDisplay(test.Input) + if got != test.Want { + t.Errorf("wrong result\ninput: %s\ngot: %s\nwant: %s", test.Input, got, test.Want) + } + }) + } +} + +func TestForComparison(t *testing.T) { + tests := []struct { + Input string + Want string + Err bool + }{ + { + "", + "", + true, + }, + { + "example.com", + "example.com", + false, + }, + { + "example.com:443", + "example.com", + false, + }, + { + "example.com:81", + "example.com:81", + false, + }, + { + "example.com:081", + "example.com:81", + false, + }, + { + "invalid", + "invalid", + false, // the "invalid" TLD is, confusingly, a valid hostname syntactically + }, + { + "localhost", // supported for local testing only + "localhost", + false, + }, + { + "localhost:1211", // supported for local testing only + "localhost:1211", + false, + }, + { + "HashiCorp.com", + "hashicorp.com", + false, + }, + { + "1example.com", + "1example.com", + false, + }, + { + "Испытание.com", + "xn--80akhbyknj4f.com", + false, + }, + { + "münchen.de", // this is a precomposed u with diaeresis + "xn--mnchen-3ya.de", + false, + }, + { + "münchen.de", // this is a separate u and combining diaeresis + "xn--mnchen-3ya.de", + false, + }, + { + "blah..blah", + "", + true, + }, + { + "example.com:boo", + "", + true, + }, + { + "example.com:80:boo", + "", + true, + }, + } + + for _, test := range tests { + t.Run(test.Input, func(t *testing.T) { + got, err := ForComparison(test.Input) + if (err != nil) != test.Err { + if test.Err { + t.Error("unexpected success; want error") + } else { + t.Errorf("unexpected error; want success\nerror: %s", err) + } + } + if string(got) != test.Want { + t.Errorf("wrong result\ninput: %s\ngot: %s\nwant: %s", test.Input, got, test.Want) + } + }) + } +} + +func TestHostnameForDisplay(t *testing.T) { + tests := []struct { + Input string + Want string + }{ + { + "example.com", + "example.com", + }, + { + "example.com:81", + "example.com:81", + }, + { + "xn--80akhbyknj4f.com", + "испытание.com", + }, + { + "xn--80akhbyknj4f.com:8080", + "испытание.com:8080", + }, + { + "xn--mnchen-3ya.de", + "münchen.de", // this is a precomposed u with diaeresis + }, + } + + for _, test := range tests { + t.Run(test.Input, func(t *testing.T) { + got := Hostname(test.Input).ForDisplay() + if got != test.Want { + t.Errorf("wrong result\ninput: %s\ngot: %s\nwant: %s", test.Input, got, test.Want) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/.circleci/config.yml b/vendor/github.com/hashicorp/terraform/.circleci/config.yml index 84b31559..69353ff2 100644 --- a/vendor/github.com/hashicorp/terraform/.circleci/config.yml +++ b/vendor/github.com/hashicorp/terraform/.circleci/config.yml @@ -3,10 +3,14 @@ version: 2.1 orbs: slack: circleci/slack@3.4.2 +references: + images: + middleman: &MIDDLEMAN_IMAGE docker.mirror.hashicorp.services/hashicorp/middleman-hashicorp:0.3.44 + executors: go: docker: - - image: circleci/golang:1.15 + - image: docker.mirror.hashicorp.services/circleci/golang:1.16 environment: CONSUL_VERSION: 1.7.2 GOMAXPROCS: 4 @@ -74,7 +78,7 @@ jobs: - slack/status: fail_only: true - only_for_branches: master + only_for_branches: main go-test-e2e: executor: @@ -104,28 +108,7 @@ jobs: - slack/status: fail_only: true - only_for_branches: master - - # combine code coverage results from the parallel circleci executors - coverage-merge: - executor: - name: go - steps: - - checkout - - attach_workspace: - at: . - - run: mkdir -p $TEST_RESULTS_DIR - - run: - name: merge coverage reports - command: | - echo "mode: set" > coverage.out - grep -h -v "mode: set" cov_*.part >> coverage.out - go tool cover -html=coverage.out -o $TEST_RESULTS_DIR/coverage.html - - run: - name: codecov upload - command: bash <(curl -s https://codecov.io/bash) -v -C $CIRCLE_SHA1 - - store_artifacts: - path: *TEST_RESULTS_DIR + only_for_branches: main # build all distros build-distros: &build-distros @@ -142,20 +125,12 @@ jobs: - store_artifacts: path: *ARTIFACTS_DIR - # build all 386 architecture supported OS binaries - build-386: - <<: *build-distros - environment: - <<: *build-env - XC_OS: "freebsd linux openbsd windows" - XC_ARCH: "386" - # build all amd64 architecture supported OS binaries build-amd64: <<: *build-distros environment: <<: *build-env - XC_OS: "darwin freebsd linux solaris windows" + XC_OS: "darwin linux windows" XC_ARCH: "amd64" # build all arm architecture supported OS binaries @@ -163,7 +138,7 @@ jobs: <<: *build-distros environment: <<: *build-env - XC_OS: "freebsd linux" + XC_OS: "linux" XC_ARCH: "arm" test-docker-full: @@ -176,6 +151,97 @@ jobs: name: test docker build for 'full' image command: docker build -t test-docker-full . + # Based on a similar job in terraform-website repo. + website-link-check: + docker: + - image: *MIDDLEMAN_IMAGE + steps: + - checkout: + path: terraform + + - run: + name: Determine changed website files, if any + working_directory: terraform + command: | + # Figure out what the current branch forked from. Compare against + # main and the set of "vX.Y" branches, and choose whichever branch + # we're the *fewest* commits ahead of. + # The point here isn't to perfectly predict where this will be + # merged; all we really care about is determining which commits are + # *unique to this PR,* so we don't accidentally complain about + # problems you had nothing to do with. + PARENT_BRANCH=$( + for br in $(git branch -rl --format='%(refname:short)' | grep -E '^origin/(main|v\d+\.\d+)$'); do + new_commits=$(git rev-list --first-parent ^${br} HEAD | wc -l); + echo "${br} ${new_commits}"; + done \ + | sort -n -k2 \ + | head -n1 \ + | awk '{print $1}'; + ) + echo "Checking current branch against: ${PARENT_BRANCH}" + MERGE_BASE=$(git merge-base HEAD ${PARENT_BRANCH}) + git diff --name-only -z --diff-filter=AMRCT ${MERGE_BASE}..HEAD -- ./website/ > /tmp/changed-website-files.txt + # --name-only: Return a list of affected files but don't show the changes. + # -z: Make that a null-separated list (instead of newline-separated), and + # DON'T mangle non-ASCII characters. + # --diff-filter=AMRCT: Only list files that were added, modified, renamed, + # copied, or had their type changed (file, symlink, etc.). In + # particular, we don't want to check deleted files. + # ${MERGE_BASE}..HEAD: Only consider files that have + # changed since this branch diverged from its parent branch. + # -- ./website/: Only consider files in the website directory. + echo "Changed website files:" + cat /tmp/changed-website-files.txt | tr '\0' '\n' + # Need to use "tr" for display because it's a null-separated list. + + - run: + name: Exit early if there's nothing to check + command: | + if [ ! -s /tmp/changed-website-files.txt ]; then + circleci-agent step halt + fi + + - run: + name: Check out terraform-website repo + command: git clone git@github.com:hashicorp/terraform-website.git + + - run: + name: Use local checkout for terraform submodule, instead of cloning again + working_directory: terraform-website + command: | + # Set submodule's URL to our existing checkout. + # (Using `pwd` because git's behavior with strictly relative paths is unreliable.) + git config --file=.gitmodules submodule.ext/terraform.url $(pwd)/../terraform/.git + # Make it so `make sync` will grab our current branch instead of stable-website. + git config --file=.gitmodules submodule.ext/terraform.branch HEAD + + - run: + name: Init/update terraform-website submodules + working_directory: terraform-website + command: make sync + + - run: + name: Set up terraform-website dependencies + working_directory: terraform-website/content + # If this does anything interesting, then the container needs an update. + command: bundle check || bundle install --path vendor/bundle --retry=3 + + - run: + name: Run middleman in background + working_directory: terraform-website/content + background: true + command: bundle exec middleman server + + - run: + name: Wait for server to start + command: until curl -sS http://localhost:4567/ > /dev/null; do sleep 1; done + + - run: + name: Check links in changed pages + working_directory: terraform-website/content + command: cat /tmp/changed-website-files.txt | bundle exec ./scripts/check-pr-links.rb + workflows: version: 2 test: @@ -187,19 +253,18 @@ workflows: - go-test-e2e: requires: - go-checks - - coverage-merge: - requires: - - go-test - - go-test-e2e - test-docker-full: filters: branches: only: - - master + - main - /^v\d+\.\d+$/ # v0.11, v0.12, etc. build-distros: jobs: - - build-386 - build-amd64 - build-arm + + website-test: + jobs: + - website-link-check diff --git a/vendor/github.com/hashicorp/terraform/.github/CODE_OF_CONDUCT.md b/vendor/github.com/hashicorp/terraform/.github/CODE_OF_CONDUCT.md deleted file mode 100644 index 0c8b092c..00000000 --- a/vendor/github.com/hashicorp/terraform/.github/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,5 +0,0 @@ -# Code of Conduct - -HashiCorp Community Guidelines apply to you when interacting with the community here on GitHub and contributing code. - -Please read the full text at https://www.hashicorp.com/community-guidelines diff --git a/vendor/github.com/hashicorp/terraform/.github/CONTRIBUTING.md b/vendor/github.com/hashicorp/terraform/.github/CONTRIBUTING.md deleted file mode 100644 index 7cb318a9..00000000 --- a/vendor/github.com/hashicorp/terraform/.github/CONTRIBUTING.md +++ /dev/null @@ -1,237 +0,0 @@ -# Contributing to Terraform - -This repository contains only Terraform core, which includes the command line interface and the main graph engine. Providers are implemented as plugins that each have their own repository in [the `terraform-providers` organization](https://github.com/terraform-providers) on GitHub. Instructions for developing each provider are in the associated README file. For more information, see [the provider development overview](https://www.terraform.io/docs/plugins/provider.html). - ---- - -**All communication on GitHub, the community forum, and other HashiCorp-provided communication channels is subject to [the HashiCorp community guidelines](https://www.hashicorp.com/community-guidelines).** - -This document provides guidance on Terraform contribution recommended practices. It covers what we're looking for in order to help set some expectations and help you get the most out of participation in this project. - -To record a bug report, enhancement proposal, or give any other product feedback, please [open a GitHub issue](https://github.com/hashicorp/terraform/issues/new/choose) using the most appropriate issue template. Please do fill in all of the information the issue templates request, because we've seen from experience that this will maximize the chance that we'll be able to act on your feedback. - ---- - - - -- [Contributing Fixes](#contributing-fixes) -- [Proposing a Change](#proposing-a-change) - - [Caveats & areas of special concern](#caveats--areas-of-special-concern) - - [State Storage Backends](#state-storage-backends) - - [Provisioners](#provisioners) - - [Maintainers](#maintainers) - - [Pull Request Lifecycle](#pull-request-lifecycle) - - [Getting Your Pull Requests Merged Faster](#getting-your-pull-requests-merged-faster) - - [PR Checks](#pr-checks) -- [Terraform CLI/Core Development Environment](#terraform-clicore-development-environment) -- [Acceptance Tests: Testing interactions with external services](#acceptance-tests-testing-interactions-with-external-services) -- [Generated Code](#generated-code) -- [External Dependencies](#external-dependencies) - - - -## Contributing Fixes - -It can be tempting to want to dive into an open source project and help _build the thing_ you believe you're missing. It's a wonderful and helpful intention. However, Terraform is a complex tool. Many seemingly simple changes can have serious effects on other areas of the code and it can take some time to become familiar with the effects of even basic changes. The Terraform team is not immune to unintended and sometimes undesirable changes. We do take our work seriously, and appreciate the globally diverse community that relies on Terraform for workflows of all sizes and criticality. - -As a result of Terraform's complexity and high bar for stability, the most straightforward way to start helping with the Terraform project is to pick an existing bug and [get to work](#terraform-clicore-development-environment). - -For new contributors we've labeled a few issues with `Good First Issue` as a nod to issues which will help get you familiar with Terraform development, while also providing an onramp to the codebase itself. - -Read the documentation, and don't be afraid to [ask questions](https://discuss.hashicorp.com/c/terraform-core/27). - -## Proposing a Change - -In order to be respectful of the time of community contributors, we aim to discuss potential changes in GitHub issues prior to implementation. That will allow us to give design feedback up front and set expectations about the scope of the change, and, for larger changes, how best to approach the work such that the Terraform team can review it and merge it along with other concurrent work. - -If the bug you wish to fix or enhancement you wish to implement isn't already covered by a GitHub issue that contains feedback from the Terraform team, please do start a discussion (either in [a new GitHub issue](https://github.com/hashicorp/terraform/issues/new/choose) or an existing one, as appropriate) before you invest significant development time. If you mention your intent to implement the change described in your issue, the Terraform team can, as best as possible, prioritize including implementation-related feedback in the subsequent discussion. - -At this time, we do not have a formal process for reviewing outside proposals that significantly change Terraform's workflow, its primary usage patterns, and its language. Additionally, some seemingly simple proposals can have deep effects across Terraform, which is why we strongly suggest starting with an issue-based proposal. - -For large proposals that could entail a significant design phase, we wish to be up front with potential contributors that, unfortunately, we are unlikely to be able to give prompt feedback. We are still interested to hear about your use-cases so that we can consider ways to meet them as part of other larger projects. - -Most changes will involve updates to the test suite, and changes to Terraform's documentation. The Terraform team can advise on different testing strategies for specific scenarios, and may ask you to revise the specific phrasing of your proposed documentation prose to match better with the standard "voice" of Terraform's documentation. - -This repository is primarily maintained by a small team at HashiCorp along with their other responsibilities, so unfortunately we cannot always respond promptly to pull requests, particularly if they do not relate to an existing GitHub issue where the Terraform team has already participated and indicated willingness to work on the issue or accept PRs for the proposal. We *are* grateful for all contributions however, and will give feedback on pull requests as soon as we're able. - -### Caveats & areas of special concern - -There are some areas of Terraform which are of special concern to the Terraform team. - -#### State Storage Backends - -The Terraform team is not merging PRs for new state storage backends at the current time. Our priority regarding state storage backends is to find maintainers for existing backends and remove those backends without maintainers. - -Please see the [CODEOWNERS](https://github.com/hashicorp/terraform/blob/master/CODEOWNERS) file for the status of a given backend. Community members with an interest in a particular standard backend are welcome to help maintain it. - -Currently, merging state storage backends places a significant burden on the Terraform team. The team must setup an environment and cloud service provider account, or a new database/storage/key-value service, in order to build and test remote state storage backends. The time and complexity of doing so prevents us from moving Terraform forward in other ways. - -We are working to remove ourselves from the critical path of state storage backends by moving them towards a plugin model. In the meantime, we won't be accepting new remote state backends into Terraform. - -#### Provisioners - -Provisioners are an area of concern in Terraform for a number of reasons. Chiefly, they are often used in the place of configuration management tools or custom providers. - -There are two main types of provisioners in Terraform, the generic provisioners (`file`,`local-exec`, and `remote-exec`) and the tool-specific provisioners (`chef`, `habbitat`, `puppet` & `salt-masterless`). **The tool-specific provisioners [are deprecated](https://discuss.hashicorp.com/t/notice-terraform-to-begin-deprecation-of-vendor-tool-specific-provisioners-starting-in-terraform-0-13-4/13997).** In practice this means we will not be accepting PRs for these areas of the codebase. - -From our [documentation](https://www.terraform.io/docs/provisioners/index.html): - -> ... they [...] add a considerable amount of complexity and uncertainty to Terraform usage.[...] we still recommend attempting to solve it [your problem] using other techniques first, and use provisioners only if there is no other option. - -The Terraform team is in the process of building a way forward which continues to decrease reliance on provisioners. In the mean time however, as our documentation indicates, they are a tool of last resort. As such expect that PRs and issues for provisioners are not high in priority. - -Please see the [CODEOWNERS](https://github.com/hashicorp/terraform/blob/master/CODEOWNERS) file for the status of a given provisioner. Community members with an interest in a particular provisioner are welcome to help maintain it. - -#### Maintainers - -Maintainers are key contributors to our Open Source project. They contribute their time and expertise and we ask that the community take extra special care to be mindful of this when interacting with them. - -For code that has a listed maintainer or maintainers in our [CODEOWNERS](https://github.com/hashicorp/terraform/blob/master/CODEOWNERS) file, the Terraform team will highlight them for participation in PRs which relate to the area of code they maintain. The expectation is that a maintainer will review the code and work with the PR contributor before the code is merged by the Terraform team. - -There is no expectation on response time for our maintainers; they may be indisposed for prolonged periods of time. Please be patient. Discussions on when code becomes "unmaintained" will be on a case-by-case basis. - -If an an unmaintained area of code interests you and you'd like to become a maintainer, you may simply make a PR against our [CODEOWNERS](https://github.com/hashicorp/terraform/blob/master/CODEOWNERS) file with your github handle attached to the approriate area. If there is a maintainer or team of maintainers for that area, please coordinate with them as necessary. - -### Pull Request Lifecycle - -1. You are welcome to submit a [draft pull request](https://github.blog/2019-02-14-introducing-draft-pull-requests/) for commentary or review before it is fully completed. It's also a good idea to include specific questions or items you'd like feedback on. -2. Once you believe your pull request is ready to be merged you can create your pull request. -3. When time permits Terraform's core team members will look over your contribution and either merge, or provide comments letting you know if there is anything left to do. It may take some time for us to respond. We may also have questions that we need answered about the code, either because something doesn't make sense to us or because we want to understand your thought process. We kindly ask that you do not target specific team members. -4. If we have requested changes, you can either make those changes or, if you disagree with the suggested changes, we can have a conversation about our reasoning and agree on a path forward. This may be a multi-step process. Our view is that pull requests are a chance to collaborate, and we welcome conversations about how to do things better. It is the contributor's responsibility to address any changes requested. While reviewers are happy to give guidance, it is unsustainable for us to perform the coding work necessary to get a PR into a mergeable state. -5. Once all outstanding comments and checklist items have been addressed, your contribution will be merged! Merged PRs may or may not be included in the next release based on changes the Terraform teams deems as breaking or not. The core team takes care of updating the [CHANGELOG.md](https://github.com/hashicorp/terraform/blob/master/CHANGELOG.md) as they merge. -6. In some cases, we might decide that a PR should be closed without merging. We'll make sure to provide clear reasoning when this happens. Following the recommended process above is one of the ways to ensure you don't spend time on a PR we can't or won't merge. - -#### Getting Your Pull Requests Merged Faster - -It is much easier to review pull requests that are: - -1. Well-documented: Try to explain in the pull request comments what your change does, why you have made the change, and provide instructions for how to produce the new behavior introduced in the pull request. If you can, provide screen captures or terminal output to show what the changes look like. This helps the reviewers understand and test the change. -2. Small: Try to only make one change per pull request. If you found two bugs and want to fix them both, that's *awesome*, but it's still best to submit the fixes as separate pull requests. This makes it much easier for reviewers to keep in their heads all of the implications of individual code changes, and that means the PR takes less effort and energy to merge. In general, the smaller the pull request, the sooner reviewers will be able to make time to review it. -3. Passing Tests: Based on how much time we have, we may not review pull requests which aren't passing our tests (look below for advice on how to run unit tests). If you need help figuring out why tests are failing, please feel free to ask, but while we're happy to give guidance it is generally your responsibility to make sure that tests are passing. If your pull request changes an interface or invalidates an assumption that causes a bunch of tests to fail, then you need to fix those tests before we can merge your PR. - -If we request changes, try to make those changes in a timely manner. Otherwise, PRs can go stale and be a lot more work for all of us to merge in the future. - -Even with everyone making their best effort to be responsive, it can be time-consuming to get a PR merged. It can be frustrating to deal with the back-and-forth as we make sure that we understand the changes fully. Please bear with us, and please know that we appreciate the time and energy you put into the project. - -### PR Checks - -The following checks run when a PR is opened: - -- Contributor License Agreement (CLA): If this is your first contribution to Terraform you will be asked to sign the CLA. -- Tests: tests include unit tests and acceptance tests, and all tests must pass before a PR can be merged. -- Test Coverage Report: We use [codecov](https://codecov.io/) to check both overall test coverage, and patch coverage. - --> **Note:** We are still deciding on the right targets for our code coverage check. A failure in `codecov` does not necessarily mean that your PR will not be approved or merged. - ----- - -## Terraform CLI/Core Development Environment - -This repository contains the source code for Terraform CLI, which is the main component of Terraform that contains the core Terraform engine. - -The HashiCorp-maintained Terraform providers are also open source but are not in this repository; instead, they are each in their own repository in [the `terraform-providers` organization](https://github.com/terraform-providers) on GitHub. - -This repository also does not include the source code for some other parts of the Terraform product including Terraform Cloud, Terraform Enterprise, and the Terraform Registry. Those components are not open source, though if you have feedback about them (including bug reports) please do feel free to [open a GitHub issue on this repository](https://github.com/hashicorp/terraform/issues/new/choose). - ---- - -If you wish to work on the Terraform CLI source code, you'll first need to install the [Go](https://golang.org/) compiler and the version control system [Git](https://git-scm.com/). - -At this time the Terraform development environment is targeting only Linux and Mac OS X systems. While Terraform itself is compatible with Windows, unfortunately the unit test suite currently contains Unix-specific assumptions around maximum path lengths, path separators, etc. - -Refer to the file [`.go-version`](https://github.com/hashicorp/terraform/blob/master/.go-version) to see which version of Go Terraform is currently built with. Other versions will often work, but if you run into any build or testing problems please try with the specific Go version indicated. You can optionally simplify the installation of multiple specific versions of Go on your system by installing [`goenv`](https://github.com/syndbg/goenv), which reads `.go-version` and automatically selects the correct Go version. - -Use Git to clone this repository into a location of your choice. Terraform is using [Go Modules](https://blog.golang.org/using-go-modules), and so you should *not* clone it inside your `GOPATH`. - -Switch into the root directory of the cloned repository and build Terraform using the Go toolchain in the standard way: - -``` -cd terraform -go install . -``` - -The first time you run the `go install` command, the Go toolchain will download any library dependencies that you don't already have in your Go modules cache. Subsequent builds will be faster because these dependencies will already be available on your local disk. - -Once the compilation process succeeds, you can find a `terraform` executable in the Go executable directory. If you haven't overridden it with the `GOBIN` environment variable, the executable directory is the `bin` directory inside the directory returned by the following command: - -``` -go env GOPATH -``` - -If you are planning to make changes to the Terraform source code, you should run the unit test suite before you start to make sure everything is initially passing: - -``` -go test ./... -``` - -As you make your changes, you can re-run the above command to ensure that the tests are *still* passing. If you are working only on a specific Go package, you can speed up your testing cycle by testing only that single package, or packages under a particular package prefix: - -``` -go test ./command/... -go test ./addrs -``` - -## Acceptance Tests: Testing interactions with external services - -Terraform's unit test suite is self-contained, using mocks and local files to help ensure that it can run offline and is unlikely to be broken by changes to outside systems. - -However, several Terraform components interact with external services, such as the automatic provider installation mechanism, the Terraform Registry, Terraform Cloud, etc. - -There are some optional tests in the Terraform CLI codebase that *do* interact with external services, which we collectively refer to as "acceptance tests". You can enable these by setting the environment variable `TF_ACC=1` when running the tests. We recommend focusing only on the specific package you are working on when enabling acceptance tests, both because it can help the test run to complete faster and because you are less likely to encounter failures due to drift in systems unrelated to your current goal: - -``` -TF_ACC=1 go test ./internal/initwd -``` - -Because the acceptance tests depend on services outside of the Terraform codebase, and because the acceptance tests are usually used only when making changes to the systems they cover, it is common and expected that drift in those external systems will cause test failures. Because of this, prior to working on a system covered by acceptance tests it's important to run the existing tests for that system in an *unchanged* work tree first and respond to any test failures that preexist, to avoid misinterpreting such failures as bugs in your new changes. - -## Generated Code - -Some files in the Terraform CLI codebase are generated. In most cases, we update these using `go generate`, which is the standard way to encapsulate code generation steps in a Go codebase. - -``` -go generate ./... -``` - -Use `git diff` afterwards to inspect the changes and ensure that they are what you expected. - -Terraform includes generated Go stub code for the Terraform provider plugin protocol, which is defined using Protocol Buffers. Because the Protocol Buffers tools are not written in Go and thus cannot be automatically installed using `go get`, we follow a different process for generating these, which requires that you've already installed a suitable version of `protoc`: - -``` -make protobuf -``` - -## External Dependencies - -Terraform uses Go Modules for dependency management. - -Our dependency licensing policy for Terraform excludes proprietary licenses and "copyleft"-style licenses. We accept the common Mozilla Public License v2, MIT License, and BSD licenses. We will consider other open source licenses in similar spirit to those three, but if you plan to include such a dependency in a contribution we'd recommend opening a GitHub issue first to discuss what you intend to implement and what dependencies it will require so that the Terraform team can review the relevant licenses to for whether they meet our licensing needs. - -If you need to add a new dependency to Terraform or update the selected version for an existing one, use `go get` from the root of the Terraform repository as follows: - -``` -go get github.com/hashicorp/hcl/v2@2.0.0 -``` - -This command will download the requested version (2.0.0 in the above example) and record that version selection in the `go.mod` file. It will also record checksums for the module in the `go.sum`. - -To complete the dependency change, clean up any redundancy in the module metadata files by running: - -``` -go mod tidy -``` - -To ensure that the upgrade has worked correctly, be sure to run the unit test suite at least once: - -``` -go test ./... -``` - -Because dependency changes affect a shared, top-level file, they are more likely than some other change types to become conflicted with other proposed changes during the code review process. For that reason, and to make dependency changes more visible in the change history, we prefer to record dependency changes as separate commits that include only the results of the above commands and the minimal set of changes to Terraform's own code for compatibility with the new version: - -``` -git add go.mod go.sum -git commit -m "go get github.com/hashicorp/hcl/v2@2.0.0" -``` - -You can then make use of the new or updated dependency in new code added in subsequent commits. diff --git a/vendor/github.com/hashicorp/terraform/.github/ISSUE_TEMPLATE/bug_report.md b/vendor/github.com/hashicorp/terraform/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index ca53df77..00000000 --- a/vendor/github.com/hashicorp/terraform/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -name: Bug report -about: Let us know about an unexpected error, a crash, or an incorrect behavior. -labels: bug, new - ---- - - - -### Terraform Version - - -``` -... -``` - -### Terraform Configuration Files - - -```terraform -... -``` - -### Debug Output - - -### Crash Output - - -### Expected Behavior - - -### Actual Behavior - - -### Steps to Reproduce - - -### Additional Context - - -### References - diff --git a/vendor/github.com/hashicorp/terraform/.github/ISSUE_TEMPLATE/config.yml b/vendor/github.com/hashicorp/terraform/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 3962f0fe..00000000 --- a/vendor/github.com/hashicorp/terraform/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,11 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: Provider-related Feedback and Questions - url: https://github.com/terraform-providers - about: Each provider (e.g. AWS, Azure, GCP, Oracle, K8S, etc.) has its own repository, any provider related issues or questions should be directed to appropriate provider repository. - - name: Provider Development Feedback and Questions - url: https://github.com/hashicorp/terraform-plugin-sdk/issues/new/choose - about: Plugin SDK has its own repository, any SDK and provider development related issues or questions should be directed there. - - name: Terraform Language or Workflow Questions - url: https://discuss.hashicorp.com/c/terraform-core - about: Please ask and answer language or workflow related questions through the Terraform Core Community Forum. diff --git a/vendor/github.com/hashicorp/terraform/.github/ISSUE_TEMPLATE/feature_request.md b/vendor/github.com/hashicorp/terraform/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 78393535..00000000 --- a/vendor/github.com/hashicorp/terraform/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -name: Feature request -about: Suggest a new feature or other enhancement. -labels: enhancement, new - ---- - - - -### Current Terraform Version - - -``` -... -``` - -### Use-cases - - -### Attempted Solutions - - -### Proposal - - -### References - diff --git a/vendor/github.com/hashicorp/terraform/.github/SUPPORT.md b/vendor/github.com/hashicorp/terraform/.github/SUPPORT.md deleted file mode 100644 index 9ba846c3..00000000 --- a/vendor/github.com/hashicorp/terraform/.github/SUPPORT.md +++ /dev/null @@ -1,4 +0,0 @@ -# Support - -If you have questions about Terraform usage, please feel free to create a topic -on [the official community forum](https://discuss.hashicorp.com/c/terraform-core). diff --git a/vendor/github.com/hashicorp/terraform/.gitignore b/vendor/github.com/hashicorp/terraform/.gitignore deleted file mode 100644 index 6efe5a8c..00000000 --- a/vendor/github.com/hashicorp/terraform/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -*.dll -*.exe -.DS_Store -example.tf -terraform.tfplan -terraform.tfstate -bin/ -modules-dev/ -/pkg/ -website/.vagrant -website/.bundle -website/build -website/node_modules -.vagrant/ -*.backup -./*.tfstate -.terraform/ -*.log -*.bak -*~ -.*.swp -.idea -*.iml -*.test -*.iml - -website/vendor - -# Test exclusions -!command/testdata/**/*.tfstate -!command/testdata/**/.terraform/ - -# Coverage -coverage.txt diff --git a/vendor/github.com/hashicorp/terraform/.go-version b/vendor/github.com/hashicorp/terraform/.go-version index 42cf0675..4a02d2c3 100644 --- a/vendor/github.com/hashicorp/terraform/.go-version +++ b/vendor/github.com/hashicorp/terraform/.go-version @@ -1 +1 @@ -1.15.2 +1.16.2 diff --git a/vendor/github.com/hashicorp/terraform/.tfdev b/vendor/github.com/hashicorp/terraform/.tfdev index a04d5c10..857b02d9 100644 --- a/vendor/github.com/hashicorp/terraform/.tfdev +++ b/vendor/github.com/hashicorp/terraform/.tfdev @@ -1,5 +1,4 @@ version_info { - commit_var = "main.GitCommit" version_var = "github.com/hashicorp/terraform/version.Version" prerelease_var = "github.com/hashicorp/terraform/version.Prerelease" } diff --git a/vendor/github.com/hashicorp/terraform/BUGPROCESS.md b/vendor/github.com/hashicorp/terraform/BUGPROCESS.md index adb8ec6e..2126f4fb 100644 --- a/vendor/github.com/hashicorp/terraform/BUGPROCESS.md +++ b/vendor/github.com/hashicorp/terraform/BUGPROCESS.md @@ -7,7 +7,7 @@ When a bug report is filed, our goal is to either: ## Process -### 1. [Newly created issues](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Anew+label%3Abug+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+-label%3Aconfirmed+-label%3A%22pending+project%22+-label%3A%22waiting+for+reproduction%22+-label%3A%22waiting-response%22+-label%3Aexplained) require initial filtering. +### 1. [Newly created issues](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Anew+label%3Abug+-label%3Abackend%2Fk8s+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+-label%3Aconfirmed+-label%3A%22pending+project%22+-label%3A%22waiting+for+reproduction%22+-label%3A%22waiting-response%22+-label%3Aexplained+) require initial filtering. These are raw reports that need categorization and support clarifying them. They need the following done: @@ -20,7 +20,7 @@ If an issue requires discussion with the user to get it out of this initial stat Once this initial filtering has been done, remove the new label. If an issue subjectively looks very high-impact and likely to impact many users, assign it to the [appropriate milestone](https://github.com/hashicorp/terraform/milestones) to mark it as being urgent. -### 2. Clarify [unreproduced issues](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+created%3A%3E2020-05-01+-label%3Aprovisioner%2Fsalt-masterless+-label%3Adocumentation+-label%3Aprovider%2Fazuredevops+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent+-label%3Abackend%2Fmanta+-label%3Abackend%2Fatlas+-label%3Abackend%2Fetcdv3+-label%3Abackend%2Fetcdv2+-label%3Aconfirmed+-label%3A%22pending+project%22+-label%3Anew+-label%3A%22waiting+for+reproduction%22+-label%3Awaiting-response+-label%3Aexplained+sort%3Acreated-asc) +### 2. Clarify [unreproduced issues](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+created%3A%3E2020-05-01+-label%3Abackend%2Fk8s+-label%3Aprovisioner%2Fsalt-masterless+-label%3Adocumentation+-label%3Aprovider%2Fazuredevops+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent+-label%3Abackend%2Fmanta+-label%3Abackend%2Fatlas+-label%3Abackend%2Fetcdv3+-label%3Abackend%2Fetcdv2+-label%3Aconfirmed+-label%3A%22pending+project%22+-label%3Anew+-label%3A%22waiting+for+reproduction%22+-label%3Awaiting-response+-label%3Aexplained+sort%3Acreated-asc+) A core team member initially determines whether the issue is immediately reproducible. If they cannot readily reproduce it, they label it "waiting for reproduction" and correspond with the reporter to describe what is needed. When the issue is reproduced by a core team member, they label it "confirmed". @@ -29,15 +29,15 @@ A core team member initially determines whether the issue is immediately reprodu Note that the link above excludes issues reported before May 2020; this is to avoid including issues that were reported prior to this new process being implemented. [Unreproduced issues reported before May 2020](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+created%3A%3C2020-05-01+-label%3Aprovisioner%2Fsalt-masterless+-label%3Adocumentation+-label%3Aprovider%2Fazuredevops+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent+-label%3Abackend%2Fmanta+-label%3Abackend%2Fatlas+-label%3Abackend%2Fetcdv3+-label%3Abackend%2Fetcdv2+-label%3Aconfirmed+-label%3A%22pending+project%22+-label%3Anew+-label%3A%22waiting+for+reproduction%22+-label%3Awaiting-response+-label%3Aexplained+sort%3Areactions-%2B1-desc) will be triaged as capacity permits. -### 3. Explain or fix [confirmed issues](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+-label%3Aexplained+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+label%3Aconfirmed+-label%3A%22pending+project%22+) +### 3. Explain or fix [confirmed issues](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+-label%3Aexplained+-label%3Abackend%2Foss+-label%3Abackend%2Fk8s+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+label%3Aconfirmed+-label%3A%22pending+project%22+) The next step for confirmed issues is to either: * explain why the behavior is expected, label the issue as "working as designed", and close it, or * locate the cause of the defect in the codebase. When the defect is located, and that description is posted on the issue, the issue is labeled "explained". In many cases, this step will get skipped if the fix is obvious, and engineers will jump forward and make a PR. - [Confirmed crashes](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Acrash+label%3Abug+-label%3Aexplained+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+label%3Aconfirmed+-label%3A%22pending+project%22+) should generally be considered high impact + [Confirmed crashes](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Acrash+label%3Abug+-label%3Abackend%2Fk8s+-label%3Aexplained+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+label%3Aconfirmed+-label%3A%22pending+project%22+) should generally be considered high impact -### 4. The last step for [explained issues](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+label%3Aexplained+no%3Amilestone+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+label%3Aconfirmed+-label%3A%22pending+project%22+) is to make a PR to fix them. +### 4. The last step for [explained issues](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+label%3Aexplained+no%3Amilestone+-label%3Abackend%2Fk8s+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+label%3Aconfirmed+-label%3A%22pending+project%22+) is to make a PR to fix them. Explained issues that are expected to be fixed in a future release should be assigned to a milestone @@ -54,23 +54,23 @@ working as designed | confirmed as reported and closed because the behavior pending project | issue is confirmed but will require a significant project to fix ## Lack of response and unreproducible issues -When bugs that have been [labeled waiting response](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent+-label%3Abackend%2Fmanta+-label%3Abackend%2Fatlas+-label%3Abackend%2Fetcdv3+-label%3Abackend%2Fetcdv2+-label%3Aconfirmed+-label%3A%22pending+project%22+-label%3A%22waiting+for+reproduction%22+label%3Awaiting-response+-label%3Aexplained+sort%3Aupdated-asc) or [labeled "waiting for reproduction"](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent+-label%3Abackend%2Fmanta+-label%3Abackend%2Fatlas+-label%3Abackend%2Fetcdv3+-label%3Abackend%2Fetcdv2+-label%3Aconfirmed+-label%3A%22pending+project%22+label%3A%22waiting+for+reproduction%22+-label%3Aexplained+sort%3Aupdated-asc+) for more than 30 days, we'll use our best judgement to determine whether it's more helpful to close it or prompt the reporter again. If they again go without a response for 30 days, they can be closed with a polite message explaining why and inviting the person to submit the needed information or reproduction case in the future. +When bugs that have been [labeled waiting response](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+-label%3Abackend%2Foss+-label%3Abackend%2Fk8s+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent+-label%3Abackend%2Fmanta+-label%3Abackend%2Fatlas+-label%3Abackend%2Fetcdv3+-label%3Abackend%2Fetcdv2+-label%3Aconfirmed+-label%3A%22pending+project%22+-label%3A%22waiting+for+reproduction%22+label%3Awaiting-response+-label%3Aexplained+sort%3Aupdated-asc+) or [labeled "waiting for reproduction"](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent+-label%3Abackend%2Fmanta+-label%3Abackend%2Fatlas+-label%3Abackend%2Fetcdv3+-label%3Abackend%2Fetcdv2+-label%3Aconfirmed+-label%3A%22pending+project%22+label%3A%22waiting+for+reproduction%22+-label%3Aexplained+sort%3Aupdated-asc+) for more than 30 days, we'll use our best judgement to determine whether it's more helpful to close it or prompt the reporter again. If they again go without a response for 30 days, they can be closed with a polite message explaining why and inviting the person to submit the needed information or reproduction case in the future. The intent of this process is to get fix the maximum number of bugs in Terraform as quickly as possible, and having un-actionable bug reports makes it harder for Terraform Core team members and community contributors to find bugs they can actually work on. ## Helpful GitHub Filters ### Triage Process -1. [Newly created issues](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Anew+label%3Abug+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+-label%3Aconfirmed+-label%3A%22pending+project%22+-label%3A%22waiting+for+reproduction%22+-label%3A%22waiting-response%22+-label%3Aexplained) require initial filtering. -2. Clarify [unreproduced issues](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+created%3A%3E2020-05-01+-label%3Aprovisioner%2Fsalt-masterless+-label%3Adocumentation+-label%3Aprovider%2Fazuredevops+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent+-label%3Abackend%2Fmanta+-label%3Abackend%2Fatlas+-label%3Abackend%2Fetcdv3+-label%3Abackend%2Fetcdv2+-label%3Aconfirmed+-label%3A%22pending+project%22+-label%3Anew+-label%3A%22waiting+for+reproduction%22+-label%3Awaiting-response+-label%3Aexplained+sort%3Acreated-asc) -3. Explain or fix [confirmed issues](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+-label%3Aexplained+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+label%3Aconfirmed+-label%3A%22pending+project%22+). Prioritize [confirmed crashes](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Acrash+label%3Abug+-label%3Aexplained+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+label%3Aconfirmed+-label%3A%22pending+project%22+). -4. Fix [explained issues](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+label%3Aexplained+no%3Amilestone+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+label%3Aconfirmed+-label%3A%22pending+project%22+) +1. [Newly created issues](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Anew+label%3Abug+-label%3Abackend%2Foss+-label%3Abackend%2Fk8s+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+-label%3Aconfirmed+-label%3A%22pending+project%22+-label%3A%22waiting+for+reproduction%22+-label%3A%22waiting-response%22+-label%3Aexplained+) require initial filtering. +2. Clarify [unreproduced issues](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+created%3A%3E2020-05-01+-label%3Abackend%2Fk8s+-label%3Aprovisioner%2Fsalt-masterless+-label%3Adocumentation+-label%3Aprovider%2Fazuredevops+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent+-label%3Abackend%2Fmanta+-label%3Abackend%2Fatlas+-label%3Abackend%2Fetcdv3+-label%3Abackend%2Fetcdv2+-label%3Aconfirmed+-label%3A%22pending+project%22+-label%3Anew+-label%3A%22waiting+for+reproduction%22+-label%3Awaiting-response+-label%3Aexplained+sort%3Acreated-asc+) +3. Explain or fix [confirmed issues](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+-label%3Aexplained+-label%3Abackend%2Fk8s+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+label%3Aconfirmed+-label%3A%22pending+project%22+). Prioritize [confirmed crashes](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Acrash+label%3Abug+-label%3Aexplained+-label%3Abackend%2Fk8s+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+label%3Aconfirmed+-label%3A%22pending+project%22+). +4. Fix [explained issues](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+label%3Aexplained+no%3Amilestone+-label%3Abackend%2Fk8s+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+label%3Aconfirmed+-label%3A%22pending+project%22+) ### Other Backlog -[Confirmed needs for documentation fixes](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+label%3Adocumentation++label%3Aconfirmed+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+) +[Confirmed needs for documentation fixes](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+label%3Adocumentation++label%3Aconfirmed+-label%3Abackend%2Fk8s+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+) -[Confirmed bugs that will require significant projects to fix](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+label%3Aconfirmed+label%3A%22pending+project%22++-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2) +[Confirmed bugs that will require significant projects to fix](https://github.com/hashicorp/terraform/issues?q=is%3Aopen+label%3Abug+label%3Aconfirmed+label%3A%22pending+project%22+-label%3Abackend%2Fk8s+-label%3Abackend%2Foss+-label%3Abackend%2Fazure+-label%3Abackend%2Fs3+-label%3Abackend%2Fgcs+-label%3Abackend%2Fconsul+-label%3Abackend%2Fartifactory+-label%3Aterraform-cloud+-label%3Abackend%2Fremote+-label%3Abackend%2Fswift+-label%3Abackend%2Fpg+-label%3Abackend%2Ftencent++-label%3Abackend%2Fmanta++-label%3Abackend%2Fatlas++-label%3Abackend%2Fetcdv3++-label%3Abackend%2Fetcdv2+) ### Milestone Use diff --git a/vendor/github.com/hashicorp/terraform/CHANGELOG.md b/vendor/github.com/hashicorp/terraform/CHANGELOG.md index 641c916d..f21d50a9 100644 --- a/vendor/github.com/hashicorp/terraform/CHANGELOG.md +++ b/vendor/github.com/hashicorp/terraform/CHANGELOG.md @@ -1,17 +1,14 @@ -## 0.15.0 (Unreleased) +## Next Major Release -ENHANCEMENTS: +NEW FEATURES: -* Improved support for Windows console UI, including bold colors and underline for HCL diagnostics [GH-26588] - -BUG FIXES: - -* command: Exit with an error if unable to gather input from the UI. For example, this may happen when running in a non-interactive environment but without `-input=false`. Previously Terraform would interpret these errors as empty strings, which could be confusing. [GH-26509] +* lang/funcs: add a new `type()` function, only available in `terraform console` [GH-28501] ## Previous Releases For information on prior major releases, see their changelogs: +* [v0.15](https://github.com/hashicorp/terraform/blob/v0.15/CHANGELOG.md) * [v0.14](https://github.com/hashicorp/terraform/blob/v0.14/CHANGELOG.md) * [v0.13](https://github.com/hashicorp/terraform/blob/v0.13/CHANGELOG.md) * [v0.12](https://github.com/hashicorp/terraform/blob/v0.12/CHANGELOG.md) diff --git a/vendor/github.com/hashicorp/terraform/Dockerfile b/vendor/github.com/hashicorp/terraform/Dockerfile index 58f8f752..1e1bb976 100644 --- a/vendor/github.com/hashicorp/terraform/Dockerfile +++ b/vendor/github.com/hashicorp/terraform/Dockerfile @@ -7,7 +7,7 @@ # the officially-released binary from releases.hashicorp.com and are # built by the (closed-source) official release process. -FROM golang:alpine +FROM docker.mirror.hashicorp.services/golang:alpine LABEL maintainer="HashiCorp Terraform Team " RUN apk add --no-cache git bash openssh diff --git a/vendor/github.com/hashicorp/terraform/Makefile b/vendor/github.com/hashicorp/terraform/Makefile index 0324eca9..d26ee96e 100644 --- a/vendor/github.com/hashicorp/terraform/Makefile +++ b/vendor/github.com/hashicorp/terraform/Makefile @@ -6,12 +6,6 @@ VERSION?="0.3.44" # "make protobuf". generate: go generate ./... - # go fmt doesn't support -mod=vendor but it still wants to populate the - # module cache with everything in go.mod even though formatting requires - # no dependencies, and so we're disabling modules mode for this right - # now until the "go fmt" behavior is rationalized to either support the - # -mod= argument or _not_ try to install things. - GO111MODULE=off go fmt command/internal_plugin_list.go > /dev/null # We separate the protobuf generation because most development tasks on # Terraform do not involve changing protobuf files and protoc is not a @@ -48,29 +42,6 @@ endif --workdir /terraform-website \ hashicorp/middleman-hashicorp:${VERSION} -website-test: -ifeq (,$(wildcard $(GOPATH)/src/$(WEBSITE_REPO))) - echo "$(WEBSITE_REPO) not found in your GOPATH (necessary for layouts and assets), get-ting..." - git clone https://$(WEBSITE_REPO) $(GOPATH)/src/$(WEBSITE_REPO) -endif - $(eval WEBSITE_PATH := $(GOPATH)/src/$(WEBSITE_REPO)) - @echo "==> Testing core website in Docker..." - -@docker stop "tf-website-core-temp" - @docker run \ - --detach \ - --rm \ - --name "tf-website-core-temp" \ - --publish "4567:4567" \ - --volume "$(shell pwd)/website:/website" \ - --volume "$(shell pwd):/ext/terraform" \ - --volume "$(WEBSITE_PATH)/content:/terraform-website" \ - --volume "$(WEBSITE_PATH)/content/source/assets:/website/docs/assets" \ - --volume "$(WEBSITE_PATH)/content/source/layouts:/website/docs/layouts" \ - --workdir /terraform-website \ - hashicorp/middleman-hashicorp:${VERSION} - $(WEBSITE_PATH)/content/scripts/check-links.sh "http://127.0.0.1:4567" "/" "/docs/providers/*" - @docker stop "tf-website-core-temp" - # disallow any parallelism (-j) for Make. This is necessary since some # commands during the build process create temporary files that collide # under parallel conditions. diff --git a/vendor/github.com/hashicorp/terraform/README.md b/vendor/github.com/hashicorp/terraform/README.md index 90057764..50d33ab7 100644 --- a/vendor/github.com/hashicorp/terraform/README.md +++ b/vendor/github.com/hashicorp/terraform/README.md @@ -36,11 +36,11 @@ Show off your Terraform knowledge by passing a certification exam. Visit the [ce Developing Terraform -------------------- -This repository contains only Terraform core, which includes the command line interface and the main graph engine. Providers are implemented as plugins that each have their own repository in [the `terraform-providers` organization](https://github.com/terraform-providers) on GitHub. Instructions for developing each provider are in the associated README file. For more information, see [the provider development overview](https://www.terraform.io/docs/plugins/provider.html). +This repository contains only Terraform core, which includes the command line interface and the main graph engine. Providers are implemented as plugins, and Terraform can automatically download providers that are published on [the Terraform Registry](https://registry.terraform.io). HashiCorp develops some providers, and others are developed by other organizations. For more information, see [Extending Terraform](https://www.terraform.io/docs/extend/index.html). To learn more about compiling Terraform and contributing suggested changes, please refer to [the contributing guide](.github/CONTRIBUTING.md). To learn more about how we handle bug reports, please read the [bug triage guide](./BUGPROCESS.md). ## License -[Mozilla Public License v2.0](https://github.com/hashicorp/terraform/blob/master/LICENSE) +[Mozilla Public License v2.0](https://github.com/hashicorp/terraform/blob/main/LICENSE) diff --git a/vendor/github.com/hashicorp/terraform/addrs/module.go b/vendor/github.com/hashicorp/terraform/addrs/module.go index 2a3ffe3f..18f3dbec 100644 --- a/vendor/github.com/hashicorp/terraform/addrs/module.go +++ b/vendor/github.com/hashicorp/terraform/addrs/module.go @@ -41,7 +41,15 @@ func (m Module) String() string { } func (m Module) Equal(other Module) bool { - return m.String() == other.String() + if len(m) != len(other) { + return false + } + for i := range m { + if m[i] != other[i] { + return false + } + } + return true } func (m Module) targetableSigil() { diff --git a/vendor/github.com/hashicorp/terraform/addrs/module_instance.go b/vendor/github.com/hashicorp/terraform/addrs/module_instance.go index 75c69254..396d5f6e 100644 --- a/vendor/github.com/hashicorp/terraform/addrs/module_instance.go +++ b/vendor/github.com/hashicorp/terraform/addrs/module_instance.go @@ -1,8 +1,8 @@ package addrs import ( - "bytes" "fmt" + "strings" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" @@ -82,6 +82,7 @@ func parseModuleInstancePrefix(traversal hcl.Traversal) (ModuleInstance, hcl.Tra var mi ModuleInstance var diags tfdiags.Diagnostics +LOOP: for len(remain) > 0 { var next string switch tt := remain[0].(type) { @@ -96,7 +97,7 @@ func parseModuleInstancePrefix(traversal hcl.Traversal) (ModuleInstance, hcl.Tra Detail: "Module address prefix must be followed by dot and then a name.", Subject: remain[0].SourceRange().Ptr(), }) - break + break LOOP } if next != "module" { @@ -129,7 +130,7 @@ func parseModuleInstancePrefix(traversal hcl.Traversal) (ModuleInstance, hcl.Tra Detail: "Prefix \"module.\" must be followed by a module name.", Subject: remain[0].SourceRange().Ptr(), }) - break + break LOOP } remain = remain[1:] step := ModuleInstanceStep{ @@ -250,7 +251,17 @@ func (m ModuleInstance) Parent() ModuleInstance { // // The address of the root module has the empty string as its representation. func (m ModuleInstance) String() string { - var buf bytes.Buffer + if len(m) == 0 { + return "" + } + // Calculate minimal necessary space (no instance keys). + l := 0 + for _, step := range m { + l += len(step.Name) + } + buf := strings.Builder{} + // 8 is len(".module.") which separates entries. + buf.Grow(l + len(m)*8) sep := "" for _, step := range m { buf.WriteString(sep) @@ -267,7 +278,16 @@ func (m ModuleInstance) String() string { // Equal returns true if the receiver and the given other value // contains the exact same parts. func (m ModuleInstance) Equal(o ModuleInstance) bool { - return m.String() == o.String() + if len(m) != len(o) { + return false + } + + for i := range m { + if m[i] != o[i] { + return false + } + } + return true } // Less returns true if the receiver should sort before the given other value diff --git a/vendor/github.com/hashicorp/terraform/addrs/module_instance_test.go b/vendor/github.com/hashicorp/terraform/addrs/module_instance_test.go new file mode 100644 index 00000000..2334f28f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/addrs/module_instance_test.go @@ -0,0 +1,93 @@ +package addrs + +import ( + "fmt" + "testing" +) + +func TestModuleInstanceEqual_true(t *testing.T) { + addrs := []string{ + "module.foo", + "module.foo.module.bar", + "module.foo[1].module.bar", + `module.foo["a"].module.bar["b"]`, + `module.foo["a"].module.bar.module.baz[3]`, + } + for _, m := range addrs { + t.Run(m, func(t *testing.T) { + addr, diags := ParseModuleInstanceStr(m) + if len(diags) > 0 { + t.Fatalf("unexpected diags: %s", diags.Err()) + } + if !addr.Equal(addr) { + t.Fatalf("expected %#v to be equal to itself", addr) + } + }) + } +} + +func TestModuleInstanceEqual_false(t *testing.T) { + testCases := []struct { + left string + right string + }{ + { + "module.foo", + "module.bar", + }, + { + "module.foo", + "module.foo.module.bar", + }, + { + "module.foo[1]", + "module.bar[1]", + }, + { + `module.foo[1]`, + `module.foo["1"]`, + }, + { + "module.foo.module.bar", + "module.foo[1].module.bar", + }, + { + `module.foo.module.bar`, + `module.foo["a"].module.bar`, + }, + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("%s = %s", tc.left, tc.right), func(t *testing.T) { + left, diags := ParseModuleInstanceStr(tc.left) + if len(diags) > 0 { + t.Fatalf("unexpected diags parsing %s: %s", tc.left, diags.Err()) + } + right, diags := ParseModuleInstanceStr(tc.right) + if len(diags) > 0 { + t.Fatalf("unexpected diags parsing %s: %s", tc.right, diags.Err()) + } + + if left.Equal(right) { + t.Fatalf("expected %#v not to be equal to %#v", left, right) + } + + if right.Equal(left) { + t.Fatalf("expected %#v not to be equal to %#v", right, left) + } + }) + } +} + +func BenchmarkStringShort(b *testing.B) { + addr, _ := ParseModuleInstanceStr(`module.foo`) + for n := 0; n < b.N; n++ { + addr.String() + } +} + +func BenchmarkStringLong(b *testing.B) { + addr, _ := ParseModuleInstanceStr(`module.southamerica-brazil-region.module.user-regional-desktops.module.user-name`) + for n := 0; n < b.N; n++ { + addr.String() + } +} diff --git a/vendor/github.com/hashicorp/terraform/addrs/module_test.go b/vendor/github.com/hashicorp/terraform/addrs/module_test.go new file mode 100644 index 00000000..829704fa --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/addrs/module_test.go @@ -0,0 +1,57 @@ +package addrs + +import ( + "fmt" + "testing" +) + +func TestModuleEqual_true(t *testing.T) { + modules := []Module{ + RootModule, + {"a"}, + {"a", "b"}, + {"a", "b", "c"}, + } + for _, m := range modules { + t.Run(m.String(), func(t *testing.T) { + if !m.Equal(m) { + t.Fatalf("expected %#v to be equal to itself", m) + } + }) + } +} + +func TestModuleEqual_false(t *testing.T) { + testCases := []struct { + left Module + right Module + }{ + { + RootModule, + Module{"a"}, + }, + { + Module{"a"}, + Module{"b"}, + }, + { + Module{"a"}, + Module{"a", "a"}, + }, + { + Module{"a", "b"}, + Module{"a", "B"}, + }, + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("%s = %s", tc.left, tc.right), func(t *testing.T) { + if tc.left.Equal(tc.right) { + t.Fatalf("expected %#v not to be equal to %#v", tc.left, tc.right) + } + + if tc.right.Equal(tc.left) { + t.Fatalf("expected %#v not to be equal to %#v", tc.right, tc.left) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/addrs/output_value.go b/vendor/github.com/hashicorp/terraform/addrs/output_value.go index f97eca19..d0dfca56 100644 --- a/vendor/github.com/hashicorp/terraform/addrs/output_value.go +++ b/vendor/github.com/hashicorp/terraform/addrs/output_value.go @@ -56,6 +56,10 @@ func (v AbsOutputValue) String() string { return fmt.Sprintf("%s.%s", v.Module.String(), v.OutputValue.String()) } +func (v AbsOutputValue) Equal(o AbsOutputValue) bool { + return v.OutputValue == o.OutputValue && v.Module.Equal(o.Module) +} + // ModuleCallOutput converts an AbsModuleOutput into a ModuleCallOutput, // returning also the module instance that the ModuleCallOutput is relative // to. diff --git a/vendor/github.com/hashicorp/terraform/addrs/output_value_test.go b/vendor/github.com/hashicorp/terraform/addrs/output_value_test.go new file mode 100644 index 00000000..e56d4ca3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/addrs/output_value_test.go @@ -0,0 +1,65 @@ +package addrs + +import ( + "fmt" + "testing" +) + +func TestAbsOutputValueInstanceEqual_true(t *testing.T) { + foo, diags := ParseModuleInstanceStr("module.foo") + if len(diags) > 0 { + t.Fatalf("unexpected diags: %s", diags.Err()) + } + foobar, diags := ParseModuleInstanceStr("module.foo[1].module.bar") + if len(diags) > 0 { + t.Fatalf("unexpected diags: %s", diags.Err()) + } + + ovs := []AbsOutputValue{ + foo.OutputValue("a"), + foobar.OutputValue("b"), + } + for _, r := range ovs { + t.Run(r.String(), func(t *testing.T) { + if !r.Equal(r) { + t.Fatalf("expected %#v to be equal to itself", r) + } + }) + } +} + +func TestAbsOutputValueInstanceEqual_false(t *testing.T) { + foo, diags := ParseModuleInstanceStr("module.foo") + if len(diags) > 0 { + t.Fatalf("unexpected diags: %s", diags.Err()) + } + foobar, diags := ParseModuleInstanceStr("module.foo[1].module.bar") + if len(diags) > 0 { + t.Fatalf("unexpected diags: %s", diags.Err()) + } + + testCases := []struct { + left AbsOutputValue + right AbsOutputValue + }{ + { + foo.OutputValue("a"), + foo.OutputValue("b"), + }, + { + foo.OutputValue("a"), + foobar.OutputValue("a"), + }, + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("%s = %s", tc.left, tc.right), func(t *testing.T) { + if tc.left.Equal(tc.right) { + t.Fatalf("expected %#v not to be equal to %#v", tc.left, tc.right) + } + + if tc.right.Equal(tc.left) { + t.Fatalf("expected %#v not to be equal to %#v", tc.right, tc.left) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/addrs/resource.go b/vendor/github.com/hashicorp/terraform/addrs/resource.go index f0a4f79e..8aa0efc0 100644 --- a/vendor/github.com/hashicorp/terraform/addrs/resource.go +++ b/vendor/github.com/hashicorp/terraform/addrs/resource.go @@ -29,7 +29,7 @@ func (r Resource) String() string { } func (r Resource) Equal(o Resource) bool { - return r.String() == o.String() + return r.Mode == o.Mode && r.Name == o.Name && r.Type == o.Type } // Instance produces the address for a specific instance of the receiver @@ -91,7 +91,7 @@ func (r ResourceInstance) String() string { } func (r ResourceInstance) Equal(o ResourceInstance) bool { - return r.String() == o.String() + return r.Key == o.Key && r.Resource.Equal(o.Resource) } // Absolute returns an AbsResourceInstance from the receiver and the given module @@ -171,7 +171,7 @@ func (r AbsResource) String() string { } func (r AbsResource) Equal(o AbsResource) bool { - return r.String() == o.String() + return r.Module.Equal(o.Module) && r.Resource.Equal(o.Resource) } // AbsResourceInstance is an absolute address for a resource instance under a @@ -236,7 +236,7 @@ func (r AbsResourceInstance) String() string { } func (r AbsResourceInstance) Equal(o AbsResourceInstance) bool { - return r.String() == o.String() + return r.Module.Equal(o.Module) && r.Resource.Equal(o.Resource) } // Less returns true if the receiver should sort before the given other value @@ -320,7 +320,7 @@ func (r ConfigResource) String() string { } func (r ConfigResource) Equal(o ConfigResource) bool { - return r.String() == o.String() + return r.Module.Equal(o.Module) && r.Resource.Equal(o.Resource) } // ResourceMode defines which lifecycle applies to a given resource. Each diff --git a/vendor/github.com/hashicorp/terraform/addrs/resource_test.go b/vendor/github.com/hashicorp/terraform/addrs/resource_test.go new file mode 100644 index 00000000..fbaa981f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/addrs/resource_test.go @@ -0,0 +1,276 @@ +package addrs + +import ( + "fmt" + "testing" +) + +func TestResourceEqual_true(t *testing.T) { + resources := []Resource{ + { + Mode: ManagedResourceMode, + Type: "a", + Name: "b", + }, + { + Mode: DataResourceMode, + Type: "a", + Name: "b", + }, + } + for _, r := range resources { + t.Run(r.String(), func(t *testing.T) { + if !r.Equal(r) { + t.Fatalf("expected %#v to be equal to itself", r) + } + }) + } +} + +func TestResourceEqual_false(t *testing.T) { + testCases := []struct { + left Resource + right Resource + }{ + { + Resource{Mode: DataResourceMode, Type: "a", Name: "b"}, + Resource{Mode: ManagedResourceMode, Type: "a", Name: "b"}, + }, + { + Resource{Mode: ManagedResourceMode, Type: "a", Name: "b"}, + Resource{Mode: ManagedResourceMode, Type: "b", Name: "b"}, + }, + { + Resource{Mode: ManagedResourceMode, Type: "a", Name: "b"}, + Resource{Mode: ManagedResourceMode, Type: "a", Name: "c"}, + }, + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("%s = %s", tc.left, tc.right), func(t *testing.T) { + if tc.left.Equal(tc.right) { + t.Fatalf("expected %#v not to be equal to %#v", tc.left, tc.right) + } + + if tc.right.Equal(tc.left) { + t.Fatalf("expected %#v not to be equal to %#v", tc.right, tc.left) + } + }) + } +} + +func TestResourceInstanceEqual_true(t *testing.T) { + resources := []ResourceInstance{ + { + Resource: Resource{ + Mode: ManagedResourceMode, + Type: "a", + Name: "b", + }, + Key: IntKey(0), + }, + { + Resource: Resource{ + Mode: DataResourceMode, + Type: "a", + Name: "b", + }, + Key: StringKey("x"), + }, + } + for _, r := range resources { + t.Run(r.String(), func(t *testing.T) { + if !r.Equal(r) { + t.Fatalf("expected %#v to be equal to itself", r) + } + }) + } +} + +func TestResourceInstanceEqual_false(t *testing.T) { + testCases := []struct { + left ResourceInstance + right ResourceInstance + }{ + { + ResourceInstance{ + Resource: Resource{Mode: DataResourceMode, Type: "a", Name: "b"}, + Key: IntKey(0), + }, + ResourceInstance{ + Resource: Resource{Mode: ManagedResourceMode, Type: "a", Name: "b"}, + Key: IntKey(0), + }, + }, + { + ResourceInstance{ + Resource: Resource{Mode: ManagedResourceMode, Type: "a", Name: "b"}, + Key: IntKey(0), + }, + ResourceInstance{ + Resource: Resource{Mode: ManagedResourceMode, Type: "b", Name: "b"}, + Key: IntKey(0), + }, + }, + { + ResourceInstance{ + Resource: Resource{Mode: ManagedResourceMode, Type: "a", Name: "b"}, + Key: IntKey(0), + }, + ResourceInstance{ + Resource: Resource{Mode: ManagedResourceMode, Type: "a", Name: "c"}, + Key: IntKey(0), + }, + }, + { + ResourceInstance{ + Resource: Resource{Mode: DataResourceMode, Type: "a", Name: "b"}, + Key: IntKey(0), + }, + ResourceInstance{ + Resource: Resource{Mode: DataResourceMode, Type: "a", Name: "b"}, + Key: StringKey("0"), + }, + }, + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("%s = %s", tc.left, tc.right), func(t *testing.T) { + if tc.left.Equal(tc.right) { + t.Fatalf("expected %#v not to be equal to %#v", tc.left, tc.right) + } + + if tc.right.Equal(tc.left) { + t.Fatalf("expected %#v not to be equal to %#v", tc.right, tc.left) + } + }) + } +} + +func TestAbsResourceInstanceEqual_true(t *testing.T) { + managed := Resource{Mode: ManagedResourceMode, Type: "a", Name: "b"} + data := Resource{Mode: DataResourceMode, Type: "a", Name: "b"} + + foo, diags := ParseModuleInstanceStr("module.foo") + if len(diags) > 0 { + t.Fatalf("unexpected diags: %s", diags.Err()) + } + foobar, diags := ParseModuleInstanceStr("module.foo[1].module.bar") + if len(diags) > 0 { + t.Fatalf("unexpected diags: %s", diags.Err()) + } + + instances := []AbsResourceInstance{ + managed.Instance(IntKey(0)).Absolute(foo), + data.Instance(IntKey(0)).Absolute(foo), + managed.Instance(StringKey("a")).Absolute(foobar), + } + for _, r := range instances { + t.Run(r.String(), func(t *testing.T) { + if !r.Equal(r) { + t.Fatalf("expected %#v to be equal to itself", r) + } + }) + } +} + +func TestAbsResourceInstanceEqual_false(t *testing.T) { + managed := Resource{Mode: ManagedResourceMode, Type: "a", Name: "b"} + data := Resource{Mode: DataResourceMode, Type: "a", Name: "b"} + + foo, diags := ParseModuleInstanceStr("module.foo") + if len(diags) > 0 { + t.Fatalf("unexpected diags: %s", diags.Err()) + } + foobar, diags := ParseModuleInstanceStr("module.foo[1].module.bar") + if len(diags) > 0 { + t.Fatalf("unexpected diags: %s", diags.Err()) + } + + testCases := []struct { + left AbsResourceInstance + right AbsResourceInstance + }{ + { + managed.Instance(IntKey(0)).Absolute(foo), + data.Instance(IntKey(0)).Absolute(foo), + }, + { + managed.Instance(IntKey(0)).Absolute(foo), + managed.Instance(IntKey(0)).Absolute(foobar), + }, + { + managed.Instance(IntKey(0)).Absolute(foo), + managed.Instance(StringKey("0")).Absolute(foo), + }, + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("%s = %s", tc.left, tc.right), func(t *testing.T) { + if tc.left.Equal(tc.right) { + t.Fatalf("expected %#v not to be equal to %#v", tc.left, tc.right) + } + + if tc.right.Equal(tc.left) { + t.Fatalf("expected %#v not to be equal to %#v", tc.right, tc.left) + } + }) + } +} + +func TestConfigResourceEqual_true(t *testing.T) { + resources := []ConfigResource{ + { + Resource: Resource{Mode: ManagedResourceMode, Type: "a", Name: "b"}, + Module: RootModule, + }, + { + Resource: Resource{Mode: DataResourceMode, Type: "a", Name: "b"}, + Module: RootModule, + }, + { + Resource: Resource{Mode: ManagedResourceMode, Type: "a", Name: "b"}, + Module: Module{"foo"}, + }, + { + Resource: Resource{Mode: DataResourceMode, Type: "a", Name: "b"}, + Module: Module{"foo"}, + }, + } + for _, r := range resources { + t.Run(r.String(), func(t *testing.T) { + if !r.Equal(r) { + t.Fatalf("expected %#v to be equal to itself", r) + } + }) + } +} + +func TestConfigResourceEqual_false(t *testing.T) { + managed := Resource{Mode: ManagedResourceMode, Type: "a", Name: "b"} + data := Resource{Mode: DataResourceMode, Type: "a", Name: "b"} + + foo := Module{"foo"} + foobar := Module{"foobar"} + testCases := []struct { + left ConfigResource + right ConfigResource + }{ + { + ConfigResource{Resource: managed, Module: foo}, + ConfigResource{Resource: data, Module: foo}, + }, + { + ConfigResource{Resource: managed, Module: foo}, + ConfigResource{Resource: managed, Module: foobar}, + }, + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("%s = %s", tc.left, tc.right), func(t *testing.T) { + if tc.left.Equal(tc.right) { + t.Fatalf("expected %#v not to be equal to %#v", tc.left, tc.right) + } + + if tc.right.Equal(tc.left) { + t.Fatalf("expected %#v not to be equal to %#v", tc.right, tc.left) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/backend/atlas/backend.go b/vendor/github.com/hashicorp/terraform/backend/atlas/backend.go deleted file mode 100644 index a8698382..00000000 --- a/vendor/github.com/hashicorp/terraform/backend/atlas/backend.go +++ /dev/null @@ -1,194 +0,0 @@ -package atlas - -import ( - "fmt" - "net/url" - "os" - "strings" - "sync" - - "github.com/hashicorp/terraform/states/statemgr" - "github.com/hashicorp/terraform/tfdiags" - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform/backend" - "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/states/remote" - "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/cli" - "github.com/mitchellh/colorstring" -) - -const EnvVarToken = "ATLAS_TOKEN" -const EnvVarAddress = "ATLAS_ADDRESS" - -// Backend is an implementation of EnhancedBackend that performs all operations -// in Atlas. State must currently also be stored in Atlas, although it is worth -// investigating in the future if state storage can be external as well. -type Backend struct { - // CLI and Colorize control the CLI output. If CLI is nil then no CLI - // output will be done. If CLIColor is nil then no coloring will be done. - CLI cli.Ui - CLIColor *colorstring.Colorize - - // ContextOpts are the base context options to set when initializing a - // Terraform context. Many of these will be overridden or merged by - // Operation. See Operation for more details. - ContextOpts *terraform.ContextOpts - - //--------------------------------------------------------------- - // Internal fields, do not set - //--------------------------------------------------------------- - // stateClient is the legacy state client, setup in Configure - stateClient *stateClient - - // opLock locks operations - opLock sync.Mutex -} - -var _ backend.Backend = (*Backend)(nil) - -// New returns a new initialized Atlas backend. -func New() *Backend { - return &Backend{} -} - -func (b *Backend) ConfigSchema() *configschema.Block { - return &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "name": { - Type: cty.String, - Required: true, - Description: "Full name of the environment in Terraform Enterprise, such as 'myorg/myenv'", - }, - "access_token": { - Type: cty.String, - Optional: true, - Description: "Access token to use to access Terraform Enterprise; the ATLAS_TOKEN environment variable is used if this argument is not set", - }, - "address": { - Type: cty.String, - Optional: true, - Description: "Base URL for your Terraform Enterprise installation; the ATLAS_ADDRESS environment variable is used if this argument is not set, finally falling back to a default of 'https://atlas.hashicorp.com/' if neither are set.", - }, - }, - } -} - -func (b *Backend) PrepareConfig(obj cty.Value) (cty.Value, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - name := obj.GetAttr("name").AsString() - if ct := strings.Count(name, "/"); ct != 1 { - diags = diags.Append(tfdiags.AttributeValue( - tfdiags.Error, - "Invalid workspace selector", - `The "name" argument must be an organization name and a workspace name separated by a slash, such as "acme/network-production".`, - cty.Path{cty.GetAttrStep{Name: "name"}}, - )) - } - - if v := obj.GetAttr("address"); !v.IsNull() { - addr := v.AsString() - _, err := url.Parse(addr) - if err != nil { - diags = diags.Append(tfdiags.AttributeValue( - tfdiags.Error, - "Invalid Terraform Enterprise URL", - fmt.Sprintf(`The "address" argument must be a valid URL: %s.`, err), - cty.Path{cty.GetAttrStep{Name: "address"}}, - )) - } - } - - return obj, diags -} - -func (b *Backend) Configure(obj cty.Value) tfdiags.Diagnostics { - var diags tfdiags.Diagnostics - - client := &stateClient{ - // This is optionally set during Atlas Terraform runs. - RunId: os.Getenv("ATLAS_RUN_ID"), - } - - name := obj.GetAttr("name").AsString() // assumed valid due to PrepareConfig method - slashIdx := strings.Index(name, "/") - client.User = name[:slashIdx] - client.Name = name[slashIdx+1:] - - if v := obj.GetAttr("access_token"); !v.IsNull() { - client.AccessToken = v.AsString() - } else { - client.AccessToken = os.Getenv(EnvVarToken) - if client.AccessToken == "" { - diags = diags.Append(tfdiags.AttributeValue( - tfdiags.Error, - "Missing Terraform Enterprise access token", - `The "access_token" argument must be set unless the ATLAS_TOKEN environment variable is set to provide the authentication token for Terraform Enterprise.`, - cty.Path{cty.GetAttrStep{Name: "access_token"}}, - )) - } - } - - if v := obj.GetAttr("address"); !v.IsNull() { - addr := v.AsString() - addrURL, err := url.Parse(addr) - if err != nil { - // We already validated the URL in PrepareConfig, so this shouldn't happen - panic(err) - } - client.Server = addr - client.ServerURL = addrURL - } else { - addr := os.Getenv(EnvVarAddress) - if addr == "" { - addr = defaultAtlasServer - } - addrURL, err := url.Parse(addr) - if err != nil { - diags = diags.Append(tfdiags.AttributeValue( - tfdiags.Error, - "Invalid Terraform Enterprise URL", - fmt.Sprintf(`The ATLAS_ADDRESS environment variable must contain a valid URL: %s.`, err), - cty.Path{cty.GetAttrStep{Name: "address"}}, - )) - } - client.Server = addr - client.ServerURL = addrURL - } - - b.stateClient = client - - return diags -} - -func (b *Backend) Workspaces() ([]string, error) { - return nil, backend.ErrWorkspacesNotSupported -} - -func (b *Backend) DeleteWorkspace(name string) error { - return backend.ErrWorkspacesNotSupported -} - -func (b *Backend) StateMgr(name string) (statemgr.Full, error) { - if name != backend.DefaultStateName { - return nil, backend.ErrWorkspacesNotSupported - } - - return &remote.State{Client: b.stateClient}, nil -} - -// Colorize returns the Colorize structure that can be used for colorizing -// output. This is gauranteed to always return a non-nil value and so is useful -// as a helper to wrap any potentially colored strings. -func (b *Backend) Colorize() *colorstring.Colorize { - if b.CLIColor != nil { - return b.CLIColor - } - - return &colorstring.Colorize{ - Colors: colorstring.DefaultColors, - Disable: true, - } -} diff --git a/vendor/github.com/hashicorp/terraform/backend/atlas/backend_test.go b/vendor/github.com/hashicorp/terraform/backend/atlas/backend_test.go deleted file mode 100644 index b85eb340..00000000 --- a/vendor/github.com/hashicorp/terraform/backend/atlas/backend_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package atlas - -import ( - "os" - "testing" - - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform/backend" -) - -func TestImpl(t *testing.T) { - var _ backend.Backend = new(Backend) - var _ backend.CLI = new(Backend) -} - -func TestConfigure_envAddr(t *testing.T) { - defer os.Setenv("ATLAS_ADDRESS", os.Getenv("ATLAS_ADDRESS")) - os.Setenv("ATLAS_ADDRESS", "http://foo.com") - - b := New() - diags := b.Configure(cty.ObjectVal(map[string]cty.Value{ - "name": cty.StringVal("foo/bar"), - "address": cty.NullVal(cty.String), - "access_token": cty.StringVal("placeholder"), - })) - for _, diag := range diags { - t.Error(diag) - } - - if got, want := b.stateClient.Server, "http://foo.com"; got != want { - t.Fatalf("wrong URL %#v; want %#v", got, want) - } -} - -func TestConfigure_envToken(t *testing.T) { - defer os.Setenv("ATLAS_TOKEN", os.Getenv("ATLAS_TOKEN")) - os.Setenv("ATLAS_TOKEN", "foo") - - b := New() - diags := b.Configure(cty.ObjectVal(map[string]cty.Value{ - "name": cty.StringVal("foo/bar"), - "address": cty.NullVal(cty.String), - "access_token": cty.NullVal(cty.String), - })) - for _, diag := range diags { - t.Error(diag) - } - - if got, want := b.stateClient.AccessToken, "foo"; got != want { - t.Fatalf("wrong access token %#v; want %#v", got, want) - } -} diff --git a/vendor/github.com/hashicorp/terraform/backend/atlas/cli.go b/vendor/github.com/hashicorp/terraform/backend/atlas/cli.go deleted file mode 100644 index 5b3656ef..00000000 --- a/vendor/github.com/hashicorp/terraform/backend/atlas/cli.go +++ /dev/null @@ -1,13 +0,0 @@ -package atlas - -import ( - "github.com/hashicorp/terraform/backend" -) - -// backend.CLI impl. -func (b *Backend) CLIInit(opts *backend.CLIOpts) error { - b.CLI = opts.CLI - b.CLIColor = opts.CLIColor - b.ContextOpts = opts.ContextOpts - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/backend/atlas/state_client.go b/vendor/github.com/hashicorp/terraform/backend/atlas/state_client.go deleted file mode 100644 index e954c791..00000000 --- a/vendor/github.com/hashicorp/terraform/backend/atlas/state_client.go +++ /dev/null @@ -1,318 +0,0 @@ -package atlas - -import ( - "bytes" - "context" - "crypto/md5" - "crypto/tls" - "crypto/x509" - "encoding/base64" - "fmt" - "io" - "log" - "net/http" - "net/url" - "os" - "path" - - "github.com/hashicorp/go-cleanhttp" - "github.com/hashicorp/go-retryablehttp" - "github.com/hashicorp/go-rootcerts" - "github.com/hashicorp/terraform/states/remote" - "github.com/hashicorp/terraform/terraform" -) - -const ( - // defaultAtlasServer is used when no address is given - defaultAtlasServer = "https://atlas.hashicorp.com/" - atlasTokenHeader = "X-Atlas-Token" -) - -// AtlasClient implements the Client interface for an Atlas compatible server. -type stateClient struct { - Server string - ServerURL *url.URL - User string - Name string - AccessToken string - RunId string - HTTPClient *retryablehttp.Client - - conflictHandlingAttempted bool -} - -func (c *stateClient) Get() (*remote.Payload, error) { - // Make the HTTP request - req, err := retryablehttp.NewRequest("GET", c.url().String(), nil) - if err != nil { - return nil, fmt.Errorf("Failed to make HTTP request: %v", err) - } - - req.Header.Set(atlasTokenHeader, c.AccessToken) - - // Request the url - client, err := c.http() - if err != nil { - return nil, err - } - resp, err := client.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - // Handle the common status codes - switch resp.StatusCode { - case http.StatusOK: - // Handled after - case http.StatusNoContent: - return nil, nil - case http.StatusNotFound: - return nil, nil - case http.StatusUnauthorized: - return nil, fmt.Errorf("HTTP remote state endpoint requires auth") - case http.StatusForbidden: - return nil, fmt.Errorf("HTTP remote state endpoint invalid auth") - case http.StatusInternalServerError: - return nil, fmt.Errorf("HTTP remote state internal server error") - default: - return nil, fmt.Errorf( - "Unexpected HTTP response code: %d\n\nBody: %s", - resp.StatusCode, c.readBody(resp.Body)) - } - - // Read in the body - buf := bytes.NewBuffer(nil) - if _, err := io.Copy(buf, resp.Body); err != nil { - return nil, fmt.Errorf("Failed to read remote state: %v", err) - } - - // Create the payload - payload := &remote.Payload{ - Data: buf.Bytes(), - } - - if len(payload.Data) == 0 { - return nil, nil - } - - // Check for the MD5 - if raw := resp.Header.Get("Content-MD5"); raw != "" { - md5, err := base64.StdEncoding.DecodeString(raw) - if err != nil { - return nil, fmt.Errorf("Failed to decode Content-MD5 '%s': %v", raw, err) - } - - payload.MD5 = md5 - } else { - // Generate the MD5 - hash := md5.Sum(payload.Data) - payload.MD5 = hash[:] - } - - return payload, nil -} - -func (c *stateClient) Put(state []byte) error { - // Get the target URL - base := c.url() - - // Generate the MD5 - hash := md5.Sum(state) - b64 := base64.StdEncoding.EncodeToString(hash[:]) - - // Make the HTTP client and request - req, err := retryablehttp.NewRequest("PUT", base.String(), bytes.NewReader(state)) - if err != nil { - return fmt.Errorf("Failed to make HTTP request: %v", err) - } - - // Prepare the request - req.Header.Set(atlasTokenHeader, c.AccessToken) - req.Header.Set("Content-MD5", b64) - req.Header.Set("Content-Type", "application/json") - req.ContentLength = int64(len(state)) - - // Make the request - client, err := c.http() - if err != nil { - return err - } - resp, err := client.Do(req) - if err != nil { - return fmt.Errorf("Failed to upload state: %v", err) - } - defer resp.Body.Close() - - // Handle the error codes - switch resp.StatusCode { - case http.StatusOK: - return nil - case http.StatusConflict: - return c.handleConflict(c.readBody(resp.Body), state) - default: - return fmt.Errorf( - "HTTP error: %d\n\nBody: %s", - resp.StatusCode, c.readBody(resp.Body)) - } -} - -func (c *stateClient) Delete() error { - // Make the HTTP request - req, err := retryablehttp.NewRequest("DELETE", c.url().String(), nil) - if err != nil { - return fmt.Errorf("Failed to make HTTP request: %v", err) - } - req.Header.Set(atlasTokenHeader, c.AccessToken) - - // Make the request - client, err := c.http() - if err != nil { - return err - } - resp, err := client.Do(req) - if err != nil { - return fmt.Errorf("Failed to delete state: %v", err) - } - defer resp.Body.Close() - - // Handle the error codes - switch resp.StatusCode { - case http.StatusOK: - return nil - case http.StatusNoContent: - return nil - case http.StatusNotFound: - return nil - default: - return fmt.Errorf( - "HTTP error: %d\n\nBody: %s", - resp.StatusCode, c.readBody(resp.Body)) - } -} - -func (c *stateClient) readBody(b io.Reader) string { - var buf bytes.Buffer - if _, err := io.Copy(&buf, b); err != nil { - return fmt.Sprintf("Error reading body: %s", err) - } - - result := buf.String() - if result == "" { - result = "" - } - - return result -} - -func (c *stateClient) url() *url.URL { - values := url.Values{} - - values.Add("atlas_run_id", c.RunId) - - return &url.URL{ - Scheme: c.ServerURL.Scheme, - Host: c.ServerURL.Host, - Path: path.Join("api/v1/terraform/state", c.User, c.Name), - RawQuery: values.Encode(), - } -} - -func (c *stateClient) http() (*retryablehttp.Client, error) { - if c.HTTPClient != nil { - return c.HTTPClient, nil - } - tlsConfig := &tls.Config{} - err := rootcerts.ConfigureTLS(tlsConfig, &rootcerts.Config{ - CAFile: os.Getenv("ATLAS_CAFILE"), - CAPath: os.Getenv("ATLAS_CAPATH"), - }) - if err != nil { - return nil, err - } - rc := retryablehttp.NewClient() - - rc.CheckRetry = func(ctx context.Context, resp *http.Response, err error) (bool, error) { - if err != nil { - // don't bother retrying if the certs don't match - if err, ok := err.(*url.Error); ok { - if _, ok := err.Err.(x509.UnknownAuthorityError); ok { - return false, nil - } - } - } - return retryablehttp.DefaultRetryPolicy(ctx, resp, err) - } - - t := cleanhttp.DefaultTransport() - t.TLSClientConfig = tlsConfig - rc.HTTPClient.Transport = t - - c.HTTPClient = rc - return rc, nil -} - -// Atlas returns an HTTP 409 - Conflict if the pushed state reports the same -// Serial number but the checksum of the raw content differs. This can -// sometimes happen when Terraform changes state representation internally -// between versions in a way that's semantically neutral but affects the JSON -// output and therefore the checksum. -// -// Here we detect and handle this situation by ticking the serial and retrying -// iff for the previous state and the proposed state: -// -// * the serials match -// * the parsed states are Equal (semantically equivalent) -// -// In other words, in this situation Terraform can override Atlas's detected -// conflict by asserting that the state it is pushing is indeed correct. -func (c *stateClient) handleConflict(msg string, state []byte) error { - log.Printf("[DEBUG] Handling Atlas conflict response: %s", msg) - - if c.conflictHandlingAttempted { - log.Printf("[DEBUG] Already attempted conflict resolution; returning conflict.") - } else { - c.conflictHandlingAttempted = true - log.Printf("[DEBUG] Atlas reported conflict, checking for equivalent states.") - - payload, err := c.Get() - if err != nil { - return conflictHandlingError(err) - } - - currentState, err := terraform.ReadState(bytes.NewReader(payload.Data)) - if err != nil { - return conflictHandlingError(err) - } - - proposedState, err := terraform.ReadState(bytes.NewReader(state)) - if err != nil { - return conflictHandlingError(err) - } - - if statesAreEquivalent(currentState, proposedState) { - log.Printf("[DEBUG] States are equivalent, incrementing serial and retrying.") - proposedState.Serial++ - var buf bytes.Buffer - if err := terraform.WriteState(proposedState, &buf); err != nil { - return conflictHandlingError(err) - - } - return c.Put(buf.Bytes()) - } else { - log.Printf("[DEBUG] States are not equivalent, returning conflict.") - } - } - - return fmt.Errorf( - "Atlas detected a remote state conflict.\n\nMessage: %s", msg) -} - -func conflictHandlingError(err error) error { - return fmt.Errorf( - "Error while handling a conflict response from Atlas: %s", err) -} - -func statesAreEquivalent(current, proposed *terraform.State) bool { - return current.Serial == proposed.Serial && current.Equal(proposed) -} diff --git a/vendor/github.com/hashicorp/terraform/backend/atlas/state_client_test.go b/vendor/github.com/hashicorp/terraform/backend/atlas/state_client_test.go deleted file mode 100644 index eaec7fb7..00000000 --- a/vendor/github.com/hashicorp/terraform/backend/atlas/state_client_test.go +++ /dev/null @@ -1,398 +0,0 @@ -package atlas - -import ( - "bytes" - "context" - "crypto/md5" - "crypto/tls" - "crypto/x509" - "encoding/json" - "errors" - "net/http" - "net/http/httptest" - "net/url" - "os" - "testing" - "time" - - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform/backend" - "github.com/hashicorp/terraform/configs" - "github.com/hashicorp/terraform/helper/acctest" - "github.com/hashicorp/terraform/states/remote" - "github.com/hashicorp/terraform/terraform" -) - -func testStateClient(t *testing.T, c map[string]string) remote.Client { - vals := make(map[string]cty.Value) - for k, s := range c { - vals[k] = cty.StringVal(s) - } - synthBody := configs.SynthBody("", vals) - - b := backend.TestBackendConfig(t, New(), synthBody) - raw, err := b.StateMgr(backend.DefaultStateName) - if err != nil { - t.Fatalf("err: %s", err) - } - - s := raw.(*remote.State) - return s.Client -} - -func TestStateClient_impl(t *testing.T) { - var _ remote.Client = new(stateClient) -} - -func TestStateClient(t *testing.T) { - acctest.RemoteTestPrecheck(t) - - token := os.Getenv("ATLAS_TOKEN") - if token == "" { - t.Skipf("skipping, ATLAS_TOKEN must be set") - } - - client := testStateClient(t, map[string]string{ - "access_token": token, - "name": "hashicorp/test-remote-state", - }) - - remote.TestClient(t, client) -} - -func TestStateClient_noRetryOnBadCerts(t *testing.T) { - acctest.RemoteTestPrecheck(t) - - client := testStateClient(t, map[string]string{ - "access_token": "NOT_REQUIRED", - "name": "hashicorp/test-remote-state", - }) - - ac := client.(*stateClient) - // trigger the StateClient to build the http client and assign HTTPClient - httpClient, err := ac.http() - if err != nil { - t.Fatal(err) - } - - // remove the CA certs from the client - brokenCfg := &tls.Config{ - RootCAs: new(x509.CertPool), - } - httpClient.HTTPClient.Transport.(*http.Transport).TLSClientConfig = brokenCfg - - // Instrument CheckRetry to make sure we didn't retry - retries := 0 - oldCheck := httpClient.CheckRetry - httpClient.CheckRetry = func(ctx context.Context, resp *http.Response, err error) (bool, error) { - if retries > 0 { - t.Fatal("retried after certificate error") - } - retries++ - return oldCheck(ctx, resp, err) - } - - _, err = client.Get() - if err != nil { - if err, ok := err.(*url.Error); ok { - if _, ok := err.Err.(x509.UnknownAuthorityError); ok { - return - } - } - } - - t.Fatalf("expected x509.UnknownAuthorityError, got %v", err) -} - -func TestStateClient_ReportedConflictEqualStates(t *testing.T) { - fakeAtlas := newFakeAtlas(t, testStateModuleOrderChange) - srv := fakeAtlas.Server() - defer srv.Close() - - client := testStateClient(t, map[string]string{ - "access_token": "sometoken", - "name": "someuser/some-test-remote-state", - "address": srv.URL, - }) - - state, err := terraform.ReadState(bytes.NewReader(testStateModuleOrderChange)) - if err != nil { - t.Fatalf("err: %s", err) - } - - var stateJson bytes.Buffer - if err := terraform.WriteState(state, &stateJson); err != nil { - t.Fatalf("err: %s", err) - } - if err := client.Put(stateJson.Bytes()); err != nil { - t.Fatalf("err: %s", err) - } -} - -func TestStateClient_NoConflict(t *testing.T) { - fakeAtlas := newFakeAtlas(t, testStateSimple) - srv := fakeAtlas.Server() - defer srv.Close() - - client := testStateClient(t, map[string]string{ - "access_token": "sometoken", - "name": "someuser/some-test-remote-state", - "address": srv.URL, - }) - - state, err := terraform.ReadState(bytes.NewReader(testStateSimple)) - if err != nil { - t.Fatalf("err: %s", err) - } - - fakeAtlas.NoConflictAllowed(true) - - var stateJson bytes.Buffer - if err := terraform.WriteState(state, &stateJson); err != nil { - t.Fatalf("err: %s", err) - } - - if err := client.Put(stateJson.Bytes()); err != nil { - t.Fatalf("err: %s", err) - } -} - -func TestStateClient_LegitimateConflict(t *testing.T) { - fakeAtlas := newFakeAtlas(t, testStateSimple) - srv := fakeAtlas.Server() - defer srv.Close() - - client := testStateClient(t, map[string]string{ - "access_token": "sometoken", - "name": "someuser/some-test-remote-state", - "address": srv.URL, - }) - - state, err := terraform.ReadState(bytes.NewReader(testStateSimple)) - if err != nil { - t.Fatalf("err: %s", err) - } - - var buf bytes.Buffer - terraform.WriteState(state, &buf) - - // Changing the state but not the serial. Should generate a conflict. - state.RootModule().Outputs["drift"] = &terraform.OutputState{ - Type: "string", - Sensitive: false, - Value: "happens", - } - - var stateJson bytes.Buffer - if err := terraform.WriteState(state, &stateJson); err != nil { - t.Fatalf("err: %s", err) - } - if err := client.Put(stateJson.Bytes()); err == nil { - t.Fatal("Expected error from state conflict, got none.") - } -} - -func TestStateClient_UnresolvableConflict(t *testing.T) { - fakeAtlas := newFakeAtlas(t, testStateSimple) - - // Something unexpected causes Atlas to conflict in a way that we can't fix. - fakeAtlas.AlwaysConflict(true) - - srv := fakeAtlas.Server() - defer srv.Close() - - client := testStateClient(t, map[string]string{ - "access_token": "sometoken", - "name": "someuser/some-test-remote-state", - "address": srv.URL, - }) - - state, err := terraform.ReadState(bytes.NewReader(testStateSimple)) - if err != nil { - t.Fatalf("err: %s", err) - } - - var stateJson bytes.Buffer - if err := terraform.WriteState(state, &stateJson); err != nil { - t.Fatalf("err: %s", err) - } - errCh := make(chan error) - go func() { - defer close(errCh) - if err := client.Put(stateJson.Bytes()); err == nil { - errCh <- errors.New("expected error from state conflict, got none.") - return - } - }() - - select { - case err := <-errCh: - if err != nil { - t.Fatalf("error from anonymous test goroutine: %s", err) - } - case <-time.After(500 * time.Millisecond): - t.Fatalf("Timed out after 500ms, probably because retrying infinitely.") - } -} - -// Stub Atlas HTTP API for a given state JSON string; does checksum-based -// conflict detection equivalent to Atlas's. -type fakeAtlas struct { - state []byte - t *testing.T - - // Used to test that we only do the special conflict handling retry once. - alwaysConflict bool - - // Used to fail the test immediately if a conflict happens. - noConflictAllowed bool -} - -func newFakeAtlas(t *testing.T, state []byte) *fakeAtlas { - return &fakeAtlas{ - state: state, - t: t, - } -} - -func (f *fakeAtlas) Server() *httptest.Server { - return httptest.NewServer(http.HandlerFunc(f.handler)) -} - -func (f *fakeAtlas) CurrentState() *terraform.State { - // we read the state manually here, because terraform may alter state - // during read - currentState := &terraform.State{} - err := json.Unmarshal(f.state, currentState) - if err != nil { - f.t.Fatalf("err: %s", err) - } - return currentState -} - -func (f *fakeAtlas) CurrentSerial() int64 { - return f.CurrentState().Serial -} - -func (f *fakeAtlas) CurrentSum() [md5.Size]byte { - return md5.Sum(f.state) -} - -func (f *fakeAtlas) AlwaysConflict(b bool) { - f.alwaysConflict = b -} - -func (f *fakeAtlas) NoConflictAllowed(b bool) { - f.noConflictAllowed = b -} - -func (f *fakeAtlas) handler(resp http.ResponseWriter, req *http.Request) { - // access tokens should only be sent as a header - if req.FormValue("access_token") != "" { - http.Error(resp, "access_token in request params", http.StatusBadRequest) - return - } - - if req.Header.Get(atlasTokenHeader) == "" { - http.Error(resp, "missing access token", http.StatusBadRequest) - return - } - - switch req.Method { - case "GET": - // Respond with the current stored state. - resp.Header().Set("Content-Type", "application/json") - resp.Write(f.state) - case "PUT": - var buf bytes.Buffer - buf.ReadFrom(req.Body) - sum := md5.Sum(buf.Bytes()) - - // we read the state manually here, because terraform may alter state - // during read - state := &terraform.State{} - err := json.Unmarshal(buf.Bytes(), state) - if err != nil { - f.t.Fatalf("err: %s", err) - } - - conflict := f.CurrentSerial() == state.Serial && f.CurrentSum() != sum - conflict = conflict || f.alwaysConflict - if conflict { - if f.noConflictAllowed { - f.t.Fatal("Got conflict when NoConflictAllowed was set.") - } - http.Error(resp, "Conflict", 409) - } else { - f.state = buf.Bytes() - resp.WriteHeader(200) - } - } -} - -// This is a tfstate file with the module order changed, which is a structural -// but not a semantic difference. Terraform will sort these modules as it -// loads the state. -var testStateModuleOrderChange = []byte( - `{ - "version": 3, - "serial": 1, - "modules": [ - { - "path": [ - "root", - "child2", - "grandchild" - ], - "outputs": { - "foo": { - "sensitive": false, - "type": "string", - "value": "bar" - } - }, - "resources": null - }, - { - "path": [ - "root", - "child1", - "grandchild" - ], - "outputs": { - "foo": { - "sensitive": false, - "type": "string", - "value": "bar" - } - }, - "resources": null - } - ] -} -`) - -var testStateSimple = []byte( - `{ - "version": 3, - "serial": 2, - "lineage": "c00ad9ac-9b35-42fe-846e-b06f0ef877e9", - "modules": [ - { - "path": [ - "root" - ], - "outputs": { - "foo": { - "sensitive": false, - "type": "string", - "value": "bar" - } - }, - "resources": {}, - "depends_on": [] - } - ] -} -`) diff --git a/vendor/github.com/hashicorp/terraform/backend/backend.go b/vendor/github.com/hashicorp/terraform/backend/backend.go index 0394ae29..349fe149 100644 --- a/vendor/github.com/hashicorp/terraform/backend/backend.go +++ b/vendor/github.com/hashicorp/terraform/backend/backend.go @@ -8,11 +8,12 @@ import ( "context" "errors" "io/ioutil" + "log" "os" - "time" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs/configload" "github.com/hashicorp/terraform/configs/configschema" @@ -182,17 +183,21 @@ type Operation struct { // configuration from ConfigDir. ConfigLoader *configload.Loader + // Hooks can be used to perform actions triggered by various events during + // the operation's lifecycle. + Hooks []terraform.Hook + // Plan is a plan that was passed as an argument. This is valid for // plan and apply arguments but may not work for all backends. PlanFile *planfile.Reader // The options below are more self-explanatory and affect the runtime // behavior of the operation. + PlanMode plans.Mode AutoApprove bool - Destroy bool - DestroyForce bool Parallelism int Targets []addrs.Targetable + ForceReplace []addrs.AbsResourceInstance Variables map[string]UnparsedVariableValue // Some operations use root module variables only opportunistically or @@ -205,21 +210,20 @@ type Operation struct { // the variables set in the plan are used instead, and they must be valid. AllowUnsetVariables bool + // View implements the logic for all UI interactions. + View views.Operation + // Input/output/control options. UIIn terraform.UIInput UIOut terraform.UIOutput - // If LockState is true, the Operation must Lock any - // statemgr.Lockers for its duration, and Unlock when complete. - LockState bool - // StateLocker is used to lock the state while providing UI feedback to the - // user. This will be supplied by the Backend itself. + // user. This will be replaced by the Backend to update the context. + // + // If state locking is not necessary, this should be set to a no-op + // implementation of clistate.Locker. StateLocker clistate.Locker - // The duration to retry obtaining a State lock. - StateLockTimeout time.Duration - // Workspace is the name of the workspace that this operation should run // in, which controls which named state is used. Workspace string @@ -241,6 +245,39 @@ func (o *Operation) Config() (*configs.Config, tfdiags.Diagnostics) { return config, diags } +// ReportResult is a helper for the common chore of setting the status of +// a running operation and showing any diagnostics produced during that +// operation. +// +// If the given diagnostics contains errors then the operation's result +// will be set to backend.OperationFailure. It will be set to +// backend.OperationSuccess otherwise. It will then use o.View.Diagnostics +// to show the given diagnostics before returning. +// +// Callers should feel free to do each of these operations separately in +// more complex cases where e.g. diagnostics are interleaved with other +// output, but terminating immediately after reporting error diagnostics is +// common and can be expressed concisely via this method. +func (o *Operation) ReportResult(op *RunningOperation, diags tfdiags.Diagnostics) { + if diags.HasErrors() { + op.Result = OperationFailure + } else { + op.Result = OperationSuccess + } + if o.View != nil { + o.View.Diagnostics(diags) + } else { + // Shouldn't generally happen, but if it does then we'll at least + // make some noise in the logs to help us spot it. + if len(diags) != 0 { + log.Printf( + "[ERROR] Backend needs to report diagnostics but View is not set:\n%s", + diags.ErrWithWarnings(), + ) + } + } +} + // RunningOperation is the result of starting an operation. type RunningOperation struct { // For implementers of a backend, this context should not wrap the diff --git a/vendor/github.com/hashicorp/terraform/backend/cli.go b/vendor/github.com/hashicorp/terraform/backend/cli.go index cd29e386..454473b2 100644 --- a/vendor/github.com/hashicorp/terraform/backend/cli.go +++ b/vendor/github.com/hashicorp/terraform/backend/cli.go @@ -1,9 +1,11 @@ package backend import ( - "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/cli" "github.com/mitchellh/colorstring" + + "github.com/hashicorp/terraform/internal/terminal" + "github.com/hashicorp/terraform/terraform" ) // CLI is an optional interface that can be implemented to be initialized @@ -48,9 +50,11 @@ type CLIOpts struct { CLI cli.Ui CLIColor *colorstring.Colorize - // ShowDiagnostics is a function that will format and print diagnostic - // messages to the UI. - ShowDiagnostics func(vals ...interface{}) + // Streams describes the low-level streams for Stdout, Stderr and Stdin, + // including some metadata about whether they are terminals. Most output + // should go via the object in field CLI above, but Streams can be useful + // for tailoring the output to fit the attached terminal, for example. + Streams *terminal.Streams // StatePath is the local path where state is read from. // diff --git a/vendor/github.com/hashicorp/terraform/backend/init/init.go b/vendor/github.com/hashicorp/terraform/backend/init/init.go index e020b4c8..9e990da0 100644 --- a/vendor/github.com/hashicorp/terraform/backend/init/init.go +++ b/vendor/github.com/hashicorp/terraform/backend/init/init.go @@ -10,7 +10,6 @@ import ( "github.com/hashicorp/terraform/tfdiags" "github.com/zclconf/go-cty/cty" - backendAtlas "github.com/hashicorp/terraform/backend/atlas" backendLocal "github.com/hashicorp/terraform/backend/local" backendRemote "github.com/hashicorp/terraform/backend/remote" backendArtifactory "github.com/hashicorp/terraform/backend/remote-state/artifactory" @@ -56,7 +55,6 @@ func Init(services *disco.Disco) { // Remote State backends. "artifactory": func() backend.Backend { return backendArtifactory.New() }, - "atlas": func() backend.Backend { return backendAtlas.New() }, "azurerm": func() backend.Backend { return backendAzure.New() }, "consul": func() backend.Backend { return backendConsul.New() }, "cos": func() backend.Backend { return backendCos.New() }, diff --git a/vendor/github.com/hashicorp/terraform/backend/init/init_test.go b/vendor/github.com/hashicorp/terraform/backend/init/init_test.go index 2b7571b5..61b100f4 100644 --- a/vendor/github.com/hashicorp/terraform/backend/init/init_test.go +++ b/vendor/github.com/hashicorp/terraform/backend/init/init_test.go @@ -15,7 +15,6 @@ func TestInit_backend(t *testing.T) { }{ {"local", "*local.Local"}, {"remote", "*remote.Remote"}, - {"atlas", "*atlas.Backend"}, {"azurerm", "*azure.Backend"}, {"consul", "*consul.Backend"}, {"cos", "*cos.Backend"}, diff --git a/vendor/github.com/hashicorp/terraform/backend/local/backend.go b/vendor/github.com/hashicorp/terraform/backend/local/backend.go index 866c4899..ecfe0c90 100644 --- a/vendor/github.com/hashicorp/terraform/backend/local/backend.go +++ b/vendor/github.com/hashicorp/terraform/backend/local/backend.go @@ -12,13 +12,11 @@ import ( "sync" "github.com/hashicorp/terraform/backend" - "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/states/statemgr" "github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/tfdiags" - "github.com/mitchellh/cli" - "github.com/mitchellh/colorstring" "github.com/zclconf/go-cty/cty" ) @@ -33,14 +31,6 @@ const ( // locally. This is the "default" backend and implements normal Terraform // behavior as it is well known. type Local struct { - // CLI and Colorize control the CLI output. If CLI is nil then no CLI - // output will be done. If CLIColor is nil then no coloring will be done. - CLI cli.Ui - CLIColor *colorstring.Colorize - - // ShowDiagnostics prints diagnostic messages to the UI. - ShowDiagnostics func(vals ...interface{}) - // The State* paths are set from the backend config, and may be left blank // to use the defaults. If the actual paths for the local backend state are // needed, use the StatePaths method. @@ -92,15 +82,6 @@ type Local struct { // If this is nil, local performs normal state loading and storage. Backend backend.Backend - // RunningInAutomation indicates that commands are being run by an - // automated system rather than directly at a command prompt. - // - // This is a hint not to produce messages that expect that a user can - // run a follow-up command, perhaps because Terraform is running in - // some sort of workflow automation tool that abstracts away the - // exact commands that are being run. - RunningInAutomation bool - // opLock locks operations opLock sync.Mutex } @@ -288,6 +269,10 @@ func (b *Local) StateMgr(name string) (statemgr.Full, error) { // the structure with the following rules. If a rule isn't specified and the // name conflicts, assume that the field is overwritten if set. func (b *Local) Operation(ctx context.Context, op *backend.Operation) (*backend.RunningOperation, error) { + if op.View == nil { + panic("Operation called with nil View") + } + // Determine the function to call for our operation var f func(context.Context, context.Context, *backend.Operation, *backend.RunningOperation) switch op.Type { @@ -324,11 +309,7 @@ func (b *Local) Operation(ctx context.Context, op *backend.Operation) (*backend. cancelCtx, cancel := context.WithCancel(context.Background()) runningOp.Cancel = cancel - if op.LockState { - op.StateLocker = clistate.NewLocker(stopCtx, op.StateLockTimeout, b.CLI, b.Colorize()) - } else { - op.StateLocker = clistate.NewNoopLocker() - } + op.StateLocker = op.StateLocker.WithContext(stopCtx) // Do it go func() { @@ -351,14 +332,13 @@ func (b *Local) opWait( stopCtx context.Context, cancelCtx context.Context, tfCtx *terraform.Context, - opStateMgr statemgr.Persister) (canceled bool) { + opStateMgr statemgr.Persister, + view views.Operation) (canceled bool) { // Wait for the operation to finish or for us to be interrupted so // we can handle it properly. select { case <-stopCtx.Done(): - if b.CLI != nil { - b.CLI.Output("Stopping operation...") - } + view.Stopping() // try to force a PersistState just in case the process is terminated // before we can complete. @@ -366,9 +346,13 @@ func (b *Local) opWait( // We can't error out from here, but warn the user if there was an error. // If this isn't transient, we will catch it again below, and // attempt to save the state another way. - if b.CLI != nil { - b.CLI.Error(fmt.Sprintf(earlyStateWriteErrorFmt, err)) - } + var diags tfdiags.Diagnostics + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Error saving current state", + fmt.Sprintf(earlyStateWriteErrorFmt, err), + )) + view.Diagnostics(diags) } // Stop execution @@ -393,53 +377,6 @@ func (b *Local) opWait( return } -// ReportResult is a helper for the common chore of setting the status of -// a running operation and showing any diagnostics produced during that -// operation. -// -// If the given diagnostics contains errors then the operation's result -// will be set to backend.OperationFailure. It will be set to -// backend.OperationSuccess otherwise. It will then use b.ShowDiagnostics -// to show the given diagnostics before returning. -// -// Callers should feel free to do each of these operations separately in -// more complex cases where e.g. diagnostics are interleaved with other -// output, but terminating immediately after reporting error diagnostics is -// common and can be expressed concisely via this method. -func (b *Local) ReportResult(op *backend.RunningOperation, diags tfdiags.Diagnostics) { - if diags.HasErrors() { - op.Result = backend.OperationFailure - } else { - op.Result = backend.OperationSuccess - } - if b.ShowDiagnostics != nil { - b.ShowDiagnostics(diags) - } else { - // Shouldn't generally happen, but if it does then we'll at least - // make some noise in the logs to help us spot it. - if len(diags) != 0 { - log.Printf( - "[ERROR] Local backend needs to report diagnostics but ShowDiagnostics is not set:\n%s", - diags.ErrWithWarnings(), - ) - } - } -} - -// Colorize returns the Colorize structure that can be used for colorizing -// output. This is guaranteed to always return a non-nil value and so is useful -// as a helper to wrap any potentially colored strings. -func (b *Local) Colorize() *colorstring.Colorize { - if b.CLIColor != nil { - return b.CLIColor - } - - return &colorstring.Colorize{ - Colors: colorstring.DefaultColors, - Disable: true, - } -} - // StatePaths returns the StatePath, StateOutPath, and StateBackupPath as // configured from the CLI. func (b *Local) StatePaths(name string) (stateIn, stateOut, backupOut string) { @@ -544,3 +481,7 @@ func (b *Local) stateWorkspaceDir() string { return DefaultWorkspaceDir } + +const earlyStateWriteErrorFmt = `Error: %s + +Terraform encountered an error attempting to save the state before cancelling the current operation. Once the operation is complete another attempt will be made to save the final state.` diff --git a/vendor/github.com/hashicorp/terraform/backend/local/backend_apply.go b/vendor/github.com/hashicorp/terraform/backend/local/backend_apply.go index 7e177eac..86104ef5 100644 --- a/vendor/github.com/hashicorp/terraform/backend/local/backend_apply.go +++ b/vendor/github.com/hashicorp/terraform/backend/local/backend_apply.go @@ -1,14 +1,14 @@ package local import ( - "bytes" "context" - "errors" "fmt" "log" "github.com/hashicorp/errwrap" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/command/views" + "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/states/statefile" "github.com/hashicorp/terraform/states/statemgr" @@ -27,7 +27,7 @@ func (b *Local) opApply( // If we have a nil module at this point, then set it to an empty tree // to avoid any potential crashes. - if op.PlanFile == nil && !op.Destroy && !op.HasConfig() { + if op.PlanFile == nil && op.PlanMode != plans.DestroyMode && !op.HasConfig() { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, "No configuration files", @@ -35,33 +35,26 @@ func (b *Local) opApply( "would mark everything for destruction, which is normally not what is desired. "+ "If you would like to destroy everything, run 'terraform destroy' instead.", )) - b.ReportResult(runningOp, diags) + op.ReportResult(runningOp, diags) return } - // Setup our count hook that keeps track of resource changes - countHook := new(CountHook) stateHook := new(StateHook) - if b.ContextOpts == nil { - b.ContextOpts = new(terraform.ContextOpts) - } - old := b.ContextOpts.Hooks - defer func() { b.ContextOpts.Hooks = old }() - b.ContextOpts.Hooks = append(b.ContextOpts.Hooks, countHook, stateHook) + op.Hooks = append(op.Hooks, stateHook) // Get our context tfCtx, _, opState, contextDiags := b.context(op) diags = diags.Append(contextDiags) if contextDiags.HasErrors() { - b.ReportResult(runningOp, diags) + op.ReportResult(runningOp, diags) return } // the state was locked during succesfull context creation; unlock the state // when the operation completes defer func() { - err := op.StateLocker.Unlock(nil) - if err != nil { - b.ShowDiagnostics(err) + diags := op.StateLocker.Unlock() + if diags.HasErrors() { + op.View.Diagnostics(diags) runningOp.Result = backend.OperationFailure } }() @@ -75,16 +68,16 @@ func (b *Local) opApply( plan, planDiags := tfCtx.Plan() diags = diags.Append(planDiags) if planDiags.HasErrors() { - b.ReportResult(runningOp, diags) + op.ReportResult(runningOp, diags) return } trivialPlan := plan.Changes.Empty() hasUI := op.UIOut != nil && op.UIIn != nil - mustConfirm := hasUI && ((op.Destroy && (!op.DestroyForce && !op.AutoApprove)) || (!op.Destroy && !op.AutoApprove && !trivialPlan)) + mustConfirm := hasUI && !op.AutoApprove && !trivialPlan if mustConfirm { var desc, query string - if op.Destroy { + if op.PlanMode == plans.DestroyMode { if op.Workspace != "default" { query = "Do you really want to destroy all resources in workspace \"" + op.Workspace + "\"?" } else { @@ -103,41 +96,57 @@ func (b *Local) opApply( } if !trivialPlan { - // Display the plan of what we are going to apply/destroy. - b.renderPlan(plan, runningOp.State, tfCtx.Schemas()) - b.CLI.Output("") + op.View.Plan(plan, tfCtx.Schemas()) } // We'll show any accumulated warnings before we display the prompt, // so the user can consider them when deciding how to answer. if len(diags) > 0 { - b.ShowDiagnostics(diags) + op.View.Diagnostics(diags) diags = nil // reset so we won't show the same diagnostics again later } v, err := op.UIIn.Input(stopCtx, &terraform.InputOpts{ Id: "approve", - Query: query, + Query: "\n" + query, Description: desc, }) if err != nil { diags = diags.Append(errwrap.Wrapf("Error asking for approval: {{err}}", err)) - b.ReportResult(runningOp, diags) + op.ReportResult(runningOp, diags) return } if v != "yes" { - if op.Destroy { - b.CLI.Info("Destroy cancelled.") - } else { - b.CLI.Info("Apply cancelled.") - } + op.View.Cancelled(op.PlanMode) runningOp.Result = backend.OperationFailure return } + } else { + for _, change := range plan.Changes.Resources { + if change.Action != plans.NoOp { + op.View.PlannedChange(change) + } + } + } + } else { + plan, err := op.PlanFile.ReadPlan() + if err != nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Invalid plan file", + fmt.Sprintf("Failed to read plan from plan file: %s.", err), + )) + op.ReportResult(runningOp, diags) + return + } + for _, change := range plan.Changes.Resources { + if change.Action != plans.NoOp { + op.View.PlannedChange(change) + } } } - // Setup our hook for continuous state updates + // Set up our hook for continuous state updates stateHook.StateMgr = opState // Start the apply in a goroutine so that we can be interrupted. @@ -152,7 +161,7 @@ func (b *Local) opApply( applyState = tfCtx.State() }() - if b.opWait(doneCh, stopCtx, cancelCtx, tfCtx, opState) { + if b.opWait(doneCh, stopCtx, cancelCtx, tfCtx, opState, op.View) { return } @@ -168,50 +177,21 @@ func (b *Local) opApply( } stateFile.State = applyState - diags = diags.Append(b.backupStateForError(stateFile, err)) - b.ReportResult(runningOp, diags) + diags = diags.Append(b.backupStateForError(stateFile, err, op.View)) + op.ReportResult(runningOp, diags) return } diags = diags.Append(applyDiags) if applyDiags.HasErrors() { - b.ReportResult(runningOp, diags) + op.ReportResult(runningOp, diags) return } // If we've accumulated any warnings along the way then we'll show them // here just before we show the summary and next steps. If we encountered // errors then we would've returned early at some other point above. - b.ShowDiagnostics(diags) - - // If we have a UI, output the results - if b.CLI != nil { - if op.Destroy { - b.CLI.Output(b.Colorize().Color(fmt.Sprintf( - "[reset][bold][green]\n"+ - "Destroy complete! Resources: %d destroyed.", - countHook.Removed))) - } else { - b.CLI.Output(b.Colorize().Color(fmt.Sprintf( - "[reset][bold][green]\n"+ - "Apply complete! Resources: %d added, %d changed, %d destroyed.", - countHook.Added, - countHook.Changed, - countHook.Removed))) - } - - // only show the state file help message if the state is local. - if (countHook.Added > 0 || countHook.Changed > 0) && b.StateOutPath != "" { - b.CLI.Output(b.Colorize().Color(fmt.Sprintf( - "[reset]\n"+ - "The state of your infrastructure has been saved to the path\n"+ - "below. This state is required to modify and destroy your\n"+ - "infrastructure, so keep it safe. To inspect the complete state\n"+ - "use the `terraform show` command.\n\n"+ - "State path: %s", - b.StateOutPath))) - } - } + op.View.Diagnostics(diags) } // backupStateForError is called in a scenario where we're unable to persist the @@ -219,78 +199,77 @@ func (b *Local) opApply( // to local disk to help the user recover. This is a "last ditch effort" sort // of thing, so we really don't want to end up in this codepath; we should do // everything we possibly can to get the state saved _somewhere_. -func (b *Local) backupStateForError(stateFile *statefile.File, err error) error { - b.CLI.Error(fmt.Sprintf("Failed to save state: %s\n", err)) +func (b *Local) backupStateForError(stateFile *statefile.File, err error, view views.Operation) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to save state", + fmt.Sprintf("Error saving state: %s", err), + )) local := statemgr.NewFilesystem("errored.tfstate") writeErr := local.WriteStateForMigration(stateFile, true) if writeErr != nil { - b.CLI.Error(fmt.Sprintf( - "Also failed to create local state file for recovery: %s\n\n", writeErr, + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to create local state file", + fmt.Sprintf("Error creating local state file for recovery: %s", writeErr), )) + // To avoid leaving the user with no state at all, our last resort // is to print the JSON state out onto the terminal. This is an awful // UX, so we should definitely avoid doing this if at all possible, // but at least the user has _some_ path to recover if we end up // here for some reason. - stateBuf := new(bytes.Buffer) - jsonErr := statefile.Write(stateFile, stateBuf) - if jsonErr != nil { - b.CLI.Error(fmt.Sprintf( - "Also failed to JSON-serialize the state to print it: %s\n\n", jsonErr, + if dumpErr := view.EmergencyDumpState(stateFile); dumpErr != nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to serialize state", + fmt.Sprintf(stateWriteFatalErrorFmt, dumpErr), )) - return errors.New(stateWriteFatalError) } - b.CLI.Output(stateBuf.String()) - - return errors.New(stateWriteConsoleFallbackError) + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to persist state to backend", + stateWriteConsoleFallbackError, + )) + return diags } - return errors.New(stateWriteBackedUpError) -} + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to persist state to backend", + stateWriteBackedUpError, + )) -const stateWriteBackedUpError = `Failed to persist state to backend. + return diags +} -The error shown above has prevented Terraform from writing the updated state -to the configured backend. To allow for recovery, the state has been written -to the file "errored.tfstate" in the current working directory. +const stateWriteBackedUpError = `The error shown above has prevented Terraform from writing the updated state to the configured backend. To allow for recovery, the state has been written to the file "errored.tfstate" in the current working directory. -Running "terraform apply" again at this point will create a forked state, -making it harder to recover. +Running "terraform apply" again at this point will create a forked state, making it harder to recover. To retry writing this state, use the following command: terraform state push errored.tfstate ` -const stateWriteConsoleFallbackError = `Failed to persist state to backend. - -The errors shown above prevented Terraform from writing the updated state to +const stateWriteConsoleFallbackError = `The errors shown above prevented Terraform from writing the updated state to the configured backend and from creating a local backup file. As a fallback, the raw state data is printed above as a JSON object. -To retry writing this state, copy the state data (from the first { to the -last } inclusive) and save it into a local file called errored.tfstate, then -run the following command: +To retry writing this state, copy the state data (from the first { to the last } inclusive) and save it into a local file called errored.tfstate, then run the following command: terraform state push errored.tfstate ` -const stateWriteFatalError = `Failed to save state after apply. +const stateWriteFatalErrorFmt = `Failed to save state after apply. -A catastrophic error has prevented Terraform from persisting the state file -or creating a backup. Unfortunately this means that the record of any resources -created during this apply has been lost, and such resources may exist outside -of Terraform's management. +Error serializing state: %s -For resources that support import, it is possible to recover by manually -importing each resource using its id from the target system. +A catastrophic error has prevented Terraform from persisting the state file or creating a backup. Unfortunately this means that the record of any resources created during this apply has been lost, and such resources may exist outside of Terraform's management. -This is a serious bug in Terraform and should be reported. -` +For resources that support import, it is possible to recover by manually importing each resource using its id from the target system. -const earlyStateWriteErrorFmt = `Error saving current state: %s - -Terraform encountered an error attempting to save the state before cancelling -the current operation. Once the operation is complete another attempt will be -made to save the final state. +This is a serious bug in Terraform and should be reported. ` diff --git a/vendor/github.com/hashicorp/terraform/backend/local/backend_apply_test.go b/vendor/github.com/hashicorp/terraform/backend/local/backend_apply_test.go index 73c384d4..95848d11 100644 --- a/vendor/github.com/hashicorp/terraform/backend/local/backend_apply_test.go +++ b/vendor/github.com/hashicorp/terraform/backend/local/backend_apply_test.go @@ -9,12 +9,16 @@ import ( "sync" "testing" - "github.com/mitchellh/cli" "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/internal/initwd" + "github.com/hashicorp/terraform/internal/terminal" + "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/states/statemgr" @@ -27,12 +31,12 @@ func TestLocal_applyBasic(t *testing.T) { defer cleanup() p := TestLocalProvider(t, b, "test", applyFixtureSchema()) - p.ApplyResourceChangeResponse = providers.ApplyResourceChangeResponse{NewState: cty.ObjectVal(map[string]cty.Value{ + p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{NewState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("yes"), "ami": cty.StringVal("bar"), })} - op, configCleanup := testOperationApply(t, "./testdata/apply") + op, configCleanup, done := testOperationApply(t, "./testdata/apply") defer configCleanup() run, err := b.Operation(context.Background(), op) @@ -63,6 +67,9 @@ test_instance.foo: ami = bar `) + if errOutput := done(t).Stderr(); errOutput != "" { + t.Fatalf("unexpected error output:\n%s", errOutput) + } } func TestLocal_applyEmptyDir(t *testing.T) { @@ -70,9 +77,9 @@ func TestLocal_applyEmptyDir(t *testing.T) { defer cleanup() p := TestLocalProvider(t, b, "test", &terraform.ProviderSchema{}) - p.ApplyResourceChangeResponse = providers.ApplyResourceChangeResponse{NewState: cty.ObjectVal(map[string]cty.Value{"id": cty.StringVal("yes")})} + p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{NewState: cty.ObjectVal(map[string]cty.Value{"id": cty.StringVal("yes")})} - op, configCleanup := testOperationApply(t, "./testdata/empty") + op, configCleanup, done := testOperationApply(t, "./testdata/empty") defer configCleanup() run, err := b.Operation(context.Background(), op) @@ -94,6 +101,10 @@ func TestLocal_applyEmptyDir(t *testing.T) { // the backend should be unlocked after a run assertBackendStateUnlocked(t, b) + + if got, want := done(t).Stderr(), "Error: No configuration files"; !strings.Contains(got, want) { + t.Fatalf("unexpected error output:\n%s\nwant: %s", got, want) + } } func TestLocal_applyEmptyDirDestroy(t *testing.T) { @@ -101,11 +112,11 @@ func TestLocal_applyEmptyDirDestroy(t *testing.T) { defer cleanup() p := TestLocalProvider(t, b, "test", &terraform.ProviderSchema{}) - p.ApplyResourceChangeResponse = providers.ApplyResourceChangeResponse{} + p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{} - op, configCleanup := testOperationApply(t, "./testdata/empty") + op, configCleanup, done := testOperationApply(t, "./testdata/empty") defer configCleanup() - op.Destroy = true + op.PlanMode = plans.DestroyMode run, err := b.Operation(context.Background(), op) if err != nil { @@ -121,6 +132,10 @@ func TestLocal_applyEmptyDirDestroy(t *testing.T) { } checkState(t, b.StateOutPath, ``) + + if errOutput := done(t).Stderr(); errOutput != "" { + t.Fatalf("unexpected error output:\n%s", errOutput) + } } func TestLocal_applyError(t *testing.T) { @@ -151,7 +166,7 @@ func TestLocal_applyError(t *testing.T) { ami := r.Config.GetAttr("ami").AsString() if !errored && ami == "error" { errored = true - diags = diags.Append(errors.New("error")) + diags = diags.Append(errors.New("ami error")) return providers.ApplyResourceChangeResponse{ Diagnostics: diags, } @@ -165,7 +180,7 @@ func TestLocal_applyError(t *testing.T) { } } - op, configCleanup := testOperationApply(t, "./testdata/apply-error") + op, configCleanup, done := testOperationApply(t, "./testdata/apply-error") defer configCleanup() run, err := b.Operation(context.Background(), op) @@ -186,6 +201,10 @@ test_instance.foo: // the backend should be unlocked after a run assertBackendStateUnlocked(t, b) + + if got, want := done(t).Stderr(), "Error: ami error"; !strings.Contains(got, want) { + t.Fatalf("unexpected error output:\n%s\nwant: %s", got, want) + } } func TestLocal_applyBackendFail(t *testing.T) { @@ -193,7 +212,7 @@ func TestLocal_applyBackendFail(t *testing.T) { defer cleanup() p := TestLocalProvider(t, b, "test", applyFixtureSchema()) - p.ApplyResourceChangeResponse = providers.ApplyResourceChangeResponse{NewState: cty.ObjectVal(map[string]cty.Value{ + p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{NewState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("yes"), "ami": cty.StringVal("bar"), })} @@ -208,24 +227,26 @@ func TestLocal_applyBackendFail(t *testing.T) { } defer os.Chdir(wd) - op, configCleanup := testOperationApply(t, wd+"/testdata/apply") + op, configCleanup, done := testOperationApply(t, wd+"/testdata/apply") defer configCleanup() b.Backend = &backendWithFailingState{} - b.CLI = new(cli.MockUi) run, err := b.Operation(context.Background(), op) if err != nil { t.Fatalf("bad: %s", err) } <-run.Done() + + output := done(t) + if run.Result == backend.OperationSuccess { t.Fatalf("apply succeeded; want error") } - msgStr := b.CLI.(*cli.MockUi).ErrorWriter.String() - if !strings.Contains(msgStr, "Failed to save state: fake failure") { - t.Fatalf("missing \"fake failure\" message in output:\n%s", msgStr) + diagErr := output.Stderr() + if !strings.Contains(diagErr, "Error saving state: fake failure") { + t.Fatalf("missing \"fake failure\" message in diags:\n%s", diagErr) } // The fallback behavior should've created a file errored.tfstate in the @@ -241,6 +262,34 @@ test_instance.foo: assertBackendStateUnlocked(t, b) } +func TestLocal_applyRefreshFalse(t *testing.T) { + b, cleanup := TestLocal(t) + defer cleanup() + + p := TestLocalProvider(t, b, "test", planFixtureSchema()) + testStateFile(t, b.StatePath, testPlanState()) + + op, configCleanup, done := testOperationApply(t, "./testdata/plan") + defer configCleanup() + + run, err := b.Operation(context.Background(), op) + if err != nil { + t.Fatalf("bad: %s", err) + } + <-run.Done() + if run.Result != backend.OperationSuccess { + t.Fatalf("plan operation failed") + } + + if p.ReadResourceCalled { + t.Fatal("ReadResource should not be called") + } + + if errOutput := done(t).Stderr(); errOutput != "" { + t.Fatalf("unexpected error output:\n%s", errOutput) + } +} + type backendWithFailingState struct { Local } @@ -259,16 +308,21 @@ func (s failingState) WriteState(state *states.State) error { return errors.New("fake failure") } -func testOperationApply(t *testing.T, configDir string) (*backend.Operation, func()) { +func testOperationApply(t *testing.T, configDir string) (*backend.Operation, func(), func(*testing.T) *terminal.TestOutput) { t.Helper() _, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir) + streams, done := terminal.StreamsForTesting(t) + view := views.NewOperation(arguments.ViewHuman, false, views.NewView(streams)) + return &backend.Operation{ Type: backend.OperationTypeApply, ConfigDir: configDir, ConfigLoader: configLoader, - }, configCleanup + StateLocker: clistate.NewNoopLocker(), + View: view, + }, configCleanup, done } // applyFixtureSchema returns a schema suitable for processing the diff --git a/vendor/github.com/hashicorp/terraform/backend/local/backend_local.go b/vendor/github.com/hashicorp/terraform/backend/local/backend_local.go index b4d6002c..eade3ec6 100644 --- a/vendor/github.com/hashicorp/terraform/backend/local/backend_local.go +++ b/vendor/github.com/hashicorp/terraform/backend/local/backend_local.go @@ -8,7 +8,6 @@ import ( "github.com/hashicorp/errwrap" "github.com/hashicorp/terraform/backend" - "github.com/hashicorp/terraform/command/clistate" "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs/configload" "github.com/hashicorp/terraform/plans/planfile" @@ -24,11 +23,7 @@ func (b *Local) Context(op *backend.Operation) (*terraform.Context, statemgr.Ful // to ask for input/validate. op.Type = backend.OperationTypeInvalid - if op.LockState { - op.StateLocker = clistate.NewLocker(context.Background(), op.StateLockTimeout, b.CLI, b.Colorize()) - } else { - op.StateLocker = clistate.NewNoopLocker() - } + op.StateLocker = op.StateLocker.WithContext(context.Background()) ctx, _, stateMgr, diags := b.context(op) return ctx, stateMgr, diags @@ -45,8 +40,7 @@ func (b *Local) context(op *backend.Operation) (*terraform.Context, *configload. return nil, nil, nil, diags } log.Printf("[TRACE] backend/local: requesting state lock for workspace %q", op.Workspace) - if err := op.StateLocker.Lock(s, op.Type.String()); err != nil { - diags = diags.Append(errwrap.Wrapf("Error locking state: {{err}}", err)) + if diags := op.StateLocker.Lock(s, op.Type.String()); diags.HasErrors() { return nil, nil, nil, diags } @@ -54,10 +48,7 @@ func (b *Local) context(op *backend.Operation) (*terraform.Context, *configload. // If we're returning with errors, and thus not producing a valid // context, we'll want to avoid leaving the workspace locked. if diags.HasErrors() { - err := op.StateLocker.Unlock(nil) - if err != nil { - diags = diags.Append(errwrap.Wrapf("Error unlocking state: {{err}}", err)) - } + diags = diags.Append(op.StateLocker.Unlock()) } }() @@ -74,11 +65,13 @@ func (b *Local) context(op *backend.Operation) (*terraform.Context, *configload. } // Copy set options from the operation - opts.Destroy = op.Destroy + opts.PlanMode = op.PlanMode opts.Targets = op.Targets + opts.ForceReplace = op.ForceReplace opts.UIInput = op.UIIn + opts.Hooks = op.Hooks - opts.SkipRefresh = op.Type == backend.OperationTypePlan && !op.PlanRefresh + opts.SkipRefresh = op.Type != backend.OperationTypeRefresh && !op.PlanRefresh if opts.SkipRefresh { log.Printf("[DEBUG] backend/local: skipping refresh of managed resources") } @@ -103,6 +96,10 @@ func (b *Local) context(op *backend.Operation) (*terraform.Context, *configload. } log.Printf("[TRACE] backend/local: building context from plan file") tfCtx, configSnap, ctxDiags = b.contextFromPlanFile(op.PlanFile, opts, stateMeta) + if ctxDiags.HasErrors() { + return nil, nil, nil, ctxDiags + } + // Write sources into the cache of the main loader so that they are // available if we need to generate diagnostic message snippets. op.ConfigLoader.ImportSourcesFromSnapshot(configSnap) @@ -268,6 +265,7 @@ func (b *Local) contextFromPlanFile(pf *planfile.Reader, opts terraform.ContextO opts.Variables = variables opts.Changes = plan.Changes opts.Targets = plan.TargetAddrs + opts.ForceReplace = plan.ForceReplaceAddrs opts.ProviderSHA256s = plan.ProviderSHA256s tfCtx, ctxDiags := terraform.NewContext(&opts) diff --git a/vendor/github.com/hashicorp/terraform/backend/local/backend_local_test.go b/vendor/github.com/hashicorp/terraform/backend/local/backend_local_test.go index 3354b709..97f18129 100644 --- a/vendor/github.com/hashicorp/terraform/backend/local/backend_local_test.go +++ b/vendor/github.com/hashicorp/terraform/backend/local/backend_local_test.go @@ -4,7 +4,11 @@ import ( "testing" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/internal/initwd" + "github.com/hashicorp/terraform/internal/terminal" ) func TestLocalContext(t *testing.T) { @@ -15,11 +19,15 @@ func TestLocalContext(t *testing.T) { _, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir) defer configCleanup() + streams, _ := terminal.StreamsForTesting(t) + view := views.NewView(streams) + stateLocker := clistate.NewLocker(0, views.NewStateLocker(arguments.ViewHuman, view)) + op := &backend.Operation{ ConfigDir: configDir, ConfigLoader: configLoader, Workspace: backend.DefaultStateName, - LockState: true, + StateLocker: stateLocker, } _, _, diags := b.Context(op) @@ -39,11 +47,15 @@ func TestLocalContext_error(t *testing.T) { _, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir) defer configCleanup() + streams, _ := terminal.StreamsForTesting(t) + view := views.NewView(streams) + stateLocker := clistate.NewLocker(0, views.NewStateLocker(arguments.ViewHuman, view)) + op := &backend.Operation{ ConfigDir: configDir, ConfigLoader: configLoader, Workspace: backend.DefaultStateName, - LockState: true, + StateLocker: stateLocker, } _, _, diags := b.Context(op) @@ -53,5 +65,4 @@ func TestLocalContext_error(t *testing.T) { // Context() unlocks the state on failure assertBackendStateUnlocked(t, b) - } diff --git a/vendor/github.com/hashicorp/terraform/backend/local/backend_plan.go b/vendor/github.com/hashicorp/terraform/backend/local/backend_plan.go index 0fbb0b58..e3d56a61 100644 --- a/vendor/github.com/hashicorp/terraform/backend/local/backend_plan.go +++ b/vendor/github.com/hashicorp/terraform/backend/local/backend_plan.go @@ -1,22 +1,14 @@ package local import ( - "bytes" "context" "fmt" "log" - "sort" - "strings" - "github.com/mitchellh/cli" - "github.com/mitchellh/colorstring" - - "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/backend" - "github.com/hashicorp/terraform/command/format" "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/plans/planfile" - "github.com/hashicorp/terraform/states" + "github.com/hashicorp/terraform/states/statefile" "github.com/hashicorp/terraform/states/statemgr" "github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/tfdiags" @@ -39,12 +31,12 @@ func (b *Local) opPlan( "The plan command was given a saved plan file as its input. This command generates "+ "a new plan, and so it requires a configuration directory as its argument.", )) - b.ReportResult(runningOp, diags) + op.ReportResult(runningOp, diags) return } // Local planning requires a config, unless we're planning to destroy. - if !op.Destroy && !op.HasConfig() { + if op.PlanMode != plans.DestroyMode && !op.HasConfig() { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, "No configuration files", @@ -53,32 +45,27 @@ func (b *Local) opPlan( "would like to destroy everything, run plan with the -destroy option. Otherwise, "+ "create a Terraform configuration file (.tf file) and try again.", )) - b.ReportResult(runningOp, diags) + op.ReportResult(runningOp, diags) return } - // Setup our count hook that keeps track of resource changes - countHook := new(CountHook) if b.ContextOpts == nil { b.ContextOpts = new(terraform.ContextOpts) } - old := b.ContextOpts.Hooks - defer func() { b.ContextOpts.Hooks = old }() - b.ContextOpts.Hooks = append(b.ContextOpts.Hooks, countHook) // Get our context tfCtx, configSnap, opState, ctxDiags := b.context(op) diags = diags.Append(ctxDiags) if ctxDiags.HasErrors() { - b.ReportResult(runningOp, diags) + op.ReportResult(runningOp, diags) return } // the state was locked during succesfull context creation; unlock the state // when the operation completes defer func() { - err := op.StateLocker.Unlock(nil) - if err != nil { - b.ShowDiagnostics(err) + diags := op.StateLocker.Unlock() + if diags.HasErrors() { + op.View.Diagnostics(diags) runningOp.Result = backend.OperationFailure } }() @@ -95,7 +82,7 @@ func (b *Local) opPlan( plan, planDiags = tfCtx.Plan() }() - if b.opWait(doneCh, stopCtx, cancelCtx, tfCtx, opState) { + if b.opWait(doneCh, stopCtx, cancelCtx, tfCtx, opState, op.View) { // If we get in here then the operation was cancelled, which is always // considered to be a failure. log.Printf("[INFO] backend/local: plan operation was force-cancelled by interrupt") @@ -106,7 +93,7 @@ func (b *Local) opPlan( diags = diags.Append(planDiags) if planDiags.HasErrors() { - b.ReportResult(runningOp, diags) + op.ReportResult(runningOp, diags) return } @@ -121,7 +108,7 @@ func (b *Local) opPlan( diags = diags.Append(fmt.Errorf( "PlanOutPath set without also setting PlanOutBackend (this is a bug in Terraform)"), ) - b.ReportResult(runningOp, diags) + op.ReportResult(runningOp, diags) return } plan.Backend = *op.PlanOutBackend @@ -129,224 +116,49 @@ func (b *Local) opPlan( // We may have updated the state in the refresh step above, but we // will freeze that updated state in the plan file for now and // only write it if this plan is subsequently applied. - plannedStateFile := statemgr.PlannedStateUpdate(opState, plan.State) + plannedStateFile := statemgr.PlannedStateUpdate(opState, plan.PriorState) + + // We also include a file containing the state as it existed before + // we took any action at all, but this one isn't intended to ever + // be saved to the backend (an equivalent snapshot should already be + // there) and so we just use a stub state file header in this case. + // NOTE: This won't be exactly identical to the latest state snapshot + // in the backend because it's still been subject to state upgrading + // to make it consumable by the current Terraform version, and + // intentionally doesn't preserve the header info. + prevStateFile := &statefile.File{ + State: plan.PrevRunState, + } log.Printf("[INFO] backend/local: writing plan output to: %s", path) - err := planfile.Create(path, configSnap, plannedStateFile, plan) + err := planfile.Create(path, configSnap, prevStateFile, plannedStateFile, plan) if err != nil { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, "Failed to write plan file", fmt.Sprintf("The plan file could not be written: %s.", err), )) - b.ReportResult(runningOp, diags) + op.ReportResult(runningOp, diags) return } } - // Perform some output tasks if we have a CLI to output to. - if b.CLI != nil { - schemas := tfCtx.Schemas() - - if runningOp.PlanEmpty { - b.CLI.Output("\n" + b.Colorize().Color(strings.TrimSpace(planNoChanges))) - // Even if there are no changes, there still could be some warnings - b.ShowDiagnostics(diags) - return - } - - b.renderPlan(plan, plan.State, schemas) + // Perform some output tasks + if runningOp.PlanEmpty { + op.View.PlanNoChanges() - // If we've accumulated any warnings along the way then we'll show them - // here just before we show the summary and next steps. If we encountered - // errors then we would've returned early at some other point above. - b.ShowDiagnostics(diags) - - // Give the user some next-steps, unless we're running in an automation - // tool which is presumed to provide its own UI for further actions. - if !b.RunningInAutomation { - - b.CLI.Output("\n------------------------------------------------------------------------") - - if path := op.PlanOutPath; path == "" { - b.CLI.Output(fmt.Sprintf( - "\n" + strings.TrimSpace(planHeaderNoOutput) + "\n", - )) - } else { - b.CLI.Output(fmt.Sprintf( - "\n"+strings.TrimSpace(planHeaderYesOutput)+"\n", - path, path, - )) - } - } - } -} - -func (b *Local) renderPlan(plan *plans.Plan, baseState *states.State, schemas *terraform.Schemas) { - RenderPlan(plan, baseState, schemas, b.CLI, b.Colorize()) -} - -// RenderPlan renders the given plan to the given UI. -// -// This is exported only so that the "terraform show" command can re-use it. -// Ideally it would be somewhere outside of this backend code so that both -// can call into it, but we're leaving it here for now in order to avoid -// disruptive refactoring. -// -// If you find yourself wanting to call this function from a third callsite, -// please consider whether it's time to do the more disruptive refactoring -// so that something other than the local backend package is offering this -// functionality. -// -// The difference between baseState and priorState is that baseState is the -// result of implicitly running refresh (unless that was disabled) while -// priorState is a snapshot of the state as it was before we took any actions -// at all. priorState can optionally be nil if the caller has only a saved -// plan and not the prior state it was built from. In that case, changes to -// output values will not currently be rendered because their prior values -// are currently stored only in the prior state. (see the docstring for -// func planHasSideEffects for why this is and when that might change) -func RenderPlan(plan *plans.Plan, baseState *states.State, schemas *terraform.Schemas, ui cli.Ui, colorize *colorstring.Colorize) { - counts := map[plans.Action]int{} - var rChanges []*plans.ResourceInstanceChangeSrc - for _, change := range plan.Changes.Resources { - if change.Action == plans.Delete && change.Addr.Resource.Resource.Mode == addrs.DataResourceMode { - // Avoid rendering data sources on deletion - continue - } - - rChanges = append(rChanges, change) - counts[change.Action]++ - } - - headerBuf := &bytes.Buffer{} - fmt.Fprintf(headerBuf, "\n%s\n", strings.TrimSpace(planHeaderIntro)) - if counts[plans.Create] > 0 { - fmt.Fprintf(headerBuf, "%s create\n", format.DiffActionSymbol(plans.Create)) - } - if counts[plans.Update] > 0 { - fmt.Fprintf(headerBuf, "%s update in-place\n", format.DiffActionSymbol(plans.Update)) - } - if counts[plans.Delete] > 0 { - fmt.Fprintf(headerBuf, "%s destroy\n", format.DiffActionSymbol(plans.Delete)) - } - if counts[plans.DeleteThenCreate] > 0 { - fmt.Fprintf(headerBuf, "%s destroy and then create replacement\n", format.DiffActionSymbol(plans.DeleteThenCreate)) - } - if counts[plans.CreateThenDelete] > 0 { - fmt.Fprintf(headerBuf, "%s create replacement and then destroy\n", format.DiffActionSymbol(plans.CreateThenDelete)) - } - if counts[plans.Read] > 0 { - fmt.Fprintf(headerBuf, "%s read (data resources)\n", format.DiffActionSymbol(plans.Read)) + // Even if there are no changes, there still could be some warnings + op.View.Diagnostics(diags) + return } - ui.Output(colorize.Color(headerBuf.String())) - - ui.Output("Terraform will perform the following actions:\n") - - // Note: we're modifying the backing slice of this plan object in-place - // here. The ordering of resource changes in a plan is not significant, - // but we can only do this safely here because we can assume that nobody - // is concurrently modifying our changes while we're trying to print it. - sort.Slice(rChanges, func(i, j int) bool { - iA := rChanges[i].Addr - jA := rChanges[j].Addr - if iA.String() == jA.String() { - return rChanges[i].DeposedKey < rChanges[j].DeposedKey - } - return iA.Less(jA) - }) - - for _, rcs := range rChanges { - if rcs.Action == plans.NoOp { - continue - } - - providerSchema := schemas.ProviderSchema(rcs.ProviderAddr.Provider) - if providerSchema == nil { - // Should never happen - ui.Output(fmt.Sprintf("(schema missing for %s)\n", rcs.ProviderAddr)) - continue - } - rSchema, _ := providerSchema.SchemaForResourceAddr(rcs.Addr.Resource.Resource) - if rSchema == nil { - // Should never happen - ui.Output(fmt.Sprintf("(schema missing for %s)\n", rcs.Addr)) - continue - } - - // check if the change is due to a tainted resource - tainted := false - if !baseState.Empty() { - if is := baseState.ResourceInstance(rcs.Addr); is != nil { - if obj := is.GetGeneration(rcs.DeposedKey.Generation()); obj != nil { - tainted = obj.Status == states.ObjectTainted - } - } - } - - ui.Output(format.ResourceChange( - rcs, - tainted, - rSchema, - colorize, - )) - } + // Render the plan + op.View.Plan(plan, tfCtx.Schemas()) - // stats is similar to counts above, but: - // - it considers only resource changes - // - it simplifies "replace" into both a create and a delete - stats := map[plans.Action]int{} - for _, change := range rChanges { - switch change.Action { - case plans.CreateThenDelete, plans.DeleteThenCreate: - stats[plans.Create]++ - stats[plans.Delete]++ - default: - stats[change.Action]++ - } - } - ui.Output(colorize.Color(fmt.Sprintf( - "[reset][bold]Plan:[reset] "+ - "%d to add, %d to change, %d to destroy.", - stats[plans.Create], stats[plans.Update], stats[plans.Delete], - ))) + // If we've accumulated any warnings along the way then we'll show them + // here just before we show the summary and next steps. If we encountered + // errors then we would've returned early at some other point above. + op.View.Diagnostics(diags) - // If there is at least one planned change to the root module outputs - // then we'll render a summary of those too. - if len(plan.Changes.Outputs) > 0 { - ui.Output(colorize.Color("[reset]\n[bold]Changes to Outputs:[reset]" + format.OutputChanges(plan.Changes.Outputs, colorize))) - } + op.View.PlanNextStep(op.PlanOutPath) } - -const planHeaderIntro = ` -An execution plan has been generated and is shown below. -Resource actions are indicated with the following symbols: -` - -const planHeaderNoOutput = ` -Note: You didn't specify an "-out" parameter to save this plan, so Terraform -can't guarantee that exactly these actions will be performed if -"terraform apply" is subsequently run. -` - -const planHeaderYesOutput = ` -This plan was saved to: %s - -To perform exactly these actions, run the following command to apply: - terraform apply %q -` - -const planNoChanges = ` -[reset][bold][green]No changes. Infrastructure is up-to-date.[reset][green] - -This means that Terraform did not detect any differences between your -configuration and real physical resources that exist. As a result, no -actions need to be performed. -` - -const planRefreshing = ` -[reset][bold]Refreshing Terraform state in-memory prior to plan...[reset] -The refreshed state will be used to calculate this plan, but will not be -persisted to local or remote state storage. -` diff --git a/vendor/github.com/hashicorp/terraform/backend/local/backend_plan_test.go b/vendor/github.com/hashicorp/terraform/backend/local/backend_plan_test.go index f275d20b..02ed4d5b 100644 --- a/vendor/github.com/hashicorp/terraform/backend/local/backend_plan_test.go +++ b/vendor/github.com/hashicorp/terraform/backend/local/backend_plan_test.go @@ -4,19 +4,21 @@ import ( "context" "os" "path/filepath" - "reflect" "strings" "testing" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/internal/initwd" + "github.com/hashicorp/terraform/internal/terminal" "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/plans/planfile" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/cli" "github.com/zclconf/go-cty/cty" ) @@ -25,7 +27,7 @@ func TestLocal_planBasic(t *testing.T) { defer cleanup() p := TestLocalProvider(t, b, "test", planFixtureSchema()) - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() op.PlanRefresh = true @@ -44,6 +46,10 @@ func TestLocal_planBasic(t *testing.T) { // the backend should be unlocked after a run assertBackendStateUnlocked(t, b) + + if errOutput := done(t).Stderr(); errOutput != "" { + t.Fatalf("unexpected error output:\n%s", errOutput) + } } func TestLocal_planInAutomation(t *testing.T) { @@ -51,60 +57,31 @@ func TestLocal_planInAutomation(t *testing.T) { defer cleanup() TestLocalProvider(t, b, "test", planFixtureSchema()) - const msg = `You didn't specify an "-out" parameter` - - // When we're "in automation" we omit certain text from the - // plan output. However, testing for the absense of text is - // unreliable in the face of future copy changes, so we'll - // mitigate that by running both with and without the flag - // set so we can ensure that the expected messages _are_ - // included the first time. - b.RunningInAutomation = false - b.CLI = cli.NewMockUi() - { - op, configCleanup := testOperationPlan(t, "./testdata/plan") - defer configCleanup() - op.PlanRefresh = true - - run, err := b.Operation(context.Background(), op) - if err != nil { - t.Fatalf("unexpected error: %s", err) - } - <-run.Done() - if run.Result != backend.OperationSuccess { - t.Fatalf("plan operation failed") - } - - output := b.CLI.(*cli.MockUi).OutputWriter.String() - if !strings.Contains(output, msg) { - t.Fatalf("missing next-steps message when not in automation") - } - } - - // On the second run, we expect the next-steps messaging to be absent - // since we're now "running in automation". - b.RunningInAutomation = true - b.CLI = cli.NewMockUi() - { - op, configCleanup := testOperationPlan(t, "./testdata/plan") - defer configCleanup() - op.PlanRefresh = true + const msg = `You didn't use the -out option` - run, err := b.Operation(context.Background(), op) - if err != nil { - t.Fatalf("unexpected error: %s", err) - } - <-run.Done() - if run.Result != backend.OperationSuccess { - t.Fatalf("plan operation failed") - } + // When we're "in automation" we omit certain text from the plan output. + // However, the responsibility for this omission is in the view, so here we + // test for its presence while the "in automation" setting is false, to + // validate that we are calling the correct view method. + // + // Ideally this test would be replaced by a call-logging mock view, but + // that's future work. + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") + defer configCleanup() + op.PlanRefresh = true - output := b.CLI.(*cli.MockUi).OutputWriter.String() - if strings.Contains(output, msg) { - t.Fatalf("next-steps message present when in automation") - } + run, err := b.Operation(context.Background(), op) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + <-run.Done() + if run.Result != backend.OperationSuccess { + t.Fatalf("plan operation failed") } + if output := done(t).Stdout(); !strings.Contains(output, msg) { + t.Fatalf("missing next-steps message when not in automation\nwant: %s\noutput:\n%s", msg, output) + } } func TestLocal_planNoConfig(t *testing.T) { @@ -112,9 +89,7 @@ func TestLocal_planNoConfig(t *testing.T) { defer cleanup() TestLocalProvider(t, b, "test", &terraform.ProviderSchema{}) - b.CLI = cli.NewMockUi() - - op, configCleanup := testOperationPlan(t, "./testdata/empty") + op, configCleanup, done := testOperationPlan(t, "./testdata/empty") defer configCleanup() op.PlanRefresh = true @@ -124,12 +99,14 @@ func TestLocal_planNoConfig(t *testing.T) { } <-run.Done() + output := done(t) + if run.Result == backend.OperationSuccess { t.Fatal("plan operation succeeded; want failure") } - output := b.CLI.(*cli.MockUi).ErrorWriter.String() - if !strings.Contains(output, "configuration") { - t.Fatalf("bad: %s", err) + + if stderr := output.Stderr(); !strings.Contains(stderr, "No configuration files") { + t.Fatalf("bad: %s", stderr) } // the backend should be unlocked after a run @@ -142,7 +119,7 @@ func TestLocal_plan_context_error(t *testing.T) { b, cleanup := TestLocal(t) defer cleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() op.PlanRefresh = true @@ -158,6 +135,10 @@ func TestLocal_plan_context_error(t *testing.T) { // the backend should be unlocked after a run assertBackendStateUnlocked(t, b) + + if got, want := done(t).Stderr(), "Error: Could not load plugin"; !strings.Contains(got, want) { + t.Fatalf("unexpected error output:\n%s\nwant: %s", got, want) + } } func TestLocal_planOutputsChanged(t *testing.T) { @@ -193,11 +174,10 @@ func TestLocal_planOutputsChanged(t *testing.T) { // unknown" situation because that's already common for printing out // resource changes and we already have many tests for that. })) - b.CLI = cli.NewMockUi() outDir := testTempDir(t) defer os.RemoveAll(outDir) planPath := filepath.Join(outDir, "plan.tfplan") - op, configCleanup := testOperationPlan(t, "./testdata/plan-outputs-changed") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan-outputs-changed") defer configCleanup() op.PlanRefresh = true op.PlanOutPath = planPath @@ -235,8 +215,56 @@ Changes to Outputs: ~ sensitive_after = (sensitive value) ~ sensitive_before = (sensitive value) `) - output := b.CLI.(*cli.MockUi).OutputWriter.String() - if !strings.Contains(output, expectedOutput) { + + if output := done(t).Stdout(); !strings.Contains(output, expectedOutput) { + t.Fatalf("Unexpected output:\n%s\n\nwant output containing:\n%s", output, expectedOutput) + } +} + +// Module outputs should not cause the plan to be rendered +func TestLocal_planModuleOutputsChanged(t *testing.T) { + b, cleanup := TestLocal(t) + defer cleanup() + testStateFile(t, b.StatePath, states.BuildState(func(ss *states.SyncState) { + ss.SetOutputValue(addrs.AbsOutputValue{ + Module: addrs.RootModuleInstance.Child("mod", addrs.NoKey), + OutputValue: addrs.OutputValue{Name: "changed"}, + }, cty.StringVal("before"), false) + })) + outDir := testTempDir(t) + defer os.RemoveAll(outDir) + planPath := filepath.Join(outDir, "plan.tfplan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan-module-outputs-changed") + defer configCleanup() + op.PlanRefresh = true + op.PlanOutPath = planPath + cfg := cty.ObjectVal(map[string]cty.Value{ + "path": cty.StringVal(b.StatePath), + }) + cfgRaw, err := plans.NewDynamicValue(cfg, cfg.Type()) + if err != nil { + t.Fatal(err) + } + op.PlanOutBackend = &plans.Backend{ + Type: "local", + Config: cfgRaw, + } + run, err := b.Operation(context.Background(), op) + if err != nil { + t.Fatalf("bad: %s", err) + } + <-run.Done() + if run.Result != backend.OperationSuccess { + t.Fatalf("plan operation failed") + } + if !run.PlanEmpty { + t.Fatal("plan should be empty") + } + + expectedOutput := strings.TrimSpace(` +No changes. Infrastructure is up-to-date. +`) + if output := done(t).Stdout(); !strings.Contains(output, expectedOutput) { t.Fatalf("Unexpected output:\n%s\n\nwant output containing:\n%s", output, expectedOutput) } } @@ -246,11 +274,10 @@ func TestLocal_planTainted(t *testing.T) { defer cleanup() p := TestLocalProvider(t, b, "test", planFixtureSchema()) testStateFile(t, b.StatePath, testPlanState_tainted()) - b.CLI = cli.NewMockUi() outDir := testTempDir(t) defer os.RemoveAll(outDir) planPath := filepath.Join(outDir, "plan.tfplan") - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() op.PlanRefresh = true op.PlanOutPath = planPath @@ -281,8 +308,8 @@ func TestLocal_planTainted(t *testing.T) { t.Fatal("plan should not be empty") } - expectedOutput := `An execution plan has been generated and is shown below. -Resource actions are indicated with the following symbols: + expectedOutput := `Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: -/+ destroy and then create replacement Terraform will perform the following actions: @@ -295,8 +322,7 @@ Terraform will perform the following actions: } Plan: 1 to add, 0 to change, 1 to destroy.` - output := b.CLI.(*cli.MockUi).OutputWriter.String() - if !strings.Contains(output, expectedOutput) { + if output := done(t).Stdout(); !strings.Contains(output, expectedOutput) { t.Fatalf("Unexpected output:\n%s", output) } } @@ -329,11 +355,10 @@ func TestLocal_planDeposedOnly(t *testing.T) { }, ) })) - b.CLI = cli.NewMockUi() outDir := testTempDir(t) defer os.RemoveAll(outDir) planPath := filepath.Join(outDir, "plan.tfplan") - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() op.PlanRefresh = true op.PlanOutPath = planPath @@ -383,8 +408,8 @@ func TestLocal_planDeposedOnly(t *testing.T) { // it's also possible for there to be _multiple_ deposed objects, in the // unlikely event that create_before_destroy _keeps_ crashing across // subsequent runs. - expectedOutput := `An execution plan has been generated and is shown below. -Resource actions are indicated with the following symbols: + expectedOutput := `Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + create - destroy @@ -411,9 +436,8 @@ Terraform will perform the following actions: } Plan: 1 to add, 0 to change, 1 to destroy.` - output := b.CLI.(*cli.MockUi).OutputWriter.String() - if !strings.Contains(output, expectedOutput) { - t.Fatalf("Unexpected output:\n%s\n\nwant output containing:\n%s", output, expectedOutput) + if output := done(t).Stdout(); !strings.Contains(output, expectedOutput) { + t.Fatalf("Unexpected output:\n%s", output) } } @@ -422,11 +446,10 @@ func TestLocal_planTainted_createBeforeDestroy(t *testing.T) { defer cleanup() p := TestLocalProvider(t, b, "test", planFixtureSchema()) testStateFile(t, b.StatePath, testPlanState_tainted()) - b.CLI = cli.NewMockUi() outDir := testTempDir(t) defer os.RemoveAll(outDir) planPath := filepath.Join(outDir, "plan.tfplan") - op, configCleanup := testOperationPlan(t, "./testdata/plan-cbd") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan-cbd") defer configCleanup() op.PlanRefresh = true op.PlanOutPath = planPath @@ -457,8 +480,8 @@ func TestLocal_planTainted_createBeforeDestroy(t *testing.T) { t.Fatal("plan should not be empty") } - expectedOutput := `An execution plan has been generated and is shown below. -Resource actions are indicated with the following symbols: + expectedOutput := `Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: +/- create replacement and then destroy Terraform will perform the following actions: @@ -471,8 +494,7 @@ Terraform will perform the following actions: } Plan: 1 to add, 0 to change, 1 to destroy.` - output := b.CLI.(*cli.MockUi).OutputWriter.String() - if !strings.Contains(output, expectedOutput) { + if output := done(t).Stdout(); !strings.Contains(output, expectedOutput) { t.Fatalf("Unexpected output:\n%s", output) } } @@ -484,7 +506,7 @@ func TestLocal_planRefreshFalse(t *testing.T) { p := TestLocalProvider(t, b, "test", planFixtureSchema()) testStateFile(t, b.StatePath, testPlanState()) - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() run, err := b.Operation(context.Background(), op) @@ -503,22 +525,26 @@ func TestLocal_planRefreshFalse(t *testing.T) { if !run.PlanEmpty { t.Fatal("plan should be empty") } + + if errOutput := done(t).Stderr(); errOutput != "" { + t.Fatalf("unexpected error output:\n%s", errOutput) + } } func TestLocal_planDestroy(t *testing.T) { b, cleanup := TestLocal(t) defer cleanup() - p := TestLocalProvider(t, b, "test", planFixtureSchema()) + TestLocalProvider(t, b, "test", planFixtureSchema()) testStateFile(t, b.StatePath, testPlanState()) outDir := testTempDir(t) defer os.RemoveAll(outDir) planPath := filepath.Join(outDir, "plan.tfplan") - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() - op.Destroy = true + op.PlanMode = plans.DestroyMode op.PlanRefresh = true op.PlanOutPath = planPath cfg := cty.ObjectVal(map[string]cty.Value{ @@ -543,10 +569,6 @@ func TestLocal_planDestroy(t *testing.T) { t.Fatalf("plan operation failed") } - if p.ReadResourceCalled { - t.Fatal("ReadResource should not be called") - } - if run.PlanEmpty { t.Fatal("plan should not be empty") } @@ -557,24 +579,26 @@ func TestLocal_planDestroy(t *testing.T) { t.Fatalf("bad: %#v", r.Action.String()) } } + + if errOutput := done(t).Stderr(); errOutput != "" { + t.Fatalf("unexpected error output:\n%s", errOutput) + } } func TestLocal_planDestroy_withDataSources(t *testing.T) { b, cleanup := TestLocal(t) defer cleanup() - p := TestLocalProvider(t, b, "test", planFixtureSchema()) + TestLocalProvider(t, b, "test", planFixtureSchema()) testStateFile(t, b.StatePath, testPlanState_withDataSource()) - b.CLI = cli.NewMockUi() - outDir := testTempDir(t) defer os.RemoveAll(outDir) planPath := filepath.Join(outDir, "plan.tfplan") - op, configCleanup := testOperationPlan(t, "./testdata/destroy-with-ds") + op, configCleanup, done := testOperationPlan(t, "./testdata/destroy-with-ds") defer configCleanup() - op.Destroy = true + op.PlanMode = plans.DestroyMode op.PlanRefresh = true op.PlanOutPath = planPath cfg := cty.ObjectVal(map[string]cty.Value{ @@ -599,14 +623,6 @@ func TestLocal_planDestroy_withDataSources(t *testing.T) { t.Fatalf("plan operation failed") } - if p.ReadResourceCalled { - t.Fatal("ReadResource should not be called") - } - - if p.ReadDataSourceCalled { - t.Fatal("ReadDataSourceCalled should not be called") - } - if run.PlanEmpty { t.Fatal("plan should not be empty") } @@ -633,14 +649,13 @@ func TestLocal_planDestroy_withDataSources(t *testing.T) { Plan: 0 to add, 0 to change, 1 to destroy.` - output := b.CLI.(*cli.MockUi).OutputWriter.String() - if !strings.Contains(output, expectedOutput) { + if output := done(t).Stdout(); !strings.Contains(output, expectedOutput) { t.Fatalf("Unexpected output:\n%s", output) } } func getAddrs(resources []*plans.ResourceInstanceChangeSrc) []string { - addrs := make([]string, len(resources), len(resources)) + addrs := make([]string, len(resources)) for i, r := range resources { addrs[i] = r.Addr.String() } @@ -657,7 +672,7 @@ func TestLocal_planOutPathNoChange(t *testing.T) { defer os.RemoveAll(outDir) planPath := filepath.Join(outDir, "plan.tfplan") - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() op.PlanOutPath = planPath cfg := cty.ObjectVal(map[string]cty.Value{ @@ -688,61 +703,27 @@ func TestLocal_planOutPathNoChange(t *testing.T) { if !plan.Changes.Empty() { t.Fatalf("expected empty plan to be written") } -} - -// TestLocal_planScaleOutNoDupeCount tests a Refresh/Plan sequence when a -// resource count is scaled out. The scaled out node needs to exist in the -// graph and run through a plan-style sequence during the refresh phase, but -// can conflate the count if its post-diff count hooks are not skipped. This -// checks to make sure the correct resource count is ultimately given to the -// UI. -func TestLocal_planScaleOutNoDupeCount(t *testing.T) { - b, cleanup := TestLocal(t) - defer cleanup() - TestLocalProvider(t, b, "test", planFixtureSchema()) - testStateFile(t, b.StatePath, testPlanState()) - - actual := new(CountHook) - b.ContextOpts.Hooks = append(b.ContextOpts.Hooks, actual) - - outDir := testTempDir(t) - defer os.RemoveAll(outDir) - - op, configCleanup := testOperationPlan(t, "./testdata/plan-scaleout") - defer configCleanup() - op.PlanRefresh = true - - run, err := b.Operation(context.Background(), op) - if err != nil { - t.Fatalf("bad: %s", err) - } - <-run.Done() - if run.Result != backend.OperationSuccess { - t.Fatalf("plan operation failed") - } - - expected := new(CountHook) - expected.ToAdd = 1 - expected.ToChange = 0 - expected.ToRemoveAndAdd = 0 - expected.ToRemove = 0 - if !reflect.DeepEqual(expected, actual) { - t.Fatalf("Expected %#v, got %#v instead.", - expected, actual) + if errOutput := done(t).Stderr(); errOutput != "" { + t.Fatalf("unexpected error output:\n%s", errOutput) } } -func testOperationPlan(t *testing.T, configDir string) (*backend.Operation, func()) { +func testOperationPlan(t *testing.T, configDir string) (*backend.Operation, func(), func(*testing.T) *terminal.TestOutput) { t.Helper() _, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir) + streams, done := terminal.StreamsForTesting(t) + view := views.NewOperation(arguments.ViewHuman, false, views.NewView(streams)) + return &backend.Operation{ Type: backend.OperationTypePlan, ConfigDir: configDir, ConfigLoader: configLoader, - }, configCleanup + StateLocker: clistate.NewNoopLocker(), + View: view, + }, configCleanup, done } // testPlanState is just a common state that we use for testing plan. diff --git a/vendor/github.com/hashicorp/terraform/backend/local/backend_refresh.go b/vendor/github.com/hashicorp/terraform/backend/local/backend_refresh.go index af6058dc..54ecc07b 100644 --- a/vendor/github.com/hashicorp/terraform/backend/local/backend_refresh.go +++ b/vendor/github.com/hashicorp/terraform/backend/local/backend_refresh.go @@ -5,7 +5,6 @@ import ( "fmt" "log" "os" - "strings" "github.com/hashicorp/errwrap" "github.com/hashicorp/terraform/backend" @@ -36,7 +35,7 @@ func (b *Local) opRefresh( "Cannot read state file", fmt.Sprintf("Failed to read %s: %s", b.StatePath, err), )) - b.ReportResult(runningOp, diags) + op.ReportResult(runningOp, diags) return } } @@ -49,16 +48,16 @@ func (b *Local) opRefresh( tfCtx, _, opState, contextDiags := b.context(op) diags = diags.Append(contextDiags) if contextDiags.HasErrors() { - b.ReportResult(runningOp, diags) + op.ReportResult(runningOp, diags) return } // the state was locked during succesfull context creation; unlock the state // when the operation completes defer func() { - err := op.StateLocker.Unlock(nil) - if err != nil { - b.ShowDiagnostics(err) + diags := op.StateLocker.Unlock() + if diags.HasErrors() { + op.View.Diagnostics(diags) runningOp.Result = backend.OperationFailure } }() @@ -66,14 +65,11 @@ func (b *Local) opRefresh( // Set our state runningOp.State = opState.State() if !runningOp.State.HasResources() { - if b.CLI != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Warning, - "Empty or non-existent state", - "There are currently no resources tracked in the state, so there is nothing to refresh.", - )) - b.CLI.Output(b.Colorize().Color(strings.TrimSpace(refreshNoState) + "\n")) - } + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Warning, + "Empty or non-existent state", + "There are currently no resources tracked in the state, so there is nothing to refresh.", + )) } // Perform the refresh in a goroutine so we can be interrupted @@ -86,30 +82,25 @@ func (b *Local) opRefresh( log.Printf("[INFO] backend/local: refresh calling Refresh") }() - if b.opWait(doneCh, stopCtx, cancelCtx, tfCtx, opState) { + if b.opWait(doneCh, stopCtx, cancelCtx, tfCtx, opState, op.View) { return } - // write the resulting state to the running op + // Write the resulting state to the running op runningOp.State = newState diags = diags.Append(refreshDiags) if refreshDiags.HasErrors() { - b.ReportResult(runningOp, diags) + op.ReportResult(runningOp, diags) return } err := statemgr.WriteAndPersist(opState, newState) if err != nil { diags = diags.Append(errwrap.Wrapf("Failed to write state: {{err}}", err)) - b.ReportResult(runningOp, diags) + op.ReportResult(runningOp, diags) return } -} -const refreshNoState = ` -[reset][bold][yellow]Empty or non-existent state file.[reset][yellow] - -Refresh will do nothing. Refresh does not error or return an erroneous -exit status because many automation scripts use refresh, plan, then apply -and may not have a state file yet for the first run. -` + // Show any remaining warnings before exiting + op.ReportResult(runningOp, diags) +} diff --git a/vendor/github.com/hashicorp/terraform/backend/local/backend_refresh_test.go b/vendor/github.com/hashicorp/terraform/backend/local/backend_refresh_test.go index cb6cb9b4..bef5c5d0 100644 --- a/vendor/github.com/hashicorp/terraform/backend/local/backend_refresh_test.go +++ b/vendor/github.com/hashicorp/terraform/backend/local/backend_refresh_test.go @@ -3,12 +3,17 @@ package local import ( "context" "fmt" + "strings" "testing" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/internal/initwd" + "github.com/hashicorp/terraform/internal/terminal" "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/terraform" @@ -24,12 +29,13 @@ func TestLocal_refresh(t *testing.T) { testStateFile(t, b.StatePath, testRefreshState()) p.ReadResourceFn = nil - p.ReadResourceResponse = providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{ + p.ReadResourceResponse = &providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("yes"), })} - op, configCleanup := testOperationRefresh(t, "./testdata/refresh") + op, configCleanup, done := testOperationRefresh(t, "./testdata/refresh") defer configCleanup() + defer done(t) run, err := b.Operation(context.Background(), op) if err != nil { @@ -76,10 +82,10 @@ func TestLocal_refreshInput(t *testing.T) { testStateFile(t, b.StatePath, testRefreshState()) p.ReadResourceFn = nil - p.ReadResourceResponse = providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{ + p.ReadResourceResponse = &providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("yes"), })} - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { val := req.Config.GetAttr("value") if val.IsNull() || val.AsString() != "bar" { resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("incorrect value %#v", val)) @@ -92,8 +98,9 @@ func TestLocal_refreshInput(t *testing.T) { b.OpInput = true b.ContextOpts.UIInput = &terraform.MockUIInput{InputReturnString: "bar"} - op, configCleanup := testOperationRefresh(t, "./testdata/refresh-var-unset") + op, configCleanup, done := testOperationRefresh(t, "./testdata/refresh-var-unset") defer configCleanup() + defer done(t) op.UIIn = b.ContextOpts.UIInput run, err := b.Operation(context.Background(), op) @@ -119,15 +126,16 @@ func TestLocal_refreshValidate(t *testing.T) { p := TestLocalProvider(t, b, "test", refreshFixtureSchema()) testStateFile(t, b.StatePath, testRefreshState()) p.ReadResourceFn = nil - p.ReadResourceResponse = providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{ + p.ReadResourceResponse = &providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("yes"), })} // Enable validation b.OpValidation = true - op, configCleanup := testOperationRefresh(t, "./testdata/refresh") + op, configCleanup, done := testOperationRefresh(t, "./testdata/refresh") defer configCleanup() + defer done(t) run, err := b.Operation(context.Background(), op) if err != nil { @@ -135,8 +143,55 @@ func TestLocal_refreshValidate(t *testing.T) { } <-run.Done() - if !p.PrepareProviderConfigCalled { - t.Fatal("Prepare provider config should be called") + checkState(t, b.StateOutPath, ` +test_instance.foo: + ID = yes + provider = provider["registry.terraform.io/hashicorp/test"] + `) +} + +func TestLocal_refreshValidateProviderConfigured(t *testing.T) { + b, cleanup := TestLocal(t) + defer cleanup() + + schema := &terraform.ProviderSchema{ + Provider: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "value": {Type: cty.String, Optional: true}, + }, + }, + ResourceTypes: map[string]*configschema.Block{ + "test_instance": { + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + "ami": {Type: cty.String, Optional: true}, + }, + }, + }, + } + + p := TestLocalProvider(t, b, "test", schema) + testStateFile(t, b.StatePath, testRefreshState()) + p.ReadResourceFn = nil + p.ReadResourceResponse = &providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("yes"), + })} + + // Enable validation + b.OpValidation = true + + op, configCleanup, done := testOperationRefresh(t, "./testdata/refresh-provider-config") + defer configCleanup() + defer done(t) + + run, err := b.Operation(context.Background(), op) + if err != nil { + t.Fatalf("bad: %s", err) + } + <-run.Done() + + if !p.ValidateProviderConfigCalled { + t.Fatal("Validate provider config should be called") } checkState(t, b.StateOutPath, ` @@ -152,8 +207,9 @@ func TestLocal_refresh_context_error(t *testing.T) { b, cleanup := TestLocal(t) defer cleanup() testStateFile(t, b.StatePath, testRefreshState()) - op, configCleanup := testOperationRefresh(t, "./testdata/apply") + op, configCleanup, done := testOperationRefresh(t, "./testdata/apply") defer configCleanup() + defer done(t) // we coerce a failure in Context() by omitting the provider schema @@ -168,17 +224,55 @@ func TestLocal_refresh_context_error(t *testing.T) { assertBackendStateUnlocked(t, b) } -func testOperationRefresh(t *testing.T, configDir string) (*backend.Operation, func()) { +func TestLocal_refreshEmptyState(t *testing.T) { + b, cleanup := TestLocal(t) + defer cleanup() + + p := TestLocalProvider(t, b, "test", refreshFixtureSchema()) + testStateFile(t, b.StatePath, states.NewState()) + + p.ReadResourceFn = nil + p.ReadResourceResponse = &providers.ReadResourceResponse{NewState: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("yes"), + })} + + op, configCleanup, done := testOperationRefresh(t, "./testdata/refresh") + defer configCleanup() + + run, err := b.Operation(context.Background(), op) + if err != nil { + t.Fatalf("bad: %s", err) + } + <-run.Done() + + output := done(t) + + if stderr := output.Stderr(); stderr != "" { + t.Fatalf("expected only warning diags, got errors: %s", stderr) + } + if got, want := output.Stdout(), "Warning: Empty or non-existent state"; !strings.Contains(got, want) { + t.Errorf("wrong diags\n got: %s\nwant: %s", got, want) + } + + // the backend should be unlocked after a run + assertBackendStateUnlocked(t, b) +} + +func testOperationRefresh(t *testing.T, configDir string) (*backend.Operation, func(), func(*testing.T) *terminal.TestOutput) { t.Helper() _, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir) + streams, done := terminal.StreamsForTesting(t) + view := views.NewOperation(arguments.ViewHuman, false, views.NewView(streams)) + return &backend.Operation{ Type: backend.OperationTypeRefresh, ConfigDir: configDir, ConfigLoader: configLoader, - LockState: true, - }, configCleanup + StateLocker: clistate.NewNoopLocker(), + View: view, + }, configCleanup, done } // testRefreshState is just a common state that we use for testing refresh. diff --git a/vendor/github.com/hashicorp/terraform/backend/local/cli.go b/vendor/github.com/hashicorp/terraform/backend/local/cli.go index c3d7a65a..9963cbee 100644 --- a/vendor/github.com/hashicorp/terraform/backend/local/cli.go +++ b/vendor/github.com/hashicorp/terraform/backend/local/cli.go @@ -8,13 +8,9 @@ import ( // backend.CLI impl. func (b *Local) CLIInit(opts *backend.CLIOpts) error { - b.CLI = opts.CLI - b.CLIColor = opts.CLIColor - b.ShowDiagnostics = opts.ShowDiagnostics b.ContextOpts = opts.ContextOpts b.OpInput = opts.Input b.OpValidation = opts.Validation - b.RunningInAutomation = opts.RunningInAutomation // configure any new cli options if opts.StatePath != "" { diff --git a/vendor/github.com/hashicorp/terraform/backend/local/counthookaction_string.go b/vendor/github.com/hashicorp/terraform/backend/local/counthookaction_string.go deleted file mode 100644 index 59100474..00000000 --- a/vendor/github.com/hashicorp/terraform/backend/local/counthookaction_string.go +++ /dev/null @@ -1,25 +0,0 @@ -// Code generated by "stringer -type=countHookAction hook_count_action.go"; DO NOT EDIT. - -package local - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[countHookActionAdd-0] - _ = x[countHookActionChange-1] - _ = x[countHookActionRemove-2] -} - -const _countHookAction_name = "countHookActionAddcountHookActionChangecountHookActionRemove" - -var _countHookAction_index = [...]uint8{0, 18, 39, 60} - -func (i countHookAction) String() string { - if i >= countHookAction(len(_countHookAction_index)-1) { - return "countHookAction(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _countHookAction_name[_countHookAction_index[i]:_countHookAction_index[i+1]] -} diff --git a/vendor/github.com/hashicorp/terraform/backend/local/hook_count_action.go b/vendor/github.com/hashicorp/terraform/backend/local/hook_count_action.go deleted file mode 100644 index 9adcd904..00000000 --- a/vendor/github.com/hashicorp/terraform/backend/local/hook_count_action.go +++ /dev/null @@ -1,11 +0,0 @@ -package local - -//go:generate go run golang.org/x/tools/cmd/stringer -type=countHookAction hook_count_action.go - -type countHookAction byte - -const ( - countHookActionAdd countHookAction = iota - countHookActionChange - countHookActionRemove -) diff --git a/vendor/github.com/hashicorp/terraform/backend/local/hook_count_test.go b/vendor/github.com/hashicorp/terraform/backend/local/hook_count_test.go deleted file mode 100644 index 938e730d..00000000 --- a/vendor/github.com/hashicorp/terraform/backend/local/hook_count_test.go +++ /dev/null @@ -1,335 +0,0 @@ -package local - -import ( - "reflect" - "testing" - - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/plans" - "github.com/hashicorp/terraform/states" - "github.com/hashicorp/terraform/terraform" -) - -func TestCountHook_impl(t *testing.T) { - var _ terraform.Hook = new(CountHook) -} - -func TestCountHookPostDiff_DestroyDeposed(t *testing.T) { - h := new(CountHook) - - resources := map[string]*terraform.InstanceDiff{ - "lorem": &terraform.InstanceDiff{DestroyDeposed: true}, - } - - for k := range resources { - addr := addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test_instance", - Name: k, - }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) - - h.PostDiff(addr, states.DeposedKey("deadbeef"), plans.Delete, cty.DynamicVal, cty.DynamicVal) - } - - expected := new(CountHook) - expected.ToAdd = 0 - expected.ToChange = 0 - expected.ToRemoveAndAdd = 0 - expected.ToRemove = 1 - - if !reflect.DeepEqual(expected, h) { - t.Fatalf("Expected %#v, got %#v instead.", expected, h) - } -} - -func TestCountHookPostDiff_DestroyOnly(t *testing.T) { - h := new(CountHook) - - resources := map[string]*terraform.InstanceDiff{ - "foo": &terraform.InstanceDiff{Destroy: true}, - "bar": &terraform.InstanceDiff{Destroy: true}, - "lorem": &terraform.InstanceDiff{Destroy: true}, - "ipsum": &terraform.InstanceDiff{Destroy: true}, - } - - for k := range resources { - addr := addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test_instance", - Name: k, - }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) - - h.PostDiff(addr, states.CurrentGen, plans.Delete, cty.DynamicVal, cty.DynamicVal) - } - - expected := new(CountHook) - expected.ToAdd = 0 - expected.ToChange = 0 - expected.ToRemoveAndAdd = 0 - expected.ToRemove = 4 - - if !reflect.DeepEqual(expected, h) { - t.Fatalf("Expected %#v, got %#v instead.", expected, h) - } -} - -func TestCountHookPostDiff_AddOnly(t *testing.T) { - h := new(CountHook) - - resources := map[string]*terraform.InstanceDiff{ - "foo": &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{RequiresNew: true}, - }, - }, - "bar": &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{RequiresNew: true}, - }, - }, - "lorem": &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{RequiresNew: true}, - }, - }, - } - - for k := range resources { - addr := addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test_instance", - Name: k, - }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) - - h.PostDiff(addr, states.CurrentGen, plans.Create, cty.DynamicVal, cty.DynamicVal) - } - - expected := new(CountHook) - expected.ToAdd = 3 - expected.ToChange = 0 - expected.ToRemoveAndAdd = 0 - expected.ToRemove = 0 - - if !reflect.DeepEqual(expected, h) { - t.Fatalf("Expected %#v, got %#v instead.", expected, h) - } -} - -func TestCountHookPostDiff_ChangeOnly(t *testing.T) { - h := new(CountHook) - - resources := map[string]*terraform.InstanceDiff{ - "foo": &terraform.InstanceDiff{ - Destroy: false, - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{}, - }, - }, - "bar": &terraform.InstanceDiff{ - Destroy: false, - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{}, - }, - }, - "lorem": &terraform.InstanceDiff{ - Destroy: false, - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{}, - }, - }, - } - - for k := range resources { - addr := addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test_instance", - Name: k, - }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) - - h.PostDiff(addr, states.CurrentGen, plans.Update, cty.DynamicVal, cty.DynamicVal) - } - - expected := new(CountHook) - expected.ToAdd = 0 - expected.ToChange = 3 - expected.ToRemoveAndAdd = 0 - expected.ToRemove = 0 - - if !reflect.DeepEqual(expected, h) { - t.Fatalf("Expected %#v, got %#v instead.", expected, h) - } -} - -func TestCountHookPostDiff_Mixed(t *testing.T) { - h := new(CountHook) - - resources := map[string]plans.Action{ - "foo": plans.Delete, - "bar": plans.NoOp, - "lorem": plans.Update, - "ipsum": plans.Delete, - } - - for k, a := range resources { - addr := addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test_instance", - Name: k, - }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) - - h.PostDiff(addr, states.CurrentGen, a, cty.DynamicVal, cty.DynamicVal) - } - - expected := new(CountHook) - expected.ToAdd = 0 - expected.ToChange = 1 - expected.ToRemoveAndAdd = 0 - expected.ToRemove = 2 - - if !reflect.DeepEqual(expected, h) { - t.Fatalf("Expected %#v, got %#v instead.", - expected, h) - } -} - -func TestCountHookPostDiff_NoChange(t *testing.T) { - h := new(CountHook) - - resources := map[string]*terraform.InstanceDiff{ - "foo": &terraform.InstanceDiff{}, - "bar": &terraform.InstanceDiff{}, - "lorem": &terraform.InstanceDiff{}, - "ipsum": &terraform.InstanceDiff{}, - } - - for k := range resources { - addr := addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test_instance", - Name: k, - }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) - - h.PostDiff(addr, states.CurrentGen, plans.NoOp, cty.DynamicVal, cty.DynamicVal) - } - - expected := new(CountHook) - expected.ToAdd = 0 - expected.ToChange = 0 - expected.ToRemoveAndAdd = 0 - expected.ToRemove = 0 - - if !reflect.DeepEqual(expected, h) { - t.Fatalf("Expected %#v, got %#v instead.", - expected, h) - } -} - -func TestCountHookPostDiff_DataSource(t *testing.T) { - h := new(CountHook) - - resources := map[string]plans.Action{ - "foo": plans.Delete, - "bar": plans.NoOp, - "lorem": plans.Update, - "ipsum": plans.Delete, - } - - for k, a := range resources { - addr := addrs.Resource{ - Mode: addrs.DataResourceMode, - Type: "test_instance", - Name: k, - }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) - - h.PostDiff(addr, states.CurrentGen, a, cty.DynamicVal, cty.DynamicVal) - } - - expected := new(CountHook) - expected.ToAdd = 0 - expected.ToChange = 0 - expected.ToRemoveAndAdd = 0 - expected.ToRemove = 0 - - if !reflect.DeepEqual(expected, h) { - t.Fatalf("Expected %#v, got %#v instead.", - expected, h) - } -} - -func TestCountHookApply_ChangeOnly(t *testing.T) { - h := new(CountHook) - - resources := map[string]*terraform.InstanceDiff{ - "foo": &terraform.InstanceDiff{ - Destroy: false, - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{}, - }, - }, - "bar": &terraform.InstanceDiff{ - Destroy: false, - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{}, - }, - }, - "lorem": &terraform.InstanceDiff{ - Destroy: false, - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{}, - }, - }, - } - - for k := range resources { - addr := addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test_instance", - Name: k, - }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) - - h.PreApply(addr, states.CurrentGen, plans.Update, cty.DynamicVal, cty.DynamicVal) - h.PostApply(addr, states.CurrentGen, cty.DynamicVal, nil) - } - - expected := &CountHook{pending: make(map[string]plans.Action)} - expected.Added = 0 - expected.Changed = 3 - expected.Removed = 0 - - if !reflect.DeepEqual(expected, h) { - t.Fatalf("Expected:\n%#v\nGot:\n%#v\n", expected, h) - } -} - -func TestCountHookApply_DestroyOnly(t *testing.T) { - h := new(CountHook) - - resources := map[string]*terraform.InstanceDiff{ - "foo": &terraform.InstanceDiff{Destroy: true}, - "bar": &terraform.InstanceDiff{Destroy: true}, - "lorem": &terraform.InstanceDiff{Destroy: true}, - "ipsum": &terraform.InstanceDiff{Destroy: true}, - } - - for k := range resources { - addr := addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test_instance", - Name: k, - }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) - - h.PreApply(addr, states.CurrentGen, plans.Delete, cty.DynamicVal, cty.DynamicVal) - h.PostApply(addr, states.CurrentGen, cty.DynamicVal, nil) - } - - expected := &CountHook{pending: make(map[string]plans.Action)} - expected.Added = 0 - expected.Changed = 0 - expected.Removed = 4 - - if !reflect.DeepEqual(expected, h) { - t.Fatalf("Expected:\n%#v\nGot:\n%#v\n", expected, h) - } -} diff --git a/vendor/github.com/hashicorp/terraform/backend/local/testdata/destroy-with-ds/main.tf b/vendor/github.com/hashicorp/terraform/backend/local/testdata/destroy-with-ds/main.tf index 4ee80ea3..7062d896 100644 --- a/vendor/github.com/hashicorp/terraform/backend/local/testdata/destroy-with-ds/main.tf +++ b/vendor/github.com/hashicorp/terraform/backend/local/testdata/destroy-with-ds/main.tf @@ -1,4 +1,5 @@ resource "test_instance" "foo" { + count = 1 ami = "bar" } diff --git a/vendor/github.com/hashicorp/terraform/backend/local/testdata/plan-module-outputs-changed/main.tf b/vendor/github.com/hashicorp/terraform/backend/local/testdata/plan-module-outputs-changed/main.tf new file mode 100644 index 00000000..ba846846 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/local/testdata/plan-module-outputs-changed/main.tf @@ -0,0 +1,3 @@ +module "mod" { + source = "./mod" +} diff --git a/vendor/github.com/hashicorp/terraform/backend/local/testdata/plan-module-outputs-changed/mod/main.tf b/vendor/github.com/hashicorp/terraform/backend/local/testdata/plan-module-outputs-changed/mod/main.tf new file mode 100644 index 00000000..cee14bd9 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/local/testdata/plan-module-outputs-changed/mod/main.tf @@ -0,0 +1,3 @@ +output "changed" { + value = "after" +} diff --git a/vendor/github.com/hashicorp/terraform/backend/local/testdata/plan-outputs-changed/main.tf b/vendor/github.com/hashicorp/terraform/backend/local/testdata/plan-outputs-changed/main.tf index c1686a89..1df236ff 100644 --- a/vendor/github.com/hashicorp/terraform/backend/local/testdata/plan-outputs-changed/main.tf +++ b/vendor/github.com/hashicorp/terraform/backend/local/testdata/plan-outputs-changed/main.tf @@ -1,3 +1,7 @@ +module "submodule" { + source = "./submodule" +} + output "changed" { value = "after" } diff --git a/vendor/github.com/hashicorp/terraform/backend/local/testdata/plan-outputs-changed/submodule/main.tf b/vendor/github.com/hashicorp/terraform/backend/local/testdata/plan-outputs-changed/submodule/main.tf new file mode 100644 index 00000000..ae32f8aa --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/local/testdata/plan-outputs-changed/submodule/main.tf @@ -0,0 +1,3 @@ +output "foo" { + value = "bar" +} diff --git a/vendor/github.com/hashicorp/terraform/backend/local/testdata/plan-scaleout/main.tf b/vendor/github.com/hashicorp/terraform/backend/local/testdata/plan-scaleout/main.tf deleted file mode 100644 index 4fc97baf..00000000 --- a/vendor/github.com/hashicorp/terraform/backend/local/testdata/plan-scaleout/main.tf +++ /dev/null @@ -1,10 +0,0 @@ -resource "test_instance" "foo" { - count = 2 - ami = "bar" - - # This is here because at some point it caused a test failure - network_interface { - device_index = 0 - description = "Main network interface" - } -} diff --git a/vendor/github.com/hashicorp/terraform/backend/local/testdata/refresh-provider-config/main.tf b/vendor/github.com/hashicorp/terraform/backend/local/testdata/refresh-provider-config/main.tf new file mode 100644 index 00000000..f3a3ebb8 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/local/testdata/refresh-provider-config/main.tf @@ -0,0 +1,7 @@ +resource "test_instance" "foo" { + ami = "bar" +} + +provider "test" { + value = "foo" +} diff --git a/vendor/github.com/hashicorp/terraform/backend/local/testing.go b/vendor/github.com/hashicorp/terraform/backend/local/testing.go index 0e6d426f..87e57fc3 100644 --- a/vendor/github.com/hashicorp/terraform/backend/local/testing.go +++ b/vendor/github.com/hashicorp/terraform/backend/local/testing.go @@ -15,7 +15,6 @@ import ( "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/states/statemgr" "github.com/hashicorp/terraform/terraform" - "github.com/hashicorp/terraform/tfdiags" ) // TestLocal returns a configured Local struct with temporary paths and @@ -34,26 +33,6 @@ func TestLocal(t *testing.T) (*Local, func()) { local.StateWorkspaceDir = filepath.Join(tempDir, "state.tfstate.d") local.ContextOpts = &terraform.ContextOpts{} - local.ShowDiagnostics = func(vals ...interface{}) { - var diags tfdiags.Diagnostics - diags = diags.Append(vals...) - for _, diag := range diags { - // NOTE: Since the caller here is not directly the TestLocal - // function, t.Helper doesn't apply and so the log source - // isn't correctly shown in the test log output. This seems - // unavoidable as long as this is happening so indirectly. - desc := diag.Description() - if desc.Detail != "" { - t.Logf("%s: %s", desc.Summary, desc.Detail) - } else { - t.Log(desc.Summary) - } - if local.CLI != nil { - local.CLI.Error(desc.Summary) - } - } - } - cleanup := func() { if err := os.RemoveAll(tempDir); err != nil { t.Fatal("error cleanup up test:", err) @@ -72,7 +51,21 @@ func TestLocalProvider(t *testing.T, b *Local, name string, schema *terraform.Pr if schema == nil { schema = &terraform.ProviderSchema{} // default schema is empty } - p.GetSchemaReturn = schema + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{Block: schema.Provider}, + ProviderMeta: providers.Schema{Block: schema.ProviderMeta}, + ResourceTypes: map[string]providers.Schema{}, + DataSources: map[string]providers.Schema{}, + } + for name, res := range schema.ResourceTypes { + p.GetProviderSchemaResponse.ResourceTypes[name] = providers.Schema{ + Block: res, + Version: int64(schema.ResourceTypeSchemaVersions[name]), + } + } + for name, dat := range schema.DataSources { + p.GetProviderSchemaResponse.DataSources[name] = providers.Schema{Block: dat} + } p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { rSchema, _ := schema.SchemaForResourceType(addrs.ManagedResourceMode, req.TypeName) @@ -111,7 +104,7 @@ func TestLocalProvider(t *testing.T, b *Local, name string, schema *terraform.Pr b.ContextOpts = &terraform.ContextOpts{} } - // Setup our provider + // Set up our provider b.ContextOpts.Providers = map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider(name): providers.FactoryFixed(p), } diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/artifactory/backend.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/artifactory/backend.go index 2062968a..8f504a61 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/artifactory/backend.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/artifactory/backend.go @@ -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" diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/arm_client.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/arm_client.go index e174f9ed..53a43664 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/arm_client.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/arm_client.go @@ -11,7 +11,7 @@ import ( "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/containers" "github.com/Azure/azure-sdk-for-go/profiles/2017-03-09/resources/mgmt/resources" - armStorage "github.com/Azure/azure-sdk-for-go/profiles/2017-03-09/storage/mgmt/storage" + armStorage "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2021-01-01/storage" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/azure" "github.com/hashicorp/go-azure-helpers/authentication" @@ -26,6 +26,9 @@ type ArmClient struct { containersClient *containers.Client blobsClient *blobs.Client + // azureAdStorageAuth is only here if we're using AzureAD Authentication but is an Authorizer for Storage + azureAdStorageAuth *autorest.Authorizer + accessKey string environment azure.Environment resourceGroupName string @@ -62,7 +65,7 @@ func buildArmClient(ctx context.Context, config BackendConfig) (*ArmClient, erro SubscriptionID: config.SubscriptionID, TenantID: config.TenantID, CustomResourceManagerEndpoint: config.CustomResourceManagerEndpoint, - MetadataURL: config.MetadataHost, + MetadataHost: config.MetadataHost, Environment: config.Environment, ClientSecretDocsLink: "https://www.terraform.io/docs/providers/azurerm/guides/service_principal_client_secret.html", @@ -92,11 +95,20 @@ func buildArmClient(ctx context.Context, config BackendConfig) (*ArmClient, erro return nil, err } - auth, err := armConfig.GetAuthorizationToken(sender.BuildSender("backend/remote-state/azure"), oauthConfig, env.TokenAudience) + sender := sender.BuildSender("backend/remote-state/azure") + auth, err := armConfig.GetAuthorizationToken(sender, oauthConfig, env.TokenAudience) if err != nil { return nil, err } + if config.UseAzureADAuthentication { + storageAuth, err := armConfig.GetAuthorizationToken(sender, oauthConfig, env.ResourceIdentifiers.Storage) + if err != nil { + return nil, err + } + client.azureAdStorageAuth = &storageAuth + } + accountsClient := armStorage.NewAccountsClientWithBaseURI(env.ResourceManagerEndpoint, armConfig.SubscriptionID) client.configureClient(&accountsClient.Client, auth) client.storageAccountsClient = &accountsClient @@ -109,6 +121,8 @@ func buildArmClient(ctx context.Context, config BackendConfig) (*ArmClient, erro } func buildArmEnvironment(config BackendConfig) (*azure.Environment, error) { + // TODO: can we remove this? + // https://github.com/hashicorp/terraform/issues/27156 if config.CustomResourceManagerEndpoint != "" { log.Printf("[DEBUG] Loading Environment from Endpoint %q", config.CustomResourceManagerEndpoint) return authentication.LoadEnvironmentFromUrl(config.CustomResourceManagerEndpoint) @@ -131,10 +145,16 @@ func (c ArmClient) getBlobClient(ctx context.Context) (*blobs.Client, error) { return &blobsClient, nil } + if c.azureAdStorageAuth != nil { + blobsClient := blobs.NewWithEnvironment(c.environment) + c.configureClient(&blobsClient.Client, *c.azureAdStorageAuth) + return &blobsClient, nil + } + accessKey := c.accessKey if accessKey == "" { log.Printf("[DEBUG] Building the Blob Client from an Access Token (using user credentials)") - keys, err := c.storageAccountsClient.ListKeys(ctx, c.resourceGroupName, c.storageAccountName) + keys, err := c.storageAccountsClient.ListKeys(ctx, c.resourceGroupName, c.storageAccountName, "") if err != nil { return nil, fmt.Errorf("Error retrieving keys for Storage Account %q: %s", c.storageAccountName, err) } @@ -169,10 +189,17 @@ func (c ArmClient) getContainersClient(ctx context.Context) (*containers.Client, c.configureClient(&containersClient.Client, storageAuth) return &containersClient, nil } + + if c.azureAdStorageAuth != nil { + containersClient := containers.NewWithEnvironment(c.environment) + c.configureClient(&containersClient.Client, *c.azureAdStorageAuth) + return &containersClient, nil + } + accessKey := c.accessKey if accessKey == "" { log.Printf("[DEBUG] Building the Container Client from an Access Token (using user credentials)") - keys, err := c.storageAccountsClient.ListKeys(ctx, c.resourceGroupName, c.storageAccountName) + keys, err := c.storageAccountsClient.ListKeys(ctx, c.resourceGroupName, c.storageAccountName, "") if err != nil { return nil, fmt.Errorf("Error retrieving keys for Storage Account %q: %s", c.storageAccountName, err) } diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/backend.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/backend.go index 00995d97..8b8c7d32 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/backend.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/backend.go @@ -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. @@ -125,7 +125,7 @@ func New() backend.Backend { "use_msi": { Type: schema.TypeBool, Optional: true, - Description: "Should Managed Service Identity be used?.", + Description: "Should Managed Service Identity be used?", DefaultFunc: schema.EnvDefaultFunc("ARM_USE_MSI", false), }, "msi_endpoint": { @@ -135,33 +135,12 @@ func New() backend.Backend { DefaultFunc: schema.EnvDefaultFunc("ARM_MSI_ENDPOINT", ""), }, - // Deprecated fields - "arm_client_id": { - Type: schema.TypeString, - Optional: true, - Description: "The Client ID.", - Deprecated: "`arm_client_id` has been replaced by `client_id`", - }, - - "arm_client_secret": { - Type: schema.TypeString, - Optional: true, - Description: "The Client Secret.", - Deprecated: "`arm_client_secret` has been replaced by `client_secret`", - }, - - "arm_subscription_id": { - Type: schema.TypeString, - Optional: true, - Description: "The Subscription ID.", - Deprecated: "`arm_subscription_id` has been replaced by `subscription_id`", - }, - - "arm_tenant_id": { - Type: schema.TypeString, + // Feature Flags + "use_azuread_auth": { + Type: schema.TypeBool, Optional: true, - Description: "The Tenant ID.", - Deprecated: "`arm_tenant_id` has been replaced by `tenant_id`", + Description: "Should Terraform use AzureAD Authentication to access the Blob?", + DefaultFunc: schema.EnvDefaultFunc("ARM_USE_AZUREAD", false), }, }, } @@ -201,6 +180,7 @@ type BackendConfig struct { SubscriptionID string TenantID string UseMsi bool + UseAzureADAuthentication bool } func (b *Backend) configure(ctx context.Context) error { @@ -215,18 +195,12 @@ func (b *Backend) configure(ctx context.Context) error { b.keyName = data.Get("key").(string) b.snapshot = data.Get("snapshot").(bool) - // support for previously deprecated fields - clientId := valueFromDeprecatedField(data, "client_id", "arm_client_id") - clientSecret := valueFromDeprecatedField(data, "client_secret", "arm_client_secret") - subscriptionId := valueFromDeprecatedField(data, "subscription_id", "arm_subscription_id") - tenantId := valueFromDeprecatedField(data, "tenant_id", "arm_tenant_id") - config := BackendConfig{ AccessKey: data.Get("access_key").(string), - ClientID: clientId, + ClientID: data.Get("client_id").(string), ClientCertificatePassword: data.Get("client_certificate_password").(string), ClientCertificatePath: data.Get("client_certificate_path").(string), - ClientSecret: clientSecret, + ClientSecret: data.Get("client_secret").(string), CustomResourceManagerEndpoint: data.Get("endpoint").(string), MetadataHost: data.Get("metadata_host").(string), Environment: data.Get("environment").(string), @@ -234,9 +208,10 @@ func (b *Backend) configure(ctx context.Context) error { ResourceGroupName: data.Get("resource_group_name").(string), SasToken: data.Get("sas_token").(string), StorageAccountName: data.Get("storage_account_name").(string), - SubscriptionID: subscriptionId, - TenantID: tenantId, + SubscriptionID: data.Get("subscription_id").(string), + TenantID: data.Get("tenant_id").(string), UseMsi: data.Get("use_msi").(bool), + UseAzureADAuthentication: data.Get("use_azuread_auth").(bool), } armClient, err := buildArmClient(context.TODO(), config) @@ -244,20 +219,11 @@ func (b *Backend) configure(ctx context.Context) error { return err } - if config.AccessKey == "" && config.SasToken == "" && config.ResourceGroupName == "" { - return fmt.Errorf("Either an Access Key / SAS Token or the Resource Group for the Storage Account must be specified") + thingsNeededToLookupAccessKeySpecified := config.AccessKey == "" && config.SasToken == "" && config.ResourceGroupName == "" + if thingsNeededToLookupAccessKeySpecified && !config.UseAzureADAuthentication { + return fmt.Errorf("Either an Access Key / SAS Token or the Resource Group for the Storage Account must be specified - or Azure AD Authentication must be enabled") } b.armClient = armClient return nil } - -func valueFromDeprecatedField(d *schema.ResourceData, key, deprecatedFieldKey string) string { - v := d.Get(key).(string) - - if v == "" { - v = d.Get(deprecatedFieldKey).(string) - } - - return v -} diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/backend_state.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/backend_state.go index e7d31628..9017690e 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/backend_state.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/backend_state.go @@ -95,9 +95,13 @@ func (b *Backend) StateMgr(name string) (statemgr.Full, error) { stateMgr := &remote.State{Client: client} + // Grab the value + if err := stateMgr.RefreshState(); err != nil { + return nil, err + } //if this isn't the default state name, we need to create the object so //it's listed by States. - if name != backend.DefaultStateName { + if v := stateMgr.State(); v == nil { // take a lock on this state while we write it lockInfo := statemgr.NewLockInfo() lockInfo.Operation = "init" @@ -119,9 +123,10 @@ func (b *Backend) StateMgr(name string) (statemgr.Full, error) { err = lockUnlock(err) return nil, err } - - // If we have no state, we have to create an empty state + //if this isn't the default state name, we need to create the object so + //it's listed by States. if v := stateMgr.State(); v == nil { + // If we have no state, we have to create an empty state if err := stateMgr.WriteState(states.NewState()); err != nil { err = lockUnlock(err) return nil, err @@ -130,13 +135,12 @@ func (b *Backend) StateMgr(name string) (statemgr.Full, error) { err = lockUnlock(err) return nil, err } - } - // Unlock, the state should now be initialized - if err := lockUnlock(nil); err != nil { - return nil, err + // Unlock, the state should now be initialized + if err := lockUnlock(nil); err != nil { + return nil, err + } } - } return stateMgr, nil diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/backend_test.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/backend_test.go index 3f0a2853..c292fd63 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/backend_test.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/backend_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/hashicorp/terraform/backend" - "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/internal/legacy/helper/acctest" ) func TestBackend_impl(t *testing.T) { @@ -123,6 +123,34 @@ func TestBackendSASTokenBasic(t *testing.T) { backend.TestBackendStates(t, b) } +func TestBackendAzureADAuthBasic(t *testing.T) { + testAccAzureBackend(t) + rs := acctest.RandString(4) + res := testResourceNames(rs, "testState") + res.useAzureADAuth = true + armClient := buildTestClient(t, res) + + ctx := context.TODO() + err := armClient.buildTestResources(ctx, &res) + defer armClient.destroyTestResources(ctx, res) + if err != nil { + armClient.destroyTestResources(ctx, res) + t.Fatalf("Error creating Test Resources: %q", err) + } + + b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ + "storage_account_name": res.storageAccountName, + "container_name": res.storageContainerName, + "key": res.storageKeyName, + "access_key": res.storageAccountAccessKey, + "environment": os.Getenv("ARM_ENVIRONMENT"), + "endpoint": os.Getenv("ARM_ENDPOINT"), + "use_azuread_auth": true, + })).(*Backend) + + backend.TestBackendStates(t, b) +} + func TestBackendServicePrincipalClientCertificateBasic(t *testing.T) { testAccAzureBackend(t) @@ -258,6 +286,9 @@ func TestBackendAccessKeyLocked(t *testing.T) { backend.TestBackendStateLocks(t, b1, b2) backend.TestBackendStateForceUnlock(t, b1, b2) + + backend.TestBackendStateLocksInWS(t, b1, b2, "foo") + backend.TestBackendStateForceUnlockInWS(t, b1, b2, "foo") } func TestBackendServicePrincipalLocked(t *testing.T) { @@ -301,4 +332,7 @@ func TestBackendServicePrincipalLocked(t *testing.T) { backend.TestBackendStateLocks(t, b1, b2) backend.TestBackendStateForceUnlock(t, b1, b2) + + backend.TestBackendStateLocksInWS(t, b1, b2, "foo") + backend.TestBackendStateForceUnlockInWS(t, b1, b2, "foo") } diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/client.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/client.go index 6c120e72..d3066454 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/client.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/client.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "log" + "net/http" "github.com/hashicorp/go-multierror" "github.com/hashicorp/go-uuid" @@ -39,7 +40,7 @@ func (c *RemoteClient) Get() (*remote.Payload, error) { ctx := context.TODO() blob, err := c.giovanniBlobClient.Get(ctx, c.accountName, c.containerName, c.keyName, options) if err != nil { - if blob.Response.StatusCode == 404 { + if blob.Response.IsHTTPStatus(http.StatusNotFound) { return nil, nil } return nil, err @@ -109,7 +110,7 @@ func (c *RemoteClient) Delete() error { ctx := context.TODO() resp, err := c.giovanniBlobClient.Delete(ctx, c.accountName, c.containerName, c.keyName, options) if err != nil { - if resp.Response.StatusCode != 404 { + if !resp.IsHTTPStatus(http.StatusNotFound) { return err } } @@ -151,7 +152,7 @@ func (c *RemoteClient) Lock(info *statemgr.LockInfo) (string, error) { properties, err := c.giovanniBlobClient.GetProperties(ctx, c.accountName, c.containerName, c.keyName, blobs.GetPropertiesInput{}) if err != nil { // error if we had issues getting the blob - if properties.Response.StatusCode != 404 { + if !properties.Response.IsHTTPStatus(http.StatusNotFound) { return "", getLockInfoErr(err) } // if we don't find the blob, we need to build it diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/client_test.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/client_test.go index 45af094a..c254e9ca 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/client_test.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/client_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/hashicorp/terraform/backend" - "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/internal/legacy/helper/acctest" "github.com/hashicorp/terraform/states/remote" "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/blobs" ) diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/helpers_test.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/helpers_test.go index a9879321..c1d2ffd4 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/helpers_test.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/azure/helpers_test.go @@ -10,7 +10,7 @@ import ( "time" "github.com/Azure/azure-sdk-for-go/profiles/2017-03-09/resources/mgmt/resources" - armStorage "github.com/Azure/azure-sdk-for-go/profiles/2017-03-09/storage/mgmt/storage" + armStorage "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2021-01-01/storage" "github.com/Azure/go-autorest/autorest" sasStorage "github.com/hashicorp/go-azure-helpers/storage" "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/containers" @@ -83,6 +83,7 @@ func buildTestClient(t *testing.T, res resourceNames) *ArmClient { ResourceGroupName: res.resourceGroup, StorageAccountName: res.storageAccountName, UseMsi: msiEnabled, + UseAzureADAuthentication: res.useAzureADAuth, }) if err != nil { t.Fatalf("Failed to build ArmClient: %+v", err) @@ -125,6 +126,7 @@ type resourceNames struct { storageContainerName string storageKeyName string storageAccountAccessKey string + useAzureADAuth bool } func testResourceNames(rString string, keyName string) resourceNames { @@ -134,6 +136,7 @@ func testResourceNames(rString string, keyName string) resourceNames { storageAccountName: fmt.Sprintf("acctestsa%s", rString), storageContainerName: "acctestcont", storageKeyName: keyName, + useAzureADAuth: false, } } @@ -145,13 +148,20 @@ func (c *ArmClient) buildTestResources(ctx context.Context, names *resourceNames } log.Printf("Creating Storage Account %q in Resource Group %q", names.storageAccountName, names.resourceGroup) - future, err := c.storageAccountsClient.Create(ctx, names.resourceGroup, names.storageAccountName, armStorage.AccountCreateParameters{ + storageProps := armStorage.AccountCreateParameters{ Sku: &armStorage.Sku{ Name: armStorage.StandardLRS, Tier: armStorage.Standard, }, Location: &names.location, - }) + } + if names.useAzureADAuth { + allowSharedKeyAccess := false + storageProps.AccountPropertiesCreateParameters = &armStorage.AccountPropertiesCreateParameters{ + AllowSharedKeyAccess: &allowSharedKeyAccess, + } + } + future, err := c.storageAccountsClient.Create(ctx, names.resourceGroup, names.storageAccountName, storageProps) if err != nil { return fmt.Errorf("failed to create test storage account: %s", err) } @@ -161,23 +171,27 @@ func (c *ArmClient) buildTestResources(ctx context.Context, names *resourceNames return fmt.Errorf("failed waiting for the creation of storage account: %s", err) } - log.Printf("fetching access key for storage account") - resp, err := c.storageAccountsClient.ListKeys(ctx, names.resourceGroup, names.storageAccountName) - if err != nil { - return fmt.Errorf("failed to list storage account keys %s:", err) - } - - keys := *resp.Keys - accessKey := *keys[0].Value - names.storageAccountAccessKey = accessKey - - storageAuth, err := autorest.NewSharedKeyAuthorizer(names.storageAccountName, accessKey, autorest.SharedKey) - if err != nil { - return fmt.Errorf("Error building Authorizer: %+v", err) - } - containersClient := containers.NewWithEnvironment(c.environment) - containersClient.Client.Authorizer = storageAuth + if names.useAzureADAuth { + containersClient.Client.Authorizer = *c.azureAdStorageAuth + } else { + log.Printf("fetching access key for storage account") + resp, err := c.storageAccountsClient.ListKeys(ctx, names.resourceGroup, names.storageAccountName, "") + if err != nil { + return fmt.Errorf("failed to list storage account keys %s:", err) + } + + keys := *resp.Keys + accessKey := *keys[0].Value + names.storageAccountAccessKey = accessKey + + storageAuth, err := autorest.NewSharedKeyAuthorizer(names.storageAccountName, accessKey, autorest.SharedKey) + if err != nil { + return fmt.Errorf("Error building Authorizer: %+v", err) + } + + containersClient.Client.Authorizer = storageAuth + } log.Printf("Creating Container %q in Storage Account %q (Resource Group %q)", names.storageContainerName, names.storageAccountName, names.resourceGroup) _, err = containersClient.Create(ctx, names.storageAccountName, names.storageContainerName, containers.CreateInput{}) diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/consul/backend.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/consul/backend.go index 271a60b6..ebe62471 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/consul/backend.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/consul/backend.go @@ -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. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/cos/backend.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/cos/backend.go index ce502e5c..fa358aa1 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/cos/backend.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/cos/backend.go @@ -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" diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv2/backend.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv2/backend.go index 9f9fa090..ee0f0bda 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv2/backend.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv2/backend.go @@ -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" ) diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv3/backend.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv3/backend.go index fb3f5e20..1bf5809b 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv3/backend.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv3/backend.go @@ -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 ( diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv3/backend_state.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv3/backend_state.go index 1b8b1882..1f514a8f 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv3/backend_state.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv3/backend_state.go @@ -23,7 +23,9 @@ func (b *Backend) Workspaces() ([]string, error) { result := make([]string, 1, len(res.Kvs)+1) result[0] = backend.DefaultStateName for _, kv := range res.Kvs { - result = append(result, strings.TrimPrefix(string(kv.Key), b.prefix)) + if strings.TrimPrefix(string(kv.Key), b.prefix) != backend.DefaultStateName { + result = append(result, strings.TrimPrefix(string(kv.Key), b.prefix)) + } } sort.Strings(result[1:]) diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv3/backend_test.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv3/backend_test.go index b37f8e3e..4b05220e 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv3/backend_test.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv3/backend_test.go @@ -60,12 +60,12 @@ func TestBackend(t *testing.T) { // Get the backend. We need two to test locking. b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "endpoints": etcdv3Endpoints, + "endpoints": stringsToInterfaces(etcdv3Endpoints), "prefix": prefix, })) b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "endpoints": etcdv3Endpoints, + "endpoints": stringsToInterfaces(etcdv3Endpoints), "prefix": prefix, })) @@ -83,13 +83,13 @@ func TestBackend_lockDisabled(t *testing.T) { // Get the backend. We need two to test locking. b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "endpoints": etcdv3Endpoints, + "endpoints": stringsToInterfaces(etcdv3Endpoints), "prefix": prefix, "lock": false, })) b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "endpoints": etcdv3Endpoints, + "endpoints": stringsToInterfaces(etcdv3Endpoints), "prefix": prefix + "/" + "different", // Diff so locking test would fail if it was locking "lock": false, })) @@ -97,3 +97,11 @@ func TestBackend_lockDisabled(t *testing.T) { // Test backend.TestBackendStateLocks(t, b1, b2) } + +func stringsToInterfaces(strSlice []string) []interface{} { + var interfaceSlice []interface{} + for _, v := range strSlice { + interfaceSlice = append(interfaceSlice, v) + } + return interfaceSlice +} diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv3/client_test.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv3/client_test.go index 8abb22a7..69f9c944 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv3/client_test.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/etcdv3/client_test.go @@ -23,7 +23,7 @@ func TestRemoteClient(t *testing.T) { // Get the backend b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "endpoints": etcdv3Endpoints, + "endpoints": stringsToInterfaces(etcdv3Endpoints), "prefix": prefix, })) @@ -45,7 +45,7 @@ func TestEtcdv3_stateLock(t *testing.T) { // Get the backend s1, err := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "endpoints": etcdv3Endpoints, + "endpoints": stringsToInterfaces(etcdv3Endpoints), "prefix": prefix, })).StateMgr(backend.DefaultStateName) if err != nil { @@ -53,7 +53,7 @@ func TestEtcdv3_stateLock(t *testing.T) { } s2, err := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "endpoints": etcdv3Endpoints, + "endpoints": stringsToInterfaces(etcdv3Endpoints), "prefix": prefix, })).StateMgr(backend.DefaultStateName) if err != nil { @@ -71,7 +71,7 @@ func TestEtcdv3_destroyLock(t *testing.T) { // Get the backend b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ - "endpoints": etcdv3Endpoints, + "endpoints": stringsToInterfaces(etcdv3Endpoints), "prefix": prefix, })) diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/gcs/backend.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/gcs/backend.go index 1ad3078b..8184dafb 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/gcs/backend.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/gcs/backend.go @@ -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" @@ -27,9 +27,8 @@ type Backend struct { storageClient *storage.Client storageContext context.Context - bucketName string - prefix string - defaultStateFile string + bucketName string + prefix string encryptionKey []byte } @@ -45,13 +44,6 @@ func New() backend.Backend { Description: "The name of the Google Cloud Storage bucket", }, - "path": { - Type: schema.TypeString, - Optional: true, - Description: "Path of the default state file", - Deprecated: "Use the \"prefix\" option instead", - }, - "prefix": { Type: schema.TypeString, Optional: true, @@ -74,6 +66,22 @@ func New() backend.Backend { Description: "An OAuth2 token used for GCP authentication", }, + "impersonate_service_account": { + Type: schema.TypeString, + Optional: true, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_IMPERSONATE_SERVICE_ACCOUNT", + }, nil), + Description: "The service account to impersonate for all Google API Calls", + }, + + "impersonate_service_account_delegates": { + Type: schema.TypeList, + Optional: true, + Description: "The delegation chain for the impersonated service account", + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "encryption_key": { Type: schema.TypeString, Optional: true, @@ -121,8 +129,6 @@ func (b *Backend) configure(ctx context.Context) error { b.prefix = b.prefix + "/" } - b.defaultStateFile = strings.TrimLeft(data.Get("path").(string), "/") - var opts []option.ClientOption // Add credential source @@ -168,6 +174,24 @@ func (b *Backend) configure(ctx context.Context) error { opts = append(opts, option.WithScopes(storage.ScopeReadWrite)) } + // Service Account Impersonation + if v, ok := data.GetOk("impersonate_service_account"); ok { + ServiceAccount := v.(string) + opts = append(opts, option.ImpersonateCredentials(ServiceAccount)) + + if v, ok := data.GetOk("impersonate_service_account_delegates"); ok { + var delegates []string + d := v.([]interface{}) + if len(delegates) > 0 { + delegates = make([]string, len(d)) + } + for _, delegate := range d { + delegates = append(delegates, delegate.(string)) + } + opts = append(opts, option.ImpersonateCredentials(ServiceAccount, delegates...)) + } + } + opts = append(opts, option.WithUserAgent(httpclient.UserAgentString())) client, err := storage.NewClient(b.storageContext, opts...) if err != nil { diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/gcs/backend_state.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/gcs/backend_state.go index d4916190..a7e511cd 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/gcs/backend_state.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/gcs/backend_state.go @@ -146,15 +146,9 @@ func (b *Backend) StateMgr(name string) (statemgr.Full, error) { } func (b *Backend) stateFile(name string) string { - if name == backend.DefaultStateName && b.defaultStateFile != "" { - return b.defaultStateFile - } return path.Join(b.prefix, name+stateFileSuffix) } func (b *Backend) lockFile(name string) string { - if name == backend.DefaultStateName && b.defaultStateFile != "" { - return strings.TrimSuffix(b.defaultStateFile, stateFileSuffix) + lockFileSuffix - } return path.Join(b.prefix, name+lockFileSuffix) } diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/gcs/backend_test.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/gcs/backend_test.go index 6d71cb34..dd089aeb 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/gcs/backend_test.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/gcs/backend_test.go @@ -25,23 +25,19 @@ func TestStateFile(t *testing.T) { t.Parallel() cases := []struct { - prefix string - defaultStateFile string - name string - wantStateFile string - wantLockFile string + prefix string + name string + wantStateFile string + wantLockFile string }{ - {"state", "", "default", "state/default.tfstate", "state/default.tflock"}, - {"state", "", "test", "state/test.tfstate", "state/test.tflock"}, - {"state", "legacy.tfstate", "default", "legacy.tfstate", "legacy.tflock"}, - {"state", "legacy.tfstate", "test", "state/test.tfstate", "state/test.tflock"}, - {"state", "legacy.state", "default", "legacy.state", "legacy.state.tflock"}, - {"state", "legacy.state", "test", "state/test.tfstate", "state/test.tflock"}, + {"state", "default", "state/default.tfstate", "state/default.tflock"}, + {"state", "test", "state/test.tfstate", "state/test.tflock"}, + {"state", "test", "state/test.tfstate", "state/test.tflock"}, + {"state", "test", "state/test.tfstate", "state/test.tflock"}, } for _, c := range cases { b := &Backend{ - prefix: c.prefix, - defaultStateFile: c.defaultStateFile, + prefix: c.prefix, } if got := b.stateFile(c.name); got != c.wantStateFile { diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/http/backend.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/http/backend.go index 12076e01..dee59f48 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/http/backend.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/http/backend.go @@ -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" ) diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/http/client.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/http/client.go index 9dd91ff2..4c299521 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/http/client.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/http/client.go @@ -49,7 +49,7 @@ func (c *httpClient) httpRequest(method string, url *url.URL, data *[]byte, what if err != nil { return nil, fmt.Errorf("Failed to make %s HTTP request: %s", what, err) } - // Setup basic auth + // Set up basic auth if c.Username != "" { req.SetBasicAuth(c.Username, c.Password) } diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/inmem/backend.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/inmem/backend.go index 1a974a05..035f3c97 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/inmem/backend.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/inmem/backend.go @@ -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" diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/kubernetes/backend.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/kubernetes/backend.go index eed598f8..a3398a46 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/kubernetes/backend.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/kubernetes/backend.go @@ -6,11 +6,11 @@ import ( "fmt" "log" "os" + "path/filepath" "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" k8sSchema "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" @@ -114,16 +114,17 @@ func New() backend.Backend { DefaultFunc: schema.EnvDefaultFunc("KUBE_CLUSTER_CA_CERT_DATA", ""), Description: "PEM-encoded root certificates bundle for TLS authentication.", }, + "config_paths": { + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + Description: "A list of paths to kube config files. Can be set with KUBE_CONFIG_PATHS environment variable.", + }, "config_path": { - Type: schema.TypeString, - Optional: true, - DefaultFunc: schema.MultiEnvDefaultFunc( - []string{ - "KUBE_CONFIG", - "KUBECONFIG", - }, - "~/.kube/config"), - Description: "Path to the kube config file, defaults to ~/.kube/config", + Type: schema.TypeString, + Optional: true, + DefaultFunc: schema.EnvDefaultFunc("KUBE_CONFIG_PATH", ""), + Description: "Path to the kube config file. Can be set with KUBE_CONFIG_PATH environment variable.", }, "config_context": { Type: schema.TypeString, @@ -285,15 +286,7 @@ func getInitialConfig(data *schema.ResourceData) (*restclient.Config, error) { var cfg *restclient.Config var err error - c := &cli.BasicUi{Writer: os.Stdout} - inCluster := data.Get("in_cluster_config").(bool) - cf := data.Get("load_config_file").(bool) - - if !inCluster && !cf { - c.Output(noConfigError) - } - if inCluster { cfg, err = restclient.InClusterConfig() if err != nil { @@ -313,13 +306,34 @@ func getInitialConfig(data *schema.ResourceData) (*restclient.Config, error) { } func tryLoadingConfigFile(d *schema.ResourceData) (*restclient.Config, error) { - path, err := homedir.Expand(d.Get("config_path").(string)) - if err != nil { - return nil, err + loader := &clientcmd.ClientConfigLoadingRules{} + + configPaths := []string{} + if v, ok := d.Get("config_path").(string); ok && v != "" { + configPaths = []string{v} + } else if v, ok := d.Get("config_paths").([]interface{}); ok && len(v) > 0 { + for _, p := range v { + configPaths = append(configPaths, p.(string)) + } + } else if v := os.Getenv("KUBE_CONFIG_PATHS"); v != "" { + configPaths = filepath.SplitList(v) + } + + expandedPaths := []string{} + for _, p := range configPaths { + path, err := homedir.Expand(p) + if err != nil { + log.Printf("[DEBUG] Could not expand path: %s", err) + return nil, err + } + log.Printf("[DEBUG] Using kubeconfig: %s", path) + expandedPaths = append(expandedPaths, path) } - loader := &clientcmd.ClientConfigLoadingRules{ - ExplicitPath: path, + if len(expandedPaths) == 1 { + loader.ExplicitPath = expandedPaths[0] + } else { + loader.Precedence = expandedPaths } overrides := &clientcmd.ConfigOverrides{} @@ -367,13 +381,13 @@ func tryLoadingConfigFile(d *schema.ResourceData) (*restclient.Config, error) { cfg, err := cc.ClientConfig() if err != nil { if pathErr, ok := err.(*os.PathError); ok && os.IsNotExist(pathErr.Err) { - log.Printf("[INFO] Unable to load config file as it doesn't exist at %q", path) + log.Printf("[INFO] Unable to load config file as it doesn't exist at %q", pathErr.Path) return nil, nil } - return nil, fmt.Errorf("Failed to load config (%s%s): %s", path, ctxSuffix, err) + return nil, fmt.Errorf("Failed to initialize kubernetes configuration: %s", err) } - log.Printf("[INFO] Successfully loaded config file (%s%s)", path, ctxSuffix) + log.Printf("[INFO] Successfully initialized config") return cfg, nil } diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/kubernetes/client.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/kubernetes/client.go index 5bc57f6b..3490f91b 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/kubernetes/client.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/kubernetes/client.go @@ -17,6 +17,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/util/validation" "k8s.io/client-go/dynamic" + _ "k8s.io/client-go/plugin/pkg/client/auth" // Import to initialize client auth plugins. "k8s.io/utils/pointer" coordinationv1 "k8s.io/api/coordination/v1" diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/kubernetes/log.txt b/vendor/github.com/hashicorp/terraform/backend/remote-state/kubernetes/log.txt deleted file mode 100644 index 38cae58d..00000000 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/kubernetes/log.txt +++ /dev/null @@ -1,1995 +0,0 @@ -=== RUN TestBackendLocksSoak - TestBackendLocksSoak: backend_test.go:122: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:94: Error creating state manager: the state is already locked by another terraform client - Lock Info: - ID: 3e2169be-df88-a073-df20-d5d9863abd5d - Path: - Operation: init - Who: john@Johns-MacBook-Pro.local - Version: 0.13.0 - Created: 2020-06-05 16:54:18.347493 +0000 UTC - Info: - TestBackendLocksSoak: backend_test.go:94: Error creating state manager: the state is already locked by another terraform client - Lock Info: - ID: 3e2169be-df88-a073-df20-d5d9863abd5d - Path: - Operation: init - Who: john@Johns-MacBook-Pro.local - Version: 0.13.0 - Created: 2020-06-05 16:54:18.347493 +0000 UTC - Info: - TestBackendLocksSoak: backend_test.go:94: Error creating state manager: the state is already locked by another terraform client - Lock Info: - ID: 3e2169be-df88-a073-df20-d5d9863abd5d - Path: - Operation: init - Who: john@Johns-MacBook-Pro.local - Version: 0.13.0 - Created: 2020-06-05 16:54:18.347493 +0000 UTC - Info: - TestBackendLocksSoak: backend_test.go:94: Error creating state manager: the state is already locked by another terraform client - Lock Info: - ID: 3e2169be-df88-a073-df20-d5d9863abd5d - Path: - Operation: init - Who: john@Johns-MacBook-Pro.local - Version: 0.13.0 - Created: 2020-06-05 16:54:18.347493 +0000 UTC - Info: - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} - TestBackendLocksSoak: backend_test.go:88: TestBackendConfig on *kubernetes.Backend with configs.synthBody{Filename:"", Values:map[string]cty.Value{"secret_suffix":cty.StringVal("test-state")}} -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -2020/06/05 12:54:18 [INFO] Successfully loaded config file (/Users/john/.kube/config; default context) -panic: interface conversion: interface is nil, not statemgr.Locker - -goroutine 203 [running]: -github.com/hashicorp/terraform/backend/remote-state/kubernetes.TestBackendLocksSoak.func1(0xc000157200, 0xc00038cc00, 0xc00038cc10, 0x85) - /Users/john/dev/hashicorp/terraform/backend/remote-state/kubernetes/backend_test.go:103 +0x290 -created by github.com/hashicorp/terraform/backend/remote-state/kubernetes.TestBackendLocksSoak - /Users/john/dev/hashicorp/terraform/backend/remote-state/kubernetes/backend_test.go:87 +0xef -FAIL github.com/hashicorp/terraform/backend/remote-state/kubernetes 3.487s -FAIL diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/manta/backend.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/manta/backend.go index 9189ad89..c7e32403 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/manta/backend.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/manta/backend.go @@ -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" diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/oss/backend.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/oss/backend.go index 9b122763..f87c5f46 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/oss/backend.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/oss/backend.go @@ -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" diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/pg/backend.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/pg/backend.go index 0191176a..cc246973 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/pg/backend.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/pg/backend.go @@ -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" ) @@ -105,10 +105,14 @@ func (b *Backend) configure(ctx context.Context) error { } if !data.Get("skip_table_creation").(bool) { + if _, err := db.Exec("CREATE SEQUENCE IF NOT EXISTS public.global_states_id_seq AS bigint"); err != nil { + return err + } + query = `CREATE TABLE IF NOT EXISTS %s.%s ( - id SERIAL PRIMARY KEY, - name TEXT, - data TEXT + id bigint NOT NULL DEFAULT nextval('public.global_states_id_seq') PRIMARY KEY, + name text, + data text )` if _, err := db.Exec(fmt.Sprintf(query, b.schemaName, statesTableName)); err != nil { return err diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/pg/backend_test.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/pg/backend_test.go index 21a4272a..00f1fd96 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/pg/backend_test.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/pg/backend_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/states/remote" + "github.com/hashicorp/terraform/states/statemgr" "github.com/lib/pq" _ "github.com/lib/pq" ) @@ -266,6 +267,88 @@ func TestBackendStateLocks(t *testing.T) { backend.TestBackendStateLocks(t, b, bb) } +func TestBackendConcurrentLock(t *testing.T) { + testACC(t) + connStr := getDatabaseUrl() + dbCleaner, err := sql.Open("postgres", connStr) + if err != nil { + t.Fatal(err) + } + + getStateMgr := func(schemaName string) (statemgr.Full, *statemgr.LockInfo) { + defer dbCleaner.Query(fmt.Sprintf("DROP SCHEMA IF EXISTS %s CASCADE", schemaName)) + config := backend.TestWrapConfig(map[string]interface{}{ + "conn_str": connStr, + "schema_name": schemaName, + }) + b := backend.TestBackendConfig(t, New(), config).(*Backend) + + if b == nil { + t.Fatal("Backend could not be configured") + } + stateMgr, err := b.StateMgr(backend.DefaultStateName) + if err != nil { + t.Fatalf("Failed to get the state manager: %v", err) + } + + info := statemgr.NewLockInfo() + info.Operation = "test" + info.Who = schemaName + + return stateMgr, info + } + + s1, i1 := getStateMgr(fmt.Sprintf("terraform_%s_1", t.Name())) + s2, i2 := getStateMgr(fmt.Sprintf("terraform_%s_2", t.Name())) + + // First we need to create the workspace as the lock for creating them is + // global + lockID1, err := s1.Lock(i1) + if err != nil { + t.Fatalf("failed to lock first state: %v", err) + } + + if err = s1.PersistState(); err != nil { + t.Fatalf("failed to persist state: %v", err) + } + + if err := s1.Unlock(lockID1); err != nil { + t.Fatalf("failed to unlock first state: %v", err) + } + + lockID2, err := s2.Lock(i2) + if err != nil { + t.Fatalf("failed to lock second state: %v", err) + } + + if err = s2.PersistState(); err != nil { + t.Fatalf("failed to persist state: %v", err) + } + + if err := s2.Unlock(lockID2); err != nil { + t.Fatalf("failed to unlock first state: %v", err) + } + + // Now we can test concurrent lock + lockID1, err = s1.Lock(i1) + if err != nil { + t.Fatalf("failed to lock first state: %v", err) + } + + lockID2, err = s2.Lock(i2) + if err != nil { + t.Fatalf("failed to lock second state: %v", err) + } + + if err := s1.Unlock(lockID1); err != nil { + t.Fatalf("failed to unlock first state: %v", err) + } + + if err := s2.Unlock(lockID2); err != nil { + t.Fatalf("failed to unlock first state: %v", err) + } +} + func getDatabaseUrl() string { return os.Getenv("DATABASE_URL") } diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/s3/backend.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/s3/backend.go index b656e395..90570d8e 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/s3/backend.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/s3/backend.go @@ -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" ) @@ -327,7 +327,7 @@ func (b *Backend) configure(ctx context.Context) error { AssumeRoleExternalID: data.Get("external_id").(string), AssumeRolePolicy: data.Get("assume_role_policy").(string), AssumeRoleSessionName: data.Get("session_name").(string), - CallerDocumentationURL: "https://www.terraform.io/docs/backends/types/s3.html", + CallerDocumentationURL: "https://www.terraform.io/docs/language/settings/backends/s3.html", CallerName: "S3 Backend", CredsFilename: data.Get("shared_credentials_file").(string), DebugLogging: logging.IsDebugOrHigher(), diff --git a/vendor/github.com/hashicorp/terraform/backend/remote-state/swift/backend.go b/vendor/github.com/hashicorp/terraform/backend/remote-state/swift/backend.go index 3a22438f..ca7571c7 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote-state/swift/backend.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote-state/swift/backend.go @@ -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" ) diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/backend.go b/vendor/github.com/hashicorp/terraform/backend/remote/backend.go index 77ad2622..43eb77fb 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote/backend.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote/backend.go @@ -45,9 +45,6 @@ type Remote struct { CLI cli.Ui CLIColor *colorstring.Colorize - // ShowDiagnostics prints diagnostic messages to the UI. - ShowDiagnostics func(vals ...interface{}) - // ContextOpts are the base context options to set when initializing a // new Terraform context. Many of these will be overridden or merged by // Operation. See Operation for more details. @@ -85,6 +82,12 @@ type Remote struct { // opLock locks operations opLock sync.Mutex + + // ignoreVersionConflict, if true, will disable the requirement that the + // local Terraform version matches the remote workspace's configured + // version. This will also cause VerifyWorkspaceTerraformVersion to return + // a warning diagnostic instead of an error. + ignoreVersionConflict bool } var _ backend.Backend = (*Remote)(nil) @@ -629,6 +632,20 @@ func (b *Remote) StateMgr(name string) (statemgr.Full, error) { } } + // This is a fallback error check. Most code paths should use other + // mechanisms to check the version, then set the ignoreVersionConflict + // field to true. This check is only in place to ensure that we don't + // accidentally upgrade state with a new code path, and the version check + // logic is coarser and simpler. + if !b.ignoreVersionConflict { + wsv := workspace.TerraformVersion + // Explicitly ignore the pseudo-version "latest" here, as it will cause + // plan and apply to always fail. + if wsv != tfversion.String() && wsv != "latest" { + return nil, fmt.Errorf("Remote workspace Terraform version %q does not match local Terraform version %q", workspace.TerraformVersion, tfversion.String()) + } + } + client := &remoteClient{ client: b.client, organization: b.organization, @@ -674,8 +691,22 @@ func (b *Remote) Operation(ctx context.Context, op *backend.Operation) (*backend } } + // Terraform remote version conflicts are not a concern for operations. We + // are in one of three states: + // + // - Running remotely, in which case the local version is irrelevant; + // - Workspace configured for local operations, in which case the remote + // version is meaningless; + // - Forcing local operations with a remote backend, which should only + // happen in the Terraform Cloud worker, in which case the Terraform + // versions by definition match. + b.IgnoreVersionConflict() + // Check if we need to use the local backend to run the operation. if b.forceLocal || !w.Operations { + // Record that we're forced to run operations locally to allow the + // command package UI to operate correctly + b.forceLocal = true return b.local.Operation(ctx, op) } @@ -724,7 +755,9 @@ func (b *Remote) Operation(ctx context.Context, op *backend.Operation) (*backend r, opErr := f(stopCtx, cancelCtx, op, w) if opErr != nil && opErr != context.Canceled { - b.ReportResult(runningOp, opErr) + var diags tfdiags.Diagnostics + diags = diags.Append(opErr) + op.ReportResult(runningOp, diags) return } @@ -737,7 +770,9 @@ func (b *Remote) Operation(ctx context.Context, op *backend.Operation) (*backend // Retrieve the run to get its current status. r, err := b.client.Runs.Read(cancelCtx, r.ID) if err != nil { - b.ReportResult(runningOp, generalError("Failed to retrieve run", err)) + var diags tfdiags.Diagnostics + diags = diags.Append(generalError("Failed to retrieve run", err)) + op.ReportResult(runningOp, diags) return } @@ -746,7 +781,9 @@ func (b *Remote) Operation(ctx context.Context, op *backend.Operation) (*backend if opErr == context.Canceled { if err := b.cancel(cancelCtx, op, r); err != nil { - b.ReportResult(runningOp, generalError("Failed to retrieve run", err)) + var diags tfdiags.Diagnostics + diags = diags.Append(generalError("Failed to retrieve run", err)) + op.ReportResult(runningOp, diags) return } } @@ -800,41 +837,123 @@ func (b *Remote) cancel(cancelCtx context.Context, op *backend.Operation, r *tfe return nil } -// ReportResult is a helper for the common chore of setting the status of -// a running operation and showing any diagnostics produced during that -// operation. -// -// If the given diagnostics contains errors then the operation's result -// will be set to backend.OperationFailure. It will be set to -// backend.OperationSuccess otherwise. It will then use b.ShowDiagnostics -// to show the given diagnostics before returning. +// IgnoreVersionConflict allows commands to disable the fall-back check that +// the local Terraform version matches the remote workspace's configured +// Terraform version. This should be called by commands where this check is +// unnecessary, such as those performing remote operations, or read-only +// operations. It will also be called if the user uses a command-line flag to +// override this check. +func (b *Remote) IgnoreVersionConflict() { + b.ignoreVersionConflict = true +} + +// VerifyWorkspaceTerraformVersion compares the local Terraform version against +// the workspace's configured Terraform version. If they are equal, this means +// that there are no compatibility concerns, so it returns no diagnostics. // -// Callers should feel free to do each of these operations separately in -// more complex cases where e.g. diagnostics are interleaved with other -// output, but terminating immediately after reporting error diagnostics is -// common and can be expressed concisely via this method. -func (b *Remote) ReportResult(op *backend.RunningOperation, err error) { +// If the versions differ, +func (b *Remote) VerifyWorkspaceTerraformVersion(workspaceName string) tfdiags.Diagnostics { var diags tfdiags.Diagnostics - diags = diags.Append(err) - if diags.HasErrors() { - op.Result = backend.OperationFailure - } else { - op.Result = backend.OperationSuccess + workspace, err := b.getRemoteWorkspace(context.Background(), workspaceName) + if err != nil { + // If the workspace doesn't exist, there can be no compatibility + // problem, so we can return. This is most likely to happen when + // migrating state from a local backend to a new workspace. + if err == tfe.ErrResourceNotFound { + return nil + } + + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Error looking up workspace", + fmt.Sprintf("Workspace read failed: %s", err), + )) + return diags } - if b.ShowDiagnostics != nil { - b.ShowDiagnostics(diags) - } else { - // Shouldn't generally happen, but if it does then we'll at least - // make some noise in the logs to help us spot it. - if len(diags) != 0 { - log.Printf( - "[ERROR] Remote backend needs to report diagnostics but ShowDiagnostics is not set:\n%s", - diags.ErrWithWarnings(), - ) + // If the workspace has the pseudo-version "latest", all bets are off. We + // cannot reasonably determine what the intended Terraform version is, so + // we'll skip version verification. + if workspace.TerraformVersion == "latest" { + return nil + } + + // If the workspace has remote operations disabled, the remote Terraform + // version is effectively meaningless, so we'll skip version verification. + if workspace.Operations == false { + return nil + } + + remoteVersion, err := version.NewSemver(workspace.TerraformVersion) + if err != nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Error looking up workspace", + fmt.Sprintf("Invalid Terraform version: %s", err), + )) + return diags + } + + v014 := version.Must(version.NewSemver("0.14.0")) + if tfversion.SemVer.LessThan(v014) || remoteVersion.LessThan(v014) { + // Versions of Terraform prior to 0.14.0 will refuse to load state files + // written by a newer version of Terraform, even if it is only a patch + // level difference. As a result we require an exact match. + if tfversion.SemVer.Equal(remoteVersion) { + return diags } } + if tfversion.SemVer.GreaterThanOrEqual(v014) && remoteVersion.GreaterThanOrEqual(v014) { + // Versions of Terraform after 0.14.0 should be compatible with each + // other. At the time this code was written, the only constraints we + // are aware of are: + // + // - 0.14.0 is guaranteed to be compatible with versions up to but not + // including 1.1.0 + v110 := version.Must(version.NewSemver("1.1.0")) + if tfversion.SemVer.LessThan(v110) && remoteVersion.LessThan(v110) { + return diags + } + // - Any new Terraform state version will require at least minor patch + // increment, so x.y.* will always be compatible with each other + tfvs := tfversion.SemVer.Segments64() + rwvs := remoteVersion.Segments64() + if len(tfvs) == 3 && len(rwvs) == 3 && tfvs[0] == rwvs[0] && tfvs[1] == rwvs[1] { + return diags + } + } + + // Even if ignoring version conflicts, it may still be useful to call this + // method and warn the user about a mismatch between the local and remote + // Terraform versions. + severity := tfdiags.Error + if b.ignoreVersionConflict { + severity = tfdiags.Warning + } + + suggestion := " If you're sure you want to upgrade the state, you can force Terraform to continue using the -ignore-remote-version flag. This may result in an unusable workspace." + if b.ignoreVersionConflict { + suggestion = "" + } + diags = diags.Append(tfdiags.Sourceless( + severity, + "Terraform version mismatch", + fmt.Sprintf( + "The local Terraform version (%s) does not match the configured version for remote workspace %s/%s (%s).%s", + tfversion.String(), + b.organization, + workspace.Name, + workspace.TerraformVersion, + suggestion, + ), + )) + + return diags +} + +func (b *Remote) IsLocalOperations() bool { + return b.forceLocal } // Colorize returns the Colorize structure that can be used for colorizing diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/backend_apply.go b/vendor/github.com/hashicorp/terraform/backend/remote/backend_apply.go index 5db412ba..23e81625 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote/backend_apply.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote/backend_apply.go @@ -10,6 +10,7 @@ import ( tfe "github.com/hashicorp/go-tfe" version "github.com/hashicorp/go-version" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/tfdiags" ) @@ -84,7 +85,7 @@ func (b *Remote) opApply(stopCtx, cancelCtx context.Context, op *backend.Operati )) } - if !op.HasConfig() && !op.Destroy { + if !op.HasConfig() && op.PlanMode != plans.DestroyMode { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, "No configuration files found", @@ -152,10 +153,12 @@ func (b *Remote) opApply(stopCtx, cancelCtx context.Context, op *backend.Operati if r.Actions.IsDiscardable { err = b.client.Runs.Discard(stopCtx, r.ID, tfe.RunDiscardOptions{}) if err != nil { - if op.Destroy { + switch op.PlanMode { + case plans.DestroyMode: return r, generalError("Failed to discard destroy", err) + default: + return r, generalError("Failed to discard apply", err) } - return r, generalError("Failed to discard apply", err) } } diags = diags.Append(tfdiags.Sourceless( @@ -170,14 +173,13 @@ func (b *Remote) opApply(stopCtx, cancelCtx context.Context, op *backend.Operati return r, diags.Err() } - mustConfirm := (op.UIIn != nil && op.UIOut != nil) && - ((op.Destroy && (!op.DestroyForce && !op.AutoApprove)) || (!op.Destroy && !op.AutoApprove)) + mustConfirm := (op.UIIn != nil && op.UIOut != nil) && !op.AutoApprove if !w.AutoApply { if mustConfirm { opts := &terraform.InputOpts{Id: "approve"} - if op.Destroy { + if op.PlanMode == plans.DestroyMode { opts.Query = "\nDo you really want to destroy all resources in workspace \"" + op.Workspace + "\"?" opts.Description = "Terraform will destroy all your managed infrastructure, as shown above.\n" + "There is no undo. Only 'yes' will be accepted to confirm." diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/backend_apply_test.go b/vendor/github.com/hashicorp/terraform/backend/remote/backend_apply_test.go index 7fe5c1c0..5576fecd 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote/backend_apply_test.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote/backend_apply_test.go @@ -11,35 +11,56 @@ import ( "github.com/google/go-cmp/cmp" tfe "github.com/hashicorp/go-tfe" + version "github.com/hashicorp/go-version" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/internal/initwd" + "github.com/hashicorp/terraform/internal/terminal" + "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/plans/planfile" "github.com/hashicorp/terraform/states/statemgr" "github.com/hashicorp/terraform/terraform" + tfversion "github.com/hashicorp/terraform/version" "github.com/mitchellh/cli" ) -func testOperationApply(t *testing.T, configDir string) (*backend.Operation, func()) { +func testOperationApply(t *testing.T, configDir string) (*backend.Operation, func(), func(*testing.T) *terminal.TestOutput) { + t.Helper() + + return testOperationApplyWithTimeout(t, configDir, 0) +} + +func testOperationApplyWithTimeout(t *testing.T, configDir string, timeout time.Duration) (*backend.Operation, func(), func(*testing.T) *terminal.TestOutput) { t.Helper() _, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir) + streams, done := terminal.StreamsForTesting(t) + view := views.NewView(streams) + stateLockerView := views.NewStateLocker(arguments.ViewHuman, view) + operationView := views.NewOperation(arguments.ViewHuman, false, view) + return &backend.Operation{ ConfigDir: configDir, ConfigLoader: configLoader, Parallelism: defaultParallelism, PlanRefresh: true, + StateLocker: clistate.NewLocker(timeout, stateLockerView), Type: backend.OperationTypeApply, - }, configCleanup + View: operationView, + }, configCleanup, done } func TestRemote_applyBasic(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply") + op, configCleanup, done := testOperationApply(t, "./testdata/apply") defer configCleanup() + defer done(t) input := testInput(t, map[string]string{ "approve": "yes", @@ -88,8 +109,9 @@ func TestRemote_applyCanceled(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply") + op, configCleanup, done := testOperationApply(t, "./testdata/apply") defer configCleanup() + defer done(t) op.Workspace = backend.DefaultStateName @@ -129,7 +151,7 @@ func TestRemote_applyWithoutPermissions(t *testing.T) { } w.Permissions.CanQueueApply = false - op, configCleanup := testOperationApply(t, "./testdata/apply") + op, configCleanup, done := testOperationApply(t, "./testdata/apply") defer configCleanup() op.UIOut = b.CLI @@ -141,11 +163,12 @@ func TestRemote_applyWithoutPermissions(t *testing.T) { } <-run.Done() + output := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected apply operation to fail") } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := output.Stderr() if !strings.Contains(errOutput, "Insufficient rights to apply changes") { t.Fatalf("expected a permissions error, got: %v", errOutput) } @@ -168,7 +191,7 @@ func TestRemote_applyWithVCS(t *testing.T) { t.Fatalf("error creating named workspace: %v", err) } - op, configCleanup := testOperationApply(t, "./testdata/apply") + op, configCleanup, done := testOperationApply(t, "./testdata/apply") defer configCleanup() op.Workspace = "prod" @@ -179,6 +202,7 @@ func TestRemote_applyWithVCS(t *testing.T) { } <-run.Done() + output := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected apply operation to fail") } @@ -186,7 +210,7 @@ func TestRemote_applyWithVCS(t *testing.T) { t.Fatalf("expected plan to be empty") } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := output.Stderr() if !strings.Contains(errOutput, "not allowed for workspaces with a VCS") { t.Fatalf("expected a VCS error, got: %v", errOutput) } @@ -196,7 +220,7 @@ func TestRemote_applyWithParallelism(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply") + op, configCleanup, done := testOperationApply(t, "./testdata/apply") defer configCleanup() op.Parallelism = 3 @@ -208,11 +232,12 @@ func TestRemote_applyWithParallelism(t *testing.T) { } <-run.Done() + output := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected apply operation to fail") } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := output.Stderr() if !strings.Contains(errOutput, "parallelism values are currently not supported") { t.Fatalf("expected a parallelism error, got: %v", errOutput) } @@ -222,7 +247,7 @@ func TestRemote_applyWithPlan(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply") + op, configCleanup, done := testOperationApply(t, "./testdata/apply") defer configCleanup() op.PlanFile = &planfile.Reader{} @@ -234,6 +259,7 @@ func TestRemote_applyWithPlan(t *testing.T) { } <-run.Done() + output := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected apply operation to fail") } @@ -241,7 +267,7 @@ func TestRemote_applyWithPlan(t *testing.T) { t.Fatalf("expected plan to be empty") } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := output.Stderr() if !strings.Contains(errOutput, "saved plan is currently not supported") { t.Fatalf("expected a saved plan error, got: %v", errOutput) } @@ -251,7 +277,7 @@ func TestRemote_applyWithoutRefresh(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply") + op, configCleanup, done := testOperationApply(t, "./testdata/apply") defer configCleanup() op.PlanRefresh = false @@ -263,11 +289,12 @@ func TestRemote_applyWithoutRefresh(t *testing.T) { } <-run.Done() + output := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected apply operation to fail") } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := output.Stderr() if !strings.Contains(errOutput, "refresh is currently not supported") { t.Fatalf("expected a refresh error, got: %v", errOutput) } @@ -277,8 +304,9 @@ func TestRemote_applyWithTarget(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply") + op, configCleanup, done := testOperationApply(t, "./testdata/apply") defer configCleanup() + defer done(t) addr, _ := addrs.ParseAbsResourceStr("null_resource.foo") @@ -315,7 +343,7 @@ func TestRemote_applyWithTargetIncompatibleAPIVersion(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationApply(t, "./testdata/apply") defer configCleanup() // Set the tfe client's RemoteAPIVersion to an empty string, to mimic @@ -333,6 +361,7 @@ func TestRemote_applyWithTargetIncompatibleAPIVersion(t *testing.T) { } <-run.Done() + output := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected apply operation to fail") } @@ -340,7 +369,7 @@ func TestRemote_applyWithTargetIncompatibleAPIVersion(t *testing.T) { t.Fatalf("expected plan to be empty") } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := output.Stderr() if !strings.Contains(errOutput, "Resource targeting is not supported") { t.Fatalf("expected a targeting error, got: %v", errOutput) } @@ -350,7 +379,7 @@ func TestRemote_applyWithVariables(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply-variables") + op, configCleanup, done := testOperationApply(t, "./testdata/apply-variables") defer configCleanup() op.Variables = testVariables(terraform.ValueFromNamedFile, "foo", "bar") @@ -362,11 +391,12 @@ func TestRemote_applyWithVariables(t *testing.T) { } <-run.Done() + output := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected apply operation to fail") } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := output.Stderr() if !strings.Contains(errOutput, "variables are currently not supported") { t.Fatalf("expected a variables error, got: %v", errOutput) } @@ -376,7 +406,7 @@ func TestRemote_applyNoConfig(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/empty") + op, configCleanup, done := testOperationApply(t, "./testdata/empty") defer configCleanup() op.Workspace = backend.DefaultStateName @@ -387,6 +417,7 @@ func TestRemote_applyNoConfig(t *testing.T) { } <-run.Done() + output := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected apply operation to fail") } @@ -394,7 +425,7 @@ func TestRemote_applyNoConfig(t *testing.T) { t.Fatalf("expected plan to be empty") } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := output.Stderr() if !strings.Contains(errOutput, "configuration files found") { t.Fatalf("expected configuration files error, got: %v", errOutput) } @@ -410,8 +441,9 @@ func TestRemote_applyNoChanges(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply-no-changes") + op, configCleanup, done := testOperationApply(t, "./testdata/apply-no-changes") defer configCleanup() + defer done(t) op.Workspace = backend.DefaultStateName @@ -441,7 +473,7 @@ func TestRemote_applyNoApprove(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply") + op, configCleanup, done := testOperationApply(t, "./testdata/apply") defer configCleanup() input := testInput(t, map[string]string{ @@ -458,6 +490,7 @@ func TestRemote_applyNoApprove(t *testing.T) { } <-run.Done() + output := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected apply operation to fail") } @@ -469,7 +502,7 @@ func TestRemote_applyNoApprove(t *testing.T) { t.Fatalf("expected no unused answers, got: %v", input.answers) } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := output.Stderr() if !strings.Contains(errOutput, "Apply discarded") { t.Fatalf("expected an apply discarded error, got: %v", errOutput) } @@ -479,8 +512,9 @@ func TestRemote_applyAutoApprove(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply") + op, configCleanup, done := testOperationApply(t, "./testdata/apply") defer configCleanup() + defer done(t) input := testInput(t, map[string]string{ "approve": "no", @@ -524,8 +558,9 @@ func TestRemote_applyApprovedExternally(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply") + op, configCleanup, done := testOperationApply(t, "./testdata/apply") defer configCleanup() + defer done(t) input := testInput(t, map[string]string{ "approve": "wait-for-external-update", @@ -542,8 +577,8 @@ func TestRemote_applyApprovedExternally(t *testing.T) { t.Fatalf("error starting operation: %v", err) } - // Wait 2 seconds to make sure the run started. - time.Sleep(2 * time.Second) + // Wait 50 milliseconds to make sure the run started. + time.Sleep(50 * time.Millisecond) wl, err := b.client.Workspaces.List( ctx, @@ -599,8 +634,9 @@ func TestRemote_applyDiscardedExternally(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply") + op, configCleanup, done := testOperationApply(t, "./testdata/apply") defer configCleanup() + defer done(t) input := testInput(t, map[string]string{ "approve": "wait-for-external-update", @@ -617,8 +653,8 @@ func TestRemote_applyDiscardedExternally(t *testing.T) { t.Fatalf("error starting operation: %v", err) } - // Wait 2 seconds to make sure the run started. - time.Sleep(2 * time.Second) + // Wait 50 milliseconds to make sure the run started. + time.Sleep(50 * time.Millisecond) wl, err := b.client.Workspaces.List( ctx, @@ -687,8 +723,9 @@ func TestRemote_applyWithAutoApply(t *testing.T) { t.Fatalf("error creating named workspace: %v", err) } - op, configCleanup := testOperationApply(t, "./testdata/apply") + op, configCleanup, done := testOperationApply(t, "./testdata/apply") defer configCleanup() + defer done(t) input := testInput(t, map[string]string{ "approve": "yes", @@ -738,8 +775,9 @@ func TestRemote_applyForceLocal(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply") + op, configCleanup, done := testOperationApply(t, "./testdata/apply") defer configCleanup() + defer done(t) input := testInput(t, map[string]string{ "approve": "yes", @@ -749,6 +787,10 @@ func TestRemote_applyForceLocal(t *testing.T) { op.UIOut = b.CLI op.Workspace = backend.DefaultStateName + streams, done := terminal.StreamsForTesting(t) + view := views.NewOperation(arguments.ViewHuman, false, views.NewView(streams)) + op.View = view + run, err := b.Operation(context.Background(), op) if err != nil { t.Fatalf("error starting operation: %v", err) @@ -770,11 +812,11 @@ func TestRemote_applyForceLocal(t *testing.T) { if strings.Contains(output, "Running apply in the remote backend") { t.Fatalf("unexpected remote backend header in output: %s", output) } - if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { - t.Fatalf("expected plan summery in output: %s", output) + if output := done(t).Stdout(); !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { + t.Fatalf("expected plan summary in output: %s", output) } - if !strings.Contains(output, "1 added, 0 changed, 0 destroyed") { - t.Fatalf("expected apply summery in output: %s", output) + if !run.State.HasResources() { + t.Fatalf("expected resources in state") } } @@ -796,8 +838,9 @@ func TestRemote_applyWorkspaceWithoutOperations(t *testing.T) { t.Fatalf("error creating named workspace: %v", err) } - op, configCleanup := testOperationApply(t, "./testdata/apply") + op, configCleanup, done := testOperationApply(t, "./testdata/apply") defer configCleanup() + defer done(t) input := testInput(t, map[string]string{ "approve": "yes", @@ -807,6 +850,10 @@ func TestRemote_applyWorkspaceWithoutOperations(t *testing.T) { op.UIOut = b.CLI op.Workspace = "no-operations" + streams, done := terminal.StreamsForTesting(t) + view := views.NewOperation(arguments.ViewHuman, false, views.NewView(streams)) + op.View = view + run, err := b.Operation(ctx, op) if err != nil { t.Fatalf("error starting operation: %v", err) @@ -828,11 +875,11 @@ func TestRemote_applyWorkspaceWithoutOperations(t *testing.T) { if strings.Contains(output, "Running apply in the remote backend") { t.Fatalf("unexpected remote backend header in output: %s", output) } - if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { - t.Fatalf("expected plan summery in output: %s", output) + if output := done(t).Stdout(); !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { + t.Fatalf("expected plan summary in output: %s", output) } - if !strings.Contains(output, "1 added, 0 changed, 0 destroyed") { - t.Fatalf("expected apply summery in output: %s", output) + if !run.State.HasResources() { + t.Fatalf("expected resources in state") } } @@ -863,15 +910,15 @@ func TestRemote_applyLockTimeout(t *testing.T) { t.Fatalf("error creating pending run: %v", err) } - op, configCleanup := testOperationApply(t, "./testdata/apply") + op, configCleanup, done := testOperationApplyWithTimeout(t, "./testdata/apply", 50*time.Millisecond) defer configCleanup() + defer done(t) input := testInput(t, map[string]string{ "cancel": "yes", "approve": "yes", }) - op.StateLockTimeout = 5 * time.Second op.UIIn = input op.UIOut = b.CLI op.Workspace = backend.DefaultStateName @@ -887,8 +934,8 @@ func TestRemote_applyLockTimeout(t *testing.T) { case <-sigint: // Stop redirecting SIGINT signals. signal.Stop(sigint) - case <-time.After(10 * time.Second): - t.Fatalf("expected lock timeout after 5 seconds, waited 10 seconds") + case <-time.After(200 * time.Millisecond): + t.Fatalf("expected lock timeout after 50 milliseconds, waited 200 milliseconds") } if len(input.answers) != 2 { @@ -914,14 +961,15 @@ func TestRemote_applyDestroy(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply-destroy") + op, configCleanup, done := testOperationApply(t, "./testdata/apply-destroy") defer configCleanup() + defer done(t) input := testInput(t, map[string]string{ "approve": "yes", }) - op.Destroy = true + op.PlanMode = plans.DestroyMode op.UIIn = input op.UIOut = b.CLI op.Workspace = backend.DefaultStateName @@ -963,10 +1011,11 @@ func TestRemote_applyDestroyNoConfig(t *testing.T) { "approve": "yes", }) - op, configCleanup := testOperationApply(t, "./testdata/empty") + op, configCleanup, done := testOperationApply(t, "./testdata/empty") defer configCleanup() + defer done(t) - op.Destroy = true + op.PlanMode = plans.DestroyMode op.UIIn = input op.UIOut = b.CLI op.Workspace = backend.DefaultStateName @@ -993,8 +1042,9 @@ func TestRemote_applyPolicyPass(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply-policy-passed") + op, configCleanup, done := testOperationApply(t, "./testdata/apply-policy-passed") defer configCleanup() + defer done(t) input := testInput(t, map[string]string{ "approve": "yes", @@ -1040,7 +1090,7 @@ func TestRemote_applyPolicyHardFail(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply-policy-hard-failed") + op, configCleanup, done := testOperationApply(t, "./testdata/apply-policy-hard-failed") defer configCleanup() input := testInput(t, map[string]string{ @@ -1057,6 +1107,7 @@ func TestRemote_applyPolicyHardFail(t *testing.T) { } <-run.Done() + viewOutput := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected apply operation to fail") } @@ -1068,7 +1119,7 @@ func TestRemote_applyPolicyHardFail(t *testing.T) { t.Fatalf("expected an unused answers, got: %v", input.answers) } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := viewOutput.Stderr() if !strings.Contains(errOutput, "hard failed") { t.Fatalf("expected a policy check error, got: %v", errOutput) } @@ -1092,14 +1143,16 @@ func TestRemote_applyPolicySoftFail(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply-policy-soft-failed") + op, configCleanup, done := testOperationApply(t, "./testdata/apply-policy-soft-failed") defer configCleanup() + defer done(t) input := testInput(t, map[string]string{ "override": "override", "approve": "yes", }) + op.AutoApprove = false op.UIIn = input op.UIOut = b.CLI op.Workspace = backend.DefaultStateName @@ -1136,16 +1189,14 @@ func TestRemote_applyPolicySoftFail(t *testing.T) { } } -func TestRemote_applyPolicySoftFailAutoApprove(t *testing.T) { +func TestRemote_applyPolicySoftFailAutoApproveSuccess(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply-policy-soft-failed") + op, configCleanup, done := testOperationApply(t, "./testdata/apply-policy-soft-failed") defer configCleanup() - input := testInput(t, map[string]string{ - "override": "override", - }) + input := testInput(t, map[string]string{}) op.AutoApprove = true op.UIIn = input @@ -1158,34 +1209,34 @@ func TestRemote_applyPolicySoftFailAutoApprove(t *testing.T) { } <-run.Done() - if run.Result == backend.OperationSuccess { - t.Fatal("expected apply operation to fail") + viewOutput := done(t) + if run.Result != backend.OperationSuccess { + t.Fatal("expected apply operation to success due to auto-approve") } - if !run.PlanEmpty { - t.Fatalf("expected plan to be empty") + + if run.PlanEmpty { + t.Fatalf("expected plan to not be empty, plan opertion completed without error") } - if len(input.answers) != 1 { - t.Fatalf("expected an unused answers, got: %v", input.answers) + if len(input.answers) != 0 { + t.Fatalf("expected no answers, got: %v", input.answers) } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() - if !strings.Contains(errOutput, "soft failed") { - t.Fatalf("expected a policy check error, got: %v", errOutput) + errOutput := viewOutput.Stderr() + if strings.Contains(errOutput, "soft failed") { + t.Fatalf("expected no policy check errors, instead got: %v", errOutput) } output := b.CLI.(*cli.MockUi).OutputWriter.String() - if !strings.Contains(output, "Running apply in the remote backend") { - t.Fatalf("expected remote backend header in output: %s", output) - } - if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { - t.Fatalf("expected plan summery in output: %s", output) - } if !strings.Contains(output, "Sentinel Result: false") { - t.Fatalf("expected policy check result in output: %s", output) + t.Fatalf("expected policy check to be false, insead got: %s", output) } - if strings.Contains(output, "1 added, 0 changed, 0 destroyed") { - t.Fatalf("unexpected apply summery in output: %s", output) + if !strings.Contains(output, "Apply complete!") { + t.Fatalf("expected apply to be complete, instead got: %s", output) + } + + if !strings.Contains(output, "Resources: 1 added, 0 changed, 0 destroyed") { + t.Fatalf("expected resources, instead got: %s", output) } } @@ -1206,8 +1257,9 @@ func TestRemote_applyPolicySoftFailAutoApply(t *testing.T) { t.Fatalf("error creating named workspace: %v", err) } - op, configCleanup := testOperationApply(t, "./testdata/apply-policy-soft-failed") + op, configCleanup, done := testOperationApply(t, "./testdata/apply-policy-soft-failed") defer configCleanup() + defer done(t) input := testInput(t, map[string]string{ "override": "override", @@ -1254,8 +1306,9 @@ func TestRemote_applyWithRemoteError(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationApply(t, "./testdata/apply-with-error") + op, configCleanup, done := testOperationApply(t, "./testdata/apply-with-error") defer configCleanup() + defer done(t) op.Workspace = backend.DefaultStateName @@ -1277,3 +1330,146 @@ func TestRemote_applyWithRemoteError(t *testing.T) { t.Fatalf("expected apply error in output: %s", output) } } + +func TestRemote_applyVersionCheck(t *testing.T) { + testCases := map[string]struct { + localVersion string + remoteVersion string + forceLocal bool + hasOperations bool + wantErr string + }{ + "versions can be different for remote apply": { + localVersion: "0.14.0", + remoteVersion: "0.13.5", + hasOperations: true, + }, + "versions can be different for local apply": { + localVersion: "0.14.0", + remoteVersion: "0.13.5", + hasOperations: false, + }, + "force local with remote operations and different versions is acceptable": { + localVersion: "0.14.0", + remoteVersion: "0.14.0-acme-provider-bundle", + forceLocal: true, + hasOperations: true, + }, + "no error if versions are identical": { + localVersion: "0.14.0", + remoteVersion: "0.14.0", + forceLocal: true, + hasOperations: true, + }, + "no error if force local but workspace has remote operations disabled": { + localVersion: "0.14.0", + remoteVersion: "0.13.5", + forceLocal: true, + hasOperations: false, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + b, bCleanup := testBackendDefault(t) + defer bCleanup() + + // SETUP: Save original local version state and restore afterwards + p := tfversion.Prerelease + v := tfversion.Version + s := tfversion.SemVer + defer func() { + tfversion.Prerelease = p + tfversion.Version = v + tfversion.SemVer = s + }() + + // SETUP: Set local version for the test case + tfversion.Prerelease = "" + tfversion.Version = tc.localVersion + tfversion.SemVer = version.Must(version.NewSemver(tc.localVersion)) + + // SETUP: Set force local for the test case + b.forceLocal = tc.forceLocal + + ctx := context.Background() + + // SETUP: set the operations and Terraform Version fields on the + // remote workspace + _, err := b.client.Workspaces.Update( + ctx, + b.organization, + b.workspace, + tfe.WorkspaceUpdateOptions{ + Operations: tfe.Bool(tc.hasOperations), + TerraformVersion: tfe.String(tc.remoteVersion), + }, + ) + if err != nil { + t.Fatalf("error creating named workspace: %v", err) + } + + // RUN: prepare the apply operation and run it + op, configCleanup, done := testOperationApply(t, "./testdata/apply") + defer configCleanup() + + streams, done := terminal.StreamsForTesting(t) + view := views.NewOperation(arguments.ViewHuman, false, views.NewView(streams)) + op.View = view + + input := testInput(t, map[string]string{ + "approve": "yes", + }) + + op.UIIn = input + op.UIOut = b.CLI + op.Workspace = backend.DefaultStateName + + run, err := b.Operation(ctx, op) + if err != nil { + t.Fatalf("error starting operation: %v", err) + } + + // RUN: wait for completion + <-run.Done() + output := done(t) + + if tc.wantErr != "" { + // ASSERT: if the test case wants an error, check for failure + // and the error message + if run.Result != backend.OperationFailure { + t.Fatalf("expected run to fail, but result was %#v", run.Result) + } + errOutput := output.Stderr() + if !strings.Contains(errOutput, tc.wantErr) { + t.Fatalf("missing error %q\noutput: %s", tc.wantErr, errOutput) + } + } else { + // ASSERT: otherwise, check for success and appropriate output + // based on whether the run should be local or remote + if run.Result != backend.OperationSuccess { + t.Fatalf("operation failed: %s", b.CLI.(*cli.MockUi).ErrorWriter.String()) + } + output := b.CLI.(*cli.MockUi).OutputWriter.String() + hasRemote := strings.Contains(output, "Running apply in the remote backend") + hasSummary := strings.Contains(output, "1 added, 0 changed, 0 destroyed") + hasResources := run.State.HasResources() + if !tc.forceLocal && tc.hasOperations { + if !hasRemote { + t.Errorf("missing remote backend header in output: %s", output) + } + if !hasSummary { + t.Errorf("expected apply summary in output: %s", output) + } + } else { + if hasRemote { + t.Errorf("unexpected remote backend header in output: %s", output) + } + if !hasResources { + t.Errorf("expected resources in state") + } + } + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/backend_common.go b/vendor/github.com/hashicorp/terraform/backend/remote/backend_common.go index 7fe5373d..eb8ed2c8 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote/backend_common.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote/backend_common.go @@ -13,6 +13,7 @@ import ( tfe "github.com/hashicorp/go-tfe" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/terraform" ) @@ -24,6 +25,13 @@ var ( errRunOverridden = errors.New("overridden using the UI or API") ) +var ( + backoffMin = 1000.0 + backoffMax = 3000.0 + + runPollInterval = 3 * time.Second +) + // backoff will perform exponential backoff based on the iteration and // limited by the provided min and max (in milliseconds) durations. func backoff(min, max float64, iter int) time.Duration { @@ -43,7 +51,7 @@ func (b *Remote) waitForRun(stopCtx, cancelCtx context.Context, op *backend.Oper return r, stopCtx.Err() case <-cancelCtx.Done(): return r, cancelCtx.Err() - case <-time.After(backoff(1000, 3000, i)): + case <-time.After(backoff(backoffMin, backoffMax, i)): // Timer up, show status } @@ -243,15 +251,7 @@ func (b *Remote) costEstimate(stopCtx, cancelCtx context.Context, op *backend.Op return nil } - if b.CLI != nil { - b.CLI.Output("\n------------------------------------------------------------------------\n") - } - msgPrefix := "Cost estimation" - if b.CLI != nil { - b.CLI.Output(b.Colorize().Color(msgPrefix + ":\n")) - } - started := time.Now() updated := started for i := 0; ; i++ { @@ -260,7 +260,7 @@ func (b *Remote) costEstimate(stopCtx, cancelCtx context.Context, op *backend.Op return stopCtx.Err() case <-cancelCtx.Done(): return cancelCtx.Err() - case <-time.After(1 * time.Second): + case <-time.After(backoff(backoffMin, backoffMax, i)): } // Retrieve the cost estimate to get its current status. @@ -277,6 +277,12 @@ func (b *Remote) costEstimate(stopCtx, cancelCtx context.Context, op *backend.Op } } + // checking if i == 0 so as to avoid printing this starting horizontal-rule + // every retry, and that it only prints it on the first (i=0) attempt. + if b.CLI != nil && i == 0 { + b.CLI.Output("\n------------------------------------------------------------------------\n") + } + switch ce.Status { case tfe.CostEstimateFinished: delta, err := strconv.ParseFloat(ce.DeltaMonthlyCost, 64) @@ -292,6 +298,7 @@ func (b *Remote) costEstimate(stopCtx, cancelCtx context.Context, op *backend.Op deltaRepr := strings.Replace(ce.DeltaMonthlyCost, "-", "", 1) if b.CLI != nil { + b.CLI.Output(b.Colorize().Color(msgPrefix + ":\n")) b.CLI.Output(b.Colorize().Color(fmt.Sprintf("Resources: %d of %d estimated", ce.MatchedResourcesCount, ce.ResourcesCount))) b.CLI.Output(b.Colorize().Color(fmt.Sprintf(" $%s/mo %s$%s", ce.ProposedMonthlyCost, sign, deltaRepr))) @@ -313,16 +320,17 @@ func (b *Remote) costEstimate(stopCtx, cancelCtx context.Context, op *backend.Op elapsed = fmt.Sprintf( " (%s elapsed)", current.Sub(started).Truncate(30*time.Second)) } + b.CLI.Output(b.Colorize().Color(msgPrefix + ":\n")) b.CLI.Output(b.Colorize().Color("Waiting for cost estimate to complete..." + elapsed + "\n")) } continue case tfe.CostEstimateSkippedDueToTargeting: + b.CLI.Output(b.Colorize().Color(msgPrefix + ":\n")) b.CLI.Output("Not available for this plan, because it was created with the -target option.") b.CLI.Output("\n------------------------------------------------------------------------") return nil case tfe.CostEstimateErrored: - b.CLI.Output(msgPrefix + " errored:\n") - b.CLI.Output(ce.ErrorMessage) + b.CLI.Output(msgPrefix + " errored.\n") b.CLI.Output("\n------------------------------------------------------------------------") return nil case tfe.CostEstimateCanceled: @@ -407,33 +415,44 @@ func (b *Remote) checkPolicy(stopCtx, cancelCtx context.Context, op *backend.Ope case tfe.PolicyHardFailed: return fmt.Errorf(msgPrefix + " hard failed.") case tfe.PolicySoftFailed: + runUrl := fmt.Sprintf(runHeader, b.hostname, b.organization, op.Workspace, r.ID) + if op.Type == backend.OperationTypePlan || op.UIOut == nil || op.UIIn == nil || - op.AutoApprove || !pc.Actions.IsOverridable || !pc.Permissions.CanOverride { - return fmt.Errorf(msgPrefix + " soft failed.") + !pc.Actions.IsOverridable || !pc.Permissions.CanOverride { + return fmt.Errorf(msgPrefix + " soft failed.\n" + runUrl) } - default: - return fmt.Errorf("Unknown or unexpected policy state: %s", pc.Status) - } - - opts := &terraform.InputOpts{ - Id: "override", - Query: "\nDo you want to override the soft failed policy check?", - Description: "Only 'override' will be accepted to override.", - } - err = b.confirm(stopCtx, op, opts, r, "override") - if err != nil && err != errRunOverridden { - return err - } + if op.AutoApprove { + if _, err = b.client.PolicyChecks.Override(stopCtx, pc.ID); err != nil { + return generalError(fmt.Sprintf("Failed to override policy check.\n%s", runUrl), err) + } + } else { + opts := &terraform.InputOpts{ + Id: "override", + Query: "\nDo you want to override the soft failed policy check?", + Description: "Only 'override' will be accepted to override.", + } + err = b.confirm(stopCtx, op, opts, r, "override") + if err != nil && err != errRunOverridden { + return fmt.Errorf( + fmt.Sprintf("Failed to override: %s\n%s\n", err.Error(), runUrl), + ) + } - if err != errRunOverridden { - if _, err = b.client.PolicyChecks.Override(stopCtx, pc.ID); err != nil { - return generalError("Failed to override policy check", err) + if err != errRunOverridden { + if _, err = b.client.PolicyChecks.Override(stopCtx, pc.ID); err != nil { + return generalError(fmt.Sprintf("Failed to override policy check.\n%s", runUrl), err) + } + } else { + b.CLI.Output(fmt.Sprintf("The run needs to be manually overridden or discarded.\n%s\n", runUrl)) + } } - } - if b.CLI != nil { - b.CLI.Output("------------------------------------------------------------------------") + if b.CLI != nil { + b.CLI.Output("------------------------------------------------------------------------") + } + default: + return fmt.Errorf("Unknown or unexpected policy state: %s", pc.Status) } } @@ -455,7 +474,7 @@ func (b *Remote) confirm(stopCtx context.Context, op *backend.Operation, opts *t return case <-stopCtx.Done(): return - case <-time.After(3 * time.Second): + case <-time.After(runPollInterval): // Retrieve the run again to get its current status. r, err := b.client.Runs.Read(stopCtx, r.ID) if err != nil { @@ -489,10 +508,10 @@ func (b *Remote) confirm(stopCtx context.Context, op *backend.Operation, opts *t } if err == errRunDiscarded { - if op.Destroy { + err = errApplyDiscarded + if op.PlanMode == plans.DestroyMode { err = errDestroyDiscarded } - err = errApplyDiscarded } result <- err @@ -533,7 +552,7 @@ func (b *Remote) confirm(stopCtx context.Context, op *backend.Operation, opts *t if r.Actions.IsDiscardable { err = b.client.Runs.Discard(stopCtx, r.ID, tfe.RunDiscardOptions{}) if err != nil { - if op.Destroy { + if op.PlanMode == plans.DestroyMode { return generalError("Failed to discard destroy", err) } return generalError("Failed to discard apply", err) @@ -542,7 +561,7 @@ func (b *Remote) confirm(stopCtx context.Context, op *backend.Operation, opts *t // Even if the run was discarded successfully, we still // return an error as the apply command was canceled. - if op.Destroy { + if op.PlanMode == plans.DestroyMode { return errDestroyDiscarded } return errApplyDiscarded diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/backend_context.go b/vendor/github.com/hashicorp/terraform/backend/remote/backend_context.go index 13202f54..09bace58 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote/backend_context.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote/backend_context.go @@ -11,7 +11,6 @@ import ( "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" "github.com/hashicorp/terraform/backend" - "github.com/hashicorp/terraform/command/clistate" "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/states/statemgr" "github.com/hashicorp/terraform/terraform" @@ -23,11 +22,7 @@ import ( func (b *Remote) Context(op *backend.Operation) (*terraform.Context, statemgr.Full, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics - if op.LockState { - op.StateLocker = clistate.NewLocker(context.Background(), op.StateLockTimeout, b.CLI, b.cliColorize()) - } else { - op.StateLocker = clistate.NewNoopLocker() - } + op.StateLocker = op.StateLocker.WithContext(context.Background()) // Get the remote workspace name. remoteWorkspaceName := b.getRemoteWorkspaceName(op.Workspace) @@ -41,8 +36,7 @@ func (b *Remote) Context(op *backend.Operation) (*terraform.Context, statemgr.Fu } log.Printf("[TRACE] backend/remote: requesting state lock for workspace %q", remoteWorkspaceName) - if err := op.StateLocker.Lock(stateMgr, op.Type.String()); err != nil { - diags = diags.Append(errwrap.Wrapf("Error locking state: {{err}}", err)) + if diags := op.StateLocker.Lock(stateMgr, op.Type.String()); diags.HasErrors() { return nil, nil, diags } @@ -50,10 +44,7 @@ func (b *Remote) Context(op *backend.Operation) (*terraform.Context, statemgr.Fu // If we're returning with errors, and thus not producing a valid // context, we'll want to avoid leaving the remote workspace locked. if diags.HasErrors() { - err := op.StateLocker.Unlock(nil) - if err != nil { - diags = diags.Append(errwrap.Wrapf("Error unlocking state: {{err}}", err)) - } + diags = diags.Append(op.StateLocker.Unlock()) } }() @@ -70,7 +61,7 @@ func (b *Remote) Context(op *backend.Operation) (*terraform.Context, statemgr.Fu } // Copy set options from the operation - opts.Destroy = op.Destroy + opts.PlanMode = op.PlanMode opts.Targets = op.Targets opts.UIInput = op.UIIn @@ -156,11 +147,20 @@ func (b *Remote) getRemoteWorkspaceName(localWorkspaceName string) string { } } -func (b *Remote) getRemoteWorkspaceID(ctx context.Context, localWorkspaceName string) (string, error) { +func (b *Remote) getRemoteWorkspace(ctx context.Context, localWorkspaceName string) (*tfe.Workspace, error) { remoteWorkspaceName := b.getRemoteWorkspaceName(localWorkspaceName) - log.Printf("[TRACE] backend/remote: looking up workspace id for %s/%s", b.organization, remoteWorkspaceName) + log.Printf("[TRACE] backend/remote: looking up workspace for %s/%s", b.organization, remoteWorkspaceName) remoteWorkspace, err := b.client.Workspaces.Read(ctx, b.organization, remoteWorkspaceName) + if err != nil { + return nil, err + } + + return remoteWorkspace, nil +} + +func (b *Remote) getRemoteWorkspaceID(ctx context.Context, localWorkspaceName string) (string, error) { + remoteWorkspace, err := b.getRemoteWorkspace(ctx, localWorkspaceName) if err != nil { return "", err } diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/backend_context_test.go b/vendor/github.com/hashicorp/terraform/backend/remote/backend_context_test.go index 48a6a852..3fa61f14 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote/backend_context_test.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote/backend_context_test.go @@ -6,8 +6,12 @@ import ( tfe "github.com/hashicorp/go-tfe" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/internal/initwd" + "github.com/hashicorp/terraform/internal/terminal" "github.com/hashicorp/terraform/states/statemgr" "github.com/zclconf/go-cty/cty" ) @@ -183,11 +187,14 @@ func TestRemoteContextWithVars(t *testing.T) { t.Fatal(err) } + streams, _ := terminal.StreamsForTesting(t) + view := views.NewStateLocker(arguments.ViewHuman, views.NewView(streams)) + op := &backend.Operation{ ConfigDir: configDir, ConfigLoader: configLoader, + StateLocker: clistate.NewLocker(0, view), Workspace: backend.DefaultStateName, - LockState: true, } v := test.Opts @@ -195,7 +202,7 @@ func TestRemoteContextWithVars(t *testing.T) { key := "key" v.Key = &key } - b.client.Variables.Create(nil, workspaceID, *v) + b.client.Variables.Create(context.TODO(), workspaceID, *v) _, _, diags := b.Context(op) diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/backend_mock.go b/vendor/github.com/hashicorp/terraform/backend/remote/backend_mock.go index eaa81cbf..7f70a0e5 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote/backend_mock.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote/backend_mock.go @@ -17,6 +17,7 @@ import ( tfe "github.com/hashicorp/go-tfe" "github.com/hashicorp/terraform/terraform" + tfversion "github.com/hashicorp/terraform/version" "github.com/mitchellh/copystructure" ) @@ -360,7 +361,7 @@ func (m *mockLogReader) Read(l []byte) (int, error) { if written, err := m.read(l); err != io.ErrNoProgress { return written, err } - time.Sleep(500 * time.Millisecond) + time.Sleep(1 * time.Millisecond) } } @@ -446,16 +447,18 @@ func (m *mockOrganizations) RunQueue(ctx context.Context, name string, options t } type mockPlans struct { - client *mockClient - logs map[string]string - plans map[string]*tfe.Plan + client *mockClient + logs map[string]string + planOutputs map[string]string + plans map[string]*tfe.Plan } func newMockPlans(client *mockClient) *mockPlans { return &mockPlans{ - client: client, - logs: make(map[string]string), - plans: make(map[string]*tfe.Plan), + client: client, + logs: make(map[string]string), + planOutputs: make(map[string]string), + plans: make(map[string]*tfe.Plan), } } @@ -535,6 +538,15 @@ func (m *mockPlans) Logs(ctx context.Context, planID string) (io.Reader, error) }, nil } +func (m *mockPlans) JSONOutput(ctx context.Context, planID string) ([]byte, error) { + planOutput, ok := m.planOutputs[planID] + if !ok { + return nil, tfe.ErrResourceNotFound + } + + return []byte(planOutput), nil +} + type mockPolicyChecks struct { client *mockClient checks map[string]*tfe.PolicyCheck @@ -813,6 +825,10 @@ func (m *mockRuns) Create(ctx context.Context, options tfe.RunCreateOptions) (*t } func (m *mockRuns) Read(ctx context.Context, runID string) (*tfe.Run, error) { + return m.ReadWithOptions(ctx, runID, nil) +} + +func (m *mockRuns) ReadWithOptions(ctx context.Context, runID string, _ *tfe.RunReadOptions) (*tfe.Run, error) { m.Lock() defer m.Unlock() @@ -959,6 +975,10 @@ func (m *mockStateVersions) Create(ctx context.Context, workspaceID string, opti } func (m *mockStateVersions) Read(ctx context.Context, svID string) (*tfe.StateVersion, error) { + return m.ReadWithOptions(ctx, svID, nil) +} + +func (m *mockStateVersions) ReadWithOptions(ctx context.Context, svID string, options *tfe.StateVersionReadOptions) (*tfe.StateVersion, error) { sv, ok := m.stateVersions[svID] if !ok { return nil, tfe.ErrResourceNotFound @@ -967,6 +987,10 @@ func (m *mockStateVersions) Read(ctx context.Context, svID string) (*tfe.StateVe } func (m *mockStateVersions) Current(ctx context.Context, workspaceID string) (*tfe.StateVersion, error) { + return m.CurrentWithOptions(ctx, workspaceID, nil) +} + +func (m *mockStateVersions) CurrentWithOptions(ctx context.Context, workspaceID string, options *tfe.StateVersionCurrentOptions) (*tfe.StateVersion, error) { w, ok := m.client.Workspaces.workspaceIDs[workspaceID] if !ok { return nil, tfe.ErrResourceNotFound @@ -1124,10 +1148,15 @@ func (m *mockWorkspaces) List(ctx context.Context, organization string, options } func (m *mockWorkspaces) Create(ctx context.Context, organization string, options tfe.WorkspaceCreateOptions) (*tfe.Workspace, error) { + if strings.HasSuffix(*options.Name, "no-operations") { + options.Operations = tfe.Bool(false) + } else if options.Operations == nil { + options.Operations = tfe.Bool(true) + } w := &tfe.Workspace{ ID: generateID("ws-"), Name: *options.Name, - Operations: !strings.HasSuffix(*options.Name, "no-operations"), + Operations: *options.Operations, Permissions: &tfe.WorkspacePermissions{ CanQueueApply: true, CanQueueRun: true, @@ -1139,6 +1168,11 @@ func (m *mockWorkspaces) Create(ctx context.Context, organization string, option if options.VCSRepo != nil { w.VCSRepo = &tfe.VCSRepo{} } + if options.TerraformVersion != nil { + w.TerraformVersion = *options.TerraformVersion + } else { + w.TerraformVersion = tfversion.String() + } m.workspaceIDs[w.ID] = w m.workspaceNames[w.Name] = w return w, nil @@ -1171,6 +1205,9 @@ func (m *mockWorkspaces) Update(ctx context.Context, organization, workspace str return nil, tfe.ErrResourceNotFound } + if options.Operations != nil { + w.Operations = *options.Operations + } if options.Name != nil { w.Name = *options.Name } @@ -1287,6 +1324,26 @@ func (m *mockWorkspaces) UnassignSSHKey(ctx context.Context, workspaceID string) panic("not implemented") } +func (m *mockWorkspaces) RemoteStateConsumers(ctx context.Context, workspaceID string) (*tfe.WorkspaceList, error) { + panic("not implemented") +} + +func (m *mockWorkspaces) AddRemoteStateConsumers(ctx context.Context, workspaceID string, options tfe.WorkspaceAddRemoteStateConsumersOptions) error { + panic("not implemented") +} + +func (m *mockWorkspaces) RemoveRemoteStateConsumers(ctx context.Context, workspaceID string, options tfe.WorkspaceRemoveRemoteStateConsumersOptions) error { + panic("not implemented") +} + +func (m *mockWorkspaces) UpdateRemoteStateConsumers(ctx context.Context, workspaceID string, options tfe.WorkspaceUpdateRemoteStateConsumersOptions) error { + panic("not implemented") +} + +func (m *mockWorkspaces) Readme(ctx context.Context, workspaceID string) (io.Reader, error) { + panic("not implemented") +} + const alphanumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" func generateID(s string) string { diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/backend_plan.go b/vendor/github.com/hashicorp/terraform/backend/remote/backend_plan.go index f9fcf82b..401e7415 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote/backend_plan.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote/backend_plan.go @@ -17,9 +17,12 @@ import ( tfe "github.com/hashicorp/go-tfe" version "github.com/hashicorp/go-version" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/tfdiags" ) +var planConfigurationVersionsPollInterval = 500 * time.Millisecond + func (b *Remote) opPlan(stopCtx, cancelCtx context.Context, op *backend.Operation, w *tfe.Workspace) (*tfe.Run, error) { log.Printf("[INFO] backend/remote: starting Plan operation") @@ -87,7 +90,7 @@ func (b *Remote) opPlan(stopCtx, cancelCtx context.Context, op *backend.Operatio )) } - if !op.HasConfig() && !op.Destroy { + if !op.HasConfig() && op.PlanMode != plans.DestroyMode { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, "No configuration files found", @@ -213,7 +216,7 @@ in order to capture the filesystem context the remote workspace expects: return nil, context.Canceled case <-cancelCtx.Done(): return nil, context.Canceled - case <-time.After(500 * time.Millisecond): + case <-time.After(planConfigurationVersionsPollInterval): cv, err = b.client.ConfigurationVersions.Read(stopCtx, cv.ID) if err != nil { return nil, generalError("Failed to retrieve configuration version", err) @@ -236,12 +239,26 @@ in order to capture the filesystem context the remote workspace expects: } runOptions := tfe.RunCreateOptions{ - IsDestroy: tfe.Bool(op.Destroy), Message: tfe.String(queueMessage), ConfigurationVersion: cv, Workspace: w, } + switch op.PlanMode { + case plans.NormalMode: + // okay, but we don't need to do anything special for this + case plans.DestroyMode: + runOptions.IsDestroy = tfe.Bool(true) + default: + // Shouldn't get here because we should update this for each new + // plan mode we add, mapping it to the corresponding RunCreateOptions + // field. + return nil, generalError( + "Invalid plan mode", + fmt.Errorf("remote backend doesn't support %s", op.PlanMode), + ) + } + if len(op.Targets) != 0 { runOptions.TargetAddrs = make([]string, 0, len(op.Targets)) for _, addr := range op.Targets { @@ -258,15 +275,16 @@ in order to capture the filesystem context the remote workspace expects: return r, generalError("Failed to create run", err) } - // When the lock timeout is set, - if op.StateLockTimeout > 0 { + // When the lock timeout is set, if the run is still pending and + // cancellable after that period, we attempt to cancel it. + if lockTimeout := op.StateLocker.Timeout(); lockTimeout > 0 { go func() { select { case <-stopCtx.Done(): return case <-cancelCtx.Done(): return - case <-time.After(op.StateLockTimeout): + case <-time.After(lockTimeout): // Retrieve the run to get its current status. r, err := b.client.Runs.Read(cancelCtx, r.ID) if err != nil { diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/backend_plan_test.go b/vendor/github.com/hashicorp/terraform/backend/remote/backend_plan_test.go index a2c6e4ad..16e482e3 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote/backend_plan_test.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote/backend_plan_test.go @@ -13,33 +13,52 @@ import ( tfe "github.com/hashicorp/go-tfe" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/internal/initwd" + "github.com/hashicorp/terraform/internal/terminal" + "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/plans/planfile" "github.com/hashicorp/terraform/states/statemgr" "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/cli" ) -func testOperationPlan(t *testing.T, configDir string) (*backend.Operation, func()) { +func testOperationPlan(t *testing.T, configDir string) (*backend.Operation, func(), func(*testing.T) *terminal.TestOutput) { + t.Helper() + + return testOperationPlanWithTimeout(t, configDir, 0) +} + +func testOperationPlanWithTimeout(t *testing.T, configDir string, timeout time.Duration) (*backend.Operation, func(), func(*testing.T) *terminal.TestOutput) { t.Helper() _, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir) + streams, done := terminal.StreamsForTesting(t) + view := views.NewView(streams) + stateLockerView := views.NewStateLocker(arguments.ViewHuman, view) + operationView := views.NewOperation(arguments.ViewHuman, false, view) + return &backend.Operation{ ConfigDir: configDir, ConfigLoader: configLoader, Parallelism: defaultParallelism, PlanRefresh: true, + StateLocker: clistate.NewLocker(timeout, stateLockerView), Type: backend.OperationTypePlan, - }, configCleanup + View: operationView, + }, configCleanup, done } func TestRemote_planBasic(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() + defer done(t) op.Workspace = backend.DefaultStateName @@ -75,8 +94,9 @@ func TestRemote_planCanceled(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() + defer done(t) op.Workspace = backend.DefaultStateName @@ -104,8 +124,9 @@ func TestRemote_planLongLine(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan-long-line") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan-long-line") defer configCleanup() + defer done(t) op.Workspace = backend.DefaultStateName @@ -148,7 +169,7 @@ func TestRemote_planWithoutPermissions(t *testing.T) { } w.Permissions.CanQueueRun = false - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() op.Workspace = "prod" @@ -159,11 +180,12 @@ func TestRemote_planWithoutPermissions(t *testing.T) { } <-run.Done() + output := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected plan operation to fail") } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := output.Stderr() if !strings.Contains(errOutput, "Insufficient rights to generate a plan") { t.Fatalf("expected a permissions error, got: %v", errOutput) } @@ -173,7 +195,7 @@ func TestRemote_planWithParallelism(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() op.Parallelism = 3 @@ -185,11 +207,12 @@ func TestRemote_planWithParallelism(t *testing.T) { } <-run.Done() + output := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected plan operation to fail") } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := output.Stderr() if !strings.Contains(errOutput, "parallelism values are currently not supported") { t.Fatalf("expected a parallelism error, got: %v", errOutput) } @@ -199,7 +222,7 @@ func TestRemote_planWithPlan(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() op.PlanFile = &planfile.Reader{} @@ -211,6 +234,7 @@ func TestRemote_planWithPlan(t *testing.T) { } <-run.Done() + output := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected plan operation to fail") } @@ -218,7 +242,7 @@ func TestRemote_planWithPlan(t *testing.T) { t.Fatalf("expected plan to be empty") } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := output.Stderr() if !strings.Contains(errOutput, "saved plan is currently not supported") { t.Fatalf("expected a saved plan error, got: %v", errOutput) } @@ -228,7 +252,7 @@ func TestRemote_planWithPath(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() op.PlanOutPath = "./testdata/plan" @@ -240,6 +264,7 @@ func TestRemote_planWithPath(t *testing.T) { } <-run.Done() + output := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected plan operation to fail") } @@ -247,7 +272,7 @@ func TestRemote_planWithPath(t *testing.T) { t.Fatalf("expected plan to be empty") } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := output.Stderr() if !strings.Contains(errOutput, "generated plan is currently not supported") { t.Fatalf("expected a generated plan error, got: %v", errOutput) } @@ -257,7 +282,7 @@ func TestRemote_planWithoutRefresh(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() op.PlanRefresh = false @@ -269,11 +294,12 @@ func TestRemote_planWithoutRefresh(t *testing.T) { } <-run.Done() + output := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected plan operation to fail") } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := output.Stderr() if !strings.Contains(errOutput, "refresh is currently not supported") { t.Fatalf("expected a refresh error, got: %v", errOutput) } @@ -306,8 +332,9 @@ func TestRemote_planWithTarget(t *testing.T) { } } - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() + defer done(t) addr, _ := addrs.ParseAbsResourceStr("null_resource.foo") @@ -355,7 +382,7 @@ func TestRemote_planWithTargetIncompatibleAPIVersion(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() // Set the tfe client's RemoteAPIVersion to an empty string, to mimic @@ -373,6 +400,7 @@ func TestRemote_planWithTargetIncompatibleAPIVersion(t *testing.T) { } <-run.Done() + output := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected plan operation to fail") } @@ -380,7 +408,7 @@ func TestRemote_planWithTargetIncompatibleAPIVersion(t *testing.T) { t.Fatalf("expected plan to be empty") } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := output.Stderr() if !strings.Contains(errOutput, "Resource targeting is not supported") { t.Fatalf("expected a targeting error, got: %v", errOutput) } @@ -390,7 +418,7 @@ func TestRemote_planWithVariables(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan-variables") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan-variables") defer configCleanup() op.Variables = testVariables(terraform.ValueFromCLIArg, "foo", "bar") @@ -402,11 +430,12 @@ func TestRemote_planWithVariables(t *testing.T) { } <-run.Done() + output := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected plan operation to fail") } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := output.Stderr() if !strings.Contains(errOutput, "variables are currently not supported") { t.Fatalf("expected a variables error, got: %v", errOutput) } @@ -416,7 +445,7 @@ func TestRemote_planNoConfig(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/empty") + op, configCleanup, done := testOperationPlan(t, "./testdata/empty") defer configCleanup() op.Workspace = backend.DefaultStateName @@ -427,6 +456,7 @@ func TestRemote_planNoConfig(t *testing.T) { } <-run.Done() + output := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected plan operation to fail") } @@ -434,7 +464,7 @@ func TestRemote_planNoConfig(t *testing.T) { t.Fatalf("expected plan to be empty") } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := output.Stderr() if !strings.Contains(errOutput, "configuration files found") { t.Fatalf("expected configuration files error, got: %v", errOutput) } @@ -444,8 +474,9 @@ func TestRemote_planNoChanges(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan-no-changes") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan-no-changes") defer configCleanup() + defer done(t) op.Workspace = backend.DefaultStateName @@ -482,11 +513,16 @@ func TestRemote_planForceLocal(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() + defer done(t) op.Workspace = backend.DefaultStateName + streams, done := terminal.StreamsForTesting(t) + view := views.NewOperation(arguments.ViewHuman, false, views.NewView(streams)) + op.View = view + run, err := b.Operation(context.Background(), op) if err != nil { t.Fatalf("error starting operation: %v", err) @@ -504,7 +540,7 @@ func TestRemote_planForceLocal(t *testing.T) { if strings.Contains(output, "Running plan in the remote backend") { t.Fatalf("unexpected remote backend header in output: %s", output) } - if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { + if output := done(t).Stdout(); !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { t.Fatalf("expected plan summary in output: %s", output) } } @@ -513,11 +549,16 @@ func TestRemote_planWithoutOperationsEntitlement(t *testing.T) { b, bCleanup := testBackendNoOperations(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() + defer done(t) op.Workspace = backend.DefaultStateName + streams, done := terminal.StreamsForTesting(t) + view := views.NewOperation(arguments.ViewHuman, false, views.NewView(streams)) + op.View = view + run, err := b.Operation(context.Background(), op) if err != nil { t.Fatalf("error starting operation: %v", err) @@ -535,7 +576,7 @@ func TestRemote_planWithoutOperationsEntitlement(t *testing.T) { if strings.Contains(output, "Running plan in the remote backend") { t.Fatalf("unexpected remote backend header in output: %s", output) } - if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { + if output := done(t).Stdout(); !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { t.Fatalf("expected plan summary in output: %s", output) } } @@ -558,11 +599,16 @@ func TestRemote_planWorkspaceWithoutOperations(t *testing.T) { t.Fatalf("error creating named workspace: %v", err) } - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() + defer done(t) op.Workspace = "no-operations" + streams, done := terminal.StreamsForTesting(t) + view := views.NewOperation(arguments.ViewHuman, false, views.NewView(streams)) + op.View = view + run, err := b.Operation(ctx, op) if err != nil { t.Fatalf("error starting operation: %v", err) @@ -580,7 +626,7 @@ func TestRemote_planWorkspaceWithoutOperations(t *testing.T) { if strings.Contains(output, "Running plan in the remote backend") { t.Fatalf("unexpected remote backend header in output: %s", output) } - if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { + if output := done(t).Stdout(); !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") { t.Fatalf("expected plan summary in output: %s", output) } } @@ -612,15 +658,15 @@ func TestRemote_planLockTimeout(t *testing.T) { t.Fatalf("error creating pending run: %v", err) } - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlanWithTimeout(t, "./testdata/plan", 50) defer configCleanup() + defer done(t) input := testInput(t, map[string]string{ "cancel": "yes", "approve": "yes", }) - op.StateLockTimeout = 5 * time.Second op.UIIn = input op.UIOut = b.CLI op.Workspace = backend.DefaultStateName @@ -636,8 +682,8 @@ func TestRemote_planLockTimeout(t *testing.T) { case <-sigint: // Stop redirecting SIGINT signals. signal.Stop(sigint) - case <-time.After(10 * time.Second): - t.Fatalf("expected lock timeout after 5 seconds, waited 10 seconds") + case <-time.After(200 * time.Millisecond): + t.Fatalf("expected lock timeout after 50 milliseconds, waited 200 milliseconds") } if len(input.answers) != 2 { @@ -660,10 +706,11 @@ func TestRemote_planDestroy(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() + defer done(t) - op.Destroy = true + op.PlanMode = plans.DestroyMode op.Workspace = backend.DefaultStateName run, err := b.Operation(context.Background(), op) @@ -684,10 +731,11 @@ func TestRemote_planDestroyNoConfig(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/empty") + op, configCleanup, done := testOperationPlan(t, "./testdata/empty") defer configCleanup() + defer done(t) - op.Destroy = true + op.PlanMode = plans.DestroyMode op.Workspace = backend.DefaultStateName run, err := b.Operation(context.Background(), op) @@ -718,8 +766,9 @@ func TestRemote_planWithWorkingDirectory(t *testing.T) { t.Fatalf("error configuring working directory: %v", err) } - op, configCleanup := testOperationPlan(t, "./testdata/plan-with-working-directory/terraform") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan-with-working-directory/terraform") defer configCleanup() + defer done(t) op.Workspace = backend.DefaultStateName @@ -776,8 +825,9 @@ func TestRemote_planWithWorkingDirectoryFromCurrentPath(t *testing.T) { // For this test we need to give our current directory instead of the // full path to the configuration as we already changed directories. - op, configCleanup := testOperationPlan(t, ".") + op, configCleanup, done := testOperationPlan(t, ".") defer configCleanup() + defer done(t) op.Workspace = backend.DefaultStateName @@ -807,8 +857,9 @@ func TestRemote_planCostEstimation(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan-cost-estimation") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan-cost-estimation") defer configCleanup() + defer done(t) op.Workspace = backend.DefaultStateName @@ -841,8 +892,9 @@ func TestRemote_planPolicyPass(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan-policy-passed") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan-policy-passed") defer configCleanup() + defer done(t) op.Workspace = backend.DefaultStateName @@ -875,7 +927,7 @@ func TestRemote_planPolicyHardFail(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan-policy-hard-failed") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan-policy-hard-failed") defer configCleanup() op.Workspace = backend.DefaultStateName @@ -886,6 +938,7 @@ func TestRemote_planPolicyHardFail(t *testing.T) { } <-run.Done() + viewOutput := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected plan operation to fail") } @@ -893,7 +946,7 @@ func TestRemote_planPolicyHardFail(t *testing.T) { t.Fatalf("expected plan to be empty") } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := viewOutput.Stderr() if !strings.Contains(errOutput, "hard failed") { t.Fatalf("expected a policy check error, got: %v", errOutput) } @@ -914,7 +967,7 @@ func TestRemote_planPolicySoftFail(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan-policy-soft-failed") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan-policy-soft-failed") defer configCleanup() op.Workspace = backend.DefaultStateName @@ -925,6 +978,7 @@ func TestRemote_planPolicySoftFail(t *testing.T) { } <-run.Done() + viewOutput := done(t) if run.Result == backend.OperationSuccess { t.Fatal("expected plan operation to fail") } @@ -932,7 +986,7 @@ func TestRemote_planPolicySoftFail(t *testing.T) { t.Fatalf("expected plan to be empty") } - errOutput := b.CLI.(*cli.MockUi).ErrorWriter.String() + errOutput := viewOutput.Stderr() if !strings.Contains(errOutput, "soft failed") { t.Fatalf("expected a policy check error, got: %v", errOutput) } @@ -953,8 +1007,9 @@ func TestRemote_planWithRemoteError(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan-with-error") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan-with-error") defer configCleanup() + defer done(t) op.Workspace = backend.DefaultStateName @@ -984,8 +1039,9 @@ func TestRemote_planOtherError(t *testing.T) { b, bCleanup := testBackendDefault(t) defer bCleanup() - op, configCleanup := testOperationPlan(t, "./testdata/plan") + op, configCleanup, done := testOperationPlan(t, "./testdata/plan") defer configCleanup() + defer done(t) op.Workspace = "network-error" // custom error response in backend_mock.go diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/backend_state.go b/vendor/github.com/hashicorp/terraform/backend/remote/backend_state.go index 5bbba65e..22b9563e 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote/backend_state.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote/backend_state.go @@ -117,6 +117,7 @@ func (r *remoteClient) Lock(info *statemgr.LockInfo) (string, error) { }) if err != nil { if err == tfe.ErrWorkspaceLocked { + lockErr.Info = info err = fmt.Errorf("%s (lock ID: \"%s/%s\")", err, r.organization, r.workspace.Name) } lockErr.Err = err diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/backend_test.go b/vendor/github.com/hashicorp/terraform/backend/remote/backend_test.go index 8c1e9a80..051d5641 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote/backend_test.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote/backend_test.go @@ -1,13 +1,18 @@ package remote import ( + "context" + "fmt" "reflect" "strings" "testing" + tfe "github.com/hashicorp/go-tfe" + version "github.com/hashicorp/go-version" "github.com/hashicorp/terraform-svchost/disco" "github.com/hashicorp/terraform/backend" - "github.com/hashicorp/terraform/version" + "github.com/hashicorp/terraform/tfdiags" + tfversion "github.com/hashicorp/terraform/version" "github.com/zclconf/go-cty/cty" backendLocal "github.com/hashicorp/terraform/backend/local" @@ -196,11 +201,11 @@ func TestRemote_versionConstraints(t *testing.T) { } // Save and restore the actual version. - p := version.Prerelease - v := version.Version + p := tfversion.Prerelease + v := tfversion.Version defer func() { - version.Prerelease = p - version.Version = v + tfversion.Prerelease = p + tfversion.Version = v }() for name, tc := range cases { @@ -208,8 +213,8 @@ func TestRemote_versionConstraints(t *testing.T) { b := New(testDisco(s)) // Set the version for this test. - version.Prerelease = tc.prerelease - version.Version = tc.version + tfversion.Prerelease = tc.prerelease + tfversion.Version = tc.version // Validate _, valDiags := b.PrepareConfig(tc.config) @@ -428,17 +433,17 @@ func TestRemote_checkConstraints(t *testing.T) { } // Save and restore the actual version. - p := version.Prerelease - v := version.Version + p := tfversion.Prerelease + v := tfversion.Version defer func() { - version.Prerelease = p - version.Version = v + tfversion.Prerelease = p + tfversion.Version = v }() for name, tc := range cases { // Set the version for this test. - version.Prerelease = tc.prerelease - version.Version = tc.version + tfversion.Prerelease = tc.prerelease + tfversion.Version = tc.version // Check the constraints. diags := b.checkConstraints(tc.constraints) @@ -448,3 +453,271 @@ func TestRemote_checkConstraints(t *testing.T) { } } } + +func TestRemote_StateMgr_versionCheck(t *testing.T) { + b, bCleanup := testBackendDefault(t) + defer bCleanup() + + // Some fixed versions for testing with. This logic is a simple string + // comparison, so we don't need many test cases. + v0135 := version.Must(version.NewSemver("0.13.5")) + v0140 := version.Must(version.NewSemver("0.14.0")) + + // Save original local version state and restore afterwards + p := tfversion.Prerelease + v := tfversion.Version + s := tfversion.SemVer + defer func() { + tfversion.Prerelease = p + tfversion.Version = v + tfversion.SemVer = s + }() + + // For this test, the local Terraform version is set to 0.14.0 + tfversion.Prerelease = "" + tfversion.Version = v0140.String() + tfversion.SemVer = v0140 + + // Update the mock remote workspace Terraform version to match the local + // Terraform version + if _, err := b.client.Workspaces.Update( + context.Background(), + b.organization, + b.workspace, + tfe.WorkspaceUpdateOptions{ + TerraformVersion: tfe.String(v0140.String()), + }, + ); err != nil { + t.Fatalf("error: %v", err) + } + + // This should succeed + if _, err := b.StateMgr(backend.DefaultStateName); err != nil { + t.Fatalf("expected no error, got %v", err) + } + + // Now change the remote workspace to a different Terraform version + if _, err := b.client.Workspaces.Update( + context.Background(), + b.organization, + b.workspace, + tfe.WorkspaceUpdateOptions{ + TerraformVersion: tfe.String(v0135.String()), + }, + ); err != nil { + t.Fatalf("error: %v", err) + } + + // This should fail + want := `Remote workspace Terraform version "0.13.5" does not match local Terraform version "0.14.0"` + if _, err := b.StateMgr(backend.DefaultStateName); err.Error() != want { + t.Fatalf("wrong error\n got: %v\nwant: %v", err.Error(), want) + } +} + +func TestRemote_StateMgr_versionCheckLatest(t *testing.T) { + b, bCleanup := testBackendDefault(t) + defer bCleanup() + + v0140 := version.Must(version.NewSemver("0.14.0")) + + // Save original local version state and restore afterwards + p := tfversion.Prerelease + v := tfversion.Version + s := tfversion.SemVer + defer func() { + tfversion.Prerelease = p + tfversion.Version = v + tfversion.SemVer = s + }() + + // For this test, the local Terraform version is set to 0.14.0 + tfversion.Prerelease = "" + tfversion.Version = v0140.String() + tfversion.SemVer = v0140 + + // Update the remote workspace to the pseudo-version "latest" + if _, err := b.client.Workspaces.Update( + context.Background(), + b.organization, + b.workspace, + tfe.WorkspaceUpdateOptions{ + TerraformVersion: tfe.String("latest"), + }, + ); err != nil { + t.Fatalf("error: %v", err) + } + + // This should succeed despite not being a string match + if _, err := b.StateMgr(backend.DefaultStateName); err != nil { + t.Fatalf("expected no error, got %v", err) + } +} + +func TestRemote_VerifyWorkspaceTerraformVersion(t *testing.T) { + testCases := []struct { + local string + remote string + operations bool + wantErr bool + }{ + {"0.13.5", "0.13.5", true, false}, + {"0.14.0", "0.13.5", true, true}, + {"0.14.0", "0.13.5", false, false}, + {"0.14.0", "0.14.1", true, false}, + {"0.14.0", "1.0.99", true, false}, + {"0.14.0", "1.1.0", true, true}, + {"1.2.0", "1.2.99", true, false}, + {"1.2.0", "1.3.0", true, true}, + {"0.15.0", "latest", true, false}, + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("local %s, remote %s", tc.local, tc.remote), func(t *testing.T) { + b, bCleanup := testBackendDefault(t) + defer bCleanup() + + local := version.Must(version.NewSemver(tc.local)) + + // Save original local version state and restore afterwards + p := tfversion.Prerelease + v := tfversion.Version + s := tfversion.SemVer + defer func() { + tfversion.Prerelease = p + tfversion.Version = v + tfversion.SemVer = s + }() + + // Override local version as specified + tfversion.Prerelease = "" + tfversion.Version = local.String() + tfversion.SemVer = local + + // Update the mock remote workspace Terraform version to the + // specified remote version + if _, err := b.client.Workspaces.Update( + context.Background(), + b.organization, + b.workspace, + tfe.WorkspaceUpdateOptions{ + Operations: tfe.Bool(tc.operations), + TerraformVersion: tfe.String(tc.remote), + }, + ); err != nil { + t.Fatalf("error: %v", err) + } + + diags := b.VerifyWorkspaceTerraformVersion(backend.DefaultStateName) + if tc.wantErr { + if len(diags) != 1 { + t.Fatal("expected diag, but none returned") + } + if got := diags.Err().Error(); !strings.Contains(got, "Terraform version mismatch") { + t.Fatalf("unexpected error: %s", got) + } + } else { + if len(diags) != 0 { + t.Fatalf("unexpected diags: %s", diags.Err()) + } + } + }) + } +} + +func TestRemote_VerifyWorkspaceTerraformVersion_workspaceErrors(t *testing.T) { + b, bCleanup := testBackendDefault(t) + defer bCleanup() + + // Attempting to check the version against a workspace which doesn't exist + // should result in no errors + diags := b.VerifyWorkspaceTerraformVersion("invalid-workspace") + if len(diags) != 0 { + t.Fatalf("unexpected error: %s", diags.Err()) + } + + // Use a special workspace ID to trigger a 500 error, which should result + // in a failed check + diags = b.VerifyWorkspaceTerraformVersion("network-error") + if len(diags) != 1 { + t.Fatal("expected diag, but none returned") + } + if got := diags.Err().Error(); !strings.Contains(got, "Error looking up workspace: Workspace read failed") { + t.Fatalf("unexpected error: %s", got) + } + + // Update the mock remote workspace Terraform version to an invalid version + if _, err := b.client.Workspaces.Update( + context.Background(), + b.organization, + b.workspace, + tfe.WorkspaceUpdateOptions{ + TerraformVersion: tfe.String("1.0.cheetarah"), + }, + ); err != nil { + t.Fatalf("error: %v", err) + } + diags = b.VerifyWorkspaceTerraformVersion(backend.DefaultStateName) + + if len(diags) != 1 { + t.Fatal("expected diag, but none returned") + } + if got := diags.Err().Error(); !strings.Contains(got, "Error looking up workspace: Invalid Terraform version") { + t.Fatalf("unexpected error: %s", got) + } +} + +func TestRemote_VerifyWorkspaceTerraformVersion_ignoreFlagSet(t *testing.T) { + b, bCleanup := testBackendDefault(t) + defer bCleanup() + + // If the ignore flag is set, the behaviour changes + b.IgnoreVersionConflict() + + // Different local & remote versions to cause an error + local := version.Must(version.NewSemver("0.14.0")) + remote := version.Must(version.NewSemver("0.13.5")) + + // Save original local version state and restore afterwards + p := tfversion.Prerelease + v := tfversion.Version + s := tfversion.SemVer + defer func() { + tfversion.Prerelease = p + tfversion.Version = v + tfversion.SemVer = s + }() + + // Override local version as specified + tfversion.Prerelease = "" + tfversion.Version = local.String() + tfversion.SemVer = local + + // Update the mock remote workspace Terraform version to the + // specified remote version + if _, err := b.client.Workspaces.Update( + context.Background(), + b.organization, + b.workspace, + tfe.WorkspaceUpdateOptions{ + TerraformVersion: tfe.String(remote.String()), + }, + ); err != nil { + t.Fatalf("error: %v", err) + } + + diags := b.VerifyWorkspaceTerraformVersion(backend.DefaultStateName) + if len(diags) != 1 { + t.Fatal("expected diag, but none returned") + } + + if got, want := diags[0].Severity(), tfdiags.Warning; got != want { + t.Errorf("wrong severity: got %#v, want %#v", got, want) + } + if got, want := diags[0].Description().Summary, "Terraform version mismatch"; got != want { + t.Errorf("wrong summary: got %s, want %s", got, want) + } + wantDetail := "The local Terraform version (0.14.0) does not match the configured version for remote workspace hashicorp/prod (0.13.5)." + if got := diags[0].Description().Detail; got != wantDetail { + t.Errorf("wrong summary: got %s, want %s", got, wantDetail) + } +} diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/cli.go b/vendor/github.com/hashicorp/terraform/backend/remote/cli.go index 5a6afa7e..9a4f24d0 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote/cli.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote/cli.go @@ -14,7 +14,6 @@ func (b *Remote) CLIInit(opts *backend.CLIOpts) error { b.CLI = opts.CLI b.CLIColor = opts.CLIColor - b.ShowDiagnostics = opts.ShowDiagnostics b.ContextOpts = opts.ContextOpts return nil diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/remote_test.go b/vendor/github.com/hashicorp/terraform/backend/remote/remote_test.go index dbd0a72d..f4cc3c5c 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote/remote_test.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote/remote_test.go @@ -4,6 +4,7 @@ import ( "flag" "os" "testing" + "time" _ "github.com/hashicorp/terraform/internal/logging" ) @@ -14,5 +15,11 @@ func TestMain(m *testing.M) { // Make sure TF_FORCE_LOCAL_BACKEND is unset os.Unsetenv("TF_FORCE_LOCAL_BACKEND") + // Reduce delays to make tests run faster + backoffMin = 1.0 + backoffMax = 1.0 + planConfigurationVersionsPollInterval = 1 * time.Millisecond + runPollInterval = 1 * time.Millisecond + os.Exit(m.Run()) } diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-destroy/apply.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-destroy/apply.log new file mode 100644 index 00000000..d126547d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-destroy/apply.log @@ -0,0 +1,7 @@ +Terraform v0.11.10 + +Initializing plugins and modules... +null_resource.hello: Destroying... (ID: 8657651096157629581) +null_resource.hello: Destruction complete after 0s + +Apply complete! Resources: 0 added, 0 changed, 1 destroyed. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-destroy/plan.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-destroy/plan.log new file mode 100644 index 00000000..1d38d416 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-destroy/plan.log @@ -0,0 +1,22 @@ +Terraform v0.11.7 + +Configuring remote state backend... +Initializing Terraform configuration... +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + +null_resource.hello: Refreshing state... (ID: 8657651096157629581) + +------------------------------------------------------------------------ + +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + - destroy + +Terraform will perform the following actions: + + - null_resource.hello + + +Plan: 0 to add, 0 to change, 1 to destroy. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-no-changes/plan.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-no-changes/plan.log new file mode 100644 index 00000000..70416815 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-no-changes/plan.log @@ -0,0 +1,17 @@ +Terraform v0.11.7 + +Configuring remote state backend... +Initializing Terraform configuration... +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + +null_resource.hello: Refreshing state... (ID: 8657651096157629581) + +------------------------------------------------------------------------ + +No changes. Infrastructure is up-to-date. + +This means that Terraform did not detect any differences between your +configuration and real physical resources that exist. As a result, no +actions need to be performed. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-no-changes/policy.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-no-changes/policy.log new file mode 100644 index 00000000..b0cb1e59 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-no-changes/policy.log @@ -0,0 +1,12 @@ +Sentinel Result: true + +This result means that Sentinel policies returned true and the protected +behavior is allowed by Sentinel policies. + +1 policies evaluated. + +## Policy 1: Passthrough.sentinel (soft-mandatory) + +Result: true + +TRUE - Passthrough.sentinel:1:1 - Rule "main" diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-hard-failed/plan.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-hard-failed/plan.log new file mode 100644 index 00000000..5849e575 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-hard-failed/plan.log @@ -0,0 +1,21 @@ +Terraform v0.11.7 + +Configuring remote state backend... +Initializing Terraform configuration... +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + +------------------------------------------------------------------------ + +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + + null_resource.foo + id: + + +Plan: 1 to add, 0 to change, 0 to destroy. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-hard-failed/policy.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-hard-failed/policy.log new file mode 100644 index 00000000..5d6e6935 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-hard-failed/policy.log @@ -0,0 +1,12 @@ +Sentinel Result: false + +Sentinel evaluated to false because one or more Sentinel policies evaluated +to false. This false was not due to an undefined value or runtime error. + +1 policies evaluated. + +## Policy 1: Passthrough.sentinel (hard-mandatory) + +Result: false + +FALSE - Passthrough.sentinel:1:1 - Rule "main" diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-passed/apply.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-passed/apply.log new file mode 100644 index 00000000..90199483 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-passed/apply.log @@ -0,0 +1,7 @@ +Terraform v0.11.10 + +Initializing plugins and modules... +null_resource.hello: Creating... +null_resource.hello: Creation complete after 0s (ID: 8657651096157629581) + +Apply complete! Resources: 1 added, 0 changed, 0 destroyed. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-passed/plan.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-passed/plan.log new file mode 100644 index 00000000..5849e575 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-passed/plan.log @@ -0,0 +1,21 @@ +Terraform v0.11.7 + +Configuring remote state backend... +Initializing Terraform configuration... +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + +------------------------------------------------------------------------ + +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + + null_resource.foo + id: + + +Plan: 1 to add, 0 to change, 0 to destroy. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-passed/policy.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-passed/policy.log new file mode 100644 index 00000000..b0cb1e59 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-passed/policy.log @@ -0,0 +1,12 @@ +Sentinel Result: true + +This result means that Sentinel policies returned true and the protected +behavior is allowed by Sentinel policies. + +1 policies evaluated. + +## Policy 1: Passthrough.sentinel (soft-mandatory) + +Result: true + +TRUE - Passthrough.sentinel:1:1 - Rule "main" diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-soft-failed/apply.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-soft-failed/apply.log new file mode 100644 index 00000000..90199483 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-soft-failed/apply.log @@ -0,0 +1,7 @@ +Terraform v0.11.10 + +Initializing plugins and modules... +null_resource.hello: Creating... +null_resource.hello: Creation complete after 0s (ID: 8657651096157629581) + +Apply complete! Resources: 1 added, 0 changed, 0 destroyed. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-soft-failed/plan.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-soft-failed/plan.log new file mode 100644 index 00000000..5849e575 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-soft-failed/plan.log @@ -0,0 +1,21 @@ +Terraform v0.11.7 + +Configuring remote state backend... +Initializing Terraform configuration... +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + +------------------------------------------------------------------------ + +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + + null_resource.foo + id: + + +Plan: 1 to add, 0 to change, 0 to destroy. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-soft-failed/policy.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-soft-failed/policy.log new file mode 100644 index 00000000..3e4ebedf --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-policy-soft-failed/policy.log @@ -0,0 +1,12 @@ +Sentinel Result: false + +Sentinel evaluated to false because one or more Sentinel policies evaluated +to false. This false was not due to an undefined value or runtime error. + +1 policies evaluated. + +## Policy 1: Passthrough.sentinel (soft-mandatory) + +Result: false + +FALSE - Passthrough.sentinel:1:1 - Rule "main" diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-variables/apply.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-variables/apply.log new file mode 100644 index 00000000..90199483 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-variables/apply.log @@ -0,0 +1,7 @@ +Terraform v0.11.10 + +Initializing plugins and modules... +null_resource.hello: Creating... +null_resource.hello: Creation complete after 0s (ID: 8657651096157629581) + +Apply complete! Resources: 1 added, 0 changed, 0 destroyed. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-variables/plan.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-variables/plan.log new file mode 100644 index 00000000..5849e575 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-variables/plan.log @@ -0,0 +1,21 @@ +Terraform v0.11.7 + +Configuring remote state backend... +Initializing Terraform configuration... +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + +------------------------------------------------------------------------ + +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + + null_resource.foo + id: + + +Plan: 1 to add, 0 to change, 0 to destroy. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-with-error/plan.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-with-error/plan.log new file mode 100644 index 00000000..4344a372 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply-with-error/plan.log @@ -0,0 +1,10 @@ +Terraform v0.11.7 + +Configuring remote state backend... +Initializing Terraform configuration... + +Error: null_resource.foo: 1 error(s) occurred: + +* null_resource.foo: 1:3: unknown function called: guid in: + +${guid()} diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply/apply.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply/apply.log new file mode 100644 index 00000000..90199483 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply/apply.log @@ -0,0 +1,7 @@ +Terraform v0.11.10 + +Initializing plugins and modules... +null_resource.hello: Creating... +null_resource.hello: Creation complete after 0s (ID: 8657651096157629581) + +Apply complete! Resources: 1 added, 0 changed, 0 destroyed. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply/plan.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply/plan.log new file mode 100644 index 00000000..5849e575 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/apply/plan.log @@ -0,0 +1,21 @@ +Terraform v0.11.7 + +Configuring remote state backend... +Initializing Terraform configuration... +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + +------------------------------------------------------------------------ + +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + + null_resource.foo + id: + + +Plan: 1 to add, 0 to change, 0 to destroy. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-cost-estimation/ce.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-cost-estimation/ce.log new file mode 100644 index 00000000..e51fef1e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-cost-estimation/ce.log @@ -0,0 +1,6 @@ ++---------+------+-----+-------------+----------------------+ +| PRODUCT | NAME | SKU | DESCRIPTION | DELTA | ++---------+------+-----+-------------+----------------------+ ++---------+------+-----+-------------+----------------------+ +| TOTAL | $0.000 USD / 720 HRS | ++---------+------+-----+-------------+----------------------+ diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-cost-estimation/cost-estimate.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-cost-estimation/cost-estimate.log new file mode 100644 index 00000000..67a50928 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-cost-estimation/cost-estimate.log @@ -0,0 +1,5 @@ +Cost estimation: + +Waiting for cost estimation to complete... +Resources: 1 of 1 estimated + $25.488/mo +$25.488 \ No newline at end of file diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-cost-estimation/plan.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-cost-estimation/plan.log new file mode 100644 index 00000000..fae287f4 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-cost-estimation/plan.log @@ -0,0 +1,20 @@ +Terraform v0.12.9 +Configuring remote state backend... +Initializing Terraform configuration... +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + +------------------------------------------------------------------------ + +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + + null_resource.foo + id: + + +Plan: 1 to add, 0 to change, 0 to destroy. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-long-line/plan.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-long-line/plan.log new file mode 100644 index 00000000..f34ed170 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-long-line/plan.log @@ -0,0 +1,23 @@ +Terraform v0.11.7 + +Configuring remote state backend... +Initializing Terraform configuration... +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + +------------------------------------------------------------------------ + +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + + null_resource.foo + id: + triggers.%: "1" + triggers.long_line: "[{'_id':'5c5ab0ed7de45e993ffb9eeb','index':0,'guid':'e734d772-6b5a-4cb0-805c-91cd5e560e20','isActive':false,'balance':'$1,472.03','picture':'http://placehold.it/32x32','age':30,'eyeColor':'blue','name':{'first':'Darlene','last':'Garza'},'company':'GEEKOSIS','email':'darlene.garza@geekosis.io','phone':'+1 (850) 506-3347','address':'165 Kiely Place, Como, New Mexico, 4335','about':'Officia ullamco et sunt magna voluptate culpa cupidatat ea tempor laboris cupidatat ea anim laboris. Minim enim quis enim esse laborum est veniam. Lorem excepteur elit Lorem cupidatat elit ea anim irure fugiat fugiat sunt mollit. Consectetur ad nulla dolor amet esse occaecat aliquip sit. Magna sit elit adipisicing ut reprehenderit anim exercitation sit quis ea pariatur Lorem magna dolore.','registered':'Wednesday, March 11, 2015 12:58 PM','latitude':'20.729127','longitude':'-127.343593','tags':['minim','in','deserunt','occaecat','fugiat'],'greeting':'Hello, Darlene! You have 8 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0eda9117d15f1c1f112','index':1,'guid':'f0d1eed2-c6a9-4535-8800-d4bd53fe7eee','isActive':true,'balance':'$2,901.90','picture':'http://placehold.it/32x32','age':28,'eyeColor':'brown','name':{'first':'Flora','last':'Short'},'company':'SIGNITY','email':'flora.short@signity.me','phone':'+1 (840) 520-2666','address':'636 Johnson Avenue, Gerber, Wisconsin, 9139','about':'Veniam dolore deserunt Lorem aliqua qui eiusmod. Amet tempor fugiat duis incididunt amet adipisicing. Id ea nisi veniam eiusmod.','registered':'Wednesday, May 2, 2018 5:59 AM','latitude':'-63.267612','longitude':'4.224102','tags':['veniam','incididunt','id','aliqua','reprehenderit'],'greeting':'Hello, Flora! You have 10 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed83fd574d8041fa16','index':2,'guid':'29499a07-414a-436f-ba62-6634ca16bdcc','isActive':true,'balance':'$2,781.28','picture':'http://placehold.it/32x32','age':22,'eyeColor':'green','name':{'first':'Trevino','last':'Marks'},'company':'KEGULAR','email':'trevino.marks@kegular.com','phone':'+1 (843) 571-2269','address':'200 Alabama Avenue, Grenelefe, Florida, 7963','about':'Occaecat nisi exercitation Lorem mollit laborum magna adipisicing culpa dolor proident dolore. Non consequat ea amet et id mollit incididunt minim anim amet nostrud labore tempor. Proident eu sint commodo nisi consequat voluptate do fugiat proident. Laboris eiusmod veniam non et elit nulla nisi labore incididunt Lorem consequat consectetur voluptate.','registered':'Saturday, January 25, 2014 5:56 AM','latitude':'65.044005','longitude':'-127.454864','tags':['anim','duis','velit','pariatur','enim'],'greeting':'Hello, Trevino! You have 10 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed784eb6e350ff0a07','index':3,'guid':'40ed47e2-1747-4665-ab59-cdb3630a7642','isActive':true,'balance':'$2,000.78','picture':'http://placehold.it/32x32','age':25,'eyeColor':'brown','name':{'first':'Solis','last':'Mckinney'},'company':'QABOOS','email':'solis.mckinney@qaboos.org','phone':'+1 (924) 405-2560','address':'712 Herkimer Court, Klondike, Ohio, 8133','about':'Minim ad anim minim tempor mollit magna tempor et non commodo amet. Nisi cupidatat labore culpa consectetur exercitation laborum adipisicing fugiat officia adipisicing consequat non. Qui voluptate tempor laboris exercitation qui non adipisicing occaecat voluptate sunt do nostrud velit. Consequat tempor officia laboris tempor irure cupidatat aliquip voluptate nostrud velit ex nulla tempor laboris. Qui pariatur pariatur enim aliquip velit. Officia mollit ullamco laboris velit velit eiusmod enim amet incididunt consectetur sunt.','registered':'Wednesday, April 12, 2017 6:59 AM','latitude':'-25.055596','longitude':'-140.126525','tags':['ipsum','adipisicing','amet','nulla','dolore'],'greeting':'Hello, Solis! You have 5 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0ed02ce1ea9a2155d51','index':4,'guid':'1b5fb7d3-3b9a-4382-81b5-9ab01a27e74b','isActive':true,'balance':'$1,373.67','picture':'http://placehold.it/32x32','age':28,'eyeColor':'green','name':{'first':'Janell','last':'Battle'},'company':'GEEKMOSIS','email':'janell.battle@geekmosis.net','phone':'+1 (810) 591-3014','address':'517 Onderdonk Avenue, Shrewsbury, District Of Columbia, 2335','about':'Reprehenderit ad proident do anim qui officia magna magna duis cillum esse minim est. Excepteur ipsum anim ad laboris. In occaecat dolore nulla ea Lorem tempor et culpa in sint. Officia eu eu incididunt sit amet. Culpa duis id reprehenderit ut anim sit sunt. Duis dolore proident velit incididunt adipisicing pariatur fugiat incididunt eiusmod eu veniam irure.','registered':'Thursday, February 8, 2018 1:44 AM','latitude':'-33.254864','longitude':'-154.145885','tags':['aute','deserunt','ipsum','eiusmod','laborum'],'greeting':'Hello, Janell! You have 5 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0edab58604bd7d3dd1c','index':5,'guid':'6354c035-af22-44c9-8be9-b2ea9decc24d','isActive':true,'balance':'$3,535.68','picture':'http://placehold.it/32x32','age':30,'eyeColor':'green','name':{'first':'Combs','last':'Kirby'},'company':'LUXURIA','email':'combs.kirby@luxuria.name','phone':'+1 (900) 498-3266','address':'377 Kingsland Avenue, Ruckersville, Maine, 9916','about':'Lorem duis ipsum pariatur aliquip sunt. Commodo esse laborum incididunt mollit quis est laboris ea ea quis fugiat. Enim elit ullamco velit et fugiat veniam irure deserunt aliqua ad irure veniam.','registered':'Tuesday, February 21, 2017 4:04 PM','latitude':'-70.20591','longitude':'162.546871','tags':['reprehenderit','est','enim','aute','ad'],'greeting':'Hello, Combs! You have 10 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0edf7fafeffc6357c51','index':6,'guid':'02523e0b-cc90-4309-b6b2-f493dc6076f6','isActive':false,'balance':'$3,754.30','picture':'http://placehold.it/32x32','age':29,'eyeColor':'green','name':{'first':'Macias','last':'Calderon'},'company':'AMTAP','email':'macias.calderon@amtap.us','phone':'+1 (996) 569-3667','address':'305 Royce Street, Glidden, Iowa, 9248','about':'Exercitation nulla deserunt pariatur adipisicing. In commodo deserunt incididunt ut velit minim qui ut quis. Labore elit ullamco eiusmod voluptate in eu do est fugiat aute mollit deserunt. Eu duis proident velit fugiat velit ut. Ut non esse amet laborum nisi tempor in nulla.','registered':'Thursday, October 23, 2014 10:28 PM','latitude':'32.371629','longitude':'60.155135','tags':['commodo','elit','velit','excepteur','aliqua'],'greeting':'Hello, Macias! You have 9 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0ed0e8a6109e7fabf17','index':7,'guid':'675ff6b6-197b-4154-9775-813d661df822','isActive':false,'balance':'$2,850.62','picture':'http://placehold.it/32x32','age':37,'eyeColor':'green','name':{'first':'Stefanie','last':'Rivers'},'company':'RECRITUBE','email':'stefanie.rivers@recritube.biz','phone':'+1 (994) 591-3551','address':'995 Campus Road, Abrams, Virginia, 3251','about':'Esse aute non laborum Lorem nulla irure. Veniam elit aute ut et dolor non deserunt laboris tempor. Ipsum quis cupidatat laborum laboris voluptate esse duis eiusmod excepteur consectetur commodo ullamco qui occaecat. Culpa velit cillum occaecat minim nisi.','registered':'Thursday, June 9, 2016 3:40 PM','latitude':'-18.526825','longitude':'149.670782','tags':['occaecat','sunt','reprehenderit','ipsum','magna'],'greeting':'Hello, Stefanie! You have 9 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0edf7d9bc2db4e476e3','index':8,'guid':'adaefc55-f6ea-4bd1-a147-0e31c3ce7a21','isActive':true,'balance':'$2,555.13','picture':'http://placehold.it/32x32','age':20,'eyeColor':'blue','name':{'first':'Hillary','last':'Lancaster'},'company':'OLUCORE','email':'hillary.lancaster@olucore.ca','phone':'+1 (964) 474-3018','address':'232 Berriman Street, Kaka, Massachusetts, 6792','about':'Veniam ad laboris quis reprehenderit aliquip nisi sunt excepteur ea aute laborum excepteur incididunt. Nisi exercitation aliquip do culpa commodo ex officia ut enim mollit in deserunt in amet. Anim eu deserunt dolore non cupidatat ut enim incididunt aute dolore voluptate. Do cillum mollit laborum non incididunt occaecat aute voluptate nisi irure.','registered':'Thursday, June 4, 2015 9:45 PM','latitude':'88.075919','longitude':'-148.951368','tags':['reprehenderit','veniam','ad','aute','anim'],'greeting':'Hello, Hillary! You have 6 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed7b7192ad6a0f267c','index':9,'guid':'0ca9b8ea-f671-474e-be26-4a49cae4838a','isActive':true,'balance':'$3,684.51','picture':'http://placehold.it/32x32','age':40,'eyeColor':'brown','name':{'first':'Jill','last':'Conner'},'company':'EXOZENT','email':'jill.conner@exozent.info','phone':'+1 (887) 467-2168','address':'751 Thames Street, Juarez, American Samoa, 8386','about':'Enim voluptate et non est in magna laborum aliqua enim aliqua est non nostrud. Tempor est nulla ipsum consectetur esse nostrud est id. Consequat do voluptate cupidatat eu fugiat et fugiat velit id. Sint dolore ad qui tempor anim eu amet consectetur do elit aute adipisicing consequat ex.','registered':'Sunday, October 22, 2017 7:35 AM','latitude':'84.384911','longitude':'40.305648','tags':['tempor','sint','irure','et','ex'],'greeting':'Hello, Jill! You have 9 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed713fe676575aa72b','index':10,'guid':'c28023cf-cc57-4c2e-8d91-dfbe6bafadcd','isActive':false,'balance':'$2,792.45','picture':'http://placehold.it/32x32','age':25,'eyeColor':'brown','name':{'first':'Hurley','last':'George'},'company':'ZAJ','email':'hurley.george@zaj.tv','phone':'+1 (984) 547-3284','address':'727 Minna Street, Lacomb, Colorado, 2557','about':'Ex velit cupidatat veniam culpa. Eiusmod ut fugiat adipisicing incididunt consectetur exercitation Lorem exercitation ex. Incididunt anim aute incididunt fugiat cupidatat qui eu non reprehenderit. Eiusmod dolor nisi culpa excepteur ut velit minim dolor voluptate amet commodo culpa in.','registered':'Thursday, February 16, 2017 6:41 AM','latitude':'25.989949','longitude':'10.200053','tags':['minim','ut','sunt','consequat','ullamco'],'greeting':'Hello, Hurley! You have 8 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed1e56732746c70d8b','index':11,'guid':'e9766f13-766c-4450-b4d2-8b04580f60b7','isActive':true,'balance':'$3,874.26','picture':'http://placehold.it/32x32','age':35,'eyeColor':'green','name':{'first':'Leticia','last':'Pace'},'company':'HONOTRON','email':'leticia.pace@honotron.co.uk','phone':'+1 (974) 536-3322','address':'365 Goodwin Place, Savage, Nevada, 9191','about':'Nisi Lorem aliqua esse eiusmod magna. Ad minim incididunt proident ut Lorem cupidatat qui velit aliqua ullamco et ipsum in. Aliquip elit consectetur pariatur esse exercitation et officia quis. Occaecat tempor proident cillum anim ad commodo velit ut voluptate. Tempor et occaecat sit sint aliquip tempor nulla velit magna nisi proident exercitation Lorem id.','registered':'Saturday, August 4, 2018 5:05 AM','latitude':'70.620386','longitude':'-86.335813','tags':['occaecat','velit','labore','laboris','esse'],'greeting':'Hello, Leticia! You have 8 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed941337fe42f47426','index':12,'guid':'6d390762-17ea-4b58-9a36-b0c9a8748a42','isActive':true,'balance':'$1,049.61','picture':'http://placehold.it/32x32','age':38,'eyeColor':'green','name':{'first':'Rose','last':'Humphrey'},'company':'MYOPIUM','email':'rose.humphrey@myopium.io','phone':'+1 (828) 426-3086','address':'389 Sapphire Street, Saticoy, Marshall Islands, 1423','about':'Aliquip enim excepteur adipisicing ex. Consequat aliqua consequat nostrud do occaecat deserunt excepteur sit et ipsum sunt dolor eu. Dolore laborum commodo excepteur tempor ad adipisicing proident excepteur magna non Lorem proident consequat aute. Fugiat minim consequat occaecat voluptate esse velit officia laboris nostrud nisi ut voluptate.','registered':'Monday, April 16, 2018 12:38 PM','latitude':'-47.083742','longitude':'109.022423','tags':['aute','non','sit','adipisicing','mollit'],'greeting':'Hello, Rose! You have 9 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0edd0c02fc3fdc01a40','index':13,'guid':'07755618-6fdf-4b33-af50-364c18909227','isActive':true,'balance':'$1,823.61','picture':'http://placehold.it/32x32','age':36,'eyeColor':'green','name':{'first':'Judith','last':'Hale'},'company':'COLLAIRE','email':'judith.hale@collaire.me','phone':'+1 (922) 508-2843','address':'193 Coffey Street, Castleton, North Dakota, 3638','about':'Minim non ullamco ad anim nostrud dolore nostrud veniam consequat id eiusmod veniam laboris. Lorem irure esse mollit non velit aute id cupidatat est mollit occaecat magna excepteur. Adipisicing tempor nisi sit aliquip tempor pariatur tempor eu consectetur nulla amet nulla. Quis nisi nisi ea incididunt culpa et do. Esse officia eu pariatur velit sunt quis proident amet consectetur consequat. Nisi excepteur culpa nulla sit dolor deserunt excepteur dolor consequat elit cillum tempor Lorem.','registered':'Wednesday, August 24, 2016 12:29 AM','latitude':'-80.15514','longitude':'39.91007','tags':['consectetur','incididunt','aliquip','dolor','consequat'],'greeting':'Hello, Judith! You have 8 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0edb3e1e29caa4f728b','index':14,'guid':'2c6617a2-e7a9-4ff7-a8b9-e99554fe70fe','isActive':true,'balance':'$1,971.00','picture':'http://placehold.it/32x32','age':39,'eyeColor':'blue','name':{'first':'Estes','last':'Sweet'},'company':'GEEKKO','email':'estes.sweet@geekko.com','phone':'+1 (866) 448-3032','address':'847 Cove Lane, Kula, Mississippi, 9178','about':'Veniam consectetur occaecat est excepteur consequat ipsum cillum sit consectetur. Ut cupidatat et reprehenderit dolore enim do cillum qui pariatur ad laborum incididunt esse. Fugiat sunt dolor veniam laboris ipsum deserunt proident reprehenderit laboris non nostrud. Magna excepteur sint magna laborum tempor sit exercitation ipsum labore est ullamco ullamco. Cillum voluptate cillum ea laborum Lorem. Excepteur sint ut nisi est esse non. Minim excepteur ullamco velit nisi ut in elit exercitation ut dolore.','registered':'Sunday, August 12, 2018 5:06 PM','latitude':'-9.57771','longitude':'-159.94577','tags':['culpa','dolor','velit','anim','pariatur'],'greeting':'Hello, Estes! You have 7 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0edbcf088c6fd593091','index':15,'guid':'2cc79958-1b40-4e2c-907a-433903fd3da9','isActive':false,'balance':'$3,751.53','picture':'http://placehold.it/32x32','age':34,'eyeColor':'brown','name':{'first':'Kemp','last':'Spence'},'company':'EXOBLUE','email':'kemp.spence@exoblue.org','phone':'+1 (864) 487-2992','address':'217 Clay Street, Monument, North Carolina, 1460','about':'Nostrud duis cillum sint non commodo dolor aute aliqua adipisicing ad nulla non excepteur proident. Fugiat labore elit tempor cillum veniam reprehenderit laboris consectetur dolore amet qui cupidatat. Amet aliqua elit anim et consequat commodo excepteur officia anim aliqua ea eu labore cillum. Et ex dolor duis dolore commodo veniam et nisi.','registered':'Monday, October 29, 2018 5:23 AM','latitude':'-70.304222','longitude':'83.582371','tags':['velit','duis','consequat','incididunt','duis'],'greeting':'Hello, Kemp! You have 7 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed6400479feb3de505','index':16,'guid':'91ccae6d-a3ea-43cf-bb00-3f2729256cc9','isActive':false,'balance':'$2,477.79','picture':'http://placehold.it/32x32','age':40,'eyeColor':'blue','name':{'first':'Ronda','last':'Burris'},'company':'EQUITOX','email':'ronda.burris@equitox.net','phone':'+1 (817) 553-3228','address':'708 Lawton Street, Deputy, Wyoming, 8598','about':'Excepteur voluptate aliquip consequat cillum est duis sit cillum eu eiusmod et laborum ullamco. Et minim reprehenderit aute voluptate amet ullamco. Amet sit enim ad irure deserunt nostrud anim veniam consequat dolor commodo. Consequat do occaecat do exercitation ullamco dolor ut. Id laboris consequat est dolor dolore tempor ullamco anim do ut nulla deserunt labore. Mollit ex Lorem ullamco mollit.','registered':'Monday, April 23, 2018 5:27 PM','latitude':'-31.227208','longitude':'0.63785','tags':['ipsum','magna','consectetur','sit','irure'],'greeting':'Hello, Ronda! You have 5 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0eddbeab2e53e04d563','index':17,'guid':'a86d4eb6-6bd8-48c2-a8fc-1c933c835852','isActive':false,'balance':'$3,709.03','picture':'http://placehold.it/32x32','age':37,'eyeColor':'blue','name':{'first':'Rosario','last':'Dillard'},'company':'BARKARAMA','email':'rosario.dillard@barkarama.name','phone':'+1 (933) 525-3898','address':'730 Chauncey Street, Forbestown, South Carolina, 6894','about':'Est eu fugiat aliquip ea ad qui ad mollit ad tempor voluptate et incididunt reprehenderit. Incididunt fugiat commodo minim adipisicing culpa consectetur duis eu ut commodo consequat voluptate labore. Nostrud irure labore adipisicing irure quis magna consequat dolor Lorem sint enim. Sint excepteur eu dolore elit ut do mollit sunt enim est. Labore id nostrud sint Lorem esse nostrud.','registered':'Friday, December 25, 2015 8:59 PM','latitude':'37.440827','longitude':'44.580474','tags':['Lorem','sit','ipsum','ea','ut'],'greeting':'Hello, Rosario! You have 5 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0eddf8e9b9c031d04e8','index':18,'guid':'a96f997c-daf8-40d4-92e1-be07e2cf0f60','isActive':false,'balance':'$1,878.37','picture':'http://placehold.it/32x32','age':37,'eyeColor':'brown','name':{'first':'Sondra','last':'Gonzales'},'company':'XUMONK','email':'sondra.gonzales@xumonk.us','phone':'+1 (838) 560-2255','address':'230 Cox Place, Geyserville, Georgia, 6805','about':'Laborum sunt voluptate ea laboris nostrud. Amet deserunt aliqua Lorem voluptate velit deserunt occaecat minim ullamco. Lorem occaecat sit labore adipisicing ad magna mollit labore ullamco proident. Ea velit do proident fugiat esse commodo ex nostrud eu mollit pariatur. Labore laborum qui voluptate quis proident reprehenderit tempor dolore duis deserunt esse aliqua aliquip. Non veniam enim pariatur cupidatat ipsum dolore est reprehenderit. Non exercitation adipisicing proident magna elit occaecat non magna.','registered':'Sunday, June 26, 2016 4:02 AM','latitude':'62.247742','longitude':'-44.90666','tags':['ea','aute','in','voluptate','magna'],'greeting':'Hello, Sondra! You have 6 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed2c1bcd06781f677e','index':19,'guid':'6ac47a16-eed4-4460-92ee-e0dd33c1fbb5','isActive':false,'balance':'$3,730.64','picture':'http://placehold.it/32x32','age':20,'eyeColor':'brown','name':{'first':'Anastasia','last':'Vega'},'company':'FIREWAX','email':'anastasia.vega@firewax.biz','phone':'+1 (867) 493-3698','address':'803 Arlington Avenue, Rosburg, Northern Mariana Islands, 8769','about':'Sint ex nisi tempor sunt voluptate non et eiusmod irure. Aute reprehenderit dolor mollit aliqua Lorem voluptate occaecat. Sint laboris deserunt Lorem incididunt nulla cupidatat do.','registered':'Friday, March 18, 2016 12:02 PM','latitude':'-32.010216','longitude':'-87.874753','tags':['aliquip','mollit','mollit','ad','laborum'],'greeting':'Hello, Anastasia! You have 7 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed727fd645854bbf43','index':20,'guid':'67bd8cdb-ce6b-455c-944c-a80e17c6fa75','isActive':true,'balance':'$2,868.06','picture':'http://placehold.it/32x32','age':29,'eyeColor':'green','name':{'first':'Lucinda','last':'Cox'},'company':'ENDIPINE','email':'lucinda.cox@endipine.ca','phone':'+1 (990) 428-3002','address':'412 Thatford Avenue, Lafferty, New Jersey, 5271','about':'Esse nulla sunt ut consequat aute mollit. Est occaecat sunt nisi irure id anim est commodo. Elit mollit amet dolore sunt adipisicing ea laborum quis ea reprehenderit non consequat dolore. Minim sunt occaecat quis aute commodo dolore quis commodo proident. Sunt sint duis ullamco sit ea esse Lorem. Consequat pariatur eiusmod laboris adipisicing labore in laboris adipisicing adipisicing consequat aute ea et.','registered':'Friday, May 1, 2015 10:16 PM','latitude':'-14.200957','longitude':'-82.211386','tags':['do','sit','qui','officia','aliquip'],'greeting':'Hello, Lucinda! You have 9 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed5a97284eb2cbd3a8','index':21,'guid':'f9fc999d-515c-4fc4-b339-76300e1b4bf2','isActive':true,'balance':'$1,172.57','picture':'http://placehold.it/32x32','age':35,'eyeColor':'brown','name':{'first':'Conrad','last':'Bradley'},'company':'FUELWORKS','email':'conrad.bradley@fuelworks.info','phone':'+1 (956) 561-3226','address':'685 Fenimore Street, Esmont, Maryland, 7523','about':'Labore reprehenderit anim nisi sunt do nisi in. Est anim cillum id minim exercitation ullamco voluptate ipsum eu. Elit culpa consequat reprehenderit laborum in eu. Laboris amet voluptate laboris qui voluptate duis minim reprehenderit. Commodo sunt irure dolore sunt occaecat velit nisi eu minim minim.','registered':'Wednesday, January 18, 2017 11:13 PM','latitude':'31.665993','longitude':'38.868968','tags':['excepteur','exercitation','est','nisi','mollit'],'greeting':'Hello, Conrad! You have 10 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0edc4eaf6f760c38218','index':22,'guid':'8794ef5f-da2f-46f0-a755-c18a16409fd5','isActive':false,'balance':'$3,594.73','picture':'http://placehold.it/32x32','age':27,'eyeColor':'blue','name':{'first':'Marquez','last':'Vargas'},'company':'MALATHION','email':'marquez.vargas@malathion.tv','phone':'+1 (976) 438-3126','address':'296 Hall Street, National, Texas, 2067','about':'Proident cillum aute minim fugiat sunt aliqua non occaecat est duis id id tempor. Qui deserunt nisi amet pariatur proident eu laboris esse adipisicing magna. Anim anim mollit aute non magna nisi aute magna labore ullamco reprehenderit voluptate et ad. Proident adipisicing aute eiusmod nostrud nostrud deserunt culpa. Elit eu ullamco nisi aliqua dolor sint pariatur excepteur sit consectetur tempor. Consequat Lorem ullamco commodo veniam qui sint magna. Sit mollit ad aliquip est id eu officia id adipisicing duis ad.','registered':'Tuesday, November 17, 2015 6:16 PM','latitude':'-36.443667','longitude':'22.336776','tags':['aliquip','veniam','ipsum','Lorem','ex'],'greeting':'Hello, Marquez! You have 9 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0edd7c718518ee0466a','index':23,'guid':'ad8781a2-059e-4288-9879-309d53a99bf5','isActive':true,'balance':'$3,570.68','picture':'http://placehold.it/32x32','age':21,'eyeColor':'brown','name':{'first':'Snider','last':'Frost'},'company':'ZILODYNE','email':'snider.frost@zilodyne.co.uk','phone':'+1 (913) 485-3275','address':'721 Lincoln Road, Richmond, Utah, 672','about':'Minim enim Lorem esse incididunt do reprehenderit velit laborum ullamco. In aute eiusmod esse aliqua et labore tempor sunt ex mollit veniam tempor. Nulla elit cillum qui ullamco dolore amet deserunt magna amet laborum.','registered':'Saturday, August 23, 2014 12:58 AM','latitude':'-88.682554','longitude':'74.063179','tags':['nulla','ea','sint','aliquip','duis'],'greeting':'Hello, Snider! You have 6 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0edf026fece8e2c0970','index':24,'guid':'1b7d81e1-1dba-4322-bb1a-eaa6a24cccea','isActive':false,'balance':'$2,037.91','picture':'http://placehold.it/32x32','age':28,'eyeColor':'green','name':{'first':'Snyder','last':'Fletcher'},'company':'COMTEST','email':'snyder.fletcher@comtest.io','phone':'+1 (830) 538-3860','address':'221 Lewis Place, Zortman, Idaho, 572','about':'Elit anim enim esse dolore exercitation. Laboris esse sint adipisicing fugiat sint do occaecat ut voluptate sint nulla. Ad sint ut reprehenderit nostrud irure id consectetur officia velit consequat.','registered':'Sunday, January 1, 2017 1:13 AM','latitude':'-54.742604','longitude':'69.534932','tags':['exercitation','commodo','in','id','aliqua'],'greeting':'Hello, Snyder! You have 10 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed4b9a7f83da6d2dfd','index':25,'guid':'0b2cc6b6-0044-4b1c-aa31-bd72963457a0','isActive':false,'balance':'$1,152.76','picture':'http://placehold.it/32x32','age':27,'eyeColor':'blue','name':{'first':'Regina','last':'James'},'company':'TELPOD','email':'regina.james@telpod.me','phone':'+1 (989) 455-3228','address':'688 Essex Street, Clayville, Alabama, 2772','about':'Eiusmod elit culpa reprehenderit ea veniam. Officia irure culpa duis aute ut. Irure duis cillum officia ea pariatur velit ut dolor incididunt reprehenderit ex elit laborum. Est pariatur veniam ad irure. Labore velit sunt esse laboris aliqua velit deserunt deserunt sit. Elit eiusmod ad laboris aliquip minim irure excepteur enim quis. Quis incididunt adipisicing ut magna cupidatat sit amet culpa.','registered':'Tuesday, April 25, 2017 10:16 PM','latitude':'-75.088027','longitude':'47.209828','tags':['elit','nisi','est','voluptate','proident'],'greeting':'Hello, Regina! You have 6 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0ed10884f32f779f2bf','index':26,'guid':'1f6fb522-0002-46ff-8dac-451247f28168','isActive':true,'balance':'$1,948.79','picture':'http://placehold.it/32x32','age':25,'eyeColor':'brown','name':{'first':'Collins','last':'Mcpherson'},'company':'DIGIGEN','email':'collins.mcpherson@digigen.com','phone':'+1 (991) 519-2334','address':'317 Merit Court, Sanford, Michigan, 6468','about':'Magna qui culpa dolor officia labore mollit ex excepteur duis eiusmod. Ea cupidatat ex ipsum mollit do minim duis. Nisi eiusmod minim tempor id esse commodo sunt sunt ullamco ut do laborum ullamco magna. Aliquip laborum dolor officia officia eu nostrud velit minim est anim. Ex elit laborum sunt magna exercitation nisi cillum sunt aute qui ea ullamco. Cupidatat ea sunt aute dolor duis nisi Lorem ullamco eiusmod. Sit ea velit ad veniam aliqua ad elit cupidatat ut magna in.','registered':'Friday, June 10, 2016 4:38 PM','latitude':'25.513996','longitude':'14.911124','tags':['exercitation','non','sit','velit','officia'],'greeting':'Hello, Collins! You have 5 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed8a575110efb15c6c','index':27,'guid':'2a904c82-068b-4ded-9ae6-cfeb6d7e62c9','isActive':true,'balance':'$3,427.91','picture':'http://placehold.it/32x32','age':24,'eyeColor':'green','name':{'first':'Mckay','last':'Barrera'},'company':'COMVEYER','email':'mckay.barrera@comveyer.org','phone':'+1 (853) 470-2560','address':'907 Glenwood Road, Churchill, Oregon, 8583','about':'In voluptate esse dolore enim sint quis dolor do exercitation sint et labore nisi. Eiusmod tempor exercitation dolore elit sit velit sint et. Sit magna adipisicing eiusmod do anim velit deserunt laboris ad ea pariatur. Irure nisi anim mollit elit commodo nulla. Aute eiusmod sit nulla eiusmod. Eiusmod est officia commodo mollit laboris do deserunt eu do nisi amet. Proident ad duis eiusmod laboris Lorem ut culpa pariatur Lorem reprehenderit minim aliquip irure sunt.','registered':'Saturday, December 19, 2015 2:49 PM','latitude':'-55.243287','longitude':'138.035406','tags':['non','quis','laboris','enim','nisi'],'greeting':'Hello, Mckay! You have 7 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0edcd49ab6a73ff7f32','index':28,'guid':'5d3e0dae-3f58-437f-b12d-de24667a904d','isActive':true,'balance':'$3,270.52','picture':'http://placehold.it/32x32','age':35,'eyeColor':'blue','name':{'first':'Mabel','last':'Leonard'},'company':'QUADEEBO','email':'mabel.leonard@quadeebo.net','phone':'+1 (805) 432-2356','address':'965 Underhill Avenue, Falconaire, Minnesota, 4450','about':'Cupidatat amet sunt est ipsum occaecat sit fugiat excepteur Lorem Lorem ex ea ipsum. Ad incididunt est irure magna excepteur occaecat nostrud. Minim dolor id anim ipsum qui nostrud ullamco aute ex Lorem magna deserunt excepteur Lorem.','registered':'Saturday, March 28, 2015 5:55 AM','latitude':'27.388359','longitude':'156.408728','tags':['quis','velit','deserunt','dolore','sit'],'greeting':'Hello, Mabel! You have 7 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0edde16ac2dc2fbb6c1','index':29,'guid':'d50c2233-70fc-4748-8ebf-02d45ac2a446','isActive':false,'balance':'$3,100.70','picture':'http://placehold.it/32x32','age':30,'eyeColor':'green','name':{'first':'Pace','last':'Duke'},'company':'SEQUITUR','email':'pace.duke@sequitur.name','phone':'+1 (983) 568-3119','address':'895 Melrose Street, Reno, Connecticut, 6259','about':'Ex veniam aliquip exercitation mollit elit est minim veniam aliqua labore deserunt. Dolor sunt sint cillum Lorem nisi ea irure cupidatat. Velit ut culpa cupidatat consequat cillum. Sint voluptate quis laboris qui incididunt do elit Lorem qui ullamco ut eu pariatur occaecat.','registered':'Saturday, August 18, 2018 2:18 PM','latitude':'31.930443','longitude':'-129.494784','tags':['culpa','est','nostrud','quis','aliquip'],'greeting':'Hello, Pace! You have 8 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0edb908d85642ba77e8','index':30,'guid':'3edb6e42-367a-403d-a511-eb78bcc11f60','isActive':true,'balance':'$1,912.07','picture':'http://placehold.it/32x32','age':24,'eyeColor':'green','name':{'first':'Cohen','last':'Morrison'},'company':'POWERNET','email':'cohen.morrison@powernet.us','phone':'+1 (888) 597-2141','address':'565 Troutman Street, Idledale, West Virginia, 3196','about':'Ullamco voluptate duis commodo amet occaecat consequat et occaecat dolore nulla eu. Do aliqua sunt deserunt occaecat laboris labore voluptate cupidatat ullamco exercitation aliquip elit voluptate anim. Occaecat deserunt in labore cillum aute deserunt ea excepteur laboris sunt. Officia irure sint incididunt labore sint ipsum ullamco ea elit. Fugiat nostrud sunt ut officia mollit proident sunt dolor fugiat esse tempor do.','registered':'Friday, January 1, 2016 5:42 AM','latitude':'-20.01215','longitude':'26.361552','tags':['consectetur','sunt','nulla','reprehenderit','dolore'],'greeting':'Hello, Cohen! You have 10 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0ed91c77aa25a64a757','index':31,'guid':'8999a97b-0035-4f19-b555-91dd69aaa9b8','isActive':false,'balance':'$3,097.67','picture':'http://placehold.it/32x32','age':25,'eyeColor':'brown','name':{'first':'Stout','last':'Valdez'},'company':'UPLINX','email':'stout.valdez@uplinx.biz','phone':'+1 (854) 480-3633','address':'880 Chestnut Avenue, Lowgap, Hawaii, 1537','about':'Cupidatat enim dolore non voluptate. Aliqua ut non Lorem in exercitation reprehenderit voluptate. Excepteur deserunt tempor laboris quis.','registered':'Wednesday, March 16, 2016 6:53 AM','latitude':'50.328393','longitude':'-25.990308','tags':['ea','fugiat','duis','consectetur','enim'],'greeting':'Hello, Stout! You have 5 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0ed0f52176c8c3e1bed','index':32,'guid':'743abcbd-1fab-4aed-8cb7-3c935eb64c74','isActive':false,'balance':'$1,118.54','picture':'http://placehold.it/32x32','age':30,'eyeColor':'blue','name':{'first':'Ortega','last':'Joseph'},'company':'APEXIA','email':'ortega.joseph@apexia.ca','phone':'+1 (872) 596-3024','address':'304 Canda Avenue, Mulino, New York, 8721','about':'Ipsum elit id cupidatat minim nisi minim. Ea ex amet ea ipsum Lorem deserunt. Occaecat cupidatat magna cillum aliquip sint id quis amet nostrud officia enim laborum. Aliqua deserunt amet commodo laboris labore mollit est. Officia voluptate Lorem esse mollit aliquip laboris cupidatat minim et. Labore esse incididunt officia nostrud pariatur reprehenderit.','registered':'Tuesday, January 31, 2017 6:06 AM','latitude':'43.861714','longitude':'33.771783','tags':['ut','Lorem','esse','quis','fugiat'],'greeting':'Hello, Ortega! You have 6 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0ed2c00cdd101b6cd52','index':33,'guid':'4f6f99cf-f692-4d03-b23a-26f2b27273bd','isActive':true,'balance':'$1,682.91','picture':'http://placehold.it/32x32','age':20,'eyeColor':'blue','name':{'first':'Sampson','last':'Taylor'},'company':'GEOFORMA','email':'sampson.taylor@geoforma.info','phone':'+1 (911) 482-2993','address':'582 Kent Street, Umapine, Virgin Islands, 5300','about':'Voluptate laboris occaecat laboris tempor cillum quis cupidatat qui pariatur. Lorem minim commodo mollit adipisicing Lorem ut dolor consectetur ipsum. Sint sit voluptate labore aliqua ex labore velit. Ullamco tempor consectetur voluptate deserunt voluptate minim enim. Cillum commodo duis reprehenderit eu duis.','registered':'Thursday, November 9, 2017 11:24 PM','latitude':'24.949379','longitude':'155.034468','tags':['Lorem','cupidatat','elit','reprehenderit','commodo'],'greeting':'Hello, Sampson! You have 8 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed4b7210ba0bc0d508','index':34,'guid':'73fd415f-f8cf-43e0-a86c-e725d000abd4','isActive':false,'balance':'$1,289.37','picture':'http://placehold.it/32x32','age':30,'eyeColor':'green','name':{'first':'Shari','last':'Melendez'},'company':'DIGIPRINT','email':'shari.melendez@digiprint.tv','phone':'+1 (914) 475-3995','address':'950 Wolf Place, Enetai, Alaska, 693','about':'Dolor incididunt et est commodo aliquip labore ad ullamco. Velit ex cillum nulla elit ex esse. Consectetur mollit fugiat cillum proident elit sunt non officia cillum ex laboris sint eu. Esse nulla eu officia in Lorem sint minim esse velit. Est Lorem ipsum enim aute. Elit minim eiusmod officia reprehenderit officia ut irure Lorem.','registered':'Wednesday, August 23, 2017 11:12 PM','latitude':'-70.347863','longitude':'94.812072','tags':['ea','ex','fugiat','duis','eu'],'greeting':'Hello, Shari! You have 7 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed85ac364619d892ef','index':35,'guid':'c1905f34-14ff-4bd8-b683-02cac4d52623','isActive':false,'balance':'$2,538.50','picture':'http://placehold.it/32x32','age':30,'eyeColor':'green','name':{'first':'Santiago','last':'Joyner'},'company':'BRAINCLIP','email':'santiago.joyner@brainclip.co.uk','phone':'+1 (835) 405-2676','address':'554 Rose Street, Muir, Kentucky, 7752','about':'Quis culpa dolore fugiat magna culpa non deserunt consectetur elit. Id cupidatat occaecat duis irure ullamco elit in labore magna pariatur cillum est. Mollit dolore velit ipsum anim aliqua culpa sint. Occaecat aute anim ut sunt eu.','registered':'Thursday, January 18, 2018 4:49 PM','latitude':'57.057918','longitude':'-50.472596','tags':['ullamco','ullamco','sunt','voluptate','irure'],'greeting':'Hello, Santiago! You have 7 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed1763f56b1121fa88','index':36,'guid':'a7f50659-4ae3-4f3e-a9d8-087e05334b51','isActive':false,'balance':'$1,435.16','picture':'http://placehold.it/32x32','age':37,'eyeColor':'blue','name':{'first':'Adeline','last':'Hoffman'},'company':'BITREX','email':'adeline.hoffman@bitrex.io','phone':'+1 (823) 488-3201','address':'221 Corbin Place, Edmund, Palau, 193','about':'Magna ullamco consectetur velit adipisicing cillum ea. Est qui incididunt est ullamco ex aute exercitation irure. Cupidatat consectetur proident qui fugiat do. Labore magna aliqua consectetur fugiat. Excepteur deserunt sit qui dolor fugiat aute sunt anim ipsum magna ea commodo qui. Minim eu adipisicing ut irure excepteur eiusmod aliqua. Voluptate nisi ad consequat qui.','registered':'Tuesday, June 14, 2016 9:26 AM','latitude':'-53.123355','longitude':'88.180776','tags':['non','est','commodo','ut','aliquip'],'greeting':'Hello, Adeline! You have 9 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0ed945d079f63e3185e','index':37,'guid':'1f4619e0-9289-4bea-a9db-a75f4cba1138','isActive':true,'balance':'$2,019.54','picture':'http://placehold.it/32x32','age':36,'eyeColor':'blue','name':{'first':'Porter','last':'Morse'},'company':'COMVOY','email':'porter.morse@comvoy.me','phone':'+1 (933) 562-3220','address':'416 India Street, Bourg, Rhode Island, 2266','about':'Et sint anim et sunt. Non mollit sunt cillum veniam sunt sint amet non mollit. Fugiat ea ullamco pariatur deserunt ex do minim irure irure.','registered':'Saturday, July 16, 2016 10:03 PM','latitude':'-81.782545','longitude':'69.783509','tags':['irure','consequat','veniam','nulla','velit'],'greeting':'Hello, Porter! You have 10 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed411dd0f06c66bba6','index':38,'guid':'93c900f0-54c0-4c4c-b21d-d59d8d7c6177','isActive':true,'balance':'$3,764.84','picture':'http://placehold.it/32x32','age':26,'eyeColor':'green','name':{'first':'Fitzgerald','last':'Logan'},'company':'UTARIAN','email':'fitzgerald.logan@utarian.com','phone':'+1 (815) 461-2709','address':'498 Logan Street, Tonopah, Arkansas, 6652','about':'Quis Lorem sit est et dolor est esse in veniam. Mollit anim nostrud laboris consequat voluptate qui ad ipsum sint laborum exercitation quis ipsum. Incididunt cupidatat esse ea amet deserunt consequat eu proident duis adipisicing pariatur. Amet deserunt mollit aliquip mollit consequat sunt quis labore laboris quis. Magna cillum fugiat anim velit Lorem duis. Lorem duis amet veniam occaecat est excepteur ut ea velit esse non pariatur. Do veniam quis eu consequat ad duis incididunt minim dolore sit non minim adipisicing et.','registered':'Wednesday, August 9, 2017 9:20 PM','latitude':'24.480657','longitude':'-108.693421','tags':['dolore','ad','occaecat','quis','labore'],'greeting':'Hello, Fitzgerald! You have 5 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0edbb6f14559d8a7b28','index':39,'guid':'9434f48b-70a0-4161-8d06-c53bf8b9df94','isActive':true,'balance':'$3,713.47','picture':'http://placehold.it/32x32','age':25,'eyeColor':'blue','name':{'first':'Mcconnell','last':'Nash'},'company':'TETAK','email':'mcconnell.nash@tetak.org','phone':'+1 (956) 477-3586','address':'853 Turnbull Avenue, Clarence, Missouri, 1599','about':'Culpa excepteur minim anim magna dolor dolore ad ex eu. In cupidatat cillum elit dolore in est minim dolore consectetur reprehenderit voluptate laborum. Deserunt id velit ad dolor mollit.','registered':'Saturday, November 10, 2018 9:27 AM','latitude':'1.691589','longitude':'143.704377','tags':['ut','deserunt','sit','cupidatat','ea'],'greeting':'Hello, Mcconnell! You have 10 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed1a87ea0390733ffa','index':40,'guid':'ec8a55f7-7114-4787-b1ff-4e631731bc2c','isActive':true,'balance':'$2,200.71','picture':'http://placehold.it/32x32','age':25,'eyeColor':'brown','name':{'first':'Kitty','last':'Meyers'},'company':'FIBEROX','email':'kitty.meyers@fiberox.net','phone':'+1 (864) 458-3826','address':'537 Georgia Avenue, Thermal, Illinois, 7930','about':'Non excepteur laboris Lorem magna adipisicing exercitation. Anim esse in pariatur minim ipsum qui voluptate irure. Pariatur Lorem pariatur esse commodo aute adipisicing anim commodo. Exercitation nostrud aliqua duis et amet amet tempor.','registered':'Tuesday, September 13, 2016 8:16 PM','latitude':'19.59506','longitude':'-57.814297','tags':['duis','ullamco','velit','sint','consequat'],'greeting':'Hello, Kitty! You have 9 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0ed4dc76717bf1217b3','index':41,'guid':'40521cde-f835-4620-902b-af7abf185d8d','isActive':false,'balance':'$2,907.02','picture':'http://placehold.it/32x32','age':26,'eyeColor':'green','name':{'first':'Klein','last':'Goodwin'},'company':'PLASTO','email':'klein.goodwin@plasto.name','phone':'+1 (950) 563-3104','address':'764 Devoe Street, Lindcove, Oklahoma, 458','about':'Amet aliqua magna ea veniam non aliquip irure esse id ipsum cillum sint tempor dolor. Ullamco deserunt fugiat amet pariatur culpa nostrud commodo commodo. Ad occaecat magna adipisicing voluptate. Minim ad adipisicing cupidatat elit nostrud eu irure. Cupidatat occaecat aute magna consectetur dolore anim et. Ex voluptate velit exercitation laborum ad ullamco ad. Aliquip nulla ipsum dolore cillum qui nostrud eu adipisicing amet tempor do.','registered':'Tuesday, February 13, 2018 3:56 PM','latitude':'-27.168725','longitude':'-29.499285','tags':['minim','labore','do','deserunt','dolor'],'greeting':'Hello, Klein! You have 6 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed1ac77396b29aee9e','index':42,'guid':'7cfc03e3-30e9-4ae1-a1f5-f6c3223ca770','isActive':true,'balance':'$2,986.47','picture':'http://placehold.it/32x32','age':22,'eyeColor':'brown','name':{'first':'Isabelle','last':'Bishop'},'company':'GEEKNET','email':'isabelle.bishop@geeknet.us','phone':'+1 (908) 418-2642','address':'729 Willmohr Street, Aguila, Montana, 7510','about':'In nulla commodo nostrud sint. Elit et occaecat et aliqua aliquip magna esse commodo duis Lorem dolor magna enim deserunt. Ipsum pariatur reprehenderit ipsum adipisicing mollit incididunt ut. Sunt in consequat ex ut minim non qui anim labore. Deserunt minim voluptate in nulla occaecat.','registered':'Monday, September 15, 2014 6:22 AM','latitude':'-81.686947','longitude':'38.409291','tags':['proident','est','aliqua','veniam','anim'],'greeting':'Hello, Isabelle! You have 7 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0edb3a070c9469a4893','index':43,'guid':'3dec76b4-0b55-4765-a2fd-b8dbd9c82f8f','isActive':true,'balance':'$2,501.24','picture':'http://placehold.it/32x32','age':31,'eyeColor':'blue','name':{'first':'Josefina','last':'Turner'},'company':'COMSTAR','email':'josefina.turner@comstar.biz','phone':'+1 (908) 566-3029','address':'606 Schenck Place, Brutus, Vermont, 8681','about':'Enim consectetur pariatur sint dolor nostrud est deserunt nulla quis pariatur sit. Ad aute incididunt nisi excepteur duis est velit voluptate ullamco occaecat magna reprehenderit aliquip. Proident deserunt consectetur non et exercitation elit dolore enim aliqua incididunt anim amet. Ex esse sint commodo minim aliqua ut irure. Proident ex culpa voluptate fugiat nisi. Sint commodo laboris excepteur minim ipsum labore tempor quis magna.','registered':'Saturday, December 31, 2016 6:38 AM','latitude':'35.275088','longitude':'24.30485','tags':['minim','ut','irure','Lorem','veniam'],'greeting':'Hello, Josefina! You have 6 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed1aa7d74128ee3d0f','index':44,'guid':'10599279-c367-46c4-9f7a-744c2e4bf6c9','isActive':true,'balance':'$1,753.06','picture':'http://placehold.it/32x32','age':27,'eyeColor':'blue','name':{'first':'Lily','last':'Haynes'},'company':'KIOSK','email':'lily.haynes@kiosk.ca','phone':'+1 (872) 451-2301','address':'509 Balfour Place, Grazierville, New Hampshire, 2750','about':'Nisi aliquip occaecat nostrud do sint qui nisi officia Lorem. Ad et et laboris nisi dolore aliqua eu. Aliqua veniam quis eu pariatur incididunt mollit id deserunt officia eiusmod. Consequat adipisicing do nisi voluptate eiusmod minim pariatur minim nisi nostrud culpa cupidatat. Irure consectetur id consequat adipisicing ullamco occaecat do. Ex proident ea quis nulla incididunt sunt excepteur incididunt. Aliquip minim nostrud non anim Lorem.','registered':'Tuesday, November 20, 2018 9:28 AM','latitude':'-12.677798','longitude':'114.506787','tags':['culpa','amet','elit','officia','irure'],'greeting':'Hello, Lily! You have 8 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed74c76f2e84e201ce','index':45,'guid':'ec0a68d4-629e-46c9-9af7-f6ea867f02ba','isActive':true,'balance':'$1,477.93','picture':'http://placehold.it/32x32','age':23,'eyeColor':'green','name':{'first':'Shauna','last':'Pitts'},'company':'SPACEWAX','email':'shauna.pitts@spacewax.info','phone':'+1 (841) 406-2360','address':'348 Tabor Court, Westwood, Puerto Rico, 8297','about':'Aliquip irure officia magna ea magna mollit ea non amet deserunt. Veniam mollit labore culpa magna aliqua quis consequat est consectetur ea reprehenderit nostrud consequat aliqua. Mollit do ipsum mollit eiusmod.','registered':'Thursday, October 2, 2014 2:48 AM','latitude':'-55.17388','longitude':'-13.370494','tags':['anim','consectetur','cillum','veniam','duis'],'greeting':'Hello, Shauna! You have 7 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed419e718484b16722','index':46,'guid':'b2d6101d-5646-43f4-8207-284494e5a990','isActive':false,'balance':'$2,006.96','picture':'http://placehold.it/32x32','age':27,'eyeColor':'brown','name':{'first':'Lawrence','last':'Boyer'},'company':'SKYPLEX','email':'lawrence.boyer@skyplex.tv','phone':'+1 (953) 548-2618','address':'464 Pilling Street, Blandburg, Arizona, 5531','about':'Culpa sit minim pariatur mollit cupidatat sunt duis. Nisi ea proident veniam exercitation adipisicing Lorem aliquip amet dolor voluptate in nisi. Non commodo anim sunt est fugiat laborum nisi aliqua non Lorem exercitation dolor. Laboris dolore do minim ut eiusmod enim magna cillum laborum consectetur aliquip minim enim Lorem. Veniam ex veniam occaecat aliquip elit aliquip est eiusmod minim minim adipisicing.','registered':'Wednesday, July 30, 2014 2:17 AM','latitude':'-78.681255','longitude':'139.960626','tags':['consequat','Lorem','incididunt','dolor','esse'],'greeting':'Hello, Lawrence! You have 6 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed08a9024998292c70','index':47,'guid':'277de142-ebeb-4828-906a-7fd8bc0a738a','isActive':true,'balance':'$1,273.19','picture':'http://placehold.it/32x32','age':27,'eyeColor':'brown','name':{'first':'Sonya','last':'Stafford'},'company':'AQUACINE','email':'sonya.stafford@aquacine.co.uk','phone':'+1 (824) 581-3927','address':'641 Bowery Street, Hillsboro, Delaware, 7893','about':'Culpa labore ex reprehenderit mollit cupidatat dolore et ut quis in. Sint esse culpa enim culpa tempor exercitation veniam minim consectetur. Sunt est laboris minim quis incididunt exercitation laboris cupidatat fugiat ad. Deserunt ipsum do dolor cillum excepteur incididunt.','registered':'Thursday, March 26, 2015 1:10 PM','latitude':'-84.750592','longitude':'165.493533','tags':['minim','officia','dolore','ipsum','est'],'greeting':'Hello, Sonya! You have 8 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0edd5037f2c79ecde68','index':48,'guid':'2dc6532f-9a26-49aa-b444-8923896db89c','isActive':false,'balance':'$3,168.93','picture':'http://placehold.it/32x32','age':36,'eyeColor':'brown','name':{'first':'Marguerite','last':'Stuart'},'company':'ACCUFARM','email':'marguerite.stuart@accufarm.io','phone':'+1 (848) 535-2253','address':'301 Menahan Street, Sunnyside, Nebraska, 4809','about':'Deserunt sint labore voluptate amet anim culpa nostrud adipisicing enim cupidatat ullamco exercitation fugiat est. Magna dolor aute incididunt ea ad adipisicing. Do cupidatat ut officia officia culpa sit do.','registered':'Thursday, May 8, 2014 1:25 PM','latitude':'21.82277','longitude':'-7.368347','tags':['labore','nulla','ullamco','irure','adipisicing'],'greeting':'Hello, Marguerite! You have 6 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0edb26d315635818dae','index':49,'guid':'083a5eda-0a70-4f89-87f7-2cd386c0f22a','isActive':false,'balance':'$2,576.25','picture':'http://placehold.it/32x32','age':38,'eyeColor':'blue','name':{'first':'Louella','last':'Holloway'},'company':'BEDDER','email':'louella.holloway@bedder.me','phone':'+1 (801) 425-3761','address':'545 Lafayette Avenue, Caledonia, Louisiana, 2816','about':'Qui exercitation occaecat dolore mollit. Fugiat cupidatat proident culpa fugiat quis. In cupidatat commodo elit ea enim occaecat esse exercitation nostrud occaecat veniam laboris fugiat. Nisi sunt reprehenderit aliqua reprehenderit tempor id dolore ullamco pariatur reprehenderit et eu ex pariatur.','registered':'Wednesday, November 5, 2014 1:10 AM','latitude':'36.385637','longitude':'77.949423','tags':['eu','irure','velit','non','aliquip'],'greeting':'Hello, Louella! You have 7 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed77cd60a1abc1ecce','index':50,'guid':'2887c3c1-3eba-4237-a0db-1977eed94554','isActive':true,'balance':'$1,633.51','picture':'http://placehold.it/32x32','age':22,'eyeColor':'green','name':{'first':'Bates','last':'Carrillo'},'company':'ZOMBOID','email':'bates.carrillo@zomboid.com','phone':'+1 (934) 405-2006','address':'330 Howard Alley, Troy, Kansas, 4881','about':'Voluptate esse est ullamco anim tempor ea reprehenderit. Occaecat pariatur deserunt cillum laboris labore id exercitation esse ipsum ipsum ex aliquip. Sunt non elit est ea occaecat. Magna deserunt commodo aliqua ipsum est cillum dolor nisi. Ex duis est tempor tempor laboris do do quis id magna. Dolor do est elit eu laborum ullamco culpa consequat velit eiusmod tempor.','registered':'Saturday, May 28, 2016 3:56 AM','latitude':'83.310134','longitude':'-105.862836','tags':['est','commodo','ea','commodo','sunt'],'greeting':'Hello, Bates! You have 9 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0ed5ec0ec299b471fb5','index':51,'guid':'512b5e67-f785-492e-9d94-e43ef8b399b8','isActive':false,'balance':'$3,032.22','picture':'http://placehold.it/32x32','age':30,'eyeColor':'blue','name':{'first':'Floyd','last':'Yang'},'company':'FRENEX','email':'floyd.yang@frenex.org','phone':'+1 (924) 566-3304','address':'418 Quay Street, Chumuckla, Guam, 7743','about':'Irure sit velit exercitation dolore est nisi incididunt ut quis consectetur incididunt est dolor. Aute nisi enim esse aliquip enim culpa commodo consectetur. Duis laborum magna ad duis ipsum aliqua eiusmod cillum. Consectetur et duis eiusmod irure ad est nisi incididunt eiusmod labore. Pariatur proident in Lorem adipisicing mollit proident excepteur nulla do nostrud mollit eiusmod. Duis ad dolore irure fugiat anim laboris ipsum et sit duis ipsum voluptate. Lorem non aute exercitation qui ullamco officia minim sint pariatur ut dolor.','registered':'Wednesday, January 18, 2017 2:01 AM','latitude':'45.888721','longitude':'-41.232793','tags':['elit','in','esse','ea','officia'],'greeting':'Hello, Floyd! You have 5 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0ed51e26ca89e5caf49','index':52,'guid':'4e0907f6-facc-46df-8952-73561a53fe33','isActive':true,'balance':'$3,767.41','picture':'http://placehold.it/32x32','age':25,'eyeColor':'blue','name':{'first':'Gardner','last':'Carey'},'company':'KLUGGER','email':'gardner.carey@klugger.net','phone':'+1 (876) 481-3502','address':'131 Utica Avenue, Cannondale, Federated States Of Micronesia, 610','about':'Amet ad pariatur excepteur anim ex officia commodo proident aliqua occaecat consequat Lorem officia sit. Id minim velit nisi laboris nisi nulla incididunt eiusmod velit. Deserunt labore quis et tempor. Et labore exercitation laborum officia ullamco nostrud adipisicing laboris esse laborum aute anim elit. Sunt ad officia tempor esse et quis aliquip irure pariatur laborum id quis ex. Eu consequat nisi deserunt id eu proident ex minim aute nulla tempor ex.','registered':'Friday, February 21, 2014 6:42 AM','latitude':'-54.740231','longitude':'15.01484','tags':['commodo','laboris','occaecat','aliquip','adipisicing'],'greeting':'Hello, Gardner! You have 10 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed52e3c9407105093a','index':53,'guid':'1d3b9e7a-1bc3-40ea-b808-1c33f0d48c70','isActive':true,'balance':'$1,113.30','picture':'http://placehold.it/32x32','age':26,'eyeColor':'blue','name':{'first':'Herman','last':'Rogers'},'company':'TALENDULA','email':'herman.rogers@talendula.name','phone':'+1 (818) 521-2005','address':'541 Norman Avenue, Winfred, Tennessee, 447','about':'Culpa ex laborum non ad ullamco officia. Nisi mollit mollit voluptate sit sint ullamco. Lorem exercitation nulla anim eiusmod deserunt magna sint. Officia sunt eiusmod aliqua reprehenderit sunt mollit sit cupidatat sint.','registered':'Wednesday, July 11, 2018 1:05 AM','latitude':'-20.708105','longitude':'-151.294563','tags':['exercitation','minim','officia','qui','enim'],'greeting':'Hello, Herman! You have 10 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0edfcb123d545b6edb4','index':54,'guid':'c0e0c669-4eed-43ee-bdd0-78fe6e9ca4d5','isActive':true,'balance':'$3,309.64','picture':'http://placehold.it/32x32','age':22,'eyeColor':'green','name':{'first':'Whitley','last':'Stark'},'company':'MUSAPHICS','email':'whitley.stark@musaphics.us','phone':'+1 (803) 476-2151','address':'548 Cobek Court, Chamizal, Indiana, 204','about':'Adipisicing veniam dolor ex sint sit id eu voluptate. Excepteur veniam proident exercitation id eu et sunt pariatur. Qui occaecat culpa aliqua nisi excepteur minim veniam. Est duis nulla laborum excepteur cillum pariatur sint incididunt. Velit commodo eu incididunt voluptate. Amet laboris laboris id adipisicing labore eiusmod consequat minim cillum et.','registered':'Thursday, March 27, 2014 9:10 AM','latitude':'71.219596','longitude':'51.012855','tags':['reprehenderit','mollit','laborum','voluptate','aliquip'],'greeting':'Hello, Whitley! You have 7 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed81510dfc61602fcf','index':55,'guid':'7ec5c24d-f169-4399-a2a3-300c0f45e52e','isActive':false,'balance':'$3,721.04','picture':'http://placehold.it/32x32','age':23,'eyeColor':'green','name':{'first':'Gretchen','last':'Wade'},'company':'EWEVILLE','email':'gretchen.wade@eweville.biz','phone':'+1 (977) 598-3700','address':'721 Colonial Road, Brookfield, South Dakota, 3888','about':'Fugiat consequat sint ut ut et ullamco eiusmod deserunt pariatur. Veniam eiusmod esse fugiat mollit. Proident laboris minim qui do ipsum excepteur exercitation irure anim. Aliqua labore quis eu fugiat dolore ullamco velit Lorem voluptate ipsum nostrud eiusmod laborum proident.','registered':'Friday, October 12, 2018 10:59 AM','latitude':'41.937653','longitude':'63.378531','tags':['aute','cillum','ea','ex','aute'],'greeting':'Hello, Gretchen! You have 9 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0edf78f77d4a7d557bb','index':56,'guid':'8718ada7-6fd0-49ef-a405-29850503948b','isActive':false,'balance':'$3,341.33','picture':'http://placehold.it/32x32','age':32,'eyeColor':'blue','name':{'first':'Naomi','last':'Frye'},'company':'MAZUDA','email':'naomi.frye@mazuda.ca','phone':'+1 (825) 427-2255','address':'741 Coyle Street, Comptche, Pennsylvania, 8441','about':'Aliqua fugiat laborum quis ullamco cupidatat sit dolor nulla dolore. Do Lorem et ipsum culpa irure sit do dolor qui sit laboris aliqua. Ex consectetur irure in veniam reprehenderit amet do elit eiusmod est magna.','registered':'Thursday, January 9, 2014 7:18 AM','latitude':'41.078645','longitude':'-50.241966','tags':['do','aliquip','eiusmod','velit','id'],'greeting':'Hello, Naomi! You have 7 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0edbf45db2e072a48b4','index':57,'guid':'c158ebf7-fb8b-4ea8-adbf-8c51c6486715','isActive':true,'balance':'$2,811.55','picture':'http://placehold.it/32x32','age':25,'eyeColor':'blue','name':{'first':'Lamb','last':'Johns'},'company':'DOGTOWN','email':'lamb.johns@dogtown.info','phone':'+1 (946) 530-3057','address':'559 Malbone Street, Kennedyville, California, 2052','about':'Eiusmod dolor labore cillum ad veniam elit voluptate voluptate pariatur est cupidatat. Laboris ut qui in cillum sunt dolore ut enim. Minim nostrud ex qui quis reprehenderit magna ipsum cupidatat irure minim laboris veniam irure. Fugiat velit deserunt aliquip in esse proident excepteur labore reprehenderit excepteur sunt in cupidatat exercitation. Ex pariatur irure mollit tempor non magna ex.','registered':'Friday, April 21, 2017 1:51 AM','latitude':'-61.403599','longitude':'-93.447102','tags':['aliquip','tempor','sint','enim','ipsum'],'greeting':'Hello, Lamb! You have 6 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0edbb9c88190cb59cf2','index':58,'guid':'f0de5ac5-eb28-491b-81c5-76d447c9055e','isActive':true,'balance':'$1,611.99','picture':'http://placehold.it/32x32','age':37,'eyeColor':'brown','name':{'first':'Lynette','last':'Cleveland'},'company':'ARTWORLDS','email':'lynette.cleveland@artworlds.tv','phone':'+1 (889) 596-3723','address':'439 Montauk Avenue, Felt, New Mexico, 9681','about':'Incididunt aliquip est aliquip est ullamco do consectetur dolor. Lorem mollit mollit dolor et ipsum ut qui veniam aute ea. Adipisicing reprehenderit culpa velit laborum adipisicing amet consectetur velit nisi. Ut qui proident ad cillum excepteur adipisicing quis labore. Duis velit culpa et excepteur eiusmod ex labore in nisi nostrud. Et ullamco minim excepteur ut enim reprehenderit consequat eiusmod laboris Lorem commodo exercitation qui laborum.','registered':'Wednesday, August 26, 2015 12:53 PM','latitude':'49.861336','longitude':'86.865926','tags':['reprehenderit','minim','in','minim','nostrud'],'greeting':'Hello, Lynette! You have 6 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0ed5b760ddde7295fa8','index':59,'guid':'f8180d3f-c5c0-48b2-966e-a0b2a80f8e84','isActive':true,'balance':'$3,376.75','picture':'http://placehold.it/32x32','age':32,'eyeColor':'green','name':{'first':'Obrien','last':'Page'},'company':'GLASSTEP','email':'obrien.page@glasstep.co.uk','phone':'+1 (902) 583-3086','address':'183 Ridgewood Avenue, Vicksburg, Wisconsin, 7430','about':'Aute excepteur cillum exercitation duis Lorem irure labore elit. Labore magna cupidatat velit consectetur minim do Lorem in excepteur commodo ea consequat ullamco laborum. Ut in id occaecat eu quis duis id ea deserunt veniam.','registered':'Wednesday, March 29, 2017 12:13 AM','latitude':'-40.156154','longitude':'72.76301','tags':['excepteur','non','anim','nulla','anim'],'greeting':'Hello, Obrien! You have 6 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed52985d3d8901d653','index':60,'guid':'d2e14fa1-8c54-4bcb-8a58-eb2e6f8d0e45','isActive':true,'balance':'$1,659.47','picture':'http://placehold.it/32x32','age':33,'eyeColor':'brown','name':{'first':'Knowles','last':'Goodman'},'company':'CENTREE','email':'knowles.goodman@centree.io','phone':'+1 (862) 563-3692','address':'504 Lott Street, Allensworth, Florida, 7148','about':'Do aliquip voluptate aliqua nostrud. Eu dolore ex occaecat pariatur aute laborum aute nulla aute amet. Excepteur sit laboris ad non anim ut officia ut ad exercitation officia dolore laboris. Esse voluptate minim deserunt nostrud exercitation laborum voluptate exercitation id laborum fugiat proident cupidatat proident. Nulla nostrud est sint adipisicing incididunt exercitation dolor sit et elit tempor occaecat sint culpa. Pariatur occaecat laboris pariatur laboris ad pariatur in cillum fugiat est fugiat. Proident eu id irure excepteur esse aute cillum adipisicing.','registered':'Wednesday, October 15, 2014 6:17 PM','latitude':'-15.73863','longitude':'87.422009','tags':['consequat','sint','tempor','veniam','culpa'],'greeting':'Hello, Knowles! You have 6 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0eda00b73bdb7ea54e9','index':61,'guid':'c8a064db-0ec6-4832-9820-7280a0333709','isActive':true,'balance':'$3,701.14','picture':'http://placehold.it/32x32','age':35,'eyeColor':'brown','name':{'first':'Shepherd','last':'Todd'},'company':'ECRATIC','email':'shepherd.todd@ecratic.me','phone':'+1 (881) 444-3389','address':'450 Frank Court, Temperanceville, Ohio, 7006','about':'Voluptate cillum ad fugiat velit adipisicing sint consequat veniam Lorem reprehenderit. Cillum sit non deserunt consequat. Amet sunt pariatur non mollit ullamco proident sint dolore anim elit cupidatat anim do ullamco. Lorem Lorem incididunt ea elit consequat laboris enim duis quis Lorem id aute veniam consequat. Cillum veniam cillum sint qui Lorem fugiat culpa consequat. Est sint duis ut qui fugiat. Laborum pariatur velit et sunt mollit eiusmod excepteur culpa ex et officia.','registered':'Tuesday, October 10, 2017 2:01 AM','latitude':'82.951563','longitude':'-4.866954','tags':['eu','qui','proident','esse','ex'],'greeting':'Hello, Shepherd! You have 5 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed0e51d1a7e2d9e559','index':62,'guid':'739c3d38-200d-4531-84d8-4e7c39ae5b8c','isActive':true,'balance':'$3,679.01','picture':'http://placehold.it/32x32','age':31,'eyeColor':'brown','name':{'first':'Rosalyn','last':'Heath'},'company':'ZAYA','email':'rosalyn.heath@zaya.com','phone':'+1 (865) 403-3520','address':'303 Henderson Walk, Hoehne, District Of Columbia, 4306','about':'Sint occaecat nulla mollit sint fugiat eu proident dolor labore consequat. Occaecat tempor excepteur do fugiat incididunt Lorem in ullamco dolore laborum. Cillum mollit aliquip excepteur aliquip sint sunt minim non irure irure. Cillum fugiat aliqua enim dolore. Nulla culpa culpa nostrud ad. Eiusmod culpa proident proident non est cupidatat eu sunt sit incididunt id nisi.','registered':'Wednesday, April 22, 2015 12:35 PM','latitude':'33.628504','longitude':'110.772802','tags':['consequat','ut','ex','labore','consectetur'],'greeting':'Hello, Rosalyn! You have 6 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0edd5274c01d353d0c5','index':63,'guid':'8815fe55-8af1-4708-a62a-d554dbd74a4a','isActive':true,'balance':'$2,126.01','picture':'http://placehold.it/32x32','age':30,'eyeColor':'blue','name':{'first':'Queen','last':'Harper'},'company':'TRI@TRIBALOG','email':'queen.harper@tri@tribalog.org','phone':'+1 (903) 592-3145','address':'926 Heath Place, Wawona, Maine, 7340','about':'Laborum cupidatat commodo aliquip reprehenderit. Excepteur eu labore duis minim minim voluptate aute nostrud deserunt ut velit ullamco. Adipisicing nisi occaecat laborum proident. Id reprehenderit eiusmod cupidatat qui aute consequat amet enim commodo duis non ipsum. Amet ut aliqua magna qui proident mollit aute.','registered':'Saturday, April 9, 2016 5:12 AM','latitude':'51.814216','longitude':'177.348115','tags':['cillum','ut','dolor','do','nisi'],'greeting':'Hello, Queen! You have 10 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed126298b6ce62ed56','index':64,'guid':'001c87fe-182f-450f-903b-2e29a9bb0322','isActive':true,'balance':'$3,578.29','picture':'http://placehold.it/32x32','age':20,'eyeColor':'green','name':{'first':'Pauline','last':'Mills'},'company':'CRUSTATIA','email':'pauline.mills@crustatia.net','phone':'+1 (984) 582-3899','address':'899 Revere Place, Welch, Iowa, 216','about':'Tempor eu exercitation ut id. Deserunt ex reprehenderit veniam nisi. Aute laborum veniam velit dolore ut deserunt Lorem sit esse quis dolor ex do nisi. In dolor tempor officia id. Velit nisi culpa nostrud laborum officia incididunt laborum velit non quis id exercitation exercitation. Anim elit ullamco in enim Lorem culpa aliqua Lorem.','registered':'Monday, June 2, 2014 2:03 PM','latitude':'56.427576','longitude':'172.183669','tags':['pariatur','pariatur','pariatur','fugiat','Lorem'],'greeting':'Hello, Pauline! You have 8 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed3e332ad9e8a178d8','index':65,'guid':'5ad7292b-feef-4a7e-b485-142cadfbe8ea','isActive':false,'balance':'$3,916.54','picture':'http://placehold.it/32x32','age':22,'eyeColor':'brown','name':{'first':'Garrett','last':'Richmond'},'company':'XYQAG','email':'garrett.richmond@xyqag.name','phone':'+1 (952) 584-3794','address':'233 Grove Street, Summerfield, Virginia, 4735','about':'Nostrud quis pariatur occaecat laborum laboris aliqua ut fugiat dolor. Commodo tempor excepteur enim nostrud Lorem. Aute elit nulla labore ad pariatur cupidatat Lorem qui cupidatat velit deserunt excepteur esse. Excepteur nulla et nostrud quis labore est veniam enim nisi laboris ut enim. Ea esse nulla anim excepteur reprehenderit deserunt voluptate minim qui labore adipisicing amet eu enim.','registered':'Wednesday, March 5, 2014 4:35 PM','latitude':'68.665041','longitude':'148.799524','tags':['irure','reprehenderit','minim','ea','do'],'greeting':'Hello, Garrett! You have 7 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed541aa2ec47466ace','index':66,'guid':'9cda6f3c-c9ab-451c-bb19-2e4c8463d011','isActive':true,'balance':'$3,352.52','picture':'http://placehold.it/32x32','age':30,'eyeColor':'brown','name':{'first':'Cobb','last':'Whitley'},'company':'UNIA','email':'cobb.whitley@unia.us','phone':'+1 (888) 490-3342','address':'864 Belmont Avenue, Needmore, Massachusetts, 8286','about':'Nisi aliquip fugiat ipsum nisi ullamco minim pariatur labore. Sint labore anim do ad ad esse eu nostrud nulla commodo anim. Cillum anim enim duis cillum non do nisi aliquip veniam voluptate commodo aliqua laborum. Exercitation in do eu qui sint aliquip. Esse adipisicing deserunt deserunt qui anim aliqua occaecat et nostrud elit ea in anim cillum. Tempor mollit proident tempor sunt est sint laborum ullamco incididunt non. Velit aliqua sunt excepteur nisi qui eiusmod ipsum dolore aliquip velit ullamco ullamco.','registered':'Friday, May 23, 2014 7:11 PM','latitude':'-32.950581','longitude':'147.772494','tags':['mollit','adipisicing','irure','ad','minim'],'greeting':'Hello, Cobb! You have 6 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed8186c3d6f34c2be3','index':67,'guid':'fee98f6d-d68a-4189-8180-b6cb337e537e','isActive':false,'balance':'$1,698.42','picture':'http://placehold.it/32x32','age':20,'eyeColor':'blue','name':{'first':'Brennan','last':'Tyler'},'company':'PODUNK','email':'brennan.tyler@podunk.biz','phone':'+1 (867) 498-2727','address':'599 Harkness Avenue, Gorst, American Samoa, 322','about':'Reprehenderit id sit qui id qui aute ea sit magna in qui proident. Excepteur ad nostrud do nostrud in incididunt voluptate adipisicing sint anim. Ullamco consequat minim nulla irure ex est irure reprehenderit deserunt voluptate dolore anim sunt. Occaecat dolore voluptate voluptate elit commodo nulla laborum ad do irure.','registered':'Friday, February 9, 2018 5:40 PM','latitude':'11.150893','longitude':'-85.298004','tags':['quis','minim','deserunt','cillum','laboris'],'greeting':'Hello, Brennan! You have 10 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed075c9c4f7439818d','index':68,'guid':'1ef76b18-6b8d-4c3c-aca3-9fa2b43f0242','isActive':false,'balance':'$2,091.17','picture':'http://placehold.it/32x32','age':26,'eyeColor':'brown','name':{'first':'Neal','last':'Stephenson'},'company':'OTHERSIDE','email':'neal.stephenson@otherside.ca','phone':'+1 (820) 496-3344','address':'867 Wilson Street, Kidder, Colorado, 4599','about':'Do laboris enim proident in qui velit adipisicing magna anim. Amet proident non exercitation ipsum aliqua excepteur nostrud. Enim esse non sit in nostrud deserunt id laborum cillum deserunt consequat. Anim velit exercitation qui sit voluptate. Irure duis non veniam velit mollit exercitation id exercitation.','registered':'Thursday, November 13, 2014 11:00 PM','latitude':'54.809693','longitude':'1.877241','tags':['anim','duis','in','officia','sint'],'greeting':'Hello, Neal! You have 7 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0eda0a2dc24db64b638','index':69,'guid':'194744fd-089b-40b6-a290-98a6ec30a415','isActive':false,'balance':'$3,191.67','picture':'http://placehold.it/32x32','age':24,'eyeColor':'brown','name':{'first':'Shields','last':'Hubbard'},'company':'MIRACULA','email':'shields.hubbard@miracula.info','phone':'+1 (885) 582-2001','address':'529 Eagle Street, Guilford, Nevada, 1460','about':'Eiusmod exercitation ut incididunt veniam commodo culpa ullamco mollit id adipisicing exercitation ad sint. Nostrud excepteur amet aliqua mollit incididunt laborum voluptate id anim. Nulla sint laboris dolor esse cupidatat laborum ex sint. Ex non sunt sit nulla.','registered':'Monday, February 13, 2017 6:22 AM','latitude':'-69.145209','longitude':'-40.69755','tags':['tempor','enim','qui','velit','elit'],'greeting':'Hello, Shields! You have 10 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0edf939c130177e074d','index':70,'guid':'303b176c-7803-4ed2-a35f-3e3c831793ef','isActive':false,'balance':'$2,359.09','picture':'http://placehold.it/32x32','age':31,'eyeColor':'blue','name':{'first':'Coleen','last':'Knight'},'company':'BLEEKO','email':'coleen.knight@bleeko.tv','phone':'+1 (867) 423-3146','address':'527 Broadway , Bonanza, Marshall Islands, 4988','about':'Laboris nulla pariatur laborum ad aute excepteur sunt pariatur exercitation. Do nostrud qui ipsum ullamco et sint do Lorem cillum ullamco do. Exercitation labore excepteur commodo incididunt eiusmod proident consectetur adipisicing nostrud aute voluptate laboris. Commodo anim proident eiusmod pariatur est ea laborum incididunt qui tempor reprehenderit ullamco id. Eiusmod commodo nisi consectetur ut qui quis aliqua sit minim nostrud sunt laborum eiusmod adipisicing.','registered':'Sunday, May 6, 2018 8:03 AM','latitude':'70.729041','longitude':'113.052761','tags':['Lorem','ullamco','nulla','ullamco','commodo'],'greeting':'Hello, Coleen! You have 7 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0edae8b1ce688b61223','index':71,'guid':'7d6f3b1a-c367-4068-9e8e-1717d513ece3','isActive':false,'balance':'$2,911.07','picture':'http://placehold.it/32x32','age':21,'eyeColor':'brown','name':{'first':'Clark','last':'Ryan'},'company':'ECLIPSENT','email':'clark.ryan@eclipsent.co.uk','phone':'+1 (938) 562-2740','address':'500 Lewis Avenue, Rockbridge, North Dakota, 5133','about':'Adipisicing exercitation officia sit excepteur excepteur sunt sint amet. Aliqua ipsum sint laboris eiusmod esse culpa elit sunt. Dolore est consectetur est quis quis magna. Aliquip nostrud dolore ex pariatur. Anim nostrud duis exercitation ut magna magna culpa. Nisi irure id mollit labore non sit mollit occaecat Lorem est ipsum. Nulla est fugiat cillum nisi aliqua consectetur amet nulla nostrud esse.','registered':'Friday, July 24, 2015 9:28 AM','latitude':'-68.055815','longitude':'-50.926966','tags':['deserunt','ad','ad','ut','id'],'greeting':'Hello, Clark! You have 7 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0ed5d1e8df45d8ab4db','index':72,'guid':'ce85db37-7d04-4f4c-a4b0-78003533e5c6','isActive':false,'balance':'$1,127.43','picture':'http://placehold.it/32x32','age':21,'eyeColor':'green','name':{'first':'Dillon','last':'Hooper'},'company':'MEDESIGN','email':'dillon.hooper@medesign.io','phone':'+1 (929) 600-3797','address':'652 Mill Avenue, Elliston, Mississippi, 2958','about':'Dolore culpa qui exercitation nostrud do. Irure duis in ad ipsum aliqua aliquip nulla sit veniam officia quis occaecat est. Magna qui eiusmod pariatur aliquip minim commodo. Qui ex dolor excepteur consequat eiusmod occaecat. In officia ipsum do Lorem excepteur proident pariatur labore.','registered':'Monday, May 26, 2014 2:38 AM','latitude':'-36.032189','longitude':'86.865529','tags':['non','ut','ex','Lorem','quis'],'greeting':'Hello, Dillon! You have 10 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0edb84814579c3121b3','index':73,'guid':'d7303901-5186-4595-a759-22306f67d0a3','isActive':true,'balance':'$2,326.59','picture':'http://placehold.it/32x32','age':33,'eyeColor':'green','name':{'first':'Moreno','last':'Hull'},'company':'ZEAM','email':'moreno.hull@zeam.me','phone':'+1 (984) 586-3738','address':'265 Pine Street, Talpa, North Carolina, 6041','about':'Fugiat exercitation est ullamco anim. Exercitation proident id sunt culpa Lorem amet. Consectetur anim consectetur pariatur consequat consectetur amet excepteur voluptate ea velit duis eiusmod proident. In sint laborum cupidatat ea amet ex. Reprehenderit amet sunt dolor ullamco est ex deserunt.','registered':'Wednesday, January 24, 2018 8:52 PM','latitude':'84.956857','longitude':'113.210051','tags':['est','excepteur','anim','Lorem','dolor'],'greeting':'Hello, Moreno! You have 6 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0eda4eb9dcb92c82d06','index':74,'guid':'8ee28651-802e-4523-b676-c713f6e874b8','isActive':true,'balance':'$3,783.97','picture':'http://placehold.it/32x32','age':38,'eyeColor':'blue','name':{'first':'Tracie','last':'Price'},'company':'ICOLOGY','email':'tracie.price@icology.com','phone':'+1 (897) 403-3768','address':'487 Sheffield Avenue, Vallonia, Wyoming, 276','about':'Voluptate laboris laborum aute ex sint voluptate officia proident. Sit esse nostrud cupidatat in veniam sit duis est. Do mollit elit exercitation aliqua id irure ex. Lorem reprehenderit do ullamco sint ea ad nisi ad ut.','registered':'Saturday, December 10, 2016 9:44 AM','latitude':'77.770464','longitude':'151.392903','tags':['incididunt','labore','aliquip','anim','minim'],'greeting':'Hello, Tracie! You have 6 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed68ab1a55d1c35e6c','index':75,'guid':'deedd26a-8928-4064-9666-5c59ea8144b4','isActive':true,'balance':'$2,848.08','picture':'http://placehold.it/32x32','age':32,'eyeColor':'brown','name':{'first':'Montgomery','last':'Bruce'},'company':'CYTREK','email':'montgomery.bruce@cytrek.org','phone':'+1 (824) 414-2731','address':'397 Beach Place, Ellerslie, South Carolina, 967','about':'Mollit minim excepteur magna velit cillum excepteur exercitation anim id labore deserunt do. Fugiat ex et id ad. Duis excepteur laboris est nulla do id irure quis eiusmod do esse ut culpa in.','registered':'Tuesday, August 25, 2015 6:42 AM','latitude':'79.722631','longitude':'-7.516885','tags':['Lorem','sint','voluptate','proident','incididunt'],'greeting':'Hello, Montgomery! You have 6 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0edd90e0abb1cc2b0aa','index':76,'guid':'a072159d-12db-4747-9c2a-e2486a53d043','isActive':false,'balance':'$2,723.54','picture':'http://placehold.it/32x32','age':40,'eyeColor':'green','name':{'first':'Zelma','last':'Salinas'},'company':'IMAGEFLOW','email':'zelma.salinas@imageflow.net','phone':'+1 (964) 555-3856','address':'584 Reeve Place, Nord, Georgia, 7473','about':'Aliqua proident excepteur duis cupidatat cillum amet esse esse consectetur ea. Officia sunt consequat nostrud minim enim dolore dolor duis cillum. Esse labore veniam sint laborum excepteur sint tempor do ad cupidatat aliquip laboris elit id. Velit reprehenderit ullamco velit ullamco adipisicing velit esse irure velit et.','registered':'Thursday, February 25, 2016 8:18 PM','latitude':'-32.880524','longitude':'115.180489','tags':['id','nulla','reprehenderit','consequat','reprehenderit'],'greeting':'Hello, Zelma! You have 10 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed98d836c8da283bb2','index':77,'guid':'838bebad-cc20-44e9-9eb7-902a8ca25efb','isActive':false,'balance':'$3,488.91','picture':'http://placehold.it/32x32','age':20,'eyeColor':'green','name':{'first':'Shaw','last':'Parsons'},'company':'PEARLESEX','email':'shaw.parsons@pearlesex.name','phone':'+1 (912) 567-3580','address':'606 Ocean Avenue, Tyro, Northern Mariana Islands, 3367','about':'Laborum labore occaecat culpa pariatur nisi non adipisicing esse consectetur officia officia. Deserunt velit eu enim consectetur ut cillum aliqua occaecat dolor qui esse. Incididunt ad est ex eu culpa anim aliquip laborum. Aliqua consectetur velit exercitation magna minim nulla do ut excepteur enim aliquip et. Nostrud enim sunt amet amet proident aliqua velit dolore. Consectetur ipsum fugiat proident id est reprehenderit tempor irure commodo. Sit excepteur fugiat occaecat nulla Lorem et cillum.','registered':'Thursday, April 19, 2018 1:41 AM','latitude':'69.715573','longitude':'-118.481237','tags':['laboris','adipisicing','magna','voluptate','id'],'greeting':'Hello, Shaw! You have 9 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0ed1101734633c6ebba','index':78,'guid':'8fd0c52a-9d74-4984-a608-d612ecd8ddf0','isActive':true,'balance':'$3,820.02','picture':'http://placehold.it/32x32','age':39,'eyeColor':'brown','name':{'first':'Jaime','last':'Beard'},'company':'IZZBY','email':'jaime.beard@izzby.us','phone':'+1 (820) 412-3806','address':'362 Hudson Avenue, Delco, New Jersey, 5684','about':'Ut cupidatat veniam nulla magna commodo sit duis veniam consectetur cupidatat elit quis tempor. Duis officia ullamco proident sunt non mollit excepteur. Nisi ex amet laboris proident duis reprehenderit et est aliqua mollit amet ad. Enim eu elit excepteur eu exercitation duis consequat culpa. Adipisicing reprehenderit duis Lorem reprehenderit dolor aliqua incididunt eiusmod consequat ad occaecat fugiat do laborum. Qui ad aliquip ex do sunt. Fugiat non ut fugiat eu.','registered':'Sunday, March 9, 2014 3:41 PM','latitude':'17.926318','longitude':'108.985996','tags':['ut','voluptate','veniam','non','commodo'],'greeting':'Hello, Jaime! You have 7 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0edcd125a89dcf18e0d','index':79,'guid':'eccaa4ca-0fa7-4b00-a1e3-fe7953403894','isActive':true,'balance':'$1,521.33','picture':'http://placehold.it/32x32','age':30,'eyeColor':'green','name':{'first':'Terra','last':'Sullivan'},'company':'ZANITY','email':'terra.sullivan@zanity.biz','phone':'+1 (995) 498-2714','address':'346 Congress Street, Tuttle, Maryland, 3152','about':'Incididunt enim veniam ut veniam quis dolore pariatur culpa ex. Cillum laboris dolor exercitation officia. Officia irure magna aliqua veniam officia ullamco culpa. Cillum enim velit ea sint sint officia labore ea adipisicing culpa laboris. Anim aute sint commodo culpa ex quis minim ut laborum.','registered':'Sunday, June 1, 2014 5:38 AM','latitude':'-4.655435','longitude':'5.851803','tags':['anim','non','anim','laborum','pariatur'],'greeting':'Hello, Terra! You have 5 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed9b9fc3041a674c87','index':80,'guid':'9f95fa36-4e45-4c3f-9362-3d4d809bf57f','isActive':true,'balance':'$3,403.16','picture':'http://placehold.it/32x32','age':39,'eyeColor':'brown','name':{'first':'Sharpe','last':'Berger'},'company':'ZILLAN','email':'sharpe.berger@zillan.ca','phone':'+1 (913) 498-3005','address':'277 Bragg Street, Faywood, Texas, 6487','about':'Dolor duis id aute ea veniam amet ullamco id. Culpa deserunt irure mollit tempor dolore veniam culpa officia culpa laborum eiusmod. Ullamco tempor qui aliqua cupidatat veniam cillum eu ut ex minim eu in. Quis exercitation anim eiusmod tempor esse mollit exercitation cillum ipsum reprehenderit. Sint voluptate ipsum officia sint magna nulla tempor eiusmod eiusmod veniam. Consectetur non ad veniam exercitation voluptate non nostrud.','registered':'Tuesday, June 27, 2017 12:58 AM','latitude':'-0.54085','longitude':'106.258693','tags':['proident','eiusmod','commodo','excepteur','pariatur'],'greeting':'Hello, Sharpe! You have 5 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0ed1a1866757bf675e0','index':81,'guid':'1b944a01-01d3-4846-94e3-630f4d0e51a3','isActive':true,'balance':'$2,038.61','picture':'http://placehold.it/32x32','age':28,'eyeColor':'brown','name':{'first':'Blanchard','last':'Ewing'},'company':'CONJURICA','email':'blanchard.ewing@conjurica.info','phone':'+1 (859) 593-3212','address':'252 Beaver Street, Kiskimere, Utah, 3255','about':'Labore magna aute adipisicing ut dolor sit ea. Officia culpa aute occaecat sit ex ullamco aliquip ad sit culpa. Ex in enim dolore ex est sit. Do irure nulla magna sint aliquip in duis aute. Magna ullamco sit labore ea tempor voluptate.','registered':'Monday, May 4, 2015 10:50 AM','latitude':'76.207595','longitude':'0.672563','tags':['proident','pariatur','officia','in','culpa'],'greeting':'Hello, Blanchard! You have 9 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0ed987d82f4e22d939c','index':82,'guid':'97a90aee-3cee-4678-819e-24fb94279dc1','isActive':false,'balance':'$1,201.55','picture':'http://placehold.it/32x32','age':28,'eyeColor':'blue','name':{'first':'Wells','last':'Solomon'},'company':'CORPULSE','email':'wells.solomon@corpulse.tv','phone':'+1 (840) 539-3349','address':'159 Radde Place, Linganore, Idaho, 230','about':'Consequat dolore mollit sit irure cupidatat commodo. Incididunt cillum reprehenderit ullamco sit proident cupidatat occaecat reprehenderit officia. Ad anim Lorem elit in officia minim proident nisi commodo eiusmod ea Lorem dolore voluptate. Dolor aliquip est commodo Lorem dolor ut aliquip ut. Sit anim officia dolore excepteur aute enim cillum.','registered':'Friday, January 6, 2017 1:59 PM','latitude':'70.020883','longitude':'14.503588','tags':['mollit','aute','officia','nostrud','laboris'],'greeting':'Hello, Wells! You have 7 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0eddf7a904ea0d0bc2a','index':83,'guid':'fe639a0c-7517-43e6-b0da-cd9ca5b9e267','isActive':false,'balance':'$3,664.47','picture':'http://placehold.it/32x32','age':33,'eyeColor':'blue','name':{'first':'Natalia','last':'Brown'},'company':'SYNTAC','email':'natalia.brown@syntac.co.uk','phone':'+1 (952) 595-3513','address':'332 Lenox Road, Springville, Alabama, 8406','about':'Nulla consequat officia commodo ea sunt irure anim velit aliquip aliquip. Labore ullamco occaecat proident voluptate cillum labore minim nostrud excepteur. Qui fugiat nostrud cillum fugiat ullamco id commodo aliqua voluptate mollit id id laboris. Cillum qui duis duis sit adipisicing elit ut aliqua eu. Anim nisi aliqua sit mollit.','registered':'Sunday, July 30, 2017 1:02 PM','latitude':'31.937613','longitude':'-9.957927','tags':['magna','adipisicing','exercitation','tempor','consectetur'],'greeting':'Hello, Natalia! You have 9 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0ed8823fa385cad4aa3','index':84,'guid':'5cf280da-f5f0-4cc6-9063-e9d5863c8c89','isActive':false,'balance':'$1,624.17','picture':'http://placehold.it/32x32','age':25,'eyeColor':'blue','name':{'first':'Greene','last':'Waller'},'company':'ISOTRACK','email':'greene.waller@isotrack.io','phone':'+1 (838) 406-3608','address':'362 Albemarle Road, Gardiner, Michigan, 2764','about':'Ut nisi sit sint nulla dolor magna. Culpa occaecat adipisicing veniam proident excepteur tempor quis ex. Fugiat tempor laborum dolor adipisicing irure anim cupidatat ut exercitation ex sit. Cupidatat exercitation commodo sunt ex irure fugiat eu esse do ullamco mollit dolore cupidatat. Cupidatat magna incididunt officia dolore esse voluptate deserunt in laborum dolor. Sit fugiat Lorem eu ullamco. Laboris veniam quis cillum tempor ex fugiat cillum cupidatat.','registered':'Sunday, June 10, 2018 10:32 PM','latitude':'0.256921','longitude':'-96.141941','tags':['magna','dolore','deserunt','aliquip','cillum'],'greeting':'Hello, Greene! You have 6 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0eda7c905c2d24c7d31','index':85,'guid':'aa30a9fb-8a16-48eb-8bb7-1307d1e1f191','isActive':false,'balance':'$1,974.04','picture':'http://placehold.it/32x32','age':36,'eyeColor':'green','name':{'first':'Carlene','last':'Hanson'},'company':'DIGIRANG','email':'carlene.hanson@digirang.me','phone':'+1 (981) 417-3209','address':'435 Clark Street, Choctaw, Oregon, 9888','about':'Amet labore esse cillum irure laborum consectetur occaecat non aliquip aliquip proident. Nisi magna nulla officia duis labore aute nulla laborum duis tempor minim. Velit elit reprehenderit nisi exercitation officia incididunt amet cupidatat excepteur proident consectetur.','registered':'Thursday, April 20, 2017 6:13 AM','latitude':'68.529086','longitude':'68.802409','tags':['pariatur','nulla','qui','amet','labore'],'greeting':'Hello, Carlene! You have 10 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed6fbee12ce9e55dbf','index':86,'guid':'0fce89aa-3310-48df-862a-68bd3d776644','isActive':false,'balance':'$3,909.64','picture':'http://placehold.it/32x32','age':40,'eyeColor':'brown','name':{'first':'Doris','last':'Collins'},'company':'ZIORE','email':'doris.collins@ziore.com','phone':'+1 (914) 405-2360','address':'301 Lorraine Street, Stouchsburg, Minnesota, 7476','about':'Nisi deserunt aliquip et deserunt ipsum ad consectetur est non ullamco. Dolore do ut voluptate do eiusmod. Culpa ad in eiusmod nisi cillum do. Officia magna cillum sint aliqua reprehenderit amet est ipsum. Eiusmod deserunt commodo proident consequat. Amet minim dolor consequat aliquip aliquip culpa non exercitation non.','registered':'Wednesday, February 25, 2015 9:15 PM','latitude':'-57.364906','longitude':'130.766587','tags':['nulla','deserunt','cillum','eiusmod','adipisicing'],'greeting':'Hello, Doris! You have 10 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0edede9402476c398c0','index':87,'guid':'60cf0aa6-bc6d-4305-8842-d27e6af1306f','isActive':false,'balance':'$2,817.53','picture':'http://placehold.it/32x32','age':28,'eyeColor':'green','name':{'first':'Cline','last':'Hayden'},'company':'ECRAZE','email':'cline.hayden@ecraze.org','phone':'+1 (965) 507-2138','address':'352 Rutland Road, Ebro, Connecticut, 1196','about':'Dolor eiusmod enim anim sit enim ea tempor. Tempor amet consectetur aliquip culpa do ex excepteur deserunt. Dolor commodo veniam culpa sint. Commodo consectetur pariatur irure nisi deserunt cillum est dolor ipsum ea.','registered':'Thursday, September 29, 2016 5:58 AM','latitude':'62.50713','longitude':'86.247286','tags':['enim','tempor','anim','veniam','proident'],'greeting':'Hello, Cline! You have 9 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0edeb72f151994a551b','index':88,'guid':'dbb49c62-86b1-409f-b8b8-f609c709d2a8','isActive':false,'balance':'$3,122.56','picture':'http://placehold.it/32x32','age':39,'eyeColor':'green','name':{'first':'Janelle','last':'Rutledge'},'company':'TERRAGEN','email':'janelle.rutledge@terragen.net','phone':'+1 (914) 581-3749','address':'170 Falmouth Street, Alderpoint, West Virginia, 642','about':'Laboris proident cillum sunt qui ea sunt. Officia adipisicing exercitation dolore magna reprehenderit amet anim id. Laboris commodo sit irure irure. Excepteur est mollit fugiat incididunt consectetur veniam irure ea mollit. Cillum enim consequat sunt sunt nisi incididunt tempor enim.','registered':'Monday, February 16, 2015 5:46 AM','latitude':'-46.392023','longitude':'32.054562','tags':['eu','eu','nisi','labore','deserunt'],'greeting':'Hello, Janelle! You have 9 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0edc9c2604846ff9a0d','index':89,'guid':'c4d7a365-f1d3-4584-b78e-008394c219f7','isActive':true,'balance':'$1,807.19','picture':'http://placehold.it/32x32','age':24,'eyeColor':'green','name':{'first':'Abby','last':'Lopez'},'company':'GRAINSPOT','email':'abby.lopez@grainspot.name','phone':'+1 (917) 442-3955','address':'488 Kensington Walk, Winston, Hawaii, 9109','about':'Incididunt deserunt Lorem proident magna tempor enim quis duis eu ut adipisicing in. Ex mollit non irure aliqua officia. Fugiat id ipsum consequat irure id ullamco culpa quis nulla enim aliquip consequat et. Dolor ut anim velit irure consequat cillum eu. Aute occaecat laborum est aliqua.','registered':'Sunday, April 1, 2018 11:28 PM','latitude':'-10.177041','longitude':'-165.756718','tags':['est','laborum','culpa','non','quis'],'greeting':'Hello, Abby! You have 9 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed03237438b158af9e','index':90,'guid':'36c4a19f-2d00-4e40-bd49-155fd2ce0a6c','isActive':false,'balance':'$2,757.86','picture':'http://placehold.it/32x32','age':31,'eyeColor':'blue','name':{'first':'Whitney','last':'Sheppard'},'company':'ANACHO','email':'whitney.sheppard@anacho.us','phone':'+1 (922) 437-2383','address':'951 Beekman Place, Homeworth, New York, 6088','about':'Sint minim nisi minim non minim aliqua pariatur ullamco do sint qui labore. Aute elit reprehenderit ad do fugiat est amet. In incididunt tempor commodo cillum tempor est labore anim.','registered':'Tuesday, September 13, 2016 6:43 PM','latitude':'-49.732527','longitude':'-171.846715','tags':['exercitation','veniam','sunt','est','proident'],'greeting':'Hello, Whitney! You have 6 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0edb99dd3aa53d2cb7f','index':91,'guid':'17afd430-f37f-4d55-958c-72f35cdb5997','isActive':false,'balance':'$3,683.86','picture':'http://placehold.it/32x32','age':38,'eyeColor':'blue','name':{'first':'Ilene','last':'Blackwell'},'company':'ENQUILITY','email':'ilene.blackwell@enquility.biz','phone':'+1 (817) 555-2616','address':'950 Varanda Place, Belgreen, Virgin Islands, 1765','about':'Id eiusmod deserunt eiusmod adipisicing adipisicing est enim pariatur esse duis. Qui velit duis irure magna consectetur dolore reprehenderit. Cillum dolore minim consectetur irure non qui velit cillum veniam adipisicing incididunt. Deserunt veniam excepteur veniam velit aliquip labore quis exercitation magna do non dolor. Aliquip occaecat minim adipisicing deserunt fugiat nulla occaecat proident irure consectetur eiusmod irure. Enim Lorem deserunt amet Lorem commodo eiusmod reprehenderit occaecat adipisicing dolor voluptate cillum.','registered':'Thursday, February 1, 2018 8:39 AM','latitude':'57.393644','longitude':'-3.704258','tags':['adipisicing','dolor','commodo','Lorem','Lorem'],'greeting':'Hello, Ilene! You have 6 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed353f4deb62c3342a','index':92,'guid':'9953e285-2095-4f1c-978b-9ece2a867e9d','isActive':false,'balance':'$1,202.44','picture':'http://placehold.it/32x32','age':38,'eyeColor':'blue','name':{'first':'Dawson','last':'Herman'},'company':'BITENDREX','email':'dawson.herman@bitendrex.ca','phone':'+1 (843) 522-2655','address':'471 Channel Avenue, Denio, Alaska, 5040','about':'Nisi occaecat mollit reprehenderit nisi minim Lorem mollit. Ea proident irure cillum quis. Deserunt consectetur consectetur consequat quis enim minim ea ipsum proident nisi ad non aliquip. Veniam aute minim consequat irure voluptate aute amet excepteur exercitation cillum duis quis adipisicing nostrud.','registered':'Tuesday, December 8, 2015 5:40 PM','latitude':'-55.602721','longitude':'-26.683234','tags':['qui','dolor','deserunt','eiusmod','labore'],'greeting':'Hello, Dawson! You have 7 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0edd5464bc50a5310ad','index':93,'guid':'724b2434-4dbd-417d-aa07-6065715f434f','isActive':false,'balance':'$1,595.98','picture':'http://placehold.it/32x32','age':25,'eyeColor':'brown','name':{'first':'Alice','last':'Christian'},'company':'ZENOLUX','email':'alice.christian@zenolux.info','phone':'+1 (954) 466-2650','address':'875 Gerritsen Avenue, Townsend, Kentucky, 6568','about':'Nulla labore occaecat ex culpa magna. Commodo occaecat et in consequat cillum laborum magna adipisicing excepteur. Do ut Lorem esse voluptate officia ea aliquip proident amet veniam minim nulla adipisicing. Enim consectetur incididunt laborum voluptate tempor deserunt non laboris. Aliquip deserunt aute irure dolore magna anim aliquip sint magna Lorem. Officia laboris nulla officia sint labore nisi. Do Lorem id in est esse adipisicing id fugiat enim esse laborum.','registered':'Wednesday, October 3, 2018 9:26 PM','latitude':'-88.790637','longitude':'138.817328','tags':['duis','ea','magna','ea','incididunt'],'greeting':'Hello, Alice! You have 8 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0eda01886247b6a4f3d','index':94,'guid':'17c9f4d3-7d72-44e3-8f7c-08d7de920f46','isActive':false,'balance':'$3,173.29','picture':'http://placehold.it/32x32','age':31,'eyeColor':'blue','name':{'first':'Schwartz','last':'Mccormick'},'company':'EVIDENDS','email':'schwartz.mccormick@evidends.tv','phone':'+1 (924) 531-2802','address':'160 Midwood Street, Indio, Palau, 4241','about':'Anim reprehenderit et et adipisicing voluptate consequat elit. Sint Lorem laboris Lorem minim nostrud aute reprehenderit elit aute quis nulla. Officia aute eiusmod mollit cillum eu aliquip non enim ea occaecat quis fugiat occaecat officia. Eiusmod culpa exercitation dolor aliqua enim occaecat nisi cupidatat duis ex dolore id. Id consequat aliqua cupidatat ut. Sit nisi est sunt culpa ullamco excepteur sunt pariatur incididunt amet. Ut tempor duis velit eu ut id culpa aute anim occaecat labore.','registered':'Thursday, March 2, 2017 5:57 PM','latitude':'38.618587','longitude':'-165.142529','tags':['ad','reprehenderit','magna','elit','mollit'],'greeting':'Hello, Schwartz! You have 10 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0ed51be4df456ec2bc9','index':95,'guid':'44f68f65-959b-4ec2-bd2a-1f30035f76fc','isActive':false,'balance':'$3,242.24','picture':'http://placehold.it/32x32','age':39,'eyeColor':'blue','name':{'first':'Bonita','last':'Stevens'},'company':'SLOFAST','email':'bonita.stevens@slofast.co.uk','phone':'+1 (886) 473-2105','address':'459 Bushwick Court, Kilbourne, Rhode Island, 9450','about':'Consequat reprehenderit qui reprehenderit nisi sit est in qui aliquip amet. Ex deserunt cupidatat amet cillum eiusmod irure anim in amet proident voluptate. Ad officia culpa in non incididunt do.','registered':'Saturday, August 22, 2015 5:23 AM','latitude':'60.013542','longitude':'58.242132','tags':['aute','adipisicing','in','cillum','officia'],'greeting':'Hello, Bonita! You have 5 unread messages.','favoriteFruit':'banana'},{'_id':'5c5ab0ed50a55e3587993f68','index':96,'guid':'652e434f-221e-4899-af12-38dca5c9621d','isActive':false,'balance':'$2,720.06','picture':'http://placehold.it/32x32','age':28,'eyeColor':'green','name':{'first':'Charmaine','last':'Jackson'},'company':'FLUM','email':'charmaine.jackson@flum.io','phone':'+1 (947) 573-2692','address':'788 Windsor Place, Highland, Arkansas, 8869','about':'Dolore reprehenderit irure excepteur eu reprehenderit sint Lorem ut amet in. Consequat anim elit sunt aliquip incididunt. Culpa consequat do exercitation dolor enim dolor sunt sit excepteur ad anim. Dolor aute elit velit mollit minim eu.','registered':'Wednesday, April 6, 2016 7:54 PM','latitude':'25.756553','longitude':'-5.482531','tags':['amet','sint','consequat','est','ex'],'greeting':'Hello, Charmaine! You have 10 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed213621949bbdd5d3','index':97,'guid':'7d7d93d8-3e37-4b4a-9fa2-591fb7d153ce','isActive':true,'balance':'$1,370.63','picture':'http://placehold.it/32x32','age':36,'eyeColor':'brown','name':{'first':'Petersen','last':'Cooley'},'company':'ROTODYNE','email':'petersen.cooley@rotodyne.me','phone':'+1 (929) 563-3339','address':'338 Pioneer Street, Carbonville, Missouri, 3030','about':'Cillum elit dolore labore aute. Cillum ea incididunt cupidatat consequat sint eu mollit. Excepteur commodo eiusmod ex Lorem enim velit minim.','registered':'Friday, December 8, 2017 5:53 AM','latitude':'-10.576254','longitude':'-111.176861','tags':['veniam','eu','eiusmod','dolore','voluptate'],'greeting':'Hello, Petersen! You have 9 unread messages.','favoriteFruit':'apple'},{'_id':'5c5ab0ed3e938138d58ed453','index':98,'guid':'d6fea4a3-03f6-46ee-90b9-8ec51a585e29','isActive':true,'balance':'$1,216.54','picture':'http://placehold.it/32x32','age':39,'eyeColor':'blue','name':{'first':'Rosanne','last':'Terry'},'company':'EXTREMO','email':'rosanne.terry@extremo.com','phone':'+1 (812) 496-2691','address':'368 Rockaway Avenue, Gloucester, Illinois, 7913','about':'Duis et nostrud duis quis minim eiusmod culpa do ea ad pariatur tempor. Velit veniam aliqua aliquip est enim ex et culpa dolor ullamco culpa officia. Eu id occaecat aute cillum aute sit aute laboris ipsum voluptate ex. Amet tempor minim tempor Lorem quis dolore. Pariatur consequat dolore nulla veniam dolor exercitation consequat nulla laboris incididunt do. Dolore do tempor deserunt exercitation incididunt officia incididunt ut do reprehenderit do eiusmod nulla.','registered':'Sunday, August 6, 2017 12:46 PM','latitude':'-43.257964','longitude':'-45.147686','tags':['et','incididunt','esse','commodo','ipsum'],'greeting':'Hello, Rosanne! You have 6 unread messages.','favoriteFruit':'strawberry'},{'_id':'5c5ab0ed632b1a1d65501d6b','index':99,'guid':'bf8c6ac1-ee18-48ee-ae94-ea515a53c951','isActive':true,'balance':'$2,905.58','picture':'http://placehold.it/32x32','age':21,'eyeColor':'blue','name':{'first':'Irene','last':'Castro'},'company':'POLARIA','email':'irene.castro@polaria.org','phone':'+1 (818) 417-3761','address':'901 Dupont Street, Sperryville, Oklahoma, 953','about':'Pariatur minim laboris aliqua dolor aliquip consequat ea do duis voluptate id Lorem. In reprehenderit et adipisicing anim elit incididunt velit in laborum laborum. Qui minim magna et amet sit do voluptate reprehenderit ea sit sint velit.','registered':'Tuesday, August 18, 2015 10:48 AM','latitude':'-7.004055','longitude':'116.052433','tags':['sit','proident','enim','ullamco','non'],'greeting':'Hello, Irene! You have 10 unread messages.','favoriteFruit':'apple'}]" + + +Plan: 1 to add, 0 to change, 0 to destroy. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-no-changes/plan.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-no-changes/plan.log new file mode 100644 index 00000000..70416815 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-no-changes/plan.log @@ -0,0 +1,17 @@ +Terraform v0.11.7 + +Configuring remote state backend... +Initializing Terraform configuration... +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + +null_resource.hello: Refreshing state... (ID: 8657651096157629581) + +------------------------------------------------------------------------ + +No changes. Infrastructure is up-to-date. + +This means that Terraform did not detect any differences between your +configuration and real physical resources that exist. As a result, no +actions need to be performed. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-no-changes/policy.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-no-changes/policy.log new file mode 100644 index 00000000..b0cb1e59 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-no-changes/policy.log @@ -0,0 +1,12 @@ +Sentinel Result: true + +This result means that Sentinel policies returned true and the protected +behavior is allowed by Sentinel policies. + +1 policies evaluated. + +## Policy 1: Passthrough.sentinel (soft-mandatory) + +Result: true + +TRUE - Passthrough.sentinel:1:1 - Rule "main" diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-policy-hard-failed/plan.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-policy-hard-failed/plan.log new file mode 100644 index 00000000..5849e575 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-policy-hard-failed/plan.log @@ -0,0 +1,21 @@ +Terraform v0.11.7 + +Configuring remote state backend... +Initializing Terraform configuration... +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + +------------------------------------------------------------------------ + +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + + null_resource.foo + id: + + +Plan: 1 to add, 0 to change, 0 to destroy. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-policy-hard-failed/policy.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-policy-hard-failed/policy.log new file mode 100644 index 00000000..5d6e6935 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-policy-hard-failed/policy.log @@ -0,0 +1,12 @@ +Sentinel Result: false + +Sentinel evaluated to false because one or more Sentinel policies evaluated +to false. This false was not due to an undefined value or runtime error. + +1 policies evaluated. + +## Policy 1: Passthrough.sentinel (hard-mandatory) + +Result: false + +FALSE - Passthrough.sentinel:1:1 - Rule "main" diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-policy-passed/plan.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-policy-passed/plan.log new file mode 100644 index 00000000..5849e575 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-policy-passed/plan.log @@ -0,0 +1,21 @@ +Terraform v0.11.7 + +Configuring remote state backend... +Initializing Terraform configuration... +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + +------------------------------------------------------------------------ + +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + + null_resource.foo + id: + + +Plan: 1 to add, 0 to change, 0 to destroy. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-policy-passed/policy.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-policy-passed/policy.log new file mode 100644 index 00000000..b0cb1e59 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-policy-passed/policy.log @@ -0,0 +1,12 @@ +Sentinel Result: true + +This result means that Sentinel policies returned true and the protected +behavior is allowed by Sentinel policies. + +1 policies evaluated. + +## Policy 1: Passthrough.sentinel (soft-mandatory) + +Result: true + +TRUE - Passthrough.sentinel:1:1 - Rule "main" diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-policy-soft-failed/plan.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-policy-soft-failed/plan.log new file mode 100644 index 00000000..5849e575 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-policy-soft-failed/plan.log @@ -0,0 +1,21 @@ +Terraform v0.11.7 + +Configuring remote state backend... +Initializing Terraform configuration... +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + +------------------------------------------------------------------------ + +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + + null_resource.foo + id: + + +Plan: 1 to add, 0 to change, 0 to destroy. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-policy-soft-failed/policy.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-policy-soft-failed/policy.log new file mode 100644 index 00000000..3e4ebedf --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-policy-soft-failed/policy.log @@ -0,0 +1,12 @@ +Sentinel Result: false + +Sentinel evaluated to false because one or more Sentinel policies evaluated +to false. This false was not due to an undefined value or runtime error. + +1 policies evaluated. + +## Policy 1: Passthrough.sentinel (soft-mandatory) + +Result: false + +FALSE - Passthrough.sentinel:1:1 - Rule "main" diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-variables/plan.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-variables/plan.log new file mode 100644 index 00000000..5849e575 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-variables/plan.log @@ -0,0 +1,21 @@ +Terraform v0.11.7 + +Configuring remote state backend... +Initializing Terraform configuration... +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + +------------------------------------------------------------------------ + +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + + null_resource.foo + id: + + +Plan: 1 to add, 0 to change, 0 to destroy. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-with-error/plan.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-with-error/plan.log new file mode 100644 index 00000000..4344a372 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-with-error/plan.log @@ -0,0 +1,10 @@ +Terraform v0.11.7 + +Configuring remote state backend... +Initializing Terraform configuration... + +Error: null_resource.foo: 1 error(s) occurred: + +* null_resource.foo: 1:3: unknown function called: guid in: + +${guid()} diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-with-working-directory/terraform/plan.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-with-working-directory/terraform/plan.log new file mode 100644 index 00000000..5849e575 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan-with-working-directory/terraform/plan.log @@ -0,0 +1,21 @@ +Terraform v0.11.7 + +Configuring remote state backend... +Initializing Terraform configuration... +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + +------------------------------------------------------------------------ + +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + + null_resource.foo + id: + + +Plan: 1 to add, 0 to change, 0 to destroy. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan/plan.log b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan/plan.log new file mode 100644 index 00000000..5849e575 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testdata/plan/plan.log @@ -0,0 +1,21 @@ +Terraform v0.11.7 + +Configuring remote state backend... +Initializing Terraform configuration... +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + +------------------------------------------------------------------------ + +An execution plan has been generated and is shown below. +Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + + null_resource.foo + id: + + +Plan: 1 to add, 0 to change, 0 to destroy. diff --git a/vendor/github.com/hashicorp/terraform/backend/remote/testing.go b/vendor/github.com/hashicorp/terraform/backend/remote/testing.go index 9f152b5d..82e201e3 100644 --- a/vendor/github.com/hashicorp/terraform/backend/remote/testing.go +++ b/vendor/github.com/hashicorp/terraform/backend/remote/testing.go @@ -126,13 +126,6 @@ func testBackend(t *testing.T, obj cty.Value) (*Remote, func()) { b.client.Variables = mc.Variables b.client.Workspaces = mc.Workspaces - b.ShowDiagnostics = func(vals ...interface{}) { - var diags tfdiags.Diagnostics - for _, diag := range diags.Append(vals...) { - b.CLI.Error(diag.Description().Summary) - } - } - // Set local to a local test backend. b.local = testLocalBackend(t, b) @@ -162,9 +155,6 @@ func testBackend(t *testing.T, obj cty.Value) (*Remote, func()) { func testLocalBackend(t *testing.T, remote *Remote) backend.Enhanced { b := backendLocal.NewWithBackend(remote) - b.CLI = remote.CLI - b.ShowDiagnostics = remote.ShowDiagnostics - // Add a test provider to the local backend. p := backendLocal.TestLocalProvider(t, b, "null", &terraform.ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ @@ -175,7 +165,7 @@ func testLocalBackend(t *testing.T, remote *Remote) backend.Enhanced { }, }, }) - p.ApplyResourceChangeResponse = providers.ApplyResourceChangeResponse{NewState: cty.ObjectVal(map[string]cty.Value{ + p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{NewState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("yes"), })} diff --git a/vendor/github.com/hashicorp/terraform/backend/testing.go b/vendor/github.com/hashicorp/terraform/backend/testing.go index b1d78ea6..6a4c134c 100644 --- a/vendor/github.com/hashicorp/terraform/backend/testing.go +++ b/vendor/github.com/hashicorp/terraform/backend/testing.go @@ -39,7 +39,7 @@ func TestBackendConfig(t *testing.T, b Backend, c hcl.Body) Backend { diags = diags.Append(decDiags) newObj, valDiags := b.PrepareConfig(obj) - diags = diags.Append(valDiags.InConfigBody(c)) + diags = diags.Append(valDiags.InConfigBody(c, "")) if len(diags) != 0 { t.Fatal(diags.ErrWithWarnings()) @@ -49,7 +49,7 @@ func TestBackendConfig(t *testing.T, b Backend, c hcl.Body) Backend { confDiags := b.Configure(obj) if len(confDiags) != 0 { - confDiags = confDiags.InConfigBody(c) + confDiags = confDiags.InConfigBody(c, "") t.Fatal(confDiags.ErrWithWarnings()) } @@ -279,7 +279,27 @@ func TestBackendStateForceUnlock(t *testing.T, b1, b2 Backend) { testLocks(t, b1, b2, true) } +// TestBackendStateLocksInWS will test the locking functionality of the remote +// state backend. +func TestBackendStateLocksInWS(t *testing.T, b1, b2 Backend, ws string) { + t.Helper() + testLocksInWorkspace(t, b1, b2, false, ws) +} + +// TestBackendStateForceUnlockInWS verifies that the lock error is the expected +// type, and the lock can be unlocked using the ID reported in the error. +// Remote state backends that support -force-unlock should call this in at +// least one of the acceptance tests. +func TestBackendStateForceUnlockInWS(t *testing.T, b1, b2 Backend, ws string) { + t.Helper() + testLocksInWorkspace(t, b1, b2, true, ws) +} + func testLocks(t *testing.T, b1, b2 Backend, testForceUnlock bool) { + testLocksInWorkspace(t, b1, b2, testForceUnlock, DefaultStateName) +} + +func testLocksInWorkspace(t *testing.T, b1, b2 Backend, testForceUnlock bool, workspace string) { t.Helper() // Get the default state for each diff --git a/vendor/github.com/hashicorp/terraform/backend/unparsed_value.go b/vendor/github.com/hashicorp/terraform/backend/unparsed_value.go index 65a05c82..22bc8136 100644 --- a/vendor/github.com/hashicorp/terraform/backend/unparsed_value.go +++ b/vendor/github.com/hashicorp/terraform/backend/unparsed_value.go @@ -69,21 +69,15 @@ func ParseVariableValues(vv map[string]UnparsedVariableValue, decls map[string]* if !declared { switch val.SourceType { case terraform.ValueFromConfig, terraform.ValueFromAutoFile, terraform.ValueFromNamedFile: - // These source types have source ranges, so we can produce - // a nice error message with good context. - // - // This one is a warning for now because there is an existing - // pattern of providing a file containing the superset of - // variables across all configurations in an organization. This - // is deprecated in v0.12.0 because it's more important to give - // feedback to users who make typos. Those using this approach - // should migrate to using environment variables instead before - // this becomes an error in a future major release. - if seenUndeclaredInFile < 3 { + // We allow undeclared names for variable values from files and warn in case + // users have forgotten a variable {} declaration or have a typo in their var name. + // Some users will actively ignore this warning because they use a .tfvars file + // across multiple configurations. + if seenUndeclaredInFile < 2 { diags = diags.Append(tfdiags.Sourceless( tfdiags.Warning, "Value for undeclared variable", - fmt.Sprintf("The root module does not declare a variable named %q but a value was found in file %q. To use this value, add a \"variable\" block to the configuration.\n\nUsing a variables file to set an undeclared variable is deprecated and will become an error in a future release. If you wish to provide certain \"global\" settings to all configurations in your organization, use TF_VAR_... environment variables to set these instead.", name, val.SourceRange.Filename), + fmt.Sprintf("The root module does not declare a variable named %q but a value was found in file %q. If you meant to use this value, add a \"variable\" block to the configuration.\n\nTo silence these warnings, use TF_VAR_... environment variables to provide certain \"global\" settings to all configurations in your organization. To reduce the verbosity of these warnings, use the -compact-warnings option.", name, val.SourceRange.Filename), )) } seenUndeclaredInFile++ @@ -114,7 +108,7 @@ func ParseVariableValues(vv map[string]UnparsedVariableValue, decls map[string]* ret[name] = val } - if seenUndeclaredInFile >= 3 { + if seenUndeclaredInFile > 2 { extras := seenUndeclaredInFile - 2 diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagWarning, diff --git a/vendor/github.com/hashicorp/terraform/backend/unparsed_value_test.go b/vendor/github.com/hashicorp/terraform/backend/unparsed_value_test.go index 27fba625..0c37adbc 100644 --- a/vendor/github.com/hashicorp/terraform/backend/unparsed_value_test.go +++ b/vendor/github.com/hashicorp/terraform/backend/unparsed_value_test.go @@ -1,6 +1,7 @@ package backend import ( + "strings" "testing" "github.com/google/go-cmp/cmp" @@ -59,7 +60,7 @@ func TestParseVariableValuesUndeclared(t *testing.T) { for _, diag := range diags { t.Logf("%s: %s", diag.Description().Summary, diag.Description().Detail) } - if got, want := len(diags), 5; got != want { + if got, want := len(diags), 4; got != want { t.Fatalf("wrong number of diagnostics %d; want %d", got, want) } @@ -73,14 +74,14 @@ func TestParseVariableValuesUndeclared(t *testing.T) { if got, want := diags[1].Description().Summary, undeclSingular; got != want { t.Errorf("wrong summary for diagnostic 1\ngot: %s\nwant: %s", got, want) } - if got, want := diags[2].Description().Summary, undeclSingular; got != want { + if got, want := diags[2].Description().Summary, undeclPlural; got != want { t.Errorf("wrong summary for diagnostic 2\ngot: %s\nwant: %s", got, want) } - if got, want := diags[3].Description().Summary, undeclPlural; got != want { - t.Errorf("wrong summary for diagnostic 3\ngot: %s\nwant: %s", got, want) + if got, want := diags[2].Description().Detail, "3 other variable(s)"; !strings.Contains(got, want) { + t.Errorf("wrong detail for diagnostic 2\ngot: %s\nmust contain: %s", got, want) } - if got, want := diags[4].Description().Summary, missingRequired; got != want { - t.Errorf("wrong summary for diagnostic 4\ngot: %s\nwant: %s", got, want) + if got, want := diags[3].Description().Summary, missingRequired; got != want { + t.Errorf("wrong summary for diagnostic 3\ngot: %s\nwant: %s", got, want) } wantVals := terraform.InputValues{ diff --git a/vendor/github.com/hashicorp/terraform/builtin/bins/provider-test/main.go b/vendor/github.com/hashicorp/terraform/builtin/bins/provider-test/main.go deleted file mode 100644 index 97d03f25..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/bins/provider-test/main.go +++ /dev/null @@ -1,15 +0,0 @@ -package main - -import ( - "github.com/hashicorp/terraform/builtin/providers/test" - "github.com/hashicorp/terraform/plugin" - "github.com/hashicorp/terraform/terraform" -) - -func main() { - plugin.Serve(&plugin.ServeOpts{ - ProviderFunc: func() terraform.ResourceProvider { - return test.Provider() - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-chef/main.go b/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-chef/main.go deleted file mode 100644 index 6e81fde4..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-chef/main.go +++ /dev/null @@ -1,12 +0,0 @@ -package main - -import ( - "github.com/hashicorp/terraform/builtin/provisioners/chef" - "github.com/hashicorp/terraform/plugin" -) - -func main() { - plugin.Serve(&plugin.ServeOpts{ - ProvisionerFunc: chef.Provisioner, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-file/main.go b/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-file/main.go deleted file mode 100644 index c0982b0b..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-file/main.go +++ /dev/null @@ -1,12 +0,0 @@ -package main - -import ( - "github.com/hashicorp/terraform/builtin/provisioners/file" - "github.com/hashicorp/terraform/plugin" -) - -func main() { - plugin.Serve(&plugin.ServeOpts{ - ProvisionerFunc: file.Provisioner, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-habitat/main.go b/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-habitat/main.go deleted file mode 100644 index 0311b4f2..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-habitat/main.go +++ /dev/null @@ -1,12 +0,0 @@ -package main - -import ( - "github.com/hashicorp/terraform/builtin/provisioners/habitat" - "github.com/hashicorp/terraform/plugin" -) - -func main() { - plugin.Serve(&plugin.ServeOpts{ - ProvisionerFunc: habitat.Provisioner, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-local-exec/main.go b/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-local-exec/main.go deleted file mode 100644 index 2e0433ff..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-local-exec/main.go +++ /dev/null @@ -1,12 +0,0 @@ -package main - -import ( - "github.com/hashicorp/terraform/builtin/provisioners/local-exec" - "github.com/hashicorp/terraform/plugin" -) - -func main() { - plugin.Serve(&plugin.ServeOpts{ - ProvisionerFunc: localexec.Provisioner, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-puppet/main.go b/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-puppet/main.go deleted file mode 100644 index 63797cb2..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-puppet/main.go +++ /dev/null @@ -1,12 +0,0 @@ -package main - -import ( - "github.com/hashicorp/terraform/builtin/provisioners/puppet" - "github.com/hashicorp/terraform/plugin" -) - -func main() { - plugin.Serve(&plugin.ServeOpts{ - ProvisionerFunc: puppet.Provisioner, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-remote-exec/main.go b/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-remote-exec/main.go deleted file mode 100644 index 83ba43a9..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-remote-exec/main.go +++ /dev/null @@ -1,12 +0,0 @@ -package main - -import ( - "github.com/hashicorp/terraform/builtin/provisioners/remote-exec" - "github.com/hashicorp/terraform/plugin" -) - -func main() { - plugin.Serve(&plugin.ServeOpts{ - ProvisionerFunc: remoteexec.Provisioner, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-salt-masterless/main.go b/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-salt-masterless/main.go deleted file mode 100644 index b7d68341..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/bins/provisioner-salt-masterless/main.go +++ /dev/null @@ -1,12 +0,0 @@ -package main - -import ( - "github.com/hashicorp/terraform/builtin/provisioners/salt-masterless" - "github.com/hashicorp/terraform/plugin" -) - -func main() { - plugin.Serve(&plugin.ServeOpts{ - ProvisionerFunc: saltmasterless.Provisioner, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/terraform/data_source_state.go b/vendor/github.com/hashicorp/terraform/builtin/providers/terraform/data_source_state.go index 3cec518a..f2fdd512 100644 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/terraform/data_source_state.go +++ b/vendor/github.com/hashicorp/terraform/builtin/providers/terraform/data_source_state.go @@ -5,6 +5,7 @@ import ( "log" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/backend/remote" "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/tfdiags" @@ -18,24 +19,42 @@ func dataSourceRemoteStateGetSchema() providers.Schema { Block: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "backend": { - Type: cty.String, - Required: true, + Type: cty.String, + Description: "The remote backend to use, e.g. `remote` or `http`.", + DescriptionKind: configschema.StringMarkdown, + Required: true, }, "config": { - Type: cty.DynamicPseudoType, - Optional: true, + Type: cty.DynamicPseudoType, + Description: "The configuration of the remote backend. " + + "Although this is optional, most backends require " + + "some configuration.\n\n" + + "The object can use any arguments that would be valid " + + "in the equivalent `terraform { backend \"\" { ... } }` " + + "block.", + DescriptionKind: configschema.StringMarkdown, + Optional: true, }, "defaults": { - Type: cty.DynamicPseudoType, - Optional: true, + Type: cty.DynamicPseudoType, + Description: "Default values for outputs, in case " + + "the state file is empty or lacks a required output.", + DescriptionKind: configschema.StringMarkdown, + Optional: true, }, "outputs": { - Type: cty.DynamicPseudoType, - Computed: true, + Type: cty.DynamicPseudoType, + Description: "An object containing every root-level " + + "output in the remote state.", + DescriptionKind: configschema.StringMarkdown, + Computed: true, }, "workspace": { - Type: cty.String, - Optional: true, + Type: cty.String, + Description: "The Terraform workspace to use, if " + + "the backend supports workspaces.", + DescriptionKind: configschema.StringMarkdown, + Optional: true, }, }, }, @@ -215,6 +234,12 @@ func getBackend(cfg cty.Value) (backend.Backend, cty.Value, tfdiags.Diagnostics) return nil, cty.NilVal, diags } + // If this is the enhanced remote backend, we want to disable the version + // check, because this is a read-only operation + if rb, ok := b.(*remote.Remote); ok { + rb.IgnoreVersionConflict() + } + return b, newVal, diags } diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/terraform/flatten.go b/vendor/github.com/hashicorp/terraform/builtin/providers/terraform/flatten.go deleted file mode 100644 index 4766a4f5..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/terraform/flatten.go +++ /dev/null @@ -1,76 +0,0 @@ -package terraform - -import ( - "fmt" - "reflect" -) - -// remoteStateFlatten takes a structure and turns into a flat map[string]string. -// -// Within the "thing" parameter, only primitive values are allowed. Structs are -// not supported. Therefore, it can only be slices, maps, primitives, and -// any combination of those together. -// -// The difference between this version and the version in package flatmap is that -// we add the count key for maps in this version, and return a normal -// map[string]string instead of a flatmap.Map -func remoteStateFlatten(thing map[string]interface{}) map[string]string { - result := make(map[string]string) - - for k, raw := range thing { - flatten(result, k, reflect.ValueOf(raw)) - } - - return result -} - -func flatten(result map[string]string, prefix string, v reflect.Value) { - if v.Kind() == reflect.Interface { - v = v.Elem() - } - - switch v.Kind() { - case reflect.Bool: - if v.Bool() { - result[prefix] = "true" - } else { - result[prefix] = "false" - } - case reflect.Int: - result[prefix] = fmt.Sprintf("%d", v.Int()) - case reflect.Map: - flattenMap(result, prefix, v) - case reflect.Slice: - flattenSlice(result, prefix, v) - case reflect.String: - result[prefix] = v.String() - default: - panic(fmt.Sprintf("Unknown: %s", v)) - } -} - -func flattenMap(result map[string]string, prefix string, v reflect.Value) { - mapKeys := v.MapKeys() - - result[fmt.Sprintf("%s.%%", prefix)] = fmt.Sprintf("%d", len(mapKeys)) - for _, k := range mapKeys { - if k.Kind() == reflect.Interface { - k = k.Elem() - } - - if k.Kind() != reflect.String { - panic(fmt.Sprintf("%s: map key is not string: %s", prefix, k)) - } - - flatten(result, fmt.Sprintf("%s.%s", prefix, k.String()), v.MapIndex(k)) - } -} - -func flattenSlice(result map[string]string, prefix string, v reflect.Value) { - prefix = prefix + "." - - result[prefix+"#"] = fmt.Sprintf("%d", v.Len()) - for i := 0; i < v.Len(); i++ { - flatten(result, fmt.Sprintf("%s%d", prefix, i), v.Index(i)) - } -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/terraform/provider.go b/vendor/github.com/hashicorp/terraform/builtin/providers/terraform/provider.go index 605362e3..9e10507c 100644 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/terraform/provider.go +++ b/vendor/github.com/hashicorp/terraform/builtin/providers/terraform/provider.go @@ -17,13 +17,13 @@ type Provider struct { } // NewProvider returns a new terraform provider -func NewProvider() *Provider { +func NewProvider() providers.Interface { return &Provider{} } // GetSchema returns the complete schema for the provider. -func (p *Provider) GetSchema() providers.GetSchemaResponse { - return providers.GetSchemaResponse{ +func (p *Provider) GetProviderSchema() providers.GetProviderSchemaResponse { + return providers.GetProviderSchemaResponse{ DataSources: map[string]providers.Schema{ "terraform_remote_state": dataSourceRemoteStateGetSchema(), }, @@ -31,20 +31,20 @@ func (p *Provider) GetSchema() providers.GetSchemaResponse { } // ValidateProviderConfig is used to validate the configuration values. -func (p *Provider) PrepareProviderConfig(req providers.PrepareProviderConfigRequest) providers.PrepareProviderConfigResponse { +func (p *Provider) ValidateProviderConfig(req providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse { // At this moment there is nothing to configure for the terraform provider, // so we will happily return without taking any action - var res providers.PrepareProviderConfigResponse + var res providers.ValidateProviderConfigResponse res.PreparedConfig = req.Config return res } -// ValidateDataSourceConfig is used to validate the data source configuration values. -func (p *Provider) ValidateDataSourceConfig(req providers.ValidateDataSourceConfigRequest) providers.ValidateDataSourceConfigResponse { +// ValidateDataResourceConfig is used to validate the data source configuration values. +func (p *Provider) ValidateDataResourceConfig(req providers.ValidateDataResourceConfigRequest) providers.ValidateDataResourceConfigResponse { // FIXME: move the backend configuration validate call that's currently // inside the read method into here so that we can catch provider configuration // errors in terraform validate as well as during terraform plan. - var res providers.ValidateDataSourceConfigResponse + var res providers.ValidateDataResourceConfigResponse // This should not happen if req.TypeName != "terraform_remote_state" { @@ -59,10 +59,10 @@ func (p *Provider) ValidateDataSourceConfig(req providers.ValidateDataSourceConf } // Configure configures and initializes the provider. -func (p *Provider) Configure(providers.ConfigureRequest) providers.ConfigureResponse { +func (p *Provider) ConfigureProvider(providers.ConfigureProviderRequest) providers.ConfigureProviderResponse { // At this moment there is nothing to configure for the terraform provider, // so we will happily return without taking any action - var res providers.ConfigureResponse + var res providers.ConfigureProviderResponse return res } @@ -126,11 +126,11 @@ func (p *Provider) ImportResourceState(providers.ImportResourceStateRequest) pro panic("unimplemented - terraform_remote_state has no resources") } -// ValidateResourceTypeConfig is used to to validate the resource configuration values. -func (p *Provider) ValidateResourceTypeConfig(providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse { +// ValidateResourceConfig is used to to validate the resource configuration values. +func (p *Provider) ValidateResourceConfig(providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { // At this moment there is nothing to configure for the terraform provider, // so we will happily return without taking any action - var res providers.ValidateResourceTypeConfigResponse + var res providers.ValidateResourceConfigResponse return res } diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/terraform/provider_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/terraform/provider_test.go index 2a3a2bfe..fecf720d 100644 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/terraform/provider_test.go +++ b/vendor/github.com/hashicorp/terraform/builtin/providers/terraform/provider_test.go @@ -1,29 +1,10 @@ package terraform import ( - "testing" - - "github.com/hashicorp/terraform/providers" - backendInit "github.com/hashicorp/terraform/backend/init" ) -var testAccProviders map[string]*Provider -var testAccProvider *Provider - func init() { // Initialize the backends backendInit.Init(nil) - - testAccProvider = NewProvider() - testAccProviders = map[string]*Provider{ - "terraform": testAccProvider, - } -} - -func TestProvider_impl(t *testing.T) { - var _ providers.Interface = NewProvider() -} - -func testAccPreCheck(t *testing.T) { } diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/data_source.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/data_source.go deleted file mode 100644 index 2a735703..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/data_source.go +++ /dev/null @@ -1,63 +0,0 @@ -package test - -import ( - "time" - - "github.com/hashicorp/terraform/helper/schema" -) - -func testDataSource() *schema.Resource { - return &schema.Resource{ - Read: testDataSourceRead, - - Schema: map[string]*schema.Schema{ - "list": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - - "input": { - Type: schema.TypeString, - Optional: true, - }, - - "output": { - Type: schema.TypeString, - Computed: true, - }, - // this attribute is computed, but never set by the provider - "nil": { - Type: schema.TypeString, - Computed: true, - }, - - "input_map": { - Type: schema.TypeMap, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - }, - "output_map": { - Type: schema.TypeMap, - Elem: &schema.Schema{Type: schema.TypeString}, - Computed: true, - }, - }, - } -} - -func testDataSourceRead(d *schema.ResourceData, meta interface{}) error { - d.SetId(time.Now().UTC().String()) - d.Set("list", []interface{}{"one", "two", "three"}) - - if input, hasInput := d.GetOk("input"); hasInput { - d.Set("output", input) - } else { - d.Set("output", "some output") - } - - if inputMap, hasInput := d.GetOk("input_map"); hasInput { - d.Set("output_map", inputMap) - } - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/data_source_label.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/data_source_label.go deleted file mode 100644 index 40f3bad5..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/data_source_label.go +++ /dev/null @@ -1,25 +0,0 @@ -package test - -import ( - "github.com/hashicorp/terraform/helper/schema" -) - -func providerLabelDataSource() *schema.Resource { - return &schema.Resource{ - Read: providerLabelDataSourceRead, - - Schema: map[string]*schema.Schema{ - "label": { - Type: schema.TypeString, - Computed: true, - }, - }, - } -} - -func providerLabelDataSourceRead(d *schema.ResourceData, meta interface{}) error { - label := meta.(string) - d.SetId(label) - d.Set("label", label) - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/data_source_label_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/data_source_label_test.go deleted file mode 100644 index d98a27b0..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/data_source_label_test.go +++ /dev/null @@ -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 - }, - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/data_source_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/data_source_test.go deleted file mode 100644 index c0a1ae57..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/data_source_test.go +++ /dev/null @@ -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"), - ), - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/diff_apply_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/diff_apply_test.go deleted file mode 100644 index b28e110e..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/diff_apply_test.go +++ /dev/null @@ -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)) - } -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/provider.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/provider.go deleted file mode 100644 index 1066f37c..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/provider.go +++ /dev/null @@ -1,59 +0,0 @@ -package test - -import ( - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" -) - -func Provider() terraform.ResourceProvider { - return &schema.Provider{ - Schema: map[string]*schema.Schema{ - // Optional attribute to label a particular instance for a test - // that has multiple instances of this provider, so that they - // can be distinguished using the test_provider_label data source. - "label": { - Type: schema.TypeString, - Optional: true, - }, - }, - ProviderMetaSchema: map[string]*schema.Schema{ - // Optionally allow specifying information at a module-level - "foo": { - Type: schema.TypeString, - Optional: true, - }, - }, - ResourcesMap: map[string]*schema.Resource{ - "test_resource": testResource(), - "test_resource_gh12183": testResourceGH12183(), - "test_resource_with_custom_diff": testResourceCustomDiff(), - "test_resource_timeout": testResourceTimeout(), - "test_resource_diff_suppress": testResourceDiffSuppress(), - "test_resource_force_new": testResourceForceNew(), - "test_resource_nested": testResourceNested(), - "test_resource_nested_set": testResourceNestedSet(), - "test_resource_state_func": testResourceStateFunc(), - "test_resource_deprecated": testResourceDeprecated(), - "test_resource_defaults": testResourceDefaults(), - "test_resource_list": testResourceList(), - "test_resource_list_set": testResourceListSet(), - "test_resource_map": testResourceMap(), - "test_resource_computed_set": testResourceComputedSet(), - "test_resource_config_mode": testResourceConfigMode(), - "test_resource_nested_id": testResourceNestedId(), - "test_resource_provider_meta": testResourceProviderMeta(), - "test_resource_signal": testResourceSignal(), - "test_undeleteable": testResourceUndeleteable(), - "test_resource_required_min": testResourceRequiredMin(), - }, - DataSourcesMap: map[string]*schema.Resource{ - "test_data_source": testDataSource(), - "test_provider_label": providerLabelDataSource(), - }, - ConfigureFunc: providerConfigure, - } -} - -func providerConfigure(d *schema.ResourceData) (interface{}, error) { - return d.Get("label"), nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/provider_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/provider_test.go deleted file mode 100644 index 40defefa..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/provider_test.go +++ /dev/null @@ -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, - } -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource.go deleted file mode 100644 index b05fcc68..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource.go +++ /dev/null @@ -1,233 +0,0 @@ -package test - -import ( - "errors" - "fmt" - - "github.com/hashicorp/terraform/helper/schema" -) - -func testResource() *schema.Resource { - return &schema.Resource{ - Create: testResourceCreate, - Read: testResourceRead, - Update: testResourceUpdate, - Delete: testResourceDelete, - - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - - CustomizeDiff: func(d *schema.ResourceDiff, _ interface{}) error { - if d.HasChange("optional") { - d.SetNewComputed("planned_computed") - } - return nil - }, - - Schema: map[string]*schema.Schema{ - "required": { - Type: schema.TypeString, - Required: true, - }, - "optional": { - Type: schema.TypeString, - Optional: true, - }, - "optional_bool": { - Type: schema.TypeBool, - Optional: true, - }, - "optional_force_new": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - "optional_computed_map": { - Type: schema.TypeMap, - Optional: true, - Computed: true, - }, - "optional_computed_force_new": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - "optional_computed": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "computed_read_only": { - Type: schema.TypeString, - Computed: true, - }, - "computed_from_required": { - Type: schema.TypeString, - Computed: true, - ForceNew: true, - }, - "computed_read_only_force_new": { - Type: schema.TypeString, - Computed: true, - ForceNew: true, - }, - "computed_list": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "set": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Set: schema.HashString, - }, - "computed_set": { - Type: schema.TypeSet, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Set: schema.HashString, - }, - "map": { - Type: schema.TypeMap, - Optional: true, - }, - "optional_map": { - Type: schema.TypeMap, - Optional: true, - }, - "required_map": { - Type: schema.TypeMap, - Required: true, - }, - "map_that_look_like_set": { - Type: schema.TypeMap, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "computed_map": { - Type: schema.TypeMap, - Computed: true, - }, - "list": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "list_of_map": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeMap, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - }, - "apply_error": { - Type: schema.TypeString, - Optional: true, - Description: "return and error during apply", - }, - "planned_computed": { - Type: schema.TypeString, - Computed: true, - Description: "copied the required field during apply, and plans computed when changed", - }, - // this should return unset from GetOkExists - "get_ok_exists_false": { - Type: schema.TypeBool, - Computed: true, - Optional: true, - Description: "do not set in config", - }, - "int": { - Type: schema.TypeInt, - Optional: true, - }, - }, - } -} - -func testResourceCreate(d *schema.ResourceData, meta interface{}) error { - d.SetId("testId") - - errMsg, _ := d.Get("apply_error").(string) - if errMsg != "" { - return errors.New(errMsg) - } - - // Required must make it through to Create - if _, ok := d.GetOk("required"); !ok { - return fmt.Errorf("Missing attribute 'required', but it's required!") - } - if _, ok := d.GetOk("required_map"); !ok { - return fmt.Errorf("Missing attribute 'required_map', but it's required!") - } - - d.Set("computed_from_required", d.Get("required")) - - return testResourceRead(d, meta) -} - -func testResourceRead(d *schema.ResourceData, meta interface{}) error { - d.Set("computed_read_only", "value_from_api") - d.Set("computed_read_only_force_new", "value_from_api") - if _, ok := d.GetOk("optional_computed_map"); !ok { - d.Set("optional_computed_map", map[string]string{}) - } - d.Set("computed_map", map[string]string{"key1": "value1"}) - d.Set("computed_list", []string{"listval1", "listval2"}) - d.Set("computed_set", []string{"setval1", "setval2"}) - - d.Set("planned_computed", d.Get("optional")) - - // if there is no "set" value, erroneously set it to an empty set. This - // might change a null value to an empty set, but we should be able to - // ignore that. - s := d.Get("set") - if s == nil || s.(*schema.Set).Len() == 0 { - d.Set("set", []interface{}{}) - } - - // This mimics many providers always setting a *string value. - // The existing behavior is that this will appear in the state as an empty - // string, which we have to maintain. - o := d.Get("optional") - if o == "" { - d.Set("optional", nil) - } - - // This should not show as set unless it's set in the config - _, ok := d.GetOkExists("get_ok_exists_false") - if ok { - return errors.New("get_ok_exists_false should not be set") - } - - return nil -} - -func testResourceUpdate(d *schema.ResourceData, meta interface{}) error { - errMsg, _ := d.Get("apply_error").(string) - if errMsg != "" { - return errors.New(errMsg) - } - return testResourceRead(d, meta) -} - -func testResourceDelete(d *schema.ResourceData, meta interface{}) error { - d.SetId("") - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_computed_set.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_computed_set.go deleted file mode 100644 index 092cd227..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_computed_set.go +++ /dev/null @@ -1,123 +0,0 @@ -package test - -import ( - "bytes" - "fmt" - "math/rand" - "strings" - - "github.com/hashicorp/terraform/helper/hashcode" - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceComputedSet() *schema.Resource { - return &schema.Resource{ - Create: testResourceComputedSetCreate, - Read: testResourceComputedSetRead, - Delete: testResourceComputedSetDelete, - Update: testResourceComputedSetUpdate, - - CustomizeDiff: func(d *schema.ResourceDiff, _ interface{}) error { - o, n := d.GetChange("set_count") - if o != n { - d.SetNewComputed("string_set") - } - return nil - }, - - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - - Schema: map[string]*schema.Schema{ - "set_count": { - Type: schema.TypeInt, - Optional: true, - }, - "string_set": { - Type: schema.TypeSet, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Set: schema.HashString, - }, - - "rule": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Computed: true, - }, - - "ip_protocol": { - Type: schema.TypeString, - Required: true, - ForceNew: false, - }, - - "cidr": { - Type: schema.TypeString, - Optional: true, - ForceNew: false, - StateFunc: func(v interface{}) string { - return strings.ToLower(v.(string)) - }, - }, - }, - }, - }, - "optional_set": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, - }, - }, - } -} - -func computeSecGroupV2RuleHash(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s-", m["ip_protocol"].(string))) - buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(m["cidr"].(string)))) - - return hashcode.String(buf.String()) -} - -func testResourceComputedSetCreate(d *schema.ResourceData, meta interface{}) error { - d.SetId(fmt.Sprintf("%x", rand.Int63())) - return testResourceComputedSetRead(d, meta) -} - -func testResourceComputedSetRead(d *schema.ResourceData, meta interface{}) error { - count := 3 - v, ok := d.GetOk("set_count") - if ok { - count = v.(int) - } - - var set []interface{} - for i := 0; i < count; i++ { - set = append(set, fmt.Sprintf("%d", i)) - } - - d.Set("string_set", schema.NewSet(schema.HashString, set)) - - // This isn't computed, but we should be able to ignore without issues. - d.Set("optional_set", []interface{}{}) - return nil -} - -func testResourceComputedSetUpdate(d *schema.ResourceData, meta interface{}) error { - return testResourceComputedSetRead(d, meta) -} - -func testResourceComputedSetDelete(d *schema.ResourceData, meta interface{}) error { - d.SetId("") - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_computed_set_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_computed_set_test.go deleted file mode 100644 index 06e60823..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_computed_set_test.go +++ /dev/null @@ -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" - } -} - `), - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_config_mode.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_config_mode.go deleted file mode 100644 index 82b47603..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_config_mode.go +++ /dev/null @@ -1,78 +0,0 @@ -package test - -import ( - "fmt" - - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceConfigMode() *schema.Resource { - return &schema.Resource{ - Create: testResourceConfigModeCreate, - Read: testResourceConfigModeRead, - Delete: testResourceConfigModeDelete, - Update: testResourceConfigModeUpdate, - - Schema: map[string]*schema.Schema{ - "resource_as_attr": { - Type: schema.TypeList, - ConfigMode: schema.SchemaConfigModeAttr, - Optional: true, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "foo": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, - "nested_set": { - Type: schema.TypeSet, - Optional: true, - ConfigMode: schema.SchemaConfigModeAttr, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "value": { - Type: schema.TypeString, - Optional: true, - }, - "set": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - }, - }, - }, - }, - } -} - -func testResourceConfigModeCreate(d *schema.ResourceData, meta interface{}) error { - d.SetId("placeholder") - return testResourceConfigModeRead(d, meta) -} - -func testResourceConfigModeRead(d *schema.ResourceData, meta interface{}) error { - if l, ok := d.Get("resource_as_attr").([]interface{}); !ok { - return fmt.Errorf("resource_as_attr should appear as []interface{}, not %T", l) - } else { - for i, item := range l { - if _, ok := item.(map[string]interface{}); !ok { - return fmt.Errorf("resource_as_attr[%d] should appear as map[string]interface{}, not %T", i, item) - } - } - } - return nil -} - -func testResourceConfigModeUpdate(d *schema.ResourceData, meta interface{}) error { - return testResourceConfigModeRead(d, meta) -} - -func testResourceConfigModeDelete(d *schema.ResourceData, meta interface{}) error { - d.SetId("") - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_config_mode_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_config_mode_test.go deleted file mode 100644 index f73adc8f..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_config_mode_test.go +++ /dev/null @@ -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(), - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_data_dep_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_data_dep_test.go deleted file mode 100644 index 0cd773d5..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_data_dep_test.go +++ /dev/null @@ -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) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_dataproc_cluster_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_dataproc_cluster_test.go deleted file mode 100644 index 3d5a2282..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_dataproc_cluster_test.go +++ /dev/null @@ -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)) - } -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_defaults.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_defaults.go deleted file mode 100644 index 41038de6..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_defaults.go +++ /dev/null @@ -1,70 +0,0 @@ -package test - -import ( - "fmt" - "math/rand" - - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceDefaults() *schema.Resource { - return &schema.Resource{ - Create: testResourceDefaultsCreate, - Read: testResourceDefaultsRead, - Delete: testResourceDefaultsDelete, - Update: testResourceDefaultsUpdate, - - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - - Schema: map[string]*schema.Schema{ - "default_string": { - Type: schema.TypeString, - Optional: true, - Default: "default string", - }, - "default_bool": { - Type: schema.TypeString, - Optional: true, - Default: true, - }, - "nested": { - Type: schema.TypeSet, - Optional: true, - ForceNew: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "string": { - Type: schema.TypeString, - Optional: true, - Default: "default nested", - }, - "optional": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, - }, - } -} - -func testResourceDefaultsCreate(d *schema.ResourceData, meta interface{}) error { - d.SetId(fmt.Sprintf("%x", rand.Int63())) - return testResourceDefaultsRead(d, meta) -} - -func testResourceDefaultsUpdate(d *schema.ResourceData, meta interface{}) error { - return testResourceDefaultsRead(d, meta) -} - -func testResourceDefaultsRead(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func testResourceDefaultsDelete(d *schema.ResourceData, meta interface{}) error { - d.SetId("") - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_defaults_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_defaults_test.go deleted file mode 100644 index 8aabd448..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_defaults_test.go +++ /dev/null @@ -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", ""), - ), - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_deprecated.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_deprecated.go deleted file mode 100644 index a176977b..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_deprecated.go +++ /dev/null @@ -1,119 +0,0 @@ -package test - -import ( - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceDeprecated() *schema.Resource { - return &schema.Resource{ - Create: testResourceDeprecatedCreate, - Read: testResourceDeprecatedRead, - Update: testResourceDeprecatedUpdate, - Delete: testResourceDeprecatedDelete, - - Schema: map[string]*schema.Schema{ - "map_deprecated": { - Type: schema.TypeMap, - Optional: true, - Deprecated: "deprecated", - }, - "map_removed": { - Type: schema.TypeMap, - Optional: true, - Removed: "removed", - }, - "set_block_deprecated": { - Type: schema.TypeSet, - Optional: true, - MaxItems: 1, - Deprecated: "deprecated", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "value": { - Type: schema.TypeString, - Required: true, - Deprecated: "deprecated", - }, - "optional": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - Deprecated: "deprecated", - }, - }, - }, - }, - "set_block_removed": { - Type: schema.TypeSet, - Optional: true, - MaxItems: 1, - Removed: "Removed", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "optional": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - Computed: true, - Removed: "removed", - }, - }, - }, - }, - "list_block_deprecated": { - Type: schema.TypeList, - Optional: true, - Deprecated: "deprecated", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "value": { - Type: schema.TypeString, - Required: true, - Deprecated: "deprecated", - }, - "optional": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - Deprecated: "deprecated", - }, - }, - }, - }, - "list_block_removed": { - Type: schema.TypeList, - Optional: true, - Removed: "removed", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "optional": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - Removed: "removed", - }, - }, - }, - }, - }, - } -} - -func testResourceDeprecatedCreate(d *schema.ResourceData, meta interface{}) error { - d.SetId("testId") - return nil -} - -func testResourceDeprecatedRead(d *schema.ResourceData, meta interface{}) error { - - return nil -} - -func testResourceDeprecatedUpdate(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func testResourceDeprecatedDelete(d *schema.ResourceData, meta interface{}) error { - d.SetId("") - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_deprecated_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_deprecated_test.go deleted file mode 100644 index 8817567d..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_deprecated_test.go +++ /dev/null @@ -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"), - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_diff_suppress.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_diff_suppress.go deleted file mode 100644 index f5cfc933..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_diff_suppress.go +++ /dev/null @@ -1,104 +0,0 @@ -package test - -import ( - "fmt" - "math/rand" - "strings" - - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceDiffSuppress() *schema.Resource { - diffSuppress := func(k, old, new string, d *schema.ResourceData) bool { - if old == "" || strings.Contains(new, "replace") { - return false - } - return true - } - - return &schema.Resource{ - Create: testResourceDiffSuppressCreate, - Read: testResourceDiffSuppressRead, - Delete: testResourceDiffSuppressDelete, - Update: testResourceDiffSuppressUpdate, - - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - - Schema: map[string]*schema.Schema{ - "optional": { - Type: schema.TypeString, - Optional: true, - }, - "val_to_upper": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - StateFunc: func(val interface{}) string { - return strings.ToUpper(val.(string)) - }, - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - return strings.ToUpper(old) == strings.ToUpper(new) - }, - }, - "network": { - Type: schema.TypeString, - Optional: true, - Default: "default", - ForceNew: true, - DiffSuppressFunc: diffSuppress, - }, - "subnetwork": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - DiffSuppressFunc: diffSuppress, - }, - - "node_pool": { - Type: schema.TypeList, - Optional: true, - Computed: true, - ForceNew: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - }, - }, - }, - } -} - -func testResourceDiffSuppressCreate(d *schema.ResourceData, meta interface{}) error { - d.Set("network", "modified") - d.Set("subnetwork", "modified") - - if _, ok := d.GetOk("node_pool"); !ok { - d.Set("node_pool", []string{}) - } - - id := fmt.Sprintf("%x", rand.Int63()) - d.SetId(id) - return nil -} - -func testResourceDiffSuppressRead(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func testResourceDiffSuppressUpdate(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func testResourceDiffSuppressDelete(d *schema.ResourceData, meta interface{}) error { - d.SetId("") - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_diff_suppress_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_diff_suppress_test.go deleted file mode 100644 index 89416f32..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_diff_suppress_test.go +++ /dev/null @@ -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, - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_force_new.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_force_new.go deleted file mode 100644 index 81a06736..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_force_new.go +++ /dev/null @@ -1,39 +0,0 @@ -package test - -import ( - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceForceNew() *schema.Resource { - return &schema.Resource{ - Create: testResourceForceNewCreate, - Read: testResourceForceNewRead, - Delete: testResourceForceNewDelete, - - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - - Schema: map[string]*schema.Schema{ - "triggers": { - Type: schema.TypeMap, - Optional: true, - ForceNew: true, - }, - }, - } -} - -func testResourceForceNewCreate(d *schema.ResourceData, meta interface{}) error { - d.SetId("testId") - return testResourceForceNewRead(d, meta) -} - -func testResourceForceNewRead(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func testResourceForceNewDelete(d *schema.ResourceData, meta interface{}) error { - d.SetId("") - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_force_new_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_force_new_test.go deleted file mode 100644 index 3e0bf19c..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_force_new_test.go +++ /dev/null @@ -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" { -} `), - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_gh12183.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_gh12183.go deleted file mode 100644 index d67bcf75..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_gh12183.go +++ /dev/null @@ -1,64 +0,0 @@ -package test - -import ( - "github.com/hashicorp/terraform/helper/schema" -) - -// This is a test resource to help reproduce GH-12183. This issue came up -// as a complex mixing of core + helper/schema and while we added core tests -// to cover some of the cases, this test helps top it off with an end-to-end -// test. -func testResourceGH12183() *schema.Resource { - return &schema.Resource{ - Create: testResourceCreate_gh12183, - Read: testResourceRead_gh12183, - Update: testResourceUpdate_gh12183, - Delete: testResourceDelete_gh12183, - Schema: map[string]*schema.Schema{ - "key": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - - "config": &schema.Schema{ - Type: schema.TypeList, - Optional: true, - ForceNew: true, - MinItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - }, - - "rules": { - Type: schema.TypeSet, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, - }, - }, - }, - }, - }, - } -} - -func testResourceCreate_gh12183(d *schema.ResourceData, meta interface{}) error { - d.SetId("testId") - return testResourceRead_gh12183(d, meta) -} - -func testResourceRead_gh12183(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func testResourceUpdate_gh12183(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func testResourceDelete_gh12183(d *schema.ResourceData, meta interface{}) error { - d.SetId("") - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_gh12183_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_gh12183_test.go deleted file mode 100644 index 9cf10058..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_gh12183_test.go +++ /dev/null @@ -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 - }, - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_list.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_list.go deleted file mode 100644 index 895298eb..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_list.go +++ /dev/null @@ -1,192 +0,0 @@ -package test - -import ( - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceList() *schema.Resource { - return &schema.Resource{ - Create: testResourceListCreate, - Read: testResourceListRead, - Update: testResourceListUpdate, - Delete: testResourceListDelete, - - CustomizeDiff: func(d *schema.ResourceDiff, _ interface{}) error { - if d.HasChange("dependent_list") { - d.SetNewComputed("computed_list") - } - return nil - }, - - Schema: map[string]*schema.Schema{ - "list_block": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "string": { - Type: schema.TypeString, - Optional: true, - }, - "int": { - Type: schema.TypeInt, - Optional: true, - }, - "force_new": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - "sublist": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "sublist_block": { - Type: schema.TypeList, - Optional: true, - Computed: true, - ForceNew: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "string": { - Type: schema.TypeString, - Required: true, - }, - "int": { - Type: schema.TypeInt, - Required: true, - }, - }, - }, - }, - "sublist_block_optional": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "list": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - }, - }, - }, - }, - }, - }, - "dependent_list": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "val": { - Type: schema.TypeString, - Required: true, - }, - }, - }, - }, - "computed_list": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "min_items": { - Type: schema.TypeList, - Optional: true, - MinItems: 2, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "val": { - Type: schema.TypeString, - Required: true, - }, - }, - }, - }, - "never_set": { - Type: schema.TypeList, - MaxItems: 1, - Optional: true, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "sublist": { - Type: schema.TypeList, - MaxItems: 1, - ForceNew: true, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "bool": { - Type: schema.TypeBool, - ForceNew: true, - Required: true, - }, - "string": { - Type: schema.TypeString, - Computed: true, - }, - }, - }, - }, - }, - }, - }, - "map_list": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeMap}, - }, - }, - } -} - -func testResourceListCreate(d *schema.ResourceData, meta interface{}) error { - d.SetId("testId") - return testResourceListRead(d, meta) -} - -func testResourceListRead(d *schema.ResourceData, meta interface{}) error { - fixedIps := d.Get("dependent_list") - - // all_fixed_ips should be set as computed with a CustomizeDiff func, but - // we're trying to emulate legacy provider behavior, and updating a - // computed field was a common case. - ips := []interface{}{} - if fixedIps != nil { - for _, v := range fixedIps.([]interface{}) { - m := v.(map[string]interface{}) - ips = append(ips, m["val"]) - } - } - if err := d.Set("computed_list", ips); err != nil { - return err - } - - // "computing" these values should insert empty containers into the - // never_set block. - values := make(map[string]interface{}) - values["sublist"] = []interface{}{} - d.Set("never_set", []interface{}{values}) - - return nil -} - -func testResourceListUpdate(d *schema.ResourceData, meta interface{}) error { - block := d.Get("never_set").([]interface{}) - if len(block) > 0 { - // if profiles contains any values, they should not be nil - _ = block[0].(map[string]interface{}) - } - return testResourceListRead(d, meta) -} - -func testResourceListDelete(d *schema.ResourceData, meta interface{}) error { - d.SetId("") - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_list_set.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_list_set.go deleted file mode 100644 index 0ce5abc8..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_list_set.go +++ /dev/null @@ -1,192 +0,0 @@ -package test - -import ( - "fmt" - "math/rand" - - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceListSet() *schema.Resource { - return &schema.Resource{ - Create: testResourceListSetCreate, - Read: testResourceListSetRead, - Delete: testResourceListSetDelete, - Update: testResourceListSetUpdate, - - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - - Schema: map[string]*schema.Schema{ - "list": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "set": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "elem": { - Type: schema.TypeString, - Optional: true, - DiffSuppressFunc: func(_, o, n string, _ *schema.ResourceData) bool { - return o == n - }, - }, - }, - }, - Set: func(v interface{}) int { - raw := v.(map[string]interface{}) - if el, ok := raw["elem"]; ok { - return schema.HashString(el) - } - return 42 - }, - }, - }, - }, - }, - "replication_configuration": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "role": { - Type: schema.TypeString, - Required: true, - }, - "rules": { - Type: schema.TypeSet, - Required: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Optional: true, - }, - "destination": { - Type: schema.TypeSet, - MaxItems: 1, - MinItems: 1, - Required: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "account_id": { - Type: schema.TypeString, - Optional: true, - }, - "bucket": { - Type: schema.TypeString, - Required: true, - }, - "storage_class": { - Type: schema.TypeString, - Optional: true, - }, - "replica_kms_key_id": { - Type: schema.TypeString, - Optional: true, - }, - "access_control_translation": { - Type: schema.TypeList, - Optional: true, - MinItems: 1, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "owner": { - Type: schema.TypeString, - Required: true, - }, - }, - }, - }, - }, - }, - }, - "source_selection_criteria": { - Type: schema.TypeSet, - Optional: true, - MinItems: 1, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "sse_kms_encrypted_objects": { - Type: schema.TypeSet, - Optional: true, - MinItems: 1, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "enabled": { - Type: schema.TypeBool, - Required: true, - }, - }, - }, - }, - }, - }, - }, - "prefix": { - Type: schema.TypeString, - Optional: true, - }, - "status": { - Type: schema.TypeString, - Required: true, - }, - "priority": { - Type: schema.TypeInt, - Optional: true, - }, - "filter": { - Type: schema.TypeList, - Optional: true, - MinItems: 1, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "prefix": { - Type: schema.TypeString, - Optional: true, - }, - "tags": { - Type: schema.TypeMap, - Optional: true, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - } -} - -func testResourceListSetCreate(d *schema.ResourceData, meta interface{}) error { - d.SetId(fmt.Sprintf("%x", rand.Int63())) - return testResourceListSetRead(d, meta) -} - -func testResourceListSetUpdate(d *schema.ResourceData, meta interface{}) error { - return testResourceListSetRead(d, meta) -} - -func testResourceListSetRead(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func testResourceListSetDelete(d *schema.ResourceData, meta interface{}) error { - d.SetId("") - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_list_set_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_list_set_test.go deleted file mode 100644 index f1e8353f..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_list_set_test.go +++ /dev/null @@ -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"), - ), - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_list_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_list_test.go deleted file mode 100644 index 876a81fd..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_list_test.go +++ /dev/null @@ -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 - } - } -} - `), - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_map.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_map.go deleted file mode 100644 index c6bf62bd..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_map.go +++ /dev/null @@ -1,77 +0,0 @@ -package test - -import ( - "fmt" - - "github.com/hashicorp/terraform/configs/hcl2shim" - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceMap() *schema.Resource { - return &schema.Resource{ - Create: testResourceMapCreate, - Read: testResourceMapRead, - Update: testResourceMapUpdate, - Delete: testResourceMapDelete, - - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - }, - "map_of_three": { - Type: schema.TypeMap, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - ValidateFunc: func(v interface{}, _ string) ([]string, []error) { - errs := []error{} - for k, v := range v.(map[string]interface{}) { - if v == hcl2shim.UnknownVariableValue { - errs = append(errs, fmt.Errorf("unknown value in ValidateFunc: %q=%q", k, v)) - } - } - return nil, errs - }, - }, - "map_values": { - Type: schema.TypeMap, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "computed_map": { - Type: schema.TypeMap, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - }, - } -} - -func testResourceMapCreate(d *schema.ResourceData, meta interface{}) error { - // make sure all elements are passed to the map - m := d.Get("map_of_three").(map[string]interface{}) - if len(m) != 3 { - return fmt.Errorf("expected 3 map values, got %#v\n", m) - } - - d.SetId("testId") - return testResourceMapRead(d, meta) -} - -func testResourceMapRead(d *schema.ResourceData, meta interface{}) error { - var computedMap map[string]interface{} - if v, ok := d.GetOk("map_values"); ok { - computedMap = v.(map[string]interface{}) - } - d.Set("computed_map", computedMap) - return nil -} - -func testResourceMapUpdate(d *schema.ResourceData, meta interface{}) error { - return testResourceMapRead(d, meta) -} - -func testResourceMapDelete(d *schema.ResourceData, meta interface{}) error { - d.SetId("") - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_map_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_map_test.go deleted file mode 100644 index 0d82d5f4..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_map_test.go +++ /dev/null @@ -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", - ), - ), - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_nested.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_nested.go deleted file mode 100644 index bff74325..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_nested.go +++ /dev/null @@ -1,114 +0,0 @@ -package test - -import ( - "fmt" - "math/rand" - - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceNested() *schema.Resource { - return &schema.Resource{ - Create: testResourceNestedCreate, - Read: testResourceNestedRead, - Delete: testResourceNestedDelete, - - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - - Schema: map[string]*schema.Schema{ - "optional": { - Type: schema.TypeBool, - Optional: true, - ForceNew: true, - }, - "nested": { - Type: schema.TypeSet, - Optional: true, - ForceNew: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "string": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - "optional": { - Type: schema.TypeBool, - Optional: true, - ForceNew: true, - }, - "nested_again": { - Type: schema.TypeSet, - Optional: true, - ForceNew: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "string": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - }, - }, - }, - }, - }, - }, - "list_block": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "sub_list_block": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "bool": { - Type: schema.TypeBool, - Optional: true, - }, - "set": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - }, - }, - }, - }, - }, - }, - }, - } -} - -func testResourceNestedCreate(d *schema.ResourceData, meta interface{}) error { - d.SetId(fmt.Sprintf("%x", rand.Int63())) - return testResourceNestedRead(d, meta) -} - -func testResourceNestedUpdate(d *schema.ResourceData, meta interface{}) error { - return testResourceNestedRead(d, meta) -} - -func testResourceNestedRead(d *schema.ResourceData, meta interface{}) error { - set := []map[string]interface{}{map[string]interface{}{ - "sub_list_block": []map[string]interface{}{map[string]interface{}{ - "bool": false, - "set": schema.NewSet(schema.HashString, nil), - }}, - }} - d.Set("list_block", set) - return nil -} - -func testResourceNestedDelete(d *schema.ResourceData, meta interface{}) error { - d.SetId("") - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_nested_id.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_nested_id.go deleted file mode 100644 index c3bd4197..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_nested_id.go +++ /dev/null @@ -1,48 +0,0 @@ -package test - -import ( - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceNestedId() *schema.Resource { - return &schema.Resource{ - Create: testResourceNestedIdCreate, - Read: testResourceNestedIdRead, - Update: testResourceNestedIdUpdate, - Delete: testResourceNestedIdDelete, - - Schema: map[string]*schema.Schema{ - "list_block": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - }, - }, - }, - }, - } -} - -func testResourceNestedIdCreate(d *schema.ResourceData, meta interface{}) error { - d.SetId("testId") - return nil -} - -func testResourceNestedIdRead(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func testResourceNestedIdUpdate(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func testResourceNestedIdDelete(d *schema.ResourceData, meta interface{}) error { - d.SetId("") - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_nested_id_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_nested_id_test.go deleted file mode 100644 index 9ca7a246..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_nested_id_test.go +++ /dev/null @@ -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"), - ), - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_nested_set.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_nested_set.go deleted file mode 100644 index 81d7ab0f..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_nested_set.go +++ /dev/null @@ -1,171 +0,0 @@ -package test - -import ( - "fmt" - "math/rand" - - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceNestedSet() *schema.Resource { - return &schema.Resource{ - Create: testResourceNestedSetCreate, - Read: testResourceNestedSetRead, - Delete: testResourceNestedSetDelete, - Update: testResourceNestedSetUpdate, - - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - - Schema: map[string]*schema.Schema{ - "optional": { - Type: schema.TypeBool, - Optional: true, - }, - "force_new": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - "type_list": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "value": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - }, - }, - }, - }, - "single": { - Type: schema.TypeSet, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "value": { - Type: schema.TypeString, - ForceNew: true, - Required: true, - }, - - "optional": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - Computed: true, - }, - }, - }, - }, - "multi": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "set": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "required": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "optional_int": { - Type: schema.TypeInt, - Optional: true, - }, - "bool": { - Type: schema.TypeBool, - Optional: true, - }, - }, - }, - }, - - "optional": { - Type: schema.TypeString, - // commenting this causes it to get missed during apply - //ForceNew: true, - Optional: true, - }, - "bool": { - Type: schema.TypeBool, - Optional: true, - }, - }, - }, - }, - "with_list": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "required": { - Type: schema.TypeString, - Required: true, - }, - "list": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "list_block": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "unused": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, - }, - }, - }, - }, - } -} - -func testResourceNestedSetCreate(d *schema.ResourceData, meta interface{}) error { - id := fmt.Sprintf("%x", rand.Int63()) - d.SetId(id) - - // replicate some awkward handling of a computed value in a set - set := d.Get("single").(*schema.Set) - l := set.List() - if len(l) == 1 { - if s, ok := l[0].(map[string]interface{}); ok { - if v, _ := s["optional"].(string); v == "" { - s["optional"] = id - } - } - } - - d.Set("single", set) - - return testResourceNestedSetRead(d, meta) -} - -func testResourceNestedSetRead(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func testResourceNestedSetDelete(d *schema.ResourceData, meta interface{}) error { - d.SetId("") - return nil -} - -func testResourceNestedSetUpdate(d *schema.ResourceData, meta interface{}) error { - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_nested_set_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_nested_set_test.go deleted file mode 100644 index dddce0e8..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_nested_set_test.go +++ /dev/null @@ -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(), - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_nested_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_nested_test.go deleted file mode 100644 index c525f625..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_nested_test.go +++ /dev/null @@ -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 - }, - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_provider_meta.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_provider_meta.go deleted file mode 100644 index c05adb17..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_provider_meta.go +++ /dev/null @@ -1,95 +0,0 @@ -package test - -import ( - "fmt" - - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceProviderMeta() *schema.Resource { - return &schema.Resource{ - Create: testResourceProviderMetaCreate, - Read: testResourceProviderMetaRead, - Update: testResourceProviderMetaUpdate, - Delete: testResourceProviderMetaDelete, - - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - - Schema: map[string]*schema.Schema{ - "optional": { - Type: schema.TypeString, - Optional: true, - }, - }, - } -} - -type providerMeta struct { - Foo string `cty:"foo"` -} - -func testResourceProviderMetaCreate(d *schema.ResourceData, meta interface{}) error { - d.SetId("testId") - var m providerMeta - - err := d.GetProviderMeta(&m) - if err != nil { - return err - } - - if m.Foo != "bar" { - return fmt.Errorf("expected provider_meta.foo to be %q, was %q", - "bar", m.Foo) - } - - return testResourceProviderMetaRead(d, meta) -} - -func testResourceProviderMetaRead(d *schema.ResourceData, meta interface{}) error { - var m providerMeta - - err := d.GetProviderMeta(&m) - if err != nil { - return err - } - - if m.Foo != "bar" { - return fmt.Errorf("expected provider_meta.foo to be %q, was %q", - "bar", m.Foo) - } - - return nil -} - -func testResourceProviderMetaUpdate(d *schema.ResourceData, meta interface{}) error { - var m providerMeta - - err := d.GetProviderMeta(&m) - if err != nil { - return err - } - - if m.Foo != "bar" { - return fmt.Errorf("expected provider_meta.foo to be %q, was %q", - "bar", m.Foo) - } - return testResourceProviderMetaRead(d, meta) -} - -func testResourceProviderMetaDelete(d *schema.ResourceData, meta interface{}) error { - d.SetId("") - var m providerMeta - - err := d.GetProviderMeta(&m) - if err != nil { - return err - } - - if m.Foo != "bar" { - return fmt.Errorf("expected provider_meta.foo to be %q, was %q", - "bar", m.Foo) - } - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_provider_meta_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_provider_meta_test.go deleted file mode 100644 index 3b92d0a4..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_provider_meta_test.go +++ /dev/null @@ -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" { -} - `), - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_required_min.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_required_min.go deleted file mode 100644 index 413d4c51..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_required_min.go +++ /dev/null @@ -1,68 +0,0 @@ -package test - -import ( - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceRequiredMin() *schema.Resource { - return &schema.Resource{ - Create: testResourceRequiredMinCreate, - Read: testResourceRequiredMinRead, - Update: testResourceRequiredMinUpdate, - Delete: testResourceRequiredMinDelete, - - CustomizeDiff: func(d *schema.ResourceDiff, _ interface{}) error { - if d.HasChange("dependent_list") { - d.SetNewComputed("computed_list") - } - return nil - }, - - Schema: map[string]*schema.Schema{ - "min_items": { - Type: schema.TypeList, - Optional: true, - MinItems: 2, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "val": { - Type: schema.TypeString, - Required: true, - }, - }, - }, - }, - "required_min_items": { - Type: schema.TypeList, - Required: true, - MinItems: 2, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "val": { - Type: schema.TypeString, - Required: true, - }, - }, - }, - }, - }, - } -} - -func testResourceRequiredMinCreate(d *schema.ResourceData, meta interface{}) error { - d.SetId("testId") - return testResourceRequiredMinRead(d, meta) -} - -func testResourceRequiredMinRead(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func testResourceRequiredMinUpdate(d *schema.ResourceData, meta interface{}) error { - return testResourceRequiredMinRead(d, meta) -} - -func testResourceRequiredMinDelete(d *schema.ResourceData, meta interface{}) error { - d.SetId("") - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_required_min_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_required_min_test.go deleted file mode 100644 index 180c28cc..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_required_min_test.go +++ /dev/null @@ -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 - } - } -} - `), - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_signal.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_signal.go deleted file mode 100644 index 57e4bf0e..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_signal.go +++ /dev/null @@ -1,43 +0,0 @@ -package test - -import ( - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceSignal() *schema.Resource { - return &schema.Resource{ - Create: testResourceSignalCreate, - Read: testResourceSignalRead, - Update: testResourceSignalUpdate, - Delete: testResourceSignalDelete, - - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - - Schema: map[string]*schema.Schema{ - "optional": { - Type: schema.TypeString, - Optional: true, - }, - }, - } -} - -func testResourceSignalCreate(d *schema.ResourceData, meta interface{}) error { - d.SetId("testId") - - return testResourceSignalRead(d, meta) -} - -func testResourceSignalRead(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func testResourceSignalUpdate(d *schema.ResourceData, meta interface{}) error { - return testResourceSignalRead(d, meta) -} - -func testResourceSignalDelete(d *schema.ResourceData, meta interface{}) error { - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_state_func.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_state_func.go deleted file mode 100644 index 609e5ea5..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_state_func.go +++ /dev/null @@ -1,118 +0,0 @@ -package test - -import ( - "crypto/sha1" - "encoding/hex" - "fmt" - "math/rand" - - "github.com/hashicorp/terraform/helper/hashcode" - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceStateFunc() *schema.Resource { - return &schema.Resource{ - Create: testResourceStateFuncCreate, - Read: testResourceStateFuncRead, - Update: testResourceStateFuncUpdate, - Delete: testResourceStateFuncDelete, - - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - - Schema: map[string]*schema.Schema{ - "optional": { - Type: schema.TypeString, - Optional: true, - }, - "state_func": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - StateFunc: stateFuncHash, - }, - "state_func_value": { - Type: schema.TypeString, - Optional: true, - }, - - // set block with computed elements - "set_block": { - Type: schema.TypeSet, - Optional: true, - Set: setBlockHash, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "required": { - Type: schema.TypeString, - Required: true, - }, - "optional": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - }, - }, - }, - }, - } -} - -func stateFuncHash(v interface{}) string { - hash := sha1.Sum([]byte(v.(string))) - return hex.EncodeToString(hash[:]) -} - -func setBlockHash(v interface{}) int { - m := v.(map[string]interface{}) - required, _ := m["required"].(string) - optional, _ := m["optional"].(string) - return hashcode.String(fmt.Sprintf("%s|%s", required, optional)) -} - -func testResourceStateFuncCreate(d *schema.ResourceData, meta interface{}) error { - d.SetId(fmt.Sprintf("%x", rand.Int63())) - - // if we have a reference for the actual data in the state_func field, - // compare it - if data, ok := d.GetOk("state_func_value"); ok { - expected := data.(string) - got := d.Get("state_func").(string) - if expected != got { - return fmt.Errorf("expected state_func value:%q, got%q", expected, got) - } - } - - // Check that we can lookup set elements by our computed hash. - // This is not advised, but we can use this to make sure the final diff was - // prepared with the correct values. - setBlock, ok := d.GetOk("set_block") - if ok { - set := setBlock.(*schema.Set) - for _, obj := range set.List() { - idx := setBlockHash(obj) - requiredAddr := fmt.Sprintf("%s.%d.%s", "set_block", idx, "required") - _, ok := d.GetOkExists(requiredAddr) - if !ok { - return fmt.Errorf("failed to get attr %q from %#v", fmt.Sprintf(requiredAddr), d.State().Attributes) - } - } - } - - return testResourceStateFuncRead(d, meta) -} - -func testResourceStateFuncRead(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func testResourceStateFuncUpdate(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func testResourceStateFuncDelete(d *schema.ResourceData, meta interface{}) error { - d.SetId("") - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_state_func_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_state_func_test.go deleted file mode 100644 index cf5726ee..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_state_func_test.go +++ /dev/null @@ -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 - } -} - `), - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_test.go deleted file mode 100644 index 510b7816..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_test.go +++ /dev/null @@ -1,1220 +0,0 @@ -package test - -import ( - "reflect" - "regexp" - "strings" - "testing" - - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" -) - -func TestResource_basic(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } -} - `), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckNoResourceAttr( - "test_resource.foo", "list.#", - ), - ), - }, - }, - }) -} - -func TestResource_changedList(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - { - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } -} - `), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckNoResourceAttr( - "test_resource.foo", "list.#", - ), - ), - }, - { - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - list = ["a"] -} - `), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "test_resource.foo", "list.#", "1", - ), - resource.TestCheckResourceAttr( - "test_resource.foo", "list.0", "a", - ), - ), - }, - { - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - list = ["a", "b"] -} - `), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "test_resource.foo", "list.#", "2", - ), - resource.TestCheckResourceAttr( - "test_resource.foo", "list.0", "a", - ), - resource.TestCheckResourceAttr( - "test_resource.foo", "list.1", "b", - ), - ), - }, - { - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - list = ["b"] -} - `), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "test_resource.foo", "list.#", "1", - ), - resource.TestCheckResourceAttr( - "test_resource.foo", "list.0", "b", - ), - ), - }, - }, - }) -} - -// Targeted test in TestContext2Apply_ignoreChangesCreate -func TestResource_ignoreChangesRequired(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - lifecycle { - ignore_changes = ["required"] - } -} - `), - Check: func(s *terraform.State) error { - return nil - }, - }, - }, - }) -} - -func TestResource_ignoreChangesEmpty(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - optional_force_new = "one" - lifecycle { - ignore_changes = [] - } -} - `), - Check: func(s *terraform.State) error { - return nil - }, - }, - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - optional_force_new = "two" - lifecycle { - ignore_changes = [] - } -} - `), - Check: func(s *terraform.State) error { - return nil - }, - }, - }, - }) -} - -func TestResource_ignoreChangesForceNew(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - optional_force_new = "one" - lifecycle { - ignore_changes = ["optional_force_new"] - } -} - `), - Check: func(s *terraform.State) error { - return nil - }, - }, - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - optional_force_new = "two" - lifecycle { - ignore_changes = ["optional_force_new"] - } -} - `), - Check: func(s *terraform.State) error { - return nil - }, - }, - }, - }) -} - -// Covers specific scenario in #6005, handled by normalizing boolean strings in -// helper/schema -func TestResource_ignoreChangesForceNewBoolean(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - optional_force_new = "one" - optional_bool = true - lifecycle { - ignore_changes = ["optional_force_new"] - } -} - `), - }, - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - optional_force_new = "two" - optional_bool = true - lifecycle { - ignore_changes = ["optional_force_new"] - } -} - `), - Check: func(s *terraform.State) error { - return nil - }, - }, - }, - }) -} - -func TestResource_ignoreChangesMap(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - optional_computed_map = { - foo = "bar" - } - lifecycle { - ignore_changes = ["optional_computed_map"] - } -} - `), - Check: func(s *terraform.State) error { - return nil - }, - }, - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - optional_computed_map = { - foo = "bar" - no = "update" - } - lifecycle { - ignore_changes = ["optional_computed_map"] - } -} - `), - Check: func(s *terraform.State) error { - return nil - }, - }, - }, - }) -} - -func TestResource_ignoreChangesDependent(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - count = 2 - required = "yep" - required_map = { - key = "value" - } - - optional_force_new = "one" - lifecycle { - ignore_changes = ["optional_force_new"] - } -} -resource "test_resource" "bar" { - count = 2 - required = "yep" - required_map = { - key = "value" - } - optional = "${element(test_resource.foo.*.id, count.index)}" -} - `), - Check: func(s *terraform.State) error { - return nil - }, - }, - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - count = 2 - required = "yep" - required_map = { - key = "value" - } - - optional_force_new = "two" - lifecycle { - ignore_changes = ["optional_force_new"] - } -} -resource "test_resource" "bar" { - count = 2 - required = "yep" - required_map = { - key = "value" - } - optional = "${element(test_resource.foo.*.id, count.index)}" -} - `), - Check: func(s *terraform.State) error { - return nil - }, - }, - }, - }) -} - -func TestResource_ignoreChangesStillReplaced(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - optional_force_new = "one" - optional_bool = true - lifecycle { - ignore_changes = ["optional_bool"] - } -} - `), - Check: func(s *terraform.State) error { - return nil - }, - }, - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - optional_force_new = "two" - optional_bool = false - lifecycle { - ignore_changes = ["optional_bool"] - } -} - `), - Check: func(s *terraform.State) error { - return nil - }, - }, - }, - }) -} - -func TestResource_ignoreChangesCustomizeDiff(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - optional = "a" - lifecycle { - ignore_changes = [optional] - } -} - `), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "test_resource.foo", "planned_computed", "a", - ), - ), - }, - // On this step, `optional` changes, but `planned_computed` - // should remain as "a" because we have set `ignore_changes` - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - optional = "b" - lifecycle { - ignore_changes = [optional] - } -} - `), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "test_resource.foo", "planned_computed", "a", - ), - ), - }, - }, - }) -} - -// Reproduces plan-time panic when the wrong type is interpolated in a list of -// maps. -// TODO: this should return a type error, rather than silently setting an empty -// list -func TestResource_dataSourceListMapPanic(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "val" - required_map = {x = "y"} - list_of_map = "${var.maplist}" -} - -variable "maplist" { - type = "list" - - default = [ - {a = "b"} - ] -} - `), - ExpectError: nil, - Check: func(s *terraform.State) error { - return nil - }, - }, - }, - }) -} - -func TestResource_dataSourceIndexMapList(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "val" - - required_map = { - x = "y" - } - - list_of_map = [ - { - a = "1" - b = "2" - }, - { - c = "3" - d = "4" - }, - ] -} - -output "map_from_list" { - value = "${test_resource.foo.list_of_map[0]}" -} - -output "value_from_map_from_list" { - value = "${lookup(test_resource.foo.list_of_map[1], "d")}" -} - `), - ExpectError: nil, - Check: func(s *terraform.State) error { - root := s.ModuleByPath(addrs.RootModuleInstance) - mapOut := root.Outputs["map_from_list"].Value - expectedMapOut := map[string]interface{}{ - "a": "1", - "b": "2", - } - - valueOut := root.Outputs["value_from_map_from_list"].Value - expectedValueOut := "4" - - if !reflect.DeepEqual(mapOut, expectedMapOut) { - t.Fatalf("Expected: %#v\nGot: %#v", expectedMapOut, mapOut) - } - if !reflect.DeepEqual(valueOut, expectedValueOut) { - t.Fatalf("Expected: %#v\nGot: %#v", valueOut, expectedValueOut) - } - return nil - }, - }, - }, - }) -} - -func testAccCheckResourceDestroy(s *terraform.State) error { - return nil -} - -func TestResource_removeForceNew(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - optional_force_new = "here" -} - `), - }, - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } -} - `), - }, - }, - }) -} - -func TestResource_unknownFuncInMap(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "ok" - required_map = { - key = "${uuid()}" - } -} - `), - ExpectNonEmptyPlan: true, - }, - }, - }) -} - -// Verify that we can destroy when a managed resource references something with -// a count of 1. -func TestResource_countRefDestroyError(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: strings.TrimSpace(` -resource "test_resource" "one" { - count = 1 - required = "ok" - required_map = { - key = "val" - } -} - -resource "test_resource" "two" { - required = test_resource.one[0].id - required_map = { - key = "val" - } -} - `), - }, - }, - }) -} - -func TestResource_emptyMapValue(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "ok" - required_map = { - a = "a" - b = "" - } -} - `), - }, - }, - }) -} - -func TestResource_updateError(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "first" - required_map = { - a = "a" - } -} -`), - }, - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "second" - required_map = { - a = "a" - } - apply_error = "update_error" -} -`), - ExpectError: regexp.MustCompile("update_error"), - }, - }, - }) -} - -func TestResource_applyError(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "second" - required_map = { - a = "a" - } - apply_error = "apply_error" -} -`), - ExpectError: regexp.MustCompile("apply_error"), - }, - }, - }) -} - -func TestResource_emptyStrings(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "second" - required_map = { - a = "a" - } - - list = [""] -} -`), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("test_resource.foo", "list.0", ""), - ), - }, - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "second" - required_map = { - a = "a" - } - - list = ["", "b"] -} -`), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("test_resource.foo", "list.0", ""), - resource.TestCheckResourceAttr("test_resource.foo", "list.1", "b"), - ), - }, - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "second" - required_map = { - a = "a" - } - - list = [""] -} -`), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("test_resource.foo", "list.0", ""), - ), - }, - }, - }) -} - -func TestResource_setDrift(t *testing.T) { - testProvider := testAccProviders["test"] - res := testProvider.(*schema.Provider).ResourcesMap["test_resource"] - - // reset the Read function after the test - defer func() { - res.Read = testResourceRead - }() - - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "first" - required_map = { - a = "a" - } - set = ["a", "b"] -} -`), - Check: func(s *terraform.State) error { - return nil - }, - }, - resource.TestStep{ - PreConfig: func() { - // update the Read function to return the wrong "set" attribute values. - res.Read = func(d *schema.ResourceData, meta interface{}) error { - // update as expected first - if err := testResourceRead(d, meta); err != nil { - return err - } - d.Set("set", []interface{}{"a", "x"}) - return nil - } - }, - // Leave the config, so we can detect the mismatched set values. - // Updating the config would force the test to pass even if the Read - // function values were ignored. - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "second" - required_map = { - a = "a" - } - set = ["a", "b"] -} -`), - ExpectNonEmptyPlan: true, - }, - }, - }) -} - -func TestResource_optionalComputedMap(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - optional_computed_map = { - foo = "bar" - baz = "" - } -} - `), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "test_resource.foo", "optional_computed_map.foo", "bar", - ), - resource.TestCheckResourceAttr( - "test_resource.foo", "optional_computed_map.baz", "", - ), - ), - }, - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - optional_computed_map = {} -} - `), - // removing the map from the config should still leave an empty computed map - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "test_resource.foo", "optional_computed_map.%", "0", - ), - ), - }, - }, - }) -} - -func TestResource_plannedComputed(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "ok" - required_map = { - key = "value" - } - optional = "hi" -} - `), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "test_resource.foo", "planned_computed", "hi", - ), - ), - }, - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "ok" - required_map = { - key = "value" - } - optional = "changed" -} - `), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "test_resource.foo", "planned_computed", "changed", - ), - ), - }, - }, - }) -} - -func TestDiffApply_map(t *testing.T) { - resSchema := map[string]*schema.Schema{ - "map": { - Type: schema.TypeMap, - Optional: true, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - } - - priorAttrs := map[string]string{ - "id": "ok", - "map.%": "2", - "map.foo": "bar", - "map.bar": "", - } - - diff := &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "map.foo": &terraform.ResourceAttrDiff{Old: "bar", New: "", NewRemoved: true}, - "map.bar": &terraform.ResourceAttrDiff{Old: "", New: "", NewRemoved: true}, - }, - } - - newAttrs, err := diff.Apply(priorAttrs, (&schema.Resource{Schema: resSchema}).CoreConfigSchema()) - if err != nil { - t.Fatal(err) - } - - expect := map[string]string{ - "id": "ok", - "map.%": "0", - } - - if !reflect.DeepEqual(newAttrs, expect) { - t.Fatalf("expected:%#v got:%#v", expect, newAttrs) - } -} - -func TestResource_dependsComputed(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -variable "change" { - default = false -} - -resource "test_resource" "foo" { - required = "ok" - required_map = { - key = "value" - } - optional = var.change ? "after" : "" -} - -resource "test_resource" "bar" { - count = var.change ? 1 : 0 - required = test_resource.foo.planned_computed - required_map = { - key = "value" - } -} - `), - }, - resource.TestStep{ - Config: strings.TrimSpace(` -variable "change" { - default = true -} - -resource "test_resource" "foo" { - required = "ok" - required_map = { - key = "value" - } - optional = var.change ? "after" : "" -} - -resource "test_resource" "bar" { - count = var.change ? 1 : 0 - required = test_resource.foo.planned_computed - required_map = { - key = "value" - } -} - `), - }, - }, - }) -} - -func TestResource_optionalComputedBool(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } -} - `), - }, - }, - }) -} - -func TestResource_replacedOptionalComputed(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource_nested" "a" { -} - -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - optional_computed = test_resource_nested.a.id -} - `), - }, - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource_nested" "b" { -} - -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - optional_computed = test_resource_nested.b.id -} - `), - }, - }, - }) -} - -func TestResource_floatInIntAttr(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - int = 40.2 -} - `), - ExpectError: regexp.MustCompile(`must be a whole number, got 40.2`), - }, - }, - }) -} - -func TestResource_unsetNil(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - optional = "a" -} - `), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("test_resource.foo", "optional", "a"), - ), - }, - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } -} - `), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("test_resource.foo", "optional", ""), - ), - }, - }, - }) -} - -// Verify we can use use numeric indices in `ignore_changes` paths. -func TestResource_ignoreChangesIndex(t *testing.T) { - resource.UnitTest(t, resource.TestCase{ - Providers: testAccProviders, - CheckDestroy: testAccCheckResourceDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - list_of_map = [ - { - a = "b" - } - ] - - lifecycle { - ignore_changes = [list_of_map[0]["a"]] - } -} - `), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "test_resource.foo", "list_of_map.0.a", "b", - ), - ), - }, - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - list_of_map = [ - { - a = "c" - } - ] - - lifecycle { - ignore_changes = [list_of_map[0]["a"]] - } -} - `), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "test_resource.foo", "list_of_map.0.a", "b", - ), - ), - }, - // set ignore_changes to a prefix of the changed value - resource.TestStep{ - Config: strings.TrimSpace(` -resource "test_resource" "foo" { - required = "yep" - required_map = { - key = "value" - } - list_of_map = [ - { - a = "d" - } - ] - - lifecycle { - ignore_changes = [list_of_map[0]] - } -} - `), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr( - "test_resource.foo", "list_of_map.0.a", "b", - ), - ), - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_timeout.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_timeout.go deleted file mode 100644 index a1071755..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_timeout.go +++ /dev/null @@ -1,125 +0,0 @@ -package test - -import ( - "fmt" - "time" - - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceTimeout() *schema.Resource { - return &schema.Resource{ - Create: testResourceTimeoutCreate, - Read: testResourceTimeoutRead, - Update: testResourceTimeoutUpdate, - Delete: testResourceTimeoutDelete, - - // Due to the schema version also being stashed in the private/meta - // data, we need to ensure that it does not overwrite the map - // containing the timeouts. - SchemaVersion: 1, - - Timeouts: &schema.ResourceTimeout{ - Create: schema.DefaultTimeout(time.Second), - Update: schema.DefaultTimeout(time.Second), - Delete: schema.DefaultTimeout(time.Second), - }, - - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - - Schema: map[string]*schema.Schema{ - "create_delay": { - Type: schema.TypeString, - Optional: true, - }, - "read_delay": { - Type: schema.TypeString, - Optional: true, - }, - "update_delay": { - Type: schema.TypeString, - Optional: true, - }, - "delete_delay": { - Type: schema.TypeString, - Optional: true, - }, - }, - } -} - -func testResourceTimeoutCreate(d *schema.ResourceData, meta interface{}) error { - delayString := d.Get("create_delay").(string) - var delay time.Duration - var err error - if delayString != "" { - delay, err = time.ParseDuration(delayString) - if err != nil { - return err - } - } - - if delay > d.Timeout(schema.TimeoutCreate) { - return fmt.Errorf("timeout while creating resource") - } - - d.SetId("testId") - - return testResourceRead(d, meta) -} - -func testResourceTimeoutRead(d *schema.ResourceData, meta interface{}) error { - delayString := d.Get("read_delay").(string) - var delay time.Duration - var err error - if delayString != "" { - delay, err = time.ParseDuration(delayString) - if err != nil { - return err - } - } - - if delay > d.Timeout(schema.TimeoutRead) { - return fmt.Errorf("timeout while reading resource") - } - - return nil -} - -func testResourceTimeoutUpdate(d *schema.ResourceData, meta interface{}) error { - delayString := d.Get("update_delay").(string) - var delay time.Duration - var err error - if delayString != "" { - delay, err = time.ParseDuration(delayString) - if err != nil { - return err - } - } - - if delay > d.Timeout(schema.TimeoutUpdate) { - return fmt.Errorf("timeout while updating resource") - } - return nil -} - -func testResourceTimeoutDelete(d *schema.ResourceData, meta interface{}) error { - delayString := d.Get("delete_delay").(string) - var delay time.Duration - var err error - if delayString != "" { - delay, err = time.ParseDuration(delayString) - if err != nil { - return err - } - } - - if delay > d.Timeout(schema.TimeoutDelete) { - return fmt.Errorf("timeout while deleting resource") - } - - d.SetId("") - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_timeout_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_timeout_test.go deleted file mode 100644 index 312a37a7..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_timeout_test.go +++ /dev/null @@ -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"), - }, - }, - }) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_undeletable.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_undeletable.go deleted file mode 100644 index e5c9bb3b..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_undeletable.go +++ /dev/null @@ -1,30 +0,0 @@ -package test - -import ( - "fmt" - - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceUndeleteable() *schema.Resource { - return &schema.Resource{ - Create: testResourceUndeleteableCreate, - Read: testResourceUndeleteableRead, - Delete: testResourceUndeleteableDelete, - - Schema: map[string]*schema.Schema{}, - } -} - -func testResourceUndeleteableCreate(d *schema.ResourceData, meta interface{}) error { - d.SetId("placeholder") - return testResourceUndeleteableRead(d, meta) -} - -func testResourceUndeleteableRead(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func testResourceUndeleteableDelete(d *schema.ResourceData, meta interface{}) error { - return fmt.Errorf("test_undeleteable always fails deletion (use terraform state rm if you really want to delete it)") -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_with_custom_diff.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_with_custom_diff.go deleted file mode 100644 index 10756548..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_with_custom_diff.go +++ /dev/null @@ -1,154 +0,0 @@ -package test - -import ( - "fmt" - - "github.com/hashicorp/terraform/helper/schema" -) - -func testResourceCustomDiff() *schema.Resource { - return &schema.Resource{ - Create: testResourceCustomDiffCreate, - Read: testResourceCustomDiffRead, - CustomizeDiff: testResourceCustomDiffCustomizeDiff, - Update: testResourceCustomDiffUpdate, - Delete: testResourceCustomDiffDelete, - Schema: map[string]*schema.Schema{ - "required": { - Type: schema.TypeString, - Required: true, - }, - "computed": { - Type: schema.TypeInt, - Computed: true, - }, - "index": { - Type: schema.TypeInt, - Computed: true, - }, - "veto": { - Type: schema.TypeBool, - Optional: true, - }, - "list": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - }, - } -} - -type listDiffCases struct { - Type string - Value string -} - -func testListDiffCases(index int) []listDiffCases { - switch index { - case 0: - return []listDiffCases{ - { - Type: "add", - Value: "dc1", - }, - } - case 1: - return []listDiffCases{ - { - Type: "remove", - Value: "dc1", - }, - { - Type: "add", - Value: "dc2", - }, - { - Type: "add", - Value: "dc3", - }, - } - } - return nil -} - -func testListDiffCasesReadResult(index int) []interface{} { - switch index { - case 1: - return []interface{}{"dc1"} - default: - return []interface{}{"dc2", "dc3"} - } -} - -func testResourceCustomDiffCreate(d *schema.ResourceData, meta interface{}) error { - d.SetId("testId") - - // Required must make it through to Create - if _, ok := d.GetOk("required"); !ok { - return fmt.Errorf("missing attribute 'required', but it's required") - } - - _, new := d.GetChange("computed") - expected := new.(int) - 1 - actual := d.Get("index").(int) - if expected != actual { - return fmt.Errorf("expected computed to be 1 ahead of index, got computed: %d, index: %d", expected, actual) - } - d.Set("index", new) - - return testResourceCustomDiffRead(d, meta) -} - -func testResourceCustomDiffRead(d *schema.ResourceData, meta interface{}) error { - if err := d.Set("list", testListDiffCasesReadResult(d.Get("index").(int))); err != nil { - return err - } - return nil -} - -func testResourceCustomDiffCustomizeDiff(d *schema.ResourceDiff, meta interface{}) error { - if d.Get("veto").(bool) == true { - return fmt.Errorf("veto is true, diff vetoed") - } - // Note that this gets put into state after the update, regardless of whether - // or not anything is acted upon in the diff. - d.SetNew("computed", d.Get("computed").(int)+1) - - // This tests a diffed list, based off of the value of index - dcs := testListDiffCases(d.Get("index").(int)) - s := d.Get("list").([]interface{}) - for _, dc := range dcs { - switch dc.Type { - case "add": - s = append(s, dc.Value) - case "remove": - for i := range s { - if s[i].(string) == dc.Value { - copy(s[i:], s[i+1:]) - s = s[:len(s)-1] - break - } - } - } - } - d.SetNew("list", s) - - return nil -} - -func testResourceCustomDiffUpdate(d *schema.ResourceData, meta interface{}) error { - _, new := d.GetChange("computed") - expected := new.(int) - 1 - actual := d.Get("index").(int) - if expected != actual { - return fmt.Errorf("expected computed to be 1 ahead of index, got computed: %d, index: %d", expected, actual) - } - d.Set("index", new) - return testResourceCustomDiffRead(d, meta) -} - -func testResourceCustomDiffDelete(d *schema.ResourceData, meta interface{}) error { - d.SetId("") - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_with_custom_diff_test.go b/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_with_custom_diff_test.go deleted file mode 100644 index 05982bec..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/providers/test/resource_with_custom_diff_test.go +++ /dev/null @@ -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) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/linux_provisioner.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/linux_provisioner.go deleted file mode 100644 index 39996770..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/linux_provisioner.go +++ /dev/null @@ -1,115 +0,0 @@ -package chef - -import ( - "fmt" - "path" - "strings" - - "github.com/hashicorp/terraform/communicator" - "github.com/hashicorp/terraform/terraform" -) - -const ( - chmod = "find %s -maxdepth 1 -type f -exec /bin/chmod %d {} +" - installURL = "https://omnitruck.chef.io/install.sh" -) - -func (p *provisioner) linuxInstallChefClient(o terraform.UIOutput, comm communicator.Communicator) error { - // Build up the command prefix - prefix := "" - if p.HTTPProxy != "" { - prefix += fmt.Sprintf("http_proxy='%s' ", p.HTTPProxy) - } - if p.HTTPSProxy != "" { - prefix += fmt.Sprintf("https_proxy='%s' ", p.HTTPSProxy) - } - if len(p.NOProxy) > 0 { - prefix += fmt.Sprintf("no_proxy='%s' ", strings.Join(p.NOProxy, ",")) - } - - // First download the install.sh script from Chef - err := p.runCommand(o, comm, fmt.Sprintf("%scurl -LO %s", prefix, installURL)) - if err != nil { - return err - } - - // Then execute the install.sh scrip to download and install Chef Client - err = p.runCommand(o, comm, fmt.Sprintf("%sbash ./install.sh -v %q -c %s", prefix, p.Version, p.Channel)) - if err != nil { - return err - } - - // And finally cleanup the install.sh script again - return p.runCommand(o, comm, fmt.Sprintf("%srm -f install.sh", prefix)) -} - -func (p *provisioner) linuxCreateConfigFiles(o terraform.UIOutput, comm communicator.Communicator) error { - // Make sure the config directory exists - if err := p.runCommand(o, comm, "mkdir -p "+linuxConfDir); err != nil { - return err - } - - // Make sure we have enough rights to upload the files if using sudo - if p.useSudo { - if err := p.runCommand(o, comm, "chmod 777 "+linuxConfDir); err != nil { - return err - } - if err := p.runCommand(o, comm, fmt.Sprintf(chmod, linuxConfDir, 666)); err != nil { - return err - } - } - - if err := p.deployConfigFiles(o, comm, linuxConfDir); err != nil { - return err - } - - if len(p.OhaiHints) > 0 { - // Make sure the hits directory exists - hintsDir := path.Join(linuxConfDir, "ohai/hints") - if err := p.runCommand(o, comm, "mkdir -p "+hintsDir); err != nil { - return err - } - - // Make sure we have enough rights to upload the hints if using sudo - if p.useSudo { - if err := p.runCommand(o, comm, "chmod 777 "+hintsDir); err != nil { - return err - } - if err := p.runCommand(o, comm, fmt.Sprintf(chmod, hintsDir, 666)); err != nil { - return err - } - } - - if err := p.deployOhaiHints(o, comm, hintsDir); err != nil { - return err - } - - // When done copying the hints restore the rights and make sure root is owner - if p.useSudo { - if err := p.runCommand(o, comm, "chmod 755 "+hintsDir); err != nil { - return err - } - if err := p.runCommand(o, comm, fmt.Sprintf(chmod, hintsDir, 600)); err != nil { - return err - } - if err := p.runCommand(o, comm, "chown -R root:root "+hintsDir); err != nil { - return err - } - } - } - - // When done copying all files restore the rights and make sure root is owner - if p.useSudo { - if err := p.runCommand(o, comm, "chmod 755 "+linuxConfDir); err != nil { - return err - } - if err := p.runCommand(o, comm, fmt.Sprintf(chmod, linuxConfDir, 600)); err != nil { - return err - } - if err := p.runCommand(o, comm, "chown -R root:root "+linuxConfDir); err != nil { - return err - } - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/linux_provisioner_test.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/linux_provisioner_test.go deleted file mode 100644 index 6bf43b57..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/linux_provisioner_test.go +++ /dev/null @@ -1,330 +0,0 @@ -package chef - -import ( - "fmt" - "path" - "testing" - - "github.com/hashicorp/terraform/communicator" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" -) - -func TestResourceProvider_linuxInstallChefClient(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - Commands map[string]bool - }{ - "Sudo": { - Config: map[string]interface{}{ - "node_name": "nodename1", - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - Commands: map[string]bool{ - "sudo curl -LO https://omnitruck.chef.io/install.sh": true, - "sudo bash ./install.sh -v \"\" -c stable": true, - "sudo rm -f install.sh": true, - }, - }, - - "NoSudo": { - Config: map[string]interface{}{ - "node_name": "nodename1", - "prevent_sudo": true, - "run_list": []interface{}{"cookbook::recipe"}, - "secret_key": "SECRET-KEY", - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - Commands: map[string]bool{ - "curl -LO https://omnitruck.chef.io/install.sh": true, - "bash ./install.sh -v \"\" -c stable": true, - "rm -f install.sh": true, - }, - }, - - "HTTPProxy": { - Config: map[string]interface{}{ - "http_proxy": "http://proxy.local", - "node_name": "nodename1", - "prevent_sudo": true, - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - Commands: map[string]bool{ - "http_proxy='http://proxy.local' curl -LO https://omnitruck.chef.io/install.sh": true, - "http_proxy='http://proxy.local' bash ./install.sh -v \"\" -c stable": true, - "http_proxy='http://proxy.local' rm -f install.sh": true, - }, - }, - - "HTTPSProxy": { - Config: map[string]interface{}{ - "https_proxy": "https://proxy.local", - "node_name": "nodename1", - "prevent_sudo": true, - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - Commands: map[string]bool{ - "https_proxy='https://proxy.local' curl -LO https://omnitruck.chef.io/install.sh": true, - "https_proxy='https://proxy.local' bash ./install.sh -v \"\" -c stable": true, - "https_proxy='https://proxy.local' rm -f install.sh": true, - }, - }, - - "NoProxy": { - Config: map[string]interface{}{ - "http_proxy": "http://proxy.local", - "no_proxy": []interface{}{"http://local.local", "http://local.org"}, - "node_name": "nodename1", - "prevent_sudo": true, - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - Commands: map[string]bool{ - "http_proxy='http://proxy.local' no_proxy='http://local.local,http://local.org' " + - "curl -LO https://omnitruck.chef.io/install.sh": true, - "http_proxy='http://proxy.local' no_proxy='http://local.local,http://local.org' " + - "bash ./install.sh -v \"\" -c stable": true, - "http_proxy='http://proxy.local' no_proxy='http://local.local,http://local.org' " + - "rm -f install.sh": true, - }, - }, - - "Version": { - Config: map[string]interface{}{ - "node_name": "nodename1", - "prevent_sudo": true, - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - "version": "11.18.6", - }, - - Commands: map[string]bool{ - "curl -LO https://omnitruck.chef.io/install.sh": true, - "bash ./install.sh -v \"11.18.6\" -c stable": true, - "rm -f install.sh": true, - }, - }, - - "Channel": { - Config: map[string]interface{}{ - "channel": "current", - "node_name": "nodename1", - "prevent_sudo": true, - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - "version": "11.18.6", - }, - - Commands: map[string]bool{ - "curl -LO https://omnitruck.chef.io/install.sh": true, - "bash ./install.sh -v \"11.18.6\" -c current": true, - "rm -f install.sh": true, - }, - }, - } - - o := new(terraform.MockUIOutput) - c := new(communicator.MockCommunicator) - - for k, tc := range cases { - c.Commands = tc.Commands - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - p.useSudo = !p.PreventSudo - - err = p.linuxInstallChefClient(o, c) - if err != nil { - t.Fatalf("Test %q failed: %v", k, err) - } - } -} - -func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - Commands map[string]bool - Uploads map[string]string - }{ - "Sudo": { - Config: map[string]interface{}{ - "ohai_hints": []interface{}{"testdata/ohaihint.json"}, - "node_name": "nodename1", - "run_list": []interface{}{"cookbook::recipe"}, - "secret_key": "SECRET-KEY", - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - Commands: map[string]bool{ - "sudo mkdir -p " + linuxConfDir: true, - "sudo chmod 777 " + linuxConfDir: true, - "sudo " + fmt.Sprintf(chmod, linuxConfDir, 666): true, - "sudo mkdir -p " + path.Join(linuxConfDir, "ohai/hints"): true, - "sudo chmod 777 " + path.Join(linuxConfDir, "ohai/hints"): true, - "sudo " + fmt.Sprintf(chmod, path.Join(linuxConfDir, "ohai/hints"), 666): true, - "sudo chmod 755 " + path.Join(linuxConfDir, "ohai/hints"): true, - "sudo " + fmt.Sprintf(chmod, path.Join(linuxConfDir, "ohai/hints"), 600): true, - "sudo chown -R root:root " + path.Join(linuxConfDir, "ohai/hints"): true, - "sudo chmod 755 " + linuxConfDir: true, - "sudo " + fmt.Sprintf(chmod, linuxConfDir, 600): true, - "sudo chown -R root:root " + linuxConfDir: true, - }, - - Uploads: map[string]string{ - linuxConfDir + "/client.rb": defaultLinuxClientConf, - linuxConfDir + "/encrypted_data_bag_secret": "SECRET-KEY", - linuxConfDir + "/first-boot.json": `{"run_list":["cookbook::recipe"]}`, - linuxConfDir + "/ohai/hints/ohaihint.json": "OHAI-HINT-FILE", - linuxConfDir + "/bob.pem": "USER-KEY", - }, - }, - - "NoSudo": { - Config: map[string]interface{}{ - "node_name": "nodename1", - "prevent_sudo": true, - "run_list": []interface{}{"cookbook::recipe"}, - "secret_key": "SECRET-KEY", - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - Commands: map[string]bool{ - "mkdir -p " + linuxConfDir: true, - }, - - Uploads: map[string]string{ - linuxConfDir + "/client.rb": defaultLinuxClientConf, - linuxConfDir + "/encrypted_data_bag_secret": "SECRET-KEY", - linuxConfDir + "/first-boot.json": `{"run_list":["cookbook::recipe"]}`, - linuxConfDir + "/bob.pem": "USER-KEY", - }, - }, - - "Proxy": { - Config: map[string]interface{}{ - "http_proxy": "http://proxy.local", - "https_proxy": "https://proxy.local", - "no_proxy": []interface{}{"http://local.local", "https://local.local"}, - "node_name": "nodename1", - "prevent_sudo": true, - "run_list": []interface{}{"cookbook::recipe"}, - "secret_key": "SECRET-KEY", - "server_url": "https://chef.local", - "ssl_verify_mode": "verify_none", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - Commands: map[string]bool{ - "mkdir -p " + linuxConfDir: true, - }, - - Uploads: map[string]string{ - linuxConfDir + "/client.rb": proxyLinuxClientConf, - linuxConfDir + "/encrypted_data_bag_secret": "SECRET-KEY", - linuxConfDir + "/first-boot.json": `{"run_list":["cookbook::recipe"]}`, - linuxConfDir + "/bob.pem": "USER-KEY", - }, - }, - - "Attributes JSON": { - Config: map[string]interface{}{ - "attributes_json": `{"key1":{"subkey1":{"subkey2a":["val1","val2","val3"],` + - `"subkey2b":{"subkey3":"value3"}}},"key2":"value2"}`, - "node_name": "nodename1", - "prevent_sudo": true, - "run_list": []interface{}{"cookbook::recipe"}, - "secret_key": "SECRET-KEY", - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - Commands: map[string]bool{ - "mkdir -p " + linuxConfDir: true, - }, - - Uploads: map[string]string{ - linuxConfDir + "/client.rb": defaultLinuxClientConf, - linuxConfDir + "/encrypted_data_bag_secret": "SECRET-KEY", - linuxConfDir + "/bob.pem": "USER-KEY", - linuxConfDir + "/first-boot.json": `{"key1":{"subkey1":{"subkey2a":["val1","val2","val3"],` + - `"subkey2b":{"subkey3":"value3"}}},"key2":"value2","run_list":["cookbook::recipe"]}`, - }, - }, - } - - o := new(terraform.MockUIOutput) - c := new(communicator.MockCommunicator) - - for k, tc := range cases { - c.Commands = tc.Commands - c.Uploads = tc.Uploads - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - p.useSudo = !p.PreventSudo - - err = p.linuxCreateConfigFiles(o, c) - if err != nil { - t.Fatalf("Test %q failed: %v", k, err) - } - } -} - -const defaultLinuxClientConf = `log_location STDOUT -chef_server_url "https://chef.local/" -node_name "nodename1"` - -const proxyLinuxClientConf = `log_location STDOUT -chef_server_url "https://chef.local/" -node_name "nodename1" - -http_proxy "http://proxy.local" -ENV['http_proxy'] = "http://proxy.local" -ENV['HTTP_PROXY'] = "http://proxy.local" - -https_proxy "https://proxy.local" -ENV['https_proxy'] = "https://proxy.local" -ENV['HTTPS_PROXY'] = "https://proxy.local" - -no_proxy "http://local.local,https://local.local" -ENV['no_proxy'] = "http://local.local,https://local.local" - -ssl_verify_mode :verify_none` diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/resource_provisioner.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/resource_provisioner.go deleted file mode 100644 index 6f2f3ae5..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/resource_provisioner.go +++ /dev/null @@ -1,904 +0,0 @@ -package chef - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io" - "log" - "os" - "path" - "regexp" - "strconv" - "strings" - "sync" - "text/template" - "time" - - "github.com/hashicorp/terraform/communicator" - "github.com/hashicorp/terraform/communicator/remote" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/go-homedir" - "github.com/mitchellh/go-linereader" -) - -const ( - clienrb = "client.rb" - defaultEnv = "_default" - firstBoot = "first-boot.json" - logfileDir = "logfiles" - linuxChefCmd = "chef-client" - linuxConfDir = "/etc/chef" - linuxNoOutput = "> /dev/null 2>&1" - linuxGemCmd = "/opt/chef/embedded/bin/gem" - linuxKnifeCmd = "knife" - secretKey = "encrypted_data_bag_secret" - windowsChefCmd = "cmd /c chef-client" - windowsConfDir = "C:/chef" - windowsNoOutput = "> nul 2>&1" - windowsGemCmd = "C:/opscode/chef/embedded/bin/gem" - windowsKnifeCmd = "cmd /c knife" -) - -const clientConf = ` -log_location STDOUT -chef_server_url "{{ .ServerURL }}" -node_name "{{ .NodeName }}" -{{ if .UsePolicyfile }} -use_policyfile true -policy_group "{{ .PolicyGroup }}" -policy_name "{{ .PolicyName }}" -{{ end -}} - -{{ if .HTTPProxy }} -http_proxy "{{ .HTTPProxy }}" -ENV['http_proxy'] = "{{ .HTTPProxy }}" -ENV['HTTP_PROXY'] = "{{ .HTTPProxy }}" -{{ end -}} - -{{ if .HTTPSProxy }} -https_proxy "{{ .HTTPSProxy }}" -ENV['https_proxy'] = "{{ .HTTPSProxy }}" -ENV['HTTPS_PROXY'] = "{{ .HTTPSProxy }}" -{{ end -}} - -{{ if .NOProxy }} -no_proxy "{{ join .NOProxy "," }}" -ENV['no_proxy'] = "{{ join .NOProxy "," }}" -{{ end -}} - -{{ if .SSLVerifyMode }} -ssl_verify_mode {{ .SSLVerifyMode }} -{{- end -}} - -{{ if .DisableReporting }} -enable_reporting false -{{ end -}} - -{{ if .ClientOptions }} -{{ join .ClientOptions "\n" }} -{{ end }} -` - -type provisionFn func(terraform.UIOutput, communicator.Communicator) error - -type provisioner struct { - Attributes map[string]interface{} - Channel string - ClientOptions []string - DisableReporting bool - Environment string - FetchChefCertificates bool - LogToFile bool - UsePolicyfile bool - PolicyGroup string - PolicyName string - HTTPProxy string - HTTPSProxy string - MaxRetries int - NamedRunList string - NOProxy []string - NodeName string - OhaiHints []string - OSType string - RecreateClient bool - PreventSudo bool - RetryOnExitCode map[int]bool - RunList []string - SecretKey string - ServerURL string - SkipInstall bool - SkipRegister bool - SSLVerifyMode string - UserName string - UserKey string - Vaults map[string][]string - Version string - WaitForRetry time.Duration - - cleanupUserKeyCmd string - createConfigFiles provisionFn - installChefClient provisionFn - fetchChefCertificates provisionFn - generateClientKey provisionFn - configureVaults provisionFn - runChefClient provisionFn - useSudo bool -} - -// Provisioner returns a Chef provisioner -func Provisioner() terraform.ResourceProvisioner { - return &schema.Provisioner{ - Schema: map[string]*schema.Schema{ - "node_name": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - "server_url": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - "user_name": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - "user_key": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - - "attributes_json": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "channel": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: "stable", - }, - "client_options": &schema.Schema{ - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - }, - "disable_reporting": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - }, - "environment": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: defaultEnv, - }, - "fetch_chef_certificates": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - }, - "log_to_file": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - }, - "use_policyfile": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - }, - "policy_group": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "policy_name": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "http_proxy": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "https_proxy": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "max_retries": &schema.Schema{ - Type: schema.TypeInt, - Optional: true, - Default: 0, - }, - "no_proxy": &schema.Schema{ - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - }, - "named_run_list": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "ohai_hints": &schema.Schema{ - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - }, - "os_type": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "prevent_sudo": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - }, - "recreate_client": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - }, - "retry_on_exit_code": &schema.Schema{ - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeInt}, - Optional: true, - }, - "run_list": &schema.Schema{ - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - }, - "secret_key": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "skip_install": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - }, - "skip_register": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - }, - "ssl_verify_mode": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "vault_json": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "version": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "wait_for_retry": &schema.Schema{ - Type: schema.TypeInt, - Optional: true, - Default: 30, - }, - }, - - ApplyFunc: applyFn, - ValidateFunc: validateFn, - } -} - -// TODO: Support context cancelling (Provisioner Stop) -func applyFn(ctx context.Context) error { - o := ctx.Value(schema.ProvOutputKey).(terraform.UIOutput) - s := ctx.Value(schema.ProvRawStateKey).(*terraform.InstanceState) - d := ctx.Value(schema.ProvConfigDataKey).(*schema.ResourceData) - - // Decode the provisioner config - p, err := decodeConfig(d) - if err != nil { - return err - } - - if p.OSType == "" { - switch t := s.Ephemeral.ConnInfo["type"]; t { - case "ssh", "": // The default connection type is ssh, so if the type is empty assume ssh - p.OSType = "linux" - case "winrm": - p.OSType = "windows" - default: - return fmt.Errorf("Unsupported connection type: %s", t) - } - } - - // Set some values based on the targeted OS - switch p.OSType { - case "linux": - p.cleanupUserKeyCmd = fmt.Sprintf("rm -f %s", path.Join(linuxConfDir, p.UserName+".pem")) - p.createConfigFiles = p.linuxCreateConfigFiles - p.installChefClient = p.linuxInstallChefClient - p.fetchChefCertificates = p.fetchChefCertificatesFunc(linuxKnifeCmd, linuxConfDir) - p.generateClientKey = p.generateClientKeyFunc(linuxKnifeCmd, linuxConfDir, linuxNoOutput) - p.configureVaults = p.configureVaultsFunc(linuxGemCmd, linuxKnifeCmd, linuxConfDir) - p.runChefClient = p.runChefClientFunc(linuxChefCmd, linuxConfDir) - p.useSudo = !p.PreventSudo && s.Ephemeral.ConnInfo["user"] != "root" - case "windows": - p.cleanupUserKeyCmd = fmt.Sprintf("cd %s && del /F /Q %s", windowsConfDir, p.UserName+".pem") - p.createConfigFiles = p.windowsCreateConfigFiles - p.installChefClient = p.windowsInstallChefClient - p.fetchChefCertificates = p.fetchChefCertificatesFunc(windowsKnifeCmd, windowsConfDir) - p.generateClientKey = p.generateClientKeyFunc(windowsKnifeCmd, windowsConfDir, windowsNoOutput) - p.configureVaults = p.configureVaultsFunc(windowsGemCmd, windowsKnifeCmd, windowsConfDir) - p.runChefClient = p.runChefClientFunc(windowsChefCmd, windowsConfDir) - p.useSudo = false - default: - return fmt.Errorf("Unsupported os type: %s", p.OSType) - } - - // Get a new communicator - comm, err := communicator.New(s) - if err != nil { - return err - } - - retryCtx, cancel := context.WithTimeout(ctx, comm.Timeout()) - defer cancel() - - // Wait and retry until we establish the connection - err = communicator.Retry(retryCtx, func() error { - return comm.Connect(o) - }) - if err != nil { - return err - } - defer comm.Disconnect() - - // Make sure we always delete the user key from the new node! - var once sync.Once - cleanupUserKey := func() { - o.Output("Cleanup user key...") - if err := p.runCommand(o, comm, p.cleanupUserKeyCmd); err != nil { - o.Output("WARNING: Failed to cleanup user key on new node: " + err.Error()) - } - } - defer once.Do(cleanupUserKey) - - if !p.SkipInstall { - if err := p.installChefClient(o, comm); err != nil { - return err - } - } - - o.Output("Creating configuration files...") - if err := p.createConfigFiles(o, comm); err != nil { - return err - } - - if !p.SkipRegister { - if p.FetchChefCertificates { - o.Output("Fetch Chef certificates...") - if err := p.fetchChefCertificates(o, comm); err != nil { - return err - } - } - - o.Output("Generate the private key...") - if err := p.generateClientKey(o, comm); err != nil { - return err - } - } - - if p.Vaults != nil { - o.Output("Configure Chef vaults...") - if err := p.configureVaults(o, comm); err != nil { - return err - } - } - - // Cleanup the user key before we run Chef-Client to prevent issues - // with rights caused by changing settings during the run. - once.Do(cleanupUserKey) - - o.Output("Starting initial Chef-Client run...") - - for attempt := 0; attempt <= p.MaxRetries; attempt++ { - // We need a new retry context for each attempt, to make sure - // they all get the correct timeout. - retryCtx, cancel := context.WithTimeout(ctx, comm.Timeout()) - defer cancel() - - // Make sure to (re)connect before trying to run Chef-Client. - if err := communicator.Retry(retryCtx, func() error { - return comm.Connect(o) - }); err != nil { - return err - } - - err = p.runChefClient(o, comm) - if err == nil { - return nil - } - - // Allow RFC062 Exit Codes: - // https://github.com/chef/chef-rfc/blob/master/rfc062-exit-status.md - exitError, ok := err.(*remote.ExitError) - if !ok { - return err - } - - switch exitError.ExitStatus { - case 35: - o.Output("Reboot has been scheduled in the run state") - err = nil - case 37: - o.Output("Reboot needs to be completed") - err = nil - case 213: - o.Output("Chef has exited during a client upgrade") - err = nil - } - - if !p.RetryOnExitCode[exitError.ExitStatus] { - return err - } - - if attempt < p.MaxRetries { - o.Output(fmt.Sprintf("Waiting %s before retrying Chef-Client run...", p.WaitForRetry)) - time.Sleep(p.WaitForRetry) - } - } - - return err -} - -func validateFn(c *terraform.ResourceConfig) (ws []string, es []error) { - usePolicyFile := false - if usePolicyFileRaw, ok := c.Get("use_policyfile"); ok { - switch usePolicyFileRaw := usePolicyFileRaw.(type) { - case bool: - usePolicyFile = usePolicyFileRaw - case string: - usePolicyFileBool, err := strconv.ParseBool(usePolicyFileRaw) - if err != nil { - return ws, append(es, errors.New("\"use_policyfile\" must be a boolean")) - } - usePolicyFile = usePolicyFileBool - default: - return ws, append(es, errors.New("\"use_policyfile\" must be a boolean")) - } - } - - if !usePolicyFile && !c.IsSet("run_list") { - es = append(es, errors.New("\"run_list\": required field is not set")) - } - if usePolicyFile && !c.IsSet("policy_name") { - es = append(es, errors.New("using policyfile, but \"policy_name\" not set")) - } - if usePolicyFile && !c.IsSet("policy_group") { - es = append(es, errors.New("using policyfile, but \"policy_group\" not set")) - } - - return ws, es -} - -func (p *provisioner) deployConfigFiles(o terraform.UIOutput, comm communicator.Communicator, confDir string) error { - // Copy the user key to the new instance - pk := strings.NewReader(p.UserKey) - if err := comm.Upload(path.Join(confDir, p.UserName+".pem"), pk); err != nil { - return fmt.Errorf("Uploading user key failed: %v", err) - } - - if p.SecretKey != "" { - // Copy the secret key to the new instance - s := strings.NewReader(p.SecretKey) - if err := comm.Upload(path.Join(confDir, secretKey), s); err != nil { - return fmt.Errorf("Uploading %s failed: %v", secretKey, err) - } - } - - // Make sure the SSLVerifyMode value is written as a symbol - if p.SSLVerifyMode != "" && !strings.HasPrefix(p.SSLVerifyMode, ":") { - p.SSLVerifyMode = fmt.Sprintf(":%s", p.SSLVerifyMode) - } - - // Make strings.Join available for use within the template - funcMap := template.FuncMap{ - "join": strings.Join, - } - - // Create a new template and parse the client config into it - t := template.Must(template.New(clienrb).Funcs(funcMap).Parse(clientConf)) - - var buf bytes.Buffer - err := t.Execute(&buf, p) - if err != nil { - return fmt.Errorf("Error executing %s template: %s", clienrb, err) - } - - // Copy the client config to the new instance - if err = comm.Upload(path.Join(confDir, clienrb), &buf); err != nil { - return fmt.Errorf("Uploading %s failed: %v", clienrb, err) - } - - // Create a map with first boot settings - fb := make(map[string]interface{}) - if p.Attributes != nil { - fb = p.Attributes - } - - // Check if the run_list was also in the attributes and if so log a warning - // that it will be overwritten with the value of the run_list argument. - if _, found := fb["run_list"]; found { - log.Printf("[WARN] Found a 'run_list' specified in the configured attributes! " + - "This value will be overwritten by the value of the `run_list` argument!") - } - - // Add the initial runlist to the first boot settings - if !p.UsePolicyfile { - fb["run_list"] = p.RunList - } - - // Marshal the first boot settings to JSON - d, err := json.Marshal(fb) - if err != nil { - return fmt.Errorf("Failed to create %s data: %s", firstBoot, err) - } - - // Copy the first-boot.json to the new instance - if err := comm.Upload(path.Join(confDir, firstBoot), bytes.NewReader(d)); err != nil { - return fmt.Errorf("Uploading %s failed: %v", firstBoot, err) - } - - return nil -} - -func (p *provisioner) deployOhaiHints(o terraform.UIOutput, comm communicator.Communicator, hintDir string) error { - for _, hint := range p.OhaiHints { - // Open the hint file - f, err := os.Open(hint) - if err != nil { - return err - } - defer f.Close() - - // Copy the hint to the new instance - if err := comm.Upload(path.Join(hintDir, path.Base(hint)), f); err != nil { - return fmt.Errorf("Uploading %s failed: %v", path.Base(hint), err) - } - } - - return nil -} - -func (p *provisioner) fetchChefCertificatesFunc( - knifeCmd string, - confDir string) func(terraform.UIOutput, communicator.Communicator) error { - return func(o terraform.UIOutput, comm communicator.Communicator) error { - clientrb := path.Join(confDir, clienrb) - cmd := fmt.Sprintf("%s ssl fetch -c %s", knifeCmd, clientrb) - - return p.runCommand(o, comm, cmd) - } -} - -func (p *provisioner) generateClientKeyFunc(knifeCmd string, confDir string, noOutput string) provisionFn { - return func(o terraform.UIOutput, comm communicator.Communicator) error { - options := fmt.Sprintf("-c %s -u %s --key %s", - path.Join(confDir, clienrb), - p.UserName, - path.Join(confDir, p.UserName+".pem"), - ) - - // See if we already have a node object - getNodeCmd := fmt.Sprintf("%s node show %s %s %s", knifeCmd, p.NodeName, options, noOutput) - node := p.runCommand(o, comm, getNodeCmd) == nil - - // See if we already have a client object - getClientCmd := fmt.Sprintf("%s client show %s %s %s", knifeCmd, p.NodeName, options, noOutput) - client := p.runCommand(o, comm, getClientCmd) == nil - - // If we have a client, we can only continue if we are to recreate the client - if client && !p.RecreateClient { - return fmt.Errorf( - "Chef client %q already exists, set recreate_client=true to automatically recreate the client", p.NodeName) - } - - // If the node exists, try to delete it - if node { - deleteNodeCmd := fmt.Sprintf("%s node delete %s -y %s", - knifeCmd, - p.NodeName, - options, - ) - if err := p.runCommand(o, comm, deleteNodeCmd); err != nil { - return err - } - } - - // If the client exists, try to delete it - if client { - deleteClientCmd := fmt.Sprintf("%s client delete %s -y %s", - knifeCmd, - p.NodeName, - options, - ) - if err := p.runCommand(o, comm, deleteClientCmd); err != nil { - return err - } - } - - // Create the new client object - createClientCmd := fmt.Sprintf("%s client create %s -d -f %s %s", - knifeCmd, - p.NodeName, - path.Join(confDir, "client.pem"), - options, - ) - - return p.runCommand(o, comm, createClientCmd) - } -} - -func (p *provisioner) configureVaultsFunc(gemCmd string, knifeCmd string, confDir string) provisionFn { - return func(o terraform.UIOutput, comm communicator.Communicator) error { - if err := p.runCommand(o, comm, fmt.Sprintf("%s install chef-vault", gemCmd)); err != nil { - return err - } - - options := fmt.Sprintf("-c %s -u %s --key %s", - path.Join(confDir, clienrb), - p.UserName, - path.Join(confDir, p.UserName+".pem"), - ) - - // if client gets recreated, remove (old) client (with old keys) from vaults/items - // otherwise, the (new) client (with new keys) will not be able to decrypt the vault - if p.RecreateClient { - for vault, items := range p.Vaults { - for _, item := range items { - deleteCmd := fmt.Sprintf("%s vault remove %s %s -C \"%s\" -M client %s", - knifeCmd, - vault, - item, - p.NodeName, - options, - ) - if err := p.runCommand(o, comm, deleteCmd); err != nil { - return err - } - } - } - } - - for vault, items := range p.Vaults { - for _, item := range items { - updateCmd := fmt.Sprintf("%s vault update %s %s -C %s -M client %s", - knifeCmd, - vault, - item, - p.NodeName, - options, - ) - if err := p.runCommand(o, comm, updateCmd); err != nil { - return err - } - } - } - - return nil - } -} - -func (p *provisioner) runChefClientFunc(chefCmd string, confDir string) provisionFn { - return func(o terraform.UIOutput, comm communicator.Communicator) error { - fb := path.Join(confDir, firstBoot) - var cmd string - - // Policyfiles do not support chef environments, so don't pass the `-E` flag. - switch { - case p.UsePolicyfile && p.NamedRunList == "": - cmd = fmt.Sprintf("%s -j %q", chefCmd, fb) - case p.UsePolicyfile && p.NamedRunList != "": - cmd = fmt.Sprintf("%s -j %q -n %q", chefCmd, fb, p.NamedRunList) - default: - cmd = fmt.Sprintf("%s -j %q -E %q", chefCmd, fb, p.Environment) - } - - if p.LogToFile { - if err := os.MkdirAll(logfileDir, 0755); err != nil { - return fmt.Errorf("Error creating logfile directory %s: %v", logfileDir, err) - } - - logFile := path.Join(logfileDir, p.NodeName) - f, err := os.Create(path.Join(logFile)) - if err != nil { - return fmt.Errorf("Error creating logfile %s: %v", logFile, err) - } - f.Close() - - o.Output("Writing Chef Client output to " + logFile) - o = p - } - - return p.runCommand(o, comm, cmd) - } -} - -// Output implementation of terraform.UIOutput interface -func (p *provisioner) Output(output string) { - logFile := path.Join(logfileDir, p.NodeName) - f, err := os.OpenFile(logFile, os.O_APPEND|os.O_WRONLY, 0666) - if err != nil { - log.Printf("Error creating logfile %s: %v", logFile, err) - return - } - defer f.Close() - - // These steps are needed to remove any ANSI escape codes used to colorize - // the output and to make sure we have proper line endings before writing - // the string to the logfile. - re := regexp.MustCompile(`\x1b\[[0-9;]+m`) - output = re.ReplaceAllString(output, "") - output = strings.Replace(output, "\r", "\n", -1) - - if _, err := f.WriteString(output); err != nil { - log.Printf("Error writing output to logfile %s: %v", logFile, err) - } - - if err := f.Sync(); err != nil { - log.Printf("Error saving logfile %s to disk: %v", logFile, err) - } -} - -// runCommand is used to run already prepared commands -func (p *provisioner) runCommand(o terraform.UIOutput, comm communicator.Communicator, command string) error { - // Unless prevented, prefix the command with sudo - if p.useSudo { - command = "sudo " + command - } - - outR, outW := io.Pipe() - errR, errW := io.Pipe() - go p.copyOutput(o, outR) - go p.copyOutput(o, errR) - defer outW.Close() - defer errW.Close() - - cmd := &remote.Cmd{ - Command: command, - Stdout: outW, - Stderr: errW, - } - - err := comm.Start(cmd) - if err != nil { - return fmt.Errorf("Error executing command %q: %v", cmd.Command, err) - } - - if err := cmd.Wait(); err != nil { - return err - } - - return nil -} - -func (p *provisioner) copyOutput(o terraform.UIOutput, r io.Reader) { - lr := linereader.New(r) - for line := range lr.Ch { - o.Output(line) - } -} - -func decodeConfig(d *schema.ResourceData) (*provisioner, error) { - p := &provisioner{ - Channel: d.Get("channel").(string), - ClientOptions: getStringList(d.Get("client_options")), - DisableReporting: d.Get("disable_reporting").(bool), - Environment: d.Get("environment").(string), - FetchChefCertificates: d.Get("fetch_chef_certificates").(bool), - LogToFile: d.Get("log_to_file").(bool), - UsePolicyfile: d.Get("use_policyfile").(bool), - PolicyGroup: d.Get("policy_group").(string), - PolicyName: d.Get("policy_name").(string), - HTTPProxy: d.Get("http_proxy").(string), - HTTPSProxy: d.Get("https_proxy").(string), - NOProxy: getStringList(d.Get("no_proxy")), - MaxRetries: d.Get("max_retries").(int), - NamedRunList: d.Get("named_run_list").(string), - NodeName: d.Get("node_name").(string), - OhaiHints: getStringList(d.Get("ohai_hints")), - OSType: d.Get("os_type").(string), - RecreateClient: d.Get("recreate_client").(bool), - PreventSudo: d.Get("prevent_sudo").(bool), - RetryOnExitCode: getRetryOnExitCodes(d), - RunList: getStringList(d.Get("run_list")), - SecretKey: d.Get("secret_key").(string), - ServerURL: d.Get("server_url").(string), - SkipInstall: d.Get("skip_install").(bool), - SkipRegister: d.Get("skip_register").(bool), - SSLVerifyMode: d.Get("ssl_verify_mode").(string), - UserName: d.Get("user_name").(string), - UserKey: d.Get("user_key").(string), - Version: d.Get("version").(string), - WaitForRetry: time.Duration(d.Get("wait_for_retry").(int)) * time.Second, - } - - // Make sure the supplied URL has a trailing slash - p.ServerURL = strings.TrimSuffix(p.ServerURL, "/") + "/" - - for i, hint := range p.OhaiHints { - hintPath, err := homedir.Expand(hint) - if err != nil { - return nil, fmt.Errorf("Error expanding the path %s: %v", hint, err) - } - p.OhaiHints[i] = hintPath - } - - if attrs, ok := d.GetOk("attributes_json"); ok { - var m map[string]interface{} - if err := json.Unmarshal([]byte(attrs.(string)), &m); err != nil { - return nil, fmt.Errorf("Error parsing attributes_json: %v", err) - } - p.Attributes = m - } - - if vaults, ok := d.GetOk("vault_json"); ok { - var m map[string]interface{} - if err := json.Unmarshal([]byte(vaults.(string)), &m); err != nil { - return nil, fmt.Errorf("Error parsing vault_json: %v", err) - } - - v := make(map[string][]string) - for vault, items := range m { - switch items := items.(type) { - case []interface{}: - for _, item := range items { - if item, ok := item.(string); ok { - v[vault] = append(v[vault], item) - } - } - case interface{}: - if item, ok := items.(string); ok { - v[vault] = append(v[vault], item) - } - } - } - - p.Vaults = v - } - - return p, nil -} - -func getRetryOnExitCodes(d *schema.ResourceData) map[int]bool { - result := make(map[int]bool) - - v, ok := d.GetOk("retry_on_exit_code") - if !ok || v == nil { - // Use default exit codes - result[35] = true - result[37] = true - result[213] = true - return result - } - - switch v := v.(type) { - case []interface{}: - for _, vv := range v { - if vv, ok := vv.(int); ok { - result[vv] = true - } - } - return result - default: - panic(fmt.Sprintf("Unsupported type: %T", v)) - } -} - -func getStringList(v interface{}) []string { - var result []string - - switch v := v.(type) { - case nil: - return result - case []interface{}: - for _, vv := range v { - if vv, ok := vv.(string); ok { - result = append(result, vv) - } - } - return result - default: - panic(fmt.Sprintf("Unsupported type: %T", v)) - } -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/resource_provisioner_test.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/resource_provisioner_test.go deleted file mode 100644 index bc9f2427..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/resource_provisioner_test.go +++ /dev/null @@ -1,435 +0,0 @@ -package chef - -import ( - "fmt" - "path" - "testing" - - "github.com/hashicorp/terraform/communicator" - "github.com/hashicorp/terraform/configs/hcl2shim" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" -) - -func TestResourceProvisioner_impl(t *testing.T) { - var _ terraform.ResourceProvisioner = Provisioner() -} - -func TestProvisioner(t *testing.T) { - if err := Provisioner().(*schema.Provisioner).InternalValidate(); err != nil { - t.Fatalf("err: %s", err) - } -} - -func TestResourceProvider_Validate_good(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "environment": "_default", - "node_name": "nodename1", - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - }) - - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) > 0 { - t.Fatalf("Errors: %v", errs) - } -} - -func TestResourceProvider_Validate_bad(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "invalid": "nope", - }) - - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) == 0 { - t.Fatalf("Should have errors") - } -} - -// Test that the JSON attributes with an unknown value don't -// validate. -func TestResourceProvider_Validate_computedValues(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "environment": "_default", - "node_name": "nodename1", - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - "attributes_json": hcl2shim.UnknownVariableValue, - }) - - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) > 0 { - t.Fatalf("Errors: %v", errs) - } -} - -func TestResourceProvider_runChefClient(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - ChefCmd string - ConfDir string - Commands map[string]bool - }{ - "Sudo": { - Config: map[string]interface{}{ - "node_name": "nodename1", - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - ChefCmd: linuxChefCmd, - - ConfDir: linuxConfDir, - - Commands: map[string]bool{ - fmt.Sprintf(`sudo %s -j %q -E "_default"`, - linuxChefCmd, - path.Join(linuxConfDir, "first-boot.json")): true, - }, - }, - - "NoSudo": { - Config: map[string]interface{}{ - "node_name": "nodename1", - "prevent_sudo": true, - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - ChefCmd: linuxChefCmd, - - ConfDir: linuxConfDir, - - Commands: map[string]bool{ - fmt.Sprintf(`%s -j %q -E "_default"`, - linuxChefCmd, - path.Join(linuxConfDir, "first-boot.json")): true, - }, - }, - - "Environment": { - Config: map[string]interface{}{ - "environment": "production", - "node_name": "nodename1", - "prevent_sudo": true, - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - ChefCmd: windowsChefCmd, - - ConfDir: windowsConfDir, - - Commands: map[string]bool{ - fmt.Sprintf(`%s -j %q -E "production"`, - windowsChefCmd, - path.Join(windowsConfDir, "first-boot.json")): true, - }, - }, - } - - o := new(terraform.MockUIOutput) - c := new(communicator.MockCommunicator) - - for k, tc := range cases { - c.Commands = tc.Commands - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - p.runChefClient = p.runChefClientFunc(tc.ChefCmd, tc.ConfDir) - p.useSudo = !p.PreventSudo - - err = p.runChefClient(o, c) - if err != nil { - t.Fatalf("Test %q failed: %v", k, err) - } - } -} - -func TestResourceProvider_fetchChefCertificates(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - KnifeCmd string - ConfDir string - Commands map[string]bool - }{ - "Sudo": { - Config: map[string]interface{}{ - "fetch_chef_certificates": true, - "node_name": "nodename1", - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - KnifeCmd: linuxKnifeCmd, - - ConfDir: linuxConfDir, - - Commands: map[string]bool{ - fmt.Sprintf(`sudo %s ssl fetch -c %s`, - linuxKnifeCmd, - path.Join(linuxConfDir, "client.rb")): true, - }, - }, - - "NoSudo": { - Config: map[string]interface{}{ - "fetch_chef_certificates": true, - "node_name": "nodename1", - "prevent_sudo": true, - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - KnifeCmd: windowsKnifeCmd, - - ConfDir: windowsConfDir, - - Commands: map[string]bool{ - fmt.Sprintf(`%s ssl fetch -c %s`, - windowsKnifeCmd, - path.Join(windowsConfDir, "client.rb")): true, - }, - }, - } - - o := new(terraform.MockUIOutput) - c := new(communicator.MockCommunicator) - - for k, tc := range cases { - c.Commands = tc.Commands - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - p.fetchChefCertificates = p.fetchChefCertificatesFunc(tc.KnifeCmd, tc.ConfDir) - p.useSudo = !p.PreventSudo - - err = p.fetchChefCertificates(o, c) - if err != nil { - t.Fatalf("Test %q failed: %v", k, err) - } - } -} - -func TestResourceProvider_configureVaults(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - GemCmd string - KnifeCmd string - ConfDir string - Commands map[string]bool - }{ - "Linux Vault string": { - Config: map[string]interface{}{ - "node_name": "nodename1", - "prevent_sudo": true, - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - "vault_json": `{"vault1": "item1"}`, - }, - - GemCmd: linuxGemCmd, - KnifeCmd: linuxKnifeCmd, - ConfDir: linuxConfDir, - - Commands: map[string]bool{ - fmt.Sprintf("%s install chef-vault", linuxGemCmd): true, - fmt.Sprintf("%s vault update vault1 item1 -C nodename1 -M client -c %s/client.rb "+ - "-u bob --key %s/bob.pem", linuxKnifeCmd, linuxConfDir, linuxConfDir): true, - }, - }, - - "Linux Vault []string": { - Config: map[string]interface{}{ - "fetch_chef_certificates": true, - "node_name": "nodename1", - "prevent_sudo": true, - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - "vault_json": `{"vault1": ["item1", "item2"]}`, - }, - - GemCmd: linuxGemCmd, - KnifeCmd: linuxKnifeCmd, - ConfDir: linuxConfDir, - - Commands: map[string]bool{ - fmt.Sprintf("%s install chef-vault", linuxGemCmd): true, - fmt.Sprintf("%s vault update vault1 item1 -C nodename1 -M client -c %s/client.rb "+ - "-u bob --key %s/bob.pem", linuxKnifeCmd, linuxConfDir, linuxConfDir): true, - fmt.Sprintf("%s vault update vault1 item2 -C nodename1 -M client -c %s/client.rb "+ - "-u bob --key %s/bob.pem", linuxKnifeCmd, linuxConfDir, linuxConfDir): true, - }, - }, - - "Linux Vault []string (recreate-client for vault)": { - Config: map[string]interface{}{ - "fetch_chef_certificates": true, - "node_name": "nodename1", - "prevent_sudo": true, - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - "vault_json": `{"vault1": ["item1", "item2"]}`, - "recreate_client": true, - }, - - GemCmd: linuxGemCmd, - KnifeCmd: linuxKnifeCmd, - ConfDir: linuxConfDir, - - Commands: map[string]bool{ - fmt.Sprintf("%s install chef-vault", linuxGemCmd): true, - fmt.Sprintf("%s vault remove vault1 item1 -C \"nodename1\" -M client -c %s/client.rb "+ - "-u bob --key %s/bob.pem", linuxKnifeCmd, linuxConfDir, linuxConfDir): true, - fmt.Sprintf("%s vault remove vault1 item2 -C \"nodename1\" -M client -c %s/client.rb "+ - "-u bob --key %s/bob.pem", linuxKnifeCmd, linuxConfDir, linuxConfDir): true, - fmt.Sprintf("%s vault update vault1 item1 -C nodename1 -M client -c %s/client.rb "+ - "-u bob --key %s/bob.pem", linuxKnifeCmd, linuxConfDir, linuxConfDir): true, - fmt.Sprintf("%s vault update vault1 item2 -C nodename1 -M client -c %s/client.rb "+ - "-u bob --key %s/bob.pem", linuxKnifeCmd, linuxConfDir, linuxConfDir): true, - }, - }, - - "Windows Vault string": { - Config: map[string]interface{}{ - "node_name": "nodename1", - "prevent_sudo": true, - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - "vault_json": `{"vault1": "item1"}`, - }, - - GemCmd: windowsGemCmd, - KnifeCmd: windowsKnifeCmd, - ConfDir: windowsConfDir, - - Commands: map[string]bool{ - fmt.Sprintf("%s install chef-vault", windowsGemCmd): true, - fmt.Sprintf("%s vault update vault1 item1 -C nodename1 -M client -c %s/client.rb "+ - "-u bob --key %s/bob.pem", windowsKnifeCmd, windowsConfDir, windowsConfDir): true, - }, - }, - - "Windows Vault []string": { - Config: map[string]interface{}{ - "fetch_chef_certificates": true, - "node_name": "nodename1", - "prevent_sudo": true, - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - "vault_json": `{"vault1": ["item1", "item2"]}`, - }, - - GemCmd: windowsGemCmd, - KnifeCmd: windowsKnifeCmd, - ConfDir: windowsConfDir, - - Commands: map[string]bool{ - fmt.Sprintf("%s install chef-vault", windowsGemCmd): true, - fmt.Sprintf("%s vault update vault1 item1 -C nodename1 -M client -c %s/client.rb "+ - "-u bob --key %s/bob.pem", windowsKnifeCmd, windowsConfDir, windowsConfDir): true, - fmt.Sprintf("%s vault update vault1 item2 -C nodename1 -M client -c %s/client.rb "+ - "-u bob --key %s/bob.pem", windowsKnifeCmd, windowsConfDir, windowsConfDir): true, - }, - }, - - "Windows Vault [] string (recreate-client for vault)": { - Config: map[string]interface{}{ - "fetch_chef_certificates": true, - "node_name": "nodename1", - "prevent_sudo": true, - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - "vault_json": `{"vault1": ["item1", "item2"]}`, - "recreate_client": true, - }, - - GemCmd: windowsGemCmd, - KnifeCmd: windowsKnifeCmd, - ConfDir: windowsConfDir, - - Commands: map[string]bool{ - fmt.Sprintf("%s install chef-vault", windowsGemCmd): true, - fmt.Sprintf("%s vault remove vault1 item1 -C \"nodename1\" -M client -c %s/client.rb "+ - "-u bob --key %s/bob.pem", windowsKnifeCmd, windowsConfDir, windowsConfDir): true, - fmt.Sprintf("%s vault remove vault1 item2 -C \"nodename1\" -M client -c %s/client.rb "+ - "-u bob --key %s/bob.pem", windowsKnifeCmd, windowsConfDir, windowsConfDir): true, - fmt.Sprintf("%s vault update vault1 item1 -C nodename1 -M client -c %s/client.rb "+ - "-u bob --key %s/bob.pem", windowsKnifeCmd, windowsConfDir, windowsConfDir): true, - fmt.Sprintf("%s vault update vault1 item2 -C nodename1 -M client -c %s/client.rb "+ - "-u bob --key %s/bob.pem", windowsKnifeCmd, windowsConfDir, windowsConfDir): true, - }, - }, - } - - o := new(terraform.MockUIOutput) - c := new(communicator.MockCommunicator) - - for k, tc := range cases { - c.Commands = tc.Commands - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - p.configureVaults = p.configureVaultsFunc(tc.GemCmd, tc.KnifeCmd, tc.ConfDir) - p.useSudo = !p.PreventSudo - - err = p.configureVaults(o, c) - if err != nil { - t.Fatalf("Test %q failed: %v", k, err) - } - } -} - -func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig { - return terraform.NewResourceConfigRaw(c) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/testdata/ohaihint.json b/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/testdata/ohaihint.json deleted file mode 100644 index 9aabc328..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/testdata/ohaihint.json +++ /dev/null @@ -1 +0,0 @@ -OHAI-HINT-FILE diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/windows_provisioner.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/windows_provisioner.go deleted file mode 100644 index 02010acd..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/windows_provisioner.go +++ /dev/null @@ -1,84 +0,0 @@ -package chef - -import ( - "fmt" - "path" - "strings" - - "github.com/hashicorp/terraform/communicator" - "github.com/hashicorp/terraform/terraform" -) - -const installScript = ` -$winver = [System.Environment]::OSVersion.Version | %% {"{0}.{1}" -f $_.Major,$_.Minor} - -switch ($winver) -{ - "6.0" {$machine_os = "2008"} - "6.1" {$machine_os = "2008r2"} - "6.2" {$machine_os = "2012"} - "6.3" {$machine_os = "2012"} - default {$machine_os = "2008r2"} -} - -if ([System.IntPtr]::Size -eq 4) {$machine_arch = "i686"} else {$machine_arch = "x86_64"} - -$url = "http://omnitruck.chef.io/%s/chef/download?p=windows&pv=$machine_os&m=$machine_arch&v=%s" -$dest = [System.IO.Path]::GetTempFileName() -$dest = [System.IO.Path]::ChangeExtension($dest, ".msi") -$downloader = New-Object System.Net.WebClient - -$http_proxy = '%s' -if ($http_proxy -ne '') { - $no_proxy = '%s' - if ($no_proxy -eq ''){ - $no_proxy = "127.0.0.1" - } - - $proxy = New-Object System.Net.WebProxy($http_proxy, $true, ,$no_proxy.Split(',')) - $downloader.proxy = $proxy -} - -Write-Host 'Downloading Chef Client...' -$downloader.DownloadFile($url, $dest) - -Write-Host 'Installing Chef Client...' -Start-Process -FilePath msiexec -ArgumentList /qn, /i, $dest -Wait -` - -func (p *provisioner) windowsInstallChefClient(o terraform.UIOutput, comm communicator.Communicator) error { - script := path.Join(path.Dir(comm.ScriptPath()), "ChefClient.ps1") - content := fmt.Sprintf(installScript, p.Channel, p.Version, p.HTTPProxy, strings.Join(p.NOProxy, ",")) - - // Copy the script to the new instance - if err := comm.UploadScript(script, strings.NewReader(content)); err != nil { - return fmt.Errorf("Uploading client.rb failed: %v", err) - } - - // Execute the script to install Chef Client - installCmd := fmt.Sprintf("powershell -NoProfile -ExecutionPolicy Bypass -File %s", script) - return p.runCommand(o, comm, installCmd) -} - -func (p *provisioner) windowsCreateConfigFiles(o terraform.UIOutput, comm communicator.Communicator) error { - // Make sure the config directory exists - cmd := fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir) - if err := p.runCommand(o, comm, cmd); err != nil { - return err - } - - if len(p.OhaiHints) > 0 { - // Make sure the hits directory exists - hintsDir := path.Join(windowsConfDir, "ohai/hints") - cmd := fmt.Sprintf("cmd /c if not exist %q mkdir %q", hintsDir, hintsDir) - if err := p.runCommand(o, comm, cmd); err != nil { - return err - } - - if err := p.deployOhaiHints(o, comm, hintsDir); err != nil { - return err - } - } - - return p.deployConfigFiles(o, comm, windowsConfDir) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/windows_provisioner_test.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/windows_provisioner_test.go deleted file mode 100644 index 603d9240..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/chef/windows_provisioner_test.go +++ /dev/null @@ -1,394 +0,0 @@ -package chef - -import ( - "fmt" - "path" - "testing" - - "github.com/hashicorp/terraform/communicator" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" -) - -func TestResourceProvider_windowsInstallChefClient(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - Commands map[string]bool - UploadScripts map[string]string - }{ - "Default": { - Config: map[string]interface{}{ - "node_name": "nodename1", - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - Commands: map[string]bool{ - "powershell -NoProfile -ExecutionPolicy Bypass -File ChefClient.ps1": true, - }, - - UploadScripts: map[string]string{ - "ChefClient.ps1": defaultWindowsInstallScript, - }, - }, - - "Proxy": { - Config: map[string]interface{}{ - "http_proxy": "http://proxy.local", - "no_proxy": []interface{}{"http://local.local", "http://local.org"}, - "node_name": "nodename1", - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - Commands: map[string]bool{ - "powershell -NoProfile -ExecutionPolicy Bypass -File ChefClient.ps1": true, - }, - - UploadScripts: map[string]string{ - "ChefClient.ps1": proxyWindowsInstallScript, - }, - }, - - "Version": { - Config: map[string]interface{}{ - "node_name": "nodename1", - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - "version": "11.18.6", - }, - - Commands: map[string]bool{ - "powershell -NoProfile -ExecutionPolicy Bypass -File ChefClient.ps1": true, - }, - - UploadScripts: map[string]string{ - "ChefClient.ps1": versionWindowsInstallScript, - }, - }, - - "Channel": { - Config: map[string]interface{}{ - "channel": "current", - "node_name": "nodename1", - "run_list": []interface{}{"cookbook::recipe"}, - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - "version": "11.18.6", - }, - - Commands: map[string]bool{ - "powershell -NoProfile -ExecutionPolicy Bypass -File ChefClient.ps1": true, - }, - - UploadScripts: map[string]string{ - "ChefClient.ps1": channelWindowsInstallScript, - }, - }, - } - - o := new(terraform.MockUIOutput) - c := new(communicator.MockCommunicator) - - for k, tc := range cases { - c.Commands = tc.Commands - c.UploadScripts = tc.UploadScripts - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - p.useSudo = false - - err = p.windowsInstallChefClient(o, c) - if err != nil { - t.Fatalf("Test %q failed: %v", k, err) - } - } -} - -func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - Commands map[string]bool - Uploads map[string]string - }{ - "Default": { - Config: map[string]interface{}{ - "ohai_hints": []interface{}{"testdata/ohaihint.json"}, - "node_name": "nodename1", - "run_list": []interface{}{"cookbook::recipe"}, - "secret_key": "SECRET-KEY", - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - Commands: map[string]bool{ - fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir): true, - fmt.Sprintf("cmd /c if not exist %q mkdir %q", - path.Join(windowsConfDir, "ohai/hints"), - path.Join(windowsConfDir, "ohai/hints")): true, - }, - - Uploads: map[string]string{ - windowsConfDir + "/client.rb": defaultWindowsClientConf, - windowsConfDir + "/encrypted_data_bag_secret": "SECRET-KEY", - windowsConfDir + "/first-boot.json": `{"run_list":["cookbook::recipe"]}`, - windowsConfDir + "/ohai/hints/ohaihint.json": "OHAI-HINT-FILE", - windowsConfDir + "/bob.pem": "USER-KEY", - }, - }, - - "Proxy": { - Config: map[string]interface{}{ - "http_proxy": "http://proxy.local", - "https_proxy": "https://proxy.local", - "no_proxy": []interface{}{"http://local.local", "https://local.local"}, - "node_name": "nodename1", - "run_list": []interface{}{"cookbook::recipe"}, - "secret_key": "SECRET-KEY", - "server_url": "https://chef.local", - "ssl_verify_mode": "verify_none", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - Commands: map[string]bool{ - fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir): true, - }, - - Uploads: map[string]string{ - windowsConfDir + "/client.rb": proxyWindowsClientConf, - windowsConfDir + "/first-boot.json": `{"run_list":["cookbook::recipe"]}`, - windowsConfDir + "/encrypted_data_bag_secret": "SECRET-KEY", - windowsConfDir + "/bob.pem": "USER-KEY", - }, - }, - - "Attributes JSON": { - Config: map[string]interface{}{ - "attributes_json": `{"key1":{"subkey1":{"subkey2a":["val1","val2","val3"],` + - `"subkey2b":{"subkey3":"value3"}}},"key2":"value2"}`, - "node_name": "nodename1", - "run_list": []interface{}{"cookbook::recipe"}, - "secret_key": "SECRET-KEY", - "server_url": "https://chef.local", - "user_name": "bob", - "user_key": "USER-KEY", - }, - - Commands: map[string]bool{ - fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir): true, - }, - - Uploads: map[string]string{ - windowsConfDir + "/client.rb": defaultWindowsClientConf, - windowsConfDir + "/encrypted_data_bag_secret": "SECRET-KEY", - windowsConfDir + "/bob.pem": "USER-KEY", - windowsConfDir + "/first-boot.json": `{"key1":{"subkey1":{"subkey2a":["val1","val2","val3"],` + - `"subkey2b":{"subkey3":"value3"}}},"key2":"value2","run_list":["cookbook::recipe"]}`, - }, - }, - } - - o := new(terraform.MockUIOutput) - c := new(communicator.MockCommunicator) - - for k, tc := range cases { - c.Commands = tc.Commands - c.Uploads = tc.Uploads - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - p.useSudo = false - - err = p.windowsCreateConfigFiles(o, c) - if err != nil { - t.Fatalf("Test %q failed: %v", k, err) - } - } -} - -const defaultWindowsInstallScript = ` -$winver = [System.Environment]::OSVersion.Version | % {"{0}.{1}" -f $_.Major,$_.Minor} - -switch ($winver) -{ - "6.0" {$machine_os = "2008"} - "6.1" {$machine_os = "2008r2"} - "6.2" {$machine_os = "2012"} - "6.3" {$machine_os = "2012"} - default {$machine_os = "2008r2"} -} - -if ([System.IntPtr]::Size -eq 4) {$machine_arch = "i686"} else {$machine_arch = "x86_64"} - -$url = "http://omnitruck.chef.io/stable/chef/download?p=windows&pv=$machine_os&m=$machine_arch&v=" -$dest = [System.IO.Path]::GetTempFileName() -$dest = [System.IO.Path]::ChangeExtension($dest, ".msi") -$downloader = New-Object System.Net.WebClient - -$http_proxy = '' -if ($http_proxy -ne '') { - $no_proxy = '' - if ($no_proxy -eq ''){ - $no_proxy = "127.0.0.1" - } - - $proxy = New-Object System.Net.WebProxy($http_proxy, $true, ,$no_proxy.Split(',')) - $downloader.proxy = $proxy -} - -Write-Host 'Downloading Chef Client...' -$downloader.DownloadFile($url, $dest) - -Write-Host 'Installing Chef Client...' -Start-Process -FilePath msiexec -ArgumentList /qn, /i, $dest -Wait -` - -const proxyWindowsInstallScript = ` -$winver = [System.Environment]::OSVersion.Version | % {"{0}.{1}" -f $_.Major,$_.Minor} - -switch ($winver) -{ - "6.0" {$machine_os = "2008"} - "6.1" {$machine_os = "2008r2"} - "6.2" {$machine_os = "2012"} - "6.3" {$machine_os = "2012"} - default {$machine_os = "2008r2"} -} - -if ([System.IntPtr]::Size -eq 4) {$machine_arch = "i686"} else {$machine_arch = "x86_64"} - -$url = "http://omnitruck.chef.io/stable/chef/download?p=windows&pv=$machine_os&m=$machine_arch&v=" -$dest = [System.IO.Path]::GetTempFileName() -$dest = [System.IO.Path]::ChangeExtension($dest, ".msi") -$downloader = New-Object System.Net.WebClient - -$http_proxy = 'http://proxy.local' -if ($http_proxy -ne '') { - $no_proxy = 'http://local.local,http://local.org' - if ($no_proxy -eq ''){ - $no_proxy = "127.0.0.1" - } - - $proxy = New-Object System.Net.WebProxy($http_proxy, $true, ,$no_proxy.Split(',')) - $downloader.proxy = $proxy -} - -Write-Host 'Downloading Chef Client...' -$downloader.DownloadFile($url, $dest) - -Write-Host 'Installing Chef Client...' -Start-Process -FilePath msiexec -ArgumentList /qn, /i, $dest -Wait -` - -const versionWindowsInstallScript = ` -$winver = [System.Environment]::OSVersion.Version | % {"{0}.{1}" -f $_.Major,$_.Minor} - -switch ($winver) -{ - "6.0" {$machine_os = "2008"} - "6.1" {$machine_os = "2008r2"} - "6.2" {$machine_os = "2012"} - "6.3" {$machine_os = "2012"} - default {$machine_os = "2008r2"} -} - -if ([System.IntPtr]::Size -eq 4) {$machine_arch = "i686"} else {$machine_arch = "x86_64"} - -$url = "http://omnitruck.chef.io/stable/chef/download?p=windows&pv=$machine_os&m=$machine_arch&v=11.18.6" -$dest = [System.IO.Path]::GetTempFileName() -$dest = [System.IO.Path]::ChangeExtension($dest, ".msi") -$downloader = New-Object System.Net.WebClient - -$http_proxy = '' -if ($http_proxy -ne '') { - $no_proxy = '' - if ($no_proxy -eq ''){ - $no_proxy = "127.0.0.1" - } - - $proxy = New-Object System.Net.WebProxy($http_proxy, $true, ,$no_proxy.Split(',')) - $downloader.proxy = $proxy -} - -Write-Host 'Downloading Chef Client...' -$downloader.DownloadFile($url, $dest) - -Write-Host 'Installing Chef Client...' -Start-Process -FilePath msiexec -ArgumentList /qn, /i, $dest -Wait -` -const channelWindowsInstallScript = ` -$winver = [System.Environment]::OSVersion.Version | % {"{0}.{1}" -f $_.Major,$_.Minor} - -switch ($winver) -{ - "6.0" {$machine_os = "2008"} - "6.1" {$machine_os = "2008r2"} - "6.2" {$machine_os = "2012"} - "6.3" {$machine_os = "2012"} - default {$machine_os = "2008r2"} -} - -if ([System.IntPtr]::Size -eq 4) {$machine_arch = "i686"} else {$machine_arch = "x86_64"} - -$url = "http://omnitruck.chef.io/current/chef/download?p=windows&pv=$machine_os&m=$machine_arch&v=11.18.6" -$dest = [System.IO.Path]::GetTempFileName() -$dest = [System.IO.Path]::ChangeExtension($dest, ".msi") -$downloader = New-Object System.Net.WebClient - -$http_proxy = '' -if ($http_proxy -ne '') { - $no_proxy = '' - if ($no_proxy -eq ''){ - $no_proxy = "127.0.0.1" - } - - $proxy = New-Object System.Net.WebProxy($http_proxy, $true, ,$no_proxy.Split(',')) - $downloader.proxy = $proxy -} - -Write-Host 'Downloading Chef Client...' -$downloader.DownloadFile($url, $dest) - -Write-Host 'Installing Chef Client...' -Start-Process -FilePath msiexec -ArgumentList /qn, /i, $dest -Wait -` - -const defaultWindowsClientConf = `log_location STDOUT -chef_server_url "https://chef.local/" -node_name "nodename1"` - -const proxyWindowsClientConf = `log_location STDOUT -chef_server_url "https://chef.local/" -node_name "nodename1" - -http_proxy "http://proxy.local" -ENV['http_proxy'] = "http://proxy.local" -ENV['HTTP_PROXY'] = "http://proxy.local" - -https_proxy "https://proxy.local" -ENV['https_proxy'] = "https://proxy.local" -ENV['HTTPS_PROXY'] = "https://proxy.local" - -no_proxy "http://local.local,https://local.local" -ENV['no_proxy'] = "http://local.local,https://local.local" - -ssl_verify_mode :verify_none` diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/file/resource_provisioner.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/file/resource_provisioner.go index 26f2f4da..29ed8587 100644 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/file/resource_provisioner.go +++ b/vendor/github.com/hashicorp/terraform/builtin/provisioners/file/resource_provisioner.go @@ -2,96 +2,134 @@ package file import ( "context" + "errors" "fmt" "io/ioutil" "os" "github.com/hashicorp/terraform/communicator" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/provisioners" "github.com/mitchellh/go-homedir" + "github.com/zclconf/go-cty/cty" ) -func Provisioner() terraform.ResourceProvisioner { - return &schema.Provisioner{ - Schema: map[string]*schema.Schema{ - "source": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ConflictsWith: []string{"content"}, +func New() provisioners.Interface { + ctx, cancel := context.WithCancel(context.Background()) + return &provisioner{ + ctx: ctx, + cancel: cancel, + } +} + +type provisioner struct { + // We store a context here tied to the lifetime of the provisioner. + // This allows the Stop method to cancel any in-flight requests. + ctx context.Context + cancel context.CancelFunc +} + +func (p *provisioner) GetSchema() (resp provisioners.GetSchemaResponse) { + schema := &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "source": { + Type: cty.String, + Optional: true, }, - "content": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ConflictsWith: []string{"source"}, + "content": { + Type: cty.String, + Optional: true, }, - "destination": &schema.Schema{ - Type: schema.TypeString, + "destination": { + Type: cty.String, Required: true, }, }, + } + resp.Provisioner = schema + return resp +} + +func (p *provisioner) ValidateProvisionerConfig(req provisioners.ValidateProvisionerConfigRequest) (resp provisioners.ValidateProvisionerConfigResponse) { + cfg, err := p.GetSchema().Provisioner.CoerceValue(req.Config) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + } - ApplyFunc: applyFn, - ValidateFunc: validateFn, + source := cfg.GetAttr("source") + content := cfg.GetAttr("content") + + switch { + case !source.IsNull() && !content.IsNull(): + resp.Diagnostics = resp.Diagnostics.Append(errors.New("Cannot set both 'source' and 'content'")) + return resp + case source.IsNull() && content.IsNull(): + resp.Diagnostics = resp.Diagnostics.Append(errors.New("Must provide one of 'source' or 'content'")) + return resp } + + return resp } -func applyFn(ctx context.Context) error { - connState := ctx.Value(schema.ProvRawStateKey).(*terraform.InstanceState) - data := ctx.Value(schema.ProvConfigDataKey).(*schema.ResourceData) +func (p *provisioner) ProvisionResource(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { + if req.Connection.IsNull() { + resp.Diagnostics = resp.Diagnostics.Append(errors.New("missing connection configuration for provisioner")) + return resp + } - // Get a new communicator - comm, err := communicator.New(connState) + comm, err := communicator.New(req.Connection) if err != nil { - return err + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp } // Get the source - src, deleteSource, err := getSrc(data) + src, deleteSource, err := getSrc(req.Config) if err != nil { - return err + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp } if deleteSource { defer os.Remove(src) } // Begin the file copy - dst := data.Get("destination").(string) - - if err := copyFiles(ctx, comm, src, dst); err != nil { - return err - } - return nil -} - -func validateFn(c *terraform.ResourceConfig) (ws []string, es []error) { - if !c.IsSet("source") && !c.IsSet("content") { - es = append(es, fmt.Errorf("Must provide one of 'source' or 'content'")) + dst := req.Config.GetAttr("destination").AsString() + if err := copyFiles(p.ctx, comm, src, dst); err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp } - return ws, es + return resp } // getSrc returns the file to use as source -func getSrc(data *schema.ResourceData) (string, bool, error) { - src := data.Get("source").(string) - if content, ok := data.GetOk("content"); ok { +func getSrc(v cty.Value) (string, bool, error) { + content := v.GetAttr("content") + src := v.GetAttr("source") + + switch { + case !content.IsNull(): file, err := ioutil.TempFile("", "tf-file-content") if err != nil { return "", true, err } - if _, err = file.WriteString(content.(string)); err != nil { + if _, err = file.WriteString(content.AsString()); err != nil { return "", true, err } return file.Name(), true, nil - } - expansion, err := homedir.Expand(src) - return expansion, false, err + case !src.IsNull(): + expansion, err := homedir.Expand(src.AsString()) + return expansion, false, err + + default: + panic("source and content cannot both be null") + } } // copyFiles is used to copy the files from a source to a destination @@ -138,5 +176,15 @@ func copyFiles(ctx context.Context, comm communicator.Communicator, src, dst str if err != nil { return fmt.Errorf("Upload failed: %v", err) } + return err } + +func (p *provisioner) Stop() error { + p.cancel() + return nil +} + +func (p *provisioner) Close() error { + return nil +} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/file/resource_provisioner_test.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/file/resource_provisioner_test.go index c7e34c0a..52b99cb9 100644 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/file/resource_provisioner_test.go +++ b/vendor/github.com/hashicorp/terraform/builtin/provisioners/file/resource_provisioner_test.go @@ -1,112 +1,118 @@ package file import ( + "strings" "testing" - "github.com/hashicorp/terraform/configs/hcl2shim" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/provisioners" + "github.com/zclconf/go-cty/cty" ) -func TestResourceProvisioner_impl(t *testing.T) { - var _ terraform.ResourceProvisioner = Provisioner() -} - -func TestProvisioner(t *testing.T) { - if err := Provisioner().(*schema.Provisioner).InternalValidate(); err != nil { - t.Fatalf("err: %s", err) - } -} - func TestResourceProvider_Validate_good_source(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "source": "/tmp/foo", - "destination": "/tmp/bar", + v := cty.ObjectVal(map[string]cty.Value{ + "source": cty.StringVal("/tmp/foo"), + "destination": cty.StringVal("/tmp/bar"), }) - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) > 0 { - t.Fatalf("Errors: %v", errs) + resp := New().ValidateProvisionerConfig(provisioners.ValidateProvisionerConfigRequest{ + Config: v, + }) + + if len(resp.Diagnostics) > 0 { + t.Fatal(resp.Diagnostics.ErrWithWarnings()) } } func TestResourceProvider_Validate_good_content(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "content": "value to copy", - "destination": "/tmp/bar", + v := cty.ObjectVal(map[string]cty.Value{ + "content": cty.StringVal("value to copy"), + "destination": cty.StringVal("/tmp/bar"), }) - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) > 0 { - t.Fatalf("Errors: %v", errs) + resp := New().ValidateProvisionerConfig(provisioners.ValidateProvisionerConfigRequest{ + Config: v, + }) + + if len(resp.Diagnostics) > 0 { + t.Fatal(resp.Diagnostics.ErrWithWarnings()) } } func TestResourceProvider_Validate_good_unknown_variable_value(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "content": hcl2shim.UnknownVariableValue, - "destination": "/tmp/bar", + v := cty.ObjectVal(map[string]cty.Value{ + "content": cty.UnknownVal(cty.String), + "destination": cty.StringVal("/tmp/bar"), }) - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) > 0 { - t.Fatalf("Errors: %v", errs) + resp := New().ValidateProvisionerConfig(provisioners.ValidateProvisionerConfigRequest{ + Config: v, + }) + + if len(resp.Diagnostics) > 0 { + t.Fatal(resp.Diagnostics.ErrWithWarnings()) } } func TestResourceProvider_Validate_bad_not_destination(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "source": "nope", + v := cty.ObjectVal(map[string]cty.Value{ + "source": cty.StringVal("nope"), }) - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) == 0 { - t.Fatalf("Should have errors") + resp := New().ValidateProvisionerConfig(provisioners.ValidateProvisionerConfigRequest{ + Config: v, + }) + + if !resp.Diagnostics.HasErrors() { + t.Fatal("Should have errors") } } func TestResourceProvider_Validate_bad_no_source(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "destination": "/tmp/bar", + v := cty.ObjectVal(map[string]cty.Value{ + "destination": cty.StringVal("/tmp/bar"), }) - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) == 0 { - t.Fatalf("Should have errors") + resp := New().ValidateProvisionerConfig(provisioners.ValidateProvisionerConfigRequest{ + Config: v, + }) + + if !resp.Diagnostics.HasErrors() { + t.Fatal("Should have errors") } } func TestResourceProvider_Validate_bad_to_many_src(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "source": "nope", - "content": "value to copy", - "destination": "/tmp/bar", + v := cty.ObjectVal(map[string]cty.Value{ + "source": cty.StringVal("nope"), + "content": cty.StringVal("vlue to copy"), + "destination": cty.StringVal("/tmp/bar"), }) - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) == 0 { - t.Fatalf("Should have errors") + resp := New().ValidateProvisionerConfig(provisioners.ValidateProvisionerConfigRequest{ + Config: v, + }) + + if !resp.Diagnostics.HasErrors() { + t.Fatal("Should have errors") } } -func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig { - return terraform.NewResourceConfigRaw(c) +// Validate that Stop can Close can be called even when not provisioning. +func TestResourceProvisioner_StopClose(t *testing.T) { + p := New() + p.Stop() + p.Close() +} + +func TestResourceProvisioner_connectionRequired(t *testing.T) { + p := New() + resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{}) + if !resp.Diagnostics.HasErrors() { + t.Fatal("expected error") + } + + got := resp.Diagnostics.Err().Error() + if !strings.Contains(got, "missing connection") { + t.Fatalf("expected 'missing connection' error: got %q", got) + } } diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/habitat/linux_provisioner.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/habitat/linux_provisioner.go deleted file mode 100644 index 414be692..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/habitat/linux_provisioner.go +++ /dev/null @@ -1,377 +0,0 @@ -package habitat - -import ( - "bytes" - "errors" - "fmt" - "github.com/hashicorp/terraform/communicator" - "github.com/hashicorp/terraform/terraform" - "path" - "path/filepath" - "strings" - "text/template" -) - -const installURL = "https://raw.githubusercontent.com/habitat-sh/habitat/master/components/hab/install.sh" -const systemdUnit = `[Unit] -Description=Habitat Supervisor - -[Service] -ExecStart=/bin/hab sup run{{ .SupOptions }} -Restart=on-failure -{{ if .GatewayAuthToken -}} -Environment="HAB_SUP_GATEWAY_AUTH_TOKEN={{ .GatewayAuthToken }}" -{{ end -}} -{{ if .BuilderAuthToken -}} -Environment="HAB_AUTH_TOKEN={{ .BuilderAuthToken }}" -{{ end -}} - -[Install] -WantedBy=default.target -` - -func (p *provisioner) linuxInstallHabitat(o terraform.UIOutput, comm communicator.Communicator) error { - // Download the hab installer - if err := p.runCommand(o, comm, p.linuxGetCommand(fmt.Sprintf("curl --silent -L0 %s > install.sh", installURL))); err != nil { - return err - } - - // Run the install script - var command string - if p.Version == "" { - command = fmt.Sprintf("bash ./install.sh ") - } else { - command = fmt.Sprintf("bash ./install.sh -v %s", p.Version) - } - - if err := p.runCommand(o, comm, p.linuxGetCommand(command)); err != nil { - return err - } - - // Accept the license - if p.AcceptLicense { - var cmd string - - if p.UseSudo == true { - cmd = "env HAB_LICENSE=accept sudo -E /bin/bash -c 'hab -V'" - } else { - cmd = "env HAB_LICENSE=accept /bin/bash -c 'hab -V'" - } - - if err := p.runCommand(o, comm, cmd); err != nil { - return err - } - } - - // Create the hab user - if err := p.createHabUser(o, comm); err != nil { - return err - } - - // Cleanup the installer - return p.runCommand(o, comm, p.linuxGetCommand(fmt.Sprintf("rm -f install.sh"))) -} - -func (p *provisioner) createHabUser(o terraform.UIOutput, comm communicator.Communicator) error { - var addUser bool - - // Install busybox to get us the user tools we need - if err := p.runCommand(o, comm, p.linuxGetCommand(fmt.Sprintf("hab install core/busybox"))); err != nil { - return err - } - - // Check for existing hab user - if err := p.runCommand(o, comm, p.linuxGetCommand(fmt.Sprintf("hab pkg exec core/busybox id hab"))); err != nil { - o.Output("No existing hab user detected, creating...") - addUser = true - } - - if addUser { - return p.runCommand(o, comm, p.linuxGetCommand(fmt.Sprintf("hab pkg exec core/busybox adduser -D -g \"\" hab"))) - } - - return nil -} - -func (p *provisioner) linuxStartHabitat(o terraform.UIOutput, comm communicator.Communicator) error { - // Install the supervisor first - var command string - if p.Version == "" { - command += p.linuxGetCommand(fmt.Sprintf("hab install core/hab-sup")) - } else { - command += p.linuxGetCommand(fmt.Sprintf("hab install core/hab-sup/%s", p.Version)) - } - - if err := p.runCommand(o, comm, command); err != nil { - return err - } - - // Build up supervisor options - options := "" - if p.PermanentPeer { - options += " --permanent-peer" - } - - if p.ListenCtl != "" { - options += fmt.Sprintf(" --listen-ctl %s", p.ListenCtl) - } - - if p.ListenGossip != "" { - options += fmt.Sprintf(" --listen-gossip %s", p.ListenGossip) - } - - if p.ListenHTTP != "" { - options += fmt.Sprintf(" --listen-http %s", p.ListenHTTP) - } - - if p.Peer != "" { - options += fmt.Sprintf(" %s", p.Peer) - } - - if len(p.Peers) > 0 { - if len(p.Peers) == 1 { - options += fmt.Sprintf(" --peer %s", p.Peers[0]) - } else { - options += fmt.Sprintf(" --peer %s", strings.Join(p.Peers, " --peer ")) - } - } - - if p.RingKey != "" { - options += fmt.Sprintf(" --ring %s", p.RingKey) - } - - if p.URL != "" { - options += fmt.Sprintf(" --url %s", p.URL) - } - - if p.Channel != "" { - options += fmt.Sprintf(" --channel %s", p.Channel) - } - - if p.Events != "" { - options += fmt.Sprintf(" --events %s", p.Events) - } - - if p.Organization != "" { - options += fmt.Sprintf(" --org %s", p.Organization) - } - - if p.HttpDisable == true { - options += fmt.Sprintf(" --http-disable") - } - - if p.AutoUpdate == true { - options += fmt.Sprintf(" --auto-update") - } - - p.SupOptions = options - - // Start hab depending on service type - switch p.ServiceType { - case "unmanaged": - return p.linuxStartHabitatUnmanaged(o, comm, options) - case "systemd": - return p.linuxStartHabitatSystemd(o, comm, options) - default: - return errors.New("unsupported service type") - } -} - -// This func is a little different than the others since we need to expose HAB_AUTH_TOKEN to a shell -// sub-process that's actually running the supervisor. -func (p *provisioner) linuxStartHabitatUnmanaged(o terraform.UIOutput, comm communicator.Communicator, options string) error { - var token string - - // Create the sup directory for the log file - if err := p.runCommand(o, comm, p.linuxGetCommand("mkdir -p /hab/sup/default && chmod o+w /hab/sup/default")); err != nil { - return err - } - - // Set HAB_AUTH_TOKEN if provided - if p.BuilderAuthToken != "" { - token = fmt.Sprintf("env HAB_AUTH_TOKEN=%s ", p.BuilderAuthToken) - } - - return p.runCommand(o, comm, p.linuxGetCommand(fmt.Sprintf("(%ssetsid hab sup run%s > /hab/sup/default/sup.log 2>&1 <&1 &) ; sleep 1", token, options))) -} - -func (p *provisioner) linuxStartHabitatSystemd(o terraform.UIOutput, comm communicator.Communicator, options string) error { - // Create a new template and parse the client config into it - unitString := template.Must(template.New("hab-supervisor.service").Parse(systemdUnit)) - - var buf bytes.Buffer - err := unitString.Execute(&buf, p) - if err != nil { - return fmt.Errorf("error executing %s.service template: %s", p.ServiceName, err) - } - - if err := p.linuxUploadSystemdUnit(o, comm, &buf); err != nil { - return err - } - - return p.runCommand(o, comm, p.linuxGetCommand(fmt.Sprintf("systemctl enable %s && systemctl start %s", p.ServiceName, p.ServiceName))) -} - -func (p *provisioner) linuxUploadSystemdUnit(o terraform.UIOutput, comm communicator.Communicator, contents *bytes.Buffer) error { - destination := fmt.Sprintf("/etc/systemd/system/%s.service", p.ServiceName) - - if p.UseSudo { - tempPath := fmt.Sprintf("/tmp/%s.service", p.ServiceName) - if err := comm.Upload(tempPath, contents); err != nil { - return err - } - - return p.runCommand(o, comm, p.linuxGetCommand(fmt.Sprintf("mv %s %s", tempPath, destination))) - } - - return comm.Upload(destination, contents) -} - -func (p *provisioner) linuxUploadRingKey(o terraform.UIOutput, comm communicator.Communicator) error { - return p.runCommand(o, comm, p.linuxGetCommand(fmt.Sprintf(`echo -e "%s" | hab ring key import`, p.RingKeyContent))) -} - -func (p *provisioner) linuxUploadCtlSecret(o terraform.UIOutput, comm communicator.Communicator) error { - destination := fmt.Sprintf("/hab/sup/default/CTL_SECRET") - // Create the destination directory - err := p.runCommand(o, comm, p.linuxGetCommand(fmt.Sprintf("mkdir -p %s", filepath.Dir(destination)))) - if err != nil { - return err - } - - keyContent := strings.NewReader(p.CtlSecret) - if p.UseSudo { - tempPath := fmt.Sprintf("/tmp/CTL_SECRET") - if err := comm.Upload(tempPath, keyContent); err != nil { - return err - } - - return p.runCommand(o, comm, p.linuxGetCommand(fmt.Sprintf("chown root:root %s && chmod 0600 %s && mv %s %s", tempPath, tempPath, tempPath, destination))) - } - - return comm.Upload(destination, keyContent) -} - -// -// Habitat Services -// -func (p *provisioner) linuxStartHabitatService(o terraform.UIOutput, comm communicator.Communicator, service Service) error { - var options string - - if err := p.linuxInstallHabitatPackage(o, comm, service); err != nil { - return err - } - if err := p.uploadUserTOML(o, comm, service); err != nil { - return err - } - - // Upload service group key - if service.ServiceGroupKey != "" { - err := p.uploadServiceGroupKey(o, comm, service.ServiceGroupKey) - if err != nil { - return err - } - } - - if service.Topology != "" { - options += fmt.Sprintf(" --topology %s", service.Topology) - } - - if service.Strategy != "" { - options += fmt.Sprintf(" --strategy %s", service.Strategy) - } - - if service.Channel != "" { - options += fmt.Sprintf(" --channel %s", service.Channel) - } - - if service.URL != "" { - options += fmt.Sprintf(" --url %s", service.URL) - } - - if service.Group != "" { - options += fmt.Sprintf(" --group %s", service.Group) - } - - for _, bind := range service.Binds { - options += fmt.Sprintf(" --bind %s", bind.toBindString()) - } - - return p.runCommand(o, comm, p.linuxGetCommand(fmt.Sprintf("hab svc load %s %s", service.Name, options))) -} - -// In the future we'll remove the dedicated install once the synchronous load feature in hab-sup is -// available. Until then we install here to provide output and a noisy failure mechanism because -// if you install with the pkg load, it occurs asynchronously and fails quietly. -func (p *provisioner) linuxInstallHabitatPackage(o terraform.UIOutput, comm communicator.Communicator, service Service) error { - var options string - - if service.Channel != "" { - options += fmt.Sprintf(" --channel %s", service.Channel) - } - - if service.URL != "" { - options += fmt.Sprintf(" --url %s", service.URL) - } - - return p.runCommand(o, comm, p.linuxGetCommand(fmt.Sprintf("hab pkg install %s %s", service.Name, options))) -} - -func (p *provisioner) uploadServiceGroupKey(o terraform.UIOutput, comm communicator.Communicator, key string) error { - keyName := strings.Split(key, "\n")[1] - o.Output("Uploading service group key: " + keyName) - keyFileName := fmt.Sprintf("%s.box.key", keyName) - destPath := path.Join("/hab/cache/keys", keyFileName) - keyContent := strings.NewReader(key) - if p.UseSudo { - tempPath := path.Join("/tmp", keyFileName) - if err := comm.Upload(tempPath, keyContent); err != nil { - return err - } - - return p.runCommand(o, comm, p.linuxGetCommand(fmt.Sprintf("mv %s %s", tempPath, destPath))) - } - - return comm.Upload(destPath, keyContent) -} - -func (p *provisioner) uploadUserTOML(o terraform.UIOutput, comm communicator.Communicator, service Service) error { - // Create the hab svc directory to lay down the user.toml before loading the service - o.Output("Uploading user.toml for service: " + service.Name) - destDir := fmt.Sprintf("/hab/user/%s/config", service.getPackageName(service.Name)) - command := p.linuxGetCommand(fmt.Sprintf("mkdir -p %s", destDir)) - if err := p.runCommand(o, comm, command); err != nil { - return err - } - - userToml := strings.NewReader(service.UserTOML) - - if p.UseSudo { - checksum := service.getServiceNameChecksum() - if err := comm.Upload(fmt.Sprintf("/tmp/user-%s.toml", checksum), userToml); err != nil { - return err - } - command = p.linuxGetCommand(fmt.Sprintf("chmod o-r /tmp/user-%s.toml && mv /tmp/user-%s.toml %s/user.toml", checksum, checksum, destDir)) - return p.runCommand(o, comm, command) - } - - return comm.Upload(path.Join(destDir, "user.toml"), userToml) -} - -func (p *provisioner) linuxGetCommand(command string) string { - // Always set HAB_NONINTERACTIVE & HAB_NOCOLORING - env := fmt.Sprintf("env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true") - - // Set builder auth token - if p.BuilderAuthToken != "" { - env += fmt.Sprintf(" HAB_AUTH_TOKEN=%s", p.BuilderAuthToken) - } - - if p.UseSudo { - command = fmt.Sprintf("%s sudo -E /bin/bash -c '%s'", env, command) - } else { - command = fmt.Sprintf("%s /bin/bash -c '%s'", env, command) - } - - return command -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/habitat/linux_provisioner_test.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/habitat/linux_provisioner_test.go deleted file mode 100644 index 2706fa2d..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/habitat/linux_provisioner_test.go +++ /dev/null @@ -1,348 +0,0 @@ -package habitat - -import ( - "github.com/hashicorp/terraform/communicator" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" - "testing" -) - -const linuxDefaultSystemdUnitFileContents = `[Unit] -Description=Habitat Supervisor - -[Service] -ExecStart=/bin/hab sup run --peer host1 --peer 1.2.3.4 --auto-update -Restart=on-failure -[Install] -WantedBy=default.target` - -const linuxCustomSystemdUnitFileContents = `[Unit] -Description=Habitat Supervisor - -[Service] -ExecStart=/bin/hab sup run --listen-ctl 192.168.0.1:8443 --listen-gossip 192.168.10.1:9443 --listen-http 192.168.20.1:8080 --peer host1 --peer host2 --peer 1.2.3.4 --peer 5.6.7.8 --peer foo.example.com -Restart=on-failure -Environment="HAB_SUP_GATEWAY_AUTH_TOKEN=ea7-beef" -Environment="HAB_AUTH_TOKEN=dead-beef" -[Install] -WantedBy=default.target` - -func TestLinuxProvisioner_linuxInstallHabitat(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - Commands map[string]bool - }{ - "Installation with sudo": { - Config: map[string]interface{}{ - "version": "0.79.1", - "auto_update": true, - "use_sudo": true, - }, - - Commands: map[string]bool{ - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'curl --silent -L0 https://raw.githubusercontent.com/habitat-sh/habitat/master/components/hab/install.sh > install.sh'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'bash ./install.sh -v 0.79.1'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'hab install core/busybox'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'hab pkg exec core/busybox adduser -D -g \"\" hab'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'rm -f install.sh'": true, - }, - }, - "Installation without sudo": { - Config: map[string]interface{}{ - "version": "0.79.1", - "auto_update": true, - "use_sudo": false, - }, - - Commands: map[string]bool{ - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true /bin/bash -c 'curl --silent -L0 https://raw.githubusercontent.com/habitat-sh/habitat/master/components/hab/install.sh > install.sh'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true /bin/bash -c 'bash ./install.sh -v 0.79.1'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true /bin/bash -c 'hab install core/busybox'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true /bin/bash -c 'hab pkg exec core/busybox adduser -D -g \"\" hab'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true /bin/bash -c 'rm -f install.sh'": true, - }, - }, - "Installation with Habitat license acceptance": { - Config: map[string]interface{}{ - "version": "0.81.0", - "accept_license": true, - "auto_update": true, - "use_sudo": true, - }, - - Commands: map[string]bool{ - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'curl --silent -L0 https://raw.githubusercontent.com/habitat-sh/habitat/master/components/hab/install.sh > install.sh'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'bash ./install.sh -v 0.81.0'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'hab install core/busybox'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'hab pkg exec core/busybox adduser -D -g \"\" hab'": true, - "env HAB_LICENSE=accept sudo -E /bin/bash -c 'hab -V'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'rm -f install.sh'": true, - }, - }, - } - - o := new(terraform.MockUIOutput) - c := new(communicator.MockCommunicator) - - for k, tc := range cases { - c.Commands = tc.Commands - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - err = p.linuxInstallHabitat(o, c) - if err != nil { - t.Fatalf("Test %q failed: %v", k, err) - } - } -} - -func TestLinuxProvisioner_linuxStartHabitat(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - Commands map[string]bool - Uploads map[string]string - }{ - "Start systemd Habitat with sudo": { - Config: map[string]interface{}{ - "version": "0.79.1", - "auto_update": true, - "use_sudo": true, - "service_name": "hab-sup", - "peer": "--peer host1", - "peers": []interface{}{"1.2.3.4"}, - }, - - Commands: map[string]bool{ - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'hab install core/hab-sup/0.79.1'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'systemctl enable hab-sup && systemctl start hab-sup'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'mv /tmp/hab-sup.service /etc/systemd/system/hab-sup.service'": true, - }, - - Uploads: map[string]string{ - "/tmp/hab-sup.service": linuxDefaultSystemdUnitFileContents, - }, - }, - "Start systemd Habitat without sudo": { - Config: map[string]interface{}{ - "version": "0.79.1", - "auto_update": true, - "use_sudo": false, - "service_name": "hab-sup", - "peer": "--peer host1", - "peers": []interface{}{"1.2.3.4"}, - }, - - Commands: map[string]bool{ - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true /bin/bash -c 'hab install core/hab-sup/0.79.1'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true /bin/bash -c 'systemctl enable hab-sup && systemctl start hab-sup'": true, - }, - - Uploads: map[string]string{ - "/etc/systemd/system/hab-sup.service": linuxDefaultSystemdUnitFileContents, - }, - }, - "Start unmanaged Habitat with sudo": { - Config: map[string]interface{}{ - "version": "0.81.0", - "license": "accept-no-persist", - "auto_update": true, - "use_sudo": true, - "service_type": "unmanaged", - "peer": "--peer host1", - "peers": []interface{}{"1.2.3.4"}, - }, - - Commands: map[string]bool{ - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'hab install core/hab-sup/0.81.0'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'mkdir -p /hab/sup/default && chmod o+w /hab/sup/default'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c '(setsid hab sup run --peer host1 --peer 1.2.3.4 --auto-update > /hab/sup/default/sup.log 2>&1 <&1 &) ; sleep 1'": true, - }, - - Uploads: map[string]string{ - "/etc/systemd/system/hab-sup.service": linuxDefaultSystemdUnitFileContents, - }, - }, - "Start Habitat with custom config": { - Config: map[string]interface{}{ - "version": "0.79.1", - "auto_update": false, - "use_sudo": true, - "service_name": "hab-sup", - "peer": "--peer host1 --peer host2", - "peers": []interface{}{"1.2.3.4", "5.6.7.8", "foo.example.com"}, - "listen_ctl": "192.168.0.1:8443", - "listen_gossip": "192.168.10.1:9443", - "listen_http": "192.168.20.1:8080", - "builder_auth_token": "dead-beef", - "gateway_auth_token": "ea7-beef", - "ctl_secret": "bad-beef", - }, - - Commands: map[string]bool{ - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true HAB_AUTH_TOKEN=dead-beef sudo -E /bin/bash -c 'hab install core/hab-sup/0.79.1'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true HAB_AUTH_TOKEN=dead-beef sudo -E /bin/bash -c 'systemctl enable hab-sup && systemctl start hab-sup'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true HAB_AUTH_TOKEN=dead-beef sudo -E /bin/bash -c 'mv /tmp/hab-sup.service /etc/systemd/system/hab-sup.service'": true, - }, - - Uploads: map[string]string{ - "/tmp/hab-sup.service": linuxCustomSystemdUnitFileContents, - }, - }, - } - - o := new(terraform.MockUIOutput) - c := new(communicator.MockCommunicator) - - for k, tc := range cases { - c.Commands = tc.Commands - c.Uploads = tc.Uploads - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - err = p.linuxStartHabitat(o, c) - if err != nil { - t.Fatalf("Test %q failed: %v", k, err) - } - } -} - -func TestLinuxProvisioner_linuxUploadRingKey(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - Commands map[string]bool - }{ - "Upload ring key": { - Config: map[string]interface{}{ - "version": "0.79.1", - "auto_update": true, - "use_sudo": true, - "service_name": "hab-sup", - "peers": []interface{}{"1.2.3.4"}, - "ring_key": "test-ring", - "ring_key_content": "dead-beef", - }, - - Commands: map[string]bool{ - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'echo -e \"dead-beef\" | hab ring key import'": true, - }, - }, - } - - o := new(terraform.MockUIOutput) - c := new(communicator.MockCommunicator) - - for k, tc := range cases { - c.Commands = tc.Commands - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - err = p.linuxUploadRingKey(o, c) - if err != nil { - t.Fatalf("Test %q failed: %v", k, err) - } - } -} - -func TestLinuxProvisioner_linuxStartHabitatService(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - Commands map[string]bool - Uploads map[string]string - }{ - "Start Habitat service with sudo": { - Config: map[string]interface{}{ - "version": "0.79.1", - "auto_update": false, - "use_sudo": true, - "service_name": "hab-sup", - "peers": []interface{}{"1.2.3.4"}, - "ring_key": "test-ring", - "ring_key_content": "dead-beef", - "service": []interface{}{ - map[string]interface{}{ - "name": "core/foo", - "topology": "standalone", - "strategy": "none", - "channel": "stable", - "user_toml": "[config]\nlisten = 0.0.0.0:8080", - "bind": []interface{}{ - map[string]interface{}{ - "alias": "backend", - "service": "bar", - "group": "default", - }, - }, - }, - map[string]interface{}{ - "name": "core/bar", - "topology": "standalone", - "strategy": "rolling", - "channel": "staging", - "user_toml": "[config]\nlisten = 0.0.0.0:443", - }, - }, - }, - - Commands: map[string]bool{ - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'hab pkg install core/foo --channel stable'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'mkdir -p /hab/user/foo/config'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'chmod o-r /tmp/user-a5b83ec1b302d109f41852ae17379f75c36dff9bc598aae76b6f7c9cd425fd76.toml && mv /tmp/user-a5b83ec1b302d109f41852ae17379f75c36dff9bc598aae76b6f7c9cd425fd76.toml /hab/user/foo/config/user.toml'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'hab svc load core/foo --topology standalone --strategy none --channel stable --bind backend:bar.default'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'hab pkg install core/bar --channel staging'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'mkdir -p /hab/user/bar/config'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'chmod o-r /tmp/user-6466ae3283ae1bd4737b00367bc676c6465b25682169ea5f7da222f3f078a5bf.toml && mv /tmp/user-6466ae3283ae1bd4737b00367bc676c6465b25682169ea5f7da222f3f078a5bf.toml /hab/user/bar/config/user.toml'": true, - "env HAB_NONINTERACTIVE=true HAB_NOCOLORING=true sudo -E /bin/bash -c 'hab svc load core/bar --topology standalone --strategy rolling --channel staging'": true, - }, - - Uploads: map[string]string{ - "/tmp/user-a5b83ec1b302d109f41852ae17379f75c36dff9bc598aae76b6f7c9cd425fd76.toml": "[config]\nlisten = 0.0.0.0:8080", - "/tmp/user-6466ae3283ae1bd4737b00367bc676c6465b25682169ea5f7da222f3f078a5bf.toml": "[config]\nlisten = 0.0.0.0:443", - }, - }, - } - - o := new(terraform.MockUIOutput) - c := new(communicator.MockCommunicator) - - for k, tc := range cases { - c.Commands = tc.Commands - c.Uploads = tc.Uploads - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - var errs []error - for _, s := range p.Services { - err = p.linuxStartHabitatService(o, c, s) - if err != nil { - errs = append(errs, err) - } - } - - if len(errs) > 0 { - for _, e := range errs { - t.Logf("Test %q failed: %v", k, e) - t.Fail() - } - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/habitat/resource_provisioner.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/habitat/resource_provisioner.go deleted file mode 100644 index 87534a6f..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/habitat/resource_provisioner.go +++ /dev/null @@ -1,572 +0,0 @@ -package habitat - -import ( - "context" - "crypto/sha256" - "errors" - "fmt" - "io" - "net/url" - "strings" - - version "github.com/hashicorp/go-version" - "github.com/hashicorp/terraform/communicator" - "github.com/hashicorp/terraform/communicator/remote" - "github.com/hashicorp/terraform/configs/hcl2shim" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/helper/validation" - "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/go-linereader" -) - -type provisioner struct { - Version string - AutoUpdate bool - HttpDisable bool - Services []Service - PermanentPeer bool - ListenCtl string - ListenGossip string - ListenHTTP string - Peer string - Peers []string - RingKey string - RingKeyContent string - CtlSecret string - SkipInstall bool - UseSudo bool - ServiceType string - ServiceName string - URL string - Channel string - Events string - Organization string - GatewayAuthToken string - BuilderAuthToken string - SupOptions string - AcceptLicense bool - - installHabitat provisionFn - startHabitat provisionFn - uploadRingKey provisionFn - uploadCtlSecret provisionFn - startHabitatService provisionServiceFn - - osType string -} - -type provisionFn func(terraform.UIOutput, communicator.Communicator) error -type provisionServiceFn func(terraform.UIOutput, communicator.Communicator, Service) error - -func Provisioner() terraform.ResourceProvisioner { - return &schema.Provisioner{ - Schema: map[string]*schema.Schema{ - "version": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "auto_update": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - "http_disable": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - "peer": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "peers": &schema.Schema{ - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - }, - "service_type": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: "systemd", - ValidateFunc: validation.StringInSlice([]string{"systemd", "unmanaged"}, false), - }, - "service_name": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: "hab-supervisor", - }, - "use_sudo": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: true, - }, - "accept_license": &schema.Schema{ - Type: schema.TypeBool, - Required: true, - }, - "permanent_peer": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - "listen_ctl": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "listen_gossip": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "listen_http": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "ring_key": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "ring_key_content": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "ctl_secret": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "url": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) { - u, err := url.Parse(val.(string)) - if err != nil { - errs = append(errs, fmt.Errorf("invalid URL specified for %q: %v", key, err)) - } - - if u.Scheme == "" { - errs = append(errs, fmt.Errorf("invalid URL specified for %q (scheme must be specified)", key)) - } - - return warns, errs - }, - }, - "channel": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "events": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "organization": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "gateway_auth_token": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "builder_auth_token": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "service": &schema.Schema{ - Type: schema.TypeSet, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - "binds": &schema.Schema{ - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - }, - "bind": &schema.Schema{ - Type: schema.TypeSet, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "alias": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - "service": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - "group": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - }, - }, - Optional: true, - }, - "topology": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice([]string{"leader", "standalone"}, false), - }, - "user_toml": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "strategy": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice([]string{"none", "rolling", "at-once"}, false), - }, - "channel": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "group": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "url": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) { - u, err := url.Parse(val.(string)) - if err != nil { - errs = append(errs, fmt.Errorf("invalid URL specified for %q: %v", key, err)) - } - - if u.Scheme == "" { - errs = append(errs, fmt.Errorf("invalid URL specified for %q (scheme must be specified)", key)) - } - - return warns, errs - }, - }, - "application": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "environment": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "service_key": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - }, - }, - Optional: true, - }, - }, - ApplyFunc: applyFn, - ValidateFunc: validateFn, - } -} - -func applyFn(ctx context.Context) error { - o := ctx.Value(schema.ProvOutputKey).(terraform.UIOutput) - s := ctx.Value(schema.ProvRawStateKey).(*terraform.InstanceState) - d := ctx.Value(schema.ProvConfigDataKey).(*schema.ResourceData) - - p, err := decodeConfig(d) - if err != nil { - return err - } - - // Automatically determine the OS type - switch t := s.Ephemeral.ConnInfo["type"]; t { - case "ssh", "": - p.osType = "linux" - case "winrm": - p.osType = "windows" - default: - return fmt.Errorf("unsupported connection type: %s", t) - } - - switch p.osType { - case "linux": - p.installHabitat = p.linuxInstallHabitat - p.uploadRingKey = p.linuxUploadRingKey - p.uploadCtlSecret = p.linuxUploadCtlSecret - p.startHabitat = p.linuxStartHabitat - p.startHabitatService = p.linuxStartHabitatService - case "windows": - return fmt.Errorf("windows is not supported yet for the habitat provisioner") - default: - return fmt.Errorf("unsupported os type: %s", p.osType) - } - - // Get a new communicator - comm, err := communicator.New(s) - if err != nil { - return err - } - - retryCtx, cancel := context.WithTimeout(ctx, comm.Timeout()) - defer cancel() - - // Wait and retry until we establish the connection - err = communicator.Retry(retryCtx, func() error { - return comm.Connect(o) - }) - - if err != nil { - return err - } - defer comm.Disconnect() - - if !p.SkipInstall { - o.Output("Installing habitat...") - if err := p.installHabitat(o, comm); err != nil { - return err - } - } - - if p.RingKeyContent != "" { - o.Output("Uploading supervisor ring key...") - if err := p.uploadRingKey(o, comm); err != nil { - return err - } - } - - if p.CtlSecret != "" { - o.Output("Uploading ctl secret...") - if err := p.uploadCtlSecret(o, comm); err != nil { - return err - } - } - - o.Output("Starting the habitat supervisor...") - if err := p.startHabitat(o, comm); err != nil { - return err - } - - if p.Services != nil { - for _, service := range p.Services { - o.Output("Starting service: " + service.Name) - if err := p.startHabitatService(o, comm, service); err != nil { - return err - } - } - } - - return nil -} - -func validateFn(c *terraform.ResourceConfig) (ws []string, es []error) { - ringKeyContent, ok := c.Get("ring_key_content") - if ok && ringKeyContent != "" && ringKeyContent != hcl2shim.UnknownVariableValue { - ringKey, ringOk := c.Get("ring_key") - if ringOk && ringKey == "" { - es = append(es, errors.New("if ring_key_content is specified, ring_key must be specified as well")) - } - } - - v, ok := c.Get("version") - if ok && v != nil && strings.TrimSpace(v.(string)) != "" { - if _, err := version.NewVersion(v.(string)); err != nil { - es = append(es, errors.New(v.(string)+" is not a valid version.")) - } - } - - acceptLicense, ok := c.Get("accept_license") - if ok && !acceptLicense.(bool) { - if v != nil && strings.TrimSpace(v.(string)) != "" { - versionOld, _ := version.NewVersion("0.79.0") - versionRequired, _ := version.NewVersion(v.(string)) - if versionRequired.GreaterThan(versionOld) { - es = append(es, errors.New("Habitat end user license agreement needs to be accepted, set the accept_license argument to true to accept")) - } - } else { // blank means latest version - es = append(es, errors.New("Habitat end user license agreement needs to be accepted, set the accept_license argument to true to accept")) - } - } - - // Validate service level configs - services, ok := c.Get("service") - if ok { - data, dataOk := services.(string) - if dataOk { - es = append(es, fmt.Errorf("service '%v': must be a block", data)) - } - } - - return ws, es -} - -type Service struct { - Name string - Strategy string - Topology string - Channel string - Group string - URL string - Binds []Bind - BindStrings []string - UserTOML string - AppName string - Environment string - ServiceGroupKey string -} - -func (s *Service) getPackageName(fullName string) string { - return strings.Split(fullName, "/")[1] -} - -func (s *Service) getServiceNameChecksum() string { - return fmt.Sprintf("%x", sha256.Sum256([]byte(s.Name))) -} - -type Bind struct { - Alias string - Service string - Group string -} - -func (b *Bind) toBindString() string { - return fmt.Sprintf("%s:%s.%s", b.Alias, b.Service, b.Group) -} - -func decodeConfig(d *schema.ResourceData) (*provisioner, error) { - p := &provisioner{ - Version: d.Get("version").(string), - AutoUpdate: d.Get("auto_update").(bool), - HttpDisable: d.Get("http_disable").(bool), - Peer: d.Get("peer").(string), - Peers: getPeers(d.Get("peers").([]interface{})), - Services: getServices(d.Get("service").(*schema.Set).List()), - UseSudo: d.Get("use_sudo").(bool), - AcceptLicense: d.Get("accept_license").(bool), - ServiceType: d.Get("service_type").(string), - ServiceName: d.Get("service_name").(string), - RingKey: d.Get("ring_key").(string), - RingKeyContent: d.Get("ring_key_content").(string), - CtlSecret: d.Get("ctl_secret").(string), - PermanentPeer: d.Get("permanent_peer").(bool), - ListenCtl: d.Get("listen_ctl").(string), - ListenGossip: d.Get("listen_gossip").(string), - ListenHTTP: d.Get("listen_http").(string), - URL: d.Get("url").(string), - Channel: d.Get("channel").(string), - Events: d.Get("events").(string), - Organization: d.Get("organization").(string), - BuilderAuthToken: d.Get("builder_auth_token").(string), - GatewayAuthToken: d.Get("gateway_auth_token").(string), - } - - return p, nil -} - -func getPeers(v []interface{}) []string { - peers := make([]string, 0, len(v)) - for _, rawPeerData := range v { - peers = append(peers, rawPeerData.(string)) - } - return peers -} - -func getServices(v []interface{}) []Service { - services := make([]Service, 0, len(v)) - for _, rawServiceData := range v { - serviceData := rawServiceData.(map[string]interface{}) - name := (serviceData["name"].(string)) - strategy := (serviceData["strategy"].(string)) - topology := (serviceData["topology"].(string)) - channel := (serviceData["channel"].(string)) - group := (serviceData["group"].(string)) - url := (serviceData["url"].(string)) - app := (serviceData["application"].(string)) - env := (serviceData["environment"].(string)) - userToml := (serviceData["user_toml"].(string)) - serviceGroupKey := (serviceData["service_key"].(string)) - var bindStrings []string - binds := getBinds(serviceData["bind"].(*schema.Set).List()) - for _, b := range serviceData["binds"].([]interface{}) { - bind, err := getBindFromString(b.(string)) - if err != nil { - return nil - } - binds = append(binds, bind) - } - - service := Service{ - Name: name, - Strategy: strategy, - Topology: topology, - Channel: channel, - Group: group, - URL: url, - UserTOML: userToml, - BindStrings: bindStrings, - Binds: binds, - AppName: app, - Environment: env, - ServiceGroupKey: serviceGroupKey, - } - services = append(services, service) - } - return services -} - -func getBinds(v []interface{}) []Bind { - binds := make([]Bind, 0, len(v)) - for _, rawBindData := range v { - bindData := rawBindData.(map[string]interface{}) - alias := bindData["alias"].(string) - service := bindData["service"].(string) - group := bindData["group"].(string) - bind := Bind{ - Alias: alias, - Service: service, - Group: group, - } - binds = append(binds, bind) - } - return binds -} - -func (p *provisioner) copyOutput(o terraform.UIOutput, r io.Reader) { - lr := linereader.New(r) - for line := range lr.Ch { - o.Output(line) - } -} - -func (p *provisioner) runCommand(o terraform.UIOutput, comm communicator.Communicator, command string) error { - outR, outW := io.Pipe() - errR, errW := io.Pipe() - - go p.copyOutput(o, outR) - go p.copyOutput(o, errR) - defer outW.Close() - defer errW.Close() - - cmd := &remote.Cmd{ - Command: command, - Stdout: outW, - Stderr: errW, - } - - if err := comm.Start(cmd); err != nil { - return fmt.Errorf("error executing command %q: %v", cmd.Command, err) - } - - if err := cmd.Wait(); err != nil { - return err - } - - return nil -} - -func getBindFromString(bind string) (Bind, error) { - t := strings.FieldsFunc(bind, func(d rune) bool { - switch d { - case ':', '.': - return true - } - return false - }) - if len(t) != 3 { - return Bind{}, errors.New("invalid bind specification: " + bind) - } - return Bind{Alias: t[0], Service: t[1], Group: t[2]}, nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/habitat/resource_provisioner_test.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/habitat/resource_provisioner_test.go deleted file mode 100644 index 054aa9c1..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/habitat/resource_provisioner_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package habitat - -import ( - "testing" - - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" -) - -func TestResourceProvisioner_impl(t *testing.T) { - var _ terraform.ResourceProvisioner = Provisioner() -} - -func TestProvisioner(t *testing.T) { - if err := Provisioner().(*schema.Provisioner).InternalValidate(); err != nil { - t.Fatalf("error: %s", err) - } -} - -func TestResourceProvisioner_Validate_good(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "peers": []interface{}{"1.2.3.4"}, - "version": "0.32.0", - "service_type": "systemd", - "accept_license": false, - }) - - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) > 0 { - t.Fatalf("Errors: %v", errs) - } -} - -func TestResourceProvisioner_Validate_bad(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "service_type": "invalidtype", - "url": "badurl", - }) - - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - // 3 errors, bad service_type, bad url, missing accept_license - if len(errs) != 3 { - t.Fatalf("Should have three errors, got %d", len(errs)) - } -} - -func TestResourceProvisioner_Validate_bad_service_config(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "accept_license": true, - "service": []interface{}{ - map[string]interface{}{ - "name": "core/foo", - "strategy": "bar", - "topology": "baz", - "url": "badurl", - }, - }, - }) - - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) != 3 { - t.Fatalf("Should have three errors, got %d", len(errs)) - } -} - -func TestResourceProvisioner_Validate_bad_service_definition(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "service": "core/vault", - }) - - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) != 3 { - t.Fatalf("Should have three errors, got %d", len(errs)) - } -} - -func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig { - return terraform.NewResourceConfigRaw(c) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/local-exec/resource_provisioner.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/local-exec/resource_provisioner.go index f0ba28f3..618df7e6 100644 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/local-exec/resource_provisioner.go +++ b/vendor/github.com/hashicorp/terraform/builtin/provisioners/local-exec/resource_provisioner.go @@ -9,9 +9,10 @@ import ( "runtime" "github.com/armon/circbuf" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/provisioners" "github.com/mitchellh/go-linereader" + "github.com/zclconf/go-cty/cty" ) const ( @@ -21,58 +22,81 @@ const ( maxBufSize = 8 * 1024 ) -func Provisioner() terraform.ResourceProvisioner { - return &schema.Provisioner{ - Schema: map[string]*schema.Schema{ - "command": &schema.Schema{ - Type: schema.TypeString, +func New() provisioners.Interface { + ctx, cancel := context.WithCancel(context.Background()) + return &provisioner{ + ctx: ctx, + cancel: cancel, + } +} + +type provisioner struct { + // We store a context here tied to the lifetime of the provisioner. + // This allows the Stop method to cancel any in-flight requests. + ctx context.Context + cancel context.CancelFunc +} + +func (p *provisioner) GetSchema() (resp provisioners.GetSchemaResponse) { + schema := &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "command": { + Type: cty.String, Required: true, }, - "interpreter": &schema.Schema{ - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeString}, + "interpreter": { + Type: cty.List(cty.String), Optional: true, }, - "working_dir": &schema.Schema{ - Type: schema.TypeString, + "working_dir": { + Type: cty.String, Optional: true, }, - "environment": &schema.Schema{ - Type: schema.TypeMap, + "environment": { + Type: cty.Map(cty.String), Optional: true, }, }, - - ApplyFunc: applyFn, } + + resp.Provisioner = schema + return resp } -func applyFn(ctx context.Context) error { - data := ctx.Value(schema.ProvConfigDataKey).(*schema.ResourceData) - o := ctx.Value(schema.ProvOutputKey).(terraform.UIOutput) +func (p *provisioner) ValidateProvisionerConfig(req provisioners.ValidateProvisionerConfigRequest) (resp provisioners.ValidateProvisionerConfigResponse) { + if _, err := p.GetSchema().Provisioner.CoerceValue(req.Config); err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + } + return resp +} - command := data.Get("command").(string) +func (p *provisioner) ProvisionResource(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { + command := req.Config.GetAttr("command").AsString() if command == "" { - return fmt.Errorf("local-exec provisioner command must be a non-empty string") + resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("local-exec provisioner command must be a non-empty string")) + return resp } - // Execute the command with env - environment := data.Get("environment").(map[string]interface{}) - + envVal := req.Config.GetAttr("environment") var env []string - for k := range environment { - entry := fmt.Sprintf("%s=%s", k, environment[k].(string)) - env = append(env, entry) + + if !envVal.IsNull() { + for k, v := range envVal.AsValueMap() { + if !v.IsNull() { + entry := fmt.Sprintf("%s=%s", k, v.AsString()) + env = append(env, entry) + } + } } // Execute the command using a shell - interpreter := data.Get("interpreter").([]interface{}) + intrVal := req.Config.GetAttr("interpreter") var cmdargs []string - if len(interpreter) > 0 { - for _, i := range interpreter { - if arg, ok := i.(string); ok { - cmdargs = append(cmdargs, arg) + if !intrVal.IsNull() && intrVal.LengthInt() > 0 { + for _, v := range intrVal.AsValueSlice() { + if !v.IsNull() { + cmdargs = append(cmdargs, v.AsString()) } } } else { @@ -82,25 +106,30 @@ func applyFn(ctx context.Context) error { cmdargs = []string{"/bin/sh", "-c"} } } + cmdargs = append(cmdargs, command) - workingdir := data.Get("working_dir").(string) + workingdir := "" + if wdVal := req.Config.GetAttr("working_dir"); !wdVal.IsNull() { + workingdir = wdVal.AsString() + } - // Setup the reader that will read the output from the command. + // Set up the reader that will read the output from the command. // We use an os.Pipe so that the *os.File can be passed directly to the // process, and not rely on goroutines copying the data which may block. // See golang.org/issue/18874 pr, pw, err := os.Pipe() if err != nil { - return fmt.Errorf("failed to initialize pipe for output: %s", err) + resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("failed to initialize pipe for output: %s", err)) + return resp } var cmdEnv []string cmdEnv = os.Environ() cmdEnv = append(cmdEnv, env...) - // Setup the command - cmd := exec.CommandContext(ctx, cmdargs[0], cmdargs[1:]...) + // Set up the command + cmd := exec.CommandContext(p.ctx, cmdargs[0], cmdargs[1:]...) cmd.Stderr = pw cmd.Stdout = pw // Dir specifies the working directory of the command. @@ -118,10 +147,10 @@ func applyFn(ctx context.Context) error { // copy the teed output to the UI output copyDoneCh := make(chan struct{}) - go copyOutput(o, tee, copyDoneCh) + go copyUIOutput(req.UIOutput, tee, copyDoneCh) // Output what we're about to run - o.Output(fmt.Sprintf("Executing: %q", cmdargs)) + req.UIOutput.Output(fmt.Sprintf("Executing: %q", cmdargs)) // Start the command err = cmd.Start() @@ -138,18 +167,28 @@ func applyFn(ctx context.Context) error { // copyOutput goroutine will just hang out until exit. select { case <-copyDoneCh: - case <-ctx.Done(): + case <-p.ctx.Done(): } if err != nil { - return fmt.Errorf("Error running command '%s': %v. Output: %s", - command, err, output.Bytes()) + resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Error running command '%s': %v. Output: %s", + command, err, output.Bytes())) + return resp } + return resp +} + +func (p *provisioner) Stop() error { + p.cancel() + return nil +} + +func (p *provisioner) Close() error { return nil } -func copyOutput(o terraform.UIOutput, r io.Reader, doneCh chan<- struct{}) { +func copyUIOutput(o provisioners.UIOutput, r io.Reader, doneCh chan<- struct{}) { defer close(doneCh) lr := linereader.New(r) for line := range lr.Ch { diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/local-exec/resource_provisioner_test.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/local-exec/resource_provisioner_test.go index 8718d4da..f10a3ac1 100644 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/local-exec/resource_provisioner_test.go +++ b/vendor/github.com/hashicorp/terraform/builtin/provisioners/local-exec/resource_provisioner_test.go @@ -1,37 +1,37 @@ package localexec import ( + "fmt" "io/ioutil" "os" "strings" "testing" "time" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/provisioners" + "github.com/mitchellh/cli" + "github.com/zclconf/go-cty/cty" ) -func TestResourceProvisioner_impl(t *testing.T) { - var _ terraform.ResourceProvisioner = Provisioner() -} - -func TestProvisioner(t *testing.T) { - if err := Provisioner().(*schema.Provisioner).InternalValidate(); err != nil { - t.Fatalf("err: %s", err) - } -} - func TestResourceProvider_Apply(t *testing.T) { defer os.Remove("test_out") - c := testConfig(t, map[string]interface{}{ - "command": "echo foo > test_out", - }) + output := cli.NewMockUi() + p := New() + schema := p.GetSchema().Provisioner + c, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ + "command": cty.StringVal("echo foo > test_out"), + })) + if err != nil { + t.Fatal(err) + } - output := new(terraform.MockUIOutput) - p := Provisioner() + resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{ + Config: c, + UIOutput: output, + }) - if err := p.Apply(output, nil, c); err != nil { - t.Fatalf("err: %v", err) + if resp.Diagnostics.HasErrors() { + t.Fatalf("err: %v", resp.Diagnostics.Err()) } // Check the file @@ -48,14 +48,18 @@ func TestResourceProvider_Apply(t *testing.T) { } func TestResourceProvider_stop(t *testing.T) { - c := testConfig(t, map[string]interface{}{ + output := cli.NewMockUi() + p := New() + schema := p.GetSchema().Provisioner + + c, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ // bash/zsh/ksh will exec a single command in the same process. This // makes certain there's a subprocess in the shell. - "command": "sleep 30; sleep 30", - }) - - output := new(terraform.MockUIOutput) - p := Provisioner() + "command": cty.StringVal("sleep 30; sleep 30"), + })) + if err != nil { + t.Fatal(err) + } doneCh := make(chan struct{}) startTime := time.Now() @@ -65,7 +69,10 @@ func TestResourceProvider_stop(t *testing.T) { // Because p.Apply is called in a goroutine, trying to t.Fatal() on its // result would be ignored or would cause a panic if the parent goroutine // has already completed. - _ = p.Apply(output, nil, c) + _ = p.ProvisionResource(provisioners.ProvisionResourceRequest{ + Config: c, + UIOutput: output, + }) }() mustExceed := (50 * time.Millisecond) @@ -90,51 +97,32 @@ func TestResourceProvider_stop(t *testing.T) { } } -func TestResourceProvider_Validate_good(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "command": "echo foo", - }) - - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) > 0 { - t.Fatalf("Errors: %v", errs) - } -} +func TestResourceProvider_ApplyCustomInterpreter(t *testing.T) { + output := cli.NewMockUi() + p := New() -func TestResourceProvider_Validate_missing(t *testing.T) { - c := testConfig(t, map[string]interface{}{}) + schema := p.GetSchema().Provisioner - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) == 0 { - t.Fatalf("Should have errors") + c, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ + "interpreter": cty.ListVal([]cty.Value{cty.StringVal("echo"), cty.StringVal("is")}), + "command": cty.StringVal("not really an interpreter"), + })) + if err != nil { + t.Fatal(err) } -} -func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig { - return terraform.NewResourceConfigRaw(c) -} - -func TestResourceProvider_ApplyCustomInterpreter(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "interpreter": []interface{}{"echo", "is"}, - "command": "not really an interpreter", + resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{ + Config: c, + UIOutput: output, }) - output := new(terraform.MockUIOutput) - p := Provisioner() - - if err := p.Apply(output, nil, c); err != nil { - t.Fatalf("err: %v", err) + if resp.Diagnostics.HasErrors() { + t.Fatal(resp.Diagnostics.Err()) } - got := strings.TrimSpace(output.OutputMessage) - want := "is not really an interpreter" + got := strings.TrimSpace(output.OutputWriter.String()) + want := `Executing: ["echo" "is" "not really an interpreter"] +is not really an interpreter` if got != want { t.Errorf("wrong output\ngot: %s\nwant: %s", got, want) } @@ -145,16 +133,25 @@ func TestResourceProvider_ApplyCustomWorkingDirectory(t *testing.T) { os.Mkdir(testdir, 0755) defer os.Remove(testdir) - c := testConfig(t, map[string]interface{}{ - "working_dir": testdir, - "command": "echo `pwd`", - }) + output := cli.NewMockUi() + p := New() + schema := p.GetSchema().Provisioner - output := new(terraform.MockUIOutput) - p := Provisioner() + c, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ + "working_dir": cty.StringVal(testdir), + "command": cty.StringVal("echo `pwd`"), + })) + if err != nil { + t.Fatal(err) + } - if err := p.Apply(output, nil, c); err != nil { - t.Fatalf("err: %v", err) + resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{ + Config: c, + UIOutput: output, + }) + + if resp.Diagnostics.HasErrors() { + t.Fatal(resp.Diagnostics.Err()) } dir, err := os.Getwd() @@ -162,33 +159,94 @@ func TestResourceProvider_ApplyCustomWorkingDirectory(t *testing.T) { t.Fatalf("err: %v", err) } - got := strings.TrimSpace(output.OutputMessage) - want := dir + "/" + testdir + got := strings.TrimSpace(output.OutputWriter.String()) + want := "Executing: [\"/bin/sh\" \"-c\" \"echo `pwd`\"]\n" + dir + "/" + testdir if got != want { t.Errorf("wrong output\ngot: %s\nwant: %s", got, want) } } func TestResourceProvider_ApplyCustomEnv(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "command": "echo $FOO $BAR $BAZ", - "environment": map[string]interface{}{ - "FOO": "BAR", - "BAR": 1, - "BAZ": "true", - }, - }) - - output := new(terraform.MockUIOutput) - p := Provisioner() + output := cli.NewMockUi() + p := New() + schema := p.GetSchema().Provisioner + + c, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ + "command": cty.StringVal("echo $FOO $BAR $BAZ"), + "environment": cty.MapVal(map[string]cty.Value{ + "FOO": cty.StringVal("BAR"), + "BAR": cty.StringVal("1"), + "BAZ": cty.StringVal("true"), + }), + })) + if err != nil { + t.Fatal(err) + } - if err := p.Apply(output, nil, c); err != nil { - t.Fatalf("err: %v", err) + resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{ + Config: c, + UIOutput: output, + }) + if resp.Diagnostics.HasErrors() { + t.Fatal(resp.Diagnostics.Err()) } - got := strings.TrimSpace(output.OutputMessage) - want := "BAR 1 true" + got := strings.TrimSpace(output.OutputWriter.String()) + want := `Executing: ["/bin/sh" "-c" "echo $FOO $BAR $BAZ"] +BAR 1 true` if got != want { t.Errorf("wrong output\ngot: %s\nwant: %s", got, want) } } + +// Validate that Stop can Close can be called even when not provisioning. +func TestResourceProvisioner_StopClose(t *testing.T) { + p := New() + p.Stop() + p.Close() +} + +func TestResourceProvisioner_nullsInOptionals(t *testing.T) { + output := cli.NewMockUi() + p := New() + schema := p.GetSchema().Provisioner + + for i, cfg := range []cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "command": cty.StringVal("echo OK"), + "environment": cty.MapVal(map[string]cty.Value{ + "FOO": cty.NullVal(cty.String), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "command": cty.StringVal("echo OK"), + "environment": cty.NullVal(cty.Map(cty.String)), + }), + cty.ObjectVal(map[string]cty.Value{ + "command": cty.StringVal("echo OK"), + "interpreter": cty.ListVal([]cty.Value{cty.NullVal(cty.String)}), + }), + cty.ObjectVal(map[string]cty.Value{ + "command": cty.StringVal("echo OK"), + "interpreter": cty.NullVal(cty.List(cty.String)), + }), + cty.ObjectVal(map[string]cty.Value{ + "command": cty.StringVal("echo OK"), + "working_dir": cty.NullVal(cty.String), + }), + } { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + + cfg, err := schema.CoerceValue(cfg) + if err != nil { + t.Fatal(err) + } + + // verifying there are no panics + p.ProvisionResource(provisioners.ProvisionResourceRequest{ + Config: cfg, + UIOutput: output, + }) + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/bolt/bolt.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/bolt/bolt.go deleted file mode 100644 index 0fc70e32..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/bolt/bolt.go +++ /dev/null @@ -1,74 +0,0 @@ -package bolt - -import ( - "context" - "encoding/json" - "fmt" - "os/exec" - "runtime" - "strings" - "time" -) - -type Result struct { - Items []struct { - Node string `json:"node"` - Status string `json:"status"` - Result map[string]string `json:"result"` - } `json:"items"` - NodeCount int `json:"node_count"` - ElapsedTime int `json:"elapsed_time"` -} - -func runCommand(command string, timeout time.Duration) ([]byte, error) { - var cmdargs []string - - if runtime.GOOS == "windows" { - cmdargs = []string{"cmd", "/C"} - } else { - cmdargs = []string{"/bin/sh", "-c"} - } - cmdargs = append(cmdargs, command) - - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - cmd := exec.CommandContext(ctx, cmdargs[0], cmdargs[1:]...) - return cmd.Output() -} - -func Task(connInfo map[string]string, timeout time.Duration, sudo bool, task string, args map[string]string) (*Result, error) { - cmdargs := []string{ - "bolt", "task", "run", "--nodes", connInfo["type"] + "://" + connInfo["host"], "-u", connInfo["user"], - } - - if connInfo["type"] == "winrm" { - cmdargs = append(cmdargs, "-p", "\""+connInfo["password"]+"\"", "--no-ssl") - } else { - if sudo { - cmdargs = append(cmdargs, "--run-as", "root") - } - - cmdargs = append(cmdargs, "--no-host-key-check") - } - - cmdargs = append(cmdargs, "--format", "json", "--connect-timeout", "120", task) - - if args != nil { - for key, value := range args { - cmdargs = append(cmdargs, strings.Join([]string{key, value}, "=")) - } - } - - out, err := runCommand(strings.Join(cmdargs, " "), timeout) - if err != nil { - return nil, fmt.Errorf("Bolt: \"%s\": %s: %s", strings.Join(cmdargs, " "), out, err) - } - - result := new(Result) - if err = json.Unmarshal(out, result); err != nil { - return nil, err - } - - return result, nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/linux_provisioner.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/linux_provisioner.go deleted file mode 100644 index f480b0e7..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/linux_provisioner.go +++ /dev/null @@ -1,65 +0,0 @@ -package puppet - -import ( - "fmt" - "io" - - "github.com/hashicorp/terraform/communicator/remote" -) - -func (p *provisioner) linuxUploadFile(f io.Reader, dir string, filename string) error { - _, err := p.runCommand("mkdir -p " + dir) - if err != nil { - return fmt.Errorf("Failed to make directory %s: %s", dir, err) - } - - err = p.comm.Upload("/tmp/"+filename, f) - if err != nil { - return fmt.Errorf("Failed to upload %s to /tmp: %s", filename, err) - } - - _, err = p.runCommand(fmt.Sprintf("mv /tmp/%s %s/%s", filename, dir, filename)) - return err -} - -func (p *provisioner) linuxDefaultCertname() (string, error) { - certname, err := p.runCommand("hostname -f") - if err != nil { - return "", err - } - - return certname, nil -} - -func (p *provisioner) linuxInstallPuppetAgent() error { - _, err := p.runCommand(fmt.Sprintf("curl -kO https://%s:8140/packages/current/install.bash", p.Server)) - if err != nil { - return err - } - - _, err = p.runCommand("bash -- ./install.bash --puppet-service-ensure stopped") - if err != nil { - return err - } - - _, err = p.runCommand("rm -f install.bash") - return err -} - -func (p *provisioner) linuxRunPuppetAgent() error { - _, err := p.runCommand(fmt.Sprintf( - "/opt/puppetlabs/puppet/bin/puppet agent --test --server %s --environment %s", - p.Server, - p.Environment, - )) - - // Puppet exits 2 if changes have been successfully made. - if err != nil { - errStruct, _ := err.(*remote.ExitError) - if errStruct.ExitStatus == 2 { - return nil - } - } - - return err -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/linux_provisioner_test.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/linux_provisioner_test.go deleted file mode 100644 index 828d6670..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/linux_provisioner_test.go +++ /dev/null @@ -1,379 +0,0 @@ -package puppet - -import ( - "io" - "strings" - "testing" - - "github.com/hashicorp/terraform/communicator" - "github.com/hashicorp/terraform/communicator/remote" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" -) - -func TestResourceProvisioner_linuxUploadFile(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - Commands map[string]bool - CommandFunc func(*remote.Cmd) error - ExpectedError bool - Uploads map[string]string - File io.Reader - Dir string - Filename string - }{ - "Successful upload": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - Commands: map[string]bool{ - "mkdir -p /etc/puppetlabs/puppet": true, - "mv /tmp/csr_attributes.yaml /etc/puppetlabs/puppet/csr_attributes.yaml": true, - }, - Uploads: map[string]string{ - "/tmp/csr_attributes.yaml": "", - }, - Dir: "/etc/puppetlabs/puppet", - Filename: "csr_attributes.yaml", - File: strings.NewReader(""), - }, - "Failure when creating the directory": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - Commands: map[string]bool{ - "mkdir -p /etc/puppetlabs/puppet": true, - }, - Dir: "/etc/puppetlabs/puppet", - Filename: "csr_attributes.yaml", - File: strings.NewReader(""), - CommandFunc: func(r *remote.Cmd) error { - r.SetExitStatus(1, &remote.ExitError{ - Command: "mkdir -p /etc/puppetlabs/puppet", - ExitStatus: 1, - Err: nil, - }) - return nil - }, - ExpectedError: true, - }, - } - - for k, tc := range cases { - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - c := new(communicator.MockCommunicator) - c.Commands = tc.Commands - c.Uploads = tc.Uploads - if tc.CommandFunc != nil { - c.CommandFunc = tc.CommandFunc - } - p.comm = c - p.output = new(terraform.MockUIOutput) - - err = p.linuxUploadFile(tc.File, tc.Dir, tc.Filename) - if tc.ExpectedError { - if err == nil { - t.Fatalf("Expected error, but no error returned") - } - } else { - if err != nil { - t.Fatalf("Test %q failed: %v", k, err) - } - } - } -} - -func TestResourceProvisioner_linuxDefaultCertname(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - Commands map[string]bool - CommandFunc func(*remote.Cmd) error - ExpectedError bool - }{ - "No sudo": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - Commands: map[string]bool{ - "hostname -f": true, - }, - }, - "With sudo": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": true, - }, - Commands: map[string]bool{ - "sudo hostname -f": true, - }, - }, - "Failed execution": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - Commands: map[string]bool{ - "hostname -f": true, - }, - CommandFunc: func(r *remote.Cmd) error { - if r.Command == "hostname -f" { - r.SetExitStatus(1, &remote.ExitError{ - Command: "hostname -f", - ExitStatus: 1, - Err: nil, - }) - } else { - r.SetExitStatus(0, nil) - } - return nil - }, - ExpectedError: true, - }, - } - - for k, tc := range cases { - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - c := new(communicator.MockCommunicator) - c.Commands = tc.Commands - if tc.CommandFunc != nil { - c.CommandFunc = tc.CommandFunc - } - p.comm = c - p.output = new(terraform.MockUIOutput) - - _, err = p.linuxDefaultCertname() - if tc.ExpectedError { - if err == nil { - t.Fatalf("Expected error, but no error returned") - } - } else { - if err != nil { - t.Fatalf("Test %q failed: %v", k, err) - } - } - } -} - -func TestResourceProvisioner_linuxInstallPuppetAgent(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - Commands map[string]bool - CommandFunc func(*remote.Cmd) error - ExpectedError bool - }{ - "Everything runs succcessfully": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - Commands: map[string]bool{ - "curl -kO https://puppet.test.com:8140/packages/current/install.bash": true, - "bash -- ./install.bash --puppet-service-ensure stopped": true, - "rm -f install.bash": true, - }, - }, - "Respects the use_sudo config flag": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": true, - }, - Commands: map[string]bool{ - "sudo curl -kO https://puppet.test.com:8140/packages/current/install.bash": true, - "sudo bash -- ./install.bash --puppet-service-ensure stopped": true, - "sudo rm -f install.bash": true, - }, - }, - "When the curl command fails": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - Commands: map[string]bool{ - "curl -kO https://puppet.test.com:8140/packages/current/install.bash": true, - "bash -- ./install.bash --puppet-service-ensure stopped": false, - "rm -f install.bash": false, - }, - CommandFunc: func(r *remote.Cmd) error { - if r.Command == "curl -kO https://puppet.test.com:8140/packages/current/install.bash" { - r.SetExitStatus(1, &remote.ExitError{ - Command: "curl -kO https://puppet.test.com:8140/packages/current/install.bash", - ExitStatus: 1, - Err: nil, - }) - } else { - r.SetExitStatus(0, nil) - } - return nil - }, - ExpectedError: true, - }, - "When the install script fails": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - Commands: map[string]bool{ - "curl -kO https://puppet.test.com:8140/packages/current/install.bash": true, - "bash -- ./install.bash --puppet-service-ensure stopped": true, - "rm -f install.bash": false, - }, - CommandFunc: func(r *remote.Cmd) error { - if r.Command == "bash -- ./install.bash --puppet-service-ensure stopped" { - r.SetExitStatus(1, &remote.ExitError{ - Command: "bash -- ./install.bash --puppet-service-ensure stopped", - ExitStatus: 1, - Err: nil, - }) - } else { - r.SetExitStatus(0, nil) - } - return nil - }, - ExpectedError: true, - }, - "When the cleanup rm fails": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - Commands: map[string]bool{ - "curl -kO https://puppet.test.com:8140/packages/current/install.bash": true, - "bash -- ./install.bash --puppet-service-ensure stopped": true, - "rm -f install.bash": true, - }, - CommandFunc: func(r *remote.Cmd) error { - if r.Command == "rm -f install.bash" { - r.SetExitStatus(1, &remote.ExitError{ - Command: "rm -f install.bash", - ExitStatus: 1, - Err: nil, - }) - } else { - r.SetExitStatus(0, nil) - } - return nil - }, - ExpectedError: true, - }, - } - - for k, tc := range cases { - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - c := new(communicator.MockCommunicator) - c.Commands = tc.Commands - if tc.CommandFunc != nil { - c.CommandFunc = tc.CommandFunc - } - p.comm = c - p.output = new(terraform.MockUIOutput) - - err = p.linuxInstallPuppetAgent() - if tc.ExpectedError { - if err == nil { - t.Fatalf("Expected error, but no error returned") - } - } else { - if err != nil { - t.Fatalf("Test %q failed: %v", k, err) - } - } - } -} - -func TestResourceProvisioner_linuxRunPuppetAgent(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - Commands map[string]bool - CommandFunc func(*remote.Cmd) error - ExpectedError bool - }{ - "When puppet returns 0": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - Commands: map[string]bool{ - "/opt/puppetlabs/puppet/bin/puppet agent --test --server puppet.test.com --environment production": true, - }, - }, - "When puppet returns 2 (changes applied without error)": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - CommandFunc: func(r *remote.Cmd) error { - r.SetExitStatus(2, &remote.ExitError{ - Command: "/opt/puppetlabs/puppet/bin/puppet agent --test --server puppet.test.com", - ExitStatus: 2, - Err: nil, - }) - return nil - }, - ExpectedError: false, - }, - "When puppet returns something not 0 or 2": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - CommandFunc: func(r *remote.Cmd) error { - r.SetExitStatus(1, &remote.ExitError{ - Command: "/opt/puppetlabs/puppet/bin/puppet agent --test --server puppet.test.com", - ExitStatus: 1, - Err: nil, - }) - return nil - }, - ExpectedError: true, - }, - } - - for k, tc := range cases { - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - c := new(communicator.MockCommunicator) - c.Commands = tc.Commands - if tc.CommandFunc != nil { - c.CommandFunc = tc.CommandFunc - } - p.comm = c - p.output = new(terraform.MockUIOutput) - - err = p.linuxRunPuppetAgent() - if tc.ExpectedError { - if err == nil { - t.Fatalf("Expected error, but no error returned") - } - } else { - if err != nil { - t.Fatalf("Test %q failed: %v", k, err) - } - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/resource_provisioner.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/resource_provisioner.go deleted file mode 100644 index 70a99cdd..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/resource_provisioner.go +++ /dev/null @@ -1,359 +0,0 @@ -package puppet - -import ( - "bytes" - "context" - "fmt" - "io" - "strings" - "sync" - "time" - - "github.com/hashicorp/terraform/builtin/provisioners/puppet/bolt" - "github.com/hashicorp/terraform/communicator" - "github.com/hashicorp/terraform/communicator/remote" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/helper/validation" - "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/go-linereader" - "gopkg.in/yaml.v2" -) - -type provisioner struct { - Server string - ServerUser string - OSType string - Certname string - Environment string - Autosign bool - OpenSource bool - UseSudo bool - BoltTimeout time.Duration - CustomAttributes map[string]interface{} - ExtensionRequests map[string]interface{} - - runPuppetAgent func() error - installPuppetAgent func() error - uploadFile func(f io.Reader, dir string, filename string) error - defaultCertname func() (string, error) - - instanceState *terraform.InstanceState - output terraform.UIOutput - comm communicator.Communicator - - outputWG sync.WaitGroup -} - -type csrAttributes struct { - CustomAttributes map[string]string `yaml:"custom_attributes"` - ExtensionRequests map[string]string `yaml:"extension_requests"` -} - -// Provisioner returns a Puppet resource provisioner. -func Provisioner() terraform.ResourceProvisioner { - return &schema.Provisioner{ - Schema: map[string]*schema.Schema{ - "server": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - "server_user": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: "root", - }, - "os_type": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice([]string{"linux", "windows"}, false), - }, - "use_sudo": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: true, - }, - "autosign": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: true, - }, - "open_source": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: true, - }, - "certname": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "extension_requests": &schema.Schema{ - Type: schema.TypeMap, - Optional: true, - }, - "custom_attributes": &schema.Schema{ - Type: schema.TypeMap, - Optional: true, - }, - "environment": &schema.Schema{ - Type: schema.TypeString, - Default: "production", - Optional: true, - }, - "bolt_timeout": &schema.Schema{ - Type: schema.TypeString, - Default: "5m", - Optional: true, - ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) { - _, err := time.ParseDuration(val.(string)) - if err != nil { - errs = append(errs, err) - } - return warns, errs - }, - }, - }, - ApplyFunc: applyFn, - } -} - -func applyFn(ctx context.Context) error { - output := ctx.Value(schema.ProvOutputKey).(terraform.UIOutput) - state := ctx.Value(schema.ProvRawStateKey).(*terraform.InstanceState) - configData := ctx.Value(schema.ProvConfigDataKey).(*schema.ResourceData) - - p, err := decodeConfig(configData) - if err != nil { - return err - } - - p.instanceState = state - p.output = output - - if p.OSType == "" { - switch connType := state.Ephemeral.ConnInfo["type"]; connType { - case "ssh", "": // The default connection type is ssh, so if the type is empty assume ssh - p.OSType = "linux" - case "winrm": - p.OSType = "windows" - default: - return fmt.Errorf("Unsupported connection type: %s", connType) - } - } - - switch p.OSType { - case "linux": - p.runPuppetAgent = p.linuxRunPuppetAgent - p.installPuppetAgent = p.linuxInstallPuppetAgent - p.uploadFile = p.linuxUploadFile - p.defaultCertname = p.linuxDefaultCertname - case "windows": - p.runPuppetAgent = p.windowsRunPuppetAgent - p.installPuppetAgent = p.windowsInstallPuppetAgent - p.uploadFile = p.windowsUploadFile - p.UseSudo = false - p.defaultCertname = p.windowsDefaultCertname - default: - return fmt.Errorf("Unsupported OS type: %s", p.OSType) - } - - comm, err := communicator.New(state) - if err != nil { - return err - } - - retryCtx, cancel := context.WithTimeout(ctx, comm.Timeout()) - defer cancel() - - err = communicator.Retry(retryCtx, func() error { - return comm.Connect(output) - }) - if err != nil { - return err - } - defer comm.Disconnect() - - p.comm = comm - - if p.OpenSource { - p.installPuppetAgent = p.installPuppetAgentOpenSource - } - - csrAttrs := new(csrAttributes) - csrAttrs.CustomAttributes = make(map[string]string) - for k, v := range p.CustomAttributes { - csrAttrs.CustomAttributes[k] = v.(string) - } - - csrAttrs.ExtensionRequests = make(map[string]string) - for k, v := range p.ExtensionRequests { - csrAttrs.ExtensionRequests[k] = v.(string) - } - - if p.Autosign { - if p.Certname == "" { - p.Certname, _ = p.defaultCertname() - } - - autosignToken, err := p.generateAutosignToken(p.Certname) - if err != nil { - return fmt.Errorf("Failed to generate an autosign token: %s", err) - } - csrAttrs.CustomAttributes["challengePassword"] = autosignToken - } - - if err = p.writeCSRAttributes(csrAttrs); err != nil { - return fmt.Errorf("Failed to write csr_attributes.yaml: %s", err) - } - - if err = p.installPuppetAgent(); err != nil { - return err - } - - if err = p.runPuppetAgent(); err != nil { - return err - } - - return nil -} - -func (p *provisioner) writeCSRAttributes(attrs *csrAttributes) (rerr error) { - content, err := yaml.Marshal(attrs) - if err != nil { - return fmt.Errorf("Failed to marshal CSR attributes to YAML: %s", err) - } - - configDir := map[string]string{ - "linux": "/etc/puppetlabs/puppet", - "windows": "C:\\ProgramData\\PuppetLabs\\Puppet\\etc", - } - - return p.uploadFile(bytes.NewBuffer(content), configDir[p.OSType], "csr_attributes.yaml") -} - -func (p *provisioner) generateAutosignToken(certname string) (string, error) { - task := "autosign::generate_token" - - masterConnInfo := map[string]string{ - "type": "ssh", - "host": p.Server, - "user": p.ServerUser, - } - - result, err := bolt.Task( - masterConnInfo, - p.BoltTimeout, - p.ServerUser != "root", - task, - map[string]string{"certname": certname}, - ) - if err != nil { - return "", err - } - - if result.Items[0].Status != "success" { - return "", fmt.Errorf("Bolt %s failed on %s: %v", - task, - result.Items[0].Node, - result.Items[0].Result["_error"], - ) - } - - return result.Items[0].Result["_output"], nil -} - -func (p *provisioner) installPuppetAgentOpenSource() error { - task := "puppet_agent::install" - - connType := p.instanceState.Ephemeral.ConnInfo["type"] - if connType == "" { - connType = "ssh" - } - - agentConnInfo := map[string]string{ - "type": connType, - "host": p.instanceState.Ephemeral.ConnInfo["host"], - "user": p.instanceState.Ephemeral.ConnInfo["user"], - "password": p.instanceState.Ephemeral.ConnInfo["password"], // Required on Windows only - } - - result, err := bolt.Task( - agentConnInfo, - p.BoltTimeout, - p.UseSudo, - task, - nil, - ) - - if err != nil || result.Items[0].Status != "success" { - return fmt.Errorf("%s failed: %s\n%+v", task, err, result) - } - - return nil -} - -func (p *provisioner) runCommand(command string) (stdout string, err error) { - if p.UseSudo { - command = "sudo " + command - } - - var stdoutBuffer bytes.Buffer - outR, outW := io.Pipe() - errR, errW := io.Pipe() - outTee := io.TeeReader(outR, &stdoutBuffer) - - p.outputWG.Add(2) - go p.copyToOutput(outTee) - go p.copyToOutput(errR) - - defer outW.Close() - defer errW.Close() - - cmd := &remote.Cmd{ - Command: command, - Stdout: outW, - Stderr: errW, - } - - err = p.comm.Start(cmd) - if err != nil { - err = fmt.Errorf("Error executing command %q: %v", cmd.Command, err) - return stdout, err - } - - err = cmd.Wait() - - outW.Close() - errW.Close() - p.outputWG.Wait() - - stdout = strings.TrimSpace(stdoutBuffer.String()) - - return stdout, err -} - -func (p *provisioner) copyToOutput(reader io.Reader) { - defer p.outputWG.Done() - - lr := linereader.New(reader) - for line := range lr.Ch { - p.output.Output(line) - } -} - -func decodeConfig(d *schema.ResourceData) (*provisioner, error) { - p := &provisioner{ - UseSudo: d.Get("use_sudo").(bool), - Server: d.Get("server").(string), - ServerUser: d.Get("server_user").(string), - OSType: strings.ToLower(d.Get("os_type").(string)), - Autosign: d.Get("autosign").(bool), - OpenSource: d.Get("open_source").(bool), - Certname: strings.ToLower(d.Get("certname").(string)), - ExtensionRequests: d.Get("extension_requests").(map[string]interface{}), - CustomAttributes: d.Get("custom_attributes").(map[string]interface{}), - Environment: d.Get("environment").(string), - } - p.BoltTimeout, _ = time.ParseDuration(d.Get("bolt_timeout").(string)) - - return p, nil -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/resource_provisioner_test.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/resource_provisioner_test.go deleted file mode 100644 index 4a5cde03..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/resource_provisioner_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package puppet - -import ( - "testing" - - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" -) - -func TestResourceProvisioner_impl(t *testing.T) { - var _ terraform.ResourceProvisioner = Provisioner() -} - -func TestProvisioner(t *testing.T) { - if err := Provisioner().(*schema.Provisioner).InternalValidate(); err != nil { - t.Fatalf("err: %s", err) - } -} - -func TestProvisioner_Validate_good_server(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "server": "puppet.test.com", - }) - - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) > 0 { - t.Fatalf("Errors: %v", errs) - } -} - -func TestProvisioner_Validate_bad_no_server(t *testing.T) { - c := testConfig(t, map[string]interface{}{}) - - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) == 0 { - t.Fatalf("Should have errors") - } -} - -func TestProvisioner_Validate_bad_os_type(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "server": "puppet.test.com", - "os_type": "OS/2", - }) - - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) == 0 { - t.Fatalf("Should have errors") - } -} - -func TestProvisioner_Validate_good_os_type_linux(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "server": "puppet.test.com", - "os_type": "linux", - }) - - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) > 0 { - t.Fatalf("Errors: %v", errs) - } -} - -func TestProvisioner_Validate_good_os_type_windows(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "server": "puppet.test.com", - "os_type": "windows", - }) - - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) > 0 { - t.Fatalf("Errors: %v", errs) - } -} - -func TestProvisioner_Validate_bad_bolt_timeout(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "server": "puppet.test.com", - "bolt_timeout": "123oeau", - }) - - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) == 0 { - t.Fatalf("Should have errors") - } -} - -func TestProvisioner_Validate_good_bolt_timeout(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "server": "puppet.test.com", - "bolt_timeout": "123m", - }) - - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) > 0 { - t.Fatalf("Errors: %v", warn) - } -} - -func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig { - return terraform.NewResourceConfigRaw(c) -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/windows_provisioner.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/windows_provisioner.go deleted file mode 100644 index eeb2154e..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/windows_provisioner.go +++ /dev/null @@ -1,71 +0,0 @@ -package puppet - -import ( - "fmt" - "io" - "strings" - - "github.com/hashicorp/terraform/communicator/remote" -) - -const ( - getHostByName = "([System.Net.Dns]::GetHostByName(($env:computerName))).Hostname" - domainQuery = "(Get-WmiObject -Query 'select DNSDomain from Win32_NetworkAdapterConfiguration where IPEnabled = True').DNSDomain" -) - -func (p *provisioner) windowsUploadFile(f io.Reader, dir string, filename string) error { - _, err := p.runCommand("powershell.exe new-item -itemtype directory -force -path " + dir) - if err != nil { - return fmt.Errorf("Failed to make directory %s: %s", dir, err) - } - - return p.comm.Upload(dir+"\\"+filename, f) -} - -func (p *provisioner) windowsDefaultCertname() (string, error) { - certname, err := p.runCommand(fmt.Sprintf(`powershell -Command "& {%s}"`, getHostByName)) - if err != nil { - return "", err - } - - // Sometimes System.Net.Dns::GetHostByName does not return a full FQDN, so - // we have to look up the domain separately. - if strings.Contains(certname, ".") { - return certname, nil - } - - domain, err := p.runCommand(fmt.Sprintf(`powershell -Command "& {%s}"`, domainQuery)) - if err != nil { - return "", err - } - - return strings.ToLower(certname + "." + domain), nil -} - -func (p *provisioner) windowsInstallPuppetAgent() error { - _, err := p.runCommand(fmt.Sprintf( - `powershell -Command "& {[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}; `+ - `(New-Object System.Net.WebClient).DownloadFile('https://%s:8140/packages/current/install.ps1', `+ - `'install.ps1')}"`, - p.Server, - )) - if err != nil { - return err - } - - _, err = p.runCommand(`powershell -Command "& .\install.ps1 -PuppetServiceEnsure stopped"`) - - return err -} - -func (p *provisioner) windowsRunPuppetAgent() error { - _, err := p.runCommand(fmt.Sprintf("puppet agent --test --server %s --environment %s", p.Server, p.Environment)) - if err != nil { - errStruct, _ := err.(*remote.ExitError) - if errStruct.ExitStatus == 2 { - return nil - } - } - - return err -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/windows_provisioner_test.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/windows_provisioner_test.go deleted file mode 100644 index 5c823ab5..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/puppet/windows_provisioner_test.go +++ /dev/null @@ -1,393 +0,0 @@ -package puppet - -import ( - "fmt" - "io" - "strings" - "testing" - "time" - - "github.com/hashicorp/terraform/communicator" - "github.com/hashicorp/terraform/communicator/remote" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" -) - -const ( - getHostByNameCmd = `powershell -Command "& {([System.Net.Dns]::GetHostByName(($env:computerName))).Hostname}"` - domainQueryCmd = `powershell -Command "& {(Get-WmiObject -Query 'select DNSDomain from Win32_NetworkAdapterConfiguration where IPEnabled = True').DNSDomain}"` - downloadInstallerCmd = `powershell -Command "& {[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}; (New-Object System.Net.WebClient).DownloadFile('https://puppet.test.com:8140/packages/current/install.ps1', 'install.ps1')}"` - runInstallerCmd = `powershell -Command "& .\install.ps1 -PuppetServiceEnsure stopped"` - runPuppetCmd = "puppet agent --test --server puppet.test.com --environment production" -) - -func TestResourceProvisioner_windowsUploadFile(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - Commands map[string]bool - CommandFunc func(*remote.Cmd) error - ExpectedError bool - Uploads map[string]string - File io.Reader - Dir string - Filename string - }{ - "Successful upload": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - Commands: map[string]bool{ - `powershell.exe new-item -itemtype directory -force -path C:\ProgramData\PuppetLabs\puppet\etc`: true, - }, - Uploads: map[string]string{ - `C:\ProgramData\PuppetLabs\puppet\etc\csr_attributes.yaml`: "", - }, - Dir: `C:\ProgramData\PuppetLabs\puppet\etc`, - Filename: "csr_attributes.yaml", - File: strings.NewReader(""), - }, - "Failure when creating the directory": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - Commands: map[string]bool{ - `powershell.exe new-item -itemtype directory -force -path C:\ProgramData\PuppetLabs\puppet\etc`: true, - }, - Dir: `C:\ProgramData\PuppetLabs\puppet\etc`, - Filename: "csr_attributes.yaml", - File: strings.NewReader(""), - CommandFunc: func(r *remote.Cmd) error { - r.SetExitStatus(1, &remote.ExitError{ - Command: `powershell.exe new-item -itemtype directory -force -path C:\ProgramData\PuppetLabs\puppet\etc`, - ExitStatus: 1, - Err: nil, - }) - return nil - }, - ExpectedError: true, - }, - } - - for k, tc := range cases { - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - c := new(communicator.MockCommunicator) - c.Commands = tc.Commands - c.Uploads = tc.Uploads - if tc.CommandFunc != nil { - c.CommandFunc = tc.CommandFunc - } - p.comm = c - p.output = new(terraform.MockUIOutput) - - err = p.windowsUploadFile(tc.File, tc.Dir, tc.Filename) - if tc.ExpectedError { - if err == nil { - t.Fatalf("Expected error, but no error returned") - } - } else { - if err != nil { - t.Fatalf("Test %q failed: %v", k, err) - } - } - } -} - -func TestResourceProvisioner_windowsDefaultCertname(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - Commands map[string]bool - CommandFunc func(*remote.Cmd) error - ExpectedError bool - }{ - "GetHostByName failure": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - CommandFunc: func(r *remote.Cmd) error { - switch r.Command { - case getHostByNameCmd: - r.SetExitStatus(1, &remote.ExitError{ - Command: getHostByNameCmd, - ExitStatus: 1, - Err: nil, - }) - default: - return fmt.Errorf("Command not found!") - } - - return nil - }, - ExpectedError: true, - }, - "GetHostByName returns FQDN": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - CommandFunc: func(r *remote.Cmd) error { - switch r.Command { - case getHostByNameCmd: - r.Stdout.Write([]byte("example.test.com\n")) - time.Sleep(200 * time.Millisecond) - r.SetExitStatus(0, nil) - default: - return fmt.Errorf("Command not found!") - } - - return nil - }, - }, - "GetHostByName returns hostname, DNSDomain query succeeds": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - CommandFunc: func(r *remote.Cmd) error { - switch r.Command { - case getHostByNameCmd: - r.Stdout.Write([]byte("example\n")) - time.Sleep(200 * time.Millisecond) - r.SetExitStatus(0, nil) - case domainQueryCmd: - r.Stdout.Write([]byte("test.com\n")) - time.Sleep(200 * time.Millisecond) - r.SetExitStatus(0, nil) - default: - return fmt.Errorf("Command not found!") - } - - return nil - }, - }, - "GetHostByName returns hostname, DNSDomain query fails": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - CommandFunc: func(r *remote.Cmd) error { - switch r.Command { - case getHostByNameCmd: - r.Stdout.Write([]byte("example\n")) - time.Sleep(200 * time.Millisecond) - r.SetExitStatus(0, nil) - case domainQueryCmd: - r.SetExitStatus(1, &remote.ExitError{ - Command: domainQueryCmd, - ExitStatus: 1, - Err: nil, - }) - default: - return fmt.Errorf("Command not found!") - } - - return nil - }, - ExpectedError: true, - }, - } - - for k, tc := range cases { - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - c := new(communicator.MockCommunicator) - c.Commands = tc.Commands - if tc.CommandFunc != nil { - c.CommandFunc = tc.CommandFunc - } - p.comm = c - p.output = new(terraform.MockUIOutput) - - _, err = p.windowsDefaultCertname() - if tc.ExpectedError { - if err == nil { - t.Fatalf("Expected error, but no error returned") - } - } else { - if err != nil { - t.Fatalf("Test %q failed: %v", k, err) - } - } - } -} - -func TestResourceProvisioner_windowsInstallPuppetAgent(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - Commands map[string]bool - CommandFunc func(*remote.Cmd) error - ExpectedError bool - }{ - "Everything runs succcessfully": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - Commands: map[string]bool{ - downloadInstallerCmd: true, - runInstallerCmd: true, - }, - }, - "Installer download fails": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": true, - }, - CommandFunc: func(r *remote.Cmd) error { - switch r.Command { - case downloadInstallerCmd: - r.SetExitStatus(1, &remote.ExitError{ - Command: downloadInstallerCmd, - ExitStatus: 1, - Err: nil, - }) - default: - return fmt.Errorf("Command not found!") - } - - return nil - }, - ExpectedError: true, - }, - "Install script fails": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - CommandFunc: func(r *remote.Cmd) error { - switch r.Command { - case downloadInstallerCmd: - r.SetExitStatus(0, nil) - case runInstallerCmd: - r.SetExitStatus(1, &remote.ExitError{ - Command: runInstallerCmd, - ExitStatus: 1, - Err: nil, - }) - default: - return fmt.Errorf("Command not found!") - } - - return nil - }, - ExpectedError: true, - }, - } - - for k, tc := range cases { - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - c := new(communicator.MockCommunicator) - c.Commands = tc.Commands - if tc.CommandFunc != nil { - c.CommandFunc = tc.CommandFunc - } - p.comm = c - p.output = new(terraform.MockUIOutput) - - err = p.windowsInstallPuppetAgent() - if tc.ExpectedError { - if err == nil { - t.Fatalf("Expected error, but no error returned") - } - } else { - if err != nil { - t.Fatalf("Test %q failed: %v", k, err) - } - } - } -} - -func TestResourceProvisioner_windowsRunPuppetAgent(t *testing.T) { - cases := map[string]struct { - Config map[string]interface{} - Commands map[string]bool - CommandFunc func(*remote.Cmd) error - ExpectedError bool - }{ - "When puppet returns 0": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - Commands: map[string]bool{ - runPuppetCmd: true, - }, - }, - "When puppet returns 2 (changes applied without error)": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - CommandFunc: func(r *remote.Cmd) error { - r.SetExitStatus(2, &remote.ExitError{ - Command: runPuppetCmd, - ExitStatus: 2, - Err: nil, - }) - return nil - }, - }, - "When puppet returns something not 0 or 2": { - Config: map[string]interface{}{ - "server": "puppet.test.com", - "use_sudo": false, - }, - CommandFunc: func(r *remote.Cmd) error { - r.SetExitStatus(1, &remote.ExitError{ - Command: runPuppetCmd, - ExitStatus: 1, - Err: nil, - }) - return nil - }, - ExpectedError: true, - }, - } - - for k, tc := range cases { - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), - ) - if err != nil { - t.Fatalf("Error: %v", err) - } - - c := new(communicator.MockCommunicator) - c.Commands = tc.Commands - if tc.CommandFunc != nil { - c.CommandFunc = tc.CommandFunc - } - p.comm = c - p.output = new(terraform.MockUIOutput) - - err = p.windowsRunPuppetAgent() - if tc.ExpectedError { - if err == nil { - t.Fatalf("Expected error, but no error returned") - } - } else { - if err != nil { - t.Fatalf("Test %q failed: %v", k, err) - } - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/remote-exec/resource_provisioner.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/remote-exec/resource_provisioner.go index 50042977..1abc562e 100644 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/remote-exec/resource_provisioner.go +++ b/vendor/github.com/hashicorp/terraform/builtin/provisioners/remote-exec/resource_provisioner.go @@ -3,92 +3,140 @@ package remoteexec import ( "bytes" "context" + "errors" "fmt" "io" "io/ioutil" "log" "os" "strings" - "time" "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/configs/configschema" + "github.com/hashicorp/terraform/provisioners" "github.com/mitchellh/go-linereader" + "github.com/zclconf/go-cty/cty" ) -// maxBackoffDealy is the maximum delay between retry attempts -var maxBackoffDelay = 10 * time.Second -var initialBackoffDelay = time.Second +func New() provisioners.Interface { + ctx, cancel := context.WithCancel(context.Background()) + return &provisioner{ + ctx: ctx, + cancel: cancel, + } +} + +type provisioner struct { + // We store a context here tied to the lifetime of the provisioner. + // This allows the Stop method to cancel any in-flight requests. + ctx context.Context + cancel context.CancelFunc +} -func Provisioner() terraform.ResourceProvisioner { - return &schema.Provisioner{ - Schema: map[string]*schema.Schema{ +func (p *provisioner) GetSchema() (resp provisioners.GetSchemaResponse) { + schema := &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ "inline": { - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeString}, - PromoteSingle: true, - Optional: true, - ConflictsWith: []string{"script", "scripts"}, + Type: cty.List(cty.String), + Optional: true, }, - "script": { - Type: schema.TypeString, - Optional: true, - ConflictsWith: []string{"inline", "scripts"}, + Type: cty.String, + Optional: true, }, - "scripts": { - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - ConflictsWith: []string{"script", "inline"}, + Type: cty.List(cty.String), + Optional: true, }, }, + } - ApplyFunc: applyFn, + resp.Provisioner = schema + return resp +} + +func (p *provisioner) ValidateProvisionerConfig(req provisioners.ValidateProvisionerConfigRequest) (resp provisioners.ValidateProvisionerConfigResponse) { + cfg, err := p.GetSchema().Provisioner.CoerceValue(req.Config) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + + inline := cfg.GetAttr("inline") + script := cfg.GetAttr("script") + scripts := cfg.GetAttr("scripts") + + set := 0 + if !inline.IsNull() { + set++ + } + if !script.IsNull() { + set++ + } + if !scripts.IsNull() { + set++ } + if set != 1 { + resp.Diagnostics = resp.Diagnostics.Append(errors.New( + `only one of "inline", "script", or "scripts" must be set`)) + } + return resp } -// Apply executes the remote exec provisioner -func applyFn(ctx context.Context) error { - connState := ctx.Value(schema.ProvRawStateKey).(*terraform.InstanceState) - data := ctx.Value(schema.ProvConfigDataKey).(*schema.ResourceData) - o := ctx.Value(schema.ProvOutputKey).(terraform.UIOutput) +func (p *provisioner) ProvisionResource(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { + if req.Connection.IsNull() { + resp.Diagnostics = resp.Diagnostics.Append(errors.New("missing connection configuration for provisioner")) + return resp + } - // Get a new communicator - comm, err := communicator.New(connState) + comm, err := communicator.New(req.Connection) if err != nil { - return err + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp } // Collect the scripts - scripts, err := collectScripts(data) + scripts, err := collectScripts(req.Config) if err != nil { - return err + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp } for _, s := range scripts { defer s.Close() } // Copy and execute each script - if err := runScripts(ctx, o, comm, scripts); err != nil { - return err + if err := runScripts(p.ctx, req.UIOutput, comm, scripts); err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp } + return resp +} + +func (p *provisioner) Stop() error { + p.cancel() + return nil +} + +func (p *provisioner) Close() error { return nil } // generateScripts takes the configuration and creates a script from each inline config -func generateScripts(d *schema.ResourceData) ([]string, error) { +func generateScripts(inline cty.Value) ([]string, error) { var lines []string - for _, l := range d.Get("inline").([]interface{}) { - line, ok := l.(string) - if !ok { - return nil, fmt.Errorf("Error parsing %v as a string", l) + for _, l := range inline.AsValueSlice() { + if l.IsNull() { + return nil, errors.New("invalid null string in 'scripts'") } - lines = append(lines, line) + + s := l.AsString() + if s == "" { + return nil, errors.New("invalid empty string in 'scripts'") + } + lines = append(lines, s) } lines = append(lines, "") @@ -97,10 +145,10 @@ func generateScripts(d *schema.ResourceData) ([]string, error) { // collectScripts is used to collect all the scripts we need // to execute in preparation for copying them. -func collectScripts(d *schema.ResourceData) ([]io.ReadCloser, error) { +func collectScripts(v cty.Value) ([]io.ReadCloser, error) { // Check if inline - if _, ok := d.GetOk("inline"); ok { - scripts, err := generateScripts(d) + if inline := v.GetAttr("inline"); !inline.IsNull() { + scripts, err := generateScripts(inline) if err != nil { return nil, err } @@ -115,21 +163,24 @@ func collectScripts(d *schema.ResourceData) ([]io.ReadCloser, error) { // Collect scripts var scripts []string - if script, ok := d.GetOk("script"); ok { - scr, ok := script.(string) - if !ok { - return nil, fmt.Errorf("Error parsing script %v as string", script) + if script := v.GetAttr("script"); !script.IsNull() { + s := script.AsString() + if s == "" { + return nil, errors.New("invalid empty string in 'script'") } - scripts = append(scripts, scr) + scripts = append(scripts, s) } - if scriptList, ok := d.GetOk("scripts"); ok { - for _, script := range scriptList.([]interface{}) { - scr, ok := script.(string) - if !ok { - return nil, fmt.Errorf("Error parsing script %v as string", script) + if scriptList := v.GetAttr("scripts"); !scriptList.IsNull() { + for _, script := range scriptList.AsValueSlice() { + if script.IsNull() { + return nil, errors.New("invalid null string in 'script'") + } + s := script.AsString() + if s == "" { + return nil, errors.New("invalid empty string in 'script'") } - scripts = append(scripts, scr) + scripts = append(scripts, s) } } @@ -151,12 +202,7 @@ func collectScripts(d *schema.ResourceData) ([]io.ReadCloser, error) { } // runScripts is used to copy and execute a set of scripts -func runScripts( - ctx context.Context, - o terraform.UIOutput, - comm communicator.Communicator, - scripts []io.ReadCloser) error { - +func runScripts(ctx context.Context, o provisioners.UIOutput, comm communicator.Communicator, scripts []io.ReadCloser) error { retryCtx, cancel := context.WithTimeout(ctx, comm.Timeout()) defer cancel() @@ -182,8 +228,8 @@ func runScripts( defer outW.Close() defer errW.Close() - go copyOutput(o, outR) - go copyOutput(o, errR) + go copyUIOutput(o, outR) + go copyUIOutput(o, errR) remotePath := comm.ScriptPath() @@ -216,8 +262,7 @@ func runScripts( return nil } -func copyOutput( - o terraform.UIOutput, r io.Reader) { +func copyUIOutput(o provisioners.UIOutput, r io.Reader) { lr := linereader.New(r) for line := range lr.Ch { o.Output(line) diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/remote-exec/resource_provisioner_test.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/remote-exec/resource_provisioner_test.go index cb865a8e..eb9da611 100644 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/remote-exec/resource_provisioner_test.go +++ b/vendor/github.com/hashicorp/terraform/builtin/provisioners/remote-exec/resource_provisioner_test.go @@ -3,7 +3,9 @@ package remoteexec import ( "bytes" "context" + "fmt" "io" + "log" "testing" "time" @@ -11,44 +13,33 @@ 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/provisioners" + "github.com/mitchellh/cli" + "github.com/zclconf/go-cty/cty" ) -func TestResourceProvisioner_impl(t *testing.T) { - var _ terraform.ResourceProvisioner = Provisioner() -} - -func TestProvisioner(t *testing.T) { - if err := Provisioner().(*schema.Provisioner).InternalValidate(); err != nil { - t.Fatalf("err: %s", err) - } -} - func TestResourceProvider_Validate_good(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "inline": "echo foo", + c := cty.ObjectVal(map[string]cty.Value{ + "inline": cty.ListVal([]cty.Value{cty.StringVal("echo foo")}), }) - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) > 0 { - t.Fatalf("Errors: %v", errs) + resp := New().ValidateProvisionerConfig(provisioners.ValidateProvisionerConfigRequest{ + Config: c, + }) + if len(resp.Diagnostics) > 0 { + t.Fatal(resp.Diagnostics.ErrWithWarnings()) } } func TestResourceProvider_Validate_bad(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "invalid": "nope", + c := cty.ObjectVal(map[string]cty.Value{ + "invalid": cty.StringVal("nope"), }) - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) == 0 { + resp := New().ValidateProvisionerConfig(provisioners.ValidateProvisionerConfigRequest{ + Config: c, + }) + if !resp.Diagnostics.HasErrors() { t.Fatalf("Should have errors") } } @@ -59,17 +50,13 @@ exit 0 ` func TestResourceProvider_generateScript(t *testing.T) { - conf := map[string]interface{}{ - "inline": []interface{}{ - "cd /tmp", - "wget http://foobar", - "exit 0", - }, - } + inline := cty.ListVal([]cty.Value{ + cty.StringVal("cd /tmp"), + cty.StringVal("wget http://foobar"), + cty.StringVal("exit 0"), + }) - out, err := generateScripts( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, conf), - ) + out, err := generateScripts(inline) if err != nil { t.Fatalf("err: %v", err) } @@ -84,34 +71,28 @@ func TestResourceProvider_generateScript(t *testing.T) { } func TestResourceProvider_generateScriptEmptyInline(t *testing.T) { - p := Provisioner().(*schema.Provisioner) - conf := map[string]interface{}{ - "inline": []interface{}{""}, - } + inline := cty.ListVal([]cty.Value{cty.StringVal("")}) - _, err := generateScripts(schema.TestResourceDataRaw( - t, p.Schema, conf)) + _, err := generateScripts(inline) if err == nil { t.Fatal("expected error, got none") } - if !strings.Contains(err.Error(), "Error parsing") { - t.Fatalf("expected parsing error, got: %s", err) + if !strings.Contains(err.Error(), "empty string") { + t.Fatalf("expected empty string error, got: %s", err) } } func TestResourceProvider_CollectScripts_inline(t *testing.T) { - conf := map[string]interface{}{ - "inline": []interface{}{ - "cd /tmp", - "wget http://foobar", - "exit 0", - }, + conf := map[string]cty.Value{ + "inline": cty.ListVal([]cty.Value{ + cty.StringVal("cd /tmp"), + cty.StringVal("wget http://foobar"), + cty.StringVal("exit 0"), + }), } - scripts, err := collectScripts( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, conf), - ) + scripts, err := collectScripts(cty.ObjectVal(conf)) if err != nil { t.Fatalf("err: %v", err) } @@ -132,13 +113,19 @@ func TestResourceProvider_CollectScripts_inline(t *testing.T) { } func TestResourceProvider_CollectScripts_script(t *testing.T) { - conf := map[string]interface{}{ - "script": "testdata/script1.sh", + p := New() + schema := p.GetSchema().Provisioner + + conf, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ + "scripts": cty.ListVal([]cty.Value{ + cty.StringVal("testdata/script1.sh"), + }), + })) + if err != nil { + t.Fatal(err) } - scripts, err := collectScripts( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, conf), - ) + scripts, err := collectScripts(conf) if err != nil { t.Fatalf("err: %v", err) } @@ -159,17 +146,21 @@ func TestResourceProvider_CollectScripts_script(t *testing.T) { } func TestResourceProvider_CollectScripts_scripts(t *testing.T) { - conf := map[string]interface{}{ - "scripts": []interface{}{ - "testdata/script1.sh", - "testdata/script1.sh", - "testdata/script1.sh", - }, + p := New() + schema := p.GetSchema().Provisioner + + conf, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ + "scripts": cty.ListVal([]cty.Value{ + cty.StringVal("testdata/script1.sh"), + cty.StringVal("testdata/script1.sh"), + cty.StringVal("testdata/script1.sh"), + }), + })) + if err != nil { + log.Fatal(err) } - scripts, err := collectScripts( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, conf), - ) + scripts, err := collectScripts(conf) if err != nil { t.Fatalf("err: %v", err) } @@ -192,25 +183,28 @@ func TestResourceProvider_CollectScripts_scripts(t *testing.T) { } func TestResourceProvider_CollectScripts_scriptsEmpty(t *testing.T) { - p := Provisioner().(*schema.Provisioner) - conf := map[string]interface{}{ - "scripts": []interface{}{""}, - } + p := New() + schema := p.GetSchema().Provisioner - _, err := collectScripts(schema.TestResourceDataRaw( - t, p.Schema, conf)) + conf, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ + "scripts": cty.ListVal([]cty.Value{cty.StringVal("")}), + })) + if err != nil { + t.Fatal(err) + } + _, err = collectScripts(conf) if err == nil { t.Fatal("expected error") } - if !strings.Contains(err.Error(), "Error parsing") { - t.Fatalf("Expected parsing error, got: %s", err) + if !strings.Contains(err.Error(), "empty string") { + t.Fatalf("Expected empty string error, got: %s", err) } } func TestProvisionerTimeout(t *testing.T) { - o := new(terraform.MockUIOutput) + o := cli.NewMockUi() c := new(communicator.MockCommunicator) disconnected := make(chan struct{}) @@ -231,13 +225,11 @@ func TestProvisionerTimeout(t *testing.T) { c.UploadScripts = map[string]string{"hello": "echo hello"} c.RemoteScriptPath = "hello" - p := Provisioner().(*schema.Provisioner) - conf := map[string]interface{}{ - "inline": []interface{}{"echo hello"}, + conf := map[string]cty.Value{ + "inline": cty.ListVal([]cty.Value{cty.StringVal("echo hello")}), } - scripts, err := collectScripts(schema.TestResourceDataRaw( - t, p.Schema, conf)) + scripts, err := collectScripts(cty.ObjectVal(conf)) if err != nil { t.Fatal(err) } @@ -246,11 +238,10 @@ func TestProvisionerTimeout(t *testing.T) { done := make(chan struct{}) + var runErr error go func() { defer close(done) - if err := runScripts(ctx, o, c, scripts); err != nil { - t.Fatal(err) - } + runErr = runScripts(ctx, o, c, scripts) }() select { @@ -260,8 +251,70 @@ func TestProvisionerTimeout(t *testing.T) { } <-done + if runErr != nil { + t.Fatal(err) + } +} + +// Validate that Stop can Close can be called even when not provisioning. +func TestResourceProvisioner_StopClose(t *testing.T) { + p := New() + p.Stop() + p.Close() } -func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig { - return terraform.NewResourceConfigRaw(c) +func TestResourceProvisioner_connectionRequired(t *testing.T) { + p := New() + resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{}) + if !resp.Diagnostics.HasErrors() { + t.Fatal("expected error") + } + + got := resp.Diagnostics.Err().Error() + if !strings.Contains(got, "missing connection") { + t.Fatalf("expected 'missing connection' error: got %q", got) + } +} + +func TestResourceProvisioner_nullsInOptionals(t *testing.T) { + output := cli.NewMockUi() + p := New() + schema := p.GetSchema().Provisioner + + for i, cfg := range []cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "script": cty.StringVal("echo"), + "inline": cty.NullVal(cty.List(cty.String)), + }), + cty.ObjectVal(map[string]cty.Value{ + "inline": cty.ListVal([]cty.Value{ + cty.NullVal(cty.String), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "script": cty.NullVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "scripts": cty.NullVal(cty.List(cty.String)), + }), + cty.ObjectVal(map[string]cty.Value{ + "scripts": cty.ListVal([]cty.Value{ + cty.NullVal(cty.String), + }), + }), + } { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + + cfg, err := schema.CoerceValue(cfg) + if err != nil { + t.Fatal(err) + } + + // verifying there are no panics + p.ProvisionResource(provisioners.ProvisionResourceRequest{ + Config: cfg, + UIOutput: output, + }) + }) + } } diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/salt-masterless/resource_provisioner.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/salt-masterless/resource_provisioner.go deleted file mode 100644 index 0be7ed00..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/salt-masterless/resource_provisioner.go +++ /dev/null @@ -1,525 +0,0 @@ -// This package implements a provisioner for Terraform that executes a -// saltstack state within the remote machine -// -// Adapted from gitub.com/hashicorp/packer/provisioner/salt-masterless - -package saltmasterless - -import ( - "bytes" - "context" - "errors" - "fmt" - "io" - "os" - "path/filepath" - - "github.com/hashicorp/terraform/communicator" - "github.com/hashicorp/terraform/communicator/remote" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" - linereader "github.com/mitchellh/go-linereader" -) - -type provisionFn func(terraform.UIOutput, communicator.Communicator) error - -type provisioner struct { - SkipBootstrap bool - BootstrapArgs string - LocalStateTree string - DisableSudo bool - CustomState string - MinionConfig string - LocalPillarRoots string - RemoteStateTree string - RemotePillarRoots string - TempConfigDir string - NoExitOnFailure bool - LogLevel string - SaltCallArgs string - CmdArgs string -} - -const DefaultStateTreeDir = "/srv/salt" -const DefaultPillarRootDir = "/srv/pillar" - -// Provisioner returns a salt-masterless provisioner -func Provisioner() terraform.ResourceProvisioner { - return &schema.Provisioner{ - Schema: map[string]*schema.Schema{ - "local_state_tree": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - "local_pillar_roots": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "remote_state_tree": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: DefaultStateTreeDir, - }, - "remote_pillar_roots": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: DefaultPillarRootDir, - }, - "temp_config_dir": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: "/tmp/salt", - }, - "skip_bootstrap": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - }, - "no_exit_on_failure": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - }, - "bootstrap_args": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "disable_sudo": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - }, - "custom_state": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "minion_config_file": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "cmd_args": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "salt_call_args": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - "log_level": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - }, - - ApplyFunc: applyFn, - ValidateFunc: validateFn, - } -} - -// Apply executes the file provisioner -func applyFn(ctx context.Context) error { - // Decode the raw config for this provisioner - o := ctx.Value(schema.ProvOutputKey).(terraform.UIOutput) - d := ctx.Value(schema.ProvConfigDataKey).(*schema.ResourceData) - connState := ctx.Value(schema.ProvRawStateKey).(*terraform.InstanceState) - - p, err := decodeConfig(d) - if err != nil { - return err - } - - // Get a new communicator - comm, err := communicator.New(connState) - if err != nil { - return err - } - - retryCtx, cancel := context.WithTimeout(ctx, comm.Timeout()) - defer cancel() - - // Wait and retry until we establish the connection - err = communicator.Retry(retryCtx, func() error { - return comm.Connect(o) - }) - - if err != nil { - return err - } - - // Wait for the context to end and then disconnect - go func() { - <-ctx.Done() - comm.Disconnect() - }() - - var src, dst string - - o.Output("Provisioning with Salt...") - if !p.SkipBootstrap { - cmd := &remote.Cmd{ - // Fallback on wget if curl failed for any reason (such as not being installed) - Command: fmt.Sprintf("curl -L https://bootstrap.saltstack.com -o /tmp/install_salt.sh || wget -O /tmp/install_salt.sh https://bootstrap.saltstack.com"), - } - o.Output(fmt.Sprintf("Downloading saltstack bootstrap to /tmp/install_salt.sh")) - if err = comm.Start(cmd); err != nil { - err = fmt.Errorf("Unable to download Salt: %s", err) - } - - if err := cmd.Wait(); err != nil { - return err - } - - outR, outW := io.Pipe() - errR, errW := io.Pipe() - go copyOutput(o, outR) - go copyOutput(o, errR) - defer outW.Close() - defer errW.Close() - - cmd = &remote.Cmd{ - Command: fmt.Sprintf("%s /tmp/install_salt.sh %s", p.sudo("sh"), p.BootstrapArgs), - Stdout: outW, - Stderr: errW, - } - - o.Output(fmt.Sprintf("Installing Salt with command %s", cmd.Command)) - if err := comm.Start(cmd); err != nil { - return fmt.Errorf("Unable to install Salt: %s", err) - } - - if err := cmd.Wait(); err != nil { - return err - } - } - - o.Output(fmt.Sprintf("Creating remote temporary directory: %s", p.TempConfigDir)) - if err := p.createDir(o, comm, p.TempConfigDir); err != nil { - return fmt.Errorf("Error creating remote temporary directory: %s", err) - } - - if p.MinionConfig != "" { - o.Output(fmt.Sprintf("Uploading minion config: %s", p.MinionConfig)) - src = p.MinionConfig - dst = filepath.ToSlash(filepath.Join(p.TempConfigDir, "minion")) - if err = p.uploadFile(o, comm, dst, src); err != nil { - return fmt.Errorf("Error uploading local minion config file to remote: %s", err) - } - - // move minion config into /etc/salt - o.Output(fmt.Sprintf("Make sure directory %s exists", "/etc/salt")) - if err := p.createDir(o, comm, "/etc/salt"); err != nil { - return fmt.Errorf("Error creating remote salt configuration directory: %s", err) - } - src = filepath.ToSlash(filepath.Join(p.TempConfigDir, "minion")) - dst = "/etc/salt/minion" - if err = p.moveFile(o, comm, dst, src); err != nil { - return fmt.Errorf("Unable to move %s/minion to /etc/salt/minion: %s", p.TempConfigDir, err) - } - } - - o.Output(fmt.Sprintf("Uploading local state tree: %s", p.LocalStateTree)) - src = p.LocalStateTree - dst = filepath.ToSlash(filepath.Join(p.TempConfigDir, "states")) - if err = p.uploadDir(o, comm, dst, src, []string{".git"}); err != nil { - return fmt.Errorf("Error uploading local state tree to remote: %s", err) - } - - // move state tree from temporary directory - src = filepath.ToSlash(filepath.Join(p.TempConfigDir, "states")) - dst = p.RemoteStateTree - if err = p.removeDir(o, comm, dst); err != nil { - return fmt.Errorf("Unable to clear salt tree: %s", err) - } - if err = p.moveFile(o, comm, dst, src); err != nil { - return fmt.Errorf("Unable to move %s/states to %s: %s", p.TempConfigDir, dst, err) - } - - if p.LocalPillarRoots != "" { - o.Output(fmt.Sprintf("Uploading local pillar roots: %s", p.LocalPillarRoots)) - src = p.LocalPillarRoots - dst = filepath.ToSlash(filepath.Join(p.TempConfigDir, "pillar")) - if err = p.uploadDir(o, comm, dst, src, []string{".git"}); err != nil { - return fmt.Errorf("Error uploading local pillar roots to remote: %s", err) - } - - // move pillar root from temporary directory - src = filepath.ToSlash(filepath.Join(p.TempConfigDir, "pillar")) - dst = p.RemotePillarRoots - - if err = p.removeDir(o, comm, dst); err != nil { - return fmt.Errorf("Unable to clear pillar root: %s", err) - } - if err = p.moveFile(o, comm, dst, src); err != nil { - return fmt.Errorf("Unable to move %s/pillar to %s: %s", p.TempConfigDir, dst, err) - } - } - - outR, outW := io.Pipe() - errR, errW := io.Pipe() - go copyOutput(o, outR) - go copyOutput(o, errR) - defer outW.Close() - defer errW.Close() - - o.Output(fmt.Sprintf("Running: salt-call --local %s", p.CmdArgs)) - cmd := &remote.Cmd{ - Command: p.sudo(fmt.Sprintf("salt-call --local %s", p.CmdArgs)), - Stdout: outW, - Stderr: errW, - } - if err = comm.Start(cmd); err != nil { - err = fmt.Errorf("Error executing salt-call: %s", err) - } - - if err := cmd.Wait(); err != nil { - return err - } - return nil -} - -// Prepends sudo to supplied command if config says to -func (p *provisioner) sudo(cmd string) string { - if p.DisableSudo { - return cmd - } - - return "sudo " + cmd -} - -func validateDirConfig(path string, name string, required bool) error { - if required == true && path == "" { - return fmt.Errorf("%s cannot be empty", name) - } else if required == false && path == "" { - return nil - } - info, err := os.Stat(path) - if err != nil { - return fmt.Errorf("%s: path '%s' is invalid: %s", name, path, err) - } else if !info.IsDir() { - return fmt.Errorf("%s: path '%s' must point to a directory", name, path) - } - return nil -} - -func validateFileConfig(path string, name string, required bool) error { - if required == true && path == "" { - return fmt.Errorf("%s cannot be empty", name) - } else if required == false && path == "" { - return nil - } - info, err := os.Stat(path) - if err != nil { - return fmt.Errorf("%s: path '%s' is invalid: %s", name, path, err) - } else if info.IsDir() { - return fmt.Errorf("%s: path '%s' must point to a file", name, path) - } - return nil -} - -func (p *provisioner) uploadFile(o terraform.UIOutput, comm communicator.Communicator, dst, src string) error { - f, err := os.Open(src) - if err != nil { - return fmt.Errorf("Error opening: %s", err) - } - defer f.Close() - - if err = comm.Upload(dst, f); err != nil { - return fmt.Errorf("Error uploading %s: %s", src, err) - } - return nil -} - -func (p *provisioner) moveFile(o terraform.UIOutput, comm communicator.Communicator, dst, src string) error { - o.Output(fmt.Sprintf("Moving %s to %s", src, dst)) - cmd := &remote.Cmd{Command: fmt.Sprintf(p.sudo("mv %s %s"), src, dst)} - if err := comm.Start(cmd); err != nil { - return fmt.Errorf("Unable to move %s to %s: %s", src, dst, err) - } - if err := cmd.Wait(); err != nil { - return err - } - return nil -} - -func (p *provisioner) createDir(o terraform.UIOutput, comm communicator.Communicator, dir string) error { - o.Output(fmt.Sprintf("Creating directory: %s", dir)) - cmd := &remote.Cmd{ - Command: fmt.Sprintf("mkdir -p '%s'", dir), - } - if err := comm.Start(cmd); err != nil { - return err - } - - if err := cmd.Wait(); err != nil { - return err - } - return nil -} - -func (p *provisioner) removeDir(o terraform.UIOutput, comm communicator.Communicator, dir string) error { - o.Output(fmt.Sprintf("Removing directory: %s", dir)) - cmd := &remote.Cmd{ - Command: fmt.Sprintf("rm -rf '%s'", dir), - } - if err := comm.Start(cmd); err != nil { - return err - } - if err := cmd.Wait(); err != nil { - return err - } - return nil -} - -func (p *provisioner) uploadDir(o terraform.UIOutput, comm communicator.Communicator, dst, src string, ignore []string) error { - if err := p.createDir(o, comm, dst); err != nil { - return err - } - - // Make sure there is a trailing "/" so that the directory isn't - // created on the other side. - if src[len(src)-1] != '/' { - src = src + "/" - } - return comm.UploadDir(dst, src) -} - -// Validate checks if the required arguments are configured -func validateFn(c *terraform.ResourceConfig) (ws []string, es []error) { - // require a salt state tree - localStateTreeTmp, ok := c.Get("local_state_tree") - var localStateTree string - if !ok { - es = append(es, - errors.New("Required local_state_tree is not set")) - } else { - localStateTree = localStateTreeTmp.(string) - } - err := validateDirConfig(localStateTree, "local_state_tree", true) - if err != nil { - es = append(es, err) - } - - var localPillarRoots string - localPillarRootsTmp, ok := c.Get("local_pillar_roots") - if !ok { - localPillarRoots = "" - } else { - localPillarRoots = localPillarRootsTmp.(string) - } - - err = validateDirConfig(localPillarRoots, "local_pillar_roots", false) - if err != nil { - es = append(es, err) - } - - var minionConfig string - minionConfigTmp, ok := c.Get("minion_config_file") - if !ok { - minionConfig = "" - } else { - minionConfig = minionConfigTmp.(string) - } - err = validateFileConfig(minionConfig, "minion_config_file", false) - if err != nil { - es = append(es, err) - } - - var remoteStateTree string - remoteStateTreeTmp, ok := c.Get("remote_state_tree") - if !ok { - remoteStateTree = DefaultStateTreeDir - } else { - remoteStateTree = remoteStateTreeTmp.(string) - } - - var remotePillarRoots string - remotePillarRootsTmp, ok := c.Get("remote_pillar_roots") - if !ok { - remotePillarRoots = DefaultPillarRootDir - } else { - remotePillarRoots = remotePillarRootsTmp.(string) - } - - if minionConfig != "" && (remoteStateTree != DefaultStateTreeDir || remotePillarRoots != DefaultPillarRootDir) { - es = append(es, - errors.New("remote_state_tree and remote_pillar_roots only apply when minion_config_file is not used")) - } - - if len(es) > 0 { - return ws, es - } - - return ws, es -} - -func decodeConfig(d *schema.ResourceData) (*provisioner, error) { - p := &provisioner{ - LocalStateTree: d.Get("local_state_tree").(string), - LogLevel: d.Get("log_level").(string), - SaltCallArgs: d.Get("salt_call_args").(string), - CmdArgs: d.Get("cmd_args").(string), - MinionConfig: d.Get("minion_config_file").(string), - CustomState: d.Get("custom_state").(string), - DisableSudo: d.Get("disable_sudo").(bool), - BootstrapArgs: d.Get("bootstrap_args").(string), - NoExitOnFailure: d.Get("no_exit_on_failure").(bool), - SkipBootstrap: d.Get("skip_bootstrap").(bool), - TempConfigDir: d.Get("temp_config_dir").(string), - RemotePillarRoots: d.Get("remote_pillar_roots").(string), - RemoteStateTree: d.Get("remote_state_tree").(string), - LocalPillarRoots: d.Get("local_pillar_roots").(string), - } - - // build the command line args to pass onto salt - var cmdArgs bytes.Buffer - - if p.CustomState == "" { - cmdArgs.WriteString(" state.highstate") - } else { - cmdArgs.WriteString(" state.sls ") - cmdArgs.WriteString(p.CustomState) - } - - if p.MinionConfig == "" { - // pass --file-root and --pillar-root if no minion_config_file is supplied - if p.RemoteStateTree != "" { - cmdArgs.WriteString(" --file-root=") - cmdArgs.WriteString(p.RemoteStateTree) - } else { - cmdArgs.WriteString(" --file-root=") - cmdArgs.WriteString(DefaultStateTreeDir) - } - if p.RemotePillarRoots != "" { - cmdArgs.WriteString(" --pillar-root=") - cmdArgs.WriteString(p.RemotePillarRoots) - } else { - cmdArgs.WriteString(" --pillar-root=") - cmdArgs.WriteString(DefaultPillarRootDir) - } - } - - if !p.NoExitOnFailure { - cmdArgs.WriteString(" --retcode-passthrough") - } - - if p.LogLevel == "" { - cmdArgs.WriteString(" -l info") - } else { - cmdArgs.WriteString(" -l ") - cmdArgs.WriteString(p.LogLevel) - } - - if p.SaltCallArgs != "" { - cmdArgs.WriteString(" ") - cmdArgs.WriteString(p.SaltCallArgs) - } - - p.CmdArgs = cmdArgs.String() - - return p, nil -} - -func copyOutput( - o terraform.UIOutput, r io.Reader) { - lr := linereader.New(r) - for line := range lr.Ch { - o.Output(line) - } -} diff --git a/vendor/github.com/hashicorp/terraform/builtin/provisioners/salt-masterless/resource_provisioner_test.go b/vendor/github.com/hashicorp/terraform/builtin/provisioners/salt-masterless/resource_provisioner_test.go deleted file mode 100644 index 10d11e56..00000000 --- a/vendor/github.com/hashicorp/terraform/builtin/provisioners/salt-masterless/resource_provisioner_test.go +++ /dev/null @@ -1,452 +0,0 @@ -package saltmasterless - -import ( - "io/ioutil" - "os" - "strings" - "testing" - - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/terraform" -) - -func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig { - return terraform.NewResourceConfigRaw(c) -} - -func TestResourceProvisioner_impl(t *testing.T) { - var _ terraform.ResourceProvisioner = Provisioner() -} - -func TestProvisioner(t *testing.T) { - if err := Provisioner().(*schema.Provisioner).InternalValidate(); err != nil { - t.Fatalf("err: %s", err) - } -} - -func TestResourceProvisioner_Validate_good(t *testing.T) { - dir, err := ioutil.TempDir("", "_terraform_saltmasterless_test") - if err != nil { - t.Fatalf("Error when creating temp dir: %v", err) - } - - defer os.RemoveAll(dir) // clean up - - c := testConfig(t, map[string]interface{}{ - "local_state_tree": dir, - }) - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) > 0 { - t.Fatalf("Errors: %v", errs) - } -} - -func TestResourceProvider_Validate_missing_required(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "remote_state_tree": "_default", - }) - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) == 0 { - t.Fatalf("Should have errors") - } -} - -func TestResourceProvider_Validate_LocalStateTree_doesnt_exist(t *testing.T) { - c := testConfig(t, map[string]interface{}{ - "local_state_tree": "/i/dont/exist", - }) - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) == 0 { - t.Fatalf("Should have errors") - } -} - -func TestResourceProvisioner_Validate_invalid(t *testing.T) { - dir, err := ioutil.TempDir("", "_terraform_saltmasterless_test") - if err != nil { - t.Fatalf("Error when creating temp dir: %v", err) - } - - defer os.RemoveAll(dir) // clean up - - c := testConfig(t, map[string]interface{}{ - "local_state_tree": dir, - "i_am_not_valid": "_invalid", - }) - - warn, errs := Provisioner().Validate(c) - if len(warn) > 0 { - t.Fatalf("Warnings: %v", warn) - } - if len(errs) == 0 { - t.Fatalf("Should have errors") - } -} - -func TestProvisionerPrepare_CustomState(t *testing.T) { - c := map[string]interface{}{ - "local_state_tree": "/tmp/local_state_tree", - } - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, c), - ) - - if err != nil { - t.Fatalf("Error: %v", err) - } - - if !strings.Contains(p.CmdArgs, "state.highstate") { - t.Fatal("CmdArgs should contain state.highstate") - } - - if err != nil { - t.Fatalf("err: %s", err) - } - - c = map[string]interface{}{ - "local_state_tree": "/tmp/local_state_tree", - "custom_state": "custom", - } - - p, err = decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, c), - ) - - if err != nil { - t.Fatalf("Error: %v", err) - } - - if !strings.Contains(p.CmdArgs, "state.sls custom") { - t.Fatal("CmdArgs should contain state.sls custom") - } - - if err != nil { - t.Fatalf("err: %s", err) - } -} - -func TestProvisionerPrepare_MinionConfig(t *testing.T) { - dir, err := ioutil.TempDir("", "_terraform_saltmasterless_test") - if err != nil { - t.Fatalf("Error when creating temp dir: %v", err) - } - - defer os.RemoveAll(dir) // clean up - - c := testConfig(t, map[string]interface{}{ - "local_state_tree": dir, - "minion_config_file": "i/dont/exist", - }) - - warns, errs := Provisioner().Validate(c) - - if len(warns) > 0 { - t.Fatalf("Warnings: %v", warns) - } - if len(errs) == 0 { - t.Fatalf("Should have error") - } - - tf, err := ioutil.TempFile("", "minion") - if err != nil { - t.Fatalf("error tempfile: %s", err) - } - - defer os.Remove(tf.Name()) - - c = testConfig(t, map[string]interface{}{ - "local_state_tree": dir, - "minion_config_file": tf.Name(), - }) - - warns, errs = Provisioner().Validate(c) - - if len(warns) > 0 { - t.Fatalf("Warnings: %v", warns) - } - if len(errs) > 0 { - t.Fatalf("errs: %s", errs) - } -} - -func TestProvisionerPrepare_MinionConfig_RemoteStateTree(t *testing.T) { - dir, err := ioutil.TempDir("", "_terraform_saltmasterless_test") - if err != nil { - t.Fatalf("Error when creating temp dir: %v", err) - } - - c := testConfig(t, map[string]interface{}{ - "local_state_tree": dir, - "minion_config_file": "i/dont/exist", - "remote_state_tree": "i/dont/exist/remote_state_tree", - }) - - warns, errs := Provisioner().Validate(c) - if len(warns) > 0 { - t.Fatalf("Warnings: %v", warns) - } - if len(errs) == 0 { - t.Fatalf("Should be error") - } -} - -func TestProvisionerPrepare_MinionConfig_RemotePillarRoots(t *testing.T) { - dir, err := ioutil.TempDir("", "_terraform_saltmasterless_test") - if err != nil { - t.Fatalf("Error when creating temp dir: %v", err) - } - - c := testConfig(t, map[string]interface{}{ - "local_state_tree": dir, - "minion_config_file": "i/dont/exist", - "remote_pillar_roots": "i/dont/exist/remote_pillar_roots", - }) - - warns, errs := Provisioner().Validate(c) - if len(warns) > 0 { - t.Fatalf("Warnings: %v", warns) - } - if len(errs) == 0 { - t.Fatalf("Should be error") - } -} - -func TestProvisionerPrepare_LocalPillarRoots(t *testing.T) { - dir, err := ioutil.TempDir("", "_terraform_saltmasterless_test") - if err != nil { - t.Fatalf("Error when creating temp dir: %v", err) - } - - c := testConfig(t, map[string]interface{}{ - "local_state_tree": dir, - "minion_config_file": "i/dont/exist", - "local_pillar_roots": "i/dont/exist/local_pillar_roots", - }) - - warns, errs := Provisioner().Validate(c) - if len(warns) > 0 { - t.Fatalf("Warnings: %v", warns) - } - if len(errs) == 0 { - t.Fatalf("Should be error") - } -} - -func TestProvisionerSudo(t *testing.T) { - dir, err := ioutil.TempDir("", "_terraform_saltmasterless_test") - if err != nil { - t.Fatalf("Error when creating temp dir: %v", err) - } - - c := map[string]interface{}{ - "local_state_tree": dir, - } - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, c), - ) - - withSudo := p.sudo("echo hello") - if withSudo != "sudo echo hello" { - t.Fatalf("sudo command not generated correctly") - } - - c = map[string]interface{}{ - "local_state_tree": dir, - "disable_sudo": "true", - } - - p, err = decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, c), - ) - - if err != nil { - t.Fatalf("err: %s", err) - } - withoutSudo := p.sudo("echo hello") - if withoutSudo != "echo hello" { - t.Fatalf("sudo-less command not generated correctly") - } -} - -func TestProvisionerPrepare_RemoteStateTree(t *testing.T) { - dir, err := ioutil.TempDir("", "_terraform_saltmasterless_test") - if err != nil { - t.Fatalf("Error when creating temp dir: %v", err) - } - - c := map[string]interface{}{ - "local_state_tree": dir, - "remote_state_tree": "/remote_state_tree", - } - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, c), - ) - - if err != nil { - t.Fatalf("err: %s", err) - } - - if !strings.Contains(p.CmdArgs, "--file-root=/remote_state_tree") { - t.Fatal("--file-root should be set in CmdArgs") - } -} - -func TestProvisionerPrepare_RemotePillarRoots(t *testing.T) { - dir, err := ioutil.TempDir("", "_terraform_saltmasterless_test") - if err != nil { - t.Fatalf("Error when creating temp dir: %v", err) - } - - c := map[string]interface{}{ - "local_state_tree": dir, - "remote_pillar_roots": "/remote_pillar_roots", - } - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, c), - ) - - if err != nil { - t.Fatalf("err: %s", err) - } - - if !strings.Contains(p.CmdArgs, "--pillar-root=/remote_pillar_roots") { - t.Fatal("--pillar-root should be set in CmdArgs") - } -} - -func TestProvisionerPrepare_RemoteStateTree_Default(t *testing.T) { - dir, err := ioutil.TempDir("", "_terraform_saltmasterless_test") - if err != nil { - t.Fatalf("Error when creating temp dir: %v", err) - } - - c := map[string]interface{}{ - "local_state_tree": dir, - } - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, c), - ) - - if err != nil { - t.Fatalf("err: %s", err) - } - - if !strings.Contains(p.CmdArgs, "--file-root=/srv/salt") { - t.Fatal("--file-root should be set in CmdArgs") - } -} - -func TestProvisionerPrepare_RemotePillarRoots_Default(t *testing.T) { - dir, err := ioutil.TempDir("", "_terraform_saltmasterless_test") - if err != nil { - t.Fatalf("Error when creating temp dir: %v", err) - } - - c := map[string]interface{}{ - "local_state_tree": dir, - } - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, c), - ) - - if err != nil { - t.Fatalf("err: %s", err) - } - - if !strings.Contains(p.CmdArgs, "--pillar-root=/srv/pillar") { - t.Fatal("--pillar-root should be set in CmdArgs") - } -} - -func TestProvisionerPrepare_NoExitOnFailure(t *testing.T) { - dir, err := ioutil.TempDir("", "_terraform_saltmasterless_test") - if err != nil { - t.Fatalf("Error when creating temp dir: %v", err) - } - - c := map[string]interface{}{ - "local_state_tree": dir, - } - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, c), - ) - - if err != nil { - t.Fatalf("err: %s", err) - } - - if !strings.Contains(p.CmdArgs, "--retcode-passthrough") { - t.Fatal("--retcode-passthrough should be set in CmdArgs") - } - - c = map[string]interface{}{ - "no_exit_on_failure": true, - } - - p, err = decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, c), - ) - - if err != nil { - t.Fatalf("err: %s", err) - } - - if strings.Contains(p.CmdArgs, "--retcode-passthrough") { - t.Fatal("--retcode-passthrough should not be set in CmdArgs") - } -} - -func TestProvisionerPrepare_LogLevel(t *testing.T) { - dir, err := ioutil.TempDir("", "_terraform_saltmasterless_test") - if err != nil { - t.Fatalf("Error when creating temp dir: %v", err) - } - - c := map[string]interface{}{ - "local_state_tree": dir, - } - - p, err := decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, c), - ) - - if err != nil { - t.Fatalf("err: %s", err) - } - - if !strings.Contains(p.CmdArgs, "-l info") { - t.Fatal("-l info should be set in CmdArgs") - } - - c = map[string]interface{}{ - "log_level": "debug", - } - - p, err = decodeConfig( - schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, c), - ) - - if err != nil { - t.Fatalf("err: %s", err) - } - - if !strings.Contains(p.CmdArgs, "-l debug") { - t.Fatal("-l debug should be set in CmdArgs") - } -} diff --git a/vendor/github.com/hashicorp/terraform/codecov.yml b/vendor/github.com/hashicorp/terraform/codecov.yml index 783bebcc..5aeb0a38 100644 --- a/vendor/github.com/hashicorp/terraform/codecov.yml +++ b/vendor/github.com/hashicorp/terraform/codecov.yml @@ -5,7 +5,7 @@ comment: require_base: yes # [yes :: must have a base report to post] require_head: yes # [yes :: must have a head report to post] branches: # branch names that can post comment - - "master" + - "main" coverage: status: @@ -14,6 +14,11 @@ coverage: informational: true target: auto threshold: "0.5%" + patch: + default: + informational: true + target: auto + threshold: "0.5%" github_checks: annotations: false diff --git a/vendor/github.com/hashicorp/terraform/command/012_config_upgrade.go b/vendor/github.com/hashicorp/terraform/command/012_config_upgrade.go deleted file mode 100644 index 6d2ff2fd..00000000 --- a/vendor/github.com/hashicorp/terraform/command/012_config_upgrade.go +++ /dev/null @@ -1,33 +0,0 @@ -package command - -import ( - "fmt" - "strings" -) - -type ZeroTwelveUpgradeCommand struct { - Meta -} - -func (c *ZeroTwelveUpgradeCommand) Run(args []string) int { - c.Ui.Output(fmt.Sprintf(` -The 0.12upgrade command has been removed. You must run this command with -Terraform v0.12 to upgrade your configuration syntax before upgrading to the -current version.`)) - return 0 -} - -func (c *ZeroTwelveUpgradeCommand) Help() string { - helpText := ` -Usage: terraform 0.12upgrade - - The 0.12upgrade command has been removed. You must run this command with - Terraform v0.12 to upgrade your configuration syntax before upgrading to - the current version. -` - return strings.TrimSpace(helpText) -} - -func (c *ZeroTwelveUpgradeCommand) Synopsis() string { - return "Rewrites pre-0.12 module source code for v0.12" -} diff --git a/vendor/github.com/hashicorp/terraform/command/012_config_upgrade_test.go b/vendor/github.com/hashicorp/terraform/command/012_config_upgrade_test.go deleted file mode 100644 index 270f9611..00000000 --- a/vendor/github.com/hashicorp/terraform/command/012_config_upgrade_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package command - -import ( - "strings" - "testing" - - "github.com/mitchellh/cli" -) - -func TestZeroTwelveUpgrade_deprecated(t *testing.T) { - ui := new(cli.MockUi) - c := &ZeroTwelveUpgradeCommand{ - Meta: Meta{ - Ui: ui, - }, - } - - if code := c.Run([]string{}); code != 0 { - t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) - } - - output := ui.OutputWriter.String() - if !strings.Contains(output, "The 0.12upgrade command has been removed.") { - t.Fatal("unexpected output:", output) - } -} diff --git a/vendor/github.com/hashicorp/terraform/command/013_config_upgrade.go b/vendor/github.com/hashicorp/terraform/command/013_config_upgrade.go deleted file mode 100644 index 53d65a2e..00000000 --- a/vendor/github.com/hashicorp/terraform/command/013_config_upgrade.go +++ /dev/null @@ -1,35 +0,0 @@ -package command - -import ( - "fmt" - "strings" -) - -// ZeroThirteenUpgradeCommand upgrades configuration files for a module -// to include explicit provider source settings -type ZeroThirteenUpgradeCommand struct { - Meta -} - -func (c *ZeroThirteenUpgradeCommand) Run(args []string) int { - c.Ui.Output(fmt.Sprintf(` -The 0.13upgrade command has been removed. You must run this command with -Terraform v0.13 to upgrade your provider requirements before upgrading to the -current version.`)) - return 0 -} - -func (c *ZeroThirteenUpgradeCommand) Help() string { - helpText := ` -Usage: terraform 0.13upgrade - - The 0.13upgrade command has been removed. You must run this command with - Terraform v0.13 to upgrade your provider requirements before upgrading to - the current version. -` - return strings.TrimSpace(helpText) -} - -func (c *ZeroThirteenUpgradeCommand) Synopsis() string { - return "Rewrites pre-0.13 module source code for v0.13" -} diff --git a/vendor/github.com/hashicorp/terraform/command/013_config_upgrade_test.go b/vendor/github.com/hashicorp/terraform/command/013_config_upgrade_test.go deleted file mode 100644 index 2daad8fd..00000000 --- a/vendor/github.com/hashicorp/terraform/command/013_config_upgrade_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package command - -import ( - "strings" - "testing" - - "github.com/mitchellh/cli" -) - -func TestZeroThirteenUpgrade(t *testing.T) { - ui := new(cli.MockUi) - c := &ZeroThirteenUpgradeCommand{ - Meta: Meta{ - Ui: ui, - }, - } - - if code := c.Run([]string{}); code != 0 { - t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) - } - - output := ui.OutputWriter.String() - if !strings.Contains(output, "The 0.13upgrade command has been removed.") { - t.Fatal("unexpected output:", output) - } -} diff --git a/vendor/github.com/hashicorp/terraform/command/apply.go b/vendor/github.com/hashicorp/terraform/command/apply.go index eb2740e7..cca55001 100644 --- a/vendor/github.com/hashicorp/terraform/command/apply.go +++ b/vendor/github.com/hashicorp/terraform/command/apply.go @@ -1,15 +1,14 @@ package command import ( - "bytes" "fmt" - "sort" "strings" - "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/backend" - "github.com/hashicorp/terraform/repl" - "github.com/hashicorp/terraform/states" + remoteBackend "github.com/hashicorp/terraform/backend/remote" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/views" + "github.com/hashicorp/terraform/plans/planfile" "github.com/hashicorp/terraform/tfdiags" ) @@ -23,80 +22,193 @@ type ApplyCommand struct { Destroy bool } -func (c *ApplyCommand) Run(args []string) int { - var destroyForce, refresh, autoApprove bool - args = c.Meta.process(args) - cmdName := "apply" - if c.Destroy { - cmdName = "destroy" +func (c *ApplyCommand) Run(rawArgs []string) int { + var diags tfdiags.Diagnostics + + // Parse and apply global view arguments + common, rawArgs := arguments.ParseView(rawArgs) + c.View.Configure(common) + + // Propagate -no-color for the remote backend's legacy use of Ui. This + // should be removed when the remote backend is migrated to views. + c.Meta.color = !common.NoColor + c.Meta.Color = c.Meta.color + + // Parse and validate flags + var args *arguments.Apply + switch { + case c.Destroy: + args, diags = arguments.ParseApplyDestroy(rawArgs) + default: + args, diags = arguments.ParseApply(rawArgs) } - cmdFlags := c.Meta.extendedFlagSet(cmdName) - cmdFlags.BoolVar(&autoApprove, "auto-approve", false, "skip interactive approval of plan before applying") - if c.Destroy { - cmdFlags.BoolVar(&destroyForce, "force", false, "deprecated: same as auto-approve") + // Instantiate the view, even if there are flag errors, so that we render + // diagnostics according to the desired view + var view views.Apply + view = views.NewApply(args.ViewType, c.Destroy, c.View) + + if diags.HasErrors() { + view.Diagnostics(diags) + view.HelpPrompt() + return 1 } - cmdFlags.BoolVar(&refresh, "refresh", true, "refresh") - cmdFlags.IntVar(&c.Meta.parallelism, "parallelism", DefaultParallelism, "parallelism") - cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path") - cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path") - cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path") - cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state") - cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout") - cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } - if err := cmdFlags.Parse(args); err != nil { + + // Check for user-supplied plugin path + var err error + if c.pluginPath, err = c.loadPluginPath(); err != nil { + diags = diags.Append(err) + view.Diagnostics(diags) return 1 } - var diags tfdiags.Diagnostics + // Attempt to load the plan file, if specified + planFile, diags := c.LoadPlanFile(args.PlanPath) + if diags.HasErrors() { + view.Diagnostics(diags) + return 1 + } - args = cmdFlags.Args() - configPath, err := ModulePath(args) - if err != nil { - c.Ui.Error(err.Error()) + // Check for invalid combination of plan file and variable overrides + if planFile != nil && !args.Vars.Empty() { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Can't set variables when applying a saved plan", + "The -var and -var-file options cannot be used when applying a saved plan file, because a saved plan includes the variable values that were set when it was created.", + )) + view.Diagnostics(diags) return 1 } - // Check for user-supplied plugin path - if c.pluginPath, err = c.loadPluginPath(); err != nil { - c.Ui.Error(fmt.Sprintf("Error loading plugin path: %s", err)) + // FIXME: the -input flag value is needed to initialize the backend and the + // operation, but there is no clear path to pass this value down, so we + // continue to mutate the Meta object state for now. + c.Meta.input = args.InputEnabled + + // FIXME: the -parallelism flag is used to control the concurrency of + // Terraform operations. At the moment, this value is used both to + // initialize the backend via the ContextOpts field inside CLIOpts, and to + // set a largely unused field on the Operation request. Again, there is no + // clear path to pass this value down, so we continue to mutate the Meta + // object state for now. + c.Meta.parallelism = args.Operation.Parallelism + + // Prepare the backend, passing the plan file if present, and the + // backend-specific arguments + be, beDiags := c.PrepareBackend(planFile, args.State) + diags = diags.Append(beDiags) + if diags.HasErrors() { + view.Diagnostics(diags) return 1 } - // Check if the path is a plan - planFile, err := c.PlanFile(configPath) + // Build the operation request + opReq, opDiags := c.OperationRequest(be, view, planFile, args.Operation, args.AutoApprove) + diags = diags.Append(opDiags) + + // Collect variable value and add them to the operation request + diags = diags.Append(c.GatherVariables(opReq, args.Vars)) + + // Before we delegate to the backend, we'll print any warning diagnostics + // we've accumulated here, since the backend will start fresh with its own + // diagnostics. + view.Diagnostics(diags) + if diags.HasErrors() { + return 1 + } + diags = nil + + // Run the operation + op, err := c.RunOperation(be, opReq) if err != nil { - c.Ui.Error(err.Error()) + diags = diags.Append(err) + view.Diagnostics(diags) return 1 } - if c.Destroy && planFile != nil { - c.Ui.Error(fmt.Sprintf("Destroy can't be called with a plan file.")) + + if op.Result != backend.OperationSuccess { + return op.Result.ExitStatus() + } + + // Render the resource count and outputs, unless we're using the remote + // backend locally, in which case these are rendered remotely + if rb, isRemoteBackend := be.(*remoteBackend.Remote); !isRemoteBackend || rb.IsLocalOperations() { + view.ResourceCount(args.State.StateOutPath) + if !c.Destroy && op.State != nil { + view.Outputs(op.State.RootModule().OutputValues) + } + } + + view.Diagnostics(diags) + + if diags.HasErrors() { return 1 } - if planFile != nil { - // Reset the config path for backend loading - configPath = "" - if !c.variableArgs.Empty() { + return 0 +} + +func (c *ApplyCommand) LoadPlanFile(path string) (*planfile.Reader, tfdiags.Diagnostics) { + var planFile *planfile.Reader + var diags tfdiags.Diagnostics + + // Try to load plan if path is specified + if path != "" { + var err error + planFile, err = c.PlanFile(path) + if err != nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + fmt.Sprintf("Failed to load %q as a plan file", path), + fmt.Sprintf("Error: %s", err), + )) + return nil, diags + } + + // If the path doesn't look like a plan, both planFile and err will be + // nil. In that case, the user is probably trying to use the positional + // argument to specify a configuration path. Point them at -chdir. + if planFile == nil { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, - "Can't set variables when applying a saved plan", - "The -var and -var-file options cannot be used when applying a saved plan file, because a saved plan includes the variable values that were set when it was created.", + fmt.Sprintf("Failed to load %q as a plan file", path), + "The specified path is a directory, not a plan file. You can use the global -chdir flag to use this directory as the configuration root.", )) - c.showDiagnostics(diags) - return 1 + return nil, diags + } + + // If we successfully loaded a plan but this is a destroy operation, + // explain that this is not supported. + if c.Destroy { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Destroy can't be called with a plan file", + fmt.Sprintf("If this plan was created using plan -destroy, apply it using:\n terraform apply %q", path), + )) + return nil, diags } } + return planFile, diags +} + +func (c *ApplyCommand) PrepareBackend(planFile *planfile.Reader, args *arguments.State) (backend.Enhanced, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + + // FIXME: we need to apply the state arguments to the meta object here + // because they are later used when initializing the backend. Carving a + // path to pass these arguments to the functions that need them is + // difficult but would make their use easier to understand. + c.Meta.applyStateArguments(args) + // Load the backend var be backend.Enhanced var beDiags tfdiags.Diagnostics if planFile == nil { - backendConfig, configDiags := c.loadBackendConfig(configPath) + backendConfig, configDiags := c.loadBackendConfig(".") diags = diags.Append(configDiags) if configDiags.HasErrors() { - c.showDiagnostics(diags) - return 1 + return nil, diags } be, beDiags = c.Backend(&BackendOpts{ @@ -110,80 +222,84 @@ func (c *ApplyCommand) Run(args []string) int { "Failed to read plan from plan file", fmt.Sprintf("Cannot read the plan from the given plan file: %s.", err), )) - c.showDiagnostics(diags) - return 1 + return nil, diags } if plan.Backend.Config == nil { // Should never happen; always indicates a bug in the creation of the plan file diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, "Failed to read plan from plan file", - fmt.Sprintf("The given plan file does not have a valid backend configuration. This is a bug in the Terraform command that generated this plan file."), + "The given plan file does not have a valid backend configuration. This is a bug in the Terraform command that generated this plan file.", )) - c.showDiagnostics(diags) - return 1 + return nil, diags } be, beDiags = c.BackendForPlan(plan.Backend) } + diags = diags.Append(beDiags) if beDiags.HasErrors() { - c.showDiagnostics(diags) - return 1 + return nil, diags } + return be, diags +} + +func (c *ApplyCommand) OperationRequest( + be backend.Enhanced, + view views.Apply, + planFile *planfile.Reader, + args *arguments.Operation, + autoApprove bool, +) (*backend.Operation, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics // Applying changes with dev overrides in effect could make it impossible // to switch back to a release version if the schema isn't compatible, // so we'll warn about it. - diags = diags.Append(c.providerDevOverrideWarnings()) - - // Before we delegate to the backend, we'll print any warning diagnostics - // we've accumulated here, since the backend will start fresh with its own - // diagnostics. - c.showDiagnostics(diags) - diags = nil + diags = diags.Append(c.providerDevOverrideRuntimeWarnings()) // Build the operation opReq := c.Operation(be) opReq.AutoApprove = autoApprove - opReq.ConfigDir = configPath - opReq.Destroy = c.Destroy - opReq.DestroyForce = destroyForce + opReq.ConfigDir = "." + opReq.PlanMode = args.PlanMode + opReq.Hooks = view.Hooks() opReq.PlanFile = planFile - opReq.PlanRefresh = refresh + opReq.PlanRefresh = args.Refresh + opReq.Targets = args.Targets + opReq.ForceReplace = args.ForceReplace opReq.Type = backend.OperationTypeApply + opReq.View = view.Operation() + var err error opReq.ConfigLoader, err = c.initConfigLoader() if err != nil { - c.showDiagnostics(err) - return 1 + diags = diags.Append(fmt.Errorf("Failed to initialize config loader: %s", err)) + return nil, diags } - { - var moreDiags tfdiags.Diagnostics - opReq.Variables, moreDiags = c.collectVariableValues() - diags = diags.Append(moreDiags) - if moreDiags.HasErrors() { - c.showDiagnostics(diags) - return 1 - } - } + return opReq, diags +} - op, err := c.RunOperation(be, opReq) - if err != nil { - c.showDiagnostics(err) - return 1 - } - if op.Result != backend.OperationSuccess { - return op.Result.ExitStatus() - } +func (c *ApplyCommand) GatherVariables(opReq *backend.Operation, args *arguments.Vars) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics - if !c.Destroy { - if outputs := outputsAsString(op.State, addrs.RootModuleInstance, true); outputs != "" { - c.Ui.Output(c.Colorize().Color(outputs)) - } + // FIXME the arguments package currently trivially gathers variable related + // arguments in a heterogenous slice, in order to minimize the number of + // code paths gathering variables during the transition to this structure. + // Once all commands that gather variables have been converted to this + // structure, we could move the variable gathering code to the arguments + // package directly, removing this shim layer. + + varArgs := args.All() + items := make([]rawFlag, len(varArgs)) + for i := range varArgs { + items[i].Name = varArgs[i].Name + items[i].Value = varArgs[i].Value } + c.Meta.variableArgs = rawFlags{items: &items} + opReq.Variables, diags = c.collectVariableValues() - return op.Result.ExitStatus() + return diags } func (c *ApplyCommand) Help() string { @@ -196,23 +312,24 @@ func (c *ApplyCommand) Help() string { func (c *ApplyCommand) Synopsis() string { if c.Destroy { - return "Destroy Terraform-managed infrastructure" + return "Destroy previously-created infrastructure" } - return "Builds or changes infrastructure" + return "Create or update infrastructure" } func (c *ApplyCommand) helpApply() string { helpText := ` -Usage: terraform apply [options] [DIR-OR-PLAN] +Usage: terraform [global options] apply [options] [PLAN] - Builds or changes infrastructure according to Terraform configuration - files in DIR. + Creates or updates infrastructure according to Terraform configuration + files in the current directory. - By default, apply scans the current directory for the configuration - and applies the changes appropriately. However, a path to another - configuration or an execution plan can be provided. Execution plans can be - used to only execute a pre-determined set of actions. + By default, Terraform will generate a new plan and present it for your + approval before taking any action. You can optionally provide a plan + file created by a previous call to "terraform plan", in which case + Terraform will take the actions described in that plan without any + confirmation prompt. Options: @@ -237,9 +354,6 @@ Options: -parallelism=n Limit the number of parallel resource operations. Defaults to 10. - -refresh=true Update state prior to checking for differences. This - has no effect if a plan file is given to apply. - -state=path Path to read and save state (unless state-out is specified). Defaults to "terraform.tfstate". @@ -247,116 +361,26 @@ Options: "-state". This can be used to preserve the old state. - -target=resource Resource to target. Operation will be limited to this - resource and its dependencies. This flag can be used - multiple times. - - -var 'foo=bar' Set a variable in the Terraform configuration. This - flag can be set multiple times. - - -var-file=foo Set variables in the Terraform configuration from - a file. If "terraform.tfvars" or any ".auto.tfvars" - files are present, they will be automatically loaded. - - + If you don't provide a saved plan file then this command will also accept + all of the plan-customization options accepted by the terraform plan command. + For more information on those options, run: + terraform plan -help ` return strings.TrimSpace(helpText) } func (c *ApplyCommand) helpDestroy() string { helpText := ` -Usage: terraform destroy [options] [DIR] +Usage: terraform [global options] destroy [options] Destroy Terraform-managed infrastructure. -Options: - - -backup=path Path to backup the existing state file before - modifying. Defaults to the "-state-out" path with - ".backup" extension. Set to "-" to disable backup. - - -auto-approve Skip interactive approval before destroying. - - -force Deprecated: same as auto-approve. - - -lock=true Lock the state file when locking is supported. - - -lock-timeout=0s Duration to retry a state lock. - - -no-color If specified, output won't contain any color. - - -parallelism=n Limit the number of concurrent operations. - Defaults to 10. - - -refresh=true Update state prior to checking for differences. This - has no effect if a plan file is given to apply. - - -state=path Path to read and save state (unless state-out - is specified). Defaults to "terraform.tfstate". - - -state-out=path Path to write state to that is different than - "-state". This can be used to preserve the old - state. - - -target=resource Resource to target. Operation will be limited to this - resource and its dependencies. This flag can be used - multiple times. - - -var 'foo=bar' Set a variable in the Terraform configuration. This - flag can be set multiple times. - - -var-file=foo Set variables in the Terraform configuration from - a file. If "terraform.tfvars" or any ".auto.tfvars" - files are present, they will be automatically loaded. - + This command is a convenience alias for: + terraform apply -destroy + This command also accepts many of the plan-customization options accepted by + the terraform plan command. For more information on those options, run: + terraform plan -help ` return strings.TrimSpace(helpText) } - -func outputsAsString(state *states.State, modPath addrs.ModuleInstance, includeHeader bool) string { - if state == nil { - return "" - } - - ms := state.Module(modPath) - if ms == nil { - return "" - } - - outputs := ms.OutputValues - outputBuf := new(bytes.Buffer) - if len(outputs) > 0 { - if includeHeader { - outputBuf.WriteString("[reset][bold][green]\nOutputs:\n\n") - } - - // Output the outputs in alphabetical order - keyLen := 0 - ks := make([]string, 0, len(outputs)) - for key, _ := range outputs { - ks = append(ks, key) - if len(key) > keyLen { - keyLen = len(key) - } - } - sort.Strings(ks) - - for _, k := range ks { - v := outputs[k] - if v.Sensitive { - outputBuf.WriteString(fmt.Sprintf("%s = \n", k)) - continue - } - - result := repl.FormatValue(v.Value, 0) - outputBuf.WriteString(fmt.Sprintf("%s = %s\n", k, result)) - } - } - - return strings.TrimSpace(outputBuf.String()) -} - -const outputInterrupt = `Interrupt received. -Please wait for Terraform to exit or data loss may occur. -Gracefully shutting down...` diff --git a/vendor/github.com/hashicorp/terraform/command/apply_destroy_test.go b/vendor/github.com/hashicorp/terraform/command/apply_destroy_test.go index 6acffdf7..545b2138 100644 --- a/vendor/github.com/hashicorp/terraform/command/apply_destroy_test.go +++ b/vendor/github.com/hashicorp/terraform/command/apply_destroy_test.go @@ -14,10 +14,15 @@ import ( "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/states/statefile" - "github.com/hashicorp/terraform/terraform" ) func TestApply_destroy(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + originalState := states.BuildState(func(s *states.SyncState) { s.SetResourceInstanceCurrent( addrs.Resource{ @@ -38,23 +43,25 @@ func TestApply_destroy(t *testing.T) { statePath := testStateFile(t, originalState) p := testProvider() - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Computed: true}, - "ami": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + "ami": {Type: cty.String, Optional: true}, + }, }, }, }, } - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Destroy: true, Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -62,11 +69,12 @@ func TestApply_destroy(t *testing.T) { args := []string{ "-auto-approve", "-state", statePath, - testFixturePath("apply"), } - if code := c.Run(args); code != 0 { - t.Log(ui.OutputWriter.String()) - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Log(output.Stdout()) + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } // Verify a new state exists @@ -113,7 +121,154 @@ func TestApply_destroy(t *testing.T) { } } +func TestApply_destroyApproveNo(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + // Create some existing state + originalState := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"bar"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + }) + statePath := testStateFile(t, originalState) + + p := applyFixtureProvider() + + defer testInputMap(t, map[string]string{ + "approve": "no", + })() + + // Do not use the NewMockUi initializer here, as we want to delay + // the call to init until after setting up the input mocks + ui := new(cli.MockUi) + view, done := testView(t) + c := &ApplyCommand{ + Destroy: true, + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + Ui: ui, + View: view, + }, + } + + args := []string{ + "-state", statePath, + } + code := c.Run(args) + output := done(t) + if code != 1 { + t.Fatalf("bad: %d\n\n%s", code, output.Stdout()) + } + if got, want := output.Stdout(), "Destroy cancelled"; !strings.Contains(got, want) { + t.Fatalf("expected output to include %q, but was:\n%s", want, got) + } + + state := testStateRead(t, statePath) + if state == nil { + t.Fatal("state should not be nil") + } + actualStr := strings.TrimSpace(state.String()) + expectedStr := strings.TrimSpace(originalState.String()) + if actualStr != expectedStr { + t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr) + } +} + +func TestApply_destroyApproveYes(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + // Create some existing state + originalState := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"bar"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + }) + statePath := testStateFile(t, originalState) + + p := applyFixtureProvider() + + defer testInputMap(t, map[string]string{ + "approve": "yes", + })() + + // Do not use the NewMockUi initializer here, as we want to delay + // the call to init until after setting up the input mocks + ui := new(cli.MockUi) + view, done := testView(t) + c := &ApplyCommand{ + Destroy: true, + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + Ui: ui, + View: view, + }, + } + + args := []string{ + "-state", statePath, + } + code := c.Run(args) + output := done(t) + if code != 0 { + t.Log(output.Stdout()) + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + + if _, err := os.Stat(statePath); err != nil { + t.Fatalf("err: %s", err) + } + + state := testStateRead(t, statePath) + if state == nil { + t.Fatal("state should not be nil") + } + + actualStr := strings.TrimSpace(state.String()) + expectedStr := strings.TrimSpace(testApplyDestroyStr) + if actualStr != expectedStr { + t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr) + } +} + func TestApply_destroyLockedState(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + originalState := states.BuildState(func(s *states.SyncState) { s.SetResourceInstanceCurrent( addrs.Resource{ @@ -140,12 +295,12 @@ func TestApply_destroyLockedState(t *testing.T) { defer unlock() p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Destroy: true, Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -153,29 +308,35 @@ func TestApply_destroyLockedState(t *testing.T) { args := []string{ "-auto-approve", "-state", statePath, - testFixturePath("apply"), } - if code := c.Run(args); code == 0 { - t.Fatal("expected error") + code := c.Run(args) + output := done(t) + if code == 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stdout()) } - output := ui.ErrorWriter.String() - if !strings.Contains(output, "lock") { - t.Fatal("command output does not look like a lock error:", output) + if !strings.Contains(output.Stderr(), "lock") { + t.Fatal("command output does not look like a lock error:", output.Stderr()) } } func TestApply_destroyPlan(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + planPath := testPlanFileNoop(t) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Destroy: true, Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -183,12 +344,58 @@ func TestApply_destroyPlan(t *testing.T) { args := []string{ planPath, } - if code := c.Run(args); code != 1 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 1 { + t.Fatalf("bad: %d\n\n%s", code, output.Stdout()) + } + if !strings.Contains(output.Stderr(), "plan file") { + t.Fatal("expected command output to refer to plan file, but got:", output.Stderr()) } } -func TestApply_destroyTargeted(t *testing.T) { +func TestApply_destroyPath(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + p := applyFixtureProvider() + + view, done := testView(t) + c := &ApplyCommand{ + Destroy: true, + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + View: view, + }, + } + + args := []string{ + "-auto-approve", + testFixturePath("apply"), + } + code := c.Run(args) + output := done(t) + if code != 1 { + t.Fatalf("bad: %d\n\n%s", code, output.Stdout()) + } + if !strings.Contains(output.Stderr(), "-chdir") { + t.Fatal("expected command output to refer to -chdir flag, but got:", output.Stderr()) + } +} + +// Config with multiple resources with dependencies, targeting destroy of a +// root node, expecting all other resources to be destroyed due to +// dependencies. +func TestApply_destroyTargetedDependencies(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-destroy-targeted"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + originalState := states.BuildState(func(s *states.SyncState) { s.SetResourceInstanceCurrent( addrs.Resource{ @@ -225,17 +432,21 @@ func TestApply_destroyTargeted(t *testing.T) { statePath := testStateFile(t, originalState) p := testProvider() - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Computed: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + }, }, }, "test_load_balancer": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Computed: true}, - "instances": {Type: cty.List(cty.String), Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + "instances": {Type: cty.List(cty.String), Optional: true}, + }, }, }, }, @@ -246,12 +457,12 @@ func TestApply_destroyTargeted(t *testing.T) { } } - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Destroy: true, Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -260,10 +471,12 @@ func TestApply_destroyTargeted(t *testing.T) { "-auto-approve", "-target", "test_instance.foo", "-state", statePath, - testFixturePath("apply-destroy-targeted"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Log(output.Stdout()) + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } // Verify a new state exists @@ -309,6 +522,159 @@ func TestApply_destroyTargeted(t *testing.T) { } } +// Config with multiple resources with dependencies, targeting destroy of a +// leaf node, expecting the other resources to remain. +func TestApply_destroyTargeted(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-destroy-targeted"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + originalState := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"i-ab123"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_load_balancer", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"i-abc123"}`), + Dependencies: []addrs.ConfigResource{mustResourceAddr("test_instance.foo")}, + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + }) + wantState := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"i-ab123"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + }) + statePath := testStateFile(t, originalState) + + p := testProvider() + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ + "test_instance": { + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + }, + }, + }, + "test_load_balancer": { + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + "instances": {Type: cty.List(cty.String), Optional: true}, + }, + }, + }, + }, + } + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { + return providers.PlanResourceChangeResponse{ + PlannedState: req.ProposedNewState, + } + } + + view, done := testView(t) + c := &ApplyCommand{ + Destroy: true, + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + View: view, + }, + } + + // Run the apply command pointing to our existing state + args := []string{ + "-auto-approve", + "-target", "test_load_balancer.foo", + "-state", statePath, + } + code := c.Run(args) + output := done(t) + if code != 0 { + t.Log(output.Stdout()) + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + + // Verify a new state exists + if _, err := os.Stat(statePath); err != nil { + t.Fatalf("err: %s", err) + } + + f, err := os.Open(statePath) + if err != nil { + t.Fatalf("err: %s", err) + } + defer f.Close() + + stateFile, err := statefile.Read(f) + if err != nil { + t.Fatalf("err: %s", err) + } + if stateFile == nil || stateFile.State == nil { + t.Fatal("state should not be nil") + } + + actualStr := strings.TrimSpace(stateFile.State.String()) + expectedStr := strings.TrimSpace(wantState.String()) + if actualStr != expectedStr { + t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\nb%s", actualStr, expectedStr) + } + + // Should have a backup file + f, err = os.Open(statePath + DefaultBackupExtension) + if err != nil { + t.Fatalf("err: %s", err) + } + + backupStateFile, err := statefile.Read(f) + f.Close() + if err != nil { + t.Fatalf("err: %s", err) + } + + backupActualStr := strings.TrimSpace(backupStateFile.State.String()) + backupExpectedStr := strings.TrimSpace(originalState.String()) + if backupActualStr != backupExpectedStr { + t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\nb%s", backupActualStr, backupExpectedStr) + } +} + const testApplyDestroyStr = ` ` diff --git a/vendor/github.com/hashicorp/terraform/command/apply_test.go b/vendor/github.com/hashicorp/terraform/command/apply_test.go index 280568b4..ab6d2c6a 100644 --- a/vendor/github.com/hashicorp/terraform/command/apply_test.go +++ b/vendor/github.com/hashicorp/terraform/command/apply_test.go @@ -2,13 +2,14 @@ package command import ( "bytes" + "context" + "encoding/json" "fmt" "io/ioutil" - "net" - "net/http" - "net/url" "os" + "path" "path/filepath" + "reflect" "strings" "sync" "testing" @@ -20,34 +21,166 @@ import ( "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/states/statemgr" "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/tfdiags" + tfversion "github.com/hashicorp/terraform/version" ) func TestApply(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + statePath := testTempFile(t) p := applyFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{ "-state", statePath, "-auto-approve", + } + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + + if _, err := os.Stat(statePath); err != nil { + t.Fatalf("err: %s", err) + } + + state := testStateRead(t, statePath) + if state == nil { + t.Fatal("state should not be nil") + } +} + +func TestApply_path(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + p := applyFixtureProvider() + + view, done := testView(t) + c := &ApplyCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + View: view, + }, + } + + args := []string{ + "-auto-approve", testFixturePath("apply"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 1 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + if !strings.Contains(output.Stderr(), "-chdir") { + t.Fatal("expected command output to refer to -chdir flag, but got:", output.Stderr()) + } +} + +func TestApply_approveNo(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + statePath := testTempFile(t) + + defer testInputMap(t, map[string]string{ + "approve": "no", + })() + + // Do not use the NewMockUi initializer here, as we want to delay + // the call to init until after setting up the input mocks + ui := new(cli.MockUi) + + p := applyFixtureProvider() + view, done := testView(t) + c := &ApplyCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + Ui: ui, + View: view, + }, + } + + args := []string{ + "-state", statePath, + } + code := c.Run(args) + output := done(t) + if code != 1 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + if got, want := output.Stdout(), "Apply cancelled"; !strings.Contains(got, want) { + t.Fatalf("expected output to include %q, but was:\n%s", want, got) + } + + if _, err := os.Stat(statePath); err == nil || !os.IsNotExist(err) { + t.Fatalf("state file should not exist") + } +} + +func TestApply_approveYes(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + statePath := testTempFile(t) + + p := applyFixtureProvider() + + defer testInputMap(t, map[string]string{ + "approve": "yes", + })() + + // Do not use the NewMockUi initializer here, as we want to delay + // the call to init until after setting up the input mocks + ui := new(cli.MockUi) + + view, done := testView(t) + c := &ApplyCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + Ui: ui, + View: view, + }, + } + + args := []string{ + "-state", statePath, + } + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if _, err := os.Stat(statePath); err != nil { @@ -62,6 +195,12 @@ func TestApply(t *testing.T) { // test apply with locked state func TestApply_lockedState(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + statePath := testTempFile(t) unlock, err := testLockState(testDataDir, statePath) @@ -71,31 +210,37 @@ func TestApply_lockedState(t *testing.T) { defer unlock() p := applyFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{ "-state", statePath, "-auto-approve", - testFixturePath("apply"), } - if code := c.Run(args); code == 0 { + code := c.Run(args) + output := done(t) + if code == 0 { t.Fatal("expected error") } - output := ui.ErrorWriter.String() - if !strings.Contains(output, "lock") { - t.Fatal("command output does not look like a lock error:", output) + if !strings.Contains(output.Stderr(), "lock") { + t.Fatal("command output does not look like a lock error:", output.Stderr()) } } // test apply with locked state, waiting for unlock func TestApply_lockedStateWait(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + statePath := testTempFile(t) unlock, err := testLockState(testDataDir, statePath) @@ -110,11 +255,11 @@ func TestApply_lockedStateWait(t *testing.T) { }() p := applyFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -124,53 +269,36 @@ func TestApply_lockedStateWait(t *testing.T) { "-state", statePath, "-lock-timeout", "4s", "-auto-approve", - testFixturePath("apply"), } - if code := c.Run(args); code != 0 { - t.Fatalf("lock should have succeeded in less than 3s: %s", ui.ErrorWriter) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("lock should have succeeded in less than 3s: %s", output.Stderr()) } } -// high water mark counter -type hwm struct { - sync.Mutex - val int - max int -} - -func (t *hwm) Inc() { - t.Lock() - defer t.Unlock() - t.val++ - if t.val > t.max { - t.max = t.val - } -} - -func (t *hwm) Dec() { - t.Lock() - defer t.Unlock() - t.val-- -} - -func (t *hwm) Max() int { - t.Lock() - defer t.Unlock() - return t.max -} - +// Verify that the parallelism flag allows no more than the desired number of +// concurrent calls to ApplyResourceChange. func TestApply_parallelism(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("parallelism"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + statePath := testTempFile(t) par := 4 - // This blocks all the apply functions. We close it when we exit so - // they end quickly after this test finishes. - block := make(chan struct{}) - // signal how many goroutines have started - started := make(chan int, 100) + // started is a semaphore that we use to ensure that we never have more + // than "par" apply operations happening concurrently + started := make(chan struct{}, par) - runCount := &hwm{} + // beginCtx is used as a starting gate to hold back ApplyResourceChange + // calls until we reach the desired concurrency. The cancel func "begin" is + // called once we reach the desired concurrency, allowing all apply calls + // to proceed in unison. + beginCtx, begin := context.WithCancel(context.Background()) // Since our mock provider has its own mutex preventing concurrent calls // to ApplyResourceChange, we need to use a number of separate providers @@ -180,9 +308,9 @@ func TestApply_parallelism(t *testing.T) { for i := 0; i < 10; i++ { name := fmt.Sprintf("test%d", i) provider := &terraform.MockProvider{} - provider.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ - name + "_instance": {}, + provider.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ + name + "_instance": {Block: &configschema.Block{}}, }, } provider.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { @@ -191,12 +319,29 @@ func TestApply_parallelism(t *testing.T) { } } provider.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { - // Increment so we're counting parallelism - started <- 1 - runCount.Inc() - defer runCount.Dec() - // Block here to stage up our max number of parallel instances - <-block + + // If we ever have more than our intended parallelism number of + // apply operations running concurrently, the semaphore will fail. + select { + case started <- struct{}{}: + defer func() { + <-started + }() + default: + t.Fatal("too many concurrent apply operations") + } + + // If we never reach our intended parallelism, the context will + // never be canceled and the test will time out. + if len(started) >= par { + begin() + } + <-beginCtx.Done() + + // do some "work" + // Not required for correctness, but makes it easier to spot a + // failure when there is more overlap. + time.Sleep(10 * time.Millisecond) return providers.ApplyResourceChangeResponse{ NewState: cty.EmptyObjectVal, @@ -208,11 +353,11 @@ func TestApply_parallelism(t *testing.T) { Providers: providerFactories, } - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: testingOverrides, - Ui: ui, + View: view, }, } @@ -220,68 +365,49 @@ func TestApply_parallelism(t *testing.T) { "-state", statePath, "-auto-approve", fmt.Sprintf("-parallelism=%d", par), - testFixturePath("parallelism"), - } - - // Run in a goroutine. We can get any errors from the ui.OutputWriter - doneCh := make(chan int, 1) - go func() { - doneCh <- c.Run(args) - }() - - timeout := time.After(5 * time.Second) - - // ensure things are running - for i := 0; i < par; i++ { - select { - case <-timeout: - t.Fatal("timeout waiting for all goroutines to start") - case <-started: - } - } - - // a little extra sleep, since we can't ensure all goroutines from the walk have - // really started - time.Sleep(100 * time.Millisecond) - close(block) - - select { - case res := <-doneCh: - if res != 0 { - t.Fatal(ui.OutputWriter.String()) - } - case <-timeout: - t.Fatal("timeout waiting from Run()") } - // The total in flight should equal the parallelism - if runCount.Max() != par { - t.Fatalf("Expected parallelism: %d, got: %d", par, runCount.Max()) + res := c.Run(args) + output := done(t) + if res != 0 { + t.Fatal(output.Stdout()) } } func TestApply_configInvalid(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-config-invalid"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{ "-state", testTempFile(t), "-auto-approve", - testFixturePath("apply-config-invalid"), } - if code := c.Run(args); code != 1 { - t.Fatalf("bad: \n%s", ui.OutputWriter.String()) + code := c.Run(args) + output := done(t) + if code != 1 { + t.Fatalf("bad: \n%s", output.Stdout()) } } func TestApply_defaultState(t *testing.T) { - td := testTempDir(t) + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + statePath := filepath.Join(td, DefaultStateFilename) // Change to the temporary directory @@ -295,11 +421,11 @@ func TestApply_defaultState(t *testing.T) { defer os.Chdir(cwd) p := applyFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -311,10 +437,11 @@ func TestApply_defaultState(t *testing.T) { args := []string{ "-auto-approve", - testFixturePath("apply"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if _, err := os.Stat(statePath); err != nil { @@ -328,14 +455,20 @@ func TestApply_defaultState(t *testing.T) { } func TestApply_error(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-error"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + statePath := testTempFile(t) p := testProvider() - ui := cli.NewMockUi() + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -362,13 +495,15 @@ func TestApply_error(t *testing.T) { resp.PlannedState = cty.ObjectVal(s) return } - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, - "error": {Type: cty.Bool, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "ami": {Type: cty.String, Optional: true}, + "error": {Type: cty.Bool, Optional: true}, + }, }, }, }, @@ -377,14 +512,11 @@ func TestApply_error(t *testing.T) { args := []string{ "-state", statePath, "-auto-approve", - testFixturePath("apply-error"), - } - if ui.ErrorWriter != nil { - t.Logf("stdout:\n%s", ui.OutputWriter.String()) - t.Logf("stderr:\n%s", ui.ErrorWriter.String()) } - if code := c.Run(args); code != 1 { - t.Fatalf("wrong exit code %d; want 1", code) + code := c.Run(args) + output := done(t) + if code != 1 { + t.Fatalf("wrong exit code %d; want 1\n%s", code, output.Stdout()) } if _, err := os.Stat(statePath); err != nil { @@ -401,6 +533,12 @@ func TestApply_error(t *testing.T) { } func TestApply_input(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-input"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + // Disable test mode so input would be asked test = false defer func() { test = true }() @@ -416,21 +554,22 @@ func TestApply_input(t *testing.T) { statePath := testTempFile(t) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{ "-state", statePath, "-auto-approve", - testFixturePath("apply-input"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } expected := strings.TrimSpace(` @@ -445,6 +584,12 @@ result = foo // When only a partial set of the variables are set, Terraform // should still ask for the unset ones by default (with -input=true) func TestApply_inputPartial(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-input-partial"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + // Disable test mode so input would be asked test = false defer func() { test = true }() @@ -456,11 +601,11 @@ func TestApply_inputPartial(t *testing.T) { statePath := testTempFile(t) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -468,10 +613,11 @@ func TestApply_inputPartial(t *testing.T) { "-state", statePath, "-auto-approve", "-var", "foo=foovalue", - testFixturePath("apply-input-partial"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } expected := strings.TrimSpace(` @@ -485,23 +631,20 @@ foo = foovalue } func TestApply_noArgs(t *testing.T) { - cwd, err := os.Getwd() - if err != nil { - t.Fatalf("err: %s", err) - } - if err := os.Chdir(testFixturePath("apply")); err != nil { - t.Fatalf("err: %s", err) - } - defer os.Chdir(cwd) + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() statePath := testTempFile(t) p := applyFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -509,8 +652,10 @@ func TestApply_noArgs(t *testing.T) { "-state", statePath, "-auto-approve", } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if _, err := os.Stat(statePath); err != nil { @@ -518,9 +663,6 @@ func TestApply_noArgs(t *testing.T) { } state := testStateRead(t, statePath) - if err != nil { - t.Fatalf("err: %s", err) - } if state == nil { t.Fatal("state should not be nil") } @@ -539,11 +681,11 @@ func TestApply_plan(t *testing.T) { statePath := testTempFile(t) p := applyFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -551,8 +693,10 @@ func TestApply_plan(t *testing.T) { "-state-out", statePath, planPath, } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if _, err := os.Stat(statePath); err != nil { @@ -571,11 +715,11 @@ func TestApply_plan_backup(t *testing.T) { backupPath := testTempFile(t) p := applyFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -590,8 +734,10 @@ func TestApply_plan_backup(t *testing.T) { "-backup", backupPath, planPath, } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } // Should have a backup file @@ -603,11 +749,11 @@ func TestApply_plan_noBackup(t *testing.T) { statePath := testTempFile(t) p := applyFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -616,8 +762,10 @@ func TestApply_plan_noBackup(t *testing.T) { "-backup", "-", planPath, } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } // Ensure there is no backup @@ -681,19 +829,21 @@ func TestApply_plan_remoteState(t *testing.T) { }) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{ planPath, } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } // State file should be not be installed @@ -728,11 +878,11 @@ func TestApply_planWithVarFile(t *testing.T) { defer os.Chdir(cwd) p := applyFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -740,8 +890,10 @@ func TestApply_planWithVarFile(t *testing.T) { "-state-out", statePath, planPath, } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if _, err := os.Stat(statePath); err != nil { @@ -759,11 +911,11 @@ func TestApply_planVars(t *testing.T) { statePath := testTempFile(t) p := applyFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -772,8 +924,10 @@ func TestApply_planVars(t *testing.T) { "-var", "foo=bar", planPath, } - if code := c.Run(args); code == 0 { - t.Fatal("should've failed") + code := c.Run(args) + output := done(t) + if code == 0 { + t.Fatal("should've failed: ", output.Stdout()) } } @@ -787,23 +941,28 @@ func TestApply_planNoModuleFiles(t *testing.T) { p := applyFixtureProvider() planPath := applyFixturePlanFile(t) - + view, done := testView(t) apply := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: new(cli.MockUi), + View: view, }, } args := []string{ planPath, } apply.Run(args) - if p.PrepareProviderConfigCalled { - t.Fatal("Prepare provider config should not be called with a plan") - } + done(t) } func TestApply_refresh(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + originalState := states.BuildState(func(s *states.SyncState) { s.SetResourceInstanceCurrent( addrs.Resource{ @@ -824,21 +983,22 @@ func TestApply_refresh(t *testing.T) { statePath := testStateFile(t, originalState) p := applyFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{ "-state", statePath, "-auto-approve", - testFixturePath("apply"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if !p.ReadResourceCalled { @@ -864,35 +1024,91 @@ func TestApply_refresh(t *testing.T) { } } -func TestApply_shutdown(t *testing.T) { - cancelled := make(chan struct{}) - shutdownCh := make(chan struct{}) +func TestApply_refreshFalse(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() - statePath := testTempFile(t) - p := testProvider() + originalState := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"ami":"bar"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + }) + statePath := testStateFile(t, originalState) - ui := new(cli.MockUi) + p := applyFixtureProvider() + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, - ShutdownCh: shutdownCh, + View: view, }, } - p.StopFn = func() error { - close(cancelled) - return nil + args := []string{ + "-state", statePath, + "-auto-approve", + "-refresh=false", } - - p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { - resp.PlannedState = req.ProposedNewState - return + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } - var once sync.Once - p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { - // only cancel once + if p.ReadResourceCalled { + t.Fatal("should not call ReadResource when refresh=false") + } +} +func TestApply_shutdown(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-shutdown"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + cancelled := make(chan struct{}) + shutdownCh := make(chan struct{}) + + statePath := testTempFile(t) + p := testProvider() + + view, done := testView(t) + c := &ApplyCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + View: view, + ShutdownCh: shutdownCh, + }, + } + + p.StopFn = func() error { + close(cancelled) + return nil + } + + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { + resp.PlannedState = req.ProposedNewState + return + } + + var once sync.Once + p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { + // only cancel once once.Do(func() { shutdownCh <- struct{}{} }) @@ -909,11 +1125,13 @@ func TestApply_shutdown(t *testing.T) { return } - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "ami": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "ami": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -922,10 +1140,11 @@ func TestApply_shutdown(t *testing.T) { args := []string{ "-state", statePath, "-auto-approve", - testFixturePath("apply-shutdown"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 1 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if _, err := os.Stat(statePath); err != nil { @@ -945,6 +1164,12 @@ func TestApply_shutdown(t *testing.T) { } func TestApply_state(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + originalState := states.BuildState(func(s *states.SyncState) { s.SetResourceInstanceCurrent( addrs.Resource{ @@ -965,22 +1190,22 @@ func TestApply_state(t *testing.T) { statePath := testStateFile(t, originalState) p := applyFixtureProvider() - p.PlanResourceChangeResponse = providers.PlanResourceChangeResponse{ + p.PlanResourceChangeResponse = &providers.PlanResourceChangeResponse{ PlannedState: cty.ObjectVal(map[string]cty.Value{ "ami": cty.StringVal("bar"), }), } - p.ApplyResourceChangeResponse = providers.ApplyResourceChangeResponse{ + p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{ NewState: cty.ObjectVal(map[string]cty.Value{ "ami": cty.StringVal("bar"), }), } - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -988,10 +1213,11 @@ func TestApply_state(t *testing.T) { args := []string{ "-state", statePath, "-auto-approve", - testFixturePath("apply"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } // Verify that the provider was called with the existing state @@ -1033,31 +1259,44 @@ func TestApply_state(t *testing.T) { } func TestApply_stateNoExist(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + p := applyFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{ "idontexist.tfstate", - testFixturePath("apply"), } - if code := c.Run(args); code != 1 { - t.Fatalf("bad: \n%s", ui.OutputWriter.String()) + code := c.Run(args) + output := done(t) + if code != 1 { + t.Fatalf("bad: \n%s", output.Stdout()) } } func TestApply_sensitiveOutput(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-sensitive-output"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -1066,40 +1305,49 @@ func TestApply_sensitiveOutput(t *testing.T) { args := []string{ "-state", statePath, "-auto-approve", - testFixturePath("apply-sensitive-output"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: \n%s", ui.OutputWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: \n%s", output.Stdout()) } - output := ui.OutputWriter.String() - if !strings.Contains(output, "notsensitive = \"Hello world\"") { - t.Fatalf("bad: output should contain 'notsensitive' output\n%s", output) + stdout := output.Stdout() + if !strings.Contains(stdout, "notsensitive = \"Hello world\"") { + t.Fatalf("bad: output should contain 'notsensitive' output\n%s", stdout) } - if !strings.Contains(output, "sensitive = ") { - t.Fatalf("bad: output should contain 'sensitive' output\n%s", output) + if !strings.Contains(stdout, "sensitive = ") { + t.Fatalf("bad: output should contain 'sensitive' output\n%s", stdout) } } func TestApply_vars(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-vars"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + statePath := testTempFile(t) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } actual := "" - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "value": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "value": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -1120,10 +1368,11 @@ func TestApply_vars(t *testing.T) { "-auto-approve", "-var", "foo=bar", "-state", statePath, - testFixturePath("apply-vars"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if actual != "bar" { @@ -1132,6 +1381,12 @@ func TestApply_vars(t *testing.T) { } func TestApply_varFile(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-vars"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + varFilePath := testTempFile(t) if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil { t.Fatalf("err: %s", err) @@ -1140,20 +1395,22 @@ func TestApply_varFile(t *testing.T) { statePath := testTempFile(t) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } actual := "" - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "value": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "value": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -1174,10 +1431,11 @@ func TestApply_varFile(t *testing.T) { "-auto-approve", "-var-file", varFilePath, "-state", statePath, - testFixturePath("apply-vars"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if actual != "bar" { @@ -1186,38 +1444,36 @@ func TestApply_varFile(t *testing.T) { } func TestApply_varFileDefault(t *testing.T) { - varFileDir := testTempDir(t) - varFilePath := filepath.Join(varFileDir, "terraform.tfvars") + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-vars"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + varFilePath := filepath.Join(td, "terraform.tfvars") if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil { t.Fatalf("err: %s", err) } statePath := testTempFile(t) - cwd, err := os.Getwd() - if err != nil { - t.Fatalf("err: %s", err) - } - if err := os.Chdir(varFileDir); err != nil { - t.Fatalf("err: %s", err) - } - defer os.Chdir(cwd) - p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } actual := "" - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "value": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "value": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -1237,10 +1493,11 @@ func TestApply_varFileDefault(t *testing.T) { args := []string{ "-auto-approve", "-state", statePath, - testFixturePath("apply-vars"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if actual != "bar" { @@ -1249,38 +1506,36 @@ func TestApply_varFileDefault(t *testing.T) { } func TestApply_varFileDefaultJSON(t *testing.T) { - varFileDir := testTempDir(t) - varFilePath := filepath.Join(varFileDir, "terraform.tfvars.json") + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-vars"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + varFilePath := filepath.Join(td, "terraform.tfvars.json") if err := ioutil.WriteFile(varFilePath, []byte(applyVarFileJSON), 0644); err != nil { t.Fatalf("err: %s", err) } statePath := testTempFile(t) - cwd, err := os.Getwd() - if err != nil { - t.Fatalf("err: %s", err) - } - if err := os.Chdir(varFileDir); err != nil { - t.Fatalf("err: %s", err) - } - defer os.Chdir(cwd) - p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } actual := "" - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "value": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "value": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -1300,10 +1555,11 @@ func TestApply_varFileDefaultJSON(t *testing.T) { args := []string{ "-auto-approve", "-state", statePath, - testFixturePath("apply-vars"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if actual != "bar" { @@ -1312,6 +1568,12 @@ func TestApply_varFileDefaultJSON(t *testing.T) { } func TestApply_backup(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + originalState := states.BuildState(func(s *states.SyncState) { s.SetResourceInstanceCurrent( addrs.Resource{ @@ -1333,17 +1595,17 @@ func TestApply_backup(t *testing.T) { backupPath := testTempFile(t) p := applyFixtureProvider() - p.PlanResourceChangeResponse = providers.PlanResourceChangeResponse{ + p.PlanResourceChangeResponse = &providers.PlanResourceChangeResponse{ PlannedState: cty.ObjectVal(map[string]cty.Value{ "ami": cty.StringVal("bar"), }), } - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -1352,10 +1614,11 @@ func TestApply_backup(t *testing.T) { "-auto-approve", "-state", statePath, "-backup", backupPath, - testFixturePath("apply"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } // Verify a new state exists @@ -1383,21 +1646,27 @@ func TestApply_backup(t *testing.T) { } func TestApply_disableBackup(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + originalState := testState() statePath := testStateFile(t, originalState) p := applyFixtureProvider() - p.PlanResourceChangeResponse = providers.PlanResourceChangeResponse{ + p.PlanResourceChangeResponse = &providers.PlanResourceChangeResponse{ PlannedState: cty.ObjectVal(map[string]cty.Value{ "ami": cty.StringVal("bar"), }), } - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -1406,10 +1675,11 @@ func TestApply_disableBackup(t *testing.T) { "-auto-approve", "-state", statePath, "-backup", "-", - testFixturePath("apply"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } // Verify that the provider was called with the existing state @@ -1456,24 +1726,31 @@ func TestApply_disableBackup(t *testing.T) { // Test that the Terraform env is passed through func TestApply_terraformEnv(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-terraform-env"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + statePath := testTempFile(t) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{ "-auto-approve", "-state", statePath, - testFixturePath("apply-terraform-env"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } expected := strings.TrimSpace(` @@ -1489,17 +1766,19 @@ output = default func TestApply_terraformEnvNonDefault(t *testing.T) { // Create a temporary working directory that is empty td := tempDir(t) - os.MkdirAll(td, 0755) + testCopyDir(t, testFixturePath("apply-terraform-env"), td) defer os.RemoveAll(td) defer testChdir(t, td)() // Create new env { ui := new(cli.MockUi) - newCmd := &WorkspaceNewCommand{} - newCmd.Meta = Meta{Ui: ui} + newCmd := &WorkspaceNewCommand{ + Meta: Meta{ + Ui: ui, + }, + } if code := newCmd.Run([]string{"test"}); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter) } } @@ -1507,28 +1786,31 @@ func TestApply_terraformEnvNonDefault(t *testing.T) { { args := []string{"test"} ui := new(cli.MockUi) - selCmd := &WorkspaceSelectCommand{} - selCmd.Meta = Meta{Ui: ui} + selCmd := &WorkspaceSelectCommand{ + Meta: Meta{ + Ui: ui, + }, + } if code := selCmd.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter) } } p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &ApplyCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{ "-auto-approve", - testFixturePath("apply-terraform-env"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } statePath := filepath.Join("terraform.tfstate.d", "test", "terraform.tfstate") @@ -1541,41 +1823,426 @@ output = test testStateOutput(t, statePath, expected) } -func testHttpServer(t *testing.T) net.Listener { - ln, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { +// Config with multiple resources, targeting apply of a subset +func TestApply_targeted(t *testing.T) { + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-targeted"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + p := testProvider() + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ + "test_instance": { + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + }, + }, + }, + }, + } + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { + return providers.PlanResourceChangeResponse{ + PlannedState: req.ProposedNewState, + } + } + + view, done := testView(t) + c := &ApplyCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + View: view, + }, + } + + args := []string{ + "-auto-approve", + "-target", "test_instance.foo", + "-target", "test_instance.baz", + } + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + + if got, want := output.Stdout(), "3 added, 0 changed, 0 destroyed"; !strings.Contains(got, want) { + t.Fatalf("bad change summary, want %q, got:\n%s", want, got) + } +} + +// Diagnostics for invalid -target flags +func TestApply_targetFlagsDiags(t *testing.T) { + testCases := map[string]string{ + "test_instance.": "Dot must be followed by attribute name.", + "test_instance": "Resource specification must include a resource type and name.", + } + + for target, wantDiag := range testCases { + t.Run(target, func(t *testing.T) { + td := testTempDir(t) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + view, done := testView(t) + c := &ApplyCommand{ + Meta: Meta{ + View: view, + }, + } + + args := []string{ + "-auto-approve", + "-target", target, + } + code := c.Run(args) + output := done(t) + if code != 1 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + + got := output.Stderr() + if !strings.Contains(got, target) { + t.Fatalf("bad error output, want %q, got:\n%s", target, got) + } + if !strings.Contains(got, wantDiag) { + t.Fatalf("bad error output, want %q, got:\n%s", wantDiag, got) + } + }) + } +} + +func TestApply_replace(t *testing.T) { + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-replace"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + originalState := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "a", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"hello"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + }) + statePath := testStateFile(t, originalState) + + p := testProvider() + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ + "test_instance": { + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + }, + }, + }, + }, + } + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { + return providers.PlanResourceChangeResponse{ + PlannedState: req.ProposedNewState, + } + } + createCount := 0 + deleteCount := 0 + p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { + if req.PriorState.IsNull() { + createCount++ + } + if req.PlannedState.IsNull() { + deleteCount++ + } + return providers.ApplyResourceChangeResponse{ + NewState: req.PlannedState, + } + } + + view, done := testView(t) + c := &ApplyCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + View: view, + }, + } + + args := []string{ + "-auto-approve", + "-state", statePath, + "-replace", "test_instance.a", + } + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("wrong exit code %d\n\n%s", code, output.Stderr()) + } + + if got, want := output.Stdout(), "1 added, 0 changed, 1 destroyed"; !strings.Contains(got, want) { + t.Errorf("wrong change summary\ngot output:\n%s\n\nwant substring: %s", got, want) + } + + if got, want := createCount, 1; got != want { + t.Errorf("wrong create count %d; want %d", got, want) + } + if got, want := deleteCount, 1; got != want { + t.Errorf("wrong create count %d; want %d", got, want) + } +} + +func TestApply_pluginPath(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + statePath := testTempFile(t) + + p := applyFixtureProvider() + + view, done := testView(t) + c := &ApplyCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + View: view, + }, + } + + pluginPath := []string{"a", "b", "c"} + + if err := c.Meta.storePluginPath(pluginPath); err != nil { + t.Fatal(err) + } + c.Meta.pluginPath = nil + + args := []string{ + "-state", statePath, + "-auto-approve", + } + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + + if !reflect.DeepEqual(pluginPath, c.Meta.pluginPath) { + t.Fatalf("expected plugin path %#v, got %#v", pluginPath, c.Meta.pluginPath) + } +} + +func TestApply_jsonGoldenReference(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + statePath := testTempFile(t) + + p := applyFixtureProvider() + + view, done := testView(t) + c := &ApplyCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + View: view, + }, + } + + args := []string{ + "-json", + "-state", statePath, + "-auto-approve", + } + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + + if _, err := os.Stat(statePath); err != nil { t.Fatalf("err: %s", err) } - mux := http.NewServeMux() - mux.HandleFunc("/header", testHttpHandlerHeader) + state := testStateRead(t, statePath) + if state == nil { + t.Fatal("state should not be nil") + } + + // Load the golden reference fixture + wantFile, err := os.Open(path.Join(testFixturePath("apply"), "output.jsonlog")) + if err != nil { + t.Fatalf("failed to open output file: %s", err) + } + defer wantFile.Close() + wantBytes, err := ioutil.ReadAll(wantFile) + if err != nil { + t.Fatalf("failed to read output file: %s", err) + } + want := string(wantBytes) + + got := output.Stdout() + + // Split the output and the reference into lines so that we can compare + // messages + got = strings.TrimSuffix(got, "\n") + gotLines := strings.Split(got, "\n") + + want = strings.TrimSuffix(want, "\n") + wantLines := strings.Split(want, "\n") - var server http.Server - server.Handler = mux - go server.Serve(ln) + if len(gotLines) != len(wantLines) { + t.Fatalf("unexpected number of log lines: got %d, want %d", len(gotLines), len(wantLines)) + } + + // Verify that the log starts with a version message + type versionMessage struct { + Level string `json:"@level"` + Message string `json:"@message"` + Type string `json:"type"` + Terraform string `json:"terraform"` + UI string `json:"ui"` + } + var gotVersion versionMessage + if err := json.Unmarshal([]byte(gotLines[0]), &gotVersion); err != nil { + t.Errorf("failed to unmarshal version line: %s\n%s", err, gotLines[0]) + } + wantVersion := versionMessage{ + "info", + fmt.Sprintf("Terraform %s", tfversion.String()), + "version", + tfversion.String(), + views.JSON_UI_VERSION, + } + if !cmp.Equal(wantVersion, gotVersion) { + t.Errorf("unexpected first message:\n%s", cmp.Diff(wantVersion, gotVersion)) + } + + // Compare the rest of the lines against the golden reference + for i := range gotLines[1:] { + index := i + 1 + var gotMap, wantMap map[string]interface{} + if err := json.Unmarshal([]byte(gotLines[index]), &gotMap); err != nil { + t.Errorf("failed to unmarshal got line %d: %s\n%s", index, err, gotLines[i]) + } + if err := json.Unmarshal([]byte(wantLines[index]), &wantMap); err != nil { + t.Errorf("failed to unmarshal want line %d: %s\n%s", index, err, wantLines[i]) + } + + // The timestamp field is the only one that should change, so we drop + // it from the comparison + if _, ok := gotMap["@timestamp"]; !ok { + t.Errorf("missing @timestamp field in log: %s", gotLines[i]) + } + delete(gotMap, "@timestamp") - return ln + if !cmp.Equal(wantMap, gotMap) { + t.Errorf("unexpected log:\n%s", cmp.Diff(wantMap, gotMap)) + } + } } -func testHttpHandlerHeader(w http.ResponseWriter, r *http.Request) { - var url url.URL - url.Scheme = "file" - url.Path = filepath.ToSlash(testFixturePath("init")) +func TestApply_warnings(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + p := testProvider() + p.GetProviderSchemaResponse = applyFixtureSchema() + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { + return providers.PlanResourceChangeResponse{ + PlannedState: req.ProposedNewState, + Diagnostics: tfdiags.Diagnostics{ + tfdiags.SimpleWarning("warning 1"), + tfdiags.SimpleWarning("warning 2"), + }, + } + } + p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { + return providers.ApplyResourceChangeResponse{ + NewState: cty.UnknownAsNull(req.PlannedState), + } + } + + t.Run("full warnings", func(t *testing.T) { + view, done := testView(t) + c := &ApplyCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + View: view, + }, + } + + args := []string{"-auto-approve"} + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + wantWarnings := []string{ + "warning 1", + "warning 2", + } + for _, want := range wantWarnings { + if !strings.Contains(output.Stdout(), want) { + t.Errorf("missing warning %s", want) + } + } + }) - w.Header().Add("X-Terraform-Get", url.String()) - w.WriteHeader(200) + t.Run("compact warnings", func(t *testing.T) { + view, done := testView(t) + c := &ApplyCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + View: view, + }, + } + + code := c.Run([]string{"-auto-approve", "-compact-warnings"}) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + // the output should contain 2 warnings and a message about -compact-warnings + wantWarnings := []string{ + "warning 1", + "warning 2", + "To see the full warning notes, run Terraform without -compact-warnings.", + } + for _, want := range wantWarnings { + if !strings.Contains(output.Stdout(), want) { + t.Errorf("missing warning %s", want) + } + } + }) } // applyFixtureSchema returns a schema suitable for processing the // configuration in testdata/apply . This schema should be // assigned to a mock provider named "test". -func applyFixtureSchema() *terraform.ProviderSchema { - return &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ +func applyFixtureSchema() *providers.GetProviderSchemaResponse { + return &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "ami": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -1584,12 +2251,12 @@ func applyFixtureSchema() *terraform.ProviderSchema { // applyFixtureProvider returns a mock provider that is configured for basic // operation with the configuration in testdata/apply. This mock has -// GetSchemaReturn, PlanResourceChangeFn, and ApplyResourceChangeFn populated, +// GetSchemaResponse, PlanResourceChangeFn, and ApplyResourceChangeFn populated, // with the plan/apply steps just passing through the data determined by // Terraform Core. func applyFixtureProvider() *terraform.MockProvider { p := testProvider() - p.GetSchemaReturn = applyFixtureSchema() + p.GetProviderSchemaResponse = applyFixtureSchema() p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { return providers.PlanResourceChangeResponse{ PlannedState: req.ProposedNewState, @@ -1652,23 +2319,3 @@ foo = "bar" const applyVarFileJSON = ` { "foo": "bar" } ` - -const testApplyDisableBackupStr = ` -ID = bar -Tainted = false -` - -const testApplyDisableBackupStateStr = ` -ID = bar -Tainted = false -` - -const testApplyStateStr = ` -ID = bar -Tainted = false -` - -const testApplyStateDiffStr = ` -ID = bar -Tainted = false -` diff --git a/vendor/github.com/hashicorp/terraform/command/arguments/apply.go b/vendor/github.com/hashicorp/terraform/command/arguments/apply.go new file mode 100644 index 00000000..267c8e53 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/arguments/apply.go @@ -0,0 +1,147 @@ +package arguments + +import ( + "fmt" + + "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/tfdiags" +) + +// Apply represents the command-line arguments for the apply command. +type Apply struct { + // State, Operation, and Vars are the common extended flags + State *State + Operation *Operation + Vars *Vars + + // AutoApprove skips the manual verification step for the apply operation. + AutoApprove bool + + // InputEnabled is used to disable interactive input for unspecified + // variable and backend config values. Default is true. + InputEnabled bool + + // PlanPath contains an optional path to a stored plan file + PlanPath string + + // ViewType specifies which output format to use + ViewType ViewType +} + +// ParseApply processes CLI arguments, returning an Apply value and errors. +// If errors are encountered, an Apply value is still returned representing +// the best effort interpretation of the arguments. +func ParseApply(args []string) (*Apply, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + apply := &Apply{ + State: &State{}, + Operation: &Operation{}, + Vars: &Vars{}, + } + + cmdFlags := extendedFlagSet("apply", apply.State, apply.Operation, apply.Vars) + cmdFlags.BoolVar(&apply.AutoApprove, "auto-approve", false, "auto-approve") + cmdFlags.BoolVar(&apply.InputEnabled, "input", true, "input") + + var json bool + cmdFlags.BoolVar(&json, "json", false, "json") + + if err := cmdFlags.Parse(args); err != nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to parse command-line flags", + err.Error(), + )) + } + + args = cmdFlags.Args() + if len(args) > 0 { + apply.PlanPath = args[0] + args = args[1:] + } + + if len(args) > 0 { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Too many command line arguments", + "Expected at most one positional argument.", + )) + } + + // JSON view currently does not support input, so we disable it here. + if json { + apply.InputEnabled = false + } + + // JSON view cannot confirm apply, so we require either a plan file or + // auto-approve to be specified. We intentionally fail here rather than + // override auto-approve, which would be dangerous. + if json && apply.PlanPath == "" && !apply.AutoApprove { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Plan file or auto-approve required", + "Terraform cannot ask for interactive approval when -json is set. You can either apply a saved plan file, or enable the -auto-approve option.", + )) + } + + diags = diags.Append(apply.Operation.Parse()) + + switch { + case json: + apply.ViewType = ViewJSON + default: + apply.ViewType = ViewHuman + } + + return apply, diags +} + +// ParseApplyDestroy is a special case of ParseApply that deals with the +// "terraform destroy" command, which is effectively an alias for +// "terraform apply -destroy". +func ParseApplyDestroy(args []string) (*Apply, tfdiags.Diagnostics) { + apply, diags := ParseApply(args) + + // So far ParseApply was using the command line options like -destroy + // and -refresh-only to determine the plan mode. For "terraform destroy" + // we expect neither of those arguments to be set, and so the plan mode + // should currently be set to NormalMode, which we'll replace with + // DestroyMode here. If it's already set to something else then that + // suggests incorrect usage. + switch apply.Operation.PlanMode { + case plans.NormalMode: + // This indicates that the user didn't specify any mode options at + // all, which is correct, although we know from the command that + // they actually intended to use DestroyMode here. + apply.Operation.PlanMode = plans.DestroyMode + case plans.DestroyMode: + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Invalid mode option", + "The -destroy option is not valid for \"terraform destroy\", because this command always runs in destroy mode.", + )) + case plans.RefreshOnlyMode: + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Invalid mode option", + "The -refresh-only option is not valid for \"terraform destroy\".", + )) + default: + // This is a non-ideal error message for if we forget to handle a + // newly-handled plan mode in Operation.Parse. Ideally they should all + // have cases above so we can produce better error messages. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Invalid mode option", + fmt.Sprintf("The \"terraform destroy\" command doesn't support %s.", apply.Operation.PlanMode), + )) + } + + // NOTE: It's also invalid to have apply.PlanPath set in this codepath, + // but we don't check that in here because we'll return a different error + // message depending on whether the given path seems to refer to a saved + // plan file or to a configuration directory. The apply command + // implementation itself therefore handles this situation. + + return apply, diags +} diff --git a/vendor/github.com/hashicorp/terraform/command/arguments/apply_test.go b/vendor/github.com/hashicorp/terraform/command/arguments/apply_test.go new file mode 100644 index 00000000..eb23919c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/arguments/apply_test.go @@ -0,0 +1,389 @@ +package arguments + +import ( + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/plans" +) + +func TestParseApply_basicValid(t *testing.T) { + testCases := map[string]struct { + args []string + want *Apply + }{ + "defaults": { + nil, + &Apply{ + AutoApprove: false, + InputEnabled: true, + PlanPath: "", + ViewType: ViewHuman, + State: &State{Lock: true}, + Vars: &Vars{}, + Operation: &Operation{ + PlanMode: plans.NormalMode, + Parallelism: 10, + Refresh: true, + }, + }, + }, + "auto-approve, disabled input, and plan path": { + []string{"-auto-approve", "-input=false", "saved.tfplan"}, + &Apply{ + AutoApprove: true, + InputEnabled: false, + PlanPath: "saved.tfplan", + ViewType: ViewHuman, + State: &State{Lock: true}, + Vars: &Vars{}, + Operation: &Operation{ + PlanMode: plans.NormalMode, + Parallelism: 10, + Refresh: true, + }, + }, + }, + "destroy mode": { + []string{"-destroy"}, + &Apply{ + AutoApprove: false, + InputEnabled: true, + PlanPath: "", + ViewType: ViewHuman, + State: &State{Lock: true}, + Vars: &Vars{}, + Operation: &Operation{ + PlanMode: plans.DestroyMode, + Parallelism: 10, + Refresh: true, + }, + }, + }, + "JSON view disables input": { + []string{"-json", "-auto-approve"}, + &Apply{ + AutoApprove: true, + InputEnabled: false, + PlanPath: "", + ViewType: ViewJSON, + State: &State{Lock: true}, + Vars: &Vars{}, + Operation: &Operation{ + PlanMode: plans.NormalMode, + Parallelism: 10, + Refresh: true, + }, + }, + }, + } + + cmpOpts := cmpopts.IgnoreUnexported(Operation{}, Vars{}, State{}) + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, diags := ParseApply(tc.args) + if len(diags) > 0 { + t.Fatalf("unexpected diags: %v", diags) + } + if diff := cmp.Diff(tc.want, got, cmpOpts); diff != "" { + t.Errorf("unexpected result\n%s", diff) + } + }) + } +} + +func TestParseApply_json(t *testing.T) { + testCases := map[string]struct { + args []string + wantSuccess bool + }{ + "-json": { + []string{"-json"}, + false, + }, + "-json -auto-approve": { + []string{"-json", "-auto-approve"}, + true, + }, + "-json saved.tfplan": { + []string{"-json", "saved.tfplan"}, + true, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, diags := ParseApply(tc.args) + + if tc.wantSuccess { + if len(diags) > 0 { + t.Errorf("unexpected diags: %v", diags) + } + } else { + if got, want := diags.Err().Error(), "Plan file or auto-approve required"; !strings.Contains(got, want) { + t.Errorf("wrong diags\n got: %s\nwant: %s", got, want) + } + } + + if got.ViewType != ViewJSON { + t.Errorf("unexpected view type. got: %#v, want: %#v", got.ViewType, ViewJSON) + } + }) + } +} + +func TestParseApply_invalid(t *testing.T) { + got, diags := ParseApply([]string{"-frob"}) + if len(diags) == 0 { + t.Fatal("expected diags but got none") + } + if got, want := diags.Err().Error(), "flag provided but not defined"; !strings.Contains(got, want) { + t.Fatalf("wrong diags\n got: %s\nwant: %s", got, want) + } + if got.ViewType != ViewHuman { + t.Fatalf("wrong view type, got %#v, want %#v", got.ViewType, ViewHuman) + } +} + +func TestParseApply_tooManyArguments(t *testing.T) { + got, diags := ParseApply([]string{"saved.tfplan", "please"}) + if len(diags) == 0 { + t.Fatal("expected diags but got none") + } + if got, want := diags.Err().Error(), "Too many command line arguments"; !strings.Contains(got, want) { + t.Fatalf("wrong diags\n got: %s\nwant: %s", got, want) + } + if got.ViewType != ViewHuman { + t.Fatalf("wrong view type, got %#v, want %#v", got.ViewType, ViewHuman) + } +} + +func TestParseApply_targets(t *testing.T) { + foobarbaz, _ := addrs.ParseTargetStr("foo_bar.baz") + boop, _ := addrs.ParseTargetStr("module.boop") + testCases := map[string]struct { + args []string + want []addrs.Targetable + wantErr string + }{ + "no targets by default": { + args: nil, + want: nil, + }, + "one target": { + args: []string{"-target=foo_bar.baz"}, + want: []addrs.Targetable{foobarbaz.Subject}, + }, + "two targets": { + args: []string{"-target=foo_bar.baz", "-target", "module.boop"}, + want: []addrs.Targetable{foobarbaz.Subject, boop.Subject}, + }, + "invalid traversal": { + args: []string{"-target=foo."}, + want: nil, + wantErr: "Dot must be followed by attribute name", + }, + "invalid target": { + args: []string{"-target=data[0].foo"}, + want: nil, + wantErr: "A data source name is required", + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, diags := ParseApply(tc.args) + if len(diags) > 0 { + if tc.wantErr == "" { + t.Fatalf("unexpected diags: %v", diags) + } else if got := diags.Err().Error(); !strings.Contains(got, tc.wantErr) { + t.Fatalf("wrong diags\n got: %s\nwant: %s", got, tc.wantErr) + } + } + if !cmp.Equal(got.Operation.Targets, tc.want) { + t.Fatalf("unexpected result\n%s", cmp.Diff(got.Operation.Targets, tc.want)) + } + }) + } +} + +func TestParseApply_replace(t *testing.T) { + foobarbaz, _ := addrs.ParseAbsResourceInstanceStr("foo_bar.baz") + foobarbeep, _ := addrs.ParseAbsResourceInstanceStr("foo_bar.beep") + testCases := map[string]struct { + args []string + want []addrs.AbsResourceInstance + wantErr string + }{ + "no addresses by default": { + args: nil, + want: nil, + }, + "one address": { + args: []string{"-replace=foo_bar.baz"}, + want: []addrs.AbsResourceInstance{foobarbaz}, + }, + "two addresses": { + args: []string{"-replace=foo_bar.baz", "-replace", "foo_bar.beep"}, + want: []addrs.AbsResourceInstance{foobarbaz, foobarbeep}, + }, + "non-resource-instance address": { + args: []string{"-replace=module.boop"}, + want: nil, + wantErr: "A resource instance address is required here.", + }, + "data resource address": { + args: []string{"-replace=data.foo.bar"}, + want: nil, + wantErr: "Only managed resources can be used", + }, + "invalid traversal": { + args: []string{"-replace=foo."}, + want: nil, + wantErr: "Dot must be followed by attribute name", + }, + "invalid address": { + args: []string{"-replace=data[0].foo"}, + want: nil, + wantErr: "A data source name is required", + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, diags := ParseApply(tc.args) + if len(diags) > 0 { + if tc.wantErr == "" { + t.Fatalf("unexpected diags: %v", diags) + } else if got := diags.Err().Error(); !strings.Contains(got, tc.wantErr) { + t.Fatalf("wrong diags\n got: %s\nwant: %s", got, tc.wantErr) + } + } + if !cmp.Equal(got.Operation.ForceReplace, tc.want) { + t.Fatalf("unexpected result\n%s", cmp.Diff(got.Operation.Targets, tc.want)) + } + }) + } +} + +func TestParseApply_vars(t *testing.T) { + testCases := map[string]struct { + args []string + want []FlagNameValue + }{ + "no var flags by default": { + args: nil, + want: nil, + }, + "one var": { + args: []string{"-var", "foo=bar"}, + want: []FlagNameValue{ + {Name: "-var", Value: "foo=bar"}, + }, + }, + "one var-file": { + args: []string{"-var-file", "cool.tfvars"}, + want: []FlagNameValue{ + {Name: "-var-file", Value: "cool.tfvars"}, + }, + }, + "ordering preserved": { + args: []string{ + "-var", "foo=bar", + "-var-file", "cool.tfvars", + "-var", "boop=beep", + }, + want: []FlagNameValue{ + {Name: "-var", Value: "foo=bar"}, + {Name: "-var-file", Value: "cool.tfvars"}, + {Name: "-var", Value: "boop=beep"}, + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, diags := ParseApply(tc.args) + if len(diags) > 0 { + t.Fatalf("unexpected diags: %v", diags) + } + if vars := got.Vars.All(); !cmp.Equal(vars, tc.want) { + t.Fatalf("unexpected result\n%s", cmp.Diff(vars, tc.want)) + } + if got, want := got.Vars.Empty(), len(tc.want) == 0; got != want { + t.Fatalf("expected Empty() to return %t, but was %t", want, got) + } + }) + } +} + +func TestParseApplyDestroy_basicValid(t *testing.T) { + testCases := map[string]struct { + args []string + want *Apply + }{ + "defaults": { + nil, + &Apply{ + AutoApprove: false, + InputEnabled: true, + ViewType: ViewHuman, + State: &State{Lock: true}, + Vars: &Vars{}, + Operation: &Operation{ + PlanMode: plans.DestroyMode, + Parallelism: 10, + Refresh: true, + }, + }, + }, + "auto-approve and disabled input": { + []string{"-auto-approve", "-input=false"}, + &Apply{ + AutoApprove: true, + InputEnabled: false, + ViewType: ViewHuman, + State: &State{Lock: true}, + Vars: &Vars{}, + Operation: &Operation{ + PlanMode: plans.DestroyMode, + Parallelism: 10, + Refresh: true, + }, + }, + }, + } + + cmpOpts := cmpopts.IgnoreUnexported(Operation{}, Vars{}, State{}) + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, diags := ParseApplyDestroy(tc.args) + if len(diags) > 0 { + t.Fatalf("unexpected diags: %v", diags) + } + if diff := cmp.Diff(tc.want, got, cmpOpts); diff != "" { + t.Errorf("unexpected result\n%s", diff) + } + }) + } +} + +func TestParseApplyDestroy_invalid(t *testing.T) { + t.Run("explicit destroy mode", func(t *testing.T) { + got, diags := ParseApplyDestroy([]string{"-destroy"}) + if len(diags) == 0 { + t.Fatal("expected diags but got none") + } + if got, want := diags.Err().Error(), "Invalid mode option:"; !strings.Contains(got, want) { + t.Fatalf("wrong diags\n got: %s\nwant: %s", got, want) + } + if got.ViewType != ViewHuman { + t.Fatalf("wrong view type, got %#v, want %#v", got.ViewType, ViewHuman) + } + }) +} diff --git a/vendor/github.com/hashicorp/terraform/command/arguments/default.go b/vendor/github.com/hashicorp/terraform/command/arguments/default.go new file mode 100644 index 00000000..4b7bb402 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/arguments/default.go @@ -0,0 +1,16 @@ +package arguments + +import ( + "flag" + "io/ioutil" +) + +// defaultFlagSet creates a FlagSet with the common settings to override +// the flag package's noisy defaults. +func defaultFlagSet(name string) *flag.FlagSet { + f := flag.NewFlagSet(name, flag.ContinueOnError) + f.SetOutput(ioutil.Discard) + f.Usage = func() {} + + return f +} diff --git a/vendor/github.com/hashicorp/terraform/command/arguments/extended.go b/vendor/github.com/hashicorp/terraform/command/arguments/extended.go new file mode 100644 index 00000000..af0b9dcb --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/arguments/extended.go @@ -0,0 +1,225 @@ +package arguments + +import ( + "flag" + "fmt" + "time" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclsyntax" + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/tfdiags" +) + +// DefaultParallelism is the limit Terraform places on total parallel +// operations as it walks the dependency graph. +const DefaultParallelism = 10 + +// State describes arguments which are used to define how Terraform interacts +// with state. +type State struct { + // Lock controls whether or not the state manager is used to lock state + // during operations. + Lock bool + + // LockTimeout allows setting a time limit on acquiring the state lock. + // The default is 0, meaning no limit. + LockTimeout time.Duration + + // StatePath specifies a non-default location for the state file. The + // default value is blank, which is interpeted as "terraform.tfstate". + StatePath string + + // StateOutPath specifies a different path to write the final state file. + // The default value is blank, which results in state being written back to + // StatePath. + StateOutPath string + + // BackupPath specifies the path where a backup copy of the state file will + // be stored before the new state is written. The default value is blank, + // which is interpreted as StateOutPath + + // ".backup". + BackupPath string +} + +// Operation describes arguments which are used to configure how a Terraform +// operation such as a plan or apply executes. +type Operation struct { + // PlanMode selects one of the mutually-exclusive planning modes that + // decides the overall goal of a plan operation. This field is relevant + // only for an operation that produces a plan. + PlanMode plans.Mode + + // Parallelism is the limit Terraform places on total parallel operations + // as it walks the dependency graph. + Parallelism int + + // Refresh controls whether or not the operation should refresh existing + // state before proceeding. Default is true. + Refresh bool + + // Targets allow limiting an operation to a set of resource addresses and + // their dependencies. + Targets []addrs.Targetable + + // ForceReplace addresses cause Terraform to force a particular set of + // resource instances to generate "replace" actions in any plan where they + // would normally have generated "no-op" or "update" actions. + // + // This is currently limited to specific instances because typical uses + // of replace are associated with only specific remote objects that the + // user has somehow learned to be malfunctioning, in which case it + // would be unusual and potentially dangerous to replace everything under + // a module all at once. We could potentially loosen this later if we + // learn a use-case for broader matching. + ForceReplace []addrs.AbsResourceInstance + + // These private fields are used only temporarily during decoding. Use + // method Parse to populate the exported fields from these, validating + // the raw values in the process. + targetsRaw []string + forceReplaceRaw []string + destroyRaw bool +} + +// Parse must be called on Operation after initial flag parse. This processes +// the raw target flags into addrs.Targetable values, returning diagnostics if +// invalid. +func (o *Operation) Parse() tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + o.Targets = nil + + for _, tr := range o.targetsRaw { + traversal, syntaxDiags := hclsyntax.ParseTraversalAbs([]byte(tr), "", hcl.Pos{Line: 1, Column: 1}) + if syntaxDiags.HasErrors() { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + fmt.Sprintf("Invalid target %q", tr), + syntaxDiags[0].Detail, + )) + continue + } + + target, targetDiags := addrs.ParseTarget(traversal) + if targetDiags.HasErrors() { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + fmt.Sprintf("Invalid target %q", tr), + targetDiags[0].Description().Detail, + )) + continue + } + + o.Targets = append(o.Targets, target.Subject) + } + + for _, raw := range o.forceReplaceRaw { + traversal, syntaxDiags := hclsyntax.ParseTraversalAbs([]byte(raw), "", hcl.Pos{Line: 1, Column: 1}) + if syntaxDiags.HasErrors() { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + fmt.Sprintf("Invalid force-replace address %q", raw), + syntaxDiags[0].Detail, + )) + continue + } + + addr, addrDiags := addrs.ParseAbsResourceInstance(traversal) + if addrDiags.HasErrors() { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + fmt.Sprintf("Invalid force-replace address %q", raw), + addrDiags[0].Description().Detail, + )) + continue + } + + if addr.Resource.Resource.Mode != addrs.ManagedResourceMode { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + fmt.Sprintf("Invalid force-replace address %q", raw), + "Only managed resources can be used with the -replace=... option.", + )) + continue + } + + o.ForceReplace = append(o.ForceReplace, addr) + } + + // If you add a new possible value for o.PlanMode here, consider also + // adding a specialized error message for it in ParseApplyDestroy. + switch { + case o.destroyRaw: + o.PlanMode = plans.DestroyMode + default: + o.PlanMode = plans.NormalMode + } + + return diags +} + +// Vars describes arguments which specify non-default variable values. This +// interfce is unfortunately obscure, because the order of the CLI arguments +// determines the final value of the gathered variables. In future it might be +// desirable for the arguments package to handle the gathering of variables +// directly, returning a map of variable values. +type Vars struct { + vars *flagNameValueSlice + varFiles *flagNameValueSlice +} + +func (v *Vars) All() []FlagNameValue { + if v.vars == nil { + return nil + } + return v.vars.AllItems() +} + +func (v *Vars) Empty() bool { + if v.vars == nil { + return true + } + return v.vars.Empty() +} + +// extendedFlagSet creates a FlagSet with common backend, operation, and vars +// flags used in many commands. Target structs for each subset of flags must be +// provided in order to support those flags. +func extendedFlagSet(name string, state *State, operation *Operation, vars *Vars) *flag.FlagSet { + f := defaultFlagSet(name) + + if state == nil && operation == nil && vars == nil { + panic("use defaultFlagSet") + } + + if state != nil { + f.BoolVar(&state.Lock, "lock", true, "lock") + f.DurationVar(&state.LockTimeout, "lock-timeout", 0, "lock-timeout") + f.StringVar(&state.StatePath, "state", "", "state-path") + f.StringVar(&state.StateOutPath, "state-out", "", "state-path") + f.StringVar(&state.BackupPath, "backup", "", "backup-path") + } + + if operation != nil { + f.IntVar(&operation.Parallelism, "parallelism", DefaultParallelism, "parallelism") + f.BoolVar(&operation.Refresh, "refresh", true, "refresh") + f.BoolVar(&operation.destroyRaw, "destroy", false, "destroy") + f.Var((*flagStringSlice)(&operation.targetsRaw), "target", "target") + f.Var((*flagStringSlice)(&operation.forceReplaceRaw), "replace", "replace") + } + + // Gather all -var and -var-file arguments into one heterogenous structure + // to preserve the overall order. + if vars != nil { + varsFlags := newFlagNameValueSlice("-var") + varFilesFlags := varsFlags.Alias("-var-file") + vars.vars = &varsFlags + vars.varFiles = &varFilesFlags + f.Var(vars.vars, "var", "var") + f.Var(vars.varFiles, "var-file", "var-file") + } + + return f +} diff --git a/vendor/github.com/hashicorp/terraform/command/arguments/flags.go b/vendor/github.com/hashicorp/terraform/command/arguments/flags.go new file mode 100644 index 00000000..d36486d9 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/arguments/flags.go @@ -0,0 +1,86 @@ +package arguments + +import ( + "flag" + "fmt" +) + +// flagStringSlice is a flag.Value implementation which allows collecting +// multiple instances of a single flag into a slice. This is used for flags +// such as -target=aws_instance.foo and -var x=y. +type flagStringSlice []string + +var _ flag.Value = (*flagStringSlice)(nil) + +func (v *flagStringSlice) String() string { + return "" +} +func (v *flagStringSlice) Set(raw string) error { + *v = append(*v, raw) + + return nil +} + +// flagNameValueSlice is a flag.Value implementation that appends raw flag +// names and values to a slice. This is used to collect a sequence of flags +// with possibly different names, preserving the overall order. +// +// FIXME: this is a copy of rawFlags from command/meta_config.go, with the +// eventual aim of replacing it altogether by gathering variables in the +// arguments package. +type flagNameValueSlice struct { + flagName string + items *[]FlagNameValue +} + +var _ flag.Value = flagNameValueSlice{} + +func newFlagNameValueSlice(flagName string) flagNameValueSlice { + var items []FlagNameValue + return flagNameValueSlice{ + flagName: flagName, + items: &items, + } +} + +func (f flagNameValueSlice) Empty() bool { + if f.items == nil { + return true + } + return len(*f.items) == 0 +} + +func (f flagNameValueSlice) AllItems() []FlagNameValue { + if f.items == nil { + return nil + } + return *f.items +} + +func (f flagNameValueSlice) Alias(flagName string) flagNameValueSlice { + return flagNameValueSlice{ + flagName: flagName, + items: f.items, + } +} + +func (f flagNameValueSlice) String() string { + return "" +} + +func (f flagNameValueSlice) Set(str string) error { + *f.items = append(*f.items, FlagNameValue{ + Name: f.flagName, + Value: str, + }) + return nil +} + +type FlagNameValue struct { + Name string + Value string +} + +func (f FlagNameValue) String() string { + return fmt.Sprintf("%s=%q", f.Name, f.Value) +} diff --git a/vendor/github.com/hashicorp/terraform/command/arguments/output.go b/vendor/github.com/hashicorp/terraform/command/arguments/output.go new file mode 100644 index 00000000..f77c283c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/arguments/output.go @@ -0,0 +1,88 @@ +package arguments + +import ( + "github.com/hashicorp/terraform/tfdiags" +) + +// Output represents the command-line arguments for the output command. +type Output struct { + // Name identifies which root module output to show. If empty, show all + // outputs. + Name string + + // StatePath is an optional path to a state file, from which outputs will + // be loaded. + StatePath string + + // ViewType specifies which output format to use: human, JSON, or "raw". + ViewType ViewType +} + +// ParseOutput processes CLI arguments, returning an Output value and errors. +// If errors are encountered, an Output value is still returned representing +// the best effort interpretation of the arguments. +func ParseOutput(args []string) (*Output, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + output := &Output{} + + var jsonOutput, rawOutput bool + var statePath string + cmdFlags := defaultFlagSet("output") + cmdFlags.BoolVar(&jsonOutput, "json", false, "json") + cmdFlags.BoolVar(&rawOutput, "raw", false, "raw") + cmdFlags.StringVar(&statePath, "state", "", "path") + + if err := cmdFlags.Parse(args); err != nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to parse command-line flags", + err.Error(), + )) + } + + args = cmdFlags.Args() + if len(args) > 1 { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Unexpected argument", + "The output command expects exactly one argument with the name of an output variable or no arguments to show all outputs.", + )) + } + + if jsonOutput && rawOutput { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Invalid output format", + "The -raw and -json options are mutually-exclusive.", + )) + + // Since the desired output format is unknowable, fall back to default + jsonOutput = false + rawOutput = false + } + + output.StatePath = statePath + + if len(args) > 0 { + output.Name = args[0] + } + + if rawOutput && output.Name == "" { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Output name required", + "You must give the name of a single output value when using the -raw option.", + )) + } + + switch { + case jsonOutput: + output.ViewType = ViewJSON + case rawOutput: + output.ViewType = ViewRaw + default: + output.ViewType = ViewHuman + } + + return output, diags +} diff --git a/vendor/github.com/hashicorp/terraform/command/arguments/output_test.go b/vendor/github.com/hashicorp/terraform/command/arguments/output_test.go new file mode 100644 index 00000000..304a156b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/arguments/output_test.go @@ -0,0 +1,142 @@ +package arguments + +import ( + "reflect" + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/hashicorp/terraform/tfdiags" +) + +func TestParseOutput_valid(t *testing.T) { + testCases := map[string]struct { + args []string + want *Output + }{ + "defaults": { + nil, + &Output{ + Name: "", + ViewType: ViewHuman, + StatePath: "", + }, + }, + "json": { + []string{"-json"}, + &Output{ + Name: "", + ViewType: ViewJSON, + StatePath: "", + }, + }, + "raw": { + []string{"-raw", "foo"}, + &Output{ + Name: "foo", + ViewType: ViewRaw, + StatePath: "", + }, + }, + "state": { + []string{"-state=foobar.tfstate", "-raw", "foo"}, + &Output{ + Name: "foo", + ViewType: ViewRaw, + StatePath: "foobar.tfstate", + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, diags := ParseOutput(tc.args) + if len(diags) > 0 { + t.Fatalf("unexpected diags: %v", diags) + } + if *got != *tc.want { + t.Fatalf("unexpected result\n got: %#v\nwant: %#v", got, tc.want) + } + }) + } +} + +func TestParseOutput_invalid(t *testing.T) { + testCases := map[string]struct { + args []string + want *Output + wantDiags tfdiags.Diagnostics + }{ + "unknown flag": { + []string{"-boop"}, + &Output{ + Name: "", + ViewType: ViewHuman, + StatePath: "", + }, + tfdiags.Diagnostics{ + tfdiags.Sourceless( + tfdiags.Error, + "Failed to parse command-line flags", + "flag provided but not defined: -boop", + ), + }, + }, + "json and raw specified": { + []string{"-json", "-raw"}, + &Output{ + Name: "", + ViewType: ViewHuman, + StatePath: "", + }, + tfdiags.Diagnostics{ + tfdiags.Sourceless( + tfdiags.Error, + "Invalid output format", + "The -raw and -json options are mutually-exclusive.", + ), + }, + }, + "raw with no name": { + []string{"-raw"}, + &Output{ + Name: "", + ViewType: ViewRaw, + StatePath: "", + }, + tfdiags.Diagnostics{ + tfdiags.Sourceless( + tfdiags.Error, + "Output name required", + "You must give the name of a single output value when using the -raw option.", + ), + }, + }, + "too many arguments": { + []string{"-raw", "-state=foo.tfstate", "bar", "baz"}, + &Output{ + Name: "bar", + ViewType: ViewRaw, + StatePath: "foo.tfstate", + }, + tfdiags.Diagnostics{ + tfdiags.Sourceless( + tfdiags.Error, + "Unexpected argument", + "The output command expects exactly one argument with the name of an output variable or no arguments to show all outputs.", + ), + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, gotDiags := ParseOutput(tc.args) + if *got != *tc.want { + t.Fatalf("unexpected result\n got: %#v\nwant: %#v", got, tc.want) + } + if !reflect.DeepEqual(gotDiags, tc.wantDiags) { + t.Errorf("wrong result\ngot: %s\nwant: %s", spew.Sdump(gotDiags), spew.Sdump(tc.wantDiags)) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/arguments/plan.go b/vendor/github.com/hashicorp/terraform/command/arguments/plan.go new file mode 100644 index 00000000..0a937594 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/arguments/plan.go @@ -0,0 +1,81 @@ +package arguments + +import ( + "github.com/hashicorp/terraform/tfdiags" +) + +// Plan represents the command-line arguments for the plan command. +type Plan struct { + // State, Operation, and Vars are the common extended flags + State *State + Operation *Operation + Vars *Vars + + // DetailedExitCode enables different exit codes for error, success with + // changes, and success with no changes. + DetailedExitCode bool + + // InputEnabled is used to disable interactive input for unspecified + // variable and backend config values. Default is true. + InputEnabled bool + + // OutPath contains an optional path to store the plan file + OutPath string + + // ViewType specifies which output format to use + ViewType ViewType +} + +// ParsePlan processes CLI arguments, returning a Plan value and errors. +// If errors are encountered, a Plan value is still returned representing +// the best effort interpretation of the arguments. +func ParsePlan(args []string) (*Plan, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + plan := &Plan{ + State: &State{}, + Operation: &Operation{}, + Vars: &Vars{}, + } + + cmdFlags := extendedFlagSet("plan", plan.State, plan.Operation, plan.Vars) + cmdFlags.BoolVar(&plan.DetailedExitCode, "detailed-exitcode", false, "detailed-exitcode") + cmdFlags.BoolVar(&plan.InputEnabled, "input", true, "input") + cmdFlags.StringVar(&plan.OutPath, "out", "", "out") + + var json bool + cmdFlags.BoolVar(&json, "json", false, "json") + + if err := cmdFlags.Parse(args); err != nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to parse command-line flags", + err.Error(), + )) + } + + args = cmdFlags.Args() + + if len(args) > 0 { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Too many command line arguments", + "To specify a working directory for the plan, use the global -chdir flag.", + )) + } + + diags = diags.Append(plan.Operation.Parse()) + + // JSON view currently does not support input, so we disable it here + if json { + plan.InputEnabled = false + } + + switch { + case json: + plan.ViewType = ViewJSON + default: + plan.ViewType = ViewHuman + } + + return plan, diags +} diff --git a/vendor/github.com/hashicorp/terraform/command/arguments/plan_test.go b/vendor/github.com/hashicorp/terraform/command/arguments/plan_test.go new file mode 100644 index 00000000..14c72e9f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/arguments/plan_test.go @@ -0,0 +1,207 @@ +package arguments + +import ( + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/plans" +) + +func TestParsePlan_basicValid(t *testing.T) { + testCases := map[string]struct { + args []string + want *Plan + }{ + "defaults": { + nil, + &Plan{ + DetailedExitCode: false, + InputEnabled: true, + OutPath: "", + ViewType: ViewHuman, + State: &State{Lock: true}, + Vars: &Vars{}, + Operation: &Operation{ + PlanMode: plans.NormalMode, + Parallelism: 10, + Refresh: true, + }, + }, + }, + "setting all options": { + []string{"-destroy", "-detailed-exitcode", "-input=false", "-out=saved.tfplan"}, + &Plan{ + DetailedExitCode: true, + InputEnabled: false, + OutPath: "saved.tfplan", + ViewType: ViewHuman, + State: &State{Lock: true}, + Vars: &Vars{}, + Operation: &Operation{ + PlanMode: plans.DestroyMode, + Parallelism: 10, + Refresh: true, + }, + }, + }, + "JSON view disables input": { + []string{"-json"}, + &Plan{ + DetailedExitCode: false, + InputEnabled: false, + OutPath: "", + ViewType: ViewJSON, + State: &State{Lock: true}, + Vars: &Vars{}, + Operation: &Operation{ + PlanMode: plans.NormalMode, + Parallelism: 10, + Refresh: true, + }, + }, + }, + } + + cmpOpts := cmpopts.IgnoreUnexported(Operation{}, Vars{}, State{}) + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, diags := ParsePlan(tc.args) + if len(diags) > 0 { + t.Fatalf("unexpected diags: %v", diags) + } + if diff := cmp.Diff(tc.want, got, cmpOpts); diff != "" { + t.Errorf("unexpected result\n%s", diff) + } + }) + } +} + +func TestParsePlan_invalid(t *testing.T) { + got, diags := ParsePlan([]string{"-frob"}) + if len(diags) == 0 { + t.Fatal("expected diags but got none") + } + if got, want := diags.Err().Error(), "flag provided but not defined"; !strings.Contains(got, want) { + t.Fatalf("wrong diags\n got: %s\nwant: %s", got, want) + } + if got.ViewType != ViewHuman { + t.Fatalf("wrong view type, got %#v, want %#v", got.ViewType, ViewHuman) + } +} + +func TestParsePlan_tooManyArguments(t *testing.T) { + got, diags := ParsePlan([]string{"saved.tfplan"}) + if len(diags) == 0 { + t.Fatal("expected diags but got none") + } + if got, want := diags.Err().Error(), "Too many command line arguments"; !strings.Contains(got, want) { + t.Fatalf("wrong diags\n got: %s\nwant: %s", got, want) + } + if got.ViewType != ViewHuman { + t.Fatalf("wrong view type, got %#v, want %#v", got.ViewType, ViewHuman) + } +} + +func TestParsePlan_targets(t *testing.T) { + foobarbaz, _ := addrs.ParseTargetStr("foo_bar.baz") + boop, _ := addrs.ParseTargetStr("module.boop") + testCases := map[string]struct { + args []string + want []addrs.Targetable + wantErr string + }{ + "no targets by default": { + args: nil, + want: nil, + }, + "one target": { + args: []string{"-target=foo_bar.baz"}, + want: []addrs.Targetable{foobarbaz.Subject}, + }, + "two targets": { + args: []string{"-target=foo_bar.baz", "-target", "module.boop"}, + want: []addrs.Targetable{foobarbaz.Subject, boop.Subject}, + }, + "invalid traversal": { + args: []string{"-target=foo."}, + want: nil, + wantErr: "Dot must be followed by attribute name", + }, + "invalid target": { + args: []string{"-target=data[0].foo"}, + want: nil, + wantErr: "A data source name is required", + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, diags := ParsePlan(tc.args) + if len(diags) > 0 { + if tc.wantErr == "" { + t.Fatalf("unexpected diags: %v", diags) + } else if got := diags.Err().Error(); !strings.Contains(got, tc.wantErr) { + t.Fatalf("wrong diags\n got: %s\nwant: %s", got, tc.wantErr) + } + } + if !cmp.Equal(got.Operation.Targets, tc.want) { + t.Fatalf("unexpected result\n%s", cmp.Diff(got.Operation.Targets, tc.want)) + } + }) + } +} + +func TestParsePlan_vars(t *testing.T) { + testCases := map[string]struct { + args []string + want []FlagNameValue + }{ + "no var flags by default": { + args: nil, + want: nil, + }, + "one var": { + args: []string{"-var", "foo=bar"}, + want: []FlagNameValue{ + {Name: "-var", Value: "foo=bar"}, + }, + }, + "one var-file": { + args: []string{"-var-file", "cool.tfvars"}, + want: []FlagNameValue{ + {Name: "-var-file", Value: "cool.tfvars"}, + }, + }, + "ordering preserved": { + args: []string{ + "-var", "foo=bar", + "-var-file", "cool.tfvars", + "-var", "boop=beep", + }, + want: []FlagNameValue{ + {Name: "-var", Value: "foo=bar"}, + {Name: "-var-file", Value: "cool.tfvars"}, + {Name: "-var", Value: "boop=beep"}, + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, diags := ParsePlan(tc.args) + if len(diags) > 0 { + t.Fatalf("unexpected diags: %v", diags) + } + if vars := got.Vars.All(); !cmp.Equal(vars, tc.want) { + t.Fatalf("unexpected result\n%s", cmp.Diff(vars, tc.want)) + } + if got, want := got.Vars.Empty(), len(tc.want) == 0; got != want { + t.Fatalf("expected Empty() to return %t, but was %t", want, got) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/arguments/refresh.go b/vendor/github.com/hashicorp/terraform/command/arguments/refresh.go new file mode 100644 index 00000000..0e35483a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/arguments/refresh.go @@ -0,0 +1,71 @@ +package arguments + +import ( + "github.com/hashicorp/terraform/tfdiags" +) + +// Refresh represents the command-line arguments for the apply command. +type Refresh struct { + // State, Operation, and Vars are the common extended flags + State *State + Operation *Operation + Vars *Vars + + // InputEnabled is used to disable interactive input for unspecified + // variable and backend config values. Default is true. + InputEnabled bool + + // ViewType specifies which output format to use + ViewType ViewType +} + +// ParseRefresh processes CLI arguments, returning a Refresh value and errors. +// If errors are encountered, a Refresh value is still returned representing +// the best effort interpretation of the arguments. +func ParseRefresh(args []string) (*Refresh, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + refresh := &Refresh{ + State: &State{}, + Operation: &Operation{}, + Vars: &Vars{}, + } + + cmdFlags := extendedFlagSet("refresh", refresh.State, refresh.Operation, refresh.Vars) + cmdFlags.BoolVar(&refresh.InputEnabled, "input", true, "input") + + var json bool + cmdFlags.BoolVar(&json, "json", false, "json") + + if err := cmdFlags.Parse(args); err != nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to parse command-line flags", + err.Error(), + )) + } + + args = cmdFlags.Args() + if len(args) > 0 { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Too many command line arguments", + "Expected at most one positional argument.", + )) + } + + diags = diags.Append(refresh.Operation.Parse()) + + // JSON view currently does not support input, so we disable it here + if json { + refresh.InputEnabled = false + } + + switch { + case json: + refresh.ViewType = ViewJSON + default: + refresh.ViewType = ViewHuman + } + + return refresh, diags +} diff --git a/vendor/github.com/hashicorp/terraform/command/arguments/refresh_test.go b/vendor/github.com/hashicorp/terraform/command/arguments/refresh_test.go new file mode 100644 index 00000000..6988b77f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/arguments/refresh_test.go @@ -0,0 +1,180 @@ +package arguments + +import ( + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform/addrs" +) + +func TestParseRefresh_basicValid(t *testing.T) { + testCases := map[string]struct { + args []string + want *Refresh + }{ + "defaults": { + nil, + &Refresh{ + InputEnabled: true, + ViewType: ViewHuman, + }, + }, + "input=false": { + []string{"-input=false"}, + &Refresh{ + InputEnabled: false, + ViewType: ViewHuman, + }, + }, + "JSON view disables input": { + []string{"-json"}, + &Refresh{ + InputEnabled: false, + ViewType: ViewJSON, + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, diags := ParseRefresh(tc.args) + if len(diags) > 0 { + t.Fatalf("unexpected diags: %v", diags) + } + // Ignore the extended arguments for simplicity + got.State = nil + got.Operation = nil + got.Vars = nil + if *got != *tc.want { + t.Fatalf("unexpected result\n got: %#v\nwant: %#v", got, tc.want) + } + }) + } +} + +func TestParseRefresh_invalid(t *testing.T) { + got, diags := ParseRefresh([]string{"-frob"}) + if len(diags) == 0 { + t.Fatal("expected diags but got none") + } + if got, want := diags.Err().Error(), "flag provided but not defined"; !strings.Contains(got, want) { + t.Fatalf("wrong diags\n got: %s\nwant: %s", got, want) + } + if got.ViewType != ViewHuman { + t.Fatalf("wrong view type, got %#v, want %#v", got.ViewType, ViewHuman) + } +} + +func TestParseRefresh_tooManyArguments(t *testing.T) { + got, diags := ParseRefresh([]string{"saved.tfplan"}) + if len(diags) == 0 { + t.Fatal("expected diags but got none") + } + if got, want := diags.Err().Error(), "Too many command line arguments"; !strings.Contains(got, want) { + t.Fatalf("wrong diags\n got: %s\nwant: %s", got, want) + } + if got.ViewType != ViewHuman { + t.Fatalf("wrong view type, got %#v, want %#v", got.ViewType, ViewHuman) + } +} + +func TestParseRefresh_targets(t *testing.T) { + foobarbaz, _ := addrs.ParseTargetStr("foo_bar.baz") + boop, _ := addrs.ParseTargetStr("module.boop") + testCases := map[string]struct { + args []string + want []addrs.Targetable + wantErr string + }{ + "no targets by default": { + args: nil, + want: nil, + }, + "one target": { + args: []string{"-target=foo_bar.baz"}, + want: []addrs.Targetable{foobarbaz.Subject}, + }, + "two targets": { + args: []string{"-target=foo_bar.baz", "-target", "module.boop"}, + want: []addrs.Targetable{foobarbaz.Subject, boop.Subject}, + }, + "invalid traversal": { + args: []string{"-target=foo."}, + want: nil, + wantErr: "Dot must be followed by attribute name", + }, + "invalid target": { + args: []string{"-target=data[0].foo"}, + want: nil, + wantErr: "A data source name is required", + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, diags := ParseRefresh(tc.args) + if len(diags) > 0 { + if tc.wantErr == "" { + t.Fatalf("unexpected diags: %v", diags) + } else if got := diags.Err().Error(); !strings.Contains(got, tc.wantErr) { + t.Fatalf("wrong diags\n got: %s\nwant: %s", got, tc.wantErr) + } + } + if !cmp.Equal(got.Operation.Targets, tc.want) { + t.Fatalf("unexpected result\n%s", cmp.Diff(got.Operation.Targets, tc.want)) + } + }) + } +} + +func TestParseRefresh_vars(t *testing.T) { + testCases := map[string]struct { + args []string + want []FlagNameValue + }{ + "no var flags by default": { + args: nil, + want: nil, + }, + "one var": { + args: []string{"-var", "foo=bar"}, + want: []FlagNameValue{ + {Name: "-var", Value: "foo=bar"}, + }, + }, + "one var-file": { + args: []string{"-var-file", "cool.tfvars"}, + want: []FlagNameValue{ + {Name: "-var-file", Value: "cool.tfvars"}, + }, + }, + "ordering preserved": { + args: []string{ + "-var", "foo=bar", + "-var-file", "cool.tfvars", + "-var", "boop=beep", + }, + want: []FlagNameValue{ + {Name: "-var", Value: "foo=bar"}, + {Name: "-var-file", Value: "cool.tfvars"}, + {Name: "-var", Value: "boop=beep"}, + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, diags := ParseRefresh(tc.args) + if len(diags) > 0 { + t.Fatalf("unexpected diags: %v", diags) + } + if vars := got.Vars.All(); !cmp.Equal(vars, tc.want) { + t.Fatalf("unexpected result\n%s", cmp.Diff(vars, tc.want)) + } + if got, want := got.Vars.Empty(), len(tc.want) == 0; got != want { + t.Fatalf("expected Empty() to return %t, but was %t", want, got) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/arguments/test.go b/vendor/github.com/hashicorp/terraform/command/arguments/test.go new file mode 100644 index 00000000..c49759a8 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/arguments/test.go @@ -0,0 +1,63 @@ +package arguments + +import ( + "flag" + "io/ioutil" + + "github.com/hashicorp/terraform/tfdiags" +) + +// Test represents the command line arguments for the "terraform test" command. +type Test struct { + Output TestOutput +} + +// TestOutput represents a subset of the arguments for "terraform test" +// related to how it presents its results. That is, it's the arguments that +// are relevant to the command's view rather than its controller. +type TestOutput struct { + // If not an empty string, JUnitXMLFile gives a filename where JUnit-style + // XML test result output should be written, in addition to the normal + // output printed to the standard output and error streams. + // (The typical usage pattern for tools that can consume this file format + // is to configure them to look for a separate test result file on disk + // after running the tests.) + JUnitXMLFile string +} + +// ParseTest interprets a slice of raw command line arguments into a +// Test value. +func ParseTest(args []string) (Test, tfdiags.Diagnostics) { + var ret Test + var diags tfdiags.Diagnostics + + // NOTE: ParseTest should still return at least a partial + // Test even on error, containing enough information for the + // command to report error diagnostics in a suitable way. + + f := flag.NewFlagSet("test", flag.ContinueOnError) + f.SetOutput(ioutil.Discard) + f.Usage = func() {} + f.StringVar(&ret.Output.JUnitXMLFile, "junit-xml", "", "Write a JUnit XML file describing the results") + + err := f.Parse(args) + if err != nil { + diags = diags.Append(err) + return ret, diags + } + + // We'll now discard all of the arguments that the flag package handled, + // and focus only on the positional arguments for the rest of the function. + args = f.Args() + + if len(args) != 0 { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Invalid command arguments", + "The test command doesn't expect any positional command-line arguments.", + )) + return ret, diags + } + + return ret, diags +} diff --git a/vendor/github.com/hashicorp/terraform/command/arguments/test_test.go b/vendor/github.com/hashicorp/terraform/command/arguments/test_test.go new file mode 100644 index 00000000..7cd671a7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/arguments/test_test.go @@ -0,0 +1,83 @@ +package arguments + +import ( + "testing" + + "github.com/apparentlymart/go-shquot/shquot" + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform/tfdiags" +) + +func TestParseTest(t *testing.T) { + tests := []struct { + Input []string + Want Test + WantError string + }{ + { + nil, + Test{ + Output: TestOutput{ + JUnitXMLFile: "", + }, + }, + ``, + }, + { + []string{"-invalid"}, + Test{ + Output: TestOutput{ + JUnitXMLFile: "", + }, + }, + `flag provided but not defined: -invalid`, + }, + { + []string{"-junit-xml=result.xml"}, + Test{ + Output: TestOutput{ + JUnitXMLFile: "result.xml", + }, + }, + ``, + }, + { + []string{"baz"}, + Test{ + Output: TestOutput{ + JUnitXMLFile: "", + }, + }, + `Invalid command arguments`, + }, + } + + baseCmdline := []string{"terraform", "test"} + for _, test := range tests { + name := shquot.POSIXShell(append(baseCmdline, test.Input...)) + t.Run(name, func(t *testing.T) { + t.Log(name) + got, diags := ParseTest(test.Input) + + if test.WantError != "" { + if len(diags) != 1 { + t.Fatalf("got %d diagnostics; want exactly 1\n%s", len(diags), diags.Err().Error()) + } + if diags[0].Severity() != tfdiags.Error { + t.Fatalf("got a warning; want an error\n%s", diags.Err().Error()) + } + if desc := diags[0].Description(); desc.Summary != test.WantError { + t.Fatalf("wrong error\ngot: %s\nwant: %s", desc.Summary, test.WantError) + } + } else { + if len(diags) != 0 { + t.Fatalf("got %d diagnostics; want none\n%s", len(diags), diags.Err().Error()) + } + } + + if diff := cmp.Diff(test.Want, got); diff != "" { + t.Errorf("wrong result\n%s", diff) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/arguments/types.go b/vendor/github.com/hashicorp/terraform/command/arguments/types.go new file mode 100644 index 00000000..ff529361 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/arguments/types.go @@ -0,0 +1,28 @@ +package arguments + +// ViewType represents which view layer to use for a given command. Not all +// commands will support all view types, and validation that the type is +// supported should happen in the view constructor. +type ViewType rune + +const ( + ViewNone ViewType = 0 + ViewHuman ViewType = 'H' + ViewJSON ViewType = 'J' + ViewRaw ViewType = 'R' +) + +func (vt ViewType) String() string { + switch vt { + case ViewNone: + return "none" + case ViewHuman: + return "human" + case ViewJSON: + return "json" + case ViewRaw: + return "raw" + default: + return "unknown" + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/arguments/validate.go b/vendor/github.com/hashicorp/terraform/command/arguments/validate.go new file mode 100644 index 00000000..71b31e09 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/arguments/validate.go @@ -0,0 +1,59 @@ +package arguments + +import ( + "github.com/hashicorp/terraform/tfdiags" +) + +// Validate represents the command-line arguments for the validate command. +type Validate struct { + // Path is the directory containing the configuration to be validated. If + // unspecified, validate will use the current directory. + Path string + + // ViewType specifies which output format to use: human, JSON, or "raw". + ViewType ViewType +} + +// ParseValidate processes CLI arguments, returning a Validate value and errors. +// If errors are encountered, a Validate value is still returned representing +// the best effort interpretation of the arguments. +func ParseValidate(args []string) (*Validate, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + validate := &Validate{ + Path: ".", + } + + var jsonOutput bool + cmdFlags := defaultFlagSet("validate") + cmdFlags.BoolVar(&jsonOutput, "json", false, "json") + + if err := cmdFlags.Parse(args); err != nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to parse command-line flags", + err.Error(), + )) + } + + args = cmdFlags.Args() + if len(args) > 1 { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Too many command line arguments", + "Expected at most one positional argument.", + )) + } + + if len(args) > 0 { + validate.Path = args[0] + } + + switch { + case jsonOutput: + validate.ViewType = ViewJSON + default: + validate.ViewType = ViewHuman + } + + return validate, diags +} diff --git a/vendor/github.com/hashicorp/terraform/command/arguments/validate_test.go b/vendor/github.com/hashicorp/terraform/command/arguments/validate_test.go new file mode 100644 index 00000000..29b90d16 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/arguments/validate_test.go @@ -0,0 +1,99 @@ +package arguments + +import ( + "reflect" + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/hashicorp/terraform/tfdiags" +) + +func TestParseValidate_valid(t *testing.T) { + testCases := map[string]struct { + args []string + want *Validate + }{ + "defaults": { + nil, + &Validate{ + Path: ".", + ViewType: ViewHuman, + }, + }, + "json": { + []string{"-json"}, + &Validate{ + Path: ".", + ViewType: ViewJSON, + }, + }, + "path": { + []string{"-json", "foo"}, + &Validate{ + Path: "foo", + ViewType: ViewJSON, + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, diags := ParseValidate(tc.args) + if len(diags) > 0 { + t.Fatalf("unexpected diags: %v", diags) + } + if *got != *tc.want { + t.Fatalf("unexpected result\n got: %#v\nwant: %#v", got, tc.want) + } + }) + } +} + +func TestParseValidate_invalid(t *testing.T) { + testCases := map[string]struct { + args []string + want *Validate + wantDiags tfdiags.Diagnostics + }{ + "unknown flag": { + []string{"-boop"}, + &Validate{ + Path: ".", + ViewType: ViewHuman, + }, + tfdiags.Diagnostics{ + tfdiags.Sourceless( + tfdiags.Error, + "Failed to parse command-line flags", + "flag provided but not defined: -boop", + ), + }, + }, + "too many arguments": { + []string{"-json", "bar", "baz"}, + &Validate{ + Path: "bar", + ViewType: ViewJSON, + }, + tfdiags.Diagnostics{ + tfdiags.Sourceless( + tfdiags.Error, + "Too many command line arguments", + "Expected at most one positional argument.", + ), + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, gotDiags := ParseValidate(tc.args) + if *got != *tc.want { + t.Fatalf("unexpected result\n got: %#v\nwant: %#v", got, tc.want) + } + if !reflect.DeepEqual(gotDiags, tc.wantDiags) { + t.Errorf("wrong result\ngot: %s\nwant: %s", spew.Sdump(gotDiags), spew.Sdump(tc.wantDiags)) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/arguments/view.go b/vendor/github.com/hashicorp/terraform/command/arguments/view.go new file mode 100644 index 00000000..3d6372b6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/arguments/view.go @@ -0,0 +1,43 @@ +package arguments + +// View represents the global command-line arguments which configure the view. +type View struct { + // NoColor is used to disable the use of terminal color codes in all + // output. + NoColor bool + + // CompactWarnings is used to coalesce duplicate warnings, to reduce the + // level of noise when multiple instances of the same warning are raised + // for a configuration. + CompactWarnings bool +} + +// ParseView processes CLI arguments, returning a View value and a +// possibly-modified slice of arguments. If any of the supported flags are +// found, they will be removed from the slice. +func ParseView(args []string) (*View, []string) { + common := &View{} + + // Keep track of the length of the returned slice. When we find an + // argument we support, i will not be incremented. + i := 0 + for _, v := range args { + switch v { + case "-no-color": + common.NoColor = true + case "-compact-warnings": + common.CompactWarnings = true + default: + // Unsupported argument: move left to the current position, and + // increment the index. + args[i] = v + i++ + } + } + + // Reduce the slice to the number of unsupported arguments. Any remaining + // to the right of i have already been moved left. + args = args[:i] + + return common, args +} diff --git a/vendor/github.com/hashicorp/terraform/command/arguments/view_test.go b/vendor/github.com/hashicorp/terraform/command/arguments/view_test.go new file mode 100644 index 00000000..d2e7c3f7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/arguments/view_test.go @@ -0,0 +1,62 @@ +package arguments + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestParseView(t *testing.T) { + testCases := map[string]struct { + args []string + want *View + wantArgs []string + }{ + "nil": { + nil, + &View{NoColor: false, CompactWarnings: false}, + nil, + }, + "empty": { + []string{}, + &View{NoColor: false, CompactWarnings: false}, + []string{}, + }, + "none matching": { + []string{"-foo", "bar", "-baz"}, + &View{NoColor: false, CompactWarnings: false}, + []string{"-foo", "bar", "-baz"}, + }, + "no-color": { + []string{"-foo", "-no-color", "-baz"}, + &View{NoColor: true, CompactWarnings: false}, + []string{"-foo", "-baz"}, + }, + "compact-warnings": { + []string{"-foo", "-compact-warnings", "-baz"}, + &View{NoColor: false, CompactWarnings: true}, + []string{"-foo", "-baz"}, + }, + "both": { + []string{"-foo", "-no-color", "-compact-warnings", "-baz"}, + &View{NoColor: true, CompactWarnings: true}, + []string{"-foo", "-baz"}, + }, + "both, resulting in empty args": { + []string{"-no-color", "-compact-warnings"}, + &View{NoColor: true, CompactWarnings: true}, + []string{}, + }, + } + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got, gotArgs := ParseView(tc.args) + if *got != *tc.want { + t.Errorf("unexpected result\n got: %#v\nwant: %#v", got, tc.want) + } + if !cmp.Equal(gotArgs, tc.wantArgs) { + t.Errorf("unexpected args\n got: %#v\nwant: %#v", gotArgs, tc.wantArgs) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/cliconfig/cliconfig.go b/vendor/github.com/hashicorp/terraform/command/cliconfig/cliconfig.go index 7ac6eeb0..0896738c 100644 --- a/vendor/github.com/hashicorp/terraform/command/cliconfig/cliconfig.go +++ b/vendor/github.com/hashicorp/terraform/command/cliconfig/cliconfig.go @@ -287,39 +287,39 @@ func (c *Config) Validate() tfdiags.Diagnostics { // Merge merges two configurations and returns a third entirely // new configuration with the two merged. -func (c1 *Config) Merge(c2 *Config) *Config { +func (c *Config) Merge(c2 *Config) *Config { var result Config result.Providers = make(map[string]string) result.Provisioners = make(map[string]string) - for k, v := range c1.Providers { + for k, v := range c.Providers { result.Providers[k] = v } for k, v := range c2.Providers { - if v1, ok := c1.Providers[k]; ok { + if v1, ok := c.Providers[k]; ok { log.Printf("[INFO] Local %s provider configuration '%s' overrides '%s'", k, v, v1) } result.Providers[k] = v } - for k, v := range c1.Provisioners { + for k, v := range c.Provisioners { result.Provisioners[k] = v } for k, v := range c2.Provisioners { - if v1, ok := c1.Provisioners[k]; ok { + if v1, ok := c.Provisioners[k]; ok { log.Printf("[INFO] Local %s provisioner configuration '%s' overrides '%s'", k, v, v1) } result.Provisioners[k] = v } - result.DisableCheckpoint = c1.DisableCheckpoint || c2.DisableCheckpoint - result.DisableCheckpointSignature = c1.DisableCheckpointSignature || c2.DisableCheckpointSignature + result.DisableCheckpoint = c.DisableCheckpoint || c2.DisableCheckpoint + result.DisableCheckpointSignature = c.DisableCheckpointSignature || c2.DisableCheckpointSignature - result.PluginCacheDir = c1.PluginCacheDir + result.PluginCacheDir = c.PluginCacheDir if result.PluginCacheDir == "" { result.PluginCacheDir = c2.PluginCacheDir } - if (len(c1.Hosts) + len(c2.Hosts)) > 0 { + if (len(c.Hosts) + len(c2.Hosts)) > 0 { result.Hosts = make(map[string]*ConfigHost) - for name, host := range c1.Hosts { + for name, host := range c.Hosts { result.Hosts[name] = host } for name, host := range c2.Hosts { @@ -327,9 +327,9 @@ func (c1 *Config) Merge(c2 *Config) *Config { } } - if (len(c1.Credentials) + len(c2.Credentials)) > 0 { + if (len(c.Credentials) + len(c2.Credentials)) > 0 { result.Credentials = make(map[string]map[string]interface{}) - for host, creds := range c1.Credentials { + for host, creds := range c.Credentials { result.Credentials[host] = creds } for host, creds := range c2.Credentials { @@ -340,9 +340,9 @@ func (c1 *Config) Merge(c2 *Config) *Config { } } - if (len(c1.CredentialsHelpers) + len(c2.CredentialsHelpers)) > 0 { + if (len(c.CredentialsHelpers) + len(c2.CredentialsHelpers)) > 0 { result.CredentialsHelpers = make(map[string]*ConfigCredentialsHelper) - for name, helper := range c1.CredentialsHelpers { + for name, helper := range c.CredentialsHelpers { result.CredentialsHelpers[name] = helper } for name, helper := range c2.CredentialsHelpers { @@ -350,8 +350,8 @@ func (c1 *Config) Merge(c2 *Config) *Config { } } - if (len(c1.ProviderInstallation) + len(c2.ProviderInstallation)) > 0 { - result.ProviderInstallation = append(result.ProviderInstallation, c1.ProviderInstallation...) + if (len(c.ProviderInstallation) + len(c2.ProviderInstallation)) > 0 { + result.ProviderInstallation = append(result.ProviderInstallation, c.ProviderInstallation...) result.ProviderInstallation = append(result.ProviderInstallation, c2.ProviderInstallation...) } diff --git a/vendor/github.com/hashicorp/terraform/command/cliconfig/config_unix.go b/vendor/github.com/hashicorp/terraform/command/cliconfig/config_unix.go index 5922c17a..f1a9d593 100644 --- a/vendor/github.com/hashicorp/terraform/command/cliconfig/config_unix.go +++ b/vendor/github.com/hashicorp/terraform/command/cliconfig/config_unix.go @@ -31,7 +31,7 @@ func homeDir() (string, error) { // First prefer the HOME environmental variable if home := os.Getenv("HOME"); home != "" { // FIXME: homeDir gets called from globalPluginDirs during init, before - // the logging is setup. We should move meta initializtion outside of + // the logging is set up. We should move meta initializtion outside of // init, but in the meantime we just need to silence this output. //log.Printf("[DEBUG] Detected home directory from env var: %s", home) diff --git a/vendor/github.com/hashicorp/terraform/command/clistate/local_state.go b/vendor/github.com/hashicorp/terraform/command/clistate/local_state.go index f5f9dbac..42f921f1 100644 --- a/vendor/github.com/hashicorp/terraform/command/clistate/local_state.go +++ b/vendor/github.com/hashicorp/terraform/command/clistate/local_state.go @@ -12,8 +12,8 @@ import ( "time" multierror "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform/internal/legacy/terraform" "github.com/hashicorp/terraform/states/statemgr" - "github.com/hashicorp/terraform/terraform" ) // LocalState manages a state storage that is local to the filesystem. diff --git a/vendor/github.com/hashicorp/terraform/command/clistate/state.go b/vendor/github.com/hashicorp/terraform/command/clistate/state.go index 2620c62f..5aa3f1d2 100644 --- a/vendor/github.com/hashicorp/terraform/command/clistate/state.go +++ b/vendor/github.com/hashicorp/terraform/command/clistate/state.go @@ -7,33 +7,25 @@ package clistate import ( "context" "fmt" - "strings" "sync" "time" - "github.com/hashicorp/errwrap" - multierror "github.com/hashicorp/go-multierror" - "github.com/hashicorp/terraform/helper/slowmessage" + "github.com/hashicorp/terraform/command/views" + "github.com/hashicorp/terraform/internal/helper/slowmessage" "github.com/hashicorp/terraform/states/statemgr" - "github.com/mitchellh/cli" - "github.com/mitchellh/colorstring" + "github.com/hashicorp/terraform/tfdiags" ) const ( LockThreshold = 400 * time.Millisecond - LockMessage = "Acquiring state lock. This may take a few moments..." - LockErrorMessage = `Error acquiring the state lock: {{err}} + LockErrorMessage = `Error message: %s Terraform acquires a state lock to protect the state from being written by multiple users at the same time. Please resolve the issue above and try again. For most commands, you can disable locking with the "-lock=false" flag, but this is not recommended.` - UnlockMessage = "Releasing state lock. This may take a few moments..." - UnlockErrorMessage = ` -[reset][bold][red]Error releasing the state lock![reset][red] - -Error message: %s + UnlockErrorMessage = `Error message: %s Terraform acquires a lock when accessing your state to prevent others running Terraform to potentially modify the state at the same time. An @@ -46,8 +38,7 @@ In this scenario, please call the "force-unlock" command to unlock the state manually. This is a very dangerous operation since if it is done erroneously it could result in two people modifying state at the same time. Only call this command if you're certain that the unlock above failed and -that no one else is holding a lock. -` +that no one else is holding a lock.` ) // Locker allows for more convenient usage of the lower-level statemgr.Locker @@ -60,12 +51,17 @@ that no one else is holding a lock. // Unlock, which is at a minimum the LockID string returned by the // statemgr.Locker. type Locker interface { + // Returns a shallow copy of the locker with its context changed to ctx. + WithContext(ctx context.Context) Locker + // Lock the provided state manager, storing the reason string in the LockInfo. - Lock(s statemgr.Locker, reason string) error + Lock(s statemgr.Locker, reason string) tfdiags.Diagnostics + // Unlock the previously locked state. - // An optional error can be passed in, and will be combined with any error - // from the Unlock operation. - Unlock(error) error + Unlock() tfdiags.Diagnostics + + // Timeout returns the configured timeout duration + Timeout() time.Duration } type locker struct { @@ -73,34 +69,43 @@ type locker struct { ctx context.Context timeout time.Duration state statemgr.Locker - ui cli.Ui - color *colorstring.Colorize + view views.StateLocker lockID string } +var _ Locker = (*locker)(nil) + // Create a new Locker. // This Locker uses state.LockWithContext to retry the lock until the provided // timeout is reached, or the context is canceled. Lock progress will be be // reported to the user through the provided UI. -func NewLocker( - ctx context.Context, - timeout time.Duration, - ui cli.Ui, - color *colorstring.Colorize) Locker { +func NewLocker(timeout time.Duration, view views.StateLocker) Locker { + return &locker{ + ctx: context.Background(), + timeout: timeout, + view: view, + } +} - l := &locker{ +// WithContext returns a new Locker with the specified context, copying the +// timeout and view parameters from the original Locker. +func (l *locker) WithContext(ctx context.Context) Locker { + if ctx == nil { + panic("nil context") + } + return &locker{ ctx: ctx, - timeout: timeout, - ui: ui, - color: color, + timeout: l.timeout, + view: l.view, } - return l } // Locker locks the given state and outputs to the user if locking is taking // longer than the threshold. The lock is retried until the context is // cancelled. -func (l *locker) Lock(s statemgr.Locker, reason string) error { +func (l *locker) Lock(s statemgr.Locker, reason string) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + l.mu.Lock() defer l.mu.Unlock() @@ -116,46 +121,49 @@ func (l *locker) Lock(s statemgr.Locker, reason string) error { id, err := statemgr.LockWithContext(ctx, s, lockInfo) l.lockID = id return err - }, func() { - if l.ui != nil { - l.ui.Output(l.color.Color(LockMessage)) - } - }) + }, l.view.Locking) if err != nil { - return errwrap.Wrapf(strings.TrimSpace(LockErrorMessage), err) + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Error acquiring the state lock", + fmt.Sprintf(LockErrorMessage, err), + )) } - return nil + return diags } -func (l *locker) Unlock(parentErr error) error { +func (l *locker) Unlock() tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + l.mu.Lock() defer l.mu.Unlock() if l.lockID == "" { - return parentErr + return diags } err := slowmessage.Do(LockThreshold, func() error { return l.state.Unlock(l.lockID) - }, func() { - if l.ui != nil { - l.ui.Output(l.color.Color(UnlockMessage)) - } - }) + }, l.view.Unlocking) if err != nil { - l.ui.Output(l.color.Color(fmt.Sprintf( - "\n"+strings.TrimSpace(UnlockErrorMessage)+"\n", err))) - - parentErr = multierror.Append(parentErr, err) + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Error releasing the state lock", + fmt.Sprintf(UnlockErrorMessage, err), + )) } - return parentErr + return diags } +func (l *locker) Timeout() time.Duration { + return l.timeout +} + type noopLocker struct{} // NewNoopLocker returns a valid Locker that does nothing. @@ -163,10 +171,20 @@ func NewNoopLocker() Locker { return noopLocker{} } -func (l noopLocker) Lock(statemgr.Locker, string) error { +var _ Locker = noopLocker{} + +func (l noopLocker) WithContext(ctx context.Context) Locker { + return l +} + +func (l noopLocker) Lock(statemgr.Locker, string) tfdiags.Diagnostics { + return nil +} + +func (l noopLocker) Unlock() tfdiags.Diagnostics { return nil } -func (l noopLocker) Unlock(err error) error { - return err +func (l noopLocker) Timeout() time.Duration { + return 0 } diff --git a/vendor/github.com/hashicorp/terraform/command/clistate/state_test.go b/vendor/github.com/hashicorp/terraform/command/clistate/state_test.go index f1ba88ab..b455b58b 100644 --- a/vendor/github.com/hashicorp/terraform/command/clistate/state_test.go +++ b/vendor/github.com/hashicorp/terraform/command/clistate/state_test.go @@ -1,24 +1,24 @@ package clistate import ( - "context" - "fmt" "testing" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/views" + "github.com/hashicorp/terraform/internal/terminal" "github.com/hashicorp/terraform/states/statemgr" - "github.com/mitchellh/cli" - "github.com/mitchellh/colorstring" ) func TestUnlock(t *testing.T) { - ui := new(cli.MockUi) + streams, _ := terminal.StreamsForTesting(t) + view := views.NewView(streams) - l := NewLocker(context.Background(), 0, ui, &colorstring.Colorize{Disable: true}) + l := NewLocker(0, views.NewStateLocker(arguments.ViewHuman, view)) l.Lock(statemgr.NewUnlockErrorFull(nil, nil), "test-lock") - err := l.Unlock(nil) - if err != nil { - fmt.Printf(err.Error()) + diags := l.Unlock() + if diags.HasErrors() { + t.Log(diags.Err().Error()) } else { t.Error("expected error") } diff --git a/vendor/github.com/hashicorp/terraform/command/command.go b/vendor/github.com/hashicorp/terraform/command/command.go index 815a6fa6..41748d65 100644 --- a/vendor/github.com/hashicorp/terraform/command/command.go +++ b/vendor/github.com/hashicorp/terraform/command/command.go @@ -2,11 +2,8 @@ package command import ( "fmt" - "log" "os" "runtime" - - "github.com/hashicorp/terraform/terraform" ) // Set to true when we're testing @@ -50,45 +47,25 @@ is configured to use a non-local backend. This backend doesn't support this operation. ` -// ModulePath returns the path to the root module from the CLI args. +// ModulePath returns the path to the root module and validates CLI arguments. // -// This centralizes the logic for any commands that expect a module path -// on their CLI args. This will verify that only one argument is given -// and that it is a path to configuration. +// This centralizes the logic for any commands that previously accepted +// a module path via CLI arguments. This will error if any extraneous arguments +// are given and suggest using the -chdir flag instead. // // If your command accepts more than one arg, then change the slice bounds // to pass validation. func ModulePath(args []string) (string, error) { // TODO: test - if len(args) > 1 { - return "", fmt.Errorf("Too many command line arguments. Configuration path expected.") - } - - if len(args) == 0 { - path, err := os.Getwd() - if err != nil { - return "", fmt.Errorf("Error getting pwd: %s", err) - } - - return path, nil + if len(args) > 0 { + return "", fmt.Errorf("Too many command line arguments. Did you mean to use -chdir?") } - return args[0], nil -} - -func (m *Meta) validateContext(ctx *terraform.Context) bool { - log.Println("[INFO] Validating the context...") - diags := ctx.Validate() - log.Printf("[INFO] Validation result: %d diagnostics", len(diags)) - - if len(diags) > 0 { - m.Ui.Output( - "There are warnings and/or errors related to your configuration. Please\n" + - "fix these before continuing.\n") - - m.showDiagnostics(diags) + path, err := os.Getwd() + if err != nil { + return "", fmt.Errorf("Error getting pwd: %s", err) } - return !diags.HasErrors() + return path, nil } diff --git a/vendor/github.com/hashicorp/terraform/command/command_test.go b/vendor/github.com/hashicorp/terraform/command/command_test.go index e28ee6e1..44369a96 100644 --- a/vendor/github.com/hashicorp/terraform/command/command_test.go +++ b/vendor/github.com/hashicorp/terraform/command/command_test.go @@ -19,8 +19,10 @@ import ( svchost "github.com/hashicorp/terraform-svchost" "github.com/hashicorp/terraform-svchost/disco" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/internal/getproviders" "github.com/hashicorp/terraform/internal/initwd" + "github.com/hashicorp/terraform/internal/terminal" "github.com/hashicorp/terraform/registry" "github.com/hashicorp/terraform/addrs" @@ -31,7 +33,6 @@ import ( "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/plans/planfile" "github.com/hashicorp/terraform/providers" - "github.com/hashicorp/terraform/provisioners" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/states/statefile" "github.com/hashicorp/terraform/states/statemgr" @@ -41,6 +42,7 @@ import ( backendInit "github.com/hashicorp/terraform/backend/init" backendLocal "github.com/hashicorp/terraform/backend/local" + legacy "github.com/hashicorp/terraform/internal/legacy/terraform" _ "github.com/hashicorp/terraform/internal/logging" ) @@ -119,23 +121,6 @@ func metaOverridesForProvider(p providers.Interface) *testingOverrides { } } -func metaOverridesForProviderAndProvisioner(p providers.Interface, pr provisioners.Interface) *testingOverrides { - return &testingOverrides{ - Providers: map[addrs.Provider]providers.Factory{ - addrs.NewDefaultProvider("test"): providers.FactoryFixed(p), - }, - Provisioners: map[string]provisioners.Factory{ - "shell": provisioners.FactoryFixed(pr), - }, - } -} - -func testModule(t *testing.T, name string) *configs.Config { - t.Helper() - c, _ := testModuleWithSnapshot(t, name) - return c -} - func testModuleWithSnapshot(t *testing.T, name string) (*configs.Config, *configload.Snapshot) { t.Helper() @@ -197,9 +182,14 @@ func testPlanFile(t *testing.T, configSnap *configload.Snapshot, state *states.S State: state, TerraformVersion: version.SemVer, } + prevStateFile := &statefile.File{ + Lineage: "", + State: state, // we just assume no changes detected during refresh + TerraformVersion: version.SemVer, + } path := testTempFile(t) - err := planfile.Create(path, configSnap, stateFile, plan) + err := planfile.Create(path, configSnap, prevStateFile, stateFile, plan) if err != nil { t.Fatalf("failed to create temporary plan file: %s", err) } @@ -404,7 +394,7 @@ func testStateFileWorkspaceDefault(t *testing.T, workspace string, s *states.Sta // testStateFileRemote writes the state out to the remote statefile // in the cwd. Use `testCwd` to change into a temp cwd. -func testStateFileRemote(t *testing.T, s *terraform.State) string { +func testStateFileRemote(t *testing.T, s *legacy.State) string { t.Helper() path := filepath.Join(DefaultDataDir, DefaultStateFilename) @@ -418,7 +408,7 @@ func testStateFileRemote(t *testing.T, s *terraform.State) string { } defer f.Close() - if err := terraform.WriteState(s, f); err != nil { + if err := legacy.WriteState(s, f); err != nil { t.Fatalf("err: %s", err) } @@ -446,9 +436,9 @@ func testStateRead(t *testing.T, path string) *states.State { // testDataStateRead reads a "data state", which is a file format resembling // our state format v3 that is used only to track current backend settings. // -// This old format still uses *terraform.State, but should be replaced with +// This old format still uses *legacy.State, but should be replaced with // a more specialized type in a later release. -func testDataStateRead(t *testing.T, path string) *terraform.State { +func testDataStateRead(t *testing.T, path string) *legacy.State { t.Helper() f, err := os.Open(path) @@ -457,7 +447,7 @@ func testDataStateRead(t *testing.T, path string) *terraform.State { } defer f.Close() - s, err := terraform.ReadState(f) + s, err := legacy.ReadState(f) if err != nil { t.Fatalf("err: %s", err) } @@ -515,26 +505,6 @@ func testTempDir(t *testing.T) string { return d } -// testRename renames the path to new and returns a function to defer to -// revert the rename. -func testRename(t *testing.T, base, path, new string) func() { - t.Helper() - - if base != "" { - path = filepath.Join(base, path) - new = filepath.Join(base, new) - } - - if err := os.Rename(path, new); err != nil { - t.Fatalf("err: %s", err) - } - - return func() { - // Just re-rename and ignore the return value - testRename(t, "", new, path) - } -} - // testChdir changes the directory and returns a function to defer to // revert the old cwd. func testChdir(t *testing.T, new string) func() { @@ -667,7 +637,7 @@ func testInteractiveInput(t *testing.T, answers []string) func() { // Disable test mode so input is called test = false - // Setup reader/writers + // Set up reader/writers testInputResponse = answers defaultInputReader = bytes.NewBufferString("") defaultInputWriter = new(bytes.Buffer) @@ -688,7 +658,7 @@ func testInputMap(t *testing.T, answers map[string]string) func() { // Disable test mode so input is called test = false - // Setup reader/writers + // Set up reader/writers defaultInputReader = bytes.NewBufferString("") defaultInputWriter = new(bytes.Buffer) @@ -719,7 +689,7 @@ func testInputMap(t *testing.T, answers map[string]string) func() { // be returned about the backend configuration having changed and that // "terraform init" must be run, since the test backend config cache created // by this function contains the hash for an empty configuration. -func testBackendState(t *testing.T, s *states.State, c int) (*terraform.State, *httptest.Server) { +func testBackendState(t *testing.T, s *states.State, c int) (*legacy.State, *httptest.Server) { t.Helper() var b64md5 string @@ -759,8 +729,8 @@ func testBackendState(t *testing.T, s *states.State, c int) (*terraform.State, * configSchema := b.ConfigSchema() hash := backendConfig.Hash(configSchema) - state := terraform.NewState() - state.Backend = &terraform.BackendState{ + state := legacy.NewState() + state.Backend = &legacy.BackendState{ Type: "http", ConfigRaw: json.RawMessage(fmt.Sprintf(`{"address":%q}`, srv.URL)), Hash: uint64(hash), @@ -772,10 +742,10 @@ func testBackendState(t *testing.T, s *states.State, c int) (*terraform.State, * // testRemoteState is used to make a test HTTP server to return a given // state file that can be used for testing legacy remote state. // -// The return values are a *terraform.State instance that should be written +// The return values are a *legacy.State instance that should be written // as the "data state" (really: backend state) and the server that the // returned data state refers to. -func testRemoteState(t *testing.T, s *states.State, c int) (*terraform.State, *httptest.Server) { +func testRemoteState(t *testing.T, s *states.State, c int) (*legacy.State, *httptest.Server) { t.Helper() var b64md5 string @@ -795,10 +765,10 @@ func testRemoteState(t *testing.T, s *states.State, c int) (*terraform.State, *h resp.Write(buf.Bytes()) } - retState := terraform.NewState() + retState := legacy.NewState() srv := httptest.NewServer(http.HandlerFunc(cb)) - b := &terraform.BackendState{ + b := &legacy.BackendState{ Type: "http", } b.SetConfig(cty.ObjectVal(map[string]cty.Value{ @@ -944,8 +914,6 @@ func testCopyDir(t *testing.T, src, dst string) { } } } - - return } // normalizeJSON removes all insignificant whitespace from the given JSON buffer @@ -1067,3 +1035,8 @@ func fakeRegistryHandler(resp http.ResponseWriter, req *http.Request) { resp.Write([]byte(`provider not found`)) } } + +func testView(t *testing.T) (*views.View, func(*testing.T) *terminal.TestOutput) { + streams, done := terminal.StreamsForTesting(t) + return views.NewView(streams), done +} diff --git a/vendor/github.com/hashicorp/terraform/command/console.go b/vendor/github.com/hashicorp/terraform/command/console.go index 359d9dca..ebecd9ed 100644 --- a/vendor/github.com/hashicorp/terraform/command/console.go +++ b/vendor/github.com/hashicorp/terraform/command/console.go @@ -7,7 +7,7 @@ import ( "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/backend" - "github.com/hashicorp/terraform/helper/wrappedstreams" + "github.com/hashicorp/terraform/internal/helper/wrappedstreams" "github.com/hashicorp/terraform/repl" "github.com/hashicorp/terraform/tfdiags" @@ -35,6 +35,7 @@ func (c *ConsoleCommand) Run(args []string) int { c.Ui.Error(err.Error()) return 1 } + configPath = c.Meta.normalizePath(configPath) // Check for user-supplied plugin path if c.pluginPath, err = c.loadPluginPath(); err != nil { @@ -69,6 +70,9 @@ func (c *ConsoleCommand) Run(args []string) int { return 1 } + // This is a read-only command + c.ignoreRemoteBackendVersionConflict(b) + // Build the operation opReq := c.Operation(b) opReq.ConfigDir = configPath @@ -100,13 +104,13 @@ func (c *ConsoleCommand) Run(args []string) int { // Successfully creating the context can result in a lock, so ensure we release it defer func() { - err := opReq.StateLocker.Unlock(nil) - if err != nil { - c.Ui.Error(err.Error()) + diags := opReq.StateLocker.Unlock() + if diags.HasErrors() { + c.showDiagnostics(diags) } }() - // Setup the UI so we can output directly to stdout + // Set up the UI so we can output directly to stdout ui := &cli.BasicUi{ Writer: wrappedstreams.Stdout(), ErrorWriter: wrappedstreams.Stderr(), @@ -123,6 +127,10 @@ func (c *ConsoleCommand) Run(args []string) int { c.showDiagnostics(diags) return 1 } + + // set the ConsoleMode to true so any available console-only functions included. + scope.ConsoleMode = true + if diags.HasErrors() { diags = diags.Append(tfdiags.SimpleWarning("Due to the problems above, some expressions may produce unexpected results.")) } @@ -171,7 +179,7 @@ func (c *ConsoleCommand) modePiped(session *repl.Session, ui cli.Ui) int { func (c *ConsoleCommand) Help() string { helpText := ` -Usage: terraform console [options] [DIR] +Usage: terraform [global options] console [options] Starts an interactive console for experimenting with Terraform interpolations. @@ -183,25 +191,21 @@ Usage: terraform console [options] [DIR] This command will never modify your state. - DIR can be set to a directory with a Terraform state to load. By - default, this will default to the current working directory. - Options: - -state=path Path to read state. Defaults to "terraform.tfstate" - - -var 'foo=bar' Set a variable in the Terraform configuration. This - flag can be set multiple times. - - -var-file=foo Set variables in the Terraform configuration from - a file. If "terraform.tfvars" or any ".auto.tfvars" - files are present, they will be automatically loaded. + -state=path Legacy option for the local backend only. See the local + backend's documentation for more information. + -var 'foo=bar' Set a variable in the Terraform configuration. This + flag can be set multiple times. + -var-file=foo Set variables in the Terraform configuration from + a file. If "terraform.tfvars" or any ".auto.tfvars" + files are present, they will be automatically loaded. ` return strings.TrimSpace(helpText) } func (c *ConsoleCommand) Synopsis() string { - return "Interactive console for Terraform interpolations" + return "Try Terraform expressions at an interactive command prompt" } diff --git a/vendor/github.com/hashicorp/terraform/command/console_interactive.go b/vendor/github.com/hashicorp/terraform/command/console_interactive.go index f8261bb5..92f3b4ef 100644 --- a/vendor/github.com/hashicorp/terraform/command/console_interactive.go +++ b/vendor/github.com/hashicorp/terraform/command/console_interactive.go @@ -9,7 +9,7 @@ import ( "fmt" "io" - "github.com/hashicorp/terraform/helper/wrappedreadline" + "github.com/hashicorp/terraform/internal/helper/wrappedreadline" "github.com/hashicorp/terraform/repl" "github.com/chzyer/readline" diff --git a/vendor/github.com/hashicorp/terraform/command/console_test.go b/vendor/github.com/hashicorp/terraform/command/console_test.go index 921e664d..1352a6ad 100644 --- a/vendor/github.com/hashicorp/terraform/command/console_test.go +++ b/vendor/github.com/hashicorp/terraform/command/console_test.go @@ -9,7 +9,7 @@ import ( "testing" "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/providers" "github.com/mitchellh/cli" "github.com/zclconf/go-cty/cty" ) @@ -27,10 +27,12 @@ func TestConsole_basic(t *testing.T) { p := testProvider() ui := cli.NewMockUi() + view, _ := testView(t) c := &ConsoleCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -52,31 +54,36 @@ func TestConsole_basic(t *testing.T) { } func TestConsole_tfvars(t *testing.T) { - tmp, cwd := testCwd(t) - defer testFixCwd(t, tmp, cwd) + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-vars"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() // Write a terraform.tvars - varFilePath := filepath.Join(tmp, "terraform.tfvars") + varFilePath := filepath.Join(td, "terraform.tfvars") if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil { t.Fatalf("err: %s", err) } p := testProvider() - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "value": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "value": {Type: cty.String, Optional: true}, + }, }, }, }, } - ui := cli.NewMockUi() + view, _ := testView(t) c := &ConsoleCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -84,9 +91,7 @@ func TestConsole_tfvars(t *testing.T) { defer testStdinPipe(t, strings.NewReader("var.foo\n"))() outCloser := testStdoutCapture(t, &output) - args := []string{ - testFixturePath("apply-vars"), - } + args := []string{} code := c.Run(args) outCloser() if code != 0 { @@ -105,25 +110,33 @@ func TestConsole_unsetRequiredVars(t *testing.T) { // "terraform console" producing an interactive prompt for those variables // or producing errors. Instead, it should allow evaluation in that // partial context but see the unset variables values as being unknown. - - tmp, cwd := testCwd(t) - defer testFixCwd(t, tmp, cwd) + // + // This test fixture includes variable "foo" {}, which we are + // intentionally not setting here. + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-vars"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() p := testProvider() - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "value": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "value": {Type: cty.String, Optional: true}, + }, }, }, }, } ui := cli.NewMockUi() + view, _ := testView(t) c := &ConsoleCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -131,11 +144,7 @@ func TestConsole_unsetRequiredVars(t *testing.T) { defer testStdinPipe(t, strings.NewReader("var.foo\n"))() outCloser := testStdoutCapture(t, &output) - args := []string{ - // This test fixture includes variable "foo" {}, which we are - // intentionally not setting here. - testFixturePath("apply-vars"), - } + args := []string{} code := c.Run(args) outCloser() @@ -149,15 +158,19 @@ func TestConsole_unsetRequiredVars(t *testing.T) { } func TestConsole_variables(t *testing.T) { - tmp, cwd := testCwd(t) - defer testFixCwd(t, tmp, cwd) + td := tempDir(t) + testCopyDir(t, testFixturePath("variables"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() p := testProvider() ui := cli.NewMockUi() + view, _ := testView(t) c := &ConsoleCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -168,9 +181,7 @@ func TestConsole_variables(t *testing.T) { "local.snack_bar\n": "[\n \"popcorn\",\n (sensitive),\n]\n", } - args := []string{ - testFixturePath("variables"), - } + args := []string{} for cmd, val := range commands { var output bytes.Buffer @@ -197,11 +208,13 @@ func TestConsole_modules(t *testing.T) { p := applyFixtureProvider() ui := cli.NewMockUi() + view, _ := testView(t) c := &ConsoleCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -211,9 +224,7 @@ func TestConsole_modules(t *testing.T) { "local.foo\n": "3\n", } - args := []string{ - testFixturePath("modules"), - } + args := []string{} for cmd, val := range commands { var output bytes.Buffer diff --git a/vendor/github.com/hashicorp/terraform/command/debug_command.go b/vendor/github.com/hashicorp/terraform/command/debug_command.go deleted file mode 100644 index 7058553b..00000000 --- a/vendor/github.com/hashicorp/terraform/command/debug_command.go +++ /dev/null @@ -1,30 +0,0 @@ -package command - -import ( - "strings" - - "github.com/mitchellh/cli" -) - -// DebugCommand is a Command implementation that just shows help for -// the subcommands nested below it. -type DebugCommand struct { - Meta -} - -func (c *DebugCommand) Run(args []string) int { - return cli.RunResultHelp -} - -func (c *DebugCommand) Help() string { - helpText := ` -Usage: terraform debug [options] [args] - - This command has subcommands for debug output management -` - return strings.TrimSpace(helpText) -} - -func (c *DebugCommand) Synopsis() string { - return "Debug output management (experimental)" -} diff --git a/vendor/github.com/hashicorp/terraform/command/e2etest/automation_test.go b/vendor/github.com/hashicorp/terraform/command/e2etest/automation_test.go index b7214bc0..d65afd17 100644 --- a/vendor/github.com/hashicorp/terraform/command/e2etest/automation_test.go +++ b/vendor/github.com/hashicorp/terraform/command/e2etest/automation_test.go @@ -108,7 +108,7 @@ func TestPlanApplyInAutomation(t *testing.T) { stateResources := state.RootModule().Resources var gotResources []string - for n, _ := range stateResources { + for n := range stateResources { gotResources = append(gotResources, n) } sort.Strings(gotResources) diff --git a/vendor/github.com/hashicorp/terraform/command/e2etest/init_test.go b/vendor/github.com/hashicorp/terraform/command/e2etest/init_test.go index 963ec9d8..1f0e3fc5 100644 --- a/vendor/github.com/hashicorp/terraform/command/e2etest/init_test.go +++ b/vendor/github.com/hashicorp/terraform/command/e2etest/init_test.go @@ -9,6 +9,8 @@ import ( "strings" "testing" + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform/e2e" ) @@ -256,19 +258,11 @@ func TestInitProviders_pluginCache(t *testing.T) { // convert the slashes if building for windows. p := filepath.FromSlash("./cache") cmd.Env = append(cmd.Env, "TF_PLUGIN_CACHE_DIR="+p) - cmd.Stdin = nil - cmd.Stderr = &bytes.Buffer{} - err = cmd.Run() if err != nil { t.Errorf("unexpected error: %s", err) } - stderr := cmd.Stderr.(*bytes.Buffer).String() - if stderr != "" { - t.Errorf("unexpected stderr output:\n%s\n", stderr) - } - path := filepath.FromSlash(fmt.Sprintf(".terraform/providers/registry.terraform.io/hashicorp/template/2.1.0/%s_%s/terraform-provider-template_v2.1.0_x4", runtime.GOOS, runtime.GOARCH)) content, err := tf.ReadFile(path) if err != nil { @@ -341,7 +335,7 @@ func TestInitProviderNotFound(t *testing.T) { defer tf.Close() t.Run("registry provider not found", func(t *testing.T) { - _, stderr, err := tf.Run("init") + _, stderr, err := tf.Run("init", "-no-color") if err == nil { t.Fatal("expected error, got success") } @@ -350,6 +344,10 @@ func TestInitProviderNotFound(t *testing.T) { if !strings.Contains(oneLineStderr, "provider registry registry.terraform.io does not have a provider named registry.terraform.io/hashicorp/nonexist") { t.Errorf("expected error message is missing from output:\n%s", stderr) } + + if !strings.Contains(oneLineStderr, "All modules should specify their required_providers") { + t.Errorf("expected error message is missing from output:\n%s", stderr) + } }) t.Run("local provider not found", func(t *testing.T) { @@ -359,7 +357,7 @@ func TestInitProviderNotFound(t *testing.T) { t.Fatal(err) } - _, stderr, err := tf.Run("init", "-plugin-dir="+pluginDir) + _, stderr, err := tf.Run("init", "-no-color", "-plugin-dir="+pluginDir) if err == nil { t.Fatal("expected error, got success") } @@ -368,6 +366,32 @@ func TestInitProviderNotFound(t *testing.T) { t.Errorf("expected error message is missing from output:\n%s", stderr) } }) + + t.Run("special characters enabled", func(t *testing.T) { + _, stderr, err := tf.Run("init") + if err == nil { + t.Fatal("expected error, got success") + } + + expectedErr := `╷ +│ Error: Failed to query available provider packages +│` + ` ` + ` +│ Could not retrieve the list of available versions for provider +│ hashicorp/nonexist: provider registry registry.terraform.io does not have a +│ provider named registry.terraform.io/hashicorp/nonexist +│ +│ All modules should specify their required_providers so that external +│ consumers will get the correct providers when using a module. To see which +│ modules are currently depending on hashicorp/nonexist, run the following +│ command: +│ terraform providers +╵ + +` + if stripAnsi(stderr) != expectedErr { + t.Errorf("wrong output:\n%s", cmp.Diff(stripAnsi(stderr), expectedErr)) + } + }) } func TestInitProviderWarnings(t *testing.T) { @@ -386,7 +410,7 @@ func TestInitProviderWarnings(t *testing.T) { t.Fatal("expected error, got success") } - if !strings.Contains(stdout, "This provider is archived and no longer needed. The terraform_remote_state\ndata source is built into the latest Terraform release.") { + if !strings.Contains(stdout, "This provider is archived and no longer needed.") { t.Errorf("expected warning message is missing from output:\n%s", stdout) } diff --git a/vendor/github.com/hashicorp/terraform/command/e2etest/primary_test.go b/vendor/github.com/hashicorp/terraform/command/e2etest/primary_test.go index 28aa3c45..304ddedc 100644 --- a/vendor/github.com/hashicorp/terraform/command/e2etest/primary_test.go +++ b/vendor/github.com/hashicorp/terraform/command/e2etest/primary_test.go @@ -59,8 +59,8 @@ func TestPrimarySeparatePlan(t *testing.T) { t.Errorf("incorrect plan tally; want 1 to add:\n%s", stdout) } - if !strings.Contains(stdout, "This plan was saved to: tfplan") { - t.Errorf("missing \"This plan was saved to...\" message in plan output\n%s", stdout) + if !strings.Contains(stdout, "Saved the plan to: tfplan") { + t.Errorf("missing \"Saved the plan to...\" message in plan output\n%s", stdout) } if !strings.Contains(stdout, "terraform apply \"tfplan\"") { t.Errorf("missing next-step instruction in plan output\n%s", stdout) @@ -107,7 +107,7 @@ func TestPrimarySeparatePlan(t *testing.T) { stateResources := state.RootModule().Resources var gotResources []string - for n, _ := range stateResources { + for n := range stateResources { gotResources = append(gotResources, n) } sort.Strings(gotResources) @@ -154,13 +154,13 @@ func TestPrimaryChdirOption(t *testing.T) { defer tf.Close() //// INIT - stdout, stderr, err := tf.Run("-chdir=subdir", "init") + _, stderr, err := tf.Run("-chdir=subdir", "init") if err != nil { t.Fatalf("unexpected init error: %s\nstderr:\n%s", err, stderr) } //// PLAN - stdout, stderr, err = tf.Run("-chdir=subdir", "plan", "-out=tfplan") + stdout, stderr, err := tf.Run("-chdir=subdir", "plan", "-out=tfplan") if err != nil { t.Fatalf("unexpected plan error: %s\nstderr:\n%s", err, stderr) } @@ -169,8 +169,8 @@ func TestPrimaryChdirOption(t *testing.T) { t.Errorf("incorrect plan tally; want 0 to add:\n%s", stdout) } - if !strings.Contains(stdout, "This plan was saved to: tfplan") { - t.Errorf("missing \"This plan was saved to...\" message in plan output\n%s", stdout) + if !strings.Contains(stdout, "Saved the plan to: tfplan") { + t.Errorf("missing \"Saved the plan to...\" message in plan output\n%s", stdout) } if !strings.Contains(stdout, "terraform apply \"tfplan\"") { t.Errorf("missing next-step instruction in plan output\n%s", stdout) diff --git a/vendor/github.com/hashicorp/terraform/command/e2etest/provider_dev_test.go b/vendor/github.com/hashicorp/terraform/command/e2etest/provider_dev_test.go index 723591db..401eb779 100644 --- a/vendor/github.com/hashicorp/terraform/command/e2etest/provider_dev_test.go +++ b/vendor/github.com/hashicorp/terraform/command/e2etest/provider_dev_test.go @@ -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/provider-simple/main", providerExePrefix) t.Logf("temporary provider executable is %s", providerExe) err := ioutil.WriteFile(filepath.Join(tf.WorkDir(), "dev.tfrc"), []byte(fmt.Sprintf(` @@ -61,7 +61,8 @@ func TestProviderDevOverrides(t *testing.T) { // dev overrides are always ready to use and don't need any special action // to "install" them. This test is mimicking the a happy path of going // directly from "go build" to validate/plan/apply without interacting - // with any registries, mirrors, lock files, etc. + // with any registries, mirrors, lock files, etc. To verify "terraform + // init" does actually show a warning, that behavior is tested at the end. stdout, stderr, err = tf.Run("validate") if err != nil { t.Fatalf("unexpected error: %s\n%s", err, stderr) @@ -73,4 +74,15 @@ func TestProviderDevOverrides(t *testing.T) { if got, want := stdout, `Provider development overrides are in effect`; !strings.Contains(got, want) { t.Errorf("stdout doesn't include the warning about development overrides\nwant: %s\n%s", want, got) } + + stdout, stderr, err = tf.Run("init") + if err == nil { + t.Fatal("expected error: Failed to query available provider packages") + } + if got, want := stdout, `Provider development overrides are in effect`; !strings.Contains(got, want) { + t.Errorf("stdout doesn't include the warning about development overrides\nwant: %s\n%s", want, got) + } + if got, want := stderr, `Failed to query available provider packages`; !strings.Contains(got, want) { + t.Errorf("stderr doesn't include the error about listing unavailable development provider\nwant: %s\n%s", want, got) + } } diff --git a/vendor/github.com/hashicorp/terraform/command/e2etest/provider_plugin_test.go b/vendor/github.com/hashicorp/terraform/command/e2etest/provider_plugin_test.go new file mode 100644 index 00000000..62099fa8 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/e2etest/provider_plugin_test.go @@ -0,0 +1,70 @@ +package e2etest + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/hashicorp/terraform/e2e" + "github.com/hashicorp/terraform/internal/getproviders" +) + +// TestProviderProtocols verifies that Terraform can execute provider plugins +// with both supported protocol versions. +func TestProviderProtocols(t *testing.T) { + t.Parallel() + + tf := e2e.NewBinary(terraformBin, "testdata/provider-plugin") + defer tf.Close() + + // In order to do a decent end-to-end test for this case we will need a real + // enough provider plugin to try to run and make sure we are able to + // actually run it. Here will build the simple and simple6 (built with + // protocol v6) providers. + simple6Provider := filepath.Join(tf.WorkDir(), "terraform-provider-simple6") + simple6ProviderExe := e2e.GoBuild("github.com/hashicorp/terraform/internal/provider-simple-v6/main", simple6Provider) + + simpleProvider := filepath.Join(tf.WorkDir(), "terraform-provider-simple") + simpleProviderExe := e2e.GoBuild("github.com/hashicorp/terraform/internal/provider-simple/main", simpleProvider) + + // Move the provider binaries into a directory that we will point terraform + // to using the -plugin-dir cli flag. + platform := getproviders.CurrentPlatform.String() + hashiDir := "cache/registry.terraform.io/hashicorp/" + if err := os.MkdirAll(tf.Path(hashiDir, "simple6/0.0.1/", platform), os.ModePerm); err != nil { + t.Fatal(err) + } + if err := os.Rename(simple6ProviderExe, tf.Path(hashiDir, "simple6/0.0.1/", platform, "terraform-provider-simple6")); err != nil { + t.Fatal(err) + } + + if err := os.MkdirAll(tf.Path(hashiDir, "simple/0.0.1/", platform), os.ModePerm); err != nil { + t.Fatal(err) + } + if err := os.Rename(simpleProviderExe, tf.Path(hashiDir, "simple/0.0.1/", platform, "terraform-provider-simple")); err != nil { + t.Fatal(err) + } + + //// INIT + _, stderr, err := tf.Run("init", "-plugin-dir=cache") + if err != nil { + t.Fatalf("unexpected init error: %s\nstderr:\n%s", err, stderr) + } + + //// PLAN + _, stderr, err = tf.Run("plan", "-out=tfplan") + if err != nil { + t.Fatalf("unexpected plan error: %s\nstderr:\n%s", err, stderr) + } + + //// APPLY + stdout, stderr, err := tf.Run("apply", "tfplan") + if err != nil { + t.Fatalf("unexpected apply error: %s\nstderr:\n%s", err, stderr) + } + + if !strings.Contains(stdout, "Apply complete! Resources: 2 added, 0 changed, 0 destroyed.") { + t.Fatalf("wrong output:\n%s", stdout) + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/e2etest/providers_mirror_test.go b/vendor/github.com/hashicorp/terraform/command/e2etest/providers_mirror_test.go index 32512a66..3d229e6b 100644 --- a/vendor/github.com/hashicorp/terraform/command/e2etest/providers_mirror_test.go +++ b/vendor/github.com/hashicorp/terraform/command/e2etest/providers_mirror_test.go @@ -54,7 +54,10 @@ func TestTerraformProvidersMirror(t *testing.T) { "registry.terraform.io/hashicorp/template/terraform-provider-template_2.1.1_windows_386.zip", } var got []string - err = filepath.Walk(outputDir, func(path string, info os.FileInfo, err error) error { + walkErr := filepath.Walk(outputDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } if info.IsDir() { return nil // we only care about leaf files for this test } @@ -65,8 +68,8 @@ func TestTerraformProvidersMirror(t *testing.T) { got = append(got, filepath.ToSlash(relPath)) return nil }) - if err != nil { - t.Fatal(err) + if walkErr != nil { + t.Fatal(walkErr) } sort.Strings(got) diff --git a/vendor/github.com/hashicorp/terraform/command/e2etest/provisioner_plugin_test.go b/vendor/github.com/hashicorp/terraform/command/e2etest/provisioner_plugin_test.go new file mode 100644 index 00000000..285df73b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/e2etest/provisioner_plugin_test.go @@ -0,0 +1,65 @@ +package e2etest + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/hashicorp/terraform/e2e" +) + +// TestProvisionerPlugin is a test that terraform can execute a 3rd party +// provisioner plugin. +func TestProvisionerPlugin(t *testing.T) { + t.Parallel() + + // This test reaches out to releases.hashicorp.com to download the + // template and null providers, so it can only run if network access is + // allowed. + skipIfCannotAccessNetwork(t) + + tf := e2e.NewBinary(terraformBin, "testdata/provisioner-plugin") + defer tf.Close() + + // In order to do a decent end-to-end test for this case we will need a + // real enough provisioner plugin to try to run and make sure we are able + // to actually run it. Here will build the local-exec provisioner into a + // binary called test-provisioner + provisionerExePrefix := filepath.Join(tf.WorkDir(), "terraform-provisioner-test_") + provisionerExe := e2e.GoBuild("github.com/hashicorp/terraform/internal/provisioner-local-exec/main", provisionerExePrefix) + + // provisioners must use the old binary name format, so rename this binary + newExe := filepath.Join(tf.WorkDir(), "terraform-provisioner-test") + if _, err := os.Stat(newExe); !os.IsNotExist(err) { + t.Fatalf("%q already exists", newExe) + } + if err := os.Rename(provisionerExe, newExe); err != nil { + t.Fatalf("error renaming provisioner binary: %v", err) + } + provisionerExe = newExe + + t.Logf("temporary provisioner executable is %s", provisionerExe) + + //// INIT + _, stderr, err := tf.Run("init") + if err != nil { + t.Fatalf("unexpected init error: %s\nstderr:\n%s", err, stderr) + } + + //// PLAN + _, stderr, err = tf.Run("plan", "-out=tfplan") + if err != nil { + t.Fatalf("unexpected plan error: %s\nstderr:\n%s", err, stderr) + } + + //// APPLY + stdout, stderr, err := tf.Run("apply", "tfplan") + if err != nil { + t.Fatalf("unexpected apply error: %s\nstderr:\n%s", err, stderr) + } + + if !strings.Contains(stdout, "HelloProvisioner") { + t.Fatalf("missing provisioner output:\n%s", stdout) + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/e2etest/provisioner_test.go b/vendor/github.com/hashicorp/terraform/command/e2etest/provisioner_test.go new file mode 100644 index 00000000..39bfa375 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/e2etest/provisioner_test.go @@ -0,0 +1,44 @@ +package e2etest + +import ( + "strings" + "testing" + + "github.com/hashicorp/terraform/e2e" +) + +// TestProviderDevOverrides is a test that terraform can execute a 3rd party +// provisioner plugin. +func TestProvisioner(t *testing.T) { + t.Parallel() + + // This test reaches out to releases.hashicorp.com to download the + // template and null providers, so it can only run if network access is + // allowed. + skipIfCannotAccessNetwork(t) + + tf := e2e.NewBinary(terraformBin, "testdata/provisioner") + defer tf.Close() + + //// INIT + _, stderr, err := tf.Run("init") + if err != nil { + t.Fatalf("unexpected init error: %s\nstderr:\n%s", err, stderr) + } + + //// PLAN + _, stderr, err = tf.Run("plan", "-out=tfplan") + if err != nil { + t.Fatalf("unexpected plan error: %s\nstderr:\n%s", err, stderr) + } + + //// APPLY + stdout, stderr, err := tf.Run("apply", "tfplan") + if err != nil { + t.Fatalf("unexpected apply error: %s\nstderr:\n%s", err, stderr) + } + + if !strings.Contains(stdout, "HelloProvisioner") { + t.Fatalf("missing provisioner output:\n%s", stdout) + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/e2etest/strip_ansi.go b/vendor/github.com/hashicorp/terraform/command/e2etest/strip_ansi.go new file mode 100644 index 00000000..22b66bae --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/e2etest/strip_ansi.go @@ -0,0 +1,13 @@ +package e2etest + +import ( + "regexp" +) + +const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))" + +var ansiRe = regexp.MustCompile(ansi) + +func stripAnsi(str string) string { + return ansiRe.ReplaceAllString(str, "") +} diff --git a/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/provider-dev-override/provider-dev-override.tf b/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/provider-dev-override/provider-dev-override.tf index 195cb1a3..9c629f72 100644 --- a/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/provider-dev-override/provider-dev-override.tf +++ b/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/provider-dev-override/provider-dev-override.tf @@ -1,14 +1,11 @@ terraform { required_providers { - test = { + simple = { source = "example.com/test/test" version = "2.0.0" } } } -provider "test" { -} - -data "test_data_source" "test" { +data "simple_resource" "test" { } diff --git a/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/provider-not-found-non-default/main.tf b/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/provider-not-found-non-default/main.tf new file mode 100644 index 00000000..fe511273 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/provider-not-found-non-default/main.tf @@ -0,0 +1,7 @@ +terraform { + required_providers { + nonexist = { + source = "teamterraform/nonexist" + } + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/provider-plugin/main.tf b/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/provider-plugin/main.tf new file mode 100644 index 00000000..ea4de021 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/provider-plugin/main.tf @@ -0,0 +1,20 @@ +// the provider-plugin tests uses the -plugin-cache flag so terraform pulls the +// test binaries instead of reaching out to the registry. +terraform { + required_providers { + simple5 = { + source = "registry.terraform.io/hashicorp/simple" + } + simple6 = { + source = "registry.terraform.io/hashicorp/simple6" + } + } +} + +resource "simple_resource" "test-proto5" { + provider = simple5 +} + +resource "simple_resource" "test-proto6" { + provider = simple6 +} diff --git a/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/provisioner-plugin/main.tf b/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/provisioner-plugin/main.tf new file mode 100644 index 00000000..8e6268b9 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/provisioner-plugin/main.tf @@ -0,0 +1,5 @@ +resource "null_resource" "a" { + provisioner "test" { + command = "echo HelloProvisioner" + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/provisioner/main.tf b/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/provisioner/main.tf new file mode 100644 index 00000000..c37ad380 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/provisioner/main.tf @@ -0,0 +1,5 @@ +resource "null_resource" "a" { + provisioner "local-exec" { + command = "echo HelloProvisioner" + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/test-provider/main.tf b/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/test-provider/main.tf index 864643ef..a4de134c 100644 --- a/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/test-provider/main.tf +++ b/vendor/github.com/hashicorp/terraform/command/e2etest/testdata/test-provider/main.tf @@ -1,6 +1,10 @@ -provider "test" { - +terraform { + required_providers { + simple = { + source = "hashicorp/test" + } + } } -resource "test_resource_signal" "test" { +resource "simple_resource" "test" { } diff --git a/vendor/github.com/hashicorp/terraform/command/e2etest/unmanaged_test.go b/vendor/github.com/hashicorp/terraform/command/e2etest/unmanaged_test.go index ab8e19aa..b32748ad 100644 --- a/vendor/github.com/hashicorp/terraform/command/e2etest/unmanaged_test.go +++ b/vendor/github.com/hashicorp/terraform/command/e2etest/unmanaged_test.go @@ -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/grpcwrap" + simple "github.com/hashicorp/terraform/internal/provider-simple" proto "github.com/hashicorp/terraform/internal/tfplugin5" tfplugin "github.com/hashicorp/terraform/plugin" ) @@ -42,7 +42,7 @@ type reattachConfigAddr struct { type providerServer struct { sync.Mutex - *grpcplugin.GRPCProviderServer + proto.ProviderServer planResourceChangeCalled bool applyResourceChangeCalled bool } @@ -52,7 +52,7 @@ func (p *providerServer) PlanResourceChange(ctx context.Context, req *proto.Plan defer p.Unlock() p.planResourceChangeCalled = true - return p.GRPCProviderServer.PlanResourceChange(ctx, req) + return p.ProviderServer.PlanResourceChange(ctx, req) } func (p *providerServer) ApplyResourceChange(ctx context.Context, req *proto.ApplyResourceChange_Request) (*proto.ApplyResourceChange_Response, error) { @@ -60,7 +60,7 @@ func (p *providerServer) ApplyResourceChange(ctx context.Context, req *proto.App defer p.Unlock() p.applyResourceChangeCalled = true - return p.GRPCProviderServer.ApplyResourceChange(ctx, req) + return p.ProviderServer.ApplyResourceChange(ctx, req) } func (p *providerServer) PlanResourceChangeCalled() bool { @@ -99,7 +99,7 @@ func TestUnmanagedSeparatePlan(t *testing.T) { reattachCh := make(chan *plugin.ReattachConfig) closeCh := make(chan struct{}) provider := &providerServer{ - GRPCProviderServer: grpcplugin.NewGRPCProviderServerShim(test.Provider()), + ProviderServer: grpcwrap.Provider(simple.Provider()), } ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -140,6 +140,10 @@ func TestUnmanagedSeparatePlan(t *testing.T) { }, }, }) + if err != nil { + t.Fatal(err) + } + tf.AddEnv("TF_REATTACH_PROVIDERS=" + string(reattachStr)) tf.AddEnv("PLUGIN_PROTOCOL_VERSION=5") @@ -164,7 +168,7 @@ func TestUnmanagedSeparatePlan(t *testing.T) { } if !provider.PlanResourceChangeCalled() { - t.Error("PlanResourceChange not called on in-process provider") + t.Error("PlanResourceChange not called on un-managed provider") } //// APPLY @@ -174,7 +178,7 @@ func TestUnmanagedSeparatePlan(t *testing.T) { } if !provider.ApplyResourceChangeCalled() { - t.Error("ApplyResourceChange not called on in-process provider") + t.Error("ApplyResourceChange not called on un-managed provider") } provider.ResetApplyResourceChangeCalled() diff --git a/vendor/github.com/hashicorp/terraform/command/e2etest/version_test.go b/vendor/github.com/hashicorp/terraform/command/e2etest/version_test.go index 26187729..1f19ecf6 100644 --- a/vendor/github.com/hashicorp/terraform/command/e2etest/version_test.go +++ b/vendor/github.com/hashicorp/terraform/command/e2etest/version_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/hashicorp/terraform/e2e" - tfcore "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/version" ) func TestVersion(t *testing.T) { @@ -31,7 +31,7 @@ func TestVersion(t *testing.T) { t.Errorf("unexpected stderr output:\n%s", stderr) } - wantVersion := fmt.Sprintf("Terraform v%s", tfcore.VersionString()) + wantVersion := fmt.Sprintf("Terraform v%s", version.String()) if !strings.Contains(stdout, wantVersion) { t.Errorf("output does not contain our current version %q:\n%s", wantVersion, stdout) } @@ -63,7 +63,7 @@ func TestVersionWithProvider(t *testing.T) { t.Errorf("unexpected stderr output:\n%s", stderr) } - wantVersion := fmt.Sprintf("Terraform v%s", tfcore.VersionString()) + wantVersion := fmt.Sprintf("Terraform v%s", version.String()) if !strings.Contains(stdout, wantVersion) { t.Errorf("output does not contain our current version %q:\n%s", wantVersion, stdout) } diff --git a/vendor/github.com/hashicorp/terraform/command/flag_kv.go b/vendor/github.com/hashicorp/terraform/command/flag_kv.go index 9f38018a..b084c513 100644 --- a/vendor/github.com/hashicorp/terraform/command/flag_kv.go +++ b/vendor/github.com/hashicorp/terraform/command/flag_kv.go @@ -3,11 +3,6 @@ package command import ( "fmt" "strings" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/tfdiags" ) // FlagStringKV is a flag.Value implementation for parsing user variables @@ -46,34 +41,3 @@ func (v *FlagStringSlice) Set(raw string) error { return nil } - -// FlagTargetSlice is a flag.Value implementation for parsing target addresses -// from the command line, such as -target=aws_instance.foo -target=aws_vpc.bar . -type FlagTargetSlice []addrs.Targetable - -func (v *FlagTargetSlice) String() string { - return "" -} - -func (v *FlagTargetSlice) Set(raw string) error { - // FIXME: This is not an ideal way to deal with this because it requires - // us to do parsing in a context where we can't nicely return errors - // to the user. - - var diags tfdiags.Diagnostics - synthFilename := fmt.Sprintf("-target=%q", raw) - traversal, syntaxDiags := hclsyntax.ParseTraversalAbs([]byte(raw), synthFilename, hcl.Pos{Line: 1, Column: 1}) - diags = diags.Append(syntaxDiags) - if syntaxDiags.HasErrors() { - return diags.Err() - } - - target, targetDiags := addrs.ParseTarget(traversal) - diags = diags.Append(targetDiags) - if targetDiags.HasErrors() { - return diags.Err() - } - - *v = append(*v, target.Subject) - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/command/fmt.go b/vendor/github.com/hashicorp/terraform/command/fmt.go index 13e84a21..d133e307 100644 --- a/vendor/github.com/hashicorp/terraform/command/fmt.go +++ b/vendor/github.com/hashicorp/terraform/command/fmt.go @@ -366,7 +366,41 @@ func (c *FmtCommand) formatValueExpr(tokens hclwrite.Tokens) hclwrite.Tokens { // "${ // foo // }" - return c.trimNewlines(inside) + trimmed := c.trimNewlines(inside) + + // Finally, we check if the unwrapped expression is on multiple lines. If + // so, we ensure that it is surrounded by parenthesis to make sure that it + // parses correctly after unwrapping. This may be redundant in some cases, + // but is required for at least multi-line ternary expressions. + isMultiLine := false + hasLeadingParen := false + hasTrailingParen := false + for i, token := range trimmed { + switch { + case i == 0 && token.Type == hclsyntax.TokenOParen: + hasLeadingParen = true + case token.Type == hclsyntax.TokenNewline: + isMultiLine = true + case i == len(trimmed)-1 && token.Type == hclsyntax.TokenCParen: + hasTrailingParen = true + } + } + if isMultiLine && !(hasLeadingParen && hasTrailingParen) { + wrapped := make(hclwrite.Tokens, 0, len(trimmed)+2) + wrapped = append(wrapped, &hclwrite.Token{ + Type: hclsyntax.TokenOParen, + Bytes: []byte("("), + }) + wrapped = append(wrapped, trimmed...) + wrapped = append(wrapped, &hclwrite.Token{ + Type: hclsyntax.TokenCParen, + Bytes: []byte(")"), + }) + + return wrapped + } + + return trimmed } func (c *FmtCommand) formatTypeExpr(tokens hclwrite.Tokens) hclwrite.Tokens { @@ -494,7 +528,7 @@ func (c *FmtCommand) trimNewlines(tokens hclwrite.Tokens) hclwrite.Tokens { func (c *FmtCommand) Help() string { helpText := ` -Usage: terraform fmt [options] [DIR] +Usage: terraform [global options] fmt [options] [DIR] Rewrites all Terraform configuration files to a canonical format. Both configuration files (.tf) and variables files (.tfvars) are updated. @@ -526,7 +560,7 @@ Options: } func (c *FmtCommand) Synopsis() string { - return "Rewrites config files to canonical format" + return "Reformat your configuration in the standard style" } func bytesDiff(b1, b2 []byte, path string) (data []byte, err error) { diff --git a/vendor/github.com/hashicorp/terraform/command/format/diagnostic.go b/vendor/github.com/hashicorp/terraform/command/format/diagnostic.go index 38626e30..2d8522de 100644 --- a/vendor/github.com/hashicorp/terraform/command/format/diagnostic.go +++ b/vendor/github.com/hashicorp/terraform/command/format/diagnostic.go @@ -7,15 +7,18 @@ import ( "sort" "strings" - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hcled" - "github.com/hashicorp/hcl/v2/hclparse" + viewsjson "github.com/hashicorp/terraform/command/views/json" "github.com/hashicorp/terraform/tfdiags" + "github.com/mitchellh/colorstring" wordwrap "github.com/mitchellh/go-wordwrap" - "github.com/zclconf/go-cty/cty" ) +var disabledColorize = &colorstring.Colorize{ + Colors: colorstring.DefaultColors, + Disable: true, +} + // Diagnostic formats a single diagnostic message. // // The width argument specifies at what column the diagnostic messages will @@ -24,6 +27,10 @@ import ( // not all aspects of the message are guaranteed to fit within the specified // terminal width. func Diagnostic(diag tfdiags.Diagnostic, sources map[string][]byte, color *colorstring.Colorize, width int) string { + return DiagnosticFromJSON(viewsjson.NewDiagnostic(diag, sources), color, width) +} + +func DiagnosticFromJSON(diag *viewsjson.Diagnostic, color *colorstring.Colorize, width int) string { if diag == nil { // No good reason to pass a nil diagnostic in here... return "" @@ -31,160 +38,129 @@ func Diagnostic(diag tfdiags.Diagnostic, sources map[string][]byte, color *color var buf bytes.Buffer - switch diag.Severity() { - case tfdiags.Error: - buf.WriteString(color.Color("\n[bold][red]Error: [reset]")) - case tfdiags.Warning: - buf.WriteString(color.Color("\n[bold][yellow]Warning: [reset]")) + // these leftRule* variables are markers for the beginning of the lines + // containing the diagnostic that are intended to help sighted users + // better understand the information hierarchy when diagnostics appear + // alongside other information or alongside other diagnostics. + // + // Without this, it seems (based on folks sharing incomplete messages when + // asking questions, or including extra content that's not part of the + // diagnostic) that some readers have trouble easily identifying which + // text belongs to the diagnostic and which does not. + var leftRuleLine, leftRuleStart, leftRuleEnd string + var leftRuleWidth int // in visual character cells + + switch diag.Severity { + case viewsjson.DiagnosticSeverityError: + buf.WriteString(color.Color("[bold][red]Error: [reset]")) + leftRuleLine = color.Color("[red]│[reset] ") + leftRuleStart = color.Color("[red]╷[reset]") + leftRuleEnd = color.Color("[red]╵[reset]") + leftRuleWidth = 2 + case viewsjson.DiagnosticSeverityWarning: + buf.WriteString(color.Color("[bold][yellow]Warning: [reset]")) + leftRuleLine = color.Color("[yellow]│[reset] ") + leftRuleStart = color.Color("[yellow]╷[reset]") + leftRuleEnd = color.Color("[yellow]╵[reset]") + leftRuleWidth = 2 default: // Clear out any coloring that might be applied by Terraform's UI helper, // so our result is not context-sensitive. buf.WriteString(color.Color("\n[reset]")) } - desc := diag.Description() - sourceRefs := diag.Source() - // We don't wrap the summary, since we expect it to be terse, and since // this is where we put the text of a native Go error it may not always // be pure text that lends itself well to word-wrapping. - fmt.Fprintf(&buf, color.Color("[bold]%s[reset]\n\n"), desc.Summary) - - if sourceRefs.Subject != nil { - // We'll borrow HCL's range implementation here, because it has some - // handy features to help us produce a nice source code snippet. - highlightRange := sourceRefs.Subject.ToHCL() - snippetRange := highlightRange - if sourceRefs.Context != nil { - snippetRange = sourceRefs.Context.ToHCL() - } + fmt.Fprintf(&buf, color.Color("[bold]%s[reset]\n\n"), diag.Summary) - // Make sure the snippet includes the highlight. This should be true - // for any reasonable diagnostic, but we'll make sure. - snippetRange = hcl.RangeOver(snippetRange, highlightRange) - if snippetRange.Empty() { - snippetRange.End.Byte++ - snippetRange.End.Column++ - } - if highlightRange.Empty() { - highlightRange.End.Byte++ - highlightRange.End.Column++ - } + appendSourceSnippets(&buf, diag, color) - var src []byte - if sources != nil { - src = sources[snippetRange.Filename] - } - if src == nil { - // This should generally not happen, as long as sources are always - // loaded through the main loader. We may load things in other - // ways in weird cases, so we'll tolerate it at the expense of - // a not-so-helpful error message. - fmt.Fprintf(&buf, " on %s line %d:\n (source code not available)\n", highlightRange.Filename, highlightRange.Start.Line) - } else { - file, offset := parseRange(src, highlightRange) - - headerRange := highlightRange - - contextStr := hcled.ContextString(file, offset-1) - if contextStr != "" { - contextStr = ", in " + contextStr - } - - fmt.Fprintf(&buf, " on %s line %d%s:\n", headerRange.Filename, headerRange.Start.Line, contextStr) - - // Config snippet rendering - sc := hcl.NewRangeScanner(src, highlightRange.Filename, bufio.ScanLines) - for sc.Scan() { - lineRange := sc.Range() - if !lineRange.Overlaps(snippetRange) { - continue - } - if !lineRange.Overlap(highlightRange).Empty() { - beforeRange, highlightedRange, afterRange := lineRange.PartitionAround(highlightRange) - before := beforeRange.SliceBytes(src) - highlighted := highlightedRange.SliceBytes(src) - after := afterRange.SliceBytes(src) - fmt.Fprintf( - &buf, color.Color("%4d: %s[underline]%s[reset]%s\n"), - lineRange.Start.Line, - before, highlighted, after, - ) - } else { - fmt.Fprintf( - &buf, "%4d: %s\n", - lineRange.Start.Line, - lineRange.SliceBytes(src), - ) + if diag.Detail != "" { + paraWidth := width - leftRuleWidth - 1 // leave room for the left rule + if paraWidth > 0 { + lines := strings.Split(diag.Detail, "\n") + for _, line := range lines { + if !strings.HasPrefix(line, " ") { + line = wordwrap.WrapString(line, uint(paraWidth)) } + fmt.Fprintf(&buf, "%s\n", line) } + } else { + fmt.Fprintf(&buf, "%s\n", diag.Detail) + } + } + // Before we return, we'll finally add the left rule prefixes to each + // line so that the overall message is visually delimited from what's + // around it. We'll do that by scanning over what we already generated + // and adding the prefix for each line. + var ruleBuf strings.Builder + sc := bufio.NewScanner(&buf) + ruleBuf.WriteString(leftRuleStart) + ruleBuf.WriteByte('\n') + for sc.Scan() { + line := sc.Text() + prefix := leftRuleLine + if line == "" { + // Don't print the space after the line if there would be nothing + // after it anyway. + prefix = strings.TrimSpace(prefix) } + ruleBuf.WriteString(prefix) + ruleBuf.WriteString(line) + ruleBuf.WriteByte('\n') + } + ruleBuf.WriteString(leftRuleEnd) + ruleBuf.WriteByte('\n') - if fromExpr := diag.FromExpr(); fromExpr != nil { - // We may also be able to generate information about the dynamic - // values of relevant variables at the point of evaluation, then. - // This is particularly useful for expressions that get evaluated - // multiple times with different values, such as blocks using - // "count" and "for_each", or within "for" expressions. - expr := fromExpr.Expression - ctx := fromExpr.EvalContext - vars := expr.Variables() - stmts := make([]string, 0, len(vars)) - seen := make(map[string]struct{}, len(vars)) - Traversals: - for _, traversal := range vars { - for len(traversal) > 1 { - val, diags := traversal.TraverseAbs(ctx) - if diags.HasErrors() { - // Skip anything that generates errors, since we probably - // already have the same error in our diagnostics set - // already. - traversal = traversal[:len(traversal)-1] - continue - } - - traversalStr := traversalStr(traversal) - if _, exists := seen[traversalStr]; exists { - continue Traversals // don't show duplicates when the same variable is referenced multiple times - } - switch { - case !val.IsKnown(): - // Can't say anything about this yet, then. - continue Traversals - case val.IsNull(): - stmts = append(stmts, fmt.Sprintf(color.Color("[bold]%s[reset] is null"), traversalStr)) - default: - stmts = append(stmts, fmt.Sprintf(color.Color("[bold]%s[reset] is %s"), traversalStr, compactValueStr(val))) - } - seen[traversalStr] = struct{}{} - } - } + return ruleBuf.String() +} - sort.Strings(stmts) // FIXME: Should maybe use a traversal-aware sort that can sort numeric indexes properly? +// DiagnosticPlain is an alternative to Diagnostic which minimises the use of +// virtual terminal formatting sequences. +// +// It is intended for use in automation and other contexts in which diagnostic +// messages are parsed from the Terraform output. +func DiagnosticPlain(diag tfdiags.Diagnostic, sources map[string][]byte, width int) string { + return DiagnosticPlainFromJSON(viewsjson.NewDiagnostic(diag, sources), width) +} - if len(stmts) > 0 { - fmt.Fprint(&buf, color.Color(" [dark_gray]|----------------[reset]\n")) - } - for _, stmt := range stmts { - fmt.Fprintf(&buf, color.Color(" [dark_gray]|[reset] %s\n"), stmt) - } - } +func DiagnosticPlainFromJSON(diag *viewsjson.Diagnostic, width int) string { + if diag == nil { + // No good reason to pass a nil diagnostic in here... + return "" + } + + var buf bytes.Buffer - buf.WriteByte('\n') + switch diag.Severity { + case viewsjson.DiagnosticSeverityError: + buf.WriteString("\nError: ") + case viewsjson.DiagnosticSeverityWarning: + buf.WriteString("\nWarning: ") + default: + buf.WriteString("\n") } - if desc.Detail != "" { - if width != 0 { - lines := strings.Split(desc.Detail, "\n") + // We don't wrap the summary, since we expect it to be terse, and since + // this is where we put the text of a native Go error it may not always + // be pure text that lends itself well to word-wrapping. + fmt.Fprintf(&buf, "%s\n\n", diag.Summary) + + appendSourceSnippets(&buf, diag, disabledColorize) + + if diag.Detail != "" { + if width > 1 { + lines := strings.Split(diag.Detail, "\n") for _, line := range lines { if !strings.HasPrefix(line, " ") { - line = wordwrap.WrapString(line, uint(width)) + line = wordwrap.WrapString(line, uint(width-1)) } fmt.Fprintf(&buf, "%s\n", line) } } else { - fmt.Fprintf(&buf, "%s\n", desc.Detail) + fmt.Fprintf(&buf, "%s\n", diag.Detail) } } @@ -236,123 +212,75 @@ func DiagnosticWarningsCompact(diags tfdiags.Diagnostics, color *colorstring.Col return b.String() } -func parseRange(src []byte, rng hcl.Range) (*hcl.File, int) { - filename := rng.Filename - offset := rng.Start.Byte - - // We need to re-parse here to get a *hcl.File we can interrogate. This - // is not awesome since we presumably already parsed the file earlier too, - // but this re-parsing is architecturally simpler than retaining all of - // the hcl.File objects and we only do this in the case of an error anyway - // so the overhead here is not a big problem. - parser := hclparse.NewParser() - var file *hcl.File - var diags hcl.Diagnostics - if strings.HasSuffix(filename, ".json") { - file, diags = parser.ParseJSON(src, filename) - } else { - file, diags = parser.ParseHCL(src, filename) - } - if diags.HasErrors() { - return file, offset +func appendSourceSnippets(buf *bytes.Buffer, diag *viewsjson.Diagnostic, color *colorstring.Colorize) { + if diag.Address != "" { + fmt.Fprintf(buf, " with %s,\n", diag.Address) } - return file, offset -} + if diag.Range == nil { + return + } -// traversalStr produces a representation of an HCL traversal that is compact, -// resembles HCL native syntax, and is suitable for display in the UI. -func traversalStr(traversal hcl.Traversal) string { - // This is a specialized subset of traversal rendering tailored to - // producing helpful contextual messages in diagnostics. It is not - // comprehensive nor intended to be used for other purposes. + if diag.Snippet == nil { + // This should generally not happen, as long as sources are always + // loaded through the main loader. We may load things in other + // ways in weird cases, so we'll tolerate it at the expense of + // a not-so-helpful error message. + fmt.Fprintf(buf, " on %s line %d:\n (source code not available)\n", diag.Range.Filename, diag.Range.Start.Line) + } else { + snippet := diag.Snippet + code := snippet.Code - var buf bytes.Buffer - for _, step := range traversal { - switch tStep := step.(type) { - case hcl.TraverseRoot: - buf.WriteString(tStep.Name) - case hcl.TraverseAttr: - buf.WriteByte('.') - buf.WriteString(tStep.Name) - case hcl.TraverseIndex: - buf.WriteByte('[') - if keyTy := tStep.Key.Type(); keyTy.IsPrimitiveType() { - buf.WriteString(compactValueStr(tStep.Key)) - } else { - // We'll just use a placeholder for more complex values, - // since otherwise our result could grow ridiculously long. - buf.WriteString("...") + var contextStr string + if snippet.Context != nil { + contextStr = fmt.Sprintf(", in %s", *snippet.Context) + } + fmt.Fprintf(buf, " on %s line %d%s:\n", diag.Range.Filename, diag.Range.Start.Line, contextStr) + + // Split the snippet and render the highlighted section with underlines + start := snippet.HighlightStartOffset + end := snippet.HighlightEndOffset + + // Only buggy diagnostics can have an end range before the start, but + // we need to ensure we don't crash here if that happens. + if end < start { + end = start + 1 + if end > len(code) { + end = len(code) } - buf.WriteByte(']') } - } - return buf.String() -} -// compactValueStr produces a compact, single-line summary of a given value -// that is suitable for display in the UI. -// -// For primitives it returns a full representation, while for more complex -// types it instead summarizes the type, size, etc to produce something -// that is hopefully still somewhat useful but not as verbose as a rendering -// of the entire data structure. -func compactValueStr(val cty.Value) string { - // This is a specialized subset of value rendering tailored to producing - // helpful but concise messages in diagnostics. It is not comprehensive - // nor intended to be used for other purposes. - - if val.ContainsMarked() { - return "(sensitive value)" - } - - ty := val.Type() - switch { - case val.IsNull(): - return "null" - case !val.IsKnown(): - // Should never happen here because we should filter before we get - // in here, but we'll do something reasonable rather than panic. - return "(not yet known)" - case ty == cty.Bool: - if val.True() { - return "true" - } - return "false" - case ty == cty.Number: - bf := val.AsBigFloat() - return bf.Text('g', 10) - case ty == cty.String: - // Go string syntax is not exactly the same as HCL native string syntax, - // but we'll accept the minor edge-cases where this is different here - // for now, just to get something reasonable here. - return fmt.Sprintf("%q", val.AsString()) - case ty.IsCollectionType() || ty.IsTupleType(): - l := val.LengthInt() - switch l { - case 0: - return "empty " + ty.FriendlyName() - case 1: - return ty.FriendlyName() + " with 1 element" - default: - return fmt.Sprintf("%s with %d elements", ty.FriendlyName(), l) + before, highlight, after := code[0:start], code[start:end], code[end:] + code = fmt.Sprintf(color.Color("%s[underline]%s[reset]%s"), before, highlight, after) + + // Split the snippet into lines and render one at a time + lines := strings.Split(code, "\n") + for i, line := range lines { + fmt.Fprintf( + buf, "%4d: %s\n", + snippet.StartLine+i, + line, + ) } - case ty.IsObjectType(): - atys := ty.AttributeTypes() - l := len(atys) - switch l { - case 0: - return "object with no attributes" - case 1: - var name string - for k := range atys { - name = k + + if len(snippet.Values) > 0 { + // The diagnostic may also have information about the dynamic + // values of relevant variables at the point of evaluation. + // This is particularly useful for expressions that get evaluated + // multiple times with different values, such as blocks using + // "count" and "for_each", or within "for" expressions. + values := make([]viewsjson.DiagnosticExpressionValue, len(snippet.Values)) + copy(values, snippet.Values) + sort.Slice(values, func(i, j int) bool { + return values[i].Traversal < values[j].Traversal + }) + + fmt.Fprint(buf, color.Color(" [dark_gray]├────────────────[reset]\n")) + for _, value := range values { + fmt.Fprintf(buf, color.Color(" [dark_gray]│[reset] [bold]%s[reset] %s\n"), value.Traversal, value.Statement) } - return fmt.Sprintf("object with 1 attribute %q", name) - default: - return fmt.Sprintf("object with %d attributes", l) } - default: - return ty.FriendlyName() } + + buf.WriteByte('\n') } diff --git a/vendor/github.com/hashicorp/terraform/command/format/diagnostic_test.go b/vendor/github.com/hashicorp/terraform/command/format/diagnostic_test.go index 2062f633..6680442d 100644 --- a/vendor/github.com/hashicorp/terraform/command/format/diagnostic_test.go +++ b/vendor/github.com/hashicorp/terraform/command/format/diagnostic_test.go @@ -1,15 +1,443 @@ package format import ( + "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hcltest" "github.com/mitchellh/colorstring" + "github.com/zclconf/go-cty/cty" + + viewsjson "github.com/hashicorp/terraform/command/views/json" "github.com/hashicorp/terraform/tfdiags" ) +func TestDiagnostic(t *testing.T) { + + tests := map[string]struct { + Diag interface{} + Want string + }{ + "sourceless error": { + tfdiags.Sourceless( + tfdiags.Error, + "A sourceless error", + "It has no source references but it does have a pretty long detail that should wrap over multiple lines.", + ), + `[red]╷[reset] +[red]│[reset] [bold][red]Error: [reset][bold]A sourceless error[reset] +[red]│[reset] +[red]│[reset] It has no source references but it +[red]│[reset] does have a pretty long detail that +[red]│[reset] should wrap over multiple lines. +[red]╵[reset] +`, + }, + "sourceless warning": { + tfdiags.Sourceless( + tfdiags.Warning, + "A sourceless warning", + "It has no source references but it does have a pretty long detail that should wrap over multiple lines.", + ), + `[yellow]╷[reset] +[yellow]│[reset] [bold][yellow]Warning: [reset][bold]A sourceless warning[reset] +[yellow]│[reset] +[yellow]│[reset] It has no source references but it +[yellow]│[reset] does have a pretty long detail that +[yellow]│[reset] should wrap over multiple lines. +[yellow]╵[reset] +`, + }, + "error with source code subject": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Bad bad bad", + Detail: "Whatever shall we do?", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 6, Byte: 5}, + End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, + }, + }, + `[red]╷[reset] +[red]│[reset] [bold][red]Error: [reset][bold]Bad bad bad[reset] +[red]│[reset] +[red]│[reset] on test.tf line 1: +[red]│[reset] 1: test [underline]source[reset] code +[red]│[reset] +[red]│[reset] Whatever shall we do? +[red]╵[reset] +`, + }, + "error with source code subject and known expression": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Bad bad bad", + Detail: "Whatever shall we do?", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 6, Byte: 5}, + End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, + }, + Expression: hcltest.MockExprTraversal(hcl.Traversal{ + hcl.TraverseRoot{Name: "boop"}, + hcl.TraverseAttr{Name: "beep"}, + }), + EvalContext: &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "boop": cty.ObjectVal(map[string]cty.Value{ + "beep": cty.StringVal("blah"), + }), + }, + }, + }, + `[red]╷[reset] +[red]│[reset] [bold][red]Error: [reset][bold]Bad bad bad[reset] +[red]│[reset] +[red]│[reset] on test.tf line 1: +[red]│[reset] 1: test [underline]source[reset] code +[red]│[reset] [dark_gray]├────────────────[reset] +[red]│[reset] [dark_gray]│[reset] [bold]boop.beep[reset] is "blah" +[red]│[reset] +[red]│[reset] Whatever shall we do? +[red]╵[reset] +`, + }, + "error with source code subject and expression referring to sensitive value": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Bad bad bad", + Detail: "Whatever shall we do?", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 6, Byte: 5}, + End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, + }, + Expression: hcltest.MockExprTraversal(hcl.Traversal{ + hcl.TraverseRoot{Name: "boop"}, + hcl.TraverseAttr{Name: "beep"}, + }), + EvalContext: &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "boop": cty.ObjectVal(map[string]cty.Value{ + "beep": cty.StringVal("blah").Mark("sensitive"), + }), + }, + }, + }, + `[red]╷[reset] +[red]│[reset] [bold][red]Error: [reset][bold]Bad bad bad[reset] +[red]│[reset] +[red]│[reset] on test.tf line 1: +[red]│[reset] 1: test [underline]source[reset] code +[red]│[reset] [dark_gray]├────────────────[reset] +[red]│[reset] [dark_gray]│[reset] [bold]boop.beep[reset] has a sensitive value +[red]│[reset] +[red]│[reset] Whatever shall we do? +[red]╵[reset] +`, + }, + "error with source code subject and unknown string expression": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Bad bad bad", + Detail: "Whatever shall we do?", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 6, Byte: 5}, + End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, + }, + Expression: hcltest.MockExprTraversal(hcl.Traversal{ + hcl.TraverseRoot{Name: "boop"}, + hcl.TraverseAttr{Name: "beep"}, + }), + EvalContext: &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "boop": cty.ObjectVal(map[string]cty.Value{ + "beep": cty.UnknownVal(cty.String), + }), + }, + }, + }, + `[red]╷[reset] +[red]│[reset] [bold][red]Error: [reset][bold]Bad bad bad[reset] +[red]│[reset] +[red]│[reset] on test.tf line 1: +[red]│[reset] 1: test [underline]source[reset] code +[red]│[reset] [dark_gray]├────────────────[reset] +[red]│[reset] [dark_gray]│[reset] [bold]boop.beep[reset] is a string, known only after apply +[red]│[reset] +[red]│[reset] Whatever shall we do? +[red]╵[reset] +`, + }, + "error with source code subject and unknown expression of unknown type": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Bad bad bad", + Detail: "Whatever shall we do?", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 6, Byte: 5}, + End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, + }, + Expression: hcltest.MockExprTraversal(hcl.Traversal{ + hcl.TraverseRoot{Name: "boop"}, + hcl.TraverseAttr{Name: "beep"}, + }), + EvalContext: &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "boop": cty.ObjectVal(map[string]cty.Value{ + "beep": cty.UnknownVal(cty.DynamicPseudoType), + }), + }, + }, + }, + `[red]╷[reset] +[red]│[reset] [bold][red]Error: [reset][bold]Bad bad bad[reset] +[red]│[reset] +[red]│[reset] on test.tf line 1: +[red]│[reset] 1: test [underline]source[reset] code +[red]│[reset] [dark_gray]├────────────────[reset] +[red]│[reset] [dark_gray]│[reset] [bold]boop.beep[reset] will be known only after apply +[red]│[reset] +[red]│[reset] Whatever shall we do? +[red]╵[reset] +`, + }, + } + + sources := map[string][]byte{ + "test.tf": []byte(`test source code`), + } + + // This empty Colorize just passes through all of the formatting codes + // untouched, because it doesn't define any formatting keywords. + colorize := &colorstring.Colorize{} + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + var diags tfdiags.Diagnostics + diags = diags.Append(test.Diag) // to normalize it into a tfdiag.Diagnostic + diag := diags[0] + got := strings.TrimSpace(Diagnostic(diag, sources, colorize, 40)) + want := strings.TrimSpace(test.Want) + if got != want { + t.Errorf("wrong result\ngot:\n%s\n\nwant:\n%s\n\n", got, want) + } + }) + } +} + +func TestDiagnosticPlain(t *testing.T) { + + tests := map[string]struct { + Diag interface{} + Want string + }{ + "sourceless error": { + tfdiags.Sourceless( + tfdiags.Error, + "A sourceless error", + "It has no source references but it does have a pretty long detail that should wrap over multiple lines.", + ), + ` +Error: A sourceless error + +It has no source references but it does +have a pretty long detail that should +wrap over multiple lines. +`, + }, + "sourceless warning": { + tfdiags.Sourceless( + tfdiags.Warning, + "A sourceless warning", + "It has no source references but it does have a pretty long detail that should wrap over multiple lines.", + ), + ` +Warning: A sourceless warning + +It has no source references but it does +have a pretty long detail that should +wrap over multiple lines. +`, + }, + "error with source code subject": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Bad bad bad", + Detail: "Whatever shall we do?", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 6, Byte: 5}, + End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, + }, + }, + ` +Error: Bad bad bad + + on test.tf line 1: + 1: test source code + +Whatever shall we do? +`, + }, + "error with source code subject and known expression": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Bad bad bad", + Detail: "Whatever shall we do?", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 6, Byte: 5}, + End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, + }, + Expression: hcltest.MockExprTraversal(hcl.Traversal{ + hcl.TraverseRoot{Name: "boop"}, + hcl.TraverseAttr{Name: "beep"}, + }), + EvalContext: &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "boop": cty.ObjectVal(map[string]cty.Value{ + "beep": cty.StringVal("blah"), + }), + }, + }, + }, + ` +Error: Bad bad bad + + on test.tf line 1: + 1: test source code + ├──────────────── + │ boop.beep is "blah" + +Whatever shall we do? +`, + }, + "error with source code subject and expression referring to sensitive value": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Bad bad bad", + Detail: "Whatever shall we do?", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 6, Byte: 5}, + End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, + }, + Expression: hcltest.MockExprTraversal(hcl.Traversal{ + hcl.TraverseRoot{Name: "boop"}, + hcl.TraverseAttr{Name: "beep"}, + }), + EvalContext: &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "boop": cty.ObjectVal(map[string]cty.Value{ + "beep": cty.StringVal("blah").Mark("sensitive"), + }), + }, + }, + }, + ` +Error: Bad bad bad + + on test.tf line 1: + 1: test source code + ├──────────────── + │ boop.beep has a sensitive value + +Whatever shall we do? +`, + }, + "error with source code subject and unknown string expression": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Bad bad bad", + Detail: "Whatever shall we do?", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 6, Byte: 5}, + End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, + }, + Expression: hcltest.MockExprTraversal(hcl.Traversal{ + hcl.TraverseRoot{Name: "boop"}, + hcl.TraverseAttr{Name: "beep"}, + }), + EvalContext: &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "boop": cty.ObjectVal(map[string]cty.Value{ + "beep": cty.UnknownVal(cty.String), + }), + }, + }, + }, + ` +Error: Bad bad bad + + on test.tf line 1: + 1: test source code + ├──────────────── + │ boop.beep is a string, known only after apply + +Whatever shall we do? +`, + }, + "error with source code subject and unknown expression of unknown type": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Bad bad bad", + Detail: "Whatever shall we do?", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 6, Byte: 5}, + End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, + }, + Expression: hcltest.MockExprTraversal(hcl.Traversal{ + hcl.TraverseRoot{Name: "boop"}, + hcl.TraverseAttr{Name: "beep"}, + }), + EvalContext: &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "boop": cty.ObjectVal(map[string]cty.Value{ + "beep": cty.UnknownVal(cty.DynamicPseudoType), + }), + }, + }, + }, + ` +Error: Bad bad bad + + on test.tf line 1: + 1: test source code + ├──────────────── + │ boop.beep will be known only after apply + +Whatever shall we do? +`, + }, + } + + sources := map[string][]byte{ + "test.tf": []byte(`test source code`), + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + var diags tfdiags.Diagnostics + diags = diags.Append(test.Diag) // to normalize it into a tfdiag.Diagnostic + diag := diags[0] + got := strings.TrimSpace(DiagnosticPlain(diag, sources, 40)) + want := strings.TrimSpace(test.Want) + if got != want { + t.Errorf("wrong result\ngot:\n%s\n\nwant:\n%s\n\n", got, want) + } + }) + } +} + func TestDiagnosticWarningsCompact(t *testing.T) { var diags tfdiags.Diagnostics diags = diags.Append(tfdiags.SimpleWarning("foo")) @@ -103,16 +531,17 @@ func TestDiagnostic_nonOverlappingHighlightContext(t *testing.T) { Reset: true, Disable: true, } - expected := ` -Error: Some error - - on source.tf line 1: - 1: x = somefunc("testing", { - 2: alpha = "foo" - 3: beta = "bar" - 4: }) - -... + expected := `╷ +│ Error: Some error +│ +│ on source.tf line 1: +│ 1: x = somefunc("testing", { +│ 2: alpha = "foo" +│ 3: beta = "bar" +│ 4: }) +│ +│ ... +╵ ` output := Diagnostic(diags[0], sources, color, 80) @@ -151,6 +580,50 @@ func TestDiagnostic_emptyOverlapHighlightContext(t *testing.T) { Reset: true, Disable: true, } + expected := `╷ +│ Error: Some error +│ +│ on source.tf line 3, in variable "x": +│ 2: default = { +│ 3: "foo" +│ 4: } +│ +│ ... +╵ +` + output := Diagnostic(diags[0], sources, color, 80) + + if output != expected { + t.Fatalf("unexpected output: got:\n%s\nwant\n%s\n", output, expected) + } +} + +func TestDiagnosticPlain_emptyOverlapHighlightContext(t *testing.T) { + var diags tfdiags.Diagnostics + + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Some error", + Detail: "...", + Subject: &hcl.Range{ + Filename: "source.tf", + Start: hcl.Pos{Line: 3, Column: 10, Byte: 38}, + End: hcl.Pos{Line: 4, Column: 1, Byte: 39}, + }, + Context: &hcl.Range{ + Filename: "source.tf", + Start: hcl.Pos{Line: 2, Column: 13, Byte: 27}, + End: hcl.Pos{Line: 4, Column: 1, Byte: 39}, + }, + }) + sources := map[string][]byte{ + "source.tf": []byte(`variable "x" { + default = { + "foo" + } +`), + } + expected := ` Error: Some error @@ -161,7 +634,7 @@ Error: Some error ... ` - output := Diagnostic(diags[0], sources, color, 80) + output := DiagnosticPlain(diags[0], sources, 80) if output != expected { t.Fatalf("unexpected output: got:\n%s\nwant\n%s\n", output, expected) @@ -181,6 +654,35 @@ func TestDiagnostic_wrapDetailIncludingCommand(t *testing.T) { Reset: true, Disable: true, } + expected := `╷ +│ Error: Everything went wrong +│ +│ This is a very long sentence about whatever went wrong which is supposed +│ to wrap onto multiple lines. Thank-you very much for listening. +│ +│ To fix this, run this very long command: +│ terraform read-my-mind -please -thanks -but-do-not-wrap-this-line-because-it-is-prefixed-with-spaces +│ +│ Here is a coda which is also long enough to wrap and so it should +│ eventually make it onto multiple lines. THE END +╵ +` + output := Diagnostic(diags[0], nil, color, 76) + + if output != expected { + t.Fatalf("unexpected output: got:\n%s\nwant\n%s\n", output, expected) + } +} + +func TestDiagnosticPlain_wrapDetailIncludingCommand(t *testing.T) { + var diags tfdiags.Diagnostics + + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Everything went wrong", + Detail: "This is a very long sentence about whatever went wrong which is supposed to wrap onto multiple lines. Thank-you very much for listening.\n\nTo fix this, run this very long command:\n terraform read-my-mind -please -thanks -but-do-not-wrap-this-line-because-it-is-prefixed-with-spaces\n\nHere is a coda which is also long enough to wrap and so it should eventually make it onto multiple lines. THE END", + }) + expected := ` Error: Everything went wrong @@ -190,12 +692,65 @@ wrap onto multiple lines. Thank-you very much for listening. To fix this, run this very long command: terraform read-my-mind -please -thanks -but-do-not-wrap-this-line-because-it-is-prefixed-with-spaces -Here is a coda which is also long enough to wrap and so it should eventually -make it onto multiple lines. THE END +Here is a coda which is also long enough to wrap and so it should +eventually make it onto multiple lines. THE END ` - output := Diagnostic(diags[0], nil, color, 76) + output := DiagnosticPlain(diags[0], nil, 76) if output != expected { t.Fatalf("unexpected output: got:\n%s\nwant\n%s\n", output, expected) } } + +// Test cases covering invalid JSON diagnostics which should still render +// correctly. These JSON diagnostic values cannot be generated from the +// json.NewDiagnostic code path, but we may read and display JSON diagnostics +// in future from other sources. +func TestDiagnosticFromJSON_invalid(t *testing.T) { + tests := map[string]struct { + Diag *viewsjson.Diagnostic + Want string + }{ + "zero-value end range and highlight end byte": { + &viewsjson.Diagnostic{ + Severity: viewsjson.DiagnosticSeverityError, + Summary: "Bad end", + Detail: "It all went wrong.", + Range: &viewsjson.DiagnosticRange{ + Filename: "ohno.tf", + Start: viewsjson.Pos{Line: 1, Column: 23, Byte: 22}, + End: viewsjson.Pos{Line: 0, Column: 0, Byte: 0}, + }, + Snippet: &viewsjson.DiagnosticSnippet{ + Code: `resource "foo_bar "baz" {`, + StartLine: 1, + HighlightStartOffset: 22, + HighlightEndOffset: 0, + }, + }, + `[red]╷[reset] +[red]│[reset] [bold][red]Error: [reset][bold]Bad end[reset] +[red]│[reset] +[red]│[reset] on ohno.tf line 1: +[red]│[reset] 1: resource "foo_bar "baz[underline]"[reset] { +[red]│[reset] +[red]│[reset] It all went wrong. +[red]╵[reset] +`, + }, + } + + // This empty Colorize just passes through all of the formatting codes + // untouched, because it doesn't define any formatting keywords. + colorize := &colorstring.Colorize{} + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + got := strings.TrimSpace(DiagnosticFromJSON(test.Diag, colorize, 40)) + want := strings.TrimSpace(test.Want) + if got != want { + t.Errorf("wrong result\ngot:\n%s\n\nwant:\n%s\n\n", got, want) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/format/diff.go b/vendor/github.com/hashicorp/terraform/command/format/diff.go index 7a3df5f0..23b8d095 100644 --- a/vendor/github.com/hashicorp/terraform/command/format/diff.go +++ b/vendor/github.com/hashicorp/terraform/command/format/diff.go @@ -14,7 +14,6 @@ import ( "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/helper/experiment" "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/plans/objchange" "github.com/hashicorp/terraform/states" @@ -31,7 +30,6 @@ import ( // no color codes will be included. func ResourceChange( change *plans.ResourceInstanceChangeSrc, - tainted bool, schema *configschema.Block, color *colorstring.Colorize, ) string { @@ -59,9 +57,12 @@ func ResourceChange( case plans.Update: buf.WriteString(color.Color(fmt.Sprintf("[bold] # %s[reset] will be updated in-place", dispAddr))) case plans.CreateThenDelete, plans.DeleteThenCreate: - if tainted { + switch change.ActionReason { + case plans.ResourceInstanceReplaceBecauseTainted: buf.WriteString(color.Color(fmt.Sprintf("[bold] # %s[reset] is tainted, so must be [bold][red]replaced", dispAddr))) - } else { + case plans.ResourceInstanceReplaceByRequest: + buf.WriteString(color.Color(fmt.Sprintf("[bold] # %s[reset] will be [bold][red]replaced[reset], as requested", dispAddr))) + default: buf.WriteString(color.Color(fmt.Sprintf("[bold] # %s[reset] must be [bold][red]replaced", dispAddr))) } case plans.Delete: @@ -99,7 +100,6 @@ func ResourceChange( color: color, action: change.Action, requiredReplace: change.RequiredReplace, - concise: experiment.Enabled(experiment.X_concise_diff), } // Most commonly-used resources have nested blocks that result in us @@ -154,10 +154,9 @@ func OutputChanges( ) string { var buf bytes.Buffer p := blockBodyDiffPrinter{ - buf: &buf, - color: color, - action: plans.Update, // not actually used in this case, because we're not printing a containing block - concise: experiment.Enabled(experiment.X_concise_diff), + buf: &buf, + color: color, + action: plans.Update, // not actually used in this case, because we're not printing a containing block } // We're going to reuse the codepath we used for printing resource block @@ -200,7 +199,8 @@ type blockBodyDiffPrinter struct { color *colorstring.Colorize action plans.Action requiredReplace cty.PathSet - concise bool + // verbose is set to true when using the "diff" printer to format state + verbose bool } type blockBodyDiffResult struct { @@ -215,56 +215,11 @@ const forcesNewResourceCaption = " [red]# forces replacement[reset]" // and returns true if any differences were found and written func (p *blockBodyDiffPrinter) writeBlockBodyDiff(schema *configschema.Block, old, new cty.Value, indent int, path cty.Path) blockBodyDiffResult { path = ctyEnsurePathCapacity(path, 1) - result := blockBodyDiffResult{} - blankBeforeBlocks := false - { - attrNames := make([]string, 0, len(schema.Attributes)) - attrNameLen := 0 - for name := range schema.Attributes { - oldVal := ctyGetAttrMaybeNull(old, name) - newVal := ctyGetAttrMaybeNull(new, name) - if oldVal.IsNull() && newVal.IsNull() { - // Skip attributes where both old and new values are null - // (we do this early here so that we'll do our value alignment - // based on the longest attribute name that has a change, rather - // than the longest attribute name in the full set.) - continue - } - - attrNames = append(attrNames, name) - if len(name) > attrNameLen { - attrNameLen = len(name) - } - } - sort.Strings(attrNames) - if len(attrNames) > 0 { - blankBeforeBlocks = true - } - - for _, name := range attrNames { - attrS := schema.Attributes[name] - oldVal := ctyGetAttrMaybeNull(old, name) - newVal := ctyGetAttrMaybeNull(new, name) - - result.bodyWritten = true - skipped := p.writeAttrDiff(name, attrS, oldVal, newVal, attrNameLen, indent, path) - if skipped { - result.skippedAttributes++ - } - } - - if result.skippedAttributes > 0 { - noun := "attributes" - if result.skippedAttributes == 1 { - noun = "attribute" - } - p.buf.WriteString("\n") - p.buf.WriteString(strings.Repeat(" ", indent+2)) - p.buf.WriteString(p.color.Color(fmt.Sprintf("[dark_gray]# (%d unchanged %s hidden)[reset]", result.skippedAttributes, noun))) - } - } + // write the attributes diff + blankBeforeBlocks := p.writeAttrsDiff(schema.Attributes, old, new, indent, path, &result) + p.writeSkippedAttr(result.skippedAttributes, indent+2) { blockTypeNames := make([]string, 0, len(schema.BlockTypes)) @@ -301,6 +256,53 @@ func (p *blockBodyDiffPrinter) writeBlockBodyDiff(schema *configschema.Block, ol return result } +func (p *blockBodyDiffPrinter) writeAttrsDiff( + attrsS map[string]*configschema.Attribute, + old, new cty.Value, + indent int, + path cty.Path, + result *blockBodyDiffResult) bool { + + blankBeforeBlocks := false + + attrNames := make([]string, 0, len(attrsS)) + attrNameLen := 0 + for name := range attrsS { + oldVal := ctyGetAttrMaybeNull(old, name) + newVal := ctyGetAttrMaybeNull(new, name) + if oldVal.IsNull() && newVal.IsNull() { + // Skip attributes where both old and new values are null + // (we do this early here so that we'll do our value alignment + // based on the longest attribute name that has a change, rather + // than the longest attribute name in the full set.) + continue + } + + attrNames = append(attrNames, name) + if len(name) > attrNameLen { + attrNameLen = len(name) + } + } + sort.Strings(attrNames) + if len(attrNames) > 0 { + blankBeforeBlocks = true + } + + for _, name := range attrNames { + attrS := attrsS[name] + oldVal := ctyGetAttrMaybeNull(old, name) + newVal := ctyGetAttrMaybeNull(new, name) + + result.bodyWritten = true + skipped := p.writeAttrDiff(name, attrS, oldVal, newVal, attrNameLen, indent, path) + if skipped { + result.skippedAttributes++ + } + } + + return blankBeforeBlocks +} + // getPlanActionAndShow returns the action value // and a boolean for showJustNew. In this function we // modify the old and new values to remove any possible marks @@ -326,10 +328,15 @@ func (p *blockBodyDiffPrinter) writeAttrDiff(name string, attrS *configschema.At path = append(path, cty.GetAttrStep{Name: name}) action, showJustNew := getPlanActionAndShow(old, new) - if action == plans.NoOp && p.concise && !identifyingAttribute(name, attrS) { + if action == plans.NoOp && !p.verbose && !identifyingAttribute(name, attrS) { return true } + if attrS.NestedType != nil { + p.writeNestedAttrDiff(name, attrS.NestedType, old, new, nameLen, indent, path, action, showJustNew) + return false + } + p.buf.WriteString("\n") p.writeSensitivityWarning(old, new, indent, action, false) @@ -345,6 +352,9 @@ func (p *blockBodyDiffPrinter) writeAttrDiff(name string, attrS *configschema.At if attrS.Sensitive { p.buf.WriteString("(sensitive value)") + if p.pathForcesNewResource(path) { + p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) + } } else { switch { case showJustNew: @@ -363,6 +373,218 @@ func (p *blockBodyDiffPrinter) writeAttrDiff(name string, attrS *configschema.At return false } +// writeNestedAttrDiff is responsible for formatting Attributes with NestedTypes +// in the diff. +func (p *blockBodyDiffPrinter) writeNestedAttrDiff( + name string, objS *configschema.Object, old, new cty.Value, + nameLen, indent int, path cty.Path, action plans.Action, showJustNew bool) { + + p.buf.WriteString("\n") + p.buf.WriteString(strings.Repeat(" ", indent)) + p.writeActionSymbol(action) + + p.buf.WriteString(p.color.Color("[bold]")) + p.buf.WriteString(name) + p.buf.WriteString(p.color.Color("[reset]")) + p.buf.WriteString(strings.Repeat(" ", nameLen-len(name))) + + result := &blockBodyDiffResult{} + switch objS.Nesting { + case configschema.NestingSingle: + p.buf.WriteString(" = {") + if action != plans.NoOp && (p.pathForcesNewResource(path) || p.pathForcesNewResource(path[:len(path)-1])) { + p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) + } + p.writeAttrsDiff(objS.Attributes, old, new, indent+2, path, result) + p.writeSkippedAttr(result.skippedAttributes, indent+4) + p.buf.WriteString("\n") + p.buf.WriteString(strings.Repeat(" ", indent)) + p.buf.WriteString("}") + + case configschema.NestingList: + p.buf.WriteString(" = [") + if action != plans.NoOp && (p.pathForcesNewResource(path) || p.pathForcesNewResource(path[:len(path)-1])) { + p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) + } + p.buf.WriteString("\n") + p.buf.WriteString(strings.Repeat(" ", indent+4)) + p.writeActionSymbol(action) + p.buf.WriteString("{") + + oldItems := ctyCollectionValues(old) + newItems := ctyCollectionValues(new) + // Here we intentionally preserve the index-based correspondance + // between old and new, rather than trying to detect insertions + // and removals in the list, because this more accurately reflects + // how Terraform Core and providers will understand the change, + // particularly when the nested block contains computed attributes + // that will themselves maintain correspondance by index. + + // commonLen is number of elements that exist in both lists, which + // will be presented as updates (~). Any additional items in one + // of the lists will be presented as either creates (+) or deletes (-) + // depending on which list they belong to. + var commonLen int + // unchanged is the number of unchanged elements + var unchanged int + + switch { + case len(oldItems) < len(newItems): + commonLen = len(oldItems) + default: + commonLen = len(newItems) + } + for i := 0; i < commonLen; i++ { + path := append(path, cty.IndexStep{Key: cty.NumberIntVal(int64(i))}) + oldItem := oldItems[i] + newItem := newItems[i] + if oldItem.RawEquals(newItem) { + action = plans.NoOp + unchanged++ + } + if action != plans.NoOp { + p.writeAttrsDiff(objS.Attributes, oldItem, newItem, indent+6, path, result) + p.writeSkippedAttr(result.skippedAttributes, indent+8) + p.buf.WriteString("\n") + } + } + for i := commonLen; i < len(oldItems); i++ { + path := append(path, cty.IndexStep{Key: cty.NumberIntVal(int64(i))}) + oldItem := oldItems[i] + newItem := cty.NullVal(oldItem.Type()) + p.writeAttrsDiff(objS.Attributes, oldItem, newItem, indent+6, path, result) + p.buf.WriteString("\n") + } + for i := commonLen; i < len(newItems); i++ { + path := append(path, cty.IndexStep{Key: cty.NumberIntVal(int64(i))}) + newItem := newItems[i] + oldItem := cty.NullVal(newItem.Type()) + p.writeAttrsDiff(objS.Attributes, oldItem, newItem, indent+6, path, result) + p.buf.WriteString("\n") + } + p.buf.WriteString(strings.Repeat(" ", indent+4)) + p.buf.WriteString("},\n") + p.writeSkippedElems(unchanged, indent+4) + p.buf.WriteString(strings.Repeat(" ", indent+2)) + p.buf.WriteString("]") + + case configschema.NestingSet: + oldItems := ctyCollectionValues(old) + newItems := ctyCollectionValues(new) + + allItems := make([]cty.Value, 0, len(oldItems)+len(newItems)) + allItems = append(allItems, oldItems...) + allItems = append(allItems, newItems...) + all := cty.SetVal(allItems) + + p.buf.WriteString(" = [") + + for it := all.ElementIterator(); it.Next(); { + _, val := it.Element() + var action plans.Action + var oldValue, newValue cty.Value + switch { + case !val.IsKnown(): + action = plans.Update + newValue = val + case !old.HasElement(val).True(): + action = plans.Create + oldValue = cty.NullVal(val.Type()) + newValue = val + case !new.HasElement(val).True(): + action = plans.Delete + oldValue = val + newValue = cty.NullVal(val.Type()) + default: + action = plans.NoOp + oldValue = val + newValue = val + } + + p.buf.WriteString("\n") + p.buf.WriteString(strings.Repeat(" ", indent+4)) + p.writeActionSymbol(action) + p.buf.WriteString("{") + + if action != plans.NoOp && (p.pathForcesNewResource(path) || p.pathForcesNewResource(path[:len(path)-1])) { + p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) + } + + path := append(path, cty.IndexStep{Key: val}) + p.writeAttrsDiff(objS.Attributes, oldValue, newValue, indent+6, path, result) + + p.buf.WriteString("\n") + p.buf.WriteString(strings.Repeat(" ", indent+4)) + p.buf.WriteString("},") + } + p.buf.WriteString("\n") + p.buf.WriteString(strings.Repeat(" ", indent+2)) + p.buf.WriteString("]") + + case configschema.NestingMap: + oldItems := old.AsValueMap() + newItems := new.AsValueMap() + + allKeys := make(map[string]bool) + for k := range oldItems { + allKeys[k] = true + } + for k := range newItems { + allKeys[k] = true + } + allKeysOrder := make([]string, 0, len(allKeys)) + for k := range allKeys { + allKeysOrder = append(allKeysOrder, k) + } + sort.Strings(allKeysOrder) + + p.buf.WriteString(" = {\n") + + // unchanged tracks the number of unchanged elements + unchanged := 0 + for _, k := range allKeysOrder { + var action plans.Action + oldValue := oldItems[k] + newValue := newItems[k] + switch { + case oldValue == cty.NilVal: + oldValue = cty.NullVal(newValue.Type()) + action = plans.Create + case newValue == cty.NilVal: + newValue = cty.NullVal(oldValue.Type()) + action = plans.Delete + case !newValue.RawEquals(oldValue): + action = plans.Update + default: + action = plans.NoOp + unchanged++ + } + + if action != plans.NoOp { + p.buf.WriteString(strings.Repeat(" ", indent+4)) + p.writeActionSymbol(action) + fmt.Fprintf(p.buf, "%q = {", k) + if p.pathForcesNewResource(path) || p.pathForcesNewResource(path[:len(path)-1]) { + p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) + } + + path := append(path, cty.IndexStep{Key: cty.StringVal(k)}) + p.writeAttrsDiff(objS.Attributes, oldValue, newValue, indent+6, path, result) + p.writeSkippedAttr(result.skippedAttributes, indent+8) + p.buf.WriteString("\n") + p.buf.WriteString(strings.Repeat(" ", indent+4)) + p.buf.WriteString("},\n") + } + } + + p.writeSkippedElems(unchanged, indent+4) + p.buf.WriteString(strings.Repeat(" ", indent+2)) + p.buf.WriteString("}") + } + + return +} + func (p *blockBodyDiffPrinter) writeNestedBlockDiffs(name string, blockS *configschema.NestedBlock, old, new cty.Value, blankBefore bool, indent int, path cty.Path) int { skippedBlocks := 0 path = append(path, cty.GetAttrStep{Name: name}) @@ -620,11 +842,10 @@ func (p *blockBodyDiffPrinter) writeSensitiveNestedBlockDiff(name string, old, n p.buf.WriteRune('\n') p.buf.WriteString(strings.Repeat(" ", indent+2)) p.buf.WriteString("}") - return } func (p *blockBodyDiffPrinter) writeNestedBlockDiff(name string, label *string, blockS *configschema.Block, action plans.Action, old, new cty.Value, indent int, path cty.Path) bool { - if action == plans.NoOp && p.concise { + if action == plans.NoOp && !p.verbose { return true } @@ -878,7 +1099,7 @@ func (p *blockBodyDiffPrinter) writeValueDiff(old, new cty.Value, indent int, pa } } - if strings.Index(oldS, "\n") < 0 && strings.Index(newS, "\n") < 0 { + if !strings.Contains(oldS, "\n") && !strings.Contains(newS, "\n") { break } @@ -904,23 +1125,35 @@ func (p *blockBodyDiffPrinter) writeValueDiff(old, new cty.Value, indent int, pa } } - diffLines := ctySequenceDiff(oldLines, newLines) - for _, diffLine := range diffLines { - p.buf.WriteString(strings.Repeat(" ", indent+2)) - p.writeActionSymbol(diffLine.Action) - - switch diffLine.Action { - case plans.NoOp, plans.Delete: - p.buf.WriteString(diffLine.Before.AsString()) - case plans.Create: - p.buf.WriteString(diffLine.After.AsString()) - default: - // Should never happen since the above covers all - // actions that ctySequenceDiff can return for strings - p.buf.WriteString(diffLine.After.AsString()) + // Optimization for strings which are exactly equal: just print + // directly without calculating the sequence diff. This makes a + // significant difference when this code path is reached via a + // writeValue call with a large multi-line string. + if oldS == newS { + for _, line := range newLines { + p.buf.WriteString(strings.Repeat(" ", indent+4)) + p.buf.WriteString(line.AsString()) + p.buf.WriteString("\n") + } + } else { + diffLines := ctySequenceDiff(oldLines, newLines) + for _, diffLine := range diffLines { + p.buf.WriteString(strings.Repeat(" ", indent+2)) + p.writeActionSymbol(diffLine.Action) + + switch diffLine.Action { + case plans.NoOp, plans.Delete: + p.buf.WriteString(diffLine.Before.AsString()) + case plans.Create: + p.buf.WriteString(diffLine.After.AsString()) + default: + // Should never happen since the above covers all + // actions that ctySequenceDiff can return for strings + p.buf.WriteString(diffLine.After.AsString()) + } + p.buf.WriteString("\n") } - p.buf.WriteString("\n") } p.buf.WriteString(strings.Repeat(" ", indent)) // +4 here because there's no symbol @@ -984,7 +1217,7 @@ func (p *blockBodyDiffPrinter) writeValueDiff(old, new cty.Value, indent int, pa action = plans.NoOp } - if action == plans.NoOp && p.concise { + if action == plans.NoOp && !p.verbose { suppressedElements++ continue } @@ -1024,8 +1257,7 @@ func (p *blockBodyDiffPrinter) writeValueDiff(old, new cty.Value, indent int, pa var changeShown bool for i := 0; i < len(elemDiffs); i++ { - // In concise mode, push any no-op diff elements onto the stack - if p.concise { + if !p.verbose { for i < len(elemDiffs) && elemDiffs[i].Action == plans.NoOp { suppressedElements = append(suppressedElements, elemDiffs[i]) i++ @@ -1053,7 +1285,6 @@ func (p *blockBodyDiffPrinter) writeValueDiff(old, new cty.Value, indent int, pa if hidden > 0 && i < len(elemDiffs) { hidden-- nextContextDiff = suppressedElements[hidden] - suppressedElements = suppressedElements[:hidden] } // If there are still hidden elements, show an elision @@ -1161,7 +1392,7 @@ func (p *blockBodyDiffPrinter) writeValueDiff(old, new cty.Value, indent int, pa } } - if action == plans.NoOp && p.concise { + if action == plans.NoOp && !p.verbose { suppressedElements++ continue } @@ -1262,7 +1493,7 @@ func (p *blockBodyDiffPrinter) writeValueDiff(old, new cty.Value, indent int, pa action = plans.Update } - if action == plans.NoOp && p.concise { + if action == plans.NoOp && !p.verbose { suppressedElements++ continue } @@ -1598,3 +1829,27 @@ func DiffActionSymbol(action plans.Action) string { func identifyingAttribute(name string, attrSchema *configschema.Attribute) bool { return name == "id" || name == "tags" || name == "name" } + +func (p *blockBodyDiffPrinter) writeSkippedAttr(skipped, indent int) { + if skipped > 0 { + noun := "attributes" + if skipped == 1 { + noun = "attribute" + } + p.buf.WriteString("\n") + p.buf.WriteString(strings.Repeat(" ", indent)) + p.buf.WriteString(p.color.Color(fmt.Sprintf("[dark_gray]# (%d unchanged %s hidden)[reset]", skipped, noun))) + } +} + +func (p *blockBodyDiffPrinter) writeSkippedElems(skipped, indent int) { + if skipped > 0 { + noun := "elements" + if skipped == 1 { + noun = "element" + } + p.buf.WriteString(strings.Repeat(" ", indent)) + p.buf.WriteString(p.color.Color(fmt.Sprintf("[dark_gray]# (%d unchanged %s hidden)[reset]", skipped, noun))) + p.buf.WriteString("\n") + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/format/diff_test.go b/vendor/github.com/hashicorp/terraform/command/format/diff_test.go index 076b1a9a..41c42553 100644 --- a/vendor/github.com/hashicorp/terraform/command/format/diff_test.go +++ b/vendor/github.com/hashicorp/terraform/command/format/diff_test.go @@ -7,7 +7,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/helper/experiment" "github.com/hashicorp/terraform/plans" "github.com/mitchellh/colorstring" "github.com/zclconf/go-cty/cty" @@ -28,7 +27,6 @@ func TestResourceChange_primitiveTypes(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be created + resource "test_instance" "example" { + id = (known after apply) @@ -48,7 +46,6 @@ func TestResourceChange_primitiveTypes(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be created + resource "test_instance" "example" { + string = "null" @@ -68,7 +65,6 @@ func TestResourceChange_primitiveTypes(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be created + resource "test_instance" "example" { + string = "null " @@ -88,7 +84,6 @@ func TestResourceChange_primitiveTypes(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be destroyed - resource "test_instance" "example" { - id = "i-02ae66f368e8518a9" -> null @@ -110,7 +105,6 @@ func TestResourceChange_primitiveTypes(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be destroyed - resource "test_instance" "example" { - id = "i-02ae66f368e8518a9" -> null @@ -135,7 +129,6 @@ func TestResourceChange_primitiveTypes(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ ami = "ami-BEFORE" -> "ami-AFTER" @@ -144,8 +137,9 @@ func TestResourceChange_primitiveTypes(t *testing.T) { `, }, "string force-new update": { - Action: plans.DeleteThenCreate, - Mode: addrs.ManagedResourceMode, + Action: plans.DeleteThenCreate, + ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, + Mode: addrs.ManagedResourceMode, Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-BEFORE"), @@ -163,7 +157,6 @@ func TestResourceChange_primitiveTypes(t *testing.T) { RequiredReplace: cty.NewPathSet(cty.Path{ cty.GetAttrStep{Name: "ami"}, }), - Tainted: false, ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { ~ ami = "ami-BEFORE" -> "ami-AFTER" # forces replacement @@ -192,7 +185,6 @@ func TestResourceChange_primitiveTypes(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ ami = "ami-BEFORE" -> "ami-AFTER" @@ -228,7 +220,6 @@ field }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -263,7 +254,6 @@ new line }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -297,7 +287,6 @@ new line RequiredReplace: cty.NewPathSet(cty.Path{ cty.GetAttrStep{Name: "more_lines"}, }), - Tainted: false, ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -318,19 +307,35 @@ new line After: cty.ObjectVal(map[string]cty.Value{ "id": cty.UnknownVal(cty.String), "password": cty.StringVal("top-secret"), + "conn_info": cty.ObjectVal(map[string]cty.Value{ + "user": cty.StringVal("not-secret"), + "password": cty.StringVal("top-secret"), + }), }), Schema: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "id": {Type: cty.String, Computed: true}, "password": {Type: cty.String, Optional: true, Sensitive: true}, + "conn_info": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSingle, + Attributes: map[string]*configschema.Attribute{ + "user": {Type: cty.String, Optional: true}, + "password": {Type: cty.String, Optional: true, Sensitive: true}, + }, + }, + }, }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be created + resource "test_instance" "example" { - + id = (known after apply) - + password = (sensitive value) + + conn_info = { + + password = (sensitive value) + + user = "not-secret" + } + + id = (known after apply) + + password = (sensitive value) } `, }, @@ -355,27 +360,20 @@ new line }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "blah" -> (known after apply) ~ str = "before" -> "after" # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ~ id = "blah" -> (known after apply) - password = (sensitive value) - ~ str = "before" -> "after" - } `, }, - // tainted resources + // tainted objects "replace tainted resource": { - Action: plans.DeleteThenCreate, - Mode: addrs.ManagedResourceMode, + Action: plans.DeleteThenCreate, + ActionReason: plans.ResourceInstanceReplaceBecauseTainted, + Mode: addrs.ManagedResourceMode, Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-BEFORE"), @@ -393,7 +391,6 @@ new line RequiredReplace: cty.NewPathSet(cty.Path{ cty.GetAttrStep{Name: "ami"}, }), - Tainted: true, ExpectedOutput: ` # test_instance.example is tainted, so must be replaced -/+ resource "test_instance" "example" { ~ ami = "ami-BEFORE" -> "ami-AFTER" # forces replacement @@ -402,8 +399,9 @@ new line `, }, "force replacement with empty before value": { - Action: plans.DeleteThenCreate, - Mode: addrs.ManagedResourceMode, + Action: plans.DeleteThenCreate, + ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, + Mode: addrs.ManagedResourceMode, Before: cty.ObjectVal(map[string]cty.Value{ "name": cty.StringVal("name"), "forced": cty.NullVal(cty.String), @@ -421,7 +419,6 @@ new line RequiredReplace: cty.NewPathSet(cty.Path{ cty.GetAttrStep{Name: "forced"}, }), - Tainted: false, ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { + forced = "example" # forces replacement @@ -430,8 +427,9 @@ new line `, }, "force replacement with empty before value legacy": { - Action: plans.DeleteThenCreate, - Mode: addrs.ManagedResourceMode, + Action: plans.DeleteThenCreate, + ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, + Mode: addrs.ManagedResourceMode, Before: cty.ObjectVal(map[string]cty.Value{ "name": cty.StringVal("name"), "forced": cty.StringVal(""), @@ -449,7 +447,6 @@ new line RequiredReplace: cty.NewPathSet(cty.Path{ cty.GetAttrStep{Name: "forced"}, }), - Tainted: false, ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { + forced = "example" # forces replacement @@ -491,7 +488,6 @@ new line }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ ami = "ami-BEFORE" -> "ami-AFTER" @@ -502,18 +498,6 @@ new line } # (2 unchanged attributes hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - bar = "bar" - foo = "foo" - id = "i-02ae66f368e8518a9" - name = "alice" - tags = { - "name" = "bob" - } - } `, }, } @@ -542,7 +526,6 @@ func TestResourceChange_JSON(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be created + resource "test_instance" "example" { + id = (known after apply) @@ -581,7 +564,6 @@ func TestResourceChange_JSON(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -594,19 +576,6 @@ func TestResourceChange_JSON(t *testing.T) { ) } `, - - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ json_field = jsonencode( - ~ { - aaa = "value" - + bbb = "new_value" - - ccc = 5 -> null - } - ) - } -`, }, "in-place update (from empty tuple)": { Action: plans.Update, @@ -626,7 +595,6 @@ func TestResourceChange_JSON(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -658,7 +626,6 @@ func TestResourceChange_JSON(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -690,7 +657,6 @@ func TestResourceChange_JSON(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -709,8 +675,9 @@ func TestResourceChange_JSON(t *testing.T) { `, }, "force-new update": { - Action: plans.DeleteThenCreate, - Mode: addrs.ManagedResourceMode, + Action: plans.DeleteThenCreate, + ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, + Mode: addrs.ManagedResourceMode, Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "json_field": cty.StringVal(`{"aaa": "value"}`), @@ -728,7 +695,6 @@ func TestResourceChange_JSON(t *testing.T) { RequiredReplace: cty.NewPathSet(cty.Path{ cty.GetAttrStep{Name: "json_field"}, }), - Tainted: false, ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -739,17 +705,6 @@ func TestResourceChange_JSON(t *testing.T) { } # forces replacement ) } -`, - VerboseOutput: ` # test_instance.example must be replaced --/+ resource "test_instance" "example" { - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ json_field = jsonencode( - ~ { - aaa = "value" - + bbb = "new_value" - } # forces replacement - ) - } `, }, "in-place update (whitespace change)": { @@ -771,7 +726,6 @@ func TestResourceChange_JSON(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -785,8 +739,9 @@ func TestResourceChange_JSON(t *testing.T) { `, }, "force-new update (whitespace change)": { - Action: plans.DeleteThenCreate, - Mode: addrs.ManagedResourceMode, + Action: plans.DeleteThenCreate, + ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, + Mode: addrs.ManagedResourceMode, Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "json_field": cty.StringVal(`{"aaa": "value", "bbb": "another"}`), @@ -805,7 +760,6 @@ func TestResourceChange_JSON(t *testing.T) { RequiredReplace: cty.NewPathSet(cty.Path{ cty.GetAttrStep{Name: "json_field"}, }), - Tainted: false, ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -833,7 +787,6 @@ func TestResourceChange_JSON(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be created + resource "test_instance" "example" { + id = (known after apply) @@ -859,7 +812,6 @@ func TestResourceChange_JSON(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -871,18 +823,6 @@ func TestResourceChange_JSON(t *testing.T) { ] ) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ json_field = jsonencode( - ~ [ - "first", - "second", - - "third", - ] - ) - } `, }, "JSON list item addition": { @@ -903,7 +843,6 @@ func TestResourceChange_JSON(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -916,19 +855,6 @@ func TestResourceChange_JSON(t *testing.T) { ) } `, - - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ json_field = jsonencode( - ~ [ - "first", - "second", - + "third", - ] - ) - } -`, }, "JSON list object addition": { Action: plans.Update, @@ -948,7 +874,6 @@ func TestResourceChange_JSON(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -983,7 +908,6 @@ func TestResourceChange_JSON(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1016,7 +940,6 @@ func TestResourceChange_JSON(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1051,7 +974,6 @@ func TestResourceChange_JSON(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1089,7 +1011,6 @@ func TestResourceChange_JSON(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1126,7 +1047,6 @@ func TestResourceChange_JSON(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1163,7 +1083,6 @@ func TestResourceChange_JSON(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1214,7 +1133,6 @@ func TestResourceChange_primitiveList(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1223,15 +1141,6 @@ func TestResourceChange_primitiveList(t *testing.T) { ] # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - + list_field = [ - + "new-element", - ] - } `, }, "in-place update - first addition": { @@ -1257,7 +1166,6 @@ func TestResourceChange_primitiveList(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1266,15 +1174,6 @@ func TestResourceChange_primitiveList(t *testing.T) { ] # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ list_field = [ - + "new-element", - ] - } `, }, "in-place update - insertion": { @@ -1311,7 +1210,6 @@ func TestResourceChange_primitiveList(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1324,25 +1222,12 @@ func TestResourceChange_primitiveList(t *testing.T) { ] # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ list_field = [ - "aaaa", - "bbbb", - + "cccc", - "dddd", - "eeee", - "ffff", - ] - } `, }, "force-new update - insertion": { - Action: plans.DeleteThenCreate, - Mode: addrs.ManagedResourceMode, + Action: plans.DeleteThenCreate, + ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, + Mode: addrs.ManagedResourceMode, Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-STATIC"), @@ -1370,7 +1255,6 @@ func TestResourceChange_primitiveList(t *testing.T) { RequiredReplace: cty.NewPathSet(cty.Path{ cty.GetAttrStep{Name: "list_field"}, }), - Tainted: false, ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1381,17 +1265,6 @@ func TestResourceChange_primitiveList(t *testing.T) { ] # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example must be replaced --/+ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ list_field = [ # forces replacement - "aaaa", - + "bbbb", - "cccc", - ] - } `, }, "in-place update - deletion": { @@ -1425,7 +1298,6 @@ func TestResourceChange_primitiveList(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1438,19 +1310,6 @@ func TestResourceChange_primitiveList(t *testing.T) { ] # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ list_field = [ - - "aaaa", - "bbbb", - - "cccc", - "dddd", - "eeee", - ] - } `, }, "creation - empty list": { @@ -1470,7 +1329,6 @@ func TestResourceChange_primitiveList(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be created + resource "test_instance" "example" { + ami = "ami-STATIC" @@ -1504,7 +1362,6 @@ func TestResourceChange_primitiveList(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1515,17 +1372,6 @@ func TestResourceChange_primitiveList(t *testing.T) { ] # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ list_field = [ - - "aaaa", - - "bbbb", - - "cccc", - ] - } `, }, "in-place update - null to empty": { @@ -1549,20 +1395,12 @@ func TestResourceChange_primitiveList(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) + list_field = [] # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - + list_field = [] - } `, }, "update to unknown element": { @@ -1594,7 +1432,6 @@ func TestResourceChange_primitiveList(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1606,18 +1443,6 @@ func TestResourceChange_primitiveList(t *testing.T) { ] # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ list_field = [ - "aaaa", - - "bbbb", - + (known after apply), - "cccc", - ] - } `, }, "update - two new unknown elements": { @@ -1654,7 +1479,6 @@ func TestResourceChange_primitiveList(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1668,21 +1492,6 @@ func TestResourceChange_primitiveList(t *testing.T) { ] # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ list_field = [ - "aaaa", - - "bbbb", - + (known after apply), - + (known after apply), - "cccc", - "dddd", - "eeee", - ] - } `, }, } @@ -1721,7 +1530,6 @@ func TestResourceChange_primitiveTuple(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { id = "i-02ae66f368e8518a9" @@ -1734,19 +1542,6 @@ func TestResourceChange_primitiveTuple(t *testing.T) { # (1 unchanged element hidden) ] } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - id = "i-02ae66f368e8518a9" - ~ tuple_field = [ - "aaaa", - "bbbb", - - "dddd", - + "cccc", - "eeee", - "ffff", - ] - } `, }, } @@ -1778,7 +1573,6 @@ func TestResourceChange_primitiveSet(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1787,15 +1581,6 @@ func TestResourceChange_primitiveSet(t *testing.T) { ] # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - + set_field = [ - + "new-element", - ] - } `, }, "in-place update - first insertion": { @@ -1821,7 +1606,6 @@ func TestResourceChange_primitiveSet(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1830,15 +1614,6 @@ func TestResourceChange_primitiveSet(t *testing.T) { ] # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ set_field = [ - + "new-element", - ] - } `, }, "in-place update - insertion": { @@ -1869,7 +1644,6 @@ func TestResourceChange_primitiveSet(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1879,22 +1653,12 @@ func TestResourceChange_primitiveSet(t *testing.T) { ] # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ set_field = [ - "aaaa", - + "bbbb", - "cccc", - ] - } `, }, "force-new update - insertion": { - Action: plans.DeleteThenCreate, - Mode: addrs.ManagedResourceMode, + Action: plans.DeleteThenCreate, + ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, + Mode: addrs.ManagedResourceMode, Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-STATIC"), @@ -1922,7 +1686,6 @@ func TestResourceChange_primitiveSet(t *testing.T) { RequiredReplace: cty.NewPathSet(cty.Path{ cty.GetAttrStep{Name: "set_field"}, }), - Tainted: false, ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1932,17 +1695,6 @@ func TestResourceChange_primitiveSet(t *testing.T) { ] # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example must be replaced --/+ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ set_field = [ # forces replacement - "aaaa", - + "bbbb", - "cccc", - ] - } `, }, "in-place update - deletion": { @@ -1972,7 +1724,6 @@ func TestResourceChange_primitiveSet(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -1983,17 +1734,6 @@ func TestResourceChange_primitiveSet(t *testing.T) { ] # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ set_field = [ - - "aaaa", - "bbbb", - - "cccc", - ] - } `, }, "creation - empty set": { @@ -2013,7 +1753,6 @@ func TestResourceChange_primitiveSet(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be created + resource "test_instance" "example" { + ami = "ami-STATIC" @@ -2055,16 +1794,6 @@ func TestResourceChange_primitiveSet(t *testing.T) { ] # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ set_field = [ - - "aaaa", - - "bbbb", - ] - } `, }, "in-place update - null to empty set": { @@ -2088,20 +1817,12 @@ func TestResourceChange_primitiveSet(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) + set_field = [] # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - + set_field = [] - } `, }, "in-place update to unknown": { @@ -2128,7 +1849,6 @@ func TestResourceChange_primitiveSet(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -2138,16 +1858,6 @@ func TestResourceChange_primitiveSet(t *testing.T) { ] -> (known after apply) # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ set_field = [ - - "aaaa", - - "bbbb", - ] -> (known after apply) - } `, }, "in-place update to unknown element": { @@ -2177,7 +1887,6 @@ func TestResourceChange_primitiveSet(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -2188,17 +1897,6 @@ func TestResourceChange_primitiveSet(t *testing.T) { ] # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ set_field = [ - "aaaa", - - "bbbb", - ~ (known after apply), - ] - } `, }, } @@ -2230,7 +1928,6 @@ func TestResourceChange_map(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -2239,15 +1936,6 @@ func TestResourceChange_map(t *testing.T) { } # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - + map_field = { - + "new-key" = "new-element" - } - } `, }, "in-place update - first insertion": { @@ -2273,7 +1961,6 @@ func TestResourceChange_map(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -2282,15 +1969,6 @@ func TestResourceChange_map(t *testing.T) { } # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ map_field = { - + "new-key" = "new-element" - } - } `, }, "in-place update - insertion": { @@ -2321,7 +1999,6 @@ func TestResourceChange_map(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -2331,22 +2008,12 @@ func TestResourceChange_map(t *testing.T) { } # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ map_field = { - "a" = "aaaa" - + "b" = "bbbb" - "c" = "cccc" - } - } `, }, "force-new update - insertion": { - Action: plans.DeleteThenCreate, - Mode: addrs.ManagedResourceMode, + Action: plans.DeleteThenCreate, + ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, + Mode: addrs.ManagedResourceMode, Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-STATIC"), @@ -2374,7 +2041,6 @@ func TestResourceChange_map(t *testing.T) { RequiredReplace: cty.NewPathSet(cty.Path{ cty.GetAttrStep{Name: "map_field"}, }), - Tainted: false, ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -2384,17 +2050,6 @@ func TestResourceChange_map(t *testing.T) { } # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example must be replaced --/+ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ map_field = { # forces replacement - "a" = "aaaa" - + "b" = "bbbb" - "c" = "cccc" - } - } `, }, "in-place update - deletion": { @@ -2424,7 +2079,6 @@ func TestResourceChange_map(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -2435,17 +2089,6 @@ func TestResourceChange_map(t *testing.T) { } # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ map_field = { - - "a" = "aaaa" -> null - "b" = "bbbb" - - "c" = "cccc" -> null - } - } `, }, "creation - empty": { @@ -2465,7 +2108,6 @@ func TestResourceChange_map(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be created + resource "test_instance" "example" { + ami = "ami-STATIC" @@ -2503,7 +2145,6 @@ func TestResourceChange_map(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ id = "i-02ae66f368e8518a9" -> (known after apply) @@ -2513,17 +2154,6 @@ func TestResourceChange_map(t *testing.T) { } # (1 unchanged attribute hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ami = "ami-STATIC" - ~ id = "i-02ae66f368e8518a9" -> (known after apply) - ~ map_field = { - "a" = "aaaa" - ~ "b" = "bbbb" -> (known after apply) - "c" = "cccc" - } - } `, }, } @@ -2543,6 +2173,12 @@ func TestResourceChange_nestedList(t *testing.T) { "volume_type": cty.StringVal("gp2"), }), }), + "disks": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), }), After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), @@ -2552,46 +2188,23 @@ func TestResourceChange_nestedList(t *testing.T) { "volume_type": cty.StringVal("gp2"), }), }), + "disks": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), }), RequiredReplace: cty.NewPathSet(), - Tainted: false, - Schema: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "root_block_device": { - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "volume_type": { - Type: cty.String, - Optional: true, - Computed: true, - }, - }, - }, - Nesting: configschema.NestingList, - }, - }, - }, + Schema: testSchema(configschema.NestingList), ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" + ~ ami = "ami-BEFORE" -> "ami-AFTER" + id = "i-02ae66f368e8518a9" + # (1 unchanged attribute hidden) # (1 unchanged block hidden) } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" - - root_block_device { - volume_type = "gp2" - } - } `, }, "in-place update - creation": { @@ -2603,10 +2216,18 @@ func TestResourceChange_nestedList(t *testing.T) { "root_block_device": cty.ListValEmpty(cty.Object(map[string]cty.Type{ "volume_type": cty.String, })), + "disks": cty.ListValEmpty(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + })), }), After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-AFTER"), + "disks": cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + })}), "root_block_device": cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.NullVal(cty.String), @@ -2614,31 +2235,17 @@ func TestResourceChange_nestedList(t *testing.T) { }), }), RequiredReplace: cty.NewPathSet(), - Tainted: false, - Schema: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "root_block_device": { - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "volume_type": { - Type: cty.String, - Optional: true, - Computed: true, - }, - }, - }, - Nesting: configschema.NestingList, - }, - }, - }, + Schema: testSchema(configschema.NestingList), ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = [ + ~ { + + mount_point = "/var/diska" + + size = "50GB" + }, + ] + id = "i-02ae66f368e8518a9" + root_block_device {} } @@ -2653,10 +2260,20 @@ func TestResourceChange_nestedList(t *testing.T) { "root_block_device": cty.ListValEmpty(cty.Object(map[string]cty.Type{ "volume_type": cty.String, })), + "disks": cty.ListValEmpty(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + })), }), After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-AFTER"), + "disks": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.NullVal(cty.String), + }), + }), "root_block_device": cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("gp2"), @@ -2664,31 +2281,16 @@ func TestResourceChange_nestedList(t *testing.T) { }), }), RequiredReplace: cty.NewPathSet(), - Tainted: false, - Schema: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "root_block_device": { - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "volume_type": { - Type: cty.String, - Optional: true, - Computed: true, - }, - }, - }, - Nesting: configschema.NestingList, - }, - }, - }, + Schema: testSchema(configschema.NestingList), ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = [ + ~ { + + mount_point = "/var/diska" + }, + ] + id = "i-02ae66f368e8518a9" + root_block_device { + volume_type = "gp2" @@ -2702,6 +2304,16 @@ func TestResourceChange_nestedList(t *testing.T) { Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.NullVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diskb"), + "size": cty.StringVal("50GB"), + }), + }), "root_block_device": cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("gp2"), @@ -2712,6 +2324,16 @@ func TestResourceChange_nestedList(t *testing.T) { After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-AFTER"), + "disks": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diskb"), + "size": cty.StringVal("50GB"), + }), + }), "root_block_device": cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("gp2"), @@ -2720,61 +2342,39 @@ func TestResourceChange_nestedList(t *testing.T) { }), }), RequiredReplace: cty.NewPathSet(), - Tainted: false, - Schema: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "root_block_device": { - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "volume_type": { - Type: cty.String, - Optional: true, - Computed: true, - }, - "new_field": { - Type: cty.String, - Optional: true, - Computed: true, - }, - }, - }, - Nesting: configschema.NestingList, - }, - }, - }, + Schema: testSchemaPlus(configschema.NestingList), ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = [ + ~ { + + size = "50GB" + # (1 unchanged attribute hidden) + }, + # (1 unchanged element hidden) + ] + id = "i-02ae66f368e8518a9" ~ root_block_device { + new_field = "new_value" # (1 unchanged attribute hidden) } } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" - - ~ root_block_device { - + new_field = "new_value" - volume_type = "gp2" - } - } `, }, - "force-new update (inside block)": { - Action: plans.DeleteThenCreate, - Mode: addrs.ManagedResourceMode, + "force-new update (inside blocks)": { + Action: plans.DeleteThenCreate, + ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, + Mode: addrs.ManagedResourceMode, Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), "root_block_device": cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("gp2"), @@ -2784,42 +2384,41 @@ func TestResourceChange_nestedList(t *testing.T) { After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-AFTER"), + "disks": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diskb"), + "size": cty.StringVal("50GB"), + }), + }), "root_block_device": cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("different"), }), }), }), - RequiredReplace: cty.NewPathSet(cty.Path{ - cty.GetAttrStep{Name: "root_block_device"}, - cty.IndexStep{Key: cty.NumberIntVal(0)}, - cty.GetAttrStep{Name: "volume_type"}, - }), - Tainted: false, - Schema: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, + RequiredReplace: cty.NewPathSet( + cty.Path{ + cty.GetAttrStep{Name: "root_block_device"}, + cty.IndexStep{Key: cty.NumberIntVal(0)}, + cty.GetAttrStep{Name: "volume_type"}, }, - BlockTypes: map[string]*configschema.NestedBlock{ - "root_block_device": { - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "volume_type": { - Type: cty.String, - Optional: true, - Computed: true, - }, - }, - }, - Nesting: configschema.NestingList, - }, + cty.Path{ + cty.GetAttrStep{Name: "disks"}, + cty.IndexStep{Key: cty.NumberIntVal(0)}, + cty.GetAttrStep{Name: "mount_point"}, }, - }, + ), + Schema: testSchema(configschema.NestingList), ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = [ + ~ { + ~ mount_point = "/var/diska" -> "/var/diskb" # forces replacement + # (1 unchanged attribute hidden) + }, + ] + id = "i-02ae66f368e8518a9" ~ root_block_device { ~ volume_type = "gp2" -> "different" # forces replacement @@ -2828,11 +2427,18 @@ func TestResourceChange_nestedList(t *testing.T) { `, }, "force-new update (whole block)": { - Action: plans.DeleteThenCreate, - Mode: addrs.ManagedResourceMode, + Action: plans.DeleteThenCreate, + ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, + Mode: addrs.ManagedResourceMode, Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), "root_block_device": cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("gp2"), @@ -2842,40 +2448,33 @@ func TestResourceChange_nestedList(t *testing.T) { After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-AFTER"), + "disks": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diskb"), + "size": cty.StringVal("50GB"), + }), + }), "root_block_device": cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("different"), }), }), }), - RequiredReplace: cty.NewPathSet(cty.Path{ - cty.GetAttrStep{Name: "root_block_device"}, - }), - Tainted: false, - Schema: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "root_block_device": { - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "volume_type": { - Type: cty.String, - Optional: true, - Computed: true, - }, - }, - }, - Nesting: configschema.NestingList, - }, - }, - }, + RequiredReplace: cty.NewPathSet( + cty.Path{cty.GetAttrStep{Name: "root_block_device"}}, + cty.Path{cty.GetAttrStep{Name: "disks"}}, + ), + Schema: testSchema(configschema.NestingList), ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = [ # forces replacement + ~ { + ~ mount_point = "/var/diska" -> "/var/diskb" + # (1 unchanged attribute hidden) + }, + ] + id = "i-02ae66f368e8518a9" ~ root_block_device { # forces replacement ~ volume_type = "gp2" -> "different" @@ -2889,55 +2488,43 @@ func TestResourceChange_nestedList(t *testing.T) { Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), "root_block_device": cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("gp2"), - "new_field": cty.StringVal("new_value"), }), }), }), After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-AFTER"), + "disks": cty.ListValEmpty(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + })), "root_block_device": cty.ListValEmpty(cty.Object(map[string]cty.Type{ "volume_type": cty.String, - "new_field": cty.String, })), }), RequiredReplace: cty.NewPathSet(), - Tainted: false, - Schema: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "root_block_device": { - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "volume_type": { - Type: cty.String, - Optional: true, - Computed: true, - }, - "new_field": { - Type: cty.String, - Optional: true, - Computed: true, - }, - }, - }, - Nesting: configschema.NestingList, - }, - }, - }, + Schema: testSchema(configschema.NestingList), ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = [ + ~ { + - mount_point = "/var/diska" -> null + - size = "50GB" -> null + }, + ] + id = "i-02ae66f368e8518a9" - root_block_device { - - new_field = "new_value" -> null - volume_type = "gp2" -> null } } @@ -2960,7 +2547,6 @@ func TestResourceChange_nestedList(t *testing.T) { }), }), RequiredReplace: cty.NewPathSet(), - Tainted: false, Schema: &configschema.Block{ BlockTypes: map[string]*configschema.NestedBlock{ "block": { @@ -2982,6 +2568,48 @@ func TestResourceChange_nestedList(t *testing.T) { + attr = true } } +`, + }, + "in-place sequence update - deletion": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "list": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{"attr": cty.StringVal("x")}), + cty.ObjectVal(map[string]cty.Value{"attr": cty.StringVal("y")}), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "list": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{"attr": cty.StringVal("y")}), + cty.ObjectVal(map[string]cty.Value{"attr": cty.StringVal("z")}), + }), + }), + RequiredReplace: cty.NewPathSet(), + Schema: &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "list": { + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "attr": { + Type: cty.String, + Required: true, + }, + }, + }, + Nesting: configschema.NestingList, + }, + }, + }, + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ list { + ~ attr = "x" -> "y" + } + ~ list { + ~ attr = "y" -> "z" + } + } `, }, } @@ -2996,6 +2624,10 @@ func TestResourceChange_nestedSet(t *testing.T) { Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.SetValEmpty(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + })), "root_block_device": cty.SetValEmpty(cty.Object(map[string]cty.Type{ "volume_type": cty.String, })), @@ -3003,6 +2635,12 @@ func TestResourceChange_nestedSet(t *testing.T) { After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-AFTER"), + "disks": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.NullVal(cty.String), + }), + }), "root_block_device": cty.SetVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("gp2"), @@ -3010,31 +2648,16 @@ func TestResourceChange_nestedSet(t *testing.T) { }), }), RequiredReplace: cty.NewPathSet(), - Tainted: false, - Schema: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "root_block_device": { - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "volume_type": { - Type: cty.String, - Optional: true, - Computed: true, - }, - }, - }, - Nesting: configschema.NestingSet, - }, - }, - }, + Schema: testSchema(configschema.NestingSet), ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = [ + + { + + mount_point = "/var/diska" + }, + ] + id = "i-02ae66f368e8518a9" + root_block_device { + volume_type = "gp2" @@ -3048,6 +2671,12 @@ func TestResourceChange_nestedSet(t *testing.T) { Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.NullVal(cty.String), + }), + }), "root_block_device": cty.SetVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("gp2"), @@ -3058,6 +2687,12 @@ func TestResourceChange_nestedSet(t *testing.T) { After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-AFTER"), + "disks": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), "root_block_device": cty.SetVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("gp2"), @@ -3066,36 +2701,20 @@ func TestResourceChange_nestedSet(t *testing.T) { }), }), RequiredReplace: cty.NewPathSet(), - Tainted: false, - Schema: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "root_block_device": { - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "volume_type": { - Type: cty.String, - Optional: true, - Computed: true, - }, - "new_field": { - Type: cty.String, - Optional: true, - Computed: true, - }, - }, - }, - Nesting: configschema.NestingSet, - }, - }, - }, + Schema: testSchemaPlus(configschema.NestingSet), ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = [ + + { + + mount_point = "/var/diska" + + size = "50GB" + }, + - { + - mount_point = "/var/diska" -> null + }, + ] + id = "i-02ae66f368e8518a9" + root_block_device { + new_field = "new_value" @@ -3108,8 +2727,9 @@ func TestResourceChange_nestedSet(t *testing.T) { `, }, "force-new update (whole block)": { - Action: plans.DeleteThenCreate, - Mode: addrs.ManagedResourceMode, + Action: plans.DeleteThenCreate, + ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, + Mode: addrs.ManagedResourceMode, Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-BEFORE"), @@ -3118,6 +2738,12 @@ func TestResourceChange_nestedSet(t *testing.T) { "volume_type": cty.StringVal("gp2"), }), }), + "disks": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), }), After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), @@ -3127,35 +2753,32 @@ func TestResourceChange_nestedSet(t *testing.T) { "volume_type": cty.StringVal("different"), }), }), + "disks": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diskb"), + "size": cty.StringVal("50GB"), + }), + }), }), - RequiredReplace: cty.NewPathSet(cty.Path{ - cty.GetAttrStep{Name: "root_block_device"}, - }), - Tainted: false, - Schema: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "root_block_device": { - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "volume_type": { - Type: cty.String, - Optional: true, - Computed: true, - }, - }, - }, - Nesting: configschema.NestingSet, - }, - }, - }, + RequiredReplace: cty.NewPathSet( + cty.Path{cty.GetAttrStep{Name: "root_block_device"}}, + cty.Path{cty.GetAttrStep{Name: "disks"}}, + ), + Schema: testSchema(configschema.NestingSet), ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = [ + - { # forces replacement + - mount_point = "/var/diska" -> null + - size = "50GB" -> null + }, + + { # forces replacement + + mount_point = "/var/diskb" + + size = "50GB" + }, + ] + id = "i-02ae66f368e8518a9" + root_block_device { # forces replacement + volume_type = "different" @@ -3178,6 +2801,12 @@ func TestResourceChange_nestedSet(t *testing.T) { "new_field": cty.StringVal("new_value"), }), }), + "disks": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), }), After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), @@ -3186,38 +2815,23 @@ func TestResourceChange_nestedSet(t *testing.T) { "volume_type": cty.String, "new_field": cty.String, })), + "disks": cty.SetValEmpty(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + })), }), RequiredReplace: cty.NewPathSet(), - Tainted: false, - Schema: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "root_block_device": { - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "volume_type": { - Type: cty.String, - Optional: true, - Computed: true, - }, - "new_field": { - Type: cty.String, - Optional: true, - Computed: true, - }, - }, - }, - Nesting: configschema.NestingSet, - }, - }, - }, + Schema: testSchemaPlus(configschema.NestingSet), ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = [ + - { + - mount_point = "/var/diska" -> null + - size = "50GB" -> null + }, + ] + id = "i-02ae66f368e8518a9" - root_block_device { - new_field = "new_value" -> null @@ -3238,6 +2852,10 @@ func TestResourceChange_nestedMap(t *testing.T) { Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.MapValEmpty(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + })), "root_block_device": cty.MapValEmpty(cty.Object(map[string]cty.Type{ "volume_type": cty.String, })), @@ -3245,38 +2863,29 @@ func TestResourceChange_nestedMap(t *testing.T) { After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-AFTER"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.NullVal(cty.String), + }), + }), "root_block_device": cty.MapVal(map[string]cty.Value{ "a": cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("gp2"), }), }), - }), - RequiredReplace: cty.NewPathSet(), - Tainted: false, - Schema: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "root_block_device": { - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "volume_type": { - Type: cty.String, - Optional: true, - Computed: true, - }, - }, - }, - Nesting: configschema.NestingMap, - }, - }, - }, + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchema(configschema.NestingMap), ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = { + + "disk_a" = { + + mount_point = "/var/diska" + }, + } + id = "i-02ae66f368e8518a9" + root_block_device "a" { + volume_type = "gp2" @@ -3290,6 +2899,12 @@ func TestResourceChange_nestedMap(t *testing.T) { Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.NullVal(cty.String), + }), + }), "root_block_device": cty.MapVal(map[string]cty.Value{ "a": cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("gp2"), @@ -3300,6 +2915,12 @@ func TestResourceChange_nestedMap(t *testing.T) { After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-AFTER"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), "root_block_device": cty.MapVal(map[string]cty.Value{ "a": cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("gp2"), @@ -3308,51 +2929,21 @@ func TestResourceChange_nestedMap(t *testing.T) { }), }), RequiredReplace: cty.NewPathSet(), - Tainted: false, - Schema: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "root_block_device": { - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "volume_type": { - Type: cty.String, - Optional: true, - Computed: true, - }, - "new_field": { - Type: cty.String, - Optional: true, - Computed: true, - }, - }, - }, - Nesting: configschema.NestingMap, - }, - }, - }, + Schema: testSchemaPlus(configschema.NestingMap), ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" - - ~ root_block_device "a" { - + new_field = "new_value" - # (1 unchanged attribute hidden) + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = { + ~ "disk_a" = { + + size = "50GB" + # (1 unchanged attribute hidden) + }, } - } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" + id = "i-02ae66f368e8518a9" ~ root_block_device "a" { + new_field = "new_value" - volume_type = "gp2" + # (1 unchanged attribute hidden) } } `, @@ -3363,6 +2954,12 @@ func TestResourceChange_nestedMap(t *testing.T) { Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), "root_block_device": cty.MapVal(map[string]cty.Value{ "a": cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("gp2"), @@ -3373,6 +2970,16 @@ func TestResourceChange_nestedMap(t *testing.T) { After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-AFTER"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + "disk_2": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/disk2"), + "size": cty.StringVal("50GB"), + }), + }), "root_block_device": cty.MapVal(map[string]cty.Value{ "a": cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("gp2"), @@ -3385,65 +2992,40 @@ func TestResourceChange_nestedMap(t *testing.T) { }), }), RequiredReplace: cty.NewPathSet(), - Tainted: false, - Schema: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "root_block_device": { - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "volume_type": { - Type: cty.String, - Optional: true, - Computed: true, - }, - "new_field": { - Type: cty.String, - Optional: true, - Computed: true, - }, - }, - }, - Nesting: configschema.NestingMap, - }, - }, - }, + Schema: testSchemaPlus(configschema.NestingMap), ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" - - + root_block_device "b" { - + new_field = "new_value" - + volume_type = "gp2" + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = { + + "disk_2" = { + + mount_point = "/var/disk2" + + size = "50GB" + }, + # (1 unchanged element hidden) } - # (1 unchanged block hidden) - } -`, - VerboseOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" + id = "i-02ae66f368e8518a9" - root_block_device "a" { - volume_type = "gp2" - } + root_block_device "b" { + new_field = "new_value" + volume_type = "gp2" } + # (1 unchanged block hidden) } `, }, "force-new update (whole block)": { - Action: plans.DeleteThenCreate, - Mode: addrs.ManagedResourceMode, + Action: plans.DeleteThenCreate, + ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, + Mode: addrs.ManagedResourceMode, Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), "root_block_device": cty.MapVal(map[string]cty.Value{ "a": cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("gp2"), @@ -3456,6 +3038,12 @@ func TestResourceChange_nestedMap(t *testing.T) { After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-AFTER"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("100GB"), + }), + }), "root_block_device": cty.MapVal(map[string]cty.Value{ "a": cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("different"), @@ -3468,50 +3056,25 @@ func TestResourceChange_nestedMap(t *testing.T) { RequiredReplace: cty.NewPathSet(cty.Path{ cty.GetAttrStep{Name: "root_block_device"}, cty.IndexStep{Key: cty.StringVal("a")}, - }), - Tainted: false, - Schema: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "root_block_device": { - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "volume_type": { - Type: cty.String, - Optional: true, - Computed: true, - }, - }, - }, - Nesting: configschema.NestingMap, - }, - }, }, + cty.Path{cty.GetAttrStep{Name: "disks"}}, + ), + Schema: testSchema(configschema.NestingMap), ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" - - ~ root_block_device "a" { # forces replacement - ~ volume_type = "gp2" -> "different" + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = { + ~ "disk_a" = { # forces replacement + ~ size = "50GB" -> "100GB" + # (1 unchanged attribute hidden) + }, } - # (1 unchanged block hidden) - } -`, - VerboseOutput: ` # test_instance.example must be replaced --/+ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" + id = "i-02ae66f368e8518a9" ~ root_block_device "a" { # forces replacement ~ volume_type = "gp2" -> "different" } - root_block_device "b" { - volume_type = "standard" - } + # (1 unchanged block hidden) } `, }, @@ -3521,6 +3084,12 @@ func TestResourceChange_nestedMap(t *testing.T) { Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), "root_block_device": cty.MapVal(map[string]cty.Value{ "a": cty.ObjectVal(map[string]cty.Value{ "volume_type": cty.StringVal("gp2"), @@ -3531,91 +3100,33 @@ func TestResourceChange_nestedMap(t *testing.T) { After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-AFTER"), + "disks": cty.MapValEmpty(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + })), "root_block_device": cty.MapValEmpty(cty.Object(map[string]cty.Type{ "volume_type": cty.String, "new_field": cty.String, })), }), RequiredReplace: cty.NewPathSet(), - Tainted: false, - Schema: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "root_block_device": { - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "volume_type": { - Type: cty.String, - Optional: true, - Computed: true, - }, - "new_field": { - Type: cty.String, - Optional: true, - Computed: true, - }, - }, - }, - Nesting: configschema.NestingMap, - }, - }, - }, + Schema: testSchemaPlus(configschema.NestingMap), ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = { + - "disk_a" = { + - mount_point = "/var/diska" -> null + - size = "50GB" -> null + }, + } + id = "i-02ae66f368e8518a9" - root_block_device "a" { - new_field = "new_value" -> null - volume_type = "gp2" -> null } } -`, - }, - "in-place sequence update - deletion": { - Action: plans.Update, - Mode: addrs.ManagedResourceMode, - Before: cty.ObjectVal(map[string]cty.Value{ - "list": cty.ListVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{"attr": cty.StringVal("x")}), - cty.ObjectVal(map[string]cty.Value{"attr": cty.StringVal("y")}), - }), - }), - After: cty.ObjectVal(map[string]cty.Value{ - "list": cty.ListVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{"attr": cty.StringVal("y")}), - cty.ObjectVal(map[string]cty.Value{"attr": cty.StringVal("z")}), - }), - }), - RequiredReplace: cty.NewPathSet(), - Tainted: false, - Schema: &configschema.Block{ - BlockTypes: map[string]*configschema.NestedBlock{ - "list": { - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "attr": { - Type: cty.String, - Required: true, - }, - }, - }, - Nesting: configschema.NestingList, - }, - }, - }, - ExpectedOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { - ~ list { - ~ attr = "x" -> "y" - } - ~ list { - ~ attr = "y" -> "z" - } - } `, }, } @@ -3685,7 +3196,6 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, Schema: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "id": {Type: cty.String, Optional: true, Computed: true}, @@ -3838,7 +3348,6 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, Schema: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "id": {Type: cty.String, Optional: true, Computed: true}, @@ -3974,7 +3483,6 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, Schema: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "id": {Type: cty.String, Optional: true, Computed: true}, @@ -4110,7 +3618,6 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, Schema: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "id": {Type: cty.String, Optional: true, Computed: true}, @@ -4248,7 +3755,6 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, Schema: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "id": {Type: cty.String, Optional: true, Computed: true}, @@ -4381,7 +3887,6 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { }, }, RequiredReplace: cty.NewPathSet(), - Tainted: false, Schema: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "id": {Type: cty.String, Optional: true, Computed: true}, @@ -4484,7 +3989,6 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { cty.GetAttrPath("ami"), cty.GetAttrPath("nested_block_set"), ), - Tainted: false, ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { ~ ami = (sensitive) # forces replacement @@ -4495,6 +3999,78 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { # so its contents will not be displayed. } } +`, + }, + "update with sensitive attribute forcing replacement": { + Action: plans.DeleteThenCreate, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "ami": {Type: cty.String, Optional: true, Computed: true, Sensitive: true}, + }, + }, + RequiredReplace: cty.NewPathSet( + cty.GetAttrPath("ami"), + ), + ExpectedOutput: ` # test_instance.example must be replaced +-/+ resource "test_instance" "example" { + ~ ami = (sensitive value) # forces replacement + id = "i-02ae66f368e8518a9" + } +`, + }, + "update with sensitive nested type attribute forcing replacement": { + Action: plans.DeleteThenCreate, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "conn_info": cty.ObjectVal(map[string]cty.Value{ + "user": cty.StringVal("not-secret"), + "password": cty.StringVal("top-secret"), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "conn_info": cty.ObjectVal(map[string]cty.Value{ + "user": cty.StringVal("not-secret"), + "password": cty.StringVal("new-secret"), + }), + }), + Schema: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "conn_info": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSingle, + Attributes: map[string]*configschema.Attribute{ + "user": {Type: cty.String, Optional: true}, + "password": {Type: cty.String, Optional: true, Sensitive: true}, + }, + }, + }, + }, + }, + RequiredReplace: cty.NewPathSet( + cty.GetAttrPath("conn_info"), + cty.GetAttrPath("password"), + ), + ExpectedOutput: ` # test_instance.example must be replaced +-/+ resource "test_instance" "example" { + ~ conn_info = { # forces replacement + ~ password = (sensitive value) + # (1 unchanged attribute hidden) + } + id = "i-02ae66f368e8518a9" + } `, }, } @@ -4503,6 +4079,7 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { type testCase struct { Action plans.Action + ActionReason plans.ResourceInstanceChangeActionReason Mode addrs.ResourceMode Before cty.Value BeforeValMarks []cty.PathValueMarks @@ -4510,12 +4087,7 @@ type testCase struct { After cty.Value Schema *configschema.Block RequiredReplace cty.PathSet - Tainted bool ExpectedOutput string - - // This field and all associated values can be removed if the concise diff - // experiment succeeds. - VerboseOutput string } func runTestCases(t *testing.T, testCases map[string]testCase) { @@ -4566,28 +4138,15 @@ func runTestCases(t *testing.T, testCases map[string]testCase) { BeforeValMarks: tc.BeforeValMarks, AfterValMarks: tc.AfterValMarks, }, + ActionReason: tc.ActionReason, RequiredReplace: tc.RequiredReplace, } - experiment.SetEnabled(experiment.X_concise_diff, true) - output := ResourceChange(change, tc.Tainted, tc.Schema, color) + output := ResourceChange(change, tc.Schema, color) if output != tc.ExpectedOutput { t.Errorf("Unexpected diff.\ngot:\n%s\nwant:\n%s\n", output, tc.ExpectedOutput) t.Errorf("%s", cmp.Diff(output, tc.ExpectedOutput)) } - - // Temporary coverage for verbose diff behaviour. All lines below - // in this function can be removed if the concise diff experiment - // succeeds. - if tc.VerboseOutput == "" { - return - } - experiment.SetEnabled(experiment.X_concise_diff, false) - output = ResourceChange(change, tc.Tainted, tc.Schema, color) - if output != tc.VerboseOutput { - t.Errorf("Unexpected diff.\ngot:\n%s\nwant:\n%s\n", output, tc.VerboseOutput) - t.Errorf("%s", cmp.Diff(output, tc.VerboseOutput)) - } }) } } @@ -4694,7 +4253,6 @@ func TestOutputChanges(t *testing.T) { for name, tc := range testCases { t.Run(name, func(t *testing.T) { - experiment.SetEnabled(experiment.X_concise_diff, true) output := OutputChanges(tc.changes, color) if output != tc.output { t.Errorf("Unexpected diff.\ngot:\n%s\nwant:\n%s\n", output, tc.output) @@ -4723,3 +4281,74 @@ func outputChange(name string, before, after cty.Value, sensitive bool) *plans.O return changeSrc } + +// A basic test schema using a configurable NestingMode for one (NestedType) attribute and one block +func testSchema(nesting configschema.NestingMode) *configschema.Block { + return &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "ami": {Type: cty.String, Optional: true}, + "disks": { + NestedType: &configschema.Object{ + Attributes: map[string]*configschema.Attribute{ + "mount_point": {Type: cty.String, Optional: true}, + "size": {Type: cty.String, Optional: true}, + }, + Nesting: nesting, + }, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "root_block_device": { + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "volume_type": { + Type: cty.String, + Optional: true, + Computed: true, + }, + }, + }, + Nesting: nesting, + }, + }, + } +} + +// similar to testSchema with the addition of a "new_field" block +func testSchemaPlus(nesting configschema.NestingMode) *configschema.Block { + return &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "ami": {Type: cty.String, Optional: true}, + "disks": { + NestedType: &configschema.Object{ + Attributes: map[string]*configschema.Attribute{ + "mount_point": {Type: cty.String, Optional: true}, + "size": {Type: cty.String, Optional: true}, + }, + Nesting: nesting, + }, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "root_block_device": { + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "volume_type": { + Type: cty.String, + Optional: true, + Computed: true, + }, + "new_field": { + Type: cty.String, + Optional: true, + Computed: true, + }, + }, + }, + Nesting: nesting, + }, + }, + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/format/state.go b/vendor/github.com/hashicorp/terraform/command/format/state.go index 6d6e2cee..9fcb41f1 100644 --- a/vendor/github.com/hashicorp/terraform/command/format/state.go +++ b/vendor/github.com/hashicorp/terraform/command/format/state.go @@ -45,9 +45,10 @@ func State(opts *StateOpts) string { buf := bytes.NewBufferString("[reset]") p := blockBodyDiffPrinter{ - buf: buf, - color: opts.Color, - action: plans.NoOp, + buf: buf, + color: opts.Color, + action: plans.NoOp, + verbose: true, } // Format all the modules @@ -74,7 +75,11 @@ func State(opts *StateOpts) string { for _, k := range ks { v := m.OutputValues[k] p.buf.WriteString(fmt.Sprintf("%s = ", k)) - p.writeValue(v.Value, plans.NoOp, 0) + if v.Sensitive { + p.buf.WriteString("(sensitive value)") + } else { + p.writeValue(v.Value, plans.NoOp, 0) + } p.buf.WriteString("\n") } } @@ -209,116 +214,3 @@ func formatStateModule(p blockBodyDiffPrinter, m *states.Module, schemas *terraf } p.buf.WriteString("\n") } - -func formatNestedList(indent string, outputList []interface{}) string { - outputBuf := new(bytes.Buffer) - outputBuf.WriteString(fmt.Sprintf("%s[", indent)) - - lastIdx := len(outputList) - 1 - - for i, value := range outputList { - outputBuf.WriteString(fmt.Sprintf("\n%s%s%s", indent, " ", value)) - if i != lastIdx { - outputBuf.WriteString(",") - } - } - - outputBuf.WriteString(fmt.Sprintf("\n%s]", indent)) - return strings.TrimPrefix(outputBuf.String(), "\n") -} - -func formatListOutput(indent, outputName string, outputList []interface{}) string { - keyIndent := "" - - outputBuf := new(bytes.Buffer) - - if outputName != "" { - outputBuf.WriteString(fmt.Sprintf("%s%s = [", indent, outputName)) - keyIndent = " " - } - - lastIdx := len(outputList) - 1 - - for i, value := range outputList { - switch typedValue := value.(type) { - case string: - outputBuf.WriteString(fmt.Sprintf("\n%s%s%s", indent, keyIndent, value)) - case []interface{}: - outputBuf.WriteString(fmt.Sprintf("\n%s%s", indent, - formatNestedList(indent+keyIndent, typedValue))) - case map[string]interface{}: - outputBuf.WriteString(fmt.Sprintf("\n%s%s", indent, - formatNestedMap(indent+keyIndent, typedValue))) - } - - if lastIdx != i { - outputBuf.WriteString(",") - } - } - - if outputName != "" { - if len(outputList) > 0 { - outputBuf.WriteString(fmt.Sprintf("\n%s]", indent)) - } else { - outputBuf.WriteString("]") - } - } - - return strings.TrimPrefix(outputBuf.String(), "\n") -} - -func formatNestedMap(indent string, outputMap map[string]interface{}) string { - ks := make([]string, 0, len(outputMap)) - for k := range outputMap { - ks = append(ks, k) - } - sort.Strings(ks) - - outputBuf := new(bytes.Buffer) - outputBuf.WriteString(fmt.Sprintf("%s{", indent)) - - lastIdx := len(outputMap) - 1 - for i, k := range ks { - v := outputMap[k] - outputBuf.WriteString(fmt.Sprintf("\n%s%s = %v", indent+" ", k, v)) - - if lastIdx != i { - outputBuf.WriteString(",") - } - } - - outputBuf.WriteString(fmt.Sprintf("\n%s}", indent)) - - return strings.TrimPrefix(outputBuf.String(), "\n") -} - -func formatMapOutput(indent, outputName string, outputMap map[string]interface{}) string { - ks := make([]string, 0, len(outputMap)) - for k := range outputMap { - ks = append(ks, k) - } - sort.Strings(ks) - - keyIndent := "" - - outputBuf := new(bytes.Buffer) - if outputName != "" { - outputBuf.WriteString(fmt.Sprintf("%s%s = {", indent, outputName)) - keyIndent = " " - } - - for _, k := range ks { - v := outputMap[k] - outputBuf.WriteString(fmt.Sprintf("\n%s%s%s = %v", indent, keyIndent, k, v)) - } - - if outputName != "" { - if len(outputMap) > 0 { - outputBuf.WriteString(fmt.Sprintf("\n%s}", indent)) - } else { - outputBuf.WriteString("}") - } - } - - return strings.TrimPrefix(outputBuf.String(), "\n") -} diff --git a/vendor/github.com/hashicorp/terraform/command/format/state_test.go b/vendor/github.com/hashicorp/terraform/command/format/state_test.go index d7864ee1..fb9da80f 100644 --- a/vendor/github.com/hashicorp/terraform/command/format/state_test.go +++ b/vendor/github.com/hashicorp/terraform/command/format/state_test.go @@ -9,15 +9,9 @@ import ( "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/colorstring" "github.com/zclconf/go-cty/cty" ) -var disabledColorize = &colorstring.Colorize{ - Colors: colorstring.DefaultColors, - Disable: true, -} - func TestState(t *testing.T) { tests := []struct { State *StateOpts @@ -92,43 +86,49 @@ func testProvider() *terraform.MockProvider { return providers.ReadResourceResponse{NewState: req.PriorState} } - p.GetSchemaReturn = testProviderSchema() + p.GetProviderSchemaResponse = testProviderSchema() return p } -func testProviderSchema() *terraform.ProviderSchema { - return &terraform.ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "region": {Type: cty.String, Optional: true}, +func testProviderSchema() *providers.GetProviderSchemaResponse { + return &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "region": {Type: cty.String, Optional: true}, + }, }, }, - ResourceTypes: map[string]*configschema.Block{ + ResourceTypes: map[string]providers.Schema{ "test_resource": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Computed: true}, - "foo": {Type: cty.String, Optional: true}, - "woozles": {Type: cty.String, Optional: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "nested": { - Nesting: configschema.NestingList, - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "compute": {Type: cty.String, Optional: true}, - "value": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + "foo": {Type: cty.String, Optional: true}, + "woozles": {Type: cty.String, Optional: true}, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "nested": { + Nesting: configschema.NestingList, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "compute": {Type: cty.String, Optional: true}, + "value": {Type: cty.String, Optional: true}, + }, }, }, }, }, }, }, - DataSources: map[string]*configschema.Block{ + DataSources: map[string]providers.Schema{ "test_data_source": { - Attributes: map[string]*configschema.Attribute{ - "compute": {Type: cty.String, Optional: true}, - "value": {Type: cty.String, Computed: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "compute": {Type: cty.String, Optional: true}, + "value": {Type: cty.String, Computed: true}, + }, }, }, }, @@ -139,7 +139,7 @@ func testSchemas() *terraform.Schemas { provider := testProvider() return &terraform.Schemas{ Providers: map[addrs.Provider]*terraform.ProviderSchema{ - addrs.NewDefaultProvider("test"): provider.GetSchemaReturn, + addrs.NewDefaultProvider("test"): provider.ProviderSchema(), }, } } @@ -219,7 +219,7 @@ map_var = { "first" = "foo" "second" = "bar" } -sensitive_var = "secret!!!" +sensitive_var = (sensitive value) string_var = "string value"` func basicState(t *testing.T) *states.State { diff --git a/vendor/github.com/hashicorp/terraform/command/format/trivia.go b/vendor/github.com/hashicorp/terraform/command/format/trivia.go new file mode 100644 index 00000000..b97d50b0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/format/trivia.go @@ -0,0 +1,58 @@ +package format + +import ( + "strings" + + "github.com/mitchellh/colorstring" + wordwrap "github.com/mitchellh/go-wordwrap" +) + +// HorizontalRule returns a newline character followed by a number of +// horizontal line characters to fill the given width. +// +// If the given colorize has colors enabled, the rule will also be given a +// dark grey color to attempt to visually de-emphasize it for sighted users. +// +// This is intended for printing to the UI via mitchellh/cli.UI.Output, or +// similar, which will automatically append a trailing newline too. +func HorizontalRule(color *colorstring.Colorize, width int) string { + if width <= 1 { + return "\n" + } + rule := strings.Repeat("─", width-1) + if color == nil { // sometimes unit tests don't populate this properly + return "\n" + rule + } + return color.Color("[dark_gray]\n" + rule) +} + +// WordWrap takes a string containing unbroken lines of text and inserts +// newline characters to try to make the text fit within the given width. +// +// The string can already contain newline characters, for example if you are +// trying to render multiple paragraphs of text. (In that case, our usual +// style would be to have _two_ newline characters as the paragraph separator.) +// +// As a special case, any line that begins with at least one space will be left +// unbroken. This allows including literal segments in the output, such as +// code snippets or filenames, where word wrapping would be confusing. +func WordWrap(str string, width int) string { + if width <= 1 { + // Silly edge case. We'll just return the original string to avoid + // panicking or doing other weird stuff. + return str + } + + var buf strings.Builder + lines := strings.Split(str, "\n") + for i, line := range lines { + if !strings.HasPrefix(line, " ") { + line = wordwrap.WrapString(line, uint(width-1)) + } + if i > 0 { + buf.WriteByte('\n') // reintroduce the newlines we skipped in Scan + } + buf.WriteString(line) + } + return buf.String() +} diff --git a/vendor/github.com/hashicorp/terraform/command/get.go b/vendor/github.com/hashicorp/terraform/command/get.go index 238020a9..238f3ec2 100644 --- a/vendor/github.com/hashicorp/terraform/command/get.go +++ b/vendor/github.com/hashicorp/terraform/command/get.go @@ -44,7 +44,7 @@ func (c *GetCommand) Run(args []string) int { func (c *GetCommand) Help() string { helpText := ` -Usage: terraform get [options] PATH +Usage: terraform [global options] get [options] PATH Downloads and installs modules needed for the configuration given by PATH. @@ -54,6 +54,10 @@ Usage: terraform get [options] PATH already downloaded, it will not be redownloaded or checked for updates unless the -update flag is specified. + Module installation also happens automatically by default as part of + the "terraform init" command, so you should rarely need to run this + command separately. + Options: -update Check already-downloaded modules for available updates @@ -66,7 +70,7 @@ Options: } func (c *GetCommand) Synopsis() string { - return "Download and install modules for the configuration" + return "Install or upgrade remote Terraform modules" } func getModules(m *Meta, path string, upgrade bool) tfdiags.Diagnostics { diff --git a/vendor/github.com/hashicorp/terraform/command/get_test.go b/vendor/github.com/hashicorp/terraform/command/get_test.go index 013b9779..7d913742 100644 --- a/vendor/github.com/hashicorp/terraform/command/get_test.go +++ b/vendor/github.com/hashicorp/terraform/command/get_test.go @@ -9,8 +9,10 @@ import ( ) func TestGet(t *testing.T) { - tmp, cwd := testCwd(t) - defer testFixCwd(t, tmp, cwd) + td := tempDir(t) + testCopyDir(t, testFixturePath("get"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() ui := new(cli.MockUi) c := &GetCommand{ @@ -21,9 +23,7 @@ func TestGet(t *testing.T) { }, } - args := []string{ - testFixturePath("get"), - } + args := []string{} if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) } @@ -53,7 +53,7 @@ func TestGet_multipleArgs(t *testing.T) { } } -func TestGet_noArgs(t *testing.T) { +func TestGet_update(t *testing.T) { td := tempDir(t) testCopyDir(t, testFixturePath("get"), td) defer os.RemoveAll(td) @@ -68,33 +68,8 @@ func TestGet_noArgs(t *testing.T) { }, } - args := []string{} - if code := c.Run(args); code != 0 { - t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) - } - - output := ui.OutputWriter.String() - if !strings.Contains(output, "- foo in") { - t.Fatalf("doesn't look like get: %s", output) - } -} - -func TestGet_update(t *testing.T) { - tmp, cwd := testCwd(t) - defer testFixCwd(t, tmp, cwd) - - ui := new(cli.MockUi) - c := &GetCommand{ - Meta: Meta{ - testingOverrides: metaOverridesForProvider(testProvider()), - Ui: ui, - dataDir: tempDir(t), - }, - } - args := []string{ "-update", - testFixturePath("get"), } if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) diff --git a/vendor/github.com/hashicorp/terraform/command/graph.go b/vendor/github.com/hashicorp/terraform/command/graph.go index cdfaaa2e..2144fd2b 100644 --- a/vendor/github.com/hashicorp/terraform/command/graph.go +++ b/vendor/github.com/hashicorp/terraform/command/graph.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/plans/planfile" "github.com/hashicorp/terraform/tfdiags" "github.com/hashicorp/terraform/backend" @@ -23,6 +23,7 @@ func (c *GraphCommand) Run(args []string) int { var graphTypeStr string var moduleDepth int var verbose bool + var planPath string args = c.Meta.process(args) cmdFlags := c.Meta.defaultFlagSet("graph") @@ -30,6 +31,7 @@ func (c *GraphCommand) Run(args []string) int { cmdFlags.StringVar(&graphTypeStr, "type", "", "type") cmdFlags.IntVar(&moduleDepth, "module-depth", -1, "module-depth") cmdFlags.BoolVar(&verbose, "verbose", false, "verbose") + cmdFlags.StringVar(&planPath, "plan", "", "plan") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s\n", err.Error())) @@ -48,16 +50,14 @@ func (c *GraphCommand) Run(args []string) int { return 1 } - // Check if the path is a plan - var plan *plans.Plan - planFile, err := c.PlanFile(configPath) - if err != nil { - c.Ui.Error(err.Error()) - return 1 - } - if planFile != nil { - // Reset for backend loading - configPath = "" + // Try to load plan if path is specified + var planFile *planfile.Reader + if planPath != "" { + planFile, err = c.PlanFile(planPath) + if err != nil { + c.Ui.Error(err.Error()) + return 1 + } } var diags tfdiags.Diagnostics @@ -87,6 +87,9 @@ func (c *GraphCommand) Run(args []string) int { return 1 } + // This is a read-only command + c.ignoreRemoteBackendVersionConflict(b) + // Build the operation opReq := c.Operation(b) opReq.ConfigDir = configPath @@ -109,7 +112,7 @@ func (c *GraphCommand) Run(args []string) int { // Determine the graph type graphType := terraform.GraphTypePlan - if plan != nil { + if planFile != nil { graphType = terraform.GraphTypeApply } @@ -160,10 +163,10 @@ func (c *GraphCommand) Run(args []string) int { func (c *GraphCommand) Help() string { helpText := ` -Usage: terraform graph [options] [DIR] +Usage: terraform [global options] graph [options] Outputs the visual execution graph of Terraform resources according to - configuration files in DIR (or the current directory if omitted). + either the current configuration or an execution plan. The graph is outputted in DOT format. The typical program that can read this format is GraphViz, but many web services are also available @@ -177,6 +180,9 @@ Usage: terraform graph [options] [DIR] Options: + -plan=tfplan Render graph using the specified plan file instead of the + configuration in the current directory. + -draw-cycles Highlight any cycles in the graph with colored edges. This helps when diagnosing cycle errors. @@ -190,5 +196,5 @@ Options: } func (c *GraphCommand) Synopsis() string { - return "Create a visual graph of Terraform resources" + return "Generate a Graphviz graph of the steps in an operation" } diff --git a/vendor/github.com/hashicorp/terraform/command/graph_test.go b/vendor/github.com/hashicorp/terraform/command/graph_test.go index dd9f4413..392db1a9 100644 --- a/vendor/github.com/hashicorp/terraform/command/graph_test.go +++ b/vendor/github.com/hashicorp/terraform/command/graph_test.go @@ -14,8 +14,10 @@ import ( ) func TestGraph(t *testing.T) { - tmp, cwd := testCwd(t) - defer testFixCwd(t, tmp, cwd) + td := tempDir(t) + testCopyDir(t, testFixturePath("graph"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() ui := new(cli.MockUi) c := &GraphCommand{ @@ -25,9 +27,7 @@ func TestGraph(t *testing.T) { }, } - args := []string{ - testFixturePath("graph"), - } + args := []string{} if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) } @@ -57,14 +57,10 @@ func TestGraph_multipleArgs(t *testing.T) { } func TestGraph_noArgs(t *testing.T) { - cwd, err := os.Getwd() - if err != nil { - t.Fatalf("err: %s", err) - } - if err := os.Chdir(testFixturePath("graph")); err != nil { - t.Fatalf("err: %s", err) - } - defer os.Chdir(cwd) + td := tempDir(t) + testCopyDir(t, testFixturePath("graph"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() ui := new(cli.MockUi) c := &GraphCommand{ @@ -154,7 +150,7 @@ func TestGraph_plan(t *testing.T) { } args := []string{ - planPath, + "-plan", planPath, } if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) diff --git a/vendor/github.com/hashicorp/terraform/command/hook_ui_test.go b/vendor/github.com/hashicorp/terraform/command/hook_ui_test.go deleted file mode 100644 index 03b62add..00000000 --- a/vendor/github.com/hashicorp/terraform/command/hook_ui_test.go +++ /dev/null @@ -1,311 +0,0 @@ -package command - -import ( - "fmt" - "regexp" - "testing" - "time" - - "github.com/mitchellh/cli" - "github.com/mitchellh/colorstring" - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/plans" - "github.com/hashicorp/terraform/states" - "github.com/hashicorp/terraform/terraform" -) - -func TestUiHookPreApply_periodicTimer(t *testing.T) { - ui := cli.NewMockUi() - h := &UiHook{ - Colorize: &colorstring.Colorize{ - Colors: colorstring.DefaultColors, - Disable: true, - Reset: true, - }, - Ui: ui, - PeriodicUiTimer: 1 * time.Second, - } - h.init() - h.resources = map[string]uiResourceState{ - "data.aws_availability_zones.available": uiResourceState{ - Op: uiResourceDestroy, - Start: time.Now(), - }, - } - - addr := addrs.Resource{ - Mode: addrs.DataResourceMode, - Type: "aws_availability_zones", - Name: "available", - }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) - - priorState := cty.ObjectVal(map[string]cty.Value{ - "id": cty.StringVal("2017-03-05 10:56:59.298784526 +0000 UTC"), - "names": cty.ListVal([]cty.Value{ - cty.StringVal("us-east-1a"), - cty.StringVal("us-east-1b"), - cty.StringVal("us-east-1c"), - cty.StringVal("us-east-1d"), - }), - }) - plannedNewState := cty.NullVal(cty.Object(map[string]cty.Type{ - "id": cty.String, - "names": cty.List(cty.String), - })) - - action, err := h.PreApply(addr, states.CurrentGen, plans.Delete, priorState, plannedNewState) - if err != nil { - t.Fatal(err) - } - if action != terraform.HookActionContinue { - t.Fatalf("Expected hook to continue, given: %#v", action) - } - - time.Sleep(3100 * time.Millisecond) - - // stop the background writer - uiState := h.resources[addr.String()] - close(uiState.DoneCh) - <-uiState.done - - expectedOutput := `data.aws_availability_zones.available: Destroying... [id=2017-03-05 10:56:59.298784526 +0000 UTC] -data.aws_availability_zones.available: Still destroying... [id=2017-03-05 10:56:59.298784526 +0000 UTC, 1s elapsed] -data.aws_availability_zones.available: Still destroying... [id=2017-03-05 10:56:59.298784526 +0000 UTC, 2s elapsed] -data.aws_availability_zones.available: Still destroying... [id=2017-03-05 10:56:59.298784526 +0000 UTC, 3s elapsed] -` - output := ui.OutputWriter.String() - if output != expectedOutput { - t.Fatalf("Output didn't match.\nExpected: %q\nGiven: %q", expectedOutput, output) - } - - expectedErrOutput := "" - errOutput := ui.ErrorWriter.String() - if errOutput != expectedErrOutput { - t.Fatalf("Error output didn't match.\nExpected: %q\nGiven: %q", expectedErrOutput, errOutput) - } -} - -func TestUiHookPreApply_destroy(t *testing.T) { - ui := cli.NewMockUi() - h := &UiHook{ - Colorize: &colorstring.Colorize{ - Colors: colorstring.DefaultColors, - Disable: true, - Reset: true, - }, - Ui: ui, - } - h.init() - h.resources = map[string]uiResourceState{ - "data.aws_availability_zones.available": uiResourceState{ - Op: uiResourceDestroy, - Start: time.Now(), - }, - } - - addr := addrs.Resource{ - Mode: addrs.DataResourceMode, - Type: "aws_availability_zones", - Name: "available", - }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) - - priorState := cty.ObjectVal(map[string]cty.Value{ - "id": cty.StringVal("2017-03-05 10:56:59.298784526 +0000 UTC"), - "names": cty.ListVal([]cty.Value{ - cty.StringVal("us-east-1a"), - cty.StringVal("us-east-1b"), - cty.StringVal("us-east-1c"), - cty.StringVal("us-east-1d"), - }), - }) - plannedNewState := cty.NullVal(cty.Object(map[string]cty.Type{ - "id": cty.String, - "names": cty.List(cty.String), - })) - - action, err := h.PreApply(addr, states.CurrentGen, plans.Delete, priorState, plannedNewState) - if err != nil { - t.Fatal(err) - } - if action != terraform.HookActionContinue { - t.Fatalf("Expected hook to continue, given: %#v", action) - } - - // stop the background writer - uiState := h.resources[addr.String()] - close(uiState.DoneCh) - <-uiState.done - - expectedOutput := "data.aws_availability_zones.available: Destroying... [id=2017-03-05 10:56:59.298784526 +0000 UTC]\n" - output := ui.OutputWriter.String() - if output != expectedOutput { - t.Fatalf("Output didn't match.\nExpected: %q\nGiven: %q", expectedOutput, output) - } - - expectedErrOutput := "" - errOutput := ui.ErrorWriter.String() - if errOutput != expectedErrOutput { - t.Fatalf("Error output didn't match.\nExpected: %q\nGiven: %q", expectedErrOutput, errOutput) - } -} - -func TestUiHookPostApply_emptyState(t *testing.T) { - ui := cli.NewMockUi() - h := &UiHook{ - Colorize: &colorstring.Colorize{ - Colors: colorstring.DefaultColors, - Disable: true, - Reset: true, - }, - Ui: ui, - } - h.init() - h.resources = map[string]uiResourceState{ - "data.google_compute_zones.available": uiResourceState{ - Op: uiResourceDestroy, - Start: time.Now(), - }, - } - - addr := addrs.Resource{ - Mode: addrs.DataResourceMode, - Type: "google_compute_zones", - Name: "available", - }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) - - newState := cty.NullVal(cty.Object(map[string]cty.Type{ - "id": cty.String, - "names": cty.List(cty.String), - })) - - action, err := h.PostApply(addr, states.CurrentGen, newState, nil) - if err != nil { - t.Fatal(err) - } - if action != terraform.HookActionContinue { - t.Fatalf("Expected hook to continue, given: %#v", action) - } - - expectedRegexp := "^data.google_compute_zones.available: Destruction complete after -?[a-z0-9µ.]+\n$" - output := ui.OutputWriter.String() - if matched, _ := regexp.MatchString(expectedRegexp, output); !matched { - t.Fatalf("Output didn't match regexp.\nExpected: %q\nGiven: %q", expectedRegexp, output) - } - - expectedErrOutput := "" - errOutput := ui.ErrorWriter.String() - if errOutput != expectedErrOutput { - t.Fatalf("Error output didn't match.\nExpected: %q\nGiven: %q", expectedErrOutput, errOutput) - } -} - -func TestTruncateId(t *testing.T) { - testCases := []struct { - Input string - Expected string - MaxLen int - }{ - { - Input: "Hello world", - Expected: "H...d", - MaxLen: 3, - }, - { - Input: "Hello world", - Expected: "H...d", - MaxLen: 5, - }, - { - Input: "Hello world", - Expected: "He...d", - MaxLen: 6, - }, - { - Input: "Hello world", - Expected: "He...ld", - MaxLen: 7, - }, - { - Input: "Hello world", - Expected: "Hel...ld", - MaxLen: 8, - }, - { - Input: "Hello world", - Expected: "Hel...rld", - MaxLen: 9, - }, - { - Input: "Hello world", - Expected: "Hell...rld", - MaxLen: 10, - }, - { - Input: "Hello world", - Expected: "Hello world", - MaxLen: 11, - }, - { - Input: "Hello world", - Expected: "Hello world", - MaxLen: 12, - }, - { - Input: "あいうえおかきくけこさ", - Expected: "あ...さ", - MaxLen: 3, - }, - { - Input: "あいうえおかきくけこさ", - Expected: "あ...さ", - MaxLen: 5, - }, - { - Input: "あいうえおかきくけこさ", - Expected: "あい...さ", - MaxLen: 6, - }, - { - Input: "あいうえおかきくけこさ", - Expected: "あい...こさ", - MaxLen: 7, - }, - { - Input: "あいうえおかきくけこさ", - Expected: "あいう...こさ", - MaxLen: 8, - }, - { - Input: "あいうえおかきくけこさ", - Expected: "あいう...けこさ", - MaxLen: 9, - }, - { - Input: "あいうえおかきくけこさ", - Expected: "あいうえ...けこさ", - MaxLen: 10, - }, - { - Input: "あいうえおかきくけこさ", - Expected: "あいうえおかきくけこさ", - MaxLen: 11, - }, - { - Input: "あいうえおかきくけこさ", - Expected: "あいうえおかきくけこさ", - MaxLen: 12, - }, - } - for i, tc := range testCases { - testName := fmt.Sprintf("%d", i) - t.Run(testName, func(t *testing.T) { - out := truncateId(tc.Input, tc.MaxLen) - if out != tc.Expected { - t.Fatalf("Expected %q to be shortened to %d as %q (given: %q)", - tc.Input, tc.MaxLen, tc.Expected, out) - } - }) - } -} diff --git a/vendor/github.com/hashicorp/terraform/command/import.go b/vendor/github.com/hashicorp/terraform/command/import.go index f5891ed6..4067e4fc 100644 --- a/vendor/github.com/hashicorp/terraform/command/import.go +++ b/vendor/github.com/hashicorp/terraform/command/import.go @@ -12,6 +12,8 @@ import ( "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/tfdiags" @@ -35,6 +37,7 @@ func (c *ImportCommand) Run(args []string) int { args = c.Meta.process(args) cmdFlags := c.Meta.extendedFlagSet("import") + cmdFlags.BoolVar(&c.ignoreRemoteVersion, "ignore-remote-version", false, "continue even if remote and local Terraform versions are incompatible") cmdFlags.IntVar(&c.Meta.parallelism, "parallelism", DefaultParallelism, "parallelism") cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path") cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path") @@ -188,6 +191,7 @@ func (c *ImportCommand) Run(args []string) int { c.showDiagnostics(diags) return 1 } + opReq.Hooks = []terraform.Hook{c.uiHook()} { var moreDiags tfdiags.Diagnostics opReq.Variables, moreDiags = c.collectVariableValues() @@ -197,6 +201,15 @@ func (c *ImportCommand) Run(args []string) int { return 1 } } + opReq.View = views.NewOperation(arguments.ViewHuman, c.RunningInAutomation, c.View) + + // Check remote Terraform version is compatible + remoteVersionDiags := c.remoteBackendVersionCheck(b, opReq.Workspace) + diags = diags.Append(remoteVersionDiags) + c.showDiagnostics(diags) + if diags.HasErrors() { + return 1 + } // Get the context ctx, state, ctxDiags := local.Context(opReq) @@ -208,9 +221,9 @@ func (c *ImportCommand) Run(args []string) int { // Successfully creating the context can result in a lock, so ensure we release it defer func() { - err := opReq.StateLocker.Unlock(nil) - if err != nil { - c.Ui.Error(err.Error()) + diags := opReq.StateLocker.Unlock() + if diags.HasErrors() { + c.showDiagnostics(diags) } }() @@ -258,7 +271,7 @@ func (c *ImportCommand) Run(args []string) int { func (c *ImportCommand) Help() string { helpText := ` -Usage: terraform import [options] ADDR ID +Usage: terraform [global options] import [options] ADDR ID Import existing infrastructure into your Terraform state. @@ -287,10 +300,6 @@ Usage: terraform import [options] ADDR ID Options: - -backup=path Path to backup the existing state file before - modifying. Defaults to the "-state-out" path with - ".backup" extension. Set to "-" to disable backup. - -config=path Path to a directory of Terraform configuration files to use to configure the provider. Defaults to pwd. If no config files are present, they must be provided @@ -306,13 +315,6 @@ Options: -no-color If specified, output won't contain any color. - -state=PATH Path to the source state file. Defaults to the configured - backend, or "terraform.tfstate" - - -state-out=PATH Path to the destination state file to write to. If this - isn't specified, the source state file will be used. This - can be a new or existing path. - -var 'foo=bar' Set a variable in the Terraform configuration. This flag can be set multiple times. This is only useful with the "-config" flag. @@ -321,17 +323,23 @@ Options: a file. If "terraform.tfvars" or any ".auto.tfvars" files are present, they will be automatically loaded. + -ignore-remote-version Continue even if remote and local Terraform versions + are incompatible. This may result in an unusable + workspace, and should be used with extreme caution. + + -state, state-out, and -backup are legacy options supported for the local + backend only. For more information, see the local backend's documentation. ` return strings.TrimSpace(helpText) } func (c *ImportCommand) Synopsis() string { - return "Import existing infrastructure into Terraform" + return "Associate existing infrastructure with a Terraform resource" } const importCommandInvalidAddressReference = `For information on valid syntax, see: -https://www.terraform.io/docs/internals/resource-addressing.html` +https://www.terraform.io/docs/cli/state/resource-addressing.html` const importCommandMissingResourceFmt = `[reset][bold][red]Error:[reset][bold] resource address %q does not exist in the configuration.[reset] diff --git a/vendor/github.com/hashicorp/terraform/command/import_test.go b/vendor/github.com/hashicorp/terraform/command/import_test.go index cb36589b..e71e4c22 100644 --- a/vendor/github.com/hashicorp/terraform/command/import_test.go +++ b/vendor/github.com/hashicorp/terraform/command/import_test.go @@ -14,7 +14,6 @@ import ( "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/internal/copy" "github.com/hashicorp/terraform/providers" - "github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/tfdiags" ) @@ -25,15 +24,17 @@ func TestImport(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &ImportCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } p.ImportResourceStateFn = nil - p.ImportResourceStateResponse = providers.ImportResourceStateResponse{ + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ ImportedResources: []providers.ImportedResource{ { TypeName: "test_instance", @@ -43,11 +44,13 @@ func TestImport(t *testing.T) { }, }, } - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + }, }, }, }, @@ -76,15 +79,17 @@ func TestImport_providerConfig(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &ImportCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } p.ImportResourceStateFn = nil - p.ImportResourceStateResponse = providers.ImportResourceStateResponse{ + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ ImportedResources: []providers.ImportedResource{ { TypeName: "test_instance", @@ -94,38 +99,42 @@ func TestImport_providerConfig(t *testing.T) { }, }, } - p.GetSchemaReturn = &terraform.ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, - ResourceTypes: map[string]*configschema.Block{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + }, }, }, }, } configured := false - p.ConfigureFn = func(req providers.ConfigureRequest) providers.ConfigureResponse { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) providers.ConfigureProviderResponse { configured = true cfg := req.Config if !cfg.Type().HasAttribute("foo") { - return providers.ConfigureResponse{ + return providers.ConfigureProviderResponse{ Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("configuration has no foo argument")), } } if got, want := cfg.GetAttr("foo"), cty.StringVal("bar"); !want.RawEquals(got) { - return providers.ConfigureResponse{ + return providers.ConfigureProviderResponse{ Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("foo argument is %#v, but want %#v", got, want)), } } - return providers.ConfigureResponse{} + return providers.ConfigureProviderResponse{} } args := []string{ @@ -165,9 +174,11 @@ func TestImport_remoteState(t *testing.T) { // init our backend ui := cli.NewMockUi() + view, _ := testView(t) m := Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, ProviderSource: providerSource, } @@ -187,11 +198,12 @@ func TestImport_remoteState(t *testing.T) { Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } p.ImportResourceStateFn = nil - p.ImportResourceStateResponse = providers.ImportResourceStateResponse{ + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ ImportedResources: []providers.ImportedResource{ { TypeName: "test_instance", @@ -201,29 +213,33 @@ func TestImport_remoteState(t *testing.T) { }, }, } - p.GetSchemaReturn = &terraform.ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, - ResourceTypes: map[string]*configschema.Block{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + }, }, }, }, } configured := false - p.ConfigureFn = func(req providers.ConfigureRequest) providers.ConfigureResponse { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) providers.ConfigureProviderResponse { var diags tfdiags.Diagnostics configured = true if got, want := req.Config.GetAttr("foo"), cty.StringVal("bar"); !want.RawEquals(got) { diags = diags.Append(fmt.Errorf("wrong \"foo\" value %#v; want %#v", got, want)) } - return providers.ConfigureResponse{ + return providers.ConfigureProviderResponse{ Diagnostics: diags, } } @@ -271,9 +287,11 @@ func TestImport_initializationErrorShouldUnlock(t *testing.T) { // init our backend ui := cli.NewMockUi() + view, _ := testView(t) m := Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, ProviderSource: providerSource, } @@ -296,6 +314,7 @@ func TestImport_initializationErrorShouldUnlock(t *testing.T) { Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -330,15 +349,17 @@ func TestImport_providerConfigWithVar(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &ImportCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } p.ImportResourceStateFn = nil - p.ImportResourceStateResponse = providers.ImportResourceStateResponse{ + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ ImportedResources: []providers.ImportedResource{ { TypeName: "test_instance", @@ -348,29 +369,33 @@ func TestImport_providerConfigWithVar(t *testing.T) { }, }, } - p.GetSchemaReturn = &terraform.ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, - ResourceTypes: map[string]*configschema.Block{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + }, }, }, }, } configured := false - p.ConfigureFn = func(req providers.ConfigureRequest) providers.ConfigureResponse { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) providers.ConfigureProviderResponse { var diags tfdiags.Diagnostics configured = true if got, want := req.Config.GetAttr("foo"), cty.StringVal("bar"); !want.RawEquals(got) { diags = diags.Append(fmt.Errorf("wrong \"foo\" value %#v; want %#v", got, want)) } - return providers.ConfigureResponse{ + return providers.ConfigureProviderResponse{ Diagnostics: diags, } } @@ -404,15 +429,17 @@ func TestImport_providerConfigWithDataSource(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &ImportCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } p.ImportResourceStateFn = nil - p.ImportResourceStateResponse = providers.ImportResourceStateResponse{ + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ ImportedResources: []providers.ImportedResource{ { TypeName: "test_instance", @@ -422,23 +449,29 @@ func TestImport_providerConfigWithDataSource(t *testing.T) { }, }, } - p.GetSchemaReturn = &terraform.ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, - ResourceTypes: map[string]*configschema.Block{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + }, }, }, }, - DataSources: map[string]*configschema.Block{ + DataSources: map[string]providers.Schema{ "test_data": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -461,15 +494,17 @@ func TestImport_providerConfigWithVarDefault(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &ImportCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } p.ImportResourceStateFn = nil - p.ImportResourceStateResponse = providers.ImportResourceStateResponse{ + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ ImportedResources: []providers.ImportedResource{ { TypeName: "test_instance", @@ -479,29 +514,33 @@ func TestImport_providerConfigWithVarDefault(t *testing.T) { }, }, } - p.GetSchemaReturn = &terraform.ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, - ResourceTypes: map[string]*configschema.Block{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + }, }, }, }, } configured := false - p.ConfigureFn = func(req providers.ConfigureRequest) providers.ConfigureResponse { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) providers.ConfigureProviderResponse { var diags tfdiags.Diagnostics configured = true if got, want := req.Config.GetAttr("foo"), cty.StringVal("bar"); !want.RawEquals(got) { diags = diags.Append(fmt.Errorf("wrong \"foo\" value %#v; want %#v", got, want)) } - return providers.ConfigureResponse{ + return providers.ConfigureProviderResponse{ Diagnostics: diags, } } @@ -534,15 +573,17 @@ func TestImport_providerConfigWithVarFile(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &ImportCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } p.ImportResourceStateFn = nil - p.ImportResourceStateResponse = providers.ImportResourceStateResponse{ + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ ImportedResources: []providers.ImportedResource{ { TypeName: "test_instance", @@ -552,29 +593,33 @@ func TestImport_providerConfigWithVarFile(t *testing.T) { }, }, } - p.GetSchemaReturn = &terraform.ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, - ResourceTypes: map[string]*configschema.Block{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + }, }, }, }, } configured := false - p.ConfigureFn = func(req providers.ConfigureRequest) providers.ConfigureResponse { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) providers.ConfigureProviderResponse { var diags tfdiags.Diagnostics configured = true if got, want := req.Config.GetAttr("foo"), cty.StringVal("bar"); !want.RawEquals(got) { diags = diags.Append(fmt.Errorf("wrong \"foo\" value %#v; want %#v", got, want)) } - return providers.ConfigureResponse{ + return providers.ConfigureProviderResponse{ Diagnostics: diags, } } @@ -608,15 +653,17 @@ func TestImport_allowMissingResourceConfig(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &ImportCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } p.ImportResourceStateFn = nil - p.ImportResourceStateResponse = providers.ImportResourceStateResponse{ + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ ImportedResources: []providers.ImportedResource{ { TypeName: "test_instance", @@ -626,11 +673,13 @@ func TestImport_allowMissingResourceConfig(t *testing.T) { }, }, } - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + }, }, }, }, @@ -661,10 +710,12 @@ func TestImport_emptyConfig(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &ImportCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -691,10 +742,12 @@ func TestImport_missingResourceConfig(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &ImportCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -721,10 +774,12 @@ func TestImport_missingModuleConfig(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &ImportCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -753,11 +808,13 @@ func TestImportModuleVarFile(t *testing.T) { statePath := testTempFile(t) p := testProvider() - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -770,9 +827,11 @@ func TestImportModuleVarFile(t *testing.T) { // init to install the module ui := new(cli.MockUi) + view, _ := testView(t) m := Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, ProviderSource: providerSource, } @@ -789,6 +848,7 @@ func TestImportModuleVarFile(t *testing.T) { Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } args := []string{ @@ -823,11 +883,13 @@ func TestImportModuleInputVariableEvaluation(t *testing.T) { statePath := testTempFile(t) p := testProvider() - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -840,9 +902,11 @@ func TestImportModuleInputVariableEvaluation(t *testing.T) { // init to install the module ui := new(cli.MockUi) + view, _ := testView(t) m := Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, ProviderSource: providerSource, } @@ -859,6 +923,7 @@ func TestImportModuleInputVariableEvaluation(t *testing.T) { Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } args := []string{ @@ -879,10 +944,12 @@ func TestImport_dataResource(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &ImportCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -909,10 +976,12 @@ func TestImport_invalidResourceAddr(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &ImportCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -939,10 +1008,12 @@ func TestImport_targetIsModule(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &ImportCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -967,15 +1038,3 @@ test_instance.foo: ID = yay provider = provider["registry.terraform.io/hashicorp/test"] ` - -const testImportCustomProviderStr = ` -test_instance.foo: - ID = yay - provider = provider["registry.terraform.io/hashicorp/test"].alias -` - -const testImportProviderMismatchStr = ` -test_instance.foo: - ID = yay - provider = provider["registry.terraform.io/hashicorp/test-beta"] -` diff --git a/vendor/github.com/hashicorp/terraform/command/init.go b/vendor/github.com/hashicorp/terraform/command/init.go index 6cd9a27b..f9b39fbd 100644 --- a/vendor/github.com/hashicorp/terraform/command/init.go +++ b/vendor/github.com/hashicorp/terraform/command/init.go @@ -4,8 +4,6 @@ import ( "context" "fmt" "log" - "os" - "path/filepath" "strings" "github.com/hashicorp/hcl/v2" @@ -31,16 +29,12 @@ import ( // module and clones it to the working directory. type InitCommand struct { Meta - - // getPlugins is for the -get-plugins flag - getPlugins bool } func (c *InitCommand) Run(args []string) int { - var flagFromModule string + var flagFromModule, flagLockfile string var flagBackend, flagGet, flagUpgrade bool var flagPluginPath FlagStringSlice - var flagVerifyPlugins bool flagConfigExtra := newRawFlags("-backend-config") args = c.Meta.process(args) @@ -49,14 +43,11 @@ func (c *InitCommand) Run(args []string) int { cmdFlags.Var(flagConfigExtra, "backend-config", "") cmdFlags.StringVar(&flagFromModule, "from-module", "", "copy the source of the given module into the directory before init") cmdFlags.BoolVar(&flagGet, "get", true, "") - cmdFlags.BoolVar(&c.getPlugins, "get-plugins", true, "") cmdFlags.BoolVar(&c.forceInitCopy, "force-copy", false, "suppress prompts about copying state data") - cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state") - cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout") cmdFlags.BoolVar(&c.reconfigure, "reconfigure", false, "reconfigure") cmdFlags.BoolVar(&flagUpgrade, "upgrade", false, "") cmdFlags.Var(&flagPluginPath, "plugin-dir", "plugin directory") - cmdFlags.BoolVar(&flagVerifyPlugins, "verify-plugins", true, "verify plugins") + cmdFlags.StringVar(&flagLockfile, "lockfile", "", "Set a dependency lockfile mode") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 @@ -66,14 +57,13 @@ func (c *InitCommand) Run(args []string) int { if len(flagPluginPath) > 0 { c.pluginPath = flagPluginPath - c.getPlugins = false } - // Validate the arg count + // Validate the arg count and get the working directory args = cmdFlags.Args() - if len(args) > 1 { - c.Ui.Error("The init command expects at most one argument.\n") - cmdFlags.Usage() + path, err := ModulePath(args) + if err != nil { + c.Ui.Error(err.Error()) return 1 } @@ -82,20 +72,6 @@ func (c *InitCommand) Run(args []string) int { return 1 } - // Get our pwd. We don't always need it but always getting it is easier - // than the logic to determine if it is or isn't needed. - pwd, err := os.Getwd() - if err != nil { - c.Ui.Error(fmt.Sprintf("Error getting pwd: %s", err)) - return 1 - } - - // If an argument is provided then it overrides our working directory. - path := pwd - if len(args) == 1 { - path = args[0] - } - // This will track whether we outputted anything so that we know whether // to output a newline before the success message var header bool @@ -133,7 +109,7 @@ func (c *InitCommand) Run(args []string) int { c.Ui.Output("") } - // If our directory is empty, then we're done. We can't get or setup + // If our directory is empty, then we're done. We can't get or set up // the backend with an empty directory. empty, err := configs.IsEmptyDir(path) if err != nil { @@ -264,6 +240,7 @@ func (c *InitCommand) Run(args []string) int { // on a previous run) we'll use the current state as a potential source // of provider dependencies. if back != nil { + c.ignoreRemoteBackendVersionConflict(back) workspace, err := c.Workspace() if err != nil { c.Ui.Error(fmt.Sprintf("Error selecting workspace: %s", err)) @@ -284,7 +261,7 @@ func (c *InitCommand) Run(args []string) int { } // Now that we have loaded all modules, check the module tree for missing providers. - providersOutput, providersAbort, providerDiags := c.getProviders(config, state, flagUpgrade, flagPluginPath) + providersOutput, providersAbort, providerDiags := c.getProviders(config, state, flagUpgrade, flagPluginPath, flagLockfile) diags = diags.Append(providerDiags) if providersAbort || providerDiags.HasErrors() { c.showDiagnostics(diags) @@ -321,9 +298,9 @@ func (c *InitCommand) getModules(path string, earlyRoot *tfconfig.Module, upgrad } if upgrade { - c.Ui.Output(c.Colorize().Color(fmt.Sprintf("[reset][bold]Upgrading modules..."))) + c.Ui.Output(c.Colorize().Color("[reset][bold]Upgrading modules...")) } else { - c.Ui.Output(c.Colorize().Color(fmt.Sprintf("[reset][bold]Initializing modules..."))) + c.Ui.Output(c.Colorize().Color("[reset][bold]Initializing modules...")) } hooks := uiModuleInstallHooks{ @@ -350,7 +327,7 @@ func (c *InitCommand) getModules(path string, earlyRoot *tfconfig.Module, upgrad } func (c *InitCommand) initBackend(root *configs.Module, extraConfig rawFlags) (be backend.Backend, output bool, diags tfdiags.Diagnostics) { - c.Ui.Output(c.Colorize().Color(fmt.Sprintf("\n[reset][bold]Initializing the backend..."))) + c.Ui.Output(c.Colorize().Color("\n[reset][bold]Initializing the backend...")) var backendConfig *configs.Backend var backendConfigOverride hcl.Body @@ -415,7 +392,13 @@ the backend configuration is present and valid. // Load the complete module tree, and fetch any missing providers. // This method outputs its own Ui. -func (c *InitCommand) getProviders(config *configs.Config, state *states.State, upgrade bool, pluginDirs []string) (output, abort bool, diags tfdiags.Diagnostics) { +func (c *InitCommand) getProviders(config *configs.Config, state *states.State, upgrade bool, pluginDirs []string, flagLockfile string) (output, abort bool, diags tfdiags.Diagnostics) { + // Dev overrides cause the result of "terraform init" to be irrelevant for + // any overridden providers, so we'll warn about it to avoid later + // confusion when Terraform ends up using a different provider than the + // lock file called for. + diags = diags.Append(c.providerDevOverrideInitWarnings()) + // First we'll collect all the provider dependencies we can see in the // configuration and the state. reqs, hclDiags := config.ProviderRequirements() @@ -467,12 +450,15 @@ func (c *InitCommand) getProviders(config *configs.Config, state *states.State, log.Printf("[DEBUG] will search for provider plugins in %s", pluginDirs) } + // Installation can be aborted by interruption signals + ctx, done := c.InterruptibleContext() + defer done() + // Because we're currently just streaming a series of events sequentially // into the terminal, we're showing only a subset of the events to keep // things relatively concise. Later it'd be nice to have a progress UI // where statuses update in-place, but we can't do that as long as we // are shimming our vt100 output to the legacy console API on Windows. - missingProviders := make(map[addrs.Provider]struct{}) evts := &providercache.InstallerEvents{ PendingProviders: func(reqs map[addrs.Provider]getproviders.VersionConstraints) { c.Ui.Output(c.Colorize().Color( @@ -510,10 +496,6 @@ func (c *InitCommand) getProviders(config *configs.Config, state *states.State, c.Ui.Info(fmt.Sprintf("- Installing %s v%s...", provider.ForDisplay(), version)) }, QueryPackagesFailure: func(provider addrs.Provider, err error) { - // We track providers that had missing metadata because we might - // generate additional hints for some of them at the end. - missingProviders[provider] = struct{}{} - switch errorTy := err.(type) { case getproviders.ErrProviderNotFound: sources := errorTy.Sources @@ -529,11 +511,22 @@ func (c *InitCommand) getProviders(config *configs.Config, state *states.State, ), )) case getproviders.ErrRegistryProviderNotKnown: + // We might be able to suggest an alternative provider to use + // instead of this one. + suggestion := fmt.Sprintf("\n\nAll modules should specify their required_providers so that external consumers will get the correct providers when using a module. To see which modules are currently depending on %s, run the following command:\n terraform providers", provider.ForDisplay()) + alternative := getproviders.MissingProviderSuggestion(ctx, provider, inst.ProviderSource(), reqs) + if alternative != provider { + suggestion = fmt.Sprintf( + "\n\nDid you intend to use %s? If so, you must specify that source address in each module which requires that provider. To see which modules are currently depending on %s, run the following command:\n terraform providers", + alternative.ForDisplay(), provider.ForDisplay(), + ) + } + diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, "Failed to query available provider packages", - fmt.Sprintf("Could not retrieve the list of available versions for provider %s: %s", - provider.ForDisplay(), err, + fmt.Sprintf("Could not retrieve the list of available versions for provider %s: %s%s", + provider.ForDisplay(), err, suggestion, ), )) case getproviders.ErrHostNoProviders: @@ -713,7 +706,7 @@ func (c *InitCommand) getProviders(config *configs.Config, state *states.State, if thirdPartySigned { c.Ui.Info(fmt.Sprintf("\nPartner and community providers are signed by their developers.\n" + "If you'd like to know more about provider signing, you can read about it here:\n" + - "https://www.terraform.io/docs/plugins/signing.html")) + "https://www.terraform.io/docs/cli/plugins/signing.html")) } }, HashPackageFailure: func(provider addrs.Provider, version getproviders.Version, err error) { @@ -729,81 +722,23 @@ func (c *InitCommand) getProviders(config *configs.Config, state *states.State, )) }, } - - // Dev overrides cause the result of "terraform init" to be irrelevant for - // any overridden providers, so we'll warn about it to avoid later - // confusion when Terraform ends up using a different provider than the - // lock file called for. - diags = diags.Append(c.providerDevOverrideWarnings()) + ctx = evts.OnContext(ctx) mode := providercache.InstallNewProvidersOnly if upgrade { + if flagLockfile == "readonly" { + c.Ui.Error("The -upgrade flag conflicts with -lockfile=readonly.") + return true, true, diags + } + mode = providercache.InstallUpgrades } - // Installation can be aborted by interruption signals - ctx, done := c.InterruptibleContext() - defer done() - ctx = evts.OnContext(ctx) newLocks, err := inst.EnsureProviderVersions(ctx, previousLocks, reqs, mode) if ctx.Err() == context.Canceled { c.showDiagnostics(diags) c.Ui.Error("Provider installation was canceled by an interrupt signal.") return true, true, diags } - if len(missingProviders) > 0 { - // If we encountered requirements for one or more providers where we - // weren't able to find any metadata, that _might_ be because a - // user had previously (before 0.14) been incorrectly using the - // .terraform/plugins directory as if it were a local filesystem - // mirror, rather than as the main cache directory. - // - // We no longer allow that because it'd be ambiguous whether plugins in - // there are explictly intended to be a local mirror or if they are - // just leftover cache entries from provider installation in - // Terraform 0.13. - // - // To help those users migrate we have a specialized warning message - // for it, which we'll produce only if one of the missing providers can - // be seen in the "legacy" cache directory, which is what we're now - // considering .terraform/plugins to be. (The _current_ cache directory - // is .terraform/providers.) - // - // This is only a heuristic, so it might potentially produce false - // positives if a user happens to encounter another sort of error - // while they are upgrading from Terraform 0.13 to 0.14. Aside from - // upgrading users should not end up in here because they won't - // have a legacy cache directory at all. - legacyDir := c.providerLegacyCacheDir() - if legacyDir != nil { // if the legacy directory is present at all - for missingProvider := range missingProviders { - if missingProvider.IsDefault() { - // If we get here for a default provider then it's more - // likely that something _else_ went wrong, like a network - // problem, so we'll skip the warning in this case to - // avoid potentially misleading the user into creating an - // unnecessary local mirror for an official provider. - continue - } - entry := legacyDir.ProviderLatestVersion(missingProvider) - if entry == nil { - continue - } - // If we get here then the missing provider was cached, which - // implies that it might be an in-house provider the user - // placed manually to try to make Terraform use it as if it - // were a local mirror directory. - wantDir := filepath.FromSlash(fmt.Sprintf("terraform.d/plugins/%s/%s/%s", missingProvider, entry.Version, getproviders.CurrentPlatform)) - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Warning, - "Missing provider is in legacy cache directory", - fmt.Sprintf( - "Terraform supports a number of local directories that can serve as automatic local filesystem mirrors, but .terraform/plugins is not one of them because Terraform v0.13 and earlier used this directory to cache copies of provider plugins retrieved from elsewhere.\n\nIf you intended to use this directory as a filesystem mirror for %s, place it instead in the following directory:\n %s", - missingProvider, wantDir, - ), - )) - } - } - } if err != nil { // The errors captured in "err" should be redundant with what we // received via the InstallerEvents callbacks above, so we'll @@ -823,6 +758,28 @@ func (c *InitCommand) getProviders(config *configs.Config, state *states.State, // it's the smallest change relative to what came before it, which was // a hidden JSON file specifically for tracking providers.) if !newLocks.Equal(previousLocks) { + // if readonly mode + if flagLockfile == "readonly" { + // check if required provider dependences change + if !newLocks.EqualProviderAddress(previousLocks) { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + `Provider dependency changes detected`, + `Changes to the required provider dependencies were detected, but the lock file is read-only. To use and record these requirements, run "terraform init" without the "-lockfile=readonly" flag.`, + )) + return true, true, diags + } + + // suppress updating the file to record any new information it learned, + // such as a hash using a new scheme. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Warning, + `Provider lock file not updated`, + `Changes to the provider selections were detected, but not saved in the .terraform.lock.hcl file. To record these selections, run "terraform init" without the "-lockfile=readonly" flag.`, + )) + return true, false, diags + } + if previousLocks.Empty() { // A change from empty to non-empty is special because it suggests // we're running "terraform init" for the first time against a @@ -841,15 +798,10 @@ Terraform has made some changes to the provider dependency selections recorded in the .terraform.lock.hcl file. Review those changes and commit them to your version control system if they represent changes you intended to make.`)) } - } - - // TODO: Check whether newLocks is different from previousLocks and mention - // in the UI if so. We should emit a different message if previousLocks was - // empty, because that indicates we were creating a lock file for the first - // time and so we need to introduce the user to the idea of it. - moreDiags = c.replaceLockedDependencies(newLocks) - diags = diags.Append(moreDiags) + moreDiags = c.replaceLockedDependencies(newLocks) + diags = diags.Append(moreDiags) + } return true, false, diags } @@ -970,21 +922,17 @@ func (c *InitCommand) AutocompleteFlags() complete.Flags { "-force-copy": complete.PredictNothing, "-from-module": completePredictModuleSource, "-get": completePredictBoolean, - "-get-plugins": completePredictBoolean, "-input": completePredictBoolean, - "-lock": completePredictBoolean, - "-lock-timeout": complete.PredictAnything, "-no-color": complete.PredictNothing, "-plugin-dir": complete.PredictDirs(""), "-reconfigure": complete.PredictNothing, "-upgrade": completePredictBoolean, - "-verify-plugins": completePredictBoolean, } } func (c *InitCommand) Help() string { helpText := ` -Usage: terraform init [options] [DIR] +Usage: terraform [global options] init [options] Initialize a new or existing Terraform working directory by creating initial files, loading any remote state, downloading modules, etc. @@ -999,9 +947,6 @@ Usage: terraform init [options] [DIR] state. Even so, if you have important information, please back it up prior to running this command, just in case. - If no arguments are given, the configuration in this working directory - is initialized. - Options: -backend=true Configure the backend for this configuration. @@ -1022,37 +967,32 @@ Options: -get=true Download any modules for this configuration. - -get-plugins=true Download any missing plugins for this configuration. - -input=true Ask for input if necessary. If false, will error if input was required. - -lock=true Lock the state file when locking is supported. - - -lock-timeout=0s Duration to retry a state lock. - -no-color If specified, output won't contain any color. -plugin-dir Directory containing plugin binaries. This overrides all - default search paths for plugins, and prevents the + default search paths for plugins, and prevents the automatic installation of plugins. This flag can be used multiple times. -reconfigure Reconfigure the backend, ignoring any saved configuration. - -upgrade=false If installing modules (-get) or plugins (-get-plugins), - ignore previously-downloaded objects and install the + -upgrade=false If installing modules (-get) or plugins, ignore + previously-downloaded objects and install the latest version allowed within configured constraints. - -verify-plugins=true Verify the authenticity and integrity of automatically - downloaded plugins. + -lockfile=MODE Set a dependency lockfile mode. + Currently only "readonly" is valid. + ` return strings.TrimSpace(helpText) } func (c *InitCommand) Synopsis() string { - return "Initialize a Terraform working directory" + return "Prepare your working directory for other commands" } const errInitConfigError = ` @@ -1091,15 +1031,6 @@ rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary. ` -const outputInitProvidersUnconstrained = ` -The following providers do not have any version constraints in configuration, -so the latest version was installed. - -To prevent automatic upgrades to new major versions that may contain breaking -changes, we recommend adding version constraints in a required_providers block -in your configuration, with the constraint strings suggested below. -` - // providerProtocolTooOld is a message sent to the CLI UI if the provider's // supported protocol versions are too old for the user's version of terraform, // but a newer version of the provider is compatible. diff --git a/vendor/github.com/hashicorp/terraform/command/init_test.go b/vendor/github.com/hashicorp/terraform/command/init_test.go index 96d853d3..99fdbe96 100644 --- a/vendor/github.com/hashicorp/terraform/command/init_test.go +++ b/vendor/github.com/hashicorp/terraform/command/init_test.go @@ -17,7 +17,6 @@ import ( "github.com/mitchellh/cli" "github.com/zclconf/go-cty/cty" - version "github.com/hashicorp/go-version" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs/configschema" @@ -25,7 +24,6 @@ import ( "github.com/hashicorp/terraform/internal/getproviders" "github.com/hashicorp/terraform/internal/providercache" "github.com/hashicorp/terraform/states" - "github.com/hashicorp/terraform/states/statefile" "github.com/hashicorp/terraform/states/statemgr" ) @@ -37,10 +35,12 @@ func TestInit_empty(t *testing.T) { defer testChdir(t, td)() ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -58,10 +58,12 @@ func TestInit_multipleArgs(t *testing.T) { defer testChdir(t, td)() ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -74,42 +76,6 @@ func TestInit_multipleArgs(t *testing.T) { } } -func TestInit_fromModule_explicitDest(t *testing.T) { - td := tempDir(t) - os.MkdirAll(td, 0755) - defer os.RemoveAll(td) - defer testChdir(t, td)() - - ui := new(cli.MockUi) - c := &InitCommand{ - Meta: Meta{ - testingOverrides: metaOverridesForProvider(testProvider()), - Ui: ui, - }, - } - - if _, err := os.Stat(DefaultStateFilename); err == nil { - // This should never happen; it indicates a bug in another test - // is causing a terraform.tfstate to get left behind in our directory - // here, which can interfere with our init process in a way that - // isn't relevant to this test. - fullPath, _ := filepath.Abs(DefaultStateFilename) - t.Fatalf("some other test has left terraform.tfstate behind:\n%s", fullPath) - } - - args := []string{ - "-from-module=" + testFixturePath("init"), - td, - } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) - } - - if _, err := os.Stat(filepath.Join(td, "hello.tf")); err != nil { - t.Fatalf("err: %s", err) - } -} - func TestInit_fromModule_cwdDest(t *testing.T) { // Create a temporary working directory that is empty td := tempDir(t) @@ -118,10 +84,12 @@ func TestInit_fromModule_cwdDest(t *testing.T) { defer testChdir(t, td)() ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -163,17 +131,22 @@ func TestInit_fromModule_dstInSrc(t *testing.T) { t.Fatalf("err: %s", err) } + if err := os.Chdir("foo"); err != nil { + t.Fatalf("err: %s", err) + } + ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } args := []string{ - "-from-module=.", - "foo", + "-from-module=./..", } if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) @@ -192,10 +165,12 @@ func TestInit_get(t *testing.T) { defer testChdir(t, td)() ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -214,23 +189,23 @@ func TestInit_get(t *testing.T) { func TestInit_getUpgradeModules(t *testing.T) { // Create a temporary working directory that is empty td := tempDir(t) - os.MkdirAll(td, 0755) + testCopyDir(t, testFixturePath("init-get"), td) defer os.RemoveAll(td) defer testChdir(t, td)() ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } args := []string{ "-get=true", - "-get-plugins=false", "-upgrade", - testFixturePath("init-get"), } if code := c.Run(args); code != 0 { t.Fatalf("command did not complete successfully:\n%s", ui.ErrorWriter.String()) @@ -251,10 +226,12 @@ func TestInit_backend(t *testing.T) { defer testChdir(t, td)() ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -279,10 +256,12 @@ func TestInit_backendUnset(t *testing.T) { log.Printf("[TRACE] TestInit_backendUnset: beginning first init") ui := cli.NewMockUi() + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -309,10 +288,12 @@ func TestInit_backendUnset(t *testing.T) { } ui := cli.NewMockUi() + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -340,10 +321,12 @@ func TestInit_backendConfigFile(t *testing.T) { t.Run("good-config-file", func(t *testing.T) { ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } args := []string{"-backend-config", "input.config"} @@ -361,10 +344,12 @@ func TestInit_backendConfigFile(t *testing.T) { // the backend config file must not be a full terraform block t.Run("full-backend-config-file", func(t *testing.T) { ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } args := []string{"-backend-config", "backend.config"} @@ -379,10 +364,12 @@ func TestInit_backendConfigFile(t *testing.T) { // the backend config file must match the schema for the backend t.Run("invalid-config-file", func(t *testing.T) { ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } args := []string{"-backend-config", "invalid.config"} @@ -397,10 +384,12 @@ func TestInit_backendConfigFile(t *testing.T) { // missing file is an error t.Run("missing-config-file", func(t *testing.T) { ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } args := []string{"-backend-config", "missing.config"} @@ -415,10 +404,12 @@ func TestInit_backendConfigFile(t *testing.T) { // blank filename clears the backend config t.Run("blank-config-file", func(t *testing.T) { ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } args := []string{"-backend-config="} @@ -466,10 +457,12 @@ func TestInit_backendConfigFilePowershellConfusion(t *testing.T) { defer testChdir(t, td)() ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -487,11 +480,59 @@ func TestInit_backendConfigFilePowershellConfusion(t *testing.T) { } output := ui.ErrorWriter.String() - if got, want := output, `Module directory ./input.config does not exist`; !strings.Contains(got, want) { + if got, want := output, `Too many command line arguments`; !strings.Contains(got, want) { t.Fatalf("wrong output\ngot:\n%s\n\nwant: message containing %q", got, want) } } +func TestInit_backendReconfigure(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("init-backend"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + providerSource, close := newMockProviderSource(t, map[string][]string{ + "hashicorp/test": {"1.2.3"}, + }) + defer close() + + ui := new(cli.MockUi) + view, _ := testView(t) + c := &InitCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(testProvider()), + ProviderSource: providerSource, + Ui: ui, + View: view, + }, + } + + // create some state, so the backend has something to migrate. + f, err := os.Create("foo") // this is the path" in the backend config + if err != nil { + t.Fatalf("err: %s", err) + } + err = writeStateForTesting(testState(), f) + f.Close() + if err != nil { + t.Fatalf("err: %s", err) + } + + args := []string{} + if code := c.Run(args); code != 0 { + t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + } + + // now run init again, changing the path. + // The -reconfigure flag prevents init from migrating + // Without -reconfigure, the test fails since the backend asks for input on migrating state + args = []string{"-reconfigure", "-backend-config", "path=changed"} + if code := c.Run(args); code != 0 { + t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + } +} + func TestInit_backendConfigFileChange(t *testing.T) { // Create a temporary working directory that is empty td := tempDir(t) @@ -505,10 +546,12 @@ func TestInit_backendConfigFileChange(t *testing.T) { })() ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -532,10 +575,12 @@ func TestInit_backendConfigKV(t *testing.T) { defer testChdir(t, td)() ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -559,10 +604,12 @@ func TestInit_backendConfigKVReInit(t *testing.T) { defer testChdir(t, td)() ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -576,6 +623,7 @@ func TestInit_backendConfigKVReInit(t *testing.T) { Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -620,10 +668,12 @@ func TestInit_backendConfigKVReInitWithConfigDiff(t *testing.T) { defer testChdir(t, td)() ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -637,6 +687,7 @@ func TestInit_backendConfigKVReInitWithConfigDiff(t *testing.T) { Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -666,10 +717,12 @@ func TestInit_backendCli_no_config_block(t *testing.T) { defer testChdir(t, td)() ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -684,41 +737,6 @@ func TestInit_backendCli_no_config_block(t *testing.T) { } } -func TestInit_targetSubdir(t *testing.T) { - // Create a temporary working directory that is empty - td := tempDir(t) - os.MkdirAll(td, 0755) - defer os.RemoveAll(td) - defer testChdir(t, td)() - - // copy the source into a subdir - testCopyDir(t, testFixturePath("init-backend"), filepath.Join(td, "source")) - - ui := new(cli.MockUi) - c := &InitCommand{ - Meta: Meta{ - testingOverrides: metaOverridesForProvider(testProvider()), - Ui: ui, - }, - } - - args := []string{ - "source", - } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) - } - - if _, err := os.Stat(filepath.Join(td, DefaultDataDir, DefaultStateFilename)); err != nil { - t.Fatalf("err: %s", err) - } - - // a data directory should not have been added to out working dir - if _, err := os.Stat(filepath.Join(td, "source", DefaultDataDir)); !os.IsNotExist(err) { - t.Fatalf("err: %s", err) - } -} - func TestInit_backendReinitWithExtra(t *testing.T) { td := tempDir(t) testCopyDir(t, testFixturePath("init-backend-empty"), td) @@ -739,10 +757,12 @@ func TestInit_backendReinitWithExtra(t *testing.T) { } ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -782,10 +802,12 @@ func TestInit_backendReinitConfigToExtra(t *testing.T) { defer testChdir(t, td)() ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -813,6 +835,7 @@ func TestInit_backendReinitConfigToExtra(t *testing.T) { Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -838,15 +861,17 @@ func TestInit_inputFalse(t *testing.T) { defer testChdir(t, td)() ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } args := []string{"-input=false", "-backend-config=path=foo"} - if code := c.Run([]string{"-input=false"}); code != 0 { + if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter) } @@ -877,6 +902,7 @@ func TestInit_inputFalse(t *testing.T) { Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -895,6 +921,7 @@ func TestInit_inputFalse(t *testing.T) { Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -914,18 +941,20 @@ func TestInit_getProvider(t *testing.T) { overrides := metaOverridesForProvider(testProvider()) ui := new(cli.MockUi) + view, _ := testView(t) providerSource, close := newMockProviderSource(t, map[string][]string{ // looking for an exact version - "exact": []string{"1.2.3"}, + "exact": {"1.2.3"}, // config requires >= 2.3.3 - "greater-than": []string{"2.3.4", "2.3.3", "2.3.0"}, + "greater-than": {"2.3.4", "2.3.3", "2.3.0"}, // config specifies - "between": []string{"3.4.5", "2.3.4", "1.2.3"}, + "between": {"3.4.5", "2.3.4", "1.2.3"}, }) defer close() m := Meta{ testingOverrides: overrides, Ui: ui, + View: view, ProviderSource: providerSource, } @@ -965,15 +994,35 @@ func TestInit_getProvider(t *testing.T) { } defer f.Close() - s := &statefile.File{ - Lineage: "", - State: states.NewState(), - TerraformVersion: version.Must(version.NewVersion("100.1.0")), + // Construct a mock state file from the far future + type FutureState struct { + Version uint `json:"version"` + Lineage string `json:"lineage"` + TerraformVersion string `json:"terraform_version"` + Outputs map[string]interface{} `json:"outputs"` + Resources []map[string]interface{} `json:"resources"` + } + fs := &FutureState{ + Version: 999, + Lineage: "123-456-789", + TerraformVersion: "999.0.0", + Outputs: make(map[string]interface{}), + Resources: make([]map[string]interface{}, 0), + } + src, err := json.MarshalIndent(fs, "", " ") + if err != nil { + t.Fatalf("failed to marshal future state: %s", err) + } + src = append(src, '\n') + _, err = f.Write(src) + if err != nil { + t.Fatal(err) } - statefile.WriteForTest(s, f) ui := new(cli.MockUi) + view, _ := testView(t) m.Ui = ui + m.View = view c := &InitCommand{ Meta: m, } @@ -983,7 +1032,7 @@ func TestInit_getProvider(t *testing.T) { } errMsg := ui.ErrorWriter.String() - if !strings.Contains(errMsg, "which is newer than current") { + if !strings.Contains(errMsg, "Unsupported state file format") { t.Fatal("unexpected error:", errMsg) } }) @@ -998,17 +1047,19 @@ func TestInit_getProviderSource(t *testing.T) { overrides := metaOverridesForProvider(testProvider()) ui := new(cli.MockUi) + view, _ := testView(t) providerSource, close := newMockProviderSource(t, map[string][]string{ // looking for an exact version - "acme/alpha": []string{"1.2.3"}, + "acme/alpha": {"1.2.3"}, // config doesn't specify versions for other providers - "registry.example.com/acme/beta": []string{"1.0.0"}, - "gamma": []string{"2.0.0"}, + "registry.example.com/acme/beta": {"1.0.0"}, + "gamma": {"2.0.0"}, }) defer close() m := Meta{ testingOverrides: overrides, Ui: ui, + View: view, ProviderSource: providerSource, } @@ -1038,85 +1089,6 @@ func TestInit_getProviderSource(t *testing.T) { } } -func TestInit_getProviderInLegacyPluginCacheDir(t *testing.T) { - // Create a temporary working directory that is empty - td := tempDir(t) - testCopyDir(t, testFixturePath("init-legacy-provider-cache"), td) - defer os.RemoveAll(td) - defer testChdir(t, td)() - - // The test fixture has placeholder os_arch directories which we must - // now rename to match the current platform, or else the entries inside - // will be ignored. - platformStr := getproviders.CurrentPlatform.String() - if err := os.Rename( - ".terraform/plugins/example.com/test/b/1.1.0/os_arch", - ".terraform/plugins/example.com/test/b/1.1.0/"+platformStr, - ); err != nil { - t.Fatal(err) - } - if err := os.Rename( - ".terraform/plugins/registry.terraform.io/hashicorp/c/2.0.0/os_arch", - ".terraform/plugins/registry.terraform.io/hashicorp/c/2.0.0/"+platformStr, - ); err != nil { - t.Fatal(err) - } - - // An empty MultiSource serves as a way to make sure no providers are - // actually available for installation, which suits us here because - // we're testing an error case. - providerSource := getproviders.MultiSource{} - - ui := cli.NewMockUi() - m := Meta{ - Ui: ui, - ProviderSource: providerSource, - } - - c := &InitCommand{ - Meta: m, - } - - args := []string{ - "-backend=false", - } - if code := c.Run(args); code == 0 { - t.Fatalf("succeeded; want error\n%s", ui.OutputWriter.String()) - } - - // We remove all of the newlines so that we don't need to contend with - // the automatic word wrapping that our diagnostic printer does. - stderr := strings.Replace(ui.ErrorWriter.String(), "\n", " ", -1) - - if got, want := stderr, `example.com/test/a: no available releases match the given constraints`; !strings.Contains(got, want) { - t.Errorf("missing error about example.com/test/a\nwant substring: %s\n%s", want, got) - } - if got, want := stderr, `example.com/test/b: no available releases match the given constraints`; !strings.Contains(got, want) { - t.Errorf("missing error about example.com/test/b\nwant substring: %s\n%s", want, got) - } - if got, want := stderr, `hashicorp/c: no available releases match the given constraints`; !strings.Contains(got, want) { - t.Errorf("missing error about registry.terraform.io/hashicorp/c\nwant substring: %s\n%s", want, got) - } - - if got, want := stderr, `terraform.d/plugins/example.com/test/a`; strings.Contains(got, want) { - // We _don't_ expect to see a warning about the "a" provider, because - // there's no copy of that in the legacy plugin cache dir. - t.Errorf("unexpected suggested path for local example.com/test/a\ndon't want substring: %s\n%s", want, got) - } - if got, want := stderr, `terraform.d/plugins/example.com/test/b/1.1.0/`+platformStr; !strings.Contains(got, want) { - // ...but we should see a warning about the "b" provider, because - // there's an entry for that in the legacy cache dir. - t.Errorf("missing suggested path for local example.com/test/b 1.0.0 on %s\nwant substring: %s\n%s", platformStr, want, got) - } - if got, want := stderr, `terraform.d/plugins/registry.terraform.io/hashicorp/c`; strings.Contains(got, want) { - // We _don't_ expect to see a warning about the "a" provider, even - // though it's in the cache dir, because it's an official provider - // and so we assume it ended up there as a result of normal provider - // installation in Terraform 0.13. - t.Errorf("unexpected suggested path for local hashicorp/c\ndon't want substring: %s\n%s", want, got) - } -} - func TestInit_getProviderLegacyFromState(t *testing.T) { // Create a temporary working directory that is empty td := tempDir(t) @@ -1126,6 +1098,7 @@ func TestInit_getProviderLegacyFromState(t *testing.T) { overrides := metaOverridesForProvider(testProvider()) ui := new(cli.MockUi) + view, _ := testView(t) providerSource, close := newMockProviderSource(t, map[string][]string{ "acme/alpha": {"1.2.3"}, }) @@ -1133,6 +1106,7 @@ func TestInit_getProviderLegacyFromState(t *testing.T) { m := Meta{ testingOverrides: overrides, Ui: ui, + View: view, ProviderSource: providerSource, } @@ -1166,6 +1140,7 @@ func TestInit_getProviderInvalidPackage(t *testing.T) { overrides := metaOverridesForProvider(testProvider()) ui := new(cli.MockUi) + view, _ := testView(t) // create a provider source which allows installing an invalid package addr := addrs.MustParseProviderSourceString("invalid/package") @@ -1186,6 +1161,7 @@ func TestInit_getProviderInvalidPackage(t *testing.T) { m := Meta{ testingOverrides: overrides, Ui: ui, + View: view, ProviderSource: providerSource, } @@ -1230,8 +1206,8 @@ func TestInit_getProviderDetectedLegacy(t *testing.T) { // unknown provider, and the registry source will allow us to look up the // appropriate namespace if possible. providerSource, psClose := newMockProviderSource(t, map[string][]string{ - "hashicorp/foo": []string{"1.2.3"}, - "terraform-providers/baz": []string{"2.3.4"}, // this will not be installed + "hashicorp/foo": {"1.2.3"}, + "terraform-providers/baz": {"2.3.4"}, // this will not be installed }) defer psClose() registrySource, rsClose := testRegistrySource(t) @@ -1242,8 +1218,10 @@ func TestInit_getProviderDetectedLegacy(t *testing.T) { } ui := new(cli.MockUi) + view, _ := testView(t) m := Meta{ Ui: ui, + View: view, ProviderSource: multiSource, } @@ -1287,22 +1265,23 @@ func TestInit_getProviderDetectedLegacy(t *testing.T) { func TestInit_providerSource(t *testing.T) { // Create a temporary working directory that is empty td := tempDir(t) - configDirName := "init-required-providers" - testCopyDir(t, testFixturePath(configDirName), filepath.Join(td, configDirName)) + testCopyDir(t, testFixturePath("init-required-providers"), td) defer os.RemoveAll(td) defer testChdir(t, td)() providerSource, close := newMockProviderSource(t, map[string][]string{ - "test": []string{"1.2.3", "1.2.4"}, - "test-beta": []string{"1.2.4"}, - "source": []string{"1.2.2", "1.2.3", "1.2.1"}, + "test": {"1.2.3", "1.2.4"}, + "test-beta": {"1.2.4"}, + "source": {"1.2.2", "1.2.3", "1.2.1"}, }) defer close() ui := new(cli.MockUi) + view, _ := testView(t) m := Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, ProviderSource: providerSource, } @@ -1310,7 +1289,7 @@ func TestInit_providerSource(t *testing.T) { Meta: m, } - args := []string{configDirName} + args := []string{} if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) @@ -1394,15 +1373,14 @@ func TestInit_cancel(t *testing.T) { // platforms) were sent to it, testing that it is interruptible. td := tempDir(t) - configDirName := "init-required-providers" - testCopyDir(t, testFixturePath(configDirName), filepath.Join(td, configDirName)) + testCopyDir(t, testFixturePath("init-required-providers"), td) defer os.RemoveAll(td) defer testChdir(t, td)() providerSource, closeSrc := newMockProviderSource(t, map[string][]string{ - "test": []string{"1.2.3", "1.2.4"}, - "test-beta": []string{"1.2.4"}, - "source": []string{"1.2.2", "1.2.3", "1.2.1"}, + "test": {"1.2.3", "1.2.4"}, + "test-beta": {"1.2.4"}, + "source": {"1.2.2", "1.2.3", "1.2.1"}, }) defer closeSrc() @@ -1412,9 +1390,11 @@ func TestInit_cancel(t *testing.T) { close(shutdownCh) ui := cli.NewMockUi() + view, _ := testView(t) m := Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, ProviderSource: providerSource, ShutdownCh: shutdownCh, } @@ -1423,7 +1403,7 @@ func TestInit_cancel(t *testing.T) { Meta: m, } - args := []string{configDirName} + args := []string{} if code := c.Run(args); code == 0 { t.Fatalf("succeeded; wanted error") @@ -1446,24 +1426,26 @@ func TestInit_getUpgradePlugins(t *testing.T) { providerSource, close := newMockProviderSource(t, map[string][]string{ // looking for an exact version - "exact": []string{"1.2.3"}, + "exact": {"1.2.3"}, // config requires >= 2.3.3 - "greater-than": []string{"2.3.4", "2.3.3", "2.3.0"}, + "greater-than": {"2.3.4", "2.3.3", "2.3.0"}, // config specifies > 1.0.0 , < 3.0.0 - "between": []string{"3.4.5", "2.3.4", "1.2.3"}, + "between": {"3.4.5", "2.3.4", "1.2.3"}, }) defer close() ui := new(cli.MockUi) + view, _ := testView(t) m := Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, ProviderSource: providerSource, } installFakeProviderPackages(t, &m, map[string][]string{ - "exact": []string{"0.0.1"}, - "greater-than": []string{"2.3.3"}, + "exact": {"0.0.1"}, + "greater-than": {"2.3.3"}, }) c := &InitCommand{ @@ -1570,18 +1552,20 @@ func TestInit_getProviderMissing(t *testing.T) { providerSource, close := newMockProviderSource(t, map[string][]string{ // looking for exact version 1.2.3 - "exact": []string{"1.2.4"}, + "exact": {"1.2.4"}, // config requires >= 2.3.3 - "greater-than": []string{"2.3.4", "2.3.3", "2.3.0"}, + "greater-than": {"2.3.4", "2.3.3", "2.3.0"}, // config specifies - "between": []string{"3.4.5", "2.3.4", "1.2.3"}, + "between": {"3.4.5", "2.3.4", "1.2.3"}, }) defer close() ui := new(cli.MockUi) + view, _ := testView(t) m := Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, ProviderSource: providerSource, } @@ -1607,10 +1591,12 @@ func TestInit_checkRequiredVersion(t *testing.T) { defer testChdir(t, td)() ui := cli.NewMockUi() + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -1640,9 +1626,11 @@ func TestInit_providerLockFile(t *testing.T) { defer close() ui := new(cli.MockUi) + view, _ := testView(t) m := Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, ProviderSource: providerSource, } @@ -1678,6 +1666,163 @@ provider "registry.terraform.io/hashicorp/test" { if diff := cmp.Diff(wantLockFile, string(buf)); diff != "" { t.Errorf("wrong dependency lock file contents\n%s", diff) } + + // Make the local directory read-only, and verify that rerunning init + // succeeds, to ensure that we don't try to rewrite an unchanged lock file + os.Chmod(".", 0555) + if code := c.Run(args); code != 0 { + t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + } +} + +func TestInit_providerLockFileReadonly(t *testing.T) { + // The hash in here is for the fake package that newMockProviderSource produces + // (so it'll change if newMockProviderSource starts producing different contents) + inputLockFile := strings.TrimSpace(` +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/test" { + version = "1.2.3" + constraints = "1.2.3" + hashes = [ + "zh:e919b507a91e23a00da5c2c4d0b64bcc7900b68d43b3951ac0f6e5d80387fbdc", + ] +} +`) + + badLockFile := strings.TrimSpace(` +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/test" { + version = "1.2.3" + constraints = "1.2.3" + hashes = [ + "zh:0000000000000000000000000000000000000000000000000000000000000000", + ] +} +`) + + updatedLockFile := strings.TrimSpace(` +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/test" { + version = "1.2.3" + constraints = "1.2.3" + hashes = [ + "h1:wlbEC2mChQZ2hhgUhl6SeVLPP7fMqOFUZAQhQ9GIIno=", + "zh:e919b507a91e23a00da5c2c4d0b64bcc7900b68d43b3951ac0f6e5d80387fbdc", + ] +} +`) + + cases := []struct { + desc string + fixture string + providers map[string][]string + input string + args []string + ok bool + want string + }{ + { + desc: "default", + fixture: "init-provider-lock-file", + providers: map[string][]string{"test": {"1.2.3"}}, + input: inputLockFile, + args: []string{}, + ok: true, + want: updatedLockFile, + }, + { + desc: "readonly", + fixture: "init-provider-lock-file", + providers: map[string][]string{"test": {"1.2.3"}}, + input: inputLockFile, + args: []string{"-lockfile=readonly"}, + ok: true, + want: inputLockFile, + }, + { + desc: "conflict", + fixture: "init-provider-lock-file", + providers: map[string][]string{"test": {"1.2.3"}}, + input: inputLockFile, + args: []string{"-lockfile=readonly", "-upgrade"}, + ok: false, + want: inputLockFile, + }, + { + desc: "checksum mismatch", + fixture: "init-provider-lock-file", + providers: map[string][]string{"test": {"1.2.3"}}, + input: badLockFile, + args: []string{"-lockfile=readonly"}, + ok: false, + want: badLockFile, + }, + { + desc: "reject to change required provider dependences", + fixture: "init-provider-lock-file-readonly-add", + providers: map[string][]string{ + "test": {"1.2.3"}, + "foo": {"1.0.0"}, + }, + input: inputLockFile, + args: []string{"-lockfile=readonly"}, + ok: false, + want: inputLockFile, + }, + } + + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath(tc.fixture), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + providerSource, close := newMockProviderSource(t, tc.providers) + defer close() + + ui := new(cli.MockUi) + m := Meta{ + testingOverrides: metaOverridesForProvider(testProvider()), + Ui: ui, + ProviderSource: providerSource, + } + + c := &InitCommand{ + Meta: m, + } + + // write input lockfile + lockFile := ".terraform.lock.hcl" + if err := ioutil.WriteFile(lockFile, []byte(tc.input), 0644); err != nil { + t.Fatalf("failed to write input lockfile: %s", err) + } + + code := c.Run(tc.args) + if tc.ok && code != 0 { + t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + } + if !tc.ok && code == 0 { + t.Fatalf("expected error, got output: \n%s", ui.OutputWriter.String()) + } + + buf, err := ioutil.ReadFile(lockFile) + if err != nil { + t.Fatalf("failed to read dependency lock file %s: %s", lockFile, err) + } + buf = bytes.TrimSpace(buf) + if diff := cmp.Diff(tc.want, string(buf)); diff != "" { + t.Errorf("wrong dependency lock file contents\n%s", diff) + } + }) + } } func TestInit_pluginDirReset(t *testing.T) { @@ -1690,10 +1835,12 @@ func TestInit_pluginDirReset(t *testing.T) { defer close() ui := new(cli.MockUi) + view, _ := testView(t) c := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, ProviderSource: providerSource, }, } @@ -1726,6 +1873,7 @@ func TestInit_pluginDirReset(t *testing.T) { Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, ProviderSource: providerSource, // still empty }, } @@ -1758,9 +1906,11 @@ func TestInit_pluginDirProviders(t *testing.T) { defer close() ui := new(cli.MockUi) + view, _ := testView(t) m := Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, ProviderSource: providerSource, } @@ -1780,14 +1930,14 @@ func TestInit_pluginDirProviders(t *testing.T) { // for a moment that they are provider cache directories just because that // allows us to lean on our existing test helper functions to do this. for i, def := range [][]string{ - []string{"exact", "1.2.3"}, - []string{"greater-than", "2.3.4"}, - []string{"between", "2.3.4"}, + {"exact", "1.2.3"}, + {"greater-than", "2.3.4"}, + {"between", "2.3.4"}, } { name, version := def[0], def[1] dir := providercache.NewDir(pluginPath[i]) installFakeProviderPackagesElsewhere(t, dir, map[string][]string{ - name: []string{version}, + name: {version}, }) } @@ -1853,14 +2003,16 @@ func TestInit_pluginDirProvidersDoesNotGet(t *testing.T) { // but we should ignore it because -plugin-dir is set and thus this // source is temporarily overridden during install. providerSource, close := newMockProviderSource(t, map[string][]string{ - "between": []string{"2.3.4"}, + "between": {"2.3.4"}, }) defer close() ui := cli.NewMockUi() + view, _ := testView(t) m := Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, ProviderSource: providerSource, } @@ -1880,13 +2032,13 @@ func TestInit_pluginDirProvidersDoesNotGet(t *testing.T) { // for a moment that they are provider cache directories just because that // allows us to lean on our existing test helper functions to do this. for i, def := range [][]string{ - []string{"exact", "1.2.3"}, - []string{"greater-than", "2.3.4"}, + {"exact", "1.2.3"}, + {"greater-than", "2.3.4"}, } { name, version := def[0], def[1] dir := providercache.NewDir(pluginPath[i]) installFakeProviderPackagesElsewhere(t, dir, map[string][]string{ - name: []string{version}, + name: {version}, }) } @@ -1930,9 +2082,11 @@ func TestInit_pluginDirWithBuiltIn(t *testing.T) { defer close() ui := cli.NewMockUi() + view, _ := testView(t) m := Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, ProviderSource: providerSource, } @@ -1967,9 +2121,11 @@ func TestInit_invalidBuiltInProviders(t *testing.T) { defer close() ui := cli.NewMockUi() + view, _ := testView(t) m := Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, ProviderSource: providerSource, } diff --git a/vendor/github.com/hashicorp/terraform/command/internal_plugin.go b/vendor/github.com/hashicorp/terraform/command/internal_plugin.go deleted file mode 100644 index 33de8569..00000000 --- a/vendor/github.com/hashicorp/terraform/command/internal_plugin.go +++ /dev/null @@ -1,97 +0,0 @@ -package command - -import ( - "fmt" - "log" - "strings" - - "github.com/hashicorp/terraform/plugin" - "github.com/kardianos/osext" -) - -// InternalPluginCommand is a Command implementation that allows plugins to be -// compiled into the main Terraform binary and executed via a subcommand. -type InternalPluginCommand struct { - Meta -} - -const TFSPACE = "-TFSPACE-" - -// BuildPluginCommandString builds a special string for executing internal -// plugins. It has the following format: -// -// /path/to/terraform-TFSPACE-internal-plugin-TFSPACE-terraform-provider-aws -// -// We split the string on -TFSPACE- to build the command executor. The reason we -// use -TFSPACE- is so we can support spaces in the /path/to/terraform part. -func BuildPluginCommandString(pluginType, pluginName string) (string, error) { - terraformPath, err := osext.Executable() - if err != nil { - return "", err - } - parts := []string{terraformPath, "internal-plugin", pluginType, pluginName} - return strings.Join(parts, TFSPACE), nil -} - -// Internal plugins do not support any CLI args, but we do receive flags that -// main.go:mergeEnvArgs has merged in from EnvCLI. Instead of making main.go -// aware of this exception, we strip all flags from our args. Flags are easily -// identified by the '-' prefix, ensured by the cli package used. -func StripArgFlags(args []string) []string { - argsNoFlags := []string{} - for i := range args { - if !strings.HasPrefix(args[i], "-") { - argsNoFlags = append(argsNoFlags, args[i]) - } - } - return argsNoFlags -} - -func (c *InternalPluginCommand) Run(args []string) int { - // strip flags from args, only use subcommands. - args = StripArgFlags(args) - - if len(args) != 2 { - log.Printf("Wrong number of args; expected: terraform internal-plugin pluginType pluginName") - return 1 - } - - pluginType := args[0] - pluginName := args[1] - - log.SetPrefix(fmt.Sprintf("%s-%s (internal) ", pluginName, pluginType)) - - switch pluginType { - case "provisioner": - pluginFunc, found := InternalProvisioners[pluginName] - if !found { - log.Printf("[ERROR] Could not load provisioner: %s", pluginName) - return 1 - } - log.Printf("[INFO] Starting provisioner plugin %s", pluginName) - plugin.Serve(&plugin.ServeOpts{ - ProvisionerFunc: pluginFunc, - }) - default: - log.Printf("[ERROR] Invalid plugin type %s", pluginType) - return 1 - } - - return 0 -} - -func (c *InternalPluginCommand) Help() string { - helpText := ` -Usage: terraform internal-plugin pluginType pluginName - - Runs an internally-compiled version of a plugin from the terraform binary. - - NOTE: this is an internal command and you should not call it yourself. -` - - return strings.TrimSpace(helpText) -} - -func (c *InternalPluginCommand) Synopsis() string { - return "internal plugin command" -} diff --git a/vendor/github.com/hashicorp/terraform/command/internal_plugin_list.go b/vendor/github.com/hashicorp/terraform/command/internal_plugin_list.go deleted file mode 100644 index e26c2c19..00000000 --- a/vendor/github.com/hashicorp/terraform/command/internal_plugin_list.go +++ /dev/null @@ -1,28 +0,0 @@ -// -// This file is automatically generated by scripts/generate-plugins.go -- Do not edit! -// -package command - -import ( - chefprovisioner "github.com/hashicorp/terraform/builtin/provisioners/chef" - fileprovisioner "github.com/hashicorp/terraform/builtin/provisioners/file" - habitatprovisioner "github.com/hashicorp/terraform/builtin/provisioners/habitat" - localexecprovisioner "github.com/hashicorp/terraform/builtin/provisioners/local-exec" - puppetprovisioner "github.com/hashicorp/terraform/builtin/provisioners/puppet" - remoteexecprovisioner "github.com/hashicorp/terraform/builtin/provisioners/remote-exec" - saltmasterlessprovisioner "github.com/hashicorp/terraform/builtin/provisioners/salt-masterless" - - "github.com/hashicorp/terraform/plugin" -) - -var InternalProviders = map[string]plugin.ProviderFunc{} - -var InternalProvisioners = map[string]plugin.ProvisionerFunc{ - "chef": chefprovisioner.Provisioner, - "file": fileprovisioner.Provisioner, - "habitat": habitatprovisioner.Provisioner, - "local-exec": localexecprovisioner.Provisioner, - "puppet": puppetprovisioner.Provisioner, - "remote-exec": remoteexecprovisioner.Provisioner, - "salt-masterless": saltmasterlessprovisioner.Provisioner, -} diff --git a/vendor/github.com/hashicorp/terraform/command/internal_plugin_test.go b/vendor/github.com/hashicorp/terraform/command/internal_plugin_test.go deleted file mode 100644 index bcfb9782..00000000 --- a/vendor/github.com/hashicorp/terraform/command/internal_plugin_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package command - -import ( - "testing" -) - -func TestInternalPlugin_InternalProviders(t *testing.T) { - m := new(Meta) - providers := m.internalProviders() - // terraform is the only provider moved back to internal - for _, name := range []string{"terraform"} { - pf, ok := providers[name] - if !ok { - t.Errorf("Expected to find %s in InternalProviders", name) - } - - provider, err := pf() - if err != nil { - t.Fatal(err) - } - - if provider == nil { - t.Fatal("provider factory returned a nil provider") - } - } -} - -func TestInternalPlugin_InternalProvisioners(t *testing.T) { - for _, name := range []string{"chef", "file", "local-exec", "remote-exec", "salt-masterless"} { - if _, ok := InternalProvisioners[name]; !ok { - t.Errorf("Expected to find %s in InternalProvisioners", name) - } - } -} - -func TestInternalPlugin_BuildPluginCommandString(t *testing.T) { - actual, err := BuildPluginCommandString("provisioner", "remote-exec") - if err != nil { - t.Fatalf(err.Error()) - } - - expected := "-TFSPACE-internal-plugin-TFSPACE-provisioner-TFSPACE-remote-exec" - if actual[len(actual)-len(expected):] != expected { - t.Errorf("Expected command to end with %s; got:\n%s\n", expected, actual) - } -} - -func TestInternalPlugin_StripArgFlags(t *testing.T) { - actual := StripArgFlags([]string{"provisioner", "remote-exec", "-var-file=my_vars.tfvars", "-flag"}) - expected := []string{"provisioner", "remote-exec"} - // Must be same length and order. - if len(actual) != len(expected) || expected[0] != actual[0] || actual[1] != actual[1] { - t.Fatalf("Expected args to be exactly '%s', got '%s'", expected, actual) - } -} diff --git a/vendor/github.com/hashicorp/terraform/command/jsonconfig/config.go b/vendor/github.com/hashicorp/terraform/command/jsonconfig/config.go index bbb56231..8575bd25 100644 --- a/vendor/github.com/hashicorp/terraform/command/jsonconfig/config.go +++ b/vendor/github.com/hashicorp/terraform/command/jsonconfig/config.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/internal/getproviders" "github.com/hashicorp/terraform/terraform" ) @@ -58,6 +59,7 @@ type variables map[string]*variable type variable struct { Default json.RawMessage `json:"default,omitempty"` Description string `json:"description,omitempty"` + Sensitive bool `json:"sensitive,omitempty"` } // Resource is the representation of a resource in the config @@ -139,19 +141,61 @@ func marshalProviderConfigs( return } + // We want to determine only the provider requirements from this module, + // ignoring any descendants. Disregard any diagnostics when determining + // requirements because we want this marshalling to succeed even if there + // are invalid constraints. + reqs, _ := c.ProviderRequirementsShallow() + + // Add an entry for each provider configuration block in the module. for k, pc := range c.Module.ProviderConfigs { providerFqn := c.ProviderForConfigAddr(addrs.LocalProviderConfig{LocalName: pc.Name}) schema := schemas.ProviderConfig(providerFqn) + + p := providerConfig{ + Name: pc.Name, + Alias: pc.Alias, + ModuleAddress: c.Path.String(), + Expressions: marshalExpressions(pc.Config, schema), + } + + // Store the fully resolved provider version constraint, rather than + // using the version argument in the configuration block. This is both + // future proof (for when we finish the deprecation of the provider config + // version argument) and more accurate (as it reflects the full set of + // constraints, in case there are multiple). + if vc, ok := reqs[providerFqn]; ok { + p.VersionConstraint = getproviders.VersionConstraintsString(vc) + } + + key := opaqueProviderKey(k, c.Path.String()) + + m[key] = p + } + + // Ensure that any required providers with no associated configuration + // block are included in the set. + for k, pr := range c.Module.ProviderRequirements.RequiredProviders { + // If there exists a value for this provider, we have nothing to add + // to it, so skip. + key := opaqueProviderKey(k, c.Path.String()) + if _, exists := m[key]; exists { + continue + } + + // Given no provider configuration block exists, the only fields we can + // fill here are the local name, module address, and version + // constraints. p := providerConfig{ - Name: pc.Name, - Alias: pc.Alias, - ModuleAddress: c.Path.String(), - Expressions: marshalExpressions(pc.Config, schema), - VersionConstraint: pc.Version.Required.String(), + Name: pr.Name, + ModuleAddress: c.Path.String(), + } + + if vc, ok := reqs[pr.Type]; ok { + p.VersionConstraint = getproviders.VersionConstraintsString(vc) } - absPC := opaqueProviderKey(k, c.Path.String()) - m[absPC] = p + m[key] = p } // Must also visit our child modules, recursively. @@ -220,6 +264,7 @@ func marshalModule(c *configs.Config, schemas *terraform.Schemas, addr string) ( vars[k] = &variable{ Default: defaultValJSON, Description: v.Description, + Sensitive: v.Sensitive, } } module.Variables = vars diff --git a/vendor/github.com/hashicorp/terraform/command/jsonplan/plan.go b/vendor/github.com/hashicorp/terraform/command/jsonplan/plan.go index ee12a408..1db2067d 100644 --- a/vendor/github.com/hashicorp/terraform/command/jsonplan/plan.go +++ b/vendor/github.com/hashicorp/terraform/command/jsonplan/plan.go @@ -67,9 +67,31 @@ type change struct { // or "after" is unset (respectively). For ["no-op"], the before and after // values are identical. The "after" value will be incomplete if there are // values within it that won't be known until after apply. - Before json.RawMessage `json:"before,omitempty"` - After json.RawMessage `json:"after,omitempty"` + Before json.RawMessage `json:"before,omitempty"` + After json.RawMessage `json:"after,omitempty"` + + // AfterUnknown is an object value with similar structure to After, but + // with all unknown leaf values replaced with true, and all known leaf + // values omitted. This can be combined with After to reconstruct a full + // value after the action, including values which will only be known after + // apply. AfterUnknown json.RawMessage `json:"after_unknown,omitempty"` + + // BeforeSensitive and AfterSensitive are object values with similar + // structure to Before and After, but with all sensitive leaf values + // replaced with true, and all non-sensitive leaf values omitted. These + // objects should be combined with Before and After to prevent accidental + // display of sensitive values in user interfaces. + BeforeSensitive json.RawMessage `json:"before_sensitive,omitempty"` + AfterSensitive json.RawMessage `json:"after_sensitive,omitempty"` + + // ReplacePaths is an array of arrays representing a set of paths into the + // object value which resulted in the action being "replace". This will be + // omitted if the action is not replace, or if no paths caused the + // replacement (for example, if the resource was tainted). Each path + // consists of one or more steps, each of which will be a number or a + // string. + ReplacePaths json.RawMessage `json:"replace_paths,omitempty"` } type output struct { @@ -192,12 +214,22 @@ func (p *plan) marshalResourceChanges(changes *plans.Changes, schemas *terraform } var before, after []byte + var beforeSensitive, afterSensitive []byte var afterUnknown cty.Value if changeV.Before != cty.NilVal { before, err = ctyjson.Marshal(changeV.Before, changeV.Before.Type()) if err != nil { return err } + marks := rc.BeforeValMarks + if schema.ContainsSensitive() { + marks = append(marks, schema.ValueMarks(changeV.Before, nil)...) + } + bs := sensitiveAsBool(changeV.Before.MarkWithPaths(marks)) + beforeSensitive, err = ctyjson.Marshal(bs, bs.Type()) + if err != nil { + return err + } } if changeV.After != cty.NilVal { if changeV.After.IsWhollyKnown() { @@ -218,18 +250,34 @@ func (p *plan) marshalResourceChanges(changes *plans.Changes, schemas *terraform } afterUnknown = unknownAsBool(changeV.After) } + marks := rc.AfterValMarks + if schema.ContainsSensitive() { + marks = append(marks, schema.ValueMarks(changeV.After, nil)...) + } + as := sensitiveAsBool(changeV.After.MarkWithPaths(marks)) + afterSensitive, err = ctyjson.Marshal(as, as.Type()) + if err != nil { + return err + } } a, err := ctyjson.Marshal(afterUnknown, afterUnknown.Type()) if err != nil { return err } + replacePaths, err := encodePaths(rc.RequiredReplace) + if err != nil { + return err + } r.Change = change{ - Actions: actionString(rc.Action.String()), - Before: json.RawMessage(before), - After: json.RawMessage(after), - AfterUnknown: a, + Actions: actionString(rc.Action.String()), + Before: json.RawMessage(before), + After: json.RawMessage(after), + AfterUnknown: a, + BeforeSensitive: json.RawMessage(beforeSensitive), + AfterSensitive: json.RawMessage(afterSensitive), + ReplacePaths: replacePaths, } if rc.DeposedKey != states.NotDeposed { @@ -254,6 +302,19 @@ func (p *plan) marshalResourceChanges(changes *plans.Changes, schemas *terraform r.Type = addr.Resource.Resource.Type r.ProviderName = rc.ProviderAddr.Provider.String() + switch rc.ActionReason { + case plans.ResourceInstanceChangeNoReason: + r.ActionReason = "" // will be omitted in output + case plans.ResourceInstanceReplaceBecauseCannotUpdate: + r.ActionReason = "replace_because_cannot_update" + case plans.ResourceInstanceReplaceBecauseTainted: + r.ActionReason = "replace_because_tainted" + case plans.ResourceInstanceReplaceByRequest: + r.ActionReason = "replace_by_request" + default: + return fmt.Errorf("resource %s has an unsupported action reason %s", r.Address, rc.ActionReason) + } + p.ResourceChanges = append(p.ResourceChanges, r) } @@ -297,13 +358,28 @@ func (p *plan) marshalOutputChanges(changes *plans.Changes) error { } } + // The only information we have in the plan about output sensitivity is + // a boolean which is true if the output was or is marked sensitive. As + // a result, BeforeSensitive and AfterSensitive will be identical, and + // either false or true. + outputSensitive := cty.False + if oc.Sensitive { + outputSensitive = cty.True + } + sensitive, err := ctyjson.Marshal(outputSensitive, outputSensitive.Type()) + if err != nil { + return err + } + a, _ := ctyjson.Marshal(afterUnknown, afterUnknown.Type()) c := change{ - Actions: actionString(oc.Action.String()), - Before: json.RawMessage(before), - After: json.RawMessage(after), - AfterUnknown: a, + Actions: actionString(oc.Action.String()), + Before: json.RawMessage(before), + After: json.RawMessage(after), + AfterUnknown: a, + BeforeSensitive: json.RawMessage(sensitive), + AfterSensitive: json.RawMessage(sensitive), } p.OutputChanges[oc.Addr.OutputValue.Name] = c @@ -392,6 +468,9 @@ func omitUnknowns(val cty.Value) cty.Value { // tuple types and all mapping types are converted to object types, since we // assume the result of this is just going to be serialized as JSON (and thus // lose those distinctions) anyway. +// +// For map/object values, all known attribute values will be omitted instead of +// returning false, as this results in a more compact serialization. func unknownAsBool(val cty.Value) cty.Value { ty := val.Type() switch { @@ -439,7 +518,9 @@ func unknownAsBool(val cty.Value) cty.Value { for it.Next() { k, v := it.Element() vAsBool := unknownAsBool(v) - if !vAsBool.RawEquals(cty.False) { // all of the "false"s for known values for more compact serialization + // Omit all of the "false"s for known values for more compact + // serialization + if !vAsBool.RawEquals(cty.False) { vals[k.AsString()] = unknownAsBool(v) } } @@ -455,6 +536,88 @@ func unknownAsBool(val cty.Value) cty.Value { } } +// recursively iterate through a marked cty.Value, replacing sensitive values +// with cty.True and non-sensitive values with cty.False. +// +// The result also normalizes some types: all sequence types are turned into +// tuple types and all mapping types are converted to object types, since we +// assume the result of this is just going to be serialized as JSON (and thus +// lose those distinctions) anyway. +// +// For map/object values, all non-sensitive attribute values will be omitted +// instead of returning false, as this results in a more compact serialization. +func sensitiveAsBool(val cty.Value) cty.Value { + if val.HasMark("sensitive") { + return cty.True + } + + ty := val.Type() + switch { + case val.IsNull(), ty.IsPrimitiveType(), ty.Equals(cty.DynamicPseudoType): + return cty.False + case ty.IsListType() || ty.IsTupleType() || ty.IsSetType(): + if !val.IsKnown() { + // If the collection is unknown we can't say anything about the + // sensitivity of its contents + return cty.EmptyTupleVal + } + length := val.LengthInt() + if length == 0 { + // If there are no elements then we can't have sensitive values + return cty.EmptyTupleVal + } + vals := make([]cty.Value, 0, length) + it := val.ElementIterator() + for it.Next() { + _, v := it.Element() + vals = append(vals, sensitiveAsBool(v)) + } + // The above transform may have changed the types of some of the + // elements, so we'll always use a tuple here in case we've now made + // different elements have different types. Our ultimate goal is to + // marshal to JSON anyway, and all of these sequence types are + // indistinguishable in JSON. + return cty.TupleVal(vals) + case ty.IsMapType() || ty.IsObjectType(): + if !val.IsKnown() { + // If the map/object is unknown we can't say anything about the + // sensitivity of its attributes + return cty.EmptyObjectVal + } + var length int + switch { + case ty.IsMapType(): + length = val.LengthInt() + default: + length = len(val.Type().AttributeTypes()) + } + if length == 0 { + // If there are no elements then we can't have sensitive values + return cty.EmptyObjectVal + } + vals := make(map[string]cty.Value) + it := val.ElementIterator() + for it.Next() { + k, v := it.Element() + s := sensitiveAsBool(v) + // Omit all of the "false"s for non-sensitive values for more + // compact serialization + if !s.RawEquals(cty.False) { + vals[k.AsString()] = s + } + } + // The above transform may have changed the types of some of the + // elements, so we'll always use an object here in case we've now made + // different elements have different types. Our ultimate goal is to + // marshal to JSON anyway, and all of these mapping types are + // indistinguishable in JSON. + return cty.ObjectVal(vals) + default: + // Should never happen, since the above should cover all types + panic(fmt.Sprintf("sensitiveAsBool cannot handle %#v", val)) + } +} + func actionString(action string) []string { switch { case action == "NoOp": @@ -475,3 +638,54 @@ func actionString(action string) []string { return []string{action} } } + +// encodePaths lossily encodes a cty.PathSet into an array of arrays of step +// values, such as: +// +// [["length"],["triggers",0,"value"]] +// +// The lossiness is that we cannot distinguish between an IndexStep with string +// key and a GetAttr step. This is fine with JSON output, because JSON's type +// system means that those two steps are equivalent anyway: both are object +// indexes. +// +// JavaScript (or similar dynamic language) consumers of these values can +// recursively apply the steps to a given object using an index operation for +// each step. +func encodePaths(pathSet cty.PathSet) (json.RawMessage, error) { + if pathSet.Empty() { + return nil, nil + } + + pathList := pathSet.List() + jsonPaths := make([]json.RawMessage, 0, len(pathList)) + + for _, path := range pathList { + steps := make([]json.RawMessage, 0, len(path)) + for _, step := range path { + switch s := step.(type) { + case cty.IndexStep: + key, err := ctyjson.Marshal(s.Key, s.Key.Type()) + if err != nil { + return nil, fmt.Errorf("Failed to marshal index step key %#v: %s", s.Key, err) + } + steps = append(steps, key) + case cty.GetAttrStep: + name, err := json.Marshal(s.Name) + if err != nil { + return nil, fmt.Errorf("Failed to marshal get attr step name %#v: %s", s.Name, err) + } + steps = append(steps, name) + default: + return nil, fmt.Errorf("Unsupported path step %#v (%t)", step, step) + } + } + jsonPath, err := json.Marshal(steps) + if err != nil { + return nil, err + } + jsonPaths = append(jsonPaths, jsonPath) + } + + return json.Marshal(jsonPaths) +} diff --git a/vendor/github.com/hashicorp/terraform/command/jsonplan/plan_test.go b/vendor/github.com/hashicorp/terraform/command/jsonplan/plan_test.go index 2a2e6b14..5656a623 100644 --- a/vendor/github.com/hashicorp/terraform/command/jsonplan/plan_test.go +++ b/vendor/github.com/hashicorp/terraform/command/jsonplan/plan_test.go @@ -1,9 +1,11 @@ package jsonplan import ( + "encoding/json" "reflect" "testing" + "github.com/google/go-cmp/cmp" "github.com/zclconf/go-cty/cty" ) @@ -262,3 +264,256 @@ func TestUnknownAsBool(t *testing.T) { } } } + +func TestSensitiveAsBool(t *testing.T) { + sensitive := "sensitive" + tests := []struct { + Input cty.Value + Want cty.Value + }{ + { + cty.StringVal("hello"), + cty.False, + }, + { + cty.NullVal(cty.String), + cty.False, + }, + { + cty.StringVal("hello").Mark(sensitive), + cty.True, + }, + { + cty.NullVal(cty.String).Mark(sensitive), + cty.True, + }, + + { + cty.NullVal(cty.DynamicPseudoType).Mark(sensitive), + cty.True, + }, + { + cty.NullVal(cty.Object(map[string]cty.Type{"test": cty.String})), + cty.False, + }, + { + cty.NullVal(cty.Object(map[string]cty.Type{"test": cty.String})).Mark(sensitive), + cty.True, + }, + { + cty.DynamicVal, + cty.False, + }, + { + cty.DynamicVal.Mark(sensitive), + cty.True, + }, + + { + cty.ListValEmpty(cty.String), + cty.EmptyTupleVal, + }, + { + cty.ListValEmpty(cty.String).Mark(sensitive), + cty.True, + }, + { + cty.ListVal([]cty.Value{ + cty.StringVal("hello"), + cty.StringVal("friend").Mark(sensitive), + }), + cty.TupleVal([]cty.Value{ + cty.False, + cty.True, + }), + }, + { + cty.SetValEmpty(cty.String), + cty.EmptyTupleVal, + }, + { + cty.SetValEmpty(cty.String).Mark(sensitive), + cty.True, + }, + { + cty.SetVal([]cty.Value{cty.StringVal("hello")}), + cty.TupleVal([]cty.Value{cty.False}), + }, + { + cty.SetVal([]cty.Value{cty.StringVal("hello").Mark(sensitive)}), + cty.True, + }, + { + cty.EmptyTupleVal.Mark(sensitive), + cty.True, + }, + { + cty.TupleVal([]cty.Value{ + cty.StringVal("hello"), + cty.StringVal("friend").Mark(sensitive), + }), + cty.TupleVal([]cty.Value{ + cty.False, + cty.True, + }), + }, + { + cty.MapValEmpty(cty.String), + cty.EmptyObjectVal, + }, + { + cty.MapValEmpty(cty.String).Mark(sensitive), + cty.True, + }, + { + cty.MapVal(map[string]cty.Value{ + "greeting": cty.StringVal("hello"), + "animal": cty.StringVal("horse"), + }), + cty.EmptyObjectVal, + }, + { + cty.MapVal(map[string]cty.Value{ + "greeting": cty.StringVal("hello"), + "animal": cty.StringVal("horse").Mark(sensitive), + }), + cty.ObjectVal(map[string]cty.Value{ + "animal": cty.True, + }), + }, + { + cty.MapVal(map[string]cty.Value{ + "greeting": cty.StringVal("hello"), + "animal": cty.StringVal("horse").Mark(sensitive), + }).Mark(sensitive), + cty.True, + }, + { + cty.EmptyObjectVal, + cty.EmptyObjectVal, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "greeting": cty.StringVal("hello"), + "animal": cty.StringVal("horse"), + }), + cty.EmptyObjectVal, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "greeting": cty.StringVal("hello"), + "animal": cty.StringVal("horse").Mark(sensitive), + }), + cty.ObjectVal(map[string]cty.Value{ + "animal": cty.True, + }), + }, + { + cty.ObjectVal(map[string]cty.Value{ + "greeting": cty.StringVal("hello"), + "animal": cty.StringVal("horse").Mark(sensitive), + }).Mark(sensitive), + cty.True, + }, + { + cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "a": cty.UnknownVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("known").Mark(sensitive), + }), + }), + cty.TupleVal([]cty.Value{ + cty.EmptyObjectVal, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.True, + }), + }), + }, + { + cty.ListVal([]cty.Value{ + cty.MapValEmpty(cty.String), + cty.MapVal(map[string]cty.Value{ + "a": cty.StringVal("known").Mark(sensitive), + }), + cty.MapVal(map[string]cty.Value{ + "a": cty.UnknownVal(cty.String), + }), + }), + cty.TupleVal([]cty.Value{ + cty.EmptyObjectVal, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.True, + }), + cty.EmptyObjectVal, + }), + }, + { + cty.ObjectVal(map[string]cty.Value{ + "list": cty.UnknownVal(cty.List(cty.String)), + "set": cty.UnknownVal(cty.Set(cty.Bool)), + "tuple": cty.UnknownVal(cty.Tuple([]cty.Type{cty.String, cty.Number})), + "map": cty.UnknownVal(cty.Map(cty.String)), + "object": cty.UnknownVal(cty.Object(map[string]cty.Type{"a": cty.String})), + }), + cty.ObjectVal(map[string]cty.Value{ + "list": cty.EmptyTupleVal, + "set": cty.EmptyTupleVal, + "tuple": cty.EmptyTupleVal, + "map": cty.EmptyObjectVal, + "object": cty.EmptyObjectVal, + }), + }, + } + + for _, test := range tests { + got := sensitiveAsBool(test.Input) + if !reflect.DeepEqual(got, test.Want) { + t.Errorf( + "wrong result\ninput: %#v\ngot: %#v\nwant: %#v", + test.Input, got, test.Want, + ) + } + } +} + +func TestEncodePaths(t *testing.T) { + tests := map[string]struct { + Input cty.PathSet + Want json.RawMessage + }{ + "empty set": { + cty.NewPathSet(), + json.RawMessage(nil), + }, + "index path with string and int steps": { + cty.NewPathSet(cty.IndexStringPath("boop").IndexInt(0)), + json.RawMessage(`[["boop",0]]`), + }, + "get attr path with one step": { + cty.NewPathSet(cty.GetAttrPath("triggers")), + json.RawMessage(`[["triggers"]]`), + }, + "multiple paths of different types": { + cty.NewPathSet( + cty.GetAttrPath("alpha").GetAttr("beta").GetAttr("gamma"), + cty.GetAttrPath("triggers").IndexString("name"), + cty.IndexIntPath(0).IndexInt(1).IndexInt(2).IndexInt(3), + ), + json.RawMessage(`[["alpha","beta","gamma"],["triggers","name"],[0,1,2,3]]`), + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + got, err := encodePaths(test.Input) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + if !cmp.Equal(got, test.Want) { + t.Errorf("wrong result:\n %v\n", cmp.Diff(got, test.Want)) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/jsonplan/resource.go b/vendor/github.com/hashicorp/terraform/command/jsonplan/resource.go index 97e4851a..3f6e9a50 100644 --- a/vendor/github.com/hashicorp/terraform/command/jsonplan/resource.go +++ b/vendor/github.com/hashicorp/terraform/command/jsonplan/resource.go @@ -61,4 +61,14 @@ type resourceChange struct { // Change describes the change that will be made to this object Change change `json:"change,omitempty"` + + // ActionReason is a keyword representing some optional extra context + // for why the actions in Change.Actions were chosen. + // + // This extra detail is only for display purposes, to help a UI layer + // present some additional explanation to a human user. The possible + // values here might grow and change over time, so any consumer of this + // information should be resilient to encountering unrecognized values + // and treat them as an unspecified reason. + ActionReason string `json:"action_reason,omitempty"` } diff --git a/vendor/github.com/hashicorp/terraform/command/jsonprovider/attribute.go b/vendor/github.com/hashicorp/terraform/command/jsonprovider/attribute.go index fd5adb26..0702e73c 100644 --- a/vendor/github.com/hashicorp/terraform/command/jsonprovider/attribute.go +++ b/vendor/github.com/hashicorp/terraform/command/jsonprovider/attribute.go @@ -4,17 +4,26 @@ import ( "encoding/json" "github.com/hashicorp/terraform/configs/configschema" + "github.com/zclconf/go-cty/cty" ) type attribute struct { - AttributeType json.RawMessage `json:"type,omitempty"` - Description string `json:"description,omitempty"` - DescriptionKind string `json:"description_kind,omitempty"` - Deprecated bool `json:"deprecated,omitempty"` - Required bool `json:"required,omitempty"` - Optional bool `json:"optional,omitempty"` - Computed bool `json:"computed,omitempty"` - Sensitive bool `json:"sensitive,omitempty"` + AttributeType json.RawMessage `json:"type,omitempty"` + AttributeNestedType *nestedType `json:"nested_type,omitempty"` + Description string `json:"description,omitempty"` + DescriptionKind string `json:"description_kind,omitempty"` + Deprecated bool `json:"deprecated,omitempty"` + Required bool `json:"required,omitempty"` + Optional bool `json:"optional,omitempty"` + Computed bool `json:"computed,omitempty"` + Sensitive bool `json:"sensitive,omitempty"` +} + +type nestedType struct { + Attributes map[string]*attribute `json:"attributes,omitempty"` + NestingMode string `json:"nesting_mode,omitempty"` + MinItems uint64 `json:"min_items,omitempty"` + MaxItems uint64 `json:"max_items,omitempty"` } func marshalStringKind(sk configschema.StringKind) string { @@ -27,12 +36,7 @@ func marshalStringKind(sk configschema.StringKind) string { } func marshalAttribute(attr *configschema.Attribute) *attribute { - // we're not concerned about errors because at this point the schema has - // already been checked and re-checked. - attrTy, _ := attr.Type.MarshalJSON() - - return &attribute{ - AttributeType: attrTy, + ret := &attribute{ Description: attr.Description, DescriptionKind: marshalStringKind(attr.DescriptionKind), Required: attr.Required, @@ -41,4 +45,27 @@ func marshalAttribute(attr *configschema.Attribute) *attribute { Sensitive: attr.Sensitive, Deprecated: attr.Deprecated, } + + // we're not concerned about errors because at this point the schema has + // already been checked and re-checked. + if attr.Type != cty.NilType { + attrTy, _ := attr.Type.MarshalJSON() + ret.AttributeType = attrTy + } + + if attr.NestedType != nil { + nestedTy := nestedType{ + MinItems: uint64(attr.NestedType.MinItems), + MaxItems: uint64(attr.NestedType.MaxItems), + NestingMode: nestingModeString(attr.NestedType.Nesting), + } + attrs := make(map[string]*attribute, len(attr.NestedType.Attributes)) + for k, attr := range attr.NestedType.Attributes { + attrs[k] = marshalAttribute(attr) + } + nestedTy.Attributes = attrs + ret.AttributeNestedType = &nestedTy + } + + return ret } diff --git a/vendor/github.com/hashicorp/terraform/command/jsonprovider/block.go b/vendor/github.com/hashicorp/terraform/command/jsonprovider/block.go index 3f90987f..a41f9a83 100644 --- a/vendor/github.com/hashicorp/terraform/command/jsonprovider/block.go +++ b/vendor/github.com/hashicorp/terraform/command/jsonprovider/block.go @@ -24,24 +24,10 @@ func marshalBlockTypes(nestedBlock *configschema.NestedBlock) *blockType { return &blockType{} } ret := &blockType{ - Block: marshalBlock(&nestedBlock.Block), - MinItems: uint64(nestedBlock.MinItems), - MaxItems: uint64(nestedBlock.MaxItems), - } - - switch nestedBlock.Nesting { - case configschema.NestingSingle: - ret.NestingMode = "single" - case configschema.NestingGroup: - ret.NestingMode = "group" - case configschema.NestingList: - ret.NestingMode = "list" - case configschema.NestingSet: - ret.NestingMode = "set" - case configschema.NestingMap: - ret.NestingMode = "map" - default: - ret.NestingMode = "invalid" + Block: marshalBlock(&nestedBlock.Block), + MinItems: uint64(nestedBlock.MinItems), + MaxItems: uint64(nestedBlock.MaxItems), + NestingMode: nestingModeString(nestedBlock.Nesting), } return ret } @@ -75,3 +61,20 @@ func marshalBlock(configBlock *configschema.Block) *block { return &ret } + +func nestingModeString(mode configschema.NestingMode) string { + switch mode { + case configschema.NestingSingle: + return "single" + case configschema.NestingGroup: + return "group" + case configschema.NestingList: + return "list" + case configschema.NestingSet: + return "set" + case configschema.NestingMap: + return "map" + default: + return "invalid" + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/jsonprovider/provider.go b/vendor/github.com/hashicorp/terraform/command/jsonprovider/provider.go index c5d6734f..e6a84b87 100644 --- a/vendor/github.com/hashicorp/terraform/command/jsonprovider/provider.go +++ b/vendor/github.com/hashicorp/terraform/command/jsonprovider/provider.go @@ -9,7 +9,7 @@ import ( // FormatVersion represents the version of the json format and will be // incremented for any change to this format that requires changes to a // consuming parser. -const FormatVersion = "0.1" +const FormatVersion = "0.2" // providers is the top-level object returned when exporting provider schemas type providers struct { diff --git a/vendor/github.com/hashicorp/terraform/command/jsonprovider/provider_test.go b/vendor/github.com/hashicorp/terraform/command/jsonprovider/provider_test.go index 8ae049f0..098d5dc1 100644 --- a/vendor/github.com/hashicorp/terraform/command/jsonprovider/provider_test.go +++ b/vendor/github.com/hashicorp/terraform/command/jsonprovider/provider_test.go @@ -7,7 +7,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/zclconf/go-cty/cty" - "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/terraform" ) @@ -52,6 +51,25 @@ func TestMarshalProvider(t *testing.T) { Optional: true, DescriptionKind: "plain", }, + "volumes": { + AttributeNestedType: &nestedType{ + NestingMode: "list", + Attributes: map[string]*attribute{ + "size": { + AttributeType: json.RawMessage(`"string"`), + Required: true, + DescriptionKind: "plain", + }, + "mount_point": { + AttributeType: json.RawMessage(`"string"`), + Required: true, + DescriptionKind: "plain", + }, + }, + }, + Optional: true, + DescriptionKind: "plain", + }, }, BlockTypes: map[string]*blockType{ "network_interface": { @@ -130,14 +148,6 @@ func TestMarshalProvider(t *testing.T) { } } -func testProviders() *terraform.Schemas { - return &terraform.Schemas{ - Providers: map[addrs.Provider]*terraform.ProviderSchema{ - addrs.NewDefaultProvider("test"): testProvider(), - }, - } -} - func testProvider() *terraform.ProviderSchema { return &terraform.ProviderSchema{ Provider: &configschema.Block{ @@ -150,6 +160,16 @@ func testProvider() *terraform.ProviderSchema { Attributes: map[string]*configschema.Attribute{ "id": {Type: cty.String, Optional: true, Computed: true}, "ami": {Type: cty.String, Optional: true}, + "volumes": { + Optional: true, + NestedType: &configschema.Object{ + Nesting: configschema.NestingList, + Attributes: map[string]*configschema.Attribute{ + "size": {Type: cty.String, Required: true}, + "mount_point": {Type: cty.String, Required: true}, + }, + }, + }, }, BlockTypes: map[string]*configschema.NestedBlock{ "network_interface": { diff --git a/vendor/github.com/hashicorp/terraform/command/jsonstate/state.go b/vendor/github.com/hashicorp/terraform/command/jsonstate/state.go index 24eba59b..2bb5e157 100644 --- a/vendor/github.com/hashicorp/terraform/command/jsonstate/state.go +++ b/vendor/github.com/hashicorp/terraform/command/jsonstate/state.go @@ -9,7 +9,6 @@ import ( ctyjson "github.com/zclconf/go-cty/cty/json" "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/states/statefile" "github.com/hashicorp/terraform/terraform" @@ -100,7 +99,10 @@ type resource struct { // resource, whose structure depends on the resource type schema. type attributeValues map[string]interface{} -func marshalAttributeValues(value cty.Value, schema *configschema.Block) attributeValues { +func marshalAttributeValues(value cty.Value) attributeValues { + // unmark our value to show all values + value, _ = value.UnmarkDeep() + if value == cty.NilVal || value.IsNull() { return nil } @@ -196,14 +198,30 @@ func marshalRootModule(s *states.State, schemas *terraform.Schemas) (module, err return ret, err } - // build a map of module -> [child module addresses] - moduleMap := make(map[string][]addrs.ModuleInstance) + // build a map of module -> set[child module addresses] + moduleChildSet := make(map[string]map[string]struct{}) for _, mod := range s.Modules { if mod.Addr.IsRoot() { continue } else { - parent := mod.Addr.Parent().String() - moduleMap[parent] = append(moduleMap[parent], mod.Addr) + for childAddr := mod.Addr; !childAddr.IsRoot(); childAddr = childAddr.Parent() { + if _, ok := moduleChildSet[childAddr.Parent().String()]; !ok { + moduleChildSet[childAddr.Parent().String()] = map[string]struct{}{} + } + moduleChildSet[childAddr.Parent().String()][childAddr.String()] = struct{}{} + } + } + } + + // transform the previous map into map of module -> [child module addresses] + moduleMap := make(map[string][]addrs.ModuleInstance) + for parent, children := range moduleChildSet { + for child := range children { + childModuleInstance, diags := addrs.ParseModuleInstanceStr(child) + if diags.HasErrors() { + return ret, diags.Err() + } + moduleMap[parent] = append(moduleMap[parent], childModuleInstance) } } @@ -222,14 +240,19 @@ func marshalModules( ) ([]module, error) { var ret []module for _, child := range modules { - stateMod := s.Module(child) // cm for child module, naming things is hard. - cm := module{Address: stateMod.Addr.String()} - rs, err := marshalResources(stateMod.Resources, stateMod.Addr, schemas) - if err != nil { - return nil, err + cm := module{Address: child.String()} + + // the module may be resourceless and contain only submodules, it will then be nil here + stateMod := s.Module(child) + if stateMod != nil { + rs, err := marshalResources(stateMod.Resources, stateMod.Addr, schemas) + if err != nil { + return nil, err + } + cm.Resources = rs } - cm.Resources = rs + if moduleMap[child.String()] != nil { moreChildModules, err := marshalModules(s, schemas, moduleMap[child.String()], moduleMap) if err != nil { @@ -277,7 +300,7 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module ) } - schema, _ := schemas.ResourceTypeConfig( + schema, version := schemas.ResourceTypeConfig( r.ProviderConfig.Provider, resAddr.Mode, resAddr.Type, @@ -285,6 +308,10 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module // It is possible that the only instance is deposed if ri.Current != nil { + if version != ri.Current.SchemaVersion { + return nil, fmt.Errorf("schema version %d for %s in state does not match version %d from the provider", ri.Current.SchemaVersion, resAddr, version) + } + current.SchemaVersion = ri.Current.SchemaVersion if schema == nil { @@ -295,7 +322,7 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module return nil, err } - current.AttributeValues = marshalAttributeValues(riObj.Value, schema) + current.AttributeValues = marshalAttributeValues(riObj.Value) if len(riObj.Dependencies) > 0 { dependencies := make([]string, len(riObj.Dependencies)) @@ -327,7 +354,7 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module return nil, err } - deposed.AttributeValues = marshalAttributeValues(riObj.Value, schema) + deposed.AttributeValues = marshalAttributeValues(riObj.Value) if len(riObj.Dependencies) > 0 { dependencies := make([]string, len(riObj.Dependencies)) diff --git a/vendor/github.com/hashicorp/terraform/command/jsonstate/state_test.go b/vendor/github.com/hashicorp/terraform/command/jsonstate/state_test.go index 55d3c47f..c4b86810 100644 --- a/vendor/github.com/hashicorp/terraform/command/jsonstate/state_test.go +++ b/vendor/github.com/hashicorp/terraform/command/jsonstate/state_test.go @@ -75,60 +75,27 @@ func TestMarshalOutputs(t *testing.T) { func TestMarshalAttributeValues(t *testing.T) { tests := []struct { - Attr cty.Value - Schema *configschema.Block - Want attributeValues + Attr cty.Value + Want attributeValues }{ { cty.NilVal, - &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": { - Type: cty.String, - Optional: true, - }, - }, - }, nil, }, { cty.NullVal(cty.String), - &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": { - Type: cty.String, - Optional: true, - }, - }, - }, nil, }, { cty.ObjectVal(map[string]cty.Value{ "foo": cty.StringVal("bar"), }), - &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": { - Type: cty.String, - Optional: true, - }, - }, - }, attributeValues{"foo": json.RawMessage(`"bar"`)}, }, { cty.ObjectVal(map[string]cty.Value{ "foo": cty.NullVal(cty.String), }), - &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": { - Type: cty.String, - Optional: true, - }, - }, - }, attributeValues{"foo": json.RawMessage(`null`)}, }, { @@ -141,18 +108,22 @@ func TestMarshalAttributeValues(t *testing.T) { cty.StringVal("moon"), }), }), - &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "bar": { - Type: cty.Map(cty.String), - Required: true, - }, - "baz": { - Type: cty.List(cty.String), - Optional: true, - }, - }, + attributeValues{ + "bar": json.RawMessage(`{"hello":"world"}`), + "baz": json.RawMessage(`["goodnight","moon"]`), }, + }, + // Marked values + { + cty.ObjectVal(map[string]cty.Value{ + "bar": cty.MapVal(map[string]cty.Value{ + "hello": cty.StringVal("world"), + }), + "baz": cty.ListVal([]cty.Value{ + cty.StringVal("goodnight"), + cty.StringVal("moon").Mark("sensitive"), + }), + }), attributeValues{ "bar": json.RawMessage(`{"hello":"world"}`), "baz": json.RawMessage(`["goodnight","moon"]`), @@ -161,7 +132,7 @@ func TestMarshalAttributeValues(t *testing.T) { } for _, test := range tests { - got := marshalAttributeValues(test.Attr, test.Schema) + got := marshalAttributeValues(test.Attr) eq := reflect.DeepEqual(got, test.Want) if !eq { t.Fatalf("wrong result:\nGot: %#v\nWant: %#v\n", got, test.Want) @@ -196,9 +167,8 @@ func TestMarshalResources(t *testing.T) { Instances: map[addrs.InstanceKey]*states.ResourceInstance{ addrs.NoKey: { Current: &states.ResourceInstanceObjectSrc{ - SchemaVersion: 1, - Status: states.ObjectReady, - AttrsJSON: []byte(`{"woozles":"confuzles"}`), + Status: states.ObjectReady, + AttrsJSON: []byte(`{"woozles":"confuzles"}`), }, }, }, @@ -211,13 +181,12 @@ func TestMarshalResources(t *testing.T) { testSchemas(), []resource{ resource{ - Address: "test_thing.bar", - Mode: "managed", - Type: "test_thing", - Name: "bar", - Index: addrs.InstanceKey(nil), - ProviderName: "registry.terraform.io/hashicorp/test", - SchemaVersion: 1, + Address: "test_thing.bar", + Mode: "managed", + Type: "test_thing", + Name: "bar", + Index: addrs.InstanceKey(nil), + ProviderName: "registry.terraform.io/hashicorp/test", AttributeValues: attributeValues{ "foozles": json.RawMessage(`null`), "woozles": json.RawMessage(`"confuzles"`), @@ -226,6 +195,35 @@ func TestMarshalResources(t *testing.T) { }, false, }, + "single resource wrong schema": { + map[string]*states.Resource{ + "test_thing.baz": { + Addr: addrs.AbsResource{ + Resource: addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_thing", + Name: "bar", + }, + }, + Instances: map[addrs.InstanceKey]*states.ResourceInstance{ + addrs.NoKey: { + Current: &states.ResourceInstanceObjectSrc{ + SchemaVersion: 1, + Status: states.ObjectReady, + AttrsJSON: []byte(`{"woozles":["confuzles"]}`), + }, + }, + }, + ProviderConfig: addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + }, + }, + testSchemas(), + nil, + true, + }, "resource with count": { map[string]*states.Resource{ "test_thing.bar": { @@ -239,9 +237,8 @@ func TestMarshalResources(t *testing.T) { Instances: map[addrs.InstanceKey]*states.ResourceInstance{ addrs.IntKey(0): { Current: &states.ResourceInstanceObjectSrc{ - SchemaVersion: 1, - Status: states.ObjectReady, - AttrsJSON: []byte(`{"woozles":"confuzles"}`), + Status: states.ObjectReady, + AttrsJSON: []byte(`{"woozles":"confuzles"}`), }, }, }, @@ -254,13 +251,12 @@ func TestMarshalResources(t *testing.T) { testSchemas(), []resource{ resource{ - Address: "test_thing.bar[0]", - Mode: "managed", - Type: "test_thing", - Name: "bar", - Index: addrs.IntKey(0), - ProviderName: "registry.terraform.io/hashicorp/test", - SchemaVersion: 1, + Address: "test_thing.bar[0]", + Mode: "managed", + Type: "test_thing", + Name: "bar", + Index: addrs.IntKey(0), + ProviderName: "registry.terraform.io/hashicorp/test", AttributeValues: attributeValues{ "foozles": json.RawMessage(`null`), "woozles": json.RawMessage(`"confuzles"`), @@ -282,9 +278,8 @@ func TestMarshalResources(t *testing.T) { Instances: map[addrs.InstanceKey]*states.ResourceInstance{ addrs.StringKey("rockhopper"): { Current: &states.ResourceInstanceObjectSrc{ - SchemaVersion: 1, - Status: states.ObjectReady, - AttrsJSON: []byte(`{"woozles":"confuzles"}`), + Status: states.ObjectReady, + AttrsJSON: []byte(`{"woozles":"confuzles"}`), }, }, }, @@ -297,13 +292,12 @@ func TestMarshalResources(t *testing.T) { testSchemas(), []resource{ resource{ - Address: "test_thing.bar[\"rockhopper\"]", - Mode: "managed", - Type: "test_thing", - Name: "bar", - Index: addrs.StringKey("rockhopper"), - ProviderName: "registry.terraform.io/hashicorp/test", - SchemaVersion: 1, + Address: "test_thing.bar[\"rockhopper\"]", + Mode: "managed", + Type: "test_thing", + Name: "bar", + Index: addrs.StringKey("rockhopper"), + ProviderName: "registry.terraform.io/hashicorp/test", AttributeValues: attributeValues{ "foozles": json.RawMessage(`null`), "woozles": json.RawMessage(`"confuzles"`), @@ -326,9 +320,8 @@ func TestMarshalResources(t *testing.T) { addrs.NoKey: { Deposed: map[states.DeposedKey]*states.ResourceInstanceObjectSrc{ states.DeposedKey(deposedKey): &states.ResourceInstanceObjectSrc{ - SchemaVersion: 1, - Status: states.ObjectReady, - AttrsJSON: []byte(`{"woozles":"confuzles"}`), + Status: states.ObjectReady, + AttrsJSON: []byte(`{"woozles":"confuzles"}`), }, }, }, @@ -371,15 +364,13 @@ func TestMarshalResources(t *testing.T) { addrs.NoKey: { Deposed: map[states.DeposedKey]*states.ResourceInstanceObjectSrc{ states.DeposedKey(deposedKey): &states.ResourceInstanceObjectSrc{ - SchemaVersion: 1, - Status: states.ObjectReady, - AttrsJSON: []byte(`{"woozles":"confuzles"}`), + Status: states.ObjectReady, + AttrsJSON: []byte(`{"woozles":"confuzles"}`), }, }, Current: &states.ResourceInstanceObjectSrc{ - SchemaVersion: 1, - Status: states.ObjectReady, - AttrsJSON: []byte(`{"woozles":"confuzles"}`), + Status: states.ObjectReady, + AttrsJSON: []byte(`{"woozles":"confuzles"}`), }, }, }, @@ -392,13 +383,12 @@ func TestMarshalResources(t *testing.T) { testSchemas(), []resource{ resource{ - Address: "test_thing.bar", - Mode: "managed", - Type: "test_thing", - Name: "bar", - Index: addrs.InstanceKey(nil), - ProviderName: "registry.terraform.io/hashicorp/test", - SchemaVersion: 1, + Address: "test_thing.bar", + Mode: "managed", + Type: "test_thing", + Name: "bar", + Index: addrs.InstanceKey(nil), + ProviderName: "registry.terraform.io/hashicorp/test", AttributeValues: attributeValues{ "foozles": json.RawMessage(`null`), "woozles": json.RawMessage(`"confuzles"`), @@ -583,6 +573,59 @@ func TestMarshalModules_nested(t *testing.T) { } } +func TestMarshalModules_parent_no_resources(t *testing.T) { + subModule, _ := addrs.ParseModuleInstanceStr("module.child.module.submodule") + testState := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(subModule), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: subModule.Module(), + }, + ) + }) + got, err := marshalRootModule(testState, testSchemas()) + + if err != nil { + t.Fatalf("unexpected error: %s", err.Error()) + } + + if len(got.ChildModules) != 1 { + t.Fatalf("wrong result! got %d modules, expected 1", len(got.ChildModules)) + } + + if got.ChildModules[0].Address != "module.child" { + t.Fatalf("wrong result! got %#v\n", got) + } + + if got.ChildModules[0].ChildModules[0].Address != "module.child.module.submodule" { + t.Fatalf("wrong result! got %#v\n", got) + } +} + func testSchemas() *terraform.Schemas { return &terraform.Schemas{ Providers: map[addrs.Provider]*terraform.ProviderSchema{ diff --git a/vendor/github.com/hashicorp/terraform/command/login.go b/vendor/github.com/hashicorp/terraform/command/login.go index c249fd22..3a673b8e 100644 --- a/vendor/github.com/hashicorp/terraform/command/login.go +++ b/vendor/github.com/hashicorp/terraform/command/login.go @@ -4,8 +4,10 @@ import ( "context" "crypto/sha256" "encoding/base64" + "encoding/json" "errors" "fmt" + "io/ioutil" "log" "math/rand" "net" @@ -131,9 +133,9 @@ func (c *LoginCommand) Run(args []string) int { } // If login service is unavailable, check for a TFE v2 API as fallback - var service *url.URL + var tfeservice *url.URL if clientConfig == nil { - service, err = host.ServiceURL("tfe.v2") + tfeservice, err = host.ServiceURL("tfe.v2") switch err.(type) { case nil: // Success! @@ -184,6 +186,8 @@ func (c *LoginCommand) Run(args []string) int { oauthToken, tokenDiags = c.interactiveGetTokenByCode(hostname, credsCtx, clientConfig) case clientConfig.SupportedGrantTypes.Has(disco.OAuthOwnerPasswordGrant) && hostname == svchost.Hostname("app.terraform.io"): // The password grant type is allowed only for Terraform Cloud SaaS. + // Note this case is purely theoretical at this point, as TFC currently uses + // its own bespoke login protocol (tfe) oauthToken, tokenDiags = c.interactiveGetTokenByPassword(hostname, credsCtx, clientConfig) default: tokenDiags = tokenDiags.Append(tfdiags.Sourceless( @@ -195,8 +199,8 @@ func (c *LoginCommand) Run(args []string) int { if oauthToken != nil { token = svcauth.HostCredentialsToken(oauthToken.AccessToken) } - } else if service != nil { - token, tokenDiags = c.interactiveGetTokenByUI(hostname, credsCtx, service) + } else if tfeservice != nil { + token, tokenDiags = c.interactiveGetTokenByUI(hostname, credsCtx, tfeservice) } diags = diags.Append(tokenDiags) @@ -220,19 +224,104 @@ func (c *LoginCommand) Run(args []string) int { } c.Ui.Output("\n---------------------------------------------------------------------------------\n") - c.Ui.Output( - fmt.Sprintf( - c.Colorize().Color(strings.TrimSpace(` + if hostname == "app.terraform.io" { // Terraform Cloud + var motd struct { + Message string `json:"msg"` + Errors []interface{} `json:"errors"` + } + + // Throughout the entire process of fetching a MOTD from TFC, use a default + // message if the platform-provided message is unavailable for any reason - + // be it the service isn't provided, the request failed, or any sort of + // platform error returned. + + motdServiceURL, err := host.ServiceURL("motd.v1") + if err != nil { + c.logMOTDError(err) + c.outputDefaultTFCLoginSuccess() + return 0 + } + + req, err := http.NewRequest("GET", motdServiceURL.String(), nil) + if err != nil { + c.logMOTDError(err) + c.outputDefaultTFCLoginSuccess() + return 0 + } + + req.Header.Set("Authorization", "Bearer "+token.Token()) + + resp, err := httpclient.New().Do(req) + if err != nil { + c.logMOTDError(err) + c.outputDefaultTFCLoginSuccess() + return 0 + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + c.logMOTDError(err) + c.outputDefaultTFCLoginSuccess() + return 0 + } + + defer resp.Body.Close() + json.Unmarshal(body, &motd) + + if motd.Errors == nil && motd.Message != "" { + c.Ui.Output( + c.Colorize().Color(motd.Message), + ) + return 0 + } else { + c.logMOTDError(fmt.Errorf("platform responded with errors or an empty message")) + c.outputDefaultTFCLoginSuccess() + return 0 + } + } + + if tfeservice != nil { // Terraform Enterprise + c.outputDefaultTFELoginSuccess(dispHostname) + } else { + c.Ui.Output( + fmt.Sprintf( + c.Colorize().Color(strings.TrimSpace(` [green][bold]Success![reset] [bold]Terraform has obtained and saved an API token.[reset] The new API token will be used for any future Terraform command that must make authenticated requests to %s. +`)), + dispHostname, + ) + "\n", + ) + } + + return 0 +} + +func (c *LoginCommand) outputDefaultTFELoginSuccess(dispHostname string) { + c.Ui.Output( + fmt.Sprintf( + c.Colorize().Color(strings.TrimSpace(` +[green][bold]Success![reset] [bold]Logged in to Terraform Enterprise (%s)[reset] `)), dispHostname, ) + "\n", ) +} - return 0 +func (c *LoginCommand) outputDefaultTFCLoginSuccess() { + c.Ui.Output( + fmt.Sprintf( + c.Colorize().Color(strings.TrimSpace(` +[green][bold]Success![reset] [bold]Logged in to Terraform Cloud[reset] +`)), + ) + "\n", + ) +} + +func (c *LoginCommand) logMOTDError(err error) { + log.Printf("[TRACE] login: An error occurred attempting to fetch a message of the day for Terraform Cloud: %s", err) } // Help implements cli.Command. @@ -249,7 +338,7 @@ func (c *LoginCommand) Help() string { } helpText := fmt.Sprintf(` -Usage: terraform login [hostname] +Usage: terraform [global options] login [hostname] Retrieves an authentication token for the given hostname, if it supports automatic login, and saves it in a credentials file in your home directory. diff --git a/vendor/github.com/hashicorp/terraform/command/login_test.go b/vendor/github.com/hashicorp/terraform/command/login_test.go index d0450099..9b0e8de9 100644 --- a/vendor/github.com/hashicorp/terraform/command/login_test.go +++ b/vendor/github.com/hashicorp/terraform/command/login_test.go @@ -56,16 +56,6 @@ func TestLogin(t *testing.T) { svcs := disco.NewWithCredentialsSource(creds) svcs.SetUserAgent(httpclient.TerraformUserAgent(version.String())) - svcs.ForceHostServices(svchost.Hostname("app.terraform.io"), map[string]interface{}{ - "login.v1": map[string]interface{}{ - // On app.terraform.io we use password-based authorization. - // That's the only hostname that it's permitted for, so we can't - // use a fake hostname here. - "client": "terraformcli", - "token": s.URL + "/token", - "grant_types": []interface{}{"password"}, - }, - }) svcs.ForceHostServices(svchost.Hostname("example.com"), map[string]interface{}{ "login.v1": map[string]interface{}{ // For this fake hostname we'll use a conventional OAuth flow, @@ -86,9 +76,17 @@ func TestLogin(t *testing.T) { "scopes": []interface{}{"app1.full_access", "app2.read_only"}, }, }) + svcs.ForceHostServices(svchost.Hostname("app.terraform.io"), map[string]interface{}{ + // This represents Terraform Cloud, which does not yet support the + // login API, but does support its own bespoke tokens API. + "tfe.v2": ts.URL + "/api/v2", + "tfe.v2.1": ts.URL + "/api/v2", + "tfe.v2.2": ts.URL + "/api/v2", + "motd.v1": ts.URL + "/api/terraform/motd", + }) svcs.ForceHostServices(svchost.Hostname("tfe.acme.com"), map[string]interface{}{ // This represents a Terraform Enterprise instance which does not - // yet support the login API, but does support the TFE tokens API. + // yet support the login API, but does support its own bespoke tokens API. "tfe.v2": ts.URL + "/api/v2", "tfe.v2.1": ts.URL + "/api/v2", "tfe.v2.2": ts.URL + "/api/v2", @@ -109,13 +107,14 @@ func TestLogin(t *testing.T) { } } - t.Run("defaulting to app.terraform.io with password flow", loginTestCase(func(t *testing.T, c *LoginCommand, ui *cli.MockUi) { + t.Run("app.terraform.io (no login support)", loginTestCase(func(t *testing.T, c *LoginCommand, ui *cli.MockUi) { + // Enter "yes" at the consent prompt, then paste a token with some + // accidental whitespace. defer testInputMap(t, map[string]string{ - "approve": "yes", - "username": "foo", - "password": "bar", + "approve": "yes", + "token": " good-token ", })() - status := c.Run(nil) + status := c.Run([]string{"app.terraform.io"}) if status != 0 { t.Fatalf("unexpected error code %d\nstderr:\n%s", status, ui.ErrorWriter.String()) } @@ -128,6 +127,9 @@ func TestLogin(t *testing.T) { if got, want := creds.Token(), "good-token"; got != want { t.Errorf("wrong token %q; want %q", got, want) } + if got, want := ui.OutputWriter.String(), "Welcome to Terraform Cloud!"; !strings.Contains(got, want) { + t.Errorf("expected output to contain %q, but was:\n%s", want, got) + } })) t.Run("example.com with authorization code flow", loginTestCase(func(t *testing.T, c *LoginCommand, ui *cli.MockUi) { @@ -148,6 +150,10 @@ func TestLogin(t *testing.T) { if got, want := creds.Token(), "good-token"; got != want { t.Errorf("wrong token %q; want %q", got, want) } + + if got, want := ui.OutputWriter.String(), "Terraform has obtained and saved an API token."; !strings.Contains(got, want) { + t.Errorf("expected output to contain %q, but was:\n%s", want, got) + } })) t.Run("example.com results in no scopes", loginTestCase(func(t *testing.T, c *LoginCommand, ui *cli.MockUi) { @@ -179,6 +185,10 @@ func TestLogin(t *testing.T) { if got, want := creds.Token(), "good-token"; got != want { t.Errorf("wrong token %q; want %q", got, want) } + + if got, want := ui.OutputWriter.String(), "Terraform has obtained and saved an API token."; !strings.Contains(got, want) { + t.Errorf("expected output to contain %q, but was:\n%s", want, got) + } })) t.Run("with-scopes.example.com results in expected scopes", loginTestCase(func(t *testing.T, c *LoginCommand, ui *cli.MockUi) { @@ -216,6 +226,10 @@ func TestLogin(t *testing.T) { if got, want := creds.Token(), "good-token"; got != want { t.Errorf("wrong token %q; want %q", got, want) } + + if got, want := ui.OutputWriter.String(), "Logged in to Terraform Enterprise"; !strings.Contains(got, want) { + t.Errorf("expected output to contain %q, but was:\n%s", want, got) + } })) t.Run("TFE host without login support, incorrectly pasted token", loginTestCase(func(t *testing.T, c *LoginCommand, ui *cli.MockUi) { diff --git a/vendor/github.com/hashicorp/terraform/command/logout.go b/vendor/github.com/hashicorp/terraform/command/logout.go index 8e30be98..5f2af110 100644 --- a/vendor/github.com/hashicorp/terraform/command/logout.go +++ b/vendor/github.com/hashicorp/terraform/command/logout.go @@ -132,7 +132,7 @@ func (c *LogoutCommand) Help() string { } helpText := ` -Usage: terraform logout [hostname] +Usage: terraform [global options] logout [hostname] Removes locally-stored credentials for specified hostname. diff --git a/vendor/github.com/hashicorp/terraform/command/meta.go b/vendor/github.com/hashicorp/terraform/command/meta.go index 2eb2ba89..1cad1bff 100644 --- a/vendor/github.com/hashicorp/terraform/command/meta.go +++ b/vendor/github.com/hashicorp/terraform/command/meta.go @@ -15,22 +15,27 @@ import ( "time" plugin "github.com/hashicorp/go-plugin" + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclsyntax" "github.com/hashicorp/terraform-svchost/disco" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/backend/local" + "github.com/hashicorp/terraform/command/arguments" "github.com/hashicorp/terraform/command/format" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/command/webbrowser" "github.com/hashicorp/terraform/configs/configload" - "github.com/hashicorp/terraform/helper/experiment" - "github.com/hashicorp/terraform/helper/wrappedstreams" "github.com/hashicorp/terraform/internal/getproviders" + "github.com/hashicorp/terraform/internal/terminal" "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/provisioners" "github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/tfdiags" "github.com/mitchellh/cli" "github.com/mitchellh/colorstring" + + legacy "github.com/hashicorp/terraform/internal/legacy/terraform" ) // Meta are the meta-options that are available on all or most commands. @@ -50,13 +55,21 @@ type Meta struct { // for some reason. OriginalWorkingDir string + // Streams tracks the raw Stdout, Stderr, and Stdin handles along with + // some basic metadata about them, such as whether each is connected to + // a terminal, how wide the possible terminal is, etc. + // + // For historical reasons this might not be set in unit test code, and + // so functions working with this field must check if it's nil and + // do some default behavior instead if so, rather than panicking. + Streams *terminal.Streams + + View *views.View + Color bool // True if output should be colored GlobalPluginDirs []string // Additional paths to search for plugins Ui cli.Ui // Ui for output - // ExtraHooks are extra hooks to add to the context. - ExtraHooks []terraform.Hook - // Services provides access to remote endpoint information for // "terraform-native' services running at a specific user-facing hostname. Services *disco.Disco @@ -147,14 +160,15 @@ type Meta struct { configLoader *configload.Loader // backendState is the currently active backend state - backendState *terraform.BackendState + backendState *legacy.BackendState // Variables for the context (private) variableArgs rawFlags input bool // Targets for this context (private) - targets []addrs.Targetable + targets []addrs.Targetable + targetFlags []string // Internal fields color bool @@ -196,7 +210,6 @@ type Meta struct { stateOutPath string backupPath string parallelism int - provider string stateLock bool stateLockTimeout time.Duration forceInitCopy bool @@ -205,6 +218,10 @@ type Meta struct { // Used with the import command to allow import of state when no matching config exists. allowMissingConfig bool + + // Used with commands which write state to allow users to write remote + // state even if the remote and local Terraform versions don't match. + ignoreRemoteVersion bool } type testingOverrides struct { @@ -233,8 +250,14 @@ func (m *Meta) StateOutPath() string { // Colorize returns the colorization structure for a command. func (m *Meta) Colorize() *colorstring.Colorize { + colors := make(map[string]string) + for k, v := range colorstring.DefaultColors { + colors[k] = v + } + colors["purple"] = "38;5;57" + return &colorstring.Colorize{ - Colors: colorstring.DefaultColors, + Colors: colors, Disable: !m.color, Reset: true, } @@ -284,15 +307,42 @@ func (m *Meta) UIInput() terraform.UIInput { } } +// OutputColumns returns the number of columns that normal (non-error) UI +// output should be wrapped to fill. +// +// This is the column count to use if you'll be printing your message via +// the Output or Info methods of m.Ui. +func (m *Meta) OutputColumns() int { + if m.Streams == nil { + // A default for unit tests that don't populate Meta fully. + return 78 + } + return m.Streams.Stdout.Columns() +} + +// ErrorColumns returns the number of columns that error UI output should be +// wrapped to fill. +// +// This is the column count to use if you'll be printing your message via +// the Error or Warn methods of m.Ui. +func (m *Meta) ErrorColumns() int { + if m.Streams == nil { + // A default for unit tests that don't populate Meta fully. + return 78 + } + return m.Streams.Stderr.Columns() +} + // StdinPiped returns true if the input is piped. func (m *Meta) StdinPiped() bool { - fi, err := wrappedstreams.Stdin().Stat() - if err != nil { - // If there is an error, let's just say its not piped + if m.Streams == nil { + // If we don't have m.Streams populated then we're presumably in a unit + // test that doesn't properly populate Meta, so we'll just say the + // output _isn't_ piped because that's the common case and so most likely + // to be useful to a unit test. return false } - - return fi.Mode()&os.ModeNamedPipe != 0 + return !m.Streams.Stdin.IsTerminal() } // InterruptibleContext returns a context.Context that will be cancelled @@ -332,6 +382,9 @@ func (m *Meta) InterruptibleContext() (context.Context, context.CancelFunc) { // operation itself is unsuccessful. Use the "Result" field of the // returned operation object to recognize operation-level failure. func (m *Meta) RunOperation(b backend.Enhanced, opReq *backend.Operation) (*backend.RunningOperation, error) { + if opReq.View == nil { + panic("RunOperation called with nil View") + } if opReq.ConfigDir != "" { opReq.ConfigDir = m.normalizePath(opReq.ConfigDir) } @@ -348,14 +401,12 @@ func (m *Meta) RunOperation(b backend.Enhanced, opReq *backend.Operation) (*back op.Stop() // Notify the user - m.Ui.Output(outputInterrupt) + opReq.View.Interrupted() // Still get the result, since there is still one select { case <-m.ShutdownCh: - m.Ui.Error( - "Two interrupts received. Exiting immediately. Note that data\n" + - "loss may have occurred.") + opReq.View.FatalInterrupt() // cancel the operation completely op.Cancel() @@ -388,8 +439,6 @@ func (m *Meta) contextOpts() (*terraform.ContextOpts, error) { } var opts terraform.ContextOpts - opts.Hooks = []terraform.Hook{m.uiHook()} - opts.Hooks = append(opts.Hooks, m.ExtraHooks...) opts.Targets = m.targets opts.UIInput = m.UIInput() @@ -421,6 +470,28 @@ func (m *Meta) contextOpts() (*terraform.ContextOpts, error) { } opts.Providers = providerFactories opts.Provisioners = m.provisionerFactories() + + // Read the dependency locks so that they can be verified against the + // provider requirements in the configuration + lockedDependencies, diags := m.lockedDependencies() + + // If the locks file is invalid, we should fail early rather than + // ignore it. A missing locks file will return no error. + if diags.HasErrors() { + return nil, diags.Err() + } + opts.LockedDependencies = lockedDependencies + + // If any unmanaged providers or dev overrides are enabled, they must + // be listed in the context so that they can be ignored when verifying + // the locks against the configuration + opts.ProvidersInDevelopment = make(map[addrs.Provider]struct{}) + for provider := range m.UnmanagedProviders { + opts.ProvidersInDevelopment[provider] = struct{}{} + } + for provider := range m.ProviderDevOverrides { + opts.ProvidersInDevelopment[provider] = struct{}{} + } } opts.ProviderSHA256s = m.providerPluginsLock().Read() @@ -434,6 +505,7 @@ func (m *Meta) contextOpts() (*terraform.ContextOpts, error) { } // defaultFlagSet creates a default flag set for commands. +// See also command/arguments/default.go func (m *Meta) defaultFlagSet(n string) *flag.FlagSet { f := flag.NewFlagSet(n, flag.ContinueOnError) f.SetOutput(ioutil.Discard) @@ -444,13 +516,24 @@ func (m *Meta) defaultFlagSet(n string) *flag.FlagSet { return f } +// ignoreRemoteVersionFlagSet add the ignore-remote version flag to suppress +// the error when the configured Terraform version on the remote workspace +// does not match the local Terraform version. +func (m *Meta) ignoreRemoteVersionFlagSet(n string) *flag.FlagSet { + f := m.defaultFlagSet(n) + + f.BoolVar(&m.ignoreRemoteVersion, "ignore-remote-version", false, "continue even if remote and local Terraform versions are incompatible") + + return f +} + // extendedFlagSet adds custom flags that are mostly used by commands // that are used to run an operation like plan or apply. func (m *Meta) extendedFlagSet(n string) *flag.FlagSet { f := m.defaultFlagSet(n) f.BoolVar(&m.input, "input", true, "input") - f.Var((*FlagTargetSlice)(&m.targets), "target", "resource to target") + f.Var((*FlagStringSlice)(&m.targetFlags), "target", "resource to target") f.BoolVar(&m.compactWarnings, "compact-warnings", false, "use compact warnings") if m.variableArgs.items == nil { @@ -461,9 +544,6 @@ func (m *Meta) extendedFlagSet(n string) *flag.FlagSet { f.Var(varValues, "var", "variables") f.Var(varFiles, "var-file", "variable file") - // Experimental features - experiment.Flag(f) - // commands that bypass locking will supply their own flag on this var, // but set the initial meta value to true as a failsafe. m.stateLock = true @@ -471,9 +551,46 @@ func (m *Meta) extendedFlagSet(n string) *flag.FlagSet { return f } -// process will process the meta-parameters out of the arguments. This +// parseTargetFlags must be called for any commands supporting -target +// arguments. This method attempts to parse each -target flag into an +// addrs.Target, storing in the Meta.targets slice. +// +// If any flags cannot be parsed, we rewrap the first error diagnostic with a +// custom title to clarify the source of the error. The normal approach of +// directly returning the diags from HCL or the addrs package results in +// confusing incorrect "source" results when presented. +func (m *Meta) parseTargetFlags() tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + m.targets = nil + for _, tf := range m.targetFlags { + traversal, syntaxDiags := hclsyntax.ParseTraversalAbs([]byte(tf), "", hcl.Pos{Line: 1, Column: 1}) + if syntaxDiags.HasErrors() { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + fmt.Sprintf("Invalid target %q", tf), + syntaxDiags[0].Detail, + )) + continue + } + + target, targetDiags := addrs.ParseTarget(traversal) + if targetDiags.HasErrors() { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + fmt.Sprintf("Invalid target %q", tf), + targetDiags[0].Description().Detail, + )) + continue + } + + m.targets = append(m.targets, target.Subject) + } + return diags +} + +// process will process any -no-color entries out of the arguments. This // will potentially modify the args in-place. It will return the resulting -// slice. +// slice, and update the Meta and Ui. func (m *Meta) process(args []string) []string { // We do this so that we retain the ability to technically call // process multiple times, even if we have no plans to do so @@ -507,15 +624,21 @@ func (m *Meta) process(args []string) []string { }, } + // Reconfigure the view. This is necessary for commands which use both + // views.View and cli.Ui during the migration phase. + if m.View != nil { + m.View.Configure(&arguments.View{ + CompactWarnings: m.compactWarnings, + NoColor: !m.Color, + }) + } + return args } // uiHook returns the UiHook to use with the context. -func (m *Meta) uiHook() *UiHook { - return &UiHook{ - Colorize: m.Colorize(), - Ui: m.Ui, - } +func (m *Meta) uiHook() *views.UiHook { + return views.NewUiHook(m.View) } // confirm asks a yes/no confirmation. @@ -559,6 +682,8 @@ func (m *Meta) showDiagnostics(vals ...interface{}) { return } + outputWidth := m.ErrorColumns() + diags = diags.ConsolidateWarnings(1) // Since warning messages are generally competing @@ -584,11 +709,13 @@ func (m *Meta) showDiagnostics(vals ...interface{}) { } for _, diag := range diags { - // TODO: Actually measure the terminal width and pass it here. - // For now, we don't have easy access to the writer that - // ui.Error (etc) are writing to and thus can't interrogate - // to see if it's a terminal and what size it is. - msg := format.Diagnostic(diag, m.configSources(), m.Colorize(), 78) + var msg string + if m.Color { + msg = format.Diagnostic(diag, m.configSources(), m.Colorize(), outputWidth) + } else { + msg = format.DiagnosticPlain(diag, m.configSources(), outputWidth) + } + switch diag.Severity() { case tfdiags.Error: m.Ui.Error(msg) @@ -600,49 +727,6 @@ func (m *Meta) showDiagnostics(vals ...interface{}) { } } -// outputShadowError outputs the error from ctx.ShadowError. If the -// error is nil then nothing happens. If output is false then it isn't -// outputted to the user (you can define logic to guard against outputting). -func (m *Meta) outputShadowError(err error, output bool) bool { - // Do nothing if no error - if err == nil { - return false - } - - // If not outputting, do nothing - if !output { - return false - } - - // Write the shadow error output to a file - path := fmt.Sprintf("terraform-error-%d.log", time.Now().UTC().Unix()) - if err := ioutil.WriteFile(path, []byte(err.Error()), 0644); err != nil { - // If there is an error writing it, just let it go - log.Printf("[ERROR] Error writing shadow error: %s", err) - return false - } - - // Output! - m.Ui.Output(m.Colorize().Color(fmt.Sprintf( - "[reset][bold][yellow]\nExperimental feature failure! Please report a bug.\n\n"+ - "This is not an error. Your Terraform operation completed successfully.\n"+ - "Your real infrastructure is unaffected by this message.\n\n"+ - "[reset][yellow]While running, Terraform sometimes tests experimental features in the\n"+ - "background. These features cannot affect real state and never touch\n"+ - "real infrastructure. If the features work properly, you see nothing.\n"+ - "If the features fail, this message appears.\n\n"+ - "You can report an issue at: https://github.com/hashicorp/terraform/issues\n\n"+ - "The failure was written to %q. Please\n"+ - "double check this file contains no sensitive information and report\n"+ - "it with your issue.\n\n"+ - "This is not an error. Your terraform operation completed successfully\n"+ - "and your real infrastructure is unaffected by this message.", - path, - ))) - - return true -} - // WorkspaceNameEnvVar is the name of the environment variable that can be used // to set the name of the Terraform workspace, overriding the workspace chosen // by `terraform workspace select`. @@ -651,14 +735,14 @@ func (m *Meta) outputShadowError(err error, output bool) bool { // and `terraform workspace delete`. const WorkspaceNameEnvVar = "TF_WORKSPACE" -var invalidWorkspaceNameEnvVar = fmt.Errorf("Invalid workspace name set using %s", WorkspaceNameEnvVar) +var errInvalidWorkspaceNameEnvVar = fmt.Errorf("Invalid workspace name set using %s", WorkspaceNameEnvVar) // Workspace returns the name of the currently configured workspace, corresponding // to the desired named state. func (m *Meta) Workspace() (string, error) { current, overridden := m.WorkspaceOverridden() if overridden && !validWorkspaceName(current) { - return "", invalidWorkspaceNameEnvVar + return "", errInvalidWorkspaceNameEnvVar } return current, nil } @@ -705,3 +789,14 @@ func isAutoVarFile(path string) bool { return strings.HasSuffix(path, ".auto.tfvars") || strings.HasSuffix(path, ".auto.tfvars.json") } + +// FIXME: as an interim refactoring step, we apply the contents of the state +// arguments directly to the Meta object. Future work would ideally update the +// code paths which use these arguments to be passed them directly for clarity. +func (m *Meta) applyStateArguments(args *arguments.State) { + m.stateLock = args.Lock + m.stateLockTimeout = args.LockTimeout + m.statePath = args.StatePath + m.stateOutPath = args.StateOutPath + m.backupPath = args.BackupPath +} diff --git a/vendor/github.com/hashicorp/terraform/command/meta_backend.go b/vendor/github.com/hashicorp/terraform/command/meta_backend.go index 61c645aa..3aed8432 100644 --- a/vendor/github.com/hashicorp/terraform/command/meta_backend.go +++ b/vendor/github.com/hashicorp/terraform/command/meta_backend.go @@ -17,7 +17,10 @@ import ( "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/terraform/backend" + remoteBackend "github.com/hashicorp/terraform/backend/remote" + "github.com/hashicorp/terraform/command/arguments" "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/states/statemgr" @@ -28,6 +31,7 @@ import ( backendInit "github.com/hashicorp/terraform/backend/init" backendLocal "github.com/hashicorp/terraform/backend/local" + legacy "github.com/hashicorp/terraform/internal/legacy/terraform" ) // BackendOpts are the options used to initialize a backend.Backend. @@ -100,7 +104,7 @@ func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, tfdiags.Diagnostics log.Printf("[TRACE] Meta.Backend: instantiated backend of type %T", b) } - // Setup the CLI opts we pass into backends that support it. + // Set up the CLI opts we pass into backends that support it. cliOpts, err := m.backendCLIOpts() if err != nil { diags = diags.Append(err) @@ -159,7 +163,7 @@ func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, tfdiags.Diagnostics // with inside backendFromConfig, because we still need that codepath // to be able to recognize the lack of a config as distinct from // explicitly setting local until we do some more refactoring here. - m.backendState = &terraform.BackendState{ + m.backendState = &legacy.BackendState{ Type: "local", ConfigRaw: json.RawMessage("{}"), } @@ -306,7 +310,7 @@ func (m *Meta) backendCLIOpts() (*backend.CLIOpts, error) { return &backend.CLIOpts{ CLI: m.Ui, CLIColor: m.Colorize(), - ShowDiagnostics: m.showDiagnostics, + Streams: m.Streams, StatePath: m.statePath, StateOutPath: m.stateOutPath, StateBackupPath: m.backupPath, @@ -339,15 +343,20 @@ func (m *Meta) Operation(b backend.Backend) *backend.Operation { panic(fmt.Sprintf("failed to encode backend configuration for plan: %s", err)) } + stateLocker := clistate.NewNoopLocker() + if m.stateLock { + view := views.NewStateLocker(arguments.ViewHuman, m.View) + stateLocker = clistate.NewLocker(m.stateLockTimeout, view) + } + return &backend.Operation{ - PlanOutBackend: planOutBackend, - Parallelism: m.parallelism, - Targets: m.targets, - UIIn: m.UIInput(), - UIOut: m.Ui, - Workspace: workspace, - LockState: m.stateLock, - StateLockTimeout: m.stateLockTimeout, + PlanOutBackend: planOutBackend, + Parallelism: m.parallelism, + Targets: m.targets, + UIIn: m.UIInput(), + UIOut: m.Ui, + Workspace: workspace, + StateLocker: stateLocker, } } @@ -460,7 +469,7 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di s := sMgr.State() if s == nil { log.Printf("[TRACE] Meta.Backend: backend has not previously been initialized in this working directory") - s = terraform.NewState() + s = legacy.NewState() } else if s.Backend != nil { log.Printf("[TRACE] Meta.Backend: working directory was previously initialized for %q backend", s.Backend.Type) } else { @@ -800,12 +809,13 @@ func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.Local } if m.stateLock { - stateLocker := clistate.NewLocker(context.Background(), m.stateLockTimeout, m.Ui, m.Colorize()) + view := views.NewStateLocker(arguments.ViewHuman, m.View) + stateLocker := clistate.NewLocker(m.stateLockTimeout, view) if err := stateLocker.Lock(sMgr, "backend from plan"); err != nil { diags = diags.Append(fmt.Errorf("Error locking state: %s", err)) return nil, diags } - defer stateLocker.Unlock(nil) + defer stateLocker.Unlock() } configJSON, err := ctyjson.Marshal(configVal, b.ConfigSchema().ImpliedType()) @@ -817,9 +827,9 @@ func (m *Meta) backend_C_r_s(c *configs.Backend, cHash int, sMgr *clistate.Local // Store the metadata in our saved state location s := sMgr.State() if s == nil { - s = terraform.NewState() + s = legacy.NewState() } - s.Backend = &terraform.BackendState{ + s.Backend = &legacy.BackendState{ Type: c.Type, ConfigRaw: json.RawMessage(configJSON), Hash: uint64(cHash), @@ -884,12 +894,13 @@ func (m *Meta) backend_C_r_S_changed(c *configs.Backend, cHash int, sMgr *clista } if m.stateLock { - stateLocker := clistate.NewLocker(context.Background(), m.stateLockTimeout, m.Ui, m.Colorize()) + view := views.NewStateLocker(arguments.ViewHuman, m.View) + stateLocker := clistate.NewLocker(m.stateLockTimeout, view) if err := stateLocker.Lock(sMgr, "backend from plan"); err != nil { diags = diags.Append(fmt.Errorf("Error locking state: %s", err)) return nil, diags } - defer stateLocker.Unlock(nil) + defer stateLocker.Unlock() } configJSON, err := ctyjson.Marshal(configVal, b.ConfigSchema().ImpliedType()) @@ -901,9 +912,9 @@ func (m *Meta) backend_C_r_S_changed(c *configs.Backend, cHash int, sMgr *clista // Update the backend state s = sMgr.State() if s == nil { - s = terraform.NewState() + s = legacy.NewState() } - s.Backend = &terraform.BackendState{ + s.Backend = &legacy.BackendState{ Type: c.Type, ConfigRaw: json.RawMessage(configJSON), Hash: uint64(cHash), @@ -995,7 +1006,7 @@ func (m *Meta) backend_C_r_S_unchanged(c *configs.Backend, cHash int, sMgr *clis // this function will conservatively assume that migration is required, // expecting that the migration code will subsequently deal with the same // errors. -func (m *Meta) backendConfigNeedsMigration(c *configs.Backend, s *terraform.BackendState) bool { +func (m *Meta) backendConfigNeedsMigration(c *configs.Backend, s *legacy.BackendState) bool { if s == nil || s.Empty() { log.Print("[TRACE] backendConfigNeedsMigration: no cached config, so migration is required") return true @@ -1075,13 +1086,13 @@ func (m *Meta) backendInitFromConfig(c *configs.Backend) (backend.Backend, cty.V } newVal, validateDiags := b.PrepareConfig(configVal) - diags = diags.Append(validateDiags.InConfigBody(c.Config)) + diags = diags.Append(validateDiags.InConfigBody(c.Config, "")) if validateDiags.HasErrors() { return nil, cty.NilVal, diags } configureDiags := b.Configure(newVal) - diags = diags.Append(configureDiags.InConfigBody(c.Config)) + diags = diags.Append(configureDiags.InConfigBody(c.Config, "")) return b, configVal, diags } @@ -1091,6 +1102,38 @@ func (m *Meta) backendInitRequired(reason string) { "[reset]"+strings.TrimSpace(errBackendInit)+"\n", reason))) } +// Helper method to ignore remote backend version conflicts. Only call this +// for commands which cannot accidentally upgrade remote state files. +func (m *Meta) ignoreRemoteBackendVersionConflict(b backend.Backend) { + if rb, ok := b.(*remoteBackend.Remote); ok { + rb.IgnoreVersionConflict() + } +} + +// Helper method to check the local Terraform version against the configured +// version in the remote workspace, returning diagnostics if they conflict. +func (m *Meta) remoteBackendVersionCheck(b backend.Backend, workspace string) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + if rb, ok := b.(*remoteBackend.Remote); ok { + // Allow user override based on command-line flag + if m.ignoreRemoteVersion { + rb.IgnoreVersionConflict() + } + // If the override is set, this check will return a warning instead of + // an error + versionDiags := rb.VerifyWorkspaceTerraformVersion(workspace) + diags = diags.Append(versionDiags) + // If there are no errors resulting from this check, we do not need to + // check again + if !diags.HasErrors() { + rb.IgnoreVersionConflict() + } + } + + return diags +} + //------------------------------------------------------------------- // Output constants and initialization code //------------------------------------------------------------------- @@ -1171,7 +1214,7 @@ Terraform configuration you're using is using a custom configuration for the Terraform backend. Changes to backend configurations require reinitialization. This allows -Terraform to setup the new configuration, copy existing state, etc. This is +Terraform to set up the new configuration, copy existing state, etc. This is only done during "terraform init". Please run that command now then try again. If the change reason above is incorrect, please verify your configuration @@ -1211,12 +1254,3 @@ const successBackendSet = ` Successfully configured the backend %q! Terraform will automatically use this backend unless the backend configuration changes. ` - -const errBackendLegacy = ` -This working directory is configured to use the legacy remote state features -from Terraform 0.8 or earlier. Remote state changed significantly in Terraform -0.9 and the automatic upgrade mechanism has now been removed. - -To upgrade, please first use Terraform v0.11 to complete the upgrade steps: - https://www.terraform.io/docs/backends/legacy-0-8.html -` diff --git a/vendor/github.com/hashicorp/terraform/command/meta_backend_migrate.go b/vendor/github.com/hashicorp/terraform/command/meta_backend_migrate.go index 6283bd99..85644508 100644 --- a/vendor/github.com/hashicorp/terraform/command/meta_backend_migrate.go +++ b/vendor/github.com/hashicorp/terraform/command/meta_backend_migrate.go @@ -12,7 +12,9 @@ import ( "strings" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/command/arguments" "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/states/statemgr" "github.com/hashicorp/terraform/terraform" @@ -65,11 +67,24 @@ func (m *Meta) backendMigrateState(opts *backendMigrateOpts) error { errMigrateLoadStates), opts.TwoType, err) } - // Setup defaults + // Set up defaults opts.oneEnv = backend.DefaultStateName opts.twoEnv = backend.DefaultStateName opts.force = m.forceInitCopy + // Disregard remote Terraform version for the state source backend. If it's a + // Terraform Cloud remote backend, we don't care about the remote version, + // as we are migrating away and will not break a remote workspace. + m.ignoreRemoteBackendVersionConflict(opts.One) + + // Check the remote Terraform version for the state destination backend. If + // it's a Terraform Cloud remote backend, we want to ensure that we don't + // break the workspace by uploading an incompatible state file. + diags := m.remoteBackendVersionCheck(opts.Two, opts.twoEnv) + if diags.HasErrors() { + return diags.Err() + } + // Determine migration behavior based on whether the source/destination // supports multi-state. switch { @@ -312,17 +327,20 @@ func (m *Meta) backendMigrateState_s_s(opts *backendMigrateOpts) error { if m.stateLock { lockCtx := context.Background() - lockerOne := clistate.NewLocker(lockCtx, m.stateLockTimeout, m.Ui, m.Colorize()) - if err := lockerOne.Lock(stateOne, "migration source state"); err != nil { - return fmt.Errorf("Error locking source state: %s", err) + view := views.NewStateLocker(arguments.ViewHuman, m.View) + locker := clistate.NewLocker(m.stateLockTimeout, view) + + lockerOne := locker.WithContext(lockCtx) + if diags := lockerOne.Lock(stateOne, "migration source state"); diags.HasErrors() { + return diags.Err() } - defer lockerOne.Unlock(nil) + defer lockerOne.Unlock() - lockerTwo := clistate.NewLocker(lockCtx, m.stateLockTimeout, m.Ui, m.Colorize()) - if err := lockerTwo.Lock(stateTwo, "migration destination state"); err != nil { - return fmt.Errorf("Error locking destination state: %s", err) + lockerTwo := locker.WithContext(lockCtx) + if diags := lockerTwo.Lock(stateTwo, "migration destination state"); diags.HasErrors() { + return diags.Err() } - defer lockerTwo.Unlock(nil) + defer lockerTwo.Unlock() // We now own a lock, so double check that we have the version // corresponding to the lock. diff --git a/vendor/github.com/hashicorp/terraform/command/meta_backend_test.go b/vendor/github.com/hashicorp/terraform/command/meta_backend_test.go index 07d26881..cbd642ed 100644 --- a/vendor/github.com/hashicorp/terraform/command/meta_backend_test.go +++ b/vendor/github.com/hashicorp/terraform/command/meta_backend_test.go @@ -1541,7 +1541,7 @@ func TestMetaBackend_planLocalStatePath(t *testing.T) { defer testChdir(t, td)() original := testState() - mark := markStateForMatching(original, "hello") + markStateForMatching(original, "hello") backendConfigBlock := cty.ObjectVal(map[string]cty.Value{ "path": cty.NullVal(cty.String), @@ -1607,7 +1607,7 @@ func TestMetaBackend_planLocalStatePath(t *testing.T) { // Write some state state = states.NewState() - mark = markStateForMatching(state, "changing") + mark := markStateForMatching(state, "changing") s.WriteState(state) if err := s.PersistState(); err != nil { @@ -1876,6 +1876,8 @@ func TestBackendFromState(t *testing.T) { func testMetaBackend(t *testing.T, args []string) *Meta { var m Meta m.Ui = new(cli.MockUi) + view, _ := testView(t) + m.View = view m.process(args) f := m.extendedFlagSet("test") if err := f.Parse(args); err != nil { diff --git a/vendor/github.com/hashicorp/terraform/command/meta_config.go b/vendor/github.com/hashicorp/terraform/command/meta_config.go index d5f65355..51ca2744 100644 --- a/vendor/github.com/hashicorp/terraform/command/meta_config.go +++ b/vendor/github.com/hashicorp/terraform/command/meta_config.go @@ -329,6 +329,9 @@ func (m *Meta) initConfigLoader() (*configload.Loader, error) { return nil, err } m.configLoader = loader + if m.View != nil { + m.View.SetConfigSources(loader.Sources) + } } return m.configLoader, nil } diff --git a/vendor/github.com/hashicorp/terraform/command/meta_providers.go b/vendor/github.com/hashicorp/terraform/command/meta_providers.go index 8cc111fb..8050e439 100644 --- a/vendor/github.com/hashicorp/terraform/command/meta_providers.go +++ b/vendor/github.com/hashicorp/terraform/command/meta_providers.go @@ -15,8 +15,10 @@ import ( terraformProvider "github.com/hashicorp/terraform/builtin/providers/terraform" "github.com/hashicorp/terraform/internal/getproviders" "github.com/hashicorp/terraform/internal/logging" + "github.com/hashicorp/terraform/internal/moduletest" "github.com/hashicorp/terraform/internal/providercache" tfplugin "github.com/hashicorp/terraform/plugin" + tfplugin6 "github.com/hashicorp/terraform/plugin6" "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/tfdiags" ) @@ -128,31 +130,6 @@ func (m *Meta) providerGlobalCacheDir() *providercache.Dir { return providercache.NewDir(dir) } -// providerLegacyCacheDir returns an object representing the former location -// of the local cache directory from Terraform 0.13 and earlier. -// -// This is no longer viable for use as a real cache directory because some -// incorrect documentation called for Terraform Cloud users to use it as if it -// were an implied local filesystem mirror directory. Therefore we now use it -// only to generate some hopefully-helpful migration guidance during -// "terraform init" for anyone who _was_ trying to use it as a local filesystem -// mirror directory. -// -// providerLegacyCacheDir returns nil if the legacy cache directory isn't -// present or isn't a directory, so that callers can more easily skip over -// any backward compatibility behavior that applies only when the directory -// is present. -// -// Callers must use the resulting object in a read-only mode only. Don't -// install any new providers into this directory. -func (m *Meta) providerLegacyCacheDir() *providercache.Dir { - dir := filepath.Join(m.DataDir(), "plugins") - if info, err := os.Stat(dir); err != nil || !info.IsDir() { - return nil - } - return providercache.NewDir(dir) -} - // providerInstallSource returns an object that knows how to consult one or // more external sources to determine the availability of and package // locations for versions of Terraform providers that are available for @@ -177,8 +154,35 @@ func (m *Meta) providerInstallSource() getproviders.Source { return m.ProviderSource } -// providerDevOverrideWarnings returns a diagnostics that contains at least -// one warning if and only if there is at least one provider development +// providerDevOverrideInitWarnings returns a diagnostics that contains at +// least one warning if and only if there is at least one provider development +// override in effect. If not, the result is always empty. The result never +// contains error diagnostics. +// +// The init command can use this to include a warning that the results +// may differ from what's expected due to the development overrides. For +// other commands, providerDevOverrideRuntimeWarnings should be used. +func (m *Meta) providerDevOverrideInitWarnings() tfdiags.Diagnostics { + if len(m.ProviderDevOverrides) == 0 { + return nil + } + var detailMsg strings.Builder + detailMsg.WriteString("The following provider development overrides are set in the CLI configuration:\n") + for addr, path := range m.ProviderDevOverrides { + detailMsg.WriteString(fmt.Sprintf(" - %s in %s\n", addr.ForDisplay(), path)) + } + detailMsg.WriteString("\nSkip terraform init when using provider development overrides. It is not necessary and may error unexpectedly.") + return tfdiags.Diagnostics{ + tfdiags.Sourceless( + tfdiags.Warning, + "Provider development overrides are in effect", + detailMsg.String(), + ), + } +} + +// providerDevOverrideRuntimeWarnings returns a diagnostics that contains at +// least one warning if and only if there is at least one provider development // override in effect. If not, the result is always empty. The result never // contains error diagnostics. // @@ -187,7 +191,10 @@ func (m *Meta) providerInstallSource() getproviders.Source { // not necessary to bother the user with this warning on every command, but // it's helpful to return it on commands that have externally-visible side // effects and on commands that are used to verify conformance to schemas. -func (m *Meta) providerDevOverrideWarnings() tfdiags.Diagnostics { +// +// See providerDevOverrideInitWarnings for warnings specific to the init +// command. +func (m *Meta) providerDevOverrideRuntimeWarnings() tfdiags.Diagnostics { if len(m.ProviderDevOverrides) == 0 { return nil } @@ -320,6 +327,9 @@ func (m *Meta) internalProviders() map[string]providers.Factory { "terraform": func() (providers.Interface, error) { return terraformProvider.NewProvider(), nil }, + "test": func() (providers.Interface, error) { + return moduletest.NewProvider(), nil + }, } } @@ -335,7 +345,7 @@ func providerFactory(meta *providercache.CachedProvider) providers.Factory { config := &plugin.ClientConfig{ HandshakeConfig: tfplugin.Handshake, - Logger: logging.NewHCLogger("plugin"), + Logger: logging.NewProviderLogger(""), AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, Managed: true, Cmd: exec.Command(execFile), @@ -355,10 +365,19 @@ func providerFactory(meta *providercache.CachedProvider) providers.Factory { } // store the client so that the plugin can kill the child process - p := raw.(*tfplugin.GRPCProvider) - p.PluginClient = client - - return p, nil + protoVer := client.NegotiatedVersion() + switch protoVer { + case 5: + p := raw.(*tfplugin.GRPCProvider) + p.PluginClient = client + return p, nil + case 6: + p := raw.(*tfplugin6.GRPCProvider) + p.PluginClient = client + return p, nil + default: + panic("unsupported protocol version") + } } } @@ -383,7 +402,7 @@ func unmanagedProviderFactory(provider addrs.Provider, reattach *plugin.Reattach config := &plugin.ClientConfig{ HandshakeConfig: tfplugin.Handshake, - Logger: logging.NewHCLogger("unmanaged-plugin"), + Logger: logging.NewProviderLogger("unmanaged."), AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, Managed: false, Reattach: reattach, diff --git a/vendor/github.com/hashicorp/terraform/command/meta_test.go b/vendor/github.com/hashicorp/terraform/command/meta_test.go index 0de9cf79..fe212d53 100644 --- a/vendor/github.com/hashicorp/terraform/command/meta_test.go +++ b/vendor/github.com/hashicorp/terraform/command/meta_test.go @@ -236,7 +236,7 @@ func TestMeta_Workspace_override(t *testing.T) { }, "invalid name": { "", - invalidWorkspaceNameEnvVar, + errInvalidWorkspaceNameEnvVar, }, } diff --git a/vendor/github.com/hashicorp/terraform/command/output.go b/vendor/github.com/hashicorp/terraform/command/output.go index e389c458..c228bf37 100644 --- a/vendor/github.com/hashicorp/terraform/command/output.go +++ b/vendor/github.com/hashicorp/terraform/command/output.go @@ -1,14 +1,11 @@ package command import ( - "encoding/json" "fmt" "strings" - ctyjson "github.com/zclconf/go-cty/cty/json" - - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/repl" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/tfdiags" ) @@ -19,81 +16,75 @@ type OutputCommand struct { Meta } -func (c *OutputCommand) Run(args []string) int { - args = c.Meta.process(args) - var module, statePath string - var jsonOutput bool - cmdFlags := c.Meta.defaultFlagSet("output") - cmdFlags.BoolVar(&jsonOutput, "json", false, "json") - cmdFlags.StringVar(&statePath, "state", "", "path") - cmdFlags.StringVar(&module, "module", "", "module") - cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } - if err := cmdFlags.Parse(args); err != nil { - c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s\n", err.Error())) +func (c *OutputCommand) Run(rawArgs []string) int { + // Parse and apply global view arguments + common, rawArgs := arguments.ParseView(rawArgs) + c.View.Configure(common) + + // Parse and validate flags + args, diags := arguments.ParseOutput(rawArgs) + if diags.HasErrors() { + c.View.Diagnostics(diags) + c.View.HelpPrompt("output") return 1 } - args = cmdFlags.Args() - if len(args) > 1 { - c.Ui.Error( - "The output command expects exactly one argument with the name\n" + - "of an output variable or no arguments to show all outputs.\n") - cmdFlags.Usage() + view := views.NewOutput(args.ViewType, c.View) + + // Fetch data from state + outputs, diags := c.Outputs(args.StatePath) + if diags.HasErrors() { + view.Diagnostics(diags) return 1 } - name := "" - if len(args) > 0 { - name = args[0] + // Render the view + viewDiags := view.Output(args.Name, outputs) + diags = diags.Append(viewDiags) + + view.Diagnostics(diags) + + if diags.HasErrors() { + return 1 } + return 0 +} + +func (c *OutputCommand) Outputs(statePath string) (map[string]*states.OutputValue, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + + // Allow state path override if statePath != "" { c.Meta.statePath = statePath } - var diags tfdiags.Diagnostics - // Load the backend b, backendDiags := c.Backend(nil) diags = diags.Append(backendDiags) - if backendDiags.HasErrors() { - c.showDiagnostics(diags) - return 1 + if diags.HasErrors() { + return nil, diags } + // This is a read-only command + c.ignoreRemoteBackendVersionConflict(b) + env, err := c.Workspace() if err != nil { - c.Ui.Error(fmt.Sprintf("Error selecting workspace: %s", err)) - return 1 + diags = diags.Append(fmt.Errorf("Error selecting workspace: %s", err)) + return nil, diags } // Get the state stateStore, err := b.StateMgr(env) if err != nil { - c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err)) - return 1 + diags = diags.Append(fmt.Errorf("Failed to load state: %s", err)) + return nil, diags } if err := stateStore.RefreshState(); err != nil { - c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err)) - return 1 - } - - moduleAddr := addrs.RootModuleInstance - if module != "" { - // This option was supported prior to 0.12.0, but no longer supported - // because we only persist the root module outputs in state. - // (We could perhaps re-introduce this by doing an eval walk here to - // repopulate them, similar to how "terraform console" does it, but - // that requires more thought since it would imply this command - // supporting remote operations, which is a big change.) - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Unsupported option", - "The -module option is no longer supported since Terraform 0.12, because now only root outputs are persisted in the state.", - )) - c.showDiagnostics(diags) - return 1 + diags = diags.Append(fmt.Errorf("Failed to load state: %s", err)) + return nil, diags } state := stateStore.State() @@ -101,107 +92,12 @@ func (c *OutputCommand) Run(args []string) int { state = states.NewState() } - mod := state.Module(moduleAddr) - if mod == nil { - c.Ui.Error(fmt.Sprintf( - "The module %s could not be found. There is nothing to output.", - module)) - return 1 - } - - if !jsonOutput && (state.Empty() || len(mod.OutputValues) == 0) { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Warning, - "No outputs found", - "The state file either has no outputs defined, or all the defined "+ - "outputs are empty. Please define an output in your configuration "+ - "with the `output` keyword and run `terraform refresh` for it to "+ - "become available. If you are using interpolation, please verify "+ - "the interpolated value is not empty. You can use the "+ - "`terraform console` command to assist.", - )) - c.showDiagnostics(diags) - return 0 - } - - if name == "" { - if jsonOutput { - // Due to a historical accident, the switch from state version 2 to - // 3 caused our JSON output here to be the full metadata about the - // outputs rather than just the output values themselves as we'd - // show in the single value case. We must now maintain that behavior - // for compatibility, so this is an emulation of the JSON - // serialization of outputs used in state format version 3. - type OutputMeta struct { - Sensitive bool `json:"sensitive"` - Type json.RawMessage `json:"type"` - Value json.RawMessage `json:"value"` - } - outputs := map[string]OutputMeta{} - - for n, os := range mod.OutputValues { - jsonVal, err := ctyjson.Marshal(os.Value, os.Value.Type()) - if err != nil { - diags = diags.Append(err) - c.showDiagnostics(diags) - return 1 - } - jsonType, err := ctyjson.MarshalType(os.Value.Type()) - if err != nil { - diags = diags.Append(err) - c.showDiagnostics(diags) - return 1 - } - outputs[n] = OutputMeta{ - Sensitive: os.Sensitive, - Type: json.RawMessage(jsonType), - Value: json.RawMessage(jsonVal), - } - } - - jsonOutputs, err := json.MarshalIndent(outputs, "", " ") - if err != nil { - diags = diags.Append(err) - c.showDiagnostics(diags) - return 1 - } - c.Ui.Output(string(jsonOutputs)) - return 0 - } else { - c.Ui.Output(outputsAsString(state, moduleAddr, false)) - return 0 - } - } - - os, ok := mod.OutputValues[name] - if !ok { - c.Ui.Error(fmt.Sprintf( - "The output variable requested could not be found in the state\n" + - "file. If you recently added this to your configuration, be\n" + - "sure to run `terraform apply`, since the state won't be updated\n" + - "with new output variables until that command is run.")) - return 1 - } - v := os.Value - - if jsonOutput { - jsonOutput, err := ctyjson.Marshal(v, v.Type()) - if err != nil { - return 1 - } - - c.Ui.Output(string(jsonOutput)) - } else { - result := repl.FormatValue(v, 0) - c.Ui.Output(result) - } - - return 0 + return state.RootModule().OutputValues, nil } func (c *OutputCommand) Help() string { helpText := ` -Usage: terraform output [options] [NAME] +Usage: terraform [global options] output [options] [NAME] Reads an output variable from a Terraform state file and prints the value. With no additional arguments, output will display all @@ -216,12 +112,16 @@ Options: -no-color If specified, output won't contain any color. -json If specified, machine readable output will be - printed in JSON format + printed in JSON format. + -raw For value types that can be automatically + converted to a string, will print the raw + string directly, rather than a human-oriented + representation of the value. ` return strings.TrimSpace(helpText) } func (c *OutputCommand) Synopsis() string { - return "Read an output from a state file" + return "Show output values from your root module" } diff --git a/vendor/github.com/hashicorp/terraform/command/output_test.go b/vendor/github.com/hashicorp/terraform/command/output_test.go index 4ca121aa..22e8afe2 100644 --- a/vendor/github.com/hashicorp/terraform/command/output_test.go +++ b/vendor/github.com/hashicorp/terraform/command/output_test.go @@ -6,7 +6,6 @@ import ( "strings" "testing" - "github.com/mitchellh/cli" "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/addrs" @@ -24,11 +23,11 @@ func TestOutput(t *testing.T) { statePath := testStateFile(t, originalState) - ui := new(cli.MockUi) + view, done := testView(t) c := &OutputCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), - Ui: ui, + View: view, }, } @@ -36,66 +35,18 @@ func TestOutput(t *testing.T) { "-state", statePath, "foo", } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: \n%s", output.Stderr()) } - actual := strings.TrimSpace(ui.OutputWriter.String()) + actual := strings.TrimSpace(output.Stdout()) if actual != `"bar"` { t.Fatalf("bad: %#v", actual) } } -func TestOutput_nestedListAndMap(t *testing.T) { - originalState := states.BuildState(func(s *states.SyncState) { - s.SetOutputValue( - addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance), - cty.ListVal([]cty.Value{ - cty.MapVal(map[string]cty.Value{ - "key": cty.StringVal("value"), - "key2": cty.StringVal("value2"), - }), - cty.MapVal(map[string]cty.Value{ - "key": cty.StringVal("value"), - }), - }), - false, - ) - }) - statePath := testStateFile(t, originalState) - - ui := new(cli.MockUi) - c := &OutputCommand{ - Meta: Meta{ - testingOverrides: metaOverridesForProvider(testProvider()), - Ui: ui, - }, - } - - args := []string{ - "-state", statePath, - } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) - } - - actual := strings.TrimSpace(ui.OutputWriter.String()) - expected := strings.TrimSpace(` -foo = tolist([ - tomap({ - "key" = "value" - "key2" = "value2" - }), - tomap({ - "key" = "value" - }), -]) -`) - if actual != expected { - t.Fatalf("wrong output\ngot: %s\nwant: %s", actual, expected) - } -} - func TestOutput_json(t *testing.T) { originalState := states.BuildState(func(s *states.SyncState) { s.SetOutputValue( @@ -107,11 +58,11 @@ func TestOutput_json(t *testing.T) { statePath := testStateFile(t, originalState) - ui := new(cli.MockUi) + view, done := testView(t) c := &OutputCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), - Ui: ui, + View: view, }, } @@ -119,11 +70,13 @@ func TestOutput_json(t *testing.T) { "-state", statePath, "-json", } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: \n%s", output.Stderr()) } - actual := strings.TrimSpace(ui.OutputWriter.String()) + actual := strings.TrimSpace(output.Stdout()) expected := "{\n \"foo\": {\n \"sensitive\": false,\n \"type\": \"string\",\n \"value\": \"bar\"\n }\n}" if actual != expected { t.Fatalf("wrong output\ngot: %#v\nwant: %#v", actual, expected) @@ -135,82 +88,29 @@ func TestOutput_emptyOutputs(t *testing.T) { statePath := testStateFile(t, originalState) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &OutputCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{ + "-no-color", "-state", statePath, } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: \n%s", output.Stderr()) } - if got, want := ui.ErrorWriter.String(), "Warning: No outputs found"; !strings.Contains(got, want) { + // Warning diagnostics should go to stdout + if got, want := output.Stdout(), "Warning: No outputs found"; !strings.Contains(got, want) { t.Fatalf("bad output: expected to contain %q, got:\n%s", want, got) } } -func TestOutput_jsonEmptyOutputs(t *testing.T) { - originalState := states.NewState() - statePath := testStateFile(t, originalState) - - p := testProvider() - ui := new(cli.MockUi) - c := &OutputCommand{ - Meta: Meta{ - testingOverrides: metaOverridesForProvider(p), - Ui: ui, - }, - } - - args := []string{ - "-state", statePath, - "-json", - } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) - } - - actual := strings.TrimSpace(ui.OutputWriter.String()) - expected := "{}" - if actual != expected { - t.Fatalf("bad:\n%#v\n%#v", expected, actual) - } -} - -func TestMissingModuleOutput(t *testing.T) { - originalState := states.BuildState(func(s *states.SyncState) { - s.SetOutputValue( - addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance), - cty.StringVal("bar"), - false, - ) - }) - statePath := testStateFile(t, originalState) - - ui := new(cli.MockUi) - c := &OutputCommand{ - Meta: Meta{ - testingOverrides: metaOverridesForProvider(testProvider()), - Ui: ui, - }, - } - - args := []string{ - "-state", statePath, - "-module", "not_existing_module", - "blah", - } - - if code := c.Run(args); code != 1 { - t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) - } -} - func TestOutput_badVar(t *testing.T) { originalState := states.BuildState(func(s *states.SyncState) { s.SetOutputValue( @@ -221,11 +121,11 @@ func TestOutput_badVar(t *testing.T) { }) statePath := testStateFile(t, originalState) - ui := new(cli.MockUi) + view, done := testView(t) c := &OutputCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), - Ui: ui, + View: view, }, } @@ -233,8 +133,10 @@ func TestOutput_badVar(t *testing.T) { "-state", statePath, "bar", } - if code := c.Run(args); code != 1 { - t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 1 { + t.Fatalf("bad: \n%s", output.Stderr()) } } @@ -253,11 +155,11 @@ func TestOutput_blank(t *testing.T) { }) statePath := testStateFile(t, originalState) - ui := new(cli.MockUi) + view, done := testView(t) c := &OutputCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), - Ui: ui, + View: view, }, } @@ -266,23 +168,24 @@ func TestOutput_blank(t *testing.T) { "", } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: \n%s", output.Stderr()) } expectedOutput := "foo = \"bar\"\nname = \"john-doe\"\n" - output := ui.OutputWriter.String() - if output != expectedOutput { - t.Fatalf("wrong output\ngot: %#v\nwant: %#v", output, expectedOutput) + if got := output.Stdout(); got != expectedOutput { + t.Fatalf("wrong output\ngot: %#v\nwant: %#v", got, expectedOutput) } } func TestOutput_manyArgs(t *testing.T) { - ui := new(cli.MockUi) + view, done := testView(t) c := &OutputCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), - Ui: ui, + View: view, }, } @@ -290,23 +193,27 @@ func TestOutput_manyArgs(t *testing.T) { "bad", "bad", } - if code := c.Run(args); code != 1 { - t.Fatalf("bad: \n%s", ui.OutputWriter.String()) + code := c.Run(args) + output := done(t) + if code != 1 { + t.Fatalf("bad: \n%s", output.Stdout()) } } func TestOutput_noArgs(t *testing.T) { - ui := new(cli.MockUi) + view, done := testView(t) c := &OutputCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), - Ui: ui, + View: view, }, } args := []string{} - if code := c.Run(args); code != 0 { - t.Fatalf("bad: \n%s", ui.OutputWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: \n%s", output.Stdout()) } } @@ -314,11 +221,11 @@ func TestOutput_noState(t *testing.T) { originalState := states.NewState() statePath := testStateFile(t, originalState) - ui := new(cli.MockUi) + view, done := testView(t) c := &OutputCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), - Ui: ui, + View: view, }, } @@ -326,8 +233,10 @@ func TestOutput_noState(t *testing.T) { "-state", statePath, "foo", } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: \n%s", output.Stderr()) } } @@ -336,11 +245,11 @@ func TestOutput_noVars(t *testing.T) { statePath := testStateFile(t, originalState) - ui := new(cli.MockUi) + view, done := testView(t) c := &OutputCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), - Ui: ui, + View: view, }, } @@ -348,8 +257,10 @@ func TestOutput_noVars(t *testing.T) { "-state", statePath, "bar", } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: \n%s", output.Stderr()) } } @@ -387,22 +298,24 @@ func TestOutput_stateDefault(t *testing.T) { } defer os.Chdir(cwd) - ui := new(cli.MockUi) + view, done := testView(t) c := &OutputCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), - Ui: ui, + View: view, }, } args := []string{ "foo", } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: \n%s", output.Stderr()) } - actual := strings.TrimSpace(ui.OutputWriter.String()) + actual := strings.TrimSpace(output.Stdout()) if actual != `"bar"` { t.Fatalf("bad: %#v", actual) } diff --git a/vendor/github.com/hashicorp/terraform/command/plan.go b/vendor/github.com/hashicorp/terraform/command/plan.go index 8064cad5..8f3d1195 100644 --- a/vendor/github.com/hashicorp/terraform/command/plan.go +++ b/vendor/github.com/hashicorp/terraform/command/plan.go @@ -5,8 +5,8 @@ import ( "strings" "github.com/hashicorp/terraform/backend" - "github.com/hashicorp/terraform/configs" - "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/tfdiags" ) @@ -16,199 +16,224 @@ type PlanCommand struct { Meta } -func (c *PlanCommand) Run(args []string) int { - var destroy, refresh, detailed bool - var outPath string - - args = c.Meta.process(args) - cmdFlags := c.Meta.extendedFlagSet("plan") - cmdFlags.BoolVar(&destroy, "destroy", false, "destroy") - cmdFlags.BoolVar(&refresh, "refresh", true, "refresh") - cmdFlags.StringVar(&outPath, "out", "", "path") - cmdFlags.IntVar(&c.Meta.parallelism, "parallelism", DefaultParallelism, "parallelism") - cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path") - cmdFlags.BoolVar(&detailed, "detailed-exitcode", false, "detailed-exitcode") - cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state") - cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout") - cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } - if err := cmdFlags.Parse(args); err != nil { - return 1 - } +func (c *PlanCommand) Run(rawArgs []string) int { + // Parse and apply global view arguments + common, rawArgs := arguments.ParseView(rawArgs) + c.View.Configure(common) - configPath, err := ModulePath(cmdFlags.Args()) - if err != nil { - c.Ui.Error(err.Error()) + // Propagate -no-color for the remote backend's legacy use of Ui. This + // should be removed when the remote backend is migrated to views. + c.Meta.color = !common.NoColor + c.Meta.Color = c.Meta.color + + // Parse and validate flags + args, diags := arguments.ParsePlan(rawArgs) + + // Instantiate the view, even if there are flag errors, so that we render + // diagnostics according to the desired view + view := views.NewPlan(args.ViewType, c.View) + + if diags.HasErrors() { + view.Diagnostics(diags) + view.HelpPrompt() return 1 } // Check for user-supplied plugin path + var err error if c.pluginPath, err = c.loadPluginPath(); err != nil { - c.Ui.Error(fmt.Sprintf("Error loading plugin path: %s", err)) + diags = diags.Append(err) + view.Diagnostics(diags) return 1 } - // Check if the path is a plan, which is not permitted - planFileReader, err := c.PlanFile(configPath) - if err != nil { - c.Ui.Error(err.Error()) + // FIXME: the -input flag value is needed to initialize the backend and the + // operation, but there is no clear path to pass this value down, so we + // continue to mutate the Meta object state for now. + c.Meta.input = args.InputEnabled + + // FIXME: the -parallelism flag is used to control the concurrency of + // Terraform operations. At the moment, this value is used both to + // initialize the backend via the ContextOpts field inside CLIOpts, and to + // set a largely unused field on the Operation request. Again, there is no + // clear path to pass this value down, so we continue to mutate the Meta + // object state for now. + c.Meta.parallelism = args.Operation.Parallelism + + diags = diags.Append(c.providerDevOverrideRuntimeWarnings()) + + // Prepare the backend with the backend-specific arguments + be, beDiags := c.PrepareBackend(args.State) + diags = diags.Append(beDiags) + if diags.HasErrors() { + view.Diagnostics(diags) return 1 } - if planFileReader != nil { - c.showDiagnostics(tfdiags.Sourceless( - tfdiags.Error, - "Invalid configuration directory", - fmt.Sprintf("Cannot pass a saved plan file to the 'terraform plan' command. To apply a saved plan, use: terraform apply %s", configPath), - )) + + // Build the operation request + opReq, opDiags := c.OperationRequest(be, view, args.Operation, args.OutPath) + diags = diags.Append(opDiags) + if diags.HasErrors() { + view.Diagnostics(diags) return 1 } - var diags tfdiags.Diagnostics + // Collect variable value and add them to the operation request + diags = diags.Append(c.GatherVariables(opReq, args.Vars)) + if diags.HasErrors() { + view.Diagnostics(diags) + return 1 + } + + // Before we delegate to the backend, we'll print any warning diagnostics + // we've accumulated here, since the backend will start fresh with its own + // diagnostics. + view.Diagnostics(diags) + diags = nil - var backendConfig *configs.Backend - var configDiags tfdiags.Diagnostics - backendConfig, configDiags = c.loadBackendConfig(configPath) - diags = diags.Append(configDiags) - if configDiags.HasErrors() { - c.showDiagnostics(diags) + // Perform the operation + op, err := c.RunOperation(be, opReq) + if err != nil { + diags = diags.Append(err) + view.Diagnostics(diags) return 1 } + if op.Result != backend.OperationSuccess { + return op.Result.ExitStatus() + } + if args.DetailedExitCode && !op.PlanEmpty { + return 2 + } + + return op.Result.ExitStatus() +} + +func (c *PlanCommand) PrepareBackend(args *arguments.State) (backend.Enhanced, tfdiags.Diagnostics) { + // FIXME: we need to apply the state arguments to the meta object here + // because they are later used when initializing the backend. Carving a + // path to pass these arguments to the functions that need them is + // difficult but would make their use easier to understand. + c.Meta.applyStateArguments(args) + + backendConfig, diags := c.loadBackendConfig(".") + if diags.HasErrors() { + return nil, diags + } + // Load the backend - b, backendDiags := c.Backend(&BackendOpts{ + be, beDiags := c.Backend(&BackendOpts{ Config: backendConfig, }) - diags = diags.Append(backendDiags) - if backendDiags.HasErrors() { - c.showDiagnostics(diags) - return 1 + diags = diags.Append(beDiags) + if beDiags.HasErrors() { + return nil, diags } - // Emit any diagnostics we've accumulated before we delegate to the - // backend, since the backend will handle its own diagnostics internally. - c.showDiagnostics(diags) - diags = nil + return be, diags +} + +func (c *PlanCommand) OperationRequest( + be backend.Enhanced, + view views.Plan, + args *arguments.Operation, + planOutPath string, +) (*backend.Operation, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics // Build the operation - opReq := c.Operation(b) - opReq.ConfigDir = configPath - opReq.Destroy = destroy - opReq.PlanOutPath = outPath - opReq.PlanRefresh = refresh + opReq := c.Operation(be) + opReq.ConfigDir = "." + opReq.PlanMode = args.PlanMode + opReq.Hooks = view.Hooks() + opReq.PlanRefresh = args.Refresh + opReq.PlanOutPath = planOutPath + opReq.Targets = args.Targets + opReq.ForceReplace = args.ForceReplace opReq.Type = backend.OperationTypePlan + opReq.View = view.Operation() + var err error opReq.ConfigLoader, err = c.initConfigLoader() if err != nil { - c.showDiagnostics(err) - return 1 + diags = diags.Append(fmt.Errorf("Failed to initialize config loader: %s", err)) + return nil, diags } - { - var moreDiags tfdiags.Diagnostics - opReq.Variables, moreDiags = c.collectVariableValues() - diags = diags.Append(moreDiags) - if moreDiags.HasErrors() { - c.showDiagnostics(diags) - return 1 - } - } - - // c.Backend above has a non-obvious side-effect of also populating - // c.backendState, which is the state-shaped formulation of the effective - // backend configuration after evaluation of the backend configuration. - // We will in turn adapt that to a plans.Backend to include in a plan file - // if opReq.PlanOutPath was set to a non-empty value above. - // - // FIXME: It's ugly to be doing this inline here, but it's also not really - // clear where would be better to do it. In future we should find a better - // home for this logic, and ideally also stop depending on the side-effect - // of c.Backend setting c.backendState. - { - // This is not actually a state in the usual sense, but rather a - // representation of part of the current working directory's - // "configuration state". - backendPseudoState := c.backendState - if backendPseudoState == nil { - // Should never happen if c.Backend is behaving properly. - diags = diags.Append(fmt.Errorf("Backend initialization didn't produce resolved configuration (This is a bug in Terraform)")) - c.showDiagnostics(diags) - return 1 - } - var backendForPlan plans.Backend - backendForPlan.Type = backendPseudoState.Type - workspace, err := c.Workspace() - if err != nil { - c.Ui.Error(fmt.Sprintf("Error selecting workspace: %s", err)) - return 1 - } - backendForPlan.Workspace = workspace - - // Configuration is a little more awkward to handle here because it's - // stored in state as raw JSON but we need it as a plans.DynamicValue - // to save it in the state. To do that conversion we need to know the - // configuration schema of the backend. - configSchema := b.ConfigSchema() - config, err := backendPseudoState.Config(configSchema) - if err != nil { - // This means that the stored settings don't conform to the current - // schema, which could either be because we're reading something - // created by an older version that is no longer compatible, or - // because the user manually tampered with the stored config. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid backend initialization", - fmt.Sprintf("The backend configuration for this working directory is not valid: %s.\n\nIf you have recently upgraded Terraform, you may need to re-run \"terraform init\" to re-initialize this working directory.", err), - )) - c.showDiagnostics(diags) - return 1 - } - configForPlan, err := plans.NewDynamicValue(config, configSchema.ImpliedType()) - if err != nil { - // This should never happen, since we've just decoded this value - // using the same schema. - diags = diags.Append(fmt.Errorf("Failed to encode backend configuration to store in plan: %s", err)) - c.showDiagnostics(diags) - return 1 - } - backendForPlan.Config = configForPlan - } + return opReq, diags +} - // Perform the operation - op, err := c.RunOperation(b, opReq) - if err != nil { - c.showDiagnostics(err) - return 1 - } +func (c *PlanCommand) GatherVariables(opReq *backend.Operation, args *arguments.Vars) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics - if op.Result != backend.OperationSuccess { - return op.Result.ExitStatus() - } - if detailed && !op.PlanEmpty { - return 2 + // FIXME the arguments package currently trivially gathers variable related + // arguments in a heterogenous slice, in order to minimize the number of + // code paths gathering variables during the transition to this structure. + // Once all commands that gather variables have been converted to this + // structure, we could move the variable gathering code to the arguments + // package directly, removing this shim layer. + + varArgs := args.All() + items := make([]rawFlag, len(varArgs)) + for i := range varArgs { + items[i].Name = varArgs[i].Name + items[i].Value = varArgs[i].Value } + c.Meta.variableArgs = rawFlags{items: &items} + opReq.Variables, diags = c.collectVariableValues() - return op.Result.ExitStatus() + return diags } func (c *PlanCommand) Help() string { helpText := ` -Usage: terraform plan [options] [DIR] +Usage: terraform [global options] plan [options] - Generates an execution plan for Terraform. + Generates a speculative execution plan, showing what actions Terraform + would take to apply the current configuration. This command will not + actually perform the planned actions. - This execution plan can be reviewed prior to running apply to get a - sense for what Terraform will do. Optionally, the plan can be saved to - a Terraform plan file, and apply can take this plan file to execute - this plan exactly. + You can optionally save the plan to a file, which you can then pass to + the "apply" command to perform exactly the actions described in the plan. -Options: +Plan Customization Options: - -compact-warnings If Terraform produces any warnings that are not - accompanied by errors, show them in a more compact form - that includes only the summary messages. + The following options customize how Terraform will produce its plan. You + can also use these options when you run "terraform apply" without passing + it a saved plan, in order to plan and apply in a single command. -destroy If set, a plan will be generated to destroy all resources managed by the given configuration and state. + -refresh=false Skip checking for changes to remote objects while + creating the plan. This can potentially make planning + faster, but at the expense of possibly planning against + a stale record of the remote system state. + + -replace=resource Force replacement of a particular resource instance using + its resource address. If the plan would've normally + produced an update or no-op action for this instance, + Terraform will plan to replace it instead. + + -target=resource Limit the planning operation to only the given module, + resource, or resource instance and all of its + dependencies. You can use this option multiple times to + include more than one object. This is for exceptional + use only. + + -var 'foo=bar' Set a variable in the Terraform configuration. This + flag can be set multiple times. + + -var-file=foo Set variables in the Terraform configuration from + a file. If "terraform.tfvars" or any ".auto.tfvars" + files are present, they will be automatically loaded. + +Other Options: + + -compact-warnings If Terraform produces any warnings that are not + accompanied by errors, show them in a more compact form + that includes only the summary messages. + -detailed-exitcode Return detailed exit codes when the command exits. This will change the meaning of exit codes to: 0 - Succeeded, diff is empty (no changes) @@ -228,26 +253,12 @@ Options: -parallelism=n Limit the number of concurrent operations. Defaults to 10. - -refresh=true Update state prior to checking for differences. - - -state=statefile Path to a Terraform state file to use to look - up Terraform-managed resources. By default it will - use the state "terraform.tfstate" if it exists. - - -target=resource Resource to target. Operation will be limited to this - resource and its dependencies. This flag can be used - multiple times. - - -var 'foo=bar' Set a variable in the Terraform configuration. This - flag can be set multiple times. - - -var-file=foo Set variables in the Terraform configuration from - a file. If "terraform.tfvars" or any ".auto.tfvars" - files are present, they will be automatically loaded. + -state=statefile A legacy option used for the local backend only. See the + local backend's documentation for more information. ` return strings.TrimSpace(helpText) } func (c *PlanCommand) Synopsis() string { - return "Generate and show an execution plan" + return "Show changes required by the current configuration" } diff --git a/vendor/github.com/hashicorp/terraform/command/plan_test.go b/vendor/github.com/hashicorp/terraform/command/plan_test.go index 7a99446a..7d4611b1 100644 --- a/vendor/github.com/hashicorp/terraform/command/plan_test.go +++ b/vendor/github.com/hashicorp/terraform/command/plan_test.go @@ -2,8 +2,11 @@ package command import ( "bytes" + "context" + "fmt" "io/ioutil" "os" + "path" "path/filepath" "strings" "sync" @@ -11,7 +14,6 @@ import ( "time" "github.com/davecgh/go-spew/spew" - "github.com/mitchellh/cli" "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/addrs" @@ -21,6 +23,7 @@ import ( "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/tfdiags" ) func TestPlan(t *testing.T) { @@ -30,17 +33,19 @@ func TestPlan(t *testing.T) { defer testChdir(t, td)() p := planFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{} - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } } @@ -57,20 +62,21 @@ func TestPlan_lockedState(t *testing.T) { defer unlock() p := planFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{} - if code := c.Run(args); code == 0 { - t.Fatal("expected error") + code := c.Run(args) + if code == 0 { + t.Fatal("expected error", done(t).Stdout()) } - output := ui.ErrorWriter.String() + output := done(t).Stderr() if !strings.Contains(output, "lock") { t.Fatal("command output does not look like a lock error:", output) } @@ -83,21 +89,28 @@ func TestPlan_plan(t *testing.T) { planPath := testPlanFileNoop(t) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{planPath} - if code := c.Run(args); code != 1 { - t.Fatalf("wrong exit status %d; want 1\nstderr: %s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 1 { + t.Fatalf("wrong exit status %d; want 1\nstderr: %s", code, output.Stderr()) } } func TestPlan_destroy(t *testing.T) { + td := tempDir(t) + testCopyDir(t, testFixturePath("plan"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + originalState := states.BuildState(func(s *states.SyncState) { s.SetResourceInstanceCurrent( addrs.Resource{ @@ -119,11 +132,11 @@ func TestPlan_destroy(t *testing.T) { statePath := testStateFile(t, originalState) p := planFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -131,10 +144,11 @@ func TestPlan_destroy(t *testing.T) { "-destroy", "-out", outPath, "-state", statePath, - testFixturePath("plan"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } plan := testReadPlan(t, outPath) @@ -146,23 +160,25 @@ func TestPlan_destroy(t *testing.T) { } func TestPlan_noState(t *testing.T) { - tmp, cwd := testCwd(t) - defer testFixCwd(t, tmp, cwd) + td := tempDir(t) + testCopyDir(t, testFixturePath("plan"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() p := planFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } - args := []string{ - testFixturePath("plan"), - } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + args := []string{} + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } // Verify that refresh was called @@ -172,44 +188,51 @@ func TestPlan_noState(t *testing.T) { // Verify that the provider was called with the existing state actual := p.PlanResourceChangeRequest.PriorState - expected := cty.NullVal(p.GetSchemaReturn.ResourceTypes["test_instance"].ImpliedType()) + expected := cty.NullVal(p.GetProviderSchemaResponse.ResourceTypes["test_instance"].Block.ImpliedType()) if !expected.RawEquals(actual) { t.Fatalf("wrong prior state\ngot: %#v\nwant: %#v", actual, expected) } } func TestPlan_outPath(t *testing.T) { - tmp, cwd := testCwd(t) - defer testFixCwd(t, tmp, cwd) + td := tempDir(t) + testCopyDir(t, testFixturePath("plan"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() - td := testTempDir(t) outPath := filepath.Join(td, "test.plan") p := planFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } - p.PlanResourceChangeResponse = providers.PlanResourceChangeResponse{ + p.PlanResourceChangeResponse = &providers.PlanResourceChangeResponse{ PlannedState: cty.NullVal(cty.EmptyObject), } args := []string{ "-out", outPath, - testFixturePath("plan"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } testReadPlan(t, outPath) // will call t.Fatal itself if the file cannot be read } func TestPlan_outPathNoChange(t *testing.T) { + td := tempDir(t) + testCopyDir(t, testFixturePath("plan"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + originalState := states.BuildState(func(s *states.SyncState) { s.SetResourceInstanceCurrent( addrs.Resource{ @@ -232,25 +255,25 @@ func TestPlan_outPathNoChange(t *testing.T) { }) statePath := testStateFile(t, originalState) - td := testTempDir(t) outPath := filepath.Join(td, "test.plan") p := planFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{ "-out", outPath, "-state", statePath, - testFixturePath("plan"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } plan := testReadPlan(t, outPath) @@ -285,24 +308,26 @@ func TestPlan_outBackend(t *testing.T) { ) }) - // Setup our backend state + // Set up our backend state dataState, srv := testBackendState(t, originalState, 200) defer srv.Close() testStateFileRemote(t, dataState) outPath := "foo" p := testProvider() - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": { - Type: cty.String, - Computed: true, - }, - "ami": { - Type: cty.String, - Optional: true, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": { + Type: cty.String, + Computed: true, + }, + "ami": { + Type: cty.String, + Optional: true, + }, }, }, }, @@ -313,20 +338,22 @@ func TestPlan_outBackend(t *testing.T) { PlannedState: req.ProposedNewState, } } - ui := cli.NewMockUi() + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{ "-out", outPath, } - if code := c.Run(args); code != 0 { - t.Logf("stdout: %s", ui.OutputWriter.String()) - t.Fatalf("plan command failed with exit code %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Logf("stdout: %s", output.Stdout()) + t.Fatalf("plan command failed with exit code %d\n\n%s", code, output.Stderr()) } plan := testReadPlan(t, outPath) @@ -358,24 +385,28 @@ func TestPlan_outBackend(t *testing.T) { } func TestPlan_refreshFalse(t *testing.T) { - tmp, cwd := testCwd(t) - defer testFixCwd(t, tmp, cwd) + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("plan"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() p := planFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{ "-refresh=false", - testFixturePath("plan"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if p.ReadResourceCalled { @@ -384,24 +415,31 @@ func TestPlan_refreshFalse(t *testing.T) { } func TestPlan_state(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("plan"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + originalState := testState() statePath := testStateFile(t, originalState) p := planFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{ "-state", statePath, - testFixturePath("plan"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } // Verify that the provider was called with the existing state @@ -420,34 +458,31 @@ func TestPlan_state(t *testing.T) { } func TestPlan_stateDefault(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("plan"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + // Generate state and move it to the default path originalState := testState() statePath := testStateFile(t, originalState) - - // Change to that directory - cwd, err := os.Getwd() - if err != nil { - t.Fatalf("err: %s", err) - } - if err := os.Chdir(filepath.Dir(statePath)); err != nil { - t.Fatalf("err: %s", err) - } - defer os.Chdir(cwd) + os.Rename(statePath, path.Join(td, "terraform.tfstate")) p := planFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } - args := []string{ - "-state", statePath, - testFixturePath("plan"), - } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + args := []string{} + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } // Verify that the provider was called with the existing state @@ -476,11 +511,13 @@ func TestPlan_validate(t *testing.T) { defer testChdir(t, td)() p := testProvider() - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + }, }, }, }, @@ -490,35 +527,43 @@ func TestPlan_validate(t *testing.T) { PlannedState: req.ProposedNewState, } } - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } - args := []string{} - if code := c.Run(args); code != 1 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + args := []string{"-no-color"} + code := c.Run(args) + output := done(t) + if code != 1 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } - actual := ui.ErrorWriter.String() + actual := output.Stderr() if want := "Error: Invalid count argument"; !strings.Contains(actual, want) { t.Fatalf("unexpected error output\ngot:\n%s\n\nshould contain: %s", actual, want) } + if want := "9: count = timestamp()"; !strings.Contains(actual, want) { + t.Fatalf("unexpected error output\ngot:\n%s\n\nshould contain: %s", actual, want) + } } func TestPlan_vars(t *testing.T) { - tmp, cwd := testCwd(t) - defer testFixCwd(t, tmp, cwd) + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("plan-vars"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() p := planVarsFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -531,10 +576,11 @@ func TestPlan_vars(t *testing.T) { args := []string{ "-var", "foo=bar", - testFixturePath("plan-vars"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if actual != "bar" { @@ -543,8 +589,11 @@ func TestPlan_vars(t *testing.T) { } func TestPlan_varsUnset(t *testing.T) { - tmp, cwd := testCwd(t) - defer testFixCwd(t, tmp, cwd) + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("plan-vars"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() // The plan command will prompt for interactive input of var.foo. // We'll answer "bar" to that prompt, which should then allow this @@ -557,19 +606,19 @@ func TestPlan_varsUnset(t *testing.T) { defer close() p := planVarsFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } - args := []string{ - testFixturePath("plan-vars"), - } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + args := []string{} + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } } @@ -577,8 +626,11 @@ func TestPlan_varsUnset(t *testing.T) { // processing of user input: // https://github.com/hashicorp/terraform/issues/26035 func TestPlan_providerArgumentUnset(t *testing.T) { - tmp, cwd := testCwd(t) - defer testFixCwd(t, tmp, cwd) + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("plan"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() // Disable test mode so input would be asked test = false @@ -589,25 +641,29 @@ func TestPlan_providerArgumentUnset(t *testing.T) { p := planFixtureProvider() // override the planFixtureProvider schema to include a required provider argument - p.GetSchemaReturn = &terraform.ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "region": {Type: cty.String, Required: true}, + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "region": {Type: cty.String, Required: true}, + }, }, }, - ResourceTypes: map[string]*configschema.Block{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true, Computed: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "network_interface": { - Nesting: configschema.NestingList, - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "device_index": {Type: cty.String, Optional: true}, - "description": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "ami": {Type: cty.String, Optional: true, Computed: true}, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "network_interface": { + Nesting: configschema.NestingList, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "device_index": {Type: cty.String, Optional: true}, + "description": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -615,25 +671,28 @@ func TestPlan_providerArgumentUnset(t *testing.T) { }, }, } - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } - args := []string{ - testFixturePath("plan"), - } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + args := []string{} + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } } func TestPlan_varFile(t *testing.T) { - tmp, cwd := testCwd(t) - defer testFixCwd(t, tmp, cwd) + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("plan-vars"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() varFilePath := testTempFile(t) if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil { @@ -641,11 +700,11 @@ func TestPlan_varFile(t *testing.T) { } p := planVarsFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -658,10 +717,11 @@ func TestPlan_varFile(t *testing.T) { args := []string{ "-var-file", varFilePath, - testFixturePath("plan-vars"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if actual != "bar" { @@ -670,27 +730,23 @@ func TestPlan_varFile(t *testing.T) { } func TestPlan_varFileDefault(t *testing.T) { - varFileDir := testTempDir(t) - varFilePath := filepath.Join(varFileDir, "terraform.tfvars") - if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil { - t.Fatalf("err: %s", err) - } + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("plan-vars"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() - cwd, err := os.Getwd() - if err != nil { - t.Fatalf("err: %s", err) - } - if err := os.Chdir(varFileDir); err != nil { + varFilePath := filepath.Join(td, "terraform.tfvars") + if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil { t.Fatalf("err: %s", err) } - defer os.Chdir(cwd) p := planVarsFixtureProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } @@ -701,11 +757,11 @@ func TestPlan_varFileDefault(t *testing.T) { return } - args := []string{ - testFixturePath("plan-vars"), - } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + args := []string{} + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if actual != "bar" { @@ -714,8 +770,11 @@ func TestPlan_varFileDefault(t *testing.T) { } func TestPlan_varFileWithDecls(t *testing.T) { - tmp, cwd := testCwd(t) - defer testFixCwd(t, tmp, cwd) + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("plan-vars"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() varFilePath := testTempFile(t) if err := ioutil.WriteFile(varFilePath, []byte(planVarFileWithDecl), 0644); err != nil { @@ -723,23 +782,24 @@ func TestPlan_varFileWithDecls(t *testing.T) { } p := planVarsFixtureProvider() - ui := cli.NewMockUi() + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{ "-var-file", varFilePath, - testFixturePath("plan-vars"), } - if code := c.Run(args); code == 0 { - t.Fatalf("succeeded; want failure\n\n%s", ui.OutputWriter.String()) + code := c.Run(args) + output := done(t) + if code == 0 { + t.Fatalf("succeeded; want failure\n\n%s", output.Stdout()) } - msg := ui.ErrorWriter.String() + msg := output.Stderr() if got, want := msg, "Variable declaration in .tfvars file"; !strings.Contains(got, want) { t.Fatalf("missing expected error message\nwant message containing %q\ngot:\n%s", want, got) } @@ -751,19 +811,37 @@ func TestPlan_detailedExitcode(t *testing.T) { defer os.RemoveAll(td) defer testChdir(t, td)() - p := planFixtureProvider() - ui := new(cli.MockUi) - c := &PlanCommand{ - Meta: Meta{ - testingOverrides: metaOverridesForProvider(p), - Ui: ui, - }, - } + t.Run("return 1", func(t *testing.T) { + view, done := testView(t) + c := &PlanCommand{ + Meta: Meta{ + // Running plan without setting testingOverrides is similar to plan without init + View: view, + }, + } + code := c.Run([]string{"-detailed-exitcode"}) + output := done(t) + if code != 1 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + }) - args := []string{"-detailed-exitcode"} - if code := c.Run(args); code != 2 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) - } + t.Run("return 2", func(t *testing.T) { + p := planFixtureProvider() + view, done := testView(t) + c := &PlanCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + View: view, + }, + } + + code := c.Run([]string{"-detailed-exitcode"}) + output := done(t) + if code != 2 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + }) } func TestPlan_detailedExitcode_emptyDiff(t *testing.T) { @@ -773,30 +851,38 @@ func TestPlan_detailedExitcode_emptyDiff(t *testing.T) { defer testChdir(t, td)() p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } args := []string{"-detailed-exitcode"} - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } } func TestPlan_shutdown(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-shutdown"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + cancelled := make(chan struct{}) shutdownCh := make(chan struct{}) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, ShutdownCh: shutdownCh, }, } @@ -827,30 +913,22 @@ func TestPlan_shutdown(t *testing.T) { return } - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "ami": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "ami": {Type: cty.String, Optional: true}, + }, }, }, }, } - code := c.Run([]string{ - // Unfortunately it seems like this test can inadvertently pick up - // leftover state from other tests without this. Ideally we should - // find which test is leaving a terraform.tfstate behind and stop it - // doing that, but this will stop this test flapping for now. - "-state=nonexistent.tfstate", - testFixturePath("apply-shutdown"), - }) - if code != 0 { - // FIXME: In retrospect cancellation ought to be an unsuccessful exit - // case, but we need to do that cautiously in case it impacts automation - // wrappers. See the note about this in the terraform.stopHook - // implementation for more. - t.Errorf("wrong exit code %d; want 0\noutput:\n%s", code, ui.OutputWriter.String()) + code := c.Run([]string{}) + output := done(t) + if code != 1 { + t.Errorf("wrong exit code %d; want 1\noutput:\n%s", code, output.Stdout()) } select { @@ -866,42 +944,354 @@ func TestPlan_init_required(t *testing.T) { defer os.RemoveAll(td) defer testChdir(t, td)() - ui := new(cli.MockUi) + view, done := testView(t) c := &PlanCommand{ Meta: Meta{ // Running plan without setting testingOverrides is similar to plan without init - Ui: ui, + View: view, }, } - args := []string{} - if code := c.Run(args); code != 1 { + args := []string{"-no-color"} + code := c.Run(args) + output := done(t) + if code != 1 { t.Fatalf("expected error, got success") } - output := ui.ErrorWriter.String() - if !strings.Contains(output, `Plugin reinitialization required. Please run "terraform init".`) { - t.Fatal("wrong error message in output:", output) + got := output.Stderr() + if !strings.Contains(got, `Error: Could not load plugin`) { + t.Fatal("wrong error message in output:", got) + } +} + +// Config with multiple resources, targeting plan of a subset +func TestPlan_targeted(t *testing.T) { + td := tempDir(t) + testCopyDir(t, testFixturePath("apply-targeted"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + p := testProvider() + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ + "test_instance": { + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + }, + }, + }, + }, + } + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { + return providers.PlanResourceChangeResponse{ + PlannedState: req.ProposedNewState, + } + } + + view, done := testView(t) + c := &PlanCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + View: view, + }, + } + + args := []string{ + "-target", "test_instance.foo", + "-target", "test_instance.baz", + } + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + + if got, want := output.Stdout(), "3 to add, 0 to change, 0 to destroy"; !strings.Contains(got, want) { + t.Fatalf("bad change summary, want %q, got:\n%s", want, got) } } +// Diagnostics for invalid -target flags +func TestPlan_targetFlagsDiags(t *testing.T) { + testCases := map[string]string{ + "test_instance.": "Dot must be followed by attribute name.", + "test_instance": "Resource specification must include a resource type and name.", + } + + for target, wantDiag := range testCases { + t.Run(target, func(t *testing.T) { + td := testTempDir(t) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + view, done := testView(t) + c := &PlanCommand{ + Meta: Meta{ + View: view, + }, + } + + args := []string{ + "-target", target, + } + code := c.Run(args) + output := done(t) + if code != 1 { + t.Fatalf("bad: %d\n\n%s", code, output.Stdout()) + } + + got := output.Stderr() + if !strings.Contains(got, target) { + t.Fatalf("bad error output, want %q, got:\n%s", target, got) + } + if !strings.Contains(got, wantDiag) { + t.Fatalf("bad error output, want %q, got:\n%s", wantDiag, got) + } + }) + } +} + +func TestPlan_replace(t *testing.T) { + td := tempDir(t) + testCopyDir(t, testFixturePath("plan-replace"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + originalState := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "a", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"hello"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + }) + statePath := testStateFile(t, originalState) + + p := testProvider() + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ + "test_instance": { + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + }, + }, + }, + }, + } + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { + return providers.PlanResourceChangeResponse{ + PlannedState: req.ProposedNewState, + } + } + + view, done := testView(t) + c := &PlanCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + View: view, + }, + } + + args := []string{ + "-state", statePath, + "-no-color", + "-replace", "test_instance.a", + } + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("wrong exit code %d\n\n%s", code, output.Stderr()) + } + + stdout := output.Stdout() + if got, want := stdout, "1 to add, 0 to change, 1 to destroy"; !strings.Contains(got, want) { + t.Errorf("wrong plan summary\ngot output:\n%s\n\nwant substring: %s", got, want) + } + if got, want := stdout, "test_instance.a will be replaced, as requested"; !strings.Contains(got, want) { + t.Errorf("missing replace explanation\ngot output:\n%s\n\nwant substring: %s", got, want) + } +} + +// Verify that the parallelism flag allows no more than the desired number of +// concurrent calls to PlanResourceChange. +func TestPlan_parallelism(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("parallelism"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + par := 4 + + // started is a semaphore that we use to ensure that we never have more + // than "par" plan operations happening concurrently + started := make(chan struct{}, par) + + // beginCtx is used as a starting gate to hold back PlanResourceChange + // calls until we reach the desired concurrency. The cancel func "begin" is + // called once we reach the desired concurrency, allowing all apply calls + // to proceed in unison. + beginCtx, begin := context.WithCancel(context.Background()) + + // Since our mock provider has its own mutex preventing concurrent calls + // to ApplyResourceChange, we need to use a number of separate providers + // here. They will all have the same mock implementation function assigned + // but crucially they will each have their own mutex. + providerFactories := map[addrs.Provider]providers.Factory{} + for i := 0; i < 10; i++ { + name := fmt.Sprintf("test%d", i) + provider := &terraform.MockProvider{} + provider.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ + name + "_instance": {Block: &configschema.Block{}}, + }, + } + provider.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { + // If we ever have more than our intended parallelism number of + // plan operations running concurrently, the semaphore will fail. + select { + case started <- struct{}{}: + defer func() { + <-started + }() + default: + t.Fatal("too many concurrent apply operations") + } + + // If we never reach our intended parallelism, the context will + // never be canceled and the test will time out. + if len(started) >= par { + begin() + } + <-beginCtx.Done() + + // do some "work" + // Not required for correctness, but makes it easier to spot a + // failure when there is more overlap. + time.Sleep(10 * time.Millisecond) + return providers.PlanResourceChangeResponse{ + PlannedState: req.ProposedNewState, + } + } + providerFactories[addrs.NewDefaultProvider(name)] = providers.FactoryFixed(provider) + } + testingOverrides := &testingOverrides{ + Providers: providerFactories, + } + + view, done := testView(t) + c := &PlanCommand{ + Meta: Meta{ + testingOverrides: testingOverrides, + View: view, + }, + } + + args := []string{ + fmt.Sprintf("-parallelism=%d", par), + } + + res := c.Run(args) + output := done(t) + if res != 0 { + t.Fatal(output.Stdout()) + } +} + +func TestPlan_warnings(t *testing.T) { + td := tempDir(t) + testCopyDir(t, testFixturePath("plan"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + t.Run("full warnings", func(t *testing.T) { + p := planWarningsFixtureProvider() + view, done := testView(t) + c := &PlanCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + View: view, + }, + } + code := c.Run([]string{}) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + // the output should contain 3 warnings (returned by planWarningsFixtureProvider()) + wantWarnings := []string{ + "warning 1", + "warning 2", + "warning 3", + } + for _, want := range wantWarnings { + if !strings.Contains(output.Stdout(), want) { + t.Errorf("missing warning %s", want) + } + } + }) + + t.Run("compact warnings", func(t *testing.T) { + p := planWarningsFixtureProvider() + view, done := testView(t) + c := &PlanCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + View: view, + }, + } + code := c.Run([]string{"-compact-warnings"}) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + // the output should contain 3 warnings (returned by planWarningsFixtureProvider()) + // and the message that plan was run with -compact-warnings + wantWarnings := []string{ + "warning 1", + "warning 2", + "warning 3", + "To see the full warning notes, run Terraform without -compact-warnings.", + } + for _, want := range wantWarnings { + if !strings.Contains(output.Stdout(), want) { + t.Errorf("missing warning %s", want) + } + } + }) +} + // planFixtureSchema returns a schema suitable for processing the // configuration in testdata/plan . This schema should be // assigned to a mock provider named "test". -func planFixtureSchema() *terraform.ProviderSchema { - return &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ +func planFixtureSchema() *providers.GetProviderSchemaResponse { + return &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "network_interface": { - Nesting: configschema.NestingList, - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "device_index": {Type: cty.String, Optional: true}, - "description": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "ami": {Type: cty.String, Optional: true}, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "network_interface": { + Nesting: configschema.NestingList, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "device_index": {Type: cty.String, Optional: true}, + "description": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -913,11 +1303,11 @@ func planFixtureSchema() *terraform.ProviderSchema { // planFixtureProvider returns a mock provider that is configured for basic // operation with the configuration in testdata/plan. This mock has -// GetSchemaReturn and PlanResourceChangeFn populated, with the plan +// GetSchemaResponse and PlanResourceChangeFn populated, with the plan // step just passing through the new object proposed by Terraform Core. func planFixtureProvider() *terraform.MockProvider { p := testProvider() - p.GetSchemaReturn = planFixtureSchema() + p.GetProviderSchemaResponse = planFixtureSchema() p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { return providers.PlanResourceChangeResponse{ PlannedState: req.ProposedNewState, @@ -929,13 +1319,15 @@ func planFixtureProvider() *terraform.MockProvider { // planVarsFixtureSchema returns a schema suitable for processing the // configuration in testdata/plan-vars . This schema should be // assigned to a mock provider named "test". -func planVarsFixtureSchema() *terraform.ProviderSchema { - return &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ +func planVarsFixtureSchema() *providers.GetProviderSchemaResponse { + return &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "value": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "value": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -944,11 +1336,11 @@ func planVarsFixtureSchema() *terraform.ProviderSchema { // planVarsFixtureProvider returns a mock provider that is configured for basic // operation with the configuration in testdata/plan-vars. This mock has -// GetSchemaReturn and PlanResourceChangeFn populated, with the plan +// GetSchemaResponse and PlanResourceChangeFn populated, with the plan // step just passing through the new object proposed by Terraform Core. func planVarsFixtureProvider() *terraform.MockProvider { p := testProvider() - p.GetSchemaReturn = planVarsFixtureSchema() + p.GetProviderSchemaResponse = planVarsFixtureSchema() p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { return providers.PlanResourceChangeResponse{ PlannedState: req.ProposedNewState, @@ -957,6 +1349,25 @@ func planVarsFixtureProvider() *terraform.MockProvider { return p } +// planFixtureProvider returns a mock provider that is configured for basic +// operation with the configuration in testdata/plan. This mock has +// GetSchemaResponse and PlanResourceChangeFn populated, returning 3 warnings. +func planWarningsFixtureProvider() *terraform.MockProvider { + p := testProvider() + p.GetProviderSchemaResponse = planFixtureSchema() + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { + return providers.PlanResourceChangeResponse{ + Diagnostics: tfdiags.Diagnostics{ + tfdiags.SimpleWarning("warning 1"), + tfdiags.SimpleWarning("warning 2"), + tfdiags.SimpleWarning("warning 3"), + }, + PlannedState: req.ProposedNewState, + } + } + return p +} + const planVarFile = ` foo = "bar" ` @@ -967,17 +1378,3 @@ foo = "bar" variable "nope" { } ` - -const testPlanNoStateStr = ` - -` - -const testPlanStateStr = ` -ID = bar -Tainted = false -` - -const testPlanStateDefaultStr = ` -ID = bar -Tainted = false -` diff --git a/vendor/github.com/hashicorp/terraform/command/plugins.go b/vendor/github.com/hashicorp/terraform/command/plugins.go index 032e00ce..f57de348 100644 --- a/vendor/github.com/hashicorp/terraform/command/plugins.go +++ b/vendor/github.com/hashicorp/terraform/command/plugins.go @@ -9,16 +9,17 @@ import ( "os/exec" "path/filepath" "runtime" - "strings" plugin "github.com/hashicorp/go-plugin" "github.com/kardianos/osext" + fileprovisioner "github.com/hashicorp/terraform/builtin/provisioners/file" + localexec "github.com/hashicorp/terraform/builtin/provisioners/local-exec" + remoteexec "github.com/hashicorp/terraform/builtin/provisioners/remote-exec" "github.com/hashicorp/terraform/internal/logging" tfplugin "github.com/hashicorp/terraform/plugin" "github.com/hashicorp/terraform/plugin/discovery" "github.com/hashicorp/terraform/provisioners" - "github.com/hashicorp/terraform/terraform" ) // NOTE WELL: The logic in this file is primarily about plugin types OTHER THAN @@ -120,7 +121,7 @@ func (m *Meta) pluginDirs(includeAutoInstalled bool) []string { return dirs } -func (m *Meta) provisionerFactories() map[string]terraform.ProvisionerFactory { +func (m *Meta) provisionerFactories() map[string]provisioners.Factory { dirs := m.pluginDirs(true) plugins := discovery.FindPlugins("provisioner", dirs) plugins, _ = plugins.ValidateVersions() @@ -131,12 +132,12 @@ func (m *Meta) provisionerFactories() map[string]terraform.ProvisionerFactory { // name here, even though the discovery interface forces us to pretend // that might not be true. - factories := make(map[string]terraform.ProvisionerFactory) + factories := make(map[string]provisioners.Factory) // Wire up the internal provisioners first. These might be overridden // by discovered provisioners below. - for name := range InternalProvisioners { - factories[name] = internalProvisionerFactory(discovery.PluginMeta{Name: name}) + for name, factory := range internalProvisionerFactories() { + factories[name] = factory } byName := plugins.ByName() @@ -152,37 +153,14 @@ func (m *Meta) provisionerFactories() map[string]terraform.ProvisionerFactory { return factories } -func internalPluginClient(kind, name string) (*plugin.Client, error) { - cmdLine, err := BuildPluginCommandString(kind, name) - if err != nil { - return nil, err - } - - // See the docstring for BuildPluginCommandString for why we need to do - // this split here. - cmdArgv := strings.Split(cmdLine, TFSPACE) - - cfg := &plugin.ClientConfig{ - Cmd: exec.Command(cmdArgv[0], cmdArgv[1:]...), - HandshakeConfig: tfplugin.Handshake, - Managed: true, - VersionedPlugins: tfplugin.VersionedPlugins, - AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, - AutoMTLS: enableProviderAutoMTLS, - Logger: logging.NewHCLogger("plugin"), - } - - return plugin.NewClient(cfg), nil -} - -func provisionerFactory(meta discovery.PluginMeta) terraform.ProvisionerFactory { +func provisionerFactory(meta discovery.PluginMeta) provisioners.Factory { return func() (provisioners.Interface, error) { cfg := &plugin.ClientConfig{ Cmd: exec.Command(meta.Path), HandshakeConfig: tfplugin.Handshake, VersionedPlugins: tfplugin.VersionedPlugins, Managed: true, - Logger: logging.NewHCLogger("provisioner"), + Logger: logging.NewLogger("provisioner"), AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, AutoMTLS: enableProviderAutoMTLS, } @@ -191,13 +169,11 @@ func provisionerFactory(meta discovery.PluginMeta) terraform.ProvisionerFactory } } -func internalProvisionerFactory(meta discovery.PluginMeta) terraform.ProvisionerFactory { - return func() (provisioners.Interface, error) { - client, err := internalPluginClient("provisioner", meta.Name) - if err != nil { - return nil, fmt.Errorf("[WARN] failed to build command line for internal plugin %q: %s", meta.Name, err) - } - return newProvisionerClient(client) +func internalProvisionerFactories() map[string]provisioners.Factory { + return map[string]provisioners.Factory{ + "file": provisioners.FactoryFixed(fileprovisioner.New()), + "local-exec": provisioners.FactoryFixed(localexec.New()), + "remote-exec": provisioners.FactoryFixed(remoteexec.New()), } } diff --git a/vendor/github.com/hashicorp/terraform/command/plugins_test.go b/vendor/github.com/hashicorp/terraform/command/plugins_test.go index 379cc5bc..015a47d3 100644 --- a/vendor/github.com/hashicorp/terraform/command/plugins_test.go +++ b/vendor/github.com/hashicorp/terraform/command/plugins_test.go @@ -36,7 +36,7 @@ func TestInternalProviders(t *testing.T) { t.Fatal(err) } - schema := tfProvider.GetSchema() + schema := tfProvider.GetProviderSchema() _, found := schema.DataSources["terraform_remote_state"] if !found { t.Errorf("didn't find terraform_remote_state in internal \"terraform\" provider") diff --git a/vendor/github.com/hashicorp/terraform/command/providers.go b/vendor/github.com/hashicorp/terraform/command/providers.go index 918fd841..a846b85d 100644 --- a/vendor/github.com/hashicorp/terraform/command/providers.go +++ b/vendor/github.com/hashicorp/terraform/command/providers.go @@ -21,7 +21,7 @@ func (c *ProvidersCommand) Help() string { } func (c *ProvidersCommand) Synopsis() string { - return "Prints a tree of the providers used in the configuration" + return "Show the providers required for this configuration" } func (c *ProvidersCommand) Run(args []string) int { @@ -82,6 +82,9 @@ func (c *ProvidersCommand) Run(args []string) int { return 1 } + // This is a read-only command + c.ignoreRemoteBackendVersionConflict(b) + // Get the state env, err := c.Workspace() if err != nil { @@ -146,7 +149,7 @@ func (c *ProvidersCommand) populateTreeNode(tree treeprint.Tree, node *configs.M } const providersCommandHelp = ` -Usage: terraform providers [dir] +Usage: terraform [global options] providers [dir] Prints out a tree of modules in the referenced configuration annotated with their provider requirements. diff --git a/vendor/github.com/hashicorp/terraform/command/providers_lock.go b/vendor/github.com/hashicorp/terraform/command/providers_lock.go index 38bf2184..1c8d3ae2 100644 --- a/vendor/github.com/hashicorp/terraform/command/providers_lock.go +++ b/vendor/github.com/hashicorp/terraform/command/providers_lock.go @@ -23,7 +23,7 @@ type ProvidersLockCommand struct { } func (c *ProvidersLockCommand) Synopsis() string { - return "Creates or updates dependency locks for providers in the configuration" + return "Write out dependency locks for the configured providers" } func (c *ProvidersLockCommand) Run(args []string) int { @@ -301,7 +301,7 @@ func (c *ProvidersLockCommand) Run(args []string) int { func (c *ProvidersLockCommand) Help() string { return ` -Usage: terraform providers lock [options] [providers...] +Usage: terraform [global options] providers lock [options] [providers...] Normally the dependency lock file (.terraform.lock.hcl) is updated automatically by "terraform init", but the information available to the diff --git a/vendor/github.com/hashicorp/terraform/command/providers_mirror.go b/vendor/github.com/hashicorp/terraform/command/providers_mirror.go index 029b0863..3922840e 100644 --- a/vendor/github.com/hashicorp/terraform/command/providers_mirror.go +++ b/vendor/github.com/hashicorp/terraform/command/providers_mirror.go @@ -24,7 +24,7 @@ type ProvidersMirrorCommand struct { } func (c *ProvidersMirrorCommand) Synopsis() string { - return "Mirrors the provider plugins needed for the current configuration" + return "Save local copies of all required provider plugins" } func (c *ProvidersMirrorCommand) Run(args []string) int { @@ -329,7 +329,7 @@ func (c *ProvidersMirrorCommand) Run(args []string) int { func (c *ProvidersMirrorCommand) Help() string { return ` -Usage: terraform providers mirror [options] +Usage: terraform [global options] providers mirror [options] Populates a local directory with copies of the provider plugins needed for the current configuration, so that the directory can be used either directly diff --git a/vendor/github.com/hashicorp/terraform/command/providers_schema.go b/vendor/github.com/hashicorp/terraform/command/providers_schema.go index 60e4c4c3..287af436 100644 --- a/vendor/github.com/hashicorp/terraform/command/providers_schema.go +++ b/vendor/github.com/hashicorp/terraform/command/providers_schema.go @@ -20,7 +20,7 @@ func (c *ProvidersSchemaCommand) Help() string { } func (c *ProvidersSchemaCommand) Synopsis() string { - return "Prints the schemas of the providers used in the configuration" + return "Show schemas for the providers used in the configuration" } func (c *ProvidersSchemaCommand) Run(args []string) int { @@ -67,6 +67,9 @@ func (c *ProvidersSchemaCommand) Run(args []string) int { return 1 } + // This is a read-only command + c.ignoreRemoteBackendVersionConflict(b) + // we expect that the config dir is the cwd cwd, err := os.Getwd() if err != nil { @@ -105,7 +108,7 @@ func (c *ProvidersSchemaCommand) Run(args []string) int { } const providersSchemaCommandHelp = ` -Usage: terraform providers schema -json +Usage: terraform [global options] providers schema -json Prints out a json representation of the schemas for all providers used in the current configuration. diff --git a/vendor/github.com/hashicorp/terraform/command/providers_schema_test.go b/vendor/github.com/hashicorp/terraform/command/providers_schema_test.go index 96890f25..3cf0de1f 100644 --- a/vendor/github.com/hashicorp/terraform/command/providers_schema_test.go +++ b/vendor/github.com/hashicorp/terraform/command/providers_schema_test.go @@ -9,7 +9,11 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/cli" + "github.com/zclconf/go-cty/cty" ) func TestProvidersSchema_error(t *testing.T) { @@ -28,8 +32,6 @@ func TestProvidersSchema_error(t *testing.T) { } func TestProvidersSchema_output(t *testing.T) { - // there's only one test at this time. This can be refactored to have - // multiple test cases in individual directories as needed. fixtureDir := "testdata/providers-schema" testDirs, err := ioutil.ReadDir(fixtureDir) if err != nil { @@ -48,11 +50,11 @@ func TestProvidersSchema_output(t *testing.T) { defer testChdir(t, td)() providerSource, close := newMockProviderSource(t, map[string][]string{ - "test": []string{"1.2.3"}, + "test": {"1.2.3"}, }) defer close() - p := showFixtureProvider() + p := providersSchemaFixtureProvider() ui := new(cli.MockUi) m := Meta{ testingOverrides: metaOverridesForProvider(p), @@ -109,3 +111,45 @@ type providerSchema struct { ResourceSchemas map[string]interface{} `json:"resource_schemas,omitempty"` DataSourceSchemas map[string]interface{} `json:"data_source_schemas,omitempty"` } + +// testProvider returns a mock provider that is configured for basic +// operation with the configuration in testdata/providers-schema. +func providersSchemaFixtureProvider() *terraform.MockProvider { + p := testProvider() + p.GetProviderSchemaResponse = providersSchemaFixtureSchema() + return p +} + +// providersSchemaFixtureSchema returns a schema suitable for processing the +// configuration in testdata/providers-schema.ß +func providersSchemaFixtureSchema() *providers.GetProviderSchemaResponse { + return &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "region": {Type: cty.String, Optional: true}, + }, + }, + }, + ResourceTypes: map[string]providers.Schema{ + "test_instance": { + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "ami": {Type: cty.String, Optional: true}, + "volumes": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingList, + Attributes: map[string]*configschema.Attribute{ + "size": {Type: cty.String, Required: true}, + "mount_point": {Type: cty.String, Required: true}, + }, + }, + Optional: true, + }, + }, + }, + }, + }, + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/push.go b/vendor/github.com/hashicorp/terraform/command/push.go index 45be43d6..27e6d605 100644 --- a/vendor/github.com/hashicorp/terraform/command/push.go +++ b/vendor/github.com/hashicorp/terraform/command/push.go @@ -23,7 +23,7 @@ func (c *PushCommand) Run(args []string) int { func (c *PushCommand) Help() string { helpText := ` -Usage: terraform push [options] [DIR] +Usage: terraform [global options] push [options] [DIR] This command was for the legacy version of Terraform Enterprise (v1), which has now reached end-of-life. Therefore this command is no longer supported. diff --git a/vendor/github.com/hashicorp/terraform/command/refresh.go b/vendor/github.com/hashicorp/terraform/command/refresh.go index 4ab3932f..77942fd6 100644 --- a/vendor/github.com/hashicorp/terraform/command/refresh.go +++ b/vendor/github.com/hashicorp/terraform/command/refresh.go @@ -4,8 +4,9 @@ import ( "fmt" "strings" - "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/backend" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/tfdiags" ) @@ -15,97 +16,161 @@ type RefreshCommand struct { Meta } -func (c *RefreshCommand) Run(args []string) int { - args = c.Meta.process(args) - cmdFlags := c.Meta.extendedFlagSet("refresh") - cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path") - cmdFlags.IntVar(&c.Meta.parallelism, "parallelism", DefaultParallelism, "parallelism") - cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path") - cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path") - cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state") - cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout") - cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } - if err := cmdFlags.Parse(args); err != nil { - return 1 - } +func (c *RefreshCommand) Run(rawArgs []string) int { + // Parse and apply global view arguments + common, rawArgs := arguments.ParseView(rawArgs) + c.View.Configure(common) - configPath, err := ModulePath(cmdFlags.Args()) - if err != nil { - c.Ui.Error(err.Error()) + // Parse and validate flags + args, diags := arguments.ParseRefresh(rawArgs) + + // Instantiate the view, even if there are flag errors, so that we render + // diagnostics according to the desired view + var view views.Refresh + view = views.NewRefresh(args.ViewType, c.View) + + if diags.HasErrors() { + view.Diagnostics(diags) + view.HelpPrompt() return 1 } - var diags tfdiags.Diagnostics - // Check for user-supplied plugin path + var err error if c.pluginPath, err = c.loadPluginPath(); err != nil { - c.Ui.Error(fmt.Sprintf("Error loading plugin path: %s", err)) + diags = diags.Append(err) + view.Diagnostics(diags) return 1 } - backendConfig, configDiags := c.loadBackendConfig(configPath) - diags = diags.Append(configDiags) - if configDiags.HasErrors() { - c.showDiagnostics(diags) + // FIXME: the -input flag value is needed to initialize the backend and the + // operation, but there is no clear path to pass this value down, so we + // continue to mutate the Meta object state for now. + c.Meta.input = args.InputEnabled + + // FIXME: the -parallelism flag is used to control the concurrency of + // Terraform operations. At the moment, this value is used both to + // initialize the backend via the ContextOpts field inside CLIOpts, and to + // set a largely unused field on the Operation request. Again, there is no + // clear path to pass this value down, so we continue to mutate the Meta + // object state for now. + c.Meta.parallelism = args.Operation.Parallelism + + // Prepare the backend with the backend-specific arguments + be, beDiags := c.PrepareBackend(args.State) + diags = diags.Append(beDiags) + if diags.HasErrors() { + view.Diagnostics(diags) return 1 } - // Load the backend - b, backendDiags := c.Backend(&BackendOpts{ - Config: backendConfig, - }) - diags = diags.Append(backendDiags) - if backendDiags.HasErrors() { - c.showDiagnostics(diags) + // Build the operation request + opReq, opDiags := c.OperationRequest(be, view, args.Operation) + diags = diags.Append(opDiags) + if diags.HasErrors() { + view.Diagnostics(diags) + return 1 + } + + // Collect variable value and add them to the operation request + diags = diags.Append(c.GatherVariables(opReq, args.Vars)) + if diags.HasErrors() { + view.Diagnostics(diags) return 1 } // Before we delegate to the backend, we'll print any warning diagnostics // we've accumulated here, since the backend will start fresh with its own // diagnostics. - c.showDiagnostics(diags) + view.Diagnostics(diags) diags = nil - // Build the operation - opReq := c.Operation(b) - opReq.ConfigDir = configPath - opReq.Type = backend.OperationTypeRefresh - - opReq.ConfigLoader, err = c.initConfigLoader() + // Perform the operation + op, err := c.RunOperation(be, opReq) if err != nil { - c.showDiagnostics(err) + diags = diags.Append(err) + view.Diagnostics(diags) return 1 } - { - var moreDiags tfdiags.Diagnostics - opReq.Variables, moreDiags = c.collectVariableValues() - diags = diags.Append(moreDiags) - if moreDiags.HasErrors() { - c.showDiagnostics(diags) - return 1 - } + if op.State != nil { + view.Outputs(op.State.RootModule().OutputValues) } - op, err := c.RunOperation(b, opReq) - if err != nil { - c.showDiagnostics(err) - return 1 + return op.Result.ExitStatus() +} + +func (c *RefreshCommand) PrepareBackend(args *arguments.State) (backend.Enhanced, tfdiags.Diagnostics) { + // FIXME: we need to apply the state arguments to the meta object here + // because they are later used when initializing the backend. Carving a + // path to pass these arguments to the functions that need them is + // difficult but would make their use easier to understand. + c.Meta.applyStateArguments(args) + + backendConfig, diags := c.loadBackendConfig(".") + if diags.HasErrors() { + return nil, diags } - if op.Result != backend.OperationSuccess { - return op.Result.ExitStatus() + + // Load the backend + be, beDiags := c.Backend(&BackendOpts{ + Config: backendConfig, + }) + diags = diags.Append(beDiags) + if beDiags.HasErrors() { + return nil, diags } - if outputs := outputsAsString(op.State, addrs.RootModuleInstance, true); outputs != "" { - c.Ui.Output(c.Colorize().Color(outputs)) + return be, diags +} + +func (c *RefreshCommand) OperationRequest(be backend.Enhanced, view views.Refresh, args *arguments.Operation, +) (*backend.Operation, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + + // Build the operation + opReq := c.Operation(be) + opReq.ConfigDir = "." + opReq.Hooks = view.Hooks() + opReq.Targets = args.Targets + opReq.Type = backend.OperationTypeRefresh + opReq.View = view.Operation() + + var err error + opReq.ConfigLoader, err = c.initConfigLoader() + if err != nil { + diags = diags.Append(fmt.Errorf("Failed to initialize config loader: %s", err)) + return nil, diags } - return op.Result.ExitStatus() + return opReq, diags +} + +func (c *RefreshCommand) GatherVariables(opReq *backend.Operation, args *arguments.Vars) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + // FIXME the arguments package currently trivially gathers variable related + // arguments in a heterogenous slice, in order to minimize the number of + // code paths gathering variables during the transition to this structure. + // Once all commands that gather variables have been converted to this + // structure, we could move the variable gathering code to the arguments + // package directly, removing this shim layer. + + varArgs := args.All() + items := make([]rawFlag, len(varArgs)) + for i := range varArgs { + items[i].Name = varArgs[i].Name + items[i].Value = varArgs[i].Value + } + c.Meta.variableArgs = rawFlags{items: &items} + opReq.Variables, diags = c.collectVariableValues() + + return diags } func (c *RefreshCommand) Help() string { helpText := ` -Usage: terraform refresh [options] [dir] +Usage: terraform [global options] refresh [options] Update the state file of your infrastructure with metadata that matches the physical resources they are tracking. @@ -116,10 +181,6 @@ Usage: terraform refresh [options] [dir] Options: - -backup=path Path to backup the existing state file before - modifying. Defaults to the "-state-out" path with - ".backup" extension. Set to "-" to disable backup. - -compact-warnings If Terraform produces any warnings that are not accompanied by errors, show them in a more compact form that includes only the summary messages. @@ -132,12 +193,6 @@ Options: -no-color If specified, output won't contain any color. - -state=path Path to read and save state (unless state-out - is specified). Defaults to "terraform.tfstate". - - -state-out=path Path to write updated state file. By default, the - "-state" path will be used. - -target=resource Resource to target. Operation will be limited to this resource and its dependencies. This flag can be used multiple times. @@ -149,10 +204,12 @@ Options: a file. If "terraform.tfvars" or any ".auto.tfvars" files are present, they will be automatically loaded. + -state, state-out, and -backup are legacy options supported for the local + backend only. For more information, see the local backend's documentation. ` return strings.TrimSpace(helpText) } func (c *RefreshCommand) Synopsis() string { - return "Update local state file against real resources" + return "Update the state to match remote systems" } diff --git a/vendor/github.com/hashicorp/terraform/command/refresh_test.go b/vendor/github.com/hashicorp/terraform/command/refresh_test.go index af3bbba8..e593e494 100644 --- a/vendor/github.com/hashicorp/terraform/command/refresh_test.go +++ b/vendor/github.com/hashicorp/terraform/command/refresh_test.go @@ -2,7 +2,6 @@ package command import ( "bytes" - "encoding/json" "fmt" "io/ioutil" "os" @@ -23,27 +22,33 @@ import ( "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/states/statefile" "github.com/hashicorp/terraform/states/statemgr" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/tfdiags" ) var equateEmpty = cmpopts.EquateEmpty() func TestRefresh(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("refresh"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + state := testState() statePath := testStateFile(t, state) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &RefreshCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } - p.GetSchemaReturn = refreshFixtureSchema() + p.GetProviderSchemaResponse = refreshFixtureSchema() p.ReadResourceFn = nil - p.ReadResourceResponse = providers.ReadResourceResponse{ + p.ReadResourceResponse = &providers.ReadResourceResponse{ NewState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("yes"), }), @@ -51,10 +56,11 @@ func TestRefresh(t *testing.T) { args := []string{ "-state", statePath, - testFixturePath("refresh"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if !p.ReadResourceCalled { @@ -87,26 +93,26 @@ func TestRefresh_empty(t *testing.T) { defer testChdir(t, td)() p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &RefreshCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } p.ReadResourceFn = nil - p.ReadResourceResponse = providers.ReadResourceResponse{ + p.ReadResourceResponse = &providers.ReadResourceResponse{ NewState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("yes"), }), } - args := []string{ - td, - } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + args := []string{} + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if p.ReadResourceCalled { @@ -115,6 +121,12 @@ func TestRefresh_empty(t *testing.T) { } func TestRefresh_lockedState(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("refresh"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + state := testState() statePath := testStateFile(t, state) @@ -125,17 +137,17 @@ func TestRefresh_lockedState(t *testing.T) { defer unlock() p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &RefreshCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } - p.GetSchemaReturn = refreshFixtureSchema() + p.GetProviderSchemaResponse = refreshFixtureSchema() p.ReadResourceFn = nil - p.ReadResourceResponse = providers.ReadResourceResponse{ + p.ReadResourceResponse = &providers.ReadResourceResponse{ NewState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("yes"), }), @@ -143,16 +155,17 @@ func TestRefresh_lockedState(t *testing.T) { args := []string{ "-state", statePath, - testFixturePath("refresh"), } - if code := c.Run(args); code == 0 { + code := c.Run(args) + output := done(t) + if code == 0 { t.Fatal("expected error") } - output := ui.ErrorWriter.String() - if !strings.Contains(output, "lock") { - t.Fatal("command output does not look like a lock error:", output) + got := output.Stderr() + if !strings.Contains(got, "lock") { + t.Fatal("command output does not look like a lock error:", got) } } @@ -170,17 +183,17 @@ func TestRefresh_cwd(t *testing.T) { statePath := testStateFile(t, state) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &RefreshCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } - p.GetSchemaReturn = refreshFixtureSchema() + p.GetProviderSchemaResponse = refreshFixtureSchema() p.ReadResourceFn = nil - p.ReadResourceResponse = providers.ReadResourceResponse{ + p.ReadResourceResponse = &providers.ReadResourceResponse{ NewState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("yes"), }), @@ -189,8 +202,10 @@ func TestRefresh_cwd(t *testing.T) { args := []string{ "-state", statePath, } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if !p.ReadResourceCalled { @@ -216,6 +231,12 @@ func TestRefresh_cwd(t *testing.T) { } func TestRefresh_defaultState(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("refresh"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + originalState := testState() // Write the state file in a temporary directory with the @@ -242,17 +263,17 @@ func TestRefresh_defaultState(t *testing.T) { defer os.Chdir(cwd) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &RefreshCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } - p.GetSchemaReturn = refreshFixtureSchema() + p.GetProviderSchemaResponse = refreshFixtureSchema() p.ReadResourceFn = nil - p.ReadResourceResponse = providers.ReadResourceResponse{ + p.ReadResourceResponse = &providers.ReadResourceResponse{ NewState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("yes"), }), @@ -260,10 +281,11 @@ func TestRefresh_defaultState(t *testing.T) { args := []string{ "-state", statePath, - testFixturePath("refresh"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } if !p.ReadResourceCalled { @@ -292,6 +314,12 @@ func TestRefresh_defaultState(t *testing.T) { } func TestRefresh_outPath(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("refresh"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + state := testState() statePath := testStateFile(t, state) @@ -305,17 +333,17 @@ func TestRefresh_outPath(t *testing.T) { os.Remove(outPath) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &RefreshCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } - p.GetSchemaReturn = refreshFixtureSchema() + p.GetProviderSchemaResponse = refreshFixtureSchema() p.ReadResourceFn = nil - p.ReadResourceResponse = providers.ReadResourceResponse{ + p.ReadResourceResponse = &providers.ReadResourceResponse{ NewState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("yes"), }), @@ -324,10 +352,11 @@ func TestRefresh_outPath(t *testing.T) { args := []string{ "-state", statePath, "-state-out", outPath, - testFixturePath("refresh"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } newState := testStateRead(t, statePath) @@ -355,49 +384,62 @@ func TestRefresh_outPath(t *testing.T) { } func TestRefresh_var(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("refresh-var"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + state := testState() statePath := testStateFile(t, state) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &RefreshCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } - p.GetSchemaReturn = refreshVarFixtureSchema() + p.GetProviderSchemaResponse = refreshVarFixtureSchema() args := []string{ "-var", "foo=bar", "-state", statePath, - testFixturePath("refresh-var"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } - if !p.ConfigureCalled { + if !p.ConfigureProviderCalled { t.Fatal("configure should be called") } - if got, want := p.ConfigureRequest.Config.GetAttr("value"), cty.StringVal("bar"); !want.RawEquals(got) { + if got, want := p.ConfigureProviderRequest.Config.GetAttr("value"), cty.StringVal("bar"); !want.RawEquals(got) { t.Fatalf("wrong provider configuration\ngot: %#v\nwant: %#v", got, want) } } func TestRefresh_varFile(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("refresh-var"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + state := testState() statePath := testStateFile(t, state) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &RefreshCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } - p.GetSchemaReturn = refreshVarFixtureSchema() + p.GetProviderSchemaResponse = refreshVarFixtureSchema() varFilePath := testTempFile(t) if err := ioutil.WriteFile(varFilePath, []byte(refreshVarFile), 0644); err != nil { @@ -407,66 +449,70 @@ func TestRefresh_varFile(t *testing.T) { args := []string{ "-var-file", varFilePath, "-state", statePath, - testFixturePath("refresh-var"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } - if !p.ConfigureCalled { + if !p.ConfigureProviderCalled { t.Fatal("configure should be called") } - if got, want := p.ConfigureRequest.Config.GetAttr("value"), cty.StringVal("bar"); !want.RawEquals(got) { + if got, want := p.ConfigureProviderRequest.Config.GetAttr("value"), cty.StringVal("bar"); !want.RawEquals(got) { t.Fatalf("wrong provider configuration\ngot: %#v\nwant: %#v", got, want) } } func TestRefresh_varFileDefault(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("refresh-var"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + state := testState() statePath := testStateFile(t, state) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &RefreshCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } - p.GetSchemaReturn = refreshVarFixtureSchema() + p.GetProviderSchemaResponse = refreshVarFixtureSchema() - varFileDir := testTempDir(t) - varFilePath := filepath.Join(varFileDir, "terraform.tfvars") + varFilePath := filepath.Join(td, "terraform.tfvars") if err := ioutil.WriteFile(varFilePath, []byte(refreshVarFile), 0644); err != nil { t.Fatalf("err: %s", err) } - cwd, err := os.Getwd() - if err != nil { - t.Fatalf("err: %s", err) - } - if err := os.Chdir(varFileDir); err != nil { - t.Fatalf("err: %s", err) - } - defer os.Chdir(cwd) - args := []string{ "-state", statePath, - testFixturePath("refresh-var"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } - if !p.ConfigureCalled { + if !p.ConfigureProviderCalled { t.Fatal("configure should be called") } - if got, want := p.ConfigureRequest.Config.GetAttr("value"), cty.StringVal("bar"); !want.RawEquals(got) { + if got, want := p.ConfigureProviderRequest.Config.GetAttr("value"), cty.StringVal("bar"); !want.RawEquals(got) { t.Fatalf("wrong provider configuration\ngot: %#v\nwant: %#v", got, want) } } func TestRefresh_varsUnset(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("refresh-unset-var"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + // Disable test mode so input would be asked test = false defer func() { test = true }() @@ -478,18 +524,22 @@ func TestRefresh_varsUnset(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, done := testView(t) c := &RefreshCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "ami": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -497,14 +547,21 @@ func TestRefresh_varsUnset(t *testing.T) { args := []string{ "-state", statePath, - testFixturePath("refresh-unset-var"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } } func TestRefresh_backup(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("refresh"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + state := testState() statePath := testStateFile(t, state) @@ -533,17 +590,17 @@ func TestRefresh_backup(t *testing.T) { os.Remove(backupPath) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &RefreshCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } - p.GetSchemaReturn = refreshFixtureSchema() + p.GetProviderSchemaResponse = refreshFixtureSchema() p.ReadResourceFn = nil - p.ReadResourceResponse = providers.ReadResourceResponse{ + p.ReadResourceResponse = &providers.ReadResourceResponse{ NewState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("changed"), }), @@ -553,10 +610,11 @@ func TestRefresh_backup(t *testing.T) { "-state", statePath, "-state-out", outPath, "-backup", backupPath, - testFixturePath("refresh"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } newState := testStateRead(t, statePath) @@ -584,6 +642,12 @@ func TestRefresh_backup(t *testing.T) { } func TestRefresh_disableBackup(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("refresh"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + state := testState() statePath := testStateFile(t, state) @@ -597,17 +661,17 @@ func TestRefresh_disableBackup(t *testing.T) { os.Remove(outPath) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &RefreshCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } - p.GetSchemaReturn = refreshFixtureSchema() + p.GetProviderSchemaResponse = refreshFixtureSchema() p.ReadResourceFn = nil - p.ReadResourceResponse = providers.ReadResourceResponse{ + p.ReadResourceResponse = &providers.ReadResourceResponse{ NewState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("yes"), }), @@ -617,10 +681,11 @@ func TestRefresh_disableBackup(t *testing.T) { "-state", statePath, "-state-out", outPath, "-backup", "-", - testFixturePath("refresh"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } newState := testStateRead(t, statePath) @@ -653,23 +718,31 @@ func TestRefresh_disableBackup(t *testing.T) { } func TestRefresh_displaysOutputs(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("refresh-output"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + state := testState() statePath := testStateFile(t, state) p := testProvider() - ui := new(cli.MockUi) + view, done := testView(t) c := &RefreshCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "ami": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -677,48 +750,199 @@ func TestRefresh_displaysOutputs(t *testing.T) { args := []string{ "-state", statePath, - testFixturePath("refresh-output"), } - if code := c.Run(args); code != 0 { - t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } // Test that outputs were displayed outputValue := "foo.example.com" - actual := ui.OutputWriter.String() + actual := output.Stdout() if !strings.Contains(actual, outputValue) { t.Fatalf("Expected:\n%s\n\nTo include: %q", actual, outputValue) } } -// newInstanceState creates a new states.ResourceInstanceObjectSrc with the -// given value for its single id attribute. It is named newInstanceState for -// historical reasons, because it was originally written for the poorly-named -// terraform.InstanceState type. -func newInstanceState(id string) *states.ResourceInstanceObjectSrc { - attrs := map[string]interface{}{ - "id": id, +// Config with multiple resources, targeting refresh of a subset +func TestRefresh_targeted(t *testing.T) { + td := tempDir(t) + testCopyDir(t, testFixturePath("refresh-targeted"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + state := testState() + statePath := testStateFile(t, state) + + p := testProvider() + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ + "test_instance": { + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + }, + }, + }, + }, } - attrsJSON, err := json.Marshal(attrs) - if err != nil { - panic(fmt.Sprintf("failed to marshal attributes: %s", err)) // should never happen + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { + return providers.PlanResourceChangeResponse{ + PlannedState: req.ProposedNewState, + } + } + + view, done := testView(t) + c := &RefreshCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + View: view, + }, + } + + args := []string{ + "-target", "test_instance.foo", + "-state", statePath, } - return &states.ResourceInstanceObjectSrc{ - AttrsJSON: attrsJSON, - Status: states.ObjectReady, + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) } + + got := output.Stdout() + if want := "test_instance.foo: Refreshing"; !strings.Contains(got, want) { + t.Fatalf("expected output to contain %q, got:\n%s", want, got) + } + if doNotWant := "test_instance.bar: Refreshing"; strings.Contains(got, doNotWant) { + t.Fatalf("expected output not to contain %q, got:\n%s", doNotWant, got) + } +} + +// Diagnostics for invalid -target flags +func TestRefresh_targetFlagsDiags(t *testing.T) { + testCases := map[string]string{ + "test_instance.": "Dot must be followed by attribute name.", + "test_instance": "Resource specification must include a resource type and name.", + } + + for target, wantDiag := range testCases { + t.Run(target, func(t *testing.T) { + td := testTempDir(t) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + view, done := testView(t) + c := &RefreshCommand{ + Meta: Meta{ + View: view, + }, + } + + args := []string{ + "-target", target, + } + code := c.Run(args) + output := done(t) + if code != 1 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + + got := output.Stderr() + if !strings.Contains(got, target) { + t.Fatalf("bad error output, want %q, got:\n%s", target, got) + } + if !strings.Contains(got, wantDiag) { + t.Fatalf("bad error output, want %q, got:\n%s", wantDiag, got) + } + }) + } +} + +func TestRefresh_warnings(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("apply"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + p := testProvider() + p.GetProviderSchemaResponse = refreshFixtureSchema() + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { + return providers.PlanResourceChangeResponse{ + PlannedState: req.ProposedNewState, + Diagnostics: tfdiags.Diagnostics{ + tfdiags.SimpleWarning("warning 1"), + tfdiags.SimpleWarning("warning 2"), + }, + } + } + + t.Run("full warnings", func(t *testing.T) { + view, done := testView(t) + c := &RefreshCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + View: view, + }, + } + + code := c.Run([]string{}) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + wantWarnings := []string{ + "warning 1", + "warning 2", + } + for _, want := range wantWarnings { + if !strings.Contains(output.Stdout(), want) { + t.Errorf("missing warning %s", want) + } + } + }) + + t.Run("compact warnings", func(t *testing.T) { + view, done := testView(t) + c := &RefreshCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + View: view, + }, + } + + code := c.Run([]string{"-compact-warnings"}) + output := done(t) + if code != 0 { + t.Fatalf("bad: %d\n\n%s", code, output.Stderr()) + } + // the output should contain 2 warnings and a message about -compact-warnings + wantWarnings := []string{ + "warning 1", + "warning 2", + "To see the full warning notes, run Terraform without -compact-warnings.", + } + for _, want := range wantWarnings { + if !strings.Contains(output.Stdout(), want) { + t.Errorf("missing warning %s", want) + } + } + }) } -// refreshFixtureSchema returns a schema suitable for processing the // configuration in testdata/refresh . This schema should be // assigned to a mock provider named "test". -func refreshFixtureSchema() *terraform.ProviderSchema { - return &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ +func refreshFixtureSchema() *providers.GetProviderSchemaResponse { + return &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "ami": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -728,17 +952,21 @@ func refreshFixtureSchema() *terraform.ProviderSchema { // refreshVarFixtureSchema returns a schema suitable for processing the // configuration in testdata/refresh-var . This schema should be // assigned to a mock provider named "test". -func refreshVarFixtureSchema() *terraform.ProviderSchema { - return &terraform.ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "value": {Type: cty.String, Optional: true}, +func refreshVarFixtureSchema() *providers.GetProviderSchemaResponse { + return &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "value": {Type: cty.String, Optional: true}, + }, }, }, - ResourceTypes: map[string]*configschema.Block{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + }, }, }, }, diff --git a/vendor/github.com/hashicorp/terraform/command/show.go b/vendor/github.com/hashicorp/terraform/command/show.go index 3bf034c9..e8cc8804 100644 --- a/vendor/github.com/hashicorp/terraform/command/show.go +++ b/vendor/github.com/hashicorp/terraform/command/show.go @@ -6,10 +6,11 @@ import ( "strings" "github.com/hashicorp/terraform/backend" - localBackend "github.com/hashicorp/terraform/backend/local" + "github.com/hashicorp/terraform/command/arguments" "github.com/hashicorp/terraform/command/format" "github.com/hashicorp/terraform/command/jsonplan" "github.com/hashicorp/terraform/command/jsonstate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/plans/planfile" "github.com/hashicorp/terraform/states/statefile" @@ -68,6 +69,9 @@ func (c *ShowCommand) Run(args []string) int { return 1 } + // This is a read-only command + c.ignoreRemoteBackendVersionConflict(b) + // the show command expects the config dir to always be the cwd cwd, err := os.Getwd() if err != nil { @@ -155,15 +159,8 @@ func (c *ShowCommand) Run(args []string) int { return 0 } - // FIXME: We currently call into the local backend for this, since - // the "terraform plan" logic lives there and our package call graph - // means we can't orient this dependency the other way around. In - // future we'll hopefully be able to refactor the backend architecture - // a little so that CLI UI rendering always happens in this "command" - // package rather than in the backends themselves, but for now we're - // accepting this oddity because "terraform show" is a less commonly - // used way to render a plan than "terraform plan" is. - localBackend.RenderPlan(plan, stateFile.State, schemas, c.Ui, c.Colorize()) + view := views.NewShow(arguments.ViewHuman, c.View) + view.Plan(plan, schemas) return 0 } @@ -193,7 +190,7 @@ func (c *ShowCommand) Run(args []string) int { func (c *ShowCommand) Help() string { helpText := ` -Usage: terraform show [options] [path] +Usage: terraform [global options] show [options] [path] Reads and outputs a Terraform state or plan file in a human-readable form. If no path is specified, the current state will be shown. @@ -209,7 +206,7 @@ Options: } func (c *ShowCommand) Synopsis() string { - return "Inspect Terraform state or plan" + return "Show the current state or a saved plan" } // getPlanFromPath returns a plan and statefile if the user-supplied path points diff --git a/vendor/github.com/hashicorp/terraform/command/show_test.go b/vendor/github.com/hashicorp/terraform/command/show_test.go index 93f300d4..a520e4f8 100644 --- a/vendor/github.com/hashicorp/terraform/command/show_test.go +++ b/vendor/github.com/hashicorp/terraform/command/show_test.go @@ -22,10 +22,12 @@ import ( func TestShow(t *testing.T) { ui := new(cli.MockUi) + view, _ := testView(t) c := &ShowCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -39,24 +41,23 @@ func TestShow(t *testing.T) { } func TestShow_noArgs(t *testing.T) { + // Get a temp cwd + tmp, cwd := testCwd(t) + defer testFixCwd(t, tmp, cwd) // Create the default state - statePath := testStateFile(t, testState()) - stateDir := filepath.Dir(statePath) - defer os.RemoveAll(stateDir) - defer testChdir(t, stateDir)() + testStateFileDefault(t, testState()) ui := new(cli.MockUi) + view, _ := testView(t) c := &ShowCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } - // the statefile created by testStateFile is named state.tfstate - // so one arg is required - args := []string{"state.tfstate"} - if code := c.Run(args); code != 0 { + if code := c.Run([]string{}); code != 0 { t.Fatalf("bad: \n%s", ui.OutputWriter.String()) } @@ -93,10 +94,12 @@ func TestShow_aliasedProvider(t *testing.T) { defer testChdir(t, stateDir)() ui := new(cli.MockUi) + view, _ := testView(t) c := &ShowCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -121,10 +124,12 @@ func TestShow_noArgsNoState(t *testing.T) { defer testChdir(t, stateDir)() ui := new(cli.MockUi) + view, _ := testView(t) c := &ShowCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -139,10 +144,12 @@ func TestShow_plan(t *testing.T) { planPath := testPlanFileNoop(t) ui := cli.NewMockUi() + view, done := testView(t) c := &ShowCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -154,7 +161,7 @@ func TestShow_plan(t *testing.T) { } want := `Terraform will perform the following actions` - got := ui.OutputWriter.String() + got := done(t).Stdout() if !strings.Contains(got, want) { t.Errorf("missing expected output\nwant: %s\ngot:\n%s", want, got) } @@ -164,10 +171,12 @@ func TestShow_planWithChanges(t *testing.T) { planPathWithChanges := showFixturePlanFile(t, plans.DeleteThenCreate) ui := cli.NewMockUi() + view, done := testView(t) c := &ShowCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(showFixtureProvider()), Ui: ui, + View: view, }, } @@ -180,20 +189,94 @@ func TestShow_planWithChanges(t *testing.T) { } want := `test_instance.foo must be replaced` - got := ui.OutputWriter.String() + got := done(t).Stdout() if !strings.Contains(got, want) { t.Errorf("missing expected output\nwant: %s\ngot:\n%s", want, got) } } +func TestShow_planWithForceReplaceChange(t *testing.T) { + // The main goal of this test is to see that the "replace by request" + // resource instance action reason can round-trip through a plan file and + // be reflected correctly in the "terraform show" output, the same way + // as it would appear in "terraform plan" output. + + _, snap := testModuleWithSnapshot(t, "show") + plannedVal := cty.ObjectVal(map[string]cty.Value{ + "id": cty.UnknownVal(cty.String), + "ami": cty.StringVal("bar"), + }) + priorValRaw, err := plans.NewDynamicValue(cty.NullVal(plannedVal.Type()), plannedVal.Type()) + if err != nil { + t.Fatal(err) + } + plannedValRaw, err := plans.NewDynamicValue(plannedVal, plannedVal.Type()) + if err != nil { + t.Fatal(err) + } + plan := testPlan(t) + plan.Changes.SyncWrapper().AppendResourceInstanceChange(&plans.ResourceInstanceChangeSrc{ + Addr: addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + ProviderAddr: addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ChangeSrc: plans.ChangeSrc{ + Action: plans.CreateThenDelete, + Before: priorValRaw, + After: plannedValRaw, + }, + ActionReason: plans.ResourceInstanceReplaceByRequest, + }) + planFilePath := testPlanFile( + t, + snap, + states.NewState(), + plan, + ) + + ui := cli.NewMockUi() + view, done := testView(t) + c := &ShowCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(showFixtureProvider()), + Ui: ui, + View: view, + }, + } + + args := []string{ + planFilePath, + } + + if code := c.Run(args); code != 0 { + t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + } + + got := done(t).Stdout() + if want := `test_instance.foo will be replaced, as requested`; !strings.Contains(got, want) { + t.Errorf("wrong output\ngot:\n%s\n\nwant substring: %s", got, want) + } + if want := `Plan: 1 to add, 0 to change, 1 to destroy.`; !strings.Contains(got, want) { + t.Errorf("wrong output\ngot:\n%s\n\nwant substring: %s", got, want) + } + +} + func TestShow_plan_json(t *testing.T) { planPath := showFixturePlanFile(t, plans.Create) ui := new(cli.MockUi) + view, _ := testView(t) c := &ShowCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(showFixtureProvider()), Ui: ui, + View: view, }, } @@ -212,10 +295,12 @@ func TestShow_state(t *testing.T) { defer os.RemoveAll(filepath.Dir(statePath)) ui := new(cli.MockUi) + view, _ := testView(t) c := &ShowCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -255,9 +340,11 @@ func TestShow_json_output(t *testing.T) { p := showFixtureProvider() ui := new(cli.MockUi) + view, _ := testView(t) m := Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, ProviderSource: providerSource, } @@ -325,6 +412,88 @@ func TestShow_json_output(t *testing.T) { } } +func TestShow_json_output_sensitive(t *testing.T) { + td := tempDir(t) + inputDir := "testdata/show-json-sensitive" + testCopyDir(t, inputDir, td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + providerSource, close := newMockProviderSource(t, map[string][]string{"test": {"1.2.3"}}) + defer close() + + p := showFixtureSensitiveProvider() + ui := new(cli.MockUi) + view, _ := testView(t) + m := Meta{ + testingOverrides: metaOverridesForProvider(p), + Ui: ui, + View: view, + ProviderSource: providerSource, + } + + // init + ic := &InitCommand{ + Meta: m, + } + if code := ic.Run([]string{}); code != 0 { + t.Fatalf("init failed\n%s", ui.ErrorWriter) + } + + // flush init output + ui.OutputWriter.Reset() + + pc := &PlanCommand{ + Meta: m, + } + + args := []string{ + "-out=terraform.plan", + } + + if code := pc.Run(args); code != 0 { + fmt.Println(ui.OutputWriter.String()) + t.Fatalf("wrong exit status %d; want 0\nstderr: %s", code, ui.ErrorWriter.String()) + } + + // flush the plan output from the mock ui + ui.OutputWriter.Reset() + sc := &ShowCommand{ + Meta: m, + } + + args = []string{ + "-json", + "terraform.plan", + } + defer os.Remove("terraform.plan") + + if code := sc.Run(args); code != 0 { + t.Fatalf("wrong exit status %d; want 0\nstderr: %s", code, ui.ErrorWriter.String()) + } + + // compare ui output to wanted output + var got, want plan + + gotString := ui.OutputWriter.String() + json.Unmarshal([]byte(gotString), &got) + + wantFile, err := os.Open("output.json") + if err != nil { + t.Fatalf("err: %s", err) + } + defer wantFile.Close() + byteValue, err := ioutil.ReadAll(wantFile) + if err != nil { + t.Fatalf("err: %s", err) + } + json.Unmarshal([]byte(byteValue), &want) + + if !cmp.Equal(got, want) { + t.Fatalf("wrong result:\n %v\n", cmp.Diff(got, want)) + } +} + // similar test as above, without the plan func TestShow_json_output_state(t *testing.T) { fixtureDir := "testdata/show-json-state" @@ -352,9 +521,11 @@ func TestShow_json_output_state(t *testing.T) { p := showFixtureProvider() ui := new(cli.MockUi) + view, _ := testView(t) m := Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, ProviderSource: providerSource, } @@ -408,18 +579,48 @@ func TestShow_json_output_state(t *testing.T) { // showFixtureSchema returns a schema suitable for processing the configuration // in testdata/show. This schema should be assigned to a mock provider // named "test". -func showFixtureSchema() *terraform.ProviderSchema { - return &terraform.ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "region": {Type: cty.String, Optional: true}, +func showFixtureSchema() *providers.GetProviderSchemaResponse { + return &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "region": {Type: cty.String, Optional: true}, + }, }, }, - ResourceTypes: map[string]*configschema.Block{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "ami": {Type: cty.String, Optional: true}, + }, + }, + }, + }, + } +} + +// showFixtureSensitiveSchema returns a schema suitable for processing the configuration +// in testdata/show. This schema should be assigned to a mock provider +// named "test". It includes a sensitive attribute. +func showFixtureSensitiveSchema() *providers.GetProviderSchemaResponse { + return &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "ami": {Type: cty.String, Optional: true}, + "region": {Type: cty.String, Optional: true}, + }, + }, + }, + ResourceTypes: map[string]providers.Schema{ + "test_instance": { + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "ami": {Type: cty.String, Optional: true}, + "password": {Type: cty.String, Optional: true, Sensitive: true}, + }, }, }, }, @@ -428,23 +629,28 @@ func showFixtureSchema() *terraform.ProviderSchema { // showFixtureProvider returns a mock provider that is configured for basic // operation with the configuration in testdata/show. This mock has -// GetSchemaReturn, PlanResourceChangeFn, and ApplyResourceChangeFn populated, +// GetSchemaResponse, PlanResourceChangeFn, and ApplyResourceChangeFn populated, // with the plan/apply steps just passing through the data determined by // Terraform Core. func showFixtureProvider() *terraform.MockProvider { p := testProvider() - p.GetSchemaReturn = showFixtureSchema() + p.GetProviderSchemaResponse = showFixtureSchema() p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { idVal := req.ProposedNewState.GetAttr("id") amiVal := req.ProposedNewState.GetAttr("ami") if idVal.IsNull() { idVal = cty.UnknownVal(cty.String) } + var reqRep []cty.Path + if amiVal.RawEquals(cty.StringVal("force-replace")) { + reqRep = append(reqRep, cty.GetAttrPath("ami")) + } return providers.PlanResourceChangeResponse{ PlannedState: cty.ObjectVal(map[string]cty.Value{ "id": idVal, "ami": amiVal, }), + RequiresReplace: reqRep, } } p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { @@ -463,6 +669,43 @@ func showFixtureProvider() *terraform.MockProvider { return p } +// showFixtureSensitiveProvider returns a mock provider that is configured for basic +// operation with the configuration in testdata/show. This mock has +// GetSchemaResponse, PlanResourceChangeFn, and ApplyResourceChangeFn populated, +// with the plan/apply steps just passing through the data determined by +// Terraform Core. It also has a sensitive attribute in the provider schema. +func showFixtureSensitiveProvider() *terraform.MockProvider { + p := testProvider() + p.GetProviderSchemaResponse = showFixtureSensitiveSchema() + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { + idVal := req.ProposedNewState.GetAttr("id") + if idVal.IsNull() { + idVal = cty.UnknownVal(cty.String) + } + return providers.PlanResourceChangeResponse{ + PlannedState: cty.ObjectVal(map[string]cty.Value{ + "id": idVal, + "ami": req.ProposedNewState.GetAttr("ami"), + "password": req.ProposedNewState.GetAttr("password"), + }), + } + } + p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { + idVal := req.PlannedState.GetAttr("id") + if !idVal.IsKnown() { + idVal = cty.StringVal("placeholder") + } + return providers.ApplyResourceChangeResponse{ + NewState: cty.ObjectVal(map[string]cty.Value{ + "id": idVal, + "ami": req.PlannedState.GetAttr("ami"), + "password": req.PlannedState.GetAttr("password"), + }), + } + } + return p +} + // showFixturePlanFile creates a plan file at a temporary location containing a // single change to create or update the test_instance.foo that is included in the "show" // test fixture, returning the location of that plan file. diff --git a/vendor/github.com/hashicorp/terraform/command/state_command.go b/vendor/github.com/hashicorp/terraform/command/state_command.go index 7e3a6af1..5e7915b6 100644 --- a/vendor/github.com/hashicorp/terraform/command/state_command.go +++ b/vendor/github.com/hashicorp/terraform/command/state_command.go @@ -18,7 +18,7 @@ func (c *StateCommand) Run(args []string) int { func (c *StateCommand) Help() string { helpText := ` -Usage: terraform state [options] [args] +Usage: terraform [global options] state [options] [args] This command has subcommands for advanced state management. diff --git a/vendor/github.com/hashicorp/terraform/command/state_list.go b/vendor/github.com/hashicorp/terraform/command/state_list.go index 8c8a2390..5e2f2527 100644 --- a/vendor/github.com/hashicorp/terraform/command/state_list.go +++ b/vendor/github.com/hashicorp/terraform/command/state_list.go @@ -40,6 +40,9 @@ func (c *StateListCommand) Run(args []string) int { return 1 } + // This is a read-only command + c.ignoreRemoteBackendVersionConflict(b) + // Get the state env, err := c.Workspace() if err != nil { @@ -58,7 +61,7 @@ func (c *StateListCommand) Run(args []string) int { state := stateMgr.State() if state == nil { - c.Ui.Error(fmt.Sprintf(errStateNotFound)) + c.Ui.Error(errStateNotFound) return 1 } @@ -89,7 +92,7 @@ func (c *StateListCommand) Run(args []string) int { func (c *StateListCommand) Help() string { helpText := ` -Usage: terraform state list [options] [address...] +Usage: terraform [global options] state list [options] [address...] List resources in the Terraform state. diff --git a/vendor/github.com/hashicorp/terraform/command/state_list_test.go b/vendor/github.com/hashicorp/terraform/command/state_list_test.go index 1a275db2..e66a7968 100644 --- a/vendor/github.com/hashicorp/terraform/command/state_list_test.go +++ b/vendor/github.com/hashicorp/terraform/command/state_list_test.go @@ -202,6 +202,75 @@ func TestStateList_noState(t *testing.T) { } } +func TestStateList_modules(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("state-list-nested-modules"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + p := testProvider() + ui := cli.NewMockUi() + c := &StateListCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + Ui: ui, + }, + } + + t.Run("list resources in module and submodules", func(t *testing.T) { + args := []string{"module.nest"} + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d", code) + } + + // resources in the module and any submodules should be included in the outputs + expected := "module.nest.test_instance.nest\nmodule.nest.module.subnest.test_instance.subnest\n" + actual := ui.OutputWriter.String() + if actual != expected { + t.Fatalf("Expected:\n%q\n\nTo equal: %q", actual, expected) + } + }) + + t.Run("submodule has resources only", func(t *testing.T) { + // now get the state for a module that has no resources, only another nested module + ui.OutputWriter.Reset() + args := []string{"module.nonexist"} + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d", code) + } + expected := "module.nonexist.module.child.test_instance.child\n" + actual := ui.OutputWriter.String() + if actual != expected { + t.Fatalf("Expected:\n%q\n\nTo equal: %q", actual, expected) + } + }) + + t.Run("expanded module", func(t *testing.T) { + // finally get the state for a module with an index + ui.OutputWriter.Reset() + args := []string{"module.count"} + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d", code) + } + expected := "module.count[0].test_instance.count\nmodule.count[1].test_instance.count\n" + actual := ui.OutputWriter.String() + if actual != expected { + t.Fatalf("Expected:\n%q\n\nTo equal: %q", actual, expected) + } + }) + + t.Run("completely nonexistent module", func(t *testing.T) { + // finally get the state for a module with an index + ui.OutputWriter.Reset() + args := []string{"module.notevenalittlebit"} + if code := c.Run(args); code != 1 { + t.Fatalf("bad: %d", code) + } + }) + +} + const testStateListOutput = ` test_instance.foo ` diff --git a/vendor/github.com/hashicorp/terraform/command/state_meta.go b/vendor/github.com/hashicorp/terraform/command/state_meta.go index bc70649a..50ff5d38 100644 --- a/vendor/github.com/hashicorp/terraform/command/state_meta.go +++ b/vendor/github.com/hashicorp/terraform/command/state_meta.go @@ -41,6 +41,14 @@ func (c *StateMeta) State() (statemgr.Full, error) { if err != nil { return nil, err } + + // Check remote Terraform version is compatible + remoteVersionDiags := c.remoteBackendVersionCheck(b, workspace) + c.showDiagnostics(remoteVersionDiags) + if remoteVersionDiags.HasErrors() { + return nil, fmt.Errorf("Error checking remote Terraform version") + } + // Get the state s, err := b.StateMgr(workspace) if err != nil { @@ -95,24 +103,32 @@ func (c *StateMeta) lookupResourceInstanceAddr(state *states.State, allowMissing case addrs.ModuleInstance: // Matches all instances within the indicated module and all of its // descendent modules. + + // found is used to identify cases where the selected module has no + // resources, but one or more of its submodules does. + found := false ms := state.Module(addr) - if ms == nil { - if !allowMissing { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Unknown module", - fmt.Sprintf(`The current state contains no module at %s. If you've just added this module to the configuration, you must run "terraform apply" first to create the module's entry in the state.`, addr), - )) - } - break + if ms != nil { + found = true + ret = append(ret, c.collectModuleResourceInstances(ms)...) } - ret = append(ret, c.collectModuleResourceInstances(ms)...) for _, cms := range state.Modules { - candidateAddr := ms.Addr - if len(candidateAddr) > len(addr) && candidateAddr[:len(addr)].Equal(addr) { - ret = append(ret, c.collectModuleResourceInstances(cms)...) + if !addr.Equal(cms.Addr) { + if addr.IsAncestor(cms.Addr) || addr.TargetContains(cms.Addr) { + found = true + ret = append(ret, c.collectModuleResourceInstances(cms)...) + } } } + + if found == false && !allowMissing { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Unknown module", + fmt.Sprintf(`The current state contains no module at %s. If you've just added this module to the configuration, you must run "terraform apply" first to create the module's entry in the state.`, addr), + )) + } + case addrs.AbsResource: // Matches all instances of the specific selected resource. rs := state.Resource(addr) diff --git a/vendor/github.com/hashicorp/terraform/command/state_mv.go b/vendor/github.com/hashicorp/terraform/command/state_mv.go index dcaaf23a..6691aad3 100644 --- a/vendor/github.com/hashicorp/terraform/command/state_mv.go +++ b/vendor/github.com/hashicorp/terraform/command/state_mv.go @@ -1,12 +1,13 @@ package command import ( - "context" "fmt" "strings" "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/command/arguments" "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/tfdiags" "github.com/mitchellh/cli" @@ -23,7 +24,7 @@ func (c *StateMvCommand) Run(args []string) int { var backupPathOut, statePathOut string var dryRun bool - cmdFlags := c.Meta.defaultFlagSet("state mv") + cmdFlags := c.Meta.ignoreRemoteVersionFlagSet("state mv") cmdFlags.BoolVar(&dryRun, "dry-run", false, "dry run") cmdFlags.StringVar(&c.backupPath, "backup", "-", "backup") cmdFlags.StringVar(&backupPathOut, "backup-out", "-", "backup") @@ -49,12 +50,16 @@ func (c *StateMvCommand) Run(args []string) int { } if c.stateLock { - stateLocker := clistate.NewLocker(context.Background(), c.stateLockTimeout, c.Ui, c.Colorize()) - if err := stateLocker.Lock(stateFromMgr, "state-mv"); err != nil { - c.Ui.Error(fmt.Sprintf("Error locking source state: %s", err)) + stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View)) + if diags := stateLocker.Lock(stateFromMgr, "state-mv"); diags.HasErrors() { + c.showDiagnostics(diags) return 1 } - defer stateLocker.Unlock(nil) + defer func() { + if diags := stateLocker.Unlock(); diags.HasErrors() { + c.showDiagnostics(diags) + } + }() } if err := stateFromMgr.RefreshState(); err != nil { @@ -64,7 +69,7 @@ func (c *StateMvCommand) Run(args []string) int { stateFrom := stateFromMgr.State() if stateFrom == nil { - c.Ui.Error(fmt.Sprintf(errStateNotFound)) + c.Ui.Error(errStateNotFound) return 1 } @@ -83,12 +88,16 @@ func (c *StateMvCommand) Run(args []string) int { } if c.stateLock { - stateLocker := clistate.NewLocker(context.Background(), c.stateLockTimeout, c.Ui, c.Colorize()) - if err := stateLocker.Lock(stateToMgr, "state-mv"); err != nil { - c.Ui.Error(fmt.Sprintf("Error locking destination state: %s", err)) + stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View)) + if diags := stateLocker.Lock(stateToMgr, "state-mv"); diags.HasErrors() { + c.showDiagnostics(diags) return 1 } - defer stateLocker.Unlock(nil) + defer func() { + if diags := stateLocker.Unlock(); diags.HasErrors() { + c.showDiagnostics(diags) + } + }() } if err := stateToMgr.RefreshState(); err != nil { @@ -139,7 +148,7 @@ func (c *StateMvCommand) Run(args []string) int { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, msgInvalidTarget, - fmt.Sprintf("Cannot move %s to %s: the target must also be a module.", addrFrom, addrTo), + fmt.Sprintf("Cannot move %s to %s: the target must also be a module.", addrFrom, destAddr), )) c.showDiagnostics(diags) return 1 @@ -184,7 +193,7 @@ func (c *StateMvCommand) Run(args []string) int { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, msgInvalidTarget, - fmt.Sprintf("Cannot move %s to %s: the target must also be a whole resource.", addrFrom, addrTo), + fmt.Sprintf("Cannot move %s to %s: the source is a whole resource (not a resource instance) so the target must also be a whole resource.", addrFrom, destAddr), )) c.showDiagnostics(diags) return 1 @@ -231,7 +240,7 @@ func (c *StateMvCommand) Run(args []string) int { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, msgInvalidTarget, - fmt.Sprintf("Cannot move %s to %s: the target must also be a resource instance.", addrFrom, addrTo), + fmt.Sprintf("Cannot move %s to %s: the target must also be a resource instance.", addrFrom, destAddr), )) c.showDiagnostics(diags) return 1 @@ -445,7 +454,7 @@ func (c *StateMvCommand) validateResourceMove(addrFrom, addrTo addrs.AbsResource func (c *StateMvCommand) Help() string { helpText := ` -Usage: terraform state mv [options] SOURCE DESTINATION +Usage: terraform [global options] state mv [options] SOURCE DESTINATION This command will move an item matched by the address given to the destination address. This command can also move to a destination address @@ -465,31 +474,35 @@ Usage: terraform state mv [options] SOURCE DESTINATION Options: - -dry-run If set, prints out what would've been moved but doesn't - actually move anything. + -dry-run If set, prints out what would've been moved but doesn't + actually move anything. + + -backup=PATH Path where Terraform should write the backup for the + original state. This can't be disabled. If not set, + Terraform will write it to the same path as the + statefile with a ".backup" extension. - -backup=PATH Path where Terraform should write the backup for the original - state. This can't be disabled. If not set, Terraform - will write it to the same path as the statefile with - a ".backup" extension. + -backup-out=PATH Path where Terraform should write the backup for the + destination state. This can't be disabled. If not + set, Terraform will write it to the same path as the + destination state file with a backup extension. This + only needs to be specified if -state-out is set to a + different path than -state. - -backup-out=PATH Path where Terraform should write the backup for the destination - state. This can't be disabled. If not set, Terraform - will write it to the same path as the destination state - file with a backup extension. This only needs - to be specified if -state-out is set to a different path - than -state. + -lock=true Lock the state files when locking is supported. - -lock=true Lock the state files when locking is supported. + -lock-timeout=0s Duration to retry a state lock. - -lock-timeout=0s Duration to retry a state lock. + -state=PATH Path to the source state file. Defaults to the + configured backend, or "terraform.tfstate" - -state=PATH Path to the source state file. Defaults to the configured - backend, or "terraform.tfstate" + -state-out=PATH Path to the destination state file to write to. If + this isn't specified, the source state file will be + used. This can be a new or existing path. - -state-out=PATH Path to the destination state file to write to. If this - isn't specified, the source state file will be used. This - can be a new or existing path. + -ignore-remote-version Continue even if remote and local Terraform versions + are incompatible. This may result in an unusable + workspace, and should be used with extreme caution. ` return strings.TrimSpace(helpText) diff --git a/vendor/github.com/hashicorp/terraform/command/state_mv_test.go b/vendor/github.com/hashicorp/terraform/command/state_mv_test.go index 7a631c44..a651876b 100644 --- a/vendor/github.com/hashicorp/terraform/command/state_mv_test.go +++ b/vendor/github.com/hashicorp/terraform/command/state_mv_test.go @@ -7,12 +7,19 @@ import ( "strings" "testing" + "github.com/google/go-cmp/cmp" "github.com/mitchellh/cli" + "github.com/mitchellh/colorstring" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/states" ) +var disabledColorize = &colorstring.Colorize{ + Colors: colorstring.DefaultColors, + Disable: true, +} + func TestStateMv(t *testing.T) { state := states.BuildState(func(s *states.SyncState) { s.SetResourceInstanceCurrent( @@ -51,11 +58,13 @@ func TestStateMv(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateMvCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -148,6 +157,7 @@ func TestStateMv(t *testing.T) { } func TestStateMv_resourceToInstance(t *testing.T) { + // A single resource (no count defined) state := states.BuildState(func(s *states.SyncState) { s.SetResourceInstanceCurrent( addrs.Resource{ @@ -196,11 +206,13 @@ func TestStateMv_resourceToInstance(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateMvCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -236,6 +248,145 @@ test_instance.baz: testStateOutput(t, backups[0], testStateMvOutputOriginal) } +func TestStateMv_resourceToInstanceErr(t *testing.T) { + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + s.SetResourceProvider( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "bar", + }.Absolute(addrs.RootModuleInstance), + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + }) + statePath := testStateFile(t, state) + + p := testProvider() + ui := cli.NewMockUi() + view, _ := testView(t) + + c := &StateMvCommand{ + StateMeta{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + Ui: ui, + View: view, + }, + }, + } + + args := []string{ + "-state", statePath, + "test_instance.foo", + "test_instance.bar[0]", + } + + if code := c.Run(args); code == 0 { + t.Fatalf("expected error output, got:\n%s", ui.OutputWriter.String()) + } + + expectedErr := ` +Error: Invalid target address + +Cannot move test_instance.foo to test_instance.bar[0]: the source is a whole +resource (not a resource instance) so the target must also be a whole +resource. + +` + errOutput := ui.ErrorWriter.String() + if errOutput != expectedErr { + t.Errorf("wrong output\n%s", cmp.Diff(errOutput, expectedErr)) + } +} + +func TestStateMv_resourceToInstanceErrInAutomation(t *testing.T) { + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`), + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + s.SetResourceProvider( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "bar", + }.Absolute(addrs.RootModuleInstance), + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + }) + statePath := testStateFile(t, state) + + p := testProvider() + ui := new(cli.MockUi) + view, _ := testView(t) + c := &StateMvCommand{ + StateMeta{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(p), + Ui: ui, + View: view, + RunningInAutomation: true, + }, + }, + } + + args := []string{ + "-state", statePath, + "test_instance.foo", + "test_instance.bar[0]", + } + + if code := c.Run(args); code == 0 { + t.Fatalf("expected error output, got:\n%s", ui.OutputWriter.String()) + } + + expectedErr := ` +Error: Invalid target address + +Cannot move test_instance.foo to test_instance.bar[0]: the source is a whole +resource (not a resource instance) so the target must also be a whole +resource. + +` + errOutput := ui.ErrorWriter.String() + if errOutput != expectedErr { + t.Errorf("Unexpected diff.\ngot:\n%s\nwant:\n%s\n", errOutput, expectedErr) + t.Errorf("%s", cmp.Diff(errOutput, expectedErr)) + } +} + func TestStateMv_instanceToResource(t *testing.T) { state := states.BuildState(func(s *states.SyncState) { s.SetResourceInstanceCurrent( @@ -273,11 +424,13 @@ func TestStateMv_instanceToResource(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateMvCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -346,11 +499,13 @@ func TestStateMv_instanceToNewResource(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateMvCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -417,11 +572,13 @@ func TestStateMv_differentResourceTypes(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateMvCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -435,8 +592,16 @@ func TestStateMv_differentResourceTypes(t *testing.T) { t.Fatalf("expected error output, got:\n%s", ui.OutputWriter.String()) } - if !strings.Contains(ui.ErrorWriter.String(), "resource types don't match") { - t.Fatalf("expected initialization error, got:\n%s", ui.ErrorWriter.String()) + gotErr := ui.ErrorWriter.String() + wantErr := ` +Error: Invalid state move request + +Cannot move test_instance.foo to test_network.bar: resource types don't +match. + +` + if gotErr != wantErr { + t.Fatalf("expected initialization error\ngot:\n%s\n\nwant:%s", gotErr, wantErr) } } @@ -485,10 +650,12 @@ func TestStateMv_explicitWithBackend(t *testing.T) { // init our backend ui := new(cli.MockUi) + view, _ := testView(t) ic := &InitCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } @@ -505,6 +672,7 @@ func TestStateMv_explicitWithBackend(t *testing.T) { Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -562,11 +730,13 @@ func TestStateMv_backupExplicit(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateMvCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -611,11 +781,13 @@ func TestStateMv_stateOutNew(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateMvCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -683,11 +855,13 @@ func TestStateMv_stateOutExisting(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateMvCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -726,11 +900,13 @@ func TestStateMv_noState(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateMvCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -794,11 +970,13 @@ func TestStateMv_stateOutNew_count(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateMvCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -868,11 +1046,13 @@ func TestStateMv_stateOutNew_largeCount(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateMvCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -938,11 +1118,13 @@ func TestStateMv_stateOutNew_nestedModule(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateMvCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -994,11 +1176,13 @@ func TestStateMv_toNewModule(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateMvCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -1093,11 +1277,13 @@ func TestStateMv_withinBackend(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateMvCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -1163,11 +1349,13 @@ func TestStateMv_fromBackendToLocal(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateMvCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -1214,11 +1402,13 @@ func TestStateMv_onlyResourceInModule(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateMvCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -1243,6 +1433,13 @@ func TestStateMv_onlyResourceInModule(t *testing.T) { testStateOutput(t, backups[0], testStateMvOnlyResourceInModule_original) } +func TestStateMvHelp(t *testing.T) { + c := &StateMvCommand{} + if strings.ContainsRune(c.Help(), '\t') { + t.Fatal("help text contains tab character, which will result in poor formatting") + } +} + const testStateMvOutputOriginal = ` test_instance.baz: ID = foo diff --git a/vendor/github.com/hashicorp/terraform/command/state_pull.go b/vendor/github.com/hashicorp/terraform/command/state_pull.go index 6ab6328a..503e5de1 100644 --- a/vendor/github.com/hashicorp/terraform/command/state_pull.go +++ b/vendor/github.com/hashicorp/terraform/command/state_pull.go @@ -22,7 +22,6 @@ func (c *StatePullCommand) Run(args []string) int { c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s\n", err.Error())) return 1 } - args = cmdFlags.Args() // Load the backend b, backendDiags := c.Backend(nil) @@ -31,6 +30,9 @@ func (c *StatePullCommand) Run(args []string) int { return 1 } + // This is a read-only command + c.ignoreRemoteBackendVersionConflict(b) + // Get the state manager for the current workspace env, err := c.Workspace() if err != nil { @@ -66,11 +68,15 @@ func (c *StatePullCommand) Run(args []string) int { func (c *StatePullCommand) Help() string { helpText := ` -Usage: terraform state pull [options] +Usage: terraform [global options] state pull [options] - Pull the state from its location and output it to stdout. + Pull the state from its location, upgrade the local copy, and output it + to stdout. This command "pulls" the current state and outputs it to stdout. + As part of this process, Terraform will upgrade the state format of the + local copy to the current version. + The primary use of this is for state stored remotely. This command will still work with local state but is less useful for this. diff --git a/vendor/github.com/hashicorp/terraform/command/state_push.go b/vendor/github.com/hashicorp/terraform/command/state_push.go index facbf786..dcffa092 100644 --- a/vendor/github.com/hashicorp/terraform/command/state_push.go +++ b/vendor/github.com/hashicorp/terraform/command/state_push.go @@ -1,13 +1,14 @@ package command import ( - "context" "fmt" "io" "os" "strings" + "github.com/hashicorp/terraform/command/arguments" "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/states/statefile" "github.com/hashicorp/terraform/states/statemgr" "github.com/mitchellh/cli" @@ -22,7 +23,7 @@ type StatePushCommand struct { func (c *StatePushCommand) Run(args []string) int { args = c.Meta.process(args) var flagForce bool - cmdFlags := c.Meta.defaultFlagSet("state push") + cmdFlags := c.Meta.ignoreRemoteVersionFlagSet("state push") cmdFlags.BoolVar(&flagForce, "force", false, "") cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state") cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout") @@ -71,25 +72,38 @@ func (c *StatePushCommand) Run(args []string) int { return 1 } - // Get the state manager for the currently-selected workspace - env, err := c.Workspace() + // Determine the workspace name + workspace, err := c.Workspace() if err != nil { c.Ui.Error(fmt.Sprintf("Error selecting workspace: %s", err)) return 1 } - stateMgr, err := b.StateMgr(env) + + // Check remote Terraform version is compatible + remoteVersionDiags := c.remoteBackendVersionCheck(b, workspace) + c.showDiagnostics(remoteVersionDiags) + if remoteVersionDiags.HasErrors() { + return 1 + } + + // Get the state manager for the currently-selected workspace + stateMgr, err := b.StateMgr(workspace) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to load destination state: %s", err)) return 1 } if c.stateLock { - stateLocker := clistate.NewLocker(context.Background(), c.stateLockTimeout, c.Ui, c.Colorize()) - if err := stateLocker.Lock(stateMgr, "state-push"); err != nil { - c.Ui.Error(fmt.Sprintf("Error locking state: %s", err)) + stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View)) + if diags := stateLocker.Lock(stateMgr, "state-push"); diags.HasErrors() { + c.showDiagnostics(diags) return 1 } - defer stateLocker.Unlock(nil) + defer func() { + if diags := stateLocker.Unlock(); diags.HasErrors() { + c.showDiagnostics(diags) + } + }() } if err := stateMgr.RefreshState(); err != nil { @@ -121,7 +135,7 @@ func (c *StatePushCommand) Run(args []string) int { func (c *StatePushCommand) Help() string { helpText := ` -Usage: terraform state push [options] PATH +Usage: terraform [global options] state push [options] PATH Update remote state from a local state file at PATH. diff --git a/vendor/github.com/hashicorp/terraform/command/state_push_test.go b/vendor/github.com/hashicorp/terraform/command/state_push_test.go index b0858469..d906053d 100644 --- a/vendor/github.com/hashicorp/terraform/command/state_push_test.go +++ b/vendor/github.com/hashicorp/terraform/command/state_push_test.go @@ -23,10 +23,12 @@ func TestStatePush_empty(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StatePushCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -50,10 +52,12 @@ func TestStatePush_lockedState(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StatePushCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -83,10 +87,12 @@ func TestStatePush_replaceMatch(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StatePushCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -110,7 +116,7 @@ func TestStatePush_replaceMatchStdin(t *testing.T) { expected := testStateRead(t, "replace.tfstate") - // Setup the replacement to come from stdin + // Set up the replacement to come from stdin var buf bytes.Buffer if err := writeStateForTesting(expected, &buf); err != nil { t.Fatalf("err: %s", err) @@ -119,10 +125,12 @@ func TestStatePush_replaceMatchStdin(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StatePushCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -148,10 +156,12 @@ func TestStatePush_lineageMismatch(t *testing.T) { p := testProvider() ui := cli.NewMockUi() + view, _ := testView(t) c := &StatePushCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -177,10 +187,12 @@ func TestStatePush_serialNewer(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StatePushCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -206,10 +218,12 @@ func TestStatePush_serialOlder(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StatePushCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -236,8 +250,9 @@ func TestStatePush_forceRemoteState(t *testing.T) { // init the backend ui := new(cli.MockUi) + view, _ := testView(t) initCmd := &InitCommand{ - Meta: Meta{Ui: ui}, + Meta: Meta{Ui: ui, View: view}, } if code := initCmd.Run([]string{}); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) @@ -246,7 +261,7 @@ func TestStatePush_forceRemoteState(t *testing.T) { // create a new workspace ui = new(cli.MockUi) newCmd := &WorkspaceNewCommand{ - Meta: Meta{Ui: ui}, + Meta: Meta{Ui: ui, View: view}, } if code := newCmd.Run([]string{"test"}); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter) @@ -268,7 +283,7 @@ func TestStatePush_forceRemoteState(t *testing.T) { // push our local state to that new workspace ui = new(cli.MockUi) c := &StatePushCommand{ - Meta: Meta{Ui: ui}, + Meta: Meta{Ui: ui, View: view}, } args := []string{"-force", statePath} diff --git a/vendor/github.com/hashicorp/terraform/command/state_replace_provider.go b/vendor/github.com/hashicorp/terraform/command/state_replace_provider.go index 3d5acf67..2397f7d6 100644 --- a/vendor/github.com/hashicorp/terraform/command/state_replace_provider.go +++ b/vendor/github.com/hashicorp/terraform/command/state_replace_provider.go @@ -1,12 +1,13 @@ package command import ( - "context" "fmt" "strings" "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/command/arguments" "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/tfdiags" "github.com/mitchellh/cli" @@ -25,7 +26,7 @@ func (c *StateReplaceProviderCommand) Run(args []string) int { args = c.Meta.process(args) var autoApprove bool - cmdFlags := c.Meta.defaultFlagSet("state replace-provider") + cmdFlags := c.Meta.ignoreRemoteVersionFlagSet("state replace-provider") cmdFlags.BoolVar(&autoApprove, "auto-approve", false, "skip interactive approval of replacements") cmdFlags.StringVar(&c.backupPath, "backup", "-", "backup") cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock states") @@ -74,12 +75,16 @@ func (c *StateReplaceProviderCommand) Run(args []string) int { // Acquire lock if requested if c.stateLock { - stateLocker := clistate.NewLocker(context.Background(), c.stateLockTimeout, c.Ui, c.Colorize()) - if err := stateLocker.Lock(stateMgr, "state-replace-provider"); err != nil { - c.Ui.Error(fmt.Sprintf("Error locking source state: %s", err)) + stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View)) + if diags := stateLocker.Lock(stateMgr, "state-replace-provider"); diags.HasErrors() { + c.showDiagnostics(diags) return 1 } - defer stateLocker.Unlock(nil) + defer func() { + if diags := stateLocker.Unlock(); diags.HasErrors() { + c.showDiagnostics(diags) + } + }() } // Refresh and load state @@ -90,7 +95,7 @@ func (c *StateReplaceProviderCommand) Run(args []string) int { state := stateMgr.State() if state == nil { - c.Ui.Error(fmt.Sprintf(errStateNotFound)) + c.Ui.Error(errStateNotFound) return 1 } @@ -119,7 +124,7 @@ func (c *StateReplaceProviderCommand) Run(args []string) int { // Explain the changes colorize := c.Colorize() c.Ui.Output("Terraform will perform the following actions:\n") - c.Ui.Output(colorize.Color(fmt.Sprintf(" [yellow]~[reset] Updating provider:"))) + c.Ui.Output(colorize.Color(" [yellow]~[reset] Updating provider:")) c.Ui.Output(colorize.Color(fmt.Sprintf(" [red]-[reset] %s", from))) c.Ui.Output(colorize.Color(fmt.Sprintf(" [green]+[reset] %s\n", to))) @@ -134,7 +139,7 @@ func (c *StateReplaceProviderCommand) Run(args []string) int { "\n[bold]Do you want to make these changes?[reset]\n" + "Only 'yes' will be accepted to continue.\n", )) - v, err := c.Ui.Ask(fmt.Sprintf("Enter a value:")) + v, err := c.Ui.Ask("Enter a value:") if err != nil { c.Ui.Error(fmt.Sprintf("Error asking for approval: %s", err)) return 1 @@ -166,25 +171,30 @@ func (c *StateReplaceProviderCommand) Run(args []string) int { func (c *StateReplaceProviderCommand) Help() string { helpText := ` -Usage: terraform state replace-provider [options] FROM_PROVIDER_FQN TO_PROVIDER_FQN +Usage: terraform [global options] state replace-provider [options] FROM_PROVIDER_FQN TO_PROVIDER_FQN Replace provider for resources in the Terraform state. Options: - -auto-approve Skip interactive approval. + -auto-approve Skip interactive approval. - -backup=PATH Path where Terraform should write the backup for the - state file. This can't be disabled. If not set, Terraform - will write it to the same path as the state file with - a ".backup" extension. + -backup=PATH Path where Terraform should write the backup for the + state file. This can't be disabled. If not set, + Terraform will write it to the same path as the state + file with a ".backup" extension. - -lock=true Lock the state files when locking is supported. + -lock=true Lock the state files when locking is supported. - -lock-timeout=0s Duration to retry a state lock. + -lock-timeout=0s Duration to retry a state lock. + + -state=PATH Path to the state file to update. Defaults to the + configured backend, or "terraform.tfstate" + + -ignore-remote-version Continue even if remote and local Terraform versions + are incompatible. This may result in an unusable + workspace, and should be used with extreme caution. - -state=PATH Path to the state file to update. Defaults to the configured - backend, or "terraform.tfstate" ` return strings.TrimSpace(helpText) } diff --git a/vendor/github.com/hashicorp/terraform/command/state_replace_provider_test.go b/vendor/github.com/hashicorp/terraform/command/state_replace_provider_test.go index 563af4fd..7b471af2 100644 --- a/vendor/github.com/hashicorp/terraform/command/state_replace_provider_test.go +++ b/vendor/github.com/hashicorp/terraform/command/state_replace_provider_test.go @@ -65,10 +65,12 @@ func TestStateReplaceProvider(t *testing.T) { statePath := testStateFile(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &StateReplaceProviderCommand{ StateMeta{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, }, } @@ -99,10 +101,12 @@ func TestStateReplaceProvider(t *testing.T) { statePath := testStateFile(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &StateReplaceProviderCommand{ StateMeta{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, }, } @@ -133,10 +137,12 @@ func TestStateReplaceProvider(t *testing.T) { statePath := testStateFile(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &StateReplaceProviderCommand{ StateMeta{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, }, } @@ -166,10 +172,12 @@ func TestStateReplaceProvider(t *testing.T) { statePath := testStateFile(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &StateReplaceProviderCommand{ StateMeta{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, }, } @@ -193,10 +201,12 @@ func TestStateReplaceProvider(t *testing.T) { t.Run("invalid flags", func(t *testing.T) { ui := new(cli.MockUi) + view, _ := testView(t) c := &StateReplaceProviderCommand{ StateMeta{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, }, } @@ -217,10 +227,12 @@ func TestStateReplaceProvider(t *testing.T) { t.Run("wrong number of arguments", func(t *testing.T) { ui := new(cli.MockUi) + view, _ := testView(t) c := &StateReplaceProviderCommand{ StateMeta{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, }, } @@ -237,10 +249,12 @@ func TestStateReplaceProvider(t *testing.T) { t.Run("invalid provider strings", func(t *testing.T) { ui := new(cli.MockUi) + view, _ := testView(t) c := &StateReplaceProviderCommand{ StateMeta{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, }, } @@ -271,7 +285,7 @@ func TestStateReplaceProvider(t *testing.T) { func TestStateReplaceProvider_docs(t *testing.T) { c := &StateReplaceProviderCommand{} - if got, want := c.Help(), "Usage: terraform state replace-provider"; !strings.Contains(got, want) { + if got, want := c.Help(), "Usage: terraform [global options] state replace-provider"; !strings.Contains(got, want) { t.Fatalf("unexpected help text\nwant: %s\nfull output:\n%s", want, got) } diff --git a/vendor/github.com/hashicorp/terraform/command/state_rm.go b/vendor/github.com/hashicorp/terraform/command/state_rm.go index 1254de41..8c236a74 100644 --- a/vendor/github.com/hashicorp/terraform/command/state_rm.go +++ b/vendor/github.com/hashicorp/terraform/command/state_rm.go @@ -1,12 +1,13 @@ package command import ( - "context" "fmt" "strings" "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/command/arguments" "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/tfdiags" "github.com/mitchellh/cli" ) @@ -19,7 +20,7 @@ type StateRmCommand struct { func (c *StateRmCommand) Run(args []string) int { args = c.Meta.process(args) var dryRun bool - cmdFlags := c.Meta.defaultFlagSet("state rm") + cmdFlags := c.Meta.ignoreRemoteVersionFlagSet("state rm") cmdFlags.BoolVar(&dryRun, "dry-run", false, "dry run") cmdFlags.StringVar(&c.backupPath, "backup", "-", "backup") cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state") @@ -44,12 +45,16 @@ func (c *StateRmCommand) Run(args []string) int { } if c.stateLock { - stateLocker := clistate.NewLocker(context.Background(), c.stateLockTimeout, c.Ui, c.Colorize()) - if err := stateLocker.Lock(stateMgr, "state-rm"); err != nil { - c.Ui.Error(fmt.Sprintf("Error locking state: %s", err)) + stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View)) + if diags := stateLocker.Lock(stateMgr, "state-rm"); diags.HasErrors() { + c.showDiagnostics(diags) return 1 } - defer stateLocker.Unlock(nil) + defer func() { + if diags := stateLocker.Unlock(); diags.HasErrors() { + c.showDiagnostics(diags) + } + }() } if err := stateMgr.RefreshState(); err != nil { @@ -59,7 +64,7 @@ func (c *StateRmCommand) Run(args []string) int { state := stateMgr.State() if state == nil { - c.Ui.Error(fmt.Sprintf(errStateNotFound)) + c.Ui.Error(errStateNotFound) return 1 } @@ -129,7 +134,7 @@ func (c *StateRmCommand) Run(args []string) int { func (c *StateRmCommand) Help() string { helpText := ` -Usage: terraform state rm [options] ADDRESS... +Usage: terraform [global options] state rm [options] ADDRESS... Remove one or more items from the Terraform state, causing Terraform to "forget" those items without first destroying them in the remote system. @@ -146,18 +151,22 @@ Usage: terraform state rm [options] ADDRESS... Options: - -dry-run If set, prints out what would've been removed but - doesn't actually remove anything. + -dry-run If set, prints out what would've been removed but + doesn't actually remove anything. - -backup=PATH Path where Terraform should write the backup - state. + -backup=PATH Path where Terraform should write the backup + state. - -lock=true Lock the state file when locking is supported. + -lock=true Lock the state file when locking is supported. - -lock-timeout=0s Duration to retry a state lock. + -lock-timeout=0s Duration to retry a state lock. - -state=PATH Path to the state file to update. Defaults to the current - workspace state. + -state=PATH Path to the state file to update. Defaults to the + current workspace state. + + -ignore-remote-version Continue even if remote and local Terraform versions + are incompatible. This may result in an unusable + workspace, and should be used with extreme caution. ` return strings.TrimSpace(helpText) diff --git a/vendor/github.com/hashicorp/terraform/command/state_rm_test.go b/vendor/github.com/hashicorp/terraform/command/state_rm_test.go index d0f2c4fd..e88cfff8 100644 --- a/vendor/github.com/hashicorp/terraform/command/state_rm_test.go +++ b/vendor/github.com/hashicorp/terraform/command/state_rm_test.go @@ -49,11 +49,13 @@ func TestStateRm(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateRmCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -117,11 +119,13 @@ func TestStateRmNotChildModule(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateRmCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -203,11 +207,13 @@ func TestStateRmNoArgs(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateRmCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -262,11 +268,13 @@ func TestStateRmNonExist(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateRmCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -318,11 +326,13 @@ func TestStateRm_backupExplicit(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateRmCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -349,11 +359,13 @@ func TestStateRm_noState(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateRmCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -372,11 +384,13 @@ func TestStateRm_needsInit(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateRmCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } @@ -446,11 +460,13 @@ func TestStateRm_backendState(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &StateRmCommand{ StateMeta{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, }, } diff --git a/vendor/github.com/hashicorp/terraform/command/state_show.go b/vendor/github.com/hashicorp/terraform/command/state_show.go index dd6e292b..ee018cc5 100644 --- a/vendor/github.com/hashicorp/terraform/command/state_show.go +++ b/vendor/github.com/hashicorp/terraform/command/state_show.go @@ -53,6 +53,9 @@ func (c *StateShowCommand) Run(args []string) int { return 1 } + // This is a read-only command + c.ignoreRemoteBackendVersionConflict(b) + // Check if the address can be parsed addr, addrDiags := addrs.ParseAbsResourceInstanceStr(args[0]) if addrDiags.HasErrors() { @@ -106,7 +109,7 @@ func (c *StateShowCommand) Run(args []string) int { state := stateMgr.State() if state == nil { - c.Ui.Error(fmt.Sprintf(errStateNotFound)) + c.Ui.Error(errStateNotFound) return 1 } @@ -142,7 +145,7 @@ func (c *StateShowCommand) Run(args []string) int { func (c *StateShowCommand) Help() string { helpText := ` -Usage: terraform state show [options] ADDRESS +Usage: terraform [global options] state show [options] ADDRESS Shows the attributes of a resource in the Terraform state. diff --git a/vendor/github.com/hashicorp/terraform/command/state_show_test.go b/vendor/github.com/hashicorp/terraform/command/state_show_test.go index 0f649cb7..3231a5d7 100644 --- a/vendor/github.com/hashicorp/terraform/command/state_show_test.go +++ b/vendor/github.com/hashicorp/terraform/command/state_show_test.go @@ -8,7 +8,6 @@ import ( "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/states" - "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/cli" "github.com/zclconf/go-cty/cty" ) @@ -34,13 +33,15 @@ func TestStateShow(t *testing.T) { statePath := testStateFile(t, state) p := testProvider() - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "foo": {Type: cty.String, Optional: true}, - "bar": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "foo": {Type: cty.String, Optional: true}, + "bar": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -107,13 +108,15 @@ func TestStateShow_multi(t *testing.T) { statePath := testStateFile(t, state) p := testProvider() - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "foo": {Type: cty.String, Optional: true}, - "bar": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "foo": {Type: cty.String, Optional: true}, + "bar": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -213,13 +216,15 @@ func TestStateShow_configured_provider(t *testing.T) { statePath := testStateFile(t, state) p := testProvider() - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true, Computed: true}, - "foo": {Type: cty.String, Optional: true}, - "bar": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "foo": {Type: cty.String, Optional: true}, + "bar": {Type: cty.String, Optional: true}, + }, }, }, }, diff --git a/vendor/github.com/hashicorp/terraform/command/taint.go b/vendor/github.com/hashicorp/terraform/command/taint.go index 04a5bf1f..aef940db 100644 --- a/vendor/github.com/hashicorp/terraform/command/taint.go +++ b/vendor/github.com/hashicorp/terraform/command/taint.go @@ -1,13 +1,14 @@ package command import ( - "context" "fmt" "os" "strings" "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/command/arguments" "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/tfdiags" @@ -21,14 +22,12 @@ type TaintCommand struct { func (c *TaintCommand) Run(args []string) int { args = c.Meta.process(args) - var module string var allowMissing bool - cmdFlags := c.Meta.defaultFlagSet("taint") - cmdFlags.BoolVar(&allowMissing, "allow-missing", false, "module") + cmdFlags := c.Meta.ignoreRemoteVersionFlagSet("taint") + cmdFlags.BoolVar(&allowMissing, "allow-missing", false, "allow missing") cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path") cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state") cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout") - cmdFlags.StringVar(&module, "module", "", "module") cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path") cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } @@ -47,11 +46,6 @@ func (c *TaintCommand) Run(args []string) int { return 1 } - if module != "" { - c.Ui.Error("The -module option is no longer used. Instead, include the module path in the main resource address, like \"module.foo.module.bar.null_resource.baz\".") - return 1 - } - addr, addrDiags := addrs.ParseAbsResourceInstanceStr(args[0]) diags = diags.Append(addrDiags) if addrDiags.HasErrors() { @@ -100,25 +94,39 @@ func (c *TaintCommand) Run(args []string) int { return 1 } - // Get the state - env, err := c.Workspace() + // Determine the workspace name + workspace, err := c.Workspace() if err != nil { c.Ui.Error(fmt.Sprintf("Error selecting workspace: %s", err)) return 1 } - stateMgr, err := b.StateMgr(env) + + // Check remote Terraform version is compatible + remoteVersionDiags := c.remoteBackendVersionCheck(b, workspace) + diags = diags.Append(remoteVersionDiags) + c.showDiagnostics(diags) + if diags.HasErrors() { + return 1 + } + + // Get the state + stateMgr, err := b.StateMgr(workspace) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err)) return 1 } if c.stateLock { - stateLocker := clistate.NewLocker(context.Background(), c.stateLockTimeout, c.Ui, c.Colorize()) - if err := stateLocker.Lock(stateMgr, "taint"); err != nil { - c.Ui.Error(fmt.Sprintf("Error locking state: %s", err)) + stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View)) + if diags := stateLocker.Lock(stateMgr, "taint"); diags.HasErrors() { + c.showDiagnostics(diags) return 1 } - defer stateLocker.Unlock(nil) + defer func() { + if diags := stateLocker.Unlock(); diags.HasErrors() { + c.showDiagnostics(diags) + } + }() } if err := stateMgr.RefreshState(); err != nil { @@ -199,55 +207,65 @@ func (c *TaintCommand) Run(args []string) int { func (c *TaintCommand) Help() string { helpText := ` -Usage: terraform taint [options]
+Usage: terraform [global options] taint [options]
- Manually mark a resource as tainted, forcing a destroy and recreate - on the next plan/apply. + Terraform uses the term "tainted" to describe a resource instance + which may not be fully functional, either because its creation + partially failed or because you've manually marked it as such using + this command. - This will not modify your infrastructure. This command changes your - state to mark a resource as tainted so that during the next plan or - apply that resource will be destroyed and recreated. This command on - its own will not modify infrastructure. This command can be undone - using the "terraform untaint" command with the same address. + This will not modify your infrastructure directly, but subsequent + Terraform plans will include actions to destroy the remote object + and create a new object to replace it. - The address is in the usual resource address syntax, as shown in - the output from other commands, such as: + You can remove the "taint" state from a resource instance using + the "terraform untaint" command. + + The address is in the usual resource address syntax, such as: aws_instance.foo aws_instance.bar[1] module.foo.module.bar.aws_instance.baz + Use your shell's quoting or escaping syntax to ensure that the + address will reach Terraform correctly, without any special + interpretation. + Options: - -allow-missing If specified, the command will succeed (exit code 0) - even if the resource is missing. + -allow-missing If specified, the command will succeed (exit code 0) + even if the resource is missing. + + -backup=path Path to backup the existing state file before + modifying. Defaults to the "-state-out" path with + ".backup" extension. Set to "-" to disable backup. - -backup=path Path to backup the existing state file before - modifying. Defaults to the "-state-out" path with - ".backup" extension. Set to "-" to disable backup. + -lock=true Lock the state file when locking is supported. - -lock=true Lock the state file when locking is supported. + -lock-timeout=0s Duration to retry a state lock. - -lock-timeout=0s Duration to retry a state lock. + -state=path Path to read and save state (unless state-out + is specified). Defaults to "terraform.tfstate". - -state=path Path to read and save state (unless state-out - is specified). Defaults to "terraform.tfstate". + -state-out=path Path to write updated state file. By default, the + "-state" path will be used. - -state-out=path Path to write updated state file. By default, the - "-state" path will be used. + -ignore-remote-version Continue even if remote and local Terraform versions + are incompatible. This may result in an unusable + workspace, and should be used with extreme caution. ` return strings.TrimSpace(helpText) } func (c *TaintCommand) Synopsis() string { - return "Manually mark a resource for recreation" + return "Mark a resource instance as not fully functional" } func (c *TaintCommand) allowMissingExit(name addrs.AbsResourceInstance) int { c.showDiagnostics(tfdiags.Sourceless( tfdiags.Warning, "No such resource instance", - "Resource instance %s was not found, but this is not an error because -allow-missing was set.", + fmt.Sprintf("Resource instance %s was not found, but this is not an error because -allow-missing was set.", name), )) return 0 } diff --git a/vendor/github.com/hashicorp/terraform/command/taint_test.go b/vendor/github.com/hashicorp/terraform/command/taint_test.go index e09a0c33..27171aec 100644 --- a/vendor/github.com/hashicorp/terraform/command/taint_test.go +++ b/vendor/github.com/hashicorp/terraform/command/taint_test.go @@ -5,6 +5,7 @@ import ( "strings" "testing" + "github.com/google/go-cmp/cmp" "github.com/mitchellh/cli" "github.com/hashicorp/terraform/addrs" @@ -32,9 +33,11 @@ func TestTaint(t *testing.T) { statePath := testStateFile(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &TaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -75,9 +78,11 @@ func TestTaint_lockedState(t *testing.T) { } defer unlock() ui := new(cli.MockUi) + view, _ := testView(t) c := &TaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -121,9 +126,11 @@ func TestTaint_backup(t *testing.T) { testStateFileDefault(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &TaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -164,9 +171,11 @@ func TestTaint_backupDisable(t *testing.T) { testStateFileDefault(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &TaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -187,9 +196,11 @@ func TestTaint_backupDisable(t *testing.T) { func TestTaint_badState(t *testing.T) { ui := new(cli.MockUi) + view, _ := testView(t) c := &TaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -228,9 +239,11 @@ func TestTaint_defaultState(t *testing.T) { testStateFileDefault(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &TaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -270,7 +283,8 @@ func TestTaint_defaultWorkspaceState(t *testing.T) { path := testStateFileWorkspaceDefault(t, testWorkspace, state) ui := new(cli.MockUi) - meta := Meta{Ui: ui} + view, _ := testView(t) + meta := Meta{Ui: ui, View: view} meta.SetWorkspace(testWorkspace) c := &TaintCommand{ Meta: meta, @@ -307,9 +321,11 @@ func TestTaint_missing(t *testing.T) { statePath := testStateFile(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &TaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -343,9 +359,11 @@ func TestTaint_missingAllow(t *testing.T) { statePath := testStateFile(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &TaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -357,6 +375,19 @@ func TestTaint_missingAllow(t *testing.T) { if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } + + // Check for the warning + actual := strings.TrimSpace(ui.ErrorWriter.String()) + expected := strings.TrimSpace(` +Warning: No such resource instance + +Resource instance test_instance.bar was not found, but this is not an error +because -allow-missing was set. + +`) + if diff := cmp.Diff(expected, actual); diff != "" { + t.Fatalf("wrong output\n%s", diff) + } } func TestTaint_stateOut(t *testing.T) { @@ -385,9 +416,11 @@ func TestTaint_stateOut(t *testing.T) { testStateFileDefault(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &TaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -439,9 +472,11 @@ func TestTaint_module(t *testing.T) { statePath := testStateFile(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &TaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -484,10 +519,12 @@ func TestTaint_checkRequiredVersion(t *testing.T) { path := testStateFile(t, state) ui := cli.NewMockUi() + view, _ := testView(t) c := &TaintCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, + View: view, }, } diff --git a/vendor/github.com/hashicorp/terraform/command/test.go b/vendor/github.com/hashicorp/terraform/command/test.go new file mode 100644 index 00000000..9e6d19e0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/test.go @@ -0,0 +1,712 @@ +package command + +import ( + "context" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" + + ctyjson "github.com/zclconf/go-cty/cty/json" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/format" + "github.com/hashicorp/terraform/command/views" + "github.com/hashicorp/terraform/configs" + "github.com/hashicorp/terraform/configs/configload" + "github.com/hashicorp/terraform/internal/depsfile" + "github.com/hashicorp/terraform/internal/initwd" + "github.com/hashicorp/terraform/internal/moduletest" + "github.com/hashicorp/terraform/internal/providercache" + "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/states" + "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/tfdiags" +) + +// TestCommand is the implementation of "terraform test". +type TestCommand struct { + Meta +} + +func (c *TestCommand) Run(rawArgs []string) int { + // Parse and apply global view arguments + common, rawArgs := arguments.ParseView(rawArgs) + c.View.Configure(common) + + args, diags := arguments.ParseTest(rawArgs) + view := views.NewTest(c.View, args.Output) + if diags.HasErrors() { + view.Diagnostics(diags) + return 1 + } + + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Warning, + `The "terraform test" command is experimental`, + "We'd like to invite adventurous module authors to write integration tests for their modules using this command, but all of the behaviors of this command are currently experimental and may change based on feedback.\n\nFor more information on the testing experiment, including ongoing research goals and avenues for feedback, see:\n https://www.terraform.io/docs/language/modules/testing-experiment.html", + )) + + ctx, cancel := c.InterruptibleContext() + defer cancel() + + results, moreDiags := c.run(ctx, args) + diags = diags.Append(moreDiags) + + initFailed := diags.HasErrors() + view.Diagnostics(diags) + diags = view.Results(results) + resultsFailed := diags.HasErrors() + view.Diagnostics(diags) // possible additional errors from saving the results + + var testsFailed bool + for _, suite := range results { + for _, component := range suite.Components { + for _, assertion := range component.Assertions { + if !assertion.Outcome.SuiteCanPass() { + testsFailed = true + } + } + } + } + + // Lots of things can possibly have failed + if initFailed || resultsFailed || testsFailed { + return 1 + } + return 0 +} + +func (c *TestCommand) run(ctx context.Context, args arguments.Test) (results map[string]*moduletest.Suite, diags tfdiags.Diagnostics) { + suiteNames, err := c.collectSuiteNames() + if err != nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Error while searching for test configurations", + fmt.Sprintf("While attempting to scan the 'tests' subdirectory for potential test configurations, Terraform encountered an error: %s.", err), + )) + return nil, diags + } + + ret := make(map[string]*moduletest.Suite, len(suiteNames)) + for _, suiteName := range suiteNames { + if ctx.Err() != nil { + // If the context has already failed in some way then we'll + // halt early and report whatever's already happened. + break + } + suite, moreDiags := c.runSuite(ctx, suiteName) + diags = diags.Append(moreDiags) + ret[suiteName] = suite + } + + return ret, diags +} + +func (c *TestCommand) runSuite(ctx context.Context, suiteName string) (*moduletest.Suite, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + ret := moduletest.Suite{ + Name: suiteName, + Components: map[string]*moduletest.Component{}, + } + + // In order to make this initial round of "terraform test" pretty self + // contained while it's experimental, it's largely just mimicking what + // would happen when running the main Terraform workflow commands, which + // comes at the expense of a few irritants that we'll hopefully resolve + // in future iterations as the design solidifies: + // - We need to install remote modules separately for each of the + // test suites, because we don't have any sense of a shared cache + // of modules that multiple configurations can refer to at once. + // - We _do_ have a sense of a cache of remote providers, but it's fixed + // at being specifically a two-level cache (global vs. directory-specific) + // and so we can't easily capture a third level of "all of the test suites + // for this module" that sits between the two. Consequently, we need to + // dynamically choose between creating a directory-specific "global" + // cache or using the user's existing global cache, to avoid any + // situation were we'd be re-downloading the same providers for every + // one of the test suites. + // - We need to do something a bit horrid in order to have our test + // provider instance persist between the plan and apply steps, because + // normally that is the exact opposite of what we want. + // The above notes are here mainly as an aid to someone who might be + // planning a subsequent phase of this R&D effort, to help distinguish + // between things we're doing here because they are valuable vs. things + // we're doing just to make it work without doing any disruptive + // refactoring. + + suiteDirs, moreDiags := c.prepareSuiteDir(ctx, suiteName) + diags = diags.Append(moreDiags) + if diags.HasErrors() { + // Generate a special failure representing the test initialization + // having failed, since we therefore won'tbe able to run the actual + // tests defined inside. + ret.Components["(init)"] = &moduletest.Component{ + Assertions: map[string]*moduletest.Assertion{ + "(init)": { + Outcome: moduletest.Error, + Description: "terraform init", + Message: "failed to install test suite dependencies", + Diagnostics: diags, + }, + }, + } + return &ret, nil + } + + // When we run the suite itself, we collect up diagnostics associated + // with individual components, so ret.Components may or may not contain + // failed/errored components after runTestSuite returns. + var finalState *states.State + ret.Components, finalState = c.runTestSuite(ctx, suiteDirs) + + // Regardless of the success or failure of the test suite, if there are + // any objects left in the state then we'll generate a top-level error + // about each one to minimize the chance of the user failing to notice + // that there are leftover objects that might continue to cost money + // unless manually deleted. + for _, ms := range finalState.Modules { + for _, rs := range ms.Resources { + for instanceKey, is := range rs.Instances { + var objs []*states.ResourceInstanceObjectSrc + if is.Current != nil { + objs = append(objs, is.Current) + } + for _, obj := range is.Deposed { + objs = append(objs, obj) + } + for _, obj := range objs { + // Unfortunately we don't have provider schemas out here + // and so we're limited in what we can achieve with these + // ResourceInstanceObjectSrc values, but we can try some + // heuristicy things to try to give some useful information + // in common cases. + var k, v string + if ty, err := ctyjson.ImpliedType(obj.AttrsJSON); err == nil { + if approxV, err := ctyjson.Unmarshal(obj.AttrsJSON, ty); err == nil { + k, v = format.ObjectValueIDOrName(approxV) + } + } + + var detail string + if k != "" { + // We can be more specific if we were able to infer + // an identifying attribute for this object. + detail = fmt.Sprintf( + "Due to errors during destroy, test suite %q has left behind an object for %s, with the following identity:\n %s = %q\n\nYou will need to delete this object manually in the remote system, or else it may have an ongoing cost.", + suiteName, + rs.Addr.Instance(instanceKey), + k, v, + ) + } else { + // If our heuristics for finding a suitable identifier + // failed then unfortunately we must be more vague. + // (We can't just print the entire object, because it + // might be overly large and it might contain sensitive + // values.) + detail = fmt.Sprintf( + "Due to errors during destroy, test suite %q has left behind an object for %s. You will need to delete this object manually in the remote system, or else it may have an ongoing cost.", + suiteName, + rs.Addr.Instance(instanceKey), + ) + } + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to clean up after tests", + detail, + )) + } + } + } + } + + return &ret, diags +} + +func (c *TestCommand) prepareSuiteDir(ctx context.Context, suiteName string) (testCommandSuiteDirs, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + configDir := filepath.Join("tests", suiteName) + log.Printf("[TRACE] terraform test: Prepare directory for suite %q in %s", suiteName, configDir) + + suiteDirs := testCommandSuiteDirs{ + SuiteName: suiteName, + ConfigDir: configDir, + } + + // Before we can run a test suite we need to make sure that we have all of + // its dependencies available, so the following is essentially an + // abbreviated form of what happens during "terraform init", with some + // extra trickery in places. + + // First, module installation. This will include linking in the module + // under test, but also includes grabbing the dependencies of that module + // if it has any. + suiteDirs.ModulesDir = filepath.Join(configDir, ".terraform", "modules") + os.MkdirAll(suiteDirs.ModulesDir, 0755) // if this fails then we'll ignore it and let InstallModules below fail instead + reg := c.registryClient() + moduleInst := initwd.NewModuleInstaller(suiteDirs.ModulesDir, reg) + _, moreDiags := moduleInst.InstallModules(configDir, true, nil) + diags = diags.Append(moreDiags) + if diags.HasErrors() { + return suiteDirs, diags + } + + // The installer puts the files in a suitable place on disk, but we + // still need to actually load the configuration. We need to do this + // with a separate config loader because the Meta.configLoader instance + // is intended for interacting with the current working directory, not + // with the test suite subdirectories. + loader, err := configload.NewLoader(&configload.Config{ + ModulesDir: suiteDirs.ModulesDir, + Services: c.Services, + }) + if err != nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to create test configuration loader", + fmt.Sprintf("Failed to prepare loader for test configuration %s: %s.", configDir, err), + )) + return suiteDirs, diags + } + cfg, hclDiags := loader.LoadConfig(configDir) + diags = diags.Append(hclDiags) + if diags.HasErrors() { + return suiteDirs, diags + } + suiteDirs.Config = cfg + + // With the full configuration tree available, we can now install + // the necessary providers. We'll use a separate local cache directory + // here, because the test configuration might have additional requirements + // compared to the module itself. + suiteDirs.ProvidersDir = filepath.Join(configDir, ".terraform", "providers") + os.MkdirAll(suiteDirs.ProvidersDir, 0755) // if this fails then we'll ignore it and operations below fail instead + localCacheDir := providercache.NewDir(suiteDirs.ProvidersDir) + providerInst := c.providerInstaller().Clone(localCacheDir) + if !providerInst.HasGlobalCacheDir() { + // If the user already configured a global cache directory then we'll + // just use it for caching the test providers too, because then we + // can potentially reuse cache entries they already have. However, + // if they didn't configure one then we'll still establish one locally + // in the working directory, which we'll then share across all tests + // to avoid downloading the same providers repeatedly. + cachePath := filepath.Join(c.DataDir(), "testing-providers") // note this is _not_ under the suite dir + err := os.MkdirAll(cachePath, 0755) + // If we were unable to create the directory for any reason then we'll + // just proceed without a cache, at the expense of repeated downloads. + // (With that said, later installing might end up failing for the + // same reason anyway...) + if err == nil || os.IsExist(err) { + cacheDir := providercache.NewDir(cachePath) + providerInst.SetGlobalCacheDir(cacheDir) + } + } + reqs, hclDiags := cfg.ProviderRequirements() + diags = diags.Append(hclDiags) + if diags.HasErrors() { + return suiteDirs, diags + } + + // For test suites we only retain the "locks" in memory for the duration + // for one run, just to make sure that we use the same providers when we + // eventually run the test suite. + locks := depsfile.NewLocks() + evts := &providercache.InstallerEvents{ + QueryPackagesFailure: func(provider addrs.Provider, err error) { + if err != nil && provider.IsDefault() && provider.Type == "test" { + // This is some additional context for the failure error + // we'll generate afterwards. Not the most ideal UX but + // good enough for this prototype implementation, to help + // hint about the special builtin provider we use here. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Warning, + "Probably-unintended reference to \"hashicorp/test\" provider", + "For the purposes of this experimental implementation of module test suites, you must use the built-in test provider terraform.io/builtin/test, which requires an explicit required_providers declaration.", + )) + } + }, + } + ctx = evts.OnContext(ctx) + locks, err = providerInst.EnsureProviderVersions(ctx, locks, reqs, providercache.InstallUpgrades) + if err != nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to install required providers", + fmt.Sprintf("Couldn't install necessary providers for test configuration %s: %s.", configDir, err), + )) + return suiteDirs, diags + } + suiteDirs.ProviderLocks = locks + suiteDirs.ProviderCache = localCacheDir + + return suiteDirs, diags +} + +func (c *TestCommand) runTestSuite(ctx context.Context, suiteDirs testCommandSuiteDirs) (map[string]*moduletest.Component, *states.State) { + log.Printf("[TRACE] terraform test: Run test suite %q", suiteDirs.SuiteName) + + ret := make(map[string]*moduletest.Component) + + // To collect test results we'll use an instance of the special "test" + // provider, which records the intention to make a test assertion during + // planning and then hopefully updates that to an actual assertion result + // during apply, unless an apply error causes the graph walk to exit early. + // For this to work correctly, we must ensure we're using the same provider + // instance for both plan and apply. + testProvider := moduletest.NewProvider() + + // synthError is a helper to return early with a synthetic failing + // component, for problems that prevent us from even discovering what an + // appropriate component and assertion name might be. + state := states.NewState() + synthError := func(name string, desc string, msg string, diags tfdiags.Diagnostics) (map[string]*moduletest.Component, *states.State) { + key := "(" + name + ")" // parens ensure this can't conflict with an actual component/assertion key + ret[key] = &moduletest.Component{ + Assertions: map[string]*moduletest.Assertion{ + key: { + Outcome: moduletest.Error, + Description: desc, + Message: msg, + Diagnostics: diags, + }, + }, + } + return ret, state + } + + // NOTE: This function intentionally deviates from the usual pattern of + // gradually appending more diagnostics to the same diags, because + // here we're associating each set of diagnostics with the specific + // operation it belongs to. + + providerFactories, diags := c.testSuiteProviders(suiteDirs, testProvider) + if diags.HasErrors() { + // It should be unusual to get in here, because testSuiteProviders + // should rely only on things guaranteed by prepareSuiteDir, but + // since we're doing external I/O here there is always the risk that + // the filesystem changes or fails between setting up and using the + // providers. + return synthError( + "init", + "terraform init", + "failed to resolve the required providers", + diags, + ) + } + + plan, diags := c.testSuitePlan(ctx, suiteDirs, providerFactories) + if diags.HasErrors() { + // It should be unusual to get in here, because testSuitePlan + // should rely only on things guaranteed by prepareSuiteDir, but + // since we're doing external I/O here there is always the risk that + // the filesystem changes or fails between setting up and using the + // providers. + return synthError( + "plan", + "terraform plan", + "failed to create a plan", + diags, + ) + } + + // Now we'll apply the plan. Once we try to apply, we might've created + // real remote objects, and so we must try to run destroy even if the + // apply returns errors, and we must return whatever state we end up + // with so the caller can generate additional loud errors if anything + // is left in it. + + state, diags = c.testSuiteApply(ctx, plan, suiteDirs, providerFactories) + if diags.HasErrors() { + // We don't return here, unlike the others above, because we want to + // continue to the destroy below even if there are apply errors. + synthError( + "apply", + "terraform apply", + "failed to apply the created plan", + diags, + ) + } + + // By the time we get here, the test provider will have gathered up all + // of the planned assertions and the final results for any assertions that + // were not blocked by an error. This also resets the provider so that + // the destroy operation below won't get tripped up on stale results. + ret = testProvider.Reset() + + state, diags = c.testSuiteDestroy(ctx, state, suiteDirs, providerFactories) + if diags.HasErrors() { + synthError( + "destroy", + "terraform destroy", + "failed to destroy objects created during test (NOTE: leftover remote objects may still exist)", + diags, + ) + } + + return ret, state +} + +func (c *TestCommand) testSuiteProviders(suiteDirs testCommandSuiteDirs, testProvider *moduletest.Provider) (map[addrs.Provider]providers.Factory, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + ret := make(map[addrs.Provider]providers.Factory) + + // We can safely use the internal providers returned by Meta here because + // the built-in provider versions can never vary based on the configuration + // and thus we don't need to worry about potential version differences + // between main module and test suite modules. + for name, factory := range c.internalProviders() { + ret[addrs.NewBuiltInProvider(name)] = factory + } + + // For the remaining non-builtin providers, we'll just take whatever we + // recorded earlier in the in-memory-only "lock file". All of these should + // typically still be available because we would've only just installed + // them, but this could fail if e.g. the filesystem has been somehow + // damaged in the meantime. + for provider, lock := range suiteDirs.ProviderLocks.AllProviders() { + version := lock.Version() + cached := suiteDirs.ProviderCache.ProviderVersion(provider, version) + if cached == nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Required provider not found", + fmt.Sprintf("Although installation previously succeeded for %s v%s, it no longer seems to be present in the cache directory.", provider.ForDisplay(), version.String()), + )) + continue // potentially collect up multiple errors + } + + // NOTE: We don't consider the checksums for test suite dependencies, + // because we're creating a fresh "lock file" each time we run anyway + // and so they wouldn't actually guarantee anything useful. + + ret[provider] = providerFactory(cached) + } + + // We'll replace the test provider instance with the one our caller + // provided, so it'll be able to interrogate the test results directly. + ret[addrs.NewBuiltInProvider("test")] = func() (providers.Interface, error) { + return testProvider, nil + } + + return ret, diags +} + +func (c *TestCommand) testSuiteContext(suiteDirs testCommandSuiteDirs, providerFactories map[addrs.Provider]providers.Factory, state *states.State, plan *plans.Plan, destroy bool) (*terraform.Context, tfdiags.Diagnostics) { + var changes *plans.Changes + if plan != nil { + changes = plan.Changes + } + + planMode := plans.NormalMode + if destroy { + planMode = plans.DestroyMode + } + + return terraform.NewContext(&terraform.ContextOpts{ + Config: suiteDirs.Config, + Providers: providerFactories, + + // We just use the provisioners from the main Meta here, because + // unlike providers provisioner plugins are not automatically + // installable anyway, and so we'll need to hunt for them in the same + // legacy way that normal Terraform operations do. + Provisioners: c.provisionerFactories(), + + Meta: &terraform.ContextMeta{ + Env: "test_" + suiteDirs.SuiteName, + }, + + State: state, + Changes: changes, + PlanMode: planMode, + }) +} + +func (c *TestCommand) testSuitePlan(ctx context.Context, suiteDirs testCommandSuiteDirs, providerFactories map[addrs.Provider]providers.Factory) (*plans.Plan, tfdiags.Diagnostics) { + log.Printf("[TRACE] terraform test: create plan for suite %q", suiteDirs.SuiteName) + tfCtx, diags := c.testSuiteContext(suiteDirs, providerFactories, nil, nil, false) + if diags.HasErrors() { + return nil, diags + } + + // We'll also validate as part of planning, since the "terraform plan" + // command would typically do that and so inconsistencies we detect only + // during planning typically produce error messages saying that they are + // a bug in Terraform. + // (It's safe to use the same context for both validate and plan, because + // validate doesn't generate any new sticky content inside the context + // as plan and apply both do.) + moreDiags := tfCtx.Validate() + diags = diags.Append(moreDiags) + if diags.HasErrors() { + return nil, diags + } + + plan, moreDiags := tfCtx.Plan() + diags = diags.Append(moreDiags) + return plan, diags +} + +func (c *TestCommand) testSuiteApply(ctx context.Context, plan *plans.Plan, suiteDirs testCommandSuiteDirs, providerFactories map[addrs.Provider]providers.Factory) (*states.State, tfdiags.Diagnostics) { + log.Printf("[TRACE] terraform test: apply plan for suite %q", suiteDirs.SuiteName) + tfCtx, diags := c.testSuiteContext(suiteDirs, providerFactories, nil, plan, false) + if diags.HasErrors() { + // To make things easier on the caller, we'll return a valid empty + // state even in this case. + return states.NewState(), diags + } + + state, moreDiags := tfCtx.Apply() + diags = diags.Append(moreDiags) + return state, diags +} + +func (c *TestCommand) testSuiteDestroy(ctx context.Context, state *states.State, suiteDirs testCommandSuiteDirs, providerFactories map[addrs.Provider]providers.Factory) (*states.State, tfdiags.Diagnostics) { + log.Printf("[TRACE] terraform test: plan to destroy any existing objects for suite %q", suiteDirs.SuiteName) + tfCtx, diags := c.testSuiteContext(suiteDirs, providerFactories, state, nil, true) + if diags.HasErrors() { + return state, diags + } + + plan, moreDiags := tfCtx.Plan() + diags = diags.Append(moreDiags) + if diags.HasErrors() { + return state, diags + } + + log.Printf("[TRACE] terraform test: apply the plan to destroy any existing objects for suite %q", suiteDirs.SuiteName) + tfCtx, moreDiags = c.testSuiteContext(suiteDirs, providerFactories, state, plan, true) + diags = diags.Append(moreDiags) + if diags.HasErrors() { + return state, diags + } + + state, moreDiags = tfCtx.Apply() + diags = diags.Append(moreDiags) + return state, diags +} + +func (c *TestCommand) collectSuiteNames() ([]string, error) { + items, err := ioutil.ReadDir("tests") + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, err + } + + ret := make([]string, 0, len(items)) + for _, item := range items { + if !item.IsDir() { + continue + } + name := item.Name() + suitePath := filepath.Join("tests", name) + tfFiles, err := filepath.Glob(filepath.Join(suitePath, "*.tf")) + if err != nil { + // We'll just ignore it and treat it like a dir with no .tf files + tfFiles = nil + } + tfJSONFiles, err := filepath.Glob(filepath.Join(suitePath, "*.tf.json")) + if err != nil { + // We'll just ignore it and treat it like a dir with no .tf.json files + tfJSONFiles = nil + } + if (len(tfFiles) + len(tfJSONFiles)) == 0 { + // Not a test suite, then. + continue + } + ret = append(ret, name) + } + + return ret, nil +} + +func (c *TestCommand) Help() string { + helpText := ` +Usage: terraform test [options] + + This is an experimental command to help with automated integration + testing of shared modules. The usage and behavior of this command is + likely to change in breaking ways in subsequent releases, as we + are currently using this command primarily for research purposes. + + In its current experimental form, "test" will look under the current + working directory for a subdirectory called "tests", and then within + that directory search for one or more subdirectories that contain + ".tf" or ".tf.json" files. For any that it finds, it will perform + Terraform operations similar to the following sequence of commands + in each of those directories: + terraform validate + terraform apply + terraform destroy + + The test configurations should not declare any input variables and + should at least contain a call to the module being tested, which + will always be available at the path ../.. due to the expected + filesystem layout. + + The tests are considered to be successful if all of the above steps + succeed. + + Test configurations may optionally include uses of the special + built-in test provider terraform.io/builtin/test, which allows + writing explicit test assertions which must also all pass in order + for the test run to be considered successful. + + This initial implementation is intended as a minimally-viable + product to use for further research and experimentation, and in + particular it currently lacks the following capabilities that we + expect to consider in later iterations, based on feedback: + - Testing of subsequent updates to existing infrastructure, + where currently it only supports initial creation and + then destruction. + - Testing top-level modules that are intended to be used for + "real" environments, which typically have hard-coded values + that don't permit creating a separate "copy" for testing. + - Some sort of support for unit test runs that don't interact + with remote systems at all, e.g. for use in checking pull + requests from untrusted contributors. + + In the meantime, we'd like to hear feedback from module authors + who have tried writing some experimental tests for their modules + about what sorts of tests you were able to write, what sorts of + tests you weren't able to write, and any tests that you were + able to write but that were difficult to model in some way. + +Options: + + -compact-warnings Use a more compact representation for warnings, if + this command produces only warnings and no errors. + + -junit-xml=FILE In addition to the usual output, also write test + results to the given file path in JUnit XML format. + This format is commonly supported by CI systems, and + they typically expect to be given a filename to search + for in the test workspace after the test run finishes. + + -no-color Don't include virtual terminal formatting sequences in + the output. +` + return strings.TrimSpace(helpText) +} + +func (c *TestCommand) Synopsis() string { + return "Experimental support for module integration testing" +} + +type testCommandSuiteDirs struct { + SuiteName string + + ConfigDir string + ModulesDir string + ProvidersDir string + + Config *configs.Config + ProviderCache *providercache.Dir + ProviderLocks *depsfile.Locks +} diff --git a/vendor/github.com/hashicorp/terraform/command/test_test.go b/vendor/github.com/hashicorp/terraform/command/test_test.go new file mode 100644 index 00000000..ea7a1aa7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/test_test.go @@ -0,0 +1,166 @@ +package command + +import ( + "bytes" + "io/ioutil" + "os" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform/command/views" + "github.com/hashicorp/terraform/internal/terminal" +) + +// These are the main tests for the "terraform test" command. +func TestTest(t *testing.T) { + t.Run("passes", func(t *testing.T) { + td := tempDir(t) + testCopyDir(t, testFixturePath("test-passes"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + streams, close := terminal.StreamsForTesting(t) + cmd := &TestCommand{ + Meta: Meta{ + Streams: streams, + View: views.NewView(streams), + }, + } + exitStatus := cmd.Run([]string{"-junit-xml=junit.xml", "-no-color"}) + outp := close(t) + if got, want := exitStatus, 0; got != want { + t.Fatalf("wrong exit status %d; want %d\nstderr:\n%s", got, want, outp.Stderr()) + } + + gotStdout := strings.TrimSpace(outp.Stdout()) + wantStdout := strings.TrimSpace(` +Warning: The "terraform test" command is experimental + +We'd like to invite adventurous module authors to write integration tests for +their modules using this command, but all of the behaviors of this command +are currently experimental and may change based on feedback. + +For more information on the testing experiment, including ongoing research +goals and avenues for feedback, see: + https://www.terraform.io/docs/language/modules/testing-experiment.html +`) + if diff := cmp.Diff(wantStdout, gotStdout); diff != "" { + t.Errorf("wrong stdout\n%s", diff) + } + + gotStderr := strings.TrimSpace(outp.Stderr()) + wantStderr := strings.TrimSpace(` +Success! All of the test assertions passed. +`) + if diff := cmp.Diff(wantStderr, gotStderr); diff != "" { + t.Errorf("wrong stderr\n%s", diff) + } + + gotXMLSrc, err := ioutil.ReadFile("junit.xml") + if err != nil { + t.Fatal(err) + } + gotXML := string(bytes.TrimSpace(gotXMLSrc)) + wantXML := strings.TrimSpace(` + + 0 + 0 + 1 + + hello + 1 + 0 + 0 + 0 + + output + foo + + + +`) + if diff := cmp.Diff(wantXML, gotXML); diff != "" { + t.Errorf("wrong JUnit XML\n%s", diff) + } + }) + t.Run("fails", func(t *testing.T) { + td := tempDir(t) + testCopyDir(t, testFixturePath("test-fails"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + streams, close := terminal.StreamsForTesting(t) + cmd := &TestCommand{ + Meta: Meta{ + Streams: streams, + View: views.NewView(streams), + }, + } + exitStatus := cmd.Run([]string{"-junit-xml=junit.xml", "-no-color"}) + outp := close(t) + if got, want := exitStatus, 1; got != want { + t.Fatalf("wrong exit status %d; want %d\nstderr:\n%s", got, want, outp.Stderr()) + } + + gotStdout := strings.TrimSpace(outp.Stdout()) + wantStdout := strings.TrimSpace(` +Warning: The "terraform test" command is experimental + +We'd like to invite adventurous module authors to write integration tests for +their modules using this command, but all of the behaviors of this command +are currently experimental and may change based on feedback. + +For more information on the testing experiment, including ongoing research +goals and avenues for feedback, see: + https://www.terraform.io/docs/language/modules/testing-experiment.html +`) + if diff := cmp.Diff(wantStdout, gotStdout); diff != "" { + t.Errorf("wrong stdout\n%s", diff) + } + + gotStderr := strings.TrimSpace(outp.Stderr()) + wantStderr := strings.TrimSpace(` +─── Failed: hello.foo.output (output "foo" value) ─────────────────────────── +wrong value + got: "foo value boop" + want: "foo not boop" + +───────────────────────────────────────────────────────────────────────────── +`) + if diff := cmp.Diff(wantStderr, gotStderr); diff != "" { + t.Errorf("wrong stderr\n%s", diff) + } + + gotXMLSrc, err := ioutil.ReadFile("junit.xml") + if err != nil { + t.Fatal(err) + } + gotXML := string(bytes.TrimSpace(gotXMLSrc)) + wantXML := strings.TrimSpace(` + + 0 + 1 + 1 + + hello + 1 + 0 + 0 + 1 + + output + foo + + wrong value got: "foo value boop" want: "foo not boop" + + + + +`) + if diff := cmp.Diff(wantXML, gotXML); diff != "" { + t.Errorf("wrong JUnit XML\n%s", diff) + } + }) + +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-existing-versions-tf/expected/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-existing-versions-tf/expected/main.tf deleted file mode 100644 index 109e0699..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-existing-versions-tf/expected/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -resource foo_resource b {} -resource bar_resource c {} -resource bar_resource ab { - provider = baz -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-existing-versions-tf/expected/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-existing-versions-tf/expected/versions.tf deleted file mode 100644 index 22c14e5c..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-existing-versions-tf/expected/versions.tf +++ /dev/null @@ -1,18 +0,0 @@ -# This is a file called versions.tf which does not originally have a -# required_providers block. -resource foo_resource a {} - -terraform { - required_version = ">= 0.13" - required_providers { - bar = { - source = "hashicorp/bar" - } - baz = { - source = "terraform-providers/baz" - } - foo = { - source = "hashicorp/foo" - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-existing-versions-tf/input/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-existing-versions-tf/input/main.tf deleted file mode 100644 index 109e0699..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-existing-versions-tf/input/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -resource foo_resource b {} -resource bar_resource c {} -resource bar_resource ab { - provider = baz -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-existing-versions-tf/input/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-existing-versions-tf/input/versions.tf deleted file mode 100644 index c4ab732d..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-existing-versions-tf/input/versions.tf +++ /dev/null @@ -1,7 +0,0 @@ -# This is a file called versions.tf which does not originally have a -# required_providers block. -resource foo_resource a {} - -terraform { - required_version = ">= 0.12" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-explicit-providers/expected/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-explicit-providers/expected/main.tf deleted file mode 100644 index a70002e5..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-explicit-providers/expected/main.tf +++ /dev/null @@ -1,21 +0,0 @@ -provider "foo" { - version = "1.2.3" -} - -terraform { - required_providers { - bar = { - source = "hashicorp/bar" - version = "1.0.0" - } - baz = { - source = "terraform-providers/baz" - version = "~> 2.0.0" - } - foo = { - source = "hashicorp/foo" - } - } -} - -provider "terraform" {} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-explicit-providers/expected/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-explicit-providers/expected/versions.tf deleted file mode 100644 index 6b6318de..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-explicit-providers/expected/versions.tf +++ /dev/null @@ -1,3 +0,0 @@ -terraform { - required_version = ">= 0.13" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-explicit-providers/input/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-explicit-providers/input/main.tf deleted file mode 100644 index 40862252..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-explicit-providers/input/main.tf +++ /dev/null @@ -1,14 +0,0 @@ -provider "foo" { - version = "1.2.3" -} - -terraform { - required_providers { - bar = "1.0.0" - baz = { - version = "~> 2.0.0" - } - } -} - -provider "terraform" { } diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-file-exists/expected/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-file-exists/expected/versions.tf deleted file mode 100644 index 973e28c1..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-file-exists/expected/versions.tf +++ /dev/null @@ -1,13 +0,0 @@ -provider foo {} -provider bar {} -terraform { - required_providers { - bar = { - source = "hashicorp/bar" - } - foo = { - source = "hashicorp/foo" - } - } - required_version = ">= 0.13" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-file-exists/input/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-file-exists/input/versions.tf deleted file mode 100644 index e9f22a97..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-file-exists/input/versions.tf +++ /dev/null @@ -1,2 +0,0 @@ -provider foo {} -provider bar {} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-implicit-not-found/expected/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-implicit-not-found/expected/main.tf deleted file mode 100644 index afb4083b..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-implicit-not-found/expected/main.tf +++ /dev/null @@ -1 +0,0 @@ -resource something_resource a {} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-implicit-not-found/expected/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-implicit-not-found/expected/versions.tf deleted file mode 100644 index 795fe47e..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-implicit-not-found/expected/versions.tf +++ /dev/null @@ -1,17 +0,0 @@ -terraform { - required_providers { - something = { - # TF-UPGRADE-TODO - # - # No source detected for this provider. You must add a source address - # in the following format: - # - # source = "your-registry.example.com/organization/something" - # - # For more information, see the provider source documentation: - # - # https://www.terraform.io/docs/configuration/providers.html#provider-source - } - } - required_version = ">= 0.13" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-implicit-not-found/input/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-implicit-not-found/input/main.tf deleted file mode 100644 index afb4083b..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-implicit-not-found/input/main.tf +++ /dev/null @@ -1 +0,0 @@ -resource something_resource a {} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-implicit-providers/expected/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-implicit-providers/expected/main.tf deleted file mode 100644 index 82c3e837..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-implicit-providers/expected/main.tf +++ /dev/null @@ -1,6 +0,0 @@ -resource "foo_resource" "b" {} -resource "bar_resource" "c" {} -resource "bar_resource" "ab" { - provider = baz -} -resource "terraform_remote_state" "production" {} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-implicit-providers/expected/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-implicit-providers/expected/versions.tf deleted file mode 100644 index 1bfef179..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-implicit-providers/expected/versions.tf +++ /dev/null @@ -1,14 +0,0 @@ -terraform { - required_providers { - bar = { - source = "hashicorp/bar" - } - baz = { - source = "terraform-providers/baz" - } - foo = { - source = "hashicorp/foo" - } - } - required_version = ">= 0.13" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-implicit-providers/input/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-implicit-providers/input/main.tf deleted file mode 100644 index 82c3e837..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-implicit-providers/input/main.tf +++ /dev/null @@ -1,6 +0,0 @@ -resource "foo_resource" "b" {} -resource "bar_resource" "c" {} -resource "bar_resource" "ab" { - provider = baz -} -resource "terraform_remote_state" "production" {} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-blocks/expected/bar-baz.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-blocks/expected/bar-baz.tf deleted file mode 100644 index f2f1d69a..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-blocks/expected/bar-baz.tf +++ /dev/null @@ -1,2 +0,0 @@ -resource bar_instance a {} -resource baz_instance b {} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-blocks/expected/foo.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-blocks/expected/foo.tf deleted file mode 100644 index e29e6d31..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-blocks/expected/foo.tf +++ /dev/null @@ -1,4 +0,0 @@ -terraform { - required_version = ">= 0.12" -} -resource foo_instance c {} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-blocks/expected/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-blocks/expected/versions.tf deleted file mode 100644 index ba49a0da..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-blocks/expected/versions.tf +++ /dev/null @@ -1,16 +0,0 @@ -terraform { - required_providers { - foo = { - source = "hashicorp/foo" - version = "0.5" - } - bar = { - source = "registry.acme.corp/acme/bar" - } - baz = { - source = "terraform-providers/baz" - version = "~> 2.0.0" - } - } - required_version = ">= 0.13" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-blocks/input/bar-baz.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-blocks/input/bar-baz.tf deleted file mode 100644 index f2f1d69a..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-blocks/input/bar-baz.tf +++ /dev/null @@ -1,2 +0,0 @@ -resource bar_instance a {} -resource baz_instance b {} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-blocks/input/foo.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-blocks/input/foo.tf deleted file mode 100644 index 582cf0e2..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-blocks/input/foo.tf +++ /dev/null @@ -1,7 +0,0 @@ -terraform { - required_version = ">= 0.12" - required_providers { - baz = "~> 2.0.0" - } -} -resource foo_instance c {} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-blocks/input/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-blocks/input/versions.tf deleted file mode 100644 index 9252a15e..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-blocks/input/versions.tf +++ /dev/null @@ -1,12 +0,0 @@ -terraform { - required_providers { - foo = "0.5" - } -} -terraform { - required_providers { - bar = { - source = "registry.acme.corp/acme/bar" - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-files/expected/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-files/expected/main.tf deleted file mode 100644 index fa25239e..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-files/expected/main.tf +++ /dev/null @@ -1,4 +0,0 @@ -# This file starts with a resource and a required providers block, and should -# end up with just the resource. -resource foo_instance a {} - diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-files/expected/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-files/expected/versions.tf deleted file mode 100644 index 5604764e..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-files/expected/versions.tf +++ /dev/null @@ -1,17 +0,0 @@ -# This file starts with a resource and a required providers block, and should -# end up with the full required providers configuration. This file is chosen -# to keep the required providers block because its file name is "providers.tf". -resource bar_instance b {} - -terraform { - required_providers { - bar = { - source = "registry.acme.corp/acme/bar" - } - foo = { - source = "hashicorp/foo" - version = "1.0.0" - } - } - required_version = ">= 0.13" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-files/input/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-files/input/main.tf deleted file mode 100644 index 168ebff7..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-files/input/main.tf +++ /dev/null @@ -1,9 +0,0 @@ -# This file starts with a resource and a required providers block, and should -# end up with just the resource. -resource foo_instance a {} - -terraform { - required_providers { - foo = "1.0.0" - } -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-files/input/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-files/input/versions.tf deleted file mode 100644 index 8823c6fe..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-multiple-files/input/versions.tf +++ /dev/null @@ -1,12 +0,0 @@ -# This file starts with a resource and a required providers block, and should -# end up with the full required providers configuration. This file is chosen -# to keep the required providers block because its file name is "providers.tf". -resource bar_instance b {} - -terraform { - required_providers { - bar = { - source = "registry.acme.corp/acme/bar" - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-no-providers/expected/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-no-providers/expected/main.tf deleted file mode 100644 index d7ad4349..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-no-providers/expected/main.tf +++ /dev/null @@ -1,11 +0,0 @@ -variable "x" { - default = 3 -} - -variable "y" { - default = 5 -} - -output "product" { - value = var.x * var.y -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-no-providers/input/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-no-providers/input/main.tf deleted file mode 100644 index d7ad4349..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-no-providers/input/main.tf +++ /dev/null @@ -1,11 +0,0 @@ -variable "x" { - default = 3 -} - -variable "y" { - default = 5 -} - -output "product" { - value = var.x * var.y -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-preserves-comments/expected/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-preserves-comments/expected/main.tf deleted file mode 100644 index 7ddfeb0f..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-preserves-comments/expected/main.tf +++ /dev/null @@ -1,23 +0,0 @@ -resource foo_instance a {} -resource bar_instance b {} - -terraform { - # Provider requirements go here - required_providers { - # Pin bar to this version - bar = { - source = "hashicorp/bar" - version = "0.5.0" - } - # An explicit requirement - baz = { - # Comment inside the block should stay - source = "foo/baz" - } - # Foo is required - foo = { - source = "hashicorp/foo" - version = "1.0.0" - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-preserves-comments/expected/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-preserves-comments/expected/versions.tf deleted file mode 100644 index 6b6318de..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-preserves-comments/expected/versions.tf +++ /dev/null @@ -1,3 +0,0 @@ -terraform { - required_version = ">= 0.13" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-preserves-comments/input/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-preserves-comments/input/main.tf deleted file mode 100644 index 7d12b433..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-preserves-comments/input/main.tf +++ /dev/null @@ -1,20 +0,0 @@ -resource foo_instance a {} -resource bar_instance b {} - -terraform { - # Provider requirements go here - required_providers { - # Pin bar to this version - bar = "0.5.0" - # An explicit requirement - baz = { - # Comment inside the block should stay - source = "foo/baz" - } - # Foo is required - foo = { - # This comment sadly won't make it - version = "1.0.0" - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-not-found/expected/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-not-found/expected/main.tf deleted file mode 100644 index 951db47d..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-not-found/expected/main.tf +++ /dev/null @@ -1,26 +0,0 @@ -provider foo {} - -terraform { - required_providers { - bar = { - source = "hashicorp/bar" - version = "1.0.0" - } - unknown = { - # TF-UPGRADE-TODO - # - # No source detected for this provider. You must add a source address - # in the following format: - # - # source = "your-registry.example.com/organization/unknown" - # - # For more information, see the provider source documentation: - # - # https://www.terraform.io/docs/configuration/providers.html#provider-source - version = "~> 2.0.0" - } - foo = { - source = "hashicorp/foo" - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-not-found/expected/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-not-found/expected/versions.tf deleted file mode 100644 index 6b6318de..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-not-found/expected/versions.tf +++ /dev/null @@ -1,3 +0,0 @@ -terraform { - required_version = ">= 0.13" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-not-found/input/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-not-found/input/main.tf deleted file mode 100644 index 9b6e3810..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-not-found/input/main.tf +++ /dev/null @@ -1,10 +0,0 @@ -provider foo {} - -terraform { - required_providers { - bar = "1.0.0" - unknown = { - version = "~> 2.0.0" - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-redirect-version-unavailable/expected/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-redirect-version-unavailable/expected/main.tf deleted file mode 100644 index 2e6a3b2c..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-redirect-version-unavailable/expected/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -provider "qux" { - version = "~> 0.9.0" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-redirect-version-unavailable/expected/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-redirect-version-unavailable/expected/versions.tf deleted file mode 100644 index 21c925ec..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-redirect-version-unavailable/expected/versions.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - required_providers { - qux = { - source = "hashicorp/qux" - } - } - required_version = ">= 0.13" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-redirect-version-unavailable/input/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-redirect-version-unavailable/input/main.tf deleted file mode 100644 index 2e6a3b2c..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-redirect-version-unavailable/input/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -provider "qux" { - version = "~> 0.9.0" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-redirect/expected/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-redirect/expected/main.tf deleted file mode 100644 index d8b5dfb0..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-redirect/expected/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -provider "qux" { - version = ">= 1.0.0" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-redirect/expected/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-redirect/expected/versions.tf deleted file mode 100644 index c0b9ee57..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-redirect/expected/versions.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - required_providers { - qux = { - source = "acme/qux" - } - } - required_version = ">= 0.13" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-redirect/input/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-redirect/input/main.tf deleted file mode 100644 index d8b5dfb0..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-provider-redirect/input/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -provider "qux" { - version = ">= 1.0.0" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-providers-with-source/expected/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-providers-with-source/expected/main.tf deleted file mode 100644 index 3d366ebf..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-providers-with-source/expected/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -terraform { - required_providers { - aws = { - source = "registry.acme.corp/acme/aws" - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-providers-with-source/expected/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-providers-with-source/expected/versions.tf deleted file mode 100644 index 6b6318de..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-providers-with-source/expected/versions.tf +++ /dev/null @@ -1,3 +0,0 @@ -terraform { - required_version = ">= 0.13" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-providers-with-source/input/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-providers-with-source/input/main.tf deleted file mode 100644 index 3d366ebf..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-providers-with-source/input/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -terraform { - required_providers { - aws = { - source = "registry.acme.corp/acme/aws" - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/expected/bar_override.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/expected/bar_override.tf deleted file mode 100644 index 524ef2c5..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/expected/bar_override.tf +++ /dev/null @@ -1,3 +0,0 @@ -provider bar { - version = "1.0.0" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/expected/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/expected/main.tf deleted file mode 100644 index 83d1158f..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/expected/main.tf +++ /dev/null @@ -1,19 +0,0 @@ -provider foo { - version = "1.2.3" -} - -terraform { - required_providers { - bar = { - source = "hashicorp/bar" - version = "1.0.0" - } - baz = { - source = "terraform-providers/baz" - version = "~> 2.0.0" - } - foo = { - source = "hashicorp/foo" - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/expected/variables.tf.json b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/expected/variables.tf.json deleted file mode 100644 index 6126f1f0..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/expected/variables.tf.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "variable": { - "example": { - "default": "hello" - } - } - "terraform": { - "required_providers": { - "aws": "2.50.0" - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/expected/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/expected/versions.tf deleted file mode 100644 index 6b6318de..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/expected/versions.tf +++ /dev/null @@ -1,3 +0,0 @@ -terraform { - required_version = ">= 0.13" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/input/bar_override.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/input/bar_override.tf deleted file mode 100644 index 524ef2c5..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/input/bar_override.tf +++ /dev/null @@ -1,3 +0,0 @@ -provider bar { - version = "1.0.0" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/input/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/input/main.tf deleted file mode 100644 index f3c982a0..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/input/main.tf +++ /dev/null @@ -1,12 +0,0 @@ -provider foo { - version = "1.2.3" -} - -terraform { - required_providers { - bar = "1.0.0" - baz = { - version = "~> 2.0.0" - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/input/variables.tf.json b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/input/variables.tf.json deleted file mode 100644 index 6126f1f0..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-skipped-files/input/variables.tf.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "variable": { - "example": { - "default": "hello" - } - } - "terraform": { - "required_providers": { - "aws": "2.50.0" - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/expected-module/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/expected-module/main.tf deleted file mode 100644 index 6c4de24a..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/expected-module/main.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - required_providers { - bar = { - version = "~> 2.0.0" - } - } -} - diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/expected-module/module/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/expected-module/module/main.tf deleted file mode 100644 index 0f5566e5..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/expected-module/module/main.tf +++ /dev/null @@ -1 +0,0 @@ -resource foo_resource b {} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/expected-module/module/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/expected-module/module/versions.tf deleted file mode 100644 index 999c9480..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/expected-module/module/versions.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - required_providers { - foo = { - source = "hashicorp/foo" - } - } - required_version = ">= 0.13" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/expected/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/expected/main.tf deleted file mode 100644 index 89b6f402..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/expected/main.tf +++ /dev/null @@ -1,9 +0,0 @@ -terraform { - required_providers { - bar = { - source = "hashicorp/bar" - version = "~> 2.0.0" - } - } -} - diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/expected/module/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/expected/module/main.tf deleted file mode 100644 index 0f5566e5..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/expected/module/main.tf +++ /dev/null @@ -1 +0,0 @@ -resource foo_resource b {} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/expected/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/expected/versions.tf deleted file mode 100644 index 6b6318de..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/expected/versions.tf +++ /dev/null @@ -1,3 +0,0 @@ -terraform { - required_version = ">= 0.13" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/input/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/input/main.tf deleted file mode 100644 index 6c4de24a..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/input/main.tf +++ /dev/null @@ -1,8 +0,0 @@ -terraform { - required_providers { - bar = { - version = "~> 2.0.0" - } - } -} - diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/input/module/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/input/module/main.tf deleted file mode 100644 index 0f5566e5..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-submodule/input/module/main.tf +++ /dev/null @@ -1 +0,0 @@ -resource foo_resource b {} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-unsupported-version/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-unsupported-version/main.tf deleted file mode 100644 index c7dee23c..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-unsupported-version/main.tf +++ /dev/null @@ -1 +0,0 @@ -resource foo_instance a {} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-unsupported-version/versions.tf b/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-unsupported-version/versions.tf deleted file mode 100644 index 7000b050..00000000 --- a/vendor/github.com/hashicorp/terraform/command/testdata/013upgrade-unsupported-version/versions.tf +++ /dev/null @@ -1,3 +0,0 @@ -terraform { - required_version = "~> 0.12.0" -} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/apply-destroy-targeted/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/apply-destroy-targeted/main.tf index 45ebc5b9..0f249b38 100644 --- a/vendor/github.com/hashicorp/terraform/command/testdata/apply-destroy-targeted/main.tf +++ b/vendor/github.com/hashicorp/terraform/command/testdata/apply-destroy-targeted/main.tf @@ -3,5 +3,5 @@ resource "test_instance" "foo" { } resource "test_load_balancer" "foo" { - instances = ["${test_instance.foo.*.id}"] + instances = test_instance.foo.*.id } diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/apply-replace/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/apply-replace/main.tf new file mode 100644 index 00000000..efc6729f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/apply-replace/main.tf @@ -0,0 +1,2 @@ +resource "test_instance" "a" { +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/apply-targeted/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/apply-targeted/main.tf new file mode 100644 index 00000000..1b6c4245 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/apply-targeted/main.tf @@ -0,0 +1,9 @@ +resource "test_instance" "foo" { + count = 2 +} + +resource "test_instance" "bar" { +} + +resource "test_instance" "baz" { +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/apply/output.jsonlog b/vendor/github.com/hashicorp/terraform/command/testdata/apply/output.jsonlog new file mode 100644 index 00000000..a7060349 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/apply/output.jsonlog @@ -0,0 +1,6 @@ +{"@level":"info","@message":"Terraform 0.15.0-dev","@module":"terraform.ui","terraform":"0.15.0-dev","type":"version","ui":"0.1.0"} +{"@level":"info","@message":"test_instance.foo: Plan to create","@module":"terraform.ui","change":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create"},"type":"planned_change"} +{"@level":"info","@message":"test_instance.foo: Creating...","@module":"terraform.ui","hook":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create"},"type":"apply_start"} +{"@level":"info","@message":"test_instance.foo: Creation complete after 0s","@module":"terraform.ui","hook":{"resource":{"addr":"test_instance.foo","module":"","resource":"test_instance.foo","implied_provider":"test","resource_type":"test_instance","resource_name":"foo","resource_key":null},"action":"create","elapsed_seconds":0},"type":"apply_complete"} +{"@level":"info","@message":"Apply complete! Resources: 1 added, 0 changed, 0 destroyed.","@module":"terraform.ui","changes":{"add":1,"change":0,"remove":0,"operation":"apply"},"type":"change_summary"} +{"@level":"info","@message":"Outputs: 0","@module":"terraform.ui","outputs":{},"type":"outputs"} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/fmt/general_in.tf b/vendor/github.com/hashicorp/terraform/command/testdata/fmt/general_in.tf index 44432f97..94db1893 100644 --- a/vendor/github.com/hashicorp/terraform/command/testdata/fmt/general_in.tf +++ b/vendor/github.com/hashicorp/terraform/command/testdata/fmt/general_in.tf @@ -39,3 +39,15 @@ resource "foo_instance" /* ... */ "baz" { thingy = "${var.instance_type}" } } + + provider "" { +} + +locals { + name = "${contains(["foo"], var.my_var) ? "${var.my_var}-bar" : + contains(["baz"], var.my_var) ? "baz-${var.my_var}" : + file("ERROR: unsupported type ${var.my_var}")}" + wrapped = "${(var.my_var == null ? 1 : + var.your_var == null ? 2 : + 3)}" +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/fmt/general_out.tf b/vendor/github.com/hashicorp/terraform/command/testdata/fmt/general_out.tf index 4b1bc489..1fe6b5b3 100644 --- a/vendor/github.com/hashicorp/terraform/command/testdata/fmt/general_out.tf +++ b/vendor/github.com/hashicorp/terraform/command/testdata/fmt/general_out.tf @@ -39,3 +39,15 @@ resource "foo_instance" "baz" { thingy = var.instance_type } } + +provider "" { +} + +locals { + name = (contains(["foo"], var.my_var) ? "${var.my_var}-bar" : + contains(["baz"], var.my_var) ? "baz-${var.my_var}" : + file("ERROR: unsupported type ${var.my_var}")) + wrapped = (var.my_var == null ? 1 : + var.your_var == null ? 2 : + 3) +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/init-provider-lock-file-readonly-add/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/init-provider-lock-file-readonly-add/main.tf new file mode 100644 index 00000000..a706a538 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/init-provider-lock-file-readonly-add/main.tf @@ -0,0 +1,10 @@ +terraform { + required_providers { + test = { + version = "1.2.3" + } + foo = { + version = "1.0.0" + } + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/login-tfe-server/tfeserver.go b/vendor/github.com/hashicorp/terraform/command/testdata/login-tfe-server/tfeserver.go index 11d164ab..111c0712 100644 --- a/vendor/github.com/hashicorp/terraform/command/testdata/login-tfe-server/tfeserver.go +++ b/vendor/github.com/hashicorp/terraform/command/testdata/login-tfe-server/tfeserver.go @@ -11,6 +11,7 @@ import ( const ( goodToken = "good-token" accountDetails = `{"data":{"id":"user-abc123","type":"users","attributes":{"username":"testuser","email":"testuser@example.com"}}}` + MOTD = `{"msg":"Welcome to Terraform Cloud!"}` ) // Handler is an implementation of net/http.Handler that provides a stub @@ -29,6 +30,8 @@ func (h handler) ServeHTTP(resp http.ResponseWriter, req *http.Request) { h.servePing(resp, req) case "/api/v2/account/details": h.serveAccountDetails(resp, req) + case "/api/terraform/motd": + h.serveMOTD(resp, req) default: fmt.Printf("404 when fetching %s\n", req.URL.String()) http.Error(resp, `{"errors":[{"status":"404","title":"not found"}]}`, http.StatusNotFound) @@ -49,6 +52,11 @@ func (h handler) serveAccountDetails(resp http.ResponseWriter, req *http.Request resp.Write([]byte(accountDetails)) } +func (h handler) serveMOTD(resp http.ResponseWriter, req *http.Request) { + resp.WriteHeader(http.StatusOK) + resp.Write([]byte(MOTD)) +} + func init() { Handler = handler{} } diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/plan-replace/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/plan-replace/main.tf new file mode 100644 index 00000000..efc6729f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/plan-replace/main.tf @@ -0,0 +1,2 @@ +resource "test_instance" "a" { +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/providers-schema/basic/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/providers-schema/basic/output.json index ba58e2f3..f14786c3 100644 --- a/vendor/github.com/hashicorp/terraform/command/testdata/providers-schema/basic/output.json +++ b/vendor/github.com/hashicorp/terraform/command/testdata/providers-schema/basic/output.json @@ -1,5 +1,5 @@ { - "format_version": "0.1", + "format_version": "0.2", "provider_schemas": { "registry.terraform.io/hashicorp/test": { "provider": { @@ -30,6 +30,25 @@ "optional": true, "computed": true, "description_kind": "plain" + }, + "volumes": { + "nested_type": { + "nesting_mode": "list", + "attributes": { + "size": { + "type": "string", + "required": true, + "description_kind": "plain" + }, + "mount_point": { + "type": "string", + "required": true, + "description_kind": "plain" + } + } + }, + "description_kind": "plain", + "optional": true } }, "description_kind": "plain" diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/providers-schema/empty/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/providers-schema/empty/output.json index d457a374..12d30d20 100644 --- a/vendor/github.com/hashicorp/terraform/command/testdata/providers-schema/empty/output.json +++ b/vendor/github.com/hashicorp/terraform/command/testdata/providers-schema/empty/output.json @@ -1,3 +1,3 @@ { - "format_version": "0.1" + "format_version": "0.2" } \ No newline at end of file diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/providers-schema/required/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/providers-schema/required/output.json index ba58e2f3..f14786c3 100644 --- a/vendor/github.com/hashicorp/terraform/command/testdata/providers-schema/required/output.json +++ b/vendor/github.com/hashicorp/terraform/command/testdata/providers-schema/required/output.json @@ -1,5 +1,5 @@ { - "format_version": "0.1", + "format_version": "0.2", "provider_schemas": { "registry.terraform.io/hashicorp/test": { "provider": { @@ -30,6 +30,25 @@ "optional": true, "computed": true, "description_kind": "plain" + }, + "volumes": { + "nested_type": { + "nesting_mode": "list", + "attributes": { + "size": { + "type": "string", + "required": true, + "description_kind": "plain" + }, + "mount_point": { + "type": "string", + "required": true, + "description_kind": "plain" + } + } + }, + "description_kind": "plain", + "optional": true } }, "description_kind": "plain" diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/refresh-targeted/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/refresh-targeted/main.tf new file mode 100644 index 00000000..734f5854 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/refresh-targeted/main.tf @@ -0,0 +1,7 @@ +resource "test_instance" "foo" { + id = "foo" +} + +resource "test_instance" "bar" { + id = "bar" +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json-sensitive/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/show-json-sensitive/main.tf new file mode 100644 index 00000000..a980e4dc --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json-sensitive/main.tf @@ -0,0 +1,21 @@ +provider "test" { + region = "somewhere" +} + +variable "test_var" { + default = "bar" + sensitive = true +} + +resource "test_instance" "test" { + // this variable is sensitive + ami = var.test_var + // the password attribute is sensitive in the showFixtureSensitiveProvider schema. + password = "secret" + count = 3 +} + +output "test" { + value = var.test_var + sensitive = true +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json-sensitive/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/show-json-sensitive/output.json new file mode 100644 index 00000000..f625fb31 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json-sensitive/output.json @@ -0,0 +1,205 @@ +{ + "format_version": "0.1", + "variables": { + "test_var": { + "value": "bar" + } + }, + "planned_values": { + "outputs": { + "test": { + "sensitive": true, + "value": "bar" + } + }, + "root_module": { + "resources": [ + { + "address": "test_instance.test[0]", + "index": 0, + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "ami": "bar", + "password": "secret" + } + }, + { + "address": "test_instance.test[1]", + "index": 1, + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "ami": "bar", + "password": "secret" + } + }, + { + "address": "test_instance.test[2]", + "index": 2, + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "ami": "bar", + "password": "secret" + } + } + ] + } + }, + "prior_state": { + "format_version": "0.1", + "values": { + "outputs": { + "test": { + "sensitive": true, + "value": "bar" + } + }, + "root_module": {} + } + }, + "resource_changes": [ + { + "address": "test_instance.test[0]", + "index": 0, + "mode": "managed", + "type": "test_instance", + "provider_name": "registry.terraform.io/hashicorp/test", + "name": "test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after_unknown": { + "id": true + }, + "after": { + "ami": "bar", + "password": "secret" + }, + "after_sensitive": {"ami": true, "password": true}, + "before_sensitive": false + } + }, + { + "address": "test_instance.test[1]", + "index": 1, + "mode": "managed", + "type": "test_instance", + "provider_name": "registry.terraform.io/hashicorp/test", + "name": "test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after_unknown": { + "id": true + }, + "after": { + "ami": "bar", + "password": "secret" + }, + "after_sensitive": {"ami": true, "password": true}, + "before_sensitive": false + } + }, + { + "address": "test_instance.test[2]", + "index": 2, + "mode": "managed", + "type": "test_instance", + "provider_name": "registry.terraform.io/hashicorp/test", + "name": "test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after_unknown": { + "id": true + }, + "after": { + "ami": "bar", + "password": "secret" + }, + "after_sensitive": {"ami": true, "password": true}, + "before_sensitive": false + } + } + ], + "output_changes": { + "test": { + "actions": [ + "create" + ], + "before": null, + "after": "bar", + "after_unknown": false, + "before_sensitive": true, + "after_sensitive": true + } + }, + "configuration": { + "provider_config": { + "test": { + "name": "test", + "expressions": { + "region": { + "constant_value": "somewhere" + } + } + } + }, + "root_module": { + "outputs": { + "test": { + "expression": { + "references": [ + "var.test_var" + ] + }, + "sensitive": true + } + }, + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_config_key": "test", + "schema_version": 0, + "expressions": { + "ami": { + "references": [ + "var.test_var" + ] + }, + "password": {"constant_value": "secret"} + }, + "count_expression": { + "constant_value": 3 + } + } + ], + "variables": { + "test_var": { + "default": "bar", + "sensitive": true + } + } + } + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json-state/sensitive-variables/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/show-json-state/sensitive-variables/output.json new file mode 100644 index 00000000..a4e74aa3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json-state/sensitive-variables/output.json @@ -0,0 +1,22 @@ +{ + "format_version": "0.1", + "terraform_version": "0.14.0", + "values": { + "root_module": { + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "id": "621124146446964903", + "ami": "abc" + } + } + ] + } + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json-state/sensitive-variables/terraform.tfstate b/vendor/github.com/hashicorp/terraform/command/testdata/show-json-state/sensitive-variables/terraform.tfstate new file mode 100644 index 00000000..55712452 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json-state/sensitive-variables/terraform.tfstate @@ -0,0 +1,33 @@ +{ + "version": 4, + "terraform_version": "0.14.0", + "serial": 1, + "lineage": "d7a6880b-6875-288f-13a9-696a65c73036", + "outputs": {}, + "resources": [ + { + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider": "provider[\"registry.terraform.io/hashicorp/test\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "id": "621124146446964903", + "ami": "abc" + }, + "sensitive_attributes": [ + [ + { + "type": "get_attr", + "value": "ami" + } + ] + ], + "private": "bnVsbA==" + } + ] + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/basic-create/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/basic-create/output.json index ff83de3f..017054bc 100644 --- a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/basic-create/output.json +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/basic-create/output.json @@ -83,7 +83,9 @@ }, "after": { "ami": "bar" - } + }, + "after_sensitive": {}, + "before_sensitive": false } }, { @@ -103,7 +105,9 @@ }, "after": { "ami": "bar" - } + }, + "after_sensitive": {}, + "before_sensitive": false } }, { @@ -123,7 +127,9 @@ }, "after": { "ami": "bar" - } + }, + "after_sensitive": {}, + "before_sensitive": false } } ], @@ -134,7 +140,9 @@ ], "before": null, "after": "bar", - "after_unknown": false + "after_unknown": false, + "before_sensitive": false, + "after_sensitive": false } }, "configuration": { diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/basic-delete/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/basic-delete/output.json index f9580b40..6b29d785 100644 --- a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/basic-delete/output.json +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/basic-delete/output.json @@ -48,7 +48,9 @@ "ami": "bar", "id": "placeholder" }, - "after_unknown": {} + "after_unknown": {}, + "after_sensitive": {}, + "before_sensitive": {} } }, { @@ -66,7 +68,9 @@ "id": "placeholder" }, "after": null, - "after_unknown": {} + "after_unknown": {}, + "after_sensitive": false, + "before_sensitive": {} } } ], @@ -77,7 +81,9 @@ ], "before": null, "after": "bar", - "after_unknown": false + "after_unknown": false, + "before_sensitive": false, + "after_sensitive": false } }, "prior_state": { diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/basic-update/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/basic-update/output.json index 5486bece..a6779801 100644 --- a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/basic-update/output.json +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/basic-update/output.json @@ -48,7 +48,9 @@ "ami": "bar", "id": "placeholder" }, - "after_unknown": {} + "after_unknown": {}, + "after_sensitive": {}, + "before_sensitive": {} } } ], @@ -59,7 +61,9 @@ ], "before": "bar", "after": "bar", - "after_unknown": false + "after_unknown": false, + "before_sensitive": false, + "after_sensitive": false } }, "prior_state": { diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/module-depends-on/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/module-depends-on/output.json index 26e7e218..5bfb8969 100644 --- a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/module-depends-on/output.json +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/module-depends-on/output.json @@ -35,7 +35,9 @@ }, "after_unknown": { "id": true - } + }, + "after_sensitive": {}, + "before_sensitive": false } } ], diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/modules/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/modules/output.json index 898763aa..445f269c 100644 --- a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/modules/output.json +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/modules/output.json @@ -99,7 +99,9 @@ }, "after_unknown": { "id": true - } + }, + "after_sensitive": {}, + "before_sensitive": false } }, { @@ -120,7 +122,9 @@ }, "after_unknown": { "id": true - } + }, + "after_sensitive": {}, + "before_sensitive": false } }, { @@ -141,7 +145,9 @@ }, "after_unknown": { "id": true - } + }, + "after_sensitive": {}, + "before_sensitive": false } }, { @@ -162,7 +168,9 @@ }, "after_unknown": { "id": true - } + }, + "after_sensitive": {}, + "before_sensitive": false } } ], @@ -173,7 +181,9 @@ ], "before": null, "after": "baz", - "after_unknown": false + "after_unknown": false, + "before_sensitive": false, + "after_sensitive": false } }, "configuration": { diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/multi-resource-update/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/multi-resource-update/output.json index 6725c254..564a4d71 100644 --- a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/multi-resource-update/output.json +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/multi-resource-update/output.json @@ -63,7 +63,9 @@ "ami": "bar", "id": "placeholder" }, - "after_unknown": {} + "after_unknown": {}, + "after_sensitive": {}, + "before_sensitive": {} } }, { @@ -83,7 +85,9 @@ }, "after_unknown": { "id": true - } + }, + "after_sensitive": {}, + "before_sensitive": false } } ], @@ -94,7 +98,9 @@ ], "before": "bar", "after": "bar", - "after_unknown": false + "after_unknown": false, + "before_sensitive": false, + "after_sensitive": false } }, "prior_state": { diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/nested-modules/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/nested-modules/output.json index 369a58a4..96e6b39e 100644 --- a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/nested-modules/output.json +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/nested-modules/output.json @@ -45,7 +45,9 @@ }, "after_unknown": { "id": true - } + }, + "after_sensitive": {}, + "before_sensitive": false } } ], diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/provider-version-no-config/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/provider-version-no-config/main.tf new file mode 100644 index 00000000..c5df9bf7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/provider-version-no-config/main.tf @@ -0,0 +1,21 @@ +terraform { + required_providers { + test = { + source = "hashicorp/test" + version = ">= 1.2.3" + } + } +} + +variable "test_var" { + default = "bar" +} + +resource "test_instance" "test" { + ami = var.test_var + count = 3 +} + +output "test" { + value = var.test_var +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/provider-version-no-config/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/provider-version-no-config/output.json new file mode 100644 index 00000000..7e0b841f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/provider-version-no-config/output.json @@ -0,0 +1,192 @@ +{ + "format_version": "0.1", + "variables": { + "test_var": { + "value": "bar" + } + }, + "planned_values": { + "outputs": { + "test": { + "sensitive": false, + "value": "bar" + } + }, + "root_module": { + "resources": [ + { + "address": "test_instance.test[0]", + "index": 0, + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "ami": "bar" + } + }, + { + "address": "test_instance.test[1]", + "index": 1, + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "ami": "bar" + } + }, + { + "address": "test_instance.test[2]", + "index": 2, + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "ami": "bar" + } + } + ] + } + }, + "prior_state": { + "format_version": "0.1", + "values": { + "outputs": { + "test": { + "sensitive": false, + "value": "bar" + } + }, + "root_module": {} + } + }, + "resource_changes": [ + { + "address": "test_instance.test[0]", + "index": 0, + "mode": "managed", + "type": "test_instance", + "provider_name": "registry.terraform.io/hashicorp/test", + "name": "test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after_unknown": { + "id": true + }, + "after": { + "ami": "bar" + }, + "after_sensitive": {}, + "before_sensitive": false + } + }, + { + "address": "test_instance.test[1]", + "index": 1, + "mode": "managed", + "type": "test_instance", + "provider_name": "registry.terraform.io/hashicorp/test", + "name": "test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after_unknown": { + "id": true + }, + "after": { + "ami": "bar" + }, + "after_sensitive": {}, + "before_sensitive": false + } + }, + { + "address": "test_instance.test[2]", + "index": 2, + "mode": "managed", + "type": "test_instance", + "provider_name": "registry.terraform.io/hashicorp/test", + "name": "test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after_unknown": { + "id": true + }, + "after": { + "ami": "bar" + }, + "after_sensitive": {}, + "before_sensitive": false + } + } + ], + "output_changes": { + "test": { + "actions": [ + "create" + ], + "before": null, + "after": "bar", + "after_unknown": false, + "before_sensitive": false, + "after_sensitive": false + } + }, + "configuration": { + "provider_config": { + "test": { + "name": "test", + "version_constraint": ">= 1.2.3" + } + }, + "root_module": { + "outputs": { + "test": { + "expression": { + "references": [ + "var.test_var" + ] + } + } + }, + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_config_key": "test", + "schema_version": 0, + "expressions": { + "ami": { + "references": [ + "var.test_var" + ] + } + }, + "count_expression": { + "constant_value": 3 + } + } + ], + "variables": { + "test_var": { + "default": "bar" + } + } + } + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/provider-version/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/provider-version/main.tf new file mode 100644 index 00000000..b6d3cb97 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/provider-version/main.tf @@ -0,0 +1,26 @@ +terraform { + required_providers { + test = { + source = "hashicorp/test" + version = ">= 1.2.3" + } + } +} + +provider "test" { + region = "somewhere" + version = "1.2.3" +} + +variable "test_var" { + default = "bar" +} + +resource "test_instance" "test" { + ami = var.test_var + count = 3 +} + +output "test" { + value = var.test_var +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/provider-version/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/provider-version/output.json new file mode 100644 index 00000000..eef936ec --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/provider-version/output.json @@ -0,0 +1,197 @@ +{ + "format_version": "0.1", + "variables": { + "test_var": { + "value": "bar" + } + }, + "planned_values": { + "outputs": { + "test": { + "sensitive": false, + "value": "bar" + } + }, + "root_module": { + "resources": [ + { + "address": "test_instance.test[0]", + "index": 0, + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "ami": "bar" + } + }, + { + "address": "test_instance.test[1]", + "index": 1, + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "ami": "bar" + } + }, + { + "address": "test_instance.test[2]", + "index": 2, + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "ami": "bar" + } + } + ] + } + }, + "prior_state": { + "format_version": "0.1", + "values": { + "outputs": { + "test": { + "sensitive": false, + "value": "bar" + } + }, + "root_module": {} + } + }, + "resource_changes": [ + { + "address": "test_instance.test[0]", + "index": 0, + "mode": "managed", + "type": "test_instance", + "provider_name": "registry.terraform.io/hashicorp/test", + "name": "test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after_unknown": { + "id": true + }, + "after": { + "ami": "bar" + }, + "after_sensitive": {}, + "before_sensitive": false + } + }, + { + "address": "test_instance.test[1]", + "index": 1, + "mode": "managed", + "type": "test_instance", + "provider_name": "registry.terraform.io/hashicorp/test", + "name": "test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after_unknown": { + "id": true + }, + "after": { + "ami": "bar" + }, + "after_sensitive": {}, + "before_sensitive": false + } + }, + { + "address": "test_instance.test[2]", + "index": 2, + "mode": "managed", + "type": "test_instance", + "provider_name": "registry.terraform.io/hashicorp/test", + "name": "test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after_unknown": { + "id": true + }, + "after": { + "ami": "bar" + }, + "after_sensitive": {}, + "before_sensitive": false + } + } + ], + "output_changes": { + "test": { + "actions": [ + "create" + ], + "before": null, + "after": "bar", + "after_unknown": false, + "before_sensitive": false, + "after_sensitive": false + } + }, + "configuration": { + "provider_config": { + "test": { + "name": "test", + "expressions": { + "region": { + "constant_value": "somewhere" + } + }, + "version_constraint": ">= 1.2.3, 1.2.3" + } + }, + "root_module": { + "outputs": { + "test": { + "expression": { + "references": [ + "var.test_var" + ] + } + } + }, + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_config_key": "test", + "schema_version": 0, + "expressions": { + "ami": { + "references": [ + "var.test_var" + ] + } + }, + "count_expression": { + "constant_value": 3 + } + } + ], + "variables": { + "test_var": { + "default": "bar" + } + } + } + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/requires-replace/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/requires-replace/main.tf new file mode 100644 index 00000000..6be6611c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/requires-replace/main.tf @@ -0,0 +1,3 @@ +resource "test_instance" "test" { + ami = "force-replace" +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/requires-replace/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/requires-replace/output.json new file mode 100644 index 00000000..34dddcef --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/requires-replace/output.json @@ -0,0 +1,89 @@ +{ + "format_version": "0.1", + "planned_values": { + "root_module": { + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "ami": "force-replace" + } + } + ] + } + }, + "resource_changes": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "provider_name": "registry.terraform.io/hashicorp/test", + "name": "test", + "change": { + "actions": [ + "delete", + "create" + ], + "before": { + "ami": "bar", + "id": "placeholder" + }, + "after": { + "ami": "force-replace" + }, + "after_unknown": { + "id": true + }, + "after_sensitive": {}, + "before_sensitive": {}, + "replace_paths": [["ami"]] + }, + "action_reason": "replace_because_cannot_update" + } + ], + "prior_state": { + "format_version": "0.1", + "values": { + "root_module": { + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "schema_version": 0, + "provider_name": "registry.terraform.io/hashicorp/test", + "values": { + "ami": "bar", + "id": "placeholder" + } + } + ] + } + } + }, + "configuration": { + "root_module": { + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_config_key": "test", + "schema_version": 0, + "expressions": { + "ami": { + "constant_value": "force-replace" + } + } + } + ] + } + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/requires-replace/terraform.tfstate b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/requires-replace/terraform.tfstate new file mode 100644 index 00000000..b57f60f8 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/requires-replace/terraform.tfstate @@ -0,0 +1,24 @@ +{ + "version": 4, + "terraform_version": "0.12.0", + "serial": 7, + "lineage": "configuredUnchanged", + "outputs": {}, + "resources": [ + { + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider": "provider[\"registry.terraform.io/hashicorp/test\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "ami": "bar", + "id": "placeholder" + } + } + ] + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/sensitive-values/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/sensitive-values/main.tf new file mode 100644 index 00000000..3f8ba824 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/sensitive-values/main.tf @@ -0,0 +1,13 @@ +variable "test_var" { + default = "boop" + sensitive = true +} + +resource "test_instance" "test" { + ami = var.test_var +} + +output "test" { + value = test_instance.test.ami + sensitive = true +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/show-json/sensitive-values/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/sensitive-values/output.json new file mode 100644 index 00000000..51105382 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/show-json/sensitive-values/output.json @@ -0,0 +1,117 @@ +{ + "format_version": "0.1", + "variables": { + "test_var": { + "value": "boop" + } + }, + "planned_values": { + "outputs": { + "test": { + "sensitive": true, + "value": "boop" + } + }, + "root_module": { + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "ami": "boop" + } + } + ] + } + }, + "resource_changes": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "provider_name": "registry.terraform.io/hashicorp/test", + "name": "test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "ami": "boop" + }, + "after_unknown": { + "id": true + }, + "after_sensitive": { + "ami": true + }, + "before_sensitive": false + } + } + ], + "output_changes": { + "test": { + "actions": [ + "create" + ], + "before": null, + "after": "boop", + "after_unknown": false, + "before_sensitive": true, + "after_sensitive": true + } + }, + "prior_state": { + "format_version": "0.1", + "values": { + "outputs": { + "test": { + "sensitive": true, + "value": "boop" + } + }, + "root_module": {} + } + }, + "configuration": { + "root_module": { + "outputs": { + "test": { + "expression": { + "references": [ + "test_instance.test" + ] + }, + "sensitive": true + } + }, + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_config_key": "test", + "schema_version": 0, + "expressions": { + "ami": { + "references": [ + "var.test_var" + ] + } + } + } + ], + "variables": { + "test_var": { + "default": "boop", + "sensitive": true + } + } + } + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/state-list-nested-modules/terraform.tfstate b/vendor/github.com/hashicorp/terraform/command/testdata/state-list-nested-modules/terraform.tfstate new file mode 100644 index 00000000..3e4689a8 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/state-list-nested-modules/terraform.tfstate @@ -0,0 +1,91 @@ +{ + "version": 4, + "terraform_version": "0.15.0", + "serial": 8, + "lineage": "00bfda35-ad61-ec8d-c013-14b0320bc416", + "resources": [ + { + "mode": "managed", + "type": "test_instance", + "name": "root", + "provider": "provider[\"registry.terraform.io/hashicorp/test\"]", + "instances": [ + { + "attributes": { + "id": "root" + } + } + ] + }, + { + "module": "module.nest", + "mode": "managed", + "type": "test_instance", + "name": "nest", + "provider": "provider[\"registry.terraform.io/hashicorp/test\"]", + "instances": [ + { + "attributes": { + "ami": "nested" + } + } + ] + }, + { + "module": "module.nest.module.subnest", + "mode": "managed", + "type": "test_instance", + "name": "subnest", + "provider": "provider[\"registry.terraform.io/hashicorp/test\"]", + "instances": [ + { + "attributes": { + "id": "subnested" + } + } + ] + }, + { + "module": "module.nonexist.module.child", + "mode": "managed", + "type": "test_instance", + "name": "child", + "provider": "provider[\"registry.terraform.io/hashicorp/test\"]", + "instances": [ + { + "attributes": { + "id": "child" + } + } + ] + }, + { + "module": "module.count[0]", + "mode": "managed", + "type": "test_instance", + "name": "count", + "provider": "provider[\"registry.terraform.io/hashicorp/test\"]", + "instances": [ + { + "attributes": { + "id": "zero" + } + } + ] + }, + { + "module": "module.count[1]", + "mode": "managed", + "type": "test_instance", + "name": "count", + "provider": "provider[\"registry.terraform.io/hashicorp/test\"]", + "instances": [ + { + "attributes": { + "id": "one" + } + } + ] + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/test-fails/test-fails.tf b/vendor/github.com/hashicorp/terraform/command/testdata/test-fails/test-fails.tf new file mode 100644 index 00000000..d618fb9d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/test-fails/test-fails.tf @@ -0,0 +1,7 @@ +variable "input" { + type = string +} + +output "foo" { + value = "foo value ${var.input}" +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/test-fails/tests/hello/hello.tf b/vendor/github.com/hashicorp/terraform/command/testdata/test-fails/tests/hello/hello.tf new file mode 100644 index 00000000..6fbf8ee6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/test-fails/tests/hello/hello.tf @@ -0,0 +1,23 @@ +terraform { + required_providers { + test = { + source = "terraform.io/builtin/test" + } + } +} + +module "main" { + source = "../.." + + input = "boop" +} + +resource "test_assertions" "foo" { + component = "foo" + + equal "output" { + description = "output \"foo\" value" + got = module.main.foo + want = "foo not boop" + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/test-passes/test-passes.tf b/vendor/github.com/hashicorp/terraform/command/testdata/test-passes/test-passes.tf new file mode 100644 index 00000000..d618fb9d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/test-passes/test-passes.tf @@ -0,0 +1,7 @@ +variable "input" { + type = string +} + +output "foo" { + value = "foo value ${var.input}" +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/test-passes/tests/hello/hello.tf b/vendor/github.com/hashicorp/terraform/command/testdata/test-passes/tests/hello/hello.tf new file mode 100644 index 00000000..2129f43d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/test-passes/tests/hello/hello.tf @@ -0,0 +1,23 @@ +terraform { + required_providers { + test = { + source = "terraform.io/builtin/test" + } + } +} + +module "main" { + source = "../.." + + input = "boop" +} + +resource "test_assertions" "foo" { + component = "foo" + + equal "output" { + description = "output \"foo\" value" + got = module.main.foo + want = "foo value boop" + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/incorrectmodulename/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/incorrectmodulename/main.tf index e58d0ade..45509406 100644 --- a/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/incorrectmodulename/main.tf +++ b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/incorrectmodulename/main.tf @@ -2,5 +2,5 @@ module "super#module" { } module "super" { - source = "${var.modulename}" + source = var.modulename } diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/incorrectmodulename/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/incorrectmodulename/output.json new file mode 100644 index 00000000..0c2ce68a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/incorrectmodulename/output.json @@ -0,0 +1,112 @@ +{ + "format_version": "0.1", + "valid": false, + "error_count": 4, + "warning_count": 0, + "diagnostics": [ + { + "severity": "error", + "summary": "Missing required argument", + "detail": "The argument \"source\" is required, but no definition was found.", + "range": { + "filename": "testdata/validate-invalid/incorrectmodulename/main.tf", + "start": { + "line": 1, + "column": 23, + "byte": 22 + }, + "end": { + "line": 1, + "column": 24, + "byte": 23 + } + }, + "snippet": { + "context": "module \"super#module\"", + "code": "module \"super#module\" {", + "start_line": 1, + "highlight_start_offset": 22, + "highlight_end_offset": 23, + "values": [] + } + }, + { + "severity": "error", + "summary": "Invalid module instance name", + "detail": "A name must start with a letter or underscore and may contain only letters, digits, underscores, and dashes.", + "range": { + "filename": "testdata/validate-invalid/incorrectmodulename/main.tf", + "start": { + "line": 1, + "column": 8, + "byte": 7 + }, + "end": { + "line": 1, + "column": 22, + "byte": 21 + } + }, + "snippet": { + "context": "module \"super#module\"", + "code": "module \"super#module\" {", + "start_line": 1, + "highlight_start_offset": 7, + "highlight_end_offset": 21, + "values": [] + } + }, + { + "severity": "error", + "summary": "Variables not allowed", + "detail": "Variables may not be used here.", + "range": { + "filename": "testdata/validate-invalid/incorrectmodulename/main.tf", + "start": { + "line": 5, + "column": 12, + "byte": 55 + }, + "end": { + "line": 5, + "column": 15, + "byte": 58 + } + }, + "snippet": { + "context": "module \"super\"", + "code": " source = var.modulename", + "start_line": 5, + "highlight_start_offset": 11, + "highlight_end_offset": 14, + "values": [] + } + }, + { + "severity": "error", + "summary": "Unsuitable value type", + "detail": "Unsuitable value: value must be known", + "range": { + "filename": "testdata/validate-invalid/incorrectmodulename/main.tf", + "start": { + "line": 5, + "column": 12, + "byte": 55 + }, + "end": { + "line": 5, + "column": 26, + "byte": 69 + } + }, + "snippet": { + "context": "module \"super\"", + "code": " source = var.modulename", + "start_line": 5, + "highlight_start_offset": 11, + "highlight_end_offset": 25, + "values": [] + } + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/interpolation/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/interpolation/output.json new file mode 100644 index 00000000..7845ec0f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/interpolation/output.json @@ -0,0 +1,60 @@ +{ + "format_version": "0.1", + "valid": false, + "error_count": 2, + "warning_count": 0, + "diagnostics": [ + { + "severity": "error", + "summary": "Variables not allowed", + "detail": "Variables may not be used here.", + "range": { + "filename": "testdata/validate-invalid/interpolation/main.tf", + "start": { + "line": 6, + "column": 16, + "byte": 122 + }, + "end": { + "line": 6, + "column": 19, + "byte": 125 + } + }, + "snippet": { + "context": "variable \"vairable_with_interpolation\"", + "code": " default = \"${var.otherresourcename}\"", + "start_line": 6, + "highlight_start_offset": 15, + "highlight_end_offset": 18, + "values": [] + } + }, + { + "severity": "error", + "summary": "Invalid expression", + "detail": "A single static variable reference is required: only attribute access and indexing with constant keys. No calculations, function calls, template expressions, etc are allowed here.", + "range": { + "filename": "testdata/validate-invalid/interpolation/main.tf", + "start": { + "line": 10, + "column": 17, + "byte": 197 + }, + "end": { + "line": 10, + "column": 44, + "byte": 224 + } + }, + "snippet": { + "context": "resource \"aws_instance\" \"web\"", + "code": " depends_on = [\"${var.otherresourcename}}\"]", + "start_line": 10, + "highlight_start_offset": 16, + "highlight_end_offset": 43, + "values": [] + } + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/missing_defined_var/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/missing_defined_var/output.json new file mode 100644 index 00000000..c2a57c5e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/missing_defined_var/output.json @@ -0,0 +1,7 @@ +{ + "format_version": "0.1", + "valid": true, + "error_count": 0, + "warning_count": 0, + "diagnostics": [] +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/missing_quote/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/missing_quote/output.json new file mode 100644 index 00000000..cdf99d8b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/missing_quote/output.json @@ -0,0 +1,34 @@ +{ + "format_version": "0.1", + "valid": false, + "error_count": 1, + "warning_count": 0, + "diagnostics": [ + { + "severity": "error", + "summary": "Invalid reference", + "detail": "A reference to a resource type must be followed by at least one attribute access, specifying the resource name.", + "range": { + "filename": "testdata/validate-invalid/missing_quote/main.tf", + "start": { + "line": 6, + "column": 14, + "byte": 110 + }, + "end": { + "line": 6, + "column": 18, + "byte": 114 + } + }, + "snippet": { + "context": "resource \"test_instance\" \"foo\"", + "code": " name = test", + "start_line": 6, + "highlight_start_offset": 13, + "highlight_end_offset": 17, + "values": [] + } + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/missing_var/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/missing_var/main.tf index 385828cb..37a77555 100644 --- a/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/missing_var/main.tf +++ b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/missing_var/main.tf @@ -3,6 +3,6 @@ resource "test_instance" "foo" { network_interface { device_index = 0 - description = "${var.description}" + description = var.description } } diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/missing_var/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/missing_var/output.json new file mode 100644 index 00000000..2a4e0be7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/missing_var/output.json @@ -0,0 +1,34 @@ +{ + "format_version": "0.1", + "valid": false, + "error_count": 1, + "warning_count": 0, + "diagnostics": [ + { + "severity": "error", + "summary": "Reference to undeclared input variable", + "detail": "An input variable with the name \"description\" has not been declared. This variable can be declared with a variable \"description\" {} block.", + "range": { + "filename": "testdata/validate-invalid/missing_var/main.tf", + "start": { + "line": 6, + "column": 21, + "byte": 117 + }, + "end": { + "line": 6, + "column": 36, + "byte": 132 + } + }, + "snippet": { + "context": "resource \"test_instance\" \"foo\"", + "code": " description = var.description", + "start_line": 6, + "highlight_start_offset": 20, + "highlight_end_offset": 35, + "values": [] + } + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/multiple_modules/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/multiple_modules/output.json new file mode 100644 index 00000000..4cd6dfb9 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/multiple_modules/output.json @@ -0,0 +1,34 @@ +{ + "format_version": "0.1", + "valid": false, + "error_count": 1, + "warning_count": 0, + "diagnostics": [ + { + "severity": "error", + "summary": "Duplicate module call", + "detail": "A module call named \"multi_module\" was already defined at testdata/validate-invalid/multiple_modules/main.tf:1,1-22. Module calls must have unique names within a module.", + "range": { + "filename": "testdata/validate-invalid/multiple_modules/main.tf", + "start": { + "line": 5, + "column": 1, + "byte": 46 + }, + "end": { + "line": 5, + "column": 22, + "byte": 67 + } + }, + "snippet": { + "context": null, + "code": "module \"multi_module\" {", + "start_line": 5, + "highlight_start_offset": 0, + "highlight_end_offset": 21, + "values": [] + } + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/multiple_providers/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/multiple_providers/output.json new file mode 100644 index 00000000..63eb2d19 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/multiple_providers/output.json @@ -0,0 +1,34 @@ +{ + "format_version": "0.1", + "valid": false, + "error_count": 1, + "warning_count": 0, + "diagnostics": [ + { + "severity": "error", + "summary": "Duplicate provider configuration", + "detail": "A default (non-aliased) provider configuration for \"aws\" was already given at testdata/validate-invalid/multiple_providers/main.tf:1,1-15. If multiple configurations are required, set the \"alias\" argument for alternative configurations.", + "range": { + "filename": "testdata/validate-invalid/multiple_providers/main.tf", + "start": { + "line": 7, + "column": 1, + "byte": 85 + }, + "end": { + "line": 7, + "column": 15, + "byte": 99 + } + }, + "snippet": { + "context": null, + "code": "provider \"aws\" {", + "start_line": 7, + "highlight_start_offset": 0, + "highlight_end_offset": 14, + "values": [] + } + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/multiple_resources/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/multiple_resources/output.json new file mode 100644 index 00000000..33d50522 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/multiple_resources/output.json @@ -0,0 +1,34 @@ +{ + "format_version": "0.1", + "valid": false, + "error_count": 1, + "warning_count": 0, + "diagnostics": [ + { + "severity": "error", + "summary": "Duplicate resource \"aws_instance\" configuration", + "detail": "A aws_instance resource named \"web\" was already declared at testdata/validate-invalid/multiple_resources/main.tf:1,1-30. Resource names must be unique per type in each module.", + "range": { + "filename": "testdata/validate-invalid/multiple_resources/main.tf", + "start": { + "line": 4, + "column": 1, + "byte": 35 + }, + "end": { + "line": 4, + "column": 30, + "byte": 64 + } + }, + "snippet": { + "context": null, + "code": "resource \"aws_instance\" \"web\" {", + "start_line": 4, + "highlight_start_offset": 0, + "highlight_end_offset": 29, + "values": [] + } + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/output.json new file mode 100644 index 00000000..663fe015 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/output.json @@ -0,0 +1,34 @@ +{ + "format_version": "0.1", + "valid": false, + "error_count": 1, + "warning_count": 0, + "diagnostics": [ + { + "severity": "error", + "summary": "Unsupported block type", + "detail": "Blocks of type \"resorce\" are not expected here. Did you mean \"resource\"?", + "range": { + "filename": "testdata/validate-invalid/main.tf", + "start": { + "line": 1, + "column": 1, + "byte": 0 + }, + "end": { + "line": 1, + "column": 8, + "byte": 7 + } + }, + "snippet": { + "context": null, + "code": "resorce \"test_instance\" \"foo\" { # Intentional typo to test error reporting", + "start_line": 1, + "highlight_start_offset": 0, + "highlight_end_offset": 7, + "values": [] + } + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/outputs/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/outputs/output.json new file mode 100644 index 00000000..d05ed4b7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/validate-invalid/outputs/output.json @@ -0,0 +1,60 @@ +{ + "format_version": "0.1", + "valid": false, + "error_count": 2, + "warning_count": 0, + "diagnostics": [ + { + "severity": "error", + "summary": "Missing required argument", + "detail": "The argument \"value\" is required, but no definition was found.", + "range": { + "filename": "testdata/validate-invalid/outputs/main.tf", + "start": { + "line": 1, + "column": 18, + "byte": 17 + }, + "end": { + "line": 1, + "column": 19, + "byte": 18 + } + }, + "snippet": { + "context": "output \"myvalue\"", + "code": "output \"myvalue\" {", + "start_line": 1, + "highlight_start_offset": 17, + "highlight_end_offset": 18, + "values": [] + } + }, + { + "severity": "error", + "summary": "Unsupported argument", + "detail": "An argument named \"values\" is not expected here. Did you mean \"value\"?", + "range": { + "filename": "testdata/validate-invalid/outputs/main.tf", + "start": { + "line": 2, + "column": 3, + "byte": 21 + }, + "end": { + "line": 2, + "column": 9, + "byte": 27 + } + }, + "snippet": { + "context": "output \"myvalue\"", + "code": " values = \"Some value\"", + "start_line": 2, + "highlight_start_offset": 2, + "highlight_end_offset": 8, + "values": [] + } + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/validate-valid/output.json b/vendor/github.com/hashicorp/terraform/command/testdata/validate-valid/output.json new file mode 100644 index 00000000..c2a57c5e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/testdata/validate-valid/output.json @@ -0,0 +1,7 @@ +{ + "format_version": "0.1", + "valid": true, + "error_count": 0, + "warning_count": 0, + "diagnostics": [] +} diff --git a/vendor/github.com/hashicorp/terraform/command/testdata/validate-valid/with-tfvars-file/main.tf b/vendor/github.com/hashicorp/terraform/command/testdata/validate-valid/with-tfvars-file/main.tf index 86a19b8f..b318fbf0 100644 --- a/vendor/github.com/hashicorp/terraform/command/testdata/validate-valid/with-tfvars-file/main.tf +++ b/vendor/github.com/hashicorp/terraform/command/testdata/validate-valid/with-tfvars-file/main.tf @@ -1,4 +1,4 @@ variable "var_without_default" { - type = "string" + type = string } diff --git a/vendor/github.com/hashicorp/terraform/command/unlock.go b/vendor/github.com/hashicorp/terraform/command/unlock.go index 94b2f7d8..ce8d4360 100644 --- a/vendor/github.com/hashicorp/terraform/command/unlock.go +++ b/vendor/github.com/hashicorp/terraform/command/unlock.go @@ -30,8 +30,8 @@ func (c *UnlockCommand) Run(args []string) int { } args = cmdFlags.Args() - if len(args) == 0 { - c.Ui.Error("unlock requires a lock id argument") + if len(args) != 1 { + c.Ui.Error("Expected a single argument: LOCK_ID") return cli.RunResultHelp } @@ -116,12 +116,12 @@ func (c *UnlockCommand) Run(args []string) int { func (c *UnlockCommand) Help() string { helpText := ` -Usage: terraform force-unlock LOCK_ID [DIR] +Usage: terraform [global options] force-unlock LOCK_ID Manually unlock the state for the defined configuration. This will not modify your infrastructure. This command removes the lock on the - state for the current configuration. The behavior of this lock is dependent + state for the current workspace. The behavior of this lock is dependent on the backend being used. Local state files cannot be unlocked by another process. @@ -133,7 +133,7 @@ Options: } func (c *UnlockCommand) Synopsis() string { - return "Manually unlock the terraform state" + return "Release a stuck lock on the current workspace" } const outputUnlockSuccess = ` diff --git a/vendor/github.com/hashicorp/terraform/command/unlock_test.go b/vendor/github.com/hashicorp/terraform/command/unlock_test.go index 66b4d3e8..0b42b0cb 100644 --- a/vendor/github.com/hashicorp/terraform/command/unlock_test.go +++ b/vendor/github.com/hashicorp/terraform/command/unlock_test.go @@ -5,8 +5,9 @@ import ( "testing" "github.com/hashicorp/terraform/backend/remote-state/inmem" - "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/cli" + + legacy "github.com/hashicorp/terraform/internal/legacy/terraform" ) // Since we can't unlock a local state file, just test that calling unlock @@ -24,7 +25,7 @@ func TestUnlock(t *testing.T) { if err != nil { t.Fatalf("err: %s", err) } - err = terraform.WriteState(terraform.NewState(), f) + err = legacy.WriteState(legacy.NewState(), f) f.Close() if err != nil { t.Fatalf("err: %s", err) @@ -33,10 +34,12 @@ func TestUnlock(t *testing.T) { p := testProvider() ui := new(cli.MockUi) + view, _ := testView(t) c := &UnlockCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), Ui: ui, + View: view, }, } @@ -55,7 +58,7 @@ func TestUnlock(t *testing.T) { "-force", } - if code := c.Run(args); code != 1 { + if code := c.Run(args); code != cli.RunResultHelp { t.Fatalf("bad: %d\n%s\n%s", code, ui.OutputWriter.String(), ui.ErrorWriter.String()) } } @@ -71,9 +74,11 @@ func TestUnlock_inmemBackend(t *testing.T) { // init backend ui := new(cli.MockUi) + view, _ := testView(t) ci := &InitCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } if code := ci.Run(nil); code != 0 { @@ -83,7 +88,8 @@ func TestUnlock_inmemBackend(t *testing.T) { ui = new(cli.MockUi) c := &UnlockCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -100,7 +106,8 @@ func TestUnlock_inmemBackend(t *testing.T) { ui = new(cli.MockUi) c = &UnlockCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } diff --git a/vendor/github.com/hashicorp/terraform/command/untaint.go b/vendor/github.com/hashicorp/terraform/command/untaint.go index 2f91d448..9b398f14 100644 --- a/vendor/github.com/hashicorp/terraform/command/untaint.go +++ b/vendor/github.com/hashicorp/terraform/command/untaint.go @@ -1,12 +1,13 @@ package command import ( - "context" "fmt" "strings" "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/command/arguments" "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/tfdiags" ) @@ -19,14 +20,12 @@ type UntaintCommand struct { func (c *UntaintCommand) Run(args []string) int { args = c.Meta.process(args) - var module string var allowMissing bool - cmdFlags := c.Meta.defaultFlagSet("untaint") - cmdFlags.BoolVar(&allowMissing, "allow-missing", false, "module") + cmdFlags := c.Meta.ignoreRemoteVersionFlagSet("untaint") + cmdFlags.BoolVar(&allowMissing, "allow-missing", false, "allow missing") cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path") cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state") cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout") - cmdFlags.StringVar(&module, "module", "", "module") cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path") cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } @@ -45,11 +44,6 @@ func (c *UntaintCommand) Run(args []string) int { return 1 } - if module != "" { - c.Ui.Error("The -module option is no longer used. Instead, include the module path in the main resource address, like \"module.foo.module.bar.null_resource.baz\".") - return 1 - } - addr, addrDiags := addrs.ParseAbsResourceInstanceStr(args[0]) diags = diags.Append(addrDiags) if addrDiags.HasErrors() { @@ -65,12 +59,22 @@ func (c *UntaintCommand) Run(args []string) int { return 1 } - // Get the state + // Determine the workspace name workspace, err := c.Workspace() if err != nil { c.Ui.Error(fmt.Sprintf("Error selecting workspace: %s", err)) return 1 } + + // Check remote Terraform version is compatible + remoteVersionDiags := c.remoteBackendVersionCheck(b, workspace) + diags = diags.Append(remoteVersionDiags) + c.showDiagnostics(diags) + if diags.HasErrors() { + return 1 + } + + // Get the state stateMgr, err := b.StateMgr(workspace) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err)) @@ -78,12 +82,16 @@ func (c *UntaintCommand) Run(args []string) int { } if c.stateLock { - stateLocker := clistate.NewLocker(context.Background(), c.stateLockTimeout, c.Ui, c.Colorize()) - if err := stateLocker.Lock(stateMgr, "untaint"); err != nil { - c.Ui.Error(fmt.Sprintf("Error locking state: %s", err)) + stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View)) + if diags := stateLocker.Lock(stateMgr, "untaint"); diags.HasErrors() { + c.showDiagnostics(diags) return 1 } - defer stateLocker.Unlock(nil) + defer func() { + if diags := stateLocker.Unlock(); diags.HasErrors() { + c.showDiagnostics(diags) + } + }() } if err := stateMgr.RefreshState(); err != nil { @@ -173,53 +181,56 @@ func (c *UntaintCommand) Run(args []string) int { func (c *UntaintCommand) Help() string { helpText := ` -Usage: terraform untaint [options] name +Usage: terraform [global options] untaint [options] name + + Terraform uses the term "tainted" to describe a resource instance + which may not be fully functional, either because its creation + partially failed or because you've manually marked it as such using + the "terraform taint" command. - Manually unmark a resource as tainted, restoring it as the primary - instance in the state. This reverses either a manual 'terraform taint' - or the result of provisioners failing on a resource. + This command removes that state from a resource instance, causing + Terraform to see it as fully-functional and not in need of + replacement. - This will not modify your infrastructure. This command changes your - state to unmark a resource as tainted. This command can be undone by - reverting the state backup file that is created, or by running - 'terraform taint' on the resource. + This will not modify your infrastructure directly. It only avoids + Terraform planning to replace a tainted instance in a future operation. Options: - -allow-missing If specified, the command will succeed (exit code 0) - even if the resource is missing. + -allow-missing If specified, the command will succeed (exit code 0) + even if the resource is missing. - -backup=path Path to backup the existing state file before - modifying. Defaults to the "-state-out" path with - ".backup" extension. Set to "-" to disable backup. + -backup=path Path to backup the existing state file before + modifying. Defaults to the "-state-out" path with + ".backup" extension. Set to "-" to disable backup. - -lock=true Lock the state file when locking is supported. + -lock=true Lock the state file when locking is supported. - -lock-timeout=0s Duration to retry a state lock. + -lock-timeout=0s Duration to retry a state lock. - -module=path The module path where the resource lives. By - default this will be root. Child modules can be specified - by names. Ex. "consul" or "consul.vpc" (nested modules). + -state=path Path to read and save state (unless state-out + is specified). Defaults to "terraform.tfstate". - -state=path Path to read and save state (unless state-out - is specified). Defaults to "terraform.tfstate". + -state-out=path Path to write updated state file. By default, the + "-state" path will be used. - -state-out=path Path to write updated state file. By default, the - "-state" path will be used. + -ignore-remote-version Continue even if remote and local Terraform versions + are incompatible. This may result in an unusable + workspace, and should be used with extreme caution. ` return strings.TrimSpace(helpText) } func (c *UntaintCommand) Synopsis() string { - return "Manually unmark a resource as tainted" + return "Remove the 'tainted' state from a resource instance" } func (c *UntaintCommand) allowMissingExit(name addrs.AbsResourceInstance) int { c.showDiagnostics(tfdiags.Sourceless( tfdiags.Warning, "No such resource instance", - "Resource instance %s was not found, but this is not an error because -allow-missing was set.", + fmt.Sprintf("Resource instance %s was not found, but this is not an error because -allow-missing was set.", name), )) return 0 } diff --git a/vendor/github.com/hashicorp/terraform/command/untaint_test.go b/vendor/github.com/hashicorp/terraform/command/untaint_test.go index 3ef0dbf7..5eba15a0 100644 --- a/vendor/github.com/hashicorp/terraform/command/untaint_test.go +++ b/vendor/github.com/hashicorp/terraform/command/untaint_test.go @@ -5,6 +5,7 @@ import ( "strings" "testing" + "github.com/google/go-cmp/cmp" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/states" "github.com/mitchellh/cli" @@ -31,9 +32,11 @@ func TestUntaint(t *testing.T) { statePath := testStateFile(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &UntaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -79,9 +82,11 @@ func TestUntaint_lockedState(t *testing.T) { defer unlock() ui := new(cli.MockUi) + view, _ := testView(t) c := &UntaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -125,9 +130,11 @@ func TestUntaint_backup(t *testing.T) { testStateFileDefault(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &UntaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -179,9 +186,11 @@ func TestUntaint_backupDisable(t *testing.T) { testStateFileDefault(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &UntaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -206,9 +215,11 @@ test_instance.foo: func TestUntaint_badState(t *testing.T) { ui := new(cli.MockUi) + view, _ := testView(t) c := &UntaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -247,9 +258,11 @@ func TestUntaint_defaultState(t *testing.T) { testStateFileDefault(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &UntaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -294,7 +307,8 @@ func TestUntaint_defaultWorkspaceState(t *testing.T) { path := testStateFileWorkspaceDefault(t, testWorkspace, state) ui := new(cli.MockUi) - meta := Meta{Ui: ui} + view, _ := testView(t) + meta := Meta{Ui: ui, View: view} meta.SetWorkspace(testWorkspace) c := &UntaintCommand{ Meta: meta, @@ -335,9 +349,11 @@ func TestUntaint_missing(t *testing.T) { statePath := testStateFile(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &UntaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -371,9 +387,11 @@ func TestUntaint_missingAllow(t *testing.T) { statePath := testStateFile(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &UntaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -385,6 +403,19 @@ func TestUntaint_missingAllow(t *testing.T) { if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } + + // Check for the warning + actual := strings.TrimSpace(ui.ErrorWriter.String()) + expected := strings.TrimSpace(` +Warning: No such resource instance + +Resource instance test_instance.bar was not found, but this is not an error +because -allow-missing was set. + +`) + if diff := cmp.Diff(expected, actual); diff != "" { + t.Fatalf("wrong output\n%s", diff) + } } func TestUntaint_stateOut(t *testing.T) { @@ -413,9 +444,11 @@ func TestUntaint_stateOut(t *testing.T) { testStateFileDefault(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &UntaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } @@ -475,9 +508,11 @@ func TestUntaint_module(t *testing.T) { statePath := testStateFile(t, state) ui := new(cli.MockUi) + view, _ := testView(t) c := &UntaintCommand{ Meta: Meta{ - Ui: ui, + Ui: ui, + View: view, }, } diff --git a/vendor/github.com/hashicorp/terraform/command/validate.go b/vendor/github.com/hashicorp/terraform/command/validate.go index e9c8914d..765a3292 100644 --- a/vendor/github.com/hashicorp/terraform/command/validate.go +++ b/vendor/github.com/hashicorp/terraform/command/validate.go @@ -1,13 +1,14 @@ package command import ( - "encoding/json" "fmt" "path/filepath" "strings" "github.com/zclconf/go-cty/cty" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/tfdiags" ) @@ -17,61 +18,37 @@ type ValidateCommand struct { Meta } -func (c *ValidateCommand) Run(args []string) int { - args = c.Meta.process(args) - // TODO: The `var` and `var-file` options are not actually used, and should - // be removed in the next major release. - if c.Meta.variableArgs.items == nil { - c.Meta.variableArgs = newRawFlags("-var") - } - varValues := c.Meta.variableArgs.Alias("-var") - varFiles := c.Meta.variableArgs.Alias("-var-file") +func (c *ValidateCommand) Run(rawArgs []string) int { + // Parse and apply global view arguments + common, rawArgs := arguments.ParseView(rawArgs) + c.View.Configure(common) - var jsonOutput bool - cmdFlags := c.Meta.defaultFlagSet("validate") - cmdFlags.BoolVar(&jsonOutput, "json", false, "produce JSON output") - cmdFlags.Var(varValues, "var", "variables") - cmdFlags.Var(varFiles, "var-file", "variable file") - cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } - if err := cmdFlags.Parse(args); err != nil { - c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s\n", err.Error())) + // Parse and validate flags + args, diags := arguments.ParseValidate(rawArgs) + if diags.HasErrors() { + c.View.Diagnostics(diags) + c.View.HelpPrompt("validate") return 1 } - var diags tfdiags.Diagnostics - - // If set, output a warning indicating that these values are not used. - if !varValues.Empty() || !varFiles.Empty() { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Warning, - "The -var and -var-file flags are not used in validate. Setting them has no effect.", - "These flags will be removed in a future version of Terraform.", - )) - } + view := views.NewValidate(args.ViewType, c.View) // After this point, we must only produce JSON output if JSON mode is // enabled, so all errors should be accumulated into diags and we'll // print out a suitable result at the end, depending on the format // selection. All returns from this point on must be tail-calls into - // c.showResults in order to produce the expected output. - args = cmdFlags.Args() + // view.Results in order to produce the expected output. - var dirPath string - if len(args) == 1 { - dirPath = args[0] - } else { - dirPath = "." - } - dir, err := filepath.Abs(dirPath) + dir, err := filepath.Abs(args.Path) if err != nil { diags = diags.Append(fmt.Errorf("unable to locate module: %s", err)) - return c.showResults(diags, jsonOutput) + return view.Results(diags) } // Check for user-supplied plugin path if c.pluginPath, err = c.loadPluginPath(); err != nil { diags = diags.Append(fmt.Errorf("error loading plugin path: %s", err)) - return c.showResults(diags, jsonOutput) + return view.Results(diags) } validateDiags := c.validate(dir) @@ -81,9 +58,9 @@ func (c *ValidateCommand) Run(args []string) int { // not be valid for a stable release, so we'll warn about that in case // the user is trying to use "terraform validate" as a sort of pre-flight // check before submitting a change. - diags = diags.Append(c.providerDevOverrideWarnings()) + diags = diags.Append(c.providerDevOverrideRuntimeWarnings()) - return c.showResults(diags, jsonOutput) + return view.Results(diags) } func (c *ValidateCommand) validate(dir string) tfdiags.Diagnostics { @@ -133,114 +110,13 @@ func (c *ValidateCommand) validate(dir string) tfdiags.Diagnostics { return diags } -func (c *ValidateCommand) showResults(diags tfdiags.Diagnostics, jsonOutput bool) int { - switch { - case jsonOutput: - // FIXME: Eventually we'll probably want to factor this out somewhere - // to support machine-readable outputs for other commands too, but for - // now it's simplest to do this inline here. - type Pos struct { - Line int `json:"line"` - Column int `json:"column"` - Byte int `json:"byte"` - } - type Range struct { - Filename string `json:"filename"` - Start Pos `json:"start"` - End Pos `json:"end"` - } - type Diagnostic struct { - Severity string `json:"severity,omitempty"` - Summary string `json:"summary,omitempty"` - Detail string `json:"detail,omitempty"` - Range *Range `json:"range,omitempty"` - } - type Output struct { - // We include some summary information that is actually redundant - // with the detailed diagnostics, but avoids the need for callers - // to re-implement our logic for deciding these. - Valid bool `json:"valid"` - ErrorCount int `json:"error_count"` - WarningCount int `json:"warning_count"` - Diagnostics []Diagnostic `json:"diagnostics"` - } - - var output Output - output.Valid = true // until proven otherwise - for _, diag := range diags { - var jsonDiag Diagnostic - switch diag.Severity() { - case tfdiags.Error: - jsonDiag.Severity = "error" - output.ErrorCount++ - output.Valid = false - case tfdiags.Warning: - jsonDiag.Severity = "warning" - output.WarningCount++ - } - - desc := diag.Description() - jsonDiag.Summary = desc.Summary - jsonDiag.Detail = desc.Detail - - ranges := diag.Source() - if ranges.Subject != nil { - subj := ranges.Subject - jsonDiag.Range = &Range{ - Filename: subj.Filename, - Start: Pos{ - Line: subj.Start.Line, - Column: subj.Start.Column, - Byte: subj.Start.Byte, - }, - End: Pos{ - Line: subj.End.Line, - Column: subj.End.Column, - Byte: subj.End.Byte, - }, - } - } - - output.Diagnostics = append(output.Diagnostics, jsonDiag) - } - if output.Diagnostics == nil { - // Make sure this always appears as an array in our output, since - // this is easier to consume for dynamically-typed languages. - output.Diagnostics = []Diagnostic{} - } - - j, err := json.MarshalIndent(&output, "", " ") - if err != nil { - // Should never happen because we fully-control the input here - panic(err) - } - c.Ui.Output(string(j)) - - default: - if len(diags) == 0 { - c.Ui.Output(c.Colorize().Color("[green][bold]Success![reset] The configuration is valid.\n")) - } else { - c.showDiagnostics(diags) - - if !diags.HasErrors() { - c.Ui.Output(c.Colorize().Color("[green][bold]Success![reset] The configuration is valid, but there were some validation warnings as shown above.\n")) - } - } - } - - if diags.HasErrors() { - return 1 - } - return 0 -} - func (c *ValidateCommand) Synopsis() string { - return "Validates the Terraform files" + return "Check whether the configuration is valid" } func (c *ValidateCommand) Help() string { helpText := ` -Usage: terraform validate [options] [dir] +Usage: terraform [global options] validate [options] Validate the configuration files in a directory, referring only to the configuration and not accessing any remote services such as remote state, @@ -260,8 +136,6 @@ Usage: terraform validate [options] [dir] validation without accessing any configured remote backend, use: terraform init -backend=false - If dir is not specified, then the current directory will be used. - To verify configuration in the context of a particular run (a particular target workspace, input variable values, etc), use the 'terraform plan' command instead, which includes an implied validation check. diff --git a/vendor/github.com/hashicorp/terraform/command/validate_test.go b/vendor/github.com/hashicorp/terraform/command/validate_test.go index cc5242f2..c6278da4 100644 --- a/vendor/github.com/hashicorp/terraform/command/validate_test.go +++ b/vendor/github.com/hashicorp/terraform/command/validate_test.go @@ -1,33 +1,40 @@ package command import ( + "encoding/json" + "io/ioutil" "os" + "path" "strings" "testing" - "github.com/mitchellh/cli" + "github.com/google/go-cmp/cmp" "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/internal/terminal" + "github.com/hashicorp/terraform/providers" ) -func setupTest(fixturepath string, args ...string) (*cli.MockUi, int) { - ui := new(cli.MockUi) +func setupTest(t *testing.T, fixturepath string, args ...string) (*terminal.TestOutput, int) { + view, done := testView(t) p := testProvider() - p.GetSchemaReturn = &terraform.ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "ami": {Type: cty.String, Optional: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "network_interface": { - Nesting: configschema.NestingList, - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "device_index": {Type: cty.String, Optional: true}, - "description": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "ami": {Type: cty.String, Optional: true}, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "network_interface": { + Nesting: configschema.NestingList, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "device_index": {Type: cty.String, Optional: true}, + "description": {Type: cty.String, Optional: true}, + "name": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -38,19 +45,20 @@ func setupTest(fixturepath string, args ...string) (*cli.MockUi, int) { c := &ValidateCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(p), - Ui: ui, + View: view, }, } + args = append(args, "-no-color") args = append(args, testFixturePath(fixturepath)) code := c.Run(args) - return ui, code + return done(t), code } func TestValidateCommand(t *testing.T) { - if ui, code := setupTest("validate-valid"); code != 0 { - t.Fatalf("unexpected non-successful exit code %d\n\n%s", code, ui.ErrorWriter.String()) + if output, code := setupTest(t, "validate-valid"); code != 0 { + t.Fatalf("unexpected non-successful exit code %d\n\n%s", code, output.Stderr()) } } @@ -62,138 +70,198 @@ func TestValidateCommandWithTfvarsFile(t *testing.T) { defer os.RemoveAll(td) defer testChdir(t, td)() - ui := new(cli.MockUi) + view, done := testView(t) c := &ValidateCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), - Ui: ui, + View: view, }, } args := []string{} - if code := c.Run(args); code != 0 { - t.Fatalf("bad %d\n\n%s", code, ui.ErrorWriter.String()) + code := c.Run(args) + output := done(t) + if code != 0 { + t.Fatalf("bad %d\n\n%s", code, output.Stderr()) } } func TestValidateFailingCommand(t *testing.T) { - if ui, code := setupTest("validate-invalid"); code != 1 { - t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String()) + if output, code := setupTest(t, "validate-invalid"); code != 1 { + t.Fatalf("Should have failed: %d\n\n%s", code, output.Stderr()) } } func TestValidateFailingCommandMissingQuote(t *testing.T) { - // FIXME: Re-enable once we've updated core for new data structures - t.Skip("test temporarily disabled until deep validate supports new config structures") - - ui, code := setupTest("validate-invalid/missing_quote") + output, code := setupTest(t, "validate-invalid/missing_quote") if code != 1 { - t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String()) + t.Fatalf("Should have failed: %d\n\n%s", code, output.Stderr()) } - if !strings.HasSuffix(strings.TrimSpace(ui.ErrorWriter.String()), "IDENT test") { - t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String()) + wantError := "Error: Invalid reference" + if !strings.Contains(output.Stderr(), wantError) { + t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr()) } } func TestValidateFailingCommandMissingVariable(t *testing.T) { - // FIXME: Re-enable once we've updated core for new data structures - t.Skip("test temporarily disabled until deep validate supports new config structures") - - ui, code := setupTest("validate-invalid/missing_var") + output, code := setupTest(t, "validate-invalid/missing_var") if code != 1 { - t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String()) + t.Fatalf("Should have failed: %d\n\n%s", code, output.Stderr()) } - if !strings.HasSuffix(strings.TrimSpace(ui.ErrorWriter.String()), "config: unknown variable referenced: 'description'; define it with a 'variable' block") { - t.Fatalf("Should have failed: %d\n\n'%s'", code, ui.ErrorWriter.String()) + wantError := "Error: Reference to undeclared input variable" + if !strings.Contains(output.Stderr(), wantError) { + t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr()) } } func TestSameProviderMutipleTimesShouldFail(t *testing.T) { - ui, code := setupTest("validate-invalid/multiple_providers") + output, code := setupTest(t, "validate-invalid/multiple_providers") if code != 1 { - t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String()) + t.Fatalf("Should have failed: %d\n\n%s", code, output.Stderr()) } wantError := "Error: Duplicate provider configuration" - if !strings.Contains(ui.ErrorWriter.String(), wantError) { - t.Fatalf("Missing error string %q\n\n'%s'", wantError, ui.ErrorWriter.String()) + if !strings.Contains(output.Stderr(), wantError) { + t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr()) } } func TestSameModuleMultipleTimesShouldFail(t *testing.T) { - ui, code := setupTest("validate-invalid/multiple_modules") + output, code := setupTest(t, "validate-invalid/multiple_modules") if code != 1 { - t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String()) + t.Fatalf("Should have failed: %d\n\n%s", code, output.Stderr()) } wantError := "Error: Duplicate module call" - if !strings.Contains(ui.ErrorWriter.String(), wantError) { - t.Fatalf("Missing error string %q\n\n'%s'", wantError, ui.ErrorWriter.String()) + if !strings.Contains(output.Stderr(), wantError) { + t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr()) } } func TestSameResourceMultipleTimesShouldFail(t *testing.T) { - ui, code := setupTest("validate-invalid/multiple_resources") + output, code := setupTest(t, "validate-invalid/multiple_resources") if code != 1 { - t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String()) + t.Fatalf("Should have failed: %d\n\n%s", code, output.Stderr()) } wantError := `Error: Duplicate resource "aws_instance" configuration` - if !strings.Contains(ui.ErrorWriter.String(), wantError) { - t.Fatalf("Missing error string %q\n\n'%s'", wantError, ui.ErrorWriter.String()) + if !strings.Contains(output.Stderr(), wantError) { + t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr()) } } func TestOutputWithoutValueShouldFail(t *testing.T) { - ui, code := setupTest("validate-invalid/outputs") + output, code := setupTest(t, "validate-invalid/outputs") if code != 1 { - t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String()) + t.Fatalf("Should have failed: %d\n\n%s", code, output.Stderr()) } wantError := `The argument "value" is required, but no definition was found.` - if !strings.Contains(ui.ErrorWriter.String(), wantError) { - t.Fatalf("Missing error string %q\n\n'%s'", wantError, ui.ErrorWriter.String()) + if !strings.Contains(output.Stderr(), wantError) { + t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr()) } wantError = `An argument named "values" is not expected here. Did you mean "value"?` - if !strings.Contains(ui.ErrorWriter.String(), wantError) { - t.Fatalf("Missing error string %q\n\n'%s'", wantError, ui.ErrorWriter.String()) + if !strings.Contains(output.Stderr(), wantError) { + t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr()) } } func TestModuleWithIncorrectNameShouldFail(t *testing.T) { - ui, code := setupTest("validate-invalid/incorrectmodulename") + output, code := setupTest(t, "validate-invalid/incorrectmodulename") if code != 1 { - t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String()) + t.Fatalf("Should have failed: %d\n\n%s", code, output.Stderr()) } wantError := `Error: Invalid module instance name` - if !strings.Contains(ui.ErrorWriter.String(), wantError) { - t.Fatalf("Missing error string %q\n\n'%s'", wantError, ui.ErrorWriter.String()) + if !strings.Contains(output.Stderr(), wantError) { + t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr()) } wantError = `Error: Variables not allowed` - if !strings.Contains(ui.ErrorWriter.String(), wantError) { - t.Fatalf("Missing error string %q\n\n'%s'", wantError, ui.ErrorWriter.String()) + if !strings.Contains(output.Stderr(), wantError) { + t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr()) } } func TestWronglyUsedInterpolationShouldFail(t *testing.T) { - ui, code := setupTest("validate-invalid/interpolation") + output, code := setupTest(t, "validate-invalid/interpolation") if code != 1 { - t.Fatalf("Should have failed: %d\n\n%s", code, ui.ErrorWriter.String()) + t.Fatalf("Should have failed: %d\n\n%s", code, output.Stderr()) } wantError := `Error: Variables not allowed` - if !strings.Contains(ui.ErrorWriter.String(), wantError) { - t.Fatalf("Missing error string %q\n\n'%s'", wantError, ui.ErrorWriter.String()) + if !strings.Contains(output.Stderr(), wantError) { + t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr()) } wantError = `A single static variable reference is required` - if !strings.Contains(ui.ErrorWriter.String(), wantError) { - t.Fatalf("Missing error string %q\n\n'%s'", wantError, ui.ErrorWriter.String()) + if !strings.Contains(output.Stderr(), wantError) { + t.Fatalf("Missing error string %q\n\n'%s'", wantError, output.Stderr()) } } func TestMissingDefinedVar(t *testing.T) { - ui, code := setupTest("validate-invalid/missing_defined_var") + output, code := setupTest(t, "validate-invalid/missing_defined_var") // This is allowed because validate tests only that variables are referenced // correctly, not that they all have defined values. if code != 0 { - t.Fatalf("Should have passed: %d\n\n%s", code, ui.ErrorWriter.String()) + t.Fatalf("Should have passed: %d\n\n%s", code, output.Stderr()) + } +} + +func TestValidate_json(t *testing.T) { + tests := []struct { + path string + valid bool + }{ + {"validate-valid", true}, + {"validate-invalid", false}, + {"validate-invalid/missing_quote", false}, + {"validate-invalid/missing_var", false}, + {"validate-invalid/multiple_providers", false}, + {"validate-invalid/multiple_modules", false}, + {"validate-invalid/multiple_resources", false}, + {"validate-invalid/outputs", false}, + {"validate-invalid/incorrectmodulename", false}, + {"validate-invalid/interpolation", false}, + {"validate-invalid/missing_defined_var", true}, + } + + for _, tc := range tests { + t.Run(tc.path, func(t *testing.T) { + var want, got map[string]interface{} + + wantFile, err := os.Open(path.Join(testFixturePath(tc.path), "output.json")) + if err != nil { + t.Fatalf("failed to open output file: %s", err) + } + defer wantFile.Close() + wantBytes, err := ioutil.ReadAll(wantFile) + if err != nil { + t.Fatalf("failed to read output file: %s", err) + } + err = json.Unmarshal([]byte(wantBytes), &want) + if err != nil { + t.Fatalf("failed to unmarshal expected JSON: %s", err) + } + + output, code := setupTest(t, tc.path, "-json") + + gotString := output.Stdout() + err = json.Unmarshal([]byte(gotString), &got) + if err != nil { + t.Fatalf("failed to unmarshal actual JSON: %s", err) + } + + if !cmp.Equal(got, want) { + t.Errorf("wrong output:\n %v\n", cmp.Diff(got, want)) + t.Errorf("raw output:\n%s\n", gotString) + } + + if tc.valid && code != 0 { + t.Errorf("wrong exit code: want 0, got %d", code) + } else if !tc.valid && code != 1 { + t.Errorf("wrong exit code: want 1, got %d", code) + } + + if errorOutput := output.Stderr(); errorOutput != "" { + t.Errorf("unexpected error output:\n%s", errorOutput) + } + }) } } diff --git a/vendor/github.com/hashicorp/terraform/command/version.go b/vendor/github.com/hashicorp/terraform/command/version.go index b20cc4c6..414a42a3 100644 --- a/vendor/github.com/hashicorp/terraform/command/version.go +++ b/vendor/github.com/hashicorp/terraform/command/version.go @@ -9,21 +9,22 @@ import ( "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/internal/depsfile" + "github.com/hashicorp/terraform/internal/getproviders" ) // VersionCommand is a Command implementation prints the version. type VersionCommand struct { Meta - Revision string Version string VersionPrerelease string CheckFunc VersionCheckFunc + Platform getproviders.Platform } type VersionOutput struct { Version string `json:"terraform_version"` - Revision string `json:"terraform_revision"` + Platform string `json:"platform"` ProviderSelections map[string]string `json:"provider_selections"` Outdated bool `json:"terraform_outdated"` } @@ -43,7 +44,7 @@ type VersionCheckInfo struct { func (c *VersionCommand) Help() string { helpText := ` -Usage: terraform version [options] +Usage: terraform [global options] version [options] Displays the version of Terraform and all installed plugins @@ -77,10 +78,6 @@ func (c *VersionCommand) Run(args []string) int { fmt.Fprintf(&versionString, "Terraform v%s", c.Version) if c.VersionPrerelease != "" { fmt.Fprintf(&versionString, "-%s", c.VersionPrerelease) - - if c.Revision != "" { - fmt.Fprintf(&versionString, " (%s)", c.Revision) - } } // We'll also attempt to print out the selected plugin versions. We do @@ -136,7 +133,7 @@ func (c *VersionCommand) Run(args []string) int { output := VersionOutput{ Version: versionOutput, - Revision: c.Revision, + Platform: c.Platform.String(), ProviderSelections: selectionsOutput, Outdated: outdated, } @@ -150,6 +147,8 @@ func (c *VersionCommand) Run(args []string) int { return 0 } else { c.Ui.Output(versionString.String()) + c.Ui.Output(fmt.Sprintf("on %s", c.Platform)) + if len(providerVersions) != 0 { sort.Strings(providerVersions) for _, str := range providerVersions { @@ -169,5 +168,5 @@ func (c *VersionCommand) Run(args []string) int { } func (c *VersionCommand) Synopsis() string { - return "Prints the Terraform version" + return "Show the current Terraform version" } diff --git a/vendor/github.com/hashicorp/terraform/command/version_test.go b/vendor/github.com/hashicorp/terraform/command/version_test.go index 3844dbd1..10f384c4 100644 --- a/vendor/github.com/hashicorp/terraform/command/version_test.go +++ b/vendor/github.com/hashicorp/terraform/command/version_test.go @@ -49,6 +49,7 @@ func TestVersion(t *testing.T) { }, Version: "4.5.6", VersionPrerelease: "foo", + Platform: getproviders.Platform{OS: "aros", Arch: "riscv64"}, } if err := c.replaceLockedDependencies(locks); err != nil { t.Fatal(err) @@ -58,7 +59,7 @@ func TestVersion(t *testing.T) { } actual := strings.TrimSpace(ui.OutputWriter.String()) - expected := "Terraform v4.5.6-foo\n+ provider registry.terraform.io/hashicorp/test1 v7.8.9-beta.2\n+ provider registry.terraform.io/hashicorp/test2 v1.2.3" + expected := "Terraform v4.5.6-foo\non aros_riscv64\n+ provider registry.terraform.io/hashicorp/test1 v7.8.9-beta.2\n+ provider registry.terraform.io/hashicorp/test2 v1.2.3" if actual != expected { t.Fatalf("wrong output\ngot:\n%s\nwant:\n%s", actual, expected) } @@ -76,6 +77,7 @@ func TestVersion_flags(t *testing.T) { Meta: m, Version: "4.5.6", VersionPrerelease: "foo", + Platform: getproviders.Platform{OS: "aros", Arch: "riscv64"}, } if code := c.Run([]string{"-v", "-version"}); code != 0 { @@ -83,7 +85,7 @@ func TestVersion_flags(t *testing.T) { } actual := strings.TrimSpace(ui.OutputWriter.String()) - expected := "Terraform v4.5.6-foo" + expected := "Terraform v4.5.6-foo\non aros_riscv64" if actual != expected { t.Fatalf("wrong output\ngot: %#v\nwant: %#v", actual, expected) } @@ -99,6 +101,7 @@ func TestVersion_outdated(t *testing.T) { Meta: m, Version: "4.5.6", CheckFunc: mockVersionCheckFunc(true, "4.5.7"), + Platform: getproviders.Platform{OS: "aros", Arch: "riscv64"}, } if code := c.Run([]string{}); code != 0 { @@ -106,7 +109,7 @@ func TestVersion_outdated(t *testing.T) { } actual := strings.TrimSpace(ui.OutputWriter.String()) - expected := "Terraform v4.5.6\n\nYour version of Terraform is out of date! The latest version\nis 4.5.7. You can update by downloading from https://www.terraform.io/downloads.html" + expected := "Terraform v4.5.6\non aros_riscv64\n\nYour version of Terraform is out of date! The latest version\nis 4.5.7. You can update by downloading from https://www.terraform.io/downloads.html" if actual != expected { t.Fatalf("wrong output\ngot: %#v\nwant: %#v", actual, expected) } @@ -127,8 +130,9 @@ func TestVersion_json(t *testing.T) { // `terraform version -json` without prerelease c := &VersionCommand{ - Meta: meta, - Version: "4.5.6", + Meta: meta, + Version: "4.5.6", + Platform: getproviders.Platform{OS: "aros", Arch: "riscv64"}, } if code := c.Run([]string{"-json"}); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) @@ -138,7 +142,7 @@ func TestVersion_json(t *testing.T) { expected := strings.TrimSpace(` { "terraform_version": "4.5.6", - "terraform_revision": "", + "platform": "aros_riscv64", "provider_selections": {}, "terraform_outdated": false } @@ -172,6 +176,7 @@ func TestVersion_json(t *testing.T) { Meta: meta, Version: "4.5.6", VersionPrerelease: "foo", + Platform: getproviders.Platform{OS: "aros", Arch: "riscv64"}, } if err := c.replaceLockedDependencies(locks); err != nil { t.Fatal(err) @@ -184,7 +189,7 @@ func TestVersion_json(t *testing.T) { expected = strings.TrimSpace(` { "terraform_version": "4.5.6-foo", - "terraform_revision": "", + "platform": "aros_riscv64", "provider_selections": { "registry.terraform.io/hashicorp/test1": "7.8.9-beta.2", "registry.terraform.io/hashicorp/test2": "1.2.3" @@ -208,6 +213,7 @@ func TestVersion_jsonoutdated(t *testing.T) { Meta: m, Version: "4.5.6", CheckFunc: mockVersionCheckFunc(true, "4.5.7"), + Platform: getproviders.Platform{OS: "aros", Arch: "riscv64"}, } if code := c.Run([]string{"-json"}); code != 0 { @@ -215,7 +221,7 @@ func TestVersion_jsonoutdated(t *testing.T) { } actual := strings.TrimSpace(ui.OutputWriter.String()) - expected := "{\n \"terraform_version\": \"4.5.6\",\n \"terraform_revision\": \"\",\n \"provider_selections\": {},\n \"terraform_outdated\": true\n}" + expected := "{\n \"terraform_version\": \"4.5.6\",\n \"platform\": \"aros_riscv64\",\n \"provider_selections\": {},\n \"terraform_outdated\": true\n}" if actual != expected { t.Fatalf("wrong output\ngot: %#v\nwant: %#v", actual, expected) } diff --git a/vendor/github.com/hashicorp/terraform/command/views/apply.go b/vendor/github.com/hashicorp/terraform/command/views/apply.go new file mode 100644 index 00000000..988924f9 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/apply.go @@ -0,0 +1,162 @@ +package views + +import ( + "fmt" + + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/format" + "github.com/hashicorp/terraform/command/views/json" + "github.com/hashicorp/terraform/states" + "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/tfdiags" +) + +// The Apply view is used for the apply command. +type Apply interface { + ResourceCount(stateOutPath string) + Outputs(outputValues map[string]*states.OutputValue) + + Operation() Operation + Hooks() []terraform.Hook + + Diagnostics(diags tfdiags.Diagnostics) + HelpPrompt() +} + +// NewApply returns an initialized Apply implementation for the given ViewType. +func NewApply(vt arguments.ViewType, destroy bool, view *View) Apply { + switch vt { + case arguments.ViewJSON: + return &ApplyJSON{ + view: NewJSONView(view), + destroy: destroy, + countHook: &countHook{}, + } + case arguments.ViewHuman: + return &ApplyHuman{ + view: view, + destroy: destroy, + inAutomation: view.RunningInAutomation(), + countHook: &countHook{}, + } + default: + panic(fmt.Sprintf("unknown view type %v", vt)) + } +} + +// The ApplyHuman implementation renders human-readable text logs, suitable for +// a scrolling terminal. +type ApplyHuman struct { + view *View + + destroy bool + inAutomation bool + + countHook *countHook +} + +var _ Apply = (*ApplyHuman)(nil) + +func (v *ApplyHuman) ResourceCount(stateOutPath string) { + if v.destroy { + v.view.streams.Printf( + v.view.colorize.Color("[reset][bold][green]\nDestroy complete! Resources: %d destroyed.\n"), + v.countHook.Removed, + ) + } else { + v.view.streams.Printf( + v.view.colorize.Color("[reset][bold][green]\nApply complete! Resources: %d added, %d changed, %d destroyed.\n"), + v.countHook.Added, + v.countHook.Changed, + v.countHook.Removed, + ) + } + if (v.countHook.Added > 0 || v.countHook.Changed > 0) && stateOutPath != "" { + v.view.streams.Printf("\n%s\n\n", format.WordWrap(stateOutPathPostApply, v.view.outputColumns())) + v.view.streams.Printf("State path: %s\n", stateOutPath) + } +} + +func (v *ApplyHuman) Outputs(outputValues map[string]*states.OutputValue) { + if len(outputValues) > 0 { + v.view.streams.Print(v.view.colorize.Color("[reset][bold][green]\nOutputs:\n\n")) + NewOutput(arguments.ViewHuman, v.view).Output("", outputValues) + } +} + +func (v *ApplyHuman) Operation() Operation { + return NewOperation(arguments.ViewHuman, v.inAutomation, v.view) +} + +func (v *ApplyHuman) Hooks() []terraform.Hook { + return []terraform.Hook{ + v.countHook, + NewUiHook(v.view), + } +} + +func (v *ApplyHuman) Diagnostics(diags tfdiags.Diagnostics) { + v.view.Diagnostics(diags) +} + +func (v *ApplyHuman) HelpPrompt() { + command := "apply" + if v.destroy { + command = "destroy" + } + v.view.HelpPrompt(command) +} + +const stateOutPathPostApply = "The state of your infrastructure has been saved to the path below. This state is required to modify and destroy your infrastructure, so keep it safe. To inspect the complete state use the `terraform show` command." + +// The ApplyJSON implementation renders streaming JSON logs, suitable for +// integrating with other software. +type ApplyJSON struct { + view *JSONView + + destroy bool + + countHook *countHook +} + +var _ Apply = (*ApplyJSON)(nil) + +func (v *ApplyJSON) ResourceCount(stateOutPath string) { + operation := json.OperationApplied + if v.destroy { + operation = json.OperationDestroyed + } + v.view.ChangeSummary(&json.ChangeSummary{ + Add: v.countHook.Added, + Change: v.countHook.Changed, + Remove: v.countHook.Removed, + Operation: operation, + }) +} + +func (v *ApplyJSON) Outputs(outputValues map[string]*states.OutputValue) { + outputs, diags := json.OutputsFromMap(outputValues) + if diags.HasErrors() { + v.Diagnostics(diags) + } else { + v.view.Outputs(outputs) + } +} + +func (v *ApplyJSON) Operation() Operation { + return &OperationJSON{view: v.view} +} + +func (v *ApplyJSON) Hooks() []terraform.Hook { + return []terraform.Hook{ + v.countHook, + newJSONHook(v.view), + } +} + +func (v *ApplyJSON) Diagnostics(diags tfdiags.Diagnostics) { + v.view.Diagnostics(diags) +} + +func (v *ApplyJSON) HelpPrompt() { +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/apply_test.go b/vendor/github.com/hashicorp/terraform/command/views/apply_test.go new file mode 100644 index 00000000..fac161ac --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/apply_test.go @@ -0,0 +1,255 @@ +package views + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/internal/terminal" + "github.com/hashicorp/terraform/states" + "github.com/zclconf/go-cty/cty" +) + +// This test is mostly because I am paranoid about having two consecutive +// boolean arguments. +func TestApply_new(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + defer done(t) + v := NewApply(arguments.ViewHuman, false, NewView(streams).SetRunningInAutomation(true)) + hv, ok := v.(*ApplyHuman) + if !ok { + t.Fatalf("unexpected return type %t", v) + } + + if hv.destroy != false { + t.Fatalf("unexpected destroy value") + } + + if hv.inAutomation != true { + t.Fatalf("unexpected inAutomation value") + } +} + +// Basic test coverage of Outputs, since most of its functionality is tested +// elsewhere. +func TestApplyHuman_outputs(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewApply(arguments.ViewHuman, false, NewView(streams)) + + v.Outputs(map[string]*states.OutputValue{ + "foo": {Value: cty.StringVal("secret")}, + }) + + got := done(t).Stdout() + for _, want := range []string{"Outputs:", `foo = "secret"`} { + if !strings.Contains(got, want) { + t.Errorf("wrong result\ngot: %q\nwant: %q", got, want) + } + } +} + +// Outputs should do nothing if there are no outputs to render. +func TestApplyHuman_outputsEmpty(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewApply(arguments.ViewHuman, false, NewView(streams)) + + v.Outputs(map[string]*states.OutputValue{}) + + got := done(t).Stdout() + if got != "" { + t.Errorf("output should be empty, but got: %q", got) + } +} + +// Ensure that the correct view type and in-automation settings propagate to the +// Operation view. +func TestApplyHuman_operation(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + defer done(t) + v := NewApply(arguments.ViewHuman, false, NewView(streams).SetRunningInAutomation(true)).Operation() + if hv, ok := v.(*OperationHuman); !ok { + t.Fatalf("unexpected return type %t", v) + } else if hv.inAutomation != true { + t.Fatalf("unexpected inAutomation value on Operation view") + } +} + +// This view is used for both apply and destroy commands, so the help output +// needs to cover both. +func TestApplyHuman_help(t *testing.T) { + testCases := map[string]bool{ + "apply": false, + "destroy": true, + } + + for name, destroy := range testCases { + t.Run(name, func(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewApply(arguments.ViewHuman, destroy, NewView(streams)) + v.HelpPrompt() + got := done(t).Stderr() + if !strings.Contains(got, name) { + t.Errorf("wrong result\ngot: %q\nwant: %q", got, name) + } + }) + } +} + +// Hooks and ResourceCount are tangled up and easiest to test together. +func TestApply_resourceCount(t *testing.T) { + testCases := map[string]struct { + destroy bool + want string + }{ + "apply": { + false, + "Apply complete! Resources: 1 added, 2 changed, 3 destroyed.", + }, + "destroy": { + true, + "Destroy complete! Resources: 3 destroyed.", + }, + } + + // For compatibility reasons, these tests should hold true for both human + // and JSON output modes + views := []arguments.ViewType{arguments.ViewHuman, arguments.ViewJSON} + + for name, tc := range testCases { + for _, viewType := range views { + t.Run(fmt.Sprintf("%s (%s view)", name, viewType), func(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewApply(viewType, tc.destroy, NewView(streams)) + hooks := v.Hooks() + + var count *countHook + for _, hook := range hooks { + if ch, ok := hook.(*countHook); ok { + count = ch + } + } + if count == nil { + t.Fatalf("expected Hooks to include a countHook: %#v", hooks) + } + + count.Added = 1 + count.Changed = 2 + count.Removed = 3 + + v.ResourceCount("") + + got := done(t).Stdout() + if !strings.Contains(got, tc.want) { + t.Errorf("wrong result\ngot: %q\nwant: %q", got, tc.want) + } + }) + } + } +} + +func TestApplyHuman_resourceCountStatePath(t *testing.T) { + testCases := map[string]struct { + added int + changed int + removed int + statePath string + wantContains bool + }{ + "default state path": { + added: 1, + changed: 2, + removed: 3, + statePath: "", + wantContains: false, + }, + "only removed": { + added: 0, + changed: 0, + removed: 5, + statePath: "foo.tfstate", + wantContains: false, + }, + "added": { + added: 5, + changed: 0, + removed: 0, + statePath: "foo.tfstate", + wantContains: true, + }, + "changed": { + added: 0, + changed: 5, + removed: 0, + statePath: "foo.tfstate", + wantContains: true, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewApply(arguments.ViewHuman, false, NewView(streams)) + hooks := v.Hooks() + + var count *countHook + for _, hook := range hooks { + if ch, ok := hook.(*countHook); ok { + count = ch + } + } + if count == nil { + t.Fatalf("expected Hooks to include a countHook: %#v", hooks) + } + + count.Added = tc.added + count.Changed = tc.changed + count.Removed = tc.removed + + v.ResourceCount(tc.statePath) + + got := done(t).Stdout() + want := "State path: " + tc.statePath + contains := strings.Contains(got, want) + if contains && !tc.wantContains { + t.Errorf("wrong result\ngot: %q\nshould not contain: %q", got, want) + } else if !contains && tc.wantContains { + t.Errorf("wrong result\ngot: %q\nshould contain: %q", got, want) + } + }) + } +} + +// Basic test coverage of Outputs, since most of its functionality is tested +// elsewhere. +func TestApplyJSON_outputs(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewApply(arguments.ViewJSON, false, NewView(streams)) + + v.Outputs(map[string]*states.OutputValue{ + "boop_count": {Value: cty.NumberIntVal(92)}, + "password": {Value: cty.StringVal("horse-battery").Mark("sensitive"), Sensitive: true}, + }) + + want := []map[string]interface{}{ + { + "@level": "info", + "@message": "Outputs: 2", + "@module": "terraform.ui", + "type": "outputs", + "outputs": map[string]interface{}{ + "boop_count": map[string]interface{}{ + "sensitive": false, + "value": float64(92), + "type": "number", + }, + "password": map[string]interface{}{ + "sensitive": true, + "value": "horse-battery", + "type": "string", + }, + }, + }, + } + testJSONViewOutputEquals(t, done(t).Stdout(), want) +} diff --git a/vendor/github.com/hashicorp/terraform/backend/local/hook_count.go b/vendor/github.com/hashicorp/terraform/command/views/hook_count.go similarity index 84% rename from vendor/github.com/hashicorp/terraform/backend/local/hook_count.go rename to vendor/github.com/hashicorp/terraform/command/views/hook_count.go index f9d619ef..25e1a5e7 100644 --- a/vendor/github.com/hashicorp/terraform/backend/local/hook_count.go +++ b/vendor/github.com/hashicorp/terraform/command/views/hook_count.go @@ -1,4 +1,4 @@ -package local +package views import ( "sync" @@ -11,9 +11,9 @@ import ( "github.com/hashicorp/terraform/terraform" ) -// CountHook is a hook that counts the number of resources +// countHook is a hook that counts the number of resources // added, removed, changed during the course of an apply. -type CountHook struct { +type countHook struct { Added int Changed int Removed int @@ -29,9 +29,9 @@ type CountHook struct { terraform.NilHook } -var _ terraform.Hook = (*CountHook)(nil) +var _ terraform.Hook = (*countHook)(nil) -func (h *CountHook) Reset() { +func (h *countHook) Reset() { h.Lock() defer h.Unlock() @@ -41,7 +41,7 @@ func (h *CountHook) Reset() { h.Removed = 0 } -func (h *CountHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (terraform.HookAction, error) { +func (h *countHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (terraform.HookAction, error) { h.Lock() defer h.Unlock() @@ -54,7 +54,7 @@ func (h *CountHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generati return terraform.HookActionContinue, nil } -func (h *CountHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generation, newState cty.Value, err error) (terraform.HookAction, error) { +func (h *countHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generation, newState cty.Value, err error) (terraform.HookAction, error) { h.Lock() defer h.Unlock() @@ -82,7 +82,7 @@ func (h *CountHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generat return terraform.HookActionContinue, nil } -func (h *CountHook) PostDiff(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (terraform.HookAction, error) { +func (h *countHook) PostDiff(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (terraform.HookAction, error) { h.Lock() defer h.Unlock() diff --git a/vendor/github.com/hashicorp/terraform/command/views/hook_count_test.go b/vendor/github.com/hashicorp/terraform/command/views/hook_count_test.go new file mode 100644 index 00000000..83e75e09 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/hook_count_test.go @@ -0,0 +1,337 @@ +package views + +import ( + "reflect" + "testing" + + "github.com/zclconf/go-cty/cty" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/states" + "github.com/hashicorp/terraform/terraform" + + legacy "github.com/hashicorp/terraform/internal/legacy/terraform" +) + +func TestCountHook_impl(t *testing.T) { + var _ terraform.Hook = new(countHook) +} + +func TestCountHookPostDiff_DestroyDeposed(t *testing.T) { + h := new(countHook) + + resources := map[string]*legacy.InstanceDiff{ + "lorem": &legacy.InstanceDiff{DestroyDeposed: true}, + } + + for k := range resources { + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: k, + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + h.PostDiff(addr, states.DeposedKey("deadbeef"), plans.Delete, cty.DynamicVal, cty.DynamicVal) + } + + expected := new(countHook) + expected.ToAdd = 0 + expected.ToChange = 0 + expected.ToRemoveAndAdd = 0 + expected.ToRemove = 1 + + if !reflect.DeepEqual(expected, h) { + t.Fatalf("Expected %#v, got %#v instead.", expected, h) + } +} + +func TestCountHookPostDiff_DestroyOnly(t *testing.T) { + h := new(countHook) + + resources := map[string]*legacy.InstanceDiff{ + "foo": &legacy.InstanceDiff{Destroy: true}, + "bar": &legacy.InstanceDiff{Destroy: true}, + "lorem": &legacy.InstanceDiff{Destroy: true}, + "ipsum": &legacy.InstanceDiff{Destroy: true}, + } + + for k := range resources { + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: k, + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + h.PostDiff(addr, states.CurrentGen, plans.Delete, cty.DynamicVal, cty.DynamicVal) + } + + expected := new(countHook) + expected.ToAdd = 0 + expected.ToChange = 0 + expected.ToRemoveAndAdd = 0 + expected.ToRemove = 4 + + if !reflect.DeepEqual(expected, h) { + t.Fatalf("Expected %#v, got %#v instead.", expected, h) + } +} + +func TestCountHookPostDiff_AddOnly(t *testing.T) { + h := new(countHook) + + resources := map[string]*legacy.InstanceDiff{ + "foo": &legacy.InstanceDiff{ + Attributes: map[string]*legacy.ResourceAttrDiff{ + "foo": &legacy.ResourceAttrDiff{RequiresNew: true}, + }, + }, + "bar": &legacy.InstanceDiff{ + Attributes: map[string]*legacy.ResourceAttrDiff{ + "foo": &legacy.ResourceAttrDiff{RequiresNew: true}, + }, + }, + "lorem": &legacy.InstanceDiff{ + Attributes: map[string]*legacy.ResourceAttrDiff{ + "foo": &legacy.ResourceAttrDiff{RequiresNew: true}, + }, + }, + } + + for k := range resources { + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: k, + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + h.PostDiff(addr, states.CurrentGen, plans.Create, cty.DynamicVal, cty.DynamicVal) + } + + expected := new(countHook) + expected.ToAdd = 3 + expected.ToChange = 0 + expected.ToRemoveAndAdd = 0 + expected.ToRemove = 0 + + if !reflect.DeepEqual(expected, h) { + t.Fatalf("Expected %#v, got %#v instead.", expected, h) + } +} + +func TestCountHookPostDiff_ChangeOnly(t *testing.T) { + h := new(countHook) + + resources := map[string]*legacy.InstanceDiff{ + "foo": &legacy.InstanceDiff{ + Destroy: false, + Attributes: map[string]*legacy.ResourceAttrDiff{ + "foo": &legacy.ResourceAttrDiff{}, + }, + }, + "bar": &legacy.InstanceDiff{ + Destroy: false, + Attributes: map[string]*legacy.ResourceAttrDiff{ + "foo": &legacy.ResourceAttrDiff{}, + }, + }, + "lorem": &legacy.InstanceDiff{ + Destroy: false, + Attributes: map[string]*legacy.ResourceAttrDiff{ + "foo": &legacy.ResourceAttrDiff{}, + }, + }, + } + + for k := range resources { + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: k, + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + h.PostDiff(addr, states.CurrentGen, plans.Update, cty.DynamicVal, cty.DynamicVal) + } + + expected := new(countHook) + expected.ToAdd = 0 + expected.ToChange = 3 + expected.ToRemoveAndAdd = 0 + expected.ToRemove = 0 + + if !reflect.DeepEqual(expected, h) { + t.Fatalf("Expected %#v, got %#v instead.", expected, h) + } +} + +func TestCountHookPostDiff_Mixed(t *testing.T) { + h := new(countHook) + + resources := map[string]plans.Action{ + "foo": plans.Delete, + "bar": plans.NoOp, + "lorem": plans.Update, + "ipsum": plans.Delete, + } + + for k, a := range resources { + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: k, + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + h.PostDiff(addr, states.CurrentGen, a, cty.DynamicVal, cty.DynamicVal) + } + + expected := new(countHook) + expected.ToAdd = 0 + expected.ToChange = 1 + expected.ToRemoveAndAdd = 0 + expected.ToRemove = 2 + + if !reflect.DeepEqual(expected, h) { + t.Fatalf("Expected %#v, got %#v instead.", + expected, h) + } +} + +func TestCountHookPostDiff_NoChange(t *testing.T) { + h := new(countHook) + + resources := map[string]*legacy.InstanceDiff{ + "foo": &legacy.InstanceDiff{}, + "bar": &legacy.InstanceDiff{}, + "lorem": &legacy.InstanceDiff{}, + "ipsum": &legacy.InstanceDiff{}, + } + + for k := range resources { + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: k, + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + h.PostDiff(addr, states.CurrentGen, plans.NoOp, cty.DynamicVal, cty.DynamicVal) + } + + expected := new(countHook) + expected.ToAdd = 0 + expected.ToChange = 0 + expected.ToRemoveAndAdd = 0 + expected.ToRemove = 0 + + if !reflect.DeepEqual(expected, h) { + t.Fatalf("Expected %#v, got %#v instead.", + expected, h) + } +} + +func TestCountHookPostDiff_DataSource(t *testing.T) { + h := new(countHook) + + resources := map[string]plans.Action{ + "foo": plans.Delete, + "bar": plans.NoOp, + "lorem": plans.Update, + "ipsum": plans.Delete, + } + + for k, a := range resources { + addr := addrs.Resource{ + Mode: addrs.DataResourceMode, + Type: "test_instance", + Name: k, + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + h.PostDiff(addr, states.CurrentGen, a, cty.DynamicVal, cty.DynamicVal) + } + + expected := new(countHook) + expected.ToAdd = 0 + expected.ToChange = 0 + expected.ToRemoveAndAdd = 0 + expected.ToRemove = 0 + + if !reflect.DeepEqual(expected, h) { + t.Fatalf("Expected %#v, got %#v instead.", + expected, h) + } +} + +func TestCountHookApply_ChangeOnly(t *testing.T) { + h := new(countHook) + + resources := map[string]*legacy.InstanceDiff{ + "foo": &legacy.InstanceDiff{ + Destroy: false, + Attributes: map[string]*legacy.ResourceAttrDiff{ + "foo": &legacy.ResourceAttrDiff{}, + }, + }, + "bar": &legacy.InstanceDiff{ + Destroy: false, + Attributes: map[string]*legacy.ResourceAttrDiff{ + "foo": &legacy.ResourceAttrDiff{}, + }, + }, + "lorem": &legacy.InstanceDiff{ + Destroy: false, + Attributes: map[string]*legacy.ResourceAttrDiff{ + "foo": &legacy.ResourceAttrDiff{}, + }, + }, + } + + for k := range resources { + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: k, + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + h.PreApply(addr, states.CurrentGen, plans.Update, cty.DynamicVal, cty.DynamicVal) + h.PostApply(addr, states.CurrentGen, cty.DynamicVal, nil) + } + + expected := &countHook{pending: make(map[string]plans.Action)} + expected.Added = 0 + expected.Changed = 3 + expected.Removed = 0 + + if !reflect.DeepEqual(expected, h) { + t.Fatalf("Expected:\n%#v\nGot:\n%#v\n", expected, h) + } +} + +func TestCountHookApply_DestroyOnly(t *testing.T) { + h := new(countHook) + + resources := map[string]*legacy.InstanceDiff{ + "foo": &legacy.InstanceDiff{Destroy: true}, + "bar": &legacy.InstanceDiff{Destroy: true}, + "lorem": &legacy.InstanceDiff{Destroy: true}, + "ipsum": &legacy.InstanceDiff{Destroy: true}, + } + + for k := range resources { + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: k, + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + h.PreApply(addr, states.CurrentGen, plans.Delete, cty.DynamicVal, cty.DynamicVal) + h.PostApply(addr, states.CurrentGen, cty.DynamicVal, nil) + } + + expected := &countHook{pending: make(map[string]plans.Action)} + expected.Added = 0 + expected.Changed = 0 + expected.Removed = 4 + + if !reflect.DeepEqual(expected, h) { + t.Fatalf("Expected:\n%#v\nGot:\n%#v\n", expected, h) + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/hook_json.go b/vendor/github.com/hashicorp/terraform/command/views/hook_json.go new file mode 100644 index 00000000..6dd27a64 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/hook_json.go @@ -0,0 +1,156 @@ +package views + +import ( + "bufio" + "strings" + "sync" + "time" + "unicode" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/command/format" + "github.com/hashicorp/terraform/command/views/json" + "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/states" + "github.com/hashicorp/terraform/terraform" + "github.com/zclconf/go-cty/cty" +) + +// How long to wait between sending heartbeat/progress messages +const heartbeatInterval = 10 * time.Second + +func newJSONHook(view *JSONView) *jsonHook { + return &jsonHook{ + view: view, + applying: make(map[string]applyProgress), + timeNow: time.Now, + timeAfter: time.After, + } +} + +type jsonHook struct { + terraform.NilHook + + view *JSONView + + // Concurrent map of resource addresses to allow the sequence of pre-apply, + // progress, and post-apply messages to share data about the resource + applying map[string]applyProgress + applyingLock sync.Mutex + + // Mockable functions for testing the progress timer goroutine + timeNow func() time.Time + timeAfter func(time.Duration) <-chan time.Time +} + +var _ terraform.Hook = (*jsonHook)(nil) + +type applyProgress struct { + addr addrs.AbsResourceInstance + action plans.Action + start time.Time + + // done is used for post-apply to stop the progress goroutine + done chan struct{} + + // heartbeatDone is used to allow tests to safely wait for the progress + // goroutine to finish + heartbeatDone chan struct{} +} + +func (h *jsonHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (terraform.HookAction, error) { + idKey, idValue := format.ObjectValueIDOrName(priorState) + h.view.Hook(json.NewApplyStart(addr, action, idKey, idValue)) + + progress := applyProgress{ + addr: addr, + action: action, + start: h.timeNow().Round(time.Second), + done: make(chan struct{}), + heartbeatDone: make(chan struct{}), + } + h.applyingLock.Lock() + h.applying[addr.String()] = progress + h.applyingLock.Unlock() + + go h.applyingHeartbeat(progress) + return terraform.HookActionContinue, nil +} + +func (h *jsonHook) applyingHeartbeat(progress applyProgress) { + defer close(progress.heartbeatDone) + for { + select { + case <-progress.done: + return + case <-h.timeAfter(heartbeatInterval): + } + + elapsed := h.timeNow().Round(time.Second).Sub(progress.start) + h.view.Hook(json.NewApplyProgress(progress.addr, progress.action, elapsed)) + } +} + +func (h *jsonHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generation, newState cty.Value, err error) (terraform.HookAction, error) { + key := addr.String() + h.applyingLock.Lock() + progress := h.applying[key] + if progress.done != nil { + close(progress.done) + } + delete(h.applying, key) + h.applyingLock.Unlock() + + elapsed := h.timeNow().Round(time.Second).Sub(progress.start) + + if err != nil { + // Errors are collected and displayed post-apply, so no need to + // re-render them here. Instead just signal that this resource failed + // to apply. + h.view.Hook(json.NewApplyErrored(addr, progress.action, elapsed)) + } else { + idKey, idValue := format.ObjectValueID(newState) + h.view.Hook(json.NewApplyComplete(addr, progress.action, idKey, idValue, elapsed)) + } + return terraform.HookActionContinue, nil +} + +func (h *jsonHook) PreProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string) (terraform.HookAction, error) { + h.view.Hook(json.NewProvisionStart(addr, typeName)) + return terraform.HookActionContinue, nil +} + +func (h *jsonHook) PostProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string, err error) (terraform.HookAction, error) { + if err != nil { + // Errors are collected and displayed post-apply, so no need to + // re-render them here. Instead just signal that this provisioner step + // failed. + h.view.Hook(json.NewProvisionErrored(addr, typeName)) + } else { + h.view.Hook(json.NewProvisionComplete(addr, typeName)) + } + return terraform.HookActionContinue, nil +} + +func (h *jsonHook) ProvisionOutput(addr addrs.AbsResourceInstance, typeName string, msg string) { + s := bufio.NewScanner(strings.NewReader(msg)) + s.Split(scanLines) + for s.Scan() { + line := strings.TrimRightFunc(s.Text(), unicode.IsSpace) + if line != "" { + h.view.Hook(json.NewProvisionProgress(addr, typeName, line)) + } + } +} + +func (h *jsonHook) PreRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value) (terraform.HookAction, error) { + idKey, idValue := format.ObjectValueID(priorState) + h.view.Hook(json.NewRefreshStart(addr, idKey, idValue)) + return terraform.HookActionContinue, nil +} + +func (h *jsonHook) PostRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value, newState cty.Value) (terraform.HookAction, error) { + idKey, idValue := format.ObjectValueID(newState) + h.view.Hook(json.NewRefreshComplete(addr, idKey, idValue)) + return terraform.HookActionContinue, nil +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/hook_json_test.go b/vendor/github.com/hashicorp/terraform/command/views/hook_json_test.go new file mode 100644 index 00000000..ee20c450 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/hook_json_test.go @@ -0,0 +1,325 @@ +package views + +import ( + "fmt" + "testing" + "time" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/internal/terminal" + "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/states" + "github.com/hashicorp/terraform/terraform" + "github.com/zclconf/go-cty/cty" +) + +// Test a sequence of hooks associated with creating a resource +func TestJSONHook_create(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + hook := newJSONHook(NewJSONView(NewView(streams))) + + now := time.Now() + hook.timeNow = func() time.Time { return now } + after := make(chan time.Time, 1) + hook.timeAfter = func(time.Duration) <-chan time.Time { return after } + + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "boop", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + priorState := cty.NullVal(cty.Object(map[string]cty.Type{ + "id": cty.String, + "bar": cty.List(cty.String), + })) + plannedNewState := cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("test"), + "bar": cty.ListVal([]cty.Value{ + cty.StringVal("baz"), + }), + }) + + action, err := hook.PreApply(addr, states.CurrentGen, plans.Create, priorState, plannedNewState) + testHookReturnValues(t, action, err) + + action, err = hook.PreProvisionInstanceStep(addr, "local-exec") + testHookReturnValues(t, action, err) + + hook.ProvisionOutput(addr, "local-exec", `Executing: ["/bin/sh" "-c" "touch /etc/motd"]`) + + action, err = hook.PostProvisionInstanceStep(addr, "local-exec", nil) + testHookReturnValues(t, action, err) + + // Travel 10s into the future, notify the progress goroutine, and sleep + // briefly to allow it to execute + now = now.Add(10 * time.Second) + after <- now + time.Sleep(1 * time.Millisecond) + + // Travel 10s into the future, notify the progress goroutine, and sleep + // briefly to allow it to execute + now = now.Add(10 * time.Second) + after <- now + time.Sleep(1 * time.Millisecond) + + // Travel 2s into the future. We have arrived! + now = now.Add(2 * time.Second) + + action, err = hook.PostApply(addr, states.CurrentGen, plannedNewState, nil) + testHookReturnValues(t, action, err) + + // Shut down the progress goroutine if still active + hook.applyingLock.Lock() + for key, progress := range hook.applying { + close(progress.done) + <-progress.heartbeatDone + delete(hook.applying, key) + } + hook.applyingLock.Unlock() + + wantResource := map[string]interface{}{ + "addr": string("test_instance.boop"), + "implied_provider": string("test"), + "module": string(""), + "resource": string("test_instance.boop"), + "resource_key": nil, + "resource_name": string("boop"), + "resource_type": string("test_instance"), + } + want := []map[string]interface{}{ + { + "@level": "info", + "@message": "test_instance.boop: Creating...", + "@module": "terraform.ui", + "type": "apply_start", + "hook": map[string]interface{}{ + "action": string("create"), + "resource": wantResource, + }, + }, + { + "@level": "info", + "@message": "test_instance.boop: Provisioning with 'local-exec'...", + "@module": "terraform.ui", + "type": "provision_start", + "hook": map[string]interface{}{ + "provisioner": "local-exec", + "resource": wantResource, + }, + }, + { + "@level": "info", + "@message": `test_instance.boop: (local-exec): Executing: ["/bin/sh" "-c" "touch /etc/motd"]`, + "@module": "terraform.ui", + "type": "provision_progress", + "hook": map[string]interface{}{ + "output": `Executing: ["/bin/sh" "-c" "touch /etc/motd"]`, + "provisioner": "local-exec", + "resource": wantResource, + }, + }, + { + "@level": "info", + "@message": "test_instance.boop: (local-exec) Provisioning complete", + "@module": "terraform.ui", + "type": "provision_complete", + "hook": map[string]interface{}{ + "provisioner": "local-exec", + "resource": wantResource, + }, + }, + { + "@level": "info", + "@message": "test_instance.boop: Still creating... [10s elapsed]", + "@module": "terraform.ui", + "type": "apply_progress", + "hook": map[string]interface{}{ + "action": string("create"), + "elapsed_seconds": float64(10), + "resource": wantResource, + }, + }, + { + "@level": "info", + "@message": "test_instance.boop: Still creating... [20s elapsed]", + "@module": "terraform.ui", + "type": "apply_progress", + "hook": map[string]interface{}{ + "action": string("create"), + "elapsed_seconds": float64(20), + "resource": wantResource, + }, + }, + { + "@level": "info", + "@message": "test_instance.boop: Creation complete after 22s [id=test]", + "@module": "terraform.ui", + "type": "apply_complete", + "hook": map[string]interface{}{ + "action": string("create"), + "elapsed_seconds": float64(22), + "id_key": "id", + "id_value": "test", + "resource": wantResource, + }, + }, + } + + testJSONViewOutputEquals(t, done(t).Stdout(), want) +} + +func TestJSONHook_errors(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + hook := newJSONHook(NewJSONView(NewView(streams))) + + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "boop", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + priorState := cty.NullVal(cty.Object(map[string]cty.Type{ + "id": cty.String, + "bar": cty.List(cty.String), + })) + plannedNewState := cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("test"), + "bar": cty.ListVal([]cty.Value{ + cty.StringVal("baz"), + }), + }) + + action, err := hook.PreApply(addr, states.CurrentGen, plans.Delete, priorState, plannedNewState) + testHookReturnValues(t, action, err) + + provisionError := fmt.Errorf("provisioner didn't want to") + action, err = hook.PostProvisionInstanceStep(addr, "local-exec", provisionError) + testHookReturnValues(t, action, err) + + applyError := fmt.Errorf("provider was sad") + action, err = hook.PostApply(addr, states.CurrentGen, plannedNewState, applyError) + testHookReturnValues(t, action, err) + + // Shut down the progress goroutine + hook.applyingLock.Lock() + for key, progress := range hook.applying { + close(progress.done) + <-progress.heartbeatDone + delete(hook.applying, key) + } + hook.applyingLock.Unlock() + + wantResource := map[string]interface{}{ + "addr": string("test_instance.boop"), + "implied_provider": string("test"), + "module": string(""), + "resource": string("test_instance.boop"), + "resource_key": nil, + "resource_name": string("boop"), + "resource_type": string("test_instance"), + } + want := []map[string]interface{}{ + { + "@level": "info", + "@message": "test_instance.boop: Destroying...", + "@module": "terraform.ui", + "type": "apply_start", + "hook": map[string]interface{}{ + "action": string("delete"), + "resource": wantResource, + }, + }, + { + "@level": "info", + "@message": "test_instance.boop: (local-exec) Provisioning errored", + "@module": "terraform.ui", + "type": "provision_errored", + "hook": map[string]interface{}{ + "provisioner": "local-exec", + "resource": wantResource, + }, + }, + { + "@level": "info", + "@message": "test_instance.boop: Destruction errored after 0s", + "@module": "terraform.ui", + "type": "apply_errored", + "hook": map[string]interface{}{ + "action": string("delete"), + "elapsed_seconds": float64(0), + "resource": wantResource, + }, + }, + } + + testJSONViewOutputEquals(t, done(t).Stdout(), want) +} + +func TestJSONHook_refresh(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + hook := newJSONHook(NewJSONView(NewView(streams))) + + addr := addrs.Resource{ + Mode: addrs.DataResourceMode, + Type: "test_data_source", + Name: "beep", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + state := cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("honk"), + "bar": cty.ListVal([]cty.Value{ + cty.StringVal("baz"), + }), + }) + + action, err := hook.PreRefresh(addr, states.CurrentGen, state) + testHookReturnValues(t, action, err) + + action, err = hook.PostRefresh(addr, states.CurrentGen, state, state) + testHookReturnValues(t, action, err) + + wantResource := map[string]interface{}{ + "addr": string("data.test_data_source.beep"), + "implied_provider": string("test"), + "module": string(""), + "resource": string("data.test_data_source.beep"), + "resource_key": nil, + "resource_name": string("beep"), + "resource_type": string("test_data_source"), + } + want := []map[string]interface{}{ + { + "@level": "info", + "@message": "data.test_data_source.beep: Refreshing state... [id=honk]", + "@module": "terraform.ui", + "type": "refresh_start", + "hook": map[string]interface{}{ + "resource": wantResource, + "id_key": "id", + "id_value": "honk", + }, + }, + { + "@level": "info", + "@message": "data.test_data_source.beep: Refresh complete [id=honk]", + "@module": "terraform.ui", + "type": "refresh_complete", + "hook": map[string]interface{}{ + "resource": wantResource, + "id_key": "id", + "id_value": "honk", + }, + }, + } + + testJSONViewOutputEquals(t, done(t).Stdout(), want) +} + +func testHookReturnValues(t *testing.T, action terraform.HookAction, err error) { + t.Helper() + + if err != nil { + t.Fatal(err) + } + if action != terraform.HookActionContinue { + t.Fatalf("Expected hook to continue, given: %#v", action) + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/hook_ui.go b/vendor/github.com/hashicorp/terraform/command/views/hook_ui.go similarity index 77% rename from vendor/github.com/hashicorp/terraform/command/hook_ui.go rename to vendor/github.com/hashicorp/terraform/command/views/hook_ui.go index b6a2e52f..a869b45a 100644 --- a/vendor/github.com/hashicorp/terraform/command/hook_ui.go +++ b/vendor/github.com/hashicorp/terraform/command/views/hook_ui.go @@ -1,4 +1,4 @@ -package command +package views import ( "bufio" @@ -9,8 +9,6 @@ import ( "time" "unicode" - "github.com/mitchellh/cli" - "github.com/mitchellh/colorstring" "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/addrs" @@ -24,17 +22,24 @@ import ( const defaultPeriodicUiTimer = 10 * time.Second const maxIdLen = 80 +func NewUiHook(view *View) *UiHook { + return &UiHook{ + view: view, + periodicUiTimer: defaultPeriodicUiTimer, + resources: make(map[string]uiResourceState), + } +} + type UiHook struct { terraform.NilHook - Colorize *colorstring.Colorize - Ui cli.Ui - PeriodicUiTimer time.Duration + view *View + viewLock sync.Mutex - l sync.Mutex - once sync.Once - resources map[string]uiResourceState - ui cli.Ui + periodicUiTimer time.Duration + + resources map[string]uiResourceState + resourcesLock sync.Mutex } var _ terraform.Hook = (*UiHook)(nil) @@ -63,8 +68,6 @@ const ( ) func (h *UiHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, action plans.Action, priorState, plannedNewState cty.Value) (terraform.HookAction, error) { - h.once.Do(h.init) - dispAddr := addr.String() if gen != states.CurrentGen { dispAddr = fmt.Sprintf("%s (%s)", dispAddr, gen) @@ -89,7 +92,7 @@ func (h *UiHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, default: // We don't expect any other actions in here, so anything else is a // bug in the caller but we'll ignore it in order to be robust. - h.ui.Output(fmt.Sprintf("(Unknown action %s for %s)", action, dispAddr)) + h.println(fmt.Sprintf("(Unknown action %s for %s)", action, dispAddr)) return terraform.HookActionContinue, nil } @@ -103,12 +106,12 @@ func (h *UiHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, idValue = "" } - h.ui.Output(h.Colorize.Color(fmt.Sprintf( - "[reset][bold]%s: %s%s[reset]", + h.println(fmt.Sprintf( + h.view.colorize.Color("[reset][bold]%s: %s%s[reset]"), dispAddr, operation, stateIdSuffix, - ))) + )) key := addr.String() uiState := uiResourceState{ @@ -121,9 +124,9 @@ func (h *UiHook) PreApply(addr addrs.AbsResourceInstance, gen states.Generation, done: make(chan struct{}), } - h.l.Lock() + h.resourcesLock.Lock() h.resources[key] = uiState - h.l.Unlock() + h.resourcesLock.Unlock() // Start goroutine that shows progress go h.stillApplying(uiState) @@ -138,7 +141,7 @@ func (h *UiHook) stillApplying(state uiResourceState) { case <-state.DoneCh: return - case <-time.After(h.PeriodicUiTimer): + case <-time.After(h.periodicUiTimer): // Timer up, show status } @@ -161,28 +164,27 @@ func (h *UiHook) stillApplying(state uiResourceState) { idSuffix = fmt.Sprintf("%s=%s, ", state.IDKey, truncateId(state.IDValue, maxIdLen)) } - h.ui.Output(h.Colorize.Color(fmt.Sprintf( - "[reset][bold]%s: %s [%s%s elapsed][reset]", + h.println(fmt.Sprintf( + h.view.colorize.Color("[reset][bold]%s: %s [%s%s elapsed][reset]"), state.DispAddr, msg, idSuffix, time.Now().Round(time.Second).Sub(state.Start), - ))) + )) } } func (h *UiHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generation, newState cty.Value, applyerr error) (terraform.HookAction, error) { - id := addr.String() - h.l.Lock() + h.resourcesLock.Lock() state := h.resources[id] if state.DoneCh != nil { close(state.DoneCh) } delete(h.resources, id) - h.l.Unlock() + h.resourcesLock.Unlock() var stateIdSuffix string if k, v := format.ObjectValueID(newState); k != "" && v != "" { @@ -208,32 +210,30 @@ func (h *UiHook) PostApply(addr addrs.AbsResourceInstance, gen states.Generation return terraform.HookActionContinue, nil } - colorized := h.Colorize.Color(fmt.Sprintf( - "[reset][bold]%s: %s after %s%s[reset]", - addr, msg, time.Now().Round(time.Second).Sub(state.Start), stateIdSuffix)) - - h.ui.Output(colorized) + colorized := fmt.Sprintf( + h.view.colorize.Color("[reset][bold]%s: %s after %s%s"), + addr, msg, time.Now().Round(time.Second).Sub(state.Start), stateIdSuffix) - return terraform.HookActionContinue, nil -} + h.println(colorized) -func (h *UiHook) PreDiff(addr addrs.AbsResourceInstance, gen states.Generation, priorState, proposedNewState cty.Value) (terraform.HookAction, error) { return terraform.HookActionContinue, nil } func (h *UiHook) PreProvisionInstanceStep(addr addrs.AbsResourceInstance, typeName string) (terraform.HookAction, error) { - h.ui.Output(h.Colorize.Color(fmt.Sprintf( - "[reset][bold]%s: Provisioning with '%s'...[reset]", + h.println(fmt.Sprintf( + h.view.colorize.Color("[reset][bold]%s: Provisioning with '%s'...[reset]"), addr, typeName, - ))) + )) return terraform.HookActionContinue, nil } func (h *UiHook) ProvisionOutput(addr addrs.AbsResourceInstance, typeName string, msg string) { var buf bytes.Buffer - buf.WriteString(h.Colorize.Color("[reset]")) - prefix := fmt.Sprintf("%s (%s): ", addr, typeName) + prefix := fmt.Sprintf( + h.view.colorize.Color("[reset][bold]%s (%s):[reset] "), + addr, typeName, + ) s := bufio.NewScanner(strings.NewReader(msg)) s.Split(scanLines) for s.Scan() { @@ -243,61 +243,49 @@ func (h *UiHook) ProvisionOutput(addr addrs.AbsResourceInstance, typeName string } } - h.ui.Output(strings.TrimSpace(buf.String())) + h.println(strings.TrimSpace(buf.String())) } func (h *UiHook) PreRefresh(addr addrs.AbsResourceInstance, gen states.Generation, priorState cty.Value) (terraform.HookAction, error) { - h.once.Do(h.init) - var stateIdSuffix string if k, v := format.ObjectValueID(priorState); k != "" && v != "" { stateIdSuffix = fmt.Sprintf(" [%s=%s]", k, v) } - h.ui.Output(h.Colorize.Color(fmt.Sprintf( - "[reset][bold]%s: Refreshing state...%s", - addr, stateIdSuffix))) + h.println(fmt.Sprintf( + h.view.colorize.Color("[reset][bold]%s: Refreshing state...%s"), + addr, stateIdSuffix)) return terraform.HookActionContinue, nil } func (h *UiHook) PreImportState(addr addrs.AbsResourceInstance, importID string) (terraform.HookAction, error) { - h.once.Do(h.init) - - h.ui.Output(h.Colorize.Color(fmt.Sprintf( - "[reset][bold]%s: Importing from ID %q...", + h.println(fmt.Sprintf( + h.view.colorize.Color("[reset][bold]%s: Importing from ID %q..."), addr, importID, - ))) + )) return terraform.HookActionContinue, nil } func (h *UiHook) PostImportState(addr addrs.AbsResourceInstance, imported []providers.ImportedResource) (terraform.HookAction, error) { - h.once.Do(h.init) - - h.ui.Output(h.Colorize.Color(fmt.Sprintf( - "[reset][bold][green]%s: Import prepared!", addr))) + h.println(fmt.Sprintf( + h.view.colorize.Color("[reset][bold][green]%s: Import prepared!"), + addr, + )) for _, s := range imported { - h.ui.Output(h.Colorize.Color(fmt.Sprintf( - "[reset][green] Prepared %s for import", + h.println(fmt.Sprintf( + h.view.colorize.Color("[reset][green] Prepared %s for import"), s.TypeName, - ))) + )) } return terraform.HookActionContinue, nil } -func (h *UiHook) init() { - if h.Colorize == nil { - panic("colorize not given") - } - if h.PeriodicUiTimer == 0 { - h.PeriodicUiTimer = defaultPeriodicUiTimer - } - - h.resources = make(map[string]uiResourceState) - - // Wrap the ui so that it is safe for concurrency regardless of the - // underlying reader/writer that is in place. - h.ui = &cli.ConcurrentUi{Ui: h.Ui} +// Wrap calls to the view so that concurrent calls do not interleave println. +func (h *UiHook) println(s string) { + h.viewLock.Lock() + defer h.viewLock.Unlock() + h.view.streams.Println(s) } // scanLines is basically copied from the Go standard library except @@ -311,7 +299,7 @@ func scanLines(data []byte, atEOF bool) (advance int, token []byte, err error) { return i + 1, dropCR(data[0:i]), nil } if i := bytes.IndexByte(data, '\r'); i >= 0 { - // We have a full newline-terminated line. + // We have a full carriage-return-terminated line. return i + 1, dropCR(data[0:i]), nil } // If we're at EOF, we have a final, non-terminated line. Return it. diff --git a/vendor/github.com/hashicorp/terraform/command/views/hook_ui_test.go b/vendor/github.com/hashicorp/terraform/command/views/hook_ui_test.go new file mode 100644 index 00000000..146d4e3d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/hook_ui_test.go @@ -0,0 +1,658 @@ +package views + +import ( + "fmt" + "regexp" + "testing" + "time" + + "strings" + + "github.com/zclconf/go-cty/cty" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/internal/terminal" + "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/states" + "github.com/hashicorp/terraform/terraform" +) + +// Test the PreApply hook for creating a new resource +func TestUiHookPreApply_create(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + view := NewView(streams) + h := NewUiHook(view) + h.resources = map[string]uiResourceState{ + "test_instance.foo": { + Op: uiResourceCreate, + Start: time.Now(), + }, + } + + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + priorState := cty.NullVal(cty.Object(map[string]cty.Type{ + "id": cty.String, + "bar": cty.List(cty.String), + })) + plannedNewState := cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("test"), + "bar": cty.ListVal([]cty.Value{ + cty.StringVal("baz"), + }), + }) + + action, err := h.PreApply(addr, states.CurrentGen, plans.Create, priorState, plannedNewState) + if err != nil { + t.Fatal(err) + } + if action != terraform.HookActionContinue { + t.Fatalf("Expected hook to continue, given: %#v", action) + } + + // stop the background writer + uiState := h.resources[addr.String()] + close(uiState.DoneCh) + <-uiState.done + + expectedOutput := "test_instance.foo: Creating...\n" + result := done(t) + output := result.Stdout() + if output != expectedOutput { + t.Fatalf("Output didn't match.\nExpected: %q\nGiven: %q", expectedOutput, output) + } + + expectedErrOutput := "" + errOutput := result.Stderr() + if errOutput != expectedErrOutput { + t.Fatalf("Error output didn't match.\nExpected: %q\nGiven: %q", expectedErrOutput, errOutput) + } +} + +// Test the PreApply hook's use of a periodic timer to display "still working" +// log lines +func TestUiHookPreApply_periodicTimer(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + view := NewView(streams) + h := NewUiHook(view) + h.periodicUiTimer = 1 * time.Second + h.resources = map[string]uiResourceState{ + "test_instance.foo": { + Op: uiResourceModify, + Start: time.Now(), + }, + } + + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + priorState := cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("test"), + "bar": cty.ListValEmpty(cty.String), + }) + plannedNewState := cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("test"), + "bar": cty.ListVal([]cty.Value{ + cty.StringVal("baz"), + }), + }) + + action, err := h.PreApply(addr, states.CurrentGen, plans.Update, priorState, plannedNewState) + if err != nil { + t.Fatal(err) + } + if action != terraform.HookActionContinue { + t.Fatalf("Expected hook to continue, given: %#v", action) + } + + time.Sleep(3100 * time.Millisecond) + + // stop the background writer + uiState := h.resources[addr.String()] + close(uiState.DoneCh) + <-uiState.done + + expectedOutput := `test_instance.foo: Modifying... [id=test] +test_instance.foo: Still modifying... [id=test, 1s elapsed] +test_instance.foo: Still modifying... [id=test, 2s elapsed] +test_instance.foo: Still modifying... [id=test, 3s elapsed] +` + result := done(t) + output := result.Stdout() + if output != expectedOutput { + t.Fatalf("Output didn't match.\nExpected: %q\nGiven: %q", expectedOutput, output) + } + + expectedErrOutput := "" + errOutput := result.Stderr() + if errOutput != expectedErrOutput { + t.Fatalf("Error output didn't match.\nExpected: %q\nGiven: %q", expectedErrOutput, errOutput) + } +} + +// Test the PreApply hook's destroy path, including passing a deposed key as +// the gen argument. +func TestUiHookPreApply_destroy(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + view := NewView(streams) + h := NewUiHook(view) + h.resources = map[string]uiResourceState{ + "test_instance.foo": { + Op: uiResourceDestroy, + Start: time.Now(), + }, + } + + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + priorState := cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("abc123"), + "verbs": cty.ListVal([]cty.Value{ + cty.StringVal("boop"), + }), + }) + plannedNewState := cty.NullVal(cty.Object(map[string]cty.Type{ + "id": cty.String, + "verbs": cty.List(cty.String), + })) + + key := states.NewDeposedKey() + action, err := h.PreApply(addr, key, plans.Delete, priorState, plannedNewState) + if err != nil { + t.Fatal(err) + } + if action != terraform.HookActionContinue { + t.Fatalf("Expected hook to continue, given: %#v", action) + } + + // stop the background writer + uiState := h.resources[addr.String()] + close(uiState.DoneCh) + <-uiState.done + + result := done(t) + expectedOutput := fmt.Sprintf("test_instance.foo (%s): Destroying... [id=abc123]\n", key) + output := result.Stdout() + if output != expectedOutput { + t.Fatalf("Output didn't match.\nExpected: %q\nGiven: %q", expectedOutput, output) + } + + expectedErrOutput := "" + errOutput := result.Stderr() + if errOutput != expectedErrOutput { + t.Fatalf("Error output didn't match.\nExpected: %q\nGiven: %q", expectedErrOutput, errOutput) + } +} + +// Verify that colorize is called on format strings, not user input, by adding +// valid color codes as resource names and IDs. +func TestUiHookPostApply_colorInterpolation(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + view := NewView(streams) + view.Configure(&arguments.View{NoColor: false}) + h := NewUiHook(view) + h.resources = map[string]uiResourceState{ + "test_instance.foo[\"[red]\"]": { + Op: uiResourceCreate, + Start: time.Now(), + }, + } + + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.StringKey("[red]")).Absolute(addrs.RootModuleInstance) + + newState := cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("[blue]"), + }) + + action, err := h.PostApply(addr, states.CurrentGen, newState, nil) + if err != nil { + t.Fatal(err) + } + if action != terraform.HookActionContinue { + t.Fatalf("Expected hook to continue, given: %#v", action) + } + result := done(t) + + reset := "\x1b[0m" + bold := "\x1b[1m" + wantPrefix := reset + bold + `test_instance.foo["[red]"]: Creation complete after` + wantSuffix := "[id=[blue]]" + reset + "\n" + output := result.Stdout() + + if !strings.HasPrefix(output, wantPrefix) { + t.Fatalf("wrong output prefix\n got: %#v\nwant: %#v", output, wantPrefix) + } + + if !strings.HasSuffix(output, wantSuffix) { + t.Fatalf("wrong output suffix\n got: %#v\nwant: %#v", output, wantSuffix) + } + + expectedErrOutput := "" + errOutput := result.Stderr() + if errOutput != expectedErrOutput { + t.Fatalf("Error output didn't match.\nExpected: %q\nGiven: %q", expectedErrOutput, errOutput) + } +} + +// Test that the PostApply hook renders a total time. +func TestUiHookPostApply_emptyState(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + view := NewView(streams) + h := NewUiHook(view) + h.resources = map[string]uiResourceState{ + "data.google_compute_zones.available": { + Op: uiResourceDestroy, + Start: time.Now(), + }, + } + + addr := addrs.Resource{ + Mode: addrs.DataResourceMode, + Type: "google_compute_zones", + Name: "available", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + newState := cty.NullVal(cty.Object(map[string]cty.Type{ + "id": cty.String, + "names": cty.List(cty.String), + })) + + action, err := h.PostApply(addr, states.CurrentGen, newState, nil) + if err != nil { + t.Fatal(err) + } + if action != terraform.HookActionContinue { + t.Fatalf("Expected hook to continue, given: %#v", action) + } + result := done(t) + + expectedRegexp := "^data.google_compute_zones.available: Destruction complete after -?[a-z0-9µ.]+\n$" + output := result.Stdout() + if matched, _ := regexp.MatchString(expectedRegexp, output); !matched { + t.Fatalf("Output didn't match regexp.\nExpected: %q\nGiven: %q", expectedRegexp, output) + } + + expectedErrOutput := "" + errOutput := result.Stderr() + if errOutput != expectedErrOutput { + t.Fatalf("Error output didn't match.\nExpected: %q\nGiven: %q", expectedErrOutput, errOutput) + } +} + +func TestPreProvisionInstanceStep(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + view := NewView(streams) + h := NewUiHook(view) + + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + action, err := h.PreProvisionInstanceStep(addr, "local-exec") + if err != nil { + t.Fatal(err) + } + if action != terraform.HookActionContinue { + t.Fatalf("Expected hook to continue, given: %#v", action) + } + result := done(t) + + if got, want := result.Stdout(), "test_instance.foo: Provisioning with 'local-exec'...\n"; got != want { + t.Fatalf("unexpected output\n got: %q\nwant: %q", got, want) + } +} + +// Test ProvisionOutput, including lots of edge cases for the output +// whitespace/line ending logic. +func TestProvisionOutput(t *testing.T) { + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + testCases := map[string]struct { + provisioner string + input string + wantOutput string + }{ + "single line": { + "local-exec", + "foo\n", + "test_instance.foo (local-exec): foo\n", + }, + "multiple lines": { + "x", + `foo +bar +baz +`, + `test_instance.foo (x): foo +test_instance.foo (x): bar +test_instance.foo (x): baz +`, + }, + "trailing whitespace": { + "x", + "foo \nbar\n", + "test_instance.foo (x): foo\ntest_instance.foo (x): bar\n", + }, + "blank lines": { + "x", + "foo\n\nbar\n\n\nbaz\n", + `test_instance.foo (x): foo +test_instance.foo (x): bar +test_instance.foo (x): baz +`, + }, + "no final newline": { + "x", + `foo +bar`, + `test_instance.foo (x): foo +test_instance.foo (x): bar +`, + }, + "CR, no LF": { + "MacOS 9?", + "foo\rbar\r", + `test_instance.foo (MacOS 9?): foo +test_instance.foo (MacOS 9?): bar +`, + }, + "CRLF": { + "winrm", + "foo\r\nbar\r\n", + `test_instance.foo (winrm): foo +test_instance.foo (winrm): bar +`, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + view := NewView(streams) + h := NewUiHook(view) + + h.ProvisionOutput(addr, tc.provisioner, tc.input) + result := done(t) + + if got := result.Stdout(); got != tc.wantOutput { + t.Fatalf("unexpected output\n got: %q\nwant: %q", got, tc.wantOutput) + } + }) + } +} + +// Test the PreRefresh hook in the normal path where the resource exists with +// an ID key and value in the state. +func TestPreRefresh(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + view := NewView(streams) + h := NewUiHook(view) + + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + priorState := cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("test"), + "bar": cty.ListValEmpty(cty.String), + }) + + action, err := h.PreRefresh(addr, states.CurrentGen, priorState) + + if err != nil { + t.Fatal(err) + } + if action != terraform.HookActionContinue { + t.Fatalf("Expected hook to continue, given: %#v", action) + } + result := done(t) + + if got, want := result.Stdout(), "test_instance.foo: Refreshing state... [id=test]\n"; got != want { + t.Fatalf("unexpected output\n got: %q\nwant: %q", got, want) + } +} + +// Test that PreRefresh still works if no ID key and value can be determined +// from state. +func TestPreRefresh_noID(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + view := NewView(streams) + h := NewUiHook(view) + + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + priorState := cty.ObjectVal(map[string]cty.Value{ + "bar": cty.ListValEmpty(cty.String), + }) + + action, err := h.PreRefresh(addr, states.CurrentGen, priorState) + + if err != nil { + t.Fatal(err) + } + if action != terraform.HookActionContinue { + t.Fatalf("Expected hook to continue, given: %#v", action) + } + result := done(t) + + if got, want := result.Stdout(), "test_instance.foo: Refreshing state...\n"; got != want { + t.Fatalf("unexpected output\n got: %q\nwant: %q", got, want) + } +} + +// Test the very simple PreImportState hook. +func TestPreImportState(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + view := NewView(streams) + h := NewUiHook(view) + + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + action, err := h.PreImportState(addr, "test") + + if err != nil { + t.Fatal(err) + } + if action != terraform.HookActionContinue { + t.Fatalf("Expected hook to continue, given: %#v", action) + } + result := done(t) + + if got, want := result.Stdout(), "test_instance.foo: Importing from ID \"test\"...\n"; got != want { + t.Fatalf("unexpected output\n got: %q\nwant: %q", got, want) + } +} + +// Test the PostImportState UI hook. Again, this hook behaviour seems odd to +// me (see below), so please don't consider these tests as justification for +// keeping this behaviour. +func TestPostImportState(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + view := NewView(streams) + h := NewUiHook(view) + + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + // The "Prepared [...] for import" lines display the type name of each of + // the imported resources passed to the hook. I'm not sure how it's + // possible for an import to result in a different resource type name than + // the target address, but the hook works like this so we're covering it. + imported := []providers.ImportedResource{ + { + TypeName: "test_some_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("test"), + }), + }, + { + TypeName: "test_other_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("test"), + }), + }, + } + + action, err := h.PostImportState(addr, imported) + + if err != nil { + t.Fatal(err) + } + if action != terraform.HookActionContinue { + t.Fatalf("Expected hook to continue, given: %#v", action) + } + result := done(t) + + want := `test_instance.foo: Import prepared! + Prepared test_some_instance for import + Prepared test_other_instance for import +` + if got := result.Stdout(); got != want { + t.Fatalf("unexpected output\n got: %q\nwant: %q", got, want) + } +} + +func TestTruncateId(t *testing.T) { + testCases := []struct { + Input string + Expected string + MaxLen int + }{ + { + Input: "Hello world", + Expected: "H...d", + MaxLen: 3, + }, + { + Input: "Hello world", + Expected: "H...d", + MaxLen: 5, + }, + { + Input: "Hello world", + Expected: "He...d", + MaxLen: 6, + }, + { + Input: "Hello world", + Expected: "He...ld", + MaxLen: 7, + }, + { + Input: "Hello world", + Expected: "Hel...ld", + MaxLen: 8, + }, + { + Input: "Hello world", + Expected: "Hel...rld", + MaxLen: 9, + }, + { + Input: "Hello world", + Expected: "Hell...rld", + MaxLen: 10, + }, + { + Input: "Hello world", + Expected: "Hello world", + MaxLen: 11, + }, + { + Input: "Hello world", + Expected: "Hello world", + MaxLen: 12, + }, + { + Input: "あいうえおかきくけこさ", + Expected: "あ...さ", + MaxLen: 3, + }, + { + Input: "あいうえおかきくけこさ", + Expected: "あ...さ", + MaxLen: 5, + }, + { + Input: "あいうえおかきくけこさ", + Expected: "あい...さ", + MaxLen: 6, + }, + { + Input: "あいうえおかきくけこさ", + Expected: "あい...こさ", + MaxLen: 7, + }, + { + Input: "あいうえおかきくけこさ", + Expected: "あいう...こさ", + MaxLen: 8, + }, + { + Input: "あいうえおかきくけこさ", + Expected: "あいう...けこさ", + MaxLen: 9, + }, + { + Input: "あいうえおかきくけこさ", + Expected: "あいうえ...けこさ", + MaxLen: 10, + }, + { + Input: "あいうえおかきくけこさ", + Expected: "あいうえおかきくけこさ", + MaxLen: 11, + }, + { + Input: "あいうえおかきくけこさ", + Expected: "あいうえおかきくけこさ", + MaxLen: 12, + }, + } + for i, tc := range testCases { + testName := fmt.Sprintf("%d", i) + t.Run(testName, func(t *testing.T) { + out := truncateId(tc.Input, tc.MaxLen) + if out != tc.Expected { + t.Fatalf("Expected %q to be shortened to %d as %q (given: %q)", + tc.Input, tc.MaxLen, tc.Expected, out) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/change.go b/vendor/github.com/hashicorp/terraform/command/views/json/change.go new file mode 100644 index 00000000..4b576071 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/change.go @@ -0,0 +1,85 @@ +package json + +import ( + "fmt" + + "github.com/hashicorp/terraform/plans" +) + +func NewResourceInstanceChange(change *plans.ResourceInstanceChangeSrc) *ResourceInstanceChange { + c := &ResourceInstanceChange{ + Resource: newResourceAddr(change.Addr), + Action: changeAction(change.Action), + Reason: changeReason(change.ActionReason), + } + + return c +} + +type ResourceInstanceChange struct { + Resource ResourceAddr `json:"resource"` + Action ChangeAction `json:"action"` + Reason ChangeReason `json:"reason,omitempty"` +} + +func (c *ResourceInstanceChange) String() string { + return fmt.Sprintf("%s: Plan to %s", c.Resource.Addr, c.Action) +} + +type ChangeAction string + +const ( + ActionNoOp ChangeAction = "noop" + ActionCreate ChangeAction = "create" + ActionRead ChangeAction = "read" + ActionUpdate ChangeAction = "update" + ActionReplace ChangeAction = "replace" + ActionDelete ChangeAction = "delete" +) + +func changeAction(action plans.Action) ChangeAction { + switch action { + case plans.NoOp: + return ActionNoOp + case plans.Create: + return ActionCreate + case plans.Read: + return ActionRead + case plans.Update: + return ActionUpdate + case plans.DeleteThenCreate, plans.CreateThenDelete: + return ActionReplace + case plans.Delete: + return ActionDelete + default: + return ActionNoOp + } +} + +type ChangeReason string + +const ( + ReasonNone ChangeReason = "" + ReasonTainted ChangeReason = "tainted" + ReasonRequested ChangeReason = "requested" + ReasonCannotUpdate ChangeReason = "cannot_update" + ReasonUnknown ChangeReason = "unknown" +) + +func changeReason(reason plans.ResourceInstanceChangeActionReason) ChangeReason { + switch reason { + case plans.ResourceInstanceChangeNoReason: + return ReasonNone + case plans.ResourceInstanceReplaceBecauseTainted: + return ReasonTainted + case plans.ResourceInstanceReplaceByRequest: + return ReasonRequested + case plans.ResourceInstanceReplaceBecauseCannotUpdate: + return ReasonCannotUpdate + default: + // This should never happen, but there's no good way to guarantee + // exhaustive handling of the enum, so a generic fall back is better + // than a misleading result or a panic + return ReasonUnknown + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/change_summary.go b/vendor/github.com/hashicorp/terraform/command/views/json/change_summary.go new file mode 100644 index 00000000..2b87f62e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/change_summary.go @@ -0,0 +1,34 @@ +package json + +import "fmt" + +type Operation string + +const ( + OperationApplied Operation = "apply" + OperationDestroyed Operation = "destroy" + OperationPlanned Operation = "plan" +) + +type ChangeSummary struct { + Add int `json:"add"` + Change int `json:"change"` + Remove int `json:"remove"` + Operation Operation `json:"operation"` +} + +// The summary strings for apply and plan are accidentally a public interface +// used by Terraform Cloud and Terraform Enterprise, so the exact formats of +// these strings are important. +func (cs *ChangeSummary) String() string { + switch cs.Operation { + case OperationApplied: + return fmt.Sprintf("Apply complete! Resources: %d added, %d changed, %d destroyed.", cs.Add, cs.Change, cs.Remove) + case OperationDestroyed: + return fmt.Sprintf("Destroy complete! Resources: %d destroyed.", cs.Remove) + case OperationPlanned: + return fmt.Sprintf("Plan: %d to add, %d to change, %d to destroy.", cs.Add, cs.Change, cs.Remove) + default: + return fmt.Sprintf("%s: %d add, %d change, %d destroy", cs.Operation, cs.Add, cs.Change, cs.Remove) + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/diagnostic.go b/vendor/github.com/hashicorp/terraform/command/views/json/diagnostic.go new file mode 100644 index 00000000..1290e6bb --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/diagnostic.go @@ -0,0 +1,419 @@ +package json + +import ( + "bufio" + "bytes" + "fmt" + "sort" + "strings" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hcled" + "github.com/hashicorp/hcl/v2/hclparse" + "github.com/hashicorp/terraform/tfdiags" + "github.com/zclconf/go-cty/cty" +) + +// These severities map to the tfdiags.Severity values, plus an explicit +// unknown in case that enum grows without us noticing here. +const ( + DiagnosticSeverityUnknown = "unknown" + DiagnosticSeverityError = "error" + DiagnosticSeverityWarning = "warning" +) + +// Diagnostic represents any tfdiags.Diagnostic value. The simplest form has +// just a severity, single line summary, and optional detail. If there is more +// information about the source of the diagnostic, this is represented in the +// range field. +type Diagnostic struct { + Severity string `json:"severity"` + Summary string `json:"summary"` + Detail string `json:"detail"` + Address string `json:"address,omitempty"` + Range *DiagnosticRange `json:"range,omitempty"` + Snippet *DiagnosticSnippet `json:"snippet,omitempty"` +} + +// Pos represents a position in the source code. +type Pos struct { + // Line is a one-based count for the line in the indicated file. + Line int `json:"line"` + + // Column is a one-based count of Unicode characters from the start of the line. + Column int `json:"column"` + + // Byte is a zero-based offset into the indicated file. + Byte int `json:"byte"` +} + +// DiagnosticRange represents the filename and position of the diagnostic +// subject. This defines the range of the source to be highlighted in the +// output. Note that the snippet may include additional surrounding source code +// if the diagnostic has a context range. +// +// The Start position is inclusive, and the End position is exclusive. Exact +// positions are intended for highlighting for human interpretation only and +// are subject to change. +type DiagnosticRange struct { + Filename string `json:"filename"` + Start Pos `json:"start"` + End Pos `json:"end"` +} + +// DiagnosticSnippet represents source code information about the diagnostic. +// It is possible for a diagnostic to have a source (and therefore a range) but +// no source code can be found. In this case, the range field will be present and +// the snippet field will not. +type DiagnosticSnippet struct { + // Context is derived from HCL's hcled.ContextString output. This gives a + // high-level summary of the root context of the diagnostic: for example, + // the resource block in which an expression causes an error. + Context *string `json:"context"` + + // Code is a possibly-multi-line string of Terraform configuration, which + // includes both the diagnostic source and any relevant context as defined + // by the diagnostic. + Code string `json:"code"` + + // StartLine is the line number in the source file for the first line of + // the snippet code block. This is not necessarily the same as the value of + // Range.Start.Line, as it is possible to have zero or more lines of + // context source code before the diagnostic range starts. + StartLine int `json:"start_line"` + + // HighlightStartOffset is the character offset into Code at which the + // diagnostic source range starts, which ought to be highlighted as such by + // the consumer of this data. + HighlightStartOffset int `json:"highlight_start_offset"` + + // HighlightEndOffset is the character offset into Code at which the + // diagnostic source range ends. + HighlightEndOffset int `json:"highlight_end_offset"` + + // Values is a sorted slice of expression values which may be useful in + // understanding the source of an error in a complex expression. + Values []DiagnosticExpressionValue `json:"values"` +} + +// DiagnosticExpressionValue represents an HCL traversal string (e.g. +// "var.foo") and a statement about its value while the expression was +// evaluated (e.g. "is a string", "will be known only after apply"). These are +// intended to help the consumer diagnose why an expression caused a diagnostic +// to be emitted. +type DiagnosticExpressionValue struct { + Traversal string `json:"traversal"` + Statement string `json:"statement"` +} + +// NewDiagnostic takes a tfdiags.Diagnostic and a map of configuration sources, +// and returns a Diagnostic struct. +func NewDiagnostic(diag tfdiags.Diagnostic, sources map[string][]byte) *Diagnostic { + var sev string + switch diag.Severity() { + case tfdiags.Error: + sev = DiagnosticSeverityError + case tfdiags.Warning: + sev = DiagnosticSeverityWarning + default: + sev = DiagnosticSeverityUnknown + } + + desc := diag.Description() + + diagnostic := &Diagnostic{ + Severity: sev, + Summary: desc.Summary, + Detail: desc.Detail, + Address: desc.Address, + } + + sourceRefs := diag.Source() + if sourceRefs.Subject != nil { + // We'll borrow HCL's range implementation here, because it has some + // handy features to help us produce a nice source code snippet. + highlightRange := sourceRefs.Subject.ToHCL() + + // Some diagnostic sources fail to set the end of the subject range. + if highlightRange.End == (hcl.Pos{}) { + highlightRange.End = highlightRange.Start + } + + snippetRange := highlightRange + if sourceRefs.Context != nil { + snippetRange = sourceRefs.Context.ToHCL() + } + + // Make sure the snippet includes the highlight. This should be true + // for any reasonable diagnostic, but we'll make sure. + snippetRange = hcl.RangeOver(snippetRange, highlightRange) + + // Empty ranges result in odd diagnostic output, so extend the end to + // ensure there's at least one byte in the snippet or highlight. + if snippetRange.Empty() { + snippetRange.End.Byte++ + snippetRange.End.Column++ + } + if highlightRange.Empty() { + highlightRange.End.Byte++ + highlightRange.End.Column++ + } + + diagnostic.Range = &DiagnosticRange{ + Filename: highlightRange.Filename, + Start: Pos{ + Line: highlightRange.Start.Line, + Column: highlightRange.Start.Column, + Byte: highlightRange.Start.Byte, + }, + End: Pos{ + Line: highlightRange.End.Line, + Column: highlightRange.End.Column, + Byte: highlightRange.End.Byte, + }, + } + + var src []byte + if sources != nil { + src = sources[highlightRange.Filename] + } + + // If we have a source file for the diagnostic, we can emit a code + // snippet. + if src != nil { + diagnostic.Snippet = &DiagnosticSnippet{ + StartLine: snippetRange.Start.Line, + + // Ensure that the default Values struct is an empty array, as this + // makes consuming the JSON structure easier in most languages. + Values: []DiagnosticExpressionValue{}, + } + + file, offset := parseRange(src, highlightRange) + + // Some diagnostics may have a useful top-level context to add to + // the code snippet output. + contextStr := hcled.ContextString(file, offset-1) + if contextStr != "" { + diagnostic.Snippet.Context = &contextStr + } + + // Build the string of the code snippet, tracking at which byte of + // the file the snippet starts. + var codeStartByte int + sc := hcl.NewRangeScanner(src, highlightRange.Filename, bufio.ScanLines) + var code strings.Builder + for sc.Scan() { + lineRange := sc.Range() + if lineRange.Overlaps(snippetRange) { + if codeStartByte == 0 && code.Len() == 0 { + codeStartByte = lineRange.Start.Byte + } + code.Write(lineRange.SliceBytes(src)) + code.WriteRune('\n') + } + } + codeStr := strings.TrimSuffix(code.String(), "\n") + diagnostic.Snippet.Code = codeStr + + // Calculate the start and end byte of the highlight range relative + // to the code snippet string. + start := highlightRange.Start.Byte - codeStartByte + end := start + (highlightRange.End.Byte - highlightRange.Start.Byte) + if start > len(codeStr) { + start = len(codeStr) + } + if end > len(codeStr) { + end = len(codeStr) + } + diagnostic.Snippet.HighlightStartOffset = start + diagnostic.Snippet.HighlightEndOffset = end + + if fromExpr := diag.FromExpr(); fromExpr != nil { + // We may also be able to generate information about the dynamic + // values of relevant variables at the point of evaluation, then. + // This is particularly useful for expressions that get evaluated + // multiple times with different values, such as blocks using + // "count" and "for_each", or within "for" expressions. + expr := fromExpr.Expression + ctx := fromExpr.EvalContext + vars := expr.Variables() + values := make([]DiagnosticExpressionValue, 0, len(vars)) + seen := make(map[string]struct{}, len(vars)) + Traversals: + for _, traversal := range vars { + for len(traversal) > 1 { + val, diags := traversal.TraverseAbs(ctx) + if diags.HasErrors() { + // Skip anything that generates errors, since we probably + // already have the same error in our diagnostics set + // already. + traversal = traversal[:len(traversal)-1] + continue + } + + traversalStr := traversalStr(traversal) + if _, exists := seen[traversalStr]; exists { + continue Traversals // don't show duplicates when the same variable is referenced multiple times + } + value := DiagnosticExpressionValue{ + Traversal: traversalStr, + } + switch { + case val.IsMarked(): + // We won't say anything at all about sensitive values, + // because we might give away something that was + // sensitive about them. + value.Statement = "has a sensitive value" + case !val.IsKnown(): + if ty := val.Type(); ty != cty.DynamicPseudoType { + value.Statement = fmt.Sprintf("is a %s, known only after apply", ty.FriendlyName()) + } else { + value.Statement = "will be known only after apply" + } + default: + value.Statement = fmt.Sprintf("is %s", compactValueStr(val)) + } + values = append(values, value) + seen[traversalStr] = struct{}{} + } + } + sort.Slice(values, func(i, j int) bool { + return values[i].Traversal < values[j].Traversal + }) + diagnostic.Snippet.Values = values + } + } + } + + return diagnostic +} + +func parseRange(src []byte, rng hcl.Range) (*hcl.File, int) { + filename := rng.Filename + offset := rng.Start.Byte + + // We need to re-parse here to get a *hcl.File we can interrogate. This + // is not awesome since we presumably already parsed the file earlier too, + // but this re-parsing is architecturally simpler than retaining all of + // the hcl.File objects and we only do this in the case of an error anyway + // so the overhead here is not a big problem. + parser := hclparse.NewParser() + var file *hcl.File + + // Ignore diagnostics here as there is nothing we can do with them. + if strings.HasSuffix(filename, ".json") { + file, _ = parser.ParseJSON(src, filename) + } else { + file, _ = parser.ParseHCL(src, filename) + } + + return file, offset +} + +// compactValueStr produces a compact, single-line summary of a given value +// that is suitable for display in the UI. +// +// For primitives it returns a full representation, while for more complex +// types it instead summarizes the type, size, etc to produce something +// that is hopefully still somewhat useful but not as verbose as a rendering +// of the entire data structure. +func compactValueStr(val cty.Value) string { + // This is a specialized subset of value rendering tailored to producing + // helpful but concise messages in diagnostics. It is not comprehensive + // nor intended to be used for other purposes. + + if val.IsMarked() { + // We check this in here just to make sure, but note that the caller + // of compactValueStr ought to have already checked this and skipped + // calling into compactValueStr anyway, so this shouldn't actually + // be reachable. + return "(sensitive value)" + } + + // WARNING: We've only checked that the value isn't sensitive _shallowly_ + // here, and so we must never show any element values from complex types + // in here. However, it's fine to show map keys and attribute names because + // those are never sensitive in isolation: the entire value would be + // sensitive in that case. + + ty := val.Type() + switch { + case val.IsNull(): + return "null" + case !val.IsKnown(): + // Should never happen here because we should filter before we get + // in here, but we'll do something reasonable rather than panic. + return "(not yet known)" + case ty == cty.Bool: + if val.True() { + return "true" + } + return "false" + case ty == cty.Number: + bf := val.AsBigFloat() + return bf.Text('g', 10) + case ty == cty.String: + // Go string syntax is not exactly the same as HCL native string syntax, + // but we'll accept the minor edge-cases where this is different here + // for now, just to get something reasonable here. + return fmt.Sprintf("%q", val.AsString()) + case ty.IsCollectionType() || ty.IsTupleType(): + l := val.LengthInt() + switch l { + case 0: + return "empty " + ty.FriendlyName() + case 1: + return ty.FriendlyName() + " with 1 element" + default: + return fmt.Sprintf("%s with %d elements", ty.FriendlyName(), l) + } + case ty.IsObjectType(): + atys := ty.AttributeTypes() + l := len(atys) + switch l { + case 0: + return "object with no attributes" + case 1: + var name string + for k := range atys { + name = k + } + return fmt.Sprintf("object with 1 attribute %q", name) + default: + return fmt.Sprintf("object with %d attributes", l) + } + default: + return ty.FriendlyName() + } +} + +// traversalStr produces a representation of an HCL traversal that is compact, +// resembles HCL native syntax, and is suitable for display in the UI. +func traversalStr(traversal hcl.Traversal) string { + // This is a specialized subset of traversal rendering tailored to + // producing helpful contextual messages in diagnostics. It is not + // comprehensive nor intended to be used for other purposes. + + var buf bytes.Buffer + for _, step := range traversal { + switch tStep := step.(type) { + case hcl.TraverseRoot: + buf.WriteString(tStep.Name) + case hcl.TraverseAttr: + buf.WriteByte('.') + buf.WriteString(tStep.Name) + case hcl.TraverseIndex: + buf.WriteByte('[') + if keyTy := tStep.Key.Type(); keyTy.IsPrimitiveType() { + buf.WriteString(compactValueStr(tStep.Key)) + } else { + // We'll just use a placeholder for more complex values, + // since otherwise our result could grow ridiculously long. + buf.WriteString("...") + } + buf.WriteByte(']') + } + } + return buf.String() +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/diagnostic_test.go b/vendor/github.com/hashicorp/terraform/command/views/json/diagnostic_test.go new file mode 100644 index 00000000..a11d815e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/diagnostic_test.go @@ -0,0 +1,768 @@ +package json + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hcltest" + "github.com/hashicorp/terraform/tfdiags" + "github.com/zclconf/go-cty/cty" +) + +func TestNewDiagnostic(t *testing.T) { + // Common HCL for diags with source ranges. This does not have any real + // semantic errors, but we can synthesize fake HCL errors which will + // exercise the diagnostic rendering code using this + sources := map[string][]byte{ + "test.tf": []byte(`resource "test_resource" "test" { + foo = var.boop["hello!"] + bar = { + baz = maybe + } +} +`), + "short.tf": []byte("bad source code"), + "values.tf": []byte(`[ + var.a, + var.b, + var.c, + var.d, + var.e, + var.f, + var.g, + var.h, + var.i, + var.j, + var.k, +] +`), + } + testCases := map[string]struct { + diag interface{} // allow various kinds of diags + want *Diagnostic + }{ + "sourceless warning": { + tfdiags.Sourceless( + tfdiags.Warning, + "Oh no", + "Something is broken", + ), + &Diagnostic{ + Severity: "warning", + Summary: "Oh no", + Detail: "Something is broken", + }, + }, + "error with source code unavailable": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Bad news", + Detail: "It went wrong", + Subject: &hcl.Range{ + Filename: "modules/oops/missing.tf", + Start: hcl.Pos{Line: 1, Column: 6, Byte: 5}, + End: hcl.Pos{Line: 2, Column: 12, Byte: 33}, + }, + }, + &Diagnostic{ + Severity: "error", + Summary: "Bad news", + Detail: "It went wrong", + Range: &DiagnosticRange{ + Filename: "modules/oops/missing.tf", + Start: Pos{ + Line: 1, + Column: 6, + Byte: 5, + }, + End: Pos{ + Line: 2, + Column: 12, + Byte: 33, + }, + }, + }, + }, + "error with source code subject": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Tiny explosion", + Detail: "Unexpected detonation while parsing", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 10, Byte: 9}, + End: hcl.Pos{Line: 1, Column: 25, Byte: 24}, + }, + }, + &Diagnostic{ + Severity: "error", + Summary: "Tiny explosion", + Detail: "Unexpected detonation while parsing", + Range: &DiagnosticRange{ + Filename: "test.tf", + Start: Pos{ + Line: 1, + Column: 10, + Byte: 9, + }, + End: Pos{ + Line: 1, + Column: 25, + Byte: 24, + }, + }, + Snippet: &DiagnosticSnippet{ + Context: strPtr(`resource "test_resource" "test"`), + Code: `resource "test_resource" "test" {`, + StartLine: 1, + HighlightStartOffset: 9, + HighlightEndOffset: 24, + Values: []DiagnosticExpressionValue{}, + }, + }, + }, + "error with source code subject but no context": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Nonsense input", + Detail: "What you wrote makes no sense", + Subject: &hcl.Range{ + Filename: "short.tf", + Start: hcl.Pos{Line: 1, Column: 5, Byte: 4}, + End: hcl.Pos{Line: 1, Column: 10, Byte: 9}, + }, + }, + &Diagnostic{ + Severity: "error", + Summary: "Nonsense input", + Detail: "What you wrote makes no sense", + Range: &DiagnosticRange{ + Filename: "short.tf", + Start: Pos{ + Line: 1, + Column: 5, + Byte: 4, + }, + End: Pos{ + Line: 1, + Column: 10, + Byte: 9, + }, + }, + Snippet: &DiagnosticSnippet{ + Context: nil, + Code: (`bad source code`), + StartLine: (1), + HighlightStartOffset: (4), + HighlightEndOffset: (9), + Values: []DiagnosticExpressionValue{}, + }, + }, + }, + "error with multi-line snippet": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "In this house we respect booleans", + Detail: "True or false, there is no maybe", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 4, Column: 11, Byte: 81}, + End: hcl.Pos{Line: 4, Column: 16, Byte: 86}, + }, + Context: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 3, Column: 3, Byte: 63}, + End: hcl.Pos{Line: 5, Column: 4, Byte: 90}, + }, + }, + &Diagnostic{ + Severity: "error", + Summary: "In this house we respect booleans", + Detail: "True or false, there is no maybe", + Range: &DiagnosticRange{ + Filename: "test.tf", + Start: Pos{ + Line: 4, + Column: 11, + Byte: 81, + }, + End: Pos{ + Line: 4, + Column: 16, + Byte: 86, + }, + }, + Snippet: &DiagnosticSnippet{ + Context: strPtr(`resource "test_resource" "test"`), + Code: " bar = {\n baz = maybe\n }", + StartLine: 3, + HighlightStartOffset: 20, + HighlightEndOffset: 25, + Values: []DiagnosticExpressionValue{}, + }, + }, + }, + "error with empty highlight range at end of source code": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "You forgot something", + Detail: "Please finish your thought", + Subject: &hcl.Range{ + Filename: "short.tf", + Start: hcl.Pos{Line: 1, Column: 16, Byte: 15}, + End: hcl.Pos{Line: 1, Column: 16, Byte: 15}, + }, + }, + &Diagnostic{ + Severity: "error", + Summary: "You forgot something", + Detail: "Please finish your thought", + Range: &DiagnosticRange{ + Filename: "short.tf", + Start: Pos{ + Line: 1, + Column: 16, + Byte: 15, + }, + End: Pos{ + Line: 1, + Column: 17, + Byte: 16, + }, + }, + Snippet: &DiagnosticSnippet{ + Code: ("bad source code"), + StartLine: (1), + HighlightStartOffset: (15), + HighlightEndOffset: (15), + Values: []DiagnosticExpressionValue{}, + }, + }, + }, + "error with unset highlight end position": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "There is no end", + Detail: "But there is a beginning", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 16, Byte: 15}, + End: hcl.Pos{Line: 0, Column: 0, Byte: 0}, + }, + }, + &Diagnostic{ + Severity: "error", + Summary: "There is no end", + Detail: "But there is a beginning", + Range: &DiagnosticRange{ + Filename: "test.tf", + Start: Pos{ + Line: 1, + Column: 16, + Byte: 15, + }, + End: Pos{ + Line: 1, + Column: 17, + Byte: 16, + }, + }, + Snippet: &DiagnosticSnippet{ + Context: strPtr(`resource "test_resource" "test"`), + Code: `resource "test_resource" "test" {`, + StartLine: 1, + HighlightStartOffset: 15, + HighlightEndOffset: 16, + Values: []DiagnosticExpressionValue{}, + }, + }, + }, + "error with source code subject and known expression": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Wrong noises", + Detail: "Biological sounds are not allowed", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 2, Column: 9, Byte: 42}, + End: hcl.Pos{Line: 2, Column: 26, Byte: 59}, + }, + Expression: hcltest.MockExprTraversal(hcl.Traversal{ + hcl.TraverseRoot{Name: "var"}, + hcl.TraverseAttr{Name: "boop"}, + hcl.TraverseIndex{Key: cty.StringVal("hello!")}, + }), + EvalContext: &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "var": cty.ObjectVal(map[string]cty.Value{ + "boop": cty.MapVal(map[string]cty.Value{ + "hello!": cty.StringVal("bleurgh"), + }), + }), + }, + }, + }, + &Diagnostic{ + Severity: "error", + Summary: "Wrong noises", + Detail: "Biological sounds are not allowed", + Range: &DiagnosticRange{ + Filename: "test.tf", + Start: Pos{ + Line: 2, + Column: 9, + Byte: 42, + }, + End: Pos{ + Line: 2, + Column: 26, + Byte: 59, + }, + }, + Snippet: &DiagnosticSnippet{ + Context: strPtr(`resource "test_resource" "test"`), + Code: (` foo = var.boop["hello!"]`), + StartLine: (2), + HighlightStartOffset: (8), + HighlightEndOffset: (25), + Values: []DiagnosticExpressionValue{ + { + Traversal: `var.boop["hello!"]`, + Statement: `is "bleurgh"`, + }, + }, + }, + }, + }, + "error with source code subject and expression referring to sensitive value": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Wrong noises", + Detail: "Biological sounds are not allowed", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 2, Column: 9, Byte: 42}, + End: hcl.Pos{Line: 2, Column: 26, Byte: 59}, + }, + Expression: hcltest.MockExprTraversal(hcl.Traversal{ + hcl.TraverseRoot{Name: "var"}, + hcl.TraverseAttr{Name: "boop"}, + hcl.TraverseIndex{Key: cty.StringVal("hello!")}, + }), + EvalContext: &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "var": cty.ObjectVal(map[string]cty.Value{ + "boop": cty.MapVal(map[string]cty.Value{ + "hello!": cty.StringVal("bleurgh").Mark("sensitive"), + }), + }), + }, + }, + }, + &Diagnostic{ + Severity: "error", + Summary: "Wrong noises", + Detail: "Biological sounds are not allowed", + Range: &DiagnosticRange{ + Filename: "test.tf", + Start: Pos{ + Line: 2, + Column: 9, + Byte: 42, + }, + End: Pos{ + Line: 2, + Column: 26, + Byte: 59, + }, + }, + Snippet: &DiagnosticSnippet{ + Context: strPtr(`resource "test_resource" "test"`), + Code: (` foo = var.boop["hello!"]`), + StartLine: (2), + HighlightStartOffset: (8), + HighlightEndOffset: (25), + Values: []DiagnosticExpressionValue{ + { + Traversal: `var.boop["hello!"]`, + Statement: `has a sensitive value`, + }, + }, + }, + }, + }, + "error with source code subject and expression referring to a collection containing a sensitive value": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Wrong noises", + Detail: "Biological sounds are not allowed", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 2, Column: 9, Byte: 42}, + End: hcl.Pos{Line: 2, Column: 26, Byte: 59}, + }, + Expression: hcltest.MockExprTraversal(hcl.Traversal{ + hcl.TraverseRoot{Name: "var"}, + hcl.TraverseAttr{Name: "boop"}, + }), + EvalContext: &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "var": cty.ObjectVal(map[string]cty.Value{ + "boop": cty.MapVal(map[string]cty.Value{ + "hello!": cty.StringVal("bleurgh").Mark("sensitive"), + }), + }), + }, + }, + }, + &Diagnostic{ + Severity: "error", + Summary: "Wrong noises", + Detail: "Biological sounds are not allowed", + Range: &DiagnosticRange{ + Filename: "test.tf", + Start: Pos{ + Line: 2, + Column: 9, + Byte: 42, + }, + End: Pos{ + Line: 2, + Column: 26, + Byte: 59, + }, + }, + Snippet: &DiagnosticSnippet{ + Context: strPtr(`resource "test_resource" "test"`), + Code: (` foo = var.boop["hello!"]`), + StartLine: (2), + HighlightStartOffset: (8), + HighlightEndOffset: (25), + Values: []DiagnosticExpressionValue{ + { + Traversal: `var.boop`, + Statement: `is map of string with 1 element`, + }, + }, + }, + }, + }, + "error with source code subject and unknown string expression": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Wrong noises", + Detail: "Biological sounds are not allowed", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 2, Column: 9, Byte: 42}, + End: hcl.Pos{Line: 2, Column: 26, Byte: 59}, + }, + Expression: hcltest.MockExprTraversal(hcl.Traversal{ + hcl.TraverseRoot{Name: "var"}, + hcl.TraverseAttr{Name: "boop"}, + hcl.TraverseIndex{Key: cty.StringVal("hello!")}, + }), + EvalContext: &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "var": cty.ObjectVal(map[string]cty.Value{ + "boop": cty.MapVal(map[string]cty.Value{ + "hello!": cty.UnknownVal(cty.String), + }), + }), + }, + }, + }, + &Diagnostic{ + Severity: "error", + Summary: "Wrong noises", + Detail: "Biological sounds are not allowed", + Range: &DiagnosticRange{ + Filename: "test.tf", + Start: Pos{ + Line: 2, + Column: 9, + Byte: 42, + }, + End: Pos{ + Line: 2, + Column: 26, + Byte: 59, + }, + }, + Snippet: &DiagnosticSnippet{ + Context: strPtr(`resource "test_resource" "test"`), + Code: (` foo = var.boop["hello!"]`), + StartLine: (2), + HighlightStartOffset: (8), + HighlightEndOffset: (25), + Values: []DiagnosticExpressionValue{ + { + Traversal: `var.boop["hello!"]`, + Statement: `is a string, known only after apply`, + }, + }, + }, + }, + }, + "error with source code subject and unknown expression of unknown type": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Wrong noises", + Detail: "Biological sounds are not allowed", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 2, Column: 9, Byte: 42}, + End: hcl.Pos{Line: 2, Column: 26, Byte: 59}, + }, + Expression: hcltest.MockExprTraversal(hcl.Traversal{ + hcl.TraverseRoot{Name: "var"}, + hcl.TraverseAttr{Name: "boop"}, + hcl.TraverseIndex{Key: cty.StringVal("hello!")}, + }), + EvalContext: &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "var": cty.ObjectVal(map[string]cty.Value{ + "boop": cty.MapVal(map[string]cty.Value{ + "hello!": cty.UnknownVal(cty.DynamicPseudoType), + }), + }), + }, + }, + }, + &Diagnostic{ + Severity: "error", + Summary: "Wrong noises", + Detail: "Biological sounds are not allowed", + Range: &DiagnosticRange{ + Filename: "test.tf", + Start: Pos{ + Line: 2, + Column: 9, + Byte: 42, + }, + End: Pos{ + Line: 2, + Column: 26, + Byte: 59, + }, + }, + Snippet: &DiagnosticSnippet{ + Context: strPtr(`resource "test_resource" "test"`), + Code: (` foo = var.boop["hello!"]`), + StartLine: (2), + HighlightStartOffset: (8), + HighlightEndOffset: (25), + Values: []DiagnosticExpressionValue{ + { + Traversal: `var.boop["hello!"]`, + Statement: `will be known only after apply`, + }, + }, + }, + }, + }, + "error with source code subject with multiple expression values": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Catastrophic failure", + Detail: "Basically, everything went wrong", + Subject: &hcl.Range{ + Filename: "values.tf", + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 13, Column: 2, Byte: 102}, + }, + Expression: hcltest.MockExprList([]hcl.Expression{ + hcltest.MockExprTraversalSrc("var.a"), + hcltest.MockExprTraversalSrc("var.b"), + hcltest.MockExprTraversalSrc("var.c"), + hcltest.MockExprTraversalSrc("var.d"), + hcltest.MockExprTraversalSrc("var.e"), + hcltest.MockExprTraversalSrc("var.f"), + hcltest.MockExprTraversalSrc("var.g"), + hcltest.MockExprTraversalSrc("var.h"), + hcltest.MockExprTraversalSrc("var.i"), + hcltest.MockExprTraversalSrc("var.j"), + hcltest.MockExprTraversalSrc("var.k"), + }), + EvalContext: &hcl.EvalContext{ + Variables: map[string]cty.Value{ + "var": cty.ObjectVal(map[string]cty.Value{ + "a": cty.True, + "b": cty.NumberFloatVal(123.45), + "c": cty.NullVal(cty.String), + "d": cty.StringVal("secret").Mark("sensitive"), + "e": cty.False, + "f": cty.ListValEmpty(cty.String), + "g": cty.MapVal(map[string]cty.Value{ + "boop": cty.StringVal("beep"), + }), + "h": cty.ListVal([]cty.Value{ + cty.StringVal("boop"), + cty.StringVal("beep"), + cty.StringVal("blorp"), + }), + "i": cty.EmptyObjectVal, + "j": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + }), + "k": cty.ObjectVal(map[string]cty.Value{ + "a": cty.True, + "b": cty.False, + }), + }), + }, + }, + }, + &Diagnostic{ + Severity: "error", + Summary: "Catastrophic failure", + Detail: "Basically, everything went wrong", + Range: &DiagnosticRange{ + Filename: "values.tf", + Start: Pos{ + Line: 1, + Column: 1, + Byte: 0, + }, + End: Pos{ + Line: 13, + Column: 2, + Byte: 102, + }, + }, + Snippet: &DiagnosticSnippet{ + Code: `[ + var.a, + var.b, + var.c, + var.d, + var.e, + var.f, + var.g, + var.h, + var.i, + var.j, + var.k, +]`, + StartLine: (1), + HighlightStartOffset: (0), + HighlightEndOffset: (102), + Values: []DiagnosticExpressionValue{ + { + Traversal: `var.a`, + Statement: `is true`, + }, + { + Traversal: `var.b`, + Statement: `is 123.45`, + }, + { + Traversal: `var.c`, + Statement: `is null`, + }, + { + Traversal: `var.d`, + Statement: `has a sensitive value`, + }, + { + Traversal: `var.e`, + Statement: `is false`, + }, + { + Traversal: `var.f`, + Statement: `is empty list of string`, + }, + { + Traversal: `var.g`, + Statement: `is map of string with 1 element`, + }, + { + Traversal: `var.h`, + Statement: `is list of string with 3 elements`, + }, + { + Traversal: `var.i`, + Statement: `is object with no attributes`, + }, + { + Traversal: `var.j`, + Statement: `is object with 1 attribute "foo"`, + }, + { + Traversal: `var.k`, + Statement: `is object with 2 attributes`, + }, + }, + }, + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + // Convert the diag into a tfdiags.Diagnostic + var diags tfdiags.Diagnostics + diags = diags.Append(tc.diag) + + got := NewDiagnostic(diags[0], sources) + if !cmp.Equal(tc.want, got) { + t.Fatalf("wrong result\n:%s", cmp.Diff(tc.want, got)) + } + }) + + t.Run(fmt.Sprintf("golden test for %s", name), func(t *testing.T) { + // Convert the diag into a tfdiags.Diagnostic + var diags tfdiags.Diagnostics + diags = diags.Append(tc.diag) + + got := NewDiagnostic(diags[0], sources) + + // Render the diagnostic to indented JSON + gotBytes, err := json.MarshalIndent(got, "", " ") + if err != nil { + t.Fatal(err) + } + + // Compare against the golden reference + filename := path.Join( + "testdata", + "diagnostic", + fmt.Sprintf("%s.json", strings.ReplaceAll(name, " ", "-")), + ) + + // Generate golden reference by uncommenting the next two lines: + // gotBytes = append(gotBytes, '\n') + // os.WriteFile(filename, gotBytes, 0644) + + wantFile, err := os.Open(filename) + if err != nil { + t.Fatalf("failed to open golden file: %s", err) + } + defer wantFile.Close() + wantBytes, err := ioutil.ReadAll(wantFile) + if err != nil { + t.Fatalf("failed to read output file: %s", err) + } + + // Don't care about leading or trailing whitespace + gotString := strings.TrimSpace(string(gotBytes)) + wantString := strings.TrimSpace(string(wantBytes)) + + if !cmp.Equal(wantString, gotString) { + t.Fatalf("wrong result\n:%s", cmp.Diff(wantString, gotString)) + } + }) + } +} + +// Helper function to make constructing literal Diagnostics easier. There +// are fields which are pointer-to-string to ensure that the rendered JSON +// results in `null` for an empty value, rather than `""`. +func strPtr(s string) *string { return &s } diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/hook.go b/vendor/github.com/hashicorp/terraform/command/views/json/hook.go new file mode 100644 index 00000000..0e824200 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/hook.go @@ -0,0 +1,364 @@ +package json + +import ( + "fmt" + "time" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/plans" +) + +type Hook interface { + HookType() MessageType + String() string +} + +// ApplyStart: triggered by PreApply hook +type applyStart struct { + Resource ResourceAddr `json:"resource"` + Action ChangeAction `json:"action"` + IDKey string `json:"id_key,omitempty"` + IDValue string `json:"id_value,omitempty"` + actionVerb string +} + +var _ Hook = (*applyStart)(nil) + +func (h *applyStart) HookType() MessageType { + return MessageApplyStart +} + +func (h *applyStart) String() string { + var id string + if h.IDKey != "" && h.IDValue != "" { + id = fmt.Sprintf(" [%s=%s]", h.IDKey, h.IDValue) + } + return fmt.Sprintf("%s: %s...%s", h.Resource.Addr, h.actionVerb, id) +} + +func NewApplyStart(addr addrs.AbsResourceInstance, action plans.Action, idKey string, idValue string) Hook { + hook := &applyStart{ + Resource: newResourceAddr(addr), + Action: changeAction(action), + IDKey: idKey, + IDValue: idValue, + actionVerb: startActionVerb(action), + } + + return hook +} + +// ApplyProgress: currently triggered by a timer started on PreApply. In +// future, this might also be triggered by provider progress reporting. +type applyProgress struct { + Resource ResourceAddr `json:"resource"` + Action ChangeAction `json:"action"` + Elapsed float64 `json:"elapsed_seconds"` + actionVerb string + elapsed time.Duration +} + +var _ Hook = (*applyProgress)(nil) + +func (h *applyProgress) HookType() MessageType { + return MessageApplyProgress +} + +func (h *applyProgress) String() string { + return fmt.Sprintf("%s: Still %s... [%s elapsed]", h.Resource.Addr, h.actionVerb, h.elapsed) +} + +func NewApplyProgress(addr addrs.AbsResourceInstance, action plans.Action, elapsed time.Duration) Hook { + return &applyProgress{ + Resource: newResourceAddr(addr), + Action: changeAction(action), + Elapsed: elapsed.Seconds(), + actionVerb: progressActionVerb(action), + elapsed: elapsed, + } +} + +// ApplyComplete: triggered by PostApply hook +type applyComplete struct { + Resource ResourceAddr `json:"resource"` + Action ChangeAction `json:"action"` + IDKey string `json:"id_key,omitempty"` + IDValue string `json:"id_value,omitempty"` + Elapsed float64 `json:"elapsed_seconds"` + actionNoun string + elapsed time.Duration +} + +var _ Hook = (*applyComplete)(nil) + +func (h *applyComplete) HookType() MessageType { + return MessageApplyComplete +} + +func (h *applyComplete) String() string { + var id string + if h.IDKey != "" && h.IDValue != "" { + id = fmt.Sprintf(" [%s=%s]", h.IDKey, h.IDValue) + } + return fmt.Sprintf("%s: %s complete after %s%s", h.Resource.Addr, h.actionNoun, h.elapsed, id) +} + +func NewApplyComplete(addr addrs.AbsResourceInstance, action plans.Action, idKey, idValue string, elapsed time.Duration) Hook { + return &applyComplete{ + Resource: newResourceAddr(addr), + Action: changeAction(action), + IDKey: idKey, + IDValue: idValue, + Elapsed: elapsed.Seconds(), + actionNoun: actionNoun(action), + elapsed: elapsed, + } +} + +// ApplyErrored: triggered by PostApply hook on failure. This will be followed +// by diagnostics when the apply finishes. +type applyErrored struct { + Resource ResourceAddr `json:"resource"` + Action ChangeAction `json:"action"` + Elapsed float64 `json:"elapsed_seconds"` + actionNoun string + elapsed time.Duration +} + +var _ Hook = (*applyErrored)(nil) + +func (h *applyErrored) HookType() MessageType { + return MessageApplyErrored +} + +func (h *applyErrored) String() string { + return fmt.Sprintf("%s: %s errored after %s", h.Resource.Addr, h.actionNoun, h.elapsed) +} + +func NewApplyErrored(addr addrs.AbsResourceInstance, action plans.Action, elapsed time.Duration) Hook { + return &applyErrored{ + Resource: newResourceAddr(addr), + Action: changeAction(action), + Elapsed: elapsed.Seconds(), + actionNoun: actionNoun(action), + elapsed: elapsed, + } +} + +// ProvisionStart: triggered by PreProvisionInstanceStep hook +type provisionStart struct { + Resource ResourceAddr `json:"resource"` + Provisioner string `json:"provisioner"` +} + +var _ Hook = (*provisionStart)(nil) + +func (h *provisionStart) HookType() MessageType { + return MessageProvisionStart +} + +func (h *provisionStart) String() string { + return fmt.Sprintf("%s: Provisioning with '%s'...", h.Resource.Addr, h.Provisioner) +} + +func NewProvisionStart(addr addrs.AbsResourceInstance, provisioner string) Hook { + return &provisionStart{ + Resource: newResourceAddr(addr), + Provisioner: provisioner, + } +} + +// ProvisionProgress: triggered by ProvisionOutput hook +type provisionProgress struct { + Resource ResourceAddr `json:"resource"` + Provisioner string `json:"provisioner"` + Output string `json:"output"` +} + +var _ Hook = (*provisionProgress)(nil) + +func (h *provisionProgress) HookType() MessageType { + return MessageProvisionProgress +} + +func (h *provisionProgress) String() string { + return fmt.Sprintf("%s: (%s): %s", h.Resource.Addr, h.Provisioner, h.Output) +} + +func NewProvisionProgress(addr addrs.AbsResourceInstance, provisioner string, output string) Hook { + return &provisionProgress{ + Resource: newResourceAddr(addr), + Provisioner: provisioner, + Output: output, + } +} + +// ProvisionComplete: triggered by PostProvisionInstanceStep hook +type provisionComplete struct { + Resource ResourceAddr `json:"resource"` + Provisioner string `json:"provisioner"` +} + +var _ Hook = (*provisionComplete)(nil) + +func (h *provisionComplete) HookType() MessageType { + return MessageProvisionComplete +} + +func (h *provisionComplete) String() string { + return fmt.Sprintf("%s: (%s) Provisioning complete", h.Resource.Addr, h.Provisioner) +} + +func NewProvisionComplete(addr addrs.AbsResourceInstance, provisioner string) Hook { + return &provisionComplete{ + Resource: newResourceAddr(addr), + Provisioner: provisioner, + } +} + +// ProvisionErrored: triggered by PostProvisionInstanceStep hook on failure. +// This will be followed by diagnostics when the apply finishes. +type provisionErrored struct { + Resource ResourceAddr `json:"resource"` + Provisioner string `json:"provisioner"` +} + +var _ Hook = (*provisionErrored)(nil) + +func (h *provisionErrored) HookType() MessageType { + return MessageProvisionErrored +} + +func (h *provisionErrored) String() string { + return fmt.Sprintf("%s: (%s) Provisioning errored", h.Resource.Addr, h.Provisioner) +} + +func NewProvisionErrored(addr addrs.AbsResourceInstance, provisioner string) Hook { + return &provisionErrored{ + Resource: newResourceAddr(addr), + Provisioner: provisioner, + } +} + +// RefreshStart: triggered by PreRefresh hook +type refreshStart struct { + Resource ResourceAddr `json:"resource"` + IDKey string `json:"id_key,omitempty"` + IDValue string `json:"id_value,omitempty"` +} + +var _ Hook = (*refreshStart)(nil) + +func (h *refreshStart) HookType() MessageType { + return MessageRefreshStart +} + +func (h *refreshStart) String() string { + var id string + if h.IDKey != "" && h.IDValue != "" { + id = fmt.Sprintf(" [%s=%s]", h.IDKey, h.IDValue) + } + return fmt.Sprintf("%s: Refreshing state...%s", h.Resource.Addr, id) +} + +func NewRefreshStart(addr addrs.AbsResourceInstance, idKey, idValue string) Hook { + return &refreshStart{ + Resource: newResourceAddr(addr), + IDKey: idKey, + IDValue: idValue, + } +} + +// RefreshComplete: triggered by PostRefresh hook +type refreshComplete struct { + Resource ResourceAddr `json:"resource"` + IDKey string `json:"id_key,omitempty"` + IDValue string `json:"id_value,omitempty"` +} + +var _ Hook = (*refreshComplete)(nil) + +func (h *refreshComplete) HookType() MessageType { + return MessageRefreshComplete +} + +func (h *refreshComplete) String() string { + var id string + if h.IDKey != "" && h.IDValue != "" { + id = fmt.Sprintf(" [%s=%s]", h.IDKey, h.IDValue) + } + return fmt.Sprintf("%s: Refresh complete%s", h.Resource.Addr, id) +} + +func NewRefreshComplete(addr addrs.AbsResourceInstance, idKey, idValue string) Hook { + return &refreshComplete{ + Resource: newResourceAddr(addr), + IDKey: idKey, + IDValue: idValue, + } +} + +// Convert the subset of plans.Action values we expect to receive into a +// present-tense verb for the applyStart hook message. +func startActionVerb(action plans.Action) string { + switch action { + case plans.Create: + return "Creating" + case plans.Update: + return "Modifying" + case plans.Delete: + return "Destroying" + case plans.Read: + return "Refreshing" + case plans.CreateThenDelete, plans.DeleteThenCreate: + // This is not currently possible to reach, as we receive separate + // passes for create and delete + return "Replacing" + default: + return "Applying" + } +} + +// Convert the subset of plans.Action values we expect to receive into a +// present-tense verb for the applyProgress hook message. This will be +// prefixed with "Still ", so it is lower-case. +func progressActionVerb(action plans.Action) string { + switch action { + case plans.Create: + return "creating" + case plans.Update: + return "modifying" + case plans.Delete: + return "destroying" + case plans.Read: + return "refreshing" + case plans.CreateThenDelete, plans.DeleteThenCreate: + // This is not currently possible to reach, as we receive separate + // passes for create and delete + return "replacing" + default: + return "applying" + } +} + +// Convert the subset of plans.Action values we expect to receive into a +// noun for the applyComplete and applyErrored hook messages. This will be +// combined into a phrase like "Creation complete after 1m4s". +func actionNoun(action plans.Action) string { + switch action { + case plans.Create: + return "Creation" + case plans.Update: + return "Modifications" + case plans.Delete: + return "Destruction" + case plans.Read: + return "Refresh" + case plans.CreateThenDelete, plans.DeleteThenCreate: + // This is not currently possible to reach, as we receive separate + // passes for create and delete + return "Replacement" + default: + return "Apply" + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/message_types.go b/vendor/github.com/hashicorp/terraform/command/views/json/message_types.go new file mode 100644 index 00000000..eb10c7b1 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/message_types.go @@ -0,0 +1,27 @@ +package json + +type MessageType string + +const ( + // Generic messages + MessageVersion MessageType = "version" + MessageLog MessageType = "log" + MessageDiagnostic MessageType = "diagnostic" + + // Operation results + MessagePlannedChange MessageType = "planned_change" + MessageChangeSummary MessageType = "change_summary" + MessageOutputs MessageType = "outputs" + + // Hook-driven messages + MessageApplyStart MessageType = "apply_start" + MessageApplyProgress MessageType = "apply_progress" + MessageApplyComplete MessageType = "apply_complete" + MessageApplyErrored MessageType = "apply_errored" + MessageProvisionStart MessageType = "provision_start" + MessageProvisionProgress MessageType = "provision_progress" + MessageProvisionComplete MessageType = "provision_complete" + MessageProvisionErrored MessageType = "provision_errored" + MessageRefreshStart MessageType = "refresh_start" + MessageRefreshComplete MessageType = "refresh_complete" +) diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/output.go b/vendor/github.com/hashicorp/terraform/command/views/json/output.go new file mode 100644 index 00000000..1cb19a43 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/output.go @@ -0,0 +1,55 @@ +package json + +import ( + "encoding/json" + "fmt" + + ctyjson "github.com/zclconf/go-cty/cty/json" + + "github.com/hashicorp/terraform/states" + "github.com/hashicorp/terraform/tfdiags" +) + +type Output struct { + Sensitive bool `json:"sensitive"` + Type json.RawMessage `json:"type"` + Value json.RawMessage `json:"value"` +} + +type Outputs map[string]Output + +func OutputsFromMap(outputValues map[string]*states.OutputValue) (Outputs, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + + outputs := make(map[string]Output, len(outputValues)) + + for name, ov := range outputValues { + unmarked, _ := ov.Value.UnmarkDeep() + value, err := ctyjson.Marshal(unmarked, unmarked.Type()) + if err != nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + fmt.Sprintf("Error serializing output %q", name), + fmt.Sprintf("Error: %s", err), + )) + return nil, diags + } + valueType, err := ctyjson.MarshalType(unmarked.Type()) + if err != nil { + diags = diags.Append(err) + return nil, diags + } + + outputs[name] = Output{ + Sensitive: ov.Sensitive, + Type: json.RawMessage(valueType), + Value: json.RawMessage(value), + } + } + + return outputs, nil +} + +func (o Outputs) String() string { + return fmt.Sprintf("Outputs: %d", len(o)) +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/output_test.go b/vendor/github.com/hashicorp/terraform/command/views/json/output_test.go new file mode 100644 index 00000000..4babfa2c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/output_test.go @@ -0,0 +1,87 @@ +package json + +import ( + "encoding/json" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform/states" + "github.com/zclconf/go-cty/cty" +) + +func TestOutputsFromMap(t *testing.T) { + got, diags := OutputsFromMap(map[string]*states.OutputValue{ + // Normal non-sensitive output + "boop": { + Value: cty.NumberIntVal(1234), + }, + // Sensitive string output + "beep": { + Value: cty.StringVal("horse-battery").Mark("sensitive"), + Sensitive: true, + }, + // Sensitive object output which is marked at the leaf + "blorp": { + Value: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "b": cty.ObjectVal(map[string]cty.Value{ + "c": cty.StringVal("oh, hi").Mark("sensitive"), + }), + }), + }), + Sensitive: true, + }, + // Null value + "honk": { + Value: cty.NullVal(cty.Map(cty.Bool)), + }, + }) + if len(diags) > 0 { + t.Fatal(diags.Err()) + } + + want := Outputs{ + "boop": { + Sensitive: false, + Type: json.RawMessage(`"number"`), + Value: json.RawMessage(`1234`), + }, + "beep": { + Sensitive: true, + Type: json.RawMessage(`"string"`), + Value: json.RawMessage(`"horse-battery"`), + }, + "blorp": { + Sensitive: true, + Type: json.RawMessage(`["object",{"a":["object",{"b":["object",{"c":"string"}]}]}]`), + Value: json.RawMessage(`{"a":{"b":{"c":"oh, hi"}}}`), + }, + "honk": { + Sensitive: false, + Type: json.RawMessage(`["map","bool"]`), + Value: json.RawMessage(`null`), + }, + } + + if !cmp.Equal(want, got) { + t.Fatalf("unexpected result\n%s", cmp.Diff(want, got)) + } +} + +func TestOutputs_String(t *testing.T) { + outputs := Outputs{ + "boop": { + Sensitive: false, + Type: json.RawMessage(`"number"`), + Value: json.RawMessage(`1234`), + }, + "beep": { + Sensitive: true, + Type: json.RawMessage(`"string"`), + Value: json.RawMessage(`"horse-battery"`), + }, + } + if got, want := outputs.String(), "Outputs: 2"; got != want { + t.Fatalf("unexpected value\n got: %q\nwant: %q", got, want) + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/resource_addr.go b/vendor/github.com/hashicorp/terraform/command/views/json/resource_addr.go new file mode 100644 index 00000000..414ce33d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/resource_addr.go @@ -0,0 +1,34 @@ +package json + +import ( + "github.com/zclconf/go-cty/cty" + ctyjson "github.com/zclconf/go-cty/cty/json" + + "github.com/hashicorp/terraform/addrs" +) + +type ResourceAddr struct { + Addr string `json:"addr"` + Module string `json:"module"` + Resource string `json:"resource"` + ImpliedProvider string `json:"implied_provider"` + ResourceType string `json:"resource_type"` + ResourceName string `json:"resource_name"` + ResourceKey ctyjson.SimpleJSONValue `json:"resource_key"` +} + +func newResourceAddr(addr addrs.AbsResourceInstance) ResourceAddr { + resourceKey := ctyjson.SimpleJSONValue{Value: cty.NilVal} + if addr.Resource.Key != nil { + resourceKey.Value = addr.Resource.Key.Value() + } + return ResourceAddr{ + Addr: addr.String(), + Module: addr.Module.String(), + Resource: addr.Resource.String(), + ImpliedProvider: addr.Resource.Resource.ImpliedProvider(), + ResourceType: addr.Resource.Resource.Type, + ResourceName: addr.Resource.Resource.Name, + ResourceKey: resourceKey, + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-empty-highlight-range-at-end-of-source-code.json b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-empty-highlight-range-at-end-of-source-code.json new file mode 100644 index 00000000..e8cd0099 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-empty-highlight-range-at-end-of-source-code.json @@ -0,0 +1,26 @@ +{ + "severity": "error", + "summary": "You forgot something", + "detail": "Please finish your thought", + "range": { + "filename": "short.tf", + "start": { + "line": 1, + "column": 16, + "byte": 15 + }, + "end": { + "line": 1, + "column": 17, + "byte": 16 + } + }, + "snippet": { + "context": null, + "code": "bad source code", + "start_line": 1, + "highlight_start_offset": 15, + "highlight_end_offset": 15, + "values": [] + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-multi-line-snippet.json b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-multi-line-snippet.json new file mode 100644 index 00000000..5acb67d0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-multi-line-snippet.json @@ -0,0 +1,26 @@ +{ + "severity": "error", + "summary": "In this house we respect booleans", + "detail": "True or false, there is no maybe", + "range": { + "filename": "test.tf", + "start": { + "line": 4, + "column": 11, + "byte": 81 + }, + "end": { + "line": 4, + "column": 16, + "byte": 86 + } + }, + "snippet": { + "context": "resource \"test_resource\" \"test\"", + "code": " bar = {\n baz = maybe\n }", + "start_line": 3, + "highlight_start_offset": 20, + "highlight_end_offset": 25, + "values": [] + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-expression-referring-to-a-collection-containing-a-sensitive-value.json b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-expression-referring-to-a-collection-containing-a-sensitive-value.json new file mode 100644 index 00000000..e6599faf --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-expression-referring-to-a-collection-containing-a-sensitive-value.json @@ -0,0 +1,31 @@ +{ + "severity": "error", + "summary": "Wrong noises", + "detail": "Biological sounds are not allowed", + "range": { + "filename": "test.tf", + "start": { + "line": 2, + "column": 9, + "byte": 42 + }, + "end": { + "line": 2, + "column": 26, + "byte": 59 + } + }, + "snippet": { + "context": "resource \"test_resource\" \"test\"", + "code": " foo = var.boop[\"hello!\"]", + "start_line": 2, + "highlight_start_offset": 8, + "highlight_end_offset": 25, + "values": [ + { + "traversal": "var.boop", + "statement": "is map of string with 1 element" + } + ] + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-expression-referring-to-sensitive-value.json b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-expression-referring-to-sensitive-value.json new file mode 100644 index 00000000..abffcdb3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-expression-referring-to-sensitive-value.json @@ -0,0 +1,31 @@ +{ + "severity": "error", + "summary": "Wrong noises", + "detail": "Biological sounds are not allowed", + "range": { + "filename": "test.tf", + "start": { + "line": 2, + "column": 9, + "byte": 42 + }, + "end": { + "line": 2, + "column": 26, + "byte": 59 + } + }, + "snippet": { + "context": "resource \"test_resource\" \"test\"", + "code": " foo = var.boop[\"hello!\"]", + "start_line": 2, + "highlight_start_offset": 8, + "highlight_end_offset": 25, + "values": [ + { + "traversal": "var.boop[\"hello!\"]", + "statement": "has a sensitive value" + } + ] + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-known-expression.json b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-known-expression.json new file mode 100644 index 00000000..dde961cd --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-known-expression.json @@ -0,0 +1,31 @@ +{ + "severity": "error", + "summary": "Wrong noises", + "detail": "Biological sounds are not allowed", + "range": { + "filename": "test.tf", + "start": { + "line": 2, + "column": 9, + "byte": 42 + }, + "end": { + "line": 2, + "column": 26, + "byte": 59 + } + }, + "snippet": { + "context": "resource \"test_resource\" \"test\"", + "code": " foo = var.boop[\"hello!\"]", + "start_line": 2, + "highlight_start_offset": 8, + "highlight_end_offset": 25, + "values": [ + { + "traversal": "var.boop[\"hello!\"]", + "statement": "is \"bleurgh\"" + } + ] + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-unknown-expression-of-unknown-type.json b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-unknown-expression-of-unknown-type.json new file mode 100644 index 00000000..d8aa3c4c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-unknown-expression-of-unknown-type.json @@ -0,0 +1,31 @@ +{ + "severity": "error", + "summary": "Wrong noises", + "detail": "Biological sounds are not allowed", + "range": { + "filename": "test.tf", + "start": { + "line": 2, + "column": 9, + "byte": 42 + }, + "end": { + "line": 2, + "column": 26, + "byte": 59 + } + }, + "snippet": { + "context": "resource \"test_resource\" \"test\"", + "code": " foo = var.boop[\"hello!\"]", + "start_line": 2, + "highlight_start_offset": 8, + "highlight_end_offset": 25, + "values": [ + { + "traversal": "var.boop[\"hello!\"]", + "statement": "will be known only after apply" + } + ] + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-unknown-string-expression.json b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-unknown-string-expression.json new file mode 100644 index 00000000..8255af8f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-and-unknown-string-expression.json @@ -0,0 +1,31 @@ +{ + "severity": "error", + "summary": "Wrong noises", + "detail": "Biological sounds are not allowed", + "range": { + "filename": "test.tf", + "start": { + "line": 2, + "column": 9, + "byte": 42 + }, + "end": { + "line": 2, + "column": 26, + "byte": 59 + } + }, + "snippet": { + "context": "resource \"test_resource\" \"test\"", + "code": " foo = var.boop[\"hello!\"]", + "start_line": 2, + "highlight_start_offset": 8, + "highlight_end_offset": 25, + "values": [ + { + "traversal": "var.boop[\"hello!\"]", + "statement": "is a string, known only after apply" + } + ] + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-but-no-context.json b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-but-no-context.json new file mode 100644 index 00000000..9cfafbc4 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-but-no-context.json @@ -0,0 +1,26 @@ +{ + "severity": "error", + "summary": "Nonsense input", + "detail": "What you wrote makes no sense", + "range": { + "filename": "short.tf", + "start": { + "line": 1, + "column": 5, + "byte": 4 + }, + "end": { + "line": 1, + "column": 10, + "byte": 9 + } + }, + "snippet": { + "context": null, + "code": "bad source code", + "start_line": 1, + "highlight_start_offset": 4, + "highlight_end_offset": 9, + "values": [] + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-with-multiple-expression-values.json b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-with-multiple-expression-values.json new file mode 100644 index 00000000..b88e4905 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject-with-multiple-expression-values.json @@ -0,0 +1,71 @@ +{ + "severity": "error", + "summary": "Catastrophic failure", + "detail": "Basically, everything went wrong", + "range": { + "filename": "values.tf", + "start": { + "line": 1, + "column": 1, + "byte": 0 + }, + "end": { + "line": 13, + "column": 2, + "byte": 102 + } + }, + "snippet": { + "context": null, + "code": "[\n var.a,\n var.b,\n var.c,\n var.d,\n var.e,\n var.f,\n var.g,\n var.h,\n var.i,\n var.j,\n var.k,\n]", + "start_line": 1, + "highlight_start_offset": 0, + "highlight_end_offset": 102, + "values": [ + { + "traversal": "var.a", + "statement": "is true" + }, + { + "traversal": "var.b", + "statement": "is 123.45" + }, + { + "traversal": "var.c", + "statement": "is null" + }, + { + "traversal": "var.d", + "statement": "has a sensitive value" + }, + { + "traversal": "var.e", + "statement": "is false" + }, + { + "traversal": "var.f", + "statement": "is empty list of string" + }, + { + "traversal": "var.g", + "statement": "is map of string with 1 element" + }, + { + "traversal": "var.h", + "statement": "is list of string with 3 elements" + }, + { + "traversal": "var.i", + "statement": "is object with no attributes" + }, + { + "traversal": "var.j", + "statement": "is object with 1 attribute \"foo\"" + }, + { + "traversal": "var.k", + "statement": "is object with 2 attributes" + } + ] + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject.json b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject.json new file mode 100644 index 00000000..762d811d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-subject.json @@ -0,0 +1,26 @@ +{ + "severity": "error", + "summary": "Tiny explosion", + "detail": "Unexpected detonation while parsing", + "range": { + "filename": "test.tf", + "start": { + "line": 1, + "column": 10, + "byte": 9 + }, + "end": { + "line": 1, + "column": 25, + "byte": 24 + } + }, + "snippet": { + "context": "resource \"test_resource\" \"test\"", + "code": "resource \"test_resource\" \"test\" {", + "start_line": 1, + "highlight_start_offset": 9, + "highlight_end_offset": 24, + "values": [] + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-unavailable.json b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-unavailable.json new file mode 100644 index 00000000..2646f474 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-source-code-unavailable.json @@ -0,0 +1,18 @@ +{ + "severity": "error", + "summary": "Bad news", + "detail": "It went wrong", + "range": { + "filename": "modules/oops/missing.tf", + "start": { + "line": 1, + "column": 6, + "byte": 5 + }, + "end": { + "line": 2, + "column": 12, + "byte": 33 + } + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-unset-highlight-end-position.json b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-unset-highlight-end-position.json new file mode 100644 index 00000000..1f7351f0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/error-with-unset-highlight-end-position.json @@ -0,0 +1,26 @@ +{ + "severity": "error", + "summary": "There is no end", + "detail": "But there is a beginning", + "range": { + "filename": "test.tf", + "start": { + "line": 1, + "column": 16, + "byte": 15 + }, + "end": { + "line": 1, + "column": 17, + "byte": 16 + } + }, + "snippet": { + "context": "resource \"test_resource\" \"test\"", + "code": "resource \"test_resource\" \"test\" {", + "start_line": 1, + "highlight_start_offset": 15, + "highlight_end_offset": 16, + "values": [] + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/sourceless-warning.json b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/sourceless-warning.json new file mode 100644 index 00000000..56e17185 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json/testdata/diagnostic/sourceless-warning.json @@ -0,0 +1,5 @@ +{ + "severity": "warning", + "summary": "Oh no", + "detail": "Something is broken" +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json_view.go b/vendor/github.com/hashicorp/terraform/command/views/json_view.go new file mode 100644 index 00000000..fdb851c3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json_view.go @@ -0,0 +1,120 @@ +package views + +import ( + encJson "encoding/json" + "fmt" + + "github.com/hashicorp/go-hclog" + "github.com/hashicorp/terraform/command/views/json" + "github.com/hashicorp/terraform/tfdiags" + tfversion "github.com/hashicorp/terraform/version" +) + +// This version describes the schema of JSON UI messages. This version must be +// updated after making any changes to this view, the jsonHook, or any of the +// command/views/json package. +const JSON_UI_VERSION = "0.1.0" + +func NewJSONView(view *View) *JSONView { + log := hclog.New(&hclog.LoggerOptions{ + Name: "terraform.ui", + Output: view.streams.Stdout.File, + JSONFormat: true, + }) + jv := &JSONView{ + log: log, + view: view, + } + jv.Version() + return jv +} + +type JSONView struct { + // hclog is used for all output in JSON UI mode. The logger has an internal + // mutex to ensure that messages are not interleaved. + log hclog.Logger + + // We hold a reference to the view entirely to allow us to access the + // ConfigSources function pointer, in order to render source snippets into + // diagnostics. This is even more unfortunate than the same reference in the + // view. + // + // Do not be tempted to dereference the configSource value upon logger init, + // as it will likely be updated later. + view *View +} + +func (v *JSONView) Version() { + version := tfversion.String() + v.log.Info( + fmt.Sprintf("Terraform %s", version), + "type", json.MessageVersion, + "terraform", version, + "ui", JSON_UI_VERSION, + ) +} + +func (v *JSONView) Log(message string) { + v.log.Info(message, "type", json.MessageLog) +} + +func (v *JSONView) StateDump(state string) { + v.log.Info( + "Emergency state dump", + "type", json.MessageLog, + "state", encJson.RawMessage(state), + ) +} + +func (v *JSONView) Diagnostics(diags tfdiags.Diagnostics) { + sources := v.view.configSources() + for _, diag := range diags { + diagnostic := json.NewDiagnostic(diag, sources) + switch diag.Severity() { + case tfdiags.Warning: + v.log.Warn( + fmt.Sprintf("Warning: %s", diag.Description().Summary), + "type", json.MessageDiagnostic, + "diagnostic", diagnostic, + ) + default: + v.log.Error( + fmt.Sprintf("Error: %s", diag.Description().Summary), + "type", json.MessageDiagnostic, + "diagnostic", diagnostic, + ) + } + } +} + +func (v *JSONView) PlannedChange(c *json.ResourceInstanceChange) { + v.log.Info( + c.String(), + "type", json.MessagePlannedChange, + "change", c, + ) +} + +func (v *JSONView) ChangeSummary(cs *json.ChangeSummary) { + v.log.Info( + cs.String(), + "type", json.MessageChangeSummary, + "changes", cs, + ) +} + +func (v *JSONView) Hook(h json.Hook) { + v.log.Info( + h.String(), + "type", h.HookType(), + "hook", h, + ) +} + +func (v *JSONView) Outputs(outputs json.Outputs) { + v.log.Info( + outputs.String(), + "type", json.MessageOutputs, + "outputs", outputs, + ) +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/json_view_test.go b/vendor/github.com/hashicorp/terraform/command/views/json_view_test.go new file mode 100644 index 00000000..9aa4c692 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/json_view_test.go @@ -0,0 +1,307 @@ +package views + +import ( + "encoding/json" + "fmt" + "strings" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform/addrs" + viewsjson "github.com/hashicorp/terraform/command/views/json" + "github.com/hashicorp/terraform/internal/terminal" + "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/tfdiags" + tfversion "github.com/hashicorp/terraform/version" +) + +// Calling NewJSONView should also always output a version message, which is a +// convenient way to test that NewJSONView works. +func TestNewJSONView(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + NewJSONView(NewView(streams)) + + version := tfversion.String() + want := []map[string]interface{}{ + { + "@level": "info", + "@message": fmt.Sprintf("Terraform %s", version), + "@module": "terraform.ui", + "type": "version", + "terraform": version, + "ui": JSON_UI_VERSION, + }, + } + + testJSONViewOutputEqualsFull(t, done(t).Stdout(), want) +} + +func TestJSONView_Log(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + jv := NewJSONView(NewView(streams)) + + jv.Log("hello, world") + + want := []map[string]interface{}{ + { + "@level": "info", + "@message": "hello, world", + "@module": "terraform.ui", + "type": "log", + }, + } + testJSONViewOutputEquals(t, done(t).Stdout(), want) +} + +// This test covers only the basics of JSON diagnostic rendering, as more +// complex diagnostics are tested elsewhere. +func TestJSONView_Diagnostics(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + jv := NewJSONView(NewView(streams)) + + var diags tfdiags.Diagnostics + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Warning, + `Improper use of "less"`, + `You probably mean "10 buckets or fewer"`, + )) + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Unusually stripey cat detected", + "Are you sure this random_pet isn't a cheetah?", + )) + + jv.Diagnostics(diags) + + want := []map[string]interface{}{ + { + "@level": "warn", + "@message": `Warning: Improper use of "less"`, + "@module": "terraform.ui", + "type": "diagnostic", + "diagnostic": map[string]interface{}{ + "severity": "warning", + "summary": `Improper use of "less"`, + "detail": `You probably mean "10 buckets or fewer"`, + }, + }, + { + "@level": "error", + "@message": "Error: Unusually stripey cat detected", + "@module": "terraform.ui", + "type": "diagnostic", + "diagnostic": map[string]interface{}{ + "severity": "error", + "summary": "Unusually stripey cat detected", + "detail": "Are you sure this random_pet isn't a cheetah?", + }, + }, + } + testJSONViewOutputEquals(t, done(t).Stdout(), want) +} + +func TestJSONView_PlannedChange(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + jv := NewJSONView(NewView(streams)) + + foo, diags := addrs.ParseModuleInstanceStr("module.foo") + if len(diags) > 0 { + t.Fatal(diags.Err()) + } + managed := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_instance", Name: "bar"} + cs := &plans.ResourceInstanceChangeSrc{ + Addr: managed.Instance(addrs.StringKey("boop")).Absolute(foo), + ChangeSrc: plans.ChangeSrc{ + Action: plans.Create, + }, + } + jv.PlannedChange(viewsjson.NewResourceInstanceChange(cs)) + + want := []map[string]interface{}{ + { + "@level": "info", + "@message": `module.foo.test_instance.bar["boop"]: Plan to create`, + "@module": "terraform.ui", + "type": "planned_change", + "change": map[string]interface{}{ + "action": "create", + "resource": map[string]interface{}{ + "addr": `module.foo.test_instance.bar["boop"]`, + "implied_provider": "test", + "module": "module.foo", + "resource": `test_instance.bar["boop"]`, + "resource_key": "boop", + "resource_name": "bar", + "resource_type": "test_instance", + }, + }, + }, + } + testJSONViewOutputEquals(t, done(t).Stdout(), want) +} + +func TestJSONView_ChangeSummary(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + jv := NewJSONView(NewView(streams)) + + jv.ChangeSummary(&viewsjson.ChangeSummary{ + Add: 1, + Change: 2, + Remove: 3, + Operation: viewsjson.OperationApplied, + }) + + want := []map[string]interface{}{ + { + "@level": "info", + "@message": "Apply complete! Resources: 1 added, 2 changed, 3 destroyed.", + "@module": "terraform.ui", + "type": "change_summary", + "changes": map[string]interface{}{ + "add": float64(1), + "change": float64(2), + "remove": float64(3), + "operation": "apply", + }, + }, + } + testJSONViewOutputEquals(t, done(t).Stdout(), want) +} + +func TestJSONView_Hook(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + jv := NewJSONView(NewView(streams)) + + foo, diags := addrs.ParseModuleInstanceStr("module.foo") + if len(diags) > 0 { + t.Fatal(diags.Err()) + } + managed := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_instance", Name: "bar"} + addr := managed.Instance(addrs.StringKey("boop")).Absolute(foo) + hook := viewsjson.NewApplyComplete(addr, plans.Create, "id", "boop-beep", 34*time.Second) + + jv.Hook(hook) + + want := []map[string]interface{}{ + { + "@level": "info", + "@message": `module.foo.test_instance.bar["boop"]: Creation complete after 34s [id=boop-beep]`, + "@module": "terraform.ui", + "type": "apply_complete", + "hook": map[string]interface{}{ + "resource": map[string]interface{}{ + "addr": `module.foo.test_instance.bar["boop"]`, + "implied_provider": "test", + "module": "module.foo", + "resource": `test_instance.bar["boop"]`, + "resource_key": "boop", + "resource_name": "bar", + "resource_type": "test_instance", + }, + "action": "create", + "id_key": "id", + "id_value": "boop-beep", + "elapsed_seconds": float64(34), + }, + }, + } + testJSONViewOutputEquals(t, done(t).Stdout(), want) +} + +func TestJSONView_Outputs(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + jv := NewJSONView(NewView(streams)) + + jv.Outputs(viewsjson.Outputs{ + "boop_count": { + Sensitive: false, + Value: json.RawMessage(`92`), + Type: json.RawMessage(`"number"`), + }, + "password": { + Sensitive: true, + Value: json.RawMessage(`"horse-battery"`), + Type: json.RawMessage(`"string"`), + }, + }) + + want := []map[string]interface{}{ + { + "@level": "info", + "@message": "Outputs: 2", + "@module": "terraform.ui", + "type": "outputs", + "outputs": map[string]interface{}{ + "boop_count": map[string]interface{}{ + "sensitive": false, + "value": float64(92), + "type": "number", + }, + "password": map[string]interface{}{ + "sensitive": true, + "value": "horse-battery", + "type": "string", + }, + }, + }, + } + testJSONViewOutputEquals(t, done(t).Stdout(), want) +} + +// This helper function tests a possibly multi-line JSONView output string +// against a slice of structs representing the desired log messages. It +// verifies that the output of JSONView is in JSON log format, one message per +// line. +func testJSONViewOutputEqualsFull(t *testing.T, output string, want []map[string]interface{}) { + t.Helper() + + // Remove final trailing newline + output = strings.TrimSuffix(output, "\n") + + // Split log into lines, each of which should be a JSON log message + gotLines := strings.Split(output, "\n") + + if len(gotLines) != len(want) { + t.Fatalf("unexpected number of messages. got %d, want %d", len(gotLines), len(want)) + } + + // Unmarshal each line and compare to the expected value + for i := range gotLines { + var gotStruct map[string]interface{} + wantStruct := want[i] + + if err := json.Unmarshal([]byte(gotLines[i]), &gotStruct); err != nil { + t.Fatal(err) + } + + if timestamp, ok := gotStruct["@timestamp"]; !ok { + t.Errorf("message has no timestamp: %#v", gotStruct) + } else { + // Remove the timestamp value from the struct to allow comparison + delete(gotStruct, "@timestamp") + + // Verify the timestamp format + if _, err := time.Parse("2006-01-02T15:04:05.000000Z07:00", timestamp.(string)); err != nil { + t.Fatalf("error parsing timestamp: %s", err) + } + } + + if !cmp.Equal(wantStruct, gotStruct) { + t.Fatalf("unexpected output on line %d:\n%s", i, cmp.Diff(wantStruct, gotStruct)) + } + } +} + +// testJSONViewOutputEquals skips the first line of output, since it ought to +// be a version message that we don't care about for most of our tests. +func testJSONViewOutputEquals(t *testing.T, output string, want []map[string]interface{}) { + t.Helper() + + // Remove up to the first newline + index := strings.Index(output, "\n") + if index >= 0 { + output = output[index+1:] + } + testJSONViewOutputEqualsFull(t, output, want) +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/operation.go b/vendor/github.com/hashicorp/terraform/command/views/operation.go new file mode 100644 index 00000000..2f3307c8 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/operation.go @@ -0,0 +1,247 @@ +package views + +import ( + "bytes" + "fmt" + "strings" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/format" + "github.com/hashicorp/terraform/command/views/json" + "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/states/statefile" + "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/tfdiags" +) + +type Operation interface { + Interrupted() + FatalInterrupt() + Stopping() + Cancelled(planMode plans.Mode) + + EmergencyDumpState(stateFile *statefile.File) error + + PlannedChange(change *plans.ResourceInstanceChangeSrc) + PlanNoChanges() + Plan(plan *plans.Plan, schemas *terraform.Schemas) + PlanNextStep(planPath string) + + Diagnostics(diags tfdiags.Diagnostics) +} + +func NewOperation(vt arguments.ViewType, inAutomation bool, view *View) Operation { + switch vt { + case arguments.ViewHuman: + return &OperationHuman{view: view, inAutomation: inAutomation} + default: + panic(fmt.Sprintf("unknown view type %v", vt)) + } +} + +type OperationHuman struct { + view *View + + // inAutomation indicates that commands are being run by an + // automated system rather than directly at a command prompt. + // + // This is a hint not to produce messages that expect that a user can + // run a follow-up command, perhaps because Terraform is running in + // some sort of workflow automation tool that abstracts away the + // exact commands that are being run. + inAutomation bool +} + +var _ Operation = (*OperationHuman)(nil) + +func (v *OperationHuman) Interrupted() { + v.view.streams.Println(format.WordWrap(interrupted, v.view.outputColumns())) +} + +func (v *OperationHuman) FatalInterrupt() { + v.view.streams.Eprintln(format.WordWrap(fatalInterrupt, v.view.errorColumns())) +} + +func (v *OperationHuman) Stopping() { + v.view.streams.Println("Stopping operation...") +} + +func (v *OperationHuman) Cancelled(planMode plans.Mode) { + switch planMode { + case plans.DestroyMode: + v.view.streams.Println("Destroy cancelled.") + default: + v.view.streams.Println("Apply cancelled.") + } +} + +func (v *OperationHuman) EmergencyDumpState(stateFile *statefile.File) error { + stateBuf := new(bytes.Buffer) + jsonErr := statefile.Write(stateFile, stateBuf) + if jsonErr != nil { + return jsonErr + } + v.view.streams.Eprintln(stateBuf) + return nil +} + +func (v *OperationHuman) PlanNoChanges() { + v.view.streams.Println("\n" + v.view.colorize.Color(strings.TrimSpace(planNoChanges))) + v.view.streams.Println("\n" + strings.TrimSpace(format.WordWrap(planNoChangesDetail, v.view.outputColumns()))) +} + +func (v *OperationHuman) Plan(plan *plans.Plan, schemas *terraform.Schemas) { + renderPlan(plan, schemas, v.view) +} + +func (v *OperationHuman) PlannedChange(change *plans.ResourceInstanceChangeSrc) { +} + +// PlanNextStep gives the user some next-steps, unless we're running in an +// automation tool which is presumed to provide its own UI for further actions. +func (v *OperationHuman) PlanNextStep(planPath string) { + if v.inAutomation { + return + } + v.view.outputHorizRule() + + if planPath == "" { + v.view.streams.Print( + "\n" + strings.TrimSpace(format.WordWrap(planHeaderNoOutput, v.view.outputColumns())) + "\n", + ) + } else { + v.view.streams.Printf( + "\n"+strings.TrimSpace(format.WordWrap(planHeaderYesOutput, v.view.outputColumns()))+"\n", + planPath, planPath, + ) + } +} + +func (v *OperationHuman) Diagnostics(diags tfdiags.Diagnostics) { + v.view.Diagnostics(diags) +} + +type OperationJSON struct { + view *JSONView +} + +var _ Operation = (*OperationJSON)(nil) + +func (v *OperationJSON) Interrupted() { + v.view.Log(interrupted) +} + +func (v *OperationJSON) FatalInterrupt() { + v.view.Log(fatalInterrupt) +} + +func (v *OperationJSON) Stopping() { + v.view.Log("Stopping operation...") +} + +func (v *OperationJSON) Cancelled(planMode plans.Mode) { + switch planMode { + case plans.DestroyMode: + v.view.Log("Destroy cancelled") + default: + v.view.Log("Apply cancelled") + } +} + +func (v *OperationJSON) EmergencyDumpState(stateFile *statefile.File) error { + stateBuf := new(bytes.Buffer) + jsonErr := statefile.Write(stateFile, stateBuf) + if jsonErr != nil { + return jsonErr + } + v.view.StateDump(stateBuf.String()) + return nil +} + +// Log an empty change summary. +func (v *OperationJSON) PlanNoChanges() { + v.view.ChangeSummary(&json.ChangeSummary{ + Add: 0, + Change: 0, + Remove: 0, + Operation: json.OperationPlanned, + }) +} + +// Log a change summary and a series of "planned" messages for the changes in +// the plan. +func (v *OperationJSON) Plan(plan *plans.Plan, schemas *terraform.Schemas) { + cs := &json.ChangeSummary{ + Operation: json.OperationPlanned, + } + for _, change := range plan.Changes.Resources { + if change.Action == plans.Delete && change.Addr.Resource.Resource.Mode == addrs.DataResourceMode { + // Avoid rendering data sources on deletion + continue + } + switch change.Action { + case plans.Create: + cs.Add++ + case plans.Delete: + cs.Remove++ + case plans.Update: + cs.Change++ + case plans.CreateThenDelete, plans.DeleteThenCreate: + cs.Add++ + cs.Remove++ + } + + if change.Action != plans.NoOp { + v.view.PlannedChange(json.NewResourceInstanceChange(change)) + } + } + + v.view.ChangeSummary(cs) +} + +func (v *OperationJSON) PlannedChange(change *plans.ResourceInstanceChangeSrc) { + if change.Action == plans.Delete && change.Addr.Resource.Resource.Mode == addrs.DataResourceMode { + // Avoid rendering data sources on deletion + return + } + v.view.PlannedChange(json.NewResourceInstanceChange(change)) +} + +// PlanNextStep does nothing for the JSON view as it is a hook for user-facing +// output only applicable to human-readable UI. +func (v *OperationJSON) PlanNextStep(planPath string) { +} + +func (v *OperationJSON) Diagnostics(diags tfdiags.Diagnostics) { + v.view.Diagnostics(diags) +} + +const fatalInterrupt = ` +Two interrupts received. Exiting immediately. Note that data loss may have occurred. +` + +const interrupted = ` +Interrupt received. +Please wait for Terraform to exit or data loss may occur. +Gracefully shutting down... +` + +const planNoChanges = ` +[reset][bold][green]No changes. Infrastructure is up-to-date.[reset][green] +` + +const planNoChangesDetail = ` +This means that Terraform did not detect any differences between your configuration and the remote system(s). As a result, there are no actions to take. +` + +const planHeaderNoOutput = ` +Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now. +` + +const planHeaderYesOutput = ` +Saved the plan to: %s + +To perform exactly these actions, run the following command to apply: + terraform apply %q +` diff --git a/vendor/github.com/hashicorp/terraform/command/views/operation_test.go b/vendor/github.com/hashicorp/terraform/command/views/operation_test.go new file mode 100644 index 00000000..d2e74dee --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/operation_test.go @@ -0,0 +1,495 @@ +package views + +import ( + "bytes" + "encoding/json" + "strings" + "testing" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/internal/terminal" + "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/states/statefile" +) + +func TestOperation_stopping(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewOperation(arguments.ViewHuman, false, NewView(streams)) + + v.Stopping() + + if got, want := done(t).Stdout(), "Stopping operation...\n"; got != want { + t.Errorf("wrong result\ngot: %q\nwant: %q", got, want) + } +} + +func TestOperation_cancelled(t *testing.T) { + testCases := map[string]struct { + planMode plans.Mode + want string + }{ + "apply": { + planMode: plans.NormalMode, + want: "Apply cancelled.\n", + }, + "destroy": { + planMode: plans.DestroyMode, + want: "Destroy cancelled.\n", + }, + } + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewOperation(arguments.ViewHuman, false, NewView(streams)) + + v.Cancelled(tc.planMode) + + if got, want := done(t).Stdout(), tc.want; got != want { + t.Errorf("wrong result\ngot: %q\nwant: %q", got, want) + } + }) + } +} + +func TestOperation_emergencyDumpState(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewOperation(arguments.ViewHuman, false, NewView(streams)) + + stateFile := statefile.New(nil, "foo", 1) + + err := v.EmergencyDumpState(stateFile) + if err != nil { + t.Fatalf("unexpected error dumping state: %s", err) + } + + // Check that the result (on stderr) looks like JSON state + raw := done(t).Stderr() + var state map[string]interface{} + if err := json.Unmarshal([]byte(raw), &state); err != nil { + t.Fatalf("unexpected error parsing dumped state: %s\nraw:\n%s", err, raw) + } +} + +func TestOperation_planNoChanges(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewOperation(arguments.ViewHuman, false, NewView(streams)) + + v.PlanNoChanges() + + if got, want := done(t).Stdout(), "No changes. Infrastructure is up-to-date."; !strings.Contains(got, want) { + t.Errorf("wrong result\ngot: %q\nwant: %q", got, want) + } +} + +func TestOperation_plan(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewOperation(arguments.ViewHuman, true, NewView(streams)) + + plan := testPlan(t) + schemas := testSchemas() + v.Plan(plan, schemas) + + want := ` +Terraform used the selected providers to generate the following execution +plan. Resource actions are indicated with the following symbols: + + create + +Terraform will perform the following actions: + + # test_resource.foo will be created + + resource "test_resource" "foo" { + + foo = "bar" + + id = (known after apply) + } + +Plan: 1 to add, 0 to change, 0 to destroy. +` + + if got := done(t).Stdout(); got != want { + t.Errorf("unexpected output\ngot:\n%s\nwant:\n%s", got, want) + } +} + +func TestOperation_planNextStep(t *testing.T) { + testCases := map[string]struct { + path string + want string + }{ + "no state path": { + path: "", + want: "You didn't use the -out option", + }, + "state path": { + path: "good plan.tfplan", + want: `terraform apply "good plan.tfplan"`, + }, + } + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewOperation(arguments.ViewHuman, false, NewView(streams)) + + v.PlanNextStep(tc.path) + + if got := done(t).Stdout(); !strings.Contains(got, tc.want) { + t.Errorf("wrong result\ngot: %q\nwant: %q", got, tc.want) + } + }) + } +} + +// The in-automation state is on the view itself, so testing it separately is +// clearer. +func TestOperation_planNextStepInAutomation(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewOperation(arguments.ViewHuman, true, NewView(streams)) + + v.PlanNextStep("") + + if got := done(t).Stdout(); got != "" { + t.Errorf("unexpected output\ngot: %q", got) + } +} + +// Test all the trivial OperationJSON methods together. Y'know, for brevity. +// This test is not a realistic stream of messages. +func TestOperationJSON_logs(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := &OperationJSON{view: NewJSONView(NewView(streams))} + + v.Cancelled(plans.NormalMode) + v.Cancelled(plans.DestroyMode) + v.Stopping() + v.Interrupted() + v.FatalInterrupt() + + want := []map[string]interface{}{ + { + "@level": "info", + "@message": "Apply cancelled", + "@module": "terraform.ui", + "type": "log", + }, + { + "@level": "info", + "@message": "Destroy cancelled", + "@module": "terraform.ui", + "type": "log", + }, + { + "@level": "info", + "@message": "Stopping operation...", + "@module": "terraform.ui", + "type": "log", + }, + { + "@level": "info", + "@message": interrupted, + "@module": "terraform.ui", + "type": "log", + }, + { + "@level": "info", + "@message": fatalInterrupt, + "@module": "terraform.ui", + "type": "log", + }, + } + + testJSONViewOutputEquals(t, done(t).Stdout(), want) +} + +// This is a fairly circular test, but it's such a rarely executed code path +// that I think it's probably still worth having. We're not testing against +// a fixed state JSON output because this test ought not fail just because +// we upgrade state format in the future. +func TestOperationJSON_emergencyDumpState(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := &OperationJSON{view: NewJSONView(NewView(streams))} + + stateFile := statefile.New(nil, "foo", 1) + stateBuf := new(bytes.Buffer) + err := statefile.Write(stateFile, stateBuf) + if err != nil { + t.Fatal(err) + } + var stateJSON map[string]interface{} + err = json.Unmarshal(stateBuf.Bytes(), &stateJSON) + if err != nil { + t.Fatal(err) + } + + err = v.EmergencyDumpState(stateFile) + if err != nil { + t.Fatalf("unexpected error dumping state: %s", err) + } + + want := []map[string]interface{}{ + { + "@level": "info", + "@message": "Emergency state dump", + "@module": "terraform.ui", + "type": "log", + "state": stateJSON, + }, + } + + testJSONViewOutputEquals(t, done(t).Stdout(), want) +} + +func TestOperationJSON_planNoChanges(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := &OperationJSON{view: NewJSONView(NewView(streams))} + + v.PlanNoChanges() + + want := []map[string]interface{}{ + { + "@level": "info", + "@message": "Plan: 0 to add, 0 to change, 0 to destroy.", + "@module": "terraform.ui", + "type": "change_summary", + "changes": map[string]interface{}{ + "operation": "plan", + "add": float64(0), + "change": float64(0), + "remove": float64(0), + }, + }, + } + + testJSONViewOutputEquals(t, done(t).Stdout(), want) +} + +func TestOperationJSON_plan(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := &OperationJSON{view: NewJSONView(NewView(streams))} + + root := addrs.RootModuleInstance + vpc, diags := addrs.ParseModuleInstanceStr("module.vpc") + if len(diags) > 0 { + t.Fatal(diags.Err()) + } + boop := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_instance", Name: "boop"} + beep := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_instance", Name: "beep"} + derp := addrs.Resource{Mode: addrs.DataResourceMode, Type: "test_source", Name: "derp"} + + plan := &plans.Plan{ + Changes: &plans.Changes{ + Resources: []*plans.ResourceInstanceChangeSrc{ + { + Addr: boop.Instance(addrs.IntKey(0)).Absolute(root), + ChangeSrc: plans.ChangeSrc{Action: plans.CreateThenDelete}, + }, + { + Addr: boop.Instance(addrs.IntKey(1)).Absolute(root), + ChangeSrc: plans.ChangeSrc{Action: plans.Create}, + }, + { + Addr: boop.Instance(addrs.IntKey(0)).Absolute(vpc), + ChangeSrc: plans.ChangeSrc{Action: plans.Delete}, + }, + { + Addr: beep.Instance(addrs.NoKey).Absolute(root), + ChangeSrc: plans.ChangeSrc{Action: plans.DeleteThenCreate}, + }, + { + Addr: beep.Instance(addrs.NoKey).Absolute(vpc), + ChangeSrc: plans.ChangeSrc{Action: plans.Update}, + }, + // Data source deletion should not show up in the logs + { + Addr: derp.Instance(addrs.NoKey).Absolute(root), + ChangeSrc: plans.ChangeSrc{Action: plans.Delete}, + }, + }, + }, + } + v.Plan(plan, nil) + + want := []map[string]interface{}{ + // Create-then-delete should result in replace + { + "@level": "info", + "@message": "test_instance.boop[0]: Plan to replace", + "@module": "terraform.ui", + "type": "planned_change", + "change": map[string]interface{}{ + "action": "replace", + "resource": map[string]interface{}{ + "addr": `test_instance.boop[0]`, + "implied_provider": "test", + "module": "", + "resource": `test_instance.boop[0]`, + "resource_key": float64(0), + "resource_name": "boop", + "resource_type": "test_instance", + }, + }, + }, + // Simple create + { + "@level": "info", + "@message": "test_instance.boop[1]: Plan to create", + "@module": "terraform.ui", + "type": "planned_change", + "change": map[string]interface{}{ + "action": "create", + "resource": map[string]interface{}{ + "addr": `test_instance.boop[1]`, + "implied_provider": "test", + "module": "", + "resource": `test_instance.boop[1]`, + "resource_key": float64(1), + "resource_name": "boop", + "resource_type": "test_instance", + }, + }, + }, + // Simple delete + { + "@level": "info", + "@message": "module.vpc.test_instance.boop[0]: Plan to delete", + "@module": "terraform.ui", + "type": "planned_change", + "change": map[string]interface{}{ + "action": "delete", + "resource": map[string]interface{}{ + "addr": `module.vpc.test_instance.boop[0]`, + "implied_provider": "test", + "module": "module.vpc", + "resource": `test_instance.boop[0]`, + "resource_key": float64(0), + "resource_name": "boop", + "resource_type": "test_instance", + }, + }, + }, + // Delete-then-create is also a replace + { + "@level": "info", + "@message": "test_instance.beep: Plan to replace", + "@module": "terraform.ui", + "type": "planned_change", + "change": map[string]interface{}{ + "action": "replace", + "resource": map[string]interface{}{ + "addr": `test_instance.beep`, + "implied_provider": "test", + "module": "", + "resource": `test_instance.beep`, + "resource_key": nil, + "resource_name": "beep", + "resource_type": "test_instance", + }, + }, + }, + // Simple update + { + "@level": "info", + "@message": "module.vpc.test_instance.beep: Plan to update", + "@module": "terraform.ui", + "type": "planned_change", + "change": map[string]interface{}{ + "action": "update", + "resource": map[string]interface{}{ + "addr": `module.vpc.test_instance.beep`, + "implied_provider": "test", + "module": "module.vpc", + "resource": `test_instance.beep`, + "resource_key": nil, + "resource_name": "beep", + "resource_type": "test_instance", + }, + }, + }, + // These counts are 3 add/1 change/3 destroy because the replace + // changes result in both add and destroy counts. + { + "@level": "info", + "@message": "Plan: 3 to add, 1 to change, 3 to destroy.", + "@module": "terraform.ui", + "type": "change_summary", + "changes": map[string]interface{}{ + "operation": "plan", + "add": float64(3), + "change": float64(1), + "remove": float64(3), + }, + }, + } + + testJSONViewOutputEquals(t, done(t).Stdout(), want) +} + +func TestOperationJSON_plannedChange(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := &OperationJSON{view: NewJSONView(NewView(streams))} + + root := addrs.RootModuleInstance + boop := addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "test_instance", Name: "boop"} + derp := addrs.Resource{Mode: addrs.DataResourceMode, Type: "test_source", Name: "derp"} + + // Replace requested by user + v.PlannedChange(&plans.ResourceInstanceChangeSrc{ + Addr: boop.Instance(addrs.IntKey(0)).Absolute(root), + ChangeSrc: plans.ChangeSrc{Action: plans.DeleteThenCreate}, + ActionReason: plans.ResourceInstanceReplaceByRequest, + }) + + // Simple create + v.PlannedChange(&plans.ResourceInstanceChangeSrc{ + Addr: boop.Instance(addrs.IntKey(1)).Absolute(root), + ChangeSrc: plans.ChangeSrc{Action: plans.Create}, + }) + + // Data source deletion + v.PlannedChange(&plans.ResourceInstanceChangeSrc{ + Addr: derp.Instance(addrs.NoKey).Absolute(root), + ChangeSrc: plans.ChangeSrc{Action: plans.Delete}, + }) + + // Expect only two messages, as the data source deletion should be a no-op + want := []map[string]interface{}{ + { + "@level": "info", + "@message": "test_instance.boop[0]: Plan to replace", + "@module": "terraform.ui", + "type": "planned_change", + "change": map[string]interface{}{ + "action": "replace", + "reason": "requested", + "resource": map[string]interface{}{ + "addr": `test_instance.boop[0]`, + "implied_provider": "test", + "module": "", + "resource": `test_instance.boop[0]`, + "resource_key": float64(0), + "resource_name": "boop", + "resource_type": "test_instance", + }, + }, + }, + { + "@level": "info", + "@message": "test_instance.boop[1]: Plan to create", + "@module": "terraform.ui", + "type": "planned_change", + "change": map[string]interface{}{ + "action": "create", + "resource": map[string]interface{}{ + "addr": `test_instance.boop[1]`, + "implied_provider": "test", + "module": "", + "resource": `test_instance.boop[1]`, + "resource_key": float64(1), + "resource_name": "boop", + "resource_type": "test_instance", + }, + }, + }, + } + + testJSONViewOutputEquals(t, done(t).Stdout(), want) +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/output.go b/vendor/github.com/hashicorp/terraform/command/views/output.go new file mode 100644 index 00000000..e189898b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/output.go @@ -0,0 +1,289 @@ +package views + +import ( + "bytes" + "encoding/json" + "fmt" + "sort" + "strings" + + "github.com/zclconf/go-cty/cty" + "github.com/zclconf/go-cty/cty/convert" + ctyjson "github.com/zclconf/go-cty/cty/json" + + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/repl" + "github.com/hashicorp/terraform/states" + "github.com/hashicorp/terraform/tfdiags" +) + +// The Output view renders either one or all outputs, depending on whether or +// not the name argument is empty. +type Output interface { + Output(name string, outputs map[string]*states.OutputValue) tfdiags.Diagnostics + Diagnostics(diags tfdiags.Diagnostics) +} + +// NewOutput returns an initialized Output implementation for the given ViewType. +func NewOutput(vt arguments.ViewType, view *View) Output { + switch vt { + case arguments.ViewJSON: + return &OutputJSON{view: view} + case arguments.ViewRaw: + return &OutputRaw{view: view} + case arguments.ViewHuman: + return &OutputHuman{view: view} + default: + panic(fmt.Sprintf("unknown view type %v", vt)) + } +} + +// The OutputHuman implementation renders outputs in a format equivalent to HCL +// source. This uses the same formatting logic as in the console REPL. +type OutputHuman struct { + view *View +} + +var _ Output = (*OutputHuman)(nil) + +func (v *OutputHuman) Output(name string, outputs map[string]*states.OutputValue) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + if len(outputs) == 0 { + diags = diags.Append(noOutputsWarning()) + return diags + } + + if name != "" { + output, ok := outputs[name] + if !ok { + diags = diags.Append(missingOutputError(name)) + return diags + } + result := repl.FormatValue(output.Value, 0) + v.view.streams.Println(result) + return nil + } + + outputBuf := new(bytes.Buffer) + if len(outputs) > 0 { + // Output the outputs in alphabetical order + keyLen := 0 + ks := make([]string, 0, len(outputs)) + for key := range outputs { + ks = append(ks, key) + if len(key) > keyLen { + keyLen = len(key) + } + } + sort.Strings(ks) + + for _, k := range ks { + v := outputs[k] + if v.Sensitive { + outputBuf.WriteString(fmt.Sprintf("%s = \n", k)) + continue + } + + result := repl.FormatValue(v.Value, 0) + outputBuf.WriteString(fmt.Sprintf("%s = %s\n", k, result)) + } + } + + v.view.streams.Println(strings.TrimSpace(outputBuf.String())) + + return nil +} + +func (v *OutputHuman) Diagnostics(diags tfdiags.Diagnostics) { + v.view.Diagnostics(diags) +} + +// The OutputRaw implementation renders single string, number, or boolean +// output values directly and without quotes or other formatting. This is +// intended for use in shell scripting or other environments where the exact +// type of an output value is not important. +type OutputRaw struct { + view *View + + // Unit tests may set rawPrint to capture the output from the Output + // method, which would normally go to stdout directly. + rawPrint func(string) +} + +var _ Output = (*OutputRaw)(nil) + +func (v *OutputRaw) Output(name string, outputs map[string]*states.OutputValue) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + if len(outputs) == 0 { + diags = diags.Append(noOutputsWarning()) + return diags + } + + if name == "" { + diags = diags.Append(fmt.Errorf("Raw output format is only supported for single outputs")) + return diags + } + + output, ok := outputs[name] + if !ok { + diags = diags.Append(missingOutputError(name)) + return diags + } + + strV, err := convert.Convert(output.Value, cty.String) + if err != nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Unsupported value for raw output", + fmt.Sprintf( + "The -raw option only supports strings, numbers, and boolean values, but output value %q is %s.\n\nUse the -json option for machine-readable representations of output values that have complex types.", + name, output.Value.Type().FriendlyName(), + ), + )) + return diags + } + if strV.IsNull() { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Unsupported value for raw output", + fmt.Sprintf( + "The value for output value %q is null, so -raw mode cannot print it.", + name, + ), + )) + return diags + } + if !strV.IsKnown() { + // Since we're working with values from the state it would be very + // odd to end up in here, but we'll handle it anyway to avoid a + // panic in case our rules somehow change in future. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Unsupported value for raw output", + fmt.Sprintf( + "The value for output value %q won't be known until after a successful terraform apply, so -raw mode cannot print it.", + name, + ), + )) + return diags + } + // If we get out here then we should have a valid string to print. + // We're writing it using Print here so that a shell caller will get + // exactly the value and no extra whitespace (including trailing newline). + v.view.streams.Print(strV.AsString()) + return nil +} + +func (v *OutputRaw) Diagnostics(diags tfdiags.Diagnostics) { + v.view.Diagnostics(diags) +} + +// The OutputJSON implementation renders outputs as JSON values. When rendering +// a single output, only the value is displayed. When rendering all outputs, +// the result is a JSON object with keys matching the output names and object +// values including type and sensitivity metadata. +type OutputJSON struct { + view *View +} + +var _ Output = (*OutputJSON)(nil) + +func (v *OutputJSON) Output(name string, outputs map[string]*states.OutputValue) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + if name != "" { + output, ok := outputs[name] + if !ok { + diags = diags.Append(missingOutputError(name)) + return diags + } + value := output.Value + + jsonOutput, err := ctyjson.Marshal(value, value.Type()) + if err != nil { + diags = diags.Append(err) + return diags + } + + v.view.streams.Println(string(jsonOutput)) + + return nil + } + + // Due to a historical accident, the switch from state version 2 to + // 3 caused our JSON output here to be the full metadata about the + // outputs rather than just the output values themselves as we'd + // show in the single value case. We must now maintain that behavior + // for compatibility, so this is an emulation of the JSON + // serialization of outputs used in state format version 3. + type OutputMeta struct { + Sensitive bool `json:"sensitive"` + Type json.RawMessage `json:"type"` + Value json.RawMessage `json:"value"` + } + outputMetas := map[string]OutputMeta{} + + for n, os := range outputs { + jsonVal, err := ctyjson.Marshal(os.Value, os.Value.Type()) + if err != nil { + diags = diags.Append(err) + return diags + } + jsonType, err := ctyjson.MarshalType(os.Value.Type()) + if err != nil { + diags = diags.Append(err) + return diags + } + outputMetas[n] = OutputMeta{ + Sensitive: os.Sensitive, + Type: json.RawMessage(jsonType), + Value: json.RawMessage(jsonVal), + } + } + + jsonOutputs, err := json.MarshalIndent(outputMetas, "", " ") + if err != nil { + diags = diags.Append(err) + return diags + } + + v.view.streams.Println(string(jsonOutputs)) + + return nil +} + +func (v *OutputJSON) Diagnostics(diags tfdiags.Diagnostics) { + v.view.Diagnostics(diags) +} + +// For text and raw output modes, an empty map of outputs is considered a +// separate and higher priority failure mode than an output not being present +// in a non-empty map. This warning diagnostic explains how this might have +// happened. +func noOutputsWarning() tfdiags.Diagnostic { + return tfdiags.Sourceless( + tfdiags.Warning, + "No outputs found", + "The state file either has no outputs defined, or all the defined "+ + "outputs are empty. Please define an output in your configuration "+ + "with the `output` keyword and run `terraform refresh` for it to "+ + "become available. If you are using interpolation, please verify "+ + "the interpolated value is not empty. You can use the "+ + "`terraform console` command to assist.", + ) +} + +// Attempting to display a missing output results in this failure, which +// includes suggestions on how to rectify the problem. +func missingOutputError(name string) tfdiags.Diagnostic { + return tfdiags.Sourceless( + tfdiags.Error, + fmt.Sprintf("Output %q not found", name), + "The output variable requested could not be found in the state "+ + "file. If you recently added this to your configuration, be "+ + "sure to run `terraform apply`, since the state won't be updated "+ + "with new output variables until that command is run.", + ) +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/output_test.go b/vendor/github.com/hashicorp/terraform/command/views/output_test.go new file mode 100644 index 00000000..a1204950 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/output_test.go @@ -0,0 +1,363 @@ +package views + +import ( + "strings" + "testing" + + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/internal/terminal" + "github.com/hashicorp/terraform/states" + "github.com/zclconf/go-cty/cty" +) + +// Test various single output values for human-readable UI. Note that since +// OutputHuman defers to repl.FormatValue to render a single value, most of the +// test coverage should be in that package. +func TestOutputHuman_single(t *testing.T) { + testCases := map[string]struct { + value cty.Value + want string + wantErr bool + }{ + "string": { + value: cty.StringVal("hello"), + want: "\"hello\"\n", + }, + "list of maps": { + value: cty.ListVal([]cty.Value{ + cty.MapVal(map[string]cty.Value{ + "key": cty.StringVal("value"), + "key2": cty.StringVal("value2"), + }), + cty.MapVal(map[string]cty.Value{ + "key": cty.StringVal("value"), + }), + }), + want: `tolist([ + tomap({ + "key" = "value" + "key2" = "value2" + }), + tomap({ + "key" = "value" + }), +]) +`, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewOutput(arguments.ViewHuman, NewView(streams)) + + outputs := map[string]*states.OutputValue{ + "foo": {Value: tc.value}, + } + diags := v.Output("foo", outputs) + + if diags.HasErrors() { + if !tc.wantErr { + t.Fatalf("unexpected diagnostics: %s", diags) + } + } else if tc.wantErr { + t.Fatalf("succeeded, but want error") + } + + if got, want := done(t).Stdout(), tc.want; got != want { + t.Errorf("wrong result\ngot: %q\nwant: %q", got, want) + } + }) + } +} + +// Sensitive output values are rendered to the console intentionally when +// requesting a single output. +func TestOutput_sensitive(t *testing.T) { + testCases := map[string]arguments.ViewType{ + "human": arguments.ViewHuman, + "json": arguments.ViewJSON, + "raw": arguments.ViewRaw, + } + for name, vt := range testCases { + t.Run(name, func(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewOutput(vt, NewView(streams)) + + outputs := map[string]*states.OutputValue{ + "foo": { + Value: cty.StringVal("secret"), + Sensitive: true, + }, + } + diags := v.Output("foo", outputs) + + if diags.HasErrors() { + t.Fatalf("unexpected diagnostics: %s", diags) + } + + // Test for substring match here because we don't care about exact + // output format in this test, just the presence of the sensitive + // value. + if got, want := done(t).Stdout(), "secret"; !strings.Contains(got, want) { + t.Errorf("wrong result\ngot: %q\nwant: %q", got, want) + } + }) + } +} + +// Showing all outputs is supported by human and JSON output format. +func TestOutput_all(t *testing.T) { + outputs := map[string]*states.OutputValue{ + "foo": { + Value: cty.StringVal("secret"), + Sensitive: true, + }, + "bar": { + Value: cty.ListVal([]cty.Value{cty.True, cty.False, cty.True}), + }, + "baz": { + Value: cty.ObjectVal(map[string]cty.Value{ + "boop": cty.NumberIntVal(5), + "beep": cty.StringVal("true"), + }), + }, + } + + testCases := map[string]struct { + vt arguments.ViewType + want string + }{ + "human": { + arguments.ViewHuman, + `bar = tolist([ + true, + false, + true, +]) +baz = { + "beep" = "true" + "boop" = 5 +} +foo = +`, + }, + "json": { + arguments.ViewJSON, + `{ + "bar": { + "sensitive": false, + "type": [ + "list", + "bool" + ], + "value": [ + true, + false, + true + ] + }, + "baz": { + "sensitive": false, + "type": [ + "object", + { + "beep": "string", + "boop": "number" + } + ], + "value": { + "beep": "true", + "boop": 5 + } + }, + "foo": { + "sensitive": true, + "type": "string", + "value": "secret" + } +} +`, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewOutput(tc.vt, NewView(streams)) + diags := v.Output("", outputs) + + if diags.HasErrors() { + t.Fatalf("unexpected diagnostics: %s", diags) + } + + if got := done(t).Stdout(); got != tc.want { + t.Errorf("wrong result\ngot: %q\nwant: %q", got, tc.want) + } + }) + } +} + +// JSON output format supports empty outputs by rendering an empty object +// without diagnostics. +func TestOutputJSON_empty(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewOutput(arguments.ViewJSON, NewView(streams)) + + diags := v.Output("", map[string]*states.OutputValue{}) + + if diags.HasErrors() { + t.Fatalf("unexpected diagnostics: %s", diags) + } + + if got, want := done(t).Stdout(), "{}\n"; got != want { + t.Errorf("wrong result\ngot: %q\nwant: %q", got, want) + } +} + +// Human and raw formats render a warning if there are no outputs. +func TestOutput_emptyWarning(t *testing.T) { + testCases := map[string]arguments.ViewType{ + "human": arguments.ViewHuman, + "raw": arguments.ViewRaw, + } + + for name, vt := range testCases { + t.Run(name, func(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewOutput(vt, NewView(streams)) + + diags := v.Output("", map[string]*states.OutputValue{}) + + if got, want := done(t).Stdout(), ""; got != want { + t.Errorf("wrong result\ngot: %q\nwant: %q", got, want) + } + + if len(diags) != 1 { + t.Fatalf("expected 1 diagnostic, got %d", len(diags)) + } + + if diags.HasErrors() { + t.Fatalf("unexpected error diagnostics: %s", diags) + } + + if got, want := diags[0].Description().Summary, "No outputs found"; got != want { + t.Errorf("unexpected diagnostics: %s", diags) + } + }) + } +} + +// Raw output is a simple unquoted output format designed for shell scripts, +// which relies on the cty.AsString() implementation. This test covers +// formatting for supported value types. +func TestOutputRaw(t *testing.T) { + values := map[string]cty.Value{ + "str": cty.StringVal("bar"), + "multistr": cty.StringVal("bar\nbaz"), + "num": cty.NumberIntVal(2), + "bool": cty.True, + "obj": cty.EmptyObjectVal, + "null": cty.NullVal(cty.String), + "unknown": cty.UnknownVal(cty.String), + } + + tests := map[string]struct { + WantOutput string + WantErr bool + }{ + "str": {WantOutput: "bar"}, + "multistr": {WantOutput: "bar\nbaz"}, + "num": {WantOutput: "2"}, + "bool": {WantOutput: "true"}, + "obj": {WantErr: true}, + "null": {WantErr: true}, + "unknown": {WantErr: true}, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewOutput(arguments.ViewRaw, NewView(streams)) + + value := values[name] + outputs := map[string]*states.OutputValue{ + name: {Value: value}, + } + diags := v.Output(name, outputs) + + if diags.HasErrors() { + if !test.WantErr { + t.Fatalf("unexpected diagnostics: %s", diags) + } + } else if test.WantErr { + t.Fatalf("succeeded, but want error") + } + + if got, want := done(t).Stdout(), test.WantOutput; got != want { + t.Errorf("wrong result\ngot: %q\nwant: %q", got, want) + } + }) + } +} + +// Raw cannot render all outputs. +func TestOutputRaw_all(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewOutput(arguments.ViewRaw, NewView(streams)) + + outputs := map[string]*states.OutputValue{ + "foo": {Value: cty.StringVal("secret")}, + "bar": {Value: cty.True}, + } + diags := v.Output("", outputs) + + if got, want := done(t).Stdout(), ""; got != want { + t.Errorf("wrong result\ngot: %q\nwant: %q", got, want) + } + + if !diags.HasErrors() { + t.Fatalf("expected diagnostics, got %s", diags) + } + + if got, want := diags.Err().Error(), "Raw output format is only supported for single outputs"; got != want { + t.Errorf("unexpected diagnostics: %s", diags) + } +} + +// All outputs render an error if a specific output is requested which is +// missing from the map of outputs. +func TestOutput_missing(t *testing.T) { + testCases := map[string]arguments.ViewType{ + "human": arguments.ViewHuman, + "json": arguments.ViewJSON, + "raw": arguments.ViewRaw, + } + + for name, vt := range testCases { + t.Run(name, func(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewOutput(vt, NewView(streams)) + + diags := v.Output("foo", map[string]*states.OutputValue{ + "bar": {Value: cty.StringVal("boop")}, + }) + + if len(diags) != 1 { + t.Fatalf("expected 1 diagnostic, got %d", len(diags)) + } + + if !diags.HasErrors() { + t.Fatalf("expected error diagnostics, got %s", diags) + } + + if got, want := diags[0].Description().Summary, `Output "foo" not found`; got != want { + t.Errorf("unexpected diagnostics: %s", diags) + } + + if got, want := done(t).Stdout(), ""; got != want { + t.Errorf("wrong result\ngot: %q\nwant: %q", got, want) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/plan.go b/vendor/github.com/hashicorp/terraform/command/views/plan.go new file mode 100644 index 00000000..fca9f0ec --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/plan.go @@ -0,0 +1,214 @@ +package views + +import ( + "bytes" + "fmt" + "sort" + "strings" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/format" + "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/tfdiags" +) + +// The Plan view is used for the plan command. +type Plan interface { + Operation() Operation + Hooks() []terraform.Hook + + Diagnostics(diags tfdiags.Diagnostics) + HelpPrompt() +} + +// NewPlan returns an initialized Plan implementation for the given ViewType. +func NewPlan(vt arguments.ViewType, view *View) Plan { + switch vt { + case arguments.ViewJSON: + return &PlanJSON{ + view: NewJSONView(view), + } + case arguments.ViewHuman: + return &PlanHuman{ + view: view, + inAutomation: view.RunningInAutomation(), + } + default: + panic(fmt.Sprintf("unknown view type %v", vt)) + } +} + +// The PlanHuman implementation renders human-readable text logs, suitable for +// a scrolling terminal. +type PlanHuman struct { + view *View + + inAutomation bool +} + +var _ Plan = (*PlanHuman)(nil) + +func (v *PlanHuman) Operation() Operation { + return NewOperation(arguments.ViewHuman, v.inAutomation, v.view) +} + +func (v *PlanHuman) Hooks() []terraform.Hook { + return []terraform.Hook{ + NewUiHook(v.view), + } +} + +func (v *PlanHuman) Diagnostics(diags tfdiags.Diagnostics) { + v.view.Diagnostics(diags) +} + +func (v *PlanHuman) HelpPrompt() { + v.view.HelpPrompt("plan") +} + +// The PlanJSON implementation renders streaming JSON logs, suitable for +// integrating with other software. +type PlanJSON struct { + view *JSONView +} + +var _ Plan = (*PlanJSON)(nil) + +func (v *PlanJSON) Operation() Operation { + return &OperationJSON{view: v.view} +} + +func (v *PlanJSON) Hooks() []terraform.Hook { + return []terraform.Hook{ + newJSONHook(v.view), + } +} + +func (v *PlanJSON) Diagnostics(diags tfdiags.Diagnostics) { + v.view.Diagnostics(diags) +} + +func (v *PlanJSON) HelpPrompt() { +} + +// The plan renderer is used by the Operation view (for plan and apply +// commands) and the Show view (for the show command). +func renderPlan(plan *plans.Plan, schemas *terraform.Schemas, view *View) { + counts := map[plans.Action]int{} + var rChanges []*plans.ResourceInstanceChangeSrc + for _, change := range plan.Changes.Resources { + if change.Action == plans.Delete && change.Addr.Resource.Resource.Mode == addrs.DataResourceMode { + // Avoid rendering data sources on deletion + continue + } + + rChanges = append(rChanges, change) + counts[change.Action]++ + } + + headerBuf := &bytes.Buffer{} + fmt.Fprintf(headerBuf, "\n%s\n", strings.TrimSpace(format.WordWrap(planHeaderIntro, view.outputColumns()))) + if counts[plans.Create] > 0 { + fmt.Fprintf(headerBuf, "%s create\n", format.DiffActionSymbol(plans.Create)) + } + if counts[plans.Update] > 0 { + fmt.Fprintf(headerBuf, "%s update in-place\n", format.DiffActionSymbol(plans.Update)) + } + if counts[plans.Delete] > 0 { + fmt.Fprintf(headerBuf, "%s destroy\n", format.DiffActionSymbol(plans.Delete)) + } + if counts[plans.DeleteThenCreate] > 0 { + fmt.Fprintf(headerBuf, "%s destroy and then create replacement\n", format.DiffActionSymbol(plans.DeleteThenCreate)) + } + if counts[plans.CreateThenDelete] > 0 { + fmt.Fprintf(headerBuf, "%s create replacement and then destroy\n", format.DiffActionSymbol(plans.CreateThenDelete)) + } + if counts[plans.Read] > 0 { + fmt.Fprintf(headerBuf, "%s read (data resources)\n", format.DiffActionSymbol(plans.Read)) + } + + view.streams.Println(view.colorize.Color(headerBuf.String())) + + view.streams.Printf("Terraform will perform the following actions:\n\n") + + // Note: we're modifying the backing slice of this plan object in-place + // here. The ordering of resource changes in a plan is not significant, + // but we can only do this safely here because we can assume that nobody + // is concurrently modifying our changes while we're trying to print it. + sort.Slice(rChanges, func(i, j int) bool { + iA := rChanges[i].Addr + jA := rChanges[j].Addr + if iA.String() == jA.String() { + return rChanges[i].DeposedKey < rChanges[j].DeposedKey + } + return iA.Less(jA) + }) + + for _, rcs := range rChanges { + if rcs.Action == plans.NoOp { + continue + } + + providerSchema := schemas.ProviderSchema(rcs.ProviderAddr.Provider) + if providerSchema == nil { + // Should never happen + view.streams.Printf("(schema missing for %s)\n\n", rcs.ProviderAddr) + continue + } + rSchema, _ := providerSchema.SchemaForResourceAddr(rcs.Addr.Resource.Resource) + if rSchema == nil { + // Should never happen + view.streams.Printf("(schema missing for %s)\n\n", rcs.Addr) + continue + } + + view.streams.Println(format.ResourceChange( + rcs, + rSchema, + view.colorize, + )) + } + + // stats is similar to counts above, but: + // - it considers only resource changes + // - it simplifies "replace" into both a create and a delete + stats := map[plans.Action]int{} + for _, change := range rChanges { + switch change.Action { + case plans.CreateThenDelete, plans.DeleteThenCreate: + stats[plans.Create]++ + stats[plans.Delete]++ + default: + stats[change.Action]++ + } + } + view.streams.Printf( + view.colorize.Color("[reset][bold]Plan:[reset] %d to add, %d to change, %d to destroy.\n"), + stats[plans.Create], stats[plans.Update], stats[plans.Delete], + ) + + // If there is at least one planned change to the root module outputs + // then we'll render a summary of those too. + var changedRootModuleOutputs []*plans.OutputChangeSrc + for _, output := range plan.Changes.Outputs { + if !output.Addr.Module.IsRoot() { + continue + } + if output.ChangeSrc.Action == plans.NoOp { + continue + } + changedRootModuleOutputs = append(changedRootModuleOutputs, output) + } + if len(changedRootModuleOutputs) > 0 { + view.streams.Println( + view.colorize.Color("[reset]\n[bold]Changes to Outputs:[reset]") + + format.OutputChanges(changedRootModuleOutputs, view.colorize), + ) + } +} + +const planHeaderIntro = ` +Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: +` diff --git a/vendor/github.com/hashicorp/terraform/command/views/plan_test.go b/vendor/github.com/hashicorp/terraform/command/views/plan_test.go new file mode 100644 index 00000000..f3c33abb --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/plan_test.go @@ -0,0 +1,124 @@ +package views + +import ( + "testing" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/internal/terminal" + "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/terraform" + "github.com/zclconf/go-cty/cty" +) + +// Ensure that the correct view type and in-automation settings propagate to the +// Operation view. +func TestPlanHuman_operation(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + defer done(t) + v := NewPlan(arguments.ViewHuman, NewView(streams).SetRunningInAutomation(true)).Operation() + if hv, ok := v.(*OperationHuman); !ok { + t.Fatalf("unexpected return type %t", v) + } else if hv.inAutomation != true { + t.Fatalf("unexpected inAutomation value on Operation view") + } +} + +// Verify that Hooks includes a UI hook +func TestPlanHuman_hooks(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + defer done(t) + v := NewPlan(arguments.ViewHuman, NewView(streams).SetRunningInAutomation((true))) + hooks := v.Hooks() + + var uiHook *UiHook + for _, hook := range hooks { + if ch, ok := hook.(*UiHook); ok { + uiHook = ch + } + } + if uiHook == nil { + t.Fatalf("expected Hooks to include a UiHook: %#v", hooks) + } +} + +// Helper functions to build a trivial test plan, to exercise the plan +// renderer. +func testPlan(t *testing.T) *plans.Plan { + t.Helper() + + plannedVal := cty.ObjectVal(map[string]cty.Value{ + "id": cty.UnknownVal(cty.String), + "foo": cty.StringVal("bar"), + }) + priorValRaw, err := plans.NewDynamicValue(cty.NullVal(plannedVal.Type()), plannedVal.Type()) + if err != nil { + t.Fatal(err) + } + plannedValRaw, err := plans.NewDynamicValue(plannedVal, plannedVal.Type()) + if err != nil { + t.Fatal(err) + } + + changes := plans.NewChanges() + changes.SyncWrapper().AppendResourceInstanceChange(&plans.ResourceInstanceChangeSrc{ + Addr: addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_resource", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + ProviderAddr: addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ChangeSrc: plans.ChangeSrc{ + Action: plans.Create, + Before: priorValRaw, + After: plannedValRaw, + }, + }) + + return &plans.Plan{ + Changes: changes, + } +} + +func testSchemas() *terraform.Schemas { + provider := testProvider() + return &terraform.Schemas{ + Providers: map[addrs.Provider]*terraform.ProviderSchema{ + addrs.NewDefaultProvider("test"): provider.ProviderSchema(), + }, + } +} + +func testProvider() *terraform.MockProvider { + p := new(terraform.MockProvider) + p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { + return providers.ReadResourceResponse{NewState: req.PriorState} + } + + p.GetProviderSchemaResponse = testProviderSchema() + + return p +} + +func testProviderSchema() *providers.GetProviderSchemaResponse { + return &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{}, + }, + ResourceTypes: map[string]providers.Schema{ + "test_resource": { + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + "foo": {Type: cty.String, Optional: true}, + }, + }, + }, + }, + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/refresh.go b/vendor/github.com/hashicorp/terraform/command/views/refresh.go new file mode 100644 index 00000000..39db5a3b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/refresh.go @@ -0,0 +1,112 @@ +package views + +import ( + "fmt" + + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/views/json" + "github.com/hashicorp/terraform/states" + "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/tfdiags" +) + +// The Refresh view is used for the refresh command. +type Refresh interface { + Outputs(outputValues map[string]*states.OutputValue) + + Operation() Operation + Hooks() []terraform.Hook + + Diagnostics(diags tfdiags.Diagnostics) + HelpPrompt() +} + +// NewRefresh returns an initialized Refresh implementation for the given ViewType. +func NewRefresh(vt arguments.ViewType, view *View) Refresh { + switch vt { + case arguments.ViewJSON: + return &RefreshJSON{ + view: NewJSONView(view), + } + case arguments.ViewHuman: + return &RefreshHuman{ + view: view, + inAutomation: view.RunningInAutomation(), + countHook: &countHook{}, + } + default: + panic(fmt.Sprintf("unknown view type %v", vt)) + } +} + +// The RefreshHuman implementation renders human-readable text logs, suitable for +// a scrolling terminal. +type RefreshHuman struct { + view *View + + inAutomation bool + + countHook *countHook +} + +var _ Refresh = (*RefreshHuman)(nil) + +func (v *RefreshHuman) Outputs(outputValues map[string]*states.OutputValue) { + if len(outputValues) > 0 { + v.view.streams.Print(v.view.colorize.Color("[reset][bold][green]\nOutputs:\n\n")) + NewOutput(arguments.ViewHuman, v.view).Output("", outputValues) + } +} + +func (v *RefreshHuman) Operation() Operation { + return NewOperation(arguments.ViewHuman, v.inAutomation, v.view) +} + +func (v *RefreshHuman) Hooks() []terraform.Hook { + return []terraform.Hook{ + v.countHook, + NewUiHook(v.view), + } +} + +func (v *RefreshHuman) Diagnostics(diags tfdiags.Diagnostics) { + v.view.Diagnostics(diags) +} + +func (v *RefreshHuman) HelpPrompt() { + v.view.HelpPrompt("refresh") +} + +// The RefreshJSON implementation renders streaming JSON logs, suitable for +// integrating with other software. +type RefreshJSON struct { + view *JSONView +} + +var _ Refresh = (*RefreshJSON)(nil) + +func (v *RefreshJSON) Outputs(outputValues map[string]*states.OutputValue) { + outputs, diags := json.OutputsFromMap(outputValues) + if diags.HasErrors() { + v.Diagnostics(diags) + } else { + v.view.Outputs(outputs) + } +} + +func (v *RefreshJSON) Operation() Operation { + return &OperationJSON{view: v.view} +} + +func (v *RefreshJSON) Hooks() []terraform.Hook { + return []terraform.Hook{ + newJSONHook(v.view), + } +} + +func (v *RefreshJSON) Diagnostics(diags tfdiags.Diagnostics) { + v.view.Diagnostics(diags) +} + +func (v *RefreshJSON) HelpPrompt() { +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/refresh_test.go b/vendor/github.com/hashicorp/terraform/command/views/refresh_test.go new file mode 100644 index 00000000..ff145118 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/refresh_test.go @@ -0,0 +1,107 @@ +package views + +import ( + "strings" + "testing" + + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/internal/terminal" + "github.com/hashicorp/terraform/states" + "github.com/zclconf/go-cty/cty" +) + +// Ensure that the correct view type and in-automation settings propagate to the +// Operation view. +func TestRefreshHuman_operation(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + defer done(t) + v := NewRefresh(arguments.ViewHuman, NewView(streams).SetRunningInAutomation(true)).Operation() + if hv, ok := v.(*OperationHuman); !ok { + t.Fatalf("unexpected return type %t", v) + } else if hv.inAutomation != true { + t.Fatalf("unexpected inAutomation value on Operation view") + } +} + +// Verify that Hooks includes a UI hook +func TestRefreshHuman_hooks(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + defer done(t) + v := NewRefresh(arguments.ViewHuman, NewView(streams).SetRunningInAutomation(true)) + hooks := v.Hooks() + + var uiHook *UiHook + for _, hook := range hooks { + if ch, ok := hook.(*UiHook); ok { + uiHook = ch + } + } + if uiHook == nil { + t.Fatalf("expected Hooks to include a UiHook: %#v", hooks) + } +} + +// Basic test coverage of Outputs, since most of its functionality is tested +// elsewhere. +func TestRefreshHuman_outputs(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewRefresh(arguments.ViewHuman, NewView(streams)) + + v.Outputs(map[string]*states.OutputValue{ + "foo": {Value: cty.StringVal("secret")}, + }) + + got := done(t).Stdout() + for _, want := range []string{"Outputs:", `foo = "secret"`} { + if !strings.Contains(got, want) { + t.Errorf("wrong result\ngot: %q\nwant: %q", got, want) + } + } +} + +// Outputs should do nothing if there are no outputs to render. +func TestRefreshHuman_outputsEmpty(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewRefresh(arguments.ViewHuman, NewView(streams)) + + v.Outputs(map[string]*states.OutputValue{}) + + got := done(t).Stdout() + if got != "" { + t.Errorf("output should be empty, but got: %q", got) + } +} + +// Basic test coverage of Outputs, since most of its functionality is tested +// elsewhere. +func TestRefreshJSON_outputs(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + v := NewRefresh(arguments.ViewJSON, NewView(streams)) + + v.Outputs(map[string]*states.OutputValue{ + "boop_count": {Value: cty.NumberIntVal(92)}, + "password": {Value: cty.StringVal("horse-battery").Mark("sensitive"), Sensitive: true}, + }) + + want := []map[string]interface{}{ + { + "@level": "info", + "@message": "Outputs: 2", + "@module": "terraform.ui", + "type": "outputs", + "outputs": map[string]interface{}{ + "boop_count": map[string]interface{}{ + "sensitive": false, + "value": float64(92), + "type": "number", + }, + "password": map[string]interface{}{ + "sensitive": true, + "value": "horse-battery", + "type": "string", + }, + }, + }, + } + testJSONViewOutputEquals(t, done(t).Stdout(), want) +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/show.go b/vendor/github.com/hashicorp/terraform/command/views/show.go new file mode 100644 index 00000000..d04d9e20 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/show.go @@ -0,0 +1,38 @@ +package views + +import ( + "fmt" + + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/terraform" +) + +// FIXME: this is a temporary partial definition of the view for the show +// command, in place to allow access to the plan renderer which is now in the +// views package. +type Show interface { + Plan(plan *plans.Plan, schemas *terraform.Schemas) +} + +// FIXME: the show view should support both human and JSON types. This code is +// currently only used to render the plan in human-readable UI, so does not yet +// support JSON. +func NewShow(vt arguments.ViewType, view *View) Show { + switch vt { + case arguments.ViewHuman: + return &ShowHuman{View: *view} + default: + panic(fmt.Sprintf("unknown view type %v", vt)) + } +} + +type ShowHuman struct { + View +} + +var _ Show = (*ShowHuman)(nil) + +func (v *ShowHuman) Plan(plan *plans.Plan, schemas *terraform.Schemas) { + renderPlan(plan, schemas, &v.View) +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/state_locker.go b/vendor/github.com/hashicorp/terraform/command/views/state_locker.go new file mode 100644 index 00000000..3a06bcec --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/state_locker.go @@ -0,0 +1,40 @@ +package views + +import ( + "fmt" + + "github.com/hashicorp/terraform/command/arguments" +) + +// The StateLocker view is used to display locking/unlocking status messages +// if the state lock process takes longer than expected. +type StateLocker interface { + Locking() + Unlocking() +} + +// NewStateLocker returns an initialized StateLocker implementation for the given ViewType. +func NewStateLocker(vt arguments.ViewType, view *View) StateLocker { + switch vt { + case arguments.ViewHuman: + return &StateLockerHuman{view: view} + default: + panic(fmt.Sprintf("unknown view type %v", vt)) + } +} + +// StateLockerHuman is an implementation of StateLocker which prints status to +// a terminal. +type StateLockerHuman struct { + view *View +} + +var _ StateLocker = (*StateLockerHuman)(nil) + +func (v *StateLockerHuman) Locking() { + v.view.streams.Println("Acquiring state lock. This may take a few moments...") +} + +func (v *StateLockerHuman) Unlocking() { + v.view.streams.Println("Releasing state lock. This may take a few moments...") +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/test.go b/vendor/github.com/hashicorp/terraform/command/views/test.go new file mode 100644 index 00000000..4ba3162d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/test.go @@ -0,0 +1,373 @@ +package views + +import ( + "encoding/xml" + "fmt" + "io/ioutil" + "sort" + "strings" + + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/format" + "github.com/hashicorp/terraform/internal/moduletest" + "github.com/hashicorp/terraform/internal/terminal" + "github.com/hashicorp/terraform/tfdiags" + "github.com/mitchellh/colorstring" +) + +// Test is the view interface for the "terraform test" command. +type Test interface { + // Results presents the given test results. + Results(map[string]*moduletest.Suite) tfdiags.Diagnostics + + // Diagnostics is for reporting warnings or errors that occurred with the + // mechanics of running tests. For this command in particular, some + // errors are considered to be test failures rather than mechanism failures, + // and so those will be reported via Results rather than via Diagnostics. + Diagnostics(tfdiags.Diagnostics) +} + +// NewTest returns an implementation of Test configured to respect the +// settings described in the given arguments. +func NewTest(base *View, args arguments.TestOutput) Test { + return &testHuman{ + streams: base.streams, + showDiagnostics: base.Diagnostics, + colorize: base.colorize, + junitXMLFile: args.JUnitXMLFile, + } +} + +type testHuman struct { + // This is the subset of functionality we need from the base view. + streams *terminal.Streams + showDiagnostics func(diags tfdiags.Diagnostics) + colorize *colorstring.Colorize + + // If junitXMLFile is not empty then results will be written to + // the given file path in addition to the usual output. + junitXMLFile string +} + +func (v *testHuman) Results(results map[string]*moduletest.Suite) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + // FIXME: Due to how this prototype command evolved concurrently with + // establishing the idea of command views, the handling of JUnit output + // as part of the "human" view rather than as a separate view in its + // own right is a little odd and awkward. We should refactor this + // prior to making "terraform test" a real supported command to make + // it be structured more like the other commands that use the views + // package. + + v.humanResults(results) + + if v.junitXMLFile != "" { + moreDiags := v.junitXMLResults(results, v.junitXMLFile) + diags = diags.Append(moreDiags) + } + + return diags +} + +func (v *testHuman) Diagnostics(diags tfdiags.Diagnostics) { + if len(diags) == 0 { + return + } + v.showDiagnostics(diags) +} + +func (v *testHuman) humanResults(results map[string]*moduletest.Suite) { + failCount := 0 + width := v.streams.Stderr.Columns() + + suiteNames := make([]string, 0, len(results)) + for suiteName := range results { + suiteNames = append(suiteNames, suiteName) + } + sort.Strings(suiteNames) + for _, suiteName := range suiteNames { + suite := results[suiteName] + + componentNames := make([]string, 0, len(suite.Components)) + for componentName := range suite.Components { + componentNames = append(componentNames, componentName) + } + for _, componentName := range componentNames { + component := suite.Components[componentName] + + assertionNames := make([]string, 0, len(component.Assertions)) + for assertionName := range component.Assertions { + assertionNames = append(assertionNames, assertionName) + } + sort.Strings(assertionNames) + + for _, assertionName := range assertionNames { + assertion := component.Assertions[assertionName] + + fullName := fmt.Sprintf("%s.%s.%s", suiteName, componentName, assertionName) + if strings.HasPrefix(componentName, "(") { + // parenthesis-prefixed components are placeholders that + // the test harness generates to represent problems that + // prevented checking any assertions at all, so we'll + // just hide them and show the suite name. + fullName = suiteName + } + headingExtra := fmt.Sprintf("%s (%s)", fullName, assertion.Description) + + switch assertion.Outcome { + case moduletest.Failed: + // Failed means that the assertion was successfully + // excecuted but that the assertion condition didn't hold. + v.eprintRuleHeading("yellow", "Failed", headingExtra) + + case moduletest.Error: + // Error means that the system encountered an unexpected + // error when trying to evaluate the assertion. + v.eprintRuleHeading("red", "Error", headingExtra) + + default: + // We don't do anything for moduletest.Passed or + // moduletest.Skipped. Perhaps in future we'll offer a + // -verbose option to include information about those. + continue + } + failCount++ + + if len(assertion.Message) > 0 { + dispMsg := format.WordWrap(assertion.Message, width) + v.streams.Eprintln(dispMsg) + } + if len(assertion.Diagnostics) > 0 { + // We'll do our own writing of the diagnostics in this + // case, rather than using v.Diagnostics, because we + // specifically want all of these diagnostics to go to + // Stderr along with all of the other output we've + // generated. + for _, diag := range assertion.Diagnostics { + diagStr := format.Diagnostic(diag, nil, v.colorize, width) + v.streams.Eprint(diagStr) + } + } + } + } + } + + if failCount > 0 { + // If we've printed at least one failure then we'll have printed at + // least one horizontal rule across the terminal, and so we'll balance + // that with another horizontal rule. + if width > 1 { + rule := strings.Repeat("─", width-1) + v.streams.Eprintln(v.colorize.Color("[dark_gray]" + rule)) + } + } + + if failCount == 0 { + if len(results) > 0 { + // This is not actually an error, but it's convenient if all of our + // result output goes to the same stream for when this is running in + // automation that might be gathering this output via a pipe. + v.streams.Eprint(v.colorize.Color("[bold][green]Success![reset] All of the test assertions passed.\n\n")) + } else { + v.streams.Eprint(v.colorize.Color("[bold][yellow]No tests defined.[reset] This module doesn't have any test suites to run.\n\n")) + } + } + + // Try to flush any buffering that might be happening. (This isn't always + // successful, depending on what sort of fd Stderr is connected to.) + v.streams.Stderr.File.Sync() +} + +func (v *testHuman) junitXMLResults(results map[string]*moduletest.Suite, filename string) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + // "JUnit XML" is a file format that has become a de-facto standard for + // test reporting tools but that is not formally specified anywhere, and + // so each producer and consumer implementation unfortunately tends to + // differ in certain ways from others. + // With that in mind, this is a best effort sort of thing aimed at being + // broadly compatible with various consumers, but it's likely that + // some consumers will present these results better than others. + // This implementation is based mainly on the pseudo-specification of the + // format curated here, based on the Jenkins parser implementation: + // https://llg.cubic.org/docs/junit/ + + // An "Outcome" represents one of the various XML elements allowed inside + // a testcase element to indicate the test outcome. + type Outcome struct { + Message string `xml:"message,omitempty"` + } + + // TestCase represents an individual test case as part of a suite. Note + // that a JUnit XML incorporates both the "component" and "assertion" + // levels of our model: we pretend that component is a class name and + // assertion is a method name in order to match with the Java-flavored + // expectations of JUnit XML, which are hopefully close enough to get + // a test result rendering that's useful to humans. + type TestCase struct { + AssertionName string `xml:"name"` + ComponentName string `xml:"classname"` + + // These fields represent the different outcomes of a TestCase. Only one + // of these should be populated in each TestCase; this awkward + // structure is just to make this play nicely with encoding/xml's + // expecatations. + Skipped *Outcome `xml:"skipped,omitempty"` + Error *Outcome `xml:"error,omitempty"` + Failure *Outcome `xml:"failure,omitempty"` + + Stderr string `xml:"system-out,omitempty"` + } + + // TestSuite represents an individual test suite, of potentially many + // in a JUnit XML document. + type TestSuite struct { + Name string `xml:"name"` + TotalCount int `xml:"tests"` + SkippedCount int `xml:"skipped"` + ErrorCount int `xml:"errors"` + FailureCount int `xml:"failures"` + Cases []*TestCase `xml:"testcase"` + } + + // TestSuites represents the root element of the XML document. + type TestSuites struct { + XMLName struct{} `xml:"testsuites"` + ErrorCount int `xml:"errors"` + FailureCount int `xml:"failures"` + TotalCount int `xml:"tests"` + Suites []*TestSuite `xml:"testsuite"` + } + + xmlSuites := TestSuites{} + suiteNames := make([]string, 0, len(results)) + for suiteName := range results { + suiteNames = append(suiteNames, suiteName) + } + sort.Strings(suiteNames) + for _, suiteName := range suiteNames { + suite := results[suiteName] + + xmlSuite := &TestSuite{ + Name: suiteName, + } + xmlSuites.Suites = append(xmlSuites.Suites, xmlSuite) + + componentNames := make([]string, 0, len(suite.Components)) + for componentName := range suite.Components { + componentNames = append(componentNames, componentName) + } + for _, componentName := range componentNames { + component := suite.Components[componentName] + + assertionNames := make([]string, 0, len(component.Assertions)) + for assertionName := range component.Assertions { + assertionNames = append(assertionNames, assertionName) + } + sort.Strings(assertionNames) + + for _, assertionName := range assertionNames { + assertion := component.Assertions[assertionName] + xmlSuites.TotalCount++ + xmlSuite.TotalCount++ + + xmlCase := &TestCase{ + ComponentName: componentName, + AssertionName: assertionName, + } + xmlSuite.Cases = append(xmlSuite.Cases, xmlCase) + + switch assertion.Outcome { + case moduletest.Pending: + // We represent "pending" cases -- cases blocked by + // upstream errors -- as if they were "skipped" in JUnit + // terms, because we didn't actually check them and so + // can't say whether they succeeded or not. + xmlSuite.SkippedCount++ + xmlCase.Skipped = &Outcome{ + Message: assertion.Message, + } + case moduletest.Failed: + xmlSuites.FailureCount++ + xmlSuite.FailureCount++ + xmlCase.Failure = &Outcome{ + Message: assertion.Message, + } + case moduletest.Error: + xmlSuites.ErrorCount++ + xmlSuite.ErrorCount++ + xmlCase.Error = &Outcome{ + Message: assertion.Message, + } + + // We'll also include the diagnostics in the "stderr" + // portion of the output, so they'll hopefully be visible + // in a test log viewer in JUnit-XML-Consuming CI systems. + var buf strings.Builder + for _, diag := range assertion.Diagnostics { + diagStr := format.DiagnosticPlain(diag, nil, 68) + buf.WriteString(diagStr) + } + xmlCase.Stderr = buf.String() + } + + } + } + } + + xmlOut, err := xml.MarshalIndent(&xmlSuites, "", " ") + if err != nil { + // If marshalling fails then that's a bug in the code above, + // because we should always be producing a value that is + // accepted by encoding/xml. + panic(fmt.Sprintf("invalid values to marshal as JUnit XML: %s", err)) + } + + err = ioutil.WriteFile(filename, xmlOut, 0644) + if err != nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to write JUnit XML file", + fmt.Sprintf( + "Could not create %s to record the test results in JUnit XML format: %s.", + filename, + err, + ), + )) + } + + return diags +} + +func (v *testHuman) eprintRuleHeading(color, prefix, extra string) { + const lineCell string = "─" + textLen := len(prefix) + len(": ") + len(extra) + spacingLen := 2 + leftLineLen := 3 + + rightLineLen := 0 + width := v.streams.Stderr.Columns() + if (textLen + spacingLen + leftLineLen) < (width - 1) { + // (we allow an extra column at the end because some terminals can't + // print in the final column without wrapping to the next line) + rightLineLen = width - (textLen + spacingLen + leftLineLen) - 1 + } + + colorCode := "[" + color + "]" + + // We'll prepare what we're going to print in memory first, so that we can + // send it all to stderr in one write in case other programs are also + // concurrently trying to write to the terminal for some reason. + var buf strings.Builder + buf.WriteString(v.colorize.Color(colorCode + strings.Repeat(lineCell, leftLineLen))) + buf.WriteByte(' ') + buf.WriteString(v.colorize.Color("[bold]" + colorCode + prefix + ":")) + buf.WriteByte(' ') + buf.WriteString(extra) + if rightLineLen > 0 { + buf.WriteByte(' ') + buf.WriteString(v.colorize.Color(colorCode + strings.Repeat(lineCell, rightLineLen))) + } + v.streams.Eprintln(buf.String()) +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/test_test.go b/vendor/github.com/hashicorp/terraform/command/views/test_test.go new file mode 100644 index 00000000..ad339b30 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/test_test.go @@ -0,0 +1,32 @@ +package views + +import ( + "strings" + "testing" + + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/internal/moduletest" + "github.com/hashicorp/terraform/internal/terminal" +) + +func TestTest(t *testing.T) { + streams, close := terminal.StreamsForTesting(t) + baseView := NewView(streams) + view := NewTest(baseView, arguments.TestOutput{ + JUnitXMLFile: "", + }) + + results := map[string]*moduletest.Suite{} + view.Results(results) + + output := close(t) + gotOutput := strings.TrimSpace(output.All()) + wantOutput := `No tests defined. This module doesn't have any test suites to run.` + if gotOutput != wantOutput { + t.Errorf("wrong output\ngot:\n%s\nwant:\n%s", gotOutput, wantOutput) + } + + // TODO: Test more at this layer. For now, the main UI output tests for + // the "terraform test" command are in the command package as part of + // the overall command tests. +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/validate.go b/vendor/github.com/hashicorp/terraform/command/views/validate.go new file mode 100644 index 00000000..864f4e29 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/validate.go @@ -0,0 +1,138 @@ +package views + +import ( + "encoding/json" + "fmt" + + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/format" + viewsjson "github.com/hashicorp/terraform/command/views/json" + "github.com/hashicorp/terraform/tfdiags" +) + +// The Validate is used for the validate command. +type Validate interface { + // Results renders the diagnostics returned from a validation walk, and + // returns a CLI exit code: 0 if there are no errors, 1 otherwise + Results(diags tfdiags.Diagnostics) int + + // Diagnostics renders early diagnostics, resulting from argument parsing. + Diagnostics(diags tfdiags.Diagnostics) +} + +// NewValidate returns an initialized Validate implementation for the given ViewType. +func NewValidate(vt arguments.ViewType, view *View) Validate { + switch vt { + case arguments.ViewJSON: + return &ValidateJSON{view: view} + case arguments.ViewHuman: + return &ValidateHuman{view: view} + default: + panic(fmt.Sprintf("unknown view type %v", vt)) + } +} + +// The ValidateHuman implementation renders diagnostics in a human-readable form, +// along with a success/failure message if Terraform is able to execute the +// validation walk. +type ValidateHuman struct { + view *View +} + +var _ Validate = (*ValidateHuman)(nil) + +func (v *ValidateHuman) Results(diags tfdiags.Diagnostics) int { + columns := v.view.outputColumns() + + if len(diags) == 0 { + v.view.streams.Println(format.WordWrap(v.view.colorize.Color(validateSuccess), columns)) + } else { + v.Diagnostics(diags) + + if !diags.HasErrors() { + v.view.streams.Println(format.WordWrap(v.view.colorize.Color(validateWarnings), columns)) + } + } + + if diags.HasErrors() { + return 1 + } + return 0 +} + +const validateSuccess = "[green][bold]Success![reset] The configuration is valid.\n" + +const validateWarnings = "[green][bold]Success![reset] The configuration is valid, but there were some validation warnings as shown above.\n" + +func (v *ValidateHuman) Diagnostics(diags tfdiags.Diagnostics) { + v.view.Diagnostics(diags) +} + +// The ValidateJSON implementation renders validation results as a JSON object. +// This object includes top-level fields summarizing the result, and an array +// of JSON diagnostic objects. +type ValidateJSON struct { + view *View +} + +var _ Validate = (*ValidateJSON)(nil) + +func (v *ValidateJSON) Results(diags tfdiags.Diagnostics) int { + // FormatVersion represents the version of the json format and will be + // incremented for any change to this format that requires changes to a + // consuming parser. + const FormatVersion = "0.1" + + type Output struct { + FormatVersion string `json:"format_version"` + + // We include some summary information that is actually redundant + // with the detailed diagnostics, but avoids the need for callers + // to re-implement our logic for deciding these. + Valid bool `json:"valid"` + ErrorCount int `json:"error_count"` + WarningCount int `json:"warning_count"` + Diagnostics []*viewsjson.Diagnostic `json:"diagnostics"` + } + + output := Output{ + FormatVersion: FormatVersion, + Valid: true, // until proven otherwise + } + configSources := v.view.configSources() + for _, diag := range diags { + output.Diagnostics = append(output.Diagnostics, viewsjson.NewDiagnostic(diag, configSources)) + + switch diag.Severity() { + case tfdiags.Error: + output.ErrorCount++ + output.Valid = false + case tfdiags.Warning: + output.WarningCount++ + } + } + if output.Diagnostics == nil { + // Make sure this always appears as an array in our output, since + // this is easier to consume for dynamically-typed languages. + output.Diagnostics = []*viewsjson.Diagnostic{} + } + + j, err := json.MarshalIndent(&output, "", " ") + if err != nil { + // Should never happen because we fully-control the input here + panic(err) + } + v.view.streams.Println(string(j)) + + if diags.HasErrors() { + return 1 + } + return 0 +} + +// Diagnostics should only be called if the validation walk cannot be executed. +// In this case, we choose to render human-readable diagnostic output, +// primarily for backwards compatibility. +func (v *ValidateJSON) Diagnostics(diags tfdiags.Diagnostics) { + v.view.Diagnostics(diags) +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/validate_test.go b/vendor/github.com/hashicorp/terraform/command/views/validate_test.go new file mode 100644 index 00000000..65969a41 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/validate_test.go @@ -0,0 +1,133 @@ +package views + +import ( + "encoding/json" + "strings" + "testing" + + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/internal/terminal" + "github.com/hashicorp/terraform/tfdiags" +) + +func TestValidateHuman(t *testing.T) { + testCases := map[string]struct { + diag tfdiags.Diagnostic + wantSuccess bool + wantSubstring string + }{ + "success": { + nil, + true, + "The configuration is valid.", + }, + "warning": { + tfdiags.Sourceless( + tfdiags.Warning, + "Your shoelaces are untied", + "Watch out, or you'll trip!", + ), + true, + "The configuration is valid, but there were some validation warnings", + }, + "error": { + tfdiags.Sourceless( + tfdiags.Error, + "Configuration is missing random_pet", + "Every configuration should have a random_pet.", + ), + false, + "Error: Configuration is missing random_pet", + }, + } + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + view := NewView(streams) + view.Configure(&arguments.View{NoColor: true}) + v := NewValidate(arguments.ViewHuman, view) + + var diags tfdiags.Diagnostics + + if tc.diag != nil { + diags = diags.Append(tc.diag) + } + + ret := v.Results(diags) + + if tc.wantSuccess && ret != 0 { + t.Errorf("expected 0 return code, got %d", ret) + } else if !tc.wantSuccess && ret != 1 { + t.Errorf("expected 1 return code, got %d", ret) + } + + got := done(t).All() + if strings.Contains(got, "Success!") != tc.wantSuccess { + t.Errorf("unexpected output:\n%s", got) + } + if !strings.Contains(got, tc.wantSubstring) { + t.Errorf("expected output to include %q, but was:\n%s", tc.wantSubstring, got) + } + }) + } +} + +func TestValidateJSON(t *testing.T) { + testCases := map[string]struct { + diag tfdiags.Diagnostic + wantSuccess bool + }{ + "success": { + nil, + true, + }, + "warning": { + tfdiags.Sourceless( + tfdiags.Warning, + "Your shoelaces are untied", + "Watch out, or you'll trip!", + ), + true, + }, + "error": { + tfdiags.Sourceless( + tfdiags.Error, + "Configuration is missing random_pet", + "Every configuration should have a random_pet.", + ), + false, + }, + } + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + view := NewView(streams) + view.Configure(&arguments.View{NoColor: true}) + v := NewValidate(arguments.ViewJSON, view) + + var diags tfdiags.Diagnostics + + if tc.diag != nil { + diags = diags.Append(tc.diag) + } + + ret := v.Results(diags) + + if tc.wantSuccess && ret != 0 { + t.Errorf("expected 0 return code, got %d", ret) + } else if !tc.wantSuccess && ret != 1 { + t.Errorf("expected 1 return code, got %d", ret) + } + + got := done(t).All() + + // Make sure the result looks like JSON; we comprehensively test + // the structure of this output in the command package tests. + var result map[string]interface{} + + if err := json.Unmarshal([]byte(got), &result); err != nil { + t.Fatal(err) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/command/views/view.go b/vendor/github.com/hashicorp/terraform/command/views/view.go new file mode 100644 index 00000000..f59fd440 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/command/views/view.go @@ -0,0 +1,162 @@ +package views + +import ( + "github.com/hashicorp/terraform/command/arguments" + "github.com/hashicorp/terraform/command/format" + "github.com/hashicorp/terraform/internal/terminal" + "github.com/hashicorp/terraform/tfdiags" + "github.com/mitchellh/colorstring" +) + +// View is the base layer for command views, encapsulating a set of I/O +// streams, a colorize implementation, and implementing a human friendly view +// for diagnostics. +type View struct { + streams *terminal.Streams + colorize *colorstring.Colorize + + compactWarnings bool + + // When this is true it's a hint that Terraform is being run indirectly + // via a wrapper script or other automation and so we may wish to replace + // direct examples of commands to run with more conceptual directions. + // However, we only do this on a best-effort basis, typically prioritizing + // the messages that users are most likely to see. + runningInAutomation bool + + // This unfortunate wart is required to enable rendering of diagnostics which + // have associated source code in the configuration. This function pointer + // will be dereferenced as late as possible when rendering diagnostics in + // order to access the config loader cache. + configSources func() map[string][]byte +} + +// Initialize a View with the given streams, a disabled colorize object, and a +// no-op configSources callback. +func NewView(streams *terminal.Streams) *View { + return &View{ + streams: streams, + colorize: &colorstring.Colorize{ + Colors: colorstring.DefaultColors, + Disable: true, + Reset: true, + }, + configSources: func() map[string][]byte { return nil }, + } +} + +// SetRunningInAutomation modifies the view's "running in automation" flag, +// which causes some slight adjustments to certain messages that would normally +// suggest specific Terraform commands to run, to make more conceptual gestures +// instead for situations where the user isn't running Terraform directly. +// +// For convenient use during initialization (in conjunction with NewView), +// SetRunningInAutomation returns the reciever after modifying it. +func (v *View) SetRunningInAutomation(new bool) *View { + v.runningInAutomation = new + return v +} + +func (v *View) RunningInAutomation() bool { + return v.runningInAutomation +} + +// Configure applies the global view configuration flags. +func (v *View) Configure(view *arguments.View) { + v.colorize.Disable = view.NoColor + v.compactWarnings = view.CompactWarnings +} + +// SetConfigSources overrides the default no-op callback with a new function +// pointer, and should be called when the config loader is initialized. +func (v *View) SetConfigSources(cb func() map[string][]byte) { + v.configSources = cb +} + +// Diagnostics renders a set of warnings and errors in human-readable form. +// Warnings are printed to stdout, and errors to stderr. +func (v *View) Diagnostics(diags tfdiags.Diagnostics) { + diags.Sort() + + if len(diags) == 0 { + return + } + + diags = diags.ConsolidateWarnings(1) + + // Since warning messages are generally competing + if v.compactWarnings { + // If the user selected compact warnings and all of the diagnostics are + // warnings then we'll use a more compact representation of the warnings + // that only includes their summaries. + // We show full warnings if there are also errors, because a warning + // can sometimes serve as good context for a subsequent error. + useCompact := true + for _, diag := range diags { + if diag.Severity() != tfdiags.Warning { + useCompact = false + break + } + } + if useCompact { + msg := format.DiagnosticWarningsCompact(diags, v.colorize) + msg = "\n" + msg + "\nTo see the full warning notes, run Terraform without -compact-warnings.\n" + v.streams.Print(msg) + return + } + } + + for _, diag := range diags { + var msg string + if v.colorize.Disable { + msg = format.DiagnosticPlain(diag, v.configSources(), v.streams.Stderr.Columns()) + } else { + msg = format.Diagnostic(diag, v.configSources(), v.colorize, v.streams.Stderr.Columns()) + } + + if diag.Severity() == tfdiags.Error { + v.streams.Eprint(msg) + } else { + v.streams.Print(msg) + } + } +} + +// HelpPrompt is intended to be called from commands which fail to parse all +// of their CLI arguments successfully. It refers users to the full help output +// rather than rendering it directly, which can be overwhelming and confusing. +func (v *View) HelpPrompt(command string) { + v.streams.Eprintf(helpPrompt, command) +} + +const helpPrompt = ` +For more help on using this command, run: + terraform %s -help +` + +// outputColumns returns the number of text character cells any non-error +// output should be wrapped to. +// +// This is the number of columns to use if you are calling v.streams.Print or +// related functions. +func (v *View) outputColumns() int { + return v.streams.Stdout.Columns() +} + +// errorColumns returns the number of text character cells any error +// output should be wrapped to. +// +// This is the number of columns to use if you are calling v.streams.Eprint +// or related functions. +func (v *View) errorColumns() int { + return v.streams.Stderr.Columns() +} + +// outputHorizRule will call v.streams.Println with enough horizontal line +// characters to fill an entire row of output. +// +// If UI color is enabled, the rule will get a dark grey coloring to try to +// visually de-emphasize it. +func (v *View) outputHorizRule() { + v.streams.Println(format.HorizontalRule(v.colorize, v.outputColumns())) +} diff --git a/vendor/github.com/hashicorp/terraform/command/webbrowser/native.go b/vendor/github.com/hashicorp/terraform/command/webbrowser/native.go index 77d503a2..4e8281ce 100644 --- a/vendor/github.com/hashicorp/terraform/command/webbrowser/native.go +++ b/vendor/github.com/hashicorp/terraform/command/webbrowser/native.go @@ -2,8 +2,6 @@ package webbrowser import ( "github.com/pkg/browser" - "os/exec" - "strings" ) // NewNativeLauncher creates and returns a Launcher that will attempt to interact @@ -15,18 +13,6 @@ func NewNativeLauncher() Launcher { type nativeLauncher struct{} -func hasProgram(name string) bool { - _, err := exec.LookPath(name) - return err == nil -} - func (l nativeLauncher) OpenURL(url string) error { - // Windows Subsystem for Linux (bash for Windows) doesn't have xdg-open available - // but you can execute cmd.exe from there; try to identify it - if !hasProgram("xdg-open") && hasProgram("cmd.exe") { - r := strings.NewReplacer("&", "^&") - exec.Command("cmd.exe", "/c", "start", r.Replace(url)).Run() - } - return browser.OpenURL(url) } diff --git a/vendor/github.com/hashicorp/terraform/command/workspace_command.go b/vendor/github.com/hashicorp/terraform/command/workspace_command.go index 3de83dbc..993b2a87 100644 --- a/vendor/github.com/hashicorp/terraform/command/workspace_command.go +++ b/vendor/github.com/hashicorp/terraform/command/workspace_command.go @@ -15,7 +15,7 @@ type WorkspaceCommand struct { } func (c *WorkspaceCommand) Run(args []string) int { - args = c.Meta.process(args) + c.Meta.process(args) envCommandShowWarning(c.Ui, c.LegacyName) cmdFlags := c.Meta.extendedFlagSet("workspace") @@ -27,7 +27,7 @@ func (c *WorkspaceCommand) Run(args []string) int { func (c *WorkspaceCommand) Help() string { helpText := ` -Usage: terraform workspace +Usage: terraform [global options] workspace new, list, show, select and delete Terraform workspaces. diff --git a/vendor/github.com/hashicorp/terraform/command/workspace_command_test.go b/vendor/github.com/hashicorp/terraform/command/workspace_command_test.go index df4076b4..99c156bb 100644 --- a/vendor/github.com/hashicorp/terraform/command/workspace_command_test.go +++ b/vendor/github.com/hashicorp/terraform/command/workspace_command_test.go @@ -13,8 +13,9 @@ import ( "github.com/hashicorp/terraform/backend/remote-state/inmem" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/states/statemgr" - "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/cli" + + legacy "github.com/hashicorp/terraform/internal/legacy/terraform" ) func TestWorkspace_createAndChange(t *testing.T) { @@ -33,7 +34,8 @@ func TestWorkspace_createAndChange(t *testing.T) { args := []string{"test"} ui := new(cli.MockUi) - newCmd.Meta = Meta{Ui: ui} + view, _ := testView(t) + newCmd.Meta = Meta{Ui: ui, View: view} if code := newCmd.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter) } @@ -46,7 +48,7 @@ func TestWorkspace_createAndChange(t *testing.T) { selCmd := &WorkspaceSelectCommand{} args = []string{backend.DefaultStateName} ui = new(cli.MockUi) - selCmd.Meta = Meta{Ui: ui} + selCmd.Meta = Meta{Ui: ui, View: view} if code := selCmd.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter) } @@ -82,8 +84,9 @@ func TestWorkspace_createAndList(t *testing.T) { // create multiple workspaces for _, env := range envs { ui := new(cli.MockUi) + view, _ := testView(t) newCmd := &WorkspaceNewCommand{ - Meta: Meta{Ui: ui}, + Meta: Meta{Ui: ui, View: view}, } if code := newCmd.Run([]string{env}); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter) @@ -92,7 +95,8 @@ func TestWorkspace_createAndList(t *testing.T) { listCmd := &WorkspaceListCommand{} ui := new(cli.MockUi) - listCmd.Meta = Meta{Ui: ui} + view, _ := testView(t) + listCmd.Meta = Meta{Ui: ui, View: view} if code := listCmd.Run(nil); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter) @@ -127,7 +131,8 @@ func TestWorkspace_createAndShow(t *testing.T) { // make sure current workspace show outputs "default" showCmd := &WorkspaceShowCommand{} ui := new(cli.MockUi) - showCmd.Meta = Meta{Ui: ui} + view, _ := testView(t) + showCmd.Meta = Meta{Ui: ui, View: view} if code := showCmd.Run(nil); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter) @@ -146,21 +151,21 @@ func TestWorkspace_createAndShow(t *testing.T) { // create test_a workspace ui = new(cli.MockUi) - newCmd.Meta = Meta{Ui: ui} + newCmd.Meta = Meta{Ui: ui, View: view} if code := newCmd.Run(env); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter) } selCmd := &WorkspaceSelectCommand{} ui = new(cli.MockUi) - selCmd.Meta = Meta{Ui: ui} + selCmd.Meta = Meta{Ui: ui, View: view} if code := selCmd.Run(env); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter) } showCmd = &WorkspaceShowCommand{} ui = new(cli.MockUi) - showCmd.Meta = Meta{Ui: ui} + showCmd.Meta = Meta{Ui: ui, View: view} if code := showCmd.Run(nil); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter) @@ -187,8 +192,9 @@ func TestWorkspace_createInvalid(t *testing.T) { // create multiple workspaces for _, env := range envs { ui := new(cli.MockUi) + view, _ := testView(t) newCmd := &WorkspaceNewCommand{ - Meta: Meta{Ui: ui}, + Meta: Meta{Ui: ui, View: view}, } if code := newCmd.Run([]string{env}); code == 0 { t.Fatalf("expected failure: \n%s", ui.OutputWriter) @@ -198,7 +204,8 @@ func TestWorkspace_createInvalid(t *testing.T) { // list workspaces to make sure none were created listCmd := &WorkspaceListCommand{} ui := new(cli.MockUi) - listCmd.Meta = Meta{Ui: ui} + view, _ := testView(t) + listCmd.Meta = Meta{Ui: ui, View: view} if code := listCmd.Run(nil); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter) @@ -221,8 +228,9 @@ func TestWorkspace_createWithState(t *testing.T) { // init the backend ui := new(cli.MockUi) + view, _ := testView(t) initCmd := &InitCommand{ - Meta: Meta{Ui: ui}, + Meta: Meta{Ui: ui, View: view}, } if code := initCmd.Run([]string{}); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) @@ -256,7 +264,7 @@ func TestWorkspace_createWithState(t *testing.T) { args := []string{"-state", "test.tfstate", workspace} ui = new(cli.MockUi) newCmd := &WorkspaceNewCommand{ - Meta: Meta{Ui: ui}, + Meta: Meta{Ui: ui, View: view}, } if code := newCmd.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter) @@ -302,8 +310,9 @@ func TestWorkspace_delete(t *testing.T) { } ui := new(cli.MockUi) + view, _ := testView(t) delCmd := &WorkspaceDeleteCommand{ - Meta: Meta{Ui: ui}, + Meta: Meta{Ui: ui, View: view}, } current, _ := delCmd.Workspace() @@ -351,8 +360,9 @@ func TestWorkspace_deleteInvalid(t *testing.T) { } ui := new(cli.MockUi) + view, _ := testView(t) delCmd := &WorkspaceDeleteCommand{ - Meta: Meta{Ui: ui}, + Meta: Meta{Ui: ui, View: view}, } // delete the workspace @@ -379,14 +389,14 @@ func TestWorkspace_deleteWithState(t *testing.T) { } // create a non-empty state - originalState := &terraform.State{ - Modules: []*terraform.ModuleState{ - &terraform.ModuleState{ + originalState := &legacy.State{ + Modules: []*legacy.ModuleState{ + &legacy.ModuleState{ Path: []string{"root"}, - Resources: map[string]*terraform.ResourceState{ - "test_instance.foo": &terraform.ResourceState{ + Resources: map[string]*legacy.ResourceState{ + "test_instance.foo": &legacy.ResourceState{ Type: "test_instance", - Primary: &terraform.InstanceState{ + Primary: &legacy.InstanceState{ ID: "bar", }, }, @@ -400,13 +410,14 @@ func TestWorkspace_deleteWithState(t *testing.T) { t.Fatal(err) } defer f.Close() - if err := terraform.WriteState(originalState, f); err != nil { + if err := legacy.WriteState(originalState, f); err != nil { t.Fatal(err) } ui := new(cli.MockUi) + view, _ := testView(t) delCmd := &WorkspaceDeleteCommand{ - Meta: Meta{Ui: ui}, + Meta: Meta{Ui: ui, View: view}, } args := []string{"test"} if code := delCmd.Run(args); code == 0 { diff --git a/vendor/github.com/hashicorp/terraform/command/workspace_delete.go b/vendor/github.com/hashicorp/terraform/command/workspace_delete.go index ebb7c5ee..0fb90c42 100644 --- a/vendor/github.com/hashicorp/terraform/command/workspace_delete.go +++ b/vendor/github.com/hashicorp/terraform/command/workspace_delete.go @@ -1,12 +1,13 @@ package command import ( - "context" "fmt" "strings" "time" + "github.com/hashicorp/terraform/command/arguments" "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/tfdiags" "github.com/mitchellh/cli" "github.com/posener/complete" @@ -35,8 +36,8 @@ func (c *WorkspaceDeleteCommand) Run(args []string) int { } args = cmdFlags.Args() - if len(args) == 0 { - c.Ui.Error("expected NAME.\n") + if len(args) != 1 { + c.Ui.Error("Expected a single argument: NAME.\n") return cli.RunResultHelp } @@ -65,6 +66,9 @@ func (c *WorkspaceDeleteCommand) Run(args []string) int { return 1 } + // This command will not write state + c.ignoreRemoteBackendVersionConflict(b) + workspaces, err := b.Workspaces() if err != nil { c.Ui.Error(err.Error()) @@ -104,9 +108,9 @@ func (c *WorkspaceDeleteCommand) Run(args []string) int { var stateLocker clistate.Locker if stateLock { - stateLocker = clistate.NewLocker(context.Background(), stateLockTimeout, c.Ui, c.Colorize()) - if err := stateLocker.Lock(stateMgr, "workspace_delete"); err != nil { - c.Ui.Error(fmt.Sprintf("Error locking state: %s", err)) + stateLocker = clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View)) + if diags := stateLocker.Lock(stateMgr, "state-replace-provider"); diags.HasErrors() { + c.showDiagnostics(diags) return 1 } } else { @@ -115,7 +119,7 @@ func (c *WorkspaceDeleteCommand) Run(args []string) int { if err := stateMgr.RefreshState(); err != nil { // We need to release the lock before exit - stateLocker.Unlock(nil) + stateLocker.Unlock() c.Ui.Error(err.Error()) return 1 } @@ -124,7 +128,7 @@ func (c *WorkspaceDeleteCommand) Run(args []string) int { if hasResources && !force { // We need to release the lock before exit - stateLocker.Unlock(nil) + stateLocker.Unlock() c.Ui.Error(fmt.Sprintf(strings.TrimSpace(envNotEmpty), workspace)) return 1 } @@ -138,7 +142,7 @@ func (c *WorkspaceDeleteCommand) Run(args []string) int { // state deletion, i.e. in a CI environment. Adding Delete() as a // required method of States would allow the removal of the resource to // be delegated from the Backend to the State itself. - stateLocker.Unlock(nil) + stateLocker.Unlock() err = b.DeleteWorkspace(workspace) if err != nil { @@ -179,7 +183,7 @@ func (c *WorkspaceDeleteCommand) AutocompleteFlags() complete.Flags { func (c *WorkspaceDeleteCommand) Help() string { helpText := ` -Usage: terraform workspace delete [OPTIONS] NAME [DIR] +Usage: terraform [global options] workspace delete [OPTIONS] NAME Delete a Terraform workspace diff --git a/vendor/github.com/hashicorp/terraform/command/workspace_list.go b/vendor/github.com/hashicorp/terraform/command/workspace_list.go index 03fdf4a5..dda891d4 100644 --- a/vendor/github.com/hashicorp/terraform/command/workspace_list.go +++ b/vendor/github.com/hashicorp/terraform/command/workspace_list.go @@ -51,6 +51,9 @@ func (c *WorkspaceListCommand) Run(args []string) int { return 1 } + // This command will not write state + c.ignoreRemoteBackendVersionConflict(b) + states, err := b.Workspaces() if err != nil { c.Ui.Error(err.Error()) @@ -88,7 +91,7 @@ func (c *WorkspaceListCommand) AutocompleteFlags() complete.Flags { func (c *WorkspaceListCommand) Help() string { helpText := ` -Usage: terraform workspace list [DIR] +Usage: terraform [global options] workspace list List Terraform workspaces. diff --git a/vendor/github.com/hashicorp/terraform/command/workspace_new.go b/vendor/github.com/hashicorp/terraform/command/workspace_new.go index 549beeba..ce074d66 100644 --- a/vendor/github.com/hashicorp/terraform/command/workspace_new.go +++ b/vendor/github.com/hashicorp/terraform/command/workspace_new.go @@ -1,13 +1,14 @@ package command import ( - "context" "fmt" "os" "strings" "time" + "github.com/hashicorp/terraform/command/arguments" "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/states/statefile" "github.com/hashicorp/terraform/tfdiags" "github.com/mitchellh/cli" @@ -37,7 +38,7 @@ func (c *WorkspaceNewCommand) Run(args []string) int { } args = cmdFlags.Args() - if len(args) == 0 { + if len(args) != 1 { c.Ui.Error("Expected a single argument: NAME.\n") return cli.RunResultHelp } @@ -81,6 +82,9 @@ func (c *WorkspaceNewCommand) Run(args []string) int { return 1 } + // This command will not write state + c.ignoreRemoteBackendVersionConflict(b) + workspaces, err := b.Workspaces() if err != nil { c.Ui.Error(fmt.Sprintf("Failed to get configured named states: %s", err)) @@ -121,12 +125,16 @@ func (c *WorkspaceNewCommand) Run(args []string) int { } if stateLock { - stateLocker := clistate.NewLocker(context.Background(), stateLockTimeout, c.Ui, c.Colorize()) - if err := stateLocker.Lock(stateMgr, "workspace_new"); err != nil { - c.Ui.Error(fmt.Sprintf("Error locking state: %s", err)) + stateLocker := clistate.NewLocker(c.stateLockTimeout, views.NewStateLocker(arguments.ViewHuman, c.View)) + if diags := stateLocker.Lock(stateMgr, "workspace-new"); diags.HasErrors() { + c.showDiagnostics(diags) return 1 } - defer stateLocker.Unlock(nil) + defer func() { + if diags := stateLocker.Unlock(); diags.HasErrors() { + c.showDiagnostics(diags) + } + }() } // read the existing state file @@ -173,7 +181,7 @@ func (c *WorkspaceNewCommand) AutocompleteFlags() complete.Flags { func (c *WorkspaceNewCommand) Help() string { helpText := ` -Usage: terraform workspace new [OPTIONS] NAME [DIR] +Usage: terraform [global options] workspace new [OPTIONS] NAME Create a new Terraform workspace. diff --git a/vendor/github.com/hashicorp/terraform/command/workspace_select.go b/vendor/github.com/hashicorp/terraform/command/workspace_select.go index 9667ff9d..db0d67b9 100644 --- a/vendor/github.com/hashicorp/terraform/command/workspace_select.go +++ b/vendor/github.com/hashicorp/terraform/command/workspace_select.go @@ -26,7 +26,7 @@ func (c *WorkspaceSelectCommand) Run(args []string) int { } args = cmdFlags.Args() - if len(args) == 0 { + if len(args) != 1 { c.Ui.Error("Expected a single argument: NAME.\n") return cli.RunResultHelp } @@ -67,6 +67,9 @@ func (c *WorkspaceSelectCommand) Run(args []string) int { return 1 } + // This command will not write state + c.ignoreRemoteBackendVersionConflict(b) + name := args[0] if !validWorkspaceName(name) { c.Ui.Error(fmt.Sprintf(envInvalidName, name)) @@ -126,7 +129,7 @@ func (c *WorkspaceSelectCommand) AutocompleteFlags() complete.Flags { func (c *WorkspaceSelectCommand) Help() string { helpText := ` -Usage: terraform workspace select NAME [DIR] +Usage: terraform [global options] workspace select NAME Select a different Terraform workspace. diff --git a/vendor/github.com/hashicorp/terraform/command/workspace_show.go b/vendor/github.com/hashicorp/terraform/command/workspace_show.go index 23d438c5..f1f372e8 100644 --- a/vendor/github.com/hashicorp/terraform/command/workspace_show.go +++ b/vendor/github.com/hashicorp/terraform/command/workspace_show.go @@ -40,7 +40,7 @@ func (c *WorkspaceShowCommand) AutocompleteFlags() complete.Flags { func (c *WorkspaceShowCommand) Help() string { helpText := ` -Usage: terraform workspace show +Usage: terraform [global options] workspace show Show the name of the current workspace. ` diff --git a/vendor/github.com/hashicorp/terraform/commands.go b/vendor/github.com/hashicorp/terraform/commands.go index 1e947d68..dcb8d1c1 100644 --- a/vendor/github.com/hashicorp/terraform/commands.go +++ b/vendor/github.com/hashicorp/terraform/commands.go @@ -13,8 +13,10 @@ import ( "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/command" "github.com/hashicorp/terraform/command/cliconfig" + "github.com/hashicorp/terraform/command/views" "github.com/hashicorp/terraform/command/webbrowser" "github.com/hashicorp/terraform/internal/getproviders" + "github.com/hashicorp/terraform/internal/terminal" pluginDiscovery "github.com/hashicorp/terraform/plugin/discovery" ) @@ -25,18 +27,30 @@ const runningInAutomationEnvName = "TF_IN_AUTOMATION" // Commands is the mapping of all the available Terraform commands. var Commands map[string]cli.CommandFactory -var PlumbingCommands map[string]struct{} + +// PrimaryCommands is an ordered sequence of the top-level commands (not +// subcommands) that we emphasize at the top of our help output. This is +// ordered so that we can show them in the typical workflow order, rather +// than in alphabetical order. Anything not in this sequence or in the +// HiddenCommands set appears under "all other commands". +var PrimaryCommands []string + +// HiddenCommands is a set of top-level commands (not subcommands) that are +// not advertised in the top-level help at all. This is typically because +// they are either just stubs that return an error message about something +// no longer being supported or backward-compatibility aliases for other +// commands. +// +// No commands in the PrimaryCommands sequence should also appear in the +// HiddenCommands set, because that would be rather silly. +var HiddenCommands map[string]struct{} // Ui is the cli.Ui used for communicating to the outside world. var Ui cli.Ui -const ( - ErrorPrefix = "e:" - OutputPrefix = "o:" -) - func initCommands( originalWorkingDir string, + streams *terminal.Streams, config *cliconfig.Config, services *disco.Disco, providerSrc getproviders.Source, @@ -67,6 +81,8 @@ func initCommands( meta := command.Meta{ OriginalWorkingDir: originalWorkingDir, + Streams: streams, + View: views.NewView(streams).SetRunningInAutomation(inAutomation), Color: true, GlobalPluginDirs: globalPluginDirs(), @@ -89,19 +105,10 @@ func initCommands( // The command list is included in the terraform -help // output, which is in turn included in the docs at - // website/source/docs/commands/index.html.markdown; if you + // website/docs/cli/commands/index.html.markdown; if you // add, remove or reclassify commands then consider updating // that to match. - PlumbingCommands = map[string]struct{}{ - "state": struct{}{}, // includes all subcommands - "debug": struct{}{}, // includes all subcommands - "force-unlock": struct{}{}, - "push": struct{}{}, - "0.12upgrade": struct{}{}, - "0.13upgrade": struct{}{}, - } - Commands = map[string]cli.CommandFactory{ "apply": func() (cli.Command, error) { return &command.ApplyCommand{ @@ -187,12 +194,6 @@ func initCommands( }, nil }, - "internal-plugin": func() (cli.Command, error) { - return &command.InternalPluginCommand{ - Meta: meta, - }, nil - }, - "login": func() (cli.Command, error) { return &command.LoginCommand{ Meta: meta, @@ -265,6 +266,12 @@ func initCommands( }, nil }, + "test": func() (cli.Command, error) { + return &command.TestCommand{ + Meta: meta, + }, nil + }, + "validate": func() (cli.Command, error) { return &command.ValidateCommand{ Meta: meta, @@ -274,9 +281,9 @@ func initCommands( "version": func() (cli.Command, error) { return &command.VersionCommand{ Meta: meta, - Revision: GitCommit, Version: Version, VersionPrerelease: VersionPrerelease, + Platform: getproviders.CurrentPlatform, CheckFunc: commandVersionCheck, }, nil }, @@ -327,24 +334,6 @@ func initCommands( // Plumbing //----------------------------------------------------------- - "0.12upgrade": func() (cli.Command, error) { - return &command.ZeroTwelveUpgradeCommand{ - Meta: meta, - }, nil - }, - - "0.13upgrade": func() (cli.Command, error) { - return &command.ZeroThirteenUpgradeCommand{ - Meta: meta, - }, nil - }, - - "debug": func() (cli.Command, error) { - return &command.DebugCommand{ - Meta: meta, - }, nil - }, - "force-unlock": func() (cli.Command, error) { return &command.UnlockCommand{ Meta: meta, @@ -403,6 +392,21 @@ func initCommands( }, nil }, } + + PrimaryCommands = []string{ + "init", + "validate", + "plan", + "apply", + "destroy", + } + + HiddenCommands = map[string]struct{}{ + "env": struct{}{}, + "internal-plugin": struct{}{}, + "push": struct{}{}, + } + } // makeShutdownCh creates an interrupt listener and returns a channel. diff --git a/vendor/github.com/hashicorp/terraform/communicator/communicator.go b/vendor/github.com/hashicorp/terraform/communicator/communicator.go index 12b725b3..27261421 100644 --- a/vendor/github.com/hashicorp/terraform/communicator/communicator.go +++ b/vendor/github.com/hashicorp/terraform/communicator/communicator.go @@ -9,16 +9,18 @@ import ( "time" "github.com/hashicorp/terraform/communicator/remote" + "github.com/hashicorp/terraform/communicator/shared" "github.com/hashicorp/terraform/communicator/ssh" "github.com/hashicorp/terraform/communicator/winrm" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/provisioners" + "github.com/zclconf/go-cty/cty" ) // Communicator is an interface that must be implemented by all communicators // used for any of the provisioners type Communicator interface { - // Connect is used to setup the connection - Connect(terraform.UIOutput) error + // Connect is used to set up the connection + Connect(provisioners.UIOutput) error // Disconnect is used to terminate the connection Disconnect() error @@ -43,13 +45,23 @@ type Communicator interface { } // New returns a configured Communicator or an error if the connection type is not supported -func New(s *terraform.InstanceState) (Communicator, error) { - connType := s.Ephemeral.ConnInfo["type"] +func New(v cty.Value) (Communicator, error) { + v, err := shared.ConnectionBlockSupersetSchema.CoerceValue(v) + if err != nil { + return nil, err + } + + typeVal := v.GetAttr("type") + connType := "" + if !typeVal.IsNull() { + connType = typeVal.AsString() + } + switch connType { case "ssh", "": // The default connection type is ssh, so if connType is empty use ssh - return ssh.New(s) + return ssh.New(v) case "winrm": - return winrm.New(s) + return winrm.New(v) default: return nil, fmt.Errorf("connection type '%s' not supported", connType) } diff --git a/vendor/github.com/hashicorp/terraform/communicator/communicator_mock.go b/vendor/github.com/hashicorp/terraform/communicator/communicator_mock.go index 49304a07..b619560c 100644 --- a/vendor/github.com/hashicorp/terraform/communicator/communicator_mock.go +++ b/vendor/github.com/hashicorp/terraform/communicator/communicator_mock.go @@ -8,7 +8,7 @@ import ( "time" "github.com/hashicorp/terraform/communicator/remote" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/provisioners" ) // MockCommunicator is an implementation of Communicator that can be used for tests. @@ -24,7 +24,7 @@ type MockCommunicator struct { } // Connect implementation of communicator.Communicator interface -func (c *MockCommunicator) Connect(o terraform.UIOutput) error { +func (c *MockCommunicator) Connect(o provisioners.UIOutput) error { return nil } diff --git a/vendor/github.com/hashicorp/terraform/communicator/communicator_test.go b/vendor/github.com/hashicorp/terraform/communicator/communicator_test.go index e20d0368..20cdd8ff 100644 --- a/vendor/github.com/hashicorp/terraform/communicator/communicator_test.go +++ b/vendor/github.com/hashicorp/terraform/communicator/communicator_test.go @@ -8,29 +8,26 @@ import ( "testing" "time" - "github.com/hashicorp/terraform/terraform" + "github.com/zclconf/go-cty/cty" ) func TestCommunicator_new(t *testing.T) { - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "telnet", - "host": "127.0.0.1", - }, - }, + cfg := map[string]cty.Value{ + "type": cty.StringVal("telnet"), + "host": cty.StringVal("127.0.0.1"), } - if _, err := New(r); err == nil { + + if _, err := New(cty.ObjectVal(cfg)); err == nil { t.Fatalf("expected error with telnet") } - r.Ephemeral.ConnInfo["type"] = "ssh" - if _, err := New(r); err != nil { + cfg["type"] = cty.StringVal("ssh") + if _, err := New(cty.ObjectVal(cfg)); err != nil { t.Fatalf("err: %v", err) } - r.Ephemeral.ConnInfo["type"] = "winrm" - if _, err := New(r); err != nil { + cfg["type"] = cty.StringVal("winrm") + if _, err := New(cty.ObjectVal(cfg)); err != nil { t.Fatalf("err: %v", err) } } diff --git a/vendor/github.com/hashicorp/terraform/communicator/shared/shared.go b/vendor/github.com/hashicorp/terraform/communicator/shared/shared.go index 39cb1696..509aadd2 100644 --- a/vendor/github.com/hashicorp/terraform/communicator/shared/shared.go +++ b/vendor/github.com/hashicorp/terraform/communicator/shared/shared.go @@ -3,8 +3,124 @@ package shared import ( "fmt" "net" + + "github.com/hashicorp/terraform/configs/configschema" + "github.com/zclconf/go-cty/cty" ) +// ConnectionBlockSupersetSchema is a schema representing the superset of all +// possible arguments for "connection" blocks across all supported connection +// types. +// +// This currently lives here because we've not yet updated our communicator +// subsystem to be aware of schema itself. Once that is done, we can remove +// this and use a type-specific schema from the communicator to validate +// exactly what is expected for a given connection type. +var ConnectionBlockSupersetSchema = &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + // Common attributes for both connection types + "host": { + Type: cty.String, + Required: true, + }, + "type": { + Type: cty.String, + Optional: true, + }, + "user": { + Type: cty.String, + Optional: true, + }, + "password": { + Type: cty.String, + Optional: true, + }, + "port": { + Type: cty.String, + Optional: true, + }, + "timeout": { + Type: cty.String, + Optional: true, + }, + "script_path": { + Type: cty.String, + Optional: true, + }, + // For type=ssh only (enforced in ssh communicator) + "target_platform": { + Type: cty.String, + Optional: true, + }, + "private_key": { + Type: cty.String, + Optional: true, + }, + "certificate": { + Type: cty.String, + Optional: true, + }, + "host_key": { + Type: cty.String, + Optional: true, + }, + "agent": { + Type: cty.Bool, + Optional: true, + }, + "agent_identity": { + Type: cty.String, + Optional: true, + }, + "bastion_host": { + Type: cty.String, + Optional: true, + }, + "bastion_host_key": { + Type: cty.String, + Optional: true, + }, + "bastion_port": { + Type: cty.Number, + Optional: true, + }, + "bastion_user": { + Type: cty.String, + Optional: true, + }, + "bastion_password": { + Type: cty.String, + Optional: true, + }, + "bastion_private_key": { + Type: cty.String, + Optional: true, + }, + "bastion_certificate": { + Type: cty.String, + Optional: true, + }, + + // For type=winrm only (enforced in winrm communicator) + "https": { + Type: cty.Bool, + Optional: true, + }, + "insecure": { + Type: cty.Bool, + Optional: true, + }, + "cacert": { + Type: cty.String, + Optional: true, + }, + "use_ntlm": { + Type: cty.Bool, + Optional: true, + }, + }, +} + // IpFormat formats the IP correctly, so we don't provide IPv6 address in an IPv4 format during node communication. We return the ip parameter as is if it's an IPv4 address or a hostname. func IpFormat(ip string) string { ipObj := net.ParseIP(ip) diff --git a/vendor/github.com/hashicorp/terraform/communicator/ssh/communicator.go b/vendor/github.com/hashicorp/terraform/communicator/ssh/communicator.go index f39d7089..afbe5494 100644 --- a/vendor/github.com/hashicorp/terraform/communicator/ssh/communicator.go +++ b/vendor/github.com/hashicorp/terraform/communicator/ssh/communicator.go @@ -20,9 +20,12 @@ import ( "github.com/hashicorp/errwrap" "github.com/hashicorp/terraform/communicator/remote" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/provisioners" + "github.com/zclconf/go-cty/cty" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" + + _ "github.com/hashicorp/terraform/internal/logging" ) const ( @@ -52,7 +55,6 @@ type Communicator struct { client *ssh.Client config *sshConfig conn net.Conn - address string cancelKeepAlive context.CancelFunc lock sync.Mutex @@ -84,8 +86,8 @@ func (e fatalError) FatalError() error { } // New creates a new communicator implementation over SSH. -func New(s *terraform.InstanceState) (*Communicator, error) { - connInfo, err := parseConnectionInfo(s) +func New(v cty.Value) (*Communicator, error) { + connInfo, err := parseConnectionInfo(v) if err != nil { return nil, err } @@ -95,7 +97,7 @@ func New(s *terraform.InstanceState) (*Communicator, error) { return nil, err } - // Setup the random number generator once. The seed value is the + // Set up the random number generator once. The seed value is the // time multiplied by the PID. This can overflow the int64 but that // is okay. We multiply by the PID in case we have multiple processes // grabbing this at the same time. This is possible with Terraform and @@ -117,7 +119,7 @@ func New(s *terraform.InstanceState) (*Communicator, error) { } // Connect implementation of communicator.Communicator interface -func (c *Communicator) Connect(o terraform.UIOutput) (err error) { +func (c *Communicator) Connect(o provisioners.UIOutput) (err error) { // Grab a lock so we can modify our internal attributes c.lock.Lock() defer c.lock.Unlock() @@ -139,13 +141,15 @@ func (c *Communicator) Connect(o terraform.UIOutput) (err error) { " Private key: %t\n"+ " Certificate: %t\n"+ " SSH Agent: %t\n"+ - " Checking Host Key: %t", + " Checking Host Key: %t\n"+ + " Target Platform: %s\n", c.connInfo.Host, c.connInfo.User, c.connInfo.Password != "", c.connInfo.PrivateKey != "", c.connInfo.Certificate != "", c.connInfo.Agent, c.connInfo.HostKey != "", + c.connInfo.TargetPlatform, )) if c.connInfo.BastionHost != "" { @@ -338,12 +342,12 @@ func (c *Communicator) Start(cmd *remote.Cmd) error { return err } - // Setup our session + // Set up our session session.Stdin = cmd.Stdin session.Stdout = cmd.Stdout session.Stderr = cmd.Stderr - if !c.config.noPty { + if !c.config.noPty && c.connInfo.TargetPlatform != TargetPlatformWindows { // Request a PTY termModes := ssh.TerminalModes{ ssh.ECHO: 0, // do not echo @@ -425,35 +429,35 @@ func (c *Communicator) UploadScript(path string, input io.Reader) error { if err != nil { return fmt.Errorf("Error reading script: %s", err) } - var script bytes.Buffer - if string(prefix) != "#!" { + + if string(prefix) != "#!" && c.connInfo.TargetPlatform != TargetPlatformWindows { script.WriteString(DefaultShebang) } - script.ReadFrom(reader) + if err := c.Upload(path, &script); err != nil { return err } + if c.connInfo.TargetPlatform != TargetPlatformWindows { + var stdout, stderr bytes.Buffer + cmd := &remote.Cmd{ + Command: fmt.Sprintf("chmod 0777 %s", path), + Stdout: &stdout, + Stderr: &stderr, + } + if err := c.Start(cmd); err != nil { + return fmt.Errorf( + "Error chmodding script file to 0777 in remote "+ + "machine: %s", err) + } - var stdout, stderr bytes.Buffer - cmd := &remote.Cmd{ - Command: fmt.Sprintf("chmod 0777 %s", path), - Stdout: &stdout, - Stderr: &stderr, - } - if err := c.Start(cmd); err != nil { - return fmt.Errorf( - "Error chmodding script file to 0777 in remote "+ - "machine: %s", err) - } - - if err := cmd.Wait(); err != nil { - return fmt.Errorf( - "Error chmodding script file to 0777 in remote "+ - "machine %v: %s %s", err, stdout.String(), stderr.String()) + if err := cmd.Wait(); err != nil { + return fmt.Errorf( + "Error chmodding script file to 0777 in remote "+ + "machine %v: %s %s", err, stdout.String(), stderr.String()) + } } - return nil } diff --git a/vendor/github.com/hashicorp/terraform/communicator/ssh/communicator_test.go b/vendor/github.com/hashicorp/terraform/communicator/ssh/communicator_test.go index 445fbebb..d7104432 100644 --- a/vendor/github.com/hashicorp/terraform/communicator/ssh/communicator_test.go +++ b/vendor/github.com/hashicorp/terraform/communicator/ssh/communicator_test.go @@ -20,7 +20,7 @@ import ( "time" "github.com/hashicorp/terraform/communicator/remote" - "github.com/hashicorp/terraform/terraform" + "github.com/zclconf/go-cty/cty" "golang.org/x/crypto/ssh" ) @@ -99,6 +99,7 @@ func newMockLineServer(t *testing.T, signer ssh.Signer, pubKey string) string { t.Log("Accepted channel") go func(in <-chan *ssh.Request) { + defer channel.Close() for req := range in { // since this channel's requests are serviced serially, // this will block keepalive probes, and can simulate a @@ -112,8 +113,6 @@ func newMockLineServer(t *testing.T, signer ssh.Signer, pubKey string) string { } } }(requests) - - defer channel.Close() } conn.Close() }() @@ -125,20 +124,16 @@ func TestNew_Invalid(t *testing.T) { address := newMockLineServer(t, nil, testClientPublicKey) parts := strings.Split(address, ":") - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "ssh", - "user": "user", - "password": "i-am-invalid", - "host": parts[0], - "port": parts[1], - "timeout": "30s", - }, - }, - } + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("ssh"), + "user": cty.StringVal("user"), + "password": cty.StringVal("i-am-invalid"), + "host": cty.StringVal(parts[0]), + "port": cty.StringVal(parts[1]), + "timeout": cty.StringVal("30s"), + }) - c, err := New(r) + c, err := New(v) if err != nil { t.Fatalf("error creating communicator: %s", err) } @@ -150,19 +145,15 @@ func TestNew_Invalid(t *testing.T) { } func TestNew_InvalidHost(t *testing.T) { - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "ssh", - "user": "user", - "password": "i-am-invalid", - "port": "22", - "timeout": "30s", - }, - }, - } + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("ssh"), + "user": cty.StringVal("user"), + "password": cty.StringVal("i-am-invalid"), + "port": cty.StringVal("22"), + "timeout": cty.StringVal("30s"), + }) - _, err := New(r) + _, err := New(v) if err == nil { t.Fatal("should have had an error creating communicator") } @@ -172,20 +163,16 @@ func TestStart(t *testing.T) { address := newMockLineServer(t, nil, testClientPublicKey) parts := strings.Split(address, ":") - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "ssh", - "user": "user", - "password": "pass", - "host": parts[0], - "port": parts[1], - "timeout": "30s", - }, - }, - } + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("ssh"), + "user": cty.StringVal("user"), + "password": cty.StringVal("pass"), + "host": cty.StringVal(parts[0]), + "port": cty.StringVal(parts[1]), + "timeout": cty.StringVal("30s"), + }) - c, err := New(r) + c, err := New(v) if err != nil { t.Fatalf("error creating communicator: %s", err) } @@ -211,19 +198,15 @@ func TestKeepAlives(t *testing.T) { address := newMockLineServer(t, nil, testClientPublicKey) parts := strings.Split(address, ":") - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "ssh", - "user": "user", - "password": "pass", - "host": parts[0], - "port": parts[1], - }, - }, - } + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("ssh"), + "user": cty.StringVal("user"), + "password": cty.StringVal("pass"), + "host": cty.StringVal(parts[0]), + "port": cty.StringVal(parts[1]), + }) - c, err := New(r) + c, err := New(v) if err != nil { t.Fatalf("error creating communicator: %s", err) } @@ -261,19 +244,16 @@ func TestFailedKeepAlives(t *testing.T) { address := newMockLineServer(t, nil, testClientPublicKey) parts := strings.Split(address, ":") - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "ssh", - "user": "user", - "password": "pass", - "host": parts[0], - "port": parts[1], - }, - }, - } + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("ssh"), + "user": cty.StringVal("user"), + "password": cty.StringVal("pass"), + "host": cty.StringVal(parts[0]), + "port": cty.StringVal(parts[1]), + "timeout": cty.StringVal("30s"), + }) - c, err := New(r) + c, err := New(v) if err != nil { t.Fatalf("error creating communicator: %s", err) } @@ -296,20 +276,16 @@ func TestLostConnection(t *testing.T) { address := newMockLineServer(t, nil, testClientPublicKey) parts := strings.Split(address, ":") - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "ssh", - "user": "user", - "password": "pass", - "host": parts[0], - "port": parts[1], - "timeout": "30s", - }, - }, - } + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("ssh"), + "user": cty.StringVal("user"), + "password": cty.StringVal("pass"), + "host": cty.StringVal(parts[0]), + "port": cty.StringVal(parts[1]), + "timeout": cty.StringVal("30s"), + }) - c, err := New(r) + c, err := New(v) if err != nil { t.Fatalf("error creating communicator: %s", err) } @@ -586,19 +562,15 @@ func TestAccUploadFile(t *testing.T) { t.Skip() } - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "ssh", - "user": os.Getenv("USER"), - "host": "127.0.0.1", - "port": "22", - "timeout": "30s", - }, - }, - } + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("ssh"), + "user": cty.StringVal(os.Getenv("USER")), + "host": cty.StringVal("127.0.0.1"), + "port": cty.StringVal("22"), + "timeout": cty.StringVal("30s"), + }) - c, err := New(r) + c, err := New(v) if err != nil { t.Fatalf("error creating communicator: %s", err) } @@ -634,19 +606,15 @@ func TestAccHugeUploadFile(t *testing.T) { t.Skip() } - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "ssh", - "user": os.Getenv("USER"), - "host": "127.0.0.1", - "port": "22", - "timeout": "30s", - }, - }, - } + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("ssh"), + "host": cty.StringVal("127.0.0.1"), + "user": cty.StringVal(os.Getenv("USER")), + "port": cty.StringVal("22"), + "timeout": cty.StringVal("30s"), + }) - c, err := New(r) + c, err := New(v) if err != nil { t.Fatalf("error creating communicator: %s", err) } @@ -706,16 +674,13 @@ func TestScriptPath(t *testing.T) { } for _, tc := range cases { - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "ssh", - "host": "127.0.0.1", - "script_path": tc.Input, - }, - }, - } - comm, err := New(r) + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("ssh"), + "host": cty.StringVal("127.0.0.1"), + "script_path": cty.StringVal(tc.Input), + }) + + comm, err := New(v) if err != nil { t.Fatalf("err: %s", err) } @@ -735,14 +700,10 @@ func TestScriptPath_randSeed(t *testing.T) { // Pre GH-4186 fix, this value was the deterministic start the pseudorandom // chain of unseeded math/rand values for Int31(). staticSeedPath := "/tmp/terraform_1298498081.sh" - c, err := New(&terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "ssh", - "host": "127.0.0.1", - }, - }, - }) + c, err := New(cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("ssh"), + "host": cty.StringVal("127.0.0.1"), + })) if err != nil { t.Fatalf("err: %s", err) } @@ -752,34 +713,6 @@ func TestScriptPath_randSeed(t *testing.T) { } } -const testClientPrivateKey = `-----BEGIN RSA PRIVATE KEY----- -MIIEpQIBAAKCAQEAxOgNXOJ/jrRDxBZTSk2X9otNy9zcpUmJr5ifDi5sy7j2ZiQS -beBt1Wf+tLNWis8Cyq06ttEvjjRuM75yucyD6GrqDTXVCSm4PeOIQeDhPhw26wYZ -O0h/mFgrAiCwaEl8AFXVBInOhVn/0nqgUpkwckh76bjTsNeifkiugK3cfJOuBdrU -ZGbgugJjmYo4Cmv7ECo1gCFT5N+BAjOji3z3N5ClBH5HaWC77jH7kTH0k5cZ+ZRQ -tG9EqLyvWlnzTAR/Yly0JInkOa16Ms1Au5sIJwEoJfHKsNVK06IlLob53nblwYW0 -H5gv1Kb/rS+nUkpPtA5YFShB7iZnPLPPv6qXSwIDAQABAoIBAC0UY1rMkB9/rbQK -2G6+bPgI1HrDydAdkeQdsOxyPH43jlG8GGwHYZ3l/S4pkLqewijcmACay6Rm5IP8 -Kg/XfquLLqJvnKJIZuHkYaGTdn3dv8T21Hf6FRwvs0j9auW1TSpWfDpZwmpNPIBX -irTeVXUUmynbIrvt4km/IhRbuYrbbb964CLYD1DCl3XssXxoRNvPpc5EtOuyDorA -5g1hvZR1FqbOAmOuNQMYJociMuWB8mCaHb+o1Sg4A65OLXxoKs0cuwInJ/n/R4Z3 -+GrV+x5ypBMxXgjjQtKMLEOujkvxs1cp34hkbhKMHHXxbMu5jl74YtGGsLLk90rq -ieZGIgECgYEA49OM9mMCrDoFUTZdJaSARA/MOXkdQgrqVTv9kUHee7oeMZZ6lS0i -bPU7g+Bq+UAN0qcw9x992eAElKjBA71Q5UbZYWh29BDMZd8bRJmwz4P6aSMoYLWI -Sr31caJU9LdmPFatarNeehjSJtlTuoZD9+NElnnUwNaTeOOo5UdhTQsCgYEA3UGm -QWoDUttFwK9oL2KL8M54Bx6EzNhnyk03WrqBbR7PJcPKnsF0R/0soQ+y0FW0r8RJ -TqG6ze5fUJII72B4GlMTQdP+BIvaKQttwWQTNIjbbv4NksF445gdVOO1xi9SvQ7k -uvMVxOb+1jL3HAFa3furWu2tJRDs6dhuaILLxsECgYEAhnhlKUBDYZhVbxvhWsh/ -lKymY/3ikQqUSX7BKa1xPiIalDY3YDllql4MpMgfG8L85asdMZ96ztB0o7H/Ss/B -IbLxt5bLLz+DBVXsaE82lyVU9h10RbCgI01/w3SHJHHjfBXFAcehKfvgfmGkE+IP -2A5ie1aphrCgFqh5FetNuQUCgYEAibL42I804FUtFR1VduAa/dRRqQSaW6528dWa -lLGsKRBalUNEEAeP6dmr89UEUVp1qEo94V0QGGe5FDi+rNPaC3AWdQqNdaDgNlkx -hoFU3oYqIuqj4ejc5rBd2N4a2+vJz3W8bokozDGC+iYf2mMRfUPKwj1XW9Er0OFs -3UhBsEECgYEAto/iJB7ZlCM7EyV9JW0tsEt83rbKMQ/Ex0ShbBIejej0Xx7bwx60 -tVgay+bzJnNkXu6J4XVI98A/WsdI2kW4hL0STYdHV5HVA1l87V4ZbvTF2Bx8a8RJ -OF3UjpMTWKqOprw9nAu5VuwNRVzORF8ER8rgGeaR2/gsSvIYFy9VXq8= ------END RSA PRIVATE KEY-----` - var testClientPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDE6A1c4n+OtEPEFlNKTZf2i03L3NylSYmvmJ8OLmzLuPZmJBJt4G3VZ/60s1aKzwLKrTq20S+ONG4zvnK5zIPoauoNNdUJKbg944hB4OE+HDbrBhk7SH+YWCsCILBoSXwAVdUEic6FWf/SeqBSmTBySHvpuNOw16J+SK6Ardx8k64F2tRkZuC6AmOZijgKa/sQKjWAIVPk34ECM6OLfPc3kKUEfkdpYLvuMfuRMfSTlxn5lFC0b0SovK9aWfNMBH9iXLQkieQ5rXoyzUC7mwgnASgl8cqw1UrToiUuhvneduXBhbQfmC/Upv+tL6dSSk+0DlgVKEHuJmc8s8+/qpdL` func acceptUserPass(goodUser, goodPass string) func(ssh.ConnMetadata, []byte) (*ssh.Permissions, error) { diff --git a/vendor/github.com/hashicorp/terraform/communicator/ssh/password_test.go b/vendor/github.com/hashicorp/terraform/communicator/ssh/password_test.go index e513716d..219669e4 100644 --- a/vendor/github.com/hashicorp/terraform/communicator/ssh/password_test.go +++ b/vendor/github.com/hashicorp/terraform/communicator/ssh/password_test.go @@ -3,18 +3,8 @@ package ssh import ( "reflect" "testing" - - "golang.org/x/crypto/ssh" ) -func TestPasswordKeyboardInteractive_Impl(t *testing.T) { - var raw interface{} - raw = PasswordKeyboardInteractive("foo") - if _, ok := raw.(ssh.KeyboardInteractiveChallenge); !ok { - t.Fatal("PasswordKeyboardInteractive must implement KeyboardInteractiveChallenge") - } -} - func TestPasswordKeybardInteractive_Challenge(t *testing.T) { p := PasswordKeyboardInteractive("foo") result, err := p("foo", "bar", []string{"one", "two"}, nil) diff --git a/vendor/github.com/hashicorp/terraform/communicator/ssh/provisioner.go b/vendor/github.com/hashicorp/terraform/communicator/ssh/provisioner.go index b6fe80a4..a3fa80c4 100644 --- a/vendor/github.com/hashicorp/terraform/communicator/ssh/provisioner.go +++ b/vendor/github.com/hashicorp/terraform/communicator/ssh/provisioner.go @@ -10,13 +10,13 @@ import ( "net" "os" "path/filepath" + "strconv" "strings" "time" "github.com/hashicorp/terraform/communicator/shared" - "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/mapstructure" sshagent "github.com/xanzy/ssh-agent" + "github.com/zclconf/go-cty/cty" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" "golang.org/x/crypto/ssh/knownhosts" @@ -29,54 +29,126 @@ const ( // DefaultPort is used if there is no port given DefaultPort = 22 - // DefaultScriptPath is used as the path to copy the file to - // for remote execution if not provided otherwise. - DefaultScriptPath = "/tmp/terraform_%RAND%.sh" + // DefaultUnixScriptPath is used as the path to copy the file to + // for remote execution on unix if not provided otherwise. + DefaultUnixScriptPath = "/tmp/terraform_%RAND%.sh" + // DefaultWindowsScriptPath is used as the path to copy the file to + // for remote execution on windows if not provided otherwise. + DefaultWindowsScriptPath = "C:/windows/temp/terraform_%RAND%.cmd" // DefaultTimeout is used if there is no timeout given DefaultTimeout = 5 * time.Minute + + // TargetPlatformUnix used for cleaner code, and is used if no target platform has been specified + TargetPlatformUnix = "unix" + //TargetPlatformWindows used for cleaner code + TargetPlatformWindows = "windows" ) // connectionInfo is decoded from the ConnInfo of the resource. These are the // only keys we look at. If a PrivateKey is given, that is used instead // of a password. type connectionInfo struct { - User string - Password string - PrivateKey string `mapstructure:"private_key"` - Certificate string `mapstructure:"certificate"` - Host string - HostKey string `mapstructure:"host_key"` - Port int - Agent bool - Timeout string - ScriptPath string `mapstructure:"script_path"` - TimeoutVal time.Duration `mapstructure:"-"` - - BastionUser string `mapstructure:"bastion_user"` - BastionPassword string `mapstructure:"bastion_password"` - BastionPrivateKey string `mapstructure:"bastion_private_key"` - BastionCertificate string `mapstructure:"bastion_certificate"` - BastionHost string `mapstructure:"bastion_host"` - BastionHostKey string `mapstructure:"bastion_host_key"` - BastionPort int `mapstructure:"bastion_port"` - - AgentIdentity string `mapstructure:"agent_identity"` + User string + Password string + PrivateKey string + Certificate string + Host string + HostKey string + Port int + Agent bool + ScriptPath string + TargetPlatform string + Timeout string + TimeoutVal time.Duration + + BastionUser string + BastionPassword string + BastionPrivateKey string + BastionCertificate string + BastionHost string + BastionHostKey string + BastionPort int + + AgentIdentity string } -// parseConnectionInfo is used to convert the ConnInfo of the InstanceState into -// a ConnectionInfo struct -func parseConnectionInfo(s *terraform.InstanceState) (*connectionInfo, error) { +// decodeConnInfo decodes the given cty.Value using the same behavior as the +// lgeacy mapstructure decoder in order to preserve as much of the existing +// logic as possible for compatibility. +func decodeConnInfo(v cty.Value) (*connectionInfo, error) { connInfo := &connectionInfo{} - decConf := &mapstructure.DecoderConfig{ - WeaklyTypedInput: true, - Result: connInfo, + if v.IsNull() { + return connInfo, nil + } + + for k, v := range v.AsValueMap() { + if v.IsNull() { + continue + } + + switch k { + case "user": + connInfo.User = v.AsString() + case "password": + connInfo.Password = v.AsString() + case "private_key": + connInfo.PrivateKey = v.AsString() + case "certificate": + connInfo.Certificate = v.AsString() + case "host": + connInfo.Host = v.AsString() + case "host_key": + connInfo.HostKey = v.AsString() + case "port": + p, err := strconv.Atoi(v.AsString()) + if err != nil { + return nil, err + } + connInfo.Port = p + case "agent": + connInfo.Agent = v.True() + case "script_path": + connInfo.ScriptPath = v.AsString() + case "target_platform": + connInfo.TargetPlatform = v.AsString() + case "timeout": + connInfo.Timeout = v.AsString() + case "bastion_user": + connInfo.BastionUser = v.AsString() + case "bastion_password": + connInfo.BastionPassword = v.AsString() + case "bastion_private_key": + connInfo.BastionPrivateKey = v.AsString() + case "bastion_certificate": + connInfo.BastionCertificate = v.AsString() + case "bastion_host": + connInfo.BastionHost = v.AsString() + case "bastion_host_key": + connInfo.BastionHostKey = v.AsString() + case "bastion_port": + p, err := strconv.Atoi(v.AsString()) + if err != nil { + return nil, err + } + connInfo.BastionPort = p + case "agent_identity": + connInfo.AgentIdentity = v.AsString() + } } - dec, err := mapstructure.NewDecoder(decConf) + return connInfo, nil +} + +// parseConnectionInfo is used to convert the raw configuration into the +// *connectionInfo struct. +func parseConnectionInfo(v cty.Value) (*connectionInfo, error) { + v, err := shared.ConnectionBlockSupersetSchema.CoerceValue(v) if err != nil { return nil, err } - if err := dec.Decode(s.Ephemeral.ConnInfo); err != nil { + + connInfo, err := decodeConnInfo(v) + if err != nil { return nil, err } @@ -85,7 +157,8 @@ func parseConnectionInfo(s *terraform.InstanceState) (*connectionInfo, error) { // // And if SSH_AUTH_SOCK is not set, there's no agent to connect to, so we // shouldn't try. - if s.Ephemeral.ConnInfo["agent"] == "" && os.Getenv("SSH_AUTH_SOCK") != "" { + agent := v.GetAttr("agent") + if agent.IsNull() && os.Getenv("SSH_AUTH_SOCK") != "" { connInfo.Agent = true } @@ -106,8 +179,19 @@ func parseConnectionInfo(s *terraform.InstanceState) (*connectionInfo, error) { if connInfo.Port == 0 { connInfo.Port = DefaultPort } - if connInfo.ScriptPath == "" { - connInfo.ScriptPath = DefaultScriptPath + // Set default targetPlatform to unix if it's empty + if connInfo.TargetPlatform == "" { + connInfo.TargetPlatform = TargetPlatformUnix + } else if connInfo.TargetPlatform != TargetPlatformUnix && connInfo.TargetPlatform != TargetPlatformWindows { + return nil, fmt.Errorf("target_platform for provisioner has to be either %s or %s", TargetPlatformUnix, TargetPlatformWindows) + } + // Choose an appropriate default script path based on the target platform. There is no single + // suitable default script path which works on both UNIX and Windows targets. + if connInfo.ScriptPath == "" && connInfo.TargetPlatform == TargetPlatformUnix { + connInfo.ScriptPath = DefaultUnixScriptPath + } + if connInfo.ScriptPath == "" && connInfo.TargetPlatform == TargetPlatformWindows { + connInfo.ScriptPath = DefaultWindowsScriptPath } if connInfo.Timeout != "" { connInfo.TimeoutVal = safeDuration(connInfo.Timeout, DefaultTimeout) @@ -328,7 +412,7 @@ func readPrivateKey(pk string) (ssh.AuthMethod, error) { } func connectToAgent(connInfo *connectionInfo) (*sshAgent, error) { - if connInfo.Agent != true { + if !connInfo.Agent { // No agent configured return nil, nil } @@ -463,13 +547,6 @@ func (s *sshAgent) sortSigners(signers []ssh.Signer) { continue } } - - ss := []string{} - for _, signer := range signers { - pk := signer.PublicKey() - k := pk.(*agent.Key) - ss = append(ss, k.Comment) - } } func (s *sshAgent) Signers() ([]ssh.Signer, error) { diff --git a/vendor/github.com/hashicorp/terraform/communicator/ssh/provisioner_test.go b/vendor/github.com/hashicorp/terraform/communicator/ssh/provisioner_test.go index f8e0f77d..ace46f49 100644 --- a/vendor/github.com/hashicorp/terraform/communicator/ssh/provisioner_test.go +++ b/vendor/github.com/hashicorp/terraform/communicator/ssh/provisioner_test.go @@ -3,28 +3,23 @@ package ssh import ( "testing" - "github.com/hashicorp/terraform/terraform" + "github.com/zclconf/go-cty/cty" ) func TestProvisioner_connInfo(t *testing.T) { - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "ssh", - "user": "root", - "password": "supersecret", - "private_key": "someprivatekeycontents", - "certificate": "somecertificate", - "host": "127.0.0.1", - "port": "22", - "timeout": "30s", - - "bastion_host": "127.0.1.1", - }, - }, - } - - conf, err := parseConnectionInfo(r) + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("ssh"), + "user": cty.StringVal("root"), + "password": cty.StringVal("supersecret"), + "private_key": cty.StringVal("someprivatekeycontents"), + "certificate": cty.StringVal("somecertificate"), + "host": cty.StringVal("127.0.0.1"), + "port": cty.StringVal("22"), + "timeout": cty.StringVal("30s"), + "bastion_host": cty.StringVal("127.0.1.1"), + }) + + conf, err := parseConnectionInfo(v) if err != nil { t.Fatalf("err: %v", err) } @@ -50,7 +45,10 @@ func TestProvisioner_connInfo(t *testing.T) { if conf.Timeout != "30s" { t.Fatalf("bad: %v", conf) } - if conf.ScriptPath != DefaultScriptPath { + if conf.ScriptPath != DefaultUnixScriptPath { + t.Fatalf("bad: %v", conf) + } + if conf.TargetPlatform != TargetPlatformUnix { t.Fatalf("bad: %v", conf) } if conf.BastionHost != "127.0.1.1" { @@ -71,24 +69,18 @@ func TestProvisioner_connInfo(t *testing.T) { } func TestProvisioner_connInfoIpv6(t *testing.T) { - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "ssh", - "user": "root", - "password": "supersecret", - "private_key": "someprivatekeycontents", - "certificate": "somecertificate", - "host": "::1", - "port": "22", - "timeout": "30s", - - "bastion_host": "::1", - }, - }, - } - - conf, err := parseConnectionInfo(r) + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("ssh"), + "user": cty.StringVal("root"), + "password": cty.StringVal("supersecret"), + "private_key": cty.StringVal("someprivatekeycontents"), + "host": cty.StringVal("::1"), + "port": cty.StringVal("22"), + "timeout": cty.StringVal("30s"), + "bastion_host": cty.StringVal("::1"), + }) + + conf, err := parseConnectionInfo(v) if err != nil { t.Fatalf("err: %v", err) } @@ -103,22 +95,18 @@ func TestProvisioner_connInfoIpv6(t *testing.T) { } func TestProvisioner_connInfoHostname(t *testing.T) { - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "ssh", - "user": "root", - "password": "supersecret", - "private_key": "someprivatekeycontents", - "host": "example.com", - "port": "22", - "timeout": "30s", - "bastion_host": "example.com", - }, - }, - } - - conf, err := parseConnectionInfo(r) + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("ssh"), + "user": cty.StringVal("root"), + "password": cty.StringVal("supersecret"), + "private_key": cty.StringVal("someprivatekeycontents"), + "host": cty.StringVal("example.com"), + "port": cty.StringVal("22"), + "timeout": cty.StringVal("30s"), + "bastion_host": cty.StringVal("example.com"), + }) + + conf, err := parseConnectionInfo(v) if err != nil { t.Fatalf("err: %v", err) } @@ -133,21 +121,16 @@ func TestProvisioner_connInfoHostname(t *testing.T) { } func TestProvisioner_connInfoEmptyHostname(t *testing.T) { - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "ssh", - "user": "root", - "password": "supersecret", - "private_key": "someprivatekeycontents", - "host": "", - "port": "22", - "timeout": "30s", - }, - }, - } - - _, err := parseConnectionInfo(r) + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("ssh"), + "user": cty.StringVal("root"), + "password": cty.StringVal("supersecret"), + "private_key": cty.StringVal("someprivatekeycontents"), + "port": cty.StringVal("22"), + "timeout": cty.StringVal("30s"), + }) + + _, err := parseConnectionInfo(v) if err == nil { t.Fatalf("bad: should not allow empty host") } diff --git a/vendor/github.com/hashicorp/terraform/communicator/ssh/ssh_test.go b/vendor/github.com/hashicorp/terraform/communicator/ssh/ssh_test.go index 9cd10a0a..c1086569 100644 --- a/vendor/github.com/hashicorp/terraform/communicator/ssh/ssh_test.go +++ b/vendor/github.com/hashicorp/terraform/communicator/ssh/ssh_test.go @@ -16,7 +16,7 @@ import ( // verify that we can locate public key data func TestFindKeyData(t *testing.T) { - // setup a test directory + // set up a test directory td, err := ioutil.TempDir("", "ssh") if err != nil { t.Fatal(err) @@ -81,10 +81,10 @@ func generateSSHKey(t *testing.T, idFile string) ssh.PublicKey { } privFile, err := os.OpenFile(idFile, os.O_RDWR|os.O_CREATE, 0600) - defer privFile.Close() if err != nil { t.Fatal(err) } + defer privFile.Close() privPEM := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)} if err := pem.Encode(privFile, privPEM); err != nil { t.Fatal(err) diff --git a/vendor/github.com/hashicorp/terraform/communicator/winrm/communicator.go b/vendor/github.com/hashicorp/terraform/communicator/winrm/communicator.go index 82734491..4f9f2883 100644 --- a/vendor/github.com/hashicorp/terraform/communicator/winrm/communicator.go +++ b/vendor/github.com/hashicorp/terraform/communicator/winrm/communicator.go @@ -10,9 +10,10 @@ import ( "time" "github.com/hashicorp/terraform/communicator/remote" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/provisioners" "github.com/masterzen/winrm" "github.com/packer-community/winrmcp/winrmcp" + "github.com/zclconf/go-cty/cty" ) // Communicator represents the WinRM communicator @@ -24,8 +25,8 @@ type Communicator struct { } // New creates a new communicator implementation over WinRM. -func New(s *terraform.InstanceState) (*Communicator, error) { - connInfo, err := parseConnectionInfo(s) +func New(v cty.Value) (*Communicator, error) { + connInfo, err := parseConnectionInfo(v) if err != nil { return nil, err } @@ -52,7 +53,7 @@ func New(s *terraform.InstanceState) (*Communicator, error) { } // Connect implementation of communicator.Communicator interface -func (c *Communicator) Connect(o terraform.UIOutput) error { +func (c *Communicator) Connect(o provisioners.UIOutput) error { // Set the client to nil since we'll (re)create it c.client = nil diff --git a/vendor/github.com/hashicorp/terraform/communicator/winrm/communicator_test.go b/vendor/github.com/hashicorp/terraform/communicator/winrm/communicator_test.go index f6a04993..bd8d2ecd 100644 --- a/vendor/github.com/hashicorp/terraform/communicator/winrm/communicator_test.go +++ b/vendor/github.com/hashicorp/terraform/communicator/winrm/communicator_test.go @@ -9,7 +9,8 @@ import ( "github.com/dylanmei/winrmtest" "github.com/hashicorp/terraform/communicator/remote" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/communicator/shared" + "github.com/zclconf/go-cty/cty" ) func newMockWinRMServer(t *testing.T) *winrmtest.Remote { @@ -47,20 +48,16 @@ func TestStart(t *testing.T) { wrm := newMockWinRMServer(t) defer wrm.Close() - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "winrm", - "user": "user", - "password": "pass", - "host": wrm.Host, - "port": strconv.Itoa(wrm.Port), - "timeout": "30s", - }, - }, - } + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("winrm"), + "user": cty.StringVal("user"), + "password": cty.StringVal("pass"), + "host": cty.StringVal(wrm.Host), + "port": cty.StringVal(strconv.Itoa(wrm.Port)), + "timeout": cty.StringVal("30s"), + }) - c, err := New(r) + c, err := New(v) if err != nil { t.Fatalf("error creating communicator: %s", err) } @@ -84,21 +81,16 @@ func TestStart(t *testing.T) { func TestUpload(t *testing.T) { wrm := newMockWinRMServer(t) defer wrm.Close() - - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "winrm", - "user": "user", - "password": "pass", - "host": wrm.Host, - "port": strconv.Itoa(wrm.Port), - "timeout": "30s", - }, - }, - } - - c, err := New(r) + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("winrm"), + "user": cty.StringVal("user"), + "password": cty.StringVal("pass"), + "host": cty.StringVal(wrm.Host), + "port": cty.StringVal(strconv.Itoa(wrm.Port)), + "timeout": cty.StringVal("30s"), + }) + + c, err := New(v) if err != nil { t.Fatalf("error creating communicator: %s", err) } @@ -131,15 +123,13 @@ func TestScriptPath(t *testing.T) { } for _, tc := range cases { - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "winrm", - "script_path": tc.Input, - }, - }, - } - comm, err := New(r) + v := cty.ObjectVal(map[string]cty.Value{ + "host": cty.StringVal(""), + "type": cty.StringVal("winrm"), + "script_path": cty.StringVal(tc.Input), + }) + + comm, err := New(v) if err != nil { t.Fatalf("err: %s", err) } @@ -158,21 +148,16 @@ func TestScriptPath(t *testing.T) { func TestNoTransportDecorator(t *testing.T) { wrm := newMockWinRMServer(t) defer wrm.Close() - - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "winrm", - "user": "user", - "password": "pass", - "host": wrm.Host, - "port": strconv.Itoa(wrm.Port), - "timeout": "30s", - }, - }, - } - - c, err := New(r) + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("winrm"), + "user": cty.StringVal("user"), + "password": cty.StringVal("pass"), + "host": cty.StringVal(wrm.Host), + "port": cty.StringVal(strconv.Itoa(wrm.Port)), + "timeout": cty.StringVal("30s"), + }) + + c, err := New(v) if err != nil { t.Fatalf("error creating communicator: %s", err) } @@ -192,21 +177,17 @@ func TestTransportDecorator(t *testing.T) { wrm := newMockWinRMServer(t) defer wrm.Close() - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "winrm", - "user": "user", - "password": "pass", - "host": wrm.Host, - "port": strconv.Itoa(wrm.Port), - "use_ntlm": "true", - "timeout": "30s", - }, - }, - } - - c, err := New(r) + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("winrm"), + "user": cty.StringVal("user"), + "password": cty.StringVal("pass"), + "host": cty.StringVal(wrm.Host), + "port": cty.StringVal(strconv.Itoa(wrm.Port)), + "use_ntlm": cty.StringVal("true"), + "timeout": cty.StringVal("30s"), + }) + + c, err := New(v) if err != nil { t.Fatalf("error creating communicator: %s", err) } @@ -226,7 +207,7 @@ func TestScriptPath_randSeed(t *testing.T) { // Pre GH-4186 fix, this value was the deterministic start the pseudorandom // chain of unseeded math/rand values for Int31(). staticSeedPath := "C:/Temp/terraform_1298498081.cmd" - c, err := New(&terraform.InstanceState{}) + c, err := New(cty.NullVal(shared.ConnectionBlockSupersetSchema.ImpliedType())) if err != nil { t.Fatalf("err: %s", err) } diff --git a/vendor/github.com/hashicorp/terraform/communicator/winrm/provisioner.go b/vendor/github.com/hashicorp/terraform/communicator/winrm/provisioner.go index 5cef1309..7a71fe92 100644 --- a/vendor/github.com/hashicorp/terraform/communicator/winrm/provisioner.go +++ b/vendor/github.com/hashicorp/terraform/communicator/winrm/provisioner.go @@ -4,12 +4,12 @@ import ( "fmt" "log" "path/filepath" + "strconv" "strings" "time" "github.com/hashicorp/terraform/communicator/shared" - "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/mapstructure" + "github.com/zclconf/go-cty/cty" ) const ( @@ -47,22 +47,62 @@ type connectionInfo struct { TimeoutVal time.Duration `mapstructure:"-"` } -// parseConnectionInfo is used to convert the ConnInfo of the InstanceState into -// a ConnectionInfo struct -func parseConnectionInfo(s *terraform.InstanceState) (*connectionInfo, error) { +// decodeConnInfo decodes the given cty.Value using the same behavior as the +// lgeacy mapstructure decoder in order to preserve as much of the existing +// logic as possible for compatibility. +func decodeConnInfo(v cty.Value) (*connectionInfo, error) { connInfo := &connectionInfo{} - decConf := &mapstructure.DecoderConfig{ - WeaklyTypedInput: true, - Result: connInfo, + if v.IsNull() { + return connInfo, nil + } + + for k, v := range v.AsValueMap() { + if v.IsNull() { + continue + } + + switch k { + case "user": + connInfo.User = v.AsString() + case "password": + connInfo.Password = v.AsString() + case "host": + connInfo.Host = v.AsString() + case "port": + p, err := strconv.Atoi(v.AsString()) + if err != nil { + return nil, err + } + connInfo.Port = p + case "https": + connInfo.HTTPS = v.True() + case "insecure": + connInfo.Insecure = v.True() + case "use_ntlm": + connInfo.NTLM = v.True() + case "cacert": + connInfo.CACert = v.AsString() + case "script_path": + connInfo.ScriptPath = v.AsString() + case "timeout": + connInfo.Timeout = v.AsString() + } } - dec, err := mapstructure.NewDecoder(decConf) + return connInfo, nil +} + +// parseConnectionInfo is used to convert the ConnInfo of the InstanceState into +// a ConnectionInfo struct +func parseConnectionInfo(v cty.Value) (*connectionInfo, error) { + v, err := shared.ConnectionBlockSupersetSchema.CoerceValue(v) if err != nil { return nil, err } - if err := dec.Decode(s.Ephemeral.ConnInfo); err != nil { + + connInfo, err := decodeConnInfo(v) + if err != nil { return nil, err } - // Check on script paths which point to the default Windows TEMP folder because files // which are put in there very early in the boot process could get cleaned/deleted // before you had the change to execute them. diff --git a/vendor/github.com/hashicorp/terraform/communicator/winrm/provisioner_test.go b/vendor/github.com/hashicorp/terraform/communicator/winrm/provisioner_test.go index fbc45c34..50718aa8 100644 --- a/vendor/github.com/hashicorp/terraform/communicator/winrm/provisioner_test.go +++ b/vendor/github.com/hashicorp/terraform/communicator/winrm/provisioner_test.go @@ -3,23 +3,19 @@ package winrm import ( "testing" - "github.com/hashicorp/terraform/terraform" + "github.com/zclconf/go-cty/cty" ) func TestProvisioner_defaultHTTPSPort(t *testing.T) { - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "winrm", - "user": "Administrator", - "password": "supersecret", - "host": "127.0.0.1", - "https": "true", - }, - }, - } + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("winrm"), + "user": cty.StringVal("Administrator"), + "password": cty.StringVal("supersecret"), + "host": cty.StringVal("127.0.0.1"), + "https": cty.True, + }) - conf, err := parseConnectionInfo(r) + conf, err := parseConnectionInfo(v) if err != nil { t.Fatalf("err: %v", err) } @@ -32,22 +28,18 @@ func TestProvisioner_defaultHTTPSPort(t *testing.T) { } func TestProvisioner_connInfo(t *testing.T) { - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "winrm", - "user": "Administrator", - "password": "supersecret", - "host": "127.0.0.1", - "port": "5985", - "https": "true", - "use_ntlm": "true", - "timeout": "30s", - }, - }, - } + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("winrm"), + "user": cty.StringVal("Administrator"), + "password": cty.StringVal("supersecret"), + "host": cty.StringVal("127.0.0.1"), + "port": cty.StringVal("5985"), + "https": cty.True, + "use_ntlm": cty.True, + "timeout": cty.StringVal("30s"), + }) - conf, err := parseConnectionInfo(r) + conf, err := parseConnectionInfo(v) if err != nil { t.Fatalf("err: %v", err) } @@ -100,23 +92,18 @@ CqDUFjhydXxYRsxXBBrEiLOE5BdtJR1sH/QHxIJe23C9iHI2nS1NbLziNEApLwC4 GnSud83VUo9G9w== -----END CERTIFICATE----- ` + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("winrm"), + "user": cty.StringVal("Administrator"), + "password": cty.StringVal("supersecret"), + "host": cty.StringVal("127.0.0.1"), + "port": cty.StringVal("5985"), + "https": cty.True, + "timeout": cty.StringVal("30s"), + "cacert": cty.StringVal(caCert), + }) - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "winrm", - "user": "Administrator", - "password": "supersecret", - "host": "127.0.0.1", - "port": "5985", - "https": "true", - "timeout": "30s", - "cacert": caCert, - }, - }, - } - - conf, err := parseConnectionInfo(r) + conf, err := parseConnectionInfo(v) if err != nil { t.Fatalf("err: %v", err) } @@ -148,21 +135,17 @@ GnSud83VUo9G9w== } func TestProvisioner_connInfoIpv6(t *testing.T) { - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "winrm", - "user": "Administrator", - "password": "supersecret", - "host": "::1", - "port": "5985", - "https": "true", - "timeout": "30s", - }, - }, - } + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("winrm"), + "user": cty.StringVal("Administrator"), + "password": cty.StringVal("supersecret"), + "host": cty.StringVal("::1"), + "port": cty.StringVal("5985"), + "https": cty.True, + "timeout": cty.StringVal("30s"), + }) - conf, err := parseConnectionInfo(r) + conf, err := parseConnectionInfo(v) if err != nil { t.Fatalf("err: %v", err) } @@ -191,21 +174,17 @@ func TestProvisioner_connInfoIpv6(t *testing.T) { } func TestProvisioner_connInfoHostname(t *testing.T) { - r := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "type": "winrm", - "user": "Administrator", - "password": "supersecret", - "host": "example.com", - "port": "5985", - "https": "true", - "timeout": "30s", - }, - }, - } + v := cty.ObjectVal(map[string]cty.Value{ + "type": cty.StringVal("winrm"), + "user": cty.StringVal("Administrator"), + "password": cty.StringVal("supersecret"), + "host": cty.StringVal("example.com"), + "port": cty.StringVal("5985"), + "https": cty.True, + "timeout": cty.StringVal("30s"), + }) - conf, err := parseConnectionInfo(r) + conf, err := parseConnectionInfo(v) if err != nil { t.Fatalf("err: %v", err) } @@ -235,38 +214,26 @@ func TestProvisioner_connInfoHostname(t *testing.T) { func TestProvisioner_formatDuration(t *testing.T) { cases := map[string]struct { - InstanceState *terraform.InstanceState - Result string + Config map[string]cty.Value + Result string }{ "testSeconds": { - InstanceState: &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "timeout": "90s", - }, - }, + Config: map[string]cty.Value{ + "timeout": cty.StringVal("90s"), }, Result: "PT1M30S", }, "testMinutes": { - InstanceState: &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "timeout": "5m", - }, - }, + Config: map[string]cty.Value{ + "timeout": cty.StringVal("5m"), }, Result: "PT5M", }, "testHours": { - InstanceState: &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: map[string]string{ - "timeout": "1h", - }, - }, + Config: map[string]cty.Value{ + "timeout": cty.StringVal("1h"), }, Result: "PT1H", @@ -274,7 +241,10 @@ func TestProvisioner_formatDuration(t *testing.T) { } for name, tc := range cases { - conf, err := parseConnectionInfo(tc.InstanceState) + // host is required in the schema + tc.Config["host"] = cty.StringVal("") + + conf, err := parseConnectionInfo(cty.ObjectVal(tc.Config)) if err != nil { t.Fatalf("err: %v", err) } diff --git a/vendor/github.com/hashicorp/terraform/configs/compat_shim.go b/vendor/github.com/hashicorp/terraform/configs/compat_shim.go index 4c6c1b75..79360201 100644 --- a/vendor/github.com/hashicorp/terraform/configs/compat_shim.go +++ b/vendor/github.com/hashicorp/terraform/configs/compat_shim.go @@ -107,62 +107,3 @@ func shimIsIgnoreChangesStar(expr hcl.Expression) bool { } return val.AsString() == "*" } - -// warnForDeprecatedInterpolations returns warning diagnostics if the given -// body can be proven to contain attributes whose expressions are native -// syntax expressions consisting entirely of a single template interpolation, -// which is a deprecated way to include a non-literal value in configuration. -// -// This is a best-effort sort of thing which relies on the physical HCL native -// syntax AST, so it might not catch everything. The main goal is to catch the -// "obvious" cases in order to help spread awareness that this old form is -// deprecated, when folks copy it from older examples they've found on the -// internet that were written for Terraform 0.11 or earlier. -func warnForDeprecatedInterpolationsInBody(body hcl.Body) hcl.Diagnostics { - var diags hcl.Diagnostics - - nativeBody, ok := body.(*hclsyntax.Body) - if !ok { - // If it's not native syntax then we've nothing to do here. - return diags - } - - for _, attr := range nativeBody.Attributes { - moreDiags := warnForDeprecatedInterpolationsInExpr(attr.Expr) - diags = append(diags, moreDiags...) - } - - for _, block := range nativeBody.Blocks { - // We'll also go hunting in nested blocks - moreDiags := warnForDeprecatedInterpolationsInBody(block.Body) - diags = append(diags, moreDiags...) - } - - return diags -} - -func warnForDeprecatedInterpolationsInExpr(expr hcl.Expression) hcl.Diagnostics { - node, ok := expr.(hclsyntax.Node) - if !ok { - return nil - } - - return hclsyntax.VisitAll(node, func(n hclsyntax.Node) hcl.Diagnostics { - e, ok := n.(*hclsyntax.TemplateWrapExpr) - if !ok { - // We're only interested in TemplateWrapExpr, because that's how - // the HCL native syntax parser represents the case of a template - // that consists entirely of a single interpolation expression, which - // is therefore subject to the special case of passing through the - // inner value without conversion to string. - return nil - } - - return hcl.Diagnostics{&hcl.Diagnostic{ - Severity: hcl.DiagWarning, - Summary: "Interpolation-only expressions are deprecated", - Detail: "Terraform 0.11 and earlier required all non-constant expressions to be provided via interpolation syntax, but this pattern is now deprecated. To silence this warning, remove the \"${ sequence from the start and the }\" sequence from the end of this expression, leaving just the inner expression.\n\nTemplate interpolation syntax is still used to construct strings from expressions when the template includes multiple interpolation sequences or a mixture of literal strings and interpolations. This deprecation applies only to templates that consist entirely of a single interpolation sequence.", - Subject: e.Range().Ptr(), - }} - }) -} diff --git a/vendor/github.com/hashicorp/terraform/configs/config.go b/vendor/github.com/hashicorp/terraform/configs/config.go index affa93d1..6f1603bf 100644 --- a/vendor/github.com/hashicorp/terraform/configs/config.go +++ b/vendor/github.com/hashicorp/terraform/configs/config.go @@ -190,6 +190,18 @@ func (c *Config) ProviderRequirements() (getproviders.Requirements, hcl.Diagnost return reqs, diags } +// ProviderRequirementsShallow searches only the direct receiver for explicit +// and implicit dependencies on providers. Descendant modules are ignored. +// +// If the returned diagnostics includes errors then the resulting Requirements +// may be incomplete. +func (c *Config) ProviderRequirementsShallow() (getproviders.Requirements, hcl.Diagnostics) { + reqs := make(getproviders.Requirements) + diags := c.addProviderRequirements(reqs, false) + + return reqs, diags +} + // ProviderRequirementsByModule searches the full tree of modules under the // receiver for both explicit and implicit dependencies on providers, // constructing a tree where the requirements are broken out by module. @@ -317,6 +329,53 @@ func (c *Config) addProviderRequirements(reqs getproviders.Requirements, recurse return diags } +// resolveProviderTypes walks through the providers in the module and ensures +// the true types are assigned based on the provider requirements for the +// module. +func (c *Config) resolveProviderTypes() { + for _, child := range c.Children { + child.resolveProviderTypes() + } + + // collect the required_providers, and then add any missing default providers + providers := map[string]addrs.Provider{} + for name, p := range c.Module.ProviderRequirements.RequiredProviders { + providers[name] = p.Type + } + + // ensure all provider configs know their correct type + for _, p := range c.Module.ProviderConfigs { + addr, required := providers[p.Name] + if required { + p.providerType = addr + } else { + addr := addrs.NewDefaultProvider(p.Name) + p.providerType = addr + providers[p.Name] = addr + } + } + + // connect module call providers to the correct type + for _, mod := range c.Module.ModuleCalls { + for _, p := range mod.Providers { + if addr, known := providers[p.InParent.Name]; known { + p.InParent.providerType = addr + } + } + } + + // fill in parent module calls too + if c.Parent != nil { + for _, mod := range c.Parent.Module.ModuleCalls { + for _, p := range mod.Providers { + if addr, known := providers[p.InChild.Name]; known { + p.InChild.providerType = addr + } + } + } + } +} + // ProviderTypes returns the FQNs of each distinct provider type referenced // in the receiving configuration. // diff --git a/vendor/github.com/hashicorp/terraform/configs/config_build.go b/vendor/github.com/hashicorp/terraform/configs/config_build.go index c38a6792..1083a1a2 100644 --- a/vendor/github.com/hashicorp/terraform/configs/config_build.go +++ b/vendor/github.com/hashicorp/terraform/configs/config_build.go @@ -22,6 +22,13 @@ func BuildConfig(root *Module, walker ModuleWalker) (*Config, hcl.Diagnostics) { } cfg.Root = cfg // Root module is self-referential. cfg.Children, diags = buildChildModules(cfg, walker) + + // Now that the config is built, we can connect the provider names to all + // the known types for validation. + cfg.resolveProviderTypes() + + diags = append(diags, validateProviderConfigs(nil, cfg, false)...) + return cfg, diags } @@ -78,6 +85,15 @@ func buildChildModules(parent *Config, walker ModuleWalker) (map[string]*Config, child.Children, modDiags = buildChildModules(child, walker) diags = append(diags, modDiags...) + if mod.Backend != nil { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagWarning, + Summary: "Backend configuration ignored", + Detail: "Any selected backend applies to the entire configuration, so Terraform expects provider configurations only in the root module.\n\nThis is a warning rather than an error because it's sometimes convenient to temporarily call a root module as a child module for testing purposes, but this backend configuration block will have no effect.", + Subject: mod.Backend.DeclRange.Ptr(), + }) + } + ret[call.Name] = child } diff --git a/vendor/github.com/hashicorp/terraform/configs/config_build_test.go b/vendor/github.com/hashicorp/terraform/configs/config_build_test.go index 1092558b..a977e432 100644 --- a/vendor/github.com/hashicorp/terraform/configs/config_build_test.go +++ b/vendor/github.com/hashicorp/terraform/configs/config_build_test.go @@ -2,6 +2,7 @@ package configs import ( "fmt" + "io/ioutil" "path/filepath" "reflect" "sort" @@ -114,3 +115,167 @@ func TestBuildConfigDiags(t *testing.T) { t.Fatalf("wrong result\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(want)) } } + +func TestBuildConfigChildModuleBackend(t *testing.T) { + parser := NewParser(nil) + mod, diags := parser.LoadConfigDir("testdata/nested-backend-warning") + assertNoDiagnostics(t, diags) + if mod == nil { + t.Fatal("got nil root module; want non-nil") + } + + cfg, diags := BuildConfig(mod, ModuleWalkerFunc( + func(req *ModuleRequest) (*Module, *version.Version, hcl.Diagnostics) { + // For the sake of this test we're going to just treat our + // SourceAddr as a path relative to our fixture directory. + // A "real" implementation of ModuleWalker should accept the + // various different source address syntaxes Terraform supports. + sourcePath := filepath.Join("testdata/nested-backend-warning", req.SourceAddr) + + mod, diags := parser.LoadConfigDir(sourcePath) + version, _ := version.NewVersion("1.0.0") + return mod, version, diags + }, + )) + + assertDiagnosticSummary(t, diags, "Backend configuration ignored") + + // we should still have module structure loaded + var got []string + cfg.DeepEach(func(c *Config) { + got = append(got, fmt.Sprintf("%s %s", strings.Join(c.Path, "."), c.Version)) + }) + sort.Strings(got) + want := []string{ + " ", + "child 1.0.0", + } + + if !reflect.DeepEqual(got, want) { + t.Fatalf("wrong result\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(want)) + } +} + +func TestBuildConfigInvalidModules(t *testing.T) { + testDir := "testdata/config-diagnostics" + dirs, err := ioutil.ReadDir(testDir) + if err != nil { + t.Fatal(err) + } + + for _, info := range dirs { + name := info.Name() + t.Run(name, func(t *testing.T) { + parser := NewParser(nil) + path := filepath.Join(testDir, name) + + mod, diags := parser.LoadConfigDir(path) + if diags.HasErrors() { + // these tests should only trigger errors that are caught in + // the config loader. + t.Errorf("error loading config dir") + for _, diag := range diags { + t.Logf("- %s", diag) + } + } + + readDiags := func(data []byte, _ error) []string { + var expected []string + for _, s := range strings.Split(string(data), "\n") { + msg := strings.TrimSpace(s) + msg = strings.ReplaceAll(msg, `\n`, "\n") + if msg != "" { + expected = append(expected, msg) + } + } + return expected + } + + // Load expected errors and warnings. + // Each line in the file is matched as a substring against the + // diagnostic outputs. + // Capturing part of the path and source range in the message lets + // us also ensure the diagnostic is being attributed to the + // expected location in the source, but is not required. + // The literal characters `\n` are replaced with newlines, but + // otherwise the string is unchanged. + expectedErrs := readDiags(ioutil.ReadFile(filepath.Join(testDir, name, "errors"))) + expectedWarnings := readDiags(ioutil.ReadFile(filepath.Join(testDir, name, "warnings"))) + + _, buildDiags := BuildConfig(mod, ModuleWalkerFunc( + func(req *ModuleRequest) (*Module, *version.Version, hcl.Diagnostics) { + // for simplicity, these tests will treat all source + // addresses as relative to the root module + sourcePath := filepath.Join(path, req.SourceAddr) + mod, diags := parser.LoadConfigDir(sourcePath) + version, _ := version.NewVersion("1.0.0") + return mod, version, diags + }, + )) + + // we can make this less repetitive later if we want + for _, msg := range expectedErrs { + found := false + for _, diag := range buildDiags { + if diag.Severity == hcl.DiagError && strings.Contains(diag.Error(), msg) { + found = true + break + } + } + + if !found { + t.Errorf("Expected error diagnostic containing %q", msg) + } + } + + for _, diag := range buildDiags { + if diag.Severity != hcl.DiagError { + continue + } + found := false + for _, msg := range expectedErrs { + if strings.Contains(diag.Error(), msg) { + found = true + break + } + } + + if !found { + t.Errorf("Unexpected error: %q", diag) + } + } + + for _, msg := range expectedWarnings { + found := false + for _, diag := range buildDiags { + if diag.Severity == hcl.DiagWarning && strings.Contains(diag.Error(), msg) { + found = true + break + } + } + + if !found { + t.Errorf("Expected warning diagnostic containing %q", msg) + } + } + + for _, diag := range buildDiags { + if diag.Severity != hcl.DiagWarning { + continue + } + found := false + for _, msg := range expectedWarnings { + if strings.Contains(diag.Error(), msg) { + found = true + break + } + } + + if !found { + t.Errorf("Unexpected warning: %q", diag) + } + } + + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/configs/config_test.go b/vendor/github.com/hashicorp/terraform/configs/config_test.go index d3a6974b..17b6a4d3 100644 --- a/vendor/github.com/hashicorp/terraform/configs/config_test.go +++ b/vendor/github.com/hashicorp/terraform/configs/config_test.go @@ -157,6 +157,42 @@ func TestConfigProviderRequirements(t *testing.T) { } } +func TestConfigProviderRequirementsShallow(t *testing.T) { + cfg, diags := testNestedModuleConfigFromDir(t, "testdata/provider-reqs") + // TODO: Version Constraint Deprecation. + // Once we've removed the version argument from provider configuration + // blocks, this can go back to expected 0 diagnostics. + // assertNoDiagnostics(t, diags) + assertDiagnosticCount(t, diags, 1) + assertDiagnosticSummary(t, diags, "Version constraints inside provider configuration blocks are deprecated") + + tlsProvider := addrs.NewProvider( + addrs.DefaultRegistryHost, + "hashicorp", "tls", + ) + nullProvider := addrs.NewDefaultProvider("null") + randomProvider := addrs.NewDefaultProvider("random") + impliedProvider := addrs.NewDefaultProvider("implied") + terraformProvider := addrs.NewBuiltInProvider("terraform") + configuredProvider := addrs.NewDefaultProvider("configured") + + got, diags := cfg.ProviderRequirementsShallow() + assertNoDiagnostics(t, diags) + want := getproviders.Requirements{ + // the nullProvider constraint is only from the root module + nullProvider: getproviders.MustParseVersionConstraints("~> 2.0.0"), + randomProvider: getproviders.MustParseVersionConstraints("~> 1.2.0"), + tlsProvider: getproviders.MustParseVersionConstraints("~> 3.0"), + configuredProvider: getproviders.MustParseVersionConstraints("~> 1.4"), + impliedProvider: nil, + terraformProvider: nil, + } + + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("wrong result\n%s", diff) + } +} + func TestConfigProviderRequirementsByModule(t *testing.T) { cfg, diags := testNestedModuleConfigFromDir(t, "testdata/provider-reqs") // TODO: Version Constraint Deprecation. diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/getter.go b/vendor/github.com/hashicorp/terraform/configs/configload/getter.go deleted file mode 100644 index d0c4567b..00000000 --- a/vendor/github.com/hashicorp/terraform/configs/configload/getter.go +++ /dev/null @@ -1,168 +0,0 @@ -package configload - -import ( - "fmt" - "log" - "os" - "path/filepath" - - cleanhttp "github.com/hashicorp/go-cleanhttp" - getter "github.com/hashicorp/go-getter" -) - -// We configure our own go-getter detector and getter sets here, because -// the set of sources we support is part of Terraform's documentation and -// so we don't want any new sources introduced in go-getter to sneak in here -// and work even though they aren't documented. This also insulates us from -// any meddling that might be done by other go-getter callers linked into our -// executable. - -var goGetterDetectors = []getter.Detector{ - new(getter.GitHubDetector), - new(getter.GitDetector), - new(getter.BitBucketDetector), - new(getter.GCSDetector), - new(getter.S3Detector), - new(getter.FileDetector), -} - -var goGetterNoDetectors = []getter.Detector{} - -var goGetterDecompressors = map[string]getter.Decompressor{ - "bz2": new(getter.Bzip2Decompressor), - "gz": new(getter.GzipDecompressor), - "xz": new(getter.XzDecompressor), - "zip": new(getter.ZipDecompressor), - - "tar.bz2": new(getter.TarBzip2Decompressor), - "tar.tbz2": new(getter.TarBzip2Decompressor), - - "tar.gz": new(getter.TarGzipDecompressor), - "tgz": new(getter.TarGzipDecompressor), - - "tar.xz": new(getter.TarXzDecompressor), - "txz": new(getter.TarXzDecompressor), -} - -var goGetterGetters = map[string]getter.Getter{ - "file": new(getter.FileGetter), - "gcs": new(getter.GCSGetter), - "git": new(getter.GitGetter), - "hg": new(getter.HgGetter), - "s3": new(getter.S3Getter), - "http": getterHTTPGetter, - "https": getterHTTPGetter, -} - -var getterHTTPClient = cleanhttp.DefaultClient() - -var getterHTTPGetter = &getter.HttpGetter{ - Client: getterHTTPClient, - Netrc: true, -} - -// A reusingGetter is a helper for the module installer that remembers -// the final resolved addresses of all of the sources it has already been -// asked to install, and will copy from a prior installation directory if -// it has the same resolved source address. -// -// The keys in a reusingGetter are resolved and trimmed source addresses -// (with a scheme always present, and without any "subdir" component), -// and the values are the paths where each source was previously installed. -type reusingGetter map[string]string - -// getWithGoGetter retrieves the package referenced in the given address -// into the installation path and then returns the full path to any subdir -// indicated in the address. -// -// The errors returned by this function are those surfaced by the underlying -// go-getter library, which have very inconsistent quality as -// end-user-actionable error messages. At this time we do not have any -// reasonable way to improve these error messages at this layer because -// the underlying errors are not separatelyr recognizable. -func (g reusingGetter) getWithGoGetter(instPath, addr string) (string, error) { - packageAddr, subDir := splitAddrSubdir(addr) - - log.Printf("[DEBUG] will download %q to %s", packageAddr, instPath) - - realAddr, err := getter.Detect(packageAddr, instPath, goGetterDetectors) - if err != nil { - return "", err - } - - var realSubDir string - realAddr, realSubDir = splitAddrSubdir(realAddr) - if realSubDir != "" { - subDir = filepath.Join(realSubDir, subDir) - } - - if realAddr != packageAddr { - log.Printf("[TRACE] go-getter detectors rewrote %q to %q", packageAddr, realAddr) - } - - if prevDir, exists := g[realAddr]; exists { - log.Printf("[TRACE] copying previous install %s to %s", prevDir, instPath) - err := os.Mkdir(instPath, os.ModePerm) - if err != nil { - return "", fmt.Errorf("failed to create directory %s: %s", instPath, err) - } - err = copyDir(instPath, prevDir) - if err != nil { - return "", fmt.Errorf("failed to copy from %s to %s: %s", prevDir, instPath, err) - } - } else { - log.Printf("[TRACE] fetching %q to %q", realAddr, instPath) - client := getter.Client{ - Src: realAddr, - Dst: instPath, - Pwd: instPath, - - Mode: getter.ClientModeDir, - - Detectors: goGetterNoDetectors, // we already did detection above - Decompressors: goGetterDecompressors, - Getters: goGetterGetters, - } - err = client.Get() - if err != nil { - return "", err - } - // Remember where we installed this so we might reuse this directory - // on subsequent calls to avoid re-downloading. - g[realAddr] = instPath - } - - // Our subDir string can contain wildcards until this point, so that - // e.g. a subDir of * can expand to one top-level directory in a .tar.gz - // archive. Now that we've expanded the archive successfully we must - // resolve that into a concrete path. - var finalDir string - if subDir != "" { - finalDir, err = getter.SubdirGlob(instPath, subDir) - log.Printf("[TRACE] expanded %q to %q", subDir, finalDir) - if err != nil { - return "", err - } - } else { - finalDir = instPath - } - - // If we got this far then we have apparently succeeded in downloading - // the requested object! - return filepath.Clean(finalDir), nil -} - -// splitAddrSubdir splits the given address (which is assumed to be a -// registry address or go-getter-style address) into a package portion -// and a sub-directory portion. -// -// The package portion defines what should be downloaded and then the -// sub-directory portion, if present, specifies a sub-directory within -// the downloaded object (an archive, VCS repository, etc) that contains -// the module's configuration files. -// -// The subDir portion will be returned as empty if no subdir separator -// ("//") is present in the address. -func splitAddrSubdir(addr string) (packageAddr, subDir string) { - return getter.SourceDirSubdir(addr) -} diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/loader_load.go b/vendor/github.com/hashicorp/terraform/configs/configload/loader_load.go index eab38495..8f5f48a9 100644 --- a/vendor/github.com/hashicorp/terraform/configs/configload/loader_load.go +++ b/vendor/github.com/hashicorp/terraform/configs/configload/loader_load.go @@ -20,7 +20,7 @@ import ( // required to process the individual modules func (l *Loader) LoadConfig(rootDir string) (*configs.Config, hcl.Diagnostics) { rootMod, diags := l.parser.LoadConfigDir(rootDir) - if rootMod == nil { + if rootMod == nil || diags.HasErrors() { return nil, diags } @@ -100,83 +100,5 @@ func (l *Loader) moduleWalkerLoad(req *configs.ModuleRequest) (*configs.Module, } } - // The providers associated with expanding modules must be present in the proxy/passed providers - // block. Guarding here for accessing the module call just in case. - if mc, exists := req.Parent.Module.ModuleCalls[req.Name]; exists { - var validateDiags hcl.Diagnostics - validateDiags = validateProviderConfigs(mc, mod, req.Parent, validateDiags) - diags = append(diags, validateDiags...) - } return mod, record.Version, diags } - -func validateProviderConfigs(mc *configs.ModuleCall, mod *configs.Module, parent *configs.Config, diags hcl.Diagnostics) hcl.Diagnostics { - if mc.Count != nil || mc.ForEach != nil || mc.DependsOn != nil { - for key, pc := range mod.ProviderConfigs { - // Use these to track if a provider is configured (not allowed), - // or if we've found its matching proxy - var isConfigured bool - var foundMatchingProxy bool - - // Validate the config against an empty schema to see if it's empty. - _, pcConfigDiags := pc.Config.Content(&hcl.BodySchema{}) - if pcConfigDiags.HasErrors() || pc.Version.Required != nil { - isConfigured = true - } - - // If it is empty or only has an alias, - // does this provider exist in our proxy configs? - for _, r := range mc.Providers { - // Must match on name and Alias - if pc.Name == r.InChild.Name && pc.Alias == r.InChild.Alias { - foundMatchingProxy = true - break - } - } - if isConfigured || !foundMatchingProxy { - if mc.Count != nil { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Module does not support count", - Detail: fmt.Sprintf(moduleProviderError, mc.Name, "count", key, pc.NameRange), - Subject: mc.Count.Range().Ptr(), - }) - } - if mc.ForEach != nil { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Module does not support for_each", - Detail: fmt.Sprintf(moduleProviderError, mc.Name, "for_each", key, pc.NameRange), - Subject: mc.ForEach.Range().Ptr(), - }) - } - if mc.DependsOn != nil { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Module does not support depends_on", - Detail: fmt.Sprintf(moduleProviderError, mc.Name, "depends_on", key, pc.NameRange), - Subject: mc.SourceAddrRange.Ptr(), - }) - } - } - } - } - // If this module has further parents, go through them recursively - if !parent.Path.IsRoot() { - // Use the path to get the name so we can look it up in the parent module calls - path := parent.Path - name := path[len(path)-1] - // This parent's module call, so we can check for count/for_each here, - // guarding with exists just in case. We pass the diags through to the recursive - // call so they will accumulate if needed. - if mc, exists := parent.Parent.Module.ModuleCalls[name]; exists { - return validateProviderConfigs(mc, mod, parent.Parent, diags) - } - } - - return diags -} - -var moduleProviderError = `Module "%s" cannot be used with %s because it contains a nested provider configuration for "%s", at %s. - -This module can be made compatible with %[2]s by changing it to receive all of its provider configurations from the calling module, by using the "providers" argument in the calling module block.` diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/loader_load_test.go b/vendor/github.com/hashicorp/terraform/configs/configload/loader_load_test.go index b7f396cf..60fef238 100644 --- a/vendor/github.com/hashicorp/terraform/configs/configload/loader_load_test.go +++ b/vendor/github.com/hashicorp/terraform/configs/configload/loader_load_test.go @@ -1,7 +1,6 @@ package configload import ( - "fmt" "path/filepath" "reflect" "sort" @@ -82,50 +81,9 @@ func TestLoaderLoadConfig_addVersion(t *testing.T) { } } -func TestLoaderLoadConfig_moduleExpand(t *testing.T) { - // We do not allow providers to be configured in expanding modules - // In addition, if a provider is present but an empty block, it is allowed, - // but IFF a provider is passed through the module call - paths := []string{"provider-configured", "no-provider-passed", "nested-provider", "more-nested-provider"} - for _, p := range paths { - fixtureDir := filepath.Clean(fmt.Sprintf("testdata/expand-modules/%s", p)) - loader, err := NewLoader(&Config{ - ModulesDir: filepath.Join(fixtureDir, ".terraform/modules"), - }) - if err != nil { - t.Fatalf("unexpected error from NewLoader at path %s: %s", p, err) - } - - _, diags := loader.LoadConfig(fixtureDir) - if !diags.HasErrors() { - t.Fatalf("success; want error at path %s", p) - } - got := diags.Error() - want := "Module does not support count" - if !strings.Contains(got, want) { - t.Fatalf("wrong error at path %s \ngot:\n%s\n\nwant: containing %q", p, got, want) - } - } -} - -func TestLoaderLoadConfig_moduleExpandValid(t *testing.T) { - // This tests for when valid configs are passing a provider through as a proxy, - // either with or without an alias present. - fixtureDir := filepath.Clean("testdata/expand-modules/valid") - loader, err := NewLoader(&Config{ - ModulesDir: filepath.Join(fixtureDir, ".terraform/modules"), - }) - if err != nil { - t.Fatalf("unexpected error from NewLoader: %s", err) - } - - _, diags := loader.LoadConfig(fixtureDir) - assertNoDiagnostics(t, diags) -} - -func TestLoaderLoadConfig_moduleDependsOnProviders(t *testing.T) { - // We do not allow providers to be configured in module using depends_on. - fixtureDir := filepath.Clean("testdata/module-depends-on") +func TestLoaderLoadConfig_loadDiags(t *testing.T) { + // building a config which didn't load correctly may cause configs to panic + fixtureDir := filepath.Clean("testdata/invalid-names") loader, err := NewLoader(&Config{ ModulesDir: filepath.Join(fixtureDir, ".terraform/modules"), }) @@ -135,11 +93,6 @@ func TestLoaderLoadConfig_moduleDependsOnProviders(t *testing.T) { _, diags := loader.LoadConfig(fixtureDir) if !diags.HasErrors() { - t.Fatal("success; want error") - } - got := diags.Error() - want := "Module does not support depends_on" - if !strings.Contains(got, want) { - t.Fatalf("wrong error\ngot:\n%s\n\nwant: containing %q", got, want) + t.Fatalf("success; want error") } } diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/loader_test.go b/vendor/github.com/hashicorp/terraform/configs/configload/loader_test.go index 54b6ed2f..7b3483b4 100644 --- a/vendor/github.com/hashicorp/terraform/configs/configload/loader_test.go +++ b/vendor/github.com/hashicorp/terraform/configs/configload/loader_test.go @@ -1,92 +1,12 @@ package configload import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" "testing" - "github.com/go-test/deep" "github.com/hashicorp/hcl/v2" "github.com/zclconf/go-cty/cty" ) -// tempChdir copies the contents of the given directory to a temporary -// directory and changes the test process's current working directory to -// point to that directory. Also returned is a function that should be -// called at the end of the test (e.g. via "defer") to restore the previous -// working directory. -// -// Tests using this helper cannot safely be run in parallel with other tests. -func tempChdir(t *testing.T, sourceDir string) (string, func()) { - t.Helper() - - tmpDir, err := ioutil.TempDir("", "terraform-configload") - if err != nil { - t.Fatalf("failed to create temporary directory: %s", err) - return "", nil - } - - if err := copyDir(tmpDir, sourceDir); err != nil { - t.Fatalf("failed to copy fixture to temporary directory: %s", err) - return "", nil - } - - oldDir, err := os.Getwd() - if err != nil { - t.Fatalf("failed to determine current working directory: %s", err) - return "", nil - } - - err = os.Chdir(tmpDir) - if err != nil { - t.Fatalf("failed to switch to temp dir %s: %s", tmpDir, err) - return "", nil - } - - t.Logf("tempChdir switched to %s after copying from %s", tmpDir, sourceDir) - - return tmpDir, func() { - err := os.Chdir(oldDir) - if err != nil { - panic(fmt.Errorf("failed to restore previous working directory %s: %s", oldDir, err)) - } - - if os.Getenv("TF_CONFIGLOAD_TEST_KEEP_TMP") == "" { - os.RemoveAll(tmpDir) - } - } -} - -// tempChdirLoader is a wrapper around tempChdir that also returns a Loader -// whose modules directory is at the conventional location within the -// created temporary directory. -func tempChdirLoader(t *testing.T, sourceDir string) (*Loader, func()) { - t.Helper() - - _, done := tempChdir(t, sourceDir) - modulesDir := filepath.Clean(".terraform/modules") - - err := os.MkdirAll(modulesDir, os.ModePerm) - if err != nil { - done() // undo the chdir in tempChdir so we can safely run other tests - t.Fatalf("failed to create modules directory: %s", err) - return nil, nil - } - - loader, err := NewLoader(&Config{ - ModulesDir: modulesDir, - }) - if err != nil { - done() // undo the chdir in tempChdir so we can safely run other tests - t.Fatalf("failed to create loader: %s", err) - return nil, nil - } - - return loader, done -} - func assertNoDiagnostics(t *testing.T, diags hcl.Diagnostics) bool { t.Helper() return assertDiagnosticCount(t, diags, 0) @@ -103,34 +23,6 @@ func assertDiagnosticCount(t *testing.T, diags hcl.Diagnostics, want int) bool { } return false } - -func assertDiagnosticSummary(t *testing.T, diags hcl.Diagnostics, want string) bool { - t.Helper() - - for _, diag := range diags { - if diag.Summary == want { - return false - } - } - - t.Errorf("missing diagnostic summary %q", want) - for _, diag := range diags { - t.Logf("- %s", diag) - } - return true -} - -func assertResultDeepEqual(t *testing.T, got, want interface{}) bool { - t.Helper() - if diff := deep.Equal(got, want); diff != nil { - for _, problem := range diff { - t.Errorf("%s", problem) - } - return true - } - return false -} - func assertResultCtyEqual(t *testing.T, got, want cty.Value) bool { t.Helper() if !got.RawEquals(want) { diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/module_mgr.go b/vendor/github.com/hashicorp/terraform/configs/configload/module_mgr.go index 16871e31..cf930f53 100644 --- a/vendor/github.com/hashicorp/terraform/configs/configload/module_mgr.go +++ b/vendor/github.com/hashicorp/terraform/configs/configload/module_mgr.go @@ -60,17 +60,3 @@ func (m *moduleMgr) readModuleManifestSnapshot() error { m.manifest, err = modsdir.ReadManifestSnapshot(r) return err } - -// writeModuleManifestSnapshot writes a snapshot of the current manifest -// to the filesystem. -// -// The caller must guarantee no concurrent modifications of the manifest for -// the duration of a call to this function, or the behavior is undefined. -func (m *moduleMgr) writeModuleManifestSnapshot() error { - w, err := m.FS.Create(m.manifestSnapshotPath()) - if err != nil { - return err - } - - return m.manifest.WriteSnapshot(w) -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-path-module/child/main.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/add-version-constraint/.terraform/modules/child/empty.tf similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-path-module/child/main.tf rename to vendor/github.com/hashicorp/terraform/configs/configload/testdata/add-version-constraint/.terraform/modules/child/empty.tf diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/add-version-constraint/.terraform/modules/modules.json b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/add-version-constraint/.terraform/modules/modules.json new file mode 100644 index 00000000..c02f4001 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/add-version-constraint/.terraform/modules/modules.json @@ -0,0 +1,14 @@ +{ + "Modules": [ + { + "Key": "", + "Source": "", + "Dir": "testdata/add-version-constraint" + }, + { + "Key": "child", + "Source": "hashicorp/module-installer-acctest/aws", + "Dir": "testdata/add-version-constraint/.terraform/modules/child" + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/already-installed/.terraform/modules/child_a/child_a.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/already-installed/.terraform/modules/child_a/child_a.tf new file mode 100644 index 00000000..2f4d0f1a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/already-installed/.terraform/modules/child_a/child_a.tf @@ -0,0 +1,4 @@ + +module "child_c" { + source = "./child_c" +} diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/already-installed/.terraform/modules/child_a/child_c/child_c.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/already-installed/.terraform/modules/child_a/child_c/child_c.tf new file mode 100644 index 00000000..785d98d9 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/already-installed/.terraform/modules/child_a/child_c/child_c.tf @@ -0,0 +1,4 @@ + +output "hello" { + value = "Hello from child_c" +} diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/already-installed/.terraform/modules/child_b.child_d/child_d.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/already-installed/.terraform/modules/child_b.child_d/child_d.tf new file mode 100644 index 00000000..145576a3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/already-installed/.terraform/modules/child_b.child_d/child_d.tf @@ -0,0 +1,4 @@ + +output "hello" { + value = "Hello from child_d" +} diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/already-installed/.terraform/modules/child_b/child_b.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/already-installed/.terraform/modules/child_b/child_b.tf new file mode 100644 index 00000000..4a1b247d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/already-installed/.terraform/modules/child_b/child_b.tf @@ -0,0 +1,5 @@ + +module "child_d" { + source = "example.com/foo/bar_d/baz" + # Intentionally no version here +} diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/already-installed/.terraform/modules/modules.json b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/already-installed/.terraform/modules/modules.json new file mode 100644 index 00000000..43439865 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/already-installed/.terraform/modules/modules.json @@ -0,0 +1 @@ +{"Modules":[{"Key":"","Source":"","Dir":"testdata/already-installed"},{"Key":"child_a","Source":"example.com/foo/bar_a/baz","Version":"1.0.1","Dir":"testdata/already-installed/.terraform/modules/child_a"},{"Key":"child_b","Source":"example.com/foo/bar_b/baz","Version":"1.0.0","Dir":"testdata/already-installed/.terraform/modules/child_b"},{"Key":"child_a.child_c","Source":"./child_c","Dir":"testdata/already-installed/.terraform/modules/child_a/child_c"},{"Key":"child_b.child_d","Source":"example.com/foo/bar_d/baz","Version":"1.2.0","Dir":"testdata/already-installed/.terraform/modules/child_b.child_d"}]} \ No newline at end of file diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/more-nested-provider/child/main.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/more-nested-provider/child/main.tf deleted file mode 100644 index b4bbb38c..00000000 --- a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/more-nested-provider/child/main.tf +++ /dev/null @@ -1,4 +0,0 @@ -module "child2" { - source = "../child2" - -} diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/more-nested-provider/child2/main.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/more-nested-provider/child2/main.tf deleted file mode 100644 index d107faad..00000000 --- a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/more-nested-provider/child2/main.tf +++ /dev/null @@ -1,4 +0,0 @@ -module "child3" { - source = "../child3" - -} diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/more-nested-provider/child3/main.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/more-nested-provider/child3/main.tf deleted file mode 100644 index 01cd8542..00000000 --- a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/more-nested-provider/child3/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -provider "aws" { - -} - -output "my_output" { - value = "my output" -} \ No newline at end of file diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/nested-provider/child/main.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/nested-provider/child/main.tf deleted file mode 100644 index b4bbb38c..00000000 --- a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/nested-provider/child/main.tf +++ /dev/null @@ -1,4 +0,0 @@ -module "child2" { - source = "../child2" - -} diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/nested-provider/child2/main.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/nested-provider/child2/main.tf deleted file mode 100644 index 01cd8542..00000000 --- a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/nested-provider/child2/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -provider "aws" { - -} - -output "my_output" { - value = "my output" -} \ No newline at end of file diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/nested-provider/root.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/nested-provider/root.tf deleted file mode 100644 index 71b90f6d..00000000 --- a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/nested-provider/root.tf +++ /dev/null @@ -1,4 +0,0 @@ -module "child" { - count = 1 - source = "./child" -} diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/no-provider-passed/child/main.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/no-provider-passed/child/main.tf deleted file mode 100644 index a5c3c47b..00000000 --- a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/no-provider-passed/child/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -provider "aws" { -} - -output "my_output" { - value = "my output" -} - diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/no-provider-passed/root.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/no-provider-passed/root.tf deleted file mode 100644 index 195cfeb5..00000000 --- a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/no-provider-passed/root.tf +++ /dev/null @@ -1,9 +0,0 @@ -provider "aws" { - alias = "usw2" - region = "us-west-2" -} -module "child" { - count = 1 - source = "./child" - # To make this test fail, add a valid providers {} block passing "aws" to the child -} diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/provider-configured/child/main.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/provider-configured/child/main.tf deleted file mode 100644 index 61ff4b57..00000000 --- a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/provider-configured/child/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -provider "aws" { - region = "us-west-2" -} - -output "my_output" { - value = "my output" -} diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/provider-configured/root.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/provider-configured/root.tf deleted file mode 100644 index 953d4ab5..00000000 --- a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/provider-configured/root.tf +++ /dev/null @@ -1,11 +0,0 @@ -provider "aws" { - region = "us-west-2" -} - -module "child" { - count = 1 - source = "./child" - providers = { - aws = aws.w2 - } -} diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/valid/child-with-alias/main.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/valid/child-with-alias/main.tf deleted file mode 100644 index 3a59131c..00000000 --- a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/valid/child-with-alias/main.tf +++ /dev/null @@ -1,8 +0,0 @@ -provider "aws" { - alias = "east" -} - -output "my_output" { - value = "my output" -} - diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/valid/child/main.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/valid/child/main.tf deleted file mode 100644 index a5c3c47b..00000000 --- a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/valid/child/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -provider "aws" { -} - -output "my_output" { - value = "my output" -} - diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/valid/root.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/valid/root.tf deleted file mode 100644 index 27205a50..00000000 --- a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/valid/root.tf +++ /dev/null @@ -1,20 +0,0 @@ -provider "aws" { - region = "us-east-1" - alias = "east" -} - -module "child" { - count = 1 - source = "./child" - providers = { - aws = aws.east - } -} - -module "child_with_alias" { - for_each = toset(["a", "b"]) - source = "./child-with-alias" - providers = { - aws.east = aws.east - } -} \ No newline at end of file diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/invalid-names/main.tf b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/invalid-names/main.tf new file mode 100644 index 00000000..d4eee4c3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/invalid-names/main.tf @@ -0,0 +1,3 @@ +provider "42_bad!" { + invalid_provider_name = "yes" +} diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/module-depends-on/.terraform/modules/modules.json b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/module-depends-on/.terraform/modules/modules.json new file mode 100644 index 00000000..28f81303 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/testdata/module-depends-on/.terraform/modules/modules.json @@ -0,0 +1,24 @@ +{ + "Modules": [ + { + "Key": "", + "Source": "", + "Dir": "testdata/expand-modules/nested-provider" + }, + { + "Key": "child", + "Source": "./child", + "Dir": "testdata/expand-modules/nested-provider/child" + }, + { + "Key": "child2", + "Source": "./child2", + "Dir": "testdata/expand-modules/nested-provider/child2" + }, + { + "Key": "child.child2", + "Source": "../child2", + "Dir": "testdata/expand-modules/nested-provider/child2" + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/decoder_spec.go b/vendor/github.com/hashicorp/terraform/configs/configschema/decoder_spec.go index f7193ec0..33327450 100644 --- a/vendor/github.com/hashicorp/terraform/configs/configschema/decoder_spec.go +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/decoder_spec.go @@ -6,6 +6,7 @@ import ( "unsafe" "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" ) var mapLabelNames = []string{"key"} @@ -91,24 +92,15 @@ func (b *Block) DecoderSpec() hcldec.Spec { for name, blockS := range b.BlockTypes { if _, exists := ret[name]; exists { - // This indicates an invalid schema, since it's not valid to - // define both an attribute and a block type of the same name. - // However, we don't raise this here since it's checked by - // InternalValidate. + // This indicates an invalid schema, since it's not valid to define + // both an attribute and a block type of the same name. We assume + // that the provider has already used something like + // InternalValidate to validate their schema. continue } childSpec := blockS.Block.DecoderSpec() - // We can only validate 0 or 1 for MinItems, because a dynamic block - // may satisfy any number of min items while only having a single - // block in the config. We cannot validate MaxItems because a - // configuration may have any number of dynamic blocks - minItems := 0 - if blockS.MinItems > 1 { - minItems = 1 - } - switch blockS.Nesting { case NestingSingle, NestingGroup: ret[name] = &hcldec.BlockSpec{ @@ -133,25 +125,30 @@ func (b *Block) DecoderSpec() hcldec.Spec { ret[name] = &hcldec.BlockTupleSpec{ TypeName: name, Nested: childSpec, - MinItems: minItems, + MinItems: blockS.MinItems, + MaxItems: blockS.MaxItems, } } else { ret[name] = &hcldec.BlockListSpec{ TypeName: name, Nested: childSpec, - MinItems: minItems, + MinItems: blockS.MinItems, + MaxItems: blockS.MaxItems, } } case NestingSet: // We forbid dynamically-typed attributes inside NestingSet in - // InternalValidate, so we don't do anything special to handle - // that here. (There is no set analog to tuple and object types, - // because cty's set implementation depends on knowing the static - // type in order to properly compute its internal hashes.) + // InternalValidate, so we don't do anything special to handle that + // here. (There is no set analog to tuple and object types, because + // cty's set implementation depends on knowing the static type in + // order to properly compute its internal hashes.) We assume that + // the provider has already used something like InternalValidate to + // validate their schema. ret[name] = &hcldec.BlockSetSpec{ TypeName: name, Nested: childSpec, - MinItems: minItems, + MinItems: blockS.MinItems, + MaxItems: blockS.MaxItems, } case NestingMap: // We prefer to use a list where possible, since it makes our @@ -173,7 +170,8 @@ func (b *Block) DecoderSpec() hcldec.Spec { } default: // Invalid nesting type is just ignored. It's checked by - // InternalValidate. + // InternalValidate. We assume that the provider has already used + // something like InternalValidate to validate their schema. continue } } @@ -183,9 +181,41 @@ func (b *Block) DecoderSpec() hcldec.Spec { } func (a *Attribute) decoderSpec(name string) hcldec.Spec { - return &hcldec.AttrSpec{ - Name: name, - Type: a.Type, - Required: a.Required, + ret := &hcldec.AttrSpec{Name: name} + if a == nil { + return ret + } + + if a.NestedType != nil { + // FIXME: a panic() is a bad UX. InternalValidate() can check Attribute + // schemas as well so a fix might be to call it when we get the schema + // from the provider in Context(). Since this could be a breaking + // change, we'd need to communicate well before adding that call. + if a.Type != cty.NilType { + panic("Invalid attribute schema: NestedType and Type cannot both be set. This is a bug in the provider.") + } + + ty := a.NestedType.ImpliedType() + ret.Type = ty + ret.Required = a.Required || a.NestedType.MinItems > 0 + return ret } + + ret.Type = a.Type + ret.Required = a.Required + return ret +} + +// listOptionalAttrsFromObject is a helper function which does *not* recurse +// into NestedType Attributes, because the optional types for each of those will +// belong to their own cty.Object definitions. It is used in other functions +// which themselves handle that recursion. +func listOptionalAttrsFromObject(o *Object) []string { + var ret []string + for name, attr := range o.Attributes { + if attr.Optional == true { + ret = append(ret, name) + } + } + return ret } diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/decoder_spec_test.go b/vendor/github.com/hashicorp/terraform/configs/configschema/decoder_spec_test.go index 492ee5da..a6571eaa 100644 --- a/vendor/github.com/hashicorp/terraform/configs/configschema/decoder_spec_test.go +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/decoder_spec_test.go @@ -344,15 +344,12 @@ func TestBlockDecoderSpec(t *testing.T) { }, &hcl.Block{ Type: "foo", - Body: hcl.EmptyBody(), + Body: unknownBody{hcl.EmptyBody()}, }, }, }), cty.ObjectVal(map[string]cty.Value{ - "foo": cty.ListVal([]cty.Value{ - cty.EmptyObjectVal, - cty.EmptyObjectVal, - }), + "foo": cty.UnknownVal(cty.List(cty.EmptyObject)), }), 0, // max items cannot be validated during decode }, @@ -372,14 +369,12 @@ func TestBlockDecoderSpec(t *testing.T) { Blocks: hcl.Blocks{ &hcl.Block{ Type: "foo", - Body: hcl.EmptyBody(), + Body: unknownBody{hcl.EmptyBody()}, }, }, }), cty.ObjectVal(map[string]cty.Value{ - "foo": cty.ListVal([]cty.Value{ - cty.EmptyObjectVal, - }), + "foo": cty.UnknownVal(cty.List(cty.EmptyObject)), }), 0, }, @@ -401,6 +396,7 @@ func TestBlockDecoderSpec(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { spec := test.Schema.DecoderSpec() + got, diags := hcldec.Decode(test.TestBody, spec, nil) if len(diags) != test.DiagCount { t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.DiagCount) @@ -426,3 +422,466 @@ func TestBlockDecoderSpec(t *testing.T) { }) } } + +// this satisfies hcldec.UnknownBody to simulate a dynamic block with an +// unknown number of values. +type unknownBody struct { + hcl.Body +} + +func (b unknownBody) Unknown() bool { + return true +} + +func TestAttributeDecoderSpec(t *testing.T) { + tests := map[string]struct { + Schema *Attribute + TestBody hcl.Body + Want cty.Value + DiagCount int + }{ + "empty": { + &Attribute{}, + hcl.EmptyBody(), + cty.NilVal, + 0, + }, + "nil": { + nil, + hcl.EmptyBody(), + cty.NilVal, + 0, + }, + "optional string (null)": { + &Attribute{ + Type: cty.String, + Optional: true, + }, + hcltest.MockBody(&hcl.BodyContent{}), + cty.NullVal(cty.String), + 0, + }, + "optional string": { + &Attribute{ + Type: cty.String, + Optional: true, + }, + hcltest.MockBody(&hcl.BodyContent{ + Attributes: hcl.Attributes{ + "attr": { + Name: "attr", + Expr: hcltest.MockExprLiteral(cty.StringVal("bar")), + }, + }, + }), + cty.StringVal("bar"), + 0, + }, + "NestedType with required string": { + &Attribute{ + NestedType: &Object{ + Nesting: NestingSingle, + Attributes: map[string]*Attribute{ + "foo": { + Type: cty.String, + Required: true, + }, + }, + }, + Optional: true, + }, + hcltest.MockBody(&hcl.BodyContent{ + Attributes: hcl.Attributes{ + "attr": { + Name: "attr", + Expr: hcltest.MockExprLiteral(cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + })), + }, + }, + }), + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + }), + 0, + }, + "NestedType with optional attributes": { + &Attribute{ + NestedType: &Object{ + Nesting: NestingSingle, + Attributes: map[string]*Attribute{ + "foo": { + Type: cty.String, + Optional: true, + }, + "bar": { + Type: cty.String, + Optional: true, + }, + }, + }, + Optional: true, + }, + hcltest.MockBody(&hcl.BodyContent{ + Attributes: hcl.Attributes{ + "attr": { + Name: "attr", + Expr: hcltest.MockExprLiteral(cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + })), + }, + }, + }), + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + "bar": cty.NullVal(cty.String), + }), + 0, + }, + "NestedType with missing required string": { + &Attribute{ + NestedType: &Object{ + Nesting: NestingSingle, + Attributes: map[string]*Attribute{ + "foo": { + Type: cty.String, + Required: true, + }, + }, + }, + Optional: true, + }, + hcltest.MockBody(&hcl.BodyContent{ + Attributes: hcl.Attributes{ + "attr": { + Name: "attr", + Expr: hcltest.MockExprLiteral(cty.EmptyObjectVal), + }, + }, + }), + cty.UnknownVal(cty.Object(map[string]cty.Type{ + "foo": cty.String, + })), + 1, + }, + // NestedModes + "NestedType NestingModeList valid": { + &Attribute{ + NestedType: &Object{ + Nesting: NestingList, + Attributes: map[string]*Attribute{ + "foo": { + Type: cty.String, + Required: true, + }, + }, + }, + Optional: true, + }, + hcltest.MockBody(&hcl.BodyContent{ + Attributes: hcl.Attributes{ + "attr": { + Name: "attr", + Expr: hcltest.MockExprLiteral(cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + }), + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("baz"), + }), + })), + }, + }, + }), + cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("bar")}), + cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("baz")}), + }), + 0, + }, + "NestedType NestingModeList invalid": { + &Attribute{ + NestedType: &Object{ + Nesting: NestingList, + Attributes: map[string]*Attribute{ + "foo": { + Type: cty.String, + Required: true, + }, + }, + }, + Optional: true, + }, + hcltest.MockBody(&hcl.BodyContent{ + Attributes: hcl.Attributes{ + "attr": { + Name: "attr", + Expr: hcltest.MockExprLiteral(cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + // "foo" should be a string, not a list + "foo": cty.ListVal([]cty.Value{cty.StringVal("bar"), cty.StringVal("baz")}), + })})), + }, + }, + }), + cty.UnknownVal(cty.List(cty.Object(map[string]cty.Type{"foo": cty.String}))), + 1, + }, + "NestedType NestingModeSet valid": { + &Attribute{ + NestedType: &Object{ + Nesting: NestingSet, + Attributes: map[string]*Attribute{ + "foo": { + Type: cty.String, + Required: true, + }, + }, + }, + Optional: true, + }, + hcltest.MockBody(&hcl.BodyContent{ + Attributes: hcl.Attributes{ + "attr": { + Name: "attr", + Expr: hcltest.MockExprLiteral(cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + }), + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("baz"), + }), + })), + }, + }, + }), + cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("bar")}), + cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("baz")}), + }), + 0, + }, + "NestedType NestingModeSet invalid": { + &Attribute{ + NestedType: &Object{ + Nesting: NestingSet, + Attributes: map[string]*Attribute{ + "foo": { + Type: cty.String, + Required: true, + }, + }, + }, + Optional: true, + }, + hcltest.MockBody(&hcl.BodyContent{ + Attributes: hcl.Attributes{ + "attr": { + Name: "attr", + Expr: hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + // "foo" should be a string, not a list + "foo": cty.ListVal([]cty.Value{cty.StringVal("bar"), cty.StringVal("baz")}), + })})), + }, + }, + }), + cty.UnknownVal(cty.Set(cty.Object(map[string]cty.Type{"foo": cty.String}))), + 1, + }, + "NestedType NestingModeMap valid": { + &Attribute{ + NestedType: &Object{ + Nesting: NestingMap, + Attributes: map[string]*Attribute{ + "foo": { + Type: cty.String, + Required: true, + }, + }, + }, + Optional: true, + }, + hcltest.MockBody(&hcl.BodyContent{ + Attributes: hcl.Attributes{ + "attr": { + Name: "attr", + Expr: hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{ + "one": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + }), + "two": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("baz"), + }), + })), + }, + }, + }), + cty.MapVal(map[string]cty.Value{ + "one": cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("bar")}), + "two": cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("baz")}), + }), + 0, + }, + "NestedType NestingModeMap invalid": { + &Attribute{ + NestedType: &Object{ + Nesting: NestingMap, + Attributes: map[string]*Attribute{ + "foo": { + Type: cty.String, + Required: true, + }, + }, + }, + Optional: true, + }, + hcltest.MockBody(&hcl.BodyContent{ + Attributes: hcl.Attributes{ + "attr": { + Name: "attr", + Expr: hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{ + "one": cty.ObjectVal(map[string]cty.Value{ + // "foo" should be a string, not a list + "foo": cty.ListVal([]cty.Value{cty.StringVal("bar"), cty.StringVal("baz")}), + }), + })), + }, + }, + }), + cty.UnknownVal(cty.Map(cty.Object(map[string]cty.Type{"foo": cty.String}))), + 1, + }, + "deeply NestedType NestingModeList valid": { + &Attribute{ + NestedType: &Object{ + Nesting: NestingList, + Attributes: map[string]*Attribute{ + "foo": { + NestedType: &Object{ + Nesting: NestingList, + Attributes: map[string]*Attribute{ + "bar": { + Type: cty.String, + Required: true, + }, + }, + }, + Required: true, + }, + }, + }, + Optional: true, + }, + hcltest.MockBody(&hcl.BodyContent{ + Attributes: hcl.Attributes{ + "attr": { + Name: "attr", + Expr: hcltest.MockExprLiteral(cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")}), + cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("boz")}), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("biz")}), + cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("buz")}), + }), + }), + })), + }, + }, + }), + cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{"foo": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")}), + cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("boz")}), + })}), + cty.ObjectVal(map[string]cty.Value{"foo": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("biz")}), + cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("buz")}), + })}), + }), + 0, + }, + "deeply NestedType NestingList invalid": { + &Attribute{ + NestedType: &Object{ + Nesting: NestingList, + Attributes: map[string]*Attribute{ + "foo": { + NestedType: &Object{ + Nesting: NestingList, + Attributes: map[string]*Attribute{ + "bar": { + Type: cty.Number, + Required: true, + }, + }, + }, + Required: true, + }, + }, + }, + Optional: true, + }, + hcltest.MockBody(&hcl.BodyContent{ + Attributes: hcl.Attributes{ + "attr": { + Name: "attr", + Expr: hcltest.MockExprLiteral(cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ListVal([]cty.Value{ + // bar should be a Number + cty.ObjectVal(map[string]cty.Value{"bar": cty.True}), + cty.ObjectVal(map[string]cty.Value{"bar": cty.False}), + }), + }), + })), + }, + }, + }), + cty.UnknownVal(cty.List(cty.Object(map[string]cty.Type{ + "foo": cty.List(cty.Object(map[string]cty.Type{"bar": cty.Number})), + }))), + 1, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + spec := test.Schema.decoderSpec("attr") + got, diags := hcldec.Decode(test.TestBody, spec, nil) + if len(diags) != test.DiagCount { + t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.DiagCount) + for _, diag := range diags { + t.Logf("- %s", diag.Error()) + } + } + + if !got.RawEquals(test.Want) { + t.Logf("[INFO] implied schema is %s", spew.Sdump(hcldec.ImpliedSchema(spec))) + t.Errorf("wrong result\ngot: %s\nwant: %s", dump.Value(got), dump.Value(test.Want)) + } + }) + } + +} + +// TestAttributeDecodeSpec_panic is a temporary test which verifies that +// decoderSpec panics when an invalid Attribute schema is encountered. It will +// be removed when InternalValidate() is extended to validate Attribute specs +// (and is used). See the #FIXME in decoderSpec. +func TestAttributeDecoderSpec_panic(t *testing.T) { + attrS := &Attribute{ + Type: cty.Object(map[string]cty.Type{ + "nested_attribute": cty.String, + }), + NestedType: &Object{}, + Optional: true, + } + + defer func() { recover() }() + attrS.decoderSpec("attr") + t.Errorf("expected panic") +} diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/empty_value.go b/vendor/github.com/hashicorp/terraform/configs/configschema/empty_value.go index 005da56b..ea8cdbbc 100644 --- a/vendor/github.com/hashicorp/terraform/configs/configschema/empty_value.go +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/empty_value.go @@ -26,6 +26,9 @@ func (b *Block) EmptyValue() cty.Value { // the value that would be returned if there were no definition of the attribute // at all, ignoring any required constraint. func (a *Attribute) EmptyValue() cty.Value { + if a.NestedType != nil { + return cty.NullVal(a.NestedType.ImpliedType()) + } return cty.NullVal(a.Type) } diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/empty_value_test.go b/vendor/github.com/hashicorp/terraform/configs/configschema/empty_value_test.go index 44d27fe7..a9d8e307 100644 --- a/vendor/github.com/hashicorp/terraform/configs/configschema/empty_value_test.go +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/empty_value_test.go @@ -168,3 +168,90 @@ func TestBlockEmptyValue(t *testing.T) { }) } } + +// Attribute EmptyValue() is well covered by the Block tests above; these tests +// focus on the behavior with NestedType field inside an Attribute +func TestAttributeEmptyValue(t *testing.T) { + tests := []struct { + Schema *Attribute + Want cty.Value + }{ + { + &Attribute{}, + cty.NilVal, + }, + { + &Attribute{ + Type: cty.String, + }, + cty.NullVal(cty.String), + }, + { + &Attribute{ + NestedType: &Object{ + Nesting: NestingSingle, + Attributes: map[string]*Attribute{ + "str": {Type: cty.String, Required: true}, + }, + }, + }, + cty.NullVal(cty.Object(map[string]cty.Type{ + "str": cty.String, + })), + }, + { + &Attribute{ + NestedType: &Object{ + Nesting: NestingList, + Attributes: map[string]*Attribute{ + "str": {Type: cty.String, Required: true}, + }, + }, + }, + cty.NullVal(cty.List( + cty.Object(map[string]cty.Type{ + "str": cty.String, + }), + )), + }, + { + &Attribute{ + NestedType: &Object{ + Nesting: NestingMap, + Attributes: map[string]*Attribute{ + "str": {Type: cty.String, Required: true}, + }, + }, + }, + cty.NullVal(cty.Map( + cty.Object(map[string]cty.Type{ + "str": cty.String, + }), + )), + }, + { + &Attribute{ + NestedType: &Object{ + Nesting: NestingSet, + Attributes: map[string]*Attribute{ + "str": {Type: cty.String, Required: true}, + }, + }, + }, + cty.NullVal(cty.Set( + cty.Object(map[string]cty.Type{ + "str": cty.String, + }), + )), + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%#v", test.Schema), func(t *testing.T) { + got := test.Schema.EmptyValue() + if !test.Want.RawEquals(got) { + t.Errorf("wrong result\nschema: %s\ngot: %s\nwant: %s", spew.Sdump(test.Schema), dump.Value(got), dump.Value(test.Want)) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/implied_type.go b/vendor/github.com/hashicorp/terraform/configs/configschema/implied_type.go index a81b7eab..4d4a042c 100644 --- a/vendor/github.com/hashicorp/terraform/configs/configschema/implied_type.go +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/implied_type.go @@ -40,3 +40,59 @@ func (b *Block) ContainsSensitive() bool { } return false } + +// ImpliedType returns the cty.Type that would result from decoding a NestedType +// Attribute using the receiving block schema. +// +// ImpliedType always returns a result, even if the given schema is +// inconsistent. Code that creates configschema.Object objects should be tested +// using the InternalValidate method to detect any inconsistencies that would +// cause this method to fall back on defaults and assumptions. +func (o *Object) ImpliedType() cty.Type { + if o == nil { + return cty.EmptyObject + } + + attrTys := make(map[string]cty.Type, len(o.Attributes)) + for name, attrS := range o.Attributes { + if attrS.NestedType != nil { + attrTys[name] = attrS.NestedType.ImpliedType() + } else { + attrTys[name] = attrS.Type + } + } + optAttrs := listOptionalAttrsFromObject(o) + + var ret cty.Type + if len(optAttrs) > 0 { + ret = cty.ObjectWithOptionalAttrs(attrTys, optAttrs) + } else { + ret = cty.Object(attrTys) + } + switch o.Nesting { + case NestingSingle: + return ret + case NestingList: + return cty.List(ret) + case NestingMap: + return cty.Map(ret) + case NestingSet: + return cty.Set(ret) + default: // Should never happen + panic("invalid Nesting") + } +} + +// ContainsSensitive returns true if any of the attributes of the receiving +// Object are marked as sensitive. +func (o *Object) ContainsSensitive() bool { + for _, attrS := range o.Attributes { + if attrS.Sensitive { + return true + } + if attrS.NestedType != nil { + return attrS.NestedType.ContainsSensitive() + } + } + return false +} diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/implied_type_test.go b/vendor/github.com/hashicorp/terraform/configs/configschema/implied_type_test.go index 85e4a88d..7cd0f730 100644 --- a/vendor/github.com/hashicorp/terraform/configs/configschema/implied_type_test.go +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/implied_type_test.go @@ -122,3 +122,202 @@ func TestBlockImpliedType(t *testing.T) { }) } } + +func TestObjectImpliedType(t *testing.T) { + tests := map[string]struct { + Schema *Object + Want cty.Type + }{ + "nil": { + nil, + cty.EmptyObject, + }, + "attributes": { + &Object{ + Nesting: NestingSingle, + Attributes: map[string]*Attribute{ + "optional": { + Type: cty.String, + Optional: true, + }, + "required": { + Type: cty.Number, + Required: true, + }, + "computed": { + Type: cty.List(cty.Bool), + Computed: true, + }, + "optional_computed": { + Type: cty.Map(cty.Bool), + Optional: true, + }, + }, + }, + cty.ObjectWithOptionalAttrs( + map[string]cty.Type{ + "optional": cty.String, + "required": cty.Number, + "computed": cty.List(cty.Bool), + "optional_computed": cty.Map(cty.Bool), + }, + []string{"optional", "optional_computed"}, + ), + }, + "nested attributes": { + &Object{ + Nesting: NestingSingle, + Attributes: map[string]*Attribute{ + "nested_type": { + NestedType: &Object{ + Nesting: NestingSingle, + Attributes: map[string]*Attribute{ + "optional": { + Type: cty.String, + Optional: true, + }, + "required": { + Type: cty.Number, + Required: true, + }, + "computed": { + Type: cty.List(cty.Bool), + Computed: true, + }, + "optional_computed": { + Type: cty.Map(cty.Bool), + Optional: true, + }, + }, + }, + Optional: true, + }, + }, + }, + cty.ObjectWithOptionalAttrs(map[string]cty.Type{ + "nested_type": cty.ObjectWithOptionalAttrs(map[string]cty.Type{ + "optional": cty.String, + "required": cty.Number, + "computed": cty.List(cty.Bool), + "optional_computed": cty.Map(cty.Bool), + }, []string{"optional", "optional_computed"}), + }, []string{"nested_type"}), + }, + "NestingList": { + &Object{ + Nesting: NestingList, + Attributes: map[string]*Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, + }, + cty.List(cty.ObjectWithOptionalAttrs(map[string]cty.Type{"foo": cty.String}, []string{"foo"})), + }, + "NestingMap": { + &Object{ + Nesting: NestingMap, + Attributes: map[string]*Attribute{ + "foo": {Type: cty.String}, + }, + }, + cty.Map(cty.Object(map[string]cty.Type{"foo": cty.String})), + }, + "NestingSet": { + &Object{ + Nesting: NestingSet, + Attributes: map[string]*Attribute{ + "foo": {Type: cty.String}, + }, + }, + cty.Set(cty.Object(map[string]cty.Type{"foo": cty.String})), + }, + "deeply nested NestingList": { + &Object{ + Nesting: NestingList, + Attributes: map[string]*Attribute{ + "foo": { + NestedType: &Object{ + Nesting: NestingList, + Attributes: map[string]*Attribute{ + "bar": {Type: cty.String}, + }, + }, + }, + }, + }, + cty.List(cty.Object(map[string]cty.Type{"foo": cty.List(cty.Object(map[string]cty.Type{"bar": cty.String}))})), + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + got := test.Schema.ImpliedType() + if !got.Equals(test.Want) { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) + } + }) + } +} + +func TestObjectContainsSensitive(t *testing.T) { + tests := map[string]struct { + Schema *Object + Want bool + }{ + "object contains sensitive": { + &Object{ + Attributes: map[string]*Attribute{ + "sensitive": {Sensitive: true}, + }, + }, + true, + }, + "no sensitive attrs": { + &Object{ + Attributes: map[string]*Attribute{ + "insensitive": {}, + }, + }, + false, + }, + "nested object contains sensitive": { + &Object{ + Attributes: map[string]*Attribute{ + "nested": { + NestedType: &Object{ + Nesting: NestingSingle, + Attributes: map[string]*Attribute{ + "sensitive": {Sensitive: true}, + }, + }, + }, + }, + }, + true, + }, + "nested obj, no sensitive attrs": { + &Object{ + Attributes: map[string]*Attribute{ + "nested": { + NestedType: &Object{ + Nesting: NestingSingle, + Attributes: map[string]*Attribute{ + "public": {}, + }, + }, + }, + }, + }, + false, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + got := test.Schema.ContainsSensitive() + if got != test.Want { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) + } + }) + } + +} diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/internal_validate.go b/vendor/github.com/hashicorp/terraform/configs/configschema/internal_validate.go index ebf1abba..2afa724e 100644 --- a/vendor/github.com/hashicorp/terraform/configs/configschema/internal_validate.go +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/internal_validate.go @@ -11,74 +11,61 @@ import ( var validName = regexp.MustCompile(`^[a-z0-9_]+$`) -// InternalValidate returns an error if the receiving block and its child -// schema definitions have any consistencies with the documented rules for -// valid schema. +// InternalValidate returns an error if the receiving block and its child schema +// definitions have any inconsistencies with the documented rules for valid +// schema. // -// This is intended to be used within unit tests to detect when a given -// schema is invalid. +// This can be used within unit tests to detect when a given schema is invalid, +// and is run when terraform loads provider schemas during NewContext. func (b *Block) InternalValidate() error { if b == nil { return fmt.Errorf("top-level block schema is nil") } - return b.internalValidate("", nil) - + return b.internalValidate("") } -func (b *Block) internalValidate(prefix string, err error) error { +func (b *Block) internalValidate(prefix string) error { + var multiErr *multierror.Error + for name, attrS := range b.Attributes { if attrS == nil { - err = multierror.Append(err, fmt.Errorf("%s%s: attribute schema is nil", prefix, name)) + multiErr = multierror.Append(multiErr, fmt.Errorf("%s%s: attribute schema is nil", prefix, name)) continue } - if !validName.MatchString(name) { - err = multierror.Append(err, fmt.Errorf("%s%s: name may contain only lowercase letters, digits and underscores", prefix, name)) - } - if attrS.Optional == false && attrS.Required == false && attrS.Computed == false { - err = multierror.Append(err, fmt.Errorf("%s%s: must set Optional, Required or Computed", prefix, name)) - } - if attrS.Optional && attrS.Required { - err = multierror.Append(err, fmt.Errorf("%s%s: cannot set both Optional and Required", prefix, name)) - } - if attrS.Computed && attrS.Required { - err = multierror.Append(err, fmt.Errorf("%s%s: cannot set both Computed and Required", prefix, name)) - } - if attrS.Type == cty.NilType { - err = multierror.Append(err, fmt.Errorf("%s%s: Type must be set to something other than cty.NilType", prefix, name)) - } + multiErr = multierror.Append(multiErr, attrS.internalValidate(name, prefix)) } for name, blockS := range b.BlockTypes { if blockS == nil { - err = multierror.Append(err, fmt.Errorf("%s%s: block schema is nil", prefix, name)) + multiErr = multierror.Append(multiErr, fmt.Errorf("%s%s: block schema is nil", prefix, name)) continue } if _, isAttr := b.Attributes[name]; isAttr { - err = multierror.Append(err, fmt.Errorf("%s%s: name defined as both attribute and child block type", prefix, name)) + multiErr = multierror.Append(multiErr, fmt.Errorf("%s%s: name defined as both attribute and child block type", prefix, name)) } else if !validName.MatchString(name) { - err = multierror.Append(err, fmt.Errorf("%s%s: name may contain only lowercase letters, digits and underscores", prefix, name)) + multiErr = multierror.Append(multiErr, fmt.Errorf("%s%s: name may contain only lowercase letters, digits and underscores", prefix, name)) } if blockS.MinItems < 0 || blockS.MaxItems < 0 { - err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must both be greater than zero", prefix, name)) + multiErr = multierror.Append(multiErr, fmt.Errorf("%s%s: MinItems and MaxItems must both be greater than zero", prefix, name)) } switch blockS.Nesting { case NestingSingle: switch { case blockS.MinItems != blockS.MaxItems: - err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must match in NestingSingle mode", prefix, name)) + multiErr = multierror.Append(multiErr, fmt.Errorf("%s%s: MinItems and MaxItems must match in NestingSingle mode", prefix, name)) case blockS.MinItems < 0 || blockS.MinItems > 1: - err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must be set to either 0 or 1 in NestingSingle mode", prefix, name)) + multiErr = multierror.Append(multiErr, fmt.Errorf("%s%s: MinItems and MaxItems must be set to either 0 or 1 in NestingSingle mode", prefix, name)) } case NestingGroup: if blockS.MinItems != 0 || blockS.MaxItems != 0 { - err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems cannot be used in NestingGroup mode", prefix, name)) + multiErr = multierror.Append(multiErr, fmt.Errorf("%s%s: MinItems and MaxItems cannot be used in NestingGroup mode", prefix, name)) } case NestingList, NestingSet: if blockS.MinItems > blockS.MaxItems && blockS.MaxItems != 0 { - err = multierror.Append(err, fmt.Errorf("%s%s: MinItems must be less than or equal to MaxItems in %s mode", prefix, name, blockS.Nesting)) + multiErr = multierror.Append(multiErr, fmt.Errorf("%s%s: MinItems must be less than or equal to MaxItems in %s mode", prefix, name, blockS.Nesting)) } if blockS.Nesting == NestingSet { ety := blockS.Block.ImpliedType() @@ -86,20 +73,99 @@ func (b *Block) internalValidate(prefix string, err error) error { // This is not permitted because the HCL (cty) set implementation // needs to know the exact type of set elements in order to // properly hash them, and so can't support mixed types. - err = multierror.Append(err, fmt.Errorf("%s%s: NestingSet blocks may not contain attributes of cty.DynamicPseudoType", prefix, name)) + multiErr = multierror.Append(multiErr, fmt.Errorf("%s%s: NestingSet blocks may not contain attributes of cty.DynamicPseudoType", prefix, name)) } } case NestingMap: if blockS.MinItems != 0 || blockS.MaxItems != 0 { - err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must both be 0 in NestingMap mode", prefix, name)) + multiErr = multierror.Append(multiErr, fmt.Errorf("%s%s: MinItems and MaxItems must both be 0 in NestingMap mode", prefix, name)) } default: - err = multierror.Append(err, fmt.Errorf("%s%s: invalid nesting mode %s", prefix, name, blockS.Nesting)) + multiErr = multierror.Append(multiErr, fmt.Errorf("%s%s: invalid nesting mode %s", prefix, name, blockS.Nesting)) } subPrefix := prefix + name + "." - err = blockS.Block.internalValidate(subPrefix, err) + multiErr = multierror.Append(multiErr, blockS.Block.internalValidate(subPrefix)) + } + + return multiErr.ErrorOrNil() +} + +// InternalValidate returns an error if the receiving attribute and its child +// schema definitions have any inconsistencies with the documented rules for +// valid schema. +func (a *Attribute) InternalValidate(name string) error { + if a == nil { + return fmt.Errorf("attribute schema is nil") + } + return a.internalValidate(name, "") +} + +func (a *Attribute) internalValidate(name, prefix string) error { + var err *multierror.Error + + /* FIXME: this validation breaks certain existing providers and cannot be enforced without coordination. + if !validName.MatchString(name) { + err = multierror.Append(err, fmt.Errorf("%s%s: name may contain only lowercase letters, digits and underscores", prefix, name)) + } + */ + if !a.Optional && !a.Required && !a.Computed { + err = multierror.Append(err, fmt.Errorf("%s%s: must set Optional, Required or Computed", prefix, name)) + } + if a.Optional && a.Required { + err = multierror.Append(err, fmt.Errorf("%s%s: cannot set both Optional and Required", prefix, name)) + } + if a.Computed && a.Required { + err = multierror.Append(err, fmt.Errorf("%s%s: cannot set both Computed and Required", prefix, name)) + } + + if a.Type == cty.NilType && a.NestedType == nil { + err = multierror.Append(err, fmt.Errorf("%s%s: either Type or NestedType must be defined", prefix, name)) + } + + if a.Type != cty.NilType { + if a.NestedType != nil { + err = multierror.Append(fmt.Errorf("%s: Type and NestedType cannot both be set", name)) + } + } + + if a.NestedType != nil { + switch a.NestedType.Nesting { + case NestingSingle: + switch { + case a.NestedType.MinItems != a.NestedType.MaxItems: + err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must match in NestingSingle mode", prefix, name)) + case a.NestedType.MinItems < 0 || a.NestedType.MinItems > 1: + err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must be set to either 0 or 1 in NestingSingle mode", prefix, name)) + } + case NestingList, NestingSet: + if a.NestedType.MinItems > a.NestedType.MaxItems && a.NestedType.MaxItems != 0 { + err = multierror.Append(err, fmt.Errorf("%s%s: MinItems must be less than or equal to MaxItems in %s mode", prefix, name, a.NestedType.Nesting)) + } + if a.NestedType.Nesting == NestingSet { + ety := a.NestedType.ImpliedType() + if ety.HasDynamicTypes() { + // This is not permitted because the HCL (cty) set implementation + // needs to know the exact type of set elements in order to + // properly hash them, and so can't support mixed types. + err = multierror.Append(err, fmt.Errorf("%s%s: NestingSet blocks may not contain attributes of cty.DynamicPseudoType", prefix, name)) + } + } + case NestingMap: + if a.NestedType.MinItems != 0 || a.NestedType.MaxItems != 0 { + err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must both be 0 in NestingMap mode", prefix, name)) + } + default: + err = multierror.Append(err, fmt.Errorf("%s%s: invalid nesting mode %s", prefix, name, a.NestedType.Nesting)) + } + for name, attrS := range a.NestedType.Attributes { + if attrS == nil { + err = multierror.Append(err, fmt.Errorf("%s%s: attribute schema is nil", prefix, name)) + continue + } + err = multierror.Append(err, attrS.internalValidate(name, prefix)) + } } - return err + return err.ErrorOrNil() } diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/internal_validate_test.go b/vendor/github.com/hashicorp/terraform/configs/configschema/internal_validate_test.go index 512c7adb..dc70a5fa 100644 --- a/vendor/github.com/hashicorp/terraform/configs/configschema/internal_validate_test.go +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/internal_validate_test.go @@ -10,172 +10,208 @@ import ( func TestBlockInternalValidate(t *testing.T) { tests := map[string]struct { - Block *Block - ErrCount int + Block *Block + Errs []string }{ "empty": { &Block{}, - 0, + []string{}, }, "valid": { &Block{ Attributes: map[string]*Attribute{ - "foo": &Attribute{ + "foo": { Type: cty.String, Required: true, }, - "bar": &Attribute{ + "bar": { Type: cty.String, Optional: true, }, - "baz": &Attribute{ + "baz": { Type: cty.String, Computed: true, }, - "baz_maybe": &Attribute{ + "baz_maybe": { Type: cty.String, Optional: true, Computed: true, }, }, BlockTypes: map[string]*NestedBlock{ - "single": &NestedBlock{ + "single": { Nesting: NestingSingle, Block: Block{}, }, - "single_required": &NestedBlock{ + "single_required": { Nesting: NestingSingle, Block: Block{}, MinItems: 1, MaxItems: 1, }, - "list": &NestedBlock{ + "list": { Nesting: NestingList, Block: Block{}, }, - "list_required": &NestedBlock{ + "list_required": { Nesting: NestingList, Block: Block{}, MinItems: 1, }, - "set": &NestedBlock{ + "set": { Nesting: NestingSet, Block: Block{}, }, - "set_required": &NestedBlock{ + "set_required": { Nesting: NestingSet, Block: Block{}, MinItems: 1, }, - "map": &NestedBlock{ + "map": { Nesting: NestingMap, Block: Block{}, }, }, }, - 0, + []string{}, }, "attribute with no flags set": { &Block{ Attributes: map[string]*Attribute{ - "foo": &Attribute{ + "foo": { Type: cty.String, }, }, }, - 1, // must set one of the flags + []string{"foo: must set Optional, Required or Computed"}, }, "attribute required and optional": { &Block{ Attributes: map[string]*Attribute{ - "foo": &Attribute{ + "foo": { Type: cty.String, Required: true, Optional: true, }, }, }, - 1, // both required and optional + []string{"foo: cannot set both Optional and Required"}, }, "attribute required and computed": { &Block{ Attributes: map[string]*Attribute{ - "foo": &Attribute{ + "foo": { Type: cty.String, Required: true, Computed: true, }, }, }, - 1, // both required and computed + []string{"foo: cannot set both Computed and Required"}, }, "attribute optional and computed": { &Block{ Attributes: map[string]*Attribute{ - "foo": &Attribute{ + "foo": { Type: cty.String, Optional: true, Computed: true, }, }, }, - 0, + []string{}, }, "attribute with missing type": { &Block{ Attributes: map[string]*Attribute{ - "foo": &Attribute{ + "foo": { Optional: true, }, }, }, - 1, // Type must be set + []string{"foo: either Type or NestedType must be defined"}, }, - "attribute with invalid name": { + /* FIXME: This caused errors when applied to existing providers (oci) + and cannot be enforced without coordination. + + "attribute with invalid name": {&Block{Attributes: + map[string]*Attribute{"fooBar": {Type: cty.String, Optional: + true, + }, + }, + }, + []string{"fooBar: name may contain only lowercase letters, digits and underscores"}, + }, + */ + "attribute with invalid NestedType nesting": { &Block{ Attributes: map[string]*Attribute{ - "fooBar": &Attribute{ - Type: cty.String, + "foo": { + NestedType: &Object{ + Nesting: NestingSingle, + MinItems: 10, + MaxItems: 10, + }, Optional: true, }, }, }, - 1, // name may not contain uppercase letters + []string{"foo: MinItems and MaxItems must be set to either 0 or 1 in NestingSingle mode"}, + }, + "attribute with invalid NestedType attribute": { + &Block{ + Attributes: map[string]*Attribute{ + "foo": { + NestedType: &Object{ + Nesting: NestingSingle, + Attributes: map[string]*Attribute{ + "foo": { + Type: cty.String, + Required: true, + Optional: true, + }, + }, + }, + Optional: true, + }, + }, + }, + []string{"foo: cannot set both Optional and Required"}, }, "block type with invalid name": { &Block{ BlockTypes: map[string]*NestedBlock{ - "fooBar": &NestedBlock{ + "fooBar": { Nesting: NestingSingle, }, }, }, - 1, // name may not contain uppercase letters + []string{"fooBar: name may contain only lowercase letters, digits and underscores"}, }, "colliding names": { &Block{ Attributes: map[string]*Attribute{ - "foo": &Attribute{ + "foo": { Type: cty.String, Optional: true, }, }, BlockTypes: map[string]*NestedBlock{ - "foo": &NestedBlock{ + "foo": { Nesting: NestingSingle, }, }, }, - 1, // "foo" is defined as both attribute and block type + []string{"foo: name defined as both attribute and child block type"}, }, "nested block with badness": { &Block{ BlockTypes: map[string]*NestedBlock{ - "bad": &NestedBlock{ + "bad": { Nesting: NestingSingle, Block: Block{ Attributes: map[string]*Attribute{ - "nested_bad": &Attribute{ + "nested_bad": { Type: cty.String, Required: true, Optional: true, @@ -185,16 +221,16 @@ func TestBlockInternalValidate(t *testing.T) { }, }, }, - 1, // nested_bad is both required and optional + []string{"bad.nested_bad: cannot set both Optional and Required"}, }, "nested list block with dynamically-typed attribute": { &Block{ BlockTypes: map[string]*NestedBlock{ - "bad": &NestedBlock{ + "bad": { Nesting: NestingList, Block: Block{ Attributes: map[string]*Attribute{ - "nested_bad": &Attribute{ + "nested_bad": { Type: cty.DynamicPseudoType, Optional: true, }, @@ -203,16 +239,16 @@ func TestBlockInternalValidate(t *testing.T) { }, }, }, - 0, + []string{}, }, "nested set block with dynamically-typed attribute": { &Block{ BlockTypes: map[string]*NestedBlock{ - "bad": &NestedBlock{ + "bad": { Nesting: NestingSet, Block: Block{ Attributes: map[string]*Attribute{ - "nested_bad": &Attribute{ + "nested_bad": { Type: cty.DynamicPseudoType, Optional: true, }, @@ -221,11 +257,11 @@ func TestBlockInternalValidate(t *testing.T) { }, }, }, - 1, // NestingSet blocks may not contain attributes of cty.DynamicPseudoType + []string{"bad: NestingSet blocks may not contain attributes of cty.DynamicPseudoType"}, }, "nil": { nil, - 1, // block is nil + []string{"top-level block schema is nil"}, }, "nil attr": { &Block{ @@ -233,7 +269,7 @@ func TestBlockInternalValidate(t *testing.T) { "bad": nil, }, }, - 1, // attribute schema is nil + []string{"bad: attribute schema is nil"}, }, "nil block type": { &Block{ @@ -241,18 +277,26 @@ func TestBlockInternalValidate(t *testing.T) { "bad": nil, }, }, - 1, // block schema is nil + []string{"bad: block schema is nil"}, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { errs := multierrorErrors(test.Block.InternalValidate()) - if got, want := len(errs), test.ErrCount; got != want { + if got, want := len(errs), len(test.Errs); got != want { t.Errorf("wrong number of errors %d; want %d", got, want) for _, err := range errs { t.Logf("- %s", err.Error()) } + } else { + if len(errs) > 0 { + for i := range errs { + if errs[i].Error() != test.Errs[i] { + t.Errorf("wrong error: got %s, want %s", errs[i].Error(), test.Errs[i]) + } + } + } } }) } diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/marks.go b/vendor/github.com/hashicorp/terraform/configs/configschema/marks.go new file mode 100644 index 00000000..bf8f8156 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/marks.go @@ -0,0 +1,60 @@ +package configschema + +import ( + "fmt" + + "github.com/zclconf/go-cty/cty" +) + +// ValueMarks returns a set of path value marks for a given value and path, +// based on the sensitive flag for each attribute within the schema. Nested +// blocks are descended (if present in the given value). +func (b *Block) ValueMarks(val cty.Value, path cty.Path) []cty.PathValueMarks { + var pvm []cty.PathValueMarks + for name, attrS := range b.Attributes { + if attrS.Sensitive { + // Create a copy of the path, with this step added, to add to our PathValueMarks slice + attrPath := make(cty.Path, len(path), len(path)+1) + copy(attrPath, path) + attrPath = append(path, cty.GetAttrStep{Name: name}) + pvm = append(pvm, cty.PathValueMarks{ + Path: attrPath, + Marks: cty.NewValueMarks("sensitive"), + }) + } + } + + if val.IsNull() { + return pvm + } + for name, blockS := range b.BlockTypes { + // If our block doesn't contain any sensitive attributes, skip inspecting it + if !blockS.Block.ContainsSensitive() { + continue + } + + blockV := val.GetAttr(name) + if blockV.IsNull() || !blockV.IsKnown() { + continue + } + + // Create a copy of the path, with this step added, to add to our PathValueMarks slice + blockPath := make(cty.Path, len(path), len(path)+1) + copy(blockPath, path) + blockPath = append(path, cty.GetAttrStep{Name: name}) + + switch blockS.Nesting { + case NestingSingle, NestingGroup: + pvm = append(pvm, blockS.Block.ValueMarks(blockV, blockPath)...) + case NestingList, NestingMap, NestingSet: + for it := blockV.ElementIterator(); it.Next(); { + idx, blockEV := it.Element() + morePaths := blockS.Block.ValueMarks(blockEV, append(blockPath, cty.IndexStep{Key: idx})) + pvm = append(pvm, morePaths...) + } + default: + panic(fmt.Sprintf("unsupported nesting mode %s", blockS.Nesting)) + } + } + return pvm +} diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/marks_test.go b/vendor/github.com/hashicorp/terraform/configs/configschema/marks_test.go new file mode 100644 index 00000000..b5c672c3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/marks_test.go @@ -0,0 +1,104 @@ +package configschema + +import ( + "fmt" + "testing" + + "github.com/zclconf/go-cty/cty" +) + +func TestBlockValueMarks(t *testing.T) { + schema := &Block{ + Attributes: map[string]*Attribute{ + "unsensitive": { + Type: cty.String, + Optional: true, + }, + "sensitive": { + Type: cty.String, + Sensitive: true, + }, + }, + + BlockTypes: map[string]*NestedBlock{ + "list": { + Nesting: NestingList, + Block: Block{ + Attributes: map[string]*Attribute{ + "unsensitive": { + Type: cty.String, + Optional: true, + }, + "sensitive": { + Type: cty.String, + Sensitive: true, + }, + }, + }, + }, + }, + } + + for _, tc := range []struct { + given cty.Value + expect cty.Value + }{ + { + cty.UnknownVal(schema.ImpliedType()), + cty.UnknownVal(schema.ImpliedType()), + }, + { + cty.NullVal(schema.ImpliedType()), + cty.NullVal(schema.ImpliedType()), + }, + { + cty.ObjectVal(map[string]cty.Value{ + "sensitive": cty.UnknownVal(cty.String), + "unsensitive": cty.UnknownVal(cty.String), + "list": cty.UnknownVal(schema.BlockTypes["list"].ImpliedType()), + }), + cty.ObjectVal(map[string]cty.Value{ + "sensitive": cty.UnknownVal(cty.String).Mark("sensitive"), + "unsensitive": cty.UnknownVal(cty.String), + "list": cty.UnknownVal(schema.BlockTypes["list"].ImpliedType()), + }), + }, + { + cty.ObjectVal(map[string]cty.Value{ + "sensitive": cty.NullVal(cty.String), + "unsensitive": cty.UnknownVal(cty.String), + "list": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "sensitive": cty.UnknownVal(cty.String), + "unsensitive": cty.UnknownVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "sensitive": cty.NullVal(cty.String), + "unsensitive": cty.NullVal(cty.String), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "sensitive": cty.NullVal(cty.String).Mark("sensitive"), + "unsensitive": cty.UnknownVal(cty.String), + "list": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "sensitive": cty.UnknownVal(cty.String).Mark("sensitive"), + "unsensitive": cty.UnknownVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "sensitive": cty.NullVal(cty.String).Mark("sensitive"), + "unsensitive": cty.NullVal(cty.String), + }), + }), + }), + }, + } { + t.Run(fmt.Sprintf("%#v", tc.given), func(t *testing.T) { + got := tc.given.MarkWithPaths(schema.ValueMarks(tc.given, nil)) + if !got.RawEquals(tc.expect) { + t.Fatalf("\nexpected: %#v\ngot: %#v\n", tc.expect, got) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/path.go b/vendor/github.com/hashicorp/terraform/configs/configschema/path.go new file mode 100644 index 00000000..4c48c1a0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/path.go @@ -0,0 +1,29 @@ +package configschema + +import ( + "github.com/zclconf/go-cty/cty" +) + +// AttributeByPath looks up the Attribute schema which corresponds to the given +// cty.Path. A nil value is returned if the given path does not correspond to a +// specific attribute. +// TODO: this will need to be updated for nested attributes +func (b *Block) AttributeByPath(path cty.Path) *Attribute { + block := b + for _, step := range path { + switch step := step.(type) { + case cty.GetAttrStep: + if attr := block.Attributes[step.Name]; attr != nil { + return attr + } + + if nestedBlock := block.BlockTypes[step.Name]; nestedBlock != nil { + block = &nestedBlock.Block + continue + } + + return nil + } + } + return nil +} diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/path_test.go b/vendor/github.com/hashicorp/terraform/configs/configschema/path_test.go new file mode 100644 index 00000000..c4f673ba --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/path_test.go @@ -0,0 +1,121 @@ +package configschema + +import ( + "testing" + + "github.com/zclconf/go-cty/cty" +) + +func TestAttributeByPath(t *testing.T) { + schema := &Block{ + Attributes: map[string]*Attribute{ + "a1": {Description: "a1"}, + "a2": {Description: "a2"}, + }, + BlockTypes: map[string]*NestedBlock{ + "b1": { + Nesting: NestingList, + Block: Block{ + Attributes: map[string]*Attribute{ + "a3": {Description: "a3"}, + "a4": {Description: "a4"}, + }, + BlockTypes: map[string]*NestedBlock{ + "b2": { + Nesting: NestingMap, + Block: Block{ + Attributes: map[string]*Attribute{ + "a5": {Description: "a5"}, + "a6": {Description: "a6"}, + }, + }, + }, + }, + }, + }, + "b3": { + Nesting: NestingMap, + Block: Block{ + Attributes: map[string]*Attribute{ + "a7": {Description: "a7"}, + "a8": {Description: "a8"}, + }, + BlockTypes: map[string]*NestedBlock{ + "b4": { + Nesting: NestingSet, + Block: Block{ + Attributes: map[string]*Attribute{ + "a9": {Description: "a9"}, + "a10": {Description: "a10"}, + }, + }, + }, + }, + }, + }, + }, + } + + for _, tc := range []struct { + path cty.Path + attrDescription string + exists bool + }{ + { + cty.GetAttrPath("a2"), + "a2", + true, + }, + { + cty.GetAttrPath("b1"), + "block", + false, + }, + { + cty.GetAttrPath("b1").IndexInt(1).GetAttr("a3"), + "a3", + true, + }, + { + cty.GetAttrPath("b1").IndexInt(1).GetAttr("b2").IndexString("foo").GetAttr("a7"), + "missing", + false, + }, + { + cty.GetAttrPath("b1").IndexInt(1).GetAttr("b2").IndexString("foo").GetAttr("a6"), + "a6", + true, + }, + { + cty.GetAttrPath("b3").IndexString("foo").GetAttr("b2").IndexString("foo").GetAttr("a7"), + "missing_block", + false, + }, + { + cty.GetAttrPath("b3").IndexString("foo").GetAttr("a7"), + "a7", + true, + }, + { + // Index steps don't apply to the schema, so the set Index value doesn't matter. + cty.GetAttrPath("b3").IndexString("foo").GetAttr("b4").Index(cty.EmptyObjectVal).GetAttr("a9"), + "a9", + true, + }, + } { + t.Run(tc.attrDescription, func(t *testing.T) { + attr := schema.AttributeByPath(tc.path) + if !tc.exists && attr == nil { + return + } + + if attr == nil { + t.Fatalf("missing attribute from path %#v\n", tc.path) + } + + if attr.Description != tc.attrDescription { + t.Fatalf("expected Attribute for %q, got %#v\n", tc.attrDescription, attr) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/schema.go b/vendor/github.com/hashicorp/terraform/configs/configschema/schema.go index 4d3e7cab..581bead8 100644 --- a/vendor/github.com/hashicorp/terraform/configs/configschema/schema.go +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/schema.go @@ -38,8 +38,13 @@ type Block struct { // Attribute represents a configuration attribute, within a block. type Attribute struct { // Type is a type specification that the attribute's value must conform to. + // It conflicts with NestedType. Type cty.Type + // NestedType indicates that the attribute is a NestedBlock-style object. + // This field conflicts with Type. + NestedType *Object + // Description is an English-language description of the purpose and // usage of the attribute. A description should be concise and use only // one or two sentences, leaving full definition to longer-form @@ -72,6 +77,25 @@ type Attribute struct { Deprecated bool } +// Object represents the embedding of a structural object inside an Attribute. +type Object struct { + // Attributes describes the nested attributes which may appear inside the + // Object. + Attributes map[string]*Attribute + + // Nesting provides the nesting mode for this Object, which determines how + // many instances of the Object are allowed, how many labels it expects, and + // how the resulting data will be converted into a data structure. + Nesting NestingMode + + // MinItems and MaxItems set, for the NestingList and NestingSet nesting + // modes, lower and upper limits on the number of child blocks allowed + // of the given type. If both are left at zero, no limit is applied. + // These fields are ignored for other nesting modes and must both be left + // at zero. + MinItems, MaxItems int +} + // NestedBlock represents the embedding of one block within another. type NestedBlock struct { // Block is the description of the block that's nested. @@ -98,6 +122,8 @@ type NestedBlock struct { // blocks. type NestingMode int +// Object represents the embedding of a NestedBl + //go:generate go run golang.org/x/tools/cmd/stringer -type=NestingMode const ( diff --git a/vendor/github.com/hashicorp/terraform/configs/experiments.go b/vendor/github.com/hashicorp/terraform/configs/experiments.go index 82ff3bd9..83557a85 100644 --- a/vendor/github.com/hashicorp/terraform/configs/experiments.go +++ b/vendor/github.com/hashicorp/terraform/configs/experiments.go @@ -5,6 +5,7 @@ import ( "github.com/hashicorp/hcl/v2" "github.com/hashicorp/terraform/experiments" + "github.com/hashicorp/terraform/version" "github.com/zclconf/go-cty/cty" ) @@ -25,6 +26,49 @@ func sniffActiveExperiments(body hcl.Body) (experiments.Set, hcl.Diagnostics) { content, _, blockDiags := block.Body.PartialContent(configFileExperimentsSniffBlockSchema) diags = append(diags, blockDiags...) + if attr, exists := content.Attributes["language"]; exists { + // We don't yet have a sense of selecting an edition of the + // language, but we're reserving this syntax for now so that + // if and when we do this later older versions of Terraform + // will emit a more helpful error message than just saying + // this attribute doesn't exist. Handling this as part of + // experiments is a bit odd for now but justified by the + // fact that a future fuller implementation of switchable + // languages would be likely use a similar implementation + // strategy as experiments, and thus would lead to this + // function being refactored to deal with both concerns at + // once. We'll see, though! + kw := hcl.ExprAsKeyword(attr.Expr) + currentVersion := version.SemVer.String() + const firstEdition = "TF2021" + switch { + case kw == "": // (the expression wasn't a keyword at all) + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid language edition", + Detail: fmt.Sprintf( + "The language argument expects a bare language edition keyword. Terraform %s supports only language edition %s, which is the default.", + currentVersion, firstEdition, + ), + Subject: attr.Expr.Range().Ptr(), + }) + case kw != firstEdition: + rel := "different" + if kw > firstEdition { // would be weird for this not to be true, but it's user input so anything goes + rel = "newer" + } + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Unsupported language edition", + Detail: fmt.Sprintf( + "Terraform v%s only supports language edition %s. This module requires a %s version of Terraform CLI.", + currentVersion, firstEdition, rel, + ), + Subject: attr.Expr.Range().Ptr(), + }) + } + } + attr, exists := content.Attributes["experiments"] if !exists { continue diff --git a/vendor/github.com/hashicorp/terraform/configs/module.go b/vendor/github.com/hashicorp/terraform/configs/module.go index 9ca4c2b7..126eebbd 100644 --- a/vendor/github.com/hashicorp/terraform/configs/module.go +++ b/vendor/github.com/hashicorp/terraform/configs/module.go @@ -167,11 +167,9 @@ func (m *Module) ResourceByAddr(addr addrs.Resource) *Resource { func (m *Module) appendFile(file *File) hcl.Diagnostics { var diags hcl.Diagnostics - for _, constraint := range file.CoreVersionConstraints { - // If there are any conflicting requirements then we'll catch them - // when we actually check these constraints. - m.CoreVersionConstraints = append(m.CoreVersionConstraints, constraint) - } + // If there are any conflicting requirements then we'll catch them + // when we actually check these constraints. + m.CoreVersionConstraints = append(m.CoreVersionConstraints, file.CoreVersionConstraints...) m.ActiveExperiments = experiments.SetUnion(m.ActiveExperiments, file.ActiveExperiments) @@ -341,9 +339,7 @@ func (m *Module) mergeFile(file *File) hcl.Diagnostics { // would union together across multiple files anyway, but we'll // allow it and have each override file clobber any existing list. m.CoreVersionConstraints = nil - for _, constraint := range file.CoreVersionConstraints { - m.CoreVersionConstraints = append(m.CoreVersionConstraints, constraint) - } + m.CoreVersionConstraints = append(m.CoreVersionConstraints, file.CoreVersionConstraints...) } if len(file.Backends) != 0 { diff --git a/vendor/github.com/hashicorp/terraform/configs/module_call.go b/vendor/github.com/hashicorp/terraform/configs/module_call.go index eefb3f2c..6d1678ce 100644 --- a/vendor/github.com/hashicorp/terraform/configs/module_call.go +++ b/vendor/github.com/hashicorp/terraform/configs/module_call.go @@ -33,11 +33,6 @@ type ModuleCall struct { func decodeModuleBlock(block *hcl.Block, override bool) (*ModuleCall, hcl.Diagnostics) { var diags hcl.Diagnostics - // Produce deprecation messages for any pre-0.12-style - // single-interpolation-only expressions. - moreDiags := warnForDeprecatedInterpolationsInBody(block.Body) - diags = append(diags, moreDiags...) - mc := &ModuleCall{ Name: block.Labels[0], DeclRange: block.DefRange, diff --git a/vendor/github.com/hashicorp/terraform/configs/module_merge_body.go b/vendor/github.com/hashicorp/terraform/configs/module_merge_body.go index 7b51eae8..6ae64a2a 100644 --- a/vendor/github.com/hashicorp/terraform/configs/module_merge_body.go +++ b/vendor/github.com/hashicorp/terraform/configs/module_merge_body.go @@ -112,9 +112,7 @@ func (b mergeBody) prepareContent(base *hcl.BodyContent, override *hcl.BodyConte } content.Blocks = append(content.Blocks, block) } - for _, block := range override.Blocks { - content.Blocks = append(content.Blocks, block) - } + content.Blocks = append(content.Blocks, override.Blocks...) return content } diff --git a/vendor/github.com/hashicorp/terraform/configs/named_values.go b/vendor/github.com/hashicorp/terraform/configs/named_values.go index 375d1e28..cfa7bfc8 100644 --- a/vendor/github.com/hashicorp/terraform/configs/named_values.go +++ b/vendor/github.com/hashicorp/terraform/configs/named_values.go @@ -149,12 +149,12 @@ func decodeVariableBlock(block *hcl.Block, override bool) (*Variable, hcl.Diagno func decodeVariableType(expr hcl.Expression) (cty.Type, VariableParsingMode, hcl.Diagnostics) { if exprIsNativeQuotedString(expr) { - // Here we're accepting the pre-0.12 form of variable type argument where - // the string values "string", "list" and "map" are accepted has a hint - // about the type used primarily for deciding how to parse values - // given on the command line and in environment variables. + // If a user provides the pre-0.12 form of variable type argument where + // the string values "string", "list" and "map" are accepted, we + // provide an error to point the user towards using the type system + // correctly has a hint. // Only the native syntax ends up in this codepath; we handle the - // JSON syntax (which is, of course, quoted even in the new format) + // JSON syntax (which is, of course, quoted within the type system) // in the normal codepath below. val, diags := expr.Value(nil) if diags.HasErrors() { @@ -164,33 +164,33 @@ func decodeVariableType(expr hcl.Expression) (cty.Type, VariableParsingMode, hcl switch str { case "string": diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagWarning, - Summary: "Quoted type constraints are deprecated", - Detail: "Terraform 0.11 and earlier required type constraints to be given in quotes, but that form is now deprecated and will be removed in a future version of Terraform. To silence this warning, remove the quotes around \"string\".", + Severity: hcl.DiagError, + Summary: "Invalid quoted type constraints", + Detail: "Terraform 0.11 and earlier required type constraints to be given in quotes, but that form is now deprecated and will be removed in a future version of Terraform. Remove the quotes around \"string\".", Subject: expr.Range().Ptr(), }) - return cty.String, VariableParseLiteral, diags + return cty.DynamicPseudoType, VariableParseLiteral, diags case "list": diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagWarning, - Summary: "Quoted type constraints are deprecated", - Detail: "Terraform 0.11 and earlier required type constraints to be given in quotes, but that form is now deprecated and will be removed in a future version of Terraform. To silence this warning, remove the quotes around \"list\" and write list(string) instead to explicitly indicate that the list elements are strings.", + Severity: hcl.DiagError, + Summary: "Invalid quoted type constraints", + Detail: "Terraform 0.11 and earlier required type constraints to be given in quotes, but that form is now deprecated and will be removed in a future version of Terraform. Remove the quotes around \"list\" and write list(string) instead to explicitly indicate that the list elements are strings.", Subject: expr.Range().Ptr(), }) - return cty.List(cty.DynamicPseudoType), VariableParseHCL, diags + return cty.DynamicPseudoType, VariableParseHCL, diags case "map": diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagWarning, - Summary: "Quoted type constraints are deprecated", - Detail: "Terraform 0.11 and earlier required type constraints to be given in quotes, but that form is now deprecated and will be removed in a future version of Terraform. To silence this warning, remove the quotes around \"map\" and write map(string) instead to explicitly indicate that the map elements are strings.", + Severity: hcl.DiagError, + Summary: "Invalid quoted type constraints", + Detail: "Terraform 0.11 and earlier required type constraints to be given in quotes, but that form is now deprecated and will be removed in a future version of Terraform. Remove the quotes around \"map\" and write map(string) instead to explicitly indicate that the map elements are strings.", Subject: expr.Range().Ptr(), }) - return cty.Map(cty.DynamicPseudoType), VariableParseHCL, diags + return cty.DynamicPseudoType, VariableParseHCL, diags default: return cty.DynamicPseudoType, VariableParseHCL, hcl.Diagnostics{{ Severity: hcl.DiagError, Summary: "Invalid legacy variable type hint", - Detail: `The legacy variable type hint form, using a quoted string, allows only the values "string", "list", and "map". To provide a full type expression, remove the surrounding quotes and give the type expression directly.`, + Detail: `To provide a full type expression, remove the surrounding quotes and give the type expression directly.`, Subject: expr.Range().Ptr(), }} } @@ -382,7 +382,7 @@ func decodeVariableValidationBlock(varName string, block *hcl.Block, override bo diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: errSummary, - Detail: "Validation error message must be at least one full English sentence starting with an uppercase letter and ending with a period or question mark.", + Detail: "The validation error message must be at least one full sentence starting with an uppercase letter and ending with a period or question mark.\n\nYour given message will be included as part of a larger Terraform error message, written as English prose. For broadly-shared modules we suggest using a similar writing style so that the overall result will be consistent.", Subject: attr.Expr.Range().Ptr(), }) } @@ -453,11 +453,6 @@ func decodeOutputBlock(block *hcl.Block, override bool) (*Output, hcl.Diagnostic schema = schemaForOverrides(schema) } - // Produce deprecation messages for any pre-0.12-style - // single-interpolation-only expressions. - moreDiags := warnForDeprecatedInterpolationsInBody(block.Body) - diags = append(diags, moreDiags...) - content, moreDiags := block.Body.Content(schema) diags = append(diags, moreDiags...) @@ -522,11 +517,6 @@ func decodeLocalsBlock(block *hcl.Block) ([]*Local, hcl.Diagnostics) { }) } - // Produce deprecation messages for any pre-0.12-style - // single-interpolation-only expressions. - moreDiags := warnForDeprecatedInterpolationsInExpr(attr.Expr) - diags = append(diags, moreDiags...) - locals = append(locals, &Local{ Name: name, Expr: attr.Expr, diff --git a/vendor/github.com/hashicorp/terraform/configs/parser_config.go b/vendor/github.com/hashicorp/terraform/configs/parser_config.go index 354b96a7..ec66b4cc 100644 --- a/vendor/github.com/hashicorp/terraform/configs/parser_config.go +++ b/vendor/github.com/hashicorp/terraform/configs/parser_config.go @@ -58,8 +58,8 @@ func (p *Parser) loadConfigFile(path string, override bool) (*File, hcl.Diagnost content, contentDiags := block.Body.Content(terraformBlockSchema) diags = append(diags, contentDiags...) - // We ignore the "terraform_version" and "experiments" attributes - // here because sniffCoreVersionRequirements and + // We ignore the "terraform_version", "language" and "experiments" + // attributes here because sniffCoreVersionRequirements and // sniffActiveExperiments already dealt with those above. for _, innerBlock := range content.Blocks { @@ -244,6 +244,7 @@ var terraformBlockSchema = &hcl.BodySchema{ Attributes: []hcl.AttributeSchema{ {Name: "required_version"}, {Name: "experiments"}, + {Name: "language"}, }, Blocks: []hcl.BlockHeaderSchema{ { @@ -283,8 +284,7 @@ var configFileVersionSniffBlockSchema = &hcl.BodySchema{ // to decode a single attribute from inside a "terraform" block. var configFileExperimentsSniffBlockSchema = &hcl.BodySchema{ Attributes: []hcl.AttributeSchema{ - { - Name: "experiments", - }, + {Name: "experiments"}, + {Name: "language"}, }, } diff --git a/vendor/github.com/hashicorp/terraform/configs/parser_test.go b/vendor/github.com/hashicorp/terraform/configs/parser_test.go index a87ad68f..cb223928 100644 --- a/vendor/github.com/hashicorp/terraform/configs/parser_test.go +++ b/vendor/github.com/hashicorp/terraform/configs/parser_test.go @@ -38,16 +38,6 @@ func testParser(files map[string]string) *Parser { return NewParser(fs) } -// testModuleFromFile reads a single file, wraps it in a module, and returns -// it. This is a helper for use in unit tests. -func testModuleFromFile(filename string) (*Module, hcl.Diagnostics) { - parser := NewParser(nil) - f, diags := parser.LoadConfigFile(filename) - mod, modDiags := NewModule([]*File{f}, nil) - diags = append(diags, modDiags...) - return mod, modDiags -} - // testModuleConfigFrom File reads a single file from the given path as a // module and returns its configuration. This is a helper for use in unit tests. func testModuleConfigFromFile(filename string) (*Config, hcl.Diagnostics) { diff --git a/vendor/github.com/hashicorp/terraform/configs/provider.go b/vendor/github.com/hashicorp/terraform/configs/provider.go index 8317cc83..3bbba305 100644 --- a/vendor/github.com/hashicorp/terraform/configs/provider.go +++ b/vendor/github.com/hashicorp/terraform/configs/provider.go @@ -25,18 +25,18 @@ type Provider struct { Config hcl.Body DeclRange hcl.Range + + // TODO: this may not be set in some cases, so it is not yet suitable for + // use outside of this package. We currently only use it for internal + // validation, but once we verify that this can be set in all cases, we can + // export this so providers don't need to be re-resolved. + // This same field is also added to the ProviderConfigRef struct. + providerType addrs.Provider } func decodeProviderBlock(block *hcl.Block) (*Provider, hcl.Diagnostics) { var diags hcl.Diagnostics - // Produce deprecation messages for any pre-0.12-style - // single-interpolation-only expressions. We do this up front here because - // then we can also catch instances inside special blocks like "connection", - // before PartialContent extracts them. - moreDiags := warnForDeprecatedInterpolationsInBody(block.Body) - diags = append(diags, moreDiags...) - content, config, moreDiags := block.Body.PartialContent(providerBlockSchema) diags = append(diags, moreDiags...) diff --git a/vendor/github.com/hashicorp/terraform/configs/provider_meta.go b/vendor/github.com/hashicorp/terraform/configs/provider_meta.go index e49614f6..d1e817e7 100644 --- a/vendor/github.com/hashicorp/terraform/configs/provider_meta.go +++ b/vendor/github.com/hashicorp/terraform/configs/provider_meta.go @@ -13,8 +13,20 @@ type ProviderMeta struct { } func decodeProviderMetaBlock(block *hcl.Block) (*ProviderMeta, hcl.Diagnostics) { + // provider_meta must be a static map. We can verify this by attempting to + // evaluate the values. + attrs, diags := block.Body.JustAttributes() + if diags.HasErrors() { + return nil, diags + } + + for _, attr := range attrs { + _, d := attr.Expr.Value(nil) + diags = append(diags, d...) + } + // verify that the local name is already localized or produce an error. - diags := checkProviderNameNormalized(block.Labels[0], block.DefRange) + diags = append(diags, checkProviderNameNormalized(block.Labels[0], block.DefRange)...) return &ProviderMeta{ Provider: block.Labels[0], diff --git a/vendor/github.com/hashicorp/terraform/configs/provider_requirements.go b/vendor/github.com/hashicorp/terraform/configs/provider_requirements.go index ef746f3f..f870e1cc 100644 --- a/vendor/github.com/hashicorp/terraform/configs/provider_requirements.go +++ b/vendor/github.com/hashicorp/terraform/configs/provider_requirements.go @@ -1,6 +1,8 @@ package configs import ( + "fmt" + version "github.com/hashicorp/go-version" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/terraform/addrs" @@ -17,6 +19,7 @@ type RequiredProvider struct { Type addrs.Provider Requirement VersionConstraint DeclRange hcl.Range + Aliases []addrs.LocalProviderConfig } type RequiredProviders struct { @@ -26,118 +29,202 @@ type RequiredProviders struct { func decodeRequiredProvidersBlock(block *hcl.Block) (*RequiredProviders, hcl.Diagnostics) { attrs, diags := block.Body.JustAttributes() + if diags.HasErrors() { + return nil, diags + } + ret := &RequiredProviders{ RequiredProviders: make(map[string]*RequiredProvider), DeclRange: block.DefRange, } - for name, attr := range attrs { - expr, err := attr.Expr.Value(nil) - if err != nil { - diags = append(diags, err...) - } - - // verify that the local name is already localized or produce an error. - nameDiags := checkProviderNameNormalized(name, attr.Expr.Range()) - diags = append(diags, nameDiags...) + for name, attr := range attrs { rp := &RequiredProvider{ Name: name, DeclRange: attr.Expr.Range(), } - switch { - case expr.Type().IsPrimitiveType(): + // Look for a single static string, in case we have the legacy version-only + // format in the configuration. + if expr, err := attr.Expr.Value(nil); err == nil && expr.Type().IsPrimitiveType() { vc, reqDiags := decodeVersionConstraint(attr) diags = append(diags, reqDiags...) + + pType, err := addrs.ParseProviderPart(rp.Name) + if err != nil { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid provider name", + Detail: err.Error(), + Subject: attr.Expr.Range().Ptr(), + }) + continue + } + rp.Requirement = vc + rp.Type = addrs.ImpliedProviderForUnqualifiedType(pType) + ret.RequiredProviders[name] = rp + + continue + } - case expr.Type().IsObjectType(): - if expr.Type().HasAttribute("version") { + // verify that the local name is already localized or produce an error. + nameDiags := checkProviderNameNormalized(name, attr.Expr.Range()) + if nameDiags.HasErrors() { + diags = append(diags, nameDiags...) + continue + } + + kvs, mapDiags := hcl.ExprMap(attr.Expr) + if mapDiags.HasErrors() { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid required_providers object", + Detail: "required_providers entries must be strings or objects.", + Subject: attr.Expr.Range().Ptr(), + }) + continue + } + + for _, kv := range kvs { + key, keyDiags := kv.Key.Value(nil) + if keyDiags.HasErrors() { + diags = append(diags, keyDiags...) + continue + } + + if key.Type() != cty.String { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid Attribute", + Detail: fmt.Sprintf("Invalid attribute value for provider requirement: %#v", key), + Subject: kv.Key.Range().Ptr(), + }) + continue + } + + switch key.AsString() { + case "version": vc := VersionConstraint{ DeclRange: attr.Range, } - constraint := expr.GetAttr("version") - if !constraint.Type().Equals(cty.String) || constraint.IsNull() { + + constraint, valDiags := kv.Value.Value(nil) + if valDiags.HasErrors() || !constraint.Type().Equals(cty.String) { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid version constraint", Detail: "Version must be specified as a string.", - Subject: attr.Expr.Range().Ptr(), + Subject: kv.Value.Range().Ptr(), }) - } else { - constraintStr := constraint.AsString() - constraints, err := version.NewConstraint(constraintStr) - if err != nil { - // NewConstraint doesn't return user-friendly errors, so we'll just - // ignore the provided error and produce our own generic one. - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid version constraint", - Detail: "This string does not use correct version constraint syntax.", - Subject: attr.Expr.Range().Ptr(), - }) - } else { - vc.Required = constraints - rp.Requirement = vc - } + continue } - } - if expr.Type().HasAttribute("source") { - source := expr.GetAttr("source") - if !source.Type().Equals(cty.String) || source.IsNull() { + + constraintStr := constraint.AsString() + constraints, err := version.NewConstraint(constraintStr) + if err != nil { + // NewConstraint doesn't return user-friendly errors, so we'll just + // ignore the provided error and produce our own generic one. + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid version constraint", + Detail: "This string does not use correct version constraint syntax.", + Subject: kv.Value.Range().Ptr(), + }) + continue + } + + vc.Required = constraints + rp.Requirement = vc + + case "source": + source, err := kv.Value.Value(nil) + if err != nil || !source.Type().Equals(cty.String) { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid source", Detail: "Source must be specified as a string.", - Subject: attr.Expr.Range().Ptr(), + Subject: kv.Value.Range().Ptr(), }) - } else { - rp.Source = source.AsString() - - fqn, sourceDiags := addrs.ParseProviderSourceString(rp.Source) - - if sourceDiags.HasErrors() { - hclDiags := sourceDiags.ToHCL() - // The diagnostics from ParseProviderSourceString don't contain - // source location information because it has no context to compute - // them from, and so we'll add those in quickly here before we - // return. - for _, diag := range hclDiags { - if diag.Subject == nil { - diag.Subject = attr.Expr.Range().Ptr() - } + continue + } + + fqn, sourceDiags := addrs.ParseProviderSourceString(source.AsString()) + if sourceDiags.HasErrors() { + hclDiags := sourceDiags.ToHCL() + // The diagnostics from ParseProviderSourceString don't contain + // source location information because it has no context to compute + // them from, and so we'll add those in quickly here before we + // return. + for _, diag := range hclDiags { + if diag.Subject == nil { + diag.Subject = kv.Value.Range().Ptr() } - diags = append(diags, hclDiags...) - } else { - rp.Type = fqn } + diags = append(diags, hclDiags...) + continue } - } - attrTypes := expr.Type().AttributeTypes() - for name := range attrTypes { - if name == "version" || name == "source" { + + rp.Source = source.AsString() + rp.Type = fqn + + case "configuration_aliases": + exprs, listDiags := hcl.ExprList(kv.Value) + if listDiags.HasErrors() { + diags = append(diags, listDiags...) continue } + + for _, expr := range exprs { + traversal, travDiags := hcl.AbsTraversalForExpr(expr) + if travDiags.HasErrors() { + diags = append(diags, travDiags...) + continue + } + + addr, cfgDiags := ParseProviderConfigCompact(traversal) + if cfgDiags.HasErrors() { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid configuration_aliases value", + Detail: `Configuration aliases can only contain references to local provider configuration names in the format of provider.alias`, + Subject: kv.Value.Range().Ptr(), + }) + continue + } + + if addr.LocalName != name { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid configuration_aliases value", + Detail: fmt.Sprintf(`Configuration aliases must be prefixed with the provider name. Expected %q, but found %q.`, name, addr.LocalName), + Subject: kv.Value.Range().Ptr(), + }) + continue + } + + rp.Aliases = append(rp.Aliases, addr) + } + + default: diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagError, Summary: "Invalid required_providers object", - Detail: `required_providers objects can only contain "version" and "source" attributes. To configure a provider, use a "provider" block.`, - Subject: attr.Expr.Range().Ptr(), + Detail: `required_providers objects can only contain "version", "source" and "configuration_aliases" attributes. To configure a provider, use a "provider" block.`, + Subject: kv.Key.Range().Ptr(), }) break } - default: - // should not happen - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid required_providers syntax", - Detail: "required_providers entries must be strings or objects.", - Subject: attr.Expr.Range().Ptr(), - }) } - if rp.Type.IsZero() && !diags.HasErrors() { // Don't try to generate an FQN if we've encountered errors + if diags.HasErrors() { + continue + } + + // We can add the required provider when there are no errors. + // If a source was not given, create an implied type. + if rp.Type.IsZero() { pType, err := addrs.ParseProviderPart(rp.Name) if err != nil { diags = append(diags, &hcl.Diagnostic{ diff --git a/vendor/github.com/hashicorp/terraform/configs/provider_requirements_test.go b/vendor/github.com/hashicorp/terraform/configs/provider_requirements_test.go index bf4d9988..69cac1b5 100644 --- a/vendor/github.com/hashicorp/terraform/configs/provider_requirements_test.go +++ b/vendor/github.com/hashicorp/terraform/configs/provider_requirements_test.go @@ -185,15 +185,8 @@ func TestDecodeRequiredProvidersBlock(t *testing.T) { DefRange: blockRange, }, Want: &RequiredProviders{ - RequiredProviders: map[string]*RequiredProvider{ - "my-test": { - Name: "my-test", - Source: "some/invalid/provider/source/test", - Requirement: testVC("~>2.0.0"), - DeclRange: mockRange, - }, - }, - DeclRange: blockRange, + RequiredProviders: map[string]*RequiredProvider{}, + DeclRange: blockRange, }, Error: "Invalid provider source string", }, @@ -213,15 +206,8 @@ func TestDecodeRequiredProvidersBlock(t *testing.T) { DefRange: blockRange, }, Want: &RequiredProviders{ - RequiredProviders: map[string]*RequiredProvider{ - "my_test": { - Name: "my_test", - Type: addrs.Provider{}, - Requirement: testVC("~>2.0.0"), - DeclRange: mockRange, - }, - }, - DeclRange: blockRange, + RequiredProviders: map[string]*RequiredProvider{}, + DeclRange: blockRange, }, Error: "Invalid provider local name", }, @@ -241,15 +227,8 @@ func TestDecodeRequiredProvidersBlock(t *testing.T) { DefRange: blockRange, }, Want: &RequiredProviders{ - RequiredProviders: map[string]*RequiredProvider{ - "MYTEST": { - Name: "MYTEST", - Type: addrs.Provider{}, - Requirement: testVC("~>2.0.0"), - DeclRange: mockRange, - }, - }, - DeclRange: blockRange, + RequiredProviders: map[string]*RequiredProvider{}, + DeclRange: blockRange, }, Error: "Invalid provider local name", }, @@ -270,15 +249,8 @@ func TestDecodeRequiredProvidersBlock(t *testing.T) { DefRange: blockRange, }, Want: &RequiredProviders{ - RequiredProviders: map[string]*RequiredProvider{ - "my-test": { - Name: "my-test", - Source: "mycloud/test", - Type: addrs.NewProvider(addrs.DefaultRegistryHost, "mycloud", "test"), - DeclRange: mockRange, - }, - }, - DeclRange: blockRange, + RequiredProviders: map[string]*RequiredProvider{}, + DeclRange: blockRange, }, Error: "Invalid version constraint", }, @@ -296,15 +268,10 @@ func TestDecodeRequiredProvidersBlock(t *testing.T) { DefRange: blockRange, }, Want: &RequiredProviders{ - RequiredProviders: map[string]*RequiredProvider{ - "test": { - Name: "test", - DeclRange: mockRange, - }, - }, - DeclRange: blockRange, + RequiredProviders: map[string]*RequiredProvider{}, + DeclRange: blockRange, }, - Error: "Invalid required_providers syntax", + Error: "Invalid required_providers object", }, "invalid source attribute type": { Block: &hcl.Block{ @@ -322,13 +289,8 @@ func TestDecodeRequiredProvidersBlock(t *testing.T) { DefRange: blockRange, }, Want: &RequiredProviders{ - RequiredProviders: map[string]*RequiredProvider{ - "my-test": { - Name: "my-test", - DeclRange: mockRange, - }, - }, - DeclRange: blockRange, + RequiredProviders: map[string]*RequiredProvider{}, + DeclRange: blockRange, }, Error: "Invalid source", }, @@ -350,16 +312,8 @@ func TestDecodeRequiredProvidersBlock(t *testing.T) { DefRange: blockRange, }, Want: &RequiredProviders{ - RequiredProviders: map[string]*RequiredProvider{ - "my-test": { - Name: "my-test", - Source: "mycloud/test", - Type: addrs.NewProvider(addrs.DefaultRegistryHost, "mycloud", "test"), - Requirement: testVC("2.0.0"), - DeclRange: mockRange, - }, - }, - DeclRange: blockRange, + RequiredProviders: map[string]*RequiredProvider{}, + DeclRange: blockRange, }, Error: "Invalid required_providers object", }, @@ -370,7 +324,7 @@ func TestDecodeRequiredProvidersBlock(t *testing.T) { got, diags := decodeRequiredProvidersBlock(test.Block) if diags.HasErrors() { if test.Error == "" { - t.Fatalf("unexpected error") + t.Fatalf("unexpected error: %v", diags) } if gotErr := diags[0].Summary; gotErr != test.Error { t.Errorf("wrong error, got %q, want %q", gotErr, test.Error) diff --git a/vendor/github.com/hashicorp/terraform/configs/provider_validation.go b/vendor/github.com/hashicorp/terraform/configs/provider_validation.go new file mode 100644 index 00000000..425d1dff --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/provider_validation.go @@ -0,0 +1,312 @@ +package configs + +import ( + "fmt" + "strings" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/terraform/addrs" +) + +// validateProviderConfigs walks the full configuration tree from the root +// module outward, static validation rules to the various combinations of +// provider configuration, required_providers values, and module call providers +// mappings. +// +// To retain compatibility with previous terraform versions, empty "proxy +// provider blocks" are still allowed within modules, though they will +// generate warnings when the configuration is loaded. The new validation +// however will generate an error if a suitable provider configuration is not +// passed in through the module call. +// +// The call argument is the ModuleCall for the provided Config cfg. The +// noProviderConfig argument is passed down the call stack, indicating that the +// module call, or a parent module call, has used a feature that precludes +// providers from being configured at all within the module. +func validateProviderConfigs(parentCall *ModuleCall, cfg *Config, noProviderConfig bool) (diags hcl.Diagnostics) { + mod := cfg.Module + + for name, child := range cfg.Children { + mc := mod.ModuleCalls[name] + + // if the module call has any of count, for_each or depends_on, + // providers are prohibited from being configured in this module, or + // any module beneath this module. + nope := noProviderConfig || mc.Count != nil || mc.ForEach != nil || mc.DependsOn != nil + diags = append(diags, validateProviderConfigs(mc, child, nope)...) + } + + // the set of provider configuration names passed into the module, with the + // source range of the provider assignment in the module call. + passedIn := map[string]PassedProviderConfig{} + + // the set of empty configurations that could be proxy configurations, with + // the source range of the empty configuration block. + emptyConfigs := map[string]*hcl.Range{} + + // the set of provider with a defined configuration, with the source range + // of the configuration block declaration. + configured := map[string]*hcl.Range{} + + // the set of configuration_aliases defined in the required_providers + // block, with the fully qualified provider type. + configAliases := map[string]addrs.AbsProviderConfig{} + + // the set of provider names defined in the required_providers block, and + // their provider types. + localNames := map[string]addrs.AbsProviderConfig{} + + for _, pc := range mod.ProviderConfigs { + name := providerName(pc.Name, pc.Alias) + // Validate the config against an empty schema to see if it's empty. + _, pcConfigDiags := pc.Config.Content(&hcl.BodySchema{}) + if pcConfigDiags.HasErrors() || pc.Version.Required != nil { + configured[name] = &pc.DeclRange + } else { + emptyConfigs[name] = &pc.DeclRange + } + } + + if mod.ProviderRequirements != nil { + for _, req := range mod.ProviderRequirements.RequiredProviders { + addr := addrs.AbsProviderConfig{ + Module: cfg.Path, + Provider: req.Type, + } + localNames[req.Name] = addr + for _, alias := range req.Aliases { + addr := addrs.AbsProviderConfig{ + Module: cfg.Path, + Provider: req.Type, + Alias: alias.Alias, + } + configAliases[providerName(alias.LocalName, alias.Alias)] = addr + } + } + } + + // collect providers passed from the parent + if parentCall != nil { + for _, passed := range parentCall.Providers { + name := providerName(passed.InChild.Name, passed.InChild.Alias) + passedIn[name] = passed + } + } + + parentModuleText := "the root module" + moduleText := "the root module" + if !cfg.Path.IsRoot() { + moduleText = cfg.Path.String() + if parent := cfg.Path.Parent(); !parent.IsRoot() { + // module address are prefixed with `module.` + parentModuleText = parent.String() + } + } + + // Verify that any module calls only refer to named providers, and that + // those providers will have a configuration at runtime. This way we can + // direct users where to add the missing configuration, because the runtime + // error is only "missing provider X". + for _, modCall := range mod.ModuleCalls { + for _, passed := range modCall.Providers { + // aliased providers are handled more strictly, and are never + // inherited, so they are validated within modules further down. + // Skip these checks to prevent redundant diagnostics. + if passed.InParent.Alias != "" { + continue + } + + name := passed.InParent.String() + _, confOK := configured[name] + _, localOK := localNames[name] + _, passedOK := passedIn[name] + + // This name was not declared somewhere within in the + // configuration. We ignore empty configs, because they will + // already produce a warning. + if !(confOK || localOK) { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagWarning, + Summary: fmt.Sprintf("Provider %s is undefined", name), + Detail: fmt.Sprintf("No provider named %s has been declared in %s.\n", name, moduleText) + + fmt.Sprintf("If you wish to refer to the %s provider within the module, add a provider configuration, or an entry in the required_providers block.", name), + Subject: &passed.InParent.NameRange, + }) + continue + } + + // Now we may have named this provider within the module, but + // there won't be a configuration available at runtime if the + // parent module did not pass one in. + if !cfg.Path.IsRoot() && !(confOK || passedOK) { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagWarning, + Summary: fmt.Sprintf("No configuration passed in for provider %s in %s", name, cfg.Path), + Detail: fmt.Sprintf("Provider %s is referenced within %s, but no configuration has been supplied.\n", name, moduleText) + + fmt.Sprintf("Add a provider named %s to the providers map for %s in %s.", name, cfg.Path, parentModuleText), + Subject: &passed.InParent.NameRange, + }) + } + } + } + + if cfg.Path.IsRoot() { + // nothing else to do in the root module + return diags + } + + // there cannot be any configurations if no provider config is allowed + if len(configured) > 0 && noProviderConfig { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: fmt.Sprintf("Module %s contains provider configuration", cfg.Path), + Detail: "Providers cannot be configured within modules using count, for_each or depends_on.", + }) + } + + // now check that the user is not attempting to override a config + for name := range configured { + if passed, ok := passedIn[name]; ok { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Cannot override provider configuration", + Detail: fmt.Sprintf("Provider %s is configured within the module %s and cannot be overridden.", name, cfg.Path), + Subject: &passed.InChild.NameRange, + }) + } + } + + // A declared alias requires either a matching configuration within the + // module, or one must be passed in. + for name, providerAddr := range configAliases { + _, confOk := configured[name] + _, passedOk := passedIn[name] + + if confOk || passedOk { + continue + } + + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: fmt.Sprintf("No configuration for provider %s", name), + Detail: fmt.Sprintf("Configuration required for %s.\n", providerAddr) + + fmt.Sprintf("Add a provider named %s to the providers map for %s in %s.", name, cfg.Path, parentModuleText), + Subject: &parentCall.DeclRange, + }) + } + + // You cannot pass in a provider that cannot be used + for name, passed := range passedIn { + childTy := passed.InChild.providerType + // get a default type if there was none set + if childTy.IsZero() { + // This means the child module is only using an inferred + // provider type. We allow this but will generate a warning to + // declare provider_requirements below. + childTy = addrs.NewDefaultProvider(passed.InChild.Name) + } + + providerAddr := addrs.AbsProviderConfig{ + Module: cfg.Path, + Provider: childTy, + Alias: passed.InChild.Alias, + } + + localAddr, localName := localNames[name] + if localName { + providerAddr = localAddr + } + + aliasAddr, configAlias := configAliases[name] + if configAlias { + providerAddr = aliasAddr + } + + _, emptyConfig := emptyConfigs[name] + + if !(localName || configAlias || emptyConfig) { + severity := hcl.DiagError + + // we still allow default configs, so switch to a warning if the incoming provider is a default + if providerAddr.Provider.IsDefault() { + severity = hcl.DiagWarning + } + + diags = append(diags, &hcl.Diagnostic{ + Severity: severity, + Summary: fmt.Sprintf("Provider %s is undefined", name), + Detail: fmt.Sprintf("Module %s does not declare a provider named %s.\n", cfg.Path, name) + + fmt.Sprintf("If you wish to specify a provider configuration for the module, add an entry for %s in the required_providers block within the module.", name), + Subject: &passed.InChild.NameRange, + }) + } + + // The provider being passed in must also be of the correct type. + pTy := passed.InParent.providerType + if pTy.IsZero() { + // While we would like to ensure required_providers exists here, + // implied default configuration is still allowed. + pTy = addrs.NewDefaultProvider(passed.InParent.Name) + } + + // use the full address for a nice diagnostic output + parentAddr := addrs.AbsProviderConfig{ + Module: cfg.Parent.Path, + Provider: pTy, + Alias: passed.InParent.Alias, + } + + if cfg.Parent.Module.ProviderRequirements != nil { + req, defined := cfg.Parent.Module.ProviderRequirements.RequiredProviders[name] + if defined { + parentAddr.Provider = req.Type + } + } + + if !providerAddr.Provider.Equals(parentAddr.Provider) { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: fmt.Sprintf("Invalid type for provider %s", providerAddr), + Detail: fmt.Sprintf("Cannot use configuration from %s for %s. ", parentAddr, providerAddr) + + "The given provider configuration is for a different provider type.", + Subject: &passed.InChild.NameRange, + }) + } + } + + // Empty configurations are no longer needed + for name, src := range emptyConfigs { + detail := fmt.Sprintf("Remove the %s provider block from %s.", name, cfg.Path) + + isAlias := strings.Contains(name, ".") + _, isConfigAlias := configAliases[name] + _, isLocalName := localNames[name] + + if isAlias && !isConfigAlias { + localName := strings.Split(name, ".")[0] + detail = fmt.Sprintf("Remove the %s provider block from %s. Add %s to the list of configuration_aliases for %s in required_providers to define the provider configuration name.", name, cfg.Path, name, localName) + } + + if !isAlias && !isLocalName { + // if there is no local name, add a note to include it in the + // required_provider block + detail += fmt.Sprintf("\nTo ensure the correct provider configuration is used, add %s to the required_providers configuration", name) + } + + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagWarning, + Summary: "Empty provider configuration blocks are not required", + Detail: detail, + Subject: src, + }) + } + + return diags +} + +func providerName(name, alias string) string { + if alias != "" { + name = name + "." + alias + } + return name +} diff --git a/vendor/github.com/hashicorp/terraform/configs/provisioner.go b/vendor/github.com/hashicorp/terraform/configs/provisioner.go index 5b664d39..84deda3a 100644 --- a/vendor/github.com/hashicorp/terraform/configs/provisioner.go +++ b/vendor/github.com/hashicorp/terraform/configs/provisioner.go @@ -34,11 +34,12 @@ func decodeProvisionerBlock(block *hcl.Block) (*Provisioner, hcl.Diagnostics) { switch pv.Type { case "chef", "habitat", "puppet", "salt-masterless": diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagWarning, - Summary: fmt.Sprintf("The \"%s\" provisioner is deprecated", pv.Type), - Detail: fmt.Sprintf("The \"%s\" provisioner is deprecated and will be removed from future versions of Terraform. Visit https://learn.hashicorp.com/collections/terraform/provision for alternatives to using provisioners that are a better fit for the Terraform workflow.", pv.Type), + Severity: hcl.DiagError, + Summary: fmt.Sprintf("The \"%s\" provisioner has been removed", pv.Type), + Detail: fmt.Sprintf("The \"%s\" provisioner was deprecated in Terraform 0.13.4 has been removed from Terraform. Visit https://learn.hashicorp.com/collections/terraform/provision for alternatives to using provisioners that are a better fit for the Terraform workflow.", pv.Type), Subject: &pv.TypeRange, }) + return nil, diags } if attr, exists := content.Attributes["when"]; exists { diff --git a/vendor/github.com/hashicorp/terraform/configs/resource.go b/vendor/github.com/hashicorp/terraform/configs/resource.go index e5cc8c60..73c0d7c8 100644 --- a/vendor/github.com/hashicorp/terraform/configs/resource.go +++ b/vendor/github.com/hashicorp/terraform/configs/resource.go @@ -66,8 +66,12 @@ func (r *Resource) Addr() addrs.Resource { // config addr if an explicit "provider" argument was not provided. func (r *Resource) ProviderConfigAddr() addrs.LocalProviderConfig { if r.ProviderConfigRef == nil { + // If no specific "provider" argument is given, we want to look up the + // provider config where the local name matches the implied provider + // from the resource type. This may be different from the resource's + // provider type. return addrs.LocalProviderConfig{ - LocalName: r.Provider.Type, + LocalName: r.Addr().ImpliedProvider(), } } @@ -88,13 +92,6 @@ func decodeResourceBlock(block *hcl.Block) (*Resource, hcl.Diagnostics) { Managed: &ManagedResource{}, } - // Produce deprecation messages for any pre-0.12-style - // single-interpolation-only expressions. We do this up front here because - // then we can also catch instances inside special blocks like "connection", - // before PartialContent extracts them. - moreDiags := warnForDeprecatedInterpolationsInBody(block.Body) - diags = append(diags, moreDiags...) - content, remain, moreDiags := block.Body.PartialContent(resourceBlockSchema) diags = append(diags, moreDiags...) r.Config = remain @@ -207,9 +204,9 @@ func decodeResourceBlock(block *hcl.Block) (*Resource, hcl.Diagnostics) { r.Managed.IgnoreAllChanges = true ignoreAllRange = expr.Range() diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagWarning, - Summary: "Deprecated ignore_changes wildcard", - Detail: "The [\"*\"] form of ignore_changes wildcard is deprecated. Use \"ignore_changes = all\" to ignore changes to all attributes.", + Severity: hcl.DiagError, + Summary: "Invalid ignore_changes wildcard", + Detail: "The [\"*\"] form of ignore_changes wildcard is was deprecated and is now invalid. Use \"ignore_changes = all\" to ignore changes to all attributes.", Subject: attr.Expr.Range().Ptr(), }) continue @@ -299,11 +296,6 @@ func decodeDataBlock(block *hcl.Block) (*Resource, hcl.Diagnostics) { TypeRange: block.LabelRanges[0], } - // Produce deprecation messages for any pre-0.12-style - // single-interpolation-only expressions. - moreDiags := warnForDeprecatedInterpolationsInBody(block.Body) - diags = append(diags, moreDiags...) - content, remain, moreDiags := block.Body.PartialContent(dataBlockSchema) diags = append(diags, moreDiags...) r.Config = remain @@ -382,6 +374,13 @@ type ProviderConfigRef struct { NameRange hcl.Range Alias string AliasRange *hcl.Range // nil if alias not set + + // TODO: this may not be set in some cases, so it is not yet suitable for + // use outside of this package. We currently only use it for internal + // validation, but once we verify that this can be set in all cases, we can + // export this so providers don't need to be re-resolved. + // This same field is also added to the Provider struct. + providerType addrs.Provider } func decodeProviderConfigRef(expr hcl.Expression, argName string) (*ProviderConfigRef, hcl.Diagnostics) { diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/empty-configs/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/empty-configs/main.tf new file mode 100644 index 00000000..c0edba27 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/empty-configs/main.tf @@ -0,0 +1,20 @@ +terraform { + required_providers { + foo = { + source = "hashicorp/foo" + } + baz = { + source = "hashicorp/baz" + } + } +} + +module "mod" { + source = "./mod" + providers = { + foo = foo + foo.bar = foo + baz = baz + baz.bing = baz + } +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/empty-configs/mod/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/empty-configs/mod/main.tf new file mode 100644 index 00000000..50995ca0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/empty-configs/mod/main.tf @@ -0,0 +1,22 @@ +terraform { + required_providers { + foo = { + source = "hashicorp/foo" + configuration_aliases = [ foo.bar ] + } + } +} + +provider "foo" { +} + +provider "foo" { + alias = "bar" +} + +provider "baz" { +} + +provider "baz" { + alias = "bing" +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/empty-configs/warnings b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/empty-configs/warnings new file mode 100644 index 00000000..dcf6736e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/empty-configs/warnings @@ -0,0 +1,4 @@ +empty-configs/mod/main.tf:10,1-15: Empty provider configuration blocks are not required; Remove the foo provider block from module.mod +empty-configs/mod/main.tf:13,1-15: Empty provider configuration blocks are not required; Remove the foo.bar provider block from module.mod +empty-configs/mod/main.tf:17,1-15: Empty provider configuration blocks are not required; Remove the baz provider block from module.mod.\nTo ensure the correct provider configuration is used, add baz to the required_providers configuration +empty-configs/mod/main.tf:20,1-15: Empty provider configuration blocks are not required; Remove the baz.bing provider block from module.mod. Add baz.bing to the list of configuration_aliases for baz in required_providers to define the provider configuration name diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/incorrect-type/errors b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/incorrect-type/errors new file mode 100644 index 00000000..28b21085 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/incorrect-type/errors @@ -0,0 +1 @@ +incorrect-type/main.tf:15,5-8: Invalid type for provider module.mod.provider["example.com/vendor/foo"]; Cannot use configuration from provider["registry.terraform.io/hashicorp/foo"] for module.mod.provider["example.com/vendor/foo"] diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/incorrect-type/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/incorrect-type/main.tf new file mode 100644 index 00000000..074cc842 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/incorrect-type/main.tf @@ -0,0 +1,18 @@ +terraform { + required_providers { + foo = { + source = "hashicorp/foo" + } + baz = { + source = "hashicorp/baz" + } + } +} + +module "mod" { + source = "./mod" + providers = { + foo = foo + baz = baz + } +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/incorrect-type/mod/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/incorrect-type/mod/main.tf new file mode 100644 index 00000000..14c3239e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/incorrect-type/mod/main.tf @@ -0,0 +1,14 @@ +terraform { + required_providers { + foo = { + source = "example.com/vendor/foo" + } + } +} + +resource "foo_resource" "a" { +} + +// implied default provider baz +resource "baz_resource" "a" { +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/incorrect-type/warnings b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/incorrect-type/warnings new file mode 100644 index 00000000..a87f1f74 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/incorrect-type/warnings @@ -0,0 +1 @@ +incorrect-type/main.tf:16,5-8: Provider baz is undefined; Module module.mod does not declare a provider named baz.\nIf you wish to specify a provider configuration for the module diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/nested-provider/child/child2/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/nested-provider/child/child2/main.tf new file mode 100644 index 00000000..f2695a66 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/nested-provider/child/child2/main.tf @@ -0,0 +1,7 @@ +provider "aws" { + value = "foo" +} + +output "my_output" { + value = "my output" +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/nested-provider/child/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/nested-provider/child/main.tf new file mode 100644 index 00000000..9a725a52 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/nested-provider/child/main.tf @@ -0,0 +1,4 @@ +module "child2" { + // the test fixture treats these sources as relative to the root + source = "./child/child2" +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/nested-provider/errors b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/nested-provider/errors new file mode 100644 index 00000000..8f44cac7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/nested-provider/errors @@ -0,0 +1,3 @@ +Module module.child.module.child2 contains provider configuration; Providers cannot be configured within modules using count, for_each or depends_on + + diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/more-nested-provider/root.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/nested-provider/root.tf similarity index 100% rename from vendor/github.com/hashicorp/terraform/configs/configload/testdata/expand-modules/more-nested-provider/root.tf rename to vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/nested-provider/root.tf diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/override-provider/errors b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/override-provider/errors new file mode 100644 index 00000000..a8d59d6e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/override-provider/errors @@ -0,0 +1 @@ +override-provider/main.tf:17,5-8: Cannot override provider configuration; Provider bar is configured within the module module.mod and cannot be overridden. diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/override-provider/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/override-provider/main.tf new file mode 100644 index 00000000..30feec1c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/override-provider/main.tf @@ -0,0 +1,19 @@ +terraform { + required_providers { + bar = { + version = "~>1.0.0" + } + } +} + +provider "bar" { + value = "not ok" +} + +// this module configures its own provider, which cannot be overridden +module "mod" { + source = "./mod" + providers = { + bar = bar + } +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/override-provider/mod/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/override-provider/mod/main.tf new file mode 100644 index 00000000..c0b61697 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/override-provider/mod/main.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + bar = { + version = "~>1.0.0" + } + } +} + +// this configuration cannot be overridden from an outside module +provider "bar" { + value = "ok" +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/pass-inherited-provider/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/pass-inherited-provider/main.tf new file mode 100644 index 00000000..76456399 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/pass-inherited-provider/main.tf @@ -0,0 +1,7 @@ +provider "test" { + value = "ok" +} + +module "mod" { + source = "./mod" +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/pass-inherited-provider/mod/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/pass-inherited-provider/mod/main.tf new file mode 100644 index 00000000..e0d142d5 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/pass-inherited-provider/mod/main.tf @@ -0,0 +1,17 @@ +terraform { + required_providers { + test = { + source = "hashicorp/test" + } + } +} + +module "mod2" { + source = "./mod2" + + // the test provider is named here, but a config must be supplied from the + // parent module. + providers = { + test.foo = test + } +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/pass-inherited-provider/mod2/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/pass-inherited-provider/mod2/main.tf new file mode 100644 index 00000000..0b736169 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/pass-inherited-provider/mod2/main.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + test = { + source = "hashicorp/test" + configuration_aliases = [test.foo] + } + } +} + +resource "test_resource" "foo" { + provider = test.foo +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/pass-inherited-provider/warnings b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/pass-inherited-provider/warnings new file mode 100644 index 00000000..fee53bc4 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/pass-inherited-provider/warnings @@ -0,0 +1 @@ +pass-inherited-provider/mod/main.tf:15,16-20: No configuration passed in for provider test in module.mod; Provider test is referenced within module.mod, but no configuration has been supplied diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/required-alias/errors b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/required-alias/errors new file mode 100644 index 00000000..a1b944a4 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/required-alias/errors @@ -0,0 +1 @@ +required-alias/main.tf:1,1-13: No configuration for provider foo.bar; Configuration required for module.mod.provider["registry.terraform.io/hashicorp/foo"].bar diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/required-alias/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/required-alias/main.tf new file mode 100644 index 00000000..c2cfe60b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/required-alias/main.tf @@ -0,0 +1,4 @@ +module "mod" { + source = "./mod" + // missing providers with foo.bar provider config +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/required-alias/mod/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/required-alias/mod/main.tf new file mode 100644 index 00000000..0f2a5216 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/required-alias/mod/main.tf @@ -0,0 +1,13 @@ +terraform { + required_providers { + foo = { + source = "hashicorp/foo" + version = "1.0.0" + configuration_aliases = [ foo.bar ] + } + } +} + +resource "foo_resource" "a" { + provider = foo.bar +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/unexpected-provider/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/unexpected-provider/main.tf new file mode 100644 index 00000000..cd859a72 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/unexpected-provider/main.tf @@ -0,0 +1,15 @@ +terraform { + required_providers { + foo = { + source = "hashicorp/foo" + version = "1.0.0" + } + } +} + +module "mod" { + source = "./mod" + providers = { + foo = foo + } +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/unexpected-provider/mod/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/unexpected-provider/mod/main.tf new file mode 100644 index 00000000..f69bfa81 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/unexpected-provider/mod/main.tf @@ -0,0 +1,2 @@ +resource "foo_resource" "a" { +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/unexpected-provider/warnings b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/unexpected-provider/warnings new file mode 100644 index 00000000..0e41b39a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/unexpected-provider/warnings @@ -0,0 +1,2 @@ +unexpected-provider/main.tf:13,5-8: Provider foo is undefined; Module module.mod does not declare a provider named foo. + diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/unknown-root-provider/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/unknown-root-provider/main.tf new file mode 100644 index 00000000..cf60f915 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/unknown-root-provider/main.tf @@ -0,0 +1,7 @@ +module "mod" { + source = "./mod" + providers = { + // bar may be required by the module, but the name is not defined here + bar = bar + } +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/unknown-root-provider/mod/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/unknown-root-provider/mod/main.tf new file mode 100644 index 00000000..ea8d0321 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/unknown-root-provider/mod/main.tf @@ -0,0 +1,10 @@ +terraform { + required_providers { + bar = { + version = "~>1.0.0" + } + } +} + +resource "bar_resource" "x" { +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/unknown-root-provider/warnings b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/unknown-root-provider/warnings new file mode 100644 index 00000000..766670d9 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/unknown-root-provider/warnings @@ -0,0 +1 @@ +unknown-root-provider/main.tf:5,11-14: Provider bar is undefined; No provider named bar has been declared in the root module diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/with-depends-on/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/with-depends-on/main.tf new file mode 100644 index 00000000..49c2dcd6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/with-depends-on/main.tf @@ -0,0 +1,14 @@ +terraform { + required_providers { + foo = { + source = "hashicorp/foo" + } + } +} + +module "mod2" { + source = "./mod1" + providers = { + foo = foo + } +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/with-depends-on/mod1/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/with-depends-on/mod1/main.tf new file mode 100644 index 00000000..c318484b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/with-depends-on/mod1/main.tf @@ -0,0 +1,19 @@ +terraform { + required_providers { + foo = { + source = "hashicorp/foo" + } + } +} + +resource "foo_resource" "a" { +} + +module "mod2" { + depends_on = [foo_resource.a] + // test fixture source is from root + source = "./mod1/mod2" + providers = { + foo = foo + } +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/with-depends-on/mod1/mod2/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/with-depends-on/mod1/mod2/main.tf new file mode 100644 index 00000000..eaa3550b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/with-depends-on/mod1/mod2/main.tf @@ -0,0 +1,15 @@ +terraform { + required_providers { + foo = { + source = "hashicorp/foo" + } + } +} + +module "mod3" { + // test fixture source is from root + source = "./mod1/mod2/mod3" + providers = { + foo.bar = foo + } +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/with-depends-on/mod1/mod2/mod3/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/with-depends-on/mod1/mod2/mod3/main.tf new file mode 100644 index 00000000..b1827126 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/config-diagnostics/with-depends-on/mod1/mod2/mod3/main.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + foo = { + source = "hashicorp/foo" + configuration_aliases = [ foo.bar ] + } + } +} + +resource "foo_resource" "a" { + providers = foo.bar +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/ignore_changes.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/ignore_changes.tf new file mode 100644 index 00000000..061209c1 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/ignore_changes.tf @@ -0,0 +1,5 @@ +resource "null_resource" "all" { + lifecycle { + ignore_changes = ["*"] # ERROR: Invalid ignore_changes wildcard + } +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/invalid_language_edition.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/invalid_language_edition.tf new file mode 100644 index 00000000..255aefe0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/invalid_language_edition.tf @@ -0,0 +1,4 @@ +terraform { + # The language argument expects a bare keyword, not a string. + language = "TF2021" # ERROR: Invalid language edition +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/provider-source-prefix.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/provider-source-prefix.tf index 99cd76df..96811699 100644 --- a/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/provider-source-prefix.tf +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/provider-source-prefix.tf @@ -1,10 +1,10 @@ terraform { required_providers { - usererror = { # ERROR: Invalid provider type - source = "foo/terraform-provider-foo" + usererror = { + source = "foo/terraform-provider-foo" # ERROR: Invalid provider type } - badname = { # ERROR: Invalid provider type - source = "foo/terraform-foo" + badname = { + source = "foo/terraform-foo" # ERROR: Invalid provider type } } } diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/unsupported_language_edition.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/unsupported_language_edition.tf new file mode 100644 index 00000000..0ee616f7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/unsupported_language_edition.tf @@ -0,0 +1,6 @@ +terraform { + # If a future change in this repository happens to make TF2038 a valid + # edition then this will start failing; in that case, change this file to + # select a different edition that isn't supported. + language = TF2038 # ERROR: Unsupported language edition +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/variable_type_quoted.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/variable_type_quoted.tf new file mode 100644 index 00000000..2292ce15 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/variable_type_quoted.tf @@ -0,0 +1,11 @@ +variable "bad_string" { + type = "string" # ERROR: Invalid quoted type constraints +} + +variable "bad_map" { + type = "map" # ERROR: Invalid quoted type constraints +} + +variable "bad_list" { + type = "list" # ERROR: Invalid quoted type constraints +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/vendor_provisioners.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/vendor_provisioners.tf new file mode 100644 index 00000000..4d2ec789 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/error-files/vendor_provisioners.tf @@ -0,0 +1,3 @@ +resource "null_resource" "test" { + provisioner "habitat" {} # ERROR: The "habitat" provisioner has been removed +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/invalid-modules/provider-meta/invalid-interpolation.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/invalid-modules/provider-meta/invalid-interpolation.tf new file mode 100644 index 00000000..d8a0d444 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/invalid-modules/provider-meta/invalid-interpolation.tf @@ -0,0 +1,10 @@ +terraform { + provider_meta "my-provider" { + hello = var.name + } +} + +variable "name" { + type = string +} + diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/nested-backend-warning/child/child.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/nested-backend-warning/child/child.tf new file mode 100644 index 00000000..5a6948e8 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/nested-backend-warning/child/child.tf @@ -0,0 +1,6 @@ +terraform { + # Only the root module can declare a backend. Terraform should emit a warning + # about this child module backend declaration. + backend "ignored" { + } +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/empty-with-child-module/root.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/nested-backend-warning/root.tf similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/testdata/empty-with-child-module/root.tf rename to vendor/github.com/hashicorp/terraform/configs/testdata/nested-backend-warning/root.tf diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/valid-files/valid-language-edition.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/valid-files/valid-language-edition.tf new file mode 100644 index 00000000..60f2df3e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/valid-files/valid-language-edition.tf @@ -0,0 +1,8 @@ +terraform { + # If we drop support for TF2021 in a future Terraform release then this + # test will fail. In that case, update this to a newer edition that is + # still supported, because the purpose of this test is to verify that + # we can successfully decode the language argument, not specifically + # that we support TF2021. + language = TF2021 +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/valid-modules/nested-providers-fqns/child/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/valid-modules/nested-providers-fqns/child/main.tf index 1973912d..79e449bf 100644 --- a/vendor/github.com/hashicorp/terraform/configs/testdata/valid-modules/nested-providers-fqns/child/main.tf +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/valid-modules/nested-providers-fqns/child/main.tf @@ -3,11 +3,13 @@ terraform { bar-test = { source = "bar/test" } + foo-test = { + source = "foo/test" + configuration_aliases = [foo-test.other] + } } } -provider "bar-test" {} - resource "test_instance" "explicit" { // explicitly setting provider bar-test provider = bar-test @@ -17,3 +19,7 @@ resource "test_instance" "implicit" { // since the provider type name "test" does not match an entry in // required_providers, the default provider "test" should be used } + +resource "test_instance" "other" { + provider = foo-test.other +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/valid-modules/nested-providers-fqns/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/valid-modules/nested-providers-fqns/main.tf index 3a9dc4a1..27988f42 100644 --- a/vendor/github.com/hashicorp/terraform/configs/testdata/valid-modules/nested-providers-fqns/main.tf +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/valid-modules/nested-providers-fqns/main.tf @@ -10,6 +10,9 @@ provider "foo-test" {} module "child" { source = "./child" + providers = { + foo-test.other = foo-test + } } resource "test_instance" "explicit" { diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/valid-modules/provider-aliases/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/valid-modules/provider-aliases/main.tf new file mode 100644 index 00000000..dd9fb084 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/valid-modules/provider-aliases/main.tf @@ -0,0 +1,17 @@ +terraform { + required_providers { + foo-test = { + source = "foo/test" + configuration_aliases = [foo-test.a, foo-test.b] + } + } +} + +resource "test_instance" "explicit" { + provider = foo-test.a +} + +data "test_resource" "explicit" { + provider = foo-test.b +} + diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/valid-modules/provider-meta/main.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/valid-modules/provider-meta/main.tf new file mode 100644 index 00000000..073e35b5 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/testdata/valid-modules/provider-meta/main.tf @@ -0,0 +1,5 @@ +terraform { + provider_meta "my-provider" { + hello = "test-module" + } +} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/warning-files/ignore_changes.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/warning-files/ignore_changes.tf deleted file mode 100644 index 5678fe7b..00000000 --- a/vendor/github.com/hashicorp/terraform/configs/testdata/warning-files/ignore_changes.tf +++ /dev/null @@ -1,11 +0,0 @@ -resource "null_resource" "one" { - lifecycle { - ignore_changes = ["triggers"] # WARNING: Quoted references are deprecated - } -} - -resource "null_resource" "all" { - lifecycle { - ignore_changes = ["*"] # WARNING: Deprecated ignore_changes wildcard - } -} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/warning-files/redundant_interp.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/warning-files/redundant_interp.tf deleted file mode 100644 index d1a3522d..00000000 --- a/vendor/github.com/hashicorp/terraform/configs/testdata/warning-files/redundant_interp.tf +++ /dev/null @@ -1,55 +0,0 @@ -# It's redundant to write an expression that is just a single template -# interpolation with another expression inside, like "${foo}", but it -# was required before Terraform v0.12 and so there are lots of existing -# examples out there using that style. -# -# We are generating warnings for that situation in order to guide those -# who are following old examples toward the new idiom. - -variable "triggers" { - type = "map" # WARNING: Quoted type constraints are deprecated -} - -provider "null" { - foo = "${var.triggers["foo"]}" # WARNING: Interpolation-only expressions are deprecated -} - -resource "null_resource" "a" { - triggers = "${var.triggers}" # WARNING: Interpolation-only expressions are deprecated - - connection { - type = "ssh" - host = "${var.triggers["host"]}" # WARNING: Interpolation-only expressions are deprecated - } - - provisioner "local-exec" { - single = "${var.triggers["greeting"]}" # WARNING: Interpolation-only expressions are deprecated - - # No warning for this one, because there's more than just one interpolation - # in the template. - template = " ${var.triggers["greeting"]} " - - wrapped = ["${var.triggers["greeting"]}"] # WARNING: Interpolation-only expressions are deprecated - } -} - -module "foo" { - source = "./foo" - foo = "${var.foo}" # WARNING: Interpolation-only expressions are deprecated -} - -data "null_data_source" "b" { - inputs = { - host = "${var.triggers["host"]}" # WARNING: Interpolation-only expressions are deprecated - } - - has_computed_default = "${var.foo}" # WARNING: Interpolation-only expressions are deprecated -} - -output "output" { - value = "${var.foo}" # WARNING: Interpolation-only expressions are deprecated -} - -locals { - foo = "${var.foo}" # WARNING: Interpolation-only expressions are deprecated -} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/warning-files/variable_type_quoted.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/warning-files/variable_type_quoted.tf deleted file mode 100644 index 9201ba62..00000000 --- a/vendor/github.com/hashicorp/terraform/configs/testdata/warning-files/variable_type_quoted.tf +++ /dev/null @@ -1,11 +0,0 @@ -variable "bad_string" { - type = "string" # WARNING: Quoted type constraints are deprecated -} - -variable "bad_map" { - type = "map" # WARNING: Quoted type constraints are deprecated -} - -variable "bad_list" { - type = "list" # WARNING: Quoted type constraints are deprecated -} diff --git a/vendor/github.com/hashicorp/terraform/configs/testdata/warning-files/vendor_provisioners.tf b/vendor/github.com/hashicorp/terraform/configs/testdata/warning-files/vendor_provisioners.tf deleted file mode 100644 index 7da591df..00000000 --- a/vendor/github.com/hashicorp/terraform/configs/testdata/warning-files/vendor_provisioners.tf +++ /dev/null @@ -1,3 +0,0 @@ -resource "null_resource" "test" { - provisioner "habitat" {} # WARNING: The "habitat" provisioner is deprecated -} diff --git a/vendor/github.com/hashicorp/terraform/contrib/fish-completion/README.md b/vendor/github.com/hashicorp/terraform/contrib/fish-completion/README.md deleted file mode 100644 index a50ed1e8..00000000 --- a/vendor/github.com/hashicorp/terraform/contrib/fish-completion/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Terraform fish shell completion - -Copy the completions to your local fish configuration: - -``` -mkdir -p ~/.config/fish/completions -cp terraform.fish ~/.config/fish/completions -``` - -Please note that these completions have been merged upstream and should be bundled with fish 2.6 or later. diff --git a/vendor/github.com/hashicorp/terraform/contrib/fish-completion/terraform.fish b/vendor/github.com/hashicorp/terraform/contrib/fish-completion/terraform.fish deleted file mode 100644 index 0c564623..00000000 --- a/vendor/github.com/hashicorp/terraform/contrib/fish-completion/terraform.fish +++ /dev/null @@ -1,170 +0,0 @@ -# general options -complete -f -c terraform -l version -d 'Print version information' -complete -f -c terraform -l help -d 'Show help' - -### apply -complete -f -c terraform -n '__fish_use_subcommand' -a apply -d 'Build or change infrastructure' -complete -f -c terraform -n '__fish_seen_subcommand_from apply' -o backup -d 'Path to backup the existing state file' -complete -f -c terraform -n '__fish_seen_subcommand_from apply' -o lock -d 'Lock the state file when locking is supported' -complete -f -c terraform -n '__fish_seen_subcommand_from apply' -o lock-timeout -d 'Duration to retry a state lock' -complete -f -c terraform -n '__fish_seen_subcommand_from apply' -o input -d 'Ask for input for variables if not directly set' -complete -f -c terraform -n '__fish_seen_subcommand_from apply' -o no-color -d 'If specified, output won\'t contain any color' -complete -f -c terraform -n '__fish_seen_subcommand_from apply' -o parallelism -d 'Limit the number of concurrent operations' -complete -f -c terraform -n '__fish_seen_subcommand_from apply' -o refresh -d 'Update state prior to checking for differences' -complete -f -c terraform -n '__fish_seen_subcommand_from apply' -o state -d 'Path to a Terraform state file' -complete -f -c terraform -n '__fish_seen_subcommand_from apply' -o state-out -d 'Path to write state' -complete -f -c terraform -n '__fish_seen_subcommand_from apply' -o target -d 'Resource to target' -complete -f -c terraform -n '__fish_seen_subcommand_from apply' -o var -d 'Set a variable in the Terraform configuration' -complete -f -c terraform -n '__fish_seen_subcommand_from apply' -o var-file -d 'Set variables from a file' - -### console -complete -f -c terraform -n '__fish_use_subcommand' -a console -d 'Interactive console for Terraform interpolations' -complete -f -c terraform -n '__fish_seen_subcommand_from console' -o state -d 'Path to a Terraform state file' -complete -f -c terraform -n '__fish_seen_subcommand_from console' -o var -d 'Set a variable in the Terraform configuration' -complete -f -c terraform -n '__fish_seen_subcommand_from console' -o var-file -d 'Set variables from a file' - -### destroy -complete -f -c terraform -n '__fish_use_subcommand' -a destroy -d 'Destroy Terraform-managed infrastructure' -complete -f -c terraform -n '__fish_seen_subcommand_from destroy' -o backup -d 'Path to backup the existing state file' -complete -f -c terraform -n '__fish_seen_subcommand_from destroy' -o force -d 'Don\'t ask for input for destroy confirmation' -complete -f -c terraform -n '__fish_seen_subcommand_from destroy' -o lock -d 'Lock the state file when locking is supported' -complete -f -c terraform -n '__fish_seen_subcommand_from destroy' -o lock-timeout -d 'Duration to retry a state lock' -complete -f -c terraform -n '__fish_seen_subcommand_from destroy' -o no-color -d 'If specified, output won\'t contain any color' -complete -f -c terraform -n '__fish_seen_subcommand_from destroy' -o parallelism -d 'Limit the number of concurrent operations' -complete -f -c terraform -n '__fish_seen_subcommand_from destroy' -o refresh -d 'Update state prior to checking for differences' -complete -f -c terraform -n '__fish_seen_subcommand_from destroy' -o state -d 'Path to a Terraform state file' -complete -f -c terraform -n '__fish_seen_subcommand_from destroy' -o state-out -d 'Path to write state' -complete -f -c terraform -n '__fish_seen_subcommand_from destroy' -o target -d 'Resource to target' -complete -f -c terraform -n '__fish_seen_subcommand_from destroy' -o var -d 'Set a variable in the Terraform configuration' -complete -f -c terraform -n '__fish_seen_subcommand_from destroy' -o var-file -d 'Set variables from a file' - -### env -complete -f -c terraform -n '__fish_use_subcommand' -a env -d 'Environment management' -complete -f -c terraform -n '__fish_seen_subcommand_from env' -a list -d 'List environments' -complete -f -c terraform -n '__fish_seen_subcommand_from env' -a select -d 'Select an environment' -complete -f -c terraform -n '__fish_seen_subcommand_from env' -a new -d 'Create a new environment' -complete -f -c terraform -n '__fish_seen_subcommand_from env' -a delete -d 'Delete an existing environment' - -### fmt -complete -f -c terraform -n '__fish_use_subcommand' -a fmt -d 'Rewrite config files to canonical format' -complete -f -c terraform -n '__fish_seen_subcommand_from fmt' -o list -d 'List files whose formatting differs' -complete -f -c terraform -n '__fish_seen_subcommand_from fmt' -o write -d 'Write result to source file' -complete -f -c terraform -n '__fish_seen_subcommand_from fmt' -o diff -d 'Display diffs of formatting changes' - -### get -complete -f -c terraform -n '__fish_use_subcommand' -a get -d 'Download and install modules for the configuration' -complete -f -c terraform -n '__fish_seen_subcommand_from get' -o update -d 'Check modules for updates' -complete -f -c terraform -n '__fish_seen_subcommand_from get' -o no-color -d 'If specified, output won\'t contain any color' - -### graph -complete -f -c terraform -n '__fish_use_subcommand' -a graph -d 'Create a visual graph of Terraform resources' -complete -f -c terraform -n '__fish_seen_subcommand_from graph' -o draw-cycles -d 'Highlight any cycles in the graph' -complete -f -c terraform -n '__fish_seen_subcommand_from graph' -o module-depth -d 'Depth of modules to show in the output' -complete -f -c terraform -n '__fish_seen_subcommand_from graph' -o no-color -d 'If specified, output won\'t contain any color' -complete -f -c terraform -n '__fish_seen_subcommand_from graph' -o type -d 'Type of graph to output' - -### import -complete -f -c terraform -n '__fish_use_subcommand' -a import -d 'Import existing infrastructure into Terraform' -complete -f -c terraform -n '__fish_seen_subcommand_from import' -o backup -d 'Path to backup the existing state file' -complete -f -c terraform -n '__fish_seen_subcommand_from import' -o config -d 'Path to a directory of configuration files' -complete -f -c terraform -n '__fish_seen_subcommand_from import' -o input -d 'Ask for input for variables if not directly set' -complete -f -c terraform -n '__fish_seen_subcommand_from import' -o lock -d 'Lock the state file when locking is supported' -complete -f -c terraform -n '__fish_seen_subcommand_from import' -o lock-timeout -d 'Duration to retry a state lock' -complete -f -c terraform -n '__fish_seen_subcommand_from import' -o no-color -d 'If specified, output won\'t contain any color' -complete -f -c terraform -n '__fish_seen_subcommand_from import' -o provider -d 'Specific provider to use for import' -complete -f -c terraform -n '__fish_seen_subcommand_from import' -o state -d 'Path to a Terraform state file' -complete -f -c terraform -n '__fish_seen_subcommand_from import' -o state-out -d 'Path to write state' -complete -f -c terraform -n '__fish_seen_subcommand_from import' -o var -d 'Set a variable in the Terraform configuration' -complete -f -c terraform -n '__fish_seen_subcommand_from import' -o var-file -d 'Set variables from a file' - -### init -complete -f -c terraform -n '__fish_use_subcommand' -a init -d 'Initialize a new or existing Terraform configuration' -complete -f -c terraform -n '__fish_seen_subcommand_from init' -o backend -d 'Configure the backend for this environment' -complete -f -c terraform -n '__fish_seen_subcommand_from init' -o backend-config -d 'Backend configuration' -complete -f -c terraform -n '__fish_seen_subcommand_from init' -o get -d 'Download modules for this configuration' -complete -f -c terraform -n '__fish_seen_subcommand_from init' -o input -d 'Ask for input if necessary' -complete -f -c terraform -n '__fish_seen_subcommand_from init' -o lock -d 'Lock the state file when locking is supported' -complete -f -c terraform -n '__fish_seen_subcommand_from init' -o lock-timeout -d 'Duration to retry a state lock' -complete -f -c terraform -n '__fish_seen_subcommand_from init' -o no-color -d 'If specified, output won\'t contain any color' -complete -f -c terraform -n '__fish_seen_subcommand_from init' -o force-copy -d 'Suppress prompts about copying state data' - -### output -complete -f -c terraform -n '__fish_use_subcommand' -a output -d 'Read an output from a state file' -complete -f -c terraform -n '__fish_seen_subcommand_from output' -o state -d 'Path to the state file to read' -complete -f -c terraform -n '__fish_seen_subcommand_from output' -o no-color -d 'If specified, output won\'t contain any color' -complete -f -c terraform -n '__fish_seen_subcommand_from output' -o module -d 'Return the outputs for a specific module' -complete -f -c terraform -n '__fish_seen_subcommand_from output' -o json -d 'Print output in JSON format' - -### plan -complete -f -c terraform -n '__fish_use_subcommand' -a plan -d 'Generate and show an execution plan' -complete -f -c terraform -n '__fish_seen_subcommand_from plan' -o destroy -d 'Generate a plan to destroy all resources' -complete -f -c terraform -n '__fish_seen_subcommand_from plan' -o detailed-exitcode -d 'Return detailed exit codes' -complete -f -c terraform -n '__fish_seen_subcommand_from plan' -o input -d 'Ask for input for variables if not directly set' -complete -f -c terraform -n '__fish_seen_subcommand_from plan' -o lock -d 'Lock the state file when locking is supported' -complete -f -c terraform -n '__fish_seen_subcommand_from plan' -o lock-timeout -d 'Duration to retry a state lock' -complete -f -c terraform -n '__fish_seen_subcommand_from plan' -o no-color -d 'If specified, output won\'t contain any color' -complete -f -c terraform -n '__fish_seen_subcommand_from plan' -o out -d 'Write a plan file to the given path' -complete -f -c terraform -n '__fish_seen_subcommand_from plan' -o parallelism -d 'Limit the number of concurrent operations' -complete -f -c terraform -n '__fish_seen_subcommand_from plan' -o refresh -d 'Update state prior to checking for differences' -complete -f -c terraform -n '__fish_seen_subcommand_from plan' -o state -d 'Path to a Terraform state file' -complete -f -c terraform -n '__fish_seen_subcommand_from plan' -o target -d 'Resource to target' -complete -f -c terraform -n '__fish_seen_subcommand_from plan' -o var -d 'Set a variable in the Terraform configuration' -complete -f -c terraform -n '__fish_seen_subcommand_from plan' -o var-file -d 'Set variables from a file' - -### push -complete -f -c terraform -n '__fish_use_subcommand' -a push -d 'Upload this Terraform module to Atlas to run' -complete -f -c terraform -n '__fish_seen_subcommand_from push' -o atlas-address -d 'An alternate address to an Atlas instance' -complete -f -c terraform -n '__fish_seen_subcommand_from push' -o upload-modules -d 'Lock modules and upload completely' -complete -f -c terraform -n '__fish_seen_subcommand_from push' -o name -d 'Name of the configuration in Atlas' -complete -f -c terraform -n '__fish_seen_subcommand_from push' -o token -d 'Access token to use to upload' -complete -f -c terraform -n '__fish_seen_subcommand_from push' -o overwrite -d 'Variable keys that should overwrite values in Atlas' -complete -f -c terraform -n '__fish_seen_subcommand_from push' -o var -d 'Set a variable in the Terraform configuration' -complete -f -c terraform -n '__fish_seen_subcommand_from push' -o var-file -d 'Set variables from a file' -complete -f -c terraform -n '__fish_seen_subcommand_from push' -o vcs -d 'Upload only files committed to your VCS' -complete -f -c terraform -n '__fish_seen_subcommand_from push' -o no-color -d 'If specified, output won\'t contain any color' - -### refresh -complete -f -c terraform -n '__fish_use_subcommand' -a refresh -d 'Update local state file against real resources' -complete -f -c terraform -n '__fish_seen_subcommand_from refresh' -o backup -d 'Path to backup the existing state file' -complete -f -c terraform -n '__fish_seen_subcommand_from refresh' -o input -d 'Ask for input for variables if not directly set' -complete -f -c terraform -n '__fish_seen_subcommand_from refresh' -o lock -d 'Lock the state file when locking is supported' -complete -f -c terraform -n '__fish_seen_subcommand_from refresh' -o lock-timeout -d 'Duration to retry a state lock' -complete -f -c terraform -n '__fish_seen_subcommand_from refresh' -o no-color -d 'If specified, output won\'t contain any color' -complete -f -c terraform -n '__fish_seen_subcommand_from refresh' -o state -d 'Path to a Terraform state file' -complete -f -c terraform -n '__fish_seen_subcommand_from refresh' -o state-out -d 'Path to write state' -complete -f -c terraform -n '__fish_seen_subcommand_from refresh' -o target -d 'Resource to target' -complete -f -c terraform -n '__fish_seen_subcommand_from refresh' -o var -d 'Set a variable in the Terraform configuration' -complete -f -c terraform -n '__fish_seen_subcommand_from refresh' -o var-file -d 'Set variables from a file' - -### show -complete -f -c terraform -n '__fish_use_subcommand' -a show -d 'Inspect Terraform state or plan' -complete -f -c terraform -n '__fish_seen_subcommand_from show' -o no-color -d 'If specified, output won\'t contain any color' - -### taint -complete -f -c terraform -n '__fish_use_subcommand' -a taint -d 'Manually mark a resource for recreation' -complete -f -c terraform -n '__fish_seen_subcommand_from taint' -o allow-missing -d 'Succeed even if resource is missing' -complete -f -c terraform -n '__fish_seen_subcommand_from taint' -o backup -d 'Path to backup the existing state file' -complete -f -c terraform -n '__fish_seen_subcommand_from taint' -o lock -d 'Lock the state file when locking is supported' -complete -f -c terraform -n '__fish_seen_subcommand_from taint' -o lock-timeout -d 'Duration to retry a state lock' -complete -f -c terraform -n '__fish_seen_subcommand_from taint' -o module -d 'The module path where the resource lives' -complete -f -c terraform -n '__fish_seen_subcommand_from taint' -o no-color -d 'If specified, output won\'t contain any color' -complete -f -c terraform -n '__fish_seen_subcommand_from taint' -o state -d 'Path to a Terraform state file' -complete -f -c terraform -n '__fish_seen_subcommand_from taint' -o state-out -d 'Path to write state' - -### untaint -complete -f -c terraform -n '__fish_use_subcommand' -a untaint -d 'Manually unmark a resource as tainted' -complete -f -c terraform -n '__fish_seen_subcommand_from untaint' -o allow-missing -d 'Succeed even if resource is missing' -complete -f -c terraform -n '__fish_seen_subcommand_from untaint' -o backup -d 'Path to backup the existing state file' -complete -f -c terraform -n '__fish_seen_subcommand_from untaint' -o lock -d 'Lock the state file when locking is supported' -complete -f -c terraform -n '__fish_seen_subcommand_from untaint' -o lock-timeout -d 'Duration to retry a state lock' -complete -f -c terraform -n '__fish_seen_subcommand_from untaint' -o module -d 'The module path where the resource lives' -complete -f -c terraform -n '__fish_seen_subcommand_from untaint' -o no-color -d 'If specified, output won\'t contain any color' -complete -f -c terraform -n '__fish_seen_subcommand_from untaint' -o state -d 'Path to a Terraform state file' -complete -f -c terraform -n '__fish_seen_subcommand_from untaint' -o state-out -d 'Path to write state' - -### validate -complete -f -c terraform -n '__fish_use_subcommand' -a validate -d 'Validate the Terraform files' -complete -f -c terraform -n '__fish_seen_subcommand_from validate' -o no-color -d 'If specified, output won\'t contain any color' - -### version -complete -f -c terraform -n '__fish_use_subcommand' -a version -d 'Print the Terraform version' diff --git a/vendor/github.com/hashicorp/terraform/contrib/zsh-completion/README.md b/vendor/github.com/hashicorp/terraform/contrib/zsh-completion/README.md deleted file mode 100644 index 0f8e2e81..00000000 --- a/vendor/github.com/hashicorp/terraform/contrib/zsh-completion/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Terraform zsh completion - -## Install -```console -% terraform -install-autocomplete -``` - -## Uninstall -```console -% terraform -uninstall-autocomplete -``` diff --git a/vendor/github.com/hashicorp/terraform/dag/dag_test.go b/vendor/github.com/hashicorp/terraform/dag/dag_test.go index ae2c2387..90a9f75c 100644 --- a/vendor/github.com/hashicorp/terraform/dag/dag_test.go +++ b/vendor/github.com/hashicorp/terraform/dag/dag_test.go @@ -340,7 +340,7 @@ func BenchmarkDAG(b *testing.B) { // layer B for i := 0; i < count; i++ { B := fmt.Sprintf("B%d", i) - g.Add(fmt.Sprintf(B)) + g.Add(B) for j := 0; j < count; j++ { g.Connect(BasicEdge(B, fmt.Sprintf("A%d", j))) } @@ -349,7 +349,7 @@ func BenchmarkDAG(b *testing.B) { // layer C for i := 0; i < count; i++ { c := fmt.Sprintf("C%d", i) - g.Add(fmt.Sprintf(c)) + g.Add(c) for j := 0; j < count; j++ { // connect them to previous layers so we have something that requires reduction g.Connect(BasicEdge(c, fmt.Sprintf("A%d", j))) @@ -360,7 +360,7 @@ func BenchmarkDAG(b *testing.B) { // layer D for i := 0; i < count; i++ { d := fmt.Sprintf("D%d", i) - g.Add(fmt.Sprintf(d)) + g.Add(d) for j := 0; j < count; j++ { g.Connect(BasicEdge(d, fmt.Sprintf("A%d", j))) g.Connect(BasicEdge(d, fmt.Sprintf("B%d", j))) diff --git a/vendor/github.com/hashicorp/terraform/dag/graph.go b/vendor/github.com/hashicorp/terraform/dag/graph.go index 1d054435..222ac078 100644 --- a/vendor/github.com/hashicorp/terraform/dag/graph.go +++ b/vendor/github.com/hashicorp/terraform/dag/graph.go @@ -337,7 +337,7 @@ func VertexName(raw Vertex) string { case NamedVertex: return v.Name() case fmt.Stringer: - return fmt.Sprintf("%s", v) + return v.String() default: return fmt.Sprintf("%v", v) } diff --git a/vendor/github.com/hashicorp/terraform/dag/marshal.go b/vendor/github.com/hashicorp/terraform/dag/marshal.go index 0ad45e8c..0ba52152 100644 --- a/vendor/github.com/hashicorp/terraform/dag/marshal.go +++ b/vendor/github.com/hashicorp/terraform/dag/marshal.go @@ -7,18 +7,6 @@ import ( "strconv" ) -const ( - typeOperation = "Operation" - typeTransform = "Transform" - typeWalk = "Walk" - typeDepthFirstWalk = "DepthFirstWalk" - typeReverseDepthFirstWalk = "ReverseDepthFirstWalk" - typeTransitiveReduction = "TransitiveReduction" - typeEdgeInfo = "EdgeInfo" - typeVertexInfo = "VertexInfo" - typeVisitInfo = "VisitInfo" -) - // the marshal* structs are for serialization of the graph data. type marshalGraph struct { // Type is always "Graph", for identification as a top level object in the @@ -49,36 +37,6 @@ type marshalGraph struct { Cycles [][]*marshalVertex `json:",omitempty"` } -// The add, remove, connect, removeEdge methods mirror the basic Graph -// manipulations to reconstruct a marshalGraph from a debug log. -func (g *marshalGraph) add(v *marshalVertex) { - g.Vertices = append(g.Vertices, v) - sort.Sort(vertices(g.Vertices)) -} - -func (g *marshalGraph) remove(v *marshalVertex) { - for i, existing := range g.Vertices { - if v.ID == existing.ID { - g.Vertices = append(g.Vertices[:i], g.Vertices[i+1:]...) - return - } - } -} - -func (g *marshalGraph) connect(e *marshalEdge) { - g.Edges = append(g.Edges, e) - sort.Sort(edges(g.Edges)) -} - -func (g *marshalGraph) removeEdge(e *marshalEdge) { - for i, existing := range g.Edges { - if e.Source == existing.Source && e.Target == existing.Target { - g.Edges = append(g.Edges[:i], g.Edges[i+1:]...) - return - } - } -} - func (g *marshalGraph) vertexByID(id string) *marshalVertex { for _, v := range g.Vertices { if id == v.ID { diff --git a/vendor/github.com/hashicorp/terraform/dag/set.go b/vendor/github.com/hashicorp/terraform/dag/set.go index c5c1af12..390415e3 100644 --- a/vendor/github.com/hashicorp/terraform/dag/set.go +++ b/vendor/github.com/hashicorp/terraform/dag/set.go @@ -38,33 +38,32 @@ func (s Set) Include(v interface{}) bool { // Intersection computes the set intersection with other. func (s Set) Intersection(other Set) Set { result := make(Set) - if s == nil { + if s == nil || other == nil { return result } - if other != nil { - for _, v := range s { - if other.Include(v) { - result.Add(v) - } + // Iteration over a smaller set has better performance. + if other.Len() < s.Len() { + s, other = other, s + } + for _, v := range s { + if other.Include(v) { + result.Add(v) } } - return result } // Difference returns a set with the elements that s has but // other doesn't. func (s Set) Difference(other Set) Set { + if other == nil || other.Len() == 0 { + return s.Copy() + } + result := make(Set) - if s != nil { - for k, v := range s { - var ok bool - if other != nil { - _, ok = other[k] - } - if !ok { - result.Add(v) - } + for k, v := range s { + if _, ok := other[k]; !ok { + result.Add(v) } } @@ -106,7 +105,7 @@ func (s Set) List() []interface{} { // Copy returns a shallow copy of the set. func (s Set) Copy() Set { - c := make(Set) + c := make(Set, len(s)) for k, v := range s { c[k] = v } diff --git a/vendor/github.com/hashicorp/terraform/dag/set_test.go b/vendor/github.com/hashicorp/terraform/dag/set_test.go index 36bd6a65..721f2467 100644 --- a/vendor/github.com/hashicorp/terraform/dag/set_test.go +++ b/vendor/github.com/hashicorp/terraform/dag/set_test.go @@ -31,6 +31,12 @@ func TestSetDifference(t *testing.T) { []interface{}{3, 2, 1, 4}, []interface{}{}, }, + { + "B is nil", + []interface{}{1, 2, 3}, + nil, + []interface{}{1, 2, 3}, + }, } for i, tc := range cases { @@ -44,6 +50,9 @@ func TestSetDifference(t *testing.T) { for _, v := range tc.B { two.Add(v) } + if tc.B == nil { + two = nil + } for _, v := range tc.Expected { expected.Add(v) } @@ -119,3 +128,31 @@ func TestSetCopy(t *testing.T) { } } + +func makeSet(n int) Set { + ret := make(Set, n) + for i := 0; i < n; i++ { + ret.Add(i) + } + return ret +} + +func BenchmarkSetIntersection_100_100000(b *testing.B) { + small := makeSet(100) + large := makeSet(100000) + + b.ResetTimer() + for n := 0; n < b.N; n++ { + small.Intersection(large) + } +} + +func BenchmarkSetIntersection_100000_100(b *testing.B) { + small := makeSet(100) + large := makeSet(100000) + + b.ResetTimer() + for n := 0; n < b.N; n++ { + large.Intersection(small) + } +} diff --git a/vendor/github.com/hashicorp/terraform/dag/walk.go b/vendor/github.com/hashicorp/terraform/dag/walk.go index f9fdf2df..26b24923 100644 --- a/vendor/github.com/hashicorp/terraform/dag/walk.go +++ b/vendor/github.com/hashicorp/terraform/dag/walk.go @@ -106,11 +106,6 @@ type walkerVertex struct { depsCancelCh chan struct{} } -// errWalkUpstream is used in the errMap of a walk to note that an upstream -// dependency failed so this vertex wasn't run. This is not shown in the final -// user-returned error. -var errWalkUpstream = errors.New("upstream dependency failed") - // Wait waits for the completion of the walk and returns diagnostics describing // any problems that arose. Update should be called to populate the walk with // vertices and edges prior to calling this. @@ -303,7 +298,7 @@ func (w *Walker) Update(g *AcyclicGraph) { } // Start all the new vertices. We do this at the end so that all - // the edge waiters and changes are setup above. + // the edge waiters and changes are set up above. for _, raw := range newVerts { v := raw.(Vertex) go w.walkVertex(v, w.vertexMap[v]) diff --git a/vendor/github.com/hashicorp/terraform/digraph/basic.go b/vendor/github.com/hashicorp/terraform/digraph/basic.go deleted file mode 100644 index 8dc76838..00000000 --- a/vendor/github.com/hashicorp/terraform/digraph/basic.go +++ /dev/null @@ -1,89 +0,0 @@ -package digraph - -import ( - "fmt" - "strings" -) - -// BasicNode is a digraph Node that has a name and out edges -type BasicNode struct { - Name string - NodeEdges []Edge -} - -func (b *BasicNode) Edges() []Edge { - return b.NodeEdges -} - -func (b *BasicNode) AddEdge(edge Edge) { - b.NodeEdges = append(b.NodeEdges, edge) -} - -func (b *BasicNode) String() string { - if b.Name == "" { - return "Node" - } - return fmt.Sprintf("%v", b.Name) -} - -// BasicEdge is a digraph Edge that has a name, head and tail -type BasicEdge struct { - Name string - EdgeHead *BasicNode - EdgeTail *BasicNode -} - -func (b *BasicEdge) Head() Node { - return b.EdgeHead -} - -// Tail returns the end point of the Edge -func (b *BasicEdge) Tail() Node { - return b.EdgeTail -} - -func (b *BasicEdge) String() string { - if b.Name == "" { - return "Edge" - } - return fmt.Sprintf("%v", b.Name) -} - -// ParseBasic is used to parse a string in the format of: -// a -> b ; edge name -// b -> c -// Into a series of basic node and basic edges -func ParseBasic(s string) map[string]*BasicNode { - lines := strings.Split(s, "\n") - nodes := make(map[string]*BasicNode) - for _, line := range lines { - var edgeName string - if idx := strings.Index(line, ";"); idx >= 0 { - edgeName = strings.Trim(line[idx+1:], " \t\r\n") - line = line[:idx] - } - parts := strings.SplitN(line, "->", 2) - if len(parts) != 2 { - continue - } - head_name := strings.Trim(parts[0], " \t\r\n") - tail_name := strings.Trim(parts[1], " \t\r\n") - head := nodes[head_name] - if head == nil { - head = &BasicNode{Name: head_name} - nodes[head_name] = head - } - tail := nodes[tail_name] - if tail == nil { - tail = &BasicNode{Name: tail_name} - nodes[tail_name] = tail - } - edge := &BasicEdge{ - Name: edgeName, - EdgeHead: head, - EdgeTail: tail, - } - head.AddEdge(edge) - } - return nodes -} diff --git a/vendor/github.com/hashicorp/terraform/digraph/basic_test.go b/vendor/github.com/hashicorp/terraform/digraph/basic_test.go deleted file mode 100644 index 20584b09..00000000 --- a/vendor/github.com/hashicorp/terraform/digraph/basic_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package digraph - -import ( - "fmt" - "testing" -) - -func TestParseBasic(t *testing.T) { - spec := `a -> b ; first -b -> c ; second -b -> d ; third -z -> a` - nodes := ParseBasic(spec) - if len(nodes) != 5 { - t.Fatalf("bad: %v", nodes) - } - - a := nodes["a"] - if a.Name != "a" { - t.Fatalf("bad: %v", a) - } - aEdges := a.Edges() - if len(aEdges) != 1 { - t.Fatalf("bad: %v", a.Edges()) - } - if fmt.Sprintf("%v", aEdges[0]) != "first" { - t.Fatalf("bad: %v", aEdges[0]) - } - - b := nodes["b"] - if len(b.Edges()) != 2 { - t.Fatalf("bad: %v", b.Edges()) - } - - c := nodes["c"] - if len(c.Edges()) != 0 { - t.Fatalf("bad: %v", c.Edges()) - } - - d := nodes["d"] - if len(d.Edges()) != 0 { - t.Fatalf("bad: %v", d.Edges()) - } - - z := nodes["z"] - zEdges := z.Edges() - if len(zEdges) != 1 { - t.Fatalf("bad: %v", z.Edges()) - } - if fmt.Sprintf("%v", zEdges[0]) != "Edge" { - t.Fatalf("bad: %v", zEdges[0]) - } -} diff --git a/vendor/github.com/hashicorp/terraform/digraph/digraph.go b/vendor/github.com/hashicorp/terraform/digraph/digraph.go deleted file mode 100644 index ccf31117..00000000 --- a/vendor/github.com/hashicorp/terraform/digraph/digraph.go +++ /dev/null @@ -1,34 +0,0 @@ -package digraph - -// Digraph is used to represent a Directed Graph. This means -// we have a set of nodes, and a set of edges which are directed -// from a source and towards a destination -type Digraph interface { - // Nodes provides all the nodes in the graph - Nodes() []Node - - // Sources provides all the source nodes in the graph - Sources() []Node - - // Sinks provides all the sink nodes in the graph - Sinks() []Node - - // Transpose reverses the edge directions and returns - // a new Digraph - Transpose() Digraph -} - -// Node represents a vertex in a Digraph -type Node interface { - // Edges returns the out edges for a given nod - Edges() []Edge -} - -// Edge represents a directed edge in a Digraph -type Edge interface { - // Head returns the start point of the Edge - Head() Node - - // Tail returns the end point of the Edge - Tail() Node -} diff --git a/vendor/github.com/hashicorp/terraform/digraph/graphviz.go b/vendor/github.com/hashicorp/terraform/digraph/graphviz.go deleted file mode 100644 index db6952eb..00000000 --- a/vendor/github.com/hashicorp/terraform/digraph/graphviz.go +++ /dev/null @@ -1,28 +0,0 @@ -package digraph - -import ( - "fmt" - "io" -) - -// WriteDot is used to emit a GraphViz compatible definition -// for a directed graph. It can be used to dump a .dot file. -func WriteDot(w io.Writer, nodes []Node) error { - w.Write([]byte("digraph {\n")) - defer w.Write([]byte("}\n")) - - for _, n := range nodes { - nodeLine := fmt.Sprintf("\t\"%s\";\n", n) - - w.Write([]byte(nodeLine)) - - for _, edge := range n.Edges() { - target := edge.Tail() - line := fmt.Sprintf("\t\"%s\" -> \"%s\" [label=\"%s\"];\n", - n, target, edge) - w.Write([]byte(line)) - } - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/digraph/graphviz_test.go b/vendor/github.com/hashicorp/terraform/digraph/graphviz_test.go deleted file mode 100644 index 69e4ebb8..00000000 --- a/vendor/github.com/hashicorp/terraform/digraph/graphviz_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package digraph - -import ( - "bytes" - "strings" - "testing" -) - -func TestWriteDot(t *testing.T) { - nodes := ParseBasic(`a -> b ; foo -a -> c -b -> d -b -> e -`) - var nlist []Node - for _, n := range nodes { - nlist = append(nlist, n) - } - - buf := bytes.NewBuffer(nil) - if err := WriteDot(buf, nlist); err != nil { - t.Fatalf("err: %s", err) - } - - actual := strings.TrimSpace(string(buf.Bytes())) - expected := strings.TrimSpace(writeDotStr) - - actualLines := strings.Split(actual, "\n") - expectedLines := strings.Split(expected, "\n") - - if actualLines[0] != expectedLines[0] || - actualLines[len(actualLines)-1] != expectedLines[len(expectedLines)-1] || - len(actualLines) != len(expectedLines) { - t.Fatalf("bad: %s", actual) - } - - count := 0 - for _, el := range expectedLines[1 : len(expectedLines)-1] { - for _, al := range actualLines[1 : len(actualLines)-1] { - if el == al { - count++ - break - } - } - } - - if count != len(expectedLines)-2 { - t.Fatalf("bad: %s", actual) - } -} - -const writeDotStr = ` -digraph { - "a"; - "a" -> "b" [label="foo"]; - "a" -> "c" [label="Edge"]; - "b"; - "b" -> "d" [label="Edge"]; - "b" -> "e" [label="Edge"]; - "c"; - "d"; - "e"; -} -` diff --git a/vendor/github.com/hashicorp/terraform/digraph/tarjan.go b/vendor/github.com/hashicorp/terraform/digraph/tarjan.go deleted file mode 100644 index 2298610e..00000000 --- a/vendor/github.com/hashicorp/terraform/digraph/tarjan.go +++ /dev/null @@ -1,111 +0,0 @@ -package digraph - -// sccAcct is used ot pass around accounting information for -// the StronglyConnectedComponents algorithm -type sccAcct struct { - ExcludeSingle bool - NextIndex int - NodeIndex map[Node]int - Stack []Node - SCC [][]Node -} - -// visit assigns an index and pushes a node onto the stack -func (s *sccAcct) visit(n Node) int { - idx := s.NextIndex - s.NodeIndex[n] = idx - s.NextIndex++ - s.push(n) - return idx -} - -// push adds a node to the stack -func (s *sccAcct) push(n Node) { - s.Stack = append(s.Stack, n) -} - -// pop removes a node from the stack -func (s *sccAcct) pop() Node { - n := len(s.Stack) - if n == 0 { - return nil - } - node := s.Stack[n-1] - s.Stack = s.Stack[:n-1] - return node -} - -// inStack checks if a node is in the stack -func (s *sccAcct) inStack(needle Node) bool { - for _, n := range s.Stack { - if n == needle { - return true - } - } - return false -} - -// StronglyConnectedComponents implements Tarjan's algorithm to -// find all the strongly connected components in a graph. This can -// be used to detected any cycles in a graph, as well as which nodes -// partipate in those cycles. excludeSingle is used to exclude strongly -// connected components of size one. -func StronglyConnectedComponents(nodes []Node, excludeSingle bool) [][]Node { - acct := sccAcct{ - ExcludeSingle: excludeSingle, - NextIndex: 1, - NodeIndex: make(map[Node]int, len(nodes)), - } - for _, node := range nodes { - // Recurse on any non-visited nodes - if acct.NodeIndex[node] == 0 { - stronglyConnected(&acct, node) - } - } - return acct.SCC -} - -func stronglyConnected(acct *sccAcct, node Node) int { - // Initial node visit - index := acct.visit(node) - minIdx := index - - for _, edge := range node.Edges() { - target := edge.Tail() - targetIdx := acct.NodeIndex[target] - - // Recurse on successor if not yet visited - if targetIdx == 0 { - minIdx = min(minIdx, stronglyConnected(acct, target)) - - } else if acct.inStack(target) { - // Check if the node is in the stack - minIdx = min(minIdx, targetIdx) - } - } - - // Pop the strongly connected components off the stack if - // this is a root node - if index == minIdx { - var scc []Node - for { - n := acct.pop() - scc = append(scc, n) - if n == node { - break - } - } - if !(acct.ExcludeSingle && len(scc) == 1) { - acct.SCC = append(acct.SCC, scc) - } - } - - return minIdx -} - -func min(a, b int) int { - if a <= b { - return a - } - return b -} diff --git a/vendor/github.com/hashicorp/terraform/digraph/tarjan_test.go b/vendor/github.com/hashicorp/terraform/digraph/tarjan_test.go deleted file mode 100644 index d14a75ec..00000000 --- a/vendor/github.com/hashicorp/terraform/digraph/tarjan_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package digraph - -import ( - "reflect" - "sort" - "testing" -) - -func TestStronglyConnectedComponents(t *testing.T) { - nodes := ParseBasic(`a -> b -a -> c -b -> c -c -> b -c -> d -d -> e`) - var nlist []Node - for _, n := range nodes { - nlist = append(nlist, n) - } - - sccs := StronglyConnectedComponents(nlist, false) - if len(sccs) != 4 { - t.Fatalf("bad: %v", sccs) - } - - sccs = StronglyConnectedComponents(nlist, true) - if len(sccs) != 1 { - t.Fatalf("bad: %v", sccs) - } - - cycle := sccs[0] - if len(cycle) != 2 { - t.Fatalf("bad: %v", sccs) - } - - cycleNodes := make([]string, len(cycle)) - for i, c := range cycle { - cycleNodes[i] = c.(*BasicNode).Name - } - sort.Strings(cycleNodes) - - expected := []string{"b", "c"} - if !reflect.DeepEqual(cycleNodes, expected) { - t.Fatalf("bad: %#v", cycleNodes) - } -} - -func TestStronglyConnectedComponents2(t *testing.T) { - nodes := ParseBasic(`a -> b -a -> c -b -> d -b -> e -c -> f -c -> g -g -> a -`) - var nlist []Node - for _, n := range nodes { - nlist = append(nlist, n) - } - - sccs := StronglyConnectedComponents(nlist, true) - if len(sccs) != 1 { - t.Fatalf("bad: %v", sccs) - } - - cycle := sccs[0] - if len(cycle) != 3 { - t.Fatalf("bad: %v", sccs) - } - - cycleNodes := make([]string, len(cycle)) - for i, c := range cycle { - cycleNodes[i] = c.(*BasicNode).Name - } - sort.Strings(cycleNodes) - - expected := []string{"a", "c", "g"} - if !reflect.DeepEqual(cycleNodes, expected) { - t.Fatalf("bad: %#v", cycleNodes) - } -} diff --git a/vendor/github.com/hashicorp/terraform/digraph/util.go b/vendor/github.com/hashicorp/terraform/digraph/util.go deleted file mode 100644 index 96a09ed8..00000000 --- a/vendor/github.com/hashicorp/terraform/digraph/util.go +++ /dev/null @@ -1,113 +0,0 @@ -package digraph - -// DepthFirstWalk performs a depth-first traversal of the nodes -// that can be reached from the initial input set. The callback is -// invoked for each visited node, and may return false to prevent -// vising any children of the current node -func DepthFirstWalk(node Node, cb func(n Node) bool) { - frontier := []Node{node} - seen := make(map[Node]struct{}) - for len(frontier) > 0 { - // Pop the current node - n := len(frontier) - current := frontier[n-1] - frontier = frontier[:n-1] - - // Check for potential cycle - if _, ok := seen[current]; ok { - continue - } - seen[current] = struct{}{} - - // Visit with the callback - if !cb(current) { - continue - } - - // Add any new edges to visit, in reverse order - edges := current.Edges() - for i := len(edges) - 1; i >= 0; i-- { - frontier = append(frontier, edges[i].Tail()) - } - } -} - -// FilterDegree returns only the nodes with the desired -// degree. This can be used with OutDegree or InDegree -func FilterDegree(degree int, degrees map[Node]int) []Node { - var matching []Node - for n, d := range degrees { - if d == degree { - matching = append(matching, n) - } - } - return matching -} - -// InDegree is used to compute the in-degree of nodes -func InDegree(nodes []Node) map[Node]int { - degree := make(map[Node]int, len(nodes)) - for _, n := range nodes { - if _, ok := degree[n]; !ok { - degree[n] = 0 - } - for _, e := range n.Edges() { - degree[e.Tail()]++ - } - } - return degree -} - -// OutDegree is used to compute the in-degree of nodes -func OutDegree(nodes []Node) map[Node]int { - degree := make(map[Node]int, len(nodes)) - for _, n := range nodes { - degree[n] = len(n.Edges()) - } - return degree -} - -// Sinks is used to get the nodes with out-degree of 0 -func Sinks(nodes []Node) []Node { - return FilterDegree(0, OutDegree(nodes)) -} - -// Sources is used to get the nodes with in-degree of 0 -func Sources(nodes []Node) []Node { - return FilterDegree(0, InDegree(nodes)) -} - -// Unreachable starts at a given start node, performs -// a DFS from there, and returns the set of unreachable nodes. -func Unreachable(start Node, nodes []Node) []Node { - // DFS from the start ndoe - frontier := []Node{start} - seen := make(map[Node]struct{}) - for len(frontier) > 0 { - // Pop the current node - n := len(frontier) - current := frontier[n-1] - frontier = frontier[:n-1] - - // Check for potential cycle - if _, ok := seen[current]; ok { - continue - } - seen[current] = struct{}{} - - // Add any new edges to visit, in reverse order - edges := current.Edges() - for i := len(edges) - 1; i >= 0; i-- { - frontier = append(frontier, edges[i].Tail()) - } - } - - // Check for any unseen nodes - var unseen []Node - for _, node := range nodes { - if _, ok := seen[node]; !ok { - unseen = append(unseen, node) - } - } - return unseen -} diff --git a/vendor/github.com/hashicorp/terraform/digraph/util_test.go b/vendor/github.com/hashicorp/terraform/digraph/util_test.go deleted file mode 100644 index e6d35999..00000000 --- a/vendor/github.com/hashicorp/terraform/digraph/util_test.go +++ /dev/null @@ -1,233 +0,0 @@ -package digraph - -import ( - "reflect" - "testing" -) - -func TestDepthFirstWalk(t *testing.T) { - nodes := ParseBasic(`a -> b -a -> c -a -> d -b -> e -d -> f -e -> a ; cycle`) - root := nodes["a"] - expected := []string{ - "a", - "b", - "e", - "c", - "d", - "f", - } - index := 0 - DepthFirstWalk(root, func(n Node) bool { - name := n.(*BasicNode).Name - if expected[index] != name { - t.Fatalf("expected: %v, got %v", expected[index], name) - } - index++ - return true - }) -} - -func TestInDegree(t *testing.T) { - nodes := ParseBasic(`a -> b -a -> c -a -> d -b -> e -c -> e -d -> f`) - var nlist []Node - for _, n := range nodes { - nlist = append(nlist, n) - } - - expected := map[string]int{ - "a": 0, - "b": 1, - "c": 1, - "d": 1, - "e": 2, - "f": 1, - } - indegree := InDegree(nlist) - for n, d := range indegree { - name := n.(*BasicNode).Name - exp := expected[name] - if exp != d { - t.Fatalf("Expected %d for %s, got %d", - exp, name, d) - } - } -} - -func TestOutDegree(t *testing.T) { - nodes := ParseBasic(`a -> b -a -> c -a -> d -b -> e -c -> e -d -> f`) - var nlist []Node - for _, n := range nodes { - nlist = append(nlist, n) - } - - expected := map[string]int{ - "a": 3, - "b": 1, - "c": 1, - "d": 1, - "e": 0, - "f": 0, - } - outDegree := OutDegree(nlist) - for n, d := range outDegree { - name := n.(*BasicNode).Name - exp := expected[name] - if exp != d { - t.Fatalf("Expected %d for %s, got %d", - exp, name, d) - } - } -} - -func TestSinks(t *testing.T) { - nodes := ParseBasic(`a -> b -a -> c -a -> d -b -> e -c -> e -d -> f`) - var nlist []Node - for _, n := range nodes { - nlist = append(nlist, n) - } - - sinks := Sinks(nlist) - - var haveE, haveF bool - for _, n := range sinks { - name := n.(*BasicNode).Name - switch name { - case "e": - haveE = true - case "f": - haveF = true - } - } - if !haveE || !haveF { - t.Fatalf("missing sink") - } -} - -func TestSources(t *testing.T) { - nodes := ParseBasic(`a -> b -a -> c -a -> d -b -> e -c -> e -d -> f -x -> y`) - var nlist []Node - for _, n := range nodes { - nlist = append(nlist, n) - } - - sources := Sources(nlist) - if len(sources) != 2 { - t.Fatalf("bad: %v", sources) - } - - var haveA, haveX bool - for _, n := range sources { - name := n.(*BasicNode).Name - switch name { - case "a": - haveA = true - case "x": - haveX = true - } - } - if !haveA || !haveX { - t.Fatalf("missing source %v %v", haveA, haveX) - } -} - -func TestUnreachable(t *testing.T) { - nodes := ParseBasic(`a -> b -a -> c -a -> d -b -> e -c -> e -d -> f -f -> a -x -> y -y -> z`) - var nlist []Node - for _, n := range nodes { - nlist = append(nlist, n) - } - - unreached := Unreachable(nodes["a"], nlist) - if len(unreached) != 3 { - t.Fatalf("bad: %v", unreached) - } - - var haveX, haveY, haveZ bool - for _, n := range unreached { - name := n.(*BasicNode).Name - switch name { - case "x": - haveX = true - case "y": - haveY = true - case "z": - haveZ = true - } - } - if !haveX || !haveY || !haveZ { - t.Fatalf("missing %v %v %v", haveX, haveY, haveZ) - } -} - -func TestUnreachable2(t *testing.T) { - nodes := ParseBasic(`a -> b -a -> c -a -> d -b -> e -c -> e -d -> f -f -> a -x -> y -y -> z`) - var nlist []Node - for _, n := range nodes { - nlist = append(nlist, n) - } - - unreached := Unreachable(nodes["x"], nlist) - if len(unreached) != 6 { - t.Fatalf("bad: %v", unreached) - } - - expected := map[string]struct{}{ - "a": struct{}{}, - "b": struct{}{}, - "c": struct{}{}, - "d": struct{}{}, - "e": struct{}{}, - "f": struct{}{}, - } - out := map[string]struct{}{} - for _, n := range unreached { - name := n.(*BasicNode).Name - out[name] = struct{}{} - } - - if !reflect.DeepEqual(out, expected) { - t.Fatalf("bad: %v %v", out, expected) - } -} diff --git a/vendor/github.com/hashicorp/terraform/docs/README.md b/vendor/github.com/hashicorp/terraform/docs/README.md index 76e36599..2ed0af4b 100644 --- a/vendor/github.com/hashicorp/terraform/docs/README.md +++ b/vendor/github.com/hashicorp/terraform/docs/README.md @@ -4,7 +4,7 @@ This directory contains some documentation about the Terraform Core codebase, aimed at readers who are interested in making code contributions. If you're looking for information on _using_ Terraform, please instead refer -to [the main Terraform CLI documentation](https://www.terraform.io/docs/cli-index.html). +to [the main Terraform CLI documentation](https://www.terraform.io/docs/cli/index.html). ## Terraform Core Architecture Documents diff --git a/vendor/github.com/hashicorp/terraform/docs/architecture.md b/vendor/github.com/hashicorp/terraform/docs/architecture.md index 9f1a3d6d..6724f02f 100644 --- a/vendor/github.com/hashicorp/terraform/docs/architecture.md +++ b/vendor/github.com/hashicorp/terraform/docs/architecture.md @@ -23,7 +23,7 @@ in more detail in a corresponding section below. Each time a user runs the `terraform` program, aside from some initial bootstrapping in the root package (not shown in the diagram) execution transfers immediately into one of the "command" implementations in -[the `command` package](https://godoc.org/github.com/hashicorp/terraform/command). +[the `command` package](https://pkg.go.dev/github.com/hashicorp/terraform/command). The mapping between the user-facing command names and their corresponding `command` package types can be found in the `commands.go` file in the root of the repository. @@ -35,7 +35,7 @@ but it applies to the main Terraform workflow commands `terraform plan` and For these commands, the role of the command implementation is to read and parse any command line arguments, command line options, and environment variables that are needed for the given command and use them to produce a -[`backend.Operation`](https://godoc.org/github.com/hashicorp/terraform/backend#Operation) +[`backend.Operation`](https://pkg.go.dev/github.com/hashicorp/terraform/backend#Operation) object that describes an action to be taken. An _operation_ consists of: @@ -52,18 +52,18 @@ An _operation_ consists of: The operation is then passed to the currently-selected [backend](https://www.terraform.io/docs/backends/index.html). Each backend name corresponds to an implementation of -[`backend.Backend`](https://godoc.org/github.com/hashicorp/terraform/backend#Backend), using a +[`backend.Backend`](https://pkg.go.dev/github.com/hashicorp/terraform/backend#Backend), using a mapping table in -[the `backend/init` package](https://godoc.org/github.com/hashicorp/terraform/backend/init). +[the `backend/init` package](https://pkg.go.dev/github.com/hashicorp/terraform/backend/init). Backends that are able to execute operations additionally implement -[`backend.Enhanced`](https://godoc.org/github.com/hashicorp/terraform/backend#Enhanced); +[`backend.Enhanced`](https://pkg.go.dev/github.com/hashicorp/terraform/backend#Enhanced); the command-handling code calls `Operation` with the operation it has constructed, and then the backend is responsible for executing that action. Most backends do _not_ implement this interface, and so the `command` package wraps these backends in an instance of -[`local.Local`](https://godoc.org/github.com/hashicorp/terraform/backend/local#Local), +[`local.Local`](https://pkg.go.dev/github.com/hashicorp/terraform/backend/local#Local), causing the operation to be executed locally within the `terraform` process itself, which (at the time of writing) is currently the only way an operation can be executed. @@ -85,19 +85,19 @@ elsewhere. To execute an operation locally, the `local` backend uses a _state manager_ (either -[`statemgr.Filesystem`](https://godoc.org/github.com/hashicorp/terraform/states/statemgr#Filesystem) if the +[`statemgr.Filesystem`](https://pkg.go.dev/github.com/hashicorp/terraform/states/statemgr#Filesystem) if the local backend is being used directly, or an implementation provided by whatever backend is being wrapped) to retrieve the current state for the workspace specified in the operation, then uses the _config loader_ to load and do initial processing/validation of the configuration specified in the operation. It then uses these, along with the other settings given in the operation, to construct a -[`terraform.Context`](https://godoc.org/github.com/hashicorp/terraform/terraform#Context), +[`terraform.Context`](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#Context), which is the main object that actually performs Terraform operations. The `local` backend finally calls an appropriate method on that context to begin execution of the relevant command, such as -[`Plan`](https://godoc.org/github.com/hashicorp/terraform/terraform#Context.Plan) +[`Plan`](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#Context.Plan) or [`Apply`](), which in turn constructs a graph using a _graph builder_, described in a later section. @@ -105,21 +105,21 @@ described in a later section. ## Configuration Loader The top-level configuration structure is represented by model types in -[package `configs`](https://godoc.org/github.com/hashicorp/terraform/configs). +[package `configs`](https://pkg.go.dev/github.com/hashicorp/terraform/configs). A whole configuration (the root module plus all of its descendent modules) is represented by -[`configs.Config`](https://godoc.org/github.com/hashicorp/terraform/configs#Config). +[`configs.Config`](https://pkg.go.dev/github.com/hashicorp/terraform/configs#Config). The `configs` package contains some low-level functionality for constructing configuration objects, but the main entry point is in the sub-package -[`configload`](https://godoc.org/github.com/hashicorp/terraform/configs/configload]), +[`configload`](https://pkg.go.dev/github.com/hashicorp/terraform/configs/configload]), via -[`configload.Loader`](https://godoc.org/github.com/hashicorp/terraform/configs/configload#Loader). +[`configload.Loader`](https://pkg.go.dev/github.com/hashicorp/terraform/configs/configload#Loader). A loader deals with all of the details of installing child modules (during `terraform init`) and then locating those modules again when a configuration is loaded by a backend. It takes the path to a root module and recursively loads all of the child modules to produce a single -[`configs.Config`](https://godoc.org/github.com/hashicorp/terraform/configs#Config) +[`configs.Config`](https://pkg.go.dev/github.com/hashicorp/terraform/configs#Config) representing the entire configuration. Terraform expects configuration files written in the Terraform language, which @@ -128,37 +128,37 @@ is a DSL built on top of cannot be interpreted until we build and walk the graph, since they depend on the outcome of other parts of the configuration, and so these parts of the configuration remain represented as the low-level HCL types -[hcl.Body](https://godoc.org/github.com/hashicorp/hcl/v2/hcl#Body) +[`hcl.Body`](https://pkg.go.dev/github.com/hashicorp/hcl/v2/#Body) and -[hcl.Expression](https://godoc.org/github.com/hashicorp/hcl/v2/hcl#Expression), +[`hcl.Expression`](https://pkg.go.dev/github.com/hashicorp/hcl/v2/#Expression), allowing Terraform to interpret them at a more appropriate time. ## State Manager A _state manager_ is responsible for storing and retrieving snapshots of the -[Terraform state](https://www.terraform.io/docs/state/index.html) +[Terraform state](https://www.terraform.io/docs/language/state/index.html) for a particular workspace. Each manager is an implementation of some combination of interfaces in -[the `statemgr` package](https://godoc.org/github.com/hashicorp/terraform/states/statemgr), +[the `statemgr` package](https://pkg.go.dev/github.com/hashicorp/terraform/states/statemgr), with most practical managers implementing the full set of operations described by -[`statemgr.Full`](https://godoc.org/github.com/hashicorp/terraform/states/statemgr#Full) +[`statemgr.Full`](https://pkg.go.dev/github.com/hashicorp/terraform/states/statemgr#Full) provided by a _backend_. The smaller interfaces exist primarily for use in other function signatures to be explicit about what actions the function might take on the state manager; there is little reason to write a state manager that does not implement all of `statemgr.Full`. The implementation -[`statemgr.Filesystem`](https://godoc.org/github.com/hashicorp/terraform/states/statemgr#Filesystem) is used +[`statemgr.Filesystem`](https://pkg.go.dev/github.com/hashicorp/terraform/states/statemgr#Filesystem) is used by default (by the `local` backend) and is responsible for the familiar `terraform.tfstate` local file that most Terraform users start with, before -they switch to [remote state](https://www.terraform.io/docs/state/remote.html). +they switch to [remote state](https://www.terraform.io/docs/language/state/remote.html). Other implementations of `statemgr.Full` are used to implement remote state. Each of these saves and retrieves state via a remote network service appropriate to the backend that creates it. A state manager accepts and returns a state snapshot as a -[`states.State`](https://godoc.org/github.com/hashicorp/terraform/states#State) +[`states.State`](https://pkg.go.dev/github.com/hashicorp/terraform/states#State) object. The state manager is responsible for exactly how that object is serialized and stored, but all state managers at the time of writing use the same JSON serialization format, storing the resulting JSON bytes in some @@ -167,7 +167,7 @@ kind of arbitrary blob store. ## Graph Builder A _graph builder_ is called by a -[`terraform.Context`](https://godoc.org/github.com/hashicorp/terraform/terraform#Context) +[`terraform.Context`](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#Context) method (e.g. `Plan` or `Apply`) to produce the graph that will be used to represent the necessary steps for that operation and the dependency relationships between them. @@ -177,9 +177,9 @@ In most cases, the graphs each represent a specific object in the configuration, or something derived from those configuration objects. For example, each `resource` block in the configuration has one corresponding -[`GraphNodeResource`](https://godoc.org/github.com/hashicorp/terraform/terraform#GraphNodeResource) +[`GraphNodeConfigResource`](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#GraphNodeConfigResource) vertex representing it in the "plan" graph. (Terraform Core uses terminology -inconsistently, describing graph vertices also as graph nodes in various +inconsistently, describing graph _vertices_ also as graph _nodes_ in various places. These both describe the same concept.) The [edges](https://en.wikipedia.org/wiki/Glossary_of_graph_theory_terms#edge) @@ -194,26 +194,26 @@ graph from the set of changes described in the plan that is being applied. The graph builders all work in terms of a sequence of _transforms_, which are implementations of -[`terraform.GraphTransformer`](https://godoc.org/github.com/hashicorp/terraform/terraform#GraphTransformer). +[`terraform.GraphTransformer`](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#GraphTransformer). Implementations of this interface just take a graph and mutate it in any way needed, and so the set of available transforms is quite varied. Some -import examples include: +important examples include: -* [`ConfigTransformer`](https://godoc.org/github.com/hashicorp/terraform/terraform#ConfigTransformer), +* [`ConfigTransformer`](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#ConfigTransformer), which creates a graph vertex for each `resource` block in the configuration. -* [`StateTransformer`](https://godoc.org/github.com/hashicorp/terraform/terraform#StateTransformer), +* [`StateTransformer`](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#StateTransformer), which creates a graph vertex for each resource instance currently tracked in the state. -* [`ReferenceTransformer`](https://godoc.org/github.com/hashicorp/terraform/terraform#ReferenceTransformer), +* [`ReferenceTransformer`](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#ReferenceTransformer), which analyses the configuration to find dependencies between resources and other objects and creates any necessary "happens after" edges for these. -* [`ProviderTransformer`](https://godoc.org/github.com/hashicorp/terraform/terraform#ProviderTransformer), +* [`ProviderTransformer`](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#ProviderTransformer), which associates each resource or resource instance with exactly one provider configuration (implementing - [the inheritance rules](https://www.terraform.io/docs/modules/usage.html#providers-within-modules)) + [the inheritance rules](https://www.terraform.io/docs/language/modules/develop/providers.html)) and then creates "happens after" edges to ensure that the providers are initialized before taking any actions with the resources that belong to them. @@ -224,7 +224,7 @@ builder uses a different subset of these depending on the needs of the operation that is being performed. The result of graph building is a -[`terraform.Graph`](https://godoc.org/github.com/hashicorp/terraform/terraform#Graph), which +[`terraform.Graph`](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#Graph), which can then be processed using a _graph walker_. ## Graph Walk @@ -232,39 +232,35 @@ can then be processed using a _graph walker_. The process of walking the graph visits each vertex of that graph in a way which respects the "happens after" edges in the graph. The walk algorithm itself is implemented in -[the low-level `dag` package](https://godoc.org/github.com/hashicorp/terraform/dag#AcyclicGraph.Walk) +[the low-level `dag` package](https://pkg.go.dev/github.com/hashicorp/terraform/dag#AcyclicGraph.Walk) (where "DAG" is short for [_Directed Acyclic Graph_](https://en.wikipedia.org/wiki/Directed_acyclic_graph)), in -[`AcyclicGraph.Walk`](https://godoc.org/github.com/hashicorp/terraform/dag#AcyclicGraph.Walk). +[`AcyclicGraph.Walk`](https://pkg.go.dev/github.com/hashicorp/terraform/dag#AcyclicGraph.Walk). However, the "interesting" Terraform walk functionality is implemented in -[`terraform.ContextGraphWalker`](https://godoc.org/github.com/hashicorp/terraform/terraform#ContextGraphWalker), +[`terraform.ContextGraphWalker`](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#ContextGraphWalker), which implements a small set of higher-level operations that are performed during the graph walk: * `EnterPath` is called once for each module in the configuration, taking a module address and returning a - [`terraform.EvalContext`](https://godoc.org/github.com/hashicorp/terraform/terraform#EvalContext) + [`terraform.EvalContext`](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#EvalContext) that tracks objects within that module. `terraform.Context` is the _global_ context for the entire operation, while `terraform.EvalContext` is a context for processing within a single module, and is the primary means by which the namespaces in each module are kept separate. -* `EnterEvalTree` and `ExitEvalTree` are each called once for each vertex - in the graph during _vertex evaluation_, which is described in the following - section. - Each vertex in the graph is evaluated, in an order that guarantees that the "happens after" edges will be respected. If possible, the graph walk algorithm will evaluate multiple vertices concurrently. Vertex evaluation code must therefore make careful use of concurrency primitives such as mutexes in order to coordinate access to shared objects such as the `states.State` object. In most cases, we use the helper wrapper -[`states.SyncState`](https://godoc.org/github.com/hashicorp/terraform/states#SyncState) +[`states.SyncState`](https://pkg.go.dev/github.com/hashicorp/terraform/states#SyncState) to safely implement concurrent reads and writes from the shared state. ## Vertex Evaluation The action taken for each vertex during the graph walk is called -_evaluation_. Evaluation runs a sequence of arbitrary actions that make sense +_execution_. Execution runs a sequence of arbitrary actions that make sense for a particular vertex type. For example, evaluation of a vertex representing a resource instance during @@ -290,57 +286,33 @@ a plan operation would include the following high-level steps: * Save the instance diff as part of the plan that is being constructed by this operation. -Each evaluation step for a vertex is an implementation of -[`terraform.EvalNode`](https://godoc.org/github.com/hashicorp/terraform/terraform#EvalNode). +Each execution step for a vertex is an implementation of +[`terraform.Execute`](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#Execute). As with graph transforms, the behavior of these implementations varies widely: -whereas graph transforms can take any action against the graph, an `EvalNode` +whereas graph transforms can take any action against the graph, an `Execute` implementation can take any action against the `EvalContext`. The implementation of `terraform.EvalContext` used in real processing (as opposed to testing) is -[`terraform.BuiltinEvalContext`](https://godoc.org/github.com/hashicorp/terraform/terraform#BuiltinEvalContext). +[`terraform.BuiltinEvalContext`](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#BuiltinEvalContext). It provides coordinated access to plugins, the current state, and the current plan via the `EvalContext` interface methods. -In order to be evaluated, a vertex must implement -[`terraform.GraphNodeEvalable`](https://godoc.org/github.com/hashicorp/terraform/terraform#GraphNodeEvalable), -which has a single method that returns an `EvalNode`. In practice, most -implementations return an instance of -[`terraform.EvalSequence`](https://godoc.org/github.com/hashicorp/terraform/terraform#EvalSequence), -which wraps a number of other `EvalNode` objects to be executed in sequence. - -There are numerous `EvalNode` implementations with different behaviors, but -some prominent examples are: - -* [`EvalReadState`](https://godoc.org/github.com/hashicorp/terraform/terraform#EvalReadState), - which extracts the data for a particular resource instance from the state. +In order to be executed, a vertex must implement +[`terraform.GraphNodeExecutable`](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#GraphNodeExecutable), +which has a single `Execute` method that handles. There are numerous `Execute` +implementations with different behaviors, but some prominent examples are: -* [`EvalWriteState`](https://godoc.org/github.com/hashicorp/terraform/terraform#EvalWriteState), - which conversely replaces the data for a particular resource instance in - the state with some updated data resulting from changes made by the - provider. +* [NodePlannableResource.Execute](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#NodePlannableResourceInstance.Execute), which handles the `plan` operation. -* [`EvalInitProvider`](https://godoc.org/github.com/hashicorp/terraform/terraform#EvalInitProvider), - which starts up a provider plugin and passes the user-provided configuration - to it, caching the provider inside the `EvalContext`. +* [`NodeApplyableResourceInstance.Execute`](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#NodeApplyableResourceInstance.Execute), which handles the main `apply` operation. -* [`EvalGetProvider`](https://godoc.org/github.com/hashicorp/terraform/terraform#EvalGetProvider), - which retrieves an already-initialized provider that is cached in the - `EvalContext`. +* [`NodeDestroyResourceInstance.Execute`](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#EvalWriteState), which handles the main `destroy` operation. -* [`EvalValidateResource`](https://godoc.org/github.com/hashicorp/terraform/terraform#EvalValidateResource), - which checks to make sure that resource configuration conforms to the - expected schema and gives a provider plugin the opportunity to check that - given values are within the expected range, etc. - -* [`EvalApply`](https://godoc.org/github.com/hashicorp/terraform/terraform#EvalApply), - which calls into a provider plugin to make apply some planned changes - to a given resource instance. - -All of the evaluation steps for a vertex must complete successfully before -the graph walk will begin evaluation for other vertices that have -"happens after" edges. Evaluation can fail with one or more errors, in which -case the graph walk is halted and the errors are returned to the user. +A vertex must complete successfully before the graph walk will begin evaluation +for other vertices that have "happens after" edges. Evaluation can fail with one +or more errors, in which case the graph walk is halted and the errors are +returned to the user. ### Expression Evaluation @@ -355,31 +327,32 @@ The high-level process for expression evaluation is: to. For example, the expression `aws_instance.example[1]` refers to one of the instances created by a `resource "aws_instance" "example"` block in configuration. This analysis is performed by - [`lang.References`](https://godoc.org/github.com/hashicorp/terraform/lang#References), + [`lang.References`](https://pkg.go.dev/github.com/hashicorp/terraform/lang#References), or more often one of the helper wrappers around it: - [`lang.ReferencesInBlock`](https://godoc.org/github.com/hashicorp/terraform/lang#ReferencesInBlock) + [`lang.ReferencesInBlock`](https://pkg.go.dev/github.com/hashicorp/terraform/lang#ReferencesInBlock) or - [`lang.ReferencesInExpr`](https://godoc.org/github.com/hashicorp/terraform/lang#ReferencesInExpr) + [`lang.ReferencesInExpr`](https://pkg.go.dev/github.com/hashicorp/terraform/lang#ReferencesInExpr) -2. Retrieve from the state the data for the objects that are referred to and +1. Retrieve from the state the data for the objects that are referred to and create a lookup table of the values from these objects that the HCL evaluation code can refer to. -3. Prepare the table of built-in functions so that HCL evaluation can refer to +1. Prepare the table of built-in functions so that HCL evaluation can refer to them. -4. Ask HCL to evaluate each attribute's expression (a `hcl.Expression` object) - against the data and function lookup tables. +1. Ask HCL to evaluate each attribute's expression (a + [`hcl.Expression`](https://pkg.go.dev/github.com/hashicorp/hcl/v2/#Expression) + object) against the data and function lookup tables. In practice, steps 2 through 4 are usually run all together using one -of the methods on [`lang.Scope`](https://godoc.org/github.com/hashicorp/terraform/lang#Scope); +of the methods on [`lang.Scope`](https://pkg.go.dev/github.com/hashicorp/terraform/lang#Scope); most commonly, -[`lang.EvalBlock`](https://godoc.org/github.com/hashicorp/terraform/lang#Scope.EvalBlock) +[`lang.EvalBlock`](https://pkg.go.dev/github.com/hashicorp/terraform/lang#Scope.EvalBlock) or -[`lang.EvalExpr`](https://godoc.org/github.com/hashicorp/terraform/lang#Scope.EvalExpr). +[`lang.EvalExpr`](https://pkg.go.dev/github.com/hashicorp/terraform/lang#Scope.EvalExpr). Expression evaluation produces a dynamic value represented as a -[`cty.Value`](https://godoc.org/github.com/zclconf/go-cty/cty#Value). +[`cty.Value`](https://pkg.go.dev/github.com/zclconf/go-cty/cty#Value). This Go type represents values from the Terraform language and such values are eventually passed to provider plugins. @@ -401,9 +374,9 @@ known when the main graph is constructed, but become known while evaluating other vertices in the main graph. This special behavior applies to vertex objects that implement -[`terraform.GraphNodeDynamicExpandable`](https://godoc.org/github.com/hashicorp/terraform/terraform#GraphNodeDynamicExpandable). Such vertexes have their own nested _graph builder_, _graph walk_, +[`terraform.GraphNodeDynamicExpandable`](https://pkg.go.dev/github.com/hashicorp/terraform/terraform#GraphNodeDynamicExpandable). +Such vertices have their own nested _graph builder_, _graph walk_, and _vertex evaluation_ steps, with the same behaviors as described in these sections for the main graph. The difference is in which graph transforms are used to construct the graph and in which evaluation steps apply to the nodes in that sub-graph. - diff --git a/vendor/github.com/hashicorp/terraform/docs/destroying.md b/vendor/github.com/hashicorp/terraform/docs/destroying.md index 4d821a2e..cf65fc6a 100644 --- a/vendor/github.com/hashicorp/terraform/docs/destroying.md +++ b/vendor/github.com/hashicorp/terraform/docs/destroying.md @@ -114,6 +114,7 @@ digraph replacement { } a -> a_d; + a -> b_d [style=dotted]; b -> a_d [style=dotted]; b -> b_d; } @@ -158,6 +159,28 @@ While the dependency edge from `B update` to `A destroy` isn't necessary in these examples, it is shown here as an implementation detail which will be mentioned later on. +A final example based on the replacement graph; starting with the above +configuration where `B` depends on `A`. The graph is reduced to an update of +`A` while only destroying `B`. The interesting feature here is the remaining +dependency of `A update` on `B destroy`. We can derive this ordering of +operations from the full replacement example above, by replacing `A create` +with `A update` and removing the unused nodes. + +![Replace All](./images/destroy_then_update.png) + ## Create Before Destroy Currently, the only user-controllable method for changing the ordering of @@ -186,6 +209,7 @@ digraph replacement { } a -> a_d [dir=back]; + a -> b_d; b -> a_d [dir=back]; b -> b_d; } @@ -193,7 +217,8 @@ digraph replacement { Order of operations: -1. `B` is destroyed AND `A` is created +1. `B` is destroyed +2. `A` is created 1. `B` is created 1. `A` is destroyed @@ -291,6 +316,7 @@ digraph replacement { } a -> a_d; + a -> b_d [style=dotted]; b -> a_d [style=dotted]; b -> b_d [dir=back]; } @@ -317,6 +343,7 @@ digraph replacement { } a -> a_d [dir=back]; + a -> b_d [dir=back, style=dotted]; b -> a_d [dir=back, style=dotted]; b -> b_d [dir=back]; } diff --git a/vendor/github.com/hashicorp/terraform/docs/images/destroy_then_update.png b/vendor/github.com/hashicorp/terraform/docs/images/destroy_then_update.png new file mode 100644 index 00000000..f4f3d209 Binary files /dev/null and b/vendor/github.com/hashicorp/terraform/docs/images/destroy_then_update.png differ diff --git a/vendor/github.com/hashicorp/terraform/docs/images/replace_all.png b/vendor/github.com/hashicorp/terraform/docs/images/replace_all.png index 7b8f532d..54d5ad69 100644 Binary files a/vendor/github.com/hashicorp/terraform/docs/images/replace_all.png and b/vendor/github.com/hashicorp/terraform/docs/images/replace_all.png differ diff --git a/vendor/github.com/hashicorp/terraform/docs/images/replace_all_cbd.png b/vendor/github.com/hashicorp/terraform/docs/images/replace_all_cbd.png index fa56581a..da72fe48 100644 Binary files a/vendor/github.com/hashicorp/terraform/docs/images/replace_all_cbd.png and b/vendor/github.com/hashicorp/terraform/docs/images/replace_all_cbd.png differ diff --git a/vendor/github.com/hashicorp/terraform/docs/images/replace_all_cbd_dep.png b/vendor/github.com/hashicorp/terraform/docs/images/replace_all_cbd_dep.png index 43c3a64a..98bdbde6 100644 Binary files a/vendor/github.com/hashicorp/terraform/docs/images/replace_all_cbd_dep.png and b/vendor/github.com/hashicorp/terraform/docs/images/replace_all_cbd_dep.png differ diff --git a/vendor/github.com/hashicorp/terraform/docs/images/replace_cbd_incorrect.png b/vendor/github.com/hashicorp/terraform/docs/images/replace_cbd_incorrect.png index 6a4f34ee..72591d03 100644 Binary files a/vendor/github.com/hashicorp/terraform/docs/images/replace_cbd_incorrect.png and b/vendor/github.com/hashicorp/terraform/docs/images/replace_cbd_incorrect.png differ diff --git a/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/README.md b/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/README.md index 12ff4108..de925018 100644 --- a/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/README.md +++ b/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/README.md @@ -22,7 +22,7 @@ From Terraform v0.12.0 onwards, Terraform's plugin protocol is built on different versions of Terraform's protocol. Only `.proto` files published as part of Terraform release tags are actually -official protocol versions. If you are reading this directory on the `master` +official protocol versions. If you are reading this directory on the `main` branch or any other development branch then it may contain protocol definitions that are not yet finalized and that may change before final release. diff --git a/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/tfplugin5.0.proto b/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/tfplugin5.0.proto index cf6dda34..624ad2a8 100644 --- a/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/tfplugin5.0.proto +++ b/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/tfplugin5.0.proto @@ -13,7 +13,7 @@ // official protocol releases. Proto files taken from other commits may include // incomplete changes or features that did not make it into a final release. // In all reasonable cases, plugin developers should take the proto file from -// the tag of the most recent release of Terraform, and not from the master +// the tag of the most recent release of Terraform, and not from the main // branch or any other development branch. // syntax = "proto3"; diff --git a/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/tfplugin5.1.proto b/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/tfplugin5.1.proto index 9875d9ba..8f01ad96 100644 --- a/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/tfplugin5.1.proto +++ b/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/tfplugin5.1.proto @@ -13,7 +13,7 @@ // official protocol releases. Proto files taken from other commits may include // incomplete changes or features that did not make it into a final release. // In all reasonable cases, plugin developers should take the proto file from -// the tag of the most recent release of Terraform, and not from the master +// the tag of the most recent release of Terraform, and not from the main // branch or any other development branch. // syntax = "proto3"; diff --git a/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/tfplugin5.2.proto b/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/tfplugin5.2.proto index 4f365697..1c29f039 100644 --- a/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/tfplugin5.2.proto +++ b/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/tfplugin5.2.proto @@ -13,10 +13,11 @@ // official protocol releases. Proto files taken from other commits may include // incomplete changes or features that did not make it into a final release. // In all reasonable cases, plugin developers should take the proto file from -// the tag of the most recent release of Terraform, and not from the master +// the tag of the most recent release of Terraform, and not from the main // branch or any other development branch. // syntax = "proto3"; +option go_package = "github.com/hashicorp/terraform/internal/tfplugin5"; package tfplugin5; diff --git a/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/tfplugin6.0.proto b/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/tfplugin6.0.proto new file mode 100644 index 00000000..4d8dc060 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/docs/plugin-protocol/tfplugin6.0.proto @@ -0,0 +1,321 @@ +// Terraform Plugin RPC protocol version 6.0 +// +// This file defines version 6.0 of the RPC protocol. To implement a plugin +// against this protocol, copy this definition into your own codebase and +// use protoc to generate stubs for your target language. +// +// This file will not be updated. Any minor versions of protocol 6 to follow +// should copy this file and modify the copy while maintaing backwards +// compatibility. Breaking changes, if any are required, will come +// in a subsequent major version with its own separate proto definition. +// +// Note that only the proto files included in a release tag of Terraform are +// official protocol releases. Proto files taken from other commits may include +// incomplete changes or features that did not make it into a final release. +// In all reasonable cases, plugin developers should take the proto file from +// the tag of the most recent release of Terraform, and not from the main +// branch or any other development branch. +// +syntax = "proto3"; +option go_package = "github.com/hashicorp/terraform/internal/tfplugin6"; + +package tfplugin6; + +// DynamicValue is an opaque encoding of terraform data, with the field name +// indicating the encoding scheme used. +message DynamicValue { + bytes msgpack = 1; + bytes json = 2; +} + +message Diagnostic { + enum Severity { + INVALID = 0; + ERROR = 1; + WARNING = 2; + } + Severity severity = 1; + string summary = 2; + string detail = 3; + AttributePath attribute = 4; +} + +message AttributePath { + message Step { + oneof selector { + // Set "attribute_name" to represent looking up an attribute + // in the current object value. + string attribute_name = 1; + // Set "element_key_*" to represent looking up an element in + // an indexable collection type. + string element_key_string = 2; + int64 element_key_int = 3; + } + } + repeated Step steps = 1; +} + +message StopProvider { + message Request { + } + message Response { + string Error = 1; + } +} + +// RawState holds the stored state for a resource to be upgraded by the +// provider. It can be in one of two formats, the current json encoded format +// in bytes, or the legacy flatmap format as a map of strings. +message RawState { + bytes json = 1; + map flatmap = 2; +} + +enum StringKind { + PLAIN = 0; + MARKDOWN = 1; +} + +// Schema is the configuration schema for a Resource or Provider. +message Schema { + message Block { + int64 version = 1; + repeated Attribute attributes = 2; + repeated NestedBlock block_types = 3; + string description = 4; + StringKind description_kind = 5; + bool deprecated = 6; + } + + message Attribute { + string name = 1; + bytes type = 2; + Object nested_type = 10; + string description = 3; + bool required = 4; + bool optional = 5; + bool computed = 6; + bool sensitive = 7; + StringKind description_kind = 8; + bool deprecated = 9; + } + + message NestedBlock { + enum NestingMode { + INVALID = 0; + SINGLE = 1; + LIST = 2; + SET = 3; + MAP = 4; + GROUP = 5; + } + + string type_name = 1; + Block block = 2; + NestingMode nesting = 3; + int64 min_items = 4; + int64 max_items = 5; + } + + message Object { + enum NestingMode { + INVALID = 0; + SINGLE = 1; + LIST = 2; + SET = 3; + MAP = 4; + } + + repeated Attribute attributes = 1; + NestingMode nesting = 3; + int64 min_items = 4; + int64 max_items = 5; + } + + // The version of the schema. + // Schemas are versioned, so that providers can upgrade a saved resource + // state when the schema is changed. + int64 version = 1; + + // Block is the top level configuration block for this schema. + Block block = 2; +} + +service Provider { + //////// Information about what a provider supports/expects + rpc GetProviderSchema(GetProviderSchema.Request) returns (GetProviderSchema.Response); + rpc ValidateProviderConfig(ValidateProviderConfig.Request) returns (ValidateProviderConfig.Response); + rpc ValidateResourceConfig(ValidateResourceConfig.Request) returns (ValidateResourceConfig.Response); + rpc ValidateDataResourceConfig(ValidateDataResourceConfig.Request) returns (ValidateDataResourceConfig.Response); + rpc UpgradeResourceState(UpgradeResourceState.Request) returns (UpgradeResourceState.Response); + + //////// One-time initialization, called before other functions below + rpc ConfigureProvider(ConfigureProvider.Request) returns (ConfigureProvider.Response); + + //////// Managed Resource Lifecycle + rpc ReadResource(ReadResource.Request) returns (ReadResource.Response); + rpc PlanResourceChange(PlanResourceChange.Request) returns (PlanResourceChange.Response); + rpc ApplyResourceChange(ApplyResourceChange.Request) returns (ApplyResourceChange.Response); + rpc ImportResourceState(ImportResourceState.Request) returns (ImportResourceState.Response); + + rpc ReadDataSource(ReadDataSource.Request) returns (ReadDataSource.Response); + + //////// Graceful Shutdown + rpc StopProvider(StopProvider.Request) returns (StopProvider.Response); +} + +message GetProviderSchema { + message Request { + } + message Response { + Schema provider = 1; + map resource_schemas = 2; + map data_source_schemas = 3; + repeated Diagnostic diagnostics = 4; + Schema provider_meta = 5; + } +} + +message ValidateProviderConfig { + message Request { + DynamicValue config = 1; + } + message Response { + repeated Diagnostic diagnostics = 2; + } +} + +message UpgradeResourceState { + message Request { + string type_name = 1; + + // version is the schema_version number recorded in the state file + int64 version = 2; + + // raw_state is the raw states as stored for the resource. Core does + // not have access to the schema of prior_version, so it's the + // provider's responsibility to interpret this value using the + // appropriate older schema. The raw_state will be the json encoded + // state, or a legacy flat-mapped format. + RawState raw_state = 3; + } + message Response { + // new_state is a msgpack-encoded data structure that, when interpreted with + // the _current_ schema for this resource type, is functionally equivalent to + // that which was given in prior_state_raw. + DynamicValue upgraded_state = 1; + + // diagnostics describes any errors encountered during migration that could not + // be safely resolved, and warnings about any possibly-risky assumptions made + // in the upgrade process. + repeated Diagnostic diagnostics = 2; + } +} + +message ValidateResourceConfig { + message Request { + string type_name = 1; + DynamicValue config = 2; + } + message Response { + repeated Diagnostic diagnostics = 1; + } +} + +message ValidateDataResourceConfig { + message Request { + string type_name = 1; + DynamicValue config = 2; + } + message Response { + repeated Diagnostic diagnostics = 1; + } +} + +message ConfigureProvider { + message Request { + string terraform_version = 1; + DynamicValue config = 2; + } + message Response { + repeated Diagnostic diagnostics = 1; + } +} + +message ReadResource { + message Request { + string type_name = 1; + DynamicValue current_state = 2; + bytes private = 3; + DynamicValue provider_meta = 4; + } + message Response { + DynamicValue new_state = 1; + repeated Diagnostic diagnostics = 2; + bytes private = 3; + } +} + +message PlanResourceChange { + message Request { + string type_name = 1; + DynamicValue prior_state = 2; + DynamicValue proposed_new_state = 3; + DynamicValue config = 4; + bytes prior_private = 5; + DynamicValue provider_meta = 6; + } + + message Response { + DynamicValue planned_state = 1; + repeated AttributePath requires_replace = 2; + bytes planned_private = 3; + repeated Diagnostic diagnostics = 4; + } +} + +message ApplyResourceChange { + message Request { + string type_name = 1; + DynamicValue prior_state = 2; + DynamicValue planned_state = 3; + DynamicValue config = 4; + bytes planned_private = 5; + DynamicValue provider_meta = 6; + } + message Response { + DynamicValue new_state = 1; + bytes private = 2; + repeated Diagnostic diagnostics = 3; + } +} + +message ImportResourceState { + message Request { + string type_name = 1; + string id = 2; + } + + message ImportedResource { + string type_name = 1; + DynamicValue state = 2; + bytes private = 3; + } + + message Response { + repeated ImportedResource imported_resources = 1; + repeated Diagnostic diagnostics = 2; + } +} + +message ReadDataSource { + message Request { + string type_name = 1; + DynamicValue config = 2; + DynamicValue provider_meta = 3; + } + message Response { + DynamicValue state = 1; + repeated Diagnostic diagnostics = 2; + } +} diff --git a/vendor/github.com/hashicorp/terraform/experiments/experiment.go b/vendor/github.com/hashicorp/terraform/experiments/experiment.go index 552f775d..ee73ca23 100644 --- a/vendor/github.com/hashicorp/terraform/experiments/experiment.go +++ b/vendor/github.com/hashicorp/terraform/experiments/experiment.go @@ -13,14 +13,16 @@ type Experiment string // Each experiment is represented by a string that must be a valid HCL // identifier so that it can be specified in configuration. const ( - VariableValidation = Experiment("variable_validation") - ModuleVariableOptionalAttrs = Experiment("module_variable_optional_attrs") + VariableValidation = Experiment("variable_validation") + ModuleVariableOptionalAttrs = Experiment("module_variable_optional_attrs") + SuppressProviderSensitiveAttrs = Experiment("provider_sensitive_attrs") ) func init() { // Each experiment constant defined above must be registered here as either // a current or a concluded experiment. registerConcludedExperiment(VariableValidation, "Custom variable validation can now be used by default, without enabling an experiment.") + registerConcludedExperiment(SuppressProviderSensitiveAttrs, "Provider-defined sensitive attributes are now redacted by default, without enabling an experiment.") registerCurrentExperiment(ModuleVariableOptionalAttrs) } diff --git a/vendor/github.com/hashicorp/terraform/flatmap/expand.go b/vendor/github.com/hashicorp/terraform/flatmap/expand.go deleted file mode 100644 index b9d15461..00000000 --- a/vendor/github.com/hashicorp/terraform/flatmap/expand.go +++ /dev/null @@ -1,152 +0,0 @@ -package flatmap - -import ( - "fmt" - "sort" - "strconv" - "strings" - - "github.com/hashicorp/terraform/configs/hcl2shim" -) - -// Expand takes a map and a key (prefix) and expands that value into -// a more complex structure. This is the reverse of the Flatten operation. -func Expand(m map[string]string, key string) interface{} { - // If the key is exactly a key in the map, just return it - if v, ok := m[key]; ok { - if v == "true" { - return true - } else if v == "false" { - return false - } - - return v - } - - // Check if the key is an array, and if so, expand the array - if v, ok := m[key+".#"]; ok { - // If the count of the key is unknown, then just put the unknown - // value in the value itself. This will be detected by Terraform - // core later. - if v == hcl2shim.UnknownVariableValue { - return v - } - - return expandArray(m, key) - } - - // Check if this is a prefix in the map - prefix := key + "." - for k := range m { - if strings.HasPrefix(k, prefix) { - return expandMap(m, prefix) - } - } - - return nil -} - -func expandArray(m map[string]string, prefix string) []interface{} { - num, err := strconv.ParseInt(m[prefix+".#"], 0, 0) - if err != nil { - panic(err) - } - - // If the number of elements in this array is 0, then return an - // empty slice as there is nothing to expand. Trying to expand it - // anyway could lead to crashes as any child maps, arrays or sets - // that no longer exist are still shown as empty with a count of 0. - if num == 0 { - return []interface{}{} - } - - // NOTE: "num" is not necessarily accurate, e.g. if a user tampers - // with state, so the following code should not crash when given a - // number of items more or less than what's given in num. The - // num key is mainly just a hint that this is a list or set. - - // The Schema "Set" type stores its values in an array format, but - // using numeric hash values instead of ordinal keys. Take the set - // of keys regardless of value, and expand them in numeric order. - // See GH-11042 for more details. - keySet := map[int]bool{} - computed := map[string]bool{} - for k := range m { - if !strings.HasPrefix(k, prefix+".") { - continue - } - - key := k[len(prefix)+1:] - idx := strings.Index(key, ".") - if idx != -1 { - key = key[:idx] - } - - // skip the count value - if key == "#" { - continue - } - - // strip the computed flag if there is one - if strings.HasPrefix(key, "~") { - key = key[1:] - computed[key] = true - } - - k, err := strconv.Atoi(key) - if err != nil { - panic(err) - } - keySet[int(k)] = true - } - - keysList := make([]int, 0, num) - for key := range keySet { - keysList = append(keysList, key) - } - sort.Ints(keysList) - - result := make([]interface{}, len(keysList)) - for i, key := range keysList { - keyString := strconv.Itoa(key) - if computed[keyString] { - keyString = "~" + keyString - } - result[i] = Expand(m, fmt.Sprintf("%s.%s", prefix, keyString)) - } - - return result -} - -func expandMap(m map[string]string, prefix string) map[string]interface{} { - // Submaps may not have a '%' key, so we can't count on this value being - // here. If we don't have a count, just proceed as if we have have a map. - if count, ok := m[prefix+"%"]; ok && count == "0" { - return map[string]interface{}{} - } - - result := make(map[string]interface{}) - for k := range m { - if !strings.HasPrefix(k, prefix) { - continue - } - - key := k[len(prefix):] - idx := strings.Index(key, ".") - if idx != -1 { - key = key[:idx] - } - if _, ok := result[key]; ok { - continue - } - - // skip the map count value - if key == "%" { - continue - } - - result[key] = Expand(m, k[:len(prefix)+len(key)]) - } - - return result -} diff --git a/vendor/github.com/hashicorp/terraform/flatmap/expand_test.go b/vendor/github.com/hashicorp/terraform/flatmap/expand_test.go deleted file mode 100644 index 707c015d..00000000 --- a/vendor/github.com/hashicorp/terraform/flatmap/expand_test.go +++ /dev/null @@ -1,225 +0,0 @@ -package flatmap - -import ( - "reflect" - "testing" - - "github.com/hashicorp/terraform/configs/hcl2shim" -) - -func TestExpand(t *testing.T) { - cases := []struct { - Map map[string]string - Key string - Output interface{} - }{ - { - Map: map[string]string{ - "foo": "bar", - "bar": "baz", - }, - Key: "foo", - Output: "bar", - }, - - { - Map: map[string]string{ - "foo.#": "2", - "foo.0": "one", - "foo.1": "two", - }, - Key: "foo", - Output: []interface{}{ - "one", - "two", - }, - }, - - { - Map: map[string]string{ - // # mismatches actual number of keys; actual number should - // "win" here, since the # is just a hint that this is a list. - "foo.#": "1", - "foo.0": "one", - "foo.1": "two", - "foo.2": "three", - }, - Key: "foo", - Output: []interface{}{ - "one", - "two", - "three", - }, - }, - - { - Map: map[string]string{ - // # mismatches actual number of keys; actual number should - // "win" here, since the # is just a hint that this is a list. - "foo.#": "5", - "foo.0": "one", - "foo.1": "two", - "foo.2": "three", - }, - Key: "foo", - Output: []interface{}{ - "one", - "two", - "three", - }, - }, - - { - Map: map[string]string{ - "foo.#": "1", - "foo.0.name": "bar", - "foo.0.port": "3000", - "foo.0.enabled": "true", - }, - Key: "foo", - Output: []interface{}{ - map[string]interface{}{ - "name": "bar", - "port": "3000", - "enabled": true, - }, - }, - }, - - { - Map: map[string]string{ - "foo.#": "1", - "foo.0.name": "bar", - "foo.0.ports.#": "2", - "foo.0.ports.0": "1", - "foo.0.ports.1": "2", - }, - Key: "foo", - Output: []interface{}{ - map[string]interface{}{ - "name": "bar", - "ports": []interface{}{ - "1", - "2", - }, - }, - }, - }, - - { - Map: map[string]string{ - "list_of_map.#": "2", - "list_of_map.0.%": "1", - "list_of_map.0.a": "1", - "list_of_map.1.%": "2", - "list_of_map.1.b": "2", - "list_of_map.1.c": "3", - }, - Key: "list_of_map", - Output: []interface{}{ - map[string]interface{}{ - "a": "1", - }, - map[string]interface{}{ - "b": "2", - "c": "3", - }, - }, - }, - - { - Map: map[string]string{ - "map_of_list.%": "2", - "map_of_list.list2.#": "1", - "map_of_list.list2.0": "c", - "map_of_list.list1.#": "2", - "map_of_list.list1.0": "a", - "map_of_list.list1.1": "b", - }, - Key: "map_of_list", - Output: map[string]interface{}{ - "list1": []interface{}{"a", "b"}, - "list2": []interface{}{"c"}, - }, - }, - - { - Map: map[string]string{ - "set.#": "3", - "set.1234": "a", - "set.1235": "b", - "set.1236": "c", - }, - Key: "set", - Output: []interface{}{"a", "b", "c"}, - }, - - { - Map: map[string]string{ - "computed_set.#": "1", - "computed_set.~1234.a": "a", - "computed_set.~1234.b": "b", - "computed_set.~1234.c": "c", - }, - Key: "computed_set", - Output: []interface{}{ - map[string]interface{}{"a": "a", "b": "b", "c": "c"}, - }, - }, - - { - Map: map[string]string{ - "struct.#": "1", - "struct.0.name": "hello", - "struct.0.rules.#": hcl2shim.UnknownVariableValue, - }, - Key: "struct", - Output: []interface{}{ - map[string]interface{}{ - "name": "hello", - "rules": hcl2shim.UnknownVariableValue, - }, - }, - }, - - { - Map: map[string]string{ - "struct.#": "1", - "struct.0.name": "hello", - "struct.0.set.#": "0", - "struct.0.set.0.key": "value", - }, - Key: "struct", - Output: []interface{}{ - map[string]interface{}{ - "name": "hello", - "set": []interface{}{}, - }, - }, - }, - - { - Map: map[string]string{ - "empty_map_of_sets.%": "0", - "empty_map_of_sets.set1.#": "0", - "empty_map_of_sets.set1.1234": "x", - }, - Key: "empty_map_of_sets", - Output: map[string]interface{}{}, - }, - } - - for _, tc := range cases { - t.Run(tc.Key, func(t *testing.T) { - actual := Expand(tc.Map, tc.Key) - if !reflect.DeepEqual(actual, tc.Output) { - t.Errorf( - "Key: %v\nMap:\n\n%#v\n\nOutput:\n\n%#v\n\nExpected:\n\n%#v\n", - tc.Key, - tc.Map, - actual, - tc.Output) - } - }) - } -} diff --git a/vendor/github.com/hashicorp/terraform/flatmap/flatten.go b/vendor/github.com/hashicorp/terraform/flatmap/flatten.go deleted file mode 100644 index 9ff6e426..00000000 --- a/vendor/github.com/hashicorp/terraform/flatmap/flatten.go +++ /dev/null @@ -1,71 +0,0 @@ -package flatmap - -import ( - "fmt" - "reflect" -) - -// Flatten takes a structure and turns into a flat map[string]string. -// -// Within the "thing" parameter, only primitive values are allowed. Structs are -// not supported. Therefore, it can only be slices, maps, primitives, and -// any combination of those together. -// -// See the tests for examples of what inputs are turned into. -func Flatten(thing map[string]interface{}) Map { - result := make(map[string]string) - - for k, raw := range thing { - flatten(result, k, reflect.ValueOf(raw)) - } - - return Map(result) -} - -func flatten(result map[string]string, prefix string, v reflect.Value) { - if v.Kind() == reflect.Interface { - v = v.Elem() - } - - switch v.Kind() { - case reflect.Bool: - if v.Bool() { - result[prefix] = "true" - } else { - result[prefix] = "false" - } - case reflect.Int: - result[prefix] = fmt.Sprintf("%d", v.Int()) - case reflect.Map: - flattenMap(result, prefix, v) - case reflect.Slice: - flattenSlice(result, prefix, v) - case reflect.String: - result[prefix] = v.String() - default: - panic(fmt.Sprintf("Unknown: %s", v)) - } -} - -func flattenMap(result map[string]string, prefix string, v reflect.Value) { - for _, k := range v.MapKeys() { - if k.Kind() == reflect.Interface { - k = k.Elem() - } - - if k.Kind() != reflect.String { - panic(fmt.Sprintf("%s: map key is not string: %s", prefix, k)) - } - - flatten(result, fmt.Sprintf("%s.%s", prefix, k.String()), v.MapIndex(k)) - } -} - -func flattenSlice(result map[string]string, prefix string, v reflect.Value) { - prefix = prefix + "." - - result[prefix+"#"] = fmt.Sprintf("%d", v.Len()) - for i := 0; i < v.Len(); i++ { - flatten(result, fmt.Sprintf("%s%d", prefix, i), v.Index(i)) - } -} diff --git a/vendor/github.com/hashicorp/terraform/flatmap/flatten_test.go b/vendor/github.com/hashicorp/terraform/flatmap/flatten_test.go deleted file mode 100644 index 1aa4940f..00000000 --- a/vendor/github.com/hashicorp/terraform/flatmap/flatten_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package flatmap - -import ( - "reflect" - "testing" -) - -func TestFlatten(t *testing.T) { - cases := []struct { - Input map[string]interface{} - Output map[string]string - }{ - { - Input: map[string]interface{}{ - "foo": "bar", - "bar": "baz", - }, - Output: map[string]string{ - "foo": "bar", - "bar": "baz", - }, - }, - - { - Input: map[string]interface{}{ - "foo": []string{ - "one", - "two", - }, - }, - Output: map[string]string{ - "foo.#": "2", - "foo.0": "one", - "foo.1": "two", - }, - }, - - { - Input: map[string]interface{}{ - "foo": []map[interface{}]interface{}{ - map[interface{}]interface{}{ - "name": "bar", - "port": 3000, - "enabled": true, - }, - }, - }, - Output: map[string]string{ - "foo.#": "1", - "foo.0.name": "bar", - "foo.0.port": "3000", - "foo.0.enabled": "true", - }, - }, - - { - Input: map[string]interface{}{ - "foo": []map[interface{}]interface{}{ - map[interface{}]interface{}{ - "name": "bar", - "ports": []string{ - "1", - "2", - }, - }, - }, - }, - Output: map[string]string{ - "foo.#": "1", - "foo.0.name": "bar", - "foo.0.ports.#": "2", - "foo.0.ports.0": "1", - "foo.0.ports.1": "2", - }, - }, - } - - for _, tc := range cases { - actual := Flatten(tc.Input) - if !reflect.DeepEqual(actual, Map(tc.Output)) { - t.Fatalf( - "Input:\n\n%#v\n\nOutput:\n\n%#v\n\nExpected:\n\n%#v\n", - tc.Input, - actual, - tc.Output) - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/flatmap/map.go b/vendor/github.com/hashicorp/terraform/flatmap/map.go deleted file mode 100644 index 46b72c40..00000000 --- a/vendor/github.com/hashicorp/terraform/flatmap/map.go +++ /dev/null @@ -1,82 +0,0 @@ -package flatmap - -import ( - "strings" -) - -// Map is a wrapper around map[string]string that provides some helpers -// above it that assume the map is in the format that flatmap expects -// (the result of Flatten). -// -// All modifying functions such as Delete are done in-place unless -// otherwise noted. -type Map map[string]string - -// Contains returns true if the map contains the given key. -func (m Map) Contains(key string) bool { - for _, k := range m.Keys() { - if k == key { - return true - } - } - - return false -} - -// Delete deletes a key out of the map with the given prefix. -func (m Map) Delete(prefix string) { - for k, _ := range m { - match := k == prefix - if !match { - if !strings.HasPrefix(k, prefix) { - continue - } - - if k[len(prefix):len(prefix)+1] != "." { - continue - } - } - - delete(m, k) - } -} - -// Keys returns all of the top-level keys in this map -func (m Map) Keys() []string { - ks := make(map[string]struct{}) - for k, _ := range m { - idx := strings.Index(k, ".") - if idx == -1 { - idx = len(k) - } - - ks[k[:idx]] = struct{}{} - } - - result := make([]string, 0, len(ks)) - for k, _ := range ks { - result = append(result, k) - } - - return result -} - -// Merge merges the contents of the other Map into this one. -// -// This merge is smarter than a simple map iteration because it -// will fully replace arrays and other complex structures that -// are present in this map with the other map's. For example, if -// this map has a 3 element "foo" list, and m2 has a 2 element "foo" -// list, then the result will be that m has a 2 element "foo" -// list. -func (m Map) Merge(m2 Map) { - for _, prefix := range m2.Keys() { - m.Delete(prefix) - - for k, v := range m2 { - if strings.HasPrefix(k, prefix) { - m[k] = v - } - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/flatmap/map_test.go b/vendor/github.com/hashicorp/terraform/flatmap/map_test.go deleted file mode 100644 index e3b4cb1b..00000000 --- a/vendor/github.com/hashicorp/terraform/flatmap/map_test.go +++ /dev/null @@ -1,120 +0,0 @@ -package flatmap - -import ( - "reflect" - "sort" - "testing" -) - -func TestMapContains(t *testing.T) { - cases := []struct { - Input map[string]string - Key string - Result bool - }{ - { - Input: map[string]string{ - "foo": "bar", - "bar": "nope", - }, - Key: "foo", - Result: true, - }, - - { - Input: map[string]string{ - "foo": "bar", - "bar": "nope", - }, - Key: "baz", - Result: false, - }, - } - - for i, tc := range cases { - actual := Map(tc.Input).Contains(tc.Key) - if actual != tc.Result { - t.Fatalf("case %d bad: %#v", i, tc.Input) - } - } -} - -func TestMapDelete(t *testing.T) { - m := Flatten(map[string]interface{}{ - "foo": "bar", - "routes": []map[string]string{ - map[string]string{ - "foo": "bar", - }, - }, - }) - - m.Delete("routes") - - expected := Map(map[string]string{"foo": "bar"}) - if !reflect.DeepEqual(m, expected) { - t.Fatalf("bad: %#v", m) - } -} - -func TestMapKeys(t *testing.T) { - cases := []struct { - Input map[string]string - Output []string - }{ - { - Input: map[string]string{ - "foo": "bar", - "bar.#": "bar", - "bar.0.foo": "bar", - "bar.0.baz": "bar", - }, - Output: []string{ - "bar", - "foo", - }, - }, - } - - for _, tc := range cases { - actual := Map(tc.Input).Keys() - - // Sort so we have a consistent view of the output - sort.Strings(actual) - - if !reflect.DeepEqual(actual, tc.Output) { - t.Fatalf("input: %#v\n\nbad: %#v", tc.Input, actual) - } - } -} - -func TestMapMerge(t *testing.T) { - cases := []struct { - One map[string]string - Two map[string]string - Result map[string]string - }{ - { - One: map[string]string{ - "foo": "bar", - "bar": "nope", - }, - Two: map[string]string{ - "bar": "baz", - "baz": "buz", - }, - Result: map[string]string{ - "foo": "bar", - "bar": "baz", - "baz": "buz", - }, - }, - } - - for i, tc := range cases { - Map(tc.One).Merge(Map(tc.Two)) - if !reflect.DeepEqual(tc.One, tc.Result) { - t.Fatalf("case %d bad: %#v", i, tc.One) - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/go.mod b/vendor/github.com/hashicorp/terraform/go.mod index 2bcf8d7e..aa70e993 100644 --- a/vendor/github.com/hashicorp/terraform/go.mod +++ b/vendor/github.com/hashicorp/terraform/go.mod @@ -1,9 +1,9 @@ module github.com/hashicorp/terraform require ( - cloud.google.com/go v0.45.1 - github.com/Azure/azure-sdk-for-go v45.0.0+incompatible - github.com/Azure/go-autorest/autorest v0.11.3 + cloud.google.com/go/storage v1.10.0 + github.com/Azure/azure-sdk-for-go v52.5.0+incompatible + github.com/Azure/go-autorest/autorest v0.11.18 github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect github.com/ChrisTrenkamp/goxpath v0.0.0-20190607011252-c5096ec8773d // indirect github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af // indirect @@ -13,19 +13,18 @@ require ( github.com/aliyun/aliyun-tablestore-go-sdk v4.1.2+incompatible github.com/apparentlymart/go-cidr v1.1.0 github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 + github.com/apparentlymart/go-shquot v0.0.1 github.com/apparentlymart/go-userdirs v0.0.0-20200915174352-b0c018a67c13 github.com/apparentlymart/go-versions v1.0.1 github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect github.com/armon/go-radix v1.0.0 // indirect - github.com/aws/aws-sdk-go v1.31.9 + github.com/aws/aws-sdk-go v1.37.0 github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect github.com/bgentry/speakeasy v0.1.0 github.com/bmatcuk/doublestar v1.1.5 github.com/boltdb/bolt v1.3.1 // indirect - github.com/chzyer/logex v1.1.10 // indirect github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e - github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect github.com/coreos/bbolt v1.3.0 // indirect github.com/coreos/etcd v3.3.10+incompatible github.com/coreos/go-semver v0.2.0 // indirect @@ -36,11 +35,10 @@ require ( github.com/dylanmei/winrmtest v0.0.0-20190225150635-99b7fe2fddf1 github.com/go-test/deep v1.0.3 github.com/gofrs/uuid v3.3.0+incompatible // indirect - github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 // indirect - github.com/golang/mock v1.3.1 - github.com/golang/protobuf v1.3.4 + github.com/golang/mock v1.4.4 + github.com/golang/protobuf v1.4.3 github.com/google/go-cmp v0.5.2 - github.com/google/uuid v1.1.1 + github.com/google/uuid v1.2.0 github.com/gophercloud/gophercloud v0.10.1-0.20200424014253-c3bfe50899e5 github.com/gophercloud/utils v0.0.0-20200423144003-7c72efc7435d github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect @@ -50,45 +48,43 @@ require ( github.com/grpc-ecosystem/grpc-gateway v1.8.5 // indirect github.com/hashicorp/aws-sdk-go-base v0.6.0 github.com/hashicorp/consul v0.0.0-20171026175957-610f3c86a089 - github.com/hashicorp/errwrap v1.0.0 - github.com/hashicorp/go-azure-helpers v0.12.0 + github.com/hashicorp/errwrap v1.1.0 + github.com/hashicorp/go-azure-helpers v0.14.0 github.com/hashicorp/go-checkpoint v0.5.0 github.com/hashicorp/go-cleanhttp v0.5.1 - github.com/hashicorp/go-getter v1.4.2-0.20200106182914-9813cbd4eb02 - github.com/hashicorp/go-hclog v0.14.1 + github.com/hashicorp/go-getter v1.5.2 + github.com/hashicorp/go-hclog v0.15.0 github.com/hashicorp/go-immutable-radix v0.0.0-20180129170900-7f3cd4390caa // indirect github.com/hashicorp/go-msgpack v0.5.4 // indirect - github.com/hashicorp/go-multierror v1.0.0 - github.com/hashicorp/go-plugin v1.3.0 + github.com/hashicorp/go-multierror v1.1.1 + github.com/hashicorp/go-plugin v1.4.1 github.com/hashicorp/go-retryablehttp v0.5.2 - github.com/hashicorp/go-rootcerts v1.0.0 + github.com/hashicorp/go-rootcerts v1.0.0 // indirect github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 // indirect - github.com/hashicorp/go-tfe v0.8.1 + github.com/hashicorp/go-tfe v0.14.0 github.com/hashicorp/go-uuid v1.0.1 - github.com/hashicorp/go-version v1.2.0 + github.com/hashicorp/go-version v1.2.1 github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f - github.com/hashicorp/hcl/v2 v2.7.0 + github.com/hashicorp/hcl/v2 v2.10.0 github.com/hashicorp/memberlist v0.1.0 // indirect github.com/hashicorp/serf v0.0.0-20160124182025-e4ec8cc423bb // indirect - github.com/hashicorp/terraform-config-inspect v0.0.0-20191212124732-c6ae6269b9d7 + github.com/hashicorp/terraform-config-inspect v0.0.0-20210209133302-4fd17a0faac2 github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect - github.com/imdario/mergo v0.3.9 // indirect - github.com/jmespath/go-jmespath v0.3.0 + github.com/jmespath/go-jmespath v0.4.0 github.com/jonboulle/clockwork v0.1.0 // indirect github.com/joyent/triton-go v0.0.0-20180313100802-d8f9c0314926 github.com/jtolds/gls v4.2.1+incompatible // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 - github.com/lib/pq v1.0.0 + github.com/lib/pq v1.8.0 github.com/likexian/gokit v0.20.15 github.com/lusis/go-artifactory v0.0.0-20160115162124-7e4ce345df82 github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 // indirect github.com/masterzen/winrm v0.0.0-20200615185753-c42b5136ff88 - github.com/mattn/go-colorable v0.1.8 github.com/mattn/go-isatty v0.0.12 github.com/mattn/go-shellwords v1.0.4 github.com/miekg/dns v1.0.8 // indirect - github.com/mitchellh/cli v1.1.0 + github.com/mitchellh/cli v1.1.2 github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db github.com/mitchellh/copystructure v1.0.0 github.com/mitchellh/go-homedir v1.1.0 @@ -97,12 +93,11 @@ require ( github.com/mitchellh/gox v1.0.1 github.com/mitchellh/mapstructure v1.1.2 github.com/mitchellh/panicwrap v1.0.0 - github.com/mitchellh/prefixedio v0.0.0-20190213213902-5733675afd51 github.com/mitchellh/reflectwalk v1.0.1 github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect github.com/packer-community/winrmcp v0.0.0-20180921211025-c76d91c1e7db github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c // indirect - github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 + github.com/pkg/browser v0.0.0-20201207095918-0426ae3fba23 github.com/pkg/errors v0.9.1 github.com/posener/complete v1.2.1 github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 // indirect @@ -114,33 +109,37 @@ require ( github.com/tencentcloud/tencentcloud-sdk-go v3.0.82+incompatible github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect - github.com/tombuildsstuff/giovanni v0.12.0 + github.com/tombuildsstuff/giovanni v0.15.1 github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5 // indirect github.com/xanzy/ssh-agent v0.2.1 github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 // indirect github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557 - github.com/zclconf/go-cty v1.6.2-0.20201013200640-e5225636c8c2 + github.com/zclconf/go-cty v1.8.3 + github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b github.com/zclconf/go-cty-yaml v1.0.2 go.uber.org/atomic v1.3.2 // indirect go.uber.org/multierr v1.1.0 // indirect go.uber.org/zap v1.9.1 // indirect - golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 - golang.org/x/mod v0.2.0 - golang.org/x/net v0.0.0-20200602114024-627f9648deb9 - golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 - golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd - golang.org/x/text v0.3.2 - golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371 - google.golang.org/api v0.9.0 - google.golang.org/grpc v1.27.1 + golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 + golang.org/x/mod v0.3.0 + golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 + golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 + golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 + golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf + golang.org/x/text v0.3.5 + golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb + google.golang.org/api v0.34.0 + google.golang.org/grpc v1.31.1 + google.golang.org/protobuf v1.25.0 gopkg.in/ini.v1 v1.42.0 // indirect - gopkg.in/yaml.v2 v2.2.8 k8s.io/api v0.0.0-20190620084959-7cf5895f2711 k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655 k8s.io/client-go v10.0.0+incompatible k8s.io/utils v0.0.0-20200411171748-3d5a2fe318e4 ) +replace google.golang.org/grpc v1.31.1 => google.golang.org/grpc v1.27.1 + replace k8s.io/client-go => k8s.io/client-go v0.0.0-20190620085101-78d2af792bab go 1.14 diff --git a/vendor/github.com/hashicorp/terraform/go.sum b/vendor/github.com/hashicorp/terraform/go.sum index 4866cfeb..3321fdf3 100644 --- a/vendor/github.com/hashicorp/terraform/go.sum +++ b/vendor/github.com/hashicorp/terraform/go.sum @@ -3,43 +3,84 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1 h1:lRi0CHyU+ytlvylOlFKKq0af6JncuyoRh1J+QJBqQx0= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -github.com/Azure/azure-sdk-for-go v45.0.0+incompatible h1:/bZYPaJLCqXeCqQqEeEIQg/p7RNafOhaVFhC6IWxZ/8= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v45.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v47.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v51.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v52.5.0+incompatible h1:/NLBWHCnIHtZyLPc1P7WIqi4Te4CC23kIQyK3Ep/7lA= +github.com/Azure/azure-sdk-for-go v52.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.3 h1:fyYnmYujkIXUgv88D9/Wo2ybE4Zwd/TmQd5sSI5u2Ws= github.com/Azure/go-autorest/autorest v0.11.3/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest/adal v0.9.0 h1:SigMbuFNuKgc1xcGhaeapbh+8fgsu+GxgDRFyg7f5lM= +github.com/Azure/go-autorest/autorest v0.11.10/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= +github.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM= +github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.0 h1:Ml+UCrnlKD+cJmSzrZ/RDcDw86NjkRUpnFh7V5JUhzU= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q= +github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= github.com/Azure/go-autorest/autorest/azure/cli v0.4.0/go.mod h1:JljT387FplPzBA31vUcvsetLKF3pec5bdAxjVU4kI2s= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.0 h1:z20OWOSG5aCye0HEkDp6TPmP17ZcfeMxPi6HnSALa8c= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.0 h1:3I9AAI63HfcLtphd9g39ruUwRI+Ca+z/f36KHPFRUss= github.com/Azure/go-autorest/autorest/validation v0.3.0/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.2.0 h1:e4RVHVZKC5p6UANLJHkM4OfR1UKZPj8Wt8Pcx+3oqrE= +github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= +github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4 h1:pSm8mp0T2OH2CPmPDPtwHPr3VAQaOwVF/JbllOPP4xA= github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022 h1:y8Gs8CzNfDF5AZvjr+5UyGQvQEBL7pwo+v+wX6q9JI8= github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4= github.com/ChrisTrenkamp/goxpath v0.0.0-20190607011252-c5096ec8773d h1:W1diKnDQkXxNDhghdBSbQ4LI/E1aJNTwpqPp3KtlB8w= github.com/ChrisTrenkamp/goxpath v0.0.0-20190607011252-c5096ec8773d/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4= +github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= +github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= @@ -66,10 +107,12 @@ github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/Y github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFUye+ZcSR6opIgz9Co7WcDx6ZcY+RjfFHoA0I= github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= +github.com/apparentlymart/go-shquot v0.0.1 h1:MGV8lwxF4zw75lN7e0MGs7o6AFYn7L6AZaExUpLh0Mo= +github.com/apparentlymart/go-shquot v0.0.1/go.mod h1:lw58XsE5IgUXZ9h0cxnypdx31p9mPFIVEQ9P3c7MlrU= github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0= github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= -github.com/apparentlymart/go-textseg/v12 v12.0.0 h1:bNEQyAGak9tojivJNkoqWErVCQbjdL7GzRt3F8NvfJ0= -github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= +github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= +github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/apparentlymart/go-userdirs v0.0.0-20200915174352-b0c018a67c13 h1:JtuelWqyixKApmXm3qghhZ7O96P6NKpyrlSIe8Rwnhw= github.com/apparentlymart/go-userdirs v0.0.0-20200915174352-b0c018a67c13/go.mod h1:7kfpUbyCdGJ9fDRCp3fopPQi5+cKNHgTE4ZuNrO71Cw= github.com/apparentlymart/go-versions v1.0.1 h1:ECIpSn0adcYNsBfSRwdDdz9fWlL+S/6EUd9+irwkBgU= @@ -82,8 +125,9 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= -github.com/aws/aws-sdk-go v1.31.9 h1:n+b34ydVfgC30j0Qm69yaapmjejQPW2BoDBX7Uy/tLI= github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.37.0 h1:GzFnhOIsrGyQ69s7VgqtrG2BG8v7X7vwB3Xpbd/DBBk= +github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= @@ -105,6 +149,7 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/coreos/bbolt v1.3.0 h1:HIgH5xUWXT914HCI671AxuTTqjj64UOFr7pHn48LUTI= github.com/coreos/bbolt v1.3.0/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= @@ -122,8 +167,9 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= +github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/dylanmei/iso8601 v0.1.0 h1:812NGQDBcqquTfH5Yeo7lwR0nzx/cKdsmf3qMjPURUI= github.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ= @@ -131,16 +177,23 @@ github.com/dylanmei/winrmtest v0.0.0-20190225150635-99b7fe2fddf1 h1:r1oACdS2XYiA github.com/dylanmei/winrmtest v0.0.0-20190225150635-99b7fe2fddf1/go.mod h1:lcy9/2gH1jn/VCLouHA6tOEwLoNVd4GW6zhuKLmHC2Y= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= @@ -153,7 +206,6 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v3.3.0+incompatible h1:8K4tyRfvU1CYPgJsveYFQMhpFd/wXNM7iK6rR7UHz84= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -164,27 +216,46 @@ github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 h1:u4bArs140e9+AfE52mFHOXVFnOSBJBRlzTHrOPLOIhE= -github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= @@ -195,11 +266,21 @@ github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -226,28 +307,31 @@ github.com/hashicorp/aws-sdk-go-base v0.6.0 h1:qmUbzM36msbBF59YctwuO5w0M2oNXjlil github.com/hashicorp/aws-sdk-go-base v0.6.0/go.mod h1:2fRjWDv3jJBeN6mVWFHV6hFTNeFBx2gpDLQaZNxUVAY= github.com/hashicorp/consul v0.0.0-20171026175957-610f3c86a089 h1:1eDpXAxTh0iPv+1kc9/gfSI2pxRERDsTk/lNGolwHn8= github.com/hashicorp/consul v0.0.0-20171026175957-610f3c86a089/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-azure-helpers v0.12.0 h1:7D0mFSyP3EfHu1ySubserIsnUWY87HMzzTWOB7ASwRU= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-azure-helpers v0.12.0/go.mod h1:Zc3v4DNeX6PDdy7NljlYpnrdac1++qNW0I4U+ofGwpg= +github.com/hashicorp/go-azure-helpers v0.14.0 h1:CdC2QqxK/Vk32YS5XMKXHjnpbtNIUCUv/PoSVQHx5jY= +github.com/hashicorp/go-azure-helpers v0.14.0/go.mod h1:kR7+sTDEb9TOp/O80ss1UEJg1t4/BHLD/U8wHLS4BGQ= github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-getter v1.4.2-0.20200106182914-9813cbd4eb02 h1:l1KB3bHVdvegcIf5upQ5mjcHjs2qsWnKh4Yr9xgIuu8= -github.com/hashicorp/go-getter v1.4.2-0.20200106182914-9813cbd4eb02/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY= -github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= -github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU= +github.com/hashicorp/go-getter v1.5.2 h1:XDo8LiAcDisiqZdv0TKgz+HtX3WN7zA2JD1R1tjsabE= +github.com/hashicorp/go-getter v1.5.2/go.mod h1:orNH3BTYLu/fIxGIdLjLoAJHWMDQ/UKQr5O4m3iBuoo= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.15.0 h1:qMuK0wxsoW4D0ddCCYwPSTm4KQv1X1ke3WmPWZ0Mvsk= +github.com/hashicorp/go-hclog v0.15.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v0.0.0-20180129170900-7f3cd4390caa h1:0nA8i+6Rwqaq9xlpmVxxTwk6rxiEhX+E6Wh4vPNHiS8= github.com/hashicorp/go-immutable-radix v0.0.0-20180129170900-7f3cd4390caa/go.mod h1:6ij3Z20p+OhOkCSrA0gImAWoHYQRGbnlcuk6XYTiaRw= github.com/hashicorp/go-msgpack v0.5.4 h1:SFT72YqIkOcLdWJUYcriVX7hbrZpwc/f7h8aW2NUqrA= github.com/hashicorp/go-msgpack v0.5.4/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-plugin v1.3.0 h1:4d/wJojzvHV1I4i/rrjVaeuyxWrLzDE1mDCyDy8fXS8= -github.com/hashicorp/go-plugin v1.3.0/go.mod h1:F9eH4LrE/ZsRdbwhfjs9k9HoDUwAHnYtXdgmf1AVNs0= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.4.1 h1:6UltRQlLN9iZO513VveELp5xyaFxVD2+1OVylE+2E+w= +github.com/hashicorp/go-plugin v1.4.1/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= github.com/hashicorp/go-retryablehttp v0.5.2 h1:AoISa4P4IsW0/m4T6St8Yw38gTl5GtBAgfkhYh1xAz4= github.com/hashicorp/go-retryablehttp v0.5.2/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= @@ -258,44 +342,52 @@ github.com/hashicorp/go-slug v0.4.1 h1:/jAo8dNuLgSImoLXaX7Od7QB4TfYCVPam+OpAt5bZ github.com/hashicorp/go-slug v0.4.1/go.mod h1:I5tq5Lv0E2xcNXNkmx7BSfzi1PsJ2cNjs3cC3LwyhK8= github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 h1:7YOlAIO2YWnJZkQp7B5eFykaIY7C9JndqAFQyVV5BhM= github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-tfe v0.8.1 h1:J6ulpLaKPHrcnwudRjxvlMYIGzqQFlnPhg3SVFh5N4E= -github.com/hashicorp/go-tfe v0.8.1/go.mod h1:XAV72S4O1iP8BDaqiaPLmL2B4EE6almocnOn8E8stHc= +github.com/hashicorp/go-tfe v0.14.0 h1:TJi3tQ3B0qlZN1KqBYlxQ33LgdAbsmPR921ml4H2lDs= +github.com/hashicorp/go-tfe v0.14.0/go.mod h1:B71izbwmCZdhEo/GzHopCXN3P74cYv2tsff1mxY4J6c= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f h1:UdxlrJz4JOnY8W+DbLISwf2B8WXEolNRA8BGCwI9jws= github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90= -github.com/hashicorp/hcl/v2 v2.7.0 h1:IU8qz5UzZ1po3M1D9/Kq6S5zbDGVfI9bnzmC1ogKKmI= -github.com/hashicorp/hcl/v2 v2.7.0/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY= +github.com/hashicorp/hcl/v2 v2.10.0 h1:1S1UnuhDGlv3gRFV4+0EdwB+znNP5HmcGbIqwnSCByg= +github.com/hashicorp/hcl/v2 v2.10.0/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg= +github.com/hashicorp/jsonapi v0.0.0-20210420151930-edf82c9774bf h1:EsVVE/vPelkJ83dk/Y3CeMbH/yPR2S8bLzMtxUoMFGI= +github.com/hashicorp/jsonapi v0.0.0-20210420151930-edf82c9774bf/go.mod h1:Yog5+CPEM3c99L1CL2CFCYoSzgWm5vTU58idbRUaLik= github.com/hashicorp/memberlist v0.1.0 h1:qSsCiC0WYD39lbSitKNt40e30uorm2Ss/d4JGU1hzH8= github.com/hashicorp/memberlist v0.1.0/go.mod h1:ncdBp14cuox2iFOq3kDiquKU6fqsTBc3W6JvZwjxxsE= github.com/hashicorp/serf v0.0.0-20160124182025-e4ec8cc423bb h1:ZbgmOQt8DOg796figP87/EFCVx2v2h9yRvwHF/zceX4= github.com/hashicorp/serf v0.0.0-20160124182025-e4ec8cc423bb/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE= -github.com/hashicorp/terraform-config-inspect v0.0.0-20191212124732-c6ae6269b9d7 h1:Pc5TCv9mbxFN6UVX0LH6CpQrdTM5YjbVI2w15237Pjk= -github.com/hashicorp/terraform-config-inspect v0.0.0-20191212124732-c6ae6269b9d7/go.mod h1:p+ivJws3dpqbp1iP84+npOyAmTTOLMgCzrXd3GSdn/A= +github.com/hashicorp/terraform-config-inspect v0.0.0-20210209133302-4fd17a0faac2 h1:l+bLFvHjqtgNQwWxwrFX9PemGAAO2P1AGZM7zlMNvCs= +github.com/hashicorp/terraform-config-inspect v0.0.0-20210209133302-4fd17a0faac2/go.mod h1:Z0Nnk4+3Cy89smEbrq+sl1bxc9198gIP4I7wcQF6Kqs= github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 h1:HKLsbzeOsfXmKNpr3GiT18XAblV0BjCbzL8KQAMZGa0= github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg= -github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/joyent/triton-go v0.0.0-20180313100802-d8f9c0314926 h1:kie3qOosvRKqwij2HGzXWffwpXvcqfPPXRUw8I4F/mg= @@ -305,6 +397,8 @@ github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBv github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -312,6 +406,8 @@ github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uia github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.11.2 h1:MiK62aErc3gIiVEtyzKfeOHgW7atJb5g/KNX5m3c2nQ= +github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -323,8 +419,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg= +github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/likexian/gokit v0.0.0-20190309162924-0a377eecf7aa/go.mod h1:QdfYv6y6qPA9pbBA2qXtoT8BMKha6UyNbxWGWl/9Jfk= github.com/likexian/gokit v0.0.0-20190418170008-ace88ad0983b/go.mod h1:KKqSnk/VVSW8kEyO2vVCXoanzEutKdlBAPohmGXkxCk= github.com/likexian/gokit v0.0.0-20190501133040-e77ea8b19cdc/go.mod h1:3kvONayqCaj+UgrRZGpgfXzHdMYCAO0KAt4/8n0L57Y= @@ -336,16 +432,14 @@ github.com/likexian/simplejson-go v0.0.0-20190502021454-d8787b4bfa0b/go.mod h1:3 github.com/lusis/go-artifactory v0.0.0-20160115162124-7e4ce345df82 h1:wnfcqULT+N2seWf6y4yHzmi7GD2kNx4Ute0qArktD48= github.com/lusis/go-artifactory v0.0.0-20160115162124-7e4ce345df82/go.mod h1:y54tfGmO3NKssKveTEFFzH8C/akrSOy/iW9qEAUDV84= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9 h1:SmVbOZFWAlyQshuMfOkiAx1f5oUTsOGG5IXplAEYeeM= github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL43sCxI2jhPLRv73oVVOjEKZjKkflyqxg= github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc= github.com/masterzen/winrm v0.0.0-20200615185753-c42b5136ff88 h1:cxuVcCvCLD9yYDbRCWw0jSgh1oT6P6mv3aJDKK5o7X4= github.com/masterzen/winrm v0.0.0-20200615185753-c42b5136ff88/go.mod h1:a2HXwefeat3evJHxFXSayvRHpYEPJYtErl4uIzfaUqY= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -359,8 +453,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.8 h1:Zi8HNpze3NeRWH1PQV6O71YcvJRQ6j0lORO6DAEmAAI= github.com/miekg/dns v1.0.8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.1.0 h1:tEElEatulEHDeedTxwckzyYMA5c86fbmNIUL1hBIiTg= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/cli v1.1.2 h1:PvH+lL2B7IQ101xQL63Of8yFS2y+aDlsFcsqNc+u/Kw= +github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= @@ -384,9 +478,6 @@ github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQz github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/panicwrap v1.0.0 h1:67zIyVakCIvcs69A0FGfZjBdPleaonSgGlXRSRlb6fE= github.com/mitchellh/panicwrap v1.0.0/go.mod h1:pKvZHwWrZowLUzftuFq7coarnxbBXU4aQh3N0BJOeeA= -github.com/mitchellh/prefixedio v0.0.0-20190213213902-5733675afd51 h1:eD92Am0Qf3rqhsOeA1zwBHSfRkoHrt4o6uORamdmJP8= -github.com/mitchellh/prefixedio v0.0.0-20190213213902-5733675afd51/go.mod h1:kB1naBgV9ORnkiTVeyJOI1DavaJkG4oNIq0Af6ZVKUo= -github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -417,8 +508,8 @@ github.com/packer-community/winrmcp v0.0.0-20180921211025-c76d91c1e7db/go.mod h1 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98= -github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= +github.com/pkg/browser v0.0.0-20201207095918-0426ae3fba23 h1:dofHuld+js7eKSemxqTVIo8yRlpRw+H1SdpzZxWruBc= +github.com/pkg/browser v0.0.0-20201207095918-0426ae3fba23/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -441,6 +532,7 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1 h1:/K3IL0Z1quvmJ7X0A1AwNEK7CRkVK3YwfOU/QAL4WGg= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= @@ -467,22 +559,22 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/svanharmelen/jsonapi v0.0.0-20180618144545-0c0828c3f16d h1:Z4EH+5EffvBEhh37F0C0DnpklTMh00JOkjW5zK3ofBI= -github.com/svanharmelen/jsonapi v0.0.0-20180618144545-0c0828c3f16d/go.mod h1:BSTlc8jOjh0niykqEGVXOLXdi9o0r0kR8tCYiMvjFgw= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tencentcloud/tencentcloud-sdk-go v3.0.82+incompatible h1:5Td2b0yfaOvw9M9nZ5Oav6Li9bxUNxt4DgxMfIPpsa0= github.com/tencentcloud/tencentcloud-sdk-go v3.0.82+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4= github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c h1:iRD1CqtWUjgEVEmjwTMbP1DMzz1HRytOsgx/rlw/vNs= github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c/go.mod h1:wk2XFUg6egk4tSDNZtXeKfe2G6690UVyt163PuUxBZk= github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc= github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tombuildsstuff/giovanni v0.12.0 h1:PZmiH+twM6C0/fN5Q6gaE2OUaKjh+ypVesjGMkHZgzE= -github.com/tombuildsstuff/giovanni v0.12.0/go.mod h1:qJ5dpiYWkRsuOSXO8wHbee7+wElkLNfWVolcf59N84E= +github.com/tombuildsstuff/giovanni v0.15.1 h1:CVRaLOJ7C/eercCrKIsarfJ4SZoGMdBL9Q2deFDUXco= +github.com/tombuildsstuff/giovanni v0.15.1/go.mod h1:0TZugJPEtqzPlMpuJHYfXY6Dq2uLPrXf98D2XQSxNbA= github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5 h1:cMjKdf4PxEBN9K5HaD9UMW8gkTbM0kMzkTa9SJe0WNQ= github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= -github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= -github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= +github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U= @@ -495,16 +587,26 @@ github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 h1:MPPkRncZLN9Kh4M github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557 h1:Jpn2j6wHkC9wJv5iMfJhKqrZJx3TahFx+7sbZ7zQdxs= github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= -github.com/zclconf/go-cty v1.6.2-0.20201013200640-e5225636c8c2 h1:17Cl7LwqZt2t95i3qzcMlOWH8LfzMqaJDjjRV9N/ae4= -github.com/zclconf/go-cty v1.6.2-0.20201013200640-e5225636c8c2/go.mod h1:VDR4+I79ubFBGm1uJac1226K5yANQFHeauxPBoP54+o= +github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= +github.com/zclconf/go-cty v1.8.3 h1:48gwZXrdSADU2UW9eZKHprxAI7APZGW9XmExpJpSjT0= +github.com/zclconf/go-cty v1.8.3/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= +github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY34Wul7O/MSKey3txpPYyCqVO5ZyceuQJEI= +github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= github.com/zclconf/go-cty-yaml v1.0.2 h1:dNyg4QLTrv2IfJpm7Wtxi55ed5gLGOlPrZ6kMd51hY0= github.com/zclconf/go-cty-yaml v1.0.2/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= @@ -517,22 +619,49 @@ golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -551,27 +680,47 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM= -golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 h1:ld7aEMNHoBnnDAX15v1T6z31v8HwR2A9FYOuAhWqkwc= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -589,23 +738,49 @@ golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -618,26 +793,70 @@ golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371 h1:Cjq6sG3gnKDchzWy7ouGQklhxMtWvh4AhSNJ0qGIeo4= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb h1:KVWk3RW1AZlxWum4tYqegLgwJHb5oouozcGM8HfNQaw= +golang.org/x/tools v0.0.0-20201028111035-eafbe7b904eb/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0 h1:jbyannxz0XFD3zdjgrSUsaJbgpH4eTrkdhRChkHPfO8= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.34.0 h1:k40adF3uR+6x/+hO5Dh4ZFUqFp67vxvbpafFiJxl10A= +google.golang.org/api v0.34.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -645,21 +864,63 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d h1:92D1fum1bJLKSdr11OJ+54YeCMCGYIygTA7R/YZxH5M= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o= gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= @@ -672,12 +933,18 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.0.0-20190620084959-7cf5895f2711 h1:BblVYz/wE5WtBsD/Gvu54KyBUTJMflolzc5I2DTvh50= k8s.io/api v0.0.0-20190620084959-7cf5895f2711/go.mod h1:TBhBqb1AWbBQbW3XRusr7n7E4v2+5ZY8r8sAMnyFC5A= k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719/go.mod h1:I4A+glKBHiTgiEjQiCCQfCAIcIMFGt291SmsvcrFzJA= @@ -697,6 +964,8 @@ k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3 k8s.io/utils v0.0.0-20200411171748-3d5a2fe318e4 h1:vEYeh6f+jz98bCG4BHRQ733tuZpjzsJ+C/xv8awA0qM= k8s.io/utils v0.0.0-20200411171748-3d5a2fe318e4/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/vendor/github.com/hashicorp/terraform/help.go b/vendor/github.com/hashicorp/terraform/help.go index e020edea..16bc3209 100644 --- a/vendor/github.com/hashicorp/terraform/help.go +++ b/vendor/github.com/hashicorp/terraform/help.go @@ -13,84 +13,81 @@ import ( // helpFunc is a cli.HelpFunc that can is used to output the help for Terraform. func helpFunc(commands map[string]cli.CommandFactory) string { // Determine the maximum key length, and classify based on type - porcelain := make(map[string]cli.CommandFactory) - plumbing := make(map[string]cli.CommandFactory) + var otherCommands []string maxKeyLen := 0 - for key, f := range commands { + + for key := range commands { + if _, ok := HiddenCommands[key]; ok { + // We don't consider hidden commands when deciding the + // maximum command length. + continue + } + if len(key) > maxKeyLen { maxKeyLen = len(key) } - if _, ok := PlumbingCommands[key]; ok { - plumbing[key] = f - } else { - porcelain[key] = f + isOther := true + for _, candidate := range PrimaryCommands { + if candidate == key { + isOther = false + break + } + } + if isOther { + otherCommands = append(otherCommands, key) } } + sort.Strings(otherCommands) // The output produced by this is included in the docs at - // website/source/docs/commands/index.html.markdown; if you + // website/source/docs/cli/commands/index.html.markdown; if you // change this then consider updating that to match. helpText := fmt.Sprintf(` Usage: terraform [global options] [args] The available commands for execution are listed below. -The most common, useful commands are shown first, followed by -less common or more advanced commands. If you're just getting -started with Terraform, stick with the common commands. For the -other commands, please read the help and docs before usage. +The primary workflow commands are given first, followed by +less common or more advanced commands. -Common commands: +Main commands: %s All other commands: %s - Global options (use these before the subcommand, if any): - -chdir=DIR Switch to a different working directory before executing - the given subcommand. - -help Show this help output, or the help for a specified - subcommand. - -version An alias for the "version" subcommand. -`, listCommands(porcelain, maxKeyLen), listCommands(plumbing, maxKeyLen)) + -chdir=DIR Switch to a different working directory before executing the + given subcommand. + -help Show this help output, or the help for a specified subcommand. + -version An alias for the "version" subcommand. +`, listCommands(commands, PrimaryCommands, maxKeyLen), listCommands(commands, otherCommands, maxKeyLen)) return strings.TrimSpace(helpText) } // listCommands just lists the commands in the map with the // given maximum key length. -func listCommands(commands map[string]cli.CommandFactory, maxKeyLen int) string { +func listCommands(allCommands map[string]cli.CommandFactory, order []string, maxKeyLen int) string { var buf bytes.Buffer - // Get the list of keys so we can sort them, and also get the maximum - // key length so they can be aligned properly. - keys := make([]string, 0, len(commands)) - for key, _ := range commands { - // This is an internal command that users should never call directly so - // we will hide it from the command listing. - if key == "internal-plugin" { - continue - } - keys = append(keys, key) - } - sort.Strings(keys) - - for _, key := range keys { - commandFunc, ok := commands[key] + for _, key := range order { + commandFunc, ok := allCommands[key] if !ok { - // This should never happen since we JUST built the list of - // keys. + // This suggests an inconsistency in the command table definitions + // in commands.go . panic("command not found: " + key) } command, err := commandFunc() if err != nil { + // This would be really weird since there's no good reason for + // any of our command factories to fail. log.Printf("[ERR] cli: Command '%s' failed to load: %s", key, err) continue } key = fmt.Sprintf("%s%s", key, strings.Repeat(" ", maxKeyLen-len(key))) - buf.WriteString(fmt.Sprintf(" %s %s\n", key, command.Synopsis())) + buf.WriteString(fmt.Sprintf(" %s %s\n", key, command.Synopsis())) } return buf.String() diff --git a/vendor/github.com/hashicorp/terraform/helper/README.md b/vendor/github.com/hashicorp/terraform/helper/README.md deleted file mode 100644 index 2bcbe88e..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Legacy Helper Libraries - -The packages in this directory are all legacy code. Some of them are legacy -because they are now maintained in -[the Terraform SDK](https://github.com/hashicorp/terraform-plugin-sdk), -while others are just obsolete codepaths that we intend to migrate away -from over time. - -Avoid using functions from packages under `helper/` in new projects. diff --git a/vendor/github.com/hashicorp/terraform/helper/experiment/experiment.go b/vendor/github.com/hashicorp/terraform/helper/experiment/experiment.go deleted file mode 100644 index 72fdeaf9..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/experiment/experiment.go +++ /dev/null @@ -1,158 +0,0 @@ -// experiment package contains helper functions for tracking experimental -// features throughout Terraform. -// -// This package should be used for creating, enabling, querying, and deleting -// experimental features. By unifying all of that onto a single interface, -// we can have the Go compiler help us by enforcing every place we touch -// an experimental feature. -// -// To create a new experiment: -// -// 1. Add the experiment to the global vars list below, prefixed with X_ -// -// 2. Add the experiment variable to the All listin the init() function -// -// 3. Use it! -// -// To remove an experiment: -// -// 1. Delete the experiment global var. -// -// 2. Try to compile and fix all the places where the var was referenced. -// -// To use an experiment: -// -// 1. Use Flag() if you want the experiment to be available from the CLI. -// -// 2. Use Enabled() to check whether it is enabled. -// -// As a general user: -// -// 1. The `-Xexperiment-name` flag -// 2. The `TF_X_` env var. -// 3. The `TF_X_FORCE` env var can be set to force an experimental feature -// without human verifications. -// -package experiment - -import ( - "flag" - "fmt" - "os" - "strconv" - "strings" - "sync" -) - -// The experiments that are available are listed below. Any package in -// Terraform defining an experiment should define the experiments below. -// By keeping them all within the experiment package we force a single point -// of definition and use. This allows the compiler to enforce references -// so it becomes easy to remove the features. -var ( - // Shadow graph. This is already on by default. Disabling it will be - // allowed for awhile in order for it to not block operations. - X_shadow = newBasicID("shadow", "SHADOW", false) - - // Concise plan diff output - X_concise_diff = newBasicID("concise_diff", "CONCISE_DIFF", true) -) - -// Global variables this package uses because we are a package -// with global state. -var ( - // all is the list of all experiements. Do not modify this. - All []ID - - // enabled keeps track of what flags have been enabled - enabled map[string]bool - enabledLock sync.Mutex - - // Hidden "experiment" that forces all others to be on without verification - x_force = newBasicID("force", "FORCE", false) -) - -func init() { - // The list of all experiments, update this when an experiment is added. - All = []ID{ - X_shadow, - X_concise_diff, - x_force, - } - - // Load - reload() -} - -// reload is used by tests to reload the global state. This is called by -// init publicly. -func reload() { - // Initialize - enabledLock.Lock() - enabled = make(map[string]bool) - enabledLock.Unlock() - - // Set defaults and check env vars - for _, id := range All { - // Get the default value - def := id.Default() - - // If we set it in the env var, default it to true - key := fmt.Sprintf("TF_X_%s", strings.ToUpper(id.Env())) - if v := os.Getenv(key); v != "" { - def = v != "0" - } - - // Set the default - SetEnabled(id, def) - } -} - -// Enabled returns whether an experiment has been enabled or not. -func Enabled(id ID) bool { - enabledLock.Lock() - defer enabledLock.Unlock() - return enabled[id.Flag()] -} - -// SetEnabled sets an experiment to enabled/disabled. Please check with -// the experiment docs for when calling this actually affects the experiment. -func SetEnabled(id ID, v bool) { - enabledLock.Lock() - defer enabledLock.Unlock() - enabled[id.Flag()] = v -} - -// Force returns true if the -Xforce of TF_X_FORCE flag is present, which -// advises users of this package to not verify with the user that they want -// experimental behavior and to just continue with it. -func Force() bool { - return Enabled(x_force) -} - -// Flag configures the given FlagSet with the flags to configure -// all active experiments. -func Flag(fs *flag.FlagSet) { - for _, id := range All { - desc := id.Flag() - key := fmt.Sprintf("X%s", id.Flag()) - fs.Var(&idValue{X: id}, key, desc) - } -} - -// idValue implements flag.Value for setting the enabled/disabled state -// of an experiment from the CLI. -type idValue struct { - X ID -} - -func (v *idValue) IsBoolFlag() bool { return true } -func (v *idValue) String() string { return strconv.FormatBool(Enabled(v.X)) } -func (v *idValue) Set(raw string) error { - b, err := strconv.ParseBool(raw) - if err == nil { - SetEnabled(v.X, b) - } - - return err -} diff --git a/vendor/github.com/hashicorp/terraform/helper/experiment/experiment_test.go b/vendor/github.com/hashicorp/terraform/helper/experiment/experiment_test.go deleted file mode 100644 index 32055c2c..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/experiment/experiment_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package experiment - -import ( - "flag" - "fmt" - "os" - "testing" -) - -// Test experiments -var ( - X_test1 = newBasicID("test1", "TEST1", false) - X_test2 = newBasicID("test2", "TEST2", true) -) - -// Reinitializes the package to a clean slate -func testReinit() { - All = []ID{X_test1, X_test2, x_force} - reload() -} - -func init() { - testReinit() - - // Clear all env vars so they don't affect tests - for _, id := range All { - os.Unsetenv(fmt.Sprintf("TF_X_%s", id.Env())) - } -} - -func TestDefault(t *testing.T) { - testReinit() - - if Enabled(X_test1) { - t.Fatal("test1 should not be enabled") - } - - if !Enabled(X_test2) { - t.Fatal("test2 should be enabled") - } -} - -func TestEnv(t *testing.T) { - os.Setenv("TF_X_TEST2", "0") - defer os.Unsetenv("TF_X_TEST2") - - testReinit() - - if Enabled(X_test2) { - t.Fatal("test2 should be enabled") - } -} - -func TestFlag(t *testing.T) { - testReinit() - - // Verify default - if !Enabled(X_test2) { - t.Fatal("test2 should be enabled") - } - - // Setup a flag set - fs := flag.NewFlagSet("test", flag.ContinueOnError) - Flag(fs) - fs.Parse([]string{"-Xtest2=false"}) - - if Enabled(X_test2) { - t.Fatal("test2 should not be enabled") - } -} - -func TestFlag_overEnv(t *testing.T) { - os.Setenv("TF_X_TEST2", "1") - defer os.Unsetenv("TF_X_TEST2") - - testReinit() - - // Verify default - if !Enabled(X_test2) { - t.Fatal("test2 should be enabled") - } - - // Setup a flag set - fs := flag.NewFlagSet("test", flag.ContinueOnError) - Flag(fs) - fs.Parse([]string{"-Xtest2=false"}) - - if Enabled(X_test2) { - t.Fatal("test2 should not be enabled") - } -} - -func TestForce(t *testing.T) { - os.Setenv("TF_X_FORCE", "1") - defer os.Unsetenv("TF_X_FORCE") - - testReinit() - - if !Force() { - t.Fatal("should force") - } -} - -func TestForce_flag(t *testing.T) { - os.Unsetenv("TF_X_FORCE") - - testReinit() - - // Setup a flag set - fs := flag.NewFlagSet("test", flag.ContinueOnError) - Flag(fs) - fs.Parse([]string{"-Xforce"}) - - if !Force() { - t.Fatal("should force") - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/experiment/id.go b/vendor/github.com/hashicorp/terraform/helper/experiment/id.go deleted file mode 100644 index 8e2f7073..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/experiment/id.go +++ /dev/null @@ -1,34 +0,0 @@ -package experiment - -// ID represents an experimental feature. -// -// The global vars defined on this package should be used as ID values. -// This interface is purposely not implement-able outside of this package -// so that we can rely on the Go compiler to enforce all experiment references. -type ID interface { - Env() string - Flag() string - Default() bool - - unexported() // So the ID can't be implemented externally. -} - -// basicID implements ID. -type basicID struct { - EnvValue string - FlagValue string - DefaultValue bool -} - -func newBasicID(flag, env string, def bool) ID { - return &basicID{ - EnvValue: env, - FlagValue: flag, - DefaultValue: def, - } -} - -func (id *basicID) Env() string { return id.EnvValue } -func (id *basicID) Flag() string { return id.FlagValue } -func (id *basicID) Default() bool { return id.DefaultValue } -func (id *basicID) unexported() {} diff --git a/vendor/github.com/hashicorp/terraform/helper/plugin/doc.go b/vendor/github.com/hashicorp/terraform/helper/plugin/doc.go deleted file mode 100644 index 82b5937b..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/plugin/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// Package plugin contains types and functions to help Terraform plugins -// implement the plugin rpc interface. -// The primary Provider type will be responsible for converting from the grpc -// wire protocol to the types and methods known to the provider -// implementations. -package plugin diff --git a/vendor/github.com/hashicorp/terraform/helper/plugin/grpc_provider.go b/vendor/github.com/hashicorp/terraform/helper/plugin/grpc_provider.go deleted file mode 100644 index 06ebaf42..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/plugin/grpc_provider.go +++ /dev/null @@ -1,1436 +0,0 @@ -package plugin - -import ( - "encoding/json" - "fmt" - "log" - "strconv" - - "github.com/zclconf/go-cty/cty" - ctyconvert "github.com/zclconf/go-cty/cty/convert" - "github.com/zclconf/go-cty/cty/msgpack" - context "golang.org/x/net/context" - - "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/configs/hcl2shim" - "github.com/hashicorp/terraform/helper/schema" - proto "github.com/hashicorp/terraform/internal/tfplugin5" - "github.com/hashicorp/terraform/plans/objchange" - "github.com/hashicorp/terraform/plugin/convert" - "github.com/hashicorp/terraform/terraform" -) - -const newExtraKey = "_new_extra_shim" - -// NewGRPCProviderServerShim wraps a terraform.ResourceProvider in a -// proto.ProviderServer implementation. If the provided provider is not a -// *schema.Provider, this will return nil, -func NewGRPCProviderServerShim(p terraform.ResourceProvider) *GRPCProviderServer { - sp, ok := p.(*schema.Provider) - if !ok { - return nil - } - - return &GRPCProviderServer{ - provider: sp, - } -} - -// GRPCProviderServer handles the server, or plugin side of the rpc connection. -type GRPCProviderServer struct { - provider *schema.Provider -} - -func (s *GRPCProviderServer) GetSchema(_ context.Context, req *proto.GetProviderSchema_Request) (*proto.GetProviderSchema_Response, error) { - // Here we are certain that the provider is being called through grpc, so - // make sure the feature flag for helper/schema is set - schema.SetProto5() - - resp := &proto.GetProviderSchema_Response{ - ResourceSchemas: make(map[string]*proto.Schema), - DataSourceSchemas: make(map[string]*proto.Schema), - } - - resp.Provider = &proto.Schema{ - Block: convert.ConfigSchemaToProto(s.getProviderSchemaBlock()), - } - - resp.ProviderMeta = &proto.Schema{ - Block: convert.ConfigSchemaToProto(s.getProviderMetaSchemaBlock()), - } - - for typ, res := range s.provider.ResourcesMap { - resp.ResourceSchemas[typ] = &proto.Schema{ - Version: int64(res.SchemaVersion), - Block: convert.ConfigSchemaToProto(res.CoreConfigSchema()), - } - } - - for typ, dat := range s.provider.DataSourcesMap { - resp.DataSourceSchemas[typ] = &proto.Schema{ - Version: int64(dat.SchemaVersion), - Block: convert.ConfigSchemaToProto(dat.CoreConfigSchema()), - } - } - - return resp, nil -} - -func (s *GRPCProviderServer) getProviderSchemaBlock() *configschema.Block { - return schema.InternalMap(s.provider.Schema).CoreConfigSchema() -} - -func (s *GRPCProviderServer) getProviderMetaSchemaBlock() *configschema.Block { - return schema.InternalMap(s.provider.ProviderMetaSchema).CoreConfigSchema() -} - -func (s *GRPCProviderServer) getResourceSchemaBlock(name string) *configschema.Block { - res := s.provider.ResourcesMap[name] - return res.CoreConfigSchema() -} - -func (s *GRPCProviderServer) getDatasourceSchemaBlock(name string) *configschema.Block { - dat := s.provider.DataSourcesMap[name] - return dat.CoreConfigSchema() -} - -func (s *GRPCProviderServer) PrepareProviderConfig(_ context.Context, req *proto.PrepareProviderConfig_Request) (*proto.PrepareProviderConfig_Response, error) { - resp := &proto.PrepareProviderConfig_Response{} - - schemaBlock := s.getProviderSchemaBlock() - - configVal, err := msgpack.Unmarshal(req.Config.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // lookup any required, top-level attributes that are Null, and see if we - // have a Default value available. - configVal, err = cty.Transform(configVal, func(path cty.Path, val cty.Value) (cty.Value, error) { - // we're only looking for top-level attributes - if len(path) != 1 { - return val, nil - } - - // nothing to do if we already have a value - if !val.IsNull() { - return val, nil - } - - // get the Schema definition for this attribute - getAttr, ok := path[0].(cty.GetAttrStep) - // these should all exist, but just ignore anything strange - if !ok { - return val, nil - } - - attrSchema := s.provider.Schema[getAttr.Name] - // continue to ignore anything that doesn't match - if attrSchema == nil { - return val, nil - } - - // this is deprecated, so don't set it - if attrSchema.Deprecated != "" || attrSchema.Removed != "" { - return val, nil - } - - // find a default value if it exists - def, err := attrSchema.DefaultValue() - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, fmt.Errorf("error getting default for %q: %s", getAttr.Name, err)) - return val, err - } - - // no default - if def == nil { - return val, nil - } - - // create a cty.Value and make sure it's the correct type - tmpVal := hcl2shim.HCL2ValueFromConfigValue(def) - - // helper/schema used to allow setting "" to a bool - if val.Type() == cty.Bool && tmpVal.RawEquals(cty.StringVal("")) { - // return a warning about the conversion - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, "provider set empty string as default value for bool "+getAttr.Name) - tmpVal = cty.False - } - - val, err = ctyconvert.Convert(tmpVal, val.Type()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, fmt.Errorf("error setting default for %q: %s", getAttr.Name, err)) - } - - return val, err - }) - if err != nil { - // any error here was already added to the diagnostics - return resp, nil - } - - configVal, err = schemaBlock.CoerceValue(configVal) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // Ensure there are no nulls that will cause helper/schema to panic. - if err := validateConfigNulls(configVal, nil); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - config := terraform.NewResourceConfigShimmed(configVal, schemaBlock) - - warns, errs := s.provider.Validate(config) - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs)) - - preparedConfigMP, err := msgpack.Marshal(configVal, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - resp.PreparedConfig = &proto.DynamicValue{Msgpack: preparedConfigMP} - - return resp, nil -} - -func (s *GRPCProviderServer) ValidateResourceTypeConfig(_ context.Context, req *proto.ValidateResourceTypeConfig_Request) (*proto.ValidateResourceTypeConfig_Response, error) { - resp := &proto.ValidateResourceTypeConfig_Response{} - - schemaBlock := s.getResourceSchemaBlock(req.TypeName) - - configVal, err := msgpack.Unmarshal(req.Config.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - config := terraform.NewResourceConfigShimmed(configVal, schemaBlock) - - warns, errs := s.provider.ValidateResource(req.TypeName, config) - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs)) - - return resp, nil -} - -func (s *GRPCProviderServer) ValidateDataSourceConfig(_ context.Context, req *proto.ValidateDataSourceConfig_Request) (*proto.ValidateDataSourceConfig_Response, error) { - resp := &proto.ValidateDataSourceConfig_Response{} - - schemaBlock := s.getDatasourceSchemaBlock(req.TypeName) - - configVal, err := msgpack.Unmarshal(req.Config.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // Ensure there are no nulls that will cause helper/schema to panic. - if err := validateConfigNulls(configVal, nil); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - config := terraform.NewResourceConfigShimmed(configVal, schemaBlock) - - warns, errs := s.provider.ValidateDataSource(req.TypeName, config) - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs)) - - return resp, nil -} - -func (s *GRPCProviderServer) UpgradeResourceState(_ context.Context, req *proto.UpgradeResourceState_Request) (*proto.UpgradeResourceState_Response, error) { - resp := &proto.UpgradeResourceState_Response{} - - res := s.provider.ResourcesMap[req.TypeName] - schemaBlock := s.getResourceSchemaBlock(req.TypeName) - - version := int(req.Version) - - jsonMap := map[string]interface{}{} - var err error - - switch { - // We first need to upgrade a flatmap state if it exists. - // There should never be both a JSON and Flatmap state in the request. - case len(req.RawState.Flatmap) > 0: - jsonMap, version, err = s.upgradeFlatmapState(version, req.RawState.Flatmap, res) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - // if there's a JSON state, we need to decode it. - case len(req.RawState.Json) > 0: - err = json.Unmarshal(req.RawState.Json, &jsonMap) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - default: - log.Println("[DEBUG] no state provided to upgrade") - return resp, nil - } - - // complete the upgrade of the JSON states - jsonMap, err = s.upgradeJSONState(version, jsonMap, res) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // The provider isn't required to clean out removed fields - s.removeAttributes(jsonMap, schemaBlock.ImpliedType()) - - // now we need to turn the state into the default json representation, so - // that it can be re-decoded using the actual schema. - val, err := schema.JSONMapToStateValue(jsonMap, schemaBlock) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // Now we need to make sure blocks are represented correctly, which means - // that missing blocks are empty collections, rather than null. - // First we need to CoerceValue to ensure that all object types match. - val, err = schemaBlock.CoerceValue(val) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - // Normalize the value and fill in any missing blocks. - val = objchange.NormalizeObjectFromLegacySDK(val, schemaBlock) - - // encode the final state to the expected msgpack format - newStateMP, err := msgpack.Marshal(val, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - resp.UpgradedState = &proto.DynamicValue{Msgpack: newStateMP} - return resp, nil -} - -// upgradeFlatmapState takes a legacy flatmap state, upgrades it using Migrate -// state if necessary, and converts it to the new JSON state format decoded as a -// map[string]interface{}. -// upgradeFlatmapState returns the json map along with the corresponding schema -// version. -func (s *GRPCProviderServer) upgradeFlatmapState(version int, m map[string]string, res *schema.Resource) (map[string]interface{}, int, error) { - // this will be the version we've upgraded so, defaulting to the given - // version in case no migration was called. - upgradedVersion := version - - // first determine if we need to call the legacy MigrateState func - requiresMigrate := version < res.SchemaVersion - - schemaType := res.CoreConfigSchema().ImpliedType() - - // if there are any StateUpgraders, then we need to only compare - // against the first version there - if len(res.StateUpgraders) > 0 { - requiresMigrate = version < res.StateUpgraders[0].Version - } - - if requiresMigrate && res.MigrateState == nil { - // Providers were previously allowed to bump the version - // without declaring MigrateState. - // If there are further upgraders, then we've only updated that far. - if len(res.StateUpgraders) > 0 { - schemaType = res.StateUpgraders[0].Type - upgradedVersion = res.StateUpgraders[0].Version - } - } else if requiresMigrate { - is := &terraform.InstanceState{ - ID: m["id"], - Attributes: m, - Meta: map[string]interface{}{ - "schema_version": strconv.Itoa(version), - }, - } - - is, err := res.MigrateState(version, is, s.provider.Meta()) - if err != nil { - return nil, 0, err - } - - // re-assign the map in case there was a copy made, making sure to keep - // the ID - m := is.Attributes - m["id"] = is.ID - - // if there are further upgraders, then we've only updated that far - if len(res.StateUpgraders) > 0 { - schemaType = res.StateUpgraders[0].Type - upgradedVersion = res.StateUpgraders[0].Version - } - } else { - // the schema version may be newer than the MigrateState functions - // handled and older than the current, but still stored in the flatmap - // form. If that's the case, we need to find the correct schema type to - // convert the state. - for _, upgrader := range res.StateUpgraders { - if upgrader.Version == version { - schemaType = upgrader.Type - break - } - } - } - - // now we know the state is up to the latest version that handled the - // flatmap format state. Now we can upgrade the format and continue from - // there. - newConfigVal, err := hcl2shim.HCL2ValueFromFlatmap(m, schemaType) - if err != nil { - return nil, 0, err - } - - jsonMap, err := schema.StateValueToJSONMap(newConfigVal, schemaType) - return jsonMap, upgradedVersion, err -} - -func (s *GRPCProviderServer) upgradeJSONState(version int, m map[string]interface{}, res *schema.Resource) (map[string]interface{}, error) { - var err error - - for _, upgrader := range res.StateUpgraders { - if version != upgrader.Version { - continue - } - - m, err = upgrader.Upgrade(m, s.provider.Meta()) - if err != nil { - return nil, err - } - version++ - } - - return m, nil -} - -// Remove any attributes no longer present in the schema, so that the json can -// be correctly decoded. -func (s *GRPCProviderServer) removeAttributes(v interface{}, ty cty.Type) { - // we're only concerned with finding maps that corespond to object - // attributes - switch v := v.(type) { - case []interface{}: - // If these aren't blocks the next call will be a noop - if ty.IsListType() || ty.IsSetType() { - eTy := ty.ElementType() - for _, eV := range v { - s.removeAttributes(eV, eTy) - } - } - return - case map[string]interface{}: - // map blocks aren't yet supported, but handle this just in case - if ty.IsMapType() { - eTy := ty.ElementType() - for _, eV := range v { - s.removeAttributes(eV, eTy) - } - return - } - - if ty == cty.DynamicPseudoType { - log.Printf("[DEBUG] ignoring dynamic block: %#v\n", v) - return - } - - if !ty.IsObjectType() { - // This shouldn't happen, and will fail to decode further on, so - // there's no need to handle it here. - log.Printf("[WARN] unexpected type %#v for map in json state", ty) - return - } - - attrTypes := ty.AttributeTypes() - for attr, attrV := range v { - attrTy, ok := attrTypes[attr] - if !ok { - log.Printf("[DEBUG] attribute %q no longer present in schema", attr) - delete(v, attr) - continue - } - - s.removeAttributes(attrV, attrTy) - } - } -} - -func (s *GRPCProviderServer) Stop(_ context.Context, _ *proto.Stop_Request) (*proto.Stop_Response, error) { - resp := &proto.Stop_Response{} - - err := s.provider.Stop() - if err != nil { - resp.Error = err.Error() - } - - return resp, nil -} - -func (s *GRPCProviderServer) Configure(_ context.Context, req *proto.Configure_Request) (*proto.Configure_Response, error) { - resp := &proto.Configure_Response{} - - schemaBlock := s.getProviderSchemaBlock() - - configVal, err := msgpack.Unmarshal(req.Config.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - s.provider.TerraformVersion = req.TerraformVersion - - // Ensure there are no nulls that will cause helper/schema to panic. - if err := validateConfigNulls(configVal, nil); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - config := terraform.NewResourceConfigShimmed(configVal, schemaBlock) - err = s.provider.Configure(config) - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - - return resp, nil -} - -func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadResource_Request) (*proto.ReadResource_Response, error) { - resp := &proto.ReadResource_Response{ - // helper/schema did previously handle private data during refresh, but - // core is now going to expect this to be maintained in order to - // persist it in the state. - Private: req.Private, - } - - res := s.provider.ResourcesMap[req.TypeName] - schemaBlock := s.getResourceSchemaBlock(req.TypeName) - - stateVal, err := msgpack.Unmarshal(req.CurrentState.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - instanceState, err := res.ShimInstanceStateFromValue(stateVal) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - private := make(map[string]interface{}) - if len(req.Private) > 0 { - if err := json.Unmarshal(req.Private, &private); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - } - instanceState.Meta = private - - pmSchemaBlock := s.getProviderMetaSchemaBlock() - if pmSchemaBlock != nil && req.ProviderMeta != nil { - providerSchemaVal, err := msgpack.Unmarshal(req.ProviderMeta.Msgpack, pmSchemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - instanceState.ProviderMeta = providerSchemaVal - } - - newInstanceState, err := res.RefreshWithoutUpgrade(instanceState, s.provider.Meta()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - if newInstanceState == nil || newInstanceState.ID == "" { - // The old provider API used an empty id to signal that the remote - // object appears to have been deleted, but our new protocol expects - // to see a null value (in the cty sense) in that case. - newStateMP, err := msgpack.Marshal(cty.NullVal(schemaBlock.ImpliedType()), schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - } - resp.NewState = &proto.DynamicValue{ - Msgpack: newStateMP, - } - return resp, nil - } - - // helper/schema should always copy the ID over, but do it again just to be safe - newInstanceState.Attributes["id"] = newInstanceState.ID - - newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(newInstanceState.Attributes, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - newStateVal = normalizeNullValues(newStateVal, stateVal, false) - newStateVal = copyTimeoutValues(newStateVal, stateVal) - - newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - resp.NewState = &proto.DynamicValue{ - Msgpack: newStateMP, - } - - return resp, nil -} - -func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.PlanResourceChange_Request) (*proto.PlanResourceChange_Response, error) { - resp := &proto.PlanResourceChange_Response{} - - // This is a signal to Terraform Core that we're doing the best we can to - // shim the legacy type system of the SDK onto the Terraform type system - // but we need it to cut us some slack. This setting should not be taken - // forward to any new SDK implementations, since setting it prevents us - // from catching certain classes of provider bug that can lead to - // confusing downstream errors. - resp.LegacyTypeSystem = true - - res := s.provider.ResourcesMap[req.TypeName] - schemaBlock := s.getResourceSchemaBlock(req.TypeName) - - priorStateVal, err := msgpack.Unmarshal(req.PriorState.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - create := priorStateVal.IsNull() - - proposedNewStateVal, err := msgpack.Unmarshal(req.ProposedNewState.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // We don't usually plan destroys, but this can return early in any case. - if proposedNewStateVal.IsNull() { - resp.PlannedState = req.ProposedNewState - resp.PlannedPrivate = req.PriorPrivate - return resp, nil - } - - info := &terraform.InstanceInfo{ - Type: req.TypeName, - } - - priorState, err := res.ShimInstanceStateFromValue(priorStateVal) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - priorPrivate := make(map[string]interface{}) - if len(req.PriorPrivate) > 0 { - if err := json.Unmarshal(req.PriorPrivate, &priorPrivate); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - } - - priorState.Meta = priorPrivate - - pmSchemaBlock := s.getProviderMetaSchemaBlock() - if pmSchemaBlock != nil && req.ProviderMeta != nil { - providerSchemaVal, err := msgpack.Unmarshal(req.ProviderMeta.Msgpack, pmSchemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - priorState.ProviderMeta = providerSchemaVal - } - - // Ensure there are no nulls that will cause helper/schema to panic. - if err := validateConfigNulls(proposedNewStateVal, nil); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // turn the proposed state into a legacy configuration - cfg := terraform.NewResourceConfigShimmed(proposedNewStateVal, schemaBlock) - - diff, err := s.provider.SimpleDiff(info, priorState, cfg) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // if this is a new instance, we need to make sure ID is going to be computed - if create { - if diff == nil { - diff = terraform.NewInstanceDiff() - } - - diff.Attributes["id"] = &terraform.ResourceAttrDiff{ - NewComputed: true, - } - } - - if diff == nil || len(diff.Attributes) == 0 { - // schema.Provider.Diff returns nil if it ends up making a diff with no - // changes, but our new interface wants us to return an actual change - // description that _shows_ there are no changes. This is always the - // prior state, because we force a diff above if this is a new instance. - resp.PlannedState = req.PriorState - resp.PlannedPrivate = req.PriorPrivate - return resp, nil - } - - if priorState == nil { - priorState = &terraform.InstanceState{} - } - - // now we need to apply the diff to the prior state, so get the planned state - plannedAttrs, err := diff.Apply(priorState.Attributes, schemaBlock) - - plannedStateVal, err := hcl2shim.HCL2ValueFromFlatmap(plannedAttrs, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - plannedStateVal, err = schemaBlock.CoerceValue(plannedStateVal) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - plannedStateVal = normalizeNullValues(plannedStateVal, proposedNewStateVal, false) - - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - plannedStateVal = copyTimeoutValues(plannedStateVal, proposedNewStateVal) - - // The old SDK code has some imprecisions that cause it to sometimes - // generate differences that the SDK itself does not consider significant - // but Terraform Core would. To avoid producing weird do-nothing diffs - // in that case, we'll check if the provider as produced something we - // think is "equivalent" to the prior state and just return the prior state - // itself if so, thus ensuring that Terraform Core will treat this as - // a no-op. See the docs for ValuesSDKEquivalent for some caveats on its - // accuracy. - forceNoChanges := false - if hcl2shim.ValuesSDKEquivalent(priorStateVal, plannedStateVal) { - plannedStateVal = priorStateVal - forceNoChanges = true - } - - // if this was creating the resource, we need to set any remaining computed - // fields - if create { - plannedStateVal = SetUnknowns(plannedStateVal, schemaBlock) - } - - plannedMP, err := msgpack.Marshal(plannedStateVal, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - resp.PlannedState = &proto.DynamicValue{ - Msgpack: plannedMP, - } - - // encode any timeouts into the diff Meta - t := &schema.ResourceTimeout{} - if err := t.ConfigDecode(res, cfg); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - if err := t.DiffEncode(diff); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // Now we need to store any NewExtra values, which are where any actual - // StateFunc modified config fields are hidden. - privateMap := diff.Meta - if privateMap == nil { - privateMap = map[string]interface{}{} - } - - newExtra := map[string]interface{}{} - - for k, v := range diff.Attributes { - if v.NewExtra != nil { - newExtra[k] = v.NewExtra - } - } - privateMap[newExtraKey] = newExtra - - // the Meta field gets encoded into PlannedPrivate - plannedPrivate, err := json.Marshal(privateMap) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - resp.PlannedPrivate = plannedPrivate - - // collect the attributes that require instance replacement, and convert - // them to cty.Paths. - var requiresNew []string - if !forceNoChanges { - for attr, d := range diff.Attributes { - if d.RequiresNew { - requiresNew = append(requiresNew, attr) - } - } - } - - // If anything requires a new resource already, or the "id" field indicates - // that we will be creating a new resource, then we need to add that to - // RequiresReplace so that core can tell if the instance is being replaced - // even if changes are being suppressed via "ignore_changes". - id := plannedStateVal.GetAttr("id") - if len(requiresNew) > 0 || id.IsNull() || !id.IsKnown() { - requiresNew = append(requiresNew, "id") - } - - requiresReplace, err := hcl2shim.RequiresReplace(requiresNew, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // convert these to the protocol structures - for _, p := range requiresReplace { - resp.RequiresReplace = append(resp.RequiresReplace, pathToAttributePath(p)) - } - - return resp, nil -} - -func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.ApplyResourceChange_Request) (*proto.ApplyResourceChange_Response, error) { - resp := &proto.ApplyResourceChange_Response{ - // Start with the existing state as a fallback - NewState: req.PriorState, - } - - res := s.provider.ResourcesMap[req.TypeName] - schemaBlock := s.getResourceSchemaBlock(req.TypeName) - - priorStateVal, err := msgpack.Unmarshal(req.PriorState.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - plannedStateVal, err := msgpack.Unmarshal(req.PlannedState.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - info := &terraform.InstanceInfo{ - Type: req.TypeName, - } - - priorState, err := res.ShimInstanceStateFromValue(priorStateVal) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - private := make(map[string]interface{}) - if len(req.PlannedPrivate) > 0 { - if err := json.Unmarshal(req.PlannedPrivate, &private); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - } - - var diff *terraform.InstanceDiff - destroy := false - - // a null state means we are destroying the instance - if plannedStateVal.IsNull() { - destroy = true - diff = &terraform.InstanceDiff{ - Attributes: make(map[string]*terraform.ResourceAttrDiff), - Meta: make(map[string]interface{}), - Destroy: true, - } - } else { - diff, err = schema.DiffFromValues(priorStateVal, plannedStateVal, stripResourceModifiers(res)) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - } - - if diff == nil { - diff = &terraform.InstanceDiff{ - Attributes: make(map[string]*terraform.ResourceAttrDiff), - Meta: make(map[string]interface{}), - } - } - - // add NewExtra Fields that may have been stored in the private data - if newExtra := private[newExtraKey]; newExtra != nil { - for k, v := range newExtra.(map[string]interface{}) { - d := diff.Attributes[k] - - if d == nil { - d = &terraform.ResourceAttrDiff{} - } - - d.NewExtra = v - diff.Attributes[k] = d - } - } - - if private != nil { - diff.Meta = private - } - - for k, d := range diff.Attributes { - // We need to turn off any RequiresNew. There could be attributes - // without changes in here inserted by helper/schema, but if they have - // RequiresNew then the state will be dropped from the ResourceData. - d.RequiresNew = false - - // Check that any "removed" attributes that don't actually exist in the - // prior state, or helper/schema will confuse itself - if d.NewRemoved { - if _, ok := priorState.Attributes[k]; !ok { - delete(diff.Attributes, k) - } - } - } - - pmSchemaBlock := s.getProviderMetaSchemaBlock() - if pmSchemaBlock != nil && req.ProviderMeta != nil { - providerSchemaVal, err := msgpack.Unmarshal(req.ProviderMeta.Msgpack, pmSchemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - priorState.ProviderMeta = providerSchemaVal - } - - newInstanceState, err := s.provider.Apply(info, priorState, diff) - // we record the error here, but continue processing any returned state. - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - } - newStateVal := cty.NullVal(schemaBlock.ImpliedType()) - - // Always return a null value for destroy. - // While this is usually indicated by a nil state, check for missing ID or - // attributes in the case of a provider failure. - if destroy || newInstanceState == nil || newInstanceState.Attributes == nil || newInstanceState.ID == "" { - newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - resp.NewState = &proto.DynamicValue{ - Msgpack: newStateMP, - } - return resp, nil - } - - // We keep the null val if we destroyed the resource, otherwise build the - // entire object, even if the new state was nil. - newStateVal, err = schema.StateValueFromInstanceState(newInstanceState, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - newStateVal = normalizeNullValues(newStateVal, plannedStateVal, true) - - newStateVal = copyTimeoutValues(newStateVal, plannedStateVal) - - newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - resp.NewState = &proto.DynamicValue{ - Msgpack: newStateMP, - } - - meta, err := json.Marshal(newInstanceState.Meta) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - resp.Private = meta - - // This is a signal to Terraform Core that we're doing the best we can to - // shim the legacy type system of the SDK onto the Terraform type system - // but we need it to cut us some slack. This setting should not be taken - // forward to any new SDK implementations, since setting it prevents us - // from catching certain classes of provider bug that can lead to - // confusing downstream errors. - resp.LegacyTypeSystem = true - - return resp, nil -} - -func (s *GRPCProviderServer) ImportResourceState(_ context.Context, req *proto.ImportResourceState_Request) (*proto.ImportResourceState_Response, error) { - resp := &proto.ImportResourceState_Response{} - - info := &terraform.InstanceInfo{ - Type: req.TypeName, - } - - newInstanceStates, err := s.provider.ImportState(info, req.Id) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - for _, is := range newInstanceStates { - // copy the ID again just to be sure it wasn't missed - is.Attributes["id"] = is.ID - - resourceType := is.Ephemeral.Type - if resourceType == "" { - resourceType = req.TypeName - } - - schemaBlock := s.getResourceSchemaBlock(resourceType) - newStateVal, err := hcl2shim.HCL2ValueFromFlatmap(is.Attributes, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // Normalize the value and fill in any missing blocks. - newStateVal = objchange.NormalizeObjectFromLegacySDK(newStateVal, schemaBlock) - - newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - meta, err := json.Marshal(is.Meta) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - importedResource := &proto.ImportResourceState_ImportedResource{ - TypeName: resourceType, - State: &proto.DynamicValue{ - Msgpack: newStateMP, - }, - Private: meta, - } - - resp.ImportedResources = append(resp.ImportedResources, importedResource) - } - - return resp, nil -} - -func (s *GRPCProviderServer) ReadDataSource(_ context.Context, req *proto.ReadDataSource_Request) (*proto.ReadDataSource_Response, error) { - resp := &proto.ReadDataSource_Response{} - - schemaBlock := s.getDatasourceSchemaBlock(req.TypeName) - - configVal, err := msgpack.Unmarshal(req.Config.Msgpack, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - info := &terraform.InstanceInfo{ - Type: req.TypeName, - } - - // Ensure there are no nulls that will cause helper/schema to panic. - if err := validateConfigNulls(configVal, nil); err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - config := terraform.NewResourceConfigShimmed(configVal, schemaBlock) - - // we need to still build the diff separately with the Read method to match - // the old behavior - diff, err := s.provider.ReadDataDiff(info, config) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - // now we can get the new complete data source - newInstanceState, err := s.provider.ReadDataApply(info, diff) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - newStateVal, err := schema.StateValueFromInstanceState(newInstanceState, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - newStateVal = copyTimeoutValues(newStateVal, configVal) - - newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - resp.State = &proto.DynamicValue{ - Msgpack: newStateMP, - } - return resp, nil -} - -func pathToAttributePath(path cty.Path) *proto.AttributePath { - var steps []*proto.AttributePath_Step - - for _, step := range path { - switch s := step.(type) { - case cty.GetAttrStep: - steps = append(steps, &proto.AttributePath_Step{ - Selector: &proto.AttributePath_Step_AttributeName{ - AttributeName: s.Name, - }, - }) - case cty.IndexStep: - ty := s.Key.Type() - switch ty { - case cty.Number: - i, _ := s.Key.AsBigFloat().Int64() - steps = append(steps, &proto.AttributePath_Step{ - Selector: &proto.AttributePath_Step_ElementKeyInt{ - ElementKeyInt: i, - }, - }) - case cty.String: - steps = append(steps, &proto.AttributePath_Step{ - Selector: &proto.AttributePath_Step_ElementKeyString{ - ElementKeyString: s.Key.AsString(), - }, - }) - } - } - } - - return &proto.AttributePath{Steps: steps} -} - -// helper/schema throws away timeout values from the config and stores them in -// the Private/Meta fields. we need to copy those values into the planned state -// so that core doesn't see a perpetual diff with the timeout block. -func copyTimeoutValues(to cty.Value, from cty.Value) cty.Value { - // if `to` is null we are planning to remove it altogether. - if to.IsNull() { - return to - } - toAttrs := to.AsValueMap() - // We need to remove the key since the hcl2shims will add a non-null block - // because we can't determine if a single block was null from the flatmapped - // values. This needs to conform to the correct schema for marshaling, so - // change the value to null rather than deleting it from the object map. - timeouts, ok := toAttrs[schema.TimeoutsConfigKey] - if ok { - toAttrs[schema.TimeoutsConfigKey] = cty.NullVal(timeouts.Type()) - } - - // if from is null then there are no timeouts to copy - if from.IsNull() { - return cty.ObjectVal(toAttrs) - } - - fromAttrs := from.AsValueMap() - timeouts, ok = fromAttrs[schema.TimeoutsConfigKey] - - // timeouts shouldn't be unknown, but don't copy possibly invalid values either - if !ok || timeouts.IsNull() || !timeouts.IsWhollyKnown() { - // no timeouts block to copy - return cty.ObjectVal(toAttrs) - } - - toAttrs[schema.TimeoutsConfigKey] = timeouts - - return cty.ObjectVal(toAttrs) -} - -// stripResourceModifiers takes a *schema.Resource and returns a deep copy with all -// StateFuncs and CustomizeDiffs removed. This will be used during apply to -// create a diff from a planned state where the diff modifications have already -// been applied. -func stripResourceModifiers(r *schema.Resource) *schema.Resource { - if r == nil { - return nil - } - // start with a shallow copy - newResource := new(schema.Resource) - *newResource = *r - - newResource.CustomizeDiff = nil - newResource.Schema = map[string]*schema.Schema{} - - for k, s := range r.Schema { - newResource.Schema[k] = stripSchema(s) - } - - return newResource -} - -func stripSchema(s *schema.Schema) *schema.Schema { - if s == nil { - return nil - } - // start with a shallow copy - newSchema := new(schema.Schema) - *newSchema = *s - - newSchema.StateFunc = nil - - switch e := newSchema.Elem.(type) { - case *schema.Schema: - newSchema.Elem = stripSchema(e) - case *schema.Resource: - newSchema.Elem = stripResourceModifiers(e) - } - - return newSchema -} - -// Zero values and empty containers may be interchanged by the apply process. -// When there is a discrepency between src and dst value being null or empty, -// prefer the src value. This takes a little more liberty with set types, since -// we can't correlate modified set values. In the case of sets, if the src set -// was wholly known we assume the value was correctly applied and copy that -// entirely to the new value. -// While apply prefers the src value, during plan we prefer dst whenever there -// is an unknown or a set is involved, since the plan can alter the value -// however it sees fit. This however means that a CustomizeDiffFunction may not -// be able to change a null to an empty value or vice versa, but that should be -// very uncommon nor was it reliable before 0.12 either. -func normalizeNullValues(dst, src cty.Value, apply bool) cty.Value { - ty := dst.Type() - if !src.IsNull() && !src.IsKnown() { - // Return src during plan to retain unknown interpolated placeholders, - // which could be lost if we're only updating a resource. If this is a - // read scenario, then there shouldn't be any unknowns at all. - if dst.IsNull() && !apply { - return src - } - return dst - } - - // Handle null/empty changes for collections during apply. - // A change between null and empty values prefers src to make sure the state - // is consistent between plan and apply. - if ty.IsCollectionType() && apply { - dstEmpty := !dst.IsNull() && dst.IsKnown() && dst.LengthInt() == 0 - srcEmpty := !src.IsNull() && src.IsKnown() && src.LengthInt() == 0 - - if (src.IsNull() && dstEmpty) || (srcEmpty && dst.IsNull()) { - return src - } - } - - // check the invariants that we need below, to ensure we are working with - // non-null and known values. - if src.IsNull() || !src.IsKnown() || !dst.IsKnown() { - return dst - } - - switch { - case ty.IsMapType(), ty.IsObjectType(): - var dstMap map[string]cty.Value - if !dst.IsNull() { - dstMap = dst.AsValueMap() - } - if dstMap == nil { - dstMap = map[string]cty.Value{} - } - - srcMap := src.AsValueMap() - for key, v := range srcMap { - dstVal, ok := dstMap[key] - if !ok && apply && ty.IsMapType() { - // don't transfer old map values to dst during apply - continue - } - - if dstVal == cty.NilVal { - if !apply && ty.IsMapType() { - // let plan shape this map however it wants - continue - } - dstVal = cty.NullVal(v.Type()) - } - - dstMap[key] = normalizeNullValues(dstVal, v, apply) - } - - // you can't call MapVal/ObjectVal with empty maps, but nothing was - // copied in anyway. If the dst is nil, and the src is known, assume the - // src is correct. - if len(dstMap) == 0 { - if dst.IsNull() && src.IsWhollyKnown() && apply { - return src - } - return dst - } - - if ty.IsMapType() { - // helper/schema will populate an optional+computed map with - // unknowns which we have to fixup here. - // It would be preferable to simply prevent any known value from - // becoming unknown, but concessions have to be made to retain the - // broken legacy behavior when possible. - for k, srcVal := range srcMap { - if !srcVal.IsNull() && srcVal.IsKnown() { - dstVal, ok := dstMap[k] - if !ok { - continue - } - - if !dstVal.IsNull() && !dstVal.IsKnown() { - dstMap[k] = srcVal - } - } - } - - return cty.MapVal(dstMap) - } - - return cty.ObjectVal(dstMap) - - case ty.IsSetType(): - // If the original was wholly known, then we expect that is what the - // provider applied. The apply process loses too much information to - // reliably re-create the set. - if src.IsWhollyKnown() && apply { - return src - } - - case ty.IsListType(), ty.IsTupleType(): - // If the dst is null, and the src is known, then we lost an empty value - // so take the original. - if dst.IsNull() { - if src.IsWhollyKnown() && src.LengthInt() == 0 && apply { - return src - } - - // if dst is null and src only contains unknown values, then we lost - // those during a read or plan. - if !apply && !src.IsNull() { - allUnknown := true - for _, v := range src.AsValueSlice() { - if v.IsKnown() { - allUnknown = false - break - } - } - if allUnknown { - return src - } - } - - return dst - } - - // if the lengths are identical, then iterate over each element in succession. - srcLen := src.LengthInt() - dstLen := dst.LengthInt() - if srcLen == dstLen && srcLen > 0 { - srcs := src.AsValueSlice() - dsts := dst.AsValueSlice() - - for i := 0; i < srcLen; i++ { - dsts[i] = normalizeNullValues(dsts[i], srcs[i], apply) - } - - if ty.IsTupleType() { - return cty.TupleVal(dsts) - } - return cty.ListVal(dsts) - } - - case ty == cty.String: - // The legacy SDK should not be able to remove a value during plan or - // apply, however we are only going to overwrite this if the source was - // an empty string, since that is what is often equated with unset and - // lost in the diff process. - if dst.IsNull() && src.AsString() == "" { - return src - } - } - - return dst -} - -// validateConfigNulls checks a config value for unsupported nulls before -// attempting to shim the value. While null values can mostly be ignored in the -// configuration, since they're not supported in HCL1, the case where a null -// appears in a list-like attribute (list, set, tuple) will present a nil value -// to helper/schema which can panic. Return an error to the user in this case, -// indicating the attribute with the null value. -func validateConfigNulls(v cty.Value, path cty.Path) []*proto.Diagnostic { - var diags []*proto.Diagnostic - if v.IsNull() || !v.IsKnown() { - return diags - } - - switch { - case v.Type().IsListType() || v.Type().IsSetType() || v.Type().IsTupleType(): - it := v.ElementIterator() - for it.Next() { - kv, ev := it.Element() - if ev.IsNull() { - // if this is a set, the kv is also going to be null which - // isn't a valid path element, so we can't append it to the - // diagnostic. - p := path - if !kv.IsNull() { - p = append(p, cty.IndexStep{Key: kv}) - } - - diags = append(diags, &proto.Diagnostic{ - Severity: proto.Diagnostic_ERROR, - Summary: "Null value found in list", - Detail: "Null values are not allowed for this attribute value.", - Attribute: convert.PathToAttributePath(p), - }) - continue - } - - d := validateConfigNulls(ev, append(path, cty.IndexStep{Key: kv})) - diags = convert.AppendProtoDiag(diags, d) - } - - case v.Type().IsMapType() || v.Type().IsObjectType(): - it := v.ElementIterator() - for it.Next() { - kv, ev := it.Element() - var step cty.PathStep - switch { - case v.Type().IsMapType(): - step = cty.IndexStep{Key: kv} - case v.Type().IsObjectType(): - step = cty.GetAttrStep{Name: kv.AsString()} - } - d := validateConfigNulls(ev, append(path, step)) - diags = convert.AppendProtoDiag(diags, d) - } - } - - return diags -} diff --git a/vendor/github.com/hashicorp/terraform/helper/plugin/grpc_provider_test.go b/vendor/github.com/hashicorp/terraform/helper/plugin/grpc_provider_test.go deleted file mode 100644 index 736eb258..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/plugin/grpc_provider_test.go +++ /dev/null @@ -1,1382 +0,0 @@ -package plugin - -import ( - "context" - "fmt" - "strconv" - "strings" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/hashicorp/terraform/helper/schema" - proto "github.com/hashicorp/terraform/internal/tfplugin5" - "github.com/hashicorp/terraform/plugin/convert" - "github.com/hashicorp/terraform/terraform" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/msgpack" -) - -// The GRPCProviderServer will directly implement the go protobuf server -var _ proto.ProviderServer = (*GRPCProviderServer)(nil) - -var ( - typeComparer = cmp.Comparer(cty.Type.Equals) - valueComparer = cmp.Comparer(cty.Value.RawEquals) - equateEmpty = cmpopts.EquateEmpty() -) - -func TestUpgradeState_jsonState(t *testing.T) { - r := &schema.Resource{ - SchemaVersion: 2, - Schema: map[string]*schema.Schema{ - "two": { - Type: schema.TypeInt, - Optional: true, - }, - }, - } - - r.StateUpgraders = []schema.StateUpgrader{ - { - Version: 0, - Type: cty.Object(map[string]cty.Type{ - "id": cty.String, - "zero": cty.Number, - }), - Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) { - _, ok := m["zero"].(float64) - if !ok { - return nil, fmt.Errorf("zero not found in %#v", m) - } - m["one"] = float64(1) - delete(m, "zero") - return m, nil - }, - }, - { - Version: 1, - Type: cty.Object(map[string]cty.Type{ - "id": cty.String, - "one": cty.Number, - }), - Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) { - _, ok := m["one"].(float64) - if !ok { - return nil, fmt.Errorf("one not found in %#v", m) - } - m["two"] = float64(2) - delete(m, "one") - return m, nil - }, - }, - } - - server := &GRPCProviderServer{ - provider: &schema.Provider{ - ResourcesMap: map[string]*schema.Resource{ - "test": r, - }, - }, - } - - req := &proto.UpgradeResourceState_Request{ - TypeName: "test", - Version: 0, - RawState: &proto.RawState{ - Json: []byte(`{"id":"bar","zero":0}`), - }, - } - - resp, err := server.UpgradeResourceState(nil, req) - if err != nil { - t.Fatal(err) - } - - if len(resp.Diagnostics) > 0 { - for _, d := range resp.Diagnostics { - t.Errorf("%#v", d) - } - t.Fatal("error") - } - - val, err := msgpack.Unmarshal(resp.UpgradedState.Msgpack, r.CoreConfigSchema().ImpliedType()) - if err != nil { - t.Fatal(err) - } - - expected := cty.ObjectVal(map[string]cty.Value{ - "id": cty.StringVal("bar"), - "two": cty.NumberIntVal(2), - }) - - if !cmp.Equal(expected, val, valueComparer, equateEmpty) { - t.Fatal(cmp.Diff(expected, val, valueComparer, equateEmpty)) - } -} - -func TestUpgradeState_removedAttr(t *testing.T) { - r1 := &schema.Resource{ - Schema: map[string]*schema.Schema{ - "two": { - Type: schema.TypeString, - Optional: true, - }, - }, - } - - r2 := &schema.Resource{ - Schema: map[string]*schema.Schema{ - "multi": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "set": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "required": { - Type: schema.TypeString, - Required: true, - }, - }, - }, - }, - }, - }, - }, - }, - } - - r3 := &schema.Resource{ - Schema: map[string]*schema.Schema{ - "config_mode_attr": { - Type: schema.TypeList, - ConfigMode: schema.SchemaConfigModeAttr, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "foo": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, - }, - } - - p := &schema.Provider{ - ResourcesMap: map[string]*schema.Resource{ - "r1": r1, - "r2": r2, - "r3": r3, - }, - } - - server := &GRPCProviderServer{ - provider: p, - } - - for _, tc := range []struct { - name string - raw string - expected cty.Value - }{ - { - name: "r1", - raw: `{"id":"bar","removed":"removed","two":"2"}`, - expected: cty.ObjectVal(map[string]cty.Value{ - "id": cty.StringVal("bar"), - "two": cty.StringVal("2"), - }), - }, - { - name: "r2", - raw: `{"id":"bar","multi":[{"set":[{"required":"ok","removed":"removed"}]}]}`, - expected: cty.ObjectVal(map[string]cty.Value{ - "id": cty.StringVal("bar"), - "multi": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "set": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "required": cty.StringVal("ok"), - }), - }), - }), - }), - }), - }, - { - name: "r3", - raw: `{"id":"bar","config_mode_attr":[{"foo":"ok","removed":"removed"}]}`, - expected: cty.ObjectVal(map[string]cty.Value{ - "id": cty.StringVal("bar"), - "config_mode_attr": cty.ListVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("ok"), - }), - }), - }), - }, - } { - t.Run(tc.name, func(t *testing.T) { - req := &proto.UpgradeResourceState_Request{ - TypeName: tc.name, - Version: 0, - RawState: &proto.RawState{ - Json: []byte(tc.raw), - }, - } - resp, err := server.UpgradeResourceState(nil, req) - if err != nil { - t.Fatal(err) - } - - if len(resp.Diagnostics) > 0 { - for _, d := range resp.Diagnostics { - t.Errorf("%#v", d) - } - t.Fatal("error") - } - val, err := msgpack.Unmarshal(resp.UpgradedState.Msgpack, p.ResourcesMap[tc.name].CoreConfigSchema().ImpliedType()) - if err != nil { - t.Fatal(err) - } - if !tc.expected.RawEquals(val) { - t.Fatalf("\nexpected: %#v\ngot: %#v\n", tc.expected, val) - } - }) - } - -} - -func TestUpgradeState_flatmapState(t *testing.T) { - r := &schema.Resource{ - SchemaVersion: 4, - Schema: map[string]*schema.Schema{ - "four": { - Type: schema.TypeInt, - Required: true, - }, - "block": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "attr": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, - }, - // this MigrateState will take the state to version 2 - MigrateState: func(v int, is *terraform.InstanceState, _ interface{}) (*terraform.InstanceState, error) { - switch v { - case 0: - _, ok := is.Attributes["zero"] - if !ok { - return nil, fmt.Errorf("zero not found in %#v", is.Attributes) - } - is.Attributes["one"] = "1" - delete(is.Attributes, "zero") - fallthrough - case 1: - _, ok := is.Attributes["one"] - if !ok { - return nil, fmt.Errorf("one not found in %#v", is.Attributes) - } - is.Attributes["two"] = "2" - delete(is.Attributes, "one") - default: - return nil, fmt.Errorf("invalid schema version %d", v) - } - return is, nil - }, - } - - r.StateUpgraders = []schema.StateUpgrader{ - { - Version: 2, - Type: cty.Object(map[string]cty.Type{ - "id": cty.String, - "two": cty.Number, - }), - Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) { - _, ok := m["two"].(float64) - if !ok { - return nil, fmt.Errorf("two not found in %#v", m) - } - m["three"] = float64(3) - delete(m, "two") - return m, nil - }, - }, - { - Version: 3, - Type: cty.Object(map[string]cty.Type{ - "id": cty.String, - "three": cty.Number, - }), - Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) { - _, ok := m["three"].(float64) - if !ok { - return nil, fmt.Errorf("three not found in %#v", m) - } - m["four"] = float64(4) - delete(m, "three") - return m, nil - }, - }, - } - - server := &GRPCProviderServer{ - provider: &schema.Provider{ - ResourcesMap: map[string]*schema.Resource{ - "test": r, - }, - }, - } - - testReqs := []*proto.UpgradeResourceState_Request{ - { - TypeName: "test", - Version: 0, - RawState: &proto.RawState{ - Flatmap: map[string]string{ - "id": "bar", - "zero": "0", - }, - }, - }, - { - TypeName: "test", - Version: 1, - RawState: &proto.RawState{ - Flatmap: map[string]string{ - "id": "bar", - "one": "1", - }, - }, - }, - // two and up could be stored in flatmap or json states - { - TypeName: "test", - Version: 2, - RawState: &proto.RawState{ - Flatmap: map[string]string{ - "id": "bar", - "two": "2", - }, - }, - }, - { - TypeName: "test", - Version: 2, - RawState: &proto.RawState{ - Json: []byte(`{"id":"bar","two":2}`), - }, - }, - { - TypeName: "test", - Version: 3, - RawState: &proto.RawState{ - Flatmap: map[string]string{ - "id": "bar", - "three": "3", - }, - }, - }, - { - TypeName: "test", - Version: 3, - RawState: &proto.RawState{ - Json: []byte(`{"id":"bar","three":3}`), - }, - }, - { - TypeName: "test", - Version: 4, - RawState: &proto.RawState{ - Flatmap: map[string]string{ - "id": "bar", - "four": "4", - }, - }, - }, - { - TypeName: "test", - Version: 4, - RawState: &proto.RawState{ - Json: []byte(`{"id":"bar","four":4}`), - }, - }, - } - - for i, req := range testReqs { - t.Run(fmt.Sprintf("%d-%d", i, req.Version), func(t *testing.T) { - resp, err := server.UpgradeResourceState(nil, req) - if err != nil { - t.Fatal(err) - } - - if len(resp.Diagnostics) > 0 { - for _, d := range resp.Diagnostics { - t.Errorf("%#v", d) - } - t.Fatal("error") - } - - val, err := msgpack.Unmarshal(resp.UpgradedState.Msgpack, r.CoreConfigSchema().ImpliedType()) - if err != nil { - t.Fatal(err) - } - - expected := cty.ObjectVal(map[string]cty.Value{ - "block": cty.ListValEmpty(cty.Object(map[string]cty.Type{"attr": cty.String})), - "id": cty.StringVal("bar"), - "four": cty.NumberIntVal(4), - }) - - if !cmp.Equal(expected, val, valueComparer, equateEmpty) { - t.Fatal(cmp.Diff(expected, val, valueComparer, equateEmpty)) - } - }) - } -} - -func TestUpgradeState_flatmapStateMissingMigrateState(t *testing.T) { - r := &schema.Resource{ - SchemaVersion: 1, - Schema: map[string]*schema.Schema{ - "one": { - Type: schema.TypeInt, - Required: true, - }, - }, - } - - server := &GRPCProviderServer{ - provider: &schema.Provider{ - ResourcesMap: map[string]*schema.Resource{ - "test": r, - }, - }, - } - - testReqs := []*proto.UpgradeResourceState_Request{ - { - TypeName: "test", - Version: 0, - RawState: &proto.RawState{ - Flatmap: map[string]string{ - "id": "bar", - "one": "1", - }, - }, - }, - { - TypeName: "test", - Version: 1, - RawState: &proto.RawState{ - Flatmap: map[string]string{ - "id": "bar", - "one": "1", - }, - }, - }, - { - TypeName: "test", - Version: 1, - RawState: &proto.RawState{ - Json: []byte(`{"id":"bar","one":1}`), - }, - }, - } - - for i, req := range testReqs { - t.Run(fmt.Sprintf("%d-%d", i, req.Version), func(t *testing.T) { - resp, err := server.UpgradeResourceState(nil, req) - if err != nil { - t.Fatal(err) - } - - if len(resp.Diagnostics) > 0 { - for _, d := range resp.Diagnostics { - t.Errorf("%#v", d) - } - t.Fatal("error") - } - - val, err := msgpack.Unmarshal(resp.UpgradedState.Msgpack, r.CoreConfigSchema().ImpliedType()) - if err != nil { - t.Fatal(err) - } - - expected := cty.ObjectVal(map[string]cty.Value{ - "id": cty.StringVal("bar"), - "one": cty.NumberIntVal(1), - }) - - if !cmp.Equal(expected, val, valueComparer, equateEmpty) { - t.Fatal(cmp.Diff(expected, val, valueComparer, equateEmpty)) - } - }) - } -} - -func TestPlanResourceChange(t *testing.T) { - r := &schema.Resource{ - SchemaVersion: 4, - Schema: map[string]*schema.Schema{ - "foo": { - Type: schema.TypeInt, - Optional: true, - }, - }, - } - - server := &GRPCProviderServer{ - provider: &schema.Provider{ - ResourcesMap: map[string]*schema.Resource{ - "test": r, - }, - }, - } - - schema := r.CoreConfigSchema() - priorState, err := msgpack.Marshal(cty.NullVal(schema.ImpliedType()), schema.ImpliedType()) - if err != nil { - t.Fatal(err) - } - - // A propsed state with only the ID unknown will produce a nil diff, and - // should return the propsed state value. - proposedVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ - "id": cty.UnknownVal(cty.String), - })) - if err != nil { - t.Fatal(err) - } - proposedState, err := msgpack.Marshal(proposedVal, schema.ImpliedType()) - if err != nil { - t.Fatal(err) - } - - testReq := &proto.PlanResourceChange_Request{ - TypeName: "test", - PriorState: &proto.DynamicValue{ - Msgpack: priorState, - }, - ProposedNewState: &proto.DynamicValue{ - Msgpack: proposedState, - }, - } - - resp, err := server.PlanResourceChange(context.Background(), testReq) - if err != nil { - t.Fatal(err) - } - - plannedStateVal, err := msgpack.Unmarshal(resp.PlannedState.Msgpack, schema.ImpliedType()) - if err != nil { - t.Fatal(err) - } - - if !cmp.Equal(proposedVal, plannedStateVal, valueComparer) { - t.Fatal(cmp.Diff(proposedVal, plannedStateVal, valueComparer)) - } -} - -func TestApplyResourceChange(t *testing.T) { - r := &schema.Resource{ - SchemaVersion: 4, - Schema: map[string]*schema.Schema{ - "foo": { - Type: schema.TypeInt, - Optional: true, - }, - }, - Create: func(rd *schema.ResourceData, _ interface{}) error { - rd.SetId("bar") - return nil - }, - } - - server := &GRPCProviderServer{ - provider: &schema.Provider{ - ResourcesMap: map[string]*schema.Resource{ - "test": r, - }, - }, - } - - schema := r.CoreConfigSchema() - priorState, err := msgpack.Marshal(cty.NullVal(schema.ImpliedType()), schema.ImpliedType()) - if err != nil { - t.Fatal(err) - } - - // A proposed state with only the ID unknown will produce a nil diff, and - // should return the proposed state value. - plannedVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ - "id": cty.UnknownVal(cty.String), - })) - if err != nil { - t.Fatal(err) - } - plannedState, err := msgpack.Marshal(plannedVal, schema.ImpliedType()) - if err != nil { - t.Fatal(err) - } - - testReq := &proto.ApplyResourceChange_Request{ - TypeName: "test", - PriorState: &proto.DynamicValue{ - Msgpack: priorState, - }, - PlannedState: &proto.DynamicValue{ - Msgpack: plannedState, - }, - } - - resp, err := server.ApplyResourceChange(context.Background(), testReq) - if err != nil { - t.Fatal(err) - } - - newStateVal, err := msgpack.Unmarshal(resp.NewState.Msgpack, schema.ImpliedType()) - if err != nil { - t.Fatal(err) - } - - id := newStateVal.GetAttr("id").AsString() - if id != "bar" { - t.Fatalf("incorrect final state: %#v\n", newStateVal) - } -} - -func TestPrepareProviderConfig(t *testing.T) { - for _, tc := range []struct { - Name string - Schema map[string]*schema.Schema - ConfigVal cty.Value - ExpectError string - ExpectConfig cty.Value - }{ - { - Name: "test prepare", - Schema: map[string]*schema.Schema{ - "foo": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - }, - ConfigVal: cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("bar"), - }), - ExpectConfig: cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("bar"), - }), - }, - { - Name: "test default", - Schema: map[string]*schema.Schema{ - "foo": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: "default", - }, - }, - ConfigVal: cty.ObjectVal(map[string]cty.Value{ - "foo": cty.NullVal(cty.String), - }), - ExpectConfig: cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("default"), - }), - }, - { - Name: "test defaultfunc", - Schema: map[string]*schema.Schema{ - "foo": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - DefaultFunc: func() (interface{}, error) { - return "defaultfunc", nil - }, - }, - }, - ConfigVal: cty.ObjectVal(map[string]cty.Value{ - "foo": cty.NullVal(cty.String), - }), - ExpectConfig: cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("defaultfunc"), - }), - }, - { - Name: "test default required", - Schema: map[string]*schema.Schema{ - "foo": &schema.Schema{ - Type: schema.TypeString, - Required: true, - DefaultFunc: func() (interface{}, error) { - return "defaultfunc", nil - }, - }, - }, - ConfigVal: cty.ObjectVal(map[string]cty.Value{ - "foo": cty.NullVal(cty.String), - }), - ExpectConfig: cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("defaultfunc"), - }), - }, - { - Name: "test incorrect type", - Schema: map[string]*schema.Schema{ - "foo": &schema.Schema{ - Type: schema.TypeString, - Required: true, - }, - }, - ConfigVal: cty.ObjectVal(map[string]cty.Value{ - "foo": cty.NumberIntVal(3), - }), - ExpectConfig: cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("3"), - }), - }, - { - Name: "test incorrect default type", - Schema: map[string]*schema.Schema{ - "foo": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: true, - }, - }, - ConfigVal: cty.ObjectVal(map[string]cty.Value{ - "foo": cty.NullVal(cty.String), - }), - ExpectConfig: cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("true"), - }), - }, - { - Name: "test incorrect default bool type", - Schema: map[string]*schema.Schema{ - "foo": &schema.Schema{ - Type: schema.TypeBool, - Optional: true, - Default: "", - }, - }, - ConfigVal: cty.ObjectVal(map[string]cty.Value{ - "foo": cty.NullVal(cty.Bool), - }), - ExpectConfig: cty.ObjectVal(map[string]cty.Value{ - "foo": cty.False, - }), - }, - { - Name: "test deprecated default", - Schema: map[string]*schema.Schema{ - "foo": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Default: "do not use", - Removed: "don't use this", - }, - }, - ConfigVal: cty.ObjectVal(map[string]cty.Value{ - "foo": cty.NullVal(cty.String), - }), - ExpectConfig: cty.ObjectVal(map[string]cty.Value{ - "foo": cty.NullVal(cty.String), - }), - }, - } { - t.Run(tc.Name, func(t *testing.T) { - server := &GRPCProviderServer{ - provider: &schema.Provider{ - Schema: tc.Schema, - }, - } - - block := schema.InternalMap(tc.Schema).CoreConfigSchema() - - rawConfig, err := msgpack.Marshal(tc.ConfigVal, block.ImpliedType()) - if err != nil { - t.Fatal(err) - } - - testReq := &proto.PrepareProviderConfig_Request{ - Config: &proto.DynamicValue{ - Msgpack: rawConfig, - }, - } - - resp, err := server.PrepareProviderConfig(nil, testReq) - if err != nil { - t.Fatal(err) - } - - if tc.ExpectError != "" && len(resp.Diagnostics) > 0 { - for _, d := range resp.Diagnostics { - if !strings.Contains(d.Summary, tc.ExpectError) { - t.Fatalf("Unexpected error: %s/%s", d.Summary, d.Detail) - } - } - return - } - - // we should have no errors past this point - for _, d := range resp.Diagnostics { - if d.Severity == proto.Diagnostic_ERROR { - t.Fatal(resp.Diagnostics) - } - } - - val, err := msgpack.Unmarshal(resp.PreparedConfig.Msgpack, block.ImpliedType()) - if err != nil { - t.Fatal(err) - } - - if tc.ExpectConfig.GoString() != val.GoString() { - t.Fatalf("\nexpected: %#v\ngot: %#v", tc.ExpectConfig, val) - } - }) - } -} - -func TestGetSchemaTimeouts(t *testing.T) { - r := &schema.Resource{ - SchemaVersion: 4, - Timeouts: &schema.ResourceTimeout{ - Create: schema.DefaultTimeout(time.Second), - Read: schema.DefaultTimeout(2 * time.Second), - Update: schema.DefaultTimeout(3 * time.Second), - Default: schema.DefaultTimeout(10 * time.Second), - }, - Schema: map[string]*schema.Schema{ - "foo": { - Type: schema.TypeInt, - Optional: true, - }, - }, - } - - // verify that the timeouts appear in the schema as defined - block := r.CoreConfigSchema() - timeoutsBlock := block.BlockTypes["timeouts"] - if timeoutsBlock == nil { - t.Fatal("missing timeouts in schema") - } - - if timeoutsBlock.Attributes["create"] == nil { - t.Fatal("missing create timeout in schema") - } - if timeoutsBlock.Attributes["read"] == nil { - t.Fatal("missing read timeout in schema") - } - if timeoutsBlock.Attributes["update"] == nil { - t.Fatal("missing update timeout in schema") - } - if d := timeoutsBlock.Attributes["delete"]; d != nil { - t.Fatalf("unexpected delete timeout in schema: %#v", d) - } - if timeoutsBlock.Attributes["default"] == nil { - t.Fatal("missing default timeout in schema") - } -} - -func TestNormalizeNullValues(t *testing.T) { - for i, tc := range []struct { - Src, Dst, Expect cty.Value - Apply bool - }{ - { - // The known set value is copied over the null set value - Src: cty.ObjectVal(map[string]cty.Value{ - "set": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "foo": cty.NullVal(cty.String), - }), - }), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ - "foo": cty.String, - }))), - }), - Expect: cty.ObjectVal(map[string]cty.Value{ - "set": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "foo": cty.NullVal(cty.String), - }), - }), - }), - Apply: true, - }, - { - // A zero set value is kept - Src: cty.ObjectVal(map[string]cty.Value{ - "set": cty.SetValEmpty(cty.String), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "set": cty.SetValEmpty(cty.String), - }), - Expect: cty.ObjectVal(map[string]cty.Value{ - "set": cty.SetValEmpty(cty.String), - }), - }, - { - // The known set value is copied over the null set value - Src: cty.ObjectVal(map[string]cty.Value{ - "set": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "foo": cty.NullVal(cty.String), - }), - }), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ - "foo": cty.String, - }))), - }), - // If we're only in a plan, we can't compare sets at all - Expect: cty.ObjectVal(map[string]cty.Value{ - "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ - "foo": cty.String, - }))), - }), - }, - { - // The empty map is copied over the null map - Src: cty.ObjectVal(map[string]cty.Value{ - "map": cty.MapValEmpty(cty.String), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "map": cty.NullVal(cty.Map(cty.String)), - }), - Expect: cty.ObjectVal(map[string]cty.Value{ - "map": cty.MapValEmpty(cty.String), - }), - Apply: true, - }, - { - // A zero value primitive is copied over a null primitive - Src: cty.ObjectVal(map[string]cty.Value{ - "string": cty.StringVal(""), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "string": cty.NullVal(cty.String), - }), - Expect: cty.ObjectVal(map[string]cty.Value{ - "string": cty.StringVal(""), - }), - Apply: true, - }, - { - // Plan primitives are kept - Src: cty.ObjectVal(map[string]cty.Value{ - "string": cty.NumberIntVal(0), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "string": cty.NullVal(cty.Number), - }), - Expect: cty.ObjectVal(map[string]cty.Value{ - "string": cty.NullVal(cty.Number), - }), - }, - { - // Neither plan nor apply should remove empty strings - Src: cty.ObjectVal(map[string]cty.Value{ - "string": cty.StringVal(""), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "string": cty.NullVal(cty.String), - }), - Expect: cty.ObjectVal(map[string]cty.Value{ - "string": cty.StringVal(""), - }), - }, - { - // Neither plan nor apply should remove empty strings - Src: cty.ObjectVal(map[string]cty.Value{ - "string": cty.StringVal(""), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "string": cty.NullVal(cty.String), - }), - Expect: cty.ObjectVal(map[string]cty.Value{ - "string": cty.StringVal(""), - }), - Apply: true, - }, - { - // The null map is retained, because the src was unknown - Src: cty.ObjectVal(map[string]cty.Value{ - "map": cty.UnknownVal(cty.Map(cty.String)), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "map": cty.NullVal(cty.Map(cty.String)), - }), - Expect: cty.ObjectVal(map[string]cty.Value{ - "map": cty.NullVal(cty.Map(cty.String)), - }), - Apply: true, - }, - { - // the nul set is retained, because the src set contains an unknown value - Src: cty.ObjectVal(map[string]cty.Value{ - "set": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "foo": cty.UnknownVal(cty.String), - }), - }), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ - "foo": cty.String, - }))), - }), - Expect: cty.ObjectVal(map[string]cty.Value{ - "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ - "foo": cty.String, - }))), - }), - Apply: true, - }, - { - // Retain don't re-add unexpected planned values in a map - Src: cty.ObjectVal(map[string]cty.Value{ - "map": cty.MapVal(map[string]cty.Value{ - "a": cty.StringVal("a"), - "b": cty.StringVal(""), - }), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "map": cty.MapVal(map[string]cty.Value{ - "a": cty.StringVal("a"), - }), - }), - Expect: cty.ObjectVal(map[string]cty.Value{ - "map": cty.MapVal(map[string]cty.Value{ - "a": cty.StringVal("a"), - }), - }), - }, - { - // Remove extra values after apply - Src: cty.ObjectVal(map[string]cty.Value{ - "map": cty.MapVal(map[string]cty.Value{ - "a": cty.StringVal("a"), - "b": cty.StringVal("b"), - }), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "map": cty.MapVal(map[string]cty.Value{ - "a": cty.StringVal("a"), - }), - }), - Expect: cty.ObjectVal(map[string]cty.Value{ - "map": cty.MapVal(map[string]cty.Value{ - "a": cty.StringVal("a"), - }), - }), - Apply: true, - }, - { - Src: cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("a"), - }), - Dst: cty.EmptyObjectVal, - Expect: cty.ObjectVal(map[string]cty.Value{ - "a": cty.NullVal(cty.String), - }), - }, - - // a list in an object in a list, going from null to empty - { - Src: cty.ObjectVal(map[string]cty.Value{ - "network_interface": cty.ListVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "network_ip": cty.UnknownVal(cty.String), - "access_config": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String}))), - "address": cty.NullVal(cty.String), - "name": cty.StringVal("nic0"), - })}), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "network_interface": cty.ListVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "network_ip": cty.StringVal("10.128.0.64"), - "access_config": cty.ListValEmpty(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String})), - "address": cty.StringVal("address"), - "name": cty.StringVal("nic0"), - }), - }), - }), - Expect: cty.ObjectVal(map[string]cty.Value{ - "network_interface": cty.ListVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "network_ip": cty.StringVal("10.128.0.64"), - "access_config": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String}))), - "address": cty.StringVal("address"), - "name": cty.StringVal("nic0"), - }), - }), - }), - Apply: true, - }, - - // a list in an object in a list, going from empty to null - { - Src: cty.ObjectVal(map[string]cty.Value{ - "network_interface": cty.ListVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "network_ip": cty.UnknownVal(cty.String), - "access_config": cty.ListValEmpty(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String})), - "address": cty.NullVal(cty.String), - "name": cty.StringVal("nic0"), - })}), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "network_interface": cty.ListVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "network_ip": cty.StringVal("10.128.0.64"), - "access_config": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String}))), - "address": cty.StringVal("address"), - "name": cty.StringVal("nic0"), - }), - }), - }), - Expect: cty.ObjectVal(map[string]cty.Value{ - "network_interface": cty.ListVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "network_ip": cty.StringVal("10.128.0.64"), - "access_config": cty.ListValEmpty(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String})), - "address": cty.StringVal("address"), - "name": cty.StringVal("nic0"), - }), - }), - }), - Apply: true, - }, - // the empty list should be transferred, but the new unknown should not be overridden - { - Src: cty.ObjectVal(map[string]cty.Value{ - "network_interface": cty.ListVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "network_ip": cty.StringVal("10.128.0.64"), - "access_config": cty.ListValEmpty(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String})), - "address": cty.NullVal(cty.String), - "name": cty.StringVal("nic0"), - })}), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "network_interface": cty.ListVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "network_ip": cty.UnknownVal(cty.String), - "access_config": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String}))), - "address": cty.StringVal("address"), - "name": cty.StringVal("nic0"), - }), - }), - }), - Expect: cty.ObjectVal(map[string]cty.Value{ - "network_interface": cty.ListVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "network_ip": cty.UnknownVal(cty.String), - "access_config": cty.ListValEmpty(cty.Object(map[string]cty.Type{"public_ptr_domain_name": cty.String, "nat_ip": cty.String})), - "address": cty.StringVal("address"), - "name": cty.StringVal("nic0"), - }), - }), - }), - }, - { - // fix unknowns added to a map - Src: cty.ObjectVal(map[string]cty.Value{ - "map": cty.MapVal(map[string]cty.Value{ - "a": cty.StringVal("a"), - "b": cty.StringVal(""), - }), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "map": cty.MapVal(map[string]cty.Value{ - "a": cty.StringVal("a"), - "b": cty.UnknownVal(cty.String), - }), - }), - Expect: cty.ObjectVal(map[string]cty.Value{ - "map": cty.MapVal(map[string]cty.Value{ - "a": cty.StringVal("a"), - "b": cty.StringVal(""), - }), - }), - }, - { - // fix unknowns lost from a list - Src: cty.ObjectVal(map[string]cty.Value{ - "top": cty.ListVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "list": cty.ListVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "values": cty.ListVal([]cty.Value{cty.UnknownVal(cty.String)}), - }), - }), - }), - }), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "top": cty.ListVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "list": cty.ListVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "values": cty.NullVal(cty.List(cty.String)), - }), - }), - }), - }), - }), - Expect: cty.ObjectVal(map[string]cty.Value{ - "top": cty.ListVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "list": cty.ListVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "values": cty.ListVal([]cty.Value{cty.UnknownVal(cty.String)}), - }), - }), - }), - }), - }), - }, - { - Src: cty.ObjectVal(map[string]cty.Value{ - "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ - "list": cty.List(cty.String), - }))), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "set": cty.SetValEmpty(cty.Object(map[string]cty.Type{ - "list": cty.List(cty.String), - })), - }), - Expect: cty.ObjectVal(map[string]cty.Value{ - "set": cty.SetValEmpty(cty.Object(map[string]cty.Type{ - "list": cty.List(cty.String), - })), - }), - }, - { - Src: cty.ObjectVal(map[string]cty.Value{ - "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ - "list": cty.List(cty.String), - }))), - }), - Dst: cty.ObjectVal(map[string]cty.Value{ - "set": cty.SetValEmpty(cty.Object(map[string]cty.Type{ - "list": cty.List(cty.String), - })), - }), - Expect: cty.ObjectVal(map[string]cty.Value{ - "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ - "list": cty.List(cty.String), - }))), - }), - Apply: true, - }, - } { - t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - got := normalizeNullValues(tc.Dst, tc.Src, tc.Apply) - if !got.RawEquals(tc.Expect) { - t.Fatalf("\nexpected: %#v\ngot: %#v\n", tc.Expect, got) - } - }) - } -} - -func TestValidateNulls(t *testing.T) { - for i, tc := range []struct { - Cfg cty.Value - Err bool - }{ - { - Cfg: cty.ObjectVal(map[string]cty.Value{ - "list": cty.ListVal([]cty.Value{ - cty.StringVal("string"), - cty.NullVal(cty.String), - }), - }), - Err: true, - }, - { - Cfg: cty.ObjectVal(map[string]cty.Value{ - "map": cty.MapVal(map[string]cty.Value{ - "string": cty.StringVal("string"), - "null": cty.NullVal(cty.String), - }), - }), - Err: false, - }, - { - Cfg: cty.ObjectVal(map[string]cty.Value{ - "object": cty.ObjectVal(map[string]cty.Value{ - "list": cty.ListVal([]cty.Value{ - cty.StringVal("string"), - cty.NullVal(cty.String), - }), - }), - }), - Err: true, - }, - { - Cfg: cty.ObjectVal(map[string]cty.Value{ - "object": cty.ObjectVal(map[string]cty.Value{ - "list": cty.ListVal([]cty.Value{ - cty.StringVal("string"), - cty.NullVal(cty.String), - }), - "list2": cty.ListVal([]cty.Value{ - cty.StringVal("string"), - cty.NullVal(cty.String), - }), - }), - }), - Err: true, - }, - { - Cfg: cty.ObjectVal(map[string]cty.Value{ - "object": cty.ObjectVal(map[string]cty.Value{ - "list": cty.SetVal([]cty.Value{ - cty.StringVal("string"), - cty.NullVal(cty.String), - }), - }), - }), - Err: true, - }, - } { - t.Run(strconv.Itoa(i), func(t *testing.T) { - d := validateConfigNulls(tc.Cfg, nil) - diags := convert.ProtoToDiagnostics(d) - switch { - case tc.Err: - if !diags.HasErrors() { - t.Fatal("expected error") - } - default: - if diags.HasErrors() { - t.Fatalf("unexpected error: %q", diags.Err()) - } - } - }) - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/plugin/grpc_provisioner.go b/vendor/github.com/hashicorp/terraform/helper/plugin/grpc_provisioner.go deleted file mode 100644 index 088e94e4..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/plugin/grpc_provisioner.go +++ /dev/null @@ -1,201 +0,0 @@ -package plugin - -import ( - "log" - "strings" - "unicode/utf8" - - "github.com/hashicorp/terraform/helper/schema" - proto "github.com/hashicorp/terraform/internal/tfplugin5" - "github.com/hashicorp/terraform/plugin/convert" - "github.com/hashicorp/terraform/terraform" - "github.com/zclconf/go-cty/cty" - ctyconvert "github.com/zclconf/go-cty/cty/convert" - "github.com/zclconf/go-cty/cty/msgpack" - context "golang.org/x/net/context" -) - -// NewGRPCProvisionerServerShim wraps a terraform.ResourceProvisioner in a -// proto.ProvisionerServer implementation. If the provided provisioner is not a -// *schema.Provisioner, this will return nil, -func NewGRPCProvisionerServerShim(p terraform.ResourceProvisioner) *GRPCProvisionerServer { - sp, ok := p.(*schema.Provisioner) - if !ok { - return nil - } - return &GRPCProvisionerServer{ - provisioner: sp, - } -} - -type GRPCProvisionerServer struct { - provisioner *schema.Provisioner -} - -func (s *GRPCProvisionerServer) GetSchema(_ context.Context, req *proto.GetProvisionerSchema_Request) (*proto.GetProvisionerSchema_Response, error) { - resp := &proto.GetProvisionerSchema_Response{} - - resp.Provisioner = &proto.Schema{ - Block: convert.ConfigSchemaToProto(schema.InternalMap(s.provisioner.Schema).CoreConfigSchema()), - } - - return resp, nil -} - -func (s *GRPCProvisionerServer) ValidateProvisionerConfig(_ context.Context, req *proto.ValidateProvisionerConfig_Request) (*proto.ValidateProvisionerConfig_Response, error) { - resp := &proto.ValidateProvisionerConfig_Response{} - - cfgSchema := schema.InternalMap(s.provisioner.Schema).CoreConfigSchema() - - configVal, err := msgpack.Unmarshal(req.Config.Msgpack, cfgSchema.ImpliedType()) - if err != nil { - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) - return resp, nil - } - - config := terraform.NewResourceConfigShimmed(configVal, cfgSchema) - - warns, errs := s.provisioner.Validate(config) - resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs)) - - return resp, nil -} - -// stringMapFromValue converts a cty.Value to a map[stirng]string. -// This will panic if the val is not a cty.Map(cty.String). -func stringMapFromValue(val cty.Value) map[string]string { - m := map[string]string{} - if val.IsNull() || !val.IsKnown() { - return m - } - - for it := val.ElementIterator(); it.Next(); { - ak, av := it.Element() - name := ak.AsString() - - if !av.IsKnown() || av.IsNull() { - continue - } - - av, _ = ctyconvert.Convert(av, cty.String) - m[name] = av.AsString() - } - - return m -} - -// uiOutput implements the terraform.UIOutput interface to adapt the grpc -// stream to the legacy Provisioner.Apply method. -type uiOutput struct { - srv proto.Provisioner_ProvisionResourceServer -} - -func (o uiOutput) Output(s string) { - err := o.srv.Send(&proto.ProvisionResource_Response{ - Output: toValidUTF8(s, string(utf8.RuneError)), - }) - if err != nil { - log.Printf("[ERROR] %s", err) - } -} - -func (s *GRPCProvisionerServer) ProvisionResource(req *proto.ProvisionResource_Request, srv proto.Provisioner_ProvisionResourceServer) error { - // We send back a diagnostics over the stream if there was a - // provisioner-side problem. - srvResp := &proto.ProvisionResource_Response{} - - cfgSchema := schema.InternalMap(s.provisioner.Schema).CoreConfigSchema() - cfgVal, err := msgpack.Unmarshal(req.Config.Msgpack, cfgSchema.ImpliedType()) - if err != nil { - srvResp.Diagnostics = convert.AppendProtoDiag(srvResp.Diagnostics, err) - srv.Send(srvResp) - return nil - } - resourceConfig := terraform.NewResourceConfigShimmed(cfgVal, cfgSchema) - - connVal, err := msgpack.Unmarshal(req.Connection.Msgpack, cty.Map(cty.String)) - if err != nil { - srvResp.Diagnostics = convert.AppendProtoDiag(srvResp.Diagnostics, err) - srv.Send(srvResp) - return nil - } - - conn := stringMapFromValue(connVal) - - instanceState := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: conn, - }, - Meta: make(map[string]interface{}), - } - - err = s.provisioner.Apply(uiOutput{srv}, instanceState, resourceConfig) - if err != nil { - srvResp.Diagnostics = convert.AppendProtoDiag(srvResp.Diagnostics, err) - srv.Send(srvResp) - } - return nil -} - -func (s *GRPCProvisionerServer) Stop(_ context.Context, req *proto.Stop_Request) (*proto.Stop_Response, error) { - resp := &proto.Stop_Response{} - - err := s.provisioner.Stop() - if err != nil { - resp.Error = err.Error() - } - - return resp, nil -} - -// FIXME: backported from go1.13 strings package, remove once terraform is -// using go >= 1.13 -// ToValidUTF8 returns a copy of the string s with each run of invalid UTF-8 byte sequences -// replaced by the replacement string, which may be empty. -func toValidUTF8(s, replacement string) string { - var b strings.Builder - - for i, c := range s { - if c != utf8.RuneError { - continue - } - - _, wid := utf8.DecodeRuneInString(s[i:]) - if wid == 1 { - b.Grow(len(s) + len(replacement)) - b.WriteString(s[:i]) - s = s[i:] - break - } - } - - // Fast path for unchanged input - if b.Cap() == 0 { // didn't call b.Grow above - return s - } - - invalid := false // previous byte was from an invalid UTF-8 sequence - for i := 0; i < len(s); { - c := s[i] - if c < utf8.RuneSelf { - i++ - invalid = false - b.WriteByte(c) - continue - } - _, wid := utf8.DecodeRuneInString(s[i:]) - if wid == 1 { - i++ - if !invalid { - invalid = true - b.WriteString(replacement) - } - continue - } - invalid = false - b.WriteString(s[i : i+wid]) - i += wid - } - - return b.String() -} diff --git a/vendor/github.com/hashicorp/terraform/helper/plugin/grpc_provisioner_test.go b/vendor/github.com/hashicorp/terraform/helper/plugin/grpc_provisioner_test.go deleted file mode 100644 index 9b38daf4..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/plugin/grpc_provisioner_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package plugin - -import ( - "testing" - "unicode/utf8" - - "github.com/golang/mock/gomock" - "github.com/hashicorp/terraform/helper/schema" - proto "github.com/hashicorp/terraform/internal/tfplugin5" - mockproto "github.com/hashicorp/terraform/plugin/mock_proto" - "github.com/hashicorp/terraform/terraform" - context "golang.org/x/net/context" -) - -var _ proto.ProvisionerServer = (*GRPCProvisionerServer)(nil) - -type validUTF8Matcher string - -func (m validUTF8Matcher) Matches(x interface{}) bool { - resp := x.(*proto.ProvisionResource_Response) - return utf8.Valid([]byte(resp.Output)) -} - -func (m validUTF8Matcher) String() string { - return string(m) -} - -func mockProvisionerServer(t *testing.T, c *gomock.Controller) *mockproto.MockProvisioner_ProvisionResourceServer { - server := mockproto.NewMockProvisioner_ProvisionResourceServer(c) - - server.EXPECT().Send( - validUTF8Matcher("check for valid utf8"), - ).Return(nil) - - return server -} - -// ensure that a provsioner cannot return invalid utf8 which isn't allowed in -// the grpc protocol. -func TestProvisionerInvalidUTF8(t *testing.T) { - p := &schema.Provisioner{ - ConnSchema: map[string]*schema.Schema{ - "foo": { - Type: schema.TypeString, - Optional: true, - }, - }, - - Schema: map[string]*schema.Schema{ - "foo": { - Type: schema.TypeInt, - Optional: true, - }, - }, - - ApplyFunc: func(ctx context.Context) error { - out := ctx.Value(schema.ProvOutputKey).(terraform.UIOutput) - out.Output("invalid \xc3\x28\n") - return nil - }, - } - - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - srv := mockProvisionerServer(t, ctrl) - cfg := &proto.DynamicValue{ - Msgpack: []byte("\x81\xa3foo\x01"), - } - conn := &proto.DynamicValue{ - Msgpack: []byte("\x81\xa3foo\xa4host"), - } - provisionerServer := NewGRPCProvisionerServerShim(p) - req := &proto.ProvisionResource_Request{ - Config: cfg, - Connection: conn, - } - - if err := provisionerServer.ProvisionResource(req, srv); err != nil { - t.Fatal(err) - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/plugin/unknown.go b/vendor/github.com/hashicorp/terraform/helper/plugin/unknown.go deleted file mode 100644 index 64a6784e..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/plugin/unknown.go +++ /dev/null @@ -1,131 +0,0 @@ -package plugin - -import ( - "fmt" - - "github.com/hashicorp/terraform/configs/configschema" - "github.com/zclconf/go-cty/cty" -) - -// SetUnknowns takes a cty.Value, and compares it to the schema setting any null -// values which are computed to unknown. -func SetUnknowns(val cty.Value, schema *configschema.Block) cty.Value { - if !val.IsKnown() { - return val - } - - // If the object was null, we still need to handle the top level attributes - // which might be computed, but we don't need to expand the blocks. - if val.IsNull() { - objMap := map[string]cty.Value{} - allNull := true - for name, attr := range schema.Attributes { - switch { - case attr.Computed: - objMap[name] = cty.UnknownVal(attr.Type) - allNull = false - default: - objMap[name] = cty.NullVal(attr.Type) - } - } - - // If this object has no unknown attributes, then we can leave it null. - if allNull { - return val - } - - return cty.ObjectVal(objMap) - } - - valMap := val.AsValueMap() - newVals := make(map[string]cty.Value) - - for name, attr := range schema.Attributes { - v := valMap[name] - - if attr.Computed && v.IsNull() { - newVals[name] = cty.UnknownVal(attr.Type) - continue - } - - newVals[name] = v - } - - for name, blockS := range schema.BlockTypes { - blockVal := valMap[name] - if blockVal.IsNull() || !blockVal.IsKnown() { - newVals[name] = blockVal - continue - } - - blockValType := blockVal.Type() - blockElementType := blockS.Block.ImpliedType() - - // This switches on the value type here, so we can correctly switch - // between Tuples/Lists and Maps/Objects. - switch { - case blockS.Nesting == configschema.NestingSingle || blockS.Nesting == configschema.NestingGroup: - // NestingSingle is the only exception here, where we treat the - // block directly as an object - newVals[name] = SetUnknowns(blockVal, &blockS.Block) - - case blockValType.IsSetType(), blockValType.IsListType(), blockValType.IsTupleType(): - listVals := blockVal.AsValueSlice() - newListVals := make([]cty.Value, 0, len(listVals)) - - for _, v := range listVals { - newListVals = append(newListVals, SetUnknowns(v, &blockS.Block)) - } - - switch { - case blockValType.IsSetType(): - switch len(newListVals) { - case 0: - newVals[name] = cty.SetValEmpty(blockElementType) - default: - newVals[name] = cty.SetVal(newListVals) - } - case blockValType.IsListType(): - switch len(newListVals) { - case 0: - newVals[name] = cty.ListValEmpty(blockElementType) - default: - newVals[name] = cty.ListVal(newListVals) - } - case blockValType.IsTupleType(): - newVals[name] = cty.TupleVal(newListVals) - } - - case blockValType.IsMapType(), blockValType.IsObjectType(): - mapVals := blockVal.AsValueMap() - newMapVals := make(map[string]cty.Value) - - for k, v := range mapVals { - newMapVals[k] = SetUnknowns(v, &blockS.Block) - } - - switch { - case blockValType.IsMapType(): - switch len(newMapVals) { - case 0: - newVals[name] = cty.MapValEmpty(blockElementType) - default: - newVals[name] = cty.MapVal(newMapVals) - } - case blockValType.IsObjectType(): - if len(newMapVals) == 0 { - // We need to populate empty values to make a valid object. - for attr, ty := range blockElementType.AttributeTypes() { - newMapVals[attr] = cty.NullVal(ty) - } - } - newVals[name] = cty.ObjectVal(newMapVals) - } - - default: - panic(fmt.Sprintf("failed to set unknown values for nested block %q:%#v", name, blockValType)) - } - } - - return cty.ObjectVal(newVals) -} diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/error.go b/vendor/github.com/hashicorp/terraform/helper/resource/error.go deleted file mode 100644 index 7ee21614..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/resource/error.go +++ /dev/null @@ -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) -} diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/grpc_test_provider.go b/vendor/github.com/hashicorp/terraform/helper/resource/grpc_test_provider.go deleted file mode 100644 index 0742e993..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/resource/grpc_test_provider.go +++ /dev/null @@ -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 -} diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/id.go b/vendor/github.com/hashicorp/terraform/helper/resource/id.go deleted file mode 100644 index 44949550..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/resource/id.go +++ /dev/null @@ -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) -} diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/state.go b/vendor/github.com/hashicorp/terraform/helper/resource/state.go deleted file mode 100644 index 88a83966..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/resource/state.go +++ /dev/null @@ -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, - } - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/state_shim.go b/vendor/github.com/hashicorp/terraform/helper/resource/state_shim.go deleted file mode 100644 index aa2231b2..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/resource/state_shim.go +++ /dev/null @@ -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) -} diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/state_shim_test.go b/vendor/github.com/hashicorp/terraform/helper/resource/state_shim_test.go deleted file mode 100644 index 789e1295..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/resource/state_shim_test.go +++ /dev/null @@ -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") - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/state_test.go b/vendor/github.com/hashicorp/terraform/helper/resource/state_test.go deleted file mode 100644 index 6d6b329a..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/resource/state_test.go +++ /dev/null @@ -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") - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/testing.go b/vendor/github.com/hashicorp/terraform/helper/resource/testing.go deleted file mode 100644 index c36ff4b2..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/resource/testing.go +++ /dev/null @@ -1,1285 +0,0 @@ -package resource - -import ( - "bytes" - "flag" - "fmt" - "io/ioutil" - "log" - "os" - "path/filepath" - "reflect" - "regexp" - "strings" - "testing" - - "github.com/davecgh/go-spew/spew" - "github.com/hashicorp/errwrap" - "github.com/hashicorp/go-multierror" - "github.com/mitchellh/colorstring" - - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/command/format" - "github.com/hashicorp/terraform/configs" - "github.com/hashicorp/terraform/configs/configload" - "github.com/hashicorp/terraform/internal/initwd" - "github.com/hashicorp/terraform/providers" - "github.com/hashicorp/terraform/states" - "github.com/hashicorp/terraform/terraform" - "github.com/hashicorp/terraform/tfdiags" - - _ "github.com/hashicorp/terraform/internal/logging" -) - -// flagSweep is a flag available when running tests on the command line. It -// contains a comma seperated list of regions to for the sweeper functions to -// run in. This flag bypasses the normal Test path and instead runs functions designed to -// clean up any leaked resources a testing environment could have created. It is -// a best effort attempt, and relies on Provider authors to implement "Sweeper" -// methods for resources. - -// Adding Sweeper methods with AddTestSweepers will -// construct a list of sweeper funcs to be called here. We iterate through -// regions provided by the sweep flag, and for each region we iterate through the -// tests, and exit on any errors. At time of writing, sweepers are ran -// sequentially, however they can list dependencies to be ran first. We track -// the sweepers that have been ran, so as to not run a sweeper twice for a given -// region. -// -// WARNING: -// Sweepers are designed to be destructive. You should not use the -sweep flag -// in any environment that is not strictly a test environment. Resources will be -// destroyed. - -var flagSweep = flag.String("sweep", "", "List of Regions to run available Sweepers") -var flagSweepRun = flag.String("sweep-run", "", "Comma seperated list of Sweeper Tests to run") -var sweeperFuncs map[string]*Sweeper - -// map of sweepers that have ran, and the success/fail status based on any error -// raised -var sweeperRunList map[string]bool - -// type SweeperFunc is a signature for a function that acts as a sweeper. It -// accepts a string for the region that the sweeper is to be ran in. This -// function must be able to construct a valid client for that region. -type SweeperFunc func(r string) error - -type Sweeper struct { - // Name for sweeper. Must be unique to be ran by the Sweeper Runner - Name string - - // Dependencies list the const names of other Sweeper functions that must be ran - // prior to running this Sweeper. This is an ordered list that will be invoked - // recursively at the helper/resource level - Dependencies []string - - // Sweeper function that when invoked sweeps the Provider of specific - // resources - F SweeperFunc -} - -func init() { - sweeperFuncs = make(map[string]*Sweeper) -} - -// AddTestSweepers function adds a given name and Sweeper configuration -// pair to the internal sweeperFuncs map. Invoke this function to register a -// resource sweeper to be available for running when the -sweep flag is used -// with `go test`. Sweeper names must be unique to help ensure a given sweeper -// is only ran once per run. -func AddTestSweepers(name string, s *Sweeper) { - if _, ok := sweeperFuncs[name]; ok { - log.Fatalf("[ERR] Error adding (%s) to sweeperFuncs: function already exists in map", name) - } - - sweeperFuncs[name] = s -} - -func TestMain(m *testing.M) { - flag.Parse() - if *flagSweep != "" { - // parse flagSweep contents for regions to run - regions := strings.Split(*flagSweep, ",") - - // get filtered list of sweepers to run based on sweep-run flag - sweepers := filterSweepers(*flagSweepRun, sweeperFuncs) - for _, region := range regions { - region = strings.TrimSpace(region) - // reset sweeperRunList for each region - sweeperRunList = map[string]bool{} - - log.Printf("[DEBUG] Running Sweepers for region (%s):\n", region) - for _, sweeper := range sweepers { - if err := runSweeperWithRegion(region, sweeper); err != nil { - log.Fatalf("[ERR] error running (%s): %s", sweeper.Name, err) - } - } - - log.Printf("Sweeper Tests ran:\n") - for s, _ := range sweeperRunList { - fmt.Printf("\t- %s\n", s) - } - } - } else { - os.Exit(m.Run()) - } -} - -// filterSweepers takes a comma seperated string listing the names of sweepers -// to be ran, and returns a filtered set from the list of all of sweepers to -// run based on the names given. -func filterSweepers(f string, source map[string]*Sweeper) map[string]*Sweeper { - filterSlice := strings.Split(strings.ToLower(f), ",") - if len(filterSlice) == 1 && filterSlice[0] == "" { - // if the filter slice is a single element of "" then no sweeper list was - // given, so just return the full list - return source - } - - sweepers := make(map[string]*Sweeper) - for name, sweeper := range source { - for _, s := range filterSlice { - if strings.Contains(strings.ToLower(name), s) { - sweepers[name] = sweeper - } - } - } - return sweepers -} - -// runSweeperWithRegion recieves a sweeper and a region, and recursively calls -// itself with that region for every dependency found for that sweeper. If there -// are no dependencies, invoke the contained sweeper fun with the region, and -// add the success/fail status to the sweeperRunList. -func runSweeperWithRegion(region string, s *Sweeper) error { - for _, dep := range s.Dependencies { - if depSweeper, ok := sweeperFuncs[dep]; ok { - log.Printf("[DEBUG] Sweeper (%s) has dependency (%s), running..", s.Name, dep) - if err := runSweeperWithRegion(region, depSweeper); err != nil { - return err - } - } else { - log.Printf("[DEBUG] Sweeper (%s) has dependency (%s), but that sweeper was not found", s.Name, dep) - } - } - - if _, ok := sweeperRunList[s.Name]; ok { - log.Printf("[DEBUG] Sweeper (%s) already ran in region (%s)", s.Name, region) - return nil - } - - runE := s.F(region) - if runE == nil { - sweeperRunList[s.Name] = true - } else { - sweeperRunList[s.Name] = false - } - - return runE -} - -const TestEnvVar = "TF_ACC" - -// TestProvider can be implemented by any ResourceProvider to provide custom -// reset functionality at the start of an acceptance test. -// The helper/schema Provider implements this interface. -type TestProvider interface { - TestReset() error -} - -// TestCheckFunc is the callback type used with acceptance tests to check -// the state of a resource. The state passed in is the latest state known, -// or in the case of being after a destroy, it is the last known state when -// it was created. -type TestCheckFunc func(*terraform.State) error - -// ImportStateCheckFunc is the check function for ImportState tests -type ImportStateCheckFunc func([]*terraform.InstanceState) error - -// ImportStateIdFunc is an ID generation function to help with complex ID -// generation for ImportState tests. -type ImportStateIdFunc func(*terraform.State) (string, error) - -// TestCase is a single acceptance test case used to test the apply/destroy -// lifecycle of a resource in a specific configuration. -// -// When the destroy plan is executed, the config from the last TestStep -// is used to plan it. -type TestCase struct { - // IsUnitTest allows a test to run regardless of the TF_ACC - // environment variable. This should be used with care - only for - // fast tests on local resources (e.g. remote state with a local - // backend) but can be used to increase confidence in correct - // operation of Terraform without waiting for a full acctest run. - IsUnitTest bool - - // PreCheck, if non-nil, will be called before any test steps are - // executed. It will only be executed in the case that the steps - // would run, so it can be used for some validation before running - // acceptance tests, such as verifying that keys are setup. - PreCheck func() - - // Providers is the ResourceProvider that will be under test. - // - // Alternately, ProviderFactories can be specified for the providers - // that are valid. This takes priority over Providers. - // - // The end effect of each is the same: specifying the providers that - // are used within the tests. - Providers map[string]terraform.ResourceProvider - ProviderFactories map[string]terraform.ResourceProviderFactory - - // PreventPostDestroyRefresh can be set to true for cases where data sources - // are tested alongside real resources - PreventPostDestroyRefresh bool - - // CheckDestroy is called after the resource is finally destroyed - // to allow the tester to test that the resource is truly gone. - CheckDestroy TestCheckFunc - - // Steps are the apply sequences done within the context of the - // same state. Each step can have its own check to verify correctness. - Steps []TestStep - - // The settings below control the "ID-only refresh test." This is - // an enabled-by-default test that tests that a refresh can be - // refreshed with only an ID to result in the same attributes. - // This validates completeness of Refresh. - // - // IDRefreshName is the name of the resource to check. This will - // default to the first non-nil primary resource in the state. - // - // IDRefreshIgnore is a list of configuration keys that will be ignored. - IDRefreshName string - IDRefreshIgnore []string -} - -// TestStep is a single apply sequence of a test, done within the -// context of a state. -// -// Multiple TestSteps can be sequenced in a Test to allow testing -// potentially complex update logic. In general, simply create/destroy -// tests will only need one step. -type TestStep struct { - // ResourceName should be set to the name of the resource - // that is being tested. Example: "aws_instance.foo". Various test - // modes use this to auto-detect state information. - // - // This is only required if the test mode settings below say it is - // for the mode you're using. - ResourceName string - - // PreConfig is called before the Config is applied to perform any per-step - // setup that needs to happen. This is called regardless of "test mode" - // below. - PreConfig func() - - // Taint is a list of resource addresses to taint prior to the execution of - // the step. Be sure to only include this at a step where the referenced - // address will be present in state, as it will fail the test if the resource - // is missing. - // - // This option is ignored on ImportState tests, and currently only works for - // resources in the root module path. - Taint []string - - //--------------------------------------------------------------- - // Test modes. One of the following groups of settings must be - // set to determine what the test step will do. Ideally we would've - // used Go interfaces here but there are now hundreds of tests we don't - // want to re-type so instead we just determine which step logic - // to run based on what settings below are set. - //--------------------------------------------------------------- - - //--------------------------------------------------------------- - // Plan, Apply testing - //--------------------------------------------------------------- - - // Config a string of the configuration to give to Terraform. If this - // is set, then the TestCase will execute this step with the same logic - // as a `terraform apply`. - Config string - - // Check is called after the Config is applied. Use this step to - // make your own API calls to check the status of things, and to - // inspect the format of the ResourceState itself. - // - // If an error is returned, the test will fail. In this case, a - // destroy plan will still be attempted. - // - // If this is nil, no check is done on this step. - Check TestCheckFunc - - // Destroy will create a destroy plan if set to true. - Destroy bool - - // ExpectNonEmptyPlan can be set to true for specific types of tests that are - // looking to verify that a diff occurs - ExpectNonEmptyPlan bool - - // ExpectError allows the construction of test cases that we expect to fail - // with an error. The specified regexp must match against the error for the - // test to pass. - ExpectError *regexp.Regexp - - // PlanOnly can be set to only run `plan` with this configuration, and not - // actually apply it. This is useful for ensuring config changes result in - // no-op plans - PlanOnly bool - - // PreventDiskCleanup can be set to true for testing terraform modules which - // require access to disk at runtime. Note that this will leave files in the - // temp folder - PreventDiskCleanup bool - - // PreventPostDestroyRefresh can be set to true for cases where data sources - // are tested alongside real resources - PreventPostDestroyRefresh bool - - // SkipFunc is called before applying config, but after PreConfig - // This is useful for defining test steps with platform-dependent checks - SkipFunc func() (bool, error) - - //--------------------------------------------------------------- - // ImportState testing - //--------------------------------------------------------------- - - // ImportState, if true, will test the functionality of ImportState - // by importing the resource with ResourceName (must be set) and the - // ID of that resource. - ImportState bool - - // ImportStateId is the ID to perform an ImportState operation with. - // This is optional. If it isn't set, then the resource ID is automatically - // determined by inspecting the state for ResourceName's ID. - ImportStateId string - - // ImportStateIdPrefix is the prefix added in front of ImportStateId. - // This can be useful in complex import cases, where more than one - // attribute needs to be passed on as the Import ID. Mainly in cases - // where the ID is not known, and a known prefix needs to be added to - // the unset ImportStateId field. - ImportStateIdPrefix string - - // ImportStateIdFunc is a function that can be used to dynamically generate - // the ID for the ImportState tests. It is sent the state, which can be - // checked to derive the attributes necessary and generate the string in the - // desired format. - ImportStateIdFunc ImportStateIdFunc - - // ImportStateCheck checks the results of ImportState. It should be - // used to verify that the resulting value of ImportState has the - // proper resources, IDs, and attributes. - ImportStateCheck ImportStateCheckFunc - - // ImportStateVerify, if true, will also check that the state values - // that are finally put into the state after import match for all the - // IDs returned by the Import. Note that this checks for strict equality - // and does not respect DiffSuppressFunc or CustomizeDiff. - // - // ImportStateVerifyIgnore is a list of prefixes of fields that should - // not be verified to be equal. These can be set to ephemeral fields or - // fields that can't be refreshed and don't matter. - ImportStateVerify bool - ImportStateVerifyIgnore []string - - // provider s is used internally to maintain a reference to the - // underlying providers during the tests - providers map[string]terraform.ResourceProvider -} - -// Set to a file mask in sprintf format where %s is test name -const EnvLogPathMask = "TF_LOG_PATH_MASK" - -// ParallelTest performs an acceptance test on a resource, allowing concurrency -// with other ParallelTest. -// -// Tests will fail if they do not properly handle conditions to allow multiple -// tests to occur against the same resource or service (e.g. random naming). -// All other requirements of the Test function also apply to this function. -func ParallelTest(t TestT, c TestCase) { - t.Parallel() - Test(t, c) -} - -// Test performs an acceptance test on a resource. -// -// Tests are not run unless an environmental variable "TF_ACC" is -// set to some non-empty value. This is to avoid test cases surprising -// a user by creating real resources. -// -// Tests will fail unless the verbose flag (`go test -v`, or explicitly -// the "-test.v" flag) is set. Because some acceptance tests take quite -// long, we require the verbose flag so users are able to see progress -// output. -func Test(t TestT, c TestCase) { - // We only run acceptance tests if an env var is set because they're - // slow and generally require some outside configuration. You can opt out - // of this with OverrideEnvVar on individual TestCases. - if os.Getenv(TestEnvVar) == "" && !c.IsUnitTest { - t.Skip(fmt.Sprintf( - "Acceptance tests skipped unless env '%s' set", - TestEnvVar)) - return - } - - // We require verbose mode so that the user knows what is going on. - if !testTesting && !testing.Verbose() && !c.IsUnitTest { - t.Fatal("Acceptance tests must be run with the -v flag on tests") - return - } - - // Run the PreCheck if we have it - if c.PreCheck != nil { - c.PreCheck() - } - - providerFactories, err := testProviderFactories(c) - if err != nil { - t.Fatal(err) - } - - // get instances of all providers, so we can use the individual - // resources to shim the state during the tests. - providers := make(map[string]terraform.ResourceProvider) - legacyProviderFactories, err := testProviderFactoriesLegacy(c) - if err != nil { - t.Fatal(err) - } - for name, pf := range legacyProviderFactories { - p, err := pf() - if err != nil { - t.Fatal(err) - } - providers[name] = p - } - - opts := terraform.ContextOpts{Providers: providerFactories} - - // A single state variable to track the lifecycle, starting with no state - var state *terraform.State - - // Go through each step and run it - var idRefreshCheck *terraform.ResourceState - idRefresh := c.IDRefreshName != "" - errored := false - for i, step := range c.Steps { - // insert the providers into the step so we can get the resources for - // shimming the state - step.providers = providers - - var err error - log.Printf("[DEBUG] Test: Executing step %d", i) - - if step.SkipFunc != nil { - skip, err := step.SkipFunc() - if err != nil { - t.Fatal(err) - } - if skip { - log.Printf("[WARN] Skipping step %d", i) - continue - } - } - - if step.Config == "" && !step.ImportState { - err = fmt.Errorf( - "unknown test mode for step. Please see TestStep docs\n\n%#v", - step) - } else { - if step.ImportState { - if step.Config == "" { - step.Config = testProviderConfig(c) - } - - // Can optionally set step.Config in addition to - // step.ImportState, to provide config for the import. - state, err = testStepImportState(opts, state, step) - } else { - state, err = testStepConfig(opts, state, step) - } - } - - // If we expected an error, but did not get one, fail - if err == nil && step.ExpectError != nil { - errored = true - t.Error(fmt.Sprintf( - "Step %d, no error received, but expected a match to:\n\n%s\n\n", - i, step.ExpectError)) - break - } - - // If there was an error, exit - if err != nil { - // Perhaps we expected an error? Check if it matches - if step.ExpectError != nil { - if !step.ExpectError.MatchString(err.Error()) { - errored = true - t.Error(fmt.Sprintf( - "Step %d, expected error:\n\n%s\n\nTo match:\n\n%s\n\n", - i, err, step.ExpectError)) - break - } - } else { - errored = true - t.Error(fmt.Sprintf("Step %d error: %s", i, detailedErrorMessage(err))) - break - } - } - - // If we've never checked an id-only refresh and our state isn't - // empty, find the first resource and test it. - if idRefresh && idRefreshCheck == nil && !state.Empty() { - // Find the first non-nil resource in the state - for _, m := range state.Modules { - if len(m.Resources) > 0 { - if v, ok := m.Resources[c.IDRefreshName]; ok { - idRefreshCheck = v - } - - break - } - } - - // If we have an instance to check for refreshes, do it - // immediately. We do it in the middle of another test - // because it shouldn't affect the overall state (refresh - // is read-only semantically) and we want to fail early if - // this fails. If refresh isn't read-only, then this will have - // caught a different bug. - if idRefreshCheck != nil { - log.Printf( - "[WARN] Test: Running ID-only refresh check on %s", - idRefreshCheck.Primary.ID) - if err := testIDOnlyRefresh(c, opts, step, idRefreshCheck); err != nil { - log.Printf("[ERROR] Test: ID-only test failed: %s", err) - t.Error(fmt.Sprintf( - "[ERROR] Test: ID-only test failed: %s", err)) - break - } - } - } - } - - // If we never checked an id-only refresh, it is a failure. - if idRefresh { - if !errored && len(c.Steps) > 0 && idRefreshCheck == nil { - t.Error("ID-only refresh check never ran.") - } - } - - // If we have a state, then run the destroy - if state != nil { - lastStep := c.Steps[len(c.Steps)-1] - destroyStep := TestStep{ - Config: lastStep.Config, - Check: c.CheckDestroy, - Destroy: true, - PreventDiskCleanup: lastStep.PreventDiskCleanup, - PreventPostDestroyRefresh: c.PreventPostDestroyRefresh, - providers: providers, - } - - log.Printf("[WARN] Test: Executing destroy step") - state, err := testStep(opts, state, destroyStep) - if err != nil { - t.Error(fmt.Sprintf( - "Error destroying resource! WARNING: Dangling resources\n"+ - "may exist. The full state and error is shown below.\n\n"+ - "Error: %s\n\nState: %s", - err, - state)) - } - } else { - log.Printf("[WARN] Skipping destroy test since there is no state.") - } -} - -// testProviderConfig takes the list of Providers in a TestCase and returns a -// config with only empty provider blocks. This is useful for Import, where no -// config is provided, but the providers must be defined. -func testProviderConfig(c TestCase) string { - var lines []string - for p := range c.Providers { - lines = append(lines, fmt.Sprintf("provider %q {}\n", p)) - } - - return strings.Join(lines, "") -} - -// testProviderFactoriesLegacy is like testProviderFactories but it returns -// providers implementing the legacy interface terraform.ResourceProvider, -// rather than the current providers.Interface. -// -// It also identifies all providers as legacy-style single names rather than -// full addresses, for compatibility with legacy code that doesn't understand -// FQNs. -func testProviderFactoriesLegacy(c TestCase) (map[string]terraform.ResourceProviderFactory, error) { - ctxProviders := make(map[string]terraform.ResourceProviderFactory) - for k, pf := range c.ProviderFactories { - ctxProviders[k] = pf - } - - // add any fixed providers - for k, p := range c.Providers { - ctxProviders[k] = terraform.ResourceProviderFactoryFixed(p) - } - return ctxProviders, nil -} - -// testProviderFactories combines the fixed Providers and -// ResourceProviderFactory functions into a single map of -// ResourceProviderFactory functions. -func testProviderFactories(c TestCase) (map[addrs.Provider]providers.Factory, error) { - ctxProviders, err := testProviderFactoriesLegacy(c) - if err != nil { - return nil, err - } - - // We additionally wrap all of the factories as a GRPCTestProvider, which - // allows them to appear as a new-style providers.Interface, rather than - // the legacy terraform.ResourceProvider. - newProviders := make(map[addrs.Provider]providers.Factory) - for legacyName, pf := range ctxProviders { - factory := pf // must copy to ensure each closure sees its own value - newProviders[addrs.NewDefaultProvider(legacyName)] = func() (providers.Interface, error) { - p, err := factory() - if err != nil { - return nil, err - } - - // The provider is wrapped in a GRPCTestProvider so that it can be - // passed back to terraform core as a providers.Interface, rather - // than the legacy ResourceProvider. - return GRPCTestProvider(p), nil - } - } - - return newProviders, nil -} - -// UnitTest is a helper to force the acceptance testing harness to run in the -// normal unit test suite. This should only be used for resource that don't -// have any external dependencies. -func UnitTest(t TestT, c TestCase) { - c.IsUnitTest = true - Test(t, c) -} - -func testIDOnlyRefresh(c TestCase, opts terraform.ContextOpts, step TestStep, r *terraform.ResourceState) error { - // TODO: We guard by this right now so master doesn't explode. We - // need to remove this eventually to make this part of the normal tests. - if os.Getenv("TF_ACC_IDONLY") == "" { - return nil - } - - addr := addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: r.Type, - Name: "foo", - }.Instance(addrs.NoKey) - absAddr := addr.Absolute(addrs.RootModuleInstance) - - // Build the state. The state is just the resource with an ID. There - // are no attributes. We only set what is needed to perform a refresh. - state := states.NewState() - state.RootModule().SetResourceInstanceCurrent( - addr, - &states.ResourceInstanceObjectSrc{ - AttrsFlat: r.Primary.Attributes, - Status: states.ObjectReady, - }, - addrs.AbsProviderConfig{ - Provider: addrs.NewDefaultProvider("placeholder"), - Module: addrs.RootModule, - }, - ) - - // Create the config module. We use the full config because Refresh - // doesn't have access to it and we may need things like provider - // configurations. The initial implementation of id-only checks used - // an empty config module, but that caused the aforementioned problems. - cfg, err := testConfig(opts, step) - if err != nil { - return err - } - - // Initialize the context - opts.Config = cfg - opts.State = state - ctx, ctxDiags := terraform.NewContext(&opts) - if ctxDiags.HasErrors() { - return ctxDiags.Err() - } - if diags := ctx.Validate(); len(diags) > 0 { - if diags.HasErrors() { - return errwrap.Wrapf("config is invalid: {{err}}", diags.Err()) - } - - log.Printf("[WARN] Config warnings:\n%s", diags.Err().Error()) - } - - // Refresh! - state, refreshDiags := ctx.Refresh() - if refreshDiags.HasErrors() { - return refreshDiags.Err() - } - - // Verify attribute equivalence. - actualR := state.ResourceInstance(absAddr) - if actualR == nil { - return fmt.Errorf("Resource gone!") - } - if actualR.Current == nil { - return fmt.Errorf("Resource has no primary instance") - } - actual := actualR.Current.AttrsFlat - expected := r.Primary.Attributes - // Remove fields we're ignoring - for _, v := range c.IDRefreshIgnore { - for k, _ := range actual { - if strings.HasPrefix(k, v) { - delete(actual, k) - } - } - for k, _ := range expected { - if strings.HasPrefix(k, v) { - delete(expected, k) - } - } - } - - 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 fmt.Errorf( - "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 nil -} - -func testConfig(opts terraform.ContextOpts, step TestStep) (*configs.Config, error) { - if step.PreConfig != nil { - step.PreConfig() - } - - cfgPath, err := ioutil.TempDir("", "tf-test") - if err != nil { - return nil, fmt.Errorf("Error creating temporary directory for config: %s", err) - } - - if step.PreventDiskCleanup { - log.Printf("[INFO] Skipping defer os.RemoveAll call") - } else { - defer os.RemoveAll(cfgPath) - } - - // Write the main configuration file - err = ioutil.WriteFile(filepath.Join(cfgPath, "main.tf"), []byte(step.Config), os.ModePerm) - if err != nil { - return nil, fmt.Errorf("Error creating temporary file for config: %s", err) - } - - // Create directory for our child modules, if any. - modulesDir := filepath.Join(cfgPath, ".modules") - err = os.Mkdir(modulesDir, os.ModePerm) - if err != nil { - return nil, fmt.Errorf("Error creating child modules directory: %s", err) - } - - inst := initwd.NewModuleInstaller(modulesDir, nil) - _, installDiags := inst.InstallModules(cfgPath, true, initwd.ModuleInstallHooksImpl{}) - if installDiags.HasErrors() { - return nil, installDiags.Err() - } - - loader, err := configload.NewLoader(&configload.Config{ - ModulesDir: modulesDir, - }) - if err != nil { - return nil, fmt.Errorf("failed to create config loader: %s", err) - } - - config, configDiags := loader.LoadConfig(cfgPath) - if configDiags.HasErrors() { - return nil, configDiags - } - - return config, nil -} - -func testResource(c TestStep, state *terraform.State) (*terraform.ResourceState, error) { - if c.ResourceName == "" { - return nil, fmt.Errorf("ResourceName must be set in TestStep") - } - - for _, m := range state.Modules { - if len(m.Resources) > 0 { - if v, ok := m.Resources[c.ResourceName]; ok { - return v, nil - } - } - } - - return nil, fmt.Errorf( - "Resource specified by ResourceName couldn't be found: %s", c.ResourceName) -} - -// ComposeTestCheckFunc lets you compose multiple TestCheckFuncs into -// a single TestCheckFunc. -// -// As a user testing their provider, this lets you decompose your checks -// into smaller pieces more easily. -func ComposeTestCheckFunc(fs ...TestCheckFunc) TestCheckFunc { - return func(s *terraform.State) error { - for i, f := range fs { - if err := f(s); err != nil { - return fmt.Errorf("Check %d/%d error: %s", i+1, len(fs), err) - } - } - - return nil - } -} - -// ComposeAggregateTestCheckFunc lets you compose multiple TestCheckFuncs into -// a single TestCheckFunc. -// -// As a user testing their provider, this lets you decompose your checks -// into smaller pieces more easily. -// -// Unlike ComposeTestCheckFunc, ComposeAggergateTestCheckFunc runs _all_ of the -// TestCheckFuncs and aggregates failures. -func ComposeAggregateTestCheckFunc(fs ...TestCheckFunc) TestCheckFunc { - return func(s *terraform.State) error { - var result *multierror.Error - - for i, f := range fs { - if err := f(s); err != nil { - result = multierror.Append(result, fmt.Errorf("Check %d/%d error: %s", i+1, len(fs), err)) - } - } - - return result.ErrorOrNil() - } -} - -// TestCheckResourceAttrSet is a TestCheckFunc which ensures a value -// exists in state for the given name/key combination. It is useful when -// testing that computed values were set, when it is not possible to -// know ahead of time what the values will be. -func TestCheckResourceAttrSet(name, key string) TestCheckFunc { - return func(s *terraform.State) error { - is, err := primaryInstanceState(s, name) - if err != nil { - return err - } - - return testCheckResourceAttrSet(is, name, key) - } -} - -// TestCheckModuleResourceAttrSet - as per TestCheckResourceAttrSet but with -// support for non-root modules -func TestCheckModuleResourceAttrSet(mp []string, name string, key string) TestCheckFunc { - mpt := addrs.Module(mp).UnkeyedInstanceShim() - return func(s *terraform.State) error { - is, err := modulePathPrimaryInstanceState(s, mpt, name) - if err != nil { - return err - } - - return testCheckResourceAttrSet(is, name, key) - } -} - -func testCheckResourceAttrSet(is *terraform.InstanceState, name string, key string) error { - if val, ok := is.Attributes[key]; !ok || val == "" { - return fmt.Errorf("%s: Attribute '%s' expected to be set", name, key) - } - - return nil -} - -// TestCheckResourceAttr is a TestCheckFunc which validates -// the value in state for the given name/key combination. -func TestCheckResourceAttr(name, key, value string) TestCheckFunc { - return func(s *terraform.State) error { - is, err := primaryInstanceState(s, name) - if err != nil { - return err - } - - return testCheckResourceAttr(is, name, key, value) - } -} - -// TestCheckModuleResourceAttr - as per TestCheckResourceAttr but with -// support for non-root modules -func TestCheckModuleResourceAttr(mp []string, name string, key string, value string) TestCheckFunc { - mpt := addrs.Module(mp).UnkeyedInstanceShim() - return func(s *terraform.State) error { - is, err := modulePathPrimaryInstanceState(s, mpt, name) - if err != nil { - return err - } - - return testCheckResourceAttr(is, name, key, value) - } -} - -func testCheckResourceAttr(is *terraform.InstanceState, name string, key string, value string) error { - // Empty containers may be elided from the state. - // If the intent here is to check for an empty container, allow the key to - // also be non-existent. - emptyCheck := false - if value == "0" && (strings.HasSuffix(key, ".#") || strings.HasSuffix(key, ".%")) { - emptyCheck = true - } - - if v, ok := is.Attributes[key]; !ok || v != value { - if emptyCheck && !ok { - return nil - } - - if !ok { - return fmt.Errorf("%s: Attribute '%s' not found", name, key) - } - - return fmt.Errorf( - "%s: Attribute '%s' expected %#v, got %#v", - name, - key, - value, - v) - } - return nil -} - -// TestCheckNoResourceAttr is a TestCheckFunc which ensures that -// NO value exists in state for the given name/key combination. -func TestCheckNoResourceAttr(name, key string) TestCheckFunc { - return func(s *terraform.State) error { - is, err := primaryInstanceState(s, name) - if err != nil { - return err - } - - return testCheckNoResourceAttr(is, name, key) - } -} - -// TestCheckModuleNoResourceAttr - as per TestCheckNoResourceAttr but with -// support for non-root modules -func TestCheckModuleNoResourceAttr(mp []string, name string, key string) TestCheckFunc { - mpt := addrs.Module(mp).UnkeyedInstanceShim() - return func(s *terraform.State) error { - is, err := modulePathPrimaryInstanceState(s, mpt, name) - if err != nil { - return err - } - - return testCheckNoResourceAttr(is, name, key) - } -} - -func testCheckNoResourceAttr(is *terraform.InstanceState, name string, key string) error { - // Empty containers may sometimes be included in the state. - // If the intent here is to check for an empty container, allow the value to - // also be "0". - emptyCheck := false - if strings.HasSuffix(key, ".#") || strings.HasSuffix(key, ".%") { - emptyCheck = true - } - - val, exists := is.Attributes[key] - if emptyCheck && val == "0" { - return nil - } - - if exists { - return fmt.Errorf("%s: Attribute '%s' found when not expected", name, key) - } - - return nil -} - -// TestMatchResourceAttr is a TestCheckFunc which checks that the value -// in state for the given name/key combination matches the given regex. -func TestMatchResourceAttr(name, key string, r *regexp.Regexp) TestCheckFunc { - return func(s *terraform.State) error { - is, err := primaryInstanceState(s, name) - if err != nil { - return err - } - - return testMatchResourceAttr(is, name, key, r) - } -} - -// TestModuleMatchResourceAttr - as per TestMatchResourceAttr but with -// support for non-root modules -func TestModuleMatchResourceAttr(mp []string, name string, key string, r *regexp.Regexp) TestCheckFunc { - mpt := addrs.Module(mp).UnkeyedInstanceShim() - return func(s *terraform.State) error { - is, err := modulePathPrimaryInstanceState(s, mpt, name) - if err != nil { - return err - } - - return testMatchResourceAttr(is, name, key, r) - } -} - -func testMatchResourceAttr(is *terraform.InstanceState, name string, key string, r *regexp.Regexp) error { - if !r.MatchString(is.Attributes[key]) { - return fmt.Errorf( - "%s: Attribute '%s' didn't match %q, got %#v", - name, - key, - r.String(), - is.Attributes[key]) - } - - return nil -} - -// TestCheckResourceAttrPtr is like TestCheckResourceAttr except the -// value is a pointer so that it can be updated while the test is running. -// It will only be dereferenced at the point this step is run. -func TestCheckResourceAttrPtr(name string, key string, value *string) TestCheckFunc { - return func(s *terraform.State) error { - return TestCheckResourceAttr(name, key, *value)(s) - } -} - -// TestCheckModuleResourceAttrPtr - as per TestCheckResourceAttrPtr but with -// support for non-root modules -func TestCheckModuleResourceAttrPtr(mp []string, name string, key string, value *string) TestCheckFunc { - return func(s *terraform.State) error { - return TestCheckModuleResourceAttr(mp, name, key, *value)(s) - } -} - -// TestCheckResourceAttrPair is a TestCheckFunc which validates that the values -// in state for a pair of name/key combinations are equal. -func TestCheckResourceAttrPair(nameFirst, keyFirst, nameSecond, keySecond string) TestCheckFunc { - return func(s *terraform.State) error { - isFirst, err := primaryInstanceState(s, nameFirst) - if err != nil { - return err - } - - isSecond, err := primaryInstanceState(s, nameSecond) - if err != nil { - return err - } - - return testCheckResourceAttrPair(isFirst, nameFirst, keyFirst, isSecond, nameSecond, keySecond) - } -} - -// TestCheckModuleResourceAttrPair - as per TestCheckResourceAttrPair but with -// support for non-root modules -func TestCheckModuleResourceAttrPair(mpFirst []string, nameFirst string, keyFirst string, mpSecond []string, nameSecond string, keySecond string) TestCheckFunc { - mptFirst := addrs.Module(mpFirst).UnkeyedInstanceShim() - mptSecond := addrs.Module(mpSecond).UnkeyedInstanceShim() - return func(s *terraform.State) error { - isFirst, err := modulePathPrimaryInstanceState(s, mptFirst, nameFirst) - if err != nil { - return err - } - - isSecond, err := modulePathPrimaryInstanceState(s, mptSecond, nameSecond) - if err != nil { - return err - } - - return testCheckResourceAttrPair(isFirst, nameFirst, keyFirst, isSecond, nameSecond, keySecond) - } -} - -func testCheckResourceAttrPair(isFirst *terraform.InstanceState, nameFirst string, keyFirst string, isSecond *terraform.InstanceState, nameSecond string, keySecond string) error { - vFirst, okFirst := isFirst.Attributes[keyFirst] - vSecond, okSecond := isSecond.Attributes[keySecond] - - // Container count values of 0 should not be relied upon, and not reliably - // maintained by helper/schema. For the purpose of tests, consider unset and - // 0 to be equal. - if len(keyFirst) > 2 && len(keySecond) > 2 && keyFirst[len(keyFirst)-2:] == keySecond[len(keySecond)-2:] && - (strings.HasSuffix(keyFirst, ".#") || strings.HasSuffix(keyFirst, ".%")) { - // they have the same suffix, and it is a collection count key. - if vFirst == "0" || vFirst == "" { - okFirst = false - } - if vSecond == "0" || vSecond == "" { - okSecond = false - } - } - - if okFirst != okSecond { - if !okFirst { - return fmt.Errorf("%s: Attribute %q not set, but %q is set in %s as %q", nameFirst, keyFirst, keySecond, nameSecond, vSecond) - } - return fmt.Errorf("%s: Attribute %q is %q, but %q is not set in %s", nameFirst, keyFirst, vFirst, keySecond, nameSecond) - } - if !(okFirst || okSecond) { - // If they both don't exist then they are equally unset, so that's okay. - return nil - } - - if vFirst != vSecond { - return fmt.Errorf( - "%s: Attribute '%s' expected %#v, got %#v", - nameFirst, - keyFirst, - vSecond, - vFirst) - } - - return nil -} - -// TestCheckOutput checks an output in the Terraform configuration -func TestCheckOutput(name, value string) TestCheckFunc { - return func(s *terraform.State) error { - ms := s.RootModule() - rs, ok := ms.Outputs[name] - if !ok { - return fmt.Errorf("Not found: %s", name) - } - - if rs.Value != value { - return fmt.Errorf( - "Output '%s': expected %#v, got %#v", - name, - value, - rs) - } - - return nil - } -} - -func TestMatchOutput(name string, r *regexp.Regexp) TestCheckFunc { - return func(s *terraform.State) error { - ms := s.RootModule() - rs, ok := ms.Outputs[name] - if !ok { - return fmt.Errorf("Not found: %s", name) - } - - if !r.MatchString(rs.Value.(string)) { - return fmt.Errorf( - "Output '%s': %#v didn't match %q", - name, - rs, - r.String()) - } - - return nil - } -} - -// TestT is the interface used to handle the test lifecycle of a test. -// -// Users should just use a *testing.T object, which implements this. -type TestT interface { - Error(args ...interface{}) - Fatal(args ...interface{}) - Skip(args ...interface{}) - Name() string - Parallel() -} - -// This is set to true by unit tests to alter some behavior -var testTesting = false - -// modulePrimaryInstanceState returns the instance state for the given resource -// name in a ModuleState -func modulePrimaryInstanceState(s *terraform.State, ms *terraform.ModuleState, name string) (*terraform.InstanceState, error) { - rs, ok := ms.Resources[name] - if !ok { - return nil, fmt.Errorf("Not found: %s in %s", name, ms.Path) - } - - is := rs.Primary - if is == nil { - return nil, fmt.Errorf("No primary instance: %s in %s", name, ms.Path) - } - - return is, nil -} - -// modulePathPrimaryInstanceState returns the primary instance state for the -// given resource name in a given module path. -func modulePathPrimaryInstanceState(s *terraform.State, mp addrs.ModuleInstance, name string) (*terraform.InstanceState, error) { - ms := s.ModuleByPath(mp) - if ms == nil { - return nil, fmt.Errorf("No module found at: %s", mp) - } - - return modulePrimaryInstanceState(s, ms, name) -} - -// primaryInstanceState returns the primary instance state for the given -// resource name in the root module. -func primaryInstanceState(s *terraform.State, name string) (*terraform.InstanceState, error) { - ms := s.RootModule() - return modulePrimaryInstanceState(s, ms, name) -} - -// operationError is a specialized implementation of error used to describe -// failures during one of the several operations performed for a particular -// test case. -type operationError struct { - OpName string - Diags tfdiags.Diagnostics -} - -func newOperationError(opName string, diags tfdiags.Diagnostics) error { - return operationError{opName, diags} -} - -// Error returns a terse error string containing just the basic diagnostic -// messages, for situations where normal Go error behavior is appropriate. -func (err operationError) Error() string { - return fmt.Sprintf("errors during %s: %s", err.OpName, err.Diags.Err().Error()) -} - -// ErrorDetail is like Error except it includes verbosely-rendered diagnostics -// similar to what would come from a normal Terraform run, which include -// additional context not included in Error(). -func (err operationError) ErrorDetail() string { - var buf bytes.Buffer - fmt.Fprintf(&buf, "errors during %s:", err.OpName) - clr := &colorstring.Colorize{Disable: true, Colors: colorstring.DefaultColors} - for _, diag := range err.Diags { - diagStr := format.Diagnostic(diag, nil, clr, 78) - buf.WriteByte('\n') - buf.WriteString(diagStr) - } - return buf.String() -} - -// detailedErrorMessage is a helper for calling ErrorDetail on an error if -// it is an operationError or just taking Error otherwise. -func detailedErrorMessage(err error) string { - switch tErr := err.(type) { - case operationError: - return tErr.ErrorDetail() - default: - return err.Error() - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go b/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go deleted file mode 100644 index 74739c8a..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go +++ /dev/null @@ -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 = "" - } - // NOTE: we don't support 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 -} diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/testing_import_state.go b/vendor/github.com/hashicorp/terraform/helper/resource/testing_import_state.go deleted file mode 100644 index e163770e..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/resource/testing_import_state.go +++ /dev/null @@ -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 -} diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/testing_import_state_test.go b/vendor/github.com/hashicorp/terraform/helper/resource/testing_import_state_test.go deleted file mode 100644 index 9b2acc3c..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/resource/testing_import_state_test.go +++ /dev/null @@ -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") - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/testing_test.go b/vendor/github.com/hashicorp/terraform/helper/resource/testing_test.go deleted file mode 100644 index 5d489177..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/resource/testing_test.go +++ /dev/null @@ -1,1361 +0,0 @@ -package resource - -import ( - "errors" - "flag" - "fmt" - "os" - "reflect" - "regexp" - "sort" - "strings" - "sync" - "sync/atomic" - "testing" - - "github.com/hashicorp/go-multierror" - "github.com/hashicorp/terraform/terraform" -) - -func init() { - testTesting = true - - // TODO: Remove when we remove the guard on id checks - if err := os.Setenv("TF_ACC_IDONLY", "1"); err != nil { - panic(err) - } - - if err := os.Setenv(TestEnvVar, "1"); err != nil { - panic(err) - } -} - -// wrap the mock provider to implement TestProvider -type resetProvider struct { - *terraform.MockResourceProvider - mu sync.Mutex - TestResetCalled bool - TestResetError error -} - -func (p *resetProvider) TestReset() error { - p.mu.Lock() - defer p.mu.Unlock() - p.TestResetCalled = true - return p.TestResetError -} - -func TestParallelTest(t *testing.T) { - mt := new(mockT) - ParallelTest(mt, TestCase{}) - - if !mt.ParallelCalled { - t.Fatal("Parallel() not called") - } -} - -func TestTest(t *testing.T) { - t.Skip("test requires new provider implementation") - - mp := &resetProvider{ - MockResourceProvider: 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 - } - - var refreshCount int32 - mp.RefreshFn = func(*terraform.InstanceInfo, *terraform.InstanceState) (*terraform.InstanceState, error) { - atomic.AddInt32(&refreshCount, 1) - return &terraform.InstanceState{ID: "foo"}, nil - } - - checkDestroy := false - checkStep := false - - checkDestroyFn := func(*terraform.State) error { - checkDestroy = true - return nil - } - - checkStepFn := func(s *terraform.State) error { - checkStep = true - - rs, ok := s.RootModule().Resources["test_instance.foo"] - if !ok { - t.Error("test_instance.foo is not present") - return nil - } - is := rs.Primary - if is.ID != "foo" { - t.Errorf("bad check ID: %s", is.ID) - } - - return nil - } - - mt := new(mockT) - Test(mt, TestCase{ - Providers: map[string]terraform.ResourceProvider{ - "test": mp, - }, - CheckDestroy: checkDestroyFn, - Steps: []TestStep{ - TestStep{ - Config: testConfigStr, - Check: checkStepFn, - }, - }, - }) - - if mt.failed() { - t.Fatalf("test failed: %s", mt.failMessage()) - } - if mt.ParallelCalled { - t.Fatal("Parallel() called") - } - if !checkStep { - t.Fatal("didn't call check for step") - } - if !checkDestroy { - t.Fatal("didn't call check for destroy") - } - if !mp.TestResetCalled { - t.Fatal("didn't call TestReset") - } -} - -func TestTest_plan_only(t *testing.T) { - t.Skip("test requires new provider implementation") - - mp := testProvider() - mp.ApplyReturn = &terraform.InstanceState{ - ID: "foo", - } - - checkDestroy := false - - checkDestroyFn := func(*terraform.State) error { - checkDestroy = true - return nil - } - - mt := new(mockT) - Test(mt, TestCase{ - Providers: map[string]terraform.ResourceProvider{ - "test": mp, - }, - CheckDestroy: checkDestroyFn, - Steps: []TestStep{ - TestStep{ - Config: testConfigStr, - PlanOnly: true, - ExpectNonEmptyPlan: false, - }, - }, - }) - - if !mt.failed() { - t.Fatal("test should've failed") - } - - expected := `Step 0 error: After applying this step, the plan was not empty: - -DIFF: - -CREATE: test_instance.foo - foo: "" => "bar" - -STATE: - -` - - if mt.failMessage() != expected { - t.Fatalf("Expected message: %s\n\ngot:\n\n%s", expected, mt.failMessage()) - } - - if !checkDestroy { - t.Fatal("didn't call check for destroy") - } -} - -func TestTest_idRefresh(t *testing.T) { - t.Skip("test requires new provider implementation") - - // Refresh count should be 3: - // 1.) initial Ref/Plan/Apply - // 2.) post Ref/Plan/Apply for plan-check - // 3.) id refresh check - var expectedRefresh int32 = 3 - - 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 - } - - var refreshCount int32 - mp.RefreshFn = func(*terraform.InstanceInfo, *terraform.InstanceState) (*terraform.InstanceState, error) { - atomic.AddInt32(&refreshCount, 1) - return &terraform.InstanceState{ID: "foo"}, nil - } - - mt := new(mockT) - Test(mt, TestCase{ - IDRefreshName: "test_instance.foo", - Providers: map[string]terraform.ResourceProvider{ - "test": mp, - }, - Steps: []TestStep{ - TestStep{ - Config: testConfigStr, - }, - }, - }) - - if mt.failed() { - t.Fatalf("test failed: %s", mt.failMessage()) - } - - // See declaration of expectedRefresh for why that number - if refreshCount != expectedRefresh { - t.Fatalf("bad refresh count: %d", refreshCount) - } -} - -func TestTest_idRefreshCustomName(t *testing.T) { - t.Skip("test requires new provider implementation") - - // Refresh count should be 3: - // 1.) initial Ref/Plan/Apply - // 2.) post Ref/Plan/Apply for plan-check - // 3.) id refresh check - var expectedRefresh int32 = 3 - - 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 - } - - var refreshCount int32 - mp.RefreshFn = func(*terraform.InstanceInfo, *terraform.InstanceState) (*terraform.InstanceState, error) { - atomic.AddInt32(&refreshCount, 1) - return &terraform.InstanceState{ID: "foo"}, nil - } - - mt := new(mockT) - Test(mt, TestCase{ - IDRefreshName: "test_instance.foo", - Providers: map[string]terraform.ResourceProvider{ - "test": mp, - }, - Steps: []TestStep{ - TestStep{ - Config: testConfigStr, - }, - }, - }) - - if mt.failed() { - t.Fatalf("test failed: %s", mt.failMessage()) - } - - // See declaration of expectedRefresh for why that number - if refreshCount != expectedRefresh { - t.Fatalf("bad refresh count: %d", refreshCount) - } -} - -func TestTest_idRefreshFail(t *testing.T) { - t.Skip("test requires new provider implementation") - - // Refresh count should be 3: - // 1.) initial Ref/Plan/Apply - // 2.) post Ref/Plan/Apply for plan-check - // 3.) id refresh check - var expectedRefresh int32 = 3 - - 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 - } - - var refreshCount int32 - mp.RefreshFn = func(*terraform.InstanceInfo, *terraform.InstanceState) (*terraform.InstanceState, error) { - atomic.AddInt32(&refreshCount, 1) - if atomic.LoadInt32(&refreshCount) == expectedRefresh-1 { - return &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{"foo": "bar"}, - }, nil - } else if atomic.LoadInt32(&refreshCount) < expectedRefresh { - return &terraform.InstanceState{ID: "foo"}, nil - } else { - return nil, nil - } - } - - mt := new(mockT) - Test(mt, TestCase{ - IDRefreshName: "test_instance.foo", - Providers: map[string]terraform.ResourceProvider{ - "test": mp, - }, - Steps: []TestStep{ - TestStep{ - Config: testConfigStr, - }, - }, - }) - - if !mt.failed() { - t.Fatal("test didn't fail") - } - t.Logf("failure reason: %s", mt.failMessage()) - - // See declaration of expectedRefresh for why that number - if refreshCount != expectedRefresh { - t.Fatalf("bad refresh count: %d", refreshCount) - } -} - -func TestTest_empty(t *testing.T) { - t.Skip("test requires new provider implementation") - - destroyCalled := false - checkDestroyFn := func(*terraform.State) error { - destroyCalled = true - return nil - } - - mt := new(mockT) - Test(mt, TestCase{ - CheckDestroy: checkDestroyFn, - }) - - if mt.failed() { - t.Fatal("test failed") - } - if destroyCalled { - t.Fatal("should not call check destroy if there is no steps") - } -} - -func TestTest_noEnv(t *testing.T) { - t.Skip("test requires new provider implementation") - - // Unset the variable - if err := os.Setenv(TestEnvVar, ""); err != nil { - t.Fatalf("err: %s", err) - } - defer os.Setenv(TestEnvVar, "1") - - mt := new(mockT) - Test(mt, TestCase{}) - - if !mt.SkipCalled { - t.Fatal("skip not called") - } -} - -func TestTest_preCheck(t *testing.T) { - t.Skip("test requires new provider implementation") - - called := false - - mt := new(mockT) - Test(mt, TestCase{ - PreCheck: func() { called = true }, - }) - - if !called { - t.Fatal("precheck should be called") - } -} - -func TestTest_skipFunc(t *testing.T) { - t.Skip("test requires new provider implementation") - - preCheckCalled := false - skipped := false - - mp := testProvider() - mp.ApplyReturn = &terraform.InstanceState{ - ID: "foo", - } - - checkStepFn := func(*terraform.State) error { - return fmt.Errorf("error") - } - - mt := new(mockT) - Test(mt, TestCase{ - Providers: map[string]terraform.ResourceProvider{ - "test": mp, - }, - PreCheck: func() { preCheckCalled = true }, - Steps: []TestStep{ - { - Config: testConfigStr, - Check: checkStepFn, - SkipFunc: func() (bool, error) { skipped = true; return true, nil }, - }, - }, - }) - - if mt.failed() { - t.Fatal("Expected check to be skipped") - } - - if !preCheckCalled { - t.Fatal("precheck should be called") - } - if !skipped { - t.Fatal("SkipFunc should be called") - } -} - -func TestTest_stepError(t *testing.T) { - t.Skip("test requires new provider implementation") - - mp := testProvider() - mp.ApplyReturn = &terraform.InstanceState{ - ID: "foo", - } - - checkDestroy := false - - checkDestroyFn := func(*terraform.State) error { - checkDestroy = true - return nil - } - - checkStepFn := func(*terraform.State) error { - return fmt.Errorf("error") - } - - mt := new(mockT) - Test(mt, TestCase{ - Providers: map[string]terraform.ResourceProvider{ - "test": mp, - }, - CheckDestroy: checkDestroyFn, - Steps: []TestStep{ - TestStep{ - Config: testConfigStr, - Check: checkStepFn, - }, - }, - }) - - if !mt.failed() { - t.Fatal("test should've failed") - } - expected := "Step 0 error: Check failed: error" - if mt.failMessage() != expected { - t.Fatalf("Expected message: %s\n\ngot:\n\n%s", expected, mt.failMessage()) - } - - if !checkDestroy { - t.Fatal("didn't call check for destroy") - } -} - -func TestTest_factoryError(t *testing.T) { - resourceFactoryError := fmt.Errorf("resource factory error") - - factory := func() (terraform.ResourceProvider, error) { - return nil, resourceFactoryError - } - - mt := new(mockT) - Test(mt, TestCase{ - ProviderFactories: map[string]terraform.ResourceProviderFactory{ - "test": factory, - }, - Steps: []TestStep{ - TestStep{ - ExpectError: regexp.MustCompile("resource factory error"), - }, - }, - }) - - if !mt.failed() { - t.Fatal("test should've failed") - } -} - -func TestTest_resetError(t *testing.T) { - t.Skip("test requires new provider implementation") - - mp := &resetProvider{ - MockResourceProvider: testProvider(), - TestResetError: fmt.Errorf("provider reset error"), - } - - mt := new(mockT) - Test(mt, TestCase{ - Providers: map[string]terraform.ResourceProvider{ - "test": mp, - }, - Steps: []TestStep{ - TestStep{ - ExpectError: regexp.MustCompile("provider reset error"), - }, - }, - }) - - if !mt.failed() { - t.Fatal("test should've failed") - } -} - -func TestTest_expectError(t *testing.T) { - t.Skip("test requires new provider implementation") - - cases := []struct { - name string - planErr bool - applyErr bool - badErr bool - }{ - { - name: "successful apply", - planErr: false, - applyErr: false, - }, - { - name: "bad plan", - planErr: true, - applyErr: false, - }, - { - name: "bad apply", - planErr: false, - applyErr: true, - }, - { - name: "bad plan, bad err", - planErr: true, - applyErr: false, - badErr: true, - }, - { - name: "bad apply, bad err", - planErr: false, - applyErr: true, - badErr: true, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - mp := testProvider() - expectedText := "test provider error" - var errText string - if tc.badErr { - errText = "wrong provider error" - } else { - errText = expectedText - } - noErrText := "no error received, but expected a match to" - if tc.planErr { - mp.DiffReturnError = errors.New(errText) - } - if tc.applyErr { - mp.ApplyReturnError = errors.New(errText) - } - mt := new(mockT) - Test(mt, TestCase{ - Providers: map[string]terraform.ResourceProvider{ - "test": mp, - }, - Steps: []TestStep{ - TestStep{ - Config: testConfigStr, - ExpectError: regexp.MustCompile(expectedText), - Check: func(*terraform.State) error { return nil }, - ExpectNonEmptyPlan: true, - }, - }, - }, - ) - if mt.FatalCalled { - t.Fatalf("fatal: %+v", mt.FatalArgs) - } - switch { - case len(mt.ErrorArgs) < 1 && !tc.planErr && !tc.applyErr: - t.Fatalf("expected error, got none") - case !tc.planErr && !tc.applyErr: - for _, e := range mt.ErrorArgs { - if regexp.MustCompile(noErrText).MatchString(fmt.Sprintf("%v", e)) { - return - } - } - t.Fatalf("expected error to match %s, got %+v", noErrText, mt.ErrorArgs) - case tc.badErr: - for _, e := range mt.ErrorArgs { - if regexp.MustCompile(expectedText).MatchString(fmt.Sprintf("%v", e)) { - return - } - } - t.Fatalf("expected error to match %s, got %+v", expectedText, mt.ErrorArgs) - } - }) - } -} - -func TestComposeAggregateTestCheckFunc(t *testing.T) { - check1 := func(s *terraform.State) error { - return errors.New("Error 1") - } - - check2 := func(s *terraform.State) error { - return errors.New("Error 2") - } - - f := ComposeAggregateTestCheckFunc(check1, check2) - err := f(nil) - if err == nil { - t.Fatalf("Expected errors") - } - - multi := err.(*multierror.Error) - if !strings.Contains(multi.Errors[0].Error(), "Error 1") { - t.Fatalf("Expected Error 1, Got %s", multi.Errors[0]) - } - if !strings.Contains(multi.Errors[1].Error(), "Error 2") { - t.Fatalf("Expected Error 2, Got %s", multi.Errors[1]) - } -} - -func TestComposeTestCheckFunc(t *testing.T) { - cases := []struct { - F []TestCheckFunc - Result string - }{ - { - F: []TestCheckFunc{ - func(*terraform.State) error { return nil }, - }, - Result: "", - }, - - { - F: []TestCheckFunc{ - func(*terraform.State) error { - return fmt.Errorf("error") - }, - func(*terraform.State) error { return nil }, - }, - Result: "Check 1/2 error: error", - }, - - { - F: []TestCheckFunc{ - func(*terraform.State) error { return nil }, - func(*terraform.State) error { - return fmt.Errorf("error") - }, - }, - Result: "Check 2/2 error: error", - }, - - { - F: []TestCheckFunc{ - func(*terraform.State) error { return nil }, - func(*terraform.State) error { return nil }, - }, - Result: "", - }, - } - - for i, tc := range cases { - f := ComposeTestCheckFunc(tc.F...) - err := f(nil) - if err == nil { - err = fmt.Errorf("") - } - if tc.Result != err.Error() { - t.Fatalf("Case %d bad: %s", i, err) - } - } -} - -// mockT implements TestT for testing -type mockT struct { - ErrorCalled bool - ErrorArgs []interface{} - FatalCalled bool - FatalArgs []interface{} - ParallelCalled bool - SkipCalled bool - SkipArgs []interface{} - - f bool -} - -func (t *mockT) Error(args ...interface{}) { - t.ErrorCalled = true - t.ErrorArgs = args - t.f = true -} - -func (t *mockT) Fatal(args ...interface{}) { - t.FatalCalled = true - t.FatalArgs = args - t.f = true -} - -func (t *mockT) Parallel() { - t.ParallelCalled = true -} - -func (t *mockT) Skip(args ...interface{}) { - t.SkipCalled = true - t.SkipArgs = args - t.f = true -} - -func (t *mockT) Name() string { - return "MockedName" -} - -func (t *mockT) failed() bool { - return t.f -} - -func (t *mockT) failMessage() string { - if t.FatalCalled { - return t.FatalArgs[0].(string) - } else if t.ErrorCalled { - return t.ErrorArgs[0].(string) - } else if t.SkipCalled { - return t.SkipArgs[0].(string) - } - - return "unknown" -} - -func testProvider() *terraform.MockResourceProvider { - mp := new(terraform.MockResourceProvider) - mp.DiffReturn = &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - New: "bar", - }, - }, - } - mp.ResourcesReturn = []terraform.ResourceType{ - terraform.ResourceType{Name: "test_instance"}, - } - - return mp -} - -func TestTest_Main(t *testing.T) { - flag.Parse() - if *flagSweep == "" { - // Tests for the TestMain method used for Sweepers will panic without the -sweep - // flag specified. Mock the value for now - *flagSweep = "us-east-1" - } - - cases := []struct { - Name string - Sweepers map[string]*Sweeper - ExpectedRunList []string - SweepRun string - }{ - { - Name: "normal", - Sweepers: map[string]*Sweeper{ - "aws_dummy": &Sweeper{ - Name: "aws_dummy", - F: mockSweeperFunc, - }, - }, - ExpectedRunList: []string{"aws_dummy"}, - }, - { - Name: "with dep", - Sweepers: map[string]*Sweeper{ - "aws_dummy": &Sweeper{ - Name: "aws_dummy", - F: mockSweeperFunc, - }, - "aws_top": &Sweeper{ - Name: "aws_top", - Dependencies: []string{"aws_sub"}, - F: mockSweeperFunc, - }, - "aws_sub": &Sweeper{ - Name: "aws_sub", - F: mockSweeperFunc, - }, - }, - ExpectedRunList: []string{"aws_dummy", "aws_sub", "aws_top"}, - }, - { - Name: "with filter", - Sweepers: map[string]*Sweeper{ - "aws_dummy": &Sweeper{ - Name: "aws_dummy", - F: mockSweeperFunc, - }, - "aws_top": &Sweeper{ - Name: "aws_top", - Dependencies: []string{"aws_sub"}, - F: mockSweeperFunc, - }, - "aws_sub": &Sweeper{ - Name: "aws_sub", - F: mockSweeperFunc, - }, - }, - ExpectedRunList: []string{"aws_dummy"}, - SweepRun: "aws_dummy", - }, - { - Name: "with two filters", - Sweepers: map[string]*Sweeper{ - "aws_dummy": &Sweeper{ - Name: "aws_dummy", - F: mockSweeperFunc, - }, - "aws_top": &Sweeper{ - Name: "aws_top", - Dependencies: []string{"aws_sub"}, - F: mockSweeperFunc, - }, - "aws_sub": &Sweeper{ - Name: "aws_sub", - F: mockSweeperFunc, - }, - }, - ExpectedRunList: []string{"aws_dummy", "aws_sub"}, - SweepRun: "aws_dummy,aws_sub", - }, - { - Name: "with dep and filter", - Sweepers: map[string]*Sweeper{ - "aws_dummy": &Sweeper{ - Name: "aws_dummy", - F: mockSweeperFunc, - }, - "aws_top": &Sweeper{ - Name: "aws_top", - Dependencies: []string{"aws_sub"}, - F: mockSweeperFunc, - }, - "aws_sub": &Sweeper{ - Name: "aws_sub", - F: mockSweeperFunc, - }, - }, - ExpectedRunList: []string{"aws_top", "aws_sub"}, - SweepRun: "aws_top", - }, - { - Name: "filter and none", - Sweepers: map[string]*Sweeper{ - "aws_dummy": &Sweeper{ - Name: "aws_dummy", - F: mockSweeperFunc, - }, - "aws_top": &Sweeper{ - Name: "aws_top", - Dependencies: []string{"aws_sub"}, - F: mockSweeperFunc, - }, - "aws_sub": &Sweeper{ - Name: "aws_sub", - F: mockSweeperFunc, - }, - }, - SweepRun: "none", - }, - } - - for _, tc := range cases { - // reset sweepers - sweeperFuncs = map[string]*Sweeper{} - - t.Run(tc.Name, func(t *testing.T) { - for n, s := range tc.Sweepers { - AddTestSweepers(n, s) - } - *flagSweepRun = tc.SweepRun - - TestMain(&testing.M{}) - - // get list of tests ran from sweeperRunList keys - var keys []string - for k, _ := range sweeperRunList { - keys = append(keys, k) - } - - sort.Strings(keys) - sort.Strings(tc.ExpectedRunList) - if !reflect.DeepEqual(keys, tc.ExpectedRunList) { - t.Fatalf("Expected keys mismatch, expected:\n%#v\ngot:\n%#v\n", tc.ExpectedRunList, keys) - } - }) - } -} - -func mockSweeperFunc(s string) error { - return nil -} - -func TestTest_Taint(t *testing.T) { - t.Skip("test requires new provider implementation") - - mp := testProvider() - mp.DiffFn = func( - _ *terraform.InstanceInfo, - state *terraform.InstanceState, - _ *terraform.ResourceConfig, - ) (*terraform.InstanceDiff, error) { - return &terraform.InstanceDiff{ - DestroyTainted: state.Tainted, - }, nil - } - - mp.ApplyFn = func( - info *terraform.InstanceInfo, - state *terraform.InstanceState, - diff *terraform.InstanceDiff, - ) (*terraform.InstanceState, error) { - var id string - switch { - case diff.Destroy && !diff.DestroyTainted: - return nil, nil - case diff.DestroyTainted: - id = "tainted" - default: - id = "not_tainted" - } - - return &terraform.InstanceState{ - ID: id, - }, nil - } - - mp.RefreshFn = func( - _ *terraform.InstanceInfo, - state *terraform.InstanceState, - ) (*terraform.InstanceState, error) { - return state, nil - } - - mt := new(mockT) - Test(mt, TestCase{ - Providers: map[string]terraform.ResourceProvider{ - "test": mp, - }, - Steps: []TestStep{ - TestStep{ - Config: testConfigStr, - Check: func(s *terraform.State) error { - rs := s.RootModule().Resources["test_instance.foo"] - if rs.Primary.ID != "not_tainted" { - return fmt.Errorf("expected not_tainted, got %s", rs.Primary.ID) - } - return nil - }, - }, - TestStep{ - Taint: []string{"test_instance.foo"}, - Config: testConfigStr, - Check: func(s *terraform.State) error { - rs := s.RootModule().Resources["test_instance.foo"] - if rs.Primary.ID != "tainted" { - return fmt.Errorf("expected tainted, got %s", rs.Primary.ID) - } - return nil - }, - }, - TestStep{ - Taint: []string{"test_instance.fooo"}, - Config: testConfigStr, - ExpectError: regexp.MustCompile("resource \"test_instance.fooo\" not found in state"), - }, - }, - }) - - if mt.failed() { - t.Fatalf("test failure: %s", mt.failMessage()) - } -} - -const testConfigStr = ` -resource "test_instance" "foo" {} -` - -const testConfigStrProvider = ` -provider "test" {} -` - -func TestCheckResourceAttr_empty(t *testing.T) { - s := terraform.NewState() - s.AddModuleState(&terraform.ModuleState{ - Path: []string{"root"}, - Resources: map[string]*terraform.ResourceState{ - "test_resource": &terraform.ResourceState{ - Primary: &terraform.InstanceState{ - Attributes: map[string]string{ - "empty_list.#": "0", - "empty_map.%": "0", - }, - }, - }, - }, - }) - - for _, key := range []string{ - "empty_list.#", - "empty_map.%", - "missing_list.#", - "missing_map.%", - } { - t.Run(key, func(t *testing.T) { - check := TestCheckResourceAttr("test_resource", key, "0") - if err := check(s); err != nil { - t.Fatal(err) - } - }) - } -} - -func TestCheckNoResourceAttr_empty(t *testing.T) { - s := terraform.NewState() - s.AddModuleState(&terraform.ModuleState{ - Path: []string{"root"}, - Resources: map[string]*terraform.ResourceState{ - "test_resource": &terraform.ResourceState{ - Primary: &terraform.InstanceState{ - Attributes: map[string]string{ - "empty_list.#": "0", - "empty_map.%": "0", - }, - }, - }, - }, - }) - - for _, key := range []string{ - "empty_list.#", - "empty_map.%", - "missing_list.#", - "missing_map.%", - } { - t.Run(key, func(t *testing.T) { - check := TestCheckNoResourceAttr("test_resource", key) - if err := check(s); err != nil { - t.Fatal(err) - } - }) - } -} - -func TestTestCheckResourceAttrPair(t *testing.T) { - tests := map[string]struct { - state *terraform.State - wantErr string - }{ - "exist match": { - &terraform.State{ - Modules: []*terraform.ModuleState{ - { - Path: []string{"root"}, - Resources: map[string]*terraform.ResourceState{ - "test.a": { - Primary: &terraform.InstanceState{ - Attributes: map[string]string{ - "a": "boop", - }, - }, - }, - "test.b": { - Primary: &terraform.InstanceState{ - Attributes: map[string]string{ - "b": "boop", - }, - }, - }, - }, - }, - }, - }, - ``, - }, - "nonexist match": { - &terraform.State{ - Modules: []*terraform.ModuleState{ - { - Path: []string{"root"}, - Resources: map[string]*terraform.ResourceState{ - "test.a": { - Primary: &terraform.InstanceState{ - Attributes: map[string]string{}, - }, - }, - "test.b": { - Primary: &terraform.InstanceState{ - Attributes: map[string]string{}, - }, - }, - }, - }, - }, - }, - ``, - }, - "exist nonmatch": { - &terraform.State{ - Modules: []*terraform.ModuleState{ - { - Path: []string{"root"}, - Resources: map[string]*terraform.ResourceState{ - "test.a": { - Primary: &terraform.InstanceState{ - Attributes: map[string]string{ - "a": "beep", - }, - }, - }, - "test.b": { - Primary: &terraform.InstanceState{ - Attributes: map[string]string{ - "b": "boop", - }, - }, - }, - }, - }, - }, - }, - `test.a: Attribute 'a' expected "boop", got "beep"`, - }, - "inconsistent exist a": { - &terraform.State{ - Modules: []*terraform.ModuleState{ - { - Path: []string{"root"}, - Resources: map[string]*terraform.ResourceState{ - "test.a": { - Primary: &terraform.InstanceState{ - Attributes: map[string]string{ - "a": "beep", - }, - }, - }, - "test.b": { - Primary: &terraform.InstanceState{ - Attributes: map[string]string{}, - }, - }, - }, - }, - }, - }, - `test.a: Attribute "a" is "beep", but "b" is not set in test.b`, - }, - "inconsistent exist b": { - &terraform.State{ - Modules: []*terraform.ModuleState{ - { - Path: []string{"root"}, - Resources: map[string]*terraform.ResourceState{ - "test.a": { - Primary: &terraform.InstanceState{ - Attributes: map[string]string{}, - }, - }, - "test.b": { - Primary: &terraform.InstanceState{ - Attributes: map[string]string{ - "b": "boop", - }, - }, - }, - }, - }, - }, - }, - `test.a: Attribute "a" not set, but "b" is set in test.b as "boop"`, - }, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - fn := TestCheckResourceAttrPair("test.a", "a", "test.b", "b") - err := fn(test.state) - - if test.wantErr != "" { - if err == nil { - t.Fatalf("succeeded; want error\nwant: %s", test.wantErr) - } - if got, want := err.Error(), test.wantErr; got != want { - t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) - } - return - } - - if err != nil { - t.Fatalf("failed; want success\ngot: %s", err.Error()) - } - }) - } -} - -func TestTestCheckResourceAttrPairCount(t *testing.T) { - tests := map[string]struct { - state *terraform.State - attr string - wantErr string - }{ - "unset and 0 equal list": { - &terraform.State{ - Modules: []*terraform.ModuleState{ - { - Path: []string{"root"}, - Resources: map[string]*terraform.ResourceState{ - "test.a": { - Primary: &terraform.InstanceState{ - Attributes: map[string]string{ - "a.#": "0", - }, - }, - }, - "test.b": { - Primary: &terraform.InstanceState{ - Attributes: map[string]string{}, - }, - }, - }, - }, - }, - }, - "a.#", - ``, - }, - "unset and 0 equal map": { - &terraform.State{ - Modules: []*terraform.ModuleState{ - { - Path: []string{"root"}, - Resources: map[string]*terraform.ResourceState{ - "test.a": { - Primary: &terraform.InstanceState{ - Attributes: map[string]string{ - "a.%": "0", - }, - }, - }, - "test.b": { - Primary: &terraform.InstanceState{ - Attributes: map[string]string{}, - }, - }, - }, - }, - }, - }, - "a.%", - ``, - }, - "count equal": { - &terraform.State{ - Modules: []*terraform.ModuleState{ - { - Path: []string{"root"}, - Resources: map[string]*terraform.ResourceState{ - "test.a": { - Primary: &terraform.InstanceState{ - Attributes: map[string]string{ - "a.%": "1", - }, - }, - }, - "test.b": { - Primary: &terraform.InstanceState{ - Attributes: map[string]string{ - "a.%": "1", - }}, - }, - }, - }, - }, - }, - "a.%", - ``, - }, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - fn := TestCheckResourceAttrPair("test.a", test.attr, "test.b", test.attr) - err := fn(test.state) - - if test.wantErr != "" { - if err == nil { - t.Fatalf("succeeded; want error\nwant: %s", test.wantErr) - } - if got, want := err.Error(), test.wantErr; got != want { - t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) - } - return - } - - if err != nil { - t.Fatalf("failed; want success\ngot: %s", err.Error()) - } - }) - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/wait.go b/vendor/github.com/hashicorp/terraform/helper/resource/wait.go deleted file mode 100644 index e56a5155..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/resource/wait.go +++ /dev/null @@ -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} -} diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/wait_test.go b/vendor/github.com/hashicorp/terraform/helper/resource/wait_test.go deleted file mode 100644 index 526b21ae..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/resource/wait_test.go +++ /dev/null @@ -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") - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/README.md b/vendor/github.com/hashicorp/terraform/helper/schema/README.md deleted file mode 100644 index 28c83628..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Terraform Helper Lib: schema - -The `schema` package provides a high-level interface for writing resource -providers for Terraform. - -If you're writing a resource provider, we recommend you use this package. - -The interface exposed by this package is much friendlier than trying to -write to the Terraform API directly. The core Terraform API is low-level -and built for maximum flexibility and control, whereas this library is built -as a framework around that to more easily write common providers. diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/backend.go b/vendor/github.com/hashicorp/terraform/helper/schema/backend.go deleted file mode 100644 index 42c2bed9..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/backend.go +++ /dev/null @@ -1,200 +0,0 @@ -package schema - -import ( - "context" - "fmt" - - "github.com/hashicorp/terraform/tfdiags" - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/configs/hcl2shim" - "github.com/hashicorp/terraform/terraform" - ctyconvert "github.com/zclconf/go-cty/cty/convert" -) - -// Backend represents a partial backend.Backend implementation and simplifies -// the creation of configuration loading and validation. -// -// Unlike other schema structs such as Provider, this struct is meant to be -// embedded within your actual implementation. It provides implementations -// only for Input and Configure and gives you a method for accessing the -// configuration in the form of a ResourceData that you're expected to call -// from the other implementation funcs. -type Backend struct { - // Schema is the schema for the configuration of this backend. If this - // Backend has no configuration this can be omitted. - Schema map[string]*Schema - - // ConfigureFunc is called to configure the backend. Use the - // FromContext* methods to extract information from the context. - // This can be nil, in which case nothing will be called but the - // config will still be stored. - ConfigureFunc func(context.Context) error - - config *ResourceData -} - -var ( - backendConfigKey = contextKey("backend config") -) - -// FromContextBackendConfig extracts a ResourceData with the configuration -// from the context. This should only be called by Backend functions. -func FromContextBackendConfig(ctx context.Context) *ResourceData { - return ctx.Value(backendConfigKey).(*ResourceData) -} - -func (b *Backend) ConfigSchema() *configschema.Block { - // This is an alias of CoreConfigSchema just to implement the - // backend.Backend interface. - return b.CoreConfigSchema() -} - -func (b *Backend) PrepareConfig(configVal cty.Value) (cty.Value, tfdiags.Diagnostics) { - if b == nil { - return configVal, nil - } - var diags tfdiags.Diagnostics - var err error - - // In order to use Transform below, this needs to be filled out completely - // according the schema. - configVal, err = b.CoreConfigSchema().CoerceValue(configVal) - if err != nil { - return configVal, diags.Append(err) - } - - // lookup any required, top-level attributes that are Null, and see if we - // have a Default value available. - configVal, err = cty.Transform(configVal, func(path cty.Path, val cty.Value) (cty.Value, error) { - // we're only looking for top-level attributes - if len(path) != 1 { - return val, nil - } - - // nothing to do if we already have a value - if !val.IsNull() { - return val, nil - } - - // get the Schema definition for this attribute - getAttr, ok := path[0].(cty.GetAttrStep) - // these should all exist, but just ignore anything strange - if !ok { - return val, nil - } - - attrSchema := b.Schema[getAttr.Name] - // continue to ignore anything that doesn't match - if attrSchema == nil { - return val, nil - } - - // this is deprecated, so don't set it - if attrSchema.Deprecated != "" || attrSchema.Removed != "" { - return val, nil - } - - // find a default value if it exists - def, err := attrSchema.DefaultValue() - if err != nil { - diags = diags.Append(fmt.Errorf("error getting default for %q: %s", getAttr.Name, err)) - return val, err - } - - // no default - if def == nil { - return val, nil - } - - // create a cty.Value and make sure it's the correct type - tmpVal := hcl2shim.HCL2ValueFromConfigValue(def) - - // helper/schema used to allow setting "" to a bool - if val.Type() == cty.Bool && tmpVal.RawEquals(cty.StringVal("")) { - // return a warning about the conversion - diags = diags.Append("provider set empty string as default value for bool " + getAttr.Name) - tmpVal = cty.False - } - - val, err = ctyconvert.Convert(tmpVal, val.Type()) - if err != nil { - diags = diags.Append(fmt.Errorf("error setting default for %q: %s", getAttr.Name, err)) - } - - return val, err - }) - if err != nil { - // any error here was already added to the diagnostics - return configVal, diags - } - - shimRC := b.shimConfig(configVal) - warns, errs := schemaMap(b.Schema).Validate(shimRC) - for _, warn := range warns { - diags = diags.Append(tfdiags.SimpleWarning(warn)) - } - for _, err := range errs { - diags = diags.Append(err) - } - return configVal, diags -} - -func (b *Backend) Configure(obj cty.Value) tfdiags.Diagnostics { - if b == nil { - return nil - } - - var diags tfdiags.Diagnostics - sm := schemaMap(b.Schema) - shimRC := b.shimConfig(obj) - - // Get a ResourceData for this configuration. To do this, we actually - // generate an intermediary "diff" although that is never exposed. - diff, err := sm.Diff(nil, shimRC, nil, nil, true) - if err != nil { - diags = diags.Append(err) - return diags - } - - data, err := sm.Data(nil, diff) - if err != nil { - diags = diags.Append(err) - return diags - } - b.config = data - - if b.ConfigureFunc != nil { - err = b.ConfigureFunc(context.WithValue( - context.Background(), backendConfigKey, data)) - if err != nil { - diags = diags.Append(err) - return diags - } - } - - return diags -} - -// shimConfig turns a new-style cty.Value configuration (which must be of -// an object type) into a minimal old-style *terraform.ResourceConfig object -// that should be populated enough to appease the not-yet-updated functionality -// in this package. This should be removed once everything is updated. -func (b *Backend) shimConfig(obj cty.Value) *terraform.ResourceConfig { - shimMap, ok := hcl2shim.ConfigValueFromHCL2(obj).(map[string]interface{}) - if !ok { - // If the configVal was nil, we still want a non-nil map here. - shimMap = map[string]interface{}{} - } - return &terraform.ResourceConfig{ - Config: shimMap, - Raw: shimMap, - } -} - -// Config returns the configuration. This is available after Configure is -// called. -func (b *Backend) Config() *ResourceData { - return b.config -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config_test.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config_test.go deleted file mode 100644 index 5e272825..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config_test.go +++ /dev/null @@ -1,540 +0,0 @@ -package schema - -import ( - "bytes" - "fmt" - "reflect" - "testing" - - "github.com/hashicorp/terraform/configs/hcl2shim" - "github.com/hashicorp/terraform/helper/hashcode" - "github.com/hashicorp/terraform/terraform" -) - -func TestConfigFieldReader_impl(t *testing.T) { - var _ FieldReader = new(ConfigFieldReader) -} - -func TestConfigFieldReader(t *testing.T) { - testFieldReader(t, func(s map[string]*Schema) FieldReader { - return &ConfigFieldReader{ - Schema: s, - - Config: testConfig(t, map[string]interface{}{ - "bool": true, - "float": 3.1415, - "int": 42, - "string": "string", - - "list": []interface{}{"foo", "bar"}, - - "listInt": []interface{}{21, 42}, - - "map": map[string]interface{}{ - "foo": "bar", - "bar": "baz", - }, - "mapInt": map[string]interface{}{ - "one": "1", - "two": "2", - }, - "mapIntNestedSchema": map[string]interface{}{ - "one": "1", - "two": "2", - }, - "mapFloat": map[string]interface{}{ - "oneDotTwo": "1.2", - }, - "mapBool": map[string]interface{}{ - "True": "true", - "False": "false", - }, - - "set": []interface{}{10, 50}, - "setDeep": []interface{}{ - map[string]interface{}{ - "index": 10, - "value": "foo", - }, - map[string]interface{}{ - "index": 50, - "value": "bar", - }, - }, - }), - } - }) -} - -// This contains custom table tests for our ConfigFieldReader -func TestConfigFieldReader_custom(t *testing.T) { - schema := map[string]*Schema{ - "bool": &Schema{ - Type: TypeBool, - }, - } - - cases := map[string]struct { - Addr []string - Result FieldReadResult - Config *terraform.ResourceConfig - Err bool - }{ - "basic": { - []string{"bool"}, - FieldReadResult{ - Value: true, - Exists: true, - }, - testConfig(t, map[string]interface{}{ - "bool": true, - }), - false, - }, - - "computed": { - []string{"bool"}, - FieldReadResult{ - Exists: true, - Computed: true, - }, - testConfig(t, map[string]interface{}{ - "bool": hcl2shim.UnknownVariableValue, - }), - false, - }, - } - - for name, tc := range cases { - t.Run(name, func(t *testing.T) { - r := &ConfigFieldReader{ - Schema: schema, - Config: tc.Config, - } - out, err := r.ReadField(tc.Addr) - if err != nil != tc.Err { - t.Fatalf("%s: err: %s", name, err) - } - if s, ok := out.Value.(*Set); ok { - // If it is a set, convert to a list so its more easily checked. - out.Value = s.List() - } - if !reflect.DeepEqual(tc.Result, out) { - t.Fatalf("%s: bad: %#v", name, out) - } - }) - } -} - -func TestConfigFieldReader_DefaultHandling(t *testing.T) { - schema := map[string]*Schema{ - "strWithDefault": &Schema{ - Type: TypeString, - Default: "ImADefault", - }, - "strWithDefaultFunc": &Schema{ - Type: TypeString, - DefaultFunc: func() (interface{}, error) { - return "FuncDefault", nil - }, - }, - } - - cases := map[string]struct { - Addr []string - Result FieldReadResult - Config *terraform.ResourceConfig - Err bool - }{ - "gets default value when no config set": { - []string{"strWithDefault"}, - FieldReadResult{ - Value: "ImADefault", - Exists: true, - Computed: false, - }, - testConfig(t, map[string]interface{}{}), - false, - }, - "config overrides default value": { - []string{"strWithDefault"}, - FieldReadResult{ - Value: "fromConfig", - Exists: true, - Computed: false, - }, - testConfig(t, map[string]interface{}{ - "strWithDefault": "fromConfig", - }), - false, - }, - "gets default from function when no config set": { - []string{"strWithDefaultFunc"}, - FieldReadResult{ - Value: "FuncDefault", - Exists: true, - Computed: false, - }, - testConfig(t, map[string]interface{}{}), - false, - }, - "config overrides default function": { - []string{"strWithDefaultFunc"}, - FieldReadResult{ - Value: "fromConfig", - Exists: true, - Computed: false, - }, - testConfig(t, map[string]interface{}{ - "strWithDefaultFunc": "fromConfig", - }), - false, - }, - } - - for name, tc := range cases { - r := &ConfigFieldReader{ - Schema: schema, - Config: tc.Config, - } - out, err := r.ReadField(tc.Addr) - if err != nil != tc.Err { - t.Fatalf("%s: err: %s", name, err) - } - if s, ok := out.Value.(*Set); ok { - // If it is a set, convert to a list so its more easily checked. - out.Value = s.List() - } - if !reflect.DeepEqual(tc.Result, out) { - t.Fatalf("%s: bad: %#v", name, out) - } - } -} - -func TestConfigFieldReader_ComputedMap(t *testing.T) { - schema := map[string]*Schema{ - "map": &Schema{ - Type: TypeMap, - Computed: true, - }, - "listmap": &Schema{ - Type: TypeMap, - Computed: true, - Elem: TypeList, - }, - "maplist": &Schema{ - Type: TypeList, - Computed: true, - Elem: TypeMap, - }, - } - - cases := []struct { - Name string - Addr []string - Result FieldReadResult - Config *terraform.ResourceConfig - Err bool - }{ - { - "set, normal", - []string{"map"}, - FieldReadResult{ - Value: map[string]interface{}{ - "foo": "bar", - }, - Exists: true, - Computed: false, - }, - testConfig(t, map[string]interface{}{ - "map": map[string]interface{}{ - "foo": "bar", - }, - }), - false, - }, - - { - "computed element", - []string{"map"}, - FieldReadResult{ - Exists: true, - Computed: true, - }, - testConfig(t, map[string]interface{}{ - "map": map[string]interface{}{ - "foo": hcl2shim.UnknownVariableValue, - }, - }), - false, - }, - - { - "native map", - []string{"map"}, - FieldReadResult{ - Value: map[string]interface{}{ - "bar": "baz", - "baz": "bar", - }, - Exists: true, - Computed: false, - }, - testConfig(t, map[string]interface{}{ - "map": map[string]interface{}{ - "bar": "baz", - "baz": "bar", - }, - }), - false, - }, - - { - "map-from-list-of-maps", - []string{"maplist", "0"}, - FieldReadResult{ - Value: map[string]interface{}{ - "key": "bar", - }, - Exists: true, - Computed: false, - }, - testConfig(t, map[string]interface{}{ - "maplist": []interface{}{ - map[string]interface{}{ - "key": "bar", - }, - }, - }), - false, - }, - - { - "value-from-list-of-maps", - []string{"maplist", "0", "key"}, - FieldReadResult{ - Value: "bar", - Exists: true, - Computed: false, - }, - testConfig(t, map[string]interface{}{ - "maplist": []interface{}{ - map[string]interface{}{ - "key": "bar", - }, - }, - }), - false, - }, - - { - "list-from-map-of-lists", - []string{"listmap", "key"}, - FieldReadResult{ - Value: []interface{}{"bar"}, - Exists: true, - Computed: false, - }, - testConfig(t, map[string]interface{}{ - "listmap": map[string]interface{}{ - "key": []interface{}{ - "bar", - }, - }, - }), - false, - }, - - { - "value-from-map-of-lists", - []string{"listmap", "key", "0"}, - FieldReadResult{ - Value: "bar", - Exists: true, - Computed: false, - }, - testConfig(t, map[string]interface{}{ - "listmap": map[string]interface{}{ - "key": []interface{}{ - "bar", - }, - }, - }), - false, - }, - } - - for i, tc := range cases { - t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { - r := &ConfigFieldReader{ - Schema: schema, - Config: tc.Config, - } - out, err := r.ReadField(tc.Addr) - if err != nil != tc.Err { - t.Fatal(err) - } - if s, ok := out.Value.(*Set); ok { - // If it is a set, convert to the raw map - out.Value = s.m - if len(s.m) == 0 { - out.Value = nil - } - } - if !reflect.DeepEqual(tc.Result, out) { - t.Fatalf("\nexpected: %#v\ngot: %#v", tc.Result, out) - } - }) - } -} - -func TestConfigFieldReader_ComputedSet(t *testing.T) { - schema := map[string]*Schema{ - "strSet": &Schema{ - Type: TypeSet, - Elem: &Schema{Type: TypeString}, - Set: HashString, - }, - } - - cases := map[string]struct { - Addr []string - Result FieldReadResult - Config *terraform.ResourceConfig - Err bool - }{ - "set, normal": { - []string{"strSet"}, - FieldReadResult{ - Value: map[string]interface{}{ - "2356372769": "foo", - }, - Exists: true, - Computed: false, - }, - testConfig(t, map[string]interface{}{ - "strSet": []interface{}{"foo"}, - }), - false, - }, - - "set, computed element": { - []string{"strSet"}, - FieldReadResult{ - Value: nil, - Exists: true, - Computed: true, - }, - testConfig(t, map[string]interface{}{ - "strSet": []interface{}{hcl2shim.UnknownVariableValue}, - }), - false, - }, - } - - for name, tc := range cases { - r := &ConfigFieldReader{ - Schema: schema, - Config: tc.Config, - } - out, err := r.ReadField(tc.Addr) - if err != nil != tc.Err { - t.Fatalf("%s: err: %s", name, err) - } - if s, ok := out.Value.(*Set); ok { - // If it is a set, convert to the raw map - out.Value = s.m - if len(s.m) == 0 { - out.Value = nil - } - } - if !reflect.DeepEqual(tc.Result, out) { - t.Fatalf("%s: bad: %#v", name, out) - } - } -} - -func TestConfigFieldReader_computedComplexSet(t *testing.T) { - hashfunc := func(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["vhd_uri"].(string))) - return hashcode.String(buf.String()) - } - - schema := map[string]*Schema{ - "set": &Schema{ - Type: TypeSet, - Elem: &Resource{ - Schema: map[string]*Schema{ - "name": { - Type: TypeString, - Required: true, - }, - - "vhd_uri": { - Type: TypeString, - Required: true, - }, - }, - }, - Set: hashfunc, - }, - } - - cases := map[string]struct { - Addr []string - Result FieldReadResult - Config *terraform.ResourceConfig - Err bool - }{ - "set, normal": { - []string{"set"}, - FieldReadResult{ - Value: map[string]interface{}{ - "532860136": map[string]interface{}{ - "name": "myosdisk1", - "vhd_uri": "bar", - }, - }, - Exists: true, - Computed: false, - }, - testConfig(t, map[string]interface{}{ - "set": []interface{}{ - map[string]interface{}{ - "name": "myosdisk1", - "vhd_uri": "bar", - }, - }, - }), - false, - }, - } - - for name, tc := range cases { - r := &ConfigFieldReader{ - Schema: schema, - Config: tc.Config, - } - out, err := r.ReadField(tc.Addr) - if err != nil != tc.Err { - t.Fatalf("%s: err: %s", name, err) - } - if s, ok := out.Value.(*Set); ok { - // If it is a set, convert to the raw map - out.Value = s.m - if len(s.m) == 0 { - out.Value = nil - } - } - if !reflect.DeepEqual(tc.Result, out) { - t.Fatalf("%s: bad: %#v", name, out) - } - } -} - -func testConfig(t *testing.T, raw map[string]interface{}) *terraform.ResourceConfig { - return terraform.NewResourceConfigRaw(raw) -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff_test.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff_test.go deleted file mode 100644 index 49b05e86..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff_test.go +++ /dev/null @@ -1,524 +0,0 @@ -package schema - -import ( - "reflect" - "testing" - - "github.com/hashicorp/terraform/terraform" -) - -func TestDiffFieldReader_impl(t *testing.T) { - var _ FieldReader = new(DiffFieldReader) -} - -func TestDiffFieldReader_NestedSetUpdate(t *testing.T) { - hashFn := func(a interface{}) int { - m := a.(map[string]interface{}) - return m["val"].(int) - } - - schema := map[string]*Schema{ - "list_of_sets_1": &Schema{ - Type: TypeList, - Elem: &Resource{ - Schema: map[string]*Schema{ - "nested_set": &Schema{ - Type: TypeSet, - Elem: &Resource{ - Schema: map[string]*Schema{ - "val": &Schema{ - Type: TypeInt, - }, - }, - }, - Set: hashFn, - }, - }, - }, - }, - "list_of_sets_2": &Schema{ - Type: TypeList, - Elem: &Resource{ - Schema: map[string]*Schema{ - "nested_set": &Schema{ - Type: TypeSet, - Elem: &Resource{ - Schema: map[string]*Schema{ - "val": &Schema{ - Type: TypeInt, - }, - }, - }, - Set: hashFn, - }, - }, - }, - }, - } - - r := &DiffFieldReader{ - Schema: schema, - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "list_of_sets_1.0.nested_set.1.val": &terraform.ResourceAttrDiff{ - Old: "1", - New: "0", - NewRemoved: true, - }, - "list_of_sets_1.0.nested_set.2.val": &terraform.ResourceAttrDiff{ - New: "2", - }, - }, - }, - } - - r.Source = &MultiLevelFieldReader{ - Readers: map[string]FieldReader{ - "diff": r, - "set": &MapFieldReader{Schema: schema}, - "state": &MapFieldReader{ - Map: &BasicMapReader{ - "list_of_sets_1.#": "1", - "list_of_sets_1.0.nested_set.#": "1", - "list_of_sets_1.0.nested_set.1.val": "1", - "list_of_sets_2.#": "1", - "list_of_sets_2.0.nested_set.#": "1", - "list_of_sets_2.0.nested_set.1.val": "1", - }, - Schema: schema, - }, - }, - Levels: []string{"state", "config"}, - } - - out, err := r.ReadField([]string{"list_of_sets_2"}) - if err != nil { - t.Fatalf("err: %v", err) - } - - s := &Set{F: hashFn} - s.Add(map[string]interface{}{"val": 1}) - expected := s.List() - - l := out.Value.([]interface{}) - i := l[0].(map[string]interface{}) - actual := i["nested_set"].(*Set).List() - - if !reflect.DeepEqual(expected, actual) { - t.Fatalf("bad: NestedSetUpdate\n\nexpected: %#v\n\ngot: %#v\n\n", expected, actual) - } -} - -// https://github.com/hashicorp/terraform/issues/914 -func TestDiffFieldReader_MapHandling(t *testing.T) { - schema := map[string]*Schema{ - "tags": &Schema{ - Type: TypeMap, - }, - } - r := &DiffFieldReader{ - Schema: schema, - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "tags.%": &terraform.ResourceAttrDiff{ - Old: "1", - New: "2", - }, - "tags.baz": &terraform.ResourceAttrDiff{ - Old: "", - New: "qux", - }, - }, - }, - Source: &MapFieldReader{ - Schema: schema, - Map: BasicMapReader(map[string]string{ - "tags.%": "1", - "tags.foo": "bar", - }), - }, - } - - result, err := r.ReadField([]string{"tags"}) - if err != nil { - t.Fatalf("ReadField failed: %#v", err) - } - - expected := map[string]interface{}{ - "foo": "bar", - "baz": "qux", - } - - if !reflect.DeepEqual(expected, result.Value) { - t.Fatalf("bad: DiffHandling\n\nexpected: %#v\n\ngot: %#v\n\n", expected, result.Value) - } -} - -func TestDiffFieldReader_extra(t *testing.T) { - schema := map[string]*Schema{ - "stringComputed": &Schema{Type: TypeString}, - - "listMap": &Schema{ - Type: TypeList, - Elem: &Schema{ - Type: TypeMap, - }, - }, - - "mapRemove": &Schema{Type: TypeMap}, - - "setChange": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "index": &Schema{ - Type: TypeInt, - Required: true, - }, - - "value": &Schema{ - Type: TypeString, - Required: true, - }, - }, - }, - Set: func(a interface{}) int { - m := a.(map[string]interface{}) - return m["index"].(int) - }, - }, - - "setEmpty": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "index": &Schema{ - Type: TypeInt, - Required: true, - }, - - "value": &Schema{ - Type: TypeString, - Required: true, - }, - }, - }, - Set: func(a interface{}) int { - m := a.(map[string]interface{}) - return m["index"].(int) - }, - }, - } - - r := &DiffFieldReader{ - Schema: schema, - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "stringComputed": &terraform.ResourceAttrDiff{ - Old: "foo", - New: "bar", - NewComputed: true, - }, - - "listMap.0.bar": &terraform.ResourceAttrDiff{ - NewRemoved: true, - }, - - "mapRemove.bar": &terraform.ResourceAttrDiff{ - NewRemoved: true, - }, - - "setChange.10.value": &terraform.ResourceAttrDiff{ - Old: "50", - New: "80", - }, - - "setEmpty.#": &terraform.ResourceAttrDiff{ - Old: "2", - New: "0", - }, - }, - }, - - Source: &MapFieldReader{ - Schema: schema, - Map: BasicMapReader(map[string]string{ - "listMap.#": "2", - "listMap.0.foo": "bar", - "listMap.0.bar": "baz", - "listMap.1.baz": "baz", - - "mapRemove.foo": "bar", - "mapRemove.bar": "bar", - - "setChange.#": "1", - "setChange.10.index": "10", - "setChange.10.value": "50", - - "setEmpty.#": "2", - "setEmpty.10.index": "10", - "setEmpty.10.value": "50", - "setEmpty.20.index": "20", - "setEmpty.20.value": "50", - }), - }, - } - - cases := map[string]struct { - Addr []string - Result FieldReadResult - Err bool - }{ - "stringComputed": { - []string{"stringComputed"}, - FieldReadResult{ - Value: "", - Exists: true, - Computed: true, - }, - false, - }, - - "listMapRemoval": { - []string{"listMap"}, - FieldReadResult{ - Value: []interface{}{ - map[string]interface{}{ - "foo": "bar", - }, - map[string]interface{}{ - "baz": "baz", - }, - }, - Exists: true, - }, - false, - }, - - "mapRemove": { - []string{"mapRemove"}, - FieldReadResult{ - Value: map[string]interface{}{ - "foo": "bar", - }, - Exists: true, - Computed: false, - }, - false, - }, - - "setChange": { - []string{"setChange"}, - FieldReadResult{ - Value: []interface{}{ - map[string]interface{}{ - "index": 10, - "value": "80", - }, - }, - Exists: true, - }, - false, - }, - - "setEmpty": { - []string{"setEmpty"}, - FieldReadResult{ - Value: []interface{}{}, - Exists: true, - }, - false, - }, - } - - for name, tc := range cases { - out, err := r.ReadField(tc.Addr) - if err != nil != tc.Err { - t.Fatalf("%s: err: %s", name, err) - } - if s, ok := out.Value.(*Set); ok { - // If it is a set, convert to a list so its more easily checked. - out.Value = s.List() - } - if !reflect.DeepEqual(tc.Result, out) { - t.Fatalf("%s: bad: %#v", name, out) - } - } -} - -func TestDiffFieldReader(t *testing.T) { - testFieldReader(t, func(s map[string]*Schema) FieldReader { - return &DiffFieldReader{ - Schema: s, - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "bool": &terraform.ResourceAttrDiff{ - Old: "", - New: "true", - }, - - "int": &terraform.ResourceAttrDiff{ - Old: "", - New: "42", - }, - - "float": &terraform.ResourceAttrDiff{ - Old: "", - New: "3.1415", - }, - - "string": &terraform.ResourceAttrDiff{ - Old: "", - New: "string", - }, - - "stringComputed": &terraform.ResourceAttrDiff{ - Old: "foo", - New: "bar", - NewComputed: true, - }, - - "list.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "2", - }, - - "list.0": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - }, - - "list.1": &terraform.ResourceAttrDiff{ - Old: "", - New: "bar", - }, - - "listInt.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "2", - }, - - "listInt.0": &terraform.ResourceAttrDiff{ - Old: "", - New: "21", - }, - - "listInt.1": &terraform.ResourceAttrDiff{ - Old: "", - New: "42", - }, - - "map.foo": &terraform.ResourceAttrDiff{ - Old: "", - New: "bar", - }, - - "map.bar": &terraform.ResourceAttrDiff{ - Old: "", - New: "baz", - }, - - "mapInt.%": &terraform.ResourceAttrDiff{ - Old: "", - New: "2", - }, - "mapInt.one": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "mapInt.two": &terraform.ResourceAttrDiff{ - Old: "", - New: "2", - }, - - "mapIntNestedSchema.%": &terraform.ResourceAttrDiff{ - Old: "", - New: "2", - }, - "mapIntNestedSchema.one": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "mapIntNestedSchema.two": &terraform.ResourceAttrDiff{ - Old: "", - New: "2", - }, - - "mapFloat.%": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "mapFloat.oneDotTwo": &terraform.ResourceAttrDiff{ - Old: "", - New: "1.2", - }, - - "mapBool.%": &terraform.ResourceAttrDiff{ - Old: "", - New: "2", - }, - "mapBool.True": &terraform.ResourceAttrDiff{ - Old: "", - New: "true", - }, - "mapBool.False": &terraform.ResourceAttrDiff{ - Old: "", - New: "false", - }, - - "set.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "2", - }, - - "set.10": &terraform.ResourceAttrDiff{ - Old: "", - New: "10", - }, - - "set.50": &terraform.ResourceAttrDiff{ - Old: "", - New: "50", - }, - - "setDeep.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "2", - }, - - "setDeep.10.index": &terraform.ResourceAttrDiff{ - Old: "", - New: "10", - }, - - "setDeep.10.value": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - }, - - "setDeep.50.index": &terraform.ResourceAttrDiff{ - Old: "", - New: "50", - }, - - "setDeep.50.value": &terraform.ResourceAttrDiff{ - Old: "", - New: "bar", - }, - }, - }, - - Source: &MapFieldReader{ - Schema: s, - Map: BasicMapReader(map[string]string{ - "listMap.#": "2", - "listMap.0.foo": "bar", - "listMap.0.bar": "baz", - "listMap.1.baz": "baz", - }), - }, - } - }) -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_multi_test.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_multi_test.go deleted file mode 100644 index 85286a66..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_multi_test.go +++ /dev/null @@ -1,270 +0,0 @@ -package schema - -import ( - "reflect" - "strconv" - "testing" - - "github.com/hashicorp/terraform/terraform" -) - -func TestMultiLevelFieldReaderReadFieldExact(t *testing.T) { - cases := map[string]struct { - Addr []string - Readers []FieldReader - Level string - Result FieldReadResult - }{ - "specific": { - Addr: []string{"foo"}, - - Readers: []FieldReader{ - &MapFieldReader{ - Schema: map[string]*Schema{ - "foo": &Schema{Type: TypeString}, - }, - Map: BasicMapReader(map[string]string{ - "foo": "bar", - }), - }, - &MapFieldReader{ - Schema: map[string]*Schema{ - "foo": &Schema{Type: TypeString}, - }, - Map: BasicMapReader(map[string]string{ - "foo": "baz", - }), - }, - &MapFieldReader{ - Schema: map[string]*Schema{ - "foo": &Schema{Type: TypeString}, - }, - Map: BasicMapReader(map[string]string{}), - }, - }, - - Level: "1", - Result: FieldReadResult{ - Value: "baz", - Exists: true, - }, - }, - } - - for name, tc := range cases { - readers := make(map[string]FieldReader) - levels := make([]string, len(tc.Readers)) - for i, r := range tc.Readers { - is := strconv.FormatInt(int64(i), 10) - readers[is] = r - levels[i] = is - } - - r := &MultiLevelFieldReader{ - Readers: readers, - Levels: levels, - } - - out, err := r.ReadFieldExact(tc.Addr, tc.Level) - if err != nil { - t.Fatalf("%s: err: %s", name, err) - } - - if !reflect.DeepEqual(tc.Result, out) { - t.Fatalf("%s: bad: %#v", name, out) - } - } -} - -func TestMultiLevelFieldReaderReadFieldMerge(t *testing.T) { - cases := map[string]struct { - Addr []string - Readers []FieldReader - Result FieldReadResult - }{ - "stringInDiff": { - Addr: []string{"availability_zone"}, - - Readers: []FieldReader{ - &DiffFieldReader{ - Schema: map[string]*Schema{ - "availability_zone": &Schema{Type: TypeString}, - }, - - Source: &MapFieldReader{ - Schema: map[string]*Schema{ - "availability_zone": &Schema{Type: TypeString}, - }, - Map: BasicMapReader(map[string]string{ - "availability_zone": "foo", - }), - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "foo", - New: "bar", - RequiresNew: true, - }, - }, - }, - }, - }, - - Result: FieldReadResult{ - Value: "bar", - Exists: true, - }, - }, - - "lastLevelComputed": { - Addr: []string{"availability_zone"}, - - Readers: []FieldReader{ - &MapFieldReader{ - Schema: map[string]*Schema{ - "availability_zone": &Schema{Type: TypeString}, - }, - - Map: BasicMapReader(map[string]string{ - "availability_zone": "foo", - }), - }, - - &DiffFieldReader{ - Schema: map[string]*Schema{ - "availability_zone": &Schema{Type: TypeString}, - }, - - Source: &MapFieldReader{ - Schema: map[string]*Schema{ - "availability_zone": &Schema{Type: TypeString}, - }, - - Map: BasicMapReader(map[string]string{ - "availability_zone": "foo", - }), - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "foo", - New: "bar", - NewComputed: true, - }, - }, - }, - }, - }, - - Result: FieldReadResult{ - Value: "", - Exists: true, - Computed: true, - }, - }, - - "list of maps with removal in diff": { - Addr: []string{"config_vars"}, - - Readers: []FieldReader{ - &DiffFieldReader{ - Schema: map[string]*Schema{ - "config_vars": &Schema{ - Type: TypeList, - Elem: &Schema{Type: TypeMap}, - }, - }, - - Source: &MapFieldReader{ - Schema: map[string]*Schema{ - "config_vars": &Schema{ - Type: TypeList, - Elem: &Schema{Type: TypeMap}, - }, - }, - - Map: BasicMapReader(map[string]string{ - "config_vars.#": "2", - "config_vars.0.foo": "bar", - "config_vars.0.bar": "bar", - "config_vars.1.bar": "baz", - }), - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "config_vars.0.bar": &terraform.ResourceAttrDiff{ - NewRemoved: true, - }, - }, - }, - }, - }, - - Result: FieldReadResult{ - Value: []interface{}{ - map[string]interface{}{ - "foo": "bar", - }, - map[string]interface{}{ - "bar": "baz", - }, - }, - Exists: true, - }, - }, - - "first level only": { - Addr: []string{"foo"}, - - Readers: []FieldReader{ - &MapFieldReader{ - Schema: map[string]*Schema{ - "foo": &Schema{Type: TypeString}, - }, - Map: BasicMapReader(map[string]string{ - "foo": "bar", - }), - }, - &MapFieldReader{ - Schema: map[string]*Schema{ - "foo": &Schema{Type: TypeString}, - }, - Map: BasicMapReader(map[string]string{}), - }, - }, - - Result: FieldReadResult{ - Value: "bar", - Exists: true, - }, - }, - } - - for name, tc := range cases { - readers := make(map[string]FieldReader) - levels := make([]string, len(tc.Readers)) - for i, r := range tc.Readers { - is := strconv.FormatInt(int64(i), 10) - readers[is] = r - levels[i] = is - } - - r := &MultiLevelFieldReader{ - Readers: readers, - Levels: levels, - } - - out, err := r.ReadFieldMerge(tc.Addr, levels[len(levels)-1]) - if err != nil { - t.Fatalf("%s: err: %s", name, err) - } - - if !reflect.DeepEqual(tc.Result, out) { - t.Fatalf("%s: bad: %#v", name, out) - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/provider.go b/vendor/github.com/hashicorp/terraform/helper/schema/provider.go deleted file mode 100644 index 59dc750e..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/provider.go +++ /dev/null @@ -1,477 +0,0 @@ -package schema - -import ( - "context" - "errors" - "fmt" - "sort" - "sync" - - multierror "github.com/hashicorp/go-multierror" - "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/terraform" -) - -var ReservedProviderFields = []string{ - "alias", - "version", -} - -// Provider represents a resource provider in Terraform, and properly -// implements all of the ResourceProvider API. -// -// By defining a schema for the configuration of the provider, the -// map of supporting resources, and a configuration function, the schema -// framework takes over and handles all the provider operations for you. -// -// After defining the provider structure, it is unlikely that you'll require any -// of the methods on Provider itself. -type Provider struct { - // Schema is the schema for the configuration of this provider. If this - // provider has no configuration, this can be omitted. - // - // The keys of this map are the configuration keys, and the value is - // the schema describing the value of the configuration. - Schema map[string]*Schema - - // ResourcesMap is the list of available resources that this provider - // can manage, along with their Resource structure defining their - // own schemas and CRUD operations. - // - // Provider automatically handles routing operations such as Apply, - // Diff, etc. to the proper resource. - ResourcesMap map[string]*Resource - - // DataSourcesMap is the collection of available data sources that - // this provider implements, with a Resource instance defining - // the schema and Read operation of each. - // - // Resource instances for data sources must have a Read function - // and must *not* implement Create, Update or Delete. - DataSourcesMap map[string]*Resource - - // ProviderMetaSchema is the schema for the configuration of the meta - // information for this provider. If this provider has no meta info, - // this can be omitted. This functionality is currently experimental - // and subject to change or break without warning; it should only be - // used by providers that are collaborating on its use with the - // Terraform team. - ProviderMetaSchema map[string]*Schema - - // ConfigureFunc is a function for configuring the provider. If the - // provider doesn't need to be configured, this can be omitted. - // - // See the ConfigureFunc documentation for more information. - ConfigureFunc ConfigureFunc - - // MetaReset is called by TestReset to reset any state stored in the meta - // interface. This is especially important if the StopContext is stored by - // the provider. - MetaReset func() error - - meta interface{} - - // a mutex is required because TestReset can directly replace the stopCtx - stopMu sync.Mutex - stopCtx context.Context - stopCtxCancel context.CancelFunc - stopOnce sync.Once - - TerraformVersion string -} - -// ConfigureFunc is the function used to configure a Provider. -// -// The interface{} value returned by this function is stored and passed into -// the subsequent resources as the meta parameter. This return value is -// usually used to pass along a configured API client, a configuration -// structure, etc. -type ConfigureFunc func(*ResourceData) (interface{}, error) - -// InternalValidate should be called to validate the structure -// of the provider. -// -// This should be called in a unit test for any provider to verify -// before release that a provider is properly configured for use with -// this library. -func (p *Provider) InternalValidate() error { - if p == nil { - return errors.New("provider is nil") - } - - var validationErrors error - sm := schemaMap(p.Schema) - if err := sm.InternalValidate(sm); err != nil { - validationErrors = multierror.Append(validationErrors, err) - } - - // Provider-specific checks - for k, _ := range sm { - if isReservedProviderFieldName(k) { - return fmt.Errorf("%s is a reserved field name for a provider", k) - } - } - - for k, r := range p.ResourcesMap { - if err := r.InternalValidate(nil, true); err != nil { - validationErrors = multierror.Append(validationErrors, fmt.Errorf("resource %s: %s", k, err)) - } - } - - for k, r := range p.DataSourcesMap { - if err := r.InternalValidate(nil, false); err != nil { - validationErrors = multierror.Append(validationErrors, fmt.Errorf("data source %s: %s", k, err)) - } - } - - return validationErrors -} - -func isReservedProviderFieldName(name string) bool { - for _, reservedName := range ReservedProviderFields { - if name == reservedName { - return true - } - } - return false -} - -// Meta returns the metadata associated with this provider that was -// returned by the Configure call. It will be nil until Configure is called. -func (p *Provider) Meta() interface{} { - return p.meta -} - -// SetMeta can be used to forcefully set the Meta object of the provider. -// Note that if Configure is called the return value will override anything -// set here. -func (p *Provider) SetMeta(v interface{}) { - p.meta = v -} - -// Stopped reports whether the provider has been stopped or not. -func (p *Provider) Stopped() bool { - ctx := p.StopContext() - select { - case <-ctx.Done(): - return true - default: - return false - } -} - -// StopCh returns a channel that is closed once the provider is stopped. -func (p *Provider) StopContext() context.Context { - p.stopOnce.Do(p.stopInit) - - p.stopMu.Lock() - defer p.stopMu.Unlock() - - return p.stopCtx -} - -func (p *Provider) stopInit() { - p.stopMu.Lock() - defer p.stopMu.Unlock() - - p.stopCtx, p.stopCtxCancel = context.WithCancel(context.Background()) -} - -// Stop implementation of terraform.ResourceProvider interface. -func (p *Provider) Stop() error { - p.stopOnce.Do(p.stopInit) - - p.stopMu.Lock() - defer p.stopMu.Unlock() - - p.stopCtxCancel() - return nil -} - -// TestReset resets any state stored in the Provider, and will call TestReset -// on Meta if it implements the TestProvider interface. -// This may be used to reset the schema.Provider at the start of a test, and is -// automatically called by resource.Test. -func (p *Provider) TestReset() error { - p.stopInit() - if p.MetaReset != nil { - return p.MetaReset() - } - return nil -} - -// GetSchema implementation of terraform.ResourceProvider interface -func (p *Provider) GetSchema(req *terraform.ProviderSchemaRequest) (*terraform.ProviderSchema, error) { - resourceTypes := map[string]*configschema.Block{} - dataSources := map[string]*configschema.Block{} - - for _, name := range req.ResourceTypes { - if r, exists := p.ResourcesMap[name]; exists { - resourceTypes[name] = r.CoreConfigSchema() - } - } - for _, name := range req.DataSources { - if r, exists := p.DataSourcesMap[name]; exists { - dataSources[name] = r.CoreConfigSchema() - } - } - - return &terraform.ProviderSchema{ - Provider: schemaMap(p.Schema).CoreConfigSchema(), - ResourceTypes: resourceTypes, - DataSources: dataSources, - }, nil -} - -// Input implementation of terraform.ResourceProvider interface. -func (p *Provider) Input( - input terraform.UIInput, - c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) { - return schemaMap(p.Schema).Input(input, c) -} - -// Validate implementation of terraform.ResourceProvider interface. -func (p *Provider) Validate(c *terraform.ResourceConfig) ([]string, []error) { - if err := p.InternalValidate(); err != nil { - return nil, []error{fmt.Errorf( - "Internal validation of the provider failed! This is always a bug\n"+ - "with the provider itself, and not a user issue. Please report\n"+ - "this bug:\n\n%s", err)} - } - - return schemaMap(p.Schema).Validate(c) -} - -// ValidateResource implementation of terraform.ResourceProvider interface. -func (p *Provider) ValidateResource( - t string, c *terraform.ResourceConfig) ([]string, []error) { - r, ok := p.ResourcesMap[t] - if !ok { - return nil, []error{fmt.Errorf( - "Provider doesn't support resource: %s", t)} - } - - return r.Validate(c) -} - -// Configure implementation of terraform.ResourceProvider interface. -func (p *Provider) Configure(c *terraform.ResourceConfig) error { - // No configuration - if p.ConfigureFunc == nil { - return nil - } - - sm := schemaMap(p.Schema) - - // Get a ResourceData for this configuration. To do this, we actually - // generate an intermediary "diff" although that is never exposed. - diff, err := sm.Diff(nil, c, nil, p.meta, true) - if err != nil { - return err - } - - data, err := sm.Data(nil, diff) - if err != nil { - return err - } - - meta, err := p.ConfigureFunc(data) - if err != nil { - return err - } - - p.meta = meta - return nil -} - -// Apply implementation of terraform.ResourceProvider interface. -func (p *Provider) Apply( - info *terraform.InstanceInfo, - s *terraform.InstanceState, - d *terraform.InstanceDiff) (*terraform.InstanceState, error) { - r, ok := p.ResourcesMap[info.Type] - if !ok { - return nil, fmt.Errorf("unknown resource type: %s", info.Type) - } - - return r.Apply(s, d, p.meta) -} - -// Diff implementation of terraform.ResourceProvider interface. -func (p *Provider) Diff( - info *terraform.InstanceInfo, - s *terraform.InstanceState, - c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { - r, ok := p.ResourcesMap[info.Type] - if !ok { - return nil, fmt.Errorf("unknown resource type: %s", info.Type) - } - - return r.Diff(s, c, p.meta) -} - -// SimpleDiff is used by the new protocol wrappers to get a diff that doesn't -// attempt to calculate ignore_changes. -func (p *Provider) SimpleDiff( - info *terraform.InstanceInfo, - s *terraform.InstanceState, - c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { - r, ok := p.ResourcesMap[info.Type] - if !ok { - return nil, fmt.Errorf("unknown resource type: %s", info.Type) - } - - return r.simpleDiff(s, c, p.meta) -} - -// Refresh implementation of terraform.ResourceProvider interface. -func (p *Provider) Refresh( - info *terraform.InstanceInfo, - s *terraform.InstanceState) (*terraform.InstanceState, error) { - r, ok := p.ResourcesMap[info.Type] - if !ok { - return nil, fmt.Errorf("unknown resource type: %s", info.Type) - } - - return r.Refresh(s, p.meta) -} - -// Resources implementation of terraform.ResourceProvider interface. -func (p *Provider) Resources() []terraform.ResourceType { - keys := make([]string, 0, len(p.ResourcesMap)) - for k := range p.ResourcesMap { - keys = append(keys, k) - } - sort.Strings(keys) - - result := make([]terraform.ResourceType, 0, len(keys)) - for _, k := range keys { - resource := p.ResourcesMap[k] - - // This isn't really possible (it'd fail InternalValidate), but - // we do it anyways to avoid a panic. - if resource == nil { - resource = &Resource{} - } - - result = append(result, terraform.ResourceType{ - Name: k, - Importable: resource.Importer != nil, - - // Indicates that a provider is compiled against a new enough - // version of core to support the GetSchema method. - SchemaAvailable: true, - }) - } - - return result -} - -func (p *Provider) ImportState( - info *terraform.InstanceInfo, - id string) ([]*terraform.InstanceState, error) { - // Find the resource - r, ok := p.ResourcesMap[info.Type] - if !ok { - return nil, fmt.Errorf("unknown resource type: %s", info.Type) - } - - // If it doesn't support import, error - if r.Importer == nil { - return nil, fmt.Errorf("resource %s doesn't support import", info.Type) - } - - // Create the data - data := r.Data(nil) - data.SetId(id) - data.SetType(info.Type) - - // Call the import function - results := []*ResourceData{data} - if r.Importer.State != nil { - var err error - results, err = r.Importer.State(data, p.meta) - if err != nil { - return nil, err - } - } - - // Convert the results to InstanceState values and return it - states := make([]*terraform.InstanceState, len(results)) - for i, r := range results { - states[i] = r.State() - } - - // Verify that all are non-nil. If there are any nil the error - // isn't obvious so we circumvent that with a friendlier error. - for _, s := range states { - if s == nil { - return nil, fmt.Errorf( - "nil entry in ImportState results. This is always a bug with\n" + - "the resource that is being imported. Please report this as\n" + - "a bug to Terraform.") - } - } - - return states, nil -} - -// ValidateDataSource implementation of terraform.ResourceProvider interface. -func (p *Provider) ValidateDataSource( - t string, c *terraform.ResourceConfig) ([]string, []error) { - r, ok := p.DataSourcesMap[t] - if !ok { - return nil, []error{fmt.Errorf( - "Provider doesn't support data source: %s", t)} - } - - return r.Validate(c) -} - -// ReadDataDiff implementation of terraform.ResourceProvider interface. -func (p *Provider) ReadDataDiff( - info *terraform.InstanceInfo, - c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { - - r, ok := p.DataSourcesMap[info.Type] - if !ok { - return nil, fmt.Errorf("unknown data source: %s", info.Type) - } - - return r.Diff(nil, c, p.meta) -} - -// RefreshData implementation of terraform.ResourceProvider interface. -func (p *Provider) ReadDataApply( - info *terraform.InstanceInfo, - d *terraform.InstanceDiff) (*terraform.InstanceState, error) { - - r, ok := p.DataSourcesMap[info.Type] - if !ok { - return nil, fmt.Errorf("unknown data source: %s", info.Type) - } - - return r.ReadDataApply(d, p.meta) -} - -// DataSources implementation of terraform.ResourceProvider interface. -func (p *Provider) DataSources() []terraform.DataSource { - keys := make([]string, 0, len(p.DataSourcesMap)) - for k, _ := range p.DataSourcesMap { - keys = append(keys, k) - } - sort.Strings(keys) - - result := make([]terraform.DataSource, 0, len(keys)) - for _, k := range keys { - result = append(result, terraform.DataSource{ - Name: k, - - // Indicates that a provider is compiled against a new enough - // version of core to support the GetSchema method. - SchemaAvailable: true, - }) - } - - return result -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/provider_test.go b/vendor/github.com/hashicorp/terraform/helper/schema/provider_test.go deleted file mode 100644 index 1f9b5e8b..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/provider_test.go +++ /dev/null @@ -1,620 +0,0 @@ -package schema - -import ( - "fmt" - "reflect" - "strings" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/terraform" -) - -func TestProvider_impl(t *testing.T) { - var _ terraform.ResourceProvider = new(Provider) -} - -func TestProviderGetSchema(t *testing.T) { - // This functionality is already broadly tested in core_schema_test.go, - // so this is just to ensure that the call passes through correctly. - p := &Provider{ - Schema: map[string]*Schema{ - "bar": { - Type: TypeString, - Required: true, - }, - }, - ResourcesMap: map[string]*Resource{ - "foo": &Resource{ - Schema: map[string]*Schema{ - "bar": { - Type: TypeString, - Required: true, - }, - }, - }, - }, - DataSourcesMap: map[string]*Resource{ - "baz": &Resource{ - Schema: map[string]*Schema{ - "bur": { - Type: TypeString, - Required: true, - }, - }, - }, - }, - } - - want := &terraform.ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "bar": &configschema.Attribute{ - Type: cty.String, - Required: true, - }, - }, - BlockTypes: map[string]*configschema.NestedBlock{}, - }, - ResourceTypes: map[string]*configschema.Block{ - "foo": testResource(&configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "bar": &configschema.Attribute{ - Type: cty.String, - Required: true, - }, - }, - BlockTypes: map[string]*configschema.NestedBlock{}, - }), - }, - DataSources: map[string]*configschema.Block{ - "baz": testResource(&configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "bur": &configschema.Attribute{ - Type: cty.String, - Required: true, - }, - }, - BlockTypes: map[string]*configschema.NestedBlock{}, - }), - }, - } - got, err := p.GetSchema(&terraform.ProviderSchemaRequest{ - ResourceTypes: []string{"foo", "bar"}, - DataSources: []string{"baz", "bar"}, - }) - if err != nil { - t.Fatalf("unexpected error %s", err) - } - - if !cmp.Equal(got, want, equateEmpty, typeComparer) { - t.Error("wrong result:\n", cmp.Diff(got, want, equateEmpty, typeComparer)) - } -} - -func TestProviderConfigure(t *testing.T) { - cases := []struct { - P *Provider - Config map[string]interface{} - Err bool - }{ - { - P: &Provider{}, - Config: nil, - Err: false, - }, - - { - P: &Provider{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - - ConfigureFunc: func(d *ResourceData) (interface{}, error) { - if d.Get("foo").(int) == 42 { - return nil, nil - } - - return nil, fmt.Errorf("nope") - }, - }, - Config: map[string]interface{}{ - "foo": 42, - }, - Err: false, - }, - - { - P: &Provider{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - - ConfigureFunc: func(d *ResourceData) (interface{}, error) { - if d.Get("foo").(int) == 42 { - return nil, nil - } - - return nil, fmt.Errorf("nope") - }, - }, - Config: map[string]interface{}{ - "foo": 52, - }, - Err: true, - }, - } - - for i, tc := range cases { - c := terraform.NewResourceConfigRaw(tc.Config) - err := tc.P.Configure(c) - if err != nil != tc.Err { - t.Fatalf("%d: %s", i, err) - } - } -} - -func TestProviderResources(t *testing.T) { - cases := []struct { - P *Provider - Result []terraform.ResourceType - }{ - { - P: &Provider{}, - Result: []terraform.ResourceType{}, - }, - - { - P: &Provider{ - ResourcesMap: map[string]*Resource{ - "foo": nil, - "bar": nil, - }, - }, - Result: []terraform.ResourceType{ - terraform.ResourceType{Name: "bar", SchemaAvailable: true}, - terraform.ResourceType{Name: "foo", SchemaAvailable: true}, - }, - }, - - { - P: &Provider{ - ResourcesMap: map[string]*Resource{ - "foo": nil, - "bar": &Resource{Importer: &ResourceImporter{}}, - "baz": nil, - }, - }, - Result: []terraform.ResourceType{ - terraform.ResourceType{Name: "bar", Importable: true, SchemaAvailable: true}, - terraform.ResourceType{Name: "baz", SchemaAvailable: true}, - terraform.ResourceType{Name: "foo", SchemaAvailable: true}, - }, - }, - } - - for i, tc := range cases { - actual := tc.P.Resources() - if !reflect.DeepEqual(actual, tc.Result) { - t.Fatalf("%d: %#v", i, actual) - } - } -} - -func TestProviderDataSources(t *testing.T) { - cases := []struct { - P *Provider - Result []terraform.DataSource - }{ - { - P: &Provider{}, - Result: []terraform.DataSource{}, - }, - - { - P: &Provider{ - DataSourcesMap: map[string]*Resource{ - "foo": nil, - "bar": nil, - }, - }, - Result: []terraform.DataSource{ - terraform.DataSource{Name: "bar", SchemaAvailable: true}, - terraform.DataSource{Name: "foo", SchemaAvailable: true}, - }, - }, - } - - for i, tc := range cases { - actual := tc.P.DataSources() - if !reflect.DeepEqual(actual, tc.Result) { - t.Fatalf("%d: got %#v; want %#v", i, actual, tc.Result) - } - } -} - -func TestProviderValidate(t *testing.T) { - cases := []struct { - P *Provider - Config map[string]interface{} - Err bool - }{ - { - P: &Provider{ - Schema: map[string]*Schema{ - "foo": &Schema{}, - }, - }, - Config: nil, - Err: true, - }, - } - - for i, tc := range cases { - c := terraform.NewResourceConfigRaw(tc.Config) - _, es := tc.P.Validate(c) - if len(es) > 0 != tc.Err { - t.Fatalf("%d: %#v", i, es) - } - } -} - -func TestProviderDiff_legacyTimeoutType(t *testing.T) { - p := &Provider{ - ResourcesMap: map[string]*Resource{ - "blah": &Resource{ - Schema: map[string]*Schema{ - "foo": { - Type: TypeInt, - Optional: true, - }, - }, - Timeouts: &ResourceTimeout{ - Create: DefaultTimeout(10 * time.Minute), - }, - }, - }, - } - - invalidCfg := map[string]interface{}{ - "foo": 42, - "timeouts": []interface{}{ - map[string]interface{}{ - "create": "40m", - }, - }, - } - ic := terraform.NewResourceConfigRaw(invalidCfg) - _, err := p.Diff( - &terraform.InstanceInfo{ - Type: "blah", - }, - nil, - ic, - ) - if err != nil { - t.Fatal(err) - } -} - -func TestProviderDiff_timeoutInvalidValue(t *testing.T) { - p := &Provider{ - ResourcesMap: map[string]*Resource{ - "blah": &Resource{ - Schema: map[string]*Schema{ - "foo": { - Type: TypeInt, - Optional: true, - }, - }, - Timeouts: &ResourceTimeout{ - Create: DefaultTimeout(10 * time.Minute), - }, - }, - }, - } - - invalidCfg := map[string]interface{}{ - "foo": 42, - "timeouts": map[string]interface{}{ - "create": "invalid", - }, - } - ic := terraform.NewResourceConfigRaw(invalidCfg) - _, err := p.Diff( - &terraform.InstanceInfo{ - Type: "blah", - }, - nil, - ic, - ) - if err == nil { - t.Fatal("Expected provider.Diff to fail with invalid timeout value") - } - expectedErrMsg := `time: invalid duration "invalid"` - if !strings.Contains(err.Error(), expectedErrMsg) { - t.Fatalf("Unexpected error message: %q\nExpected message to contain %q", - err.Error(), - expectedErrMsg) - } -} - -func TestProviderValidateResource(t *testing.T) { - cases := []struct { - P *Provider - Type string - Config map[string]interface{} - Err bool - }{ - { - P: &Provider{}, - Type: "foo", - Config: nil, - Err: true, - }, - - { - P: &Provider{ - ResourcesMap: map[string]*Resource{ - "foo": &Resource{}, - }, - }, - Type: "foo", - Config: nil, - Err: false, - }, - } - - for i, tc := range cases { - c := terraform.NewResourceConfigRaw(tc.Config) - _, es := tc.P.ValidateResource(tc.Type, c) - if len(es) > 0 != tc.Err { - t.Fatalf("%d: %#v", i, es) - } - } -} - -func TestProviderImportState_default(t *testing.T) { - p := &Provider{ - ResourcesMap: map[string]*Resource{ - "foo": &Resource{ - Importer: &ResourceImporter{}, - }, - }, - } - - states, err := p.ImportState(&terraform.InstanceInfo{ - Type: "foo", - }, "bar") - if err != nil { - t.Fatalf("err: %s", err) - } - - if len(states) != 1 { - t.Fatalf("bad: %#v", states) - } - if states[0].ID != "bar" { - t.Fatalf("bad: %#v", states) - } -} - -func TestProviderImportState_setsId(t *testing.T) { - var val string - stateFunc := func(d *ResourceData, meta interface{}) ([]*ResourceData, error) { - val = d.Id() - return []*ResourceData{d}, nil - } - - p := &Provider{ - ResourcesMap: map[string]*Resource{ - "foo": &Resource{ - Importer: &ResourceImporter{ - State: stateFunc, - }, - }, - }, - } - - _, err := p.ImportState(&terraform.InstanceInfo{ - Type: "foo", - }, "bar") - if err != nil { - t.Fatalf("err: %s", err) - } - - if val != "bar" { - t.Fatal("should set id") - } -} - -func TestProviderImportState_setsType(t *testing.T) { - var tVal string - stateFunc := func(d *ResourceData, meta interface{}) ([]*ResourceData, error) { - d.SetId("foo") - tVal = d.State().Ephemeral.Type - return []*ResourceData{d}, nil - } - - p := &Provider{ - ResourcesMap: map[string]*Resource{ - "foo": &Resource{ - Importer: &ResourceImporter{ - State: stateFunc, - }, - }, - }, - } - - _, err := p.ImportState(&terraform.InstanceInfo{ - Type: "foo", - }, "bar") - if err != nil { - t.Fatalf("err: %s", err) - } - - if tVal != "foo" { - t.Fatal("should set type") - } -} - -func TestProviderMeta(t *testing.T) { - p := new(Provider) - if v := p.Meta(); v != nil { - t.Fatalf("bad: %#v", v) - } - - expected := 42 - p.SetMeta(42) - if v := p.Meta(); !reflect.DeepEqual(v, expected) { - t.Fatalf("bad: %#v", v) - } -} - -func TestProviderStop(t *testing.T) { - var p Provider - - if p.Stopped() { - t.Fatal("should not be stopped") - } - - // Verify stopch blocks - ch := p.StopContext().Done() - select { - case <-ch: - t.Fatal("should not be stopped") - case <-time.After(10 * time.Millisecond): - } - - // Stop it - if err := p.Stop(); err != nil { - t.Fatalf("err: %s", err) - } - - // Verify - if !p.Stopped() { - t.Fatal("should be stopped") - } - - select { - case <-ch: - case <-time.After(10 * time.Millisecond): - t.Fatal("should be stopped") - } -} - -func TestProviderStop_stopFirst(t *testing.T) { - var p Provider - - // Stop it - if err := p.Stop(); err != nil { - t.Fatalf("err: %s", err) - } - - // Verify - if !p.Stopped() { - t.Fatal("should be stopped") - } - - select { - case <-p.StopContext().Done(): - case <-time.After(10 * time.Millisecond): - t.Fatal("should be stopped") - } -} - -func TestProviderReset(t *testing.T) { - var p Provider - stopCtx := p.StopContext() - p.MetaReset = func() error { - stopCtx = p.StopContext() - return nil - } - - // cancel the current context - p.Stop() - - if err := p.TestReset(); err != nil { - t.Fatal(err) - } - - // the first context should have been replaced - if err := stopCtx.Err(); err != nil { - t.Fatal(err) - } - - // we should not get a canceled context here either - if err := p.StopContext().Err(); err != nil { - t.Fatal(err) - } -} - -func TestProvider_InternalValidate(t *testing.T) { - cases := []struct { - P *Provider - ExpectedErr error - }{ - { - P: &Provider{ - Schema: map[string]*Schema{ - "foo": { - Type: TypeBool, - Optional: true, - }, - }, - }, - ExpectedErr: nil, - }, - { // Reserved resource fields should be allowed in provider block - P: &Provider{ - Schema: map[string]*Schema{ - "provisioner": { - Type: TypeString, - Optional: true, - }, - "count": { - Type: TypeInt, - Optional: true, - }, - }, - }, - ExpectedErr: nil, - }, - { // Reserved provider fields should not be allowed - P: &Provider{ - Schema: map[string]*Schema{ - "alias": { - Type: TypeString, - Optional: true, - }, - }, - }, - ExpectedErr: fmt.Errorf("%s is a reserved field name for a provider", "alias"), - }, - } - - for i, tc := range cases { - err := tc.P.InternalValidate() - if tc.ExpectedErr == nil { - if err != nil { - t.Fatalf("%d: Error returned (expected no error): %s", i, err) - } - continue - } - if tc.ExpectedErr != nil && err == nil { - t.Fatalf("%d: Expected error (%s), but no error returned", i, tc.ExpectedErr) - } - if err.Error() != tc.ExpectedErr.Error() { - t.Fatalf("%d: Errors don't match. Expected: %#v Given: %#v", i, tc.ExpectedErr, err) - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go b/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go deleted file mode 100644 index eee155bf..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go +++ /dev/null @@ -1,205 +0,0 @@ -package schema - -import ( - "context" - "errors" - "fmt" - "sync" - - "github.com/hashicorp/go-multierror" - "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/terraform" -) - -// Provisioner represents a resource provisioner in Terraform and properly -// implements all of the ResourceProvisioner API. -// -// This higher level structure makes it much easier to implement a new or -// custom provisioner for Terraform. -// -// The function callbacks for this structure are all passed a context object. -// This context object has a number of pre-defined values that can be accessed -// via the global functions defined in context.go. -type Provisioner struct { - // ConnSchema is the schema for the connection settings for this - // provisioner. - // - // The keys of this map are the configuration keys, and the value is - // the schema describing the value of the configuration. - // - // NOTE: The value of connection keys can only be strings for now. - ConnSchema map[string]*Schema - - // Schema is the schema for the usage of this provisioner. - // - // The keys of this map are the configuration keys, and the value is - // the schema describing the value of the configuration. - Schema map[string]*Schema - - // ApplyFunc is the function for executing the provisioner. This is required. - // It is given a context. See the Provisioner struct docs for more - // information. - ApplyFunc func(ctx context.Context) error - - // ValidateFunc is a function for extended validation. This is optional - // and should be used when individual field validation is not enough. - ValidateFunc func(*terraform.ResourceConfig) ([]string, []error) - - stopCtx context.Context - stopCtxCancel context.CancelFunc - stopOnce sync.Once -} - -// Keys that can be used to access data in the context parameters for -// Provisioners. -var ( - connDataInvalid = contextKey("data invalid") - - // This returns a *ResourceData for the connection information. - // Guaranteed to never be nil. - ProvConnDataKey = contextKey("provider conn data") - - // This returns a *ResourceData for the config information. - // Guaranteed to never be nil. - ProvConfigDataKey = contextKey("provider config data") - - // This returns a terraform.UIOutput. Guaranteed to never be nil. - ProvOutputKey = contextKey("provider output") - - // This returns the raw InstanceState passed to Apply. Guaranteed to - // be set, but may be nil. - ProvRawStateKey = contextKey("provider raw state") -) - -// InternalValidate should be called to validate the structure -// of the provisioner. -// -// This should be called in a unit test to verify before release that this -// structure is properly configured for use. -func (p *Provisioner) InternalValidate() error { - if p == nil { - return errors.New("provisioner is nil") - } - - var validationErrors error - { - sm := schemaMap(p.ConnSchema) - if err := sm.InternalValidate(sm); err != nil { - validationErrors = multierror.Append(validationErrors, err) - } - } - - { - sm := schemaMap(p.Schema) - if err := sm.InternalValidate(sm); err != nil { - validationErrors = multierror.Append(validationErrors, err) - } - } - - if p.ApplyFunc == nil { - validationErrors = multierror.Append(validationErrors, fmt.Errorf( - "ApplyFunc must not be nil")) - } - - return validationErrors -} - -// StopContext returns a context that checks whether a provisioner is stopped. -func (p *Provisioner) StopContext() context.Context { - p.stopOnce.Do(p.stopInit) - return p.stopCtx -} - -func (p *Provisioner) stopInit() { - p.stopCtx, p.stopCtxCancel = context.WithCancel(context.Background()) -} - -// Stop implementation of terraform.ResourceProvisioner interface. -func (p *Provisioner) Stop() error { - p.stopOnce.Do(p.stopInit) - p.stopCtxCancel() - return nil -} - -// GetConfigSchema implementation of terraform.ResourceProvisioner interface. -func (p *Provisioner) GetConfigSchema() (*configschema.Block, error) { - return schemaMap(p.Schema).CoreConfigSchema(), nil -} - -// Apply implementation of terraform.ResourceProvisioner interface. -func (p *Provisioner) Apply( - o terraform.UIOutput, - s *terraform.InstanceState, - c *terraform.ResourceConfig) error { - var connData, configData *ResourceData - - { - // We first need to turn the connection information into a - // terraform.ResourceConfig so that we can use that type to more - // easily build a ResourceData structure. We do this by simply treating - // the conn info as configuration input. - raw := make(map[string]interface{}) - if s != nil { - for k, v := range s.Ephemeral.ConnInfo { - raw[k] = v - } - } - - c := terraform.NewResourceConfigRaw(raw) - sm := schemaMap(p.ConnSchema) - diff, err := sm.Diff(nil, c, nil, nil, true) - if err != nil { - return err - } - connData, err = sm.Data(nil, diff) - if err != nil { - return err - } - } - - { - // Build the configuration data. Doing this requires making a "diff" - // even though that's never used. We use that just to get the correct types. - configMap := schemaMap(p.Schema) - diff, err := configMap.Diff(nil, c, nil, nil, true) - if err != nil { - return err - } - configData, err = configMap.Data(nil, diff) - if err != nil { - return err - } - } - - // Build the context and call the function - ctx := p.StopContext() - ctx = context.WithValue(ctx, ProvConnDataKey, connData) - ctx = context.WithValue(ctx, ProvConfigDataKey, configData) - ctx = context.WithValue(ctx, ProvOutputKey, o) - ctx = context.WithValue(ctx, ProvRawStateKey, s) - return p.ApplyFunc(ctx) -} - -// Validate implements the terraform.ResourceProvisioner interface. -func (p *Provisioner) Validate(c *terraform.ResourceConfig) (ws []string, es []error) { - if err := p.InternalValidate(); err != nil { - return nil, []error{fmt.Errorf( - "Internal validation of the provisioner failed! This is always a bug\n"+ - "with the provisioner itself, and not a user issue. Please report\n"+ - "this bug:\n\n%s", err)} - } - - if p.Schema != nil { - w, e := schemaMap(p.Schema).Validate(c) - ws = append(ws, w...) - es = append(es, e...) - } - - if p.ValidateFunc != nil { - w, e := p.ValidateFunc(c) - ws = append(ws, w...) - es = append(es, e...) - } - - return ws, es -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/provisioner_test.go b/vendor/github.com/hashicorp/terraform/helper/schema/provisioner_test.go deleted file mode 100644 index bac6610d..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/provisioner_test.go +++ /dev/null @@ -1,334 +0,0 @@ -package schema - -import ( - "context" - "fmt" - "reflect" - "testing" - "time" - - "github.com/hashicorp/terraform/terraform" -) - -func TestProvisioner_impl(t *testing.T) { - var _ terraform.ResourceProvisioner = new(Provisioner) -} - -func noopApply(ctx context.Context) error { - return nil -} - -func TestProvisionerValidate(t *testing.T) { - cases := []struct { - Name string - P *Provisioner - Config map[string]interface{} - Err bool - Warns []string - }{ - { - Name: "No ApplyFunc", - P: &Provisioner{}, - Config: nil, - Err: true, - }, - { - Name: "Incorrect schema", - P: &Provisioner{ - Schema: map[string]*Schema{ - "foo": {}, - }, - ApplyFunc: noopApply, - }, - Config: nil, - Err: true, - }, - { - "Basic required field", - &Provisioner{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Required: true, - Type: TypeString, - }, - }, - ApplyFunc: noopApply, - }, - nil, - true, - nil, - }, - - { - "Basic required field set", - &Provisioner{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Required: true, - Type: TypeString, - }, - }, - ApplyFunc: noopApply, - }, - map[string]interface{}{ - "foo": "bar", - }, - false, - nil, - }, - { - Name: "Warning from property validation", - P: &Provisioner{ - Schema: map[string]*Schema{ - "foo": { - Type: TypeString, - Optional: true, - ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { - ws = append(ws, "Simple warning from property validation") - return - }, - }, - }, - ApplyFunc: noopApply, - }, - Config: map[string]interface{}{ - "foo": "", - }, - Err: false, - Warns: []string{"Simple warning from property validation"}, - }, - { - Name: "No schema", - P: &Provisioner{ - Schema: nil, - ApplyFunc: noopApply, - }, - Config: nil, - Err: false, - }, - { - Name: "Warning from provisioner ValidateFunc", - P: &Provisioner{ - Schema: nil, - ApplyFunc: noopApply, - ValidateFunc: func(*terraform.ResourceConfig) (ws []string, errors []error) { - ws = append(ws, "Simple warning from provisioner ValidateFunc") - return - }, - }, - Config: nil, - Err: false, - Warns: []string{"Simple warning from provisioner ValidateFunc"}, - }, - } - - for i, tc := range cases { - t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { - c := terraform.NewResourceConfigRaw(tc.Config) - ws, es := tc.P.Validate(c) - if len(es) > 0 != tc.Err { - t.Fatalf("%d: %#v %s", i, es, es) - } - if (tc.Warns != nil || len(ws) != 0) && !reflect.DeepEqual(ws, tc.Warns) { - t.Fatalf("%d: warnings mismatch, actual: %#v", i, ws) - } - }) - } -} - -func TestProvisionerApply(t *testing.T) { - cases := []struct { - Name string - P *Provisioner - Conn map[string]string - Config map[string]interface{} - Err bool - }{ - { - "Basic config", - &Provisioner{ - ConnSchema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - - ApplyFunc: func(ctx context.Context) error { - cd := ctx.Value(ProvConnDataKey).(*ResourceData) - d := ctx.Value(ProvConfigDataKey).(*ResourceData) - if d.Get("foo").(int) != 42 { - return fmt.Errorf("bad config data") - } - if cd.Get("foo").(string) != "bar" { - return fmt.Errorf("bad conn data") - } - - return nil - }, - }, - map[string]string{ - "foo": "bar", - }, - map[string]interface{}{ - "foo": 42, - }, - false, - }, - } - - for i, tc := range cases { - t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { - c := terraform.NewResourceConfigRaw(tc.Config) - - state := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: tc.Conn, - }, - } - - err := tc.P.Apply(nil, state, c) - if err != nil != tc.Err { - t.Fatalf("%d: %s", i, err) - } - }) - } -} - -func TestProvisionerApply_nilState(t *testing.T) { - p := &Provisioner{ - ConnSchema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - - ApplyFunc: func(ctx context.Context) error { - return nil - }, - } - - conf := map[string]interface{}{ - "foo": 42, - } - - c := terraform.NewResourceConfigRaw(conf) - err := p.Apply(nil, nil, c) - if err != nil { - t.Fatalf("err: %s", err) - } -} - -func TestProvisionerStop(t *testing.T) { - var p Provisioner - - // Verify stopch blocks - ch := p.StopContext().Done() - select { - case <-ch: - t.Fatal("should not be stopped") - case <-time.After(10 * time.Millisecond): - } - - // Stop it - if err := p.Stop(); err != nil { - t.Fatalf("err: %s", err) - } - - select { - case <-ch: - case <-time.After(10 * time.Millisecond): - t.Fatal("should be stopped") - } -} - -func TestProvisionerStop_apply(t *testing.T) { - p := &Provisioner{ - ConnSchema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - - ApplyFunc: func(ctx context.Context) error { - <-ctx.Done() - return nil - }, - } - - conn := map[string]string{ - "foo": "bar", - } - - conf := map[string]interface{}{ - "foo": 42, - } - - c := terraform.NewResourceConfigRaw(conf) - state := &terraform.InstanceState{ - Ephemeral: terraform.EphemeralState{ - ConnInfo: conn, - }, - } - - // Run the apply in a goroutine - doneCh := make(chan struct{}) - go func() { - p.Apply(nil, state, c) - close(doneCh) - }() - - // Should block - select { - case <-doneCh: - t.Fatal("should not be done") - case <-time.After(10 * time.Millisecond): - } - - // Stop! - p.Stop() - - select { - case <-doneCh: - case <-time.After(10 * time.Millisecond): - t.Fatal("should be done") - } -} - -func TestProvisionerStop_stopFirst(t *testing.T) { - var p Provisioner - - // Stop it - if err := p.Stop(); err != nil { - t.Fatalf("err: %s", err) - } - - select { - case <-p.StopContext().Done(): - case <-time.After(10 * time.Millisecond): - t.Fatal("should be stopped") - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource.go deleted file mode 100644 index dcfb32ae..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/resource.go +++ /dev/null @@ -1,842 +0,0 @@ -package schema - -import ( - "errors" - "fmt" - "log" - "strconv" - - "github.com/hashicorp/terraform/terraform" - "github.com/zclconf/go-cty/cty" -) - -var ReservedDataSourceFields = []string{ - "connection", - "count", - "depends_on", - "lifecycle", - "provider", - "provisioner", -} - -var ReservedResourceFields = []string{ - "connection", - "count", - "depends_on", - "id", - "lifecycle", - "provider", - "provisioner", -} - -// Resource represents a thing in Terraform that has a set of configurable -// attributes and a lifecycle (create, read, update, delete). -// -// The Resource schema is an abstraction that allows provider writers to -// worry only about CRUD operations while off-loading validation, diff -// generation, etc. to this higher level library. -// -// In spite of the name, this struct is not used only for terraform resources, -// but also for data sources. In the case of data sources, the Create, -// Update and Delete functions must not be provided. -type Resource struct { - // Schema is the schema for the configuration of this resource. - // - // The keys of this map are the configuration keys, and the values - // describe the schema of the configuration value. - // - // The schema is used to represent both configurable data as well - // as data that might be computed in the process of creating this - // resource. - Schema map[string]*Schema - - // SchemaVersion is the version number for this resource's Schema - // definition. The current SchemaVersion stored in the state for each - // resource. Provider authors can increment this version number - // when Schema semantics change. If the State's SchemaVersion is less than - // the current SchemaVersion, the InstanceState is yielded to the - // MigrateState callback, where the provider can make whatever changes it - // needs to update the state to be compatible to the latest version of the - // Schema. - // - // When unset, SchemaVersion defaults to 0, so provider authors can start - // their Versioning at any integer >= 1 - SchemaVersion int - - // MigrateState is deprecated and any new changes to a resource's schema - // should be handled by StateUpgraders. Existing MigrateState implementations - // should remain for compatibility with existing state. MigrateState will - // still be called if the stored SchemaVersion is less than the - // first version of the StateUpgraders. - // - // MigrateState is responsible for updating an InstanceState with an old - // version to the format expected by the current version of the Schema. - // - // It is called during Refresh if the State's stored SchemaVersion is less - // than the current SchemaVersion of the Resource. - // - // The function is yielded the state's stored SchemaVersion and a pointer to - // the InstanceState that needs updating, as well as the configured - // provider's configured meta interface{}, in case the migration process - // needs to make any remote API calls. - MigrateState StateMigrateFunc - - // StateUpgraders contains the functions responsible for upgrading an - // existing state with an old schema version to a newer schema. It is - // called specifically by Terraform when the stored schema version is less - // than the current SchemaVersion of the Resource. - // - // StateUpgraders map specific schema versions to a StateUpgrader - // function. The registered versions are expected to be ordered, - // consecutive values. The initial value may be greater than 0 to account - // for legacy schemas that weren't recorded and can be handled by - // MigrateState. - StateUpgraders []StateUpgrader - - // The functions below are the CRUD operations for this resource. - // - // The only optional operation is Update. If Update is not implemented, - // then updates will not be supported for this resource. - // - // The ResourceData parameter in the functions below are used to - // query configuration and changes for the resource as well as to set - // the ID, computed data, etc. - // - // The interface{} parameter is the result of the ConfigureFunc in - // the provider for this resource. If the provider does not define - // a ConfigureFunc, this will be nil. This parameter should be used - // to store API clients, configuration structures, etc. - // - // If any errors occur during each of the operation, an error should be - // returned. If a resource was partially updated, be careful to enable - // partial state mode for ResourceData and use it accordingly. - // - // Exists is a function that is called to check if a resource still - // exists. If this returns false, then this will affect the diff - // accordingly. If this function isn't set, it will not be called. You - // can also signal existence in the Read method by calling d.SetId("") - // if the Resource is no longer present and should be removed from state. - // The *ResourceData passed to Exists should _not_ be modified. - Create CreateFunc - Read ReadFunc - Update UpdateFunc - Delete DeleteFunc - Exists ExistsFunc - - // CustomizeDiff is a custom function for working with the diff that - // Terraform has created for this resource - it can be used to customize the - // diff that has been created, diff values not controlled by configuration, - // or even veto the diff altogether and abort the plan. It is passed a - // *ResourceDiff, a structure similar to ResourceData but lacking most write - // functions like Set, while introducing new functions that work with the - // diff such as SetNew, SetNewComputed, and ForceNew. - // - // The phases Terraform runs this in, and the state available via functions - // like Get and GetChange, are as follows: - // - // * New resource: One run with no state - // * Existing resource: One run with state - // * Existing resource, forced new: One run with state (before ForceNew), - // then one run without state (as if new resource) - // * Tainted resource: No runs (custom diff logic is skipped) - // * Destroy: No runs (standard diff logic is skipped on destroy diffs) - // - // This function needs to be resilient to support all scenarios. - // - // If this function needs to access external API resources, remember to flag - // the RequiresRefresh attribute mentioned below to ensure that - // -refresh=false is blocked when running plan or apply, as this means that - // this resource requires refresh-like behaviour to work effectively. - // - // For the most part, only computed fields can be customized by this - // function. - // - // This function is only allowed on regular resources (not data sources). - CustomizeDiff CustomizeDiffFunc - - // Importer is the ResourceImporter implementation for this resource. - // If this is nil, then this resource does not support importing. If - // this is non-nil, then it supports importing and ResourceImporter - // must be validated. The validity of ResourceImporter is verified - // by InternalValidate on Resource. - Importer *ResourceImporter - - // If non-empty, this string is emitted as a warning during Validate. - DeprecationMessage string - - // Timeouts allow users to specify specific time durations in which an - // operation should time out, to allow them to extend an action to suit their - // usage. For example, a user may specify a large Creation timeout for their - // AWS RDS Instance due to it's size, or restoring from a snapshot. - // Resource implementors must enable Timeout support by adding the allowed - // actions (Create, Read, Update, Delete, Default) to the Resource struct, and - // accessing them in the matching methods. - Timeouts *ResourceTimeout -} - -// ShimInstanceStateFromValue converts a cty.Value to a -// terraform.InstanceState. -func (r *Resource) ShimInstanceStateFromValue(state cty.Value) (*terraform.InstanceState, error) { - // Get the raw shimmed value. While this is correct, the set hashes don't - // match those from the Schema. - s := terraform.NewInstanceStateShimmedFromValue(state, r.SchemaVersion) - - // We now rebuild the state through the ResourceData, so that the set indexes - // match what helper/schema expects. - data, err := schemaMap(r.Schema).Data(s, nil) - if err != nil { - return nil, err - } - - s = data.State() - if s == nil { - s = &terraform.InstanceState{} - } - return s, nil -} - -// See Resource documentation. -type CreateFunc func(*ResourceData, interface{}) error - -// See Resource documentation. -type ReadFunc func(*ResourceData, interface{}) error - -// See Resource documentation. -type UpdateFunc func(*ResourceData, interface{}) error - -// See Resource documentation. -type DeleteFunc func(*ResourceData, interface{}) error - -// See Resource documentation. -type ExistsFunc func(*ResourceData, interface{}) (bool, error) - -// See Resource documentation. -type StateMigrateFunc func( - int, *terraform.InstanceState, interface{}) (*terraform.InstanceState, error) - -type StateUpgrader struct { - // Version is the version schema that this Upgrader will handle, converting - // it to Version+1. - Version int - - // Type describes the schema that this function can upgrade. Type is - // required to decode the schema if the state was stored in a legacy - // flatmap format. - Type cty.Type - - // Upgrade takes the JSON encoded state and the provider meta value, and - // upgrades the state one single schema version. The provided state is - // deocded into the default json types using a map[string]interface{}. It - // is up to the StateUpgradeFunc to ensure that the returned value can be - // encoded using the new schema. - Upgrade StateUpgradeFunc -} - -// See StateUpgrader -type StateUpgradeFunc func(rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) - -// See Resource documentation. -type CustomizeDiffFunc func(*ResourceDiff, interface{}) error - -// Apply creates, updates, and/or deletes a resource. -func (r *Resource) Apply( - s *terraform.InstanceState, - d *terraform.InstanceDiff, - meta interface{}) (*terraform.InstanceState, error) { - data, err := schemaMap(r.Schema).Data(s, d) - if err != nil { - return s, err - } - if s != nil && data != nil { - data.providerMeta = s.ProviderMeta - } - - // Instance Diff shoould have the timeout info, need to copy it over to the - // ResourceData meta - rt := ResourceTimeout{} - if _, ok := d.Meta[TimeoutKey]; ok { - if err := rt.DiffDecode(d); err != nil { - log.Printf("[ERR] Error decoding ResourceTimeout: %s", err) - } - } else if s != nil { - if _, ok := s.Meta[TimeoutKey]; ok { - if err := rt.StateDecode(s); err != nil { - log.Printf("[ERR] Error decoding ResourceTimeout: %s", err) - } - } - } else { - log.Printf("[DEBUG] No meta timeoutkey found in Apply()") - } - data.timeouts = &rt - - if s == nil { - // The Terraform API dictates that this should never happen, but - // it doesn't hurt to be safe in this case. - s = new(terraform.InstanceState) - } - - if d.Destroy || d.RequiresNew() { - if s.ID != "" { - // Destroy the resource since it is created - if err := r.Delete(data, meta); err != nil { - return r.recordCurrentSchemaVersion(data.State()), err - } - - // Make sure the ID is gone. - data.SetId("") - } - - // If we're only destroying, and not creating, then return - // now since we're done! - if !d.RequiresNew() { - return nil, nil - } - - // Reset the data to be stateless since we just destroyed - data, err = schemaMap(r.Schema).Data(nil, d) - // data was reset, need to re-apply the parsed timeouts - data.timeouts = &rt - if err != nil { - return nil, err - } - } - - err = nil - if data.Id() == "" { - // We're creating, it is a new resource. - data.MarkNewResource() - err = r.Create(data, meta) - } else { - if r.Update == nil { - return s, fmt.Errorf("doesn't support update") - } - - err = r.Update(data, meta) - } - - return r.recordCurrentSchemaVersion(data.State()), err -} - -// Diff returns a diff of this resource. -func (r *Resource) Diff( - s *terraform.InstanceState, - c *terraform.ResourceConfig, - meta interface{}) (*terraform.InstanceDiff, error) { - - t := &ResourceTimeout{} - err := t.ConfigDecode(r, c) - - if err != nil { - return nil, fmt.Errorf("[ERR] Error decoding timeout: %s", err) - } - - instanceDiff, err := schemaMap(r.Schema).Diff(s, c, r.CustomizeDiff, meta, true) - if err != nil { - return instanceDiff, err - } - - if instanceDiff != nil { - if err := t.DiffEncode(instanceDiff); err != nil { - log.Printf("[ERR] Error encoding timeout to instance diff: %s", err) - } - } else { - log.Printf("[DEBUG] Instance Diff is nil in Diff()") - } - - return instanceDiff, err -} - -func (r *Resource) simpleDiff( - s *terraform.InstanceState, - c *terraform.ResourceConfig, - meta interface{}) (*terraform.InstanceDiff, error) { - - instanceDiff, err := schemaMap(r.Schema).Diff(s, c, r.CustomizeDiff, meta, false) - if err != nil { - return instanceDiff, err - } - - if instanceDiff == nil { - instanceDiff = terraform.NewInstanceDiff() - } - - // Make sure the old value is set in each of the instance diffs. - // This was done by the RequiresNew logic in the full legacy Diff. - for k, attr := range instanceDiff.Attributes { - if attr == nil { - continue - } - if s != nil { - attr.Old = s.Attributes[k] - } - } - - return instanceDiff, nil -} - -// Validate validates the resource configuration against the schema. -func (r *Resource) Validate(c *terraform.ResourceConfig) ([]string, []error) { - warns, errs := schemaMap(r.Schema).Validate(c) - - if r.DeprecationMessage != "" { - warns = append(warns, r.DeprecationMessage) - } - - return warns, errs -} - -// ReadDataApply loads the data for a data source, given a diff that -// describes the configuration arguments and desired computed attributes. -func (r *Resource) ReadDataApply( - d *terraform.InstanceDiff, - meta interface{}, -) (*terraform.InstanceState, error) { - // Data sources are always built completely from scratch - // on each read, so the source state is always nil. - data, err := schemaMap(r.Schema).Data(nil, d) - if err != nil { - return nil, err - } - - err = r.Read(data, meta) - state := data.State() - if state != nil && state.ID == "" { - // Data sources can set an ID if they want, but they aren't - // required to; we'll provide a placeholder if they don't, - // to preserve the invariant that all resources have non-empty - // ids. - state.ID = "-" - } - - return r.recordCurrentSchemaVersion(state), err -} - -// RefreshWithoutUpgrade reads the instance state, but does not call -// MigrateState or the StateUpgraders, since those are now invoked in a -// separate API call. -// RefreshWithoutUpgrade is part of the new plugin shims. -func (r *Resource) RefreshWithoutUpgrade( - s *terraform.InstanceState, - meta interface{}) (*terraform.InstanceState, error) { - // If the ID is already somehow blank, it doesn't exist - if s.ID == "" { - return nil, nil - } - - rt := ResourceTimeout{} - if _, ok := s.Meta[TimeoutKey]; ok { - if err := rt.StateDecode(s); err != nil { - log.Printf("[ERR] Error decoding ResourceTimeout: %s", err) - } - } - - if r.Exists != nil { - // Make a copy of data so that if it is modified it doesn't - // affect our Read later. - data, err := schemaMap(r.Schema).Data(s, nil) - data.timeouts = &rt - - if err != nil { - return s, err - } - - if s != nil { - data.providerMeta = s.ProviderMeta - } - - exists, err := r.Exists(data, meta) - if err != nil { - return s, err - } - if !exists { - return nil, nil - } - } - - data, err := schemaMap(r.Schema).Data(s, nil) - data.timeouts = &rt - if err != nil { - return s, err - } - - if s != nil { - data.providerMeta = s.ProviderMeta - } - - err = r.Read(data, meta) - state := data.State() - if state != nil && state.ID == "" { - state = nil - } - - return r.recordCurrentSchemaVersion(state), err -} - -// Refresh refreshes the state of the resource. -func (r *Resource) Refresh( - s *terraform.InstanceState, - meta interface{}) (*terraform.InstanceState, error) { - // If the ID is already somehow blank, it doesn't exist - if s.ID == "" { - return nil, nil - } - - rt := ResourceTimeout{} - if _, ok := s.Meta[TimeoutKey]; ok { - if err := rt.StateDecode(s); err != nil { - log.Printf("[ERR] Error decoding ResourceTimeout: %s", err) - } - } - - if r.Exists != nil { - // Make a copy of data so that if it is modified it doesn't - // affect our Read later. - data, err := schemaMap(r.Schema).Data(s, nil) - data.timeouts = &rt - - if err != nil { - return s, err - } - - exists, err := r.Exists(data, meta) - if err != nil { - return s, err - } - if !exists { - return nil, nil - } - } - - // there may be new StateUpgraders that need to be run - s, err := r.upgradeState(s, meta) - if err != nil { - return s, err - } - - data, err := schemaMap(r.Schema).Data(s, nil) - data.timeouts = &rt - if err != nil { - return s, err - } - - err = r.Read(data, meta) - state := data.State() - if state != nil && state.ID == "" { - state = nil - } - - return r.recordCurrentSchemaVersion(state), err -} - -func (r *Resource) upgradeState(s *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) { - var err error - - needsMigration, stateSchemaVersion := r.checkSchemaVersion(s) - migrate := needsMigration && r.MigrateState != nil - - if migrate { - s, err = r.MigrateState(stateSchemaVersion, s, meta) - if err != nil { - return s, err - } - } - - if len(r.StateUpgraders) == 0 { - return s, nil - } - - // If we ran MigrateState, then the stateSchemaVersion value is no longer - // correct. We can expect the first upgrade function to be the correct - // schema type version. - if migrate { - stateSchemaVersion = r.StateUpgraders[0].Version - } - - schemaType := r.CoreConfigSchema().ImpliedType() - // find the expected type to convert the state - for _, upgrader := range r.StateUpgraders { - if stateSchemaVersion == upgrader.Version { - schemaType = upgrader.Type - } - } - - // StateUpgraders only operate on the new JSON format state, so the state - // need to be converted. - stateVal, err := StateValueFromInstanceState(s, schemaType) - if err != nil { - return nil, err - } - - jsonState, err := StateValueToJSONMap(stateVal, schemaType) - if err != nil { - return nil, err - } - - for _, upgrader := range r.StateUpgraders { - if stateSchemaVersion != upgrader.Version { - continue - } - - jsonState, err = upgrader.Upgrade(jsonState, meta) - if err != nil { - return nil, err - } - stateSchemaVersion++ - } - - // now we need to re-flatmap the new state - stateVal, err = JSONMapToStateValue(jsonState, r.CoreConfigSchema()) - if err != nil { - return nil, err - } - - return r.ShimInstanceStateFromValue(stateVal) -} - -// InternalValidate should be called to validate the structure -// of the resource. -// -// This should be called in a unit test for any resource to verify -// before release that a resource is properly configured for use with -// this library. -// -// Provider.InternalValidate() will automatically call this for all of -// the resources it manages, so you don't need to call this manually if it -// is part of a Provider. -func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error { - if r == nil { - return errors.New("resource is nil") - } - - if !writable { - if r.Create != nil || r.Update != nil || r.Delete != nil { - return fmt.Errorf("must not implement Create, Update or Delete") - } - - // CustomizeDiff cannot be defined for read-only resources - if r.CustomizeDiff != nil { - return fmt.Errorf("cannot implement CustomizeDiff") - } - } - - tsm := topSchemaMap - - if r.isTopLevel() && writable { - // All non-Computed attributes must be ForceNew if Update is not defined - if r.Update == nil { - nonForceNewAttrs := make([]string, 0) - for k, v := range r.Schema { - if !v.ForceNew && !v.Computed { - nonForceNewAttrs = append(nonForceNewAttrs, k) - } - } - if len(nonForceNewAttrs) > 0 { - return fmt.Errorf( - "No Update defined, must set ForceNew on: %#v", nonForceNewAttrs) - } - } else { - nonUpdateableAttrs := make([]string, 0) - for k, v := range r.Schema { - if v.ForceNew || v.Computed && !v.Optional { - nonUpdateableAttrs = append(nonUpdateableAttrs, k) - } - } - updateableAttrs := len(r.Schema) - len(nonUpdateableAttrs) - if updateableAttrs == 0 { - return fmt.Errorf( - "All fields are ForceNew or Computed w/out Optional, Update is superfluous") - } - } - - tsm = schemaMap(r.Schema) - - // Destroy, and Read are required - if r.Read == nil { - return fmt.Errorf("Read must be implemented") - } - if r.Delete == nil { - return fmt.Errorf("Delete must be implemented") - } - - // If we have an importer, we need to verify the importer. - if r.Importer != nil { - if err := r.Importer.InternalValidate(); err != nil { - return err - } - } - - for k, f := range tsm { - if isReservedResourceFieldName(k, f) { - return fmt.Errorf("%s is a reserved field name", k) - } - } - } - - lastVersion := -1 - for _, u := range r.StateUpgraders { - if lastVersion >= 0 && u.Version-lastVersion > 1 { - return fmt.Errorf("missing schema version between %d and %d", lastVersion, u.Version) - } - - if u.Version >= r.SchemaVersion { - return fmt.Errorf("StateUpgrader version %d is >= current version %d", u.Version, r.SchemaVersion) - } - - if !u.Type.IsObjectType() { - return fmt.Errorf("StateUpgrader %d type is not cty.Object", u.Version) - } - - if u.Upgrade == nil { - return fmt.Errorf("StateUpgrader %d missing StateUpgradeFunc", u.Version) - } - - lastVersion = u.Version - } - - if lastVersion >= 0 && lastVersion != r.SchemaVersion-1 { - return fmt.Errorf("missing StateUpgrader between %d and %d", lastVersion, r.SchemaVersion) - } - - // Data source - if r.isTopLevel() && !writable { - tsm = schemaMap(r.Schema) - for k, _ := range tsm { - if isReservedDataSourceFieldName(k) { - return fmt.Errorf("%s is a reserved field name", k) - } - } - } - - return schemaMap(r.Schema).InternalValidate(tsm) -} - -func isReservedDataSourceFieldName(name string) bool { - for _, reservedName := range ReservedDataSourceFields { - if name == reservedName { - return true - } - } - return false -} - -func isReservedResourceFieldName(name string, s *Schema) bool { - // Allow phasing out "id" - // See https://github.com/terraform-providers/terraform-provider-aws/pull/1626#issuecomment-328881415 - if name == "id" && (s.Deprecated != "" || s.Removed != "") { - return false - } - - for _, reservedName := range ReservedResourceFields { - if name == reservedName { - return true - } - } - return false -} - -// Data returns a ResourceData struct for this Resource. Each return value -// is a separate copy and can be safely modified differently. -// -// The data returned from this function has no actual affect on the Resource -// itself (including the state given to this function). -// -// This function is useful for unit tests and ResourceImporter functions. -func (r *Resource) Data(s *terraform.InstanceState) *ResourceData { - result, err := schemaMap(r.Schema).Data(s, nil) - if err != nil { - // At the time of writing, this isn't possible (Data never returns - // non-nil errors). We panic to find this in the future if we have to. - // I don't see a reason for Data to ever return an error. - panic(err) - } - - // load the Resource timeouts - result.timeouts = r.Timeouts - if result.timeouts == nil { - result.timeouts = &ResourceTimeout{} - } - - // Set the schema version to latest by default - result.meta = map[string]interface{}{ - "schema_version": strconv.Itoa(r.SchemaVersion), - } - - return result -} - -// TestResourceData Yields a ResourceData filled with this resource's schema for use in unit testing -// -// TODO: May be able to be removed with the above ResourceData function. -func (r *Resource) TestResourceData() *ResourceData { - return &ResourceData{ - schema: r.Schema, - } -} - -// SchemasForFlatmapPath tries its best to find a sequence of schemas that -// the given dot-delimited attribute path traverses through in the schema -// of the receiving Resource. -func (r *Resource) SchemasForFlatmapPath(path string) []*Schema { - return SchemasForFlatmapPath(path, r.Schema) -} - -// Returns true if the resource is "top level" i.e. not a sub-resource. -func (r *Resource) isTopLevel() bool { - // TODO: This is a heuristic; replace with a definitive attribute? - return (r.Create != nil || r.Read != nil) -} - -// Determines if a given InstanceState needs to be migrated by checking the -// stored version number with the current SchemaVersion -func (r *Resource) checkSchemaVersion(is *terraform.InstanceState) (bool, int) { - // Get the raw interface{} value for the schema version. If it doesn't - // exist or is nil then set it to zero. - raw := is.Meta["schema_version"] - if raw == nil { - raw = "0" - } - - // Try to convert it to a string. If it isn't a string then we pretend - // that it isn't set at all. It should never not be a string unless it - // was manually tampered with. - rawString, ok := raw.(string) - if !ok { - rawString = "0" - } - - stateSchemaVersion, _ := strconv.Atoi(rawString) - - // Don't run MigrateState if the version is handled by a StateUpgrader, - // since StateMigrateFuncs are not required to handle unknown versions - maxVersion := r.SchemaVersion - if len(r.StateUpgraders) > 0 { - maxVersion = r.StateUpgraders[0].Version - } - - return stateSchemaVersion < maxVersion, stateSchemaVersion -} - -func (r *Resource) recordCurrentSchemaVersion( - state *terraform.InstanceState) *terraform.InstanceState { - if state != nil && r.SchemaVersion > 0 { - if state.Meta == nil { - state.Meta = make(map[string]interface{}) - } - state.Meta["schema_version"] = strconv.Itoa(r.SchemaVersion) - } - return state -} - -// Noop is a convenience implementation of resource function which takes -// no action and returns no error. -func Noop(*ResourceData, interface{}) error { - return nil -} - -// RemoveFromState is a convenience implementation of a resource function -// which sets the resource ID to empty string (to remove it from state) -// and returns no error. -func RemoveFromState(d *ResourceData, _ interface{}) error { - d.SetId("") - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_data_test.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource_data_test.go deleted file mode 100644 index 5af0ff8e..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/resource_data_test.go +++ /dev/null @@ -1,3564 +0,0 @@ -package schema - -import ( - "fmt" - "math" - "os" - "reflect" - "testing" - "time" - - "github.com/hashicorp/terraform/terraform" -) - -func TestResourceDataGet(t *testing.T) { - cases := []struct { - Schema map[string]*Schema - State *terraform.InstanceState - Diff *terraform.InstanceDiff - Key string - Value interface{} - }{ - // #0 - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "foo", - New: "bar", - NewComputed: true, - }, - }, - }, - - Key: "availability_zone", - Value: "", - }, - - // #1 - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - RequiresNew: true, - }, - }, - }, - - Key: "availability_zone", - - Value: "foo", - }, - - // #2 - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo!", - NewExtra: "foo", - }, - }, - }, - - Key: "availability_zone", - Value: "foo", - }, - - // #3 - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "bar", - }, - }, - - Diff: nil, - - Key: "availability_zone", - - Value: "bar", - }, - - // #4 - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "foo", - New: "bar", - NewComputed: true, - }, - }, - }, - - Key: "availability_zone", - Value: "", - }, - - // #5 - { - Schema: map[string]*Schema{ - "port": &Schema{ - Type: TypeInt, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "port": "80", - }, - }, - - Diff: nil, - - Key: "port", - - Value: 80, - }, - - // #6 - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "3", - "ports.0": "1", - "ports.1": "2", - "ports.2": "5", - }, - }, - - Key: "ports.1", - - Value: 2, - }, - - // #7 - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "3", - "ports.0": "1", - "ports.1": "2", - "ports.2": "5", - }, - }, - - Key: "ports.#", - - Value: 3, - }, - - // #8 - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: nil, - - Key: "ports.#", - - Value: 0, - }, - - // #9 - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "3", - "ports.0": "1", - "ports.1": "2", - "ports.2": "5", - }, - }, - - Key: "ports", - - Value: []interface{}{1, 2, 5}, - }, - - // #10 - { - Schema: map[string]*Schema{ - "ingress": &Schema{ - Type: TypeList, - Required: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "from": &Schema{ - Type: TypeInt, - Required: true, - }, - }, - }, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ingress.#": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "ingress.0.from": &terraform.ResourceAttrDiff{ - Old: "", - New: "8080", - }, - }, - }, - - Key: "ingress.0", - - Value: map[string]interface{}{ - "from": 8080, - }, - }, - - // #11 - { - Schema: map[string]*Schema{ - "ingress": &Schema{ - Type: TypeList, - Required: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "from": &Schema{ - Type: TypeInt, - Required: true, - }, - }, - }, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ingress.#": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "ingress.0.from": &terraform.ResourceAttrDiff{ - Old: "", - New: "8080", - }, - }, - }, - - Key: "ingress", - - Value: []interface{}{ - map[string]interface{}{ - "from": 8080, - }, - }, - }, - - // #12 Computed get - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - - Key: "availability_zone", - - Value: "foo", - }, - - // #13 Full object - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - RequiresNew: true, - }, - }, - }, - - Key: "", - - Value: map[string]interface{}{ - "availability_zone": "foo", - }, - }, - - // #14 List of maps - { - Schema: map[string]*Schema{ - "config_vars": &Schema{ - Type: TypeList, - Optional: true, - Computed: true, - Elem: &Schema{ - Type: TypeMap, - }, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "config_vars.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "2", - }, - "config_vars.0.foo": &terraform.ResourceAttrDiff{ - Old: "", - New: "bar", - }, - "config_vars.1.bar": &terraform.ResourceAttrDiff{ - Old: "", - New: "baz", - }, - }, - }, - - Key: "config_vars", - - Value: []interface{}{ - map[string]interface{}{ - "foo": "bar", - }, - map[string]interface{}{ - "bar": "baz", - }, - }, - }, - - // #15 List of maps in state - { - Schema: map[string]*Schema{ - "config_vars": &Schema{ - Type: TypeList, - Optional: true, - Computed: true, - Elem: &Schema{ - Type: TypeMap, - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "config_vars.#": "2", - "config_vars.0.foo": "baz", - "config_vars.1.bar": "bar", - }, - }, - - Diff: nil, - - Key: "config_vars", - - Value: []interface{}{ - map[string]interface{}{ - "foo": "baz", - }, - map[string]interface{}{ - "bar": "bar", - }, - }, - }, - - // #16 List of maps with removal in diff - { - Schema: map[string]*Schema{ - "config_vars": &Schema{ - Type: TypeList, - Optional: true, - Computed: true, - Elem: &Schema{ - Type: TypeMap, - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "config_vars.#": "1", - "config_vars.0.FOO": "bar", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "config_vars.#": &terraform.ResourceAttrDiff{ - Old: "1", - New: "0", - }, - "config_vars.0.FOO": &terraform.ResourceAttrDiff{ - Old: "bar", - NewRemoved: true, - }, - }, - }, - - Key: "config_vars", - - Value: []interface{}{}, - }, - - // #17 Sets - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "1", - "ports.80": "80", - }, - }, - - Diff: nil, - - Key: "ports", - - Value: []interface{}{80}, - }, - - // #18 - { - Schema: map[string]*Schema{ - "data": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "index": &Schema{ - Type: TypeInt, - Required: true, - }, - - "value": &Schema{ - Type: TypeString, - Required: true, - }, - }, - }, - Set: func(a interface{}) int { - m := a.(map[string]interface{}) - return m["index"].(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "data.#": "1", - "data.10.index": "10", - "data.10.value": "50", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "data.10.value": &terraform.ResourceAttrDiff{ - Old: "50", - New: "80", - }, - }, - }, - - Key: "data", - - Value: []interface{}{ - map[string]interface{}{ - "index": 10, - "value": "80", - }, - }, - }, - - // #19 Empty Set - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: nil, - - Diff: nil, - - Key: "ports", - - Value: []interface{}{}, - }, - - // #20 Float zero - { - Schema: map[string]*Schema{ - "ratio": &Schema{ - Type: TypeFloat, - Optional: true, - Computed: true, - }, - }, - - State: nil, - - Diff: nil, - - Key: "ratio", - - Value: 0.0, - }, - - // #21 Float given - { - Schema: map[string]*Schema{ - "ratio": &Schema{ - Type: TypeFloat, - Optional: true, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ratio": "0.5", - }, - }, - - Diff: nil, - - Key: "ratio", - - Value: 0.5, - }, - - // #22 Float diff - { - Schema: map[string]*Schema{ - "ratio": &Schema{ - Type: TypeFloat, - Optional: true, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ratio": "-0.5", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ratio": &terraform.ResourceAttrDiff{ - Old: "-0.5", - New: "33.0", - }, - }, - }, - - Key: "ratio", - - Value: 33.0, - }, - - // #23 Sets with removed elements - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "1", - "ports.80": "80", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "2", - New: "1", - }, - "ports.80": &terraform.ResourceAttrDiff{ - Old: "80", - New: "80", - }, - "ports.8080": &terraform.ResourceAttrDiff{ - Old: "8080", - New: "0", - NewRemoved: true, - }, - }, - }, - - Key: "ports", - - Value: []interface{}{80}, - }, - } - - for i, tc := range cases { - d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) - if err != nil { - t.Fatalf("err: %s", err) - } - - v := d.Get(tc.Key) - if s, ok := v.(*Set); ok { - v = s.List() - } - - if !reflect.DeepEqual(v, tc.Value) { - t.Fatalf("Bad: %d\n\n%#v\n\nExpected: %#v", i, v, tc.Value) - } - } -} - -func TestResourceDataGetChange(t *testing.T) { - cases := []struct { - Schema map[string]*Schema - State *terraform.InstanceState - Diff *terraform.InstanceDiff - Key string - OldValue interface{} - NewValue interface{} - }{ - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - RequiresNew: true, - }, - }, - }, - - Key: "availability_zone", - - OldValue: "", - NewValue: "foo", - }, - - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - RequiresNew: true, - }, - }, - }, - - Key: "availability_zone", - - OldValue: "foo", - NewValue: "foo", - }, - } - - for i, tc := range cases { - d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) - if err != nil { - t.Fatalf("err: %s", err) - } - - o, n := d.GetChange(tc.Key) - if !reflect.DeepEqual(o, tc.OldValue) { - t.Fatalf("Old Bad: %d\n\n%#v", i, o) - } - if !reflect.DeepEqual(n, tc.NewValue) { - t.Fatalf("New Bad: %d\n\n%#v", i, n) - } - } -} - -func TestResourceDataGetOk(t *testing.T) { - cases := []struct { - Schema map[string]*Schema - State *terraform.InstanceState - Diff *terraform.InstanceDiff - Key string - Value interface{} - Ok bool - }{ - /* - * Primitives - */ - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "", - }, - }, - }, - - Key: "availability_zone", - Value: "", - Ok: false, - }, - - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "", - NewComputed: true, - }, - }, - }, - - Key: "availability_zone", - Value: "", - Ok: false, - }, - - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: nil, - - Key: "availability_zone", - Value: "", - Ok: false, - }, - - /* - * Lists - */ - - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Optional: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: nil, - - Diff: nil, - - Key: "ports", - Value: []interface{}{}, - Ok: false, - }, - - /* - * Map - */ - - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeMap, - Optional: true, - }, - }, - - State: nil, - - Diff: nil, - - Key: "ports", - Value: map[string]interface{}{}, - Ok: false, - }, - - /* - * Set - */ - - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { return a.(int) }, - }, - }, - - State: nil, - - Diff: nil, - - Key: "ports", - Value: []interface{}{}, - Ok: false, - }, - - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { return a.(int) }, - }, - }, - - State: nil, - - Diff: nil, - - Key: "ports.0", - Value: 0, - Ok: false, - }, - - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { return a.(int) }, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "0", - }, - }, - }, - - Key: "ports", - Value: []interface{}{}, - Ok: false, - }, - - // Further illustrates and clarifiies the GetOk semantics from #933, and - // highlights the limitation that zero-value config is currently - // indistinguishable from unset config. - { - Schema: map[string]*Schema{ - "from_port": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "from_port": &terraform.ResourceAttrDiff{ - Old: "", - New: "0", - }, - }, - }, - - Key: "from_port", - Value: 0, - Ok: false, - }, - } - - for i, tc := range cases { - d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) - if err != nil { - t.Fatalf("err: %s", err) - } - - v, ok := d.GetOk(tc.Key) - if s, ok := v.(*Set); ok { - v = s.List() - } - - if !reflect.DeepEqual(v, tc.Value) { - t.Fatalf("Bad: %d\n\n%#v", i, v) - } - if ok != tc.Ok { - t.Fatalf("%d: expected ok: %t, got: %t", i, tc.Ok, ok) - } - } -} - -func TestResourceDataGetOkExists(t *testing.T) { - cases := []struct { - Name string - Schema map[string]*Schema - State *terraform.InstanceState - Diff *terraform.InstanceDiff - Key string - Value interface{} - Ok bool - }{ - /* - * Primitives - */ - { - Name: "string-literal-empty", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": { - Old: "", - New: "", - }, - }, - }, - - Key: "availability_zone", - Value: "", - Ok: true, - }, - - { - Name: "string-computed-empty", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": { - Old: "", - New: "", - NewComputed: true, - }, - }, - }, - - Key: "availability_zone", - Value: "", - Ok: false, - }, - - { - Name: "string-optional-computed-nil-diff", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: nil, - - Key: "availability_zone", - Value: "", - Ok: false, - }, - - /* - * Lists - */ - - { - Name: "list-optional", - Schema: map[string]*Schema{ - "ports": { - Type: TypeList, - Optional: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: nil, - - Diff: nil, - - Key: "ports", - Value: []interface{}{}, - Ok: false, - }, - - /* - * Map - */ - - { - Name: "map-optional", - Schema: map[string]*Schema{ - "ports": { - Type: TypeMap, - Optional: true, - }, - }, - - State: nil, - - Diff: nil, - - Key: "ports", - Value: map[string]interface{}{}, - Ok: false, - }, - - /* - * Set - */ - - { - Name: "set-optional", - Schema: map[string]*Schema{ - "ports": { - Type: TypeSet, - Optional: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { return a.(int) }, - }, - }, - - State: nil, - - Diff: nil, - - Key: "ports", - Value: []interface{}{}, - Ok: false, - }, - - { - Name: "set-optional-key", - Schema: map[string]*Schema{ - "ports": { - Type: TypeSet, - Optional: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { return a.(int) }, - }, - }, - - State: nil, - - Diff: nil, - - Key: "ports.0", - Value: 0, - Ok: false, - }, - - { - Name: "bool-literal-empty", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeBool, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": { - Old: "", - New: "", - }, - }, - }, - - Key: "availability_zone", - Value: false, - Ok: true, - }, - - { - Name: "bool-literal-set", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeBool, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": { - New: "true", - }, - }, - }, - - Key: "availability_zone", - Value: true, - Ok: true, - }, - } - - for i, tc := range cases { - t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { - d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) - if err != nil { - t.Fatalf("%s err: %s", tc.Name, err) - } - - v, ok := d.GetOkExists(tc.Key) - if s, ok := v.(*Set); ok { - v = s.List() - } - - if !reflect.DeepEqual(v, tc.Value) { - t.Fatalf("Bad %s: \n%#v", tc.Name, v) - } - if ok != tc.Ok { - t.Fatalf("%s: expected ok: %t, got: %t", tc.Name, tc.Ok, ok) - } - }) - } -} - -func TestResourceDataTimeout(t *testing.T) { - cases := []struct { - Name string - Rd *ResourceData - Expected *ResourceTimeout - }{ - { - Name: "Basic example default", - Rd: &ResourceData{timeouts: timeoutForValues(10, 3, 0, 15, 0)}, - Expected: expectedTimeoutForValues(10, 3, 0, 15, 0), - }, - { - Name: "Resource and config match update, create", - Rd: &ResourceData{timeouts: timeoutForValues(10, 0, 3, 0, 0)}, - Expected: expectedTimeoutForValues(10, 0, 3, 0, 0), - }, - { - Name: "Resource provides default", - Rd: &ResourceData{timeouts: timeoutForValues(10, 0, 0, 0, 7)}, - Expected: expectedTimeoutForValues(10, 7, 7, 7, 7), - }, - { - Name: "Resource provides default and delete", - Rd: &ResourceData{timeouts: timeoutForValues(10, 0, 0, 15, 7)}, - Expected: expectedTimeoutForValues(10, 7, 7, 15, 7), - }, - { - Name: "Resource provides default, config overwrites other values", - Rd: &ResourceData{timeouts: timeoutForValues(10, 3, 0, 0, 13)}, - Expected: expectedTimeoutForValues(10, 3, 13, 13, 13), - }, - { - Name: "Resource has no config", - Rd: &ResourceData{}, - Expected: expectedTimeoutForValues(0, 0, 0, 0, 0), - }, - } - - keys := timeoutKeys() - for i, c := range cases { - t.Run(fmt.Sprintf("%d-%s", i, c.Name), func(t *testing.T) { - - for _, k := range keys { - got := c.Rd.Timeout(k) - var ex *time.Duration - switch k { - case TimeoutCreate: - ex = c.Expected.Create - case TimeoutRead: - ex = c.Expected.Read - case TimeoutUpdate: - ex = c.Expected.Update - case TimeoutDelete: - ex = c.Expected.Delete - case TimeoutDefault: - ex = c.Expected.Default - } - - if got > 0 && ex == nil { - t.Fatalf("Unexpected value in (%s), case %d check 1:\n\texpected: %#v\n\tgot: %#v", k, i, ex, got) - } - if got == 0 && ex != nil { - t.Fatalf("Unexpected value in (%s), case %d check 2:\n\texpected: %#v\n\tgot: %#v", k, i, *ex, got) - } - - // confirm values - if ex != nil { - if got != *ex { - t.Fatalf("Timeout %s case (%d) expected (%s), got (%s)", k, i, *ex, got) - } - } - } - - }) - } -} - -func TestResourceDataHasChange(t *testing.T) { - cases := []struct { - Schema map[string]*Schema - State *terraform.InstanceState - Diff *terraform.InstanceDiff - Key string - Change bool - }{ - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - RequiresNew: true, - }, - }, - }, - - Key: "availability_zone", - - Change: true, - }, - - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - RequiresNew: true, - }, - }, - }, - - Key: "availability_zone", - - Change: false, - }, - - { - Schema: map[string]*Schema{ - "tags": &Schema{ - Type: TypeMap, - Optional: true, - Computed: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "tags.Name": &terraform.ResourceAttrDiff{ - Old: "foo", - New: "foo", - }, - }, - }, - - Key: "tags", - - Change: true, - }, - - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { return a.(int) }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "1", - "ports.80": "80", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "1", - New: "0", - }, - }, - }, - - Key: "ports", - - Change: true, - }, - - // https://github.com/hashicorp/terraform/issues/927 - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { return a.(int) }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "1", - "ports.80": "80", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "tags.foo": &terraform.ResourceAttrDiff{ - Old: "", - New: "bar", - }, - }, - }, - - Key: "ports", - - Change: false, - }, - } - - for i, tc := range cases { - d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) - if err != nil { - t.Fatalf("err: %s", err) - } - - actual := d.HasChange(tc.Key) - if actual != tc.Change { - t.Fatalf("Bad: %d %#v", i, actual) - } - } -} - -func TestResourceDataSet(t *testing.T) { - var testNilPtr *string - - cases := []struct { - Schema map[string]*Schema - State *terraform.InstanceState - Diff *terraform.InstanceDiff - Key string - Value interface{} - Err bool - GetKey string - GetValue interface{} - - // GetPreProcess can be set to munge the return value before being - // compared to GetValue - GetPreProcess func(interface{}) interface{} - }{ - // #0: Basic good - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: nil, - - Key: "availability_zone", - Value: "foo", - - GetKey: "availability_zone", - GetValue: "foo", - }, - - // #1: Basic int - { - Schema: map[string]*Schema{ - "port": &Schema{ - Type: TypeInt, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: nil, - - Key: "port", - Value: 80, - - GetKey: "port", - GetValue: 80, - }, - - // #2: Basic bool - { - Schema: map[string]*Schema{ - "vpc": &Schema{ - Type: TypeBool, - Optional: true, - }, - }, - - State: nil, - - Diff: nil, - - Key: "vpc", - Value: true, - - GetKey: "vpc", - GetValue: true, - }, - - // #3 - { - Schema: map[string]*Schema{ - "vpc": &Schema{ - Type: TypeBool, - Optional: true, - }, - }, - - State: nil, - - Diff: nil, - - Key: "vpc", - Value: false, - - GetKey: "vpc", - GetValue: false, - }, - - // #4: Invalid type - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: nil, - - Key: "availability_zone", - Value: 80, - Err: true, - - GetKey: "availability_zone", - GetValue: "", - }, - - // #5: List of primitives, set list - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Computed: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: nil, - - Diff: nil, - - Key: "ports", - Value: []int{1, 2, 5}, - - GetKey: "ports", - GetValue: []interface{}{1, 2, 5}, - }, - - // #6: List of primitives, set list with error - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Computed: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: nil, - - Diff: nil, - - Key: "ports", - Value: []interface{}{1, "NOPE", 5}, - Err: true, - - GetKey: "ports", - GetValue: []interface{}{}, - }, - - // #7: Set a list of maps - { - Schema: map[string]*Schema{ - "config_vars": &Schema{ - Type: TypeList, - Optional: true, - Computed: true, - Elem: &Schema{ - Type: TypeMap, - }, - }, - }, - - State: nil, - - Diff: nil, - - Key: "config_vars", - Value: []interface{}{ - map[string]interface{}{ - "foo": "bar", - }, - map[string]interface{}{ - "bar": "baz", - }, - }, - Err: false, - - GetKey: "config_vars", - GetValue: []interface{}{ - map[string]interface{}{ - "foo": "bar", - }, - map[string]interface{}{ - "bar": "baz", - }, - }, - }, - - // #8: Set, with list - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "3", - "ports.0": "100", - "ports.1": "80", - "ports.2": "80", - }, - }, - - Key: "ports", - Value: []interface{}{100, 125, 125}, - - GetKey: "ports", - GetValue: []interface{}{100, 125}, - }, - - // #9: Set, with Set - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "3", - "ports.100": "100", - "ports.80": "80", - "ports.81": "81", - }, - }, - - Key: "ports", - Value: &Set{ - m: map[string]interface{}{ - "1": 1, - "2": 2, - }, - }, - - GetKey: "ports", - GetValue: []interface{}{1, 2}, - }, - - // #10: Set single item - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "2", - "ports.100": "100", - "ports.80": "80", - }, - }, - - Key: "ports.100", - Value: 256, - Err: true, - - GetKey: "ports", - GetValue: []interface{}{100, 80}, - }, - - // #11: Set with nested set - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Elem: &Resource{ - Schema: map[string]*Schema{ - "port": &Schema{ - Type: TypeInt, - }, - - "set": &Schema{ - Type: TypeSet, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - }, - Set: func(a interface{}) int { - return a.(map[string]interface{})["port"].(int) - }, - }, - }, - - State: nil, - - Key: "ports", - Value: []interface{}{ - map[string]interface{}{ - "port": 80, - }, - }, - - GetKey: "ports", - GetValue: []interface{}{ - map[string]interface{}{ - "port": 80, - "set": []interface{}{}, - }, - }, - - GetPreProcess: func(v interface{}) interface{} { - if v == nil { - return v - } - s, ok := v.([]interface{}) - if !ok { - return v - } - for _, v := range s { - m, ok := v.(map[string]interface{}) - if !ok { - continue - } - if m["set"] == nil { - continue - } - if s, ok := m["set"].(*Set); ok { - m["set"] = s.List() - } - } - - return v - }, - }, - - // #12: List of floats, set list - { - Schema: map[string]*Schema{ - "ratios": &Schema{ - Type: TypeList, - Computed: true, - Elem: &Schema{Type: TypeFloat}, - }, - }, - - State: nil, - - Diff: nil, - - Key: "ratios", - Value: []float64{1.0, 2.2, 5.5}, - - GetKey: "ratios", - GetValue: []interface{}{1.0, 2.2, 5.5}, - }, - - // #12: Set of floats, set list - { - Schema: map[string]*Schema{ - "ratios": &Schema{ - Type: TypeSet, - Computed: true, - Elem: &Schema{Type: TypeFloat}, - Set: func(a interface{}) int { - return int(math.Float64bits(a.(float64))) - }, - }, - }, - - State: nil, - - Diff: nil, - - Key: "ratios", - Value: []float64{1.0, 2.2, 5.5}, - - GetKey: "ratios", - GetValue: []interface{}{1.0, 2.2, 5.5}, - }, - - // #13: Basic pointer - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: nil, - - Key: "availability_zone", - Value: testPtrTo("foo"), - - GetKey: "availability_zone", - GetValue: "foo", - }, - - // #14: Basic nil value - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: nil, - - Key: "availability_zone", - Value: testPtrTo(nil), - - GetKey: "availability_zone", - GetValue: "", - }, - - // #15: Basic nil pointer - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: nil, - - Key: "availability_zone", - Value: testNilPtr, - - GetKey: "availability_zone", - GetValue: "", - }, - - // #16: Set in a list - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Elem: &Resource{ - Schema: map[string]*Schema{ - "set": &Schema{ - Type: TypeSet, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - }, - }, - }, - - State: nil, - - Key: "ports", - Value: []interface{}{ - map[string]interface{}{ - "set": []interface{}{ - 1, - }, - }, - }, - - GetKey: "ports", - GetValue: []interface{}{ - map[string]interface{}{ - "set": []interface{}{ - 1, - }, - }, - }, - GetPreProcess: func(v interface{}) interface{} { - if v == nil { - return v - } - s, ok := v.([]interface{}) - if !ok { - return v - } - for _, v := range s { - m, ok := v.(map[string]interface{}) - if !ok { - continue - } - if m["set"] == nil { - continue - } - if s, ok := m["set"].(*Set); ok { - m["set"] = s.List() - } - } - - return v - }, - }, - } - - oldEnv := os.Getenv(PanicOnErr) - os.Setenv(PanicOnErr, "") - defer os.Setenv(PanicOnErr, oldEnv) - - for i, tc := range cases { - d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) - if err != nil { - t.Fatalf("err: %s", err) - } - - err = d.Set(tc.Key, tc.Value) - if err != nil != tc.Err { - t.Fatalf("%d err: %s", i, err) - } - - v := d.Get(tc.GetKey) - if s, ok := v.(*Set); ok { - v = s.List() - } - - if tc.GetPreProcess != nil { - v = tc.GetPreProcess(v) - } - - if !reflect.DeepEqual(v, tc.GetValue) { - t.Fatalf("Get Bad: %d\n\n%#v", i, v) - } - } -} - -func TestResourceDataState_dynamicAttributes(t *testing.T) { - cases := []struct { - Schema map[string]*Schema - State *terraform.InstanceState - Diff *terraform.InstanceDiff - Set map[string]interface{} - UnsafeSet map[string]string - Result *terraform.InstanceState - }{ - { - Schema: map[string]*Schema{ - "__has_dynamic_attributes": { - Type: TypeString, - Optional: true, - }, - - "schema_field": { - Type: TypeString, - Required: true, - }, - }, - - State: nil, - - Diff: nil, - - Set: map[string]interface{}{ - "schema_field": "present", - }, - - UnsafeSet: map[string]string{ - "test1": "value", - "test2": "value", - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "schema_field": "present", - "test1": "value", - "test2": "value", - }, - }, - }, - } - - for i, tc := range cases { - d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) - if err != nil { - t.Fatalf("err: %s", err) - } - - for k, v := range tc.Set { - d.Set(k, v) - } - - for k, v := range tc.UnsafeSet { - d.UnsafeSetFieldRaw(k, v) - } - - // Set an ID so that the state returned is not nil - idSet := false - if d.Id() == "" { - idSet = true - d.SetId("foo") - } - - actual := d.State() - - // If we set an ID, then undo what we did so the comparison works - if actual != nil && idSet { - actual.ID = "" - delete(actual.Attributes, "id") - } - - if !reflect.DeepEqual(actual, tc.Result) { - t.Fatalf("Bad: %d\n\n%#v\n\nExpected:\n\n%#v", i, actual, tc.Result) - } - } -} - -func TestResourceDataState_schema(t *testing.T) { - cases := []struct { - Schema map[string]*Schema - State *terraform.InstanceState - Diff *terraform.InstanceDiff - Set map[string]interface{} - Result *terraform.InstanceState - Partial []string - }{ - // #0 Basic primitive in diff - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - RequiresNew: true, - }, - }, - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - }, - - // #1 Basic primitive set override - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - RequiresNew: true, - }, - }, - }, - - Set: map[string]interface{}{ - "availability_zone": "bar", - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "bar", - }, - }, - }, - - // #2 - { - Schema: map[string]*Schema{ - "vpc": &Schema{ - Type: TypeBool, - Optional: true, - }, - }, - - State: nil, - - Diff: nil, - - Set: map[string]interface{}{ - "vpc": true, - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "vpc": "true", - }, - }, - }, - - // #3 Basic primitive with StateFunc set - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - StateFunc: func(interface{}) string { return "" }, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - NewExtra: "foo!", - }, - }, - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - }, - - // #4 List - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "1", - "ports.0": "80", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "1", - New: "2", - }, - "ports.1": &terraform.ResourceAttrDiff{ - Old: "", - New: "100", - }, - }, - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "2", - "ports.0": "80", - "ports.1": "100", - }, - }, - }, - - // #5 List of resources - { - Schema: map[string]*Schema{ - "ingress": &Schema{ - Type: TypeList, - Required: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "from": &Schema{ - Type: TypeInt, - Required: true, - }, - }, - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ingress.#": "1", - "ingress.0.from": "80", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ingress.#": &terraform.ResourceAttrDiff{ - Old: "1", - New: "2", - }, - "ingress.0.from": &terraform.ResourceAttrDiff{ - Old: "80", - New: "150", - }, - "ingress.1.from": &terraform.ResourceAttrDiff{ - Old: "", - New: "100", - }, - }, - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "ingress.#": "2", - "ingress.0.from": "150", - "ingress.1.from": "100", - }, - }, - }, - - // #6 List of maps - { - Schema: map[string]*Schema{ - "config_vars": &Schema{ - Type: TypeList, - Optional: true, - Computed: true, - Elem: &Schema{ - Type: TypeMap, - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "config_vars.#": "2", - "config_vars.0.%": "2", - "config_vars.0.foo": "bar", - "config_vars.0.bar": "bar", - "config_vars.1.%": "1", - "config_vars.1.bar": "baz", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "config_vars.0.bar": &terraform.ResourceAttrDiff{ - NewRemoved: true, - }, - }, - }, - - Set: map[string]interface{}{ - "config_vars": []map[string]interface{}{ - map[string]interface{}{ - "foo": "bar", - }, - map[string]interface{}{ - "baz": "bang", - }, - }, - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "config_vars.#": "2", - "config_vars.0.%": "1", - "config_vars.0.foo": "bar", - "config_vars.1.%": "1", - "config_vars.1.baz": "bang", - }, - }, - }, - - // #7 List of maps with removal in diff - { - Schema: map[string]*Schema{ - "config_vars": &Schema{ - Type: TypeList, - Optional: true, - Computed: true, - Elem: &Schema{ - Type: TypeMap, - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "config_vars.#": "1", - "config_vars.0.FOO": "bar", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "config_vars.#": &terraform.ResourceAttrDiff{ - Old: "1", - New: "0", - }, - "config_vars.0.FOO": &terraform.ResourceAttrDiff{ - Old: "bar", - NewRemoved: true, - }, - }, - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "config_vars.#": "0", - }, - }, - }, - - // #8 Basic state with other keys - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "id": "bar", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - RequiresNew: true, - }, - }, - }, - - Result: &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "id": "bar", - "availability_zone": "foo", - }, - }, - }, - - // #9 Sets - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "3", - "ports.100": "100", - "ports.80": "80", - "ports.81": "81", - }, - }, - - Diff: nil, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "3", - "ports.80": "80", - "ports.81": "81", - "ports.100": "100", - }, - }, - }, - - // #10 - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: nil, - - Diff: nil, - - Set: map[string]interface{}{ - "ports": []interface{}{100, 80}, - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "2", - "ports.80": "80", - "ports.100": "100", - }, - }, - }, - - // #11 - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "order": &Schema{ - Type: TypeInt, - }, - - "a": &Schema{ - Type: TypeList, - Elem: &Schema{Type: TypeInt}, - }, - - "b": &Schema{ - Type: TypeList, - Elem: &Schema{Type: TypeInt}, - }, - }, - }, - Set: func(a interface{}) int { - m := a.(map[string]interface{}) - return m["order"].(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "2", - "ports.10.order": "10", - "ports.10.a.#": "1", - "ports.10.a.0": "80", - "ports.20.order": "20", - "ports.20.b.#": "1", - "ports.20.b.0": "100", - }, - }, - - Set: map[string]interface{}{ - "ports": []interface{}{ - map[string]interface{}{ - "order": 20, - "b": []interface{}{100}, - }, - map[string]interface{}{ - "order": 10, - "a": []interface{}{80}, - }, - }, - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "2", - "ports.10.order": "10", - "ports.10.a.#": "1", - "ports.10.a.0": "80", - "ports.10.b.#": "0", - "ports.20.order": "20", - "ports.20.a.#": "0", - "ports.20.b.#": "1", - "ports.20.b.0": "100", - }, - }, - }, - - /* - * PARTIAL STATES - */ - - // #12 Basic primitive - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - RequiresNew: true, - }, - }, - }, - - Partial: []string{}, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{}, - }, - }, - - // #13 List - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "1", - "ports.0": "80", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "1", - New: "2", - }, - "ports.1": &terraform.ResourceAttrDiff{ - Old: "", - New: "100", - }, - }, - }, - - Partial: []string{}, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "1", - "ports.0": "80", - }, - }, - }, - - // #14 - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "", - NewComputed: true, - }, - }, - }, - - Partial: []string{}, - - Set: map[string]interface{}{ - "ports": []interface{}{}, - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{}, - }, - }, - - // #15 List of resources - { - Schema: map[string]*Schema{ - "ingress": &Schema{ - Type: TypeList, - Required: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "from": &Schema{ - Type: TypeInt, - Required: true, - }, - }, - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ingress.#": "1", - "ingress.0.from": "80", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ingress.#": &terraform.ResourceAttrDiff{ - Old: "1", - New: "2", - }, - "ingress.0.from": &terraform.ResourceAttrDiff{ - Old: "80", - New: "150", - }, - "ingress.1.from": &terraform.ResourceAttrDiff{ - Old: "", - New: "100", - }, - }, - }, - - Partial: []string{}, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "ingress.#": "1", - "ingress.0.from": "80", - }, - }, - }, - - // #16 List of maps - { - Schema: map[string]*Schema{ - "config_vars": &Schema{ - Type: TypeList, - Optional: true, - Computed: true, - Elem: &Schema{ - Type: TypeMap, - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "config_vars.#": "2", - "config_vars.0.foo": "bar", - "config_vars.0.bar": "bar", - "config_vars.1.bar": "baz", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "config_vars.0.bar": &terraform.ResourceAttrDiff{ - NewRemoved: true, - }, - }, - }, - - Set: map[string]interface{}{ - "config_vars": []map[string]interface{}{ - map[string]interface{}{ - "foo": "bar", - }, - map[string]interface{}{ - "baz": "bang", - }, - }, - }, - - Partial: []string{}, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - // TODO: broken, shouldn't bar be removed? - "config_vars.#": "2", - "config_vars.0.%": "2", - "config_vars.0.foo": "bar", - "config_vars.0.bar": "bar", - "config_vars.1.%": "1", - "config_vars.1.bar": "baz", - }, - }, - }, - - // #17 Sets - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "3", - "ports.100": "100", - "ports.80": "80", - "ports.81": "81", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.120": &terraform.ResourceAttrDiff{ - New: "120", - }, - }, - }, - - Partial: []string{}, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "3", - "ports.80": "80", - "ports.81": "81", - "ports.100": "100", - }, - }, - }, - - // #18 - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "", - NewComputed: true, - }, - }, - }, - - Partial: []string{}, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{}, - }, - }, - - // #19 Maps - { - Schema: map[string]*Schema{ - "tags": &Schema{ - Type: TypeMap, - Optional: true, - Computed: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "tags.Name": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - }, - }, - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "tags.%": "1", - "tags.Name": "foo", - }, - }, - }, - - // #20 empty computed map - { - Schema: map[string]*Schema{ - "tags": &Schema{ - Type: TypeMap, - Optional: true, - Computed: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "tags.Name": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - }, - }, - }, - - Set: map[string]interface{}{ - "tags": map[string]string{}, - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "tags.%": "0", - }, - }, - }, - - // #21 - { - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - NewComputed: true, - }, - }, - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{}, - }, - }, - - // #22 - { - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - NewComputed: true, - }, - }, - }, - - Set: map[string]interface{}{ - "foo": "bar", - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "bar", - }, - }, - }, - - // #23 Set of maps - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "index": &Schema{Type: TypeInt}, - "uuids": &Schema{Type: TypeMap}, - }, - }, - Set: func(a interface{}) int { - m := a.(map[string]interface{}) - return m["index"].(int) - }, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.10.uuids.#": &terraform.ResourceAttrDiff{ - NewComputed: true, - }, - }, - }, - - Set: map[string]interface{}{ - "ports": []interface{}{ - map[string]interface{}{ - "index": 10, - "uuids": map[string]interface{}{ - "80": "value", - }, - }, - }, - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "1", - "ports.10.index": "10", - "ports.10.uuids.%": "1", - "ports.10.uuids.80": "value", - }, - }, - }, - - // #24 - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "3", - "ports.100": "100", - "ports.80": "80", - "ports.81": "81", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "3", - New: "0", - }, - }, - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "0", - }, - }, - }, - - // #25 - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: nil, - - Diff: nil, - - Set: map[string]interface{}{ - "ports": []interface{}{}, - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "0", - }, - }, - }, - - // #26 - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: nil, - - Diff: nil, - - Set: map[string]interface{}{ - "ports": []interface{}{}, - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "0", - }, - }, - }, - - // #27 Set lists - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Optional: true, - Computed: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "index": &Schema{Type: TypeInt}, - "uuids": &Schema{Type: TypeMap}, - }, - }, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - NewComputed: true, - }, - }, - }, - - Set: map[string]interface{}{ - "ports": []interface{}{ - map[string]interface{}{ - "index": 10, - "uuids": map[string]interface{}{ - "80": "value", - }, - }, - }, - }, - - Result: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "1", - "ports.0.index": "10", - "ports.0.uuids.%": "1", - "ports.0.uuids.80": "value", - }, - }, - }, - } - - for i, tc := range cases { - d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) - if err != nil { - t.Fatalf("err: %s", err) - } - - for k, v := range tc.Set { - if err := d.Set(k, v); err != nil { - t.Fatalf("%d err: %s", i, err) - } - } - - // Set an ID so that the state returned is not nil - idSet := false - if d.Id() == "" { - idSet = true - d.SetId("foo") - } - - // If we have partial, then enable partial state mode. - if tc.Partial != nil { - d.Partial(true) - for _, k := range tc.Partial { - d.SetPartial(k) - } - } - - actual := d.State() - - // If we set an ID, then undo what we did so the comparison works - if actual != nil && idSet { - actual.ID = "" - delete(actual.Attributes, "id") - } - - if !reflect.DeepEqual(actual, tc.Result) { - t.Fatalf("Bad: %d\n\n%#v\n\nExpected:\n\n%#v", i, actual, tc.Result) - } - } -} - -func TestResourceData_nonStringValuesInMap(t *testing.T) { - cases := []struct { - Schema map[string]*Schema - Diff *terraform.InstanceDiff - MapFieldName string - ItemName string - ExpectedType string - }{ - { - Schema: map[string]*Schema{ - "boolMap": &Schema{ - Type: TypeMap, - Elem: TypeBool, - Optional: true, - }, - }, - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "boolMap.%": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "boolMap.boolField": &terraform.ResourceAttrDiff{ - Old: "", - New: "true", - }, - }, - }, - MapFieldName: "boolMap", - ItemName: "boolField", - ExpectedType: "bool", - }, - { - Schema: map[string]*Schema{ - "intMap": &Schema{ - Type: TypeMap, - Elem: TypeInt, - Optional: true, - }, - }, - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "intMap.%": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "intMap.intField": &terraform.ResourceAttrDiff{ - Old: "", - New: "8", - }, - }, - }, - MapFieldName: "intMap", - ItemName: "intField", - ExpectedType: "int", - }, - { - Schema: map[string]*Schema{ - "floatMap": &Schema{ - Type: TypeMap, - Elem: TypeFloat, - Optional: true, - }, - }, - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "floatMap.%": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "floatMap.floatField": &terraform.ResourceAttrDiff{ - Old: "", - New: "8.22", - }, - }, - }, - MapFieldName: "floatMap", - ItemName: "floatField", - ExpectedType: "float64", - }, - } - - for _, c := range cases { - d, err := schemaMap(c.Schema).Data(nil, c.Diff) - if err != nil { - t.Fatalf("err: %s", err) - } - - m, ok := d.Get(c.MapFieldName).(map[string]interface{}) - if !ok { - t.Fatalf("expected %q to be castable to a map", c.MapFieldName) - } - field, ok := m[c.ItemName] - if !ok { - t.Fatalf("expected %q in the map", c.ItemName) - } - - typeName := reflect.TypeOf(field).Name() - if typeName != c.ExpectedType { - t.Fatalf("expected %q to be %q, it is %q.", - c.ItemName, c.ExpectedType, typeName) - } - } -} - -func TestResourceDataSetConnInfo(t *testing.T) { - d := &ResourceData{} - d.SetId("foo") - d.SetConnInfo(map[string]string{ - "foo": "bar", - }) - - expected := map[string]string{ - "foo": "bar", - } - - actual := d.State() - if !reflect.DeepEqual(actual.Ephemeral.ConnInfo, expected) { - t.Fatalf("bad: %#v", actual) - } -} - -func TestResourceDataSetMeta_Timeouts(t *testing.T) { - d := &ResourceData{} - d.SetId("foo") - - rt := ResourceTimeout{ - Create: DefaultTimeout(7 * time.Minute), - } - - d.timeouts = &rt - - expected := expectedForValues(7, 0, 0, 0, 0) - - actual := d.State() - if !reflect.DeepEqual(actual.Meta[TimeoutKey], expected) { - t.Fatalf("Bad Meta_timeout match:\n\texpected: %#v\n\tgot: %#v", expected, actual.Meta[TimeoutKey]) - } -} - -func TestResourceDataSetId(t *testing.T) { - d := &ResourceData{ - state: &terraform.InstanceState{ - ID: "test", - Attributes: map[string]string{ - "id": "test", - }, - }, - } - d.SetId("foo") - - actual := d.State() - - // SetId should set both the ID field as well as the attribute, to aid in - // transitioning to the new type system. - if actual.ID != "foo" || actual.Attributes["id"] != "foo" { - t.Fatalf("bad: %#v", actual) - } - - d.SetId("") - actual = d.State() - if actual != nil { - t.Fatalf("bad: %#v", actual) - } -} - -func TestResourceDataSetId_clear(t *testing.T) { - d := &ResourceData{ - state: &terraform.InstanceState{ID: "bar"}, - } - d.SetId("") - - actual := d.State() - if actual != nil { - t.Fatalf("bad: %#v", actual) - } -} - -func TestResourceDataSetId_override(t *testing.T) { - d := &ResourceData{ - state: &terraform.InstanceState{ID: "bar"}, - } - d.SetId("foo") - - actual := d.State() - if actual.ID != "foo" { - t.Fatalf("bad: %#v", actual) - } -} - -func TestResourceDataSetType(t *testing.T) { - d := &ResourceData{} - d.SetId("foo") - d.SetType("bar") - - actual := d.State() - if v := actual.Ephemeral.Type; v != "bar" { - t.Fatalf("bad: %#v", actual) - } -} - -func testPtrTo(raw interface{}) interface{} { - return &raw -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_diff_test.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource_diff_test.go deleted file mode 100644 index e6897b73..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/resource_diff_test.go +++ /dev/null @@ -1,2045 +0,0 @@ -package schema - -import ( - "fmt" - "reflect" - "sort" - "testing" - - "github.com/davecgh/go-spew/spew" - "github.com/hashicorp/terraform/configs/hcl2shim" - "github.com/hashicorp/terraform/terraform" -) - -// testSetFunc is a very simple function we use to test a foo/bar complex set. -// Both "foo" and "bar" are int values. -// -// This is not foolproof as since it performs sums, you can run into -// collisions. Spec tests accordingly. :P -func testSetFunc(v interface{}) int { - m := v.(map[string]interface{}) - return m["foo"].(int) + m["bar"].(int) -} - -// resourceDiffTestCase provides a test case struct for SetNew and SetDiff. -type resourceDiffTestCase struct { - Name string - Schema map[string]*Schema - State *terraform.InstanceState - Config *terraform.ResourceConfig - Diff *terraform.InstanceDiff - Key string - OldValue interface{} - NewValue interface{} - Expected *terraform.InstanceDiff - ExpectedKeys []string - ExpectedError bool -} - -// testDiffCases produces a list of test cases for use with SetNew and SetDiff. -func testDiffCases(t *testing.T, oldPrefix string, oldOffset int, computed bool) []resourceDiffTestCase { - return []resourceDiffTestCase{ - resourceDiffTestCase{ - Name: "basic primitive diff", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "bar", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": "baz", - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "baz", - }, - }, - }, - Key: "foo", - NewValue: "qux", - Expected: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: func() string { - if computed { - return "" - } - return "qux" - }(), - NewComputed: computed, - }, - }, - }, - }, - resourceDiffTestCase{ - Name: "basic set diff", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeString}, - Set: HashString, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo.#": "1", - "foo.1996459178": "bar", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": []interface{}{"baz"}, - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo.1996459178": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "", - NewRemoved: true, - }, - "foo.2015626392": &terraform.ResourceAttrDiff{ - Old: "", - New: "baz", - }, - }, - }, - Key: "foo", - NewValue: []interface{}{"qux"}, - Expected: &terraform.InstanceDiff{ - Attributes: func() map[string]*terraform.ResourceAttrDiff { - result := map[string]*terraform.ResourceAttrDiff{} - if computed { - result["foo.#"] = &terraform.ResourceAttrDiff{ - Old: "1", - New: "", - NewComputed: true, - } - } else { - result["foo.2800005064"] = &terraform.ResourceAttrDiff{ - Old: "", - New: "qux", - } - result["foo.1996459178"] = &terraform.ResourceAttrDiff{ - Old: "bar", - New: "", - NewRemoved: true, - } - } - return result - }(), - }, - }, - resourceDiffTestCase{ - Name: "basic list diff", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeList, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeString}, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo.#": "1", - "foo.0": "bar", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": []interface{}{"baz"}, - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo.0": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "baz", - }, - }, - }, - Key: "foo", - NewValue: []interface{}{"qux"}, - Expected: &terraform.InstanceDiff{ - Attributes: func() map[string]*terraform.ResourceAttrDiff { - result := make(map[string]*terraform.ResourceAttrDiff) - if computed { - result["foo.#"] = &terraform.ResourceAttrDiff{ - Old: "1", - New: "", - NewComputed: true, - } - } else { - result["foo.0"] = &terraform.ResourceAttrDiff{ - Old: "bar", - New: "qux", - } - } - return result - }(), - }, - }, - resourceDiffTestCase{ - Name: "basic map diff", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeMap, - Optional: true, - Computed: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo.%": "1", - "foo.bar": "baz", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": map[string]interface{}{"bar": "qux"}, - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo.bar": &terraform.ResourceAttrDiff{ - Old: "baz", - New: "qux", - }, - }, - }, - Key: "foo", - NewValue: map[string]interface{}{"bar": "quux"}, - Expected: &terraform.InstanceDiff{ - Attributes: func() map[string]*terraform.ResourceAttrDiff { - result := make(map[string]*terraform.ResourceAttrDiff) - if computed { - result["foo.%"] = &terraform.ResourceAttrDiff{ - Old: "", - New: "", - NewComputed: true, - } - result["foo.bar"] = &terraform.ResourceAttrDiff{ - Old: "baz", - New: "", - NewRemoved: true, - } - } else { - result["foo.bar"] = &terraform.ResourceAttrDiff{ - Old: "baz", - New: "quux", - } - } - return result - }(), - }, - }, - resourceDiffTestCase{ - Name: "additional diff with primitive", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Optional: true, - }, - "one": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "bar", - "one": "two", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": "baz", - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "baz", - }, - }, - }, - Key: "one", - NewValue: "four", - Expected: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "baz", - }, - "one": &terraform.ResourceAttrDiff{ - Old: "two", - New: func() string { - if computed { - return "" - } - return "four" - }(), - NewComputed: computed, - }, - }, - }, - }, - resourceDiffTestCase{ - Name: "additional diff with primitive computed only", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Optional: true, - }, - "one": &Schema{ - Type: TypeString, - Computed: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "bar", - "one": "two", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": "baz", - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "baz", - }, - }, - }, - Key: "one", - NewValue: "three", - Expected: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "baz", - }, - "one": &terraform.ResourceAttrDiff{ - Old: "two", - New: func() string { - if computed { - return "" - } - return "three" - }(), - NewComputed: computed, - }, - }, - }, - }, - resourceDiffTestCase{ - Name: "complex-ish set diff", - Schema: map[string]*Schema{ - "top": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - Computed: true, - }, - "bar": &Schema{ - Type: TypeInt, - Optional: true, - Computed: true, - }, - }, - }, - Set: testSetFunc, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "top.#": "2", - "top.3.foo": "1", - "top.3.bar": "2", - "top.23.foo": "11", - "top.23.bar": "12", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "top": []interface{}{ - map[string]interface{}{ - "foo": 1, - "bar": 3, - }, - map[string]interface{}{ - "foo": 12, - "bar": 12, - }, - }, - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "top.4.foo": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "top.4.bar": &terraform.ResourceAttrDiff{ - Old: "", - New: "3", - }, - "top.24.foo": &terraform.ResourceAttrDiff{ - Old: "", - New: "12", - }, - "top.24.bar": &terraform.ResourceAttrDiff{ - Old: "", - New: "12", - }, - }, - }, - Key: "top", - NewValue: NewSet(testSetFunc, []interface{}{ - map[string]interface{}{ - "foo": 1, - "bar": 4, - }, - map[string]interface{}{ - "foo": 13, - "bar": 12, - }, - map[string]interface{}{ - "foo": 21, - "bar": 22, - }, - }), - Expected: &terraform.InstanceDiff{ - Attributes: func() map[string]*terraform.ResourceAttrDiff { - result := make(map[string]*terraform.ResourceAttrDiff) - if computed { - result["top.#"] = &terraform.ResourceAttrDiff{ - Old: "2", - New: "", - NewComputed: true, - } - } else { - result["top.#"] = &terraform.ResourceAttrDiff{ - Old: "2", - New: "3", - } - result["top.5.foo"] = &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - } - result["top.5.bar"] = &terraform.ResourceAttrDiff{ - Old: "", - New: "4", - } - result["top.25.foo"] = &terraform.ResourceAttrDiff{ - Old: "", - New: "13", - } - result["top.25.bar"] = &terraform.ResourceAttrDiff{ - Old: "", - New: "12", - } - result["top.43.foo"] = &terraform.ResourceAttrDiff{ - Old: "", - New: "21", - } - result["top.43.bar"] = &terraform.ResourceAttrDiff{ - Old: "", - New: "22", - } - } - return result - }(), - }, - }, - resourceDiffTestCase{ - Name: "primitive, no diff, no refresh", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Computed: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "bar", - }, - }, - Config: testConfig(t, map[string]interface{}{}), - Diff: &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{}}, - Key: "foo", - NewValue: "baz", - Expected: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: func() string { - if computed { - return "" - } - return "baz" - }(), - NewComputed: computed, - }, - }, - }, - }, - resourceDiffTestCase{ - Name: "non-computed key, should error", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Required: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "bar", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": "baz", - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "baz", - }, - }, - }, - Key: "foo", - NewValue: "qux", - ExpectedError: true, - }, - resourceDiffTestCase{ - Name: "bad key, should error", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Required: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "bar", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": "baz", - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "baz", - }, - }, - }, - Key: "bad", - NewValue: "qux", - ExpectedError: true, - }, - resourceDiffTestCase{ - // NOTE: This case is technically impossible in the current - // implementation, because optional+computed values never show up in the - // diff, and we actually clear existing diffs when SetNew or - // SetNewComputed is run. This test is here to ensure that if either of - // these behaviors change that we don't introduce regressions. - Name: "NewRemoved in diff for Optional and Computed, should be fully overridden", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "bar", - }, - }, - Config: testConfig(t, map[string]interface{}{}), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "", - NewRemoved: true, - }, - }, - }, - Key: "foo", - NewValue: "qux", - Expected: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: func() string { - if computed { - return "" - } - return "qux" - }(), - NewComputed: computed, - }, - }, - }, - }, - resourceDiffTestCase{ - Name: "NewComputed should always propagate", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Computed: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "", - }, - ID: "pre-existing", - }, - Config: testConfig(t, map[string]interface{}{}), - Diff: &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{}}, - Key: "foo", - NewValue: "", - Expected: &terraform.InstanceDiff{ - Attributes: func() map[string]*terraform.ResourceAttrDiff { - if computed { - return map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - NewComputed: computed, - }, - } - } - return map[string]*terraform.ResourceAttrDiff{} - }(), - }, - }, - } -} - -func TestSetNew(t *testing.T) { - testCases := testDiffCases(t, "", 0, false) - for _, tc := range testCases { - t.Run(tc.Name, func(t *testing.T) { - m := schemaMap(tc.Schema) - d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) - err := d.SetNew(tc.Key, tc.NewValue) - switch { - case err != nil && !tc.ExpectedError: - t.Fatalf("bad: %s", err) - case err == nil && tc.ExpectedError: - t.Fatalf("Expected error, got none") - case err != nil && tc.ExpectedError: - return - } - for _, k := range d.UpdatedKeys() { - if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { - t.Fatalf("bad: %s", err) - } - } - if !reflect.DeepEqual(tc.Expected, tc.Diff) { - t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff)) - } - }) - } -} - -func TestSetNewComputed(t *testing.T) { - testCases := testDiffCases(t, "", 0, true) - for _, tc := range testCases { - t.Run(tc.Name, func(t *testing.T) { - m := schemaMap(tc.Schema) - d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) - err := d.SetNewComputed(tc.Key) - switch { - case err != nil && !tc.ExpectedError: - t.Fatalf("bad: %s", err) - case err == nil && tc.ExpectedError: - t.Fatalf("Expected error, got none") - case err != nil && tc.ExpectedError: - return - } - for _, k := range d.UpdatedKeys() { - if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { - t.Fatalf("bad: %s", err) - } - } - if !reflect.DeepEqual(tc.Expected, tc.Diff) { - t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff)) - } - }) - } -} - -func TestForceNew(t *testing.T) { - cases := []resourceDiffTestCase{ - resourceDiffTestCase{ - Name: "basic primitive diff", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "bar", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": "baz", - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "baz", - }, - }, - }, - Key: "foo", - Expected: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "baz", - RequiresNew: true, - }, - }, - }, - }, - resourceDiffTestCase{ - Name: "no change, should error", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "bar", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": "bar", - }), - ExpectedError: true, - }, - resourceDiffTestCase{ - Name: "basic primitive, non-computed key", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Required: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "bar", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": "baz", - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "baz", - }, - }, - }, - Key: "foo", - Expected: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "baz", - RequiresNew: true, - }, - }, - }, - }, - resourceDiffTestCase{ - Name: "nested field", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeList, - Required: true, - MaxItems: 1, - Elem: &Resource{ - Schema: map[string]*Schema{ - "bar": { - Type: TypeString, - Optional: true, - }, - "baz": { - Type: TypeString, - Optional: true, - }, - }, - }, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo.#": "1", - "foo.0.bar": "abc", - "foo.0.baz": "xyz", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": []interface{}{ - map[string]interface{}{ - "bar": "abcdefg", - "baz": "changed", - }, - }, - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo.0.bar": &terraform.ResourceAttrDiff{ - Old: "abc", - New: "abcdefg", - }, - "foo.0.baz": &terraform.ResourceAttrDiff{ - Old: "xyz", - New: "changed", - }, - }, - }, - Key: "foo.0.baz", - Expected: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo.0.bar": &terraform.ResourceAttrDiff{ - Old: "abc", - New: "abcdefg", - }, - "foo.0.baz": &terraform.ResourceAttrDiff{ - Old: "xyz", - New: "changed", - RequiresNew: true, - }, - }, - }, - }, - resourceDiffTestCase{ - Name: "preserve NewRemoved on existing diff", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "bar", - }, - }, - Config: testConfig(t, map[string]interface{}{}), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "", - NewRemoved: true, - }, - }, - }, - Key: "foo", - Expected: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "", - RequiresNew: true, - NewRemoved: true, - }, - }, - }, - }, - resourceDiffTestCase{ - Name: "nested field, preserve original diff without zero values", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeList, - Required: true, - MaxItems: 1, - Elem: &Resource{ - Schema: map[string]*Schema{ - "bar": { - Type: TypeString, - Optional: true, - }, - "baz": { - Type: TypeInt, - Optional: true, - }, - }, - }, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo.#": "1", - "foo.0.bar": "abc", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": []interface{}{ - map[string]interface{}{ - "bar": "abcdefg", - }, - }, - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo.0.bar": &terraform.ResourceAttrDiff{ - Old: "abc", - New: "abcdefg", - }, - }, - }, - Key: "foo.0.bar", - Expected: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo.0.bar": &terraform.ResourceAttrDiff{ - Old: "abc", - New: "abcdefg", - RequiresNew: true, - }, - }, - }, - }, - } - for _, tc := range cases { - t.Run(tc.Name, func(t *testing.T) { - m := schemaMap(tc.Schema) - d := newResourceDiff(m, tc.Config, tc.State, tc.Diff) - err := d.ForceNew(tc.Key) - switch { - case err != nil && !tc.ExpectedError: - t.Fatalf("bad: %s", err) - case err == nil && tc.ExpectedError: - t.Fatalf("Expected error, got none") - case err != nil && tc.ExpectedError: - return - } - for _, k := range d.UpdatedKeys() { - if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { - t.Fatalf("bad: %s", err) - } - } - if !reflect.DeepEqual(tc.Expected, tc.Diff) { - t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff)) - } - }) - } -} - -func TestClear(t *testing.T) { - cases := []resourceDiffTestCase{ - resourceDiffTestCase{ - Name: "basic primitive diff", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "bar", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": "baz", - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "baz", - }, - }, - }, - Key: "foo", - Expected: &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{}}, - }, - resourceDiffTestCase{ - Name: "non-computed key, should error", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Required: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "bar", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": "baz", - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "baz", - }, - }, - }, - Key: "foo", - ExpectedError: true, - }, - resourceDiffTestCase{ - Name: "multi-value, one removed", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - "one": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "bar", - "one": "two", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": "baz", - "one": "three", - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "baz", - }, - "one": &terraform.ResourceAttrDiff{ - Old: "two", - New: "three", - }, - }, - }, - Key: "one", - Expected: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "baz", - }, - }, - }, - }, - resourceDiffTestCase{ - Name: "basic sub-block diff", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeList, - Optional: true, - Computed: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "bar": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - "baz": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - }, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo.0.bar": "bar1", - "foo.0.baz": "baz1", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": []interface{}{ - map[string]interface{}{ - "bar": "bar2", - "baz": "baz1", - }, - }, - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo.0.bar": &terraform.ResourceAttrDiff{ - Old: "bar1", - New: "bar2", - }, - }, - }, - Key: "foo.0.bar", - Expected: &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{}}, - }, - resourceDiffTestCase{ - Name: "sub-block diff only partial clear", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeList, - Optional: true, - Computed: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "bar": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - "baz": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - }, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo.0.bar": "bar1", - "foo.0.baz": "baz1", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": []interface{}{ - map[string]interface{}{ - "bar": "bar2", - "baz": "baz2", - }, - }, - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo.0.bar": &terraform.ResourceAttrDiff{ - Old: "bar1", - New: "bar2", - }, - "foo.0.baz": &terraform.ResourceAttrDiff{ - Old: "baz1", - New: "baz2", - }, - }, - }, - Key: "foo.0.bar", - Expected: &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo.0.baz": &terraform.ResourceAttrDiff{ - Old: "baz1", - New: "baz2", - }, - }}, - }, - } - for _, tc := range cases { - t.Run(tc.Name, func(t *testing.T) { - m := schemaMap(tc.Schema) - d := newResourceDiff(m, tc.Config, tc.State, tc.Diff) - err := d.Clear(tc.Key) - switch { - case err != nil && !tc.ExpectedError: - t.Fatalf("bad: %s", err) - case err == nil && tc.ExpectedError: - t.Fatalf("Expected error, got none") - case err != nil && tc.ExpectedError: - return - } - for _, k := range d.UpdatedKeys() { - if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { - t.Fatalf("bad: %s", err) - } - } - if !reflect.DeepEqual(tc.Expected, tc.Diff) { - t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff)) - } - }) - } -} - -func TestGetChangedKeysPrefix(t *testing.T) { - cases := []resourceDiffTestCase{ - resourceDiffTestCase{ - Name: "basic primitive diff", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "bar", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "foo": "baz", - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "baz", - }, - }, - }, - Key: "foo", - ExpectedKeys: []string{ - "foo", - }, - }, - resourceDiffTestCase{ - Name: "nested field filtering", - Schema: map[string]*Schema{ - "testfield": &Schema{ - Type: TypeString, - Required: true, - }, - "foo": &Schema{ - Type: TypeList, - Required: true, - MaxItems: 1, - Elem: &Resource{ - Schema: map[string]*Schema{ - "bar": { - Type: TypeString, - Optional: true, - }, - "baz": { - Type: TypeString, - Optional: true, - }, - }, - }, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "testfield": "blablah", - "foo.#": "1", - "foo.0.bar": "abc", - "foo.0.baz": "xyz", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "testfield": "modified", - "foo": []interface{}{ - map[string]interface{}{ - "bar": "abcdefg", - "baz": "changed", - }, - }, - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "testfield": &terraform.ResourceAttrDiff{ - Old: "blablah", - New: "modified", - }, - "foo.0.bar": &terraform.ResourceAttrDiff{ - Old: "abc", - New: "abcdefg", - }, - "foo.0.baz": &terraform.ResourceAttrDiff{ - Old: "xyz", - New: "changed", - }, - }, - }, - Key: "foo", - ExpectedKeys: []string{ - "foo.0.bar", - "foo.0.baz", - }, - }, - } - for _, tc := range cases { - t.Run(tc.Name, func(t *testing.T) { - m := schemaMap(tc.Schema) - d := newResourceDiff(m, tc.Config, tc.State, tc.Diff) - keys := d.GetChangedKeysPrefix(tc.Key) - - for _, k := range d.UpdatedKeys() { - if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { - t.Fatalf("bad: %s", err) - } - } - - sort.Strings(keys) - - if !reflect.DeepEqual(tc.ExpectedKeys, keys) { - t.Fatalf("Expected %s, got %s", spew.Sdump(tc.ExpectedKeys), spew.Sdump(keys)) - } - }) - } -} - -func TestResourceDiffGetOkExists(t *testing.T) { - cases := []struct { - Name string - Schema map[string]*Schema - State *terraform.InstanceState - Config *terraform.ResourceConfig - Diff *terraform.InstanceDiff - Key string - Value interface{} - Ok bool - }{ - /* - * Primitives - */ - { - Name: "string-literal-empty", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - Config: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": { - Old: "", - New: "", - }, - }, - }, - - Key: "availability_zone", - Value: "", - Ok: true, - }, - - { - Name: "string-computed-empty", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - Config: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": { - Old: "", - New: "", - NewComputed: true, - }, - }, - }, - - Key: "availability_zone", - Value: "", - Ok: false, - }, - - { - Name: "string-optional-computed-nil-diff", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - Config: nil, - - Diff: nil, - - Key: "availability_zone", - Value: "", - Ok: false, - }, - - /* - * Lists - */ - - { - Name: "list-optional", - Schema: map[string]*Schema{ - "ports": { - Type: TypeList, - Optional: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: nil, - Config: nil, - - Diff: nil, - - Key: "ports", - Value: []interface{}{}, - Ok: false, - }, - - /* - * Map - */ - - { - Name: "map-optional", - Schema: map[string]*Schema{ - "ports": { - Type: TypeMap, - Optional: true, - }, - }, - - State: nil, - Config: nil, - - Diff: nil, - - Key: "ports", - Value: map[string]interface{}{}, - Ok: false, - }, - - /* - * Set - */ - - { - Name: "set-optional", - Schema: map[string]*Schema{ - "ports": { - Type: TypeSet, - Optional: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { return a.(int) }, - }, - }, - - State: nil, - Config: nil, - - Diff: nil, - - Key: "ports", - Value: []interface{}{}, - Ok: false, - }, - - { - Name: "set-optional-key", - Schema: map[string]*Schema{ - "ports": { - Type: TypeSet, - Optional: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { return a.(int) }, - }, - }, - - State: nil, - Config: nil, - - Diff: nil, - - Key: "ports.0", - Value: 0, - Ok: false, - }, - - { - Name: "bool-literal-empty", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeBool, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - Config: nil, - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": { - Old: "", - New: "", - }, - }, - }, - - Key: "availability_zone", - Value: false, - Ok: true, - }, - - { - Name: "bool-literal-set", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeBool, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - Config: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": { - New: "true", - }, - }, - }, - - Key: "availability_zone", - Value: true, - Ok: true, - }, - { - Name: "value-in-config", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "availability_zone": "foo", - }), - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{}, - }, - - Key: "availability_zone", - Value: "foo", - Ok: true, - }, - { - Name: "new-value-in-config", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - }, - }, - - State: nil, - Config: testConfig(t, map[string]interface{}{ - "availability_zone": "foo", - }), - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": { - Old: "", - New: "foo", - }, - }, - }, - - Key: "availability_zone", - Value: "foo", - Ok: true, - }, - { - Name: "optional-computed-value-in-config", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "availability_zone": "bar", - }), - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": { - Old: "foo", - New: "bar", - }, - }, - }, - - Key: "availability_zone", - Value: "bar", - Ok: true, - }, - { - Name: "removed-value", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - Config: testConfig(t, map[string]interface{}{}), - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": { - Old: "foo", - New: "", - NewRemoved: true, - }, - }, - }, - - Key: "availability_zone", - Value: "", - Ok: true, - }, - } - - for i, tc := range cases { - t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { - d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) - - v, ok := d.GetOkExists(tc.Key) - if s, ok := v.(*Set); ok { - v = s.List() - } - - if !reflect.DeepEqual(v, tc.Value) { - t.Fatalf("Bad %s: \n%#v", tc.Name, v) - } - if ok != tc.Ok { - t.Fatalf("%s: expected ok: %t, got: %t", tc.Name, tc.Ok, ok) - } - }) - } -} - -func TestResourceDiffGetOkExistsSetNew(t *testing.T) { - tc := struct { - Schema map[string]*Schema - State *terraform.InstanceState - Diff *terraform.InstanceDiff - Key string - Value interface{} - Ok bool - }{ - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{}, - }, - - Key: "availability_zone", - Value: "foobar", - Ok: true, - } - - d := newResourceDiff(tc.Schema, testConfig(t, map[string]interface{}{}), tc.State, tc.Diff) - d.SetNew(tc.Key, tc.Value) - - v, ok := d.GetOkExists(tc.Key) - if s, ok := v.(*Set); ok { - v = s.List() - } - - if !reflect.DeepEqual(v, tc.Value) { - t.Fatalf("Bad: \n%#v", v) - } - if ok != tc.Ok { - t.Fatalf("expected ok: %t, got: %t", tc.Ok, ok) - } -} - -func TestResourceDiffGetOkExistsSetNewComputed(t *testing.T) { - tc := struct { - Schema map[string]*Schema - State *terraform.InstanceState - Diff *terraform.InstanceDiff - Key string - Value interface{} - Ok bool - }{ - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{}, - }, - - Key: "availability_zone", - Value: "foobar", - Ok: false, - } - - d := newResourceDiff(tc.Schema, testConfig(t, map[string]interface{}{}), tc.State, tc.Diff) - d.SetNewComputed(tc.Key) - - _, ok := d.GetOkExists(tc.Key) - - if ok != tc.Ok { - t.Fatalf("expected ok: %t, got: %t", tc.Ok, ok) - } -} - -func TestResourceDiffNewValueKnown(t *testing.T) { - cases := []struct { - Name string - Schema map[string]*Schema - State *terraform.InstanceState - Config *terraform.ResourceConfig - Diff *terraform.InstanceDiff - Key string - Expected bool - }{ - { - Name: "in config, no state", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - }, - }, - State: nil, - Config: testConfig(t, map[string]interface{}{ - "availability_zone": "foo", - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": { - Old: "", - New: "foo", - }, - }, - }, - Key: "availability_zone", - Expected: true, - }, - { - Name: "in config, has state, no diff", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "availability_zone": "foo", - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{}, - }, - Key: "availability_zone", - Expected: true, - }, - { - Name: "computed attribute, in state, no diff", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Computed: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - Config: testConfig(t, map[string]interface{}{}), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{}, - }, - Key: "availability_zone", - Expected: true, - }, - { - Name: "optional and computed attribute, in state, no config", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - Config: testConfig(t, map[string]interface{}{}), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{}, - }, - Key: "availability_zone", - Expected: true, - }, - { - Name: "optional and computed attribute, in state, with config", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - Config: testConfig(t, map[string]interface{}{ - "availability_zone": "foo", - }), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{}, - }, - Key: "availability_zone", - Expected: true, - }, - { - Name: "computed value, through config reader", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - Config: testConfig( - t, - map[string]interface{}{ - "availability_zone": hcl2shim.UnknownVariableValue, - }, - ), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{}, - }, - Key: "availability_zone", - Expected: false, - }, - { - Name: "computed value, through diff reader", - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - Config: testConfig( - t, - map[string]interface{}{ - "availability_zone": hcl2shim.UnknownVariableValue, - }, - ), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": { - Old: "foo", - New: "", - NewComputed: true, - }, - }, - }, - Key: "availability_zone", - Expected: false, - }, - } - - for i, tc := range cases { - t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { - d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) - - actual := d.NewValueKnown(tc.Key) - if tc.Expected != actual { - t.Fatalf("%s: expected ok: %t, got: %t", tc.Name, tc.Expected, actual) - } - }) - } -} - -func TestResourceDiffNewValueKnownSetNew(t *testing.T) { - tc := struct { - Schema map[string]*Schema - State *terraform.InstanceState - Config *terraform.ResourceConfig - Diff *terraform.InstanceDiff - Key string - Value interface{} - Expected bool - }{ - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - Config: testConfig( - t, - map[string]interface{}{ - "availability_zone": hcl2shim.UnknownVariableValue, - }, - ), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": { - Old: "foo", - New: "", - NewComputed: true, - }, - }, - }, - Key: "availability_zone", - Value: "bar", - Expected: true, - } - - d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) - d.SetNew(tc.Key, tc.Value) - - actual := d.NewValueKnown(tc.Key) - if tc.Expected != actual { - t.Fatalf("expected ok: %t, got: %t", tc.Expected, actual) - } -} - -func TestResourceDiffNewValueKnownSetNewComputed(t *testing.T) { - tc := struct { - Schema map[string]*Schema - State *terraform.InstanceState - Config *terraform.ResourceConfig - Diff *terraform.InstanceDiff - Key string - Expected bool - }{ - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Computed: true, - }, - }, - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - Config: testConfig(t, map[string]interface{}{}), - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{}, - }, - Key: "availability_zone", - Expected: false, - } - - d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) - d.SetNewComputed(tc.Key) - - actual := d.NewValueKnown(tc.Key) - if tc.Expected != actual { - t.Fatalf("expected ok: %t, got: %t", tc.Expected, actual) - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_test.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource_test.go deleted file mode 100644 index a532dba3..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/resource_test.go +++ /dev/null @@ -1,1687 +0,0 @@ -package schema - -import ( - "encoding/json" - "fmt" - "reflect" - "strconv" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/hashicorp/terraform/configs/hcl2shim" - "github.com/hashicorp/terraform/terraform" - - "github.com/zclconf/go-cty/cty" - ctyjson "github.com/zclconf/go-cty/cty/json" -) - -func TestResourceApply_create(t *testing.T) { - r := &Resource{ - SchemaVersion: 2, - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - called := false - r.Create = func(d *ResourceData, m interface{}) error { - called = true - d.SetId("foo") - return nil - } - - var s *terraform.InstanceState = nil - - d := &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - New: "42", - }, - }, - } - - actual, err := r.Apply(s, d, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - if !called { - t.Fatal("not called") - } - - expected := &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "id": "foo", - "foo": "42", - }, - Meta: map[string]interface{}{ - "schema_version": "2", - }, - } - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: %#v", actual) - } -} - -func TestResourceApply_Timeout_state(t *testing.T) { - r := &Resource{ - SchemaVersion: 2, - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - Timeouts: &ResourceTimeout{ - Create: DefaultTimeout(40 * time.Minute), - Update: DefaultTimeout(80 * time.Minute), - Delete: DefaultTimeout(40 * time.Minute), - }, - } - - called := false - r.Create = func(d *ResourceData, m interface{}) error { - called = true - d.SetId("foo") - return nil - } - - var s *terraform.InstanceState = nil - - d := &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - New: "42", - }, - }, - } - - diffTimeout := &ResourceTimeout{ - Create: DefaultTimeout(40 * time.Minute), - Update: DefaultTimeout(80 * time.Minute), - Delete: DefaultTimeout(40 * time.Minute), - } - - if err := diffTimeout.DiffEncode(d); err != nil { - t.Fatalf("Error encoding timeout to diff: %s", err) - } - - actual, err := r.Apply(s, d, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - if !called { - t.Fatal("not called") - } - - expected := &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "id": "foo", - "foo": "42", - }, - Meta: map[string]interface{}{ - "schema_version": "2", - TimeoutKey: expectedForValues(40, 0, 80, 40, 0), - }, - } - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("Not equal in Timeout State:\n\texpected: %#v\n\tactual: %#v", expected.Meta, actual.Meta) - } -} - -// Regression test to ensure that the meta data is read from state, if a -// resource is destroyed and the timeout meta is no longer available from the -// config -func TestResourceApply_Timeout_destroy(t *testing.T) { - timeouts := &ResourceTimeout{ - Create: DefaultTimeout(40 * time.Minute), - Update: DefaultTimeout(80 * time.Minute), - Delete: DefaultTimeout(40 * time.Minute), - } - - r := &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - Timeouts: timeouts, - } - - called := false - var delTimeout time.Duration - r.Delete = func(d *ResourceData, m interface{}) error { - delTimeout = d.Timeout(TimeoutDelete) - called = true - return nil - } - - s := &terraform.InstanceState{ - ID: "bar", - } - - if err := timeouts.StateEncode(s); err != nil { - t.Fatalf("Error encoding to state: %s", err) - } - - d := &terraform.InstanceDiff{ - Destroy: true, - } - - actual, err := r.Apply(s, d, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - if !called { - t.Fatal("delete not called") - } - - if *timeouts.Delete != delTimeout { - t.Fatalf("timeouts don't match, expected (%#v), got (%#v)", timeouts.Delete, delTimeout) - } - - if actual != nil { - t.Fatalf("bad: %#v", actual) - } -} - -func TestResourceDiff_Timeout_diff(t *testing.T) { - r := &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - Timeouts: &ResourceTimeout{ - Create: DefaultTimeout(40 * time.Minute), - Update: DefaultTimeout(80 * time.Minute), - Delete: DefaultTimeout(40 * time.Minute), - }, - } - - r.Create = func(d *ResourceData, m interface{}) error { - d.SetId("foo") - return nil - } - - conf := terraform.NewResourceConfigRaw( - map[string]interface{}{ - "foo": 42, - TimeoutsConfigKey: map[string]interface{}{ - "create": "2h", - }, - }, - ) - var s *terraform.InstanceState - - actual, err := r.Diff(s, conf, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - expected := &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - New: "42", - }, - }, - } - - diffTimeout := &ResourceTimeout{ - Create: DefaultTimeout(120 * time.Minute), - Update: DefaultTimeout(80 * time.Minute), - Delete: DefaultTimeout(40 * time.Minute), - } - - if err := diffTimeout.DiffEncode(expected); err != nil { - t.Fatalf("Error encoding timeout to diff: %s", err) - } - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("Not equal Meta in Timeout Diff:\n\texpected: %#v\n\tactual: %#v", expected.Meta, actual.Meta) - } -} - -func TestResourceDiff_CustomizeFunc(t *testing.T) { - r := &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - var called bool - - r.CustomizeDiff = func(d *ResourceDiff, m interface{}) error { - called = true - return nil - } - - conf := terraform.NewResourceConfigRaw( - map[string]interface{}{ - "foo": 42, - }, - ) - - var s *terraform.InstanceState - - _, err := r.Diff(s, conf, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - if !called { - t.Fatalf("diff customization not called") - } -} - -func TestResourceApply_destroy(t *testing.T) { - r := &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - called := false - r.Delete = func(d *ResourceData, m interface{}) error { - called = true - return nil - } - - s := &terraform.InstanceState{ - ID: "bar", - } - - d := &terraform.InstanceDiff{ - Destroy: true, - } - - actual, err := r.Apply(s, d, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - if !called { - t.Fatal("delete not called") - } - - if actual != nil { - t.Fatalf("bad: %#v", actual) - } -} - -func TestResourceApply_destroyCreate(t *testing.T) { - r := &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - - "tags": &Schema{ - Type: TypeMap, - Optional: true, - Computed: true, - }, - }, - } - - change := false - r.Create = func(d *ResourceData, m interface{}) error { - change = d.HasChange("tags") - d.SetId("foo") - return nil - } - r.Delete = func(d *ResourceData, m interface{}) error { - return nil - } - - var s *terraform.InstanceState = &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "foo": "bar", - "tags.Name": "foo", - }, - } - - d := &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - New: "42", - RequiresNew: true, - }, - "tags.Name": &terraform.ResourceAttrDiff{ - Old: "foo", - New: "foo", - RequiresNew: true, - }, - }, - } - - actual, err := r.Apply(s, d, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - if !change { - t.Fatal("should have change") - } - - expected := &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "id": "foo", - "foo": "42", - "tags.%": "1", - "tags.Name": "foo", - }, - } - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: %#v", actual) - } -} - -func TestResourceApply_destroyPartial(t *testing.T) { - r := &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - SchemaVersion: 3, - } - - r.Delete = func(d *ResourceData, m interface{}) error { - d.Set("foo", 42) - return fmt.Errorf("some error") - } - - s := &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "foo": "12", - }, - } - - d := &terraform.InstanceDiff{ - Destroy: true, - } - - actual, err := r.Apply(s, d, nil) - if err == nil { - t.Fatal("should error") - } - - expected := &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "id": "bar", - "foo": "42", - }, - Meta: map[string]interface{}{ - "schema_version": "3", - }, - } - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("expected:\n%#v\n\ngot:\n%#v", expected, actual) - } -} - -func TestResourceApply_update(t *testing.T) { - r := &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - r.Update = func(d *ResourceData, m interface{}) error { - d.Set("foo", 42) - return nil - } - - s := &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "foo": "12", - }, - } - - d := &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - New: "13", - }, - }, - } - - actual, err := r.Apply(s, d, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - expected := &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "id": "foo", - "foo": "42", - }, - } - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: %#v", actual) - } -} - -func TestResourceApply_updateNoCallback(t *testing.T) { - r := &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - r.Update = nil - - s := &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "foo": "12", - }, - } - - d := &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - New: "13", - }, - }, - } - - actual, err := r.Apply(s, d, nil) - if err == nil { - t.Fatal("should error") - } - - expected := &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "foo": "12", - }, - } - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: %#v", actual) - } -} - -func TestResourceApply_isNewResource(t *testing.T) { - r := &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - } - - updateFunc := func(d *ResourceData, m interface{}) error { - d.Set("foo", "updated") - if d.IsNewResource() { - d.Set("foo", "new-resource") - } - return nil - } - r.Create = func(d *ResourceData, m interface{}) error { - d.SetId("foo") - d.Set("foo", "created") - return updateFunc(d, m) - } - r.Update = updateFunc - - d := &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - New: "bla-blah", - }, - }, - } - - // positive test - var s *terraform.InstanceState = nil - - actual, err := r.Apply(s, d, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - expected := &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "id": "foo", - "foo": "new-resource", - }, - } - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("actual: %#v\nexpected: %#v", - actual, expected) - } - - // negative test - s = &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "id": "foo", - "foo": "new-resource", - }, - } - - actual, err = r.Apply(s, d, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - expected = &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "id": "foo", - "foo": "updated", - }, - } - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("actual: %#v\nexpected: %#v", - actual, expected) - } -} - -func TestResourceInternalValidate(t *testing.T) { - cases := []struct { - In *Resource - Writable bool - Err bool - }{ - 0: { - nil, - true, - true, - }, - - // No optional and no required - 1: { - &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - Required: true, - }, - }, - }, - true, - true, - }, - - // Update undefined for non-ForceNew field - 2: { - &Resource{ - Create: func(d *ResourceData, meta interface{}) error { return nil }, - Schema: map[string]*Schema{ - "boo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - }, - true, - true, - }, - - // Update defined for ForceNew field - 3: { - &Resource{ - Create: func(d *ResourceData, meta interface{}) error { return nil }, - Update: func(d *ResourceData, meta interface{}) error { return nil }, - Schema: map[string]*Schema{ - "goo": &Schema{ - Type: TypeInt, - Optional: true, - ForceNew: true, - }, - }, - }, - true, - true, - }, - - // non-writable doesn't need Update, Create or Delete - 4: { - &Resource{ - Schema: map[string]*Schema{ - "goo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - }, - false, - false, - }, - - // non-writable *must not* have Create - 5: { - &Resource{ - Create: func(d *ResourceData, meta interface{}) error { return nil }, - Schema: map[string]*Schema{ - "goo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - }, - false, - true, - }, - - // writable must have Read - 6: { - &Resource{ - Create: func(d *ResourceData, meta interface{}) error { return nil }, - Update: func(d *ResourceData, meta interface{}) error { return nil }, - Delete: func(d *ResourceData, meta interface{}) error { return nil }, - Schema: map[string]*Schema{ - "goo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - }, - true, - true, - }, - - // writable must have Delete - 7: { - &Resource{ - Create: func(d *ResourceData, meta interface{}) error { return nil }, - Read: func(d *ResourceData, meta interface{}) error { return nil }, - Update: func(d *ResourceData, meta interface{}) error { return nil }, - Schema: map[string]*Schema{ - "goo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - }, - true, - true, - }, - - 8: { // Reserved name at root should be disallowed - &Resource{ - Create: func(d *ResourceData, meta interface{}) error { return nil }, - Read: func(d *ResourceData, meta interface{}) error { return nil }, - Update: func(d *ResourceData, meta interface{}) error { return nil }, - Delete: func(d *ResourceData, meta interface{}) error { return nil }, - Schema: map[string]*Schema{ - "count": { - Type: TypeInt, - Optional: true, - }, - }, - }, - true, - true, - }, - - 9: { // Reserved name at nested levels should be allowed - &Resource{ - Create: func(d *ResourceData, meta interface{}) error { return nil }, - Read: func(d *ResourceData, meta interface{}) error { return nil }, - Update: func(d *ResourceData, meta interface{}) error { return nil }, - Delete: func(d *ResourceData, meta interface{}) error { return nil }, - Schema: map[string]*Schema{ - "parent_list": &Schema{ - Type: TypeString, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "provisioner": { - Type: TypeString, - Optional: true, - }, - }, - }, - }, - }, - }, - true, - false, - }, - - 10: { // Provider reserved name should be allowed in resource - &Resource{ - Create: func(d *ResourceData, meta interface{}) error { return nil }, - Read: func(d *ResourceData, meta interface{}) error { return nil }, - Update: func(d *ResourceData, meta interface{}) error { return nil }, - Delete: func(d *ResourceData, meta interface{}) error { return nil }, - Schema: map[string]*Schema{ - "alias": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - }, - true, - false, - }, - - 11: { // ID should be allowed in data source - &Resource{ - Read: func(d *ResourceData, meta interface{}) error { return nil }, - Schema: map[string]*Schema{ - "id": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - }, - false, - false, - }, - - 12: { // Deprecated ID should be allowed in resource - &Resource{ - Create: func(d *ResourceData, meta interface{}) error { return nil }, - Read: func(d *ResourceData, meta interface{}) error { return nil }, - Update: func(d *ResourceData, meta interface{}) error { return nil }, - Delete: func(d *ResourceData, meta interface{}) error { return nil }, - Schema: map[string]*Schema{ - "id": &Schema{ - Type: TypeString, - Optional: true, - Deprecated: "Use x_id instead", - }, - }, - }, - true, - false, - }, - - 13: { // non-writable must not define CustomizeDiff - &Resource{ - Read: func(d *ResourceData, meta interface{}) error { return nil }, - Schema: map[string]*Schema{ - "goo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - CustomizeDiff: func(*ResourceDiff, interface{}) error { return nil }, - }, - false, - true, - }, - 14: { // Deprecated resource - &Resource{ - Read: func(d *ResourceData, meta interface{}) error { return nil }, - Schema: map[string]*Schema{ - "goo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - DeprecationMessage: "This resource has been deprecated.", - }, - true, - true, - }, - } - - for i, tc := range cases { - t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { - sm := schemaMap{} - if tc.In != nil { - sm = schemaMap(tc.In.Schema) - } - - err := tc.In.InternalValidate(sm, tc.Writable) - if err != nil && !tc.Err { - t.Fatalf("%d: expected validation to pass: %s", i, err) - } - if err == nil && tc.Err { - t.Fatalf("%d: expected validation to fail", i) - } - }) - } -} - -func TestResourceRefresh(t *testing.T) { - r := &Resource{ - SchemaVersion: 2, - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - r.Read = func(d *ResourceData, m interface{}) error { - if m != 42 { - return fmt.Errorf("meta not passed") - } - - return d.Set("foo", d.Get("foo").(int)+1) - } - - s := &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "foo": "12", - }, - } - - expected := &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "id": "bar", - "foo": "13", - }, - Meta: map[string]interface{}{ - "schema_version": "2", - }, - } - - actual, err := r.Refresh(s, 42) - if err != nil { - t.Fatalf("err: %s", err) - } - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: %#v", actual) - } -} - -func TestResourceRefresh_blankId(t *testing.T) { - r := &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - r.Read = func(d *ResourceData, m interface{}) error { - d.SetId("foo") - return nil - } - - s := &terraform.InstanceState{ - ID: "", - Attributes: map[string]string{}, - } - - actual, err := r.Refresh(s, 42) - if err != nil { - t.Fatalf("err: %s", err) - } - if actual != nil { - t.Fatalf("bad: %#v", actual) - } -} - -func TestResourceRefresh_delete(t *testing.T) { - r := &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - r.Read = func(d *ResourceData, m interface{}) error { - d.SetId("") - return nil - } - - s := &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "foo": "12", - }, - } - - actual, err := r.Refresh(s, 42) - if err != nil { - t.Fatalf("err: %s", err) - } - - if actual != nil { - t.Fatalf("bad: %#v", actual) - } -} - -func TestResourceRefresh_existsError(t *testing.T) { - r := &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - r.Exists = func(*ResourceData, interface{}) (bool, error) { - return false, fmt.Errorf("error") - } - - r.Read = func(d *ResourceData, m interface{}) error { - panic("shouldn't be called") - } - - s := &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "foo": "12", - }, - } - - actual, err := r.Refresh(s, 42) - if err == nil { - t.Fatalf("should error") - } - if !reflect.DeepEqual(actual, s) { - t.Fatalf("bad: %#v", actual) - } -} - -func TestResourceRefresh_noExists(t *testing.T) { - r := &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - r.Exists = func(*ResourceData, interface{}) (bool, error) { - return false, nil - } - - r.Read = func(d *ResourceData, m interface{}) error { - panic("shouldn't be called") - } - - s := &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "foo": "12", - }, - } - - actual, err := r.Refresh(s, 42) - if err != nil { - t.Fatalf("err: %s", err) - } - if actual != nil { - t.Fatalf("should have no state") - } -} - -func TestResourceRefresh_needsMigration(t *testing.T) { - // Schema v2 it deals only in newfoo, which tracks foo as an int - r := &Resource{ - SchemaVersion: 2, - Schema: map[string]*Schema{ - "newfoo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - r.Read = func(d *ResourceData, m interface{}) error { - return d.Set("newfoo", d.Get("newfoo").(int)+1) - } - - r.MigrateState = func( - v int, - s *terraform.InstanceState, - meta interface{}) (*terraform.InstanceState, error) { - // Real state migration functions will probably switch on this value, - // but we'll just assert on it for now. - if v != 1 { - t.Fatalf("Expected StateSchemaVersion to be 1, got %d", v) - } - - if meta != 42 { - t.Fatal("Expected meta to be passed through to the migration function") - } - - oldfoo, err := strconv.ParseFloat(s.Attributes["oldfoo"], 64) - if err != nil { - t.Fatalf("err: %#v", err) - } - s.Attributes["newfoo"] = strconv.Itoa(int(oldfoo * 10)) - delete(s.Attributes, "oldfoo") - - return s, nil - } - - // State is v1 and deals in oldfoo, which tracked foo as a float at 1/10th - // the scale of newfoo - s := &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "oldfoo": "1.2", - }, - Meta: map[string]interface{}{ - "schema_version": "1", - }, - } - - actual, err := r.Refresh(s, 42) - if err != nil { - t.Fatalf("err: %s", err) - } - - expected := &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "id": "bar", - "newfoo": "13", - }, - Meta: map[string]interface{}{ - "schema_version": "2", - }, - } - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad:\n\nexpected: %#v\ngot: %#v", expected, actual) - } -} - -func TestResourceRefresh_noMigrationNeeded(t *testing.T) { - r := &Resource{ - SchemaVersion: 2, - Schema: map[string]*Schema{ - "newfoo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - r.Read = func(d *ResourceData, m interface{}) error { - return d.Set("newfoo", d.Get("newfoo").(int)+1) - } - - r.MigrateState = func( - v int, - s *terraform.InstanceState, - meta interface{}) (*terraform.InstanceState, error) { - t.Fatal("Migrate function shouldn't be called!") - return nil, nil - } - - s := &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "newfoo": "12", - }, - Meta: map[string]interface{}{ - "schema_version": "2", - }, - } - - actual, err := r.Refresh(s, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - expected := &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "id": "bar", - "newfoo": "13", - }, - Meta: map[string]interface{}{ - "schema_version": "2", - }, - } - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad:\n\nexpected: %#v\ngot: %#v", expected, actual) - } -} - -func TestResourceRefresh_stateSchemaVersionUnset(t *testing.T) { - r := &Resource{ - // Version 1 > Version 0 - SchemaVersion: 1, - Schema: map[string]*Schema{ - "newfoo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - r.Read = func(d *ResourceData, m interface{}) error { - return d.Set("newfoo", d.Get("newfoo").(int)+1) - } - - r.MigrateState = func( - v int, - s *terraform.InstanceState, - meta interface{}) (*terraform.InstanceState, error) { - s.Attributes["newfoo"] = s.Attributes["oldfoo"] - return s, nil - } - - s := &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "oldfoo": "12", - }, - } - - actual, err := r.Refresh(s, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - expected := &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "id": "bar", - "newfoo": "13", - }, - Meta: map[string]interface{}{ - "schema_version": "1", - }, - } - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad:\n\nexpected: %#v\ngot: %#v", expected, actual) - } -} - -func TestResourceRefresh_migrateStateErr(t *testing.T) { - r := &Resource{ - SchemaVersion: 2, - Schema: map[string]*Schema{ - "newfoo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - r.Read = func(d *ResourceData, m interface{}) error { - t.Fatal("Read should never be called!") - return nil - } - - r.MigrateState = func( - v int, - s *terraform.InstanceState, - meta interface{}) (*terraform.InstanceState, error) { - return s, fmt.Errorf("triggering an error") - } - - s := &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "oldfoo": "12", - }, - } - - _, err := r.Refresh(s, nil) - if err == nil { - t.Fatal("expected error, but got none!") - } -} - -func TestResourceData(t *testing.T) { - r := &Resource{ - SchemaVersion: 2, - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - state := &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "id": "foo", - "foo": "42", - }, - } - - data := r.Data(state) - if data.Id() != "foo" { - t.Fatalf("err: %s", data.Id()) - } - if v := data.Get("foo"); v != 42 { - t.Fatalf("bad: %#v", v) - } - - // Set expectations - state.Meta = map[string]interface{}{ - "schema_version": "2", - } - - result := data.State() - if !reflect.DeepEqual(result, state) { - t.Fatalf("bad: %#v", result) - } -} - -func TestResourceData_blank(t *testing.T) { - r := &Resource{ - SchemaVersion: 2, - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - data := r.Data(nil) - if data.Id() != "" { - t.Fatalf("err: %s", data.Id()) - } - if v := data.Get("foo"); v != 0 { - t.Fatalf("bad: %#v", v) - } -} - -func TestResourceData_timeouts(t *testing.T) { - one := 1 * time.Second - two := 2 * time.Second - three := 3 * time.Second - four := 4 * time.Second - five := 5 * time.Second - - timeouts := &ResourceTimeout{ - Create: &one, - Read: &two, - Update: &three, - Delete: &four, - Default: &five, - } - - r := &Resource{ - SchemaVersion: 2, - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - Timeouts: timeouts, - } - - data := r.Data(nil) - if data.Id() != "" { - t.Fatalf("err: %s", data.Id()) - } - - if !reflect.DeepEqual(timeouts, data.timeouts) { - t.Fatalf("incorrect ResourceData timeouts: %#v\n", *data.timeouts) - } -} - -func TestResource_UpgradeState(t *testing.T) { - // While this really only calls itself and therefore doesn't test any of - // the Resource code directly, it still serves as an example of registering - // a StateUpgrader. - r := &Resource{ - SchemaVersion: 2, - Schema: map[string]*Schema{ - "newfoo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - r.StateUpgraders = []StateUpgrader{ - { - Version: 1, - Type: cty.Object(map[string]cty.Type{ - "id": cty.String, - "oldfoo": cty.Number, - }), - Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) { - - oldfoo, ok := m["oldfoo"].(float64) - if !ok { - t.Fatalf("expected 1.2, got %#v", m["oldfoo"]) - } - m["newfoo"] = int(oldfoo * 10) - delete(m, "oldfoo") - - return m, nil - }, - }, - } - - oldStateAttrs := map[string]string{ - "id": "bar", - "oldfoo": "1.2", - } - - // convert the legacy flatmap state to the json equivalent - ty := r.StateUpgraders[0].Type - val, err := hcl2shim.HCL2ValueFromFlatmap(oldStateAttrs, ty) - if err != nil { - t.Fatal(err) - } - js, err := ctyjson.Marshal(val, ty) - if err != nil { - t.Fatal(err) - } - - // unmarshal the state using the json default types - var m map[string]interface{} - if err := json.Unmarshal(js, &m); err != nil { - t.Fatal(err) - } - - actual, err := r.StateUpgraders[0].Upgrade(m, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - expected := map[string]interface{}{ - "id": "bar", - "newfoo": 12, - } - - if !reflect.DeepEqual(expected, actual) { - t.Fatalf("expected: %#v\ngot: %#v\n", expected, actual) - } -} - -func TestResource_ValidateUpgradeState(t *testing.T) { - r := &Resource{ - SchemaVersion: 3, - Schema: map[string]*Schema{ - "newfoo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - if err := r.InternalValidate(nil, true); err != nil { - t.Fatal(err) - } - - r.StateUpgraders = append(r.StateUpgraders, StateUpgrader{ - Version: 2, - Type: cty.Object(map[string]cty.Type{ - "id": cty.String, - }), - Upgrade: func(m map[string]interface{}, _ interface{}) (map[string]interface{}, error) { - return m, nil - }, - }) - if err := r.InternalValidate(nil, true); err != nil { - t.Fatal(err) - } - - // check for missing type - r.StateUpgraders[0].Type = cty.Type{} - if err := r.InternalValidate(nil, true); err == nil { - t.Fatal("StateUpgrader must have type") - } - r.StateUpgraders[0].Type = cty.Object(map[string]cty.Type{ - "id": cty.String, - }) - - // check for missing Upgrade func - r.StateUpgraders[0].Upgrade = nil - if err := r.InternalValidate(nil, true); err == nil { - t.Fatal("StateUpgrader must have an Upgrade func") - } - r.StateUpgraders[0].Upgrade = func(m map[string]interface{}, _ interface{}) (map[string]interface{}, error) { - return m, nil - } - - // check for skipped version - r.StateUpgraders[0].Version = 0 - r.StateUpgraders = append(r.StateUpgraders, StateUpgrader{ - Version: 2, - Type: cty.Object(map[string]cty.Type{ - "id": cty.String, - }), - Upgrade: func(m map[string]interface{}, _ interface{}) (map[string]interface{}, error) { - return m, nil - }, - }) - if err := r.InternalValidate(nil, true); err == nil { - t.Fatal("StateUpgraders cannot skip versions") - } - - // add the missing version, but fail because it's still out of order - r.StateUpgraders = append(r.StateUpgraders, StateUpgrader{ - Version: 1, - Type: cty.Object(map[string]cty.Type{ - "id": cty.String, - }), - Upgrade: func(m map[string]interface{}, _ interface{}) (map[string]interface{}, error) { - return m, nil - }, - }) - if err := r.InternalValidate(nil, true); err == nil { - t.Fatal("upgraders must be defined in order") - } - - r.StateUpgraders[1], r.StateUpgraders[2] = r.StateUpgraders[2], r.StateUpgraders[1] - if err := r.InternalValidate(nil, true); err != nil { - t.Fatal(err) - } - - // can't add an upgrader for a schema >= the current version - r.StateUpgraders = append(r.StateUpgraders, StateUpgrader{ - Version: 3, - Type: cty.Object(map[string]cty.Type{ - "id": cty.String, - }), - Upgrade: func(m map[string]interface{}, _ interface{}) (map[string]interface{}, error) { - return m, nil - }, - }) - if err := r.InternalValidate(nil, true); err == nil { - t.Fatal("StateUpgraders cannot have a version >= current SchemaVersion") - } -} - -// The legacy provider will need to be able to handle both types of schema -// transformations, which has been retrofitted into the Refresh method. -func TestResource_migrateAndUpgrade(t *testing.T) { - r := &Resource{ - SchemaVersion: 4, - Schema: map[string]*Schema{ - "four": { - Type: TypeInt, - Required: true, - }, - }, - // this MigrateState will take the state to version 2 - MigrateState: func(v int, is *terraform.InstanceState, _ interface{}) (*terraform.InstanceState, error) { - switch v { - case 0: - _, ok := is.Attributes["zero"] - if !ok { - return nil, fmt.Errorf("zero not found in %#v", is.Attributes) - } - is.Attributes["one"] = "1" - delete(is.Attributes, "zero") - fallthrough - case 1: - _, ok := is.Attributes["one"] - if !ok { - return nil, fmt.Errorf("one not found in %#v", is.Attributes) - } - is.Attributes["two"] = "2" - delete(is.Attributes, "one") - default: - return nil, fmt.Errorf("invalid schema version %d", v) - } - return is, nil - }, - } - - r.Read = func(d *ResourceData, m interface{}) error { - return d.Set("four", 4) - } - - r.StateUpgraders = []StateUpgrader{ - { - Version: 2, - Type: cty.Object(map[string]cty.Type{ - "id": cty.String, - "two": cty.Number, - }), - Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) { - _, ok := m["two"].(float64) - if !ok { - return nil, fmt.Errorf("two not found in %#v", m) - } - m["three"] = float64(3) - delete(m, "two") - return m, nil - }, - }, - { - Version: 3, - Type: cty.Object(map[string]cty.Type{ - "id": cty.String, - "three": cty.Number, - }), - Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) { - _, ok := m["three"].(float64) - if !ok { - return nil, fmt.Errorf("three not found in %#v", m) - } - m["four"] = float64(4) - delete(m, "three") - return m, nil - }, - }, - } - - testStates := []*terraform.InstanceState{ - { - ID: "bar", - Attributes: map[string]string{ - "id": "bar", - "zero": "0", - }, - Meta: map[string]interface{}{ - "schema_version": "0", - }, - }, - { - ID: "bar", - Attributes: map[string]string{ - "id": "bar", - "one": "1", - }, - Meta: map[string]interface{}{ - "schema_version": "1", - }, - }, - { - ID: "bar", - Attributes: map[string]string{ - "id": "bar", - "two": "2", - }, - Meta: map[string]interface{}{ - "schema_version": "2", - }, - }, - { - ID: "bar", - Attributes: map[string]string{ - "id": "bar", - "three": "3", - }, - Meta: map[string]interface{}{ - "schema_version": "3", - }, - }, - { - ID: "bar", - Attributes: map[string]string{ - "id": "bar", - "four": "4", - }, - Meta: map[string]interface{}{ - "schema_version": "4", - }, - }, - } - - for i, s := range testStates { - t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - newState, err := r.Refresh(s, nil) - if err != nil { - t.Fatal(err) - } - - expected := &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "id": "bar", - "four": "4", - }, - Meta: map[string]interface{}{ - "schema_version": "4", - }, - } - - if !cmp.Equal(expected, newState, equateEmpty) { - t.Fatal(cmp.Diff(expected, newState, equateEmpty)) - } - }) - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout.go deleted file mode 100644 index 5ad7aafc..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout.go +++ /dev/null @@ -1,263 +0,0 @@ -package schema - -import ( - "fmt" - "log" - "time" - - "github.com/hashicorp/terraform/configs/hcl2shim" - "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/copystructure" -) - -const TimeoutKey = "e2bfb730-ecaa-11e6-8f88-34363bc7c4c0" -const TimeoutsConfigKey = "timeouts" - -const ( - TimeoutCreate = "create" - TimeoutRead = "read" - TimeoutUpdate = "update" - TimeoutDelete = "delete" - TimeoutDefault = "default" -) - -func timeoutKeys() []string { - return []string{ - TimeoutCreate, - TimeoutRead, - TimeoutUpdate, - TimeoutDelete, - TimeoutDefault, - } -} - -// could be time.Duration, int64 or float64 -func DefaultTimeout(tx interface{}) *time.Duration { - var td time.Duration - switch raw := tx.(type) { - case time.Duration: - return &raw - case int64: - td = time.Duration(raw) - case float64: - td = time.Duration(int64(raw)) - default: - log.Printf("[WARN] Unknown type in DefaultTimeout: %#v", tx) - } - return &td -} - -type ResourceTimeout struct { - Create, Read, Update, Delete, Default *time.Duration -} - -// ConfigDecode takes a schema and the configuration (available in Diff) and -// validates, parses the timeouts into `t` -func (t *ResourceTimeout) ConfigDecode(s *Resource, c *terraform.ResourceConfig) error { - if s.Timeouts != nil { - raw, err := copystructure.Copy(s.Timeouts) - if err != nil { - log.Printf("[DEBUG] Error with deep copy: %s", err) - } - *t = *raw.(*ResourceTimeout) - } - - if raw, ok := c.Config[TimeoutsConfigKey]; ok { - var rawTimeouts []map[string]interface{} - switch raw := raw.(type) { - case map[string]interface{}: - rawTimeouts = append(rawTimeouts, raw) - case []map[string]interface{}: - rawTimeouts = raw - case string: - if raw == hcl2shim.UnknownVariableValue { - // Timeout is not defined in the config - // Defaults will be used instead - return nil - } else { - log.Printf("[ERROR] Invalid timeout value: %q", raw) - return fmt.Errorf("Invalid Timeout value found") - } - case []interface{}: - for _, r := range raw { - if rMap, ok := r.(map[string]interface{}); ok { - rawTimeouts = append(rawTimeouts, rMap) - } else { - // Go will not allow a fallthrough - log.Printf("[ERROR] Invalid timeout structure: %#v", raw) - return fmt.Errorf("Invalid Timeout structure found") - } - } - default: - log.Printf("[ERROR] Invalid timeout structure: %#v", raw) - return fmt.Errorf("Invalid Timeout structure found") - } - - for _, timeoutValues := range rawTimeouts { - for timeKey, timeValue := range timeoutValues { - // validate that we're dealing with the normal CRUD actions - var found bool - for _, key := range timeoutKeys() { - if timeKey == key { - found = true - break - } - } - - if !found { - return fmt.Errorf("Unsupported Timeout configuration key found (%s)", timeKey) - } - - // Get timeout - rt, err := time.ParseDuration(timeValue.(string)) - if err != nil { - return fmt.Errorf("Error parsing %q timeout: %s", timeKey, err) - } - - var timeout *time.Duration - switch timeKey { - case TimeoutCreate: - timeout = t.Create - case TimeoutUpdate: - timeout = t.Update - case TimeoutRead: - timeout = t.Read - case TimeoutDelete: - timeout = t.Delete - case TimeoutDefault: - timeout = t.Default - } - - // If the resource has not delcared this in the definition, then error - // with an unsupported message - if timeout == nil { - return unsupportedTimeoutKeyError(timeKey) - } - - *timeout = rt - } - return nil - } - } - - return nil -} - -func unsupportedTimeoutKeyError(key string) error { - return fmt.Errorf("Timeout Key (%s) is not supported", key) -} - -// DiffEncode, StateEncode, and MetaDecode are analogous to the Go stdlib JSONEncoder -// interface: they encode/decode a timeouts struct from an instance diff, which is -// where the timeout data is stored after a diff to pass into Apply. -// -// StateEncode encodes the timeout into the ResourceData's InstanceState for -// saving to state -// -func (t *ResourceTimeout) DiffEncode(id *terraform.InstanceDiff) error { - return t.metaEncode(id) -} - -func (t *ResourceTimeout) StateEncode(is *terraform.InstanceState) error { - return t.metaEncode(is) -} - -// metaEncode encodes the ResourceTimeout into a map[string]interface{} format -// and stores it in the Meta field of the interface it's given. -// Assumes the interface is either *terraform.InstanceState or -// *terraform.InstanceDiff, returns an error otherwise -func (t *ResourceTimeout) metaEncode(ids interface{}) error { - m := make(map[string]interface{}) - - if t.Create != nil { - m[TimeoutCreate] = t.Create.Nanoseconds() - } - if t.Read != nil { - m[TimeoutRead] = t.Read.Nanoseconds() - } - if t.Update != nil { - m[TimeoutUpdate] = t.Update.Nanoseconds() - } - if t.Delete != nil { - m[TimeoutDelete] = t.Delete.Nanoseconds() - } - if t.Default != nil { - m[TimeoutDefault] = t.Default.Nanoseconds() - // for any key above that is nil, if default is specified, we need to - // populate it with the default - for _, k := range timeoutKeys() { - if _, ok := m[k]; !ok { - m[k] = t.Default.Nanoseconds() - } - } - } - - // only add the Timeout to the Meta if we have values - if len(m) > 0 { - switch instance := ids.(type) { - case *terraform.InstanceDiff: - if instance.Meta == nil { - instance.Meta = make(map[string]interface{}) - } - instance.Meta[TimeoutKey] = m - case *terraform.InstanceState: - if instance.Meta == nil { - instance.Meta = make(map[string]interface{}) - } - instance.Meta[TimeoutKey] = m - default: - return fmt.Errorf("Error matching type for Diff Encode") - } - } - - return nil -} - -func (t *ResourceTimeout) StateDecode(id *terraform.InstanceState) error { - return t.metaDecode(id) -} -func (t *ResourceTimeout) DiffDecode(is *terraform.InstanceDiff) error { - return t.metaDecode(is) -} - -func (t *ResourceTimeout) metaDecode(ids interface{}) error { - var rawMeta interface{} - var ok bool - switch rawInstance := ids.(type) { - case *terraform.InstanceDiff: - rawMeta, ok = rawInstance.Meta[TimeoutKey] - if !ok { - return nil - } - case *terraform.InstanceState: - rawMeta, ok = rawInstance.Meta[TimeoutKey] - if !ok { - return nil - } - default: - return fmt.Errorf("Unknown or unsupported type in metaDecode: %#v", ids) - } - - times := rawMeta.(map[string]interface{}) - if len(times) == 0 { - return nil - } - - if v, ok := times[TimeoutCreate]; ok { - t.Create = DefaultTimeout(v) - } - if v, ok := times[TimeoutRead]; ok { - t.Read = DefaultTimeout(v) - } - if v, ok := times[TimeoutUpdate]; ok { - t.Update = DefaultTimeout(v) - } - if v, ok := times[TimeoutDelete]; ok { - t.Delete = DefaultTimeout(v) - } - if v, ok := times[TimeoutDefault]; ok { - t.Default = DefaultTimeout(v) - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout_test.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout_test.go deleted file mode 100644 index e53bbd84..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout_test.go +++ /dev/null @@ -1,376 +0,0 @@ -package schema - -import ( - "fmt" - "reflect" - "testing" - "time" - - "github.com/hashicorp/terraform/terraform" -) - -func TestResourceTimeout_ConfigDecode_badkey(t *testing.T) { - cases := []struct { - Name string - // what the resource has defined in source - ResourceDefaultTimeout *ResourceTimeout - // configuration provider by user in tf file - Config map[string]interface{} - // what we expect the parsed ResourceTimeout to be - Expected *ResourceTimeout - // Should we have an error (key not defined in source) - ShouldErr bool - }{ - { - Name: "Source does not define 'delete' key", - ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 0), - Config: expectedConfigForValues(2, 0, 0, 1, 0), - Expected: timeoutForValues(10, 0, 5, 0, 0), - ShouldErr: true, - }, - { - Name: "Config overrides create", - ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 0), - Config: expectedConfigForValues(2, 0, 7, 0, 0), - Expected: timeoutForValues(2, 0, 7, 0, 0), - ShouldErr: false, - }, - { - Name: "Config overrides create, default provided. Should still have zero values", - ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 3), - Config: expectedConfigForValues(2, 0, 7, 0, 0), - Expected: timeoutForValues(2, 0, 7, 0, 3), - ShouldErr: false, - }, - { - Name: "Use something besides 'minutes'", - ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 3), - Config: map[string]interface{}{ - "create": "2h", - }, - Expected: timeoutForValues(120, 0, 5, 0, 3), - ShouldErr: false, - }, - } - - for i, c := range cases { - t.Run(fmt.Sprintf("%d-%s", i, c.Name), func(t *testing.T) { - r := &Resource{ - Timeouts: c.ResourceDefaultTimeout, - } - - conf := terraform.NewResourceConfigRaw( - map[string]interface{}{ - "foo": "bar", - TimeoutsConfigKey: c.Config, - }, - ) - - timeout := &ResourceTimeout{} - decodeErr := timeout.ConfigDecode(r, conf) - if c.ShouldErr { - if decodeErr == nil { - t.Fatalf("ConfigDecode case (%d): Expected bad timeout key: %s", i, decodeErr) - } - // should error, err was not nil, continue - return - } else { - if decodeErr != nil { - // should not error, error was not nil, fatal - t.Fatalf("decodeError was not nil: %s", decodeErr) - } - } - - if !reflect.DeepEqual(c.Expected, timeout) { - t.Fatalf("ConfigDecode match error case (%d).\nExpected:\n%#v\nGot:\n%#v", i, c.Expected, timeout) - } - }) - } -} - -func TestResourceTimeout_ConfigDecode(t *testing.T) { - r := &Resource{ - Timeouts: &ResourceTimeout{ - Create: DefaultTimeout(10 * time.Minute), - Update: DefaultTimeout(5 * time.Minute), - }, - } - - c := terraform.NewResourceConfigRaw( - map[string]interface{}{ - "foo": "bar", - TimeoutsConfigKey: map[string]interface{}{ - "create": "2m", - "update": "1m", - }, - }, - ) - - timeout := &ResourceTimeout{} - err := timeout.ConfigDecode(r, c) - if err != nil { - t.Fatalf("Expected good timeout returned:, %s", err) - } - - expected := &ResourceTimeout{ - Create: DefaultTimeout(2 * time.Minute), - Update: DefaultTimeout(1 * time.Minute), - } - - if !reflect.DeepEqual(timeout, expected) { - t.Fatalf("bad timeout decode.\nExpected:\n%#v\nGot:\n%#v\n", expected, timeout) - } -} - -func TestResourceTimeout_legacyConfigDecode(t *testing.T) { - r := &Resource{ - Timeouts: &ResourceTimeout{ - Create: DefaultTimeout(10 * time.Minute), - Update: DefaultTimeout(5 * time.Minute), - }, - } - - c := terraform.NewResourceConfigRaw( - map[string]interface{}{ - "foo": "bar", - TimeoutsConfigKey: []interface{}{ - map[string]interface{}{ - "create": "2m", - "update": "1m", - }, - }, - }, - ) - - timeout := &ResourceTimeout{} - err := timeout.ConfigDecode(r, c) - if err != nil { - t.Fatalf("Expected good timeout returned:, %s", err) - } - - expected := &ResourceTimeout{ - Create: DefaultTimeout(2 * time.Minute), - Update: DefaultTimeout(1 * time.Minute), - } - - if !reflect.DeepEqual(timeout, expected) { - t.Fatalf("bad timeout decode.\nExpected:\n%#v\nGot:\n%#v\n", expected, timeout) - } -} - -func TestResourceTimeout_DiffEncode_basic(t *testing.T) { - cases := []struct { - Timeout *ResourceTimeout - Expected map[string]interface{} - // Not immediately clear when an error would hit - ShouldErr bool - }{ - // Two fields - { - Timeout: timeoutForValues(10, 0, 5, 0, 0), - Expected: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 5, 0, 0)}, - ShouldErr: false, - }, - // Two fields, one is Default - { - Timeout: timeoutForValues(10, 0, 0, 0, 7), - Expected: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 0, 0, 7)}, - ShouldErr: false, - }, - // All fields - { - Timeout: timeoutForValues(10, 3, 4, 1, 7), - Expected: map[string]interface{}{TimeoutKey: expectedForValues(10, 3, 4, 1, 7)}, - ShouldErr: false, - }, - // No fields - { - Timeout: &ResourceTimeout{}, - Expected: nil, - ShouldErr: false, - }, - } - - for _, c := range cases { - state := &terraform.InstanceDiff{} - err := c.Timeout.DiffEncode(state) - if err != nil && !c.ShouldErr { - t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, state.Meta) - } - - // should maybe just compare [TimeoutKey] but for now we're assuming only - // that in Meta - if !reflect.DeepEqual(state.Meta, c.Expected) { - t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, state.Meta) - } - } - // same test cases but for InstanceState - for _, c := range cases { - state := &terraform.InstanceState{} - err := c.Timeout.StateEncode(state) - if err != nil && !c.ShouldErr { - t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, state.Meta) - } - - // should maybe just compare [TimeoutKey] but for now we're assuming only - // that in Meta - if !reflect.DeepEqual(state.Meta, c.Expected) { - t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, state.Meta) - } - } -} - -func TestResourceTimeout_MetaDecode_basic(t *testing.T) { - cases := []struct { - State *terraform.InstanceDiff - Expected *ResourceTimeout - // Not immediately clear when an error would hit - ShouldErr bool - }{ - // Two fields - { - State: &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 5, 0, 0)}}, - Expected: timeoutForValues(10, 0, 5, 0, 0), - ShouldErr: false, - }, - // Two fields, one is Default - { - State: &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 0, 0, 7)}}, - Expected: timeoutForValues(10, 7, 7, 7, 7), - ShouldErr: false, - }, - // All fields - { - State: &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 3, 4, 1, 7)}}, - Expected: timeoutForValues(10, 3, 4, 1, 7), - ShouldErr: false, - }, - // No fields - { - State: &terraform.InstanceDiff{}, - Expected: &ResourceTimeout{}, - ShouldErr: false, - }, - } - - for _, c := range cases { - rt := &ResourceTimeout{} - err := rt.DiffDecode(c.State) - if err != nil && !c.ShouldErr { - t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, rt) - } - - // should maybe just compare [TimeoutKey] but for now we're assuming only - // that in Meta - if !reflect.DeepEqual(rt, c.Expected) { - t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, rt) - } - } -} - -func timeoutForValues(create, read, update, del, def int) *ResourceTimeout { - rt := ResourceTimeout{} - - if create != 0 { - rt.Create = DefaultTimeout(time.Duration(create) * time.Minute) - } - if read != 0 { - rt.Read = DefaultTimeout(time.Duration(read) * time.Minute) - } - if update != 0 { - rt.Update = DefaultTimeout(time.Duration(update) * time.Minute) - } - if del != 0 { - rt.Delete = DefaultTimeout(time.Duration(del) * time.Minute) - } - - if def != 0 { - rt.Default = DefaultTimeout(time.Duration(def) * time.Minute) - } - - return &rt -} - -// Generates a ResourceTimeout struct that should reflect the -// d.Timeout("key") results -func expectedTimeoutForValues(create, read, update, del, def int) *ResourceTimeout { - rt := ResourceTimeout{} - - defaultValues := []*int{&create, &read, &update, &del, &def} - for _, v := range defaultValues { - if *v == 0 { - *v = 20 - } - } - - if create != 0 { - rt.Create = DefaultTimeout(time.Duration(create) * time.Minute) - } - if read != 0 { - rt.Read = DefaultTimeout(time.Duration(read) * time.Minute) - } - if update != 0 { - rt.Update = DefaultTimeout(time.Duration(update) * time.Minute) - } - if del != 0 { - rt.Delete = DefaultTimeout(time.Duration(del) * time.Minute) - } - - if def != 0 { - rt.Default = DefaultTimeout(time.Duration(def) * time.Minute) - } - - return &rt -} - -func expectedForValues(create, read, update, del, def int) map[string]interface{} { - ex := make(map[string]interface{}) - - if create != 0 { - ex["create"] = DefaultTimeout(time.Duration(create) * time.Minute).Nanoseconds() - } - if read != 0 { - ex["read"] = DefaultTimeout(time.Duration(read) * time.Minute).Nanoseconds() - } - if update != 0 { - ex["update"] = DefaultTimeout(time.Duration(update) * time.Minute).Nanoseconds() - } - if del != 0 { - ex["delete"] = DefaultTimeout(time.Duration(del) * time.Minute).Nanoseconds() - } - - if def != 0 { - defNano := DefaultTimeout(time.Duration(def) * time.Minute).Nanoseconds() - ex["default"] = defNano - - for _, k := range timeoutKeys() { - if _, ok := ex[k]; !ok { - ex[k] = defNano - } - } - } - - return ex -} - -func expectedConfigForValues(create, read, update, delete, def int) map[string]interface{} { - ex := make(map[string]interface{}, 0) - - if create != 0 { - ex["create"] = fmt.Sprintf("%dm", create) - } - if read != 0 { - ex["read"] = fmt.Sprintf("%dm", read) - } - if update != 0 { - ex["update"] = fmt.Sprintf("%dm", update) - } - if delete != 0 { - ex["delete"] = fmt.Sprintf("%dm", delete) - } - - if def != 0 { - ex["default"] = fmt.Sprintf("%dm", def) - } - return ex -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/schema.go b/vendor/github.com/hashicorp/terraform/helper/schema/schema.go deleted file mode 100644 index 089e6b21..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/schema.go +++ /dev/null @@ -1,1854 +0,0 @@ -// schema is a high-level framework for easily writing new providers -// for Terraform. Usage of schema is recommended over attempting to write -// to the low-level plugin interfaces manually. -// -// schema breaks down provider creation into simple CRUD operations for -// resources. The logic of diffing, destroying before creating, updating -// or creating, etc. is all handled by the framework. The plugin author -// only needs to implement a configuration schema and the CRUD operations and -// everything else is meant to just work. -// -// A good starting point is to view the Provider structure. -package schema - -import ( - "context" - "fmt" - "os" - "reflect" - "regexp" - "sort" - "strconv" - "strings" - "sync" - - "github.com/hashicorp/terraform/configs/hcl2shim" - "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/copystructure" - "github.com/mitchellh/mapstructure" -) - -// Name of ENV variable which (if not empty) prefers panic over error -const PanicOnErr = "TF_SCHEMA_PANIC_ON_ERROR" - -// type used for schema package context keys -type contextKey string - -var ( - protoVersionMu sync.Mutex - protoVersion5 = false -) - -func isProto5() bool { - protoVersionMu.Lock() - defer protoVersionMu.Unlock() - return protoVersion5 - -} - -// SetProto5 enables a feature flag for any internal changes required required -// to work with the new plugin protocol. This should not be called by -// provider. -func SetProto5() { - protoVersionMu.Lock() - defer protoVersionMu.Unlock() - protoVersion5 = true -} - -// Schema is used to describe the structure of a value. -// -// Read the documentation of the struct elements for important details. -type Schema struct { - // Type is the type of the value and must be one of the ValueType values. - // - // This type not only determines what type is expected/valid in configuring - // this value, but also what type is returned when ResourceData.Get is - // called. The types returned by Get are: - // - // TypeBool - bool - // TypeInt - int - // TypeFloat - float64 - // TypeString - string - // TypeList - []interface{} - // TypeMap - map[string]interface{} - // TypeSet - *schema.Set - // - Type ValueType - - // ConfigMode allows for overriding the default behaviors for mapping - // schema entries onto configuration constructs. - // - // By default, the Elem field is used to choose whether a particular - // schema is represented in configuration as an attribute or as a nested - // block; if Elem is a *schema.Resource then it's a block and it's an - // attribute otherwise. - // - // If Elem is *schema.Resource then setting ConfigMode to - // SchemaConfigModeAttr will force it to be represented in configuration - // as an attribute, which means that the Computed flag can be used to - // provide default elements when the argument isn't set at all, while still - // allowing the user to force zero elements by explicitly assigning an - // empty list. - // - // When Computed is set without Optional, the attribute is not settable - // in configuration at all and so SchemaConfigModeAttr is the automatic - // behavior, and SchemaConfigModeBlock is not permitted. - ConfigMode SchemaConfigMode - - // If one of these is set, then this item can come from the configuration. - // Both cannot be set. If Optional is set, the value is optional. If - // Required is set, the value is required. - // - // One of these must be set if the value is not computed. That is: - // value either comes from the config, is computed, or is both. - Optional bool - Required bool - - // If this is non-nil, the provided function will be used during diff - // of this field. If this is nil, a default diff for the type of the - // schema will be used. - // - // This allows comparison based on something other than primitive, list - // or map equality - for example SSH public keys may be considered - // equivalent regardless of trailing whitespace. - DiffSuppressFunc SchemaDiffSuppressFunc - - // If this is non-nil, then this will be a default value that is used - // when this item is not set in the configuration. - // - // DefaultFunc can be specified to compute a dynamic default. - // Only one of Default or DefaultFunc can be set. If DefaultFunc is - // used then its return value should be stable to avoid generating - // confusing/perpetual diffs. - // - // Changing either Default or the return value of DefaultFunc can be - // a breaking change, especially if the attribute in question has - // ForceNew set. If a default needs to change to align with changing - // assumptions in an upstream API then it may be necessary to also use - // the MigrateState function on the resource to change the state to match, - // or have the Read function adjust the state value to align with the - // new default. - // - // If Required is true above, then Default cannot be set. DefaultFunc - // can be set with Required. If the DefaultFunc returns nil, then there - // will be no default and the user will be asked to fill it in. - // - // If either of these is set, then the user won't be asked for input - // for this key if the default is not nil. - Default interface{} - DefaultFunc SchemaDefaultFunc - - // Description is used as the description for docs or asking for user - // input. It should be relatively short (a few sentences max) and should - // be formatted to fit a CLI. - Description string - - // InputDefault is the default value to use for when inputs are requested. - // This differs from Default in that if Default is set, no input is - // asked for. If Input is asked, this will be the default value offered. - InputDefault string - - // The fields below relate to diffs. - // - // If Computed is true, then the result of this value is computed - // (unless specified by config) on creation. - // - // If ForceNew is true, then a change in this resource necessitates - // the creation of a new resource. - // - // StateFunc is a function called to change the value of this before - // storing it in the state (and likewise before comparing for diffs). - // The use for this is for example with large strings, you may want - // to simply store the hash of it. - Computed bool - ForceNew bool - StateFunc SchemaStateFunc - - // The following fields are only set for a TypeList, TypeSet, or TypeMap. - // - // Elem represents the element type. For a TypeMap, it must be a *Schema - // with a Type that is one of the primitives: TypeString, TypeBool, - // TypeInt, or TypeFloat. Otherwise it may be either a *Schema or a - // *Resource. If it is *Schema, the element type is just a simple value. - // If it is *Resource, the element type is a complex structure, - // potentially managed via its own CRUD actions on the API. - Elem interface{} - - // The following fields are only set for a TypeList or TypeSet. - // - // MaxItems defines a maximum amount of items that can exist within a - // TypeSet or TypeList. Specific use cases would be if a TypeSet is being - // used to wrap a complex structure, however more than one instance would - // cause instability. - // - // MinItems defines a minimum amount of items that can exist within a - // TypeSet or TypeList. Specific use cases would be if a TypeSet is being - // used to wrap a complex structure, however less than one instance would - // cause instability. - // - // If the field Optional is set to true then MinItems is ignored and thus - // effectively zero. - MaxItems int - MinItems int - - // PromoteSingle originally allowed for a single element to be assigned - // where a primitive list was expected, but this no longer works from - // Terraform v0.12 onwards (Terraform Core will require a list to be set - // regardless of what this is set to) and so only applies to Terraform v0.11 - // and earlier, and so should be used only to retain this functionality - // for those still using v0.11 with a provider that formerly used this. - PromoteSingle bool - - // The following fields are only valid for a TypeSet type. - // - // Set defines a function to determine the unique ID of an item so that - // a proper set can be built. - Set SchemaSetFunc - - // ComputedWhen is a set of queries on the configuration. Whenever any - // of these things is changed, it will require a recompute (this requires - // that Computed is set to true). - // - // NOTE: This currently does not work. - ComputedWhen []string - - // ConflictsWith is a set of schema keys that conflict with this schema. - // This will only check that they're set in the _config_. This will not - // raise an error for a malfunctioning resource that sets a conflicting - // key. - ConflictsWith []string - - // When Deprecated is set, this attribute is deprecated. - // - // A deprecated field still works, but will probably stop working in near - // future. This string is the message shown to the user with instructions on - // how to address the deprecation. - Deprecated string - - // When Removed is set, this attribute has been removed from the schema - // - // Removed attributes can be left in the Schema to generate informative error - // messages for the user when they show up in resource configurations. - // This string is the message shown to the user with instructions on - // what do to about the removed attribute. - Removed string - - // ValidateFunc allows individual fields to define arbitrary validation - // logic. It is yielded the provided config value as an interface{} that is - // guaranteed to be of the proper Schema type, and it can yield warnings or - // errors based on inspection of that value. - // - // ValidateFunc is honored only when the schema's Type is set to TypeInt, - // TypeFloat, TypeString, TypeBool, or TypeMap. It is ignored for all other types. - ValidateFunc SchemaValidateFunc - - // Sensitive ensures that the attribute's value does not get displayed in - // logs or regular output. It should be used for passwords or other - // secret fields. Future versions of Terraform may encrypt these - // values. - Sensitive bool -} - -// SchemaConfigMode is used to influence how a schema item is mapped into a -// corresponding configuration construct, using the ConfigMode field of -// Schema. -type SchemaConfigMode int - -const ( - SchemaConfigModeAuto SchemaConfigMode = iota - SchemaConfigModeAttr - SchemaConfigModeBlock -) - -// SchemaDiffSuppressFunc is a function which can be used to determine -// whether a detected diff on a schema element is "valid" or not, and -// suppress it from the plan if necessary. -// -// Return true if the diff should be suppressed, false to retain it. -type SchemaDiffSuppressFunc func(k, old, new string, d *ResourceData) bool - -// SchemaDefaultFunc is a function called to return a default value for -// a field. -type SchemaDefaultFunc func() (interface{}, error) - -// EnvDefaultFunc is a helper function that returns the value of the -// given environment variable, if one exists, or the default value -// otherwise. -func EnvDefaultFunc(k string, dv interface{}) SchemaDefaultFunc { - return func() (interface{}, error) { - if v := os.Getenv(k); v != "" { - return v, nil - } - - return dv, nil - } -} - -// MultiEnvDefaultFunc is a helper function that returns the value of the first -// environment variable in the given list that returns a non-empty value. If -// none of the environment variables return a value, the default value is -// returned. -func MultiEnvDefaultFunc(ks []string, dv interface{}) SchemaDefaultFunc { - return func() (interface{}, error) { - for _, k := range ks { - if v := os.Getenv(k); v != "" { - return v, nil - } - } - return dv, nil - } -} - -// SchemaSetFunc is a function that must return a unique ID for the given -// element. This unique ID is used to store the element in a hash. -type SchemaSetFunc func(interface{}) int - -// SchemaStateFunc is a function used to convert some type to a string -// to be stored in the state. -type SchemaStateFunc func(interface{}) string - -// SchemaValidateFunc is a function used to validate a single field in the -// schema. -type SchemaValidateFunc func(interface{}, string) ([]string, []error) - -func (s *Schema) GoString() string { - return fmt.Sprintf("*%#v", *s) -} - -// Returns a default value for this schema by either reading Default or -// evaluating DefaultFunc. If neither of these are defined, returns nil. -func (s *Schema) DefaultValue() (interface{}, error) { - if s.Default != nil { - return s.Default, nil - } - - if s.DefaultFunc != nil { - defaultValue, err := s.DefaultFunc() - if err != nil { - return nil, fmt.Errorf("error loading default: %s", err) - } - return defaultValue, nil - } - - return nil, nil -} - -// Returns a zero value for the schema. -func (s *Schema) ZeroValue() interface{} { - // If it's a set then we'll do a bit of extra work to provide the - // right hashing function in our empty value. - if s.Type == TypeSet { - setFunc := s.Set - if setFunc == nil { - // Default set function uses the schema to hash the whole value - elem := s.Elem - switch t := elem.(type) { - case *Schema: - setFunc = HashSchema(t) - case *Resource: - setFunc = HashResource(t) - default: - panic("invalid set element type") - } - } - return &Set{F: setFunc} - } else { - return s.Type.Zero() - } -} - -func (s *Schema) finalizeDiff(d *terraform.ResourceAttrDiff, customized bool) *terraform.ResourceAttrDiff { - if d == nil { - return d - } - - if s.Type == TypeBool { - normalizeBoolString := func(s string) string { - switch s { - case "0": - return "false" - case "1": - return "true" - } - return s - } - d.Old = normalizeBoolString(d.Old) - d.New = normalizeBoolString(d.New) - } - - if s.Computed && !d.NewRemoved && d.New == "" { - // Computed attribute without a new value set - d.NewComputed = true - } - - if s.ForceNew { - // ForceNew, mark that this field is requiring new under the - // following conditions, explained below: - // - // * Old != New - There is a change in value. This field - // is therefore causing a new resource. - // - // * NewComputed - This field is being computed, hence a - // potential change in value, mark as causing a new resource. - d.RequiresNew = d.Old != d.New || d.NewComputed - } - - if d.NewRemoved { - return d - } - - if s.Computed { - // FIXME: This is where the customized bool from getChange finally - // comes into play. It allows the previously incorrect behavior - // of an empty string being used as "unset" when the value is - // computed. This should be removed once we can properly - // represent an unset/nil value from the configuration. - if !customized { - if d.Old != "" && d.New == "" { - // This is a computed value with an old value set already, - // just let it go. - return nil - } - } - - if d.New == "" && !d.NewComputed { - // Computed attribute without a new value set - d.NewComputed = true - } - } - - if s.Sensitive { - // Set the Sensitive flag so output is hidden in the UI - d.Sensitive = true - } - - return d -} - -// InternalMap is used to aid in the transition to the new schema types and -// protocol. The name is not meant to convey any usefulness, as this is not to -// be used directly by any providers. -type InternalMap = schemaMap - -// schemaMap is a wrapper that adds nice functions on top of schemas. -type schemaMap map[string]*Schema - -func (m schemaMap) panicOnError() bool { - if os.Getenv(PanicOnErr) != "" { - return true - } - return false -} - -// Data returns a ResourceData for the given schema, state, and diff. -// -// The diff is optional. -func (m schemaMap) Data( - s *terraform.InstanceState, - d *terraform.InstanceDiff) (*ResourceData, error) { - return &ResourceData{ - schema: m, - state: s, - diff: d, - panicOnError: m.panicOnError(), - }, nil -} - -// DeepCopy returns a copy of this schemaMap. The copy can be safely modified -// without affecting the original. -func (m *schemaMap) DeepCopy() schemaMap { - copy, err := copystructure.Config{Lock: true}.Copy(m) - if err != nil { - panic(err) - } - return *copy.(*schemaMap) -} - -// Diff returns the diff for a resource given the schema map, -// state, and configuration. -func (m schemaMap) Diff( - s *terraform.InstanceState, - c *terraform.ResourceConfig, - customizeDiff CustomizeDiffFunc, - meta interface{}, - handleRequiresNew bool) (*terraform.InstanceDiff, error) { - result := new(terraform.InstanceDiff) - result.Attributes = make(map[string]*terraform.ResourceAttrDiff) - - // Make sure to mark if the resource is tainted - if s != nil { - result.DestroyTainted = s.Tainted - } - - d := &ResourceData{ - schema: m, - state: s, - config: c, - panicOnError: m.panicOnError(), - } - - for k, schema := range m { - err := m.diff(k, schema, result, d, false) - if err != nil { - return nil, err - } - } - - // Remove any nil diffs just to keep things clean - for k, v := range result.Attributes { - if v == nil { - delete(result.Attributes, k) - } - } - - // If this is a non-destroy diff, call any custom diff logic that has been - // defined. - if !result.DestroyTainted && customizeDiff != nil { - mc := m.DeepCopy() - rd := newResourceDiff(mc, c, s, result) - if err := customizeDiff(rd, meta); err != nil { - return nil, err - } - for _, k := range rd.UpdatedKeys() { - err := m.diff(k, mc[k], result, rd, false) - if err != nil { - return nil, err - } - } - } - - if handleRequiresNew { - // If the diff requires a new resource, then we recompute the diff - // so we have the complete new resource diff, and preserve the - // RequiresNew fields where necessary so the user knows exactly what - // caused that. - if result.RequiresNew() { - // Create the new diff - result2 := new(terraform.InstanceDiff) - result2.Attributes = make(map[string]*terraform.ResourceAttrDiff) - - // Preserve the DestroyTainted flag - result2.DestroyTainted = result.DestroyTainted - - // Reset the data to not contain state. We have to call init() - // again in order to reset the FieldReaders. - d.state = nil - d.init() - - // Perform the diff again - for k, schema := range m { - err := m.diff(k, schema, result2, d, false) - if err != nil { - return nil, err - } - } - - // Re-run customization - if !result2.DestroyTainted && customizeDiff != nil { - mc := m.DeepCopy() - rd := newResourceDiff(mc, c, d.state, result2) - if err := customizeDiff(rd, meta); err != nil { - return nil, err - } - for _, k := range rd.UpdatedKeys() { - err := m.diff(k, mc[k], result2, rd, false) - if err != nil { - return nil, err - } - } - } - - // Force all the fields to not force a new since we know what we - // want to force new. - for k, attr := range result2.Attributes { - if attr == nil { - continue - } - - if attr.RequiresNew { - attr.RequiresNew = false - } - - if s != nil { - attr.Old = s.Attributes[k] - } - } - - // Now copy in all the requires new diffs... - for k, attr := range result.Attributes { - if attr == nil { - continue - } - - newAttr, ok := result2.Attributes[k] - if !ok { - newAttr = attr - } - - if attr.RequiresNew { - newAttr.RequiresNew = true - } - - result2.Attributes[k] = newAttr - } - - // And set the diff! - result = result2 - } - - } - - // Go through and detect all of the ComputedWhens now that we've - // finished the diff. - // TODO - - if result.Empty() { - // If we don't have any diff elements, just return nil - return nil, nil - } - - return result, nil -} - -// Input implements the terraform.ResourceProvider method by asking -// for input for required configuration keys that don't have a value. -func (m schemaMap) Input( - input terraform.UIInput, - c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) { - keys := make([]string, 0, len(m)) - for k, _ := range m { - keys = append(keys, k) - } - sort.Strings(keys) - - for _, k := range keys { - v := m[k] - - // Skip things that don't require config, if that is even valid - // for a provider schema. - // Required XOR Optional must always be true to validate, so we only - // need to check one. - if v.Optional { - continue - } - - // Deprecated fields should never prompt - if v.Deprecated != "" { - continue - } - - // Skip things that have a value of some sort already - if _, ok := c.Raw[k]; ok { - continue - } - - // Skip if it has a default value - defaultValue, err := v.DefaultValue() - if err != nil { - return nil, fmt.Errorf("%s: error loading default: %s", k, err) - } - if defaultValue != nil { - continue - } - - var value interface{} - switch v.Type { - case TypeBool, TypeInt, TypeFloat, TypeSet, TypeList: - continue - case TypeString: - value, err = m.inputString(input, k, v) - default: - panic(fmt.Sprintf("Unknown type for input: %#v", v.Type)) - } - - if err != nil { - return nil, fmt.Errorf( - "%s: %s", k, err) - } - - c.Config[k] = value - } - - return c, nil -} - -// Validate validates the configuration against this schema mapping. -func (m schemaMap) Validate(c *terraform.ResourceConfig) ([]string, []error) { - return m.validateObject("", m, c) -} - -// InternalValidate validates the format of this schema. This should be called -// from a unit test (and not in user-path code) to verify that a schema -// is properly built. -func (m schemaMap) InternalValidate(topSchemaMap schemaMap) error { - return m.internalValidate(topSchemaMap, false) -} - -func (m schemaMap) internalValidate(topSchemaMap schemaMap, attrsOnly bool) error { - if topSchemaMap == nil { - topSchemaMap = m - } - for k, v := range m { - if v.Type == TypeInvalid { - return fmt.Errorf("%s: Type must be specified", k) - } - - if v.Optional && v.Required { - return fmt.Errorf("%s: Optional or Required must be set, not both", k) - } - - if v.Required && v.Computed { - return fmt.Errorf("%s: Cannot be both Required and Computed", k) - } - - if !v.Required && !v.Optional && !v.Computed { - return fmt.Errorf("%s: One of optional, required, or computed must be set", k) - } - - computedOnly := v.Computed && !v.Optional - - switch v.ConfigMode { - case SchemaConfigModeBlock: - if _, ok := v.Elem.(*Resource); !ok { - return fmt.Errorf("%s: ConfigMode of block is allowed only when Elem is *schema.Resource", k) - } - if attrsOnly { - return fmt.Errorf("%s: ConfigMode of block cannot be used in child of schema with ConfigMode of attribute", k) - } - if computedOnly { - return fmt.Errorf("%s: ConfigMode of block cannot be used for computed schema", k) - } - case SchemaConfigModeAttr: - // anything goes - case SchemaConfigModeAuto: - // Since "Auto" for Elem: *Resource would create a nested block, - // and that's impossible inside an attribute, we require it to be - // explicitly overridden as mode "Attr" for clarity. - if _, ok := v.Elem.(*Resource); ok { - if attrsOnly { - return fmt.Errorf("%s: in *schema.Resource with ConfigMode of attribute, so must also have ConfigMode of attribute", k) - } - } - default: - return fmt.Errorf("%s: invalid ConfigMode value", k) - } - - if v.Computed && v.Default != nil { - return fmt.Errorf("%s: Default must be nil if computed", k) - } - - if v.Required && v.Default != nil { - return fmt.Errorf("%s: Default cannot be set with Required", k) - } - - if len(v.ComputedWhen) > 0 && !v.Computed { - return fmt.Errorf("%s: ComputedWhen can only be set with Computed", k) - } - - if len(v.ConflictsWith) > 0 && v.Required { - return fmt.Errorf("%s: ConflictsWith cannot be set with Required", k) - } - - if len(v.ConflictsWith) > 0 { - for _, key := range v.ConflictsWith { - parts := strings.Split(key, ".") - sm := topSchemaMap - var target *Schema - for _, part := range parts { - // Skip index fields - if _, err := strconv.Atoi(part); err == nil { - continue - } - - var ok bool - if target, ok = sm[part]; !ok { - return fmt.Errorf("%s: ConflictsWith references unknown attribute (%s) at part (%s)", k, key, part) - } - - if subResource, ok := target.Elem.(*Resource); ok { - sm = schemaMap(subResource.Schema) - } - } - if target == nil { - return fmt.Errorf("%s: ConflictsWith cannot find target attribute (%s), sm: %#v", k, key, sm) - } - if target.Required { - return fmt.Errorf("%s: ConflictsWith cannot contain Required attribute (%s)", k, key) - } - - if len(target.ComputedWhen) > 0 { - return fmt.Errorf("%s: ConflictsWith cannot contain Computed(When) attribute (%s)", k, key) - } - } - } - - if v.Type == TypeList || v.Type == TypeSet { - if v.Elem == nil { - return fmt.Errorf("%s: Elem must be set for lists", k) - } - - if v.Default != nil { - return fmt.Errorf("%s: Default is not valid for lists or sets", k) - } - - if v.Type != TypeSet && v.Set != nil { - return fmt.Errorf("%s: Set can only be set for TypeSet", k) - } - - switch t := v.Elem.(type) { - case *Resource: - attrsOnly := attrsOnly || v.ConfigMode == SchemaConfigModeAttr - - if err := schemaMap(t.Schema).internalValidate(topSchemaMap, attrsOnly); err != nil { - return err - } - case *Schema: - bad := t.Computed || t.Optional || t.Required - if bad { - return fmt.Errorf( - "%s: Elem must have only Type set", k) - } - } - } else { - if v.MaxItems > 0 || v.MinItems > 0 { - return fmt.Errorf("%s: MaxItems and MinItems are only supported on lists or sets", k) - } - } - - // Computed-only field - if v.Computed && !v.Optional { - if v.ValidateFunc != nil { - return fmt.Errorf("%s: ValidateFunc is for validating user input, "+ - "there's nothing to validate on computed-only field", k) - } - if v.DiffSuppressFunc != nil { - return fmt.Errorf("%s: DiffSuppressFunc is for suppressing differences"+ - " between config and state representation. "+ - "There is no config for computed-only field, nothing to compare.", k) - } - } - - if v.ValidateFunc != nil { - switch v.Type { - case TypeList, TypeSet: - return fmt.Errorf("%s: ValidateFunc is not yet supported on lists or sets.", k) - } - } - - if v.Deprecated == "" && v.Removed == "" { - if !isValidFieldName(k) { - return fmt.Errorf("%s: Field name may only contain lowercase alphanumeric characters & underscores.", k) - } - } - } - - return nil -} - -func isValidFieldName(name string) bool { - re := regexp.MustCompile("^[a-z0-9_]+$") - return re.MatchString(name) -} - -// resourceDiffer is an interface that is used by the private diff functions. -// This helps facilitate diff logic for both ResourceData and ResoureDiff with -// minimal divergence in code. -type resourceDiffer interface { - diffChange(string) (interface{}, interface{}, bool, bool, bool) - Get(string) interface{} - GetChange(string) (interface{}, interface{}) - GetOk(string) (interface{}, bool) - HasChange(string) bool - Id() string -} - -func (m schemaMap) diff( - k string, - schema *Schema, - diff *terraform.InstanceDiff, - d resourceDiffer, - all bool) error { - - unsupressedDiff := new(terraform.InstanceDiff) - unsupressedDiff.Attributes = make(map[string]*terraform.ResourceAttrDiff) - - var err error - switch schema.Type { - case TypeBool, TypeInt, TypeFloat, TypeString: - err = m.diffString(k, schema, unsupressedDiff, d, all) - case TypeList: - err = m.diffList(k, schema, unsupressedDiff, d, all) - case TypeMap: - err = m.diffMap(k, schema, unsupressedDiff, d, all) - case TypeSet: - err = m.diffSet(k, schema, unsupressedDiff, d, all) - default: - err = fmt.Errorf("%s: unknown type %#v", k, schema.Type) - } - - for attrK, attrV := range unsupressedDiff.Attributes { - switch rd := d.(type) { - case *ResourceData: - if schema.DiffSuppressFunc != nil && attrV != nil && - schema.DiffSuppressFunc(attrK, attrV.Old, attrV.New, rd) { - // If this attr diff is suppressed, we may still need it in the - // overall diff if it's contained within a set. Rather than - // dropping the diff, make it a NOOP. - if !all { - continue - } - - attrV = &terraform.ResourceAttrDiff{ - Old: attrV.Old, - New: attrV.Old, - } - } - } - diff.Attributes[attrK] = attrV - } - - return err -} - -func (m schemaMap) diffList( - k string, - schema *Schema, - diff *terraform.InstanceDiff, - d resourceDiffer, - all bool) error { - o, n, _, computedList, customized := d.diffChange(k) - if computedList { - n = nil - } - nSet := n != nil - - // If we have an old value and no new value is set or will be - // computed once all variables can be interpolated and we're - // computed, then nothing has changed. - if o != nil && n == nil && !computedList && schema.Computed { - return nil - } - - if o == nil { - o = []interface{}{} - } - if n == nil { - n = []interface{}{} - } - if s, ok := o.(*Set); ok { - o = s.List() - } - if s, ok := n.(*Set); ok { - n = s.List() - } - os := o.([]interface{}) - vs := n.([]interface{}) - - // If the new value was set, and the two are equal, then we're done. - // We have to do this check here because sets might be NOT - // reflect.DeepEqual so we need to wait until we get the []interface{} - if !all && nSet && reflect.DeepEqual(os, vs) { - return nil - } - - // Get the counts - oldLen := len(os) - newLen := len(vs) - oldStr := strconv.FormatInt(int64(oldLen), 10) - - // If the whole list is computed, then say that the # is computed - if computedList { - diff.Attributes[k+".#"] = &terraform.ResourceAttrDiff{ - Old: oldStr, - NewComputed: true, - RequiresNew: schema.ForceNew, - } - return nil - } - - // If the counts are not the same, then record that diff - changed := oldLen != newLen - computed := oldLen == 0 && newLen == 0 && schema.Computed - if changed || computed || all { - countSchema := &Schema{ - Type: TypeInt, - Computed: schema.Computed, - ForceNew: schema.ForceNew, - } - - newStr := "" - if !computed { - newStr = strconv.FormatInt(int64(newLen), 10) - } else { - oldStr = "" - } - - diff.Attributes[k+".#"] = countSchema.finalizeDiff( - &terraform.ResourceAttrDiff{ - Old: oldStr, - New: newStr, - }, - customized, - ) - } - - // Figure out the maximum - maxLen := oldLen - if newLen > maxLen { - maxLen = newLen - } - - switch t := schema.Elem.(type) { - case *Resource: - // This is a complex resource - for i := 0; i < maxLen; i++ { - for k2, schema := range t.Schema { - subK := fmt.Sprintf("%s.%d.%s", k, i, k2) - err := m.diff(subK, schema, diff, d, all) - if err != nil { - return err - } - } - } - case *Schema: - // Copy the schema so that we can set Computed/ForceNew from - // the parent schema (the TypeList). - t2 := *t - t2.ForceNew = schema.ForceNew - - // This is just a primitive element, so go through each and - // just diff each. - for i := 0; i < maxLen; i++ { - subK := fmt.Sprintf("%s.%d", k, i) - err := m.diff(subK, &t2, diff, d, all) - if err != nil { - return err - } - } - default: - return fmt.Errorf("%s: unknown element type (internal)", k) - } - - return nil -} - -func (m schemaMap) diffMap( - k string, - schema *Schema, - diff *terraform.InstanceDiff, - d resourceDiffer, - all bool) error { - prefix := k + "." - - // First get all the values from the state - var stateMap, configMap map[string]string - o, n, _, nComputed, customized := d.diffChange(k) - if err := mapstructure.WeakDecode(o, &stateMap); err != nil { - return fmt.Errorf("%s: %s", k, err) - } - if err := mapstructure.WeakDecode(n, &configMap); err != nil { - return fmt.Errorf("%s: %s", k, err) - } - - // Keep track of whether the state _exists_ at all prior to clearing it - stateExists := o != nil - - // Delete any count values, since we don't use those - delete(configMap, "%") - delete(stateMap, "%") - - // Check if the number of elements has changed. - oldLen, newLen := len(stateMap), len(configMap) - changed := oldLen != newLen - if oldLen != 0 && newLen == 0 && schema.Computed { - changed = false - } - - // It is computed if we have no old value, no new value, the schema - // says it is computed, and it didn't exist in the state before. The - // last point means: if it existed in the state, even empty, then it - // has already been computed. - computed := oldLen == 0 && newLen == 0 && schema.Computed && !stateExists - - // If the count has changed or we're computed, then add a diff for the - // count. "nComputed" means that the new value _contains_ a value that - // is computed. We don't do granular diffs for this yet, so we mark the - // whole map as computed. - if changed || computed || nComputed { - countSchema := &Schema{ - Type: TypeInt, - Computed: schema.Computed || nComputed, - ForceNew: schema.ForceNew, - } - - oldStr := strconv.FormatInt(int64(oldLen), 10) - newStr := "" - if !computed && !nComputed { - newStr = strconv.FormatInt(int64(newLen), 10) - } else { - oldStr = "" - } - - diff.Attributes[k+".%"] = countSchema.finalizeDiff( - &terraform.ResourceAttrDiff{ - Old: oldStr, - New: newStr, - }, - customized, - ) - } - - // If the new map is nil and we're computed, then ignore it. - if n == nil && schema.Computed { - return nil - } - - // Now we compare, preferring values from the config map - for k, v := range configMap { - old, ok := stateMap[k] - delete(stateMap, k) - - if old == v && ok && !all { - continue - } - - diff.Attributes[prefix+k] = schema.finalizeDiff( - &terraform.ResourceAttrDiff{ - Old: old, - New: v, - }, - customized, - ) - } - for k, v := range stateMap { - diff.Attributes[prefix+k] = schema.finalizeDiff( - &terraform.ResourceAttrDiff{ - Old: v, - NewRemoved: true, - }, - customized, - ) - } - - return nil -} - -func (m schemaMap) diffSet( - k string, - schema *Schema, - diff *terraform.InstanceDiff, - d resourceDiffer, - all bool) error { - - o, n, _, computedSet, customized := d.diffChange(k) - if computedSet { - n = nil - } - nSet := n != nil - - // If we have an old value and no new value is set or will be - // computed once all variables can be interpolated and we're - // computed, then nothing has changed. - if o != nil && n == nil && !computedSet && schema.Computed { - return nil - } - - if o == nil { - o = schema.ZeroValue().(*Set) - } - if n == nil { - n = schema.ZeroValue().(*Set) - } - os := o.(*Set) - ns := n.(*Set) - - // If the new value was set, compare the listCode's to determine if - // the two are equal. Comparing listCode's instead of the actual values - // is needed because there could be computed values in the set which - // would result in false positives while comparing. - if !all && nSet && reflect.DeepEqual(os.listCode(), ns.listCode()) { - return nil - } - - // Get the counts - oldLen := os.Len() - newLen := ns.Len() - oldStr := strconv.Itoa(oldLen) - newStr := strconv.Itoa(newLen) - - // Build a schema for our count - countSchema := &Schema{ - Type: TypeInt, - Computed: schema.Computed, - ForceNew: schema.ForceNew, - } - - // If the set computed then say that the # is computed - if computedSet || schema.Computed && !nSet { - // If # already exists, equals 0 and no new set is supplied, there - // is nothing to record in the diff - count, ok := d.GetOk(k + ".#") - if ok && count.(int) == 0 && !nSet && !computedSet { - return nil - } - - // Set the count but make sure that if # does not exist, we don't - // use the zeroed value - countStr := strconv.Itoa(count.(int)) - if !ok { - countStr = "" - } - - diff.Attributes[k+".#"] = countSchema.finalizeDiff( - &terraform.ResourceAttrDiff{ - Old: countStr, - NewComputed: true, - }, - customized, - ) - return nil - } - - // If the counts are not the same, then record that diff - changed := oldLen != newLen - if changed || all { - diff.Attributes[k+".#"] = countSchema.finalizeDiff( - &terraform.ResourceAttrDiff{ - Old: oldStr, - New: newStr, - }, - customized, - ) - } - - // Build the list of codes that will make up our set. This is the - // removed codes as well as all the codes in the new codes. - codes := make([][]string, 2) - codes[0] = os.Difference(ns).listCode() - codes[1] = ns.listCode() - for _, list := range codes { - for _, code := range list { - switch t := schema.Elem.(type) { - case *Resource: - // This is a complex resource - for k2, schema := range t.Schema { - subK := fmt.Sprintf("%s.%s.%s", k, code, k2) - err := m.diff(subK, schema, diff, d, true) - if err != nil { - return err - } - } - case *Schema: - // Copy the schema so that we can set Computed/ForceNew from - // the parent schema (the TypeSet). - t2 := *t - t2.ForceNew = schema.ForceNew - - // This is just a primitive element, so go through each and - // just diff each. - subK := fmt.Sprintf("%s.%s", k, code) - err := m.diff(subK, &t2, diff, d, true) - if err != nil { - return err - } - default: - return fmt.Errorf("%s: unknown element type (internal)", k) - } - } - } - - return nil -} - -func (m schemaMap) diffString( - k string, - schema *Schema, - diff *terraform.InstanceDiff, - d resourceDiffer, - all bool) error { - var originalN interface{} - var os, ns string - o, n, _, computed, customized := d.diffChange(k) - if schema.StateFunc != nil && n != nil { - originalN = n - n = schema.StateFunc(n) - } - nraw := n - if nraw == nil && o != nil { - nraw = schema.Type.Zero() - } - if err := mapstructure.WeakDecode(o, &os); err != nil { - return fmt.Errorf("%s: %s", k, err) - } - if err := mapstructure.WeakDecode(nraw, &ns); err != nil { - return fmt.Errorf("%s: %s", k, err) - } - - if os == ns && !all && !computed { - // They're the same value. If there old value is not blank or we - // have an ID, then return right away since we're already setup. - if os != "" || d.Id() != "" { - return nil - } - - // Otherwise, only continue if we're computed - if !schema.Computed { - return nil - } - } - - removed := false - if o != nil && n == nil && !computed { - removed = true - } - if removed && schema.Computed { - return nil - } - - diff.Attributes[k] = schema.finalizeDiff( - &terraform.ResourceAttrDiff{ - Old: os, - New: ns, - NewExtra: originalN, - NewRemoved: removed, - NewComputed: computed, - }, - customized, - ) - - return nil -} - -func (m schemaMap) inputString( - input terraform.UIInput, - k string, - schema *Schema) (interface{}, error) { - result, err := input.Input(context.Background(), &terraform.InputOpts{ - Id: k, - Query: k, - Description: schema.Description, - Default: schema.InputDefault, - }) - - return result, err -} - -func (m schemaMap) validate( - k string, - schema *Schema, - c *terraform.ResourceConfig) ([]string, []error) { - raw, ok := c.Get(k) - if !ok && schema.DefaultFunc != nil { - // We have a dynamic default. Check if we have a value. - var err error - raw, err = schema.DefaultFunc() - if err != nil { - return nil, []error{fmt.Errorf( - "%q, error loading default: %s", k, err)} - } - - // We're okay as long as we had a value set - ok = raw != nil - } - if !ok { - if schema.Required { - return nil, []error{fmt.Errorf( - "%q: required field is not set", k)} - } - - return nil, nil - } - - if !schema.Required && !schema.Optional { - // This is a computed-only field - return nil, []error{fmt.Errorf( - "%q: this field cannot be set", k)} - } - - // If the value is unknown then we can't validate it yet. - // In particular, this avoids spurious type errors where downstream - // validation code sees UnknownVariableValue as being just a string. - // The SDK has to allow the unknown value through initially, so that - // Required fields set via an interpolated value are accepted. - if !isWhollyKnown(raw) { - if schema.Deprecated != "" { - return []string{fmt.Sprintf("%q: [DEPRECATED] %s", k, schema.Deprecated)}, nil - } - return nil, nil - } - - err := m.validateConflictingAttributes(k, schema, c) - if err != nil { - return nil, []error{err} - } - - return m.validateType(k, raw, schema, c) -} - -// isWhollyKnown returns false if the argument contains an UnknownVariableValue -func isWhollyKnown(raw interface{}) bool { - switch raw := raw.(type) { - case string: - if raw == hcl2shim.UnknownVariableValue { - return false - } - case []interface{}: - for _, v := range raw { - if !isWhollyKnown(v) { - return false - } - } - case map[string]interface{}: - for _, v := range raw { - if !isWhollyKnown(v) { - return false - } - } - } - return true -} -func (m schemaMap) validateConflictingAttributes( - k string, - schema *Schema, - c *terraform.ResourceConfig) error { - - if len(schema.ConflictsWith) == 0 { - return nil - } - - for _, conflictingKey := range schema.ConflictsWith { - if raw, ok := c.Get(conflictingKey); ok { - if raw == hcl2shim.UnknownVariableValue { - // An unknown value might become unset (null) once known, so - // we must defer validation until it's known. - continue - } - return fmt.Errorf( - "%q: conflicts with %s", k, conflictingKey) - } - } - - return nil -} - -func (m schemaMap) validateList( - k string, - raw interface{}, - schema *Schema, - c *terraform.ResourceConfig) ([]string, []error) { - // first check if the list is wholly unknown - if s, ok := raw.(string); ok { - if s == hcl2shim.UnknownVariableValue { - return nil, nil - } - } - - // schemaMap can't validate nil - if raw == nil { - return nil, nil - } - - // We use reflection to verify the slice because you can't - // case to []interface{} unless the slice is exactly that type. - rawV := reflect.ValueOf(raw) - - // If we support promotion and the raw value isn't a slice, wrap - // it in []interface{} and check again. - if schema.PromoteSingle && rawV.Kind() != reflect.Slice { - raw = []interface{}{raw} - rawV = reflect.ValueOf(raw) - } - - if rawV.Kind() != reflect.Slice { - return nil, []error{fmt.Errorf( - "%s: should be a list", k)} - } - - // We can't validate list length if this came from a dynamic block. - // Since there's no way to determine if something was from a dynamic block - // at this point, we're going to skip validation in the new protocol if - // there are any unknowns. Validate will eventually be called again once - // all values are known. - if isProto5() && !isWhollyKnown(raw) { - return nil, nil - } - - // Validate length - if schema.MaxItems > 0 && rawV.Len() > schema.MaxItems { - return nil, []error{fmt.Errorf( - "%s: attribute supports %d item maximum, config has %d declared", k, schema.MaxItems, rawV.Len())} - } - - if schema.MinItems > 0 && rawV.Len() < schema.MinItems { - return nil, []error{fmt.Errorf( - "%s: attribute supports %d item as a minimum, config has %d declared", k, schema.MinItems, rawV.Len())} - } - - // Now build the []interface{} - raws := make([]interface{}, rawV.Len()) - for i, _ := range raws { - raws[i] = rawV.Index(i).Interface() - } - - var ws []string - var es []error - for i, raw := range raws { - key := fmt.Sprintf("%s.%d", k, i) - - // Reify the key value from the ResourceConfig. - // If the list was computed we have all raw values, but some of these - // may be known in the config, and aren't individually marked as Computed. - if r, ok := c.Get(key); ok { - raw = r - } - - var ws2 []string - var es2 []error - switch t := schema.Elem.(type) { - case *Resource: - // This is a sub-resource - ws2, es2 = m.validateObject(key, t.Schema, c) - case *Schema: - ws2, es2 = m.validateType(key, raw, t, c) - } - - if len(ws2) > 0 { - ws = append(ws, ws2...) - } - if len(es2) > 0 { - es = append(es, es2...) - } - } - - return ws, es -} - -func (m schemaMap) validateMap( - k string, - raw interface{}, - schema *Schema, - c *terraform.ResourceConfig) ([]string, []error) { - // first check if the list is wholly unknown - if s, ok := raw.(string); ok { - if s == hcl2shim.UnknownVariableValue { - return nil, nil - } - } - - // schemaMap can't validate nil - if raw == nil { - return nil, nil - } - // We use reflection to verify the slice because you can't - // case to []interface{} unless the slice is exactly that type. - rawV := reflect.ValueOf(raw) - switch rawV.Kind() { - case reflect.String: - // If raw and reified are equal, this is a string and should - // be rejected. - reified, reifiedOk := c.Get(k) - if reifiedOk && raw == reified && !c.IsComputed(k) { - return nil, []error{fmt.Errorf("%s: should be a map", k)} - } - // Otherwise it's likely raw is an interpolation. - return nil, nil - case reflect.Map: - case reflect.Slice: - default: - return nil, []error{fmt.Errorf("%s: should be a map", k)} - } - - // If it is not a slice, validate directly - if rawV.Kind() != reflect.Slice { - mapIface := rawV.Interface() - if _, errs := validateMapValues(k, mapIface.(map[string]interface{}), schema); len(errs) > 0 { - return nil, errs - } - if schema.ValidateFunc != nil { - return schema.ValidateFunc(mapIface, k) - } - return nil, nil - } - - // It is a slice, verify that all the elements are maps - raws := make([]interface{}, rawV.Len()) - for i, _ := range raws { - raws[i] = rawV.Index(i).Interface() - } - - for _, raw := range raws { - v := reflect.ValueOf(raw) - if v.Kind() != reflect.Map { - return nil, []error{fmt.Errorf( - "%s: should be a map", k)} - } - mapIface := v.Interface() - if _, errs := validateMapValues(k, mapIface.(map[string]interface{}), schema); len(errs) > 0 { - return nil, errs - } - } - - if schema.ValidateFunc != nil { - validatableMap := make(map[string]interface{}) - for _, raw := range raws { - for k, v := range raw.(map[string]interface{}) { - validatableMap[k] = v - } - } - - return schema.ValidateFunc(validatableMap, k) - } - - return nil, nil -} - -func validateMapValues(k string, m map[string]interface{}, schema *Schema) ([]string, []error) { - for key, raw := range m { - valueType, err := getValueType(k, schema) - if err != nil { - return nil, []error{err} - } - - switch valueType { - case TypeBool: - var n bool - if err := mapstructure.WeakDecode(raw, &n); err != nil { - return nil, []error{fmt.Errorf("%s (%s): %s", k, key, err)} - } - case TypeInt: - var n int - if err := mapstructure.WeakDecode(raw, &n); err != nil { - return nil, []error{fmt.Errorf("%s (%s): %s", k, key, err)} - } - case TypeFloat: - var n float64 - if err := mapstructure.WeakDecode(raw, &n); err != nil { - return nil, []error{fmt.Errorf("%s (%s): %s", k, key, err)} - } - case TypeString: - var n string - if err := mapstructure.WeakDecode(raw, &n); err != nil { - return nil, []error{fmt.Errorf("%s (%s): %s", k, key, err)} - } - default: - panic(fmt.Sprintf("Unknown validation type: %#v", schema.Type)) - } - } - return nil, nil -} - -func getValueType(k string, schema *Schema) (ValueType, error) { - if schema.Elem == nil { - return TypeString, nil - } - if vt, ok := schema.Elem.(ValueType); ok { - return vt, nil - } - - // If a Schema is provided to a Map, we use the Type of that schema - // as the type for each element in the Map. - if s, ok := schema.Elem.(*Schema); ok { - return s.Type, nil - } - - if _, ok := schema.Elem.(*Resource); ok { - // TODO: We don't actually support this (yet) - // but silently pass the validation, until we decide - // how to handle nested structures in maps - return TypeString, nil - } - return 0, fmt.Errorf("%s: unexpected map value type: %#v", k, schema.Elem) -} - -func (m schemaMap) validateObject( - k string, - schema map[string]*Schema, - c *terraform.ResourceConfig) ([]string, []error) { - raw, _ := c.Get(k) - - // schemaMap can't validate nil - if raw == nil { - return nil, nil - } - - if _, ok := raw.(map[string]interface{}); !ok && !c.IsComputed(k) { - return nil, []error{fmt.Errorf( - "%s: expected object, got %s", - k, reflect.ValueOf(raw).Kind())} - } - - var ws []string - var es []error - for subK, s := range schema { - key := subK - if k != "" { - key = fmt.Sprintf("%s.%s", k, subK) - } - - ws2, es2 := m.validate(key, s, c) - if len(ws2) > 0 { - ws = append(ws, ws2...) - } - if len(es2) > 0 { - es = append(es, es2...) - } - } - - // Detect any extra/unknown keys and report those as errors. - if m, ok := raw.(map[string]interface{}); ok { - for subk, _ := range m { - if _, ok := schema[subk]; !ok { - if subk == TimeoutsConfigKey { - continue - } - es = append(es, fmt.Errorf( - "%s: invalid or unknown key: %s", k, subk)) - } - } - } - - return ws, es -} - -func (m schemaMap) validatePrimitive( - k string, - raw interface{}, - schema *Schema, - c *terraform.ResourceConfig) ([]string, []error) { - - // a nil value shouldn't happen in the old protocol, and in the new - // protocol the types have already been validated. Either way, we can't - // reflect on nil, so don't panic. - if raw == nil { - return nil, nil - } - - // Catch if the user gave a complex type where a primitive was - // expected, so we can return a friendly error message that - // doesn't contain Go type system terminology. - switch reflect.ValueOf(raw).Type().Kind() { - case reflect.Slice: - return nil, []error{ - fmt.Errorf("%s must be a single value, not a list", k), - } - case reflect.Map: - return nil, []error{ - fmt.Errorf("%s must be a single value, not a map", k), - } - default: // ok - } - - if c.IsComputed(k) { - // If the key is being computed, then it is not an error as - // long as it's not a slice or map. - return nil, nil - } - - var decoded interface{} - switch schema.Type { - case TypeBool: - // Verify that we can parse this as the correct type - var n bool - if err := mapstructure.WeakDecode(raw, &n); err != nil { - return nil, []error{fmt.Errorf("%s: %s", k, err)} - } - decoded = n - case TypeInt: - switch { - case isProto5(): - // We need to verify the type precisely, because WeakDecode will - // decode a float as an integer. - - // the config shims only use int for integral number values - if v, ok := raw.(int); ok { - decoded = v - } else { - return nil, []error{fmt.Errorf("%s: must be a whole number, got %v", k, raw)} - } - default: - // Verify that we can parse this as an int - var n int - if err := mapstructure.WeakDecode(raw, &n); err != nil { - return nil, []error{fmt.Errorf("%s: %s", k, err)} - } - decoded = n - } - case TypeFloat: - // Verify that we can parse this as an int - var n float64 - if err := mapstructure.WeakDecode(raw, &n); err != nil { - return nil, []error{fmt.Errorf("%s: %s", k, err)} - } - decoded = n - case TypeString: - // Verify that we can parse this as a string - var n string - if err := mapstructure.WeakDecode(raw, &n); err != nil { - return nil, []error{fmt.Errorf("%s: %s", k, err)} - } - decoded = n - default: - panic(fmt.Sprintf("Unknown validation type: %#v", schema.Type)) - } - - if schema.ValidateFunc != nil { - return schema.ValidateFunc(decoded, k) - } - - return nil, nil -} - -func (m schemaMap) validateType( - k string, - raw interface{}, - schema *Schema, - c *terraform.ResourceConfig) ([]string, []error) { - var ws []string - var es []error - switch schema.Type { - case TypeSet, TypeList: - ws, es = m.validateList(k, raw, schema, c) - case TypeMap: - ws, es = m.validateMap(k, raw, schema, c) - default: - ws, es = m.validatePrimitive(k, raw, schema, c) - } - - if schema.Deprecated != "" { - ws = append(ws, fmt.Sprintf( - "%q: [DEPRECATED] %s", k, schema.Deprecated)) - } - - if schema.Removed != "" { - es = append(es, fmt.Errorf( - "%q: [REMOVED] %s", k, schema.Removed)) - } - - return ws, es -} - -// Zero returns the zero value for a type. -func (t ValueType) Zero() interface{} { - switch t { - case TypeInvalid: - return nil - case TypeBool: - return false - case TypeInt: - return 0 - case TypeFloat: - return 0.0 - case TypeString: - return "" - case TypeList: - return []interface{}{} - case TypeMap: - return map[string]interface{}{} - case TypeSet: - return new(Set) - case typeObject: - return map[string]interface{}{} - default: - panic(fmt.Sprintf("unknown type %s", t)) - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/schema_test.go b/vendor/github.com/hashicorp/terraform/helper/schema/schema_test.go deleted file mode 100644 index 4199f3dd..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/schema_test.go +++ /dev/null @@ -1,5558 +0,0 @@ -package schema - -import ( - "bytes" - "errors" - "fmt" - "os" - "reflect" - "sort" - "strconv" - "strings" - "testing" - - "github.com/hashicorp/terraform/configs/hcl2shim" - "github.com/hashicorp/terraform/helper/hashcode" - "github.com/hashicorp/terraform/terraform" -) - -func TestEnvDefaultFunc(t *testing.T) { - key := "TF_TEST_ENV_DEFAULT_FUNC" - defer os.Unsetenv(key) - - f := EnvDefaultFunc(key, "42") - if err := os.Setenv(key, "foo"); err != nil { - t.Fatalf("err: %s", err) - } - - actual, err := f() - if err != nil { - t.Fatalf("err: %s", err) - } - if actual != "foo" { - t.Fatalf("bad: %#v", actual) - } - - if err := os.Unsetenv(key); err != nil { - t.Fatalf("err: %s", err) - } - - actual, err = f() - if err != nil { - t.Fatalf("err: %s", err) - } - if actual != "42" { - t.Fatalf("bad: %#v", actual) - } -} - -func TestMultiEnvDefaultFunc(t *testing.T) { - keys := []string{ - "TF_TEST_MULTI_ENV_DEFAULT_FUNC1", - "TF_TEST_MULTI_ENV_DEFAULT_FUNC2", - } - defer func() { - for _, k := range keys { - os.Unsetenv(k) - } - }() - - // Test that the first key is returned first - f := MultiEnvDefaultFunc(keys, "42") - if err := os.Setenv(keys[0], "foo"); err != nil { - t.Fatalf("err: %s", err) - } - - actual, err := f() - if err != nil { - t.Fatalf("err: %s", err) - } - if actual != "foo" { - t.Fatalf("bad: %#v", actual) - } - - if err := os.Unsetenv(keys[0]); err != nil { - t.Fatalf("err: %s", err) - } - - // Test that the second key is returned if the first one is empty - f = MultiEnvDefaultFunc(keys, "42") - if err := os.Setenv(keys[1], "foo"); err != nil { - t.Fatalf("err: %s", err) - } - - actual, err = f() - if err != nil { - t.Fatalf("err: %s", err) - } - if actual != "foo" { - t.Fatalf("bad: %#v", actual) - } - - if err := os.Unsetenv(keys[1]); err != nil { - t.Fatalf("err: %s", err) - } - - // Test that the default value is returned when no keys are set - actual, err = f() - if err != nil { - t.Fatalf("err: %s", err) - } - if actual != "42" { - t.Fatalf("bad: %#v", actual) - } -} - -func TestValueType_Zero(t *testing.T) { - cases := []struct { - Type ValueType - Value interface{} - }{ - {TypeBool, false}, - {TypeInt, 0}, - {TypeFloat, 0.0}, - {TypeString, ""}, - {TypeList, []interface{}{}}, - {TypeMap, map[string]interface{}{}}, - {TypeSet, new(Set)}, - } - - for i, tc := range cases { - actual := tc.Type.Zero() - if !reflect.DeepEqual(actual, tc.Value) { - t.Fatalf("%d: %#v != %#v", i, actual, tc.Value) - } - } -} - -func TestSchemaMap_Diff(t *testing.T) { - cases := []struct { - Name string - Schema map[string]*Schema - State *terraform.InstanceState - Config map[string]interface{} - CustomizeDiff CustomizeDiffFunc - Diff *terraform.InstanceDiff - Err bool - }{ - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "availability_zone": "foo", - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{}, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - NewComputed: true, - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "foo", - }, - - Config: map[string]interface{}{}, - - Diff: nil, - - Err: false, - }, - - { - Name: "Computed, but set in config", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - - Config: map[string]interface{}{ - "availability_zone": "bar", - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "foo", - New: "bar", - }, - }, - }, - - Err: false, - }, - - { - Name: "Default", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Default: "foo", - }, - }, - - State: nil, - - Config: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - }, - }, - }, - - Err: false, - }, - - { - Name: "DefaultFunc, value", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - DefaultFunc: func() (interface{}, error) { - return "foo", nil - }, - }, - }, - - State: nil, - - Config: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - }, - }, - }, - - Err: false, - }, - - { - Name: "DefaultFunc, configuration set", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - DefaultFunc: func() (interface{}, error) { - return "foo", nil - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "availability_zone": "bar", - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "bar", - }, - }, - }, - - Err: false, - }, - - { - Name: "String with StateFunc", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - StateFunc: func(a interface{}) string { - return a.(string) + "!" - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "availability_zone": "foo", - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo!", - NewExtra: "foo", - }, - }, - }, - - Err: false, - }, - - { - Name: "StateFunc not called with nil value", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - StateFunc: func(a interface{}) string { - t.Fatalf("should not get here!") - return "" - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{}, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Variable computed", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "availability_zone": hcl2shim.UnknownVariableValue, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: hcl2shim.UnknownVariableValue, - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Int decode", - Schema: map[string]*Schema{ - "port": &Schema{ - Type: TypeInt, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "port": 27, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "port": &terraform.ResourceAttrDiff{ - Old: "", - New: "27", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "bool decode", - Schema: map[string]*Schema{ - "port": &Schema{ - Type: TypeBool, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "port": false, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "port": &terraform.ResourceAttrDiff{ - Old: "", - New: "false", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Bool", - Schema: map[string]*Schema{ - "delete": &Schema{ - Type: TypeBool, - Optional: true, - Default: false, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "delete": "false", - }, - }, - - Config: nil, - - Diff: nil, - - Err: false, - }, - - { - Name: "List decode", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ports": []interface{}{1, 2, 5}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "3", - }, - "ports.0": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "ports.1": &terraform.ResourceAttrDiff{ - Old: "", - New: "2", - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - }, - }, - }, - - Err: false, - }, - - { - Name: "List decode with promotion", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - PromoteSingle: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ports": "5", - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - "ports.0": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - }, - }, - }, - - Err: false, - }, - - { - Name: "List decode with promotion with list", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - PromoteSingle: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ports": []interface{}{"5"}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - "ports.0": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - }, - }, - }, - - Err: false, - }, - - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ports": []interface{}{1, 2, 5}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "3", - }, - "ports.0": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "ports.1": &terraform.ResourceAttrDiff{ - Old: "", - New: "2", - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - }, - }, - }, - - Err: false, - }, - - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ports": []interface{}{1, hcl2shim.UnknownVariableValue, 5}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "3", - "ports.0": "1", - "ports.1": "2", - "ports.2": "5", - }, - }, - - Config: map[string]interface{}{ - "ports": []interface{}{1, 2, 5}, - }, - - Diff: nil, - - Err: false, - }, - - { - Name: "", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "2", - "ports.0": "1", - "ports.1": "2", - }, - }, - - Config: map[string]interface{}{ - "ports": []interface{}{1, 2, 5}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "2", - New: "3", - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - }, - }, - }, - - Err: false, - }, - - { - Name: "", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - ForceNew: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ports": []interface{}{1, 2, 5}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "3", - RequiresNew: true, - }, - "ports.0": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - RequiresNew: true, - }, - "ports.1": &terraform.ResourceAttrDiff{ - Old: "", - New: "2", - RequiresNew: true, - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: nil, - - Config: map[string]interface{}{}, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "List with computed set", - Schema: map[string]*Schema{ - "config": &Schema{ - Type: TypeList, - Optional: true, - ForceNew: true, - MinItems: 1, - Elem: &Resource{ - Schema: map[string]*Schema{ - "name": { - Type: TypeString, - Required: true, - }, - - "rules": { - Type: TypeSet, - Computed: true, - Elem: &Schema{Type: TypeString}, - Set: HashString, - }, - }, - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "config": []interface{}{ - map[string]interface{}{ - "name": "hello", - }, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "config.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - RequiresNew: true, - }, - - "config.0.name": &terraform.ResourceAttrDiff{ - Old: "", - New: "hello", - }, - - "config.0.rules.#": &terraform.ResourceAttrDiff{ - Old: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Set", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Required: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ports": []interface{}{5, 2, 1}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "3", - }, - "ports.1": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "", - New: "2", - }, - "ports.5": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - }, - }, - }, - - Err: false, - }, - - { - Name: "Set", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Computed: true, - Required: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "0", - }, - }, - - Config: nil, - - Diff: nil, - - Err: false, - }, - - { - Name: "Set", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: nil, - - Config: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Set", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Required: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ports": []interface{}{"2", "5", 1}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "3", - }, - "ports.1": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "", - New: "2", - }, - "ports.5": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - }, - }, - }, - - Err: false, - }, - - { - Name: "Set", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Required: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ports": []interface{}{1, hcl2shim.UnknownVariableValue, "5"}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "", - New: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Set", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Required: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "2", - "ports.1": "1", - "ports.2": "2", - }, - }, - - Config: map[string]interface{}{ - "ports": []interface{}{5, 2, 1}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "2", - New: "3", - }, - "ports.1": &terraform.ResourceAttrDiff{ - Old: "1", - New: "1", - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "2", - New: "2", - }, - "ports.5": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - }, - }, - }, - - Err: false, - }, - - { - Name: "Set", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Required: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "2", - "ports.1": "1", - "ports.2": "2", - }, - }, - - Config: map[string]interface{}{}, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "2", - New: "0", - }, - "ports.1": &terraform.ResourceAttrDiff{ - Old: "1", - New: "0", - NewRemoved: true, - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "2", - New: "0", - NewRemoved: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Set", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "bar", - "ports.#": "1", - "ports.80": "80", - }, - }, - - Config: map[string]interface{}{}, - - Diff: nil, - - Err: false, - }, - - { - Name: "Set", - Schema: map[string]*Schema{ - "ingress": &Schema{ - Type: TypeSet, - Required: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Optional: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - }, - Set: func(v interface{}) int { - m := v.(map[string]interface{}) - ps := m["ports"].([]interface{}) - result := 0 - for _, p := range ps { - result += p.(int) - } - return result - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ingress.#": "2", - "ingress.80.ports.#": "1", - "ingress.80.ports.0": "80", - "ingress.443.ports.#": "1", - "ingress.443.ports.0": "443", - }, - }, - - Config: map[string]interface{}{ - "ingress": []interface{}{ - map[string]interface{}{ - "ports": []interface{}{443}, - }, - map[string]interface{}{ - "ports": []interface{}{80}, - }, - }, - }, - - Diff: nil, - - Err: false, - }, - - { - Name: "List of structure decode", - Schema: map[string]*Schema{ - "ingress": &Schema{ - Type: TypeList, - Required: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "from": &Schema{ - Type: TypeInt, - Required: true, - }, - }, - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ingress": []interface{}{ - map[string]interface{}{ - "from": 8080, - }, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ingress.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - "ingress.0.from": &terraform.ResourceAttrDiff{ - Old: "", - New: "8080", - }, - }, - }, - - Err: false, - }, - - { - Name: "ComputedWhen", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Computed: true, - ComputedWhen: []string{"port"}, - }, - - "port": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - "port": "80", - }, - }, - - Config: map[string]interface{}{ - "port": 80, - }, - - Diff: nil, - - Err: false, - }, - - { - Name: "", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Computed: true, - ComputedWhen: []string{"port"}, - }, - - "port": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "port": "80", - }, - }, - - Config: map[string]interface{}{ - "port": 80, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - /* TODO - { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Computed: true, - ComputedWhen: []string{"port"}, - }, - - "port": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "foo", - "port": "80", - }, - }, - - Config: map[string]interface{}{ - "port": 8080, - }, - - Diff: &terraform.ResourceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "foo", - NewComputed: true, - }, - "port": &terraform.ResourceAttrDiff{ - Old: "80", - New: "8080", - }, - }, - }, - - Err: false, - }, - */ - - { - Name: "Maps", - Schema: map[string]*Schema{ - "config_vars": &Schema{ - Type: TypeMap, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "config_vars": []interface{}{ - map[string]interface{}{ - "bar": "baz", - }, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "config_vars.%": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - - "config_vars.bar": &terraform.ResourceAttrDiff{ - Old: "", - New: "baz", - }, - }, - }, - - Err: false, - }, - - { - Name: "Maps", - Schema: map[string]*Schema{ - "config_vars": &Schema{ - Type: TypeMap, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "config_vars.foo": "bar", - }, - }, - - Config: map[string]interface{}{ - "config_vars": []interface{}{ - map[string]interface{}{ - "bar": "baz", - }, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "config_vars.foo": &terraform.ResourceAttrDiff{ - Old: "bar", - NewRemoved: true, - }, - "config_vars.bar": &terraform.ResourceAttrDiff{ - Old: "", - New: "baz", - }, - }, - }, - - Err: false, - }, - - { - Name: "Maps", - Schema: map[string]*Schema{ - "vars": &Schema{ - Type: TypeMap, - Optional: true, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "vars.foo": "bar", - }, - }, - - Config: map[string]interface{}{ - "vars": []interface{}{ - map[string]interface{}{ - "bar": "baz", - }, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "vars.foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "", - NewRemoved: true, - }, - "vars.bar": &terraform.ResourceAttrDiff{ - Old: "", - New: "baz", - }, - }, - }, - - Err: false, - }, - - { - Name: "Maps", - Schema: map[string]*Schema{ - "vars": &Schema{ - Type: TypeMap, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "vars.foo": "bar", - }, - }, - - Config: nil, - - Diff: nil, - - Err: false, - }, - - { - Name: "Maps", - Schema: map[string]*Schema{ - "config_vars": &Schema{ - Type: TypeList, - Elem: &Schema{Type: TypeMap}, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "config_vars.#": "1", - "config_vars.0.foo": "bar", - }, - }, - - Config: map[string]interface{}{ - "config_vars": []interface{}{ - map[string]interface{}{ - "bar": "baz", - }, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "config_vars.0.foo": &terraform.ResourceAttrDiff{ - Old: "bar", - NewRemoved: true, - }, - "config_vars.0.bar": &terraform.ResourceAttrDiff{ - Old: "", - New: "baz", - }, - }, - }, - - Err: false, - }, - - { - Name: "Maps", - Schema: map[string]*Schema{ - "config_vars": &Schema{ - Type: TypeList, - Elem: &Schema{Type: TypeMap}, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "config_vars.#": "1", - "config_vars.0.foo": "bar", - "config_vars.0.bar": "baz", - }, - }, - - Config: map[string]interface{}{}, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "config_vars.#": &terraform.ResourceAttrDiff{ - Old: "1", - New: "0", - }, - "config_vars.0.%": &terraform.ResourceAttrDiff{ - Old: "2", - New: "0", - }, - "config_vars.0.foo": &terraform.ResourceAttrDiff{ - Old: "bar", - NewRemoved: true, - }, - "config_vars.0.bar": &terraform.ResourceAttrDiff{ - Old: "baz", - NewRemoved: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "ForceNews", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - ForceNew: true, - }, - - "address": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "bar", - "address": "foo", - }, - }, - - Config: map[string]interface{}{ - "availability_zone": "foo", - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "foo", - RequiresNew: true, - }, - - "address": &terraform.ResourceAttrDiff{ - Old: "foo", - New: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Set", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - ForceNew: true, - }, - - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "availability_zone": "bar", - "ports.#": "1", - "ports.80": "80", - }, - }, - - Config: map[string]interface{}{ - "availability_zone": "foo", - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "foo", - RequiresNew: true, - }, - - "ports.#": &terraform.ResourceAttrDiff{ - Old: "1", - New: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Set", - Schema: map[string]*Schema{ - "instances": &Schema{ - Type: TypeSet, - Elem: &Schema{Type: TypeString}, - Optional: true, - Computed: true, - Set: func(v interface{}) int { - return len(v.(string)) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "instances.#": "0", - }, - }, - - Config: map[string]interface{}{ - "instances": []interface{}{hcl2shim.UnknownVariableValue}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "instances.#": &terraform.ResourceAttrDiff{ - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Set", - Schema: map[string]*Schema{ - "route": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "index": &Schema{ - Type: TypeInt, - Required: true, - }, - - "gateway": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - }, - Set: func(v interface{}) int { - m := v.(map[string]interface{}) - return m["index"].(int) - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "route": []interface{}{ - map[string]interface{}{ - "index": "1", - "gateway": hcl2shim.UnknownVariableValue, - }, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "route.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - "route.~1.index": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "route.~1.gateway": &terraform.ResourceAttrDiff{ - Old: "", - New: hcl2shim.UnknownVariableValue, - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Set", - Schema: map[string]*Schema{ - "route": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "index": &Schema{ - Type: TypeInt, - Required: true, - }, - - "gateway": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - }, - Set: func(v interface{}) int { - m := v.(map[string]interface{}) - return m["index"].(int) - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "route": []interface{}{ - map[string]interface{}{ - "index": "1", - "gateway": []interface{}{ - hcl2shim.UnknownVariableValue, - }, - }, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "route.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - "route.~1.index": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "route.~1.gateway.#": &terraform.ResourceAttrDiff{ - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Computed maps", - Schema: map[string]*Schema{ - "vars": &Schema{ - Type: TypeMap, - Computed: true, - }, - }, - - State: nil, - - Config: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "vars.%": &terraform.ResourceAttrDiff{ - Old: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Computed maps", - Schema: map[string]*Schema{ - "vars": &Schema{ - Type: TypeMap, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "vars.%": "0", - }, - }, - - Config: map[string]interface{}{ - "vars": map[string]interface{}{ - "bar": hcl2shim.UnknownVariableValue, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "vars.%": &terraform.ResourceAttrDiff{ - Old: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: " - Empty", - Schema: map[string]*Schema{}, - - State: &terraform.InstanceState{}, - - Config: map[string]interface{}{}, - - Diff: nil, - - Err: false, - }, - - { - Name: "Float", - Schema: map[string]*Schema{ - "some_threshold": &Schema{ - Type: TypeFloat, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "some_threshold": "567.8", - }, - }, - - Config: map[string]interface{}{ - "some_threshold": 12.34, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "some_threshold": &terraform.ResourceAttrDiff{ - Old: "567.8", - New: "12.34", - }, - }, - }, - - Err: false, - }, - - { - Name: "https://github.com/hashicorp/terraform/issues/824", - Schema: map[string]*Schema{ - "block_device": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "device_name": &Schema{ - Type: TypeString, - Required: true, - }, - "delete_on_termination": &Schema{ - Type: TypeBool, - Optional: true, - Default: true, - }, - }, - }, - Set: func(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string))) - buf.WriteString(fmt.Sprintf("%t-", m["delete_on_termination"].(bool))) - return hashcode.String(buf.String()) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "block_device.#": "2", - "block_device.616397234.delete_on_termination": "true", - "block_device.616397234.device_name": "/dev/sda1", - "block_device.2801811477.delete_on_termination": "true", - "block_device.2801811477.device_name": "/dev/sdx", - }, - }, - - Config: map[string]interface{}{ - "block_device": []interface{}{ - map[string]interface{}{ - "device_name": "/dev/sda1", - }, - map[string]interface{}{ - "device_name": "/dev/sdx", - }, - }, - }, - Diff: nil, - Err: false, - }, - - { - Name: "Zero value in state shouldn't result in diff", - Schema: map[string]*Schema{ - "port": &Schema{ - Type: TypeBool, - Optional: true, - ForceNew: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "port": "false", - }, - }, - - Config: map[string]interface{}{}, - - Diff: nil, - - Err: false, - }, - - { - Name: "Same as prev, but for sets", - Schema: map[string]*Schema{ - "route": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "index": &Schema{ - Type: TypeInt, - Required: true, - }, - - "gateway": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - }, - Set: func(v interface{}) int { - m := v.(map[string]interface{}) - return m["index"].(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "route.#": "0", - }, - }, - - Config: map[string]interface{}{}, - - Diff: nil, - - Err: false, - }, - - { - Name: "A set computed element shouldn't cause a diff", - Schema: map[string]*Schema{ - "active": &Schema{ - Type: TypeBool, - Computed: true, - ForceNew: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "active": "true", - }, - }, - - Config: map[string]interface{}{}, - - Diff: nil, - - Err: false, - }, - - { - Name: "An empty set should show up in the diff", - Schema: map[string]*Schema{ - "instances": &Schema{ - Type: TypeSet, - Elem: &Schema{Type: TypeString}, - Optional: true, - ForceNew: true, - Set: func(v interface{}) int { - return len(v.(string)) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "instances.#": "1", - "instances.3": "foo", - }, - }, - - Config: map[string]interface{}{}, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "instances.#": &terraform.ResourceAttrDiff{ - Old: "1", - New: "0", - RequiresNew: true, - }, - "instances.3": &terraform.ResourceAttrDiff{ - Old: "foo", - New: "", - NewRemoved: true, - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Map with empty value", - Schema: map[string]*Schema{ - "vars": &Schema{ - Type: TypeMap, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "vars": map[string]interface{}{ - "foo": "", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "vars.%": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - "vars.foo": &terraform.ResourceAttrDiff{ - Old: "", - New: "", - }, - }, - }, - - Err: false, - }, - - { - Name: "Unset bool, not in state", - Schema: map[string]*Schema{ - "force": &Schema{ - Type: TypeBool, - Optional: true, - ForceNew: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{}, - - Diff: nil, - - Err: false, - }, - - { - Name: "Unset set, not in state", - Schema: map[string]*Schema{ - "metadata_keys": &Schema{ - Type: TypeSet, - Optional: true, - ForceNew: true, - Elem: &Schema{Type: TypeInt}, - Set: func(interface{}) int { return 0 }, - }, - }, - - State: nil, - - Config: map[string]interface{}{}, - - Diff: nil, - - Err: false, - }, - - { - Name: "Unset list in state, should not show up computed", - Schema: map[string]*Schema{ - "metadata_keys": &Schema{ - Type: TypeList, - Optional: true, - Computed: true, - ForceNew: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "metadata_keys.#": "0", - }, - }, - - Config: map[string]interface{}{}, - - Diff: nil, - - Err: false, - }, - - { - Name: "Set element computed element", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Required: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ports": []interface{}{1, hcl2shim.UnknownVariableValue}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "", - New: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Computed map without config that's known to be empty does not generate diff", - Schema: map[string]*Schema{ - "tags": &Schema{ - Type: TypeMap, - Computed: true, - }, - }, - - Config: nil, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "tags.%": "0", - }, - }, - - Diff: nil, - - Err: false, - }, - - { - Name: "Set with hyphen keys", - Schema: map[string]*Schema{ - "route": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "index": &Schema{ - Type: TypeInt, - Required: true, - }, - - "gateway-name": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - }, - Set: func(v interface{}) int { - m := v.(map[string]interface{}) - return m["index"].(int) - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "route": []interface{}{ - map[string]interface{}{ - "index": "1", - "gateway-name": "hello", - }, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "route.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - "route.1.index": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "route.1.gateway-name": &terraform.ResourceAttrDiff{ - Old: "", - New: "hello", - }, - }, - }, - - Err: false, - }, - - { - Name: ": StateFunc in nested set (#1759)", - Schema: map[string]*Schema{ - "service_account": &Schema{ - Type: TypeList, - Optional: true, - ForceNew: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "scopes": &Schema{ - Type: TypeSet, - Required: true, - ForceNew: true, - Elem: &Schema{ - Type: TypeString, - StateFunc: func(v interface{}) string { - return v.(string) + "!" - }, - }, - Set: func(v interface{}) int { - i, err := strconv.Atoi(v.(string)) - if err != nil { - t.Fatalf("err: %s", err) - } - return i - }, - }, - }, - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "service_account": []interface{}{ - map[string]interface{}{ - "scopes": []interface{}{"123"}, - }, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "service_account.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - RequiresNew: true, - }, - "service_account.0.scopes.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - RequiresNew: true, - }, - "service_account.0.scopes.123": &terraform.ResourceAttrDiff{ - Old: "", - New: "123!", - NewExtra: "123", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Removing set elements", - Schema: map[string]*Schema{ - "instances": &Schema{ - Type: TypeSet, - Elem: &Schema{Type: TypeString}, - Optional: true, - ForceNew: true, - Set: func(v interface{}) int { - return len(v.(string)) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "instances.#": "2", - "instances.3": "333", - "instances.2": "22", - }, - }, - - Config: map[string]interface{}{ - "instances": []interface{}{"333", "4444"}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "instances.#": &terraform.ResourceAttrDiff{ - Old: "2", - New: "2", - }, - "instances.2": &terraform.ResourceAttrDiff{ - Old: "22", - New: "", - NewRemoved: true, - RequiresNew: true, - }, - "instances.3": &terraform.ResourceAttrDiff{ - Old: "333", - New: "333", - }, - "instances.4": &terraform.ResourceAttrDiff{ - Old: "", - New: "4444", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Bools can be set with 0/1 in config, still get true/false", - Schema: map[string]*Schema{ - "one": &Schema{ - Type: TypeBool, - Optional: true, - }, - "two": &Schema{ - Type: TypeBool, - Optional: true, - }, - "three": &Schema{ - Type: TypeBool, - Optional: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "one": "false", - "two": "true", - "three": "true", - }, - }, - - Config: map[string]interface{}{ - "one": "1", - "two": "0", - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "one": &terraform.ResourceAttrDiff{ - Old: "false", - New: "true", - }, - "two": &terraform.ResourceAttrDiff{ - Old: "true", - New: "false", - }, - "three": &terraform.ResourceAttrDiff{ - Old: "true", - New: "false", - NewRemoved: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "tainted in state w/ no attr changes is still a replacement", - Schema: map[string]*Schema{}, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "id": "someid", - }, - Tainted: true, - }, - - Config: map[string]interface{}{}, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{}, - DestroyTainted: true, - }, - - Err: false, - }, - - { - Name: "Set ForceNew only marks the changing element as ForceNew", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Required: true, - ForceNew: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "3", - "ports.1": "1", - "ports.2": "2", - "ports.4": "4", - }, - }, - - Config: map[string]interface{}{ - "ports": []interface{}{5, 2, 1}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "3", - New: "3", - }, - "ports.1": &terraform.ResourceAttrDiff{ - Old: "1", - New: "1", - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "2", - New: "2", - }, - "ports.5": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - RequiresNew: true, - }, - "ports.4": &terraform.ResourceAttrDiff{ - Old: "4", - New: "0", - NewRemoved: true, - RequiresNew: true, - }, - }, - }, - }, - - { - Name: "removed optional items should trigger ForceNew", - Schema: map[string]*Schema{ - "description": &Schema{ - Type: TypeString, - ForceNew: true, - Optional: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "description": "foo", - }, - }, - - Config: map[string]interface{}{}, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "description": &terraform.ResourceAttrDiff{ - Old: "foo", - New: "", - RequiresNew: true, - NewRemoved: true, - }, - }, - }, - - Err: false, - }, - - // GH-7715 - { - Name: "computed value for boolean field", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeBool, - ForceNew: true, - Computed: true, - Optional: true, - }, - }, - - State: &terraform.InstanceState{}, - - Config: map[string]interface{}{ - "foo": hcl2shim.UnknownVariableValue, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "", - New: "false", - NewComputed: true, - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Set ForceNew marks count as ForceNew if computed", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Required: true, - ForceNew: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "3", - "ports.1": "1", - "ports.2": "2", - "ports.4": "4", - }, - }, - - Config: map[string]interface{}{ - "ports": []interface{}{hcl2shim.UnknownVariableValue, 2, 1}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "3", - New: "", - NewComputed: true, - RequiresNew: true, - }, - }, - }, - }, - - { - Name: "List with computed schema and ForceNew", - Schema: map[string]*Schema{ - "config": &Schema{ - Type: TypeList, - Optional: true, - ForceNew: true, - Elem: &Schema{ - Type: TypeString, - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "config.#": "2", - "config.0": "a", - "config.1": "b", - }, - }, - - Config: map[string]interface{}{ - "config": []interface{}{hcl2shim.UnknownVariableValue, hcl2shim.UnknownVariableValue}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "config.#": &terraform.ResourceAttrDiff{ - Old: "2", - New: "", - RequiresNew: true, - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "overridden diff with a CustomizeDiff function, ForceNew not in schema", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "availability_zone": "foo", - }, - - CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { - if err := d.SetNew("availability_zone", "bar"); err != nil { - return err - } - if err := d.ForceNew("availability_zone"); err != nil { - return err - } - return nil - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "bar", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - // NOTE: This case is technically impossible in the current - // implementation, because optional+computed values never show up in the - // diff. In the event behavior changes this test should ensure that the - // intended diff still shows up. - Name: "overridden removed attribute diff with a CustomizeDiff function, ForceNew not in schema", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{}, - - CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { - if err := d.SetNew("availability_zone", "bar"); err != nil { - return err - } - if err := d.ForceNew("availability_zone"); err != nil { - return err - } - return nil - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "bar", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - - Name: "overridden diff with a CustomizeDiff function, ForceNew in schema", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "availability_zone": "foo", - }, - - CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { - if err := d.SetNew("availability_zone", "bar"); err != nil { - return err - } - return nil - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "bar", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "required field with computed diff added with CustomizeDiff function", - Schema: map[string]*Schema{ - "ami_id": &Schema{ - Type: TypeString, - Required: true, - }, - "instance_id": &Schema{ - Type: TypeString, - Computed: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ami_id": "foo", - }, - - CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { - if err := d.SetNew("instance_id", "bar"); err != nil { - return err - } - return nil - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ami_id": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - }, - "instance_id": &terraform.ResourceAttrDiff{ - Old: "", - New: "bar", - }, - }, - }, - - Err: false, - }, - - { - Name: "Set ForceNew only marks the changing element as ForceNew - CustomizeDiffFunc edition", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "ports.#": "3", - "ports.1": "1", - "ports.2": "2", - "ports.4": "4", - }, - }, - - Config: map[string]interface{}{ - "ports": []interface{}{5, 2, 6}, - }, - - CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { - if err := d.SetNew("ports", []interface{}{5, 2, 1}); err != nil { - return err - } - if err := d.ForceNew("ports"); err != nil { - return err - } - return nil - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "3", - New: "3", - }, - "ports.1": &terraform.ResourceAttrDiff{ - Old: "1", - New: "1", - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "2", - New: "2", - }, - "ports.5": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - RequiresNew: true, - }, - "ports.4": &terraform.ResourceAttrDiff{ - Old: "4", - New: "0", - NewRemoved: true, - RequiresNew: true, - }, - }, - }, - }, - - { - Name: "tainted resource does not run CustomizeDiffFunc", - Schema: map[string]*Schema{}, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "id": "someid", - }, - Tainted: true, - }, - - Config: map[string]interface{}{}, - - CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { - return errors.New("diff customization should not have run") - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{}, - DestroyTainted: true, - }, - - Err: false, - }, - - { - Name: "NewComputed based on a conditional with CustomizeDiffFunc", - Schema: map[string]*Schema{ - "etag": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - "version_id": &Schema{ - Type: TypeString, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "etag": "foo", - "version_id": "1", - }, - }, - - Config: map[string]interface{}{ - "etag": "bar", - }, - - CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { - if d.HasChange("etag") { - d.SetNewComputed("version_id") - } - return nil - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "etag": &terraform.ResourceAttrDiff{ - Old: "foo", - New: "bar", - }, - "version_id": &terraform.ResourceAttrDiff{ - Old: "1", - New: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "NewComputed should always propagate with CustomizeDiff", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "", - }, - ID: "pre-existing", - }, - - Config: map[string]interface{}{}, - - CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { - d.SetNewComputed("foo") - return nil - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "vetoing a diff", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "foo": "bar", - }, - }, - - Config: map[string]interface{}{ - "foo": "baz", - }, - - CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { - return fmt.Errorf("diff vetoed") - }, - - Err: true, - }, - - // A lot of resources currently depended on using the empty string as a - // nil/unset value. - // FIXME: We want this to eventually produce a diff, since there - // technically is a new value in the config. - { - Name: "optional, computed, empty string", - Schema: map[string]*Schema{ - "attr": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "attr": "bar", - }, - }, - - Config: map[string]interface{}{ - "attr": "", - }, - }, - - { - Name: "optional, computed, empty string should not crash in CustomizeDiff", - Schema: map[string]*Schema{ - "unrelated_set": { - Type: TypeSet, - Optional: true, - Elem: &Schema{Type: TypeString}, - }, - "stream_enabled": { - Type: TypeBool, - Optional: true, - }, - "stream_view_type": { - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - Attributes: map[string]string{ - "unrelated_set.#": "0", - "stream_enabled": "true", - "stream_view_type": "KEYS_ONLY", - }, - }, - Config: map[string]interface{}{ - "stream_enabled": false, - "stream_view_type": "", - }, - CustomizeDiff: func(diff *ResourceDiff, v interface{}) error { - v, ok := diff.GetOk("unrelated_set") - if ok { - return fmt.Errorf("Didn't expect unrelated_set: %#v", v) - } - return nil - }, - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "stream_enabled": { - Old: "true", - New: "false", - }, - }, - }, - }, - } - - for i, tc := range cases { - t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { - c := terraform.NewResourceConfigRaw(tc.Config) - - d, err := schemaMap(tc.Schema).Diff(tc.State, c, tc.CustomizeDiff, nil, true) - if err != nil != tc.Err { - t.Fatalf("err: %s", err) - } - - if !reflect.DeepEqual(tc.Diff, d) { - t.Fatalf("expected:\n%#v\n\ngot:\n%#v", tc.Diff, d) - } - }) - } -} - -func TestSchemaMap_Input(t *testing.T) { - cases := map[string]struct { - Schema map[string]*Schema - Config map[string]interface{} - Input map[string]string - Result map[string]interface{} - Err bool - }{ - /* - * String decode - */ - - "no input on optional field with no config": { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - - Input: map[string]string{}, - Result: map[string]interface{}{}, - Err: false, - }, - - "input ignored when config has a value": { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - - Config: map[string]interface{}{ - "availability_zone": "bar", - }, - - Input: map[string]string{ - "availability_zone": "foo", - }, - - Result: map[string]interface{}{}, - - Err: false, - }, - - "input ignored when schema has a default": { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Default: "foo", - Optional: true, - }, - }, - - Input: map[string]string{ - "availability_zone": "bar", - }, - - Result: map[string]interface{}{}, - - Err: false, - }, - - "input ignored when default function returns a value": { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - DefaultFunc: func() (interface{}, error) { - return "foo", nil - }, - Optional: true, - }, - }, - - Input: map[string]string{ - "availability_zone": "bar", - }, - - Result: map[string]interface{}{}, - - Err: false, - }, - - "input ignored when default function returns an empty string": { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Default: "", - Optional: true, - }, - }, - - Input: map[string]string{ - "availability_zone": "bar", - }, - - Result: map[string]interface{}{}, - - Err: false, - }, - - "input used when default function returns nil": { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - DefaultFunc: func() (interface{}, error) { - return nil, nil - }, - Required: true, - }, - }, - - Input: map[string]string{ - "availability_zone": "bar", - }, - - Result: map[string]interface{}{ - "availability_zone": "bar", - }, - - Err: false, - }, - - "input not used when optional default function returns nil": { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - DefaultFunc: func() (interface{}, error) { - return nil, nil - }, - Optional: true, - }, - }, - - Input: map[string]string{}, - Result: map[string]interface{}{}, - Err: false, - }, - } - - for i, tc := range cases { - if tc.Config == nil { - tc.Config = make(map[string]interface{}) - } - - input := new(terraform.MockUIInput) - input.InputReturnMap = tc.Input - - rc := terraform.NewResourceConfigRaw(tc.Config) - rc.Config = make(map[string]interface{}) - - actual, err := schemaMap(tc.Schema).Input(input, rc) - if err != nil != tc.Err { - t.Fatalf("#%v err: %s", i, err) - } - - if !reflect.DeepEqual(tc.Result, actual.Config) { - t.Fatalf("#%v: bad:\n\ngot: %#v\nexpected: %#v", i, actual.Config, tc.Result) - } - } -} - -func TestSchemaMap_InputDefault(t *testing.T) { - emptyConfig := make(map[string]interface{}) - rc := terraform.NewResourceConfigRaw(emptyConfig) - rc.Config = make(map[string]interface{}) - - input := new(terraform.MockUIInput) - input.InputFn = func(opts *terraform.InputOpts) (string, error) { - t.Fatalf("InputFn should not be called on: %#v", opts) - return "", nil - } - - schema := map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Default: "foo", - Optional: true, - }, - } - actual, err := schemaMap(schema).Input(input, rc) - if err != nil { - t.Fatalf("err: %s", err) - } - - expected := map[string]interface{}{} - - if !reflect.DeepEqual(expected, actual.Config) { - t.Fatalf("got: %#v\nexpected: %#v", actual.Config, expected) - } -} - -func TestSchemaMap_InputDeprecated(t *testing.T) { - emptyConfig := make(map[string]interface{}) - rc := terraform.NewResourceConfigRaw(emptyConfig) - rc.Config = make(map[string]interface{}) - - input := new(terraform.MockUIInput) - input.InputFn = func(opts *terraform.InputOpts) (string, error) { - t.Fatalf("InputFn should not be called on: %#v", opts) - return "", nil - } - - schema := map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Deprecated: "long gone", - Optional: true, - }, - } - actual, err := schemaMap(schema).Input(input, rc) - if err != nil { - t.Fatalf("err: %s", err) - } - - expected := map[string]interface{}{} - - if !reflect.DeepEqual(expected, actual.Config) { - t.Fatalf("got: %#v\nexpected: %#v", actual.Config, expected) - } -} - -func TestSchemaMap_InternalValidate(t *testing.T) { - cases := map[string]struct { - In map[string]*Schema - Err bool - }{ - "nothing": { - nil, - false, - }, - - "Both optional and required": { - map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - Required: true, - }, - }, - true, - }, - - "No optional and no required": { - map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - }, - }, - true, - }, - - "Missing Type": { - map[string]*Schema{ - "foo": &Schema{ - Required: true, - }, - }, - true, - }, - - "Required but computed": { - map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Required: true, - Computed: true, - }, - }, - true, - }, - - "Looks good": { - map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Required: true, - }, - }, - false, - }, - - "Computed but has default": { - map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - Computed: true, - Default: "foo", - }, - }, - true, - }, - - "Required but has default": { - map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - Required: true, - Default: "foo", - }, - }, - true, - }, - - "List element not set": { - map[string]*Schema{ - "foo": &Schema{ - Type: TypeList, - }, - }, - true, - }, - - "List default": { - map[string]*Schema{ - "foo": &Schema{ - Type: TypeList, - Elem: &Schema{Type: TypeInt}, - Default: "foo", - }, - }, - true, - }, - - "List element computed": { - map[string]*Schema{ - "foo": &Schema{ - Type: TypeList, - Optional: true, - Elem: &Schema{ - Type: TypeInt, - Computed: true, - }, - }, - }, - true, - }, - - "List element with Set set": { - map[string]*Schema{ - "foo": &Schema{ - Type: TypeList, - Elem: &Schema{Type: TypeInt}, - Set: func(interface{}) int { return 0 }, - Optional: true, - }, - }, - true, - }, - - "Set element with no Set set": { - map[string]*Schema{ - "foo": &Schema{ - Type: TypeSet, - Elem: &Schema{Type: TypeInt}, - Optional: true, - }, - }, - false, - }, - - "Required but computedWhen": { - map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Required: true, - ComputedWhen: []string{"foo"}, - }, - }, - true, - }, - - "Conflicting attributes cannot be required": { - map[string]*Schema{ - "a": &Schema{ - Type: TypeBool, - Required: true, - }, - "b": &Schema{ - Type: TypeBool, - Optional: true, - ConflictsWith: []string{"a"}, - }, - }, - true, - }, - - "Attribute with conflicts cannot be required": { - map[string]*Schema{ - "b": &Schema{ - Type: TypeBool, - Required: true, - ConflictsWith: []string{"a"}, - }, - }, - true, - }, - - "ConflictsWith cannot be used w/ ComputedWhen": { - map[string]*Schema{ - "a": &Schema{ - Type: TypeBool, - ComputedWhen: []string{"foor"}, - }, - "b": &Schema{ - Type: TypeBool, - Required: true, - ConflictsWith: []string{"a"}, - }, - }, - true, - }, - - "Sub-resource invalid": { - map[string]*Schema{ - "foo": &Schema{ - Type: TypeList, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "foo": new(Schema), - }, - }, - }, - }, - true, - }, - - "Sub-resource valid": { - map[string]*Schema{ - "foo": &Schema{ - Type: TypeList, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - }, - }, - }, - false, - }, - - "ValidateFunc on non-primitive": { - map[string]*Schema{ - "foo": &Schema{ - Type: TypeSet, - Required: true, - ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { - return - }, - }, - }, - true, - }, - - "computed-only field with validateFunc": { - map[string]*Schema{ - "string": &Schema{ - Type: TypeString, - Computed: true, - ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { - es = append(es, fmt.Errorf("this is not fine")) - return - }, - }, - }, - true, - }, - - "computed-only field with diffSuppressFunc": { - map[string]*Schema{ - "string": &Schema{ - Type: TypeString, - Computed: true, - DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool { - // Always suppress any diff - return false - }, - }, - }, - true, - }, - - "invalid field name format #1": { - map[string]*Schema{ - "with space": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - true, - }, - - "invalid field name format #2": { - map[string]*Schema{ - "WithCapitals": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - true, - }, - - "invalid field name format of a Deprecated field": { - map[string]*Schema{ - "WithCapitals": &Schema{ - Type: TypeString, - Optional: true, - Deprecated: "Use with_underscores instead", - }, - }, - false, - }, - - "invalid field name format of a Removed field": { - map[string]*Schema{ - "WithCapitals": &Schema{ - Type: TypeString, - Optional: true, - Removed: "Use with_underscores instead", - }, - }, - false, - }, - - "ConfigModeBlock with Elem *Resource": { - map[string]*Schema{ - "block": &Schema{ - Type: TypeList, - ConfigMode: SchemaConfigModeBlock, - Optional: true, - Elem: &Resource{}, - }, - }, - false, - }, - - "ConfigModeBlock Computed with Elem *Resource": { - map[string]*Schema{ - "block": &Schema{ - Type: TypeList, - ConfigMode: SchemaConfigModeBlock, - Computed: true, - Elem: &Resource{}, - }, - }, - true, // ConfigMode of block cannot be used for computed schema - }, - - "ConfigModeBlock with Elem *Schema": { - map[string]*Schema{ - "block": &Schema{ - Type: TypeList, - ConfigMode: SchemaConfigModeBlock, - Optional: true, - Elem: &Schema{ - Type: TypeString, - }, - }, - }, - true, - }, - - "ConfigModeBlock with no Elem": { - map[string]*Schema{ - "block": &Schema{ - Type: TypeString, - ConfigMode: SchemaConfigModeBlock, - Optional: true, - }, - }, - true, - }, - - "ConfigModeBlock inside ConfigModeAttr": { - map[string]*Schema{ - "block": &Schema{ - Type: TypeList, - ConfigMode: SchemaConfigModeAttr, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "sub": &Schema{ - Type: TypeList, - ConfigMode: SchemaConfigModeBlock, - Elem: &Resource{}, - }, - }, - }, - }, - }, - true, // ConfigMode of block cannot be used in child of schema with ConfigMode of attribute - }, - - "ConfigModeAuto with *Resource inside ConfigModeAttr": { - map[string]*Schema{ - "block": &Schema{ - Type: TypeList, - ConfigMode: SchemaConfigModeAttr, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "sub": &Schema{ - Type: TypeList, - Elem: &Resource{}, - }, - }, - }, - }, - }, - true, // in *schema.Resource with ConfigMode of attribute, so must also have ConfigMode of attribute - }, - } - - for tn, tc := range cases { - t.Run(tn, func(t *testing.T) { - err := schemaMap(tc.In).InternalValidate(nil) - if err != nil != tc.Err { - if tc.Err { - t.Fatalf("%q: Expected error did not occur:\n\n%#v", tn, tc.In) - } - t.Fatalf("%q: Unexpected error occurred: %s\n\n%#v", tn, err, tc.In) - } - }) - } - -} - -func TestSchemaMap_DiffSuppress(t *testing.T) { - cases := map[string]struct { - Schema map[string]*Schema - State *terraform.InstanceState - Config map[string]interface{} - ExpectedDiff *terraform.InstanceDiff - Err bool - }{ - "#0 - Suppress otherwise valid diff by returning true": { - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool { - // Always suppress any diff - return true - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "availability_zone": "foo", - }, - - ExpectedDiff: nil, - - Err: false, - }, - - "#1 - Don't suppress diff by returning false": { - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool { - // Always suppress any diff - return false - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "availability_zone": "foo", - }, - - ExpectedDiff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": { - Old: "", - New: "foo", - }, - }, - }, - - Err: false, - }, - - "Default with suppress makes no diff": { - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - Default: "foo", - DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool { - return true - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{}, - - ExpectedDiff: nil, - - Err: false, - }, - - "Default with false suppress makes diff": { - Schema: map[string]*Schema{ - "availability_zone": { - Type: TypeString, - Optional: true, - Default: "foo", - DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool { - return false - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{}, - - ExpectedDiff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": { - Old: "", - New: "foo", - }, - }, - }, - - Err: false, - }, - - "Complex structure with set of computed string should mark root set as computed": { - Schema: map[string]*Schema{ - "outer": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "outer_str": &Schema{ - Type: TypeString, - Optional: true, - }, - "inner": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "inner_str": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - }, - Set: func(v interface{}) int { - return 2 - }, - }, - }, - }, - Set: func(v interface{}) int { - return 1 - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "outer": []interface{}{ - map[string]interface{}{ - "outer_str": "foo", - "inner": []interface{}{ - map[string]interface{}{ - "inner_str": hcl2shim.UnknownVariableValue, - }, - }, - }, - }, - }, - - ExpectedDiff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "outer.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - "outer.~1.outer_str": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - }, - "outer.~1.inner.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - "outer.~1.inner.~2.inner_str": &terraform.ResourceAttrDiff{ - Old: "", - New: hcl2shim.UnknownVariableValue, - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - "Complex structure with complex list of computed string should mark root set as computed": { - Schema: map[string]*Schema{ - "outer": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "outer_str": &Schema{ - Type: TypeString, - Optional: true, - }, - "inner": &Schema{ - Type: TypeList, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "inner_str": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - }, - }, - }, - }, - Set: func(v interface{}) int { - return 1 - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "outer": []interface{}{ - map[string]interface{}{ - "outer_str": "foo", - "inner": []interface{}{ - map[string]interface{}{ - "inner_str": hcl2shim.UnknownVariableValue, - }, - }, - }, - }, - }, - - ExpectedDiff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "outer.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - "outer.~1.outer_str": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - }, - "outer.~1.inner.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - "outer.~1.inner.0.inner_str": &terraform.ResourceAttrDiff{ - Old: "", - New: hcl2shim.UnknownVariableValue, - NewComputed: true, - }, - }, - }, - - Err: false, - }, - } - - for tn, tc := range cases { - t.Run(tn, func(t *testing.T) { - c := terraform.NewResourceConfigRaw(tc.Config) - - d, err := schemaMap(tc.Schema).Diff(tc.State, c, nil, nil, true) - if err != nil != tc.Err { - t.Fatalf("#%q err: %s", tn, err) - } - - if !reflect.DeepEqual(tc.ExpectedDiff, d) { - t.Fatalf("#%q:\n\nexpected:\n%#v\n\ngot:\n%#v", tn, tc.ExpectedDiff, d) - } - }) - } -} - -func TestSchemaMap_Validate(t *testing.T) { - cases := map[string]struct { - Schema map[string]*Schema - Config map[string]interface{} - Err bool - Errors []error - Warnings []string - }{ - "Good": { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - Config: map[string]interface{}{ - "availability_zone": "foo", - }, - }, - - "Good, because the var is not set and that error will come elsewhere": { - Schema: map[string]*Schema{ - "size": &Schema{ - Type: TypeInt, - Required: true, - }, - }, - - Config: map[string]interface{}{ - "size": hcl2shim.UnknownVariableValue, - }, - }, - - "Required field not set": { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Required: true, - }, - }, - - Config: map[string]interface{}{}, - - Err: true, - }, - - "Invalid basic type": { - Schema: map[string]*Schema{ - "port": &Schema{ - Type: TypeInt, - Required: true, - }, - }, - - Config: map[string]interface{}{ - "port": "I am invalid", - }, - - Err: true, - }, - - "Invalid complex type": { - Schema: map[string]*Schema{ - "user_data": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - - Config: map[string]interface{}{ - "user_data": []interface{}{ - map[string]interface{}{ - "foo": "bar", - }, - }, - }, - - Err: true, - }, - - "Bad type": { - Schema: map[string]*Schema{ - "size": &Schema{ - Type: TypeInt, - Required: true, - }, - }, - - Config: map[string]interface{}{ - "size": "nope", - }, - - Err: true, - }, - - "Required but has DefaultFunc": { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Required: true, - DefaultFunc: func() (interface{}, error) { - return "foo", nil - }, - }, - }, - - Config: nil, - }, - - "Required but has DefaultFunc return nil": { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Required: true, - DefaultFunc: func() (interface{}, error) { - return nil, nil - }, - }, - }, - - Config: nil, - - Err: true, - }, - - "List with promotion": { - Schema: map[string]*Schema{ - "ingress": &Schema{ - Type: TypeList, - Elem: &Schema{Type: TypeInt}, - PromoteSingle: true, - Optional: true, - }, - }, - - Config: map[string]interface{}{ - "ingress": "5", - }, - - Err: false, - }, - - "List with promotion set as list": { - Schema: map[string]*Schema{ - "ingress": &Schema{ - Type: TypeList, - Elem: &Schema{Type: TypeInt}, - PromoteSingle: true, - Optional: true, - }, - }, - - Config: map[string]interface{}{ - "ingress": []interface{}{"5"}, - }, - - Err: false, - }, - - "Optional sub-resource": { - Schema: map[string]*Schema{ - "ingress": &Schema{ - Type: TypeList, - Elem: &Resource{ - Schema: map[string]*Schema{ - "from": &Schema{ - Type: TypeInt, - Required: true, - }, - }, - }, - }, - }, - - Config: map[string]interface{}{}, - - Err: false, - }, - - "Sub-resource is the wrong type": { - Schema: map[string]*Schema{ - "ingress": &Schema{ - Type: TypeList, - Required: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "from": &Schema{ - Type: TypeInt, - Required: true, - }, - }, - }, - }, - }, - - Config: map[string]interface{}{ - "ingress": []interface{}{"foo"}, - }, - - Err: true, - }, - - "Not a list nested block": { - Schema: map[string]*Schema{ - "ingress": &Schema{ - Type: TypeList, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "from": &Schema{ - Type: TypeInt, - Required: true, - }, - }, - }, - }, - }, - - Config: map[string]interface{}{ - "ingress": "foo", - }, - - Err: true, - Errors: []error{ - fmt.Errorf(`ingress: should be a list`), - }, - }, - - "Not a list primitive": { - Schema: map[string]*Schema{ - "strings": &Schema{ - Type: TypeList, - Optional: true, - Elem: &Schema{ - Type: TypeString, - }, - }, - }, - - Config: map[string]interface{}{ - "strings": "foo", - }, - - Err: true, - Errors: []error{ - fmt.Errorf(`strings: should be a list`), - }, - }, - - "Unknown list": { - Schema: map[string]*Schema{ - "strings": &Schema{ - Type: TypeList, - Optional: true, - Elem: &Schema{ - Type: TypeString, - }, - }, - }, - - Config: map[string]interface{}{ - "strings": hcl2shim.UnknownVariableValue, - }, - - Err: false, - }, - - "Unknown + Deprecation": { - Schema: map[string]*Schema{ - "old_news": &Schema{ - Type: TypeString, - Optional: true, - Deprecated: "please use 'new_news' instead", - }, - }, - - Config: map[string]interface{}{ - "old_news": hcl2shim.UnknownVariableValue, - }, - - Warnings: []string{ - "\"old_news\": [DEPRECATED] please use 'new_news' instead", - }, - }, - - "Required sub-resource field": { - Schema: map[string]*Schema{ - "ingress": &Schema{ - Type: TypeList, - Elem: &Resource{ - Schema: map[string]*Schema{ - "from": &Schema{ - Type: TypeInt, - Required: true, - }, - }, - }, - }, - }, - - Config: map[string]interface{}{ - "ingress": []interface{}{ - map[string]interface{}{}, - }, - }, - - Err: true, - }, - - "Good sub-resource": { - Schema: map[string]*Schema{ - "ingress": &Schema{ - Type: TypeList, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "from": &Schema{ - Type: TypeInt, - Required: true, - }, - }, - }, - }, - }, - - Config: map[string]interface{}{ - "ingress": []interface{}{ - map[string]interface{}{ - "from": 80, - }, - }, - }, - - Err: false, - }, - - "Good sub-resource, computed value": { - Schema: map[string]*Schema{ - "ingress": &Schema{ - Type: TypeList, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "from": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - }, - }, - }, - - Config: map[string]interface{}{ - "ingress": []interface{}{ - map[string]interface{}{ - "from": hcl2shim.UnknownVariableValue, - }, - }, - }, - - Err: false, - }, - - "Invalid/unknown field": { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - Config: map[string]interface{}{ - "foo": "bar", - }, - - Err: true, - }, - - "Invalid/unknown field with computed value": { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - Config: map[string]interface{}{ - "foo": hcl2shim.UnknownVariableValue, - }, - - Err: true, - }, - - "Computed field set": { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Computed: true, - }, - }, - - Config: map[string]interface{}{ - "availability_zone": "bar", - }, - - Err: true, - }, - - "Not a set": { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Required: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - Config: map[string]interface{}{ - "ports": "foo", - }, - - Err: true, - }, - - "Maps": { - Schema: map[string]*Schema{ - "user_data": &Schema{ - Type: TypeMap, - Optional: true, - }, - }, - - Config: map[string]interface{}{ - "user_data": "foo", - }, - - Err: true, - }, - - "Good map: data surrounded by extra slice": { - Schema: map[string]*Schema{ - "user_data": &Schema{ - Type: TypeMap, - Optional: true, - }, - }, - - Config: map[string]interface{}{ - "user_data": []interface{}{ - map[string]interface{}{ - "foo": "bar", - }, - }, - }, - }, - - "Good map": { - Schema: map[string]*Schema{ - "user_data": &Schema{ - Type: TypeMap, - Optional: true, - }, - }, - - Config: map[string]interface{}{ - "user_data": map[string]interface{}{ - "foo": "bar", - }, - }, - }, - - "Map with type specified as value type": { - Schema: map[string]*Schema{ - "user_data": &Schema{ - Type: TypeMap, - Optional: true, - Elem: TypeBool, - }, - }, - - Config: map[string]interface{}{ - "user_data": map[string]interface{}{ - "foo": "not_a_bool", - }, - }, - - Err: true, - }, - - "Map with type specified as nested Schema": { - Schema: map[string]*Schema{ - "user_data": &Schema{ - Type: TypeMap, - Optional: true, - Elem: &Schema{Type: TypeBool}, - }, - }, - - Config: map[string]interface{}{ - "user_data": map[string]interface{}{ - "foo": "not_a_bool", - }, - }, - - Err: true, - }, - - "Bad map: just a slice": { - Schema: map[string]*Schema{ - "user_data": &Schema{ - Type: TypeMap, - Optional: true, - }, - }, - - Config: map[string]interface{}{ - "user_data": []interface{}{ - "foo", - }, - }, - - Err: true, - }, - - "Good set: config has slice with single interpolated value": { - Schema: map[string]*Schema{ - "security_groups": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - ForceNew: true, - Elem: &Schema{Type: TypeString}, - Set: func(v interface{}) int { - return len(v.(string)) - }, - }, - }, - - Config: map[string]interface{}{ - "security_groups": []interface{}{"${var.foo}"}, - }, - - Err: false, - }, - - "Bad set: config has single interpolated value": { - Schema: map[string]*Schema{ - "security_groups": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - ForceNew: true, - Elem: &Schema{Type: TypeString}, - }, - }, - - Config: map[string]interface{}{ - "security_groups": "${var.foo}", - }, - - Err: true, - }, - - "Bad, subresource should not allow unknown elements": { - Schema: map[string]*Schema{ - "ingress": &Schema{ - Type: TypeList, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "port": &Schema{ - Type: TypeInt, - Required: true, - }, - }, - }, - }, - }, - - Config: map[string]interface{}{ - "ingress": []interface{}{ - map[string]interface{}{ - "port": 80, - "other": "yes", - }, - }, - }, - - Err: true, - }, - - "Bad, subresource should not allow invalid types": { - Schema: map[string]*Schema{ - "ingress": &Schema{ - Type: TypeList, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "port": &Schema{ - Type: TypeInt, - Required: true, - }, - }, - }, - }, - }, - - Config: map[string]interface{}{ - "ingress": []interface{}{ - map[string]interface{}{ - "port": "bad", - }, - }, - }, - - Err: true, - }, - - "Bad, should not allow lists to be assigned to string attributes": { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Required: true, - }, - }, - - Config: map[string]interface{}{ - "availability_zone": []interface{}{"foo", "bar", "baz"}, - }, - - Err: true, - }, - - "Bad, should not allow maps to be assigned to string attributes": { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Required: true, - }, - }, - - Config: map[string]interface{}{ - "availability_zone": map[string]interface{}{"foo": "bar", "baz": "thing"}, - }, - - Err: true, - }, - - "Deprecated attribute usage generates warning, but not error": { - Schema: map[string]*Schema{ - "old_news": &Schema{ - Type: TypeString, - Optional: true, - Deprecated: "please use 'new_news' instead", - }, - }, - - Config: map[string]interface{}{ - "old_news": "extra extra!", - }, - - Err: false, - - Warnings: []string{ - "\"old_news\": [DEPRECATED] please use 'new_news' instead", - }, - }, - - "Deprecated generates no warnings if attr not used": { - Schema: map[string]*Schema{ - "old_news": &Schema{ - Type: TypeString, - Optional: true, - Deprecated: "please use 'new_news' instead", - }, - }, - - Err: false, - - Warnings: nil, - }, - - "Removed attribute usage generates error": { - Schema: map[string]*Schema{ - "long_gone": &Schema{ - Type: TypeString, - Optional: true, - Removed: "no longer supported by Cloud API", - }, - }, - - Config: map[string]interface{}{ - "long_gone": "still here!", - }, - - Err: true, - Errors: []error{ - fmt.Errorf("\"long_gone\": [REMOVED] no longer supported by Cloud API"), - }, - }, - - "Removed generates no errors if attr not used": { - Schema: map[string]*Schema{ - "long_gone": &Schema{ - Type: TypeString, - Optional: true, - Removed: "no longer supported by Cloud API", - }, - }, - - Err: false, - }, - - "Conflicting attributes generate error": { - Schema: map[string]*Schema{ - "b": &Schema{ - Type: TypeString, - Optional: true, - }, - "a": &Schema{ - Type: TypeString, - Optional: true, - ConflictsWith: []string{"b"}, - }, - }, - - Config: map[string]interface{}{ - "b": "b-val", - "a": "a-val", - }, - - Err: true, - Errors: []error{ - fmt.Errorf("\"a\": conflicts with b"), - }, - }, - - "Conflicting attributes okay when unknown 1": { - Schema: map[string]*Schema{ - "b": &Schema{ - Type: TypeString, - Optional: true, - }, - "a": &Schema{ - Type: TypeString, - Optional: true, - ConflictsWith: []string{"b"}, - }, - }, - - Config: map[string]interface{}{ - "b": "b-val", - "a": hcl2shim.UnknownVariableValue, - }, - - Err: false, - }, - - "Conflicting attributes okay when unknown 2": { - Schema: map[string]*Schema{ - "b": &Schema{ - Type: TypeString, - Optional: true, - }, - "a": &Schema{ - Type: TypeString, - Optional: true, - ConflictsWith: []string{"b"}, - }, - }, - - Config: map[string]interface{}{ - "b": hcl2shim.UnknownVariableValue, - "a": "a-val", - }, - - Err: false, - }, - - "Conflicting attributes generate error even if one is unknown": { - Schema: map[string]*Schema{ - "b": &Schema{ - Type: TypeString, - Optional: true, - ConflictsWith: []string{"a", "c"}, - }, - "a": &Schema{ - Type: TypeString, - Optional: true, - ConflictsWith: []string{"b", "c"}, - }, - "c": &Schema{ - Type: TypeString, - Optional: true, - ConflictsWith: []string{"b", "a"}, - }, - }, - - Config: map[string]interface{}{ - "b": hcl2shim.UnknownVariableValue, - "a": "a-val", - "c": "c-val", - }, - - Err: true, - Errors: []error{ - fmt.Errorf("\"a\": conflicts with c"), - fmt.Errorf("\"c\": conflicts with a"), - }, - }, - - "Required attribute & undefined conflicting optional are good": { - Schema: map[string]*Schema{ - "required_att": &Schema{ - Type: TypeString, - Required: true, - }, - "optional_att": &Schema{ - Type: TypeString, - Optional: true, - ConflictsWith: []string{"required_att"}, - }, - }, - - Config: map[string]interface{}{ - "required_att": "required-val", - }, - - Err: false, - }, - - "Required conflicting attribute & defined optional generate error": { - Schema: map[string]*Schema{ - "required_att": &Schema{ - Type: TypeString, - Required: true, - }, - "optional_att": &Schema{ - Type: TypeString, - Optional: true, - ConflictsWith: []string{"required_att"}, - }, - }, - - Config: map[string]interface{}{ - "required_att": "required-val", - "optional_att": "optional-val", - }, - - Err: true, - Errors: []error{ - fmt.Errorf(`"optional_att": conflicts with required_att`), - }, - }, - - "Computed + Optional fields conflicting with each other": { - Schema: map[string]*Schema{ - "foo_att": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ConflictsWith: []string{"bar_att"}, - }, - "bar_att": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ConflictsWith: []string{"foo_att"}, - }, - }, - - Config: map[string]interface{}{ - "foo_att": "foo-val", - "bar_att": "bar-val", - }, - - Err: true, - Errors: []error{ - fmt.Errorf(`"foo_att": conflicts with bar_att`), - fmt.Errorf(`"bar_att": conflicts with foo_att`), - }, - }, - - "Computed + Optional fields NOT conflicting with each other": { - Schema: map[string]*Schema{ - "foo_att": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ConflictsWith: []string{"bar_att"}, - }, - "bar_att": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ConflictsWith: []string{"foo_att"}, - }, - }, - - Config: map[string]interface{}{ - "foo_att": "foo-val", - }, - - Err: false, - }, - - "Computed + Optional fields that conflict with none set": { - Schema: map[string]*Schema{ - "foo_att": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ConflictsWith: []string{"bar_att"}, - }, - "bar_att": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ConflictsWith: []string{"foo_att"}, - }, - }, - - Config: map[string]interface{}{}, - - Err: false, - }, - - "Good with ValidateFunc": { - Schema: map[string]*Schema{ - "validate_me": &Schema{ - Type: TypeString, - Required: true, - ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { - return - }, - }, - }, - Config: map[string]interface{}{ - "validate_me": "valid", - }, - Err: false, - }, - - "Bad with ValidateFunc": { - Schema: map[string]*Schema{ - "validate_me": &Schema{ - Type: TypeString, - Required: true, - ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { - es = append(es, fmt.Errorf("something is not right here")) - return - }, - }, - }, - Config: map[string]interface{}{ - "validate_me": "invalid", - }, - Err: true, - Errors: []error{ - fmt.Errorf(`something is not right here`), - }, - }, - - "ValidateFunc not called when type does not match": { - Schema: map[string]*Schema{ - "number": &Schema{ - Type: TypeInt, - Required: true, - ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { - t.Fatalf("Should not have gotten validate call") - return - }, - }, - }, - Config: map[string]interface{}{ - "number": "NaN", - }, - Err: true, - }, - - "ValidateFunc gets decoded type": { - Schema: map[string]*Schema{ - "maybe": &Schema{ - Type: TypeBool, - Required: true, - ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { - if _, ok := v.(bool); !ok { - t.Fatalf("Expected bool, got: %#v", v) - } - return - }, - }, - }, - Config: map[string]interface{}{ - "maybe": "true", - }, - }, - - "ValidateFunc is not called with a computed value": { - Schema: map[string]*Schema{ - "validate_me": &Schema{ - Type: TypeString, - Required: true, - ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { - es = append(es, fmt.Errorf("something is not right here")) - return - }, - }, - }, - Config: map[string]interface{}{ - "validate_me": hcl2shim.UnknownVariableValue, - }, - - Err: false, - }, - - "special timeouts field": { - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - Config: map[string]interface{}{ - TimeoutsConfigKey: "bar", - }, - - Err: false, - }, - - "invalid bool field": { - Schema: map[string]*Schema{ - "bool_field": { - Type: TypeBool, - Optional: true, - }, - }, - Config: map[string]interface{}{ - "bool_field": "abcdef", - }, - Err: true, - }, - "invalid integer field": { - Schema: map[string]*Schema{ - "integer_field": { - Type: TypeInt, - Optional: true, - }, - }, - Config: map[string]interface{}{ - "integer_field": "abcdef", - }, - Err: true, - }, - "invalid float field": { - Schema: map[string]*Schema{ - "float_field": { - Type: TypeFloat, - Optional: true, - }, - }, - Config: map[string]interface{}{ - "float_field": "abcdef", - }, - Err: true, - }, - - // Invalid map values - "invalid bool map value": { - Schema: map[string]*Schema{ - "boolMap": &Schema{ - Type: TypeMap, - Elem: TypeBool, - Optional: true, - }, - }, - Config: map[string]interface{}{ - "boolMap": map[string]interface{}{ - "boolField": "notbool", - }, - }, - Err: true, - }, - "invalid int map value": { - Schema: map[string]*Schema{ - "intMap": &Schema{ - Type: TypeMap, - Elem: TypeInt, - Optional: true, - }, - }, - Config: map[string]interface{}{ - "intMap": map[string]interface{}{ - "intField": "notInt", - }, - }, - Err: true, - }, - "invalid float map value": { - Schema: map[string]*Schema{ - "floatMap": &Schema{ - Type: TypeMap, - Elem: TypeFloat, - Optional: true, - }, - }, - Config: map[string]interface{}{ - "floatMap": map[string]interface{}{ - "floatField": "notFloat", - }, - }, - Err: true, - }, - - "map with positive validate function": { - Schema: map[string]*Schema{ - "floatInt": &Schema{ - Type: TypeMap, - Elem: TypeInt, - Optional: true, - ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { - return - }, - }, - }, - Config: map[string]interface{}{ - "floatInt": map[string]interface{}{ - "rightAnswer": "42", - "tooMuch": "43", - }, - }, - Err: false, - }, - "map with negative validate function": { - Schema: map[string]*Schema{ - "floatInt": &Schema{ - Type: TypeMap, - Elem: TypeInt, - Optional: true, - ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { - es = append(es, fmt.Errorf("this is not fine")) - return - }, - }, - }, - Config: map[string]interface{}{ - "floatInt": map[string]interface{}{ - "rightAnswer": "42", - "tooMuch": "43", - }, - }, - Err: true, - }, - - // The Validation function should not see interpolation strings from - // non-computed values. - "set with partially computed list and map": { - Schema: map[string]*Schema{ - "outer": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "list": &Schema{ - Type: TypeList, - Optional: true, - Elem: &Schema{ - Type: TypeString, - ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { - if strings.HasPrefix(v.(string), "${") { - es = append(es, fmt.Errorf("should not have interpolations")) - } - return - }, - }, - }, - }, - }, - }, - }, - Config: map[string]interface{}{ - "outer": []interface{}{ - map[string]interface{}{ - "list": []interface{}{"A", hcl2shim.UnknownVariableValue, "c"}, - }, - }, - }, - Err: false, - }, - "unexpected nils values": { - Schema: map[string]*Schema{ - "strings": &Schema{ - Type: TypeList, - Optional: true, - Elem: &Schema{ - Type: TypeString, - }, - }, - "block": &Schema{ - Type: TypeList, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "int": &Schema{ - Type: TypeInt, - Required: true, - }, - }, - }, - }, - }, - - Config: map[string]interface{}{ - "strings": []interface{}{"1", nil}, - "block": []interface{}{map[string]interface{}{ - "int": nil, - }, - nil, - }, - }, - Err: true, - }, - } - - for tn, tc := range cases { - t.Run(tn, func(t *testing.T) { - c := terraform.NewResourceConfigRaw(tc.Config) - - ws, es := schemaMap(tc.Schema).Validate(c) - if len(es) > 0 != tc.Err { - if len(es) == 0 { - t.Errorf("%q: no errors", tn) - } - - for _, e := range es { - t.Errorf("%q: err: %s", tn, e) - } - - t.FailNow() - } - - if !reflect.DeepEqual(ws, tc.Warnings) { - t.Fatalf("%q: warnings:\n\nexpected: %#v\ngot:%#v", tn, tc.Warnings, ws) - } - - if tc.Errors != nil { - sort.Sort(errorSort(es)) - sort.Sort(errorSort(tc.Errors)) - - if !reflect.DeepEqual(es, tc.Errors) { - t.Fatalf("%q: errors:\n\nexpected: %q\ngot: %q", tn, tc.Errors, es) - } - } - }) - - } -} - -func TestSchemaSet_ValidateMaxItems(t *testing.T) { - cases := map[string]struct { - Schema map[string]*Schema - State *terraform.InstanceState - Config map[string]interface{} - ConfigVariables map[string]string - Diff *terraform.InstanceDiff - Err bool - Errors []error - }{ - "#0": { - Schema: map[string]*Schema{ - "aliases": &Schema{ - Type: TypeSet, - Optional: true, - MaxItems: 1, - Elem: &Schema{Type: TypeString}, - }, - }, - State: nil, - Config: map[string]interface{}{ - "aliases": []interface{}{"foo", "bar"}, - }, - Diff: nil, - Err: true, - Errors: []error{ - fmt.Errorf("aliases: attribute supports 1 item maximum, config has 2 declared"), - }, - }, - "#1": { - Schema: map[string]*Schema{ - "aliases": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Schema{Type: TypeString}, - }, - }, - State: nil, - Config: map[string]interface{}{ - "aliases": []interface{}{"foo", "bar"}, - }, - Diff: nil, - Err: false, - Errors: nil, - }, - "#2": { - Schema: map[string]*Schema{ - "aliases": &Schema{ - Type: TypeSet, - Optional: true, - MaxItems: 1, - Elem: &Schema{Type: TypeString}, - }, - }, - State: nil, - Config: map[string]interface{}{ - "aliases": []interface{}{"foo"}, - }, - Diff: nil, - Err: false, - Errors: nil, - }, - } - - for tn, tc := range cases { - c := terraform.NewResourceConfigRaw(tc.Config) - _, es := schemaMap(tc.Schema).Validate(c) - - if len(es) > 0 != tc.Err { - if len(es) == 0 { - t.Errorf("%q: no errors", tn) - } - - for _, e := range es { - t.Errorf("%q: err: %s", tn, e) - } - - t.FailNow() - } - - if tc.Errors != nil { - if !reflect.DeepEqual(es, tc.Errors) { - t.Fatalf("%q: expected: %q\ngot: %q", tn, tc.Errors, es) - } - } - } -} - -func TestSchemaSet_ValidateMinItems(t *testing.T) { - cases := map[string]struct { - Schema map[string]*Schema - State *terraform.InstanceState - Config map[string]interface{} - ConfigVariables map[string]string - Diff *terraform.InstanceDiff - Err bool - Errors []error - }{ - "#0": { - Schema: map[string]*Schema{ - "aliases": &Schema{ - Type: TypeSet, - Optional: true, - MinItems: 2, - Elem: &Schema{Type: TypeString}, - }, - }, - State: nil, - Config: map[string]interface{}{ - "aliases": []interface{}{"foo", "bar"}, - }, - Diff: nil, - Err: false, - Errors: nil, - }, - "#1": { - Schema: map[string]*Schema{ - "aliases": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Schema{Type: TypeString}, - }, - }, - State: nil, - Config: map[string]interface{}{ - "aliases": []interface{}{"foo", "bar"}, - }, - Diff: nil, - Err: false, - Errors: nil, - }, - "#2": { - Schema: map[string]*Schema{ - "aliases": &Schema{ - Type: TypeSet, - Optional: true, - MinItems: 2, - Elem: &Schema{Type: TypeString}, - }, - }, - State: nil, - Config: map[string]interface{}{ - "aliases": []interface{}{"foo"}, - }, - Diff: nil, - Err: true, - Errors: []error{ - fmt.Errorf("aliases: attribute supports 2 item as a minimum, config has 1 declared"), - }, - }, - } - - for tn, tc := range cases { - c := terraform.NewResourceConfigRaw(tc.Config) - _, es := schemaMap(tc.Schema).Validate(c) - - if len(es) > 0 != tc.Err { - if len(es) == 0 { - t.Errorf("%q: no errors", tn) - } - - for _, e := range es { - t.Errorf("%q: err: %s", tn, e) - } - - t.FailNow() - } - - if tc.Errors != nil { - if !reflect.DeepEqual(es, tc.Errors) { - t.Fatalf("%q: expected: %q\ngot: %q", tn, tc.Errors, es) - } - } - } -} - -// errorSort implements sort.Interface to sort errors by their error message -type errorSort []error - -func (e errorSort) Len() int { return len(e) } -func (e errorSort) Swap(i, j int) { e[i], e[j] = e[j], e[i] } -func (e errorSort) Less(i, j int) bool { - return e[i].Error() < e[j].Error() -} - -func TestSchemaMapDeepCopy(t *testing.T) { - schema := map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - }, - } - source := schemaMap(schema) - dest := source.DeepCopy() - dest["foo"].ForceNew = true - if reflect.DeepEqual(source, dest) { - t.Fatalf("source and dest should not match") - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/set.go b/vendor/github.com/hashicorp/terraform/helper/schema/set.go deleted file mode 100644 index 8ee89e47..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/set.go +++ /dev/null @@ -1,250 +0,0 @@ -package schema - -import ( - "bytes" - "fmt" - "reflect" - "sort" - "strconv" - "sync" - - "github.com/hashicorp/terraform/helper/hashcode" -) - -// HashString hashes strings. If you want a Set of strings, this is the -// SchemaSetFunc you want. -func HashString(v interface{}) int { - return hashcode.String(v.(string)) -} - -// HashInt hashes integers. If you want a Set of integers, this is the -// SchemaSetFunc you want. -func HashInt(v interface{}) int { - return hashcode.String(strconv.Itoa(v.(int))) -} - -// HashResource hashes complex structures that are described using -// a *Resource. This is the default set implementation used when a set's -// element type is a full resource. -func HashResource(resource *Resource) SchemaSetFunc { - return func(v interface{}) int { - var buf bytes.Buffer - SerializeResourceForHash(&buf, v, resource) - return hashcode.String(buf.String()) - } -} - -// HashSchema hashes values that are described using a *Schema. This is the -// default set implementation used when a set's element type is a single -// schema. -func HashSchema(schema *Schema) SchemaSetFunc { - return func(v interface{}) int { - var buf bytes.Buffer - SerializeValueForHash(&buf, v, schema) - return hashcode.String(buf.String()) - } -} - -// Set is a set data structure that is returned for elements of type -// TypeSet. -type Set struct { - F SchemaSetFunc - - m map[string]interface{} - once sync.Once -} - -// NewSet is a convenience method for creating a new set with the given -// items. -func NewSet(f SchemaSetFunc, items []interface{}) *Set { - s := &Set{F: f} - for _, i := range items { - s.Add(i) - } - - return s -} - -// CopySet returns a copy of another set. -func CopySet(otherSet *Set) *Set { - return NewSet(otherSet.F, otherSet.List()) -} - -// Add adds an item to the set if it isn't already in the set. -func (s *Set) Add(item interface{}) { - s.add(item, false) -} - -// Remove removes an item if it's already in the set. Idempotent. -func (s *Set) Remove(item interface{}) { - s.remove(item) -} - -// Contains checks if the set has the given item. -func (s *Set) Contains(item interface{}) bool { - _, ok := s.m[s.hash(item)] - return ok -} - -// Len returns the amount of items in the set. -func (s *Set) Len() int { - return len(s.m) -} - -// List returns the elements of this set in slice format. -// -// The order of the returned elements is deterministic. Given the same -// set, the order of this will always be the same. -func (s *Set) List() []interface{} { - result := make([]interface{}, len(s.m)) - for i, k := range s.listCode() { - result[i] = s.m[k] - } - - return result -} - -// Difference performs a set difference of the two sets, returning -// a new third set that has only the elements unique to this set. -func (s *Set) Difference(other *Set) *Set { - result := &Set{F: s.F} - result.once.Do(result.init) - - for k, v := range s.m { - if _, ok := other.m[k]; !ok { - result.m[k] = v - } - } - - return result -} - -// Intersection performs the set intersection of the two sets -// and returns a new third set. -func (s *Set) Intersection(other *Set) *Set { - result := &Set{F: s.F} - result.once.Do(result.init) - - for k, v := range s.m { - if _, ok := other.m[k]; ok { - result.m[k] = v - } - } - - return result -} - -// Union performs the set union of the two sets and returns a new third -// set. -func (s *Set) Union(other *Set) *Set { - result := &Set{F: s.F} - result.once.Do(result.init) - - for k, v := range s.m { - result.m[k] = v - } - for k, v := range other.m { - result.m[k] = v - } - - return result -} - -func (s *Set) Equal(raw interface{}) bool { - other, ok := raw.(*Set) - if !ok { - return false - } - - return reflect.DeepEqual(s.m, other.m) -} - -// HashEqual simply checks to the keys the top-level map to the keys in the -// other set's top-level map to see if they are equal. This obviously assumes -// you have a properly working hash function - use HashResource if in doubt. -func (s *Set) HashEqual(raw interface{}) bool { - other, ok := raw.(*Set) - if !ok { - return false - } - - ks1 := make([]string, 0) - ks2 := make([]string, 0) - - for k := range s.m { - ks1 = append(ks1, k) - } - for k := range other.m { - ks2 = append(ks2, k) - } - - sort.Strings(ks1) - sort.Strings(ks2) - - return reflect.DeepEqual(ks1, ks2) -} - -func (s *Set) GoString() string { - return fmt.Sprintf("*Set(%#v)", s.m) -} - -func (s *Set) init() { - s.m = make(map[string]interface{}) -} - -func (s *Set) add(item interface{}, computed bool) string { - s.once.Do(s.init) - - code := s.hash(item) - if computed { - code = "~" + code - - if isProto5() { - tmpCode := code - count := 0 - for _, exists := s.m[tmpCode]; exists; _, exists = s.m[tmpCode] { - count++ - tmpCode = fmt.Sprintf("%s%d", code, count) - } - code = tmpCode - } - } - - if _, ok := s.m[code]; !ok { - s.m[code] = item - } - - return code -} - -func (s *Set) hash(item interface{}) string { - code := s.F(item) - // Always return a nonnegative hashcode. - if code < 0 { - code = -code - } - return strconv.Itoa(code) -} - -func (s *Set) remove(item interface{}) string { - s.once.Do(s.init) - - code := s.hash(item) - delete(s.m, code) - - return code -} - -func (s *Set) index(item interface{}) int { - return sort.SearchStrings(s.listCode(), s.hash(item)) -} - -func (s *Set) listCode() []string { - // Sort the hash codes so the order of the list is deterministic - keys := make([]string, 0, len(s.m)) - for k := range s.m { - keys = append(keys, k) - } - sort.Sort(sort.StringSlice(keys)) - return keys -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/shims_test.go b/vendor/github.com/hashicorp/terraform/helper/schema/shims_test.go deleted file mode 100644 index 050286a0..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/shims_test.go +++ /dev/null @@ -1,3521 +0,0 @@ -package schema - -import ( - "bytes" - "errors" - "fmt" - "reflect" - "strconv" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/configs/hcl2shim" - "github.com/hashicorp/terraform/helper/hashcode" - "github.com/hashicorp/terraform/providers" - "github.com/hashicorp/terraform/terraform" - "github.com/hashicorp/terraform/tfdiags" - "github.com/zclconf/go-cty/cty" -) - -var ( - typeComparer = cmp.Comparer(cty.Type.Equals) - valueComparer = cmp.Comparer(cty.Value.RawEquals) - equateEmpty = cmpopts.EquateEmpty() -) - -func testApplyDiff(t *testing.T, - resource *Resource, - state, expected *terraform.InstanceState, - diff *terraform.InstanceDiff) { - - testSchema := providers.Schema{ - Version: int64(resource.SchemaVersion), - Block: resourceSchemaToBlock(resource.Schema), - } - - stateVal, err := StateValueFromInstanceState(state, testSchema.Block.ImpliedType()) - if err != nil { - t.Fatal(err) - } - - newState, err := ApplyDiff(stateVal, diff, testSchema.Block) - if err != nil { - t.Fatal(err) - } - - // verify that "id" is correct - id := newState.AsValueMap()["id"] - - switch { - case diff.Destroy || diff.DestroyDeposed || diff.DestroyTainted: - // there should be no id - if !id.IsNull() { - t.Fatalf("destroyed instance should have no id: %#v", id) - } - default: - // the "id" field always exists and is computed, so it must have a - // valid value or be unknown. - if id.IsNull() { - t.Fatal("new instance state cannot have a null id") - } - - if id.IsKnown() && id.AsString() == "" { - t.Fatal("new instance id cannot be an empty string") - } - } - - // Resource.Meta will be hanlded separately, so it's OK that we lose the - // timeout values here. - expectedState, err := StateValueFromInstanceState(expected, testSchema.Block.ImpliedType()) - if err != nil { - t.Fatal(err) - } - - if !cmp.Equal(expectedState, newState, equateEmpty, typeComparer, valueComparer) { - t.Fatalf(cmp.Diff(expectedState, newState, equateEmpty, typeComparer, valueComparer)) - } -} - -func TestShimResourcePlan_destroyCreate(t *testing.T) { - r := &Resource{ - SchemaVersion: 2, - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - ForceNew: true, - }, - }, - } - - d := &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - RequiresNew: true, - Old: "3", - New: "42", - }, - }, - } - - state := &terraform.InstanceState{ - Attributes: map[string]string{"foo": "3"}, - } - - expected := &terraform.InstanceState{ - ID: hcl2shim.UnknownVariableValue, - Attributes: map[string]string{ - "id": hcl2shim.UnknownVariableValue, - "foo": "42", - }, - Meta: map[string]interface{}{ - "schema_version": "2", - }, - } - - testApplyDiff(t, r, state, expected, d) -} - -func TestShimResourceApply_create(t *testing.T) { - r := &Resource{ - SchemaVersion: 2, - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - called := false - r.Create = func(d *ResourceData, m interface{}) error { - called = true - d.SetId("foo") - return nil - } - - var s *terraform.InstanceState = nil - - d := &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - New: "42", - }, - }, - } - - actual, err := r.Apply(s, d, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - if !called { - t.Fatal("not called") - } - - expected := &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "id": "foo", - "foo": "42", - }, - Meta: map[string]interface{}{ - "schema_version": "2", - }, - } - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: %#v", actual) - } - - // Shim - // now that we have our diff and desired state, see if we can reproduce - // that with the shim - // we're not testing Resource.Create, so we need to start with the "created" state - createdState := &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{"id": "foo"}, - } - - testApplyDiff(t, r, createdState, expected, d) -} - -func TestShimResourceApply_Timeout_state(t *testing.T) { - r := &Resource{ - SchemaVersion: 2, - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - Timeouts: &ResourceTimeout{ - Create: DefaultTimeout(40 * time.Minute), - Update: DefaultTimeout(80 * time.Minute), - Delete: DefaultTimeout(40 * time.Minute), - }, - } - - called := false - r.Create = func(d *ResourceData, m interface{}) error { - called = true - d.SetId("foo") - return nil - } - - var s *terraform.InstanceState = nil - - d := &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - New: "42", - }, - }, - } - - diffTimeout := &ResourceTimeout{ - Create: DefaultTimeout(40 * time.Minute), - Update: DefaultTimeout(80 * time.Minute), - Delete: DefaultTimeout(40 * time.Minute), - } - - if err := diffTimeout.DiffEncode(d); err != nil { - t.Fatalf("Error encoding timeout to diff: %s", err) - } - - actual, err := r.Apply(s, d, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - if !called { - t.Fatal("not called") - } - - expected := &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "id": "foo", - "foo": "42", - }, - Meta: map[string]interface{}{ - "schema_version": "2", - TimeoutKey: expectedForValues(40, 0, 80, 40, 0), - }, - } - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("Not equal in Timeout State:\n\texpected: %#v\n\tactual: %#v", expected.Meta, actual.Meta) - } - - // Shim - // we're not testing Resource.Create, so we need to start with the "created" state - createdState := &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{"id": "foo"}, - } - - testApplyDiff(t, r, createdState, expected, d) -} - -func TestShimResourceDiff_Timeout_diff(t *testing.T) { - r := &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - Timeouts: &ResourceTimeout{ - Create: DefaultTimeout(40 * time.Minute), - Update: DefaultTimeout(80 * time.Minute), - Delete: DefaultTimeout(40 * time.Minute), - }, - } - - r.Create = func(d *ResourceData, m interface{}) error { - d.SetId("foo") - return nil - } - - conf := terraform.NewResourceConfigRaw(map[string]interface{}{ - "foo": 42, - TimeoutsConfigKey: map[string]interface{}{ - "create": "2h", - }, - }) - var s *terraform.InstanceState - - actual, err := r.Diff(s, conf, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - expected := &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - New: "42", - }, - }, - } - - diffTimeout := &ResourceTimeout{ - Create: DefaultTimeout(120 * time.Minute), - Update: DefaultTimeout(80 * time.Minute), - Delete: DefaultTimeout(40 * time.Minute), - } - - if err := diffTimeout.DiffEncode(expected); err != nil { - t.Fatalf("Error encoding timeout to diff: %s", err) - } - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("Not equal in Timeout Diff:\n\texpected: %#v\n\tactual: %#v", expected.Meta, actual.Meta) - } - - // Shim - // apply this diff, so we have a state to compare - applied, err := r.Apply(s, actual, nil) - if err != nil { - t.Fatal(err) - } - - // we're not testing Resource.Create, so we need to start with the "created" state - createdState := &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{"id": "foo"}, - } - - testSchema := providers.Schema{ - Version: int64(r.SchemaVersion), - Block: resourceSchemaToBlock(r.Schema), - } - - initialVal, err := StateValueFromInstanceState(createdState, testSchema.Block.ImpliedType()) - if err != nil { - t.Fatal(err) - } - - appliedVal, err := StateValueFromInstanceState(applied, testSchema.Block.ImpliedType()) - if err != nil { - t.Fatal(err) - } - - d, err := DiffFromValues(initialVal, appliedVal, r) - if err != nil { - t.Fatal(err) - } - if eq, _ := d.Same(expected); !eq { - t.Fatal(cmp.Diff(d, expected)) - } -} - -func TestShimResourceApply_destroy(t *testing.T) { - r := &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - } - - called := false - r.Delete = func(d *ResourceData, m interface{}) error { - called = true - return nil - } - - s := &terraform.InstanceState{ - ID: "bar", - } - - d := &terraform.InstanceDiff{ - Destroy: true, - } - - actual, err := r.Apply(s, d, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - if !called { - t.Fatal("delete not called") - } - - if actual != nil { - t.Fatalf("bad: %#v", actual) - } - - // Shim - // now that we have our diff and desired state, see if we can reproduce - // that with the shim - testApplyDiff(t, r, s, actual, d) -} - -func TestShimResourceApply_destroyCreate(t *testing.T) { - r := &Resource{ - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeInt, - Optional: true, - ForceNew: true, - }, - - "tags": &Schema{ - Type: TypeMap, - Optional: true, - Computed: true, - }, - }, - } - - change := false - r.Create = func(d *ResourceData, m interface{}) error { - change = d.HasChange("tags") - d.SetId("foo") - return nil - } - r.Delete = func(d *ResourceData, m interface{}) error { - return nil - } - - var s *terraform.InstanceState = &terraform.InstanceState{ - ID: "bar", - Attributes: map[string]string{ - "foo": "7", - "tags.Name": "foo", - }, - } - - d := &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "id": &terraform.ResourceAttrDiff{ - New: "foo", - }, - "foo": &terraform.ResourceAttrDiff{ - Old: "7", - New: "42", - RequiresNew: true, - }, - "tags.Name": &terraform.ResourceAttrDiff{ - Old: "foo", - New: "foo", - RequiresNew: true, - }, - }, - } - - actual, err := r.Apply(s, d, nil) - if err != nil { - t.Fatalf("err: %s", err) - } - - if !change { - t.Fatal("should have change") - } - - expected := &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "id": "foo", - "foo": "42", - "tags.%": "1", - "tags.Name": "foo", - }, - } - - if !reflect.DeepEqual(actual, expected) { - cmp.Diff(actual, expected) - } - - // Shim - // now that we have our diff and desired state, see if we can reproduce - // that with the shim - // we're not testing Resource.Create, so we need to start with the "created" state - createdState := &terraform.InstanceState{ - ID: "foo", - Attributes: map[string]string{ - "id": "foo", - "foo": "7", - "tags.%": "1", - "tags.Name": "foo", - }, - } - - testApplyDiff(t, r, createdState, expected, d) -} - -func TestShimSchemaMap_Diff(t *testing.T) { - cases := []struct { - Name string - Schema map[string]*Schema - State *terraform.InstanceState - Config map[string]interface{} - CustomizeDiff CustomizeDiffFunc - Diff *terraform.InstanceDiff - Err bool - }{ - { - Name: "diff-1", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "availability_zone": "foo", - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "diff-2", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{}, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - NewComputed: true, - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "diff-3", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "foo", - }, - - Config: map[string]interface{}{}, - - Diff: nil, - - Err: false, - }, - - { - Name: "Computed, but set in config", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "availability_zone": "foo", - }, - }, - - Config: map[string]interface{}{ - "availability_zone": "bar", - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "foo", - New: "bar", - }, - }, - }, - - Err: false, - }, - - { - Name: "Default", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Default: "foo", - }, - }, - - State: nil, - - Config: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - }, - }, - }, - - Err: false, - }, - - { - Name: "DefaultFunc, value", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - DefaultFunc: func() (interface{}, error) { - return "foo", nil - }, - }, - }, - - State: nil, - - Config: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - }, - }, - }, - - Err: false, - }, - - { - Name: "DefaultFunc, configuration set", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - DefaultFunc: func() (interface{}, error) { - return "foo", nil - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "availability_zone": "bar", - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "bar", - }, - }, - }, - - Err: false, - }, - - { - Name: "String with StateFunc", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - StateFunc: func(a interface{}) string { - return a.(string) + "!" - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "availability_zone": "foo", - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo!", - NewExtra: "foo", - }, - }, - }, - - Err: false, - }, - - { - Name: "StateFunc not called with nil value", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - StateFunc: func(a interface{}) string { - t.Error("should not get here!") - return "" - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{}, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Variable computed", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "availability_zone": hcl2shim.UnknownVariableValue, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: hcl2shim.UnknownVariableValue, - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Int decode", - Schema: map[string]*Schema{ - "port": &Schema{ - Type: TypeInt, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "port": 27, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "port": &terraform.ResourceAttrDiff{ - Old: "", - New: "27", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "bool decode", - Schema: map[string]*Schema{ - "port": &Schema{ - Type: TypeBool, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "port": false, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "port": &terraform.ResourceAttrDiff{ - Old: "", - New: "false", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Bool", - Schema: map[string]*Schema{ - "delete": &Schema{ - Type: TypeBool, - Optional: true, - Default: false, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "delete": "false", - }, - }, - - Config: nil, - - Diff: nil, - - Err: false, - }, - - { - Name: "List decode", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ports": []interface{}{1, 2, 5}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "3", - }, - "ports.0": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "ports.1": &terraform.ResourceAttrDiff{ - Old: "", - New: "2", - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - }, - }, - }, - - Err: false, - }, - - { - Name: "List decode with promotion with list", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - PromoteSingle: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ports": []interface{}{"5"}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - "ports.0": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - }, - }, - }, - - Err: false, - }, - - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ports": []interface{}{1, 2, 5}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "3", - }, - "ports.0": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "ports.1": &terraform.ResourceAttrDiff{ - Old: "", - New: "2", - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - }, - }, - }, - - Err: false, - }, - - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ports": []interface{}{1, hcl2shim.UnknownVariableValue, "5"}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "ports.#": "3", - "ports.0": "1", - "ports.1": "2", - "ports.2": "5", - }, - }, - - Config: map[string]interface{}{ - "ports": []interface{}{1, 2, 5}, - }, - - Diff: nil, - - Err: false, - }, - - { - Name: "", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "ports.#": "2", - "ports.0": "1", - "ports.1": "2", - }, - }, - - Config: map[string]interface{}{ - "ports": []interface{}{1, 2, 5}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "2", - New: "3", - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - }, - }, - }, - - Err: false, - }, - - { - Name: "", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Required: true, - Elem: &Schema{Type: TypeInt}, - ForceNew: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ports": []interface{}{1, 2, 5}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "3", - RequiresNew: true, - }, - "ports.0": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - RequiresNew: true, - }, - "ports.1": &terraform.ResourceAttrDiff{ - Old: "", - New: "2", - RequiresNew: true, - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: nil, - - Config: map[string]interface{}{}, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "List with computed set", - Schema: map[string]*Schema{ - "config": &Schema{ - Type: TypeList, - Optional: true, - ForceNew: true, - MinItems: 1, - Elem: &Resource{ - Schema: map[string]*Schema{ - "name": { - Type: TypeString, - Required: true, - }, - - "rules": { - Type: TypeSet, - Computed: true, - Elem: &Schema{Type: TypeString}, - Set: HashString, - }, - }, - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "config": []interface{}{ - map[string]interface{}{ - "name": "hello", - }, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "config.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - RequiresNew: true, - }, - - "config.0.name": &terraform.ResourceAttrDiff{ - Old: "", - New: "hello", - }, - - "config.0.rules.#": &terraform.ResourceAttrDiff{ - Old: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Set-1", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Required: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ports": []interface{}{5, 2, 1}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "3", - }, - "ports.1": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "", - New: "2", - }, - "ports.5": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - }, - }, - }, - - Err: false, - }, - - { - Name: "Set-2", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Computed: true, - Required: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "ports.#": "0", - }, - }, - - Config: nil, - - Diff: nil, - - Err: false, - }, - - { - Name: "Set-3", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: nil, - - Config: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Set-4", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Required: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ports": []interface{}{"2", "5", 1}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "3", - }, - "ports.1": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "", - New: "2", - }, - "ports.5": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - }, - }, - }, - - Err: false, - }, - - { - Name: "Set-5", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Required: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ports": []interface{}{1, hcl2shim.UnknownVariableValue, 5}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "", - New: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Set-6", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Required: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "ports.#": "2", - "ports.1": "1", - "ports.2": "2", - }, - }, - - Config: map[string]interface{}{ - "ports": []interface{}{5, 2, 1}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - Old: "2", - New: "3", - }, - "ports.1": &terraform.ResourceAttrDiff{ - Old: "1", - New: "1", - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "2", - New: "2", - }, - "ports.5": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - }, - }, - }, - - Err: false, - }, - - { - Name: "Set-8", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "availability_zone": "bar", - "ports.#": "1", - "ports.80": "80", - }, - }, - - Config: map[string]interface{}{}, - - Diff: nil, - - Err: false, - }, - - { - Name: "Set-9", - Schema: map[string]*Schema{ - "ingress": &Schema{ - Type: TypeSet, - Required: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeList, - Optional: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - }, - Set: func(v interface{}) int { - m := v.(map[string]interface{}) - ps := m["ports"].([]interface{}) - result := 0 - for _, p := range ps { - result += p.(int) - } - return result - }, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "ingress.#": "2", - "ingress.80.ports.#": "1", - "ingress.80.ports.0": "80", - "ingress.443.ports.#": "1", - "ingress.443.ports.0": "443", - }, - }, - - Config: map[string]interface{}{ - "ingress": []interface{}{ - map[string]interface{}{ - "ports": []interface{}{443}, - }, - map[string]interface{}{ - "ports": []interface{}{80}, - }, - }, - }, - - Diff: nil, - - Err: false, - }, - - { - Name: "List of structure decode", - Schema: map[string]*Schema{ - "ingress": &Schema{ - Type: TypeList, - Required: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "from": &Schema{ - Type: TypeInt, - Required: true, - }, - }, - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ingress": []interface{}{ - map[string]interface{}{ - "from": 8080, - }, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ingress.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - "ingress.0.from": &terraform.ResourceAttrDiff{ - Old: "", - New: "8080", - }, - }, - }, - - Err: false, - }, - - { - Name: "ComputedWhen", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Computed: true, - ComputedWhen: []string{"port"}, - }, - - "port": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "availability_zone": "foo", - "port": "80", - }, - }, - - Config: map[string]interface{}{ - "port": 80, - }, - - Diff: nil, - - Err: false, - }, - - { - Name: "computed", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Computed: true, - ComputedWhen: []string{"port"}, - }, - - "port": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "port": 80, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - NewComputed: true, - }, - "port": &terraform.ResourceAttrDiff{ - New: "80", - }, - }, - }, - - Err: false, - }, - - { - Name: "computed, exists", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Computed: true, - ComputedWhen: []string{"port"}, - }, - - "port": &Schema{ - Type: TypeInt, - Optional: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "port": "80", - }, - }, - - Config: map[string]interface{}{ - "port": 80, - }, - - // there is no computed diff when the instance exists already - Diff: nil, - - Err: false, - }, - - { - Name: "Maps-1", - Schema: map[string]*Schema{ - "config_vars": &Schema{ - Type: TypeMap, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "config_vars": map[string]interface{}{ - "bar": "baz", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "config_vars.%": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - - "config_vars.bar": &terraform.ResourceAttrDiff{ - Old: "", - New: "baz", - }, - }, - }, - - Err: false, - }, - - { - Name: "Maps-2", - Schema: map[string]*Schema{ - "config_vars": &Schema{ - Type: TypeMap, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "config_vars.%": "1", - "config_vars.foo": "bar", - }, - }, - - Config: map[string]interface{}{ - "config_vars": map[string]interface{}{ - "bar": "baz", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "config_vars.foo": &terraform.ResourceAttrDiff{ - Old: "bar", - NewRemoved: true, - }, - "config_vars.bar": &terraform.ResourceAttrDiff{ - Old: "", - New: "baz", - }, - }, - }, - - Err: false, - }, - - { - Name: "Maps-3", - Schema: map[string]*Schema{ - "vars": &Schema{ - Type: TypeMap, - Optional: true, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "vars.%": "1", - "vars.foo": "bar", - }, - }, - - Config: map[string]interface{}{ - "vars": map[string]interface{}{ - "bar": "baz", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "vars.foo": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "", - NewRemoved: true, - }, - "vars.bar": &terraform.ResourceAttrDiff{ - Old: "", - New: "baz", - }, - }, - }, - - Err: false, - }, - - { - Name: "Maps-4", - Schema: map[string]*Schema{ - "vars": &Schema{ - Type: TypeMap, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "vars.%": "1", - "vars.foo": "bar", - }, - }, - - Config: nil, - - Diff: nil, - - Err: false, - }, - - { - Name: "Maps-5", - Schema: map[string]*Schema{ - "config_vars": &Schema{ - Type: TypeList, - Elem: &Schema{Type: TypeMap}, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "config_vars.#": "1", - "config_vars.0.%": "1", - "config_vars.0.foo": "bar", - }, - }, - - Config: map[string]interface{}{ - "config_vars": []interface{}{ - map[string]interface{}{ - "bar": "baz", - }, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "config_vars.0.foo": &terraform.ResourceAttrDiff{ - Old: "bar", - NewRemoved: true, - }, - "config_vars.0.bar": &terraform.ResourceAttrDiff{ - Old: "", - New: "baz", - }, - }, - }, - - Err: false, - }, - - { - Name: "Maps-6", - Schema: map[string]*Schema{ - "config_vars": &Schema{ - Type: TypeList, - Elem: &Schema{Type: TypeMap}, - Optional: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "config_vars.#": "1", - "config_vars.0.%": "2", - "config_vars.0.foo": "bar", - "config_vars.0.bar": "baz", - }, - }, - - Config: map[string]interface{}{}, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "config_vars.#": &terraform.ResourceAttrDiff{ - Old: "1", - New: "0", - }, - "config_vars.0.%": &terraform.ResourceAttrDiff{ - Old: "2", - New: "0", - }, - "config_vars.0.foo": &terraform.ResourceAttrDiff{ - Old: "bar", - NewRemoved: true, - }, - "config_vars.0.bar": &terraform.ResourceAttrDiff{ - Old: "baz", - NewRemoved: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "ForceNews", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - ForceNew: true, - }, - - "address": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "availability_zone": "bar", - "address": "foo", - }, - }, - - Config: map[string]interface{}{ - "availability_zone": "foo", - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "foo", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Set-10", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - ForceNew: true, - }, - - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "availability_zone": "bar", - "ports.#": "1", - "ports.80": "80", - }, - }, - - Config: map[string]interface{}{ - "availability_zone": "foo", - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "bar", - New: "foo", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Set-11", - Schema: map[string]*Schema{ - "instances": &Schema{ - Type: TypeSet, - Elem: &Schema{Type: TypeString}, - Optional: true, - Computed: true, - Set: func(v interface{}) int { - return len(v.(string)) - }, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "instances.#": "0", - }, - }, - - Config: map[string]interface{}{ - "instances": []interface{}{hcl2shim.UnknownVariableValue}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "instances.#": &terraform.ResourceAttrDiff{ - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Set-12", - Schema: map[string]*Schema{ - "route": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "index": &Schema{ - Type: TypeInt, - Required: true, - }, - - "gateway": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - }, - Set: func(v interface{}) int { - m := v.(map[string]interface{}) - return m["index"].(int) - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "route": []interface{}{ - map[string]interface{}{ - "index": "1", - "gateway": hcl2shim.UnknownVariableValue, - }, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "route.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - "route.~1.index": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "route.~1.gateway": &terraform.ResourceAttrDiff{ - Old: "", - New: hcl2shim.UnknownVariableValue, - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Set-13", - Schema: map[string]*Schema{ - "route": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "index": &Schema{ - Type: TypeInt, - Required: true, - }, - - "gateway": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - }, - Set: func(v interface{}) int { - m := v.(map[string]interface{}) - return m["index"].(int) - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "route": []interface{}{ - map[string]interface{}{ - "index": "1", - "gateway": []interface{}{ - hcl2shim.UnknownVariableValue, - }, - }, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "route.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - "route.~1.index": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "route.~1.gateway.#": &terraform.ResourceAttrDiff{ - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Computed maps", - Schema: map[string]*Schema{ - "vars": &Schema{ - Type: TypeMap, - Computed: true, - }, - }, - - State: nil, - - Config: nil, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "vars.%": &terraform.ResourceAttrDiff{ - Old: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Computed maps", - Schema: map[string]*Schema{ - "vars": &Schema{ - Type: TypeMap, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "vars.%": "0", - }, - }, - - Config: map[string]interface{}{ - "vars": map[string]interface{}{ - "bar": hcl2shim.UnknownVariableValue, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "vars.%": &terraform.ResourceAttrDiff{ - Old: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Empty", - Schema: map[string]*Schema{}, - - State: &terraform.InstanceState{}, - - Config: map[string]interface{}{}, - - Diff: nil, - - Err: false, - }, - - { - Name: "Float", - Schema: map[string]*Schema{ - "some_threshold": &Schema{ - Type: TypeFloat, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "some_threshold": "567.8", - }, - }, - - Config: map[string]interface{}{ - "some_threshold": 12.34, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "some_threshold": &terraform.ResourceAttrDiff{ - Old: "567.8", - New: "12.34", - }, - }, - }, - - Err: false, - }, - - { - Name: "https://github.com/hashicorp/terraform/issues/824", - Schema: map[string]*Schema{ - "block_device": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "device_name": &Schema{ - Type: TypeString, - Required: true, - }, - "delete_on_termination": &Schema{ - Type: TypeBool, - Optional: true, - Default: true, - }, - }, - }, - Set: func(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string))) - buf.WriteString(fmt.Sprintf("%t-", m["delete_on_termination"].(bool))) - return hashcode.String(buf.String()) - }, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "block_device.#": "2", - "block_device.616397234.delete_on_termination": "true", - "block_device.616397234.device_name": "/dev/sda1", - "block_device.2801811477.delete_on_termination": "true", - "block_device.2801811477.device_name": "/dev/sdx", - }, - }, - - Config: map[string]interface{}{ - "block_device": []interface{}{ - map[string]interface{}{ - "device_name": "/dev/sda1", - }, - map[string]interface{}{ - "device_name": "/dev/sdx", - }, - }, - }, - Diff: nil, - Err: false, - }, - - { - Name: "Zero value in state shouldn't result in diff", - Schema: map[string]*Schema{ - "port": &Schema{ - Type: TypeBool, - Optional: true, - ForceNew: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "port": "false", - }, - }, - - Config: map[string]interface{}{}, - - Diff: nil, - - Err: false, - }, - - { - Name: "Same as prev, but for sets", - Schema: map[string]*Schema{ - "route": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "index": &Schema{ - Type: TypeInt, - Required: true, - }, - - "gateway": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - }, - Set: func(v interface{}) int { - m := v.(map[string]interface{}) - return m["index"].(int) - }, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "route.#": "0", - }, - }, - - Config: map[string]interface{}{}, - - Diff: nil, - - Err: false, - }, - - { - Name: "A set computed element shouldn't cause a diff", - Schema: map[string]*Schema{ - "active": &Schema{ - Type: TypeBool, - Computed: true, - ForceNew: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "active": "true", - }, - }, - - Config: map[string]interface{}{}, - - Diff: nil, - - Err: false, - }, - - { - Name: "An empty set should show up in the diff", - Schema: map[string]*Schema{ - "instances": &Schema{ - Type: TypeSet, - Elem: &Schema{Type: TypeString}, - Optional: true, - ForceNew: true, - Set: func(v interface{}) int { - return len(v.(string)) - }, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "instances.#": "1", - "instances.3": "foo", - }, - }, - - Config: map[string]interface{}{}, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "instances.#": &terraform.ResourceAttrDiff{ - Old: "1", - New: "0", - RequiresNew: true, - }, - "instances.3": &terraform.ResourceAttrDiff{ - Old: "foo", - New: "", - NewRemoved: true, - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Map with empty value", - Schema: map[string]*Schema{ - "vars": &Schema{ - Type: TypeMap, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "vars": map[string]interface{}{ - "foo": "", - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "vars.%": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - "vars.foo": &terraform.ResourceAttrDiff{ - Old: "", - New: "", - }, - }, - }, - - Err: false, - }, - - { - Name: "Unset bool, not in state", - Schema: map[string]*Schema{ - "force": &Schema{ - Type: TypeBool, - Optional: true, - ForceNew: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{}, - - Diff: nil, - - Err: false, - }, - - { - Name: "Unset set, not in state", - Schema: map[string]*Schema{ - "metadata_keys": &Schema{ - Type: TypeSet, - Optional: true, - ForceNew: true, - Elem: &Schema{Type: TypeInt}, - Set: func(interface{}) int { return 0 }, - }, - }, - - State: nil, - - Config: map[string]interface{}{}, - - Diff: nil, - - Err: false, - }, - - { - Name: "Unset list in state, should not show up computed", - Schema: map[string]*Schema{ - "metadata_keys": &Schema{ - Type: TypeList, - Optional: true, - Computed: true, - ForceNew: true, - Elem: &Schema{Type: TypeInt}, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "metadata_keys.#": "0", - }, - }, - - Config: map[string]interface{}{}, - - Diff: nil, - - Err: false, - }, - - { - Name: "Computed map without config that's known to be empty does not generate diff", - Schema: map[string]*Schema{ - "tags": &Schema{ - Type: TypeMap, - Computed: true, - }, - }, - - Config: nil, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "tags.%": "0", - }, - }, - - Diff: nil, - - Err: false, - }, - - { - Name: "Set with hyphen keys", - Schema: map[string]*Schema{ - "route": &Schema{ - Type: TypeSet, - Optional: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "index": &Schema{ - Type: TypeInt, - Required: true, - }, - - "gateway-name": &Schema{ - Type: TypeString, - Optional: true, - }, - }, - }, - Set: func(v interface{}) int { - m := v.(map[string]interface{}) - return m["index"].(int) - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "route": []interface{}{ - map[string]interface{}{ - "index": "1", - "gateway-name": "hello", - }, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "route.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - }, - "route.1.index": &terraform.ResourceAttrDiff{ - Old: "", - New: "1", - }, - "route.1.gateway-name": &terraform.ResourceAttrDiff{ - Old: "", - New: "hello", - }, - }, - }, - - Err: false, - }, - - { - Name: "StateFunc in nested set (#1759)", - Schema: map[string]*Schema{ - "service_account": &Schema{ - Type: TypeList, - Optional: true, - ForceNew: true, - Elem: &Resource{ - Schema: map[string]*Schema{ - "scopes": &Schema{ - Type: TypeSet, - Required: true, - ForceNew: true, - Elem: &Schema{ - Type: TypeString, - StateFunc: func(v interface{}) string { - return v.(string) + "!" - }, - }, - Set: func(v interface{}) int { - i, err := strconv.Atoi(v.(string)) - if err != nil { - t.Fatalf("err: %s", err) - } - return i - }, - }, - }, - }, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "service_account": []interface{}{ - map[string]interface{}{ - "scopes": []interface{}{"123"}, - }, - }, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "service_account.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - RequiresNew: true, - }, - "service_account.0.scopes.#": &terraform.ResourceAttrDiff{ - Old: "0", - New: "1", - RequiresNew: true, - }, - "service_account.0.scopes.123": &terraform.ResourceAttrDiff{ - Old: "", - New: "123!", - NewExtra: "123", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Removing set elements", - Schema: map[string]*Schema{ - "instances": &Schema{ - Type: TypeSet, - Elem: &Schema{Type: TypeString}, - Optional: true, - ForceNew: true, - Set: func(v interface{}) int { - return len(v.(string)) - }, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "instances.#": "2", - "instances.3": "333", - "instances.2": "22", - }, - }, - - Config: map[string]interface{}{ - "instances": []interface{}{"333", "4444"}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "instances.2": &terraform.ResourceAttrDiff{ - Old: "22", - New: "", - NewRemoved: true, - RequiresNew: true, - }, - "instances.3": &terraform.ResourceAttrDiff{ - Old: "333", - New: "333", - }, - "instances.4": &terraform.ResourceAttrDiff{ - Old: "", - New: "4444", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Bools can be set with 0/1 in config, still get true/false", - Schema: map[string]*Schema{ - "one": &Schema{ - Type: TypeBool, - Optional: true, - }, - "two": &Schema{ - Type: TypeBool, - Optional: true, - }, - "three": &Schema{ - Type: TypeBool, - Optional: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "one": "false", - "two": "true", - "three": "true", - }, - }, - - Config: map[string]interface{}{ - "one": "1", - "two": "0", - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "one": &terraform.ResourceAttrDiff{ - Old: "false", - New: "true", - }, - "two": &terraform.ResourceAttrDiff{ - Old: "true", - New: "false", - }, - "three": &terraform.ResourceAttrDiff{ - Old: "true", - New: "false", - NewRemoved: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "tainted in state w/ no attr changes is still a replacement", - Schema: map[string]*Schema{}, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "id": "someid", - }, - Tainted: true, - }, - - Config: map[string]interface{}{}, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{}, - DestroyTainted: true, - }, - }, - - { - Name: "Set ForceNew only marks the changing element as ForceNew", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Required: true, - ForceNew: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "ports.#": "3", - "ports.1": "1", - "ports.2": "2", - "ports.4": "4", - }, - }, - - Config: map[string]interface{}{ - "ports": []interface{}{5, 2, 1}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.1": &terraform.ResourceAttrDiff{ - Old: "1", - New: "1", - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "2", - New: "2", - }, - "ports.5": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - RequiresNew: true, - }, - "ports.4": &terraform.ResourceAttrDiff{ - Old: "4", - New: "0", - NewRemoved: true, - RequiresNew: true, - }, - }, - }, - }, - - { - Name: "removed optional items should trigger ForceNew", - Schema: map[string]*Schema{ - "description": &Schema{ - Type: TypeString, - ForceNew: true, - Optional: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "description": "foo", - }, - }, - - Config: map[string]interface{}{}, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "description": &terraform.ResourceAttrDiff{ - Old: "foo", - New: "", - RequiresNew: true, - NewRemoved: true, - }, - }, - }, - - Err: false, - }, - - // GH-7715 - { - Name: "computed value for boolean field", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeBool, - ForceNew: true, - Computed: true, - Optional: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - }, - - Config: map[string]interface{}{ - "foo": hcl2shim.UnknownVariableValue, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "", - New: "false", - NewComputed: true, - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "Set ForceNew marks count as ForceNew if computed", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Required: true, - ForceNew: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "ports.#": "3", - "ports.1": "1", - "ports.2": "2", - "ports.4": "4", - }, - }, - - Config: map[string]interface{}{ - "ports": []interface{}{hcl2shim.UnknownVariableValue, 2, 1}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.#": &terraform.ResourceAttrDiff{ - NewComputed: true, - RequiresNew: true, - }, - }, - }, - }, - - { - Name: "List with computed schema and ForceNew", - Schema: map[string]*Schema{ - "config": &Schema{ - Type: TypeList, - Optional: true, - ForceNew: true, - Elem: &Schema{ - Type: TypeString, - }, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "config.#": "2", - "config.0": "a", - "config.1": "b", - }, - }, - - Config: map[string]interface{}{ - "config": []interface{}{hcl2shim.UnknownVariableValue, hcl2shim.UnknownVariableValue}, - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "config.#": &terraform.ResourceAttrDiff{ - Old: "2", - New: "", - RequiresNew: true, - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "overridden diff with a CustomizeDiff function, ForceNew not in schema", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "availability_zone": "foo", - }, - - CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { - if err := d.SetNew("availability_zone", "bar"); err != nil { - return err - } - if err := d.ForceNew("availability_zone"); err != nil { - return err - } - return nil - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "bar", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - // NOTE: This case is technically impossible in the current - // implementation, because optional+computed values never show up in the - // diff. In the event behavior changes this test should ensure that the - // intended diff still shows up. - Name: "overridden removed attribute diff with a CustomizeDiff function, ForceNew not in schema", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{}, - - CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { - if err := d.SetNew("availability_zone", "bar"); err != nil { - return err - } - if err := d.ForceNew("availability_zone"); err != nil { - return err - } - return nil - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "bar", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - - Name: "overridden diff with a CustomizeDiff function, ForceNew in schema", - Schema: map[string]*Schema{ - "availability_zone": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "availability_zone": "foo", - }, - - CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { - if err := d.SetNew("availability_zone", "bar"); err != nil { - return err - } - return nil - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "availability_zone": &terraform.ResourceAttrDiff{ - Old: "", - New: "bar", - RequiresNew: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "required field with computed diff added with CustomizeDiff function", - Schema: map[string]*Schema{ - "ami_id": &Schema{ - Type: TypeString, - Required: true, - }, - "instance_id": &Schema{ - Type: TypeString, - Computed: true, - }, - }, - - State: nil, - - Config: map[string]interface{}{ - "ami_id": "foo", - }, - - CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { - if err := d.SetNew("instance_id", "bar"); err != nil { - return err - } - return nil - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ami_id": &terraform.ResourceAttrDiff{ - Old: "", - New: "foo", - }, - "instance_id": &terraform.ResourceAttrDiff{ - Old: "", - New: "bar", - }, - }, - }, - - Err: false, - }, - - { - Name: "Set ForceNew only marks the changing element as ForceNew - CustomizeDiffFunc edition", - Schema: map[string]*Schema{ - "ports": &Schema{ - Type: TypeSet, - Optional: true, - Computed: true, - Elem: &Schema{Type: TypeInt}, - Set: func(a interface{}) int { - return a.(int) - }, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "ports.#": "3", - "ports.1": "1", - "ports.2": "2", - "ports.4": "4", - }, - }, - - Config: map[string]interface{}{ - "ports": []interface{}{5, 2, 6}, - }, - - CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { - if err := d.SetNew("ports", []interface{}{5, 2, 1}); err != nil { - return err - } - if err := d.ForceNew("ports"); err != nil { - return err - } - return nil - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "ports.1": &terraform.ResourceAttrDiff{ - Old: "1", - New: "1", - }, - "ports.2": &terraform.ResourceAttrDiff{ - Old: "2", - New: "2", - }, - "ports.5": &terraform.ResourceAttrDiff{ - Old: "", - New: "5", - RequiresNew: true, - }, - "ports.4": &terraform.ResourceAttrDiff{ - Old: "4", - New: "0", - NewRemoved: true, - RequiresNew: true, - }, - }, - }, - }, - - { - Name: "tainted resource does not run CustomizeDiffFunc", - Schema: map[string]*Schema{}, - - State: &terraform.InstanceState{ - ID: "someid", - Attributes: map[string]string{ - "id": "someid", - }, - Tainted: true, - }, - - Config: map[string]interface{}{}, - - CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { - return errors.New("diff customization should not have run") - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{}, - DestroyTainted: true, - }, - - Err: false, - }, - - { - Name: "NewComputed based on a conditional with CustomizeDiffFunc", - Schema: map[string]*Schema{ - "etag": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - "version_id": &Schema{ - Type: TypeString, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "etag": "foo", - "version_id": "1", - }, - }, - - Config: map[string]interface{}{ - "etag": "bar", - }, - - CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { - if d.HasChange("etag") { - d.SetNewComputed("version_id") - } - return nil - }, - - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "etag": &terraform.ResourceAttrDiff{ - Old: "foo", - New: "bar", - }, - "version_id": &terraform.ResourceAttrDiff{ - Old: "1", - New: "", - NewComputed: true, - }, - }, - }, - - Err: false, - }, - - { - Name: "vetoing a diff", - Schema: map[string]*Schema{ - "foo": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "foo": "bar", - }, - }, - - Config: map[string]interface{}{ - "foo": "baz", - }, - - CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { - return fmt.Errorf("diff vetoed") - }, - - Err: true, - }, - - // A lot of resources currently depended on using the empty string as a - // nil/unset value. - { - Name: "optional, computed, empty string", - Schema: map[string]*Schema{ - "attr": &Schema{ - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "attr": "bar", - }, - }, - - Config: map[string]interface{}{ - "attr": "", - }, - }, - - { - Name: "optional, computed, empty string should not crash in CustomizeDiff", - Schema: map[string]*Schema{ - "unrelated_set": { - Type: TypeSet, - Optional: true, - Elem: &Schema{Type: TypeString}, - }, - "stream_enabled": { - Type: TypeBool, - Optional: true, - }, - "stream_view_type": { - Type: TypeString, - Optional: true, - Computed: true, - }, - }, - - State: &terraform.InstanceState{ - ID: "id", - Attributes: map[string]string{ - "unrelated_set.#": "0", - "stream_enabled": "true", - "stream_view_type": "KEYS_ONLY", - }, - }, - Config: map[string]interface{}{ - "stream_enabled": false, - "stream_view_type": "", - }, - CustomizeDiff: func(diff *ResourceDiff, v interface{}) error { - v, ok := diff.GetOk("unrelated_set") - if ok { - return fmt.Errorf("Didn't expect unrelated_set: %#v", v) - } - return nil - }, - Diff: &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "stream_enabled": { - Old: "true", - New: "false", - }, - }, - }, - }, - } - - for i, tc := range cases { - t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { - c := terraform.NewResourceConfigRaw(tc.Config) - - { - d, err := schemaMap(tc.Schema).Diff(tc.State, c, tc.CustomizeDiff, nil, false) - if err != nil != tc.Err { - t.Fatalf("err: %s", err) - } - if !cmp.Equal(d, tc.Diff, equateEmpty) { - t.Fatal(cmp.Diff(d, tc.Diff, equateEmpty)) - } - } - // up to here is already tested in helper/schema; we're just - // verify that we haven't broken any tests in transition. - - // create a schema from the schemaMap - testSchema := resourceSchemaToBlock(tc.Schema) - - // get our initial state cty.Value - stateVal, err := StateValueFromInstanceState(tc.State, testSchema.ImpliedType()) - if err != nil { - t.Fatal(err) - } - - // this is the desired cty.Value from the configuration - configVal := hcl2shim.HCL2ValueFromConfigValue(c.Config) - - // verify that we can round-trip the config - origConfig := hcl2shim.ConfigValueFromHCL2(configVal) - if !cmp.Equal(c.Config, origConfig, equateEmpty) { - t.Fatal(cmp.Diff(c.Config, origConfig, equateEmpty)) - } - - // make sure our config conforms precisely to the schema - configVal, err = testSchema.CoerceValue(configVal) - if err != nil { - t.Fatal(tfdiags.FormatError(err)) - } - - // The new API requires returning the desired state rather than a - // diff, so we need to verify that we can combine the state and - // diff and recreate a new state. - - // now verify that we can create diff, using the new config and state values - // customize isn't run on tainted resources - tainted := tc.State != nil && tc.State.Tainted - if tainted { - tc.CustomizeDiff = nil - } - - res := &Resource{Schema: tc.Schema} - - d, err := diffFromValues(stateVal, configVal, res, tc.CustomizeDiff) - if err != nil { - if !tc.Err { - t.Fatal(err) - } - } - - // In a real "apply" operation there would be no unknown values, - // so for tests containing unknowns we'll stop here: the steps - // after this point apply only to the apply phase. - if !configVal.IsWhollyKnown() { - return - } - - // our diff function can't set DestroyTainted, but match the - // expected value here for the test fixtures - if tainted { - if d == nil { - d = &terraform.InstanceDiff{} - } - d.DestroyTainted = true - } - - if eq, _ := d.Same(tc.Diff); !eq { - t.Fatal(cmp.Diff(d, tc.Diff)) - } - - }) - } -} - -func resourceSchemaToBlock(s map[string]*Schema) *configschema.Block { - return (&Resource{Schema: s}).CoreConfigSchema() -} - -func TestRemoveConfigUnknowns(t *testing.T) { - cfg := map[string]interface{}{ - "id": "74D93920-ED26-11E3-AC10-0800200C9A66", - "route_rules": []interface{}{ - map[string]interface{}{ - "cidr_block": "74D93920-ED26-11E3-AC10-0800200C9A66", - "destination": "0.0.0.0/0", - "destination_type": "CIDR_BLOCK", - "network_entity_id": "1", - }, - map[string]interface{}{ - "cidr_block": "74D93920-ED26-11E3-AC10-0800200C9A66", - "destination": "0.0.0.0/0", - "destination_type": "CIDR_BLOCK", - "sub_block": []interface{}{ - map[string]interface{}{ - "computed": "74D93920-ED26-11E3-AC10-0800200C9A66", - }, - }, - }, - }, - } - - expect := map[string]interface{}{ - "route_rules": []interface{}{ - map[string]interface{}{ - "destination": "0.0.0.0/0", - "destination_type": "CIDR_BLOCK", - "network_entity_id": "1", - }, - map[string]interface{}{ - "destination": "0.0.0.0/0", - "destination_type": "CIDR_BLOCK", - "sub_block": []interface{}{ - map[string]interface{}{}, - }, - }, - }, - } - - removeConfigUnknowns(cfg) - - if !reflect.DeepEqual(cfg, expect) { - t.Fatalf("\nexpected: %#v\ngot: %#v", expect, cfg) - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/testing.go b/vendor/github.com/hashicorp/terraform/helper/schema/testing.go deleted file mode 100644 index 12278217..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/schema/testing.go +++ /dev/null @@ -1,28 +0,0 @@ -package schema - -import ( - "testing" - - "github.com/hashicorp/terraform/terraform" -) - -// TestResourceDataRaw creates a ResourceData from a raw configuration map. -func TestResourceDataRaw( - t *testing.T, schema map[string]*Schema, raw map[string]interface{}) *ResourceData { - t.Helper() - - c := terraform.NewResourceConfigRaw(raw) - - sm := schemaMap(schema) - diff, err := sm.Diff(nil, c, nil, nil, true) - if err != nil { - t.Fatalf("err: %s", err) - } - - result, err := sm.Data(nil, diff) - if err != nil { - t.Fatalf("err: %s", err) - } - - return result -} diff --git a/vendor/github.com/hashicorp/terraform/helper/validation/validation.go b/vendor/github.com/hashicorp/terraform/helper/validation/validation.go deleted file mode 100644 index 484f7d7d..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/validation/validation.go +++ /dev/null @@ -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 - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/validation/validation_test.go b/vendor/github.com/hashicorp/terraform/helper/validation/validation_test.go deleted file mode 100644 index b47fe782..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/validation/validation_test.go +++ /dev/null @@ -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) - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/helper/wrappedstreams/streams.go b/vendor/github.com/hashicorp/terraform/helper/wrappedstreams/streams.go deleted file mode 100644 index b661ed73..00000000 --- a/vendor/github.com/hashicorp/terraform/helper/wrappedstreams/streams.go +++ /dev/null @@ -1,44 +0,0 @@ -// Package wrappedstreams provides access to the standard OS streams -// (stdin, stdout, stderr) even if wrapped under panicwrap. -package wrappedstreams - -import ( - "os" - - "github.com/mitchellh/panicwrap" -) - -// Stdin returns the true stdin of the process. -func Stdin() *os.File { - stdin, _, _ := fds() - return stdin -} - -// Stdout returns the true stdout of the process. -func Stdout() *os.File { - _, stdout, _ := fds() - return stdout -} - -// Stderr returns the true stderr of the process. -func Stderr() *os.File { - _, _, stderr := fds() - return stderr -} - -func fds() (stdin, stdout, stderr *os.File) { - stdin, stdout, stderr = os.Stdin, os.Stdout, os.Stderr - if panicwrap.Wrapped(nil) { - initPlatform() - stdin, stdout, stderr = wrappedStdin, wrappedStdout, wrappedStderr - } - return -} - -// These are the wrapped standard streams. These are setup by the -// platform specific code in initPlatform. -var ( - wrappedStdin *os.File - wrappedStdout *os.File - wrappedStderr *os.File -) diff --git a/vendor/github.com/hashicorp/terraform/instances/expander_test.go b/vendor/github.com/hashicorp/terraform/instances/expander_test.go index e143d1b9..900d716d 100644 --- a/vendor/github.com/hashicorp/terraform/instances/expander_test.go +++ b/vendor/github.com/hashicorp/terraform/instances/expander_test.go @@ -433,17 +433,6 @@ func TestExpander(t *testing.T) { }) } -func mustResourceAddr(str string) addrs.Resource { - addr, diags := addrs.ParseAbsResourceStr(str) - if diags.HasErrors() { - panic(fmt.Sprintf("invalid resource address: %s", diags.Err())) - } - if !addr.Module.IsRoot() { - panic("invalid resource address: includes module path") - } - return addr.Resource -} - func mustAbsResourceInstanceAddr(str string) addrs.AbsResourceInstance { addr, diags := addrs.ParseAbsResourceInstanceStr(str) if diags.HasErrors() { diff --git a/vendor/github.com/hashicorp/terraform/internal/depsfile/locks.go b/vendor/github.com/hashicorp/terraform/internal/depsfile/locks.go index a53994a6..e636522a 100644 --- a/vendor/github.com/hashicorp/terraform/internal/depsfile/locks.go +++ b/vendor/github.com/hashicorp/terraform/internal/depsfile/locks.go @@ -203,6 +203,23 @@ func (l *Locks) Equal(other *Locks) bool { return true } +// EqualProviderAddress returns true if the given Locks have the same provider +// address as the receiver. This doesn't check version and hashes. +func (l *Locks) EqualProviderAddress(other *Locks) bool { + if len(l.providers) != len(other.providers) { + return false + } + + for addr := range l.providers { + _, ok := other.providers[addr] + if !ok { + return false + } + } + + return true +} + // Empty returns true if the given Locks object contains no actual locks. // // UI code might wish to use this to distinguish a lock file being diff --git a/vendor/github.com/hashicorp/terraform/internal/depsfile/locks_file.go b/vendor/github.com/hashicorp/terraform/internal/depsfile/locks_file.go index 1fd7f558..2c38175a 100644 --- a/vendor/github.com/hashicorp/terraform/internal/depsfile/locks_file.go +++ b/vendor/github.com/hashicorp/terraform/internal/depsfile/locks_file.go @@ -2,7 +2,6 @@ package depsfile import ( "fmt" - "os" "sort" "github.com/hashicorp/hcl/v2" @@ -31,14 +30,40 @@ import ( // If the returned diagnostics contains errors then the returned Locks may // be incomplete or invalid. func LoadLocksFromFile(filename string) (*Locks, tfdiags.Diagnostics) { + return loadLocks(func(parser *hclparse.Parser) (*hcl.File, hcl.Diagnostics) { + return parser.ParseHCLFile(filename) + }) +} + +// LoadLocksFromBytes reads locks from the given byte array, pretending that +// it was read from the given filename. +// +// The constraints and behaviors are otherwise the same as for +// LoadLocksFromFile. LoadLocksFromBytes is primarily to allow more convenient +// integration testing (avoiding creating temporary files on disk); if you +// are writing non-test code, consider whether LoadLocksFromFile might be +// more appropriate to call. +func LoadLocksFromBytes(src []byte, filename string) (*Locks, tfdiags.Diagnostics) { + return loadLocks(func(parser *hclparse.Parser) (*hcl.File, hcl.Diagnostics) { + return parser.ParseHCL(src, filename) + }) +} + +func loadLocks(loadParse func(*hclparse.Parser) (*hcl.File, hcl.Diagnostics)) (*Locks, tfdiags.Diagnostics) { ret := NewLocks() var diags tfdiags.Diagnostics parser := hclparse.NewParser() - f, hclDiags := parser.ParseHCLFile(filename) + f, hclDiags := loadParse(parser) ret.sources = parser.Sources() diags = diags.Append(hclDiags) + if f == nil { + // If we encountered an error loading the file then those errors + // should already be in diags from the above, but the file might + // also be nil itself and so we can't decode from it. + return ret, diags + } moreDiags := decodeLocksFromHCL(ret, f.Body) diags = diags.Append(moreDiags) @@ -108,7 +133,7 @@ func SaveLocksToFile(locks *Locks, filename string) tfdiags.Diagnostics { newContent := f.Bytes() - err := replacefile.AtomicWriteFile(filename, newContent, os.ModePerm) + err := replacefile.AtomicWriteFile(filename, newContent, 0644) if err != nil { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, diff --git a/vendor/github.com/hashicorp/terraform/internal/depsfile/locks_file_test.go b/vendor/github.com/hashicorp/terraform/internal/depsfile/locks_file_test.go index 6019a071..9e0d0f42 100644 --- a/vendor/github.com/hashicorp/terraform/internal/depsfile/locks_file_test.go +++ b/vendor/github.com/hashicorp/terraform/internal/depsfile/locks_file_test.go @@ -159,6 +159,45 @@ func TestLoadLocksFromFile(t *testing.T) { } } +func TestLoadLocksFromFileAbsent(t *testing.T) { + t.Run("lock file is a directory", func(t *testing.T) { + // This can never happen when Terraform is the one generating the + // lock file, but might arise if the user makes a directory with the + // lock file's name for some reason. (There is no actual reason to do + // so, so that would always be a mistake.) + locks, diags := LoadLocksFromFile("testdata") + if len(locks.providers) != 0 { + t.Errorf("returned locks has providers; expected empty locks") + } + if !diags.HasErrors() { + t.Fatalf("LoadLocksFromFile succeeded; want error") + } + // This is a generic error message from HCL itself, so upgrading HCL + // in future might cause a different error message here. + want := `Failed to read file: The configuration file "testdata" could not be read.` + got := diags.Err().Error() + if got != want { + t.Errorf("wrong error message\ngot: %s\nwant: %s", got, want) + } + }) + t.Run("lock file doesn't exist", func(t *testing.T) { + locks, diags := LoadLocksFromFile("testdata/nonexist.hcl") + if len(locks.providers) != 0 { + t.Errorf("returned locks has providers; expected empty locks") + } + if !diags.HasErrors() { + t.Fatalf("LoadLocksFromFile succeeded; want error") + } + // This is a generic error message from HCL itself, so upgrading HCL + // in future might cause a different error message here. + want := `Failed to read file: The configuration file "testdata/nonexist.hcl" could not be read.` + got := diags.Err().Error() + if got != want { + t.Errorf("wrong error message\ngot: %s\nwant: %s", got, want) + } + }) +} + func TestSaveLocksToFile(t *testing.T) { locks := NewLocks() @@ -193,6 +232,14 @@ func TestSaveLocksToFile(t *testing.T) { t.Fatalf("unexpected errors\n%s", diags.Err().Error()) } + fileInfo, err := os.Stat(filename) + if err != nil { + t.Fatalf(err.Error()) + } + if mode := fileInfo.Mode(); mode&0111 != 0 { + t.Fatalf("Expected lock file to be non-executable: %o", mode) + } + gotContentBytes, err := ioutil.ReadFile(filename) if err != nil { t.Fatalf(err.Error()) diff --git a/vendor/github.com/hashicorp/terraform/internal/depsfile/locks_test.go b/vendor/github.com/hashicorp/terraform/internal/depsfile/locks_test.go index 1723113f..9e319415 100644 --- a/vendor/github.com/hashicorp/terraform/internal/depsfile/locks_test.go +++ b/vendor/github.com/hashicorp/terraform/internal/depsfile/locks_test.go @@ -80,3 +80,61 @@ func TestLocksEqual(t *testing.T) { nonEqualBothWays(t, a, b) }) } + +func TestLocksEqualProviderAddress(t *testing.T) { + boopProvider := addrs.NewDefaultProvider("boop") + v2 := getproviders.MustParseVersion("2.0.0") + v2LocalBuild := getproviders.MustParseVersion("2.0.0+awesomecorp.1") + v2GtConstraints := getproviders.MustParseVersionConstraints(">= 2.0.0") + v2EqConstraints := getproviders.MustParseVersionConstraints("2.0.0") + hash1 := getproviders.HashScheme("test").New("1") + hash2 := getproviders.HashScheme("test").New("2") + hash3 := getproviders.HashScheme("test").New("3") + + equalProviderAddressBothWays := func(t *testing.T, a, b *Locks) { + t.Helper() + if !a.EqualProviderAddress(b) { + t.Errorf("a should be equal to b") + } + if !b.EqualProviderAddress(a) { + t.Errorf("b should be equal to a") + } + } + nonEqualProviderAddressBothWays := func(t *testing.T, a, b *Locks) { + t.Helper() + if a.EqualProviderAddress(b) { + t.Errorf("a should be equal to b") + } + if b.EqualProviderAddress(a) { + t.Errorf("b should be equal to a") + } + } + + t.Run("both empty", func(t *testing.T) { + a := NewLocks() + b := NewLocks() + equalProviderAddressBothWays(t, a, b) + }) + t.Run("an extra provider lock", func(t *testing.T) { + a := NewLocks() + b := NewLocks() + b.SetProvider(boopProvider, v2, v2GtConstraints, nil) + nonEqualProviderAddressBothWays(t, a, b) + }) + t.Run("both have boop provider with different versions", func(t *testing.T) { + a := NewLocks() + b := NewLocks() + a.SetProvider(boopProvider, v2, v2EqConstraints, nil) + b.SetProvider(boopProvider, v2LocalBuild, v2EqConstraints, nil) + equalProviderAddressBothWays(t, a, b) + }) + t.Run("both have boop provider with same version but different hashes", func(t *testing.T) { + a := NewLocks() + b := NewLocks() + hashesA := []getproviders.Hash{hash1, hash2} + hashesB := []getproviders.Hash{hash1, hash3} + a.SetProvider(boopProvider, v2, v2EqConstraints, hashesA) + b.SetProvider(boopProvider, v2, v2EqConstraints, hashesB) + equalProviderAddressBothWays(t, a, b) + }) +} diff --git a/vendor/github.com/hashicorp/terraform/internal/earlyconfig/config.go b/vendor/github.com/hashicorp/terraform/internal/earlyconfig/config.go index ff563d4d..2e6d0a90 100644 --- a/vendor/github.com/hashicorp/terraform/internal/earlyconfig/config.go +++ b/vendor/github.com/hashicorp/terraform/internal/earlyconfig/config.go @@ -158,8 +158,8 @@ func (c *Config) ProviderDependencies() (*moduledeps.Module, tfdiags.Diagnostics for name, reqs := range c.Module.RequiredProviders { var fqn addrs.Provider if source := reqs.Source; source != "" { - addr, diags := addrs.ParseProviderSourceString(source) - if diags.HasErrors() { + addr, parseDiags := addrs.ParseProviderSourceString(source) + if parseDiags.HasErrors() { diags = diags.Append(wrapDiagnostic(tfconfig.Diagnostic{ Severity: tfconfig.DiagError, Summary: "Invalid provider source", diff --git a/vendor/github.com/hashicorp/terraform/internal/getproviders/didyoumean.go b/vendor/github.com/hashicorp/terraform/internal/getproviders/didyoumean.go new file mode 100644 index 00000000..b34bc995 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/getproviders/didyoumean.go @@ -0,0 +1,262 @@ +package getproviders + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + "net/url" + "path" + + "github.com/hashicorp/go-retryablehttp" + svchost "github.com/hashicorp/terraform-svchost" + "github.com/hashicorp/terraform/addrs" +) + +// MissingProviderSuggestion takes a provider address that failed installation +// due to the remote registry reporting that it didn't exist, and attempts +// to find another provider that the user might have meant to select. +// +// If the result is equal to the given address then that indicates that there +// is no suggested alternative to offer, either because the function +// successfully determined there is no recorded alternative or because the +// lookup failed somehow. We don't consider a failure to find a suggestion +// as an installation failure, because the caller should already be reporting +// that the provider didn't exist anyway and this is only extra context for +// that error message. +// +// The result of this is a best effort, so any UI presenting it should be +// careful to give it only as a possibility and not necessarily a suitable +// replacement for the given provider. +// +// In practice today this function only knows how to suggest alternatives for +// "default" providers, which is to say ones that are in the hashicorp +// namespace in the Terraform registry. It will always return no result for +// any other provider. That might change in future if we introduce other ways +// to discover provider suggestions. +// +// If the given context is cancelled then this function might not return a +// renaming suggestion even if one would've been available for a completed +// request. +func MissingProviderSuggestion(ctx context.Context, addr addrs.Provider, source Source, reqs Requirements) addrs.Provider { + if !addr.IsDefault() { + return addr + } + + // Before possibly looking up legacy naming, see if the user has another provider + // named in their requirements that is of the same type, and offer that + // as a suggestion + for req := range reqs { + if req != addr && req.Type == addr.Type { + return req + } + } + + // Our strategy here, for a default provider, is to use the default + // registry's special API for looking up "legacy" providers and try looking + // for a legacy provider whose type name matches the type of the given + // provider. This should then find a suitable answer for any provider + // that was originally auto-installable in v0.12 and earlier but moved + // into a non-default namespace as part of introducing the hierarchical + // provider namespace. + // + // To achieve that, we need to find the direct registry client in + // particular from the given source, because that is the only Source + // implementation that can actually handle a legacy provider lookup. + regSource := findLegacyProviderLookupSource(addr.Hostname, source) + if regSource == nil { + // If there's no direct registry source in the installation config + // then we can't provide a renaming suggestion. + return addr + } + + defaultNS, redirectNS, err := regSource.lookupLegacyProviderNamespace(ctx, addr.Hostname, addr.Type) + if err != nil { + return addr + } + + switch { + case redirectNS != "": + return addrs.Provider{ + Hostname: addr.Hostname, + Namespace: redirectNS, + Type: addr.Type, + } + default: + return addrs.Provider{ + Hostname: addr.Hostname, + Namespace: defaultNS, + Type: addr.Type, + } + } +} + +// findLegacyProviderLookupSource tries to find a *RegistrySource that can talk +// to the given registry host in the given Source. It might be given directly, +// or it might be given indirectly via a MultiSource where the selector +// includes a wildcard for registry.terraform.io. +// +// Returns nil if the given source does not have any configured way to talk +// directly to the given host. +// +// If the given source contains multiple sources that can talk to the given +// host directly, the first one in the sequence takes preference. In practice +// it's pointless to have two direct installation sources that match the same +// hostname anyway, so this shouldn't arise in normal use. +func findLegacyProviderLookupSource(host svchost.Hostname, source Source) *RegistrySource { + switch source := source.(type) { + + case *RegistrySource: + // Easy case: the source is a registry source directly, and so we'll + // just use it. + return source + + case *MemoizeSource: + // Also easy: the source is a memoize wrapper, so defer to its + // underlying source. + return findLegacyProviderLookupSource(host, source.underlying) + + case MultiSource: + // Trickier case: if it's a multisource then we need to scan over + // its selectors until we find one that is a *RegistrySource _and_ + // that is configured to accept arbitrary providers from the + // given hostname. + + // For our matching purposes we'll use an address that would not be + // valid as a real provider FQN and thus can only match a selector + // that has no filters at all or a selector that wildcards everything + // except the hostname, like "registry.terraform.io/*/*" + matchAddr := addrs.Provider{ + Hostname: host, + // Other fields are intentionally left empty, to make this invalid + // as a specific provider address. + } + + for _, selector := range source { + // If this source has suitable matching patterns to install from + // the given hostname then we'll recursively search inside it + // for *RegistrySource objects. + if selector.CanHandleProvider(matchAddr) { + ret := findLegacyProviderLookupSource(host, selector.Source) + if ret != nil { + return ret + } + } + } + + // If we get here then there were no selectors that are both configured + // to handle modules from the given hostname and that are registry + // sources, so we fail. + return nil + + default: + // This source cannot be and cannot contain a *RegistrySource, so + // we fail. + return nil + } +} + +// lookupLegacyProviderNamespace is a special method available only on +// RegistrySource which can deal with legacy provider addresses that contain +// only a type and leave the namespace implied. +// +// It asks the registry at the given hostname to provide a default namespace +// for the given provider type, which can be combined with the given hostname +// and type name to produce a fully-qualified provider address. +// +// Not all unqualified type names can be resolved to a default namespace. If +// the request fails, this method returns an error describing the failure. +// +// This method exists only to allow compatibility with unqualified names +// in older configurations. New configurations should be written so as not to +// depend on it, and this fallback mechanism will likely be removed altogether +// in a future Terraform version. +func (s *RegistrySource) lookupLegacyProviderNamespace(ctx context.Context, hostname svchost.Hostname, typeName string) (string, string, error) { + client, err := s.registryClient(hostname) + if err != nil { + return "", "", err + } + return client.legacyProviderDefaultNamespace(ctx, typeName) +} + +// legacyProviderDefaultNamespace returns the raw address strings produced by +// the registry when asked about the given unqualified provider type name. +// The returned namespace string is taken verbatim from the registry's response. +// +// This method exists only to allow compatibility with unqualified names +// in older configurations. New configurations should be written so as not to +// depend on it. +func (c *registryClient) legacyProviderDefaultNamespace(ctx context.Context, typeName string) (string, string, error) { + endpointPath, err := url.Parse(path.Join("-", typeName, "versions")) + if err != nil { + // Should never happen because we're constructing this from + // already-validated components. + return "", "", err + } + endpointURL := c.baseURL.ResolveReference(endpointPath) + + req, err := retryablehttp.NewRequest("GET", endpointURL.String(), nil) + if err != nil { + return "", "", err + } + req = req.WithContext(ctx) + c.addHeadersToRequest(req.Request) + + // This is just to give us something to return in error messages. It's + // not a proper provider address. + placeholderProviderAddr := addrs.NewLegacyProvider(typeName) + + resp, err := c.httpClient.Do(req) + if err != nil { + return "", "", c.errQueryFailed(placeholderProviderAddr, err) + } + defer resp.Body.Close() + + switch resp.StatusCode { + case http.StatusOK: + // Great! + case http.StatusNotFound: + return "", "", ErrProviderNotFound{ + Provider: placeholderProviderAddr, + } + case http.StatusUnauthorized, http.StatusForbidden: + return "", "", c.errUnauthorized(placeholderProviderAddr.Hostname) + default: + return "", "", c.errQueryFailed(placeholderProviderAddr, errors.New(resp.Status)) + } + + type ResponseBody struct { + Id string `json:"id"` + MovedTo string `json:"moved_to"` + } + var body ResponseBody + + dec := json.NewDecoder(resp.Body) + if err := dec.Decode(&body); err != nil { + return "", "", c.errQueryFailed(placeholderProviderAddr, err) + } + + provider, diags := addrs.ParseProviderSourceString(body.Id) + if diags.HasErrors() { + return "", "", fmt.Errorf("Error parsing provider ID from Registry: %s", diags.Err()) + } + + if provider.Type != typeName { + return "", "", fmt.Errorf("Registry returned provider with type %q, expected %q", provider.Type, typeName) + } + + var movedTo addrs.Provider + if body.MovedTo != "" { + movedTo, diags = addrs.ParseProviderSourceString(body.MovedTo) + if diags.HasErrors() { + return "", "", fmt.Errorf("Error parsing provider ID from Registry: %s", diags.Err()) + } + + if movedTo.Type != typeName { + return "", "", fmt.Errorf("Registry returned provider with type %q, expected %q", movedTo.Type, typeName) + } + } + + return provider.Namespace, movedTo.Namespace, nil +} diff --git a/vendor/github.com/hashicorp/terraform/internal/getproviders/didyoumean_test.go b/vendor/github.com/hashicorp/terraform/internal/getproviders/didyoumean_test.go new file mode 100644 index 00000000..85bc4582 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/getproviders/didyoumean_test.go @@ -0,0 +1,195 @@ +package getproviders + +import ( + "context" + "testing" + + svchost "github.com/hashicorp/terraform-svchost" + "github.com/hashicorp/terraform/addrs" +) + +func TestMissingProviderSuggestion(t *testing.T) { + // Most of these test cases rely on specific "magic" provider addresses + // that are implemented by the fake registry source returned by + // testRegistrySource. Refer to that function for more details on how + // they work. + + t.Run("happy path", func(t *testing.T) { + ctx := context.Background() + source, _, close := testRegistrySource(t) + defer close() + + // testRegistrySource handles -/legacy as a valid legacy provider + // lookup mapping to legacycorp/legacy. + legacyAddr := addrs.NewDefaultProvider("legacy") + got := MissingProviderSuggestion( + ctx, + addrs.NewDefaultProvider("legacy"), + source, + Requirements{ + legacyAddr: MustParseVersionConstraints(">= 1.0.0"), + }, + ) + + want := addrs.Provider{ + Hostname: defaultRegistryHost, + Namespace: "legacycorp", + Type: "legacy", + } + if got != want { + t.Errorf("wrong result\ngot: %s\nwant: %s", got, want) + } + }) + t.Run("provider moved", func(t *testing.T) { + ctx := context.Background() + source, _, close := testRegistrySource(t) + defer close() + + // testRegistrySource handles -/moved as a valid legacy provider + // lookup mapping to hashicorp/moved but with an additional "redirect" + // to acme/moved. This mimics how for some providers there is both + // a copy under terraform-providers for v0.12 compatibility _and_ a + // copy in some other namespace for v0.13 or later to use. Our naming + // suggestions ignore the v0.12-compatible one and suggest the + // other one. + moved := addrs.NewDefaultProvider("moved") + want := addrs.Provider{ + Hostname: defaultRegistryHost, + Namespace: "acme", + Type: "moved", + } + + got := MissingProviderSuggestion( + ctx, + moved, + source, + Requirements{ + moved: MustParseVersionConstraints(">= 1.0.0"), + }, + ) + + if got != want { + t.Errorf("wrong result\ngot: %s\nwant: %s", got, want) + } + + // If a provider has moved, but there's provider requirements + // for something of the same type, we'll return that one + // and skip the legacy lookup process. In practice, + // hopefully this is also "acme" but it's "zcme" here to + // exercise the codepath + want2 := addrs.Provider{ + Hostname: defaultRegistryHost, + Namespace: "zcme", + Type: "moved", + } + got2 := MissingProviderSuggestion( + ctx, + moved, + source, + Requirements{ + moved: MustParseVersionConstraints(">= 1.0.0"), + want2: MustParseVersionConstraints(">= 1.0.0"), + }, + ) + + if got2 != want2 { + t.Errorf("wrong result\ngot: %s\nwant: %s", got2, want2) + } + }) + t.Run("invalid response", func(t *testing.T) { + ctx := context.Background() + source, _, close := testRegistrySource(t) + defer close() + + // testRegistrySource handles -/invalid by returning an invalid + // provider address, which MissingProviderSuggestion should reject + // and behave as if there was no suggestion available. + want := addrs.NewDefaultProvider("invalid") + got := MissingProviderSuggestion( + ctx, + want, + source, + Requirements{ + want: MustParseVersionConstraints(">= 1.0.0"), + }, + ) + if got != want { + t.Errorf("wrong result\ngot: %s\nwant: %s", got, want) + } + }) + t.Run("another registry", func(t *testing.T) { + ctx := context.Background() + source, _, close := testRegistrySource(t) + defer close() + + // Because this provider address isn't on registry.terraform.io, + // MissingProviderSuggestion won't even attempt to make a suggestion + // for it. + want := addrs.Provider{ + Hostname: svchost.Hostname("example.com"), + Namespace: "whatever", + Type: "foo", + } + got := MissingProviderSuggestion( + ctx, + want, + source, + Requirements{ + want: MustParseVersionConstraints(">= 1.0.0"), + }, + ) + if got != want { + t.Errorf("wrong result\ngot: %s\nwant: %s", got, want) + } + }) + t.Run("another namespace", func(t *testing.T) { + ctx := context.Background() + source, _, close := testRegistrySource(t) + defer close() + + // Because this provider address isn't in + // registry.terraform.io/hashicorp/..., MissingProviderSuggestion + // will provide the same addr since there's no alternative in Requirements + want := addrs.Provider{ + Hostname: defaultRegistryHost, + Namespace: "whatever", + Type: "foo", + } + got := MissingProviderSuggestion( + ctx, + want, + source, + Requirements{ + want: MustParseVersionConstraints(">= 1.0.0"), + }, + ) + if got != want { + t.Errorf("wrong result\ngot: %s\nwant: %s", got, want) + } + + // If there is a provider required that has the same type, + // but different namespace, we can suggest that + foo := addrs.Provider{ + Hostname: defaultRegistryHost, + Namespace: "hashicorp", + Type: "foo", + } + realFoo := addrs.Provider{ + Hostname: defaultRegistryHost, + Namespace: "acme", + Type: "foo", + } + got2 := MissingProviderSuggestion( + ctx, + foo, + source, + Requirements{ + foo: MustParseVersionConstraints(">= 1.0.0"), + realFoo: MustParseVersionConstraints(">= 1.0.0"), + }, + ) + if got2 != realFoo { + t.Errorf("wrong result\ngot: %s\nwant: %s", got2, realFoo) + } + }) +} diff --git a/vendor/github.com/hashicorp/terraform/internal/getproviders/filesystem_search.go b/vendor/github.com/hashicorp/terraform/internal/getproviders/filesystem_search.go index 9c0e7e8f..f4b694cd 100644 --- a/vendor/github.com/hashicorp/terraform/internal/getproviders/filesystem_search.go +++ b/vendor/github.com/hashicorp/terraform/internal/getproviders/filesystem_search.go @@ -23,7 +23,7 @@ func SearchLocalDirectory(baseDir string) (map[addrs.Provider]PackageMetaList, e ret := make(map[addrs.Provider]PackageMetaList) // We don't support symlinks at intermediate points inside the directory - // heirarchy because that could potentially cause our walk to get into + // hierarchy because that could potentially cause our walk to get into // an infinite loop, but as a measure of pragmatism we'll allow the // top-level location itself to be a symlink, so that a user can // potentially keep their plugins in a non-standard location but use a @@ -69,7 +69,7 @@ func SearchLocalDirectory(baseDir string) (map[addrs.Provider]PackageMetaList, e if (info.Mode() & os.ModeSymlink) != 0 { // We don't allow symlinks for intermediate steps in the - // heirarchy because otherwise this walk would risk getting + // hierarchy because otherwise this walk would risk getting // itself into an infinite loop, but if we do find one then // we'll warn about it to help with debugging. log.Printf("[WARN] Provider plugin search ignored symlink %s: only the base directory %s may be a symlink", fullPath, originalBaseDir) @@ -120,7 +120,8 @@ func SearchLocalDirectory(baseDir string) (map[addrs.Provider]PackageMetaList, e // filesystem object below. info, err = os.Stat(fullPath) if err != nil { - return fmt.Errorf("failed to read metadata about %s: %s", fullPath, err) + log.Printf("[WARN] failed to read metadata about %s: %s", fullPath, err) + return nil } switch len(parts) { diff --git a/vendor/github.com/hashicorp/terraform/internal/getproviders/package_authentication_test.go b/vendor/github.com/hashicorp/terraform/internal/getproviders/package_authentication_test.go index 62d7bbdd..06b7621e 100644 --- a/vendor/github.com/hashicorp/terraform/internal/getproviders/package_authentication_test.go +++ b/vendor/github.com/hashicorp/terraform/internal/getproviders/package_authentication_test.go @@ -325,18 +325,6 @@ func TestSignatureAuthentication_success(t *testing.T) { keys []SigningKey result PackageAuthenticationResult }{ - "official provider": { - testHashicorpSignatureGoodBase64, - []SigningKey{ - { - ASCIIArmor: HashicorpPublicKey, - }, - }, - PackageAuthenticationResult{ - result: officialProvider, - KeyID: testHashiCorpPublicKeyID, - }, - }, "partner provider": { testAuthorSignatureGoodBase64, []SigningKey{ @@ -402,6 +390,49 @@ func TestSignatureAuthentication_success(t *testing.T) { } } +func TestNewSignatureAuthentication_success(t *testing.T) { + tests := map[string]struct { + signature string + keys []SigningKey + result PackageAuthenticationResult + }{ + "official provider": { + testHashicorpSignatureGoodBase64, + []SigningKey{ + { + ASCIIArmor: HashicorpPublicKey, + }, + }, + PackageAuthenticationResult{ + result: officialProvider, + KeyID: testHashiCorpPublicKeyID, + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + // Location is unused + location := PackageLocalArchive("testdata/my-package.zip") + + signature, err := base64.StdEncoding.DecodeString(test.signature) + if err != nil { + t.Fatal(err) + } + + auth := NewSignatureAuthentication([]byte(testProviderShaSums), signature, test.keys) + result, err := auth.AuthenticatePackage(location) + + if result == nil || *result != test.result { + t.Errorf("wrong result: got %#v, want %#v", result, test.result) + } + if err != nil { + t.Errorf("wrong err: got %s, want nil", err) + } + }) + } +} + // Signature authentication can fail for many reasons, most of which are due // to OpenPGP failures from malformed keys or signatures. func TestSignatureAuthentication_failure(t *testing.T) { @@ -621,18 +652,35 @@ const testSignatureBadBase64 = `iQEzBAABCAAdFiEEW/7sQxfnRgCGIZcGN6arO88s` + `n1ayZdaCIw/r4w==` // testHashiCorpPublicKeyID is the Key ID of the HashiCorpPublicKey. -const testHashiCorpPublicKeyID = `51852D87348FFC4C` - -// testHashicorpSignatureGoodBase64 is a signature of testShaSums signed with +const testHashiCorpPublicKeyID = `34365D9472D7468F` + +const testProviderShaSums = `fea4227271ebf7d9e2b61b89ce2328c7262acd9fd190e1fd6d15a591abfa848e terraform-provider-null_3.1.0_darwin_amd64.zip +9ebf4d9704faba06b3ec7242c773c0fbfe12d62db7d00356d4f55385fc69bfb2 terraform-provider-null_3.1.0_darwin_arm64.zip +a6576c81adc70326e4e1c999c04ad9ca37113a6e925aefab4765e5a5198efa7e terraform-provider-null_3.1.0_freebsd_386.zip +5f9200bf708913621d0f6514179d89700e9aa3097c77dac730e8ba6e5901d521 terraform-provider-null_3.1.0_freebsd_amd64.zip +fc39cc1fe71234a0b0369d5c5c7f876c71b956d23d7d6f518289737a001ba69b terraform-provider-null_3.1.0_freebsd_arm.zip +c797744d08a5307d50210e0454f91ca4d1c7621c68740441cf4579390452321d terraform-provider-null_3.1.0_linux_386.zip +53e30545ff8926a8e30ad30648991ca8b93b6fa496272cd23b26763c8ee84515 terraform-provider-null_3.1.0_linux_amd64.zip +cecb6a304046df34c11229f20a80b24b1603960b794d68361a67c5efe58e62b8 terraform-provider-null_3.1.0_linux_arm64.zip +e1371aa1e502000d9974cfaff5be4cfa02f47b17400005a16f14d2ef30dc2a70 terraform-provider-null_3.1.0_linux_arm.zip +a8a42d13346347aff6c63a37cda9b2c6aa5cc384a55b2fe6d6adfa390e609c53 terraform-provider-null_3.1.0_windows_386.zip +02a1675fd8de126a00460942aaae242e65ca3380b5bb192e8773ef3da9073fd2 terraform-provider-null_3.1.0_windows_amd64.zip +` + +// testHashicorpSignatureGoodBase64 is a signature of testProviderShaSums signed with // HashicorpPublicKey, which represents the SHA256SUMS.sig file downloaded for // an official release. -const testHashicorpSignatureGoodBase64 = `iQFLBAABCAA1FiEEkabn+F0FxlYwvvGJUYUth` + - `zSP/EwFAl5w784XHHNlY3VyaXR5QGhhc2hpY29ycC5jb20ACgkQUYUthzSP/EyB8QgAv9ijp` + - `kTcoFwDAs+1iEUrcW18h/2cU+bvFtdqNDiffzk7+YJ9ioxeWisPta/Z6hEyhdss2+5L1MNbo` + - `oUBLABI+Aebfxa/uYFT2kX6r/eySmlY9kqNVpjXdemOQutS4NNZxdJL7CEbh2qIKCVuyo0ul` + - `YrTdDH35vwVyLXImWiZLnrXcT/fXLpQGx/N8PDy6WmCeju5Y5RD7TuntB71eCaCZi7wFe1tR` + - `qSoe9tD9A7ONB0rGuCY7BxqUj0S81hhz960YbNR9Q81WoNvF7b5SmcLJ1qJx1yvBLyqya6Su` + - `DKjU/YYCh7bwHIYzpk1/nK/7SaTHpisekqojVsfDth4TA+jGA==` +const testHashicorpSignatureGoodBase64 = `wsFcBAABCAAQBQJgga+GCRCwtEEJdoW2dgAA` + + `o0YQAAW911BGDr2WHLo5NwcZenwHyxL5DX9g+4BknKbc/WxRC1hD8Afi3eygZk1yR6eT4Gp2H` + + `yNOwCjGL1PTONBumMfj9udIeuX8onrJMMvjFHh+bORGxBi4FKr4V3b2ZV1IYOjWMEyyTGRDvw` + + `SCdxBkp3apH3s2xZLmRoAj84JZ4KaxGF7hlT0j4IkNyQKd2T5cCByN9DV80+x+HtzaOieFwJL` + + `97iyGj6aznXfKfslK6S4oIrVTwyLTrQbxSxA0LsdUjRPHnJamL3sFOG77qUEUoXG3r61yi5vW` + + `V4P5gCH/+C+VkfGHqaB1s0jHYLxoTEXtwthe66MydDBPe2Hd0J12u9ppOIeK3leeb4uiixWIi` + + `rNdpWyjr/LU1KKWPxsDqMGYJ9TexyWkXjEpYmIEiY1Rxar8jrLh+FqVAhxRJajjgSRu5pZj50` + + `CNeKmmbyolLhPCmICjYYU/xKPGXSyDFqonVVyMWCSpO+8F38OmwDQHIk5AWyc8hPOAZ+g5N95` + + `cfUAzEqlvmNvVHQIU40Y6/Ip2HZzzFCLKQkMP1aDakYHq5w4ZO/ucjhKuoh1HDQMuMnZSu4eo` + + `2nMTBzYZnUxwtROrJZF1t103avbmP2QE/GaPvLIQn7o5WMV3ZcPCJ+szzzby7H2e33WIynrY/` + + `95ensBxh7mGFbcQ1C59b5o7viwIaaY2` // entityString function is used for logging the signing key. func TestEntityString(t *testing.T) { @@ -654,7 +702,7 @@ func TestEntityString(t *testing.T) { { "HashicorpPublicKey", testReadArmoredEntity(t, HashicorpPublicKey), - "51852D87348FFC4C HashiCorp Security ", + "34365D9472D7468F HashiCorp Security (hashicorp.com/security) ", }, { "HashicorpPartnersKey", diff --git a/vendor/github.com/hashicorp/terraform/internal/getproviders/public_keys.go b/vendor/github.com/hashicorp/terraform/internal/getproviders/public_keys.go index bbbcdc80..74265645 100644 --- a/vendor/github.com/hashicorp/terraform/internal/getproviders/public_keys.go +++ b/vendor/github.com/hashicorp/terraform/internal/getproviders/public_keys.go @@ -3,34 +3,126 @@ package getproviders // HashicorpPublicKey is the HashiCorp public key, also available at // https://www.hashicorp.com/security const HashicorpPublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v1 -mQENBFMORM0BCADBRyKO1MhCirazOSVwcfTr1xUxjPvfxD3hjUwHtjsOy/bT6p9f -W2mRPfwnq2JB5As+paL3UGDsSRDnK9KAxQb0NNF4+eVhr/EJ18s3wwXXDMjpIifq -fIm2WyH3G+aRLTLPIpscUNKDyxFOUbsmgXAmJ46Re1fn8uKxKRHbfa39aeuEYWFA -3drdL1WoUngvED7f+RnKBK2G6ZEpO+LDovQk19xGjiMTtPJrjMjZJ3QXqPvx5wca -KSZLr4lMTuoTI/ZXyZy5bD4tShiZz6KcyX27cD70q2iRcEZ0poLKHyEIDAi3TM5k -SwbbWBFd5RNPOR0qzrb/0p9ksKK48IIfH2FvABEBAAG0K0hhc2hpQ29ycCBTZWN1 -cml0eSA8c2VjdXJpdHlAaGFzaGljb3JwLmNvbT6JATgEEwECACIFAlMORM0CGwMG -CwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEFGFLYc0j/xMyWIIAIPhcVqiQ59n -Jc07gjUX0SWBJAxEG1lKxfzS4Xp+57h2xxTpdotGQ1fZwsihaIqow337YHQI3q0i -SqV534Ms+j/tU7X8sq11xFJIeEVG8PASRCwmryUwghFKPlHETQ8jJ+Y8+1asRydi -psP3B/5Mjhqv/uOK+Vy3zAyIpyDOMtIpOVfjSpCplVRdtSTFWBu9Em7j5I2HMn1w -sJZnJgXKpybpibGiiTtmnFLOwibmprSu04rsnP4ncdC2XRD4wIjoyA+4PKgX3sCO -klEzKryWYBmLkJOMDdo52LttP3279s7XrkLEE7ia0fXa2c12EQ0f0DQ1tGUvyVEW -WmJVccm5bq25AQ0EUw5EzQEIANaPUY04/g7AmYkOMjaCZ6iTp9hB5Rsj/4ee/ln9 -wArzRO9+3eejLWh53FoN1rO+su7tiXJA5YAzVy6tuolrqjM8DBztPxdLBbEi4V+j -2tK0dATdBQBHEh3OJApO2UBtcjaZBT31zrG9K55D+CrcgIVEHAKY8Cb4kLBkb5wM -skn+DrASKU0BNIV1qRsxfiUdQHZfSqtp004nrql1lbFMLFEuiY8FZrkkQ9qduixo -mTT6f34/oiY+Jam3zCK7RDN/OjuWheIPGj/Qbx9JuNiwgX6yRj7OE1tjUx6d8g9y -0H1fmLJbb3WZZbuuGFnK6qrE3bGeY8+AWaJAZ37wpWh1p0cAEQEAAYkBHwQYAQIA -CQUCUw5EzQIbDAAKCRBRhS2HNI/8TJntCAClU7TOO/X053eKF1jqNW4A1qpxctVc -z8eTcY8Om5O4f6a/rfxfNFKn9Qyja/OG1xWNobETy7MiMXYjaa8uUx5iFy6kMVaP -0BXJ59NLZjMARGw6lVTYDTIvzqqqwLxgliSDfSnqUhubGwvykANPO+93BBx89MRG -unNoYGXtPlhNFrAsB1VR8+EyKLv2HQtGCPSFBhrjuzH3gxGibNDDdFQLxxuJWepJ -EK1UbTS4ms0NgZ2Uknqn1WRU1Ki7rE4sTy68iZtWpKQXZEJa0IGnuI2sSINGcXCJ -oEIgXTMyCILo34Fa/C6VCm2WBgz9zZO8/rHIiQm1J5zqz0DrDwKBUM9C -=LYpS +mQINBGB9+xkBEACabYZOWKmgZsHTdRDiyPJxhbuUiKX65GUWkyRMJKi/1dviVxOX +PG6hBPtF48IFnVgxKpIb7G6NjBousAV+CuLlv5yqFKpOZEGC6sBV+Gx8Vu1CICpl +Zm+HpQPcIzwBpN+Ar4l/exCG/f/MZq/oxGgH+TyRF3XcYDjG8dbJCpHO5nQ5Cy9h +QIp3/Bh09kET6lk+4QlofNgHKVT2epV8iK1cXlbQe2tZtfCUtxk+pxvU0UHXp+AB +0xc3/gIhjZp/dePmCOyQyGPJbp5bpO4UeAJ6frqhexmNlaw9Z897ltZmRLGq1p4a +RnWL8FPkBz9SCSKXS8uNyV5oMNVn4G1obCkc106iWuKBTibffYQzq5TG8FYVJKrh +RwWB6piacEB8hl20IIWSxIM3J9tT7CPSnk5RYYCTRHgA5OOrqZhC7JefudrP8n+M +pxkDgNORDu7GCfAuisrf7dXYjLsxG4tu22DBJJC0c/IpRpXDnOuJN1Q5e/3VUKKW +mypNumuQpP5lc1ZFG64TRzb1HR6oIdHfbrVQfdiQXpvdcFx+Fl57WuUraXRV6qfb +4ZmKHX1JEwM/7tu21QE4F1dz0jroLSricZxfaCTHHWNfvGJoZ30/MZUrpSC0IfB3 +iQutxbZrwIlTBt+fGLtm3vDtwMFNWM+Rb1lrOxEQd2eijdxhvBOHtlIcswARAQAB +tERIYXNoaUNvcnAgU2VjdXJpdHkgKGhhc2hpY29ycC5jb20vc2VjdXJpdHkpIDxz +ZWN1cml0eUBoYXNoaWNvcnAuY29tPokCVAQTAQoAPhYhBMh0AR8KtAURDQIQVTQ2 +XZRy10aPBQJgffsZAhsDBQkJZgGABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJ +EDQ2XZRy10aPtpcP/0PhJKiHtC1zREpRTrjGizoyk4Sl2SXpBZYhkdrG++abo6zs +buaAG7kgWWChVXBo5E20L7dbstFK7OjVs7vAg/OLgO9dPD8n2M19rpqSbbvKYWvp +0NSgvFTT7lbyDhtPj0/bzpkZEhmvQaDWGBsbDdb2dBHGitCXhGMpdP0BuuPWEix+ +QnUMaPwU51q9GM2guL45Tgks9EKNnpDR6ZdCeWcqo1IDmklloidxT8aKL21UOb8t +cD+Bg8iPaAr73bW7Jh8TdcV6s6DBFub+xPJEB/0bVPmq3ZHs5B4NItroZ3r+h3ke +VDoSOSIZLl6JtVooOJ2la9ZuMqxchO3mrXLlXxVCo6cGcSuOmOdQSz4OhQE5zBxx +LuzA5ASIjASSeNZaRnffLIHmht17BPslgNPtm6ufyOk02P5XXwa69UCjA3RYrA2P +QNNC+OWZ8qQLnzGldqE4MnRNAxRxV6cFNzv14ooKf7+k686LdZrP/3fQu2p3k5rY +0xQUXKh1uwMUMtGR867ZBYaxYvwqDrg9XB7xi3N6aNyNQ+r7zI2lt65lzwG1v9hg +FG2AHrDlBkQi/t3wiTS3JOo/GCT8BjN0nJh0lGaRFtQv2cXOQGVRW8+V/9IpqEJ1 +qQreftdBFWxvH7VJq2mSOXUJyRsoUrjkUuIivaA9Ocdipk2CkP8bpuGz7ZF4uQIN +BGB9+xkBEACoklYsfvWRCjOwS8TOKBTfl8myuP9V9uBNbyHufzNETbhYeT33Cj0M +GCNd9GdoaknzBQLbQVSQogA+spqVvQPz1MND18GIdtmr0BXENiZE7SRvu76jNqLp +KxYALoK2Pc3yK0JGD30HcIIgx+lOofrVPA2dfVPTj1wXvm0rbSGA4Wd4Ng3d2AoR +G/wZDAQ7sdZi1A9hhfugTFZwfqR3XAYCk+PUeoFrkJ0O7wngaon+6x2GJVedVPOs +2x/XOR4l9ytFP3o+5ILhVnsK+ESVD9AQz2fhDEU6RhvzaqtHe+sQccR3oVLoGcat +ma5rbfzH0Fhj0JtkbP7WreQf9udYgXxVJKXLQFQgel34egEGG+NlbGSPG+qHOZtY +4uWdlDSvmo+1P95P4VG/EBteqyBbDDGDGiMs6lAMg2cULrwOsbxWjsWka8y2IN3z +1stlIJFvW2kggU+bKnQ+sNQnclq3wzCJjeDBfucR3a5WRojDtGoJP6Fc3luUtS7V +5TAdOx4dhaMFU9+01OoH8ZdTRiHZ1K7RFeAIslSyd4iA/xkhOhHq89F4ECQf3Bt4 +ZhGsXDTaA/VgHmf3AULbrC94O7HNqOvTWzwGiWHLfcxXQsr+ijIEQvh6rHKmJK8R +9NMHqc3L18eMO6bqrzEHW0Xoiu9W8Yj+WuB3IKdhclT3w0pO4Pj8gQARAQABiQI8 +BBgBCgAmFiEEyHQBHwq0BRENAhBVNDZdlHLXRo8FAmB9+xkCGwwFCQlmAYAACgkQ +NDZdlHLXRo9ZnA/7BmdpQLeTjEiXEJyW46efxlV1f6THn9U50GWcE9tebxCXgmQf +u+Uju4hreltx6GDi/zbVVV3HCa0yaJ4JVvA4LBULJVe3ym6tXXSYaOfMdkiK6P1v +JgfpBQ/b/mWB0yuWTUtWx18BQQwlNEQWcGe8n1lBbYsH9g7QkacRNb8tKUrUbWlQ +QsU8wuFgly22m+Va1nO2N5C/eE/ZEHyN15jEQ+QwgQgPrK2wThcOMyNMQX/VNEr1 +Y3bI2wHfZFjotmek3d7ZfP2VjyDudnmCPQ5xjezWpKbN1kvjO3as2yhcVKfnvQI5 +P5Frj19NgMIGAp7X6pF5Csr4FX/Vw316+AFJd9Ibhfud79HAylvFydpcYbvZpScl +7zgtgaXMCVtthe3GsG4gO7IdxxEBZ/Fm4NLnmbzCIWOsPMx/FxH06a539xFq/1E2 +1nYFjiKg8a5JFmYU/4mV9MQs4bP/3ip9byi10V+fEIfp5cEEmfNeVeW5E7J8PqG9 +t4rLJ8FR4yJgQUa2gs2SNYsjWQuwS/MJvAv4fDKlkQjQmYRAOp1SszAnyaplvri4 +ncmfDsf0r65/sd6S40g5lHH8LIbGxcOIN6kwthSTPWX89r42CbY8GzjTkaeejNKx +v1aCrO58wAtursO1DiXCvBY7+NdafMRnoHwBk50iPqrVkNA8fv+auRyB2/G5Ag0E +YH3+JQEQALivllTjMolxUW2OxrXb+a2Pt6vjCBsiJzrUj0Pa63U+lT9jldbCCfgP +wDpcDuO1O05Q8k1MoYZ6HddjWnqKG7S3eqkV5c3ct3amAXp513QDKZUfIDylOmhU +qvxjEgvGjdRjz6kECFGYr6Vnj/p6AwWv4/FBRFlrq7cnQgPynbIH4hrWvewp3Tqw +GVgqm5RRofuAugi8iZQVlAiQZJo88yaztAQ/7VsXBiHTn61ugQ8bKdAsr8w/ZZU5 +HScHLqRolcYg0cKN91c0EbJq9k1LUC//CakPB9mhi5+aUVUGusIM8ECShUEgSTCi +KQiJUPZ2CFbbPE9L5o9xoPCxjXoX+r7L/WyoCPTeoS3YRUMEnWKvc42Yxz3meRb+ +BmaqgbheNmzOah5nMwPupJYmHrjWPkX7oyyHxLSFw4dtoP2j6Z7GdRXKa2dUYdk2 +x3JYKocrDoPHh3Q0TAZujtpdjFi1BS8pbxYFb3hHmGSdvz7T7KcqP7ChC7k2RAKO +GiG7QQe4NX3sSMgweYpl4OwvQOn73t5CVWYp/gIBNZGsU3Pto8g27vHeWyH9mKr4 +cSepDhw+/X8FGRNdxNfpLKm7Vc0Sm9Sof8TRFrBTqX+vIQupYHRi5QQCuYaV6OVr +ITeegNK3So4m39d6ajCR9QxRbmjnx9UcnSYYDmIB6fpBuwT0ogNtABEBAAGJBHIE +GAEKACYCGwIWIQTIdAEfCrQFEQ0CEFU0Nl2UctdGjwUCYH4bgAUJAeFQ2wJAwXQg +BBkBCgAdFiEEs2y6kaLAcwxDX8KAsLRBCXaFtnYFAmB9/iUACgkQsLRBCXaFtnYX +BhAAlxejyFXoQwyGo9U+2g9N6LUb/tNtH29RHYxy4A3/ZUY7d/FMkArmh4+dfjf0 +p9MJz98Zkps20kaYP+2YzYmaizO6OA6RIddcEXQDRCPHmLts3097mJ/skx9qLAf6 +rh9J7jWeSqWO6VW6Mlx8j9m7sm3Ae1OsjOx/m7lGZOhY4UYfY627+Jf7WQ5103Qs +lgQ09es/vhTCx0g34SYEmMW15Tc3eCjQ21b1MeJD/V26npeakV8iCZ1kHZHawPq/ +aCCuYEcCeQOOteTWvl7HXaHMhHIx7jjOd8XX9V+UxsGz2WCIxX/j7EEEc7CAxwAN +nWp9jXeLfxYfjrUB7XQZsGCd4EHHzUyCf7iRJL7OJ3tz5Z+rOlNjSgci+ycHEccL +YeFAEV+Fz+sj7q4cFAferkr7imY1XEI0Ji5P8p/uRYw/n8uUf7LrLw5TzHmZsTSC +UaiL4llRzkDC6cVhYfqQWUXDd/r385OkE4oalNNE+n+txNRx92rpvXWZ5qFYfv7E +95fltvpXc0iOugPMzyof3lwo3Xi4WZKc1CC/jEviKTQhfn3WZukuF5lbz3V1PQfI +xFsYe9WYQmp25XGgezjXzp89C/OIcYsVB1KJAKihgbYdHyUN4fRCmOszmOUwEAKR +3k5j4X8V5bk08sA69NVXPn2ofxyk3YYOMYWW8ouObnXoS8QJEDQ2XZRy10aPMpsQ +AIbwX21erVqUDMPn1uONP6o4NBEq4MwG7d+fT85rc1U0RfeKBwjucAE/iStZDQoM +ZKWvGhFR+uoyg1LrXNKuSPB82unh2bpvj4zEnJsJadiwtShTKDsikhrfFEK3aCK8 +Zuhpiu3jxMFDhpFzlxsSwaCcGJqcdwGhWUx0ZAVD2X71UCFoOXPjF9fNnpy80YNp +flPjj2RnOZbJyBIM0sWIVMd8F44qkTASf8K5Qb47WFN5tSpePq7OCm7s8u+lYZGK +wR18K7VliundR+5a8XAOyUXOL5UsDaQCK4Lj4lRaeFXunXl3DJ4E+7BKzZhReJL6 +EugV5eaGonA52TWtFdB8p+79wPUeI3KcdPmQ9Ll5Zi/jBemY4bzasmgKzNeMtwWP +fk6WgrvBwptqohw71HDymGxFUnUP7XYYjic2sVKhv9AevMGycVgwWBiWroDCQ9Ja +btKfxHhI2p+g+rcywmBobWJbZsujTNjhtme+kNn1mhJsD3bKPjKQfAxaTskBLb0V +wgV21891TS1Dq9kdPLwoS4XNpYg2LLB4p9hmeG3fu9+OmqwY5oKXsHiWc43dei9Y +yxZ1AAUOIaIdPkq+YG/PhlGE4YcQZ4RPpltAr0HfGgZhmXWigbGS+66pUj+Ojysc +j0K5tCVxVu0fhhFpOlHv0LWaxCbnkgkQH9jfMEJkAWMOuQINBGCAXCYBEADW6RNr +ZVGNXvHVBqSiOWaxl1XOiEoiHPt50Aijt25yXbG+0kHIFSoR+1g6Lh20JTCChgfQ +kGGjzQvEuG1HTw07YhsvLc0pkjNMfu6gJqFox/ogc53mz69OxXauzUQ/TZ27GDVp +UBu+EhDKt1s3OtA6Bjz/csop/Um7gT0+ivHyvJ/jGdnPEZv8tNuSE/Uo+hn/Q9hg +8SbveZzo3C+U4KcabCESEFl8Gq6aRi9vAfa65oxD5jKaIz7cy+pwb0lizqlW7H9t +Qlr3dBfdIcdzgR55hTFC5/XrcwJ6/nHVH/xGskEasnfCQX8RYKMuy0UADJy72TkZ +bYaCx+XXIcVB8GTOmJVoAhrTSSVLAZspfCnjwnSxisDn3ZzsYrq3cV6sU8b+QlIX +7VAjurE+5cZiVlaxgCjyhKqlGgmonnReWOBacCgL/UvuwMmMp5TTLmiLXLT7uxeG +ojEyoCk4sMrqrU1jevHyGlDJH9Taux15GILDwnYFfAvPF9WCid4UZ4Ouwjcaxfys +3LxNiZIlUsXNKwS3mhiMRL4TRsbs4k4QE+LIMOsauIvcvm8/frydvQ/kUwIhVTH8 +0XGOH909bYtJvY3fudK7ShIwm7ZFTduBJUG473E/Fn3VkhTmBX6+PjOC50HR/Hyb +waRCzfDruMe3TAcE/tSP5CUOb9C7+P+hPzQcDwARAQABiQRyBBgBCgAmFiEEyHQB +Hwq0BRENAhBVNDZdlHLXRo8FAmCAXCYCGwIFCQlmAYACQAkQNDZdlHLXRo/BdCAE +GQEKAB0WIQQ3TsdbSFkTYEqDHMfIIMbVzSerhwUCYIBcJgAKCRDIIMbVzSerh0Xw +D/9ghnUsoNCu1OulcoJdHboMazJvDt/znttdQSnULBVElgM5zk0Uyv87zFBzuCyQ +JWL3bWesQ2uFx5fRWEPDEfWVdDrjpQGb1OCCQyz1QlNPV/1M1/xhKGS9EeXrL8Dw +F6KTGkRwn1yXiP4BGgfeFIQHmJcKXEZ9HkrpNb8mcexkROv4aIPAwn+IaE+NHVtt +IBnufMXLyfpkWJQtJa9elh9PMLlHHnuvnYLvuAoOkhuvs7fXDMpfFZ01C+QSv1dz +Hm52GSStERQzZ51w4c0rYDneYDniC/sQT1x3dP5Xf6wzO+EhRMabkvoTbMqPsTEP +xyWr2pNtTBYp7pfQjsHxhJpQF0xjGN9C39z7f3gJG8IJhnPeulUqEZjhRFyVZQ6/ +siUeq7vu4+dM/JQL+i7KKe7Lp9UMrG6NLMH+ltaoD3+lVm8fdTUxS5MNPoA/I8cK +1OWTJHkrp7V/XaY7mUtvQn5V1yET5b4bogz4nME6WLiFMd+7x73gB+YJ6MGYNuO8 +e/NFK67MfHbk1/AiPTAJ6s5uHRQIkZcBPG7y5PpfcHpIlwPYCDGYlTajZXblyKrw +BttVnYKvKsnlysv11glSg0DphGxQJbXzWpvBNyhMNH5dffcfvd3eXJAxnD81GD2z +ZAriMJ4Av2TfeqQ2nxd2ddn0jX4WVHtAvLXfCgLM2Gveho4jD/9sZ6PZz/rEeTvt +h88t50qPcBa4bb25X0B5FO3TeK2LL3VKLuEp5lgdcHVonrcdqZFobN1CgGJua8TW +SprIkh+8ATZ/FXQTi01NzLhHXT1IQzSpFaZw0gb2f5ruXwvTPpfXzQrs2omY+7s7 +fkCwGPesvpSXPKn9v8uhUwD7NGW/Dm+jUM+QtC/FqzX7+/Q+OuEPjClUh1cqopCZ +EvAI3HjnavGrYuU6DgQdjyGT/UDbuwbCXqHxHojVVkISGzCTGpmBcQYQqhcFRedJ +yJlu6PSXlA7+8Ajh52oiMJ3ez4xSssFgUQAyOB16432tm4erpGmCyakkoRmMUn3p +wx+QIppxRlsHznhcCQKR3tcblUqH3vq5i4/ZAihusMCa0YrShtxfdSb13oKX+pFr +aZXvxyZlCa5qoQQBV1sowmPL1N2j3dR9TVpdTyCFQSv4KeiExmowtLIjeCppRBEK +eeYHJnlfkyKXPhxTVVO6H+dU4nVu0ASQZ07KiQjbI+zTpPKFLPp3/0sPRJM57r1+ +aTS71iR7nZNZ1f8LZV2OvGE6fJVtgJ1J4Nu02K54uuIhU3tg1+7Xt+IqwRc9rbVr +pHH/hFCYBPW2D2dxB+k2pQlg5NI+TpsXj5Zun8kRw5RtVb+dLuiH/xmxArIee8Jq +ZF5q4h4I33PSGDdSvGXn9UMY5Isjpg== +=7pIB -----END PGP PUBLIC KEY BLOCK-----` // HashicorpPartnersKey is a key created by HashiCorp, used to generate and diff --git a/vendor/github.com/hashicorp/terraform/internal/getproviders/registry_client.go b/vendor/github.com/hashicorp/terraform/internal/getproviders/registry_client.go index befa7427..7608fb80 100644 --- a/vendor/github.com/hashicorp/terraform/internal/getproviders/registry_client.go +++ b/vendor/github.com/hashicorp/terraform/internal/getproviders/registry_client.go @@ -261,7 +261,7 @@ func (c *registryClient) PackageMeta(ctx context.Context, provider addrs.Provide match = true } } - if match == false { + if !match { // If the protocol version is not supported, try to find the closest // matching version. closest, err := c.findClosestProtocolCompatibleVersion(ctx, provider, version) diff --git a/vendor/github.com/hashicorp/terraform/internal/getproviders/testdata/filesystem-mirror/registry.terraform.io/hashicorp/null/2.0.0/windows_amd64/terraform-provider-null.exe b/vendor/github.com/hashicorp/terraform/internal/getproviders/testdata/filesystem-mirror/registry.terraform.io/hashicorp/null/2.0.0/windows_amd64/terraform-provider-null.exe new file mode 100644 index 00000000..daa9e350 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/getproviders/testdata/filesystem-mirror/registry.terraform.io/hashicorp/null/2.0.0/windows_amd64/terraform-provider-null.exe @@ -0,0 +1 @@ +# This is just a placeholder file for discovery testing, not a real provider plugin. diff --git a/vendor/github.com/hashicorp/terraform/internal/getproviders/types.go b/vendor/github.com/hashicorp/terraform/internal/getproviders/types.go index 0f8bc7d7..c71fc589 100644 --- a/vendor/github.com/hashicorp/terraform/internal/getproviders/types.go +++ b/vendor/github.com/hashicorp/terraform/internal/getproviders/types.go @@ -395,8 +395,47 @@ func VersionConstraintsString(spec VersionConstraints) string { // lock files. Therefore the canonical forms produced here are a compatibility // constraint for the dependency lock file parser. + if len(spec) == 0 { + return "" + } + + // VersionConstraints values are typically assembled by combining together + // the version constraints from many separate declarations throughout + // a configuration, across many modules. As a consequence, they typically + // contain duplicates and the terms inside are in no particular order. + // For our canonical representation we'll both deduplicate the items + // and sort them into a consistent order. + sels := make(map[constraints.SelectionSpec]struct{}) + for _, sel := range spec { + // The parser allows writing abbreviated version (such as 2) which + // end up being represented in memory with trailing unconstrained parts + // (for example 2.*.*). For the purpose of serialization with Ruby + // style syntax, these unconstrained parts can all be represented as 0 + // with no loss of meaning, so we make that conversion here. Doing so + // allows us to deduplicate equivalent constraints, such as >= 2.0 and + // >= 2.0.0. + normalizedSel := constraints.SelectionSpec{ + Operator: sel.Operator, + Boundary: sel.Boundary.ConstrainToZero(), + } + sels[normalizedSel] = struct{}{} + } + selsOrder := make([]constraints.SelectionSpec, 0, len(sels)) + for sel := range sels { + selsOrder = append(selsOrder, sel) + } + sort.Slice(selsOrder, func(i, j int) bool { + is, js := selsOrder[i], selsOrder[j] + boundaryCmp := versionSelectionBoundaryCompare(is.Boundary, js.Boundary) + if boundaryCmp == 0 { + // The operator is the decider, then. + return versionSelectionOperatorLess(is.Operator, js.Operator) + } + return boundaryCmp < 0 + }) + var b strings.Builder - for i, sel := range spec { + for i, sel := range selsOrder { if i > 0 { b.WriteString(", ") } @@ -422,35 +461,98 @@ func VersionConstraintsString(spec VersionConstraints) string { b.WriteString("??? ") } - // The parser allows writing abbreviated version (such as 2) which - // end up being represented in memory with trailing unconstrained parts - // (for example 2.*.*). For the purpose of serialization with Ruby - // style syntax, these unconstrained parts can all be represented as 0 - // with no loss of meaning, so we make that conversion here. - // - // This is possible because we use a different constraint operator to - // distinguish between the two types of pessimistic constraint: - // minor-only and patch-only. For minor-only constraints, we always - // want to display only the major and minor version components, so we - // special-case that operator below. + // We use a different constraint operator to distinguish between the + // two types of pessimistic constraint: minor-only and patch-only. For + // minor-only constraints, we always want to display only the major and + // minor version components, so we special-case that operator below. // // One final edge case is a minor-only constraint specified with only // the major version, such as ~> 2. We treat this the same as ~> 2.0, // because a major-only pessimistic constraint does not exist: it is // logically identical to >= 2.0.0. - boundary := sel.Boundary.ConstrainToZero() if sel.Operator == constraints.OpGreaterThanOrEqualMinorOnly { // The minor-pessimistic syntax uses only two version components. - fmt.Fprintf(&b, "%s.%s", boundary.Major, boundary.Minor) + fmt.Fprintf(&b, "%s.%s", sel.Boundary.Major, sel.Boundary.Minor) } else { - fmt.Fprintf(&b, "%s.%s.%s", boundary.Major, boundary.Minor, boundary.Patch) + fmt.Fprintf(&b, "%s.%s.%s", sel.Boundary.Major, sel.Boundary.Minor, sel.Boundary.Patch) } if sel.Boundary.Prerelease != "" { - b.WriteString("-" + boundary.Prerelease) + b.WriteString("-" + sel.Boundary.Prerelease) } if sel.Boundary.Metadata != "" { - b.WriteString("+" + boundary.Metadata) + b.WriteString("+" + sel.Boundary.Metadata) } } return b.String() } + +// Our sort for selection operators is somewhat arbitrary and mainly motivated +// by consistency rather than meaning, but this ordering does at least try +// to make it so "simple" constraint sets will appear how a human might +// typically write them, with the lower bounds first and the upper bounds +// last. Weird mixtures of different sorts of constraints will likely seem +// less intuitive, but they'd be unintuitive no matter the ordering. +var versionSelectionsBoundaryPriority = map[constraints.SelectionOp]int{ + // We skip zero here so that if we end up seeing an invalid + // operator (which the string function would render as "???") + // then it will have index zero and thus appear first. + constraints.OpGreaterThan: 1, + constraints.OpGreaterThanOrEqual: 2, + constraints.OpEqual: 3, + constraints.OpGreaterThanOrEqualPatchOnly: 4, + constraints.OpGreaterThanOrEqualMinorOnly: 5, + constraints.OpLessThanOrEqual: 6, + constraints.OpLessThan: 7, + constraints.OpNotEqual: 8, +} + +func versionSelectionOperatorLess(i, j constraints.SelectionOp) bool { + iPrio := versionSelectionsBoundaryPriority[i] + jPrio := versionSelectionsBoundaryPriority[j] + return iPrio < jPrio +} + +func versionSelectionBoundaryCompare(i, j constraints.VersionSpec) int { + // In the Ruby-style constraint syntax, unconstrained parts appear + // only for omitted portions of a version string, like writing + // "2" instead of "2.0.0". For sorting purposes we'll just + // consider those as zero, which also matches how we serialize them + // to strings. + i, j = i.ConstrainToZero(), j.ConstrainToZero() + + // Once we've removed any unconstrained parts, we can safely + // convert to our main Version type so we can use its ordering. + iv := Version{ + Major: i.Major.Num, + Minor: i.Minor.Num, + Patch: i.Patch.Num, + Prerelease: versions.VersionExtra(i.Prerelease), + Metadata: versions.VersionExtra(i.Metadata), + } + jv := Version{ + Major: j.Major.Num, + Minor: j.Minor.Num, + Patch: j.Patch.Num, + Prerelease: versions.VersionExtra(j.Prerelease), + Metadata: versions.VersionExtra(j.Metadata), + } + if iv.Same(jv) { + // Although build metadata doesn't normally weigh in to + // precedence choices, we'll use it for our visual + // ordering just because we need to pick _some_ order. + switch { + case iv.Metadata.Raw() == jv.Metadata.Raw(): + return 0 + case iv.Metadata.LessThan(jv.Metadata): + return -1 + default: + return 1 // greater, by elimination + } + } + switch { + case iv.LessThan(jv): + return -1 + default: + return 1 // greater, by elimination + } +} diff --git a/vendor/github.com/hashicorp/terraform/internal/getproviders/types_test.go b/vendor/github.com/hashicorp/terraform/internal/getproviders/types_test.go new file mode 100644 index 00000000..b12cc215 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/getproviders/types_test.go @@ -0,0 +1,96 @@ +package getproviders + +import ( + "testing" +) + +func TestVersionConstraintsString(t *testing.T) { + testCases := map[string]struct { + spec VersionConstraints + want string + }{ + "exact": { + MustParseVersionConstraints("1.2.3"), + "1.2.3", + }, + "prerelease": { + MustParseVersionConstraints("1.2.3-beta"), + "1.2.3-beta", + }, + "metadata": { + MustParseVersionConstraints("1.2.3+foo.bar"), + "1.2.3+foo.bar", + }, + "prerelease and metadata": { + MustParseVersionConstraints("1.2.3-beta+foo.bar"), + "1.2.3-beta+foo.bar", + }, + "major only": { + MustParseVersionConstraints(">= 3"), + ">= 3.0.0", + }, + "major only with pessimistic operator": { + MustParseVersionConstraints("~> 3"), + "~> 3.0", + }, + "pessimistic minor": { + MustParseVersionConstraints("~> 3.0"), + "~> 3.0", + }, + "pessimistic patch": { + MustParseVersionConstraints("~> 3.0.0"), + "~> 3.0.0", + }, + "other operators": { + MustParseVersionConstraints("> 1.0.0, < 1.0.0, >= 1.0.0, <= 1.0.0, != 1.0.0"), + "> 1.0.0, >= 1.0.0, <= 1.0.0, < 1.0.0, != 1.0.0", + }, + "multiple": { + MustParseVersionConstraints(">= 3.0, < 4.0"), + ">= 3.0.0, < 4.0.0", + }, + "duplicates removed": { + MustParseVersionConstraints(">= 1.2.3, 1.2.3, ~> 1.2, 1.2.3"), + "~> 1.2, >= 1.2.3, 1.2.3", + }, + "equivalent duplicates removed": { + MustParseVersionConstraints(">= 2.68, >= 2.68.0"), + ">= 2.68.0", + }, + "consistent ordering, exhaustive": { + // This weird jumble is just to exercise the different sort + // ordering codepaths. Hopefully nothing quite this horrific + // shows up often in practice. + MustParseVersionConstraints("< 1.2.3, <= 1.2.3, != 1.2.3, 1.2.3+local.2, 1.2.3+local.1, = 1.2.4, = 1.2.3, > 2, > 1.2.3, >= 1.2.3, ~> 1.2.3, ~> 1.2"), + "~> 1.2, > 1.2.3, >= 1.2.3, 1.2.3, ~> 1.2.3, <= 1.2.3, < 1.2.3, != 1.2.3, 1.2.3+local.1, 1.2.3+local.2, 1.2.4, > 2.0.0", + }, + "consistent ordering, more typical": { + // This one is aiming to simulate a common situation where + // various different modules express compatible constraints + // but some modules are more constrained than others. The + // combined results here can be kinda confusing, but hopefully + // ordering them consistently makes them a _little_ easier to read. + MustParseVersionConstraints("~> 1.2, >= 1.2, 1.2.4"), + ">= 1.2.0, ~> 1.2, 1.2.4", + }, + "consistent ordering, disjoint": { + // One situation where our presentation of version constraints is + // particularly important is when a user accidentally ends up with + // disjoint constraints that can therefore never match. In that + // case, our ordering should hopefully make it easier to determine + // that the constraints are disjoint, as a first step to debugging, + // by showing > or >= constrains sorted after < or <= constraints. + MustParseVersionConstraints(">= 2, >= 1.2, < 1.3"), + ">= 1.2.0, < 1.3.0, >= 2.0.0", + }, + } + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + got := VersionConstraintsString(tc.spec) + + if got != tc.want { + t.Errorf("wrong\n got: %q\nwant: %q", got, tc.want) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/internal/grpcwrap/provider.go b/vendor/github.com/hashicorp/terraform/internal/grpcwrap/provider.go new file mode 100644 index 00000000..3a054a25 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/grpcwrap/provider.go @@ -0,0 +1,415 @@ +package grpcwrap + +import ( + "context" + + "github.com/hashicorp/terraform/internal/tfplugin5" + "github.com/hashicorp/terraform/plugin/convert" + "github.com/hashicorp/terraform/providers" + "github.com/zclconf/go-cty/cty" + ctyjson "github.com/zclconf/go-cty/cty/json" + "github.com/zclconf/go-cty/cty/msgpack" +) + +// New wraps a providers.Interface to implement a grpc ProviderServer. +// This is useful for creating a test binary out of an internal provider +// implementation. +func Provider(p providers.Interface) tfplugin5.ProviderServer { + return &provider{ + provider: p, + schema: p.GetProviderSchema(), + } +} + +type provider struct { + provider providers.Interface + schema providers.GetProviderSchemaResponse +} + +func (p *provider) GetSchema(_ context.Context, req *tfplugin5.GetProviderSchema_Request) (*tfplugin5.GetProviderSchema_Response, error) { + resp := &tfplugin5.GetProviderSchema_Response{ + ResourceSchemas: make(map[string]*tfplugin5.Schema), + DataSourceSchemas: make(map[string]*tfplugin5.Schema), + } + + resp.Provider = &tfplugin5.Schema{ + Block: &tfplugin5.Schema_Block{}, + } + if p.schema.Provider.Block != nil { + resp.Provider.Block = convert.ConfigSchemaToProto(p.schema.Provider.Block) + } + + resp.ProviderMeta = &tfplugin5.Schema{ + Block: &tfplugin5.Schema_Block{}, + } + if p.schema.ProviderMeta.Block != nil { + resp.ProviderMeta.Block = convert.ConfigSchemaToProto(p.schema.ProviderMeta.Block) + } + + for typ, res := range p.schema.ResourceTypes { + resp.ResourceSchemas[typ] = &tfplugin5.Schema{ + Version: res.Version, + Block: convert.ConfigSchemaToProto(res.Block), + } + } + for typ, dat := range p.schema.DataSources { + resp.DataSourceSchemas[typ] = &tfplugin5.Schema{ + Version: dat.Version, + Block: convert.ConfigSchemaToProto(dat.Block), + } + } + + // include any diagnostics from the original GetSchema call + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, p.schema.Diagnostics) + + return resp, nil +} + +func (p *provider) PrepareProviderConfig(_ context.Context, req *tfplugin5.PrepareProviderConfig_Request) (*tfplugin5.PrepareProviderConfig_Response, error) { + resp := &tfplugin5.PrepareProviderConfig_Response{} + ty := p.schema.Provider.Block.ImpliedType() + + configVal, err := decodeDynamicValue(req.Config, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + prepareResp := p.provider.ValidateProviderConfig(providers.ValidateProviderConfigRequest{ + Config: configVal, + }) + + // the PreparedConfig value is no longer used + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, prepareResp.Diagnostics) + return resp, nil +} + +func (p *provider) ValidateResourceTypeConfig(_ context.Context, req *tfplugin5.ValidateResourceTypeConfig_Request) (*tfplugin5.ValidateResourceTypeConfig_Response, error) { + resp := &tfplugin5.ValidateResourceTypeConfig_Response{} + ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType() + + configVal, err := decodeDynamicValue(req.Config, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + validateResp := p.provider.ValidateResourceConfig(providers.ValidateResourceConfigRequest{ + TypeName: req.TypeName, + Config: configVal, + }) + + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, validateResp.Diagnostics) + return resp, nil +} + +func (p *provider) ValidateDataSourceConfig(_ context.Context, req *tfplugin5.ValidateDataSourceConfig_Request) (*tfplugin5.ValidateDataSourceConfig_Response, error) { + resp := &tfplugin5.ValidateDataSourceConfig_Response{} + ty := p.schema.DataSources[req.TypeName].Block.ImpliedType() + + configVal, err := decodeDynamicValue(req.Config, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + validateResp := p.provider.ValidateDataResourceConfig(providers.ValidateDataResourceConfigRequest{ + TypeName: req.TypeName, + Config: configVal, + }) + + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, validateResp.Diagnostics) + return resp, nil +} + +func (p *provider) UpgradeResourceState(_ context.Context, req *tfplugin5.UpgradeResourceState_Request) (*tfplugin5.UpgradeResourceState_Response, error) { + resp := &tfplugin5.UpgradeResourceState_Response{} + ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType() + + upgradeResp := p.provider.UpgradeResourceState(providers.UpgradeResourceStateRequest{ + TypeName: req.TypeName, + Version: req.Version, + RawStateJSON: req.RawState.Json, + }) + + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, upgradeResp.Diagnostics) + if upgradeResp.Diagnostics.HasErrors() { + return resp, nil + } + + dv, err := encodeDynamicValue(upgradeResp.UpgradedState, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + resp.UpgradedState = dv + + return resp, nil +} + +func (p *provider) Configure(_ context.Context, req *tfplugin5.Configure_Request) (*tfplugin5.Configure_Response, error) { + resp := &tfplugin5.Configure_Response{} + ty := p.schema.Provider.Block.ImpliedType() + + configVal, err := decodeDynamicValue(req.Config, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + configureResp := p.provider.ConfigureProvider(providers.ConfigureProviderRequest{ + TerraformVersion: req.TerraformVersion, + Config: configVal, + }) + + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, configureResp.Diagnostics) + return resp, nil +} + +func (p *provider) ReadResource(_ context.Context, req *tfplugin5.ReadResource_Request) (*tfplugin5.ReadResource_Response, error) { + resp := &tfplugin5.ReadResource_Response{} + ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType() + + stateVal, err := decodeDynamicValue(req.CurrentState, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + metaTy := p.schema.ProviderMeta.Block.ImpliedType() + metaVal, err := decodeDynamicValue(req.ProviderMeta, metaTy) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + readResp := p.provider.ReadResource(providers.ReadResourceRequest{ + TypeName: req.TypeName, + PriorState: stateVal, + Private: req.Private, + ProviderMeta: metaVal, + }) + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, readResp.Diagnostics) + if readResp.Diagnostics.HasErrors() { + return resp, nil + } + resp.Private = readResp.Private + + dv, err := encodeDynamicValue(readResp.NewState, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + resp.NewState = dv + + return resp, nil +} + +func (p *provider) PlanResourceChange(_ context.Context, req *tfplugin5.PlanResourceChange_Request) (*tfplugin5.PlanResourceChange_Response, error) { + resp := &tfplugin5.PlanResourceChange_Response{} + ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType() + + priorStateVal, err := decodeDynamicValue(req.PriorState, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + proposedStateVal, err := decodeDynamicValue(req.ProposedNewState, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + configVal, err := decodeDynamicValue(req.Config, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + metaTy := p.schema.ProviderMeta.Block.ImpliedType() + metaVal, err := decodeDynamicValue(req.ProviderMeta, metaTy) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + planResp := p.provider.PlanResourceChange(providers.PlanResourceChangeRequest{ + TypeName: req.TypeName, + PriorState: priorStateVal, + ProposedNewState: proposedStateVal, + Config: configVal, + PriorPrivate: req.PriorPrivate, + ProviderMeta: metaVal, + }) + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, planResp.Diagnostics) + if planResp.Diagnostics.HasErrors() { + return resp, nil + } + + resp.PlannedPrivate = planResp.PlannedPrivate + + resp.PlannedState, err = encodeDynamicValue(planResp.PlannedState, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + for _, path := range planResp.RequiresReplace { + resp.RequiresReplace = append(resp.RequiresReplace, convert.PathToAttributePath(path)) + } + + return resp, nil +} + +func (p *provider) ApplyResourceChange(_ context.Context, req *tfplugin5.ApplyResourceChange_Request) (*tfplugin5.ApplyResourceChange_Response, error) { + resp := &tfplugin5.ApplyResourceChange_Response{} + ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType() + + priorStateVal, err := decodeDynamicValue(req.PriorState, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + plannedStateVal, err := decodeDynamicValue(req.PlannedState, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + configVal, err := decodeDynamicValue(req.Config, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + metaTy := p.schema.ProviderMeta.Block.ImpliedType() + metaVal, err := decodeDynamicValue(req.ProviderMeta, metaTy) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + applyResp := p.provider.ApplyResourceChange(providers.ApplyResourceChangeRequest{ + TypeName: req.TypeName, + PriorState: priorStateVal, + PlannedState: plannedStateVal, + Config: configVal, + PlannedPrivate: req.PlannedPrivate, + ProviderMeta: metaVal, + }) + + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, applyResp.Diagnostics) + if applyResp.Diagnostics.HasErrors() { + return resp, nil + } + resp.Private = applyResp.Private + + resp.NewState, err = encodeDynamicValue(applyResp.NewState, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + return resp, nil +} + +func (p *provider) ImportResourceState(_ context.Context, req *tfplugin5.ImportResourceState_Request) (*tfplugin5.ImportResourceState_Response, error) { + resp := &tfplugin5.ImportResourceState_Response{} + + importResp := p.provider.ImportResourceState(providers.ImportResourceStateRequest{ + TypeName: req.TypeName, + ID: req.Id, + }) + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, importResp.Diagnostics) + + for _, res := range importResp.ImportedResources { + ty := p.schema.ResourceTypes[res.TypeName].Block.ImpliedType() + state, err := encodeDynamicValue(res.State, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + continue + } + + resp.ImportedResources = append(resp.ImportedResources, &tfplugin5.ImportResourceState_ImportedResource{ + TypeName: res.TypeName, + State: state, + Private: res.Private, + }) + } + + return resp, nil +} + +func (p *provider) ReadDataSource(_ context.Context, req *tfplugin5.ReadDataSource_Request) (*tfplugin5.ReadDataSource_Response, error) { + resp := &tfplugin5.ReadDataSource_Response{} + ty := p.schema.DataSources[req.TypeName].Block.ImpliedType() + + configVal, err := decodeDynamicValue(req.Config, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + metaTy := p.schema.ProviderMeta.Block.ImpliedType() + metaVal, err := decodeDynamicValue(req.ProviderMeta, metaTy) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + readResp := p.provider.ReadDataSource(providers.ReadDataSourceRequest{ + TypeName: req.TypeName, + Config: configVal, + ProviderMeta: metaVal, + }) + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, readResp.Diagnostics) + if readResp.Diagnostics.HasErrors() { + return resp, nil + } + + resp.State, err = encodeDynamicValue(readResp.State, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + return resp, nil +} + +func (p *provider) Stop(context.Context, *tfplugin5.Stop_Request) (*tfplugin5.Stop_Response, error) { + resp := &tfplugin5.Stop_Response{} + err := p.provider.Stop() + if err != nil { + resp.Error = err.Error() + } + return resp, nil +} + +// decode a DynamicValue from either the JSON or MsgPack encoding. +func decodeDynamicValue(v *tfplugin5.DynamicValue, ty cty.Type) (cty.Value, error) { + // always return a valid value + var err error + res := cty.NullVal(ty) + if v == nil { + return res, nil + } + + switch { + case len(v.Msgpack) > 0: + res, err = msgpack.Unmarshal(v.Msgpack, ty) + case len(v.Json) > 0: + res, err = ctyjson.Unmarshal(v.Json, ty) + } + return res, err +} + +// encode a cty.Value into a DynamicValue msgpack payload. +func encodeDynamicValue(v cty.Value, ty cty.Type) (*tfplugin5.DynamicValue, error) { + mp, err := msgpack.Marshal(v, ty) + return &tfplugin5.DynamicValue{ + Msgpack: mp, + }, err +} diff --git a/vendor/github.com/hashicorp/terraform/internal/grpcwrap/provider6.go b/vendor/github.com/hashicorp/terraform/internal/grpcwrap/provider6.go new file mode 100644 index 00000000..49a91c19 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/grpcwrap/provider6.go @@ -0,0 +1,415 @@ +package grpcwrap + +import ( + "context" + + "github.com/hashicorp/terraform/internal/tfplugin6" + "github.com/hashicorp/terraform/plugin6/convert" + "github.com/hashicorp/terraform/providers" + "github.com/zclconf/go-cty/cty" + ctyjson "github.com/zclconf/go-cty/cty/json" + "github.com/zclconf/go-cty/cty/msgpack" +) + +// New wraps a providers.Interface to implement a grpc ProviderServer using +// plugin protocol v6. This is useful for creating a test binary out of an +// internal provider implementation. +func Provider6(p providers.Interface) tfplugin6.ProviderServer { + return &provider6{ + provider: p, + schema: p.GetProviderSchema(), + } +} + +type provider6 struct { + provider providers.Interface + schema providers.GetProviderSchemaResponse +} + +func (p *provider6) GetProviderSchema(_ context.Context, req *tfplugin6.GetProviderSchema_Request) (*tfplugin6.GetProviderSchema_Response, error) { + resp := &tfplugin6.GetProviderSchema_Response{ + ResourceSchemas: make(map[string]*tfplugin6.Schema), + DataSourceSchemas: make(map[string]*tfplugin6.Schema), + } + + resp.Provider = &tfplugin6.Schema{ + Block: &tfplugin6.Schema_Block{}, + } + if p.schema.Provider.Block != nil { + resp.Provider.Block = convert.ConfigSchemaToProto(p.schema.Provider.Block) + } + + resp.ProviderMeta = &tfplugin6.Schema{ + Block: &tfplugin6.Schema_Block{}, + } + if p.schema.ProviderMeta.Block != nil { + resp.ProviderMeta.Block = convert.ConfigSchemaToProto(p.schema.ProviderMeta.Block) + } + + for typ, res := range p.schema.ResourceTypes { + resp.ResourceSchemas[typ] = &tfplugin6.Schema{ + Version: res.Version, + Block: convert.ConfigSchemaToProto(res.Block), + } + } + for typ, dat := range p.schema.DataSources { + resp.DataSourceSchemas[typ] = &tfplugin6.Schema{ + Version: dat.Version, + Block: convert.ConfigSchemaToProto(dat.Block), + } + } + + // include any diagnostics from the original GetSchema call + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, p.schema.Diagnostics) + + return resp, nil +} + +func (p *provider6) ValidateProviderConfig(_ context.Context, req *tfplugin6.ValidateProviderConfig_Request) (*tfplugin6.ValidateProviderConfig_Response, error) { + resp := &tfplugin6.ValidateProviderConfig_Response{} + ty := p.schema.Provider.Block.ImpliedType() + + configVal, err := decodeDynamicValue6(req.Config, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + prepareResp := p.provider.ValidateProviderConfig(providers.ValidateProviderConfigRequest{ + Config: configVal, + }) + + // the PreparedConfig value is no longer used + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, prepareResp.Diagnostics) + return resp, nil +} + +func (p *provider6) ValidateResourceConfig(_ context.Context, req *tfplugin6.ValidateResourceConfig_Request) (*tfplugin6.ValidateResourceConfig_Response, error) { + resp := &tfplugin6.ValidateResourceConfig_Response{} + ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType() + + configVal, err := decodeDynamicValue6(req.Config, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + validateResp := p.provider.ValidateResourceConfig(providers.ValidateResourceConfigRequest{ + TypeName: req.TypeName, + Config: configVal, + }) + + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, validateResp.Diagnostics) + return resp, nil +} + +func (p *provider6) ValidateDataResourceConfig(_ context.Context, req *tfplugin6.ValidateDataResourceConfig_Request) (*tfplugin6.ValidateDataResourceConfig_Response, error) { + resp := &tfplugin6.ValidateDataResourceConfig_Response{} + ty := p.schema.DataSources[req.TypeName].Block.ImpliedType() + + configVal, err := decodeDynamicValue6(req.Config, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + validateResp := p.provider.ValidateDataResourceConfig(providers.ValidateDataResourceConfigRequest{ + TypeName: req.TypeName, + Config: configVal, + }) + + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, validateResp.Diagnostics) + return resp, nil +} + +func (p *provider6) UpgradeResourceState(_ context.Context, req *tfplugin6.UpgradeResourceState_Request) (*tfplugin6.UpgradeResourceState_Response, error) { + resp := &tfplugin6.UpgradeResourceState_Response{} + ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType() + + upgradeResp := p.provider.UpgradeResourceState(providers.UpgradeResourceStateRequest{ + TypeName: req.TypeName, + Version: req.Version, + RawStateJSON: req.RawState.Json, + }) + + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, upgradeResp.Diagnostics) + if upgradeResp.Diagnostics.HasErrors() { + return resp, nil + } + + dv, err := encodeDynamicValue6(upgradeResp.UpgradedState, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + resp.UpgradedState = dv + + return resp, nil +} + +func (p *provider6) ConfigureProvider(_ context.Context, req *tfplugin6.ConfigureProvider_Request) (*tfplugin6.ConfigureProvider_Response, error) { + resp := &tfplugin6.ConfigureProvider_Response{} + ty := p.schema.Provider.Block.ImpliedType() + + configVal, err := decodeDynamicValue6(req.Config, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + configureResp := p.provider.ConfigureProvider(providers.ConfigureProviderRequest{ + TerraformVersion: req.TerraformVersion, + Config: configVal, + }) + + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, configureResp.Diagnostics) + return resp, nil +} + +func (p *provider6) ReadResource(_ context.Context, req *tfplugin6.ReadResource_Request) (*tfplugin6.ReadResource_Response, error) { + resp := &tfplugin6.ReadResource_Response{} + ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType() + + stateVal, err := decodeDynamicValue6(req.CurrentState, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + metaTy := p.schema.ProviderMeta.Block.ImpliedType() + metaVal, err := decodeDynamicValue6(req.ProviderMeta, metaTy) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + readResp := p.provider.ReadResource(providers.ReadResourceRequest{ + TypeName: req.TypeName, + PriorState: stateVal, + Private: req.Private, + ProviderMeta: metaVal, + }) + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, readResp.Diagnostics) + if readResp.Diagnostics.HasErrors() { + return resp, nil + } + resp.Private = readResp.Private + + dv, err := encodeDynamicValue6(readResp.NewState, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + resp.NewState = dv + + return resp, nil +} + +func (p *provider6) PlanResourceChange(_ context.Context, req *tfplugin6.PlanResourceChange_Request) (*tfplugin6.PlanResourceChange_Response, error) { + resp := &tfplugin6.PlanResourceChange_Response{} + ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType() + + priorStateVal, err := decodeDynamicValue6(req.PriorState, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + proposedStateVal, err := decodeDynamicValue6(req.ProposedNewState, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + configVal, err := decodeDynamicValue6(req.Config, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + metaTy := p.schema.ProviderMeta.Block.ImpliedType() + metaVal, err := decodeDynamicValue6(req.ProviderMeta, metaTy) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + planResp := p.provider.PlanResourceChange(providers.PlanResourceChangeRequest{ + TypeName: req.TypeName, + PriorState: priorStateVal, + ProposedNewState: proposedStateVal, + Config: configVal, + PriorPrivate: req.PriorPrivate, + ProviderMeta: metaVal, + }) + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, planResp.Diagnostics) + if planResp.Diagnostics.HasErrors() { + return resp, nil + } + + resp.PlannedPrivate = planResp.PlannedPrivate + + resp.PlannedState, err = encodeDynamicValue6(planResp.PlannedState, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + for _, path := range planResp.RequiresReplace { + resp.RequiresReplace = append(resp.RequiresReplace, convert.PathToAttributePath(path)) + } + + return resp, nil +} + +func (p *provider6) ApplyResourceChange(_ context.Context, req *tfplugin6.ApplyResourceChange_Request) (*tfplugin6.ApplyResourceChange_Response, error) { + resp := &tfplugin6.ApplyResourceChange_Response{} + ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType() + + priorStateVal, err := decodeDynamicValue6(req.PriorState, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + plannedStateVal, err := decodeDynamicValue6(req.PlannedState, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + configVal, err := decodeDynamicValue6(req.Config, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + metaTy := p.schema.ProviderMeta.Block.ImpliedType() + metaVal, err := decodeDynamicValue6(req.ProviderMeta, metaTy) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + applyResp := p.provider.ApplyResourceChange(providers.ApplyResourceChangeRequest{ + TypeName: req.TypeName, + PriorState: priorStateVal, + PlannedState: plannedStateVal, + Config: configVal, + PlannedPrivate: req.PlannedPrivate, + ProviderMeta: metaVal, + }) + + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, applyResp.Diagnostics) + if applyResp.Diagnostics.HasErrors() { + return resp, nil + } + resp.Private = applyResp.Private + + resp.NewState, err = encodeDynamicValue6(applyResp.NewState, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + return resp, nil +} + +func (p *provider6) ImportResourceState(_ context.Context, req *tfplugin6.ImportResourceState_Request) (*tfplugin6.ImportResourceState_Response, error) { + resp := &tfplugin6.ImportResourceState_Response{} + + importResp := p.provider.ImportResourceState(providers.ImportResourceStateRequest{ + TypeName: req.TypeName, + ID: req.Id, + }) + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, importResp.Diagnostics) + + for _, res := range importResp.ImportedResources { + ty := p.schema.ResourceTypes[res.TypeName].Block.ImpliedType() + state, err := encodeDynamicValue6(res.State, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + continue + } + + resp.ImportedResources = append(resp.ImportedResources, &tfplugin6.ImportResourceState_ImportedResource{ + TypeName: res.TypeName, + State: state, + Private: res.Private, + }) + } + + return resp, nil +} + +func (p *provider6) ReadDataSource(_ context.Context, req *tfplugin6.ReadDataSource_Request) (*tfplugin6.ReadDataSource_Response, error) { + resp := &tfplugin6.ReadDataSource_Response{} + ty := p.schema.DataSources[req.TypeName].Block.ImpliedType() + + configVal, err := decodeDynamicValue6(req.Config, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + metaTy := p.schema.ProviderMeta.Block.ImpliedType() + metaVal, err := decodeDynamicValue6(req.ProviderMeta, metaTy) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + readResp := p.provider.ReadDataSource(providers.ReadDataSourceRequest{ + TypeName: req.TypeName, + Config: configVal, + ProviderMeta: metaVal, + }) + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, readResp.Diagnostics) + if readResp.Diagnostics.HasErrors() { + return resp, nil + } + + resp.State, err = encodeDynamicValue6(readResp.State, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + return resp, nil +} + +func (p *provider6) StopProvider(context.Context, *tfplugin6.StopProvider_Request) (*tfplugin6.StopProvider_Response, error) { + resp := &tfplugin6.StopProvider_Response{} + err := p.provider.Stop() + if err != nil { + resp.Error = err.Error() + } + return resp, nil +} + +// decode a DynamicValue from either the JSON or MsgPack encoding. +func decodeDynamicValue6(v *tfplugin6.DynamicValue, ty cty.Type) (cty.Value, error) { + // always return a valid value + var err error + res := cty.NullVal(ty) + if v == nil { + return res, nil + } + + switch { + case len(v.Msgpack) > 0: + res, err = msgpack.Unmarshal(v.Msgpack, ty) + case len(v.Json) > 0: + res, err = ctyjson.Unmarshal(v.Json, ty) + } + return res, err +} + +// encode a cty.Value into a DynamicValue msgpack payload. +func encodeDynamicValue6(v cty.Value, ty cty.Type) (*tfplugin6.DynamicValue, error) { + mp, err := msgpack.Marshal(v, ty) + return &tfplugin6.DynamicValue{ + Msgpack: mp, + }, err +} diff --git a/vendor/github.com/hashicorp/terraform/internal/grpcwrap/provisioner.go b/vendor/github.com/hashicorp/terraform/internal/grpcwrap/provisioner.go new file mode 100644 index 00000000..8213b508 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/grpcwrap/provisioner.go @@ -0,0 +1,116 @@ +package grpcwrap + +import ( + "context" + "log" + "strings" + "unicode/utf8" + + "github.com/hashicorp/terraform/communicator/shared" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/internal/tfplugin5" + "github.com/hashicorp/terraform/plugin/convert" + "github.com/hashicorp/terraform/provisioners" +) + +// New wraps a provisioners.Interface to implement a grpc ProviderServer. +// This is useful for creating a test binary out of an internal provider +// implementation. +func Provisioner(p provisioners.Interface) tfplugin5.ProvisionerServer { + return &provisioner{ + provisioner: p, + schema: p.GetSchema().Provisioner, + } +} + +type provisioner struct { + provisioner provisioners.Interface + schema *configschema.Block +} + +func (p *provisioner) GetSchema(_ context.Context, req *tfplugin5.GetProvisionerSchema_Request) (*tfplugin5.GetProvisionerSchema_Response, error) { + resp := &tfplugin5.GetProvisionerSchema_Response{} + + resp.Provisioner = &tfplugin5.Schema{ + Block: &tfplugin5.Schema_Block{}, + } + + if p.schema != nil { + resp.Provisioner.Block = convert.ConfigSchemaToProto(p.schema) + } + + return resp, nil +} + +func (p *provisioner) ValidateProvisionerConfig(_ context.Context, req *tfplugin5.ValidateProvisionerConfig_Request) (*tfplugin5.ValidateProvisionerConfig_Response, error) { + resp := &tfplugin5.ValidateProvisionerConfig_Response{} + ty := p.schema.ImpliedType() + + configVal, err := decodeDynamicValue(req.Config, ty) + if err != nil { + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) + return resp, nil + } + + validateResp := p.provisioner.ValidateProvisionerConfig(provisioners.ValidateProvisionerConfigRequest{ + Config: configVal, + }) + + resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, validateResp.Diagnostics) + return resp, nil +} + +func (p *provisioner) ProvisionResource(req *tfplugin5.ProvisionResource_Request, srv tfplugin5.Provisioner_ProvisionResourceServer) error { + // We send back a diagnostics over the stream if there was a + // provisioner-side problem. + srvResp := &tfplugin5.ProvisionResource_Response{} + + ty := p.schema.ImpliedType() + configVal, err := decodeDynamicValue(req.Config, ty) + if err != nil { + srvResp.Diagnostics = convert.AppendProtoDiag(srvResp.Diagnostics, err) + srv.Send(srvResp) + return nil + } + + connVal, err := decodeDynamicValue(req.Connection, shared.ConnectionBlockSupersetSchema.ImpliedType()) + if err != nil { + srvResp.Diagnostics = convert.AppendProtoDiag(srvResp.Diagnostics, err) + srv.Send(srvResp) + return nil + } + + resp := p.provisioner.ProvisionResource(provisioners.ProvisionResourceRequest{ + Config: configVal, + Connection: connVal, + UIOutput: uiOutput{srv}, + }) + + srvResp.Diagnostics = convert.AppendProtoDiag(srvResp.Diagnostics, resp.Diagnostics) + srv.Send(srvResp) + return nil +} + +func (p *provisioner) Stop(context.Context, *tfplugin5.Stop_Request) (*tfplugin5.Stop_Response, error) { + resp := &tfplugin5.Stop_Response{} + err := p.provisioner.Stop() + if err != nil { + resp.Error = err.Error() + } + return resp, nil +} + +// uiOutput implements the terraform.UIOutput interface to adapt the grpc +// stream to the legacy Provisioner.Apply method. +type uiOutput struct { + srv tfplugin5.Provisioner_ProvisionResourceServer +} + +func (o uiOutput) Output(s string) { + err := o.srv.Send(&tfplugin5.ProvisionResource_Response{ + Output: strings.ToValidUTF8(s, string(utf8.RuneError)), + }) + if err != nil { + log.Printf("[ERROR] %s", err) + } +} diff --git a/vendor/github.com/hashicorp/terraform/helper/slowmessage/slowmessage.go b/vendor/github.com/hashicorp/terraform/internal/helper/slowmessage/slowmessage.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/slowmessage/slowmessage.go rename to vendor/github.com/hashicorp/terraform/internal/helper/slowmessage/slowmessage.go diff --git a/vendor/github.com/hashicorp/terraform/helper/slowmessage/slowmessage_test.go b/vendor/github.com/hashicorp/terraform/internal/helper/slowmessage/slowmessage_test.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/slowmessage/slowmessage_test.go rename to vendor/github.com/hashicorp/terraform/internal/helper/slowmessage/slowmessage_test.go diff --git a/vendor/github.com/hashicorp/terraform/helper/wrappedreadline/wrappedreadline.go b/vendor/github.com/hashicorp/terraform/internal/helper/wrappedreadline/wrappedreadline.go similarity index 96% rename from vendor/github.com/hashicorp/terraform/helper/wrappedreadline/wrappedreadline.go rename to vendor/github.com/hashicorp/terraform/internal/helper/wrappedreadline/wrappedreadline.go index 86d7b921..6d2ffd15 100644 --- a/vendor/github.com/hashicorp/terraform/helper/wrappedreadline/wrappedreadline.go +++ b/vendor/github.com/hashicorp/terraform/internal/helper/wrappedreadline/wrappedreadline.go @@ -14,7 +14,7 @@ import ( "github.com/chzyer/readline" - "github.com/hashicorp/terraform/helper/wrappedstreams" + "github.com/hashicorp/terraform/internal/helper/wrappedstreams" ) // Override overrides the values in readline.Config that need to be diff --git a/vendor/github.com/hashicorp/terraform/helper/wrappedreadline/wrappedreadline_unix.go b/vendor/github.com/hashicorp/terraform/internal/helper/wrappedreadline/wrappedreadline_unix.go similarity index 91% rename from vendor/github.com/hashicorp/terraform/helper/wrappedreadline/wrappedreadline_unix.go rename to vendor/github.com/hashicorp/terraform/internal/helper/wrappedreadline/wrappedreadline_unix.go index 4e410c7b..00cf2932 100644 --- a/vendor/github.com/hashicorp/terraform/helper/wrappedreadline/wrappedreadline_unix.go +++ b/vendor/github.com/hashicorp/terraform/internal/helper/wrappedreadline/wrappedreadline_unix.go @@ -6,7 +6,7 @@ import ( "syscall" "unsafe" - "github.com/hashicorp/terraform/helper/wrappedstreams" + "github.com/hashicorp/terraform/internal/helper/wrappedstreams" ) // getWidth impl for Unix diff --git a/vendor/github.com/hashicorp/terraform/helper/wrappedreadline/wrappedreadline_windows.go b/vendor/github.com/hashicorp/terraform/internal/helper/wrappedreadline/wrappedreadline_windows.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/wrappedreadline/wrappedreadline_windows.go rename to vendor/github.com/hashicorp/terraform/internal/helper/wrappedreadline/wrappedreadline_windows.go diff --git a/vendor/github.com/hashicorp/terraform/internal/helper/wrappedstreams/streams.go b/vendor/github.com/hashicorp/terraform/internal/helper/wrappedstreams/streams.go new file mode 100644 index 00000000..1ccc4397 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/helper/wrappedstreams/streams.go @@ -0,0 +1,44 @@ +// Package wrappedstreams provides access to the standard OS streams +// (stdin, stdout, stderr) even if wrapped under panicwrap. +package wrappedstreams + +import ( + "os" + + "github.com/mitchellh/panicwrap" +) + +// Stdin returns the true stdin of the process. +func Stdin() *os.File { + stdin, _, _ := fds() + return stdin +} + +// Stdout returns the true stdout of the process. +func Stdout() *os.File { + _, stdout, _ := fds() + return stdout +} + +// Stderr returns the true stderr of the process. +func Stderr() *os.File { + _, _, stderr := fds() + return stderr +} + +func fds() (stdin, stdout, stderr *os.File) { + stdin, stdout, stderr = os.Stdin, os.Stdout, os.Stderr + if panicwrap.Wrapped(nil) { + initPlatform() + stdin, stdout, stderr = wrappedStdin, wrappedStdout, wrappedStderr + } + return +} + +// These are the wrapped standard streams. These are set up by the +// platform specific code in initPlatform. +var ( + wrappedStdin *os.File + wrappedStdout *os.File + wrappedStderr *os.File +) diff --git a/vendor/github.com/hashicorp/terraform/helper/wrappedstreams/streams_other.go b/vendor/github.com/hashicorp/terraform/internal/helper/wrappedstreams/streams_other.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/wrappedstreams/streams_other.go rename to vendor/github.com/hashicorp/terraform/internal/helper/wrappedstreams/streams_other.go diff --git a/vendor/github.com/hashicorp/terraform/helper/wrappedstreams/streams_windows.go b/vendor/github.com/hashicorp/terraform/internal/helper/wrappedstreams/streams_windows.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/wrappedstreams/streams_windows.go rename to vendor/github.com/hashicorp/terraform/internal/helper/wrappedstreams/streams_windows.go diff --git a/vendor/github.com/hashicorp/terraform/helper/acctest/acctest.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/acctest/acctest.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/acctest/acctest.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/acctest/acctest.go diff --git a/vendor/github.com/hashicorp/terraform/helper/acctest/random.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/acctest/random.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/acctest/random.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/acctest/random.go diff --git a/vendor/github.com/hashicorp/terraform/helper/acctest/random_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/acctest/random_test.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/acctest/random_test.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/acctest/random_test.go diff --git a/vendor/github.com/hashicorp/terraform/helper/acctest/remotetests.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/acctest/remotetests.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/acctest/remotetests.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/acctest/remotetests.go diff --git a/vendor/github.com/hashicorp/terraform/helper/hashcode/hashcode.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/hashcode/hashcode.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/hashcode/hashcode.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/hashcode/hashcode.go diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/hashcode/hashcode_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/hashcode/hashcode_test.go new file mode 100644 index 00000000..4b90431b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/hashcode/hashcode_test.go @@ -0,0 +1,37 @@ +package hashcode + +import ( + "testing" +) + +func TestString(t *testing.T) { + v := "hello, world" + expected := String(v) + for i := 0; i < 100; i++ { + actual := String(v) + if actual != expected { + t.Fatalf("bad: %#v\n\t%#v", actual, expected) + } + } +} + +func TestStrings(t *testing.T) { + v := []string{"hello", ",", "world"} + expected := Strings(v) + for i := 0; i < 100; i++ { + actual := Strings(v) + if actual != expected { + t.Fatalf("bad: %#v\n\t%#v", actual, expected) + } + } +} + +func TestString_positiveIndex(t *testing.T) { + // "2338615298" hashes to uint32(2147483648) which is math.MinInt32 + ips := []string{"192.168.1.3", "192.168.1.5", "2338615298"} + for _, ip := range ips { + if index := String(ip); index < 0 { + t.Fatalf("Bad Index %#v for ip %s", index, ip) + } + } +} diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/backend.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/backend.go new file mode 100644 index 00000000..a7f440e0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/backend.go @@ -0,0 +1,200 @@ +package schema + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform/tfdiags" + "github.com/zclconf/go-cty/cty" + + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/configs/hcl2shim" + "github.com/hashicorp/terraform/internal/legacy/terraform" + ctyconvert "github.com/zclconf/go-cty/cty/convert" +) + +// Backend represents a partial backend.Backend implementation and simplifies +// the creation of configuration loading and validation. +// +// Unlike other schema structs such as Provider, this struct is meant to be +// embedded within your actual implementation. It provides implementations +// only for Input and Configure and gives you a method for accessing the +// configuration in the form of a ResourceData that you're expected to call +// from the other implementation funcs. +type Backend struct { + // Schema is the schema for the configuration of this backend. If this + // Backend has no configuration this can be omitted. + Schema map[string]*Schema + + // ConfigureFunc is called to configure the backend. Use the + // FromContext* methods to extract information from the context. + // This can be nil, in which case nothing will be called but the + // config will still be stored. + ConfigureFunc func(context.Context) error + + config *ResourceData +} + +var ( + backendConfigKey = contextKey("backend config") +) + +// FromContextBackendConfig extracts a ResourceData with the configuration +// from the context. This should only be called by Backend functions. +func FromContextBackendConfig(ctx context.Context) *ResourceData { + return ctx.Value(backendConfigKey).(*ResourceData) +} + +func (b *Backend) ConfigSchema() *configschema.Block { + // This is an alias of CoreConfigSchema just to implement the + // backend.Backend interface. + return b.CoreConfigSchema() +} + +func (b *Backend) PrepareConfig(configVal cty.Value) (cty.Value, tfdiags.Diagnostics) { + if b == nil { + return configVal, nil + } + var diags tfdiags.Diagnostics + var err error + + // In order to use Transform below, this needs to be filled out completely + // according the schema. + configVal, err = b.CoreConfigSchema().CoerceValue(configVal) + if err != nil { + return configVal, diags.Append(err) + } + + // lookup any required, top-level attributes that are Null, and see if we + // have a Default value available. + configVal, err = cty.Transform(configVal, func(path cty.Path, val cty.Value) (cty.Value, error) { + // we're only looking for top-level attributes + if len(path) != 1 { + return val, nil + } + + // nothing to do if we already have a value + if !val.IsNull() { + return val, nil + } + + // get the Schema definition for this attribute + getAttr, ok := path[0].(cty.GetAttrStep) + // these should all exist, but just ignore anything strange + if !ok { + return val, nil + } + + attrSchema := b.Schema[getAttr.Name] + // continue to ignore anything that doesn't match + if attrSchema == nil { + return val, nil + } + + // this is deprecated, so don't set it + if attrSchema.Deprecated != "" || attrSchema.Removed != "" { + return val, nil + } + + // find a default value if it exists + def, err := attrSchema.DefaultValue() + if err != nil { + diags = diags.Append(fmt.Errorf("error getting default for %q: %s", getAttr.Name, err)) + return val, err + } + + // no default + if def == nil { + return val, nil + } + + // create a cty.Value and make sure it's the correct type + tmpVal := hcl2shim.HCL2ValueFromConfigValue(def) + + // helper/schema used to allow setting "" to a bool + if val.Type() == cty.Bool && tmpVal.RawEquals(cty.StringVal("")) { + // return a warning about the conversion + diags = diags.Append("provider set empty string as default value for bool " + getAttr.Name) + tmpVal = cty.False + } + + val, err = ctyconvert.Convert(tmpVal, val.Type()) + if err != nil { + diags = diags.Append(fmt.Errorf("error setting default for %q: %s", getAttr.Name, err)) + } + + return val, err + }) + if err != nil { + // any error here was already added to the diagnostics + return configVal, diags + } + + shimRC := b.shimConfig(configVal) + warns, errs := schemaMap(b.Schema).Validate(shimRC) + for _, warn := range warns { + diags = diags.Append(tfdiags.SimpleWarning(warn)) + } + for _, err := range errs { + diags = diags.Append(err) + } + return configVal, diags +} + +func (b *Backend) Configure(obj cty.Value) tfdiags.Diagnostics { + if b == nil { + return nil + } + + var diags tfdiags.Diagnostics + sm := schemaMap(b.Schema) + shimRC := b.shimConfig(obj) + + // Get a ResourceData for this configuration. To do this, we actually + // generate an intermediary "diff" although that is never exposed. + diff, err := sm.Diff(nil, shimRC, nil, nil, true) + if err != nil { + diags = diags.Append(err) + return diags + } + + data, err := sm.Data(nil, diff) + if err != nil { + diags = diags.Append(err) + return diags + } + b.config = data + + if b.ConfigureFunc != nil { + err = b.ConfigureFunc(context.WithValue( + context.Background(), backendConfigKey, data)) + if err != nil { + diags = diags.Append(err) + return diags + } + } + + return diags +} + +// shimConfig turns a new-style cty.Value configuration (which must be of +// an object type) into a minimal old-style *terraform.ResourceConfig object +// that should be populated enough to appease the not-yet-updated functionality +// in this package. This should be removed once everything is updated. +func (b *Backend) shimConfig(obj cty.Value) *terraform.ResourceConfig { + shimMap, ok := hcl2shim.ConfigValueFromHCL2(obj).(map[string]interface{}) + if !ok { + // If the configVal was nil, we still want a non-nil map here. + shimMap = map[string]interface{}{} + } + return &terraform.ResourceConfig{ + Config: shimMap, + Raw: shimMap, + } +} + +// Config returns the configuration. This is available after Configure is +// called. +func (b *Backend) Config() *ResourceData { + return b.config +} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/backend_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/backend_test.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/backend_test.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/backend_test.go diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/core_schema.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/core_schema.go diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/core_schema_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/core_schema_test.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/core_schema_test.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/core_schema_test.go diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/data_source_resource_shim.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/data_source_resource_shim.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/data_source_resource_shim.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/data_source_resource_shim.go diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/doc.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/doc.go new file mode 100644 index 00000000..f1a0e86d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/doc.go @@ -0,0 +1,5 @@ +// Package schema is a legacy package that used to represent the SDK, which is now its own +// library external to Terraform Core https://github.com/hashicorp/terraform-plugin-sdk +// Some of it is still used by Terraform's remote state backends, but this entire +// package should be removed in the future. +package schema diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/equal.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/equal.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/equal.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/equal.go diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader.go diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_config.go similarity index 99% rename from vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_config.go index 6ad3f13c..f4a43d1f 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_config.go @@ -7,7 +7,7 @@ import ( "strings" "sync" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/internal/legacy/terraform" "github.com/mitchellh/mapstructure" ) diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_config_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_config_test.go new file mode 100644 index 00000000..53b8e907 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_config_test.go @@ -0,0 +1,540 @@ +package schema + +import ( + "bytes" + "fmt" + "reflect" + "testing" + + "github.com/hashicorp/terraform/configs/hcl2shim" + "github.com/hashicorp/terraform/internal/legacy/helper/hashcode" + "github.com/hashicorp/terraform/internal/legacy/terraform" +) + +func TestConfigFieldReader_impl(t *testing.T) { + var _ FieldReader = new(ConfigFieldReader) +} + +func TestConfigFieldReader(t *testing.T) { + testFieldReader(t, func(s map[string]*Schema) FieldReader { + return &ConfigFieldReader{ + Schema: s, + + Config: testConfig(t, map[string]interface{}{ + "bool": true, + "float": 3.1415, + "int": 42, + "string": "string", + + "list": []interface{}{"foo", "bar"}, + + "listInt": []interface{}{21, 42}, + + "map": map[string]interface{}{ + "foo": "bar", + "bar": "baz", + }, + "mapInt": map[string]interface{}{ + "one": "1", + "two": "2", + }, + "mapIntNestedSchema": map[string]interface{}{ + "one": "1", + "two": "2", + }, + "mapFloat": map[string]interface{}{ + "oneDotTwo": "1.2", + }, + "mapBool": map[string]interface{}{ + "True": "true", + "False": "false", + }, + + "set": []interface{}{10, 50}, + "setDeep": []interface{}{ + map[string]interface{}{ + "index": 10, + "value": "foo", + }, + map[string]interface{}{ + "index": 50, + "value": "bar", + }, + }, + }), + } + }) +} + +// This contains custom table tests for our ConfigFieldReader +func TestConfigFieldReader_custom(t *testing.T) { + schema := map[string]*Schema{ + "bool": &Schema{ + Type: TypeBool, + }, + } + + cases := map[string]struct { + Addr []string + Result FieldReadResult + Config *terraform.ResourceConfig + Err bool + }{ + "basic": { + []string{"bool"}, + FieldReadResult{ + Value: true, + Exists: true, + }, + testConfig(t, map[string]interface{}{ + "bool": true, + }), + false, + }, + + "computed": { + []string{"bool"}, + FieldReadResult{ + Exists: true, + Computed: true, + }, + testConfig(t, map[string]interface{}{ + "bool": hcl2shim.UnknownVariableValue, + }), + false, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + r := &ConfigFieldReader{ + Schema: schema, + Config: tc.Config, + } + out, err := r.ReadField(tc.Addr) + if err != nil != tc.Err { + t.Fatalf("%s: err: %s", name, err) + } + if s, ok := out.Value.(*Set); ok { + // If it is a set, convert to a list so its more easily checked. + out.Value = s.List() + } + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("%s: bad: %#v", name, out) + } + }) + } +} + +func TestConfigFieldReader_DefaultHandling(t *testing.T) { + schema := map[string]*Schema{ + "strWithDefault": &Schema{ + Type: TypeString, + Default: "ImADefault", + }, + "strWithDefaultFunc": &Schema{ + Type: TypeString, + DefaultFunc: func() (interface{}, error) { + return "FuncDefault", nil + }, + }, + } + + cases := map[string]struct { + Addr []string + Result FieldReadResult + Config *terraform.ResourceConfig + Err bool + }{ + "gets default value when no config set": { + []string{"strWithDefault"}, + FieldReadResult{ + Value: "ImADefault", + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{}), + false, + }, + "config overrides default value": { + []string{"strWithDefault"}, + FieldReadResult{ + Value: "fromConfig", + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "strWithDefault": "fromConfig", + }), + false, + }, + "gets default from function when no config set": { + []string{"strWithDefaultFunc"}, + FieldReadResult{ + Value: "FuncDefault", + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{}), + false, + }, + "config overrides default function": { + []string{"strWithDefaultFunc"}, + FieldReadResult{ + Value: "fromConfig", + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "strWithDefaultFunc": "fromConfig", + }), + false, + }, + } + + for name, tc := range cases { + r := &ConfigFieldReader{ + Schema: schema, + Config: tc.Config, + } + out, err := r.ReadField(tc.Addr) + if err != nil != tc.Err { + t.Fatalf("%s: err: %s", name, err) + } + if s, ok := out.Value.(*Set); ok { + // If it is a set, convert to a list so its more easily checked. + out.Value = s.List() + } + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("%s: bad: %#v", name, out) + } + } +} + +func TestConfigFieldReader_ComputedMap(t *testing.T) { + schema := map[string]*Schema{ + "map": &Schema{ + Type: TypeMap, + Computed: true, + }, + "listmap": &Schema{ + Type: TypeMap, + Computed: true, + Elem: TypeList, + }, + "maplist": &Schema{ + Type: TypeList, + Computed: true, + Elem: TypeMap, + }, + } + + cases := []struct { + Name string + Addr []string + Result FieldReadResult + Config *terraform.ResourceConfig + Err bool + }{ + { + "set, normal", + []string{"map"}, + FieldReadResult{ + Value: map[string]interface{}{ + "foo": "bar", + }, + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "map": map[string]interface{}{ + "foo": "bar", + }, + }), + false, + }, + + { + "computed element", + []string{"map"}, + FieldReadResult{ + Exists: true, + Computed: true, + }, + testConfig(t, map[string]interface{}{ + "map": map[string]interface{}{ + "foo": hcl2shim.UnknownVariableValue, + }, + }), + false, + }, + + { + "native map", + []string{"map"}, + FieldReadResult{ + Value: map[string]interface{}{ + "bar": "baz", + "baz": "bar", + }, + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "map": map[string]interface{}{ + "bar": "baz", + "baz": "bar", + }, + }), + false, + }, + + { + "map-from-list-of-maps", + []string{"maplist", "0"}, + FieldReadResult{ + Value: map[string]interface{}{ + "key": "bar", + }, + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "maplist": []interface{}{ + map[string]interface{}{ + "key": "bar", + }, + }, + }), + false, + }, + + { + "value-from-list-of-maps", + []string{"maplist", "0", "key"}, + FieldReadResult{ + Value: "bar", + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "maplist": []interface{}{ + map[string]interface{}{ + "key": "bar", + }, + }, + }), + false, + }, + + { + "list-from-map-of-lists", + []string{"listmap", "key"}, + FieldReadResult{ + Value: []interface{}{"bar"}, + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "listmap": map[string]interface{}{ + "key": []interface{}{ + "bar", + }, + }, + }), + false, + }, + + { + "value-from-map-of-lists", + []string{"listmap", "key", "0"}, + FieldReadResult{ + Value: "bar", + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "listmap": map[string]interface{}{ + "key": []interface{}{ + "bar", + }, + }, + }), + false, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + r := &ConfigFieldReader{ + Schema: schema, + Config: tc.Config, + } + out, err := r.ReadField(tc.Addr) + if err != nil != tc.Err { + t.Fatal(err) + } + if s, ok := out.Value.(*Set); ok { + // If it is a set, convert to the raw map + out.Value = s.m + if len(s.m) == 0 { + out.Value = nil + } + } + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("\nexpected: %#v\ngot: %#v", tc.Result, out) + } + }) + } +} + +func TestConfigFieldReader_ComputedSet(t *testing.T) { + schema := map[string]*Schema{ + "strSet": &Schema{ + Type: TypeSet, + Elem: &Schema{Type: TypeString}, + Set: HashString, + }, + } + + cases := map[string]struct { + Addr []string + Result FieldReadResult + Config *terraform.ResourceConfig + Err bool + }{ + "set, normal": { + []string{"strSet"}, + FieldReadResult{ + Value: map[string]interface{}{ + "2356372769": "foo", + }, + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "strSet": []interface{}{"foo"}, + }), + false, + }, + + "set, computed element": { + []string{"strSet"}, + FieldReadResult{ + Value: nil, + Exists: true, + Computed: true, + }, + testConfig(t, map[string]interface{}{ + "strSet": []interface{}{hcl2shim.UnknownVariableValue}, + }), + false, + }, + } + + for name, tc := range cases { + r := &ConfigFieldReader{ + Schema: schema, + Config: tc.Config, + } + out, err := r.ReadField(tc.Addr) + if err != nil != tc.Err { + t.Fatalf("%s: err: %s", name, err) + } + if s, ok := out.Value.(*Set); ok { + // If it is a set, convert to the raw map + out.Value = s.m + if len(s.m) == 0 { + out.Value = nil + } + } + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("%s: bad: %#v", name, out) + } + } +} + +func TestConfigFieldReader_computedComplexSet(t *testing.T) { + hashfunc := func(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["vhd_uri"].(string))) + return hashcode.String(buf.String()) + } + + schema := map[string]*Schema{ + "set": &Schema{ + Type: TypeSet, + Elem: &Resource{ + Schema: map[string]*Schema{ + "name": { + Type: TypeString, + Required: true, + }, + + "vhd_uri": { + Type: TypeString, + Required: true, + }, + }, + }, + Set: hashfunc, + }, + } + + cases := map[string]struct { + Addr []string + Result FieldReadResult + Config *terraform.ResourceConfig + Err bool + }{ + "set, normal": { + []string{"set"}, + FieldReadResult{ + Value: map[string]interface{}{ + "532860136": map[string]interface{}{ + "name": "myosdisk1", + "vhd_uri": "bar", + }, + }, + Exists: true, + Computed: false, + }, + testConfig(t, map[string]interface{}{ + "set": []interface{}{ + map[string]interface{}{ + "name": "myosdisk1", + "vhd_uri": "bar", + }, + }, + }), + false, + }, + } + + for name, tc := range cases { + r := &ConfigFieldReader{ + Schema: schema, + Config: tc.Config, + } + out, err := r.ReadField(tc.Addr) + if err != nil != tc.Err { + t.Fatalf("%s: err: %s", name, err) + } + if s, ok := out.Value.(*Set); ok { + // If it is a set, convert to the raw map + out.Value = s.m + if len(s.m) == 0 { + out.Value = nil + } + } + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("%s: bad: %#v", name, out) + } + } +} + +func testConfig(t *testing.T, raw map[string]interface{}) *terraform.ResourceConfig { + return terraform.NewResourceConfigRaw(raw) +} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_diff.go similarity index 99% rename from vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_diff.go index 3e70acf0..84ebe272 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_diff.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/internal/legacy/terraform" "github.com/mitchellh/mapstructure" ) diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_diff_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_diff_test.go new file mode 100644 index 00000000..1f6fa7da --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_diff_test.go @@ -0,0 +1,524 @@ +package schema + +import ( + "reflect" + "testing" + + "github.com/hashicorp/terraform/internal/legacy/terraform" +) + +func TestDiffFieldReader_impl(t *testing.T) { + var _ FieldReader = new(DiffFieldReader) +} + +func TestDiffFieldReader_NestedSetUpdate(t *testing.T) { + hashFn := func(a interface{}) int { + m := a.(map[string]interface{}) + return m["val"].(int) + } + + schema := map[string]*Schema{ + "list_of_sets_1": &Schema{ + Type: TypeList, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_set": &Schema{ + Type: TypeSet, + Elem: &Resource{ + Schema: map[string]*Schema{ + "val": &Schema{ + Type: TypeInt, + }, + }, + }, + Set: hashFn, + }, + }, + }, + }, + "list_of_sets_2": &Schema{ + Type: TypeList, + Elem: &Resource{ + Schema: map[string]*Schema{ + "nested_set": &Schema{ + Type: TypeSet, + Elem: &Resource{ + Schema: map[string]*Schema{ + "val": &Schema{ + Type: TypeInt, + }, + }, + }, + Set: hashFn, + }, + }, + }, + }, + } + + r := &DiffFieldReader{ + Schema: schema, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "list_of_sets_1.0.nested_set.1.val": &terraform.ResourceAttrDiff{ + Old: "1", + New: "0", + NewRemoved: true, + }, + "list_of_sets_1.0.nested_set.2.val": &terraform.ResourceAttrDiff{ + New: "2", + }, + }, + }, + } + + r.Source = &MultiLevelFieldReader{ + Readers: map[string]FieldReader{ + "diff": r, + "set": &MapFieldReader{Schema: schema}, + "state": &MapFieldReader{ + Map: &BasicMapReader{ + "list_of_sets_1.#": "1", + "list_of_sets_1.0.nested_set.#": "1", + "list_of_sets_1.0.nested_set.1.val": "1", + "list_of_sets_2.#": "1", + "list_of_sets_2.0.nested_set.#": "1", + "list_of_sets_2.0.nested_set.1.val": "1", + }, + Schema: schema, + }, + }, + Levels: []string{"state", "config"}, + } + + out, err := r.ReadField([]string{"list_of_sets_2"}) + if err != nil { + t.Fatalf("err: %v", err) + } + + s := &Set{F: hashFn} + s.Add(map[string]interface{}{"val": 1}) + expected := s.List() + + l := out.Value.([]interface{}) + i := l[0].(map[string]interface{}) + actual := i["nested_set"].(*Set).List() + + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("bad: NestedSetUpdate\n\nexpected: %#v\n\ngot: %#v\n\n", expected, actual) + } +} + +// https://github.com/hashicorp/terraform/issues/914 +func TestDiffFieldReader_MapHandling(t *testing.T) { + schema := map[string]*Schema{ + "tags": &Schema{ + Type: TypeMap, + }, + } + r := &DiffFieldReader{ + Schema: schema, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "tags.%": &terraform.ResourceAttrDiff{ + Old: "1", + New: "2", + }, + "tags.baz": &terraform.ResourceAttrDiff{ + Old: "", + New: "qux", + }, + }, + }, + Source: &MapFieldReader{ + Schema: schema, + Map: BasicMapReader(map[string]string{ + "tags.%": "1", + "tags.foo": "bar", + }), + }, + } + + result, err := r.ReadField([]string{"tags"}) + if err != nil { + t.Fatalf("ReadField failed: %#v", err) + } + + expected := map[string]interface{}{ + "foo": "bar", + "baz": "qux", + } + + if !reflect.DeepEqual(expected, result.Value) { + t.Fatalf("bad: DiffHandling\n\nexpected: %#v\n\ngot: %#v\n\n", expected, result.Value) + } +} + +func TestDiffFieldReader_extra(t *testing.T) { + schema := map[string]*Schema{ + "stringComputed": &Schema{Type: TypeString}, + + "listMap": &Schema{ + Type: TypeList, + Elem: &Schema{ + Type: TypeMap, + }, + }, + + "mapRemove": &Schema{Type: TypeMap}, + + "setChange": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": &Schema{ + Type: TypeInt, + Required: true, + }, + + "value": &Schema{ + Type: TypeString, + Required: true, + }, + }, + }, + Set: func(a interface{}) int { + m := a.(map[string]interface{}) + return m["index"].(int) + }, + }, + + "setEmpty": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": &Schema{ + Type: TypeInt, + Required: true, + }, + + "value": &Schema{ + Type: TypeString, + Required: true, + }, + }, + }, + Set: func(a interface{}) int { + m := a.(map[string]interface{}) + return m["index"].(int) + }, + }, + } + + r := &DiffFieldReader{ + Schema: schema, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "stringComputed": &terraform.ResourceAttrDiff{ + Old: "foo", + New: "bar", + NewComputed: true, + }, + + "listMap.0.bar": &terraform.ResourceAttrDiff{ + NewRemoved: true, + }, + + "mapRemove.bar": &terraform.ResourceAttrDiff{ + NewRemoved: true, + }, + + "setChange.10.value": &terraform.ResourceAttrDiff{ + Old: "50", + New: "80", + }, + + "setEmpty.#": &terraform.ResourceAttrDiff{ + Old: "2", + New: "0", + }, + }, + }, + + Source: &MapFieldReader{ + Schema: schema, + Map: BasicMapReader(map[string]string{ + "listMap.#": "2", + "listMap.0.foo": "bar", + "listMap.0.bar": "baz", + "listMap.1.baz": "baz", + + "mapRemove.foo": "bar", + "mapRemove.bar": "bar", + + "setChange.#": "1", + "setChange.10.index": "10", + "setChange.10.value": "50", + + "setEmpty.#": "2", + "setEmpty.10.index": "10", + "setEmpty.10.value": "50", + "setEmpty.20.index": "20", + "setEmpty.20.value": "50", + }), + }, + } + + cases := map[string]struct { + Addr []string + Result FieldReadResult + Err bool + }{ + "stringComputed": { + []string{"stringComputed"}, + FieldReadResult{ + Value: "", + Exists: true, + Computed: true, + }, + false, + }, + + "listMapRemoval": { + []string{"listMap"}, + FieldReadResult{ + Value: []interface{}{ + map[string]interface{}{ + "foo": "bar", + }, + map[string]interface{}{ + "baz": "baz", + }, + }, + Exists: true, + }, + false, + }, + + "mapRemove": { + []string{"mapRemove"}, + FieldReadResult{ + Value: map[string]interface{}{ + "foo": "bar", + }, + Exists: true, + Computed: false, + }, + false, + }, + + "setChange": { + []string{"setChange"}, + FieldReadResult{ + Value: []interface{}{ + map[string]interface{}{ + "index": 10, + "value": "80", + }, + }, + Exists: true, + }, + false, + }, + + "setEmpty": { + []string{"setEmpty"}, + FieldReadResult{ + Value: []interface{}{}, + Exists: true, + }, + false, + }, + } + + for name, tc := range cases { + out, err := r.ReadField(tc.Addr) + if err != nil != tc.Err { + t.Fatalf("%s: err: %s", name, err) + } + if s, ok := out.Value.(*Set); ok { + // If it is a set, convert to a list so its more easily checked. + out.Value = s.List() + } + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("%s: bad: %#v", name, out) + } + } +} + +func TestDiffFieldReader(t *testing.T) { + testFieldReader(t, func(s map[string]*Schema) FieldReader { + return &DiffFieldReader{ + Schema: s, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "bool": &terraform.ResourceAttrDiff{ + Old: "", + New: "true", + }, + + "int": &terraform.ResourceAttrDiff{ + Old: "", + New: "42", + }, + + "float": &terraform.ResourceAttrDiff{ + Old: "", + New: "3.1415", + }, + + "string": &terraform.ResourceAttrDiff{ + Old: "", + New: "string", + }, + + "stringComputed": &terraform.ResourceAttrDiff{ + Old: "foo", + New: "bar", + NewComputed: true, + }, + + "list.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "2", + }, + + "list.0": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + }, + + "list.1": &terraform.ResourceAttrDiff{ + Old: "", + New: "bar", + }, + + "listInt.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "2", + }, + + "listInt.0": &terraform.ResourceAttrDiff{ + Old: "", + New: "21", + }, + + "listInt.1": &terraform.ResourceAttrDiff{ + Old: "", + New: "42", + }, + + "map.foo": &terraform.ResourceAttrDiff{ + Old: "", + New: "bar", + }, + + "map.bar": &terraform.ResourceAttrDiff{ + Old: "", + New: "baz", + }, + + "mapInt.%": &terraform.ResourceAttrDiff{ + Old: "", + New: "2", + }, + "mapInt.one": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "mapInt.two": &terraform.ResourceAttrDiff{ + Old: "", + New: "2", + }, + + "mapIntNestedSchema.%": &terraform.ResourceAttrDiff{ + Old: "", + New: "2", + }, + "mapIntNestedSchema.one": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "mapIntNestedSchema.two": &terraform.ResourceAttrDiff{ + Old: "", + New: "2", + }, + + "mapFloat.%": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "mapFloat.oneDotTwo": &terraform.ResourceAttrDiff{ + Old: "", + New: "1.2", + }, + + "mapBool.%": &terraform.ResourceAttrDiff{ + Old: "", + New: "2", + }, + "mapBool.True": &terraform.ResourceAttrDiff{ + Old: "", + New: "true", + }, + "mapBool.False": &terraform.ResourceAttrDiff{ + Old: "", + New: "false", + }, + + "set.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "2", + }, + + "set.10": &terraform.ResourceAttrDiff{ + Old: "", + New: "10", + }, + + "set.50": &terraform.ResourceAttrDiff{ + Old: "", + New: "50", + }, + + "setDeep.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "2", + }, + + "setDeep.10.index": &terraform.ResourceAttrDiff{ + Old: "", + New: "10", + }, + + "setDeep.10.value": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + }, + + "setDeep.50.index": &terraform.ResourceAttrDiff{ + Old: "", + New: "50", + }, + + "setDeep.50.value": &terraform.ResourceAttrDiff{ + Old: "", + New: "bar", + }, + }, + }, + + Source: &MapFieldReader{ + Schema: s, + Map: BasicMapReader(map[string]string{ + "listMap.#": "2", + "listMap.0.foo": "bar", + "listMap.0.bar": "baz", + "listMap.1.baz": "baz", + }), + }, + } + }) +} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_map.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_map.go diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_map_test.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map_test.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_map_test.go diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_multi.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_multi.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/field_reader_multi.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_multi.go diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_multi_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_multi_test.go new file mode 100644 index 00000000..7410335f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_multi_test.go @@ -0,0 +1,270 @@ +package schema + +import ( + "reflect" + "strconv" + "testing" + + "github.com/hashicorp/terraform/internal/legacy/terraform" +) + +func TestMultiLevelFieldReaderReadFieldExact(t *testing.T) { + cases := map[string]struct { + Addr []string + Readers []FieldReader + Level string + Result FieldReadResult + }{ + "specific": { + Addr: []string{"foo"}, + + Readers: []FieldReader{ + &MapFieldReader{ + Schema: map[string]*Schema{ + "foo": &Schema{Type: TypeString}, + }, + Map: BasicMapReader(map[string]string{ + "foo": "bar", + }), + }, + &MapFieldReader{ + Schema: map[string]*Schema{ + "foo": &Schema{Type: TypeString}, + }, + Map: BasicMapReader(map[string]string{ + "foo": "baz", + }), + }, + &MapFieldReader{ + Schema: map[string]*Schema{ + "foo": &Schema{Type: TypeString}, + }, + Map: BasicMapReader(map[string]string{}), + }, + }, + + Level: "1", + Result: FieldReadResult{ + Value: "baz", + Exists: true, + }, + }, + } + + for name, tc := range cases { + readers := make(map[string]FieldReader) + levels := make([]string, len(tc.Readers)) + for i, r := range tc.Readers { + is := strconv.FormatInt(int64(i), 10) + readers[is] = r + levels[i] = is + } + + r := &MultiLevelFieldReader{ + Readers: readers, + Levels: levels, + } + + out, err := r.ReadFieldExact(tc.Addr, tc.Level) + if err != nil { + t.Fatalf("%s: err: %s", name, err) + } + + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("%s: bad: %#v", name, out) + } + } +} + +func TestMultiLevelFieldReaderReadFieldMerge(t *testing.T) { + cases := map[string]struct { + Addr []string + Readers []FieldReader + Result FieldReadResult + }{ + "stringInDiff": { + Addr: []string{"availability_zone"}, + + Readers: []FieldReader{ + &DiffFieldReader{ + Schema: map[string]*Schema{ + "availability_zone": &Schema{Type: TypeString}, + }, + + Source: &MapFieldReader{ + Schema: map[string]*Schema{ + "availability_zone": &Schema{Type: TypeString}, + }, + Map: BasicMapReader(map[string]string{ + "availability_zone": "foo", + }), + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "foo", + New: "bar", + RequiresNew: true, + }, + }, + }, + }, + }, + + Result: FieldReadResult{ + Value: "bar", + Exists: true, + }, + }, + + "lastLevelComputed": { + Addr: []string{"availability_zone"}, + + Readers: []FieldReader{ + &MapFieldReader{ + Schema: map[string]*Schema{ + "availability_zone": &Schema{Type: TypeString}, + }, + + Map: BasicMapReader(map[string]string{ + "availability_zone": "foo", + }), + }, + + &DiffFieldReader{ + Schema: map[string]*Schema{ + "availability_zone": &Schema{Type: TypeString}, + }, + + Source: &MapFieldReader{ + Schema: map[string]*Schema{ + "availability_zone": &Schema{Type: TypeString}, + }, + + Map: BasicMapReader(map[string]string{ + "availability_zone": "foo", + }), + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "foo", + New: "bar", + NewComputed: true, + }, + }, + }, + }, + }, + + Result: FieldReadResult{ + Value: "", + Exists: true, + Computed: true, + }, + }, + + "list of maps with removal in diff": { + Addr: []string{"config_vars"}, + + Readers: []FieldReader{ + &DiffFieldReader{ + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeMap}, + }, + }, + + Source: &MapFieldReader{ + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeMap}, + }, + }, + + Map: BasicMapReader(map[string]string{ + "config_vars.#": "2", + "config_vars.0.foo": "bar", + "config_vars.0.bar": "bar", + "config_vars.1.bar": "baz", + }), + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.0.bar": &terraform.ResourceAttrDiff{ + NewRemoved: true, + }, + }, + }, + }, + }, + + Result: FieldReadResult{ + Value: []interface{}{ + map[string]interface{}{ + "foo": "bar", + }, + map[string]interface{}{ + "bar": "baz", + }, + }, + Exists: true, + }, + }, + + "first level only": { + Addr: []string{"foo"}, + + Readers: []FieldReader{ + &MapFieldReader{ + Schema: map[string]*Schema{ + "foo": &Schema{Type: TypeString}, + }, + Map: BasicMapReader(map[string]string{ + "foo": "bar", + }), + }, + &MapFieldReader{ + Schema: map[string]*Schema{ + "foo": &Schema{Type: TypeString}, + }, + Map: BasicMapReader(map[string]string{}), + }, + }, + + Result: FieldReadResult{ + Value: "bar", + Exists: true, + }, + }, + } + + for name, tc := range cases { + readers := make(map[string]FieldReader) + levels := make([]string, len(tc.Readers)) + for i, r := range tc.Readers { + is := strconv.FormatInt(int64(i), 10) + readers[is] = r + levels[i] = is + } + + r := &MultiLevelFieldReader{ + Readers: readers, + Levels: levels, + } + + out, err := r.ReadFieldMerge(tc.Addr, levels[len(levels)-1]) + if err != nil { + t.Fatalf("%s: err: %s", name, err) + } + + if !reflect.DeepEqual(tc.Result, out) { + t.Fatalf("%s: bad: %#v", name, out) + } + } +} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_test.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/field_reader_test.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_reader_test.go diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_writer.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_writer.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/field_writer.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_writer.go diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_writer_map.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_writer_map.go diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_writer_map_test.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map_test.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/field_writer_map_test.go diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/getsource_string.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/getsource_string.go diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/provider.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/provider.go new file mode 100644 index 00000000..24736566 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/provider.go @@ -0,0 +1,477 @@ +package schema + +import ( + "context" + "errors" + "fmt" + "sort" + "sync" + + multierror "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/internal/legacy/terraform" +) + +var ReservedProviderFields = []string{ + "alias", + "version", +} + +// Provider represents a resource provider in Terraform, and properly +// implements all of the ResourceProvider API. +// +// By defining a schema for the configuration of the provider, the +// map of supporting resources, and a configuration function, the schema +// framework takes over and handles all the provider operations for you. +// +// After defining the provider structure, it is unlikely that you'll require any +// of the methods on Provider itself. +type Provider struct { + // Schema is the schema for the configuration of this provider. If this + // provider has no configuration, this can be omitted. + // + // The keys of this map are the configuration keys, and the value is + // the schema describing the value of the configuration. + Schema map[string]*Schema + + // ResourcesMap is the list of available resources that this provider + // can manage, along with their Resource structure defining their + // own schemas and CRUD operations. + // + // Provider automatically handles routing operations such as Apply, + // Diff, etc. to the proper resource. + ResourcesMap map[string]*Resource + + // DataSourcesMap is the collection of available data sources that + // this provider implements, with a Resource instance defining + // the schema and Read operation of each. + // + // Resource instances for data sources must have a Read function + // and must *not* implement Create, Update or Delete. + DataSourcesMap map[string]*Resource + + // ProviderMetaSchema is the schema for the configuration of the meta + // information for this provider. If this provider has no meta info, + // this can be omitted. This functionality is currently experimental + // and subject to change or break without warning; it should only be + // used by providers that are collaborating on its use with the + // Terraform team. + ProviderMetaSchema map[string]*Schema + + // ConfigureFunc is a function for configuring the provider. If the + // provider doesn't need to be configured, this can be omitted. + // + // See the ConfigureFunc documentation for more information. + ConfigureFunc ConfigureFunc + + // MetaReset is called by TestReset to reset any state stored in the meta + // interface. This is especially important if the StopContext is stored by + // the provider. + MetaReset func() error + + meta interface{} + + // a mutex is required because TestReset can directly replace the stopCtx + stopMu sync.Mutex + stopCtx context.Context + stopCtxCancel context.CancelFunc + stopOnce sync.Once + + TerraformVersion string +} + +// ConfigureFunc is the function used to configure a Provider. +// +// The interface{} value returned by this function is stored and passed into +// the subsequent resources as the meta parameter. This return value is +// usually used to pass along a configured API client, a configuration +// structure, etc. +type ConfigureFunc func(*ResourceData) (interface{}, error) + +// InternalValidate should be called to validate the structure +// of the provider. +// +// This should be called in a unit test for any provider to verify +// before release that a provider is properly configured for use with +// this library. +func (p *Provider) InternalValidate() error { + if p == nil { + return errors.New("provider is nil") + } + + var validationErrors error + sm := schemaMap(p.Schema) + if err := sm.InternalValidate(sm); err != nil { + validationErrors = multierror.Append(validationErrors, err) + } + + // Provider-specific checks + for k, _ := range sm { + if isReservedProviderFieldName(k) { + return fmt.Errorf("%s is a reserved field name for a provider", k) + } + } + + for k, r := range p.ResourcesMap { + if err := r.InternalValidate(nil, true); err != nil { + validationErrors = multierror.Append(validationErrors, fmt.Errorf("resource %s: %s", k, err)) + } + } + + for k, r := range p.DataSourcesMap { + if err := r.InternalValidate(nil, false); err != nil { + validationErrors = multierror.Append(validationErrors, fmt.Errorf("data source %s: %s", k, err)) + } + } + + return validationErrors +} + +func isReservedProviderFieldName(name string) bool { + for _, reservedName := range ReservedProviderFields { + if name == reservedName { + return true + } + } + return false +} + +// Meta returns the metadata associated with this provider that was +// returned by the Configure call. It will be nil until Configure is called. +func (p *Provider) Meta() interface{} { + return p.meta +} + +// SetMeta can be used to forcefully set the Meta object of the provider. +// Note that if Configure is called the return value will override anything +// set here. +func (p *Provider) SetMeta(v interface{}) { + p.meta = v +} + +// Stopped reports whether the provider has been stopped or not. +func (p *Provider) Stopped() bool { + ctx := p.StopContext() + select { + case <-ctx.Done(): + return true + default: + return false + } +} + +// StopCh returns a channel that is closed once the provider is stopped. +func (p *Provider) StopContext() context.Context { + p.stopOnce.Do(p.stopInit) + + p.stopMu.Lock() + defer p.stopMu.Unlock() + + return p.stopCtx +} + +func (p *Provider) stopInit() { + p.stopMu.Lock() + defer p.stopMu.Unlock() + + p.stopCtx, p.stopCtxCancel = context.WithCancel(context.Background()) +} + +// Stop implementation of terraform.ResourceProvider interface. +func (p *Provider) Stop() error { + p.stopOnce.Do(p.stopInit) + + p.stopMu.Lock() + defer p.stopMu.Unlock() + + p.stopCtxCancel() + return nil +} + +// TestReset resets any state stored in the Provider, and will call TestReset +// on Meta if it implements the TestProvider interface. +// This may be used to reset the schema.Provider at the start of a test, and is +// automatically called by resource.Test. +func (p *Provider) TestReset() error { + p.stopInit() + if p.MetaReset != nil { + return p.MetaReset() + } + return nil +} + +// GetSchema implementation of terraform.ResourceProvider interface +func (p *Provider) GetSchema(req *terraform.ProviderSchemaRequest) (*terraform.ProviderSchema, error) { + resourceTypes := map[string]*configschema.Block{} + dataSources := map[string]*configschema.Block{} + + for _, name := range req.ResourceTypes { + if r, exists := p.ResourcesMap[name]; exists { + resourceTypes[name] = r.CoreConfigSchema() + } + } + for _, name := range req.DataSources { + if r, exists := p.DataSourcesMap[name]; exists { + dataSources[name] = r.CoreConfigSchema() + } + } + + return &terraform.ProviderSchema{ + Provider: schemaMap(p.Schema).CoreConfigSchema(), + ResourceTypes: resourceTypes, + DataSources: dataSources, + }, nil +} + +// Input implementation of terraform.ResourceProvider interface. +func (p *Provider) Input( + input terraform.UIInput, + c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) { + return schemaMap(p.Schema).Input(input, c) +} + +// Validate implementation of terraform.ResourceProvider interface. +func (p *Provider) Validate(c *terraform.ResourceConfig) ([]string, []error) { + if err := p.InternalValidate(); err != nil { + return nil, []error{fmt.Errorf( + "Internal validation of the provider failed! This is always a bug\n"+ + "with the provider itself, and not a user issue. Please report\n"+ + "this bug:\n\n%s", err)} + } + + return schemaMap(p.Schema).Validate(c) +} + +// ValidateResource implementation of terraform.ResourceProvider interface. +func (p *Provider) ValidateResource( + t string, c *terraform.ResourceConfig) ([]string, []error) { + r, ok := p.ResourcesMap[t] + if !ok { + return nil, []error{fmt.Errorf( + "Provider doesn't support resource: %s", t)} + } + + return r.Validate(c) +} + +// Configure implementation of terraform.ResourceProvider interface. +func (p *Provider) Configure(c *terraform.ResourceConfig) error { + // No configuration + if p.ConfigureFunc == nil { + return nil + } + + sm := schemaMap(p.Schema) + + // Get a ResourceData for this configuration. To do this, we actually + // generate an intermediary "diff" although that is never exposed. + diff, err := sm.Diff(nil, c, nil, p.meta, true) + if err != nil { + return err + } + + data, err := sm.Data(nil, diff) + if err != nil { + return err + } + + meta, err := p.ConfigureFunc(data) + if err != nil { + return err + } + + p.meta = meta + return nil +} + +// Apply implementation of terraform.ResourceProvider interface. +func (p *Provider) Apply( + info *terraform.InstanceInfo, + s *terraform.InstanceState, + d *terraform.InstanceDiff) (*terraform.InstanceState, error) { + r, ok := p.ResourcesMap[info.Type] + if !ok { + return nil, fmt.Errorf("unknown resource type: %s", info.Type) + } + + return r.Apply(s, d, p.meta) +} + +// Diff implementation of terraform.ResourceProvider interface. +func (p *Provider) Diff( + info *terraform.InstanceInfo, + s *terraform.InstanceState, + c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { + r, ok := p.ResourcesMap[info.Type] + if !ok { + return nil, fmt.Errorf("unknown resource type: %s", info.Type) + } + + return r.Diff(s, c, p.meta) +} + +// SimpleDiff is used by the new protocol wrappers to get a diff that doesn't +// attempt to calculate ignore_changes. +func (p *Provider) SimpleDiff( + info *terraform.InstanceInfo, + s *terraform.InstanceState, + c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { + r, ok := p.ResourcesMap[info.Type] + if !ok { + return nil, fmt.Errorf("unknown resource type: %s", info.Type) + } + + return r.simpleDiff(s, c, p.meta) +} + +// Refresh implementation of terraform.ResourceProvider interface. +func (p *Provider) Refresh( + info *terraform.InstanceInfo, + s *terraform.InstanceState) (*terraform.InstanceState, error) { + r, ok := p.ResourcesMap[info.Type] + if !ok { + return nil, fmt.Errorf("unknown resource type: %s", info.Type) + } + + return r.Refresh(s, p.meta) +} + +// Resources implementation of terraform.ResourceProvider interface. +func (p *Provider) Resources() []terraform.ResourceType { + keys := make([]string, 0, len(p.ResourcesMap)) + for k := range p.ResourcesMap { + keys = append(keys, k) + } + sort.Strings(keys) + + result := make([]terraform.ResourceType, 0, len(keys)) + for _, k := range keys { + resource := p.ResourcesMap[k] + + // This isn't really possible (it'd fail InternalValidate), but + // we do it anyways to avoid a panic. + if resource == nil { + resource = &Resource{} + } + + result = append(result, terraform.ResourceType{ + Name: k, + Importable: resource.Importer != nil, + + // Indicates that a provider is compiled against a new enough + // version of core to support the GetSchema method. + SchemaAvailable: true, + }) + } + + return result +} + +func (p *Provider) ImportState( + info *terraform.InstanceInfo, + id string) ([]*terraform.InstanceState, error) { + // Find the resource + r, ok := p.ResourcesMap[info.Type] + if !ok { + return nil, fmt.Errorf("unknown resource type: %s", info.Type) + } + + // If it doesn't support import, error + if r.Importer == nil { + return nil, fmt.Errorf("resource %s doesn't support import", info.Type) + } + + // Create the data + data := r.Data(nil) + data.SetId(id) + data.SetType(info.Type) + + // Call the import function + results := []*ResourceData{data} + if r.Importer.State != nil { + var err error + results, err = r.Importer.State(data, p.meta) + if err != nil { + return nil, err + } + } + + // Convert the results to InstanceState values and return it + states := make([]*terraform.InstanceState, len(results)) + for i, r := range results { + states[i] = r.State() + } + + // Verify that all are non-nil. If there are any nil the error + // isn't obvious so we circumvent that with a friendlier error. + for _, s := range states { + if s == nil { + return nil, fmt.Errorf( + "nil entry in ImportState results. This is always a bug with\n" + + "the resource that is being imported. Please report this as\n" + + "a bug to Terraform.") + } + } + + return states, nil +} + +// ValidateDataSource implementation of terraform.ResourceProvider interface. +func (p *Provider) ValidateDataSource( + t string, c *terraform.ResourceConfig) ([]string, []error) { + r, ok := p.DataSourcesMap[t] + if !ok { + return nil, []error{fmt.Errorf( + "Provider doesn't support data source: %s", t)} + } + + return r.Validate(c) +} + +// ReadDataDiff implementation of terraform.ResourceProvider interface. +func (p *Provider) ReadDataDiff( + info *terraform.InstanceInfo, + c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { + + r, ok := p.DataSourcesMap[info.Type] + if !ok { + return nil, fmt.Errorf("unknown data source: %s", info.Type) + } + + return r.Diff(nil, c, p.meta) +} + +// RefreshData implementation of terraform.ResourceProvider interface. +func (p *Provider) ReadDataApply( + info *terraform.InstanceInfo, + d *terraform.InstanceDiff) (*terraform.InstanceState, error) { + + r, ok := p.DataSourcesMap[info.Type] + if !ok { + return nil, fmt.Errorf("unknown data source: %s", info.Type) + } + + return r.ReadDataApply(d, p.meta) +} + +// DataSources implementation of terraform.ResourceProvider interface. +func (p *Provider) DataSources() []terraform.DataSource { + keys := make([]string, 0, len(p.DataSourcesMap)) + for k, _ := range p.DataSourcesMap { + keys = append(keys, k) + } + sort.Strings(keys) + + result := make([]terraform.DataSource, 0, len(keys)) + for _, k := range keys { + result = append(result, terraform.DataSource{ + Name: k, + + // Indicates that a provider is compiled against a new enough + // version of core to support the GetSchema method. + SchemaAvailable: true, + }) + } + + return result +} diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/provider_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/provider_test.go new file mode 100644 index 00000000..3f3eff4e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/provider_test.go @@ -0,0 +1,620 @@ +package schema + +import ( + "fmt" + "reflect" + "strings" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/zclconf/go-cty/cty" + + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/internal/legacy/terraform" +) + +func TestProvider_impl(t *testing.T) { + var _ terraform.ResourceProvider = new(Provider) +} + +func TestProviderGetSchema(t *testing.T) { + // This functionality is already broadly tested in core_schema_test.go, + // so this is just to ensure that the call passes through correctly. + p := &Provider{ + Schema: map[string]*Schema{ + "bar": { + Type: TypeString, + Required: true, + }, + }, + ResourcesMap: map[string]*Resource{ + "foo": &Resource{ + Schema: map[string]*Schema{ + "bar": { + Type: TypeString, + Required: true, + }, + }, + }, + }, + DataSourcesMap: map[string]*Resource{ + "baz": &Resource{ + Schema: map[string]*Schema{ + "bur": { + Type: TypeString, + Required: true, + }, + }, + }, + }, + } + + want := &terraform.ProviderSchema{ + Provider: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bar": &configschema.Attribute{ + Type: cty.String, + Required: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{}, + }, + ResourceTypes: map[string]*configschema.Block{ + "foo": testResource(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bar": &configschema.Attribute{ + Type: cty.String, + Required: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{}, + }), + }, + DataSources: map[string]*configschema.Block{ + "baz": testResource(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bur": &configschema.Attribute{ + Type: cty.String, + Required: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{}, + }), + }, + } + got, err := p.GetSchema(&terraform.ProviderSchemaRequest{ + ResourceTypes: []string{"foo", "bar"}, + DataSources: []string{"baz", "bar"}, + }) + if err != nil { + t.Fatalf("unexpected error %s", err) + } + + if !cmp.Equal(got, want, equateEmpty, typeComparer) { + t.Error("wrong result:\n", cmp.Diff(got, want, equateEmpty, typeComparer)) + } +} + +func TestProviderConfigure(t *testing.T) { + cases := []struct { + P *Provider + Config map[string]interface{} + Err bool + }{ + { + P: &Provider{}, + Config: nil, + Err: false, + }, + + { + P: &Provider{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + + ConfigureFunc: func(d *ResourceData) (interface{}, error) { + if d.Get("foo").(int) == 42 { + return nil, nil + } + + return nil, fmt.Errorf("nope") + }, + }, + Config: map[string]interface{}{ + "foo": 42, + }, + Err: false, + }, + + { + P: &Provider{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + + ConfigureFunc: func(d *ResourceData) (interface{}, error) { + if d.Get("foo").(int) == 42 { + return nil, nil + } + + return nil, fmt.Errorf("nope") + }, + }, + Config: map[string]interface{}{ + "foo": 52, + }, + Err: true, + }, + } + + for i, tc := range cases { + c := terraform.NewResourceConfigRaw(tc.Config) + err := tc.P.Configure(c) + if err != nil != tc.Err { + t.Fatalf("%d: %s", i, err) + } + } +} + +func TestProviderResources(t *testing.T) { + cases := []struct { + P *Provider + Result []terraform.ResourceType + }{ + { + P: &Provider{}, + Result: []terraform.ResourceType{}, + }, + + { + P: &Provider{ + ResourcesMap: map[string]*Resource{ + "foo": nil, + "bar": nil, + }, + }, + Result: []terraform.ResourceType{ + terraform.ResourceType{Name: "bar", SchemaAvailable: true}, + terraform.ResourceType{Name: "foo", SchemaAvailable: true}, + }, + }, + + { + P: &Provider{ + ResourcesMap: map[string]*Resource{ + "foo": nil, + "bar": &Resource{Importer: &ResourceImporter{}}, + "baz": nil, + }, + }, + Result: []terraform.ResourceType{ + terraform.ResourceType{Name: "bar", Importable: true, SchemaAvailable: true}, + terraform.ResourceType{Name: "baz", SchemaAvailable: true}, + terraform.ResourceType{Name: "foo", SchemaAvailable: true}, + }, + }, + } + + for i, tc := range cases { + actual := tc.P.Resources() + if !reflect.DeepEqual(actual, tc.Result) { + t.Fatalf("%d: %#v", i, actual) + } + } +} + +func TestProviderDataSources(t *testing.T) { + cases := []struct { + P *Provider + Result []terraform.DataSource + }{ + { + P: &Provider{}, + Result: []terraform.DataSource{}, + }, + + { + P: &Provider{ + DataSourcesMap: map[string]*Resource{ + "foo": nil, + "bar": nil, + }, + }, + Result: []terraform.DataSource{ + terraform.DataSource{Name: "bar", SchemaAvailable: true}, + terraform.DataSource{Name: "foo", SchemaAvailable: true}, + }, + }, + } + + for i, tc := range cases { + actual := tc.P.DataSources() + if !reflect.DeepEqual(actual, tc.Result) { + t.Fatalf("%d: got %#v; want %#v", i, actual, tc.Result) + } + } +} + +func TestProviderValidate(t *testing.T) { + cases := []struct { + P *Provider + Config map[string]interface{} + Err bool + }{ + { + P: &Provider{ + Schema: map[string]*Schema{ + "foo": &Schema{}, + }, + }, + Config: nil, + Err: true, + }, + } + + for i, tc := range cases { + c := terraform.NewResourceConfigRaw(tc.Config) + _, es := tc.P.Validate(c) + if len(es) > 0 != tc.Err { + t.Fatalf("%d: %#v", i, es) + } + } +} + +func TestProviderDiff_legacyTimeoutType(t *testing.T) { + p := &Provider{ + ResourcesMap: map[string]*Resource{ + "blah": &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + Timeouts: &ResourceTimeout{ + Create: DefaultTimeout(10 * time.Minute), + }, + }, + }, + } + + invalidCfg := map[string]interface{}{ + "foo": 42, + "timeouts": []interface{}{ + map[string]interface{}{ + "create": "40m", + }, + }, + } + ic := terraform.NewResourceConfigRaw(invalidCfg) + _, err := p.Diff( + &terraform.InstanceInfo{ + Type: "blah", + }, + nil, + ic, + ) + if err != nil { + t.Fatal(err) + } +} + +func TestProviderDiff_timeoutInvalidValue(t *testing.T) { + p := &Provider{ + ResourcesMap: map[string]*Resource{ + "blah": &Resource{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeInt, + Optional: true, + }, + }, + Timeouts: &ResourceTimeout{ + Create: DefaultTimeout(10 * time.Minute), + }, + }, + }, + } + + invalidCfg := map[string]interface{}{ + "foo": 42, + "timeouts": map[string]interface{}{ + "create": "invalid", + }, + } + ic := terraform.NewResourceConfigRaw(invalidCfg) + _, err := p.Diff( + &terraform.InstanceInfo{ + Type: "blah", + }, + nil, + ic, + ) + if err == nil { + t.Fatal("Expected provider.Diff to fail with invalid timeout value") + } + expectedErrMsg := `time: invalid duration "invalid"` + if !strings.Contains(err.Error(), expectedErrMsg) { + t.Fatalf("Unexpected error message: %q\nExpected message to contain %q", + err.Error(), + expectedErrMsg) + } +} + +func TestProviderValidateResource(t *testing.T) { + cases := []struct { + P *Provider + Type string + Config map[string]interface{} + Err bool + }{ + { + P: &Provider{}, + Type: "foo", + Config: nil, + Err: true, + }, + + { + P: &Provider{ + ResourcesMap: map[string]*Resource{ + "foo": &Resource{}, + }, + }, + Type: "foo", + Config: nil, + Err: false, + }, + } + + for i, tc := range cases { + c := terraform.NewResourceConfigRaw(tc.Config) + _, es := tc.P.ValidateResource(tc.Type, c) + if len(es) > 0 != tc.Err { + t.Fatalf("%d: %#v", i, es) + } + } +} + +func TestProviderImportState_default(t *testing.T) { + p := &Provider{ + ResourcesMap: map[string]*Resource{ + "foo": &Resource{ + Importer: &ResourceImporter{}, + }, + }, + } + + states, err := p.ImportState(&terraform.InstanceInfo{ + Type: "foo", + }, "bar") + if err != nil { + t.Fatalf("err: %s", err) + } + + if len(states) != 1 { + t.Fatalf("bad: %#v", states) + } + if states[0].ID != "bar" { + t.Fatalf("bad: %#v", states) + } +} + +func TestProviderImportState_setsId(t *testing.T) { + var val string + stateFunc := func(d *ResourceData, meta interface{}) ([]*ResourceData, error) { + val = d.Id() + return []*ResourceData{d}, nil + } + + p := &Provider{ + ResourcesMap: map[string]*Resource{ + "foo": &Resource{ + Importer: &ResourceImporter{ + State: stateFunc, + }, + }, + }, + } + + _, err := p.ImportState(&terraform.InstanceInfo{ + Type: "foo", + }, "bar") + if err != nil { + t.Fatalf("err: %s", err) + } + + if val != "bar" { + t.Fatal("should set id") + } +} + +func TestProviderImportState_setsType(t *testing.T) { + var tVal string + stateFunc := func(d *ResourceData, meta interface{}) ([]*ResourceData, error) { + d.SetId("foo") + tVal = d.State().Ephemeral.Type + return []*ResourceData{d}, nil + } + + p := &Provider{ + ResourcesMap: map[string]*Resource{ + "foo": &Resource{ + Importer: &ResourceImporter{ + State: stateFunc, + }, + }, + }, + } + + _, err := p.ImportState(&terraform.InstanceInfo{ + Type: "foo", + }, "bar") + if err != nil { + t.Fatalf("err: %s", err) + } + + if tVal != "foo" { + t.Fatal("should set type") + } +} + +func TestProviderMeta(t *testing.T) { + p := new(Provider) + if v := p.Meta(); v != nil { + t.Fatalf("bad: %#v", v) + } + + expected := 42 + p.SetMeta(42) + if v := p.Meta(); !reflect.DeepEqual(v, expected) { + t.Fatalf("bad: %#v", v) + } +} + +func TestProviderStop(t *testing.T) { + var p Provider + + if p.Stopped() { + t.Fatal("should not be stopped") + } + + // Verify stopch blocks + ch := p.StopContext().Done() + select { + case <-ch: + t.Fatal("should not be stopped") + case <-time.After(10 * time.Millisecond): + } + + // Stop it + if err := p.Stop(); err != nil { + t.Fatalf("err: %s", err) + } + + // Verify + if !p.Stopped() { + t.Fatal("should be stopped") + } + + select { + case <-ch: + case <-time.After(10 * time.Millisecond): + t.Fatal("should be stopped") + } +} + +func TestProviderStop_stopFirst(t *testing.T) { + var p Provider + + // Stop it + if err := p.Stop(); err != nil { + t.Fatalf("err: %s", err) + } + + // Verify + if !p.Stopped() { + t.Fatal("should be stopped") + } + + select { + case <-p.StopContext().Done(): + case <-time.After(10 * time.Millisecond): + t.Fatal("should be stopped") + } +} + +func TestProviderReset(t *testing.T) { + var p Provider + stopCtx := p.StopContext() + p.MetaReset = func() error { + stopCtx = p.StopContext() + return nil + } + + // cancel the current context + p.Stop() + + if err := p.TestReset(); err != nil { + t.Fatal(err) + } + + // the first context should have been replaced + if err := stopCtx.Err(); err != nil { + t.Fatal(err) + } + + // we should not get a canceled context here either + if err := p.StopContext().Err(); err != nil { + t.Fatal(err) + } +} + +func TestProvider_InternalValidate(t *testing.T) { + cases := []struct { + P *Provider + ExpectedErr error + }{ + { + P: &Provider{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeBool, + Optional: true, + }, + }, + }, + ExpectedErr: nil, + }, + { // Reserved resource fields should be allowed in provider block + P: &Provider{ + Schema: map[string]*Schema{ + "provisioner": { + Type: TypeString, + Optional: true, + }, + "count": { + Type: TypeInt, + Optional: true, + }, + }, + }, + ExpectedErr: nil, + }, + { // Reserved provider fields should not be allowed + P: &Provider{ + Schema: map[string]*Schema{ + "alias": { + Type: TypeString, + Optional: true, + }, + }, + }, + ExpectedErr: fmt.Errorf("%s is a reserved field name for a provider", "alias"), + }, + } + + for i, tc := range cases { + err := tc.P.InternalValidate() + if tc.ExpectedErr == nil { + if err != nil { + t.Fatalf("%d: Error returned (expected no error): %s", i, err) + } + continue + } + if tc.ExpectedErr != nil && err == nil { + t.Fatalf("%d: Expected error (%s), but no error returned", i, tc.ExpectedErr) + } + if err.Error() != tc.ExpectedErr.Error() { + t.Fatalf("%d: Errors don't match. Expected: %#v Given: %#v", i, tc.ExpectedErr, err) + } + } +} diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/provisioner.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/provisioner.go new file mode 100644 index 00000000..d0ee581b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/provisioner.go @@ -0,0 +1,205 @@ +package schema + +import ( + "context" + "errors" + "fmt" + "sync" + + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/internal/legacy/terraform" +) + +// Provisioner represents a resource provisioner in Terraform and properly +// implements all of the ResourceProvisioner API. +// +// This higher level structure makes it much easier to implement a new or +// custom provisioner for Terraform. +// +// The function callbacks for this structure are all passed a context object. +// This context object has a number of pre-defined values that can be accessed +// via the global functions defined in context.go. +type Provisioner struct { + // ConnSchema is the schema for the connection settings for this + // provisioner. + // + // The keys of this map are the configuration keys, and the value is + // the schema describing the value of the configuration. + // + // NOTE: The value of connection keys can only be strings for now. + ConnSchema map[string]*Schema + + // Schema is the schema for the usage of this provisioner. + // + // The keys of this map are the configuration keys, and the value is + // the schema describing the value of the configuration. + Schema map[string]*Schema + + // ApplyFunc is the function for executing the provisioner. This is required. + // It is given a context. See the Provisioner struct docs for more + // information. + ApplyFunc func(ctx context.Context) error + + // ValidateFunc is a function for extended validation. This is optional + // and should be used when individual field validation is not enough. + ValidateFunc func(*terraform.ResourceConfig) ([]string, []error) + + stopCtx context.Context + stopCtxCancel context.CancelFunc + stopOnce sync.Once +} + +// Keys that can be used to access data in the context parameters for +// Provisioners. +var ( + connDataInvalid = contextKey("data invalid") + + // This returns a *ResourceData for the connection information. + // Guaranteed to never be nil. + ProvConnDataKey = contextKey("provider conn data") + + // This returns a *ResourceData for the config information. + // Guaranteed to never be nil. + ProvConfigDataKey = contextKey("provider config data") + + // This returns a terraform.UIOutput. Guaranteed to never be nil. + ProvOutputKey = contextKey("provider output") + + // This returns the raw InstanceState passed to Apply. Guaranteed to + // be set, but may be nil. + ProvRawStateKey = contextKey("provider raw state") +) + +// InternalValidate should be called to validate the structure +// of the provisioner. +// +// This should be called in a unit test to verify before release that this +// structure is properly configured for use. +func (p *Provisioner) InternalValidate() error { + if p == nil { + return errors.New("provisioner is nil") + } + + var validationErrors error + { + sm := schemaMap(p.ConnSchema) + if err := sm.InternalValidate(sm); err != nil { + validationErrors = multierror.Append(validationErrors, err) + } + } + + { + sm := schemaMap(p.Schema) + if err := sm.InternalValidate(sm); err != nil { + validationErrors = multierror.Append(validationErrors, err) + } + } + + if p.ApplyFunc == nil { + validationErrors = multierror.Append(validationErrors, fmt.Errorf( + "ApplyFunc must not be nil")) + } + + return validationErrors +} + +// StopContext returns a context that checks whether a provisioner is stopped. +func (p *Provisioner) StopContext() context.Context { + p.stopOnce.Do(p.stopInit) + return p.stopCtx +} + +func (p *Provisioner) stopInit() { + p.stopCtx, p.stopCtxCancel = context.WithCancel(context.Background()) +} + +// Stop implementation of terraform.ResourceProvisioner interface. +func (p *Provisioner) Stop() error { + p.stopOnce.Do(p.stopInit) + p.stopCtxCancel() + return nil +} + +// GetConfigSchema implementation of terraform.ResourceProvisioner interface. +func (p *Provisioner) GetConfigSchema() (*configschema.Block, error) { + return schemaMap(p.Schema).CoreConfigSchema(), nil +} + +// Apply implementation of terraform.ResourceProvisioner interface. +func (p *Provisioner) Apply( + o terraform.UIOutput, + s *terraform.InstanceState, + c *terraform.ResourceConfig) error { + var connData, configData *ResourceData + + { + // We first need to turn the connection information into a + // terraform.ResourceConfig so that we can use that type to more + // easily build a ResourceData structure. We do this by simply treating + // the conn info as configuration input. + raw := make(map[string]interface{}) + if s != nil { + for k, v := range s.Ephemeral.ConnInfo { + raw[k] = v + } + } + + c := terraform.NewResourceConfigRaw(raw) + sm := schemaMap(p.ConnSchema) + diff, err := sm.Diff(nil, c, nil, nil, true) + if err != nil { + return err + } + connData, err = sm.Data(nil, diff) + if err != nil { + return err + } + } + + { + // Build the configuration data. Doing this requires making a "diff" + // even though that's never used. We use that just to get the correct types. + configMap := schemaMap(p.Schema) + diff, err := configMap.Diff(nil, c, nil, nil, true) + if err != nil { + return err + } + configData, err = configMap.Data(nil, diff) + if err != nil { + return err + } + } + + // Build the context and call the function + ctx := p.StopContext() + ctx = context.WithValue(ctx, ProvConnDataKey, connData) + ctx = context.WithValue(ctx, ProvConfigDataKey, configData) + ctx = context.WithValue(ctx, ProvOutputKey, o) + ctx = context.WithValue(ctx, ProvRawStateKey, s) + return p.ApplyFunc(ctx) +} + +// Validate implements the terraform.ResourceProvisioner interface. +func (p *Provisioner) Validate(c *terraform.ResourceConfig) (ws []string, es []error) { + if err := p.InternalValidate(); err != nil { + return nil, []error{fmt.Errorf( + "Internal validation of the provisioner failed! This is always a bug\n"+ + "with the provisioner itself, and not a user issue. Please report\n"+ + "this bug:\n\n%s", err)} + } + + if p.Schema != nil { + w, e := schemaMap(p.Schema).Validate(c) + ws = append(ws, w...) + es = append(es, e...) + } + + if p.ValidateFunc != nil { + w, e := p.ValidateFunc(c) + ws = append(ws, w...) + es = append(es, e...) + } + + return ws, es +} diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/provisioner_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/provisioner_test.go new file mode 100644 index 00000000..228dacd7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/provisioner_test.go @@ -0,0 +1,334 @@ +package schema + +import ( + "context" + "fmt" + "reflect" + "testing" + "time" + + "github.com/hashicorp/terraform/internal/legacy/terraform" +) + +func TestProvisioner_impl(t *testing.T) { + var _ terraform.ResourceProvisioner = new(Provisioner) +} + +func noopApply(ctx context.Context) error { + return nil +} + +func TestProvisionerValidate(t *testing.T) { + cases := []struct { + Name string + P *Provisioner + Config map[string]interface{} + Err bool + Warns []string + }{ + { + Name: "No ApplyFunc", + P: &Provisioner{}, + Config: nil, + Err: true, + }, + { + Name: "Incorrect schema", + P: &Provisioner{ + Schema: map[string]*Schema{ + "foo": {}, + }, + ApplyFunc: noopApply, + }, + Config: nil, + Err: true, + }, + { + "Basic required field", + &Provisioner{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Required: true, + Type: TypeString, + }, + }, + ApplyFunc: noopApply, + }, + nil, + true, + nil, + }, + + { + "Basic required field set", + &Provisioner{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Required: true, + Type: TypeString, + }, + }, + ApplyFunc: noopApply, + }, + map[string]interface{}{ + "foo": "bar", + }, + false, + nil, + }, + { + Name: "Warning from property validation", + P: &Provisioner{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + ws = append(ws, "Simple warning from property validation") + return + }, + }, + }, + ApplyFunc: noopApply, + }, + Config: map[string]interface{}{ + "foo": "", + }, + Err: false, + Warns: []string{"Simple warning from property validation"}, + }, + { + Name: "No schema", + P: &Provisioner{ + Schema: nil, + ApplyFunc: noopApply, + }, + Config: nil, + Err: false, + }, + { + Name: "Warning from provisioner ValidateFunc", + P: &Provisioner{ + Schema: nil, + ApplyFunc: noopApply, + ValidateFunc: func(*terraform.ResourceConfig) (ws []string, errors []error) { + ws = append(ws, "Simple warning from provisioner ValidateFunc") + return + }, + }, + Config: nil, + Err: false, + Warns: []string{"Simple warning from provisioner ValidateFunc"}, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + c := terraform.NewResourceConfigRaw(tc.Config) + ws, es := tc.P.Validate(c) + if len(es) > 0 != tc.Err { + t.Fatalf("%d: %#v %s", i, es, es) + } + if (tc.Warns != nil || len(ws) != 0) && !reflect.DeepEqual(ws, tc.Warns) { + t.Fatalf("%d: warnings mismatch, actual: %#v", i, ws) + } + }) + } +} + +func TestProvisionerApply(t *testing.T) { + cases := []struct { + Name string + P *Provisioner + Conn map[string]string + Config map[string]interface{} + Err bool + }{ + { + "Basic config", + &Provisioner{ + ConnSchema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + + ApplyFunc: func(ctx context.Context) error { + cd := ctx.Value(ProvConnDataKey).(*ResourceData) + d := ctx.Value(ProvConfigDataKey).(*ResourceData) + if d.Get("foo").(int) != 42 { + return fmt.Errorf("bad config data") + } + if cd.Get("foo").(string) != "bar" { + return fmt.Errorf("bad conn data") + } + + return nil + }, + }, + map[string]string{ + "foo": "bar", + }, + map[string]interface{}{ + "foo": 42, + }, + false, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + c := terraform.NewResourceConfigRaw(tc.Config) + + state := &terraform.InstanceState{ + Ephemeral: terraform.EphemeralState{ + ConnInfo: tc.Conn, + }, + } + + err := tc.P.Apply(nil, state, c) + if err != nil != tc.Err { + t.Fatalf("%d: %s", i, err) + } + }) + } +} + +func TestProvisionerApply_nilState(t *testing.T) { + p := &Provisioner{ + ConnSchema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + + ApplyFunc: func(ctx context.Context) error { + return nil + }, + } + + conf := map[string]interface{}{ + "foo": 42, + } + + c := terraform.NewResourceConfigRaw(conf) + err := p.Apply(nil, nil, c) + if err != nil { + t.Fatalf("err: %s", err) + } +} + +func TestProvisionerStop(t *testing.T) { + var p Provisioner + + // Verify stopch blocks + ch := p.StopContext().Done() + select { + case <-ch: + t.Fatal("should not be stopped") + case <-time.After(10 * time.Millisecond): + } + + // Stop it + if err := p.Stop(); err != nil { + t.Fatalf("err: %s", err) + } + + select { + case <-ch: + case <-time.After(10 * time.Millisecond): + t.Fatal("should be stopped") + } +} + +func TestProvisionerStop_apply(t *testing.T) { + p := &Provisioner{ + ConnSchema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + + ApplyFunc: func(ctx context.Context) error { + <-ctx.Done() + return nil + }, + } + + conn := map[string]string{ + "foo": "bar", + } + + conf := map[string]interface{}{ + "foo": 42, + } + + c := terraform.NewResourceConfigRaw(conf) + state := &terraform.InstanceState{ + Ephemeral: terraform.EphemeralState{ + ConnInfo: conn, + }, + } + + // Run the apply in a goroutine + doneCh := make(chan struct{}) + go func() { + p.Apply(nil, state, c) + close(doneCh) + }() + + // Should block + select { + case <-doneCh: + t.Fatal("should not be done") + case <-time.After(10 * time.Millisecond): + } + + // Stop! + p.Stop() + + select { + case <-doneCh: + case <-time.After(10 * time.Millisecond): + t.Fatal("should be done") + } +} + +func TestProvisionerStop_stopFirst(t *testing.T) { + var p Provisioner + + // Stop it + if err := p.Stop(); err != nil { + t.Fatalf("err: %s", err) + } + + select { + case <-p.StopContext().Done(): + case <-time.After(10 * time.Millisecond): + t.Fatal("should be stopped") + } +} diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource.go new file mode 100644 index 00000000..28fa54e3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource.go @@ -0,0 +1,842 @@ +package schema + +import ( + "errors" + "fmt" + "log" + "strconv" + + "github.com/hashicorp/terraform/internal/legacy/terraform" + "github.com/zclconf/go-cty/cty" +) + +var ReservedDataSourceFields = []string{ + "connection", + "count", + "depends_on", + "lifecycle", + "provider", + "provisioner", +} + +var ReservedResourceFields = []string{ + "connection", + "count", + "depends_on", + "id", + "lifecycle", + "provider", + "provisioner", +} + +// Resource represents a thing in Terraform that has a set of configurable +// attributes and a lifecycle (create, read, update, delete). +// +// The Resource schema is an abstraction that allows provider writers to +// worry only about CRUD operations while off-loading validation, diff +// generation, etc. to this higher level library. +// +// In spite of the name, this struct is not used only for terraform resources, +// but also for data sources. In the case of data sources, the Create, +// Update and Delete functions must not be provided. +type Resource struct { + // Schema is the schema for the configuration of this resource. + // + // The keys of this map are the configuration keys, and the values + // describe the schema of the configuration value. + // + // The schema is used to represent both configurable data as well + // as data that might be computed in the process of creating this + // resource. + Schema map[string]*Schema + + // SchemaVersion is the version number for this resource's Schema + // definition. The current SchemaVersion stored in the state for each + // resource. Provider authors can increment this version number + // when Schema semantics change. If the State's SchemaVersion is less than + // the current SchemaVersion, the InstanceState is yielded to the + // MigrateState callback, where the provider can make whatever changes it + // needs to update the state to be compatible to the latest version of the + // Schema. + // + // When unset, SchemaVersion defaults to 0, so provider authors can start + // their Versioning at any integer >= 1 + SchemaVersion int + + // MigrateState is deprecated and any new changes to a resource's schema + // should be handled by StateUpgraders. Existing MigrateState implementations + // should remain for compatibility with existing state. MigrateState will + // still be called if the stored SchemaVersion is less than the + // first version of the StateUpgraders. + // + // MigrateState is responsible for updating an InstanceState with an old + // version to the format expected by the current version of the Schema. + // + // It is called during Refresh if the State's stored SchemaVersion is less + // than the current SchemaVersion of the Resource. + // + // The function is yielded the state's stored SchemaVersion and a pointer to + // the InstanceState that needs updating, as well as the configured + // provider's configured meta interface{}, in case the migration process + // needs to make any remote API calls. + MigrateState StateMigrateFunc + + // StateUpgraders contains the functions responsible for upgrading an + // existing state with an old schema version to a newer schema. It is + // called specifically by Terraform when the stored schema version is less + // than the current SchemaVersion of the Resource. + // + // StateUpgraders map specific schema versions to a StateUpgrader + // function. The registered versions are expected to be ordered, + // consecutive values. The initial value may be greater than 0 to account + // for legacy schemas that weren't recorded and can be handled by + // MigrateState. + StateUpgraders []StateUpgrader + + // The functions below are the CRUD operations for this resource. + // + // The only optional operation is Update. If Update is not implemented, + // then updates will not be supported for this resource. + // + // The ResourceData parameter in the functions below are used to + // query configuration and changes for the resource as well as to set + // the ID, computed data, etc. + // + // The interface{} parameter is the result of the ConfigureFunc in + // the provider for this resource. If the provider does not define + // a ConfigureFunc, this will be nil. This parameter should be used + // to store API clients, configuration structures, etc. + // + // If any errors occur during each of the operation, an error should be + // returned. If a resource was partially updated, be careful to enable + // partial state mode for ResourceData and use it accordingly. + // + // Exists is a function that is called to check if a resource still + // exists. If this returns false, then this will affect the diff + // accordingly. If this function isn't set, it will not be called. You + // can also signal existence in the Read method by calling d.SetId("") + // if the Resource is no longer present and should be removed from state. + // The *ResourceData passed to Exists should _not_ be modified. + Create CreateFunc + Read ReadFunc + Update UpdateFunc + Delete DeleteFunc + Exists ExistsFunc + + // CustomizeDiff is a custom function for working with the diff that + // Terraform has created for this resource - it can be used to customize the + // diff that has been created, diff values not controlled by configuration, + // or even veto the diff altogether and abort the plan. It is passed a + // *ResourceDiff, a structure similar to ResourceData but lacking most write + // functions like Set, while introducing new functions that work with the + // diff such as SetNew, SetNewComputed, and ForceNew. + // + // The phases Terraform runs this in, and the state available via functions + // like Get and GetChange, are as follows: + // + // * New resource: One run with no state + // * Existing resource: One run with state + // * Existing resource, forced new: One run with state (before ForceNew), + // then one run without state (as if new resource) + // * Tainted resource: No runs (custom diff logic is skipped) + // * Destroy: No runs (standard diff logic is skipped on destroy diffs) + // + // This function needs to be resilient to support all scenarios. + // + // If this function needs to access external API resources, remember to flag + // the RequiresRefresh attribute mentioned below to ensure that + // -refresh=false is blocked when running plan or apply, as this means that + // this resource requires refresh-like behaviour to work effectively. + // + // For the most part, only computed fields can be customized by this + // function. + // + // This function is only allowed on regular resources (not data sources). + CustomizeDiff CustomizeDiffFunc + + // Importer is the ResourceImporter implementation for this resource. + // If this is nil, then this resource does not support importing. If + // this is non-nil, then it supports importing and ResourceImporter + // must be validated. The validity of ResourceImporter is verified + // by InternalValidate on Resource. + Importer *ResourceImporter + + // If non-empty, this string is emitted as a warning during Validate. + DeprecationMessage string + + // Timeouts allow users to specify specific time durations in which an + // operation should time out, to allow them to extend an action to suit their + // usage. For example, a user may specify a large Creation timeout for their + // AWS RDS Instance due to it's size, or restoring from a snapshot. + // Resource implementors must enable Timeout support by adding the allowed + // actions (Create, Read, Update, Delete, Default) to the Resource struct, and + // accessing them in the matching methods. + Timeouts *ResourceTimeout +} + +// ShimInstanceStateFromValue converts a cty.Value to a +// terraform.InstanceState. +func (r *Resource) ShimInstanceStateFromValue(state cty.Value) (*terraform.InstanceState, error) { + // Get the raw shimmed value. While this is correct, the set hashes don't + // match those from the Schema. + s := terraform.NewInstanceStateShimmedFromValue(state, r.SchemaVersion) + + // We now rebuild the state through the ResourceData, so that the set indexes + // match what helper/schema expects. + data, err := schemaMap(r.Schema).Data(s, nil) + if err != nil { + return nil, err + } + + s = data.State() + if s == nil { + s = &terraform.InstanceState{} + } + return s, nil +} + +// See Resource documentation. +type CreateFunc func(*ResourceData, interface{}) error + +// See Resource documentation. +type ReadFunc func(*ResourceData, interface{}) error + +// See Resource documentation. +type UpdateFunc func(*ResourceData, interface{}) error + +// See Resource documentation. +type DeleteFunc func(*ResourceData, interface{}) error + +// See Resource documentation. +type ExistsFunc func(*ResourceData, interface{}) (bool, error) + +// See Resource documentation. +type StateMigrateFunc func( + int, *terraform.InstanceState, interface{}) (*terraform.InstanceState, error) + +type StateUpgrader struct { + // Version is the version schema that this Upgrader will handle, converting + // it to Version+1. + Version int + + // Type describes the schema that this function can upgrade. Type is + // required to decode the schema if the state was stored in a legacy + // flatmap format. + Type cty.Type + + // Upgrade takes the JSON encoded state and the provider meta value, and + // upgrades the state one single schema version. The provided state is + // deocded into the default json types using a map[string]interface{}. It + // is up to the StateUpgradeFunc to ensure that the returned value can be + // encoded using the new schema. + Upgrade StateUpgradeFunc +} + +// See StateUpgrader +type StateUpgradeFunc func(rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) + +// See Resource documentation. +type CustomizeDiffFunc func(*ResourceDiff, interface{}) error + +// Apply creates, updates, and/or deletes a resource. +func (r *Resource) Apply( + s *terraform.InstanceState, + d *terraform.InstanceDiff, + meta interface{}) (*terraform.InstanceState, error) { + data, err := schemaMap(r.Schema).Data(s, d) + if err != nil { + return s, err + } + if s != nil && data != nil { + data.providerMeta = s.ProviderMeta + } + + // Instance Diff shoould have the timeout info, need to copy it over to the + // ResourceData meta + rt := ResourceTimeout{} + if _, ok := d.Meta[TimeoutKey]; ok { + if err := rt.DiffDecode(d); err != nil { + log.Printf("[ERR] Error decoding ResourceTimeout: %s", err) + } + } else if s != nil { + if _, ok := s.Meta[TimeoutKey]; ok { + if err := rt.StateDecode(s); err != nil { + log.Printf("[ERR] Error decoding ResourceTimeout: %s", err) + } + } + } else { + log.Printf("[DEBUG] No meta timeoutkey found in Apply()") + } + data.timeouts = &rt + + if s == nil { + // The Terraform API dictates that this should never happen, but + // it doesn't hurt to be safe in this case. + s = new(terraform.InstanceState) + } + + if d.Destroy || d.RequiresNew() { + if s.ID != "" { + // Destroy the resource since it is created + if err := r.Delete(data, meta); err != nil { + return r.recordCurrentSchemaVersion(data.State()), err + } + + // Make sure the ID is gone. + data.SetId("") + } + + // If we're only destroying, and not creating, then return + // now since we're done! + if !d.RequiresNew() { + return nil, nil + } + + // Reset the data to be stateless since we just destroyed + data, err = schemaMap(r.Schema).Data(nil, d) + // data was reset, need to re-apply the parsed timeouts + data.timeouts = &rt + if err != nil { + return nil, err + } + } + + err = nil + if data.Id() == "" { + // We're creating, it is a new resource. + data.MarkNewResource() + err = r.Create(data, meta) + } else { + if r.Update == nil { + return s, fmt.Errorf("doesn't support update") + } + + err = r.Update(data, meta) + } + + return r.recordCurrentSchemaVersion(data.State()), err +} + +// Diff returns a diff of this resource. +func (r *Resource) Diff( + s *terraform.InstanceState, + c *terraform.ResourceConfig, + meta interface{}) (*terraform.InstanceDiff, error) { + + t := &ResourceTimeout{} + err := t.ConfigDecode(r, c) + + if err != nil { + return nil, fmt.Errorf("[ERR] Error decoding timeout: %s", err) + } + + instanceDiff, err := schemaMap(r.Schema).Diff(s, c, r.CustomizeDiff, meta, true) + if err != nil { + return instanceDiff, err + } + + if instanceDiff != nil { + if err := t.DiffEncode(instanceDiff); err != nil { + log.Printf("[ERR] Error encoding timeout to instance diff: %s", err) + } + } else { + log.Printf("[DEBUG] Instance Diff is nil in Diff()") + } + + return instanceDiff, err +} + +func (r *Resource) simpleDiff( + s *terraform.InstanceState, + c *terraform.ResourceConfig, + meta interface{}) (*terraform.InstanceDiff, error) { + + instanceDiff, err := schemaMap(r.Schema).Diff(s, c, r.CustomizeDiff, meta, false) + if err != nil { + return instanceDiff, err + } + + if instanceDiff == nil { + instanceDiff = terraform.NewInstanceDiff() + } + + // Make sure the old value is set in each of the instance diffs. + // This was done by the RequiresNew logic in the full legacy Diff. + for k, attr := range instanceDiff.Attributes { + if attr == nil { + continue + } + if s != nil { + attr.Old = s.Attributes[k] + } + } + + return instanceDiff, nil +} + +// Validate validates the resource configuration against the schema. +func (r *Resource) Validate(c *terraform.ResourceConfig) ([]string, []error) { + warns, errs := schemaMap(r.Schema).Validate(c) + + if r.DeprecationMessage != "" { + warns = append(warns, r.DeprecationMessage) + } + + return warns, errs +} + +// ReadDataApply loads the data for a data source, given a diff that +// describes the configuration arguments and desired computed attributes. +func (r *Resource) ReadDataApply( + d *terraform.InstanceDiff, + meta interface{}, +) (*terraform.InstanceState, error) { + // Data sources are always built completely from scratch + // on each read, so the source state is always nil. + data, err := schemaMap(r.Schema).Data(nil, d) + if err != nil { + return nil, err + } + + err = r.Read(data, meta) + state := data.State() + if state != nil && state.ID == "" { + // Data sources can set an ID if they want, but they aren't + // required to; we'll provide a placeholder if they don't, + // to preserve the invariant that all resources have non-empty + // ids. + state.ID = "-" + } + + return r.recordCurrentSchemaVersion(state), err +} + +// RefreshWithoutUpgrade reads the instance state, but does not call +// MigrateState or the StateUpgraders, since those are now invoked in a +// separate API call. +// RefreshWithoutUpgrade is part of the new plugin shims. +func (r *Resource) RefreshWithoutUpgrade( + s *terraform.InstanceState, + meta interface{}) (*terraform.InstanceState, error) { + // If the ID is already somehow blank, it doesn't exist + if s.ID == "" { + return nil, nil + } + + rt := ResourceTimeout{} + if _, ok := s.Meta[TimeoutKey]; ok { + if err := rt.StateDecode(s); err != nil { + log.Printf("[ERR] Error decoding ResourceTimeout: %s", err) + } + } + + if r.Exists != nil { + // Make a copy of data so that if it is modified it doesn't + // affect our Read later. + data, err := schemaMap(r.Schema).Data(s, nil) + data.timeouts = &rt + + if err != nil { + return s, err + } + + if s != nil { + data.providerMeta = s.ProviderMeta + } + + exists, err := r.Exists(data, meta) + if err != nil { + return s, err + } + if !exists { + return nil, nil + } + } + + data, err := schemaMap(r.Schema).Data(s, nil) + data.timeouts = &rt + if err != nil { + return s, err + } + + if s != nil { + data.providerMeta = s.ProviderMeta + } + + err = r.Read(data, meta) + state := data.State() + if state != nil && state.ID == "" { + state = nil + } + + return r.recordCurrentSchemaVersion(state), err +} + +// Refresh refreshes the state of the resource. +func (r *Resource) Refresh( + s *terraform.InstanceState, + meta interface{}) (*terraform.InstanceState, error) { + // If the ID is already somehow blank, it doesn't exist + if s.ID == "" { + return nil, nil + } + + rt := ResourceTimeout{} + if _, ok := s.Meta[TimeoutKey]; ok { + if err := rt.StateDecode(s); err != nil { + log.Printf("[ERR] Error decoding ResourceTimeout: %s", err) + } + } + + if r.Exists != nil { + // Make a copy of data so that if it is modified it doesn't + // affect our Read later. + data, err := schemaMap(r.Schema).Data(s, nil) + data.timeouts = &rt + + if err != nil { + return s, err + } + + exists, err := r.Exists(data, meta) + if err != nil { + return s, err + } + if !exists { + return nil, nil + } + } + + // there may be new StateUpgraders that need to be run + s, err := r.upgradeState(s, meta) + if err != nil { + return s, err + } + + data, err := schemaMap(r.Schema).Data(s, nil) + data.timeouts = &rt + if err != nil { + return s, err + } + + err = r.Read(data, meta) + state := data.State() + if state != nil && state.ID == "" { + state = nil + } + + return r.recordCurrentSchemaVersion(state), err +} + +func (r *Resource) upgradeState(s *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) { + var err error + + needsMigration, stateSchemaVersion := r.checkSchemaVersion(s) + migrate := needsMigration && r.MigrateState != nil + + if migrate { + s, err = r.MigrateState(stateSchemaVersion, s, meta) + if err != nil { + return s, err + } + } + + if len(r.StateUpgraders) == 0 { + return s, nil + } + + // If we ran MigrateState, then the stateSchemaVersion value is no longer + // correct. We can expect the first upgrade function to be the correct + // schema type version. + if migrate { + stateSchemaVersion = r.StateUpgraders[0].Version + } + + schemaType := r.CoreConfigSchema().ImpliedType() + // find the expected type to convert the state + for _, upgrader := range r.StateUpgraders { + if stateSchemaVersion == upgrader.Version { + schemaType = upgrader.Type + } + } + + // StateUpgraders only operate on the new JSON format state, so the state + // need to be converted. + stateVal, err := StateValueFromInstanceState(s, schemaType) + if err != nil { + return nil, err + } + + jsonState, err := StateValueToJSONMap(stateVal, schemaType) + if err != nil { + return nil, err + } + + for _, upgrader := range r.StateUpgraders { + if stateSchemaVersion != upgrader.Version { + continue + } + + jsonState, err = upgrader.Upgrade(jsonState, meta) + if err != nil { + return nil, err + } + stateSchemaVersion++ + } + + // now we need to re-flatmap the new state + stateVal, err = JSONMapToStateValue(jsonState, r.CoreConfigSchema()) + if err != nil { + return nil, err + } + + return r.ShimInstanceStateFromValue(stateVal) +} + +// InternalValidate should be called to validate the structure +// of the resource. +// +// This should be called in a unit test for any resource to verify +// before release that a resource is properly configured for use with +// this library. +// +// Provider.InternalValidate() will automatically call this for all of +// the resources it manages, so you don't need to call this manually if it +// is part of a Provider. +func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error { + if r == nil { + return errors.New("resource is nil") + } + + if !writable { + if r.Create != nil || r.Update != nil || r.Delete != nil { + return fmt.Errorf("must not implement Create, Update or Delete") + } + + // CustomizeDiff cannot be defined for read-only resources + if r.CustomizeDiff != nil { + return fmt.Errorf("cannot implement CustomizeDiff") + } + } + + tsm := topSchemaMap + + if r.isTopLevel() && writable { + // All non-Computed attributes must be ForceNew if Update is not defined + if r.Update == nil { + nonForceNewAttrs := make([]string, 0) + for k, v := range r.Schema { + if !v.ForceNew && !v.Computed { + nonForceNewAttrs = append(nonForceNewAttrs, k) + } + } + if len(nonForceNewAttrs) > 0 { + return fmt.Errorf( + "No Update defined, must set ForceNew on: %#v", nonForceNewAttrs) + } + } else { + nonUpdateableAttrs := make([]string, 0) + for k, v := range r.Schema { + if v.ForceNew || v.Computed && !v.Optional { + nonUpdateableAttrs = append(nonUpdateableAttrs, k) + } + } + updateableAttrs := len(r.Schema) - len(nonUpdateableAttrs) + if updateableAttrs == 0 { + return fmt.Errorf( + "All fields are ForceNew or Computed w/out Optional, Update is superfluous") + } + } + + tsm = schemaMap(r.Schema) + + // Destroy, and Read are required + if r.Read == nil { + return fmt.Errorf("Read must be implemented") + } + if r.Delete == nil { + return fmt.Errorf("Delete must be implemented") + } + + // If we have an importer, we need to verify the importer. + if r.Importer != nil { + if err := r.Importer.InternalValidate(); err != nil { + return err + } + } + + for k, f := range tsm { + if isReservedResourceFieldName(k, f) { + return fmt.Errorf("%s is a reserved field name", k) + } + } + } + + lastVersion := -1 + for _, u := range r.StateUpgraders { + if lastVersion >= 0 && u.Version-lastVersion > 1 { + return fmt.Errorf("missing schema version between %d and %d", lastVersion, u.Version) + } + + if u.Version >= r.SchemaVersion { + return fmt.Errorf("StateUpgrader version %d is >= current version %d", u.Version, r.SchemaVersion) + } + + if !u.Type.IsObjectType() { + return fmt.Errorf("StateUpgrader %d type is not cty.Object", u.Version) + } + + if u.Upgrade == nil { + return fmt.Errorf("StateUpgrader %d missing StateUpgradeFunc", u.Version) + } + + lastVersion = u.Version + } + + if lastVersion >= 0 && lastVersion != r.SchemaVersion-1 { + return fmt.Errorf("missing StateUpgrader between %d and %d", lastVersion, r.SchemaVersion) + } + + // Data source + if r.isTopLevel() && !writable { + tsm = schemaMap(r.Schema) + for k, _ := range tsm { + if isReservedDataSourceFieldName(k) { + return fmt.Errorf("%s is a reserved field name", k) + } + } + } + + return schemaMap(r.Schema).InternalValidate(tsm) +} + +func isReservedDataSourceFieldName(name string) bool { + for _, reservedName := range ReservedDataSourceFields { + if name == reservedName { + return true + } + } + return false +} + +func isReservedResourceFieldName(name string, s *Schema) bool { + // Allow phasing out "id" + // See https://github.com/terraform-providers/terraform-provider-aws/pull/1626#issuecomment-328881415 + if name == "id" && (s.Deprecated != "" || s.Removed != "") { + return false + } + + for _, reservedName := range ReservedResourceFields { + if name == reservedName { + return true + } + } + return false +} + +// Data returns a ResourceData struct for this Resource. Each return value +// is a separate copy and can be safely modified differently. +// +// The data returned from this function has no actual affect on the Resource +// itself (including the state given to this function). +// +// This function is useful for unit tests and ResourceImporter functions. +func (r *Resource) Data(s *terraform.InstanceState) *ResourceData { + result, err := schemaMap(r.Schema).Data(s, nil) + if err != nil { + // At the time of writing, this isn't possible (Data never returns + // non-nil errors). We panic to find this in the future if we have to. + // I don't see a reason for Data to ever return an error. + panic(err) + } + + // load the Resource timeouts + result.timeouts = r.Timeouts + if result.timeouts == nil { + result.timeouts = &ResourceTimeout{} + } + + // Set the schema version to latest by default + result.meta = map[string]interface{}{ + "schema_version": strconv.Itoa(r.SchemaVersion), + } + + return result +} + +// TestResourceData Yields a ResourceData filled with this resource's schema for use in unit testing +// +// TODO: May be able to be removed with the above ResourceData function. +func (r *Resource) TestResourceData() *ResourceData { + return &ResourceData{ + schema: r.Schema, + } +} + +// SchemasForFlatmapPath tries its best to find a sequence of schemas that +// the given dot-delimited attribute path traverses through in the schema +// of the receiving Resource. +func (r *Resource) SchemasForFlatmapPath(path string) []*Schema { + return SchemasForFlatmapPath(path, r.Schema) +} + +// Returns true if the resource is "top level" i.e. not a sub-resource. +func (r *Resource) isTopLevel() bool { + // TODO: This is a heuristic; replace with a definitive attribute? + return (r.Create != nil || r.Read != nil) +} + +// Determines if a given InstanceState needs to be migrated by checking the +// stored version number with the current SchemaVersion +func (r *Resource) checkSchemaVersion(is *terraform.InstanceState) (bool, int) { + // Get the raw interface{} value for the schema version. If it doesn't + // exist or is nil then set it to zero. + raw := is.Meta["schema_version"] + if raw == nil { + raw = "0" + } + + // Try to convert it to a string. If it isn't a string then we pretend + // that it isn't set at all. It should never not be a string unless it + // was manually tampered with. + rawString, ok := raw.(string) + if !ok { + rawString = "0" + } + + stateSchemaVersion, _ := strconv.Atoi(rawString) + + // Don't run MigrateState if the version is handled by a StateUpgrader, + // since StateMigrateFuncs are not required to handle unknown versions + maxVersion := r.SchemaVersion + if len(r.StateUpgraders) > 0 { + maxVersion = r.StateUpgraders[0].Version + } + + return stateSchemaVersion < maxVersion, stateSchemaVersion +} + +func (r *Resource) recordCurrentSchemaVersion( + state *terraform.InstanceState) *terraform.InstanceState { + if state != nil && r.SchemaVersion > 0 { + if state.Meta == nil { + state.Meta = make(map[string]interface{}) + } + state.Meta["schema_version"] = strconv.Itoa(r.SchemaVersion) + } + return state +} + +// Noop is a convenience implementation of resource function which takes +// no action and returns no error. +func Noop(*ResourceData, interface{}) error { + return nil +} + +// RemoveFromState is a convenience implementation of a resource function +// which sets the resource ID to empty string (to remove it from state) +// and returns no error. +func RemoveFromState(d *ResourceData, _ interface{}) error { + d.SetId("") + return nil +} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_data.go similarity index 99% rename from vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_data.go index fb9387e2..3a61e349 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_data.go @@ -7,7 +7,7 @@ import ( "sync" "time" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/internal/legacy/terraform" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/gocty" ) diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_data_get_source.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_data_get_source.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/resource_data_get_source.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_data_get_source.go diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_data_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_data_test.go new file mode 100644 index 00000000..22ad45b6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_data_test.go @@ -0,0 +1,3564 @@ +package schema + +import ( + "fmt" + "math" + "os" + "reflect" + "testing" + "time" + + "github.com/hashicorp/terraform/internal/legacy/terraform" +) + +func TestResourceDataGet(t *testing.T) { + cases := []struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Key string + Value interface{} + }{ + // #0 + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "foo", + New: "bar", + NewComputed: true, + }, + }, + }, + + Key: "availability_zone", + Value: "", + }, + + // #1 + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Key: "availability_zone", + + Value: "foo", + }, + + // #2 + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo!", + NewExtra: "foo", + }, + }, + }, + + Key: "availability_zone", + Value: "foo", + }, + + // #3 + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "bar", + }, + }, + + Diff: nil, + + Key: "availability_zone", + + Value: "bar", + }, + + // #4 + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "foo", + New: "bar", + NewComputed: true, + }, + }, + }, + + Key: "availability_zone", + Value: "", + }, + + // #5 + { + Schema: map[string]*Schema{ + "port": &Schema{ + Type: TypeInt, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "port": "80", + }, + }, + + Diff: nil, + + Key: "port", + + Value: 80, + }, + + // #6 + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.0": "1", + "ports.1": "2", + "ports.2": "5", + }, + }, + + Key: "ports.1", + + Value: 2, + }, + + // #7 + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.0": "1", + "ports.1": "2", + "ports.2": "5", + }, + }, + + Key: "ports.#", + + Value: 3, + }, + + // #8 + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Key: "ports.#", + + Value: 0, + }, + + // #9 + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.0": "1", + "ports.1": "2", + "ports.2": "5", + }, + }, + + Key: "ports", + + Value: []interface{}{1, 2, 5}, + }, + + // #10 + { + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": &Schema{ + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ingress.#": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "ingress.0.from": &terraform.ResourceAttrDiff{ + Old: "", + New: "8080", + }, + }, + }, + + Key: "ingress.0", + + Value: map[string]interface{}{ + "from": 8080, + }, + }, + + // #11 + { + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": &Schema{ + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ingress.#": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "ingress.0.from": &terraform.ResourceAttrDiff{ + Old: "", + New: "8080", + }, + }, + }, + + Key: "ingress", + + Value: []interface{}{ + map[string]interface{}{ + "from": 8080, + }, + }, + }, + + // #12 Computed get + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + + Key: "availability_zone", + + Value: "foo", + }, + + // #13 Full object + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Key: "", + + Value: map[string]interface{}{ + "availability_zone": "foo", + }, + }, + + // #14 List of maps + { + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{ + Type: TypeMap, + }, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "2", + }, + "config_vars.0.foo": &terraform.ResourceAttrDiff{ + Old: "", + New: "bar", + }, + "config_vars.1.bar": &terraform.ResourceAttrDiff{ + Old: "", + New: "baz", + }, + }, + }, + + Key: "config_vars", + + Value: []interface{}{ + map[string]interface{}{ + "foo": "bar", + }, + map[string]interface{}{ + "bar": "baz", + }, + }, + }, + + // #15 List of maps in state + { + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{ + Type: TypeMap, + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.#": "2", + "config_vars.0.foo": "baz", + "config_vars.1.bar": "bar", + }, + }, + + Diff: nil, + + Key: "config_vars", + + Value: []interface{}{ + map[string]interface{}{ + "foo": "baz", + }, + map[string]interface{}{ + "bar": "bar", + }, + }, + }, + + // #16 List of maps with removal in diff + { + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{ + Type: TypeMap, + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.#": "1", + "config_vars.0.FOO": "bar", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.#": &terraform.ResourceAttrDiff{ + Old: "1", + New: "0", + }, + "config_vars.0.FOO": &terraform.ResourceAttrDiff{ + Old: "bar", + NewRemoved: true, + }, + }, + }, + + Key: "config_vars", + + Value: []interface{}{}, + }, + + // #17 Sets + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "1", + "ports.80": "80", + }, + }, + + Diff: nil, + + Key: "ports", + + Value: []interface{}{80}, + }, + + // #18 + { + Schema: map[string]*Schema{ + "data": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": &Schema{ + Type: TypeInt, + Required: true, + }, + + "value": &Schema{ + Type: TypeString, + Required: true, + }, + }, + }, + Set: func(a interface{}) int { + m := a.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "data.#": "1", + "data.10.index": "10", + "data.10.value": "50", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "data.10.value": &terraform.ResourceAttrDiff{ + Old: "50", + New: "80", + }, + }, + }, + + Key: "data", + + Value: []interface{}{ + map[string]interface{}{ + "index": 10, + "value": "80", + }, + }, + }, + + // #19 Empty Set + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports", + + Value: []interface{}{}, + }, + + // #20 Float zero + { + Schema: map[string]*Schema{ + "ratio": &Schema{ + Type: TypeFloat, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ratio", + + Value: 0.0, + }, + + // #21 Float given + { + Schema: map[string]*Schema{ + "ratio": &Schema{ + Type: TypeFloat, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ratio": "0.5", + }, + }, + + Diff: nil, + + Key: "ratio", + + Value: 0.5, + }, + + // #22 Float diff + { + Schema: map[string]*Schema{ + "ratio": &Schema{ + Type: TypeFloat, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ratio": "-0.5", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ratio": &terraform.ResourceAttrDiff{ + Old: "-0.5", + New: "33.0", + }, + }, + }, + + Key: "ratio", + + Value: 33.0, + }, + + // #23 Sets with removed elements + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "1", + "ports.80": "80", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "2", + New: "1", + }, + "ports.80": &terraform.ResourceAttrDiff{ + Old: "80", + New: "80", + }, + "ports.8080": &terraform.ResourceAttrDiff{ + Old: "8080", + New: "0", + NewRemoved: true, + }, + }, + }, + + Key: "ports", + + Value: []interface{}{80}, + }, + } + + for i, tc := range cases { + d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + v := d.Get(tc.Key) + if s, ok := v.(*Set); ok { + v = s.List() + } + + if !reflect.DeepEqual(v, tc.Value) { + t.Fatalf("Bad: %d\n\n%#v\n\nExpected: %#v", i, v, tc.Value) + } + } +} + +func TestResourceDataGetChange(t *testing.T) { + cases := []struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Key string + OldValue interface{} + NewValue interface{} + }{ + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Key: "availability_zone", + + OldValue: "", + NewValue: "foo", + }, + + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Key: "availability_zone", + + OldValue: "foo", + NewValue: "foo", + }, + } + + for i, tc := range cases { + d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + o, n := d.GetChange(tc.Key) + if !reflect.DeepEqual(o, tc.OldValue) { + t.Fatalf("Old Bad: %d\n\n%#v", i, o) + } + if !reflect.DeepEqual(n, tc.NewValue) { + t.Fatalf("New Bad: %d\n\n%#v", i, n) + } + } +} + +func TestResourceDataGetOk(t *testing.T) { + cases := []struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Key string + Value interface{} + Ok bool + }{ + /* + * Primitives + */ + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "", + }, + }, + }, + + Key: "availability_zone", + Value: "", + Ok: false, + }, + + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + + Key: "availability_zone", + Value: "", + Ok: false, + }, + + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "availability_zone", + Value: "", + Ok: false, + }, + + /* + * Lists + */ + + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports", + Value: []interface{}{}, + Ok: false, + }, + + /* + * Map + */ + + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeMap, + Optional: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports", + Value: map[string]interface{}{}, + Ok: false, + }, + + /* + * Set + */ + + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports", + Value: []interface{}{}, + Ok: false, + }, + + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports.0", + Value: 0, + Ok: false, + }, + + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "0", + }, + }, + }, + + Key: "ports", + Value: []interface{}{}, + Ok: false, + }, + + // Further illustrates and clarifiies the GetOk semantics from #933, and + // highlights the limitation that zero-value config is currently + // indistinguishable from unset config. + { + Schema: map[string]*Schema{ + "from_port": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "from_port": &terraform.ResourceAttrDiff{ + Old: "", + New: "0", + }, + }, + }, + + Key: "from_port", + Value: 0, + Ok: false, + }, + } + + for i, tc := range cases { + d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + v, ok := d.GetOk(tc.Key) + if s, ok := v.(*Set); ok { + v = s.List() + } + + if !reflect.DeepEqual(v, tc.Value) { + t.Fatalf("Bad: %d\n\n%#v", i, v) + } + if ok != tc.Ok { + t.Fatalf("%d: expected ok: %t, got: %t", i, tc.Ok, ok) + } + } +} + +func TestResourceDataGetOkExists(t *testing.T) { + cases := []struct { + Name string + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Key string + Value interface{} + Ok bool + }{ + /* + * Primitives + */ + { + Name: "string-literal-empty", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "", + }, + }, + }, + + Key: "availability_zone", + Value: "", + Ok: true, + }, + + { + Name: "string-computed-empty", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + + Key: "availability_zone", + Value: "", + Ok: false, + }, + + { + Name: "string-optional-computed-nil-diff", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "availability_zone", + Value: "", + Ok: false, + }, + + /* + * Lists + */ + + { + Name: "list-optional", + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports", + Value: []interface{}{}, + Ok: false, + }, + + /* + * Map + */ + + { + Name: "map-optional", + Schema: map[string]*Schema{ + "ports": { + Type: TypeMap, + Optional: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports", + Value: map[string]interface{}{}, + Ok: false, + }, + + /* + * Set + */ + + { + Name: "set-optional", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports", + Value: []interface{}{}, + Ok: false, + }, + + { + Name: "set-optional-key", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports.0", + Value: 0, + Ok: false, + }, + + { + Name: "bool-literal-empty", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeBool, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "", + }, + }, + }, + + Key: "availability_zone", + Value: false, + Ok: true, + }, + + { + Name: "bool-literal-set", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeBool, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + New: "true", + }, + }, + }, + + Key: "availability_zone", + Value: true, + Ok: true, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) + if err != nil { + t.Fatalf("%s err: %s", tc.Name, err) + } + + v, ok := d.GetOkExists(tc.Key) + if s, ok := v.(*Set); ok { + v = s.List() + } + + if !reflect.DeepEqual(v, tc.Value) { + t.Fatalf("Bad %s: \n%#v", tc.Name, v) + } + if ok != tc.Ok { + t.Fatalf("%s: expected ok: %t, got: %t", tc.Name, tc.Ok, ok) + } + }) + } +} + +func TestResourceDataTimeout(t *testing.T) { + cases := []struct { + Name string + Rd *ResourceData + Expected *ResourceTimeout + }{ + { + Name: "Basic example default", + Rd: &ResourceData{timeouts: timeoutForValues(10, 3, 0, 15, 0)}, + Expected: expectedTimeoutForValues(10, 3, 0, 15, 0), + }, + { + Name: "Resource and config match update, create", + Rd: &ResourceData{timeouts: timeoutForValues(10, 0, 3, 0, 0)}, + Expected: expectedTimeoutForValues(10, 0, 3, 0, 0), + }, + { + Name: "Resource provides default", + Rd: &ResourceData{timeouts: timeoutForValues(10, 0, 0, 0, 7)}, + Expected: expectedTimeoutForValues(10, 7, 7, 7, 7), + }, + { + Name: "Resource provides default and delete", + Rd: &ResourceData{timeouts: timeoutForValues(10, 0, 0, 15, 7)}, + Expected: expectedTimeoutForValues(10, 7, 7, 15, 7), + }, + { + Name: "Resource provides default, config overwrites other values", + Rd: &ResourceData{timeouts: timeoutForValues(10, 3, 0, 0, 13)}, + Expected: expectedTimeoutForValues(10, 3, 13, 13, 13), + }, + { + Name: "Resource has no config", + Rd: &ResourceData{}, + Expected: expectedTimeoutForValues(0, 0, 0, 0, 0), + }, + } + + keys := timeoutKeys() + for i, c := range cases { + t.Run(fmt.Sprintf("%d-%s", i, c.Name), func(t *testing.T) { + + for _, k := range keys { + got := c.Rd.Timeout(k) + var ex *time.Duration + switch k { + case TimeoutCreate: + ex = c.Expected.Create + case TimeoutRead: + ex = c.Expected.Read + case TimeoutUpdate: + ex = c.Expected.Update + case TimeoutDelete: + ex = c.Expected.Delete + case TimeoutDefault: + ex = c.Expected.Default + } + + if got > 0 && ex == nil { + t.Fatalf("Unexpected value in (%s), case %d check 1:\n\texpected: %#v\n\tgot: %#v", k, i, ex, got) + } + if got == 0 && ex != nil { + t.Fatalf("Unexpected value in (%s), case %d check 2:\n\texpected: %#v\n\tgot: %#v", k, i, *ex, got) + } + + // confirm values + if ex != nil { + if got != *ex { + t.Fatalf("Timeout %s case (%d) expected (%s), got (%s)", k, i, *ex, got) + } + } + } + + }) + } +} + +func TestResourceDataHasChange(t *testing.T) { + cases := []struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Key string + Change bool + }{ + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Key: "availability_zone", + + Change: true, + }, + + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Key: "availability_zone", + + Change: false, + }, + + { + Schema: map[string]*Schema{ + "tags": &Schema{ + Type: TypeMap, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "tags.Name": &terraform.ResourceAttrDiff{ + Old: "foo", + New: "foo", + }, + }, + }, + + Key: "tags", + + Change: true, + }, + + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "1", + "ports.80": "80", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "1", + New: "0", + }, + }, + }, + + Key: "ports", + + Change: true, + }, + + // https://github.com/hashicorp/terraform/issues/927 + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "1", + "ports.80": "80", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "tags.foo": &terraform.ResourceAttrDiff{ + Old: "", + New: "bar", + }, + }, + }, + + Key: "ports", + + Change: false, + }, + } + + for i, tc := range cases { + d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + actual := d.HasChange(tc.Key) + if actual != tc.Change { + t.Fatalf("Bad: %d %#v", i, actual) + } + } +} + +func TestResourceDataSet(t *testing.T) { + var testNilPtr *string + + cases := []struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Key string + Value interface{} + Err bool + GetKey string + GetValue interface{} + + // GetPreProcess can be set to munge the return value before being + // compared to GetValue + GetPreProcess func(interface{}) interface{} + }{ + // #0: Basic good + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "availability_zone", + Value: "foo", + + GetKey: "availability_zone", + GetValue: "foo", + }, + + // #1: Basic int + { + Schema: map[string]*Schema{ + "port": &Schema{ + Type: TypeInt, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "port", + Value: 80, + + GetKey: "port", + GetValue: 80, + }, + + // #2: Basic bool + { + Schema: map[string]*Schema{ + "vpc": &Schema{ + Type: TypeBool, + Optional: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "vpc", + Value: true, + + GetKey: "vpc", + GetValue: true, + }, + + // #3 + { + Schema: map[string]*Schema{ + "vpc": &Schema{ + Type: TypeBool, + Optional: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "vpc", + Value: false, + + GetKey: "vpc", + GetValue: false, + }, + + // #4: Invalid type + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "availability_zone", + Value: 80, + Err: true, + + GetKey: "availability_zone", + GetValue: "", + }, + + // #5: List of primitives, set list + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Computed: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports", + Value: []int{1, 2, 5}, + + GetKey: "ports", + GetValue: []interface{}{1, 2, 5}, + }, + + // #6: List of primitives, set list with error + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Computed: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ports", + Value: []interface{}{1, "NOPE", 5}, + Err: true, + + GetKey: "ports", + GetValue: []interface{}{}, + }, + + // #7: Set a list of maps + { + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{ + Type: TypeMap, + }, + }, + }, + + State: nil, + + Diff: nil, + + Key: "config_vars", + Value: []interface{}{ + map[string]interface{}{ + "foo": "bar", + }, + map[string]interface{}{ + "bar": "baz", + }, + }, + Err: false, + + GetKey: "config_vars", + GetValue: []interface{}{ + map[string]interface{}{ + "foo": "bar", + }, + map[string]interface{}{ + "bar": "baz", + }, + }, + }, + + // #8: Set, with list + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.0": "100", + "ports.1": "80", + "ports.2": "80", + }, + }, + + Key: "ports", + Value: []interface{}{100, 125, 125}, + + GetKey: "ports", + GetValue: []interface{}{100, 125}, + }, + + // #9: Set, with Set + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.100": "100", + "ports.80": "80", + "ports.81": "81", + }, + }, + + Key: "ports", + Value: &Set{ + m: map[string]interface{}{ + "1": 1, + "2": 2, + }, + }, + + GetKey: "ports", + GetValue: []interface{}{1, 2}, + }, + + // #10: Set single item + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "2", + "ports.100": "100", + "ports.80": "80", + }, + }, + + Key: "ports.100", + Value: 256, + Err: true, + + GetKey: "ports", + GetValue: []interface{}{100, 80}, + }, + + // #11: Set with nested set + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Elem: &Resource{ + Schema: map[string]*Schema{ + "port": &Schema{ + Type: TypeInt, + }, + + "set": &Schema{ + Type: TypeSet, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + }, + Set: func(a interface{}) int { + return a.(map[string]interface{})["port"].(int) + }, + }, + }, + + State: nil, + + Key: "ports", + Value: []interface{}{ + map[string]interface{}{ + "port": 80, + }, + }, + + GetKey: "ports", + GetValue: []interface{}{ + map[string]interface{}{ + "port": 80, + "set": []interface{}{}, + }, + }, + + GetPreProcess: func(v interface{}) interface{} { + if v == nil { + return v + } + s, ok := v.([]interface{}) + if !ok { + return v + } + for _, v := range s { + m, ok := v.(map[string]interface{}) + if !ok { + continue + } + if m["set"] == nil { + continue + } + if s, ok := m["set"].(*Set); ok { + m["set"] = s.List() + } + } + + return v + }, + }, + + // #12: List of floats, set list + { + Schema: map[string]*Schema{ + "ratios": &Schema{ + Type: TypeList, + Computed: true, + Elem: &Schema{Type: TypeFloat}, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ratios", + Value: []float64{1.0, 2.2, 5.5}, + + GetKey: "ratios", + GetValue: []interface{}{1.0, 2.2, 5.5}, + }, + + // #12: Set of floats, set list + { + Schema: map[string]*Schema{ + "ratios": &Schema{ + Type: TypeSet, + Computed: true, + Elem: &Schema{Type: TypeFloat}, + Set: func(a interface{}) int { + return int(math.Float64bits(a.(float64))) + }, + }, + }, + + State: nil, + + Diff: nil, + + Key: "ratios", + Value: []float64{1.0, 2.2, 5.5}, + + GetKey: "ratios", + GetValue: []interface{}{1.0, 2.2, 5.5}, + }, + + // #13: Basic pointer + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "availability_zone", + Value: testPtrTo("foo"), + + GetKey: "availability_zone", + GetValue: "foo", + }, + + // #14: Basic nil value + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "availability_zone", + Value: testPtrTo(nil), + + GetKey: "availability_zone", + GetValue: "", + }, + + // #15: Basic nil pointer + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: nil, + + Key: "availability_zone", + Value: testNilPtr, + + GetKey: "availability_zone", + GetValue: "", + }, + + // #16: Set in a list + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Elem: &Resource{ + Schema: map[string]*Schema{ + "set": &Schema{ + Type: TypeSet, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + }, + }, + }, + + State: nil, + + Key: "ports", + Value: []interface{}{ + map[string]interface{}{ + "set": []interface{}{ + 1, + }, + }, + }, + + GetKey: "ports", + GetValue: []interface{}{ + map[string]interface{}{ + "set": []interface{}{ + 1, + }, + }, + }, + GetPreProcess: func(v interface{}) interface{} { + if v == nil { + return v + } + s, ok := v.([]interface{}) + if !ok { + return v + } + for _, v := range s { + m, ok := v.(map[string]interface{}) + if !ok { + continue + } + if m["set"] == nil { + continue + } + if s, ok := m["set"].(*Set); ok { + m["set"] = s.List() + } + } + + return v + }, + }, + } + + oldEnv := os.Getenv(PanicOnErr) + os.Setenv(PanicOnErr, "") + defer os.Setenv(PanicOnErr, oldEnv) + + for i, tc := range cases { + d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + err = d.Set(tc.Key, tc.Value) + if err != nil != tc.Err { + t.Fatalf("%d err: %s", i, err) + } + + v := d.Get(tc.GetKey) + if s, ok := v.(*Set); ok { + v = s.List() + } + + if tc.GetPreProcess != nil { + v = tc.GetPreProcess(v) + } + + if !reflect.DeepEqual(v, tc.GetValue) { + t.Fatalf("Get Bad: %d\n\n%#v", i, v) + } + } +} + +func TestResourceDataState_dynamicAttributes(t *testing.T) { + cases := []struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Set map[string]interface{} + UnsafeSet map[string]string + Result *terraform.InstanceState + }{ + { + Schema: map[string]*Schema{ + "__has_dynamic_attributes": { + Type: TypeString, + Optional: true, + }, + + "schema_field": { + Type: TypeString, + Required: true, + }, + }, + + State: nil, + + Diff: nil, + + Set: map[string]interface{}{ + "schema_field": "present", + }, + + UnsafeSet: map[string]string{ + "test1": "value", + "test2": "value", + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "schema_field": "present", + "test1": "value", + "test2": "value", + }, + }, + }, + } + + for i, tc := range cases { + d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + for k, v := range tc.Set { + d.Set(k, v) + } + + for k, v := range tc.UnsafeSet { + d.UnsafeSetFieldRaw(k, v) + } + + // Set an ID so that the state returned is not nil + idSet := false + if d.Id() == "" { + idSet = true + d.SetId("foo") + } + + actual := d.State() + + // If we set an ID, then undo what we did so the comparison works + if actual != nil && idSet { + actual.ID = "" + delete(actual.Attributes, "id") + } + + if !reflect.DeepEqual(actual, tc.Result) { + t.Fatalf("Bad: %d\n\n%#v\n\nExpected:\n\n%#v", i, actual, tc.Result) + } + } +} + +func TestResourceDataState_schema(t *testing.T) { + cases := []struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Set map[string]interface{} + Result *terraform.InstanceState + Partial []string + }{ + // #0 Basic primitive in diff + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + }, + + // #1 Basic primitive set override + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Set: map[string]interface{}{ + "availability_zone": "bar", + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "bar", + }, + }, + }, + + // #2 + { + Schema: map[string]*Schema{ + "vpc": &Schema{ + Type: TypeBool, + Optional: true, + }, + }, + + State: nil, + + Diff: nil, + + Set: map[string]interface{}{ + "vpc": true, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "vpc": "true", + }, + }, + }, + + // #3 Basic primitive with StateFunc set + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + StateFunc: func(interface{}) string { return "" }, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + NewExtra: "foo!", + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + }, + + // #4 List + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "1", + "ports.0": "80", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "1", + New: "2", + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "", + New: "100", + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "2", + "ports.0": "80", + "ports.1": "100", + }, + }, + }, + + // #5 List of resources + { + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": &Schema{ + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ingress.#": "1", + "ingress.0.from": "80", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ingress.#": &terraform.ResourceAttrDiff{ + Old: "1", + New: "2", + }, + "ingress.0.from": &terraform.ResourceAttrDiff{ + Old: "80", + New: "150", + }, + "ingress.1.from": &terraform.ResourceAttrDiff{ + Old: "", + New: "100", + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ingress.#": "2", + "ingress.0.from": "150", + "ingress.1.from": "100", + }, + }, + }, + + // #6 List of maps + { + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{ + Type: TypeMap, + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.#": "2", + "config_vars.0.%": "2", + "config_vars.0.foo": "bar", + "config_vars.0.bar": "bar", + "config_vars.1.%": "1", + "config_vars.1.bar": "baz", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.0.bar": &terraform.ResourceAttrDiff{ + NewRemoved: true, + }, + }, + }, + + Set: map[string]interface{}{ + "config_vars": []map[string]interface{}{ + map[string]interface{}{ + "foo": "bar", + }, + map[string]interface{}{ + "baz": "bang", + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.#": "2", + "config_vars.0.%": "1", + "config_vars.0.foo": "bar", + "config_vars.1.%": "1", + "config_vars.1.baz": "bang", + }, + }, + }, + + // #7 List of maps with removal in diff + { + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{ + Type: TypeMap, + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.#": "1", + "config_vars.0.FOO": "bar", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.#": &terraform.ResourceAttrDiff{ + Old: "1", + New: "0", + }, + "config_vars.0.FOO": &terraform.ResourceAttrDiff{ + Old: "bar", + NewRemoved: true, + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.#": "0", + }, + }, + }, + + // #8 Basic state with other keys + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Result: &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + "availability_zone": "foo", + }, + }, + }, + + // #9 Sets + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.100": "100", + "ports.80": "80", + "ports.81": "81", + }, + }, + + Diff: nil, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.80": "80", + "ports.81": "81", + "ports.100": "100", + }, + }, + }, + + // #10 + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Diff: nil, + + Set: map[string]interface{}{ + "ports": []interface{}{100, 80}, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "2", + "ports.80": "80", + "ports.100": "100", + }, + }, + }, + + // #11 + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "order": &Schema{ + Type: TypeInt, + }, + + "a": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + }, + + "b": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + }, + }, + }, + Set: func(a interface{}) int { + m := a.(map[string]interface{}) + return m["order"].(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "2", + "ports.10.order": "10", + "ports.10.a.#": "1", + "ports.10.a.0": "80", + "ports.20.order": "20", + "ports.20.b.#": "1", + "ports.20.b.0": "100", + }, + }, + + Set: map[string]interface{}{ + "ports": []interface{}{ + map[string]interface{}{ + "order": 20, + "b": []interface{}{100}, + }, + map[string]interface{}{ + "order": 10, + "a": []interface{}{80}, + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "2", + "ports.10.order": "10", + "ports.10.a.#": "1", + "ports.10.a.0": "80", + "ports.10.b.#": "0", + "ports.20.order": "20", + "ports.20.a.#": "0", + "ports.20.b.#": "1", + "ports.20.b.0": "100", + }, + }, + }, + + /* + * PARTIAL STATES + */ + + // #12 Basic primitive + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Partial: []string{}, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{}, + }, + }, + + // #13 List + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "1", + "ports.0": "80", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "1", + New: "2", + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "", + New: "100", + }, + }, + }, + + Partial: []string{}, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "1", + "ports.0": "80", + }, + }, + }, + + // #14 + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "", + NewComputed: true, + }, + }, + }, + + Partial: []string{}, + + Set: map[string]interface{}{ + "ports": []interface{}{}, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{}, + }, + }, + + // #15 List of resources + { + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": &Schema{ + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ingress.#": "1", + "ingress.0.from": "80", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ingress.#": &terraform.ResourceAttrDiff{ + Old: "1", + New: "2", + }, + "ingress.0.from": &terraform.ResourceAttrDiff{ + Old: "80", + New: "150", + }, + "ingress.1.from": &terraform.ResourceAttrDiff{ + Old: "", + New: "100", + }, + }, + }, + + Partial: []string{}, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ingress.#": "1", + "ingress.0.from": "80", + }, + }, + }, + + // #16 List of maps + { + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{ + Type: TypeMap, + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.#": "2", + "config_vars.0.foo": "bar", + "config_vars.0.bar": "bar", + "config_vars.1.bar": "baz", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.0.bar": &terraform.ResourceAttrDiff{ + NewRemoved: true, + }, + }, + }, + + Set: map[string]interface{}{ + "config_vars": []map[string]interface{}{ + map[string]interface{}{ + "foo": "bar", + }, + map[string]interface{}{ + "baz": "bang", + }, + }, + }, + + Partial: []string{}, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + // TODO: broken, shouldn't bar be removed? + "config_vars.#": "2", + "config_vars.0.%": "2", + "config_vars.0.foo": "bar", + "config_vars.0.bar": "bar", + "config_vars.1.%": "1", + "config_vars.1.bar": "baz", + }, + }, + }, + + // #17 Sets + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.100": "100", + "ports.80": "80", + "ports.81": "81", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.120": &terraform.ResourceAttrDiff{ + New: "120", + }, + }, + }, + + Partial: []string{}, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.80": "80", + "ports.81": "81", + "ports.100": "100", + }, + }, + }, + + // #18 + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "", + NewComputed: true, + }, + }, + }, + + Partial: []string{}, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{}, + }, + }, + + // #19 Maps + { + Schema: map[string]*Schema{ + "tags": &Schema{ + Type: TypeMap, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "tags.Name": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "tags.%": "1", + "tags.Name": "foo", + }, + }, + }, + + // #20 empty computed map + { + Schema: map[string]*Schema{ + "tags": &Schema{ + Type: TypeMap, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "tags.Name": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + }, + }, + }, + + Set: map[string]interface{}{ + "tags": map[string]string{}, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "tags.%": "0", + }, + }, + }, + + // #21 + { + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + NewComputed: true, + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{}, + }, + }, + + // #22 + { + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + NewComputed: true, + }, + }, + }, + + Set: map[string]interface{}{ + "foo": "bar", + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + }, + + // #23 Set of maps + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": &Schema{Type: TypeInt}, + "uuids": &Schema{Type: TypeMap}, + }, + }, + Set: func(a interface{}) int { + m := a.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.10.uuids.#": &terraform.ResourceAttrDiff{ + NewComputed: true, + }, + }, + }, + + Set: map[string]interface{}{ + "ports": []interface{}{ + map[string]interface{}{ + "index": 10, + "uuids": map[string]interface{}{ + "80": "value", + }, + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "1", + "ports.10.index": "10", + "ports.10.uuids.%": "1", + "ports.10.uuids.80": "value", + }, + }, + }, + + // #24 + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.100": "100", + "ports.80": "80", + "ports.81": "81", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "3", + New: "0", + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "0", + }, + }, + }, + + // #25 + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Diff: nil, + + Set: map[string]interface{}{ + "ports": []interface{}{}, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "0", + }, + }, + }, + + // #26 + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Diff: nil, + + Set: map[string]interface{}{ + "ports": []interface{}{}, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "0", + }, + }, + }, + + // #27 Set lists + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": &Schema{Type: TypeInt}, + "uuids": &Schema{Type: TypeMap}, + }, + }, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + NewComputed: true, + }, + }, + }, + + Set: map[string]interface{}{ + "ports": []interface{}{ + map[string]interface{}{ + "index": 10, + "uuids": map[string]interface{}{ + "80": "value", + }, + }, + }, + }, + + Result: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "1", + "ports.0.index": "10", + "ports.0.uuids.%": "1", + "ports.0.uuids.80": "value", + }, + }, + }, + } + + for i, tc := range cases { + d, err := schemaMap(tc.Schema).Data(tc.State, tc.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + for k, v := range tc.Set { + if err := d.Set(k, v); err != nil { + t.Fatalf("%d err: %s", i, err) + } + } + + // Set an ID so that the state returned is not nil + idSet := false + if d.Id() == "" { + idSet = true + d.SetId("foo") + } + + // If we have partial, then enable partial state mode. + if tc.Partial != nil { + d.Partial(true) + for _, k := range tc.Partial { + d.SetPartial(k) + } + } + + actual := d.State() + + // If we set an ID, then undo what we did so the comparison works + if actual != nil && idSet { + actual.ID = "" + delete(actual.Attributes, "id") + } + + if !reflect.DeepEqual(actual, tc.Result) { + t.Fatalf("Bad: %d\n\n%#v\n\nExpected:\n\n%#v", i, actual, tc.Result) + } + } +} + +func TestResourceData_nonStringValuesInMap(t *testing.T) { + cases := []struct { + Schema map[string]*Schema + Diff *terraform.InstanceDiff + MapFieldName string + ItemName string + ExpectedType string + }{ + { + Schema: map[string]*Schema{ + "boolMap": &Schema{ + Type: TypeMap, + Elem: TypeBool, + Optional: true, + }, + }, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "boolMap.%": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "boolMap.boolField": &terraform.ResourceAttrDiff{ + Old: "", + New: "true", + }, + }, + }, + MapFieldName: "boolMap", + ItemName: "boolField", + ExpectedType: "bool", + }, + { + Schema: map[string]*Schema{ + "intMap": &Schema{ + Type: TypeMap, + Elem: TypeInt, + Optional: true, + }, + }, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "intMap.%": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "intMap.intField": &terraform.ResourceAttrDiff{ + Old: "", + New: "8", + }, + }, + }, + MapFieldName: "intMap", + ItemName: "intField", + ExpectedType: "int", + }, + { + Schema: map[string]*Schema{ + "floatMap": &Schema{ + Type: TypeMap, + Elem: TypeFloat, + Optional: true, + }, + }, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "floatMap.%": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "floatMap.floatField": &terraform.ResourceAttrDiff{ + Old: "", + New: "8.22", + }, + }, + }, + MapFieldName: "floatMap", + ItemName: "floatField", + ExpectedType: "float64", + }, + } + + for _, c := range cases { + d, err := schemaMap(c.Schema).Data(nil, c.Diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + m, ok := d.Get(c.MapFieldName).(map[string]interface{}) + if !ok { + t.Fatalf("expected %q to be castable to a map", c.MapFieldName) + } + field, ok := m[c.ItemName] + if !ok { + t.Fatalf("expected %q in the map", c.ItemName) + } + + typeName := reflect.TypeOf(field).Name() + if typeName != c.ExpectedType { + t.Fatalf("expected %q to be %q, it is %q.", + c.ItemName, c.ExpectedType, typeName) + } + } +} + +func TestResourceDataSetConnInfo(t *testing.T) { + d := &ResourceData{} + d.SetId("foo") + d.SetConnInfo(map[string]string{ + "foo": "bar", + }) + + expected := map[string]string{ + "foo": "bar", + } + + actual := d.State() + if !reflect.DeepEqual(actual.Ephemeral.ConnInfo, expected) { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceDataSetMeta_Timeouts(t *testing.T) { + d := &ResourceData{} + d.SetId("foo") + + rt := ResourceTimeout{ + Create: DefaultTimeout(7 * time.Minute), + } + + d.timeouts = &rt + + expected := expectedForValues(7, 0, 0, 0, 0) + + actual := d.State() + if !reflect.DeepEqual(actual.Meta[TimeoutKey], expected) { + t.Fatalf("Bad Meta_timeout match:\n\texpected: %#v\n\tgot: %#v", expected, actual.Meta[TimeoutKey]) + } +} + +func TestResourceDataSetId(t *testing.T) { + d := &ResourceData{ + state: &terraform.InstanceState{ + ID: "test", + Attributes: map[string]string{ + "id": "test", + }, + }, + } + d.SetId("foo") + + actual := d.State() + + // SetId should set both the ID field as well as the attribute, to aid in + // transitioning to the new type system. + if actual.ID != "foo" || actual.Attributes["id"] != "foo" { + t.Fatalf("bad: %#v", actual) + } + + d.SetId("") + actual = d.State() + if actual != nil { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceDataSetId_clear(t *testing.T) { + d := &ResourceData{ + state: &terraform.InstanceState{ID: "bar"}, + } + d.SetId("") + + actual := d.State() + if actual != nil { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceDataSetId_override(t *testing.T) { + d := &ResourceData{ + state: &terraform.InstanceState{ID: "bar"}, + } + d.SetId("foo") + + actual := d.State() + if actual.ID != "foo" { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceDataSetType(t *testing.T) { + d := &ResourceData{} + d.SetId("foo") + d.SetType("bar") + + actual := d.State() + if v := actual.Ephemeral.Type; v != "bar" { + t.Fatalf("bad: %#v", actual) + } +} + +func testPtrTo(raw interface{}) interface{} { + return &raw +} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_diff.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_diff.go similarity index 99% rename from vendor/github.com/hashicorp/terraform/helper/schema/resource_diff.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_diff.go index 47b54810..72d4711e 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/resource_diff.go +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_diff.go @@ -7,7 +7,7 @@ import ( "strings" "sync" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/internal/legacy/terraform" ) // newValueWriter is a minor re-implementation of MapFieldWriter to include diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_diff_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_diff_test.go new file mode 100644 index 00000000..7cb9d518 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_diff_test.go @@ -0,0 +1,2045 @@ +package schema + +import ( + "fmt" + "reflect" + "sort" + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/hashicorp/terraform/configs/hcl2shim" + "github.com/hashicorp/terraform/internal/legacy/terraform" +) + +// testSetFunc is a very simple function we use to test a foo/bar complex set. +// Both "foo" and "bar" are int values. +// +// This is not foolproof as since it performs sums, you can run into +// collisions. Spec tests accordingly. :P +func testSetFunc(v interface{}) int { + m := v.(map[string]interface{}) + return m["foo"].(int) + m["bar"].(int) +} + +// resourceDiffTestCase provides a test case struct for SetNew and SetDiff. +type resourceDiffTestCase struct { + Name string + Schema map[string]*Schema + State *terraform.InstanceState + Config *terraform.ResourceConfig + Diff *terraform.InstanceDiff + Key string + OldValue interface{} + NewValue interface{} + Expected *terraform.InstanceDiff + ExpectedKeys []string + ExpectedError bool +} + +// testDiffCases produces a list of test cases for use with SetNew and SetDiff. +func testDiffCases(t *testing.T, oldPrefix string, oldOffset int, computed bool) []resourceDiffTestCase { + return []resourceDiffTestCase{ + resourceDiffTestCase{ + Name: "basic primitive diff", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "baz", + }, + }, + }, + Key: "foo", + NewValue: "qux", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: func() string { + if computed { + return "" + } + return "qux" + }(), + NewComputed: computed, + }, + }, + }, + }, + resourceDiffTestCase{ + Name: "basic set diff", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeString}, + Set: HashString, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo.#": "1", + "foo.1996459178": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": []interface{}{"baz"}, + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.1996459178": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "", + NewRemoved: true, + }, + "foo.2015626392": &terraform.ResourceAttrDiff{ + Old: "", + New: "baz", + }, + }, + }, + Key: "foo", + NewValue: []interface{}{"qux"}, + Expected: &terraform.InstanceDiff{ + Attributes: func() map[string]*terraform.ResourceAttrDiff { + result := map[string]*terraform.ResourceAttrDiff{} + if computed { + result["foo.#"] = &terraform.ResourceAttrDiff{ + Old: "1", + New: "", + NewComputed: true, + } + } else { + result["foo.2800005064"] = &terraform.ResourceAttrDiff{ + Old: "", + New: "qux", + } + result["foo.1996459178"] = &terraform.ResourceAttrDiff{ + Old: "bar", + New: "", + NewRemoved: true, + } + } + return result + }(), + }, + }, + resourceDiffTestCase{ + Name: "basic list diff", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeString}, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo.#": "1", + "foo.0": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": []interface{}{"baz"}, + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.0": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "baz", + }, + }, + }, + Key: "foo", + NewValue: []interface{}{"qux"}, + Expected: &terraform.InstanceDiff{ + Attributes: func() map[string]*terraform.ResourceAttrDiff { + result := make(map[string]*terraform.ResourceAttrDiff) + if computed { + result["foo.#"] = &terraform.ResourceAttrDiff{ + Old: "1", + New: "", + NewComputed: true, + } + } else { + result["foo.0"] = &terraform.ResourceAttrDiff{ + Old: "bar", + New: "qux", + } + } + return result + }(), + }, + }, + resourceDiffTestCase{ + Name: "basic map diff", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeMap, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo.%": "1", + "foo.bar": "baz", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": map[string]interface{}{"bar": "qux"}, + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.bar": &terraform.ResourceAttrDiff{ + Old: "baz", + New: "qux", + }, + }, + }, + Key: "foo", + NewValue: map[string]interface{}{"bar": "quux"}, + Expected: &terraform.InstanceDiff{ + Attributes: func() map[string]*terraform.ResourceAttrDiff { + result := make(map[string]*terraform.ResourceAttrDiff) + if computed { + result["foo.%"] = &terraform.ResourceAttrDiff{ + Old: "", + New: "", + NewComputed: true, + } + result["foo.bar"] = &terraform.ResourceAttrDiff{ + Old: "baz", + New: "", + NewRemoved: true, + } + } else { + result["foo.bar"] = &terraform.ResourceAttrDiff{ + Old: "baz", + New: "quux", + } + } + return result + }(), + }, + }, + resourceDiffTestCase{ + Name: "additional diff with primitive", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Optional: true, + }, + "one": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + "one": "two", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "baz", + }, + }, + }, + Key: "one", + NewValue: "four", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "baz", + }, + "one": &terraform.ResourceAttrDiff{ + Old: "two", + New: func() string { + if computed { + return "" + } + return "four" + }(), + NewComputed: computed, + }, + }, + }, + }, + resourceDiffTestCase{ + Name: "additional diff with primitive computed only", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Optional: true, + }, + "one": &Schema{ + Type: TypeString, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + "one": "two", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "baz", + }, + }, + }, + Key: "one", + NewValue: "three", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "baz", + }, + "one": &terraform.ResourceAttrDiff{ + Old: "two", + New: func() string { + if computed { + return "" + } + return "three" + }(), + NewComputed: computed, + }, + }, + }, + }, + resourceDiffTestCase{ + Name: "complex-ish set diff", + Schema: map[string]*Schema{ + "top": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + Computed: true, + }, + "bar": &Schema{ + Type: TypeInt, + Optional: true, + Computed: true, + }, + }, + }, + Set: testSetFunc, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "top.#": "2", + "top.3.foo": "1", + "top.3.bar": "2", + "top.23.foo": "11", + "top.23.bar": "12", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "top": []interface{}{ + map[string]interface{}{ + "foo": 1, + "bar": 3, + }, + map[string]interface{}{ + "foo": 12, + "bar": 12, + }, + }, + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "top.4.foo": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "top.4.bar": &terraform.ResourceAttrDiff{ + Old: "", + New: "3", + }, + "top.24.foo": &terraform.ResourceAttrDiff{ + Old: "", + New: "12", + }, + "top.24.bar": &terraform.ResourceAttrDiff{ + Old: "", + New: "12", + }, + }, + }, + Key: "top", + NewValue: NewSet(testSetFunc, []interface{}{ + map[string]interface{}{ + "foo": 1, + "bar": 4, + }, + map[string]interface{}{ + "foo": 13, + "bar": 12, + }, + map[string]interface{}{ + "foo": 21, + "bar": 22, + }, + }), + Expected: &terraform.InstanceDiff{ + Attributes: func() map[string]*terraform.ResourceAttrDiff { + result := make(map[string]*terraform.ResourceAttrDiff) + if computed { + result["top.#"] = &terraform.ResourceAttrDiff{ + Old: "2", + New: "", + NewComputed: true, + } + } else { + result["top.#"] = &terraform.ResourceAttrDiff{ + Old: "2", + New: "3", + } + result["top.5.foo"] = &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + } + result["top.5.bar"] = &terraform.ResourceAttrDiff{ + Old: "", + New: "4", + } + result["top.25.foo"] = &terraform.ResourceAttrDiff{ + Old: "", + New: "13", + } + result["top.25.bar"] = &terraform.ResourceAttrDiff{ + Old: "", + New: "12", + } + result["top.43.foo"] = &terraform.ResourceAttrDiff{ + Old: "", + New: "21", + } + result["top.43.bar"] = &terraform.ResourceAttrDiff{ + Old: "", + New: "22", + } + } + return result + }(), + }, + }, + resourceDiffTestCase{ + Name: "primitive, no diff, no refresh", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{}), + Diff: &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{}}, + Key: "foo", + NewValue: "baz", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: func() string { + if computed { + return "" + } + return "baz" + }(), + NewComputed: computed, + }, + }, + }, + }, + resourceDiffTestCase{ + Name: "non-computed key, should error", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Required: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "baz", + }, + }, + }, + Key: "foo", + NewValue: "qux", + ExpectedError: true, + }, + resourceDiffTestCase{ + Name: "bad key, should error", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Required: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "baz", + }, + }, + }, + Key: "bad", + NewValue: "qux", + ExpectedError: true, + }, + resourceDiffTestCase{ + // NOTE: This case is technically impossible in the current + // implementation, because optional+computed values never show up in the + // diff, and we actually clear existing diffs when SetNew or + // SetNewComputed is run. This test is here to ensure that if either of + // these behaviors change that we don't introduce regressions. + Name: "NewRemoved in diff for Optional and Computed, should be fully overridden", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{}), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "", + NewRemoved: true, + }, + }, + }, + Key: "foo", + NewValue: "qux", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: func() string { + if computed { + return "" + } + return "qux" + }(), + NewComputed: computed, + }, + }, + }, + }, + resourceDiffTestCase{ + Name: "NewComputed should always propagate", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "", + }, + ID: "pre-existing", + }, + Config: testConfig(t, map[string]interface{}{}), + Diff: &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{}}, + Key: "foo", + NewValue: "", + Expected: &terraform.InstanceDiff{ + Attributes: func() map[string]*terraform.ResourceAttrDiff { + if computed { + return map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + NewComputed: computed, + }, + } + } + return map[string]*terraform.ResourceAttrDiff{} + }(), + }, + }, + } +} + +func TestSetNew(t *testing.T) { + testCases := testDiffCases(t, "", 0, false) + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + m := schemaMap(tc.Schema) + d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) + err := d.SetNew(tc.Key, tc.NewValue) + switch { + case err != nil && !tc.ExpectedError: + t.Fatalf("bad: %s", err) + case err == nil && tc.ExpectedError: + t.Fatalf("Expected error, got none") + case err != nil && tc.ExpectedError: + return + } + for _, k := range d.UpdatedKeys() { + if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { + t.Fatalf("bad: %s", err) + } + } + if !reflect.DeepEqual(tc.Expected, tc.Diff) { + t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff)) + } + }) + } +} + +func TestSetNewComputed(t *testing.T) { + testCases := testDiffCases(t, "", 0, true) + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + m := schemaMap(tc.Schema) + d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) + err := d.SetNewComputed(tc.Key) + switch { + case err != nil && !tc.ExpectedError: + t.Fatalf("bad: %s", err) + case err == nil && tc.ExpectedError: + t.Fatalf("Expected error, got none") + case err != nil && tc.ExpectedError: + return + } + for _, k := range d.UpdatedKeys() { + if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { + t.Fatalf("bad: %s", err) + } + } + if !reflect.DeepEqual(tc.Expected, tc.Diff) { + t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff)) + } + }) + } +} + +func TestForceNew(t *testing.T) { + cases := []resourceDiffTestCase{ + resourceDiffTestCase{ + Name: "basic primitive diff", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "baz", + }, + }, + }, + Key: "foo", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "baz", + RequiresNew: true, + }, + }, + }, + }, + resourceDiffTestCase{ + Name: "no change, should error", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "bar", + }), + ExpectedError: true, + }, + resourceDiffTestCase{ + Name: "basic primitive, non-computed key", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Required: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "baz", + }, + }, + }, + Key: "foo", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "baz", + RequiresNew: true, + }, + }, + }, + }, + resourceDiffTestCase{ + Name: "nested field", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeList, + Required: true, + MaxItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "bar": { + Type: TypeString, + Optional: true, + }, + "baz": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo.#": "1", + "foo.0.bar": "abc", + "foo.0.baz": "xyz", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": []interface{}{ + map[string]interface{}{ + "bar": "abcdefg", + "baz": "changed", + }, + }, + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.0.bar": &terraform.ResourceAttrDiff{ + Old: "abc", + New: "abcdefg", + }, + "foo.0.baz": &terraform.ResourceAttrDiff{ + Old: "xyz", + New: "changed", + }, + }, + }, + Key: "foo.0.baz", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.0.bar": &terraform.ResourceAttrDiff{ + Old: "abc", + New: "abcdefg", + }, + "foo.0.baz": &terraform.ResourceAttrDiff{ + Old: "xyz", + New: "changed", + RequiresNew: true, + }, + }, + }, + }, + resourceDiffTestCase{ + Name: "preserve NewRemoved on existing diff", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{}), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "", + NewRemoved: true, + }, + }, + }, + Key: "foo", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "", + RequiresNew: true, + NewRemoved: true, + }, + }, + }, + }, + resourceDiffTestCase{ + Name: "nested field, preserve original diff without zero values", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeList, + Required: true, + MaxItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "bar": { + Type: TypeString, + Optional: true, + }, + "baz": { + Type: TypeInt, + Optional: true, + }, + }, + }, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo.#": "1", + "foo.0.bar": "abc", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": []interface{}{ + map[string]interface{}{ + "bar": "abcdefg", + }, + }, + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.0.bar": &terraform.ResourceAttrDiff{ + Old: "abc", + New: "abcdefg", + }, + }, + }, + Key: "foo.0.bar", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.0.bar": &terraform.ResourceAttrDiff{ + Old: "abc", + New: "abcdefg", + RequiresNew: true, + }, + }, + }, + }, + } + for _, tc := range cases { + t.Run(tc.Name, func(t *testing.T) { + m := schemaMap(tc.Schema) + d := newResourceDiff(m, tc.Config, tc.State, tc.Diff) + err := d.ForceNew(tc.Key) + switch { + case err != nil && !tc.ExpectedError: + t.Fatalf("bad: %s", err) + case err == nil && tc.ExpectedError: + t.Fatalf("Expected error, got none") + case err != nil && tc.ExpectedError: + return + } + for _, k := range d.UpdatedKeys() { + if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { + t.Fatalf("bad: %s", err) + } + } + if !reflect.DeepEqual(tc.Expected, tc.Diff) { + t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff)) + } + }) + } +} + +func TestClear(t *testing.T) { + cases := []resourceDiffTestCase{ + resourceDiffTestCase{ + Name: "basic primitive diff", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "baz", + }, + }, + }, + Key: "foo", + Expected: &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{}}, + }, + resourceDiffTestCase{ + Name: "non-computed key, should error", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Required: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "baz", + }, + }, + }, + Key: "foo", + ExpectedError: true, + }, + resourceDiffTestCase{ + Name: "multi-value, one removed", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + "one": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + "one": "two", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + "one": "three", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "baz", + }, + "one": &terraform.ResourceAttrDiff{ + Old: "two", + New: "three", + }, + }, + }, + Key: "one", + Expected: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "baz", + }, + }, + }, + }, + resourceDiffTestCase{ + Name: "basic sub-block diff", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "bar": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + "baz": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo.0.bar": "bar1", + "foo.0.baz": "baz1", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": []interface{}{ + map[string]interface{}{ + "bar": "bar2", + "baz": "baz1", + }, + }, + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.0.bar": &terraform.ResourceAttrDiff{ + Old: "bar1", + New: "bar2", + }, + }, + }, + Key: "foo.0.bar", + Expected: &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{}}, + }, + resourceDiffTestCase{ + Name: "sub-block diff only partial clear", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "bar": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + "baz": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo.0.bar": "bar1", + "foo.0.baz": "baz1", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": []interface{}{ + map[string]interface{}{ + "bar": "bar2", + "baz": "baz2", + }, + }, + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.0.bar": &terraform.ResourceAttrDiff{ + Old: "bar1", + New: "bar2", + }, + "foo.0.baz": &terraform.ResourceAttrDiff{ + Old: "baz1", + New: "baz2", + }, + }, + }, + Key: "foo.0.bar", + Expected: &terraform.InstanceDiff{Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo.0.baz": &terraform.ResourceAttrDiff{ + Old: "baz1", + New: "baz2", + }, + }}, + }, + } + for _, tc := range cases { + t.Run(tc.Name, func(t *testing.T) { + m := schemaMap(tc.Schema) + d := newResourceDiff(m, tc.Config, tc.State, tc.Diff) + err := d.Clear(tc.Key) + switch { + case err != nil && !tc.ExpectedError: + t.Fatalf("bad: %s", err) + case err == nil && tc.ExpectedError: + t.Fatalf("Expected error, got none") + case err != nil && tc.ExpectedError: + return + } + for _, k := range d.UpdatedKeys() { + if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { + t.Fatalf("bad: %s", err) + } + } + if !reflect.DeepEqual(tc.Expected, tc.Diff) { + t.Fatalf("Expected %s, got %s", spew.Sdump(tc.Expected), spew.Sdump(tc.Diff)) + } + }) + } +} + +func TestGetChangedKeysPrefix(t *testing.T) { + cases := []resourceDiffTestCase{ + resourceDiffTestCase{ + Name: "basic primitive diff", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "foo": "baz", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "baz", + }, + }, + }, + Key: "foo", + ExpectedKeys: []string{ + "foo", + }, + }, + resourceDiffTestCase{ + Name: "nested field filtering", + Schema: map[string]*Schema{ + "testfield": &Schema{ + Type: TypeString, + Required: true, + }, + "foo": &Schema{ + Type: TypeList, + Required: true, + MaxItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "bar": { + Type: TypeString, + Optional: true, + }, + "baz": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "testfield": "blablah", + "foo.#": "1", + "foo.0.bar": "abc", + "foo.0.baz": "xyz", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "testfield": "modified", + "foo": []interface{}{ + map[string]interface{}{ + "bar": "abcdefg", + "baz": "changed", + }, + }, + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "testfield": &terraform.ResourceAttrDiff{ + Old: "blablah", + New: "modified", + }, + "foo.0.bar": &terraform.ResourceAttrDiff{ + Old: "abc", + New: "abcdefg", + }, + "foo.0.baz": &terraform.ResourceAttrDiff{ + Old: "xyz", + New: "changed", + }, + }, + }, + Key: "foo", + ExpectedKeys: []string{ + "foo.0.bar", + "foo.0.baz", + }, + }, + } + for _, tc := range cases { + t.Run(tc.Name, func(t *testing.T) { + m := schemaMap(tc.Schema) + d := newResourceDiff(m, tc.Config, tc.State, tc.Diff) + keys := d.GetChangedKeysPrefix(tc.Key) + + for _, k := range d.UpdatedKeys() { + if err := m.diff(k, m[k], tc.Diff, d, false); err != nil { + t.Fatalf("bad: %s", err) + } + } + + sort.Strings(keys) + + if !reflect.DeepEqual(tc.ExpectedKeys, keys) { + t.Fatalf("Expected %s, got %s", spew.Sdump(tc.ExpectedKeys), spew.Sdump(keys)) + } + }) + } +} + +func TestResourceDiffGetOkExists(t *testing.T) { + cases := []struct { + Name string + Schema map[string]*Schema + State *terraform.InstanceState + Config *terraform.ResourceConfig + Diff *terraform.InstanceDiff + Key string + Value interface{} + Ok bool + }{ + /* + * Primitives + */ + { + Name: "string-literal-empty", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "", + }, + }, + }, + + Key: "availability_zone", + Value: "", + Ok: true, + }, + + { + Name: "string-computed-empty", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + + Key: "availability_zone", + Value: "", + Ok: false, + }, + + { + Name: "string-optional-computed-nil-diff", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + Config: nil, + + Diff: nil, + + Key: "availability_zone", + Value: "", + Ok: false, + }, + + /* + * Lists + */ + + { + Name: "list-optional", + Schema: map[string]*Schema{ + "ports": { + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + Config: nil, + + Diff: nil, + + Key: "ports", + Value: []interface{}{}, + Ok: false, + }, + + /* + * Map + */ + + { + Name: "map-optional", + Schema: map[string]*Schema{ + "ports": { + Type: TypeMap, + Optional: true, + }, + }, + + State: nil, + Config: nil, + + Diff: nil, + + Key: "ports", + Value: map[string]interface{}{}, + Ok: false, + }, + + /* + * Set + */ + + { + Name: "set-optional", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: nil, + Config: nil, + + Diff: nil, + + Key: "ports", + Value: []interface{}{}, + Ok: false, + }, + + { + Name: "set-optional-key", + Schema: map[string]*Schema{ + "ports": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { return a.(int) }, + }, + }, + + State: nil, + Config: nil, + + Diff: nil, + + Key: "ports.0", + Value: 0, + Ok: false, + }, + + { + Name: "bool-literal-empty", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeBool, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + Config: nil, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "", + }, + }, + }, + + Key: "availability_zone", + Value: false, + Ok: true, + }, + + { + Name: "bool-literal-set", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeBool, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + New: "true", + }, + }, + }, + + Key: "availability_zone", + Value: true, + Ok: true, + }, + { + Name: "value-in-config", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "availability_zone": "foo", + }), + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + + Key: "availability_zone", + Value: "foo", + Ok: true, + }, + { + Name: "new-value-in-config", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + }, + }, + + State: nil, + Config: testConfig(t, map[string]interface{}{ + "availability_zone": "foo", + }), + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + }, + }, + }, + + Key: "availability_zone", + Value: "foo", + Ok: true, + }, + { + Name: "optional-computed-value-in-config", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "availability_zone": "bar", + }), + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "foo", + New: "bar", + }, + }, + }, + + Key: "availability_zone", + Value: "bar", + Ok: true, + }, + { + Name: "removed-value", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig(t, map[string]interface{}{}), + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "foo", + New: "", + NewRemoved: true, + }, + }, + }, + + Key: "availability_zone", + Value: "", + Ok: true, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) + + v, ok := d.GetOkExists(tc.Key) + if s, ok := v.(*Set); ok { + v = s.List() + } + + if !reflect.DeepEqual(v, tc.Value) { + t.Fatalf("Bad %s: \n%#v", tc.Name, v) + } + if ok != tc.Ok { + t.Fatalf("%s: expected ok: %t, got: %t", tc.Name, tc.Ok, ok) + } + }) + } +} + +func TestResourceDiffGetOkExistsSetNew(t *testing.T) { + tc := struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Key string + Value interface{} + Ok bool + }{ + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + + Key: "availability_zone", + Value: "foobar", + Ok: true, + } + + d := newResourceDiff(tc.Schema, testConfig(t, map[string]interface{}{}), tc.State, tc.Diff) + d.SetNew(tc.Key, tc.Value) + + v, ok := d.GetOkExists(tc.Key) + if s, ok := v.(*Set); ok { + v = s.List() + } + + if !reflect.DeepEqual(v, tc.Value) { + t.Fatalf("Bad: \n%#v", v) + } + if ok != tc.Ok { + t.Fatalf("expected ok: %t, got: %t", tc.Ok, ok) + } +} + +func TestResourceDiffGetOkExistsSetNewComputed(t *testing.T) { + tc := struct { + Schema map[string]*Schema + State *terraform.InstanceState + Diff *terraform.InstanceDiff + Key string + Value interface{} + Ok bool + }{ + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + + Key: "availability_zone", + Value: "foobar", + Ok: false, + } + + d := newResourceDiff(tc.Schema, testConfig(t, map[string]interface{}{}), tc.State, tc.Diff) + d.SetNewComputed(tc.Key) + + _, ok := d.GetOkExists(tc.Key) + + if ok != tc.Ok { + t.Fatalf("expected ok: %t, got: %t", tc.Ok, ok) + } +} + +func TestResourceDiffNewValueKnown(t *testing.T) { + cases := []struct { + Name string + Schema map[string]*Schema + State *terraform.InstanceState + Config *terraform.ResourceConfig + Diff *terraform.InstanceDiff + Key string + Expected bool + }{ + { + Name: "in config, no state", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + }, + }, + State: nil, + Config: testConfig(t, map[string]interface{}{ + "availability_zone": "foo", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + }, + }, + }, + Key: "availability_zone", + Expected: true, + }, + { + Name: "in config, has state, no diff", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "availability_zone": "foo", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + Key: "availability_zone", + Expected: true, + }, + { + Name: "computed attribute, in state, no diff", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig(t, map[string]interface{}{}), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + Key: "availability_zone", + Expected: true, + }, + { + Name: "optional and computed attribute, in state, no config", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig(t, map[string]interface{}{}), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + Key: "availability_zone", + Expected: true, + }, + { + Name: "optional and computed attribute, in state, with config", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig(t, map[string]interface{}{ + "availability_zone": "foo", + }), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + Key: "availability_zone", + Expected: true, + }, + { + Name: "computed value, through config reader", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig( + t, + map[string]interface{}{ + "availability_zone": hcl2shim.UnknownVariableValue, + }, + ), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + Key: "availability_zone", + Expected: false, + }, + { + Name: "computed value, through diff reader", + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig( + t, + map[string]interface{}{ + "availability_zone": hcl2shim.UnknownVariableValue, + }, + ), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "foo", + New: "", + NewComputed: true, + }, + }, + }, + Key: "availability_zone", + Expected: false, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) + + actual := d.NewValueKnown(tc.Key) + if tc.Expected != actual { + t.Fatalf("%s: expected ok: %t, got: %t", tc.Name, tc.Expected, actual) + } + }) + } +} + +func TestResourceDiffNewValueKnownSetNew(t *testing.T) { + tc := struct { + Schema map[string]*Schema + State *terraform.InstanceState + Config *terraform.ResourceConfig + Diff *terraform.InstanceDiff + Key string + Value interface{} + Expected bool + }{ + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig( + t, + map[string]interface{}{ + "availability_zone": hcl2shim.UnknownVariableValue, + }, + ), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "foo", + New: "", + NewComputed: true, + }, + }, + }, + Key: "availability_zone", + Value: "bar", + Expected: true, + } + + d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) + d.SetNew(tc.Key, tc.Value) + + actual := d.NewValueKnown(tc.Key) + if tc.Expected != actual { + t.Fatalf("expected ok: %t, got: %t", tc.Expected, actual) + } +} + +func TestResourceDiffNewValueKnownSetNewComputed(t *testing.T) { + tc := struct { + Schema map[string]*Schema + State *terraform.InstanceState + Config *terraform.ResourceConfig + Diff *terraform.InstanceDiff + Key string + Expected bool + }{ + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Computed: true, + }, + }, + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + Config: testConfig(t, map[string]interface{}{}), + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + }, + Key: "availability_zone", + Expected: false, + } + + d := newResourceDiff(tc.Schema, tc.Config, tc.State, tc.Diff) + d.SetNewComputed(tc.Key) + + actual := d.NewValueKnown(tc.Key) + if tc.Expected != actual { + t.Fatalf("expected ok: %t, got: %t", tc.Expected, actual) + } +} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_importer.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_importer.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/resource_importer.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_importer.go diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_test.go new file mode 100644 index 00000000..954b1a70 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_test.go @@ -0,0 +1,1687 @@ +package schema + +import ( + "encoding/json" + "fmt" + "reflect" + "strconv" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform/configs/hcl2shim" + "github.com/hashicorp/terraform/internal/legacy/terraform" + + "github.com/zclconf/go-cty/cty" + ctyjson "github.com/zclconf/go-cty/cty/json" +) + +func TestResourceApply_create(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + called := false + r.Create = func(d *ResourceData, m interface{}) error { + called = true + d.SetId("foo") + return nil + } + + var s *terraform.InstanceState = nil + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + New: "42", + }, + }, + } + + actual, err := r.Apply(s, d, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !called { + t.Fatal("not called") + } + + expected := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "42", + }, + Meta: map[string]interface{}{ + "schema_version": "2", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceApply_Timeout_state(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + Timeouts: &ResourceTimeout{ + Create: DefaultTimeout(40 * time.Minute), + Update: DefaultTimeout(80 * time.Minute), + Delete: DefaultTimeout(40 * time.Minute), + }, + } + + called := false + r.Create = func(d *ResourceData, m interface{}) error { + called = true + d.SetId("foo") + return nil + } + + var s *terraform.InstanceState = nil + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + New: "42", + }, + }, + } + + diffTimeout := &ResourceTimeout{ + Create: DefaultTimeout(40 * time.Minute), + Update: DefaultTimeout(80 * time.Minute), + Delete: DefaultTimeout(40 * time.Minute), + } + + if err := diffTimeout.DiffEncode(d); err != nil { + t.Fatalf("Error encoding timeout to diff: %s", err) + } + + actual, err := r.Apply(s, d, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !called { + t.Fatal("not called") + } + + expected := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "42", + }, + Meta: map[string]interface{}{ + "schema_version": "2", + TimeoutKey: expectedForValues(40, 0, 80, 40, 0), + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("Not equal in Timeout State:\n\texpected: %#v\n\tactual: %#v", expected.Meta, actual.Meta) + } +} + +// Regression test to ensure that the meta data is read from state, if a +// resource is destroyed and the timeout meta is no longer available from the +// config +func TestResourceApply_Timeout_destroy(t *testing.T) { + timeouts := &ResourceTimeout{ + Create: DefaultTimeout(40 * time.Minute), + Update: DefaultTimeout(80 * time.Minute), + Delete: DefaultTimeout(40 * time.Minute), + } + + r := &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + Timeouts: timeouts, + } + + called := false + var delTimeout time.Duration + r.Delete = func(d *ResourceData, m interface{}) error { + delTimeout = d.Timeout(TimeoutDelete) + called = true + return nil + } + + s := &terraform.InstanceState{ + ID: "bar", + } + + if err := timeouts.StateEncode(s); err != nil { + t.Fatalf("Error encoding to state: %s", err) + } + + d := &terraform.InstanceDiff{ + Destroy: true, + } + + actual, err := r.Apply(s, d, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !called { + t.Fatal("delete not called") + } + + if *timeouts.Delete != delTimeout { + t.Fatalf("timeouts don't match, expected (%#v), got (%#v)", timeouts.Delete, delTimeout) + } + + if actual != nil { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceDiff_Timeout_diff(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + Timeouts: &ResourceTimeout{ + Create: DefaultTimeout(40 * time.Minute), + Update: DefaultTimeout(80 * time.Minute), + Delete: DefaultTimeout(40 * time.Minute), + }, + } + + r.Create = func(d *ResourceData, m interface{}) error { + d.SetId("foo") + return nil + } + + conf := terraform.NewResourceConfigRaw( + map[string]interface{}{ + "foo": 42, + TimeoutsConfigKey: map[string]interface{}{ + "create": "2h", + }, + }, + ) + var s *terraform.InstanceState + + actual, err := r.Diff(s, conf, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + New: "42", + }, + }, + } + + diffTimeout := &ResourceTimeout{ + Create: DefaultTimeout(120 * time.Minute), + Update: DefaultTimeout(80 * time.Minute), + Delete: DefaultTimeout(40 * time.Minute), + } + + if err := diffTimeout.DiffEncode(expected); err != nil { + t.Fatalf("Error encoding timeout to diff: %s", err) + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("Not equal Meta in Timeout Diff:\n\texpected: %#v\n\tactual: %#v", expected.Meta, actual.Meta) + } +} + +func TestResourceDiff_CustomizeFunc(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + var called bool + + r.CustomizeDiff = func(d *ResourceDiff, m interface{}) error { + called = true + return nil + } + + conf := terraform.NewResourceConfigRaw( + map[string]interface{}{ + "foo": 42, + }, + ) + + var s *terraform.InstanceState + + _, err := r.Diff(s, conf, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !called { + t.Fatalf("diff customization not called") + } +} + +func TestResourceApply_destroy(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + called := false + r.Delete = func(d *ResourceData, m interface{}) error { + called = true + return nil + } + + s := &terraform.InstanceState{ + ID: "bar", + } + + d := &terraform.InstanceDiff{ + Destroy: true, + } + + actual, err := r.Apply(s, d, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !called { + t.Fatal("delete not called") + } + + if actual != nil { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceApply_destroyCreate(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + + "tags": &Schema{ + Type: TypeMap, + Optional: true, + Computed: true, + }, + }, + } + + change := false + r.Create = func(d *ResourceData, m interface{}) error { + change = d.HasChange("tags") + d.SetId("foo") + return nil + } + r.Delete = func(d *ResourceData, m interface{}) error { + return nil + } + + var s *terraform.InstanceState = &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "foo": "bar", + "tags.Name": "foo", + }, + } + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + New: "42", + RequiresNew: true, + }, + "tags.Name": &terraform.ResourceAttrDiff{ + Old: "foo", + New: "foo", + RequiresNew: true, + }, + }, + } + + actual, err := r.Apply(s, d, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !change { + t.Fatal("should have change") + } + + expected := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "42", + "tags.%": "1", + "tags.Name": "foo", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceApply_destroyPartial(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + SchemaVersion: 3, + } + + r.Delete = func(d *ResourceData, m interface{}) error { + d.Set("foo", 42) + return fmt.Errorf("some error") + } + + s := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "foo": "12", + }, + } + + d := &terraform.InstanceDiff{ + Destroy: true, + } + + actual, err := r.Apply(s, d, nil) + if err == nil { + t.Fatal("should error") + } + + expected := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + "foo": "42", + }, + Meta: map[string]interface{}{ + "schema_version": "3", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("expected:\n%#v\n\ngot:\n%#v", expected, actual) + } +} + +func TestResourceApply_update(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Update = func(d *ResourceData, m interface{}) error { + d.Set("foo", 42) + return nil + } + + s := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "foo": "12", + }, + } + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + New: "13", + }, + }, + } + + actual, err := r.Apply(s, d, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "42", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceApply_updateNoCallback(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Update = nil + + s := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "foo": "12", + }, + } + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + New: "13", + }, + }, + } + + actual, err := r.Apply(s, d, nil) + if err == nil { + t.Fatal("should error") + } + + expected := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "foo": "12", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceApply_isNewResource(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + } + + updateFunc := func(d *ResourceData, m interface{}) error { + d.Set("foo", "updated") + if d.IsNewResource() { + d.Set("foo", "new-resource") + } + return nil + } + r.Create = func(d *ResourceData, m interface{}) error { + d.SetId("foo") + d.Set("foo", "created") + return updateFunc(d, m) + } + r.Update = updateFunc + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + New: "bla-blah", + }, + }, + } + + // positive test + var s *terraform.InstanceState = nil + + actual, err := r.Apply(s, d, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "new-resource", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("actual: %#v\nexpected: %#v", + actual, expected) + } + + // negative test + s = &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "new-resource", + }, + } + + actual, err = r.Apply(s, d, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected = &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "updated", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("actual: %#v\nexpected: %#v", + actual, expected) + } +} + +func TestResourceInternalValidate(t *testing.T) { + cases := []struct { + In *Resource + Writable bool + Err bool + }{ + 0: { + nil, + true, + true, + }, + + // No optional and no required + 1: { + &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + Required: true, + }, + }, + }, + true, + true, + }, + + // Update undefined for non-ForceNew field + 2: { + &Resource{ + Create: func(d *ResourceData, meta interface{}) error { return nil }, + Schema: map[string]*Schema{ + "boo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + }, + true, + true, + }, + + // Update defined for ForceNew field + 3: { + &Resource{ + Create: func(d *ResourceData, meta interface{}) error { return nil }, + Update: func(d *ResourceData, meta interface{}) error { return nil }, + Schema: map[string]*Schema{ + "goo": &Schema{ + Type: TypeInt, + Optional: true, + ForceNew: true, + }, + }, + }, + true, + true, + }, + + // non-writable doesn't need Update, Create or Delete + 4: { + &Resource{ + Schema: map[string]*Schema{ + "goo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + }, + false, + false, + }, + + // non-writable *must not* have Create + 5: { + &Resource{ + Create: func(d *ResourceData, meta interface{}) error { return nil }, + Schema: map[string]*Schema{ + "goo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + }, + false, + true, + }, + + // writable must have Read + 6: { + &Resource{ + Create: func(d *ResourceData, meta interface{}) error { return nil }, + Update: func(d *ResourceData, meta interface{}) error { return nil }, + Delete: func(d *ResourceData, meta interface{}) error { return nil }, + Schema: map[string]*Schema{ + "goo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + }, + true, + true, + }, + + // writable must have Delete + 7: { + &Resource{ + Create: func(d *ResourceData, meta interface{}) error { return nil }, + Read: func(d *ResourceData, meta interface{}) error { return nil }, + Update: func(d *ResourceData, meta interface{}) error { return nil }, + Schema: map[string]*Schema{ + "goo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + }, + true, + true, + }, + + 8: { // Reserved name at root should be disallowed + &Resource{ + Create: func(d *ResourceData, meta interface{}) error { return nil }, + Read: func(d *ResourceData, meta interface{}) error { return nil }, + Update: func(d *ResourceData, meta interface{}) error { return nil }, + Delete: func(d *ResourceData, meta interface{}) error { return nil }, + Schema: map[string]*Schema{ + "count": { + Type: TypeInt, + Optional: true, + }, + }, + }, + true, + true, + }, + + 9: { // Reserved name at nested levels should be allowed + &Resource{ + Create: func(d *ResourceData, meta interface{}) error { return nil }, + Read: func(d *ResourceData, meta interface{}) error { return nil }, + Update: func(d *ResourceData, meta interface{}) error { return nil }, + Delete: func(d *ResourceData, meta interface{}) error { return nil }, + Schema: map[string]*Schema{ + "parent_list": &Schema{ + Type: TypeString, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "provisioner": { + Type: TypeString, + Optional: true, + }, + }, + }, + }, + }, + }, + true, + false, + }, + + 10: { // Provider reserved name should be allowed in resource + &Resource{ + Create: func(d *ResourceData, meta interface{}) error { return nil }, + Read: func(d *ResourceData, meta interface{}) error { return nil }, + Update: func(d *ResourceData, meta interface{}) error { return nil }, + Delete: func(d *ResourceData, meta interface{}) error { return nil }, + Schema: map[string]*Schema{ + "alias": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + }, + true, + false, + }, + + 11: { // ID should be allowed in data source + &Resource{ + Read: func(d *ResourceData, meta interface{}) error { return nil }, + Schema: map[string]*Schema{ + "id": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + }, + false, + false, + }, + + 12: { // Deprecated ID should be allowed in resource + &Resource{ + Create: func(d *ResourceData, meta interface{}) error { return nil }, + Read: func(d *ResourceData, meta interface{}) error { return nil }, + Update: func(d *ResourceData, meta interface{}) error { return nil }, + Delete: func(d *ResourceData, meta interface{}) error { return nil }, + Schema: map[string]*Schema{ + "id": &Schema{ + Type: TypeString, + Optional: true, + Deprecated: "Use x_id instead", + }, + }, + }, + true, + false, + }, + + 13: { // non-writable must not define CustomizeDiff + &Resource{ + Read: func(d *ResourceData, meta interface{}) error { return nil }, + Schema: map[string]*Schema{ + "goo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + CustomizeDiff: func(*ResourceDiff, interface{}) error { return nil }, + }, + false, + true, + }, + 14: { // Deprecated resource + &Resource{ + Read: func(d *ResourceData, meta interface{}) error { return nil }, + Schema: map[string]*Schema{ + "goo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + DeprecationMessage: "This resource has been deprecated.", + }, + true, + true, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + sm := schemaMap{} + if tc.In != nil { + sm = schemaMap(tc.In.Schema) + } + + err := tc.In.InternalValidate(sm, tc.Writable) + if err != nil && !tc.Err { + t.Fatalf("%d: expected validation to pass: %s", i, err) + } + if err == nil && tc.Err { + t.Fatalf("%d: expected validation to fail", i) + } + }) + } +} + +func TestResourceRefresh(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Read = func(d *ResourceData, m interface{}) error { + if m != 42 { + return fmt.Errorf("meta not passed") + } + + return d.Set("foo", d.Get("foo").(int)+1) + } + + s := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "foo": "12", + }, + } + + expected := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + "foo": "13", + }, + Meta: map[string]interface{}{ + "schema_version": "2", + }, + } + + actual, err := r.Refresh(s, 42) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceRefresh_blankId(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Read = func(d *ResourceData, m interface{}) error { + d.SetId("foo") + return nil + } + + s := &terraform.InstanceState{ + ID: "", + Attributes: map[string]string{}, + } + + actual, err := r.Refresh(s, 42) + if err != nil { + t.Fatalf("err: %s", err) + } + if actual != nil { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceRefresh_delete(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Read = func(d *ResourceData, m interface{}) error { + d.SetId("") + return nil + } + + s := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "foo": "12", + }, + } + + actual, err := r.Refresh(s, 42) + if err != nil { + t.Fatalf("err: %s", err) + } + + if actual != nil { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceRefresh_existsError(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Exists = func(*ResourceData, interface{}) (bool, error) { + return false, fmt.Errorf("error") + } + + r.Read = func(d *ResourceData, m interface{}) error { + panic("shouldn't be called") + } + + s := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "foo": "12", + }, + } + + actual, err := r.Refresh(s, 42) + if err == nil { + t.Fatalf("should error") + } + if !reflect.DeepEqual(actual, s) { + t.Fatalf("bad: %#v", actual) + } +} + +func TestResourceRefresh_noExists(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Exists = func(*ResourceData, interface{}) (bool, error) { + return false, nil + } + + r.Read = func(d *ResourceData, m interface{}) error { + panic("shouldn't be called") + } + + s := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "foo": "12", + }, + } + + actual, err := r.Refresh(s, 42) + if err != nil { + t.Fatalf("err: %s", err) + } + if actual != nil { + t.Fatalf("should have no state") + } +} + +func TestResourceRefresh_needsMigration(t *testing.T) { + // Schema v2 it deals only in newfoo, which tracks foo as an int + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "newfoo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Read = func(d *ResourceData, m interface{}) error { + return d.Set("newfoo", d.Get("newfoo").(int)+1) + } + + r.MigrateState = func( + v int, + s *terraform.InstanceState, + meta interface{}) (*terraform.InstanceState, error) { + // Real state migration functions will probably switch on this value, + // but we'll just assert on it for now. + if v != 1 { + t.Fatalf("Expected StateSchemaVersion to be 1, got %d", v) + } + + if meta != 42 { + t.Fatal("Expected meta to be passed through to the migration function") + } + + oldfoo, err := strconv.ParseFloat(s.Attributes["oldfoo"], 64) + if err != nil { + t.Fatalf("err: %#v", err) + } + s.Attributes["newfoo"] = strconv.Itoa(int(oldfoo * 10)) + delete(s.Attributes, "oldfoo") + + return s, nil + } + + // State is v1 and deals in oldfoo, which tracked foo as a float at 1/10th + // the scale of newfoo + s := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "oldfoo": "1.2", + }, + Meta: map[string]interface{}{ + "schema_version": "1", + }, + } + + actual, err := r.Refresh(s, 42) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + "newfoo": "13", + }, + Meta: map[string]interface{}{ + "schema_version": "2", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad:\n\nexpected: %#v\ngot: %#v", expected, actual) + } +} + +func TestResourceRefresh_noMigrationNeeded(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "newfoo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Read = func(d *ResourceData, m interface{}) error { + return d.Set("newfoo", d.Get("newfoo").(int)+1) + } + + r.MigrateState = func( + v int, + s *terraform.InstanceState, + meta interface{}) (*terraform.InstanceState, error) { + t.Fatal("Migrate function shouldn't be called!") + return nil, nil + } + + s := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "newfoo": "12", + }, + Meta: map[string]interface{}{ + "schema_version": "2", + }, + } + + actual, err := r.Refresh(s, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + "newfoo": "13", + }, + Meta: map[string]interface{}{ + "schema_version": "2", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad:\n\nexpected: %#v\ngot: %#v", expected, actual) + } +} + +func TestResourceRefresh_stateSchemaVersionUnset(t *testing.T) { + r := &Resource{ + // Version 1 > Version 0 + SchemaVersion: 1, + Schema: map[string]*Schema{ + "newfoo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Read = func(d *ResourceData, m interface{}) error { + return d.Set("newfoo", d.Get("newfoo").(int)+1) + } + + r.MigrateState = func( + v int, + s *terraform.InstanceState, + meta interface{}) (*terraform.InstanceState, error) { + s.Attributes["newfoo"] = s.Attributes["oldfoo"] + return s, nil + } + + s := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "oldfoo": "12", + }, + } + + actual, err := r.Refresh(s, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + "newfoo": "13", + }, + Meta: map[string]interface{}{ + "schema_version": "1", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad:\n\nexpected: %#v\ngot: %#v", expected, actual) + } +} + +func TestResourceRefresh_migrateStateErr(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "newfoo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Read = func(d *ResourceData, m interface{}) error { + t.Fatal("Read should never be called!") + return nil + } + + r.MigrateState = func( + v int, + s *terraform.InstanceState, + meta interface{}) (*terraform.InstanceState, error) { + return s, fmt.Errorf("triggering an error") + } + + s := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "oldfoo": "12", + }, + } + + _, err := r.Refresh(s, nil) + if err == nil { + t.Fatal("expected error, but got none!") + } +} + +func TestResourceData(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + state := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "42", + }, + } + + data := r.Data(state) + if data.Id() != "foo" { + t.Fatalf("err: %s", data.Id()) + } + if v := data.Get("foo"); v != 42 { + t.Fatalf("bad: %#v", v) + } + + // Set expectations + state.Meta = map[string]interface{}{ + "schema_version": "2", + } + + result := data.State() + if !reflect.DeepEqual(result, state) { + t.Fatalf("bad: %#v", result) + } +} + +func TestResourceData_blank(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + data := r.Data(nil) + if data.Id() != "" { + t.Fatalf("err: %s", data.Id()) + } + if v := data.Get("foo"); v != 0 { + t.Fatalf("bad: %#v", v) + } +} + +func TestResourceData_timeouts(t *testing.T) { + one := 1 * time.Second + two := 2 * time.Second + three := 3 * time.Second + four := 4 * time.Second + five := 5 * time.Second + + timeouts := &ResourceTimeout{ + Create: &one, + Read: &two, + Update: &three, + Delete: &four, + Default: &five, + } + + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + Timeouts: timeouts, + } + + data := r.Data(nil) + if data.Id() != "" { + t.Fatalf("err: %s", data.Id()) + } + + if !reflect.DeepEqual(timeouts, data.timeouts) { + t.Fatalf("incorrect ResourceData timeouts: %#v\n", *data.timeouts) + } +} + +func TestResource_UpgradeState(t *testing.T) { + // While this really only calls itself and therefore doesn't test any of + // the Resource code directly, it still serves as an example of registering + // a StateUpgrader. + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "newfoo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + r.StateUpgraders = []StateUpgrader{ + { + Version: 1, + Type: cty.Object(map[string]cty.Type{ + "id": cty.String, + "oldfoo": cty.Number, + }), + Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + + oldfoo, ok := m["oldfoo"].(float64) + if !ok { + t.Fatalf("expected 1.2, got %#v", m["oldfoo"]) + } + m["newfoo"] = int(oldfoo * 10) + delete(m, "oldfoo") + + return m, nil + }, + }, + } + + oldStateAttrs := map[string]string{ + "id": "bar", + "oldfoo": "1.2", + } + + // convert the legacy flatmap state to the json equivalent + ty := r.StateUpgraders[0].Type + val, err := hcl2shim.HCL2ValueFromFlatmap(oldStateAttrs, ty) + if err != nil { + t.Fatal(err) + } + js, err := ctyjson.Marshal(val, ty) + if err != nil { + t.Fatal(err) + } + + // unmarshal the state using the json default types + var m map[string]interface{} + if err := json.Unmarshal(js, &m); err != nil { + t.Fatal(err) + } + + actual, err := r.StateUpgraders[0].Upgrade(m, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := map[string]interface{}{ + "id": "bar", + "newfoo": 12, + } + + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("expected: %#v\ngot: %#v\n", expected, actual) + } +} + +func TestResource_ValidateUpgradeState(t *testing.T) { + r := &Resource{ + SchemaVersion: 3, + Schema: map[string]*Schema{ + "newfoo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + if err := r.InternalValidate(nil, true); err != nil { + t.Fatal(err) + } + + r.StateUpgraders = append(r.StateUpgraders, StateUpgrader{ + Version: 2, + Type: cty.Object(map[string]cty.Type{ + "id": cty.String, + }), + Upgrade: func(m map[string]interface{}, _ interface{}) (map[string]interface{}, error) { + return m, nil + }, + }) + if err := r.InternalValidate(nil, true); err != nil { + t.Fatal(err) + } + + // check for missing type + r.StateUpgraders[0].Type = cty.Type{} + if err := r.InternalValidate(nil, true); err == nil { + t.Fatal("StateUpgrader must have type") + } + r.StateUpgraders[0].Type = cty.Object(map[string]cty.Type{ + "id": cty.String, + }) + + // check for missing Upgrade func + r.StateUpgraders[0].Upgrade = nil + if err := r.InternalValidate(nil, true); err == nil { + t.Fatal("StateUpgrader must have an Upgrade func") + } + r.StateUpgraders[0].Upgrade = func(m map[string]interface{}, _ interface{}) (map[string]interface{}, error) { + return m, nil + } + + // check for skipped version + r.StateUpgraders[0].Version = 0 + r.StateUpgraders = append(r.StateUpgraders, StateUpgrader{ + Version: 2, + Type: cty.Object(map[string]cty.Type{ + "id": cty.String, + }), + Upgrade: func(m map[string]interface{}, _ interface{}) (map[string]interface{}, error) { + return m, nil + }, + }) + if err := r.InternalValidate(nil, true); err == nil { + t.Fatal("StateUpgraders cannot skip versions") + } + + // add the missing version, but fail because it's still out of order + r.StateUpgraders = append(r.StateUpgraders, StateUpgrader{ + Version: 1, + Type: cty.Object(map[string]cty.Type{ + "id": cty.String, + }), + Upgrade: func(m map[string]interface{}, _ interface{}) (map[string]interface{}, error) { + return m, nil + }, + }) + if err := r.InternalValidate(nil, true); err == nil { + t.Fatal("upgraders must be defined in order") + } + + r.StateUpgraders[1], r.StateUpgraders[2] = r.StateUpgraders[2], r.StateUpgraders[1] + if err := r.InternalValidate(nil, true); err != nil { + t.Fatal(err) + } + + // can't add an upgrader for a schema >= the current version + r.StateUpgraders = append(r.StateUpgraders, StateUpgrader{ + Version: 3, + Type: cty.Object(map[string]cty.Type{ + "id": cty.String, + }), + Upgrade: func(m map[string]interface{}, _ interface{}) (map[string]interface{}, error) { + return m, nil + }, + }) + if err := r.InternalValidate(nil, true); err == nil { + t.Fatal("StateUpgraders cannot have a version >= current SchemaVersion") + } +} + +// The legacy provider will need to be able to handle both types of schema +// transformations, which has been retrofitted into the Refresh method. +func TestResource_migrateAndUpgrade(t *testing.T) { + r := &Resource{ + SchemaVersion: 4, + Schema: map[string]*Schema{ + "four": { + Type: TypeInt, + Required: true, + }, + }, + // this MigrateState will take the state to version 2 + MigrateState: func(v int, is *terraform.InstanceState, _ interface{}) (*terraform.InstanceState, error) { + switch v { + case 0: + _, ok := is.Attributes["zero"] + if !ok { + return nil, fmt.Errorf("zero not found in %#v", is.Attributes) + } + is.Attributes["one"] = "1" + delete(is.Attributes, "zero") + fallthrough + case 1: + _, ok := is.Attributes["one"] + if !ok { + return nil, fmt.Errorf("one not found in %#v", is.Attributes) + } + is.Attributes["two"] = "2" + delete(is.Attributes, "one") + default: + return nil, fmt.Errorf("invalid schema version %d", v) + } + return is, nil + }, + } + + r.Read = func(d *ResourceData, m interface{}) error { + return d.Set("four", 4) + } + + r.StateUpgraders = []StateUpgrader{ + { + Version: 2, + Type: cty.Object(map[string]cty.Type{ + "id": cty.String, + "two": cty.Number, + }), + Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + _, ok := m["two"].(float64) + if !ok { + return nil, fmt.Errorf("two not found in %#v", m) + } + m["three"] = float64(3) + delete(m, "two") + return m, nil + }, + }, + { + Version: 3, + Type: cty.Object(map[string]cty.Type{ + "id": cty.String, + "three": cty.Number, + }), + Upgrade: func(m map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + _, ok := m["three"].(float64) + if !ok { + return nil, fmt.Errorf("three not found in %#v", m) + } + m["four"] = float64(4) + delete(m, "three") + return m, nil + }, + }, + } + + testStates := []*terraform.InstanceState{ + { + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + "zero": "0", + }, + Meta: map[string]interface{}{ + "schema_version": "0", + }, + }, + { + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + "one": "1", + }, + Meta: map[string]interface{}{ + "schema_version": "1", + }, + }, + { + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + "two": "2", + }, + Meta: map[string]interface{}{ + "schema_version": "2", + }, + }, + { + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + "three": "3", + }, + Meta: map[string]interface{}{ + "schema_version": "3", + }, + }, + { + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + "four": "4", + }, + Meta: map[string]interface{}{ + "schema_version": "4", + }, + }, + } + + for i, s := range testStates { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + newState, err := r.Refresh(s, nil) + if err != nil { + t.Fatal(err) + } + + expected := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + "four": "4", + }, + Meta: map[string]interface{}{ + "schema_version": "4", + }, + } + + if !cmp.Equal(expected, newState, equateEmpty) { + t.Fatal(cmp.Diff(expected, newState, equateEmpty)) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_timeout.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_timeout.go new file mode 100644 index 00000000..cf9654bc --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_timeout.go @@ -0,0 +1,263 @@ +package schema + +import ( + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform/configs/hcl2shim" + "github.com/hashicorp/terraform/internal/legacy/terraform" + "github.com/mitchellh/copystructure" +) + +const TimeoutKey = "e2bfb730-ecaa-11e6-8f88-34363bc7c4c0" +const TimeoutsConfigKey = "timeouts" + +const ( + TimeoutCreate = "create" + TimeoutRead = "read" + TimeoutUpdate = "update" + TimeoutDelete = "delete" + TimeoutDefault = "default" +) + +func timeoutKeys() []string { + return []string{ + TimeoutCreate, + TimeoutRead, + TimeoutUpdate, + TimeoutDelete, + TimeoutDefault, + } +} + +// could be time.Duration, int64 or float64 +func DefaultTimeout(tx interface{}) *time.Duration { + var td time.Duration + switch raw := tx.(type) { + case time.Duration: + return &raw + case int64: + td = time.Duration(raw) + case float64: + td = time.Duration(int64(raw)) + default: + log.Printf("[WARN] Unknown type in DefaultTimeout: %#v", tx) + } + return &td +} + +type ResourceTimeout struct { + Create, Read, Update, Delete, Default *time.Duration +} + +// ConfigDecode takes a schema and the configuration (available in Diff) and +// validates, parses the timeouts into `t` +func (t *ResourceTimeout) ConfigDecode(s *Resource, c *terraform.ResourceConfig) error { + if s.Timeouts != nil { + raw, err := copystructure.Copy(s.Timeouts) + if err != nil { + log.Printf("[DEBUG] Error with deep copy: %s", err) + } + *t = *raw.(*ResourceTimeout) + } + + if raw, ok := c.Config[TimeoutsConfigKey]; ok { + var rawTimeouts []map[string]interface{} + switch raw := raw.(type) { + case map[string]interface{}: + rawTimeouts = append(rawTimeouts, raw) + case []map[string]interface{}: + rawTimeouts = raw + case string: + if raw == hcl2shim.UnknownVariableValue { + // Timeout is not defined in the config + // Defaults will be used instead + return nil + } else { + log.Printf("[ERROR] Invalid timeout value: %q", raw) + return fmt.Errorf("Invalid Timeout value found") + } + case []interface{}: + for _, r := range raw { + if rMap, ok := r.(map[string]interface{}); ok { + rawTimeouts = append(rawTimeouts, rMap) + } else { + // Go will not allow a fallthrough + log.Printf("[ERROR] Invalid timeout structure: %#v", raw) + return fmt.Errorf("Invalid Timeout structure found") + } + } + default: + log.Printf("[ERROR] Invalid timeout structure: %#v", raw) + return fmt.Errorf("Invalid Timeout structure found") + } + + for _, timeoutValues := range rawTimeouts { + for timeKey, timeValue := range timeoutValues { + // validate that we're dealing with the normal CRUD actions + var found bool + for _, key := range timeoutKeys() { + if timeKey == key { + found = true + break + } + } + + if !found { + return fmt.Errorf("Unsupported Timeout configuration key found (%s)", timeKey) + } + + // Get timeout + rt, err := time.ParseDuration(timeValue.(string)) + if err != nil { + return fmt.Errorf("Error parsing %q timeout: %s", timeKey, err) + } + + var timeout *time.Duration + switch timeKey { + case TimeoutCreate: + timeout = t.Create + case TimeoutUpdate: + timeout = t.Update + case TimeoutRead: + timeout = t.Read + case TimeoutDelete: + timeout = t.Delete + case TimeoutDefault: + timeout = t.Default + } + + // If the resource has not delcared this in the definition, then error + // with an unsupported message + if timeout == nil { + return unsupportedTimeoutKeyError(timeKey) + } + + *timeout = rt + } + return nil + } + } + + return nil +} + +func unsupportedTimeoutKeyError(key string) error { + return fmt.Errorf("Timeout Key (%s) is not supported", key) +} + +// DiffEncode, StateEncode, and MetaDecode are analogous to the Go stdlib JSONEncoder +// interface: they encode/decode a timeouts struct from an instance diff, which is +// where the timeout data is stored after a diff to pass into Apply. +// +// StateEncode encodes the timeout into the ResourceData's InstanceState for +// saving to state +// +func (t *ResourceTimeout) DiffEncode(id *terraform.InstanceDiff) error { + return t.metaEncode(id) +} + +func (t *ResourceTimeout) StateEncode(is *terraform.InstanceState) error { + return t.metaEncode(is) +} + +// metaEncode encodes the ResourceTimeout into a map[string]interface{} format +// and stores it in the Meta field of the interface it's given. +// Assumes the interface is either *terraform.InstanceState or +// *terraform.InstanceDiff, returns an error otherwise +func (t *ResourceTimeout) metaEncode(ids interface{}) error { + m := make(map[string]interface{}) + + if t.Create != nil { + m[TimeoutCreate] = t.Create.Nanoseconds() + } + if t.Read != nil { + m[TimeoutRead] = t.Read.Nanoseconds() + } + if t.Update != nil { + m[TimeoutUpdate] = t.Update.Nanoseconds() + } + if t.Delete != nil { + m[TimeoutDelete] = t.Delete.Nanoseconds() + } + if t.Default != nil { + m[TimeoutDefault] = t.Default.Nanoseconds() + // for any key above that is nil, if default is specified, we need to + // populate it with the default + for _, k := range timeoutKeys() { + if _, ok := m[k]; !ok { + m[k] = t.Default.Nanoseconds() + } + } + } + + // only add the Timeout to the Meta if we have values + if len(m) > 0 { + switch instance := ids.(type) { + case *terraform.InstanceDiff: + if instance.Meta == nil { + instance.Meta = make(map[string]interface{}) + } + instance.Meta[TimeoutKey] = m + case *terraform.InstanceState: + if instance.Meta == nil { + instance.Meta = make(map[string]interface{}) + } + instance.Meta[TimeoutKey] = m + default: + return fmt.Errorf("Error matching type for Diff Encode") + } + } + + return nil +} + +func (t *ResourceTimeout) StateDecode(id *terraform.InstanceState) error { + return t.metaDecode(id) +} +func (t *ResourceTimeout) DiffDecode(is *terraform.InstanceDiff) error { + return t.metaDecode(is) +} + +func (t *ResourceTimeout) metaDecode(ids interface{}) error { + var rawMeta interface{} + var ok bool + switch rawInstance := ids.(type) { + case *terraform.InstanceDiff: + rawMeta, ok = rawInstance.Meta[TimeoutKey] + if !ok { + return nil + } + case *terraform.InstanceState: + rawMeta, ok = rawInstance.Meta[TimeoutKey] + if !ok { + return nil + } + default: + return fmt.Errorf("Unknown or unsupported type in metaDecode: %#v", ids) + } + + times := rawMeta.(map[string]interface{}) + if len(times) == 0 { + return nil + } + + if v, ok := times[TimeoutCreate]; ok { + t.Create = DefaultTimeout(v) + } + if v, ok := times[TimeoutRead]; ok { + t.Read = DefaultTimeout(v) + } + if v, ok := times[TimeoutUpdate]; ok { + t.Update = DefaultTimeout(v) + } + if v, ok := times[TimeoutDelete]; ok { + t.Delete = DefaultTimeout(v) + } + if v, ok := times[TimeoutDefault]; ok { + t.Default = DefaultTimeout(v) + } + + return nil +} diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_timeout_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_timeout_test.go new file mode 100644 index 00000000..f5091755 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/resource_timeout_test.go @@ -0,0 +1,376 @@ +package schema + +import ( + "fmt" + "reflect" + "testing" + "time" + + "github.com/hashicorp/terraform/internal/legacy/terraform" +) + +func TestResourceTimeout_ConfigDecode_badkey(t *testing.T) { + cases := []struct { + Name string + // what the resource has defined in source + ResourceDefaultTimeout *ResourceTimeout + // configuration provider by user in tf file + Config map[string]interface{} + // what we expect the parsed ResourceTimeout to be + Expected *ResourceTimeout + // Should we have an error (key not defined in source) + ShouldErr bool + }{ + { + Name: "Source does not define 'delete' key", + ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 0), + Config: expectedConfigForValues(2, 0, 0, 1, 0), + Expected: timeoutForValues(10, 0, 5, 0, 0), + ShouldErr: true, + }, + { + Name: "Config overrides create", + ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 0), + Config: expectedConfigForValues(2, 0, 7, 0, 0), + Expected: timeoutForValues(2, 0, 7, 0, 0), + ShouldErr: false, + }, + { + Name: "Config overrides create, default provided. Should still have zero values", + ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 3), + Config: expectedConfigForValues(2, 0, 7, 0, 0), + Expected: timeoutForValues(2, 0, 7, 0, 3), + ShouldErr: false, + }, + { + Name: "Use something besides 'minutes'", + ResourceDefaultTimeout: timeoutForValues(10, 0, 5, 0, 3), + Config: map[string]interface{}{ + "create": "2h", + }, + Expected: timeoutForValues(120, 0, 5, 0, 3), + ShouldErr: false, + }, + } + + for i, c := range cases { + t.Run(fmt.Sprintf("%d-%s", i, c.Name), func(t *testing.T) { + r := &Resource{ + Timeouts: c.ResourceDefaultTimeout, + } + + conf := terraform.NewResourceConfigRaw( + map[string]interface{}{ + "foo": "bar", + TimeoutsConfigKey: c.Config, + }, + ) + + timeout := &ResourceTimeout{} + decodeErr := timeout.ConfigDecode(r, conf) + if c.ShouldErr { + if decodeErr == nil { + t.Fatalf("ConfigDecode case (%d): Expected bad timeout key: %s", i, decodeErr) + } + // should error, err was not nil, continue + return + } else { + if decodeErr != nil { + // should not error, error was not nil, fatal + t.Fatalf("decodeError was not nil: %s", decodeErr) + } + } + + if !reflect.DeepEqual(c.Expected, timeout) { + t.Fatalf("ConfigDecode match error case (%d).\nExpected:\n%#v\nGot:\n%#v", i, c.Expected, timeout) + } + }) + } +} + +func TestResourceTimeout_ConfigDecode(t *testing.T) { + r := &Resource{ + Timeouts: &ResourceTimeout{ + Create: DefaultTimeout(10 * time.Minute), + Update: DefaultTimeout(5 * time.Minute), + }, + } + + c := terraform.NewResourceConfigRaw( + map[string]interface{}{ + "foo": "bar", + TimeoutsConfigKey: map[string]interface{}{ + "create": "2m", + "update": "1m", + }, + }, + ) + + timeout := &ResourceTimeout{} + err := timeout.ConfigDecode(r, c) + if err != nil { + t.Fatalf("Expected good timeout returned:, %s", err) + } + + expected := &ResourceTimeout{ + Create: DefaultTimeout(2 * time.Minute), + Update: DefaultTimeout(1 * time.Minute), + } + + if !reflect.DeepEqual(timeout, expected) { + t.Fatalf("bad timeout decode.\nExpected:\n%#v\nGot:\n%#v\n", expected, timeout) + } +} + +func TestResourceTimeout_legacyConfigDecode(t *testing.T) { + r := &Resource{ + Timeouts: &ResourceTimeout{ + Create: DefaultTimeout(10 * time.Minute), + Update: DefaultTimeout(5 * time.Minute), + }, + } + + c := terraform.NewResourceConfigRaw( + map[string]interface{}{ + "foo": "bar", + TimeoutsConfigKey: []interface{}{ + map[string]interface{}{ + "create": "2m", + "update": "1m", + }, + }, + }, + ) + + timeout := &ResourceTimeout{} + err := timeout.ConfigDecode(r, c) + if err != nil { + t.Fatalf("Expected good timeout returned:, %s", err) + } + + expected := &ResourceTimeout{ + Create: DefaultTimeout(2 * time.Minute), + Update: DefaultTimeout(1 * time.Minute), + } + + if !reflect.DeepEqual(timeout, expected) { + t.Fatalf("bad timeout decode.\nExpected:\n%#v\nGot:\n%#v\n", expected, timeout) + } +} + +func TestResourceTimeout_DiffEncode_basic(t *testing.T) { + cases := []struct { + Timeout *ResourceTimeout + Expected map[string]interface{} + // Not immediately clear when an error would hit + ShouldErr bool + }{ + // Two fields + { + Timeout: timeoutForValues(10, 0, 5, 0, 0), + Expected: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 5, 0, 0)}, + ShouldErr: false, + }, + // Two fields, one is Default + { + Timeout: timeoutForValues(10, 0, 0, 0, 7), + Expected: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 0, 0, 7)}, + ShouldErr: false, + }, + // All fields + { + Timeout: timeoutForValues(10, 3, 4, 1, 7), + Expected: map[string]interface{}{TimeoutKey: expectedForValues(10, 3, 4, 1, 7)}, + ShouldErr: false, + }, + // No fields + { + Timeout: &ResourceTimeout{}, + Expected: nil, + ShouldErr: false, + }, + } + + for _, c := range cases { + state := &terraform.InstanceDiff{} + err := c.Timeout.DiffEncode(state) + if err != nil && !c.ShouldErr { + t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, state.Meta) + } + + // should maybe just compare [TimeoutKey] but for now we're assuming only + // that in Meta + if !reflect.DeepEqual(state.Meta, c.Expected) { + t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, state.Meta) + } + } + // same test cases but for InstanceState + for _, c := range cases { + state := &terraform.InstanceState{} + err := c.Timeout.StateEncode(state) + if err != nil && !c.ShouldErr { + t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, state.Meta) + } + + // should maybe just compare [TimeoutKey] but for now we're assuming only + // that in Meta + if !reflect.DeepEqual(state.Meta, c.Expected) { + t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, state.Meta) + } + } +} + +func TestResourceTimeout_MetaDecode_basic(t *testing.T) { + cases := []struct { + State *terraform.InstanceDiff + Expected *ResourceTimeout + // Not immediately clear when an error would hit + ShouldErr bool + }{ + // Two fields + { + State: &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 5, 0, 0)}}, + Expected: timeoutForValues(10, 0, 5, 0, 0), + ShouldErr: false, + }, + // Two fields, one is Default + { + State: &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 0, 0, 0, 7)}}, + Expected: timeoutForValues(10, 7, 7, 7, 7), + ShouldErr: false, + }, + // All fields + { + State: &terraform.InstanceDiff{Meta: map[string]interface{}{TimeoutKey: expectedForValues(10, 3, 4, 1, 7)}}, + Expected: timeoutForValues(10, 3, 4, 1, 7), + ShouldErr: false, + }, + // No fields + { + State: &terraform.InstanceDiff{}, + Expected: &ResourceTimeout{}, + ShouldErr: false, + }, + } + + for _, c := range cases { + rt := &ResourceTimeout{} + err := rt.DiffDecode(c.State) + if err != nil && !c.ShouldErr { + t.Fatalf("Error, expected:\n%#v\n got:\n%#v\n", c.Expected, rt) + } + + // should maybe just compare [TimeoutKey] but for now we're assuming only + // that in Meta + if !reflect.DeepEqual(rt, c.Expected) { + t.Fatalf("Encode not equal, expected:\n%#v\n\ngot:\n%#v\n", c.Expected, rt) + } + } +} + +func timeoutForValues(create, read, update, del, def int) *ResourceTimeout { + rt := ResourceTimeout{} + + if create != 0 { + rt.Create = DefaultTimeout(time.Duration(create) * time.Minute) + } + if read != 0 { + rt.Read = DefaultTimeout(time.Duration(read) * time.Minute) + } + if update != 0 { + rt.Update = DefaultTimeout(time.Duration(update) * time.Minute) + } + if del != 0 { + rt.Delete = DefaultTimeout(time.Duration(del) * time.Minute) + } + + if def != 0 { + rt.Default = DefaultTimeout(time.Duration(def) * time.Minute) + } + + return &rt +} + +// Generates a ResourceTimeout struct that should reflect the +// d.Timeout("key") results +func expectedTimeoutForValues(create, read, update, del, def int) *ResourceTimeout { + rt := ResourceTimeout{} + + defaultValues := []*int{&create, &read, &update, &del, &def} + for _, v := range defaultValues { + if *v == 0 { + *v = 20 + } + } + + if create != 0 { + rt.Create = DefaultTimeout(time.Duration(create) * time.Minute) + } + if read != 0 { + rt.Read = DefaultTimeout(time.Duration(read) * time.Minute) + } + if update != 0 { + rt.Update = DefaultTimeout(time.Duration(update) * time.Minute) + } + if del != 0 { + rt.Delete = DefaultTimeout(time.Duration(del) * time.Minute) + } + + if def != 0 { + rt.Default = DefaultTimeout(time.Duration(def) * time.Minute) + } + + return &rt +} + +func expectedForValues(create, read, update, del, def int) map[string]interface{} { + ex := make(map[string]interface{}) + + if create != 0 { + ex["create"] = DefaultTimeout(time.Duration(create) * time.Minute).Nanoseconds() + } + if read != 0 { + ex["read"] = DefaultTimeout(time.Duration(read) * time.Minute).Nanoseconds() + } + if update != 0 { + ex["update"] = DefaultTimeout(time.Duration(update) * time.Minute).Nanoseconds() + } + if del != 0 { + ex["delete"] = DefaultTimeout(time.Duration(del) * time.Minute).Nanoseconds() + } + + if def != 0 { + defNano := DefaultTimeout(time.Duration(def) * time.Minute).Nanoseconds() + ex["default"] = defNano + + for _, k := range timeoutKeys() { + if _, ok := ex[k]; !ok { + ex[k] = defNano + } + } + } + + return ex +} + +func expectedConfigForValues(create, read, update, delete, def int) map[string]interface{} { + ex := make(map[string]interface{}, 0) + + if create != 0 { + ex["create"] = fmt.Sprintf("%dm", create) + } + if read != 0 { + ex["read"] = fmt.Sprintf("%dm", read) + } + if update != 0 { + ex["update"] = fmt.Sprintf("%dm", update) + } + if delete != 0 { + ex["delete"] = fmt.Sprintf("%dm", delete) + } + + if def != 0 { + ex["default"] = fmt.Sprintf("%dm", def) + } + return ex +} diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/schema.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/schema.go new file mode 100644 index 00000000..488fcaaa --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/schema.go @@ -0,0 +1,1854 @@ +// schema is a high-level framework for easily writing new providers +// for Terraform. Usage of schema is recommended over attempting to write +// to the low-level plugin interfaces manually. +// +// schema breaks down provider creation into simple CRUD operations for +// resources. The logic of diffing, destroying before creating, updating +// or creating, etc. is all handled by the framework. The plugin author +// only needs to implement a configuration schema and the CRUD operations and +// everything else is meant to just work. +// +// A good starting point is to view the Provider structure. +package schema + +import ( + "context" + "fmt" + "os" + "reflect" + "regexp" + "sort" + "strconv" + "strings" + "sync" + + "github.com/hashicorp/terraform/configs/hcl2shim" + "github.com/hashicorp/terraform/internal/legacy/terraform" + "github.com/mitchellh/copystructure" + "github.com/mitchellh/mapstructure" +) + +// Name of ENV variable which (if not empty) prefers panic over error +const PanicOnErr = "TF_SCHEMA_PANIC_ON_ERROR" + +// type used for schema package context keys +type contextKey string + +var ( + protoVersionMu sync.Mutex + protoVersion5 = false +) + +func isProto5() bool { + protoVersionMu.Lock() + defer protoVersionMu.Unlock() + return protoVersion5 + +} + +// SetProto5 enables a feature flag for any internal changes required required +// to work with the new plugin protocol. This should not be called by +// provider. +func SetProto5() { + protoVersionMu.Lock() + defer protoVersionMu.Unlock() + protoVersion5 = true +} + +// Schema is used to describe the structure of a value. +// +// Read the documentation of the struct elements for important details. +type Schema struct { + // Type is the type of the value and must be one of the ValueType values. + // + // This type not only determines what type is expected/valid in configuring + // this value, but also what type is returned when ResourceData.Get is + // called. The types returned by Get are: + // + // TypeBool - bool + // TypeInt - int + // TypeFloat - float64 + // TypeString - string + // TypeList - []interface{} + // TypeMap - map[string]interface{} + // TypeSet - *schema.Set + // + Type ValueType + + // ConfigMode allows for overriding the default behaviors for mapping + // schema entries onto configuration constructs. + // + // By default, the Elem field is used to choose whether a particular + // schema is represented in configuration as an attribute or as a nested + // block; if Elem is a *schema.Resource then it's a block and it's an + // attribute otherwise. + // + // If Elem is *schema.Resource then setting ConfigMode to + // SchemaConfigModeAttr will force it to be represented in configuration + // as an attribute, which means that the Computed flag can be used to + // provide default elements when the argument isn't set at all, while still + // allowing the user to force zero elements by explicitly assigning an + // empty list. + // + // When Computed is set without Optional, the attribute is not settable + // in configuration at all and so SchemaConfigModeAttr is the automatic + // behavior, and SchemaConfigModeBlock is not permitted. + ConfigMode SchemaConfigMode + + // If one of these is set, then this item can come from the configuration. + // Both cannot be set. If Optional is set, the value is optional. If + // Required is set, the value is required. + // + // One of these must be set if the value is not computed. That is: + // value either comes from the config, is computed, or is both. + Optional bool + Required bool + + // If this is non-nil, the provided function will be used during diff + // of this field. If this is nil, a default diff for the type of the + // schema will be used. + // + // This allows comparison based on something other than primitive, list + // or map equality - for example SSH public keys may be considered + // equivalent regardless of trailing whitespace. + DiffSuppressFunc SchemaDiffSuppressFunc + + // If this is non-nil, then this will be a default value that is used + // when this item is not set in the configuration. + // + // DefaultFunc can be specified to compute a dynamic default. + // Only one of Default or DefaultFunc can be set. If DefaultFunc is + // used then its return value should be stable to avoid generating + // confusing/perpetual diffs. + // + // Changing either Default or the return value of DefaultFunc can be + // a breaking change, especially if the attribute in question has + // ForceNew set. If a default needs to change to align with changing + // assumptions in an upstream API then it may be necessary to also use + // the MigrateState function on the resource to change the state to match, + // or have the Read function adjust the state value to align with the + // new default. + // + // If Required is true above, then Default cannot be set. DefaultFunc + // can be set with Required. If the DefaultFunc returns nil, then there + // will be no default and the user will be asked to fill it in. + // + // If either of these is set, then the user won't be asked for input + // for this key if the default is not nil. + Default interface{} + DefaultFunc SchemaDefaultFunc + + // Description is used as the description for docs or asking for user + // input. It should be relatively short (a few sentences max) and should + // be formatted to fit a CLI. + Description string + + // InputDefault is the default value to use for when inputs are requested. + // This differs from Default in that if Default is set, no input is + // asked for. If Input is asked, this will be the default value offered. + InputDefault string + + // The fields below relate to diffs. + // + // If Computed is true, then the result of this value is computed + // (unless specified by config) on creation. + // + // If ForceNew is true, then a change in this resource necessitates + // the creation of a new resource. + // + // StateFunc is a function called to change the value of this before + // storing it in the state (and likewise before comparing for diffs). + // The use for this is for example with large strings, you may want + // to simply store the hash of it. + Computed bool + ForceNew bool + StateFunc SchemaStateFunc + + // The following fields are only set for a TypeList, TypeSet, or TypeMap. + // + // Elem represents the element type. For a TypeMap, it must be a *Schema + // with a Type that is one of the primitives: TypeString, TypeBool, + // TypeInt, or TypeFloat. Otherwise it may be either a *Schema or a + // *Resource. If it is *Schema, the element type is just a simple value. + // If it is *Resource, the element type is a complex structure, + // potentially managed via its own CRUD actions on the API. + Elem interface{} + + // The following fields are only set for a TypeList or TypeSet. + // + // MaxItems defines a maximum amount of items that can exist within a + // TypeSet or TypeList. Specific use cases would be if a TypeSet is being + // used to wrap a complex structure, however more than one instance would + // cause instability. + // + // MinItems defines a minimum amount of items that can exist within a + // TypeSet or TypeList. Specific use cases would be if a TypeSet is being + // used to wrap a complex structure, however less than one instance would + // cause instability. + // + // If the field Optional is set to true then MinItems is ignored and thus + // effectively zero. + MaxItems int + MinItems int + + // PromoteSingle originally allowed for a single element to be assigned + // where a primitive list was expected, but this no longer works from + // Terraform v0.12 onwards (Terraform Core will require a list to be set + // regardless of what this is set to) and so only applies to Terraform v0.11 + // and earlier, and so should be used only to retain this functionality + // for those still using v0.11 with a provider that formerly used this. + PromoteSingle bool + + // The following fields are only valid for a TypeSet type. + // + // Set defines a function to determine the unique ID of an item so that + // a proper set can be built. + Set SchemaSetFunc + + // ComputedWhen is a set of queries on the configuration. Whenever any + // of these things is changed, it will require a recompute (this requires + // that Computed is set to true). + // + // NOTE: This currently does not work. + ComputedWhen []string + + // ConflictsWith is a set of schema keys that conflict with this schema. + // This will only check that they're set in the _config_. This will not + // raise an error for a malfunctioning resource that sets a conflicting + // key. + ConflictsWith []string + + // When Deprecated is set, this attribute is deprecated. + // + // A deprecated field still works, but will probably stop working in near + // future. This string is the message shown to the user with instructions on + // how to address the deprecation. + Deprecated string + + // When Removed is set, this attribute has been removed from the schema + // + // Removed attributes can be left in the Schema to generate informative error + // messages for the user when they show up in resource configurations. + // This string is the message shown to the user with instructions on + // what do to about the removed attribute. + Removed string + + // ValidateFunc allows individual fields to define arbitrary validation + // logic. It is yielded the provided config value as an interface{} that is + // guaranteed to be of the proper Schema type, and it can yield warnings or + // errors based on inspection of that value. + // + // ValidateFunc is honored only when the schema's Type is set to TypeInt, + // TypeFloat, TypeString, TypeBool, or TypeMap. It is ignored for all other types. + ValidateFunc SchemaValidateFunc + + // Sensitive ensures that the attribute's value does not get displayed in + // logs or regular output. It should be used for passwords or other + // secret fields. Future versions of Terraform may encrypt these + // values. + Sensitive bool +} + +// SchemaConfigMode is used to influence how a schema item is mapped into a +// corresponding configuration construct, using the ConfigMode field of +// Schema. +type SchemaConfigMode int + +const ( + SchemaConfigModeAuto SchemaConfigMode = iota + SchemaConfigModeAttr + SchemaConfigModeBlock +) + +// SchemaDiffSuppressFunc is a function which can be used to determine +// whether a detected diff on a schema element is "valid" or not, and +// suppress it from the plan if necessary. +// +// Return true if the diff should be suppressed, false to retain it. +type SchemaDiffSuppressFunc func(k, old, new string, d *ResourceData) bool + +// SchemaDefaultFunc is a function called to return a default value for +// a field. +type SchemaDefaultFunc func() (interface{}, error) + +// EnvDefaultFunc is a helper function that returns the value of the +// given environment variable, if one exists, or the default value +// otherwise. +func EnvDefaultFunc(k string, dv interface{}) SchemaDefaultFunc { + return func() (interface{}, error) { + if v := os.Getenv(k); v != "" { + return v, nil + } + + return dv, nil + } +} + +// MultiEnvDefaultFunc is a helper function that returns the value of the first +// environment variable in the given list that returns a non-empty value. If +// none of the environment variables return a value, the default value is +// returned. +func MultiEnvDefaultFunc(ks []string, dv interface{}) SchemaDefaultFunc { + return func() (interface{}, error) { + for _, k := range ks { + if v := os.Getenv(k); v != "" { + return v, nil + } + } + return dv, nil + } +} + +// SchemaSetFunc is a function that must return a unique ID for the given +// element. This unique ID is used to store the element in a hash. +type SchemaSetFunc func(interface{}) int + +// SchemaStateFunc is a function used to convert some type to a string +// to be stored in the state. +type SchemaStateFunc func(interface{}) string + +// SchemaValidateFunc is a function used to validate a single field in the +// schema. +type SchemaValidateFunc func(interface{}, string) ([]string, []error) + +func (s *Schema) GoString() string { + return fmt.Sprintf("*%#v", *s) +} + +// Returns a default value for this schema by either reading Default or +// evaluating DefaultFunc. If neither of these are defined, returns nil. +func (s *Schema) DefaultValue() (interface{}, error) { + if s.Default != nil { + return s.Default, nil + } + + if s.DefaultFunc != nil { + defaultValue, err := s.DefaultFunc() + if err != nil { + return nil, fmt.Errorf("error loading default: %s", err) + } + return defaultValue, nil + } + + return nil, nil +} + +// Returns a zero value for the schema. +func (s *Schema) ZeroValue() interface{} { + // If it's a set then we'll do a bit of extra work to provide the + // right hashing function in our empty value. + if s.Type == TypeSet { + setFunc := s.Set + if setFunc == nil { + // Default set function uses the schema to hash the whole value + elem := s.Elem + switch t := elem.(type) { + case *Schema: + setFunc = HashSchema(t) + case *Resource: + setFunc = HashResource(t) + default: + panic("invalid set element type") + } + } + return &Set{F: setFunc} + } else { + return s.Type.Zero() + } +} + +func (s *Schema) finalizeDiff(d *terraform.ResourceAttrDiff, customized bool) *terraform.ResourceAttrDiff { + if d == nil { + return d + } + + if s.Type == TypeBool { + normalizeBoolString := func(s string) string { + switch s { + case "0": + return "false" + case "1": + return "true" + } + return s + } + d.Old = normalizeBoolString(d.Old) + d.New = normalizeBoolString(d.New) + } + + if s.Computed && !d.NewRemoved && d.New == "" { + // Computed attribute without a new value set + d.NewComputed = true + } + + if s.ForceNew { + // ForceNew, mark that this field is requiring new under the + // following conditions, explained below: + // + // * Old != New - There is a change in value. This field + // is therefore causing a new resource. + // + // * NewComputed - This field is being computed, hence a + // potential change in value, mark as causing a new resource. + d.RequiresNew = d.Old != d.New || d.NewComputed + } + + if d.NewRemoved { + return d + } + + if s.Computed { + // FIXME: This is where the customized bool from getChange finally + // comes into play. It allows the previously incorrect behavior + // of an empty string being used as "unset" when the value is + // computed. This should be removed once we can properly + // represent an unset/nil value from the configuration. + if !customized { + if d.Old != "" && d.New == "" { + // This is a computed value with an old value set already, + // just let it go. + return nil + } + } + + if d.New == "" && !d.NewComputed { + // Computed attribute without a new value set + d.NewComputed = true + } + } + + if s.Sensitive { + // Set the Sensitive flag so output is hidden in the UI + d.Sensitive = true + } + + return d +} + +// InternalMap is used to aid in the transition to the new schema types and +// protocol. The name is not meant to convey any usefulness, as this is not to +// be used directly by any providers. +type InternalMap = schemaMap + +// schemaMap is a wrapper that adds nice functions on top of schemas. +type schemaMap map[string]*Schema + +func (m schemaMap) panicOnError() bool { + if os.Getenv(PanicOnErr) != "" { + return true + } + return false +} + +// Data returns a ResourceData for the given schema, state, and diff. +// +// The diff is optional. +func (m schemaMap) Data( + s *terraform.InstanceState, + d *terraform.InstanceDiff) (*ResourceData, error) { + return &ResourceData{ + schema: m, + state: s, + diff: d, + panicOnError: m.panicOnError(), + }, nil +} + +// DeepCopy returns a copy of this schemaMap. The copy can be safely modified +// without affecting the original. +func (m *schemaMap) DeepCopy() schemaMap { + copy, err := copystructure.Config{Lock: true}.Copy(m) + if err != nil { + panic(err) + } + return *copy.(*schemaMap) +} + +// Diff returns the diff for a resource given the schema map, +// state, and configuration. +func (m schemaMap) Diff( + s *terraform.InstanceState, + c *terraform.ResourceConfig, + customizeDiff CustomizeDiffFunc, + meta interface{}, + handleRequiresNew bool) (*terraform.InstanceDiff, error) { + result := new(terraform.InstanceDiff) + result.Attributes = make(map[string]*terraform.ResourceAttrDiff) + + // Make sure to mark if the resource is tainted + if s != nil { + result.DestroyTainted = s.Tainted + } + + d := &ResourceData{ + schema: m, + state: s, + config: c, + panicOnError: m.panicOnError(), + } + + for k, schema := range m { + err := m.diff(k, schema, result, d, false) + if err != nil { + return nil, err + } + } + + // Remove any nil diffs just to keep things clean + for k, v := range result.Attributes { + if v == nil { + delete(result.Attributes, k) + } + } + + // If this is a non-destroy diff, call any custom diff logic that has been + // defined. + if !result.DestroyTainted && customizeDiff != nil { + mc := m.DeepCopy() + rd := newResourceDiff(mc, c, s, result) + if err := customizeDiff(rd, meta); err != nil { + return nil, err + } + for _, k := range rd.UpdatedKeys() { + err := m.diff(k, mc[k], result, rd, false) + if err != nil { + return nil, err + } + } + } + + if handleRequiresNew { + // If the diff requires a new resource, then we recompute the diff + // so we have the complete new resource diff, and preserve the + // RequiresNew fields where necessary so the user knows exactly what + // caused that. + if result.RequiresNew() { + // Create the new diff + result2 := new(terraform.InstanceDiff) + result2.Attributes = make(map[string]*terraform.ResourceAttrDiff) + + // Preserve the DestroyTainted flag + result2.DestroyTainted = result.DestroyTainted + + // Reset the data to not contain state. We have to call init() + // again in order to reset the FieldReaders. + d.state = nil + d.init() + + // Perform the diff again + for k, schema := range m { + err := m.diff(k, schema, result2, d, false) + if err != nil { + return nil, err + } + } + + // Re-run customization + if !result2.DestroyTainted && customizeDiff != nil { + mc := m.DeepCopy() + rd := newResourceDiff(mc, c, d.state, result2) + if err := customizeDiff(rd, meta); err != nil { + return nil, err + } + for _, k := range rd.UpdatedKeys() { + err := m.diff(k, mc[k], result2, rd, false) + if err != nil { + return nil, err + } + } + } + + // Force all the fields to not force a new since we know what we + // want to force new. + for k, attr := range result2.Attributes { + if attr == nil { + continue + } + + if attr.RequiresNew { + attr.RequiresNew = false + } + + if s != nil { + attr.Old = s.Attributes[k] + } + } + + // Now copy in all the requires new diffs... + for k, attr := range result.Attributes { + if attr == nil { + continue + } + + newAttr, ok := result2.Attributes[k] + if !ok { + newAttr = attr + } + + if attr.RequiresNew { + newAttr.RequiresNew = true + } + + result2.Attributes[k] = newAttr + } + + // And set the diff! + result = result2 + } + + } + + // Go through and detect all of the ComputedWhens now that we've + // finished the diff. + // TODO + + if result.Empty() { + // If we don't have any diff elements, just return nil + return nil, nil + } + + return result, nil +} + +// Input implements the terraform.ResourceProvider method by asking +// for input for required configuration keys that don't have a value. +func (m schemaMap) Input( + input terraform.UIInput, + c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) { + keys := make([]string, 0, len(m)) + for k, _ := range m { + keys = append(keys, k) + } + sort.Strings(keys) + + for _, k := range keys { + v := m[k] + + // Skip things that don't require config, if that is even valid + // for a provider schema. + // Required XOR Optional must always be true to validate, so we only + // need to check one. + if v.Optional { + continue + } + + // Deprecated fields should never prompt + if v.Deprecated != "" { + continue + } + + // Skip things that have a value of some sort already + if _, ok := c.Raw[k]; ok { + continue + } + + // Skip if it has a default value + defaultValue, err := v.DefaultValue() + if err != nil { + return nil, fmt.Errorf("%s: error loading default: %s", k, err) + } + if defaultValue != nil { + continue + } + + var value interface{} + switch v.Type { + case TypeBool, TypeInt, TypeFloat, TypeSet, TypeList: + continue + case TypeString: + value, err = m.inputString(input, k, v) + default: + panic(fmt.Sprintf("Unknown type for input: %#v", v.Type)) + } + + if err != nil { + return nil, fmt.Errorf( + "%s: %s", k, err) + } + + c.Config[k] = value + } + + return c, nil +} + +// Validate validates the configuration against this schema mapping. +func (m schemaMap) Validate(c *terraform.ResourceConfig) ([]string, []error) { + return m.validateObject("", m, c) +} + +// InternalValidate validates the format of this schema. This should be called +// from a unit test (and not in user-path code) to verify that a schema +// is properly built. +func (m schemaMap) InternalValidate(topSchemaMap schemaMap) error { + return m.internalValidate(topSchemaMap, false) +} + +func (m schemaMap) internalValidate(topSchemaMap schemaMap, attrsOnly bool) error { + if topSchemaMap == nil { + topSchemaMap = m + } + for k, v := range m { + if v.Type == TypeInvalid { + return fmt.Errorf("%s: Type must be specified", k) + } + + if v.Optional && v.Required { + return fmt.Errorf("%s: Optional or Required must be set, not both", k) + } + + if v.Required && v.Computed { + return fmt.Errorf("%s: Cannot be both Required and Computed", k) + } + + if !v.Required && !v.Optional && !v.Computed { + return fmt.Errorf("%s: One of optional, required, or computed must be set", k) + } + + computedOnly := v.Computed && !v.Optional + + switch v.ConfigMode { + case SchemaConfigModeBlock: + if _, ok := v.Elem.(*Resource); !ok { + return fmt.Errorf("%s: ConfigMode of block is allowed only when Elem is *schema.Resource", k) + } + if attrsOnly { + return fmt.Errorf("%s: ConfigMode of block cannot be used in child of schema with ConfigMode of attribute", k) + } + if computedOnly { + return fmt.Errorf("%s: ConfigMode of block cannot be used for computed schema", k) + } + case SchemaConfigModeAttr: + // anything goes + case SchemaConfigModeAuto: + // Since "Auto" for Elem: *Resource would create a nested block, + // and that's impossible inside an attribute, we require it to be + // explicitly overridden as mode "Attr" for clarity. + if _, ok := v.Elem.(*Resource); ok { + if attrsOnly { + return fmt.Errorf("%s: in *schema.Resource with ConfigMode of attribute, so must also have ConfigMode of attribute", k) + } + } + default: + return fmt.Errorf("%s: invalid ConfigMode value", k) + } + + if v.Computed && v.Default != nil { + return fmt.Errorf("%s: Default must be nil if computed", k) + } + + if v.Required && v.Default != nil { + return fmt.Errorf("%s: Default cannot be set with Required", k) + } + + if len(v.ComputedWhen) > 0 && !v.Computed { + return fmt.Errorf("%s: ComputedWhen can only be set with Computed", k) + } + + if len(v.ConflictsWith) > 0 && v.Required { + return fmt.Errorf("%s: ConflictsWith cannot be set with Required", k) + } + + if len(v.ConflictsWith) > 0 { + for _, key := range v.ConflictsWith { + parts := strings.Split(key, ".") + sm := topSchemaMap + var target *Schema + for _, part := range parts { + // Skip index fields + if _, err := strconv.Atoi(part); err == nil { + continue + } + + var ok bool + if target, ok = sm[part]; !ok { + return fmt.Errorf("%s: ConflictsWith references unknown attribute (%s) at part (%s)", k, key, part) + } + + if subResource, ok := target.Elem.(*Resource); ok { + sm = schemaMap(subResource.Schema) + } + } + if target == nil { + return fmt.Errorf("%s: ConflictsWith cannot find target attribute (%s), sm: %#v", k, key, sm) + } + if target.Required { + return fmt.Errorf("%s: ConflictsWith cannot contain Required attribute (%s)", k, key) + } + + if len(target.ComputedWhen) > 0 { + return fmt.Errorf("%s: ConflictsWith cannot contain Computed(When) attribute (%s)", k, key) + } + } + } + + if v.Type == TypeList || v.Type == TypeSet { + if v.Elem == nil { + return fmt.Errorf("%s: Elem must be set for lists", k) + } + + if v.Default != nil { + return fmt.Errorf("%s: Default is not valid for lists or sets", k) + } + + if v.Type != TypeSet && v.Set != nil { + return fmt.Errorf("%s: Set can only be set for TypeSet", k) + } + + switch t := v.Elem.(type) { + case *Resource: + attrsOnly := attrsOnly || v.ConfigMode == SchemaConfigModeAttr + + if err := schemaMap(t.Schema).internalValidate(topSchemaMap, attrsOnly); err != nil { + return err + } + case *Schema: + bad := t.Computed || t.Optional || t.Required + if bad { + return fmt.Errorf( + "%s: Elem must have only Type set", k) + } + } + } else { + if v.MaxItems > 0 || v.MinItems > 0 { + return fmt.Errorf("%s: MaxItems and MinItems are only supported on lists or sets", k) + } + } + + // Computed-only field + if v.Computed && !v.Optional { + if v.ValidateFunc != nil { + return fmt.Errorf("%s: ValidateFunc is for validating user input, "+ + "there's nothing to validate on computed-only field", k) + } + if v.DiffSuppressFunc != nil { + return fmt.Errorf("%s: DiffSuppressFunc is for suppressing differences"+ + " between config and state representation. "+ + "There is no config for computed-only field, nothing to compare.", k) + } + } + + if v.ValidateFunc != nil { + switch v.Type { + case TypeList, TypeSet: + return fmt.Errorf("%s: ValidateFunc is not yet supported on lists or sets.", k) + } + } + + if v.Deprecated == "" && v.Removed == "" { + if !isValidFieldName(k) { + return fmt.Errorf("%s: Field name may only contain lowercase alphanumeric characters & underscores.", k) + } + } + } + + return nil +} + +func isValidFieldName(name string) bool { + re := regexp.MustCompile("^[a-z0-9_]+$") + return re.MatchString(name) +} + +// resourceDiffer is an interface that is used by the private diff functions. +// This helps facilitate diff logic for both ResourceData and ResoureDiff with +// minimal divergence in code. +type resourceDiffer interface { + diffChange(string) (interface{}, interface{}, bool, bool, bool) + Get(string) interface{} + GetChange(string) (interface{}, interface{}) + GetOk(string) (interface{}, bool) + HasChange(string) bool + Id() string +} + +func (m schemaMap) diff( + k string, + schema *Schema, + diff *terraform.InstanceDiff, + d resourceDiffer, + all bool) error { + + unsupressedDiff := new(terraform.InstanceDiff) + unsupressedDiff.Attributes = make(map[string]*terraform.ResourceAttrDiff) + + var err error + switch schema.Type { + case TypeBool, TypeInt, TypeFloat, TypeString: + err = m.diffString(k, schema, unsupressedDiff, d, all) + case TypeList: + err = m.diffList(k, schema, unsupressedDiff, d, all) + case TypeMap: + err = m.diffMap(k, schema, unsupressedDiff, d, all) + case TypeSet: + err = m.diffSet(k, schema, unsupressedDiff, d, all) + default: + err = fmt.Errorf("%s: unknown type %#v", k, schema.Type) + } + + for attrK, attrV := range unsupressedDiff.Attributes { + switch rd := d.(type) { + case *ResourceData: + if schema.DiffSuppressFunc != nil && attrV != nil && + schema.DiffSuppressFunc(attrK, attrV.Old, attrV.New, rd) { + // If this attr diff is suppressed, we may still need it in the + // overall diff if it's contained within a set. Rather than + // dropping the diff, make it a NOOP. + if !all { + continue + } + + attrV = &terraform.ResourceAttrDiff{ + Old: attrV.Old, + New: attrV.Old, + } + } + } + diff.Attributes[attrK] = attrV + } + + return err +} + +func (m schemaMap) diffList( + k string, + schema *Schema, + diff *terraform.InstanceDiff, + d resourceDiffer, + all bool) error { + o, n, _, computedList, customized := d.diffChange(k) + if computedList { + n = nil + } + nSet := n != nil + + // If we have an old value and no new value is set or will be + // computed once all variables can be interpolated and we're + // computed, then nothing has changed. + if o != nil && n == nil && !computedList && schema.Computed { + return nil + } + + if o == nil { + o = []interface{}{} + } + if n == nil { + n = []interface{}{} + } + if s, ok := o.(*Set); ok { + o = s.List() + } + if s, ok := n.(*Set); ok { + n = s.List() + } + os := o.([]interface{}) + vs := n.([]interface{}) + + // If the new value was set, and the two are equal, then we're done. + // We have to do this check here because sets might be NOT + // reflect.DeepEqual so we need to wait until we get the []interface{} + if !all && nSet && reflect.DeepEqual(os, vs) { + return nil + } + + // Get the counts + oldLen := len(os) + newLen := len(vs) + oldStr := strconv.FormatInt(int64(oldLen), 10) + + // If the whole list is computed, then say that the # is computed + if computedList { + diff.Attributes[k+".#"] = &terraform.ResourceAttrDiff{ + Old: oldStr, + NewComputed: true, + RequiresNew: schema.ForceNew, + } + return nil + } + + // If the counts are not the same, then record that diff + changed := oldLen != newLen + computed := oldLen == 0 && newLen == 0 && schema.Computed + if changed || computed || all { + countSchema := &Schema{ + Type: TypeInt, + Computed: schema.Computed, + ForceNew: schema.ForceNew, + } + + newStr := "" + if !computed { + newStr = strconv.FormatInt(int64(newLen), 10) + } else { + oldStr = "" + } + + diff.Attributes[k+".#"] = countSchema.finalizeDiff( + &terraform.ResourceAttrDiff{ + Old: oldStr, + New: newStr, + }, + customized, + ) + } + + // Figure out the maximum + maxLen := oldLen + if newLen > maxLen { + maxLen = newLen + } + + switch t := schema.Elem.(type) { + case *Resource: + // This is a complex resource + for i := 0; i < maxLen; i++ { + for k2, schema := range t.Schema { + subK := fmt.Sprintf("%s.%d.%s", k, i, k2) + err := m.diff(subK, schema, diff, d, all) + if err != nil { + return err + } + } + } + case *Schema: + // Copy the schema so that we can set Computed/ForceNew from + // the parent schema (the TypeList). + t2 := *t + t2.ForceNew = schema.ForceNew + + // This is just a primitive element, so go through each and + // just diff each. + for i := 0; i < maxLen; i++ { + subK := fmt.Sprintf("%s.%d", k, i) + err := m.diff(subK, &t2, diff, d, all) + if err != nil { + return err + } + } + default: + return fmt.Errorf("%s: unknown element type (internal)", k) + } + + return nil +} + +func (m schemaMap) diffMap( + k string, + schema *Schema, + diff *terraform.InstanceDiff, + d resourceDiffer, + all bool) error { + prefix := k + "." + + // First get all the values from the state + var stateMap, configMap map[string]string + o, n, _, nComputed, customized := d.diffChange(k) + if err := mapstructure.WeakDecode(o, &stateMap); err != nil { + return fmt.Errorf("%s: %s", k, err) + } + if err := mapstructure.WeakDecode(n, &configMap); err != nil { + return fmt.Errorf("%s: %s", k, err) + } + + // Keep track of whether the state _exists_ at all prior to clearing it + stateExists := o != nil + + // Delete any count values, since we don't use those + delete(configMap, "%") + delete(stateMap, "%") + + // Check if the number of elements has changed. + oldLen, newLen := len(stateMap), len(configMap) + changed := oldLen != newLen + if oldLen != 0 && newLen == 0 && schema.Computed { + changed = false + } + + // It is computed if we have no old value, no new value, the schema + // says it is computed, and it didn't exist in the state before. The + // last point means: if it existed in the state, even empty, then it + // has already been computed. + computed := oldLen == 0 && newLen == 0 && schema.Computed && !stateExists + + // If the count has changed or we're computed, then add a diff for the + // count. "nComputed" means that the new value _contains_ a value that + // is computed. We don't do granular diffs for this yet, so we mark the + // whole map as computed. + if changed || computed || nComputed { + countSchema := &Schema{ + Type: TypeInt, + Computed: schema.Computed || nComputed, + ForceNew: schema.ForceNew, + } + + oldStr := strconv.FormatInt(int64(oldLen), 10) + newStr := "" + if !computed && !nComputed { + newStr = strconv.FormatInt(int64(newLen), 10) + } else { + oldStr = "" + } + + diff.Attributes[k+".%"] = countSchema.finalizeDiff( + &terraform.ResourceAttrDiff{ + Old: oldStr, + New: newStr, + }, + customized, + ) + } + + // If the new map is nil and we're computed, then ignore it. + if n == nil && schema.Computed { + return nil + } + + // Now we compare, preferring values from the config map + for k, v := range configMap { + old, ok := stateMap[k] + delete(stateMap, k) + + if old == v && ok && !all { + continue + } + + diff.Attributes[prefix+k] = schema.finalizeDiff( + &terraform.ResourceAttrDiff{ + Old: old, + New: v, + }, + customized, + ) + } + for k, v := range stateMap { + diff.Attributes[prefix+k] = schema.finalizeDiff( + &terraform.ResourceAttrDiff{ + Old: v, + NewRemoved: true, + }, + customized, + ) + } + + return nil +} + +func (m schemaMap) diffSet( + k string, + schema *Schema, + diff *terraform.InstanceDiff, + d resourceDiffer, + all bool) error { + + o, n, _, computedSet, customized := d.diffChange(k) + if computedSet { + n = nil + } + nSet := n != nil + + // If we have an old value and no new value is set or will be + // computed once all variables can be interpolated and we're + // computed, then nothing has changed. + if o != nil && n == nil && !computedSet && schema.Computed { + return nil + } + + if o == nil { + o = schema.ZeroValue().(*Set) + } + if n == nil { + n = schema.ZeroValue().(*Set) + } + os := o.(*Set) + ns := n.(*Set) + + // If the new value was set, compare the listCode's to determine if + // the two are equal. Comparing listCode's instead of the actual values + // is needed because there could be computed values in the set which + // would result in false positives while comparing. + if !all && nSet && reflect.DeepEqual(os.listCode(), ns.listCode()) { + return nil + } + + // Get the counts + oldLen := os.Len() + newLen := ns.Len() + oldStr := strconv.Itoa(oldLen) + newStr := strconv.Itoa(newLen) + + // Build a schema for our count + countSchema := &Schema{ + Type: TypeInt, + Computed: schema.Computed, + ForceNew: schema.ForceNew, + } + + // If the set computed then say that the # is computed + if computedSet || schema.Computed && !nSet { + // If # already exists, equals 0 and no new set is supplied, there + // is nothing to record in the diff + count, ok := d.GetOk(k + ".#") + if ok && count.(int) == 0 && !nSet && !computedSet { + return nil + } + + // Set the count but make sure that if # does not exist, we don't + // use the zeroed value + countStr := strconv.Itoa(count.(int)) + if !ok { + countStr = "" + } + + diff.Attributes[k+".#"] = countSchema.finalizeDiff( + &terraform.ResourceAttrDiff{ + Old: countStr, + NewComputed: true, + }, + customized, + ) + return nil + } + + // If the counts are not the same, then record that diff + changed := oldLen != newLen + if changed || all { + diff.Attributes[k+".#"] = countSchema.finalizeDiff( + &terraform.ResourceAttrDiff{ + Old: oldStr, + New: newStr, + }, + customized, + ) + } + + // Build the list of codes that will make up our set. This is the + // removed codes as well as all the codes in the new codes. + codes := make([][]string, 2) + codes[0] = os.Difference(ns).listCode() + codes[1] = ns.listCode() + for _, list := range codes { + for _, code := range list { + switch t := schema.Elem.(type) { + case *Resource: + // This is a complex resource + for k2, schema := range t.Schema { + subK := fmt.Sprintf("%s.%s.%s", k, code, k2) + err := m.diff(subK, schema, diff, d, true) + if err != nil { + return err + } + } + case *Schema: + // Copy the schema so that we can set Computed/ForceNew from + // the parent schema (the TypeSet). + t2 := *t + t2.ForceNew = schema.ForceNew + + // This is just a primitive element, so go through each and + // just diff each. + subK := fmt.Sprintf("%s.%s", k, code) + err := m.diff(subK, &t2, diff, d, true) + if err != nil { + return err + } + default: + return fmt.Errorf("%s: unknown element type (internal)", k) + } + } + } + + return nil +} + +func (m schemaMap) diffString( + k string, + schema *Schema, + diff *terraform.InstanceDiff, + d resourceDiffer, + all bool) error { + var originalN interface{} + var os, ns string + o, n, _, computed, customized := d.diffChange(k) + if schema.StateFunc != nil && n != nil { + originalN = n + n = schema.StateFunc(n) + } + nraw := n + if nraw == nil && o != nil { + nraw = schema.Type.Zero() + } + if err := mapstructure.WeakDecode(o, &os); err != nil { + return fmt.Errorf("%s: %s", k, err) + } + if err := mapstructure.WeakDecode(nraw, &ns); err != nil { + return fmt.Errorf("%s: %s", k, err) + } + + if os == ns && !all && !computed { + // They're the same value. If there old value is not blank or we + // have an ID, then return right away since we're already set up. + if os != "" || d.Id() != "" { + return nil + } + + // Otherwise, only continue if we're computed + if !schema.Computed { + return nil + } + } + + removed := false + if o != nil && n == nil && !computed { + removed = true + } + if removed && schema.Computed { + return nil + } + + diff.Attributes[k] = schema.finalizeDiff( + &terraform.ResourceAttrDiff{ + Old: os, + New: ns, + NewExtra: originalN, + NewRemoved: removed, + NewComputed: computed, + }, + customized, + ) + + return nil +} + +func (m schemaMap) inputString( + input terraform.UIInput, + k string, + schema *Schema) (interface{}, error) { + result, err := input.Input(context.Background(), &terraform.InputOpts{ + Id: k, + Query: k, + Description: schema.Description, + Default: schema.InputDefault, + }) + + return result, err +} + +func (m schemaMap) validate( + k string, + schema *Schema, + c *terraform.ResourceConfig) ([]string, []error) { + raw, ok := c.Get(k) + if !ok && schema.DefaultFunc != nil { + // We have a dynamic default. Check if we have a value. + var err error + raw, err = schema.DefaultFunc() + if err != nil { + return nil, []error{fmt.Errorf( + "%q, error loading default: %s", k, err)} + } + + // We're okay as long as we had a value set + ok = raw != nil + } + if !ok { + if schema.Required { + return nil, []error{fmt.Errorf( + "%q: required field is not set", k)} + } + + return nil, nil + } + + if !schema.Required && !schema.Optional { + // This is a computed-only field + return nil, []error{fmt.Errorf( + "%q: this field cannot be set", k)} + } + + // If the value is unknown then we can't validate it yet. + // In particular, this avoids spurious type errors where downstream + // validation code sees UnknownVariableValue as being just a string. + // The SDK has to allow the unknown value through initially, so that + // Required fields set via an interpolated value are accepted. + if !isWhollyKnown(raw) { + if schema.Deprecated != "" { + return []string{fmt.Sprintf("%q: [DEPRECATED] %s", k, schema.Deprecated)}, nil + } + return nil, nil + } + + err := m.validateConflictingAttributes(k, schema, c) + if err != nil { + return nil, []error{err} + } + + return m.validateType(k, raw, schema, c) +} + +// isWhollyKnown returns false if the argument contains an UnknownVariableValue +func isWhollyKnown(raw interface{}) bool { + switch raw := raw.(type) { + case string: + if raw == hcl2shim.UnknownVariableValue { + return false + } + case []interface{}: + for _, v := range raw { + if !isWhollyKnown(v) { + return false + } + } + case map[string]interface{}: + for _, v := range raw { + if !isWhollyKnown(v) { + return false + } + } + } + return true +} +func (m schemaMap) validateConflictingAttributes( + k string, + schema *Schema, + c *terraform.ResourceConfig) error { + + if len(schema.ConflictsWith) == 0 { + return nil + } + + for _, conflictingKey := range schema.ConflictsWith { + if raw, ok := c.Get(conflictingKey); ok { + if raw == hcl2shim.UnknownVariableValue { + // An unknown value might become unset (null) once known, so + // we must defer validation until it's known. + continue + } + return fmt.Errorf( + "%q: conflicts with %s", k, conflictingKey) + } + } + + return nil +} + +func (m schemaMap) validateList( + k string, + raw interface{}, + schema *Schema, + c *terraform.ResourceConfig) ([]string, []error) { + // first check if the list is wholly unknown + if s, ok := raw.(string); ok { + if s == hcl2shim.UnknownVariableValue { + return nil, nil + } + } + + // schemaMap can't validate nil + if raw == nil { + return nil, nil + } + + // We use reflection to verify the slice because you can't + // case to []interface{} unless the slice is exactly that type. + rawV := reflect.ValueOf(raw) + + // If we support promotion and the raw value isn't a slice, wrap + // it in []interface{} and check again. + if schema.PromoteSingle && rawV.Kind() != reflect.Slice { + raw = []interface{}{raw} + rawV = reflect.ValueOf(raw) + } + + if rawV.Kind() != reflect.Slice { + return nil, []error{fmt.Errorf( + "%s: should be a list", k)} + } + + // We can't validate list length if this came from a dynamic block. + // Since there's no way to determine if something was from a dynamic block + // at this point, we're going to skip validation in the new protocol if + // there are any unknowns. Validate will eventually be called again once + // all values are known. + if isProto5() && !isWhollyKnown(raw) { + return nil, nil + } + + // Validate length + if schema.MaxItems > 0 && rawV.Len() > schema.MaxItems { + return nil, []error{fmt.Errorf( + "%s: attribute supports %d item maximum, config has %d declared", k, schema.MaxItems, rawV.Len())} + } + + if schema.MinItems > 0 && rawV.Len() < schema.MinItems { + return nil, []error{fmt.Errorf( + "%s: attribute supports %d item as a minimum, config has %d declared", k, schema.MinItems, rawV.Len())} + } + + // Now build the []interface{} + raws := make([]interface{}, rawV.Len()) + for i, _ := range raws { + raws[i] = rawV.Index(i).Interface() + } + + var ws []string + var es []error + for i, raw := range raws { + key := fmt.Sprintf("%s.%d", k, i) + + // Reify the key value from the ResourceConfig. + // If the list was computed we have all raw values, but some of these + // may be known in the config, and aren't individually marked as Computed. + if r, ok := c.Get(key); ok { + raw = r + } + + var ws2 []string + var es2 []error + switch t := schema.Elem.(type) { + case *Resource: + // This is a sub-resource + ws2, es2 = m.validateObject(key, t.Schema, c) + case *Schema: + ws2, es2 = m.validateType(key, raw, t, c) + } + + if len(ws2) > 0 { + ws = append(ws, ws2...) + } + if len(es2) > 0 { + es = append(es, es2...) + } + } + + return ws, es +} + +func (m schemaMap) validateMap( + k string, + raw interface{}, + schema *Schema, + c *terraform.ResourceConfig) ([]string, []error) { + // first check if the list is wholly unknown + if s, ok := raw.(string); ok { + if s == hcl2shim.UnknownVariableValue { + return nil, nil + } + } + + // schemaMap can't validate nil + if raw == nil { + return nil, nil + } + // We use reflection to verify the slice because you can't + // case to []interface{} unless the slice is exactly that type. + rawV := reflect.ValueOf(raw) + switch rawV.Kind() { + case reflect.String: + // If raw and reified are equal, this is a string and should + // be rejected. + reified, reifiedOk := c.Get(k) + if reifiedOk && raw == reified && !c.IsComputed(k) { + return nil, []error{fmt.Errorf("%s: should be a map", k)} + } + // Otherwise it's likely raw is an interpolation. + return nil, nil + case reflect.Map: + case reflect.Slice: + default: + return nil, []error{fmt.Errorf("%s: should be a map", k)} + } + + // If it is not a slice, validate directly + if rawV.Kind() != reflect.Slice { + mapIface := rawV.Interface() + if _, errs := validateMapValues(k, mapIface.(map[string]interface{}), schema); len(errs) > 0 { + return nil, errs + } + if schema.ValidateFunc != nil { + return schema.ValidateFunc(mapIface, k) + } + return nil, nil + } + + // It is a slice, verify that all the elements are maps + raws := make([]interface{}, rawV.Len()) + for i, _ := range raws { + raws[i] = rawV.Index(i).Interface() + } + + for _, raw := range raws { + v := reflect.ValueOf(raw) + if v.Kind() != reflect.Map { + return nil, []error{fmt.Errorf( + "%s: should be a map", k)} + } + mapIface := v.Interface() + if _, errs := validateMapValues(k, mapIface.(map[string]interface{}), schema); len(errs) > 0 { + return nil, errs + } + } + + if schema.ValidateFunc != nil { + validatableMap := make(map[string]interface{}) + for _, raw := range raws { + for k, v := range raw.(map[string]interface{}) { + validatableMap[k] = v + } + } + + return schema.ValidateFunc(validatableMap, k) + } + + return nil, nil +} + +func validateMapValues(k string, m map[string]interface{}, schema *Schema) ([]string, []error) { + for key, raw := range m { + valueType, err := getValueType(k, schema) + if err != nil { + return nil, []error{err} + } + + switch valueType { + case TypeBool: + var n bool + if err := mapstructure.WeakDecode(raw, &n); err != nil { + return nil, []error{fmt.Errorf("%s (%s): %s", k, key, err)} + } + case TypeInt: + var n int + if err := mapstructure.WeakDecode(raw, &n); err != nil { + return nil, []error{fmt.Errorf("%s (%s): %s", k, key, err)} + } + case TypeFloat: + var n float64 + if err := mapstructure.WeakDecode(raw, &n); err != nil { + return nil, []error{fmt.Errorf("%s (%s): %s", k, key, err)} + } + case TypeString: + var n string + if err := mapstructure.WeakDecode(raw, &n); err != nil { + return nil, []error{fmt.Errorf("%s (%s): %s", k, key, err)} + } + default: + panic(fmt.Sprintf("Unknown validation type: %#v", schema.Type)) + } + } + return nil, nil +} + +func getValueType(k string, schema *Schema) (ValueType, error) { + if schema.Elem == nil { + return TypeString, nil + } + if vt, ok := schema.Elem.(ValueType); ok { + return vt, nil + } + + // If a Schema is provided to a Map, we use the Type of that schema + // as the type for each element in the Map. + if s, ok := schema.Elem.(*Schema); ok { + return s.Type, nil + } + + if _, ok := schema.Elem.(*Resource); ok { + // TODO: We don't actually support this (yet) + // but silently pass the validation, until we decide + // how to handle nested structures in maps + return TypeString, nil + } + return 0, fmt.Errorf("%s: unexpected map value type: %#v", k, schema.Elem) +} + +func (m schemaMap) validateObject( + k string, + schema map[string]*Schema, + c *terraform.ResourceConfig) ([]string, []error) { + raw, _ := c.Get(k) + + // schemaMap can't validate nil + if raw == nil { + return nil, nil + } + + if _, ok := raw.(map[string]interface{}); !ok && !c.IsComputed(k) { + return nil, []error{fmt.Errorf( + "%s: expected object, got %s", + k, reflect.ValueOf(raw).Kind())} + } + + var ws []string + var es []error + for subK, s := range schema { + key := subK + if k != "" { + key = fmt.Sprintf("%s.%s", k, subK) + } + + ws2, es2 := m.validate(key, s, c) + if len(ws2) > 0 { + ws = append(ws, ws2...) + } + if len(es2) > 0 { + es = append(es, es2...) + } + } + + // Detect any extra/unknown keys and report those as errors. + if m, ok := raw.(map[string]interface{}); ok { + for subk, _ := range m { + if _, ok := schema[subk]; !ok { + if subk == TimeoutsConfigKey { + continue + } + es = append(es, fmt.Errorf( + "%s: invalid or unknown key: %s", k, subk)) + } + } + } + + return ws, es +} + +func (m schemaMap) validatePrimitive( + k string, + raw interface{}, + schema *Schema, + c *terraform.ResourceConfig) ([]string, []error) { + + // a nil value shouldn't happen in the old protocol, and in the new + // protocol the types have already been validated. Either way, we can't + // reflect on nil, so don't panic. + if raw == nil { + return nil, nil + } + + // Catch if the user gave a complex type where a primitive was + // expected, so we can return a friendly error message that + // doesn't contain Go type system terminology. + switch reflect.ValueOf(raw).Type().Kind() { + case reflect.Slice: + return nil, []error{ + fmt.Errorf("%s must be a single value, not a list", k), + } + case reflect.Map: + return nil, []error{ + fmt.Errorf("%s must be a single value, not a map", k), + } + default: // ok + } + + if c.IsComputed(k) { + // If the key is being computed, then it is not an error as + // long as it's not a slice or map. + return nil, nil + } + + var decoded interface{} + switch schema.Type { + case TypeBool: + // Verify that we can parse this as the correct type + var n bool + if err := mapstructure.WeakDecode(raw, &n); err != nil { + return nil, []error{fmt.Errorf("%s: %s", k, err)} + } + decoded = n + case TypeInt: + switch { + case isProto5(): + // We need to verify the type precisely, because WeakDecode will + // decode a float as an integer. + + // the config shims only use int for integral number values + if v, ok := raw.(int); ok { + decoded = v + } else { + return nil, []error{fmt.Errorf("%s: must be a whole number, got %v", k, raw)} + } + default: + // Verify that we can parse this as an int + var n int + if err := mapstructure.WeakDecode(raw, &n); err != nil { + return nil, []error{fmt.Errorf("%s: %s", k, err)} + } + decoded = n + } + case TypeFloat: + // Verify that we can parse this as an int + var n float64 + if err := mapstructure.WeakDecode(raw, &n); err != nil { + return nil, []error{fmt.Errorf("%s: %s", k, err)} + } + decoded = n + case TypeString: + // Verify that we can parse this as a string + var n string + if err := mapstructure.WeakDecode(raw, &n); err != nil { + return nil, []error{fmt.Errorf("%s: %s", k, err)} + } + decoded = n + default: + panic(fmt.Sprintf("Unknown validation type: %#v", schema.Type)) + } + + if schema.ValidateFunc != nil { + return schema.ValidateFunc(decoded, k) + } + + return nil, nil +} + +func (m schemaMap) validateType( + k string, + raw interface{}, + schema *Schema, + c *terraform.ResourceConfig) ([]string, []error) { + var ws []string + var es []error + switch schema.Type { + case TypeSet, TypeList: + ws, es = m.validateList(k, raw, schema, c) + case TypeMap: + ws, es = m.validateMap(k, raw, schema, c) + default: + ws, es = m.validatePrimitive(k, raw, schema, c) + } + + if schema.Deprecated != "" { + ws = append(ws, fmt.Sprintf( + "%q: [DEPRECATED] %s", k, schema.Deprecated)) + } + + if schema.Removed != "" { + es = append(es, fmt.Errorf( + "%q: [REMOVED] %s", k, schema.Removed)) + } + + return ws, es +} + +// Zero returns the zero value for a type. +func (t ValueType) Zero() interface{} { + switch t { + case TypeInvalid: + return nil + case TypeBool: + return false + case TypeInt: + return 0 + case TypeFloat: + return 0.0 + case TypeString: + return "" + case TypeList: + return []interface{}{} + case TypeMap: + return map[string]interface{}{} + case TypeSet: + return new(Set) + case typeObject: + return map[string]interface{}{} + default: + panic(fmt.Sprintf("unknown type %s", t)) + } +} diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/schema_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/schema_test.go new file mode 100644 index 00000000..2a9da0f6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/schema_test.go @@ -0,0 +1,5558 @@ +package schema + +import ( + "bytes" + "errors" + "fmt" + "os" + "reflect" + "sort" + "strconv" + "strings" + "testing" + + "github.com/hashicorp/terraform/configs/hcl2shim" + "github.com/hashicorp/terraform/internal/legacy/helper/hashcode" + "github.com/hashicorp/terraform/internal/legacy/terraform" +) + +func TestEnvDefaultFunc(t *testing.T) { + key := "TF_TEST_ENV_DEFAULT_FUNC" + defer os.Unsetenv(key) + + f := EnvDefaultFunc(key, "42") + if err := os.Setenv(key, "foo"); err != nil { + t.Fatalf("err: %s", err) + } + + actual, err := f() + if err != nil { + t.Fatalf("err: %s", err) + } + if actual != "foo" { + t.Fatalf("bad: %#v", actual) + } + + if err := os.Unsetenv(key); err != nil { + t.Fatalf("err: %s", err) + } + + actual, err = f() + if err != nil { + t.Fatalf("err: %s", err) + } + if actual != "42" { + t.Fatalf("bad: %#v", actual) + } +} + +func TestMultiEnvDefaultFunc(t *testing.T) { + keys := []string{ + "TF_TEST_MULTI_ENV_DEFAULT_FUNC1", + "TF_TEST_MULTI_ENV_DEFAULT_FUNC2", + } + defer func() { + for _, k := range keys { + os.Unsetenv(k) + } + }() + + // Test that the first key is returned first + f := MultiEnvDefaultFunc(keys, "42") + if err := os.Setenv(keys[0], "foo"); err != nil { + t.Fatalf("err: %s", err) + } + + actual, err := f() + if err != nil { + t.Fatalf("err: %s", err) + } + if actual != "foo" { + t.Fatalf("bad: %#v", actual) + } + + if err := os.Unsetenv(keys[0]); err != nil { + t.Fatalf("err: %s", err) + } + + // Test that the second key is returned if the first one is empty + f = MultiEnvDefaultFunc(keys, "42") + if err := os.Setenv(keys[1], "foo"); err != nil { + t.Fatalf("err: %s", err) + } + + actual, err = f() + if err != nil { + t.Fatalf("err: %s", err) + } + if actual != "foo" { + t.Fatalf("bad: %#v", actual) + } + + if err := os.Unsetenv(keys[1]); err != nil { + t.Fatalf("err: %s", err) + } + + // Test that the default value is returned when no keys are set + actual, err = f() + if err != nil { + t.Fatalf("err: %s", err) + } + if actual != "42" { + t.Fatalf("bad: %#v", actual) + } +} + +func TestValueType_Zero(t *testing.T) { + cases := []struct { + Type ValueType + Value interface{} + }{ + {TypeBool, false}, + {TypeInt, 0}, + {TypeFloat, 0.0}, + {TypeString, ""}, + {TypeList, []interface{}{}}, + {TypeMap, map[string]interface{}{}}, + {TypeSet, new(Set)}, + } + + for i, tc := range cases { + actual := tc.Type.Zero() + if !reflect.DeepEqual(actual, tc.Value) { + t.Fatalf("%d: %#v != %#v", i, actual, tc.Value) + } + } +} + +func TestSchemaMap_Diff(t *testing.T) { + cases := []struct { + Name string + Schema map[string]*Schema + State *terraform.InstanceState + Config map[string]interface{} + CustomizeDiff CustomizeDiffFunc + Diff *terraform.InstanceDiff + Err bool + }{ + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + NewComputed: true, + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "foo", + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Computed, but set in config", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + + Config: map[string]interface{}{ + "availability_zone": "bar", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "foo", + New: "bar", + }, + }, + }, + + Err: false, + }, + + { + Name: "Default", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Default: "foo", + }, + }, + + State: nil, + + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + }, + }, + }, + + Err: false, + }, + + { + Name: "DefaultFunc, value", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + DefaultFunc: func() (interface{}, error) { + return "foo", nil + }, + }, + }, + + State: nil, + + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + }, + }, + }, + + Err: false, + }, + + { + Name: "DefaultFunc, configuration set", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + DefaultFunc: func() (interface{}, error) { + return "foo", nil + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "bar", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "bar", + }, + }, + }, + + Err: false, + }, + + { + Name: "String with StateFunc", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + StateFunc: func(a interface{}) string { + return a.(string) + "!" + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo!", + NewExtra: "foo", + }, + }, + }, + + Err: false, + }, + + { + Name: "StateFunc not called with nil value", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + StateFunc: func(a interface{}) string { + t.Fatalf("should not get here!") + return "" + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Variable computed", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": hcl2shim.UnknownVariableValue, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: hcl2shim.UnknownVariableValue, + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Int decode", + Schema: map[string]*Schema{ + "port": &Schema{ + Type: TypeInt, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "port": 27, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "port": &terraform.ResourceAttrDiff{ + Old: "", + New: "27", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "bool decode", + Schema: map[string]*Schema{ + "port": &Schema{ + Type: TypeBool, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "port": false, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "port": &terraform.ResourceAttrDiff{ + Old: "", + New: "false", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Bool", + Schema: map[string]*Schema{ + "delete": &Schema{ + Type: TypeBool, + Optional: true, + Default: false, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "delete": "false", + }, + }, + + Config: nil, + + Diff: nil, + + Err: false, + }, + + { + Name: "List decode", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "3", + }, + "ports.0": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "", + New: "2", + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "List decode with promotion", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + PromoteSingle: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": "5", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + "ports.0": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "List decode with promotion with list", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + PromoteSingle: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{"5"}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + "ports.0": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "3", + }, + "ports.0": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "", + New: "2", + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, hcl2shim.UnknownVariableValue, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.0": "1", + "ports.1": "2", + "ports.2": "5", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: nil, + + Err: false, + }, + + { + Name: "", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "2", + "ports.0": "1", + "ports.1": "2", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "2", + New: "3", + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "3", + RequiresNew: true, + }, + "ports.0": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + RequiresNew: true, + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "", + New: "2", + RequiresNew: true, + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "List with computed set", + Schema: map[string]*Schema{ + "config": &Schema{ + Type: TypeList, + Optional: true, + ForceNew: true, + MinItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "name": { + Type: TypeString, + Required: true, + }, + + "rules": { + Type: TypeSet, + Computed: true, + Elem: &Schema{Type: TypeString}, + Set: HashString, + }, + }, + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "config": []interface{}{ + map[string]interface{}{ + "name": "hello", + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + RequiresNew: true, + }, + + "config.0.name": &terraform.ResourceAttrDiff{ + Old: "", + New: "hello", + }, + + "config.0.rules.#": &terraform.ResourceAttrDiff{ + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{5, 2, 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "3", + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "", + New: "2", + }, + "ports.5": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Computed: true, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "0", + }, + }, + + Config: nil, + + Diff: nil, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{"2", "5", 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "3", + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "", + New: "2", + }, + "ports.5": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, hcl2shim.UnknownVariableValue, "5"}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "2", + "ports.1": "1", + "ports.2": "2", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{5, 2, 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "2", + New: "3", + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "1", + New: "1", + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "2", + New: "2", + }, + "ports.5": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "2", + "ports.1": "1", + "ports.2": "2", + }, + }, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "2", + New: "0", + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "1", + New: "0", + NewRemoved: true, + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "2", + New: "0", + NewRemoved: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "bar", + "ports.#": "1", + "ports.80": "80", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeSet, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + ps := m["ports"].([]interface{}) + result := 0 + for _, p := range ps { + result += p.(int) + } + return result + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ingress.#": "2", + "ingress.80.ports.#": "1", + "ingress.80.ports.0": "80", + "ingress.443.ports.#": "1", + "ingress.443.ports.0": "443", + }, + }, + + Config: map[string]interface{}{ + "ingress": []interface{}{ + map[string]interface{}{ + "ports": []interface{}{443}, + }, + map[string]interface{}{ + "ports": []interface{}{80}, + }, + }, + }, + + Diff: nil, + + Err: false, + }, + + { + Name: "List of structure decode", + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": &Schema{ + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ingress": []interface{}{ + map[string]interface{}{ + "from": 8080, + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ingress.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + "ingress.0.from": &terraform.ResourceAttrDiff{ + Old: "", + New: "8080", + }, + }, + }, + + Err: false, + }, + + { + Name: "ComputedWhen", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Computed: true, + ComputedWhen: []string{"port"}, + }, + + "port": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + "port": "80", + }, + }, + + Config: map[string]interface{}{ + "port": 80, + }, + + Diff: nil, + + Err: false, + }, + + { + Name: "", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Computed: true, + ComputedWhen: []string{"port"}, + }, + + "port": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "port": "80", + }, + }, + + Config: map[string]interface{}{ + "port": 80, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + /* TODO + { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Computed: true, + ComputedWhen: []string{"port"}, + }, + + "port": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "foo", + "port": "80", + }, + }, + + Config: map[string]interface{}{ + "port": 8080, + }, + + Diff: &terraform.ResourceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "foo", + NewComputed: true, + }, + "port": &terraform.ResourceAttrDiff{ + Old: "80", + New: "8080", + }, + }, + }, + + Err: false, + }, + */ + + { + Name: "Maps", + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeMap, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "config_vars": []interface{}{ + map[string]interface{}{ + "bar": "baz", + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.%": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + + "config_vars.bar": &terraform.ResourceAttrDiff{ + Old: "", + New: "baz", + }, + }, + }, + + Err: false, + }, + + { + Name: "Maps", + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeMap, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.foo": "bar", + }, + }, + + Config: map[string]interface{}{ + "config_vars": []interface{}{ + map[string]interface{}{ + "bar": "baz", + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.foo": &terraform.ResourceAttrDiff{ + Old: "bar", + NewRemoved: true, + }, + "config_vars.bar": &terraform.ResourceAttrDiff{ + Old: "", + New: "baz", + }, + }, + }, + + Err: false, + }, + + { + Name: "Maps", + Schema: map[string]*Schema{ + "vars": &Schema{ + Type: TypeMap, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "vars.foo": "bar", + }, + }, + + Config: map[string]interface{}{ + "vars": []interface{}{ + map[string]interface{}{ + "bar": "baz", + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "vars.foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "", + NewRemoved: true, + }, + "vars.bar": &terraform.ResourceAttrDiff{ + Old: "", + New: "baz", + }, + }, + }, + + Err: false, + }, + + { + Name: "Maps", + Schema: map[string]*Schema{ + "vars": &Schema{ + Type: TypeMap, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "vars.foo": "bar", + }, + }, + + Config: nil, + + Diff: nil, + + Err: false, + }, + + { + Name: "Maps", + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeMap}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.#": "1", + "config_vars.0.foo": "bar", + }, + }, + + Config: map[string]interface{}{ + "config_vars": []interface{}{ + map[string]interface{}{ + "bar": "baz", + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.0.foo": &terraform.ResourceAttrDiff{ + Old: "bar", + NewRemoved: true, + }, + "config_vars.0.bar": &terraform.ResourceAttrDiff{ + Old: "", + New: "baz", + }, + }, + }, + + Err: false, + }, + + { + Name: "Maps", + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeMap}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "config_vars.#": "1", + "config_vars.0.foo": "bar", + "config_vars.0.bar": "baz", + }, + }, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.#": &terraform.ResourceAttrDiff{ + Old: "1", + New: "0", + }, + "config_vars.0.%": &terraform.ResourceAttrDiff{ + Old: "2", + New: "0", + }, + "config_vars.0.foo": &terraform.ResourceAttrDiff{ + Old: "bar", + NewRemoved: true, + }, + "config_vars.0.bar": &terraform.ResourceAttrDiff{ + Old: "baz", + NewRemoved: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "ForceNews", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + ForceNew: true, + }, + + "address": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "bar", + "address": "foo", + }, + }, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "foo", + RequiresNew: true, + }, + + "address": &terraform.ResourceAttrDiff{ + Old: "foo", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + ForceNew: true, + }, + + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "availability_zone": "bar", + "ports.#": "1", + "ports.80": "80", + }, + }, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "foo", + RequiresNew: true, + }, + + "ports.#": &terraform.ResourceAttrDiff{ + Old: "1", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "instances": &Schema{ + Type: TypeSet, + Elem: &Schema{Type: TypeString}, + Optional: true, + Computed: true, + Set: func(v interface{}) int { + return len(v.(string)) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "instances.#": "0", + }, + }, + + Config: map[string]interface{}{ + "instances": []interface{}{hcl2shim.UnknownVariableValue}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "instances.#": &terraform.ResourceAttrDiff{ + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "route": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": &Schema{ + Type: TypeInt, + Required: true, + }, + + "gateway": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "route": []interface{}{ + map[string]interface{}{ + "index": "1", + "gateway": hcl2shim.UnknownVariableValue, + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "route.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + "route.~1.index": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "route.~1.gateway": &terraform.ResourceAttrDiff{ + Old: "", + New: hcl2shim.UnknownVariableValue, + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set", + Schema: map[string]*Schema{ + "route": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": &Schema{ + Type: TypeInt, + Required: true, + }, + + "gateway": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "route": []interface{}{ + map[string]interface{}{ + "index": "1", + "gateway": []interface{}{ + hcl2shim.UnknownVariableValue, + }, + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "route.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + "route.~1.index": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "route.~1.gateway.#": &terraform.ResourceAttrDiff{ + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Computed maps", + Schema: map[string]*Schema{ + "vars": &Schema{ + Type: TypeMap, + Computed: true, + }, + }, + + State: nil, + + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "vars.%": &terraform.ResourceAttrDiff{ + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Computed maps", + Schema: map[string]*Schema{ + "vars": &Schema{ + Type: TypeMap, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "vars.%": "0", + }, + }, + + Config: map[string]interface{}{ + "vars": map[string]interface{}{ + "bar": hcl2shim.UnknownVariableValue, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "vars.%": &terraform.ResourceAttrDiff{ + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: " - Empty", + Schema: map[string]*Schema{}, + + State: &terraform.InstanceState{}, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Float", + Schema: map[string]*Schema{ + "some_threshold": &Schema{ + Type: TypeFloat, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "some_threshold": "567.8", + }, + }, + + Config: map[string]interface{}{ + "some_threshold": 12.34, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "some_threshold": &terraform.ResourceAttrDiff{ + Old: "567.8", + New: "12.34", + }, + }, + }, + + Err: false, + }, + + { + Name: "https://github.com/hashicorp/terraform/issues/824", + Schema: map[string]*Schema{ + "block_device": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "device_name": &Schema{ + Type: TypeString, + Required: true, + }, + "delete_on_termination": &Schema{ + Type: TypeBool, + Optional: true, + Default: true, + }, + }, + }, + Set: func(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string))) + buf.WriteString(fmt.Sprintf("%t-", m["delete_on_termination"].(bool))) + return hashcode.String(buf.String()) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "block_device.#": "2", + "block_device.616397234.delete_on_termination": "true", + "block_device.616397234.device_name": "/dev/sda1", + "block_device.2801811477.delete_on_termination": "true", + "block_device.2801811477.device_name": "/dev/sdx", + }, + }, + + Config: map[string]interface{}{ + "block_device": []interface{}{ + map[string]interface{}{ + "device_name": "/dev/sda1", + }, + map[string]interface{}{ + "device_name": "/dev/sdx", + }, + }, + }, + Diff: nil, + Err: false, + }, + + { + Name: "Zero value in state shouldn't result in diff", + Schema: map[string]*Schema{ + "port": &Schema{ + Type: TypeBool, + Optional: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "port": "false", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Same as prev, but for sets", + Schema: map[string]*Schema{ + "route": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": &Schema{ + Type: TypeInt, + Required: true, + }, + + "gateway": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "route.#": "0", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "A set computed element shouldn't cause a diff", + Schema: map[string]*Schema{ + "active": &Schema{ + Type: TypeBool, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "active": "true", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "An empty set should show up in the diff", + Schema: map[string]*Schema{ + "instances": &Schema{ + Type: TypeSet, + Elem: &Schema{Type: TypeString}, + Optional: true, + ForceNew: true, + Set: func(v interface{}) int { + return len(v.(string)) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "instances.#": "1", + "instances.3": "foo", + }, + }, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "instances.#": &terraform.ResourceAttrDiff{ + Old: "1", + New: "0", + RequiresNew: true, + }, + "instances.3": &terraform.ResourceAttrDiff{ + Old: "foo", + New: "", + NewRemoved: true, + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Map with empty value", + Schema: map[string]*Schema{ + "vars": &Schema{ + Type: TypeMap, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "vars": map[string]interface{}{ + "foo": "", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "vars.%": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + "vars.foo": &terraform.ResourceAttrDiff{ + Old: "", + New: "", + }, + }, + }, + + Err: false, + }, + + { + Name: "Unset bool, not in state", + Schema: map[string]*Schema{ + "force": &Schema{ + Type: TypeBool, + Optional: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Unset set, not in state", + Schema: map[string]*Schema{ + "metadata_keys": &Schema{ + Type: TypeSet, + Optional: true, + ForceNew: true, + Elem: &Schema{Type: TypeInt}, + Set: func(interface{}) int { return 0 }, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Unset list in state, should not show up computed", + Schema: map[string]*Schema{ + "metadata_keys": &Schema{ + Type: TypeList, + Optional: true, + Computed: true, + ForceNew: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "metadata_keys.#": "0", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Set element computed element", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, hcl2shim.UnknownVariableValue}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Computed map without config that's known to be empty does not generate diff", + Schema: map[string]*Schema{ + "tags": &Schema{ + Type: TypeMap, + Computed: true, + }, + }, + + Config: nil, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "tags.%": "0", + }, + }, + + Diff: nil, + + Err: false, + }, + + { + Name: "Set with hyphen keys", + Schema: map[string]*Schema{ + "route": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": &Schema{ + Type: TypeInt, + Required: true, + }, + + "gateway-name": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "route": []interface{}{ + map[string]interface{}{ + "index": "1", + "gateway-name": "hello", + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "route.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + "route.1.index": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "route.1.gateway-name": &terraform.ResourceAttrDiff{ + Old: "", + New: "hello", + }, + }, + }, + + Err: false, + }, + + { + Name: ": StateFunc in nested set (#1759)", + Schema: map[string]*Schema{ + "service_account": &Schema{ + Type: TypeList, + Optional: true, + ForceNew: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "scopes": &Schema{ + Type: TypeSet, + Required: true, + ForceNew: true, + Elem: &Schema{ + Type: TypeString, + StateFunc: func(v interface{}) string { + return v.(string) + "!" + }, + }, + Set: func(v interface{}) int { + i, err := strconv.Atoi(v.(string)) + if err != nil { + t.Fatalf("err: %s", err) + } + return i + }, + }, + }, + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "service_account": []interface{}{ + map[string]interface{}{ + "scopes": []interface{}{"123"}, + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "service_account.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + RequiresNew: true, + }, + "service_account.0.scopes.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + RequiresNew: true, + }, + "service_account.0.scopes.123": &terraform.ResourceAttrDiff{ + Old: "", + New: "123!", + NewExtra: "123", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Removing set elements", + Schema: map[string]*Schema{ + "instances": &Schema{ + Type: TypeSet, + Elem: &Schema{Type: TypeString}, + Optional: true, + ForceNew: true, + Set: func(v interface{}) int { + return len(v.(string)) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "instances.#": "2", + "instances.3": "333", + "instances.2": "22", + }, + }, + + Config: map[string]interface{}{ + "instances": []interface{}{"333", "4444"}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "instances.#": &terraform.ResourceAttrDiff{ + Old: "2", + New: "2", + }, + "instances.2": &terraform.ResourceAttrDiff{ + Old: "22", + New: "", + NewRemoved: true, + RequiresNew: true, + }, + "instances.3": &terraform.ResourceAttrDiff{ + Old: "333", + New: "333", + }, + "instances.4": &terraform.ResourceAttrDiff{ + Old: "", + New: "4444", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Bools can be set with 0/1 in config, still get true/false", + Schema: map[string]*Schema{ + "one": &Schema{ + Type: TypeBool, + Optional: true, + }, + "two": &Schema{ + Type: TypeBool, + Optional: true, + }, + "three": &Schema{ + Type: TypeBool, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "one": "false", + "two": "true", + "three": "true", + }, + }, + + Config: map[string]interface{}{ + "one": "1", + "two": "0", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "one": &terraform.ResourceAttrDiff{ + Old: "false", + New: "true", + }, + "two": &terraform.ResourceAttrDiff{ + Old: "true", + New: "false", + }, + "three": &terraform.ResourceAttrDiff{ + Old: "true", + New: "false", + NewRemoved: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "tainted in state w/ no attr changes is still a replacement", + Schema: map[string]*Schema{}, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "id": "someid", + }, + Tainted: true, + }, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + DestroyTainted: true, + }, + + Err: false, + }, + + { + Name: "Set ForceNew only marks the changing element as ForceNew", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Required: true, + ForceNew: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.1": "1", + "ports.2": "2", + "ports.4": "4", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{5, 2, 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "3", + New: "3", + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "1", + New: "1", + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "2", + New: "2", + }, + "ports.5": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + RequiresNew: true, + }, + "ports.4": &terraform.ResourceAttrDiff{ + Old: "4", + New: "0", + NewRemoved: true, + RequiresNew: true, + }, + }, + }, + }, + + { + Name: "removed optional items should trigger ForceNew", + Schema: map[string]*Schema{ + "description": &Schema{ + Type: TypeString, + ForceNew: true, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "description": "foo", + }, + }, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "description": &terraform.ResourceAttrDiff{ + Old: "foo", + New: "", + RequiresNew: true, + NewRemoved: true, + }, + }, + }, + + Err: false, + }, + + // GH-7715 + { + Name: "computed value for boolean field", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeBool, + ForceNew: true, + Computed: true, + Optional: true, + }, + }, + + State: &terraform.InstanceState{}, + + Config: map[string]interface{}{ + "foo": hcl2shim.UnknownVariableValue, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "", + New: "false", + NewComputed: true, + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set ForceNew marks count as ForceNew if computed", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Required: true, + ForceNew: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.1": "1", + "ports.2": "2", + "ports.4": "4", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{hcl2shim.UnknownVariableValue, 2, 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "3", + New: "", + NewComputed: true, + RequiresNew: true, + }, + }, + }, + }, + + { + Name: "List with computed schema and ForceNew", + Schema: map[string]*Schema{ + "config": &Schema{ + Type: TypeList, + Optional: true, + ForceNew: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "config.#": "2", + "config.0": "a", + "config.1": "b", + }, + }, + + Config: map[string]interface{}{ + "config": []interface{}{hcl2shim.UnknownVariableValue, hcl2shim.UnknownVariableValue}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config.#": &terraform.ResourceAttrDiff{ + Old: "2", + New: "", + RequiresNew: true, + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "overridden diff with a CustomizeDiff function, ForceNew not in schema", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("availability_zone", "bar"); err != nil { + return err + } + if err := d.ForceNew("availability_zone"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "bar", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + // NOTE: This case is technically impossible in the current + // implementation, because optional+computed values never show up in the + // diff. In the event behavior changes this test should ensure that the + // intended diff still shows up. + Name: "overridden removed attribute diff with a CustomizeDiff function, ForceNew not in schema", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("availability_zone", "bar"); err != nil { + return err + } + if err := d.ForceNew("availability_zone"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "bar", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + + Name: "overridden diff with a CustomizeDiff function, ForceNew in schema", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("availability_zone", "bar"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "bar", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "required field with computed diff added with CustomizeDiff function", + Schema: map[string]*Schema{ + "ami_id": &Schema{ + Type: TypeString, + Required: true, + }, + "instance_id": &Schema{ + Type: TypeString, + Computed: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ami_id": "foo", + }, + + CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("instance_id", "bar"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ami_id": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + }, + "instance_id": &terraform.ResourceAttrDiff{ + Old: "", + New: "bar", + }, + }, + }, + + Err: false, + }, + + { + Name: "Set ForceNew only marks the changing element as ForceNew - CustomizeDiffFunc edition", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "ports.#": "3", + "ports.1": "1", + "ports.2": "2", + "ports.4": "4", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{5, 2, 6}, + }, + + CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("ports", []interface{}{5, 2, 1}); err != nil { + return err + } + if err := d.ForceNew("ports"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "3", + New: "3", + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "1", + New: "1", + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "2", + New: "2", + }, + "ports.5": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + RequiresNew: true, + }, + "ports.4": &terraform.ResourceAttrDiff{ + Old: "4", + New: "0", + NewRemoved: true, + RequiresNew: true, + }, + }, + }, + }, + + { + Name: "tainted resource does not run CustomizeDiffFunc", + Schema: map[string]*Schema{}, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "id": "someid", + }, + Tainted: true, + }, + + Config: map[string]interface{}{}, + + CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { + return errors.New("diff customization should not have run") + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + DestroyTainted: true, + }, + + Err: false, + }, + + { + Name: "NewComputed based on a conditional with CustomizeDiffFunc", + Schema: map[string]*Schema{ + "etag": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + "version_id": &Schema{ + Type: TypeString, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "etag": "foo", + "version_id": "1", + }, + }, + + Config: map[string]interface{}{ + "etag": "bar", + }, + + CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { + if d.HasChange("etag") { + d.SetNewComputed("version_id") + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "etag": &terraform.ResourceAttrDiff{ + Old: "foo", + New: "bar", + }, + "version_id": &terraform.ResourceAttrDiff{ + Old: "1", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "NewComputed should always propagate with CustomizeDiff", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "", + }, + ID: "pre-existing", + }, + + Config: map[string]interface{}{}, + + CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { + d.SetNewComputed("foo") + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "vetoing a diff", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "foo": "bar", + }, + }, + + Config: map[string]interface{}{ + "foo": "baz", + }, + + CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { + return fmt.Errorf("diff vetoed") + }, + + Err: true, + }, + + // A lot of resources currently depended on using the empty string as a + // nil/unset value. + // FIXME: We want this to eventually produce a diff, since there + // technically is a new value in the config. + { + Name: "optional, computed, empty string", + Schema: map[string]*Schema{ + "attr": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "attr": "bar", + }, + }, + + Config: map[string]interface{}{ + "attr": "", + }, + }, + + { + Name: "optional, computed, empty string should not crash in CustomizeDiff", + Schema: map[string]*Schema{ + "unrelated_set": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "stream_enabled": { + Type: TypeBool, + Optional: true, + }, + "stream_view_type": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + Attributes: map[string]string{ + "unrelated_set.#": "0", + "stream_enabled": "true", + "stream_view_type": "KEYS_ONLY", + }, + }, + Config: map[string]interface{}{ + "stream_enabled": false, + "stream_view_type": "", + }, + CustomizeDiff: func(diff *ResourceDiff, v interface{}) error { + v, ok := diff.GetOk("unrelated_set") + if ok { + return fmt.Errorf("Didn't expect unrelated_set: %#v", v) + } + return nil + }, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "stream_enabled": { + Old: "true", + New: "false", + }, + }, + }, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + c := terraform.NewResourceConfigRaw(tc.Config) + + d, err := schemaMap(tc.Schema).Diff(tc.State, c, tc.CustomizeDiff, nil, true) + if err != nil != tc.Err { + t.Fatalf("err: %s", err) + } + + if !reflect.DeepEqual(tc.Diff, d) { + t.Fatalf("expected:\n%#v\n\ngot:\n%#v", tc.Diff, d) + } + }) + } +} + +func TestSchemaMap_Input(t *testing.T) { + cases := map[string]struct { + Schema map[string]*Schema + Config map[string]interface{} + Input map[string]string + Result map[string]interface{} + Err bool + }{ + /* + * String decode + */ + + "no input on optional field with no config": { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + + Input: map[string]string{}, + Result: map[string]interface{}{}, + Err: false, + }, + + "input ignored when config has a value": { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + + Config: map[string]interface{}{ + "availability_zone": "bar", + }, + + Input: map[string]string{ + "availability_zone": "foo", + }, + + Result: map[string]interface{}{}, + + Err: false, + }, + + "input ignored when schema has a default": { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Default: "foo", + Optional: true, + }, + }, + + Input: map[string]string{ + "availability_zone": "bar", + }, + + Result: map[string]interface{}{}, + + Err: false, + }, + + "input ignored when default function returns a value": { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + DefaultFunc: func() (interface{}, error) { + return "foo", nil + }, + Optional: true, + }, + }, + + Input: map[string]string{ + "availability_zone": "bar", + }, + + Result: map[string]interface{}{}, + + Err: false, + }, + + "input ignored when default function returns an empty string": { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Default: "", + Optional: true, + }, + }, + + Input: map[string]string{ + "availability_zone": "bar", + }, + + Result: map[string]interface{}{}, + + Err: false, + }, + + "input used when default function returns nil": { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + DefaultFunc: func() (interface{}, error) { + return nil, nil + }, + Required: true, + }, + }, + + Input: map[string]string{ + "availability_zone": "bar", + }, + + Result: map[string]interface{}{ + "availability_zone": "bar", + }, + + Err: false, + }, + + "input not used when optional default function returns nil": { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + DefaultFunc: func() (interface{}, error) { + return nil, nil + }, + Optional: true, + }, + }, + + Input: map[string]string{}, + Result: map[string]interface{}{}, + Err: false, + }, + } + + for i, tc := range cases { + if tc.Config == nil { + tc.Config = make(map[string]interface{}) + } + + input := new(terraform.MockUIInput) + input.InputReturnMap = tc.Input + + rc := terraform.NewResourceConfigRaw(tc.Config) + rc.Config = make(map[string]interface{}) + + actual, err := schemaMap(tc.Schema).Input(input, rc) + if err != nil != tc.Err { + t.Fatalf("#%v err: %s", i, err) + } + + if !reflect.DeepEqual(tc.Result, actual.Config) { + t.Fatalf("#%v: bad:\n\ngot: %#v\nexpected: %#v", i, actual.Config, tc.Result) + } + } +} + +func TestSchemaMap_InputDefault(t *testing.T) { + emptyConfig := make(map[string]interface{}) + rc := terraform.NewResourceConfigRaw(emptyConfig) + rc.Config = make(map[string]interface{}) + + input := new(terraform.MockUIInput) + input.InputFn = func(opts *terraform.InputOpts) (string, error) { + t.Fatalf("InputFn should not be called on: %#v", opts) + return "", nil + } + + schema := map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Default: "foo", + Optional: true, + }, + } + actual, err := schemaMap(schema).Input(input, rc) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := map[string]interface{}{} + + if !reflect.DeepEqual(expected, actual.Config) { + t.Fatalf("got: %#v\nexpected: %#v", actual.Config, expected) + } +} + +func TestSchemaMap_InputDeprecated(t *testing.T) { + emptyConfig := make(map[string]interface{}) + rc := terraform.NewResourceConfigRaw(emptyConfig) + rc.Config = make(map[string]interface{}) + + input := new(terraform.MockUIInput) + input.InputFn = func(opts *terraform.InputOpts) (string, error) { + t.Fatalf("InputFn should not be called on: %#v", opts) + return "", nil + } + + schema := map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Deprecated: "long gone", + Optional: true, + }, + } + actual, err := schemaMap(schema).Input(input, rc) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := map[string]interface{}{} + + if !reflect.DeepEqual(expected, actual.Config) { + t.Fatalf("got: %#v\nexpected: %#v", actual.Config, expected) + } +} + +func TestSchemaMap_InternalValidate(t *testing.T) { + cases := map[string]struct { + In map[string]*Schema + Err bool + }{ + "nothing": { + nil, + false, + }, + + "Both optional and required": { + map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + Required: true, + }, + }, + true, + }, + + "No optional and no required": { + map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + }, + }, + true, + }, + + "Missing Type": { + map[string]*Schema{ + "foo": &Schema{ + Required: true, + }, + }, + true, + }, + + "Required but computed": { + map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Required: true, + Computed: true, + }, + }, + true, + }, + + "Looks good": { + map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Required: true, + }, + }, + false, + }, + + "Computed but has default": { + map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + Computed: true, + Default: "foo", + }, + }, + true, + }, + + "Required but has default": { + map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + Required: true, + Default: "foo", + }, + }, + true, + }, + + "List element not set": { + map[string]*Schema{ + "foo": &Schema{ + Type: TypeList, + }, + }, + true, + }, + + "List default": { + map[string]*Schema{ + "foo": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + Default: "foo", + }, + }, + true, + }, + + "List element computed": { + map[string]*Schema{ + "foo": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeInt, + Computed: true, + }, + }, + }, + true, + }, + + "List element with Set set": { + map[string]*Schema{ + "foo": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + Set: func(interface{}) int { return 0 }, + Optional: true, + }, + }, + true, + }, + + "Set element with no Set set": { + map[string]*Schema{ + "foo": &Schema{ + Type: TypeSet, + Elem: &Schema{Type: TypeInt}, + Optional: true, + }, + }, + false, + }, + + "Required but computedWhen": { + map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Required: true, + ComputedWhen: []string{"foo"}, + }, + }, + true, + }, + + "Conflicting attributes cannot be required": { + map[string]*Schema{ + "a": &Schema{ + Type: TypeBool, + Required: true, + }, + "b": &Schema{ + Type: TypeBool, + Optional: true, + ConflictsWith: []string{"a"}, + }, + }, + true, + }, + + "Attribute with conflicts cannot be required": { + map[string]*Schema{ + "b": &Schema{ + Type: TypeBool, + Required: true, + ConflictsWith: []string{"a"}, + }, + }, + true, + }, + + "ConflictsWith cannot be used w/ ComputedWhen": { + map[string]*Schema{ + "a": &Schema{ + Type: TypeBool, + ComputedWhen: []string{"foor"}, + }, + "b": &Schema{ + Type: TypeBool, + Required: true, + ConflictsWith: []string{"a"}, + }, + }, + true, + }, + + "Sub-resource invalid": { + map[string]*Schema{ + "foo": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "foo": new(Schema), + }, + }, + }, + }, + true, + }, + + "Sub-resource valid": { + map[string]*Schema{ + "foo": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + }, + }, + }, + false, + }, + + "ValidateFunc on non-primitive": { + map[string]*Schema{ + "foo": &Schema{ + Type: TypeSet, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + return + }, + }, + }, + true, + }, + + "computed-only field with validateFunc": { + map[string]*Schema{ + "string": &Schema{ + Type: TypeString, + Computed: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + es = append(es, fmt.Errorf("this is not fine")) + return + }, + }, + }, + true, + }, + + "computed-only field with diffSuppressFunc": { + map[string]*Schema{ + "string": &Schema{ + Type: TypeString, + Computed: true, + DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool { + // Always suppress any diff + return false + }, + }, + }, + true, + }, + + "invalid field name format #1": { + map[string]*Schema{ + "with space": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + true, + }, + + "invalid field name format #2": { + map[string]*Schema{ + "WithCapitals": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + true, + }, + + "invalid field name format of a Deprecated field": { + map[string]*Schema{ + "WithCapitals": &Schema{ + Type: TypeString, + Optional: true, + Deprecated: "Use with_underscores instead", + }, + }, + false, + }, + + "invalid field name format of a Removed field": { + map[string]*Schema{ + "WithCapitals": &Schema{ + Type: TypeString, + Optional: true, + Removed: "Use with_underscores instead", + }, + }, + false, + }, + + "ConfigModeBlock with Elem *Resource": { + map[string]*Schema{ + "block": &Schema{ + Type: TypeList, + ConfigMode: SchemaConfigModeBlock, + Optional: true, + Elem: &Resource{}, + }, + }, + false, + }, + + "ConfigModeBlock Computed with Elem *Resource": { + map[string]*Schema{ + "block": &Schema{ + Type: TypeList, + ConfigMode: SchemaConfigModeBlock, + Computed: true, + Elem: &Resource{}, + }, + }, + true, // ConfigMode of block cannot be used for computed schema + }, + + "ConfigModeBlock with Elem *Schema": { + map[string]*Schema{ + "block": &Schema{ + Type: TypeList, + ConfigMode: SchemaConfigModeBlock, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + true, + }, + + "ConfigModeBlock with no Elem": { + map[string]*Schema{ + "block": &Schema{ + Type: TypeString, + ConfigMode: SchemaConfigModeBlock, + Optional: true, + }, + }, + true, + }, + + "ConfigModeBlock inside ConfigModeAttr": { + map[string]*Schema{ + "block": &Schema{ + Type: TypeList, + ConfigMode: SchemaConfigModeAttr, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "sub": &Schema{ + Type: TypeList, + ConfigMode: SchemaConfigModeBlock, + Elem: &Resource{}, + }, + }, + }, + }, + }, + true, // ConfigMode of block cannot be used in child of schema with ConfigMode of attribute + }, + + "ConfigModeAuto with *Resource inside ConfigModeAttr": { + map[string]*Schema{ + "block": &Schema{ + Type: TypeList, + ConfigMode: SchemaConfigModeAttr, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "sub": &Schema{ + Type: TypeList, + Elem: &Resource{}, + }, + }, + }, + }, + }, + true, // in *schema.Resource with ConfigMode of attribute, so must also have ConfigMode of attribute + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + err := schemaMap(tc.In).InternalValidate(nil) + if err != nil != tc.Err { + if tc.Err { + t.Fatalf("%q: Expected error did not occur:\n\n%#v", tn, tc.In) + } + t.Fatalf("%q: Unexpected error occurred: %s\n\n%#v", tn, err, tc.In) + } + }) + } + +} + +func TestSchemaMap_DiffSuppress(t *testing.T) { + cases := map[string]struct { + Schema map[string]*Schema + State *terraform.InstanceState + Config map[string]interface{} + ExpectedDiff *terraform.InstanceDiff + Err bool + }{ + "#0 - Suppress otherwise valid diff by returning true": { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool { + // Always suppress any diff + return true + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + ExpectedDiff: nil, + + Err: false, + }, + + "#1 - Don't suppress diff by returning false": { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool { + // Always suppress any diff + return false + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + ExpectedDiff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + }, + }, + }, + + Err: false, + }, + + "Default with suppress makes no diff": { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Default: "foo", + DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool { + return true + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + ExpectedDiff: nil, + + Err: false, + }, + + "Default with false suppress makes diff": { + Schema: map[string]*Schema{ + "availability_zone": { + Type: TypeString, + Optional: true, + Default: "foo", + DiffSuppressFunc: func(k, old, new string, d *ResourceData) bool { + return false + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + ExpectedDiff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": { + Old: "", + New: "foo", + }, + }, + }, + + Err: false, + }, + + "Complex structure with set of computed string should mark root set as computed": { + Schema: map[string]*Schema{ + "outer": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "outer_str": &Schema{ + Type: TypeString, + Optional: true, + }, + "inner": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "inner_str": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + }, + Set: func(v interface{}) int { + return 2 + }, + }, + }, + }, + Set: func(v interface{}) int { + return 1 + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "outer": []interface{}{ + map[string]interface{}{ + "outer_str": "foo", + "inner": []interface{}{ + map[string]interface{}{ + "inner_str": hcl2shim.UnknownVariableValue, + }, + }, + }, + }, + }, + + ExpectedDiff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "outer.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + "outer.~1.outer_str": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + }, + "outer.~1.inner.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + "outer.~1.inner.~2.inner_str": &terraform.ResourceAttrDiff{ + Old: "", + New: hcl2shim.UnknownVariableValue, + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + "Complex structure with complex list of computed string should mark root set as computed": { + Schema: map[string]*Schema{ + "outer": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "outer_str": &Schema{ + Type: TypeString, + Optional: true, + }, + "inner": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "inner_str": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + }, + }, + }, + }, + Set: func(v interface{}) int { + return 1 + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "outer": []interface{}{ + map[string]interface{}{ + "outer_str": "foo", + "inner": []interface{}{ + map[string]interface{}{ + "inner_str": hcl2shim.UnknownVariableValue, + }, + }, + }, + }, + }, + + ExpectedDiff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "outer.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + "outer.~1.outer_str": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + }, + "outer.~1.inner.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + "outer.~1.inner.0.inner_str": &terraform.ResourceAttrDiff{ + Old: "", + New: hcl2shim.UnknownVariableValue, + NewComputed: true, + }, + }, + }, + + Err: false, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + c := terraform.NewResourceConfigRaw(tc.Config) + + d, err := schemaMap(tc.Schema).Diff(tc.State, c, nil, nil, true) + if err != nil != tc.Err { + t.Fatalf("#%q err: %s", tn, err) + } + + if !reflect.DeepEqual(tc.ExpectedDiff, d) { + t.Fatalf("#%q:\n\nexpected:\n%#v\n\ngot:\n%#v", tn, tc.ExpectedDiff, d) + } + }) + } +} + +func TestSchemaMap_Validate(t *testing.T) { + cases := map[string]struct { + Schema map[string]*Schema + Config map[string]interface{} + Err bool + Errors []error + Warnings []string + }{ + "Good": { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + }, + + "Good, because the var is not set and that error will come elsewhere": { + Schema: map[string]*Schema{ + "size": &Schema{ + Type: TypeInt, + Required: true, + }, + }, + + Config: map[string]interface{}{ + "size": hcl2shim.UnknownVariableValue, + }, + }, + + "Required field not set": { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Required: true, + }, + }, + + Config: map[string]interface{}{}, + + Err: true, + }, + + "Invalid basic type": { + Schema: map[string]*Schema{ + "port": &Schema{ + Type: TypeInt, + Required: true, + }, + }, + + Config: map[string]interface{}{ + "port": "I am invalid", + }, + + Err: true, + }, + + "Invalid complex type": { + Schema: map[string]*Schema{ + "user_data": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + + Config: map[string]interface{}{ + "user_data": []interface{}{ + map[string]interface{}{ + "foo": "bar", + }, + }, + }, + + Err: true, + }, + + "Bad type": { + Schema: map[string]*Schema{ + "size": &Schema{ + Type: TypeInt, + Required: true, + }, + }, + + Config: map[string]interface{}{ + "size": "nope", + }, + + Err: true, + }, + + "Required but has DefaultFunc": { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Required: true, + DefaultFunc: func() (interface{}, error) { + return "foo", nil + }, + }, + }, + + Config: nil, + }, + + "Required but has DefaultFunc return nil": { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Required: true, + DefaultFunc: func() (interface{}, error) { + return nil, nil + }, + }, + }, + + Config: nil, + + Err: true, + }, + + "List with promotion": { + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + PromoteSingle: true, + Optional: true, + }, + }, + + Config: map[string]interface{}{ + "ingress": "5", + }, + + Err: false, + }, + + "List with promotion set as list": { + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeInt}, + PromoteSingle: true, + Optional: true, + }, + }, + + Config: map[string]interface{}{ + "ingress": []interface{}{"5"}, + }, + + Err: false, + }, + + "Optional sub-resource": { + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeList, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": &Schema{ + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + Config: map[string]interface{}{}, + + Err: false, + }, + + "Sub-resource is the wrong type": { + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": &Schema{ + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + Config: map[string]interface{}{ + "ingress": []interface{}{"foo"}, + }, + + Err: true, + }, + + "Not a list nested block": { + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": &Schema{ + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + Config: map[string]interface{}{ + "ingress": "foo", + }, + + Err: true, + Errors: []error{ + fmt.Errorf(`ingress: should be a list`), + }, + }, + + "Not a list primitive": { + Schema: map[string]*Schema{ + "strings": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + + Config: map[string]interface{}{ + "strings": "foo", + }, + + Err: true, + Errors: []error{ + fmt.Errorf(`strings: should be a list`), + }, + }, + + "Unknown list": { + Schema: map[string]*Schema{ + "strings": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + + Config: map[string]interface{}{ + "strings": hcl2shim.UnknownVariableValue, + }, + + Err: false, + }, + + "Unknown + Deprecation": { + Schema: map[string]*Schema{ + "old_news": &Schema{ + Type: TypeString, + Optional: true, + Deprecated: "please use 'new_news' instead", + }, + }, + + Config: map[string]interface{}{ + "old_news": hcl2shim.UnknownVariableValue, + }, + + Warnings: []string{ + "\"old_news\": [DEPRECATED] please use 'new_news' instead", + }, + }, + + "Required sub-resource field": { + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeList, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": &Schema{ + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + Config: map[string]interface{}{ + "ingress": []interface{}{ + map[string]interface{}{}, + }, + }, + + Err: true, + }, + + "Good sub-resource": { + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": &Schema{ + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + Config: map[string]interface{}{ + "ingress": []interface{}{ + map[string]interface{}{ + "from": 80, + }, + }, + }, + + Err: false, + }, + + "Good sub-resource, computed value": { + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + }, + }, + }, + + Config: map[string]interface{}{ + "ingress": []interface{}{ + map[string]interface{}{ + "from": hcl2shim.UnknownVariableValue, + }, + }, + }, + + Err: false, + }, + + "Invalid/unknown field": { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + Config: map[string]interface{}{ + "foo": "bar", + }, + + Err: true, + }, + + "Invalid/unknown field with computed value": { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + Config: map[string]interface{}{ + "foo": hcl2shim.UnknownVariableValue, + }, + + Err: true, + }, + + "Computed field set": { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Computed: true, + }, + }, + + Config: map[string]interface{}{ + "availability_zone": "bar", + }, + + Err: true, + }, + + "Not a set": { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + Config: map[string]interface{}{ + "ports": "foo", + }, + + Err: true, + }, + + "Maps": { + Schema: map[string]*Schema{ + "user_data": &Schema{ + Type: TypeMap, + Optional: true, + }, + }, + + Config: map[string]interface{}{ + "user_data": "foo", + }, + + Err: true, + }, + + "Good map: data surrounded by extra slice": { + Schema: map[string]*Schema{ + "user_data": &Schema{ + Type: TypeMap, + Optional: true, + }, + }, + + Config: map[string]interface{}{ + "user_data": []interface{}{ + map[string]interface{}{ + "foo": "bar", + }, + }, + }, + }, + + "Good map": { + Schema: map[string]*Schema{ + "user_data": &Schema{ + Type: TypeMap, + Optional: true, + }, + }, + + Config: map[string]interface{}{ + "user_data": map[string]interface{}{ + "foo": "bar", + }, + }, + }, + + "Map with type specified as value type": { + Schema: map[string]*Schema{ + "user_data": &Schema{ + Type: TypeMap, + Optional: true, + Elem: TypeBool, + }, + }, + + Config: map[string]interface{}{ + "user_data": map[string]interface{}{ + "foo": "not_a_bool", + }, + }, + + Err: true, + }, + + "Map with type specified as nested Schema": { + Schema: map[string]*Schema{ + "user_data": &Schema{ + Type: TypeMap, + Optional: true, + Elem: &Schema{Type: TypeBool}, + }, + }, + + Config: map[string]interface{}{ + "user_data": map[string]interface{}{ + "foo": "not_a_bool", + }, + }, + + Err: true, + }, + + "Bad map: just a slice": { + Schema: map[string]*Schema{ + "user_data": &Schema{ + Type: TypeMap, + Optional: true, + }, + }, + + Config: map[string]interface{}{ + "user_data": []interface{}{ + "foo", + }, + }, + + Err: true, + }, + + "Good set: config has slice with single interpolated value": { + Schema: map[string]*Schema{ + "security_groups": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + ForceNew: true, + Elem: &Schema{Type: TypeString}, + Set: func(v interface{}) int { + return len(v.(string)) + }, + }, + }, + + Config: map[string]interface{}{ + "security_groups": []interface{}{"${var.foo}"}, + }, + + Err: false, + }, + + "Bad set: config has single interpolated value": { + Schema: map[string]*Schema{ + "security_groups": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + ForceNew: true, + Elem: &Schema{Type: TypeString}, + }, + }, + + Config: map[string]interface{}{ + "security_groups": "${var.foo}", + }, + + Err: true, + }, + + "Bad, subresource should not allow unknown elements": { + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "port": &Schema{ + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + Config: map[string]interface{}{ + "ingress": []interface{}{ + map[string]interface{}{ + "port": 80, + "other": "yes", + }, + }, + }, + + Err: true, + }, + + "Bad, subresource should not allow invalid types": { + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "port": &Schema{ + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + Config: map[string]interface{}{ + "ingress": []interface{}{ + map[string]interface{}{ + "port": "bad", + }, + }, + }, + + Err: true, + }, + + "Bad, should not allow lists to be assigned to string attributes": { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Required: true, + }, + }, + + Config: map[string]interface{}{ + "availability_zone": []interface{}{"foo", "bar", "baz"}, + }, + + Err: true, + }, + + "Bad, should not allow maps to be assigned to string attributes": { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Required: true, + }, + }, + + Config: map[string]interface{}{ + "availability_zone": map[string]interface{}{"foo": "bar", "baz": "thing"}, + }, + + Err: true, + }, + + "Deprecated attribute usage generates warning, but not error": { + Schema: map[string]*Schema{ + "old_news": &Schema{ + Type: TypeString, + Optional: true, + Deprecated: "please use 'new_news' instead", + }, + }, + + Config: map[string]interface{}{ + "old_news": "extra extra!", + }, + + Err: false, + + Warnings: []string{ + "\"old_news\": [DEPRECATED] please use 'new_news' instead", + }, + }, + + "Deprecated generates no warnings if attr not used": { + Schema: map[string]*Schema{ + "old_news": &Schema{ + Type: TypeString, + Optional: true, + Deprecated: "please use 'new_news' instead", + }, + }, + + Err: false, + + Warnings: nil, + }, + + "Removed attribute usage generates error": { + Schema: map[string]*Schema{ + "long_gone": &Schema{ + Type: TypeString, + Optional: true, + Removed: "no longer supported by Cloud API", + }, + }, + + Config: map[string]interface{}{ + "long_gone": "still here!", + }, + + Err: true, + Errors: []error{ + fmt.Errorf("\"long_gone\": [REMOVED] no longer supported by Cloud API"), + }, + }, + + "Removed generates no errors if attr not used": { + Schema: map[string]*Schema{ + "long_gone": &Schema{ + Type: TypeString, + Optional: true, + Removed: "no longer supported by Cloud API", + }, + }, + + Err: false, + }, + + "Conflicting attributes generate error": { + Schema: map[string]*Schema{ + "b": &Schema{ + Type: TypeString, + Optional: true, + }, + "a": &Schema{ + Type: TypeString, + Optional: true, + ConflictsWith: []string{"b"}, + }, + }, + + Config: map[string]interface{}{ + "b": "b-val", + "a": "a-val", + }, + + Err: true, + Errors: []error{ + fmt.Errorf("\"a\": conflicts with b"), + }, + }, + + "Conflicting attributes okay when unknown 1": { + Schema: map[string]*Schema{ + "b": &Schema{ + Type: TypeString, + Optional: true, + }, + "a": &Schema{ + Type: TypeString, + Optional: true, + ConflictsWith: []string{"b"}, + }, + }, + + Config: map[string]interface{}{ + "b": "b-val", + "a": hcl2shim.UnknownVariableValue, + }, + + Err: false, + }, + + "Conflicting attributes okay when unknown 2": { + Schema: map[string]*Schema{ + "b": &Schema{ + Type: TypeString, + Optional: true, + }, + "a": &Schema{ + Type: TypeString, + Optional: true, + ConflictsWith: []string{"b"}, + }, + }, + + Config: map[string]interface{}{ + "b": hcl2shim.UnknownVariableValue, + "a": "a-val", + }, + + Err: false, + }, + + "Conflicting attributes generate error even if one is unknown": { + Schema: map[string]*Schema{ + "b": &Schema{ + Type: TypeString, + Optional: true, + ConflictsWith: []string{"a", "c"}, + }, + "a": &Schema{ + Type: TypeString, + Optional: true, + ConflictsWith: []string{"b", "c"}, + }, + "c": &Schema{ + Type: TypeString, + Optional: true, + ConflictsWith: []string{"b", "a"}, + }, + }, + + Config: map[string]interface{}{ + "b": hcl2shim.UnknownVariableValue, + "a": "a-val", + "c": "c-val", + }, + + Err: true, + Errors: []error{ + fmt.Errorf("\"a\": conflicts with c"), + fmt.Errorf("\"c\": conflicts with a"), + }, + }, + + "Required attribute & undefined conflicting optional are good": { + Schema: map[string]*Schema{ + "required_att": &Schema{ + Type: TypeString, + Required: true, + }, + "optional_att": &Schema{ + Type: TypeString, + Optional: true, + ConflictsWith: []string{"required_att"}, + }, + }, + + Config: map[string]interface{}{ + "required_att": "required-val", + }, + + Err: false, + }, + + "Required conflicting attribute & defined optional generate error": { + Schema: map[string]*Schema{ + "required_att": &Schema{ + Type: TypeString, + Required: true, + }, + "optional_att": &Schema{ + Type: TypeString, + Optional: true, + ConflictsWith: []string{"required_att"}, + }, + }, + + Config: map[string]interface{}{ + "required_att": "required-val", + "optional_att": "optional-val", + }, + + Err: true, + Errors: []error{ + fmt.Errorf(`"optional_att": conflicts with required_att`), + }, + }, + + "Computed + Optional fields conflicting with each other": { + Schema: map[string]*Schema{ + "foo_att": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"bar_att"}, + }, + "bar_att": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"foo_att"}, + }, + }, + + Config: map[string]interface{}{ + "foo_att": "foo-val", + "bar_att": "bar-val", + }, + + Err: true, + Errors: []error{ + fmt.Errorf(`"foo_att": conflicts with bar_att`), + fmt.Errorf(`"bar_att": conflicts with foo_att`), + }, + }, + + "Computed + Optional fields NOT conflicting with each other": { + Schema: map[string]*Schema{ + "foo_att": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"bar_att"}, + }, + "bar_att": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"foo_att"}, + }, + }, + + Config: map[string]interface{}{ + "foo_att": "foo-val", + }, + + Err: false, + }, + + "Computed + Optional fields that conflict with none set": { + Schema: map[string]*Schema{ + "foo_att": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"bar_att"}, + }, + "bar_att": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"foo_att"}, + }, + }, + + Config: map[string]interface{}{}, + + Err: false, + }, + + "Good with ValidateFunc": { + Schema: map[string]*Schema{ + "validate_me": &Schema{ + Type: TypeString, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + return + }, + }, + }, + Config: map[string]interface{}{ + "validate_me": "valid", + }, + Err: false, + }, + + "Bad with ValidateFunc": { + Schema: map[string]*Schema{ + "validate_me": &Schema{ + Type: TypeString, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + es = append(es, fmt.Errorf("something is not right here")) + return + }, + }, + }, + Config: map[string]interface{}{ + "validate_me": "invalid", + }, + Err: true, + Errors: []error{ + fmt.Errorf(`something is not right here`), + }, + }, + + "ValidateFunc not called when type does not match": { + Schema: map[string]*Schema{ + "number": &Schema{ + Type: TypeInt, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + t.Fatalf("Should not have gotten validate call") + return + }, + }, + }, + Config: map[string]interface{}{ + "number": "NaN", + }, + Err: true, + }, + + "ValidateFunc gets decoded type": { + Schema: map[string]*Schema{ + "maybe": &Schema{ + Type: TypeBool, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + if _, ok := v.(bool); !ok { + t.Fatalf("Expected bool, got: %#v", v) + } + return + }, + }, + }, + Config: map[string]interface{}{ + "maybe": "true", + }, + }, + + "ValidateFunc is not called with a computed value": { + Schema: map[string]*Schema{ + "validate_me": &Schema{ + Type: TypeString, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + es = append(es, fmt.Errorf("something is not right here")) + return + }, + }, + }, + Config: map[string]interface{}{ + "validate_me": hcl2shim.UnknownVariableValue, + }, + + Err: false, + }, + + "special timeouts field": { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + Config: map[string]interface{}{ + TimeoutsConfigKey: "bar", + }, + + Err: false, + }, + + "invalid bool field": { + Schema: map[string]*Schema{ + "bool_field": { + Type: TypeBool, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "bool_field": "abcdef", + }, + Err: true, + }, + "invalid integer field": { + Schema: map[string]*Schema{ + "integer_field": { + Type: TypeInt, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "integer_field": "abcdef", + }, + Err: true, + }, + "invalid float field": { + Schema: map[string]*Schema{ + "float_field": { + Type: TypeFloat, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "float_field": "abcdef", + }, + Err: true, + }, + + // Invalid map values + "invalid bool map value": { + Schema: map[string]*Schema{ + "boolMap": &Schema{ + Type: TypeMap, + Elem: TypeBool, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "boolMap": map[string]interface{}{ + "boolField": "notbool", + }, + }, + Err: true, + }, + "invalid int map value": { + Schema: map[string]*Schema{ + "intMap": &Schema{ + Type: TypeMap, + Elem: TypeInt, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "intMap": map[string]interface{}{ + "intField": "notInt", + }, + }, + Err: true, + }, + "invalid float map value": { + Schema: map[string]*Schema{ + "floatMap": &Schema{ + Type: TypeMap, + Elem: TypeFloat, + Optional: true, + }, + }, + Config: map[string]interface{}{ + "floatMap": map[string]interface{}{ + "floatField": "notFloat", + }, + }, + Err: true, + }, + + "map with positive validate function": { + Schema: map[string]*Schema{ + "floatInt": &Schema{ + Type: TypeMap, + Elem: TypeInt, + Optional: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + return + }, + }, + }, + Config: map[string]interface{}{ + "floatInt": map[string]interface{}{ + "rightAnswer": "42", + "tooMuch": "43", + }, + }, + Err: false, + }, + "map with negative validate function": { + Schema: map[string]*Schema{ + "floatInt": &Schema{ + Type: TypeMap, + Elem: TypeInt, + Optional: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + es = append(es, fmt.Errorf("this is not fine")) + return + }, + }, + }, + Config: map[string]interface{}{ + "floatInt": map[string]interface{}{ + "rightAnswer": "42", + "tooMuch": "43", + }, + }, + Err: true, + }, + + // The Validation function should not see interpolation strings from + // non-computed values. + "set with partially computed list and map": { + Schema: map[string]*Schema{ + "outer": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "list": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + if strings.HasPrefix(v.(string), "${") { + es = append(es, fmt.Errorf("should not have interpolations")) + } + return + }, + }, + }, + }, + }, + }, + }, + Config: map[string]interface{}{ + "outer": []interface{}{ + map[string]interface{}{ + "list": []interface{}{"A", hcl2shim.UnknownVariableValue, "c"}, + }, + }, + }, + Err: false, + }, + "unexpected nils values": { + Schema: map[string]*Schema{ + "strings": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + "block": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "int": &Schema{ + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + Config: map[string]interface{}{ + "strings": []interface{}{"1", nil}, + "block": []interface{}{map[string]interface{}{ + "int": nil, + }, + nil, + }, + }, + Err: true, + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + c := terraform.NewResourceConfigRaw(tc.Config) + + ws, es := schemaMap(tc.Schema).Validate(c) + if len(es) > 0 != tc.Err { + if len(es) == 0 { + t.Errorf("%q: no errors", tn) + } + + for _, e := range es { + t.Errorf("%q: err: %s", tn, e) + } + + t.FailNow() + } + + if !reflect.DeepEqual(ws, tc.Warnings) { + t.Fatalf("%q: warnings:\n\nexpected: %#v\ngot:%#v", tn, tc.Warnings, ws) + } + + if tc.Errors != nil { + sort.Sort(errorSort(es)) + sort.Sort(errorSort(tc.Errors)) + + if !reflect.DeepEqual(es, tc.Errors) { + t.Fatalf("%q: errors:\n\nexpected: %q\ngot: %q", tn, tc.Errors, es) + } + } + }) + + } +} + +func TestSchemaSet_ValidateMaxItems(t *testing.T) { + cases := map[string]struct { + Schema map[string]*Schema + State *terraform.InstanceState + Config map[string]interface{} + ConfigVariables map[string]string + Diff *terraform.InstanceDiff + Err bool + Errors []error + }{ + "#0": { + Schema: map[string]*Schema{ + "aliases": &Schema{ + Type: TypeSet, + Optional: true, + MaxItems: 1, + Elem: &Schema{Type: TypeString}, + }, + }, + State: nil, + Config: map[string]interface{}{ + "aliases": []interface{}{"foo", "bar"}, + }, + Diff: nil, + Err: true, + Errors: []error{ + fmt.Errorf("aliases: attribute supports 1 item maximum, config has 2 declared"), + }, + }, + "#1": { + Schema: map[string]*Schema{ + "aliases": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + }, + State: nil, + Config: map[string]interface{}{ + "aliases": []interface{}{"foo", "bar"}, + }, + Diff: nil, + Err: false, + Errors: nil, + }, + "#2": { + Schema: map[string]*Schema{ + "aliases": &Schema{ + Type: TypeSet, + Optional: true, + MaxItems: 1, + Elem: &Schema{Type: TypeString}, + }, + }, + State: nil, + Config: map[string]interface{}{ + "aliases": []interface{}{"foo"}, + }, + Diff: nil, + Err: false, + Errors: nil, + }, + } + + for tn, tc := range cases { + c := terraform.NewResourceConfigRaw(tc.Config) + _, es := schemaMap(tc.Schema).Validate(c) + + if len(es) > 0 != tc.Err { + if len(es) == 0 { + t.Errorf("%q: no errors", tn) + } + + for _, e := range es { + t.Errorf("%q: err: %s", tn, e) + } + + t.FailNow() + } + + if tc.Errors != nil { + if !reflect.DeepEqual(es, tc.Errors) { + t.Fatalf("%q: expected: %q\ngot: %q", tn, tc.Errors, es) + } + } + } +} + +func TestSchemaSet_ValidateMinItems(t *testing.T) { + cases := map[string]struct { + Schema map[string]*Schema + State *terraform.InstanceState + Config map[string]interface{} + ConfigVariables map[string]string + Diff *terraform.InstanceDiff + Err bool + Errors []error + }{ + "#0": { + Schema: map[string]*Schema{ + "aliases": &Schema{ + Type: TypeSet, + Optional: true, + MinItems: 2, + Elem: &Schema{Type: TypeString}, + }, + }, + State: nil, + Config: map[string]interface{}{ + "aliases": []interface{}{"foo", "bar"}, + }, + Diff: nil, + Err: false, + Errors: nil, + }, + "#1": { + Schema: map[string]*Schema{ + "aliases": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + }, + State: nil, + Config: map[string]interface{}{ + "aliases": []interface{}{"foo", "bar"}, + }, + Diff: nil, + Err: false, + Errors: nil, + }, + "#2": { + Schema: map[string]*Schema{ + "aliases": &Schema{ + Type: TypeSet, + Optional: true, + MinItems: 2, + Elem: &Schema{Type: TypeString}, + }, + }, + State: nil, + Config: map[string]interface{}{ + "aliases": []interface{}{"foo"}, + }, + Diff: nil, + Err: true, + Errors: []error{ + fmt.Errorf("aliases: attribute supports 2 item as a minimum, config has 1 declared"), + }, + }, + } + + for tn, tc := range cases { + c := terraform.NewResourceConfigRaw(tc.Config) + _, es := schemaMap(tc.Schema).Validate(c) + + if len(es) > 0 != tc.Err { + if len(es) == 0 { + t.Errorf("%q: no errors", tn) + } + + for _, e := range es { + t.Errorf("%q: err: %s", tn, e) + } + + t.FailNow() + } + + if tc.Errors != nil { + if !reflect.DeepEqual(es, tc.Errors) { + t.Fatalf("%q: expected: %q\ngot: %q", tn, tc.Errors, es) + } + } + } +} + +// errorSort implements sort.Interface to sort errors by their error message +type errorSort []error + +func (e errorSort) Len() int { return len(e) } +func (e errorSort) Swap(i, j int) { e[i], e[j] = e[j], e[i] } +func (e errorSort) Less(i, j int) bool { + return e[i].Error() < e[j].Error() +} + +func TestSchemaMapDeepCopy(t *testing.T) { + schema := map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + }, + } + source := schemaMap(schema) + dest := source.DeepCopy() + dest["foo"].ForceNew = true + if reflect.DeepEqual(source, dest) { + t.Fatalf("source and dest should not match") + } +} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/serialize.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/serialize.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/serialize.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/serialize.go diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/serialize_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/serialize_test.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/serialize_test.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/serialize_test.go diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/set.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/set.go new file mode 100644 index 00000000..b44035c7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/set.go @@ -0,0 +1,250 @@ +package schema + +import ( + "bytes" + "fmt" + "reflect" + "sort" + "strconv" + "sync" + + "github.com/hashicorp/terraform/internal/legacy/helper/hashcode" +) + +// HashString hashes strings. If you want a Set of strings, this is the +// SchemaSetFunc you want. +func HashString(v interface{}) int { + return hashcode.String(v.(string)) +} + +// HashInt hashes integers. If you want a Set of integers, this is the +// SchemaSetFunc you want. +func HashInt(v interface{}) int { + return hashcode.String(strconv.Itoa(v.(int))) +} + +// HashResource hashes complex structures that are described using +// a *Resource. This is the default set implementation used when a set's +// element type is a full resource. +func HashResource(resource *Resource) SchemaSetFunc { + return func(v interface{}) int { + var buf bytes.Buffer + SerializeResourceForHash(&buf, v, resource) + return hashcode.String(buf.String()) + } +} + +// HashSchema hashes values that are described using a *Schema. This is the +// default set implementation used when a set's element type is a single +// schema. +func HashSchema(schema *Schema) SchemaSetFunc { + return func(v interface{}) int { + var buf bytes.Buffer + SerializeValueForHash(&buf, v, schema) + return hashcode.String(buf.String()) + } +} + +// Set is a set data structure that is returned for elements of type +// TypeSet. +type Set struct { + F SchemaSetFunc + + m map[string]interface{} + once sync.Once +} + +// NewSet is a convenience method for creating a new set with the given +// items. +func NewSet(f SchemaSetFunc, items []interface{}) *Set { + s := &Set{F: f} + for _, i := range items { + s.Add(i) + } + + return s +} + +// CopySet returns a copy of another set. +func CopySet(otherSet *Set) *Set { + return NewSet(otherSet.F, otherSet.List()) +} + +// Add adds an item to the set if it isn't already in the set. +func (s *Set) Add(item interface{}) { + s.add(item, false) +} + +// Remove removes an item if it's already in the set. Idempotent. +func (s *Set) Remove(item interface{}) { + s.remove(item) +} + +// Contains checks if the set has the given item. +func (s *Set) Contains(item interface{}) bool { + _, ok := s.m[s.hash(item)] + return ok +} + +// Len returns the amount of items in the set. +func (s *Set) Len() int { + return len(s.m) +} + +// List returns the elements of this set in slice format. +// +// The order of the returned elements is deterministic. Given the same +// set, the order of this will always be the same. +func (s *Set) List() []interface{} { + result := make([]interface{}, len(s.m)) + for i, k := range s.listCode() { + result[i] = s.m[k] + } + + return result +} + +// Difference performs a set difference of the two sets, returning +// a new third set that has only the elements unique to this set. +func (s *Set) Difference(other *Set) *Set { + result := &Set{F: s.F} + result.once.Do(result.init) + + for k, v := range s.m { + if _, ok := other.m[k]; !ok { + result.m[k] = v + } + } + + return result +} + +// Intersection performs the set intersection of the two sets +// and returns a new third set. +func (s *Set) Intersection(other *Set) *Set { + result := &Set{F: s.F} + result.once.Do(result.init) + + for k, v := range s.m { + if _, ok := other.m[k]; ok { + result.m[k] = v + } + } + + return result +} + +// Union performs the set union of the two sets and returns a new third +// set. +func (s *Set) Union(other *Set) *Set { + result := &Set{F: s.F} + result.once.Do(result.init) + + for k, v := range s.m { + result.m[k] = v + } + for k, v := range other.m { + result.m[k] = v + } + + return result +} + +func (s *Set) Equal(raw interface{}) bool { + other, ok := raw.(*Set) + if !ok { + return false + } + + return reflect.DeepEqual(s.m, other.m) +} + +// HashEqual simply checks to the keys the top-level map to the keys in the +// other set's top-level map to see if they are equal. This obviously assumes +// you have a properly working hash function - use HashResource if in doubt. +func (s *Set) HashEqual(raw interface{}) bool { + other, ok := raw.(*Set) + if !ok { + return false + } + + ks1 := make([]string, 0) + ks2 := make([]string, 0) + + for k := range s.m { + ks1 = append(ks1, k) + } + for k := range other.m { + ks2 = append(ks2, k) + } + + sort.Strings(ks1) + sort.Strings(ks2) + + return reflect.DeepEqual(ks1, ks2) +} + +func (s *Set) GoString() string { + return fmt.Sprintf("*Set(%#v)", s.m) +} + +func (s *Set) init() { + s.m = make(map[string]interface{}) +} + +func (s *Set) add(item interface{}, computed bool) string { + s.once.Do(s.init) + + code := s.hash(item) + if computed { + code = "~" + code + + if isProto5() { + tmpCode := code + count := 0 + for _, exists := s.m[tmpCode]; exists; _, exists = s.m[tmpCode] { + count++ + tmpCode = fmt.Sprintf("%s%d", code, count) + } + code = tmpCode + } + } + + if _, ok := s.m[code]; !ok { + s.m[code] = item + } + + return code +} + +func (s *Set) hash(item interface{}) string { + code := s.F(item) + // Always return a nonnegative hashcode. + if code < 0 { + code = -code + } + return strconv.Itoa(code) +} + +func (s *Set) remove(item interface{}) string { + s.once.Do(s.init) + + code := s.hash(item) + delete(s.m, code) + + return code +} + +func (s *Set) index(item interface{}) int { + return sort.SearchStrings(s.listCode(), s.hash(item)) +} + +func (s *Set) listCode() []string { + // Sort the hash codes so the order of the list is deterministic + keys := make([]string, 0, len(s.m)) + for k := range s.m { + keys = append(keys, k) + } + sort.Sort(sort.StringSlice(keys)) + return keys +} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/set_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/set_test.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/set_test.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/set_test.go diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/shims.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/shims.go similarity index 98% rename from vendor/github.com/hashicorp/terraform/helper/schema/shims.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/shims.go index d2dbff53..b8cbf6b2 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/shims.go +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/shims.go @@ -8,7 +8,7 @@ import ( "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/configs/hcl2shim" - "github.com/hashicorp/terraform/terraform" + "github.com/hashicorp/terraform/internal/legacy/terraform" ) // DiffFromValues takes the current state and desired state as cty.Values and diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/shims_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/shims_test.go new file mode 100644 index 00000000..90e616e6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/shims_test.go @@ -0,0 +1,3521 @@ +package schema + +import ( + "bytes" + "errors" + "fmt" + "reflect" + "strconv" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/configs/hcl2shim" + "github.com/hashicorp/terraform/internal/legacy/helper/hashcode" + "github.com/hashicorp/terraform/internal/legacy/terraform" + "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/tfdiags" + "github.com/zclconf/go-cty/cty" +) + +var ( + typeComparer = cmp.Comparer(cty.Type.Equals) + valueComparer = cmp.Comparer(cty.Value.RawEquals) + equateEmpty = cmpopts.EquateEmpty() +) + +func testApplyDiff(t *testing.T, + resource *Resource, + state, expected *terraform.InstanceState, + diff *terraform.InstanceDiff) { + + testSchema := providers.Schema{ + Version: int64(resource.SchemaVersion), + Block: resourceSchemaToBlock(resource.Schema), + } + + stateVal, err := StateValueFromInstanceState(state, testSchema.Block.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + newState, err := ApplyDiff(stateVal, diff, testSchema.Block) + if err != nil { + t.Fatal(err) + } + + // verify that "id" is correct + id := newState.AsValueMap()["id"] + + switch { + case diff.Destroy || diff.DestroyDeposed || diff.DestroyTainted: + // there should be no id + if !id.IsNull() { + t.Fatalf("destroyed instance should have no id: %#v", id) + } + default: + // the "id" field always exists and is computed, so it must have a + // valid value or be unknown. + if id.IsNull() { + t.Fatal("new instance state cannot have a null id") + } + + if id.IsKnown() && id.AsString() == "" { + t.Fatal("new instance id cannot be an empty string") + } + } + + // Resource.Meta will be hanlded separately, so it's OK that we lose the + // timeout values here. + expectedState, err := StateValueFromInstanceState(expected, testSchema.Block.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + if !cmp.Equal(expectedState, newState, equateEmpty, typeComparer, valueComparer) { + t.Fatalf(cmp.Diff(expectedState, newState, equateEmpty, typeComparer, valueComparer)) + } +} + +func TestShimResourcePlan_destroyCreate(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + ForceNew: true, + }, + }, + } + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + RequiresNew: true, + Old: "3", + New: "42", + }, + }, + } + + state := &terraform.InstanceState{ + Attributes: map[string]string{"foo": "3"}, + } + + expected := &terraform.InstanceState{ + ID: hcl2shim.UnknownVariableValue, + Attributes: map[string]string{ + "id": hcl2shim.UnknownVariableValue, + "foo": "42", + }, + Meta: map[string]interface{}{ + "schema_version": "2", + }, + } + + testApplyDiff(t, r, state, expected, d) +} + +func TestShimResourceApply_create(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + called := false + r.Create = func(d *ResourceData, m interface{}) error { + called = true + d.SetId("foo") + return nil + } + + var s *terraform.InstanceState = nil + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + New: "42", + }, + }, + } + + actual, err := r.Apply(s, d, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !called { + t.Fatal("not called") + } + + expected := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "42", + }, + Meta: map[string]interface{}{ + "schema_version": "2", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } + + // Shim + // now that we have our diff and desired state, see if we can reproduce + // that with the shim + // we're not testing Resource.Create, so we need to start with the "created" state + createdState := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{"id": "foo"}, + } + + testApplyDiff(t, r, createdState, expected, d) +} + +func TestShimResourceApply_Timeout_state(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + Timeouts: &ResourceTimeout{ + Create: DefaultTimeout(40 * time.Minute), + Update: DefaultTimeout(80 * time.Minute), + Delete: DefaultTimeout(40 * time.Minute), + }, + } + + called := false + r.Create = func(d *ResourceData, m interface{}) error { + called = true + d.SetId("foo") + return nil + } + + var s *terraform.InstanceState = nil + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + New: "42", + }, + }, + } + + diffTimeout := &ResourceTimeout{ + Create: DefaultTimeout(40 * time.Minute), + Update: DefaultTimeout(80 * time.Minute), + Delete: DefaultTimeout(40 * time.Minute), + } + + if err := diffTimeout.DiffEncode(d); err != nil { + t.Fatalf("Error encoding timeout to diff: %s", err) + } + + actual, err := r.Apply(s, d, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !called { + t.Fatal("not called") + } + + expected := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "42", + }, + Meta: map[string]interface{}{ + "schema_version": "2", + TimeoutKey: expectedForValues(40, 0, 80, 40, 0), + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("Not equal in Timeout State:\n\texpected: %#v\n\tactual: %#v", expected.Meta, actual.Meta) + } + + // Shim + // we're not testing Resource.Create, so we need to start with the "created" state + createdState := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{"id": "foo"}, + } + + testApplyDiff(t, r, createdState, expected, d) +} + +func TestShimResourceDiff_Timeout_diff(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + Timeouts: &ResourceTimeout{ + Create: DefaultTimeout(40 * time.Minute), + Update: DefaultTimeout(80 * time.Minute), + Delete: DefaultTimeout(40 * time.Minute), + }, + } + + r.Create = func(d *ResourceData, m interface{}) error { + d.SetId("foo") + return nil + } + + conf := terraform.NewResourceConfigRaw(map[string]interface{}{ + "foo": 42, + TimeoutsConfigKey: map[string]interface{}{ + "create": "2h", + }, + }) + var s *terraform.InstanceState + + actual, err := r.Diff(s, conf, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + New: "42", + }, + }, + } + + diffTimeout := &ResourceTimeout{ + Create: DefaultTimeout(120 * time.Minute), + Update: DefaultTimeout(80 * time.Minute), + Delete: DefaultTimeout(40 * time.Minute), + } + + if err := diffTimeout.DiffEncode(expected); err != nil { + t.Fatalf("Error encoding timeout to diff: %s", err) + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("Not equal in Timeout Diff:\n\texpected: %#v\n\tactual: %#v", expected.Meta, actual.Meta) + } + + // Shim + // apply this diff, so we have a state to compare + applied, err := r.Apply(s, actual, nil) + if err != nil { + t.Fatal(err) + } + + // we're not testing Resource.Create, so we need to start with the "created" state + createdState := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{"id": "foo"}, + } + + testSchema := providers.Schema{ + Version: int64(r.SchemaVersion), + Block: resourceSchemaToBlock(r.Schema), + } + + initialVal, err := StateValueFromInstanceState(createdState, testSchema.Block.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + appliedVal, err := StateValueFromInstanceState(applied, testSchema.Block.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + d, err := DiffFromValues(initialVal, appliedVal, r) + if err != nil { + t.Fatal(err) + } + if eq, _ := d.Same(expected); !eq { + t.Fatal(cmp.Diff(d, expected)) + } +} + +func TestShimResourceApply_destroy(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + called := false + r.Delete = func(d *ResourceData, m interface{}) error { + called = true + return nil + } + + s := &terraform.InstanceState{ + ID: "bar", + } + + d := &terraform.InstanceDiff{ + Destroy: true, + } + + actual, err := r.Apply(s, d, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !called { + t.Fatal("delete not called") + } + + if actual != nil { + t.Fatalf("bad: %#v", actual) + } + + // Shim + // now that we have our diff and desired state, see if we can reproduce + // that with the shim + testApplyDiff(t, r, s, actual, d) +} + +func TestShimResourceApply_destroyCreate(t *testing.T) { + r := &Resource{ + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeInt, + Optional: true, + ForceNew: true, + }, + + "tags": &Schema{ + Type: TypeMap, + Optional: true, + Computed: true, + }, + }, + } + + change := false + r.Create = func(d *ResourceData, m interface{}) error { + change = d.HasChange("tags") + d.SetId("foo") + return nil + } + r.Delete = func(d *ResourceData, m interface{}) error { + return nil + } + + var s *terraform.InstanceState = &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "foo": "7", + "tags.Name": "foo", + }, + } + + d := &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "id": &terraform.ResourceAttrDiff{ + New: "foo", + }, + "foo": &terraform.ResourceAttrDiff{ + Old: "7", + New: "42", + RequiresNew: true, + }, + "tags.Name": &terraform.ResourceAttrDiff{ + Old: "foo", + New: "foo", + RequiresNew: true, + }, + }, + } + + actual, err := r.Apply(s, d, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !change { + t.Fatal("should have change") + } + + expected := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "42", + "tags.%": "1", + "tags.Name": "foo", + }, + } + + if !reflect.DeepEqual(actual, expected) { + cmp.Diff(actual, expected) + } + + // Shim + // now that we have our diff and desired state, see if we can reproduce + // that with the shim + // we're not testing Resource.Create, so we need to start with the "created" state + createdState := &terraform.InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "id": "foo", + "foo": "7", + "tags.%": "1", + "tags.Name": "foo", + }, + } + + testApplyDiff(t, r, createdState, expected, d) +} + +func TestShimSchemaMap_Diff(t *testing.T) { + cases := []struct { + Name string + Schema map[string]*Schema + State *terraform.InstanceState + Config map[string]interface{} + CustomizeDiff CustomizeDiffFunc + Diff *terraform.InstanceDiff + Err bool + }{ + { + Name: "diff-1", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "diff-2", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + NewComputed: true, + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "diff-3", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "foo", + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Computed, but set in config", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "availability_zone": "foo", + }, + }, + + Config: map[string]interface{}{ + "availability_zone": "bar", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "foo", + New: "bar", + }, + }, + }, + + Err: false, + }, + + { + Name: "Default", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Default: "foo", + }, + }, + + State: nil, + + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + }, + }, + }, + + Err: false, + }, + + { + Name: "DefaultFunc, value", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + DefaultFunc: func() (interface{}, error) { + return "foo", nil + }, + }, + }, + + State: nil, + + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + }, + }, + }, + + Err: false, + }, + + { + Name: "DefaultFunc, configuration set", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + DefaultFunc: func() (interface{}, error) { + return "foo", nil + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "bar", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "bar", + }, + }, + }, + + Err: false, + }, + + { + Name: "String with StateFunc", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + StateFunc: func(a interface{}) string { + return a.(string) + "!" + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo!", + NewExtra: "foo", + }, + }, + }, + + Err: false, + }, + + { + Name: "StateFunc not called with nil value", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + StateFunc: func(a interface{}) string { + t.Error("should not get here!") + return "" + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Variable computed", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": hcl2shim.UnknownVariableValue, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: hcl2shim.UnknownVariableValue, + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Int decode", + Schema: map[string]*Schema{ + "port": &Schema{ + Type: TypeInt, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "port": 27, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "port": &terraform.ResourceAttrDiff{ + Old: "", + New: "27", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "bool decode", + Schema: map[string]*Schema{ + "port": &Schema{ + Type: TypeBool, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "port": false, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "port": &terraform.ResourceAttrDiff{ + Old: "", + New: "false", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Bool", + Schema: map[string]*Schema{ + "delete": &Schema{ + Type: TypeBool, + Optional: true, + Default: false, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "delete": "false", + }, + }, + + Config: nil, + + Diff: nil, + + Err: false, + }, + + { + Name: "List decode", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "3", + }, + "ports.0": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "", + New: "2", + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "List decode with promotion with list", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + PromoteSingle: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{"5"}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + "ports.0": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "3", + }, + "ports.0": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "", + New: "2", + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, hcl2shim.UnknownVariableValue, "5"}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "ports.#": "3", + "ports.0": "1", + "ports.1": "2", + "ports.2": "5", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: nil, + + Err: false, + }, + + { + Name: "", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "ports.#": "2", + "ports.0": "1", + "ports.1": "2", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "2", + New: "3", + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Required: true, + Elem: &Schema{Type: TypeInt}, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, 2, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "3", + RequiresNew: true, + }, + "ports.0": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + RequiresNew: true, + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "", + New: "2", + RequiresNew: true, + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "List with computed set", + Schema: map[string]*Schema{ + "config": &Schema{ + Type: TypeList, + Optional: true, + ForceNew: true, + MinItems: 1, + Elem: &Resource{ + Schema: map[string]*Schema{ + "name": { + Type: TypeString, + Required: true, + }, + + "rules": { + Type: TypeSet, + Computed: true, + Elem: &Schema{Type: TypeString}, + Set: HashString, + }, + }, + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "config": []interface{}{ + map[string]interface{}{ + "name": "hello", + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + RequiresNew: true, + }, + + "config.0.name": &terraform.ResourceAttrDiff{ + Old: "", + New: "hello", + }, + + "config.0.rules.#": &terraform.ResourceAttrDiff{ + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-1", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{5, 2, 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "3", + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "", + New: "2", + }, + "ports.5": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-2", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Computed: true, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "ports.#": "0", + }, + }, + + Config: nil, + + Diff: nil, + + Err: false, + }, + + { + Name: "Set-3", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-4", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{"2", "5", 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "3", + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "", + New: "2", + }, + "ports.5": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-5", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ports": []interface{}{1, hcl2shim.UnknownVariableValue, 5}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-6", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Required: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "ports.#": "2", + "ports.1": "1", + "ports.2": "2", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{5, 2, 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + Old: "2", + New: "3", + }, + "ports.1": &terraform.ResourceAttrDiff{ + Old: "1", + New: "1", + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "2", + New: "2", + }, + "ports.5": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-8", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "availability_zone": "bar", + "ports.#": "1", + "ports.80": "80", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Set-9", + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeSet, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + ps := m["ports"].([]interface{}) + result := 0 + for _, p := range ps { + result += p.(int) + } + return result + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "ingress.#": "2", + "ingress.80.ports.#": "1", + "ingress.80.ports.0": "80", + "ingress.443.ports.#": "1", + "ingress.443.ports.0": "443", + }, + }, + + Config: map[string]interface{}{ + "ingress": []interface{}{ + map[string]interface{}{ + "ports": []interface{}{443}, + }, + map[string]interface{}{ + "ports": []interface{}{80}, + }, + }, + }, + + Diff: nil, + + Err: false, + }, + + { + Name: "List of structure decode", + Schema: map[string]*Schema{ + "ingress": &Schema{ + Type: TypeList, + Required: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "from": &Schema{ + Type: TypeInt, + Required: true, + }, + }, + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ingress": []interface{}{ + map[string]interface{}{ + "from": 8080, + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ingress.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + "ingress.0.from": &terraform.ResourceAttrDiff{ + Old: "", + New: "8080", + }, + }, + }, + + Err: false, + }, + + { + Name: "ComputedWhen", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Computed: true, + ComputedWhen: []string{"port"}, + }, + + "port": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "availability_zone": "foo", + "port": "80", + }, + }, + + Config: map[string]interface{}{ + "port": 80, + }, + + Diff: nil, + + Err: false, + }, + + { + Name: "computed", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Computed: true, + ComputedWhen: []string{"port"}, + }, + + "port": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "port": 80, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + NewComputed: true, + }, + "port": &terraform.ResourceAttrDiff{ + New: "80", + }, + }, + }, + + Err: false, + }, + + { + Name: "computed, exists", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Computed: true, + ComputedWhen: []string{"port"}, + }, + + "port": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "port": "80", + }, + }, + + Config: map[string]interface{}{ + "port": 80, + }, + + // there is no computed diff when the instance exists already + Diff: nil, + + Err: false, + }, + + { + Name: "Maps-1", + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeMap, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "config_vars": map[string]interface{}{ + "bar": "baz", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.%": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + + "config_vars.bar": &terraform.ResourceAttrDiff{ + Old: "", + New: "baz", + }, + }, + }, + + Err: false, + }, + + { + Name: "Maps-2", + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeMap, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "config_vars.%": "1", + "config_vars.foo": "bar", + }, + }, + + Config: map[string]interface{}{ + "config_vars": map[string]interface{}{ + "bar": "baz", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.foo": &terraform.ResourceAttrDiff{ + Old: "bar", + NewRemoved: true, + }, + "config_vars.bar": &terraform.ResourceAttrDiff{ + Old: "", + New: "baz", + }, + }, + }, + + Err: false, + }, + + { + Name: "Maps-3", + Schema: map[string]*Schema{ + "vars": &Schema{ + Type: TypeMap, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "vars.%": "1", + "vars.foo": "bar", + }, + }, + + Config: map[string]interface{}{ + "vars": map[string]interface{}{ + "bar": "baz", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "vars.foo": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "", + NewRemoved: true, + }, + "vars.bar": &terraform.ResourceAttrDiff{ + Old: "", + New: "baz", + }, + }, + }, + + Err: false, + }, + + { + Name: "Maps-4", + Schema: map[string]*Schema{ + "vars": &Schema{ + Type: TypeMap, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "vars.%": "1", + "vars.foo": "bar", + }, + }, + + Config: nil, + + Diff: nil, + + Err: false, + }, + + { + Name: "Maps-5", + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeMap}, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "config_vars.#": "1", + "config_vars.0.%": "1", + "config_vars.0.foo": "bar", + }, + }, + + Config: map[string]interface{}{ + "config_vars": []interface{}{ + map[string]interface{}{ + "bar": "baz", + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.0.foo": &terraform.ResourceAttrDiff{ + Old: "bar", + NewRemoved: true, + }, + "config_vars.0.bar": &terraform.ResourceAttrDiff{ + Old: "", + New: "baz", + }, + }, + }, + + Err: false, + }, + + { + Name: "Maps-6", + Schema: map[string]*Schema{ + "config_vars": &Schema{ + Type: TypeList, + Elem: &Schema{Type: TypeMap}, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "config_vars.#": "1", + "config_vars.0.%": "2", + "config_vars.0.foo": "bar", + "config_vars.0.bar": "baz", + }, + }, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config_vars.#": &terraform.ResourceAttrDiff{ + Old: "1", + New: "0", + }, + "config_vars.0.%": &terraform.ResourceAttrDiff{ + Old: "2", + New: "0", + }, + "config_vars.0.foo": &terraform.ResourceAttrDiff{ + Old: "bar", + NewRemoved: true, + }, + "config_vars.0.bar": &terraform.ResourceAttrDiff{ + Old: "baz", + NewRemoved: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "ForceNews", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + ForceNew: true, + }, + + "address": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "availability_zone": "bar", + "address": "foo", + }, + }, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-10", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + ForceNew: true, + }, + + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "availability_zone": "bar", + "ports.#": "1", + "ports.80": "80", + }, + }, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "bar", + New: "foo", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-11", + Schema: map[string]*Schema{ + "instances": &Schema{ + Type: TypeSet, + Elem: &Schema{Type: TypeString}, + Optional: true, + Computed: true, + Set: func(v interface{}) int { + return len(v.(string)) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "instances.#": "0", + }, + }, + + Config: map[string]interface{}{ + "instances": []interface{}{hcl2shim.UnknownVariableValue}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "instances.#": &terraform.ResourceAttrDiff{ + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-12", + Schema: map[string]*Schema{ + "route": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": &Schema{ + Type: TypeInt, + Required: true, + }, + + "gateway": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "route": []interface{}{ + map[string]interface{}{ + "index": "1", + "gateway": hcl2shim.UnknownVariableValue, + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "route.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + "route.~1.index": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "route.~1.gateway": &terraform.ResourceAttrDiff{ + Old: "", + New: hcl2shim.UnknownVariableValue, + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set-13", + Schema: map[string]*Schema{ + "route": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": &Schema{ + Type: TypeInt, + Required: true, + }, + + "gateway": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "route": []interface{}{ + map[string]interface{}{ + "index": "1", + "gateway": []interface{}{ + hcl2shim.UnknownVariableValue, + }, + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "route.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + "route.~1.index": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "route.~1.gateway.#": &terraform.ResourceAttrDiff{ + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Computed maps", + Schema: map[string]*Schema{ + "vars": &Schema{ + Type: TypeMap, + Computed: true, + }, + }, + + State: nil, + + Config: nil, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "vars.%": &terraform.ResourceAttrDiff{ + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Computed maps", + Schema: map[string]*Schema{ + "vars": &Schema{ + Type: TypeMap, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "vars.%": "0", + }, + }, + + Config: map[string]interface{}{ + "vars": map[string]interface{}{ + "bar": hcl2shim.UnknownVariableValue, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "vars.%": &terraform.ResourceAttrDiff{ + Old: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Empty", + Schema: map[string]*Schema{}, + + State: &terraform.InstanceState{}, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Float", + Schema: map[string]*Schema{ + "some_threshold": &Schema{ + Type: TypeFloat, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "some_threshold": "567.8", + }, + }, + + Config: map[string]interface{}{ + "some_threshold": 12.34, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "some_threshold": &terraform.ResourceAttrDiff{ + Old: "567.8", + New: "12.34", + }, + }, + }, + + Err: false, + }, + + { + Name: "https://github.com/hashicorp/terraform/issues/824", + Schema: map[string]*Schema{ + "block_device": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "device_name": &Schema{ + Type: TypeString, + Required: true, + }, + "delete_on_termination": &Schema{ + Type: TypeBool, + Optional: true, + Default: true, + }, + }, + }, + Set: func(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string))) + buf.WriteString(fmt.Sprintf("%t-", m["delete_on_termination"].(bool))) + return hashcode.String(buf.String()) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "block_device.#": "2", + "block_device.616397234.delete_on_termination": "true", + "block_device.616397234.device_name": "/dev/sda1", + "block_device.2801811477.delete_on_termination": "true", + "block_device.2801811477.device_name": "/dev/sdx", + }, + }, + + Config: map[string]interface{}{ + "block_device": []interface{}{ + map[string]interface{}{ + "device_name": "/dev/sda1", + }, + map[string]interface{}{ + "device_name": "/dev/sdx", + }, + }, + }, + Diff: nil, + Err: false, + }, + + { + Name: "Zero value in state shouldn't result in diff", + Schema: map[string]*Schema{ + "port": &Schema{ + Type: TypeBool, + Optional: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "port": "false", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Same as prev, but for sets", + Schema: map[string]*Schema{ + "route": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": &Schema{ + Type: TypeInt, + Required: true, + }, + + "gateway": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "route.#": "0", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "A set computed element shouldn't cause a diff", + Schema: map[string]*Schema{ + "active": &Schema{ + Type: TypeBool, + Computed: true, + ForceNew: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "active": "true", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "An empty set should show up in the diff", + Schema: map[string]*Schema{ + "instances": &Schema{ + Type: TypeSet, + Elem: &Schema{Type: TypeString}, + Optional: true, + ForceNew: true, + Set: func(v interface{}) int { + return len(v.(string)) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "instances.#": "1", + "instances.3": "foo", + }, + }, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "instances.#": &terraform.ResourceAttrDiff{ + Old: "1", + New: "0", + RequiresNew: true, + }, + "instances.3": &terraform.ResourceAttrDiff{ + Old: "foo", + New: "", + NewRemoved: true, + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Map with empty value", + Schema: map[string]*Schema{ + "vars": &Schema{ + Type: TypeMap, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "vars": map[string]interface{}{ + "foo": "", + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "vars.%": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + "vars.foo": &terraform.ResourceAttrDiff{ + Old: "", + New: "", + }, + }, + }, + + Err: false, + }, + + { + Name: "Unset bool, not in state", + Schema: map[string]*Schema{ + "force": &Schema{ + Type: TypeBool, + Optional: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Unset set, not in state", + Schema: map[string]*Schema{ + "metadata_keys": &Schema{ + Type: TypeSet, + Optional: true, + ForceNew: true, + Elem: &Schema{Type: TypeInt}, + Set: func(interface{}) int { return 0 }, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Unset list in state, should not show up computed", + Schema: map[string]*Schema{ + "metadata_keys": &Schema{ + Type: TypeList, + Optional: true, + Computed: true, + ForceNew: true, + Elem: &Schema{Type: TypeInt}, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "metadata_keys.#": "0", + }, + }, + + Config: map[string]interface{}{}, + + Diff: nil, + + Err: false, + }, + + { + Name: "Computed map without config that's known to be empty does not generate diff", + Schema: map[string]*Schema{ + "tags": &Schema{ + Type: TypeMap, + Computed: true, + }, + }, + + Config: nil, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "tags.%": "0", + }, + }, + + Diff: nil, + + Err: false, + }, + + { + Name: "Set with hyphen keys", + Schema: map[string]*Schema{ + "route": &Schema{ + Type: TypeSet, + Optional: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "index": &Schema{ + Type: TypeInt, + Required: true, + }, + + "gateway-name": &Schema{ + Type: TypeString, + Optional: true, + }, + }, + }, + Set: func(v interface{}) int { + m := v.(map[string]interface{}) + return m["index"].(int) + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "route": []interface{}{ + map[string]interface{}{ + "index": "1", + "gateway-name": "hello", + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "route.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + }, + "route.1.index": &terraform.ResourceAttrDiff{ + Old: "", + New: "1", + }, + "route.1.gateway-name": &terraform.ResourceAttrDiff{ + Old: "", + New: "hello", + }, + }, + }, + + Err: false, + }, + + { + Name: "StateFunc in nested set (#1759)", + Schema: map[string]*Schema{ + "service_account": &Schema{ + Type: TypeList, + Optional: true, + ForceNew: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "scopes": &Schema{ + Type: TypeSet, + Required: true, + ForceNew: true, + Elem: &Schema{ + Type: TypeString, + StateFunc: func(v interface{}) string { + return v.(string) + "!" + }, + }, + Set: func(v interface{}) int { + i, err := strconv.Atoi(v.(string)) + if err != nil { + t.Fatalf("err: %s", err) + } + return i + }, + }, + }, + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "service_account": []interface{}{ + map[string]interface{}{ + "scopes": []interface{}{"123"}, + }, + }, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "service_account.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + RequiresNew: true, + }, + "service_account.0.scopes.#": &terraform.ResourceAttrDiff{ + Old: "0", + New: "1", + RequiresNew: true, + }, + "service_account.0.scopes.123": &terraform.ResourceAttrDiff{ + Old: "", + New: "123!", + NewExtra: "123", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Removing set elements", + Schema: map[string]*Schema{ + "instances": &Schema{ + Type: TypeSet, + Elem: &Schema{Type: TypeString}, + Optional: true, + ForceNew: true, + Set: func(v interface{}) int { + return len(v.(string)) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "instances.#": "2", + "instances.3": "333", + "instances.2": "22", + }, + }, + + Config: map[string]interface{}{ + "instances": []interface{}{"333", "4444"}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "instances.2": &terraform.ResourceAttrDiff{ + Old: "22", + New: "", + NewRemoved: true, + RequiresNew: true, + }, + "instances.3": &terraform.ResourceAttrDiff{ + Old: "333", + New: "333", + }, + "instances.4": &terraform.ResourceAttrDiff{ + Old: "", + New: "4444", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Bools can be set with 0/1 in config, still get true/false", + Schema: map[string]*Schema{ + "one": &Schema{ + Type: TypeBool, + Optional: true, + }, + "two": &Schema{ + Type: TypeBool, + Optional: true, + }, + "three": &Schema{ + Type: TypeBool, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "one": "false", + "two": "true", + "three": "true", + }, + }, + + Config: map[string]interface{}{ + "one": "1", + "two": "0", + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "one": &terraform.ResourceAttrDiff{ + Old: "false", + New: "true", + }, + "two": &terraform.ResourceAttrDiff{ + Old: "true", + New: "false", + }, + "three": &terraform.ResourceAttrDiff{ + Old: "true", + New: "false", + NewRemoved: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "tainted in state w/ no attr changes is still a replacement", + Schema: map[string]*Schema{}, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "id": "someid", + }, + Tainted: true, + }, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + DestroyTainted: true, + }, + }, + + { + Name: "Set ForceNew only marks the changing element as ForceNew", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Required: true, + ForceNew: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "ports.#": "3", + "ports.1": "1", + "ports.2": "2", + "ports.4": "4", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{5, 2, 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.1": &terraform.ResourceAttrDiff{ + Old: "1", + New: "1", + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "2", + New: "2", + }, + "ports.5": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + RequiresNew: true, + }, + "ports.4": &terraform.ResourceAttrDiff{ + Old: "4", + New: "0", + NewRemoved: true, + RequiresNew: true, + }, + }, + }, + }, + + { + Name: "removed optional items should trigger ForceNew", + Schema: map[string]*Schema{ + "description": &Schema{ + Type: TypeString, + ForceNew: true, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "description": "foo", + }, + }, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "description": &terraform.ResourceAttrDiff{ + Old: "foo", + New: "", + RequiresNew: true, + NewRemoved: true, + }, + }, + }, + + Err: false, + }, + + // GH-7715 + { + Name: "computed value for boolean field", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeBool, + ForceNew: true, + Computed: true, + Optional: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + }, + + Config: map[string]interface{}{ + "foo": hcl2shim.UnknownVariableValue, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "foo": &terraform.ResourceAttrDiff{ + Old: "", + New: "false", + NewComputed: true, + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "Set ForceNew marks count as ForceNew if computed", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Required: true, + ForceNew: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "ports.#": "3", + "ports.1": "1", + "ports.2": "2", + "ports.4": "4", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{hcl2shim.UnknownVariableValue, 2, 1}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.#": &terraform.ResourceAttrDiff{ + NewComputed: true, + RequiresNew: true, + }, + }, + }, + }, + + { + Name: "List with computed schema and ForceNew", + Schema: map[string]*Schema{ + "config": &Schema{ + Type: TypeList, + Optional: true, + ForceNew: true, + Elem: &Schema{ + Type: TypeString, + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "config.#": "2", + "config.0": "a", + "config.1": "b", + }, + }, + + Config: map[string]interface{}{ + "config": []interface{}{hcl2shim.UnknownVariableValue, hcl2shim.UnknownVariableValue}, + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "config.#": &terraform.ResourceAttrDiff{ + Old: "2", + New: "", + RequiresNew: true, + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "overridden diff with a CustomizeDiff function, ForceNew not in schema", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("availability_zone", "bar"); err != nil { + return err + } + if err := d.ForceNew("availability_zone"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "bar", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + // NOTE: This case is technically impossible in the current + // implementation, because optional+computed values never show up in the + // diff. In the event behavior changes this test should ensure that the + // intended diff still shows up. + Name: "overridden removed attribute diff with a CustomizeDiff function, ForceNew not in schema", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("availability_zone", "bar"); err != nil { + return err + } + if err := d.ForceNew("availability_zone"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "bar", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + + Name: "overridden diff with a CustomizeDiff function, ForceNew in schema", + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "availability_zone": "foo", + }, + + CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("availability_zone", "bar"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "bar", + RequiresNew: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "required field with computed diff added with CustomizeDiff function", + Schema: map[string]*Schema{ + "ami_id": &Schema{ + Type: TypeString, + Required: true, + }, + "instance_id": &Schema{ + Type: TypeString, + Computed: true, + }, + }, + + State: nil, + + Config: map[string]interface{}{ + "ami_id": "foo", + }, + + CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("instance_id", "bar"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ami_id": &terraform.ResourceAttrDiff{ + Old: "", + New: "foo", + }, + "instance_id": &terraform.ResourceAttrDiff{ + Old: "", + New: "bar", + }, + }, + }, + + Err: false, + }, + + { + Name: "Set ForceNew only marks the changing element as ForceNew - CustomizeDiffFunc edition", + Schema: map[string]*Schema{ + "ports": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Schema{Type: TypeInt}, + Set: func(a interface{}) int { + return a.(int) + }, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "ports.#": "3", + "ports.1": "1", + "ports.2": "2", + "ports.4": "4", + }, + }, + + Config: map[string]interface{}{ + "ports": []interface{}{5, 2, 6}, + }, + + CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { + if err := d.SetNew("ports", []interface{}{5, 2, 1}); err != nil { + return err + } + if err := d.ForceNew("ports"); err != nil { + return err + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "ports.1": &terraform.ResourceAttrDiff{ + Old: "1", + New: "1", + }, + "ports.2": &terraform.ResourceAttrDiff{ + Old: "2", + New: "2", + }, + "ports.5": &terraform.ResourceAttrDiff{ + Old: "", + New: "5", + RequiresNew: true, + }, + "ports.4": &terraform.ResourceAttrDiff{ + Old: "4", + New: "0", + NewRemoved: true, + RequiresNew: true, + }, + }, + }, + }, + + { + Name: "tainted resource does not run CustomizeDiffFunc", + Schema: map[string]*Schema{}, + + State: &terraform.InstanceState{ + ID: "someid", + Attributes: map[string]string{ + "id": "someid", + }, + Tainted: true, + }, + + Config: map[string]interface{}{}, + + CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { + return errors.New("diff customization should not have run") + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{}, + DestroyTainted: true, + }, + + Err: false, + }, + + { + Name: "NewComputed based on a conditional with CustomizeDiffFunc", + Schema: map[string]*Schema{ + "etag": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + "version_id": &Schema{ + Type: TypeString, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "etag": "foo", + "version_id": "1", + }, + }, + + Config: map[string]interface{}{ + "etag": "bar", + }, + + CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { + if d.HasChange("etag") { + d.SetNewComputed("version_id") + } + return nil + }, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "etag": &terraform.ResourceAttrDiff{ + Old: "foo", + New: "bar", + }, + "version_id": &terraform.ResourceAttrDiff{ + Old: "1", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + + { + Name: "vetoing a diff", + Schema: map[string]*Schema{ + "foo": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "foo": "bar", + }, + }, + + Config: map[string]interface{}{ + "foo": "baz", + }, + + CustomizeDiff: func(d *ResourceDiff, meta interface{}) error { + return fmt.Errorf("diff vetoed") + }, + + Err: true, + }, + + // A lot of resources currently depended on using the empty string as a + // nil/unset value. + { + Name: "optional, computed, empty string", + Schema: map[string]*Schema{ + "attr": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "attr": "bar", + }, + }, + + Config: map[string]interface{}{ + "attr": "", + }, + }, + + { + Name: "optional, computed, empty string should not crash in CustomizeDiff", + Schema: map[string]*Schema{ + "unrelated_set": { + Type: TypeSet, + Optional: true, + Elem: &Schema{Type: TypeString}, + }, + "stream_enabled": { + Type: TypeBool, + Optional: true, + }, + "stream_view_type": { + Type: TypeString, + Optional: true, + Computed: true, + }, + }, + + State: &terraform.InstanceState{ + ID: "id", + Attributes: map[string]string{ + "unrelated_set.#": "0", + "stream_enabled": "true", + "stream_view_type": "KEYS_ONLY", + }, + }, + Config: map[string]interface{}{ + "stream_enabled": false, + "stream_view_type": "", + }, + CustomizeDiff: func(diff *ResourceDiff, v interface{}) error { + v, ok := diff.GetOk("unrelated_set") + if ok { + return fmt.Errorf("Didn't expect unrelated_set: %#v", v) + } + return nil + }, + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "stream_enabled": { + Old: "true", + New: "false", + }, + }, + }, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + c := terraform.NewResourceConfigRaw(tc.Config) + + { + d, err := schemaMap(tc.Schema).Diff(tc.State, c, tc.CustomizeDiff, nil, false) + if err != nil != tc.Err { + t.Fatalf("err: %s", err) + } + if !cmp.Equal(d, tc.Diff, equateEmpty) { + t.Fatal(cmp.Diff(d, tc.Diff, equateEmpty)) + } + } + // up to here is already tested in helper/schema; we're just + // verify that we haven't broken any tests in transition. + + // create a schema from the schemaMap + testSchema := resourceSchemaToBlock(tc.Schema) + + // get our initial state cty.Value + stateVal, err := StateValueFromInstanceState(tc.State, testSchema.ImpliedType()) + if err != nil { + t.Fatal(err) + } + + // this is the desired cty.Value from the configuration + configVal := hcl2shim.HCL2ValueFromConfigValue(c.Config) + + // verify that we can round-trip the config + origConfig := hcl2shim.ConfigValueFromHCL2(configVal) + if !cmp.Equal(c.Config, origConfig, equateEmpty) { + t.Fatal(cmp.Diff(c.Config, origConfig, equateEmpty)) + } + + // make sure our config conforms precisely to the schema + configVal, err = testSchema.CoerceValue(configVal) + if err != nil { + t.Fatal(tfdiags.FormatError(err)) + } + + // The new API requires returning the desired state rather than a + // diff, so we need to verify that we can combine the state and + // diff and recreate a new state. + + // now verify that we can create diff, using the new config and state values + // customize isn't run on tainted resources + tainted := tc.State != nil && tc.State.Tainted + if tainted { + tc.CustomizeDiff = nil + } + + res := &Resource{Schema: tc.Schema} + + d, err := diffFromValues(stateVal, configVal, res, tc.CustomizeDiff) + if err != nil { + if !tc.Err { + t.Fatal(err) + } + } + + // In a real "apply" operation there would be no unknown values, + // so for tests containing unknowns we'll stop here: the steps + // after this point apply only to the apply phase. + if !configVal.IsWhollyKnown() { + return + } + + // our diff function can't set DestroyTainted, but match the + // expected value here for the test fixtures + if tainted { + if d == nil { + d = &terraform.InstanceDiff{} + } + d.DestroyTainted = true + } + + if eq, _ := d.Same(tc.Diff); !eq { + t.Fatal(cmp.Diff(d, tc.Diff)) + } + + }) + } +} + +func resourceSchemaToBlock(s map[string]*Schema) *configschema.Block { + return (&Resource{Schema: s}).CoreConfigSchema() +} + +func TestRemoveConfigUnknowns(t *testing.T) { + cfg := map[string]interface{}{ + "id": "74D93920-ED26-11E3-AC10-0800200C9A66", + "route_rules": []interface{}{ + map[string]interface{}{ + "cidr_block": "74D93920-ED26-11E3-AC10-0800200C9A66", + "destination": "0.0.0.0/0", + "destination_type": "CIDR_BLOCK", + "network_entity_id": "1", + }, + map[string]interface{}{ + "cidr_block": "74D93920-ED26-11E3-AC10-0800200C9A66", + "destination": "0.0.0.0/0", + "destination_type": "CIDR_BLOCK", + "sub_block": []interface{}{ + map[string]interface{}{ + "computed": "74D93920-ED26-11E3-AC10-0800200C9A66", + }, + }, + }, + }, + } + + expect := map[string]interface{}{ + "route_rules": []interface{}{ + map[string]interface{}{ + "destination": "0.0.0.0/0", + "destination_type": "CIDR_BLOCK", + "network_entity_id": "1", + }, + map[string]interface{}{ + "destination": "0.0.0.0/0", + "destination_type": "CIDR_BLOCK", + "sub_block": []interface{}{ + map[string]interface{}{}, + }, + }, + }, + } + + removeConfigUnknowns(cfg) + + if !reflect.DeepEqual(cfg, expect) { + t.Fatalf("\nexpected: %#v\ngot: %#v", expect, cfg) + } +} diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/testing.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/testing.go new file mode 100644 index 00000000..3b328a87 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/testing.go @@ -0,0 +1,28 @@ +package schema + +import ( + "testing" + + "github.com/hashicorp/terraform/internal/legacy/terraform" +) + +// TestResourceDataRaw creates a ResourceData from a raw configuration map. +func TestResourceDataRaw( + t *testing.T, schema map[string]*Schema, raw map[string]interface{}) *ResourceData { + t.Helper() + + c := terraform.NewResourceConfigRaw(raw) + + sm := schemaMap(schema) + diff, err := sm.Diff(nil, c, nil, nil, true) + if err != nil { + t.Fatalf("err: %s", err) + } + + result, err := sm.Data(nil, diff) + if err != nil { + t.Fatalf("err: %s", err) + } + + return result +} diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/valuetype.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/valuetype.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/valuetype.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/valuetype.go diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go b/vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/valuetype_string.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/helper/schema/valuetype_string.go diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/context_components.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/context_components.go new file mode 100644 index 00000000..c893a16b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/context_components.go @@ -0,0 +1,65 @@ +package terraform + +import ( + "fmt" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/provisioners" +) + +// contextComponentFactory is the interface that Context uses +// to initialize various components such as providers and provisioners. +// This factory gets more information than the raw maps using to initialize +// a Context. This information is used for debugging. +type contextComponentFactory interface { + // ResourceProvider creates a new ResourceProvider with the given type. + ResourceProvider(typ addrs.Provider) (providers.Interface, error) + ResourceProviders() []string + + // ResourceProvisioner creates a new ResourceProvisioner with the given + // type. + ResourceProvisioner(typ string) (provisioners.Interface, error) + ResourceProvisioners() []string +} + +// basicComponentFactory just calls a factory from a map directly. +type basicComponentFactory struct { + providers map[addrs.Provider]providers.Factory + provisioners map[string]ProvisionerFactory +} + +func (c *basicComponentFactory) ResourceProviders() []string { + var result []string + for k := range c.providers { + result = append(result, k.String()) + } + return result +} + +func (c *basicComponentFactory) ResourceProvisioners() []string { + var result []string + for k := range c.provisioners { + result = append(result, k) + } + + return result +} + +func (c *basicComponentFactory) ResourceProvider(typ addrs.Provider) (providers.Interface, error) { + f, ok := c.providers[typ] + if !ok { + return nil, fmt.Errorf("unknown provider %q", typ.String()) + } + + return f() +} + +func (c *basicComponentFactory) ResourceProvisioner(typ string) (provisioners.Interface, error) { + f, ok := c.provisioners[typ] + if !ok { + return nil, fmt.Errorf("unknown provisioner %q", typ) + } + + return f() +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/diff.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/diff.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/diff.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/diff.go diff --git a/vendor/github.com/hashicorp/terraform/terraform/diff_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/diff_test.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/diff_test.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/diff_test.go diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/features.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/features.go similarity index 100% rename from vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/features.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/features.go diff --git a/vendor/github.com/hashicorp/terraform/terraform/instancetype.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/instancetype.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/instancetype.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/instancetype.go diff --git a/vendor/github.com/hashicorp/terraform/terraform/instancetype_string.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/instancetype_string.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/instancetype_string.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/instancetype_string.go diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/provider_mock.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/provider_mock.go new file mode 100644 index 00000000..9603e437 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/provider_mock.go @@ -0,0 +1,363 @@ +package terraform + +import ( + "encoding/json" + "sync" + + "github.com/zclconf/go-cty/cty" + ctyjson "github.com/zclconf/go-cty/cty/json" + + "github.com/hashicorp/terraform/configs/hcl2shim" + "github.com/hashicorp/terraform/providers" +) + +var _ providers.Interface = (*MockProvider)(nil) + +// MockProvider implements providers.Interface but mocks out all the +// calls for testing purposes. +type MockProvider struct { + sync.Mutex + + // Anything you want, in case you need to store extra data with the mock. + Meta interface{} + + GetSchemaCalled bool + GetSchemaReturn *ProviderSchema // This is using ProviderSchema directly rather than providers.GetProviderSchemaResponse for compatibility with old tests + + ValidateProviderConfigCalled bool + ValidateProviderConfigResponse providers.ValidateProviderConfigResponse + ValidateProviderConfigRequest providers.ValidateProviderConfigRequest + ValidateProviderConfigFn func(providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse + + ValidateResourceConfigCalled bool + ValidateResourceConfigTypeName string + ValidateResourceConfigResponse providers.ValidateResourceConfigResponse + ValidateResourceConfigRequest providers.ValidateResourceConfigRequest + ValidateResourceConfigFn func(providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse + + ValidateDataResourceConfigCalled bool + ValidateDataResourceConfigTypeName string + ValidateDataResourceConfigResponse providers.ValidateDataResourceConfigResponse + ValidateDataResourceConfigRequest providers.ValidateDataResourceConfigRequest + ValidateDataResourceConfigFn func(providers.ValidateDataResourceConfigRequest) providers.ValidateDataResourceConfigResponse + + UpgradeResourceStateCalled bool + UpgradeResourceStateTypeName string + UpgradeResourceStateResponse providers.UpgradeResourceStateResponse + UpgradeResourceStateRequest providers.UpgradeResourceStateRequest + UpgradeResourceStateFn func(providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse + + ConfigureProviderCalled bool + ConfigureProviderResponse providers.ConfigureProviderResponse + ConfigureProviderRequest providers.ConfigureProviderRequest + ConfigureProviderFn func(providers.ConfigureProviderRequest) providers.ConfigureProviderResponse + + StopCalled bool + StopFn func() error + StopResponse error + + ReadResourceCalled bool + ReadResourceResponse providers.ReadResourceResponse + ReadResourceRequest providers.ReadResourceRequest + ReadResourceFn func(providers.ReadResourceRequest) providers.ReadResourceResponse + + PlanResourceChangeCalled bool + PlanResourceChangeResponse providers.PlanResourceChangeResponse + PlanResourceChangeRequest providers.PlanResourceChangeRequest + PlanResourceChangeFn func(providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse + + ApplyResourceChangeCalled bool + ApplyResourceChangeResponse providers.ApplyResourceChangeResponse + ApplyResourceChangeRequest providers.ApplyResourceChangeRequest + ApplyResourceChangeFn func(providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse + + ImportResourceStateCalled bool + ImportResourceStateResponse providers.ImportResourceStateResponse + ImportResourceStateRequest providers.ImportResourceStateRequest + ImportResourceStateFn func(providers.ImportResourceStateRequest) providers.ImportResourceStateResponse + // Legacy return type for existing tests, which will be shimmed into an + // ImportResourceStateResponse if set + ImportStateReturn []*InstanceState + + ReadDataSourceCalled bool + ReadDataSourceResponse providers.ReadDataSourceResponse + ReadDataSourceRequest providers.ReadDataSourceRequest + ReadDataSourceFn func(providers.ReadDataSourceRequest) providers.ReadDataSourceResponse + + CloseCalled bool + CloseError error +} + +func (p *MockProvider) GetProviderSchema() providers.GetProviderSchemaResponse { + p.Lock() + defer p.Unlock() + p.GetSchemaCalled = true + return p.getSchema() +} + +func (p *MockProvider) getSchema() providers.GetProviderSchemaResponse { + // This version of getSchema doesn't do any locking, so it's suitable to + // call from other methods of this mock as long as they are already + // holding the lock. + + ret := providers.GetProviderSchemaResponse{ + Provider: providers.Schema{}, + DataSources: map[string]providers.Schema{}, + ResourceTypes: map[string]providers.Schema{}, + } + if p.GetSchemaReturn != nil { + ret.Provider.Block = p.GetSchemaReturn.Provider + ret.ProviderMeta.Block = p.GetSchemaReturn.ProviderMeta + for n, s := range p.GetSchemaReturn.DataSources { + ret.DataSources[n] = providers.Schema{ + Block: s, + } + } + for n, s := range p.GetSchemaReturn.ResourceTypes { + ret.ResourceTypes[n] = providers.Schema{ + Version: int64(p.GetSchemaReturn.ResourceTypeSchemaVersions[n]), + Block: s, + } + } + } + + return ret +} + +func (p *MockProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse { + p.Lock() + defer p.Unlock() + + p.ValidateProviderConfigCalled = true + p.ValidateProviderConfigRequest = r + if p.ValidateProviderConfigFn != nil { + return p.ValidateProviderConfigFn(r) + } + return p.ValidateProviderConfigResponse +} + +func (p *MockProvider) ValidateResourceConfig(r providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { + p.Lock() + defer p.Unlock() + + p.ValidateResourceConfigCalled = true + p.ValidateResourceConfigRequest = r + + if p.ValidateResourceConfigFn != nil { + return p.ValidateResourceConfigFn(r) + } + + return p.ValidateResourceConfigResponse +} + +func (p *MockProvider) ValidateDataResourceConfig(r providers.ValidateDataResourceConfigRequest) providers.ValidateDataResourceConfigResponse { + p.Lock() + defer p.Unlock() + + p.ValidateDataResourceConfigCalled = true + p.ValidateDataResourceConfigRequest = r + + if p.ValidateDataResourceConfigFn != nil { + return p.ValidateDataResourceConfigFn(r) + } + + return p.ValidateDataResourceConfigResponse +} + +func (p *MockProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse { + p.Lock() + defer p.Unlock() + + schemas := p.getSchema() + schema := schemas.ResourceTypes[r.TypeName] + schemaType := schema.Block.ImpliedType() + + p.UpgradeResourceStateCalled = true + p.UpgradeResourceStateRequest = r + + if p.UpgradeResourceStateFn != nil { + return p.UpgradeResourceStateFn(r) + } + + resp := p.UpgradeResourceStateResponse + + if resp.UpgradedState == cty.NilVal { + switch { + case r.RawStateFlatmap != nil: + v, err := hcl2shim.HCL2ValueFromFlatmap(r.RawStateFlatmap, schemaType) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + resp.UpgradedState = v + case len(r.RawStateJSON) > 0: + v, err := ctyjson.Unmarshal(r.RawStateJSON, schemaType) + + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + resp.UpgradedState = v + } + } + return resp +} + +func (p *MockProvider) ConfigureProvider(r providers.ConfigureProviderRequest) providers.ConfigureProviderResponse { + p.Lock() + defer p.Unlock() + + p.ConfigureProviderCalled = true + p.ConfigureProviderRequest = r + + if p.ConfigureProviderFn != nil { + return p.ConfigureProviderFn(r) + } + + return p.ConfigureProviderResponse +} + +func (p *MockProvider) Stop() error { + // We intentionally don't lock in this one because the whole point of this + // method is to be called concurrently with another operation that can + // be cancelled. The provider itself is responsible for handling + // any concurrency concerns in this case. + + p.StopCalled = true + if p.StopFn != nil { + return p.StopFn() + } + + return p.StopResponse +} + +func (p *MockProvider) ReadResource(r providers.ReadResourceRequest) providers.ReadResourceResponse { + p.Lock() + defer p.Unlock() + + p.ReadResourceCalled = true + p.ReadResourceRequest = r + + if p.ReadResourceFn != nil { + return p.ReadResourceFn(r) + } + + resp := p.ReadResourceResponse + if resp.NewState != cty.NilVal { + // make sure the NewState fits the schema + // This isn't always the case for the existing tests + newState, err := p.GetSchemaReturn.ResourceTypes[r.TypeName].CoerceValue(resp.NewState) + if err != nil { + panic(err) + } + resp.NewState = newState + return resp + } + + // just return the same state we received + resp.NewState = r.PriorState + return resp +} + +func (p *MockProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { + p.Lock() + defer p.Unlock() + + p.PlanResourceChangeCalled = true + p.PlanResourceChangeRequest = r + + if p.PlanResourceChangeFn != nil { + return p.PlanResourceChangeFn(r) + } + + return p.PlanResourceChangeResponse +} + +func (p *MockProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { + p.Lock() + p.ApplyResourceChangeCalled = true + p.ApplyResourceChangeRequest = r + p.Unlock() + + if p.ApplyResourceChangeFn != nil { + return p.ApplyResourceChangeFn(r) + } + + return p.ApplyResourceChangeResponse +} + +func (p *MockProvider) ImportResourceState(r providers.ImportResourceStateRequest) providers.ImportResourceStateResponse { + p.Lock() + defer p.Unlock() + + if p.ImportStateReturn != nil { + for _, is := range p.ImportStateReturn { + if is.Attributes == nil { + is.Attributes = make(map[string]string) + } + is.Attributes["id"] = is.ID + + typeName := is.Ephemeral.Type + // Use the requested type if the resource has no type of it's own. + // We still return the empty type, which will error, but this prevents a panic. + if typeName == "" { + typeName = r.TypeName + } + + schema := p.GetSchemaReturn.ResourceTypes[typeName] + if schema == nil { + panic("no schema found for " + typeName) + } + + private, err := json.Marshal(is.Meta) + if err != nil { + panic(err) + } + + state, err := hcl2shim.HCL2ValueFromFlatmap(is.Attributes, schema.ImpliedType()) + if err != nil { + panic(err) + } + + state, err = schema.CoerceValue(state) + if err != nil { + panic(err) + } + + p.ImportResourceStateResponse.ImportedResources = append( + p.ImportResourceStateResponse.ImportedResources, + providers.ImportedResource{ + TypeName: is.Ephemeral.Type, + State: state, + Private: private, + }) + } + } + + p.ImportResourceStateCalled = true + p.ImportResourceStateRequest = r + if p.ImportResourceStateFn != nil { + return p.ImportResourceStateFn(r) + } + + return p.ImportResourceStateResponse +} + +func (p *MockProvider) ReadDataSource(r providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { + p.Lock() + defer p.Unlock() + + p.ReadDataSourceCalled = true + p.ReadDataSourceRequest = r + + if p.ReadDataSourceFn != nil { + return p.ReadDataSourceFn(r) + } + + return p.ReadDataSourceResponse +} + +func (p *MockProvider) Close() error { + p.CloseCalled = true + return p.CloseError +} diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/provisioner_mock.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/provisioner_mock.go new file mode 100644 index 00000000..2a332354 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/provisioner_mock.go @@ -0,0 +1,104 @@ +package terraform + +import ( + "sync" + + "github.com/hashicorp/terraform/provisioners" +) + +var _ provisioners.Interface = (*MockProvisioner)(nil) + +// MockProvisioner implements provisioners.Interface but mocks out all the +// calls for testing purposes. +type MockProvisioner struct { + sync.Mutex + // Anything you want, in case you need to store extra data with the mock. + Meta interface{} + + GetSchemaCalled bool + GetSchemaResponse provisioners.GetSchemaResponse + + ValidateProvisionerConfigCalled bool + ValidateProvisionerConfigRequest provisioners.ValidateProvisionerConfigRequest + ValidateProvisionerConfigResponse provisioners.ValidateProvisionerConfigResponse + ValidateProvisionerConfigFn func(provisioners.ValidateProvisionerConfigRequest) provisioners.ValidateProvisionerConfigResponse + + ProvisionResourceCalled bool + ProvisionResourceRequest provisioners.ProvisionResourceRequest + ProvisionResourceResponse provisioners.ProvisionResourceResponse + ProvisionResourceFn func(provisioners.ProvisionResourceRequest) provisioners.ProvisionResourceResponse + + StopCalled bool + StopResponse error + StopFn func() error + + CloseCalled bool + CloseResponse error + CloseFn func() error +} + +func (p *MockProvisioner) GetSchema() provisioners.GetSchemaResponse { + p.Lock() + defer p.Unlock() + + p.GetSchemaCalled = true + return p.getSchema() +} + +// getSchema is the implementation of GetSchema, which can be called from other +// methods on MockProvisioner that may already be holding the lock. +func (p *MockProvisioner) getSchema() provisioners.GetSchemaResponse { + return p.GetSchemaResponse +} + +func (p *MockProvisioner) ValidateProvisionerConfig(r provisioners.ValidateProvisionerConfigRequest) provisioners.ValidateProvisionerConfigResponse { + p.Lock() + defer p.Unlock() + + p.ValidateProvisionerConfigCalled = true + p.ValidateProvisionerConfigRequest = r + if p.ValidateProvisionerConfigFn != nil { + return p.ValidateProvisionerConfigFn(r) + } + return p.ValidateProvisionerConfigResponse +} + +func (p *MockProvisioner) ProvisionResource(r provisioners.ProvisionResourceRequest) provisioners.ProvisionResourceResponse { + p.Lock() + defer p.Unlock() + + p.ProvisionResourceCalled = true + p.ProvisionResourceRequest = r + if p.ProvisionResourceFn != nil { + fn := p.ProvisionResourceFn + return fn(r) + } + + return p.ProvisionResourceResponse +} + +func (p *MockProvisioner) Stop() error { + // We intentionally don't lock in this one because the whole point of this + // method is to be called concurrently with another operation that can + // be cancelled. The provisioner itself is responsible for handling + // any concurrency concerns in this case. + + p.StopCalled = true + if p.StopFn != nil { + return p.StopFn() + } + + return p.StopResponse +} + +func (p *MockProvisioner) Close() error { + p.Lock() + defer p.Unlock() + + p.CloseCalled = true + if p.CloseFn != nil { + return p.CloseFn() + } + + return p.CloseResponse +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/resource.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource.go diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource_address.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_address.go similarity index 99% rename from vendor/github.com/hashicorp/terraform/terraform/resource_address.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_address.go index 4acf122b..75854b6b 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/resource_address.go +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_address.go @@ -92,7 +92,7 @@ func (r *ResourceAddress) String() string { // HasResourceSpec returns true if the address has a resource spec, as // defined in the documentation: -// https://www.terraform.io/docs/internals/resource-addressing.html +// https://www.terraform.io/docs/cli/state/resource-addressing.html // In particular, this returns false if the address contains only // a module path, thus addressing the entire module. func (r *ResourceAddress) HasResourceSpec() bool { @@ -419,7 +419,7 @@ func (addr *ResourceAddress) ModuleInstanceAddr() addrs.ModuleInstance { // Contains returns true if and only if the given node is contained within // the receiver. // -// Containment is defined in terms of the module and resource heirarchy: +// Containment is defined in terms of the module and resource hierarchy: // a resource is contained within its module and any ancestor modules, // an indexed resource instance is contained with the unindexed resource, etc. func (addr *ResourceAddress) Contains(other *ResourceAddress) bool { diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource_address_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_address_test.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/resource_address_test.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_address_test.go diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource_mode.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_mode.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/resource_mode.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_mode.go diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource_mode_string.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_mode_string.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/resource_mode_string.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_mode_string.go diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_provider.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_provider.go new file mode 100644 index 00000000..dccfec68 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_provider.go @@ -0,0 +1,236 @@ +package terraform + +// ResourceProvider is a legacy interface for providers. +// +// This is retained only for compatibility with legacy code. The current +// interface for providers is providers.Interface, in the sibling directory +// named "providers". +type ResourceProvider interface { + /********************************************************************* + * Functions related to the provider + *********************************************************************/ + + // ProviderSchema returns the config schema for the main provider + // configuration, as would appear in a "provider" block in the + // configuration files. + // + // Currently not all providers support schema. Callers must therefore + // first call Resources and DataSources and ensure that at least one + // resource or data source has the SchemaAvailable flag set. + GetSchema(*ProviderSchemaRequest) (*ProviderSchema, error) + + // Input was used prior to v0.12 to ask the provider to prompt the user + // for input to complete the configuration. + // + // From v0.12 onwards this method is never called because Terraform Core + // is able to handle the necessary input logic itself based on the + // schema returned from GetSchema. + Input(UIInput, *ResourceConfig) (*ResourceConfig, error) + + // Validate is called once at the beginning with the raw configuration + // (no interpolation done) and can return a list of warnings and/or + // errors. + // + // This is called once with the provider configuration only. It may not + // be called at all if no provider configuration is given. + // + // This should not assume that any values of the configurations are valid. + // The primary use case of this call is to check that required keys are + // set. + Validate(*ResourceConfig) ([]string, []error) + + // Configure configures the provider itself with the configuration + // given. This is useful for setting things like access keys. + // + // This won't be called at all if no provider configuration is given. + // + // Configure returns an error if it occurred. + Configure(*ResourceConfig) error + + // Resources returns all the available resource types that this provider + // knows how to manage. + Resources() []ResourceType + + // Stop is called when the provider should halt any in-flight actions. + // + // This can be used to make a nicer Ctrl-C experience for Terraform. + // Even if this isn't implemented to do anything (just returns nil), + // Terraform will still cleanly stop after the currently executing + // graph node is complete. However, this API can be used to make more + // efficient halts. + // + // Stop doesn't have to and shouldn't block waiting for in-flight actions + // to complete. It should take any action it wants and return immediately + // acknowledging it has received the stop request. Terraform core will + // automatically not make any further API calls to the provider soon + // after Stop is called (technically exactly once the currently executing + // graph nodes are complete). + // + // The error returned, if non-nil, is assumed to mean that signaling the + // stop somehow failed and that the user should expect potentially waiting + // a longer period of time. + Stop() error + + /********************************************************************* + * Functions related to individual resources + *********************************************************************/ + + // ValidateResource is called once at the beginning with the raw + // configuration (no interpolation done) and can return a list of warnings + // and/or errors. + // + // This is called once per resource. + // + // This should not assume any of the values in the resource configuration + // are valid since it is possible they have to be interpolated still. + // The primary use case of this call is to check that the required keys + // are set and that the general structure is correct. + ValidateResource(string, *ResourceConfig) ([]string, []error) + + // Apply applies a diff to a specific resource and returns the new + // resource state along with an error. + // + // If the resource state given has an empty ID, then a new resource + // is expected to be created. + Apply( + *InstanceInfo, + *InstanceState, + *InstanceDiff) (*InstanceState, error) + + // Diff diffs a resource versus a desired state and returns + // a diff. + Diff( + *InstanceInfo, + *InstanceState, + *ResourceConfig) (*InstanceDiff, error) + + // Refresh refreshes a resource and updates all of its attributes + // with the latest information. + Refresh(*InstanceInfo, *InstanceState) (*InstanceState, error) + + /********************************************************************* + * Functions related to importing + *********************************************************************/ + + // ImportState requests that the given resource be imported. + // + // The returned InstanceState only requires ID be set. Importing + // will always call Refresh after the state to complete it. + // + // IMPORTANT: InstanceState doesn't have the resource type attached + // to it. A type must be specified on the state via the Ephemeral + // field on the state. + // + // This function can return multiple states. Normally, an import + // will map 1:1 to a physical resource. However, some resources map + // to multiple. For example, an AWS security group may contain many rules. + // Each rule is represented by a separate resource in Terraform, + // therefore multiple states are returned. + ImportState(*InstanceInfo, string) ([]*InstanceState, error) + + /********************************************************************* + * Functions related to data resources + *********************************************************************/ + + // ValidateDataSource is called once at the beginning with the raw + // configuration (no interpolation done) and can return a list of warnings + // and/or errors. + // + // This is called once per data source instance. + // + // This should not assume any of the values in the resource configuration + // are valid since it is possible they have to be interpolated still. + // The primary use case of this call is to check that the required keys + // are set and that the general structure is correct. + ValidateDataSource(string, *ResourceConfig) ([]string, []error) + + // DataSources returns all of the available data sources that this + // provider implements. + DataSources() []DataSource + + // ReadDataDiff produces a diff that represents the state that will + // be produced when the given data source is read using a later call + // to ReadDataApply. + ReadDataDiff(*InstanceInfo, *ResourceConfig) (*InstanceDiff, error) + + // ReadDataApply initializes a data instance using the configuration + // in a diff produced by ReadDataDiff. + ReadDataApply(*InstanceInfo, *InstanceDiff) (*InstanceState, error) +} + +// ResourceProviderCloser is an interface that providers that can close +// connections that aren't needed anymore must implement. +type ResourceProviderCloser interface { + Close() error +} + +// ResourceType is a type of resource that a resource provider can manage. +type ResourceType struct { + Name string // Name of the resource, example "instance" (no provider prefix) + Importable bool // Whether this resource supports importing + + // SchemaAvailable is set if the provider supports the ProviderSchema, + // ResourceTypeSchema and DataSourceSchema methods. Although it is + // included on each resource type, it's actually a provider-wide setting + // that's smuggled here only because that avoids a breaking change to + // the plugin protocol. + SchemaAvailable bool +} + +// DataSource is a data source that a resource provider implements. +type DataSource struct { + Name string + + // SchemaAvailable is set if the provider supports the ProviderSchema, + // ResourceTypeSchema and DataSourceSchema methods. Although it is + // included on each resource type, it's actually a provider-wide setting + // that's smuggled here only because that avoids a breaking change to + // the plugin protocol. + SchemaAvailable bool +} + +// ResourceProviderFactory is a function type that creates a new instance +// of a resource provider. +type ResourceProviderFactory func() (ResourceProvider, error) + +// ResourceProviderFactoryFixed is a helper that creates a +// ResourceProviderFactory that just returns some fixed provider. +func ResourceProviderFactoryFixed(p ResourceProvider) ResourceProviderFactory { + return func() (ResourceProvider, error) { + return p, nil + } +} + +func ProviderHasResource(p ResourceProvider, n string) bool { + for _, rt := range p.Resources() { + if rt.Name == n { + return true + } + } + + return false +} + +func ProviderHasDataSource(p ResourceProvider, n string) bool { + for _, rt := range p.DataSources() { + if rt.Name == n { + return true + } + } + + return false +} + +const errPluginInit = ` +Plugin reinitialization required. Please run "terraform init". + +Plugins are external binaries that Terraform uses to access and manipulate +resources. The configuration provided requires plugins which can't be located, +don't satisfy the version constraints, or are otherwise incompatible. + +Terraform automatically discovers provider requirements from your +configuration, including providers used in child modules. To see the +requirements and constraints, run "terraform providers". + +%s +` diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_provider_mock.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_provider_mock.go new file mode 100644 index 00000000..a167b722 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_provider_mock.go @@ -0,0 +1,315 @@ +package terraform + +import ( + "sync" +) + +// MockResourceProvider implements ResourceProvider but mocks out all the +// calls for testing purposes. +type MockResourceProvider struct { + sync.Mutex + + // Anything you want, in case you need to store extra data with the mock. + Meta interface{} + + CloseCalled bool + CloseError error + GetSchemaCalled bool + GetSchemaRequest *ProviderSchemaRequest + GetSchemaReturn *ProviderSchema + GetSchemaReturnError error + InputCalled bool + InputInput UIInput + InputConfig *ResourceConfig + InputReturnConfig *ResourceConfig + InputReturnError error + InputFn func(UIInput, *ResourceConfig) (*ResourceConfig, error) + ApplyCalled bool + ApplyInfo *InstanceInfo + ApplyState *InstanceState + ApplyDiff *InstanceDiff + ApplyFn func(*InstanceInfo, *InstanceState, *InstanceDiff) (*InstanceState, error) + ApplyReturn *InstanceState + ApplyReturnError error + ConfigureCalled bool + ConfigureConfig *ResourceConfig + ConfigureProviderFn func(*ResourceConfig) error + ConfigureReturnError error + DiffCalled bool + DiffInfo *InstanceInfo + DiffState *InstanceState + DiffDesired *ResourceConfig + DiffFn func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) + DiffReturn *InstanceDiff + DiffReturnError error + RefreshCalled bool + RefreshInfo *InstanceInfo + RefreshState *InstanceState + RefreshFn func(*InstanceInfo, *InstanceState) (*InstanceState, error) + RefreshReturn *InstanceState + RefreshReturnError error + ResourcesCalled bool + ResourcesReturn []ResourceType + ReadDataApplyCalled bool + ReadDataApplyInfo *InstanceInfo + ReadDataApplyDiff *InstanceDiff + ReadDataApplyFn func(*InstanceInfo, *InstanceDiff) (*InstanceState, error) + ReadDataApplyReturn *InstanceState + ReadDataApplyReturnError error + ReadDataDiffCalled bool + ReadDataDiffInfo *InstanceInfo + ReadDataDiffDesired *ResourceConfig + ReadDataDiffFn func(*InstanceInfo, *ResourceConfig) (*InstanceDiff, error) + ReadDataDiffReturn *InstanceDiff + ReadDataDiffReturnError error + StopCalled bool + StopFn func() error + StopReturnError error + DataSourcesCalled bool + DataSourcesReturn []DataSource + ValidateCalled bool + ValidateConfig *ResourceConfig + ValidateFn func(*ResourceConfig) ([]string, []error) + ValidateReturnWarns []string + ValidateReturnErrors []error + ValidateResourceFn func(string, *ResourceConfig) ([]string, []error) + ValidateResourceCalled bool + ValidateResourceType string + ValidateResourceConfig *ResourceConfig + ValidateResourceReturnWarns []string + ValidateResourceReturnErrors []error + ValidateDataSourceFn func(string, *ResourceConfig) ([]string, []error) + ValidateDataSourceCalled bool + ValidateDataSourceType string + ValidateDataSourceConfig *ResourceConfig + ValidateDataSourceReturnWarns []string + ValidateDataSourceReturnErrors []error + + ImportStateCalled bool + ImportStateInfo *InstanceInfo + ImportStateID string + ImportStateReturn []*InstanceState + ImportStateReturnError error + ImportStateFn func(*InstanceInfo, string) ([]*InstanceState, error) +} + +func (p *MockResourceProvider) Close() error { + p.CloseCalled = true + return p.CloseError +} + +func (p *MockResourceProvider) GetSchema(req *ProviderSchemaRequest) (*ProviderSchema, error) { + p.Lock() + defer p.Unlock() + + p.GetSchemaCalled = true + p.GetSchemaRequest = req + return p.GetSchemaReturn, p.GetSchemaReturnError +} + +func (p *MockResourceProvider) Input( + input UIInput, c *ResourceConfig) (*ResourceConfig, error) { + p.Lock() + defer p.Unlock() + p.InputCalled = true + p.InputInput = input + p.InputConfig = c + if p.InputFn != nil { + return p.InputFn(input, c) + } + return p.InputReturnConfig, p.InputReturnError +} + +func (p *MockResourceProvider) Validate(c *ResourceConfig) ([]string, []error) { + p.Lock() + defer p.Unlock() + + p.ValidateCalled = true + p.ValidateConfig = c + if p.ValidateFn != nil { + return p.ValidateFn(c) + } + return p.ValidateReturnWarns, p.ValidateReturnErrors +} + +func (p *MockResourceProvider) ValidateResource(t string, c *ResourceConfig) ([]string, []error) { + p.Lock() + defer p.Unlock() + + p.ValidateResourceCalled = true + p.ValidateResourceType = t + p.ValidateResourceConfig = c + + if p.ValidateResourceFn != nil { + return p.ValidateResourceFn(t, c) + } + + return p.ValidateResourceReturnWarns, p.ValidateResourceReturnErrors +} + +func (p *MockResourceProvider) Configure(c *ResourceConfig) error { + p.Lock() + defer p.Unlock() + + p.ConfigureCalled = true + p.ConfigureConfig = c + + if p.ConfigureProviderFn != nil { + return p.ConfigureProviderFn(c) + } + + return p.ConfigureReturnError +} + +func (p *MockResourceProvider) Stop() error { + p.Lock() + defer p.Unlock() + + p.StopCalled = true + if p.StopFn != nil { + return p.StopFn() + } + + return p.StopReturnError +} + +func (p *MockResourceProvider) Apply( + info *InstanceInfo, + state *InstanceState, + diff *InstanceDiff) (*InstanceState, error) { + // We only lock while writing data. Reading is fine + p.Lock() + p.ApplyCalled = true + p.ApplyInfo = info + p.ApplyState = state + p.ApplyDiff = diff + p.Unlock() + + if p.ApplyFn != nil { + return p.ApplyFn(info, state, diff) + } + + return p.ApplyReturn.DeepCopy(), p.ApplyReturnError +} + +func (p *MockResourceProvider) Diff( + info *InstanceInfo, + state *InstanceState, + desired *ResourceConfig) (*InstanceDiff, error) { + p.Lock() + defer p.Unlock() + + p.DiffCalled = true + p.DiffInfo = info + p.DiffState = state + p.DiffDesired = desired + + if p.DiffFn != nil { + return p.DiffFn(info, state, desired) + } + + return p.DiffReturn.DeepCopy(), p.DiffReturnError +} + +func (p *MockResourceProvider) Refresh( + info *InstanceInfo, + s *InstanceState) (*InstanceState, error) { + p.Lock() + defer p.Unlock() + + p.RefreshCalled = true + p.RefreshInfo = info + p.RefreshState = s + + if p.RefreshFn != nil { + return p.RefreshFn(info, s) + } + + return p.RefreshReturn.DeepCopy(), p.RefreshReturnError +} + +func (p *MockResourceProvider) Resources() []ResourceType { + p.Lock() + defer p.Unlock() + + p.ResourcesCalled = true + return p.ResourcesReturn +} + +func (p *MockResourceProvider) ImportState(info *InstanceInfo, id string) ([]*InstanceState, error) { + p.Lock() + defer p.Unlock() + + p.ImportStateCalled = true + p.ImportStateInfo = info + p.ImportStateID = id + if p.ImportStateFn != nil { + return p.ImportStateFn(info, id) + } + + var result []*InstanceState + if p.ImportStateReturn != nil { + result = make([]*InstanceState, len(p.ImportStateReturn)) + for i, v := range p.ImportStateReturn { + result[i] = v.DeepCopy() + } + } + + return result, p.ImportStateReturnError +} + +func (p *MockResourceProvider) ValidateDataSource(t string, c *ResourceConfig) ([]string, []error) { + p.Lock() + defer p.Unlock() + + p.ValidateDataSourceCalled = true + p.ValidateDataSourceType = t + p.ValidateDataSourceConfig = c + + if p.ValidateDataSourceFn != nil { + return p.ValidateDataSourceFn(t, c) + } + + return p.ValidateDataSourceReturnWarns, p.ValidateDataSourceReturnErrors +} + +func (p *MockResourceProvider) ReadDataDiff( + info *InstanceInfo, + desired *ResourceConfig) (*InstanceDiff, error) { + p.Lock() + defer p.Unlock() + + p.ReadDataDiffCalled = true + p.ReadDataDiffInfo = info + p.ReadDataDiffDesired = desired + if p.ReadDataDiffFn != nil { + return p.ReadDataDiffFn(info, desired) + } + + return p.ReadDataDiffReturn.DeepCopy(), p.ReadDataDiffReturnError +} + +func (p *MockResourceProvider) ReadDataApply( + info *InstanceInfo, + d *InstanceDiff) (*InstanceState, error) { + p.Lock() + defer p.Unlock() + + p.ReadDataApplyCalled = true + p.ReadDataApplyInfo = info + p.ReadDataApplyDiff = d + + if p.ReadDataApplyFn != nil { + return p.ReadDataApplyFn(info, d) + } + + return p.ReadDataApplyReturn.DeepCopy(), p.ReadDataApplyReturnError +} + +func (p *MockResourceProvider) DataSources() []DataSource { + p.Lock() + defer p.Unlock() + + p.DataSourcesCalled = true + return p.DataSourcesReturn +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource_provisioner.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_provisioner.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/resource_provisioner.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_provisioner.go diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource_provisioner_mock.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_provisioner_mock.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/resource_provisioner_mock.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_provisioner_mock.go diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_test.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/resource_test.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/resource_test.go diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/schemas.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/schemas.go new file mode 100644 index 00000000..00446065 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/schemas.go @@ -0,0 +1,285 @@ +package terraform + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/configs" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/states" + "github.com/hashicorp/terraform/tfdiags" +) + +// Schemas is a container for various kinds of schema that Terraform needs +// during processing. +type Schemas struct { + Providers map[addrs.Provider]*ProviderSchema + Provisioners map[string]*configschema.Block +} + +// ProviderSchema returns the entire ProviderSchema object that was produced +// by the plugin for the given provider, or nil if no such schema is available. +// +// It's usually better to go use the more precise methods offered by type +// Schemas to handle this detail automatically. +func (ss *Schemas) ProviderSchema(provider addrs.Provider) *ProviderSchema { + if ss.Providers == nil { + return nil + } + return ss.Providers[provider] +} + +// ProviderConfig returns the schema for the provider configuration of the +// given provider type, or nil if no such schema is available. +func (ss *Schemas) ProviderConfig(provider addrs.Provider) *configschema.Block { + ps := ss.ProviderSchema(provider) + if ps == nil { + return nil + } + return ps.Provider +} + +// ResourceTypeConfig returns the schema for the configuration of a given +// resource type belonging to a given provider type, or nil of no such +// schema is available. +// +// In many cases the provider type is inferrable from the resource type name, +// but this is not always true because users can override the provider for +// a resource using the "provider" meta-argument. Therefore it's important to +// always pass the correct provider name, even though it many cases it feels +// redundant. +func (ss *Schemas) ResourceTypeConfig(provider addrs.Provider, resourceMode addrs.ResourceMode, resourceType string) (block *configschema.Block, schemaVersion uint64) { + ps := ss.ProviderSchema(provider) + if ps == nil || ps.ResourceTypes == nil { + return nil, 0 + } + return ps.SchemaForResourceType(resourceMode, resourceType) +} + +// ProvisionerConfig returns the schema for the configuration of a given +// provisioner, or nil of no such schema is available. +func (ss *Schemas) ProvisionerConfig(name string) *configschema.Block { + return ss.Provisioners[name] +} + +// LoadSchemas searches the given configuration, state and plan (any of which +// may be nil) for constructs that have an associated schema, requests the +// necessary schemas from the given component factory (which must _not_ be nil), +// and returns a single object representing all of the necessary schemas. +// +// If an error is returned, it may be a wrapped tfdiags.Diagnostics describing +// errors across multiple separate objects. Errors here will usually indicate +// either misbehavior on the part of one of the providers or of the provider +// protocol itself. When returned with errors, the returned schemas object is +// still valid but may be incomplete. +func LoadSchemas(config *configs.Config, state *states.State, components contextComponentFactory) (*Schemas, error) { + schemas := &Schemas{ + Providers: map[addrs.Provider]*ProviderSchema{}, + Provisioners: map[string]*configschema.Block{}, + } + var diags tfdiags.Diagnostics + + newDiags := loadProviderSchemas(schemas.Providers, config, state, components) + diags = diags.Append(newDiags) + newDiags = loadProvisionerSchemas(schemas.Provisioners, config, components) + diags = diags.Append(newDiags) + + return schemas, diags.Err() +} + +func loadProviderSchemas(schemas map[addrs.Provider]*ProviderSchema, config *configs.Config, state *states.State, components contextComponentFactory) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + ensure := func(fqn addrs.Provider) { + name := fqn.String() + + if _, exists := schemas[fqn]; exists { + return + } + + log.Printf("[TRACE] LoadSchemas: retrieving schema for provider type %q", name) + provider, err := components.ResourceProvider(fqn) + if err != nil { + // We'll put a stub in the map so we won't re-attempt this on + // future calls. + schemas[fqn] = &ProviderSchema{} + diags = diags.Append( + fmt.Errorf("Failed to instantiate provider %q to obtain schema: %s", name, err), + ) + return + } + defer func() { + provider.Close() + }() + + resp := provider.GetProviderSchema() + if resp.Diagnostics.HasErrors() { + // We'll put a stub in the map so we won't re-attempt this on + // future calls. + schemas[fqn] = &ProviderSchema{} + diags = diags.Append( + fmt.Errorf("Failed to retrieve schema from provider %q: %s", name, resp.Diagnostics.Err()), + ) + return + } + + s := &ProviderSchema{ + Provider: resp.Provider.Block, + ResourceTypes: make(map[string]*configschema.Block), + DataSources: make(map[string]*configschema.Block), + + ResourceTypeSchemaVersions: make(map[string]uint64), + } + + if resp.Provider.Version < 0 { + // We're not using the version numbers here yet, but we'll check + // for validity anyway in case we start using them in future. + diags = diags.Append( + fmt.Errorf("invalid negative schema version provider configuration for provider %q", name), + ) + } + + for t, r := range resp.ResourceTypes { + s.ResourceTypes[t] = r.Block + s.ResourceTypeSchemaVersions[t] = uint64(r.Version) + if r.Version < 0 { + diags = diags.Append( + fmt.Errorf("invalid negative schema version for resource type %s in provider %q", t, name), + ) + } + } + + for t, d := range resp.DataSources { + s.DataSources[t] = d.Block + if d.Version < 0 { + // We're not using the version numbers here yet, but we'll check + // for validity anyway in case we start using them in future. + diags = diags.Append( + fmt.Errorf("invalid negative schema version for data source %s in provider %q", t, name), + ) + } + } + + schemas[fqn] = s + + if resp.ProviderMeta.Block != nil { + s.ProviderMeta = resp.ProviderMeta.Block + } + } + + if config != nil { + for _, fqn := range config.ProviderTypes() { + ensure(fqn) + } + } + + if state != nil { + needed := providers.AddressedTypesAbs(state.ProviderAddrs()) + for _, typeAddr := range needed { + ensure(typeAddr) + } + } + + return diags +} + +func loadProvisionerSchemas(schemas map[string]*configschema.Block, config *configs.Config, components contextComponentFactory) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + ensure := func(name string) { + if _, exists := schemas[name]; exists { + return + } + + log.Printf("[TRACE] LoadSchemas: retrieving schema for provisioner %q", name) + provisioner, err := components.ResourceProvisioner(name) + if err != nil { + // We'll put a stub in the map so we won't re-attempt this on + // future calls. + schemas[name] = &configschema.Block{} + diags = diags.Append( + fmt.Errorf("Failed to instantiate provisioner %q to obtain schema: %s", name, err), + ) + return + } + defer func() { + if closer, ok := provisioner.(ResourceProvisionerCloser); ok { + closer.Close() + } + }() + + resp := provisioner.GetSchema() + if resp.Diagnostics.HasErrors() { + // We'll put a stub in the map so we won't re-attempt this on + // future calls. + schemas[name] = &configschema.Block{} + diags = diags.Append( + fmt.Errorf("Failed to retrieve schema from provisioner %q: %s", name, resp.Diagnostics.Err()), + ) + return + } + + schemas[name] = resp.Provisioner + } + + if config != nil { + for _, rc := range config.Module.ManagedResources { + for _, pc := range rc.Managed.Provisioners { + ensure(pc.Type) + } + } + + // Must also visit our child modules, recursively. + for _, cc := range config.Children { + childDiags := loadProvisionerSchemas(schemas, cc, components) + diags = diags.Append(childDiags) + } + } + + return diags +} + +// ProviderSchema represents the schema for a provider's own configuration +// and the configuration for some or all of its resources and data sources. +// +// The completeness of this structure depends on how it was constructed. +// When constructed for a configuration, it will generally include only +// resource types and data sources used by that configuration. +type ProviderSchema struct { + Provider *configschema.Block + ProviderMeta *configschema.Block + ResourceTypes map[string]*configschema.Block + DataSources map[string]*configschema.Block + + ResourceTypeSchemaVersions map[string]uint64 +} + +// SchemaForResourceType attempts to find a schema for the given mode and type. +// Returns nil if no such schema is available. +func (ps *ProviderSchema) SchemaForResourceType(mode addrs.ResourceMode, typeName string) (schema *configschema.Block, version uint64) { + switch mode { + case addrs.ManagedResourceMode: + return ps.ResourceTypes[typeName], ps.ResourceTypeSchemaVersions[typeName] + case addrs.DataResourceMode: + // Data resources don't have schema versions right now, since state is discarded for each refresh + return ps.DataSources[typeName], 0 + default: + // Shouldn't happen, because the above cases are comprehensive. + return nil, 0 + } +} + +// SchemaForResourceAddr attempts to find a schema for the mode and type from +// the given resource address. Returns nil if no such schema is available. +func (ps *ProviderSchema) SchemaForResourceAddr(addr addrs.Resource) (schema *configschema.Block, version uint64) { + return ps.SchemaForResourceType(addr.Mode, addr.Type) +} + +// ProviderSchemaRequest is used to describe to a ResourceProvider which +// aspects of schema are required, when calling the GetSchema method. +type ProviderSchemaRequest struct { + ResourceTypes []string + DataSources []string +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/state.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/state.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/state.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/state.go diff --git a/vendor/github.com/hashicorp/terraform/terraform/state_filter.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/state_filter.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/state_filter.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/state_filter.go diff --git a/vendor/github.com/hashicorp/terraform/terraform/state_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/state_test.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/state_test.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/state_test.go diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/state_upgrade_v1_to_v2.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/state_upgrade_v1_to_v2.go similarity index 100% rename from vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/state_upgrade_v1_to_v2.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/state_upgrade_v1_to_v2.go diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/state_upgrade_v2_to_v3.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/state_upgrade_v2_to_v3.go similarity index 100% rename from vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/state_upgrade_v2_to_v3.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/state_upgrade_v2_to_v3.go diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/state_v1.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/state_v1.go similarity index 100% rename from vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/state_v1.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/state_v1.go diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/testing.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/testing.go similarity index 100% rename from vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/testing.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/testing.go diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_input.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_input.go new file mode 100644 index 00000000..688bcf71 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_input.go @@ -0,0 +1,32 @@ +package terraform + +import "context" + +// UIInput is the interface that must be implemented to ask for input +// from this user. This should forward the request to wherever the user +// inputs things to ask for values. +type UIInput interface { + Input(context.Context, *InputOpts) (string, error) +} + +// InputOpts are options for asking for input. +type InputOpts struct { + // Id is a unique ID for the question being asked that might be + // used for logging or to look up a prior answered question. + Id string + + // Query is a human-friendly question for inputting this value. + Query string + + // Description is a description about what this option is. Be wary + // that this will probably be in a terminal so split lines as you see + // necessary. + Description string + + // Default will be the value returned if no data is entered. + Default string + + // Secret should be true if we are asking for sensitive input. + // If attached to a TTY, Terraform will disable echo. + Secret bool +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/ui_input_mock.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_input_mock.go similarity index 100% rename from vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/ui_input_mock.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_input_mock.go diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/ui_input_prefix.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_input_prefix.go similarity index 100% rename from vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/ui_input_prefix.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_input_prefix.go diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_input_prefix_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_input_prefix_test.go new file mode 100644 index 00000000..dff42c39 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_input_prefix_test.go @@ -0,0 +1,27 @@ +package terraform + +import ( + "context" + "testing" +) + +func TestPrefixUIInput_impl(t *testing.T) { + var _ UIInput = new(PrefixUIInput) +} + +func TestPrefixUIInput(t *testing.T) { + input := new(MockUIInput) + prefix := &PrefixUIInput{ + IdPrefix: "foo", + UIInput: input, + } + + _, err := prefix.Input(context.Background(), &InputOpts{Id: "bar"}) + if err != nil { + t.Fatalf("err: %s", err) + } + + if input.InputOpts.Id != "foo.bar" { + t.Fatalf("bad: %#v", input.InputOpts) + } +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/ui_output.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_output.go similarity index 100% rename from vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/ui_output.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_output.go diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/ui_output_callback.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_output_callback.go similarity index 100% rename from vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/ui_output_callback.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_output_callback.go diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_output_callback_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_output_callback_test.go new file mode 100644 index 00000000..1dd5ccdd --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_output_callback_test.go @@ -0,0 +1,9 @@ +package terraform + +import ( + "testing" +) + +func TestCallbackUIOutput_impl(t *testing.T) { + var _ UIOutput = new(CallbackUIOutput) +} diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/ui_output_mock.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_output_mock.go similarity index 100% rename from vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/ui_output_mock.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_output_mock.go diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_output_mock_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_output_mock_test.go new file mode 100644 index 00000000..0a23c2e2 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/ui_output_mock_test.go @@ -0,0 +1,9 @@ +package terraform + +import ( + "testing" +) + +func TestMockUIOutput(t *testing.T) { + var _ UIOutput = new(MockUIOutput) +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/upgrade_state_v1_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/upgrade_state_v1_test.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/upgrade_state_v1_test.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/upgrade_state_v1_test.go diff --git a/vendor/github.com/hashicorp/terraform/terraform/upgrade_state_v2_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/upgrade_state_v2_test.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/upgrade_state_v2_test.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/upgrade_state_v2_test.go diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/util.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/util.go new file mode 100644 index 00000000..7966b58d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/util.go @@ -0,0 +1,75 @@ +package terraform + +import ( + "sort" +) + +// Semaphore is a wrapper around a channel to provide +// utility methods to clarify that we are treating the +// channel as a semaphore +type Semaphore chan struct{} + +// NewSemaphore creates a semaphore that allows up +// to a given limit of simultaneous acquisitions +func NewSemaphore(n int) Semaphore { + if n <= 0 { + panic("semaphore with limit <=0") + } + ch := make(chan struct{}, n) + return Semaphore(ch) +} + +// Acquire is used to acquire an available slot. +// Blocks until available. +func (s Semaphore) Acquire() { + s <- struct{}{} +} + +// TryAcquire is used to do a non-blocking acquire. +// Returns a bool indicating success +func (s Semaphore) TryAcquire() bool { + select { + case s <- struct{}{}: + return true + default: + return false + } +} + +// Release is used to return a slot. Acquire must +// be called as a pre-condition. +func (s Semaphore) Release() { + select { + case <-s: + default: + panic("release without an acquire") + } +} + +// strSliceContains checks if a given string is contained in a slice +// When anybody asks why Go needs generics, here you go. +func strSliceContains(haystack []string, needle string) bool { + for _, s := range haystack { + if s == needle { + return true + } + } + return false +} + +// deduplicate a slice of strings +func uniqueStrings(s []string) []string { + if len(s) < 2 { + return s + } + + sort.Strings(s) + result := make([]string, 1, len(s)) + result[0] = s[0] + for i := 1; i < len(s); i++ { + if s[i] != result[len(result)-1] { + result = append(result, s[i]) + } + } + return result +} diff --git a/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/util_test.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/util_test.go new file mode 100644 index 00000000..8b3907e2 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/util_test.go @@ -0,0 +1,91 @@ +package terraform + +import ( + "fmt" + "reflect" + "testing" + "time" +) + +func TestSemaphore(t *testing.T) { + s := NewSemaphore(2) + timer := time.AfterFunc(time.Second, func() { + panic("deadlock") + }) + defer timer.Stop() + + s.Acquire() + if !s.TryAcquire() { + t.Fatalf("should acquire") + } + if s.TryAcquire() { + t.Fatalf("should not acquire") + } + s.Release() + s.Release() + + // This release should panic + defer func() { + r := recover() + if r == nil { + t.Fatalf("should panic") + } + }() + s.Release() +} + +func TestStrSliceContains(t *testing.T) { + if strSliceContains(nil, "foo") { + t.Fatalf("Bad") + } + if strSliceContains([]string{}, "foo") { + t.Fatalf("Bad") + } + if strSliceContains([]string{"bar"}, "foo") { + t.Fatalf("Bad") + } + if !strSliceContains([]string{"bar", "foo"}, "foo") { + t.Fatalf("Bad") + } +} + +func TestUniqueStrings(t *testing.T) { + cases := []struct { + Input []string + Expected []string + }{ + { + []string{}, + []string{}, + }, + { + []string{"x"}, + []string{"x"}, + }, + { + []string{"a", "b", "c"}, + []string{"a", "b", "c"}, + }, + { + []string{"a", "a", "a"}, + []string{"a"}, + }, + { + []string{"a", "b", "a", "b", "a", "a"}, + []string{"a", "b"}, + }, + { + []string{"c", "b", "a", "c", "b"}, + []string{"a", "b", "c"}, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("unique-%d", i), func(t *testing.T) { + actual := uniqueStrings(tc.Input) + if !reflect.DeepEqual(tc.Expected, actual) { + t.Fatalf("Expected: %q\nGot: %q", tc.Expected, actual) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/version.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/version.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/version.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/version.go diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/version_required.go b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/version_required.go similarity index 87% rename from vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/version_required.go rename to vendor/github.com/hashicorp/terraform/internal/legacy/terraform/version_required.go index 4cc3bbba..4c9cb34a 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/terraform/version_required.go +++ b/vendor/github.com/hashicorp/terraform/internal/legacy/terraform/version_required.go @@ -4,11 +4,11 @@ import ( "fmt" "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" + "github.com/hashicorp/terraform/tfdiags" - "github.com/hashicorp/terraform-plugin-sdk/internal/configs" + "github.com/hashicorp/terraform/configs" - tfversion "github.com/hashicorp/terraform-plugin-sdk/internal/version" + tfversion "github.com/hashicorp/terraform/version" ) // CheckCoreVersionRequirements visits each of the modules in the given @@ -37,7 +37,7 @@ func CheckCoreVersionRequirements(config *configs.Config) tfdiags.Diagnostics { "This configuration does not support Terraform version %s. To proceed, either choose another supported Terraform version or update this version constraint. Version constraints are normally set for good reason, so updating the constraint may lead to other errors or unexpected behavior.", tfversion.String(), ), - Subject: &constraint.DeclRange, + Subject: constraint.DeclRange.Ptr(), }) default: diags = diags.Append(&hcl.Diagnostic{ @@ -47,7 +47,7 @@ func CheckCoreVersionRequirements(config *configs.Config) tfdiags.Diagnostics { "Module %s (from %s) does not support Terraform version %s. To proceed, either choose another supported Terraform version or update this version constraint. Version constraints are normally set for good reason, so updating the constraint may lead to other errors or unexpected behavior.", config.Path, config.SourceAddr, tfversion.String(), ), - Subject: &constraint.DeclRange, + Subject: constraint.DeclRange.Ptr(), }) } } diff --git a/vendor/github.com/hashicorp/terraform/internal/logging/logging.go b/vendor/github.com/hashicorp/terraform/internal/logging/logging.go index 52ac75b0..11bdd474 100644 --- a/vendor/github.com/hashicorp/terraform/internal/logging/logging.go +++ b/vendor/github.com/hashicorp/terraform/internal/logging/logging.go @@ -3,7 +3,6 @@ package logging import ( "fmt" "io" - "io/ioutil" "log" "os" "strings" @@ -15,29 +14,60 @@ import ( // These are the environmental variables that determine if we log, and if // we log whether or not the log should go to a file. const ( - EnvLog = "TF_LOG" // Set to True - EnvLogFile = "TF_LOG_PATH" // Set to a file + envLog = "TF_LOG" + envLogFile = "TF_LOG_PATH" + + // Allow logging of specific subsystems. + // We only separate core and providers for now, but this could be extended + // to other loggers, like provisioners and remote-state backends. + envLogCore = "TF_LOG_CORE" + envLogProvider = "TF_LOG_PROVIDER" ) -// ValidLevels are the log level names that Terraform recognizes. -var ValidLevels = []string{"TRACE", "DEBUG", "INFO", "WARN", "ERROR"} +var ( + // ValidLevels are the log level names that Terraform recognizes. + ValidLevels = []string{"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF"} -// logger is the global hclog logger -var logger hclog.Logger + // logger is the global hclog logger + logger hclog.Logger -// logWriter is a global writer for logs, to be used with the std log package -var logWriter io.Writer + // logWriter is a global writer for logs, to be used with the std log package + logWriter io.Writer + + // initialize our cache of panic output from providers + panics = &panicRecorder{ + panics: make(map[string][]string), + maxLines: 100, + } +) func init() { - logger = NewHCLogger("") + logger = newHCLogger("") logWriter = logger.StandardWriter(&hclog.StandardLoggerOptions{InferLevels: true}) - // setup the default std library logger to use our output + // set up the default std library logger to use our output log.SetFlags(0) log.SetPrefix("") log.SetOutput(logWriter) } +// SetupTempLog adds a new log sink which writes all logs to the given file. +func RegisterSink(f *os.File) { + l, ok := logger.(hclog.InterceptLogger) + if !ok { + panic("global logger is not an InterceptLogger") + } + + if f == nil { + return + } + + l.RegisterSink(hclog.NewSinkAdapter(&hclog.LoggerOptions{ + Level: hclog.Trace, + Output: f, + })) +} + // LogOutput return the default global log io.Writer func LogOutput() io.Writer { return logWriter @@ -48,15 +78,12 @@ func HCLogger() hclog.Logger { return logger } -// NewHCLogger returns a new hclog.Logger instance with the given name -func NewHCLogger(name string) hclog.Logger { +// newHCLogger returns a new hclog.Logger instance with the given name +func newHCLogger(name string) hclog.Logger { logOutput := io.Writer(os.Stderr) - logLevel := CurrentLogLevel() - if logLevel == "" { - logOutput = ioutil.Discard - } + logLevel, json := globalLogLevel() - if logPath := os.Getenv(EnvLogFile); logPath != "" { + if logPath := os.Getenv(envLogFile); logPath != "" { f, err := os.OpenFile(logPath, syscall.O_CREAT|syscall.O_RDWR|syscall.O_APPEND, 0666) if err != nil { fmt.Fprintf(os.Stderr, "Error opening log file: %v\n", err) @@ -65,38 +92,90 @@ func NewHCLogger(name string) hclog.Logger { } } - return hclog.New(&hclog.LoggerOptions{ - Name: name, - Level: hclog.LevelFromString(logLevel), - Output: logOutput, + return hclog.NewInterceptLogger(&hclog.LoggerOptions{ + Name: name, + Level: logLevel, + Output: logOutput, + IndependentLevels: true, + JSONFormat: json, }) } +// NewLogger returns a new logger based in the current global logger, with the +// given name appended. +func NewLogger(name string) hclog.Logger { + if name == "" { + panic("logger name required") + } + return &logPanicWrapper{ + Logger: logger.Named(name), + } +} + +// NewProviderLogger returns a logger for the provider plugin, possibly with a +// different log level from the global logger. +func NewProviderLogger(prefix string) hclog.Logger { + l := &logPanicWrapper{ + Logger: logger.Named(prefix + "provider"), + } + + level := providerLogLevel() + logger.Debug("created provider logger", "level", level) + + l.SetLevel(level) + return l +} + // CurrentLogLevel returns the current log level string based the environment vars func CurrentLogLevel() string { - envLevel := strings.ToUpper(os.Getenv(EnvLog)) + ll, _ := globalLogLevel() + return strings.ToUpper(ll.String()) +} + +func providerLogLevel() hclog.Level { + providerEnvLevel := strings.ToUpper(os.Getenv(envLogProvider)) + if providerEnvLevel == "" { + providerEnvLevel = strings.ToUpper(os.Getenv(envLog)) + } + + return parseLogLevel(providerEnvLevel) +} + +func globalLogLevel() (hclog.Level, bool) { + var json bool + envLevel := strings.ToUpper(os.Getenv(envLog)) + if envLevel == "" { + envLevel = strings.ToUpper(os.Getenv(envLogCore)) + } + if envLevel == "JSON" { + json = true + } + return parseLogLevel(envLevel), json +} + +func parseLogLevel(envLevel string) hclog.Level { if envLevel == "" { - return "" + return hclog.Off + } + if envLevel == "JSON" { + envLevel = "TRACE" } - logLevel := "TRACE" + logLevel := hclog.Trace if isValidLogLevel(envLevel) { - logLevel = envLevel + logLevel = hclog.LevelFromString(envLevel) } else { - log.Printf("[WARN] Invalid log level: %q. Defaulting to level: TRACE. Valid levels are: %+v", + fmt.Fprintf(os.Stderr, "[WARN] Invalid log level: %q. Defaulting to level: TRACE. Valid levels are: %+v", envLevel, ValidLevels) } - if logLevel != "TRACE" { - log.Printf("[WARN] Log levels other than TRACE are currently unreliable, and are supported only for backward compatibility.\n Use TF_LOG=TRACE to see Terraform's internal logs.\n ----") - } return logLevel } // IsDebugOrHigher returns whether or not the current log level is debug or trace func IsDebugOrHigher() bool { - level := string(CurrentLogLevel()) - return level == "DEBUG" || level == "TRACE" + level, _ := globalLogLevel() + return level == hclog.Debug || level == hclog.Trace } func isValidLogLevel(level string) bool { diff --git a/vendor/github.com/hashicorp/terraform/internal/logging/panic.go b/vendor/github.com/hashicorp/terraform/internal/logging/panic.go new file mode 100644 index 00000000..211a1231 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/logging/panic.go @@ -0,0 +1,193 @@ +package logging + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "strings" + "sync" + + "github.com/hashicorp/go-hclog" + "github.com/mitchellh/panicwrap" +) + +// This output is shown if a panic happens. +const panicOutput = ` + +!!!!!!!!!!!!!!!!!!!!!!!!!!! TERRAFORM CRASH !!!!!!!!!!!!!!!!!!!!!!!!!!!! + +Terraform crashed! This is always indicative of a bug within Terraform. +A crash log has been placed at %[1]q relative to your current +working directory. It would be immensely helpful if you could please +report the crash with Terraform[1] so that we can fix this. + +When reporting bugs, please include your terraform version. That +information is available on the first line of crash.log. You can also +get it by running 'terraform --version' on the command line. + +SECURITY WARNING: the %[1]q file that was created may contain +sensitive information that must be redacted before it is safe to share +on the issue tracker. + +[1]: https://github.com/hashicorp/terraform/issues + +!!!!!!!!!!!!!!!!!!!!!!!!!!! TERRAFORM CRASH !!!!!!!!!!!!!!!!!!!!!!!!!!!! +` + +// panicHandler is what is called by panicwrap when a panic is encountered +// within Terraform. It is guaranteed to run after the resulting process has +// exited so we can take the log file, add in the panic, and store it +// somewhere locally. +func PanicHandler(tmpLogPath string) panicwrap.HandlerFunc { + return func(m string) { + // Create the crash log file where we'll write the logs + f, err := ioutil.TempFile(".", "crash.*.log") + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to create crash log file: %s", err) + return + } + defer f.Close() + + tmpLog, err := os.Open(tmpLogPath) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to open log file %q: %v\n", tmpLogPath, err) + return + } + defer tmpLog.Close() + + // Copy the contents to the crash file. This will include + // the panic that just happened. + if _, err = io.Copy(f, tmpLog); err != nil { + fmt.Fprintf(os.Stderr, "Failed to write crash log: %s", err) + return + } + + // add the trace back to the log + f.WriteString("\n" + m) + + // Tell the user a crash occurred in some helpful way that + // they'll hopefully notice. + fmt.Printf("\n\n") + fmt.Printf(panicOutput, f.Name()) + } +} + +const pluginPanicOutput = ` +Stack trace from the %[1]s plugin: + +%s + +Error: The %[1]s plugin crashed! + +This is always indicative of a bug within the plugin. It would be immensely +helpful if you could report the crash with the plugin's maintainers so that it +can be fixed. The output above should help diagnose the issue. +` + +// PluginPanics returns a series of provider panics that were collected during +// execution, and formatted for output. +func PluginPanics() []string { + return panics.allPanics() +} + +// panicRecorder provides a registry to check for plugin panics that may have +// happened when a plugin suddenly terminates. +type panicRecorder struct { + sync.Mutex + + // panics maps the plugin name to the panic output lines received from + // the logger. + panics map[string][]string + + // maxLines is the max number of lines we'll record after seeing a + // panic header. Since this is going to be printed in the UI output, we + // don't want to destroy the scrollback. In most cases, the first few lines + // of the stack trace is all that are required. + maxLines int +} + +// registerPlugin returns an accumulator function which will accept lines of +// a panic stack trace to collect into an error when requested. +func (p *panicRecorder) registerPlugin(name string) func(string) { + p.Lock() + defer p.Unlock() + + // In most cases we shouldn't be starting a plugin if it already + // panicked, but clear out previous entries just in case. + delete(p.panics, name) + + count := 0 + + // this callback is used by the logger to store panic output + return func(line string) { + p.Lock() + defer p.Unlock() + + // stop recording if there are too many lines. + if count > p.maxLines { + return + } + count++ + + p.panics[name] = append(p.panics[name], line) + } +} + +func (p *panicRecorder) allPanics() []string { + p.Lock() + defer p.Unlock() + + var res []string + for name, lines := range p.panics { + if len(lines) == 0 { + continue + } + + res = append(res, fmt.Sprintf(pluginPanicOutput, name, strings.Join(lines, "\n"))) + } + return res +} + +// logPanicWrapper wraps an hclog.Logger and intercepts and records any output +// that appears to be a panic. +type logPanicWrapper struct { + hclog.Logger + panicRecorder func(string) + inPanic bool +} + +// go-plugin will create a new named logger for each plugin binary. +func (l *logPanicWrapper) Named(name string) hclog.Logger { + return &logPanicWrapper{ + Logger: l.Logger.Named(name), + panicRecorder: panics.registerPlugin(name), + } +} + +// we only need to implement Debug, since that is the default output level used +// by go-plugin when encountering unstructured output on stderr. +func (l *logPanicWrapper) Debug(msg string, args ...interface{}) { + // We don't have access to the binary itself, so guess based on the stderr + // output if this is the start of the traceback. An occasional false + // positive shouldn't be a big deal, since this is only retrieved after an + // error of some sort. + + panicPrefix := strings.HasPrefix(msg, "panic: ") || strings.HasPrefix(msg, "fatal error: ") + + l.inPanic = l.inPanic || panicPrefix + + if l.inPanic && l.panicRecorder != nil { + l.panicRecorder(msg) + } + + // If we have logging turned on, we need to prevent panicwrap from seeing + // this as a core panic. This can be done by obfuscating the panic error + // line. + if panicPrefix { + colon := strings.Index(msg, ":") + msg = strings.ToUpper(msg[:colon]) + msg[colon:] + } + + l.Logger.Debug(msg, args...) +} diff --git a/vendor/github.com/hashicorp/terraform/internal/logging/panic_test.go b/vendor/github.com/hashicorp/terraform/internal/logging/panic_test.go new file mode 100644 index 00000000..d2eb7a90 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/logging/panic_test.go @@ -0,0 +1,82 @@ +package logging + +import ( + "bytes" + "fmt" + "strings" + "testing" + + "github.com/hashicorp/go-hclog" +) + +func TestPanicRecorder(t *testing.T) { + rec := panics.registerPlugin("test") + + output := []string{ + "panic: test", + " stack info", + } + + for _, line := range output { + rec(line) + } + + expected := fmt.Sprintf(pluginPanicOutput, "test", strings.Join(output, "\n")) + + res := PluginPanics() + if len(res) == 0 { + t.Fatal("no output") + } + + if res[0] != expected { + t.Fatalf("expected: %q\ngot: %q", expected, res[0]) + } +} + +func TestPanicLimit(t *testing.T) { + rec := panics.registerPlugin("test") + + rec("panic: test") + + for i := 0; i < 200; i++ { + rec(fmt.Sprintf("LINE: %d", i)) + } + + res := PluginPanics() + // take the extra content into account + max := strings.Count(pluginPanicOutput, "\n") + panics.maxLines + for _, out := range res { + found := strings.Count(out, "\n") + if found > max { + t.Fatalf("expected no more than %d lines, got: %d", max, found) + } + } +} + +func TestLogPanicWrapper(t *testing.T) { + var buf bytes.Buffer + logger := hclog.NewInterceptLogger(&hclog.LoggerOptions{ + Name: "test", + Level: hclog.Debug, + Output: &buf, + DisableTime: true, + }) + + wrapped := (&logPanicWrapper{ + Logger: logger, + }).Named("test") + + wrapped.Debug("panic: invalid foo of bar") + wrapped.Debug("\tstack trace") + + expected := `[DEBUG] test.test: PANIC: invalid foo of bar +[DEBUG] test.test: stack trace +` + + got := buf.String() + + if expected != got { + t.Fatalf("Expected:\n%q\nGot:\n%q", expected, got) + } + +} diff --git a/vendor/github.com/hashicorp/terraform/internal/moduletest/assertion.go b/vendor/github.com/hashicorp/terraform/internal/moduletest/assertion.go new file mode 100644 index 00000000..007772ed --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/moduletest/assertion.go @@ -0,0 +1,66 @@ +package moduletest + +import ( + "github.com/hashicorp/terraform/tfdiags" +) + +// Assertion is the description of a single test assertion, whether +// successful or unsuccessful. +type Assertion struct { + Outcome Status + + // Description is a user-provided, human-readable description of what + // this assertion represents. + Description string + + // Message is typically relevant only for TestFailed or TestError + // assertions, giving a human-readable description of the problem, + // formatted in the way our format package expects to receive paragraphs + // for terminal word wrapping. + Message string + + // Diagnostics includes diagnostics specific to the current test assertion, + // if available. + Diagnostics tfdiags.Diagnostics +} + +// Component represents a component being tested, each of which can have +// several associated test assertions. +type Component struct { + Assertions map[string]*Assertion +} + +// Status is an enumeration of possible outcomes of a test assertion. +type Status rune + +//go:generate go run golang.org/x/tools/cmd/stringer -type=Status assertion.go + +const ( + // Pending indicates that the test was registered (during planning) + // but didn't register an outcome during apply, perhaps due to being + // blocked by some other upstream failure. + Pending Status = '?' + + // Passed indicates that the test condition succeeded. + Passed Status = 'P' + + // Failed indicates that the test condition was valid but did not + // succeed. + Failed Status = 'F' + + // Error indicates that the test condition was invalid or that the + // test report failed in some other way. + Error Status = 'E' +) + +// SuiteCanPass returns true if a suite containing an assertion with this +// status could possibly succeed. The suite as a whole succeeds only if all +// of its assertions have statuses where SuiteCanPass returns true. +func (s Status) SuiteCanPass() bool { + switch s { + case Failed, Error: + return false + default: + return true + } +} diff --git a/vendor/github.com/hashicorp/terraform/internal/moduletest/doc.go b/vendor/github.com/hashicorp/terraform/internal/moduletest/doc.go new file mode 100644 index 00000000..4b378877 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/moduletest/doc.go @@ -0,0 +1,8 @@ +// Package moduletest contains the support code for some experimental features +// we're using to evaluate strategies for having an opinionated approach to +// testing of Terraform modules. +// +// At the moment nothing in this module is considered stable, so any features +// that are usable by end-users ought to emit experiment warnings saying that +// everything is subject to change even in patch releases. +package moduletest diff --git a/vendor/github.com/hashicorp/terraform/internal/moduletest/provider.go b/vendor/github.com/hashicorp/terraform/internal/moduletest/provider.go new file mode 100644 index 00000000..12aa90f3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/moduletest/provider.go @@ -0,0 +1,568 @@ +package moduletest + +import ( + "fmt" + "log" + "sync" + + "github.com/zclconf/go-cty/cty" + "github.com/zclconf/go-cty/cty/gocty" + ctyjson "github.com/zclconf/go-cty/cty/json" + + "github.com/hashicorp/hcl/v2/hclsyntax" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/repl" + "github.com/hashicorp/terraform/tfdiags" +) + +// Provider is an implementation of providers.Interface which we're +// using as a likely-only-temporary vehicle for research on an opinionated +// module testing workflow in Terraform. +// +// We expose this to configuration as "terraform.io/builtin/test", but +// any attempt to configure it will emit a warning that it is experimental +// and likely to change or be removed entirely in future Terraform CLI +// releases. +// +// The testing provider exists to gather up test results during a Terraform +// apply operation. Its "test_results" managed resource type doesn't have any +// user-visible effect on its own, but when used in conjunction with the +// "terraform test" experimental command it is the intermediary that holds +// the test results while the test runs, so that the test command can then +// report them. +// +// For correct behavior of the assertion tracking, the "terraform test" +// command must be sure to use the same instance of Provider for both the +// plan and apply steps, so that the assertions that were planned can still +// be tracked during apply. For other commands that don't explicitly support +// test assertions, the provider will still succeed but the assertions data +// may not be complete if the apply step fails. +type Provider struct { + // components tracks all of the "component" names that have been + // used in test assertions resources so far. Each resource must have + // a unique component name. + components map[string]*Component + + // Must lock mutex in order to interact with the components map, because + // test assertions can potentially run concurrently. + mutex sync.RWMutex +} + +var _ providers.Interface = (*Provider)(nil) + +// NewProvider returns a new instance of the test provider. +func NewProvider() *Provider { + return &Provider{ + components: make(map[string]*Component), + } +} + +// TestResults returns the current record of test results tracked inside the +// provider. +// +// The result is a direct reference to the internal state of the provider, +// so the caller mustn't modify it nor store it across calls to provider +// operations. +func (p *Provider) TestResults() map[string]*Component { + return p.components +} + +// Reset returns the recieving provider back to its original state, with no +// recorded test results. +// +// It additionally detaches the instance from any data structure previously +// returned by method TestResults, freeing the caller from the constraints +// in its documentation about mutability and storage. +// +// For convenience in the presumed common case of resetting as part of +// capturing the results for storage, this method also returns the result +// that method TestResults would've returned if called prior to the call +// to Reset. +func (p *Provider) Reset() map[string]*Component { + p.mutex.Lock() + log.Print("[TRACE] moduletest.Provider: Reset") + ret := p.components + p.components = make(map[string]*Component) + p.mutex.Unlock() + return ret +} + +// GetProviderSchema returns the complete schema for the provider. +func (p *Provider) GetProviderSchema() providers.GetProviderSchemaResponse { + return providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ + "test_assertions": testAssertionsSchema, + }, + } +} + +// ValidateProviderConfig validates the provider configuration. +func (p *Provider) ValidateProviderConfig(req providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse { + // This provider has no configurable settings, so nothing to validate. + var res providers.ValidateProviderConfigResponse + return res +} + +// ConfigureProvider configures and initializes the provider. +func (p *Provider) ConfigureProvider(providers.ConfigureProviderRequest) providers.ConfigureProviderResponse { + // This provider has no configurable settings, but we use the configure + // request as an opportunity to generate a warning about it being + // experimental. + var res providers.ConfigureProviderResponse + res.Diagnostics = res.Diagnostics.Append(tfdiags.AttributeValue( + tfdiags.Warning, + "The test provider is experimental", + "The Terraform team is using the test provider (terraform.io/builtin/test) as part of ongoing research about declarative testing of Terraform modules.\n\nThe availability and behavior of this provider is expected to change significantly even in patch releases, so we recommend using this provider only in test configurations and constraining your test configurations to an exact Terraform version.", + nil, + )) + return res +} + +// ValidateResourceConfig is used to validate configuration values for a resource. +func (p *Provider) ValidateResourceConfig(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { + log.Print("[TRACE] moduletest.Provider: ValidateResourceConfig") + + var res providers.ValidateResourceConfigResponse + if req.TypeName != "test_assertions" { // we only have one resource type + res.Diagnostics = res.Diagnostics.Append(fmt.Errorf("unsupported resource type %s", req.TypeName)) + return res + } + + config := req.Config + if !config.GetAttr("component").IsKnown() { + res.Diagnostics = res.Diagnostics.Append(tfdiags.AttributeValue( + tfdiags.Error, + "Invalid component expression", + "The component name must be a static value given in the configuration, and may not be derived from a resource type attribute that will only be known during the apply step.", + cty.GetAttrPath("component"), + )) + } + if !hclsyntax.ValidIdentifier(config.GetAttr("component").AsString()) { + res.Diagnostics = res.Diagnostics.Append(tfdiags.AttributeValue( + tfdiags.Error, + "Invalid component name", + "The component name must be a valid identifier, starting with a letter followed by zero or more letters, digits, and underscores.", + cty.GetAttrPath("component"), + )) + } + for it := config.GetAttr("equal").ElementIterator(); it.Next(); { + k, obj := it.Element() + if !hclsyntax.ValidIdentifier(k.AsString()) { + res.Diagnostics = res.Diagnostics.Append(tfdiags.AttributeValue( + tfdiags.Error, + "Invalid assertion name", + "An assertion name must be a valid identifier, starting with a letter followed by zero or more letters, digits, and underscores.", + cty.GetAttrPath("equal").Index(k), + )) + } + if !obj.GetAttr("description").IsKnown() { + res.Diagnostics = res.Diagnostics.Append(tfdiags.AttributeValue( + tfdiags.Error, + "Invalid description expression", + "The description must be a static value given in the configuration, and may not be derived from a resource type attribute that will only be known during the apply step.", + cty.GetAttrPath("equal").Index(k).GetAttr("description"), + )) + } + } + for it := config.GetAttr("check").ElementIterator(); it.Next(); { + k, obj := it.Element() + if !hclsyntax.ValidIdentifier(k.AsString()) { + res.Diagnostics = res.Diagnostics.Append(tfdiags.AttributeValue( + tfdiags.Error, + "Invalid assertion name", + "An assertion name must be a valid identifier, starting with a letter followed by zero or more letters, digits, and underscores.", + cty.GetAttrPath("check").Index(k), + )) + } + if !obj.GetAttr("description").IsKnown() { + res.Diagnostics = res.Diagnostics.Append(tfdiags.AttributeValue( + tfdiags.Error, + "Invalid description expression", + "The description must be a static value given in the configuration, and may not be derived from a resource type attribute that will only be known during the apply step.", + cty.GetAttrPath("equal").Index(k).GetAttr("description"), + )) + } + } + + return res +} + +// ReadResource refreshes a resource and returns its current state. +func (p *Provider) ReadResource(req providers.ReadResourceRequest) providers.ReadResourceResponse { + log.Print("[TRACE] moduletest.Provider: ReadResource") + + var res providers.ReadResourceResponse + if req.TypeName != "test_assertions" { // we only have one resource type + res.Diagnostics = res.Diagnostics.Append(fmt.Errorf("unsupported resource type %s", req.TypeName)) + return res + } + // Test assertions are not a real remote object, so there isn't actually + // anything to refresh here. + res.NewState = req.PriorState + return res +} + +// UpgradeResourceState is called to allow the provider to adapt the raw value +// stored in the state in case the schema has changed since it was originally +// written. +func (p *Provider) UpgradeResourceState(req providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse { + log.Print("[TRACE] moduletest.Provider: UpgradeResourceState") + + var res providers.UpgradeResourceStateResponse + if req.TypeName != "test_assertions" { // we only have one resource type + res.Diagnostics = res.Diagnostics.Append(fmt.Errorf("unsupported resource type %s", req.TypeName)) + return res + } + + // We assume here that there can never be a flatmap version of this + // resource type's data, because this provider was never included in a + // version of Terraform that used flatmap and this provider's schema + // contains attributes that are not flatmap-compatible anyway. + if len(req.RawStateFlatmap) != 0 { + res.Diagnostics = res.Diagnostics.Append(fmt.Errorf("can't upgrade a flatmap state for %q", req.TypeName)) + return res + } + if req.Version != 0 { + res.Diagnostics = res.Diagnostics.Append(fmt.Errorf("the state for this %s was created by a newer version of the provider", req.TypeName)) + return res + } + + v, err := ctyjson.Unmarshal(req.RawStateJSON, testAssertionsSchema.Block.ImpliedType()) + if err != nil { + res.Diagnostics = res.Diagnostics.Append(fmt.Errorf("failed to decode state for %s: %s", req.TypeName, err)) + return res + } + + res.UpgradedState = v + return res +} + +// PlanResourceChange takes the current state and proposed state of a +// resource, and returns the planned final state. +func (p *Provider) PlanResourceChange(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { + log.Print("[TRACE] moduletest.Provider: PlanResourceChange") + + var res providers.PlanResourceChangeResponse + if req.TypeName != "test_assertions" { // we only have one resource type + res.Diagnostics = res.Diagnostics.Append(fmt.Errorf("unsupported resource type %s", req.TypeName)) + return res + } + + // During planning, our job is to gather up all of the planned test + // assertions marked as pending, which will then allow us to include + // all of them in test results even if there's a failure during apply + // that prevents the full completion of the graph walk. + // + // In a sense our plan phase is similar to the compile step for a + // test program written in another language. Planning itself can fail, + // which means we won't be able to form a complete test plan at all, + // but if we succeed in planning then subsequent problems can be treated + // as test failures at "runtime", while still keeping a full manifest + // of all of the tests that ought to have run if the apply had run to + // completion. + + proposed := req.ProposedNewState + res.PlannedState = proposed + componentName := proposed.GetAttr("component").AsString() // proven known during validate + p.mutex.Lock() + defer p.mutex.Unlock() + // NOTE: Ideally we'd do something here to verify if two assertions + // resources in the configuration attempt to declare the same component, + // but we can't actually do that because Terraform calls PlanResourceChange + // during both plan and apply, and so the second one would always fail. + // Since this is just providing a temporary pseudo-syntax for writing tests + // anyway, we'll live with this for now and aim to solve it with a future + // iteration of testing that's better integrated into the Terraform + // language. + /* + if _, exists := p.components[componentName]; exists { + res.Diagnostics = res.Diagnostics.Append(tfdiags.AttributeValue( + tfdiags.Error, + "Duplicate test component", + fmt.Sprintf("Another test_assertions resource already declared assertions for the component name %q.", componentName), + cty.GetAttrPath("component"), + )) + return res + } + */ + + component := Component{ + Assertions: make(map[string]*Assertion), + } + + for it := proposed.GetAttr("equal").ElementIterator(); it.Next(); { + k, obj := it.Element() + name := k.AsString() + if _, exists := component.Assertions[name]; exists { + // We can't actually get here in practice because so far we've + // only been pulling keys from one map, and so any duplicates + // would've been caught during config decoding, but this is here + // just to make these two blocks symmetrical to avoid mishaps in + // future refactoring/reorganization. + res.Diagnostics = res.Diagnostics.Append(tfdiags.AttributeValue( + tfdiags.Error, + "Duplicate test assertion", + fmt.Sprintf("Another assertion block in this resource already declared an assertion named %q.", name), + cty.GetAttrPath("equal").Index(k), + )) + continue + } + + var desc string + descVal := obj.GetAttr("description") + if descVal.IsNull() { + descVal = cty.StringVal("") + } + err := gocty.FromCtyValue(descVal, &desc) + if err != nil { + // We shouldn't get here because we've already validated everything + // that would make FromCtyValue fail above and during validate. + res.Diagnostics = res.Diagnostics.Append(err) + } + + component.Assertions[name] = &Assertion{ + Outcome: Pending, + Description: desc, + } + } + + for it := proposed.GetAttr("check").ElementIterator(); it.Next(); { + k, obj := it.Element() + name := k.AsString() + if _, exists := component.Assertions[name]; exists { + res.Diagnostics = res.Diagnostics.Append(tfdiags.AttributeValue( + tfdiags.Error, + "Duplicate test assertion", + fmt.Sprintf("Another assertion block in this resource already declared an assertion named %q.", name), + cty.GetAttrPath("check").Index(k), + )) + continue + } + + var desc string + descVal := obj.GetAttr("description") + if descVal.IsNull() { + descVal = cty.StringVal("") + } + err := gocty.FromCtyValue(descVal, &desc) + if err != nil { + // We shouldn't get here because we've already validated everything + // that would make FromCtyValue fail above and during validate. + res.Diagnostics = res.Diagnostics.Append(err) + } + + component.Assertions[name] = &Assertion{ + Outcome: Pending, + Description: desc, + } + } + + p.components[componentName] = &component + return res +} + +// ApplyResourceChange takes the planned state for a resource, which may +// yet contain unknown computed values, and applies the changes returning +// the final state. +func (p *Provider) ApplyResourceChange(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { + log.Print("[TRACE] moduletest.Provider: ApplyResourceChange") + + var res providers.ApplyResourceChangeResponse + if req.TypeName != "test_assertions" { // we only have one resource type + res.Diagnostics = res.Diagnostics.Append(fmt.Errorf("unsupported resource type %s", req.TypeName)) + return res + } + + // During apply we actually check the assertions and record the results. + // An assertion failure isn't reflected as an error from the apply call + // because if possible we'd like to continue exercising other objects + // downstream in case that allows us to gather more information to report. + // (If something downstream returns an error then that could prevent us + // from completing other assertions, though.) + + planned := req.PlannedState + res.NewState = planned + if res.NewState.IsNull() { + // If we're destroying then we'll just quickly return success to + // allow the test process to clean up after itself. + return res + } + componentName := planned.GetAttr("component").AsString() // proven known during validate + + p.mutex.Lock() + defer p.mutex.Unlock() + component := p.components[componentName] + if component == nil { + // We might get here when using this provider outside of the + // "terraform test" command, where there won't be any mechanism to + // preserve the test provider instance between the plan and apply + // phases. In that case, we assume that nobody will come looking to + // collect the results anyway, and so we can just silently skip + // checking. + return res + } + + for it := planned.GetAttr("equal").ElementIterator(); it.Next(); { + k, obj := it.Element() + name := k.AsString() + var desc string + if plan, exists := component.Assertions[name]; exists { + desc = plan.Description + } + assert := &Assertion{ + Outcome: Pending, + Description: desc, + } + + gotVal := obj.GetAttr("got") + wantVal := obj.GetAttr("want") + switch { + case wantVal.RawEquals(gotVal): + assert.Outcome = Passed + gotStr := repl.FormatValue(gotVal, 4) + assert.Message = fmt.Sprintf("correct value\n got: %s\n", gotStr) + default: + assert.Outcome = Failed + gotStr := repl.FormatValue(gotVal, 4) + wantStr := repl.FormatValue(wantVal, 4) + assert.Message = fmt.Sprintf("wrong value\n got: %s\n want: %s\n", gotStr, wantStr) + } + + component.Assertions[name] = assert + } + + for it := planned.GetAttr("check").ElementIterator(); it.Next(); { + k, obj := it.Element() + name := k.AsString() + var desc string + if plan, exists := component.Assertions[name]; exists { + desc = plan.Description + } + assert := &Assertion{ + Outcome: Pending, + Description: desc, + } + + condVal := obj.GetAttr("condition") + switch { + case condVal.IsNull(): + res.Diagnostics = res.Diagnostics.Append(tfdiags.AttributeValue( + tfdiags.Error, + "Invalid check condition", + "The condition value must be a boolean expression, not null.", + cty.GetAttrPath("check").Index(k).GetAttr("condition"), + )) + continue + case condVal.True(): + assert.Outcome = Passed + assert.Message = "condition passed" + default: + assert.Outcome = Failed + // For "check" we can't really return a decent error message + // because we've lost all of the context by the time we get here. + // "equal" will be better for most tests for that reason, and also + // this is one reason why in the long run it would be better for + // test assertions to be a first-class language feature rather than + // just a provider-based concept. + assert.Message = "condition failed" + } + + component.Assertions[name] = assert + } + + return res +} + +// ImportResourceState requests that the given resource be imported. +func (p *Provider) ImportResourceState(req providers.ImportResourceStateRequest) providers.ImportResourceStateResponse { + var res providers.ImportResourceStateResponse + res.Diagnostics = res.Diagnostics.Append(fmt.Errorf("%s is not importable", req.TypeName)) + return res +} + +// ValidateDataResourceConfig is used to to validate the resource configuration values. +func (p *Provider) ValidateDataResourceConfig(req providers.ValidateDataResourceConfigRequest) providers.ValidateDataResourceConfigResponse { + // This provider has no data resouce types at all. + var res providers.ValidateDataResourceConfigResponse + res.Diagnostics = res.Diagnostics.Append(fmt.Errorf("unsupported data source %s", req.TypeName)) + return res +} + +// ReadDataSource returns the data source's current state. +func (p *Provider) ReadDataSource(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { + // This provider has no data resouce types at all. + var res providers.ReadDataSourceResponse + res.Diagnostics = res.Diagnostics.Append(fmt.Errorf("unsupported data source %s", req.TypeName)) + return res +} + +// Stop is called when the provider should halt any in-flight actions. +func (p *Provider) Stop() error { + // This provider doesn't do anything that can be cancelled. + return nil +} + +// Close is a noop for this provider, since it's run in-process. +func (p *Provider) Close() error { + return nil +} + +var testAssertionsSchema = providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "component": { + Type: cty.String, + Description: "The name of the component being tested. This is just for namespacing assertions in a result report.", + DescriptionKind: configschema.StringPlain, + Required: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "equal": { + Nesting: configschema.NestingMap, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "description": { + Type: cty.String, + Description: "An optional human-readable description of what's being tested by this assertion.", + DescriptionKind: configschema.StringPlain, + Required: true, + }, + "got": { + Type: cty.DynamicPseudoType, + Description: "The actual result value generated by the relevant component.", + DescriptionKind: configschema.StringPlain, + Required: true, + }, + "want": { + Type: cty.DynamicPseudoType, + Description: "The value that the component is expected to have generated.", + DescriptionKind: configschema.StringPlain, + Required: true, + }, + }, + }, + }, + "check": { + Nesting: configschema.NestingMap, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "description": { + Type: cty.String, + Description: "An optional (but strongly recommended) human-readable description of what's being tested by this assertion.", + DescriptionKind: configschema.StringPlain, + Required: true, + }, + "condition": { + Type: cty.Bool, + Description: "An expression that must be true in order for the test to pass.", + DescriptionKind: configschema.StringPlain, + Required: true, + }, + }, + }, + }, + }, + }, +} diff --git a/vendor/github.com/hashicorp/terraform/internal/moduletest/provider_test.go b/vendor/github.com/hashicorp/terraform/internal/moduletest/provider_test.go new file mode 100644 index 00000000..33891c53 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/moduletest/provider_test.go @@ -0,0 +1,155 @@ +package moduletest + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform/providers" + "github.com/zclconf/go-cty-debug/ctydebug" + "github.com/zclconf/go-cty/cty" +) + +func TestProvider(t *testing.T) { + + assertionConfig := cty.ObjectVal(map[string]cty.Value{ + "component": cty.StringVal("spline_reticulator"), + "equal": cty.MapVal(map[string]cty.Value{ + "match": cty.ObjectVal(map[string]cty.Value{ + "description": cty.StringVal("this should match"), + "got": cty.StringVal("a"), + "want": cty.StringVal("a"), + }), + "unmatch": cty.ObjectVal(map[string]cty.Value{ + "description": cty.StringVal("this should not match"), + "got": cty.StringVal("a"), + "want": cty.StringVal("b"), + }), + }), + "check": cty.MapVal(map[string]cty.Value{ + "pass": cty.ObjectVal(map[string]cty.Value{ + "description": cty.StringVal("this should pass"), + "condition": cty.True, + }), + "fail": cty.ObjectVal(map[string]cty.Value{ + "description": cty.StringVal("this should fail"), + "condition": cty.False, + }), + }), + }) + + // The provider code expects to receive an object that was decoded from + // HCL using the schema, so to make sure we're testing a more realistic + // situation here we'll require the config to conform to the schema. If + // this fails, it's a bug in the configuration definition above rather + // than in the provider itself. + for _, err := range assertionConfig.Type().TestConformance(testAssertionsSchema.Block.ImpliedType()) { + t.Error(err) + } + + p := NewProvider() + + configureResp := p.ConfigureProvider(providers.ConfigureProviderRequest{ + Config: cty.EmptyObjectVal, + }) + if got, want := len(configureResp.Diagnostics), 1; got != want { + t.Fatalf("got %d Configure diagnostics, but want %d", got, want) + } + if got, want := configureResp.Diagnostics[0].Description().Summary, "The test provider is experimental"; got != want { + t.Fatalf("wrong diagnostic message\ngot: %s\nwant: %s", got, want) + } + + validateResp := p.ValidateResourceConfig(providers.ValidateResourceConfigRequest{ + TypeName: "test_assertions", + Config: assertionConfig, + }) + if got, want := len(validateResp.Diagnostics), 0; got != want { + t.Fatalf("got %d ValidateResourceTypeConfig diagnostics, but want %d", got, want) + } + + planResp := p.PlanResourceChange(providers.PlanResourceChangeRequest{ + TypeName: "test_assertions", + Config: assertionConfig, + PriorState: cty.NullVal(assertionConfig.Type()), + ProposedNewState: assertionConfig, + }) + if got, want := len(planResp.Diagnostics), 0; got != want { + t.Fatalf("got %d PlanResourceChange diagnostics, but want %d", got, want) + } + planned := planResp.PlannedState + if got, want := planned, assertionConfig; !want.RawEquals(got) { + t.Fatalf("wrong planned new value\n%s", ctydebug.DiffValues(want, got)) + } + + gotComponents := p.TestResults() + wantComponents := map[string]*Component{ + "spline_reticulator": { + Assertions: map[string]*Assertion{ + "pass": { + Outcome: Pending, + Description: "this should pass", + }, + "fail": { + Outcome: Pending, + Description: "this should fail", + }, + "match": { + Outcome: Pending, + Description: "this should match", + }, + "unmatch": { + Outcome: Pending, + Description: "this should not match", + }, + }, + }, + } + if diff := cmp.Diff(wantComponents, gotComponents); diff != "" { + t.Fatalf("wrong test results after planning\n%s", diff) + } + + applyResp := p.ApplyResourceChange(providers.ApplyResourceChangeRequest{ + TypeName: "test_assertions", + Config: assertionConfig, + PriorState: cty.NullVal(assertionConfig.Type()), + PlannedState: planned, + }) + if got, want := len(applyResp.Diagnostics), 0; got != want { + t.Fatalf("got %d ApplyResourceChange diagnostics, but want %d", got, want) + } + final := applyResp.NewState + if got, want := final, assertionConfig; !want.RawEquals(got) { + t.Fatalf("wrong new value\n%s", ctydebug.DiffValues(want, got)) + } + + gotComponents = p.TestResults() + wantComponents = map[string]*Component{ + "spline_reticulator": { + Assertions: map[string]*Assertion{ + "pass": { + Outcome: Passed, + Description: "this should pass", + Message: "condition passed", + }, + "fail": { + Outcome: Failed, + Description: "this should fail", + Message: "condition failed", + }, + "match": { + Outcome: Passed, + Description: "this should match", + Message: "correct value\n got: \"a\"\n", + }, + "unmatch": { + Outcome: Failed, + Description: "this should not match", + Message: "wrong value\n got: \"a\"\n want: \"b\"\n", + }, + }, + }, + } + if diff := cmp.Diff(wantComponents, gotComponents); diff != "" { + t.Fatalf("wrong test results after applying\n%s", diff) + } + +} diff --git a/vendor/github.com/hashicorp/terraform/internal/moduletest/status_string.go b/vendor/github.com/hashicorp/terraform/internal/moduletest/status_string.go new file mode 100644 index 00000000..f2249979 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/moduletest/status_string.go @@ -0,0 +1,39 @@ +// Code generated by "stringer -type=Status assertion.go"; DO NOT EDIT. + +package moduletest + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Pending-63] + _ = x[Passed-80] + _ = x[Failed-70] + _ = x[Error-69] +} + +const ( + _Status_name_0 = "Pending" + _Status_name_1 = "ErrorFailed" + _Status_name_2 = "Passed" +) + +var ( + _Status_index_1 = [...]uint8{0, 5, 11} +) + +func (i Status) String() string { + switch { + case i == 63: + return _Status_name_0 + case 69 <= i && i <= 70: + i -= 69 + return _Status_name_1[_Status_index_1[i]:_Status_index_1[i+1]] + case i == 80: + return _Status_name_2 + default: + return "Status(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/vendor/github.com/hashicorp/terraform/internal/moduletest/suite.go b/vendor/github.com/hashicorp/terraform/internal/moduletest/suite.go new file mode 100644 index 00000000..c548c923 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/moduletest/suite.go @@ -0,0 +1,7 @@ +package moduletest + +// A Suite is a set of tests run together as a single Terraform configuration. +type Suite struct { + Name string + Components map[string]*Component +} diff --git a/vendor/github.com/hashicorp/terraform/internal/provider-simple-v6/main/main.go b/vendor/github.com/hashicorp/terraform/internal/provider-simple-v6/main/main.go new file mode 100644 index 00000000..e0acf496 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/provider-simple-v6/main/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "github.com/hashicorp/terraform/internal/grpcwrap" + simple "github.com/hashicorp/terraform/internal/provider-simple-v6" + "github.com/hashicorp/terraform/internal/tfplugin6" + plugin "github.com/hashicorp/terraform/plugin6" +) + +func main() { + plugin.Serve(&plugin.ServeOpts{ + GRPCProviderFunc: func() tfplugin6.ProviderServer { + return grpcwrap.Provider6(simple.Provider()) + }, + }) +} diff --git a/vendor/github.com/hashicorp/terraform/internal/provider-simple-v6/provider.go b/vendor/github.com/hashicorp/terraform/internal/provider-simple-v6/provider.go new file mode 100644 index 00000000..4db15e5f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/provider-simple-v6/provider.go @@ -0,0 +1,128 @@ +// simple provider a minimal provider implementation for testing +package simple + +import ( + "errors" + "time" + + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/providers" + "github.com/zclconf/go-cty/cty" + ctyjson "github.com/zclconf/go-cty/cty/json" +) + +type simple struct { + schema providers.GetProviderSchemaResponse +} + +func Provider() providers.Interface { + simpleResource := providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": { + Computed: true, + Type: cty.String, + }, + "value": { + Optional: true, + Type: cty.String, + }, + }, + }, + } + + return simple{ + schema: providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: nil, + }, + ResourceTypes: map[string]providers.Schema{ + "simple_resource": simpleResource, + }, + DataSources: map[string]providers.Schema{ + "simple_resource": simpleResource, + }, + }, + } +} + +func (s simple) GetProviderSchema() providers.GetProviderSchemaResponse { + return s.schema +} + +func (s simple) ValidateProviderConfig(req providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) { + return resp +} + +func (s simple) ValidateResourceConfig(req providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) { + return resp +} + +func (s simple) ValidateDataResourceConfig(req providers.ValidateDataResourceConfigRequest) (resp providers.ValidateDataResourceConfigResponse) { + return resp +} + +func (p simple) UpgradeResourceState(req providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) { + ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType() + val, err := ctyjson.Unmarshal(req.RawStateJSON, ty) + resp.Diagnostics = resp.Diagnostics.Append(err) + resp.UpgradedState = val + return resp +} + +func (s simple) ConfigureProvider(providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { + return resp +} + +func (s simple) Stop() error { + return nil +} + +func (s simple) ReadResource(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) { + // just return the same state we received + resp.NewState = req.PriorState + return resp +} + +func (s simple) PlanResourceChange(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { + m := req.ProposedNewState.AsValueMap() + _, ok := m["id"] + if !ok { + m["id"] = cty.UnknownVal(cty.String) + } + + resp.PlannedState = cty.ObjectVal(m) + return resp +} + +func (s simple) ApplyResourceChange(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { + if req.PlannedState.IsNull() { + resp.NewState = req.PlannedState + return resp + } + + m := req.PlannedState.AsValueMap() + _, ok := m["id"] + if !ok { + m["id"] = cty.StringVal(time.Now().String()) + } + resp.NewState = cty.ObjectVal(m) + + return resp +} + +func (s simple) ImportResourceState(providers.ImportResourceStateRequest) (resp providers.ImportResourceStateResponse) { + resp.Diagnostics = resp.Diagnostics.Append(errors.New("unsupported")) + return resp +} + +func (s simple) ReadDataSource(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { + m := req.Config.AsValueMap() + m["id"] = cty.StringVal("static_id") + resp.State = cty.ObjectVal(m) + return resp +} + +func (s simple) Close() error { + return nil +} diff --git a/vendor/github.com/hashicorp/terraform/internal/provider-simple/main/main.go b/vendor/github.com/hashicorp/terraform/internal/provider-simple/main/main.go new file mode 100644 index 00000000..be0ad2ef --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/provider-simple/main/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "github.com/hashicorp/terraform/internal/grpcwrap" + simple "github.com/hashicorp/terraform/internal/provider-simple" + "github.com/hashicorp/terraform/internal/tfplugin5" + "github.com/hashicorp/terraform/plugin" +) + +func main() { + plugin.Serve(&plugin.ServeOpts{ + GRPCProviderFunc: func() tfplugin5.ProviderServer { + return grpcwrap.Provider(simple.Provider()) + }, + }) +} diff --git a/vendor/github.com/hashicorp/terraform/internal/provider-simple/provider.go b/vendor/github.com/hashicorp/terraform/internal/provider-simple/provider.go new file mode 100644 index 00000000..4db15e5f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/provider-simple/provider.go @@ -0,0 +1,128 @@ +// simple provider a minimal provider implementation for testing +package simple + +import ( + "errors" + "time" + + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/providers" + "github.com/zclconf/go-cty/cty" + ctyjson "github.com/zclconf/go-cty/cty/json" +) + +type simple struct { + schema providers.GetProviderSchemaResponse +} + +func Provider() providers.Interface { + simpleResource := providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": { + Computed: true, + Type: cty.String, + }, + "value": { + Optional: true, + Type: cty.String, + }, + }, + }, + } + + return simple{ + schema: providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: nil, + }, + ResourceTypes: map[string]providers.Schema{ + "simple_resource": simpleResource, + }, + DataSources: map[string]providers.Schema{ + "simple_resource": simpleResource, + }, + }, + } +} + +func (s simple) GetProviderSchema() providers.GetProviderSchemaResponse { + return s.schema +} + +func (s simple) ValidateProviderConfig(req providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) { + return resp +} + +func (s simple) ValidateResourceConfig(req providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) { + return resp +} + +func (s simple) ValidateDataResourceConfig(req providers.ValidateDataResourceConfigRequest) (resp providers.ValidateDataResourceConfigResponse) { + return resp +} + +func (p simple) UpgradeResourceState(req providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) { + ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType() + val, err := ctyjson.Unmarshal(req.RawStateJSON, ty) + resp.Diagnostics = resp.Diagnostics.Append(err) + resp.UpgradedState = val + return resp +} + +func (s simple) ConfigureProvider(providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { + return resp +} + +func (s simple) Stop() error { + return nil +} + +func (s simple) ReadResource(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) { + // just return the same state we received + resp.NewState = req.PriorState + return resp +} + +func (s simple) PlanResourceChange(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { + m := req.ProposedNewState.AsValueMap() + _, ok := m["id"] + if !ok { + m["id"] = cty.UnknownVal(cty.String) + } + + resp.PlannedState = cty.ObjectVal(m) + return resp +} + +func (s simple) ApplyResourceChange(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { + if req.PlannedState.IsNull() { + resp.NewState = req.PlannedState + return resp + } + + m := req.PlannedState.AsValueMap() + _, ok := m["id"] + if !ok { + m["id"] = cty.StringVal(time.Now().String()) + } + resp.NewState = cty.ObjectVal(m) + + return resp +} + +func (s simple) ImportResourceState(providers.ImportResourceStateRequest) (resp providers.ImportResourceStateResponse) { + resp.Diagnostics = resp.Diagnostics.Append(errors.New("unsupported")) + return resp +} + +func (s simple) ReadDataSource(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { + m := req.Config.AsValueMap() + m["id"] = cty.StringVal("static_id") + resp.State = cty.ObjectVal(m) + return resp +} + +func (s simple) Close() error { + return nil +} diff --git a/vendor/github.com/hashicorp/terraform/internal/provider-terraform/main/main.go b/vendor/github.com/hashicorp/terraform/internal/provider-terraform/main/main.go new file mode 100644 index 00000000..a8ad4bd4 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/provider-terraform/main/main.go @@ -0,0 +1,17 @@ +package main + +import ( + "github.com/hashicorp/terraform/builtin/providers/terraform" + "github.com/hashicorp/terraform/internal/grpcwrap" + "github.com/hashicorp/terraform/internal/tfplugin5" + "github.com/hashicorp/terraform/plugin" +) + +func main() { + // Provide a binary version of the internal terraform provider for testing + plugin.Serve(&plugin.ServeOpts{ + GRPCProviderFunc: func() tfplugin5.ProviderServer { + return grpcwrap.Provider(terraform.NewProvider()) + }, + }) +} diff --git a/vendor/github.com/hashicorp/terraform/internal/providercache/installer.go b/vendor/github.com/hashicorp/terraform/internal/providercache/installer.go index 0fb8eeae..484d3a79 100644 --- a/vendor/github.com/hashicorp/terraform/internal/providercache/installer.go +++ b/vendor/github.com/hashicorp/terraform/internal/providercache/installer.go @@ -65,6 +65,27 @@ func NewInstaller(targetDir *Dir, source getproviders.Source) *Installer { } } +// Clone returns a new Installer which has the a new target directory but +// the same optional global cache directory, the same installation sources, +// and the same built-in/unmanaged providers. The result can be mutated further +// using the various setter methods without affecting the original. +func (i *Installer) Clone(targetDir *Dir) *Installer { + // For now all of our setter methods just overwrite field values in + // their entirety, rather than mutating things on the other side of + // the shared pointers, and so we can safely just shallow-copy the + // root. We might need to be more careful here if in future we add + // methods that allow deeper mutations through the stored pointers. + ret := *i + ret.targetDir = targetDir + return &ret +} + +// ProviderSource returns the getproviders.Source that the installer would +// use for installing any new providers. +func (i *Installer) ProviderSource() getproviders.Source { + return i.source +} + // SetGlobalCacheDir activates a second tier of caching for the receiving // installer, with the given directory used as a read-through cache for // installation operations that need to retrieve new packages. @@ -82,6 +103,12 @@ func (i *Installer) SetGlobalCacheDir(cacheDir *Dir) { i.globalCacheDir = cacheDir } +// HasGlobalCacheDir returns true if someone has previously called +// SetGlobalCacheDir to configure a global cache directory for this installer. +func (i *Installer) HasGlobalCacheDir() bool { + return i.globalCacheDir != nil +} + // SetBuiltInProviderTypes tells the receiver to consider the type names in the // given slice to be valid as providers in the special special // terraform.io/builtin/... namespace that we use for providers that are @@ -304,6 +331,18 @@ NeedProvider: preferredHashes = lock.PreferredHashes() } + // If our target directory already has the provider version that fulfills the lock file, carry on + if installed := i.targetDir.ProviderVersion(provider, version); installed != nil { + if len(preferredHashes) > 0 { + if matches, _ := installed.MatchesAnyHash(preferredHashes); matches { + if cb := evts.ProviderAlreadyInstalled; cb != nil { + cb(provider, version) + } + continue + } + } + } + if i.globalCacheDir != nil { // Step 3a: If our global cache already has this version available then // we'll just link it in. @@ -385,7 +424,7 @@ NeedProvider: // implementation, so we don't worry about potentially // creating a duplicate here. newHashes = append(newHashes, newHash) - lock = locks.SetProvider(provider, version, reqs[provider], newHashes) + locks.SetProvider(provider, version, reqs[provider], newHashes) if cb := evts.LinkFromCacheSuccess; cb != nil { cb(provider, version, new.PackageDir) @@ -511,7 +550,7 @@ NeedProvider: // and so the hashes would cover only the current platform. newHashes = append(newHashes, meta.AcceptableHashes()...) } - lock = locks.SetProvider(provider, version, reqs[provider], newHashes) + locks.SetProvider(provider, version, reqs[provider], newHashes) if cb := evts.FetchPackageSuccess; cb != nil { cb(provider, version, new.PackageDir, authResult) @@ -576,5 +615,5 @@ func (err InstallerError) Error() string { providerErr := err.ProviderErrors[addr] fmt.Fprintf(&b, "- %s: %s\n", addr, providerErr) } - return b.String() + return strings.TrimSpace(b.String()) } diff --git a/vendor/github.com/hashicorp/terraform/internal/providercache/installer_events_test.go b/vendor/github.com/hashicorp/terraform/internal/providercache/installer_events_test.go new file mode 100644 index 00000000..ab703263 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/providercache/installer_events_test.go @@ -0,0 +1,184 @@ +package providercache + +import ( + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/internal/getproviders" +) + +type testInstallerEventLogItem struct { + // The name of the event that occurred, using the same names as the + // fields of InstallerEvents. + Event string + + // Most events relate to a specific provider. For the few event types + // that don't, this will be a zero-value Provider. + Provider addrs.Provider + + // The type of Args will vary by event, but it should always be something + // that can be deterministically compared using the go-cmp package. + Args interface{} +} + +// installerLogEventsForTests is a test helper that produces an InstallerEvents +// that writes event notifications (*testInstallerEventLogItem values) to +// the given channel as they occur. +// +// The caller must keep reading from the read side of the given channel +// throughout any installer operation using the returned InstallerEvents. +// It's the caller's responsibility to close the channel if needed and +// clean up any goroutines it started to process the events. +// +// The exact sequence of events emitted for an installer operation might +// change in future, if e.g. we introduce new event callbacks to the +// InstallerEvents struct. Tests using this mechanism may therefore need to +// be updated to reflect such changes. +// +// (The channel-based approach here is so that the control flow for event +// processing will belong to the caller and thus it can safely use its +// testing.T object(s) to emit log lines without non-test-case frames in the +// call stack.) +func installerLogEventsForTests(into chan<- *testInstallerEventLogItem) *InstallerEvents { + return &InstallerEvents{ + PendingProviders: func(reqs map[addrs.Provider]getproviders.VersionConstraints) { + into <- &testInstallerEventLogItem{ + Event: "PendingProviders", + Args: reqs, + } + }, + ProviderAlreadyInstalled: func(provider addrs.Provider, selectedVersion getproviders.Version) { + into <- &testInstallerEventLogItem{ + Event: "ProviderAlreadyInstalled", + Provider: provider, + Args: selectedVersion, + } + }, + BuiltInProviderAvailable: func(provider addrs.Provider) { + into <- &testInstallerEventLogItem{ + Event: "BuiltInProviderAvailable", + Provider: provider, + } + }, + BuiltInProviderFailure: func(provider addrs.Provider, err error) { + into <- &testInstallerEventLogItem{ + Event: "BuiltInProviderFailure", + Provider: provider, + Args: err.Error(), // stringified to guarantee cmp-ability + } + }, + QueryPackagesBegin: func(provider addrs.Provider, versionConstraints getproviders.VersionConstraints, locked bool) { + into <- &testInstallerEventLogItem{ + Event: "QueryPackagesBegin", + Provider: provider, + Args: struct { + Constraints string + Locked bool + }{getproviders.VersionConstraintsString(versionConstraints), locked}, + } + }, + QueryPackagesSuccess: func(provider addrs.Provider, selectedVersion getproviders.Version) { + into <- &testInstallerEventLogItem{ + Event: "QueryPackagesSuccess", + Provider: provider, + Args: selectedVersion.String(), + } + }, + QueryPackagesFailure: func(provider addrs.Provider, err error) { + into <- &testInstallerEventLogItem{ + Event: "QueryPackagesFailure", + Provider: provider, + Args: err.Error(), // stringified to guarantee cmp-ability + } + }, + QueryPackagesWarning: func(provider addrs.Provider, warns []string) { + into <- &testInstallerEventLogItem{ + Event: "QueryPackagesWarning", + Provider: provider, + Args: warns, + } + }, + LinkFromCacheBegin: func(provider addrs.Provider, version getproviders.Version, cacheRoot string) { + into <- &testInstallerEventLogItem{ + Event: "LinkFromCacheBegin", + Provider: provider, + Args: struct { + Version string + CacheRoot string + }{version.String(), cacheRoot}, + } + }, + LinkFromCacheSuccess: func(provider addrs.Provider, version getproviders.Version, localDir string) { + into <- &testInstallerEventLogItem{ + Event: "LinkFromCacheSuccess", + Provider: provider, + Args: struct { + Version string + LocalDir string + }{version.String(), localDir}, + } + }, + LinkFromCacheFailure: func(provider addrs.Provider, version getproviders.Version, err error) { + into <- &testInstallerEventLogItem{ + Event: "LinkFromCacheFailure", + Provider: provider, + Args: struct { + Version string + Error string + }{version.String(), err.Error()}, + } + }, + FetchPackageMeta: func(provider addrs.Provider, version getproviders.Version) { + into <- &testInstallerEventLogItem{ + Event: "FetchPackageMeta", + Provider: provider, + Args: version.String(), + } + }, + FetchPackageBegin: func(provider addrs.Provider, version getproviders.Version, location getproviders.PackageLocation) { + into <- &testInstallerEventLogItem{ + Event: "FetchPackageBegin", + Provider: provider, + Args: struct { + Version string + Location getproviders.PackageLocation + }{version.String(), location}, + } + }, + FetchPackageSuccess: func(provider addrs.Provider, version getproviders.Version, localDir string, authResult *getproviders.PackageAuthenticationResult) { + into <- &testInstallerEventLogItem{ + Event: "FetchPackageSuccess", + Provider: provider, + Args: struct { + Version string + LocalDir string + AuthResult string + }{version.String(), localDir, authResult.String()}, + } + }, + FetchPackageFailure: func(provider addrs.Provider, version getproviders.Version, err error) { + into <- &testInstallerEventLogItem{ + Event: "FetchPackageFailure", + Provider: provider, + Args: struct { + Version string + Error string + }{version.String(), err.Error()}, + } + }, + ProvidersFetched: func(authResults map[addrs.Provider]*getproviders.PackageAuthenticationResult) { + into <- &testInstallerEventLogItem{ + Event: "ProvidersFetched", + Args: authResults, + } + }, + HashPackageFailure: func(provider addrs.Provider, version getproviders.Version, err error) { + into <- &testInstallerEventLogItem{ + Event: "HashPackageFailure", + Provider: provider, + Args: struct { + Version string + Error string + }{version.String(), err.Error()}, + } + }, + } +} diff --git a/vendor/github.com/hashicorp/terraform/internal/providercache/installer_test.go b/vendor/github.com/hashicorp/terraform/internal/providercache/installer_test.go index 9e338fa7..2063fc1e 100644 --- a/vendor/github.com/hashicorp/terraform/internal/providercache/installer_test.go +++ b/vendor/github.com/hashicorp/terraform/internal/providercache/installer_test.go @@ -8,9 +8,13 @@ import ( "net/http" "net/http/httptest" "os" + "path/filepath" "strings" "testing" + "github.com/apparentlymart/go-versions/versions" + "github.com/apparentlymart/go-versions/versions/constraints" + "github.com/davecgh/go-spew/spew" "github.com/google/go-cmp/cmp" svchost "github.com/hashicorp/terraform-svchost" "github.com/hashicorp/terraform-svchost/disco" @@ -19,6 +23,1297 @@ import ( "github.com/hashicorp/terraform/internal/getproviders" ) +func TestEnsureProviderVersions(t *testing.T) { + // This is a sort of hybrid between table-driven and imperative-style + // testing, because the overall sequence of steps is the same for all + // of the test cases but the setup and verification have enough different + // permutations that it ends up being more concise to express them as + // normal code. + type Test struct { + Source getproviders.Source + Prepare func(*testing.T, *Installer, *Dir) + LockFile string + Reqs getproviders.Requirements + Mode InstallMode + Check func(*testing.T, *Dir, *depsfile.Locks) + WantErr string + WantEvents func(*Installer, *Dir) map[addrs.Provider][]*testInstallerEventLogItem + } + + // noProvider is just the zero value of addrs.Provider, which we're + // using in this test as the key for installer events that are not + // specific to a particular provider. + var noProvider addrs.Provider + beepProvider := addrs.MustParseProviderSourceString("example.com/foo/beep") + beepProviderDir := getproviders.PackageLocalDir("testdata/beep-provider") + fakePlatform := getproviders.Platform{OS: "bleep", Arch: "bloop"} + wrongPlatform := getproviders.Platform{OS: "wrong", Arch: "wrong"} + beepProviderHash := getproviders.HashScheme1.New("2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84=") + terraformProvider := addrs.MustParseProviderSourceString("terraform.io/builtin/terraform") + + tests := map[string]Test{ + "no dependencies": { + Mode: InstallNewProvidersOnly, + Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { + if allCached := dir.AllAvailablePackages(); len(allCached) != 0 { + t.Errorf("unexpected cache directory entries\n%s", spew.Sdump(allCached)) + } + if allLocked := locks.AllProviders(); len(allLocked) != 0 { + t.Errorf("unexpected provider lock entries\n%s", spew.Sdump(allLocked)) + } + }, + WantEvents: func(*Installer, *Dir) map[addrs.Provider][]*testInstallerEventLogItem { + return map[addrs.Provider][]*testInstallerEventLogItem{ + noProvider: { + { + Event: "PendingProviders", + Args: map[addrs.Provider]getproviders.VersionConstraints(nil), + }, + }, + } + }, + }, + "successful initial install of one provider": { + Source: getproviders.NewMockSource( + []getproviders.PackageMeta{ + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("1.0.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.0.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.1.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + }, + nil, + ), + Mode: InstallNewProvidersOnly, + Reqs: getproviders.Requirements{ + beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), + }, + Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { + if allCached := dir.AllAvailablePackages(); len(allCached) != 1 { + t.Errorf("wrong number of cache directory entries; want only one\n%s", spew.Sdump(allCached)) + } + if allLocked := locks.AllProviders(); len(allLocked) != 1 { + t.Errorf("wrong number of provider lock entries; want only one\n%s", spew.Sdump(allLocked)) + } + + gotLock := locks.Provider(beepProvider) + wantLock := depsfile.NewProviderLock( + beepProvider, + getproviders.MustParseVersion("2.1.0"), + getproviders.MustParseVersionConstraints(">= 2.0.0"), + []getproviders.Hash{beepProviderHash}, + ) + if diff := cmp.Diff(wantLock, gotLock, depsfile.ProviderLockComparer); diff != "" { + t.Errorf("wrong lock entry\n%s", diff) + } + + gotEntry := dir.ProviderLatestVersion(beepProvider) + wantEntry := &CachedProvider{ + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.1.0"), + PackageDir: filepath.Join(dir.BasePath(), "example.com/foo/beep/2.1.0/bleep_bloop"), + } + if diff := cmp.Diff(wantEntry, gotEntry); diff != "" { + t.Errorf("wrong cache entry\n%s", diff) + } + }, + WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { + return map[addrs.Provider][]*testInstallerEventLogItem{ + noProvider: { + { + Event: "PendingProviders", + Args: map[addrs.Provider]getproviders.VersionConstraints{ + beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), + }, + }, + { + Event: "ProvidersFetched", + Args: map[addrs.Provider]*getproviders.PackageAuthenticationResult{ + beepProvider: nil, + }, + }, + }, + beepProvider: { + { + Event: "QueryPackagesBegin", + Provider: beepProvider, + Args: struct { + Constraints string + Locked bool + }{">= 2.0.0", false}, + }, + { + Event: "QueryPackagesSuccess", + Provider: beepProvider, + Args: "2.1.0", + }, + { + Event: "FetchPackageMeta", + Provider: beepProvider, + Args: "2.1.0", + }, + { + Event: "FetchPackageBegin", + Provider: beepProvider, + Args: struct { + Version string + Location getproviders.PackageLocation + }{"2.1.0", beepProviderDir}, + }, + { + Event: "FetchPackageSuccess", + Provider: beepProvider, + Args: struct { + Version string + LocalDir string + AuthResult string + }{ + "2.1.0", + filepath.Join(dir.BasePath(), "example.com/foo/beep/2.1.0/bleep_bloop"), + "unauthenticated", + }, + }, + }, + } + }, + }, + "successful initial install of one provider through a cold global cache": { + Source: getproviders.NewMockSource( + []getproviders.PackageMeta{ + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.0.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.1.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + }, + nil, + ), + Prepare: func(t *testing.T, inst *Installer, dir *Dir) { + globalCacheDirPath := tmpDir(t) + globalCacheDir := NewDirWithPlatform(globalCacheDirPath, fakePlatform) + inst.SetGlobalCacheDir(globalCacheDir) + }, + Mode: InstallNewProvidersOnly, + Reqs: getproviders.Requirements{ + beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), + }, + Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { + if allCached := dir.AllAvailablePackages(); len(allCached) != 1 { + t.Errorf("wrong number of cache directory entries; want only one\n%s", spew.Sdump(allCached)) + } + if allLocked := locks.AllProviders(); len(allLocked) != 1 { + t.Errorf("wrong number of provider lock entries; want only one\n%s", spew.Sdump(allLocked)) + } + + gotLock := locks.Provider(beepProvider) + wantLock := depsfile.NewProviderLock( + beepProvider, + getproviders.MustParseVersion("2.1.0"), + getproviders.MustParseVersionConstraints(">= 2.0.0"), + []getproviders.Hash{beepProviderHash}, + ) + if diff := cmp.Diff(wantLock, gotLock, depsfile.ProviderLockComparer); diff != "" { + t.Errorf("wrong lock entry\n%s", diff) + } + + gotEntry := dir.ProviderLatestVersion(beepProvider) + wantEntry := &CachedProvider{ + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.1.0"), + PackageDir: filepath.Join(dir.BasePath(), "example.com/foo/beep/2.1.0/bleep_bloop"), + } + if diff := cmp.Diff(wantEntry, gotEntry); diff != "" { + t.Errorf("wrong cache entry\n%s", diff) + } + }, + WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { + return map[addrs.Provider][]*testInstallerEventLogItem{ + noProvider: { + { + Event: "PendingProviders", + Args: map[addrs.Provider]getproviders.VersionConstraints{ + beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), + }, + }, + { + Event: "ProvidersFetched", + Args: map[addrs.Provider]*getproviders.PackageAuthenticationResult{ + beepProvider: nil, + }, + }, + }, + beepProvider: { + { + Event: "QueryPackagesBegin", + Provider: beepProvider, + Args: struct { + Constraints string + Locked bool + }{">= 2.0.0", false}, + }, + { + Event: "QueryPackagesSuccess", + Provider: beepProvider, + Args: "2.1.0", + }, + { + Event: "FetchPackageMeta", + Provider: beepProvider, + Args: "2.1.0", + }, + { + Event: "FetchPackageBegin", + Provider: beepProvider, + Args: struct { + Version string + Location getproviders.PackageLocation + }{"2.1.0", beepProviderDir}, + }, + { + Event: "FetchPackageSuccess", + Provider: beepProvider, + Args: struct { + Version string + LocalDir string + AuthResult string + }{ + "2.1.0", + // NOTE: With global cache enabled, the fetch + // goes into the global cache dir and + // we then to it from the local cache dir. + filepath.Join(inst.globalCacheDir.BasePath(), "example.com/foo/beep/2.1.0/bleep_bloop"), + "unauthenticated", + }, + }, + }, + } + }, + }, + "successful initial install of one provider through a warm global cache": { + Source: getproviders.NewMockSource( + []getproviders.PackageMeta{ + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.0.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.1.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + }, + nil, + ), + Prepare: func(t *testing.T, inst *Installer, dir *Dir) { + globalCacheDirPath := tmpDir(t) + globalCacheDir := NewDirWithPlatform(globalCacheDirPath, fakePlatform) + _, err := globalCacheDir.InstallPackage( + context.Background(), + getproviders.PackageMeta{ + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.1.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + nil, + ) + if err != nil { + t.Fatalf("failed to populate global cache: %s", err) + } + inst.SetGlobalCacheDir(globalCacheDir) + }, + Mode: InstallNewProvidersOnly, + Reqs: getproviders.Requirements{ + beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), + }, + Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { + if allCached := dir.AllAvailablePackages(); len(allCached) != 1 { + t.Errorf("wrong number of cache directory entries; want only one\n%s", spew.Sdump(allCached)) + } + if allLocked := locks.AllProviders(); len(allLocked) != 1 { + t.Errorf("wrong number of provider lock entries; want only one\n%s", spew.Sdump(allLocked)) + } + + gotLock := locks.Provider(beepProvider) + wantLock := depsfile.NewProviderLock( + beepProvider, + getproviders.MustParseVersion("2.1.0"), + getproviders.MustParseVersionConstraints(">= 2.0.0"), + []getproviders.Hash{beepProviderHash}, + ) + if diff := cmp.Diff(wantLock, gotLock, depsfile.ProviderLockComparer); diff != "" { + t.Errorf("wrong lock entry\n%s", diff) + } + + gotEntry := dir.ProviderLatestVersion(beepProvider) + wantEntry := &CachedProvider{ + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.1.0"), + PackageDir: filepath.Join(dir.BasePath(), "example.com/foo/beep/2.1.0/bleep_bloop"), + } + if diff := cmp.Diff(wantEntry, gotEntry); diff != "" { + t.Errorf("wrong cache entry\n%s", diff) + } + }, + WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { + return map[addrs.Provider][]*testInstallerEventLogItem{ + noProvider: { + { + Event: "PendingProviders", + Args: map[addrs.Provider]getproviders.VersionConstraints{ + beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), + }, + }, + }, + beepProvider: { + { + Event: "QueryPackagesBegin", + Provider: beepProvider, + Args: struct { + Constraints string + Locked bool + }{">= 2.0.0", false}, + }, + { + Event: "QueryPackagesSuccess", + Provider: beepProvider, + Args: "2.1.0", + }, + { + Event: "LinkFromCacheBegin", + Provider: beepProvider, + Args: struct { + Version string + CacheRoot string + }{ + "2.1.0", + inst.globalCacheDir.BasePath(), + }, + }, + { + Event: "LinkFromCacheSuccess", + Provider: beepProvider, + Args: struct { + Version string + LocalDir string + }{ + "2.1.0", + filepath.Join(dir.BasePath(), "/example.com/foo/beep/2.1.0/bleep_bloop"), + }, + }, + }, + } + }, + }, + "successful reinstall of one previously-locked provider": { + Source: getproviders.NewMockSource( + []getproviders.PackageMeta{ + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("1.0.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.0.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.1.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + }, + nil, + ), + LockFile: ` + provider "example.com/foo/beep" { + version = "2.0.0" + constraints = ">= 2.0.0" + hashes = [ + "h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84=", + ] + } + `, + Mode: InstallNewProvidersOnly, + Reqs: getproviders.Requirements{ + beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), + }, + Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { + if allCached := dir.AllAvailablePackages(); len(allCached) != 1 { + t.Errorf("wrong number of cache directory entries; want only one\n%s", spew.Sdump(allCached)) + } + if allLocked := locks.AllProviders(); len(allLocked) != 1 { + t.Errorf("wrong number of provider lock entries; want only one\n%s", spew.Sdump(allLocked)) + } + + gotLock := locks.Provider(beepProvider) + wantLock := depsfile.NewProviderLock( + beepProvider, + getproviders.MustParseVersion("2.0.0"), + getproviders.MustParseVersionConstraints(">= 2.0.0"), + []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, + ) + if diff := cmp.Diff(wantLock, gotLock, depsfile.ProviderLockComparer); diff != "" { + t.Errorf("wrong lock entry\n%s", diff) + } + + gotEntry := dir.ProviderLatestVersion(beepProvider) + wantEntry := &CachedProvider{ + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.0.0"), + PackageDir: filepath.Join(dir.BasePath(), "example.com/foo/beep/2.0.0/bleep_bloop"), + } + if diff := cmp.Diff(wantEntry, gotEntry); diff != "" { + t.Errorf("wrong cache entry\n%s", diff) + } + }, + WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { + return map[addrs.Provider][]*testInstallerEventLogItem{ + noProvider: { + { + Event: "PendingProviders", + Args: map[addrs.Provider]getproviders.VersionConstraints{ + beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), + }, + }, + { + Event: "ProvidersFetched", + Args: map[addrs.Provider]*getproviders.PackageAuthenticationResult{ + beepProvider: nil, + }, + }, + }, + beepProvider: { + { + Event: "QueryPackagesBegin", + Provider: beepProvider, + Args: struct { + Constraints string + Locked bool + }{">= 2.0.0", true}, + }, + { + Event: "QueryPackagesSuccess", + Provider: beepProvider, + Args: "2.0.0", + }, + { + Event: "FetchPackageMeta", + Provider: beepProvider, + Args: "2.0.0", + }, + { + Event: "FetchPackageBegin", + Provider: beepProvider, + Args: struct { + Version string + Location getproviders.PackageLocation + }{"2.0.0", beepProviderDir}, + }, + { + Event: "FetchPackageSuccess", + Provider: beepProvider, + Args: struct { + Version string + LocalDir string + AuthResult string + }{ + "2.0.0", + filepath.Join(dir.BasePath(), "example.com/foo/beep/2.0.0/bleep_bloop"), + "unauthenticated", + }, + }, + }, + } + }, + }, + "skipped install of one previously-locked and installed provider": { + Source: getproviders.NewMockSource( + []getproviders.PackageMeta{ + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.0.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + }, + nil, + ), + LockFile: ` + provider "example.com/foo/beep" { + version = "2.0.0" + constraints = ">= 2.0.0" + hashes = [ + "h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84=", + ] + } + `, + Prepare: func(t *testing.T, inst *Installer, dir *Dir) { + _, err := dir.InstallPackage( + context.Background(), + getproviders.PackageMeta{ + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.0.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + nil, + ) + if err != nil { + t.Fatalf("installation to the test dir failed: %s", err) + } + }, + Mode: InstallNewProvidersOnly, + Reqs: getproviders.Requirements{ + beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), + }, + Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { + if allCached := dir.AllAvailablePackages(); len(allCached) != 1 { + t.Errorf("wrong number of cache directory entries; want only one\n%s", spew.Sdump(allCached)) + } + if allLocked := locks.AllProviders(); len(allLocked) != 1 { + t.Errorf("wrong number of provider lock entries; want only one\n%s", spew.Sdump(allLocked)) + } + + gotLock := locks.Provider(beepProvider) + wantLock := depsfile.NewProviderLock( + beepProvider, + getproviders.MustParseVersion("2.0.0"), + getproviders.MustParseVersionConstraints(">= 2.0.0"), + []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, + ) + if diff := cmp.Diff(wantLock, gotLock, depsfile.ProviderLockComparer); diff != "" { + t.Errorf("wrong lock entry\n%s", diff) + } + + gotEntry := dir.ProviderLatestVersion(beepProvider) + wantEntry := &CachedProvider{ + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.0.0"), + PackageDir: filepath.Join(dir.BasePath(), "example.com/foo/beep/2.0.0/bleep_bloop"), + } + if diff := cmp.Diff(wantEntry, gotEntry); diff != "" { + t.Errorf("wrong cache entry\n%s", diff) + } + }, + WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { + return map[addrs.Provider][]*testInstallerEventLogItem{ + noProvider: { + { + Event: "PendingProviders", + Args: map[addrs.Provider]getproviders.VersionConstraints{ + beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), + }, + }, + }, + beepProvider: { + { + Event: "QueryPackagesBegin", + Provider: beepProvider, + Args: struct { + Constraints string + Locked bool + }{">= 2.0.0", true}, + }, + { + Event: "QueryPackagesSuccess", + Provider: beepProvider, + Args: "2.0.0", + }, + { + Event: "ProviderAlreadyInstalled", + Provider: beepProvider, + Args: versions.Version{Major: 2, Minor: 0, Patch: 0}, + }, + }, + } + }, + }, + "successful upgrade of one previously-locked provider": { + Source: getproviders.NewMockSource( + []getproviders.PackageMeta{ + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("1.0.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.0.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.1.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + }, + nil, + ), + LockFile: ` + provider "example.com/foo/beep" { + version = "2.0.0" + constraints = ">= 2.0.0" + hashes = [ + "h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84=", + ] + } + `, + Mode: InstallUpgrades, + Reqs: getproviders.Requirements{ + beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), + }, + Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { + if allCached := dir.AllAvailablePackages(); len(allCached) != 1 { + t.Errorf("wrong number of cache directory entries; want only one\n%s", spew.Sdump(allCached)) + } + if allLocked := locks.AllProviders(); len(allLocked) != 1 { + t.Errorf("wrong number of provider lock entries; want only one\n%s", spew.Sdump(allLocked)) + } + + gotLock := locks.Provider(beepProvider) + wantLock := depsfile.NewProviderLock( + beepProvider, + getproviders.MustParseVersion("2.1.0"), + getproviders.MustParseVersionConstraints(">= 2.0.0"), + []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, + ) + if diff := cmp.Diff(wantLock, gotLock, depsfile.ProviderLockComparer); diff != "" { + t.Errorf("wrong lock entry\n%s", diff) + } + + gotEntry := dir.ProviderLatestVersion(beepProvider) + wantEntry := &CachedProvider{ + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.1.0"), + PackageDir: filepath.Join(dir.BasePath(), "example.com/foo/beep/2.1.0/bleep_bloop"), + } + if diff := cmp.Diff(wantEntry, gotEntry); diff != "" { + t.Errorf("wrong cache entry\n%s", diff) + } + }, + WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { + return map[addrs.Provider][]*testInstallerEventLogItem{ + noProvider: { + { + Event: "PendingProviders", + Args: map[addrs.Provider]getproviders.VersionConstraints{ + beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), + }, + }, + { + Event: "ProvidersFetched", + Args: map[addrs.Provider]*getproviders.PackageAuthenticationResult{ + beepProvider: nil, + }, + }, + }, + beepProvider: { + { + Event: "QueryPackagesBegin", + Provider: beepProvider, + Args: struct { + Constraints string + Locked bool + }{">= 2.0.0", false}, + }, + { + Event: "QueryPackagesSuccess", + Provider: beepProvider, + Args: "2.1.0", + }, + { + Event: "FetchPackageMeta", + Provider: beepProvider, + Args: "2.1.0", + }, + { + Event: "FetchPackageBegin", + Provider: beepProvider, + Args: struct { + Version string + Location getproviders.PackageLocation + }{"2.1.0", beepProviderDir}, + }, + { + Event: "FetchPackageSuccess", + Provider: beepProvider, + Args: struct { + Version string + LocalDir string + AuthResult string + }{ + "2.1.0", + filepath.Join(dir.BasePath(), "example.com/foo/beep/2.1.0/bleep_bloop"), + "unauthenticated", + }, + }, + }, + } + }, + }, + "successful install of a built-in provider": { + Source: getproviders.NewMockSource( + []getproviders.PackageMeta{}, + nil, + ), + Prepare: func(t *testing.T, inst *Installer, dir *Dir) { + inst.SetBuiltInProviderTypes([]string{"terraform"}) + }, + Mode: InstallNewProvidersOnly, + Reqs: getproviders.Requirements{ + terraformProvider: nil, + }, + Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { + // Built-in providers are neither included in the cache + // directory nor mentioned in the lock file, because they + // are compiled directly into the Terraform executable. + if allCached := dir.AllAvailablePackages(); len(allCached) != 0 { + t.Errorf("wrong number of cache directory entries; want none\n%s", spew.Sdump(allCached)) + } + if allLocked := locks.AllProviders(); len(allLocked) != 0 { + t.Errorf("wrong number of provider lock entries; want none\n%s", spew.Sdump(allLocked)) + } + }, + WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { + return map[addrs.Provider][]*testInstallerEventLogItem{ + noProvider: { + { + Event: "PendingProviders", + Args: map[addrs.Provider]getproviders.VersionConstraints{ + terraformProvider: constraints.IntersectionSpec(nil), + }, + }, + }, + terraformProvider: { + { + Event: "BuiltInProviderAvailable", + Provider: terraformProvider, + }, + }, + } + }, + }, + "failed install of a non-existing built-in provider": { + Source: getproviders.NewMockSource( + []getproviders.PackageMeta{}, + nil, + ), + Prepare: func(t *testing.T, inst *Installer, dir *Dir) { + // NOTE: We're intentionally not calling + // inst.SetBuiltInProviderTypes to make the "terraform" + // built-in provider available here, so requests for it + // should fail. + }, + Mode: InstallNewProvidersOnly, + Reqs: getproviders.Requirements{ + terraformProvider: nil, + }, + WantErr: `some providers could not be installed: +- terraform.io/builtin/terraform: this Terraform release has no built-in provider named "terraform"`, + WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { + return map[addrs.Provider][]*testInstallerEventLogItem{ + noProvider: { + { + Event: "PendingProviders", + Args: map[addrs.Provider]getproviders.VersionConstraints{ + terraformProvider: constraints.IntersectionSpec(nil), + }, + }, + }, + terraformProvider: { + { + Event: "BuiltInProviderFailure", + Provider: terraformProvider, + Args: `this Terraform release has no built-in provider named "terraform"`, + }, + }, + } + }, + }, + "failed install when a built-in provider has a version constraint": { + Source: getproviders.NewMockSource( + []getproviders.PackageMeta{}, + nil, + ), + Prepare: func(t *testing.T, inst *Installer, dir *Dir) { + inst.SetBuiltInProviderTypes([]string{"terraform"}) + }, + Mode: InstallNewProvidersOnly, + Reqs: getproviders.Requirements{ + terraformProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), + }, + WantErr: `some providers could not be installed: +- terraform.io/builtin/terraform: built-in providers do not support explicit version constraints`, + WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { + return map[addrs.Provider][]*testInstallerEventLogItem{ + noProvider: { + { + Event: "PendingProviders", + Args: map[addrs.Provider]getproviders.VersionConstraints{ + terraformProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), + }, + }, + }, + terraformProvider: { + { + Event: "BuiltInProviderFailure", + Provider: terraformProvider, + Args: `built-in providers do not support explicit version constraints`, + }, + }, + } + }, + }, + "locked version is excluded by new version constraint": { + Source: getproviders.NewMockSource( + []getproviders.PackageMeta{ + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("1.0.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.0.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + }, + nil, + ), + LockFile: ` + provider "example.com/foo/beep" { + version = "1.0.0" + constraints = ">= 1.0.0" + hashes = [ + "h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84=", + ] + } + `, + Mode: InstallNewProvidersOnly, + Reqs: getproviders.Requirements{ + beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), + }, + Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { + if allCached := dir.AllAvailablePackages(); len(allCached) != 0 { + t.Errorf("wrong number of cache directory entries; want none\n%s", spew.Sdump(allCached)) + } + if allLocked := locks.AllProviders(); len(allLocked) != 1 { + t.Errorf("wrong number of provider lock entries; want only one\n%s", spew.Sdump(allLocked)) + } + + gotLock := locks.Provider(beepProvider) + wantLock := depsfile.NewProviderLock( + beepProvider, + getproviders.MustParseVersion("1.0.0"), + getproviders.MustParseVersionConstraints(">= 1.0.0"), + []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, + ) + if diff := cmp.Diff(wantLock, gotLock, depsfile.ProviderLockComparer); diff != "" { + t.Errorf("wrong lock entry\n%s", diff) + } + }, + WantErr: `some providers could not be installed: +- example.com/foo/beep: locked provider example.com/foo/beep 1.0.0 does not match configured version constraint >= 2.0.0; must use terraform init -upgrade to allow selection of new versions`, + WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { + return map[addrs.Provider][]*testInstallerEventLogItem{ + noProvider: { + { + Event: "PendingProviders", + Args: map[addrs.Provider]getproviders.VersionConstraints{ + beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), + }, + }, + }, + beepProvider: { + { + Event: "QueryPackagesBegin", + Provider: beepProvider, + Args: struct { + Constraints string + Locked bool + }{">= 2.0.0", true}, + }, + { + Event: "QueryPackagesFailure", + Provider: beepProvider, + Args: `locked provider example.com/foo/beep 1.0.0 does not match configured version constraint >= 2.0.0; must use terraform init -upgrade to allow selection of new versions`, + }, + }, + } + }, + }, + "locked version is no longer available": { + Source: getproviders.NewMockSource( + []getproviders.PackageMeta{ + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("1.0.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("2.0.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + }, + nil, + ), + LockFile: ` + provider "example.com/foo/beep" { + version = "1.2.0" + constraints = ">= 1.0.0" + hashes = [ + "h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84=", + ] + } + `, + Mode: InstallNewProvidersOnly, + Reqs: getproviders.Requirements{ + beepProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), + }, + Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { + if allCached := dir.AllAvailablePackages(); len(allCached) != 0 { + t.Errorf("wrong number of cache directory entries; want none\n%s", spew.Sdump(allCached)) + } + if allLocked := locks.AllProviders(); len(allLocked) != 1 { + t.Errorf("wrong number of provider lock entries; want only one\n%s", spew.Sdump(allLocked)) + } + + gotLock := locks.Provider(beepProvider) + wantLock := depsfile.NewProviderLock( + beepProvider, + getproviders.MustParseVersion("1.2.0"), + getproviders.MustParseVersionConstraints(">= 1.0.0"), + []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, + ) + if diff := cmp.Diff(wantLock, gotLock, depsfile.ProviderLockComparer); diff != "" { + t.Errorf("wrong lock entry\n%s", diff) + } + }, + WantErr: `some providers could not be installed: +- example.com/foo/beep: the previously-selected version 1.2.0 is no longer available`, + WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { + return map[addrs.Provider][]*testInstallerEventLogItem{ + noProvider: { + { + Event: "PendingProviders", + Args: map[addrs.Provider]getproviders.VersionConstraints{ + beepProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), + }, + }, + }, + beepProvider: { + { + Event: "QueryPackagesBegin", + Provider: beepProvider, + Args: struct { + Constraints string + Locked bool + }{">= 1.0.0", true}, + }, + { + Event: "QueryPackagesFailure", + Provider: beepProvider, + Args: `the previously-selected version 1.2.0 is no longer available`, + }, + }, + } + }, + }, + "no versions match the version constraint": { + Source: getproviders.NewMockSource( + []getproviders.PackageMeta{ + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("1.0.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + }, + nil, + ), + Mode: InstallNewProvidersOnly, + Reqs: getproviders.Requirements{ + beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), + }, + WantErr: `some providers could not be installed: +- example.com/foo/beep: no available releases match the given constraints >= 2.0.0`, + WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { + return map[addrs.Provider][]*testInstallerEventLogItem{ + noProvider: { + { + Event: "PendingProviders", + Args: map[addrs.Provider]getproviders.VersionConstraints{ + beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), + }, + }, + }, + beepProvider: { + { + Event: "QueryPackagesBegin", + Provider: beepProvider, + Args: struct { + Constraints string + Locked bool + }{">= 2.0.0", false}, + }, + { + Event: "QueryPackagesFailure", + Provider: beepProvider, + Args: `no available releases match the given constraints >= 2.0.0`, + }, + }, + } + }, + }, + "version exists but doesn't support the current platform": { + Source: getproviders.NewMockSource( + []getproviders.PackageMeta{ + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("1.0.0"), + TargetPlatform: wrongPlatform, + Location: beepProviderDir, + }, + }, + nil, + ), + Mode: InstallNewProvidersOnly, + Reqs: getproviders.Requirements{ + beepProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), + }, + WantErr: `some providers could not be installed: +- example.com/foo/beep: provider example.com/foo/beep 1.0.0 is not available for bleep_bloop`, + WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { + return map[addrs.Provider][]*testInstallerEventLogItem{ + noProvider: { + { + Event: "PendingProviders", + Args: map[addrs.Provider]getproviders.VersionConstraints{ + beepProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), + }, + }, + }, + beepProvider: { + { + Event: "QueryPackagesBegin", + Provider: beepProvider, + Args: struct { + Constraints string + Locked bool + }{">= 1.0.0", false}, + }, + { + Event: "QueryPackagesSuccess", + Provider: beepProvider, + Args: "1.0.0", + }, + { + Event: "FetchPackageMeta", + Provider: beepProvider, + Args: "1.0.0", + }, + { + Event: "FetchPackageFailure", + Provider: beepProvider, + Args: struct { + Version string + Error string + }{ + "1.0.0", + "provider example.com/foo/beep 1.0.0 is not available for bleep_bloop", + }, + }, + }, + } + }, + }, + "available package doesn't match locked hash": { + Source: getproviders.NewMockSource( + []getproviders.PackageMeta{ + { + Provider: beepProvider, + Version: getproviders.MustParseVersion("1.0.0"), + TargetPlatform: fakePlatform, + Location: beepProviderDir, + }, + }, + nil, + ), + LockFile: ` + provider "example.com/foo/beep" { + version = "1.0.0" + constraints = ">= 1.0.0" + hashes = [ + "h1:does-not-match", + ] + } + `, + Mode: InstallNewProvidersOnly, + Reqs: getproviders.Requirements{ + beepProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), + }, + WantErr: `some providers could not be installed: +- example.com/foo/beep: the local package for example.com/foo/beep 1.0.0 doesn't match any of the checksums previously recorded in the dependency lock file (this might be because the available checksums are for packages targeting different platforms)`, + WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { + return map[addrs.Provider][]*testInstallerEventLogItem{ + noProvider: { + { + Event: "PendingProviders", + Args: map[addrs.Provider]getproviders.VersionConstraints{ + beepProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), + }, + }, + }, + beepProvider: { + { + Event: "QueryPackagesBegin", + Provider: beepProvider, + Args: struct { + Constraints string + Locked bool + }{">= 1.0.0", true}, + }, + { + Event: "QueryPackagesSuccess", + Provider: beepProvider, + Args: "1.0.0", + }, + { + Event: "FetchPackageMeta", + Provider: beepProvider, + Args: "1.0.0", + }, + { + Event: "FetchPackageBegin", + Provider: beepProvider, + Args: struct { + Version string + Location getproviders.PackageLocation + }{"1.0.0", beepProviderDir}, + }, + { + Event: "FetchPackageFailure", + Provider: beepProvider, + Args: struct { + Version string + Error string + }{ + "1.0.0", + `the local package for example.com/foo/beep 1.0.0 doesn't match any of the checksums previously recorded in the dependency lock file (this might be because the available checksums are for packages targeting different platforms)`, + }, + }, + }, + } + }, + }, + } + + ctx := context.Background() + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + if test.Check == nil && test.WantEvents == nil && test.WantErr == "" { + t.Fatalf("invalid test: must set at least one of Check, WantEvents, or WantErr") + } + + outputDir := NewDirWithPlatform(tmpDir(t), fakePlatform) + source := test.Source + if source == nil { + source = getproviders.NewMockSource(nil, nil) + } + inst := NewInstaller(outputDir, source) + if test.Prepare != nil { + test.Prepare(t, inst, outputDir) + } + + locks, lockDiags := depsfile.LoadLocksFromBytes([]byte(test.LockFile), "test.lock.hcl") + if lockDiags.HasErrors() { + t.Fatalf("invalid lock file: %s", lockDiags.Err().Error()) + } + + providerEvents := make(map[addrs.Provider][]*testInstallerEventLogItem) + eventsCh := make(chan *testInstallerEventLogItem) + var newLocks *depsfile.Locks + var instErr error + go func(ch chan *testInstallerEventLogItem) { + events := installerLogEventsForTests(ch) + ctx := events.OnContext(ctx) + newLocks, instErr = inst.EnsureProviderVersions(ctx, locks, test.Reqs, test.Mode) + close(eventsCh) // exits the event loop below + }(eventsCh) + for evt := range eventsCh { + // We do the event collection in the main goroutine, rather than + // running the installer itself in the main goroutine, so that + // we can safely t.Log in here without violating the testing.T + // usage rules. + if evt.Provider == (addrs.Provider{}) { + t.Logf("%s(%s)", evt.Event, spew.Sdump(evt.Args)) + } else { + t.Logf("%s: %s(%s)", evt.Provider, evt.Event, spew.Sdump(evt.Args)) + } + providerEvents[evt.Provider] = append(providerEvents[evt.Provider], evt) + } + + if test.WantErr != "" { + if instErr == nil { + t.Errorf("succeeded; want error\nwant: %s", test.WantErr) + } else if got, want := instErr.Error(), test.WantErr; got != want { + t.Errorf("wrong error\ngot: %s\nwant: %s", got, want) + } + } else if instErr != nil { + t.Errorf("unexpected error\ngot: %s", instErr.Error()) + } + + if test.Check != nil { + test.Check(t, outputDir, newLocks) + } + + if test.WantEvents != nil { + wantEvents := test.WantEvents(inst, outputDir) + if diff := cmp.Diff(wantEvents, providerEvents); diff != "" { + t.Errorf("wrong installer events\n%s", diff) + } + } + }) + } +} + func TestEnsureProviderVersions_local_source(t *testing.T) { // create filesystem source using the test provider cache dir source := getproviders.NewFilesystemMirrorSource("testdata/cachedir") @@ -437,3 +1732,14 @@ func fakeRegistryHandler(resp http.ResponseWriter, req *http.Request) { resp.WriteHeader(404) resp.Write([]byte(`unrecognized path scheme`)) } + +// In order to be able to compare the recorded temp dir paths, we need to +// normalize the path to match what the installer would report. +func tmpDir(t *testing.T) string { + d := t.TempDir() + unlinked, err := filepath.EvalSymlinks(d) + if err != nil { + t.Fatal(err) + } + return filepath.Clean(unlinked) +} diff --git a/vendor/github.com/hashicorp/terraform/internal/providercache/package_install.go b/vendor/github.com/hashicorp/terraform/internal/providercache/package_install.go index ca8a3073..57b38888 100644 --- a/vendor/github.com/hashicorp/terraform/internal/providercache/package_install.go +++ b/vendor/github.com/hashicorp/terraform/internal/providercache/package_install.go @@ -12,7 +12,6 @@ import ( "github.com/hashicorp/terraform/httpclient" "github.com/hashicorp/terraform/internal/copy" - copydir "github.com/hashicorp/terraform/internal/copy" "github.com/hashicorp/terraform/internal/getproviders" ) @@ -126,7 +125,7 @@ func installFromLocalArchive(ctx context.Context, meta getproviders.PackageMeta, filename := meta.Location.String() - err := unzip.Decompress(targetDir, filename, true) + err := unzip.Decompress(targetDir, filename, true, 0000) if err != nil { return authResult, err } @@ -154,12 +153,58 @@ func installFromLocalDir(ctx context.Context, meta getproviders.PackageMeta, tar // these two paths are not pointing at the same physical directory on // disk. This compares the files by their OS-level device and directory // entry identifiers, not by their virtual filesystem paths. - if same, err := copydir.SameFile(absNew, absCurrent); same { + if same, err := copy.SameFile(absNew, absCurrent); same { return nil, fmt.Errorf("cannot install existing provider directory %s to itself", targetDir) } else if err != nil { return nil, fmt.Errorf("failed to determine if %s and %s are the same: %s", sourceDir, targetDir, err) } + var authResult *getproviders.PackageAuthenticationResult + if meta.Authentication != nil { + // (we have this here for completeness but note that local filesystem + // mirrors typically don't include enough information for package + // authentication and so we'll rarely get in here in practice.) + var err error + if authResult, err = meta.Authentication.AuthenticatePackage(meta.Location); err != nil { + return nil, err + } + } + + // If the caller provided at least one hash in allowedHashes then at + // least one of those hashes ought to match. However, for local directories + // in particular we can't actually verify the legacy "zh:" hash scheme + // because it requires access to the original .zip archive, and so as a + // measure of pragmatism we'll treat a set of hashes where all are "zh:" + // the same as no hashes at all, and let anything pass. This is definitely + // non-ideal but accepted for two reasons: + // - Packages we find on local disk can be considered a little more trusted + // than packages coming from over the network, because we assume that + // they were either placed intentionally by an operator or they were + // automatically installed by a previous network operation that would've + // itself verified the hashes. + // - Our installer makes a concerted effort to record at least one new-style + // hash for each lock entry, so we should very rarely end up in this + // situation anyway. + suitableHashCount := 0 + for _, hash := range allowedHashes { + if !hash.HasScheme(getproviders.HashSchemeZip) { + suitableHashCount++ + } + } + if suitableHashCount > 0 { + if matches, err := meta.MatchesAnyHash(allowedHashes); err != nil { + return authResult, fmt.Errorf( + "failed to calculate checksum for %s %s package at %s: %s", + meta.Provider, meta.Version, meta.Location, err, + ) + } else if !matches { + return authResult, fmt.Errorf( + "the local package for %s %s doesn't match any of the checksums previously recorded in the dependency lock file (this might be because the available checksums are for packages targeting different platforms)", + meta.Provider, meta.Version, + ) + } + } + // Delete anything that's already present at this path first. err = os.RemoveAll(targetDir) if err != nil && !os.IsNotExist(err) { diff --git a/vendor/github.com/hashicorp/terraform/internal/providercache/testdata/beep-provider/terraform-provider-beep b/vendor/github.com/hashicorp/terraform/internal/providercache/testdata/beep-provider/terraform-provider-beep new file mode 100644 index 00000000..e0841fd8 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/providercache/testdata/beep-provider/terraform-provider-beep @@ -0,0 +1,2 @@ +This is not a real provider executable. It's just here to give the installer +something to copy in some of our installer test cases. diff --git a/vendor/github.com/hashicorp/terraform/internal/providercache/testdata/cachedir/registry.terraform.io/hashicorp/null/2.0.0/windows_amd64/terraform-provider-null.exe b/vendor/github.com/hashicorp/terraform/internal/providercache/testdata/cachedir/registry.terraform.io/hashicorp/null/2.0.0/windows_amd64/terraform-provider-null.exe new file mode 100644 index 00000000..daa9e350 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/providercache/testdata/cachedir/registry.terraform.io/hashicorp/null/2.0.0/windows_amd64/terraform-provider-null.exe @@ -0,0 +1 @@ +# This is just a placeholder file for discovery testing, not a real provider plugin. diff --git a/vendor/github.com/hashicorp/terraform/internal/provisioner-local-exec/main/main.go b/vendor/github.com/hashicorp/terraform/internal/provisioner-local-exec/main/main.go new file mode 100644 index 00000000..86a6f07f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/provisioner-local-exec/main/main.go @@ -0,0 +1,17 @@ +package main + +import ( + localexec "github.com/hashicorp/terraform/builtin/provisioners/local-exec" + "github.com/hashicorp/terraform/internal/grpcwrap" + "github.com/hashicorp/terraform/internal/tfplugin5" + "github.com/hashicorp/terraform/plugin" +) + +func main() { + // Provide a binary version of the internal terraform provider for testing + plugin.Serve(&plugin.ServeOpts{ + GRPCProvisionerFunc: func() tfplugin5.ProvisionerServer { + return grpcwrap.Provisioner(localexec.New()) + }, + }) +} diff --git a/vendor/github.com/hashicorp/terraform/internal/terminal/impl_others.go b/vendor/github.com/hashicorp/terraform/internal/terminal/impl_others.go new file mode 100644 index 00000000..fc819aee --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/terminal/impl_others.go @@ -0,0 +1,53 @@ +// +build !windows + +package terminal + +import ( + "os" + + "golang.org/x/term" +) + +// This is the implementation for all operating systems except Windows, where +// we don't expect to need to do any special initialization to get a working +// Virtual Terminal. +// +// For this implementation we just delegate everything upstream to +// golang.org/x/term, since it already has a variety of different +// implementations for quirks of more esoteric operating systems like plan9, +// and will hopefully grow to include others as Go is ported to other platforms +// in future. +// +// For operating systems that golang.org/x/term doesn't support either, it +// defaults to indicating that nothing is a terminal and returns an error when +// asked for a size, which we'll handle below. + +func configureOutputHandle(f *os.File) (*OutputStream, error) { + return &OutputStream{ + File: f, + isTerminal: isTerminalGolangXTerm, + getColumns: getColumnsGolangXTerm, + }, nil +} + +func configureInputHandle(f *os.File) (*InputStream, error) { + return &InputStream{ + File: f, + isTerminal: isTerminalGolangXTerm, + }, nil +} + +func isTerminalGolangXTerm(f *os.File) bool { + return term.IsTerminal(int(f.Fd())) +} + +func getColumnsGolangXTerm(f *os.File) int { + width, _, err := term.GetSize(int(f.Fd())) + if err != nil { + // Suggests that it's either not a terminal at all or that we're on + // a platform that golang.org/x/term doesn't support. In both cases + // we'll just return the placeholder default value. + return defaultColumns + } + return width +} diff --git a/vendor/github.com/hashicorp/terraform/internal/terminal/impl_windows.go b/vendor/github.com/hashicorp/terraform/internal/terminal/impl_windows.go new file mode 100644 index 00000000..f6aaa78b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/terminal/impl_windows.go @@ -0,0 +1,161 @@ +// +build windows + +package terminal + +import ( + "fmt" + "os" + "syscall" + + "golang.org/x/sys/windows" + + // We're continuing to use this third-party library on Windows because it + // has the additional IsCygwinTerminal function, which includes some useful + // heuristics for recognizing when a pipe seems to be connected to a + // legacy terminal emulator on Windows versions that lack true pty support. + // We now use golang.org/x/term's functionality on other platforms. + isatty "github.com/mattn/go-isatty" +) + +func configureOutputHandle(f *os.File) (*OutputStream, error) { + ret := &OutputStream{ + File: f, + } + + if fd := f.Fd(); isatty.IsTerminal(fd) { + // We have a few things to deal with here: + // - Activating UTF-8 output support (mandatory) + // - Activating virtual terminal support (optional) + // These will not succeed on Windows 8 or early versions of Windows 10. + + // UTF-8 support means switching the console "code page" to CP_UTF8. + // Notice that this doesn't take the specific file descriptor, because + // the console is just ambiently associated with our process. + err := SetConsoleOutputCP(CP_UTF8) + if err != nil { + return nil, fmt.Errorf("failed to set the console to UTF-8 mode; you may need to use a newer version of Windows: %s", err) + } + + // If the console also allows us to turn on + // ENABLE_VIRTUAL_TERMINAL_PROCESSING then we can potentially use VT + // output, although the methods of Settings will make the final + // determination on that because we might have some handles pointing at + // terminals and other handles pointing at files/pipes. + ret.getColumns = getColumnsWindowsConsole + var mode uint32 + err = windows.GetConsoleMode(windows.Handle(fd), &mode) + if err != nil { + return ret, nil // We'll treat this as success but without VT support + } + mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING + err = windows.SetConsoleMode(windows.Handle(fd), mode) + if err != nil { + return ret, nil // We'll treat this as success but without VT support + } + + // If we get here then we've successfully turned on VT processing, so + // we can return an OutputStream that answers true when asked if it + // is a Terminal. + ret.isTerminal = staticTrue + return ret, nil + + } else if isatty.IsCygwinTerminal(fd) { + // Cygwin terminals -- and other VT100 "fakers" for older versions of + // Windows -- are not really terminals in the usual sense, but rather + // are pipes between the child process (Terraform) and the terminal + // emulator. isatty.IsCygwinTerminal uses some heuristics to + // distinguish those pipes from other pipes we might see if the user + // were, for example, using the | operator on the command line. + // If we get in here then we'll assume that we can send VT100 sequences + // to this stream, even though it isn't a terminal in the usual sense. + + ret.isTerminal = staticTrue + // TODO: Is it possible to detect the width of these fake terminals? + return ret, nil + } + + // If we fall out here then we have a non-terminal filehandle, so we'll + // just accept all of the default OutputStream behaviors + return ret, nil +} + +func configureInputHandle(f *os.File) (*InputStream, error) { + ret := &InputStream{ + File: f, + } + + if fd := f.Fd(); isatty.IsTerminal(fd) { + // We have to activate UTF-8 input, or else we fail. This will not + // succeed on Windows 8 or early versions of Windows 10. + // Notice that this doesn't take the specific file descriptor, because + // the console is just ambiently associated with our process. + err := SetConsoleCP(CP_UTF8) + if err != nil { + return nil, fmt.Errorf("failed to set the console to UTF-8 mode; you may need to use a newer version of Windows: %s", err) + } + ret.isTerminal = staticTrue + return ret, nil + } else if isatty.IsCygwinTerminal(fd) { + // As with the output handles above, we'll use isatty's heuristic to + // pretend that a pipe from mintty or a similar userspace terminal + // emulator is actually a terminal. + ret.isTerminal = staticTrue + return ret, nil + } + + // If we fall out here then we have a non-terminal filehandle, so we'll + // just accept all of the default InputStream behaviors + return ret, nil +} + +func getColumnsWindowsConsole(f *os.File) int { + // We'll just unconditionally ask the given file for its console buffer + // info here, and let it fail if the file isn't actually a console. + // (In practice, the init functions above only hook up this function + // if the handle looks like a console, so this should succeed.) + var info windows.ConsoleScreenBufferInfo + err := windows.GetConsoleScreenBufferInfo(windows.Handle(f.Fd()), &info) + if err != nil { + return defaultColumns + } + return int(info.Size.X) +} + +// Unfortunately not all of the Windows kernel functions we need are in +// x/sys/windows at the time of writing, so we need to call some of them +// directly. (If you're maintaining this in future and have the capacity to +// test it well, consider checking if these functions have been added upstream +// yet and switch to their wrapper stubs if so. +var modkernel32 = windows.NewLazySystemDLL("kernel32.dll") +var procSetConsoleCP = modkernel32.NewProc("SetConsoleCP") +var procSetConsoleOutputCP = modkernel32.NewProc("SetConsoleOutputCP") + +const CP_UTF8 = 65001 + +// (These are written in the style of the stubs in x/sys/windows, which is +// a little non-idiomatic just due to the awkwardness of the low-level syscall +// interface.) + +func SetConsoleCP(codepageID uint32) (err error) { + r1, _, e1 := syscall.Syscall(procSetConsoleCP.Addr(), 1, uintptr(codepageID), 0, 0) + if r1 == 0 { + err = e1 + } + return +} + +func SetConsoleOutputCP(codepageID uint32) (err error) { + r1, _, e1 := syscall.Syscall(procSetConsoleOutputCP.Addr(), 1, uintptr(codepageID), 0, 0) + if r1 == 0 { + err = e1 + } + return +} + +func staticTrue(f *os.File) bool { + return true +} + +func staticFalse(f *os.File) bool { + return false +} diff --git a/vendor/github.com/hashicorp/terraform/internal/terminal/panicwrap_ugh.go b/vendor/github.com/hashicorp/terraform/internal/terminal/panicwrap_ugh.go new file mode 100644 index 00000000..b17165b2 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/terminal/panicwrap_ugh.go @@ -0,0 +1,78 @@ +package terminal + +import "os" + +// This file has some annoying nonsense to, yet again, work around the +// panicwrap hack. +// +// Specifically, typically when we're running Terraform the stderr handle is +// not directly connected to the terminal but is instead a pipe into a parent +// process gathering up the output just in case a panic message appears. +// However, this package needs to know whether the _real_ stderr is connected +// to a terminal and what its width is. +// +// To work around that, we'll first initialize the terminal in the parent +// process, and then capture information about stderr into an environment +// variable so we can pass it down to the child process. The child process +// will then use the environment variable to pretend that the panicwrap pipe +// has the same characteristics as the terminal that it's indirectly writing +// to. +// +// This file has some helpers for implementing that awkward handshake, but the +// handshake itself is in package main, interspersed with all of the other +// panicwrap machinery. +// +// You might think that the code in helper/wrappedstreams could avoid this +// problem, but that package is broken on Windows: it always fails to recover +// the real stderr, and it also gets an incorrect result if the user was +// redirecting or piping stdout/stdin. So... we have this hack instead, which +// gets a correct result even on Windows and even with I/O redirection. + +// StateForAfterPanicWrap is part of the workaround for panicwrap that +// captures some characteristics of stderr that the caller can pass to the +// panicwrap child process somehow and then use ReinitInsidePanicWrap. +func (s *Streams) StateForAfterPanicWrap() *PrePanicwrapState { + return &PrePanicwrapState{ + StderrIsTerminal: s.Stderr.IsTerminal(), + StderrWidth: s.Stderr.Columns(), + } +} + +// ReinitInsidePanicwrap is part of the workaround for panicwrap that +// produces a Streams containing a potentially-lying Stderr that might +// claim to be a terminal even if it's actually a pipe connected to the +// parent process. +// +// That's an okay lie in practice because the parent process will copy any +// data it recieves via that pipe verbatim to the real stderr anyway. (The +// original call to Init in the parent process should've already done any +// necessary modesetting on the Stderr terminal, if any.) +// +// The state argument can be nil if we're not running in panicwrap mode, +// in which case this function behaves exactly the same as Init. +func ReinitInsidePanicwrap(state *PrePanicwrapState) (*Streams, error) { + ret, err := Init() + if err != nil { + return ret, err + } + if state != nil { + // A lying stderr, then. + ret.Stderr = &OutputStream{ + File: ret.Stderr.File, + isTerminal: func(f *os.File) bool { + return state.StderrIsTerminal + }, + getColumns: func(f *os.File) int { + return state.StderrWidth + }, + } + } + return ret, nil +} + +// PrePanicwrapState is a horrible thing we use to work around panicwrap, +// related to both Streams.StateForAfterPanicWrap and ReinitInsidePanicwrap. +type PrePanicwrapState struct { + StderrIsTerminal bool + StderrWidth int +} diff --git a/vendor/github.com/hashicorp/terraform/internal/terminal/stream.go b/vendor/github.com/hashicorp/terraform/internal/terminal/stream.go new file mode 100644 index 00000000..6d40e1b1 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/terminal/stream.go @@ -0,0 +1,80 @@ +package terminal + +import ( + "os" +) + +const defaultColumns int = 78 +const defaultIsTerminal bool = false + +// OutputStream represents an output stream that might or might not be connected +// to a terminal. +// +// There are typically two instances of this: one representing stdout and one +// representing stderr. +type OutputStream struct { + File *os.File + + // Interacting with a terminal is typically platform-specific, so we + // factor out these into virtual functions, although we have default + // behaviors suitable for non-Terminal output if any of these isn't + // set. (We're using function pointers rather than interfaces for this + // because it allows us to mix both normal methods and virtual methods + // on the same type, without a bunch of extra complexity.) + isTerminal func(*os.File) bool + getColumns func(*os.File) int +} + +// Columns returns a number of character cell columns that we expect will +// fill the width of the terminal that stdout is connected to, or a reasonable +// placeholder value of 78 if the output doesn't seem to be a terminal. +// +// This is a best-effort sort of function which may give an inaccurate result +// in various cases. For example, callers storing the result will not react +// to subsequent changes in the terminal width, and indeed this function itself +// may not be able to either, depending on the constraints of the current +// execution context. +func (s *OutputStream) Columns() int { + if s.getColumns == nil { + return defaultColumns + } + return s.getColumns(s.File) +} + +// IsTerminal returns true if we expect that the stream is connected to a +// terminal which supports VT100-style formatting and cursor control sequences. +func (s *OutputStream) IsTerminal() bool { + if s.isTerminal == nil { + return defaultIsTerminal + } + return s.isTerminal(s.File) +} + +// InputStream represents an input stream that might or might not be a terminal. +// +// There is typically only one instance of this type, representing stdin. +type InputStream struct { + File *os.File + + // Interacting with a terminal is typically platform-specific, so we + // factor out these into virtual functions, although we have default + // behaviors suitable for non-Terminal output if any of these isn't + // set. (We're using function pointers rather than interfaces for this + // because it allows us to mix both normal methods and virtual methods + // on the same type, without a bunch of extra complexity.) + isTerminal func(*os.File) bool +} + +// IsTerminal returns true if we expect that the stream is connected to a +// terminal which can support interactive input. +// +// If this returns false, callers might prefer to skip elaborate input prompt +// functionality like tab completion and instead just treat the input as a +// raw byte stream, or perhaps skip prompting for input at all depending on the +// situation. +func (s *InputStream) IsTerminal() bool { + if s.isTerminal == nil { + return defaultIsTerminal + } + return s.isTerminal(s.File) +} diff --git a/vendor/github.com/hashicorp/terraform/internal/terminal/streams.go b/vendor/github.com/hashicorp/terraform/internal/terminal/streams.go new file mode 100644 index 00000000..1e1de7d9 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/terminal/streams.go @@ -0,0 +1,105 @@ +// Package terminal encapsulates some platform-specific logic for detecting +// if we're running in a terminal and, if so, properly configuring that +// terminal to meet the assumptions that the rest of Terraform makes. +// +// Specifically, Terraform requires a Terminal which supports virtual terminal +// sequences and which accepts UTF-8-encoded text. +// +// This is an abstraction only over the platform-specific detection of and +// possibly initialization of terminals. It's not intended to provide +// higher-level abstractions of the sort provided by packages like termcap or +// curses; ultimately we just assume that terminals are "standard" VT100-like +// terminals and use a subset of control codes that works across the various +// platforms we support. Our approximate target is "xterm-compatible" +// virtual terminals. +package terminal + +import ( + "fmt" + "os" +) + +// Streams represents a collection of three streams that each may or may not +// be connected to a terminal. +// +// If a stream is connected to a terminal then there are more possibilities +// available, such as detecting the current terminal width. If we're connected +// to something else, such as a pipe or a file on disk, the stream will +// typically provide placeholder values or do-nothing stubs for +// terminal-requiring operatons. +// +// Note that it's possible for only a subset of the streams to be connected +// to a terminal. For example, this happens if the user runs Terraform with +// I/O redirection where Stdout might refer to a regular disk file while Stderr +// refers to a terminal, or various other similar combinations. +type Streams struct { + Stdout *OutputStream + Stderr *OutputStream + Stdin *InputStream +} + +// Init tries to initialize a terminal, if Terraform is running in one, and +// returns an object describing what it was able to set up. +// +// An error for this function indicates that the current execution context +// can't meet Terraform's assumptions. For example, on Windows Init will return +// an error if Terraform is running in a Windows Console that refuses to +// activate UTF-8 mode, which can happen if we're running on an unsupported old +// version of Windows. +// +// Note that the success of this function doesn't mean that we're actually +// running in a terminal. It could also represent successfully detecting that +// one or more of the input/output streams is not a terminal. +func Init() (*Streams, error) { + // These configure* functions are platform-specific functions in other + // files that use //+build constraints to vary based on target OS. + + stderr, err := configureOutputHandle(os.Stderr) + if err != nil { + return nil, err + } + stdout, err := configureOutputHandle(os.Stdout) + if err != nil { + return nil, err + } + stdin, err := configureInputHandle(os.Stdin) + if err != nil { + return nil, err + } + + return &Streams{ + Stdout: stdout, + Stderr: stderr, + Stdin: stdin, + }, nil +} + +// Print is a helper for conveniently calling fmt.Fprint on the Stdout stream. +func (s *Streams) Print(a ...interface{}) (n int, err error) { + return fmt.Fprint(s.Stdout.File, a...) +} + +// Printf is a helper for conveniently calling fmt.Fprintf on the Stdout stream. +func (s *Streams) Printf(format string, a ...interface{}) (n int, err error) { + return fmt.Fprintf(s.Stdout.File, format, a...) +} + +// Println is a helper for conveniently calling fmt.Fprintln on the Stdout stream. +func (s *Streams) Println(a ...interface{}) (n int, err error) { + return fmt.Fprintln(s.Stdout.File, a...) +} + +// Eprint is a helper for conveniently calling fmt.Fprint on the Stderr stream. +func (s *Streams) Eprint(a ...interface{}) (n int, err error) { + return fmt.Fprint(s.Stderr.File, a...) +} + +// Eprintf is a helper for conveniently calling fmt.Fprintf on the Stderr stream. +func (s *Streams) Eprintf(format string, a ...interface{}) (n int, err error) { + return fmt.Fprintf(s.Stderr.File, format, a...) +} + +// Eprintln is a helper for conveniently calling fmt.Fprintln on the Stderr stream. +func (s *Streams) Eprintln(a ...interface{}) (n int, err error) { + return fmt.Fprintln(s.Stderr.File, a...) +} diff --git a/vendor/github.com/hashicorp/terraform/internal/terminal/streams_test.go b/vendor/github.com/hashicorp/terraform/internal/terminal/streams_test.go new file mode 100644 index 00000000..9826b934 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/terminal/streams_test.go @@ -0,0 +1,38 @@ +package terminal + +import ( + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestStreamsFmtHelpers(t *testing.T) { + streams, close := StreamsForTesting(t) + + streams.Print("stdout print ", 5, "\n") + streams.Eprint("stderr print ", 6, "\n") + streams.Println("stdout println", 7) + streams.Eprintln("stderr println", 8) + streams.Printf("stdout printf %d\n", 9) + streams.Eprintf("stderr printf %d\n", 10) + + outp := close(t) + + gotOut := outp.Stdout() + wantOut := `stdout print 5 +stdout println 7 +stdout printf 9 +` + if diff := cmp.Diff(wantOut, gotOut); diff != "" { + t.Errorf("wrong stdout\n%s", diff) + } + + gotErr := outp.Stderr() + wantErr := `stderr print 6 +stderr println 8 +stderr printf 10 +` + if diff := cmp.Diff(wantErr, gotErr); diff != "" { + t.Errorf("wrong stderr\n%s", diff) + } +} diff --git a/vendor/github.com/hashicorp/terraform/internal/terminal/testing.go b/vendor/github.com/hashicorp/terraform/internal/terminal/testing.go new file mode 100644 index 00000000..2830b5d0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/terminal/testing.go @@ -0,0 +1,191 @@ +package terminal + +import ( + "fmt" + "io" + "os" + "strings" + "sync" + "testing" +) + +// StreamsForTesting is a helper for test code that is aiming to test functions +// that interact with the input and output streams. +// +// This particular function is for the simple case of a function that only +// produces output: the returned input stream is connected to the system's +// "null device", as if a user had run Terraform with I/O redirection like +// tfplugin5.Diagnostic.Severity + 5, // 1: tfplugin5.Diagnostic.attribute:type_name -> tfplugin5.AttributePath + 23, // 2: tfplugin5.AttributePath.steps:type_name -> tfplugin5.AttributePath.Step + 26, // 3: tfplugin5.RawState.flatmap:type_name -> tfplugin5.RawState.FlatmapEntry + 27, // 4: tfplugin5.Schema.block:type_name -> tfplugin5.Schema.Block + 28, // 5: tfplugin5.Schema.Block.attributes:type_name -> tfplugin5.Schema.Attribute + 29, // 6: tfplugin5.Schema.Block.block_types:type_name -> tfplugin5.Schema.NestedBlock + 0, // 7: tfplugin5.Schema.Block.description_kind:type_name -> tfplugin5.StringKind + 0, // 8: tfplugin5.Schema.Attribute.description_kind:type_name -> tfplugin5.StringKind + 27, // 9: tfplugin5.Schema.NestedBlock.block:type_name -> tfplugin5.Schema.Block + 2, // 10: tfplugin5.Schema.NestedBlock.nesting:type_name -> tfplugin5.Schema.NestedBlock.NestingMode + 8, // 11: tfplugin5.GetProviderSchema.Response.provider:type_name -> tfplugin5.Schema + 32, // 12: tfplugin5.GetProviderSchema.Response.resource_schemas:type_name -> tfplugin5.GetProviderSchema.Response.ResourceSchemasEntry + 33, // 13: tfplugin5.GetProviderSchema.Response.data_source_schemas:type_name -> tfplugin5.GetProviderSchema.Response.DataSourceSchemasEntry + 4, // 14: tfplugin5.GetProviderSchema.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 8, // 15: tfplugin5.GetProviderSchema.Response.provider_meta:type_name -> tfplugin5.Schema + 8, // 16: tfplugin5.GetProviderSchema.Response.ResourceSchemasEntry.value:type_name -> tfplugin5.Schema + 8, // 17: tfplugin5.GetProviderSchema.Response.DataSourceSchemasEntry.value:type_name -> tfplugin5.Schema + 3, // 18: tfplugin5.PrepareProviderConfig.Request.config:type_name -> tfplugin5.DynamicValue + 3, // 19: tfplugin5.PrepareProviderConfig.Response.prepared_config:type_name -> tfplugin5.DynamicValue + 4, // 20: tfplugin5.PrepareProviderConfig.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 7, // 21: tfplugin5.UpgradeResourceState.Request.raw_state:type_name -> tfplugin5.RawState + 3, // 22: tfplugin5.UpgradeResourceState.Response.upgraded_state:type_name -> tfplugin5.DynamicValue + 4, // 23: tfplugin5.UpgradeResourceState.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 24: tfplugin5.ValidateResourceTypeConfig.Request.config:type_name -> tfplugin5.DynamicValue + 4, // 25: tfplugin5.ValidateResourceTypeConfig.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 26: tfplugin5.ValidateDataSourceConfig.Request.config:type_name -> tfplugin5.DynamicValue + 4, // 27: tfplugin5.ValidateDataSourceConfig.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 28: tfplugin5.Configure.Request.config:type_name -> tfplugin5.DynamicValue + 4, // 29: tfplugin5.Configure.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 30: tfplugin5.ReadResource.Request.current_state:type_name -> tfplugin5.DynamicValue + 3, // 31: tfplugin5.ReadResource.Request.provider_meta:type_name -> tfplugin5.DynamicValue + 3, // 32: tfplugin5.ReadResource.Response.new_state:type_name -> tfplugin5.DynamicValue + 4, // 33: tfplugin5.ReadResource.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 34: tfplugin5.PlanResourceChange.Request.prior_state:type_name -> tfplugin5.DynamicValue + 3, // 35: tfplugin5.PlanResourceChange.Request.proposed_new_state:type_name -> tfplugin5.DynamicValue + 3, // 36: tfplugin5.PlanResourceChange.Request.config:type_name -> tfplugin5.DynamicValue + 3, // 37: tfplugin5.PlanResourceChange.Request.provider_meta:type_name -> tfplugin5.DynamicValue + 3, // 38: tfplugin5.PlanResourceChange.Response.planned_state:type_name -> tfplugin5.DynamicValue + 5, // 39: tfplugin5.PlanResourceChange.Response.requires_replace:type_name -> tfplugin5.AttributePath + 4, // 40: tfplugin5.PlanResourceChange.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 41: tfplugin5.ApplyResourceChange.Request.prior_state:type_name -> tfplugin5.DynamicValue + 3, // 42: tfplugin5.ApplyResourceChange.Request.planned_state:type_name -> tfplugin5.DynamicValue + 3, // 43: tfplugin5.ApplyResourceChange.Request.config:type_name -> tfplugin5.DynamicValue + 3, // 44: tfplugin5.ApplyResourceChange.Request.provider_meta:type_name -> tfplugin5.DynamicValue + 3, // 45: tfplugin5.ApplyResourceChange.Response.new_state:type_name -> tfplugin5.DynamicValue + 4, // 46: tfplugin5.ApplyResourceChange.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 47: tfplugin5.ImportResourceState.ImportedResource.state:type_name -> tfplugin5.DynamicValue + 51, // 48: tfplugin5.ImportResourceState.Response.imported_resources:type_name -> tfplugin5.ImportResourceState.ImportedResource + 4, // 49: tfplugin5.ImportResourceState.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 50: tfplugin5.ReadDataSource.Request.config:type_name -> tfplugin5.DynamicValue + 3, // 51: tfplugin5.ReadDataSource.Request.provider_meta:type_name -> tfplugin5.DynamicValue + 3, // 52: tfplugin5.ReadDataSource.Response.state:type_name -> tfplugin5.DynamicValue + 4, // 53: tfplugin5.ReadDataSource.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 8, // 54: tfplugin5.GetProvisionerSchema.Response.provisioner:type_name -> tfplugin5.Schema + 4, // 55: tfplugin5.GetProvisionerSchema.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 56: tfplugin5.ValidateProvisionerConfig.Request.config:type_name -> tfplugin5.DynamicValue + 4, // 57: tfplugin5.ValidateProvisionerConfig.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 3, // 58: tfplugin5.ProvisionResource.Request.config:type_name -> tfplugin5.DynamicValue + 3, // 59: tfplugin5.ProvisionResource.Request.connection:type_name -> tfplugin5.DynamicValue + 4, // 60: tfplugin5.ProvisionResource.Response.diagnostics:type_name -> tfplugin5.Diagnostic + 30, // 61: tfplugin5.Provider.GetSchema:input_type -> tfplugin5.GetProviderSchema.Request + 34, // 62: tfplugin5.Provider.PrepareProviderConfig:input_type -> tfplugin5.PrepareProviderConfig.Request + 38, // 63: tfplugin5.Provider.ValidateResourceTypeConfig:input_type -> tfplugin5.ValidateResourceTypeConfig.Request + 40, // 64: tfplugin5.Provider.ValidateDataSourceConfig:input_type -> tfplugin5.ValidateDataSourceConfig.Request + 36, // 65: tfplugin5.Provider.UpgradeResourceState:input_type -> tfplugin5.UpgradeResourceState.Request + 42, // 66: tfplugin5.Provider.Configure:input_type -> tfplugin5.Configure.Request + 44, // 67: tfplugin5.Provider.ReadResource:input_type -> tfplugin5.ReadResource.Request + 46, // 68: tfplugin5.Provider.PlanResourceChange:input_type -> tfplugin5.PlanResourceChange.Request + 48, // 69: tfplugin5.Provider.ApplyResourceChange:input_type -> tfplugin5.ApplyResourceChange.Request + 50, // 70: tfplugin5.Provider.ImportResourceState:input_type -> tfplugin5.ImportResourceState.Request + 53, // 71: tfplugin5.Provider.ReadDataSource:input_type -> tfplugin5.ReadDataSource.Request + 24, // 72: tfplugin5.Provider.Stop:input_type -> tfplugin5.Stop.Request + 55, // 73: tfplugin5.Provisioner.GetSchema:input_type -> tfplugin5.GetProvisionerSchema.Request + 57, // 74: tfplugin5.Provisioner.ValidateProvisionerConfig:input_type -> tfplugin5.ValidateProvisionerConfig.Request + 59, // 75: tfplugin5.Provisioner.ProvisionResource:input_type -> tfplugin5.ProvisionResource.Request + 24, // 76: tfplugin5.Provisioner.Stop:input_type -> tfplugin5.Stop.Request + 31, // 77: tfplugin5.Provider.GetSchema:output_type -> tfplugin5.GetProviderSchema.Response + 35, // 78: tfplugin5.Provider.PrepareProviderConfig:output_type -> tfplugin5.PrepareProviderConfig.Response + 39, // 79: tfplugin5.Provider.ValidateResourceTypeConfig:output_type -> tfplugin5.ValidateResourceTypeConfig.Response + 41, // 80: tfplugin5.Provider.ValidateDataSourceConfig:output_type -> tfplugin5.ValidateDataSourceConfig.Response + 37, // 81: tfplugin5.Provider.UpgradeResourceState:output_type -> tfplugin5.UpgradeResourceState.Response + 43, // 82: tfplugin5.Provider.Configure:output_type -> tfplugin5.Configure.Response + 45, // 83: tfplugin5.Provider.ReadResource:output_type -> tfplugin5.ReadResource.Response + 47, // 84: tfplugin5.Provider.PlanResourceChange:output_type -> tfplugin5.PlanResourceChange.Response + 49, // 85: tfplugin5.Provider.ApplyResourceChange:output_type -> tfplugin5.ApplyResourceChange.Response + 52, // 86: tfplugin5.Provider.ImportResourceState:output_type -> tfplugin5.ImportResourceState.Response + 54, // 87: tfplugin5.Provider.ReadDataSource:output_type -> tfplugin5.ReadDataSource.Response + 25, // 88: tfplugin5.Provider.Stop:output_type -> tfplugin5.Stop.Response + 56, // 89: tfplugin5.Provisioner.GetSchema:output_type -> tfplugin5.GetProvisionerSchema.Response + 58, // 90: tfplugin5.Provisioner.ValidateProvisionerConfig:output_type -> tfplugin5.ValidateProvisionerConfig.Response + 60, // 91: tfplugin5.Provisioner.ProvisionResource:output_type -> tfplugin5.ProvisionResource.Response + 25, // 92: tfplugin5.Provisioner.Stop:output_type -> tfplugin5.Stop.Response + 77, // [77:93] is the sub-list for method output_type + 61, // [61:77] is the sub-list for method input_type + 61, // [61:61] is the sub-list for extension type_name + 61, // [61:61] is the sub-list for extension extendee + 0, // [0:61] is the sub-list for field type_name +} + +func init() { file_tfplugin5_proto_init() } +func file_tfplugin5_proto_init() { + if File_tfplugin5_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_tfplugin5_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DynamicValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Diagnostic); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AttributePath); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Stop); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RawState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Schema); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetProviderSchema); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PrepareProviderConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpgradeResourceState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateResourceTypeConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateDataSourceConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configure); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadResource); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PlanResourceChange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ApplyResourceChange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ImportResourceState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadDataSource); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetProvisionerSchema); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateProvisionerConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProvisionResource); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AttributePath_Step); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Stop_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Stop_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Schema_Block); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Schema_Attribute); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Schema_NestedBlock); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetProviderSchema_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetProviderSchema_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PrepareProviderConfig_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PrepareProviderConfig_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpgradeResourceState_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpgradeResourceState_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateResourceTypeConfig_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateResourceTypeConfig_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateDataSourceConfig_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateDataSourceConfig_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configure_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Configure_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadResource_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadResource_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PlanResourceChange_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PlanResourceChange_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ApplyResourceChange_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ApplyResourceChange_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ImportResourceState_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ImportResourceState_ImportedResource); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ImportResourceState_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadDataSource_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadDataSource_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetProvisionerSchema_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetProvisionerSchema_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateProvisionerConfig_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateProvisionerConfig_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProvisionResource_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin5_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProvisionResource_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_tfplugin5_proto_msgTypes[20].OneofWrappers = []interface{}{ + (*AttributePath_Step_AttributeName)(nil), + (*AttributePath_Step_ElementKeyString)(nil), + (*AttributePath_Step_ElementKeyInt)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_tfplugin5_proto_rawDesc, + NumEnums: 3, + NumMessages: 58, + NumExtensions: 0, + NumServices: 2, + }, + GoTypes: file_tfplugin5_proto_goTypes, + DependencyIndexes: file_tfplugin5_proto_depIdxs, + EnumInfos: file_tfplugin5_proto_enumTypes, + MessageInfos: file_tfplugin5_proto_msgTypes, + }.Build() + File_tfplugin5_proto = out.File + file_tfplugin5_proto_rawDesc = nil + file_tfplugin5_proto_goTypes = nil + file_tfplugin5_proto_depIdxs = nil } // Reference imports to suppress errors if they are not otherwise used. var _ context.Context -var _ grpc.ClientConn +var _ grpc.ClientConnInterface // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 +const _ = grpc.SupportPackageIsVersion6 // ProviderClient is the client API for Provider service. // @@ -2970,10 +4640,10 @@ type ProviderClient interface { } type providerClient struct { - cc *grpc.ClientConn + cc grpc.ClientConnInterface } -func NewProviderClient(cc *grpc.ClientConn) ProviderClient { +func NewProviderClient(cc grpc.ClientConnInterface) ProviderClient { return &providerClient{cc} } @@ -3109,40 +4779,40 @@ type ProviderServer interface { type UnimplementedProviderServer struct { } -func (*UnimplementedProviderServer) GetSchema(ctx context.Context, req *GetProviderSchema_Request) (*GetProviderSchema_Response, error) { +func (*UnimplementedProviderServer) GetSchema(context.Context, *GetProviderSchema_Request) (*GetProviderSchema_Response, error) { return nil, status.Errorf(codes.Unimplemented, "method GetSchema not implemented") } -func (*UnimplementedProviderServer) PrepareProviderConfig(ctx context.Context, req *PrepareProviderConfig_Request) (*PrepareProviderConfig_Response, error) { +func (*UnimplementedProviderServer) PrepareProviderConfig(context.Context, *PrepareProviderConfig_Request) (*PrepareProviderConfig_Response, error) { return nil, status.Errorf(codes.Unimplemented, "method PrepareProviderConfig not implemented") } -func (*UnimplementedProviderServer) ValidateResourceTypeConfig(ctx context.Context, req *ValidateResourceTypeConfig_Request) (*ValidateResourceTypeConfig_Response, error) { +func (*UnimplementedProviderServer) ValidateResourceTypeConfig(context.Context, *ValidateResourceTypeConfig_Request) (*ValidateResourceTypeConfig_Response, error) { return nil, status.Errorf(codes.Unimplemented, "method ValidateResourceTypeConfig not implemented") } -func (*UnimplementedProviderServer) ValidateDataSourceConfig(ctx context.Context, req *ValidateDataSourceConfig_Request) (*ValidateDataSourceConfig_Response, error) { +func (*UnimplementedProviderServer) ValidateDataSourceConfig(context.Context, *ValidateDataSourceConfig_Request) (*ValidateDataSourceConfig_Response, error) { return nil, status.Errorf(codes.Unimplemented, "method ValidateDataSourceConfig not implemented") } -func (*UnimplementedProviderServer) UpgradeResourceState(ctx context.Context, req *UpgradeResourceState_Request) (*UpgradeResourceState_Response, error) { +func (*UnimplementedProviderServer) UpgradeResourceState(context.Context, *UpgradeResourceState_Request) (*UpgradeResourceState_Response, error) { return nil, status.Errorf(codes.Unimplemented, "method UpgradeResourceState not implemented") } -func (*UnimplementedProviderServer) Configure(ctx context.Context, req *Configure_Request) (*Configure_Response, error) { +func (*UnimplementedProviderServer) Configure(context.Context, *Configure_Request) (*Configure_Response, error) { return nil, status.Errorf(codes.Unimplemented, "method Configure not implemented") } -func (*UnimplementedProviderServer) ReadResource(ctx context.Context, req *ReadResource_Request) (*ReadResource_Response, error) { +func (*UnimplementedProviderServer) ReadResource(context.Context, *ReadResource_Request) (*ReadResource_Response, error) { return nil, status.Errorf(codes.Unimplemented, "method ReadResource not implemented") } -func (*UnimplementedProviderServer) PlanResourceChange(ctx context.Context, req *PlanResourceChange_Request) (*PlanResourceChange_Response, error) { +func (*UnimplementedProviderServer) PlanResourceChange(context.Context, *PlanResourceChange_Request) (*PlanResourceChange_Response, error) { return nil, status.Errorf(codes.Unimplemented, "method PlanResourceChange not implemented") } -func (*UnimplementedProviderServer) ApplyResourceChange(ctx context.Context, req *ApplyResourceChange_Request) (*ApplyResourceChange_Response, error) { +func (*UnimplementedProviderServer) ApplyResourceChange(context.Context, *ApplyResourceChange_Request) (*ApplyResourceChange_Response, error) { return nil, status.Errorf(codes.Unimplemented, "method ApplyResourceChange not implemented") } -func (*UnimplementedProviderServer) ImportResourceState(ctx context.Context, req *ImportResourceState_Request) (*ImportResourceState_Response, error) { +func (*UnimplementedProviderServer) ImportResourceState(context.Context, *ImportResourceState_Request) (*ImportResourceState_Response, error) { return nil, status.Errorf(codes.Unimplemented, "method ImportResourceState not implemented") } -func (*UnimplementedProviderServer) ReadDataSource(ctx context.Context, req *ReadDataSource_Request) (*ReadDataSource_Response, error) { +func (*UnimplementedProviderServer) ReadDataSource(context.Context, *ReadDataSource_Request) (*ReadDataSource_Response, error) { return nil, status.Errorf(codes.Unimplemented, "method ReadDataSource not implemented") } -func (*UnimplementedProviderServer) Stop(ctx context.Context, req *Stop_Request) (*Stop_Response, error) { +func (*UnimplementedProviderServer) Stop(context.Context, *Stop_Request) (*Stop_Response, error) { return nil, status.Errorf(codes.Unimplemented, "method Stop not implemented") } @@ -3434,10 +5104,10 @@ type ProvisionerClient interface { } type provisionerClient struct { - cc *grpc.ClientConn + cc grpc.ClientConnInterface } -func NewProvisionerClient(cc *grpc.ClientConn) ProvisionerClient { +func NewProvisionerClient(cc grpc.ClientConnInterface) ProvisionerClient { return &provisionerClient{cc} } @@ -3512,16 +5182,16 @@ type ProvisionerServer interface { type UnimplementedProvisionerServer struct { } -func (*UnimplementedProvisionerServer) GetSchema(ctx context.Context, req *GetProvisionerSchema_Request) (*GetProvisionerSchema_Response, error) { +func (*UnimplementedProvisionerServer) GetSchema(context.Context, *GetProvisionerSchema_Request) (*GetProvisionerSchema_Response, error) { return nil, status.Errorf(codes.Unimplemented, "method GetSchema not implemented") } -func (*UnimplementedProvisionerServer) ValidateProvisionerConfig(ctx context.Context, req *ValidateProvisionerConfig_Request) (*ValidateProvisionerConfig_Response, error) { +func (*UnimplementedProvisionerServer) ValidateProvisionerConfig(context.Context, *ValidateProvisionerConfig_Request) (*ValidateProvisionerConfig_Response, error) { return nil, status.Errorf(codes.Unimplemented, "method ValidateProvisionerConfig not implemented") } -func (*UnimplementedProvisionerServer) ProvisionResource(req *ProvisionResource_Request, srv Provisioner_ProvisionResourceServer) error { +func (*UnimplementedProvisionerServer) ProvisionResource(*ProvisionResource_Request, Provisioner_ProvisionResourceServer) error { return status.Errorf(codes.Unimplemented, "method ProvisionResource not implemented") } -func (*UnimplementedProvisionerServer) Stop(ctx context.Context, req *Stop_Request) (*Stop_Response, error) { +func (*UnimplementedProvisionerServer) Stop(context.Context, *Stop_Request) (*Stop_Response, error) { return nil, status.Errorf(codes.Unimplemented, "method Stop not implemented") } diff --git a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5/generate.sh b/vendor/github.com/hashicorp/terraform/internal/tfplugin6/generate.sh similarity index 85% rename from vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5/generate.sh rename to vendor/github.com/hashicorp/terraform/internal/tfplugin6/generate.sh index de1d693c..0dca95b2 100644 --- a/vendor/github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5/generate.sh +++ b/vendor/github.com/hashicorp/terraform/internal/tfplugin6/generate.sh @@ -13,4 +13,4 @@ DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" cd "$DIR" -protoc -I ./ tfplugin5.proto --go_out=plugins=grpc:./ +protoc --go_out=paths=source_relative,plugins=grpc:. ./tfplugin6.proto diff --git a/vendor/github.com/hashicorp/terraform/internal/tfplugin6/tfplugin6.pb.go b/vendor/github.com/hashicorp/terraform/internal/tfplugin6/tfplugin6.pb.go new file mode 100644 index 00000000..fa89050b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/tfplugin6/tfplugin6.pb.go @@ -0,0 +1,4611 @@ +// Terraform Plugin RPC protocol version 6.0 +// +// This file defines version 6.0 of the RPC protocol. To implement a plugin +// against this protocol, copy this definition into your own codebase and +// use protoc to generate stubs for your target language. +// +// This file will not be updated. Any minor versions of protocol 6 to follow +// should copy this file and modify the copy while maintaing backwards +// compatibility. Breaking changes, if any are required, will come +// in a subsequent major version with its own separate proto definition. +// +// Note that only the proto files included in a release tag of Terraform are +// official protocol releases. Proto files taken from other commits may include +// incomplete changes or features that did not make it into a final release. +// In all reasonable cases, plugin developers should take the proto file from +// the tag of the most recent release of Terraform, and not from the main +// branch or any other development branch. +// + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.23.0 +// protoc v3.13.0 +// source: tfplugin6.proto + +package tfplugin6 + +import ( + context "context" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type StringKind int32 + +const ( + StringKind_PLAIN StringKind = 0 + StringKind_MARKDOWN StringKind = 1 +) + +// Enum value maps for StringKind. +var ( + StringKind_name = map[int32]string{ + 0: "PLAIN", + 1: "MARKDOWN", + } + StringKind_value = map[string]int32{ + "PLAIN": 0, + "MARKDOWN": 1, + } +) + +func (x StringKind) Enum() *StringKind { + p := new(StringKind) + *p = x + return p +} + +func (x StringKind) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (StringKind) Descriptor() protoreflect.EnumDescriptor { + return file_tfplugin6_proto_enumTypes[0].Descriptor() +} + +func (StringKind) Type() protoreflect.EnumType { + return &file_tfplugin6_proto_enumTypes[0] +} + +func (x StringKind) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use StringKind.Descriptor instead. +func (StringKind) EnumDescriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{0} +} + +type Diagnostic_Severity int32 + +const ( + Diagnostic_INVALID Diagnostic_Severity = 0 + Diagnostic_ERROR Diagnostic_Severity = 1 + Diagnostic_WARNING Diagnostic_Severity = 2 +) + +// Enum value maps for Diagnostic_Severity. +var ( + Diagnostic_Severity_name = map[int32]string{ + 0: "INVALID", + 1: "ERROR", + 2: "WARNING", + } + Diagnostic_Severity_value = map[string]int32{ + "INVALID": 0, + "ERROR": 1, + "WARNING": 2, + } +) + +func (x Diagnostic_Severity) Enum() *Diagnostic_Severity { + p := new(Diagnostic_Severity) + *p = x + return p +} + +func (x Diagnostic_Severity) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Diagnostic_Severity) Descriptor() protoreflect.EnumDescriptor { + return file_tfplugin6_proto_enumTypes[1].Descriptor() +} + +func (Diagnostic_Severity) Type() protoreflect.EnumType { + return &file_tfplugin6_proto_enumTypes[1] +} + +func (x Diagnostic_Severity) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Diagnostic_Severity.Descriptor instead. +func (Diagnostic_Severity) EnumDescriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{1, 0} +} + +type Schema_NestedBlock_NestingMode int32 + +const ( + Schema_NestedBlock_INVALID Schema_NestedBlock_NestingMode = 0 + Schema_NestedBlock_SINGLE Schema_NestedBlock_NestingMode = 1 + Schema_NestedBlock_LIST Schema_NestedBlock_NestingMode = 2 + Schema_NestedBlock_SET Schema_NestedBlock_NestingMode = 3 + Schema_NestedBlock_MAP Schema_NestedBlock_NestingMode = 4 + Schema_NestedBlock_GROUP Schema_NestedBlock_NestingMode = 5 +) + +// Enum value maps for Schema_NestedBlock_NestingMode. +var ( + Schema_NestedBlock_NestingMode_name = map[int32]string{ + 0: "INVALID", + 1: "SINGLE", + 2: "LIST", + 3: "SET", + 4: "MAP", + 5: "GROUP", + } + Schema_NestedBlock_NestingMode_value = map[string]int32{ + "INVALID": 0, + "SINGLE": 1, + "LIST": 2, + "SET": 3, + "MAP": 4, + "GROUP": 5, + } +) + +func (x Schema_NestedBlock_NestingMode) Enum() *Schema_NestedBlock_NestingMode { + p := new(Schema_NestedBlock_NestingMode) + *p = x + return p +} + +func (x Schema_NestedBlock_NestingMode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Schema_NestedBlock_NestingMode) Descriptor() protoreflect.EnumDescriptor { + return file_tfplugin6_proto_enumTypes[2].Descriptor() +} + +func (Schema_NestedBlock_NestingMode) Type() protoreflect.EnumType { + return &file_tfplugin6_proto_enumTypes[2] +} + +func (x Schema_NestedBlock_NestingMode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Schema_NestedBlock_NestingMode.Descriptor instead. +func (Schema_NestedBlock_NestingMode) EnumDescriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{5, 2, 0} +} + +type Schema_Object_NestingMode int32 + +const ( + Schema_Object_INVALID Schema_Object_NestingMode = 0 + Schema_Object_SINGLE Schema_Object_NestingMode = 1 + Schema_Object_LIST Schema_Object_NestingMode = 2 + Schema_Object_SET Schema_Object_NestingMode = 3 + Schema_Object_MAP Schema_Object_NestingMode = 4 +) + +// Enum value maps for Schema_Object_NestingMode. +var ( + Schema_Object_NestingMode_name = map[int32]string{ + 0: "INVALID", + 1: "SINGLE", + 2: "LIST", + 3: "SET", + 4: "MAP", + } + Schema_Object_NestingMode_value = map[string]int32{ + "INVALID": 0, + "SINGLE": 1, + "LIST": 2, + "SET": 3, + "MAP": 4, + } +) + +func (x Schema_Object_NestingMode) Enum() *Schema_Object_NestingMode { + p := new(Schema_Object_NestingMode) + *p = x + return p +} + +func (x Schema_Object_NestingMode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Schema_Object_NestingMode) Descriptor() protoreflect.EnumDescriptor { + return file_tfplugin6_proto_enumTypes[3].Descriptor() +} + +func (Schema_Object_NestingMode) Type() protoreflect.EnumType { + return &file_tfplugin6_proto_enumTypes[3] +} + +func (x Schema_Object_NestingMode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Schema_Object_NestingMode.Descriptor instead. +func (Schema_Object_NestingMode) EnumDescriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{5, 3, 0} +} + +// DynamicValue is an opaque encoding of terraform data, with the field name +// indicating the encoding scheme used. +type DynamicValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Msgpack []byte `protobuf:"bytes,1,opt,name=msgpack,proto3" json:"msgpack,omitempty"` + Json []byte `protobuf:"bytes,2,opt,name=json,proto3" json:"json,omitempty"` +} + +func (x *DynamicValue) Reset() { + *x = DynamicValue{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DynamicValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DynamicValue) ProtoMessage() {} + +func (x *DynamicValue) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DynamicValue.ProtoReflect.Descriptor instead. +func (*DynamicValue) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{0} +} + +func (x *DynamicValue) GetMsgpack() []byte { + if x != nil { + return x.Msgpack + } + return nil +} + +func (x *DynamicValue) GetJson() []byte { + if x != nil { + return x.Json + } + return nil +} + +type Diagnostic struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Severity Diagnostic_Severity `protobuf:"varint,1,opt,name=severity,proto3,enum=tfplugin6.Diagnostic_Severity" json:"severity,omitempty"` + Summary string `protobuf:"bytes,2,opt,name=summary,proto3" json:"summary,omitempty"` + Detail string `protobuf:"bytes,3,opt,name=detail,proto3" json:"detail,omitempty"` + Attribute *AttributePath `protobuf:"bytes,4,opt,name=attribute,proto3" json:"attribute,omitempty"` +} + +func (x *Diagnostic) Reset() { + *x = Diagnostic{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Diagnostic) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Diagnostic) ProtoMessage() {} + +func (x *Diagnostic) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Diagnostic.ProtoReflect.Descriptor instead. +func (*Diagnostic) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{1} +} + +func (x *Diagnostic) GetSeverity() Diagnostic_Severity { + if x != nil { + return x.Severity + } + return Diagnostic_INVALID +} + +func (x *Diagnostic) GetSummary() string { + if x != nil { + return x.Summary + } + return "" +} + +func (x *Diagnostic) GetDetail() string { + if x != nil { + return x.Detail + } + return "" +} + +func (x *Diagnostic) GetAttribute() *AttributePath { + if x != nil { + return x.Attribute + } + return nil +} + +type AttributePath struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Steps []*AttributePath_Step `protobuf:"bytes,1,rep,name=steps,proto3" json:"steps,omitempty"` +} + +func (x *AttributePath) Reset() { + *x = AttributePath{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AttributePath) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AttributePath) ProtoMessage() {} + +func (x *AttributePath) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AttributePath.ProtoReflect.Descriptor instead. +func (*AttributePath) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{2} +} + +func (x *AttributePath) GetSteps() []*AttributePath_Step { + if x != nil { + return x.Steps + } + return nil +} + +type StopProvider struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *StopProvider) Reset() { + *x = StopProvider{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StopProvider) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StopProvider) ProtoMessage() {} + +func (x *StopProvider) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StopProvider.ProtoReflect.Descriptor instead. +func (*StopProvider) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{3} +} + +// RawState holds the stored state for a resource to be upgraded by the +// provider. It can be in one of two formats, the current json encoded format +// in bytes, or the legacy flatmap format as a map of strings. +type RawState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Json []byte `protobuf:"bytes,1,opt,name=json,proto3" json:"json,omitempty"` + Flatmap map[string]string `protobuf:"bytes,2,rep,name=flatmap,proto3" json:"flatmap,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *RawState) Reset() { + *x = RawState{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RawState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RawState) ProtoMessage() {} + +func (x *RawState) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RawState.ProtoReflect.Descriptor instead. +func (*RawState) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{4} +} + +func (x *RawState) GetJson() []byte { + if x != nil { + return x.Json + } + return nil +} + +func (x *RawState) GetFlatmap() map[string]string { + if x != nil { + return x.Flatmap + } + return nil +} + +// Schema is the configuration schema for a Resource or Provider. +type Schema struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The version of the schema. + // Schemas are versioned, so that providers can upgrade a saved resource + // state when the schema is changed. + Version int64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + // Block is the top level configuration block for this schema. + Block *Schema_Block `protobuf:"bytes,2,opt,name=block,proto3" json:"block,omitempty"` +} + +func (x *Schema) Reset() { + *x = Schema{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Schema) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Schema) ProtoMessage() {} + +func (x *Schema) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Schema.ProtoReflect.Descriptor instead. +func (*Schema) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{5} +} + +func (x *Schema) GetVersion() int64 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *Schema) GetBlock() *Schema_Block { + if x != nil { + return x.Block + } + return nil +} + +type GetProviderSchema struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GetProviderSchema) Reset() { + *x = GetProviderSchema{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetProviderSchema) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetProviderSchema) ProtoMessage() {} + +func (x *GetProviderSchema) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetProviderSchema.ProtoReflect.Descriptor instead. +func (*GetProviderSchema) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{6} +} + +type ValidateProviderConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ValidateProviderConfig) Reset() { + *x = ValidateProviderConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValidateProviderConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValidateProviderConfig) ProtoMessage() {} + +func (x *ValidateProviderConfig) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValidateProviderConfig.ProtoReflect.Descriptor instead. +func (*ValidateProviderConfig) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{7} +} + +type UpgradeResourceState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *UpgradeResourceState) Reset() { + *x = UpgradeResourceState{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpgradeResourceState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpgradeResourceState) ProtoMessage() {} + +func (x *UpgradeResourceState) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpgradeResourceState.ProtoReflect.Descriptor instead. +func (*UpgradeResourceState) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{8} +} + +type ValidateResourceConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ValidateResourceConfig) Reset() { + *x = ValidateResourceConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValidateResourceConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValidateResourceConfig) ProtoMessage() {} + +func (x *ValidateResourceConfig) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValidateResourceConfig.ProtoReflect.Descriptor instead. +func (*ValidateResourceConfig) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{9} +} + +type ValidateDataResourceConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ValidateDataResourceConfig) Reset() { + *x = ValidateDataResourceConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValidateDataResourceConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValidateDataResourceConfig) ProtoMessage() {} + +func (x *ValidateDataResourceConfig) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValidateDataResourceConfig.ProtoReflect.Descriptor instead. +func (*ValidateDataResourceConfig) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{10} +} + +type ConfigureProvider struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ConfigureProvider) Reset() { + *x = ConfigureProvider{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConfigureProvider) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConfigureProvider) ProtoMessage() {} + +func (x *ConfigureProvider) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConfigureProvider.ProtoReflect.Descriptor instead. +func (*ConfigureProvider) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{11} +} + +type ReadResource struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ReadResource) Reset() { + *x = ReadResource{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadResource) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadResource) ProtoMessage() {} + +func (x *ReadResource) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadResource.ProtoReflect.Descriptor instead. +func (*ReadResource) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{12} +} + +type PlanResourceChange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *PlanResourceChange) Reset() { + *x = PlanResourceChange{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PlanResourceChange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PlanResourceChange) ProtoMessage() {} + +func (x *PlanResourceChange) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PlanResourceChange.ProtoReflect.Descriptor instead. +func (*PlanResourceChange) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{13} +} + +type ApplyResourceChange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ApplyResourceChange) Reset() { + *x = ApplyResourceChange{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ApplyResourceChange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApplyResourceChange) ProtoMessage() {} + +func (x *ApplyResourceChange) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ApplyResourceChange.ProtoReflect.Descriptor instead. +func (*ApplyResourceChange) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{14} +} + +type ImportResourceState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ImportResourceState) Reset() { + *x = ImportResourceState{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ImportResourceState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ImportResourceState) ProtoMessage() {} + +func (x *ImportResourceState) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ImportResourceState.ProtoReflect.Descriptor instead. +func (*ImportResourceState) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{15} +} + +type ReadDataSource struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ReadDataSource) Reset() { + *x = ReadDataSource{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadDataSource) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadDataSource) ProtoMessage() {} + +func (x *ReadDataSource) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadDataSource.ProtoReflect.Descriptor instead. +func (*ReadDataSource) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{16} +} + +type AttributePath_Step struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Selector: + // *AttributePath_Step_AttributeName + // *AttributePath_Step_ElementKeyString + // *AttributePath_Step_ElementKeyInt + Selector isAttributePath_Step_Selector `protobuf_oneof:"selector"` +} + +func (x *AttributePath_Step) Reset() { + *x = AttributePath_Step{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AttributePath_Step) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AttributePath_Step) ProtoMessage() {} + +func (x *AttributePath_Step) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AttributePath_Step.ProtoReflect.Descriptor instead. +func (*AttributePath_Step) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{2, 0} +} + +func (m *AttributePath_Step) GetSelector() isAttributePath_Step_Selector { + if m != nil { + return m.Selector + } + return nil +} + +func (x *AttributePath_Step) GetAttributeName() string { + if x, ok := x.GetSelector().(*AttributePath_Step_AttributeName); ok { + return x.AttributeName + } + return "" +} + +func (x *AttributePath_Step) GetElementKeyString() string { + if x, ok := x.GetSelector().(*AttributePath_Step_ElementKeyString); ok { + return x.ElementKeyString + } + return "" +} + +func (x *AttributePath_Step) GetElementKeyInt() int64 { + if x, ok := x.GetSelector().(*AttributePath_Step_ElementKeyInt); ok { + return x.ElementKeyInt + } + return 0 +} + +type isAttributePath_Step_Selector interface { + isAttributePath_Step_Selector() +} + +type AttributePath_Step_AttributeName struct { + // Set "attribute_name" to represent looking up an attribute + // in the current object value. + AttributeName string `protobuf:"bytes,1,opt,name=attribute_name,json=attributeName,proto3,oneof"` +} + +type AttributePath_Step_ElementKeyString struct { + // Set "element_key_*" to represent looking up an element in + // an indexable collection type. + ElementKeyString string `protobuf:"bytes,2,opt,name=element_key_string,json=elementKeyString,proto3,oneof"` +} + +type AttributePath_Step_ElementKeyInt struct { + ElementKeyInt int64 `protobuf:"varint,3,opt,name=element_key_int,json=elementKeyInt,proto3,oneof"` +} + +func (*AttributePath_Step_AttributeName) isAttributePath_Step_Selector() {} + +func (*AttributePath_Step_ElementKeyString) isAttributePath_Step_Selector() {} + +func (*AttributePath_Step_ElementKeyInt) isAttributePath_Step_Selector() {} + +type StopProvider_Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *StopProvider_Request) Reset() { + *x = StopProvider_Request{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StopProvider_Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StopProvider_Request) ProtoMessage() {} + +func (x *StopProvider_Request) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StopProvider_Request.ProtoReflect.Descriptor instead. +func (*StopProvider_Request) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{3, 0} +} + +type StopProvider_Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Error string `protobuf:"bytes,1,opt,name=Error,proto3" json:"Error,omitempty"` +} + +func (x *StopProvider_Response) Reset() { + *x = StopProvider_Response{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StopProvider_Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StopProvider_Response) ProtoMessage() {} + +func (x *StopProvider_Response) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StopProvider_Response.ProtoReflect.Descriptor instead. +func (*StopProvider_Response) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{3, 1} +} + +func (x *StopProvider_Response) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +type Schema_Block struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Version int64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + Attributes []*Schema_Attribute `protobuf:"bytes,2,rep,name=attributes,proto3" json:"attributes,omitempty"` + BlockTypes []*Schema_NestedBlock `protobuf:"bytes,3,rep,name=block_types,json=blockTypes,proto3" json:"block_types,omitempty"` + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + DescriptionKind StringKind `protobuf:"varint,5,opt,name=description_kind,json=descriptionKind,proto3,enum=tfplugin6.StringKind" json:"description_kind,omitempty"` + Deprecated bool `protobuf:"varint,6,opt,name=deprecated,proto3" json:"deprecated,omitempty"` +} + +func (x *Schema_Block) Reset() { + *x = Schema_Block{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Schema_Block) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Schema_Block) ProtoMessage() {} + +func (x *Schema_Block) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Schema_Block.ProtoReflect.Descriptor instead. +func (*Schema_Block) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{5, 0} +} + +func (x *Schema_Block) GetVersion() int64 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *Schema_Block) GetAttributes() []*Schema_Attribute { + if x != nil { + return x.Attributes + } + return nil +} + +func (x *Schema_Block) GetBlockTypes() []*Schema_NestedBlock { + if x != nil { + return x.BlockTypes + } + return nil +} + +func (x *Schema_Block) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Schema_Block) GetDescriptionKind() StringKind { + if x != nil { + return x.DescriptionKind + } + return StringKind_PLAIN +} + +func (x *Schema_Block) GetDeprecated() bool { + if x != nil { + return x.Deprecated + } + return false +} + +type Schema_Attribute struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Type []byte `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + NestedType *Schema_Object `protobuf:"bytes,10,opt,name=nested_type,json=nestedType,proto3" json:"nested_type,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + Required bool `protobuf:"varint,4,opt,name=required,proto3" json:"required,omitempty"` + Optional bool `protobuf:"varint,5,opt,name=optional,proto3" json:"optional,omitempty"` + Computed bool `protobuf:"varint,6,opt,name=computed,proto3" json:"computed,omitempty"` + Sensitive bool `protobuf:"varint,7,opt,name=sensitive,proto3" json:"sensitive,omitempty"` + DescriptionKind StringKind `protobuf:"varint,8,opt,name=description_kind,json=descriptionKind,proto3,enum=tfplugin6.StringKind" json:"description_kind,omitempty"` + Deprecated bool `protobuf:"varint,9,opt,name=deprecated,proto3" json:"deprecated,omitempty"` +} + +func (x *Schema_Attribute) Reset() { + *x = Schema_Attribute{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Schema_Attribute) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Schema_Attribute) ProtoMessage() {} + +func (x *Schema_Attribute) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Schema_Attribute.ProtoReflect.Descriptor instead. +func (*Schema_Attribute) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{5, 1} +} + +func (x *Schema_Attribute) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Schema_Attribute) GetType() []byte { + if x != nil { + return x.Type + } + return nil +} + +func (x *Schema_Attribute) GetNestedType() *Schema_Object { + if x != nil { + return x.NestedType + } + return nil +} + +func (x *Schema_Attribute) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Schema_Attribute) GetRequired() bool { + if x != nil { + return x.Required + } + return false +} + +func (x *Schema_Attribute) GetOptional() bool { + if x != nil { + return x.Optional + } + return false +} + +func (x *Schema_Attribute) GetComputed() bool { + if x != nil { + return x.Computed + } + return false +} + +func (x *Schema_Attribute) GetSensitive() bool { + if x != nil { + return x.Sensitive + } + return false +} + +func (x *Schema_Attribute) GetDescriptionKind() StringKind { + if x != nil { + return x.DescriptionKind + } + return StringKind_PLAIN +} + +func (x *Schema_Attribute) GetDeprecated() bool { + if x != nil { + return x.Deprecated + } + return false +} + +type Schema_NestedBlock struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` + Block *Schema_Block `protobuf:"bytes,2,opt,name=block,proto3" json:"block,omitempty"` + Nesting Schema_NestedBlock_NestingMode `protobuf:"varint,3,opt,name=nesting,proto3,enum=tfplugin6.Schema_NestedBlock_NestingMode" json:"nesting,omitempty"` + MinItems int64 `protobuf:"varint,4,opt,name=min_items,json=minItems,proto3" json:"min_items,omitempty"` + MaxItems int64 `protobuf:"varint,5,opt,name=max_items,json=maxItems,proto3" json:"max_items,omitempty"` +} + +func (x *Schema_NestedBlock) Reset() { + *x = Schema_NestedBlock{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Schema_NestedBlock) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Schema_NestedBlock) ProtoMessage() {} + +func (x *Schema_NestedBlock) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Schema_NestedBlock.ProtoReflect.Descriptor instead. +func (*Schema_NestedBlock) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{5, 2} +} + +func (x *Schema_NestedBlock) GetTypeName() string { + if x != nil { + return x.TypeName + } + return "" +} + +func (x *Schema_NestedBlock) GetBlock() *Schema_Block { + if x != nil { + return x.Block + } + return nil +} + +func (x *Schema_NestedBlock) GetNesting() Schema_NestedBlock_NestingMode { + if x != nil { + return x.Nesting + } + return Schema_NestedBlock_INVALID +} + +func (x *Schema_NestedBlock) GetMinItems() int64 { + if x != nil { + return x.MinItems + } + return 0 +} + +func (x *Schema_NestedBlock) GetMaxItems() int64 { + if x != nil { + return x.MaxItems + } + return 0 +} + +type Schema_Object struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Attributes []*Schema_Attribute `protobuf:"bytes,1,rep,name=attributes,proto3" json:"attributes,omitempty"` + Nesting Schema_Object_NestingMode `protobuf:"varint,3,opt,name=nesting,proto3,enum=tfplugin6.Schema_Object_NestingMode" json:"nesting,omitempty"` + MinItems int64 `protobuf:"varint,4,opt,name=min_items,json=minItems,proto3" json:"min_items,omitempty"` + MaxItems int64 `protobuf:"varint,5,opt,name=max_items,json=maxItems,proto3" json:"max_items,omitempty"` +} + +func (x *Schema_Object) Reset() { + *x = Schema_Object{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Schema_Object) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Schema_Object) ProtoMessage() {} + +func (x *Schema_Object) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Schema_Object.ProtoReflect.Descriptor instead. +func (*Schema_Object) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{5, 3} +} + +func (x *Schema_Object) GetAttributes() []*Schema_Attribute { + if x != nil { + return x.Attributes + } + return nil +} + +func (x *Schema_Object) GetNesting() Schema_Object_NestingMode { + if x != nil { + return x.Nesting + } + return Schema_Object_INVALID +} + +func (x *Schema_Object) GetMinItems() int64 { + if x != nil { + return x.MinItems + } + return 0 +} + +func (x *Schema_Object) GetMaxItems() int64 { + if x != nil { + return x.MaxItems + } + return 0 +} + +type GetProviderSchema_Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GetProviderSchema_Request) Reset() { + *x = GetProviderSchema_Request{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetProviderSchema_Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetProviderSchema_Request) ProtoMessage() {} + +func (x *GetProviderSchema_Request) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetProviderSchema_Request.ProtoReflect.Descriptor instead. +func (*GetProviderSchema_Request) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{6, 0} +} + +type GetProviderSchema_Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Provider *Schema `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` + ResourceSchemas map[string]*Schema `protobuf:"bytes,2,rep,name=resource_schemas,json=resourceSchemas,proto3" json:"resource_schemas,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + DataSourceSchemas map[string]*Schema `protobuf:"bytes,3,rep,name=data_source_schemas,json=dataSourceSchemas,proto3" json:"data_source_schemas,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Diagnostics []*Diagnostic `protobuf:"bytes,4,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` + ProviderMeta *Schema `protobuf:"bytes,5,opt,name=provider_meta,json=providerMeta,proto3" json:"provider_meta,omitempty"` +} + +func (x *GetProviderSchema_Response) Reset() { + *x = GetProviderSchema_Response{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetProviderSchema_Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetProviderSchema_Response) ProtoMessage() {} + +func (x *GetProviderSchema_Response) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[26] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetProviderSchema_Response.ProtoReflect.Descriptor instead. +func (*GetProviderSchema_Response) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{6, 1} +} + +func (x *GetProviderSchema_Response) GetProvider() *Schema { + if x != nil { + return x.Provider + } + return nil +} + +func (x *GetProviderSchema_Response) GetResourceSchemas() map[string]*Schema { + if x != nil { + return x.ResourceSchemas + } + return nil +} + +func (x *GetProviderSchema_Response) GetDataSourceSchemas() map[string]*Schema { + if x != nil { + return x.DataSourceSchemas + } + return nil +} + +func (x *GetProviderSchema_Response) GetDiagnostics() []*Diagnostic { + if x != nil { + return x.Diagnostics + } + return nil +} + +func (x *GetProviderSchema_Response) GetProviderMeta() *Schema { + if x != nil { + return x.ProviderMeta + } + return nil +} + +type ValidateProviderConfig_Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Config *DynamicValue `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` +} + +func (x *ValidateProviderConfig_Request) Reset() { + *x = ValidateProviderConfig_Request{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValidateProviderConfig_Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValidateProviderConfig_Request) ProtoMessage() {} + +func (x *ValidateProviderConfig_Request) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValidateProviderConfig_Request.ProtoReflect.Descriptor instead. +func (*ValidateProviderConfig_Request) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{7, 0} +} + +func (x *ValidateProviderConfig_Request) GetConfig() *DynamicValue { + if x != nil { + return x.Config + } + return nil +} + +type ValidateProviderConfig_Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` +} + +func (x *ValidateProviderConfig_Response) Reset() { + *x = ValidateProviderConfig_Response{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValidateProviderConfig_Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValidateProviderConfig_Response) ProtoMessage() {} + +func (x *ValidateProviderConfig_Response) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValidateProviderConfig_Response.ProtoReflect.Descriptor instead. +func (*ValidateProviderConfig_Response) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{7, 1} +} + +func (x *ValidateProviderConfig_Response) GetDiagnostics() []*Diagnostic { + if x != nil { + return x.Diagnostics + } + return nil +} + +type UpgradeResourceState_Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` + // version is the schema_version number recorded in the state file + Version int64 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` + // raw_state is the raw states as stored for the resource. Core does + // not have access to the schema of prior_version, so it's the + // provider's responsibility to interpret this value using the + // appropriate older schema. The raw_state will be the json encoded + // state, or a legacy flat-mapped format. + RawState *RawState `protobuf:"bytes,3,opt,name=raw_state,json=rawState,proto3" json:"raw_state,omitempty"` +} + +func (x *UpgradeResourceState_Request) Reset() { + *x = UpgradeResourceState_Request{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpgradeResourceState_Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpgradeResourceState_Request) ProtoMessage() {} + +func (x *UpgradeResourceState_Request) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[31] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpgradeResourceState_Request.ProtoReflect.Descriptor instead. +func (*UpgradeResourceState_Request) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{8, 0} +} + +func (x *UpgradeResourceState_Request) GetTypeName() string { + if x != nil { + return x.TypeName + } + return "" +} + +func (x *UpgradeResourceState_Request) GetVersion() int64 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *UpgradeResourceState_Request) GetRawState() *RawState { + if x != nil { + return x.RawState + } + return nil +} + +type UpgradeResourceState_Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // new_state is a msgpack-encoded data structure that, when interpreted with + // the _current_ schema for this resource type, is functionally equivalent to + // that which was given in prior_state_raw. + UpgradedState *DynamicValue `protobuf:"bytes,1,opt,name=upgraded_state,json=upgradedState,proto3" json:"upgraded_state,omitempty"` + // diagnostics describes any errors encountered during migration that could not + // be safely resolved, and warnings about any possibly-risky assumptions made + // in the upgrade process. + Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` +} + +func (x *UpgradeResourceState_Response) Reset() { + *x = UpgradeResourceState_Response{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpgradeResourceState_Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpgradeResourceState_Response) ProtoMessage() {} + +func (x *UpgradeResourceState_Response) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[32] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpgradeResourceState_Response.ProtoReflect.Descriptor instead. +func (*UpgradeResourceState_Response) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{8, 1} +} + +func (x *UpgradeResourceState_Response) GetUpgradedState() *DynamicValue { + if x != nil { + return x.UpgradedState + } + return nil +} + +func (x *UpgradeResourceState_Response) GetDiagnostics() []*Diagnostic { + if x != nil { + return x.Diagnostics + } + return nil +} + +type ValidateResourceConfig_Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` + Config *DynamicValue `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` +} + +func (x *ValidateResourceConfig_Request) Reset() { + *x = ValidateResourceConfig_Request{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValidateResourceConfig_Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValidateResourceConfig_Request) ProtoMessage() {} + +func (x *ValidateResourceConfig_Request) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[33] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValidateResourceConfig_Request.ProtoReflect.Descriptor instead. +func (*ValidateResourceConfig_Request) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{9, 0} +} + +func (x *ValidateResourceConfig_Request) GetTypeName() string { + if x != nil { + return x.TypeName + } + return "" +} + +func (x *ValidateResourceConfig_Request) GetConfig() *DynamicValue { + if x != nil { + return x.Config + } + return nil +} + +type ValidateResourceConfig_Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Diagnostics []*Diagnostic `protobuf:"bytes,1,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` +} + +func (x *ValidateResourceConfig_Response) Reset() { + *x = ValidateResourceConfig_Response{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValidateResourceConfig_Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValidateResourceConfig_Response) ProtoMessage() {} + +func (x *ValidateResourceConfig_Response) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[34] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValidateResourceConfig_Response.ProtoReflect.Descriptor instead. +func (*ValidateResourceConfig_Response) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{9, 1} +} + +func (x *ValidateResourceConfig_Response) GetDiagnostics() []*Diagnostic { + if x != nil { + return x.Diagnostics + } + return nil +} + +type ValidateDataResourceConfig_Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` + Config *DynamicValue `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` +} + +func (x *ValidateDataResourceConfig_Request) Reset() { + *x = ValidateDataResourceConfig_Request{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[35] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValidateDataResourceConfig_Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValidateDataResourceConfig_Request) ProtoMessage() {} + +func (x *ValidateDataResourceConfig_Request) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[35] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValidateDataResourceConfig_Request.ProtoReflect.Descriptor instead. +func (*ValidateDataResourceConfig_Request) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{10, 0} +} + +func (x *ValidateDataResourceConfig_Request) GetTypeName() string { + if x != nil { + return x.TypeName + } + return "" +} + +func (x *ValidateDataResourceConfig_Request) GetConfig() *DynamicValue { + if x != nil { + return x.Config + } + return nil +} + +type ValidateDataResourceConfig_Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Diagnostics []*Diagnostic `protobuf:"bytes,1,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` +} + +func (x *ValidateDataResourceConfig_Response) Reset() { + *x = ValidateDataResourceConfig_Response{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[36] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValidateDataResourceConfig_Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValidateDataResourceConfig_Response) ProtoMessage() {} + +func (x *ValidateDataResourceConfig_Response) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[36] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValidateDataResourceConfig_Response.ProtoReflect.Descriptor instead. +func (*ValidateDataResourceConfig_Response) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{10, 1} +} + +func (x *ValidateDataResourceConfig_Response) GetDiagnostics() []*Diagnostic { + if x != nil { + return x.Diagnostics + } + return nil +} + +type ConfigureProvider_Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TerraformVersion string `protobuf:"bytes,1,opt,name=terraform_version,json=terraformVersion,proto3" json:"terraform_version,omitempty"` + Config *DynamicValue `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` +} + +func (x *ConfigureProvider_Request) Reset() { + *x = ConfigureProvider_Request{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConfigureProvider_Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConfigureProvider_Request) ProtoMessage() {} + +func (x *ConfigureProvider_Request) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[37] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConfigureProvider_Request.ProtoReflect.Descriptor instead. +func (*ConfigureProvider_Request) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{11, 0} +} + +func (x *ConfigureProvider_Request) GetTerraformVersion() string { + if x != nil { + return x.TerraformVersion + } + return "" +} + +func (x *ConfigureProvider_Request) GetConfig() *DynamicValue { + if x != nil { + return x.Config + } + return nil +} + +type ConfigureProvider_Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Diagnostics []*Diagnostic `protobuf:"bytes,1,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` +} + +func (x *ConfigureProvider_Response) Reset() { + *x = ConfigureProvider_Response{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConfigureProvider_Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConfigureProvider_Response) ProtoMessage() {} + +func (x *ConfigureProvider_Response) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[38] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConfigureProvider_Response.ProtoReflect.Descriptor instead. +func (*ConfigureProvider_Response) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{11, 1} +} + +func (x *ConfigureProvider_Response) GetDiagnostics() []*Diagnostic { + if x != nil { + return x.Diagnostics + } + return nil +} + +type ReadResource_Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` + CurrentState *DynamicValue `protobuf:"bytes,2,opt,name=current_state,json=currentState,proto3" json:"current_state,omitempty"` + Private []byte `protobuf:"bytes,3,opt,name=private,proto3" json:"private,omitempty"` + ProviderMeta *DynamicValue `protobuf:"bytes,4,opt,name=provider_meta,json=providerMeta,proto3" json:"provider_meta,omitempty"` +} + +func (x *ReadResource_Request) Reset() { + *x = ReadResource_Request{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[39] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadResource_Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadResource_Request) ProtoMessage() {} + +func (x *ReadResource_Request) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[39] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadResource_Request.ProtoReflect.Descriptor instead. +func (*ReadResource_Request) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{12, 0} +} + +func (x *ReadResource_Request) GetTypeName() string { + if x != nil { + return x.TypeName + } + return "" +} + +func (x *ReadResource_Request) GetCurrentState() *DynamicValue { + if x != nil { + return x.CurrentState + } + return nil +} + +func (x *ReadResource_Request) GetPrivate() []byte { + if x != nil { + return x.Private + } + return nil +} + +func (x *ReadResource_Request) GetProviderMeta() *DynamicValue { + if x != nil { + return x.ProviderMeta + } + return nil +} + +type ReadResource_Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NewState *DynamicValue `protobuf:"bytes,1,opt,name=new_state,json=newState,proto3" json:"new_state,omitempty"` + Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` + Private []byte `protobuf:"bytes,3,opt,name=private,proto3" json:"private,omitempty"` +} + +func (x *ReadResource_Response) Reset() { + *x = ReadResource_Response{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[40] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadResource_Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadResource_Response) ProtoMessage() {} + +func (x *ReadResource_Response) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[40] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadResource_Response.ProtoReflect.Descriptor instead. +func (*ReadResource_Response) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{12, 1} +} + +func (x *ReadResource_Response) GetNewState() *DynamicValue { + if x != nil { + return x.NewState + } + return nil +} + +func (x *ReadResource_Response) GetDiagnostics() []*Diagnostic { + if x != nil { + return x.Diagnostics + } + return nil +} + +func (x *ReadResource_Response) GetPrivate() []byte { + if x != nil { + return x.Private + } + return nil +} + +type PlanResourceChange_Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` + PriorState *DynamicValue `protobuf:"bytes,2,opt,name=prior_state,json=priorState,proto3" json:"prior_state,omitempty"` + ProposedNewState *DynamicValue `protobuf:"bytes,3,opt,name=proposed_new_state,json=proposedNewState,proto3" json:"proposed_new_state,omitempty"` + Config *DynamicValue `protobuf:"bytes,4,opt,name=config,proto3" json:"config,omitempty"` + PriorPrivate []byte `protobuf:"bytes,5,opt,name=prior_private,json=priorPrivate,proto3" json:"prior_private,omitempty"` + ProviderMeta *DynamicValue `protobuf:"bytes,6,opt,name=provider_meta,json=providerMeta,proto3" json:"provider_meta,omitempty"` +} + +func (x *PlanResourceChange_Request) Reset() { + *x = PlanResourceChange_Request{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PlanResourceChange_Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PlanResourceChange_Request) ProtoMessage() {} + +func (x *PlanResourceChange_Request) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[41] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PlanResourceChange_Request.ProtoReflect.Descriptor instead. +func (*PlanResourceChange_Request) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{13, 0} +} + +func (x *PlanResourceChange_Request) GetTypeName() string { + if x != nil { + return x.TypeName + } + return "" +} + +func (x *PlanResourceChange_Request) GetPriorState() *DynamicValue { + if x != nil { + return x.PriorState + } + return nil +} + +func (x *PlanResourceChange_Request) GetProposedNewState() *DynamicValue { + if x != nil { + return x.ProposedNewState + } + return nil +} + +func (x *PlanResourceChange_Request) GetConfig() *DynamicValue { + if x != nil { + return x.Config + } + return nil +} + +func (x *PlanResourceChange_Request) GetPriorPrivate() []byte { + if x != nil { + return x.PriorPrivate + } + return nil +} + +func (x *PlanResourceChange_Request) GetProviderMeta() *DynamicValue { + if x != nil { + return x.ProviderMeta + } + return nil +} + +type PlanResourceChange_Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PlannedState *DynamicValue `protobuf:"bytes,1,opt,name=planned_state,json=plannedState,proto3" json:"planned_state,omitempty"` + RequiresReplace []*AttributePath `protobuf:"bytes,2,rep,name=requires_replace,json=requiresReplace,proto3" json:"requires_replace,omitempty"` + PlannedPrivate []byte `protobuf:"bytes,3,opt,name=planned_private,json=plannedPrivate,proto3" json:"planned_private,omitempty"` + Diagnostics []*Diagnostic `protobuf:"bytes,4,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` +} + +func (x *PlanResourceChange_Response) Reset() { + *x = PlanResourceChange_Response{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PlanResourceChange_Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PlanResourceChange_Response) ProtoMessage() {} + +func (x *PlanResourceChange_Response) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[42] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PlanResourceChange_Response.ProtoReflect.Descriptor instead. +func (*PlanResourceChange_Response) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{13, 1} +} + +func (x *PlanResourceChange_Response) GetPlannedState() *DynamicValue { + if x != nil { + return x.PlannedState + } + return nil +} + +func (x *PlanResourceChange_Response) GetRequiresReplace() []*AttributePath { + if x != nil { + return x.RequiresReplace + } + return nil +} + +func (x *PlanResourceChange_Response) GetPlannedPrivate() []byte { + if x != nil { + return x.PlannedPrivate + } + return nil +} + +func (x *PlanResourceChange_Response) GetDiagnostics() []*Diagnostic { + if x != nil { + return x.Diagnostics + } + return nil +} + +type ApplyResourceChange_Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` + PriorState *DynamicValue `protobuf:"bytes,2,opt,name=prior_state,json=priorState,proto3" json:"prior_state,omitempty"` + PlannedState *DynamicValue `protobuf:"bytes,3,opt,name=planned_state,json=plannedState,proto3" json:"planned_state,omitempty"` + Config *DynamicValue `protobuf:"bytes,4,opt,name=config,proto3" json:"config,omitempty"` + PlannedPrivate []byte `protobuf:"bytes,5,opt,name=planned_private,json=plannedPrivate,proto3" json:"planned_private,omitempty"` + ProviderMeta *DynamicValue `protobuf:"bytes,6,opt,name=provider_meta,json=providerMeta,proto3" json:"provider_meta,omitempty"` +} + +func (x *ApplyResourceChange_Request) Reset() { + *x = ApplyResourceChange_Request{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[43] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ApplyResourceChange_Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApplyResourceChange_Request) ProtoMessage() {} + +func (x *ApplyResourceChange_Request) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[43] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ApplyResourceChange_Request.ProtoReflect.Descriptor instead. +func (*ApplyResourceChange_Request) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{14, 0} +} + +func (x *ApplyResourceChange_Request) GetTypeName() string { + if x != nil { + return x.TypeName + } + return "" +} + +func (x *ApplyResourceChange_Request) GetPriorState() *DynamicValue { + if x != nil { + return x.PriorState + } + return nil +} + +func (x *ApplyResourceChange_Request) GetPlannedState() *DynamicValue { + if x != nil { + return x.PlannedState + } + return nil +} + +func (x *ApplyResourceChange_Request) GetConfig() *DynamicValue { + if x != nil { + return x.Config + } + return nil +} + +func (x *ApplyResourceChange_Request) GetPlannedPrivate() []byte { + if x != nil { + return x.PlannedPrivate + } + return nil +} + +func (x *ApplyResourceChange_Request) GetProviderMeta() *DynamicValue { + if x != nil { + return x.ProviderMeta + } + return nil +} + +type ApplyResourceChange_Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NewState *DynamicValue `protobuf:"bytes,1,opt,name=new_state,json=newState,proto3" json:"new_state,omitempty"` + Private []byte `protobuf:"bytes,2,opt,name=private,proto3" json:"private,omitempty"` + Diagnostics []*Diagnostic `protobuf:"bytes,3,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` +} + +func (x *ApplyResourceChange_Response) Reset() { + *x = ApplyResourceChange_Response{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ApplyResourceChange_Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApplyResourceChange_Response) ProtoMessage() {} + +func (x *ApplyResourceChange_Response) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[44] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ApplyResourceChange_Response.ProtoReflect.Descriptor instead. +func (*ApplyResourceChange_Response) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{14, 1} +} + +func (x *ApplyResourceChange_Response) GetNewState() *DynamicValue { + if x != nil { + return x.NewState + } + return nil +} + +func (x *ApplyResourceChange_Response) GetPrivate() []byte { + if x != nil { + return x.Private + } + return nil +} + +func (x *ApplyResourceChange_Response) GetDiagnostics() []*Diagnostic { + if x != nil { + return x.Diagnostics + } + return nil +} + +type ImportResourceState_Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *ImportResourceState_Request) Reset() { + *x = ImportResourceState_Request{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ImportResourceState_Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ImportResourceState_Request) ProtoMessage() {} + +func (x *ImportResourceState_Request) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[45] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ImportResourceState_Request.ProtoReflect.Descriptor instead. +func (*ImportResourceState_Request) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{15, 0} +} + +func (x *ImportResourceState_Request) GetTypeName() string { + if x != nil { + return x.TypeName + } + return "" +} + +func (x *ImportResourceState_Request) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type ImportResourceState_ImportedResource struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` + State *DynamicValue `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` + Private []byte `protobuf:"bytes,3,opt,name=private,proto3" json:"private,omitempty"` +} + +func (x *ImportResourceState_ImportedResource) Reset() { + *x = ImportResourceState_ImportedResource{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[46] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ImportResourceState_ImportedResource) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ImportResourceState_ImportedResource) ProtoMessage() {} + +func (x *ImportResourceState_ImportedResource) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[46] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ImportResourceState_ImportedResource.ProtoReflect.Descriptor instead. +func (*ImportResourceState_ImportedResource) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{15, 1} +} + +func (x *ImportResourceState_ImportedResource) GetTypeName() string { + if x != nil { + return x.TypeName + } + return "" +} + +func (x *ImportResourceState_ImportedResource) GetState() *DynamicValue { + if x != nil { + return x.State + } + return nil +} + +func (x *ImportResourceState_ImportedResource) GetPrivate() []byte { + if x != nil { + return x.Private + } + return nil +} + +type ImportResourceState_Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ImportedResources []*ImportResourceState_ImportedResource `protobuf:"bytes,1,rep,name=imported_resources,json=importedResources,proto3" json:"imported_resources,omitempty"` + Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` +} + +func (x *ImportResourceState_Response) Reset() { + *x = ImportResourceState_Response{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[47] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ImportResourceState_Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ImportResourceState_Response) ProtoMessage() {} + +func (x *ImportResourceState_Response) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[47] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ImportResourceState_Response.ProtoReflect.Descriptor instead. +func (*ImportResourceState_Response) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{15, 2} +} + +func (x *ImportResourceState_Response) GetImportedResources() []*ImportResourceState_ImportedResource { + if x != nil { + return x.ImportedResources + } + return nil +} + +func (x *ImportResourceState_Response) GetDiagnostics() []*Diagnostic { + if x != nil { + return x.Diagnostics + } + return nil +} + +type ReadDataSource_Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TypeName string `protobuf:"bytes,1,opt,name=type_name,json=typeName,proto3" json:"type_name,omitempty"` + Config *DynamicValue `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` + ProviderMeta *DynamicValue `protobuf:"bytes,3,opt,name=provider_meta,json=providerMeta,proto3" json:"provider_meta,omitempty"` +} + +func (x *ReadDataSource_Request) Reset() { + *x = ReadDataSource_Request{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[48] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadDataSource_Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadDataSource_Request) ProtoMessage() {} + +func (x *ReadDataSource_Request) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[48] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadDataSource_Request.ProtoReflect.Descriptor instead. +func (*ReadDataSource_Request) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{16, 0} +} + +func (x *ReadDataSource_Request) GetTypeName() string { + if x != nil { + return x.TypeName + } + return "" +} + +func (x *ReadDataSource_Request) GetConfig() *DynamicValue { + if x != nil { + return x.Config + } + return nil +} + +func (x *ReadDataSource_Request) GetProviderMeta() *DynamicValue { + if x != nil { + return x.ProviderMeta + } + return nil +} + +type ReadDataSource_Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + State *DynamicValue `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` + Diagnostics []*Diagnostic `protobuf:"bytes,2,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` +} + +func (x *ReadDataSource_Response) Reset() { + *x = ReadDataSource_Response{} + if protoimpl.UnsafeEnabled { + mi := &file_tfplugin6_proto_msgTypes[49] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadDataSource_Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadDataSource_Response) ProtoMessage() {} + +func (x *ReadDataSource_Response) ProtoReflect() protoreflect.Message { + mi := &file_tfplugin6_proto_msgTypes[49] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadDataSource_Response.ProtoReflect.Descriptor instead. +func (*ReadDataSource_Response) Descriptor() ([]byte, []int) { + return file_tfplugin6_proto_rawDescGZIP(), []int{16, 1} +} + +func (x *ReadDataSource_Response) GetState() *DynamicValue { + if x != nil { + return x.State + } + return nil +} + +func (x *ReadDataSource_Response) GetDiagnostics() []*Diagnostic { + if x != nil { + return x.Diagnostics + } + return nil +} + +var File_tfplugin6_proto protoreflect.FileDescriptor + +var file_tfplugin6_proto_rawDesc = []byte{ + 0x0a, 0x0f, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x09, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x22, 0x3c, 0x0a, 0x0c, + 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x6d, 0x73, 0x67, 0x70, 0x61, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, + 0x73, 0x67, 0x70, 0x61, 0x63, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6a, 0x73, 0x6f, 0x6e, 0x22, 0xe3, 0x01, 0x0a, 0x0a, 0x44, + 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x12, 0x3a, 0x0a, 0x08, 0x73, 0x65, 0x76, + 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x74, 0x66, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, + 0x69, 0x63, 0x2e, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x52, 0x08, 0x73, 0x65, 0x76, + 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, + 0x16, 0x0a, 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x36, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x50, 0x61, 0x74, 0x68, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x22, + 0x2f, 0x0a, 0x08, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x0b, 0x0a, 0x07, 0x49, + 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, + 0x52, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x02, + 0x22, 0xdc, 0x01, 0x0a, 0x0d, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x50, 0x61, + 0x74, 0x68, 0x12, 0x33, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x2e, 0x53, 0x74, 0x65, 0x70, + 0x52, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x1a, 0x95, 0x01, 0x0a, 0x04, 0x53, 0x74, 0x65, 0x70, + 0x12, 0x27, 0x0a, 0x0e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x10, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x4b, 0x65, 0x79, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x28, 0x0a, 0x0f, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x48, 0x00, 0x52, 0x0d, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, + 0x49, 0x6e, 0x74, 0x42, 0x0a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, + 0x3b, 0x0a, 0x0c, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x1a, + 0x09, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x0a, 0x08, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x96, 0x01, 0x0a, + 0x08, 0x52, 0x61, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6a, 0x73, 0x6f, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x6a, 0x73, 0x6f, 0x6e, 0x12, 0x3a, 0x0a, + 0x07, 0x66, 0x6c, 0x61, 0x74, 0x6d, 0x61, 0x70, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x52, 0x61, 0x77, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x2e, 0x46, 0x6c, 0x61, 0x74, 0x6d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x07, 0x66, 0x6c, 0x61, 0x74, 0x6d, 0x61, 0x70, 0x1a, 0x3a, 0x0a, 0x0c, 0x46, 0x6c, 0x61, + 0x74, 0x6d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8d, 0x0a, 0x0a, 0x06, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x05, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x1a, 0xa2, 0x02, 0x0a, 0x05, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, + 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x0a, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x0b, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1d, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x2e, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x0a, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x10, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x69, 0x6e, 0x64, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x36, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x0f, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1e, + 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x1a, 0xe4, + 0x02, 0x0a, 0x09, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x52, 0x0a, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x1a, 0x0a, + 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6d, + 0x70, 0x75, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x6f, 0x6d, + 0x70, 0x75, 0x74, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, + 0x76, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, + 0x69, 0x76, 0x65, 0x12, 0x40, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, + 0x74, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, + 0x63, 0x61, 0x74, 0x65, 0x64, 0x1a, 0xa7, 0x02, 0x0a, 0x0b, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x12, 0x43, 0x0a, 0x07, 0x6e, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x2e, 0x4e, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x6e, + 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6e, 0x5f, 0x69, 0x74, + 0x65, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x49, 0x74, + 0x65, 0x6d, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x49, 0x74, 0x65, 0x6d, 0x73, + 0x22, 0x4d, 0x0a, 0x0b, 0x4e, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x12, + 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, + 0x53, 0x49, 0x4e, 0x47, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x49, 0x53, 0x54, + 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x45, 0x54, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x4d, + 0x41, 0x50, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x10, 0x05, 0x1a, + 0x83, 0x02, 0x0a, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x3b, 0x0a, 0x0a, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x0a, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x07, 0x6e, 0x65, 0x73, 0x74, 0x69, + 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x2e, 0x4e, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x07, + 0x6e, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6e, 0x5f, 0x69, + 0x74, 0x65, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x49, + 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x74, 0x65, 0x6d, + 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x49, 0x74, 0x65, 0x6d, + 0x73, 0x22, 0x42, 0x0a, 0x0b, 0x4e, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x64, 0x65, + 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, + 0x06, 0x53, 0x49, 0x4e, 0x47, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x49, 0x53, + 0x54, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x45, 0x54, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, + 0x4d, 0x41, 0x50, 0x10, 0x04, 0x22, 0xd0, 0x04, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x1a, 0x09, 0x0a, 0x07, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0xaf, 0x04, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x36, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x12, 0x65, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x12, 0x6c, 0x0a, 0x13, 0x64, 0x61, 0x74, + 0x61, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x36, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x44, 0x61, + 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x64, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, + 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, + 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, + 0x12, 0x36, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, + 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x55, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x27, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, + 0x57, 0x0a, 0x16, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x27, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x99, 0x01, 0x0a, 0x16, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x1a, 0x3a, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, + 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, + 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, + 0x43, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, + 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, + 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, + 0x74, 0x69, 0x63, 0x73, 0x22, 0x90, 0x02, 0x0a, 0x14, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x72, 0x0a, + 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x30, 0x0a, 0x09, 0x72, 0x61, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x52, + 0x61, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x08, 0x72, 0x61, 0x77, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x1a, 0x83, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, + 0x0a, 0x0e, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x0d, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, + 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, + 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, + 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0xb6, 0x01, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x1a, 0x57, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, + 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x43, 0x0a, 0x08, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, + 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, + 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, + 0x22, 0xba, 0x01, 0x0a, 0x1a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, + 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, + 0x57, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, + 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, + 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x43, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, + 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, + 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0xc1, 0x01, + 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x1a, 0x67, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, + 0x0a, 0x11, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x65, 0x72, 0x72, 0x61, + 0x66, 0x6f, 0x72, 0x6d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2f, 0x0a, 0x06, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x43, 0x0a, 0x08, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, + 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, + 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, + 0x73, 0x22, 0xe3, 0x02, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x1a, 0xbc, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, + 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, + 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, + 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, + 0x6d, 0x65, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, + 0x61, 0x1a, 0x93, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, + 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, + 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, + 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, + 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x18, 0x0a, + 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, + 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x22, 0xc4, 0x04, 0x0a, 0x12, 0x50, 0x6c, 0x61, 0x6e, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x1a, 0xbb, + 0x02, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, + 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, + 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x6f, 0x72, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x45, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x6e, 0x65, + 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, + 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x64, + 0x4e, 0x65, 0x77, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x69, + 0x6f, 0x72, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0c, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x3c, + 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0xef, 0x01, 0x0a, + 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x6c, 0x61, + 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, + 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x6c, 0x61, 0x6e, 0x6e, + 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0f, 0x72, 0x65, 0x71, + 0x75, 0x69, 0x72, 0x65, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0f, + 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x72, + 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, + 0x74, 0x69, 0x63, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, + 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0xe4, + 0x03, 0x0a, 0x13, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x1a, 0xb6, 0x02, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x38, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, + 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x70, + 0x72, 0x69, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x6c, 0x61, + 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, + 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x70, 0x6c, 0x61, 0x6e, 0x6e, + 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x6c, 0x61, 0x6e, + 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0e, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, 0x65, + 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x1a, + 0x93, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x09, + 0x6e, 0x65, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, + 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, + 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, + 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, + 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0xed, 0x02, 0x0a, 0x13, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x36, 0x0a, + 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x1a, 0x78, 0x0a, 0x10, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, + 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x1a, + 0xa3, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x12, + 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x11, 0x69, 0x6d, 0x70, 0x6f, 0x72, + 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0b, + 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, + 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, + 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0x9c, 0x02, 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, + 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0x95, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x79, 0x70, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, + 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x6d, + 0x65, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, + 0x1a, 0x72, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x66, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x64, + 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x44, 0x69, 0x61, + 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, + 0x74, 0x69, 0x63, 0x73, 0x2a, 0x25, 0x0a, 0x0a, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4b, 0x69, + 0x6e, 0x64, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x4c, 0x41, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x0c, 0x0a, + 0x08, 0x4d, 0x41, 0x52, 0x4b, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x01, 0x32, 0xcc, 0x09, 0x0a, 0x08, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x60, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x24, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, + 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x16, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, + 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2a, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x16, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2a, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7b, 0x0a, 0x1a, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, + 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x61, + 0x74, 0x61, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, 0x14, 0x55, 0x70, 0x67, + 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x55, 0x70, + 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x74, 0x66, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x55, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, + 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x24, 0x2e, 0x74, 0x66, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x25, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x36, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x36, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x12, 0x50, 0x6c, 0x61, + 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, + 0x25, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x50, 0x6c, 0x61, 0x6e, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x36, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, + 0x0a, 0x13, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x26, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x36, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x13, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x26, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x36, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, + 0x0a, 0x0e, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x12, 0x21, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x52, 0x65, 0x61, + 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x2e, + 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0c, 0x53, 0x74, 0x6f, 0x70, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x36, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, + 0x72, 0x70, 0x2f, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x66, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x36, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_tfplugin6_proto_rawDescOnce sync.Once + file_tfplugin6_proto_rawDescData = file_tfplugin6_proto_rawDesc +) + +func file_tfplugin6_proto_rawDescGZIP() []byte { + file_tfplugin6_proto_rawDescOnce.Do(func() { + file_tfplugin6_proto_rawDescData = protoimpl.X.CompressGZIP(file_tfplugin6_proto_rawDescData) + }) + return file_tfplugin6_proto_rawDescData +} + +var file_tfplugin6_proto_enumTypes = make([]protoimpl.EnumInfo, 4) +var file_tfplugin6_proto_msgTypes = make([]protoimpl.MessageInfo, 50) +var file_tfplugin6_proto_goTypes = []interface{}{ + (StringKind)(0), // 0: tfplugin6.StringKind + (Diagnostic_Severity)(0), // 1: tfplugin6.Diagnostic.Severity + (Schema_NestedBlock_NestingMode)(0), // 2: tfplugin6.Schema.NestedBlock.NestingMode + (Schema_Object_NestingMode)(0), // 3: tfplugin6.Schema.Object.NestingMode + (*DynamicValue)(nil), // 4: tfplugin6.DynamicValue + (*Diagnostic)(nil), // 5: tfplugin6.Diagnostic + (*AttributePath)(nil), // 6: tfplugin6.AttributePath + (*StopProvider)(nil), // 7: tfplugin6.StopProvider + (*RawState)(nil), // 8: tfplugin6.RawState + (*Schema)(nil), // 9: tfplugin6.Schema + (*GetProviderSchema)(nil), // 10: tfplugin6.GetProviderSchema + (*ValidateProviderConfig)(nil), // 11: tfplugin6.ValidateProviderConfig + (*UpgradeResourceState)(nil), // 12: tfplugin6.UpgradeResourceState + (*ValidateResourceConfig)(nil), // 13: tfplugin6.ValidateResourceConfig + (*ValidateDataResourceConfig)(nil), // 14: tfplugin6.ValidateDataResourceConfig + (*ConfigureProvider)(nil), // 15: tfplugin6.ConfigureProvider + (*ReadResource)(nil), // 16: tfplugin6.ReadResource + (*PlanResourceChange)(nil), // 17: tfplugin6.PlanResourceChange + (*ApplyResourceChange)(nil), // 18: tfplugin6.ApplyResourceChange + (*ImportResourceState)(nil), // 19: tfplugin6.ImportResourceState + (*ReadDataSource)(nil), // 20: tfplugin6.ReadDataSource + (*AttributePath_Step)(nil), // 21: tfplugin6.AttributePath.Step + (*StopProvider_Request)(nil), // 22: tfplugin6.StopProvider.Request + (*StopProvider_Response)(nil), // 23: tfplugin6.StopProvider.Response + nil, // 24: tfplugin6.RawState.FlatmapEntry + (*Schema_Block)(nil), // 25: tfplugin6.Schema.Block + (*Schema_Attribute)(nil), // 26: tfplugin6.Schema.Attribute + (*Schema_NestedBlock)(nil), // 27: tfplugin6.Schema.NestedBlock + (*Schema_Object)(nil), // 28: tfplugin6.Schema.Object + (*GetProviderSchema_Request)(nil), // 29: tfplugin6.GetProviderSchema.Request + (*GetProviderSchema_Response)(nil), // 30: tfplugin6.GetProviderSchema.Response + nil, // 31: tfplugin6.GetProviderSchema.Response.ResourceSchemasEntry + nil, // 32: tfplugin6.GetProviderSchema.Response.DataSourceSchemasEntry + (*ValidateProviderConfig_Request)(nil), // 33: tfplugin6.ValidateProviderConfig.Request + (*ValidateProviderConfig_Response)(nil), // 34: tfplugin6.ValidateProviderConfig.Response + (*UpgradeResourceState_Request)(nil), // 35: tfplugin6.UpgradeResourceState.Request + (*UpgradeResourceState_Response)(nil), // 36: tfplugin6.UpgradeResourceState.Response + (*ValidateResourceConfig_Request)(nil), // 37: tfplugin6.ValidateResourceConfig.Request + (*ValidateResourceConfig_Response)(nil), // 38: tfplugin6.ValidateResourceConfig.Response + (*ValidateDataResourceConfig_Request)(nil), // 39: tfplugin6.ValidateDataResourceConfig.Request + (*ValidateDataResourceConfig_Response)(nil), // 40: tfplugin6.ValidateDataResourceConfig.Response + (*ConfigureProvider_Request)(nil), // 41: tfplugin6.ConfigureProvider.Request + (*ConfigureProvider_Response)(nil), // 42: tfplugin6.ConfigureProvider.Response + (*ReadResource_Request)(nil), // 43: tfplugin6.ReadResource.Request + (*ReadResource_Response)(nil), // 44: tfplugin6.ReadResource.Response + (*PlanResourceChange_Request)(nil), // 45: tfplugin6.PlanResourceChange.Request + (*PlanResourceChange_Response)(nil), // 46: tfplugin6.PlanResourceChange.Response + (*ApplyResourceChange_Request)(nil), // 47: tfplugin6.ApplyResourceChange.Request + (*ApplyResourceChange_Response)(nil), // 48: tfplugin6.ApplyResourceChange.Response + (*ImportResourceState_Request)(nil), // 49: tfplugin6.ImportResourceState.Request + (*ImportResourceState_ImportedResource)(nil), // 50: tfplugin6.ImportResourceState.ImportedResource + (*ImportResourceState_Response)(nil), // 51: tfplugin6.ImportResourceState.Response + (*ReadDataSource_Request)(nil), // 52: tfplugin6.ReadDataSource.Request + (*ReadDataSource_Response)(nil), // 53: tfplugin6.ReadDataSource.Response +} +var file_tfplugin6_proto_depIdxs = []int32{ + 1, // 0: tfplugin6.Diagnostic.severity:type_name -> tfplugin6.Diagnostic.Severity + 6, // 1: tfplugin6.Diagnostic.attribute:type_name -> tfplugin6.AttributePath + 21, // 2: tfplugin6.AttributePath.steps:type_name -> tfplugin6.AttributePath.Step + 24, // 3: tfplugin6.RawState.flatmap:type_name -> tfplugin6.RawState.FlatmapEntry + 25, // 4: tfplugin6.Schema.block:type_name -> tfplugin6.Schema.Block + 26, // 5: tfplugin6.Schema.Block.attributes:type_name -> tfplugin6.Schema.Attribute + 27, // 6: tfplugin6.Schema.Block.block_types:type_name -> tfplugin6.Schema.NestedBlock + 0, // 7: tfplugin6.Schema.Block.description_kind:type_name -> tfplugin6.StringKind + 28, // 8: tfplugin6.Schema.Attribute.nested_type:type_name -> tfplugin6.Schema.Object + 0, // 9: tfplugin6.Schema.Attribute.description_kind:type_name -> tfplugin6.StringKind + 25, // 10: tfplugin6.Schema.NestedBlock.block:type_name -> tfplugin6.Schema.Block + 2, // 11: tfplugin6.Schema.NestedBlock.nesting:type_name -> tfplugin6.Schema.NestedBlock.NestingMode + 26, // 12: tfplugin6.Schema.Object.attributes:type_name -> tfplugin6.Schema.Attribute + 3, // 13: tfplugin6.Schema.Object.nesting:type_name -> tfplugin6.Schema.Object.NestingMode + 9, // 14: tfplugin6.GetProviderSchema.Response.provider:type_name -> tfplugin6.Schema + 31, // 15: tfplugin6.GetProviderSchema.Response.resource_schemas:type_name -> tfplugin6.GetProviderSchema.Response.ResourceSchemasEntry + 32, // 16: tfplugin6.GetProviderSchema.Response.data_source_schemas:type_name -> tfplugin6.GetProviderSchema.Response.DataSourceSchemasEntry + 5, // 17: tfplugin6.GetProviderSchema.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 9, // 18: tfplugin6.GetProviderSchema.Response.provider_meta:type_name -> tfplugin6.Schema + 9, // 19: tfplugin6.GetProviderSchema.Response.ResourceSchemasEntry.value:type_name -> tfplugin6.Schema + 9, // 20: tfplugin6.GetProviderSchema.Response.DataSourceSchemasEntry.value:type_name -> tfplugin6.Schema + 4, // 21: tfplugin6.ValidateProviderConfig.Request.config:type_name -> tfplugin6.DynamicValue + 5, // 22: tfplugin6.ValidateProviderConfig.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 8, // 23: tfplugin6.UpgradeResourceState.Request.raw_state:type_name -> tfplugin6.RawState + 4, // 24: tfplugin6.UpgradeResourceState.Response.upgraded_state:type_name -> tfplugin6.DynamicValue + 5, // 25: tfplugin6.UpgradeResourceState.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 26: tfplugin6.ValidateResourceConfig.Request.config:type_name -> tfplugin6.DynamicValue + 5, // 27: tfplugin6.ValidateResourceConfig.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 28: tfplugin6.ValidateDataResourceConfig.Request.config:type_name -> tfplugin6.DynamicValue + 5, // 29: tfplugin6.ValidateDataResourceConfig.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 30: tfplugin6.ConfigureProvider.Request.config:type_name -> tfplugin6.DynamicValue + 5, // 31: tfplugin6.ConfigureProvider.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 32: tfplugin6.ReadResource.Request.current_state:type_name -> tfplugin6.DynamicValue + 4, // 33: tfplugin6.ReadResource.Request.provider_meta:type_name -> tfplugin6.DynamicValue + 4, // 34: tfplugin6.ReadResource.Response.new_state:type_name -> tfplugin6.DynamicValue + 5, // 35: tfplugin6.ReadResource.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 36: tfplugin6.PlanResourceChange.Request.prior_state:type_name -> tfplugin6.DynamicValue + 4, // 37: tfplugin6.PlanResourceChange.Request.proposed_new_state:type_name -> tfplugin6.DynamicValue + 4, // 38: tfplugin6.PlanResourceChange.Request.config:type_name -> tfplugin6.DynamicValue + 4, // 39: tfplugin6.PlanResourceChange.Request.provider_meta:type_name -> tfplugin6.DynamicValue + 4, // 40: tfplugin6.PlanResourceChange.Response.planned_state:type_name -> tfplugin6.DynamicValue + 6, // 41: tfplugin6.PlanResourceChange.Response.requires_replace:type_name -> tfplugin6.AttributePath + 5, // 42: tfplugin6.PlanResourceChange.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 43: tfplugin6.ApplyResourceChange.Request.prior_state:type_name -> tfplugin6.DynamicValue + 4, // 44: tfplugin6.ApplyResourceChange.Request.planned_state:type_name -> tfplugin6.DynamicValue + 4, // 45: tfplugin6.ApplyResourceChange.Request.config:type_name -> tfplugin6.DynamicValue + 4, // 46: tfplugin6.ApplyResourceChange.Request.provider_meta:type_name -> tfplugin6.DynamicValue + 4, // 47: tfplugin6.ApplyResourceChange.Response.new_state:type_name -> tfplugin6.DynamicValue + 5, // 48: tfplugin6.ApplyResourceChange.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 49: tfplugin6.ImportResourceState.ImportedResource.state:type_name -> tfplugin6.DynamicValue + 50, // 50: tfplugin6.ImportResourceState.Response.imported_resources:type_name -> tfplugin6.ImportResourceState.ImportedResource + 5, // 51: tfplugin6.ImportResourceState.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 4, // 52: tfplugin6.ReadDataSource.Request.config:type_name -> tfplugin6.DynamicValue + 4, // 53: tfplugin6.ReadDataSource.Request.provider_meta:type_name -> tfplugin6.DynamicValue + 4, // 54: tfplugin6.ReadDataSource.Response.state:type_name -> tfplugin6.DynamicValue + 5, // 55: tfplugin6.ReadDataSource.Response.diagnostics:type_name -> tfplugin6.Diagnostic + 29, // 56: tfplugin6.Provider.GetProviderSchema:input_type -> tfplugin6.GetProviderSchema.Request + 33, // 57: tfplugin6.Provider.ValidateProviderConfig:input_type -> tfplugin6.ValidateProviderConfig.Request + 37, // 58: tfplugin6.Provider.ValidateResourceConfig:input_type -> tfplugin6.ValidateResourceConfig.Request + 39, // 59: tfplugin6.Provider.ValidateDataResourceConfig:input_type -> tfplugin6.ValidateDataResourceConfig.Request + 35, // 60: tfplugin6.Provider.UpgradeResourceState:input_type -> tfplugin6.UpgradeResourceState.Request + 41, // 61: tfplugin6.Provider.ConfigureProvider:input_type -> tfplugin6.ConfigureProvider.Request + 43, // 62: tfplugin6.Provider.ReadResource:input_type -> tfplugin6.ReadResource.Request + 45, // 63: tfplugin6.Provider.PlanResourceChange:input_type -> tfplugin6.PlanResourceChange.Request + 47, // 64: tfplugin6.Provider.ApplyResourceChange:input_type -> tfplugin6.ApplyResourceChange.Request + 49, // 65: tfplugin6.Provider.ImportResourceState:input_type -> tfplugin6.ImportResourceState.Request + 52, // 66: tfplugin6.Provider.ReadDataSource:input_type -> tfplugin6.ReadDataSource.Request + 22, // 67: tfplugin6.Provider.StopProvider:input_type -> tfplugin6.StopProvider.Request + 30, // 68: tfplugin6.Provider.GetProviderSchema:output_type -> tfplugin6.GetProviderSchema.Response + 34, // 69: tfplugin6.Provider.ValidateProviderConfig:output_type -> tfplugin6.ValidateProviderConfig.Response + 38, // 70: tfplugin6.Provider.ValidateResourceConfig:output_type -> tfplugin6.ValidateResourceConfig.Response + 40, // 71: tfplugin6.Provider.ValidateDataResourceConfig:output_type -> tfplugin6.ValidateDataResourceConfig.Response + 36, // 72: tfplugin6.Provider.UpgradeResourceState:output_type -> tfplugin6.UpgradeResourceState.Response + 42, // 73: tfplugin6.Provider.ConfigureProvider:output_type -> tfplugin6.ConfigureProvider.Response + 44, // 74: tfplugin6.Provider.ReadResource:output_type -> tfplugin6.ReadResource.Response + 46, // 75: tfplugin6.Provider.PlanResourceChange:output_type -> tfplugin6.PlanResourceChange.Response + 48, // 76: tfplugin6.Provider.ApplyResourceChange:output_type -> tfplugin6.ApplyResourceChange.Response + 51, // 77: tfplugin6.Provider.ImportResourceState:output_type -> tfplugin6.ImportResourceState.Response + 53, // 78: tfplugin6.Provider.ReadDataSource:output_type -> tfplugin6.ReadDataSource.Response + 23, // 79: tfplugin6.Provider.StopProvider:output_type -> tfplugin6.StopProvider.Response + 68, // [68:80] is the sub-list for method output_type + 56, // [56:68] is the sub-list for method input_type + 56, // [56:56] is the sub-list for extension type_name + 56, // [56:56] is the sub-list for extension extendee + 0, // [0:56] is the sub-list for field type_name +} + +func init() { file_tfplugin6_proto_init() } +func file_tfplugin6_proto_init() { + if File_tfplugin6_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_tfplugin6_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DynamicValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Diagnostic); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AttributePath); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StopProvider); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RawState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Schema); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetProviderSchema); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateProviderConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpgradeResourceState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateResourceConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateDataResourceConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConfigureProvider); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadResource); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PlanResourceChange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ApplyResourceChange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ImportResourceState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadDataSource); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AttributePath_Step); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StopProvider_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StopProvider_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Schema_Block); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Schema_Attribute); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Schema_NestedBlock); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Schema_Object); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetProviderSchema_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetProviderSchema_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateProviderConfig_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateProviderConfig_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpgradeResourceState_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpgradeResourceState_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateResourceConfig_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateResourceConfig_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateDataResourceConfig_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateDataResourceConfig_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConfigureProvider_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConfigureProvider_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadResource_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadResource_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PlanResourceChange_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PlanResourceChange_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ApplyResourceChange_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ApplyResourceChange_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ImportResourceState_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ImportResourceState_ImportedResource); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ImportResourceState_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadDataSource_Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tfplugin6_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadDataSource_Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_tfplugin6_proto_msgTypes[17].OneofWrappers = []interface{}{ + (*AttributePath_Step_AttributeName)(nil), + (*AttributePath_Step_ElementKeyString)(nil), + (*AttributePath_Step_ElementKeyInt)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_tfplugin6_proto_rawDesc, + NumEnums: 4, + NumMessages: 50, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_tfplugin6_proto_goTypes, + DependencyIndexes: file_tfplugin6_proto_depIdxs, + EnumInfos: file_tfplugin6_proto_enumTypes, + MessageInfos: file_tfplugin6_proto_msgTypes, + }.Build() + File_tfplugin6_proto = out.File + file_tfplugin6_proto_rawDesc = nil + file_tfplugin6_proto_goTypes = nil + file_tfplugin6_proto_depIdxs = nil +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConnInterface + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion6 + +// ProviderClient is the client API for Provider service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type ProviderClient interface { + //////// Information about what a provider supports/expects + GetProviderSchema(ctx context.Context, in *GetProviderSchema_Request, opts ...grpc.CallOption) (*GetProviderSchema_Response, error) + ValidateProviderConfig(ctx context.Context, in *ValidateProviderConfig_Request, opts ...grpc.CallOption) (*ValidateProviderConfig_Response, error) + ValidateResourceConfig(ctx context.Context, in *ValidateResourceConfig_Request, opts ...grpc.CallOption) (*ValidateResourceConfig_Response, error) + ValidateDataResourceConfig(ctx context.Context, in *ValidateDataResourceConfig_Request, opts ...grpc.CallOption) (*ValidateDataResourceConfig_Response, error) + UpgradeResourceState(ctx context.Context, in *UpgradeResourceState_Request, opts ...grpc.CallOption) (*UpgradeResourceState_Response, error) + //////// One-time initialization, called before other functions below + ConfigureProvider(ctx context.Context, in *ConfigureProvider_Request, opts ...grpc.CallOption) (*ConfigureProvider_Response, error) + //////// Managed Resource Lifecycle + ReadResource(ctx context.Context, in *ReadResource_Request, opts ...grpc.CallOption) (*ReadResource_Response, error) + PlanResourceChange(ctx context.Context, in *PlanResourceChange_Request, opts ...grpc.CallOption) (*PlanResourceChange_Response, error) + ApplyResourceChange(ctx context.Context, in *ApplyResourceChange_Request, opts ...grpc.CallOption) (*ApplyResourceChange_Response, error) + ImportResourceState(ctx context.Context, in *ImportResourceState_Request, opts ...grpc.CallOption) (*ImportResourceState_Response, error) + ReadDataSource(ctx context.Context, in *ReadDataSource_Request, opts ...grpc.CallOption) (*ReadDataSource_Response, error) + //////// Graceful Shutdown + StopProvider(ctx context.Context, in *StopProvider_Request, opts ...grpc.CallOption) (*StopProvider_Response, error) +} + +type providerClient struct { + cc grpc.ClientConnInterface +} + +func NewProviderClient(cc grpc.ClientConnInterface) ProviderClient { + return &providerClient{cc} +} + +func (c *providerClient) GetProviderSchema(ctx context.Context, in *GetProviderSchema_Request, opts ...grpc.CallOption) (*GetProviderSchema_Response, error) { + out := new(GetProviderSchema_Response) + err := c.cc.Invoke(ctx, "/tfplugin6.Provider/GetProviderSchema", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *providerClient) ValidateProviderConfig(ctx context.Context, in *ValidateProviderConfig_Request, opts ...grpc.CallOption) (*ValidateProviderConfig_Response, error) { + out := new(ValidateProviderConfig_Response) + err := c.cc.Invoke(ctx, "/tfplugin6.Provider/ValidateProviderConfig", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *providerClient) ValidateResourceConfig(ctx context.Context, in *ValidateResourceConfig_Request, opts ...grpc.CallOption) (*ValidateResourceConfig_Response, error) { + out := new(ValidateResourceConfig_Response) + err := c.cc.Invoke(ctx, "/tfplugin6.Provider/ValidateResourceConfig", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *providerClient) ValidateDataResourceConfig(ctx context.Context, in *ValidateDataResourceConfig_Request, opts ...grpc.CallOption) (*ValidateDataResourceConfig_Response, error) { + out := new(ValidateDataResourceConfig_Response) + err := c.cc.Invoke(ctx, "/tfplugin6.Provider/ValidateDataResourceConfig", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *providerClient) UpgradeResourceState(ctx context.Context, in *UpgradeResourceState_Request, opts ...grpc.CallOption) (*UpgradeResourceState_Response, error) { + out := new(UpgradeResourceState_Response) + err := c.cc.Invoke(ctx, "/tfplugin6.Provider/UpgradeResourceState", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *providerClient) ConfigureProvider(ctx context.Context, in *ConfigureProvider_Request, opts ...grpc.CallOption) (*ConfigureProvider_Response, error) { + out := new(ConfigureProvider_Response) + err := c.cc.Invoke(ctx, "/tfplugin6.Provider/ConfigureProvider", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *providerClient) ReadResource(ctx context.Context, in *ReadResource_Request, opts ...grpc.CallOption) (*ReadResource_Response, error) { + out := new(ReadResource_Response) + err := c.cc.Invoke(ctx, "/tfplugin6.Provider/ReadResource", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *providerClient) PlanResourceChange(ctx context.Context, in *PlanResourceChange_Request, opts ...grpc.CallOption) (*PlanResourceChange_Response, error) { + out := new(PlanResourceChange_Response) + err := c.cc.Invoke(ctx, "/tfplugin6.Provider/PlanResourceChange", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *providerClient) ApplyResourceChange(ctx context.Context, in *ApplyResourceChange_Request, opts ...grpc.CallOption) (*ApplyResourceChange_Response, error) { + out := new(ApplyResourceChange_Response) + err := c.cc.Invoke(ctx, "/tfplugin6.Provider/ApplyResourceChange", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *providerClient) ImportResourceState(ctx context.Context, in *ImportResourceState_Request, opts ...grpc.CallOption) (*ImportResourceState_Response, error) { + out := new(ImportResourceState_Response) + err := c.cc.Invoke(ctx, "/tfplugin6.Provider/ImportResourceState", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *providerClient) ReadDataSource(ctx context.Context, in *ReadDataSource_Request, opts ...grpc.CallOption) (*ReadDataSource_Response, error) { + out := new(ReadDataSource_Response) + err := c.cc.Invoke(ctx, "/tfplugin6.Provider/ReadDataSource", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *providerClient) StopProvider(ctx context.Context, in *StopProvider_Request, opts ...grpc.CallOption) (*StopProvider_Response, error) { + out := new(StopProvider_Response) + err := c.cc.Invoke(ctx, "/tfplugin6.Provider/StopProvider", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ProviderServer is the server API for Provider service. +type ProviderServer interface { + //////// Information about what a provider supports/expects + GetProviderSchema(context.Context, *GetProviderSchema_Request) (*GetProviderSchema_Response, error) + ValidateProviderConfig(context.Context, *ValidateProviderConfig_Request) (*ValidateProviderConfig_Response, error) + ValidateResourceConfig(context.Context, *ValidateResourceConfig_Request) (*ValidateResourceConfig_Response, error) + ValidateDataResourceConfig(context.Context, *ValidateDataResourceConfig_Request) (*ValidateDataResourceConfig_Response, error) + UpgradeResourceState(context.Context, *UpgradeResourceState_Request) (*UpgradeResourceState_Response, error) + //////// One-time initialization, called before other functions below + ConfigureProvider(context.Context, *ConfigureProvider_Request) (*ConfigureProvider_Response, error) + //////// Managed Resource Lifecycle + ReadResource(context.Context, *ReadResource_Request) (*ReadResource_Response, error) + PlanResourceChange(context.Context, *PlanResourceChange_Request) (*PlanResourceChange_Response, error) + ApplyResourceChange(context.Context, *ApplyResourceChange_Request) (*ApplyResourceChange_Response, error) + ImportResourceState(context.Context, *ImportResourceState_Request) (*ImportResourceState_Response, error) + ReadDataSource(context.Context, *ReadDataSource_Request) (*ReadDataSource_Response, error) + //////// Graceful Shutdown + StopProvider(context.Context, *StopProvider_Request) (*StopProvider_Response, error) +} + +// UnimplementedProviderServer can be embedded to have forward compatible implementations. +type UnimplementedProviderServer struct { +} + +func (*UnimplementedProviderServer) GetProviderSchema(context.Context, *GetProviderSchema_Request) (*GetProviderSchema_Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetProviderSchema not implemented") +} +func (*UnimplementedProviderServer) ValidateProviderConfig(context.Context, *ValidateProviderConfig_Request) (*ValidateProviderConfig_Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method ValidateProviderConfig not implemented") +} +func (*UnimplementedProviderServer) ValidateResourceConfig(context.Context, *ValidateResourceConfig_Request) (*ValidateResourceConfig_Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method ValidateResourceConfig not implemented") +} +func (*UnimplementedProviderServer) ValidateDataResourceConfig(context.Context, *ValidateDataResourceConfig_Request) (*ValidateDataResourceConfig_Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method ValidateDataResourceConfig not implemented") +} +func (*UnimplementedProviderServer) UpgradeResourceState(context.Context, *UpgradeResourceState_Request) (*UpgradeResourceState_Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpgradeResourceState not implemented") +} +func (*UnimplementedProviderServer) ConfigureProvider(context.Context, *ConfigureProvider_Request) (*ConfigureProvider_Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConfigureProvider not implemented") +} +func (*UnimplementedProviderServer) ReadResource(context.Context, *ReadResource_Request) (*ReadResource_Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method ReadResource not implemented") +} +func (*UnimplementedProviderServer) PlanResourceChange(context.Context, *PlanResourceChange_Request) (*PlanResourceChange_Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method PlanResourceChange not implemented") +} +func (*UnimplementedProviderServer) ApplyResourceChange(context.Context, *ApplyResourceChange_Request) (*ApplyResourceChange_Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method ApplyResourceChange not implemented") +} +func (*UnimplementedProviderServer) ImportResourceState(context.Context, *ImportResourceState_Request) (*ImportResourceState_Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method ImportResourceState not implemented") +} +func (*UnimplementedProviderServer) ReadDataSource(context.Context, *ReadDataSource_Request) (*ReadDataSource_Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method ReadDataSource not implemented") +} +func (*UnimplementedProviderServer) StopProvider(context.Context, *StopProvider_Request) (*StopProvider_Response, error) { + return nil, status.Errorf(codes.Unimplemented, "method StopProvider not implemented") +} + +func RegisterProviderServer(s *grpc.Server, srv ProviderServer) { + s.RegisterService(&_Provider_serviceDesc, srv) +} + +func _Provider_GetProviderSchema_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetProviderSchema_Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProviderServer).GetProviderSchema(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/tfplugin6.Provider/GetProviderSchema", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProviderServer).GetProviderSchema(ctx, req.(*GetProviderSchema_Request)) + } + return interceptor(ctx, in, info, handler) +} + +func _Provider_ValidateProviderConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ValidateProviderConfig_Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProviderServer).ValidateProviderConfig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/tfplugin6.Provider/ValidateProviderConfig", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProviderServer).ValidateProviderConfig(ctx, req.(*ValidateProviderConfig_Request)) + } + return interceptor(ctx, in, info, handler) +} + +func _Provider_ValidateResourceConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ValidateResourceConfig_Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProviderServer).ValidateResourceConfig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/tfplugin6.Provider/ValidateResourceConfig", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProviderServer).ValidateResourceConfig(ctx, req.(*ValidateResourceConfig_Request)) + } + return interceptor(ctx, in, info, handler) +} + +func _Provider_ValidateDataResourceConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ValidateDataResourceConfig_Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProviderServer).ValidateDataResourceConfig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/tfplugin6.Provider/ValidateDataResourceConfig", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProviderServer).ValidateDataResourceConfig(ctx, req.(*ValidateDataResourceConfig_Request)) + } + return interceptor(ctx, in, info, handler) +} + +func _Provider_UpgradeResourceState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpgradeResourceState_Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProviderServer).UpgradeResourceState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/tfplugin6.Provider/UpgradeResourceState", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProviderServer).UpgradeResourceState(ctx, req.(*UpgradeResourceState_Request)) + } + return interceptor(ctx, in, info, handler) +} + +func _Provider_ConfigureProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ConfigureProvider_Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProviderServer).ConfigureProvider(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/tfplugin6.Provider/ConfigureProvider", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProviderServer).ConfigureProvider(ctx, req.(*ConfigureProvider_Request)) + } + return interceptor(ctx, in, info, handler) +} + +func _Provider_ReadResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReadResource_Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProviderServer).ReadResource(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/tfplugin6.Provider/ReadResource", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProviderServer).ReadResource(ctx, req.(*ReadResource_Request)) + } + return interceptor(ctx, in, info, handler) +} + +func _Provider_PlanResourceChange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PlanResourceChange_Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProviderServer).PlanResourceChange(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/tfplugin6.Provider/PlanResourceChange", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProviderServer).PlanResourceChange(ctx, req.(*PlanResourceChange_Request)) + } + return interceptor(ctx, in, info, handler) +} + +func _Provider_ApplyResourceChange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ApplyResourceChange_Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProviderServer).ApplyResourceChange(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/tfplugin6.Provider/ApplyResourceChange", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProviderServer).ApplyResourceChange(ctx, req.(*ApplyResourceChange_Request)) + } + return interceptor(ctx, in, info, handler) +} + +func _Provider_ImportResourceState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ImportResourceState_Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProviderServer).ImportResourceState(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/tfplugin6.Provider/ImportResourceState", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProviderServer).ImportResourceState(ctx, req.(*ImportResourceState_Request)) + } + return interceptor(ctx, in, info, handler) +} + +func _Provider_ReadDataSource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReadDataSource_Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProviderServer).ReadDataSource(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/tfplugin6.Provider/ReadDataSource", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProviderServer).ReadDataSource(ctx, req.(*ReadDataSource_Request)) + } + return interceptor(ctx, in, info, handler) +} + +func _Provider_StopProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StopProvider_Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProviderServer).StopProvider(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/tfplugin6.Provider/StopProvider", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProviderServer).StopProvider(ctx, req.(*StopProvider_Request)) + } + return interceptor(ctx, in, info, handler) +} + +var _Provider_serviceDesc = grpc.ServiceDesc{ + ServiceName: "tfplugin6.Provider", + HandlerType: (*ProviderServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetProviderSchema", + Handler: _Provider_GetProviderSchema_Handler, + }, + { + MethodName: "ValidateProviderConfig", + Handler: _Provider_ValidateProviderConfig_Handler, + }, + { + MethodName: "ValidateResourceConfig", + Handler: _Provider_ValidateResourceConfig_Handler, + }, + { + MethodName: "ValidateDataResourceConfig", + Handler: _Provider_ValidateDataResourceConfig_Handler, + }, + { + MethodName: "UpgradeResourceState", + Handler: _Provider_UpgradeResourceState_Handler, + }, + { + MethodName: "ConfigureProvider", + Handler: _Provider_ConfigureProvider_Handler, + }, + { + MethodName: "ReadResource", + Handler: _Provider_ReadResource_Handler, + }, + { + MethodName: "PlanResourceChange", + Handler: _Provider_PlanResourceChange_Handler, + }, + { + MethodName: "ApplyResourceChange", + Handler: _Provider_ApplyResourceChange_Handler, + }, + { + MethodName: "ImportResourceState", + Handler: _Provider_ImportResourceState_Handler, + }, + { + MethodName: "ReadDataSource", + Handler: _Provider_ReadDataSource_Handler, + }, + { + MethodName: "StopProvider", + Handler: _Provider_StopProvider_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "tfplugin6.proto", +} diff --git a/vendor/github.com/hashicorp/terraform/internal/tfplugin6/tfplugin6.proto b/vendor/github.com/hashicorp/terraform/internal/tfplugin6/tfplugin6.proto new file mode 120000 index 00000000..ae470600 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/internal/tfplugin6/tfplugin6.proto @@ -0,0 +1 @@ +../../docs/plugin-protocol/tfplugin6.0.proto \ No newline at end of file diff --git a/vendor/github.com/hashicorp/terraform/internal/typeexpr/get_type.go b/vendor/github.com/hashicorp/terraform/internal/typeexpr/get_type.go index da84f5dc..de5465b9 100644 --- a/vendor/github.com/hashicorp/terraform/internal/typeexpr/get_type.go +++ b/vendor/github.com/hashicorp/terraform/internal/typeexpr/get_type.go @@ -167,7 +167,7 @@ func getType(expr hcl.Expression, constraint bool) (cty.Type, hcl.Diagnostics) { // modifier optional(...) to indicate an optional attribute. If // so, we'll unwrap that first and make a note about it being // optional for when we construct the type below. - if call, diags := hcl.ExprCall(atyExpr); !diags.HasErrors() { + if call, callDiags := hcl.ExprCall(atyExpr); !callDiags.HasErrors() { if call.Name == "optional" { if len(call.Arguments) < 1 { diags = append(diags, &hcl.Diagnostic{ diff --git a/vendor/github.com/hashicorp/terraform/lang/blocktoattr/fixup.go b/vendor/github.com/hashicorp/terraform/lang/blocktoattr/fixup.go index 0af708ec..056946ab 100644 --- a/vendor/github.com/hashicorp/terraform/lang/blocktoattr/fixup.go +++ b/vendor/github.com/hashicorp/terraform/lang/blocktoattr/fixup.go @@ -41,6 +41,17 @@ type fixupBody struct { names map[string]struct{} } +type unknownBlock interface { + Unknown() bool +} + +func (b *fixupBody) Unknown() bool { + if u, ok := b.original.(unknownBlock); ok { + return u.Unknown() + } + return false +} + // Content decodes content from the body. The given schema must be the lower-level // representation of the same schema that was previously passed to FixUpBlockAttrs, // or else the result is undefined. diff --git a/vendor/github.com/hashicorp/terraform/lang/eval.go b/vendor/github.com/hashicorp/terraform/lang/eval.go index 381ec428..fab3c933 100644 --- a/vendor/github.com/hashicorp/terraform/lang/eval.go +++ b/vendor/github.com/hashicorp/terraform/lang/eval.go @@ -72,8 +72,13 @@ func (s *Scope) EvalBlock(body hcl.Body, schema *configschema.Block) (cty.Value, // EvalSelfBlock evaluates the given body only within the scope of the provided // object and instance key data. References to the object must use self, and the -// key data will only contain count.index or each.key. +// key data will only contain count.index or each.key. The static values for +// terraform and path will also be available in this context. func (s *Scope) EvalSelfBlock(body hcl.Body, self cty.Value, schema *configschema.Block, keyData instances.RepetitionData) (cty.Value, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + + spec := schema.DecoderSpec() + vals := make(map[string]cty.Value) vals["self"] = self @@ -88,12 +93,55 @@ func (s *Scope) EvalSelfBlock(body hcl.Body, self cty.Value, schema *configschem }) } + refs, refDiags := References(hcldec.Variables(body, spec)) + diags = diags.Append(refDiags) + + terraformAttrs := map[string]cty.Value{} + pathAttrs := map[string]cty.Value{} + + // We could always load the static values for Path and Terraform values, + // but we want to parse the references so that we can get source ranges for + // user diagnostics. + for _, ref := range refs { + // we already loaded the self value + if ref.Subject == addrs.Self { + continue + } + + switch subj := ref.Subject.(type) { + case addrs.PathAttr: + val, valDiags := normalizeRefValue(s.Data.GetPathAttr(subj, ref.SourceRange)) + diags = diags.Append(valDiags) + pathAttrs[subj.Name] = val + + case addrs.TerraformAttr: + val, valDiags := normalizeRefValue(s.Data.GetTerraformAttr(subj, ref.SourceRange)) + diags = diags.Append(valDiags) + terraformAttrs[subj.Name] = val + + case addrs.CountAttr, addrs.ForEachAttr: + // each and count have already been handled. + + default: + // This should have been caught in validation, but point the user + // to the correct location in case something slipped through. + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: `Invalid reference`, + Detail: fmt.Sprintf("The reference to %q is not valid in this context", ref.Subject), + Subject: ref.SourceRange.ToHCL().Ptr(), + }) + } + } + + vals["path"] = cty.ObjectVal(pathAttrs) + vals["terraform"] = cty.ObjectVal(terraformAttrs) + ctx := &hcl.EvalContext{ Variables: vals, Functions: s.Functions(), } - var diags tfdiags.Diagnostics val, decDiags := hcldec.Decode(body, schema.DecoderSpec(), ctx) diags = diags.Append(decDiags) return val, diags @@ -129,10 +177,12 @@ func (s *Scope) EvalExpr(expr hcl.Expression, wantType cty.Type) (cty.Value, tfd if convErr != nil { val = cty.UnknownVal(wantType) diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Incorrect value type", - Detail: fmt.Sprintf("Invalid expression value: %s.", tfdiags.FormatError(convErr)), - Subject: expr.Range().Ptr(), + Severity: hcl.DiagError, + Summary: "Incorrect value type", + Detail: fmt.Sprintf("Invalid expression value: %s.", tfdiags.FormatError(convErr)), + Subject: expr.Range().Ptr(), + Expression: expr, + EvalContext: ctx, }) } } @@ -273,10 +323,10 @@ func (s *Scope) evalContext(refs []*addrs.Reference, selfAddr addrs.Referenceabl switch k := subj.Key.(type) { case addrs.IntKey: self, hclDiags = hcl.Index(val, cty.NumberIntVal(int64(k)), ref.SourceRange.ToHCL().Ptr()) - diags.Append(hclDiags) + diags = diags.Append(hclDiags) case addrs.StringKey: self, hclDiags = hcl.Index(val, cty.StringVal(string(k)), ref.SourceRange.ToHCL().Ptr()) - diags.Append(hclDiags) + diags = diags.Append(hclDiags) default: self = val } diff --git a/vendor/github.com/hashicorp/terraform/lang/eval_test.go b/vendor/github.com/hashicorp/terraform/lang/eval_test.go index f9ee894d..0ddcef19 100644 --- a/vendor/github.com/hashicorp/terraform/lang/eval_test.go +++ b/vendor/github.com/hashicorp/terraform/lang/eval_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/instances" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" @@ -642,3 +643,128 @@ func formattedJSONValue(val cty.Value) string { json.Indent(&buf, j, "", " ") return buf.String() } + +func TestScopeEvalSelfBlock(t *testing.T) { + data := &dataForTests{ + PathAttrs: map[string]cty.Value{ + "module": cty.StringVal("foo/bar"), + "cwd": cty.StringVal("/home/foo/bar"), + "root": cty.StringVal("/home/foo"), + }, + TerraformAttrs: map[string]cty.Value{ + "workspace": cty.StringVal("default"), + }, + } + schema := &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "attr": { + Type: cty.String, + }, + "num": { + Type: cty.Number, + }, + }, + } + + tests := []struct { + Config string + Self cty.Value + KeyData instances.RepetitionData + Want map[string]cty.Value + }{ + { + Config: `attr = self.foo`, + Self: cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + }), + KeyData: instances.RepetitionData{ + CountIndex: cty.NumberIntVal(0), + }, + Want: map[string]cty.Value{ + "attr": cty.StringVal("bar"), + "num": cty.NullVal(cty.Number), + }, + }, + { + Config: `num = count.index`, + KeyData: instances.RepetitionData{ + CountIndex: cty.NumberIntVal(0), + }, + Want: map[string]cty.Value{ + "attr": cty.NullVal(cty.String), + "num": cty.NumberIntVal(0), + }, + }, + { + Config: `attr = each.key`, + KeyData: instances.RepetitionData{ + EachKey: cty.StringVal("a"), + }, + Want: map[string]cty.Value{ + "attr": cty.StringVal("a"), + "num": cty.NullVal(cty.Number), + }, + }, + { + Config: `attr = path.cwd`, + Want: map[string]cty.Value{ + "attr": cty.StringVal("/home/foo/bar"), + "num": cty.NullVal(cty.Number), + }, + }, + { + Config: `attr = path.module`, + Want: map[string]cty.Value{ + "attr": cty.StringVal("foo/bar"), + "num": cty.NullVal(cty.Number), + }, + }, + { + Config: `attr = path.root`, + Want: map[string]cty.Value{ + "attr": cty.StringVal("/home/foo"), + "num": cty.NullVal(cty.Number), + }, + }, + { + Config: `attr = terraform.workspace`, + Want: map[string]cty.Value{ + "attr": cty.StringVal("default"), + "num": cty.NullVal(cty.Number), + }, + }, + } + + for _, test := range tests { + t.Run(test.Config, func(t *testing.T) { + file, parseDiags := hclsyntax.ParseConfig([]byte(test.Config), "", hcl.Pos{Line: 1, Column: 1}) + if len(parseDiags) != 0 { + t.Errorf("unexpected diagnostics during parse") + for _, diag := range parseDiags { + t.Errorf("- %s", diag) + } + return + } + + body := file.Body + + scope := &Scope{ + Data: data, + } + + gotVal, ctxDiags := scope.EvalSelfBlock(body, test.Self, schema, test.KeyData) + if ctxDiags.HasErrors() { + t.Fatal(ctxDiags.Err()) + } + + wantVal := cty.ObjectVal(test.Want) + + if !gotVal.RawEquals(wantVal) { + t.Errorf( + "wrong result\nexpr: %s\ngot: %#v\nwant: %#v", + test.Config, gotVal, wantVal, + ) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/collection.go b/vendor/github.com/hashicorp/terraform/lang/funcs/collection.go index d83a79bd..f9b3e6ae 100644 --- a/vendor/github.com/hashicorp/terraform/lang/funcs/collection.go +++ b/vendor/github.com/hashicorp/terraform/lang/funcs/collection.go @@ -3,6 +3,7 @@ package funcs import ( "errors" "fmt" + "math/big" "sort" "github.com/zclconf/go-cty/cty" @@ -19,6 +20,7 @@ var LengthFunc = function.New(&function.Spec{ Type: cty.DynamicPseudoType, AllowDynamicType: true, AllowUnknown: true, + AllowMarked: true, }, }, Type: func(args []cty.Value) (cty.Type, error) { @@ -33,15 +35,16 @@ var LengthFunc = function.New(&function.Spec{ Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { coll := args[0] collTy := args[0].Type() + marks := coll.Marks() switch { case collTy == cty.DynamicPseudoType: - return cty.UnknownVal(cty.Number), nil + return cty.UnknownVal(cty.Number).WithMarks(marks), nil case collTy.IsTupleType(): l := len(collTy.TupleElementTypes()) - return cty.NumberIntVal(int64(l)), nil + return cty.NumberIntVal(int64(l)).WithMarks(marks), nil case collTy.IsObjectType(): l := len(collTy.AttributeTypes()) - return cty.NumberIntVal(int64(l)), nil + return cty.NumberIntVal(int64(l)).WithMarks(marks), nil case collTy == cty.String: // We'll delegate to the cty stdlib strlen function here, because // it deals with all of the complexities of tokenizing unicode @@ -57,40 +60,65 @@ var LengthFunc = function.New(&function.Spec{ }) // AllTrueFunc constructs a function that returns true if all elements of the -// collection are true or "true". If the collection is empty, return true. +// list are true. If the list is empty, return true. var AllTrueFunc = function.New(&function.Spec{ Params: []function.Parameter{ { - Name: "collection", - Type: cty.DynamicPseudoType, + Name: "list", + Type: cty.List(cty.Bool), }, }, Type: function.StaticReturnType(cty.Bool), Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - ty := args[0].Type() - if !ty.IsListType() && !ty.IsTupleType() && !ty.IsSetType() { - return cty.NilVal, errors.New("argument must be list, tuple, or set") - } - - tobool := MakeToFunc(cty.Bool) + result := cty.True for it := args[0].ElementIterator(); it.Next(); { _, v := it.Element() if !v.IsKnown() { return cty.UnknownVal(cty.Bool), nil } - got, err := tobool.Call([]cty.Value{v}) - if err != nil { + if v.IsNull() { return cty.False, nil } - eq, err := stdlib.Equal(got, cty.True) - if err != nil { - return cty.NilVal, err - } - if eq.False() { + result = result.And(v) + if result.False() { return cty.False, nil } } - return cty.True, nil + return result, nil + }, +}) + +// AnyTrueFunc constructs a function that returns true if any element of the +// list is true. If the list is empty, return false. +var AnyTrueFunc = function.New(&function.Spec{ + Params: []function.Parameter{ + { + Name: "list", + Type: cty.List(cty.Bool), + }, + }, + Type: function.StaticReturnType(cty.Bool), + Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { + result := cty.False + var hasUnknown bool + for it := args[0].ElementIterator(); it.Next(); { + _, v := it.Element() + if !v.IsKnown() { + hasUnknown = true + continue + } + if v.IsNull() { + continue + } + result = result.Or(v) + if result.True() { + return cty.True, nil + } + } + if hasUnknown { + return cty.UnknownVal(cty.Bool), nil + } + return result, nil }, }) @@ -182,84 +210,18 @@ var IndexFunc = function.New(&function.Spec{ }, }) -// Flatten until it's not a cty.List, and return whether the value is known. -// We can flatten lists with unknown values, as long as they are not -// lists themselves. -func flattener(flattenList cty.Value) ([]cty.Value, bool) { - out := make([]cty.Value, 0) - for it := flattenList.ElementIterator(); it.Next(); { - _, val := it.Element() - if val.Type().IsListType() || val.Type().IsSetType() || val.Type().IsTupleType() { - if !val.IsKnown() { - return out, false - } - - res, known := flattener(val) - if !known { - return res, known - } - out = append(out, res...) - } else { - out = append(out, val) - } - } - return out, true -} - -// ListFunc constructs a function that takes an arbitrary number of arguments -// and returns a list containing those values in the same order. -// -// This function is deprecated in Terraform v0.12 -var ListFunc = function.New(&function.Spec{ - Params: []function.Parameter{}, - VarParam: &function.Parameter{ - Name: "vals", - Type: cty.DynamicPseudoType, - AllowUnknown: true, - AllowDynamicType: true, - AllowNull: true, - }, - Type: func(args []cty.Value) (ret cty.Type, err error) { - if len(args) == 0 { - return cty.NilType, errors.New("at least one argument is required") - } - - argTypes := make([]cty.Type, len(args)) - - for i, arg := range args { - argTypes[i] = arg.Type() - } - - retType, _ := convert.UnifyUnsafe(argTypes) - if retType == cty.NilType { - return cty.NilType, errors.New("all arguments must have the same type") - } - - return cty.List(retType), nil - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - newList := make([]cty.Value, 0, len(args)) - - for _, arg := range args { - // We already know this will succeed because of the checks in our Type func above - arg, _ = convert.Convert(arg, retType.ElementType()) - newList = append(newList, arg) - } - - return cty.ListVal(newList), nil - }, -}) - // LookupFunc constructs a function that performs dynamic lookups of map types. var LookupFunc = function.New(&function.Spec{ Params: []function.Parameter{ { - Name: "inputMap", - Type: cty.DynamicPseudoType, + Name: "inputMap", + Type: cty.DynamicPseudoType, + AllowMarked: true, }, { - Name: "key", - Type: cty.String, + Name: "key", + Type: cty.String, + AllowMarked: true, }, }, VarParam: &function.Parameter{ @@ -268,6 +230,7 @@ var LookupFunc = function.New(&function.Spec{ AllowUnknown: true, AllowDynamicType: true, AllowNull: true, + AllowMarked: true, }, Type: func(args []cty.Value) (ret cty.Type, err error) { if len(args) < 1 || len(args) > 3 { @@ -282,7 +245,8 @@ var LookupFunc = function.New(&function.Spec{ return cty.DynamicPseudoType, nil } - key := args[1].AsString() + keyVal, _ := args[1].Unmark() + key := keyVal.AsString() if ty.HasAttribute(key) { return args[0].GetAttr(key).Type(), nil } else if len(args) == 3 { @@ -308,23 +272,35 @@ var LookupFunc = function.New(&function.Spec{ defaultValueSet := false if len(args) == 3 { + // intentionally leave default value marked defaultVal = args[2] defaultValueSet = true } - mapVar := args[0] - lookupKey := args[1].AsString() + // keep track of marks from the collection and key + var markses []cty.ValueMarks + + // unmark collection, retain marks to reapply later + mapVar, mapMarks := args[0].Unmark() + markses = append(markses, mapMarks) + + // include marks on the key in the result + keyVal, keyMarks := args[1].Unmark() + if len(keyMarks) > 0 { + markses = append(markses, keyMarks) + } + lookupKey := keyVal.AsString() if !mapVar.IsKnown() { - return cty.UnknownVal(retType), nil + return cty.UnknownVal(retType).WithMarks(markses...), nil } if mapVar.Type().IsObjectType() { if mapVar.Type().HasAttribute(lookupKey) { - return mapVar.GetAttr(lookupKey), nil + return mapVar.GetAttr(lookupKey).WithMarks(markses...), nil } } else if mapVar.HasIndex(cty.StringVal(lookupKey)) == cty.True { - return mapVar.Index(cty.StringVal(lookupKey)), nil + return mapVar.Index(cty.StringVal(lookupKey)).WithMarks(markses...), nil } if defaultValueSet { @@ -332,89 +308,14 @@ var LookupFunc = function.New(&function.Spec{ if err != nil { return cty.NilVal, err } - return defaultVal, nil + return defaultVal.WithMarks(markses...), nil } - return cty.UnknownVal(cty.DynamicPseudoType), fmt.Errorf( + return cty.UnknownVal(cty.DynamicPseudoType).WithMarks(markses...), fmt.Errorf( "lookup failed to find '%s'", lookupKey) }, }) -// MapFunc constructs a function that takes an even number of arguments and -// returns a map whose elements are constructed from consecutive pairs of arguments. -// -// This function is deprecated in Terraform v0.12 -var MapFunc = function.New(&function.Spec{ - Params: []function.Parameter{}, - VarParam: &function.Parameter{ - Name: "vals", - Type: cty.DynamicPseudoType, - AllowUnknown: true, - AllowDynamicType: true, - AllowNull: true, - }, - Type: func(args []cty.Value) (ret cty.Type, err error) { - if len(args) < 2 || len(args)%2 != 0 { - return cty.NilType, fmt.Errorf("map requires an even number of two or more arguments, got %d", len(args)) - } - - argTypes := make([]cty.Type, len(args)/2) - index := 0 - - for i := 0; i < len(args); i += 2 { - argTypes[index] = args[i+1].Type() - index++ - } - - valType, _ := convert.UnifyUnsafe(argTypes) - if valType == cty.NilType { - return cty.NilType, errors.New("all arguments must have the same type") - } - - return cty.Map(valType), nil - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - for _, arg := range args { - if !arg.IsWhollyKnown() { - return cty.UnknownVal(retType), nil - } - } - - outputMap := make(map[string]cty.Value) - - for i := 0; i < len(args); i += 2 { - - keyVal, err := convert.Convert(args[i], cty.String) - if err != nil { - return cty.NilVal, err - } - if keyVal.IsNull() { - return cty.NilVal, fmt.Errorf("argument %d is a null key", i+1) - } - key := keyVal.AsString() - - val := args[i+1] - - var variable cty.Value - err = gocty.FromCtyValue(val, &variable) - if err != nil { - return cty.NilVal, err - } - - // We already know this will succeed because of the checks in our Type func above - variable, _ = convert.Convert(variable, retType.ElementType()) - - // Check for duplicate keys - if _, ok := outputMap[key]; ok { - return cty.NilVal, fmt.Errorf("argument %d is a duplicate key: %q", i+1, key) - } - outputMap[key] = variable - } - - return cty.MapVal(outputMap), nil - }, -}) - // MatchkeysFunc constructs a function that constructs a new list by taking a // subset of elements from one list whose indexes match the corresponding // indexes of values in another list. @@ -499,6 +400,83 @@ var MatchkeysFunc = function.New(&function.Spec{ }, }) +// OneFunc returns either the first element of a one-element list, or null +// if given a zero-element list. +var OneFunc = function.New(&function.Spec{ + Params: []function.Parameter{ + { + Name: "list", + Type: cty.DynamicPseudoType, + }, + }, + Type: func(args []cty.Value) (cty.Type, error) { + ty := args[0].Type() + switch { + case ty.IsListType() || ty.IsSetType(): + return ty.ElementType(), nil + case ty.IsTupleType(): + etys := ty.TupleElementTypes() + switch len(etys) { + case 0: + // No specific type information, so we'll ultimately return + // a null value of unknown type. + return cty.DynamicPseudoType, nil + case 1: + return etys[0], nil + } + } + return cty.NilType, function.NewArgErrorf(0, "must be a list, set, or tuple value with either zero or one elements") + }, + Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { + val := args[0] + ty := val.Type() + + // Our parameter spec above doesn't set AllowUnknown or AllowNull, + // so we can assume our top-level collection is both known and non-null + // in here. + + switch { + case ty.IsListType() || ty.IsSetType(): + lenVal := val.Length() + if !lenVal.IsKnown() { + return cty.UnknownVal(retType), nil + } + var l int + err := gocty.FromCtyValue(lenVal, &l) + if err != nil { + // It would be very strange to get here, because that would + // suggest that the length is either not a number or isn't + // an integer, which would suggest a bug in cty. + return cty.NilVal, fmt.Errorf("invalid collection length: %s", err) + } + switch l { + case 0: + return cty.NullVal(retType), nil + case 1: + var ret cty.Value + // We'll use an iterator here because that works for both lists + // and sets, whereas indexing directly would only work for lists. + // Since we've just checked the length, we should only actually + // run this loop body once. + for it := val.ElementIterator(); it.Next(); { + _, ret = it.Element() + } + return ret, nil + } + case ty.IsTupleType(): + etys := ty.TupleElementTypes() + switch len(etys) { + case 0: + return cty.NullVal(retType), nil + case 1: + ret := val.Index(cty.NumberIntVal(0)) + return ret, nil + } + } + return cty.NilVal, function.NewArgErrorf(0, "must be a list, set, or tuple value with either zero or one elements") + }, +}) + // SumFunc constructs a function that returns the sum of all // numbers provided in a list var SumFunc = function.New(&function.Spec{ @@ -522,27 +500,45 @@ var SumFunc = function.New(&function.Spec{ arg := args[0].AsValueSlice() ty := args[0].Type() - var i float64 - var s float64 - if !ty.IsListType() && !ty.IsSetType() && !ty.IsTupleType() { return cty.NilVal, function.NewArgErrorf(0, fmt.Sprintf("argument must be list, set, or tuple. Received %s", ty.FriendlyName())) } - if !args[0].IsKnown() { + if !args[0].IsWhollyKnown() { return cty.UnknownVal(cty.Number), nil } - for _, v := range arg { + // big.Float.Add can panic if the input values are opposing infinities, + // so we must catch that here in order to remain within + // the cty Function abstraction. + defer func() { + if r := recover(); r != nil { + if _, ok := r.(big.ErrNaN); ok { + ret = cty.NilVal + err = fmt.Errorf("can't compute sum of opposing infinities") + } else { + // not a panic we recognize + panic(r) + } + } + }() - if err := gocty.FromCtyValue(v, &i); err != nil { - return cty.UnknownVal(cty.Number), function.NewArgErrorf(0, "argument must be list, set, or tuple of number values") - } else { - s += i + s := arg[0] + if s.IsNull() { + return cty.NilVal, function.NewArgErrorf(0, "argument must be list, set, or tuple of number values") + } + for _, v := range arg[1:] { + if v.IsNull() { + return cty.NilVal, function.NewArgErrorf(0, "argument must be list, set, or tuple of number values") + } + v, err = convert.Convert(v, cty.Number) + if err != nil { + return cty.NilVal, function.NewArgErrorf(0, "argument must be list, set, or tuple of number values") } + s = s.Add(v) } - return cty.NumberFloatVal(s), nil + return s, nil }, }) @@ -600,19 +596,47 @@ var TransposeFunc = function.New(&function.Spec{ }, }) -// helper function to add an element to a list, if it does not already exist -func appendIfMissing(slice []cty.Value, element cty.Value) ([]cty.Value, error) { - for _, ele := range slice { - eq, err := stdlib.Equal(ele, element) - if err != nil { - return slice, err - } - if eq.True() { - return slice, nil - } - } - return append(slice, element), nil -} +// ListFunc constructs a function that takes an arbitrary number of arguments +// and returns a list containing those values in the same order. +// +// This function is deprecated in Terraform v0.12 +var ListFunc = function.New(&function.Spec{ + Params: []function.Parameter{}, + VarParam: &function.Parameter{ + Name: "vals", + Type: cty.DynamicPseudoType, + AllowUnknown: true, + AllowDynamicType: true, + AllowNull: true, + }, + Type: func(args []cty.Value) (ret cty.Type, err error) { + return cty.DynamicPseudoType, fmt.Errorf("the \"list\" function was deprecated in Terraform v0.12 and is no longer available; use tolist([ ... ]) syntax to write a literal list") + }, + Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { + return cty.DynamicVal, fmt.Errorf("the \"list\" function was deprecated in Terraform v0.12 and is no longer available; use tolist([ ... ]) syntax to write a literal list") + }, +}) + +// MapFunc constructs a function that takes an even number of arguments and +// returns a map whose elements are constructed from consecutive pairs of arguments. +// +// This function is deprecated in Terraform v0.12 +var MapFunc = function.New(&function.Spec{ + Params: []function.Parameter{}, + VarParam: &function.Parameter{ + Name: "vals", + Type: cty.DynamicPseudoType, + AllowUnknown: true, + AllowDynamicType: true, + AllowNull: true, + }, + Type: func(args []cty.Value) (ret cty.Type, err error) { + return cty.DynamicPseudoType, fmt.Errorf("the \"map\" function was deprecated in Terraform v0.12 and is no longer available; use tomap({ ... }) syntax to write a literal map") + }, + Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { + return cty.DynamicVal, fmt.Errorf("the \"map\" function was deprecated in Terraform v0.12 and is no longer available; use tomap({ ... }) syntax to write a literal map") + }, +}) // Length returns the number of elements in the given collection or number of // Unicode characters in the given string. @@ -620,12 +644,18 @@ func Length(collection cty.Value) (cty.Value, error) { return LengthFunc.Call([]cty.Value{collection}) } -// AllTrue returns true if all elements of the collection are true or "true". -// If the collection is empty, return true. +// AllTrue returns true if all elements of the list are true. If the list is empty, +// return true. func AllTrue(collection cty.Value) (cty.Value, error) { return AllTrueFunc.Call([]cty.Value{collection}) } +// AnyTrue returns true if any element of the list is true. If the list is empty, +// return false. +func AnyTrue(collection cty.Value) (cty.Value, error) { + return AnyTrueFunc.Call([]cty.Value{collection}) +} + // Coalesce takes any number of arguments and returns the first one that isn't empty. func Coalesce(args ...cty.Value) (cty.Value, error) { return CoalesceFunc.Call(args) @@ -661,6 +691,12 @@ func Matchkeys(values, keys, searchset cty.Value) (cty.Value, error) { return MatchkeysFunc.Call([]cty.Value{values, keys, searchset}) } +// One returns either the first element of a one-element list, or null +// if given a zero-element list.. +func One(list cty.Value) (cty.Value, error) { + return OneFunc.Call([]cty.Value{list}) +} + // Sum adds numbers in a list, set, or tuple func Sum(list cty.Value) (cty.Value, error) { return SumFunc.Call([]cty.Value{list}) diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/collection_test.go b/vendor/github.com/hashicorp/terraform/lang/funcs/collection_test.go index d9855519..3ca3f918 100644 --- a/vendor/github.com/hashicorp/terraform/lang/funcs/collection_test.go +++ b/vendor/github.com/hashicorp/terraform/lang/funcs/collection_test.go @@ -2,10 +2,10 @@ package funcs import ( "fmt" + "math" "testing" "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" ) func TestLength(t *testing.T) { @@ -122,6 +122,54 @@ func TestLength(t *testing.T) { cty.DynamicVal, cty.UnknownVal(cty.Number), }, + { // Marked collections return a marked length + cty.ListVal([]cty.Value{ + cty.StringVal("hello"), + cty.StringVal("world"), + }).Mark("secret"), + cty.NumberIntVal(2).Mark("secret"), + }, + { // Marks on values in unmarked collections do not propagate + cty.ListVal([]cty.Value{ + cty.StringVal("hello").Mark("a"), + cty.StringVal("world").Mark("b"), + }), + cty.NumberIntVal(2), + }, + { // Marked strings return a marked length + cty.StringVal("hello world").Mark("secret"), + cty.NumberIntVal(11).Mark("secret"), + }, + { // Marked tuples return a marked length + cty.TupleVal([]cty.Value{ + cty.StringVal("hello"), + cty.StringVal("world"), + }).Mark("secret"), + cty.NumberIntVal(2).Mark("secret"), + }, + { // Marks on values in unmarked tuples do not propagate + cty.TupleVal([]cty.Value{ + cty.StringVal("hello").Mark("a"), + cty.StringVal("world").Mark("b"), + }), + cty.NumberIntVal(2), + }, + { // Marked objects return a marked length + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + "b": cty.StringVal("world"), + "c": cty.StringVal("nice to meet you"), + }).Mark("secret"), + cty.NumberIntVal(3).Mark("secret"), + }, + { // Marks on object attribute values do not propagate + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello").Mark("a"), + "b": cty.StringVal("world").Mark("b"), + "c": cty.StringVal("nice to meet you").Mark("c"), + }), + cty.NumberIntVal(3), + }, } for _, test := range tests { @@ -146,32 +194,93 @@ func TestAllTrue(t *testing.T) { Err bool }{ { - cty.ListValEmpty(cty.String), + cty.ListValEmpty(cty.Bool), cty.True, false, }, { - cty.TupleVal([]cty.Value{}), + cty.ListVal([]cty.Value{cty.True}), cty.True, false, }, { - cty.SetValEmpty(cty.Bool), - cty.True, + cty.ListVal([]cty.Value{cty.False}), + cty.False, false, }, { - cty.ListVal([]cty.Value{cty.True}), - cty.True, + cty.ListVal([]cty.Value{cty.True, cty.False}), + cty.False, false, }, { - cty.ListVal([]cty.Value{cty.StringVal("true")}), - cty.True, + cty.ListVal([]cty.Value{cty.False, cty.True}), + cty.False, + false, + }, + { + cty.ListVal([]cty.Value{cty.True, cty.NullVal(cty.Bool)}), + cty.False, + false, + }, + { + cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}), + cty.UnknownVal(cty.Bool), + false, + }, + { + cty.ListVal([]cty.Value{ + cty.UnknownVal(cty.Bool), + cty.UnknownVal(cty.Bool), + }), + cty.UnknownVal(cty.Bool), + false, + }, + { + cty.UnknownVal(cty.List(cty.Bool)), + cty.UnknownVal(cty.Bool), + false, + }, + { + cty.NullVal(cty.List(cty.Bool)), + cty.NilVal, + true, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("alltrue(%#v)", test.Collection), func(t *testing.T) { + got, err := AllTrue(test.Collection) + + if test.Err { + if err == nil { + t.Fatal("succeeded; want error") + } + return + } else if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !got.RawEquals(test.Want) { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) + } + }) + } +} + +func TestAnyTrue(t *testing.T) { + tests := []struct { + Collection cty.Value + Want cty.Value + Err bool + }{ + { + cty.ListValEmpty(cty.Bool), + cty.False, false, }, { - cty.TupleVal([]cty.Value{cty.True, cty.StringVal("true")}), + cty.ListVal([]cty.Value{cty.True}), cty.True, false, }, @@ -182,39 +291,55 @@ func TestAllTrue(t *testing.T) { }, { cty.ListVal([]cty.Value{cty.True, cty.False}), - cty.False, + cty.True, false, }, { cty.ListVal([]cty.Value{cty.False, cty.True}), - cty.False, + cty.True, false, }, { - cty.ListVal([]cty.Value{cty.NumberIntVal(1)}), - cty.False, + cty.ListVal([]cty.Value{cty.NullVal(cty.Bool), cty.True}), + cty.True, false, }, { - cty.StringVal("true"), - cty.False, - true, + cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}), + cty.UnknownVal(cty.Bool), + false, }, { - cty.ListVal([]cty.Value{cty.ListValEmpty(cty.String)}), - cty.False, + cty.ListVal([]cty.Value{ + cty.UnknownVal(cty.Bool), + cty.False, + }), + cty.UnknownVal(cty.Bool), + false, + }, + { + cty.ListVal([]cty.Value{ + cty.UnknownVal(cty.Bool), + cty.True, + }), + cty.True, false, }, { - cty.ListVal([]cty.Value{cty.UnknownVal(cty.String)}), + cty.UnknownVal(cty.List(cty.Bool)), cty.UnknownVal(cty.Bool), false, }, + { + cty.NullVal(cty.List(cty.Bool)), + cty.NilVal, + true, + }, } for _, test := range tests { - t.Run(fmt.Sprintf("alltrue(%#v)", test.Collection), func(t *testing.T) { - got, err := AllTrue(test.Collection) + t.Run(fmt.Sprintf("anytrue(%#v)", test.Collection), func(t *testing.T) { + got, err := AnyTrue(test.Collection) if test.Err { if err == nil { @@ -439,83 +564,6 @@ func TestIndex(t *testing.T) { } } -func TestList(t *testing.T) { - tests := []struct { - Values []cty.Value - Want cty.Value - Err bool - }{ - { - []cty.Value{ - cty.NilVal, - }, - cty.NilVal, - true, - }, - { - []cty.Value{ - cty.StringVal("Hello"), - }, - cty.ListVal([]cty.Value{ - cty.StringVal("Hello"), - }), - false, - }, - { - []cty.Value{ - cty.StringVal("Hello"), - cty.StringVal("World"), - }, - cty.ListVal([]cty.Value{ - cty.StringVal("Hello"), - cty.StringVal("World"), - }), - false, - }, - { - []cty.Value{ - cty.StringVal("Hello"), - cty.NumberIntVal(42), - }, - cty.ListVal([]cty.Value{ - cty.StringVal("Hello"), - cty.StringVal("42"), - }), - false, - }, - { - []cty.Value{ - cty.StringVal("Hello"), - cty.UnknownVal(cty.String), - }, - cty.ListVal([]cty.Value{ - cty.StringVal("Hello"), - cty.UnknownVal(cty.String), - }), - false, - }, - } - - for _, test := range tests { - t.Run(fmt.Sprintf("list(%#v)", test.Values), func(t *testing.T) { - got, err := List(test.Values...) - - if test.Err { - if err == nil { - t.Fatal("succeeded; want error") - } - return - } else if err != nil { - t.Fatalf("unexpected error: %s", err) - } - - if !got.RawEquals(test.Want) { - t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) - } - }) - } -} - func TestLookup(t *testing.T) { simpleMap := cty.MapVal(map[string]cty.Value{ "foo": cty.StringVal("bar"), @@ -747,179 +795,98 @@ func TestLookup(t *testing.T) { cty.DynamicVal, // if the key is unknown then we don't know which object attribute and thus can't know the type false, }, - } - - for _, test := range tests { - t.Run(fmt.Sprintf("lookup(%#v)", test.Values), func(t *testing.T) { - got, err := Lookup(test.Values...) - - if test.Err { - if err == nil { - t.Fatal("succeeded; want error") - } - return - } else if err != nil { - t.Fatalf("unexpected error: %s", err) - } - - if !got.RawEquals(test.Want) { - t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) - } - }) - } -} - -func TestMap(t *testing.T) { - tests := []struct { - Values []cty.Value - Want cty.Value - Err bool - }{ - { - []cty.Value{ - cty.StringVal("hello"), - cty.StringVal("world"), - }, - cty.MapVal(map[string]cty.Value{ - "hello": cty.StringVal("world"), - }), - false, - }, - { + { // successful marked collection lookup returns marked value []cty.Value{ - cty.StringVal("hello"), - cty.UnknownVal(cty.String), + cty.MapVal(map[string]cty.Value{ + "boop": cty.StringVal("beep"), + }).Mark("a"), + cty.StringVal("boop"), + cty.StringVal("nope"), }, - cty.UnknownVal(cty.Map(cty.String)), + cty.StringVal("beep").Mark("a"), false, }, - { + { // apply collection marks to unknown return vaue []cty.Value{ - cty.StringVal("hello"), - cty.StringVal("world"), - cty.StringVal("what's"), - cty.StringVal("up"), + cty.MapVal(map[string]cty.Value{ + "boop": cty.StringVal("beep"), + "frob": cty.UnknownVal(cty.String), + }).Mark("a"), + cty.StringVal("frob"), + cty.StringVal("nope"), }, - cty.MapVal(map[string]cty.Value{ - "hello": cty.StringVal("world"), - "what's": cty.StringVal("up"), - }), + cty.UnknownVal(cty.String).Mark("a"), false, }, - { + { // propagate collection marks to default when returning []cty.Value{ - cty.StringVal("hello"), - cty.NumberIntVal(1), - cty.StringVal("goodbye"), - cty.NumberIntVal(42), + cty.MapVal(map[string]cty.Value{ + "boop": cty.StringVal("beep"), + }).Mark("a"), + cty.StringVal("frob"), + cty.StringVal("nope").Mark("b"), }, - cty.MapVal(map[string]cty.Value{ - "hello": cty.NumberIntVal(1), - "goodbye": cty.NumberIntVal(42), - }), + cty.StringVal("nope").WithMarks(cty.NewValueMarks("a", "b")), false, }, - { // convert numbers to strings + { // on unmarked collection, return only marks from found value []cty.Value{ - cty.StringVal("hello"), - cty.NumberIntVal(1), - cty.StringVal("goodbye"), - cty.StringVal("42"), + cty.MapVal(map[string]cty.Value{ + "boop": cty.StringVal("beep").Mark("a"), + "frob": cty.StringVal("honk").Mark("b"), + }), + cty.StringVal("frob"), + cty.StringVal("nope").Mark("c"), }, - cty.MapVal(map[string]cty.Value{ - "hello": cty.StringVal("1"), - "goodbye": cty.StringVal("42"), - }), + cty.StringVal("honk").Mark("b"), false, }, - { // convert number keys to strings + { // on unmarked collection, return default exactly on missing []cty.Value{ - cty.NumberIntVal(1), - cty.StringVal("hello"), - cty.NumberIntVal(2), - cty.StringVal("goodbye"), + cty.MapVal(map[string]cty.Value{ + "boop": cty.StringVal("beep").Mark("a"), + "frob": cty.StringVal("honk").Mark("b"), + }), + cty.StringVal("squish"), + cty.StringVal("nope").Mark("c"), }, - cty.MapVal(map[string]cty.Value{ - "1": cty.StringVal("hello"), - "2": cty.StringVal("goodbye"), - }), + cty.StringVal("nope").Mark("c"), false, }, - { // map of lists is okay + { // retain marks on default if converted []cty.Value{ - cty.StringVal("hello"), - cty.ListVal([]cty.Value{ - cty.StringVal("world"), - }), - cty.StringVal("what's"), - cty.ListVal([]cty.Value{ - cty.StringVal("up"), + cty.MapVal(map[string]cty.Value{ + "boop": cty.StringVal("beep").Mark("a"), + "frob": cty.StringVal("honk").Mark("b"), }), + cty.StringVal("squish"), + cty.NumberIntVal(5).Mark("c"), }, - cty.MapVal(map[string]cty.Value{ - "hello": cty.ListVal([]cty.Value{cty.StringVal("world")}), - "what's": cty.ListVal([]cty.Value{cty.StringVal("up")}), - }), + cty.StringVal("5").Mark("c"), false, }, - { // map of maps is okay + { // propagate marks from key []cty.Value{ - cty.StringVal("hello"), - cty.MapVal(map[string]cty.Value{ - "there": cty.StringVal("world"), - }), - cty.StringVal("what's"), cty.MapVal(map[string]cty.Value{ - "really": cty.StringVal("up"), + "boop": cty.StringVal("beep"), + "frob": cty.StringVal("honk"), }), + cty.StringVal("boop").Mark("a"), + cty.StringVal("nope"), }, - cty.MapVal(map[string]cty.Value{ - "hello": cty.MapVal(map[string]cty.Value{ - "there": cty.StringVal("world"), - }), - "what's": cty.MapVal(map[string]cty.Value{ - "really": cty.StringVal("up"), - }), - }), + cty.StringVal("beep").Mark("a"), false, }, - { // single argument returns an error - []cty.Value{ - cty.StringVal("hello"), - }, - cty.NilVal, - true, - }, - { // duplicate keys returns an error - []cty.Value{ - cty.StringVal("hello"), - cty.StringVal("world"), - cty.StringVal("hello"), - cty.StringVal("universe"), - }, - cty.NilVal, - true, - }, - { // null key returns an error - []cty.Value{ - cty.NullVal(cty.DynamicPseudoType), - cty.NumberIntVal(5), - }, - cty.NilVal, - true, - }, } for _, test := range tests { - t.Run(fmt.Sprintf("map(%#v)", test.Values), func(t *testing.T) { - got, err := Map(test.Values...) + t.Run(fmt.Sprintf("lookup(%#v)", test.Values), func(t *testing.T) { + got, err := Lookup(test.Values...) + if test.Err { if err == nil { t.Fatal("succeeded; want error") } - if _, ok := err.(function.PanicError); ok { - t.Fatalf("unexpected panic: %s", err) - } return } else if err != nil { t.Fatalf("unexpected error: %s", err) @@ -1156,11 +1123,292 @@ func TestMatchkeys(t *testing.T) { } } +func TestOne(t *testing.T) { + tests := []struct { + List cty.Value + Want cty.Value + Err string + }{ + { + cty.ListVal([]cty.Value{ + cty.NumberIntVal(1), + }), + cty.NumberIntVal(1), + "", + }, + { + cty.ListValEmpty(cty.Number), + cty.NullVal(cty.Number), + "", + }, + { + cty.ListVal([]cty.Value{ + cty.NumberIntVal(1), + cty.NumberIntVal(2), + cty.NumberIntVal(3), + }), + cty.NilVal, + "must be a list, set, or tuple value with either zero or one elements", + }, + { + cty.ListVal([]cty.Value{ + cty.UnknownVal(cty.Number), + }), + cty.UnknownVal(cty.Number), + "", + }, + { + cty.ListVal([]cty.Value{ + cty.UnknownVal(cty.Number), + cty.UnknownVal(cty.Number), + }), + cty.NilVal, + "must be a list, set, or tuple value with either zero or one elements", + }, + { + cty.UnknownVal(cty.List(cty.String)), + cty.UnknownVal(cty.String), + "", + }, + { + cty.NullVal(cty.List(cty.String)), + cty.NilVal, + "argument must not be null", + }, + { + cty.ListVal([]cty.Value{ + cty.NumberIntVal(1), + }).Mark("boop"), + cty.NumberIntVal(1).Mark("boop"), + "", + }, + { + cty.ListValEmpty(cty.Bool).Mark("boop"), + cty.NullVal(cty.Bool).Mark("boop"), + "", + }, + { + cty.ListVal([]cty.Value{ + cty.NumberIntVal(1).Mark("boop"), + }), + cty.NumberIntVal(1).Mark("boop"), + "", + }, + + { + cty.SetVal([]cty.Value{ + cty.NumberIntVal(1), + }), + cty.NumberIntVal(1), + "", + }, + { + cty.SetValEmpty(cty.Number), + cty.NullVal(cty.Number), + "", + }, + { + cty.SetVal([]cty.Value{ + cty.NumberIntVal(1), + cty.NumberIntVal(2), + cty.NumberIntVal(3), + }), + cty.NilVal, + "must be a list, set, or tuple value with either zero or one elements", + }, + { + cty.SetVal([]cty.Value{ + cty.UnknownVal(cty.Number), + }), + cty.UnknownVal(cty.Number), + "", + }, + { + cty.SetVal([]cty.Value{ + cty.UnknownVal(cty.Number), + cty.UnknownVal(cty.Number), + }), + // The above would be valid if those two unknown values were + // equal known values, so this returns unknown rather than failing. + cty.UnknownVal(cty.Number), + "", + }, + { + cty.UnknownVal(cty.Set(cty.String)), + cty.UnknownVal(cty.String), + "", + }, + { + cty.NullVal(cty.Set(cty.String)), + cty.NilVal, + "argument must not be null", + }, + { + cty.SetVal([]cty.Value{ + cty.NumberIntVal(1), + }).Mark("boop"), + cty.NumberIntVal(1).Mark("boop"), + "", + }, + { + cty.SetValEmpty(cty.Bool).Mark("boop"), + cty.NullVal(cty.Bool).Mark("boop"), + "", + }, + { + cty.SetVal([]cty.Value{ + cty.NumberIntVal(1).Mark("boop"), + }), + cty.NumberIntVal(1).Mark("boop"), + "", + }, + + { + cty.TupleVal([]cty.Value{ + cty.NumberIntVal(1), + }), + cty.NumberIntVal(1), + "", + }, + { + cty.EmptyTupleVal, + cty.NullVal(cty.DynamicPseudoType), + "", + }, + { + cty.TupleVal([]cty.Value{ + cty.NumberIntVal(1), + cty.NumberIntVal(2), + cty.NumberIntVal(3), + }), + cty.NilVal, + "must be a list, set, or tuple value with either zero or one elements", + }, + { + cty.TupleVal([]cty.Value{ + cty.UnknownVal(cty.Number), + }), + cty.UnknownVal(cty.Number), + "", + }, + { + cty.TupleVal([]cty.Value{ + cty.UnknownVal(cty.Number), + cty.UnknownVal(cty.Number), + }), + cty.NilVal, + "must be a list, set, or tuple value with either zero or one elements", + }, + { + cty.UnknownVal(cty.EmptyTuple), + // Could actually return null here, but don't for consistency with unknown lists + cty.UnknownVal(cty.DynamicPseudoType), + "", + }, + { + cty.UnknownVal(cty.Tuple([]cty.Type{cty.Bool})), + cty.UnknownVal(cty.Bool), + "", + }, + { + cty.UnknownVal(cty.Tuple([]cty.Type{cty.Bool, cty.Number})), + cty.NilVal, + "must be a list, set, or tuple value with either zero or one elements", + }, + { + cty.NullVal(cty.EmptyTuple), + cty.NilVal, + "argument must not be null", + }, + { + cty.NullVal(cty.Tuple([]cty.Type{cty.Bool})), + cty.NilVal, + "argument must not be null", + }, + { + cty.NullVal(cty.Tuple([]cty.Type{cty.Bool, cty.Number})), + cty.NilVal, + "argument must not be null", + }, + { + cty.TupleVal([]cty.Value{ + cty.NumberIntVal(1), + }).Mark("boop"), + cty.NumberIntVal(1).Mark("boop"), + "", + }, + { + cty.EmptyTupleVal.Mark("boop"), + cty.NullVal(cty.DynamicPseudoType).Mark("boop"), + "", + }, + { + cty.TupleVal([]cty.Value{ + cty.NumberIntVal(1).Mark("boop"), + }), + cty.NumberIntVal(1).Mark("boop"), + "", + }, + + { + cty.DynamicVal, + cty.DynamicVal, + "", + }, + { + cty.NullVal(cty.DynamicPseudoType), + cty.NilVal, + "argument must not be null", + }, + { + cty.MapValEmpty(cty.String), + cty.NilVal, + "must be a list, set, or tuple value with either zero or one elements", + }, + { + cty.EmptyObjectVal, + cty.NilVal, + "must be a list, set, or tuple value with either zero or one elements", + }, + { + cty.True, + cty.NilVal, + "must be a list, set, or tuple value with either zero or one elements", + }, + { + cty.UnknownVal(cty.Bool), + cty.NilVal, + "must be a list, set, or tuple value with either zero or one elements", + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("one(%#v)", test.List), func(t *testing.T) { + got, err := One(test.List) + + if test.Err != "" { + if err == nil { + t.Fatal("succeeded; want error") + } else if got, want := err.Error(), test.Err; got != want { + t.Fatalf("wrong error\n got: %s\nwant: %s", got, want) + } + return + } else if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !test.Want.RawEquals(got) { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) + } + }) + } +} + func TestSum(t *testing.T) { tests := []struct { List cty.Value Want cty.Value - Err bool + Err string }{ { cty.ListVal([]cty.Value{ @@ -1169,7 +1417,7 @@ func TestSum(t *testing.T) { cty.NumberIntVal(3), }), cty.NumberIntVal(6), - false, + "", }, { cty.ListVal([]cty.Value{ @@ -1180,7 +1428,7 @@ func TestSum(t *testing.T) { cty.NumberIntVal(234), }), cty.NumberIntVal(66685532), - false, + "", }, { cty.ListVal([]cty.Value{ @@ -1189,7 +1437,7 @@ func TestSum(t *testing.T) { cty.StringVal("c"), }), cty.UnknownVal(cty.String), - true, + "argument must be list, set, or tuple of number values", }, { cty.ListVal([]cty.Value{ @@ -1198,7 +1446,7 @@ func TestSum(t *testing.T) { cty.NumberIntVal(5), }), cty.NumberIntVal(-4), - false, + "", }, { cty.ListVal([]cty.Value{ @@ -1207,7 +1455,7 @@ func TestSum(t *testing.T) { cty.NumberFloatVal(5.7), }), cty.NumberFloatVal(35.3), - false, + "", }, { cty.ListVal([]cty.Value{ @@ -1216,12 +1464,20 @@ func TestSum(t *testing.T) { cty.NumberFloatVal(-5.7), }), cty.NumberFloatVal(-35.3), - false, + "", }, { cty.ListVal([]cty.Value{cty.NullVal(cty.Number)}), cty.NilVal, - true, + "argument must be list, set, or tuple of number values", + }, + { + cty.ListVal([]cty.Value{ + cty.NumberIntVal(5), + cty.NullVal(cty.Number), + }), + cty.NilVal, + "argument must be list, set, or tuple of number values", }, { cty.SetVal([]cty.Value{ @@ -1230,7 +1486,7 @@ func TestSum(t *testing.T) { cty.StringVal("c"), }), cty.UnknownVal(cty.String), - true, + "argument must be list, set, or tuple of number values", }, { cty.SetVal([]cty.Value{ @@ -1239,7 +1495,7 @@ func TestSum(t *testing.T) { cty.NumberIntVal(5), }), cty.NumberIntVal(-4), - false, + "", }, { cty.SetVal([]cty.Value{ @@ -1248,7 +1504,7 @@ func TestSum(t *testing.T) { cty.NumberIntVal(30), }), cty.NumberIntVal(65), - false, + "", }, { cty.SetVal([]cty.Value{ @@ -1257,14 +1513,14 @@ func TestSum(t *testing.T) { cty.NumberFloatVal(3), }), cty.NumberFloatVal(2354), - false, + "", }, { cty.SetVal([]cty.Value{ cty.NumberFloatVal(2), }), cty.NumberFloatVal(2), - false, + "", }, { cty.SetVal([]cty.Value{ @@ -1275,7 +1531,7 @@ func TestSum(t *testing.T) { cty.NumberFloatVal(-4), }), cty.NumberFloatVal(-199), - false, + "", }, { cty.TupleVal([]cty.Value{ @@ -1284,27 +1540,53 @@ func TestSum(t *testing.T) { cty.NumberIntVal(38), }), cty.UnknownVal(cty.String), - true, + "argument must be list, set, or tuple of number values", }, { cty.NumberIntVal(12), cty.NilVal, - true, + "cannot sum noniterable", }, { cty.ListValEmpty(cty.Number), cty.NilVal, - true, + "cannot sum an empty list", }, { cty.MapVal(map[string]cty.Value{"hello": cty.True}), cty.NilVal, - true, + "argument must be list, set, or tuple. Received map of bool", }, { cty.UnknownVal(cty.Number), cty.UnknownVal(cty.Number), - false, + "", + }, + { + cty.UnknownVal(cty.List(cty.Number)), + cty.UnknownVal(cty.Number), + "", + }, + { // known list containing unknown values + cty.ListVal([]cty.Value{cty.UnknownVal(cty.Number)}), + cty.UnknownVal(cty.Number), + "", + }, + { // numbers too large to represent as float64 + cty.ListVal([]cty.Value{ + cty.MustParseNumberVal("1e+500"), + cty.MustParseNumberVal("1e+500"), + }), + cty.MustParseNumberVal("2e+500"), + "", + }, + { // edge case we have a special error handler for + cty.ListVal([]cty.Value{ + cty.NumberFloatVal(math.Inf(1)), + cty.NumberFloatVal(math.Inf(-1)), + }), + cty.NilVal, + "can't compute sum of opposing infinities", }, } @@ -1312,9 +1594,11 @@ func TestSum(t *testing.T) { t.Run(fmt.Sprintf("sum(%#v)", test.List), func(t *testing.T) { got, err := Sum(test.List) - if test.Err { + if test.Err != "" { if err == nil { t.Fatal("succeeded; want error") + } else if got, want := err.Error(), test.Err; got != want { + t.Fatalf("wrong error\n got: %s\nwant: %s", got, want) } return } else if err != nil { @@ -1387,6 +1671,99 @@ func TestTranspose(t *testing.T) { cty.NilVal, true, }, + { // marks (deep or shallow) on any elements will propegate to the entire return value + cty.MapVal(map[string]cty.Value{ + "key1": cty.ListVal([]cty.Value{ + cty.StringVal("a").Mark("beep"), // mark on the inner list element + cty.StringVal("b"), + }), + "key2": cty.ListVal([]cty.Value{ + cty.StringVal("a"), + cty.StringVal("b"), + cty.StringVal("c"), + }).Mark("boop"), // mark on the map element + "key3": cty.ListVal([]cty.Value{ + cty.StringVal("c"), + }), + "key4": cty.ListValEmpty(cty.String), + }), + cty.MapVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.StringVal("key1"), + cty.StringVal("key2"), + }), + "b": cty.ListVal([]cty.Value{ + cty.StringVal("key1"), + cty.StringVal("key2"), + }), + "c": cty.ListVal([]cty.Value{ + cty.StringVal("key2"), + cty.StringVal("key3")}), + }).WithMarks(cty.NewValueMarks("beep", "boop")), + false, + }, + { // Marks on the input value will be applied to the return value + cty.MapVal(map[string]cty.Value{ + "key1": cty.ListVal([]cty.Value{ + cty.StringVal("a"), + cty.StringVal("b"), + }), + "key2": cty.ListVal([]cty.Value{ + cty.StringVal("a"), + cty.StringVal("b"), + cty.StringVal("c"), + }), + "key3": cty.ListVal([]cty.Value{ + cty.StringVal("c"), + }), + }).Mark("beep"), // mark on the entire input value + cty.MapVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.StringVal("key1"), + cty.StringVal("key2"), + }), + "b": cty.ListVal([]cty.Value{ + cty.StringVal("key1"), + cty.StringVal("key2"), + }), + "c": cty.ListVal([]cty.Value{ + cty.StringVal("key2"), + cty.StringVal("key3"), + }), + }).Mark("beep"), + false, + }, + { // Marks on the entire input value AND inner elements (deep or shallow) ALL apply to the return + cty.MapVal(map[string]cty.Value{ + "key1": cty.ListVal([]cty.Value{ + cty.StringVal("a"), + cty.StringVal("b"), + }).Mark("beep"), // mark on the map element + "key2": cty.ListVal([]cty.Value{ + cty.StringVal("a"), + cty.StringVal("b"), + cty.StringVal("c"), + }), + "key3": cty.ListVal([]cty.Value{ + cty.StringVal("c").Mark("boop"), // mark on the inner list element + }), + }).Mark("bloop"), // mark on the entire input value + cty.MapVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.StringVal("key1"), + cty.StringVal("key2"), + }), + "b": cty.ListVal([]cty.Value{ + cty.StringVal("key1"), + cty.StringVal("key2"), + }), + "c": cty.ListVal([]cty.Value{ + cty.StringVal("key2"), + cty.StringVal("key3"), + }), + }).WithMarks(cty.NewValueMarks("beep", "boop", "bloop")), + false, + }, } for _, test := range tests { diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/conversion.go b/vendor/github.com/hashicorp/terraform/lang/funcs/conversion.go index 83f85979..b557bb5a 100644 --- a/vendor/github.com/hashicorp/terraform/lang/funcs/conversion.go +++ b/vendor/github.com/hashicorp/terraform/lang/funcs/conversion.go @@ -1,7 +1,10 @@ package funcs import ( + "fmt" + "sort" "strconv" + "strings" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/convert" @@ -28,8 +31,9 @@ func MakeToFunc(wantTy cty.Type) function.Function { // messages to be more appropriate for an explicit type // conversion, whereas the cty function system produces // messages aimed at _implicit_ type conversions. - Type: cty.DynamicPseudoType, - AllowNull: true, + Type: cty.DynamicPseudoType, + AllowNull: true, + AllowMarked: true, }, }, Type: func(args []cty.Value) (cty.Type, error) { @@ -65,6 +69,11 @@ func MakeToFunc(wantTy cty.Type) function.Function { // once we note that the value isn't either "true" or "false". gotTy := args[0].Type() switch { + case args[0].ContainsMarked(): + // Generic message so we won't inadvertently disclose + // information about sensitive values. + return cty.NilVal, function.NewArgErrorf(0, "cannot convert this sensitive %s to %s", gotTy.FriendlyName(), wantTy.FriendlyNameForConstraint()) + case gotTy == cty.String && wantTy == cty.Bool: what := "string" if !args[0].IsNull() { @@ -85,3 +94,128 @@ func MakeToFunc(wantTy cty.Type) function.Function { }, }) } + +var TypeFunc = function.New(&function.Spec{ + Params: []function.Parameter{ + { + Name: "value", + Type: cty.DynamicPseudoType, + AllowDynamicType: true, + AllowUnknown: true, + AllowNull: true, + }, + }, + Type: function.StaticReturnType(cty.String), + Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { + return cty.StringVal(TypeString(args[0].Type())).Mark("raw"), nil + }, +}) + +// Modified copy of TypeString from go-cty: +// https://github.com/zclconf/go-cty-debug/blob/master/ctydebug/type_string.go +// +// TypeString returns a string representation of a given type that is +// reminiscent of Go syntax calling into the cty package but is mainly +// intended for easy human inspection of values in tests, debug output, etc. +// +// The resulting string will include newlines and indentation in order to +// increase the readability of complex structures. It always ends with a +// newline, so you can print this result directly to your output. +func TypeString(ty cty.Type) string { + var b strings.Builder + writeType(ty, &b, 0) + return b.String() +} + +func writeType(ty cty.Type, b *strings.Builder, indent int) { + switch { + case ty == cty.NilType: + b.WriteString("nil") + return + case ty.IsObjectType(): + atys := ty.AttributeTypes() + if len(atys) == 0 { + b.WriteString("object({})") + return + } + attrNames := make([]string, 0, len(atys)) + for name := range atys { + attrNames = append(attrNames, name) + } + sort.Strings(attrNames) + b.WriteString("object({\n") + indent++ + for _, name := range attrNames { + aty := atys[name] + b.WriteString(indentSpaces(indent)) + fmt.Fprintf(b, "%s: ", name) + writeType(aty, b, indent) + b.WriteString(",\n") + } + indent-- + b.WriteString(indentSpaces(indent)) + b.WriteString("})") + case ty.IsTupleType(): + etys := ty.TupleElementTypes() + if len(etys) == 0 { + b.WriteString("tuple([])") + return + } + b.WriteString("tuple([\n") + indent++ + for _, ety := range etys { + b.WriteString(indentSpaces(indent)) + writeType(ety, b, indent) + b.WriteString(",\n") + } + indent-- + b.WriteString(indentSpaces(indent)) + b.WriteString("])") + case ty.IsCollectionType(): + ety := ty.ElementType() + switch { + case ty.IsListType(): + b.WriteString("list(") + case ty.IsMapType(): + b.WriteString("map(") + case ty.IsSetType(): + b.WriteString("set(") + default: + // At the time of writing there are no other collection types, + // but we'll be robust here and just pass through the GoString + // of anything we don't recognize. + b.WriteString(ty.FriendlyName()) + return + } + // Because object and tuple types render split over multiple + // lines, a collection type container around them can end up + // being hard to see when scanning, so we'll generate some extra + // indentation to make a collection of structural type more visually + // distinct from the structural type alone. + complexElem := ety.IsObjectType() || ety.IsTupleType() + if complexElem { + indent++ + b.WriteString("\n") + b.WriteString(indentSpaces(indent)) + } + writeType(ty.ElementType(), b, indent) + if complexElem { + indent-- + b.WriteString(",\n") + b.WriteString(indentSpaces(indent)) + } + b.WriteString(")") + default: + // For any other type we'll just use its GoString and assume it'll + // follow the usual GoString conventions. + b.WriteString(ty.FriendlyName()) + } +} + +func indentSpaces(level int) string { + return strings.Repeat(" ", level) +} + +func Type(input []cty.Value) (cty.Value, error) { + return TypeFunc.Call(input) +} diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/conversion_test.go b/vendor/github.com/hashicorp/terraform/lang/funcs/conversion_test.go index ca0ac465..97460f5b 100644 --- a/vendor/github.com/hashicorp/terraform/lang/funcs/conversion_test.go +++ b/vendor/github.com/hashicorp/terraform/lang/funcs/conversion_test.go @@ -4,6 +4,7 @@ import ( "fmt" "testing" + "github.com/google/go-cmp/cmp" "github.com/zclconf/go-cty/cty" ) @@ -32,6 +33,18 @@ func TestTo(t *testing.T) { cty.NullVal(cty.String), ``, }, + { + cty.StringVal("a").Mark("boop"), + cty.String, + cty.StringVal("a").Mark("boop"), + ``, + }, + { + cty.NullVal(cty.String).Mark("boop"), + cty.String, + cty.NullVal(cty.String).Mark("boop"), + ``, + }, { cty.True, cty.String, @@ -44,12 +57,24 @@ func TestTo(t *testing.T) { cty.DynamicVal, `cannot convert "a" to bool; only the strings "true" or "false" are allowed`, }, + { + cty.StringVal("a").Mark("boop"), + cty.Bool, + cty.DynamicVal, + `cannot convert this sensitive string to bool`, + }, { cty.StringVal("a"), cty.Number, cty.DynamicVal, `cannot convert "a" to number; given string must be a decimal representation of a number`, }, + { + cty.StringVal("a").Mark("boop"), + cty.Number, + cty.DynamicVal, + `cannot convert this sensitive string to number`, + }, { cty.NullVal(cty.String), cty.Number, @@ -86,6 +111,30 @@ func TestTo(t *testing.T) { cty.MapVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("true")}), ``, }, + { + cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("world").Mark("boop")}), + cty.Map(cty.String), + cty.MapVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("world").Mark("boop")}), + ``, + }, + { + cty.ObjectVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("world")}).Mark("boop"), + cty.Map(cty.String), + cty.MapVal(map[string]cty.Value{"foo": cty.StringVal("hello"), "bar": cty.StringVal("world")}).Mark("boop"), + ``, + }, + { + cty.TupleVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world").Mark("boop")}), + cty.List(cty.String), + cty.ListVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world").Mark("boop")}), + ``, + }, + { + cty.TupleVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world")}).Mark("boop"), + cty.List(cty.String), + cty.ListVal([]cty.Value{cty.StringVal("hello"), cty.StringVal("world")}).Mark("boop"), + ``, + }, { cty.EmptyTupleVal, cty.String, @@ -129,3 +178,92 @@ func TestTo(t *testing.T) { }) } } + +func TestType(t *testing.T) { + tests := []struct { + Input cty.Value + Want string + }{ + // Primititves + { + cty.StringVal("a"), + "string", + }, + { + cty.NumberIntVal(42), + "number", + }, + { + cty.BoolVal(true), + "bool", + }, + // Collections + { + cty.EmptyObjectVal, + `object({})`, + }, + { + cty.EmptyTupleVal, + `tuple([])`, + }, + { + cty.ListValEmpty(cty.String), + `list(string)`, + }, + { + cty.MapValEmpty(cty.String), + `map(string)`, + }, + { + cty.SetValEmpty(cty.String), + `set(string)`, + }, + { + cty.ListVal([]cty.Value{cty.StringVal("a")}), + `list(string)`, + }, + { + cty.ListVal([]cty.Value{cty.ListVal([]cty.Value{cty.NumberIntVal(42)})}), + `list(list(number))`, + }, + { + cty.ListVal([]cty.Value{cty.MapValEmpty(cty.String)}), + `list(map(string))`, + }, + { + cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar"), + })}), + "list(\n object({\n foo: string,\n }),\n)", + }, + // Unknowns and Nulls + { + cty.UnknownVal(cty.String), + "string", + }, + { + cty.NullVal(cty.Object(map[string]cty.Type{ + "foo": cty.String, + })), + "object({\n foo: string,\n})", + }, + { // irrelevant marks do nothing + cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("bar").Mark("ignore me"), + })}), + "list(\n object({\n foo: string,\n }),\n)", + }, + } + for _, test := range tests { + got, err := Type([]cty.Value{test.Input}) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + // The value is marked to help with formatting + got, _ = got.Unmark() + + if got.AsString() != test.Want { + t.Errorf("wrong result:\n%s", cmp.Diff(got.AsString(), test.Want)) + } + } +} diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/defaults.go b/vendor/github.com/hashicorp/terraform/lang/funcs/defaults.go new file mode 100644 index 00000000..c1b854be --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/lang/funcs/defaults.go @@ -0,0 +1,288 @@ +package funcs + +import ( + "fmt" + + "github.com/hashicorp/terraform/tfdiags" + "github.com/zclconf/go-cty/cty" + "github.com/zclconf/go-cty/cty/convert" + "github.com/zclconf/go-cty/cty/function" +) + +// DefaultsFunc is a helper function for substituting default values in +// place of null values in a given data structure. +// +// See the documentation for function Defaults for more information. +var DefaultsFunc = function.New(&function.Spec{ + Params: []function.Parameter{ + { + Name: "input", + Type: cty.DynamicPseudoType, + AllowNull: true, + AllowMarked: true, + }, + { + Name: "defaults", + Type: cty.DynamicPseudoType, + AllowMarked: true, + }, + }, + Type: func(args []cty.Value) (cty.Type, error) { + // The result type is guaranteed to be the same as the input type, + // since all we're doing is replacing null values with non-null + // values of the same type. + retType := args[0].Type() + defaultsType := args[1].Type() + + // This function is aimed at filling in object types or collections + // of object types where some of the attributes might be null, so + // it doesn't make sense to use a primitive type directly with it. + // (The "coalesce" function may be appropriate for such cases.) + if retType.IsPrimitiveType() { + // This error message is a bit of a fib because we can actually + // apply defaults to tuples too, but we expect that to be so + // unusual as to not be worth mentioning here, because mentioning + // it would require using some less-well-known Terraform language + // terminology in the message (tuple types, structural types). + return cty.DynamicPseudoType, function.NewArgErrorf(1, "only object types and collections of object types can have defaults applied") + } + + defaultsPath := make(cty.Path, 0, 4) // some capacity so that most structures won't reallocate + if err := defaultsAssertSuitableFallback(retType, defaultsType, defaultsPath); err != nil { + errMsg := tfdiags.FormatError(err) // add attribute path prefix + return cty.DynamicPseudoType, function.NewArgErrorf(1, "%s", errMsg) + } + + return retType, nil + }, + Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { + if args[0].Type().HasDynamicTypes() { + // If the types our input object aren't known yet for some reason + // then we'll defer all of our work here, because our + // interpretation of the defaults depends on the types in + // the input. + return cty.UnknownVal(retType), nil + } + + v := defaultsApply(args[0], args[1]) + return v, nil + }, +}) + +func defaultsApply(input, fallback cty.Value) cty.Value { + wantTy := input.Type() + + umInput, inputMarks := input.Unmark() + umFb, fallbackMarks := fallback.Unmark() + + // If neither are known, we very conservatively return an unknown value + // with the union of marks on both input and default. + if !(umInput.IsKnown() && umFb.IsKnown()) { + return cty.UnknownVal(wantTy).WithMarks(inputMarks).WithMarks(fallbackMarks) + } + + // For the rest of this function we're assuming that the given defaults + // will always be valid, because we expect to have caught any problems + // during the type checking phase. Any inconsistencies that reach here are + // therefore considered to be implementation bugs, and so will panic. + + // Our strategy depends on the kind of type we're working with. + switch { + case wantTy.IsPrimitiveType(): + // For leaf primitive values the rule is relatively simple: use the + // input if it's non-null, or fallback if input is null. + if !umInput.IsNull() { + return input + } + v, err := convert.Convert(umFb, wantTy) + if err != nil { + // Should not happen because we checked in defaultsAssertSuitableFallback + panic(err.Error()) + } + return v.WithMarks(fallbackMarks) + + case wantTy.IsObjectType(): + // For structural types, a null input value must be passed through. We + // do not apply default values for missing optional structural values, + // only their contents. + // + // We also pass through the input if the fallback value is null. This + // can happen if the given defaults do not include a value for this + // attribute. + if umInput.IsNull() || umFb.IsNull() { + return input + } + atys := wantTy.AttributeTypes() + ret := map[string]cty.Value{} + for attr, aty := range atys { + inputSub := umInput.GetAttr(attr) + fallbackSub := cty.NullVal(aty) + if umFb.Type().HasAttribute(attr) { + fallbackSub = umFb.GetAttr(attr) + } + ret[attr] = defaultsApply(inputSub.WithMarks(inputMarks), fallbackSub.WithMarks(fallbackMarks)) + } + return cty.ObjectVal(ret) + + case wantTy.IsTupleType(): + // For structural types, a null input value must be passed through. We + // do not apply default values for missing optional structural values, + // only their contents. + // + // We also pass through the input if the fallback value is null. This + // can happen if the given defaults do not include a value for this + // attribute. + if umInput.IsNull() || umFb.IsNull() { + return input + } + + l := wantTy.Length() + ret := make([]cty.Value, l) + for i := 0; i < l; i++ { + inputSub := umInput.Index(cty.NumberIntVal(int64(i))) + fallbackSub := umFb.Index(cty.NumberIntVal(int64(i))) + ret[i] = defaultsApply(inputSub.WithMarks(inputMarks), fallbackSub.WithMarks(fallbackMarks)) + } + return cty.TupleVal(ret) + + case wantTy.IsCollectionType(): + // For collection types we apply a single fallback value to each + // element of the input collection, because in the situations this + // function is intended for we assume that the number of elements + // is the caller's decision, and so we'll just apply the same defaults + // to all of the elements. + ety := wantTy.ElementType() + switch { + case wantTy.IsMapType(): + newVals := map[string]cty.Value{} + + if !umInput.IsNull() { + for it := umInput.ElementIterator(); it.Next(); { + k, v := it.Element() + newVals[k.AsString()] = defaultsApply(v.WithMarks(inputMarks), fallback.WithMarks(fallbackMarks)) + } + } + + if len(newVals) == 0 { + return cty.MapValEmpty(ety) + } + return cty.MapVal(newVals) + case wantTy.IsListType(), wantTy.IsSetType(): + var newVals []cty.Value + + if !umInput.IsNull() { + for it := umInput.ElementIterator(); it.Next(); { + _, v := it.Element() + newV := defaultsApply(v.WithMarks(inputMarks), fallback.WithMarks(fallbackMarks)) + newVals = append(newVals, newV) + } + } + + if len(newVals) == 0 { + if wantTy.IsSetType() { + return cty.SetValEmpty(ety) + } + return cty.ListValEmpty(ety) + } + if wantTy.IsSetType() { + return cty.SetVal(newVals) + } + return cty.ListVal(newVals) + default: + // There are no other collection types, so this should not happen + panic(fmt.Sprintf("invalid collection type %#v", wantTy)) + } + default: + // We should've caught anything else in defaultsAssertSuitableFallback, + // so this should not happen. + panic(fmt.Sprintf("invalid target type %#v", wantTy)) + } +} + +func defaultsAssertSuitableFallback(wantTy, fallbackTy cty.Type, fallbackPath cty.Path) error { + // If the type we want is a collection type then we need to keep peeling + // away collection type wrappers until we find the non-collection-type + // that's underneath, which is what the fallback will actually be applied + // to. + inCollection := false + for wantTy.IsCollectionType() { + wantTy = wantTy.ElementType() + inCollection = true + } + + switch { + case wantTy.IsPrimitiveType(): + // The fallback is valid if it's equal to or convertible to what we want. + if fallbackTy.Equals(wantTy) { + return nil + } + conversion := convert.GetConversion(fallbackTy, wantTy) + if conversion == nil { + msg := convert.MismatchMessage(fallbackTy, wantTy) + return fallbackPath.NewErrorf("invalid default value for %s: %s", wantTy.FriendlyName(), msg) + } + return nil + case wantTy.IsObjectType(): + if !fallbackTy.IsObjectType() { + if inCollection { + return fallbackPath.NewErrorf("the default value for a collection of an object type must itself be an object type, not %s", fallbackTy.FriendlyName()) + } + return fallbackPath.NewErrorf("the default value for an object type must itself be an object type, not %s", fallbackTy.FriendlyName()) + } + for attr, wantAty := range wantTy.AttributeTypes() { + if !fallbackTy.HasAttribute(attr) { + continue // it's always okay to not have a default value + } + fallbackSubpath := fallbackPath.GetAttr(attr) + fallbackSubTy := fallbackTy.AttributeType(attr) + err := defaultsAssertSuitableFallback(wantAty, fallbackSubTy, fallbackSubpath) + if err != nil { + return err + } + } + for attr := range fallbackTy.AttributeTypes() { + if !wantTy.HasAttribute(attr) { + fallbackSubpath := fallbackPath.GetAttr(attr) + return fallbackSubpath.NewErrorf("target type does not expect an attribute named %q", attr) + } + } + return nil + case wantTy.IsTupleType(): + if !fallbackTy.IsTupleType() { + if inCollection { + return fallbackPath.NewErrorf("the default value for a collection of a tuple type must itself be a tuple type, not %s", fallbackTy.FriendlyName()) + } + return fallbackPath.NewErrorf("the default value for a tuple type must itself be a tuple type, not %s", fallbackTy.FriendlyName()) + } + wantEtys := wantTy.TupleElementTypes() + fallbackEtys := fallbackTy.TupleElementTypes() + if got, want := len(wantEtys), len(fallbackEtys); got != want { + return fallbackPath.NewErrorf("the default value for a tuple type of length %d must also have length %d, not %d", want, want, got) + } + for i := 0; i < len(wantEtys); i++ { + fallbackSubpath := fallbackPath.IndexInt(i) + wantSubTy := wantEtys[i] + fallbackSubTy := fallbackEtys[i] + err := defaultsAssertSuitableFallback(wantSubTy, fallbackSubTy, fallbackSubpath) + if err != nil { + return err + } + } + return nil + default: + // No other types are supported right now. + return fallbackPath.NewErrorf("cannot apply defaults to %s", wantTy.FriendlyName()) + } +} + +// Defaults is a helper function for substituting default values in +// place of null values in a given data structure. +// +// This is primarily intended for use with a module input variable that +// has an object type constraint (or a collection thereof) that has optional +// attributes, so that the receiver of a value that omits those attributes +// can insert non-null default values in place of the null values caused by +// omitting the attributes. +func Defaults(input, defaults cty.Value) (cty.Value, error) { + return DefaultsFunc.Call([]cty.Value{input, defaults}) +} diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/defaults_test.go b/vendor/github.com/hashicorp/terraform/lang/funcs/defaults_test.go new file mode 100644 index 00000000..e4016326 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/lang/funcs/defaults_test.go @@ -0,0 +1,648 @@ +package funcs + +import ( + "fmt" + "testing" + + "github.com/zclconf/go-cty/cty" +) + +func TestDefaults(t *testing.T) { + tests := []struct { + Input, Defaults cty.Value + Want cty.Value + WantErr string + }{ + { // When *either* input or default are unknown, an unknown is returned. + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.UnknownVal(cty.String), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.UnknownVal(cty.String), + }), + }, + { + // When *either* input or default are unknown, an unknown is + // returned with marks from both input and defaults. + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.UnknownVal(cty.String), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello").Mark("marked"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.UnknownVal(cty.String).Mark("marked"), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hey"), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hey"), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{}), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{}), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + }), + WantErr: `.a: target type does not expect an attribute named "a"`, + }, + + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.NullVal(cty.String), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.StringVal("hello"), + }), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.NullVal(cty.String), + cty.StringVal("hey"), + cty.NullVal(cty.String), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.StringVal("hello"), + cty.StringVal("hey"), + cty.StringVal("hello"), + }), + }), + }, + { + // Using defaults with single set elements is a pretty + // odd thing to do, but this behavior is just here because + // it generalizes from how we handle collections. It's + // tested only to ensure it doesn't change accidentally + // in future. + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.SetVal([]cty.Value{ + cty.NullVal(cty.String), + cty.StringVal("hey"), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.SetVal([]cty.Value{ + cty.StringVal("hey"), + cty.StringVal("hello"), + }), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "x": cty.NullVal(cty.String), + "y": cty.StringVal("hey"), + "z": cty.NullVal(cty.String), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "x": cty.StringVal("hello"), + "y": cty.StringVal("hey"), + "z": cty.StringVal("hello"), + }), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.NullVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + }), + }), + }, + { + Input: cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.NullVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + Want: cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("boop"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.NullVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("boop"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + }), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.NullVal(cty.String), + }), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.SetVal([]cty.Value{ + // After applying defaults, the one with a null value + // coalesced with the one with a non-null value, + // and so there's only one left. + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + }), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "boop": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + "beep": cty.ObjectVal(map[string]cty.Value{ + "b": cty.NullVal(cty.String), + }), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "boop": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + "beep": cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hello"), + }), + }), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.NullVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("hey"), + }), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + WantErr: `.a: the default value for a collection of an object type must itself be an object type, not string`, + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.NullVal(cty.String), + cty.StringVal("hey"), + cty.NullVal(cty.String), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + // The default value for a list must be a single value + // of the list's element type which provides defaults + // for each element separately, so the default for a + // list of string should be just a single string, not + // a list of string. + "a": cty.ListVal([]cty.Value{ + cty.StringVal("hello"), + }), + }), + WantErr: `.a: invalid default value for string: string required`, + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.TupleVal([]cty.Value{ + cty.NullVal(cty.String), + cty.StringVal("hey"), + cty.NullVal(cty.String), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + WantErr: `.a: the default value for a tuple type must itself be a tuple type, not string`, + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.TupleVal([]cty.Value{ + cty.NullVal(cty.String), + cty.StringVal("hey"), + cty.NullVal(cty.String), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.TupleVal([]cty.Value{ + cty.StringVal("hello 0"), + cty.StringVal("hello 1"), + cty.StringVal("hello 2"), + }), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.TupleVal([]cty.Value{ + cty.StringVal("hello 0"), + cty.StringVal("hey"), + cty.StringVal("hello 2"), + }), + }), + }, + { + // There's no reason to use this function for plain primitive + // types, because the "default" argument in a variable definition + // already has the equivalent behavior. This function is only + // to deal with the situation of a complex-typed variable where + // only parts of the data structure are optional. + Input: cty.NullVal(cty.String), + Defaults: cty.StringVal("hello"), + WantErr: `only object types and collections of object types can have defaults applied`, + }, + // When applying default values to structural types, null objects or + // tuples in the input should be passed through. + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.Object(map[string]cty.Type{ + "x": cty.String, + "y": cty.String, + })), + "b": cty.NullVal(cty.Tuple([]cty.Type{cty.String, cty.String})), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "x": cty.StringVal("hello"), + "y": cty.StringVal("there"), + }), + "b": cty.TupleVal([]cty.Value{ + cty.StringVal("how are"), + cty.StringVal("you?"), + }), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.Object(map[string]cty.Type{ + "x": cty.String, + "y": cty.String, + })), + "b": cty.NullVal(cty.Tuple([]cty.Type{cty.String, cty.String})), + }), + }, + // When applying default values to structural types, we permit null + // values in the defaults, and just pass through the input value. + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "p": cty.StringVal("xyz"), + "q": cty.StringVal("xyz"), + }), + }), + "b": cty.SetVal([]cty.Value{ + cty.TupleVal([]cty.Value{ + cty.NumberIntVal(0), + cty.NumberIntVal(2), + }), + cty.TupleVal([]cty.Value{ + cty.NumberIntVal(1), + cty.NumberIntVal(3), + }), + }), + "c": cty.NullVal(cty.String), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "c": cty.StringVal("tada"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "p": cty.StringVal("xyz"), + "q": cty.StringVal("xyz"), + }), + }), + "b": cty.SetVal([]cty.Value{ + cty.TupleVal([]cty.Value{ + cty.NumberIntVal(0), + cty.NumberIntVal(2), + }), + cty.TupleVal([]cty.Value{ + cty.NumberIntVal(1), + cty.NumberIntVal(3), + }), + }), + "c": cty.StringVal("tada"), + }), + }, + // When applying default values to collection types, null collections in the + // input should result in empty collections in the output. + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.List(cty.String)), + "b": cty.NullVal(cty.Map(cty.String)), + "c": cty.NullVal(cty.Set(cty.String)), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + "b": cty.StringVal("hi"), + "c": cty.StringVal("greetings"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListValEmpty(cty.String), + "b": cty.MapValEmpty(cty.String), + "c": cty.SetValEmpty(cty.String), + }), + }, + // When specifying fallbacks, we allow mismatched primitive attribute + // types so long as a safe conversion is possible. This means that we + // can accept number or boolean values for string attributes. + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + "b": cty.NullVal(cty.String), + "c": cty.NullVal(cty.String), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NumberIntVal(5), + "b": cty.True, + "c": cty.StringVal("greetings"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("5"), + "b": cty.StringVal("true"), + "c": cty.StringVal("greetings"), + }), + }, + // Fallbacks with mismatched primitive attribute types which do not + // have safe conversions must not pass the suitable fallback check, + // even if unsafe conversion would be possible. + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.Bool), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("5"), + }), + WantErr: ".a: invalid default value for bool: bool required", + }, + // marks: we should preserve marks from both input value and defaults as leafily as possible + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello").Mark("world"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello").Mark("world"), + }), + }, + { // "unused" marks don't carry over + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.String).Mark("a"), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello"), + }), + }, + { // Marks on tuples remain attached to individual elements + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.TupleVal([]cty.Value{ + cty.NullVal(cty.String), + cty.StringVal("hey").Mark("input"), + cty.NullVal(cty.String), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.TupleVal([]cty.Value{ + cty.StringVal("hello 0").Mark("fallback"), + cty.StringVal("hello 1"), + cty.StringVal("hello 2"), + }), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.TupleVal([]cty.Value{ + cty.StringVal("hello 0").Mark("fallback"), + cty.StringVal("hey").Mark("input"), + cty.StringVal("hello 2"), + }), + }), + }, + { // Marks from list elements + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.NullVal(cty.String), + cty.StringVal("hey").Mark("input"), + cty.NullVal(cty.String), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello 0").Mark("fallback"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.StringVal("hello 0").Mark("fallback"), + cty.StringVal("hey").Mark("input"), + cty.StringVal("hello 0").Mark("fallback"), + }), + }), + }, + { + // Sets don't allow individually-marked elements, so the marks + // end up aggregating on the set itself anyway in this case. + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.SetVal([]cty.Value{ + cty.NullVal(cty.String), + cty.NullVal(cty.String), + cty.StringVal("hey").Mark("input"), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello 0").Mark("fallback"), + }), + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.SetVal([]cty.Value{ + cty.StringVal("hello 0"), + cty.StringVal("hey"), + cty.StringVal("hello 0"), + }).WithMarks(cty.NewValueMarks("fallback", "input")), + }), + }, + { + Input: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.NullVal(cty.String), + }), + }), + Defaults: cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("hello").Mark("beep"), + }).Mark("boop"), + // This is the least-intuitive case. The mark "boop" is attached to + // the default object, not it's elements, but both marks end up + // aggregated on the list element. + Want: cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.StringVal("hello").WithMarks(cty.NewValueMarks("beep", "boop")), + }), + }), + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("defaults(%#v, %#v)", test.Input, test.Defaults), func(t *testing.T) { + got, gotErr := Defaults(test.Input, test.Defaults) + + if test.WantErr != "" { + if gotErr == nil { + t.Fatalf("unexpected success\nwant error: %s", test.WantErr) + } + if got, want := gotErr.Error(), test.WantErr; got != want { + t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) + } + return + } else if gotErr != nil { + t.Fatalf("unexpected error\ngot: %s", gotErr.Error()) + } + + if !test.Want.RawEquals(got) { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/sensitive.go b/vendor/github.com/hashicorp/terraform/lang/funcs/sensitive.go new file mode 100644 index 00000000..2fc505e7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/lang/funcs/sensitive.go @@ -0,0 +1,66 @@ +package funcs + +import ( + "github.com/zclconf/go-cty/cty" + "github.com/zclconf/go-cty/cty/function" +) + +// SensitiveFunc returns a value identical to its argument except that +// Terraform will consider it to be sensitive. +var SensitiveFunc = function.New(&function.Spec{ + Params: []function.Parameter{ + { + Name: "value", + Type: cty.DynamicPseudoType, + AllowUnknown: true, + AllowNull: true, + AllowMarked: true, + AllowDynamicType: true, + }, + }, + Type: func(args []cty.Value) (cty.Type, error) { + // This function only affects the value's marks, so the result + // type is always the same as the argument type. + return args[0].Type(), nil + }, + Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { + val, _ := args[0].Unmark() + return val.Mark("sensitive"), nil + }, +}) + +// NonsensitiveFunc takes a sensitive value and returns the same value without +// the sensitive marking, effectively exposing the value. +var NonsensitiveFunc = function.New(&function.Spec{ + Params: []function.Parameter{ + { + Name: "value", + Type: cty.DynamicPseudoType, + AllowUnknown: true, + AllowNull: true, + AllowMarked: true, + AllowDynamicType: true, + }, + }, + Type: func(args []cty.Value) (cty.Type, error) { + // This function only affects the value's marks, so the result + // type is always the same as the argument type. + return args[0].Type(), nil + }, + Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { + if args[0].IsKnown() && !args[0].HasMark("sensitive") { + return cty.DynamicVal, function.NewArgErrorf(0, "the given value is not sensitive, so this call is redundant") + } + v, marks := args[0].Unmark() + delete(marks, "sensitive") // remove the sensitive marking + return v.WithMarks(marks), nil + }, +}) + +func Sensitive(v cty.Value) (cty.Value, error) { + return SensitiveFunc.Call([]cty.Value{v}) +} + +func Nonsensitive(v cty.Value) (cty.Value, error) { + return NonsensitiveFunc.Call([]cty.Value{v}) +} diff --git a/vendor/github.com/hashicorp/terraform/lang/funcs/sensitive_test.go b/vendor/github.com/hashicorp/terraform/lang/funcs/sensitive_test.go new file mode 100644 index 00000000..591f53cf --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/lang/funcs/sensitive_test.go @@ -0,0 +1,178 @@ +package funcs + +import ( + "fmt" + "testing" + + "github.com/zclconf/go-cty/cty" +) + +func TestSensitive(t *testing.T) { + tests := []struct { + Input cty.Value + WantErr string + }{ + { + cty.NumberIntVal(1), + ``, + }, + { + // Unknown values stay unknown while becoming sensitive + cty.UnknownVal(cty.String), + ``, + }, + { + // Null values stay unknown while becoming sensitive + cty.NullVal(cty.String), + ``, + }, + { + // DynamicVal can be marked as sensitive + cty.DynamicVal, + ``, + }, + { + // The marking is shallow only + cty.ListVal([]cty.Value{cty.NumberIntVal(1)}), + ``, + }, + { + // A value already marked is allowed and stays marked + cty.NumberIntVal(1).Mark("sensitive"), + ``, + }, + { + // A value with some non-standard mark gets "fixed" to be marked + // with the standard "sensitive" mark. (This situation occurring + // would imply an inconsistency/bug elsewhere, so we're just + // being robust about it here.) + cty.NumberIntVal(1).Mark("bloop"), + ``, + }, + { + // A value deep already marked is allowed and stays marked, + // _and_ we'll also mark the outer collection as sensitive. + cty.ListVal([]cty.Value{cty.NumberIntVal(1).Mark("sensitive")}), + ``, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("sensitive(%#v)", test.Input), func(t *testing.T) { + got, err := Sensitive(test.Input) + + if test.WantErr != "" { + if err == nil { + t.Fatal("succeeded; want error") + } + if got, want := err.Error(), test.WantErr; got != want { + t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) + } + return + } else if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !got.HasMark("sensitive") { + t.Errorf("result is not marked sensitive") + } + + gotRaw, gotMarks := got.Unmark() + if len(gotMarks) != 1 { + // We're only expecting to have the "sensitive" mark we checked + // above. Any others are an error, even if they happen to + // appear alongside "sensitive". (We might change this rule + // if someday we decide to use marks for some additional + // unrelated thing in Terraform, but currently we assume that + // _all_ marks imply sensitive, and so returning any other + // marks would be confusing.) + t.Errorf("extraneous marks %#v", gotMarks) + } + + // Disregarding shallow marks, the result should have the same + // effective value as the input. + wantRaw, _ := test.Input.Unmark() + if !gotRaw.RawEquals(wantRaw) { + t.Errorf("wrong unmarked result\ngot: %#v\nwant: %#v", got, wantRaw) + } + }) + } +} + +func TestNonsensitive(t *testing.T) { + tests := []struct { + Input cty.Value + WantErr string + }{ + { + cty.NumberIntVal(1).Mark("sensitive"), + ``, + }, + { + cty.DynamicVal.Mark("sensitive"), + ``, + }, + { + cty.UnknownVal(cty.String).Mark("sensitive"), + ``, + }, + { + cty.NullVal(cty.EmptyObject).Mark("sensitive"), + ``, + }, + { + // The inner sensitive remains afterwards + cty.ListVal([]cty.Value{cty.NumberIntVal(1).Mark("sensitive")}).Mark("sensitive"), + ``, + }, + + // Passing a value that is already non-sensitive is an error, + // because this function should always be used with specific + // intention, not just as a "make everything visible" hammer. + { + cty.NumberIntVal(1), + `the given value is not sensitive, so this call is redundant`, + }, + { + cty.NullVal(cty.String), + `the given value is not sensitive, so this call is redundant`, + }, + + // Unknown values may become sensitive once they are known, so we + // permit them to be marked nonsensitive. + { + cty.DynamicVal, + ``, + }, + { + cty.UnknownVal(cty.String), + ``, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("nonsensitive(%#v)", test.Input), func(t *testing.T) { + got, err := Nonsensitive(test.Input) + + if test.WantErr != "" { + if err == nil { + t.Fatal("succeeded; want error") + } + if got, want := err.Error(), test.WantErr; got != want { + t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) + } + return + } else if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if got.HasMark("sensitive") { + t.Errorf("result is still marked sensitive") + } + wantRaw, _ := test.Input.Unmark() + if !got.RawEquals(wantRaw) { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Input) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/lang/functions.go b/vendor/github.com/hashicorp/terraform/lang/functions.go index c5ff6ac6..21be9a7f 100644 --- a/vendor/github.com/hashicorp/terraform/lang/functions.go +++ b/vendor/github.com/hashicorp/terraform/lang/functions.go @@ -9,6 +9,7 @@ import ( "github.com/zclconf/go-cty/cty/function" "github.com/zclconf/go-cty/cty/function/stdlib" + "github.com/hashicorp/terraform/experiments" "github.com/hashicorp/terraform/lang/funcs" ) @@ -34,6 +35,7 @@ func (s *Scope) Functions() map[string]function.Function { "abs": stdlib.AbsoluteFunc, "abspath": funcs.AbsPathFunc, "alltrue": funcs.AllTrueFunc, + "anytrue": funcs.AnyTrueFunc, "basename": funcs.BasenameFunc, "base64decode": funcs.Base64DecodeFunc, "base64encode": funcs.Base64EncodeFunc, @@ -54,6 +56,7 @@ func (s *Scope) Functions() map[string]function.Function { "concat": stdlib.ConcatFunc, "contains": stdlib.ContainsFunc, "csvdecode": stdlib.CSVDecodeFunc, + "defaults": s.experimentalFunction(experiments.ModuleVariableOptionalAttrs, funcs.DefaultsFunc), "dirname": funcs.DirnameFunc, "distinct": stdlib.DistinctFunc, "element": stdlib.ElementFunc, @@ -90,6 +93,7 @@ func (s *Scope) Functions() map[string]function.Function { "md5": funcs.Md5Func, "merge": stdlib.MergeFunc, "min": stdlib.MinFunc, + "one": funcs.OneFunc, "parseint": stdlib.ParseIntFunc, "pathexpand": funcs.PathExpandFunc, "pow": stdlib.PowFunc, @@ -99,6 +103,8 @@ func (s *Scope) Functions() map[string]function.Function { "replace": funcs.ReplaceFunc, "reverse": stdlib.ReverseListFunc, "rsadecrypt": funcs.RsaDecryptFunc, + "sensitive": funcs.SensitiveFunc, + "nonsensitive": funcs.NonsensitiveFunc, "setintersection": stdlib.SetIntersectionFunc, "setproduct": stdlib.SetProductFunc, "setsubtract": stdlib.SetSubtractFunc, @@ -146,6 +152,11 @@ func (s *Scope) Functions() map[string]function.Function { return s.funcs }) + if s.ConsoleMode { + // The type function is only available in terraform console. + s.funcs["type"] = funcs.TypeFunc + } + if s.PureOnly { // Force our few impure functions to return unknown so that we // can defer evaluating them until a later pass. @@ -159,11 +170,31 @@ func (s *Scope) Functions() map[string]function.Function { return s.funcs } -var unimplFunc = function.New(&function.Spec{ - Type: func([]cty.Value) (cty.Type, error) { - return cty.DynamicPseudoType, fmt.Errorf("function not yet implemented") - }, - Impl: func([]cty.Value, cty.Type) (cty.Value, error) { - return cty.DynamicVal, fmt.Errorf("function not yet implemented") - }, -}) +// experimentalFunction checks whether the given experiment is enabled for +// the recieving scope. If so, it will return the given function verbatim. +// If not, it will return a placeholder function that just returns an +// error explaining that the function requires the experiment to be enabled. +func (s *Scope) experimentalFunction(experiment experiments.Experiment, fn function.Function) function.Function { + if s.activeExperiments.Has(experiment) { + return fn + } + + err := fmt.Errorf( + "this function is experimental and available only when the experiment keyword %s is enabled for the current module", + experiment.Keyword(), + ) + + return function.New(&function.Spec{ + Params: fn.Params(), + VarParam: fn.VarParam(), + Type: func(args []cty.Value) (cty.Type, error) { + return cty.DynamicPseudoType, err + }, + Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { + // It would be weird to get here because the Type function always + // fails, but we'll return an error here too anyway just to be + // robust. + return cty.DynamicVal, err + }, + }) +} diff --git a/vendor/github.com/hashicorp/terraform/lang/functions_test.go b/vendor/github.com/hashicorp/terraform/lang/functions_test.go index ce62a7c4..c33bc367 100644 --- a/vendor/github.com/hashicorp/terraform/lang/functions_test.go +++ b/vendor/github.com/hashicorp/terraform/lang/functions_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" + "github.com/hashicorp/terraform/experiments" homedir "github.com/mitchellh/go-homedir" "github.com/zclconf/go-cty/cty" ) @@ -62,18 +63,25 @@ func TestFunctions(t *testing.T) { if err != nil { panic(err) } - return cwd + return filepath.ToSlash(cwd) })()), }, }, "alltrue": { { - `alltrue([true])`, + `alltrue(["true", true])`, cty.True, }, }, + "anytrue": { + { + `anytrue([])`, + cty.False, + }, + }, + "base64decode": { { `base64decode("YWJjMTIzIT8kKiYoKSctPUB+")`, @@ -217,7 +225,7 @@ func TestFunctions(t *testing.T) { "coalescelist": { { - `coalescelist(list("a", "b"), list("c", "d"))`, + `coalescelist(tolist(["a", "b"]), tolist(["c", "d"]))`, cty.ListVal([]cty.Value{ cty.StringVal("a"), cty.StringVal("b"), @@ -282,6 +290,18 @@ func TestFunctions(t *testing.T) { }, }, + "defaults": { + // This function is pretty specialized and so this is mainly + // just a test that it is defined at all. See the function's + // own unit tests for more interesting test cases. + { + `defaults({a: 4}, {a: 5})`, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NumberIntVal(4), + }), + }, + }, + "dirname": { { `dirname("testdata/hello.txt")`, @@ -505,12 +525,8 @@ func TestFunctions(t *testing.T) { }, "list": { - { - `list("hello")`, - cty.ListVal([]cty.Value{ - cty.StringVal("hello"), - }), - }, + // There are intentionally no test cases for "list" because + // it is a stub that always returns an error. }, "log": { @@ -535,12 +551,8 @@ func TestFunctions(t *testing.T) { }, "map": { - { - `map("hello", "world")`, - cty.MapVal(map[string]cty.Value{ - "hello": cty.StringVal("world"), - }), - }, + // There are intentionally no test cases for "map" because + // it is a stub that always returns an error. }, "matchkeys": { @@ -590,6 +602,29 @@ func TestFunctions(t *testing.T) { }, }, + "nonsensitive": { + { + // Due to how this test is set up we have no way to get + // a sensitive value other than to generate one with + // another function, so this is a bit odd but does still + // meet the goal of verifying that the "nonsensitive" + // function is correctly registered. + `nonsensitive(sensitive(1))`, + cty.NumberIntVal(1), + }, + }, + + "one": { + { + `one([])`, + cty.NullVal(cty.DynamicPseudoType), + }, + { + `one([true])`, + cty.True, + }, + }, + "parseint": { { `parseint("100", 10)`, @@ -677,6 +712,13 @@ func TestFunctions(t *testing.T) { }, }, + "sensitive": { + { + `sensitive(1)`, + cty.NumberIntVal(1).Mark("sensitive"), + }, + }, + "setintersection": { { `setintersection(["a", "b"], ["b", "c"], ["b", "d"])`, @@ -752,7 +794,7 @@ func TestFunctions(t *testing.T) { "slice": { { // force a list type here for testing - `slice(list("a", "b", "c", "d"), 1, 3)`, + `slice(tolist(["a", "b", "c", "d"]), 1, 3)`, cty.ListVal([]cty.Value{ cty.StringVal("b"), cty.StringVal("c"), }), @@ -1040,32 +1082,89 @@ func TestFunctions(t *testing.T) { }, } - data := &dataForTests{} // no variables available; we only need literals here - scope := &Scope{ - Data: data, - BaseDir: "./testdata/functions-test", // for the functions that read from the filesystem - } + experimentalFuncs := map[string]experiments.Experiment{} + experimentalFuncs["defaults"] = experiments.ModuleVariableOptionalAttrs - // Check that there is at least one test case for each function, omitting - // those functions that do not return consistent values - allFunctions := scope.Functions() + t.Run("all functions are tested", func(t *testing.T) { + data := &dataForTests{} // no variables available; we only need literals here + scope := &Scope{ + Data: data, + BaseDir: "./testdata/functions-test", // for the functions that read from the filesystem + } - // TODO: we can test the impure functions partially by configuring the scope - // with PureOnly: true and then verify that they return unknown values of a - // suitable type. - for _, impureFunc := range impureFunctions { - delete(allFunctions, impureFunc) - } - for f, _ := range scope.Functions() { - if _, ok := tests[f]; !ok { - t.Errorf("Missing test for function %s\n", f) + // Check that there is at least one test case for each function, omitting + // those functions that do not return consistent values + allFunctions := scope.Functions() + + // TODO: we can test the impure functions partially by configuring the scope + // with PureOnly: true and then verify that they return unknown values of a + // suitable type. + for _, impureFunc := range impureFunctions { + delete(allFunctions, impureFunc) } - } + for f := range scope.Functions() { + if _, ok := tests[f]; !ok { + t.Errorf("Missing test for function %s\n", f) + } + } + }) for funcName, funcTests := range tests { t.Run(funcName, func(t *testing.T) { + + // prepareScope starts as a no-op, but if a function is marked as + // experimental in our experimentalFuncs table above then we'll + // reassign this to be a function that activates the appropriate + // experiment. + prepareScope := func(t *testing.T, scope *Scope) {} + + if experiment, isExperimental := experimentalFuncs[funcName]; isExperimental { + // First, we'll run all of the tests without the experiment + // enabled to see that they do actually fail in that case. + for _, test := range funcTests { + testName := fmt.Sprintf("experimental(%s)", test.src) + t.Run(testName, func(t *testing.T) { + data := &dataForTests{} // no variables available; we only need literals here + scope := &Scope{ + Data: data, + BaseDir: "./testdata/functions-test", // for the functions that read from the filesystem + } + + expr, parseDiags := hclsyntax.ParseExpression([]byte(test.src), "test.hcl", hcl.Pos{Line: 1, Column: 1}) + if parseDiags.HasErrors() { + for _, diag := range parseDiags { + t.Error(diag.Error()) + } + return + } + + _, diags := scope.EvalExpr(expr, cty.DynamicPseudoType) + if !diags.HasErrors() { + t.Errorf("experimental function %q succeeded without its experiment %s enabled\nexpr: %s", funcName, experiment.Keyword(), test.src) + } + }) + } + + // Now make the experiment active in the scope so that the + // function will actually work when we test it below. + prepareScope = func(t *testing.T, scope *Scope) { + t.Helper() + t.Logf("activating experiment %s to test %q", experiment.Keyword(), funcName) + experimentsSet := experiments.NewSet() + experimentsSet.Add(experiment) + scope.SetActiveExperiments(experimentsSet) + } + } + for _, test := range funcTests { t.Run(test.src, func(t *testing.T) { + data := &dataForTests{} // no variables available; we only need literals here + scope := &Scope{ + Data: data, + BaseDir: "./testdata/functions-test", // for the functions that read from the filesystem + } + prepareScope(t, scope) + expr, parseDiags := hclsyntax.ParseExpression([]byte(test.src), "test.hcl", hcl.Pos{Line: 1, Column: 1}) if parseDiags.HasErrors() { for _, diag := range parseDiags { diff --git a/vendor/github.com/hashicorp/terraform/lang/scope.go b/vendor/github.com/hashicorp/terraform/lang/scope.go index 98fca6ba..3a34e9ca 100644 --- a/vendor/github.com/hashicorp/terraform/lang/scope.go +++ b/vendor/github.com/hashicorp/terraform/lang/scope.go @@ -6,6 +6,7 @@ import ( "github.com/zclconf/go-cty/cty/function" "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/experiments" ) // Scope is the main type in this package, allowing dynamic evaluation of @@ -31,4 +32,20 @@ type Scope struct { funcs map[string]function.Function funcsLock sync.Mutex + + // activeExperiments is an optional set of experiments that should be + // considered as active in the module that this scope will be used for. + // Callers can populate it by calling the SetActiveExperiments method. + activeExperiments experiments.Set + + // ConsoleMode can be set to true to request any console-only functions are + // included in this scope. + ConsoleMode bool +} + +// SetActiveExperiments allows a caller to declare that a set of experiments +// is active for the module that the receiving Scope belongs to, which might +// then cause the scope to activate some additional experimental behaviors. +func (s *Scope) SetActiveExperiments(active experiments.Set) { + s.activeExperiments = active } diff --git a/vendor/github.com/hashicorp/terraform/main.go b/vendor/github.com/hashicorp/terraform/main.go index e87a9023..68726a0f 100644 --- a/vendor/github.com/hashicorp/terraform/main.go +++ b/vendor/github.com/hashicorp/terraform/main.go @@ -3,7 +3,6 @@ package main import ( "encoding/json" "fmt" - "io" "io/ioutil" "log" "net" @@ -11,7 +10,6 @@ import ( "path/filepath" "runtime" "strings" - "sync" "github.com/hashicorp/go-plugin" "github.com/hashicorp/terraform-svchost/disco" @@ -19,13 +17,14 @@ import ( "github.com/hashicorp/terraform/command/cliconfig" "github.com/hashicorp/terraform/command/format" "github.com/hashicorp/terraform/httpclient" + "github.com/hashicorp/terraform/internal/didyoumean" + "github.com/hashicorp/terraform/internal/logging" + "github.com/hashicorp/terraform/internal/terminal" "github.com/hashicorp/terraform/version" - "github.com/mattn/go-colorable" "github.com/mattn/go-shellwords" "github.com/mitchellh/cli" "github.com/mitchellh/colorstring" "github.com/mitchellh/panicwrap" - "github.com/mitchellh/prefixedio" backendInit "github.com/hashicorp/terraform/backend/init" ) @@ -33,8 +32,28 @@ import ( const ( // EnvCLI is the environment variable name to set additional CLI args. EnvCLI = "TF_CLI_ARGS" + + // The parent process will create a file to collect crash logs + envTmpLogPath = "TF_TEMP_LOG_PATH" + + // Environment variable name used for smuggling true stderr terminal + // settings into a panicwrap child process. This is an implementation + // detail, subject to change in future, and should not ever be directly + // set by an end-user. + envTerminalPanicwrapWorkaround = "TF_PANICWRAP_STDERR" ) +// ui wraps the primary output cli.Ui, and redirects Warn calls to Output +// calls. This ensures that warnings are sent to stdout, and are properly +// serialized within the stdout stream. +type ui struct { + cli.Ui +} + +func (u *ui) Warn(msg string) { + u.Ui.Output(msg) +} + func main() { os.Exit(realMain()) } @@ -52,22 +71,35 @@ func realMain() int { // there is a panic. Otherwise, we delete it. logTempFile, err := ioutil.TempFile("", "terraform-log") if err != nil { - fmt.Fprintf(os.Stderr, "Couldn't setup logging tempfile: %s", err) + fmt.Fprintf(os.Stderr, "Couldn't set up logging tempfile: %s", err) return 1 } + // Now that we have the file, close it and leave it for the wrapped + // process to write to. + logTempFile.Close() defer os.Remove(logTempFile.Name()) - defer logTempFile.Close() - // Setup the prefixed readers that send data properly to - // stdout/stderr. - doneCh := make(chan struct{}) - outR, outW := io.Pipe() - go copyOutput(outR, doneCh) + // store the path in the environment for the wrapped executable + os.Setenv(envTmpLogPath, logTempFile.Name()) + + // We also need to do our terminal initialization before we fork, + // because the child process doesn't necessarily have access to + // the true stderr in order to initialize it. + streams, err := terminal.Init() + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to initialize terminal: %s", err) + return 1 + } + + // We need the child process to behave _as if_ connected to the real + // stderr, even though panicwrap is about to add a pipe in the way, + // so we'll smuggle the true stderr information in an environment + // varible. + streamState := streams.StateForAfterPanicWrap() + os.Setenv(envTerminalPanicwrapWorkaround, fmt.Sprintf("%t:%d", streamState.StderrIsTerminal, streamState.StderrWidth)) // Create the configuration for panicwrap and wrap our executable - wrapConfig.Handler = panicHandler(logTempFile) - wrapConfig.Writer = os.Stderr - wrapConfig.Stdout = outW + wrapConfig.Handler = logging.PanicHandler(logTempFile.Name()) wrapConfig.IgnoreSignals = ignoreSignals wrapConfig.ForwardSignals = forwardSignals exitStatus, err := panicwrap.Wrap(&wrapConfig) @@ -76,20 +108,7 @@ func realMain() int { return 1 } - // If >= 0, we're the parent, so just exit - if exitStatus >= 0 { - // Close the stdout writer so that our copy process can finish - outW.Close() - - // Wait for the output copying to finish - <-doneCh - - return exitStatus - } - - // We're the child, so just close the tempfile we made in order to - // save file handles since the tempfile is only used by the parent. - logTempFile.Close() + return exitStatus } // Call the real main @@ -97,27 +116,68 @@ func realMain() int { } func init() { - Ui = &cli.PrefixedUi{ - AskPrefix: OutputPrefix, - OutputPrefix: OutputPrefix, - InfoPrefix: OutputPrefix, - ErrorPrefix: ErrorPrefix, - Ui: &cli.BasicUi{ - Writer: os.Stdout, - Reader: os.Stdin, - }, - } + Ui = &ui{&cli.BasicUi{ + Writer: os.Stdout, + ErrorWriter: os.Stderr, + Reader: os.Stdin, + }} } func wrappedMain() int { var err error + tmpLogPath := os.Getenv(envTmpLogPath) + if tmpLogPath != "" { + f, err := os.OpenFile(tmpLogPath, os.O_RDWR|os.O_APPEND, 0666) + if err == nil { + defer f.Close() + + log.Printf("[DEBUG] Adding temp file log sink: %s", f.Name()) + logging.RegisterSink(f) + } else { + log.Printf("[ERROR] Could not open temp log file: %v", err) + } + } + log.Printf( - "[INFO] Terraform version: %s %s %s", - Version, VersionPrerelease, GitCommit) + "[INFO] Terraform version: %s %s", + Version, VersionPrerelease) log.Printf("[INFO] Go runtime version: %s", runtime.Version()) log.Printf("[INFO] CLI args: %#v", os.Args) + // This is the recieving end of our workaround to retain the metadata + // about the real stderr even though we're talking to it via the panicwrap + // pipe. See the call to StateForAfterPanicWrap above for the producer + // part of this. + var streamState *terminal.PrePanicwrapState + if raw := os.Getenv(envTerminalPanicwrapWorkaround); raw != "" { + streamState = &terminal.PrePanicwrapState{} + if _, err := fmt.Sscanf(raw, "%t:%d", &streamState.StderrIsTerminal, &streamState.StderrWidth); err != nil { + log.Printf("[WARN] %s is set but is incorrectly-formatted: %s", envTerminalPanicwrapWorkaround, err) + streamState = nil // leave it unset for a normal init, then + } + } + streams, err := terminal.ReinitInsidePanicwrap(streamState) + if err != nil { + Ui.Error(fmt.Sprintf("Failed to configure the terminal: %s", err)) + return 1 + } + if streams.Stdout.IsTerminal() { + log.Printf("[TRACE] Stdout is a terminal of width %d", streams.Stdout.Columns()) + } else { + log.Printf("[TRACE] Stdout is not a terminal") + } + if streams.Stderr.IsTerminal() { + log.Printf("[TRACE] Stderr is a terminal of width %d", streams.Stderr.Columns()) + } else { + log.Printf("[TRACE] Stderr is not a terminal") + } + if streams.Stdin.IsTerminal() { + log.Printf("[TRACE] Stdin is a terminal") + } else { + log.Printf("[TRACE] Stdin is not a terminal") + } + // NOTE: We're intentionally calling LoadConfig _before_ handling a possible // -chdir=... option on the command line, so that a possible relative // path in the TERRAFORM_CONFIG_FILE environment variable (though probably @@ -231,7 +291,7 @@ func wrappedMain() int { // in case they need to refer back to it for any special reason, though // they should primarily be working with the override working directory // that we've now switched to above. - initCommands(originalWd, config, services, providerSrc, providerDevOverrides, unmanagedProviders) + initCommands(originalWd, streams, config, services, providerSrc, providerDevOverrides, unmanagedProviders) } // Run checkpoint @@ -290,72 +350,54 @@ func wrappedMain() int { AutocompleteUninstall: "uninstall-autocomplete", } + // Before we continue we'll check whether the requested command is + // actually known. If not, we might be able to suggest an alternative + // if it seems like the user made a typo. + // (This bypasses the built-in help handling in cli.CLI for the situation + // where a command isn't found, because it's likely more helpful to + // mention what specifically went wrong, rather than just printing out + // a big block of usage information.) + + // Check if this is being run via shell auto-complete, which uses the + // binary name as the first argument and won't be listed as a subcommand. + autoComplete := os.Getenv("COMP_LINE") != "" + + if cmd := cliRunner.Subcommand(); cmd != "" && !autoComplete { + // Due to the design of cli.CLI, this special error message only works + // for typos of top-level commands. For a subcommand typo, like + // "terraform state posh", cmd would be "state" here and thus would + // be considered to exist, and it would print out its own usage message. + if _, exists := Commands[cmd]; !exists { + suggestions := make([]string, 0, len(Commands)) + for name := range Commands { + suggestions = append(suggestions, name) + } + suggestion := didyoumean.NameSuggestion(cmd, suggestions) + if suggestion != "" { + suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) + } + fmt.Fprintf(os.Stderr, "Terraform has no command named %q.%s\n\nTo see all of Terraform's top-level commands, run:\n terraform -help\n\n", cmd, suggestion) + return 1 + } + } + exitCode, err := cliRunner.Run() if err != nil { Ui.Error(fmt.Sprintf("Error executing CLI: %s", err.Error())) return 1 } - return exitCode -} - -// copyOutput uses output prefixes to determine whether data on stdout -// should go to stdout or stderr. This is due to panicwrap using stderr -// as the log and error channel. -func copyOutput(r io.Reader, doneCh chan<- struct{}) { - defer close(doneCh) - - pr, err := prefixedio.NewReader(r) - if err != nil { - panic(err) + // if we are exiting with a non-zero code, check if it was caused by any + // plugins crashing + if exitCode != 0 { + for _, panicLog := range logging.PluginPanics() { + // we don't write this to Error, or else panicwrap will think this + // process panicked + Ui.Info(panicLog) + } } - stderrR, err := pr.Prefix(ErrorPrefix) - if err != nil { - panic(err) - } - stdoutR, err := pr.Prefix(OutputPrefix) - if err != nil { - panic(err) - } - defaultR, err := pr.Prefix("") - if err != nil { - panic(err) - } - - var stdout io.Writer = os.Stdout - var stderr io.Writer = os.Stderr - - if runtime.GOOS == "windows" { - stdout = colorable.NewColorableStdout() - stderr = colorable.NewColorableStderr() - - // colorable is not concurrency-safe when stdout and stderr are the - // same console, so we need to add some synchronization to ensure that - // we can't be concurrently writing to both stderr and stdout at - // once, or else we get intermingled writes that create gibberish - // in the console. - wrapped := synchronizedWriters(stdout, stderr) - stdout = wrapped[0] - stderr = wrapped[1] - } - - var wg sync.WaitGroup - wg.Add(3) - go func() { - defer wg.Done() - io.Copy(stderr, stderrR) - }() - go func() { - defer wg.Done() - io.Copy(stdout, stdoutR) - }() - go func() { - defer wg.Done() - io.Copy(stdout, defaultR) - }() - - wg.Wait() + return exitCode } func mergeEnvArgs(envName string, cmd string, args []string) ([]string, error) { diff --git a/vendor/github.com/hashicorp/terraform/main_test.go b/vendor/github.com/hashicorp/terraform/main_test.go index 406195e8..102ea44b 100644 --- a/vendor/github.com/hashicorp/terraform/main_test.go +++ b/vendor/github.com/hashicorp/terraform/main_test.go @@ -10,14 +10,14 @@ import ( ) func TestMain_cliArgsFromEnv(t *testing.T) { - // Setup the state. This test really messes with the environment and + // Set up the state. This test really messes with the environment and // global state so we set things up to be restored. // Restore original CLI args oldArgs := os.Args defer func() { os.Args = oldArgs }() - // Setup test command and restore that + // Set up test command and restore that Commands = make(map[string]cli.CommandFactory) defer func() { Commands = nil @@ -122,7 +122,7 @@ func TestMain_cliArgsFromEnv(t *testing.T) { } } - // Setup the args + // Set up the args args := make([]string, len(tc.Args)+1) args[0] = oldArgs[0] // process name copy(args[1:], tc.Args) @@ -153,7 +153,7 @@ func TestMain_cliArgsFromEnvAdvanced(t *testing.T) { oldArgs := os.Args defer func() { os.Args = oldArgs }() - // Setup test command and restore that + // Set up test command and restore that Commands = make(map[string]cli.CommandFactory) defer func() { Commands = nil @@ -211,7 +211,7 @@ func TestMain_cliArgsFromEnvAdvanced(t *testing.T) { for i, tc := range cases { t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { - // Setup test command and restore that + // Set up test command and restore that testCommandName := tc.Command testCommand := &testCommandCLI{} defer func() { delete(Commands, testCommandName) }() @@ -229,7 +229,7 @@ func TestMain_cliArgsFromEnvAdvanced(t *testing.T) { } } - // Setup the args + // Set up the args args := make([]string, len(tc.Args)+1) args[0] = oldArgs[0] // process name copy(args[1:], tc.Args) @@ -253,6 +253,34 @@ func TestMain_cliArgsFromEnvAdvanced(t *testing.T) { } } +// verify that we output valid autocomplete results +func TestMain_autoComplete(t *testing.T) { + // Restore original CLI args + oldArgs := os.Args + defer func() { os.Args = oldArgs }() + + // Set up test command and restore that + Commands = make(map[string]cli.CommandFactory) + defer func() { + Commands = nil + }() + + // Set up test command and restore that + Commands["foo"] = func() (cli.Command, error) { + return &testCommandCLI{}, nil + } + + os.Setenv("COMP_LINE", "terraform versio") + defer os.Unsetenv("COMP_LINE") + + // Run it! + os.Args = []string{"terraform", "terraform", "versio"} + exit := wrappedMain() + if exit != 0 { + t.Fatalf("unexpected exit status %d; want 0", exit) + } +} + type testCommandCLI struct { Args []string } @@ -264,3 +292,20 @@ func (c *testCommandCLI) Run(args []string) int { func (c *testCommandCLI) Synopsis() string { return "" } func (c *testCommandCLI) Help() string { return "" } + +func TestWarnOutput(t *testing.T) { + mock := cli.NewMockUi() + wrapped := &ui{mock} + wrapped.Warn("WARNING") + + stderr := mock.ErrorWriter.String() + stdout := mock.OutputWriter.String() + + if stderr != "" { + t.Fatalf("unexpected stderr: %q", stderr) + } + + if stdout != "WARNING\n" { + t.Fatalf("unexpected stdout: %q\n", stdout) + } +} diff --git a/vendor/github.com/hashicorp/terraform/panic.go b/vendor/github.com/hashicorp/terraform/panic.go deleted file mode 100644 index 6690a93e..00000000 --- a/vendor/github.com/hashicorp/terraform/panic.go +++ /dev/null @@ -1,71 +0,0 @@ -package main - -import ( - "fmt" - "io" - "os" - "strings" - - "github.com/mitchellh/panicwrap" -) - -// This output is shown if a panic happens. -const panicOutput = ` - -!!!!!!!!!!!!!!!!!!!!!!!!!!! TERRAFORM CRASH !!!!!!!!!!!!!!!!!!!!!!!!!!!! - -Terraform crashed! This is always indicative of a bug within Terraform. -A crash log has been placed at "crash.log" relative to your current -working directory. It would be immensely helpful if you could please -report the crash with Terraform[1] so that we can fix this. - -When reporting bugs, please include your terraform version. That -information is available on the first line of crash.log. You can also -get it by running 'terraform --version' on the command line. - -SECURITY WARNING: the "crash.log" file that was created may contain -sensitive information that must be redacted before it is safe to share -on the issue tracker. - -[1]: https://github.com/hashicorp/terraform/issues - -!!!!!!!!!!!!!!!!!!!!!!!!!!! TERRAFORM CRASH !!!!!!!!!!!!!!!!!!!!!!!!!!!! -` - -// panicHandler is what is called by panicwrap when a panic is encountered -// within Terraform. It is guaranteed to run after the resulting process has -// exited so we can take the log file, add in the panic, and store it -// somewhere locally. -func panicHandler(logF *os.File) panicwrap.HandlerFunc { - return func(m string) { - // Right away just output this thing on stderr so that it gets - // shown in case anything below fails. - fmt.Fprintf(os.Stderr, fmt.Sprintf("%s\n", m)) - - // Create the crash log file where we'll write the logs - f, err := os.Create("crash.log") - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to create crash log file: %s", err) - return - } - defer f.Close() - - // Seek the log file back to the beginning - if _, err = logF.Seek(0, 0); err != nil { - fmt.Fprintf(os.Stderr, "Failed to seek log file for crash: %s", err) - return - } - - // Copy the contents to the crash file. This will include - // the panic that just happened. - if _, err = io.Copy(f, logF); err != nil { - fmt.Fprintf(os.Stderr, "Failed to write crash log: %s", err) - return - } - - // Tell the user a crash occurred in some helpful way that - // they'll hopefully notice. - fmt.Printf("\n\n") - fmt.Println(strings.TrimSpace(panicOutput)) - } -} diff --git a/vendor/github.com/hashicorp/terraform/plans/changes.go b/vendor/github.com/hashicorp/terraform/plans/changes.go index 41409948..2ababe77 100644 --- a/vendor/github.com/hashicorp/terraform/plans/changes.go +++ b/vendor/github.com/hashicorp/terraform/plans/changes.go @@ -39,7 +39,7 @@ func (c *Changes) Empty() bool { } for _, out := range c.Outputs { - if out.Action != NoOp { + if out.Addr.Module.IsRoot() && out.Action != NoOp { return false } } @@ -51,9 +51,8 @@ func (c *Changes) Empty() bool { // resource instance of the given address, if any. Returns nil if no change is // planned. func (c *Changes) ResourceInstance(addr addrs.AbsResourceInstance) *ResourceInstanceChangeSrc { - addrStr := addr.String() for _, rc := range c.Resources { - if rc.Addr.String() == addrStr && rc.DeposedKey == states.NotDeposed { + if rc.Addr.Equal(addr) && rc.DeposedKey == states.NotDeposed { return rc } } @@ -81,9 +80,8 @@ func (c *Changes) InstancesForConfigResource(addr addrs.ConfigResource) []*Resou // the resource instance of the given address, if any. Returns nil if no change // is planned. func (c *Changes) ResourceInstanceDeposed(addr addrs.AbsResourceInstance, key states.DeposedKey) *ResourceInstanceChangeSrc { - addrStr := addr.String() for _, rc := range c.Resources { - if rc.Addr.String() == addrStr && rc.DeposedKey == key { + if rc.Addr.Equal(addr) && rc.DeposedKey == key { return rc } } @@ -94,9 +92,8 @@ func (c *Changes) ResourceInstanceDeposed(addr addrs.AbsResourceInstance, key st // OutputValue returns the planned change for the output value with the // given address, if any. Returns nil if no change is planned. func (c *Changes) OutputValue(addr addrs.AbsOutputValue) *OutputChangeSrc { - addrStr := addr.String() for _, oc := range c.Outputs { - if oc.Addr.String() == addrStr { + if oc.Addr.Equal(addr) { return oc } } @@ -168,6 +165,22 @@ type ResourceInstanceChange struct { // Change is an embedded description of the change. Change + // ActionReason is an optional extra indication of why we chose the + // action recorded in Change.Action for this particular resource instance. + // + // This is an approximate mechanism only for the purpose of explaining the + // plan to end-users in the UI and is not to be used for any + // decision-making during the apply step; if apply behavior needs to vary + // depending on the "action reason" then the information for that decision + // must be recorded more precisely elsewhere for that purpose. + // + // Sometimes there might be more than one reason for choosing a particular + // action. In that case, it's up to the codepath making that decision to + // decide which value would provide the most relevant explanation to the + // end-user and return that. It's not a goal of this field to represent + // fine details about the planning process. + ActionReason ResourceInstanceChangeActionReason + // RequiredReplace is a set of paths that caused the change action to be // Replace rather than Update. Always nil if the change action is not // Replace. @@ -195,6 +208,7 @@ func (rc *ResourceInstanceChange) Encode(ty cty.Type) (*ResourceInstanceChangeSr DeposedKey: rc.DeposedKey, ProviderAddr: rc.ProviderAddr, ChangeSrc: *cs, + ActionReason: rc.ActionReason, RequiredReplace: rc.RequiredReplace, Private: rc.Private, }, err @@ -280,6 +294,43 @@ func (rc *ResourceInstanceChange) Simplify(destroying bool) *ResourceInstanceCha return rc } +// ResourceInstanceChangeActionReason allows for some extra user-facing +// reasoning for why a particular change action was chosen for a particular +// resource instance. +// +// This only represents sufficient detail to give a suitable explanation to +// an end-user, and mustn't be used for any real decision-making during the +// apply step. +type ResourceInstanceChangeActionReason rune + +//go:generate go run golang.org/x/tools/cmd/stringer -type=ResourceInstanceChangeActionReason changes.go + +const ( + // In most cases there's no special reason for choosing a particular + // action, which is represented by ResourceInstanceChangeNoReason. + ResourceInstanceChangeNoReason ResourceInstanceChangeActionReason = 0 + + // ResourceInstanceReplaceBecauseTainted indicates that the resource + // instance must be replaced because its existing current object is + // marked as "tainted". + ResourceInstanceReplaceBecauseTainted ResourceInstanceChangeActionReason = 'T' + + // ResourceInstanceReplaceByRequest indicates that the resource instance + // is planned to be replaced because a caller specifically asked for it + // to be using ReplaceAddrs. (On the command line, the -replace=... + // planning option.) + ResourceInstanceReplaceByRequest ResourceInstanceChangeActionReason = 'R' + + // ResourceInstanceReplaceBecauseCannotUpdate indicates that the resource + // instance is planned to be replaced because the provider has indicated + // that a requested change cannot be applied as an update. + // + // In this case, the RequiredReplace field will typically be populated on + // the ResourceInstanceChange object to give information about specifically + // which arguments changed in a non-updatable way. + ResourceInstanceReplaceBecauseCannotUpdate ResourceInstanceChangeActionReason = 'F' +) + // OutputChange describes a change to an output value. type OutputChange struct { // Addr is the absolute address of the output value that the change diff --git a/vendor/github.com/hashicorp/terraform/plans/changes_src.go b/vendor/github.com/hashicorp/terraform/plans/changes_src.go index 683c9f17..fdc0853c 100644 --- a/vendor/github.com/hashicorp/terraform/plans/changes_src.go +++ b/vendor/github.com/hashicorp/terraform/plans/changes_src.go @@ -34,12 +34,22 @@ type ResourceInstanceChangeSrc struct { // ChangeSrc is an embedded description of the not-yet-decoded change. ChangeSrc + // ActionReason is an optional extra indication of why we chose the + // action recorded in Change.Action for this particular resource instance. + // + // This is an approximate mechanism only for the purpose of explaining the + // plan to end-users in the UI and is not to be used for any + // decision-making during the apply step; if apply behavior needs to vary + // depending on the "action reason" then the information for that decision + // must be recorded more precisely elsewhere for that purpose. + // + // See the field of the same name in ResourceInstanceChange for more + // details. + ActionReason ResourceInstanceChangeActionReason + // RequiredReplace is a set of paths that caused the change action to be // Replace rather than Update. Always nil if the change action is not // Replace. - // - // This is retained only for UI-plan-rendering purposes and so it does not - // currently survive a round-trip through a saved plan file. RequiredReplace cty.PathSet // Private allows a provider to stash any extra data that is opaque to @@ -61,6 +71,7 @@ func (rcs *ResourceInstanceChangeSrc) Decode(ty cty.Type) (*ResourceInstanceChan DeposedKey: rcs.DeposedKey, ProviderAddr: rcs.ProviderAddr, Change: *change, + ActionReason: rcs.ActionReason, RequiredReplace: rcs.RequiredReplace, Private: rcs.Private, }, nil diff --git a/vendor/github.com/hashicorp/terraform/plans/dynamic_value_test.go b/vendor/github.com/hashicorp/terraform/plans/dynamic_value_test.go deleted file mode 100644 index bde22b12..00000000 --- a/vendor/github.com/hashicorp/terraform/plans/dynamic_value_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package plans - -import ( - "github.com/zclconf/go-cty/cty" -) - -func mustNewDynamicValue(val cty.Value, ty cty.Type) DynamicValue { - ret, err := NewDynamicValue(val, ty) - if err != nil { - panic(err) - } - return ret -} diff --git a/vendor/github.com/hashicorp/terraform/plans/internal/planproto/planfile.pb.go b/vendor/github.com/hashicorp/terraform/plans/internal/planproto/planfile.pb.go index 950d0826..faa7e330 100644 --- a/vendor/github.com/hashicorp/terraform/plans/internal/planproto/planfile.pb.go +++ b/vendor/github.com/hashicorp/terraform/plans/internal/planproto/planfile.pb.go @@ -1,24 +1,74 @@ // Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.15.6 // source: planfile.proto package planproto import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - math "math" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Mode describes the planning mode that created the plan. +type Mode int32 + +const ( + Mode_NORMAL Mode = 0 + Mode_DESTROY Mode = 1 + Mode_REFRESH_ONLY Mode = 2 +) + +// Enum value maps for Mode. +var ( + Mode_name = map[int32]string{ + 0: "NORMAL", + 1: "DESTROY", + 2: "REFRESH_ONLY", + } + Mode_value = map[string]int32{ + "NORMAL": 0, + "DESTROY": 1, + "REFRESH_ONLY": 2, + } +) + +func (x Mode) Enum() *Mode { + p := new(Mode) + *p = x + return p +} + +func (x Mode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Mode) Descriptor() protoreflect.EnumDescriptor { + return file_planfile_proto_enumTypes[0].Descriptor() +} + +func (Mode) Type() protoreflect.EnumType { + return &file_planfile_proto_enumTypes[0] +} -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package +func (x Mode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Mode.Descriptor instead. +func (Mode) EnumDescriptor() ([]byte, []int) { + return file_planfile_proto_rawDescGZIP(), []int{0} +} // Action describes the type of action planned for an object. // Not all action values are valid for all object types. @@ -34,61 +84,163 @@ const ( Action_CREATE_THEN_DELETE Action = 7 ) -var Action_name = map[int32]string{ - 0: "NOOP", - 1: "CREATE", - 2: "READ", - 3: "UPDATE", - 5: "DELETE", - 6: "DELETE_THEN_CREATE", - 7: "CREATE_THEN_DELETE", -} +// Enum value maps for Action. +var ( + Action_name = map[int32]string{ + 0: "NOOP", + 1: "CREATE", + 2: "READ", + 3: "UPDATE", + 5: "DELETE", + 6: "DELETE_THEN_CREATE", + 7: "CREATE_THEN_DELETE", + } + Action_value = map[string]int32{ + "NOOP": 0, + "CREATE": 1, + "READ": 2, + "UPDATE": 3, + "DELETE": 5, + "DELETE_THEN_CREATE": 6, + "CREATE_THEN_DELETE": 7, + } +) -var Action_value = map[string]int32{ - "NOOP": 0, - "CREATE": 1, - "READ": 2, - "UPDATE": 3, - "DELETE": 5, - "DELETE_THEN_CREATE": 6, - "CREATE_THEN_DELETE": 7, +func (x Action) Enum() *Action { + p := new(Action) + *p = x + return p } func (x Action) String() string { - return proto.EnumName(Action_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } +func (Action) Descriptor() protoreflect.EnumDescriptor { + return file_planfile_proto_enumTypes[1].Descriptor() +} + +func (Action) Type() protoreflect.EnumType { + return &file_planfile_proto_enumTypes[1] +} + +func (x Action) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Action.Descriptor instead. func (Action) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_02431083a6706c5b, []int{0} + return file_planfile_proto_rawDescGZIP(), []int{1} } -type ResourceInstanceChange_ResourceMode int32 +// ResourceInstanceActionReason sometimes provides some additional user-facing +// context for why a particular action was chosen for a resource instance. +// This is for user feedback only and never used to drive behavior during the +// subsequent apply step. +type ResourceInstanceActionReason int32 const ( - ResourceInstanceChange_managed ResourceInstanceChange_ResourceMode = 0 - ResourceInstanceChange_data ResourceInstanceChange_ResourceMode = 1 + ResourceInstanceActionReason_NONE ResourceInstanceActionReason = 0 + ResourceInstanceActionReason_REPLACE_BECAUSE_TAINTED ResourceInstanceActionReason = 1 + ResourceInstanceActionReason_REPLACE_BY_REQUEST ResourceInstanceActionReason = 2 + ResourceInstanceActionReason_REPLACE_BECAUSE_CANNOT_UPDATE ResourceInstanceActionReason = 3 ) -var ResourceInstanceChange_ResourceMode_name = map[int32]string{ - 0: "managed", - 1: "data", +// Enum value maps for ResourceInstanceActionReason. +var ( + ResourceInstanceActionReason_name = map[int32]string{ + 0: "NONE", + 1: "REPLACE_BECAUSE_TAINTED", + 2: "REPLACE_BY_REQUEST", + 3: "REPLACE_BECAUSE_CANNOT_UPDATE", + } + ResourceInstanceActionReason_value = map[string]int32{ + "NONE": 0, + "REPLACE_BECAUSE_TAINTED": 1, + "REPLACE_BY_REQUEST": 2, + "REPLACE_BECAUSE_CANNOT_UPDATE": 3, + } +) + +func (x ResourceInstanceActionReason) Enum() *ResourceInstanceActionReason { + p := new(ResourceInstanceActionReason) + *p = x + return p } -var ResourceInstanceChange_ResourceMode_value = map[string]int32{ - "managed": 0, - "data": 1, +func (x ResourceInstanceActionReason) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ResourceInstanceActionReason) Descriptor() protoreflect.EnumDescriptor { + return file_planfile_proto_enumTypes[2].Descriptor() +} + +func (ResourceInstanceActionReason) Type() protoreflect.EnumType { + return &file_planfile_proto_enumTypes[2] +} + +func (x ResourceInstanceActionReason) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ResourceInstanceActionReason.Descriptor instead. +func (ResourceInstanceActionReason) EnumDescriptor() ([]byte, []int) { + return file_planfile_proto_rawDescGZIP(), []int{2} +} + +type ResourceInstanceChange_ResourceMode int32 + +const ( + ResourceInstanceChange_managed ResourceInstanceChange_ResourceMode = 0 // for "resource" blocks in configuration + ResourceInstanceChange_data ResourceInstanceChange_ResourceMode = 1 // for "data" blocks in configuration +) + +// Enum value maps for ResourceInstanceChange_ResourceMode. +var ( + ResourceInstanceChange_ResourceMode_name = map[int32]string{ + 0: "managed", + 1: "data", + } + ResourceInstanceChange_ResourceMode_value = map[string]int32{ + "managed": 0, + "data": 1, + } +) + +func (x ResourceInstanceChange_ResourceMode) Enum() *ResourceInstanceChange_ResourceMode { + p := new(ResourceInstanceChange_ResourceMode) + *p = x + return p } func (x ResourceInstanceChange_ResourceMode) String() string { - return proto.EnumName(ResourceInstanceChange_ResourceMode_name, int32(x)) + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ResourceInstanceChange_ResourceMode) Descriptor() protoreflect.EnumDescriptor { + return file_planfile_proto_enumTypes[3].Descriptor() +} + +func (ResourceInstanceChange_ResourceMode) Type() protoreflect.EnumType { + return &file_planfile_proto_enumTypes[3] } +func (x ResourceInstanceChange_ResourceMode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ResourceInstanceChange_ResourceMode.Descriptor instead. func (ResourceInstanceChange_ResourceMode) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_02431083a6706c5b, []int{3, 0} + return file_planfile_proto_rawDescGZIP(), []int{3, 0} } // Plan is the root message type for the tfplan file type Plan struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Version is incremented whenever there is a breaking change to // the serialization format. Programs reading serialized plans should // verify that version is set to the expected value and abort processing @@ -98,6 +250,12 @@ type Plan struct { // changes to the format or if an existing consumer would fail to process // the file for another message- or field-specific reason. Version uint64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + // The mode that was active when this plan was created. + // + // This is saved only for UI purposes, so that Terraform can tailor its + // rendering of the plan depending on the mode. This must never be used to + // make decisions in Terraform Core during the applying of a plan. + UiMode Mode `protobuf:"varint,17,opt,name=ui_mode,json=uiMode,proto3,enum=tfplan.Mode" json:"ui_mode,omitempty"` // The variables that were set when creating the plan. Each value is // a msgpack serialization of an HCL value. Variables map[string]*DynamicValue `protobuf:"bytes,2,rep,name=variables,proto3" json:"variables,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` @@ -114,6 +272,11 @@ type Plan struct { // target addresses are present, the plan applies to the whole // configuration. TargetAddrs []string `protobuf:"bytes,5,rep,name=target_addrs,json=targetAddrs,proto3" json:"target_addrs,omitempty"` + // An unordered set of force-replace addresses to include when applying. + // This must match the set of addresses that was used when creating the + // plan, or else applying the plan will fail when it reaches a different + // conclusion about what action a particular resource instance needs. + ForceReplaceAddrs []string `protobuf:"bytes,16,rep,name=force_replace_addrs,json=forceReplaceAddrs,proto3" json:"force_replace_addrs,omitempty"` // The version string for the Terraform binary that created this plan. TerraformVersion string `protobuf:"bytes,14,opt,name=terraform_version,json=terraformVersion,proto3" json:"terraform_version,omitempty"` // SHA256 digests of all of the provider plugin binaries that were used @@ -121,145 +284,171 @@ type Plan struct { ProviderHashes map[string]*Hash `protobuf:"bytes,15,rep,name=provider_hashes,json=providerHashes,proto3" json:"provider_hashes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Backend is a description of the backend configuration and other related // settings at the time the plan was created. - Backend *Backend `protobuf:"bytes,13,opt,name=backend,proto3" json:"backend,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Backend *Backend `protobuf:"bytes,13,opt,name=backend,proto3" json:"backend,omitempty"` } -func (m *Plan) Reset() { *m = Plan{} } -func (m *Plan) String() string { return proto.CompactTextString(m) } -func (*Plan) ProtoMessage() {} -func (*Plan) Descriptor() ([]byte, []int) { - return fileDescriptor_02431083a6706c5b, []int{0} +func (x *Plan) Reset() { + *x = Plan{} + if protoimpl.UnsafeEnabled { + mi := &file_planfile_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Plan) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Plan.Unmarshal(m, b) +func (x *Plan) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Plan) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Plan.Marshal(b, m, deterministic) -} -func (m *Plan) XXX_Merge(src proto.Message) { - xxx_messageInfo_Plan.Merge(m, src) + +func (*Plan) ProtoMessage() {} + +func (x *Plan) ProtoReflect() protoreflect.Message { + mi := &file_planfile_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *Plan) XXX_Size() int { - return xxx_messageInfo_Plan.Size(m) + +// Deprecated: Use Plan.ProtoReflect.Descriptor instead. +func (*Plan) Descriptor() ([]byte, []int) { + return file_planfile_proto_rawDescGZIP(), []int{0} } -func (m *Plan) XXX_DiscardUnknown() { - xxx_messageInfo_Plan.DiscardUnknown(m) + +func (x *Plan) GetVersion() uint64 { + if x != nil { + return x.Version + } + return 0 } -var xxx_messageInfo_Plan proto.InternalMessageInfo +func (x *Plan) GetUiMode() Mode { + if x != nil { + return x.UiMode + } + return Mode_NORMAL +} -func (m *Plan) GetVersion() uint64 { - if m != nil { - return m.Version +func (x *Plan) GetVariables() map[string]*DynamicValue { + if x != nil { + return x.Variables } - return 0 + return nil } -func (m *Plan) GetVariables() map[string]*DynamicValue { - if m != nil { - return m.Variables +func (x *Plan) GetResourceChanges() []*ResourceInstanceChange { + if x != nil { + return x.ResourceChanges } return nil } -func (m *Plan) GetResourceChanges() []*ResourceInstanceChange { - if m != nil { - return m.ResourceChanges +func (x *Plan) GetOutputChanges() []*OutputChange { + if x != nil { + return x.OutputChanges } return nil } -func (m *Plan) GetOutputChanges() []*OutputChange { - if m != nil { - return m.OutputChanges +func (x *Plan) GetTargetAddrs() []string { + if x != nil { + return x.TargetAddrs } return nil } -func (m *Plan) GetTargetAddrs() []string { - if m != nil { - return m.TargetAddrs +func (x *Plan) GetForceReplaceAddrs() []string { + if x != nil { + return x.ForceReplaceAddrs } return nil } -func (m *Plan) GetTerraformVersion() string { - if m != nil { - return m.TerraformVersion +func (x *Plan) GetTerraformVersion() string { + if x != nil { + return x.TerraformVersion } return "" } -func (m *Plan) GetProviderHashes() map[string]*Hash { - if m != nil { - return m.ProviderHashes +func (x *Plan) GetProviderHashes() map[string]*Hash { + if x != nil { + return x.ProviderHashes } return nil } -func (m *Plan) GetBackend() *Backend { - if m != nil { - return m.Backend +func (x *Plan) GetBackend() *Backend { + if x != nil { + return x.Backend } return nil } // Backend is a description of backend configuration and other related settings. type Backend struct { - Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` - Config *DynamicValue `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` - Workspace string `protobuf:"bytes,3,opt,name=workspace,proto3" json:"workspace,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *Backend) Reset() { *m = Backend{} } -func (m *Backend) String() string { return proto.CompactTextString(m) } -func (*Backend) ProtoMessage() {} -func (*Backend) Descriptor() ([]byte, []int) { - return fileDescriptor_02431083a6706c5b, []int{1} + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + Config *DynamicValue `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` + Workspace string `protobuf:"bytes,3,opt,name=workspace,proto3" json:"workspace,omitempty"` } -func (m *Backend) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Backend.Unmarshal(m, b) -} -func (m *Backend) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Backend.Marshal(b, m, deterministic) -} -func (m *Backend) XXX_Merge(src proto.Message) { - xxx_messageInfo_Backend.Merge(m, src) +func (x *Backend) Reset() { + *x = Backend{} + if protoimpl.UnsafeEnabled { + mi := &file_planfile_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Backend) XXX_Size() int { - return xxx_messageInfo_Backend.Size(m) + +func (x *Backend) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Backend) XXX_DiscardUnknown() { - xxx_messageInfo_Backend.DiscardUnknown(m) + +func (*Backend) ProtoMessage() {} + +func (x *Backend) ProtoReflect() protoreflect.Message { + mi := &file_planfile_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Backend proto.InternalMessageInfo +// Deprecated: Use Backend.ProtoReflect.Descriptor instead. +func (*Backend) Descriptor() ([]byte, []int) { + return file_planfile_proto_rawDescGZIP(), []int{1} +} -func (m *Backend) GetType() string { - if m != nil { - return m.Type +func (x *Backend) GetType() string { + if x != nil { + return x.Type } return "" } -func (m *Backend) GetConfig() *DynamicValue { - if m != nil { - return m.Config +func (x *Backend) GetConfig() *DynamicValue { + if x != nil { + return x.Config } return nil } -func (m *Backend) GetWorkspace() string { - if m != nil { - return m.Workspace +func (x *Backend) GetWorkspace() string { + if x != nil { + return x.Workspace } return "" } @@ -267,6 +456,10 @@ func (m *Backend) GetWorkspace() string { // Change represents a change made to some object, transforming it from an old // state to a new state. type Change struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Not all action values are valid for all object types. Consult // the documentation for any message that embeds Change. Action Action `protobuf:"varint,1,opt,name=action,proto3,enum=tfplan.Action" json:"action,omitempty"` @@ -279,52 +472,82 @@ type Change struct { // (or null, if no prior value exists) and the value that was or will be read, // respectively. // - For no-op, one value is provided that is left unmodified by this non-change. - Values []*DynamicValue `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Values []*DynamicValue `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"` + // An unordered set of paths into the old value which are marked as + // sensitive. Values at these paths should be obscured in human-readable + // output. This set is always empty for create. + BeforeSensitivePaths []*Path `protobuf:"bytes,3,rep,name=before_sensitive_paths,json=beforeSensitivePaths,proto3" json:"before_sensitive_paths,omitempty"` + // An unordered set of paths into the new value which are marked as + // sensitive. Values at these paths should be obscured in human-readable + // output. This set is always empty for delete. + AfterSensitivePaths []*Path `protobuf:"bytes,4,rep,name=after_sensitive_paths,json=afterSensitivePaths,proto3" json:"after_sensitive_paths,omitempty"` +} + +func (x *Change) Reset() { + *x = Change{} + if protoimpl.UnsafeEnabled { + mi := &file_planfile_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Change) Reset() { *m = Change{} } -func (m *Change) String() string { return proto.CompactTextString(m) } -func (*Change) ProtoMessage() {} -func (*Change) Descriptor() ([]byte, []int) { - return fileDescriptor_02431083a6706c5b, []int{2} +func (x *Change) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Change) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Change.Unmarshal(m, b) -} -func (m *Change) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Change.Marshal(b, m, deterministic) -} -func (m *Change) XXX_Merge(src proto.Message) { - xxx_messageInfo_Change.Merge(m, src) +func (*Change) ProtoMessage() {} + +func (x *Change) ProtoReflect() protoreflect.Message { + mi := &file_planfile_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *Change) XXX_Size() int { - return xxx_messageInfo_Change.Size(m) + +// Deprecated: Use Change.ProtoReflect.Descriptor instead. +func (*Change) Descriptor() ([]byte, []int) { + return file_planfile_proto_rawDescGZIP(), []int{2} } -func (m *Change) XXX_DiscardUnknown() { - xxx_messageInfo_Change.DiscardUnknown(m) + +func (x *Change) GetAction() Action { + if x != nil { + return x.Action + } + return Action_NOOP } -var xxx_messageInfo_Change proto.InternalMessageInfo +func (x *Change) GetValues() []*DynamicValue { + if x != nil { + return x.Values + } + return nil +} -func (m *Change) GetAction() Action { - if m != nil { - return m.Action +func (x *Change) GetBeforeSensitivePaths() []*Path { + if x != nil { + return x.BeforeSensitivePaths } - return Action_NOOP + return nil } -func (m *Change) GetValues() []*DynamicValue { - if m != nil { - return m.Values +func (x *Change) GetAfterSensitivePaths() []*Path { + if x != nil { + return x.AfterSensitivePaths } return nil } type ResourceInstanceChange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // module_path is an address to the module that defined this resource. // module_path is omitted for resources in the root module. For descendent modules // it is a string like module.foo.module.bar as would be seen at the beginning of a @@ -342,7 +565,7 @@ type ResourceInstanceChange struct { // attributes ("count" or "for_each") are being used for this resource. If none // are in use, this field is omitted. // - // Types that are valid to be assigned to InstanceKey: + // Types that are assignable to InstanceKey: // *ResourceInstanceChange_Str // *ResourceInstanceChange_Int InstanceKey isResourceInstanceChange_InstanceKey `protobuf_oneof:"instance_key"` @@ -366,81 +589,73 @@ type ResourceInstanceChange struct { // An unordered set of paths that prompted the change action to be // "replace" rather than "update". Empty for any action other than // "replace". - RequiredReplace []*Path `protobuf:"bytes,11,rep,name=required_replace,json=requiredReplace,proto3" json:"required_replace,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + RequiredReplace []*Path `protobuf:"bytes,11,rep,name=required_replace,json=requiredReplace,proto3" json:"required_replace,omitempty"` + // Optional extra user-oriented context for why change.Action was chosen. + // This is for user feedback only and never used to drive behavior during + // apply. + ActionReason ResourceInstanceActionReason `protobuf:"varint,12,opt,name=action_reason,json=actionReason,proto3,enum=tfplan.ResourceInstanceActionReason" json:"action_reason,omitempty"` +} + +func (x *ResourceInstanceChange) Reset() { + *x = ResourceInstanceChange{} + if protoimpl.UnsafeEnabled { + mi := &file_planfile_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *ResourceInstanceChange) Reset() { *m = ResourceInstanceChange{} } -func (m *ResourceInstanceChange) String() string { return proto.CompactTextString(m) } -func (*ResourceInstanceChange) ProtoMessage() {} -func (*ResourceInstanceChange) Descriptor() ([]byte, []int) { - return fileDescriptor_02431083a6706c5b, []int{3} +func (x *ResourceInstanceChange) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *ResourceInstanceChange) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ResourceInstanceChange.Unmarshal(m, b) -} -func (m *ResourceInstanceChange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ResourceInstanceChange.Marshal(b, m, deterministic) -} -func (m *ResourceInstanceChange) XXX_Merge(src proto.Message) { - xxx_messageInfo_ResourceInstanceChange.Merge(m, src) -} -func (m *ResourceInstanceChange) XXX_Size() int { - return xxx_messageInfo_ResourceInstanceChange.Size(m) -} -func (m *ResourceInstanceChange) XXX_DiscardUnknown() { - xxx_messageInfo_ResourceInstanceChange.DiscardUnknown(m) +func (*ResourceInstanceChange) ProtoMessage() {} + +func (x *ResourceInstanceChange) ProtoReflect() protoreflect.Message { + mi := &file_planfile_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_ResourceInstanceChange proto.InternalMessageInfo +// Deprecated: Use ResourceInstanceChange.ProtoReflect.Descriptor instead. +func (*ResourceInstanceChange) Descriptor() ([]byte, []int) { + return file_planfile_proto_rawDescGZIP(), []int{3} +} -func (m *ResourceInstanceChange) GetModulePath() string { - if m != nil { - return m.ModulePath +func (x *ResourceInstanceChange) GetModulePath() string { + if x != nil { + return x.ModulePath } return "" } -func (m *ResourceInstanceChange) GetMode() ResourceInstanceChange_ResourceMode { - if m != nil { - return m.Mode +func (x *ResourceInstanceChange) GetMode() ResourceInstanceChange_ResourceMode { + if x != nil { + return x.Mode } return ResourceInstanceChange_managed } -func (m *ResourceInstanceChange) GetType() string { - if m != nil { - return m.Type +func (x *ResourceInstanceChange) GetType() string { + if x != nil { + return x.Type } return "" } -func (m *ResourceInstanceChange) GetName() string { - if m != nil { - return m.Name +func (x *ResourceInstanceChange) GetName() string { + if x != nil { + return x.Name } return "" } -type isResourceInstanceChange_InstanceKey interface { - isResourceInstanceChange_InstanceKey() -} - -type ResourceInstanceChange_Str struct { - Str string `protobuf:"bytes,5,opt,name=str,proto3,oneof"` -} - -type ResourceInstanceChange_Int struct { - Int int64 `protobuf:"varint,6,opt,name=int,proto3,oneof"` -} - -func (*ResourceInstanceChange_Str) isResourceInstanceChange_InstanceKey() {} - -func (*ResourceInstanceChange_Int) isResourceInstanceChange_InstanceKey() {} - func (m *ResourceInstanceChange) GetInstanceKey() isResourceInstanceChange_InstanceKey { if m != nil { return m.InstanceKey @@ -448,64 +663,83 @@ func (m *ResourceInstanceChange) GetInstanceKey() isResourceInstanceChange_Insta return nil } -func (m *ResourceInstanceChange) GetStr() string { - if x, ok := m.GetInstanceKey().(*ResourceInstanceChange_Str); ok { +func (x *ResourceInstanceChange) GetStr() string { + if x, ok := x.GetInstanceKey().(*ResourceInstanceChange_Str); ok { return x.Str } return "" } -func (m *ResourceInstanceChange) GetInt() int64 { - if x, ok := m.GetInstanceKey().(*ResourceInstanceChange_Int); ok { +func (x *ResourceInstanceChange) GetInt() int64 { + if x, ok := x.GetInstanceKey().(*ResourceInstanceChange_Int); ok { return x.Int } return 0 } -func (m *ResourceInstanceChange) GetDeposedKey() string { - if m != nil { - return m.DeposedKey +func (x *ResourceInstanceChange) GetDeposedKey() string { + if x != nil { + return x.DeposedKey } return "" } -func (m *ResourceInstanceChange) GetProvider() string { - if m != nil { - return m.Provider +func (x *ResourceInstanceChange) GetProvider() string { + if x != nil { + return x.Provider } return "" } -func (m *ResourceInstanceChange) GetChange() *Change { - if m != nil { - return m.Change +func (x *ResourceInstanceChange) GetChange() *Change { + if x != nil { + return x.Change } return nil } -func (m *ResourceInstanceChange) GetPrivate() []byte { - if m != nil { - return m.Private +func (x *ResourceInstanceChange) GetPrivate() []byte { + if x != nil { + return x.Private } return nil } -func (m *ResourceInstanceChange) GetRequiredReplace() []*Path { - if m != nil { - return m.RequiredReplace +func (x *ResourceInstanceChange) GetRequiredReplace() []*Path { + if x != nil { + return x.RequiredReplace } return nil } -// XXX_OneofWrappers is for the internal use of the proto package. -func (*ResourceInstanceChange) XXX_OneofWrappers() []interface{} { - return []interface{}{ - (*ResourceInstanceChange_Str)(nil), - (*ResourceInstanceChange_Int)(nil), +func (x *ResourceInstanceChange) GetActionReason() ResourceInstanceActionReason { + if x != nil { + return x.ActionReason } + return ResourceInstanceActionReason_NONE +} + +type isResourceInstanceChange_InstanceKey interface { + isResourceInstanceChange_InstanceKey() +} + +type ResourceInstanceChange_Str struct { + Str string `protobuf:"bytes,5,opt,name=str,proto3,oneof"` +} + +type ResourceInstanceChange_Int struct { + Int int64 `protobuf:"varint,6,opt,name=int,proto3,oneof"` } +func (*ResourceInstanceChange_Str) isResourceInstanceChange_InstanceKey() {} + +func (*ResourceInstanceChange_Int) isResourceInstanceChange_InstanceKey() {} + type OutputChange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Name of the output as defined in the root module. Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Description of the proposed change. May use "no-op", "create", @@ -514,54 +748,58 @@ type OutputChange struct { // Sensitive, if true, indicates that one or more of the values given // in "change" is sensitive and should not be shown directly in any // rendered plan. - Sensitive bool `protobuf:"varint,3,opt,name=sensitive,proto3" json:"sensitive,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Sensitive bool `protobuf:"varint,3,opt,name=sensitive,proto3" json:"sensitive,omitempty"` } -func (m *OutputChange) Reset() { *m = OutputChange{} } -func (m *OutputChange) String() string { return proto.CompactTextString(m) } -func (*OutputChange) ProtoMessage() {} -func (*OutputChange) Descriptor() ([]byte, []int) { - return fileDescriptor_02431083a6706c5b, []int{4} +func (x *OutputChange) Reset() { + *x = OutputChange{} + if protoimpl.UnsafeEnabled { + mi := &file_planfile_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *OutputChange) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_OutputChange.Unmarshal(m, b) -} -func (m *OutputChange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_OutputChange.Marshal(b, m, deterministic) -} -func (m *OutputChange) XXX_Merge(src proto.Message) { - xxx_messageInfo_OutputChange.Merge(m, src) -} -func (m *OutputChange) XXX_Size() int { - return xxx_messageInfo_OutputChange.Size(m) +func (x *OutputChange) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *OutputChange) XXX_DiscardUnknown() { - xxx_messageInfo_OutputChange.DiscardUnknown(m) + +func (*OutputChange) ProtoMessage() {} + +func (x *OutputChange) ProtoReflect() protoreflect.Message { + mi := &file_planfile_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_OutputChange proto.InternalMessageInfo +// Deprecated: Use OutputChange.ProtoReflect.Descriptor instead. +func (*OutputChange) Descriptor() ([]byte, []int) { + return file_planfile_proto_rawDescGZIP(), []int{4} +} -func (m *OutputChange) GetName() string { - if m != nil { - return m.Name +func (x *OutputChange) GetName() string { + if x != nil { + return x.Name } return "" } -func (m *OutputChange) GetChange() *Change { - if m != nil { - return m.Change +func (x *OutputChange) GetChange() *Change { + if x != nil { + return x.Change } return nil } -func (m *OutputChange) GetSensitive() bool { - if m != nil { - return m.Sensitive +func (x *OutputChange) GetSensitive() bool { + if x != nil { + return x.Sensitive } return false } @@ -579,40 +817,48 @@ func (m *OutputChange) GetSensitive() bool { // attribute is present. The top-level format version will not be incremented // for changes to the set of dynamic serialization formats. type DynamicValue struct { - Msgpack []byte `protobuf:"bytes,1,opt,name=msgpack,proto3" json:"msgpack,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *DynamicValue) Reset() { *m = DynamicValue{} } -func (m *DynamicValue) String() string { return proto.CompactTextString(m) } -func (*DynamicValue) ProtoMessage() {} -func (*DynamicValue) Descriptor() ([]byte, []int) { - return fileDescriptor_02431083a6706c5b, []int{5} + Msgpack []byte `protobuf:"bytes,1,opt,name=msgpack,proto3" json:"msgpack,omitempty"` } -func (m *DynamicValue) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_DynamicValue.Unmarshal(m, b) -} -func (m *DynamicValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_DynamicValue.Marshal(b, m, deterministic) -} -func (m *DynamicValue) XXX_Merge(src proto.Message) { - xxx_messageInfo_DynamicValue.Merge(m, src) +func (x *DynamicValue) Reset() { + *x = DynamicValue{} + if protoimpl.UnsafeEnabled { + mi := &file_planfile_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *DynamicValue) XXX_Size() int { - return xxx_messageInfo_DynamicValue.Size(m) + +func (x *DynamicValue) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *DynamicValue) XXX_DiscardUnknown() { - xxx_messageInfo_DynamicValue.DiscardUnknown(m) + +func (*DynamicValue) ProtoMessage() {} + +func (x *DynamicValue) ProtoReflect() protoreflect.Message { + mi := &file_planfile_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_DynamicValue proto.InternalMessageInfo +// Deprecated: Use DynamicValue.ProtoReflect.Descriptor instead. +func (*DynamicValue) Descriptor() ([]byte, []int) { + return file_planfile_proto_rawDescGZIP(), []int{5} +} -func (m *DynamicValue) GetMsgpack() []byte { - if m != nil { - return m.Msgpack +func (x *DynamicValue) GetMsgpack() []byte { + if x != nil { + return x.Msgpack } return nil } @@ -626,40 +872,48 @@ func (m *DynamicValue) GetMsgpack() []byte { // top-level format version will not be incremented for changes to the set of // hash algorithms. type Hash struct { - Sha256 []byte `protobuf:"bytes,1,opt,name=sha256,proto3" json:"sha256,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *Hash) Reset() { *m = Hash{} } -func (m *Hash) String() string { return proto.CompactTextString(m) } -func (*Hash) ProtoMessage() {} -func (*Hash) Descriptor() ([]byte, []int) { - return fileDescriptor_02431083a6706c5b, []int{6} + Sha256 []byte `protobuf:"bytes,1,opt,name=sha256,proto3" json:"sha256,omitempty"` } -func (m *Hash) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Hash.Unmarshal(m, b) -} -func (m *Hash) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Hash.Marshal(b, m, deterministic) -} -func (m *Hash) XXX_Merge(src proto.Message) { - xxx_messageInfo_Hash.Merge(m, src) +func (x *Hash) Reset() { + *x = Hash{} + if protoimpl.UnsafeEnabled { + mi := &file_planfile_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Hash) XXX_Size() int { - return xxx_messageInfo_Hash.Size(m) + +func (x *Hash) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Hash) XXX_DiscardUnknown() { - xxx_messageInfo_Hash.DiscardUnknown(m) + +func (*Hash) ProtoMessage() {} + +func (x *Hash) ProtoReflect() protoreflect.Message { + mi := &file_planfile_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Hash proto.InternalMessageInfo +// Deprecated: Use Hash.ProtoReflect.Descriptor instead. +func (*Hash) Descriptor() ([]byte, []int) { + return file_planfile_proto_rawDescGZIP(), []int{6} +} -func (m *Hash) GetSha256() []byte { - if m != nil { - return m.Sha256 +func (x *Hash) GetSha256() []byte { + if x != nil { + return x.Sha256 } return nil } @@ -668,88 +922,129 @@ func (m *Hash) GetSha256() []byte { // used to refer to a sub-structure within a dynamic data structure presented // separately. type Path struct { - Steps []*Path_Step `protobuf:"bytes,1,rep,name=steps,proto3" json:"steps,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *Path) Reset() { *m = Path{} } -func (m *Path) String() string { return proto.CompactTextString(m) } -func (*Path) ProtoMessage() {} -func (*Path) Descriptor() ([]byte, []int) { - return fileDescriptor_02431083a6706c5b, []int{7} + Steps []*Path_Step `protobuf:"bytes,1,rep,name=steps,proto3" json:"steps,omitempty"` } -func (m *Path) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Path.Unmarshal(m, b) -} -func (m *Path) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Path.Marshal(b, m, deterministic) -} -func (m *Path) XXX_Merge(src proto.Message) { - xxx_messageInfo_Path.Merge(m, src) +func (x *Path) Reset() { + *x = Path{} + if protoimpl.UnsafeEnabled { + mi := &file_planfile_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Path) XXX_Size() int { - return xxx_messageInfo_Path.Size(m) + +func (x *Path) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Path) XXX_DiscardUnknown() { - xxx_messageInfo_Path.DiscardUnknown(m) + +func (*Path) ProtoMessage() {} + +func (x *Path) ProtoReflect() protoreflect.Message { + mi := &file_planfile_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Path proto.InternalMessageInfo +// Deprecated: Use Path.ProtoReflect.Descriptor instead. +func (*Path) Descriptor() ([]byte, []int) { + return file_planfile_proto_rawDescGZIP(), []int{7} +} -func (m *Path) GetSteps() []*Path_Step { - if m != nil { - return m.Steps +func (x *Path) GetSteps() []*Path_Step { + if x != nil { + return x.Steps } return nil } type Path_Step struct { - // Types that are valid to be assigned to Selector: + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Selector: // *Path_Step_AttributeName // *Path_Step_ElementKey - Selector isPath_Step_Selector `protobuf_oneof:"selector"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Selector isPath_Step_Selector `protobuf_oneof:"selector"` } -func (m *Path_Step) Reset() { *m = Path_Step{} } -func (m *Path_Step) String() string { return proto.CompactTextString(m) } -func (*Path_Step) ProtoMessage() {} -func (*Path_Step) Descriptor() ([]byte, []int) { - return fileDescriptor_02431083a6706c5b, []int{7, 0} +func (x *Path_Step) Reset() { + *x = Path_Step{} + if protoimpl.UnsafeEnabled { + mi := &file_planfile_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Path_Step) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Path_Step.Unmarshal(m, b) +func (x *Path_Step) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Path_Step) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Path_Step.Marshal(b, m, deterministic) + +func (*Path_Step) ProtoMessage() {} + +func (x *Path_Step) ProtoReflect() protoreflect.Message { + mi := &file_planfile_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *Path_Step) XXX_Merge(src proto.Message) { - xxx_messageInfo_Path_Step.Merge(m, src) + +// Deprecated: Use Path_Step.ProtoReflect.Descriptor instead. +func (*Path_Step) Descriptor() ([]byte, []int) { + return file_planfile_proto_rawDescGZIP(), []int{7, 0} } -func (m *Path_Step) XXX_Size() int { - return xxx_messageInfo_Path_Step.Size(m) + +func (m *Path_Step) GetSelector() isPath_Step_Selector { + if m != nil { + return m.Selector + } + return nil } -func (m *Path_Step) XXX_DiscardUnknown() { - xxx_messageInfo_Path_Step.DiscardUnknown(m) + +func (x *Path_Step) GetAttributeName() string { + if x, ok := x.GetSelector().(*Path_Step_AttributeName); ok { + return x.AttributeName + } + return "" } -var xxx_messageInfo_Path_Step proto.InternalMessageInfo +func (x *Path_Step) GetElementKey() *DynamicValue { + if x, ok := x.GetSelector().(*Path_Step_ElementKey); ok { + return x.ElementKey + } + return nil +} type isPath_Step_Selector interface { isPath_Step_Selector() } type Path_Step_AttributeName struct { + // Set "attribute_name" to represent looking up an attribute + // in the current object value. AttributeName string `protobuf:"bytes,1,opt,name=attribute_name,json=attributeName,proto3,oneof"` } type Path_Step_ElementKey struct { + // Set "element_key" to represent looking up an element in + // an indexable collection type. ElementKey *DynamicValue `protobuf:"bytes,2,opt,name=element_key,json=elementKey,proto3,oneof"` } @@ -757,109 +1052,352 @@ func (*Path_Step_AttributeName) isPath_Step_Selector() {} func (*Path_Step_ElementKey) isPath_Step_Selector() {} -func (m *Path_Step) GetSelector() isPath_Step_Selector { - if m != nil { - return m.Selector - } - return nil -} +var File_planfile_proto protoreflect.FileDescriptor + +var file_planfile_proto_rawDesc = []byte{ + 0x0a, 0x0e, 0x70, 0x6c, 0x61, 0x6e, 0x66, 0x69, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x06, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x22, 0xa5, 0x05, 0x0a, 0x04, 0x50, 0x6c, 0x61, + 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x07, 0x75, + 0x69, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0c, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x75, 0x69, 0x4d, 0x6f, + 0x64, 0x65, 0x12, 0x39, 0x0a, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, + 0x6c, 0x61, 0x6e, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x49, 0x0a, + 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, + 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x0e, 0x6f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0d, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, + 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x66, 0x6f, 0x72, 0x63, + 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, + 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6c, + 0x61, 0x63, 0x65, 0x41, 0x64, 0x64, 0x72, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x65, 0x72, 0x72, + 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x49, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, + 0x12, 0x29, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x65, + 0x6e, 0x64, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x1a, 0x52, 0x0a, 0x0e, 0x56, + 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, + 0x4f, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x48, 0x61, 0x73, 0x68, 0x65, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, + 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0x69, 0x0a, 0x07, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x2c, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x0a, + 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0xe4, 0x01, 0x0a, 0x06, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c, + 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x16, + 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, + 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x14, 0x62, 0x65, 0x66, 0x6f, + 0x72, 0x65, 0x53, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, + 0x12, 0x40, 0x0a, 0x15, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, + 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x13, 0x61, + 0x66, 0x74, 0x65, 0x72, 0x53, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, + 0x68, 0x73, 0x22, 0x84, 0x04, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1f, 0x0a, + 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x3f, + 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x03, 0x73, 0x74, 0x72, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x73, 0x74, 0x72, 0x12, 0x12, 0x0a, 0x03, 0x69, + 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x03, 0x69, 0x6e, 0x74, 0x12, + 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x4b, 0x65, 0x79, + 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x06, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, + 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x63, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x37, + 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, + 0x63, 0x65, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, + 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, + 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x0d, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, + 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x0c, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x22, 0x25, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, + 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x10, 0x00, 0x12, + 0x08, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x10, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x22, 0x68, 0x0a, 0x0c, 0x4f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, + 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, + 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, + 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, + 0x69, 0x76, 0x65, 0x22, 0x28, 0x0a, 0x0c, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x73, 0x67, 0x70, 0x61, 0x63, 0x6b, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x73, 0x67, 0x70, 0x61, 0x63, 0x6b, 0x22, 0x1e, 0x0a, + 0x04, 0x48, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x22, 0xa5, 0x01, + 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x27, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, + 0x61, 0x74, 0x68, 0x2e, 0x53, 0x74, 0x65, 0x70, 0x52, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x1a, + 0x74, 0x0a, 0x04, 0x53, 0x74, 0x65, 0x70, 0x12, 0x27, 0x0a, 0x0e, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x00, 0x52, 0x0d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x37, 0x0a, 0x0b, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x44, + 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x65, + 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x73, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2a, 0x31, 0x0a, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0a, 0x0a, + 0x06, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x53, + 0x54, 0x52, 0x4f, 0x59, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x52, 0x45, 0x46, 0x52, 0x45, 0x53, + 0x48, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x02, 0x2a, 0x70, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4f, 0x50, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, + 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x52, 0x45, 0x41, 0x44, + 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x03, 0x12, 0x0a, + 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x44, 0x45, + 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, + 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x48, 0x45, + 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x07, 0x2a, 0x80, 0x01, 0x0a, 0x1c, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x4e, + 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, + 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x54, 0x41, 0x49, 0x4e, 0x54, 0x45, 0x44, + 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, 0x42, 0x59, + 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x02, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, + 0x50, 0x4c, 0x41, 0x43, 0x45, 0x5f, 0x42, 0x45, 0x43, 0x41, 0x55, 0x53, 0x45, 0x5f, 0x43, 0x41, + 0x4e, 0x4e, 0x4f, 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x03, 0x42, 0x39, 0x5a, + 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, + 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x2f, + 0x70, 0x6c, 0x61, 0x6e, 0x73, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, + 0x6c, 0x61, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_planfile_proto_rawDescOnce sync.Once + file_planfile_proto_rawDescData = file_planfile_proto_rawDesc +) -func (m *Path_Step) GetAttributeName() string { - if x, ok := m.GetSelector().(*Path_Step_AttributeName); ok { - return x.AttributeName +func file_planfile_proto_rawDescGZIP() []byte { + file_planfile_proto_rawDescOnce.Do(func() { + file_planfile_proto_rawDescData = protoimpl.X.CompressGZIP(file_planfile_proto_rawDescData) + }) + return file_planfile_proto_rawDescData +} + +var file_planfile_proto_enumTypes = make([]protoimpl.EnumInfo, 4) +var file_planfile_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_planfile_proto_goTypes = []interface{}{ + (Mode)(0), // 0: tfplan.Mode + (Action)(0), // 1: tfplan.Action + (ResourceInstanceActionReason)(0), // 2: tfplan.ResourceInstanceActionReason + (ResourceInstanceChange_ResourceMode)(0), // 3: tfplan.ResourceInstanceChange.ResourceMode + (*Plan)(nil), // 4: tfplan.Plan + (*Backend)(nil), // 5: tfplan.Backend + (*Change)(nil), // 6: tfplan.Change + (*ResourceInstanceChange)(nil), // 7: tfplan.ResourceInstanceChange + (*OutputChange)(nil), // 8: tfplan.OutputChange + (*DynamicValue)(nil), // 9: tfplan.DynamicValue + (*Hash)(nil), // 10: tfplan.Hash + (*Path)(nil), // 11: tfplan.Path + nil, // 12: tfplan.Plan.VariablesEntry + nil, // 13: tfplan.Plan.ProviderHashesEntry + (*Path_Step)(nil), // 14: tfplan.Path.Step +} +var file_planfile_proto_depIdxs = []int32{ + 0, // 0: tfplan.Plan.ui_mode:type_name -> tfplan.Mode + 12, // 1: tfplan.Plan.variables:type_name -> tfplan.Plan.VariablesEntry + 7, // 2: tfplan.Plan.resource_changes:type_name -> tfplan.ResourceInstanceChange + 8, // 3: tfplan.Plan.output_changes:type_name -> tfplan.OutputChange + 13, // 4: tfplan.Plan.provider_hashes:type_name -> tfplan.Plan.ProviderHashesEntry + 5, // 5: tfplan.Plan.backend:type_name -> tfplan.Backend + 9, // 6: tfplan.Backend.config:type_name -> tfplan.DynamicValue + 1, // 7: tfplan.Change.action:type_name -> tfplan.Action + 9, // 8: tfplan.Change.values:type_name -> tfplan.DynamicValue + 11, // 9: tfplan.Change.before_sensitive_paths:type_name -> tfplan.Path + 11, // 10: tfplan.Change.after_sensitive_paths:type_name -> tfplan.Path + 3, // 11: tfplan.ResourceInstanceChange.mode:type_name -> tfplan.ResourceInstanceChange.ResourceMode + 6, // 12: tfplan.ResourceInstanceChange.change:type_name -> tfplan.Change + 11, // 13: tfplan.ResourceInstanceChange.required_replace:type_name -> tfplan.Path + 2, // 14: tfplan.ResourceInstanceChange.action_reason:type_name -> tfplan.ResourceInstanceActionReason + 6, // 15: tfplan.OutputChange.change:type_name -> tfplan.Change + 14, // 16: tfplan.Path.steps:type_name -> tfplan.Path.Step + 9, // 17: tfplan.Plan.VariablesEntry.value:type_name -> tfplan.DynamicValue + 10, // 18: tfplan.Plan.ProviderHashesEntry.value:type_name -> tfplan.Hash + 9, // 19: tfplan.Path.Step.element_key:type_name -> tfplan.DynamicValue + 20, // [20:20] is the sub-list for method output_type + 20, // [20:20] is the sub-list for method input_type + 20, // [20:20] is the sub-list for extension type_name + 20, // [20:20] is the sub-list for extension extendee + 0, // [0:20] is the sub-list for field type_name +} + +func init() { file_planfile_proto_init() } +func file_planfile_proto_init() { + if File_planfile_proto != nil { + return } - return "" -} - -func (m *Path_Step) GetElementKey() *DynamicValue { - if x, ok := m.GetSelector().(*Path_Step_ElementKey); ok { - return x.ElementKey + if !protoimpl.UnsafeEnabled { + file_planfile_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Plan); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_planfile_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Backend); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_planfile_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Change); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_planfile_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ResourceInstanceChange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_planfile_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OutputChange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_planfile_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DynamicValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_planfile_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Hash); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_planfile_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Path); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_planfile_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Path_Step); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } - return nil -} - -// XXX_OneofWrappers is for the internal use of the proto package. -func (*Path_Step) XXX_OneofWrappers() []interface{} { - return []interface{}{ + file_planfile_proto_msgTypes[3].OneofWrappers = []interface{}{ + (*ResourceInstanceChange_Str)(nil), + (*ResourceInstanceChange_Int)(nil), + } + file_planfile_proto_msgTypes[10].OneofWrappers = []interface{}{ (*Path_Step_AttributeName)(nil), (*Path_Step_ElementKey)(nil), } -} - -func init() { - proto.RegisterEnum("tfplan.Action", Action_name, Action_value) - proto.RegisterEnum("tfplan.ResourceInstanceChange_ResourceMode", ResourceInstanceChange_ResourceMode_name, ResourceInstanceChange_ResourceMode_value) - proto.RegisterType((*Plan)(nil), "tfplan.Plan") - proto.RegisterMapType((map[string]*Hash)(nil), "tfplan.Plan.ProviderHashesEntry") - proto.RegisterMapType((map[string]*DynamicValue)(nil), "tfplan.Plan.VariablesEntry") - proto.RegisterType((*Backend)(nil), "tfplan.Backend") - proto.RegisterType((*Change)(nil), "tfplan.Change") - proto.RegisterType((*ResourceInstanceChange)(nil), "tfplan.ResourceInstanceChange") - proto.RegisterType((*OutputChange)(nil), "tfplan.OutputChange") - proto.RegisterType((*DynamicValue)(nil), "tfplan.DynamicValue") - proto.RegisterType((*Hash)(nil), "tfplan.Hash") - proto.RegisterType((*Path)(nil), "tfplan.Path") - proto.RegisterType((*Path_Step)(nil), "tfplan.Path.Step") -} - -func init() { proto.RegisterFile("planfile.proto", fileDescriptor_02431083a6706c5b) } - -var fileDescriptor_02431083a6706c5b = []byte{ - // 893 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x55, 0xe1, 0x6e, 0xe3, 0x44, - 0x10, 0xae, 0x63, 0xc7, 0x49, 0x26, 0xa9, 0x9b, 0x5b, 0x50, 0x65, 0x95, 0xd3, 0x11, 0x2c, 0xc1, - 0x85, 0x3b, 0x94, 0x4a, 0x41, 0x50, 0x0e, 0x7e, 0xa0, 0xf6, 0x1a, 0x29, 0xd5, 0x41, 0x1b, 0x2d, - 0xa5, 0x3f, 0xf8, 0x81, 0xb5, 0xb1, 0xa7, 0x89, 0x55, 0xc7, 0x36, 0xbb, 0x9b, 0xa0, 0x3c, 0x10, - 0x0f, 0xc1, 0x4b, 0xf0, 0x4c, 0x68, 0x77, 0x6d, 0x27, 0x95, 0x7a, 0xfd, 0x95, 0x9d, 0x6f, 0x66, - 0x3e, 0xcf, 0x7e, 0x33, 0xb3, 0x01, 0xaf, 0x48, 0x59, 0x76, 0x9f, 0xa4, 0x38, 0x2a, 0x78, 0x2e, - 0x73, 0xe2, 0xca, 0x7b, 0x85, 0x04, 0xff, 0x39, 0xe0, 0xcc, 0x52, 0x96, 0x11, 0x1f, 0x5a, 0x1b, - 0xe4, 0x22, 0xc9, 0x33, 0xdf, 0x1a, 0x58, 0x43, 0x87, 0x56, 0x26, 0x79, 0x07, 0x9d, 0x0d, 0xe3, - 0x09, 0x9b, 0xa7, 0x28, 0xfc, 0xc6, 0xc0, 0x1e, 0x76, 0xc7, 0x9f, 0x8d, 0x4c, 0xfa, 0x48, 0xa5, - 0x8e, 0xee, 0x2a, 0xef, 0x24, 0x93, 0x7c, 0x4b, 0x77, 0xd1, 0xe4, 0x0a, 0xfa, 0x1c, 0x45, 0xbe, - 0xe6, 0x11, 0x86, 0xd1, 0x92, 0x65, 0x0b, 0x14, 0xbe, 0xad, 0x19, 0x5e, 0x55, 0x0c, 0xb4, 0xf4, - 0x5f, 0x65, 0x42, 0xb2, 0x2c, 0xc2, 0xf7, 0x3a, 0x8c, 0x1e, 0x55, 0x79, 0xc6, 0x16, 0xe4, 0x27, - 0xf0, 0xf2, 0xb5, 0x2c, 0xd6, 0xb2, 0x26, 0x72, 0x34, 0xd1, 0xa7, 0x15, 0xd1, 0x8d, 0xf6, 0x96, - 0xe9, 0x87, 0xf9, 0x9e, 0x25, 0xc8, 0x17, 0xd0, 0x93, 0x8c, 0x2f, 0x50, 0x86, 0x2c, 0x8e, 0xb9, - 0xf0, 0x9b, 0x03, 0x7b, 0xd8, 0xa1, 0x5d, 0x83, 0x9d, 0x2b, 0x88, 0xbc, 0x85, 0x17, 0x12, 0x39, - 0x67, 0xf7, 0x39, 0x5f, 0x85, 0x95, 0x12, 0xde, 0xc0, 0x1a, 0x76, 0x68, 0xbf, 0x76, 0xdc, 0x95, - 0x92, 0x5c, 0xc1, 0x51, 0xc1, 0xf3, 0x4d, 0x12, 0x23, 0x0f, 0x97, 0x4c, 0x2c, 0x51, 0xf8, 0x47, - 0xba, 0x9a, 0xc1, 0x23, 0x61, 0x66, 0x65, 0xcc, 0x54, 0x87, 0x18, 0x75, 0xbc, 0xe2, 0x11, 0x48, - 0xbe, 0x86, 0xd6, 0x9c, 0x45, 0x0f, 0x98, 0xc5, 0xfe, 0xe1, 0xc0, 0x1a, 0x76, 0xc7, 0x47, 0x15, - 0xc5, 0x85, 0x81, 0x69, 0xe5, 0x3f, 0xa1, 0xe0, 0x3d, 0x96, 0x9a, 0xf4, 0xc1, 0x7e, 0xc0, 0xad, - 0x6e, 0x58, 0x87, 0xaa, 0x23, 0x79, 0x03, 0xcd, 0x0d, 0x4b, 0xd7, 0xe8, 0x37, 0x34, 0x59, 0xad, - 0xce, 0xe5, 0x36, 0x63, 0xab, 0x24, 0xba, 0x53, 0x3e, 0x6a, 0x42, 0x7e, 0x6c, 0xfc, 0x60, 0x9d, - 0xdc, 0xc0, 0x27, 0x4f, 0x54, 0xf9, 0x04, 0x71, 0xf0, 0x98, 0xb8, 0x57, 0x11, 0xab, 0xac, 0x3d, - 0xc2, 0x20, 0x81, 0x56, 0x59, 0x38, 0x21, 0xe0, 0xc8, 0x6d, 0x81, 0x25, 0x8b, 0x3e, 0x93, 0x6f, - 0xc0, 0x8d, 0xf2, 0xec, 0x3e, 0x59, 0x3c, 0x5b, 0x60, 0x19, 0x43, 0x5e, 0x42, 0xe7, 0xef, 0x9c, - 0x3f, 0x88, 0x82, 0x45, 0xe8, 0xdb, 0x9a, 0x66, 0x07, 0x04, 0x7f, 0x82, 0x6b, 0x1a, 0x4c, 0xbe, - 0x02, 0x97, 0x45, 0xb2, 0x9a, 0x5d, 0x6f, 0xec, 0x55, 0xac, 0xe7, 0x1a, 0xa5, 0xa5, 0x57, 0x7d, - 0x5d, 0x57, 0x5a, 0xcd, 0xf1, 0x47, 0xbe, 0x6e, 0x62, 0x82, 0x7f, 0x6d, 0x38, 0x7e, 0x7a, 0x3c, - 0xc9, 0xe7, 0xd0, 0x5d, 0xe5, 0xf1, 0x3a, 0xc5, 0xb0, 0x60, 0x72, 0x59, 0xde, 0x10, 0x0c, 0x34, - 0x63, 0x72, 0x49, 0x7e, 0x06, 0x67, 0x95, 0xc7, 0x46, 0x2d, 0x6f, 0xfc, 0xf6, 0xf9, 0x69, 0xaf, - 0xe1, 0x5f, 0xf3, 0x18, 0xa9, 0x4e, 0xac, 0xc5, 0xb3, 0xf7, 0xc4, 0x23, 0xe0, 0x64, 0x6c, 0x85, - 0xbe, 0x63, 0x30, 0x75, 0x26, 0x04, 0x6c, 0x21, 0xb9, 0xdf, 0x54, 0xd0, 0xf4, 0x80, 0x2a, 0x43, - 0x61, 0x49, 0x26, 0x7d, 0x77, 0x60, 0x0d, 0x6d, 0x85, 0x25, 0x99, 0x54, 0x15, 0xc7, 0x58, 0xe4, - 0x02, 0xe3, 0x50, 0x75, 0xb6, 0x65, 0x2a, 0x2e, 0xa1, 0x0f, 0xb8, 0x25, 0x27, 0xd0, 0xae, 0x46, - 0xd3, 0x6f, 0x6b, 0x6f, 0x6d, 0x2b, 0x7d, 0xcd, 0xd6, 0xf9, 0x1d, 0xdd, 0xb5, 0x5a, 0xdf, 0x72, - 0xdd, 0x4a, 0xaf, 0x7a, 0x44, 0x0a, 0x9e, 0x6c, 0x98, 0x44, 0x1f, 0x06, 0xd6, 0xb0, 0x47, 0x2b, - 0x93, 0x9c, 0xa9, 0x97, 0xe0, 0xaf, 0x75, 0xc2, 0x31, 0x0e, 0x39, 0x16, 0xa9, 0x6a, 0x68, 0x57, - 0xf7, 0xa0, 0x9e, 0x24, 0xa5, 0x9b, 0xda, 0x7b, 0x13, 0x45, 0x4d, 0x50, 0xf0, 0x25, 0xf4, 0xf6, - 0xd5, 0x21, 0x5d, 0x68, 0xad, 0x58, 0xc6, 0x16, 0x18, 0xf7, 0x0f, 0x48, 0x1b, 0x9c, 0x98, 0x49, - 0xd6, 0xb7, 0x2e, 0x3c, 0xe8, 0x25, 0xa5, 0xa6, 0xea, 0x7e, 0xc1, 0x12, 0x7a, 0xfb, 0x0f, 0x42, - 0x2d, 0x9d, 0xb5, 0x27, 0xdd, 0xee, 0x56, 0x8d, 0x67, 0x6f, 0xf5, 0x12, 0x3a, 0x02, 0x33, 0x91, - 0xc8, 0x64, 0x63, 0xfa, 0xd1, 0xa6, 0x3b, 0x20, 0x18, 0x42, 0x6f, 0x7f, 0x7a, 0x94, 0x06, 0x2b, - 0xb1, 0x28, 0x58, 0xf4, 0xa0, 0x3f, 0xd6, 0xa3, 0x95, 0x19, 0xbc, 0x02, 0x47, 0x6d, 0x0b, 0x39, - 0x06, 0x57, 0x2c, 0xd9, 0xf8, 0xbb, 0xef, 0xcb, 0x80, 0xd2, 0x0a, 0xfe, 0xb1, 0xc0, 0xd1, 0xc3, - 0xf3, 0x1a, 0x9a, 0x42, 0x62, 0x21, 0x7c, 0x4b, 0x2b, 0xf4, 0x62, 0x5f, 0xa1, 0xd1, 0x6f, 0x12, - 0x0b, 0x6a, 0xfc, 0x27, 0x12, 0x1c, 0x65, 0x92, 0xd7, 0xe0, 0x31, 0x29, 0x79, 0x32, 0x5f, 0x4b, - 0x0c, 0x77, 0xf7, 0x9c, 0x1e, 0xd0, 0xc3, 0x1a, 0xbf, 0x56, 0x57, 0x3e, 0x83, 0x2e, 0xa6, 0xb8, - 0xc2, 0x4c, 0xea, 0x29, 0x78, 0x66, 0x07, 0xa7, 0x07, 0x14, 0xca, 0xd0, 0x0f, 0xb8, 0xbd, 0x00, - 0x68, 0x0b, 0x4c, 0x31, 0x92, 0x39, 0x7f, 0x53, 0x80, 0x6b, 0xf6, 0x4a, 0xe9, 0x7f, 0x7d, 0x73, - 0x33, 0xeb, 0x1f, 0x10, 0x00, 0xf7, 0x3d, 0x9d, 0x9c, 0xdf, 0x4e, 0xfa, 0x96, 0x42, 0xe9, 0xe4, - 0xfc, 0xb2, 0xdf, 0x50, 0xe8, 0xef, 0xb3, 0x4b, 0x85, 0xda, 0xea, 0x7c, 0x39, 0xf9, 0x65, 0x72, - 0x3b, 0xe9, 0x37, 0xc9, 0x31, 0x10, 0x73, 0x0e, 0x6f, 0xa7, 0x93, 0xeb, 0xb0, 0xcc, 0x74, 0x15, - 0x6e, 0xce, 0x06, 0x2f, 0xe3, 0x5b, 0x17, 0xef, 0xfe, 0x38, 0x5b, 0x24, 0x72, 0xb9, 0x9e, 0x8f, - 0xa2, 0x7c, 0x75, 0xaa, 0x5e, 0xdc, 0x24, 0xca, 0x79, 0x71, 0x5a, 0x3f, 0xcc, 0xa7, 0xaa, 0x7e, - 0x71, 0x9a, 0x64, 0x12, 0x79, 0xc6, 0x52, 0x6d, 0xea, 0x3f, 0xba, 0xb9, 0xab, 0x7f, 0xbe, 0xfd, - 0x3f, 0x00, 0x00, 0xff, 0xff, 0x30, 0x3e, 0x4e, 0x33, 0x01, 0x07, 0x00, 0x00, + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_planfile_proto_rawDesc, + NumEnums: 4, + NumMessages: 11, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_planfile_proto_goTypes, + DependencyIndexes: file_planfile_proto_depIdxs, + EnumInfos: file_planfile_proto_enumTypes, + MessageInfos: file_planfile_proto_msgTypes, + }.Build() + File_planfile_proto = out.File + file_planfile_proto_rawDesc = nil + file_planfile_proto_goTypes = nil + file_planfile_proto_depIdxs = nil } diff --git a/vendor/github.com/hashicorp/terraform/plans/internal/planproto/planfile.proto b/vendor/github.com/hashicorp/terraform/plans/internal/planproto/planfile.proto index f45c3005..e1645743 100644 --- a/vendor/github.com/hashicorp/terraform/plans/internal/planproto/planfile.proto +++ b/vendor/github.com/hashicorp/terraform/plans/internal/planproto/planfile.proto @@ -17,6 +17,13 @@ message Plan { // the file for another message- or field-specific reason. uint64 version = 1; + // The mode that was active when this plan was created. + // + // This is saved only for UI purposes, so that Terraform can tailor its + // rendering of the plan depending on the mode. This must never be used to + // make decisions in Terraform Core during the applying of a plan. + Mode ui_mode = 17; + // The variables that were set when creating the plan. Each value is // a msgpack serialization of an HCL value. map variables = 2; @@ -37,6 +44,12 @@ message Plan { // configuration. repeated string target_addrs = 5; + // An unordered set of force-replace addresses to include when applying. + // This must match the set of addresses that was used when creating the + // plan, or else applying the plan will fail when it reaches a different + // conclusion about what action a particular resource instance needs. + repeated string force_replace_addrs = 16; + // The version string for the Terraform binary that created this plan. string terraform_version = 14; @@ -49,6 +62,13 @@ message Plan { Backend backend = 13; } +// Mode describes the planning mode that created the plan. +enum Mode { + NORMAL = 0; + DESTROY = 1; + REFRESH_ONLY = 2; +} + // Backend is a description of backend configuration and other related settings. message Backend { string type = 1; @@ -85,6 +105,27 @@ message Change { // respectively. // - For no-op, one value is provided that is left unmodified by this non-change. repeated DynamicValue values = 2; + + // An unordered set of paths into the old value which are marked as + // sensitive. Values at these paths should be obscured in human-readable + // output. This set is always empty for create. + repeated Path before_sensitive_paths = 3; + + // An unordered set of paths into the new value which are marked as + // sensitive. Values at these paths should be obscured in human-readable + // output. This set is always empty for delete. + repeated Path after_sensitive_paths = 4; +} + +// ResourceInstanceActionReason sometimes provides some additional user-facing +// context for why a particular action was chosen for a resource instance. +// This is for user feedback only and never used to drive behavior during the +// subsequent apply step. +enum ResourceInstanceActionReason { + NONE = 0; + REPLACE_BECAUSE_TAINTED = 1; + REPLACE_BY_REQUEST = 2; + REPLACE_BECAUSE_CANNOT_UPDATE = 3; } message ResourceInstanceChange { @@ -142,6 +183,11 @@ message ResourceInstanceChange { // "replace" rather than "update". Empty for any action other than // "replace". repeated Path required_replace = 11; + + // Optional extra user-oriented context for why change.Action was chosen. + // This is for user feedback only and never used to drive behavior during + // apply. + ResourceInstanceActionReason action_reason = 12; } message OutputChange { diff --git a/vendor/github.com/hashicorp/terraform/plans/mode.go b/vendor/github.com/hashicorp/terraform/plans/mode.go new file mode 100644 index 00000000..7e78ea85 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plans/mode.go @@ -0,0 +1,31 @@ +package plans + +// Mode represents the various mutually-exclusive modes for creating a plan. +type Mode rune + +//go:generate go run golang.org/x/tools/cmd/stringer -type Mode + +const ( + // NormalMode is the default planning mode, which aims to synchronize the + // prior state with remote objects and plan a set of actions intended to + // make those remote objects better match the current configuration. + NormalMode Mode = 0 + + // DestroyMode is a special planning mode for situations where the goal + // is to destroy all remote objects that are bound to instances in the + // prior state, even if the configuration for those instances is still + // present. + // + // This mode corresponds with the "-destroy" option to "terraform plan", + // and with the plan created by the "terraform destroy" command. + DestroyMode Mode = 'D' + + // RefreshOnlyMode is a special planning mode which only performs the + // synchronization of prior state with remote objects, and skips any + // effort to generate any change actions for resource instances even if + // the configuration has changed relative to the state. + // + // This mode corresponds with the "-refresh-only" option to + // "terraform plan". + RefreshOnlyMode Mode = 'R' +) diff --git a/vendor/github.com/hashicorp/terraform/plans/mode_string.go b/vendor/github.com/hashicorp/terraform/plans/mode_string.go new file mode 100644 index 00000000..f1757e8f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plans/mode_string.go @@ -0,0 +1,33 @@ +// Code generated by "stringer -type Mode"; DO NOT EDIT. + +package plans + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[NormalMode-0] + _ = x[DestroyMode-68] + _ = x[RefreshOnlyMode-82] +} + +const ( + _Mode_name_0 = "NormalMode" + _Mode_name_1 = "DestroyMode" + _Mode_name_2 = "RefreshOnlyMode" +) + +func (i Mode) String() string { + switch { + case i == 0: + return _Mode_name_0 + case i == 68: + return _Mode_name_1 + case i == 82: + return _Mode_name_2 + default: + return "Mode(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/vendor/github.com/hashicorp/terraform/plans/objchange/all_null.go b/vendor/github.com/hashicorp/terraform/plans/objchange/all_null.go index 18a7e99a..93bbfdf5 100644 --- a/vendor/github.com/hashicorp/terraform/plans/objchange/all_null.go +++ b/vendor/github.com/hashicorp/terraform/plans/objchange/all_null.go @@ -5,14 +5,29 @@ import ( "github.com/zclconf/go-cty/cty" ) -// AllAttributesNull constructs a non-null cty.Value of the object type implied +// AllBlockAttributesNull constructs a non-null cty.Value of the object type implied // by the given schema that has all of its leaf attributes set to null and all // of its nested block collections set to zero-length. // // This simulates what would result from decoding an empty configuration block // with the given schema, except that it does not produce errors -func AllAttributesNull(schema *configschema.Block) cty.Value { +func AllBlockAttributesNull(schema *configschema.Block) cty.Value { // "All attributes null" happens to be the definition of EmptyValue for // a Block, so we can just delegate to that. return schema.EmptyValue() } + +// AllAttributesNull returns a cty.Value of the object type implied by the given +// attriubutes that has all of its leaf attributes set to null. +func AllAttributesNull(attrs map[string]*configschema.Attribute) cty.Value { + newAttrs := make(map[string]cty.Value, len(attrs)) + + for name, attr := range attrs { + if attr.NestedType != nil { + newAttrs[name] = AllAttributesNull(attr.NestedType.Attributes) + } else { + newAttrs[name] = cty.NullVal(attr.Type) + } + } + return cty.ObjectVal(newAttrs) +} diff --git a/vendor/github.com/hashicorp/terraform/plans/objchange/compatible.go b/vendor/github.com/hashicorp/terraform/plans/objchange/compatible.go index 6d92fca4..e4ece3f9 100644 --- a/vendor/github.com/hashicorp/terraform/plans/objchange/compatible.go +++ b/vendor/github.com/hashicorp/terraform/plans/objchange/compatible.go @@ -75,20 +75,12 @@ func assertObjectCompatible(schema *configschema.Block, planned, actual cty.Valu plannedV, _ := planned.GetAttr(name).Unmark() actualV, _ := actual.GetAttr(name).Unmark() - // As a special case, if there were any blocks whose leaf attributes - // are all unknown then we assume (possibly incorrectly) that the - // HCL dynamic block extension is in use with an unknown for_each - // argument, and so we will do looser validation here that allows - // for those blocks to have expanded into a different number of blocks - // if the for_each value is now known. - maybeUnknownBlocks := couldHaveUnknownBlockPlaceholder(plannedV, blockS, false) - path := append(path, cty.GetAttrStep{Name: name}) switch blockS.Nesting { case configschema.NestingSingle, configschema.NestingGroup: // If an unknown block placeholder was present then the placeholder // may have expanded out into zero blocks, which is okay. - if maybeUnknownBlocks && actualV.IsNull() { + if !plannedV.IsKnown() && actualV.IsNull() { continue } moreErrs := assertObjectCompatible(&blockS.Block, plannedV, actualV, path) @@ -102,14 +94,6 @@ func assertObjectCompatible(schema *configschema.Block, planned, actual cty.Valu continue } - if maybeUnknownBlocks { - // When unknown blocks are present the final blocks may be - // at different indices than the planned blocks, so unfortunately - // we can't do our usual checks in this case without generating - // false negatives. - continue - } - plannedL := plannedV.LengthInt() actualL := actualV.LengthInt() if plannedL != actualL { @@ -144,7 +128,7 @@ func assertObjectCompatible(schema *configschema.Block, planned, actual cty.Valu moreErrs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.GetAttrStep{Name: k})) errs = append(errs, moreErrs...) } - if !maybeUnknownBlocks { // new blocks may appear if unknown blocks were present in the plan + if plannedV.IsKnown() { // new blocks may appear if unknown blocks were present in the plan for k := range actualAtys { if _, ok := plannedAtys[k]; !ok { errs = append(errs, path.NewErrorf("new block key %q has appeared", k)) @@ -158,7 +142,7 @@ func assertObjectCompatible(schema *configschema.Block, planned, actual cty.Valu } plannedL := plannedV.LengthInt() actualL := actualV.LengthInt() - if plannedL != actualL && !maybeUnknownBlocks { // new blocks may appear if unknown blocks were persent in the plan + if plannedL != actualL && plannedV.IsKnown() { // new blocks may appear if unknown blocks were persent in the plan errs = append(errs, path.NewErrorf("block count changed from %d to %d", plannedL, actualL)) continue } @@ -177,7 +161,7 @@ func assertObjectCompatible(schema *configschema.Block, planned, actual cty.Valu continue } - if maybeUnknownBlocks { + if !plannedV.IsKnown() { // When unknown blocks are present the final number of blocks // may be different, either because the unknown set values // become equal and are collapsed, or the count is unknown due @@ -219,7 +203,7 @@ func assertValueCompatible(planned, actual cty.Value, path cty.Path) []error { // Anything goes, then return errs } - if problems := planned.Type().TestConformance(actual.Type()); len(problems) > 0 { + if problems := actual.Type().TestConformance(planned.Type()); len(problems) > 0 { errs = append(errs, path.NewErrorf("wrong final value type: %s", convert.MismatchMessage(actual.Type(), planned.Type()))) // If the types don't match then we can't do any other comparisons, // so we bail early. @@ -328,92 +312,6 @@ func indexStrForErrors(v cty.Value) string { } } -// couldHaveUnknownBlockPlaceholder is a heuristic that recognizes how the -// HCL dynamic block extension behaves when it's asked to expand a block whose -// for_each argument is unknown. In such cases, it generates a single placeholder -// block with all leaf attribute values unknown, and once the for_each -// expression becomes known the placeholder may be replaced with any number -// of blocks, so object compatibility checks would need to be more liberal. -// -// Set "nested" if testing a block that is nested inside a candidate block -// placeholder; this changes the interpretation of there being no blocks of -// a type to allow for there being zero nested blocks. -func couldHaveUnknownBlockPlaceholder(v cty.Value, blockS *configschema.NestedBlock, nested bool) bool { - switch blockS.Nesting { - case configschema.NestingSingle, configschema.NestingGroup: - if nested && v.IsNull() { - return true // for nested blocks, a single block being unset doesn't disqualify from being an unknown block placeholder - } - return couldBeUnknownBlockPlaceholderElement(v, blockS) - default: - // These situations should be impossible for correct providers, but - // we permit the legacy SDK to produce some incorrect outcomes - // for compatibility with its existing logic, and so we must be - // tolerant here. - if !v.IsKnown() { - return true - } - if v.IsNull() { - return false // treated as if the list were empty, so we would see zero iterations below - } - - // For all other nesting modes, our value should be something iterable. - for it := v.ElementIterator(); it.Next(); { - _, ev := it.Element() - if couldBeUnknownBlockPlaceholderElement(ev, blockS) { - return true - } - } - - // Our default changes depending on whether we're testing the candidate - // block itself or something nested inside of it: zero blocks of a type - // can never contain a dynamic block placeholder, but a dynamic block - // placeholder might contain zero blocks of one of its own nested block - // types, if none were set in the config at all. - return nested - } -} - -func couldBeUnknownBlockPlaceholderElement(v cty.Value, schema *configschema.NestedBlock) bool { - if v.IsNull() { - return false // null value can never be a placeholder element - } - if !v.IsKnown() { - return true // this should never happen for well-behaved providers, but can happen with the legacy SDK opt-outs - } - for name := range schema.Attributes { - av := v.GetAttr(name) - - // Unknown block placeholders contain only unknown or null attribute - // values, depending on whether or not a particular attribute was set - // explicitly inside the content block. Note that this is imprecise: - // non-placeholders can also match this, so this function can generate - // false positives. - if av.IsKnown() && !av.IsNull() { - - // FIXME: only required for the legacy SDK, but we don't have a - // separate codepath to switch the comparisons, and we still want - // the rest of the checks from AssertObjectCompatible to apply. - // - // The legacy SDK cannot handle missing strings from set elements, - // and will insert an empty string into the planned value. - // Skipping these treats them as null values in this case, - // preventing false alerts from AssertObjectCompatible. - if schema.Nesting == configschema.NestingSet && av.Type() == cty.String && av.AsString() == "" { - continue - } - - return false - } - } - for name, blockS := range schema.BlockTypes { - if !couldHaveUnknownBlockPlaceholder(v.GetAttr(name), blockS, true) { - return false - } - } - return true -} - // assertSetValuesCompatible checks that each of the elements in a can // be correlated with at least one equivalent element in b and vice-versa, // using the given correlation function. diff --git a/vendor/github.com/hashicorp/terraform/plans/objchange/compatible_test.go b/vendor/github.com/hashicorp/terraform/plans/objchange/compatible_test.go index 9a692444..942a477e 100644 --- a/vendor/github.com/hashicorp/terraform/plans/objchange/compatible_test.go +++ b/vendor/github.com/hashicorp/terraform/plans/objchange/compatible_test.go @@ -30,10 +30,6 @@ func TestAssertObjectCompatible(t *testing.T) { "foo": cty.StringVal("bar"), "bar": cty.NullVal(cty.String), // simulating the situation where bar isn't set in the config at all }) - fooBarBlockDynamicPlaceholder := cty.ObjectVal(map[string]cty.Value{ - "foo": cty.UnknownVal(cty.String), - "bar": cty.NullVal(cty.String), // simulating the situation where bar isn't set in the config at all - }) tests := []struct { Schema *configschema.Block @@ -194,6 +190,49 @@ func TestAssertObjectCompatible(t *testing.T) { `.name: inconsistent values for sensitive attribute`, }, }, + { + // This tests the codepath that leads to couldHaveUnknownBlockPlaceholder, + // where a set may be sensitive and need to be unmarked before it + // is iterated upon + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "configuration": { + Nesting: configschema.NestingList, + Block: configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "sensitive_fields": { + Nesting: configschema.NestingSet, + Block: schemaWithFoo, + }, + }, + }, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "configuration": cty.TupleVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "sensitive_fields": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("secret"), + }), + }).Mark("sensitive"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "configuration": cty.TupleVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "sensitive_fields": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("secret"), + }), + }).Mark("sensitive"), + }), + }), + }), + nil, + }, { &configschema.Block{ Attributes: map[string]*configschema.Attribute{ @@ -217,6 +256,28 @@ func TestAssertObjectCompatible(t *testing.T) { }), []string{}, }, + { + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "obj": { + Type: cty.Object(map[string]cty.Type{ + "stuff": cty.DynamicPseudoType, + }), + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "obj": cty.ObjectVal(map[string]cty.Value{ + "stuff": cty.DynamicVal, + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "obj": cty.ObjectVal(map[string]cty.Value{ + "stuff": cty.NumberIntVal(3), + }), + }), + []string{}, + }, { &configschema.Block{ Attributes: map[string]*configschema.Attribute{ @@ -854,13 +915,11 @@ func TestAssertObjectCompatible(t *testing.T) { }, }, }, - cty.ObjectVal(map[string]cty.Value{ - "key": cty.ObjectVal(map[string]cty.Value{ - // One wholly unknown block is what "dynamic" blocks - // generate when the for_each expression is unknown. - "foo": cty.UnknownVal(cty.String), + cty.UnknownVal(cty.Object(map[string]cty.Type{ + "key": cty.Object(map[string]cty.Type{ + "foo": cty.String, }), - }), + })), cty.ObjectVal(map[string]cty.Value{ "key": cty.NullVal(cty.Object(map[string]cty.Type{ "foo": cty.String, @@ -946,11 +1005,9 @@ func TestAssertObjectCompatible(t *testing.T) { }, }, }, - cty.ObjectVal(map[string]cty.Value{ - "key": cty.ListVal([]cty.Value{ - fooBarBlockDynamicPlaceholder, // the presence of this disables some of our checks - }), - }), + cty.UnknownVal(cty.Object(map[string]cty.Type{ + "key": cty.List(fooBarBlockValue.Type()), + })), cty.ObjectVal(map[string]cty.Value{ "key": cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ @@ -961,35 +1018,7 @@ func TestAssertObjectCompatible(t *testing.T) { }), }), }), - nil, // a single block whose attrs are all unknown is allowed to expand into multiple, because that's how dynamic blocks behave when for_each is unknown - }, - { - &configschema.Block{ - BlockTypes: map[string]*configschema.NestedBlock{ - "key": { - Nesting: configschema.NestingList, - Block: schemaWithFooBar, - }, - }, - }, - cty.ObjectVal(map[string]cty.Value{ - "key": cty.ListVal([]cty.Value{ - fooBarBlockValue, // the presence of one static block does not negate that the following element looks like a dynamic placeholder - fooBarBlockDynamicPlaceholder, // the presence of this disables some of our checks - }), - }), - cty.ObjectVal(map[string]cty.Value{ - "key": cty.ListVal([]cty.Value{ - fooBlockValue, - cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("hello"), - }), - cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("world"), - }), - }), - }), - nil, // as above, the presence of a block whose attrs are all unknown indicates dynamic block expansion, so our usual count checks don't apply + nil, // an unknown block is allowed to expand into multiple, because that's how dynamic blocks behave when for_each is unknown }, { &configschema.Block{ @@ -1130,14 +1159,11 @@ func TestAssertObjectCompatible(t *testing.T) { }, }, cty.ObjectVal(map[string]cty.Value{ - "block": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "foo": cty.UnknownVal(cty.String), - }), - cty.ObjectVal(map[string]cty.Value{ - "foo": cty.UnknownVal(cty.String), + "block": cty.UnknownVal(cty.Set( + cty.Object(map[string]cty.Type{ + "foo": cty.String, }), - }), + )), }), cty.ObjectVal(map[string]cty.Value{ "block": cty.SetVal([]cty.Value{ @@ -1156,47 +1182,6 @@ func TestAssertObjectCompatible(t *testing.T) { // indicates this may be a dynamic block, and the length is unknown nil, }, - { - &configschema.Block{ - BlockTypes: map[string]*configschema.NestedBlock{ - "block": { - Nesting: configschema.NestingSet, - Block: schemaWithFooBar, - }, - }, - }, - // The legacy SDK cannot handle missing strings in sets, and will - // insert empty strings to the planned value. Empty strings should - // be handled as nulls, and this object should represent a possible - // dynamic block. - cty.ObjectVal(map[string]cty.Value{ - "block": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "foo": cty.UnknownVal(cty.String), - "bar": cty.StringVal(""), - }), - }), - }), - cty.ObjectVal(map[string]cty.Value{ - "block": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("hello"), - "bar": cty.StringVal(""), - }), - cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("world"), - "bar": cty.StringVal(""), - }), - cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("nope"), - "bar": cty.StringVal(""), - }), - }), - }), - // there is no error here, because the presence of unknowns - // indicates this may be a dynamic block, and the length is unknown - nil, - }, { &configschema.Block{ BlockTypes: map[string]*configschema.NestedBlock{ @@ -1270,11 +1255,7 @@ func TestAssertObjectCompatible(t *testing.T) { }, }, cty.ObjectVal(map[string]cty.Value{ - "block": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "foo": cty.UnknownVal(cty.String), - }), - }), + "block": cty.UnknownVal(cty.Set(fooBlockValue.Type())), }), cty.ObjectVal(map[string]cty.Value{ "block": cty.SetVal([]cty.Value{ @@ -1299,11 +1280,7 @@ func TestAssertObjectCompatible(t *testing.T) { }, }, cty.ObjectVal(map[string]cty.Value{ - "block2": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "foo": cty.UnknownVal(cty.String), - }), - }), + "block2": cty.UnknownVal(cty.Set(fooBlockValue.Type())), }), cty.ObjectVal(map[string]cty.Value{ "block2": cty.SetValEmpty(cty.Object(map[string]cty.Type{ @@ -1341,37 +1318,6 @@ func TestAssertObjectCompatible(t *testing.T) { }), nil, }, - { - &configschema.Block{ - BlockTypes: map[string]*configschema.NestedBlock{ - "block": { - Nesting: configschema.NestingSet, - Block: schemaWithFooBar, - }, - }, - }, - cty.ObjectVal(map[string]cty.Value{ - "block": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "foo": cty.UnknownVal(cty.String), - "bar": cty.NullVal(cty.String), - }), - }), - }), - cty.ObjectVal(map[string]cty.Value{ - "block": cty.SetVal([]cty.Value{ - cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("a"), - "bar": cty.StringVal(""), - }), - cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("b"), - "bar": cty.StringVal(""), - }), - }), - }), - nil, - }, { &configschema.Block{ BlockTypes: map[string]*configschema.NestedBlock{ diff --git a/vendor/github.com/hashicorp/terraform/plans/objchange/objchange.go b/vendor/github.com/hashicorp/terraform/plans/objchange/objchange.go index ab16f68a..6bfc06e2 100644 --- a/vendor/github.com/hashicorp/terraform/plans/objchange/objchange.go +++ b/vendor/github.com/hashicorp/terraform/plans/objchange/objchange.go @@ -8,7 +8,7 @@ import ( "github.com/hashicorp/terraform/configs/configschema" ) -// ProposedNewObject constructs a proposed new object value by combining the +// ProposedNew constructs a proposed new object value by combining the // computed attribute values from "prior" with the configured attribute values // from "config". // @@ -24,7 +24,7 @@ import ( // heuristic based on matching non-computed attribute values and so it may // produce strange results with more "extreme" cases, such as a nested set // block where _all_ attributes are computed. -func ProposedNewObject(schema *configschema.Block, prior, config cty.Value) cty.Value { +func ProposedNew(schema *configschema.Block, prior, config cty.Value) cty.Value { // If the config and prior are both null, return early here before // populating the prior block. The prevents non-null blocks from appearing // the proposed state value. @@ -37,12 +37,12 @@ func ProposedNewObject(schema *configschema.Block, prior, config cty.Value) cty. // similar to the result of decoding an empty configuration block, // which simplifies our handling of the top-level attributes/blocks // below by giving us one non-null level of object to pull values from. - prior = AllAttributesNull(schema) + prior = AllBlockAttributesNull(schema) } - return proposedNewObject(schema, prior, config) + return proposedNew(schema, prior, config) } -// PlannedDataResourceObject is similar to ProposedNewObject but tailored for +// PlannedDataResourceObject is similar to proposedNewBlock but tailored for // planning data resources in particular. Specifically, it replaces the values // of any Computed attributes not set in the configuration with an unknown // value, which serves as a placeholder for a value to be filled in by the @@ -51,35 +51,218 @@ func ProposedNewObject(schema *configschema.Block, prior, config cty.Value) cty. // Data resources are different because the planning of them is handled // entirely within Terraform Core and not subject to customization by the // provider. This function is, in effect, producing an equivalent result to -// passing the ProposedNewObject result into a provider's PlanResourceChange +// passing the proposedNewBlock result into a provider's PlanResourceChange // function, assuming a fixed implementation of PlanResourceChange that just // fills in unknown values as needed. func PlannedDataResourceObject(schema *configschema.Block, config cty.Value) cty.Value { - // Our trick here is to run the ProposedNewObject logic with an + // Our trick here is to run the proposedNewBlock logic with an // entirely-unknown prior value. Because of cty's unknown short-circuit // behavior, any operation on prior returns another unknown, and so // unknown values propagate into all of the parts of the resulting value // that would normally be filled in by preserving the prior state. prior := cty.UnknownVal(schema.ImpliedType()) - return proposedNewObject(schema, prior, config) + return proposedNew(schema, prior, config) } -func proposedNewObject(schema *configschema.Block, prior, config cty.Value) cty.Value { +func proposedNew(schema *configschema.Block, prior, config cty.Value) cty.Value { if config.IsNull() || !config.IsKnown() { // This is a weird situation, but we'll allow it anyway to free // callers from needing to specifically check for these cases. return prior } if (!prior.Type().IsObjectType()) || (!config.Type().IsObjectType()) { - panic("ProposedNewObject only supports object-typed values") + panic("ProposedNew only supports object-typed values") } // From this point onwards, we can assume that both values are non-null // object types, and that the config value itself is known (though it // may contain nested values that are unknown.) + newAttrs := proposedNewAttributes(schema.Attributes, prior, config) - newAttrs := map[string]cty.Value{} - for name, attr := range schema.Attributes { + // Merging nested blocks is a little more complex, since we need to + // correlate blocks between both objects and then recursively propose + // a new object for each. The correlation logic depends on the nesting + // mode for each block type. + for name, blockType := range schema.BlockTypes { + priorV := prior.GetAttr(name) + configV := config.GetAttr(name) + newAttrs[name] = proposedNewNestedBlock(blockType, priorV, configV) + } + + return cty.ObjectVal(newAttrs) +} + +func proposedNewNestedBlock(schema *configschema.NestedBlock, prior, config cty.Value) cty.Value { + // The only time we should encounter an entirely unknown block is from the + // use of dynamic with an unknown for_each expression. + if !config.IsKnown() { + return config + } + + var newV cty.Value + + switch schema.Nesting { + + case configschema.NestingSingle, configschema.NestingGroup: + newV = ProposedNew(&schema.Block, prior, config) + + case configschema.NestingList: + // Nested blocks are correlated by index. + configVLen := 0 + if !config.IsNull() { + configVLen = config.LengthInt() + } + if configVLen > 0 { + newVals := make([]cty.Value, 0, configVLen) + for it := config.ElementIterator(); it.Next(); { + idx, configEV := it.Element() + if prior.IsKnown() && (prior.IsNull() || !prior.HasIndex(idx).True()) { + // If there is no corresponding prior element then + // we just take the config value as-is. + newVals = append(newVals, configEV) + continue + } + priorEV := prior.Index(idx) + + newEV := ProposedNew(&schema.Block, priorEV, configEV) + newVals = append(newVals, newEV) + } + // Despite the name, a NestingList might also be a tuple, if + // its nested schema contains dynamically-typed attributes. + if config.Type().IsTupleType() { + newV = cty.TupleVal(newVals) + } else { + newV = cty.ListVal(newVals) + } + } else { + // Despite the name, a NestingList might also be a tuple, if + // its nested schema contains dynamically-typed attributes. + if config.Type().IsTupleType() { + newV = cty.EmptyTupleVal + } else { + newV = cty.ListValEmpty(schema.ImpliedType()) + } + } + + case configschema.NestingMap: + // Despite the name, a NestingMap may produce either a map or + // object value, depending on whether the nested schema contains + // dynamically-typed attributes. + if config.Type().IsObjectType() { + // Nested blocks are correlated by key. + configVLen := 0 + if config.IsKnown() && !config.IsNull() { + configVLen = config.LengthInt() + } + if configVLen > 0 { + newVals := make(map[string]cty.Value, configVLen) + atys := config.Type().AttributeTypes() + for name := range atys { + configEV := config.GetAttr(name) + if !prior.IsKnown() || prior.IsNull() || !prior.Type().HasAttribute(name) { + // If there is no corresponding prior element then + // we just take the config value as-is. + newVals[name] = configEV + continue + } + priorEV := prior.GetAttr(name) + + newEV := ProposedNew(&schema.Block, priorEV, configEV) + newVals[name] = newEV + } + // Although we call the nesting mode "map", we actually use + // object values so that elements might have different types + // in case of dynamically-typed attributes. + newV = cty.ObjectVal(newVals) + } else { + newV = cty.EmptyObjectVal + } + } else { + configVLen := 0 + if config.IsKnown() && !config.IsNull() { + configVLen = config.LengthInt() + } + if configVLen > 0 { + newVals := make(map[string]cty.Value, configVLen) + for it := config.ElementIterator(); it.Next(); { + idx, configEV := it.Element() + k := idx.AsString() + if prior.IsKnown() && (prior.IsNull() || !prior.HasIndex(idx).True()) { + // If there is no corresponding prior element then + // we just take the config value as-is. + newVals[k] = configEV + continue + } + priorEV := prior.Index(idx) + + newEV := ProposedNew(&schema.Block, priorEV, configEV) + newVals[k] = newEV + } + newV = cty.MapVal(newVals) + } else { + newV = cty.MapValEmpty(schema.ImpliedType()) + } + } + + case configschema.NestingSet: + if !config.Type().IsSetType() { + panic("configschema.NestingSet value is not a set as expected") + } + + // Nested blocks are correlated by comparing the element values + // after eliminating all of the computed attributes. In practice, + // this means that any config change produces an entirely new + // nested object, and we only propagate prior computed values + // if the non-computed attribute values are identical. + var cmpVals [][2]cty.Value + if prior.IsKnown() && !prior.IsNull() { + cmpVals = setElementCompareValues(&schema.Block, prior, false) + } + configVLen := 0 + if config.IsKnown() && !config.IsNull() { + configVLen = config.LengthInt() + } + if configVLen > 0 { + used := make([]bool, len(cmpVals)) // track used elements in case multiple have the same compare value + newVals := make([]cty.Value, 0, configVLen) + for it := config.ElementIterator(); it.Next(); { + _, configEV := it.Element() + var priorEV cty.Value + for i, cmp := range cmpVals { + if used[i] { + continue + } + if cmp[1].RawEquals(configEV) { + priorEV = cmp[0] + used[i] = true // we can't use this value on a future iteration + break + } + } + if priorEV == cty.NilVal { + priorEV = cty.NullVal(schema.ImpliedType()) + } + + newEV := ProposedNew(&schema.Block, priorEV, configEV) + newVals = append(newVals, newEV) + } + newV = cty.SetVal(newVals) + } else { + newV = cty.SetValEmpty(schema.Block.ImpliedType()) + } + + default: + // Should never happen, since the above cases are comprehensive. + panic(fmt.Sprintf("unsupported block nesting mode %s", schema.Nesting)) + } + return newV +} + +func proposedNewAttributes(attrs map[string]*configschema.Attribute, prior, config cty.Value) map[string]cty.Value { + if prior.IsNull() { + prior = AllAttributesNull(attrs) + } + newAttrs := make(map[string]cty.Value, len(attrs)) + for name, attr := range attrs { priorV := prior.GetAttr(name) configV := config.GetAttr(name) var newV cty.Value @@ -102,181 +285,170 @@ func proposedNewObject(schema *configschema.Block, prior, config cty.Value) cty. // priorV may also be null, but that's okay. newV = priorV default: - // For non-computed attributes, we always take the config value, - // even if it is null. If it's _required_ then null values - // should've been caught during an earlier validation step, and - // so we don't really care about that here. - newV = configV + if attr.NestedType != nil { + // For non-computed NestedType attributes, we need to descend + // into the individual nested attributes to build the final + // value, unless the entire nested attribute is unknown. + if !configV.IsKnown() { + newV = configV + } else { + newV = proposedNewNestedType(attr.NestedType, priorV, configV) + } + } else { + // For non-computed attributes, we always take the config value, + // even if it is null. If it's _required_ then null values + // should've been caught during an earlier validation step, and + // so we don't really care about that here. + newV = configV + } } newAttrs[name] = newV } + return newAttrs +} - // Merging nested blocks is a little more complex, since we need to - // correlate blocks between both objects and then recursively propose - // a new object for each. The correlation logic depends on the nesting - // mode for each block type. - for name, blockType := range schema.BlockTypes { - priorV := prior.GetAttr(name) - configV := config.GetAttr(name) - var newV cty.Value - switch blockType.Nesting { +func proposedNewNestedType(schema *configschema.Object, prior, config cty.Value) cty.Value { + var newV cty.Value + switch schema.Nesting { + case configschema.NestingSingle: + newAttrs := proposedNewAttributes(schema.Attributes, prior, config) + newV = cty.ObjectVal(newAttrs) + + case configschema.NestingList: + // Nested blocks are correlated by index. + configVLen := 0 + if config.IsKnown() && !config.IsNull() { + configVLen = config.LengthInt() + } + if configVLen > 0 { + newVals := make([]cty.Value, 0, configVLen) + for it := config.ElementIterator(); it.Next(); { + idx, configEV := it.Element() + if prior.IsKnown() && (prior.IsNull() || !prior.HasIndex(idx).True()) { + // If there is no corresponding prior element then + // we just take the config value as-is. + newVals = append(newVals, configEV) + continue + } + priorEV := prior.Index(idx) - case configschema.NestingSingle, configschema.NestingGroup: - newV = ProposedNewObject(&blockType.Block, priorV, configV) + newEV := proposedNewAttributes(schema.Attributes, priorEV, configEV) + newVals = append(newVals, cty.ObjectVal(newEV)) + } + // Despite the name, a NestingList might also be a tuple, if + // its nested schema contains dynamically-typed attributes. + if config.Type().IsTupleType() { + newV = cty.TupleVal(newVals) + } else { + newV = cty.ListVal(newVals) + } + } else { + newV = cty.NullVal(schema.ImpliedType()) + } - case configschema.NestingList: - // Nested blocks are correlated by index. + case configschema.NestingMap: + // Despite the name, a NestingMap may produce either a map or + // object value, depending on whether the nested schema contains + // dynamically-typed attributes. + if config.Type().IsObjectType() { + // Nested blocks are correlated by key. configVLen := 0 - if configV.IsKnown() && !configV.IsNull() { - configVLen = configV.LengthInt() + if config.IsKnown() && !config.IsNull() { + configVLen = config.LengthInt() } if configVLen > 0 { - newVals := make([]cty.Value, 0, configVLen) - for it := configV.ElementIterator(); it.Next(); { - idx, configEV := it.Element() - if priorV.IsKnown() && (priorV.IsNull() || !priorV.HasIndex(idx).True()) { + newVals := make(map[string]cty.Value, configVLen) + atys := config.Type().AttributeTypes() + for name := range atys { + configEV := config.GetAttr(name) + if !prior.IsKnown() || prior.IsNull() || !prior.Type().HasAttribute(name) { // If there is no corresponding prior element then // we just take the config value as-is. - newVals = append(newVals, configEV) + newVals[name] = configEV continue } - priorEV := priorV.Index(idx) - - newEV := ProposedNewObject(&blockType.Block, priorEV, configEV) - newVals = append(newVals, newEV) - } - // Despite the name, a NestingList might also be a tuple, if - // its nested schema contains dynamically-typed attributes. - if configV.Type().IsTupleType() { - newV = cty.TupleVal(newVals) - } else { - newV = cty.ListVal(newVals) + priorEV := prior.GetAttr(name) + newEV := proposedNewAttributes(schema.Attributes, priorEV, configEV) + newVals[name] = cty.ObjectVal(newEV) } + // Although we call the nesting mode "map", we actually use + // object values so that elements might have different types + // in case of dynamically-typed attributes. + newV = cty.ObjectVal(newVals) } else { - // Despite the name, a NestingList might also be a tuple, if - // its nested schema contains dynamically-typed attributes. - if configV.Type().IsTupleType() { - newV = cty.EmptyTupleVal - } else { - newV = cty.ListValEmpty(blockType.ImpliedType()) - } - } - - case configschema.NestingMap: - // Despite the name, a NestingMap may produce either a map or - // object value, depending on whether the nested schema contains - // dynamically-typed attributes. - if configV.Type().IsObjectType() { - // Nested blocks are correlated by key. - configVLen := 0 - if configV.IsKnown() && !configV.IsNull() { - configVLen = configV.LengthInt() - } - if configVLen > 0 { - newVals := make(map[string]cty.Value, configVLen) - atys := configV.Type().AttributeTypes() - for name := range atys { - configEV := configV.GetAttr(name) - if !priorV.IsKnown() || priorV.IsNull() || !priorV.Type().HasAttribute(name) { - // If there is no corresponding prior element then - // we just take the config value as-is. - newVals[name] = configEV - continue - } - priorEV := priorV.GetAttr(name) - - newEV := ProposedNewObject(&blockType.Block, priorEV, configEV) - newVals[name] = newEV - } - // Although we call the nesting mode "map", we actually use - // object values so that elements might have different types - // in case of dynamically-typed attributes. - newV = cty.ObjectVal(newVals) - } else { - newV = cty.EmptyObjectVal - } - } else { - configVLen := 0 - if configV.IsKnown() && !configV.IsNull() { - configVLen = configV.LengthInt() - } - if configVLen > 0 { - newVals := make(map[string]cty.Value, configVLen) - for it := configV.ElementIterator(); it.Next(); { - idx, configEV := it.Element() - k := idx.AsString() - if priorV.IsKnown() && (priorV.IsNull() || !priorV.HasIndex(idx).True()) { - // If there is no corresponding prior element then - // we just take the config value as-is. - newVals[k] = configEV - continue - } - priorEV := priorV.Index(idx) - - newEV := ProposedNewObject(&blockType.Block, priorEV, configEV) - newVals[k] = newEV - } - newV = cty.MapVal(newVals) - } else { - newV = cty.MapValEmpty(blockType.ImpliedType()) - } - } - - case configschema.NestingSet: - if !configV.Type().IsSetType() { - panic("configschema.NestingSet value is not a set as expected") - } - - // Nested blocks are correlated by comparing the element values - // after eliminating all of the computed attributes. In practice, - // this means that any config change produces an entirely new - // nested object, and we only propagate prior computed values - // if the non-computed attribute values are identical. - var cmpVals [][2]cty.Value - if priorV.IsKnown() && !priorV.IsNull() { - cmpVals = setElementCompareValues(&blockType.Block, priorV, false) + newV = cty.NullVal(schema.ImpliedType()) } + } else { configVLen := 0 - if configV.IsKnown() && !configV.IsNull() { - configVLen = configV.LengthInt() + if config.IsKnown() && !config.IsNull() { + configVLen = config.LengthInt() } if configVLen > 0 { - used := make([]bool, len(cmpVals)) // track used elements in case multiple have the same compare value - newVals := make([]cty.Value, 0, configVLen) - for it := configV.ElementIterator(); it.Next(); { - _, configEV := it.Element() - var priorEV cty.Value - for i, cmp := range cmpVals { - if used[i] { - continue - } - if cmp[1].RawEquals(configEV) { - priorEV = cmp[0] - used[i] = true // we can't use this value on a future iteration - break - } - } - if priorEV == cty.NilVal { - priorEV = cty.NullVal(blockType.ImpliedType()) + newVals := make(map[string]cty.Value, configVLen) + for it := config.ElementIterator(); it.Next(); { + idx, configEV := it.Element() + k := idx.AsString() + if prior.IsKnown() && (prior.IsNull() || !prior.HasIndex(idx).True()) { + // If there is no corresponding prior element then + // we just take the config value as-is. + newVals[k] = configEV + continue } + priorEV := prior.Index(idx) - newEV := ProposedNewObject(&blockType.Block, priorEV, configEV) - newVals = append(newVals, newEV) + newEV := proposedNewAttributes(schema.Attributes, priorEV, configEV) + newVals[k] = cty.ObjectVal(newEV) } - newV = cty.SetVal(newVals) + newV = cty.MapVal(newVals) } else { - newV = cty.SetValEmpty(blockType.Block.ImpliedType()) + newV = cty.NullVal(schema.ImpliedType()) } - - default: - // Should never happen, since the above cases are comprehensive. - panic(fmt.Sprintf("unsupported block nesting mode %s", blockType.Nesting)) } - newAttrs[name] = newV + case configschema.NestingSet: + // Nested blocks are correlated by comparing the element values + // after eliminating all of the computed attributes. In practice, + // this means that any config change produces an entirely new + // nested object, and we only propagate prior computed values + // if the non-computed attribute values are identical. + var cmpVals [][2]cty.Value + if prior.IsKnown() && !prior.IsNull() { + cmpVals = setElementCompareValuesFromObject(schema, prior) + } + configVLen := 0 + if config.IsKnown() && !config.IsNull() { + configVLen = config.LengthInt() + } + if configVLen > 0 { + used := make([]bool, len(cmpVals)) // track used elements in case multiple have the same compare value + newVals := make([]cty.Value, 0, configVLen) + for it := config.ElementIterator(); it.Next(); { + _, configEV := it.Element() + var priorEV cty.Value + for i, cmp := range cmpVals { + if used[i] { + continue + } + if cmp[1].RawEquals(configEV) { + priorEV = cmp[0] + used[i] = true // we can't use this value on a future iteration + break + } + } + if priorEV == cty.NilVal { + newVals = append(newVals, configEV) + } else { + newEV := proposedNewAttributes(schema.Attributes, priorEV, configEV) + newVals = append(newVals, cty.ObjectVal(newEV)) + } + } + newV = cty.SetVal(newVals) + } else { + newV = cty.NullVal(schema.ImpliedType()) + } } - return cty.ObjectVal(newAttrs) + return newV } // setElementCompareValues takes a known, non-null value of a cty.Set type and @@ -290,7 +462,7 @@ func proposedNewObject(schema *configschema.Block, prior, config cty.Value) cty. // value and the one-indexed element is the corresponding "compare value". // // This is intended to help correlate prior elements with configured elements -// in ProposedNewObject. The result is a heuristic rather than an exact science, +// in proposedNewBlock. The result is a heuristic rather than an exact science, // since e.g. two separate elements may reduce to the same value through this // process. The caller must therefore be ready to deal with duplicates. func setElementCompareValues(schema *configschema.Block, set cty.Value, isConfig bool) [][2]cty.Value { @@ -407,3 +579,51 @@ func setElementCompareValue(schema *configschema.Block, v cty.Value, isConfig bo return cty.ObjectVal(attrs) } + +// setElementCompareValues takes a known, non-null value of a cty.Set type and +// returns a table -- constructed of two-element arrays -- that maps original +// set element values to corresponding values that have all of the computed +// values removed, making them suitable for comparison with values obtained +// from configuration. The element type of the set must conform to the implied +// type of the given schema, or this function will panic. +// +// In the resulting slice, the zeroth element of each array is the original +// value and the one-indexed element is the corresponding "compare value". +// +// This is intended to help correlate prior elements with configured elements +// in proposedNewBlock. The result is a heuristic rather than an exact science, +// since e.g. two separate elements may reduce to the same value through this +// process. The caller must therefore be ready to deal with duplicates. +func setElementCompareValuesFromObject(schema *configschema.Object, set cty.Value) [][2]cty.Value { + ret := make([][2]cty.Value, 0, set.LengthInt()) + for it := set.ElementIterator(); it.Next(); { + _, ev := it.Element() + ret = append(ret, [2]cty.Value{ev, setElementCompareValueFromObject(schema, ev)}) + } + return ret +} + +// setElementCompareValue creates a new value that has all of the same +// non-computed attribute values as the one given but has all computed +// attribute values forced to null. +// +// The input value must conform to the schema's implied type, and the return +// value is guaranteed to conform to it. +func setElementCompareValueFromObject(schema *configschema.Object, v cty.Value) cty.Value { + if v.IsNull() || !v.IsKnown() { + return v + } + attrs := map[string]cty.Value{} + + for name, attr := range schema.Attributes { + attrV := v.GetAttr(name) + switch { + case attr.Computed: + attrs[name] = cty.NullVal(attr.Type) + default: + attrs[name] = attrV + } + } + + return cty.ObjectVal(attrs) +} diff --git a/vendor/github.com/hashicorp/terraform/plans/objchange/objchange_test.go b/vendor/github.com/hashicorp/terraform/plans/objchange/objchange_test.go index 065e4add..759f37e1 100644 --- a/vendor/github.com/hashicorp/terraform/plans/objchange/objchange_test.go +++ b/vendor/github.com/hashicorp/terraform/plans/objchange/objchange_test.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/terraform/configs/configschema" ) -func TestProposedNewObject(t *testing.T) { +func TestProposedNew(t *testing.T) { tests := map[string]struct { Schema *configschema.Block Prior cty.Value @@ -33,6 +33,18 @@ func TestProposedNewObject(t *testing.T) { Type: cty.String, Computed: true, }, + "bloop": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSingle, + Attributes: map[string]*configschema.Attribute{ + "blop": { + Type: cty.String, + Required: true, + }, + }, + }, + Computed: true, + }, }, BlockTypes: map[string]*configschema.NestedBlock{ "baz": { @@ -57,6 +69,9 @@ func TestProposedNewObject(t *testing.T) { cty.NullVal(cty.DynamicPseudoType), cty.ObjectVal(map[string]cty.Value{ "foo": cty.StringVal("hello"), + "bloop": cty.NullVal(cty.Object(map[string]cty.Type{ + "blop": cty.String, + })), "bar": cty.NullVal(cty.String), "baz": cty.ObjectVal(map[string]cty.Value{ "boz": cty.StringVal("world"), @@ -76,6 +91,9 @@ func TestProposedNewObject(t *testing.T) { // usually changes them to "unknown" during PlanResourceChange, // to indicate that the value will be decided during apply. "bar": cty.NullVal(cty.String), + "bloop": cty.NullVal(cty.Object(map[string]cty.Type{ + "blop": cty.String, + })), "baz": cty.ObjectVal(map[string]cty.Value{ "boz": cty.StringVal("world"), @@ -90,6 +108,18 @@ func TestProposedNewObject(t *testing.T) { Type: cty.String, Optional: true, }, + "bloop": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSingle, + Attributes: map[string]*configschema.Attribute{ + "blop": { + Type: cty.String, + Required: true, + }, + }, + }, + Computed: true, + }, }, BlockTypes: map[string]*configschema.NestedBlock{ "baz": { @@ -109,14 +139,20 @@ func TestProposedNewObject(t *testing.T) { cty.NullVal(cty.DynamicPseudoType), cty.ObjectVal(map[string]cty.Value{ "foo": cty.StringVal("bar"), + "bloop": cty.NullVal(cty.Object(map[string]cty.Type{ + "blop": cty.String, + })), "baz": cty.NullVal(cty.Object(map[string]cty.Type{ "boz": cty.String, })), }), - // The baz block does not exist in the config, and therefore - // shouldn't be planned. + // The bloop attribue and baz block does not exist in the config, + // and therefore shouldn't be planned. cty.ObjectVal(map[string]cty.Value{ "foo": cty.StringVal("bar"), + "bloop": cty.NullVal(cty.Object(map[string]cty.Type{ + "blop": cty.String, + })), "baz": cty.NullVal(cty.Object(map[string]cty.Type{ "boz": cty.String, })), @@ -141,6 +177,21 @@ func TestProposedNewObject(t *testing.T) { }, }, }, + Attributes: map[string]*configschema.Attribute{ + "bloop": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSet, + Attributes: map[string]*configschema.Attribute{ + "blop": { + Type: cty.String, + Required: true, + }, + }, + }, + Computed: true, + Optional: true, + }, + }, }, cty.NullVal(cty.DynamicPseudoType), cty.ObjectVal(map[string]cty.Value{ @@ -149,6 +200,11 @@ func TestProposedNewObject(t *testing.T) { "boz": cty.StringVal("world"), }), }), + "bloop": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("blub"), + }), + }), }), cty.ObjectVal(map[string]cty.Value{ "baz": cty.SetVal([]cty.Value{ @@ -156,6 +212,11 @@ func TestProposedNewObject(t *testing.T) { "boz": cty.StringVal("world"), }), }), + "bloop": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("blub"), + }), + }), }), }, "prior attributes": { @@ -179,6 +240,18 @@ func TestProposedNewObject(t *testing.T) { Optional: true, Computed: true, }, + "bloop": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSingle, + Attributes: map[string]*configschema.Attribute{ + "blop": { + Type: cty.String, + Required: true, + }, + }, + }, + Optional: true, + }, }, }, cty.ObjectVal(map[string]cty.Value{ @@ -186,18 +259,27 @@ func TestProposedNewObject(t *testing.T) { "bar": cty.StringVal("petit dejeuner"), "baz": cty.StringVal("grande dejeuner"), "boz": cty.StringVal("a la monde"), + "bloop": cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("glub"), + }), }), cty.ObjectVal(map[string]cty.Value{ "foo": cty.StringVal("hello"), "bar": cty.NullVal(cty.String), "baz": cty.NullVal(cty.String), "boz": cty.StringVal("world"), + "bloop": cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("bleep"), + }), }), cty.ObjectVal(map[string]cty.Value{ "foo": cty.StringVal("hello"), "bar": cty.StringVal("petit dejeuner"), "baz": cty.StringVal("grande dejeuner"), "boz": cty.StringVal("world"), + "bloop": cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("bleep"), + }), }), }, "prior nested single": { @@ -221,24 +303,54 @@ func TestProposedNewObject(t *testing.T) { }, }, }, + Attributes: map[string]*configschema.Attribute{ + "bloop": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSingle, + Attributes: map[string]*configschema.Attribute{ + "blop": { + Type: cty.String, + Required: true, + }, + "bleep": { + Type: cty.String, + Optional: true, + }, + }, + }, + Optional: true, + }, + }, }, cty.ObjectVal(map[string]cty.Value{ "foo": cty.ObjectVal(map[string]cty.Value{ "bar": cty.StringVal("beep"), "baz": cty.StringVal("boop"), }), + "bloop": cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("glub"), + "bleep": cty.NullVal(cty.String), + }), }), cty.ObjectVal(map[string]cty.Value{ "foo": cty.ObjectVal(map[string]cty.Value{ "bar": cty.StringVal("bap"), "baz": cty.NullVal(cty.String), }), + "bloop": cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("glub"), + "bleep": cty.StringVal("beep"), + }), }), cty.ObjectVal(map[string]cty.Value{ "foo": cty.ObjectVal(map[string]cty.Value{ "bar": cty.StringVal("bap"), "baz": cty.StringVal("boop"), }), + "bloop": cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("glub"), + "bleep": cty.StringVal("beep"), + }), }), }, "prior nested list": { @@ -262,6 +374,20 @@ func TestProposedNewObject(t *testing.T) { }, }, }, + Attributes: map[string]*configschema.Attribute{ + "bloop": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingList, + Attributes: map[string]*configschema.Attribute{ + "blop": { + Type: cty.String, + Required: true, + }, + }, + }, + Optional: true, + }, + }, }, cty.ObjectVal(map[string]cty.Value{ "foo": cty.ListVal([]cty.Value{ @@ -270,6 +396,14 @@ func TestProposedNewObject(t *testing.T) { "baz": cty.StringVal("boop"), }), }), + "bloop": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("bar"), + }), + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("baz"), + }), + }), }), cty.ObjectVal(map[string]cty.Value{ "foo": cty.ListVal([]cty.Value{ @@ -282,6 +416,14 @@ func TestProposedNewObject(t *testing.T) { "baz": cty.NullVal(cty.String), }), }), + "bloop": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("bar"), + }), + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("baz"), + }), + }), }), cty.ObjectVal(map[string]cty.Value{ "foo": cty.ListVal([]cty.Value{ @@ -294,6 +436,14 @@ func TestProposedNewObject(t *testing.T) { "baz": cty.NullVal(cty.String), }), }), + "bloop": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("bar"), + }), + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("baz"), + }), + }), }), }, "prior nested list with dynamic": { @@ -317,6 +467,24 @@ func TestProposedNewObject(t *testing.T) { }, }, }, + Attributes: map[string]*configschema.Attribute{ + "bloop": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingList, + Attributes: map[string]*configschema.Attribute{ + "blop": { + Type: cty.DynamicPseudoType, + Required: true, + }, + "blub": { + Type: cty.DynamicPseudoType, + Optional: true, + }, + }, + }, + Optional: true, + }, + }, }, cty.ObjectVal(map[string]cty.Value{ "foo": cty.TupleVal([]cty.Value{ @@ -325,6 +493,16 @@ func TestProposedNewObject(t *testing.T) { "baz": cty.StringVal("boop"), }), }), + "bloop": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("bar"), + "blub": cty.StringVal("glub"), + }), + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("baz"), + "blub": cty.NullVal(cty.String), + }), + }), }), cty.ObjectVal(map[string]cty.Value{ "foo": cty.TupleVal([]cty.Value{ @@ -337,6 +515,12 @@ func TestProposedNewObject(t *testing.T) { "baz": cty.NullVal(cty.String), }), }), + "bloop": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("bar"), + "blub": cty.NullVal(cty.String), + }), + }), }), cty.ObjectVal(map[string]cty.Value{ "foo": cty.TupleVal([]cty.Value{ @@ -349,6 +533,12 @@ func TestProposedNewObject(t *testing.T) { "baz": cty.NullVal(cty.String), }), }), + "bloop": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("bar"), + "blub": cty.NullVal(cty.String), + }), + }), }), }, "prior nested map": { @@ -372,6 +562,20 @@ func TestProposedNewObject(t *testing.T) { }, }, }, + Attributes: map[string]*configschema.Attribute{ + "bloop": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingMap, + Attributes: map[string]*configschema.Attribute{ + "blop": { + Type: cty.String, + Required: true, + }, + }, + }, + Optional: true, + }, + }, }, cty.ObjectVal(map[string]cty.Value{ "foo": cty.MapVal(map[string]cty.Value{ @@ -384,6 +588,14 @@ func TestProposedNewObject(t *testing.T) { "baz": cty.StringVal("boot"), }), }), + "bloop": cty.MapVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("glub"), + }), + "b": cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("blub"), + }), + }), }), cty.ObjectVal(map[string]cty.Value{ "foo": cty.MapVal(map[string]cty.Value{ @@ -396,6 +608,14 @@ func TestProposedNewObject(t *testing.T) { "baz": cty.NullVal(cty.String), }), }), + "bloop": cty.MapVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("glub"), + }), + "c": cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("blub"), + }), + }), }), cty.ObjectVal(map[string]cty.Value{ "foo": cty.MapVal(map[string]cty.Value{ @@ -408,6 +628,14 @@ func TestProposedNewObject(t *testing.T) { "baz": cty.NullVal(cty.String), }), }), + "bloop": cty.MapVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("glub"), + }), + "c": cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("blub"), + }), + }), }), }, "prior nested map with dynamic": { @@ -431,6 +659,20 @@ func TestProposedNewObject(t *testing.T) { }, }, }, + Attributes: map[string]*configschema.Attribute{ + "bloop": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingMap, + Attributes: map[string]*configschema.Attribute{ + "blop": { + Type: cty.DynamicPseudoType, + Required: true, + }, + }, + }, + Optional: true, + }, + }, }, cty.ObjectVal(map[string]cty.Value{ "foo": cty.ObjectVal(map[string]cty.Value{ @@ -443,6 +685,14 @@ func TestProposedNewObject(t *testing.T) { "baz": cty.ListVal([]cty.Value{cty.StringVal("boot")}), }), }), + "bloop": cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("glub"), + }), + "b": cty.ObjectVal(map[string]cty.Value{ + "blop": cty.NumberIntVal(13), + }), + }), }), cty.ObjectVal(map[string]cty.Value{ "foo": cty.ObjectVal(map[string]cty.Value{ @@ -455,6 +705,14 @@ func TestProposedNewObject(t *testing.T) { "baz": cty.NullVal(cty.List(cty.String)), }), }), + "bloop": cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("blep"), + }), + "c": cty.ObjectVal(map[string]cty.Value{ + "blop": cty.NumberIntVal(13), + }), + }), }), cty.ObjectVal(map[string]cty.Value{ "foo": cty.ObjectVal(map[string]cty.Value{ @@ -467,6 +725,14 @@ func TestProposedNewObject(t *testing.T) { "baz": cty.NullVal(cty.List(cty.String)), }), }), + "bloop": cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("blep"), + }), + "c": cty.ObjectVal(map[string]cty.Value{ + "blop": cty.NumberIntVal(13), + }), + }), }), }, "prior nested set": { @@ -492,6 +758,24 @@ func TestProposedNewObject(t *testing.T) { }, }, }, + Attributes: map[string]*configschema.Attribute{ + "bloop": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSet, + Attributes: map[string]*configschema.Attribute{ + "blop": { + Type: cty.String, + Required: true, + }, + "bleep": { + Type: cty.String, + Optional: true, + }, + }, + }, + Optional: true, + }, + }, }, cty.ObjectVal(map[string]cty.Value{ "foo": cty.SetVal([]cty.Value{ @@ -504,6 +788,16 @@ func TestProposedNewObject(t *testing.T) { "baz": cty.StringVal("boot"), }), }), + "bloop": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("glubglub"), + "bleep": cty.NullVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("glubglub"), + "bleep": cty.StringVal("beep"), + }), + }), }), cty.ObjectVal(map[string]cty.Value{ "foo": cty.SetVal([]cty.Value{ @@ -516,6 +810,16 @@ func TestProposedNewObject(t *testing.T) { "baz": cty.NullVal(cty.String), }), }), + "bloop": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("glubglub"), + "bleep": cty.NullVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("glub"), + "bleep": cty.NullVal(cty.String), + }), + }), }), cty.ObjectVal(map[string]cty.Value{ "foo": cty.SetVal([]cty.Value{ @@ -528,6 +832,16 @@ func TestProposedNewObject(t *testing.T) { "baz": cty.NullVal(cty.String), }), }), + "bloop": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("glubglub"), + "bleep": cty.NullVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("glub"), + "bleep": cty.NullVal(cty.String), + }), + }), }), }, "sets differing only by unknown": { @@ -546,6 +860,20 @@ func TestProposedNewObject(t *testing.T) { }, }, }, + Attributes: map[string]*configschema.Attribute{ + "bloop": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSet, + Attributes: map[string]*configschema.Attribute{ + "blop": { + Type: cty.String, + Required: true, + }, + }, + }, + Optional: true, + }, + }, }, cty.NullVal(cty.DynamicPseudoType), cty.ObjectVal(map[string]cty.Value{ @@ -557,6 +885,14 @@ func TestProposedNewObject(t *testing.T) { "optional": cty.UnknownVal(cty.String), }), }), + "bloop": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.UnknownVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.UnknownVal(cty.String), + }), + }), }), cty.ObjectVal(map[string]cty.Value{ "multi": cty.SetVal([]cty.Value{ @@ -570,6 +906,14 @@ func TestProposedNewObject(t *testing.T) { "optional": cty.UnknownVal(cty.String), }), }), + "bloop": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.UnknownVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.UnknownVal(cty.String), + }), + }), }), }, "nested list in set": { @@ -854,14 +1198,297 @@ func TestProposedNewObject(t *testing.T) { }), }), }, + // This example has a mixture of optional, computed and required in a deeply-nested NestedType attribute + "deeply NestedType": { + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSingle, + Attributes: map[string]*configschema.Attribute{ + "bar": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSingle, + Attributes: testAttributes, + }, + Required: true, + }, + "baz": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSingle, + Attributes: testAttributes, + }, + Optional: true, + }, + }, + }, + Optional: true, + }, + }, + }, + // prior + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ObjectVal(map[string]cty.Value{ + "bar": cty.NullVal(cty.DynamicPseudoType), + "baz": cty.ObjectVal(map[string]cty.Value{ + "optional": cty.NullVal(cty.String), + "computed": cty.StringVal("hello"), + "optional_computed": cty.StringVal("prior"), + "required": cty.StringVal("present"), + }), + }), + }), + // config + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ObjectVal(map[string]cty.Value{ + "bar": cty.UnknownVal(cty.Object(map[string]cty.Type{ // explicit unknown from the config + "optional": cty.String, + "computed": cty.String, + "optional_computed": cty.String, + "required": cty.String, + })), + "baz": cty.ObjectVal(map[string]cty.Value{ + "optional": cty.NullVal(cty.String), + "computed": cty.NullVal(cty.String), + "optional_computed": cty.StringVal("hello"), + "required": cty.StringVal("present"), + }), + }), + }), + // want + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.ObjectVal(map[string]cty.Value{ + "bar": cty.UnknownVal(cty.Object(map[string]cty.Type{ // explicit unknown preserved from the config + "optional": cty.String, + "computed": cty.String, + "optional_computed": cty.String, + "required": cty.String, + })), + "baz": cty.ObjectVal(map[string]cty.Value{ + "optional": cty.NullVal(cty.String), // config is null + "computed": cty.StringVal("hello"), // computed values come from prior + "optional_computed": cty.StringVal("hello"), // config takes precedent over prior in opt+computed + "required": cty.StringVal("present"), // value from config + }), + }), + }), + }, + "deeply nested set": { + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSet, + Attributes: map[string]*configschema.Attribute{ + "bar": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSet, + Attributes: testAttributes, + }, + Required: true, + }, + }, + }, + Optional: true, + }, + }, + }, + // prior values + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "bar": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "optional": cty.StringVal("prior"), + "computed": cty.StringVal("prior"), + "optional_computed": cty.StringVal("prior"), + "required": cty.StringVal("prior"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "bar": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + "optional": cty.StringVal("other_prior"), + "computed": cty.StringVal("other_prior"), + "optional_computed": cty.StringVal("other_prior"), + "required": cty.StringVal("other_prior"), + })}), + }), + }), + }), + // config differs from prior + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "bar": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + "optional": cty.StringVal("configured"), + "computed": cty.NullVal(cty.String), // computed attrs are null in config + "optional_computed": cty.StringVal("configured"), + "required": cty.StringVal("configured"), + })}), + }), + cty.ObjectVal(map[string]cty.Value{ + "bar": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + "optional": cty.NullVal(cty.String), // explicit null in config + "computed": cty.NullVal(cty.String), // computed attrs are null in config + "optional_computed": cty.StringVal("other_configured"), + "required": cty.StringVal("other_configured"), + })}), + }), + }), + }), + // want: + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "bar": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + "optional": cty.StringVal("configured"), + "computed": cty.NullVal(cty.String), + "optional_computed": cty.StringVal("configured"), + "required": cty.StringVal("configured"), + })}), + }), + cty.ObjectVal(map[string]cty.Value{ + "bar": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{ + "optional": cty.NullVal(cty.String), // explicit null in config is preserved + "computed": cty.NullVal(cty.String), + "optional_computed": cty.StringVal("other_configured"), + "required": cty.StringVal("other_configured"), + })}), + }), + }), + }), + }, + "expected null NestedTypes": { + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "single": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSingle, + Attributes: map[string]*configschema.Attribute{ + "bar": {Type: cty.String}, + }, + }, + Optional: true, + }, + "list": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingList, + Attributes: map[string]*configschema.Attribute{ + "bar": {Type: cty.String}, + }, + }, + Optional: true, + }, + "set": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSet, + Attributes: map[string]*configschema.Attribute{ + "bar": {Type: cty.String}, + }, + }, + Optional: true, + }, + "map": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingMap, + Attributes: map[string]*configschema.Attribute{ + "bar": {Type: cty.String}, + }, + }, + Optional: true, + }, + "nested_map": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingMap, + Attributes: map[string]*configschema.Attribute{ + "inner": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSingle, + Attributes: testAttributes, + }, + }, + }, + }, + Optional: true, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "single": cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")}), + "list": cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")})}), + "map": cty.MapVal(map[string]cty.Value{ + "map_entry": cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")}), + }), + "set": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")})}), + "nested_map": cty.MapVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "inner": cty.ObjectVal(map[string]cty.Value{ + "optional": cty.StringVal("foo"), + "computed": cty.StringVal("foo"), + "optional_computed": cty.StringVal("foo"), + "required": cty.StringVal("foo"), + }), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "single": cty.ObjectVal(map[string]cty.Value{"bar": cty.NullVal(cty.String)}), + "list": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"bar": cty.String}))), + "map": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{"bar": cty.String}))), + "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{"bar": cty.String}))), + "nested_map": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{ + "inner": cty.Object(map[string]cty.Type{ + "optional": cty.String, + "computed": cty.String, + "optional_computed": cty.String, + "required": cty.String, + }), + }))), + }), + cty.ObjectVal(map[string]cty.Value{ + "single": cty.ObjectVal(map[string]cty.Value{"bar": cty.NullVal(cty.String)}), + "list": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{"bar": cty.String}))), + "map": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{"bar": cty.String}))), + "set": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{"bar": cty.String}))), + "nested_map": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{ + "inner": cty.ObjectWithOptionalAttrs(map[string]cty.Type{ + "optional": cty.String, + "computed": cty.String, + "optional_computed": cty.String, + "required": cty.String, + }, []string{"optional", "optional_computed"}), + }))), + }), + }, } for name, test := range tests { t.Run(name, func(t *testing.T) { - got := ProposedNewObject(test.Schema, test.Prior, test.Config) + got := ProposedNew(test.Schema, test.Prior, test.Config) if !got.RawEquals(test.Want) { t.Errorf("wrong result\ngot: %swant: %s", dump.Value(got), dump.Value(test.Want)) } }) } } + +var testAttributes = map[string]*configschema.Attribute{ + "optional": { + Type: cty.String, + Optional: true, + }, + "computed": { + Type: cty.String, + Computed: true, + }, + "optional_computed": { + Type: cty.String, + Computed: true, + Optional: true, + }, + "required": { + Type: cty.String, + Required: true, + }, +} diff --git a/vendor/github.com/hashicorp/terraform/plans/objchange/plan_valid.go b/vendor/github.com/hashicorp/terraform/plans/objchange/plan_valid.go index 69acb897..07dd2486 100644 --- a/vendor/github.com/hashicorp/terraform/plans/objchange/plan_valid.go +++ b/vendor/github.com/hashicorp/terraform/plans/objchange/plan_valid.go @@ -53,18 +53,10 @@ func assertPlanValid(schema *configschema.Block, priorState, config, plannedStat impTy := schema.ImpliedType() - for name, attrS := range schema.Attributes { - plannedV := plannedState.GetAttr(name) - configV := config.GetAttr(name) - priorV := cty.NullVal(attrS.Type) - if !priorState.IsNull() { - priorV = priorState.GetAttr(name) - } + // verify attributes + moreErrs := assertPlannedAttrsValid(schema.Attributes, priorState, config, plannedState, path) + errs = append(errs, moreErrs...) - path := append(path, cty.GetAttrStep{Name: name}) - moreErrs := assertPlannedValueValid(attrS, priorV, configV, plannedV, path) - errs = append(errs, moreErrs...) - } for name, blockS := range schema.BlockTypes { path := append(path, cty.GetAttrStep{Name: name}) plannedV := plannedState.GetAttr(name) @@ -229,23 +221,62 @@ func assertPlanValid(schema *configschema.Block, priorState, config, plannedStat return errs } +func assertPlannedAttrsValid(schema map[string]*configschema.Attribute, priorState, config, plannedState cty.Value, path cty.Path) []error { + var errs []error + for name, attrS := range schema { + moreErrs := assertPlannedAttrValid(name, attrS, priorState, config, plannedState, path) + errs = append(errs, moreErrs...) + } + return errs +} + +func assertPlannedAttrValid(name string, attrS *configschema.Attribute, priorState, config, plannedState cty.Value, path cty.Path) []error { + plannedV := plannedState.GetAttr(name) + configV := config.GetAttr(name) + priorV := cty.NullVal(attrS.Type) + if !priorState.IsNull() { + priorV = priorState.GetAttr(name) + } + path = append(path, cty.GetAttrStep{Name: name}) + + return assertPlannedValueValid(attrS, priorV, configV, plannedV, path) +} + func assertPlannedValueValid(attrS *configschema.Attribute, priorV, configV, plannedV cty.Value, path cty.Path) []error { var errs []error if plannedV.RawEquals(configV) { // This is the easy path: provider didn't change anything at all. return errs } - if plannedV.RawEquals(priorV) && !priorV.IsNull() { + if plannedV.RawEquals(priorV) && !priorV.IsNull() && !configV.IsNull() { // Also pretty easy: there is a prior value and the provider has // returned it unchanged. This indicates that configV and plannedV // are functionally equivalent and so the provider wishes to disregard // the configuration value in favor of the prior. return errs } - if attrS.Computed && configV.IsNull() { - // The provider is allowed to change the value of any computed - // attribute that isn't explicitly set in the config. - return errs + + // the provider is allowed to insert values when the config is + // null, but only if the attribute is computed. + if configV.IsNull() { + if attrS.Computed { + return errs + } + + // if the attribute is not computed, then any planned value is incorrect + if !plannedV.IsNull() { + if attrS.Sensitive { + errs = append(errs, path.NewErrorf("sensitive planned value for a non-computed attribute")) + } else { + errs = append(errs, path.NewErrorf("planned value %#v for a non-computed attribute", plannedV)) + } + return errs + } + } + + // If this attribute has a NestedType, validate the nested object + if attrS.NestedType != nil { + return assertPlannedObjectValid(attrS.NestedType, priorV, configV, plannedV, path) } // If none of the above conditions match, the provider has made an invalid @@ -258,10 +289,160 @@ func assertPlannedValueValid(attrS *configschema.Attribute, priorV, configV, pla } return errs } + if attrS.Sensitive { errs = append(errs, path.NewErrorf("sensitive planned value does not match config value nor prior value")) } else { errs = append(errs, path.NewErrorf("planned value %#v does not match config value %#v nor prior value %#v", plannedV, configV, priorV)) } + + return errs +} + +func assertPlannedObjectValid(schema *configschema.Object, prior, config, planned cty.Value, path cty.Path) []error { + var errs []error + + if planned.IsNull() && !config.IsNull() { + errs = append(errs, path.NewErrorf("planned for absense but config wants existence")) + return errs + } + if config.IsNull() && !planned.IsNull() { + errs = append(errs, path.NewErrorf("planned for existence but config wants absense")) + return errs + } + if planned.IsNull() { + // No further checks possible if the planned value is null + return errs + } + + switch schema.Nesting { + case configschema.NestingSingle, configschema.NestingGroup: + moreErrs := assertPlannedAttrsValid(schema.Attributes, prior, config, planned, path) + errs = append(errs, moreErrs...) + + case configschema.NestingList: + // A NestingList might either be a list or a tuple, depending on + // whether there are dynamically-typed attributes inside. However, + // both support a similar-enough API that we can treat them the + // same for our purposes here. + + plannedL := planned.LengthInt() + configL := config.LengthInt() + if plannedL != configL { + errs = append(errs, path.NewErrorf("count in plan (%d) disagrees with count in config (%d)", plannedL, configL)) + return errs + } + for it := planned.ElementIterator(); it.Next(); { + idx, plannedEV := it.Element() + path := append(path, cty.IndexStep{Key: idx}) + if !plannedEV.IsKnown() { + errs = append(errs, path.NewErrorf("element representing nested block must not be unknown itself; set nested attribute values to unknown instead")) + continue + } + if !config.HasIndex(idx).True() { + continue // should never happen since we checked the lengths above + } + configEV := config.Index(idx) + priorEV := cty.NullVal(schema.ImpliedType()) + if !prior.IsNull() && prior.HasIndex(idx).True() { + priorEV = prior.Index(idx) + } + + moreErrs := assertPlannedAttrsValid(schema.Attributes, priorEV, configEV, plannedEV, path) + errs = append(errs, moreErrs...) + } + + case configschema.NestingMap: + // A NestingMap might either be a map or an object, depending on + // whether there are dynamically-typed attributes inside, but + // that's decided statically and so all values will have the same + // kind. + if planned.Type().IsObjectType() { + plannedAtys := planned.Type().AttributeTypes() + configAtys := config.Type().AttributeTypes() + for k := range plannedAtys { + if _, ok := configAtys[k]; !ok { + errs = append(errs, path.NewErrorf("block key %q from plan is not present in config", k)) + continue + } + path := append(path, cty.GetAttrStep{Name: k}) + + plannedEV := planned.GetAttr(k) + if !plannedEV.IsKnown() { + errs = append(errs, path.NewErrorf("element representing nested block must not be unknown itself; set nested attribute values to unknown instead")) + continue + } + configEV := config.GetAttr(k) + priorEV := cty.NullVal(schema.ImpliedType()) + if !prior.IsNull() && prior.Type().HasAttribute(k) { + priorEV = prior.GetAttr(k) + } + moreErrs := assertPlannedAttrsValid(schema.Attributes, priorEV, configEV, plannedEV, path) + errs = append(errs, moreErrs...) + } + for k := range configAtys { + if _, ok := plannedAtys[k]; !ok { + errs = append(errs, path.NewErrorf("block key %q from config is not present in plan", k)) + continue + } + } + } else { + plannedL := planned.LengthInt() + configL := config.LengthInt() + if plannedL != configL { + errs = append(errs, path.NewErrorf("block count in plan (%d) disagrees with count in config (%d)", plannedL, configL)) + return errs + } + for it := planned.ElementIterator(); it.Next(); { + idx, plannedEV := it.Element() + path := append(path, cty.IndexStep{Key: idx}) + if !plannedEV.IsKnown() { + errs = append(errs, path.NewErrorf("element representing nested block must not be unknown itself; set nested attribute values to unknown instead")) + continue + } + k := idx.AsString() + if !config.HasIndex(idx).True() { + errs = append(errs, path.NewErrorf("block key %q from plan is not present in config", k)) + continue + } + configEV := config.Index(idx) + priorEV := cty.NullVal(schema.ImpliedType()) + if !prior.IsNull() && prior.HasIndex(idx).True() { + priorEV = prior.Index(idx) + } + moreErrs := assertPlannedObjectValid(schema, priorEV, configEV, plannedEV, path) + errs = append(errs, moreErrs...) + } + for it := config.ElementIterator(); it.Next(); { + idx, _ := it.Element() + if !planned.HasIndex(idx).True() { + errs = append(errs, path.NewErrorf("block key %q from config is not present in plan", idx.AsString())) + continue + } + } + } + + case configschema.NestingSet: + // Because set elements have no identifier with which to correlate + // them, we can't robustly validate the plan for a nested block + // backed by a set, and so unfortunately we need to just trust the + // provider to do the right thing. :( + // + // (In principle we could correlate elements by matching the + // subset of attributes explicitly set in config, except for the + // special diff suppression rule which allows for there to be a + // planned value that is constructed by mixing part of a prior + // value with part of a config value, creating an entirely new + // element that is not present in either prior nor config.) + for it := planned.ElementIterator(); it.Next(); { + idx, plannedEV := it.Element() + path := append(path, cty.IndexStep{Key: idx}) + if !plannedEV.IsKnown() { + errs = append(errs, path.NewErrorf("element representing nested block must not be unknown itself; set nested attribute values to unknown instead")) + continue + } + } + } + return errs } diff --git a/vendor/github.com/hashicorp/terraform/plans/objchange/plan_valid_test.go b/vendor/github.com/hashicorp/terraform/plans/objchange/plan_valid_test.go index cc41d694..5fca9b5d 100644 --- a/vendor/github.com/hashicorp/terraform/plans/objchange/plan_valid_test.go +++ b/vendor/github.com/hashicorp/terraform/plans/objchange/plan_valid_test.go @@ -579,6 +579,545 @@ func TestAssertPlanValid(t *testing.T) { }), nil, }, + + // Attributes with NestedTypes + "NestedType attr, no computed, all match": { + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "a": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingList, + Attributes: map[string]*configschema.Attribute{ + "b": { + Type: cty.String, + Optional: true, + }, + }, + }, + Optional: true, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("b value"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("b value"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("b value"), + }), + }), + }), + nil, + }, + "NestedType attr, no computed, plan matches, no prior": { + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "a": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingList, + Attributes: map[string]*configschema.Attribute{ + "b": { + Type: cty.String, + Optional: true, + }, + }, + }, + Optional: true, + }, + }, + }, + cty.NullVal(cty.Object(map[string]cty.Type{ + "a": cty.List(cty.Object(map[string]cty.Type{ + "b": cty.String, + })), + })), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("c value"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("c value"), + }), + }), + }), + nil, + }, + "NestedType, no computed, invalid change in plan": { + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "a": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingList, + Attributes: map[string]*configschema.Attribute{ + "b": { + Type: cty.String, + Optional: true, + }, + }, + }, + Optional: true, + }, + }, + }, + cty.NullVal(cty.Object(map[string]cty.Type{ + "a": cty.List(cty.Object(map[string]cty.Type{ + "b": cty.String, + })), + })), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("c value"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("new c value"), + }), + }), + }), + []string{ + `.a[0].b: planned value cty.StringVal("new c value") does not match config value cty.StringVal("c value")`, + }, + }, + "NestedType attr, no computed, invalid change in plan sensitive": { + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "a": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingList, + Attributes: map[string]*configschema.Attribute{ + "b": { + Type: cty.String, + Optional: true, + Sensitive: true, + }, + }, + }, + Optional: true, + }, + }, + }, + cty.NullVal(cty.Object(map[string]cty.Type{ + "a": cty.List(cty.Object(map[string]cty.Type{ + "b": cty.String, + })), + })), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("b value"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("new b value"), + }), + }), + }), + []string{ + `.a[0].b: sensitive planned value does not match config value`, + }, + }, + "NestedType attr, no computed, diff suppression in plan": { + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "a": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingList, + Attributes: map[string]*configschema.Attribute{ + "b": { + Type: cty.String, + Optional: true, + }, + }, + }, + Optional: true, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("b value"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("new b value"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "b": cty.StringVal("b value"), // plan uses value from prior object + }), + }), + }), + nil, + }, + "NestedType attr, no computed, all null": { + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "a": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingList, + Attributes: map[string]*configschema.Attribute{ + "b": { + Type: cty.String, + Optional: true, + }, + }, + }, + Optional: true, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.DynamicPseudoType), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.DynamicPseudoType), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.DynamicPseudoType), + }), + nil, + }, + "NestedType attr, no computed, all zero value": { + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "a": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingList, + Attributes: map[string]*configschema.Attribute{ + "b": { + Type: cty.String, + Optional: true, + }, + }, + }, + Optional: true, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{ + "b": cty.String, + }))), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{ + "b": cty.String, + }))), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{ + "b": cty.String, + }))), + }), + nil, + }, + "NestedType NestingSet attribute to null": { + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bloop": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSet, + Attributes: map[string]*configschema.Attribute{ + "blop": { + Type: cty.String, + Required: true, + }, + }, + }, + Optional: true, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "bloop": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("ok"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "bloop": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ + "blop": cty.String, + }))), + }), + cty.ObjectVal(map[string]cty.Value{ + "bloop": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ + "blop": cty.String, + }))), + }), + nil, + }, + "NestedType deep nested optional set attribute to null": { + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bleep": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingList, + Attributes: map[string]*configschema.Attribute{ + "bloop": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSet, + Attributes: map[string]*configschema.Attribute{ + "blome": { + Type: cty.String, + Optional: true, + }, + }, + }, + Optional: true, + }, + }, + }, + Optional: true, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "bleep": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "bloop": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blome": cty.StringVal("ok"), + }), + }), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "bleep": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "bloop": cty.NullVal(cty.Set( + cty.Object(map[string]cty.Type{ + "blome": cty.String, + }), + )), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "bleep": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "bloop": cty.NullVal(cty.List( + cty.Object(map[string]cty.Type{ + "blome": cty.String, + }), + )), + }), + }), + }), + nil, + }, + "NestedType deep nested set": { + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bleep": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingList, + Attributes: map[string]*configschema.Attribute{ + "bloop": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSet, + Attributes: map[string]*configschema.Attribute{ + "blome": { + Type: cty.String, + Optional: true, + }, + }, + }, + Optional: true, + }, + }, + }, + Optional: true, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "bleep": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "bloop": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blome": cty.StringVal("ok"), + }), + }), + }), + }), + }), + // Note: bloop is null in the config + cty.ObjectVal(map[string]cty.Value{ + "bleep": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "bloop": cty.NullVal(cty.Set( + cty.Object(map[string]cty.Type{ + "blome": cty.String, + }), + )), + }), + }), + }), + // provider sends back the prior value, not matching the config + cty.ObjectVal(map[string]cty.Value{ + "bleep": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "bloop": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blome": cty.StringVal("ok"), + }), + }), + }), + }), + }), + nil, // we cannot validate individual set elements, and trust the provider's response + }, + "NestedType nested computed list attribute": { + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bloop": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingList, + Attributes: map[string]*configschema.Attribute{ + "blop": { + Type: cty.String, + Optional: true, + }, + }, + }, + Computed: true, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "bloop": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("ok"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "bloop": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{ + "blop": cty.String, + }))), + }), + + cty.ObjectVal(map[string]cty.Value{ + "bloop": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("ok"), + }), + }), + }), + nil, + }, + "NestedType nested list attribute to null": { + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bloop": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingList, + Attributes: map[string]*configschema.Attribute{ + "blop": { + Type: cty.String, + Optional: true, + }, + }, + }, + Optional: true, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "bloop": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("ok"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "bloop": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{ + "blop": cty.String, + }))), + }), + + // provider returned the old value + cty.ObjectVal(map[string]cty.Value{ + "bloop": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("ok"), + }), + }), + }), + []string{`.bloop: planned value cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{"blop":cty.StringVal("ok")})}) for a non-computed attribute`}, + }, + "NestedType nested set attribute to null": { + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bloop": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSet, + Attributes: map[string]*configschema.Attribute{ + "blop": { + Type: cty.String, + Optional: true, + }, + }, + }, + Optional: true, + }, + }, + }, + cty.ObjectVal(map[string]cty.Value{ + "bloop": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("ok"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "bloop": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ + "blop": cty.String, + }))), + }), + // provider returned the old value + cty.ObjectVal(map[string]cty.Value{ + "bloop": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "blop": cty.StringVal("ok"), + }), + }), + }), + []string{`.bloop: planned value cty.ListVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{"blop":cty.StringVal("ok")})}) for a non-computed attribute`}, + }, } for name, test := range tests { diff --git a/vendor/github.com/hashicorp/terraform/plans/plan.go b/vendor/github.com/hashicorp/terraform/plans/plan.go index 92778e1e..3ba7fa97 100644 --- a/vendor/github.com/hashicorp/terraform/plans/plan.go +++ b/vendor/github.com/hashicorp/terraform/plans/plan.go @@ -21,12 +21,38 @@ import ( // since the plan does not itself include all of the information required to // make the changes indicated. type Plan struct { - VariableValues map[string]DynamicValue - Changes *Changes - TargetAddrs []addrs.Targetable - ProviderSHA256s map[string][]byte - Backend Backend - State *states.State + // Mode is the mode under which this plan was created. + // + // This is only recorded to allow for UI differences when presenting plans + // to the end-user, and so it must not be used to influence apply-time + // behavior. The actions during apply must be described entirely by + // the Changes field, regardless of how the plan was created. + UIMode Mode + + VariableValues map[string]DynamicValue + Changes *Changes + TargetAddrs []addrs.Targetable + ForceReplaceAddrs []addrs.AbsResourceInstance + ProviderSHA256s map[string][]byte + Backend Backend + + // PrevRunState and PriorState both describe the situation that the plan + // was derived from: + // + // PrevRunState is a representation of the outcome of the previous + // Terraform operation, without any updates from the remote system but + // potentially including some changes that resulted from state upgrade + // actions. + // + // PriorState is a representation of the current state of remote objects, + // which will differ from PrevRunState if the "refresh" step returned + // different data, which might reflect drift. + // + // PriorState is the main snapshot we use for actions during apply. + // PrevRunState is only here so that we can diff PriorState against it in + // order to report to the user any out-of-band changes we've detected. + PrevRunState *states.State + PriorState *states.State } // Backend represents the backend-related configuration and other data as it diff --git a/vendor/github.com/hashicorp/terraform/plans/plan_test.go b/vendor/github.com/hashicorp/terraform/plans/plan_test.go index 012ff06a..b8a0e450 100644 --- a/vendor/github.com/hashicorp/terraform/plans/plan_test.go +++ b/vendor/github.com/hashicorp/terraform/plans/plan_test.go @@ -68,3 +68,28 @@ func TestProviderAddrs(t *testing.T) { t.Error(problem) } } + +// Module outputs should not effect the result of Empty +func TestModuleOutputChangesEmpty(t *testing.T) { + changes := &Changes{ + Outputs: []*OutputChangeSrc{ + { + Addr: addrs.AbsOutputValue{ + Module: addrs.RootModuleInstance.Child("child", addrs.NoKey), + OutputValue: addrs.OutputValue{ + Name: "output", + }, + }, + ChangeSrc: ChangeSrc{ + Action: Update, + Before: []byte("a"), + After: []byte("b"), + }, + }, + }, + } + + if !changes.Empty() { + t.Fatal("plan has no visible changes") + } +} diff --git a/vendor/github.com/hashicorp/terraform/plans/planfile/planfile_test.go b/vendor/github.com/hashicorp/terraform/plans/planfile/planfile_test.go index 5b5abb57..765f0892 100644 --- a/vendor/github.com/hashicorp/terraform/plans/planfile/planfile_test.go +++ b/vendor/github.com/hashicorp/terraform/plans/planfile/planfile_test.go @@ -3,10 +3,9 @@ package planfile import ( "io/ioutil" "path/filepath" - "reflect" "testing" - "github.com/davecgh/go-spew/spew" + "github.com/google/go-cmp/cmp" "github.com/hashicorp/terraform/configs/configload" "github.com/hashicorp/terraform/plans" @@ -33,6 +32,12 @@ func TestRoundtrip(t *testing.T) { // We don't need to test the entire thing because the state file // serialization is already tested in its own package. stateFileIn := &statefile.File{ + TerraformVersion: tfversion.SemVer, + Serial: 2, + Lineage: "abc123", + State: states.NewState(), + } + prevStateFileIn := &statefile.File{ TerraformVersion: tfversion.SemVer, Serial: 1, Lineage: "abc123", @@ -55,6 +60,15 @@ func TestRoundtrip(t *testing.T) { Config: plans.DynamicValue([]byte("config placeholder")), Workspace: "default", }, + + // Due to some historical oddities in how we've changed modelling over + // time, we also include the states (without the corresponding file + // headers) in the plans.Plan object. This is currently ignored by + // Create but will be returned by ReadPlan and so we need to include + // it here so that we'll get a match when we compare input and output + // below. + PrevRunState: prevStateFileIn.State, + PriorState: stateFileIn.State, } workDir, err := ioutil.TempDir("", "tf-planfile") @@ -63,7 +77,7 @@ func TestRoundtrip(t *testing.T) { } planFn := filepath.Join(workDir, "tfplan") - err = Create(planFn, snapIn, stateFileIn, planIn) + err = Create(planFn, snapIn, prevStateFileIn, stateFileIn, planIn) if err != nil { t.Fatalf("failed to create plan file: %s", err) } @@ -78,8 +92,8 @@ func TestRoundtrip(t *testing.T) { if err != nil { t.Fatalf("failed to read plan: %s", err) } - if !reflect.DeepEqual(planIn, planOut) { - t.Errorf("plan did not survive round-trip\nresult: %sinput: %s", spew.Sdump(planOut), spew.Sdump(planIn)) + if diff := cmp.Diff(planIn, planOut); diff != "" { + t.Errorf("plan did not survive round-trip\n%s", diff) } }) @@ -88,8 +102,18 @@ func TestRoundtrip(t *testing.T) { if err != nil { t.Fatalf("failed to read state: %s", err) } - if !reflect.DeepEqual(stateFileIn, stateFileOut) { - t.Errorf("state file did not survive round-trip\nresult: %sinput: %s", spew.Sdump(stateFileOut), spew.Sdump(stateFileIn)) + if diff := cmp.Diff(stateFileIn, stateFileOut); diff != "" { + t.Errorf("state file did not survive round-trip\n%s", diff) + } + }) + + t.Run("ReadPrevStateFile", func(t *testing.T) { + prevStateFileOut, err := pr.ReadPrevStateFile() + if err != nil { + t.Fatalf("failed to read state: %s", err) + } + if diff := cmp.Diff(prevStateFileIn, prevStateFileOut); diff != "" { + t.Errorf("state file did not survive round-trip\n%s", diff) } }) @@ -98,8 +122,8 @@ func TestRoundtrip(t *testing.T) { if err != nil { t.Fatalf("failed to read config snapshot: %s", err) } - if !reflect.DeepEqual(snapIn, snapOut) { - t.Errorf("config snapshot did not survive round-trip\nresult: %sinput: %s", spew.Sdump(snapOut), spew.Sdump(snapIn)) + if diff := cmp.Diff(snapIn, snapOut); diff != "" { + t.Errorf("config snapshot did not survive round-trip\n%s", diff) } }) diff --git a/vendor/github.com/hashicorp/terraform/plans/planfile/reader.go b/vendor/github.com/hashicorp/terraform/plans/planfile/reader.go index 579e2859..83b17ae9 100644 --- a/vendor/github.com/hashicorp/terraform/plans/planfile/reader.go +++ b/vendor/github.com/hashicorp/terraform/plans/planfile/reader.go @@ -14,6 +14,7 @@ import ( ) const tfstateFilename = "tfstate" +const tfstatePreviousFilename = "tfstate-prev" // Reader is the main type used to read plan files. Create a Reader by calling // Open. @@ -87,10 +88,39 @@ func (r *Reader) ReadPlan() (*plans.Plan, error) { } defer pr.Close() - return readTfplan(pr) + // There's a slight mismatch in how plans.Plan is modeled vs. how + // the underlying plan file format works, because the "tfplan" embedded + // file contains only some top-level metadata and the planned changes, + // and not the previous run or prior states. Therefore we need to + // build this up in multiple steps. + // This is some technical debt because historically we considered the + // planned changes and prior state as totally separate, but later realized + // that it made sense for a plans.Plan to include the prior state directly + // so we can see what state the plan applies to. Hopefully later we'll + // clean this up some more so that we don't have two different ways to + // access the prior state (this and the ReadStateFile method). + ret, err := readTfplan(pr) + if err != nil { + return nil, err + } + + prevRunStateFile, err := r.ReadPrevStateFile() + if err != nil { + return nil, fmt.Errorf("failed to read previous run state from plan file: %s", err) + } + priorStateFile, err := r.ReadStateFile() + if err != nil { + return nil, fmt.Errorf("failed to read prior state from plan file: %s", err) + } + + ret.PrevRunState = prevRunStateFile.State + ret.PriorState = priorStateFile.State + + return ret, nil } -// ReadStateFile reads the state file embedded in the plan file. +// ReadStateFile reads the state file embedded in the plan file, which +// represents the "PriorState" as defined in plans.Plan. // // If the plan file contains no embedded state file, the returned error is // statefile.ErrNoState. @@ -107,6 +137,24 @@ func (r *Reader) ReadStateFile() (*statefile.File, error) { return nil, statefile.ErrNoState } +// ReadPrevStateFile reads the previous state file embedded in the plan file, which +// represents the "PrevRunState" as defined in plans.Plan. +// +// If the plan file contains no embedded previous state file, the returned error is +// statefile.ErrNoState. +func (r *Reader) ReadPrevStateFile() (*statefile.File, error) { + for _, file := range r.zip.File { + if file.Name == tfstatePreviousFilename { + r, err := file.Open() + if err != nil { + return nil, fmt.Errorf("failed to extract previous state from plan file: %s", err) + } + return statefile.Read(r) + } + } + return nil, statefile.ErrNoState +} + // ReadConfigSnapshot reads the configuration snapshot embedded in the plan // file. // diff --git a/vendor/github.com/hashicorp/terraform/plans/planfile/testdata/test-config/.terraform/modules/child_a/child_a.tf b/vendor/github.com/hashicorp/terraform/plans/planfile/testdata/test-config/.terraform/modules/child_a/child_a.tf new file mode 100644 index 00000000..2f4d0f1a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plans/planfile/testdata/test-config/.terraform/modules/child_a/child_a.tf @@ -0,0 +1,4 @@ + +module "child_c" { + source = "./child_c" +} diff --git a/vendor/github.com/hashicorp/terraform/plans/planfile/testdata/test-config/.terraform/modules/child_a/child_c/child_c.tf b/vendor/github.com/hashicorp/terraform/plans/planfile/testdata/test-config/.terraform/modules/child_a/child_c/child_c.tf new file mode 100644 index 00000000..785d98d9 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plans/planfile/testdata/test-config/.terraform/modules/child_a/child_c/child_c.tf @@ -0,0 +1,4 @@ + +output "hello" { + value = "Hello from child_c" +} diff --git a/vendor/github.com/hashicorp/terraform/plans/planfile/testdata/test-config/.terraform/modules/child_b.child_d/child_d.tf b/vendor/github.com/hashicorp/terraform/plans/planfile/testdata/test-config/.terraform/modules/child_b.child_d/child_d.tf new file mode 100644 index 00000000..145576a3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plans/planfile/testdata/test-config/.terraform/modules/child_b.child_d/child_d.tf @@ -0,0 +1,4 @@ + +output "hello" { + value = "Hello from child_d" +} diff --git a/vendor/github.com/hashicorp/terraform/plans/planfile/testdata/test-config/.terraform/modules/child_b/child_b.tf b/vendor/github.com/hashicorp/terraform/plans/planfile/testdata/test-config/.terraform/modules/child_b/child_b.tf new file mode 100644 index 00000000..4a1b247d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plans/planfile/testdata/test-config/.terraform/modules/child_b/child_b.tf @@ -0,0 +1,5 @@ + +module "child_d" { + source = "example.com/foo/bar_d/baz" + # Intentionally no version here +} diff --git a/vendor/github.com/hashicorp/terraform/plans/planfile/testdata/test-config/.terraform/modules/modules.json b/vendor/github.com/hashicorp/terraform/plans/planfile/testdata/test-config/.terraform/modules/modules.json new file mode 100644 index 00000000..ba691877 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plans/planfile/testdata/test-config/.terraform/modules/modules.json @@ -0,0 +1,32 @@ +{ + "Modules": [ + { + "Key": "", + "Source": "", + "Dir": "testdata/test-config" + }, + { + "Key": "child_a", + "Source": "example.com/foo/bar_a/baz", + "Version": "1.0.1", + "Dir": "testdata/test-config/.terraform/modules/child_a" + }, + { + "Key": "child_b", + "Source": "example.com/foo/bar_b/baz", + "Version": "1.0.0", + "Dir": "testdata/test-config/.terraform/modules/child_b" + }, + { + "Key": "child_a.child_c", + "Source": "./child_c", + "Dir": "testdata/test-config/.terraform/modules/child_a/child_c" + }, + { + "Key": "child_b.child_d", + "Source": "example.com/foo/bar_d/baz", + "Version": "1.2.0", + "Dir": "testdata/test-config/.terraform/modules/child_b.child_d" + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform/plans/planfile/tfplan.go b/vendor/github.com/hashicorp/terraform/plans/planfile/tfplan.go index e1deeb08..66fc8141 100644 --- a/vendor/github.com/hashicorp/terraform/plans/planfile/tfplan.go +++ b/vendor/github.com/hashicorp/terraform/plans/planfile/tfplan.go @@ -5,7 +5,7 @@ import ( "io" "io/ioutil" - "github.com/golang/protobuf/proto" + "google.golang.org/protobuf/proto" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/plans" @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/tfdiags" "github.com/hashicorp/terraform/version" + "github.com/zclconf/go-cty/cty" ) const tfplanFormatVersion = 3 @@ -59,6 +60,17 @@ func readTfplan(r io.Reader) (*plans.Plan, error) { ProviderSHA256s: map[string][]byte{}, } + switch rawPlan.UiMode { + case planproto.Mode_NORMAL: + plan.UIMode = plans.NormalMode + case planproto.Mode_DESTROY: + plan.UIMode = plans.DestroyMode + case planproto.Mode_REFRESH_ONLY: + plan.UIMode = plans.RefreshOnlyMode + default: + return nil, fmt.Errorf("plan has invalid mode %s", rawPlan.UiMode) + } + for _, rawOC := range rawPlan.OutputChanges { name := rawOC.Name change, err := changeFromTfplan(rawOC.Change) @@ -94,6 +106,14 @@ func readTfplan(r io.Reader) (*plans.Plan, error) { plan.TargetAddrs = append(plan.TargetAddrs, target.Subject) } + for _, rawReplaceAddr := range rawPlan.ForceReplaceAddrs { + addr, diags := addrs.ParseAbsResourceInstanceStr(rawReplaceAddr) + if diags.HasErrors() { + return nil, fmt.Errorf("plan contains invalid force-replace address %q: %s", addr, diags.Err()) + } + plan.ForceReplaceAddrs = append(plan.ForceReplaceAddrs, addr) + } + for name, rawHashObj := range rawPlan.ProviderHashes { if len(rawHashObj.Sha256) == 0 { return nil, fmt.Errorf("no SHA256 hash for provider %q plugin", name) @@ -190,6 +210,15 @@ func resourceChangeFromTfplan(rawChange *planproto.ResourceInstanceChange) (*pla ret.DeposedKey = states.DeposedKey(rawChange.DeposedKey) } + ret.RequiredReplace = cty.NewPathSet() + for _, p := range rawChange.RequiredReplace { + path, err := pathFromTfplan(p) + if err != nil { + return nil, fmt.Errorf("invalid path in required replace: %s", err) + } + ret.RequiredReplace.Add(path) + } + change, err := changeFromTfplan(rawChange.Change) if err != nil { return nil, fmt.Errorf("invalid plan for resource %s: %s", ret.Addr, err) @@ -197,6 +226,19 @@ func resourceChangeFromTfplan(rawChange *planproto.ResourceInstanceChange) (*pla ret.ChangeSrc = *change + switch rawChange.ActionReason { + case planproto.ResourceInstanceActionReason_NONE: + ret.ActionReason = plans.ResourceInstanceChangeNoReason + case planproto.ResourceInstanceActionReason_REPLACE_BECAUSE_CANNOT_UPDATE: + ret.ActionReason = plans.ResourceInstanceReplaceBecauseCannotUpdate + case planproto.ResourceInstanceActionReason_REPLACE_BECAUSE_TAINTED: + ret.ActionReason = plans.ResourceInstanceReplaceBecauseTainted + case planproto.ResourceInstanceActionReason_REPLACE_BY_REQUEST: + ret.ActionReason = plans.ResourceInstanceReplaceByRequest + default: + return nil, fmt.Errorf("resource has invalid action reason %s", rawChange.ActionReason) + } + if len(rawChange.Private) != 0 { ret.Private = rawChange.Private } @@ -273,6 +315,22 @@ func changeFromTfplan(rawChange *planproto.Change) (*plans.ChangeSrc, error) { } } + sensitive := cty.NewValueMarks("sensitive") + beforeValMarks, err := pathValueMarksFromTfplan(rawChange.BeforeSensitivePaths, sensitive) + if err != nil { + return nil, fmt.Errorf("failed to decode before sensitive paths: %s", err) + } + afterValMarks, err := pathValueMarksFromTfplan(rawChange.AfterSensitivePaths, sensitive) + if err != nil { + return nil, fmt.Errorf("failed to decode after sensitive paths: %s", err) + } + if len(beforeValMarks) > 0 { + ret.BeforeValMarks = beforeValMarks + } + if len(afterValMarks) > 0 { + ret.AfterValMarks = afterValMarks + } + return ret, nil } @@ -304,6 +362,17 @@ func writeTfplan(plan *plans.Plan, w io.Writer) error { ResourceChanges: []*planproto.ResourceInstanceChange{}, } + switch plan.UIMode { + case plans.NormalMode: + rawPlan.UiMode = planproto.Mode_NORMAL + case plans.DestroyMode: + rawPlan.UiMode = planproto.Mode_DESTROY + case plans.RefreshOnlyMode: + rawPlan.UiMode = planproto.Mode_REFRESH_ONLY + default: + return fmt.Errorf("plan has unsupported mode %s", plan.UIMode) + } + for _, oc := range plan.Changes.Outputs { // When serializing a plan we only retain the root outputs, since // changes to these are externally-visible side effects (e.g. via @@ -341,6 +410,10 @@ func writeTfplan(plan *plans.Plan, w io.Writer) error { rawPlan.TargetAddrs = append(rawPlan.TargetAddrs, targetAddr.String()) } + for _, replaceAddr := range plan.ForceReplaceAddrs { + rawPlan.ForceReplaceAddrs = append(rawPlan.ForceReplaceAddrs, replaceAddr.String()) + } + for name, hash := range plan.ProviderSHA256s { rawPlan.ProviderHashes[name] = &planproto.Hash{ Sha256: hash, @@ -414,12 +487,35 @@ func resourceChangeToTfplan(change *plans.ResourceInstanceChangeSrc) (*planproto ret.DeposedKey = string(change.DeposedKey) ret.Provider = change.ProviderAddr.String() + requiredReplace := change.RequiredReplace.List() + ret.RequiredReplace = make([]*planproto.Path, 0, len(requiredReplace)) + for _, p := range requiredReplace { + path, err := pathToTfplan(p) + if err != nil { + return nil, fmt.Errorf("invalid path in required replace: %s", err) + } + ret.RequiredReplace = append(ret.RequiredReplace, path) + } + valChange, err := changeToTfplan(&change.ChangeSrc) if err != nil { return nil, fmt.Errorf("failed to serialize resource %s change: %s", relAddr, err) } ret.Change = valChange + switch change.ActionReason { + case plans.ResourceInstanceChangeNoReason: + ret.ActionReason = planproto.ResourceInstanceActionReason_NONE + case plans.ResourceInstanceReplaceBecauseCannotUpdate: + ret.ActionReason = planproto.ResourceInstanceActionReason_REPLACE_BECAUSE_CANNOT_UPDATE + case plans.ResourceInstanceReplaceBecauseTainted: + ret.ActionReason = planproto.ResourceInstanceActionReason_REPLACE_BECAUSE_TAINTED + case plans.ResourceInstanceReplaceByRequest: + ret.ActionReason = planproto.ResourceInstanceActionReason_REPLACE_BY_REQUEST + default: + return nil, fmt.Errorf("resource %s has unsupported action reason %s", relAddr, change.ActionReason) + } + if len(change.Private) > 0 { ret.Private = change.Private } @@ -433,6 +529,17 @@ func changeToTfplan(change *plans.ChangeSrc) (*planproto.Change, error) { before := valueToTfplan(change.Before) after := valueToTfplan(change.After) + beforeSensitivePaths, err := pathValueMarksToTfplan(change.BeforeValMarks) + if err != nil { + return nil, err + } + afterSensitivePaths, err := pathValueMarksToTfplan(change.AfterValMarks) + if err != nil { + return nil, err + } + ret.BeforeSensitivePaths = beforeSensitivePaths + ret.AfterSensitivePaths = afterSensitivePaths + switch change.Action { case plans.NoOp: ret.Action = planproto.Action_NOOP @@ -472,3 +579,86 @@ func valueToTfplan(val plans.DynamicValue) *planproto.DynamicValue { Msgpack: []byte(val), } } + +func pathValueMarksFromTfplan(paths []*planproto.Path, marks cty.ValueMarks) ([]cty.PathValueMarks, error) { + ret := make([]cty.PathValueMarks, 0, len(paths)) + for _, p := range paths { + path, err := pathFromTfplan(p) + if err != nil { + return nil, err + } + ret = append(ret, cty.PathValueMarks{ + Path: path, + Marks: marks, + }) + } + return ret, nil +} + +func pathValueMarksToTfplan(pvm []cty.PathValueMarks) ([]*planproto.Path, error) { + ret := make([]*planproto.Path, 0, len(pvm)) + for _, p := range pvm { + path, err := pathToTfplan(p.Path) + if err != nil { + return nil, err + } + ret = append(ret, path) + } + return ret, nil +} + +func pathFromTfplan(path *planproto.Path) (cty.Path, error) { + ret := make([]cty.PathStep, 0, len(path.Steps)) + for _, step := range path.Steps { + switch s := step.Selector.(type) { + case *planproto.Path_Step_ElementKey: + dynamicVal, err := valueFromTfplan(s.ElementKey) + if err != nil { + return nil, fmt.Errorf("error decoding path index step: %s", err) + } + ty, err := dynamicVal.ImpliedType() + if err != nil { + return nil, fmt.Errorf("error determining path index type: %s", err) + } + val, err := dynamicVal.Decode(ty) + if err != nil { + return nil, fmt.Errorf("error decoding path index value: %s", err) + } + ret = append(ret, cty.IndexStep{Key: val}) + case *planproto.Path_Step_AttributeName: + ret = append(ret, cty.GetAttrStep{Name: s.AttributeName}) + default: + return nil, fmt.Errorf("Unsupported path step %t", step.Selector) + } + } + return ret, nil +} + +func pathToTfplan(path cty.Path) (*planproto.Path, error) { + steps := make([]*planproto.Path_Step, 0, len(path)) + for _, step := range path { + switch s := step.(type) { + case cty.IndexStep: + value, err := plans.NewDynamicValue(s.Key, s.Key.Type()) + if err != nil { + return nil, fmt.Errorf("Error encoding path step: %s", err) + } + steps = append(steps, &planproto.Path_Step{ + Selector: &planproto.Path_Step_ElementKey{ + ElementKey: valueToTfplan(value), + }, + }) + case cty.GetAttrStep: + steps = append(steps, &planproto.Path_Step{ + Selector: &planproto.Path_Step_AttributeName{ + AttributeName: s.Name, + }, + }) + default: + return nil, fmt.Errorf("Unsupported path step %#v (%t)", step, step) + } + } + return &planproto.Path{ + Steps: steps, + }, nil +} diff --git a/vendor/github.com/hashicorp/terraform/plans/planfile/tfplan_test.go b/vendor/github.com/hashicorp/terraform/plans/planfile/tfplan_test.go index ffd6d1c4..8e8b4d7d 100644 --- a/vendor/github.com/hashicorp/terraform/plans/planfile/tfplan_test.go +++ b/vendor/github.com/hashicorp/terraform/plans/planfile/tfplan_test.go @@ -64,11 +64,28 @@ func TestTFPlanRoundTrip(t *testing.T) { Action: plans.DeleteThenCreate, Before: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("foo-bar-baz"), + "boop": cty.ListVal([]cty.Value{ + cty.StringVal("beep"), + }), }), objTy), After: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{ "id": cty.UnknownVal(cty.String), + "boop": cty.ListVal([]cty.Value{ + cty.StringVal("beep"), + cty.StringVal("honk"), + }), }), objTy), + AfterValMarks: []cty.PathValueMarks{ + { + Path: cty.GetAttrPath("boop").IndexInt(1), + Marks: cty.NewValueMarks("sensitive"), + }, + }, }, + RequiredReplace: cty.NewPathSet( + cty.GetAttrPath("boop"), + ), + ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, }, { Addr: addrs.Resource{ diff --git a/vendor/github.com/hashicorp/terraform/plans/planfile/writer.go b/vendor/github.com/hashicorp/terraform/plans/planfile/writer.go index 7759463b..8fdf7a56 100644 --- a/vendor/github.com/hashicorp/terraform/plans/planfile/writer.go +++ b/vendor/github.com/hashicorp/terraform/plans/planfile/writer.go @@ -18,7 +18,7 @@ import ( // state file in addition to the plan itself, so that Terraform can detect // if the world has changed since the plan was created and thus refuse to // apply it. -func Create(filename string, configSnap *configload.Snapshot, stateFile *statefile.File, plan *plans.Plan) error { +func Create(filename string, configSnap *configload.Snapshot, prevStateFile, stateFile *statefile.File, plan *plans.Plan) error { f, err := os.Create(filename) if err != nil { return err @@ -60,6 +60,22 @@ func Create(filename string, configSnap *configload.Snapshot, stateFile *statefi } } + // tfstate-prev file + { + w, err := zw.CreateHeader(&zip.FileHeader{ + Name: tfstatePreviousFilename, + Method: zip.Deflate, + Modified: time.Now(), + }) + if err != nil { + return fmt.Errorf("failed to create embedded tfstate-prev file: %s", err) + } + err = statefile.Write(prevStateFile, w) + if err != nil { + return fmt.Errorf("failed to write previous state snapshot: %s", err) + } + } + // tfconfig directory { err := writeConfigSnapshot(configSnap, zw) diff --git a/vendor/github.com/hashicorp/terraform/plans/resourceinstancechangeactionreason_string.go b/vendor/github.com/hashicorp/terraform/plans/resourceinstancechangeactionreason_string.go new file mode 100644 index 00000000..0731f675 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plans/resourceinstancechangeactionreason_string.go @@ -0,0 +1,37 @@ +// Code generated by "stringer -type=ResourceInstanceChangeActionReason changes.go"; DO NOT EDIT. + +package plans + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[ResourceInstanceChangeNoReason-0] + _ = x[ResourceInstanceReplaceBecauseTainted-84] + _ = x[ResourceInstanceReplaceByRequest-82] + _ = x[ResourceInstanceReplaceBecauseCannotUpdate-70] +} + +const ( + _ResourceInstanceChangeActionReason_name_0 = "ResourceInstanceChangeNoReason" + _ResourceInstanceChangeActionReason_name_1 = "ResourceInstanceReplaceBecauseCannotUpdate" + _ResourceInstanceChangeActionReason_name_2 = "ResourceInstanceReplaceByRequest" + _ResourceInstanceChangeActionReason_name_3 = "ResourceInstanceReplaceBecauseTainted" +) + +func (i ResourceInstanceChangeActionReason) String() string { + switch { + case i == 0: + return _ResourceInstanceChangeActionReason_name_0 + case i == 70: + return _ResourceInstanceChangeActionReason_name_1 + case i == 82: + return _ResourceInstanceChangeActionReason_name_2 + case i == 84: + return _ResourceInstanceChangeActionReason_name_3 + default: + return "ResourceInstanceChangeActionReason(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/vendor/github.com/hashicorp/terraform/plugin/convert/diagnostics.go b/vendor/github.com/hashicorp/terraform/plugin/convert/diagnostics.go index 51cb2fe2..7eca3288 100644 --- a/vendor/github.com/hashicorp/terraform/plugin/convert/diagnostics.go +++ b/vendor/github.com/hashicorp/terraform/plugin/convert/diagnostics.go @@ -65,7 +65,7 @@ func ProtoToDiagnostics(ds []*proto.Diagnostic) tfdiags.Diagnostics { var newDiag tfdiags.Diagnostic // if there's an attribute path, we need to create a AttributeValue diagnostic - if d.Attribute != nil { + if d.Attribute != nil && len(d.Attribute.Steps) > 0 { path := AttributePathToPath(d.Attribute) newDiag = tfdiags.AttributeValue(severity, d.Summary, d.Detail, path) } else { diff --git a/vendor/github.com/hashicorp/terraform/plugin/convert/diagnostics_test.go b/vendor/github.com/hashicorp/terraform/plugin/convert/diagnostics_test.go index 5825269a..00c0bf05 100644 --- a/vendor/github.com/hashicorp/terraform/plugin/convert/diagnostics_test.go +++ b/vendor/github.com/hashicorp/terraform/plugin/convert/diagnostics_test.go @@ -5,11 +5,21 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclsyntax" proto "github.com/hashicorp/terraform/internal/tfplugin5" "github.com/hashicorp/terraform/tfdiags" "github.com/zclconf/go-cty/cty" ) +var ignoreUnexported = cmpopts.IgnoreUnexported( + proto.Diagnostic{}, + proto.Schema_Block{}, + proto.Schema_NestedBlock{}, + proto.Schema_Attribute{}, +) + func TestProtoDiagnostics(t *testing.T) { diags := WarnsAndErrsToProto( []string{ @@ -41,8 +51,8 @@ func TestProtoDiagnostics(t *testing.T) { }, } - if !cmp.Equal(expected, diags) { - t.Fatal(cmp.Diff(expected, diags)) + if !cmp.Equal(expected, diags, ignoreUnexported) { + t.Fatal(cmp.Diff(expected, diags, ignoreUnexported)) } } @@ -357,3 +367,45 @@ func TestDiagnostics(t *testing.T) { }) } } + +// Test that a diagnostic with a present but empty attribute results in a +// whole body diagnostic. We verify this by inspecting the resulting Subject +// from the diagnostic when considered in the context of a config body. +func TestProtoDiagnostics_emptyAttributePath(t *testing.T) { + protoDiags := []*proto.Diagnostic{ + { + Severity: proto.Diagnostic_ERROR, + Summary: "error 1", + Detail: "error 1 detail", + Attribute: &proto.AttributePath{ + Steps: []*proto.AttributePath_Step{ + // this slice is intentionally left empty + }, + }, + }, + } + tfDiags := ProtoToDiagnostics(protoDiags) + + testConfig := `provider "test" { + foo = "bar" +}` + f, parseDiags := hclsyntax.ParseConfig([]byte(testConfig), "test.tf", hcl.Pos{Line: 1, Column: 1}) + if parseDiags.HasErrors() { + t.Fatal(parseDiags) + } + diags := tfDiags.InConfigBody(f.Body, "") + + if len(tfDiags) != 1 { + t.Fatalf("expected 1 diag, got %d", len(tfDiags)) + } + got := diags[0].Source().Subject + want := &tfdiags.SourceRange{ + Filename: "test.tf", + Start: tfdiags.SourcePos{Line: 1, Column: 1}, + End: tfdiags.SourcePos{Line: 1, Column: 1}, + } + + if !cmp.Equal(got, want, typeComparer, valueComparer) { + t.Fatal(cmp.Diff(got, want, typeComparer, valueComparer)) + } +} diff --git a/vendor/github.com/hashicorp/terraform/plugin/convert/schema_test.go b/vendor/github.com/hashicorp/terraform/plugin/convert/schema_test.go index 8ebf0fdd..45e4d8ea 100644 --- a/vendor/github.com/hashicorp/terraform/plugin/convert/schema_test.go +++ b/vendor/github.com/hashicorp/terraform/plugin/convert/schema_test.go @@ -353,8 +353,8 @@ func TestConvertProtoSchemaBlocks(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { converted := ConfigSchemaToProto(tc.Block) - if !cmp.Equal(converted, tc.Want, typeComparer, equateEmpty) { - t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, equateEmpty)) + if !cmp.Equal(converted, tc.Want, typeComparer, equateEmpty, ignoreUnexported) { + t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, equateEmpty, ignoreUnexported)) } }) } diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/meta_set.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/meta_set.go index 3a992892..72e4ce20 100644 --- a/vendor/github.com/hashicorp/terraform/plugin/discovery/meta_set.go +++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/meta_set.go @@ -118,7 +118,7 @@ func (s PluginMetaSet) Newest() PluginMeta { panic(err) } - if first == true || version.NewerThan(winnerVersion) { + if first || version.NewerThan(winnerVersion) { winner = p winnerVersion = version first = false diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/testdata/current-style-plugins/mockos_mockarch/terraform-foo-bar_v1.0.0.exe b/vendor/github.com/hashicorp/terraform/plugin/discovery/testdata/current-style-plugins/mockos_mockarch/terraform-foo-bar_v1.0.0.exe new file mode 100644 index 00000000..e69de29b diff --git a/vendor/github.com/hashicorp/terraform/plugin/grpc_error.go b/vendor/github.com/hashicorp/terraform/plugin/grpc_error.go new file mode 100644 index 00000000..99ce8c8b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plugin/grpc_error.go @@ -0,0 +1,74 @@ +package plugin + +import ( + "fmt" + "path" + "runtime" + + "github.com/hashicorp/terraform/tfdiags" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// grpcErr extracts some known error types and formats them into better +// representations for core. This must only be called from plugin methods. +// Since we don't use RPC status errors for the plugin protocol, these do not +// contain any useful details, and we can return some text that at least +// indicates the plugin call and possible error condition. +func grpcErr(err error) (diags tfdiags.Diagnostics) { + if err == nil { + return + } + + // extract the method name from the caller. + pc, _, _, ok := runtime.Caller(1) + if !ok { + logger.Error("unknown grpc call", "error", err) + return diags.Append(err) + } + + f := runtime.FuncForPC(pc) + + // Function names will contain the full import path. Take the last + // segment, which will let users know which method was being called. + _, requestName := path.Split(f.Name()) + + // Here we can at least correlate the error in the logs to a particular binary. + logger.Error(requestName, "error", err) + + // TODO: while this expands the error codes into somewhat better messages, + // this still does not easily link the error to an actual user-recognizable + // plugin. The grpc plugin does not know its configured name, and the + // errors are in a list of diagnostics, making it hard for the caller to + // annotate the returned errors. + switch status.Code(err) { + case codes.Unavailable: + // This case is when the plugin has stopped running for some reason, + // and is usually the result of a crash. + diags = diags.Append(tfdiags.WholeContainingBody( + tfdiags.Error, + "Plugin did not respond", + fmt.Sprintf("The plugin encountered an error, and failed to respond to the %s call. "+ + "The plugin logs may contain more details.", requestName), + )) + case codes.Canceled: + diags = diags.Append(tfdiags.WholeContainingBody( + tfdiags.Error, + "Request cancelled", + fmt.Sprintf("The %s request was cancelled.", requestName), + )) + case codes.Unimplemented: + diags = diags.Append(tfdiags.WholeContainingBody( + tfdiags.Error, + "Unsupported plugin method", + fmt.Sprintf("The %s method is not supported by this plugin.", requestName), + )) + default: + diags = diags.Append(tfdiags.WholeContainingBody( + tfdiags.Error, + "Plugin error", + fmt.Sprintf("The plugin returned an unexpected error from %s: %v", requestName, err), + )) + } + return +} diff --git a/vendor/github.com/hashicorp/terraform/plugin/grpc_provider.go b/vendor/github.com/hashicorp/terraform/plugin/grpc_provider.go index 495347bf..76eb87e8 100644 --- a/vendor/github.com/hashicorp/terraform/plugin/grpc_provider.go +++ b/vendor/github.com/hashicorp/terraform/plugin/grpc_provider.go @@ -3,12 +3,12 @@ package plugin import ( "context" "errors" - "log" "sync" "github.com/zclconf/go-cty/cty" plugin "github.com/hashicorp/go-plugin" + "github.com/hashicorp/terraform/internal/logging" proto "github.com/hashicorp/terraform/internal/tfplugin5" "github.com/hashicorp/terraform/plugin/convert" "github.com/hashicorp/terraform/providers" @@ -17,6 +17,8 @@ import ( "google.golang.org/grpc" ) +var logger = logging.HCLogger() + // GRPCProviderPlugin implements plugin.GRPCPlugin for the go-plugin package. type GRPCProviderPlugin struct { plugin.Plugin @@ -37,7 +39,7 @@ func (p *GRPCProviderPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Serve // GRPCProvider handles the client, or core side of the plugin rpc connection. // The GRPCProvider methods are mostly a translation layer between the -// terraform provioders types and the grpc proto types, directly converting +// terraform providers types and the grpc proto types, directly converting // between the two. type GRPCProvider struct { // PluginClient provides a reference to the plugin.Client which controls the plugin process. @@ -58,13 +60,13 @@ type GRPCProvider struct { // schema stores the schema for this provider. This is used to properly // serialize the state for requests. mu sync.Mutex - schemas providers.GetSchemaResponse + schemas providers.GetProviderSchemaResponse } // getSchema is used internally to get the saved provider schema. The schema // should have already been fetched from the provider, but we have to // synchronize access to avoid being called concurrently with GetSchema. -func (p *GRPCProvider) getSchema() providers.GetSchemaResponse { +func (p *GRPCProvider) getSchema() providers.GetProviderSchemaResponse { p.mu.Lock() // unlock inline in case GetSchema needs to be called if p.schemas.Provider.Block != nil { @@ -76,7 +78,7 @@ func (p *GRPCProvider) getSchema() providers.GetSchemaResponse { // the schema should have been fetched already, but give it another shot // just in case things are being called out of order. This may happen for // tests. - schemas := p.GetSchema() + schemas := p.GetProviderSchema() if schemas.Diagnostics.HasErrors() { panic(schemas.Diagnostics.Err()) } @@ -113,8 +115,8 @@ func (p *GRPCProvider) getProviderMetaSchema() providers.Schema { return schema.ProviderMeta } -func (p *GRPCProvider) GetSchema() (resp providers.GetSchemaResponse) { - log.Printf("[TRACE] GRPCProvider: GetSchema") +func (p *GRPCProvider) GetProviderSchema() (resp providers.GetProviderSchemaResponse) { + logger.Trace("GRPCProvider: GetProviderSchema") p.mu.Lock() defer p.mu.Unlock() @@ -133,7 +135,7 @@ func (p *GRPCProvider) GetSchema() (resp providers.GetSchemaResponse) { const maxRecvSize = 64 << 20 protoResp, err := p.client.GetSchema(p.ctx, new(proto.GetProviderSchema_Request), grpc.MaxRecvMsgSizeCallOption{MaxRecvMsgSize: maxRecvSize}) if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) return resp } @@ -146,7 +148,7 @@ func (p *GRPCProvider) GetSchema() (resp providers.GetSchemaResponse) { resp.Provider = convert.ProtoToProviderSchema(protoResp.Provider) if protoResp.ProviderMeta == nil { - log.Printf("[TRACE] No provider meta schema returned") + logger.Debug("No provider meta schema returned") } else { resp.ProviderMeta = convert.ProtoToProviderSchema(protoResp.ProviderMeta) } @@ -164,8 +166,8 @@ func (p *GRPCProvider) GetSchema() (resp providers.GetSchemaResponse) { return resp } -func (p *GRPCProvider) PrepareProviderConfig(r providers.PrepareProviderConfigRequest) (resp providers.PrepareProviderConfigResponse) { - log.Printf("[TRACE] GRPCProvider: PrepareProviderConfig") +func (p *GRPCProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) { + logger.Trace("GRPCProvider: ValidateProviderConfig") schema := p.getSchema() ty := schema.Provider.Block.ImpliedType() @@ -182,7 +184,7 @@ func (p *GRPCProvider) PrepareProviderConfig(r providers.PrepareProviderConfigRe protoResp, err := p.client.PrepareProviderConfig(p.ctx, protoReq) if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) return resp } @@ -197,8 +199,8 @@ func (p *GRPCProvider) PrepareProviderConfig(r providers.PrepareProviderConfigRe return resp } -func (p *GRPCProvider) ValidateResourceTypeConfig(r providers.ValidateResourceTypeConfigRequest) (resp providers.ValidateResourceTypeConfigResponse) { - log.Printf("[TRACE] GRPCProvider: ValidateResourceTypeConfig") +func (p *GRPCProvider) ValidateResourceConfig(r providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) { + logger.Trace("GRPCProvider: ValidateResourceConfig") resourceSchema := p.getResourceSchema(r.TypeName) mp, err := msgpack.Marshal(r.Config, resourceSchema.Block.ImpliedType()) @@ -214,7 +216,7 @@ func (p *GRPCProvider) ValidateResourceTypeConfig(r providers.ValidateResourceTy protoResp, err := p.client.ValidateResourceTypeConfig(p.ctx, protoReq) if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) return resp } @@ -222,8 +224,8 @@ func (p *GRPCProvider) ValidateResourceTypeConfig(r providers.ValidateResourceTy return resp } -func (p *GRPCProvider) ValidateDataSourceConfig(r providers.ValidateDataSourceConfigRequest) (resp providers.ValidateDataSourceConfigResponse) { - log.Printf("[TRACE] GRPCProvider: ValidateDataSourceConfig") +func (p *GRPCProvider) ValidateDataResourceConfig(r providers.ValidateDataResourceConfigRequest) (resp providers.ValidateDataResourceConfigResponse) { + logger.Trace("GRPCProvider: ValidateDataResourceConfig") dataSchema := p.getDatasourceSchema(r.TypeName) @@ -240,7 +242,7 @@ func (p *GRPCProvider) ValidateDataSourceConfig(r providers.ValidateDataSourceCo protoResp, err := p.client.ValidateDataSourceConfig(p.ctx, protoReq) if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) return resp } resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) @@ -248,7 +250,7 @@ func (p *GRPCProvider) ValidateDataSourceConfig(r providers.ValidateDataSourceCo } func (p *GRPCProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) { - log.Printf("[TRACE] GRPCProvider: UpgradeResourceState") + logger.Trace("GRPCProvider: UpgradeResourceState") resSchema := p.getResourceSchema(r.TypeName) @@ -263,7 +265,7 @@ func (p *GRPCProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequ protoResp, err := p.client.UpgradeResourceState(p.ctx, protoReq) if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) return resp } resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) @@ -284,8 +286,8 @@ func (p *GRPCProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequ return resp } -func (p *GRPCProvider) Configure(r providers.ConfigureRequest) (resp providers.ConfigureResponse) { - log.Printf("[TRACE] GRPCProvider: Configure") +func (p *GRPCProvider) ConfigureProvider(r providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { + logger.Trace("GRPCProvider: ConfigureProvider") schema := p.getSchema() @@ -307,7 +309,7 @@ func (p *GRPCProvider) Configure(r providers.ConfigureRequest) (resp providers.C protoResp, err := p.client.Configure(p.ctx, protoReq) if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) return resp } resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) @@ -315,7 +317,7 @@ func (p *GRPCProvider) Configure(r providers.ConfigureRequest) (resp providers.C } func (p *GRPCProvider) Stop() error { - log.Printf("[TRACE] GRPCProvider: Stop") + logger.Trace("GRPCProvider: Stop") resp, err := p.client.Stop(p.ctx, new(proto.Stop_Request)) if err != nil { @@ -329,7 +331,7 @@ func (p *GRPCProvider) Stop() error { } func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp providers.ReadResourceResponse) { - log.Printf("[TRACE] GRPCProvider: ReadResource") + logger.Trace("GRPCProvider: ReadResource") resSchema := p.getResourceSchema(r.TypeName) metaSchema := p.getProviderMetaSchema() @@ -357,7 +359,7 @@ func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp provi protoResp, err := p.client.ReadResource(p.ctx, protoReq) if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) return resp } resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) @@ -374,7 +376,7 @@ func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp provi } func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { - log.Printf("[TRACE] GRPCProvider: PlanResourceChange") + logger.Trace("GRPCProvider: PlanResourceChange") resSchema := p.getResourceSchema(r.TypeName) metaSchema := p.getProviderMetaSchema() @@ -416,7 +418,7 @@ func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) protoResp, err := p.client.PlanResourceChange(p.ctx, protoReq) if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) return resp } resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) @@ -440,7 +442,7 @@ func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) } func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { - log.Printf("[TRACE] GRPCProvider: ApplyResourceChange") + logger.Trace("GRPCProvider: ApplyResourceChange") resSchema := p.getResourceSchema(r.TypeName) metaSchema := p.getProviderMetaSchema() @@ -480,7 +482,7 @@ func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeReques protoResp, err := p.client.ApplyResourceChange(p.ctx, protoReq) if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) return resp } resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) @@ -500,7 +502,7 @@ func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeReques } func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateRequest) (resp providers.ImportResourceStateResponse) { - log.Printf("[TRACE] GRPCProvider: ImportResourceState") + logger.Trace("GRPCProvider: ImportResourceState") protoReq := &proto.ImportResourceState_Request{ TypeName: r.TypeName, @@ -509,7 +511,7 @@ func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateReques protoResp, err := p.client.ImportResourceState(p.ctx, protoReq) if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) return resp } resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) @@ -534,7 +536,7 @@ func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateReques } func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { - log.Printf("[TRACE] GRPCProvider: ReadDataSource") + logger.Trace("GRPCProvider: ReadDataSource") dataSchema := p.getDatasourceSchema(r.TypeName) metaSchema := p.getProviderMetaSchema() @@ -563,7 +565,7 @@ func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp p protoResp, err := p.client.ReadDataSource(p.ctx, protoReq) if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) return resp } resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) @@ -580,7 +582,7 @@ func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp p // closing the grpc connection is final, and terraform will call it at the end of every phase. func (p *GRPCProvider) Close() error { - log.Printf("[TRACE] GRPCProvider: Close") + logger.Trace("GRPCProvider: Close") // Make sure to stop the server if we're not running within go-plugin. if p.TestServer != nil { @@ -592,7 +594,7 @@ func (p *GRPCProvider) Close() error { // where the factory is built and is the only point with access to the // plugin.Client. if p.PluginClient == nil { - log.Println("[DEBUG] provider has no plugin.Client") + logger.Debug("provider has no plugin.Client") return nil } diff --git a/vendor/github.com/hashicorp/terraform/plugin/grpc_provider_test.go b/vendor/github.com/hashicorp/terraform/plugin/grpc_provider_test.go index 9cf2dca3..b94f9471 100644 --- a/vendor/github.com/hashicorp/terraform/plugin/grpc_provider_test.go +++ b/vendor/github.com/hashicorp/terraform/plugin/grpc_provider_test.go @@ -88,7 +88,7 @@ func TestGRPCProvider_GetSchema(t *testing.T) { client: mockProviderClient(t), } - resp := p.GetSchema() + resp := p.GetProviderSchema() checkDiags(t, resp.Diagnostics) } @@ -104,11 +104,11 @@ func TestGRPCProvider_PrepareProviderConfig(t *testing.T) { ).Return(&proto.PrepareProviderConfig_Response{}, nil) cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"}) - resp := p.PrepareProviderConfig(providers.PrepareProviderConfigRequest{Config: cfg}) + resp := p.ValidateProviderConfig(providers.ValidateProviderConfigRequest{Config: cfg}) checkDiags(t, resp.Diagnostics) } -func TestGRPCProvider_ValidateResourceTypeConfig(t *testing.T) { +func TestGRPCProvider_ValidateResourceConfig(t *testing.T) { client := mockProviderClient(t) p := &GRPCProvider{ client: client, @@ -120,7 +120,7 @@ func TestGRPCProvider_ValidateResourceTypeConfig(t *testing.T) { ).Return(&proto.ValidateResourceTypeConfig_Response{}, nil) cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"}) - resp := p.ValidateResourceTypeConfig(providers.ValidateResourceTypeConfigRequest{ + resp := p.ValidateResourceConfig(providers.ValidateResourceConfigRequest{ TypeName: "resource", Config: cfg, }) @@ -139,7 +139,7 @@ func TestGRPCProvider_ValidateDataSourceConfig(t *testing.T) { ).Return(&proto.ValidateDataSourceConfig_Response{}, nil) cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"}) - resp := p.ValidateDataSourceConfig(providers.ValidateDataSourceConfigRequest{ + resp := p.ValidateDataResourceConfig(providers.ValidateDataResourceConfigRequest{ TypeName: "data", Config: cfg, }) @@ -219,7 +219,7 @@ func TestGRPCProvider_Configure(t *testing.T) { gomock.Any(), ).Return(&proto.Configure_Response{}, nil) - resp := p.Configure(providers.ConfigureRequest{ + resp := p.ConfigureProvider(providers.ConfigureProviderRequest{ Config: cty.ObjectVal(map[string]cty.Value{ "attr": cty.StringVal("foo"), }), diff --git a/vendor/github.com/hashicorp/terraform/plugin/grpc_provisioner.go b/vendor/github.com/hashicorp/terraform/plugin/grpc_provisioner.go index 136c88d6..c57daa9a 100644 --- a/vendor/github.com/hashicorp/terraform/plugin/grpc_provisioner.go +++ b/vendor/github.com/hashicorp/terraform/plugin/grpc_provisioner.go @@ -4,7 +4,6 @@ import ( "context" "errors" "io" - "log" "sync" plugin "github.com/hashicorp/go-plugin" @@ -61,7 +60,7 @@ func (p *GRPCProvisioner) GetSchema() (resp provisioners.GetSchemaResponse) { protoResp, err := p.client.GetSchema(p.ctx, new(proto.GetProvisionerSchema_Request)) if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) return resp } resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) @@ -96,7 +95,7 @@ func (p *GRPCProvisioner) ValidateProvisionerConfig(r provisioners.ValidateProvi } protoResp, err := p.client.ValidateProvisionerConfig(p.ctx, protoReq) if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) return resp } resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) @@ -130,7 +129,7 @@ func (p *GRPCProvisioner) ProvisionResource(r provisioners.ProvisionResourceRequ outputClient, err := p.client.ProvisionResource(p.ctx, protoReq) if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) return resp } @@ -169,7 +168,7 @@ func (p *GRPCProvisioner) Stop() error { func (p *GRPCProvisioner) Close() error { // check this since it's not automatically inserted during plugin creation if p.PluginClient == nil { - log.Println("[DEBUG] provider has no plugin.Client") + logger.Debug("provisioner has no plugin.Client") return nil } diff --git a/vendor/github.com/hashicorp/terraform/plugin/plugin.go b/vendor/github.com/hashicorp/terraform/plugin/plugin.go index e4fb5776..3f962f11 100644 --- a/vendor/github.com/hashicorp/terraform/plugin/plugin.go +++ b/vendor/github.com/hashicorp/terraform/plugin/plugin.go @@ -2,13 +2,18 @@ package plugin import ( "github.com/hashicorp/go-plugin" + "github.com/hashicorp/terraform/plugin6" ) -// See serve.go for serving plugins - +// VersionedPlugins includes both protocol 5 and 6 because this is the function +// called in providerFactory (command/meta_providers.go) to set up the initial +// plugin client config. var VersionedPlugins = map[int]plugin.PluginSet{ 5: { "provider": &GRPCProviderPlugin{}, "provisioner": &GRPCProvisionerPlugin{}, }, + 6: { + "provider": &plugin6.GRPCProviderPlugin{}, + }, } diff --git a/vendor/github.com/hashicorp/terraform/plugin/plugin_test.go b/vendor/github.com/hashicorp/terraform/plugin/plugin_test.go deleted file mode 100644 index ddef40ab..00000000 --- a/vendor/github.com/hashicorp/terraform/plugin/plugin_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package plugin - -import ( - "github.com/hashicorp/terraform/terraform" -) - -func testProviderFixed(p terraform.ResourceProvider) ProviderFunc { - return func() terraform.ResourceProvider { - return p - } -} - -func testProvisionerFixed(p terraform.ResourceProvisioner) ProvisionerFunc { - return func() terraform.ResourceProvisioner { - return p - } -} diff --git a/vendor/github.com/hashicorp/terraform/plugin/resource_provider.go b/vendor/github.com/hashicorp/terraform/plugin/resource_provider.go deleted file mode 100644 index a9d52058..00000000 --- a/vendor/github.com/hashicorp/terraform/plugin/resource_provider.go +++ /dev/null @@ -1,620 +0,0 @@ -package plugin - -import ( - "net/rpc" - - plugin "github.com/hashicorp/go-plugin" - "github.com/hashicorp/terraform/terraform" -) - -// ResourceProviderPlugin is the plugin.Plugin implementation. -type ResourceProviderPlugin struct { - ResourceProvider func() terraform.ResourceProvider -} - -func (p *ResourceProviderPlugin) Server(b *plugin.MuxBroker) (interface{}, error) { - return &ResourceProviderServer{ - Broker: b, - Provider: p.ResourceProvider(), - }, nil -} - -func (p *ResourceProviderPlugin) Client( - b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { - return &ResourceProvider{Broker: b, Client: c}, nil -} - -// ResourceProvider is an implementation of terraform.ResourceProvider -// that communicates over RPC. -type ResourceProvider struct { - Broker *plugin.MuxBroker - Client *rpc.Client -} - -func (p *ResourceProvider) Stop() error { - var resp ResourceProviderStopResponse - err := p.Client.Call("Plugin.Stop", new(interface{}), &resp) - if err != nil { - return err - } - if resp.Error != nil { - err = resp.Error - } - - return err -} - -func (p *ResourceProvider) GetSchema(req *terraform.ProviderSchemaRequest) (*terraform.ProviderSchema, error) { - var result ResourceProviderGetSchemaResponse - args := &ResourceProviderGetSchemaArgs{ - Req: req, - } - - err := p.Client.Call("Plugin.GetSchema", args, &result) - if err != nil { - return nil, err - } - - if result.Error != nil { - err = result.Error - } - - return result.Schema, err -} - -func (p *ResourceProvider) Input( - input terraform.UIInput, - c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) { - id := p.Broker.NextId() - go p.Broker.AcceptAndServe(id, &UIInputServer{ - UIInput: input, - }) - - var resp ResourceProviderInputResponse - args := ResourceProviderInputArgs{ - InputId: id, - Config: c, - } - - err := p.Client.Call("Plugin.Input", &args, &resp) - if err != nil { - return nil, err - } - if resp.Error != nil { - err = resp.Error - return nil, err - } - - return resp.Config, nil -} - -func (p *ResourceProvider) Validate(c *terraform.ResourceConfig) ([]string, []error) { - var resp ResourceProviderValidateResponse - args := ResourceProviderValidateArgs{ - Config: c, - } - - err := p.Client.Call("Plugin.Validate", &args, &resp) - if err != nil { - return nil, []error{err} - } - - var errs []error - if len(resp.Errors) > 0 { - errs = make([]error, len(resp.Errors)) - for i, err := range resp.Errors { - errs[i] = err - } - } - - return resp.Warnings, errs -} - -func (p *ResourceProvider) ValidateResource( - t string, c *terraform.ResourceConfig) ([]string, []error) { - var resp ResourceProviderValidateResourceResponse - args := ResourceProviderValidateResourceArgs{ - Config: c, - Type: t, - } - - err := p.Client.Call("Plugin.ValidateResource", &args, &resp) - if err != nil { - return nil, []error{err} - } - - var errs []error - if len(resp.Errors) > 0 { - errs = make([]error, len(resp.Errors)) - for i, err := range resp.Errors { - errs[i] = err - } - } - - return resp.Warnings, errs -} - -func (p *ResourceProvider) Configure(c *terraform.ResourceConfig) error { - var resp ResourceProviderConfigureResponse - err := p.Client.Call("Plugin.Configure", c, &resp) - if err != nil { - return err - } - if resp.Error != nil { - err = resp.Error - } - - return err -} - -func (p *ResourceProvider) Apply( - info *terraform.InstanceInfo, - s *terraform.InstanceState, - d *terraform.InstanceDiff) (*terraform.InstanceState, error) { - var resp ResourceProviderApplyResponse - args := &ResourceProviderApplyArgs{ - Info: info, - State: s, - Diff: d, - } - - err := p.Client.Call("Plugin.Apply", args, &resp) - if err != nil { - return nil, err - } - if resp.Error != nil { - err = resp.Error - } - - return resp.State, err -} - -func (p *ResourceProvider) Diff( - info *terraform.InstanceInfo, - s *terraform.InstanceState, - c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { - var resp ResourceProviderDiffResponse - args := &ResourceProviderDiffArgs{ - Info: info, - State: s, - Config: c, - } - err := p.Client.Call("Plugin.Diff", args, &resp) - if err != nil { - return nil, err - } - if resp.Error != nil { - err = resp.Error - } - - return resp.Diff, err -} - -func (p *ResourceProvider) ValidateDataSource( - t string, c *terraform.ResourceConfig) ([]string, []error) { - var resp ResourceProviderValidateResourceResponse - args := ResourceProviderValidateResourceArgs{ - Config: c, - Type: t, - } - - err := p.Client.Call("Plugin.ValidateDataSource", &args, &resp) - if err != nil { - return nil, []error{err} - } - - var errs []error - if len(resp.Errors) > 0 { - errs = make([]error, len(resp.Errors)) - for i, err := range resp.Errors { - errs[i] = err - } - } - - return resp.Warnings, errs -} - -func (p *ResourceProvider) Refresh( - info *terraform.InstanceInfo, - s *terraform.InstanceState) (*terraform.InstanceState, error) { - var resp ResourceProviderRefreshResponse - args := &ResourceProviderRefreshArgs{ - Info: info, - State: s, - } - - err := p.Client.Call("Plugin.Refresh", args, &resp) - if err != nil { - return nil, err - } - if resp.Error != nil { - err = resp.Error - } - - return resp.State, err -} - -func (p *ResourceProvider) ImportState( - info *terraform.InstanceInfo, - id string) ([]*terraform.InstanceState, error) { - var resp ResourceProviderImportStateResponse - args := &ResourceProviderImportStateArgs{ - Info: info, - Id: id, - } - - err := p.Client.Call("Plugin.ImportState", args, &resp) - if err != nil { - return nil, err - } - if resp.Error != nil { - err = resp.Error - } - - return resp.State, err -} - -func (p *ResourceProvider) Resources() []terraform.ResourceType { - var result []terraform.ResourceType - - err := p.Client.Call("Plugin.Resources", new(interface{}), &result) - if err != nil { - // TODO: panic, log, what? - return nil - } - - return result -} - -func (p *ResourceProvider) ReadDataDiff( - info *terraform.InstanceInfo, - c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { - var resp ResourceProviderReadDataDiffResponse - args := &ResourceProviderReadDataDiffArgs{ - Info: info, - Config: c, - } - - err := p.Client.Call("Plugin.ReadDataDiff", args, &resp) - if err != nil { - return nil, err - } - if resp.Error != nil { - err = resp.Error - } - - return resp.Diff, err -} - -func (p *ResourceProvider) ReadDataApply( - info *terraform.InstanceInfo, - d *terraform.InstanceDiff) (*terraform.InstanceState, error) { - var resp ResourceProviderReadDataApplyResponse - args := &ResourceProviderReadDataApplyArgs{ - Info: info, - Diff: d, - } - - err := p.Client.Call("Plugin.ReadDataApply", args, &resp) - if err != nil { - return nil, err - } - if resp.Error != nil { - err = resp.Error - } - - return resp.State, err -} - -func (p *ResourceProvider) DataSources() []terraform.DataSource { - var result []terraform.DataSource - - err := p.Client.Call("Plugin.DataSources", new(interface{}), &result) - if err != nil { - // TODO: panic, log, what? - return nil - } - - return result -} - -func (p *ResourceProvider) Close() error { - return p.Client.Close() -} - -// ResourceProviderServer is a net/rpc compatible structure for serving -// a ResourceProvider. This should not be used directly. -type ResourceProviderServer struct { - Broker *plugin.MuxBroker - Provider terraform.ResourceProvider -} - -type ResourceProviderStopResponse struct { - Error *plugin.BasicError -} - -type ResourceProviderGetSchemaArgs struct { - Req *terraform.ProviderSchemaRequest -} - -type ResourceProviderGetSchemaResponse struct { - Schema *terraform.ProviderSchema - Error *plugin.BasicError -} - -type ResourceProviderConfigureResponse struct { - Error *plugin.BasicError -} - -type ResourceProviderInputArgs struct { - InputId uint32 - Config *terraform.ResourceConfig -} - -type ResourceProviderInputResponse struct { - Config *terraform.ResourceConfig - Error *plugin.BasicError -} - -type ResourceProviderApplyArgs struct { - Info *terraform.InstanceInfo - State *terraform.InstanceState - Diff *terraform.InstanceDiff -} - -type ResourceProviderApplyResponse struct { - State *terraform.InstanceState - Error *plugin.BasicError -} - -type ResourceProviderDiffArgs struct { - Info *terraform.InstanceInfo - State *terraform.InstanceState - Config *terraform.ResourceConfig -} - -type ResourceProviderDiffResponse struct { - Diff *terraform.InstanceDiff - Error *plugin.BasicError -} - -type ResourceProviderRefreshArgs struct { - Info *terraform.InstanceInfo - State *terraform.InstanceState -} - -type ResourceProviderRefreshResponse struct { - State *terraform.InstanceState - Error *plugin.BasicError -} - -type ResourceProviderImportStateArgs struct { - Info *terraform.InstanceInfo - Id string -} - -type ResourceProviderImportStateResponse struct { - State []*terraform.InstanceState - Error *plugin.BasicError -} - -type ResourceProviderReadDataApplyArgs struct { - Info *terraform.InstanceInfo - Diff *terraform.InstanceDiff -} - -type ResourceProviderReadDataApplyResponse struct { - State *terraform.InstanceState - Error *plugin.BasicError -} - -type ResourceProviderReadDataDiffArgs struct { - Info *terraform.InstanceInfo - Config *terraform.ResourceConfig -} - -type ResourceProviderReadDataDiffResponse struct { - Diff *terraform.InstanceDiff - Error *plugin.BasicError -} - -type ResourceProviderValidateArgs struct { - Config *terraform.ResourceConfig -} - -type ResourceProviderValidateResponse struct { - Warnings []string - Errors []*plugin.BasicError -} - -type ResourceProviderValidateResourceArgs struct { - Config *terraform.ResourceConfig - Type string -} - -type ResourceProviderValidateResourceResponse struct { - Warnings []string - Errors []*plugin.BasicError -} - -func (s *ResourceProviderServer) Stop( - _ interface{}, - reply *ResourceProviderStopResponse) error { - err := s.Provider.Stop() - *reply = ResourceProviderStopResponse{ - Error: plugin.NewBasicError(err), - } - - return nil -} - -func (s *ResourceProviderServer) GetSchema( - args *ResourceProviderGetSchemaArgs, - result *ResourceProviderGetSchemaResponse, -) error { - schema, err := s.Provider.GetSchema(args.Req) - result.Schema = schema - if err != nil { - result.Error = plugin.NewBasicError(err) - } - return nil -} - -func (s *ResourceProviderServer) Input( - args *ResourceProviderInputArgs, - reply *ResourceProviderInputResponse) error { - conn, err := s.Broker.Dial(args.InputId) - if err != nil { - *reply = ResourceProviderInputResponse{ - Error: plugin.NewBasicError(err), - } - return nil - } - client := rpc.NewClient(conn) - defer client.Close() - - input := &UIInput{Client: client} - - config, err := s.Provider.Input(input, args.Config) - *reply = ResourceProviderInputResponse{ - Config: config, - Error: plugin.NewBasicError(err), - } - - return nil -} - -func (s *ResourceProviderServer) Validate( - args *ResourceProviderValidateArgs, - reply *ResourceProviderValidateResponse) error { - warns, errs := s.Provider.Validate(args.Config) - berrs := make([]*plugin.BasicError, len(errs)) - for i, err := range errs { - berrs[i] = plugin.NewBasicError(err) - } - *reply = ResourceProviderValidateResponse{ - Warnings: warns, - Errors: berrs, - } - return nil -} - -func (s *ResourceProviderServer) ValidateResource( - args *ResourceProviderValidateResourceArgs, - reply *ResourceProviderValidateResourceResponse) error { - warns, errs := s.Provider.ValidateResource(args.Type, args.Config) - berrs := make([]*plugin.BasicError, len(errs)) - for i, err := range errs { - berrs[i] = plugin.NewBasicError(err) - } - *reply = ResourceProviderValidateResourceResponse{ - Warnings: warns, - Errors: berrs, - } - return nil -} - -func (s *ResourceProviderServer) Configure( - config *terraform.ResourceConfig, - reply *ResourceProviderConfigureResponse) error { - err := s.Provider.Configure(config) - *reply = ResourceProviderConfigureResponse{ - Error: plugin.NewBasicError(err), - } - return nil -} - -func (s *ResourceProviderServer) Apply( - args *ResourceProviderApplyArgs, - result *ResourceProviderApplyResponse) error { - state, err := s.Provider.Apply(args.Info, args.State, args.Diff) - *result = ResourceProviderApplyResponse{ - State: state, - Error: plugin.NewBasicError(err), - } - return nil -} - -func (s *ResourceProviderServer) Diff( - args *ResourceProviderDiffArgs, - result *ResourceProviderDiffResponse) error { - diff, err := s.Provider.Diff(args.Info, args.State, args.Config) - *result = ResourceProviderDiffResponse{ - Diff: diff, - Error: plugin.NewBasicError(err), - } - return nil -} - -func (s *ResourceProviderServer) Refresh( - args *ResourceProviderRefreshArgs, - result *ResourceProviderRefreshResponse) error { - newState, err := s.Provider.Refresh(args.Info, args.State) - *result = ResourceProviderRefreshResponse{ - State: newState, - Error: plugin.NewBasicError(err), - } - return nil -} - -func (s *ResourceProviderServer) ImportState( - args *ResourceProviderImportStateArgs, - result *ResourceProviderImportStateResponse) error { - states, err := s.Provider.ImportState(args.Info, args.Id) - *result = ResourceProviderImportStateResponse{ - State: states, - Error: plugin.NewBasicError(err), - } - return nil -} - -func (s *ResourceProviderServer) Resources( - nothing interface{}, - result *[]terraform.ResourceType) error { - *result = s.Provider.Resources() - return nil -} - -func (s *ResourceProviderServer) ValidateDataSource( - args *ResourceProviderValidateResourceArgs, - reply *ResourceProviderValidateResourceResponse) error { - warns, errs := s.Provider.ValidateDataSource(args.Type, args.Config) - berrs := make([]*plugin.BasicError, len(errs)) - for i, err := range errs { - berrs[i] = plugin.NewBasicError(err) - } - *reply = ResourceProviderValidateResourceResponse{ - Warnings: warns, - Errors: berrs, - } - return nil -} - -func (s *ResourceProviderServer) ReadDataDiff( - args *ResourceProviderReadDataDiffArgs, - result *ResourceProviderReadDataDiffResponse) error { - diff, err := s.Provider.ReadDataDiff(args.Info, args.Config) - *result = ResourceProviderReadDataDiffResponse{ - Diff: diff, - Error: plugin.NewBasicError(err), - } - return nil -} - -func (s *ResourceProviderServer) ReadDataApply( - args *ResourceProviderReadDataApplyArgs, - result *ResourceProviderReadDataApplyResponse) error { - newState, err := s.Provider.ReadDataApply(args.Info, args.Diff) - *result = ResourceProviderReadDataApplyResponse{ - State: newState, - Error: plugin.NewBasicError(err), - } - return nil -} - -func (s *ResourceProviderServer) DataSources( - nothing interface{}, - result *[]terraform.DataSource) error { - *result = s.Provider.DataSources() - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/plugin/resource_provider_test.go b/vendor/github.com/hashicorp/terraform/plugin/resource_provider_test.go deleted file mode 100644 index 6c3cc454..00000000 --- a/vendor/github.com/hashicorp/terraform/plugin/resource_provider_test.go +++ /dev/null @@ -1,827 +0,0 @@ -package plugin - -import ( - "errors" - "reflect" - "testing" - - "github.com/hashicorp/go-plugin" - "github.com/hashicorp/terraform/terraform" -) - -func TestResourceProvider_impl(t *testing.T) { - var _ plugin.Plugin = new(ResourceProviderPlugin) - var _ terraform.ResourceProvider = new(ResourceProvider) -} - -func TestResourceProvider_stop(t *testing.T) { - // Create a mock provider - p := new(terraform.MockResourceProvider) - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - // Stop - e := provider.Stop() - if !p.StopCalled { - t.Fatal("stop should be called") - } - if e != nil { - t.Fatalf("bad: %#v", e) - } -} - -func TestResourceProvider_stopErrors(t *testing.T) { - p := new(terraform.MockResourceProvider) - p.StopReturnError = errors.New("foo") - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - // Stop - e := provider.Stop() - if !p.StopCalled { - t.Fatal("stop should be called") - } - if e == nil { - t.Fatal("should have error") - } - if e.Error() != "foo" { - t.Fatalf("bad: %s", e) - } -} - -func TestResourceProvider_input(t *testing.T) { - // Create a mock provider - p := new(terraform.MockResourceProvider) - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - input := new(terraform.MockUIInput) - - expected := &terraform.ResourceConfig{ - Raw: map[string]interface{}{"bar": "baz"}, - } - p.InputReturnConfig = expected - - // Input - config := &terraform.ResourceConfig{ - Raw: map[string]interface{}{"foo": "bar"}, - } - actual, err := provider.Input(input, config) - if !p.InputCalled { - t.Fatal("input should be called") - } - if !reflect.DeepEqual(p.InputConfig, config) { - t.Fatalf("bad: %#v", p.InputConfig) - } - if err != nil { - t.Fatalf("bad: %#v", err) - } - - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: %#v", actual) - } -} - -func TestResourceProvider_configure(t *testing.T) { - // Create a mock provider - p := new(terraform.MockResourceProvider) - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - // Configure - config := &terraform.ResourceConfig{ - Raw: map[string]interface{}{"foo": "bar"}, - } - e := provider.Configure(config) - if !p.ConfigureCalled { - t.Fatal("configure should be called") - } - if !reflect.DeepEqual(p.ConfigureConfig, config) { - t.Fatalf("bad: %#v", p.ConfigureConfig) - } - if e != nil { - t.Fatalf("bad: %#v", e) - } -} - -func TestResourceProvider_configure_errors(t *testing.T) { - p := new(terraform.MockResourceProvider) - p.ConfigureReturnError = errors.New("foo") - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - // Configure - config := &terraform.ResourceConfig{ - Raw: map[string]interface{}{"foo": "bar"}, - } - e := provider.Configure(config) - if !p.ConfigureCalled { - t.Fatal("configure should be called") - } - if !reflect.DeepEqual(p.ConfigureConfig, config) { - t.Fatalf("bad: %#v", p.ConfigureConfig) - } - if e == nil { - t.Fatal("should have error") - } - if e.Error() != "foo" { - t.Fatalf("bad: %s", e) - } -} - -func TestResourceProvider_configure_warnings(t *testing.T) { - p := new(terraform.MockResourceProvider) - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - // Configure - config := &terraform.ResourceConfig{ - Raw: map[string]interface{}{"foo": "bar"}, - } - e := provider.Configure(config) - if !p.ConfigureCalled { - t.Fatal("configure should be called") - } - if !reflect.DeepEqual(p.ConfigureConfig, config) { - t.Fatalf("bad: %#v", p.ConfigureConfig) - } - if e != nil { - t.Fatalf("bad: %#v", e) - } -} - -func TestResourceProvider_apply(t *testing.T) { - p := new(terraform.MockResourceProvider) - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - p.ApplyReturn = &terraform.InstanceState{ - ID: "bob", - } - - // Apply - info := &terraform.InstanceInfo{} - state := &terraform.InstanceState{} - diff := &terraform.InstanceDiff{} - newState, err := provider.Apply(info, state, diff) - if !p.ApplyCalled { - t.Fatal("apply should be called") - } - if !reflect.DeepEqual(p.ApplyDiff, diff) { - t.Fatalf("bad: %#v", p.ApplyDiff) - } - if err != nil { - t.Fatalf("bad: %#v", err) - } - if !reflect.DeepEqual(p.ApplyReturn, newState) { - t.Fatalf("bad: %#v", newState) - } -} - -func TestResourceProvider_diff(t *testing.T) { - p := new(terraform.MockResourceProvider) - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - p.DiffReturn = &terraform.InstanceDiff{ - Attributes: map[string]*terraform.ResourceAttrDiff{ - "foo": &terraform.ResourceAttrDiff{ - Old: "", - New: "bar", - }, - }, - } - - // Diff - info := &terraform.InstanceInfo{} - state := &terraform.InstanceState{} - config := &terraform.ResourceConfig{ - Raw: map[string]interface{}{"foo": "bar"}, - } - diff, err := provider.Diff(info, state, config) - if !p.DiffCalled { - t.Fatal("diff should be called") - } - if !reflect.DeepEqual(p.DiffDesired, config) { - t.Fatalf("bad: %#v", p.DiffDesired) - } - if err != nil { - t.Fatalf("bad: %#v", err) - } - if !reflect.DeepEqual(p.DiffReturn, diff) { - t.Fatalf("bad: %#v", diff) - } -} - -func TestResourceProvider_diff_error(t *testing.T) { - p := new(terraform.MockResourceProvider) - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - p.DiffReturnError = errors.New("foo") - - // Diff - info := &terraform.InstanceInfo{} - state := &terraform.InstanceState{} - config := &terraform.ResourceConfig{ - Raw: map[string]interface{}{"foo": "bar"}, - } - diff, err := provider.Diff(info, state, config) - if !p.DiffCalled { - t.Fatal("diff should be called") - } - if !reflect.DeepEqual(p.DiffDesired, config) { - t.Fatalf("bad: %#v", p.DiffDesired) - } - if err == nil { - t.Fatal("should have error") - } - if diff != nil { - t.Fatal("should not have diff") - } -} - -func TestResourceProvider_refresh(t *testing.T) { - p := new(terraform.MockResourceProvider) - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - p.RefreshReturn = &terraform.InstanceState{ - ID: "bob", - } - - // Refresh - info := &terraform.InstanceInfo{} - state := &terraform.InstanceState{} - newState, err := provider.Refresh(info, state) - if !p.RefreshCalled { - t.Fatal("refresh should be called") - } - if !reflect.DeepEqual(p.RefreshState, state) { - t.Fatalf("bad: %#v", p.RefreshState) - } - if err != nil { - t.Fatalf("bad: %#v", err) - } - if !reflect.DeepEqual(p.RefreshReturn, newState) { - t.Fatalf("bad: %#v", newState) - } -} - -func TestResourceProvider_importState(t *testing.T) { - p := new(terraform.MockResourceProvider) - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - p.ImportStateReturn = []*terraform.InstanceState{ - &terraform.InstanceState{ - ID: "bob", - }, - } - - // ImportState - info := &terraform.InstanceInfo{} - states, err := provider.ImportState(info, "foo") - if !p.ImportStateCalled { - t.Fatal("ImportState should be called") - } - if !reflect.DeepEqual(p.ImportStateInfo, info) { - t.Fatalf("bad: %#v", p.ImportStateInfo) - } - if err != nil { - t.Fatalf("bad: %#v", err) - } - if !reflect.DeepEqual(p.ImportStateReturn, states) { - t.Fatalf("bad: %#v", states) - } -} - -func TestResourceProvider_resources(t *testing.T) { - p := new(terraform.MockResourceProvider) - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - expected := []terraform.ResourceType{ - terraform.ResourceType{Name: "foo"}, - terraform.ResourceType{Name: "bar", Importable: true}, - } - - p.ResourcesReturn = expected - - // Resources - result := provider.Resources() - if !p.ResourcesCalled { - t.Fatal("resources should be called") - } - if !reflect.DeepEqual(result, expected) { - t.Fatalf("bad: %#v", result) - } -} - -func TestResourceProvider_readdataapply(t *testing.T) { - p := new(terraform.MockResourceProvider) - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - p.ReadDataApplyReturn = &terraform.InstanceState{ - ID: "bob", - } - - // ReadDataApply - info := &terraform.InstanceInfo{} - diff := &terraform.InstanceDiff{} - newState, err := provider.ReadDataApply(info, diff) - if !p.ReadDataApplyCalled { - t.Fatal("ReadDataApply should be called") - } - if !reflect.DeepEqual(p.ReadDataApplyDiff, diff) { - t.Fatalf("bad: %#v", p.ReadDataApplyDiff) - } - if err != nil { - t.Fatalf("bad: %#v", err) - } - if !reflect.DeepEqual(p.ReadDataApplyReturn, newState) { - t.Fatalf("bad: %#v", newState) - } -} - -func TestResourceProvider_datasources(t *testing.T) { - p := new(terraform.MockResourceProvider) - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - expected := []terraform.DataSource{ - {Name: "foo"}, - {Name: "bar"}, - } - - p.DataSourcesReturn = expected - - // DataSources - result := provider.DataSources() - if !p.DataSourcesCalled { - t.Fatal("DataSources should be called") - } - if !reflect.DeepEqual(result, expected) { - t.Fatalf("bad: %#v", result) - } -} - -func TestResourceProvider_validate(t *testing.T) { - p := new(terraform.MockResourceProvider) - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - // Configure - config := &terraform.ResourceConfig{ - Raw: map[string]interface{}{"foo": "bar"}, - } - w, e := provider.Validate(config) - if !p.ValidateCalled { - t.Fatal("configure should be called") - } - if !reflect.DeepEqual(p.ValidateConfig, config) { - t.Fatalf("bad: %#v", p.ValidateConfig) - } - if w != nil { - t.Fatalf("bad: %#v", w) - } - if e != nil { - t.Fatalf("bad: %#v", e) - } -} - -func TestResourceProvider_validate_errors(t *testing.T) { - p := new(terraform.MockResourceProvider) - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - p.ValidateReturnErrors = []error{errors.New("foo")} - - // Configure - config := &terraform.ResourceConfig{ - Raw: map[string]interface{}{"foo": "bar"}, - } - w, e := provider.Validate(config) - if !p.ValidateCalled { - t.Fatal("configure should be called") - } - if !reflect.DeepEqual(p.ValidateConfig, config) { - t.Fatalf("bad: %#v", p.ValidateConfig) - } - if w != nil { - t.Fatalf("bad: %#v", w) - } - - if len(e) != 1 { - t.Fatalf("bad: %#v", e) - } - if e[0].Error() != "foo" { - t.Fatalf("bad: %#v", e) - } -} - -func TestResourceProvider_validate_warns(t *testing.T) { - p := new(terraform.MockResourceProvider) - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - p.ValidateReturnWarns = []string{"foo"} - - // Configure - config := &terraform.ResourceConfig{ - Raw: map[string]interface{}{"foo": "bar"}, - } - w, e := provider.Validate(config) - if !p.ValidateCalled { - t.Fatal("configure should be called") - } - if !reflect.DeepEqual(p.ValidateConfig, config) { - t.Fatalf("bad: %#v", p.ValidateConfig) - } - if e != nil { - t.Fatalf("bad: %#v", e) - } - - expected := []string{"foo"} - if !reflect.DeepEqual(w, expected) { - t.Fatalf("bad: %#v", w) - } -} - -func TestResourceProvider_validateResource(t *testing.T) { - p := new(terraform.MockResourceProvider) - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - // Configure - config := &terraform.ResourceConfig{ - Raw: map[string]interface{}{"foo": "bar"}, - } - w, e := provider.ValidateResource("foo", config) - if !p.ValidateResourceCalled { - t.Fatal("configure should be called") - } - if p.ValidateResourceType != "foo" { - t.Fatalf("bad: %#v", p.ValidateResourceType) - } - if !reflect.DeepEqual(p.ValidateResourceConfig, config) { - t.Fatalf("bad: %#v", p.ValidateResourceConfig) - } - if w != nil { - t.Fatalf("bad: %#v", w) - } - if e != nil { - t.Fatalf("bad: %#v", e) - } -} - -func TestResourceProvider_validateResource_errors(t *testing.T) { - p := new(terraform.MockResourceProvider) - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - p.ValidateResourceReturnErrors = []error{errors.New("foo")} - - // Configure - config := &terraform.ResourceConfig{ - Raw: map[string]interface{}{"foo": "bar"}, - } - w, e := provider.ValidateResource("foo", config) - if !p.ValidateResourceCalled { - t.Fatal("configure should be called") - } - if p.ValidateResourceType != "foo" { - t.Fatalf("bad: %#v", p.ValidateResourceType) - } - if !reflect.DeepEqual(p.ValidateResourceConfig, config) { - t.Fatalf("bad: %#v", p.ValidateResourceConfig) - } - if w != nil { - t.Fatalf("bad: %#v", w) - } - - if len(e) != 1 { - t.Fatalf("bad: %#v", e) - } - if e[0].Error() != "foo" { - t.Fatalf("bad: %#v", e) - } -} - -func TestResourceProvider_validateResource_warns(t *testing.T) { - p := new(terraform.MockResourceProvider) - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - p.ValidateResourceReturnWarns = []string{"foo"} - - // Configure - config := &terraform.ResourceConfig{ - Raw: map[string]interface{}{"foo": "bar"}, - } - w, e := provider.ValidateResource("foo", config) - if !p.ValidateResourceCalled { - t.Fatal("configure should be called") - } - if p.ValidateResourceType != "foo" { - t.Fatalf("bad: %#v", p.ValidateResourceType) - } - if !reflect.DeepEqual(p.ValidateResourceConfig, config) { - t.Fatalf("bad: %#v", p.ValidateResourceConfig) - } - if e != nil { - t.Fatalf("bad: %#v", e) - } - - expected := []string{"foo"} - if !reflect.DeepEqual(w, expected) { - t.Fatalf("bad: %#v", w) - } -} - -func TestResourceProvider_validateDataSource(t *testing.T) { - p := new(terraform.MockResourceProvider) - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - // Configure - config := &terraform.ResourceConfig{ - Raw: map[string]interface{}{"foo": "bar"}, - } - w, e := provider.ValidateDataSource("foo", config) - if !p.ValidateDataSourceCalled { - t.Fatal("configure should be called") - } - if p.ValidateDataSourceType != "foo" { - t.Fatalf("bad: %#v", p.ValidateDataSourceType) - } - if !reflect.DeepEqual(p.ValidateDataSourceConfig, config) { - t.Fatalf("bad: %#v", p.ValidateDataSourceConfig) - } - if w != nil { - t.Fatalf("bad: %#v", w) - } - if e != nil { - t.Fatalf("bad: %#v", e) - } -} - -func TestResourceProvider_close(t *testing.T) { - p := new(terraform.MockResourceProvider) - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProviderFunc: testProviderFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProviderPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvider) - - var iface interface{} = provider - pCloser, ok := iface.(terraform.ResourceProviderCloser) - if !ok { - t.Fatal("should be a ResourceProviderCloser") - } - - if err := pCloser.Close(); err != nil { - t.Fatalf("failed to close provider: %s", err) - } - - // The connection should be closed now, so if we to make a - // new call we should get an error. - err = provider.Configure(&terraform.ResourceConfig{}) - if err == nil { - t.Fatal("should have error") - } -} diff --git a/vendor/github.com/hashicorp/terraform/plugin/resource_provisioner.go b/vendor/github.com/hashicorp/terraform/plugin/resource_provisioner.go deleted file mode 100644 index 5bebc4c6..00000000 --- a/vendor/github.com/hashicorp/terraform/plugin/resource_provisioner.go +++ /dev/null @@ -1,181 +0,0 @@ -package plugin - -import ( - "net/rpc" - - "github.com/hashicorp/go-plugin" - "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/terraform" -) - -// ResourceProvisionerPlugin is the plugin.Plugin implementation. -type ResourceProvisionerPlugin struct { - ResourceProvisioner func() terraform.ResourceProvisioner -} - -func (p *ResourceProvisionerPlugin) Server(b *plugin.MuxBroker) (interface{}, error) { - return &ResourceProvisionerServer{ - Broker: b, - Provisioner: p.ResourceProvisioner(), - }, nil -} - -func (p *ResourceProvisionerPlugin) Client( - b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { - return &ResourceProvisioner{Broker: b, Client: c}, nil -} - -// ResourceProvisioner is an implementation of terraform.ResourceProvisioner -// that communicates over RPC. -type ResourceProvisioner struct { - Broker *plugin.MuxBroker - Client *rpc.Client -} - -func (p *ResourceProvisioner) GetConfigSchema() (*configschema.Block, error) { - panic("not implemented") -} - -func (p *ResourceProvisioner) Validate(c *terraform.ResourceConfig) ([]string, []error) { - var resp ResourceProvisionerValidateResponse - args := ResourceProvisionerValidateArgs{ - Config: c, - } - - err := p.Client.Call("Plugin.Validate", &args, &resp) - if err != nil { - return nil, []error{err} - } - - var errs []error - if len(resp.Errors) > 0 { - errs = make([]error, len(resp.Errors)) - for i, err := range resp.Errors { - errs[i] = err - } - } - - return resp.Warnings, errs -} - -func (p *ResourceProvisioner) Apply( - output terraform.UIOutput, - s *terraform.InstanceState, - c *terraform.ResourceConfig) error { - id := p.Broker.NextId() - go p.Broker.AcceptAndServe(id, &UIOutputServer{ - UIOutput: output, - }) - - var resp ResourceProvisionerApplyResponse - args := &ResourceProvisionerApplyArgs{ - OutputId: id, - State: s, - Config: c, - } - - err := p.Client.Call("Plugin.Apply", args, &resp) - if err != nil { - return err - } - if resp.Error != nil { - err = resp.Error - } - - return err -} - -func (p *ResourceProvisioner) Stop() error { - var resp ResourceProvisionerStopResponse - err := p.Client.Call("Plugin.Stop", new(interface{}), &resp) - if err != nil { - return err - } - if resp.Error != nil { - err = resp.Error - } - - return err -} - -func (p *ResourceProvisioner) Close() error { - return p.Client.Close() -} - -type ResourceProvisionerValidateArgs struct { - Config *terraform.ResourceConfig -} - -type ResourceProvisionerValidateResponse struct { - Warnings []string - Errors []*plugin.BasicError -} - -type ResourceProvisionerApplyArgs struct { - OutputId uint32 - State *terraform.InstanceState - Config *terraform.ResourceConfig -} - -type ResourceProvisionerApplyResponse struct { - Error *plugin.BasicError -} - -type ResourceProvisionerStopResponse struct { - Error *plugin.BasicError -} - -// ResourceProvisionerServer is a net/rpc compatible structure for serving -// a ResourceProvisioner. This should not be used directly. -type ResourceProvisionerServer struct { - Broker *plugin.MuxBroker - Provisioner terraform.ResourceProvisioner -} - -func (s *ResourceProvisionerServer) Apply( - args *ResourceProvisionerApplyArgs, - result *ResourceProvisionerApplyResponse) error { - conn, err := s.Broker.Dial(args.OutputId) - if err != nil { - *result = ResourceProvisionerApplyResponse{ - Error: plugin.NewBasicError(err), - } - return nil - } - client := rpc.NewClient(conn) - defer client.Close() - - output := &UIOutput{Client: client} - - err = s.Provisioner.Apply(output, args.State, args.Config) - *result = ResourceProvisionerApplyResponse{ - Error: plugin.NewBasicError(err), - } - return nil -} - -func (s *ResourceProvisionerServer) Validate( - args *ResourceProvisionerValidateArgs, - reply *ResourceProvisionerValidateResponse) error { - warns, errs := s.Provisioner.Validate(args.Config) - berrs := make([]*plugin.BasicError, len(errs)) - for i, err := range errs { - berrs[i] = plugin.NewBasicError(err) - } - *reply = ResourceProvisionerValidateResponse{ - Warnings: warns, - Errors: berrs, - } - return nil -} - -func (s *ResourceProvisionerServer) Stop( - _ interface{}, - reply *ResourceProvisionerStopResponse) error { - err := s.Provisioner.Stop() - *reply = ResourceProvisionerStopResponse{ - Error: plugin.NewBasicError(err), - } - - return nil -} diff --git a/vendor/github.com/hashicorp/terraform/plugin/resource_provisioner_test.go b/vendor/github.com/hashicorp/terraform/plugin/resource_provisioner_test.go deleted file mode 100644 index 0ff3ea23..00000000 --- a/vendor/github.com/hashicorp/terraform/plugin/resource_provisioner_test.go +++ /dev/null @@ -1,248 +0,0 @@ -package plugin - -import ( - "errors" - "reflect" - "testing" - - "github.com/hashicorp/go-plugin" - "github.com/hashicorp/terraform/terraform" -) - -func TestResourceProvisioner_impl(t *testing.T) { - var _ plugin.Plugin = new(ResourceProvisionerPlugin) - var _ terraform.ResourceProvisioner = new(ResourceProvisioner) -} - -func TestResourceProvisioner_stop(t *testing.T) { - // Create a mock provider - p := new(terraform.MockResourceProvisioner) - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProvisionerFunc: testProvisionerFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProvisionerPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvisioner) - - // Stop - e := provider.Stop() - if !p.StopCalled { - t.Fatal("stop should be called") - } - if e != nil { - t.Fatalf("bad: %#v", e) - } -} - -func TestResourceProvisioner_stopErrors(t *testing.T) { - p := new(terraform.MockResourceProvisioner) - p.StopReturnError = errors.New("foo") - - // Create a mock provider - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProvisionerFunc: testProvisionerFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProvisionerPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provider := raw.(terraform.ResourceProvisioner) - - // Stop - e := provider.Stop() - if !p.StopCalled { - t.Fatal("stop should be called") - } - if e == nil { - t.Fatal("should have error") - } - if e.Error() != "foo" { - t.Fatalf("bad: %s", e) - } -} - -func TestResourceProvisioner_apply(t *testing.T) { - // Create a mock provider - p := new(terraform.MockResourceProvisioner) - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProvisionerFunc: testProvisionerFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProvisionerPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provisioner := raw.(terraform.ResourceProvisioner) - - // Apply - output := &terraform.MockUIOutput{} - state := &terraform.InstanceState{} - conf := &terraform.ResourceConfig{} - err = provisioner.Apply(output, state, conf) - if !p.ApplyCalled { - t.Fatal("apply should be called") - } - if !reflect.DeepEqual(p.ApplyConfig, conf) { - t.Fatalf("bad: %#v", p.ApplyConfig) - } - if err != nil { - t.Fatalf("bad: %#v", err) - } -} - -func TestResourceProvisioner_validate(t *testing.T) { - // Create a mock provider - p := new(terraform.MockResourceProvisioner) - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProvisionerFunc: testProvisionerFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProvisionerPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provisioner := raw.(terraform.ResourceProvisioner) - - // Configure - config := &terraform.ResourceConfig{ - Raw: map[string]interface{}{"foo": "bar"}, - } - w, e := provisioner.Validate(config) - if !p.ValidateCalled { - t.Fatal("configure should be called") - } - if !reflect.DeepEqual(p.ValidateConfig, config) { - t.Fatalf("bad: %#v", p.ValidateConfig) - } - if w != nil { - t.Fatalf("bad: %#v", w) - } - if e != nil { - t.Fatalf("bad: %#v", e) - } -} - -func TestResourceProvisioner_validate_errors(t *testing.T) { - // Create a mock provider - p := new(terraform.MockResourceProvisioner) - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProvisionerFunc: testProvisionerFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProvisionerPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provisioner := raw.(terraform.ResourceProvisioner) - - p.ValidateReturnErrors = []error{errors.New("foo")} - - // Configure - config := &terraform.ResourceConfig{ - Raw: map[string]interface{}{"foo": "bar"}, - } - w, e := provisioner.Validate(config) - if !p.ValidateCalled { - t.Fatal("configure should be called") - } - if !reflect.DeepEqual(p.ValidateConfig, config) { - t.Fatalf("bad: %#v", p.ValidateConfig) - } - if w != nil { - t.Fatalf("bad: %#v", w) - } - - if len(e) != 1 { - t.Fatalf("bad: %#v", e) - } - if e[0].Error() != "foo" { - t.Fatalf("bad: %#v", e) - } -} - -func TestResourceProvisioner_validate_warns(t *testing.T) { - // Create a mock provider - p := new(terraform.MockResourceProvisioner) - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProvisionerFunc: testProvisionerFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProvisionerPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provisioner := raw.(terraform.ResourceProvisioner) - - p.ValidateReturnWarns = []string{"foo"} - - // Configure - config := &terraform.ResourceConfig{ - Raw: map[string]interface{}{"foo": "bar"}, - } - w, e := provisioner.Validate(config) - if !p.ValidateCalled { - t.Fatal("configure should be called") - } - if !reflect.DeepEqual(p.ValidateConfig, config) { - t.Fatalf("bad: %#v", p.ValidateConfig) - } - if e != nil { - t.Fatalf("bad: %#v", e) - } - - expected := []string{"foo"} - if !reflect.DeepEqual(w, expected) { - t.Fatalf("bad: %#v", w) - } -} - -func TestResourceProvisioner_close(t *testing.T) { - // Create a mock provider - p := new(terraform.MockResourceProvisioner) - client, _ := plugin.TestPluginRPCConn(t, legacyPluginMap(&ServeOpts{ - ProvisionerFunc: testProvisionerFixed(p), - }), nil) - defer client.Close() - - // Request the provider - raw, err := client.Dispense(ProvisionerPluginName) - if err != nil { - t.Fatalf("err: %s", err) - } - provisioner := raw.(terraform.ResourceProvisioner) - - pCloser, ok := raw.(terraform.ResourceProvisionerCloser) - if !ok { - t.Fatal("should be a ResourceProvisionerCloser") - } - - if err := pCloser.Close(); err != nil { - t.Fatalf("failed to close provisioner: %s", err) - } - - // The connection should be closed now, so if we to make a - // new call we should get an error. - o := &terraform.MockUIOutput{} - s := &terraform.InstanceState{} - c := &terraform.ResourceConfig{} - err = provisioner.Apply(o, s, c) - if err == nil { - t.Fatal("should have error") - } -} diff --git a/vendor/github.com/hashicorp/terraform/plugin/serve.go b/vendor/github.com/hashicorp/terraform/plugin/serve.go index 8d056c59..27d3c9e6 100644 --- a/vendor/github.com/hashicorp/terraform/plugin/serve.go +++ b/vendor/github.com/hashicorp/terraform/plugin/serve.go @@ -2,9 +2,7 @@ package plugin import ( "github.com/hashicorp/go-plugin" - grpcplugin "github.com/hashicorp/terraform/helper/plugin" proto "github.com/hashicorp/terraform/internal/tfplugin5" - "github.com/hashicorp/terraform/terraform" ) const ( @@ -35,16 +33,11 @@ var Handshake = plugin.HandshakeConfig{ MagicCookieValue: "d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2", } -type ProviderFunc func() terraform.ResourceProvider -type ProvisionerFunc func() terraform.ResourceProvisioner type GRPCProviderFunc func() proto.ProviderServer type GRPCProvisionerFunc func() proto.ProvisionerServer // ServeOpts are the configurations to serve a plugin. type ServeOpts struct { - ProviderFunc ProviderFunc - ProvisionerFunc ProvisionerFunc - // Wrapped versions of the above plugins will automatically shimmed and // added to the GRPC functions when possible. GRPCProviderFunc GRPCProviderFunc @@ -54,27 +47,6 @@ type ServeOpts struct { // Serve serves a plugin. This function never returns and should be the final // function called in the main function of the plugin. func Serve(opts *ServeOpts) { - // since the plugins may not yet be aware of the new protocol, we - // automatically wrap the plugins in the grpc shims. - if opts.GRPCProviderFunc == nil && opts.ProviderFunc != nil { - provider := grpcplugin.NewGRPCProviderServerShim(opts.ProviderFunc()) - // this is almost always going to be a *schema.Provider, but check that - // we got back a valid provider just in case. - if provider != nil { - opts.GRPCProviderFunc = func() proto.ProviderServer { - return provider - } - } - } - if opts.GRPCProvisionerFunc == nil && opts.ProvisionerFunc != nil { - provisioner := grpcplugin.NewGRPCProvisionerServerShim(opts.ProvisionerFunc()) - if provisioner != nil { - opts.GRPCProvisionerFunc = func() proto.ProvisionerServer { - return provisioner - } - } - } - plugin.Serve(&plugin.ServeConfig{ HandshakeConfig: Handshake, VersionedPlugins: pluginSet(opts), @@ -82,26 +54,8 @@ func Serve(opts *ServeOpts) { }) } -// pluginMap returns the legacy map[string]plugin.Plugin to use for configuring -// a plugin server or client. -func legacyPluginMap(opts *ServeOpts) map[string]plugin.Plugin { - return map[string]plugin.Plugin{ - "provider": &ResourceProviderPlugin{ - ResourceProvider: opts.ProviderFunc, - }, - "provisioner": &ResourceProvisionerPlugin{ - ResourceProvisioner: opts.ProvisionerFunc, - }, - } -} - func pluginSet(opts *ServeOpts) map[int]plugin.PluginSet { - // Set the legacy netrpc plugins at version 4. - // The oldest version is returned in when executed by a legacy go-plugin - // client. - plugins := map[int]plugin.PluginSet{ - 4: legacyPluginMap(opts), - } + plugins := map[int]plugin.PluginSet{} // add the new protocol versions if they're configured if opts.GRPCProviderFunc != nil || opts.GRPCProvisionerFunc != nil { diff --git a/vendor/github.com/hashicorp/terraform/plugin6/convert/diagnostics.go b/vendor/github.com/hashicorp/terraform/plugin6/convert/diagnostics.go new file mode 100644 index 00000000..6d1db136 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plugin6/convert/diagnostics.go @@ -0,0 +1,132 @@ +package convert + +import ( + proto "github.com/hashicorp/terraform/internal/tfplugin6" + "github.com/hashicorp/terraform/tfdiags" + "github.com/zclconf/go-cty/cty" +) + +// WarnsAndErrorsToProto converts the warnings and errors return by the legacy +// provider to protobuf diagnostics. +func WarnsAndErrsToProto(warns []string, errs []error) (diags []*proto.Diagnostic) { + for _, w := range warns { + diags = AppendProtoDiag(diags, w) + } + + for _, e := range errs { + diags = AppendProtoDiag(diags, e) + } + + return diags +} + +// AppendProtoDiag appends a new diagnostic from a warning string or an error. +// This panics if d is not a string or error. +func AppendProtoDiag(diags []*proto.Diagnostic, d interface{}) []*proto.Diagnostic { + switch d := d.(type) { + case cty.PathError: + ap := PathToAttributePath(d.Path) + diags = append(diags, &proto.Diagnostic{ + Severity: proto.Diagnostic_ERROR, + Summary: d.Error(), + Attribute: ap, + }) + case error: + diags = append(diags, &proto.Diagnostic{ + Severity: proto.Diagnostic_ERROR, + Summary: d.Error(), + }) + case string: + diags = append(diags, &proto.Diagnostic{ + Severity: proto.Diagnostic_WARNING, + Summary: d, + }) + case *proto.Diagnostic: + diags = append(diags, d) + case []*proto.Diagnostic: + diags = append(diags, d...) + } + return diags +} + +// ProtoToDiagnostics converts a list of proto.Diagnostics to a tf.Diagnostics. +func ProtoToDiagnostics(ds []*proto.Diagnostic) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + for _, d := range ds { + var severity tfdiags.Severity + + switch d.Severity { + case proto.Diagnostic_ERROR: + severity = tfdiags.Error + case proto.Diagnostic_WARNING: + severity = tfdiags.Warning + } + + var newDiag tfdiags.Diagnostic + + // if there's an attribute path, we need to create a AttributeValue diagnostic + if d.Attribute != nil { + path := AttributePathToPath(d.Attribute) + newDiag = tfdiags.AttributeValue(severity, d.Summary, d.Detail, path) + } else { + newDiag = tfdiags.WholeContainingBody(severity, d.Summary, d.Detail) + } + + diags = diags.Append(newDiag) + } + + return diags +} + +// AttributePathToPath takes the proto encoded path and converts it to a cty.Path +func AttributePathToPath(ap *proto.AttributePath) cty.Path { + var p cty.Path + for _, step := range ap.Steps { + switch selector := step.Selector.(type) { + case *proto.AttributePath_Step_AttributeName: + p = p.GetAttr(selector.AttributeName) + case *proto.AttributePath_Step_ElementKeyString: + p = p.Index(cty.StringVal(selector.ElementKeyString)) + case *proto.AttributePath_Step_ElementKeyInt: + p = p.Index(cty.NumberIntVal(selector.ElementKeyInt)) + } + } + return p +} + +// AttributePathToPath takes a cty.Path and converts it to a proto-encoded path. +func PathToAttributePath(p cty.Path) *proto.AttributePath { + ap := &proto.AttributePath{} + for _, step := range p { + switch selector := step.(type) { + case cty.GetAttrStep: + ap.Steps = append(ap.Steps, &proto.AttributePath_Step{ + Selector: &proto.AttributePath_Step_AttributeName{ + AttributeName: selector.Name, + }, + }) + case cty.IndexStep: + key := selector.Key + switch key.Type() { + case cty.String: + ap.Steps = append(ap.Steps, &proto.AttributePath_Step{ + Selector: &proto.AttributePath_Step_ElementKeyString{ + ElementKeyString: key.AsString(), + }, + }) + case cty.Number: + v, _ := key.AsBigFloat().Int64() + ap.Steps = append(ap.Steps, &proto.AttributePath_Step{ + Selector: &proto.AttributePath_Step_ElementKeyInt{ + ElementKeyInt: v, + }, + }) + default: + // We'll bail early if we encounter anything else, and just + // return the valid prefix. + return ap + } + } + } + return ap +} diff --git a/vendor/github.com/hashicorp/terraform/plugin6/convert/diagnostics_test.go b/vendor/github.com/hashicorp/terraform/plugin6/convert/diagnostics_test.go new file mode 100644 index 00000000..24f269d0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plugin6/convert/diagnostics_test.go @@ -0,0 +1,367 @@ +package convert + +import ( + "errors" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + proto "github.com/hashicorp/terraform/internal/tfplugin6" + "github.com/hashicorp/terraform/tfdiags" + "github.com/zclconf/go-cty/cty" +) + +var ignoreUnexported = cmpopts.IgnoreUnexported( + proto.Diagnostic{}, + proto.Schema_Block{}, + proto.Schema_NestedBlock{}, + proto.Schema_Attribute{}, +) + +func TestProtoDiagnostics(t *testing.T) { + diags := WarnsAndErrsToProto( + []string{ + "warning 1", + "warning 2", + }, + []error{ + errors.New("error 1"), + errors.New("error 2"), + }, + ) + + expected := []*proto.Diagnostic{ + { + Severity: proto.Diagnostic_WARNING, + Summary: "warning 1", + }, + { + Severity: proto.Diagnostic_WARNING, + Summary: "warning 2", + }, + { + Severity: proto.Diagnostic_ERROR, + Summary: "error 1", + }, + { + Severity: proto.Diagnostic_ERROR, + Summary: "error 2", + }, + } + + if !cmp.Equal(expected, diags, ignoreUnexported) { + t.Fatal(cmp.Diff(expected, diags, ignoreUnexported)) + } +} + +func TestDiagnostics(t *testing.T) { + type diagFlat struct { + Severity tfdiags.Severity + Attr []interface{} + Summary string + Detail string + } + + tests := map[string]struct { + Cons func([]*proto.Diagnostic) []*proto.Diagnostic + Want []diagFlat + }{ + "nil": { + func(diags []*proto.Diagnostic) []*proto.Diagnostic { + return diags + }, + nil, + }, + "error": { + func(diags []*proto.Diagnostic) []*proto.Diagnostic { + return append(diags, &proto.Diagnostic{ + Severity: proto.Diagnostic_ERROR, + Summary: "simple error", + }) + }, + []diagFlat{ + { + Severity: tfdiags.Error, + Summary: "simple error", + }, + }, + }, + "detailed error": { + func(diags []*proto.Diagnostic) []*proto.Diagnostic { + return append(diags, &proto.Diagnostic{ + Severity: proto.Diagnostic_ERROR, + Summary: "simple error", + Detail: "detailed error", + }) + }, + []diagFlat{ + { + Severity: tfdiags.Error, + Summary: "simple error", + Detail: "detailed error", + }, + }, + }, + "warning": { + func(diags []*proto.Diagnostic) []*proto.Diagnostic { + return append(diags, &proto.Diagnostic{ + Severity: proto.Diagnostic_WARNING, + Summary: "simple warning", + }) + }, + []diagFlat{ + { + Severity: tfdiags.Warning, + Summary: "simple warning", + }, + }, + }, + "detailed warning": { + func(diags []*proto.Diagnostic) []*proto.Diagnostic { + return append(diags, &proto.Diagnostic{ + Severity: proto.Diagnostic_WARNING, + Summary: "simple warning", + Detail: "detailed warning", + }) + }, + []diagFlat{ + { + Severity: tfdiags.Warning, + Summary: "simple warning", + Detail: "detailed warning", + }, + }, + }, + "multi error": { + func(diags []*proto.Diagnostic) []*proto.Diagnostic { + diags = append(diags, &proto.Diagnostic{ + Severity: proto.Diagnostic_ERROR, + Summary: "first error", + }, &proto.Diagnostic{ + Severity: proto.Diagnostic_ERROR, + Summary: "second error", + }) + return diags + }, + []diagFlat{ + { + Severity: tfdiags.Error, + Summary: "first error", + }, + { + Severity: tfdiags.Error, + Summary: "second error", + }, + }, + }, + "warning and error": { + func(diags []*proto.Diagnostic) []*proto.Diagnostic { + diags = append(diags, &proto.Diagnostic{ + Severity: proto.Diagnostic_WARNING, + Summary: "warning", + }, &proto.Diagnostic{ + Severity: proto.Diagnostic_ERROR, + Summary: "error", + }) + return diags + }, + []diagFlat{ + { + Severity: tfdiags.Warning, + Summary: "warning", + }, + { + Severity: tfdiags.Error, + Summary: "error", + }, + }, + }, + "attr error": { + func(diags []*proto.Diagnostic) []*proto.Diagnostic { + diags = append(diags, &proto.Diagnostic{ + Severity: proto.Diagnostic_ERROR, + Summary: "error", + Detail: "error detail", + Attribute: &proto.AttributePath{ + Steps: []*proto.AttributePath_Step{ + { + Selector: &proto.AttributePath_Step_AttributeName{ + AttributeName: "attribute_name", + }, + }, + }, + }, + }) + return diags + }, + []diagFlat{ + { + Severity: tfdiags.Error, + Summary: "error", + Detail: "error detail", + Attr: []interface{}{"attribute_name"}, + }, + }, + }, + "multi attr": { + func(diags []*proto.Diagnostic) []*proto.Diagnostic { + diags = append(diags, + &proto.Diagnostic{ + Severity: proto.Diagnostic_ERROR, + Summary: "error 1", + Detail: "error 1 detail", + Attribute: &proto.AttributePath{ + Steps: []*proto.AttributePath_Step{ + { + Selector: &proto.AttributePath_Step_AttributeName{ + AttributeName: "attr", + }, + }, + }, + }, + }, + &proto.Diagnostic{ + Severity: proto.Diagnostic_ERROR, + Summary: "error 2", + Detail: "error 2 detail", + Attribute: &proto.AttributePath{ + Steps: []*proto.AttributePath_Step{ + { + Selector: &proto.AttributePath_Step_AttributeName{ + AttributeName: "attr", + }, + }, + { + Selector: &proto.AttributePath_Step_AttributeName{ + AttributeName: "sub", + }, + }, + }, + }, + }, + &proto.Diagnostic{ + Severity: proto.Diagnostic_WARNING, + Summary: "warning", + Detail: "warning detail", + Attribute: &proto.AttributePath{ + Steps: []*proto.AttributePath_Step{ + { + Selector: &proto.AttributePath_Step_AttributeName{ + AttributeName: "attr", + }, + }, + { + Selector: &proto.AttributePath_Step_ElementKeyInt{ + ElementKeyInt: 1, + }, + }, + { + Selector: &proto.AttributePath_Step_AttributeName{ + AttributeName: "sub", + }, + }, + }, + }, + }, + &proto.Diagnostic{ + Severity: proto.Diagnostic_ERROR, + Summary: "error 3", + Detail: "error 3 detail", + Attribute: &proto.AttributePath{ + Steps: []*proto.AttributePath_Step{ + { + Selector: &proto.AttributePath_Step_AttributeName{ + AttributeName: "attr", + }, + }, + { + Selector: &proto.AttributePath_Step_ElementKeyString{ + ElementKeyString: "idx", + }, + }, + { + Selector: &proto.AttributePath_Step_AttributeName{ + AttributeName: "sub", + }, + }, + }, + }, + }, + ) + + return diags + }, + []diagFlat{ + { + Severity: tfdiags.Error, + Summary: "error 1", + Detail: "error 1 detail", + Attr: []interface{}{"attr"}, + }, + { + Severity: tfdiags.Error, + Summary: "error 2", + Detail: "error 2 detail", + Attr: []interface{}{"attr", "sub"}, + }, + { + Severity: tfdiags.Warning, + Summary: "warning", + Detail: "warning detail", + Attr: []interface{}{"attr", 1, "sub"}, + }, + { + Severity: tfdiags.Error, + Summary: "error 3", + Detail: "error 3 detail", + Attr: []interface{}{"attr", "idx", "sub"}, + }, + }, + }, + } + + flattenTFDiags := func(ds tfdiags.Diagnostics) []diagFlat { + var flat []diagFlat + for _, item := range ds { + desc := item.Description() + + var attr []interface{} + + for _, a := range tfdiags.GetAttribute(item) { + switch step := a.(type) { + case cty.GetAttrStep: + attr = append(attr, step.Name) + case cty.IndexStep: + switch step.Key.Type() { + case cty.Number: + i, _ := step.Key.AsBigFloat().Int64() + attr = append(attr, int(i)) + case cty.String: + attr = append(attr, step.Key.AsString()) + } + } + } + + flat = append(flat, diagFlat{ + Severity: item.Severity(), + Attr: attr, + Summary: desc.Summary, + Detail: desc.Detail, + }) + } + return flat + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + // we take the + tfDiags := ProtoToDiagnostics(tc.Cons(nil)) + + flat := flattenTFDiags(tfDiags) + + if !cmp.Equal(flat, tc.Want, typeComparer, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(flat, tc.Want, typeComparer, valueComparer, equateEmpty)) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/plugin6/convert/schema.go b/vendor/github.com/hashicorp/terraform/plugin6/convert/schema.go new file mode 100644 index 00000000..eaaace37 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plugin6/convert/schema.go @@ -0,0 +1,301 @@ +package convert + +import ( + "encoding/json" + "reflect" + "sort" + + "github.com/hashicorp/terraform/configs/configschema" + proto "github.com/hashicorp/terraform/internal/tfplugin6" + "github.com/hashicorp/terraform/providers" + "github.com/zclconf/go-cty/cty" +) + +// ConfigSchemaToProto takes a *configschema.Block and converts it to a +// proto.Schema_Block for a grpc response. +func ConfigSchemaToProto(b *configschema.Block) *proto.Schema_Block { + block := &proto.Schema_Block{ + Description: b.Description, + DescriptionKind: protoStringKind(b.DescriptionKind), + Deprecated: b.Deprecated, + } + + for _, name := range sortedKeys(b.Attributes) { + a := b.Attributes[name] + + attr := &proto.Schema_Attribute{ + Name: name, + Description: a.Description, + DescriptionKind: protoStringKind(a.DescriptionKind), + Optional: a.Optional, + Computed: a.Computed, + Required: a.Required, + Sensitive: a.Sensitive, + Deprecated: a.Deprecated, + } + + if a.Type != cty.NilType { + ty, err := json.Marshal(a.Type) + if err != nil { + panic(err) + } + attr.Type = ty + } + + if a.NestedType != nil { + attr.NestedType = configschemaObjectToProto(a.NestedType) + } + + block.Attributes = append(block.Attributes, attr) + } + + for _, name := range sortedKeys(b.BlockTypes) { + b := b.BlockTypes[name] + block.BlockTypes = append(block.BlockTypes, protoSchemaNestedBlock(name, b)) + } + + return block +} + +func protoStringKind(k configschema.StringKind) proto.StringKind { + switch k { + default: + return proto.StringKind_PLAIN + case configschema.StringMarkdown: + return proto.StringKind_MARKDOWN + } +} + +func protoSchemaNestedBlock(name string, b *configschema.NestedBlock) *proto.Schema_NestedBlock { + var nesting proto.Schema_NestedBlock_NestingMode + switch b.Nesting { + case configschema.NestingSingle: + nesting = proto.Schema_NestedBlock_SINGLE + case configschema.NestingGroup: + nesting = proto.Schema_NestedBlock_GROUP + case configschema.NestingList: + nesting = proto.Schema_NestedBlock_LIST + case configschema.NestingSet: + nesting = proto.Schema_NestedBlock_SET + case configschema.NestingMap: + nesting = proto.Schema_NestedBlock_MAP + default: + nesting = proto.Schema_NestedBlock_INVALID + } + return &proto.Schema_NestedBlock{ + TypeName: name, + Block: ConfigSchemaToProto(&b.Block), + Nesting: nesting, + MinItems: int64(b.MinItems), + MaxItems: int64(b.MaxItems), + } +} + +// ProtoToProviderSchema takes a proto.Schema and converts it to a providers.Schema. +func ProtoToProviderSchema(s *proto.Schema) providers.Schema { + return providers.Schema{ + Version: s.Version, + Block: ProtoToConfigSchema(s.Block), + } +} + +// ProtoToConfigSchema takes the GetSchcema_Block from a grpc response and converts it +// to a terraform *configschema.Block. +func ProtoToConfigSchema(b *proto.Schema_Block) *configschema.Block { + block := &configschema.Block{ + Attributes: make(map[string]*configschema.Attribute), + BlockTypes: make(map[string]*configschema.NestedBlock), + + Description: b.Description, + DescriptionKind: schemaStringKind(b.DescriptionKind), + Deprecated: b.Deprecated, + } + + for _, a := range b.Attributes { + attr := &configschema.Attribute{ + Description: a.Description, + DescriptionKind: schemaStringKind(a.DescriptionKind), + Required: a.Required, + Optional: a.Optional, + Computed: a.Computed, + Sensitive: a.Sensitive, + Deprecated: a.Deprecated, + } + + if a.Type != nil { + if err := json.Unmarshal(a.Type, &attr.Type); err != nil { + panic(err) + } + } + + if a.NestedType != nil { + attr.NestedType = protoObjectToConfigSchema(a.NestedType) + } + + block.Attributes[a.Name] = attr + } + + for _, b := range b.BlockTypes { + block.BlockTypes[b.TypeName] = schemaNestedBlock(b) + } + + return block +} + +func schemaStringKind(k proto.StringKind) configschema.StringKind { + switch k { + default: + return configschema.StringPlain + case proto.StringKind_MARKDOWN: + return configschema.StringMarkdown + } +} + +func schemaNestedBlock(b *proto.Schema_NestedBlock) *configschema.NestedBlock { + var nesting configschema.NestingMode + switch b.Nesting { + case proto.Schema_NestedBlock_SINGLE: + nesting = configschema.NestingSingle + case proto.Schema_NestedBlock_GROUP: + nesting = configschema.NestingGroup + case proto.Schema_NestedBlock_LIST: + nesting = configschema.NestingList + case proto.Schema_NestedBlock_MAP: + nesting = configschema.NestingMap + case proto.Schema_NestedBlock_SET: + nesting = configschema.NestingSet + default: + // In all other cases we'll leave it as the zero value (invalid) and + // let the caller validate it and deal with this. + } + + nb := &configschema.NestedBlock{ + Nesting: nesting, + MinItems: int(b.MinItems), + MaxItems: int(b.MaxItems), + } + + nested := ProtoToConfigSchema(b.Block) + nb.Block = *nested + return nb +} + +func protoObjectToConfigSchema(b *proto.Schema_Object) *configschema.Object { + var nesting configschema.NestingMode + switch b.Nesting { + case proto.Schema_Object_SINGLE: + nesting = configschema.NestingSingle + case proto.Schema_Object_LIST: + nesting = configschema.NestingList + case proto.Schema_Object_MAP: + nesting = configschema.NestingMap + case proto.Schema_Object_SET: + nesting = configschema.NestingSet + default: + // In all other cases we'll leave it as the zero value (invalid) and + // let the caller validate it and deal with this. + } + + object := &configschema.Object{ + Attributes: make(map[string]*configschema.Attribute), + Nesting: nesting, + MinItems: int(b.MinItems), + MaxItems: int(b.MaxItems), + } + + for _, a := range b.Attributes { + attr := &configschema.Attribute{ + Description: a.Description, + DescriptionKind: schemaStringKind(a.DescriptionKind), + Required: a.Required, + Optional: a.Optional, + Computed: a.Computed, + Sensitive: a.Sensitive, + Deprecated: a.Deprecated, + } + + if a.Type != nil { + if err := json.Unmarshal(a.Type, &attr.Type); err != nil { + panic(err) + } + } + + if a.NestedType != nil { + attr.NestedType = protoObjectToConfigSchema(a.NestedType) + } + + object.Attributes[a.Name] = attr + } + + return object +} + +// sortedKeys returns the lexically sorted keys from the given map. This is +// used to make schema conversions are deterministic. This panics if map keys +// are not a string. +func sortedKeys(m interface{}) []string { + v := reflect.ValueOf(m) + keys := make([]string, v.Len()) + + mapKeys := v.MapKeys() + for i, k := range mapKeys { + keys[i] = k.Interface().(string) + } + + sort.Strings(keys) + return keys +} + +func configschemaObjectToProto(b *configschema.Object) *proto.Schema_Object { + var nesting proto.Schema_Object_NestingMode + switch b.Nesting { + case configschema.NestingSingle: + nesting = proto.Schema_Object_SINGLE + case configschema.NestingList: + nesting = proto.Schema_Object_LIST + case configschema.NestingSet: + nesting = proto.Schema_Object_SET + case configschema.NestingMap: + nesting = proto.Schema_Object_MAP + default: + nesting = proto.Schema_Object_INVALID + } + + attributes := make([]*proto.Schema_Attribute, len(b.Attributes)) + + for _, name := range sortedKeys(b.Attributes) { + a := b.Attributes[name] + + attr := &proto.Schema_Attribute{ + Name: name, + Description: a.Description, + DescriptionKind: protoStringKind(a.DescriptionKind), + Optional: a.Optional, + Computed: a.Computed, + Required: a.Required, + Sensitive: a.Sensitive, + Deprecated: a.Deprecated, + } + + if a.Type != cty.NilType { + ty, err := json.Marshal(a.Type) + if err != nil { + panic(err) + } + attr.Type = ty + } + + if a.NestedType != nil { + attr.NestedType = configschemaObjectToProto(a.NestedType) + } + + attributes = append(attributes, attr) + } + + return &proto.Schema_Object{ + Attributes: attributes, + Nesting: nesting, + MinItems: int64(b.MinItems), + MaxItems: int64(b.MaxItems), + } +} diff --git a/vendor/github.com/hashicorp/terraform/plugin6/convert/schema_test.go b/vendor/github.com/hashicorp/terraform/plugin6/convert/schema_test.go new file mode 100644 index 00000000..1cb97273 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plugin6/convert/schema_test.go @@ -0,0 +1,568 @@ +package convert + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/terraform/configs/configschema" + proto "github.com/hashicorp/terraform/internal/tfplugin6" + "github.com/zclconf/go-cty/cty" +) + +var ( + equateEmpty = cmpopts.EquateEmpty() + typeComparer = cmp.Comparer(cty.Type.Equals) + valueComparer = cmp.Comparer(cty.Value.RawEquals) +) + +// Test that we can convert configschema to protobuf types and back again. +func TestConvertSchemaBlocks(t *testing.T) { + tests := map[string]struct { + Block *proto.Schema_Block + Want *configschema.Block + }{ + "attributes": { + &proto.Schema_Block{ + Attributes: []*proto.Schema_Attribute{ + { + Name: "computed", + Type: []byte(`["list","bool"]`), + Computed: true, + }, + { + Name: "optional", + Type: []byte(`"string"`), + Optional: true, + }, + { + Name: "optional_computed", + Type: []byte(`["map","bool"]`), + Optional: true, + Computed: true, + }, + { + Name: "required", + Type: []byte(`"number"`), + Required: true, + }, + { + Name: "nested_type", + NestedType: &proto.Schema_Object{ + Nesting: proto.Schema_Object_SINGLE, + Attributes: []*proto.Schema_Attribute{ + { + Name: "computed", + Type: []byte(`["list","bool"]`), + Computed: true, + }, + { + Name: "optional", + Type: []byte(`"string"`), + Optional: true, + }, + { + Name: "optional_computed", + Type: []byte(`["map","bool"]`), + Optional: true, + Computed: true, + }, + { + Name: "required", + Type: []byte(`"number"`), + Required: true, + }, + }, + }, + Required: true, + }, + { + Name: "deeply_nested_type", + NestedType: &proto.Schema_Object{ + Nesting: proto.Schema_Object_SINGLE, + Attributes: []*proto.Schema_Attribute{ + { + Name: "first_level", + NestedType: &proto.Schema_Object{ + Nesting: proto.Schema_Object_SINGLE, + Attributes: []*proto.Schema_Attribute{ + { + Name: "computed", + Type: []byte(`["list","bool"]`), + Computed: true, + }, + { + Name: "optional", + Type: []byte(`"string"`), + Optional: true, + }, + { + Name: "optional_computed", + Type: []byte(`["map","bool"]`), + Optional: true, + Computed: true, + }, + { + Name: "required", + Type: []byte(`"number"`), + Required: true, + }, + }, + }, + Computed: true, + }, + }, + }, + Required: true, + }, + { + Name: "nested_list", + NestedType: &proto.Schema_Object{ + Nesting: proto.Schema_Object_LIST, + Attributes: []*proto.Schema_Attribute{ + { + Name: "required", + Type: []byte(`"string"`), + Computed: true, + }, + }, + MinItems: 3, + }, + Required: true, + }, + { + Name: "nested_set", + NestedType: &proto.Schema_Object{ + Nesting: proto.Schema_Object_SET, + Attributes: []*proto.Schema_Attribute{ + { + Name: "required", + Type: []byte(`"string"`), + Computed: true, + }, + }, + }, + Required: true, + }, + { + Name: "nested_map", + NestedType: &proto.Schema_Object{ + Nesting: proto.Schema_Object_MAP, + Attributes: []*proto.Schema_Attribute{ + { + Name: "required", + Type: []byte(`"string"`), + Computed: true, + }, + }, + }, + Required: true, + }, + }, + }, + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "computed": { + Type: cty.List(cty.Bool), + Computed: true, + }, + "optional": { + Type: cty.String, + Optional: true, + }, + "optional_computed": { + Type: cty.Map(cty.Bool), + Optional: true, + Computed: true, + }, + "required": { + Type: cty.Number, + Required: true, + }, + "nested_type": { + NestedType: &configschema.Object{ + Attributes: map[string]*configschema.Attribute{ + "computed": { + Type: cty.List(cty.Bool), + Computed: true, + }, + "optional": { + Type: cty.String, + Optional: true, + }, + "optional_computed": { + Type: cty.Map(cty.Bool), + Optional: true, + Computed: true, + }, + "required": { + Type: cty.Number, + Required: true, + }, + }, + Nesting: configschema.NestingSingle, + }, + Required: true, + }, + "deeply_nested_type": { + NestedType: &configschema.Object{ + Attributes: map[string]*configschema.Attribute{ + "first_level": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSingle, + Attributes: map[string]*configschema.Attribute{ + "computed": { + Type: cty.List(cty.Bool), + Computed: true, + }, + "optional": { + Type: cty.String, + Optional: true, + }, + "optional_computed": { + Type: cty.Map(cty.Bool), + Optional: true, + Computed: true, + }, + "required": { + Type: cty.Number, + Required: true, + }, + }, + }, + Computed: true, + }, + }, + Nesting: configschema.NestingSingle, + }, + Required: true, + }, + "nested_list": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingList, + Attributes: map[string]*configschema.Attribute{ + "required": { + Type: cty.String, + Computed: true, + }, + }, + MinItems: 3, + }, + Required: true, + }, + "nested_map": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingMap, + Attributes: map[string]*configschema.Attribute{ + "required": { + Type: cty.String, + Computed: true, + }, + }, + }, + Required: true, + }, + "nested_set": { + NestedType: &configschema.Object{ + Nesting: configschema.NestingSet, + Attributes: map[string]*configschema.Attribute{ + "required": { + Type: cty.String, + Computed: true, + }, + }, + }, + Required: true, + }, + }, + }, + }, + "blocks": { + &proto.Schema_Block{ + BlockTypes: []*proto.Schema_NestedBlock{ + { + TypeName: "list", + Nesting: proto.Schema_NestedBlock_LIST, + Block: &proto.Schema_Block{}, + }, + { + TypeName: "map", + Nesting: proto.Schema_NestedBlock_MAP, + Block: &proto.Schema_Block{}, + }, + { + TypeName: "set", + Nesting: proto.Schema_NestedBlock_SET, + Block: &proto.Schema_Block{}, + }, + { + TypeName: "single", + Nesting: proto.Schema_NestedBlock_SINGLE, + Block: &proto.Schema_Block{ + Attributes: []*proto.Schema_Attribute{ + { + Name: "foo", + Type: []byte(`"dynamic"`), + Required: true, + }, + }, + }, + }, + }, + }, + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "list": &configschema.NestedBlock{ + Nesting: configschema.NestingList, + }, + "map": &configschema.NestedBlock{ + Nesting: configschema.NestingMap, + }, + "set": &configschema.NestedBlock{ + Nesting: configschema.NestingSet, + }, + "single": &configschema.NestedBlock{ + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.DynamicPseudoType, + Required: true, + }, + }, + }, + }, + }, + }, + }, + "deep block nesting": { + &proto.Schema_Block{ + BlockTypes: []*proto.Schema_NestedBlock{ + { + TypeName: "single", + Nesting: proto.Schema_NestedBlock_SINGLE, + Block: &proto.Schema_Block{ + BlockTypes: []*proto.Schema_NestedBlock{ + { + TypeName: "list", + Nesting: proto.Schema_NestedBlock_LIST, + Block: &proto.Schema_Block{ + BlockTypes: []*proto.Schema_NestedBlock{ + { + TypeName: "set", + Nesting: proto.Schema_NestedBlock_SET, + Block: &proto.Schema_Block{}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "single": &configschema.NestedBlock{ + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "list": &configschema.NestedBlock{ + Nesting: configschema.NestingList, + Block: configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "set": &configschema.NestedBlock{ + Nesting: configschema.NestingSet, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + converted := ProtoToConfigSchema(tc.Block) + if !cmp.Equal(converted, tc.Want, typeComparer, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, valueComparer, equateEmpty)) + } + }) + } +} + +// Test that we can convert configschema to protobuf types and back again. +func TestConvertProtoSchemaBlocks(t *testing.T) { + tests := map[string]struct { + Want *proto.Schema_Block + Block *configschema.Block + }{ + "attributes": { + &proto.Schema_Block{ + Attributes: []*proto.Schema_Attribute{ + { + Name: "computed", + Type: []byte(`["list","bool"]`), + Computed: true, + }, + { + Name: "optional", + Type: []byte(`"string"`), + Optional: true, + }, + { + Name: "optional_computed", + Type: []byte(`["map","bool"]`), + Optional: true, + Computed: true, + }, + { + Name: "required", + Type: []byte(`"number"`), + Required: true, + }, + }, + }, + &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "computed": { + Type: cty.List(cty.Bool), + Computed: true, + }, + "optional": { + Type: cty.String, + Optional: true, + }, + "optional_computed": { + Type: cty.Map(cty.Bool), + Optional: true, + Computed: true, + }, + "required": { + Type: cty.Number, + Required: true, + }, + }, + }, + }, + "blocks": { + &proto.Schema_Block{ + BlockTypes: []*proto.Schema_NestedBlock{ + { + TypeName: "list", + Nesting: proto.Schema_NestedBlock_LIST, + Block: &proto.Schema_Block{}, + }, + { + TypeName: "map", + Nesting: proto.Schema_NestedBlock_MAP, + Block: &proto.Schema_Block{}, + }, + { + TypeName: "set", + Nesting: proto.Schema_NestedBlock_SET, + Block: &proto.Schema_Block{}, + }, + { + TypeName: "single", + Nesting: proto.Schema_NestedBlock_SINGLE, + Block: &proto.Schema_Block{ + Attributes: []*proto.Schema_Attribute{ + { + Name: "foo", + Type: []byte(`"dynamic"`), + Required: true, + }, + }, + }, + }, + }, + }, + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "list": &configschema.NestedBlock{ + Nesting: configschema.NestingList, + }, + "map": &configschema.NestedBlock{ + Nesting: configschema.NestingMap, + }, + "set": &configschema.NestedBlock{ + Nesting: configschema.NestingSet, + }, + "single": &configschema.NestedBlock{ + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.DynamicPseudoType, + Required: true, + }, + }, + }, + }, + }, + }, + }, + "deep block nesting": { + &proto.Schema_Block{ + BlockTypes: []*proto.Schema_NestedBlock{ + { + TypeName: "single", + Nesting: proto.Schema_NestedBlock_SINGLE, + Block: &proto.Schema_Block{ + BlockTypes: []*proto.Schema_NestedBlock{ + { + TypeName: "list", + Nesting: proto.Schema_NestedBlock_LIST, + Block: &proto.Schema_Block{ + BlockTypes: []*proto.Schema_NestedBlock{ + { + TypeName: "set", + Nesting: proto.Schema_NestedBlock_SET, + Block: &proto.Schema_Block{}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "single": &configschema.NestedBlock{ + Nesting: configschema.NestingSingle, + Block: configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "list": &configschema.NestedBlock{ + Nesting: configschema.NestingList, + Block: configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "set": &configschema.NestedBlock{ + Nesting: configschema.NestingSet, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + converted := ConfigSchemaToProto(tc.Block) + if !cmp.Equal(converted, tc.Want, typeComparer, equateEmpty, ignoreUnexported) { + t.Fatal(cmp.Diff(converted, tc.Want, typeComparer, equateEmpty, ignoreUnexported)) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/plugin6/doc.go b/vendor/github.com/hashicorp/terraform/plugin6/doc.go new file mode 100644 index 00000000..5671893c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plugin6/doc.go @@ -0,0 +1,9 @@ +package plugin6 + +// plugin6 builds on types in package plugin to include support for plugin +// protocol v6. The main gRPC functions use by terraform (and initialized in +// init.go), such as Serve, are in the plugin package. The version of those +// functions in this package are used by various mocks and in tests. + +// When provider protocol v5 is deprecated, some functions may need to be moved +// here, or the existing functions updated, before removing the plugin pacakge. diff --git a/vendor/github.com/hashicorp/terraform/plugin6/grpc_error.go b/vendor/github.com/hashicorp/terraform/plugin6/grpc_error.go new file mode 100644 index 00000000..4781d821 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plugin6/grpc_error.go @@ -0,0 +1,74 @@ +package plugin6 + +import ( + "fmt" + "path" + "runtime" + + "github.com/hashicorp/terraform/tfdiags" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// grpcErr extracts some known error types and formats them into better +// representations for core. This must only be called from plugin methods. +// Since we don't use RPC status errors for the plugin protocol, these do not +// contain any useful details, and we can return some text that at least +// indicates the plugin call and possible error condition. +func grpcErr(err error) (diags tfdiags.Diagnostics) { + if err == nil { + return + } + + // extract the method name from the caller. + pc, _, _, ok := runtime.Caller(1) + if !ok { + logger.Error("unknown grpc call", "error", err) + return diags.Append(err) + } + + f := runtime.FuncForPC(pc) + + // Function names will contain the full import path. Take the last + // segment, which will let users know which method was being called. + _, requestName := path.Split(f.Name()) + + // Here we can at least correlate the error in the logs to a particular binary. + logger.Error(requestName, "error", err) + + // TODO: while this expands the error codes into somewhat better messages, + // this still does not easily link the error to an actual user-recognizable + // plugin. The grpc plugin does not know its configured name, and the + // errors are in a list of diagnostics, making it hard for the caller to + // annotate the returned errors. + switch status.Code(err) { + case codes.Unavailable: + // This case is when the plugin has stopped running for some reason, + // and is usually the result of a crash. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Plugin did not respond", + fmt.Sprintf("The plugin encountered an error, and failed to respond to the %s call. "+ + "The plugin logs may contain more details.", requestName), + )) + case codes.Canceled: + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Request cancelled", + fmt.Sprintf("The %s request was cancelled.", requestName), + )) + case codes.Unimplemented: + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Unsupported plugin method", + fmt.Sprintf("The %s method is not supported by this plugin.", requestName), + )) + default: + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Plugin error", + fmt.Sprintf("The plugin returned an unexpected error from %s: %v", requestName, err), + )) + } + return +} diff --git a/vendor/github.com/hashicorp/terraform/plugin6/grpc_provider.go b/vendor/github.com/hashicorp/terraform/plugin6/grpc_provider.go new file mode 100644 index 00000000..23e99a79 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plugin6/grpc_provider.go @@ -0,0 +1,617 @@ +package plugin6 + +import ( + "context" + "errors" + "sync" + + "github.com/zclconf/go-cty/cty" + + plugin "github.com/hashicorp/go-plugin" + "github.com/hashicorp/terraform/internal/logging" + proto6 "github.com/hashicorp/terraform/internal/tfplugin6" + "github.com/hashicorp/terraform/plugin6/convert" + "github.com/hashicorp/terraform/providers" + ctyjson "github.com/zclconf/go-cty/cty/json" + "github.com/zclconf/go-cty/cty/msgpack" + "google.golang.org/grpc" +) + +var logger = logging.HCLogger() + +// GRPCProviderPlugin implements plugin.GRPCPlugin for the go-plugin package. +type GRPCProviderPlugin struct { + plugin.Plugin + GRPCProvider func() proto6.ProviderServer +} + +func (p *GRPCProviderPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { + return &GRPCProvider{ + client: proto6.NewProviderClient(c), + ctx: ctx, + }, nil +} + +func (p *GRPCProviderPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error { + proto6.RegisterProviderServer(s, p.GRPCProvider()) + return nil +} + +// GRPCProvider handles the client, or core side of the plugin rpc connection. +// The GRPCProvider methods are mostly a translation layer between the +// terraform providers types and the grpc proto types, directly converting +// between the two. +type GRPCProvider struct { + // PluginClient provides a reference to the plugin.Client which controls the plugin process. + // This allows the GRPCProvider a way to shutdown the plugin process. + PluginClient *plugin.Client + + // TestServer contains a grpc.Server to close when the GRPCProvider is being + // used in an end to end test of a provider. + TestServer *grpc.Server + + // Proto client use to make the grpc service calls. + client proto6.ProviderClient + + // this context is created by the plugin package, and is canceled when the + // plugin process ends. + ctx context.Context + + // schema stores the schema for this provider. This is used to properly + // serialize the state for requests. + mu sync.Mutex + schemas providers.GetProviderSchemaResponse +} + +func New(client proto6.ProviderClient, ctx context.Context) GRPCProvider { + return GRPCProvider{ + client: client, + ctx: ctx, + } +} + +// getSchema is used internally to get the saved provider schema. The schema +// should have already been fetched from the provider, but we have to +// synchronize access to avoid being called concurrently with GetProviderSchema. +func (p *GRPCProvider) getSchema() providers.GetProviderSchemaResponse { + p.mu.Lock() + // unlock inline in case GetProviderSchema needs to be called + if p.schemas.Provider.Block != nil { + p.mu.Unlock() + return p.schemas + } + p.mu.Unlock() + + // the schema should have been fetched already, but give it another shot + // just in case things are being called out of order. This may happen for + // tests. + schemas := p.GetProviderSchema() + if schemas.Diagnostics.HasErrors() { + panic(schemas.Diagnostics.Err()) + } + + return schemas +} + +// getResourceSchema is a helper to extract the schema for a resource, and +// panics if the schema is not available. +func (p *GRPCProvider) getResourceSchema(name string) providers.Schema { + schema := p.getSchema() + resSchema, ok := schema.ResourceTypes[name] + if !ok { + panic("unknown resource type " + name) + } + return resSchema +} + +// gettDatasourceSchema is a helper to extract the schema for a datasource, and +// panics if that schema is not available. +func (p *GRPCProvider) getDatasourceSchema(name string) providers.Schema { + schema := p.getSchema() + dataSchema, ok := schema.DataSources[name] + if !ok { + panic("unknown data source " + name) + } + return dataSchema +} + +// getProviderMetaSchema is a helper to extract the schema for the meta info +// defined for a provider, +func (p *GRPCProvider) getProviderMetaSchema() providers.Schema { + schema := p.getSchema() + return schema.ProviderMeta +} + +func (p *GRPCProvider) GetProviderSchema() (resp providers.GetProviderSchemaResponse) { + logger.Trace("GRPCProvider.v6: GetProviderSchema") + p.mu.Lock() + defer p.mu.Unlock() + + if p.schemas.Provider.Block != nil { + return p.schemas + } + + resp.ResourceTypes = make(map[string]providers.Schema) + resp.DataSources = make(map[string]providers.Schema) + + // Some providers may generate quite large schemas, and the internal default + // grpc response size limit is 4MB. 64MB should cover most any use case, and + // if we get providers nearing that we may want to consider a finer-grained + // API to fetch individual resource schemas. + // Note: this option is marked as EXPERIMENTAL in the grpc API. + const maxRecvSize = 64 << 20 + protoResp, err := p.client.GetProviderSchema(p.ctx, new(proto6.GetProviderSchema_Request), grpc.MaxRecvMsgSizeCallOption{MaxRecvMsgSize: maxRecvSize}) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) + return resp + } + + resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) + + if protoResp.Provider == nil { + resp.Diagnostics = resp.Diagnostics.Append(errors.New("missing provider schema")) + return resp + } + + resp.Provider = convert.ProtoToProviderSchema(protoResp.Provider) + if protoResp.ProviderMeta == nil { + logger.Debug("No provider meta schema returned") + } else { + resp.ProviderMeta = convert.ProtoToProviderSchema(protoResp.ProviderMeta) + } + + for name, res := range protoResp.ResourceSchemas { + resp.ResourceTypes[name] = convert.ProtoToProviderSchema(res) + } + + for name, data := range protoResp.DataSourceSchemas { + resp.DataSources[name] = convert.ProtoToProviderSchema(data) + } + + p.schemas = resp + + return resp +} + +func (p *GRPCProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) { + logger.Trace("GRPCProvider.v6: ValidateProviderConfig") + + schema := p.getSchema() + ty := schema.Provider.Block.ImpliedType() + + mp, err := msgpack.Marshal(r.Config, ty) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + + protoReq := &proto6.ValidateProviderConfig_Request{ + Config: &proto6.DynamicValue{Msgpack: mp}, + } + + protoResp, err := p.client.ValidateProviderConfig(p.ctx, protoReq) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) + return resp + } + + resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) + return resp +} + +func (p *GRPCProvider) ValidateResourceConfig(r providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) { + logger.Trace("GRPCProvider.v6: ValidateResourceConfig") + resourceSchema := p.getResourceSchema(r.TypeName) + + mp, err := msgpack.Marshal(r.Config, resourceSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + + protoReq := &proto6.ValidateResourceConfig_Request{ + TypeName: r.TypeName, + Config: &proto6.DynamicValue{Msgpack: mp}, + } + + protoResp, err := p.client.ValidateResourceConfig(p.ctx, protoReq) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) + return resp + } + + resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) + return resp +} + +func (p *GRPCProvider) ValidateDataResourceConfig(r providers.ValidateDataResourceConfigRequest) (resp providers.ValidateDataResourceConfigResponse) { + logger.Trace("GRPCProvider.v6: ValidateDataResourceConfig") + + dataSchema := p.getDatasourceSchema(r.TypeName) + + mp, err := msgpack.Marshal(r.Config, dataSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + + protoReq := &proto6.ValidateDataResourceConfig_Request{ + TypeName: r.TypeName, + Config: &proto6.DynamicValue{Msgpack: mp}, + } + + protoResp, err := p.client.ValidateDataResourceConfig(p.ctx, protoReq) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) + return resp + } + resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) + return resp +} + +func (p *GRPCProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) { + logger.Trace("GRPCProvider.v6: UpgradeResourceState") + + resSchema := p.getResourceSchema(r.TypeName) + + protoReq := &proto6.UpgradeResourceState_Request{ + TypeName: r.TypeName, + Version: int64(r.Version), + RawState: &proto6.RawState{ + Json: r.RawStateJSON, + Flatmap: r.RawStateFlatmap, + }, + } + + protoResp, err := p.client.UpgradeResourceState(p.ctx, protoReq) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) + return resp + } + resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) + + ty := resSchema.Block.ImpliedType() + resp.UpgradedState = cty.NullVal(ty) + if protoResp.UpgradedState == nil { + return resp + } + + state, err := decodeDynamicValue(protoResp.UpgradedState, ty) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + resp.UpgradedState = state + + return resp +} + +func (p *GRPCProvider) ConfigureProvider(r providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { + logger.Trace("GRPCProvider.v6: ConfigureProvider") + + schema := p.getSchema() + + var mp []byte + + // we don't have anything to marshal if there's no config + mp, err := msgpack.Marshal(r.Config, schema.Provider.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + + protoReq := &proto6.ConfigureProvider_Request{ + TerraformVersion: r.TerraformVersion, + Config: &proto6.DynamicValue{ + Msgpack: mp, + }, + } + + protoResp, err := p.client.ConfigureProvider(p.ctx, protoReq) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) + return resp + } + resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) + return resp +} + +func (p *GRPCProvider) Stop() error { + logger.Trace("GRPCProvider.v6: Stop") + + resp, err := p.client.StopProvider(p.ctx, new(proto6.StopProvider_Request)) + if err != nil { + return err + } + + if resp.Error != "" { + return errors.New(resp.Error) + } + return nil +} + +func (p *GRPCProvider) ReadResource(r providers.ReadResourceRequest) (resp providers.ReadResourceResponse) { + logger.Trace("GRPCProvider.v6: ReadResource") + + resSchema := p.getResourceSchema(r.TypeName) + metaSchema := p.getProviderMetaSchema() + + mp, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + + protoReq := &proto6.ReadResource_Request{ + TypeName: r.TypeName, + CurrentState: &proto6.DynamicValue{Msgpack: mp}, + Private: r.Private, + } + + if metaSchema.Block != nil { + metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + protoReq.ProviderMeta = &proto6.DynamicValue{Msgpack: metaMP} + } + + protoResp, err := p.client.ReadResource(p.ctx, protoReq) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) + return resp + } + resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) + + state, err := decodeDynamicValue(protoResp.NewState, resSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + resp.NewState = state + resp.Private = protoResp.Private + + return resp +} + +func (p *GRPCProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { + logger.Trace("GRPCProvider.v6: PlanResourceChange") + + resSchema := p.getResourceSchema(r.TypeName) + metaSchema := p.getProviderMetaSchema() + + priorMP, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + + configMP, err := msgpack.Marshal(r.Config, resSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + + propMP, err := msgpack.Marshal(r.ProposedNewState, resSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + + protoReq := &proto6.PlanResourceChange_Request{ + TypeName: r.TypeName, + PriorState: &proto6.DynamicValue{Msgpack: priorMP}, + Config: &proto6.DynamicValue{Msgpack: configMP}, + ProposedNewState: &proto6.DynamicValue{Msgpack: propMP}, + PriorPrivate: r.PriorPrivate, + } + + if metaSchema.Block != nil { + metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + protoReq.ProviderMeta = &proto6.DynamicValue{Msgpack: metaMP} + } + + protoResp, err := p.client.PlanResourceChange(p.ctx, protoReq) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) + return resp + } + resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) + + state, err := decodeDynamicValue(protoResp.PlannedState, resSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + resp.PlannedState = state + + for _, p := range protoResp.RequiresReplace { + resp.RequiresReplace = append(resp.RequiresReplace, convert.AttributePathToPath(p)) + } + + resp.PlannedPrivate = protoResp.PlannedPrivate + + return resp +} + +func (p *GRPCProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { + logger.Trace("GRPCProvider.v6: ApplyResourceChange") + + resSchema := p.getResourceSchema(r.TypeName) + metaSchema := p.getProviderMetaSchema() + + priorMP, err := msgpack.Marshal(r.PriorState, resSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + plannedMP, err := msgpack.Marshal(r.PlannedState, resSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + configMP, err := msgpack.Marshal(r.Config, resSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + + protoReq := &proto6.ApplyResourceChange_Request{ + TypeName: r.TypeName, + PriorState: &proto6.DynamicValue{Msgpack: priorMP}, + PlannedState: &proto6.DynamicValue{Msgpack: plannedMP}, + Config: &proto6.DynamicValue{Msgpack: configMP}, + PlannedPrivate: r.PlannedPrivate, + } + + if metaSchema.Block != nil { + metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + protoReq.ProviderMeta = &proto6.DynamicValue{Msgpack: metaMP} + } + + protoResp, err := p.client.ApplyResourceChange(p.ctx, protoReq) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) + return resp + } + resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) + + resp.Private = protoResp.Private + + state, err := decodeDynamicValue(protoResp.NewState, resSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + resp.NewState = state + + return resp +} + +func (p *GRPCProvider) ImportResourceState(r providers.ImportResourceStateRequest) (resp providers.ImportResourceStateResponse) { + logger.Trace("GRPCProvider.v6: ImportResourceState") + + protoReq := &proto6.ImportResourceState_Request{ + TypeName: r.TypeName, + Id: r.ID, + } + + protoResp, err := p.client.ImportResourceState(p.ctx, protoReq) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) + return resp + } + resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) + + for _, imported := range protoResp.ImportedResources { + resource := providers.ImportedResource{ + TypeName: imported.TypeName, + Private: imported.Private, + } + + resSchema := p.getResourceSchema(resource.TypeName) + state, err := decodeDynamicValue(imported.State, resSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + resource.State = state + resp.ImportedResources = append(resp.ImportedResources, resource) + } + + return resp +} + +func (p *GRPCProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { + logger.Trace("GRPCProvider.v6: ReadDataSource") + + dataSchema := p.getDatasourceSchema(r.TypeName) + metaSchema := p.getProviderMetaSchema() + + config, err := msgpack.Marshal(r.Config, dataSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + + protoReq := &proto6.ReadDataSource_Request{ + TypeName: r.TypeName, + Config: &proto6.DynamicValue{ + Msgpack: config, + }, + } + + if metaSchema.Block != nil { + metaMP, err := msgpack.Marshal(r.ProviderMeta, metaSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + protoReq.ProviderMeta = &proto6.DynamicValue{Msgpack: metaMP} + } + + protoResp, err := p.client.ReadDataSource(p.ctx, protoReq) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(grpcErr(err)) + return resp + } + resp.Diagnostics = resp.Diagnostics.Append(convert.ProtoToDiagnostics(protoResp.Diagnostics)) + + state, err := decodeDynamicValue(protoResp.State, dataSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + resp.State = state + + return resp +} + +// closing the grpc connection is final, and terraform will call it at the end of every phase. +func (p *GRPCProvider) Close() error { + logger.Trace("GRPCProvider.v6: Close") + + // Make sure to stop the server if we're not running within go-plugin. + if p.TestServer != nil { + p.TestServer.Stop() + } + + // Check this since it's not automatically inserted during plugin creation. + // It's currently only inserted by the command package, because that is + // where the factory is built and is the only point with access to the + // plugin.Client. + if p.PluginClient == nil { + logger.Debug("provider has no plugin.Client") + return nil + } + + p.PluginClient.Kill() + return nil +} + +// Decode a DynamicValue from either the JSON or MsgPack encoding. +func decodeDynamicValue(v *proto6.DynamicValue, ty cty.Type) (cty.Value, error) { + // always return a valid value + var err error + res := cty.NullVal(ty) + if v == nil { + return res, nil + } + + switch { + case len(v.Msgpack) > 0: + res, err = msgpack.Unmarshal(v.Msgpack, ty) + case len(v.Json) > 0: + res, err = ctyjson.Unmarshal(v.Json, ty) + } + return res, err +} diff --git a/vendor/github.com/hashicorp/terraform/plugin6/grpc_provider_test.go b/vendor/github.com/hashicorp/terraform/plugin6/grpc_provider_test.go new file mode 100644 index 00000000..2f8e4966 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plugin6/grpc_provider_test.go @@ -0,0 +1,722 @@ +package plugin6 + +import ( + "bytes" + "fmt" + "testing" + + "github.com/golang/mock/gomock" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/terraform/configs/hcl2shim" + "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/tfdiags" + "github.com/zclconf/go-cty/cty" + + proto "github.com/hashicorp/terraform/internal/tfplugin6" + mockproto "github.com/hashicorp/terraform/plugin6/mock_proto" +) + +var _ providers.Interface = (*GRPCProvider)(nil) + +var ( + equateEmpty = cmpopts.EquateEmpty() + typeComparer = cmp.Comparer(cty.Type.Equals) + valueComparer = cmp.Comparer(cty.Value.RawEquals) +) + +func mockProviderClient(t *testing.T) *mockproto.MockProviderClient { + ctrl := gomock.NewController(t) + client := mockproto.NewMockProviderClient(ctrl) + + // we always need a GetSchema method + client.EXPECT().GetProviderSchema( + gomock.Any(), + gomock.Any(), + gomock.Any(), + ).Return(providerProtoSchema(), nil) + + return client +} + +func checkDiags(t *testing.T, d tfdiags.Diagnostics) { + t.Helper() + if d.HasErrors() { + t.Fatal(d.Err()) + } +} + +func providerProtoSchema() *proto.GetProviderSchema_Response { + return &proto.GetProviderSchema_Response{ + Provider: &proto.Schema{ + Block: &proto.Schema_Block{ + Attributes: []*proto.Schema_Attribute{ + { + Name: "attr", + Type: []byte(`"string"`), + Required: true, + }, + }, + }, + }, + ResourceSchemas: map[string]*proto.Schema{ + "resource": { + Version: 1, + Block: &proto.Schema_Block{ + Attributes: []*proto.Schema_Attribute{ + { + Name: "attr", + Type: []byte(`"string"`), + Required: true, + }, + }, + }, + }, + }, + DataSourceSchemas: map[string]*proto.Schema{ + "data": { + Version: 1, + Block: &proto.Schema_Block{ + Attributes: []*proto.Schema_Attribute{ + { + Name: "attr", + Type: []byte(`"string"`), + Required: true, + }, + }, + }, + }, + }, + } +} + +func TestGRPCProvider_GetSchema(t *testing.T) { + p := &GRPCProvider{ + client: mockProviderClient(t), + } + + resp := p.GetProviderSchema() + checkDiags(t, resp.Diagnostics) +} + +func TestGRPCProvider_PrepareProviderConfig(t *testing.T) { + client := mockProviderClient(t) + p := &GRPCProvider{ + client: client, + } + + client.EXPECT().ValidateProviderConfig( + gomock.Any(), + gomock.Any(), + ).Return(&proto.ValidateProviderConfig_Response{}, nil) + + cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"}) + resp := p.ValidateProviderConfig(providers.ValidateProviderConfigRequest{Config: cfg}) + checkDiags(t, resp.Diagnostics) +} + +func TestGRPCProvider_ValidateResourceConfig(t *testing.T) { + client := mockProviderClient(t) + p := &GRPCProvider{ + client: client, + } + + client.EXPECT().ValidateResourceConfig( + gomock.Any(), + gomock.Any(), + ).Return(&proto.ValidateResourceConfig_Response{}, nil) + + cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"}) + resp := p.ValidateResourceConfig(providers.ValidateResourceConfigRequest{ + TypeName: "resource", + Config: cfg, + }) + checkDiags(t, resp.Diagnostics) +} + +func TestGRPCProvider_ValidateDataResourceConfig(t *testing.T) { + client := mockProviderClient(t) + p := &GRPCProvider{ + client: client, + } + + client.EXPECT().ValidateDataResourceConfig( + gomock.Any(), + gomock.Any(), + ).Return(&proto.ValidateDataResourceConfig_Response{}, nil) + + cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"}) + resp := p.ValidateDataResourceConfig(providers.ValidateDataResourceConfigRequest{ + TypeName: "data", + Config: cfg, + }) + checkDiags(t, resp.Diagnostics) +} + +func TestGRPCProvider_UpgradeResourceState(t *testing.T) { + client := mockProviderClient(t) + p := &GRPCProvider{ + client: client, + } + + client.EXPECT().UpgradeResourceState( + gomock.Any(), + gomock.Any(), + ).Return(&proto.UpgradeResourceState_Response{ + UpgradedState: &proto.DynamicValue{ + Msgpack: []byte("\x81\xa4attr\xa3bar"), + }, + }, nil) + + resp := p.UpgradeResourceState(providers.UpgradeResourceStateRequest{ + TypeName: "resource", + Version: 0, + RawStateJSON: []byte(`{"old_attr":"bar"}`), + }) + checkDiags(t, resp.Diagnostics) + + expected := cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }) + + if !cmp.Equal(expected, resp.UpgradedState, typeComparer, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(expected, resp.UpgradedState, typeComparer, valueComparer, equateEmpty)) + } +} + +func TestGRPCProvider_UpgradeResourceStateJSON(t *testing.T) { + client := mockProviderClient(t) + p := &GRPCProvider{ + client: client, + } + + client.EXPECT().UpgradeResourceState( + gomock.Any(), + gomock.Any(), + ).Return(&proto.UpgradeResourceState_Response{ + UpgradedState: &proto.DynamicValue{ + Json: []byte(`{"attr":"bar"}`), + }, + }, nil) + + resp := p.UpgradeResourceState(providers.UpgradeResourceStateRequest{ + TypeName: "resource", + Version: 0, + RawStateJSON: []byte(`{"old_attr":"bar"}`), + }) + checkDiags(t, resp.Diagnostics) + + expected := cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }) + + if !cmp.Equal(expected, resp.UpgradedState, typeComparer, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(expected, resp.UpgradedState, typeComparer, valueComparer, equateEmpty)) + } +} + +func TestGRPCProvider_Configure(t *testing.T) { + client := mockProviderClient(t) + p := &GRPCProvider{ + client: client, + } + + client.EXPECT().ConfigureProvider( + gomock.Any(), + gomock.Any(), + ).Return(&proto.ConfigureProvider_Response{}, nil) + + resp := p.ConfigureProvider(providers.ConfigureProviderRequest{ + Config: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("foo"), + }), + }) + checkDiags(t, resp.Diagnostics) +} + +func TestGRPCProvider_Stop(t *testing.T) { + client := mockProviderClient(t) + p := &GRPCProvider{ + client: client, + } + + client.EXPECT().StopProvider( + gomock.Any(), + gomock.Any(), + ).Return(&proto.StopProvider_Response{}, nil) + + err := p.Stop() + if err != nil { + t.Fatal(err) + } +} + +func TestGRPCProvider_ReadResource(t *testing.T) { + client := mockProviderClient(t) + p := &GRPCProvider{ + client: client, + } + + client.EXPECT().ReadResource( + gomock.Any(), + gomock.Any(), + ).Return(&proto.ReadResource_Response{ + NewState: &proto.DynamicValue{ + Msgpack: []byte("\x81\xa4attr\xa3bar"), + }, + }, nil) + + resp := p.ReadResource(providers.ReadResourceRequest{ + TypeName: "resource", + PriorState: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("foo"), + }), + }) + + checkDiags(t, resp.Diagnostics) + + expected := cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }) + + if !cmp.Equal(expected, resp.NewState, typeComparer, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(expected, resp.NewState, typeComparer, valueComparer, equateEmpty)) + } +} + +func TestGRPCProvider_ReadResourceJSON(t *testing.T) { + client := mockProviderClient(t) + p := &GRPCProvider{ + client: client, + } + + client.EXPECT().ReadResource( + gomock.Any(), + gomock.Any(), + ).Return(&proto.ReadResource_Response{ + NewState: &proto.DynamicValue{ + Json: []byte(`{"attr":"bar"}`), + }, + }, nil) + + resp := p.ReadResource(providers.ReadResourceRequest{ + TypeName: "resource", + PriorState: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("foo"), + }), + }) + + checkDiags(t, resp.Diagnostics) + + expected := cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }) + + if !cmp.Equal(expected, resp.NewState, typeComparer, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(expected, resp.NewState, typeComparer, valueComparer, equateEmpty)) + } +} + +func TestGRPCProvider_ReadEmptyJSON(t *testing.T) { + client := mockProviderClient(t) + p := &GRPCProvider{ + client: client, + } + + client.EXPECT().ReadResource( + gomock.Any(), + gomock.Any(), + ).Return(&proto.ReadResource_Response{ + NewState: &proto.DynamicValue{ + Json: []byte(``), + }, + }, nil) + + obj := cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("foo"), + }) + resp := p.ReadResource(providers.ReadResourceRequest{ + TypeName: "resource", + PriorState: obj, + }) + + checkDiags(t, resp.Diagnostics) + + expected := cty.NullVal(obj.Type()) + + if !cmp.Equal(expected, resp.NewState, typeComparer, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(expected, resp.NewState, typeComparer, valueComparer, equateEmpty)) + } +} + +func TestGRPCProvider_PlanResourceChange(t *testing.T) { + client := mockProviderClient(t) + p := &GRPCProvider{ + client: client, + } + + expectedPrivate := []byte(`{"meta": "data"}`) + + client.EXPECT().PlanResourceChange( + gomock.Any(), + gomock.Any(), + ).Return(&proto.PlanResourceChange_Response{ + PlannedState: &proto.DynamicValue{ + Msgpack: []byte("\x81\xa4attr\xa3bar"), + }, + RequiresReplace: []*proto.AttributePath{ + { + Steps: []*proto.AttributePath_Step{ + { + Selector: &proto.AttributePath_Step_AttributeName{ + AttributeName: "attr", + }, + }, + }, + }, + }, + PlannedPrivate: expectedPrivate, + }, nil) + + resp := p.PlanResourceChange(providers.PlanResourceChangeRequest{ + TypeName: "resource", + PriorState: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("foo"), + }), + ProposedNewState: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }), + Config: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }), + }) + + checkDiags(t, resp.Diagnostics) + + expectedState := cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }) + + if !cmp.Equal(expectedState, resp.PlannedState, typeComparer, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(expectedState, resp.PlannedState, typeComparer, valueComparer, equateEmpty)) + } + + expectedReplace := `[]cty.Path{cty.Path{cty.GetAttrStep{Name:"attr"}}}` + replace := fmt.Sprintf("%#v", resp.RequiresReplace) + if expectedReplace != replace { + t.Fatalf("expected %q, got %q", expectedReplace, replace) + } + + if !bytes.Equal(expectedPrivate, resp.PlannedPrivate) { + t.Fatalf("expected %q, got %q", expectedPrivate, resp.PlannedPrivate) + } +} + +func TestGRPCProvider_PlanResourceChangeJSON(t *testing.T) { + client := mockProviderClient(t) + p := &GRPCProvider{ + client: client, + } + + expectedPrivate := []byte(`{"meta": "data"}`) + + client.EXPECT().PlanResourceChange( + gomock.Any(), + gomock.Any(), + ).Return(&proto.PlanResourceChange_Response{ + PlannedState: &proto.DynamicValue{ + Json: []byte(`{"attr":"bar"}`), + }, + RequiresReplace: []*proto.AttributePath{ + { + Steps: []*proto.AttributePath_Step{ + { + Selector: &proto.AttributePath_Step_AttributeName{ + AttributeName: "attr", + }, + }, + }, + }, + }, + PlannedPrivate: expectedPrivate, + }, nil) + + resp := p.PlanResourceChange(providers.PlanResourceChangeRequest{ + TypeName: "resource", + PriorState: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("foo"), + }), + ProposedNewState: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }), + Config: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }), + }) + + checkDiags(t, resp.Diagnostics) + + expectedState := cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }) + + if !cmp.Equal(expectedState, resp.PlannedState, typeComparer, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(expectedState, resp.PlannedState, typeComparer, valueComparer, equateEmpty)) + } + + expectedReplace := `[]cty.Path{cty.Path{cty.GetAttrStep{Name:"attr"}}}` + replace := fmt.Sprintf("%#v", resp.RequiresReplace) + if expectedReplace != replace { + t.Fatalf("expected %q, got %q", expectedReplace, replace) + } + + if !bytes.Equal(expectedPrivate, resp.PlannedPrivate) { + t.Fatalf("expected %q, got %q", expectedPrivate, resp.PlannedPrivate) + } +} + +func TestGRPCProvider_ApplyResourceChange(t *testing.T) { + client := mockProviderClient(t) + p := &GRPCProvider{ + client: client, + } + + expectedPrivate := []byte(`{"meta": "data"}`) + + client.EXPECT().ApplyResourceChange( + gomock.Any(), + gomock.Any(), + ).Return(&proto.ApplyResourceChange_Response{ + NewState: &proto.DynamicValue{ + Msgpack: []byte("\x81\xa4attr\xa3bar"), + }, + Private: expectedPrivate, + }, nil) + + resp := p.ApplyResourceChange(providers.ApplyResourceChangeRequest{ + TypeName: "resource", + PriorState: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("foo"), + }), + PlannedState: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }), + Config: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }), + PlannedPrivate: expectedPrivate, + }) + + checkDiags(t, resp.Diagnostics) + + expectedState := cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }) + + if !cmp.Equal(expectedState, resp.NewState, typeComparer, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(expectedState, resp.NewState, typeComparer, valueComparer, equateEmpty)) + } + + if !bytes.Equal(expectedPrivate, resp.Private) { + t.Fatalf("expected %q, got %q", expectedPrivate, resp.Private) + } +} +func TestGRPCProvider_ApplyResourceChangeJSON(t *testing.T) { + client := mockProviderClient(t) + p := &GRPCProvider{ + client: client, + } + + expectedPrivate := []byte(`{"meta": "data"}`) + + client.EXPECT().ApplyResourceChange( + gomock.Any(), + gomock.Any(), + ).Return(&proto.ApplyResourceChange_Response{ + NewState: &proto.DynamicValue{ + Json: []byte(`{"attr":"bar"}`), + }, + Private: expectedPrivate, + }, nil) + + resp := p.ApplyResourceChange(providers.ApplyResourceChangeRequest{ + TypeName: "resource", + PriorState: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("foo"), + }), + PlannedState: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }), + Config: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }), + PlannedPrivate: expectedPrivate, + }) + + checkDiags(t, resp.Diagnostics) + + expectedState := cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }) + + if !cmp.Equal(expectedState, resp.NewState, typeComparer, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(expectedState, resp.NewState, typeComparer, valueComparer, equateEmpty)) + } + + if !bytes.Equal(expectedPrivate, resp.Private) { + t.Fatalf("expected %q, got %q", expectedPrivate, resp.Private) + } +} + +func TestGRPCProvider_ImportResourceState(t *testing.T) { + client := mockProviderClient(t) + p := &GRPCProvider{ + client: client, + } + + expectedPrivate := []byte(`{"meta": "data"}`) + + client.EXPECT().ImportResourceState( + gomock.Any(), + gomock.Any(), + ).Return(&proto.ImportResourceState_Response{ + ImportedResources: []*proto.ImportResourceState_ImportedResource{ + { + TypeName: "resource", + State: &proto.DynamicValue{ + Msgpack: []byte("\x81\xa4attr\xa3bar"), + }, + Private: expectedPrivate, + }, + }, + }, nil) + + resp := p.ImportResourceState(providers.ImportResourceStateRequest{ + TypeName: "resource", + ID: "foo", + }) + + checkDiags(t, resp.Diagnostics) + + expectedResource := providers.ImportedResource{ + TypeName: "resource", + State: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }), + Private: expectedPrivate, + } + + imported := resp.ImportedResources[0] + if !cmp.Equal(expectedResource, imported, typeComparer, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(expectedResource, imported, typeComparer, valueComparer, equateEmpty)) + } +} +func TestGRPCProvider_ImportResourceStateJSON(t *testing.T) { + client := mockProviderClient(t) + p := &GRPCProvider{ + client: client, + } + + expectedPrivate := []byte(`{"meta": "data"}`) + + client.EXPECT().ImportResourceState( + gomock.Any(), + gomock.Any(), + ).Return(&proto.ImportResourceState_Response{ + ImportedResources: []*proto.ImportResourceState_ImportedResource{ + { + TypeName: "resource", + State: &proto.DynamicValue{ + Json: []byte(`{"attr":"bar"}`), + }, + Private: expectedPrivate, + }, + }, + }, nil) + + resp := p.ImportResourceState(providers.ImportResourceStateRequest{ + TypeName: "resource", + ID: "foo", + }) + + checkDiags(t, resp.Diagnostics) + + expectedResource := providers.ImportedResource{ + TypeName: "resource", + State: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }), + Private: expectedPrivate, + } + + imported := resp.ImportedResources[0] + if !cmp.Equal(expectedResource, imported, typeComparer, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(expectedResource, imported, typeComparer, valueComparer, equateEmpty)) + } +} + +func TestGRPCProvider_ReadDataSource(t *testing.T) { + client := mockProviderClient(t) + p := &GRPCProvider{ + client: client, + } + + client.EXPECT().ReadDataSource( + gomock.Any(), + gomock.Any(), + ).Return(&proto.ReadDataSource_Response{ + State: &proto.DynamicValue{ + Msgpack: []byte("\x81\xa4attr\xa3bar"), + }, + }, nil) + + resp := p.ReadDataSource(providers.ReadDataSourceRequest{ + TypeName: "data", + Config: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("foo"), + }), + }) + + checkDiags(t, resp.Diagnostics) + + expected := cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }) + + if !cmp.Equal(expected, resp.State, typeComparer, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(expected, resp.State, typeComparer, valueComparer, equateEmpty)) + } +} + +func TestGRPCProvider_ReadDataSourceJSON(t *testing.T) { + client := mockProviderClient(t) + p := &GRPCProvider{ + client: client, + } + + client.EXPECT().ReadDataSource( + gomock.Any(), + gomock.Any(), + ).Return(&proto.ReadDataSource_Response{ + State: &proto.DynamicValue{ + Json: []byte(`{"attr":"bar"}`), + }, + }, nil) + + resp := p.ReadDataSource(providers.ReadDataSourceRequest{ + TypeName: "data", + Config: cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("foo"), + }), + }) + + checkDiags(t, resp.Diagnostics) + + expected := cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("bar"), + }) + + if !cmp.Equal(expected, resp.State, typeComparer, valueComparer, equateEmpty) { + t.Fatal(cmp.Diff(expected, resp.State, typeComparer, valueComparer, equateEmpty)) + } +} diff --git a/vendor/github.com/hashicorp/terraform/plugin6/mock_proto/generate.go b/vendor/github.com/hashicorp/terraform/plugin6/mock_proto/generate.go new file mode 100644 index 00000000..cde637e4 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plugin6/mock_proto/generate.go @@ -0,0 +1,3 @@ +//go:generate go run github.com/golang/mock/mockgen -destination mock.go github.com/hashicorp/terraform/internal/tfplugin6 ProviderClient + +package mock_tfplugin6 diff --git a/vendor/github.com/hashicorp/terraform/plugin6/mock_proto/mock.go b/vendor/github.com/hashicorp/terraform/plugin6/mock_proto/mock.go new file mode 100644 index 00000000..506fd7bc --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plugin6/mock_proto/mock.go @@ -0,0 +1,276 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/hashicorp/terraform/internal/tfplugin6 (interfaces: ProviderClient) + +// Package mock_tfplugin6 is a generated GoMock package. +package mock_tfplugin6 + +import ( + context "context" + gomock "github.com/golang/mock/gomock" + tfplugin6 "github.com/hashicorp/terraform/internal/tfplugin6" + grpc "google.golang.org/grpc" + reflect "reflect" +) + +// MockProviderClient is a mock of ProviderClient interface +type MockProviderClient struct { + ctrl *gomock.Controller + recorder *MockProviderClientMockRecorder +} + +// MockProviderClientMockRecorder is the mock recorder for MockProviderClient +type MockProviderClientMockRecorder struct { + mock *MockProviderClient +} + +// NewMockProviderClient creates a new mock instance +func NewMockProviderClient(ctrl *gomock.Controller) *MockProviderClient { + mock := &MockProviderClient{ctrl: ctrl} + mock.recorder = &MockProviderClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockProviderClient) EXPECT() *MockProviderClientMockRecorder { + return m.recorder +} + +// ApplyResourceChange mocks base method +func (m *MockProviderClient) ApplyResourceChange(arg0 context.Context, arg1 *tfplugin6.ApplyResourceChange_Request, arg2 ...grpc.CallOption) (*tfplugin6.ApplyResourceChange_Response, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ApplyResourceChange", varargs...) + ret0, _ := ret[0].(*tfplugin6.ApplyResourceChange_Response) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ApplyResourceChange indicates an expected call of ApplyResourceChange +func (mr *MockProviderClientMockRecorder) ApplyResourceChange(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyResourceChange", reflect.TypeOf((*MockProviderClient)(nil).ApplyResourceChange), varargs...) +} + +// ConfigureProvider mocks base method +func (m *MockProviderClient) ConfigureProvider(arg0 context.Context, arg1 *tfplugin6.ConfigureProvider_Request, arg2 ...grpc.CallOption) (*tfplugin6.ConfigureProvider_Response, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ConfigureProvider", varargs...) + ret0, _ := ret[0].(*tfplugin6.ConfigureProvider_Response) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ConfigureProvider indicates an expected call of ConfigureProvider +func (mr *MockProviderClientMockRecorder) ConfigureProvider(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfigureProvider", reflect.TypeOf((*MockProviderClient)(nil).ConfigureProvider), varargs...) +} + +// GetProviderSchema mocks base method +func (m *MockProviderClient) GetProviderSchema(arg0 context.Context, arg1 *tfplugin6.GetProviderSchema_Request, arg2 ...grpc.CallOption) (*tfplugin6.GetProviderSchema_Response, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetProviderSchema", varargs...) + ret0, _ := ret[0].(*tfplugin6.GetProviderSchema_Response) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetProviderSchema indicates an expected call of GetProviderSchema +func (mr *MockProviderClientMockRecorder) GetProviderSchema(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProviderSchema", reflect.TypeOf((*MockProviderClient)(nil).GetProviderSchema), varargs...) +} + +// ImportResourceState mocks base method +func (m *MockProviderClient) ImportResourceState(arg0 context.Context, arg1 *tfplugin6.ImportResourceState_Request, arg2 ...grpc.CallOption) (*tfplugin6.ImportResourceState_Response, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ImportResourceState", varargs...) + ret0, _ := ret[0].(*tfplugin6.ImportResourceState_Response) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ImportResourceState indicates an expected call of ImportResourceState +func (mr *MockProviderClientMockRecorder) ImportResourceState(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ImportResourceState", reflect.TypeOf((*MockProviderClient)(nil).ImportResourceState), varargs...) +} + +// PlanResourceChange mocks base method +func (m *MockProviderClient) PlanResourceChange(arg0 context.Context, arg1 *tfplugin6.PlanResourceChange_Request, arg2 ...grpc.CallOption) (*tfplugin6.PlanResourceChange_Response, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "PlanResourceChange", varargs...) + ret0, _ := ret[0].(*tfplugin6.PlanResourceChange_Response) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// PlanResourceChange indicates an expected call of PlanResourceChange +func (mr *MockProviderClientMockRecorder) PlanResourceChange(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PlanResourceChange", reflect.TypeOf((*MockProviderClient)(nil).PlanResourceChange), varargs...) +} + +// ReadDataSource mocks base method +func (m *MockProviderClient) ReadDataSource(arg0 context.Context, arg1 *tfplugin6.ReadDataSource_Request, arg2 ...grpc.CallOption) (*tfplugin6.ReadDataSource_Response, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ReadDataSource", varargs...) + ret0, _ := ret[0].(*tfplugin6.ReadDataSource_Response) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadDataSource indicates an expected call of ReadDataSource +func (mr *MockProviderClientMockRecorder) ReadDataSource(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadDataSource", reflect.TypeOf((*MockProviderClient)(nil).ReadDataSource), varargs...) +} + +// ReadResource mocks base method +func (m *MockProviderClient) ReadResource(arg0 context.Context, arg1 *tfplugin6.ReadResource_Request, arg2 ...grpc.CallOption) (*tfplugin6.ReadResource_Response, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ReadResource", varargs...) + ret0, _ := ret[0].(*tfplugin6.ReadResource_Response) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReadResource indicates an expected call of ReadResource +func (mr *MockProviderClientMockRecorder) ReadResource(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadResource", reflect.TypeOf((*MockProviderClient)(nil).ReadResource), varargs...) +} + +// StopProvider mocks base method +func (m *MockProviderClient) StopProvider(arg0 context.Context, arg1 *tfplugin6.StopProvider_Request, arg2 ...grpc.CallOption) (*tfplugin6.StopProvider_Response, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "StopProvider", varargs...) + ret0, _ := ret[0].(*tfplugin6.StopProvider_Response) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StopProvider indicates an expected call of StopProvider +func (mr *MockProviderClientMockRecorder) StopProvider(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StopProvider", reflect.TypeOf((*MockProviderClient)(nil).StopProvider), varargs...) +} + +// UpgradeResourceState mocks base method +func (m *MockProviderClient) UpgradeResourceState(arg0 context.Context, arg1 *tfplugin6.UpgradeResourceState_Request, arg2 ...grpc.CallOption) (*tfplugin6.UpgradeResourceState_Response, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "UpgradeResourceState", varargs...) + ret0, _ := ret[0].(*tfplugin6.UpgradeResourceState_Response) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpgradeResourceState indicates an expected call of UpgradeResourceState +func (mr *MockProviderClientMockRecorder) UpgradeResourceState(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpgradeResourceState", reflect.TypeOf((*MockProviderClient)(nil).UpgradeResourceState), varargs...) +} + +// ValidateDataResourceConfig mocks base method +func (m *MockProviderClient) ValidateDataResourceConfig(arg0 context.Context, arg1 *tfplugin6.ValidateDataResourceConfig_Request, arg2 ...grpc.CallOption) (*tfplugin6.ValidateDataResourceConfig_Response, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ValidateDataResourceConfig", varargs...) + ret0, _ := ret[0].(*tfplugin6.ValidateDataResourceConfig_Response) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ValidateDataResourceConfig indicates an expected call of ValidateDataResourceConfig +func (mr *MockProviderClientMockRecorder) ValidateDataResourceConfig(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateDataResourceConfig", reflect.TypeOf((*MockProviderClient)(nil).ValidateDataResourceConfig), varargs...) +} + +// ValidateProviderConfig mocks base method +func (m *MockProviderClient) ValidateProviderConfig(arg0 context.Context, arg1 *tfplugin6.ValidateProviderConfig_Request, arg2 ...grpc.CallOption) (*tfplugin6.ValidateProviderConfig_Response, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ValidateProviderConfig", varargs...) + ret0, _ := ret[0].(*tfplugin6.ValidateProviderConfig_Response) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ValidateProviderConfig indicates an expected call of ValidateProviderConfig +func (mr *MockProviderClientMockRecorder) ValidateProviderConfig(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateProviderConfig", reflect.TypeOf((*MockProviderClient)(nil).ValidateProviderConfig), varargs...) +} + +// ValidateResourceConfig mocks base method +func (m *MockProviderClient) ValidateResourceConfig(arg0 context.Context, arg1 *tfplugin6.ValidateResourceConfig_Request, arg2 ...grpc.CallOption) (*tfplugin6.ValidateResourceConfig_Response, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ValidateResourceConfig", varargs...) + ret0, _ := ret[0].(*tfplugin6.ValidateResourceConfig_Response) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ValidateResourceConfig indicates an expected call of ValidateResourceConfig +func (mr *MockProviderClientMockRecorder) ValidateResourceConfig(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateResourceConfig", reflect.TypeOf((*MockProviderClient)(nil).ValidateResourceConfig), varargs...) +} diff --git a/vendor/github.com/hashicorp/terraform/plugin6/serve.go b/vendor/github.com/hashicorp/terraform/plugin6/serve.go new file mode 100644 index 00000000..8c5203fc --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plugin6/serve.go @@ -0,0 +1,63 @@ +package plugin6 + +import ( + "github.com/hashicorp/go-plugin" + proto "github.com/hashicorp/terraform/internal/tfplugin6" +) + +const ( + // The constants below are the names of the plugins that can be dispensed + // from the plugin server. + ProviderPluginName = "provider" + + // DefaultProtocolVersion is the protocol version assumed for legacy clients + // that don't specify a particular version during their handshake. Since we + // explicitly set VersionedPlugins in Serve, this number does not need to + // change with the protocol version and can effectively stay 4 forever + // (unless we need the "biggest hammer" approach to break all provider + // compatibility). + DefaultProtocolVersion = 4 +) + +// Handshake is the HandshakeConfig used to configure clients and servers. +var Handshake = plugin.HandshakeConfig{ + // The ProtocolVersion is the version that must match between TF core + // and TF plugins. + ProtocolVersion: DefaultProtocolVersion, + + // The magic cookie values should NEVER be changed. + MagicCookieKey: "TF_PLUGIN_MAGIC_COOKIE", + MagicCookieValue: "d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2", +} + +type GRPCProviderFunc func() proto.ProviderServer + +// ServeOpts are the configurations to serve a plugin. +type ServeOpts struct { + GRPCProviderFunc GRPCProviderFunc +} + +// Serve serves a plugin. This function never returns and should be the final +// function called in the main function of the plugin. +func Serve(opts *ServeOpts) { + plugin.Serve(&plugin.ServeConfig{ + HandshakeConfig: Handshake, + VersionedPlugins: pluginSet(opts), + GRPCServer: plugin.DefaultGRPCServer, + }) +} + +func pluginSet(opts *ServeOpts) map[int]plugin.PluginSet { + plugins := map[int]plugin.PluginSet{} + + // add the new protocol versions if they're configured + if opts.GRPCProviderFunc != nil { + plugins[6] = plugin.PluginSet{} + if opts.GRPCProviderFunc != nil { + plugins[6]["provider"] = &GRPCProviderPlugin{ + GRPCProvider: opts.GRPCProviderFunc, + } + } + } + return plugins +} diff --git a/vendor/github.com/hashicorp/terraform/provider_source.go b/vendor/github.com/hashicorp/terraform/provider_source.go index dc9af04d..adfcc777 100644 --- a/vendor/github.com/hashicorp/terraform/provider_source.go +++ b/vendor/github.com/hashicorp/terraform/provider_source.go @@ -153,7 +153,7 @@ func implicitProviderSource(services *disco.Disco) getproviders.Source { // conventions for each platform: // // XDG (Unix): lowercase of the first string, "terraform" - // Windows: two-level heirarchy of first two strings, "HashiCorp\Terraform" + // Windows: two-level hierarchy of first two strings, "HashiCorp\Terraform" // OS X: reverse-DNS unique identifier, "io.terraform". sysSpecificDirs := userdirs.ForApp("Terraform", "HashiCorp", "io.terraform") for _, dir := range sysSpecificDirs.DataSearchPaths("plugins") { diff --git a/vendor/github.com/hashicorp/terraform/providers/factory.go b/vendor/github.com/hashicorp/terraform/providers/factory.go index 1586ca34..52700633 100644 --- a/vendor/github.com/hashicorp/terraform/providers/factory.go +++ b/vendor/github.com/hashicorp/terraform/providers/factory.go @@ -31,7 +31,7 @@ func FactoryFixed(p Interface) Factory { // a subsequent call to do anything with the resource type would fail // anyway. func ProviderHasResource(provider Interface, typeName string) bool { - resp := provider.GetSchema() + resp := provider.GetProviderSchema() if resp.Diagnostics.HasErrors() { return false } @@ -53,7 +53,7 @@ func ProviderHasResource(provider Interface, typeName string) bool { // a subsequent call to do anything with the data source would fail // anyway. func ProviderHasDataSource(provider Interface, dataSourceName string) bool { - resp := provider.GetSchema() + resp := provider.GetProviderSchema() if resp.Diagnostics.HasErrors() { return false } diff --git a/vendor/github.com/hashicorp/terraform/providers/provider.go b/vendor/github.com/hashicorp/terraform/providers/provider.go index 2d9db44b..341a255a 100644 --- a/vendor/github.com/hashicorp/terraform/providers/provider.go +++ b/vendor/github.com/hashicorp/terraform/providers/provider.go @@ -12,19 +12,21 @@ import ( // provider plugin. type Interface interface { // GetSchema returns the complete schema for the provider. - GetSchema() GetSchemaResponse + GetProviderSchema() GetProviderSchemaResponse - // PrepareProviderConfig allows the provider to validate the configuration - // values, and set or override any values with defaults. - PrepareProviderConfig(PrepareProviderConfigRequest) PrepareProviderConfigResponse + // ValidateProviderConfig allows the provider to validate the configuration. + // The ValidateProviderConfigResponse.PreparedConfig field is unused. The + // final configuration is not stored in the state, and any modifications + // that need to be made must be made during the Configure method call. + ValidateProviderConfig(ValidateProviderConfigRequest) ValidateProviderConfigResponse - // ValidateResourceTypeConfig allows the provider to validate the resource + // ValidateResourceConfig allows the provider to validate the resource // configuration values. - ValidateResourceTypeConfig(ValidateResourceTypeConfigRequest) ValidateResourceTypeConfigResponse + ValidateResourceConfig(ValidateResourceConfigRequest) ValidateResourceConfigResponse - // ValidateDataSource allows the provider to validate the data source + // ValidateDataResourceConfig allows the provider to validate the data source // configuration values. - ValidateDataSourceConfig(ValidateDataSourceConfigRequest) ValidateDataSourceConfigResponse + ValidateDataResourceConfig(ValidateDataResourceConfigRequest) ValidateDataResourceConfigResponse // UpgradeResourceState is called when the state loader encounters an // instance state whose schema version is less than the one reported by the @@ -33,7 +35,7 @@ type Interface interface { UpgradeResourceState(UpgradeResourceStateRequest) UpgradeResourceStateResponse // Configure configures and initialized the provider. - Configure(ConfigureRequest) ConfigureResponse + ConfigureProvider(ConfigureProviderRequest) ConfigureProviderResponse // Stop is called when the provider should halt any in-flight actions. // @@ -69,7 +71,7 @@ type Interface interface { Close() error } -type GetSchemaResponse struct { +type GetProviderSchemaResponse struct { // Provider is the schema for the provider itself. Provider Schema @@ -93,19 +95,19 @@ type Schema struct { Block *configschema.Block } -type PrepareProviderConfigRequest struct { +type ValidateProviderConfigRequest struct { // Config is the raw configuration value for the provider. Config cty.Value } -type PrepareProviderConfigResponse struct { - // PreparedConfig is the configuration as prepared by the provider. +type ValidateProviderConfigResponse struct { + // PreparedConfig is unused and will be removed with support for plugin protocol v5. PreparedConfig cty.Value // Diagnostics contains any warnings or errors from the method call. Diagnostics tfdiags.Diagnostics } -type ValidateResourceTypeConfigRequest struct { +type ValidateResourceConfigRequest struct { // TypeName is the name of the resource type to validate. TypeName string @@ -114,12 +116,12 @@ type ValidateResourceTypeConfigRequest struct { Config cty.Value } -type ValidateResourceTypeConfigResponse struct { +type ValidateResourceConfigResponse struct { // Diagnostics contains any warnings or errors from the method call. Diagnostics tfdiags.Diagnostics } -type ValidateDataSourceConfigRequest struct { +type ValidateDataResourceConfigRequest struct { // TypeName is the name of the data source type to validate. TypeName string @@ -128,7 +130,7 @@ type ValidateDataSourceConfigRequest struct { Config cty.Value } -type ValidateDataSourceConfigResponse struct { +type ValidateDataResourceConfigResponse struct { // Diagnostics contains any warnings or errors from the method call. Diagnostics tfdiags.Diagnostics } @@ -158,7 +160,7 @@ type UpgradeResourceStateResponse struct { Diagnostics tfdiags.Diagnostics } -type ConfigureRequest struct { +type ConfigureProviderRequest struct { // Terraform version is the version string from the running instance of // terraform. Providers can use TerraformVersion to verify compatibility, // and to store for informational purposes. @@ -168,7 +170,7 @@ type ConfigureRequest struct { Config cty.Value } -type ConfigureResponse struct { +type ConfigureProviderResponse struct { // Diagnostics contains any warnings or errors from the method call. Diagnostics tfdiags.Diagnostics } diff --git a/vendor/github.com/hashicorp/terraform/registry/client.go b/vendor/github.com/hashicorp/terraform/registry/client.go index 6770d718..03e6f7c9 100644 --- a/vendor/github.com/hashicorp/terraform/registry/client.go +++ b/vendor/github.com/hashicorp/terraform/registry/client.go @@ -65,10 +65,6 @@ type Client struct { // services is a required *disco.Disco, which may have services and // credentials pre-loaded. services *disco.Disco - - // retry is the number of retries the client will attempt for each request - // if it runs into a transient failure with the remote registry. - retry int } // NewClient returns a new initialized registry client. diff --git a/vendor/github.com/hashicorp/terraform/registry/response/pagination_test.go b/vendor/github.com/hashicorp/terraform/registry/response/pagination_test.go index be862abb..09c78e6c 100644 --- a/vendor/github.com/hashicorp/terraform/registry/response/pagination_test.go +++ b/vendor/github.com/hashicorp/terraform/registry/response/pagination_test.go @@ -5,10 +5,6 @@ import ( "testing" ) -func intPtr(i int) *int { - return &i -} - func prettyJSON(o interface{}) (string, error) { bytes, err := json.MarshalIndent(o, "", "\t") if err != nil { diff --git a/vendor/github.com/hashicorp/terraform/registry/test/mock_registry.go b/vendor/github.com/hashicorp/terraform/registry/test/mock_registry.go index c6a8ef70..00ead006 100644 --- a/vendor/github.com/hashicorp/terraform/registry/test/mock_registry.go +++ b/vendor/github.com/hashicorp/terraform/registry/test/mock_registry.go @@ -8,10 +8,8 @@ import ( "net/http/httptest" "os" "regexp" - "sort" "strings" - version "github.com/hashicorp/go-version" svchost "github.com/hashicorp/terraform-svchost" "github.com/hashicorp/terraform-svchost/auth" "github.com/hashicorp/terraform-svchost/disco" @@ -51,8 +49,6 @@ type testMod struct { // Only one version for now, as we only lookup latest from the registry. type testProvider struct { version string - os string - arch string url string } @@ -135,20 +131,6 @@ func init() { } } -func latestVersion(versions []string) string { - var col version.Collection - for _, v := range versions { - ver, err := version.NewVersion(v) - if err != nil { - panic(err) - } - col = append(col, ver) - } - - sort.Sort(col) - return col[len(col)-1].String() -} - func mockRegHandler() http.Handler { mux := http.NewServeMux() @@ -188,7 +170,6 @@ func mockRegHandler() http.Handler { w.Header().Set("X-Terraform-Get", location) w.WriteHeader(http.StatusNoContent) // no body - return } moduleVersions := func(w http.ResponseWriter, r *http.Request) { diff --git a/vendor/github.com/hashicorp/terraform/repl/format.go b/vendor/github.com/hashicorp/terraform/repl/format.go index 0aa19efd..c83e9ef2 100644 --- a/vendor/github.com/hashicorp/terraform/repl/format.go +++ b/vendor/github.com/hashicorp/terraform/repl/format.go @@ -16,7 +16,11 @@ func FormatValue(v cty.Value, indent int) string { if !v.IsKnown() { return "(known after apply)" } - if v.IsMarked() { + if v.Type().Equals(cty.String) && v.HasMark("raw") { + raw, _ := v.Unmark() + return raw.AsString() + } + if v.HasMark("sensitive") { return "(sensitive)" } if v.IsNull() { @@ -46,12 +50,13 @@ func FormatValue(v cty.Value, indent int) string { case ty.IsPrimitiveType(): switch ty { case cty.String: - // FIXME: If it's a multi-line string, better to render it using - // HEREDOC-style syntax. + if formatted, isMultiline := formatMultilineString(v, indent); isMultiline { + return formatted + } return strconv.Quote(v.AsString()) case cty.Number: bf := v.AsBigFloat() - return bf.Text('g', -1) + return bf.Text('f', -1) case cty.Bool: if v.True() { return "true" @@ -75,6 +80,56 @@ func FormatValue(v cty.Value, indent int) string { return fmt.Sprintf("%#v", v) } +func formatMultilineString(v cty.Value, indent int) (string, bool) { + str := v.AsString() + lines := strings.Split(str, "\n") + if len(lines) < 2 { + return "", false + } + + // If the value is indented, we use the indented form of heredoc for readability. + operator := "<<" + if indent > 0 { + operator = "<<-" + } + + // Default delimiter is "End Of Text" by convention + delimiter := "EOT" + +OUTER: + for { + // Check if any of the lines are in conflict with the delimiter. The + // parser allows leading and trailing whitespace, so we must remove it + // before comparison. + for _, line := range lines { + // If the delimiter matches a line, extend it and start again + if strings.TrimSpace(line) == delimiter { + delimiter = delimiter + "_" + continue OUTER + } + } + + // None of the lines match the delimiter, so we're ready + break + } + + // Write the heredoc, with indentation as appropriate. + var buf strings.Builder + + buf.WriteString(operator) + buf.WriteString(delimiter) + for _, line := range lines { + buf.WriteByte('\n') + buf.WriteString(strings.Repeat(" ", indent)) + buf.WriteString(line) + } + buf.WriteByte('\n') + buf.WriteString(strings.Repeat(" ", indent)) + buf.WriteString(delimiter) + + return buf.String(), true +} + func formatMappingValue(v cty.Value, indent int) string { var buf strings.Builder count := 0 diff --git a/vendor/github.com/hashicorp/terraform/repl/format_test.go b/vendor/github.com/hashicorp/terraform/repl/format_test.go index 108d32dc..6d4e484b 100644 --- a/vendor/github.com/hashicorp/terraform/repl/format_test.go +++ b/vendor/github.com/hashicorp/terraform/repl/format_test.go @@ -58,7 +58,28 @@ func TestFormatValue(t *testing.T) { }, { cty.StringVal("hello\nworld"), - `"hello\nworld"`, // Ideally we'd use heredoc syntax here for better readability, but we don't yet + `< /dev/null; then diff --git a/vendor/github.com/hashicorp/terraform/scripts/generate-plugins.go b/vendor/github.com/hashicorp/terraform/scripts/generate-plugins.go deleted file mode 100644 index 24030bcf..00000000 --- a/vendor/github.com/hashicorp/terraform/scripts/generate-plugins.go +++ /dev/null @@ -1,285 +0,0 @@ -// Generate Plugins is a small program that updates the lists of plugins in -// command/internal_plugin_list.go so they will be compiled into the main -// terraform binary. -package main - -import ( - "fmt" - "go/ast" - "go/parser" - "go/token" - "io/ioutil" - "log" - "os" - "path/filepath" - "sort" - "strings" -) - -const target = "command/internal_plugin_list.go" - -func main() { - if isProjectRoot() == false { - log.Fatalf("This program must be invoked in the terraform project root") - } - - //// Collect all of the data we need about plugins we have in the project - //providers, err := discoverProviders() - //if err != nil { - // log.Fatalf("Failed to discover providers: %s", err) - //} - - provisioners, err := discoverProvisioners() - if err != nil { - log.Fatalf("Failed to discover provisioners: %s", err) - } - - // Do some simple code generation and templating - output := source - output = strings.Replace(output, "IMPORTS", makeImports(nil, provisioners), 1) - //output = strings.Replace(output, "PROVIDERS", makeProviderMap(providers), 1) - output = strings.Replace(output, "PROVISIONERS", makeProvisionerMap(provisioners), 1) - - // TODO sort the lists of plugins so we are not subjected to random OS ordering of the plugin lists - - // Write our generated code to the command/plugin.go file - file, err := os.Create(target) - defer file.Close() - if err != nil { - log.Fatalf("Failed to open %s for writing: %s", target, err) - } - - _, err = file.WriteString(output) - if err != nil { - log.Fatalf("Failed writing to %s: %s", target, err) - } - - log.Printf("Generated %s", target) -} - -type plugin struct { - Package string // Package name from ast remoteexec - PluginName string // Path via deriveName() remote-exec - TypeName string // Type of plugin provisioner - Path string // Relative import path builtin/provisioners/remote-exec - ImportName string // See deriveImport() remoteexecprovisioner -} - -// makeProviderMap creates a map of providers like this: -// -// var InternalProviders = map[string]plugin.ProviderFunc{ -// "aws": aws.Provider, -// "azurerm": azurerm.Provider, -// "cloudflare": cloudflare.Provider, -func makeProviderMap(items []plugin) string { - output := "" - for _, item := range items { - output += fmt.Sprintf("\t\"%s\": %s.%s,\n", item.PluginName, item.ImportName, item.TypeName) - } - return output -} - -func isProjectRoot() bool { - _, err := os.Stat("go.mod") - if os.IsNotExist(err) { - return false - } - - return true -} - -// makeProvisionerMap creates a map of provisioners like this: -// -// "chef": chefprovisioner.Provisioner, -// "salt-masterless": saltmasterlessprovisioner.Provisioner, -// "file": fileprovisioner.Provisioner, -// "local-exec": localexecprovisioner.Provisioner, -// "remote-exec": remoteexecprovisioner.Provisioner, -// -func makeProvisionerMap(items []plugin) string { - output := "" - for _, item := range items { - output += fmt.Sprintf("\t\"%s\": %s.%s,\n", item.PluginName, item.ImportName, item.TypeName) - } - return output -} - -func makeImports(providers, provisioners []plugin) string { - plugins := []string{} - - for _, provider := range providers { - plugins = append(plugins, fmt.Sprintf("\t%s \"github.com/hashicorp/terraform/%s\"\n", provider.ImportName, filepath.ToSlash(provider.Path))) - } - - for _, provisioner := range provisioners { - plugins = append(plugins, fmt.Sprintf("\t%s \"github.com/hashicorp/terraform/%s\"\n", provisioner.ImportName, filepath.ToSlash(provisioner.Path))) - } - - // Make things pretty - sort.Strings(plugins) - - return strings.Join(plugins, "") -} - -// listDirectories recursively lists directories under the specified path -func listDirectories(path string) ([]string, error) { - names := []string{} - items, err := ioutil.ReadDir(path) - if err != nil { - return names, err - } - - for _, item := range items { - // We only want directories - if item.IsDir() { - if item.Name() == "testdata" { - continue - } - currentDir := filepath.Join(path, item.Name()) - names = append(names, currentDir) - - // Do some recursion - subNames, err := listDirectories(currentDir) - if err == nil { - names = append(names, subNames...) - } - } - } - - return names, nil -} - -// deriveName determines the name of the plugin relative to the specified root -// path. -func deriveName(root, full string) string { - short, _ := filepath.Rel(root, full) - bits := strings.Split(short, string(os.PathSeparator)) - return strings.Join(bits, "-") -} - -// deriveImport will build a unique import identifier based on packageName and -// the result of deriveName(). This is important for disambigutating between -// providers and provisioners that have the same name. This will be something -// like: -// -// remote-exec -> remoteexecprovisioner -// -// which is long, but is deterministic and unique. -func deriveImport(typeName, derivedName string) string { - return strings.Replace(derivedName, "-", "", -1) + strings.ToLower(typeName) -} - -// discoverTypesInPath searches for types of typeID in path using go's ast and -// returns a list of plugins it finds. -func discoverTypesInPath(path, typeID, typeName string) ([]plugin, error) { - pluginTypes := []plugin{} - - dirs, err := listDirectories(path) - if err != nil { - return pluginTypes, err - } - - for _, dir := range dirs { - fset := token.NewFileSet() - goPackages, err := parser.ParseDir(fset, dir, nil, parser.AllErrors) - if err != nil { - return pluginTypes, fmt.Errorf("Failed parsing directory %s: %s", dir, err) - } - - for _, goPackage := range goPackages { - ast.PackageExports(goPackage) - ast.Inspect(goPackage, func(n ast.Node) bool { - switch x := n.(type) { - case *ast.FuncDecl: - // If we get a function then we will check the function name - // against typeName and the function return type (Results) - // against typeID. - // - // There may be more than one return type but in the target - // case there should only be one. Also the return type is a - // ast.SelectorExpr which means we have multiple nodes. - // We'll read all of them as ast.Ident (identifier), join - // them via . to get a string like terraform.ResourceProvider - // and see if it matches our expected typeID - // - // This is somewhat verbose but prevents us from identifying - // the wrong types if the function name is amiguous or if - // there are other subfolders added later. - if x.Name.Name == typeName && len(x.Type.Results.List) == 1 { - node := x.Type.Results.List[0].Type - typeIdentifiers := []string{} - ast.Inspect(node, func(m ast.Node) bool { - switch y := m.(type) { - case *ast.Ident: - typeIdentifiers = append(typeIdentifiers, y.Name) - } - // We need all of the identifiers to join so we - // can't break early here. - return true - }) - if strings.Join(typeIdentifiers, ".") == typeID { - derivedName := deriveName(path, dir) - pluginTypes = append(pluginTypes, plugin{ - Package: goPackage.Name, - PluginName: derivedName, - ImportName: deriveImport(x.Name.Name, derivedName), - TypeName: x.Name.Name, - Path: dir, - }) - } - } - case *ast.TypeSpec: - // In the simpler case we will simply check whether the type - // declaration has the name we were looking for. - if x.Name.Name == typeID { - derivedName := deriveName(path, dir) - pluginTypes = append(pluginTypes, plugin{ - Package: goPackage.Name, - PluginName: derivedName, - ImportName: deriveImport(x.Name.Name, derivedName), - TypeName: x.Name.Name, - Path: dir, - }) - // The AST stops parsing when we return false. Once we - // find the symbol we want we can stop parsing. - return false - } - } - return true - }) - } - } - - return pluginTypes, nil -} - -func discoverProviders() ([]plugin, error) { - path := "./builtin/providers" - typeID := "terraform.ResourceProvider" - typeName := "Provider" - return discoverTypesInPath(path, typeID, typeName) -} - -func discoverProvisioners() ([]plugin, error) { - path := "./builtin/provisioners" - typeID := "terraform.ResourceProvisioner" - typeName := "Provisioner" - return discoverTypesInPath(path, typeID, typeName) -} - -const source = `// -// This file is automatically generated by scripts/generate-plugins.go -- Do not edit! -// -package command - -import ( -IMPORTS - "github.com/hashicorp/terraform/plugin" -) - -var InternalProviders = map[string]plugin.ProviderFunc{} - -var InternalProvisioners = map[string]plugin.ProvisionerFunc{ -PROVISIONERS -} -` diff --git a/vendor/github.com/hashicorp/terraform/scripts/generate-plugins_test.go b/vendor/github.com/hashicorp/terraform/scripts/generate-plugins_test.go deleted file mode 100644 index cba015b8..00000000 --- a/vendor/github.com/hashicorp/terraform/scripts/generate-plugins_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package main - -import "testing" - -func TestMakeProvisionerMap(t *testing.T) { - p := makeProvisionerMap([]plugin{ - { - Package: "file", - PluginName: "file", - TypeName: "Provisioner", - Path: "builtin/provisioners/file", - ImportName: "fileprovisioner", - }, - { - Package: "localexec", - PluginName: "local-exec", - TypeName: "Provisioner", - Path: "builtin/provisioners/local-exec", - ImportName: "localexecprovisioner", - }, - { - Package: "remoteexec", - PluginName: "remote-exec", - TypeName: "Provisioner", - Path: "builtin/provisioners/remote-exec", - ImportName: "remoteexecprovisioner", - }, - }) - - expected := ` "file": fileprovisioner.Provisioner, - "local-exec": localexecprovisioner.Provisioner, - "remote-exec": remoteexecprovisioner.Provisioner, -` - - if p != expected { - t.Errorf("Provisioner output does not match expected format.\n -- Expected -- \n%s\n -- Found --\n%s\n", expected, p) - } -} - -func TestDeriveName(t *testing.T) { - actual := deriveName("builtin/provisioners", "builtin/provisioners/magic/remote-exec") - expected := "magic-remote-exec" - if actual != expected { - t.Errorf("Expected %s; found %s", expected, actual) - } -} - -func TestDeriveImport(t *testing.T) { - actual := deriveImport("provider", "magic-aws") - expected := "magicawsprovider" - if actual != expected { - t.Errorf("Expected %s; found %s", expected, actual) - } -} - -func contains(plugins []plugin, name string) bool { - for _, plugin := range plugins { - if plugin.PluginName == name { - return true - } - } - return false -} - -//func TestDiscoverTypesProviders(t *testing.T) { -// plugins, err := discoverTypesInPath("../builtin/providers", "terraform.ResourceProvider", "Provider") -// if err != nil { -// t.Fatalf(err.Error()) -// } -// // We're just going to spot-check, not do this exhaustively -// if !contains(plugins, "aws") { -// t.Errorf("Expected to find aws provider") -// } -// if !contains(plugins, "docker") { -// t.Errorf("Expected to find docker provider") -// } -// if !contains(plugins, "dnsimple") { -// t.Errorf("Expected to find dnsimple provider") -// } -// if !contains(plugins, "triton") { -// t.Errorf("Expected to find triton provider") -// } -// if contains(plugins, "file") { -// t.Errorf("Found unexpected provider file") -// } -//} - -func TestDiscoverTypesProvisioners(t *testing.T) { - plugins, err := discoverTypesInPath("../builtin/provisioners", "terraform.ResourceProvisioner", "Provisioner") - if err != nil { - t.Fatalf(err.Error()) - } - if !contains(plugins, "remote-exec") { - t.Errorf("Expected to find remote-exec provisioner") - } - if contains(plugins, "aws") { - t.Errorf("Found unexpected provisioner aws") - } -} diff --git a/vendor/github.com/hashicorp/terraform/scripts/gofmtcheck.sh b/vendor/github.com/hashicorp/terraform/scripts/gofmtcheck.sh index 7e6fbddf..9a341da9 100755 --- a/vendor/github.com/hashicorp/terraform/scripts/gofmtcheck.sh +++ b/vendor/github.com/hashicorp/terraform/scripts/gofmtcheck.sh @@ -6,7 +6,7 @@ gofmt_files=$(gofmt -l `find . -name '*.go' | grep -v vendor`) if [[ -n ${gofmt_files} ]]; then echo 'gofmt needs running on the following files:' echo "${gofmt_files}" - echo "You can use the command: \`make fmtcheck\` to reformat code." + echo "You can use the command: \`gofmt -w .\` to reformat code." exit 1 fi diff --git a/vendor/github.com/hashicorp/terraform/states/instance_generation.go b/vendor/github.com/hashicorp/terraform/states/instance_generation.go index 617ad4ea..891adc00 100644 --- a/vendor/github.com/hashicorp/terraform/states/instance_generation.go +++ b/vendor/github.com/hashicorp/terraform/states/instance_generation.go @@ -18,7 +18,3 @@ type Generation interface { // CurrentGen is the Generation representing the currently-active object for // a resource instance. var CurrentGen Generation - -type currentGen struct{} - -func (g currentGen) generation() {} diff --git a/vendor/github.com/hashicorp/terraform/states/remote/remote.go b/vendor/github.com/hashicorp/terraform/states/remote/remote.go index d3d3f7b2..0dab1863 100644 --- a/vendor/github.com/hashicorp/terraform/states/remote/remote.go +++ b/vendor/github.com/hashicorp/terraform/states/remote/remote.go @@ -1,8 +1,6 @@ package remote import ( - "fmt" - "github.com/hashicorp/terraform/states/statemgr" ) @@ -38,18 +36,3 @@ type Payload struct { // Factory is the factory function to create a remote client. type Factory func(map[string]string) (Client, error) - -// NewClient returns a new Client with the given type and configuration. -// The client is looked up in the BuiltinClients variable. -func NewClient(t string, conf map[string]string) (Client, error) { - f, ok := BuiltinClients[t] - if !ok { - return nil, fmt.Errorf("unknown remote client type: %s", t) - } - - return f(conf) -} - -// BuiltinClients is the list of built-in clients that can be used with -// NewClient. -var BuiltinClients = map[string]Factory{} diff --git a/vendor/github.com/hashicorp/terraform/states/remote/remote_test.go b/vendor/github.com/hashicorp/terraform/states/remote/remote_test.go index 1e8edc8b..55e23342 100644 --- a/vendor/github.com/hashicorp/terraform/states/remote/remote_test.go +++ b/vendor/github.com/hashicorp/terraform/states/remote/remote_test.go @@ -1,50 +1,11 @@ package remote import ( - "bytes" "crypto/md5" "encoding/json" "testing" - - "github.com/hashicorp/terraform/states/statefile" - "github.com/hashicorp/terraform/states/statemgr" ) -// testClient is a generic function to test any client. -func testClient(t *testing.T, c Client) { - var buf bytes.Buffer - s := statemgr.TestFullInitialState() - sf := &statefile.File{State: s} - if err := statefile.Write(sf, &buf); err != nil { - t.Fatalf("err: %s", err) - } - data := buf.Bytes() - - if err := c.Put(data); err != nil { - t.Fatalf("put: %s", err) - } - - p, err := c.Get() - if err != nil { - t.Fatalf("get: %s", err) - } - if !bytes.Equal(p.Data, data) { - t.Fatalf("bad: %#v", p) - } - - if err := c.Delete(); err != nil { - t.Fatalf("delete: %s", err) - } - - p, err = c.Get() - if err != nil { - t.Fatalf("get: %s", err) - } - if p != nil { - t.Fatalf("bad: %#v", p) - } -} - func TestRemoteClient_noPayload(t *testing.T) { s := &State{ Client: nilClient{}, diff --git a/vendor/github.com/hashicorp/terraform/states/resource.go b/vendor/github.com/hashicorp/terraform/states/resource.go index 0b6a4509..28223671 100644 --- a/vendor/github.com/hashicorp/terraform/states/resource.go +++ b/vendor/github.com/hashicorp/terraform/states/resource.go @@ -135,7 +135,7 @@ func (i *ResourceInstance) GetGeneration(gen Generation) *ResourceInstanceObject return i.Deposed[dk] } if gen == nil { - panic(fmt.Sprintf("get with nil Generation")) + panic("get with nil Generation") } // Should never fall out here, since the above covers all possible // Generation values. diff --git a/vendor/github.com/hashicorp/terraform/states/state_deepcopy.go b/vendor/github.com/hashicorp/terraform/states/state_deepcopy.go index 93e96d75..74f21de0 100644 --- a/vendor/github.com/hashicorp/terraform/states/state_deepcopy.go +++ b/vendor/github.com/hashicorp/terraform/states/state_deepcopy.go @@ -101,18 +101,18 @@ func (rs *Resource) DeepCopy() *Resource { // is the caller's responsibility to ensure mutual exclusion for the duration // of the operation, but may then freely modify the receiver and the returned // copy independently once this method returns. -func (is *ResourceInstance) DeepCopy() *ResourceInstance { - if is == nil { +func (i *ResourceInstance) DeepCopy() *ResourceInstance { + if i == nil { return nil } - deposed := make(map[DeposedKey]*ResourceInstanceObjectSrc, len(is.Deposed)) - for k, obj := range is.Deposed { + deposed := make(map[DeposedKey]*ResourceInstanceObjectSrc, len(i.Deposed)) + for k, obj := range i.Deposed { deposed[k] = obj.DeepCopy() } return &ResourceInstance{ - Current: is.Current.DeepCopy(), + Current: i.Current.DeepCopy(), Deposed: deposed, } } @@ -125,54 +125,54 @@ func (is *ResourceInstance) DeepCopy() *ResourceInstance { // It is the caller's responsibility to ensure mutual exclusion for the duration // of the operation, but may then freely modify the receiver and the returned // copy independently once this method returns. -func (obj *ResourceInstanceObjectSrc) DeepCopy() *ResourceInstanceObjectSrc { - if obj == nil { +func (os *ResourceInstanceObjectSrc) DeepCopy() *ResourceInstanceObjectSrc { + if os == nil { return nil } var attrsFlat map[string]string - if obj.AttrsFlat != nil { - attrsFlat = make(map[string]string, len(obj.AttrsFlat)) - for k, v := range obj.AttrsFlat { + if os.AttrsFlat != nil { + attrsFlat = make(map[string]string, len(os.AttrsFlat)) + for k, v := range os.AttrsFlat { attrsFlat[k] = v } } var attrsJSON []byte - if obj.AttrsJSON != nil { - attrsJSON = make([]byte, len(obj.AttrsJSON)) - copy(attrsJSON, obj.AttrsJSON) + if os.AttrsJSON != nil { + attrsJSON = make([]byte, len(os.AttrsJSON)) + copy(attrsJSON, os.AttrsJSON) } var attrPaths []cty.PathValueMarks - if obj.AttrSensitivePaths != nil { - attrPaths = make([]cty.PathValueMarks, len(obj.AttrSensitivePaths)) - copy(attrPaths, obj.AttrSensitivePaths) + if os.AttrSensitivePaths != nil { + attrPaths = make([]cty.PathValueMarks, len(os.AttrSensitivePaths)) + copy(attrPaths, os.AttrSensitivePaths) } var private []byte - if obj.Private != nil { - private = make([]byte, len(obj.Private)) - copy(private, obj.Private) + if os.Private != nil { + private = make([]byte, len(os.Private)) + copy(private, os.Private) } // Some addrs.Referencable implementations are technically mutable, but // we treat them as immutable by convention and so we don't deep-copy here. var dependencies []addrs.ConfigResource - if obj.Dependencies != nil { - dependencies = make([]addrs.ConfigResource, len(obj.Dependencies)) - copy(dependencies, obj.Dependencies) + if os.Dependencies != nil { + dependencies = make([]addrs.ConfigResource, len(os.Dependencies)) + copy(dependencies, os.Dependencies) } return &ResourceInstanceObjectSrc{ - Status: obj.Status, - SchemaVersion: obj.SchemaVersion, + Status: os.Status, + SchemaVersion: os.SchemaVersion, Private: private, AttrsFlat: attrsFlat, AttrsJSON: attrsJSON, AttrSensitivePaths: attrPaths, Dependencies: dependencies, - CreateBeforeDestroy: obj.CreateBeforeDestroy, + CreateBeforeDestroy: os.CreateBeforeDestroy, } } @@ -184,30 +184,31 @@ func (obj *ResourceInstanceObjectSrc) DeepCopy() *ResourceInstanceObjectSrc { // is the caller's responsibility to ensure mutual exclusion for the duration // of the operation, but may then freely modify the receiver and the returned // copy independently once this method returns. -func (obj *ResourceInstanceObject) DeepCopy() *ResourceInstanceObject { - if obj == nil { +func (o *ResourceInstanceObject) DeepCopy() *ResourceInstanceObject { + if o == nil { return nil } var private []byte - if obj.Private != nil { - private = make([]byte, len(obj.Private)) - copy(private, obj.Private) + if o.Private != nil { + private = make([]byte, len(o.Private)) + copy(private, o.Private) } // Some addrs.Referenceable implementations are technically mutable, but // we treat them as immutable by convention and so we don't deep-copy here. var dependencies []addrs.ConfigResource - if obj.Dependencies != nil { - dependencies = make([]addrs.ConfigResource, len(obj.Dependencies)) - copy(dependencies, obj.Dependencies) + if o.Dependencies != nil { + dependencies = make([]addrs.ConfigResource, len(o.Dependencies)) + copy(dependencies, o.Dependencies) } return &ResourceInstanceObject{ - Value: obj.Value, - Status: obj.Status, - Private: private, - Dependencies: dependencies, + Value: o.Value, + Status: o.Status, + Private: private, + Dependencies: dependencies, + CreateBeforeDestroy: o.CreateBeforeDestroy, } } diff --git a/vendor/github.com/hashicorp/terraform/states/state_string.go b/vendor/github.com/hashicorp/terraform/states/state_string.go index 680acf7a..0f74d596 100644 --- a/vendor/github.com/hashicorp/terraform/states/state_string.go +++ b/vendor/github.com/hashicorp/terraform/states/state_string.go @@ -76,18 +76,18 @@ func (s *State) String() string { // testString is used to produce part of the output of State.String. It should // never be used directly. -func (m *Module) testString() string { +func (ms *Module) testString() string { var buf bytes.Buffer - if len(m.Resources) == 0 { + if len(ms.Resources) == 0 { buf.WriteString("") } // We use AbsResourceInstance here, even though everything belongs to // the same module, just because we have a sorting behavior defined // for those but not for just ResourceInstance. - addrsOrder := make([]addrs.AbsResourceInstance, 0, len(m.Resources)) - for _, rs := range m.Resources { + addrsOrder := make([]addrs.AbsResourceInstance, 0, len(ms.Resources)) + for _, rs := range ms.Resources { for ik := range rs.Instances { addrsOrder = append(addrsOrder, rs.Addr.Instance(ik)) } @@ -99,8 +99,8 @@ func (m *Module) testString() string { for _, fakeAbsAddr := range addrsOrder { addr := fakeAbsAddr.Resource - rs := m.Resource(addr.ContainingResource()) - is := m.ResourceInstance(addr) + rs := ms.Resource(addr.ContainingResource()) + is := ms.ResourceInstance(addr) // Here we need to fake up a legacy-style address as the old state // types would've used, since that's what our tests against those @@ -197,24 +197,24 @@ func (m *Module) testString() string { } if obj := is.Current; obj != nil && len(obj.Dependencies) > 0 { - buf.WriteString(fmt.Sprintf("\n Dependencies:\n")) + buf.WriteString("\n Dependencies:\n") for _, dep := range obj.Dependencies { buf.WriteString(fmt.Sprintf(" %s\n", dep.String())) } } } - if len(m.OutputValues) > 0 { + if len(ms.OutputValues) > 0 { buf.WriteString("\nOutputs:\n\n") - ks := make([]string, 0, len(m.OutputValues)) - for k := range m.OutputValues { + ks := make([]string, 0, len(ms.OutputValues)) + for k := range ms.OutputValues { ks = append(ks, k) } sort.Strings(ks) for _, k := range ks { - v := m.OutputValues[k] + v := ms.OutputValues[k] lv := hcl2shim.ConfigValueFromHCL2(v.Value) switch vTyped := lv.(type) { case string: diff --git a/vendor/github.com/hashicorp/terraform/states/state_test.go b/vendor/github.com/hashicorp/terraform/states/state_test.go index 6b3eb815..b430ef78 100644 --- a/vendor/github.com/hashicorp/terraform/states/state_test.go +++ b/vendor/github.com/hashicorp/terraform/states/state_test.go @@ -1,6 +1,7 @@ package states import ( + "reflect" "testing" "github.com/go-test/deep" @@ -191,6 +192,32 @@ func TestState(t *testing.T) { } } +func TestStateDeepCopyObject(t *testing.T) { + obj := &ResourceInstanceObject{ + Value: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("id"), + }), + Private: []byte("private"), + Status: ObjectReady, + Dependencies: []addrs.ConfigResource{ + { + Module: addrs.RootModule, + Resource: addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "bar", + }, + }, + }, + CreateBeforeDestroy: true, + } + + objCopy := obj.DeepCopy() + if !reflect.DeepEqual(obj, objCopy) { + t.Fatalf("not equal\n%#v\n%#v", obj, objCopy) + } +} + func TestStateDeepCopy(t *testing.T) { state := NewState() @@ -209,11 +236,12 @@ func TestStateDeepCopy(t *testing.T) { Name: "baz", }.Instance(addrs.IntKey(0)), &ResourceInstanceObjectSrc{ - Status: ObjectReady, - SchemaVersion: 1, - AttrsJSON: []byte(`{"woozles":"confuzles"}`), - Private: []byte("private data"), - Dependencies: []addrs.ConfigResource{}, + Status: ObjectReady, + SchemaVersion: 1, + AttrsJSON: []byte(`{"woozles":"confuzles"}`), + Private: []byte("private data"), + Dependencies: []addrs.ConfigResource{}, + CreateBeforeDestroy: true, }, addrs.AbsProviderConfig{ Provider: addrs.NewDefaultProvider("test"), diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/read.go b/vendor/github.com/hashicorp/terraform/states/statefile/read.go index d691c029..8abd3be1 100644 --- a/vendor/github.com/hashicorp/terraform/states/statefile/read.go +++ b/vendor/github.com/hashicorp/terraform/states/statefile/read.go @@ -62,15 +62,6 @@ func Read(r io.Reader) (*File, error) { panic("readState returned nil state with no errors") } - if state.TerraformVersion != nil && state.TerraformVersion.GreaterThan(tfversion.SemVer) { - return state, fmt.Errorf( - "state snapshot was created by Terraform v%s, which is newer than current v%s; upgrade to Terraform v%s or greater to work with this state", - state.TerraformVersion, - tfversion.SemVer, - state.TerraformVersion, - ) - } - return state, diags.Err() } diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/testdata/roundtrip/v4-future.in.tfstate b/vendor/github.com/hashicorp/terraform/states/statefile/testdata/roundtrip/v4-future.in.tfstate new file mode 100644 index 00000000..71d759bc --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/states/statefile/testdata/roundtrip/v4-future.in.tfstate @@ -0,0 +1,60 @@ +{ + "version": 4, + "serial": 0, + "lineage": "f2968801-fa14-41ab-a044-224f3a4adf04", + "terraform_version": "999.0.0", + "outputs": { + "numbers": { + "type": "string", + "value": "0,1" + } + }, + "resources": [ + { + "mode": "managed", + "type": "null_resource", + "name": "bar", + "provider": "provider[\"registry.terraform.io/-/null\"]", + "instances": [ + { + "schema_version": 0, + "attributes_flat": { + "id": "5388490630832483079", + "triggers.%": "1", + "triggers.whaaat": "0,1" + }, + "depends_on": [ + "null_resource.foo" + ] + } + ] + }, + { + "mode": "managed", + "type": "null_resource", + "name": "foo", + "provider": "provider[\"registry.terraform.io/-/null\"]", + "each": "list", + "instances": [ + { + "index_key": 0, + "schema_version": 0, + "attributes_flat": { + "id": "8212585058302700791", + "triggers.%": "1", + "triggers.what": "0" + } + }, + { + "index_key": 1, + "schema_version": 0, + "attributes_flat": { + "id": "1523897709610803586", + "triggers.%": "1", + "triggers.what": "0" + } + } + ] + } + ] +} diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/testdata/roundtrip/v4-future.out.tfstate b/vendor/github.com/hashicorp/terraform/states/statefile/testdata/roundtrip/v4-future.out.tfstate new file mode 120000 index 00000000..b4037372 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/states/statefile/testdata/roundtrip/v4-future.out.tfstate @@ -0,0 +1 @@ +v4-future.in.tfstate \ No newline at end of file diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/version1.go b/vendor/github.com/hashicorp/terraform/states/statefile/version1.go index 80d711bc..2a5edc01 100644 --- a/vendor/github.com/hashicorp/terraform/states/statefile/version1.go +++ b/vendor/github.com/hashicorp/terraform/states/statefile/version1.go @@ -165,10 +165,3 @@ type instanceStateV1 struct { // external client code. Meta map[string]string `json:"meta,omitempty"` } - -type ephemeralStateV1 struct { - // ConnInfo is used for the providers to export information which is - // used to connect to the resource for provisioning. For example, - // this could contain SSH or WinRM credentials. - ConnInfo map[string]string `json:"-"` -} diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/version2.go b/vendor/github.com/hashicorp/terraform/states/statefile/version2.go index be93924a..9f74815e 100644 --- a/vendor/github.com/hashicorp/terraform/states/statefile/version2.go +++ b/vendor/github.com/hashicorp/terraform/states/statefile/version2.go @@ -3,7 +3,6 @@ package statefile import ( "encoding/json" "fmt" - "sync" "github.com/hashicorp/terraform/tfdiags" ) @@ -95,8 +94,6 @@ type outputStateV2 struct { // Value contains the value of the output, in the structure described // by the Type field. Value interface{} `json:"value"` - - mu sync.Mutex } type moduleStateV2 struct { @@ -178,8 +175,6 @@ type resourceStateV2 struct { // e.g. "aws_instance" goes with the "aws" provider. // If the resource block contained a "provider" key, that value will be set here. Provider string `json:"provider"` - - mu sync.Mutex } type instanceStateV2 struct { diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/version3_upgrade.go b/vendor/github.com/hashicorp/terraform/states/statefile/version3_upgrade.go index e54a08cc..29c3eb77 100644 --- a/vendor/github.com/hashicorp/terraform/states/statefile/version3_upgrade.go +++ b/vendor/github.com/hashicorp/terraform/states/statefile/version3_upgrade.go @@ -3,7 +3,6 @@ package statefile import ( "encoding/json" "fmt" - "log" "strconv" "strings" @@ -67,7 +66,7 @@ func upgradeStateV3ToV4(old *stateV3) (*stateV4, error) { } // In a v3 state file, a "resource state" is actually an instance - // state, so we need to fill in a missing level of heirarchy here + // state, so we need to fill in a missing level of hierarchy here // by lazily creating resource states as we encounter them. // We'll track them in here, keyed on the string representation of // the resource address. @@ -336,35 +335,6 @@ func upgradeInstanceObjectV3ToV4(rsOld *resourceStateV2, isOld *instanceStateV2, } } - dependencies := make([]string, 0, len(rsOld.Dependencies)) - for _, v := range rsOld.Dependencies { - depStr, err := parseLegacyDependency(v) - if err != nil { - // We just drop invalid dependencies on the floor here, because - // they tend to get left behind in Terraform 0.11 when resources - // are renamed or moved between modules and there's no automatic - // way to fix them here. In practice it shouldn't hurt to miss - // a few dependency edges in the state because a subsequent plan - // will run a refresh walk first and re-synchronize the - // dependencies with the configuration. - // - // There is one rough edges where this can cause an incorrect - // result, though: If the first command the user runs after - // upgrading to Terraform 0.12 uses -refresh=false and thus - // prevents the dependency reorganization from occurring _and_ - // that initial plan discovered "orphaned" resources (not present - // in configuration any longer) then when the plan is applied the - // destroy ordering will be incorrect for the instances of those - // resources. We expect that is a rare enough situation that it - // isn't a big deal, and even when it _does_ occur it's common for - // the apply to succeed anyway unless many separate resources with - // complex inter-dependencies are all orphaned at once. - log.Printf("statefile: ignoring invalid dependency address %q while upgrading from state version 3 to version 4: %s", v, err) - continue - } - dependencies = append(dependencies, depStr) - } - return &instanceObjectStateV4{ IndexKey: instKeyRaw, Status: status, @@ -473,28 +443,3 @@ func simplifyImpliedValueType(ty cty.Type) cty.Type { return ty } } - -func parseLegacyDependency(s string) (string, error) { - parts := strings.Split(s, ".") - ret := parts[0] - for _, part := range parts[1:] { - if part == "*" { - break - } - if i, err := strconv.Atoi(part); err == nil { - ret = ret + fmt.Sprintf("[%d]", i) - break - } - ret = ret + "." + part - } - - // The result must parse as a reference, or else we'll create an invalid - // state file. - var diags tfdiags.Diagnostics - _, diags = addrs.ParseRefStr(ret) - if diags.HasErrors() { - return "", diags.Err() - } - - return ret, nil -} diff --git a/vendor/github.com/hashicorp/terraform/states/statemgr/filesystem.go b/vendor/github.com/hashicorp/terraform/states/statemgr/filesystem.go index 138e57da..73782916 100644 --- a/vendor/github.com/hashicorp/terraform/states/statemgr/filesystem.go +++ b/vendor/github.com/hashicorp/terraform/states/statemgr/filesystem.go @@ -184,7 +184,7 @@ func (s *Filesystem) writeState(state *states.State, meta *SnapshotMeta) error { } s.file.State = state.DeepCopy() - if _, err := s.stateFileOut.Seek(0, os.SEEK_SET); err != nil { + if _, err := s.stateFileOut.Seek(0, io.SeekStart); err != nil { return err } if err := s.stateFileOut.Truncate(0); err != nil { @@ -269,7 +269,7 @@ func (s *Filesystem) refreshState() error { } // we have a state file, make sure we're at the start - s.stateFileOut.Seek(0, os.SEEK_SET) + s.stateFileOut.Seek(0, io.SeekStart) reader = s.stateFileOut } diff --git a/vendor/github.com/hashicorp/terraform/states/statemgr/filesystem_lock_unix.go b/vendor/github.com/hashicorp/terraform/states/statemgr/filesystem_lock_unix.go index 4c4f571e..1a970945 100644 --- a/vendor/github.com/hashicorp/terraform/states/statemgr/filesystem_lock_unix.go +++ b/vendor/github.com/hashicorp/terraform/states/statemgr/filesystem_lock_unix.go @@ -3,8 +3,8 @@ package statemgr import ( + "io" "log" - "os" "syscall" ) @@ -14,7 +14,7 @@ func (s *Filesystem) lock() error { log.Printf("[TRACE] statemgr.Filesystem: locking %s using fcntl flock", s.path) flock := &syscall.Flock_t{ Type: syscall.F_RDLCK | syscall.F_WRLCK, - Whence: int16(os.SEEK_SET), + Whence: int16(io.SeekStart), Start: 0, Len: 0, } @@ -27,7 +27,7 @@ func (s *Filesystem) unlock() error { log.Printf("[TRACE] statemgr.Filesystem: unlocking %s using fcntl flock", s.path) flock := &syscall.Flock_t{ Type: syscall.F_UNLCK, - Whence: int16(os.SEEK_SET), + Whence: int16(io.SeekStart), Start: 0, Len: 0, } diff --git a/vendor/github.com/hashicorp/terraform/states/statemgr/statemgr_test.go b/vendor/github.com/hashicorp/terraform/states/statemgr/statemgr_test.go index 41c73d1d..e9e82267 100644 --- a/vendor/github.com/hashicorp/terraform/states/statemgr/statemgr_test.go +++ b/vendor/github.com/hashicorp/terraform/states/statemgr/statemgr_test.go @@ -67,12 +67,11 @@ func TestLockWithContext(t *testing.T) { // unlock the state during LockWithContext unlocked := make(chan struct{}) + var unlockErr error go func() { defer close(unlocked) <-attempted - if err := s.Unlock(id); err != nil { - t.Fatal(err) - } + unlockErr = s.Unlock(id) }() ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second) @@ -85,6 +84,9 @@ func TestLockWithContext(t *testing.T) { // ensure the goruotine completes <-unlocked + if unlockErr != nil { + t.Fatal(unlockErr) + } } func TestMain(m *testing.M) { diff --git a/vendor/github.com/hashicorp/terraform/synchronized_writers.go b/vendor/github.com/hashicorp/terraform/synchronized_writers.go deleted file mode 100644 index 2533d131..00000000 --- a/vendor/github.com/hashicorp/terraform/synchronized_writers.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - "io" - "sync" -) - -type synchronizedWriter struct { - io.Writer - mutex *sync.Mutex -} - -// synchronizedWriters takes a set of writers and returns wrappers that ensure -// that only one write can be outstanding at a time across the whole set. -func synchronizedWriters(targets ...io.Writer) []io.Writer { - mutex := &sync.Mutex{} - ret := make([]io.Writer, len(targets)) - for i, target := range targets { - ret[i] = &synchronizedWriter{ - Writer: target, - mutex: mutex, - } - } - return ret -} - -func (w *synchronizedWriter) Write(p []byte) (int, error) { - w.mutex.Lock() - defer w.mutex.Unlock() - return w.Writer.Write(p) -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/context.go b/vendor/github.com/hashicorp/terraform/terraform/context.go index e74a72ae..9da7a3ff 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/context.go +++ b/vendor/github.com/hashicorp/terraform/terraform/context.go @@ -1,12 +1,13 @@ package terraform import ( - "bytes" "context" "fmt" "log" + "strings" "sync" + "github.com/apparentlymart/go-versions/versions" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/instances" @@ -15,10 +16,11 @@ import ( "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/provisioners" "github.com/hashicorp/terraform/states" - "github.com/hashicorp/terraform/states/statefile" "github.com/hashicorp/terraform/tfdiags" "github.com/zclconf/go-cty/cty" + "github.com/hashicorp/terraform/internal/depsfile" + "github.com/hashicorp/terraform/internal/getproviders" _ "github.com/hashicorp/terraform/internal/logging" ) @@ -35,28 +37,18 @@ const ( InputModeStd = InputModeProvider ) -var ( - // contextFailOnShadowError will cause Context operations to return - // errors when shadow operations fail. This is only used for testing. - contextFailOnShadowError = false - - // contextTestDeepCopyOnPlan will perform a Diff DeepCopy on every - // Plan operation, effectively testing the Diff DeepCopy whenever - // a Plan occurs. This is enabled for tests. - contextTestDeepCopyOnPlan = false -) - // ContextOpts are the user-configurable options to create a context with // NewContext. type ContextOpts struct { - Config *configs.Config - Changes *plans.Changes - State *states.State - Targets []addrs.Targetable - Variables InputValues - Meta *ContextMeta - Destroy bool - SkipRefresh bool + Config *configs.Config + Changes *plans.Changes + State *states.State + Targets []addrs.Targetable + ForceReplace []addrs.AbsResourceInstance + Variables InputValues + Meta *ContextMeta + PlanMode plans.Mode + SkipRefresh bool Hooks []Hook Parallelism int @@ -67,6 +59,14 @@ type ContextOpts struct { // plugins that will be requested from the provider resolver. ProviderSHA256s map[string][]byte + // If non-nil, will be verified to ensure that provider requirements from + // configuration can be satisfied by the set of locked dependencies. + LockedDependencies *depsfile.Locks + + // Set of providers to exclude from the requirements check process, as they + // are marked as in local development. + ProvidersInDevelopment map[addrs.Provider]struct{} + UIInput UIInput } @@ -97,13 +97,36 @@ type ContextMeta struct { type Context struct { config *configs.Config changes *plans.Changes - state *states.State - refreshState *states.State skipRefresh bool targets []addrs.Targetable + forceReplace []addrs.AbsResourceInstance variables InputValues meta *ContextMeta - destroy bool + planMode plans.Mode + + // state, refreshState, and prevRunState simultaneously track three + // different incarnations of the Terraform state: + // + // "state" is always the most "up-to-date". During planning it represents + // our best approximation of the planned new state, and during applying + // it represents the results of all of the actions we've taken so far. + // + // "refreshState" is populated and relevant only during planning, where we + // update it to reflect a provider's sense of the current state of the + // remote object each resource instance is bound to but don't include + // any changes implied by the configuration. + // + // "prevRunState" is similar to refreshState except that it doesn't even + // include the result of the provider's refresh step, and instead reflects + // the state as we found it prior to any changes, although it does reflect + // the result of running the provider's schema upgrade actions so that the + // resource instance objects will all conform to the _current_ resource + // type schemas if planning is successful, so that in that case it will + // be meaningful to compare prevRunState to refreshState to detect changes + // made outside of Terraform. + state *states.State + refreshState *states.State + prevRunState *states.State hooks []Hook components contextComponentFactory @@ -115,11 +138,9 @@ type Context struct { parallelSem Semaphore providerInputConfig map[string]map[string]cty.Value providerSHA256s map[string][]byte - runLock sync.Mutex runCond *sync.Cond runContext context.Context runContextCancel context.CancelFunc - shadowErr error } // (additional methods on Context can be found in context_*.go files.) @@ -212,6 +233,85 @@ func NewContext(opts *ContextOpts) (*Context, tfdiags.Diagnostics) { config = configs.NewEmptyConfig() } + // If we have a configuration and a set of locked dependencies, verify that + // the provider requirements from the configuration can be satisfied by the + // locked dependencies. + if opts.LockedDependencies != nil { + reqs, providerDiags := config.ProviderRequirements() + diags = diags.Append(providerDiags) + + locked := opts.LockedDependencies.AllProviders() + unmetReqs := make(getproviders.Requirements) + for provider, versionConstraints := range reqs { + // Builtin providers are not listed in the locks file + if provider.IsBuiltIn() { + continue + } + // Development providers must be excluded from this check + if _, ok := opts.ProvidersInDevelopment[provider]; ok { + continue + } + // If the required provider doesn't exist in the lock, or the + // locked version doesn't meet the constraints, mark the + // requirement unmet + acceptable := versions.MeetingConstraints(versionConstraints) + if lock, ok := locked[provider]; !ok || !acceptable.Has(lock.Version()) { + unmetReqs[provider] = versionConstraints + } + } + + if len(unmetReqs) > 0 { + var buf strings.Builder + for provider, versionConstraints := range unmetReqs { + fmt.Fprintf(&buf, "\n- %s", provider) + if len(versionConstraints) > 0 { + fmt.Fprintf(&buf, " (%s)", getproviders.VersionConstraintsString(versionConstraints)) + } + } + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Provider requirements cannot be satisfied by locked dependencies", + fmt.Sprintf("The following required providers are not installed:\n%s\n\nPlease run \"terraform init\".", buf.String()), + )) + return nil, diags + } + } + + switch opts.PlanMode { + case plans.NormalMode, plans.DestroyMode: + // OK + case plans.RefreshOnlyMode: + if opts.SkipRefresh { + // The CLI layer (and other similar callers) should prevent this + // combination of options. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Incompatible plan options", + "Cannot skip refreshing in refresh-only mode. This is a bug in Terraform.", + )) + return nil, diags + } + default: + // The CLI layer (and other similar callers) should not try to + // create a context for a mode that Terraform Core doesn't support. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Unsupported plan mode", + fmt.Sprintf("Terraform Core doesn't know how to handle plan mode %s. This is a bug in Terraform.", opts.PlanMode), + )) + return nil, diags + } + if len(opts.ForceReplace) > 0 && opts.PlanMode != plans.NormalMode { + // The other modes don't generate no-op or update actions that we might + // upgrade to be "replace", so doesn't make sense to combine those. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Unsupported plan mode", + fmt.Sprintf("Forcing resource instance replacement (with -replace=...) is allowed only in normal planning mode."), + )) + return nil, diags + } + log.Printf("[TRACE] terraform.NewContext: complete") // By the time we get here, we should have values defined for all of @@ -229,15 +329,17 @@ func NewContext(opts *ContextOpts) (*Context, tfdiags.Diagnostics) { return &Context{ components: components, schemas: schemas, - destroy: opts.Destroy, + planMode: opts.PlanMode, changes: changes, hooks: hooks, meta: opts.Meta, config: config, state: state, refreshState: state.DeepCopy(), + prevRunState: state.DeepCopy(), skipRefresh: opts.SkipRefresh, targets: opts.Targets, + forceReplace: opts.ForceReplace, uiInput: opts.UIInput, variables: variables, @@ -272,13 +374,14 @@ func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, tfdiags. switch typ { case GraphTypeApply: return (&ApplyGraphBuilder{ - Config: c.config, - Changes: c.changes, - State: c.state, - Components: c.components, - Schemas: c.schemas, - Targets: c.targets, - Validate: opts.Validate, + Config: c.config, + Changes: c.changes, + State: c.state, + Components: c.components, + Schemas: c.schemas, + Targets: c.targets, + ForceReplace: c.forceReplace, + Validate: opts.Validate, }).Build(addrs.RootModuleInstance) case GraphTypeValidate: @@ -296,6 +399,18 @@ func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, tfdiags. case GraphTypePlan: // Create the plan graph builder return (&PlanGraphBuilder{ + Config: c.config, + State: c.state, + Components: c.components, + Schemas: c.schemas, + Targets: c.targets, + ForceReplace: c.forceReplace, + Validate: opts.Validate, + skipRefresh: c.skipRefresh, + }).Build(addrs.RootModuleInstance) + + case GraphTypePlanDestroy: + return (&DestroyPlanGraphBuilder{ Config: c.config, State: c.state, Components: c.components, @@ -305,14 +420,18 @@ func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, tfdiags. skipRefresh: c.skipRefresh, }).Build(addrs.RootModuleInstance) - case GraphTypePlanDestroy: - return (&DestroyPlanGraphBuilder{ - Config: c.config, - State: c.state, - Components: c.components, - Schemas: c.schemas, - Targets: c.targets, - Validate: opts.Validate, + case GraphTypePlanRefreshOnly: + // Create the plan graph builder, with skipPlanChanges set to + // activate the "refresh only" mode. + return (&PlanGraphBuilder{ + Config: c.config, + State: c.state, + Components: c.components, + Schemas: c.schemas, + Targets: c.targets, + Validate: opts.Validate, + skipRefresh: c.skipRefresh, + skipPlanChanges: true, // this activates "refresh only" mode. }).Build(addrs.RootModuleInstance) case GraphTypeEval: @@ -329,33 +448,6 @@ func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, tfdiags. } } -// ShadowError returns any errors caught during a shadow operation. -// -// A shadow operation is an operation run in parallel to a real operation -// that performs the same tasks using new logic on copied state. The results -// are compared to ensure that the new logic works the same as the old logic. -// The shadow never affects the real operation or return values. -// -// The result of the shadow operation are only available through this function -// call after a real operation is complete. -// -// For API consumers of Context, you can safely ignore this function -// completely if you have no interest in helping report experimental feature -// errors to Terraform maintainers. Otherwise, please call this function -// after every operation and report this to the user. -// -// IMPORTANT: Shadow errors are _never_ critical: they _never_ affect -// the real state or result of a real operation. They are purely informational -// to assist in future Terraform versions being more stable. Please message -// this effectively to the end user. -// -// This must be called only when no other operation is running (refresh, -// plan, etc.). The result can be used in parallel to any other operation -// running. -func (c *Context) ShadowError() error { - return c.shadowErr -} - // State returns a copy of the current state associated with this context. // // This cannot safely be called in parallel with any other Context function. @@ -447,7 +539,7 @@ func (c *Context) Apply() (*states.State, tfdiags.Diagnostics) { // Determine the operation operation := walkApply - if c.destroy { + if c.planMode == plans.DestroyMode { operation = walkDestroy } @@ -456,7 +548,7 @@ func (c *Context) Apply() (*states.State, tfdiags.Diagnostics) { diags = diags.Append(walker.NonFatalDiagnostics) diags = diags.Append(walkDiags) - if c.destroy && !diags.HasErrors() { + if c.planMode == plans.DestroyMode && !diags.HasErrors() { // If we know we were trying to destroy objects anyway, and we // completed without any errors, then we'll also prune out any // leftover empty resource husks (left after all of the instances @@ -516,6 +608,24 @@ The -target option is not for routine use, and is provided only for exceptional )) } + var plan *plans.Plan + var planDiags tfdiags.Diagnostics + switch c.planMode { + case plans.NormalMode: + plan, planDiags = c.plan() + case plans.DestroyMode: + plan, planDiags = c.destroyPlan() + case plans.RefreshOnlyMode: + plan, planDiags = c.refreshOnlyPlan() + default: + panic(fmt.Sprintf("unsupported plan mode %s", c.planMode)) + } + diags = diags.Append(planDiags) + if diags.HasErrors() { + return nil, diags + } + + // convert the variables into the format expected for the plan varVals := make(map[string]plans.DynamicValue, len(c.variables)) for k, iv := range c.variables { // We use cty.DynamicPseudoType here so that we'll save both the @@ -533,44 +643,157 @@ The -target option is not for routine use, and is provided only for exceptional varVals[k] = dv } - p := &plans.Plan{ - VariableValues: varVals, - TargetAddrs: c.targets, - ProviderSHA256s: c.providerSHA256s, - } + // insert the run-specific data from the context into the plan; variables, + // targets and provider SHAs. + plan.VariableValues = varVals + plan.TargetAddrs = c.targets + plan.ProviderSHA256s = c.providerSHA256s - operation := walkPlan - graphType := GraphTypePlan - if c.destroy { - operation = walkPlanDestroy - graphType = GraphTypePlanDestroy - } + return plan, diags +} + +func (c *Context) plan() (*plans.Plan, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics - graph, graphDiags := c.Graph(graphType, nil) + graph, graphDiags := c.Graph(GraphTypePlan, nil) diags = diags.Append(graphDiags) if graphDiags.HasErrors() { return nil, diags } // Do the walk - walker, walkDiags := c.walk(graph, operation) + walker, walkDiags := c.walk(graph, walkPlan) diags = diags.Append(walker.NonFatalDiagnostics) diags = diags.Append(walkDiags) if walkDiags.HasErrors() { return nil, diags } - p.Changes = c.changes + plan := &plans.Plan{ + UIMode: plans.NormalMode, + Changes: c.changes, + ForceReplaceAddrs: c.forceReplace, + PrevRunState: c.prevRunState.DeepCopy(), + } c.refreshState.SyncWrapper().RemovePlannedResourceInstanceObjects() refreshedState := c.refreshState.DeepCopy() - p.State = refreshedState + plan.PriorState = refreshedState // replace the working state with the updated state, so that immediate calls // to Apply work as expected. c.state = refreshedState - return p, diags + return plan, diags +} + +func (c *Context) destroyPlan() (*plans.Plan, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + destroyPlan := &plans.Plan{ + PriorState: c.state.DeepCopy(), + } + c.changes = plans.NewChanges() + + // A destroy plan starts by running Refresh to read any pending data + // sources, and remove missing managed resources. This is required because + // a "destroy plan" is only creating delete changes, and is essentially a + // local operation. + // + // NOTE: if skipRefresh _is_ set then we'll rely on the destroy-plan walk + // below to upgrade the prevRunState and priorState both to the latest + // resource type schemas, so NodePlanDestroyableResourceInstance.Execute + // must coordinate with this by taking that action only when c.skipRefresh + // _is_ set. This coupling between the two is unfortunate but necessary + // to work within our current structure. + if !c.skipRefresh { + refreshPlan, refreshDiags := c.plan() + diags = diags.Append(refreshDiags) + if diags.HasErrors() { + return nil, diags + } + + // insert the refreshed state into the destroy plan result, and discard + // the changes recorded from the refresh. + destroyPlan.PriorState = refreshPlan.PriorState.DeepCopy() + destroyPlan.PrevRunState = refreshPlan.PrevRunState.DeepCopy() + c.changes = plans.NewChanges() + } + + graph, graphDiags := c.Graph(GraphTypePlanDestroy, nil) + diags = diags.Append(graphDiags) + if graphDiags.HasErrors() { + return nil, diags + } + + // Do the walk + walker, walkDiags := c.walk(graph, walkPlanDestroy) + diags = diags.Append(walker.NonFatalDiagnostics) + diags = diags.Append(walkDiags) + if walkDiags.HasErrors() { + return nil, diags + } + + if c.skipRefresh { + // If we didn't do refreshing then both the previous run state and + // the prior state are the result of upgrading the previous run state, + // which we should've upgraded as part of the plan-destroy walk + // in NodePlanDestroyableResourceInstance.Execute, so they'll have the + // current schema but neither will reflect any out-of-band changes in + // the remote system. + destroyPlan.PrevRunState = c.prevRunState.DeepCopy() + destroyPlan.PriorState = c.prevRunState.DeepCopy() + } + + destroyPlan.UIMode = plans.DestroyMode + destroyPlan.Changes = c.changes + return destroyPlan, diags +} + +func (c *Context) refreshOnlyPlan() (*plans.Plan, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + + graph, graphDiags := c.Graph(GraphTypePlanRefreshOnly, nil) + diags = diags.Append(graphDiags) + if graphDiags.HasErrors() { + return nil, diags + } + + // Do the walk + walker, walkDiags := c.walk(graph, walkPlan) + diags = diags.Append(walker.NonFatalDiagnostics) + diags = diags.Append(walkDiags) + if walkDiags.HasErrors() { + return nil, diags + } + plan := &plans.Plan{ + UIMode: plans.RefreshOnlyMode, + Changes: c.changes, + PrevRunState: c.prevRunState.DeepCopy(), + } + + // If the graph builder and graph nodes correctly obeyed our directive + // to refresh only, the set of resource changes should always be empty. + // We'll safety-check that here so we can return a clear message about it, + // rather than probably just generating confusing output at the UI layer. + if len(plan.Changes.Resources) != 0 { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Invalid refresh-only plan", + "Terraform generated planned resource changes in a refresh-only plan. This is a bug in Terraform.", + )) + } + + c.refreshState.SyncWrapper().RemovePlannedResourceInstanceObjects() + + refreshedState := c.refreshState + plan.PriorState = refreshedState.DeepCopy() + + // replace the working state with the updated state, so that immediate calls + // to Apply work as expected. DeepCopy because such an apply should not + // mutate + c.state = refreshedState + + return plan, diags } // Refresh goes through all the resources in the state and refreshes them @@ -584,7 +807,7 @@ func (c *Context) Refresh() (*states.State, tfdiags.Diagnostics) { return nil, diags } - return p.State, diags + return p.PriorState, diags } // Stop stops the running task. @@ -694,9 +917,6 @@ func (c *Context) acquireRun(phase string) func() { // Reset the stop hook so we're not stopped c.sh.Reset() - // Reset the shadow errors - c.shadowErr = nil - return c.releaseRun } @@ -741,6 +961,7 @@ func (c *Context) walk(graph *Graph, operation walkOperation) (*ContextGraphWalk func (c *Context) graphWalker(operation walkOperation) *ContextGraphWalker { var state *states.SyncState var refreshState *states.SyncState + var prevRunState *states.SyncState switch operation { case walkValidate: @@ -748,12 +969,14 @@ func (c *Context) graphWalker(operation walkOperation) *ContextGraphWalker { state = states.NewState().SyncWrapper() // validate currently uses the plan graph, so we have to populate the - // refreshState. + // refreshState and the prevRunState. refreshState = states.NewState().SyncWrapper() + prevRunState = states.NewState().SyncWrapper() - case walkPlan: + case walkPlan, walkPlanDestroy: state = c.state.SyncWrapper() refreshState = c.refreshState.SyncWrapper() + prevRunState = c.prevRunState.SyncWrapper() default: state = c.state.SyncWrapper() @@ -763,6 +986,7 @@ func (c *Context) graphWalker(operation walkOperation) *ContextGraphWalker { Context: c, State: state, RefreshState: refreshState, + PrevRunState: prevRunState, Changes: c.changes.SyncWrapper(), InstanceExpander: instances.NewExpander(), Operation: operation, @@ -841,37 +1065,3 @@ func (c *Context) watchStop(walker *ContextGraphWalker) (chan struct{}, <-chan s return stop, wait } - -// ShimLegacyState is a helper that takes the legacy state type and -// converts it to the new state type. -// -// This is implemented as a state file upgrade, so it will not preserve -// parts of the state structure that are not included in a serialized state, -// such as the resolved results of any local values, outputs in non-root -// modules, etc. -func ShimLegacyState(legacy *State) (*states.State, error) { - if legacy == nil { - return nil, nil - } - var buf bytes.Buffer - err := WriteState(legacy, &buf) - if err != nil { - return nil, err - } - f, err := statefile.Read(&buf) - if err != nil { - return nil, err - } - return f.State, err -} - -// MustShimLegacyState is a wrapper around ShimLegacyState that panics if -// the conversion does not succeed. This is primarily intended for tests where -// the given legacy state is an object constructed within the test. -func MustShimLegacyState(legacy *State) *states.State { - ret, err := ShimLegacyState(legacy) - if err != nil { - panic(err) - } - return ret -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/context_apply2_test.go b/vendor/github.com/hashicorp/terraform/terraform/context_apply2_test.go new file mode 100644 index 00000000..28e655bf --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/context_apply2_test.go @@ -0,0 +1,448 @@ +package terraform + +import ( + "errors" + "fmt" + "sync" + "testing" + "time" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/states" + "github.com/zclconf/go-cty/cty" +) + +// Test that the PreApply hook is called with the correct deposed key +func TestContext2Apply_createBeforeDestroy_deposedKeyPreApply(t *testing.T) { + m := testModule(t, "apply-cbd-deposed-only") + p := testProvider("aws") + p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn + + deposedKey := states.NewDeposedKey() + + state := states.NewState() + root := state.EnsureModule(addrs.RootModuleInstance) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("aws_instance.bar").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"bar"}`), + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + ) + root.SetResourceInstanceDeposed( + mustResourceInstanceAddr("aws_instance.bar").Resource, + deposedKey, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectTainted, + AttrsJSON: []byte(`{"id":"foo"}`), + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + ) + + hook := new(MockHook) + ctx := testContext2(t, &ContextOpts{ + Config: m, + Hooks: []Hook{hook}, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), + }, + State: state, + }) + + if p, diags := ctx.Plan(); diags.HasErrors() { + t.Fatalf("diags: %s", diags.Err()) + } else { + t.Logf(legacyDiffComparisonString(p.Changes)) + } + + state, diags := ctx.Apply() + if diags.HasErrors() { + t.Fatalf("diags: %s", diags.Err()) + } + + // Verify PreApply was called correctly + if !hook.PreApplyCalled { + t.Fatalf("PreApply hook not called") + } + if addr, wantAddr := hook.PreApplyAddr, mustResourceInstanceAddr("aws_instance.bar"); !addr.Equal(wantAddr) { + t.Errorf("expected addr to be %s, but was %s", wantAddr, addr) + } + if gen := hook.PreApplyGen; gen != deposedKey { + t.Errorf("expected gen to be %q, but was %q", deposedKey, gen) + } +} + +func TestContext2Apply_destroyWithDataSourceExpansion(t *testing.T) { + // While managed resources store their destroy-time dependencies, data + // sources do not. This means that if a provider were only included in a + // destroy graph because of data sources, it could have dependencies which + // are not correctly ordered. Here we verify that the provider is not + // included in the destroy operation, and all dependency evaluations + // succeed. + + m := testModuleInline(t, map[string]string{ + "main.tf": ` +module "mod" { + source = "./mod" +} + +provider "other" { + foo = module.mod.data +} + +# this should not require the provider be present during destroy +data "other_data_source" "a" { +} +`, + + "mod/main.tf": ` +data "test_data_source" "a" { + count = 1 +} + +data "test_data_source" "b" { + count = data.test_data_source.a[0].foo == "ok" ? 1 : 0 +} + +output "data" { + value = data.test_data_source.a[0].foo == "ok" ? data.test_data_source.b[0].foo : "nope" +} +`, + }) + + testP := testProvider("test") + otherP := testProvider("other") + + readData := func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { + return providers.ReadDataSourceResponse{ + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("data_source"), + "foo": cty.StringVal("ok"), + }), + } + } + + testP.ReadDataSourceFn = readData + otherP.ReadDataSourceFn = readData + + ps := map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(testP), + addrs.NewDefaultProvider("other"): testProviderFuncFixed(otherP), + } + + otherP.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { + foo := req.Config.GetAttr("foo") + if foo.IsNull() || foo.AsString() != "ok" { + resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("incorrect config val: %#v\n", foo)) + } + return resp + } + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: ps, + }) + + _, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + _, diags = ctx.Apply() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + // now destroy the whole thing + ctx = testContext2(t, &ContextOpts{ + Config: m, + Providers: ps, + PlanMode: plans.DestroyMode, + }) + + _, diags = ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + otherP.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { + // should not be used to destroy data sources + resp.Diagnostics = resp.Diagnostics.Append(errors.New("provider should not be used")) + return resp + } + + _, diags = ctx.Apply() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } +} + +func TestContext2Apply_destroyThenUpdate(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_instance" "a" { + value = "udpated" +} +`, + }) + + p := testProvider("test") + p.PlanResourceChangeFn = testDiffFn + + var orderMu sync.Mutex + var order []string + p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { + id := req.PriorState.GetAttr("id").AsString() + if id == "b" { + // slow down the b destroy, since a should wait for it + time.Sleep(100 * time.Millisecond) + } + + orderMu.Lock() + order = append(order, id) + orderMu.Unlock() + + resp.NewState = req.PlannedState + return resp + } + + addrA := mustResourceInstanceAddr(`test_instance.a`) + addrB := mustResourceInstanceAddr(`test_instance.b`) + + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent(addrA, &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"a","value":"old","type":"test"}`), + Status: states.ObjectReady, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) + + // test_instance.b depended on test_instance.a, and therefor should be + // destroyed before any changes to test_instance.a + s.SetResourceInstanceCurrent(addrB, &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"b"}`), + Status: states.ObjectReady, + Dependencies: []addrs.ConfigResource{addrA.ContainingResource().Config()}, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) + }) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + State: state, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + if _, diags := ctx.Plan(); diags.HasErrors() { + t.Fatal(diags.Err()) + } + + _, diags := ctx.Apply() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + if order[0] != "b" { + t.Fatalf("expected apply order [b, a], got: %v\n", order) + } +} + +// verify that dependencies are updated in the state during refresh and apply +func TestApply_updateDependencies(t *testing.T) { + state := states.NewState() + root := state.EnsureModule(addrs.RootModuleInstance) + + fooAddr := mustResourceInstanceAddr("aws_instance.foo") + barAddr := mustResourceInstanceAddr("aws_instance.bar") + bazAddr := mustResourceInstanceAddr("aws_instance.baz") + bamAddr := mustResourceInstanceAddr("aws_instance.bam") + binAddr := mustResourceInstanceAddr("aws_instance.bin") + root.SetResourceInstanceCurrent( + fooAddr.Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"foo"}`), + Dependencies: []addrs.ConfigResource{ + bazAddr.ContainingResource().Config(), + }, + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + ) + root.SetResourceInstanceCurrent( + binAddr.Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"bin","type":"aws_instance","unknown":"ok"}`), + Dependencies: []addrs.ConfigResource{ + bazAddr.ContainingResource().Config(), + }, + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + ) + root.SetResourceInstanceCurrent( + bazAddr.Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"baz"}`), + Dependencies: []addrs.ConfigResource{ + // Existing dependencies should not be removed from orphaned instances + bamAddr.ContainingResource().Config(), + }, + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + ) + root.SetResourceInstanceCurrent( + barAddr.Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"bar","foo":"foo"}`), + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + ) + + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "aws_instance" "bar" { + foo = aws_instance.foo.id +} + +resource "aws_instance" "foo" { +} + +resource "aws_instance" "bin" { +} +`, + }) + + p := testProvider("aws") + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), + }, + State: state, + }) + + plan, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + bar := plan.PriorState.ResourceInstance(barAddr) + if len(bar.Current.Dependencies) == 0 || !bar.Current.Dependencies[0].Equal(fooAddr.ContainingResource().Config()) { + t.Fatalf("bar should depend on foo after refresh, but got %s", bar.Current.Dependencies) + } + + foo := plan.PriorState.ResourceInstance(fooAddr) + if len(foo.Current.Dependencies) == 0 || !foo.Current.Dependencies[0].Equal(bazAddr.ContainingResource().Config()) { + t.Fatalf("foo should depend on baz after refresh because of the update, but got %s", foo.Current.Dependencies) + } + + bin := plan.PriorState.ResourceInstance(binAddr) + if len(bin.Current.Dependencies) != 0 { + t.Fatalf("bin should depend on nothing after refresh because there is no change, but got %s", bin.Current.Dependencies) + } + + baz := plan.PriorState.ResourceInstance(bazAddr) + if len(baz.Current.Dependencies) == 0 || !baz.Current.Dependencies[0].Equal(bamAddr.ContainingResource().Config()) { + t.Fatalf("baz should depend on bam after refresh, but got %s", baz.Current.Dependencies) + } + + state, diags = ctx.Apply() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + bar = state.ResourceInstance(barAddr) + if len(bar.Current.Dependencies) == 0 || !bar.Current.Dependencies[0].Equal(fooAddr.ContainingResource().Config()) { + t.Fatalf("bar should still depend on foo after apply, but got %s", bar.Current.Dependencies) + } + + foo = state.ResourceInstance(fooAddr) + if len(foo.Current.Dependencies) != 0 { + t.Fatalf("foo should have no deps after apply, but got %s", foo.Current.Dependencies) + } + +} + +func TestContext2Apply_additionalSensitiveFromState(t *testing.T) { + // Ensure we're not trying to double-mark values decoded from state + m := testModuleInline(t, map[string]string{ + "main.tf": ` +variable "secret" { + sensitive = true + default = ["secret"] +} + +resource "test_resource" "a" { + sensitive_attr = var.secret +} + +resource "test_resource" "b" { + value = test_resource.a.id +} +`, + }) + + p := new(MockProvider) + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ + ResourceTypes: map[string]*configschema.Block{ + "test_resource": { + Attributes: map[string]*configschema.Attribute{ + "id": { + Type: cty.String, + Computed: true, + }, + "value": { + Type: cty.String, + Optional: true, + }, + "sensitive_attr": { + Type: cty.List(cty.String), + Optional: true, + Sensitive: true, + }, + }, + }, + }, + }) + + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent( + mustResourceInstanceAddr(`test_resource.a`), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"a","sensitive_attr":["secret"]}`), + AttrSensitivePaths: []cty.PathValueMarks{ + { + Path: cty.GetAttrPath("sensitive_attr"), + Marks: cty.NewValueMarks("sensitive"), + }, + }, + Status: states.ObjectReady, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + }) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + State: state, + }) + + _, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.ErrWithWarnings()) + } + + _, diags = ctx.Apply() + if diags.HasErrors() { + t.Fatal(diags.ErrWithWarnings()) + } +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/context_apply_test.go b/vendor/github.com/hashicorp/terraform/terraform/context_apply_test.go index f5b28da2..d7d0910c 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/context_apply_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/context_apply_test.go @@ -34,8 +34,8 @@ import ( func TestContext2Apply_basic(t *testing.T) { m := testModule(t, "apply-good") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -75,7 +75,6 @@ func TestContext2Apply_unstable(t *testing.T) { m := testModule(t, "apply-unstable") p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -94,7 +93,7 @@ func TestContext2Apply_unstable(t *testing.T) { Type: "test_resource", Name: "foo", }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) - schema := p.GetSchemaReturn.ResourceTypes["test_resource"] // automatically available in mock + schema := p.GetProviderSchemaResponse.ResourceTypes["test_resource"].Block rds := plan.Changes.ResourceInstance(addr) rd, err := rds.Decode(schema.ImpliedType()) if err != nil { @@ -132,8 +131,8 @@ func TestContext2Apply_unstable(t *testing.T) { func TestContext2Apply_escape(t *testing.T) { m := testModule(t, "apply-escape") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -162,8 +161,8 @@ aws_instance.bar: func TestContext2Apply_resourceCountOneList(t *testing.T) { m := testModule(t, "apply-resource-count-one-list") p := testProvider("null") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -193,7 +192,6 @@ test = [foo]`) func TestContext2Apply_resourceCountZeroList(t *testing.T) { m := testModule(t, "apply-resource-count-zero-list") p := testProvider("null") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -267,7 +265,6 @@ func TestContext2Apply_resourceDependsOnModule(t *testing.T) { } if !reflect.DeepEqual(order, []string{"child", "parent"}) { - fmt.Println("ORDER:", order) t.Fatal("resources applied out of order") } @@ -355,7 +352,6 @@ func TestContext2Apply_resourceDependsOnModuleDestroy(t *testing.T) { var globalState *states.State { - p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -404,8 +400,8 @@ func TestContext2Apply_resourceDependsOnModuleDestroy(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - State: globalState, - Destroy: true, + State: globalState, + PlanMode: plans.DestroyMode, }) if _, diags := ctx.Plan(); diags.HasErrors() { @@ -534,8 +530,8 @@ func TestContext2Apply_resourceDependsOnModuleInModule(t *testing.T) { func TestContext2Apply_mapVarBetweenModules(t *testing.T) { m := testModule(t, "apply-map-var-through-module") p := testProvider("null") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -574,8 +570,8 @@ module.test: func TestContext2Apply_refCount(t *testing.T) { m := testModule(t, "apply-ref-count") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -607,8 +603,8 @@ func TestContext2Apply_refCount(t *testing.T) { func TestContext2Apply_providerAlias(t *testing.T) { m := testModule(t, "apply-provider-alias") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -660,7 +656,7 @@ func TestContext2Apply_providerAliasConfigure(t *testing.T) { // Configure to record calls AFTER Plan above var configCount int32 - p2.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p2.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { atomic.AddInt32(&configCount, 1) foo := req.Config.GetAttr("foo").AsString() @@ -691,9 +687,9 @@ func TestContext2Apply_providerAliasConfigure(t *testing.T) { func TestContext2Apply_providerWarning(t *testing.T) { m := testModule(t, "apply-provider-warning") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn - p.ValidateResourceTypeConfigFn = func(req providers.ValidateResourceTypeConfigRequest) (resp providers.ValidateResourceTypeConfigResponse) { + p.ApplyResourceChangeFn = testApplyFn + p.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) { resp.Diagnostics = resp.Diagnostics.Append(tfdiags.SimpleWarning("just a warning")) return } @@ -724,7 +720,7 @@ aws_instance.foo: t.Fatalf("got: \n%s\n\nexpected:\n%s", actual, expected) } - if !p.ConfigureCalled { + if !p.ConfigureProviderCalled { t.Fatalf("provider Configure() was never called!") } } @@ -733,7 +729,6 @@ func TestContext2Apply_emptyModule(t *testing.T) { // A module with only outputs (no resources) m := testModule(t, "apply-empty-module") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -762,8 +757,8 @@ func TestContext2Apply_emptyModule(t *testing.T) { func TestContext2Apply_createBeforeDestroy(t *testing.T) { m := testModule(t, "apply-good-create-before") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) root.SetResourceInstanceCurrent( @@ -809,16 +804,48 @@ func TestContext2Apply_createBeforeDestroy(t *testing.T) { func TestContext2Apply_createBeforeDestroyUpdate(t *testing.T) { m := testModule(t, "apply-good-create-before-update") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + // signal that resource foo has started applying + fooChan := make(chan struct{}) + + p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { + id := req.PriorState.GetAttr("id").AsString() + switch id { + case "bar": + select { + case <-fooChan: + resp.Diagnostics = resp.Diagnostics.Append(errors.New("bar must be updated before foo is destroyed")) + return resp + case <-time.After(100 * time.Millisecond): + // wait a moment to ensure that foo is not going to be destroyed first + } + case "foo": + close(fooChan) + } + + return testApplyFn(req) + } + state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) + fooAddr := mustResourceInstanceAddr("aws_instance.foo") + root.SetResourceInstanceCurrent( + fooAddr.Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"foo","foo":"bar"}`), + CreateBeforeDestroy: true, + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + ) root.SetResourceInstanceCurrent( mustResourceInstanceAddr("aws_instance.bar").Resource, &states.ResourceInstanceObjectSrc{ - Status: states.ObjectReady, - AttrsJSON: []byte(`{"id":"bar","foo":"bar"}`), + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"bar","foo":"bar"}`), + CreateBeforeDestroy: true, + Dependencies: []addrs.ConfigResource{fooAddr.ContainingResource().Config()}, }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), ) @@ -859,8 +886,8 @@ func TestContext2Apply_createBeforeDestroyUpdate(t *testing.T) { func TestContext2Apply_createBeforeDestroy_dependsNonCBD(t *testing.T) { m := testModule(t, "apply-cbd-depends-non-cbd") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -922,8 +949,8 @@ func TestContext2Apply_createBeforeDestroy_hook(t *testing.T) { h := new(MockHook) m := testModule(t, "apply-good-create-before") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) root.SetResourceInstanceCurrent( @@ -983,8 +1010,8 @@ func TestContext2Apply_createBeforeDestroy_hook(t *testing.T) { func TestContext2Apply_createBeforeDestroy_deposedCount(t *testing.T) { m := testModule(t, "apply-cbd-count") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -1061,8 +1088,8 @@ aws_instance.bar.1: func TestContext2Apply_createBeforeDestroy_deposedOnly(t *testing.T) { m := testModule(t, "apply-cbd-deposed-only") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -1114,7 +1141,6 @@ aws_instance.bar: func TestContext2Apply_destroyComputed(t *testing.T) { m := testModule(t, "apply-destroy-computed") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -1131,8 +1157,8 @@ func TestContext2Apply_destroyComputed(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, }) if p, diags := ctx.Plan(); diags.HasErrors() { @@ -1160,7 +1186,6 @@ func TestContext2Apply_destroyDependsOn(t *testing.T) { func testContext2Apply_destroyDependsOn(t *testing.T) { m := testModule(t, "apply-destroy-depends-on") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() @@ -1201,7 +1226,7 @@ func testContext2Apply_destroyDependsOn(t *testing.T) { addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, State: state, - Destroy: true, + PlanMode: plans.DestroyMode, Parallelism: 1, // To check ordering }) @@ -1276,9 +1301,9 @@ func TestContext2Apply_destroyDependsOnStateOnly(t *testing.T) { } func testContext2Apply_destroyDependsOnStateOnly(t *testing.T, state *states.State) { + state = state.DeepCopy() m := testModule(t, "empty") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn // Record the order we see Apply var actual []string @@ -1297,7 +1322,7 @@ func testContext2Apply_destroyDependsOnStateOnly(t *testing.T, state *states.Sta addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, State: state, - Destroy: true, + PlanMode: plans.DestroyMode, Parallelism: 1, // To check ordering }) @@ -1372,9 +1397,9 @@ func TestContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T) { } func testContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T, state *states.State) { + state = state.DeepCopy() m := testModule(t, "empty") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn // Record the order we see Apply @@ -1394,7 +1419,7 @@ func testContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T, state *stat addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, State: state, - Destroy: true, + PlanMode: plans.DestroyMode, Parallelism: 1, // To check ordering }) @@ -1415,9 +1440,8 @@ func testContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T, state *stat func TestContext2Apply_dataBasic(t *testing.T) { m := testModule(t, "apply-data-basic") p := testProvider("null") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn - p.ReadDataSourceResponse = providers.ReadDataSourceResponse{ + p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ State: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("yo"), "foo": cty.NullVal(cty.String), @@ -1450,8 +1474,13 @@ func TestContext2Apply_dataBasic(t *testing.T) { func TestContext2Apply_destroyData(t *testing.T) { m := testModule(t, "apply-destroy-data-resource") p := testProvider("null") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { + return providers.ReadDataSourceResponse{ + State: req.Config, + } + } + state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) root.SetResourceInstanceCurrent( @@ -1469,9 +1498,9 @@ func TestContext2Apply_destroyData(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), }, - State: state, - Destroy: true, - Hooks: []Hook{hook}, + State: state, + PlanMode: plans.DestroyMode, + Hooks: []Hook{hook}, }) if p, diags := ctx.Plan(); diags.HasErrors() { @@ -1494,6 +1523,8 @@ func TestContext2Apply_destroyData(t *testing.T) { } wantHookCalls := []*testHookCall{ + {"PreDiff", "data.null_data_source.testing"}, + {"PostDiff", "data.null_data_source.testing"}, {"PreDiff", "data.null_data_source.testing"}, {"PostDiff", "data.null_data_source.testing"}, {"PostStateUpdate", ""}, @@ -1510,7 +1541,6 @@ func TestContext2Apply_destroySkipsCBD(t *testing.T) { // just doing a `terraform destroy`. m := testModule(t, "apply-destroy-cbd") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -1536,8 +1566,8 @@ func TestContext2Apply_destroySkipsCBD(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, }) if p, diags := ctx.Plan(); diags.HasErrors() { @@ -1554,7 +1584,6 @@ func TestContext2Apply_destroySkipsCBD(t *testing.T) { func TestContext2Apply_destroyModuleVarProviderConfig(t *testing.T) { m := testModule(t, "apply-destroy-mod-var-provider-config") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -1571,8 +1600,8 @@ func TestContext2Apply_destroyModuleVarProviderConfig(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, }) if _, diags := ctx.Plan(); diags.HasErrors() { @@ -1591,7 +1620,7 @@ func TestContext2Apply_destroyCrossProviders(t *testing.T) { p_aws := testProvider("aws") p_aws.ApplyResourceChangeFn = testApplyFn p_aws.PlanResourceChangeFn = testDiffFn - p_aws.GetSchemaReturn = &ProviderSchema{ + p_aws.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -1614,7 +1643,7 @@ func TestContext2Apply_destroyCrossProviders(t *testing.T) { }, }, }, - } + }) providers := map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p_aws), @@ -1658,7 +1687,7 @@ func getContextForApply_destroyCrossProviders(t *testing.T, m *configs.Config, p Config: m, Providers: providerFactories, State: state, - Destroy: true, + PlanMode: plans.DestroyMode, }) return ctx @@ -1667,8 +1696,8 @@ func getContextForApply_destroyCrossProviders(t *testing.T, m *configs.Config, p func TestContext2Apply_minimal(t *testing.T) { m := testModule(t, "apply-minimal") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -1735,8 +1764,16 @@ func TestContext2Apply_cancel(t *testing.T) { }() state := <-stateCh - if applyDiags.HasErrors() { - t.Fatalf("unexpected errors: %s", applyDiags.Err()) + // only expecting an early exit error + if !applyDiags.HasErrors() { + t.Fatal("expected early exit error") + } + + for _, d := range applyDiags { + desc := d.Description() + if desc.Summary != "execution halted" { + t.Fatalf("unexpected error: %v", applyDiags.Err()) + } } actual := strings.TrimSpace(state.String()) @@ -1813,8 +1850,16 @@ func TestContext2Apply_cancelBlock(t *testing.T) { // Wait for apply to complete state := <-stateCh - if applyDiags.HasErrors() { - t.Fatalf("unexpected error: %s", applyDiags.Err()) + // only expecting an early exit error + if !applyDiags.HasErrors() { + t.Fatal("expected early exit error") + } + + for _, d := range applyDiags { + desc := d.Description() + if desc.Summary != "execution halted" { + t.Fatalf("unexpected error: %v", applyDiags.Err()) + } } checkStateString(t, state, ` @@ -1829,8 +1874,8 @@ aws_instance.foo: func TestContext2Apply_cancelProvisioner(t *testing.T) { m := testModule(t, "apply-cancel-provisioner") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn pr := testProvisioner() pr.GetSchemaResponse = provisioners.GetSchemaResponse{ @@ -1849,7 +1894,7 @@ func TestContext2Apply_cancelProvisioner(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -1883,7 +1928,18 @@ func TestContext2Apply_cancelProvisioner(t *testing.T) { // Wait for completion state := <-stateCh - assertNoErrors(t, applyDiags) + + // we are expecting only an early exit error + if !applyDiags.HasErrors() { + t.Fatal("expected early exit error") + } + + for _, d := range applyDiags { + desc := d.Description() + if desc.Summary != "execution halted" { + t.Fatalf("unexpected error: %v", applyDiags.Err()) + } + } checkStateString(t, state, ` aws_instance.foo: (tainted) @@ -1901,9 +1957,9 @@ aws_instance.foo: (tainted) func TestContext2Apply_compute(t *testing.T) { m := testModule(t, "apply-compute") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ + p.ApplyResourceChangeFn = testApplyFn + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -1938,7 +1994,7 @@ func TestContext2Apply_compute(t *testing.T) { }, }, }, - } + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -2028,7 +2084,6 @@ func TestContext2Apply_countDecrease(t *testing.T) { func TestContext2Apply_countDecreaseToOneX(t *testing.T) { m := testModule(t, "apply-count-dec-one") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -2089,7 +2144,6 @@ func TestContext2Apply_countDecreaseToOneX(t *testing.T) { func TestContext2Apply_countDecreaseToOneCorrupted(t *testing.T) { m := testModule(t, "apply-count-dec-one") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -2206,8 +2260,8 @@ aws_instance.foo.1: func TestContext2Apply_countVariable(t *testing.T) { m := testModule(t, "apply-count-variable") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -2234,8 +2288,8 @@ func TestContext2Apply_countVariable(t *testing.T) { func TestContext2Apply_countVariableRef(t *testing.T) { m := testModule(t, "apply-count-variable-ref") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -2267,7 +2321,6 @@ func TestContext2Apply_provisionerInterpCount(t *testing.T) { m, snap := testModuleWithSnapshot(t, "apply-provisioner-interp-count") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn pr := testProvisioner() @@ -2276,7 +2329,7 @@ func TestContext2Apply_provisionerInterpCount(t *testing.T) { addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), } - provisioners := map[string]ProvisionerFactory{ + provisioners := map[string]provisioners.Factory{ "local-exec": testProvisionerFuncFixed(pr), } ctx := testContext2(t, &ContextOpts{ @@ -2319,8 +2372,8 @@ func TestContext2Apply_provisionerInterpCount(t *testing.T) { func TestContext2Apply_foreachVariable(t *testing.T) { m := testModule(t, "plan-for-each-unknown-value") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -2352,8 +2405,8 @@ func TestContext2Apply_foreachVariable(t *testing.T) { func TestContext2Apply_moduleBasic(t *testing.T) { m := testModule(t, "apply-module") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -2402,7 +2455,7 @@ func TestContext2Apply_moduleDestroyOrder(t *testing.T) { return resp } - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -2412,7 +2465,7 @@ func TestContext2Apply_moduleDestroyOrder(t *testing.T) { }, }, }, - } + }) state := states.NewState() child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) @@ -2440,8 +2493,8 @@ func TestContext2Apply_moduleDestroyOrder(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, }) if _, diags := ctx.Plan(); diags.HasErrors() { @@ -2470,10 +2523,10 @@ func TestContext2Apply_moduleDestroyOrder(t *testing.T) { func TestContext2Apply_moduleInheritAlias(t *testing.T) { m := testModule(t, "apply-module-provider-inherit-alias") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { val := req.Config.GetAttr("value") if val.IsNull() { return @@ -2521,9 +2574,9 @@ func TestContext2Apply_orphanResource(t *testing.T) { // 2. Apply an empty configuration against the same state, which should // then clean up both the instances and the containing resource objects. p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ + p.ApplyResourceChangeFn = testApplyFn + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test_thing": { Attributes: map[string]*configschema.Attribute{ @@ -2532,7 +2585,7 @@ func TestContext2Apply_orphanResource(t *testing.T) { }, }, }, - } + }) // Step 1: create the resources and instances m := testModule(t, "apply-orphan-resource") @@ -2596,10 +2649,9 @@ func TestContext2Apply_orphanResource(t *testing.T) { func TestContext2Apply_moduleOrphanInheritAlias(t *testing.T) { m := testModule(t, "apply-module-provider-inherit-alias-orphan") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { val := req.Config.GetAttr("value") if val.IsNull() { return @@ -2642,7 +2694,7 @@ func TestContext2Apply_moduleOrphanInheritAlias(t *testing.T) { t.Fatalf("diags: %s", diags.Err()) } - if !p.ConfigureCalled { + if !p.ConfigureProviderCalled { t.Fatal("must call configure") } @@ -2652,10 +2704,9 @@ func TestContext2Apply_moduleOrphanInheritAlias(t *testing.T) { func TestContext2Apply_moduleOrphanProvider(t *testing.T) { m := testModule(t, "apply-module-orphan-provider-inherit") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { val := req.Config.GetAttr("value") if val.IsNull() { resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found")) @@ -2696,10 +2747,9 @@ func TestContext2Apply_moduleOrphanProvider(t *testing.T) { func TestContext2Apply_moduleOrphanGrandchildProvider(t *testing.T) { m := testModule(t, "apply-module-orphan-provider-inherit") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { val := req.Config.GetAttr("value") if val.IsNull() { resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found")) @@ -2740,12 +2790,11 @@ func TestContext2Apply_moduleOrphanGrandchildProvider(t *testing.T) { func TestContext2Apply_moduleGrandchildProvider(t *testing.T) { m := testModule(t, "apply-module-grandchild-provider-inherit") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn var callLock sync.Mutex called := false - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { val := req.Config.GetAttr("value") if val.IsNull() { resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found")) @@ -2787,8 +2836,8 @@ func TestContext2Apply_moduleGrandchildProvider(t *testing.T) { func TestContext2Apply_moduleOnlyProvider(t *testing.T) { m := testModule(t, "apply-module-only-provider") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn pTest := testProvider("test") pTest.ApplyResourceChangeFn = testApplyFn pTest.PlanResourceChangeFn = testDiffFn @@ -2820,8 +2869,8 @@ func TestContext2Apply_moduleOnlyProvider(t *testing.T) { func TestContext2Apply_moduleProviderAlias(t *testing.T) { m := testModule(t, "apply-module-provider-alias") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -2848,7 +2897,6 @@ func TestContext2Apply_moduleProviderAlias(t *testing.T) { func TestContext2Apply_moduleProviderAliasTargets(t *testing.T) { m := testModule(t, "apply-module-provider-alias") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -2888,7 +2936,6 @@ func TestContext2Apply_moduleProviderAliasTargets(t *testing.T) { func TestContext2Apply_moduleProviderCloseNested(t *testing.T) { m := testModule(t, "apply-module-provider-close-nested") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -2906,8 +2953,8 @@ func TestContext2Apply_moduleProviderCloseNested(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, }) if _, diags := ctx.Plan(); diags.HasErrors() { @@ -2927,8 +2974,8 @@ func TestContext2Apply_moduleProviderCloseNested(t *testing.T) { func TestContext2Apply_moduleVarRefExisting(t *testing.T) { m := testModule(t, "apply-ref-existing") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) root.SetResourceInstanceCurrent( @@ -2967,7 +3014,6 @@ func TestContext2Apply_moduleVarRefExisting(t *testing.T) { func TestContext2Apply_moduleVarResourceCount(t *testing.T) { m := testModule(t, "apply-module-var-resource-count") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -2980,7 +3026,7 @@ func TestContext2Apply_moduleVarResourceCount(t *testing.T) { SourceType: ValueFromCaller, }, }, - Destroy: true, + PlanMode: plans.DestroyMode, }) if _, diags := ctx.Plan(); diags.HasErrors() { @@ -3017,8 +3063,8 @@ func TestContext2Apply_moduleVarResourceCount(t *testing.T) { func TestContext2Apply_moduleBool(t *testing.T) { m := testModule(t, "apply-module-bool") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -3047,8 +3093,8 @@ func TestContext2Apply_moduleBool(t *testing.T) { func TestContext2Apply_moduleTarget(t *testing.T) { m := testModule(t, "plan-targeted-cross-module") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -3095,8 +3141,8 @@ module.B: func TestContext2Apply_multiProvider(t *testing.T) { m := testModule(t, "apply-multi-provider") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn pDO := testProvider("do") pDO.ApplyResourceChangeFn = testApplyFn @@ -3134,9 +3180,8 @@ func TestContext2Apply_multiProvider(t *testing.T) { func TestContext2Apply_multiProviderDestroy(t *testing.T) { m := testModule(t, "apply-multi-provider-destroy") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "addr": {Type: cty.String, Optional: true}, @@ -3150,12 +3195,12 @@ func TestContext2Apply_multiProviderDestroy(t *testing.T) { }, }, }, - } + }) p2 := testProvider("vault") p2.ApplyResourceChangeFn = testApplyFn p2.PlanResourceChangeFn = testDiffFn - p2.GetSchemaReturn = &ProviderSchema{ + p2.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "vault_instance": { Attributes: map[string]*configschema.Attribute{ @@ -3163,7 +3208,7 @@ func TestContext2Apply_multiProviderDestroy(t *testing.T) { }, }, }, - } + }) var state *states.State @@ -3221,9 +3266,9 @@ func TestContext2Apply_multiProviderDestroy(t *testing.T) { p2.ApplyResourceChangeFn = applyFn ctx := testContext2(t, &ContextOpts{ - Destroy: true, - State: state, - Config: m, + PlanMode: plans.DestroyMode, + State: state, + Config: m, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), addrs.NewDefaultProvider("vault"): testProviderFuncFixed(p2), @@ -3255,9 +3300,8 @@ func TestContext2Apply_multiProviderDestroy(t *testing.T) { func TestContext2Apply_multiProviderDestroyChild(t *testing.T) { m := testModule(t, "apply-multi-provider-destroy-child") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "value": {Type: cty.String, Optional: true}, @@ -3271,12 +3315,12 @@ func TestContext2Apply_multiProviderDestroyChild(t *testing.T) { }, }, }, - } + }) p2 := testProvider("vault") p2.ApplyResourceChangeFn = testApplyFn p2.PlanResourceChangeFn = testDiffFn - p2.GetSchemaReturn = &ProviderSchema{ + p2.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{}, ResourceTypes: map[string]*configschema.Block{ "vault_instance": { @@ -3285,7 +3329,7 @@ func TestContext2Apply_multiProviderDestroyChild(t *testing.T) { }, }, }, - } + }) var state *states.State @@ -3343,9 +3387,9 @@ func TestContext2Apply_multiProviderDestroyChild(t *testing.T) { p2.ApplyResourceChangeFn = applyFn ctx := testContext2(t, &ContextOpts{ - Destroy: true, - State: state, - Config: m, + PlanMode: plans.DestroyMode, + State: state, + Config: m, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), addrs.NewDefaultProvider("vault"): testProviderFuncFixed(p2), @@ -3376,7 +3420,6 @@ func TestContext2Apply_multiProviderDestroyChild(t *testing.T) { func TestContext2Apply_multiVar(t *testing.T) { m := testModule(t, "apply-multi-var") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn // First, apply with a count of 3 @@ -3458,11 +3501,10 @@ func TestContext2Apply_multiVarComprehensive(t *testing.T) { m := testModule(t, "apply-multi-var-comprehensive") p := testProvider("test") - configs := map[string]*ResourceConfig{} + configs := map[string]cty.Value{} var configsLock sync.Mutex p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { proposed := req.ProposedNewState configsLock.Lock() @@ -3472,7 +3514,7 @@ func TestContext2Apply_multiVarComprehensive(t *testing.T) { // and so the assertions below expect an old-style ResourceConfig, which // we'll construct via our shim for now to avoid rewriting all of the // assertions. - configs[key] = NewResourceConfigShimmed(req.Config, p.GetSchemaReturn.ResourceTypes["test_thing"]) + configs[key] = req.ProposedNewState retVals := make(map[string]cty.Value) for it := proposed.ElementIterator(); it.Next(); { @@ -3494,7 +3536,7 @@ func TestContext2Apply_multiVarComprehensive(t *testing.T) { } } - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test_thing": { Attributes: map[string]*configschema.Attribute{ @@ -3516,7 +3558,7 @@ func TestContext2Apply_multiVarComprehensive(t *testing.T) { }, }, }, - } + }) // First, apply with a count of 3 ctx := testContext2(t, &ContextOpts{ @@ -3537,102 +3579,99 @@ func TestContext2Apply_multiVarComprehensive(t *testing.T) { t.Fatalf("errors during plan") } - checkConfig := func(key string, want map[string]interface{}) { + checkConfig := func(key string, want cty.Value) { configsLock.Lock() defer configsLock.Unlock() - if _, ok := configs[key]; !ok { + got, ok := configs[key] + if !ok { t.Errorf("no config recorded for %s; expected a configuration", key) return } - got := configs[key].Config + t.Run("config for "+key, func(t *testing.T) { - want["key"] = key // to avoid doing this for every example for _, problem := range deep.Equal(got, want) { t.Errorf(problem) } }) } - checkConfig("multi_count_var.0", map[string]interface{}{ - "source_id": hcl2shim.UnknownVariableValue, - "source_name": "source.0", - }) - checkConfig("multi_count_var.2", map[string]interface{}{ - "source_id": hcl2shim.UnknownVariableValue, - "source_name": "source.2", - }) - checkConfig("multi_count_derived.0", map[string]interface{}{ - "source_id": hcl2shim.UnknownVariableValue, - "source_name": "source.0", - }) - checkConfig("multi_count_derived.2", map[string]interface{}{ - "source_id": hcl2shim.UnknownVariableValue, - "source_name": "source.2", - }) - checkConfig("whole_splat", map[string]interface{}{ - "source_ids": []interface{}{ - hcl2shim.UnknownVariableValue, - hcl2shim.UnknownVariableValue, - hcl2shim.UnknownVariableValue, - }, - "source_names": []interface{}{ - "source.0", - "source.1", - "source.2", - }, - "source_ids_from_func": hcl2shim.UnknownVariableValue, - "source_names_from_func": []interface{}{ - "source.0", - "source.1", - "source.2", - }, - - "source_ids_wrapped": []interface{}{ - []interface{}{ - hcl2shim.UnknownVariableValue, - hcl2shim.UnknownVariableValue, - hcl2shim.UnknownVariableValue, - }, - }, - "source_names_wrapped": []interface{}{ - []interface{}{ - "source.0", - "source.1", - "source.2", - }, - }, - - "first_source_id": hcl2shim.UnknownVariableValue, - "first_source_name": "source.0", - }) - checkConfig("child.whole_splat", map[string]interface{}{ - "source_ids": []interface{}{ - hcl2shim.UnknownVariableValue, - hcl2shim.UnknownVariableValue, - hcl2shim.UnknownVariableValue, - }, - "source_names": []interface{}{ - "source.0", - "source.1", - "source.2", - }, - - "source_ids_wrapped": []interface{}{ - []interface{}{ - hcl2shim.UnknownVariableValue, - hcl2shim.UnknownVariableValue, - hcl2shim.UnknownVariableValue, - }, - }, - "source_names_wrapped": []interface{}{ - []interface{}{ - "source.0", - "source.1", - "source.2", - }, - }, - }) + checkConfig("multi_count_var.0", cty.ObjectVal(map[string]cty.Value{ + "source_id": cty.UnknownVal(cty.String), + "source_name": cty.StringVal("source.0"), + })) + checkConfig("multi_count_var.2", cty.ObjectVal(map[string]cty.Value{ + "source_id": cty.UnknownVal(cty.String), + "source_name": cty.StringVal("source.2"), + })) + checkConfig("multi_count_derived.0", cty.ObjectVal(map[string]cty.Value{ + "source_id": cty.UnknownVal(cty.String), + "source_name": cty.StringVal("source.0"), + })) + checkConfig("multi_count_derived.2", cty.ObjectVal(map[string]cty.Value{ + "source_id": cty.UnknownVal(cty.String), + "source_name": cty.StringVal("source.2"), + })) + checkConfig("whole_splat", cty.ObjectVal(map[string]cty.Value{ + "source_ids": cty.ListVal([]cty.Value{ + cty.UnknownVal(cty.String), + cty.UnknownVal(cty.String), + cty.UnknownVal(cty.String), + }), + "source_names": cty.ListVal([]cty.Value{ + cty.StringVal("source.0"), + cty.StringVal("source.1"), + cty.StringVal("source.2"), + }), + "source_ids_from_func": cty.UnknownVal(cty.String), + "source_names_from_func": cty.ListVal([]cty.Value{ + cty.StringVal("source.0"), + cty.StringVal("source.1"), + cty.StringVal("source.2"), + }), + "source_ids_wrapped": cty.ListVal([]cty.Value{ + cty.ListVal([]cty.Value{ + cty.UnknownVal(cty.String), + cty.UnknownVal(cty.String), + cty.UnknownVal(cty.String), + }), + }), + "source_names_wrapped": cty.ListVal([]cty.Value{ + cty.ListVal([]cty.Value{ + cty.StringVal("source.0"), + cty.StringVal("source.1"), + cty.StringVal("source.2"), + }), + }), + "first_source_id": cty.UnknownVal(cty.String), + "first_source_name": cty.StringVal("source.0"), + })) + checkConfig("child.whole_splat", cty.ObjectVal(map[string]cty.Value{ + "source_ids": cty.ListVal([]cty.Value{ + cty.UnknownVal(cty.String), + cty.UnknownVal(cty.String), + cty.UnknownVal(cty.String), + }), + "source_names": cty.ListVal([]cty.Value{ + cty.StringVal("source.0"), + cty.StringVal("source.1"), + cty.StringVal("source.2"), + }), + "source_ids_wrapped": cty.ListVal([]cty.Value{ + cty.ListVal([]cty.Value{ + cty.UnknownVal(cty.String), + cty.UnknownVal(cty.String), + cty.UnknownVal(cty.String), + }), + }), + "source_names_wrapped": cty.ListVal([]cty.Value{ + cty.ListVal([]cty.Value{ + cty.StringVal("source.0"), + cty.StringVal("source.1"), + cty.StringVal("source.2"), + }), + }), + })) t.Run("apply", func(t *testing.T) { state, diags := ctx.Apply() @@ -3666,7 +3705,6 @@ func TestContext2Apply_multiVarComprehensive(t *testing.T) { func TestContext2Apply_multiVarOrder(t *testing.T) { m := testModule(t, "apply-multi-var-order") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn // First, apply with a count of 3 @@ -3700,7 +3738,6 @@ func TestContext2Apply_multiVarOrder(t *testing.T) { func TestContext2Apply_multiVarOrderInterp(t *testing.T) { m := testModule(t, "apply-multi-var-order-interp") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn // First, apply with a count of 3 @@ -3738,8 +3775,8 @@ func TestContext2Apply_multiVarCountDec(t *testing.T) { { m := testModule(t, "apply-multi-var-count-dec") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -3845,9 +3882,8 @@ func TestContext2Apply_multiVarCountDec(t *testing.T) { func TestContext2Apply_multiVarMissingState(t *testing.T) { m := testModule(t, "apply-multi-var-missing-state") p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test_thing": { Attributes: map[string]*configschema.Attribute{ @@ -3856,7 +3892,7 @@ func TestContext2Apply_multiVarMissingState(t *testing.T) { }, }, }, - } + }) // First, apply with a count of 3 ctx := testContext2(t, &ContextOpts{ @@ -3881,7 +3917,6 @@ func TestContext2Apply_multiVarMissingState(t *testing.T) { func TestContext2Apply_outputOrphan(t *testing.T) { m := testModule(t, "apply-output-orphan") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() @@ -3916,7 +3951,6 @@ func TestContext2Apply_outputOrphan(t *testing.T) { func TestContext2Apply_outputOrphanModule(t *testing.T) { m := testModule(t, "apply-output-orphan-module") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() @@ -3971,7 +4005,6 @@ func TestContext2Apply_outputOrphanModule(t *testing.T) { func TestContext2Apply_providerComputedVar(t *testing.T) { m := testModule(t, "apply-provider-computed") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn pTest := testProvider("test") @@ -3986,7 +4019,7 @@ func TestContext2Apply_providerComputedVar(t *testing.T) { }, }) - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { val := req.Config.GetAttr("value") if val.IsNull() { resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found")) @@ -4007,10 +4040,9 @@ func TestContext2Apply_providerComputedVar(t *testing.T) { func TestContext2Apply_providerConfigureDisabled(t *testing.T) { m := testModule(t, "apply-provider-configure-disabled") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { val := req.Config.GetAttr("value") if val.IsNull() { resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found")) @@ -4034,7 +4066,7 @@ func TestContext2Apply_providerConfigureDisabled(t *testing.T) { t.Fatalf("apply errors: %s", diags.Err()) } - if !p.ConfigureCalled { + if !p.ConfigureProviderCalled { t.Fatal("configure never called") } } @@ -4043,8 +4075,8 @@ func TestContext2Apply_provisionerModule(t *testing.T) { m := testModule(t, "apply-provisioner-module") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn pr := testProvisioner() pr.GetSchemaResponse = provisioners.GetSchemaResponse{ @@ -4060,7 +4092,7 @@ func TestContext2Apply_provisionerModule(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -4090,8 +4122,8 @@ func TestContext2Apply_Provisioner_compute(t *testing.T) { m := testModule(t, "apply-provisioner-compute") p := testProvider("aws") pr := testProvisioner() - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { val := req.Config.GetAttr("command").AsString() @@ -4109,7 +4141,7 @@ func TestContext2Apply_Provisioner_compute(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, Variables: InputValues{ @@ -4167,7 +4199,7 @@ func TestContext2Apply_provisionerCreateFail(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -4204,7 +4236,7 @@ func TestContext2Apply_provisionerCreateFailNoId(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -4228,9 +4260,9 @@ func TestContext2Apply_provisionerCreateFailNoId(t *testing.T) { func TestContext2Apply_provisionerFail(t *testing.T) { m := testModule(t, "apply-provisioner-fail") p := testProvider("aws") - pr := testProvisioner() p.PlanResourceChangeFn = testDiffFn p.ApplyResourceChangeFn = testApplyFn + pr := testProvisioner() pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("EXPLOSION")) return @@ -4241,7 +4273,7 @@ func TestContext2Apply_provisionerFail(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -4266,8 +4298,8 @@ func TestContext2Apply_provisionerFail_createBeforeDestroy(t *testing.T) { m := testModule(t, "apply-provisioner-fail-create-before") p := testProvider("aws") pr := testProvisioner() - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("EXPLOSION")) return @@ -4289,7 +4321,7 @@ func TestContext2Apply_provisionerFail_createBeforeDestroy(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, State: state, @@ -4414,7 +4446,7 @@ func TestContext2Apply_multiDepose_createBeforeDestroy(t *testing.T) { m := testModule(t, "apply-multi-depose-create-before-destroy") p := testProvider("aws") ps := map[addrs.Provider]providers.Factory{addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p)} - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -4423,7 +4455,7 @@ func TestContext2Apply_multiDepose_createBeforeDestroy(t *testing.T) { }, }, }, - } + }) state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -4627,8 +4659,8 @@ func TestContext2Apply_provisionerFailContinue(t *testing.T) { m := testModule(t, "apply-provisioner-fail-continue") p := testProvider("aws") pr := testProvisioner() - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provisioner error")) @@ -4640,7 +4672,7 @@ func TestContext2Apply_provisionerFailContinue(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -4675,7 +4707,6 @@ func TestContext2Apply_provisionerFailContinueHook(t *testing.T) { m := testModule(t, "apply-provisioner-fail-continue") p := testProvider("aws") pr := testProvisioner() - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provisioner error")) @@ -4688,7 +4719,7 @@ func TestContext2Apply_provisionerFailContinueHook(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -4713,7 +4744,6 @@ func TestContext2Apply_provisionerDestroy(t *testing.T) { m := testModule(t, "apply-provisioner-destroy") p := testProvider("aws") pr := testProvisioner() - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { val := req.Config.GetAttr("command").AsString() @@ -4736,13 +4766,13 @@ func TestContext2Apply_provisionerDestroy(t *testing.T) { ) ctx := testContext2(t, &ContextOpts{ - Config: m, - State: state, - Destroy: true, + Config: m, + State: state, + PlanMode: plans.DestroyMode, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -4769,7 +4799,6 @@ func TestContext2Apply_provisionerDestroyFail(t *testing.T) { m := testModule(t, "apply-provisioner-destroy") p := testProvider("aws") pr := testProvisioner() - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provisioner error")) @@ -4788,13 +4817,13 @@ func TestContext2Apply_provisionerDestroyFail(t *testing.T) { ) ctx := testContext2(t, &ContextOpts{ - Config: m, - State: state, - Destroy: true, + Config: m, + State: state, + PlanMode: plans.DestroyMode, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -4827,7 +4856,6 @@ func TestContext2Apply_provisionerDestroyFailContinue(t *testing.T) { m := testModule(t, "apply-provisioner-destroy-continue") p := testProvider("aws") pr := testProvisioner() - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn var l sync.Mutex @@ -4857,13 +4885,13 @@ func TestContext2Apply_provisionerDestroyFailContinue(t *testing.T) { ) ctx := testContext2(t, &ContextOpts{ - Config: m, - State: state, - Destroy: true, + Config: m, + State: state, + PlanMode: plans.DestroyMode, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -4897,7 +4925,6 @@ func TestContext2Apply_provisionerDestroyFailContinueFail(t *testing.T) { m := testModule(t, "apply-provisioner-destroy-fail") p := testProvider("aws") pr := testProvisioner() - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn var l sync.Mutex @@ -4927,13 +4954,13 @@ func TestContext2Apply_provisionerDestroyFailContinueFail(t *testing.T) { ) ctx := testContext2(t, &ContextOpts{ - Config: m, - State: state, - Destroy: true, + Config: m, + State: state, + PlanMode: plans.DestroyMode, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -4969,8 +4996,8 @@ func TestContext2Apply_provisionerDestroyTainted(t *testing.T) { m := testModule(t, "apply-provisioner-destroy") p := testProvider("aws") pr := testProvisioner() - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn destroyCalled := false pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { @@ -5000,7 +5027,7 @@ func TestContext2Apply_provisionerDestroyTainted(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, Variables: InputValues{ @@ -5043,8 +5070,8 @@ aws_instance.foo["a"]: func TestContext2Apply_provisionerResourceRef(t *testing.T) { m := testModule(t, "apply-provisioner-resource-ref") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn pr := testProvisioner() pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { @@ -5061,7 +5088,7 @@ func TestContext2Apply_provisionerResourceRef(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -5091,8 +5118,8 @@ func TestContext2Apply_provisionerSelfRef(t *testing.T) { m := testModule(t, "apply-provisioner-self-ref") p := testProvider("aws") pr := testProvisioner() - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { val := req.Config.GetAttr("command") if val.AsString() != "bar" { @@ -5107,7 +5134,7 @@ func TestContext2Apply_provisionerSelfRef(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -5140,8 +5167,8 @@ func TestContext2Apply_provisionerMultiSelfRef(t *testing.T) { m := testModule(t, "apply-provisioner-multi-self-ref") p := testProvider("aws") pr := testProvisioner() - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { lock.Lock() defer lock.Unlock() @@ -5160,7 +5187,7 @@ func TestContext2Apply_provisionerMultiSelfRef(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -5200,8 +5227,8 @@ func TestContext2Apply_provisionerMultiSelfRefSingle(t *testing.T) { m := testModule(t, "apply-provisioner-multi-self-ref-single") p := testProvider("aws") pr := testProvisioner() - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { lock.Lock() defer lock.Unlock() @@ -5220,7 +5247,7 @@ func TestContext2Apply_provisionerMultiSelfRefSingle(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -5257,7 +5284,6 @@ func TestContext2Apply_provisionerExplicitSelfRef(t *testing.T) { m := testModule(t, "apply-provisioner-explicit-self-ref") p := testProvider("aws") pr := testProvisioner() - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { val := req.Config.GetAttr("command") @@ -5275,7 +5301,7 @@ func TestContext2Apply_provisionerExplicitSelfRef(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -5298,13 +5324,13 @@ func TestContext2Apply_provisionerExplicitSelfRef(t *testing.T) { { ctx := testContext2(t, &ContextOpts{ - Config: m, - Destroy: true, - State: state, + Config: m, + PlanMode: plans.DestroyMode, + State: state, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -5327,7 +5353,6 @@ func TestContext2Apply_provisionerForEachSelfRef(t *testing.T) { m := testModule(t, "apply-provisioner-for-each-self") p := testProvider("aws") pr := testProvisioner() - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { @@ -5344,7 +5369,7 @@ func TestContext2Apply_provisionerForEachSelfRef(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -5364,14 +5389,14 @@ func TestContext2Apply_Provisioner_Diff(t *testing.T) { m := testModule(t, "apply-provisioner-diff") p := testProvider("aws") pr := testProvisioner() - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -5419,7 +5444,7 @@ func TestContext2Apply_Provisioner_Diff(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, State: state, @@ -5470,7 +5495,6 @@ func TestContext2Apply_outputDiffVars(t *testing.T) { State: state, }) - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn //func(info *InstanceInfo, s *InstanceState, rc *ResourceConfig) (*InstanceDiff, error) { // d := &InstanceDiff{ @@ -5513,7 +5537,6 @@ func TestContext2Apply_destroyX(t *testing.T) { m := testModule(t, "apply-destroy") h := new(HookRecordApplyOrder) p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -5536,10 +5559,10 @@ func TestContext2Apply_destroyX(t *testing.T) { // Next, plan and apply a destroy operation h.Active = true ctx = testContext2(t, &ContextOpts{ - Destroy: true, - State: state, - Config: m, - Hooks: []Hook{h}, + PlanMode: plans.DestroyMode, + State: state, + Config: m, + Hooks: []Hook{h}, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, @@ -5573,7 +5596,6 @@ func TestContext2Apply_destroyOrder(t *testing.T) { m := testModule(t, "apply-destroy") h := new(HookRecordApplyOrder) p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -5598,10 +5620,10 @@ func TestContext2Apply_destroyOrder(t *testing.T) { // Next, plan and apply a destroy h.Active = true ctx = testContext2(t, &ContextOpts{ - Destroy: true, - State: state, - Config: m, - Hooks: []Hook{h}, + PlanMode: plans.DestroyMode, + State: state, + Config: m, + Hooks: []Hook{h}, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, @@ -5636,7 +5658,6 @@ func TestContext2Apply_destroyModulePrefix(t *testing.T) { m := testModule(t, "apply-destroy-module-resource-prefix") h := new(MockHook) p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -5664,10 +5685,10 @@ func TestContext2Apply_destroyModulePrefix(t *testing.T) { // Next, plan and apply a destroy operation and reset the hook h = new(MockHook) ctx = testContext2(t, &ContextOpts{ - Destroy: true, - State: state, - Config: m, - Hooks: []Hook{h}, + PlanMode: plans.DestroyMode, + State: state, + Config: m, + Hooks: []Hook{h}, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, @@ -5677,7 +5698,7 @@ func TestContext2Apply_destroyModulePrefix(t *testing.T) { t.Fatalf("plan errors: %s", diags.Err()) } - state, diags = ctx.Apply() + _, diags = ctx.Apply() if diags.HasErrors() { t.Fatalf("diags: %s", diags.Err()) } @@ -5691,7 +5712,6 @@ func TestContext2Apply_destroyModulePrefix(t *testing.T) { func TestContext2Apply_destroyNestedModule(t *testing.T) { m := testModule(t, "apply-destroy-nested-module") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() @@ -5733,7 +5753,6 @@ func TestContext2Apply_destroyNestedModule(t *testing.T) { func TestContext2Apply_destroyDeeplyNestedModule(t *testing.T) { m := testModule(t, "apply-destroy-deeply-nested-module") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() @@ -5775,7 +5794,6 @@ func TestContext2Apply_destroyDeeplyNestedModule(t *testing.T) { func TestContext2Apply_destroyModuleWithAttrsReferencingResource(t *testing.T) { m, snap := testModuleWithSnapshot(t, "apply-destroy-module-with-attrs") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn var state *states.State @@ -5808,10 +5826,10 @@ func TestContext2Apply_destroyModuleWithAttrsReferencingResource(t *testing.T) { { ctx := testContext2(t, &ContextOpts{ - Destroy: true, - Config: m, - State: state, - Hooks: []Hook{h}, + PlanMode: plans.DestroyMode, + Config: m, + State: state, + Hooks: []Hook{h}, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, @@ -5856,7 +5874,6 @@ func TestContext2Apply_destroyModuleWithAttrsReferencingResource(t *testing.T) { func TestContext2Apply_destroyWithModuleVariableAndCount(t *testing.T) { m, snap := testModuleWithSnapshot(t, "apply-destroy-mod-var-and-count") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn var state *states.State @@ -5885,10 +5902,10 @@ func TestContext2Apply_destroyWithModuleVariableAndCount(t *testing.T) { { ctx := testContext2(t, &ContextOpts{ - Destroy: true, - Config: m, - State: state, - Hooks: []Hook{h}, + PlanMode: plans.DestroyMode, + Config: m, + State: state, + Hooks: []Hook{h}, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, @@ -5933,7 +5950,6 @@ func TestContext2Apply_destroyWithModuleVariableAndCount(t *testing.T) { func TestContext2Apply_destroyTargetWithModuleVariableAndCount(t *testing.T) { m := testModule(t, "apply-destroy-mod-var-and-count") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn var state *states.State @@ -5959,9 +5975,9 @@ func TestContext2Apply_destroyTargetWithModuleVariableAndCount(t *testing.T) { { ctx := testContext2(t, &ContextOpts{ - Destroy: true, - Config: m, - State: state, + PlanMode: plans.DestroyMode, + Config: m, + State: state, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, @@ -6012,7 +6028,6 @@ func TestContext2Apply_destroyTargetWithModuleVariableAndCount(t *testing.T) { func TestContext2Apply_destroyWithModuleVariableAndCountNested(t *testing.T) { m, snap := testModuleWithSnapshot(t, "apply-destroy-mod-var-and-count-nested") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn var state *states.State @@ -6041,10 +6056,10 @@ func TestContext2Apply_destroyWithModuleVariableAndCountNested(t *testing.T) { { ctx := testContext2(t, &ContextOpts{ - Destroy: true, - Config: m, - State: state, - Hooks: []Hook{h}, + PlanMode: plans.DestroyMode, + Config: m, + State: state, + Hooks: []Hook{h}, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, @@ -6089,7 +6104,6 @@ func TestContext2Apply_destroyWithModuleVariableAndCountNested(t *testing.T) { func TestContext2Apply_destroyOutputs(t *testing.T) { m := testModule(t, "apply-destroy-outputs") p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { @@ -6122,9 +6136,9 @@ func TestContext2Apply_destroyOutputs(t *testing.T) { // Next, plan and apply a destroy operation ctx = testContext2(t, &ContextOpts{ - Destroy: true, - State: state, - Config: m, + PlanMode: plans.DestroyMode, + State: state, + Config: m, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), }, @@ -6146,9 +6160,9 @@ func TestContext2Apply_destroyOutputs(t *testing.T) { // destroying again should produce no errors ctx = testContext2(t, &ContextOpts{ - Destroy: true, - State: state, - Config: m, + PlanMode: plans.DestroyMode, + State: state, + Config: m, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), }, @@ -6183,7 +6197,6 @@ func TestContext2Apply_destroyOrphan(t *testing.T) { State: state, }) - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn if _, diags := ctx.Plan(); diags.HasErrors() { @@ -6205,7 +6218,6 @@ func TestContext2Apply_destroyTaintedProvisioner(t *testing.T) { m := testModule(t, "apply-destroy-provisioner") p := testProvider("aws") pr := testProvisioner() - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() @@ -6224,11 +6236,11 @@ func TestContext2Apply_destroyTaintedProvisioner(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, }) if _, diags := ctx.Plan(); diags.HasErrors() { @@ -6295,7 +6307,7 @@ func TestContext2Apply_errorDestroy(t *testing.T) { m := testModule(t, "empty") p := testProvider("test") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test_thing": { Attributes: map[string]*configschema.Attribute{ @@ -6303,7 +6315,7 @@ func TestContext2Apply_errorDestroy(t *testing.T) { }, }, }, - } + }) p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { // Should actually be called for this test, because Terraform Core // constructs the plan for a destroy operation itself. @@ -6368,7 +6380,7 @@ func TestContext2Apply_errorCreateInvalidNew(t *testing.T) { m := testModule(t, "apply-error") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -6377,7 +6389,7 @@ func TestContext2Apply_errorCreateInvalidNew(t *testing.T) { }, }, }, - } + }) p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { return providers.PlanResourceChangeResponse{ PlannedState: req.ProposedNewState, @@ -6432,7 +6444,7 @@ func TestContext2Apply_errorUpdateNullNew(t *testing.T) { m := testModule(t, "apply-error") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -6441,7 +6453,7 @@ func TestContext2Apply_errorUpdateNullNew(t *testing.T) { }, }, }, - } + }) p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { return providers.PlanResourceChangeResponse{ PlannedState: req.ProposedNewState, @@ -6575,7 +6587,6 @@ func TestContext2Apply_hook(t *testing.T) { m := testModule(t, "apply-good") h := new(MockHook) p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -6608,7 +6619,6 @@ func TestContext2Apply_hookOrphan(t *testing.T) { m := testModule(t, "apply-blank") h := new(MockHook) p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() @@ -6660,8 +6670,8 @@ func TestContext2Apply_idAttr(t *testing.T) { }, }) - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn if _, diags := ctx.Plan(); diags.HasErrors() { t.Fatalf("plan errors: %s", diags.Err()) @@ -6690,8 +6700,8 @@ func TestContext2Apply_idAttr(t *testing.T) { func TestContext2Apply_outputBasic(t *testing.T) { m := testModule(t, "apply-output") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -6768,8 +6778,8 @@ func TestContext2Apply_outputAdd(t *testing.T) { func TestContext2Apply_outputList(t *testing.T) { m := testModule(t, "apply-output-list") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -6796,8 +6806,8 @@ func TestContext2Apply_outputList(t *testing.T) { func TestContext2Apply_outputMulti(t *testing.T) { m := testModule(t, "apply-output-multi") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -6824,8 +6834,8 @@ func TestContext2Apply_outputMulti(t *testing.T) { func TestContext2Apply_outputMultiIndex(t *testing.T) { m := testModule(t, "apply-output-multi-index") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -6913,8 +6923,8 @@ func TestContext2Apply_taintX(t *testing.T) { func TestContext2Apply_taintDep(t *testing.T) { m := testModule(t, "apply-taint-dep") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -6965,8 +6975,8 @@ func TestContext2Apply_taintDep(t *testing.T) { func TestContext2Apply_taintDepRequiresNew(t *testing.T) { m := testModule(t, "apply-taint-dep-requires-new") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -7017,8 +7027,8 @@ func TestContext2Apply_taintDepRequiresNew(t *testing.T) { func TestContext2Apply_targeted(t *testing.T) { m := testModule(t, "apply-targeted") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -7057,8 +7067,8 @@ aws_instance.foo: func TestContext2Apply_targetedCount(t *testing.T) { m := testModule(t, "apply-targeted-count") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -7099,8 +7109,8 @@ aws_instance.foo.2: func TestContext2Apply_targetedCountIndex(t *testing.T) { m := testModule(t, "apply-targeted-count") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -7133,7 +7143,6 @@ aws_instance.foo.1: func TestContext2Apply_targetedDestroy(t *testing.T) { m := testModule(t, "destroy-targeted") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() @@ -7169,7 +7178,7 @@ func TestContext2Apply_targetedDestroy(t *testing.T) { addrs.ManagedResourceMode, "aws_instance", "a", ), }, - Destroy: true, + PlanMode: plans.DestroyMode, }) if diags := ctx.Validate(); diags.HasErrors() { @@ -7215,7 +7224,6 @@ func TestContext2Apply_targetedDestroy(t *testing.T) { func TestContext2Apply_targetedDestroyCountDeps(t *testing.T) { m := testModule(t, "apply-destroy-targeted-count") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() @@ -7249,7 +7257,7 @@ func TestContext2Apply_targetedDestroyCountDeps(t *testing.T) { addrs.ManagedResourceMode, "aws_instance", "foo", ), }, - Destroy: true, + PlanMode: plans.DestroyMode, }) if _, diags := ctx.Plan(); diags.HasErrors() { @@ -7268,7 +7276,6 @@ func TestContext2Apply_targetedDestroyCountDeps(t *testing.T) { func TestContext2Apply_targetedDestroyModule(t *testing.T) { m := testModule(t, "apply-targeted-module") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() @@ -7318,7 +7325,7 @@ func TestContext2Apply_targetedDestroyModule(t *testing.T) { addrs.ManagedResourceMode, "aws_instance", "foo", ), }, - Destroy: true, + PlanMode: plans.DestroyMode, }) if _, diags := ctx.Plan(); diags.HasErrors() { @@ -7348,7 +7355,6 @@ module.child: func TestContext2Apply_targetedDestroyCountIndex(t *testing.T) { m := testModule(t, "apply-targeted-count") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn foo := &states.ResourceInstanceObjectSrc{ @@ -7407,7 +7413,7 @@ func TestContext2Apply_targetedDestroyCountIndex(t *testing.T) { addrs.ManagedResourceMode, "aws_instance", "bar", addrs.IntKey(1), ), }, - Destroy: true, + PlanMode: plans.DestroyMode, }) if _, diags := ctx.Plan(); diags.HasErrors() { @@ -7438,8 +7444,8 @@ aws_instance.foo.1: func TestContext2Apply_targetedModule(t *testing.T) { m := testModule(t, "apply-targeted-module") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -7487,8 +7493,8 @@ module.child: func TestContext2Apply_targetedModuleDep(t *testing.T) { m := testModule(t, "apply-targeted-module-dep") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -7539,8 +7545,8 @@ module.child: func TestContext2Apply_targetedModuleUnrelatedOutputs(t *testing.T) { m := testModule(t, "apply-targeted-module-unrelated-outputs") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn state := states.NewState() _ = state.EnsureModule(addrs.RootModuleInstance.Child("child2", addrs.NoKey)) @@ -7590,8 +7596,8 @@ module.child2: func TestContext2Apply_targetedModuleResource(t *testing.T) { m := testModule(t, "apply-targeted-module-resource") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -7632,7 +7638,6 @@ module.child: func TestContext2Apply_targetedResourceOrphanModule(t *testing.T) { m := testModule(t, "apply-targeted-resource-orphan-module") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() @@ -7671,7 +7676,6 @@ func TestContext2Apply_targetedResourceOrphanModule(t *testing.T) { func TestContext2Apply_unknownAttribute(t *testing.T) { m := testModule(t, "apply-unknown") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { resp = testDiffFn(req) planned := resp.PlannedState.AsValueMap() @@ -7679,8 +7683,9 @@ func TestContext2Apply_unknownAttribute(t *testing.T) { resp.PlannedState = cty.ObjectVal(planned) return resp } + p.ApplyResourceChangeFn = testApplyFn - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -7691,7 +7696,7 @@ func TestContext2Apply_unknownAttribute(t *testing.T) { }, }, }, - } + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -7719,7 +7724,6 @@ func TestContext2Apply_unknownAttribute(t *testing.T) { func TestContext2Apply_unknownAttributeInterpolate(t *testing.T) { m := testModule(t, "apply-unknown-interpolate") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -7837,8 +7841,8 @@ func TestContext2Apply_createBefore_depends(t *testing.T) { m := testModule(t, "apply-depends-create-before") h := new(HookRecordApplyOrder) p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) root.SetResourceInstanceCurrent( @@ -8026,7 +8030,7 @@ func TestContext2Apply_singleDestroy(t *testing.T) { } h.Active = true - state, diags := ctx.Apply() + _, diags := ctx.Apply() if diags.HasErrors() { t.Fatalf("diags: %s", diags.Err()) } @@ -8039,9 +8043,8 @@ func TestContext2Apply_singleDestroy(t *testing.T) { // GH-7824 func TestContext2Apply_issue7824(t *testing.T) { p := testProvider("template") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "template_file": { Attributes: map[string]*configschema.Attribute{ @@ -8050,7 +8053,7 @@ func TestContext2Apply_issue7824(t *testing.T) { }, }, }, - } + }) m, snap := testModuleWithSnapshot(t, "issue-7824") @@ -8095,9 +8098,9 @@ func TestContext2Apply_issue5254(t *testing.T) { // Create a provider. We use "template" here just to match the repro // we got from the issue itself. p := testProvider("template") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ + p.ApplyResourceChangeFn = testApplyFn + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "template_file": { Attributes: map[string]*configschema.Attribute{ @@ -8108,7 +8111,7 @@ func TestContext2Apply_issue5254(t *testing.T) { }, }, }, - } + }) // Apply cleanly step 0 ctx := testContext2(t, &ContextOpts{ @@ -8118,7 +8121,7 @@ func TestContext2Apply_issue5254(t *testing.T) { }, }) - plan, diags := ctx.Plan() + _, diags := ctx.Plan() if diags.HasErrors() { t.Fatalf("err: %s", diags.Err()) } @@ -8139,7 +8142,7 @@ func TestContext2Apply_issue5254(t *testing.T) { }, }) - plan, diags = ctx.Plan() + plan, diags := ctx.Plan() if diags.HasErrors() { t.Fatalf("err: %s", diags.Err()) } @@ -8261,10 +8264,10 @@ aws_instance.ifailedprovisioners: (tainted) func TestContext2Apply_ignoreChangesCreate(t *testing.T) { m := testModule(t, "apply-ignore-changes-create") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn - instanceSchema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + instanceSchema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block instanceSchema.Attributes["required_field"] = &configschema.Attribute{ Type: cty.String, Required: true, @@ -8310,7 +8313,6 @@ aws_instance.foo: func TestContext2Apply_ignoreChangesWithDep(t *testing.T) { m := testModule(t, "apply-ignore-changes-dep") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { resp.PlannedState = req.ProposedNewState @@ -8402,13 +8404,13 @@ func TestContext2Apply_ignoreChangesWithDep(t *testing.T) { } } -func TestContext2Apply_ignoreChangesWildcard(t *testing.T) { - m := testModule(t, "apply-ignore-changes-wildcard") +func TestContext2Apply_ignoreChangesAll(t *testing.T) { + m := testModule(t, "apply-ignore-changes-all") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn - instanceSchema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + instanceSchema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block instanceSchema.Attributes["required_field"] = &configschema.Attribute{ Type: cty.String, Required: true, @@ -8454,7 +8456,6 @@ aws_instance.foo: func TestContext2Apply_destroyNestedModuleWithAttrsReferencingResource(t *testing.T) { m, snap := testModuleWithSnapshot(t, "apply-destroy-nested-module-with-attrs") p := testProvider("null") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn var state *states.State @@ -8480,9 +8481,9 @@ func TestContext2Apply_destroyNestedModuleWithAttrsReferencingResource(t *testin { ctx := testContext2(t, &ContextOpts{ - Destroy: true, - Config: m, - State: state, + PlanMode: plans.DestroyMode, + Config: m, + State: state, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), }, @@ -8653,7 +8654,6 @@ resource "null_instance" "depends" { func TestContext2Apply_terraformWorkspace(t *testing.T) { m := testModule(t, "apply-terraform-workspace") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ @@ -8684,7 +8684,6 @@ func TestContext2Apply_terraformWorkspace(t *testing.T) { func TestContext2Apply_multiRef(t *testing.T) { m := testModule(t, "apply-multi-ref") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -8711,8 +8710,8 @@ func TestContext2Apply_multiRef(t *testing.T) { func TestContext2Apply_targetedModuleRecursive(t *testing.T) { m := testModule(t, "apply-targeted-module-recursive") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -8785,7 +8784,6 @@ result_3 = hello world func TestContext2Apply_destroyWithLocals(t *testing.T) { m := testModule(t, "apply-destroy-with-locals") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() @@ -8805,8 +8803,8 @@ func TestContext2Apply_destroyWithLocals(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, }) if _, diags := ctx.Plan(); diags.HasErrors() { @@ -8831,7 +8829,7 @@ func TestContext2Apply_providerWithLocals(t *testing.T) { providerRegion := "" // this should not be overridden during destroy - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { val := req.Config.GetAttr("region") if !val.IsNull() { providerRegion = val.AsString() @@ -8841,7 +8839,6 @@ func TestContext2Apply_providerWithLocals(t *testing.T) { } p.PlanResourceChangeFn = testDiffFn - p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -8863,8 +8860,8 @@ func TestContext2Apply_providerWithLocals(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, }) if _, diags := ctx.Plan(); diags.HasErrors() { @@ -8888,7 +8885,6 @@ func TestContext2Apply_providerWithLocals(t *testing.T) { func TestContext2Apply_destroyWithProviders(t *testing.T) { m := testModule(t, "destroy-module-with-provider") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() @@ -8907,8 +8903,8 @@ func TestContext2Apply_destroyWithProviders(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, }) // test that we can't destroy if the provider is missing @@ -8924,8 +8920,8 @@ func TestContext2Apply_destroyWithProviders(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, }) if _, diags := ctx.Plan(); diags.HasErrors() { @@ -9046,7 +9042,6 @@ func TestContext2Apply_plannedInterpolatedCount(t *testing.T) { m, snap := testModuleWithSnapshot(t, "apply-interpolated-count") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn Providers := map[addrs.Provider]providers.Factory{ @@ -9100,7 +9095,6 @@ func TestContext2Apply_plannedDestroyInterpolatedCount(t *testing.T) { m, snap := testModuleWithSnapshot(t, "plan-destroy-interpolated-count") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn providers := map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), @@ -9130,7 +9124,7 @@ func TestContext2Apply_plannedDestroyInterpolatedCount(t *testing.T) { Config: m, Providers: providers, State: state, - Destroy: true, + PlanMode: plans.DestroyMode, }) plan, diags := ctx.Plan() @@ -9166,7 +9160,6 @@ func TestContext2Apply_scaleInMultivarRef(t *testing.T) { m := testModule(t, "apply-resource-scale-in") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn Providers := map[addrs.Provider]providers.Factory{ @@ -9215,7 +9208,7 @@ func TestContext2Apply_scaleInMultivarRef(t *testing.T) { func TestContext2Apply_inconsistentWithPlan(t *testing.T) { m := testModule(t, "apply-inconsistent-with-plan") p := testProvider("test") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test": { Attributes: map[string]*configschema.Attribute{ @@ -9223,7 +9216,7 @@ func TestContext2Apply_inconsistentWithPlan(t *testing.T) { }, }, }, - } + }) p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { return providers.PlanResourceChangeResponse{ PlannedState: cty.ObjectVal(map[string]cty.Value{ @@ -9268,7 +9261,7 @@ func TestContext2Apply_inconsistentWithPlan(t *testing.T) { func TestContext2Apply_issue19908(t *testing.T) { m := testModule(t, "apply-issue19908") p := testProvider("test") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test": { Attributes: map[string]*configschema.Attribute{ @@ -9276,7 +9269,7 @@ func TestContext2Apply_issue19908(t *testing.T) { }, }, }, - } + }) p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { return providers.PlanResourceChangeResponse{ PlannedState: req.ProposedNewState, @@ -9349,7 +9342,7 @@ func TestContext2Apply_issue19908(t *testing.T) { func TestContext2Apply_invalidIndexRef(t *testing.T) { p := testProvider("test") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test_instance": { Attributes: map[string]*configschema.Attribute{ @@ -9357,7 +9350,7 @@ func TestContext2Apply_invalidIndexRef(t *testing.T) { }, }, }, - } + }) p.PlanResourceChangeFn = testDiffFn m := testModule(t, "apply-invalid-index") @@ -9398,7 +9391,6 @@ func TestContext2Apply_moduleReplaceCycle(t *testing.T) { p := testProvider("aws") p.PlanResourceChangeFn = testDiffFn - p.ApplyResourceChangeFn = testApplyFn instanceSchema := &configschema.Block{ Attributes: map[string]*configschema.Attribute{ @@ -9407,11 +9399,11 @@ func TestContext2Apply_moduleReplaceCycle(t *testing.T) { }, } - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": instanceSchema, }, - } + }) state := states.NewState() modA := state.EnsureModule(addrs.RootModuleInstance.Child("a", addrs.NoKey)) @@ -9536,8 +9528,18 @@ func TestContext2Apply_moduleReplaceCycle(t *testing.T) { func TestContext2Apply_destroyDataCycle(t *testing.T) { m, snap := testModuleWithSnapshot(t, "apply-destroy-data-cycle") p := testProvider("null") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { + return providers.ReadDataSourceResponse{ + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("new"), + "foo": cty.NullVal(cty.String), + }), + } + } + + tp := testProvider("test") + tp.PlanResourceChangeFn = testDiffFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -9556,6 +9558,31 @@ func TestContext2Apply_destroyDataCycle(t *testing.T) { Module: addrs.RootModule, }, ) + root.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_resource", + Name: "a", + }.Instance(addrs.IntKey(0)), + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"a"}`), + Dependencies: []addrs.ConfigResource{ + addrs.ConfigResource{ + Resource: addrs.Resource{ + Mode: addrs.DataResourceMode, + Type: "null_data_source", + Name: "d", + }, + Module: addrs.RootModule, + }, + }, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) root.SetResourceInstanceCurrent( addrs.Resource{ Mode: addrs.DataResourceMode, @@ -9564,7 +9591,7 @@ func TestContext2Apply_destroyDataCycle(t *testing.T) { }.Instance(addrs.NoKey), &states.ResourceInstanceObjectSrc{ Status: states.ObjectReady, - AttrsJSON: []byte(`{"id":"data"}`), + AttrsJSON: []byte(`{"id":"old"}`), }, addrs.AbsProviderConfig{ Provider: addrs.NewDefaultProvider("null"), @@ -9574,15 +9601,14 @@ func TestContext2Apply_destroyDataCycle(t *testing.T) { Providers := map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), + addrs.NewDefaultProvider("test"): testProviderFuncFixed(tp), } - hook := &testHook{} ctx := testContext2(t, &ContextOpts{ Config: m, Providers: Providers, State: state, - Destroy: true, - Hooks: []Hook{hook}, + PlanMode: plans.DestroyMode, }) plan, diags := ctx.Plan() @@ -9604,6 +9630,19 @@ func TestContext2Apply_destroyDataCycle(t *testing.T) { t.Fatalf("failed to create context for plan: %s", diags.Err()) } + tp.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { + foo := req.Config.GetAttr("foo") + if !foo.IsKnown() { + resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown config value foo")) + return resp + } + + if foo.AsString() != "new" { + resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("wrong config value: %q", foo.AsString())) + } + return resp + } + _, diags = ctx.Apply() if diags.HasErrors() { t.Fatalf("diags: %s", diags.Err()) @@ -9633,7 +9672,7 @@ func TestContext2Apply_taintedDestroyFailure(t *testing.T) { return testApplyFn(req) } - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test_instance": { Attributes: map[string]*configschema.Attribute{ @@ -9648,7 +9687,7 @@ func TestContext2Apply_taintedDestroyFailure(t *testing.T) { }, }, }, - } + }) state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -9794,22 +9833,25 @@ func TestContext2Apply_plannedConnectionRefs(t *testing.T) { return resp } - pr := testProvisioner() - pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { - host := req.Connection.GetAttr("host") - if host.IsNull() || !host.IsKnown() { - resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("invalid host value: %#v", host)) - } + provisionerFactory := func() (provisioners.Interface, error) { + pr := testProvisioner() + pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { + host := req.Connection.GetAttr("host") + if host.IsNull() || !host.IsKnown() { + resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("invalid host value: %#v", host)) + } - return resp + return resp + } + return pr, nil } Providers := map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), } - provisioners := map[string]ProvisionerFactory{ - "shell": testProvisionerFuncFixed(pr), + provisioners := map[string]provisioners.Factory{ + "shell": provisionerFactory, } hook := &testHook{} @@ -9835,7 +9877,6 @@ func TestContext2Apply_plannedConnectionRefs(t *testing.T) { func TestContext2Apply_cbdCycle(t *testing.T) { m, snap := testModuleWithSnapshot(t, "apply-cbd-cycle") p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn state := states.NewState() @@ -9955,7 +9996,7 @@ func TestContext2Apply_ProviderMeta_apply_set(t *testing.T) { m := testModule(t, "provider-meta-set") p := testProvider("test") p.PlanResourceChangeFn = testDiffFn - schema := p.GetSchemaReturn + schema := p.ProviderSchema() schema.ProviderMeta = &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "baz": { @@ -9982,7 +10023,7 @@ func TestContext2Apply_ProviderMeta_apply_set(t *testing.T) { NewState: cty.ObjectVal(s), } } - p.GetSchemaReturn = schema + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -10038,7 +10079,7 @@ func TestContext2Apply_ProviderMeta_apply_unset(t *testing.T) { m := testModule(t, "provider-meta-unset") p := testProvider("test") p.PlanResourceChangeFn = testDiffFn - schema := p.GetSchemaReturn + schema := p.ProviderSchema() schema.ProviderMeta = &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "baz": { @@ -10063,7 +10104,7 @@ func TestContext2Apply_ProviderMeta_apply_unset(t *testing.T) { NewState: cty.ObjectVal(s), } } - p.GetSchemaReturn = schema + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -10097,8 +10138,7 @@ func TestContext2Apply_ProviderMeta_apply_unset(t *testing.T) { func TestContext2Apply_ProviderMeta_plan_set(t *testing.T) { m := testModule(t, "provider-meta-set") p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn - schema := p.GetSchemaReturn + schema := p.ProviderSchema() schema.ProviderMeta = &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "baz": { @@ -10114,7 +10154,7 @@ func TestContext2Apply_ProviderMeta_plan_set(t *testing.T) { PlannedState: req.ProposedNewState, } } - p.GetSchemaReturn = schema + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -10166,8 +10206,7 @@ func TestContext2Apply_ProviderMeta_plan_set(t *testing.T) { func TestContext2Apply_ProviderMeta_plan_unset(t *testing.T) { m := testModule(t, "provider-meta-unset") p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn - schema := p.GetSchemaReturn + schema := p.ProviderSchema() schema.ProviderMeta = &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "baz": { @@ -10183,7 +10222,7 @@ func TestContext2Apply_ProviderMeta_plan_unset(t *testing.T) { PlannedState: req.ProposedNewState, } } - p.GetSchemaReturn = schema + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -10214,7 +10253,6 @@ func TestContext2Apply_ProviderMeta_plan_unset(t *testing.T) { func TestContext2Apply_ProviderMeta_plan_setNoSchema(t *testing.T) { m := testModule(t, "provider-meta-set") p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -10254,9 +10292,8 @@ func TestContext2Apply_ProviderMeta_plan_setNoSchema(t *testing.T) { func TestContext2Apply_ProviderMeta_plan_setInvalid(t *testing.T) { m := testModule(t, "provider-meta-set") p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn - schema := p.GetSchemaReturn + schema := p.ProviderSchema() schema.ProviderMeta = &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "quux": { @@ -10265,7 +10302,7 @@ func TestContext2Apply_ProviderMeta_plan_setInvalid(t *testing.T) { }, }, } - p.GetSchemaReturn = schema + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -10308,9 +10345,8 @@ func TestContext2Apply_ProviderMeta_plan_setInvalid(t *testing.T) { func TestContext2Apply_ProviderMeta_refresh_set(t *testing.T) { m := testModule(t, "provider-meta-set") p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn - schema := p.GetSchemaReturn + schema := p.ProviderSchema() schema.ProviderMeta = &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "baz": { @@ -10320,17 +10356,16 @@ func TestContext2Apply_ProviderMeta_refresh_set(t *testing.T) { }, } rrcPMs := map[string]cty.Value{} - p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { + p.ReadResourceFn = func(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) { rrcPMs[req.TypeName] = req.ProviderMeta - newState, err := p.GetSchemaReturn.ResourceTypes[req.TypeName].CoerceValue(p.ReadResourceResponse.NewState) + newState, err := p.GetProviderSchemaResponse.ResourceTypes[req.TypeName].Block.CoerceValue(req.PriorState) if err != nil { panic(err) } - resp := p.ReadResourceResponse resp.NewState = newState return resp } - p.GetSchemaReturn = schema + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -10388,11 +10423,10 @@ func TestContext2Apply_ProviderMeta_refresh_set(t *testing.T) { func TestContext2Apply_ProviderMeta_refresh_setNoSchema(t *testing.T) { m := testModule(t, "provider-meta-set") p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn // we need a schema for plan/apply so they don't error - schema := p.GetSchemaReturn + schema := p.ProviderSchema() schema.ProviderMeta = &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "baz": { @@ -10401,7 +10435,7 @@ func TestContext2Apply_ProviderMeta_refresh_setNoSchema(t *testing.T) { }, }, } - p.GetSchemaReturn = schema + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -10417,7 +10451,7 @@ func TestContext2Apply_ProviderMeta_refresh_setNoSchema(t *testing.T) { // drop the schema before refresh, to test that it errors schema.ProviderMeta = nil - p.GetSchemaReturn = schema + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) ctx = testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -10457,11 +10491,10 @@ func TestContext2Apply_ProviderMeta_refresh_setNoSchema(t *testing.T) { func TestContext2Apply_ProviderMeta_refresh_setInvalid(t *testing.T) { m := testModule(t, "provider-meta-set") p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn // we need a matching schema for plan/apply so they don't error - schema := p.GetSchemaReturn + schema := p.ProviderSchema() schema.ProviderMeta = &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "baz": { @@ -10470,7 +10503,7 @@ func TestContext2Apply_ProviderMeta_refresh_setInvalid(t *testing.T) { }, }, } - p.GetSchemaReturn = schema + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -10493,7 +10526,7 @@ func TestContext2Apply_ProviderMeta_refresh_setInvalid(t *testing.T) { }, }, } - p.GetSchemaReturn = schema + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) ctx = testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -10537,9 +10570,8 @@ func TestContext2Apply_ProviderMeta_refresh_setInvalid(t *testing.T) { func TestContext2Apply_ProviderMeta_refreshdata_set(t *testing.T) { m := testModule(t, "provider-meta-data-set") p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn - schema := p.GetSchemaReturn + schema := p.ProviderSchema() schema.ProviderMeta = &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "baz": { @@ -10548,7 +10580,7 @@ func TestContext2Apply_ProviderMeta_refreshdata_set(t *testing.T) { }, }, } - p.GetSchemaReturn = schema + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -10633,9 +10665,8 @@ func TestContext2Apply_ProviderMeta_refreshdata_set(t *testing.T) { func TestContext2Apply_ProviderMeta_refreshdata_unset(t *testing.T) { m := testModule(t, "provider-meta-data-unset") p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn - schema := p.GetSchemaReturn + schema := p.ProviderSchema() schema.ProviderMeta = &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "baz": { @@ -10644,7 +10675,7 @@ func TestContext2Apply_ProviderMeta_refreshdata_unset(t *testing.T) { }, }, } - p.GetSchemaReturn = schema + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -10702,7 +10733,6 @@ func TestContext2Apply_ProviderMeta_refreshdata_unset(t *testing.T) { func TestContext2Apply_ProviderMeta_refreshdata_setNoSchema(t *testing.T) { m := testModule(t, "provider-meta-data-set") p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -10710,7 +10740,7 @@ func TestContext2Apply_ProviderMeta_refreshdata_setNoSchema(t *testing.T) { addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), }, }) - p.ReadDataSourceResponse = providers.ReadDataSourceResponse{ + p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ State: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("yo"), "foo": cty.StringVal("bar"), @@ -10748,9 +10778,8 @@ func TestContext2Apply_ProviderMeta_refreshdata_setNoSchema(t *testing.T) { func TestContext2Apply_ProviderMeta_refreshdata_setInvalid(t *testing.T) { m := testModule(t, "provider-meta-data-set") p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn - schema := p.GetSchemaReturn + schema := p.ProviderSchema() schema.ProviderMeta = &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "quux": { @@ -10759,14 +10788,14 @@ func TestContext2Apply_ProviderMeta_refreshdata_setInvalid(t *testing.T) { }, }, } - p.GetSchemaReturn = schema + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), }, }) - p.ReadDataSourceResponse = providers.ReadDataSourceResponse{ + p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ State: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("yo"), "foo": cty.StringVal("bar"), @@ -10835,8 +10864,8 @@ output "out" { }) p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -10896,7 +10925,6 @@ resource "aws_instance" "cbd" { }) p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -11028,7 +11056,6 @@ output "c" { }`}) p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -11052,7 +11079,7 @@ output "c" { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), }, - Destroy: true, + PlanMode: plans.DestroyMode, }) _, diags = ctx.Plan() @@ -11097,7 +11124,6 @@ output "myoutput" { `}) p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -11121,8 +11147,8 @@ output "myoutput" { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), }, - Destroy: true, - State: state, + PlanMode: plans.DestroyMode, + State: state, }) _, diags = ctx.Plan() @@ -11221,7 +11247,6 @@ locals { ) p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = func(r providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { n := r.ProposedNewState.AsValueMap() @@ -11385,10 +11410,10 @@ output "output" { testP.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { return providers.ReadResourceResponse{NewState: req.PriorState} } - testP.GetSchemaReturn = schemaFn("test") + testP.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schemaFn("test")) providerConfig := "" - testP.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + testP.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { value := req.Config.GetAttr("value") if value.IsKnown() && !value.IsNull() { providerConfig = value.AsString() @@ -11410,12 +11435,12 @@ output "output" { nullP.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { return providers.ReadResourceResponse{NewState: req.PriorState} } - nullP.GetSchemaReturn = schemaFn("null") + nullP.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schemaFn("null")) nullP.ApplyResourceChangeFn = testApplyFn nullP.PlanResourceChangeFn = testDiffFn - nullP.ReadDataSourceResponse = providers.ReadDataSourceResponse{ + nullP.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ State: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("ID"), "output": cty.StringVal("valid"), @@ -11446,8 +11471,8 @@ output "output" { addrs.NewDefaultProvider("null"): testProviderFuncFixed(nullP), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, }) if _, diags := ctx.Plan(); diags.HasErrors() { @@ -11511,7 +11536,6 @@ output "outputs" { `}) p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { return providers.ReadDataSourceResponse{ @@ -11545,8 +11569,8 @@ output "outputs" { addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, }) if _, diags := ctx.Plan(); diags.HasErrors() { @@ -11591,7 +11615,6 @@ resource "test_resource" "a" { `}) p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { proposed := req.ProposedNewState.AsValueMap() proposed["id"] = cty.UnknownVal(cty.String) @@ -11663,7 +11686,6 @@ resource "test_instance" "b" { `}) p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ @@ -11726,7 +11748,6 @@ resource "test_resource" "c" { `}) p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ @@ -11795,8 +11816,39 @@ resource "test_resource" "foo" { }`, }) - p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn + p := new(MockProvider) + p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { + return providers.ReadResourceResponse{NewState: req.PriorState} + } + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ + Provider: &configschema.Block{}, + ResourceTypes: map[string]*configschema.Block{ + "test_resource": { + Attributes: map[string]*configschema.Attribute{ + "id": { + Type: cty.String, + Computed: true, + }, + "value": { + Type: cty.String, + Optional: true, + Computed: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "network_interface": { + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "network_interface_id": {Type: cty.String, Optional: true}, + "device_index": {Type: cty.Number, Optional: true}, + }, + }, + Nesting: configschema.NestingSet, + }, + }, + }, + }, + }) p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ @@ -11870,6 +11922,55 @@ variable "sensitive_map" { resource "test_resource" "foo" { value = var.sensitive_map.x +} +`, + }) + + p := testProvider("test") + p.PlanResourceChangeFn = testDiffFn + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + plan, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatalf("plan errors: %s", diags.Err()) + } + + verifySensitiveValue := func(pvms []cty.PathValueMarks) { + if len(pvms) != 1 { + t.Fatalf("expected 1 sensitive path, got %d", len(pvms)) + } + pvm := pvms[0] + if gotPath, wantPath := pvm.Path, cty.GetAttrPath("value"); !gotPath.Equals(wantPath) { + t.Errorf("wrong path\n got: %#v\nwant: %#v", gotPath, wantPath) + } + if gotMarks, wantMarks := pvm.Marks, cty.NewValueMarks("sensitive"); !gotMarks.Equal(wantMarks) { + t.Errorf("wrong marks\n got: %#v\nwant: %#v", gotMarks, wantMarks) + } + } + + addr := mustResourceInstanceAddr("test_resource.foo") + fooChangeSrc := plan.Changes.ResourceInstance(addr) + verifySensitiveValue(fooChangeSrc.AfterValMarks) + + state, diags := ctx.Apply() + if diags.HasErrors() { + t.Fatalf("apply errors: %s", diags.Err()) + } + + fooState := state.ResourceInstance(addr) + verifySensitiveValue(fooState.Current.AttrSensitivePaths) +} + +func TestContext2Apply_variableSensitivityProviders(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_resource" "foo" { sensitive_value = "should get marked" } @@ -11890,7 +11991,6 @@ resource "test_resource" "baz" { }) p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ @@ -11918,10 +12018,6 @@ resource "test_resource" "baz" { } } - addr := mustResourceInstanceAddr("test_resource.foo") - fooChangeSrc := plan.Changes.ResourceInstance(addr) - verifySensitiveValue(fooChangeSrc.AfterValMarks) - // Sensitive attributes (defined by the provider) are marked // as sensitive when referenced from another resource // "bar" references sensitive resources in "foo" @@ -11938,9 +12034,6 @@ resource "test_resource" "baz" { t.Fatalf("apply errors: %s", diags.Err()) } - fooState := state.ResourceInstance(addr) - verifySensitiveValue(fooState.Current.AttrSensitivePaths) - barState := state.ResourceInstance(barAddr) verifySensitiveValue(barState.Current.AttrSensitivePaths) @@ -11962,7 +12055,6 @@ resource "test_resource" "foo" { }) p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ @@ -12000,6 +12092,9 @@ resource "test_resource" "foo" { fooState := state.ResourceInstance(addr) + if len(fooState.Current.AttrSensitivePaths) != 1 { + t.Fatalf("wrong number of sensitive paths, expected 1, got, %v", len(fooState.Current.AttrSensitivePaths)) + } got := fooState.Current.AttrSensitivePaths[0] want := cty.PathValueMarks{ Path: cty.GetAttrPath("value"), @@ -12099,6 +12194,7 @@ output "out" { func TestContext2Apply_provisionerSensitive(t *testing.T) { m := testModule(t, "apply-provisioner-sensitive") p := testProvider("aws") + pr := testProvisioner() pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) { if req.Config.ContainsMarked() { @@ -12111,8 +12207,8 @@ func TestContext2Apply_provisionerSensitive(t *testing.T) { req.UIOutput.Output(fmt.Sprintf("Executing: %q", command.AsString())) return } - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = testApplyFn h := new(MockHook) ctx := testContext2(t, &ContextOpts{ @@ -12121,7 +12217,7 @@ func TestContext2Apply_provisionerSensitive(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, Variables: InputValues{ @@ -12137,6 +12233,9 @@ func TestContext2Apply_provisionerSensitive(t *testing.T) { t.Fatal("plan failed") } + // "restart" provisioner + pr.CloseCalled = false + state, diags := ctx.Apply() if diags.HasErrors() { logDiagnostics(t, diags) @@ -12165,3 +12264,341 @@ func TestContext2Apply_provisionerSensitive(t *testing.T) { t.Errorf("expected hook to be called with %q, but was:\n%s", want, got) } } + +func TestContext2Apply_warnings(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_resource" "foo" { +}`, + }) + + p := testProvider("test") + p.PlanResourceChangeFn = testDiffFn + + p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { + resp := testApplyFn(req) + + resp.Diagnostics = resp.Diagnostics.Append(tfdiags.SimpleWarning("warning")) + return resp + } + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + if _, diags := ctx.Plan(); diags.HasErrors() { + t.Fatalf("plan errors: %s", diags.Err()) + } + + state, diags := ctx.Apply() + if diags.HasErrors() { + t.Fatalf("diags: %s", diags.Err()) + } + + inst := state.ResourceInstance(mustResourceInstanceAddr("test_resource.foo")) + if inst == nil { + t.Fatal("missing 'test_resource.foo' in state:", state) + } +} + +func TestContext2Apply_rpcDiagnostics(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_instance" "a" { +} +`, + }) + + p := testProvider("test") + p.PlanResourceChangeFn = testDiffFn + p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { + resp = testApplyFn(req) + resp.Diagnostics = resp.Diagnostics.Append(tfdiags.SimpleWarning("don't frobble")) + return resp + } + + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ + ResourceTypes: map[string]*configschema.Block{ + "test_instance": { + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + }, + }, + }, + }) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + _, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + _, diags = ctx.Apply() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + if len(diags) == 0 { + t.Fatal("expected warnings") + } + + for _, d := range diags { + des := d.Description().Summary + if !strings.Contains(des, "frobble") { + t.Fatalf(`expected frobble, got %q`, des) + } + } +} + +func TestContext2Apply_dataSensitive(t *testing.T) { + m := testModule(t, "apply-data-sensitive") + p := testProvider("null") + p.PlanResourceChangeFn = testDiffFn + p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { + // add the required id + m := req.Config.AsValueMap() + m["id"] = cty.StringVal("foo") + + return providers.ReadDataSourceResponse{ + State: cty.ObjectVal(m), + } + } + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("null"): testProviderFuncFixed(p), + }, + }) + + if p, diags := ctx.Plan(); diags.HasErrors() { + t.Fatalf("diags: %s", diags.Err()) + } else { + t.Logf(legacyDiffComparisonString(p.Changes)) + } + + state, diags := ctx.Apply() + assertNoErrors(t, diags) + + addr := mustResourceInstanceAddr("data.null_data_source.testing") + + dataSourceState := state.ResourceInstance(addr) + pvms := dataSourceState.Current.AttrSensitivePaths + if len(pvms) != 1 { + t.Fatalf("expected 1 sensitive path, got %d", len(pvms)) + } + pvm := pvms[0] + if gotPath, wantPath := pvm.Path, cty.GetAttrPath("foo"); !gotPath.Equals(wantPath) { + t.Errorf("wrong path\n got: %#v\nwant: %#v", gotPath, wantPath) + } + if gotMarks, wantMarks := pvm.Marks, cty.NewValueMarks("sensitive"); !gotMarks.Equal(wantMarks) { + t.Errorf("wrong marks\n got: %#v\nwant: %#v", gotMarks, wantMarks) + } +} + +func TestContext2Apply_errorRestorePrivateData(t *testing.T) { + // empty config to remove our resource + m := testModuleInline(t, map[string]string{ + "main.tf": "", + }) + + p := simpleMockProvider() + p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{ + // we error during apply, which will trigger core to preserve the last + // known state, including private data + Diagnostics: tfdiags.Diagnostics(nil).Append(errors.New("oops")), + } + + addr := mustResourceInstanceAddr("test_object.a") + + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent(addr, &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"foo"}`), + Private: []byte("private"), + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) + }) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + State: state, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + _, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + state, _ = ctx.Apply() + if string(state.ResourceInstance(addr).Current.Private) != "private" { + t.Fatal("missing private data in state") + } +} + +func TestContext2Apply_errorRestoreStatus(t *testing.T) { + // empty config to remove our resource + m := testModuleInline(t, map[string]string{ + "main.tf": "", + }) + + p := simpleMockProvider() + p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { + // We error during apply, but return the current object state. + resp.Diagnostics = resp.Diagnostics.Append(errors.New("oops")) + // return a warning too to make sure it isn't dropped + resp.Diagnostics = resp.Diagnostics.Append(tfdiags.SimpleWarning("warned")) + resp.NewState = req.PriorState + resp.Private = req.PlannedPrivate + return resp + } + + addr := mustResourceInstanceAddr("test_object.a") + + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent(addr, &states.ResourceInstanceObjectSrc{ + Status: states.ObjectTainted, + AttrsJSON: []byte(`{"test_string":"foo"}`), + Private: []byte("private"), + Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.b")}, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) + }) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + State: state, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + _, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + state, diags = ctx.Apply() + + errString := diags.ErrWithWarnings().Error() + if !strings.Contains(errString, "oops") || !strings.Contains(errString, "warned") { + t.Fatalf("error missing expected info: %q", errString) + } + + if len(diags) != 2 { + t.Fatalf("expected 1 error and 1 warning, got: %q", errString) + } + + res := state.ResourceInstance(addr) + if res == nil { + t.Fatal("resource was removed from state") + } + + if res.Current.Status != states.ObjectTainted { + t.Fatal("resource should still be tainted in the state") + } + + if len(res.Current.Dependencies) != 1 || !res.Current.Dependencies[0].Equal(mustConfigResourceAddr("test_object.b")) { + t.Fatalf("incorrect dependencies, got %q", res.Current.Dependencies) + } + + if string(res.Current.Private) != "private" { + t.Fatalf("incorrect private data, got %q", res.Current.Private) + } +} + +func TestContext2Apply_nonConformingResponse(t *testing.T) { + // empty config to remove our resource + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_object" "a" { + test_string = "x" +} +`, + }) + + p := simpleMockProvider() + respDiags := tfdiags.Diagnostics(nil).Append(tfdiags.SimpleWarning("warned")) + respDiags = respDiags.Append(errors.New("oops")) + p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{ + // Don't lose these diagnostics + Diagnostics: respDiags, + // This state is missing required attributes, and should produce an error + NewState: cty.ObjectVal(map[string]cty.Value{ + "test_string": cty.StringVal("x"), + }), + } + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + _, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + _, diags = ctx.Apply() + errString := diags.ErrWithWarnings().Error() + if !strings.Contains(errString, "oops") || !strings.Contains(errString, "warned") { + t.Fatalf("error missing expected info: %q", errString) + } + + // we should have more than the ones returned from the provider, and they + // should not be coalesced into a single value + if len(diags) < 3 { + t.Fatalf("incorrect diagnostics, got %d values with %s", len(diags), diags.ErrWithWarnings()) + } +} + +func TestContext2Apply_nilResponse(t *testing.T) { + // empty config to remove our resource + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_object" "a" { +} +`, + }) + + p := simpleMockProvider() + p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{} + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + _, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + _, diags = ctx.Apply() + if !diags.HasErrors() { + t.Fatal("expected and error") + } + + errString := diags.ErrWithWarnings().Error() + if !strings.Contains(errString, "invalid nil value") { + t.Fatalf("error missing expected info: %q", errString) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// NOTE: Due to the size of this file, new tests should be added to +// context_apply2_test.go. +//////////////////////////////////////////////////////////////////////////////// diff --git a/vendor/github.com/hashicorp/terraform/terraform/context_components.go b/vendor/github.com/hashicorp/terraform/terraform/context_components.go index c893a16b..354337dd 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/context_components.go +++ b/vendor/github.com/hashicorp/terraform/terraform/context_components.go @@ -26,7 +26,7 @@ type contextComponentFactory interface { // basicComponentFactory just calls a factory from a map directly. type basicComponentFactory struct { providers map[addrs.Provider]providers.Factory - provisioners map[string]ProvisionerFactory + provisioners map[string]provisioners.Factory } func (c *basicComponentFactory) ResourceProviders() []string { diff --git a/vendor/github.com/hashicorp/terraform/terraform/context_components_test.go b/vendor/github.com/hashicorp/terraform/terraform/context_components_test.go index 28feebdc..646b7550 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/context_components_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/context_components_test.go @@ -32,7 +32,7 @@ func simpleMockComponentFactory() *basicComponentFactory { return provider, nil }, }, - provisioners: map[string]ProvisionerFactory{ + provisioners: map[string]provisioners.Factory{ "test": func() (provisioners.Interface, error) { return provisioner, nil }, @@ -62,19 +62,19 @@ func simpleTestSchema() *configschema.Block { Optional: true, }, "test_number": { - Type: cty.String, + Type: cty.Number, Optional: true, }, "test_bool": { - Type: cty.String, + Type: cty.Bool, Optional: true, }, "test_list": { - Type: cty.String, + Type: cty.List(cty.String), Optional: true, }, "test_map": { - Type: cty.String, + Type: cty.Map(cty.String), Optional: true, }, }, diff --git a/vendor/github.com/hashicorp/terraform/terraform/context_fixtures_test.go b/vendor/github.com/hashicorp/terraform/terraform/context_fixtures_test.go index 2ed26439..3b4ab010 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/context_fixtures_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/context_fixtures_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/provisioners" "github.com/zclconf/go-cty/cty" ) @@ -16,7 +17,7 @@ import ( type contextTestFixture struct { Config *configs.Config Providers map[addrs.Provider]providers.Factory - Provisioners map[string]ProvisionerFactory + Provisioners map[string]provisioners.Factory } // ContextOpts returns a ContextOps pre-populated with the elements of this diff --git a/vendor/github.com/hashicorp/terraform/terraform/context_graph_type.go b/vendor/github.com/hashicorp/terraform/terraform/context_graph_type.go index aefa17f7..658779e6 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/context_graph_type.go +++ b/vendor/github.com/hashicorp/terraform/terraform/context_graph_type.go @@ -11,6 +11,7 @@ const ( GraphTypeInvalid GraphType = iota GraphTypePlan GraphTypePlanDestroy + GraphTypePlanRefreshOnly GraphTypeApply GraphTypeValidate GraphTypeEval // only visits in-memory elements such as variables, locals, and outputs. @@ -20,9 +21,10 @@ const ( // is useful to use as the mechanism for human input for configurable // graph types. var GraphTypeMap = map[string]GraphType{ - "apply": GraphTypeApply, - "plan": GraphTypePlan, - "plan-destroy": GraphTypePlanDestroy, - "validate": GraphTypeValidate, - "eval": GraphTypeEval, + "apply": GraphTypeApply, + "plan": GraphTypePlan, + "plan-destroy": GraphTypePlanDestroy, + "plan-refresh-only": GraphTypePlanRefreshOnly, + "validate": GraphTypeValidate, + "eval": GraphTypeEval, } diff --git a/vendor/github.com/hashicorp/terraform/terraform/context_import_test.go b/vendor/github.com/hashicorp/terraform/terraform/context_import_test.go index 01b9c2de..377d29d2 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/context_import_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/context_import_test.go @@ -22,10 +22,14 @@ func TestContextImport_basic(t *testing.T) { }, }) - p.ImportStateReturn = []*InstanceState{ - &InstanceState{ - ID: "foo", - Ephemeral: EphemeralState{Type: "aws_instance"}, + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "aws_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + }), + }, }, } @@ -59,10 +63,14 @@ func TestContextImport_countIndex(t *testing.T) { }, }) - p.ImportStateReturn = []*InstanceState{ - &InstanceState{ - ID: "foo", - Ephemeral: EphemeralState{Type: "aws_instance"}, + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "aws_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + }), + }, }, } @@ -117,10 +125,14 @@ func TestContextImport_collision(t *testing.T) { }), }) - p.ImportStateReturn = []*InstanceState{ - &InstanceState{ - ID: "foo", - Ephemeral: EphemeralState{Type: "aws_instance"}, + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "aws_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + }), + }, }, } @@ -152,9 +164,13 @@ func TestContextImport_missingType(t *testing.T) { p := testProvider("aws") m := testModule(t, "import-provider") - p.ImportStateReturn = []*InstanceState{ - &InstanceState{ - ID: "foo", + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + }), + }, }, } @@ -189,14 +205,18 @@ func TestContextImport_missingType(t *testing.T) { func TestContextImport_moduleProvider(t *testing.T) { p := testProvider("aws") - p.ImportStateReturn = []*InstanceState{ - &InstanceState{ - ID: "foo", - Ephemeral: EphemeralState{Type: "aws_instance"}, + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "aws_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + }), + }, }, } - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { foo := req.Config.GetAttr("foo").AsString() if foo != "bar" { resp.Diagnostics = resp.Diagnostics.Append(errors.New("not bar")) @@ -227,7 +247,7 @@ func TestContextImport_moduleProvider(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - if !p.ConfigureCalled { + if !p.ConfigureProviderCalled { t.Fatal("didn't configure provider") } @@ -249,14 +269,18 @@ func TestContextImport_providerModule(t *testing.T) { }, }) - p.ImportStateReturn = []*InstanceState{ - &InstanceState{ - ID: "foo", - Ephemeral: EphemeralState{Type: "aws_instance"}, + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "aws_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + }), + }, }, } - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { foo := req.Config.GetAttr("foo").AsString() if foo != "bar" { resp.Diagnostics = resp.Diagnostics.Append(errors.New("not bar")) @@ -279,7 +303,7 @@ func TestContextImport_providerModule(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - if !p.ConfigureCalled { + if !p.ConfigureProviderCalled { t.Fatal("didn't configure provider") } } @@ -317,10 +341,14 @@ func TestContextImport_providerConfig(t *testing.T) { }, }) - p.ImportStateReturn = []*InstanceState{ - &InstanceState{ - ID: "foo", - Ephemeral: EphemeralState{Type: "aws_instance"}, + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "aws_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + }), + }, }, } @@ -338,11 +366,11 @@ func TestContextImport_providerConfig(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - if !p.ConfigureCalled { + if !p.ConfigureProviderCalled { t.Fatal("didn't configure provider") } - if foo := p.ConfigureRequest.Config.GetAttr("foo").AsString(); foo != test.value { + if foo := p.ConfigureProviderRequest.Config.GetAttr("foo").AsString(); foo != test.value { t.Fatalf("bad value %#v; want %#v", foo, test.value) } @@ -368,10 +396,14 @@ func TestContextImport_providerConfigResources(t *testing.T) { }, }) - p.ImportStateReturn = []*InstanceState{ - &InstanceState{ - ID: "foo", - Ephemeral: EphemeralState{Type: "aws_instance"}, + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "aws_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + }), + }, }, } @@ -403,16 +435,20 @@ func TestContextImport_refresh(t *testing.T) { }, }) - p.ImportStateReturn = []*InstanceState{ - &InstanceState{ - ID: "foo", - Ephemeral: EphemeralState{Type: "aws_instance"}, + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "aws_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + }), + }, }, } p.ReadResourceFn = nil - p.ReadResourceResponse = providers.ReadResourceResponse{ + p.ReadResourceResponse = &providers.ReadResourceResponse{ NewState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("foo"), "foo": cty.StringVal("bar"), @@ -450,10 +486,14 @@ func TestContextImport_refreshNil(t *testing.T) { }, }) - p.ImportStateReturn = []*InstanceState{ - &InstanceState{ - ID: "foo", - Ephemeral: EphemeralState{Type: "aws_instance"}, + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "aws_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + }), + }, }, } @@ -494,10 +534,14 @@ func TestContextImport_module(t *testing.T) { }, }) - p.ImportStateReturn = []*InstanceState{ - &InstanceState{ - ID: "foo", - Ephemeral: EphemeralState{Type: "aws_instance"}, + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "aws_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + }), + }, }, } @@ -532,10 +576,14 @@ func TestContextImport_moduleDepth2(t *testing.T) { }, }) - p.ImportStateReturn = []*InstanceState{ - &InstanceState{ - ID: "foo", - Ephemeral: EphemeralState{Type: "aws_instance"}, + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "aws_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + }), + }, }, } @@ -570,10 +618,14 @@ func TestContextImport_moduleDiff(t *testing.T) { }, }) - p.ImportStateReturn = []*InstanceState{ - &InstanceState{ - ID: "foo", - Ephemeral: EphemeralState{Type: "aws_instance"}, + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "aws_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + }), + }, }, } @@ -602,7 +654,7 @@ func TestContextImport_multiState(t *testing.T) { p := testProvider("aws") m := testModule(t, "import-provider") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "foo": {Type: cty.String, Optional: true}, @@ -620,16 +672,22 @@ func TestContextImport_multiState(t *testing.T) { }, }, }, - } + }) - p.ImportStateReturn = []*InstanceState{ - &InstanceState{ - ID: "foo", - Ephemeral: EphemeralState{Type: "aws_instance"}, - }, - &InstanceState{ - ID: "bar", - Ephemeral: EphemeralState{Type: "aws_instance_thing"}, + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "aws_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + }), + }, + { + TypeName: "aws_instance_thing", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("bar"), + }), + }, }, } @@ -665,7 +723,7 @@ func TestContextImport_multiStateSame(t *testing.T) { p := testProvider("aws") m := testModule(t, "import-provider") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "foo": {Type: cty.String, Optional: true}, @@ -683,20 +741,28 @@ func TestContextImport_multiStateSame(t *testing.T) { }, }, }, - } + }) - p.ImportStateReturn = []*InstanceState{ - &InstanceState{ - ID: "foo", - Ephemeral: EphemeralState{Type: "aws_instance"}, - }, - &InstanceState{ - ID: "bar", - Ephemeral: EphemeralState{Type: "aws_instance_thing"}, - }, - &InstanceState{ - ID: "qux", - Ephemeral: EphemeralState{Type: "aws_instance_thing"}, + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "aws_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + }), + }, + { + TypeName: "aws_instance_thing", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("bar"), + }), + }, + { + TypeName: "aws_instance_thing", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("qux"), + }), + }, }, } @@ -763,7 +829,7 @@ resource "test_resource" "unused" { `, }) - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "foo": {Type: cty.String, Optional: true}, @@ -776,9 +842,19 @@ resource "test_resource" "unused" { }, }, }, - } + }) - p.ImportResourceStateResponse = providers.ImportResourceStateResponse{ + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "test_resource", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("test"), + }), + }, + }, + } + p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ ImportedResources: []providers.ImportedResource{ { TypeName: "test_resource", @@ -845,17 +921,6 @@ module.child[0].nested: provider = provider["registry.terraform.io/hashicorp/aws"] ` -const testImportModuleExistingStr = ` - -module.foo: - aws_instance.bar: - ID = bar - provider = provider["registry.terraform.io/hashicorp/aws"] - aws_instance.foo: - ID = foo - provider = provider["registry.terraform.io/hashicorp/aws"] -` - const testImportMultiStr = ` aws_instance.foo: ID = foo diff --git a/vendor/github.com/hashicorp/terraform/terraform/context_input_test.go b/vendor/github.com/hashicorp/terraform/terraform/context_input_test.go index 1887ac97..dc33e00d 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/context_input_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/context_input_test.go @@ -17,9 +17,7 @@ import ( func TestContext2Input_provider(t *testing.T) { m := testModule(t, "input-provider") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "foo": { @@ -39,7 +37,7 @@ func TestContext2Input_provider(t *testing.T) { }, }, }, - } + }) inp := &MockUIInput{ InputReturnMap: map[string]string{ @@ -56,7 +54,7 @@ func TestContext2Input_provider(t *testing.T) { }) var actual interface{} - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { actual = req.Config.GetAttr("foo").AsString() return } @@ -89,9 +87,7 @@ func TestContext2Input_providerMulti(t *testing.T) { m := testModule(t, "input-provider-multi") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "foo": { @@ -111,7 +107,7 @@ func TestContext2Input_providerMulti(t *testing.T) { }, }, }, - } + }) inp := &MockUIInput{ InputReturnMap: map[string]string{ @@ -139,7 +135,7 @@ func TestContext2Input_providerMulti(t *testing.T) { t.Fatalf("plan errors: %s", diags.Err()) } - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { lock.Lock() defer lock.Unlock() actual = append(actual, req.Config.GetAttr("foo").AsString()) @@ -158,8 +154,6 @@ func TestContext2Input_providerMulti(t *testing.T) { func TestContext2Input_providerOnce(t *testing.T) { m := testModule(t, "input-provider-once") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -178,9 +172,7 @@ func TestContext2Input_providerId(t *testing.T) { m := testModule(t, "input-provider") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "foo": { @@ -200,7 +192,7 @@ func TestContext2Input_providerId(t *testing.T) { }, }, }, - } + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -211,7 +203,7 @@ func TestContext2Input_providerId(t *testing.T) { }) var actual interface{} - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { actual = req.Config.GetAttr("foo").AsString() return } @@ -241,11 +233,8 @@ func TestContext2Input_providerOnly(t *testing.T) { input := new(MockUIInput) m := testModule(t, "input-provider-vars") - p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "foo": { @@ -263,7 +252,7 @@ func TestContext2Input_providerOnly(t *testing.T) { }, }, }, - } + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -284,7 +273,7 @@ func TestContext2Input_providerOnly(t *testing.T) { } var actual interface{} - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { actual = req.Config.GetAttr("foo").AsString() return } @@ -317,8 +306,6 @@ func TestContext2Input_providerVars(t *testing.T) { input := new(MockUIInput) m := testModule(t, "input-provider-with-vars") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -338,7 +325,7 @@ func TestContext2Input_providerVars(t *testing.T) { } var actual interface{} - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { actual = req.Config.GetAttr("foo").AsString() return } @@ -363,8 +350,6 @@ func TestContext2Input_providerVarsModuleInherit(t *testing.T) { input := new(MockUIInput) m := testModule(t, "input-provider-with-vars-and-module") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -383,8 +368,6 @@ func TestContext2Input_submoduleTriggersInvalidCount(t *testing.T) { input := new(MockUIInput) m := testModule(t, "input-submodule-count") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -405,7 +388,7 @@ func TestContext2Input_dataSourceRequiresRefresh(t *testing.T) { p := testProvider("null") m := testModule(t, "input-module-data-vars") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ DataSources: map[string]*configschema.Block{ "null_data_source": { Attributes: map[string]*configschema.Attribute{ @@ -413,7 +396,7 @@ func TestContext2Input_dataSourceRequiresRefresh(t *testing.T) { }, }, }, - } + }) p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { return providers.ReadDataSourceResponse{ State: req.Config, diff --git a/vendor/github.com/hashicorp/terraform/terraform/context_plan2_test.go b/vendor/github.com/hashicorp/terraform/terraform/context_plan2_test.go new file mode 100644 index 00000000..e6c7da29 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/context_plan2_test.go @@ -0,0 +1,1149 @@ +package terraform + +import ( + "bytes" + "errors" + "strings" + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/states" + "github.com/zclconf/go-cty/cty" +) + +func TestContext2Plan_removedDuringRefresh(t *testing.T) { + // This tests the situation where an object tracked in the previous run + // state has been deleted outside of Terraform, which we should detect + // during the refresh step and thus ultimately produce a plan to recreate + // the object, since it's still present in the configuration. + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_object" "a" { +} +`, + }) + + p := simpleMockProvider() + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{Block: simpleTestSchema()}, + ResourceTypes: map[string]providers.Schema{ + "test_object": { + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "arg": {Type: cty.String, Optional: true}, + }, + }, + }, + }, + } + p.ReadResourceFn = func(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) { + resp.NewState = cty.NullVal(req.PriorState.Type()) + return resp + } + p.UpgradeResourceStateFn = func(req providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) { + // We should've been given the prior state JSON as our input to upgrade. + if !bytes.Contains(req.RawStateJSON, []byte("previous_run")) { + t.Fatalf("UpgradeResourceState request doesn't contain the previous run object\n%s", req.RawStateJSON) + } + + // We'll put something different in "arg" as part of upgrading, just + // so that we can verify below that PrevRunState contains the upgraded + // (but NOT refreshed) version of the object. + resp.UpgradedState = cty.ObjectVal(map[string]cty.Value{ + "arg": cty.StringVal("upgraded"), + }) + return resp + } + + addr := mustResourceInstanceAddr("test_object.a") + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent(addr, &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"arg":"previous_run"}`), + Status: states.ObjectTainted, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) + }) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + State: state, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + plan, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + if !p.UpgradeResourceStateCalled { + t.Errorf("Provider's UpgradeResourceState wasn't called; should've been") + } + if !p.ReadResourceCalled { + t.Errorf("Provider's ReadResource wasn't called; should've been") + } + + // The object should be absent from the plan's prior state, because that + // records the result of refreshing. + if got := plan.PriorState.ResourceInstance(addr); got != nil { + t.Errorf( + "instance %s is in the prior state after planning; should've been removed\n%s", + addr, spew.Sdump(got), + ) + } + + // However, the object should still be in the PrevRunState, because + // that reflects what we believed to exist before refreshing. + if got := plan.PrevRunState.ResourceInstance(addr); got == nil { + t.Errorf( + "instance %s is missing from the previous run state after planning; should've been preserved", + addr, + ) + } else { + if !bytes.Contains(got.Current.AttrsJSON, []byte("upgraded")) { + t.Fatalf("previous run state has non-upgraded object\n%s", got.Current.AttrsJSON) + } + } + + // Because the configuration still mentions test_object.a, we should've + // planned to recreate it in order to fix the drift. + for _, c := range plan.Changes.Resources { + if c.Action != plans.Create { + t.Fatalf("expected Create action for missing %s, got %s", c.Addr, c.Action) + } + } +} + +func TestContext2Plan_noChangeDataSourceSensitiveNestedSet(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +variable "bar" { + sensitive = true + default = "baz" +} + +data "test_data_source" "foo" { + foo { + bar = var.bar + } +} +`, + }) + + p := new(MockProvider) + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ + DataSources: map[string]*configschema.Block{ + "test_data_source": { + Attributes: map[string]*configschema.Attribute{ + "id": { + Type: cty.String, + Computed: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "foo": { + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bar": {Type: cty.String, Optional: true}, + }, + }, + Nesting: configschema.NestingSet, + }, + }, + }, + }, + }) + + p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("data_id"), + "foo": cty.SetVal([]cty.Value{cty.ObjectVal(map[string]cty.Value{"bar": cty.StringVal("baz")})}), + }), + } + + state := states.NewState() + root := state.EnsureModule(addrs.RootModuleInstance) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("data.test_data_source.foo").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"data_id", "foo":[{"bar":"baz"}]}`), + AttrSensitivePaths: []cty.PathValueMarks{ + { + Path: cty.GetAttrPath("foo"), + Marks: cty.NewValueMarks("sensitive"), + }, + }, + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + State: state, + }) + + plan, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.ErrWithWarnings()) + } + + for _, res := range plan.Changes.Resources { + if res.Action != plans.NoOp { + t.Fatalf("expected NoOp, got: %q %s", res.Addr, res.Action) + } + } +} + +func TestContext2Plan_orphanDataInstance(t *testing.T) { + // ensure the planned replacement of the data source is evaluated properly + m := testModuleInline(t, map[string]string{ + "main.tf": ` +data "test_object" "a" { + for_each = { new = "ok" } +} + +output "out" { + value = [ for k, _ in data.test_object.a: k ] +} +`, + }) + + p := simpleMockProvider() + p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { + resp.State = req.Config + return resp + } + + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent(mustResourceInstanceAddr(`data.test_object.a["old"]`), &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"test_string":"foo"}`), + Status: states.ObjectReady, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) + }) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + State: state, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + plan, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + change, err := plan.Changes.Outputs[0].Decode() + if err != nil { + t.Fatal(err) + } + + expected := cty.TupleVal([]cty.Value{cty.StringVal("new")}) + + if change.After.Equals(expected).False() { + t.Fatalf("expected %#v, got %#v\n", expected, change.After) + } +} + +func TestContext2Plan_basicConfigurationAliases(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +provider "test" { + alias = "z" + test_string = "config" +} + +module "mod" { + source = "./mod" + providers = { + test.x = test.z + } +} +`, + + "mod/main.tf": ` +terraform { + required_providers { + test = { + source = "registry.terraform.io/hashicorp/test" + configuration_aliases = [ test.x ] + } + } +} + +resource "test_object" "a" { + provider = test.x +} + +`, + }) + + p := simpleMockProvider() + + // The resource within the module should be using the provider configured + // from the root module. We should never see an empty configuration. + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { + if req.Config.GetAttr("test_string").IsNull() { + resp.Diagnostics = resp.Diagnostics.Append(errors.New("missing test_string value")) + } + return resp + } + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + _, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } +} + +func TestContext2Plan_dataReferencesResourceInModules(t *testing.T) { + p := testProvider("test") + p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { + cfg := req.Config.AsValueMap() + cfg["id"] = cty.StringVal("d") + resp.State = cty.ObjectVal(cfg) + return resp + } + + m := testModuleInline(t, map[string]string{ + "main.tf": ` +locals { + things = { + old = "first" + new = "second" + } +} + +module "mod" { + source = "./mod" + for_each = local.things +} +`, + + "./mod/main.tf": ` +resource "test_resource" "a" { +} + +data "test_data_source" "d" { + depends_on = [test_resource.a] +} + +resource "test_resource" "b" { + value = data.test_data_source.d.id +} +`}) + + oldDataAddr := mustResourceInstanceAddr(`module.mod["old"].data.test_data_source.d`) + + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent( + mustResourceInstanceAddr(`module.mod["old"].test_resource.a`), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"a"}`), + Status: states.ObjectReady, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + s.SetResourceInstanceCurrent( + mustResourceInstanceAddr(`module.mod["old"].test_resource.b`), + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"b","value":"d"}`), + Status: states.ObjectReady, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + s.SetResourceInstanceCurrent( + oldDataAddr, + &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"id":"d"}`), + Status: states.ObjectReady, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + }) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + State: state, + }) + + plan, diags := ctx.Plan() + assertNoErrors(t, diags) + + oldMod := oldDataAddr.Module + + for _, c := range plan.Changes.Resources { + // there should be no changes from the old module instance + if c.Addr.Module.Equal(oldMod) && c.Action != plans.NoOp { + t.Errorf("unexpected change %s for %s\n", c.Action, c.Addr) + } + } +} + +func TestContext2Plan_destroyWithRefresh(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_object" "a" { +} +`, + }) + + p := simpleMockProvider() + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{Block: simpleTestSchema()}, + ResourceTypes: map[string]providers.Schema{ + "test_object": { + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "arg": {Type: cty.String, Optional: true}, + }, + }, + }, + }, + } + p.ReadResourceFn = func(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) { + newVal, err := cty.Transform(req.PriorState, func(path cty.Path, v cty.Value) (cty.Value, error) { + if len(path) == 1 && path[0] == (cty.GetAttrStep{Name: "arg"}) { + return cty.StringVal("current"), nil + } + return v, nil + }) + if err != nil { + // shouldn't get here + t.Fatalf("ReadResourceFn transform failed") + return providers.ReadResourceResponse{} + } + return providers.ReadResourceResponse{ + NewState: newVal, + } + } + p.UpgradeResourceStateFn = func(req providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) { + t.Logf("UpgradeResourceState %s", req.RawStateJSON) + + // In the destroy-with-refresh codepath we end up calling + // UpgradeResourceState twice, because we do so once during refreshing + // (as part making a normal plan) and then again during the plan-destroy + // walk. The second call recieves the result of the earlier refresh, + // so we need to tolerate both "before" and "current" as possible + // inputs here. + if !bytes.Contains(req.RawStateJSON, []byte("before")) { + if !bytes.Contains(req.RawStateJSON, []byte("current")) { + t.Fatalf("UpgradeResourceState request doesn't contain the 'before' object or the 'current' object\n%s", req.RawStateJSON) + } + } + + // We'll put something different in "arg" as part of upgrading, just + // so that we can verify below that PrevRunState contains the upgraded + // (but NOT refreshed) version of the object. + resp.UpgradedState = cty.ObjectVal(map[string]cty.Value{ + "arg": cty.StringVal("upgraded"), + }) + return resp + } + + addr := mustResourceInstanceAddr("test_object.a") + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent(addr, &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"arg":"before"}`), + Status: states.ObjectReady, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) + }) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + State: state, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + PlanMode: plans.DestroyMode, + SkipRefresh: false, + }) + + plan, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + if !p.UpgradeResourceStateCalled { + t.Errorf("Provider's UpgradeResourceState wasn't called; should've been") + } + if !p.ReadResourceCalled { + t.Errorf("Provider's ReadResource wasn't called; should've been") + } + + if plan.PriorState == nil { + t.Fatal("missing plan state") + } + + for _, c := range plan.Changes.Resources { + if c.Action != plans.Delete { + t.Errorf("unexpected %s change for %s", c.Action, c.Addr) + } + } + + if instState := plan.PrevRunState.ResourceInstance(addr); instState == nil { + t.Errorf("%s has no previous run state at all after plan", addr) + } else { + if instState.Current == nil { + t.Errorf("%s has no current object in the previous run state", addr) + } else if got, want := instState.Current.AttrsJSON, `"upgraded"`; !bytes.Contains(got, []byte(want)) { + t.Errorf("%s has wrong previous run state after plan\ngot:\n%s\n\nwant substring: %s", addr, got, want) + } + } + if instState := plan.PriorState.ResourceInstance(addr); instState == nil { + t.Errorf("%s has no prior state at all after plan", addr) + } else { + if instState.Current == nil { + t.Errorf("%s has no current object in the prior state", addr) + } else if got, want := instState.Current.AttrsJSON, `"current"`; !bytes.Contains(got, []byte(want)) { + t.Errorf("%s has wrong prior state after plan\ngot:\n%s\n\nwant substring: %s", addr, got, want) + } + } +} + +func TestContext2Plan_destroySkipRefresh(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_object" "a" { +} +`, + }) + + p := simpleMockProvider() + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{Block: simpleTestSchema()}, + ResourceTypes: map[string]providers.Schema{ + "test_object": { + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "arg": {Type: cty.String, Optional: true}, + }, + }, + }, + }, + } + p.ReadResourceFn = func(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) { + t.Helper() + t.Errorf("unexpected call to ReadResource") + resp.NewState = req.PriorState + return resp + } + p.UpgradeResourceStateFn = func(req providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) { + t.Logf("UpgradeResourceState %s", req.RawStateJSON) + // We should've been given the prior state JSON as our input to upgrade. + if !bytes.Contains(req.RawStateJSON, []byte("before")) { + t.Fatalf("UpgradeResourceState request doesn't contain the 'before' object\n%s", req.RawStateJSON) + } + + // We'll put something different in "arg" as part of upgrading, just + // so that we can verify below that PrevRunState contains the upgraded + // (but NOT refreshed) version of the object. + resp.UpgradedState = cty.ObjectVal(map[string]cty.Value{ + "arg": cty.StringVal("upgraded"), + }) + return resp + } + + addr := mustResourceInstanceAddr("test_object.a") + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent(addr, &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"arg":"before"}`), + Status: states.ObjectReady, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) + }) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + State: state, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + PlanMode: plans.DestroyMode, + SkipRefresh: true, + }) + + plan, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + if !p.UpgradeResourceStateCalled { + t.Errorf("Provider's UpgradeResourceState wasn't called; should've been") + } + if p.ReadResourceCalled { + t.Errorf("Provider's ReadResource was called; shouldn't have been") + } + + if plan.PriorState == nil { + t.Fatal("missing plan state") + } + + for _, c := range plan.Changes.Resources { + if c.Action != plans.Delete { + t.Errorf("unexpected %s change for %s", c.Action, c.Addr) + } + } + + if instState := plan.PrevRunState.ResourceInstance(addr); instState == nil { + t.Errorf("%s has no previous run state at all after plan", addr) + } else { + if instState.Current == nil { + t.Errorf("%s has no current object in the previous run state", addr) + } else if got, want := instState.Current.AttrsJSON, `"upgraded"`; !bytes.Contains(got, []byte(want)) { + t.Errorf("%s has wrong previous run state after plan\ngot:\n%s\n\nwant substring: %s", addr, got, want) + } + } + if instState := plan.PriorState.ResourceInstance(addr); instState == nil { + t.Errorf("%s has no prior state at all after plan", addr) + } else { + if instState.Current == nil { + t.Errorf("%s has no current object in the prior state", addr) + } else if got, want := instState.Current.AttrsJSON, `"upgraded"`; !bytes.Contains(got, []byte(want)) { + // NOTE: The prior state should still have been _upgraded_, even + // though we skipped running refresh after upgrading it. + t.Errorf("%s has wrong prior state after plan\ngot:\n%s\n\nwant substring: %s", addr, got, want) + } + } +} + +func TestContext2Plan_unmarkingSensitiveAttributeForOutput(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_resource" "foo" { +} + +output "result" { + value = nonsensitive(test_resource.foo.sensitive_attr) +} +`, + }) + + p := new(MockProvider) + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ + ResourceTypes: map[string]*configschema.Block{ + "test_resource": { + Attributes: map[string]*configschema.Attribute{ + "id": { + Type: cty.String, + Computed: true, + }, + "sensitive_attr": { + Type: cty.String, + Computed: true, + Sensitive: true, + }, + }, + }, + }, + }) + + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { + return providers.PlanResourceChangeResponse{ + PlannedState: cty.UnknownVal(cty.Object(map[string]cty.Type{ + "id": cty.String, + "sensitive_attr": cty.String, + })), + } + } + + state := states.NewState() + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + State: state, + }) + + plan, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.ErrWithWarnings()) + } + + for _, res := range plan.Changes.Resources { + if res.Action != plans.Create { + t.Fatalf("expected create, got: %q %s", res.Addr, res.Action) + } + } +} + +func TestContext2Plan_destroyNoProviderConfig(t *testing.T) { + // providers do not need to be configured during a destroy plan + p := simpleMockProvider() + p.ValidateProviderConfigFn = func(req providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) { + v := req.Config.GetAttr("test_string") + if v.IsNull() || !v.IsKnown() || v.AsString() != "ok" { + resp.Diagnostics = resp.Diagnostics.Append(errors.New("invalid provider configuration")) + } + return resp + } + + m := testModuleInline(t, map[string]string{ + "main.tf": ` +locals { + value = "ok" +} + +provider "test" { + test_string = local.value +} +`, + }) + + addr := mustResourceInstanceAddr("test_object.a") + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent(addr, &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"test_string":"foo"}`), + Status: states.ObjectReady, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) + }) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + State: state, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + PlanMode: plans.DestroyMode, + }) + + _, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } +} + +func TestContext2Plan_refreshOnlyMode(t *testing.T) { + addr := mustResourceInstanceAddr("test_object.a") + + // The configuration, the prior state, and the refresh result intentionally + // have different values for "test_string" so we can observe that the + // refresh took effect but the configuration change wasn't considered. + m := testModuleInline(t, map[string]string{ + "main.tf": ` + resource "test_object" "a" { + arg = "after" + } + + output "out" { + value = test_object.a.arg + } + `, + }) + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent(addr, &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"arg":"before"}`), + Status: states.ObjectReady, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) + }) + + p := simpleMockProvider() + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{Block: simpleTestSchema()}, + ResourceTypes: map[string]providers.Schema{ + "test_object": { + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "arg": {Type: cty.String, Optional: true}, + }, + }, + }, + }, + } + p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { + newVal, err := cty.Transform(req.PriorState, func(path cty.Path, v cty.Value) (cty.Value, error) { + if len(path) == 1 && path[0] == (cty.GetAttrStep{Name: "arg"}) { + return cty.StringVal("current"), nil + } + return v, nil + }) + if err != nil { + // shouldn't get here + t.Fatalf("ReadResourceFn transform failed") + return providers.ReadResourceResponse{} + } + return providers.ReadResourceResponse{ + NewState: newVal, + } + } + p.UpgradeResourceStateFn = func(req providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) { + // We should've been given the prior state JSON as our input to upgrade. + if !bytes.Contains(req.RawStateJSON, []byte("before")) { + t.Fatalf("UpgradeResourceState request doesn't contain the 'before' object\n%s", req.RawStateJSON) + } + + // We'll put something different in "arg" as part of upgrading, just + // so that we can verify below that PrevRunState contains the upgraded + // (but NOT refreshed) version of the object. + resp.UpgradedState = cty.ObjectVal(map[string]cty.Value{ + "arg": cty.StringVal("upgraded"), + }) + return resp + } + + ctx := testContext2(t, &ContextOpts{ + Config: m, + State: state, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + PlanMode: plans.RefreshOnlyMode, + }) + + plan, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatalf("unexpected errors\n%s", diags.Err().Error()) + } + + if !p.UpgradeResourceStateCalled { + t.Errorf("Provider's UpgradeResourceState wasn't called; should've been") + } + if !p.ReadResourceCalled { + t.Errorf("Provider's ReadResource wasn't called; should've been") + } + + if got, want := len(plan.Changes.Resources), 0; got != want { + t.Errorf("plan contains resource changes; want none\n%s", spew.Sdump(plan.Changes.Resources)) + } + + if instState := plan.PriorState.ResourceInstance(addr); instState == nil { + t.Errorf("%s has no prior state at all after plan", addr) + } else { + if instState.Current == nil { + t.Errorf("%s has no current object after plan", addr) + } else if got, want := instState.Current.AttrsJSON, `"current"`; !bytes.Contains(got, []byte(want)) { + // Should've saved the result of refreshing + t.Errorf("%s has wrong prior state after plan\ngot:\n%s\n\nwant substring: %s", addr, got, want) + } + } + if instState := plan.PrevRunState.ResourceInstance(addr); instState == nil { + t.Errorf("%s has no previous run state at all after plan", addr) + } else { + if instState.Current == nil { + t.Errorf("%s has no current object in the previous run state", addr) + } else if got, want := instState.Current.AttrsJSON, `"upgraded"`; !bytes.Contains(got, []byte(want)) { + // Should've saved the result of upgrading + t.Errorf("%s has wrong previous run state after plan\ngot:\n%s\n\nwant substring: %s", addr, got, want) + } + } + + // The output value should also have updated. If not, it's likely that we + // skipped updating the working state to match the refreshed state when we + // were evaluating the resource. + if outChangeSrc := plan.Changes.OutputValue(addrs.RootModuleInstance.OutputValue("out")); outChangeSrc == nil { + t.Errorf("no change planned for output value 'out'") + } else { + outChange, err := outChangeSrc.Decode() + if err != nil { + t.Fatalf("failed to decode output value 'out': %s", err) + } + got := outChange.After + want := cty.StringVal("current") + if !want.RawEquals(got) { + t.Errorf("wrong value for output value 'out'\ngot: %#v\nwant: %#v", got, want) + } + } +} + +func TestContext2Plan_invalidSensitiveModuleOutput(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "child/main.tf": ` +output "out" { + value = sensitive("xyz") +}`, + "main.tf": ` +module "child" { + source = "./child" +} + +output "root" { + value = module.child.out +}`, + }) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + }) + + _, diags := ctx.Plan() + if !diags.HasErrors() { + t.Fatal("succeeded; want errors") + } + if got, want := diags.Err().Error(), "Output refers to sensitive values"; !strings.Contains(got, want) { + t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) + } +} + +func TestContext2Plan_planDataSourceSensitiveNested(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_instance" "bar" { +} + +data "test_data_source" "foo" { + foo { + bar = test_instance.bar.sensitive + } +} +`, + }) + + p := new(MockProvider) + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { + resp.PlannedState = cty.ObjectVal(map[string]cty.Value{ + "sensitive": cty.UnknownVal(cty.String), + }) + return resp + } + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ + ResourceTypes: map[string]*configschema.Block{ + "test_instance": { + Attributes: map[string]*configschema.Attribute{ + "sensitive": { + Type: cty.String, + Computed: true, + Sensitive: true, + }, + }, + }, + }, + DataSources: map[string]*configschema.Block{ + "test_data_source": { + Attributes: map[string]*configschema.Attribute{ + "id": { + Type: cty.String, + Computed: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "foo": { + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bar": {Type: cty.String, Optional: true}, + }, + }, + Nesting: configschema.NestingSet, + }, + }, + }, + }, + }) + + state := states.NewState() + root := state.EnsureModule(addrs.RootModuleInstance) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("data.test_data_source.foo").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"string":"data_id", "foo":[{"bar":"old"}]}`), + AttrSensitivePaths: []cty.PathValueMarks{ + { + Path: cty.GetAttrPath("foo"), + Marks: cty.NewValueMarks("sensitive"), + }, + }, + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("test_instance.bar").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"sensitive":"old"}`), + AttrSensitivePaths: []cty.PathValueMarks{ + { + Path: cty.GetAttrPath("sensitive"), + Marks: cty.NewValueMarks("sensitive"), + }, + }, + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + State: state, + }) + + plan, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.ErrWithWarnings()) + } + + for _, res := range plan.Changes.Resources { + switch res.Addr.String() { + case "test_instance.bar": + if res.Action != plans.Update { + t.Fatalf("unexpected %s change for %s", res.Action, res.Addr) + } + case "data.test_data_source.foo": + if res.Action != plans.Read { + t.Fatalf("unexpected %s change for %s", res.Action, res.Addr) + } + default: + t.Fatalf("unexpected %s change for %s", res.Action, res.Addr) + } + } +} + +func TestContext2Plan_forceReplace(t *testing.T) { + addrA := mustResourceInstanceAddr("test_object.a") + addrB := mustResourceInstanceAddr("test_object.b") + m := testModuleInline(t, map[string]string{ + "main.tf": ` + resource "test_object" "a" { + } + resource "test_object" "b" { + } + `, + }) + + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent(addrA, &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{}`), + Status: states.ObjectReady, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) + s.SetResourceInstanceCurrent(addrB, &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{}`), + Status: states.ObjectReady, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) + }) + + p := simpleMockProvider() + ctx := testContext2(t, &ContextOpts{ + Config: m, + State: state, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + ForceReplace: []addrs.AbsResourceInstance{ + addrA, + }, + }) + + plan, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatalf("unexpected errors\n%s", diags.Err().Error()) + } + + t.Run(addrA.String(), func(t *testing.T) { + instPlan := plan.Changes.ResourceInstance(addrA) + if instPlan == nil { + t.Fatalf("no plan for %s at all", addrA) + } + + if got, want := instPlan.Action, plans.DeleteThenCreate; got != want { + t.Errorf("wrong planned action\ngot: %s\nwant: %s", got, want) + } + if got, want := instPlan.ActionReason, plans.ResourceInstanceReplaceByRequest; got != want { + t.Errorf("wrong action reason\ngot: %s\nwant: %s", got, want) + } + }) + t.Run(addrB.String(), func(t *testing.T) { + instPlan := plan.Changes.ResourceInstance(addrB) + if instPlan == nil { + t.Fatalf("no plan for %s at all", addrB) + } + + if got, want := instPlan.Action, plans.NoOp; got != want { + t.Errorf("wrong planned action\ngot: %s\nwant: %s", got, want) + } + if got, want := instPlan.ActionReason, plans.ResourceInstanceChangeNoReason; got != want { + t.Errorf("wrong action reason\ngot: %s\nwant: %s", got, want) + } + }) +} + +func TestContext2Plan_forceReplaceIncompleteAddr(t *testing.T) { + addr0 := mustResourceInstanceAddr("test_object.a[0]") + addr1 := mustResourceInstanceAddr("test_object.a[1]") + addrBare := mustResourceInstanceAddr("test_object.a") + m := testModuleInline(t, map[string]string{ + "main.tf": ` + resource "test_object" "a" { + count = 2 + } + `, + }) + + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent(addr0, &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{}`), + Status: states.ObjectReady, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) + s.SetResourceInstanceCurrent(addr1, &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{}`), + Status: states.ObjectReady, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) + }) + + p := simpleMockProvider() + ctx := testContext2(t, &ContextOpts{ + Config: m, + State: state, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + ForceReplace: []addrs.AbsResourceInstance{ + addrBare, + }, + }) + + plan, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatalf("unexpected errors\n%s", diags.Err().Error()) + } + diagsErr := diags.ErrWithWarnings() + if diagsErr == nil { + t.Fatalf("no warnings were returned") + } + if got, want := diagsErr.Error(), "Incompletely-matched force-replace resource instance"; !strings.Contains(got, want) { + t.Errorf("missing expected warning\ngot:\n%s\n\nwant substring: %s", got, want) + } + + t.Run(addr0.String(), func(t *testing.T) { + instPlan := plan.Changes.ResourceInstance(addr0) + if instPlan == nil { + t.Fatalf("no plan for %s at all", addr0) + } + + if got, want := instPlan.Action, plans.NoOp; got != want { + t.Errorf("wrong planned action\ngot: %s\nwant: %s", got, want) + } + if got, want := instPlan.ActionReason, plans.ResourceInstanceChangeNoReason; got != want { + t.Errorf("wrong action reason\ngot: %s\nwant: %s", got, want) + } + }) + t.Run(addr1.String(), func(t *testing.T) { + instPlan := plan.Changes.ResourceInstance(addr1) + if instPlan == nil { + t.Fatalf("no plan for %s at all", addr1) + } + + if got, want := instPlan.Action, plans.NoOp; got != want { + t.Errorf("wrong planned action\ngot: %s\nwant: %s", got, want) + } + if got, want := instPlan.ActionReason, plans.ResourceInstanceChangeNoReason; got != want { + t.Errorf("wrong action reason\ngot: %s\nwant: %s", got, want) + } + }) +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/context_plan_test.go b/vendor/github.com/hashicorp/terraform/terraform/context_plan_test.go index 819870f9..5428b1c4 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/context_plan_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/context_plan_test.go @@ -20,6 +20,7 @@ import ( "github.com/hashicorp/terraform/configs/hcl2shim" "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/provisioners" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/tfdiags" ) @@ -27,7 +28,6 @@ import ( func TestContext2Plan_basic(t *testing.T) { m := testModule(t, "plan-good") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -55,7 +55,7 @@ func TestContext2Plan_basic(t *testing.T) { t.Fatalf("expected empty state, got %#v\n", ctx.State()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() for _, r := range plan.Changes.Resources { ric, err := r.Decode(ty) @@ -78,6 +78,11 @@ func TestContext2Plan_basic(t *testing.T) { t.Fatal("unknown instance:", i) } } + + if !p.ValidateProviderConfigCalled { + t.Fatal("provider config was not checked before Configure") + } + } func TestContext2Plan_createBefore_deposed(t *testing.T) { @@ -130,7 +135,7 @@ func TestContext2Plan_createBefore_deposed(t *testing.T) { t.Fatalf("\nexpected: %q\ngot: %q\n", expectedState, ctx.State().String()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() type InstanceGen struct { @@ -198,7 +203,6 @@ func TestContext2Plan_createBefore_deposed(t *testing.T) { func TestContext2Plan_createBefore_maintainRoot(t *testing.T) { m := testModule(t, "plan-cbd-maintain-root") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -295,7 +299,7 @@ func TestContext2Plan_escapedVar(t *testing.T) { t.Fatalf("expected resource creation, got %s", res.Action) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() ric, err := res.Decode(ty) @@ -315,7 +319,6 @@ func TestContext2Plan_escapedVar(t *testing.T) { func TestContext2Plan_minimal(t *testing.T) { m := testModule(t, "plan-empty") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -371,7 +374,7 @@ func TestContext2Plan_modules(t *testing.T) { t.Error("expected 3 resource in plan, got", len(plan.Changes.Resources)) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() expectFoo := objectVal(t, schema, map[string]cty.Value{ @@ -414,7 +417,6 @@ func TestContext2Plan_moduleExpand(t *testing.T) { // Test a smattering of plan expansion behavior m := testModule(t, "plan-modules-expand") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -427,7 +429,7 @@ func TestContext2Plan_moduleExpand(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() expected := map[string]struct{}{ @@ -464,7 +466,7 @@ func TestContext2Plan_moduleExpand(t *testing.T) { func TestContext2Plan_moduleCycle(t *testing.T) { m := testModule(t, "plan-module-cycle") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -474,8 +476,7 @@ func TestContext2Plan_moduleCycle(t *testing.T) { }, }, }, - } - p.PlanResourceChangeFn = testDiffFn + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -489,7 +490,7 @@ func TestContext2Plan_moduleCycle(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -544,7 +545,7 @@ func TestContext2Plan_moduleDeadlock(t *testing.T) { t.Fatalf("err: %s", err) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() for _, res := range plan.Changes.Resources { @@ -589,7 +590,7 @@ func TestContext2Plan_moduleInput(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -644,7 +645,7 @@ func TestContext2Plan_moduleInputComputed(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -702,7 +703,7 @@ func TestContext2Plan_moduleInputFromVar(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -740,7 +741,7 @@ func TestContext2Plan_moduleInputFromVar(t *testing.T) { func TestContext2Plan_moduleMultiVar(t *testing.T) { m := testModule(t, "plan-module-multi-var") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -750,8 +751,7 @@ func TestContext2Plan_moduleMultiVar(t *testing.T) { }, }, }, - } - p.PlanResourceChangeFn = testDiffFn + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -765,7 +765,7 @@ func TestContext2Plan_moduleMultiVar(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 5 { @@ -840,7 +840,7 @@ func TestContext2Plan_moduleOrphans(t *testing.T) { if diags.HasErrors() { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -888,8 +888,8 @@ module.child: func TestContext2Plan_moduleOrphansWithProvisioner(t *testing.T) { m := testModule(t, "plan-modules-remove-provisioners") p := testProvider("aws") - pr := testProvisioner() p.PlanResourceChangeFn = testDiffFn + pr := testProvisioner() state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -925,7 +925,7 @@ func TestContext2Plan_moduleOrphansWithProvisioner(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, State: state, @@ -936,7 +936,7 @@ func TestContext2Plan_moduleOrphansWithProvisioner(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 3 { @@ -1002,7 +1002,7 @@ func TestContext2Plan_moduleProviderInherit(t *testing.T) { defer l.Unlock() p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "from": {Type: cty.String, Optional: true}, @@ -1015,8 +1015,8 @@ func TestContext2Plan_moduleProviderInherit(t *testing.T) { }, }, }, - } - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + }) + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { from := req.Config.GetAttr("from") if from.IsNull() || from.AsString() != "root" { resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("not root")) @@ -1066,7 +1066,7 @@ func TestContext2Plan_moduleProviderInheritDeep(t *testing.T) { var from string p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "from": {Type: cty.String, Optional: true}, @@ -1077,9 +1077,9 @@ func TestContext2Plan_moduleProviderInheritDeep(t *testing.T) { Attributes: map[string]*configschema.Attribute{}, }, }, - } + }) - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { v := req.Config.GetAttr("from") if v.IsNull() || v.AsString() != "root" { resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("not root")) @@ -1121,7 +1121,7 @@ func TestContext2Plan_moduleProviderDefaultsVar(t *testing.T) { defer l.Unlock() p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "to": {Type: cty.String, Optional: true}, @@ -1135,8 +1135,8 @@ func TestContext2Plan_moduleProviderDefaultsVar(t *testing.T) { }, }, }, - } - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + }) + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { var buf bytes.Buffer from := req.Config.GetAttr("from") if !from.IsNull() { @@ -1153,7 +1153,6 @@ func TestContext2Plan_moduleProviderDefaultsVar(t *testing.T) { return } - p.PlanResourceChangeFn = testDiffFn return p, nil }, }, @@ -1183,7 +1182,7 @@ func TestContext2Plan_moduleProviderDefaultsVar(t *testing.T) { func TestContext2Plan_moduleProviderVar(t *testing.T) { m := testModule(t, "plan-module-provider-var") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "value": {Type: cty.String, Optional: true}, @@ -1196,8 +1195,7 @@ func TestContext2Plan_moduleProviderVar(t *testing.T) { }, }, }, - } - p.PlanResourceChangeFn = testDiffFn + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -1211,7 +1209,7 @@ func TestContext2Plan_moduleProviderVar(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 1 { @@ -1254,7 +1252,7 @@ func TestContext2Plan_moduleVar(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -1296,7 +1294,6 @@ func TestContext2Plan_moduleVar(t *testing.T) { func TestContext2Plan_moduleVarWrongTypeBasic(t *testing.T) { m := testModule(t, "plan-module-wrong-var-type") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -1313,7 +1310,6 @@ func TestContext2Plan_moduleVarWrongTypeBasic(t *testing.T) { func TestContext2Plan_moduleVarWrongTypeNested(t *testing.T) { m := testModule(t, "plan-module-wrong-var-type-nested") p := testProvider("null") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -1330,7 +1326,6 @@ func TestContext2Plan_moduleVarWrongTypeNested(t *testing.T) { func TestContext2Plan_moduleVarWithDefaultValue(t *testing.T) { m := testModule(t, "plan-module-var-with-default-value") p := testProvider("null") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -1359,7 +1354,7 @@ func TestContext2Plan_moduleVarComputed(t *testing.T) { if diags.HasErrors() { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -1466,7 +1461,6 @@ func TestContext2Plan_preventDestroy_good(t *testing.T) { func TestContext2Plan_preventDestroy_countBad(t *testing.T) { m := testModule(t, "plan-prevent-destroy-count-bad") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -1509,7 +1503,7 @@ func TestContext2Plan_preventDestroy_countBad(t *testing.T) { func TestContext2Plan_preventDestroy_countGood(t *testing.T) { m := testModule(t, "plan-prevent-destroy-count-good") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -1518,8 +1512,7 @@ func TestContext2Plan_preventDestroy_countGood(t *testing.T) { }, }, }, - } - p.PlanResourceChangeFn = testDiffFn + }) state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -1561,7 +1554,8 @@ func TestContext2Plan_preventDestroy_countGood(t *testing.T) { func TestContext2Plan_preventDestroy_countGoodNoChange(t *testing.T) { m := testModule(t, "plan-prevent-destroy-count-good") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.PlanResourceChangeFn = testDiffFn + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -1571,8 +1565,7 @@ func TestContext2Plan_preventDestroy_countGoodNoChange(t *testing.T) { }, }, }, - } - p.PlanResourceChangeFn = testDiffFn + }) state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -1606,7 +1599,6 @@ func TestContext2Plan_preventDestroy_countGoodNoChange(t *testing.T) { func TestContext2Plan_preventDestroy_destroyPlan(t *testing.T) { m := testModule(t, "plan-prevent-destroy-good") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -1624,8 +1616,8 @@ func TestContext2Plan_preventDestroy_destroyPlan(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, }) plan, diags := ctx.Plan() @@ -1642,14 +1634,13 @@ func TestContext2Plan_preventDestroy_destroyPlan(t *testing.T) { func TestContext2Plan_provisionerCycle(t *testing.T) { m := testModule(t, "plan-provisioner-cycle") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn pr := testProvisioner() ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "local-exec": testProvisionerFuncFixed(pr), }, }) @@ -1676,7 +1667,7 @@ func TestContext2Plan_computed(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -1716,7 +1707,7 @@ func TestContext2Plan_computed(t *testing.T) { func TestContext2Plan_blockNestingGroup(t *testing.T) { m := testModule(t, "plan-block-nesting-group") p := testProvider("test") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test": { BlockTypes: map[string]*configschema.NestedBlock{ @@ -1731,7 +1722,7 @@ func TestContext2Plan_blockNestingGroup(t *testing.T) { }, }, }, - } + }) p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { return providers.PlanResourceChangeResponse{ PlannedState: req.ProposedNewState, @@ -1788,7 +1779,7 @@ func TestContext2Plan_blockNestingGroup(t *testing.T) { func TestContext2Plan_computedDataResource(t *testing.T) { m := testModule(t, "plan-computed-data-resource") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -1805,8 +1796,7 @@ func TestContext2Plan_computedDataResource(t *testing.T) { }, }, }, - } - p.PlanResourceChangeFn = testDiffFn + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -1819,7 +1809,7 @@ func TestContext2Plan_computedDataResource(t *testing.T) { if diags.HasErrors() { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.DataSources["aws_vpc"] + schema := p.GetProviderSchemaResponse.DataSources["aws_vpc"].Block ty := schema.ImpliedType() if rc := plan.Changes.ResourceInstance(addrs.Resource{Mode: addrs.ManagedResourceMode, Type: "aws_instance", Name: "foo"}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)); rc == nil { @@ -1850,7 +1840,7 @@ func TestContext2Plan_computedDataResource(t *testing.T) { func TestContext2Plan_computedInFunction(t *testing.T) { m := testModule(t, "plan-computed-in-function") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -1865,9 +1855,8 @@ func TestContext2Plan_computedInFunction(t *testing.T) { }, }, }, - } - p.PlanResourceChangeFn = testDiffFn - p.ReadDataSourceResponse = providers.ReadDataSourceResponse{ + }) + p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ State: cty.ObjectVal(map[string]cty.Value{ "computed": cty.ListVal([]cty.Value{ cty.StringVal("foo"), @@ -1896,7 +1885,7 @@ func TestContext2Plan_computedInFunction(t *testing.T) { func TestContext2Plan_computedDataCountResource(t *testing.T) { m := testModule(t, "plan-computed-data-count") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -1913,8 +1902,7 @@ func TestContext2Plan_computedDataCountResource(t *testing.T) { }, }, }, - } - p.PlanResourceChangeFn = testDiffFn + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -1945,7 +1933,6 @@ func TestContext2Plan_computedDataCountResource(t *testing.T) { func TestContext2Plan_localValueCount(t *testing.T) { m := testModule(t, "plan-local-value-count") p := testProvider("test") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -1976,7 +1963,7 @@ func TestContext2Plan_dataResourceBecomesComputed(t *testing.T) { m := testModule(t, "plan-data-resource-becomes-computed") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -1993,7 +1980,7 @@ func TestContext2Plan_dataResourceBecomesComputed(t *testing.T) { }, }, }, - } + }) p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { fooVal := req.ProposedNewState.GetAttr("foo") @@ -2006,10 +1993,10 @@ func TestContext2Plan_dataResourceBecomesComputed(t *testing.T) { } } - schema := p.GetSchemaReturn.DataSources["aws_data_source"] + schema := p.GetProviderSchemaResponse.DataSources["aws_data_source"].Block ty := schema.ImpliedType() - p.ReadDataSourceResponse = providers.ReadDataSourceResponse{ + p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ // This should not be called, because the configuration for the // data resource contains an unknown value for "foo". Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("ReadDataSource called, but should not have been")), @@ -2069,7 +2056,8 @@ func TestContext2Plan_dataResourceBecomesComputed(t *testing.T) { func TestContext2Plan_computedList(t *testing.T) { m := testModule(t, "plan-computed-list") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.PlanResourceChangeFn = testDiffFn + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -2080,8 +2068,7 @@ func TestContext2Plan_computedList(t *testing.T) { }, }, }, - } - p.PlanResourceChangeFn = testDiffFn + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -2095,7 +2082,7 @@ func TestContext2Plan_computedList(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -2135,7 +2122,7 @@ func TestContext2Plan_computedMultiIndex(t *testing.T) { p := testProvider("aws") p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -2145,9 +2132,7 @@ func TestContext2Plan_computedMultiIndex(t *testing.T) { }, }, }, - } - - p.PlanResourceChangeFn = testDiffFn + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -2161,7 +2146,7 @@ func TestContext2Plan_computedMultiIndex(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 3 { @@ -2216,7 +2201,7 @@ func TestContext2Plan_count(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 6 { @@ -2278,7 +2263,6 @@ func TestContext2Plan_count(t *testing.T) { func TestContext2Plan_countComputed(t *testing.T) { m := testModule(t, "plan-count-computed") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -2328,7 +2312,7 @@ func TestContext2Plan_countModuleStatic(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 3 { @@ -2382,7 +2366,7 @@ func TestContext2Plan_countModuleStaticGrandchild(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 3 { @@ -2436,7 +2420,7 @@ func TestContext2Plan_countIndex(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -2492,7 +2476,7 @@ func TestContext2Plan_countVar(t *testing.T) { if diags.HasErrors() { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 4 { @@ -2542,7 +2526,7 @@ func TestContext2Plan_countVar(t *testing.T) { func TestContext2Plan_countZero(t *testing.T) { m := testModule(t, "plan-count-zero") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -2550,7 +2534,7 @@ func TestContext2Plan_countZero(t *testing.T) { }, }, }, - } + }) // This schema contains a DynamicPseudoType, and therefore can't go through any shim functions p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { @@ -2570,7 +2554,7 @@ func TestContext2Plan_countZero(t *testing.T) { if diags.HasErrors() { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 1 { @@ -2612,7 +2596,7 @@ func TestContext2Plan_countOneIndex(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -2691,7 +2675,7 @@ func TestContext2Plan_countDecreaseToOne(t *testing.T) { if diags.HasErrors() { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 4 { @@ -2777,7 +2761,7 @@ func TestContext2Plan_countIncreaseFromNotSet(t *testing.T) { if diags.HasErrors() { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 4 { @@ -2856,7 +2840,7 @@ func TestContext2Plan_countIncreaseFromOne(t *testing.T) { if diags.HasErrors() { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 4 { @@ -2949,7 +2933,7 @@ func TestContext2Plan_countIncreaseFromOneCorrupted(t *testing.T) { if diags.HasErrors() { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 5 { @@ -3016,7 +3000,7 @@ func TestContext2Plan_countIncreaseFromOneCorrupted(t *testing.T) { func TestContext2Plan_countIncreaseWithSplatReference(t *testing.T) { m := testModule(t, "plan-count-splat-reference") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -3026,8 +3010,7 @@ func TestContext2Plan_countIncreaseWithSplatReference(t *testing.T) { }, }, }, - } - p.PlanResourceChangeFn = testDiffFn + }) state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -3077,7 +3060,7 @@ func TestContext2Plan_countIncreaseWithSplatReference(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 6 { @@ -3120,7 +3103,6 @@ func TestContext2Plan_countIncreaseWithSplatReference(t *testing.T) { func TestContext2Plan_forEach(t *testing.T) { m := testModule(t, "plan-for-each") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -3133,7 +3115,7 @@ func TestContext2Plan_forEach(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 8 { @@ -3156,7 +3138,6 @@ func TestContext2Plan_forEachUnknownValue(t *testing.T) { // expect this to produce an error, but not to panic. m := testModule(t, "plan-for-each-unknown-value") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -3187,7 +3168,6 @@ func TestContext2Plan_forEachUnknownValue(t *testing.T) { func TestContext2Plan_destroy(t *testing.T) { m := testModule(t, "plan-destroy") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -3213,8 +3193,8 @@ func TestContext2Plan_destroy(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, }) plan, diags := ctx.Plan() @@ -3222,7 +3202,7 @@ func TestContext2Plan_destroy(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -3250,7 +3230,6 @@ func TestContext2Plan_destroy(t *testing.T) { func TestContext2Plan_moduleDestroy(t *testing.T) { m := testModule(t, "plan-module-destroy") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -3277,15 +3256,15 @@ func TestContext2Plan_moduleDestroy(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, }) plan, diags := ctx.Plan() if diags.HasErrors() { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -3314,7 +3293,6 @@ func TestContext2Plan_moduleDestroy(t *testing.T) { func TestContext2Plan_moduleDestroyCycle(t *testing.T) { m := testModule(t, "plan-module-destroy-gh-1835") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn state := states.NewState() aModule := state.EnsureModule(addrs.RootModuleInstance.Child("a_module", addrs.NoKey)) @@ -3341,8 +3319,8 @@ func TestContext2Plan_moduleDestroyCycle(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, }) plan, diags := ctx.Plan() @@ -3350,7 +3328,7 @@ func TestContext2Plan_moduleDestroyCycle(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -3378,7 +3356,6 @@ func TestContext2Plan_moduleDestroyCycle(t *testing.T) { func TestContext2Plan_moduleDestroyMultivar(t *testing.T) { m := testModule(t, "plan-module-destroy-multivar") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn state := states.NewState() child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) @@ -3404,8 +3381,8 @@ func TestContext2Plan_moduleDestroyMultivar(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, }) plan, diags := ctx.Plan() @@ -3413,7 +3390,7 @@ func TestContext2Plan_moduleDestroyMultivar(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -3446,7 +3423,7 @@ func TestContext2Plan_pathVar(t *testing.T) { m := testModule(t, "plan-path-var") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -3456,8 +3433,7 @@ func TestContext2Plan_pathVar(t *testing.T) { }, }, }, - } - p.PlanResourceChangeFn = testDiffFn + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -3471,7 +3447,7 @@ func TestContext2Plan_pathVar(t *testing.T) { t.Fatalf("err: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 1 { @@ -3503,6 +3479,7 @@ func TestContext2Plan_pathVar(t *testing.T) { func TestContext2Plan_diffVar(t *testing.T) { m := testModule(t, "plan-diffvar") p := testProvider("aws") + p.PlanResourceChangeFn = testDiffFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) root.SetResourceInstanceCurrent( @@ -3522,14 +3499,12 @@ func TestContext2Plan_diffVar(t *testing.T) { State: state, }) - p.PlanResourceChangeFn = testDiffFn - plan, diags := ctx.Plan() if diags.HasErrors() { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -3576,7 +3551,6 @@ func TestContext2Plan_hook(t *testing.T) { m := testModule(t, "plan-good") h := new(MockHook) p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Hooks: []Hook{h}, @@ -3604,7 +3578,6 @@ func TestContext2Plan_closeProvider(t *testing.T) { // "provider.aws". m := testModule(t, "plan-close-module-provider") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -3650,7 +3623,7 @@ func TestContext2Plan_orphan(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -3668,10 +3641,16 @@ func TestContext2Plan_orphan(t *testing.T) { if res.Action != plans.Delete { t.Fatalf("resource %s should be removed", i) } + if got, want := ric.ActionReason, plans.ResourceInstanceChangeNoReason; got != want { + t.Errorf("wrong action reason\ngot: %s\nwant: %s", got, want) + } case "aws_instance.foo": if res.Action != plans.Create { t.Fatalf("resource %s should be created", i) } + if got, want := ric.ActionReason, plans.ResourceInstanceChangeNoReason; got != want { + t.Errorf("wrong action reason\ngot: %s\nwant: %s", got, want) + } checkVals(t, objectVal(t, schema, map[string]cty.Value{ "id": cty.UnknownVal(cty.String), "num": cty.NumberIntVal(2), @@ -3688,7 +3667,6 @@ func TestContext2Plan_orphan(t *testing.T) { func TestContext2Plan_shadowUuid(t *testing.T) { m := testModule(t, "plan-shadow-uuid") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -3732,7 +3710,7 @@ func TestContext2Plan_state(t *testing.T) { if len(plan.Changes.Resources) < 2 { t.Fatalf("bad: %#v", plan.Changes.Resources) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -3750,6 +3728,9 @@ func TestContext2Plan_state(t *testing.T) { if res.Action != plans.Create { t.Fatalf("resource %s should be created", i) } + if got, want := ric.ActionReason, plans.ResourceInstanceChangeNoReason; got != want { + t.Errorf("wrong action reason\ngot: %s\nwant: %s", got, want) + } checkVals(t, objectVal(t, schema, map[string]cty.Value{ "id": cty.UnknownVal(cty.String), "foo": cty.StringVal("2"), @@ -3759,6 +3740,9 @@ func TestContext2Plan_state(t *testing.T) { if res.Action != plans.Update { t.Fatalf("resource %s should be updated", i) } + if got, want := ric.ActionReason, plans.ResourceInstanceChangeNoReason; got != want { + t.Errorf("wrong action reason\ngot: %s\nwant: %s", got, want) + } checkVals(t, objectVal(t, schema, map[string]cty.Value{ "id": cty.StringVal("bar"), "num": cty.NullVal(cty.Number), @@ -3775,6 +3759,91 @@ func TestContext2Plan_state(t *testing.T) { } } +func TestContext2Plan_requiresReplace(t *testing.T) { + m := testModule(t, "plan-requires-replace") + p := testProvider("test") + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{}, + }, + ResourceTypes: map[string]providers.Schema{ + "test_thing": providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "v": { + Type: cty.String, + Required: true, + }, + }, + }, + }, + }, + } + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { + return providers.PlanResourceChangeResponse{ + PlannedState: req.ProposedNewState, + RequiresReplace: []cty.Path{ + cty.GetAttrPath("v"), + }, + } + } + + state := states.NewState() + root := state.EnsureModule(addrs.RootModuleInstance) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("test_thing.foo").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"v":"hello"}`), + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + State: state, + }) + + plan, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatalf("unexpected errors: %s", diags.Err()) + } + + schema := p.GetProviderSchemaResponse.ResourceTypes["test_thing"].Block + ty := schema.ImpliedType() + + if got, want := len(plan.Changes.Resources), 1; got != want { + t.Fatalf("got %d changes; want %d", got, want) + } + + for _, res := range plan.Changes.Resources { + t.Run(res.Addr.String(), func(t *testing.T) { + ric, err := res.Decode(ty) + if err != nil { + t.Fatal(err) + } + + switch i := ric.Addr.String(); i { + case "test_thing.foo": + if got, want := ric.Action, plans.DeleteThenCreate; got != want { + t.Errorf("wrong action\ngot: %s\nwant: %s", got, want) + } + if got, want := ric.ActionReason, plans.ResourceInstanceReplaceBecauseCannotUpdate; got != want { + t.Errorf("wrong action reason\ngot: %s\nwant: %s", got, want) + } + checkVals(t, objectVal(t, schema, map[string]cty.Value{ + "v": cty.StringVal("goodbye"), + }), ric.After) + default: + t.Fatalf("unexpected resource instance %s", i) + } + }) + } +} + func TestContext2Plan_taint(t *testing.T) { m := testModule(t, "plan-taint") p := testProvider("aws") @@ -3811,7 +3880,7 @@ func TestContext2Plan_taint(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -3819,35 +3888,43 @@ func TestContext2Plan_taint(t *testing.T) { } for _, res := range plan.Changes.Resources { - ric, err := res.Decode(ty) - if err != nil { - t.Fatal(err) - } - - switch i := ric.Addr.String(); i { - case "aws_instance.bar": - if res.Action != plans.DeleteThenCreate { - t.Fatalf("resource %s should be replaced", i) + t.Run(res.Addr.String(), func(t *testing.T) { + ric, err := res.Decode(ty) + if err != nil { + t.Fatal(err) } - checkVals(t, objectVal(t, schema, map[string]cty.Value{ - "id": cty.UnknownVal(cty.String), - "foo": cty.StringVal("2"), - "type": cty.UnknownVal(cty.String), - }), ric.After) - case "aws_instance.foo": - if res.Action != plans.NoOp { - t.Fatalf("resource %s should not be changed", i) + + switch i := ric.Addr.String(); i { + case "aws_instance.bar": + if got, want := res.Action, plans.DeleteThenCreate; got != want { + t.Errorf("wrong action\ngot: %s\nwant: %s", got, want) + } + if got, want := res.ActionReason, plans.ResourceInstanceReplaceBecauseTainted; got != want { + t.Errorf("wrong action reason\ngot: %s\nwant: %s", got, want) + } + checkVals(t, objectVal(t, schema, map[string]cty.Value{ + "id": cty.UnknownVal(cty.String), + "foo": cty.StringVal("2"), + "type": cty.UnknownVal(cty.String), + }), ric.After) + case "aws_instance.foo": + if got, want := res.Action, plans.NoOp; got != want { + t.Errorf("wrong action\ngot: %s\nwant: %s", got, want) + } + if got, want := res.ActionReason, plans.ResourceInstanceChangeNoReason; got != want { + t.Errorf("wrong action reason\ngot: %s\nwant: %s", got, want) + } + default: + t.Fatal("unknown instance:", i) } - default: - t.Fatal("unknown instance:", i) - } + }) } } func TestContext2Plan_taintIgnoreChanges(t *testing.T) { m := testModule(t, "plan-taint-ignore-changes") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -3857,9 +3934,7 @@ func TestContext2Plan_taintIgnoreChanges(t *testing.T) { }, }, }, - } - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn + }) state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -3885,7 +3960,7 @@ func TestContext2Plan_taintIgnoreChanges(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 1 { @@ -3900,8 +3975,11 @@ func TestContext2Plan_taintIgnoreChanges(t *testing.T) { switch i := ric.Addr.String(); i { case "aws_instance.foo": - if res.Action != plans.DeleteThenCreate { - t.Fatalf("resource %s should be replaced", i) + if got, want := res.Action, plans.DeleteThenCreate; got != want { + t.Errorf("wrong action\ngot: %s\nwant: %s", got, want) + } + if got, want := res.ActionReason, plans.ResourceInstanceReplaceBecauseTainted; got != want { + t.Errorf("wrong action reason\ngot: %s\nwant: %s", got, want) } checkVals(t, objectVal(t, schema, map[string]cty.Value{ "id": cty.StringVal("foo"), @@ -3965,7 +4043,7 @@ func TestContext2Plan_taintDestroyInterpolatedCountRace(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 3 { @@ -3980,8 +4058,11 @@ func TestContext2Plan_taintDestroyInterpolatedCountRace(t *testing.T) { switch i := ric.Addr.String(); i { case "aws_instance.foo[0]": - if res.Action != plans.DeleteThenCreate { - t.Fatalf("resource %s should be replaced, not %s", i, res.Action) + if got, want := ric.Action, plans.DeleteThenCreate; got != want { + t.Errorf("wrong action\ngot: %s\nwant: %s", got, want) + } + if got, want := ric.ActionReason, plans.ResourceInstanceReplaceBecauseTainted; got != want { + t.Errorf("wrong action reason\ngot: %s\nwant: %s", got, want) } checkVals(t, objectVal(t, schema, map[string]cty.Value{ "id": cty.StringVal("bar"), @@ -4022,7 +4103,7 @@ func TestContext2Plan_targeted(t *testing.T) { if diags.HasErrors() { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 1 { @@ -4071,7 +4152,7 @@ func TestContext2Plan_targetedCrossModule(t *testing.T) { if diags.HasErrors() { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -4108,7 +4189,7 @@ func TestContext2Plan_targetedCrossModule(t *testing.T) { func TestContext2Plan_targetedModuleWithProvider(t *testing.T) { m := testModule(t, "plan-targeted-module-with-provider") p := testProvider("null") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "key": {Type: cty.String, Optional: true}, @@ -4119,8 +4200,7 @@ func TestContext2Plan_targetedModuleWithProvider(t *testing.T) { Attributes: map[string]*configschema.Attribute{}, }, }, - } - p.PlanResourceChangeFn = testDiffFn + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -4137,7 +4217,7 @@ func TestContext2Plan_targetedModuleWithProvider(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["null_resource"] + schema := p.GetProviderSchemaResponse.ResourceTypes["null_resource"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 1 { @@ -4158,7 +4238,6 @@ func TestContext2Plan_targetedModuleWithProvider(t *testing.T) { func TestContext2Plan_targetedOrphan(t *testing.T) { m := testModule(t, "plan-targeted-orphan") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -4184,8 +4263,8 @@ func TestContext2Plan_targetedOrphan(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, Targets: []addrs.Targetable{ addrs.RootModuleInstance.Resource( addrs.ManagedResourceMode, "aws_instance", "orphan", @@ -4198,7 +4277,7 @@ func TestContext2Plan_targetedOrphan(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 1 { @@ -4226,7 +4305,6 @@ func TestContext2Plan_targetedOrphan(t *testing.T) { func TestContext2Plan_targetedModuleOrphan(t *testing.T) { m := testModule(t, "plan-targeted-module-orphan") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn state := states.NewState() child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) @@ -4252,8 +4330,8 @@ func TestContext2Plan_targetedModuleOrphan(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - State: state, - Destroy: true, + State: state, + PlanMode: plans.DestroyMode, Targets: []addrs.Targetable{ addrs.RootModuleInstance.Child("child", addrs.NoKey).Resource( addrs.ManagedResourceMode, "aws_instance", "orphan", @@ -4266,7 +4344,7 @@ func TestContext2Plan_targetedModuleOrphan(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 1 { @@ -4309,7 +4387,7 @@ func TestContext2Plan_targetedModuleUntargetedVariable(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -4347,7 +4425,6 @@ func TestContext2Plan_targetedModuleUntargetedVariable(t *testing.T) { func TestContext2Plan_outputContainsTargetedResource(t *testing.T) { m := testModule(t, "plan-untargeted-resource-output") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -4383,7 +4460,6 @@ func TestContext2Plan_targetedOverTen(t *testing.T) { state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) - var expectedState []string for i := 0; i < 13; i++ { key := fmt.Sprintf("aws_instance.foo[%d]", i) id := fmt.Sprintf("i-abc%d", i) @@ -4397,7 +4473,6 @@ func TestContext2Plan_targetedOverTen(t *testing.T) { }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), ) - expectedState = append(expectedState, fmt.Sprintf("%s:\n ID = %s\n", key, id)) } ctx := testContext2(t, &ContextOpts{ @@ -4418,7 +4493,7 @@ func TestContext2Plan_targetedOverTen(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() for _, res := range plan.Changes.Resources { @@ -4435,10 +4510,9 @@ func TestContext2Plan_targetedOverTen(t *testing.T) { func TestContext2Plan_provider(t *testing.T) { m := testModule(t, "plan-provider") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn var value interface{} - p.ConfigureFn = func(req providers.ConfigureRequest) (resp providers.ConfigureResponse) { + p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { value = req.Config.GetAttr("foo").AsString() return } @@ -4517,7 +4591,7 @@ func TestContext2Plan_ignoreChanges(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 1 { @@ -4590,7 +4664,7 @@ func TestContext2Plan_ignoreChangesWildcard(t *testing.T) { func TestContext2Plan_ignoreChangesInMap(t *testing.T) { p := testProvider("test") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test_ignore_changes_map": { Attributes: map[string]*configschema.Attribute{ @@ -4598,15 +4672,13 @@ func TestContext2Plan_ignoreChangesInMap(t *testing.T) { }, }, }, - } + }) p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { return providers.PlanResourceChangeResponse{ PlannedState: req.ProposedNewState, } } - p.PlanResourceChangeFn = testDiffFn - s := states.BuildState(func(ss *states.SyncState) { ss.SetResourceInstanceCurrent( addrs.Resource{ @@ -4639,7 +4711,7 @@ func TestContext2Plan_ignoreChangesInMap(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["test_ignore_changes_map"] + schema := p.GetProviderSchemaResponse.ResourceTypes["test_ignore_changes_map"].Block ty := schema.ImpliedType() if got, want := len(plan.Changes.Resources), 1; got != want { @@ -4702,7 +4774,7 @@ func TestContext2Plan_ignoreChangesSensitive(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 1 { @@ -4729,7 +4801,7 @@ func TestContext2Plan_ignoreChangesSensitive(t *testing.T) { func TestContext2Plan_moduleMapLiteral(t *testing.T) { m := testModule(t, "plan-module-map-literal") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -4738,8 +4810,7 @@ func TestContext2Plan_moduleMapLiteral(t *testing.T) { }, }, }, - } - p.ApplyResourceChangeFn = testApplyFn + }) p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { s := req.ProposedNewState.AsValueMap() m := s["tags"].AsValueMap() @@ -4770,7 +4841,7 @@ func TestContext2Plan_moduleMapLiteral(t *testing.T) { func TestContext2Plan_computedValueInMap(t *testing.T) { m := testModule(t, "plan-computed-value-in-map") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -4783,8 +4854,7 @@ func TestContext2Plan_computedValueInMap(t *testing.T) { }, }, }, - } - p.PlanResourceChangeFn = testDiffFn + }) p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { resp = testDiffFn(req) @@ -4815,7 +4885,7 @@ func TestContext2Plan_computedValueInMap(t *testing.T) { } for _, res := range plan.Changes.Resources { - schema := p.GetSchemaReturn.ResourceTypes[res.Addr.Resource.Resource.Type] + schema := p.GetProviderSchemaResponse.ResourceTypes[res.Addr.Resource.Resource.Type].Block ric, err := res.Decode(schema.ImpliedType()) if err != nil { @@ -4844,8 +4914,7 @@ func TestContext2Plan_computedValueInMap(t *testing.T) { func TestContext2Plan_moduleVariableFromSplat(t *testing.T) { m := testModule(t, "plan-module-variable-from-splat") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -4853,7 +4922,7 @@ func TestContext2Plan_moduleVariableFromSplat(t *testing.T) { }, }, }, - } + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -4872,7 +4941,7 @@ func TestContext2Plan_moduleVariableFromSplat(t *testing.T) { } for _, res := range plan.Changes.Resources { - schema := p.GetSchemaReturn.ResourceTypes[res.Addr.Resource.Resource.Type] + schema := p.GetProviderSchemaResponse.ResourceTypes[res.Addr.Resource.Resource.Type].Block ric, err := res.Decode(schema.ImpliedType()) if err != nil { @@ -4900,7 +4969,7 @@ func TestContext2Plan_moduleVariableFromSplat(t *testing.T) { func TestContext2Plan_createBeforeDestroy_depends_datasource(t *testing.T) { m := testModule(t, "plan-cbd-depends-datasource") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -4917,7 +4986,7 @@ func TestContext2Plan_createBeforeDestroy_depends_datasource(t *testing.T) { }, }, }, - } + }) p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { computedVal := req.ProposedNewState.GetAttr("computed") if computedVal.IsNull() { @@ -4955,9 +5024,9 @@ func TestContext2Plan_createBeforeDestroy_depends_datasource(t *testing.T) { var schema *configschema.Block switch res.Addr.Resource.Resource.Mode { case addrs.DataResourceMode: - schema = p.GetSchemaReturn.DataSources[res.Addr.Resource.Resource.Type] + schema = p.GetProviderSchemaResponse.DataSources[res.Addr.Resource.Resource.Type].Block case addrs.ManagedResourceMode: - schema = p.GetSchemaReturn.ResourceTypes[res.Addr.Resource.Resource.Type] + schema = p.GetProviderSchemaResponse.ResourceTypes[res.Addr.Resource.Resource.Type].Block } ric, err := res.Decode(schema.ImpliedType()) @@ -5004,9 +5073,7 @@ func TestContext2Plan_createBeforeDestroy_depends_datasource(t *testing.T) { func TestContext2Plan_listOrder(t *testing.T) { m := testModule(t, "plan-list-order") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -5014,7 +5081,7 @@ func TestContext2Plan_listOrder(t *testing.T) { }, }, }, - } + }) ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -5051,8 +5118,7 @@ func TestContext2Plan_listOrder(t *testing.T) { func TestContext2Plan_ignoreChangesWithFlatmaps(t *testing.T) { m := testModule(t, "plan-ignore-changes-with-flatmaps") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -5068,7 +5134,7 @@ func TestContext2Plan_ignoreChangesWithFlatmaps(t *testing.T) { }, }, }, - } + }) state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -5103,7 +5169,7 @@ func TestContext2Plan_ignoreChangesWithFlatmaps(t *testing.T) { } res := plan.Changes.Resources[0] - schema := p.GetSchemaReturn.ResourceTypes[res.Addr.Resource.Resource.Type] + schema := p.GetProviderSchemaResponse.ResourceTypes[res.Addr.Resource.Resource.Type].Block ric, err := res.Decode(schema.ImpliedType()) if err != nil { @@ -5235,8 +5301,7 @@ func TestContext2Plan_resourceNestedCount(t *testing.T) { func TestContext2Plan_computedAttrRefTypeMismatch(t *testing.T) { m := testModule(t, "plan-computed-attr-ref-type-mismatch") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn - p.ValidateResourceTypeConfigFn = func(req providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse { + p.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { var diags tfdiags.Diagnostics if req.TypeName == "aws_instance" { amiVal := req.Config.GetAttr("ami") @@ -5244,11 +5309,10 @@ func TestContext2Plan_computedAttrRefTypeMismatch(t *testing.T) { diags = diags.Append(fmt.Errorf("Expected ami to be cty.String, got %#v", amiVal)) } } - return providers.ValidateResourceTypeConfigResponse{ + return providers.ValidateResourceConfigResponse{ Diagnostics: diags, } } - p.PlanResourceChangeFn = testDiffFn p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { if req.TypeName != "aws_ami_list" { t.Fatalf("Reached apply for unexpected resource type! %s", req.TypeName) @@ -5284,7 +5348,7 @@ func TestContext2Plan_computedAttrRefTypeMismatch(t *testing.T) { func TestContext2Plan_selfRef(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -5292,7 +5356,7 @@ func TestContext2Plan_selfRef(t *testing.T) { }, }, }, - } + }) m := testModule(t, "plan-self-ref") c := testContext2(t, &ContextOpts{ @@ -5321,7 +5385,7 @@ func TestContext2Plan_selfRef(t *testing.T) { func TestContext2Plan_selfRefMulti(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -5329,7 +5393,7 @@ func TestContext2Plan_selfRefMulti(t *testing.T) { }, }, }, - } + }) m := testModule(t, "plan-self-ref-multi") c := testContext2(t, &ContextOpts{ @@ -5358,7 +5422,7 @@ func TestContext2Plan_selfRefMulti(t *testing.T) { func TestContext2Plan_selfRefMultiAll(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -5366,7 +5430,7 @@ func TestContext2Plan_selfRefMultiAll(t *testing.T) { }, }, }, - } + }) m := testModule(t, "plan-self-ref-multi-all") c := testContext2(t, &ContextOpts{ @@ -5408,7 +5472,7 @@ output "out" { }) p := testProvider("aws") - p.ReadDataSourceResponse = providers.ReadDataSourceResponse{ + p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ State: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("data_id"), "foo": cty.StringVal("foo"), @@ -5455,7 +5519,7 @@ resource "aws_instance" "foo" { }) p := testProvider("aws") - p.ReadDataSourceResponse = providers.ReadDataSourceResponse{ + p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ State: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("data_id"), "foo": cty.StringVal("foo"), @@ -5496,7 +5560,7 @@ resource "aws_instance" "foo" { }) p := testProvider("aws") - p.ValidateResourceTypeConfigFn = func(req providers.ValidateResourceTypeConfigRequest) (resp providers.ValidateResourceTypeConfigResponse) { + p.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) { foo := req.Config.GetAttr("foo").AsString() if foo == "bar" { resp.Diagnostics = resp.Diagnostics.Append(errors.New("foo cannot be bar")) @@ -5544,7 +5608,7 @@ func TestContext2Plan_variableSensitivity(t *testing.T) { if diags.HasErrors() { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 1 { @@ -5599,13 +5663,19 @@ func TestContext2Plan_variableSensitivityModule(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, + Variables: InputValues{ + "another_var": &InputValue{ + Value: cty.StringVal("boop"), + SourceType: ValueFromCaller, + }, + }, }) plan, diags := ctx.Plan() if diags.HasErrors() { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 1 { @@ -5624,21 +5694,32 @@ func TestContext2Plan_variableSensitivityModule(t *testing.T) { switch i := ric.Addr.String(); i { case "module.child.aws_instance.foo": checkVals(t, objectVal(t, schema, map[string]cty.Value{ - "foo": cty.StringVal("foo"), + "foo": cty.StringVal("foo"), + "value": cty.StringVal("boop"), }), ric.After) if len(res.ChangeSrc.BeforeValMarks) != 0 { t.Errorf("unexpected BeforeValMarks: %#v", res.ChangeSrc.BeforeValMarks) } - if len(res.ChangeSrc.AfterValMarks) != 1 { - t.Errorf("unexpected AfterValMarks: %#v", res.ChangeSrc.AfterValMarks) + if len(res.ChangeSrc.AfterValMarks) != 2 { + t.Errorf("expected AfterValMarks to contain two elements: %#v", res.ChangeSrc.AfterValMarks) continue } - pvm := res.ChangeSrc.AfterValMarks[0] - if got, want := pvm.Path, cty.GetAttrPath("foo"); !got.Equals(want) { - t.Errorf("unexpected path for mark\n got: %#v\nwant: %#v", got, want) + // validate that the after marks have "foo" and "value" + contains := func(pvmSlice []cty.PathValueMarks, stepName string) bool { + for _, pvm := range pvmSlice { + if pvm.Path.Equals(cty.GetAttrPath(stepName)) { + if pvm.Marks.Equal(cty.NewValueMarks("sensitive")) { + return true + } + } + } + return false } - if got, want := pvm.Marks, cty.NewValueMarks("sensitive"); !got.Equal(want) { - t.Errorf("unexpected value for mark\n got: %#v\nwant: %#v", got, want) + if !contains(res.ChangeSrc.AfterValMarks, "foo") { + t.Error("unexpected AfterValMarks to contain \"foo\" with sensitive mark") + } + if !contains(res.ChangeSrc.AfterValMarks, "value") { + t.Error("unexpected AfterValMarks to contain \"value\" with sensitive mark") } default: t.Fatal("unknown instance:", i) @@ -5667,7 +5748,7 @@ func objectVal(t *testing.T, schema *configschema.Block, m map[string]cty.Value) func TestContext2Plan_requiredModuleOutput(t *testing.T) { m := testModule(t, "plan-required-output") p := testProvider("test") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test_resource": { Attributes: map[string]*configschema.Attribute{ @@ -5676,8 +5757,7 @@ func TestContext2Plan_requiredModuleOutput(t *testing.T) { }, }, }, - } - p.PlanResourceChangeFn = testDiffFn + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -5691,7 +5771,7 @@ func TestContext2Plan_requiredModuleOutput(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["test_resource"] + schema := p.GetProviderSchemaResponse.ResourceTypes["test_resource"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -5732,7 +5812,7 @@ func TestContext2Plan_requiredModuleOutput(t *testing.T) { func TestContext2Plan_requiredModuleObject(t *testing.T) { m := testModule(t, "plan-required-whole-mod") p := testProvider("test") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test_resource": { Attributes: map[string]*configschema.Attribute{ @@ -5741,8 +5821,7 @@ func TestContext2Plan_requiredModuleObject(t *testing.T) { }, }, }, - } - p.PlanResourceChangeFn = testDiffFn + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -5756,7 +5835,7 @@ func TestContext2Plan_requiredModuleObject(t *testing.T) { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["test_resource"] + schema := p.GetProviderSchemaResponse.ResourceTypes["test_resource"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 2 { @@ -5889,7 +5968,6 @@ output"out" { }) p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -5919,7 +5997,6 @@ resource "aws_instance" "foo" { }) p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn targets := []addrs.Targetable{} target, diags := addrs.ParseTargetStr("module.mod[1].aws_instance.foo[0]") @@ -5983,7 +6060,6 @@ resource "aws_instance" "foo" { }) p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn target, diags := addrs.ParseTargetStr("module.mod[1].aws_instance.foo") if diags.HasErrors() { @@ -6053,7 +6129,6 @@ resource "aws_instance" "foo" { }) p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -6076,7 +6151,7 @@ data "test_data_source" "foo" {} }) p := new(MockProvider) - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ DataSources: map[string]*configschema.Block{ "test_data_source": { Attributes: map[string]*configschema.Attribute{ @@ -6091,9 +6166,9 @@ data "test_data_source" "foo" {} }, }, }, - } + }) - p.ReadDataSourceResponse = providers.ReadDataSourceResponse{ + p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ State: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("data_id"), "foo": cty.StringVal("foo"), @@ -6134,8 +6209,6 @@ data "test_data_source" "foo" {} // for_each can reference a resource with 0 instances func TestContext2Plan_scaleInForEach(t *testing.T) { p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn m := testModuleInline(t, map[string]string{ "main.tf": ` @@ -6203,7 +6276,7 @@ func TestContext2Plan_targetedModuleInstance(t *testing.T) { if diags.HasErrors() { t.Fatalf("unexpected errors: %s", diags.Err()) } - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() if len(plan.Changes.Resources) != 1 { @@ -6240,7 +6313,7 @@ data "test_data_source" "d" { `}) p := testProvider("test") - p.ReadDataSourceResponse = providers.ReadDataSourceResponse{ + p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ State: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("this"), "foo": cty.NullVal(cty.String), @@ -6259,9 +6332,9 @@ data "test_data_source" "d" { t.Fatal(diags.ErrWithWarnings()) } - d := plan.State.ResourceInstance(mustResourceInstanceAddr("data.test_data_source.d")) + d := plan.PriorState.ResourceInstance(mustResourceInstanceAddr("data.test_data_source.d")) if d == nil || d.Current == nil { - t.Fatal("data.test_data_source.d not found in state:", plan.State) + t.Fatal("data.test_data_source.d not found in state:", plan.PriorState) } if d.Current.Status != states.ObjectReady { @@ -6271,8 +6344,6 @@ data "test_data_source" "d" { func TestContext2Plan_dataReferencesResource(t *testing.T) { p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("data source should not be read")) @@ -6320,7 +6391,6 @@ data "test_data_source" "e" { func TestContext2Plan_skipRefresh(t *testing.T) { p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn p.PlanResourceChangeFn = testDiffFn m := testModuleInline(t, map[string]string{ @@ -6366,8 +6436,6 @@ resource "test_instance" "a" { func TestContext2Plan_dataInModuleDependsOn(t *testing.T) { p := testProvider("test") - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn readDataSourceB := false p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { @@ -6421,3 +6489,362 @@ data "test_data_source" "b" { t.Fatal("data source b was not read during plan") } } + +func TestContext2Plan_rpcDiagnostics(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_instance" "a" { +} +`, + }) + + p := testProvider("test") + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { + resp := testDiffFn(req) + resp.Diagnostics = resp.Diagnostics.Append(tfdiags.SimpleWarning("don't frobble")) + return resp + } + + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ + ResourceTypes: map[string]*configschema.Block{ + "test_instance": { + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + }, + }, + }, + }) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + _, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + if len(diags) == 0 { + t.Fatal("expected warnings") + } + + for _, d := range diags { + des := d.Description().Summary + if !strings.Contains(des, "frobble") { + t.Fatalf(`expected frobble, got %q`, des) + } + } +} + +// ignore_changes needs to be re-applied to the planned value for provider +// using the LegacyTypeSystem +func TestContext2Plan_legacyProviderIgnoreChanges(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_instance" "a" { + lifecycle { + ignore_changes = [data] + } +} +`, + }) + + p := testProvider("test") + p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { + m := req.ProposedNewState.AsValueMap() + // this provider "hashes" the data attribute as bar + m["data"] = cty.StringVal("bar") + + resp.PlannedState = cty.ObjectVal(m) + resp.LegacyTypeSystem = true + return resp + } + + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ + ResourceTypes: map[string]*configschema.Block{ + "test_instance": { + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + "data": {Type: cty.String, Optional: true}, + }, + }, + }, + }) + + state := states.NewState() + root := state.EnsureModule(addrs.RootModuleInstance) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("test_instance.a").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"a","data":"foo"}`), + Dependencies: []addrs.ConfigResource{}, + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + State: state, + }) + plan, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + for _, c := range plan.Changes.Resources { + if c.Action != plans.NoOp { + t.Fatalf("expected no changes, got %s for %q", c.Action, c.Addr) + } + } +} + +func TestContext2Plan_validateIgnoreAll(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_instance" "a" { + lifecycle { + ignore_changes = all + } +} +`, + }) + + p := testProvider("test") + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ + ResourceTypes: map[string]*configschema.Block{ + "test_instance": { + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + "data": {Type: cty.String, Optional: true}, + }, + }, + }, + }) + p.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { + var diags tfdiags.Diagnostics + if req.TypeName == "test_instance" { + if !req.Config.GetAttr("id").IsNull() { + diags = diags.Append(errors.New("id cannot be set in config")) + } + } + return providers.ValidateResourceConfigResponse{ + Diagnostics: diags, + } + } + + state := states.NewState() + root := state.EnsureModule(addrs.RootModuleInstance) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("test_instance.a").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"a","data":"foo"}`), + Dependencies: []addrs.ConfigResource{}, + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + State: state, + }) + _, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } +} + +func TestContext2Plan_dataRemovalNoProvider(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_instance" "a" { +} +`, + }) + + p := testProvider("test") + + state := states.NewState() + root := state.EnsureModule(addrs.RootModuleInstance) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("test_instance.a").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"a","data":"foo"}`), + Dependencies: []addrs.ConfigResource{}, + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + + // the provider for this data source is no longer in the config, but that + // should not matter for state removal. + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("data.test_data_source.d").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"d"}`), + Dependencies: []addrs.ConfigResource{}, + }, + mustProviderConfig(`provider["registry.terraform.io/local/test"]`), + ) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + // We still need to be able to locate the provider to decode the + // state, since we do not know during init that this provider is + // only used for an orphaned data source. + addrs.NewProvider("registry.terraform.io", "local", "test"): testProviderFuncFixed(p), + }, + State: state, + }) + _, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } +} + +func TestContext2Plan_noSensitivityChange(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +variable "sensitive_var" { + default = "hello" + sensitive = true +} + +resource "test_resource" "foo" { + value = var.sensitive_var + sensitive_value = var.sensitive_var +}`, + }) + + p := testProvider("test") + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + State: states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_resource", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"foo", "value":"hello", "sensitive_value":"hello"}`), + AttrSensitivePaths: []cty.PathValueMarks{ + {Path: cty.Path{cty.GetAttrStep{Name: "value"}}, Marks: cty.NewValueMarks("sensitive")}, + {Path: cty.Path{cty.GetAttrStep{Name: "sensitive_value"}}, Marks: cty.NewValueMarks("sensitive")}, + }, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + }), + }) + plan, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + for _, c := range plan.Changes.Resources { + if c.Action != plans.NoOp { + t.Fatalf("expected no changes, got %s for %q", c.Action, c.Addr) + } + } +} + +func TestContext2Plan_variableCustomValidationsSensitive(t *testing.T) { + m := testModule(t, "validate-variable-custom-validations-child-sensitive") + + p := testProvider("test") + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + _, diags := ctx.Plan() + if !diags.HasErrors() { + t.Fatal("succeeded; want errors") + } + if got, want := diags.Err().Error(), `Invalid value for variable: Value must not be "nope".`; !strings.Contains(got, want) { + t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) + } +} + +func TestContext2Plan_nullOutputNoOp(t *testing.T) { + // this should always plan a NoOp change for the output + m := testModuleInline(t, map[string]string{ + "main.tf": ` +output "planned" { + value = false ? 1 : null +} +`, + }) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + State: states.BuildState(func(s *states.SyncState) { + r := s.Module(addrs.RootModuleInstance) + r.SetOutputValue("planned", cty.NullVal(cty.DynamicPseudoType), false) + }), + }) + plan, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + for _, c := range plan.Changes.Outputs { + if c.Action != plans.NoOp { + t.Fatalf("expected no changes, got %s for %q", c.Action, c.Addr) + } + } +} + +func TestContext2Plan_createOutput(t *testing.T) { + // this should always plan a NoOp change for the output + m := testModuleInline(t, map[string]string{ + "main.tf": ` +output "planned" { + value = 1 +} +`, + }) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + State: states.NewState(), + }) + plan, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + for _, c := range plan.Changes.Outputs { + if c.Action != plans.Create { + t.Fatalf("expected Create change, got %s for %q", c.Action, c.Addr) + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// NOTE: Due to the size of this file, new tests should be added to +// context_plan2_test.go. +//////////////////////////////////////////////////////////////////////////////// diff --git a/vendor/github.com/hashicorp/terraform/terraform/context_refresh_test.go b/vendor/github.com/hashicorp/terraform/terraform/context_refresh_test.go index 77535d73..c578bfcb 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/context_refresh_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/context_refresh_test.go @@ -41,18 +41,16 @@ func TestContext2Refresh(t *testing.T) { State: state, }) - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() readState, err := hcl2shim.HCL2ValueFromFlatmap(map[string]string{"id": "foo", "foo": "baz"}, ty) if err != nil { t.Fatal(err) } - p.ReadResourceFn = nil - p.ReadResourceResponse = providers.ReadResourceResponse{ + p.ReadResourceResponse = &providers.ReadResourceResponse{ NewState: readState, } - p.PlanResourceChangeFn = testDiffFn s, diags := ctx.Refresh() if diags.HasErrors() { @@ -105,7 +103,7 @@ func TestContext2Refresh_dynamicAttr(t *testing.T) { }) p := testProvider("test") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test_instance": { Attributes: map[string]*configschema.Attribute{ @@ -113,7 +111,7 @@ func TestContext2Refresh_dynamicAttr(t *testing.T) { }, }, }, - } + }) p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { return providers.ReadResourceResponse{ NewState: readStateVal, @@ -132,7 +130,7 @@ func TestContext2Refresh_dynamicAttr(t *testing.T) { State: startingState, }) - schema := p.GetSchemaReturn.ResourceTypes["test_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["test_instance"].Block ty := schema.ImpliedType() s, diags := ctx.Refresh() @@ -169,7 +167,7 @@ func TestContext2Refresh_dataComputedModuleVar(t *testing.T) { return resp } - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{}, ResourceTypes: map[string]*configschema.Block{ "aws_instance": { @@ -199,7 +197,7 @@ func TestContext2Refresh_dataComputedModuleVar(t *testing.T) { }, }, }, - } + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -220,7 +218,7 @@ func TestContext2Refresh_dataComputedModuleVar(t *testing.T) { func TestContext2Refresh_targeted(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{}, ResourceTypes: map[string]*configschema.Block{ "aws_elb": { @@ -252,7 +250,7 @@ func TestContext2Refresh_targeted(t *testing.T) { }, }, }, - } + }) state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -282,7 +280,6 @@ func TestContext2Refresh_targeted(t *testing.T) { NewState: req.PriorState, } } - p.PlanResourceChangeFn = testDiffFn _, diags := ctx.Refresh() if diags.HasErrors() { @@ -297,7 +294,7 @@ func TestContext2Refresh_targeted(t *testing.T) { func TestContext2Refresh_targetedCount(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{}, ResourceTypes: map[string]*configschema.Block{ "aws_elb": { @@ -329,7 +326,7 @@ func TestContext2Refresh_targetedCount(t *testing.T) { }, }, }, - } + }) state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -361,7 +358,6 @@ func TestContext2Refresh_targetedCount(t *testing.T) { NewState: req.PriorState, } } - p.PlanResourceChangeFn = testDiffFn _, diags := ctx.Refresh() if diags.HasErrors() { @@ -384,7 +380,7 @@ func TestContext2Refresh_targetedCount(t *testing.T) { func TestContext2Refresh_targetedCountIndex(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{}, ResourceTypes: map[string]*configschema.Block{ "aws_elb": { @@ -416,7 +412,7 @@ func TestContext2Refresh_targetedCountIndex(t *testing.T) { }, }, }, - } + }) state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -448,7 +444,6 @@ func TestContext2Refresh_targetedCountIndex(t *testing.T) { NewState: req.PriorState, } } - p.PlanResourceChangeFn = testDiffFn _, diags := ctx.Refresh() if diags.HasErrors() { @@ -463,7 +458,7 @@ func TestContext2Refresh_targetedCountIndex(t *testing.T) { func TestContext2Refresh_moduleComputedVar(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{}, ResourceTypes: map[string]*configschema.Block{ "aws_instance": { @@ -479,8 +474,7 @@ func TestContext2Refresh_moduleComputedVar(t *testing.T) { }, }, }, - } - p.PlanResourceChangeFn = testDiffFn + }) m := testModule(t, "refresh-module-computed-var") ctx := testContext2(t, &ContextOpts{ @@ -513,11 +507,9 @@ func TestContext2Refresh_delete(t *testing.T) { State: state, }) - p.ReadResourceFn = nil - p.ReadResourceResponse = providers.ReadResourceResponse{ - NewState: cty.NullVal(p.GetSchemaReturn.ResourceTypes["aws_instance"].ImpliedType()), + p.ReadResourceResponse = &providers.ReadResourceResponse{ + NewState: cty.NullVal(p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block.ImpliedType()), } - p.PlanResourceChangeFn = testDiffFn s, diags := ctx.Refresh() if diags.HasErrors() { @@ -541,13 +533,11 @@ func TestContext2Refresh_ignoreUncreated(t *testing.T) { State: nil, }) - p.ReadResourceFn = nil - p.ReadResourceResponse = providers.ReadResourceResponse{ + p.ReadResourceResponse = &providers.ReadResourceResponse{ NewState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("foo"), }), } - p.PlanResourceChangeFn = testDiffFn _, diags := ctx.Refresh() if diags.HasErrors() { @@ -561,7 +551,6 @@ func TestContext2Refresh_ignoreUncreated(t *testing.T) { func TestContext2Refresh_hook(t *testing.T) { h := new(MockHook) p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn m := testModule(t, "refresh-basic") state := states.NewState() @@ -623,7 +612,6 @@ func TestContext2Refresh_modules(t *testing.T) { NewState: new, } } - p.PlanResourceChangeFn = testDiffFn s, diags := ctx.Refresh() if diags.HasErrors() { @@ -640,8 +628,7 @@ func TestContext2Refresh_modules(t *testing.T) { func TestContext2Refresh_moduleInputComputedOutput(t *testing.T) { m := testModule(t, "refresh-module-input-computed-output") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{}, ResourceTypes: map[string]*configschema.Block{ "aws_instance": { @@ -658,7 +645,7 @@ func TestContext2Refresh_moduleInputComputedOutput(t *testing.T) { }, }, }, - } + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -675,7 +662,6 @@ func TestContext2Refresh_moduleInputComputedOutput(t *testing.T) { func TestContext2Refresh_moduleVarModule(t *testing.T) { m := testModule(t, "refresh-module-var-module") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -699,13 +685,11 @@ func TestContext2Refresh_noState(t *testing.T) { }, }) - p.ReadResourceFn = nil - p.ReadResourceResponse = providers.ReadResourceResponse{ + p.ReadResourceResponse = &providers.ReadResourceResponse{ NewState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("foo"), }), } - p.PlanResourceChangeFn = testDiffFn if _, diags := ctx.Refresh(); diags.HasErrors() { t.Fatalf("refresh errs: %s", diags.Err()) @@ -714,7 +698,8 @@ func TestContext2Refresh_noState(t *testing.T) { func TestContext2Refresh_output(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.PlanResourceChangeFn = testDiffFn + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{}, ResourceTypes: map[string]*configschema.Block{ "aws_instance": { @@ -731,8 +716,7 @@ func TestContext2Refresh_output(t *testing.T) { }, }, }, - } - p.PlanResourceChangeFn = testDiffFn + }) m := testModule(t, "refresh-output") @@ -768,9 +752,8 @@ func TestContext2Refresh_outputPartial(t *testing.T) { // Refresh creates a partial plan for any instances that don't have // remote objects yet, to get stub values for interpolation. Therefore // we need to make DiffFn available to let that complete. - p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{}, ResourceTypes: map[string]*configschema.Block{ "aws_instance": { @@ -782,11 +765,10 @@ func TestContext2Refresh_outputPartial(t *testing.T) { }, }, }, - } + }) - p.ReadResourceFn = nil - p.ReadResourceResponse = providers.ReadResourceResponse{ - NewState: cty.NullVal(p.GetSchemaReturn.ResourceTypes["aws_instance"].ImpliedType()), + p.ReadResourceResponse = &providers.ReadResourceResponse{ + NewState: cty.NullVal(p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block.ImpliedType()), } state := states.NewState() @@ -829,7 +811,7 @@ func TestContext2Refresh_stateBasic(t *testing.T) { State: state, }) - schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] + schema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block ty := schema.ImpliedType() readStateVal, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ @@ -839,9 +821,7 @@ func TestContext2Refresh_stateBasic(t *testing.T) { t.Fatal(err) } - p.ReadResourceFn = nil - p.PlanResourceChangeFn = testDiffFn - p.ReadResourceResponse = providers.ReadResourceResponse{ + p.ReadResourceResponse = &providers.ReadResourceResponse{ NewState: readStateVal, } @@ -875,7 +855,7 @@ func TestContext2Refresh_dataCount(t *testing.T) { resp.PlannedState = cty.ObjectVal(m) return resp } - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test": { Attributes: map[string]*configschema.Attribute{ @@ -887,7 +867,7 @@ func TestContext2Refresh_dataCount(t *testing.T) { DataSources: map[string]*configschema.Block{ "test": {}, }, - } + }) p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { return providers.ReadDataSourceResponse{ @@ -924,12 +904,12 @@ func TestContext2Refresh_dataState(t *testing.T) { } p := testProvider("null") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{}, DataSources: map[string]*configschema.Block{ "null_data_source": schema, }, - } + }) ctx := testContext2(t, &ContextOpts{ Config: m, @@ -949,7 +929,6 @@ func TestContext2Refresh_dataState(t *testing.T) { State: readStateVal, } } - p.PlanResourceChangeFn = testDiffFn s, diags := ctx.Refresh() if diags.HasErrors() { @@ -974,7 +953,7 @@ func TestContext2Refresh_dataState(t *testing.T) { func TestContext2Refresh_dataStateRefData(t *testing.T) { p := testProvider("null") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{}, DataSources: map[string]*configschema.Block{ "null_data_source": { @@ -994,7 +973,7 @@ func TestContext2Refresh_dataStateRefData(t *testing.T) { }, }, }, - } + }) m := testModule(t, "refresh-data-ref-data") state := states.NewState() @@ -1015,7 +994,6 @@ func TestContext2Refresh_dataStateRefData(t *testing.T) { State: cty.ObjectVal(m), } } - p.PlanResourceChangeFn = testDiffFn s, diags := ctx.Refresh() if diags.HasErrors() { @@ -1053,7 +1031,6 @@ func TestContext2Refresh_tainted(t *testing.T) { NewState: cty.ObjectVal(m), } } - p.PlanResourceChangeFn = testDiffFn s, diags := ctx.Refresh() if diags.HasErrors() { @@ -1076,9 +1053,6 @@ func TestContext2Refresh_tainted(t *testing.T) { // Providers was _empty_. func TestContext2Refresh_unknownProvider(t *testing.T) { m := testModule(t, "refresh-unknown-provider") - p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -1094,7 +1068,7 @@ func TestContext2Refresh_unknownProvider(t *testing.T) { t.Fatal("successfully created context; want error") } - if !regexp.MustCompile(`Failed to instantiate provider ".+"`).MatchString(diags.Err().Error()) { + if !regexp.MustCompile(`failed to instantiate provider ".+"`).MatchString(diags.Err().Error()) { t.Fatalf("wrong error: %s", diags.Err()) } } @@ -1115,10 +1089,10 @@ func TestContext2Refresh_vars(t *testing.T) { }, } - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{}, ResourceTypes: map[string]*configschema.Block{"aws_instance": schema}, - } + }) m := testModule(t, "refresh-vars") state := states.NewState() @@ -1140,9 +1114,7 @@ func TestContext2Refresh_vars(t *testing.T) { t.Fatal(err) } - p.ReadResourceFn = nil - p.PlanResourceChangeFn = testDiffFn - p.ReadResourceResponse = providers.ReadResourceResponse{ + p.ReadResourceResponse = &providers.ReadResourceResponse{ NewState: readStateVal, } @@ -1195,7 +1167,6 @@ func TestContext2Refresh_orphanModule(t *testing.T) { NewState: req.PriorState, } } - p.PlanResourceChangeFn = testDiffFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -1248,7 +1219,7 @@ func TestContext2Refresh_orphanModule(t *testing.T) { func TestContext2Validate(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{}, ResourceTypes: map[string]*configschema.Block{ "aws_instance": { @@ -1264,8 +1235,7 @@ func TestContext2Validate(t *testing.T) { }, }, }, - } - p.PlanResourceChangeFn = testDiffFn + }) m := testModule(t, "validate-good") c := testContext2(t, &ContextOpts{ @@ -1284,8 +1254,6 @@ func TestContext2Validate(t *testing.T) { func TestContext2Refresh_updateProviderInState(t *testing.T) { m := testModule(t, "update-resource-provider") p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn - p.ApplyResourceChangeFn = testApplyFn state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) @@ -1318,7 +1286,7 @@ aws_instance.bar: func TestContext2Refresh_schemaUpgradeFlatmap(t *testing.T) { m := testModule(t, "refresh-schema-upgrade") p := testProvider("test") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test_thing": { Attributes: map[string]*configschema.Attribute{ @@ -1332,13 +1300,12 @@ func TestContext2Refresh_schemaUpgradeFlatmap(t *testing.T) { ResourceTypeSchemaVersions: map[string]uint64{ "test_thing": 5, }, - } - p.UpgradeResourceStateResponse = providers.UpgradeResourceStateResponse{ + }) + p.UpgradeResourceStateResponse = &providers.UpgradeResourceStateResponse{ UpgradedState: cty.ObjectVal(map[string]cty.Value{ "name": cty.StringVal("foo"), }), } - p.PlanResourceChangeFn = testDiffFn s := states.BuildState(func(s *states.SyncState) { s.SetResourceInstanceCurrent( @@ -1405,7 +1372,7 @@ test_thing.bar: func TestContext2Refresh_schemaUpgradeJSON(t *testing.T) { m := testModule(t, "refresh-schema-upgrade") p := testProvider("test") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test_thing": { Attributes: map[string]*configschema.Attribute{ @@ -1419,13 +1386,12 @@ func TestContext2Refresh_schemaUpgradeJSON(t *testing.T) { ResourceTypeSchemaVersions: map[string]uint64{ "test_thing": 5, }, - } - p.UpgradeResourceStateResponse = providers.UpgradeResourceStateResponse{ + }) + p.UpgradeResourceStateResponse = &providers.UpgradeResourceStateResponse{ UpgradedState: cty.ObjectVal(map[string]cty.Value{ "name": cty.StringVal("foo"), }), } - p.PlanResourceChangeFn = testDiffFn s := states.BuildState(func(s *states.SyncState) { s.SetResourceInstanceCurrent( @@ -1503,7 +1469,6 @@ data "aws_data_source" "foo" { resp.State = req.Config return } - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -1519,7 +1484,7 @@ data "aws_data_source" "foo" { t.Fatal(diags.Err()) } - if !p.ValidateDataSourceConfigCalled { + if !p.ValidateDataResourceConfigCalled { t.Fatal("ValidateDataSourceConfig not called during plan") } } @@ -1527,7 +1492,7 @@ data "aws_data_source" "foo" { func TestContext2Refresh_dataResourceDependsOn(t *testing.T) { m := testModule(t, "plan-data-depends-on") p := testProvider("test") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "test_resource": { Attributes: map[string]*configschema.Attribute{ @@ -1543,9 +1508,8 @@ func TestContext2Refresh_dataResourceDependsOn(t *testing.T) { }, }, }, - } - p.PlanResourceChangeFn = testDiffFn - p.ReadDataSourceResponse = providers.ReadDataSourceResponse{ + }) + p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{ State: cty.ObjectVal(map[string]cty.Value{ "compute": cty.StringVal("value"), }), @@ -1569,62 +1533,10 @@ func TestContext2Refresh_dataResourceDependsOn(t *testing.T) { } } -// verify that dependencies are updated in the state during refresh -func TestRefresh_updateDependencies(t *testing.T) { +// verify that create_before_destroy is updated in the state during refresh +func TestRefresh_updateLifecycle(t *testing.T) { state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) - root.SetResourceInstanceCurrent( - addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "aws_instance", - Name: "foo", - }.Instance(addrs.NoKey), - &states.ResourceInstanceObjectSrc{ - Status: states.ObjectReady, - AttrsJSON: []byte(`{"id":"foo"}`), - Dependencies: []addrs.ConfigResource{ - // Existing dependencies should be removed when overridden by the config - { - Module: addrs.RootModule, - Resource: addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "aws_instance", - Name: "baz", - }, - }, - }, - }, - addrs.AbsProviderConfig{ - Provider: addrs.NewDefaultProvider("aws"), - Module: addrs.RootModule, - }, - ) - root.SetResourceInstanceCurrent( - addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "aws_instance", - Name: "baz", - }.Instance(addrs.NoKey), - &states.ResourceInstanceObjectSrc{ - Status: states.ObjectReady, - AttrsJSON: []byte(`{"id":"baz"}`), - Dependencies: []addrs.ConfigResource{ - // Existing dependencies should not be removed from orphaned instances - { - Module: addrs.RootModule, - Resource: addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "aws_instance", - Name: "bam", - }, - }, - }, - }, - addrs.AbsProviderConfig{ - Provider: addrs.NewDefaultProvider("aws"), - Module: addrs.RootModule, - }, - ) root.SetResourceInstanceCurrent( addrs.Resource{ Mode: addrs.ManagedResourceMode, @@ -1633,7 +1545,7 @@ func TestRefresh_updateDependencies(t *testing.T) { }.Instance(addrs.NoKey), &states.ResourceInstanceObjectSrc{ Status: states.ObjectReady, - AttrsJSON: []byte(`{"id":"bar","foo":"foo"}`), + AttrsJSON: []byte(`{"id":"bar"}`), }, addrs.AbsProviderConfig{ Provider: addrs.NewDefaultProvider("aws"), @@ -1644,17 +1556,14 @@ func TestRefresh_updateDependencies(t *testing.T) { m := testModuleInline(t, map[string]string{ "main.tf": ` resource "aws_instance" "bar" { - foo = aws_instance.foo.id -} - -resource "aws_instance" "foo" { + lifecycle { + create_before_destroy = true + } } `, }) p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -1664,82 +1573,64 @@ resource "aws_instance" "foo" { State: state, }) - result, diags := ctx.Refresh() + state, diags := ctx.Refresh() if diags.HasErrors() { t.Fatalf("plan errors: %s", diags.Err()) } - expect := strings.TrimSpace(` -aws_instance.bar: - ID = bar - provider = provider["registry.terraform.io/hashicorp/aws"] - foo = foo - - Dependencies: - aws_instance.foo -aws_instance.baz: - ID = baz - provider = provider["registry.terraform.io/hashicorp/aws"] - - Dependencies: - aws_instance.bam -aws_instance.foo: - ID = foo - provider = provider["registry.terraform.io/hashicorp/aws"] -`) - - checkStateString(t, result, expect) + r := state.ResourceInstance(mustResourceInstanceAddr("aws_instance.bar")) + if !r.Current.CreateBeforeDestroy { + t.Fatal("create_before_destroy not updated in instance state") + } } -// verify that create_before_destroy is updated in the state during refresh -func TestRefresh_updateLifecycle(t *testing.T) { +func TestContext2Refresh_dataSourceOrphan(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ``, + }) + state := states.NewState() root := state.EnsureModule(addrs.RootModuleInstance) root.SetResourceInstanceCurrent( addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "aws_instance", - Name: "bar", + Mode: addrs.DataResourceMode, + Type: "test_data_source", + Name: "foo", }.Instance(addrs.NoKey), &states.ResourceInstanceObjectSrc{ - Status: states.ObjectReady, - AttrsJSON: []byte(`{"id":"bar"}`), + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"foo"}`), + Dependencies: []addrs.ConfigResource{}, }, addrs.AbsProviderConfig{ - Provider: addrs.NewDefaultProvider("aws"), + Provider: addrs.NewDefaultProvider("test"), Module: addrs.RootModule, }, ) - - m := testModuleInline(t, map[string]string{ - "main.tf": ` -resource "aws_instance" "bar" { - lifecycle { - create_before_destroy = true - } -} -`, - }) - - p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn + p := testProvider("test") + p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { + resp.State = cty.NullVal(req.Config.Type()) + return + } ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ - addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), }, State: state, }) - state, diags := ctx.Refresh() + _, diags := ctx.Refresh() if diags.HasErrors() { - t.Fatalf("plan errors: %s", diags.Err()) + t.Fatal(diags.Err()) } - r := state.ResourceInstance(mustResourceInstanceAddr("aws_instance.bar")) - if !r.Current.CreateBeforeDestroy { - t.Fatal("create_before_destroy not updated in instance state") + if p.ReadResourceCalled { + t.Fatal("there are no managed resources to read") + } + + if p.ReadDataSourceCalled { + t.Fatal("orphaned data source instance should not be read") } } diff --git a/vendor/github.com/hashicorp/terraform/terraform/context_test.go b/vendor/github.com/hashicorp/terraform/terraform/context_test.go index dde3ec5e..f07ea105 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/context_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/context_test.go @@ -15,10 +15,12 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs/configload" "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/configs/hcl2shim" + "github.com/hashicorp/terraform/internal/depsfile" "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/plans/planfile" "github.com/hashicorp/terraform/providers" @@ -117,6 +119,181 @@ func TestNewContextRequiredVersion(t *testing.T) { } } +func TestNewContext_lockedDependencies(t *testing.T) { + configBeepGreaterThanOne := ` +terraform { + required_providers { + beep = { + source = "example.com/foo/beep" + version = ">= 1.0.0" + } + } +} +` + configBeepLessThanOne := ` +terraform { + required_providers { + beep = { + source = "example.com/foo/beep" + version = "< 1.0.0" + } + } +} +` + configBuiltin := ` +terraform { + required_providers { + terraform = { + source = "terraform.io/builtin/terraform" + } + } +} +` + locksBeepGreaterThanOne := ` +provider "example.com/foo/beep" { + version = "1.0.0" + constraints = ">= 1.0.0" + hashes = [ + "h1:does-not-match", + ] +} +` + configBeepBoop := ` +terraform { + required_providers { + beep = { + source = "example.com/foo/beep" + version = "< 1.0.0" # different from locks + } + boop = { + source = "example.com/foo/boop" + version = ">= 2.0.0" + } + } +} +` + locksBeepBoop := ` +provider "example.com/foo/beep" { + version = "1.0.0" + constraints = ">= 1.0.0" + hashes = [ + "h1:does-not-match", + ] +} +provider "example.com/foo/boop" { + version = "2.3.4" + constraints = ">= 2.0.0" + hashes = [ + "h1:does-not-match", + ] +} +` + beepAddr := addrs.MustParseProviderSourceString("example.com/foo/beep") + boopAddr := addrs.MustParseProviderSourceString("example.com/foo/boop") + + testCases := map[string]struct { + Config string + LockFile string + DevProviders []addrs.Provider + WantErr string + }{ + "dependencies met": { + Config: configBeepGreaterThanOne, + LockFile: locksBeepGreaterThanOne, + }, + "no locks given": { + Config: configBeepGreaterThanOne, + }, + "builtin provider with empty locks": { + Config: configBuiltin, + LockFile: `# This file is maintained automatically by "terraform init".`, + }, + "multiple providers, one in development": { + Config: configBeepBoop, + LockFile: locksBeepBoop, + DevProviders: []addrs.Provider{beepAddr}, + }, + "development provider with empty locks": { + Config: configBeepGreaterThanOne, + LockFile: `# This file is maintained automatically by "terraform init".`, + DevProviders: []addrs.Provider{beepAddr}, + }, + "multiple providers, one in development, one missing": { + Config: configBeepBoop, + LockFile: locksBeepGreaterThanOne, + DevProviders: []addrs.Provider{beepAddr}, + WantErr: `Provider requirements cannot be satisfied by locked dependencies: The following required providers are not installed: + +- example.com/foo/boop (>= 2.0.0) + +Please run "terraform init".`, + }, + "wrong provider version": { + Config: configBeepLessThanOne, + LockFile: locksBeepGreaterThanOne, + WantErr: `Provider requirements cannot be satisfied by locked dependencies: The following required providers are not installed: + +- example.com/foo/beep (< 1.0.0) + +Please run "terraform init".`, + }, + "empty locks": { + Config: configBeepGreaterThanOne, + LockFile: `# This file is maintained automatically by "terraform init".`, + WantErr: `Provider requirements cannot be satisfied by locked dependencies: The following required providers are not installed: + +- example.com/foo/beep (>= 1.0.0) + +Please run "terraform init".`, + }, + } + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + var locks *depsfile.Locks + if tc.LockFile != "" { + var diags tfdiags.Diagnostics + locks, diags = depsfile.LoadLocksFromBytes([]byte(tc.LockFile), "test.lock.hcl") + if len(diags) > 0 { + t.Fatalf("unexpected error loading locks file: %s", diags.Err()) + } + } + devProviders := make(map[addrs.Provider]struct{}) + for _, provider := range tc.DevProviders { + devProviders[provider] = struct{}{} + } + opts := &ContextOpts{ + Config: testModuleInline(t, map[string]string{ + "main.tf": tc.Config, + }), + LockedDependencies: locks, + ProvidersInDevelopment: devProviders, + Providers: map[addrs.Provider]providers.Factory{ + beepAddr: testProviderFuncFixed(testProvider("beep")), + boopAddr: testProviderFuncFixed(testProvider("boop")), + addrs.NewBuiltInProvider("terraform"): testProviderFuncFixed(testProvider("terraform")), + }, + } + + ctx, diags := NewContext(opts) + if tc.WantErr != "" { + if len(diags) == 0 { + t.Fatal("expected diags but none returned") + } + if got, want := diags.Err().Error(), tc.WantErr; got != want { + t.Errorf("wrong diags\n got: %s\nwant: %s", got, want) + } + } else { + if len(diags) > 0 { + t.Errorf("unexpected diags: %s", diags.Err()) + } + if ctx == nil { + t.Error("ctx is nil") + } + } + }) + } +} + func testContext2(t *testing.T, opts *ContextOpts) *Context { t.Helper() @@ -224,11 +401,7 @@ func testDiffFn(req providers.PlanResourceChangeRequest) (resp providers.PlanRes func testProvider(prefix string) *MockProvider { p := new(MockProvider) - p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse { - return providers.ReadResourceResponse{NewState: req.PriorState} - } - - p.GetSchemaReturn = testProviderSchema(prefix) + p.GetProviderSchemaResponse = testProviderSchema(prefix) return p } @@ -266,20 +439,6 @@ func checkStateString(t *testing.T, state *states.State, expected string) { } } -func resourceState(resourceType, resourceID string) *ResourceState { - providerResource := strings.Split(resourceType, "_") - return &ResourceState{ - Type: resourceType, - Primary: &InstanceState{ - ID: resourceID, - Attributes: map[string]string{ - "id": resourceID, - }, - }, - Provider: "provider." + providerResource[0], - } -} - // Test helper that gives a function 3 seconds to finish, assumes deadlock and // fails test if it does not. func testCheckDeadlock(t *testing.T, f func()) { @@ -302,8 +461,8 @@ func testCheckDeadlock(t *testing.T, f func()) { } } -func testProviderSchema(name string) *ProviderSchema { - return &ProviderSchema{ +func testProviderSchema(name string) *providers.GetProviderSchemaResponse { + return getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "region": { @@ -436,15 +595,6 @@ func testProviderSchema(name string) *ProviderSchema { }, }, BlockTypes: map[string]*configschema.NestedBlock{ - "network_interface": { - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "network_interface_id": {Type: cty.String, Optional: true}, - "device_index": {Type: cty.Number, Optional: true}, - }, - }, - Nesting: configschema.NestingSet, - }, "nesting_single": { Block: configschema.Block{ Attributes: map[string]*configschema.Attribute{ @@ -554,8 +704,7 @@ func testProviderSchema(name string) *ProviderSchema { }, }, }, - } - + }) } // contextForPlanViaFile is a helper that creates a temporary plan file, then @@ -579,7 +728,10 @@ func contextOptsForPlanViaFile(configSnap *configload.Snapshot, plan *plans.Plan // to run through any of the codepaths that care about Lineage/Serial/etc // here anyway. stateFile := &statefile.File{ - State: plan.State, + State: plan.PriorState, + } + prevStateFile := &statefile.File{ + State: plan.PrevRunState, } // To make life a little easier for test authors, we'll populate a simple @@ -597,7 +749,7 @@ func contextOptsForPlanViaFile(configSnap *configload.Snapshot, plan *plans.Plan } filename := filepath.Join(dir, "tfplan") - err = planfile.Create(filename, configSnap, stateFile, plan) + err = planfile.Create(filename, configSnap, prevStateFile, stateFile, plan) if err != nil { return nil, err } @@ -891,18 +1043,6 @@ func logDiagnostics(t *testing.T, diags tfdiags.Diagnostics) { } } -const testContextGraph = ` -root: root -aws_instance.bar - aws_instance.bar -> provider.aws -aws_instance.foo - aws_instance.foo -> provider.aws -provider.aws -root - root -> aws_instance.bar - root -> aws_instance.foo -` - const testContextRefreshModuleStr = ` aws_instance.web: (tainted) ID = bar diff --git a/vendor/github.com/hashicorp/terraform/terraform/context_validate_test.go b/vendor/github.com/hashicorp/terraform/terraform/context_validate_test.go index ec1f9b09..e0048d03 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/context_validate_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/context_validate_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/provisioners" "github.com/hashicorp/terraform/states" @@ -18,13 +19,13 @@ import ( func TestContext2Validate_badCount(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{}, }, }, - } + }) m := testModule(t, "validate-bad-count") c := testContext2(t, &ContextOpts{ @@ -42,13 +43,13 @@ func TestContext2Validate_badCount(t *testing.T) { func TestContext2Validate_badResource_reference(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{}, }, }, - } + }) m := testModule(t, "validate-bad-resource-count") c := testContext2(t, &ContextOpts{ @@ -66,7 +67,7 @@ func TestContext2Validate_badResource_reference(t *testing.T) { func TestContext2Validate_badVar(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "aws_instance": { Attributes: map[string]*configschema.Attribute{ @@ -75,7 +76,7 @@ func TestContext2Validate_badVar(t *testing.T) { }, }, }, - } + }) m := testModule(t, "validate-bad-var") c := testContext2(t, &ContextOpts{ @@ -94,7 +95,7 @@ func TestContext2Validate_badVar(t *testing.T) { func TestContext2Validate_varMapOverrideOld(t *testing.T) { m := testModule(t, "validate-module-pc-vars") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ Provider: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "foo": {Type: cty.String, Optional: true}, @@ -105,7 +106,7 @@ func TestContext2Validate_varMapOverrideOld(t *testing.T) { Attributes: map[string]*configschema.Attribute{}, }, }, - } + }) _, diags := NewContext(&ContextOpts{ Config: m, @@ -133,25 +134,31 @@ func TestContext2Validate_varNoDefaultExplicitType(t *testing.T) { func TestContext2Validate_computedVar(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "value": {Type: cty.String, Optional: true}, + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "value": {Type: cty.String, Optional: true}, + }, }, }, - ResourceTypes: map[string]*configschema.Block{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{}, + }, }, }, } pt := testProvider("test") - pt.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + pt.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Computed: true}, - "value": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + "value": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -166,7 +173,7 @@ func TestContext2Validate_computedVar(t *testing.T) { }, }) - p.PrepareProviderConfigFn = func(req providers.PrepareProviderConfigRequest) (resp providers.PrepareProviderConfigResponse) { + p.ValidateProviderConfigFn = func(req providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) { val := req.Config.GetAttr("value") if val.IsKnown() { resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value isn't computed")) @@ -179,26 +186,30 @@ func TestContext2Validate_computedVar(t *testing.T) { if diags.HasErrors() { t.Fatalf("unexpected error: %s", diags.Err()) } - if p.ConfigureCalled { + if p.ConfigureProviderCalled { t.Fatal("Configure should not be called for provider") } } func TestContext2Validate_computedInFunction(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "attr": {Type: cty.Number, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "attr": {Type: cty.Number, Optional: true}, + }, }, }, }, - DataSources: map[string]*configschema.Block{ + DataSources: map[string]providers.Schema{ "aws_data_source": { - Attributes: map[string]*configschema.Attribute{ - "optional_attr": {Type: cty.String, Optional: true}, - "computed": {Type: cty.String, Computed: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "optional_attr": {Type: cty.String, Optional: true}, + "computed": {Type: cty.String, Computed: true}, + }, }, }, }, @@ -223,17 +234,21 @@ func TestContext2Validate_computedInFunction(t *testing.T) { // can be realized during a plan. func TestContext2Validate_countComputed(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{}, + }, }, }, - DataSources: map[string]*configschema.Block{ + DataSources: map[string]providers.Schema{ "aws_data_source": { - Attributes: map[string]*configschema.Attribute{ - "compute": {Type: cty.String, Optional: true}, - "value": {Type: cty.String, Computed: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "compute": {Type: cty.String, Optional: true}, + "value": {Type: cty.String, Computed: true}, + }, }, }, }, @@ -255,14 +270,15 @@ func TestContext2Validate_countComputed(t *testing.T) { func TestContext2Validate_countNegative(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{}, + }, }, }, } - m := testModule(t, "validate-count-negative") c := testContext2(t, &ContextOpts{ Config: m, @@ -279,16 +295,17 @@ func TestContext2Validate_countNegative(t *testing.T) { func TestContext2Validate_countVariable(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, }, } - m := testModule(t, "apply-count-variable") c := testContext2(t, &ContextOpts{ Config: m, @@ -306,16 +323,17 @@ func TestContext2Validate_countVariable(t *testing.T) { func TestContext2Validate_countVariableNoDefault(t *testing.T) { p := testProvider("aws") m := testModule(t, "validate-count-variable") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, }, } - _, diags := NewContext(&ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -330,16 +348,17 @@ func TestContext2Validate_countVariableNoDefault(t *testing.T) { func TestContext2Validate_moduleBadOutput(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, }, } - m := testModule(t, "validate-bad-module-output") c := testContext2(t, &ContextOpts{ Config: m, @@ -356,16 +375,17 @@ func TestContext2Validate_moduleBadOutput(t *testing.T) { func TestContext2Validate_moduleGood(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, }, } - m := testModule(t, "validate-good-module") c := testContext2(t, &ContextOpts{ Config: m, @@ -383,10 +403,12 @@ func TestContext2Validate_moduleGood(t *testing.T) { func TestContext2Validate_moduleBadResource(t *testing.T) { m := testModule(t, "validate-module-bad-rc") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{}, + }, }, }, } @@ -398,7 +420,7 @@ func TestContext2Validate_moduleBadResource(t *testing.T) { }, }) - p.ValidateResourceTypeConfigResponse = providers.ValidateResourceTypeConfigResponse{ + p.ValidateResourceConfigResponse = &providers.ValidateResourceConfigResponse{ Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")), } @@ -411,11 +433,13 @@ func TestContext2Validate_moduleBadResource(t *testing.T) { func TestContext2Validate_moduleDepsShouldNotCycle(t *testing.T) { m := testModule(t, "validate-module-deps-cycle") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -437,16 +461,20 @@ func TestContext2Validate_moduleDepsShouldNotCycle(t *testing.T) { func TestContext2Validate_moduleProviderVar(t *testing.T) { m := testModule(t, "validate-module-pc-vars") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, - ResourceTypes: map[string]*configschema.Block{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -465,7 +493,7 @@ func TestContext2Validate_moduleProviderVar(t *testing.T) { }, }) - p.PrepareProviderConfigFn = func(req providers.PrepareProviderConfigRequest) (resp providers.PrepareProviderConfigResponse) { + p.ValidateProviderConfigFn = func(req providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) { if req.Config.GetAttr("foo").IsNull() { resp.Diagnostics = resp.Diagnostics.Append(errors.New("foo is null")) } @@ -481,16 +509,20 @@ func TestContext2Validate_moduleProviderVar(t *testing.T) { func TestContext2Validate_moduleProviderInheritUnused(t *testing.T) { m := testModule(t, "validate-module-pc-inherit-unused") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, - ResourceTypes: map[string]*configschema.Block{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -503,7 +535,7 @@ func TestContext2Validate_moduleProviderInheritUnused(t *testing.T) { }, }) - p.PrepareProviderConfigFn = func(req providers.PrepareProviderConfigRequest) (resp providers.PrepareProviderConfigResponse) { + p.ValidateProviderConfigFn = func(req providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) { if req.Config.GetAttr("foo").IsNull() { resp.Diagnostics = resp.Diagnostics.Append(errors.New("foo is null")) } @@ -518,12 +550,14 @@ func TestContext2Validate_moduleProviderInheritUnused(t *testing.T) { func TestContext2Validate_orphans(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, - "num": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + "num": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -543,12 +577,12 @@ func TestContext2Validate_orphans(t *testing.T) { State: state, }) - p.ValidateResourceTypeConfigFn = func(req providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse { + p.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { var diags tfdiags.Diagnostics if req.Config.GetAttr("foo").IsNull() { - diags.Append(errors.New("foo is not set")) + diags = diags.Append(errors.New("foo is not set")) } - return providers.ValidateResourceTypeConfigResponse{ + return providers.ValidateResourceConfigResponse{ Diagnostics: diags, } } @@ -562,15 +596,19 @@ func TestContext2Validate_orphans(t *testing.T) { func TestContext2Validate_providerConfig_bad(t *testing.T) { m := testModule(t, "validate-bad-pc") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, - ResourceTypes: map[string]*configschema.Block{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{}, + }, }, }, } @@ -582,7 +620,7 @@ func TestContext2Validate_providerConfig_bad(t *testing.T) { }, }) - p.PrepareProviderConfigResponse = providers.PrepareProviderConfigResponse{ + p.ValidateProviderConfigResponse = &providers.ValidateProviderConfigResponse{ Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")), } @@ -595,18 +633,22 @@ func TestContext2Validate_providerConfig_bad(t *testing.T) { } } -func TestContext2Validate_providerConfig_badEmpty(t *testing.T) { - m := testModule(t, "validate-bad-pc-empty") +func TestContext2Validate_providerConfig_skippedEmpty(t *testing.T) { + m := testModule(t, "validate-skipped-pc-empty") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, - ResourceTypes: map[string]*configschema.Block{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{}, + }, }, }, } @@ -618,28 +660,32 @@ func TestContext2Validate_providerConfig_badEmpty(t *testing.T) { }, }) - p.PrepareProviderConfigResponse = providers.PrepareProviderConfigResponse{ - Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")), + p.ValidateProviderConfigResponse = &providers.ValidateProviderConfigResponse{ + Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("should not be called")), } diags := c.Validate() - if !diags.HasErrors() { - t.Fatalf("succeeded; want error") + if diags.HasErrors() { + t.Fatalf("unexpected error: %s", diags.Err()) } } func TestContext2Validate_providerConfig_good(t *testing.T) { m := testModule(t, "validate-bad-pc") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, - ResourceTypes: map[string]*configschema.Block{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{}, + }, }, }, } @@ -663,15 +709,19 @@ func TestContext2Validate_requiredProviderConfig(t *testing.T) { m := testModule(t, "validate-required-provider-config") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "required_attribute": {Type: cty.String, Required: true}, + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "required_attribute": {Type: cty.String, Required: true}, + }, }, }, - ResourceTypes: map[string]*configschema.Block{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{}, + }, }, }, } @@ -692,11 +742,13 @@ func TestContext2Validate_requiredProviderConfig(t *testing.T) { func TestContext2Validate_provisionerConfig_bad(t *testing.T) { m := testModule(t, "validate-bad-prov-conf") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -709,12 +761,12 @@ func TestContext2Validate_provisionerConfig_bad(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) - p.PrepareProviderConfigResponse = providers.PrepareProviderConfigResponse{ + p.ValidateProviderConfigResponse = &providers.ValidateProviderConfigResponse{ Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")), } @@ -727,11 +779,13 @@ func TestContext2Validate_provisionerConfig_bad(t *testing.T) { func TestContext2Validate_badResourceConnection(t *testing.T) { m := testModule(t, "validate-bad-resource-connection") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -744,7 +798,7 @@ func TestContext2Validate_badResourceConnection(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -759,11 +813,13 @@ func TestContext2Validate_badResourceConnection(t *testing.T) { func TestContext2Validate_badProvisionerConnection(t *testing.T) { m := testModule(t, "validate-bad-prov-connection") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -776,7 +832,7 @@ func TestContext2Validate_badProvisionerConnection(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -791,16 +847,20 @@ func TestContext2Validate_badProvisionerConnection(t *testing.T) { func TestContext2Validate_provisionerConfig_good(t *testing.T) { m := testModule(t, "validate-bad-prov-conf") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, - ResourceTypes: map[string]*configschema.Block{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -810,7 +870,7 @@ func TestContext2Validate_provisionerConfig_good(t *testing.T) { pr.ValidateProvisionerConfigFn = func(req provisioners.ValidateProvisionerConfigRequest) provisioners.ValidateProvisionerConfigResponse { var diags tfdiags.Diagnostics if req.Config.GetAttr("test_string").IsNull() { - diags.Append(errors.New("test_string is not set")) + diags = diags.Append(errors.New("test_string is not set")) } return provisioners.ValidateProvisionerConfigResponse{ Diagnostics: diags, @@ -822,7 +882,7 @@ func TestContext2Validate_provisionerConfig_good(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, }) @@ -836,16 +896,17 @@ func TestContext2Validate_provisionerConfig_good(t *testing.T) { func TestContext2Validate_requiredVar(t *testing.T) { m := testModule(t, "validate-required-var") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "ami": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "ami": {Type: cty.String, Optional: true}, + }, }, }, }, } - _, diags := NewContext(&ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -861,16 +922,17 @@ func TestContext2Validate_requiredVar(t *testing.T) { func TestContext2Validate_resourceConfig_bad(t *testing.T) { m := testModule(t, "validate-bad-rc") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, }, } - c := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -878,7 +940,7 @@ func TestContext2Validate_resourceConfig_bad(t *testing.T) { }, }) - p.ValidateResourceTypeConfigResponse = providers.ValidateResourceTypeConfigResponse{ + p.ValidateResourceConfigResponse = &providers.ValidateResourceConfigResponse{ Diagnostics: tfdiags.Diagnostics{}.Append(fmt.Errorf("bad")), } @@ -891,16 +953,17 @@ func TestContext2Validate_resourceConfig_bad(t *testing.T) { func TestContext2Validate_resourceConfig_good(t *testing.T) { m := testModule(t, "validate-bad-rc") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, }, } - c := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -916,12 +979,14 @@ func TestContext2Validate_resourceConfig_good(t *testing.T) { func TestContext2Validate_tainted(t *testing.T) { p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, - "num": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + "num": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -940,12 +1005,12 @@ func TestContext2Validate_tainted(t *testing.T) { State: state, }) - p.ValidateResourceTypeConfigFn = func(req providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse { + p.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { var diags tfdiags.Diagnostics if req.Config.GetAttr("foo").IsNull() { - diags.Append(errors.New("foo is not set")) + diags = diags.Append(errors.New("foo is not set")) } - return providers.ValidateResourceTypeConfigResponse{ + return providers.ValidateResourceConfigResponse{ Diagnostics: diags, } } @@ -960,14 +1025,14 @@ func TestContext2Validate_targetedDestroy(t *testing.T) { m := testModule(t, "validate-targeted") p := testProvider("aws") pr := simpleMockProvisioner() - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, - "num": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + "num": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -983,7 +1048,7 @@ func TestContext2Validate_targetedDestroy(t *testing.T) { Providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), }, - Provisioners: map[string]ProvisionerFactory{ + Provisioners: map[string]provisioners.Factory{ "shell": testProvisionerFuncFixed(pr), }, State: state, @@ -992,7 +1057,7 @@ func TestContext2Validate_targetedDestroy(t *testing.T) { addrs.ManagedResourceMode, "aws_instance", "foo", ), }, - Destroy: true, + PlanMode: plans.DestroyMode, }) diags := ctx.Validate() @@ -1004,11 +1069,13 @@ func TestContext2Validate_targetedDestroy(t *testing.T) { func TestContext2Validate_varRefUnknown(t *testing.T) { m := testModule(t, "validate-variable-ref") p := testProvider("aws") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -1027,9 +1094,9 @@ func TestContext2Validate_varRefUnknown(t *testing.T) { }) var value cty.Value - p.ValidateResourceTypeConfigFn = func(req providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse { + p.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { value = req.Config.GetAttr("foo") - return providers.ValidateResourceTypeConfigResponse{} + return providers.ValidateResourceConfigResponse{} } c.Validate() @@ -1049,13 +1116,13 @@ func TestContext2Validate_interpolateVar(t *testing.T) { m := testModule(t, "input-interpolate-var") p := testProvider("null") - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "template_file": { - Attributes: map[string]*configschema.Attribute{ - "template": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "template": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -1082,13 +1149,13 @@ func TestContext2Validate_interpolateComputedModuleVarDef(t *testing.T) { m := testModule(t, "validate-computed-module-var-ref") p := testProvider("aws") - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "aws_instance": { - Attributes: map[string]*configschema.Attribute{ - "attr": {Type: cty.String, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "attr": {Type: cty.String, Optional: true}, + }, }, }, }, @@ -1114,8 +1181,6 @@ func TestContext2Validate_interpolateMap(t *testing.T) { m := testModule(t, "issue-9549") p := testProvider("template") - p.ApplyResourceChangeFn = testApplyFn - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, @@ -1131,6 +1196,71 @@ func TestContext2Validate_interpolateMap(t *testing.T) { } } +func TestContext2Validate_varSensitive(t *testing.T) { + // Smoke test through validate where a variable has sensitive applied + m := testModuleInline(t, map[string]string{ + "main.tf": ` +variable "foo" { + default = "xyz" + sensitive = true +} + +variable "bar" { + sensitive = true +} + +data "aws_data_source" "bar" { + foo = var.bar +} + +resource "aws_instance" "foo" { + foo = var.foo +} +`, + }) + + p := testProvider("aws") + p.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { + // Providers receive unmarked values + if got, want := req.Config.GetAttr("foo"), cty.UnknownVal(cty.String); !got.RawEquals(want) { + t.Fatalf("wrong value for foo\ngot: %#v\nwant: %#v", got, want) + } + return providers.ValidateResourceConfigResponse{} + } + p.ValidateDataResourceConfigFn = func(req providers.ValidateDataResourceConfigRequest) (resp providers.ValidateDataResourceConfigResponse) { + if got, want := req.Config.GetAttr("foo"), cty.UnknownVal(cty.String); !got.RawEquals(want) { + t.Fatalf("wrong value for foo\ngot: %#v\nwant: %#v", got, want) + } + return providers.ValidateDataResourceConfigResponse{} + } + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), + }, + Variables: InputValues{ + "bar": &InputValue{ + Value: cty.StringVal("boop"), + SourceType: ValueFromCaller, + }, + }, + }) + + diags := ctx.Validate() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + if !p.ValidateResourceConfigCalled { + t.Fatal("expected ValidateResourceConfigFn to be called") + } + + if !p.ValidateDataResourceConfigCalled { + t.Fatal("expected ValidateDataSourceConfigFn to be called") + } +} + // Manually validate using the new PlanGraphBuilder func TestContext2Validate_PlanGraphBuilder(t *testing.T) { fixture := contextFixtureApplyVars(t) @@ -1208,7 +1338,7 @@ output "out" { } // Should get this error: // Unsupported attribute: This object does not have an attribute named "missing" - if got, want := diags.Err().Error(), "Unsupported attribute"; strings.Index(got, want) == -1 { + if got, want := diags.Err().Error(), "Unsupported attribute"; !strings.Contains(got, want) { t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) } } @@ -1245,11 +1375,43 @@ resource "aws_instance" "foo" { } // Should get this error: // Unsupported attribute: This object does not have an attribute named "missing" - if got, want := diags.Err().Error(), "Unsupported attribute"; strings.Index(got, want) == -1 { + if got, want := diags.Err().Error(), "Unsupported attribute"; !strings.Contains(got, want) { t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) } } +func TestContext2Validate_sensitiveRootModuleOutput(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "child/main.tf": ` +variable "foo" { + default = "xyz" + sensitive = true +} + +output "out" { + value = var.foo +}`, + "main.tf": ` +module "child" { + source = "./child" +} + +output "root" { + value = module.child.out + sensitive = true +}`, + }) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + }) + + diags := ctx.Validate() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } +} + func TestContext2Validate_legacyResourceCount(t *testing.T) { m := testModuleInline(t, map[string]string{ "main.tf": ` @@ -1274,7 +1436,7 @@ output "out" { } // Should get this error: // Invalid resource count attribute: The special "count" attribute is no longer supported after Terraform v0.12. Instead, use length(aws_instance.test) to count resource instances. - if got, want := diags.Err().Error(), "Invalid resource count attribute:"; strings.Index(got, want) == -1 { + if got, want := diags.Err().Error(), "Invalid resource count attribute:"; !strings.Contains(got, want) { t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) } } @@ -1305,7 +1467,7 @@ output "out" { } // Should get this error: // Reference to undeclared module: No module call named "foo" is declared in the root module. - if got, want := diags.Err().Error(), "Reference to undeclared module:"; strings.Index(got, want) == -1 { + if got, want := diags.Err().Error(), "Reference to undeclared module:"; !strings.Contains(got, want) { t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) } } @@ -1336,7 +1498,7 @@ output "out" { } // Should get this error: // Reference to undeclared module: No module call named "foo" is declared in the root module. - if got, want := diags.Err().Error(), "Reference to undeclared module:"; strings.Index(got, want) == -1 { + if got, want := diags.Err().Error(), "Reference to undeclared module:"; !strings.Contains(got, want) { t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) } } @@ -1366,7 +1528,7 @@ resource "test_instance" "bar" { } // Should get this error: // Reference to undeclared module: No module call named "foo" is declared in the root module. - if got, want := diags.Err().Error(), "Reference to undeclared resource:"; strings.Index(got, want) == -1 { + if got, want := diags.Err().Error(), "Reference to undeclared resource:"; !strings.Contains(got, want) { t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) } } @@ -1399,7 +1561,7 @@ resource "test_instance" "bar" { } // Should get this error: // Reference to undeclared module: No module call named "foo" is declared in the root module. - if got, want := diags.Err().Error(), `no argument, nested block, or exported attribute named "does_not_exist_in_schema"`; strings.Index(got, want) == -1 { + if got, want := diags.Err().Error(), `no argument, nested block, or exported attribute named "does_not_exist_in_schema"`; !strings.Contains(got, want) { t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) } } @@ -1422,7 +1584,7 @@ func TestContext2Validate_variableCustomValidationsFail(t *testing.T) { if !diags.HasErrors() { t.Fatal("succeeded; want errors") } - if got, want := diags.Err().Error(), `Invalid value for variable: Value must not be "nope".`; strings.Index(got, want) == -1 { + if got, want := diags.Err().Error(), `Invalid value for variable: Value must not be "nope".`; !strings.Contains(got, want) { t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) } } @@ -1514,7 +1676,6 @@ resource "aws_instance" "foo" { }) p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -1543,7 +1704,6 @@ resource "aws_instance" "foo" { }) p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -1555,7 +1715,7 @@ resource "aws_instance" "foo" { if !diags.HasErrors() { t.Fatal("succeeded; want errors") } - if got, want := diags.Err().Error(), `Invalid count argument`; strings.Index(got, want) == -1 { + if got, want := diags.Err().Error(), `Invalid count argument`; !strings.Contains(got, want) { t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) } } @@ -1575,7 +1735,6 @@ resource "aws_instance" "foo" { }) p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -1587,7 +1746,7 @@ resource "aws_instance" "foo" { if !diags.HasErrors() { t.Fatal("succeeded; want errors") } - if got, want := diags.Err().Error(), `Invalid for_each argument`; strings.Index(got, want) == -1 { + if got, want := diags.Err().Error(), `Invalid for_each argument`; !strings.Contains(got, want) { t.Fatalf("wrong error:\ngot: %s\nwant: message containing %q", got, want) } } @@ -1658,7 +1817,6 @@ output "out" { }) p := testProvider("aws") - p.PlanResourceChangeFn = testDiffFn ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -1752,31 +1910,32 @@ output "out" { } } -func TestContext2Validate_invalidIgnoreChanges(t *testing.T) { +func TestContext2Validate_rpcDiagnostics(t *testing.T) { // validate module and output depends_on m := testModuleInline(t, map[string]string{ "main.tf": ` resource "test_instance" "a" { - lifecycle { - ignore_changes = [foo] - } } - `, }) p := testProvider("test") - p.GetSchemaReturn = &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Computed: true}, - "foo": {Type: cty.String, Computed: true, Optional: true}, + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + }, }, }, }, } + p.ValidateResourceConfigResponse = &providers.ValidateResourceConfigResponse{ + Diagnostics: tfdiags.Diagnostics(nil).Append(tfdiags.SimpleWarning("don't frobble")), + } + ctx := testContext2(t, &ContextOpts{ Config: m, Providers: map[addrs.Provider]providers.Factory{ @@ -1784,14 +1943,221 @@ resource "test_instance" "a" { }, }) diags := ctx.Validate() - if !diags.HasErrors() { - t.Fatal("succeeded; want errors") + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + if len(diags) == 0 { + t.Fatal("expected warnings") } for _, d := range diags { des := d.Description().Summary - if !strings.Contains(des, "Cannot ignore") { - t.Fatalf(`expected "Invalid depends_on reference", got %q`, des) + if !strings.Contains(des, "frobble") { + t.Fatalf(`expected frobble, got %q`, des) + } + } +} + +func TestContext2Validate_sensitiveProvisionerConfig(t *testing.T) { + m := testModule(t, "validate-sensitive-provisioner-config") + p := testProvider("aws") + p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{ + ResourceTypes: map[string]providers.Schema{ + "aws_instance": { + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": {Type: cty.String, Optional: true}, + }, + }, + }, + }, + } + + pr := simpleMockProvisioner() + + c := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), + }, + Provisioners: map[string]provisioners.Factory{ + "test": testProvisionerFuncFixed(pr), + }, + }) + + pr.ValidateProvisionerConfigFn = func(r provisioners.ValidateProvisionerConfigRequest) provisioners.ValidateProvisionerConfigResponse { + if r.Config.ContainsMarked() { + t.Errorf("provisioner config contains marked values") } + return pr.ValidateProvisionerConfigResponse + } + + diags := c.Validate() + if diags.HasErrors() { + t.Fatalf("unexpected error: %s", diags.Err()) + } + if !pr.ValidateProvisionerConfigCalled { + t.Fatal("ValidateProvisionerConfig not called") + } +} + +func TestContext2Plan_validateMinMaxDynamicBlock(t *testing.T) { + p := new(MockProvider) + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ + ResourceTypes: map[string]*configschema.Block{ + "test_instance": { + Attributes: map[string]*configschema.Attribute{ + "id": { + Type: cty.String, + Computed: true, + }, + "things": { + Type: cty.List(cty.String), + Computed: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "foo": { + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "bar": {Type: cty.String, Optional: true}, + }, + }, + Nesting: configschema.NestingList, + MinItems: 2, + MaxItems: 3, + }, + }, + }, + }, + }) + + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_instance" "a" { + // MinItems 2 + foo { + bar = "a" + } + foo { + bar = "b" + } +} + +resource "test_instance" "b" { + // one dymamic block can satisfy MinItems of 2 + dynamic "foo" { + for_each = test_instance.a.things + content { + bar = foo.value + } + } +} + +resource "test_instance" "c" { + // we may have more than MaxItems dynamic blocks when they are unknown + foo { + bar = "b" + } + dynamic "foo" { + for_each = test_instance.a.things + content { + bar = foo.value + } + } + dynamic "foo" { + for_each = test_instance.a.things + content { + bar = "${foo.value}-2" + } + } + dynamic "foo" { + for_each = test_instance.b.things + content { + bar = foo.value + } + } +} +`}) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + diags := ctx.Validate() + if diags.HasErrors() { + t.Fatal(diags.ErrWithWarnings()) + } +} + +func TestContext2Validate_passInheritedProvider(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +terraform { + required_providers { + test = { + source = "hashicorp/test" + } + } +} + +module "first" { + source = "./first" + providers = { + test = test + } +} +`, + + // This module does not define a config for the test provider, but we + // should be able to pass whatever the implied config is to a child + // module. + "first/main.tf": ` +terraform { + required_providers { + test = { + source = "hashicorp/test" + } + } +} + +module "second" { + source = "./second" + providers = { + test.alias = test + } +}`, + + "first/second/main.tf": ` +terraform { + required_providers { + test = { + source = "hashicorp/test" + configuration_aliases = [test.alias] + } + } +} + +resource "test_object" "t" { + provider = test.alias +} +`, + }) + + p := simpleMockProvider() + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + diags := ctx.Validate() + if diags.HasErrors() { + t.Fatal(diags.ErrWithWarnings()) } } diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_apply.go b/vendor/github.com/hashicorp/terraform/terraform/eval_apply.go deleted file mode 100644 index 80ba9c1f..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_apply.go +++ /dev/null @@ -1,773 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - "reflect" - "strings" - - multierror "github.com/hashicorp/go-multierror" - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/configs" - "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/plans" - "github.com/hashicorp/terraform/plans/objchange" - "github.com/hashicorp/terraform/providers" - "github.com/hashicorp/terraform/provisioners" - "github.com/hashicorp/terraform/states" - "github.com/hashicorp/terraform/tfdiags" -) - -// EvalApply is an EvalNode implementation that writes the diff to -// the full diff. -type EvalApply struct { - Addr addrs.ResourceInstance - Config *configs.Resource - State **states.ResourceInstanceObject - Change **plans.ResourceInstanceChange - ProviderAddr addrs.AbsProviderConfig - Provider *providers.Interface - ProviderMetas map[addrs.Provider]*configs.ProviderMeta - ProviderSchema **ProviderSchema - Output **states.ResourceInstanceObject - CreateNew *bool - Error *error - CreateBeforeDestroy bool -} - -// TODO: test -func (n *EvalApply) Eval(ctx EvalContext) (interface{}, error) { - var diags tfdiags.Diagnostics - - change := *n.Change - provider := *n.Provider - state := *n.State - absAddr := n.Addr.Absolute(ctx.Path()) - - if state == nil { - state = &states.ResourceInstanceObject{} - } - - schema, _ := (*n.ProviderSchema).SchemaForResourceType(n.Addr.Resource.Mode, n.Addr.Resource.Type) - if schema == nil { - // Should be caught during validation, so we don't bother with a pretty error here - return nil, fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Type) - } - - if n.CreateNew != nil { - *n.CreateNew = (change.Action == plans.Create || change.Action.IsReplace()) - } - - configVal := cty.NullVal(cty.DynamicPseudoType) - if n.Config != nil { - var configDiags tfdiags.Diagnostics - forEach, _ := evaluateForEachExpression(n.Config.ForEach, ctx) - keyData := EvalDataForInstanceKey(n.Addr.Key, forEach) - configVal, _, configDiags = ctx.EvaluateBlock(n.Config.Config, schema, nil, keyData) - diags = diags.Append(configDiags) - if configDiags.HasErrors() { - return nil, diags.Err() - } - } - - if !configVal.IsWhollyKnown() { - return nil, fmt.Errorf( - "configuration for %s still contains unknown values during apply (this is a bug in Terraform; please report it!)", - absAddr, - ) - } - - metaConfigVal := cty.NullVal(cty.DynamicPseudoType) - if n.ProviderMetas != nil { - log.Printf("[DEBUG] EvalApply: ProviderMeta config value set") - if m, ok := n.ProviderMetas[n.ProviderAddr.Provider]; ok && m != nil { - // if the provider doesn't support this feature, throw an error - if (*n.ProviderSchema).ProviderMeta == nil { - log.Printf("[DEBUG] EvalApply: no ProviderMeta schema") - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: fmt.Sprintf("Provider %s doesn't support provider_meta", n.ProviderAddr.Provider.String()), - Detail: fmt.Sprintf("The resource %s belongs to a provider that doesn't support provider_meta blocks", n.Addr), - Subject: &m.ProviderRange, - }) - } else { - log.Printf("[DEBUG] EvalApply: ProviderMeta schema found") - var configDiags tfdiags.Diagnostics - metaConfigVal, _, configDiags = ctx.EvaluateBlock(m.Config, (*n.ProviderSchema).ProviderMeta, nil, EvalDataForNoInstanceKey) - diags = diags.Append(configDiags) - if configDiags.HasErrors() { - return nil, diags.Err() - } - } - } - } - - log.Printf("[DEBUG] %s: applying the planned %s change", n.Addr.Absolute(ctx.Path()), change.Action) - - // If our config, Before or After value contain any marked values, - // ensure those are stripped out before sending - // this to the provider - unmarkedConfigVal, _ := configVal.UnmarkDeep() - unmarkedBefore, beforePaths := change.Before.UnmarkDeepWithPaths() - unmarkedAfter, afterPaths := change.After.UnmarkDeepWithPaths() - - // If we have an Update action, our before and after values are equal, - // and only differ on their sensitivity, the newVal is the after val - // and we should not communicate with the provider or perform further action. - eqV := unmarkedBefore.Equals(unmarkedAfter) - eq := eqV.IsKnown() && eqV.True() - if change.Action == plans.Update && eq && !reflect.DeepEqual(beforePaths, afterPaths) { - return nil, diags.ErrWithWarnings() - } - - resp := provider.ApplyResourceChange(providers.ApplyResourceChangeRequest{ - TypeName: n.Addr.Resource.Type, - PriorState: unmarkedBefore, - Config: unmarkedConfigVal, - PlannedState: unmarkedAfter, - PlannedPrivate: change.Private, - ProviderMeta: metaConfigVal, - }) - applyDiags := resp.Diagnostics - if n.Config != nil { - applyDiags = applyDiags.InConfigBody(n.Config.Config) - } - diags = diags.Append(applyDiags) - - // Even if there are errors in the returned diagnostics, the provider may - // have returned a _partial_ state for an object that already exists but - // failed to fully configure, and so the remaining code must always run - // to completion but must be defensive against the new value being - // incomplete. - newVal := resp.NewState - - // If we have paths to mark, mark those on this new value - if len(afterPaths) > 0 { - newVal = newVal.MarkWithPaths(afterPaths) - } - - if newVal == cty.NilVal { - // Providers are supposed to return a partial new value even when errors - // occur, but sometimes they don't and so in that case we'll patch that up - // by just using the prior state, so we'll at least keep track of the - // object for the user to retry. - newVal = change.Before - - // As a special case, we'll set the new value to null if it looks like - // we were trying to execute a delete, because the provider in this case - // probably left the newVal unset intending it to be interpreted as "null". - if change.After.IsNull() { - newVal = cty.NullVal(schema.ImpliedType()) - } - - // Ideally we'd produce an error or warning here if newVal is nil and - // there are no errors in diags, because that indicates a buggy - // provider not properly reporting its result, but unfortunately many - // of our historical test mocks behave in this way and so producing - // a diagnostic here fails hundreds of tests. Instead, we must just - // silently retain the old value for now. Returning a nil value with - // no errors is still always considered a bug in the provider though, - // and should be fixed for any "real" providers that do it. - } - - var conformDiags tfdiags.Diagnostics - for _, err := range newVal.Type().TestConformance(schema.ImpliedType()) { - conformDiags = conformDiags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced invalid object", - fmt.Sprintf( - "Provider %q produced an invalid value after apply for %s. The result cannot not be saved in the Terraform state.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.Provider.String(), tfdiags.FormatErrorPrefixed(err, absAddr.String()), - ), - )) - } - diags = diags.Append(conformDiags) - if conformDiags.HasErrors() { - // Bail early in this particular case, because an object that doesn't - // conform to the schema can't be saved in the state anyway -- the - // serializer will reject it. - return nil, diags.Err() - } - - // After this point we have a type-conforming result object and so we - // must always run to completion to ensure it can be saved. If n.Error - // is set then we must not return a non-nil error, in order to allow - // evaluation to continue to a later point where our state object will - // be saved. - - // By this point there must not be any unknown values remaining in our - // object, because we've applied the change and we can't save unknowns - // in our persistent state. If any are present then we will indicate an - // error (which is always a bug in the provider) but we will also replace - // them with nulls so that we can successfully save the portions of the - // returned value that are known. - if !newVal.IsWhollyKnown() { - // To generate better error messages, we'll go for a walk through the - // value and make a separate diagnostic for each unknown value we - // find. - cty.Walk(newVal, func(path cty.Path, val cty.Value) (bool, error) { - if !val.IsKnown() { - pathStr := tfdiags.FormatCtyPath(path) - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider returned invalid result object after apply", - fmt.Sprintf( - "After the apply operation, the provider still indicated an unknown value for %s%s. All values must be known after apply, so this is always a bug in the provider and should be reported in the provider's own repository. Terraform will still save the other known object values in the state.", - n.Addr.Absolute(ctx.Path()), pathStr, - ), - )) - } - return true, nil - }) - - // NOTE: This operation can potentially be lossy if there are multiple - // elements in a set that differ only by unknown values: after - // replacing with null these will be merged together into a single set - // element. Since we can only get here in the presence of a provider - // bug, we accept this because storing a result here is always a - // best-effort sort of thing. - newVal = cty.UnknownAsNull(newVal) - } - - if change.Action != plans.Delete && !diags.HasErrors() { - // Only values that were marked as unknown in the planned value are allowed - // to change during the apply operation. (We do this after the unknown-ness - // check above so that we also catch anything that became unknown after - // being known during plan.) - // - // If we are returning other errors anyway then we'll give this - // a pass since the other errors are usually the explanation for - // this one and so it's more helpful to let the user focus on the - // root cause rather than distract with this extra problem. - if errs := objchange.AssertObjectCompatible(schema, change.After, newVal); len(errs) > 0 { - if resp.LegacyTypeSystem { - // The shimming of the old type system in the legacy SDK is not precise - // enough to pass this consistency check, so we'll give it a pass here, - // but we will generate a warning about it so that we are more likely - // to notice in the logs if an inconsistency beyond the type system - // leads to a downstream provider failure. - var buf strings.Builder - fmt.Fprintf(&buf, "[WARN] Provider %q produced an unexpected new value for %s, but we are tolerating it because it is using the legacy plugin SDK.\n The following problems may be the cause of any confusing errors from downstream operations:", n.ProviderAddr.Provider.String(), absAddr) - for _, err := range errs { - fmt.Fprintf(&buf, "\n - %s", tfdiags.FormatError(err)) - } - log.Print(buf.String()) - - // The sort of inconsistency we won't catch here is if a known value - // in the plan is changed during apply. That can cause downstream - // problems because a dependent resource would make its own plan based - // on the planned value, and thus get a different result during the - // apply phase. This will usually lead to a "Provider produced invalid plan" - // error that incorrectly blames the downstream resource for the change. - - } else { - for _, err := range errs { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced inconsistent result after apply", - fmt.Sprintf( - "When applying changes to %s, provider %q produced an unexpected new value: %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - absAddr, n.ProviderAddr.Provider.String(), tfdiags.FormatError(err), - ), - )) - } - } - } - } - - // If a provider returns a null or non-null object at the wrong time then - // we still want to save that but it often causes some confusing behaviors - // where it seems like Terraform is failing to take any action at all, - // so we'll generate some errors to draw attention to it. - if !diags.HasErrors() { - if change.Action == plans.Delete && !newVal.IsNull() { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider returned invalid result object after apply", - fmt.Sprintf( - "After applying a %s plan, the provider returned a non-null object for %s. Destroying should always produce a null value, so this is always a bug in the provider and should be reported in the provider's own repository. Terraform will still save this errant object in the state for debugging and recovery.", - change.Action, n.Addr.Absolute(ctx.Path()), - ), - )) - } - if change.Action != plans.Delete && newVal.IsNull() { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider returned invalid result object after apply", - fmt.Sprintf( - "After applying a %s plan, the provider returned a null object for %s. Only destroying should always produce a null value, so this is always a bug in the provider and should be reported in the provider's own repository.", - change.Action, n.Addr.Absolute(ctx.Path()), - ), - )) - } - } - - newStatus := states.ObjectReady - - // Sometimes providers return a null value when an operation fails for some - // reason, but we'd rather keep the prior state so that the error can be - // corrected on a subsequent run. We must only do this for null new value - // though, or else we may discard partial updates the provider was able to - // complete. - if diags.HasErrors() && newVal.IsNull() { - // Otherwise, we'll continue but using the prior state as the new value, - // making this effectively a no-op. If the item really _has_ been - // deleted then our next refresh will detect that and fix it up. - // If change.Action is Create then change.Before will also be null, - // which is fine. - newVal = change.Before - - // If we're recovering the previous state, we also want to restore the - // the tainted status of the object. - if state.Status == states.ObjectTainted { - newStatus = states.ObjectTainted - } - } - - var newState *states.ResourceInstanceObject - if !newVal.IsNull() { // null value indicates that the object is deleted, so we won't set a new state in that case - newState = &states.ResourceInstanceObject{ - Status: newStatus, - Value: newVal, - Private: resp.Private, - CreateBeforeDestroy: n.CreateBeforeDestroy, - } - } - - // Write the final state - if n.Output != nil { - *n.Output = newState - } - - if diags.HasErrors() { - // If the caller provided an error pointer then they are expected to - // handle the error some other way and we treat our own result as - // success. - if n.Error != nil { - err := diags.Err() - *n.Error = err - log.Printf("[DEBUG] %s: apply errored, but we're indicating that via the Error pointer rather than returning it: %s", n.Addr.Absolute(ctx.Path()), err) - return nil, nil - } - } - - return nil, diags.ErrWithWarnings() -} - -// EvalApplyPre is an EvalNode implementation that does the pre-Apply work -type EvalApplyPre struct { - Addr addrs.ResourceInstance - Gen states.Generation - State **states.ResourceInstanceObject - Change **plans.ResourceInstanceChange -} - -// TODO: test -func (n *EvalApplyPre) Eval(ctx EvalContext) (interface{}, error) { - change := *n.Change - absAddr := n.Addr.Absolute(ctx.Path()) - - if change == nil { - panic(fmt.Sprintf("EvalApplyPre for %s called with nil Change", absAddr)) - } - - if resourceHasUserVisibleApply(n.Addr) { - priorState := change.Before - plannedNewState := change.After - - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PreApply(absAddr, n.Gen, change.Action, priorState, plannedNewState) - }) - if err != nil { - return nil, err - } - } - - return nil, nil -} - -// EvalApplyPost is an EvalNode implementation that does the post-Apply work -type EvalApplyPost struct { - Addr addrs.ResourceInstance - Gen states.Generation - State **states.ResourceInstanceObject - Error *error -} - -// TODO: test -func (n *EvalApplyPost) Eval(ctx EvalContext) (interface{}, error) { - state := *n.State - - if resourceHasUserVisibleApply(n.Addr) { - absAddr := n.Addr.Absolute(ctx.Path()) - var newState cty.Value - if state != nil { - newState = state.Value - } else { - newState = cty.NullVal(cty.DynamicPseudoType) - } - var err error - if n.Error != nil { - err = *n.Error - } - - hookErr := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostApply(absAddr, n.Gen, newState, err) - }) - if hookErr != nil { - return nil, hookErr - } - } - - return nil, *n.Error -} - -// EvalMaybeTainted is an EvalNode that takes the planned change, new value, -// and possible error from an apply operation and produces a new instance -// object marked as tainted if it appears that a create operation has failed. -// -// This EvalNode never returns an error, to ensure that a subsequent EvalNode -// can still record the possibly-tainted object in the state. -type EvalMaybeTainted struct { - Addr addrs.ResourceInstance - Gen states.Generation - Change **plans.ResourceInstanceChange - State **states.ResourceInstanceObject - Error *error -} - -func (n *EvalMaybeTainted) Eval(ctx EvalContext) (interface{}, error) { - if n.State == nil || n.Change == nil || n.Error == nil { - return nil, nil - } - - state := *n.State - change := *n.Change - err := *n.Error - - // nothing to do if everything went as planned - if err == nil { - return nil, nil - } - - if state != nil && state.Status == states.ObjectTainted { - log.Printf("[TRACE] EvalMaybeTainted: %s was already tainted, so nothing to do", n.Addr.Absolute(ctx.Path())) - return nil, nil - } - - if change.Action == plans.Create { - // If there are errors during a _create_ then the object is - // in an undefined state, and so we'll mark it as tainted so - // we can try again on the next run. - // - // We don't do this for other change actions because errors - // during updates will often not change the remote object at all. - // If there _were_ changes prior to the error, it's the provider's - // responsibility to record the effect of those changes in the - // object value it returned. - log.Printf("[TRACE] EvalMaybeTainted: %s encountered an error during creation, so it is now marked as tainted", n.Addr.Absolute(ctx.Path())) - *n.State = state.AsTainted() - } - - return nil, nil -} - -// resourceHasUserVisibleApply returns true if the given resource is one where -// apply actions should be exposed to the user. -// -// Certain resources do apply actions only as an implementation detail, so -// these should not be advertised to code outside of this package. -func resourceHasUserVisibleApply(addr addrs.ResourceInstance) bool { - // Only managed resources have user-visible apply actions. - // In particular, this excludes data resources since we "apply" these - // only as an implementation detail of removing them from state when - // they are destroyed. (When reading, they don't get here at all because - // we present them as "Refresh" actions.) - return addr.ContainingResource().Mode == addrs.ManagedResourceMode -} - -// EvalApplyProvisioners is an EvalNode implementation that executes -// the provisioners for a resource. -// -// TODO(mitchellh): This should probably be split up into a more fine-grained -// ApplyProvisioner (single) that is looped over. -type EvalApplyProvisioners struct { - Addr addrs.ResourceInstance - State **states.ResourceInstanceObject - ResourceConfig *configs.Resource - CreateNew *bool - Error *error - - // When is the type of provisioner to run at this point - When configs.ProvisionerWhen -} - -// TODO: test -func (n *EvalApplyProvisioners) Eval(ctx EvalContext) (interface{}, error) { - absAddr := n.Addr.Absolute(ctx.Path()) - state := *n.State - if state == nil { - log.Printf("[TRACE] EvalApplyProvisioners: %s has no state, so skipping provisioners", n.Addr) - return nil, nil - } - if n.When == configs.ProvisionerWhenCreate && n.CreateNew != nil && !*n.CreateNew { - // If we're not creating a new resource, then don't run provisioners - log.Printf("[TRACE] EvalApplyProvisioners: %s is not freshly-created, so no provisioning is required", n.Addr) - return nil, nil - } - if state.Status == states.ObjectTainted { - // No point in provisioning an object that is already tainted, since - // it's going to get recreated on the next apply anyway. - log.Printf("[TRACE] EvalApplyProvisioners: %s is tainted, so skipping provisioning", n.Addr) - return nil, nil - } - - provs := n.filterProvisioners() - if len(provs) == 0 { - // We have no provisioners, so don't do anything - return nil, nil - } - - if n.Error != nil && *n.Error != nil { - // We're already tainted, so just return out - return nil, nil - } - - { - // Call pre hook - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PreProvisionInstance(absAddr, state.Value) - }) - if err != nil { - return nil, err - } - } - - // If there are no errors, then we append it to our output error - // if we have one, otherwise we just output it. - err := n.apply(ctx, provs) - if err != nil { - *n.Error = multierror.Append(*n.Error, err) - if n.Error == nil { - return nil, err - } else { - log.Printf("[TRACE] EvalApplyProvisioners: %s provisioning failed, but we will continue anyway at the caller's request", absAddr) - return nil, nil - } - } - - { - // Call post hook - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostProvisionInstance(absAddr, state.Value) - }) - if err != nil { - return nil, err - } - } - - return nil, nil -} - -// filterProvisioners filters the provisioners on the resource to only -// the provisioners specified by the "when" option. -func (n *EvalApplyProvisioners) filterProvisioners() []*configs.Provisioner { - // Fast path the zero case - if n.ResourceConfig == nil || n.ResourceConfig.Managed == nil { - return nil - } - - if len(n.ResourceConfig.Managed.Provisioners) == 0 { - return nil - } - - result := make([]*configs.Provisioner, 0, len(n.ResourceConfig.Managed.Provisioners)) - for _, p := range n.ResourceConfig.Managed.Provisioners { - if p.When == n.When { - result = append(result, p) - } - } - - return result -} - -func (n *EvalApplyProvisioners) apply(ctx EvalContext, provs []*configs.Provisioner) error { - var diags tfdiags.Diagnostics - instanceAddr := n.Addr - absAddr := instanceAddr.Absolute(ctx.Path()) - - // this self is only used for destroy provisioner evaluation, and must - // refer to the last known value of the resource. - self := (*n.State).Value - - var evalScope func(EvalContext, hcl.Body, cty.Value, *configschema.Block) (cty.Value, tfdiags.Diagnostics) - switch n.When { - case configs.ProvisionerWhenDestroy: - evalScope = n.evalDestroyProvisionerConfig - default: - evalScope = n.evalProvisionerConfig - } - - // If there's a connection block defined directly inside the resource block - // then it'll serve as a base connection configuration for all of the - // provisioners. - var baseConn hcl.Body - if n.ResourceConfig.Managed != nil && n.ResourceConfig.Managed.Connection != nil { - baseConn = n.ResourceConfig.Managed.Connection.Config - } - - for _, prov := range provs { - log.Printf("[TRACE] EvalApplyProvisioners: provisioning %s with %q", absAddr, prov.Type) - - // Get the provisioner - provisioner := ctx.Provisioner(prov.Type) - schema := ctx.ProvisionerSchema(prov.Type) - - config, configDiags := evalScope(ctx, prov.Config, self, schema) - diags = diags.Append(configDiags) - if diags.HasErrors() { - return diags.Err() - } - - // If the provisioner block contains a connection block of its own then - // it can override the base connection configuration, if any. - var localConn hcl.Body - if prov.Connection != nil { - localConn = prov.Connection.Config - } - - var connBody hcl.Body - switch { - case baseConn != nil && localConn != nil: - // Our standard merging logic applies here, similar to what we do - // with _override.tf configuration files: arguments from the - // base connection block will be masked by any arguments of the - // same name in the local connection block. - connBody = configs.MergeBodies(baseConn, localConn) - case baseConn != nil: - connBody = baseConn - case localConn != nil: - connBody = localConn - } - - // start with an empty connInfo - connInfo := cty.NullVal(connectionBlockSupersetSchema.ImpliedType()) - - if connBody != nil { - var connInfoDiags tfdiags.Diagnostics - connInfo, connInfoDiags = evalScope(ctx, connBody, self, connectionBlockSupersetSchema) - diags = diags.Append(connInfoDiags) - if diags.HasErrors() { - return diags.Err() - } - } - - { - // Call pre hook - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PreProvisionInstanceStep(absAddr, prov.Type) - }) - if err != nil { - return err - } - } - - // The output function - outputFn := func(msg string) { - ctx.Hook(func(h Hook) (HookAction, error) { - h.ProvisionOutput(absAddr, prov.Type, msg) - return HookActionContinue, nil - }) - } - - // If our config or connection info contains any marked values, ensure - // those are stripped out before sending to the provisioner. Unlike - // resources, we have no need to capture the marked paths and reapply - // later. - unmarkedConfig, configMarks := config.UnmarkDeep() - unmarkedConnInfo, _ := connInfo.UnmarkDeep() - - // Marks on the config might result in leaking sensitive values through - // provisioner logging, so we conservatively suppress all output in - // this case. This should not apply to connection info values, which - // provisioners ought not to be logging anyway. - if len(configMarks) > 0 { - outputFn = func(msg string) { - ctx.Hook(func(h Hook) (HookAction, error) { - h.ProvisionOutput(absAddr, prov.Type, "(output suppressed due to sensitive value in config)") - return HookActionContinue, nil - }) - } - } - - output := CallbackUIOutput{OutputFn: outputFn} - resp := provisioner.ProvisionResource(provisioners.ProvisionResourceRequest{ - Config: unmarkedConfig, - Connection: unmarkedConnInfo, - UIOutput: &output, - }) - applyDiags := resp.Diagnostics.InConfigBody(prov.Config) - - // Call post hook - hookErr := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostProvisionInstanceStep(absAddr, prov.Type, applyDiags.Err()) - }) - - switch prov.OnFailure { - case configs.ProvisionerOnFailureContinue: - if applyDiags.HasErrors() { - log.Printf("[WARN] Errors while provisioning %s with %q, but continuing as requested in configuration", n.Addr, prov.Type) - } else { - // Maybe there are warnings that we still want to see - diags = diags.Append(applyDiags) - } - default: - diags = diags.Append(applyDiags) - if applyDiags.HasErrors() { - log.Printf("[WARN] Errors while provisioning %s with %q, so aborting", n.Addr, prov.Type) - return diags.Err() - } - } - - // Deal with the hook - if hookErr != nil { - return hookErr - } - } - - return diags.ErrWithWarnings() -} - -func (n *EvalApplyProvisioners) evalProvisionerConfig(ctx EvalContext, body hcl.Body, self cty.Value, schema *configschema.Block) (cty.Value, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - forEach, forEachDiags := evaluateForEachExpression(n.ResourceConfig.ForEach, ctx) - diags = diags.Append(forEachDiags) - - keyData := EvalDataForInstanceKey(n.Addr.Key, forEach) - - config, _, configDiags := ctx.EvaluateBlock(body, schema, n.Addr, keyData) - diags = diags.Append(configDiags) - - return config, diags -} - -// during destroy a provisioner can only evaluate within the scope of the parent resource -func (n *EvalApplyProvisioners) evalDestroyProvisionerConfig(ctx EvalContext, body hcl.Body, self cty.Value, schema *configschema.Block) (cty.Value, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - - // For a destroy-time provisioner forEach is intentionally nil here, - // which EvalDataForInstanceKey responds to by not populating EachValue - // in its result. That's okay because each.value is prohibited for - // destroy-time provisioners. - keyData := EvalDataForInstanceKey(n.Addr.Key, nil) - - evalScope := ctx.EvaluationScope(n.Addr, keyData) - config, evalDiags := evalScope.EvalSelfBlock(body, self, schema, keyData) - diags = diags.Append(evalDiags) - - return config, diags -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_context.go b/vendor/github.com/hashicorp/terraform/terraform/eval_context.go index 6c8a5d9d..c5355684 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_context.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_context.go @@ -77,22 +77,16 @@ type EvalContext interface { ProviderInput(addrs.AbsProviderConfig) map[string]cty.Value SetProviderInput(addrs.AbsProviderConfig, map[string]cty.Value) - // InitProvisioner initializes the provisioner with the given name. - // It is an error to initialize the same provisioner more than once. - InitProvisioner(string) error - - // Provisioner gets the provisioner instance with the given name (already - // initialized) or returns nil if the provisioner isn't initialized. - Provisioner(string) provisioners.Interface + // Provisioner gets the provisioner instance with the given name. + Provisioner(string) (provisioners.Interface, error) // ProvisionerSchema retrieves the main configuration schema for a // particular provisioner, which must have already been initialized with // InitProvisioner. ProvisionerSchema(string) *configschema.Block - // CloseProvisioner closes provisioner connections that aren't needed - // anymore. - CloseProvisioner(string) error + // CloseProvisioner closes all provisioner plugins. + CloseProvisioners() error // EvaluateBlock takes the given raw configuration block and associated // schema and evaluates it to produce a value of an object type that @@ -157,6 +151,12 @@ type EvalContext interface { // values. RefreshState() *states.SyncState + // PrevRunState returns a wrapper object that provides safe concurrent + // access to the state which represents the result of the previous run, + // updated only so that object data conforms to current schemas for + // meaningful comparison with RefreshState. + PrevRunState() *states.SyncState + // InstanceExpander returns a helper object for tracking the expansion of // graph nodes during the plan phase in response to "count" and "for_each" // arguments. diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_context_builtin.go b/vendor/github.com/hashicorp/terraform/terraform/eval_context_builtin.go index 67264948..a93c70d3 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_context_builtin.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_context_builtin.go @@ -72,6 +72,7 @@ type BuiltinEvalContext struct { ChangesValue *plans.ChangesSync StateValue *states.SyncState RefreshStateValue *states.SyncState + PrevRunStateValue *states.SyncState InstanceExpanderValue *instances.Expander } @@ -107,7 +108,7 @@ func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error { case HookActionHalt: // Return an early exit error to trigger an early exit log.Printf("[WARN] Early exit triggered by hook: %T", h) - return EvalEarlyExitError{} + return nil } } @@ -187,12 +188,12 @@ func (ctx *BuiltinEvalContext) ConfigureProvider(addr addrs.AbsProviderConfig, c return diags } - req := providers.ConfigureRequest{ + req := providers.ConfigureProviderRequest{ TerraformVersion: version.String(), Config: cfg, } - resp := p.Configure(req) + resp := p.ConfigureProvider(req) return resp.Diagnostics } @@ -228,48 +229,41 @@ func (ctx *BuiltinEvalContext) SetProviderInput(pc addrs.AbsProviderConfig, c ma ctx.ProviderLock.Unlock() } -func (ctx *BuiltinEvalContext) InitProvisioner(n string) error { - // If we already initialized, it is an error - if p := ctx.Provisioner(n); p != nil { - return fmt.Errorf("Provisioner '%s' already initialized", n) - } - - // Warning: make sure to acquire these locks AFTER the call to Provisioner - // above, since it also acquires locks. +func (ctx *BuiltinEvalContext) Provisioner(n string) (provisioners.Interface, error) { ctx.ProvisionerLock.Lock() defer ctx.ProvisionerLock.Unlock() - p, err := ctx.Components.ResourceProvisioner(n) - if err != nil { - return err - } - - ctx.ProvisionerCache[n] = p - - return nil -} + p, ok := ctx.ProvisionerCache[n] + if !ok { + var err error + p, err = ctx.Components.ResourceProvisioner(n) + if err != nil { + return nil, err + } -func (ctx *BuiltinEvalContext) Provisioner(n string) provisioners.Interface { - ctx.ProvisionerLock.Lock() - defer ctx.ProvisionerLock.Unlock() + ctx.ProvisionerCache[n] = p + } - return ctx.ProvisionerCache[n] + return p, nil } func (ctx *BuiltinEvalContext) ProvisionerSchema(n string) *configschema.Block { return ctx.Schemas.ProvisionerConfig(n) } -func (ctx *BuiltinEvalContext) CloseProvisioner(n string) error { +func (ctx *BuiltinEvalContext) CloseProvisioners() error { + var diags tfdiags.Diagnostics ctx.ProvisionerLock.Lock() defer ctx.ProvisionerLock.Unlock() - prov := ctx.ProvisionerCache[n] - if prov != nil { - return prov.Close() + for name, prov := range ctx.ProvisionerCache { + err := prov.Close() + if err != nil { + diags = diags.Append(fmt.Errorf("provisioner.Close %s: %s", name, err)) + } } - return nil + return diags.Err() } func (ctx *BuiltinEvalContext) EvaluateBlock(body hcl.Body, schema *configschema.Block, self addrs.Referenceable, keyData InstanceKeyEvalData) (cty.Value, hcl.Body, tfdiags.Diagnostics) { @@ -297,7 +291,19 @@ func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, keyData InstanceKeyData: keyData, Operation: ctx.Evaluator.Operation, } - return ctx.Evaluator.Scope(data, self) + scope := ctx.Evaluator.Scope(data, self) + + // ctx.PathValue is the path of the module that contains whatever + // expression the caller will be trying to evaluate, so this will + // activate only the experiments from that particular module, to + // be consistent with how experiment checking in the "configs" + // package itself works. The nil check here is for robustness in + // incompletely-mocked testing situations; mc should never be nil in + // real situations. + if mc := ctx.Evaluator.Config.DescendentForInstance(ctx.PathValue); mc != nil { + scope.SetActiveExperiments(mc.Module.ActiveExperiments) + } + return scope } func (ctx *BuiltinEvalContext) Path() addrs.ModuleInstance { @@ -320,7 +326,6 @@ func (ctx *BuiltinEvalContext) SetModuleCallArguments(n addrs.ModuleCallInstance args := ctx.VariableValues[key] if args == nil { - args = make(map[string]cty.Value) ctx.VariableValues[key] = vals return } @@ -355,6 +360,10 @@ func (ctx *BuiltinEvalContext) RefreshState() *states.SyncState { return ctx.RefreshStateValue } +func (ctx *BuiltinEvalContext) PrevRunState() *states.SyncState { + return ctx.PrevRunStateValue +} + func (ctx *BuiltinEvalContext) InstanceExpander() *instances.Expander { return ctx.InstanceExpanderValue } diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_context_mock.go b/vendor/github.com/hashicorp/terraform/terraform/eval_context_mock.go index 11ae6941..16b3b0fb 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_context_mock.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_context_mock.go @@ -55,16 +55,14 @@ type MockEvalContext struct { SetProviderInputAddr addrs.AbsProviderConfig SetProviderInputValues map[string]cty.Value + ConfigureProviderFn func( + addr addrs.AbsProviderConfig, + cfg cty.Value) tfdiags.Diagnostics // overrides the other values below, if set ConfigureProviderCalled bool ConfigureProviderAddr addrs.AbsProviderConfig ConfigureProviderConfig cty.Value ConfigureProviderDiags tfdiags.Diagnostics - InitProvisionerCalled bool - InitProvisionerName string - InitProvisionerProvisioner provisioners.Interface - InitProvisionerError error - ProvisionerCalled bool ProvisionerName string ProvisionerProvisioner provisioners.Interface @@ -73,9 +71,7 @@ type MockEvalContext struct { ProvisionerSchemaName string ProvisionerSchemaSchema *configschema.Block - CloseProvisionerCalled bool - CloseProvisionerName string - CloseProvisionerProvisioner provisioners.Interface + CloseProvisionersCalled bool EvaluateBlockCalled bool EvaluateBlockBody hcl.Body @@ -129,6 +125,9 @@ type MockEvalContext struct { RefreshStateCalled bool RefreshStateState *states.SyncState + PrevRunStateCalled bool + PrevRunStateState *states.SyncState + InstanceExpanderCalled bool InstanceExpanderExpander *instances.Expander } @@ -183,9 +182,13 @@ func (c *MockEvalContext) CloseProvider(addr addrs.AbsProviderConfig) error { } func (c *MockEvalContext) ConfigureProvider(addr addrs.AbsProviderConfig, cfg cty.Value) tfdiags.Diagnostics { + c.ConfigureProviderCalled = true c.ConfigureProviderAddr = addr c.ConfigureProviderConfig = cfg + if c.ConfigureProviderFn != nil { + return c.ConfigureProviderFn(addr, cfg) + } return c.ConfigureProviderDiags } @@ -201,16 +204,10 @@ func (c *MockEvalContext) SetProviderInput(addr addrs.AbsProviderConfig, vals ma c.SetProviderInputValues = vals } -func (c *MockEvalContext) InitProvisioner(n string) error { - c.InitProvisionerCalled = true - c.InitProvisionerName = n - return c.InitProvisionerError -} - -func (c *MockEvalContext) Provisioner(n string) provisioners.Interface { +func (c *MockEvalContext) Provisioner(n string) (provisioners.Interface, error) { c.ProvisionerCalled = true c.ProvisionerName = n - return c.ProvisionerProvisioner + return c.ProvisionerProvisioner, nil } func (c *MockEvalContext) ProvisionerSchema(n string) *configschema.Block { @@ -219,9 +216,8 @@ func (c *MockEvalContext) ProvisionerSchema(n string) *configschema.Block { return c.ProvisionerSchemaSchema } -func (c *MockEvalContext) CloseProvisioner(n string) error { - c.CloseProvisionerCalled = true - c.CloseProvisionerName = n +func (c *MockEvalContext) CloseProvisioners() error { + c.CloseProvisionersCalled = true return nil } @@ -346,6 +342,11 @@ func (c *MockEvalContext) RefreshState() *states.SyncState { return c.RefreshStateState } +func (c *MockEvalContext) PrevRunState() *states.SyncState { + c.PrevRunStateCalled = true + return c.PrevRunStateState +} + func (c *MockEvalContext) InstanceExpander() *instances.Expander { c.InstanceExpanderCalled = true return c.InstanceExpanderExpander diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_count.go b/vendor/github.com/hashicorp/terraform/terraform/eval_count.go index 52470779..38a41b8d 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_count.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_count.go @@ -60,6 +60,10 @@ func evaluateCountExpressionValue(expr hcl.Expression, ctx EvalContext) (cty.Val return nullCount, diags } + // Unmark the count value, sensitive values are allowed in count but not for_each, + // as using it here will not disclose the sensitive value + countVal, _ = countVal.Unmark() + switch { case countVal.IsNull(): diags = diags.Append(&hcl.Diagnostic{ diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_count_test.go b/vendor/github.com/hashicorp/terraform/terraform/eval_count_test.go new file mode 100644 index 00000000..11e25400 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_count_test.go @@ -0,0 +1,45 @@ +package terraform + +import ( + "reflect" + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hcltest" + "github.com/zclconf/go-cty/cty" +) + +func TestEvaluateCountExpression(t *testing.T) { + tests := map[string]struct { + Expr hcl.Expression + Count int + }{ + "zero": { + hcltest.MockExprLiteral(cty.NumberIntVal(0)), + 0, + }, + "expression with marked value": { + hcltest.MockExprLiteral(cty.NumberIntVal(8).Mark("sensitive")), + 8, + }, + } + for name, test := range tests { + t.Run(name, func(t *testing.T) { + ctx := &MockEvalContext{} + ctx.installSimpleEval() + countVal, diags := evaluateCountExpression(test.Expr, ctx) + + if len(diags) != 0 { + t.Errorf("unexpected diagnostics %s", spew.Sdump(diags)) + } + + if !reflect.DeepEqual(countVal, test.Count) { + t.Errorf( + "wrong map value\ngot: %swant: %s", + spew.Sdump(countVal), spew.Sdump(test.Count), + ) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_diff.go b/vendor/github.com/hashicorp/terraform/terraform/eval_diff.go deleted file mode 100644 index cc3a9217..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_diff.go +++ /dev/null @@ -1,899 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - "reflect" - "strings" - - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/configs" - "github.com/hashicorp/terraform/plans" - "github.com/hashicorp/terraform/plans/objchange" - "github.com/hashicorp/terraform/providers" - "github.com/hashicorp/terraform/states" - "github.com/hashicorp/terraform/tfdiags" -) - -// EvalCheckPlannedChange is an EvalNode implementation that produces errors -// if the _actual_ expected value is not compatible with what was recorded -// in the plan. -// -// Errors here are most often indicative of a bug in the provider, so our -// error messages will report with that in mind. It's also possible that -// there's a bug in Terraform's Core's own "proposed new value" code in -// EvalDiff. -type EvalCheckPlannedChange struct { - Addr addrs.ResourceInstance - ProviderAddr addrs.AbsProviderConfig - ProviderSchema **ProviderSchema - - // We take ResourceInstanceChange objects here just because that's what's - // convenient to pass in from the evaltree implementation, but we really - // only look at the "After" value of each change. - Planned, Actual **plans.ResourceInstanceChange -} - -func (n *EvalCheckPlannedChange) Eval(ctx EvalContext) (interface{}, error) { - providerSchema := *n.ProviderSchema - plannedChange := *n.Planned - actualChange := *n.Actual - - schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // Should be caught during validation, so we don't bother with a pretty error here - return nil, fmt.Errorf("provider does not support %q", n.Addr.Resource.Type) - } - - var diags tfdiags.Diagnostics - absAddr := n.Addr.Absolute(ctx.Path()) - - log.Printf("[TRACE] EvalCheckPlannedChange: Verifying that actual change (action %s) matches planned change (action %s)", actualChange.Action, plannedChange.Action) - - if plannedChange.Action != actualChange.Action { - switch { - case plannedChange.Action == plans.Update && actualChange.Action == plans.NoOp: - // It's okay for an update to become a NoOp once we've filled in - // all of the unknown values, since the final values might actually - // match what was there before after all. - log.Printf("[DEBUG] After incorporating new values learned so far during apply, %s change has become NoOp", absAddr) - - case (plannedChange.Action == plans.CreateThenDelete && actualChange.Action == plans.DeleteThenCreate) || - (plannedChange.Action == plans.DeleteThenCreate && actualChange.Action == plans.CreateThenDelete): - // If the order of replacement changed, then that is a bug in terraform - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Terraform produced inconsistent final plan", - fmt.Sprintf( - "When expanding the plan for %s to include new values learned so far during apply, the planned action changed from %s to %s.\n\nThis is a bug in Terraform and should be reported.", - absAddr, plannedChange.Action, actualChange.Action, - ), - )) - default: - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced inconsistent final plan", - fmt.Sprintf( - "When expanding the plan for %s to include new values learned so far during apply, provider %q changed the planned action from %s to %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - absAddr, n.ProviderAddr.Provider.String(), - plannedChange.Action, actualChange.Action, - ), - )) - } - } - - errs := objchange.AssertObjectCompatible(schema, plannedChange.After, actualChange.After) - for _, err := range errs { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced inconsistent final plan", - fmt.Sprintf( - "When expanding the plan for %s to include new values learned so far during apply, provider %q produced an invalid new value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - absAddr, n.ProviderAddr.Provider.String(), tfdiags.FormatError(err), - ), - )) - } - return nil, diags.Err() -} - -// EvalDiff is an EvalNode implementation that detects changes for a given -// resource instance. -type EvalDiff struct { - Addr addrs.ResourceInstance - Config *configs.Resource - Provider *providers.Interface - ProviderAddr addrs.AbsProviderConfig - ProviderMetas map[addrs.Provider]*configs.ProviderMeta - ProviderSchema **ProviderSchema - State **states.ResourceInstanceObject - PreviousDiff **plans.ResourceInstanceChange - - // CreateBeforeDestroy is set if either the resource's own config sets - // create_before_destroy explicitly or if dependencies have forced the - // resource to be handled as create_before_destroy in order to avoid - // a dependency cycle. - CreateBeforeDestroy bool - - OutputChange **plans.ResourceInstanceChange - OutputState **states.ResourceInstanceObject - - Stub bool -} - -// TODO: test -func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) { - state := *n.State - config := *n.Config - provider := *n.Provider - providerSchema := *n.ProviderSchema - - createBeforeDestroy := n.CreateBeforeDestroy - if n.PreviousDiff != nil { - // If we already planned the action, we stick to that plan - createBeforeDestroy = (*n.PreviousDiff).Action == plans.CreateThenDelete - } - - if providerSchema == nil { - return nil, fmt.Errorf("provider schema is unavailable for %s", n.Addr) - } - if n.ProviderAddr.Provider.Type == "" { - panic(fmt.Sprintf("EvalDiff for %s does not have ProviderAddr set", n.Addr.Absolute(ctx.Path()))) - } - - var diags tfdiags.Diagnostics - - // Evaluate the configuration - schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // Should be caught during validation, so we don't bother with a pretty error here - return nil, fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Type) - } - forEach, _ := evaluateForEachExpression(n.Config.ForEach, ctx) - keyData := EvalDataForInstanceKey(n.Addr.Key, forEach) - origConfigVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema, nil, keyData) - diags = diags.Append(configDiags) - if configDiags.HasErrors() { - return nil, diags.Err() - } - - metaConfigVal := cty.NullVal(cty.DynamicPseudoType) - if n.ProviderMetas != nil { - if m, ok := n.ProviderMetas[n.ProviderAddr.Provider]; ok && m != nil { - // if the provider doesn't support this feature, throw an error - if (*n.ProviderSchema).ProviderMeta == nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: fmt.Sprintf("Provider %s doesn't support provider_meta", n.ProviderAddr.Provider.String()), - Detail: fmt.Sprintf("The resource %s belongs to a provider that doesn't support provider_meta blocks", n.Addr), - Subject: &m.ProviderRange, - }) - } else { - var configDiags tfdiags.Diagnostics - metaConfigVal, _, configDiags = ctx.EvaluateBlock(m.Config, (*n.ProviderSchema).ProviderMeta, nil, EvalDataForNoInstanceKey) - diags = diags.Append(configDiags) - if configDiags.HasErrors() { - return nil, diags.Err() - } - } - } - } - - absAddr := n.Addr.Absolute(ctx.Path()) - var priorVal cty.Value - var priorValTainted cty.Value - var priorPrivate []byte - if state != nil { - if state.Status != states.ObjectTainted { - priorVal = state.Value - priorPrivate = state.Private - } else { - // If the prior state is tainted then we'll proceed below like - // we're creating an entirely new object, but then turn it into - // a synthetic "Replace" change at the end, creating the same - // result as if the provider had marked at least one argument - // change as "requires replacement". - priorValTainted = state.Value - priorVal = cty.NullVal(schema.ImpliedType()) - } - } else { - priorVal = cty.NullVal(schema.ImpliedType()) - } - - // Create an unmarked version of our config val and our prior val. - // Store the paths for the config val to re-markafter - // we've sent things over the wire. - unmarkedConfigVal, unmarkedPaths := origConfigVal.UnmarkDeepWithPaths() - unmarkedPriorVal, priorPaths := priorVal.UnmarkDeepWithPaths() - - // ignore_changes is meant to only apply to the configuration, so it must - // be applied before we generate a plan. This ensures the config used for - // the proposed value, the proposed value itself, and the config presented - // to the provider in the PlanResourceChange request all agree on the - // starting values. - configValIgnored, ignoreChangeDiags := n.processIgnoreChanges(unmarkedPriorVal, unmarkedConfigVal) - diags = diags.Append(ignoreChangeDiags) - if ignoreChangeDiags.HasErrors() { - return nil, diags.Err() - } - - proposedNewVal := objchange.ProposedNewObject(schema, unmarkedPriorVal, configValIgnored) - - // Call pre-diff hook - if !n.Stub { - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PreDiff(absAddr, states.CurrentGen, priorVal, proposedNewVal) - }) - if err != nil { - return nil, err - } - } - - log.Printf("[TRACE] Re-validating config for %q", n.Addr.Absolute(ctx.Path())) - // Allow the provider to validate the final set of values. - // The config was statically validated early on, but there may have been - // unknown values which the provider could not validate at the time. - validateResp := provider.ValidateResourceTypeConfig( - providers.ValidateResourceTypeConfigRequest{ - TypeName: n.Addr.Resource.Type, - Config: configValIgnored, - }, - ) - if validateResp.Diagnostics.HasErrors() { - return nil, validateResp.Diagnostics.InConfigBody(config.Config).Err() - } - - resp := provider.PlanResourceChange(providers.PlanResourceChangeRequest{ - TypeName: n.Addr.Resource.Type, - Config: configValIgnored, - PriorState: unmarkedPriorVal, - ProposedNewState: proposedNewVal, - PriorPrivate: priorPrivate, - ProviderMeta: metaConfigVal, - }) - diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config)) - if diags.HasErrors() { - return nil, diags.Err() - } - - plannedNewVal := resp.PlannedState - plannedPrivate := resp.PlannedPrivate - - if plannedNewVal == cty.NilVal { - // Should never happen. Since real-world providers return via RPC a nil - // is always a bug in the client-side stub. This is more likely caused - // by an incompletely-configured mock provider in tests, though. - panic(fmt.Sprintf("PlanResourceChange of %s produced nil value", absAddr.String())) - } - - // We allow the planned new value to disagree with configuration _values_ - // here, since that allows the provider to do special logic like a - // DiffSuppressFunc, but we still require that the provider produces - // a value whose type conforms to the schema. - for _, err := range plannedNewVal.Type().TestConformance(schema.ImpliedType()) { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced invalid plan", - fmt.Sprintf( - "Provider %q planned an invalid value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.Provider.String(), tfdiags.FormatErrorPrefixed(err, absAddr.String()), - ), - )) - } - if diags.HasErrors() { - return nil, diags.Err() - } - - if errs := objchange.AssertPlanValid(schema, unmarkedPriorVal, configValIgnored, plannedNewVal); len(errs) > 0 { - if resp.LegacyTypeSystem { - // The shimming of the old type system in the legacy SDK is not precise - // enough to pass this consistency check, so we'll give it a pass here, - // but we will generate a warning about it so that we are more likely - // to notice in the logs if an inconsistency beyond the type system - // leads to a downstream provider failure. - var buf strings.Builder - fmt.Fprintf(&buf, - "[WARN] Provider %q produced an invalid plan for %s, but we are tolerating it because it is using the legacy plugin SDK.\n The following problems may be the cause of any confusing errors from downstream operations:", - n.ProviderAddr.Provider.String(), absAddr, - ) - for _, err := range errs { - fmt.Fprintf(&buf, "\n - %s", tfdiags.FormatError(err)) - } - log.Print(buf.String()) - } else { - for _, err := range errs { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced invalid plan", - fmt.Sprintf( - "Provider %q planned an invalid value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.Provider.String(), tfdiags.FormatErrorPrefixed(err, absAddr.String()), - ), - )) - } - return nil, diags.Err() - } - } - - // Add the marks back to the planned new value -- this must happen after ignore changes - // have been processed - unmarkedPlannedNewVal := plannedNewVal - if len(unmarkedPaths) > 0 { - plannedNewVal = plannedNewVal.MarkWithPaths(unmarkedPaths) - } - - // The provider produces a list of paths to attributes whose changes mean - // that we must replace rather than update an existing remote object. - // However, we only need to do that if the identified attributes _have_ - // actually changed -- particularly after we may have undone some of the - // changes in processIgnoreChanges -- so now we'll filter that list to - // include only where changes are detected. - reqRep := cty.NewPathSet() - if len(resp.RequiresReplace) > 0 { - for _, path := range resp.RequiresReplace { - if priorVal.IsNull() { - // If prior is null then we don't expect any RequiresReplace at all, - // because this is a Create action. - continue - } - - priorChangedVal, priorPathDiags := hcl.ApplyPath(unmarkedPriorVal, path, nil) - plannedChangedVal, plannedPathDiags := hcl.ApplyPath(plannedNewVal, path, nil) - if plannedPathDiags.HasErrors() && priorPathDiags.HasErrors() { - // This means the path was invalid in both the prior and new - // values, which is an error with the provider itself. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced invalid plan", - fmt.Sprintf( - "Provider %q has indicated \"requires replacement\" on %s for a non-existent attribute path %#v.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.Provider.String(), absAddr, path, - ), - )) - continue - } - - // Make sure we have valid Values for both values. - // Note: if the opposing value was of the type - // cty.DynamicPseudoType, the type assigned here may not exactly - // match the schema. This is fine here, since we're only going to - // check for equality, but if the NullVal is to be used, we need to - // check the schema for th true type. - switch { - case priorChangedVal == cty.NilVal && plannedChangedVal == cty.NilVal: - // this should never happen without ApplyPath errors above - panic("requires replace path returned 2 nil values") - case priorChangedVal == cty.NilVal: - priorChangedVal = cty.NullVal(plannedChangedVal.Type()) - case plannedChangedVal == cty.NilVal: - plannedChangedVal = cty.NullVal(priorChangedVal.Type()) - } - - // Unmark for this value for the equality test. If only sensitivity has changed, - // this does not require an Update or Replace - unmarkedPlannedChangedVal, _ := plannedChangedVal.UnmarkDeep() - eqV := unmarkedPlannedChangedVal.Equals(priorChangedVal) - if !eqV.IsKnown() || eqV.False() { - reqRep.Add(path) - } - } - if diags.HasErrors() { - return nil, diags.Err() - } - } - - // Unmark for this test for value equality. - eqV := unmarkedPlannedNewVal.Equals(unmarkedPriorVal) - eq := eqV.IsKnown() && eqV.True() - - var action plans.Action - switch { - case priorVal.IsNull(): - action = plans.Create - case eq: - action = plans.NoOp - case !reqRep.Empty(): - // If there are any "requires replace" paths left _after our filtering - // above_ then this is a replace action. - if createBeforeDestroy { - action = plans.CreateThenDelete - } else { - action = plans.DeleteThenCreate - } - default: - action = plans.Update - // "Delete" is never chosen here, because deletion plans are always - // created more directly elsewhere, such as in "orphan" handling. - } - - if action.IsReplace() { - // In this strange situation we want to produce a change object that - // shows our real prior object but has a _new_ object that is built - // from a null prior object, since we're going to delete the one - // that has all the computed values on it. - // - // Therefore we'll ask the provider to plan again here, giving it - // a null object for the prior, and then we'll meld that with the - // _actual_ prior state to produce a correctly-shaped replace change. - // The resulting change should show any computed attributes changing - // from known prior values to unknown values, unless the provider is - // able to predict new values for any of these computed attributes. - nullPriorVal := cty.NullVal(schema.ImpliedType()) - - // Since there is no prior state to compare after replacement, we need - // a new unmarked config from our original with no ignored values. - unmarkedConfigVal := origConfigVal - if origConfigVal.ContainsMarked() { - unmarkedConfigVal, _ = origConfigVal.UnmarkDeep() - } - - // create a new proposed value from the null state and the config - proposedNewVal = objchange.ProposedNewObject(schema, nullPriorVal, unmarkedConfigVal) - - resp = provider.PlanResourceChange(providers.PlanResourceChangeRequest{ - TypeName: n.Addr.Resource.Type, - Config: unmarkedConfigVal, - PriorState: nullPriorVal, - ProposedNewState: proposedNewVal, - PriorPrivate: plannedPrivate, - ProviderMeta: metaConfigVal, - }) - // We need to tread carefully here, since if there are any warnings - // in here they probably also came out of our previous call to - // PlanResourceChange above, and so we don't want to repeat them. - // Consequently, we break from the usual pattern here and only - // append these new diagnostics if there's at least one error inside. - if resp.Diagnostics.HasErrors() { - diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config)) - return nil, diags.Err() - } - plannedNewVal = resp.PlannedState - plannedPrivate = resp.PlannedPrivate - - if len(unmarkedPaths) > 0 { - plannedNewVal = plannedNewVal.MarkWithPaths(unmarkedPaths) - } - - for _, err := range plannedNewVal.Type().TestConformance(schema.ImpliedType()) { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced invalid plan", - fmt.Sprintf( - "Provider %q planned an invalid value for %s%s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.Provider.String(), absAddr, tfdiags.FormatError(err), - ), - )) - } - if diags.HasErrors() { - return nil, diags.Err() - } - } - - // If our prior value was tainted then we actually want this to appear - // as a replace change, even though so far we've been treating it as a - // create. - if action == plans.Create && priorValTainted != cty.NilVal { - if createBeforeDestroy { - action = plans.CreateThenDelete - } else { - action = plans.DeleteThenCreate - } - priorVal = priorValTainted - } - - // If we plan to write or delete sensitive paths from state, - // this is an Update action - if action == plans.NoOp && !reflect.DeepEqual(priorPaths, unmarkedPaths) { - action = plans.Update - } - - // As a special case, if we have a previous diff (presumably from the plan - // phases, whereas we're now in the apply phase) and it was for a replace, - // we've already deleted the original object from state by the time we - // get here and so we would've ended up with a _create_ action this time, - // which we now need to paper over to get a result consistent with what - // we originally intended. - if n.PreviousDiff != nil { - prevChange := *n.PreviousDiff - if prevChange.Action.IsReplace() && action == plans.Create { - log.Printf("[TRACE] EvalDiff: %s treating Create change as %s change to match with earlier plan", absAddr, prevChange.Action) - action = prevChange.Action - priorVal = prevChange.Before - } - } - - // Call post-refresh hook - if !n.Stub { - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostDiff(absAddr, states.CurrentGen, action, priorVal, plannedNewVal) - }) - if err != nil { - return nil, err - } - } - - // Update our output if we care - if n.OutputChange != nil { - *n.OutputChange = &plans.ResourceInstanceChange{ - Addr: absAddr, - Private: plannedPrivate, - ProviderAddr: n.ProviderAddr, - Change: plans.Change{ - Action: action, - Before: priorVal, - // Pass the marked planned value through in our change - // to propogate through evaluation. - // Marks will be removed when encoding. - After: plannedNewVal, - }, - RequiredReplace: reqRep, - } - } - - // Update the state if we care - if n.OutputState != nil { - *n.OutputState = &states.ResourceInstanceObject{ - // We use the special "planned" status here to note that this - // object's value is not yet complete. Objects with this status - // cannot be used during expression evaluation, so the caller - // must _also_ record the returned change in the active plan, - // which the expression evaluator will use in preference to this - // incomplete value recorded in the state. - Status: states.ObjectPlanned, - Value: plannedNewVal, - Private: plannedPrivate, - } - } - - return nil, nil -} - -func (n *EvalDiff) processIgnoreChanges(prior, config cty.Value) (cty.Value, tfdiags.Diagnostics) { - // ignore_changes only applies when an object already exists, since we - // can't ignore changes to a thing we've not created yet. - if prior.IsNull() { - return config, nil - } - - ignoreChanges := n.Config.Managed.IgnoreChanges - ignoreAll := n.Config.Managed.IgnoreAllChanges - - if len(ignoreChanges) == 0 && !ignoreAll { - return config, nil - } - if ignoreAll { - return prior, nil - } - if prior.IsNull() || config.IsNull() { - // Ignore changes doesn't apply when we're creating for the first time. - // Proposed should never be null here, but if it is then we'll just let it be. - return config, nil - } - - return processIgnoreChangesIndividual(prior, config, ignoreChanges) -} - -func processIgnoreChangesIndividual(prior, config cty.Value, ignoreChanges []hcl.Traversal) (cty.Value, tfdiags.Diagnostics) { - // When we walk below we will be using cty.Path values for comparison, so - // we'll convert our traversals here so we can compare more easily. - ignoreChangesPath := make([]cty.Path, len(ignoreChanges)) - for i, traversal := range ignoreChanges { - path := make(cty.Path, len(traversal)) - for si, step := range traversal { - switch ts := step.(type) { - case hcl.TraverseRoot: - path[si] = cty.GetAttrStep{ - Name: ts.Name, - } - case hcl.TraverseAttr: - path[si] = cty.GetAttrStep{ - Name: ts.Name, - } - case hcl.TraverseIndex: - path[si] = cty.IndexStep{ - Key: ts.Key, - } - default: - panic(fmt.Sprintf("unsupported traversal step %#v", step)) - } - } - ignoreChangesPath[i] = path - } - - type ignoreChange struct { - // Path is the full path, minus any trailing map index - path cty.Path - // Value is the value we are to retain at the above path. If there is a - // key value, this must be a map and the desired value will be at the - // key index. - value cty.Value - // Key is the index key if the ignored path ends in a map index. - key cty.Value - } - var ignoredValues []ignoreChange - - // Find the actual changes first and store them in the ignoreChange struct. - // If the change was to a map value, and the key doesn't exist in the - // config, it would never be visited in the transform walk. - for _, icPath := range ignoreChangesPath { - key := cty.NullVal(cty.String) - // check for a map index, since maps are the only structure where we - // could have invalid path steps. - last, ok := icPath[len(icPath)-1].(cty.IndexStep) - if ok { - if last.Key.Type() == cty.String { - icPath = icPath[:len(icPath)-1] - key = last.Key - } - } - - // The structure should have been validated already, and we already - // trimmed the trailing map index. Any other intermediate index error - // means we wouldn't be able to apply the value below, so no need to - // record this. - p, err := icPath.Apply(prior) - if err != nil { - continue - } - c, err := icPath.Apply(config) - if err != nil { - continue - } - - // If this is a map, it is checking the entire map value for equality - // rather than the individual key. This means that the change is stored - // here even if our ignored key doesn't change. That is OK since it - // won't cause any changes in the transformation, but allows us to skip - // breaking up the maps and checking for key existence here too. - eq := p.Equals(c) - if eq.IsKnown() && eq.False() { - // there a change to ignore at this path, store the prior value - ignoredValues = append(ignoredValues, ignoreChange{icPath, p, key}) - } - } - - if len(ignoredValues) == 0 { - return config, nil - } - - ret, _ := cty.Transform(config, func(path cty.Path, v cty.Value) (cty.Value, error) { - for _, ignored := range ignoredValues { - if !path.Equals(ignored.path) { - return v, nil - } - - // no index, so we can return the entire value - if ignored.key.IsNull() { - return ignored.value, nil - } - - // we have an index key, so make sure we have a map - if !v.Type().IsMapType() { - // we'll let other validation catch any type mismatch - return v, nil - } - - // Now we know we are ignoring a specific index of this map, so get - // the config map and modify, add, or remove the desired key. - var configMap map[string]cty.Value - var priorMap map[string]cty.Value - - if !v.IsNull() { - if !v.IsKnown() { - // if the entire map is not known, we can't ignore any - // specific keys yet. - continue - } - configMap = v.AsValueMap() - } - if configMap == nil { - configMap = map[string]cty.Value{} - } - - // We also need to create a prior map, so we can check for - // existence while getting the value. Value.Index will always - // return null. - if !ignored.value.IsNull() { - priorMap = ignored.value.AsValueMap() - } - if priorMap == nil { - priorMap = map[string]cty.Value{} - } - - key := ignored.key.AsString() - priorElem, keep := priorMap[key] - - switch { - case !keep: - // this didn't exist in the old map value, so we're keeping the - // "absence" of the key by removing it from the config - delete(configMap, key) - default: - configMap[key] = priorElem - } - - if len(configMap) == 0 { - return cty.MapValEmpty(v.Type().ElementType()), nil - } - - return cty.MapVal(configMap), nil - } - return v, nil - }) - return ret, nil -} - -// EvalDiffDestroy is an EvalNode implementation that returns a plain -// destroy diff. -type EvalDiffDestroy struct { - Addr addrs.ResourceInstance - DeposedKey states.DeposedKey - State **states.ResourceInstanceObject - ProviderAddr addrs.AbsProviderConfig - - Output **plans.ResourceInstanceChange - OutputState **states.ResourceInstanceObject -} - -// TODO: test -func (n *EvalDiffDestroy) Eval(ctx EvalContext) (interface{}, error) { - absAddr := n.Addr.Absolute(ctx.Path()) - state := *n.State - - if n.ProviderAddr.Provider.Type == "" { - if n.DeposedKey == "" { - panic(fmt.Sprintf("EvalDiffDestroy for %s does not have ProviderAddr set", absAddr)) - } else { - panic(fmt.Sprintf("EvalDiffDestroy for %s (deposed %s) does not have ProviderAddr set", absAddr, n.DeposedKey)) - } - } - - // If there is no state or our attributes object is null then we're already - // destroyed. - if state == nil || state.Value.IsNull() { - return nil, nil - } - - // Call pre-diff hook - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PreDiff( - absAddr, n.DeposedKey.Generation(), - state.Value, - cty.NullVal(cty.DynamicPseudoType), - ) - }) - if err != nil { - return nil, err - } - - // Change is always the same for a destroy. We don't need the provider's - // help for this one. - // TODO: Should we give the provider an opportunity to veto this? - change := &plans.ResourceInstanceChange{ - Addr: absAddr, - DeposedKey: n.DeposedKey, - Change: plans.Change{ - Action: plans.Delete, - Before: state.Value, - After: cty.NullVal(cty.DynamicPseudoType), - }, - Private: state.Private, - ProviderAddr: n.ProviderAddr, - } - - // Call post-diff hook - err = ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostDiff( - absAddr, - n.DeposedKey.Generation(), - change.Action, - change.Before, - change.After, - ) - }) - if err != nil { - return nil, err - } - - // Update our output - *n.Output = change - - if n.OutputState != nil { - // Record our proposed new state, which is nil because we're destroying. - *n.OutputState = nil - } - - return nil, nil -} - -// EvalReduceDiff is an EvalNode implementation that takes a planned resource -// instance change as might be produced by EvalDiff or EvalDiffDestroy and -// "simplifies" it to a single atomic action to be performed by a specific -// graph node. -// -// Callers must specify whether they are a destroy node or a regular apply -// node. If the result is NoOp then the given change requires no action for -// the specific graph node calling this and so evaluation of the that graph -// node should exit early and take no action. -// -// The object written to OutChange may either be identical to InChange or -// a new change object derived from InChange. Because of the former case, the -// caller must not mutate the object returned in OutChange. -type EvalReduceDiff struct { - Addr addrs.ResourceInstance - InChange **plans.ResourceInstanceChange - Destroy bool - OutChange **plans.ResourceInstanceChange -} - -// TODO: test -func (n *EvalReduceDiff) Eval(ctx EvalContext) (interface{}, error) { - in := *n.InChange - out := in.Simplify(n.Destroy) - if n.OutChange != nil { - *n.OutChange = out - } - if out.Action != in.Action { - if n.Destroy { - log.Printf("[TRACE] EvalReduceDiff: %s change simplified from %s to %s for destroy node", n.Addr, in.Action, out.Action) - } else { - log.Printf("[TRACE] EvalReduceDiff: %s change simplified from %s to %s for apply node", n.Addr, in.Action, out.Action) - } - } - return nil, nil -} - -// EvalWriteDiff is an EvalNode implementation that saves a planned change -// for an instance object into the set of global planned changes. -type EvalWriteDiff struct { - Addr addrs.ResourceInstance - DeposedKey states.DeposedKey - ProviderSchema **ProviderSchema - Change **plans.ResourceInstanceChange -} - -// TODO: test -func (n *EvalWriteDiff) Eval(ctx EvalContext) (interface{}, error) { - changes := ctx.Changes() - addr := n.Addr.Absolute(ctx.Path()) - if n.Change == nil || *n.Change == nil { - // Caller sets nil to indicate that we need to remove a change from - // the set of changes. - gen := states.CurrentGen - if n.DeposedKey != states.NotDeposed { - gen = n.DeposedKey - } - changes.RemoveResourceInstanceChange(addr, gen) - return nil, nil - } - - providerSchema := *n.ProviderSchema - change := *n.Change - - if change.Addr.String() != addr.String() || change.DeposedKey != n.DeposedKey { - // Should never happen, and indicates a bug in the caller. - panic("inconsistent address and/or deposed key in EvalWriteDiff") - } - - schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // Should be caught during validation, so we don't bother with a pretty error here - return nil, fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Type) - } - - csrc, err := change.Encode(schema.ImpliedType()) - if err != nil { - return nil, fmt.Errorf("failed to encode planned changes for %s: %s", addr, err) - } - - changes.AppendResourceInstanceChange(csrc) - if n.DeposedKey == states.NotDeposed { - log.Printf("[TRACE] EvalWriteDiff: recorded %s change for %s", change.Action, addr) - } else { - log.Printf("[TRACE] EvalWriteDiff: recorded %s change for %s deposed object %s", change.Action, addr, n.DeposedKey) - } - - return nil, nil -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_diff_test.go b/vendor/github.com/hashicorp/terraform/terraform/eval_diff_test.go deleted file mode 100644 index 8f2a100d..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_diff_test.go +++ /dev/null @@ -1,276 +0,0 @@ -package terraform - -import ( - "testing" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" - "github.com/zclconf/go-cty/cty" -) - -func TestProcessIgnoreChangesIndividual(t *testing.T) { - tests := map[string]struct { - Old, New cty.Value - Ignore []string - Want cty.Value - }{ - "string": { - cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("a value"), - "b": cty.StringVal("b value"), - }), - cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("new a value"), - "b": cty.StringVal("new b value"), - }), - []string{"a"}, - cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("a value"), - "b": cty.StringVal("new b value"), - }), - }, - "changed type": { - cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("a value"), - "b": cty.StringVal("b value"), - }), - cty.ObjectVal(map[string]cty.Value{ - "a": cty.NumberIntVal(1), - "b": cty.StringVal("new b value"), - }), - []string{"a"}, - cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("a value"), - "b": cty.StringVal("new b value"), - }), - }, - "list": { - cty.ObjectVal(map[string]cty.Value{ - "a": cty.ListVal([]cty.Value{ - cty.StringVal("a0 value"), - cty.StringVal("a1 value"), - }), - "b": cty.StringVal("b value"), - }), - cty.ObjectVal(map[string]cty.Value{ - "a": cty.ListVal([]cty.Value{ - cty.StringVal("new a0 value"), - cty.StringVal("new a1 value"), - }), - "b": cty.StringVal("new b value"), - }), - []string{"a"}, - cty.ObjectVal(map[string]cty.Value{ - "a": cty.ListVal([]cty.Value{ - cty.StringVal("a0 value"), - cty.StringVal("a1 value"), - }), - "b": cty.StringVal("new b value"), - }), - }, - "list_index": { - cty.ObjectVal(map[string]cty.Value{ - "a": cty.ListVal([]cty.Value{ - cty.StringVal("a0 value"), - cty.StringVal("a1 value"), - }), - "b": cty.StringVal("b value"), - }), - cty.ObjectVal(map[string]cty.Value{ - "a": cty.ListVal([]cty.Value{ - cty.StringVal("new a0 value"), - cty.StringVal("new a1 value"), - }), - "b": cty.StringVal("new b value"), - }), - []string{"a[1]"}, - cty.ObjectVal(map[string]cty.Value{ - "a": cty.ListVal([]cty.Value{ - cty.StringVal("new a0 value"), - cty.StringVal("a1 value"), - }), - "b": cty.StringVal("new b value"), - }), - }, - "map_index": { - cty.ObjectVal(map[string]cty.Value{ - "a": cty.MapVal(map[string]cty.Value{ - "a0": cty.StringVal("a0 value"), - "a1": cty.StringVal("a1 value"), - }), - "b": cty.StringVal("b value"), - }), - cty.ObjectVal(map[string]cty.Value{ - "a": cty.MapVal(map[string]cty.Value{ - "a0": cty.StringVal("new a0 value"), - "a1": cty.StringVal("new a1 value"), - }), - "b": cty.StringVal("b value"), - }), - []string{`a["a1"]`}, - cty.ObjectVal(map[string]cty.Value{ - "a": cty.MapVal(map[string]cty.Value{ - "a0": cty.StringVal("new a0 value"), - "a1": cty.StringVal("a1 value"), - }), - "b": cty.StringVal("b value"), - }), - }, - "map_index_no_config": { - cty.ObjectVal(map[string]cty.Value{ - "a": cty.MapVal(map[string]cty.Value{ - "a0": cty.StringVal("a0 value"), - "a1": cty.StringVal("a1 value"), - }), - "b": cty.StringVal("b value"), - }), - cty.ObjectVal(map[string]cty.Value{ - "a": cty.NullVal(cty.Map(cty.String)), - "b": cty.StringVal("b value"), - }), - []string{`a["a1"]`}, - cty.ObjectVal(map[string]cty.Value{ - "a": cty.MapVal(map[string]cty.Value{ - "a1": cty.StringVal("a1 value"), - }), - "b": cty.StringVal("b value"), - }), - }, - "missing_map_index": { - cty.ObjectVal(map[string]cty.Value{ - "a": cty.MapVal(map[string]cty.Value{ - "a0": cty.StringVal("a0 value"), - "a1": cty.StringVal("a1 value"), - }), - "b": cty.StringVal("b value"), - }), - cty.ObjectVal(map[string]cty.Value{ - "a": cty.MapValEmpty(cty.String), - "b": cty.StringVal("b value"), - }), - []string{`a["a1"]`}, - cty.ObjectVal(map[string]cty.Value{ - "a": cty.MapVal(map[string]cty.Value{ - "a1": cty.StringVal("a1 value"), - }), - "b": cty.StringVal("b value"), - }), - }, - "missing_map_index_empty": { - cty.ObjectVal(map[string]cty.Value{ - "a": cty.MapValEmpty(cty.String), - }), - cty.ObjectVal(map[string]cty.Value{ - "a": cty.MapVal(map[string]cty.Value{ - "a": cty.StringVal("a0 value"), - }), - }), - []string{`a["a"]`}, - cty.ObjectVal(map[string]cty.Value{ - "a": cty.MapValEmpty(cty.String), - }), - }, - "missing_map_index_to_object": { - cty.ObjectVal(map[string]cty.Value{ - "a": cty.MapVal(map[string]cty.Value{ - "a": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("aa0"), - "b": cty.StringVal("ab0"), - }), - "b": cty.ObjectVal(map[string]cty.Value{ - "a": cty.StringVal("ba0"), - "b": cty.StringVal("bb0"), - }), - }), - }), - cty.ObjectVal(map[string]cty.Value{ - "a": cty.MapValEmpty( - cty.Object(map[string]cty.Type{ - "a": cty.String, - "b": cty.String, - }), - ), - }), - // we expect the config to be used here, as the ignore changes was - // `a["a"].b`, but the change was larger than that removing - // `a["a"]` entirely. - []string{`a["a"].b`}, - cty.ObjectVal(map[string]cty.Value{ - "a": cty.MapValEmpty( - cty.Object(map[string]cty.Type{ - "a": cty.String, - "b": cty.String, - }), - ), - }), - }, - "missing_prior_map_index": { - cty.ObjectVal(map[string]cty.Value{ - "a": cty.MapVal(map[string]cty.Value{ - "a0": cty.StringVal("a0 value"), - }), - "b": cty.StringVal("b value"), - }), - cty.ObjectVal(map[string]cty.Value{ - "a": cty.MapVal(map[string]cty.Value{ - "a0": cty.StringVal("a0 value"), - "a1": cty.StringVal("new a1 value"), - }), - "b": cty.StringVal("b value"), - }), - []string{`a["a1"]`}, - cty.ObjectVal(map[string]cty.Value{ - "a": cty.MapVal(map[string]cty.Value{ - "a0": cty.StringVal("a0 value"), - }), - "b": cty.StringVal("b value"), - }), - }, - "object attribute": { - cty.ObjectVal(map[string]cty.Value{ - "a": cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("a.foo value"), - "bar": cty.StringVal("a.bar value"), - }), - "b": cty.StringVal("b value"), - }), - cty.ObjectVal(map[string]cty.Value{ - "a": cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("new a.foo value"), - "bar": cty.StringVal("new a.bar value"), - }), - "b": cty.StringVal("new b value"), - }), - []string{"a.bar"}, - cty.ObjectVal(map[string]cty.Value{ - "a": cty.ObjectVal(map[string]cty.Value{ - "foo": cty.StringVal("new a.foo value"), - "bar": cty.StringVal("a.bar value"), - }), - "b": cty.StringVal("new b value"), - }), - }, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - ignore := make([]hcl.Traversal, len(test.Ignore)) - for i, ignoreStr := range test.Ignore { - trav, diags := hclsyntax.ParseTraversalAbs([]byte(ignoreStr), "", hcl.Pos{Line: 1, Column: 1}) - if diags.HasErrors() { - t.Fatalf("failed to parse %q: %s", ignoreStr, diags.Error()) - } - ignore[i] = trav - } - - ret, diags := processIgnoreChangesIndividual(test.Old, test.New, ignore) - if diags.HasErrors() { - t.Fatal(diags.Err()) - } - - if got, want := ret, test.Want; !want.RawEquals(got) { - t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want) - } - }) - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_error.go b/vendor/github.com/hashicorp/terraform/terraform/eval_error.go deleted file mode 100644 index 853ea2cc..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_error.go +++ /dev/null @@ -1,7 +0,0 @@ -package terraform - -// EvalEarlyExitError is a special error return value that can be returned -// by eval nodes that does an early exit. -type EvalEarlyExitError struct{} - -func (EvalEarlyExitError) Error() string { return "early exit" } diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_for_each.go b/vendor/github.com/hashicorp/terraform/terraform/eval_for_each.go index 75e6eabf..d2be0a2c 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_for_each.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_for_each.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/terraform/lang" "github.com/hashicorp/terraform/tfdiags" "github.com/zclconf/go-cty/cty" ) @@ -17,18 +18,11 @@ import ( // returning an error if the count value is not known, and converting the // cty.Value to a map[string]cty.Value for compatibility with other calls. func evaluateForEachExpression(expr hcl.Expression, ctx EvalContext) (forEach map[string]cty.Value, diags tfdiags.Diagnostics) { - forEachVal, diags := evaluateForEachExpressionValue(expr, ctx) - if !forEachVal.IsKnown() { - // Attach a diag as we do with count, with the same downsides - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid for_each argument", - Detail: `The "for_each" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the for_each depends on.`, - Subject: expr.Range().Ptr(), - }) - } + forEachVal, diags := evaluateForEachExpressionValue(expr, ctx, false) + // forEachVal might be unknown, but if it is then there should already + // be an error about it in diags, which we'll return below. - if forEachVal.IsNull() || !forEachVal.IsKnown() || forEachVal.LengthInt() == 0 { + if forEachVal.IsNull() || !forEachVal.IsKnown() || markSafeLengthInt(forEachVal) == 0 { // we check length, because an empty set return a nil map return map[string]cty.Value{}, diags } @@ -38,7 +32,7 @@ func evaluateForEachExpression(expr hcl.Expression, ctx EvalContext) (forEach ma // evaluateForEachExpressionValue is like evaluateForEachExpression // except that it returns a cty.Value map or set which can be unknown. -func evaluateForEachExpressionValue(expr hcl.Expression, ctx EvalContext) (cty.Value, tfdiags.Diagnostics) { +func evaluateForEachExpressionValue(expr hcl.Expression, ctx EvalContext, allowUnknown bool) (cty.Value, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics nullMap := cty.NullVal(cty.Map(cty.DynamicPseudoType)) @@ -46,16 +40,38 @@ func evaluateForEachExpressionValue(expr hcl.Expression, ctx EvalContext) (cty.V return nullMap, diags } - forEachVal, forEachDiags := ctx.EvaluateExpr(expr, cty.DynamicPseudoType, nil) + refs, moreDiags := lang.ReferencesInExpr(expr) + diags = diags.Append(moreDiags) + scope := ctx.EvaluationScope(nil, EvalDataForNoInstanceKey) + var hclCtx *hcl.EvalContext + if scope != nil { + hclCtx, moreDiags = scope.EvalContext(refs) + } else { + // This shouldn't happen in real code, but it can unfortunately arise + // in unit tests due to incompletely-implemented mocks. :( + hclCtx = &hcl.EvalContext{} + } + diags = diags.Append(moreDiags) + if diags.HasErrors() { // Can't continue if we don't even have a valid scope + return nullMap, diags + } + + forEachVal, forEachDiags := expr.Value(hclCtx) diags = diags.Append(forEachDiags) - if forEachVal.ContainsMarked() { + + // If a whole map is marked, or a set contains marked values (which means the set is then marked) + // give an error diagnostic as this value cannot be used in for_each + if forEachVal.IsMarked() { diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid for_each argument", - Detail: "Sensitive variable, or values derived from sensitive variables, cannot be used as for_each arguments. If used, the sensitive value could be exposed as a resource instance key.", - Subject: expr.Range().Ptr(), + Severity: hcl.DiagError, + Summary: "Invalid for_each argument", + Detail: "Sensitive values, or values derived from sensitive values, cannot be used as for_each arguments. If used, the sensitive value could be exposed as a resource instance key.", + Subject: expr.Range().Ptr(), + Expression: expr, + EvalContext: hclCtx, }) } + if diags.HasErrors() { return nullMap, diags } @@ -64,26 +80,40 @@ func evaluateForEachExpressionValue(expr hcl.Expression, ctx EvalContext) (cty.V switch { case forEachVal.IsNull(): diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid for_each argument", - Detail: `The given "for_each" argument value is unsuitable: the given "for_each" argument value is null. A map, or set of strings is allowed.`, - Subject: expr.Range().Ptr(), + Severity: hcl.DiagError, + Summary: "Invalid for_each argument", + Detail: `The given "for_each" argument value is unsuitable: the given "for_each" argument value is null. A map, or set of strings is allowed.`, + Subject: expr.Range().Ptr(), + Expression: expr, + EvalContext: hclCtx, }) return nullMap, diags case !forEachVal.IsKnown(): + if !allowUnknown { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid for_each argument", + Detail: errInvalidForEachUnknownDetail, + Subject: expr.Range().Ptr(), + Expression: expr, + EvalContext: hclCtx, + }) + } // ensure that we have a map, and not a DynamicValue return cty.UnknownVal(cty.Map(cty.DynamicPseudoType)), diags case !(ty.IsMapType() || ty.IsSetType() || ty.IsObjectType()): diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid for_each argument", - Detail: fmt.Sprintf(`The given "for_each" argument value is unsuitable: the "for_each" argument must be a map, or set of strings, and you have provided a value of type %s.`, ty.FriendlyName()), - Subject: expr.Range().Ptr(), + Severity: hcl.DiagError, + Summary: "Invalid for_each argument", + Detail: fmt.Sprintf(`The given "for_each" argument value is unsuitable: the "for_each" argument must be a map, or set of strings, and you have provided a value of type %s.`, ty.FriendlyName()), + Subject: expr.Range().Ptr(), + Expression: expr, + EvalContext: hclCtx, }) return nullMap, diags - case forEachVal.LengthInt() == 0: + case markSafeLengthInt(forEachVal) == 0: // If the map is empty ({}), return an empty map, because cty will // return nil when representing {} AsValueMap. This also covers an empty // set (toset([])) @@ -94,15 +124,27 @@ func evaluateForEachExpressionValue(expr hcl.Expression, ctx EvalContext) (cty.V // since we can't use a set values that are unknown, we treat the // entire set as unknown if !forEachVal.IsWhollyKnown() { + if !allowUnknown { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid for_each argument", + Detail: errInvalidForEachUnknownDetail, + Subject: expr.Range().Ptr(), + Expression: expr, + EvalContext: hclCtx, + }) + } return cty.UnknownVal(ty), diags } if ty.ElementType() != cty.String { diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid for_each set argument", - Detail: fmt.Sprintf(`The given "for_each" argument value is unsuitable: "for_each" supports maps and sets of strings, but you have provided a set containing type %s.`, forEachVal.Type().ElementType().FriendlyName()), - Subject: expr.Range().Ptr(), + Severity: hcl.DiagError, + Summary: "Invalid for_each set argument", + Detail: fmt.Sprintf(`The given "for_each" argument value is unsuitable: "for_each" supports maps and sets of strings, but you have provided a set containing type %s.`, forEachVal.Type().ElementType().FriendlyName()), + Subject: expr.Range().Ptr(), + Expression: expr, + EvalContext: hclCtx, }) return cty.NullVal(ty), diags } @@ -114,10 +156,12 @@ func evaluateForEachExpressionValue(expr hcl.Expression, ctx EvalContext) (cty.V item, _ := it.Element() if item.IsNull() { diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid for_each set argument", - Detail: fmt.Sprintf(`The given "for_each" argument value is unsuitable: "for_each" sets must not contain null values.`), - Subject: expr.Range().Ptr(), + Severity: hcl.DiagError, + Summary: "Invalid for_each set argument", + Detail: `The given "for_each" argument value is unsuitable: "for_each" sets must not contain null values.`, + Subject: expr.Range().Ptr(), + Expression: expr, + EvalContext: hclCtx, }) return cty.NullVal(ty), diags } @@ -126,3 +170,11 @@ func evaluateForEachExpressionValue(expr hcl.Expression, ctx EvalContext) (cty.V return forEachVal, nil } + +const errInvalidForEachUnknownDetail = `The "for_each" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the for_each depends on.` + +// markSafeLengthInt allows calling LengthInt on marked values safely +func markSafeLengthInt(val cty.Value) int { + v, _ := val.UnmarkDeep() + return v.LengthInt() +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_for_each_test.go b/vendor/github.com/hashicorp/terraform/terraform/eval_for_each_test.go index c7920a69..2f715663 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_for_each_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_for_each_test.go @@ -52,6 +52,16 @@ func TestEvaluateForEachExpression_valid(t *testing.T) { "b": cty.UnknownVal(cty.Bool), }, }, + "map containing sensitive values, but strings are literal": { + hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{ + "a": cty.BoolVal(true).Mark("sensitive"), + "b": cty.BoolVal(false), + })), + map[string]cty.Value{ + "a": cty.BoolVal(true).Mark("sensitive"), + "b": cty.BoolVal(false), + }, + }, } for name, test := range tests { @@ -110,6 +120,14 @@ func TestEvaluateForEachExpression_errors(t *testing.T) { "Invalid for_each argument", "depends on resource attributes that cannot be determined until apply", }, + "marked map": { + hcltest.MockExprLiteral(cty.MapVal(map[string]cty.Value{ + "a": cty.BoolVal(true), + "b": cty.BoolVal(false), + }).Mark("sensitive")), + "Invalid for_each argument", + "Sensitive values, or values derived from sensitive values, cannot be used as for_each arguments. If used, the sensitive value could be exposed as a resource instance key.", + }, "set containing booleans": { hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.BoolVal(true)})), "Invalid for_each set argument", @@ -130,6 +148,11 @@ func TestEvaluateForEachExpression_errors(t *testing.T) { "Invalid for_each argument", "depends on resource attributes that cannot be determined until apply", }, + "set containing marked values": { + hcltest.MockExprLiteral(cty.SetVal([]cty.Value{cty.StringVal("beep").Mark("sensitive"), cty.StringVal("boop")})), + "Invalid for_each argument", + "Sensitive values, or values derived from sensitive values, cannot be used as for_each arguments. If used, the sensitive value could be exposed as a resource instance key.", + }, } for name, test := range tests { @@ -150,6 +173,16 @@ func TestEvaluateForEachExpression_errors(t *testing.T) { if got, want := diags[0].Description().Detail, test.DetailSubstring; !strings.Contains(got, want) { t.Errorf("wrong diagnostic detail %#v; want %#v", got, want) } + if fromExpr := diags[0].FromExpr(); fromExpr != nil { + if fromExpr.Expression == nil { + t.Errorf("diagnostic does not refer to an expression") + } + if fromExpr.EvalContext == nil { + t.Errorf("diagnostic does not refer to an EvalContext") + } + } else { + t.Errorf("diagnostic does not support FromExpr\ngot: %s", spew.Sdump(diags[0])) + } }) } } @@ -164,7 +197,7 @@ func TestEvaluateForEachExpressionKnown(t *testing.T) { t.Run(name, func(t *testing.T) { ctx := &MockEvalContext{} ctx.installSimpleEval() - forEachVal, diags := evaluateForEachExpressionValue(expr, ctx) + forEachVal, diags := evaluateForEachExpressionValue(expr, ctx, true) if len(diags) != 0 { t.Errorf("unexpected diagnostics %s", spew.Sdump(diags)) diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_provider.go b/vendor/github.com/hashicorp/terraform/terraform/eval_provider.go index 0af5964e..9b730377 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_provider.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_provider.go @@ -45,11 +45,11 @@ func buildProviderConfig(ctx EvalContext, addr addrs.AbsProviderConfig, config * } } -// GetProvider returns the providers.Interface and schema for a given provider. -func GetProvider(ctx EvalContext, addr addrs.AbsProviderConfig) (providers.Interface, *ProviderSchema, error) { +// getProvider returns the providers.Interface and schema for a given provider. +func getProvider(ctx EvalContext, addr addrs.AbsProviderConfig) (providers.Interface, *ProviderSchema, error) { if addr.Provider.Type == "" { // Should never happen - panic("EvalGetProvider used with uninitialized provider configuration address") + panic("GetProvider used with uninitialized provider configuration address") } provider := ctx.Provider(addr) if provider == nil { diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_read_data.go b/vendor/github.com/hashicorp/terraform/terraform/eval_read_data.go deleted file mode 100644 index 33eac066..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_read_data.go +++ /dev/null @@ -1,195 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/configs" - "github.com/hashicorp/terraform/plans" - "github.com/hashicorp/terraform/providers" - "github.com/hashicorp/terraform/states" - "github.com/hashicorp/terraform/tfdiags" -) - -// evalReadData implements shared methods and data for the individual data -// source eval nodes. -type evalReadData struct { - Addr addrs.ResourceInstance - Config *configs.Resource - Provider *providers.Interface - ProviderAddr addrs.AbsProviderConfig - ProviderMetas map[addrs.Provider]*configs.ProviderMeta - ProviderSchema **ProviderSchema - - // Planned is set when dealing with data resources that were deferred to - // the apply walk, to let us see what was planned. If this is set, the - // evaluation of the config is required to produce a wholly-known - // configuration which is consistent with the partial object included - // in this planned change. - Planned **plans.ResourceInstanceChange - - // State is the current state for the data source, and is updated once the - // new state has been read. - // While data sources are read-only, we need to start with the prior state - // to determine if we have a change or not. If we needed to read a new - // value, but it still matches the previous state, then we can record a - // NoNop change. If the states don't match then we record a Read change so - // that the new value is applied to the state. - State **states.ResourceInstanceObject - - // Output change records any change for this data source, which is - // interpreted differently than changes for managed resources. - // - During Refresh, this change is only used to correctly evaluate - // references to the data source, but it is not saved. - // - If a planned change has the action of plans.Read, it indicates that the - // data source could not be evaluated yet, and reading is being deferred to - // apply. - // - If planned action is plans.Update, it indicates that the data source - // was read, and the result needs to be stored in state during apply. - OutputChange **plans.ResourceInstanceChange - - // dependsOn stores the list of transitive resource addresses that any - // configuration depends_on references may resolve to. This is used to - // determine if there are any changes that will force this data sources to - // be deferred to apply. - dependsOn []addrs.ConfigResource - // forceDependsOn indicates that resources may be missing from dependsOn, - // but the parent module may have depends_on configured. - forceDependsOn bool -} - -// readDataSource handles everything needed to call ReadDataSource on the provider. -// A previously evaluated configVal can be passed in, or a new one is generated -// from the resource configuration. -func (n *evalReadData) readDataSource(ctx EvalContext, configVal cty.Value) (cty.Value, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - var newVal cty.Value - - config := *n.Config - absAddr := n.Addr.Absolute(ctx.Path()) - - if n.ProviderSchema == nil || *n.ProviderSchema == nil { - diags = diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr)) - return newVal, diags - } - - provider := *n.Provider - - providerSchema := *n.ProviderSchema - schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // Should be caught during validation, so we don't bother with a pretty error here - diags = diags.Append(fmt.Errorf("provider %q does not support data source %q", n.ProviderAddr.Provider.String(), n.Addr.Resource.Type)) - return newVal, diags - } - - metaConfigVal, metaDiags := n.providerMetas(ctx) - diags = diags.Append(metaDiags) - if diags.HasErrors() { - return newVal, diags - } - - log.Printf("[TRACE] EvalReadData: Re-validating config for %s", absAddr) - validateResp := provider.ValidateDataSourceConfig( - providers.ValidateDataSourceConfigRequest{ - TypeName: n.Addr.Resource.Type, - Config: configVal, - }, - ) - if validateResp.Diagnostics.HasErrors() { - return newVal, validateResp.Diagnostics.InConfigBody(config.Config) - } - - // If we get down here then our configuration is complete and we're read - // to actually call the provider to read the data. - log.Printf("[TRACE] EvalReadData: %s configuration is complete, so reading from provider", absAddr) - - resp := provider.ReadDataSource(providers.ReadDataSourceRequest{ - TypeName: n.Addr.Resource.Type, - Config: configVal, - ProviderMeta: metaConfigVal, - }) - diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config)) - if diags.HasErrors() { - return newVal, diags - } - newVal = resp.State - if newVal == cty.NilVal { - // This can happen with incompletely-configured mocks. We'll allow it - // and treat it as an alias for a properly-typed null value. - newVal = cty.NullVal(schema.ImpliedType()) - } - - for _, err := range newVal.Type().TestConformance(schema.ImpliedType()) { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced invalid object", - fmt.Sprintf( - "Provider %q produced an invalid value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.Provider.String(), tfdiags.FormatErrorPrefixed(err, absAddr.String()), - ), - )) - } - if diags.HasErrors() { - return newVal, diags - } - - if newVal.IsNull() { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced null object", - fmt.Sprintf( - "Provider %q produced a null value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.Provider.String(), absAddr, - ), - )) - } - - if !newVal.IsNull() && !newVal.IsWhollyKnown() { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced invalid object", - fmt.Sprintf( - "Provider %q produced a value for %s that is not wholly known.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.Provider.String(), absAddr, - ), - )) - - // We'll still save the object, but we need to eliminate any unknown - // values first because we can't serialize them in the state file. - // Note that this may cause set elements to be coalesced if they - // differed only by having unknown values, but we don't worry about - // that here because we're saving the value only for inspection - // purposes; the error we added above will halt the graph walk. - newVal = cty.UnknownAsNull(newVal) - } - - return newVal, diags -} - -func (n *evalReadData) providerMetas(ctx EvalContext) (cty.Value, tfdiags.Diagnostics) { - var diags tfdiags.Diagnostics - metaConfigVal := cty.NullVal(cty.DynamicPseudoType) - if n.ProviderMetas != nil { - if m, ok := n.ProviderMetas[n.ProviderAddr.Provider]; ok && m != nil { - // if the provider doesn't support this feature, throw an error - if (*n.ProviderSchema).ProviderMeta == nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: fmt.Sprintf("Provider %s doesn't support provider_meta", n.ProviderAddr.Provider.String()), - Detail: fmt.Sprintf("The resource %s belongs to a provider that doesn't support provider_meta blocks", n.Addr), - Subject: &m.ProviderRange, - }) - } else { - var configDiags tfdiags.Diagnostics - metaConfigVal, _, configDiags = ctx.EvaluateBlock(m.Config, (*n.ProviderSchema).ProviderMeta, nil, EvalDataForNoInstanceKey) - diags = diags.Append(configDiags) - } - } - } - return metaConfigVal, diags -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_read_data_apply.go b/vendor/github.com/hashicorp/terraform/terraform/eval_read_data_apply.go deleted file mode 100644 index 179fe368..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_read_data_apply.go +++ /dev/null @@ -1,82 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform/plans" - "github.com/hashicorp/terraform/states" - "github.com/hashicorp/terraform/tfdiags" -) - -// evalReadDataApply is an EvalNode implementation that deals with the main part -// of the data resource lifecycle: either actually reading from the data source -// or generating a plan to do so. -type evalReadDataApply struct { - evalReadData -} - -func (n *evalReadDataApply) Eval(ctx EvalContext) (interface{}, error) { - absAddr := n.Addr.Absolute(ctx.Path()) - - var diags tfdiags.Diagnostics - - var planned *plans.ResourceInstanceChange - if n.Planned != nil { - planned = *n.Planned - } - - if n.ProviderSchema == nil || *n.ProviderSchema == nil { - return nil, fmt.Errorf("provider schema not available for %s", n.Addr) - } - - if planned != nil && planned.Action != plans.Read { - // If any other action gets in here then that's always a bug; this - // EvalNode only deals with reading. - return nil, fmt.Errorf( - "invalid action %s for %s: only Read is supported (this is a bug in Terraform; please report it!)", - planned.Action, absAddr, - ) - } - - if err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PreApply(absAddr, states.CurrentGen, planned.Action, planned.Before, planned.After) - }); err != nil { - return nil, err - } - - config := *n.Config - providerSchema := *n.ProviderSchema - schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // Should be caught during validation, so we don't bother with a pretty error here - return nil, fmt.Errorf("provider %q does not support data source %q", n.ProviderAddr.Provider.String(), n.Addr.Resource.Type) - } - - forEach, _ := evaluateForEachExpression(config.ForEach, ctx) - keyData := EvalDataForInstanceKey(n.Addr.Key, forEach) - - configVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema, nil, keyData) - diags = diags.Append(configDiags) - if configDiags.HasErrors() { - return nil, diags.ErrWithWarnings() - } - - newVal, readDiags := n.readDataSource(ctx, configVal) - diags = diags.Append(readDiags) - if diags.HasErrors() { - return nil, diags.ErrWithWarnings() - } - - *n.State = &states.ResourceInstanceObject{ - Value: newVal, - Status: states.ObjectReady, - } - - if err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostApply(absAddr, states.CurrentGen, newVal, diags.Err()) - }); err != nil { - diags = diags.Append(err) - } - - return nil, diags.ErrWithWarnings() -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_read_data_plan.go b/vendor/github.com/hashicorp/terraform/terraform/eval_read_data_plan.go deleted file mode 100644 index 22659429..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_read_data_plan.go +++ /dev/null @@ -1,173 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - "strings" - - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/plans" - "github.com/hashicorp/terraform/plans/objchange" - "github.com/hashicorp/terraform/states" - "github.com/hashicorp/terraform/tfdiags" -) - -// evalReadDataPlan is an EvalNode implementation that deals with the main part -// of the data resource lifecycle: either actually reading from the data source -// or generating a plan to do so. -type evalReadDataPlan struct { - evalReadData -} - -func (n *evalReadDataPlan) Eval(ctx EvalContext) (interface{}, error) { - absAddr := n.Addr.Absolute(ctx.Path()) - - var diags tfdiags.Diagnostics - var configVal cty.Value - - if n.ProviderSchema == nil || *n.ProviderSchema == nil { - return nil, fmt.Errorf("provider schema not available for %s", n.Addr) - } - - config := *n.Config - providerSchema := *n.ProviderSchema - schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // Should be caught during validation, so we don't bother with a pretty error here - return nil, fmt.Errorf("provider %q does not support data source %q", n.ProviderAddr.Provider.String(), n.Addr.Resource.Type) - } - - objTy := schema.ImpliedType() - priorVal := cty.NullVal(objTy) - if n.State != nil && *n.State != nil { - priorVal = (*n.State).Value - } - - forEach, _ := evaluateForEachExpression(config.ForEach, ctx) - keyData := EvalDataForInstanceKey(n.Addr.Key, forEach) - - var configDiags tfdiags.Diagnostics - configVal, _, configDiags = ctx.EvaluateBlock(config.Config, schema, nil, keyData) - diags = diags.Append(configDiags) - if configDiags.HasErrors() { - return nil, diags.ErrWithWarnings() - } - - configKnown := configVal.IsWhollyKnown() - // If our configuration contains any unknown values, or we depend on any - // unknown values then we must defer the read to the apply phase by - // producing a "Read" change for this resource, and a placeholder value for - // it in the state. - if n.forcePlanRead(ctx) || !configKnown { - if configKnown { - log.Printf("[TRACE] evalReadDataPlan: %s configuration is fully known, but we're forcing a read plan to be created", absAddr) - } else { - log.Printf("[TRACE] evalReadDataPlan: %s configuration not fully known yet, so deferring to apply phase", absAddr) - } - - proposedNewVal := objchange.PlannedDataResourceObject(schema, configVal) - - if err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PreDiff(absAddr, states.CurrentGen, priorVal, proposedNewVal) - }); err != nil { - diags = diags.Append(err) - return nil, diags.ErrWithWarnings() - } - - // Apply detects that the data source will need to be read by the After - // value containing unknowns from PlanDataResourceObject. - *n.OutputChange = &plans.ResourceInstanceChange{ - Addr: absAddr, - ProviderAddr: n.ProviderAddr, - Change: plans.Change{ - Action: plans.Read, - Before: priorVal, - After: proposedNewVal, - }, - } - - *n.State = &states.ResourceInstanceObject{ - Value: proposedNewVal, - Status: states.ObjectPlanned, - } - - if err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostDiff(absAddr, states.CurrentGen, plans.Read, priorVal, proposedNewVal) - }); err != nil { - diags = diags.Append(err) - } - - return nil, diags.ErrWithWarnings() - } - - // We have a complete configuration with no dependencies to wait on, so we - // can read the data source into the state. - newVal, readDiags := n.readDataSource(ctx, configVal) - diags = diags.Append(readDiags) - if diags.HasErrors() { - return nil, diags.ErrWithWarnings() - } - - // if we have a prior value, we can check for any irregularities in the response - if !priorVal.IsNull() { - // While we don't propose planned changes for data sources, we can - // generate a proposed value for comparison to ensure the data source - // is returning a result following the rules of the provider contract. - proposedVal := objchange.ProposedNewObject(schema, priorVal, configVal) - if errs := objchange.AssertObjectCompatible(schema, proposedVal, newVal); len(errs) > 0 { - // Resources have the LegacyTypeSystem field to signal when they are - // using an SDK which may not produce precise values. While data - // sources are read-only, they can still return a value which is not - // compatible with the config+schema. Since we can't detect the legacy - // type system, we can only warn about this for now. - var buf strings.Builder - fmt.Fprintf(&buf, "[WARN] Provider %q produced an unexpected new value for %s.", - n.ProviderAddr.Provider.String(), absAddr) - for _, err := range errs { - fmt.Fprintf(&buf, "\n - %s", tfdiags.FormatError(err)) - } - log.Print(buf.String()) - } - } - - *n.State = &states.ResourceInstanceObject{ - Value: newVal, - Status: states.ObjectReady, - } - - if err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostDiff(absAddr, states.CurrentGen, plans.Update, priorVal, newVal) - }); err != nil { - return nil, err - } - - return nil, diags.ErrWithWarnings() -} - -// forcePlanRead determines if we need to override the usual behavior of -// immediately reading from the data source where possible, instead forcing us -// to generate a plan. -func (n *evalReadDataPlan) forcePlanRead(ctx EvalContext) bool { - // Check and see if any depends_on dependencies have - // changes, since they won't show up as changes in the - // configuration. - changes := ctx.Changes() - for _, d := range n.dependsOn { - if d.Resource.Mode == addrs.DataResourceMode { - // Data sources have no external side effects, so they pose a need - // to delay this read. If they do have a change planned, it must be - // because of a dependency on a managed resource, in which case - // we'll also encounter it in this list of dependencies. - continue - } - - for _, change := range changes.GetChangesForConfigResource(d) { - if change != nil && change.Action != plans.NoOp { - return true - } - } - } - return false -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_refresh.go b/vendor/github.com/hashicorp/terraform/terraform/eval_refresh.go deleted file mode 100644 index 737484c4..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_refresh.go +++ /dev/null @@ -1,162 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - "strings" - - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/configs" - "github.com/hashicorp/terraform/plans/objchange" - "github.com/hashicorp/terraform/providers" - "github.com/hashicorp/terraform/states" - "github.com/hashicorp/terraform/tfdiags" -) - -// EvalRefresh is an EvalNode implementation that does a refresh for -// a resource. -type EvalRefresh struct { - Addr addrs.ResourceInstance - ProviderAddr addrs.AbsProviderConfig - Provider *providers.Interface - ProviderMetas map[addrs.Provider]*configs.ProviderMeta - ProviderSchema **ProviderSchema - State **states.ResourceInstanceObject - Output **states.ResourceInstanceObject -} - -// TODO: test -func (n *EvalRefresh) Eval(ctx EvalContext) (interface{}, error) { - state := *n.State - absAddr := n.Addr.Absolute(ctx.Path()) - - var diags tfdiags.Diagnostics - - // If we have no state, we don't do any refreshing - if state == nil { - log.Printf("[DEBUG] refresh: %s: no state, so not refreshing", n.Addr.Absolute(ctx.Path())) - return nil, diags.ErrWithWarnings() - } - - schema, _ := (*n.ProviderSchema).SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // Should be caught during validation, so we don't bother with a pretty error here - return nil, fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Type) - } - - metaConfigVal := cty.NullVal(cty.DynamicPseudoType) - if n.ProviderMetas != nil { - if m, ok := n.ProviderMetas[n.ProviderAddr.Provider]; ok && m != nil { - log.Printf("[DEBUG] EvalRefresh: ProviderMeta config value set") - // if the provider doesn't support this feature, throw an error - if (*n.ProviderSchema).ProviderMeta == nil { - log.Printf("[DEBUG] EvalRefresh: no ProviderMeta schema") - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: fmt.Sprintf("Provider %s doesn't support provider_meta", n.ProviderAddr.Provider.String()), - Detail: fmt.Sprintf("The resource %s belongs to a provider that doesn't support provider_meta blocks", n.Addr), - Subject: &m.ProviderRange, - }) - } else { - log.Printf("[DEBUG] EvalRefresh: ProviderMeta schema found: %+v", (*n.ProviderSchema).ProviderMeta) - var configDiags tfdiags.Diagnostics - metaConfigVal, _, configDiags = ctx.EvaluateBlock(m.Config, (*n.ProviderSchema).ProviderMeta, nil, EvalDataForNoInstanceKey) - diags = diags.Append(configDiags) - if configDiags.HasErrors() { - return nil, diags.Err() - } - } - } - } - - // Call pre-refresh hook - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PreRefresh(absAddr, states.CurrentGen, state.Value) - }) - if err != nil { - return nil, diags.ErrWithWarnings() - } - - // Refresh! - priorVal := state.Value - - // Unmarked before sending to provider - var priorPaths []cty.PathValueMarks - if priorVal.ContainsMarked() { - priorVal, priorPaths = priorVal.UnmarkDeepWithPaths() - } - - req := providers.ReadResourceRequest{ - TypeName: n.Addr.Resource.Type, - PriorState: priorVal, - Private: state.Private, - ProviderMeta: metaConfigVal, - } - - provider := *n.Provider - resp := provider.ReadResource(req) - diags = diags.Append(resp.Diagnostics) - if diags.HasErrors() { - return nil, diags.Err() - } - - if resp.NewState == cty.NilVal { - // This ought not to happen in real cases since it's not possible to - // send NilVal over the plugin RPC channel, but it can come up in - // tests due to sloppy mocking. - panic("new state is cty.NilVal") - } - - for _, err := range resp.NewState.Type().TestConformance(schema.ImpliedType()) { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Provider produced invalid object", - fmt.Sprintf( - "Provider %q planned an invalid value for %s during refresh: %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", - n.ProviderAddr.Provider.String(), absAddr, tfdiags.FormatError(err), - ), - )) - } - if diags.HasErrors() { - return nil, diags.Err() - } - - // We have no way to exempt provider using the legacy SDK from this check, - // so we can only log inconsistencies with the updated state values. - if errs := objchange.AssertObjectCompatible(schema, priorVal, resp.NewState); len(errs) > 0 { - var buf strings.Builder - fmt.Fprintf(&buf, "[WARN] Provider %q produced an unexpected new value for %s during refresh.", n.ProviderAddr.Provider.String(), absAddr) - for _, err := range errs { - fmt.Fprintf(&buf, "\n - %s", tfdiags.FormatError(err)) - } - log.Print(buf.String()) - } - - newState := state.DeepCopy() - newState.Value = resp.NewState - newState.Private = resp.Private - newState.Dependencies = state.Dependencies - newState.CreateBeforeDestroy = state.CreateBeforeDestroy - - // Call post-refresh hook - err = ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostRefresh(absAddr, states.CurrentGen, priorVal, newState.Value) - }) - if err != nil { - return nil, err - } - - // Mark the value if necessary - if len(priorPaths) > 0 { - newState.Value = newState.Value.MarkWithPaths(priorPaths) - } - - if n.Output != nil { - *n.Output = newState - } - - return nil, diags.ErrWithWarnings() -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_state.go b/vendor/github.com/hashicorp/terraform/terraform/eval_state.go deleted file mode 100644 index 1edab451..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_state.go +++ /dev/null @@ -1,481 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/configs" - "github.com/hashicorp/terraform/plans" - "github.com/hashicorp/terraform/providers" - "github.com/hashicorp/terraform/states" - "github.com/hashicorp/terraform/tfdiags" -) - -type phaseState int - -const ( - workingState phaseState = iota - refreshState -) - -// EvalReadState is an EvalNode implementation that reads the -// current object for a specific instance in the state. -type EvalReadState struct { - // Addr is the address of the instance to read state for. - Addr addrs.ResourceInstance - - // ProviderSchema is the schema for the provider given in Provider. - ProviderSchema **ProviderSchema - - // Provider is the provider that will subsequently perform actions on - // the the state object. This is used to perform any schema upgrades - // that might be required to prepare the stored data for use. - Provider *providers.Interface - - // Output will be written with a pointer to the retrieved object. - Output **states.ResourceInstanceObject -} - -func (n *EvalReadState) Eval(ctx EvalContext) (interface{}, error) { - if n.Provider == nil || *n.Provider == nil { - panic("EvalReadState used with no Provider object") - } - if n.ProviderSchema == nil || *n.ProviderSchema == nil { - panic("EvalReadState used with no ProviderSchema object") - } - - absAddr := n.Addr.Absolute(ctx.Path()) - log.Printf("[TRACE] EvalReadState: reading state for %s", absAddr) - - src := ctx.State().ResourceInstanceObject(absAddr, states.CurrentGen) - if src == nil { - // Presumably we only have deposed objects, then. - log.Printf("[TRACE] EvalReadState: no state present for %s", absAddr) - return nil, nil - } - - schema, currentVersion := (*n.ProviderSchema).SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // Shouldn't happen since we should've failed long ago if no schema is present - return nil, fmt.Errorf("no schema available for %s while reading state; this is a bug in Terraform and should be reported", absAddr) - } - var diags tfdiags.Diagnostics - src, diags = UpgradeResourceState(absAddr, *n.Provider, src, schema, currentVersion) - if diags.HasErrors() { - // Note that we don't have any channel to return warnings here. We'll - // accept that for now since warnings during a schema upgrade would - // be pretty weird anyway, since this operation is supposed to seem - // invisible to the user. - return nil, diags.Err() - } - - obj, err := src.Decode(schema.ImpliedType()) - if err != nil { - return nil, err - } - - if n.Output != nil { - *n.Output = obj - } - return obj, nil -} - -// EvalReadStateDeposed is an EvalNode implementation that reads the -// deposed InstanceState for a specific resource out of the state -type EvalReadStateDeposed struct { - // Addr is the address of the instance to read state for. - Addr addrs.ResourceInstance - - // Key identifies which deposed object we will read. - Key states.DeposedKey - - // ProviderSchema is the schema for the provider given in Provider. - ProviderSchema **ProviderSchema - - // Provider is the provider that will subsequently perform actions on - // the the state object. This is used to perform any schema upgrades - // that might be required to prepare the stored data for use. - Provider *providers.Interface - - // Output will be written with a pointer to the retrieved object. - Output **states.ResourceInstanceObject -} - -func (n *EvalReadStateDeposed) Eval(ctx EvalContext) (interface{}, error) { - if n.Provider == nil || *n.Provider == nil { - panic("EvalReadStateDeposed used with no Provider object") - } - if n.ProviderSchema == nil || *n.ProviderSchema == nil { - panic("EvalReadStateDeposed used with no ProviderSchema object") - } - - key := n.Key - if key == states.NotDeposed { - return nil, fmt.Errorf("EvalReadStateDeposed used with no instance key; this is a bug in Terraform and should be reported") - } - absAddr := n.Addr.Absolute(ctx.Path()) - log.Printf("[TRACE] EvalReadStateDeposed: reading state for %s deposed object %s", absAddr, n.Key) - - src := ctx.State().ResourceInstanceObject(absAddr, key) - if src == nil { - // Presumably we only have deposed objects, then. - log.Printf("[TRACE] EvalReadStateDeposed: no state present for %s deposed object %s", absAddr, n.Key) - return nil, nil - } - - schema, currentVersion := (*n.ProviderSchema).SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // Shouldn't happen since we should've failed long ago if no schema is present - return nil, fmt.Errorf("no schema available for %s while reading state; this is a bug in Terraform and should be reported", absAddr) - } - var diags tfdiags.Diagnostics - src, diags = UpgradeResourceState(absAddr, *n.Provider, src, schema, currentVersion) - if diags.HasErrors() { - // Note that we don't have any channel to return warnings here. We'll - // accept that for now since warnings during a schema upgrade would - // be pretty weird anyway, since this operation is supposed to seem - // invisible to the user. - return nil, diags.Err() - } - - obj, err := src.Decode(schema.ImpliedType()) - if err != nil { - return nil, err - } - if n.Output != nil { - *n.Output = obj - } - return obj, nil -} - -// UpdateStateHook calls the PostStateUpdate hook with the current state. -func UpdateStateHook(ctx EvalContext) error { - // In principle we could grab the lock here just long enough to take a - // deep copy and then pass that to our hooks below, but we'll instead - // hold the hook for the duration to avoid the potential confusing - // situation of us racing to call PostStateUpdate concurrently with - // different state snapshots. - stateSync := ctx.State() - state := stateSync.Lock().DeepCopy() - defer stateSync.Unlock() - - // Call the hook - err := ctx.Hook(func(h Hook) (HookAction, error) { - return h.PostStateUpdate(state) - }) - return err -} - -// evalWriteEmptyState wraps EvalWriteState to specifically record an empty -// state for a particular object. -type evalWriteEmptyState struct { - EvalWriteState -} - -func (n *evalWriteEmptyState) Eval(ctx EvalContext) (interface{}, error) { - var state *states.ResourceInstanceObject - n.State = &state - return n.EvalWriteState.Eval(ctx) -} - -// EvalWriteState is an EvalNode implementation that saves the given object -// as the current object for the selected resource instance. -type EvalWriteState struct { - // Addr is the address of the instance to read state for. - Addr addrs.ResourceInstance - - // State is the object state to save. - State **states.ResourceInstanceObject - - // ProviderSchema is the schema for the provider given in ProviderAddr. - ProviderSchema **ProviderSchema - - // ProviderAddr is the address of the provider configuration that - // produced the given object. - ProviderAddr addrs.AbsProviderConfig - - // Dependencies are the inter-resource dependencies to be stored in the - // state. - Dependencies *[]addrs.ConfigResource - - // targetState determines which context state we're writing to during plan. - // The default is the global working state. - targetState phaseState -} - -func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) { - if n.State == nil { - // Note that a pointer _to_ nil is valid here, indicating the total - // absense of an object as we'd see during destroy. - panic("EvalWriteState used with no ResourceInstanceObject") - } - - absAddr := n.Addr.Absolute(ctx.Path()) - - var state *states.SyncState - switch n.targetState { - case refreshState: - log.Printf("[TRACE] EvalWriteState: using RefreshState for %s", absAddr) - state = ctx.RefreshState() - default: - state = ctx.State() - } - - if n.ProviderAddr.Provider.Type == "" { - return nil, fmt.Errorf("failed to write state for %s: missing provider type", absAddr) - } - obj := *n.State - if obj == nil || obj.Value.IsNull() { - // No need to encode anything: we'll just write it directly. - state.SetResourceInstanceCurrent(absAddr, nil, n.ProviderAddr) - log.Printf("[TRACE] EvalWriteState: removing state object for %s", absAddr) - return nil, nil - } - - // store the new deps in the state - if n.Dependencies != nil { - log.Printf("[TRACE] EvalWriteState: recording %d dependencies for %s", len(*n.Dependencies), absAddr) - obj.Dependencies = *n.Dependencies - } - - if n.ProviderSchema == nil || *n.ProviderSchema == nil { - // Should never happen, unless our state object is nil - panic("EvalWriteState used with pointer to nil ProviderSchema object") - } - - if obj != nil { - log.Printf("[TRACE] EvalWriteState: writing current state object for %s", absAddr) - } else { - log.Printf("[TRACE] EvalWriteState: removing current state object for %s", absAddr) - } - - schema, currentVersion := (*n.ProviderSchema).SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // It shouldn't be possible to get this far in any real scenario - // without a schema, but we might end up here in contrived tests that - // fail to set up their world properly. - return nil, fmt.Errorf("failed to encode %s in state: no resource type schema available", absAddr) - } - src, err := obj.Encode(schema.ImpliedType(), currentVersion) - if err != nil { - return nil, fmt.Errorf("failed to encode %s in state: %s", absAddr, err) - } - - state.SetResourceInstanceCurrent(absAddr, src, n.ProviderAddr) - return nil, nil -} - -// EvalWriteStateDeposed is an EvalNode implementation that writes -// an InstanceState out to the Deposed list of a resource in the state. -type EvalWriteStateDeposed struct { - // Addr is the address of the instance to read state for. - Addr addrs.ResourceInstance - - // Key indicates which deposed object to write to. - Key states.DeposedKey - - // State is the object state to save. - State **states.ResourceInstanceObject - - // ProviderSchema is the schema for the provider given in ProviderAddr. - ProviderSchema **ProviderSchema - - // ProviderAddr is the address of the provider configuration that - // produced the given object. - ProviderAddr addrs.AbsProviderConfig -} - -func (n *EvalWriteStateDeposed) Eval(ctx EvalContext) (interface{}, error) { - if n.State == nil { - // Note that a pointer _to_ nil is valid here, indicating the total - // absense of an object as we'd see during destroy. - panic("EvalWriteStateDeposed used with no ResourceInstanceObject") - } - - absAddr := n.Addr.Absolute(ctx.Path()) - key := n.Key - state := ctx.State() - - if key == states.NotDeposed { - // should never happen - return nil, fmt.Errorf("can't save deposed object for %s without a deposed key; this is a bug in Terraform that should be reported", absAddr) - } - - obj := *n.State - if obj == nil { - // No need to encode anything: we'll just write it directly. - state.SetResourceInstanceDeposed(absAddr, key, nil, n.ProviderAddr) - log.Printf("[TRACE] EvalWriteStateDeposed: removing state object for %s deposed %s", absAddr, key) - return nil, nil - } - if n.ProviderSchema == nil || *n.ProviderSchema == nil { - // Should never happen, unless our state object is nil - panic("EvalWriteStateDeposed used with no ProviderSchema object") - } - - schema, currentVersion := (*n.ProviderSchema).SchemaForResourceAddr(n.Addr.ContainingResource()) - if schema == nil { - // It shouldn't be possible to get this far in any real scenario - // without a schema, but we might end up here in contrived tests that - // fail to set up their world properly. - return nil, fmt.Errorf("failed to encode %s in state: no resource type schema available", absAddr) - } - src, err := obj.Encode(schema.ImpliedType(), currentVersion) - if err != nil { - return nil, fmt.Errorf("failed to encode %s in state: %s", absAddr, err) - } - - log.Printf("[TRACE] EvalWriteStateDeposed: writing state object for %s deposed %s", absAddr, key) - state.SetResourceInstanceDeposed(absAddr, key, src, n.ProviderAddr) - return nil, nil -} - -// EvalDeposeState is an EvalNode implementation that moves the current object -// for the given instance to instead be a deposed object, leaving the instance -// with no current object. -// This is used at the beginning of a create-before-destroy replace action so -// that the create can create while preserving the old state of the -// to-be-destroyed object. -type EvalDeposeState struct { - Addr addrs.ResourceInstance - - // ForceKey, if a value other than states.NotDeposed, will be used as the - // key for the newly-created deposed object that results from this action. - // If set to states.NotDeposed (the zero value), a new unique key will be - // allocated. - ForceKey states.DeposedKey - - // OutputKey, if non-nil, will be written with the deposed object key that - // was generated for the object. This can then be passed to - // EvalUndeposeState.Key so it knows which deposed instance to forget. - OutputKey *states.DeposedKey -} - -// TODO: test -func (n *EvalDeposeState) Eval(ctx EvalContext) (interface{}, error) { - absAddr := n.Addr.Absolute(ctx.Path()) - state := ctx.State() - - var key states.DeposedKey - if n.ForceKey == states.NotDeposed { - key = state.DeposeResourceInstanceObject(absAddr) - } else { - key = n.ForceKey - state.DeposeResourceInstanceObjectForceKey(absAddr, key) - } - log.Printf("[TRACE] EvalDeposeState: prior object for %s now deposed with key %s", absAddr, key) - - if n.OutputKey != nil { - *n.OutputKey = key - } - - return nil, nil -} - -// EvalMaybeRestoreDeposedObject is an EvalNode implementation that will -// restore a particular deposed object of the specified resource instance -// to be the "current" object if and only if the instance doesn't currently -// have a current object. -// -// This is intended for use when the create leg of a create before destroy -// fails with no partial new object: if we didn't take any action, the user -// would be left in the unfortunate situation of having no current object -// and the previously-workign object now deposed. This EvalNode causes a -// better outcome by restoring things to how they were before the replace -// operation began. -// -// The create operation may have produced a partial result even though it -// failed and it's important that we don't "forget" that state, so in that -// situation the prior object remains deposed and the partial new object -// remains the current object, allowing the situation to hopefully be -// improved in a subsequent run. -type EvalMaybeRestoreDeposedObject struct { - Addr addrs.ResourceInstance - - // PlannedChange might be the action we're performing that includes - // the possiblity of restoring a deposed object. However, it might also - // be nil. It's here only for use in error messages and must not be - // used for business logic. - PlannedChange **plans.ResourceInstanceChange - - // Key is a pointer to the deposed object key that should be forgotten - // from the state, which must be non-nil. - Key *states.DeposedKey -} - -// TODO: test -func (n *EvalMaybeRestoreDeposedObject) Eval(ctx EvalContext) (interface{}, error) { - absAddr := n.Addr.Absolute(ctx.Path()) - dk := *n.Key - state := ctx.State() - - if dk == states.NotDeposed { - // This should never happen, and so it always indicates a bug. - // We should evaluate this node only if we've previously deposed - // an object as part of the same operation. - var diags tfdiags.Diagnostics - if n.PlannedChange != nil && *n.PlannedChange != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Attempt to restore non-existent deposed object", - fmt.Sprintf( - "Terraform has encountered a bug where it would need to restore a deposed object for %s without knowing a deposed object key for that object. This occurred during a %s action. This is a bug in Terraform; please report it!", - absAddr, (*n.PlannedChange).Action, - ), - )) - } else { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Attempt to restore non-existent deposed object", - fmt.Sprintf( - "Terraform has encountered a bug where it would need to restore a deposed object for %s without knowing a deposed object key for that object. This is a bug in Terraform; please report it!", - absAddr, - ), - )) - } - return nil, diags.Err() - } - - restored := state.MaybeRestoreResourceInstanceDeposed(absAddr, dk) - if restored { - log.Printf("[TRACE] EvalMaybeRestoreDeposedObject: %s deposed object %s was restored as the current object", absAddr, dk) - } else { - log.Printf("[TRACE] EvalMaybeRestoreDeposedObject: %s deposed object %s remains deposed", absAddr, dk) - } - - return nil, nil -} - -// EvalRefreshLifecycle is an EvalNode implementation that updates -// the status of the lifecycle options stored in the state. -// This currently only applies to create_before_destroy. -type EvalRefreshLifecycle struct { - Addr addrs.AbsResourceInstance - - Config *configs.Resource - // Prior State - State **states.ResourceInstanceObject - // ForceCreateBeforeDestroy indicates a create_before_destroy resource - // depends on this resource. - ForceCreateBeforeDestroy bool -} - -func (n *EvalRefreshLifecycle) Eval(ctx EvalContext) (interface{}, error) { - state := *n.State - if state == nil { - // no existing state - return nil, nil - } - - // In 0.13 we could be refreshing a resource with no config. - // We should be operating on managed resource, but check here to be certain - if n.Config == nil || n.Config.Managed == nil { - log.Printf("[WARN] EvalRefreshLifecycle: no Managed config value found in instance state for %q", n.Addr) - return nil, nil - } - - state.CreateBeforeDestroy = n.Config.Managed.CreateBeforeDestroy || n.ForceCreateBeforeDestroy - - return nil, nil -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_state_test.go b/vendor/github.com/hashicorp/terraform/terraform/eval_state_test.go deleted file mode 100644 index a9c73cde..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_state_test.go +++ /dev/null @@ -1,277 +0,0 @@ -package terraform - -import ( - "testing" - - "github.com/davecgh/go-spew/spew" - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/providers" - "github.com/hashicorp/terraform/states" -) - -func TestEvalReadState(t *testing.T) { - var output *states.ResourceInstanceObject - mockProvider := mockProviderWithResourceTypeSchema("aws_instance", &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": { - Type: cty.String, - Optional: true, - }, - }, - }) - providerSchema := mockProvider.GetSchemaReturn - provider := providers.Interface(mockProvider) - - cases := map[string]struct { - Resources map[string]*ResourceState - Node *EvalReadState - ExpectedInstanceId string - }{ - "ReadState gets primary instance state": { - Resources: map[string]*ResourceState{ - "aws_instance.bar": &ResourceState{ - Primary: &InstanceState{ - ID: "i-abc123", - }, - }, - }, - Node: &EvalReadState{ - Addr: addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "aws_instance", - Name: "bar", - }.Instance(addrs.NoKey), - Provider: &provider, - ProviderSchema: &providerSchema, - - Output: &output, - }, - ExpectedInstanceId: "i-abc123", - }, - } - - for k, c := range cases { - t.Run(k, func(t *testing.T) { - ctx := new(MockEvalContext) - state := MustShimLegacyState(&State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: c.Resources, - }, - }, - }) - ctx.StateState = state.SyncWrapper() - ctx.PathPath = addrs.RootModuleInstance - - result, err := c.Node.Eval(ctx) - if err != nil { - t.Fatalf("[%s] Got err: %#v", k, err) - } - - expected := c.ExpectedInstanceId - if !(result != nil && instanceObjectIdForTests(result.(*states.ResourceInstanceObject)) == expected) { - t.Fatalf("[%s] Expected return with ID %#v, got: %#v", k, expected, result) - } - - if !(output != nil && output.Value.GetAttr("id") == cty.StringVal(expected)) { - t.Fatalf("[%s] Expected output with ID %#v, got: %#v", k, expected, output) - } - - output = nil - }) - } -} - -func TestEvalReadStateDeposed(t *testing.T) { - var output *states.ResourceInstanceObject - mockProvider := mockProviderWithResourceTypeSchema("aws_instance", &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": { - Type: cty.String, - Optional: true, - }, - }, - }) - providerSchema := mockProvider.GetSchemaReturn - provider := providers.Interface(mockProvider) - - cases := map[string]struct { - Resources map[string]*ResourceState - Node *EvalReadStateDeposed - ExpectedInstanceId string - }{ - "ReadStateDeposed gets deposed instance": { - Resources: map[string]*ResourceState{ - "aws_instance.bar": &ResourceState{ - Deposed: []*InstanceState{ - &InstanceState{ID: "i-abc123"}, - }, - }, - }, - Node: &EvalReadStateDeposed{ - Addr: addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "aws_instance", - Name: "bar", - }.Instance(addrs.NoKey), - Key: states.DeposedKey("00000001"), // shim from legacy state assigns 0th deposed index this key - Provider: &provider, - ProviderSchema: &providerSchema, - - Output: &output, - }, - ExpectedInstanceId: "i-abc123", - }, - } - for k, c := range cases { - t.Run(k, func(t *testing.T) { - ctx := new(MockEvalContext) - state := MustShimLegacyState(&State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: rootModulePath, - Resources: c.Resources, - }, - }, - }) - ctx.StateState = state.SyncWrapper() - ctx.PathPath = addrs.RootModuleInstance - - result, err := c.Node.Eval(ctx) - if err != nil { - t.Fatalf("[%s] Got err: %#v", k, err) - } - - expected := c.ExpectedInstanceId - if !(result != nil && instanceObjectIdForTests(result.(*states.ResourceInstanceObject)) == expected) { - t.Fatalf("[%s] Expected return with ID %#v, got: %#v", k, expected, result) - } - - if !(output != nil && output.Value.GetAttr("id") == cty.StringVal(expected)) { - t.Fatalf("[%s] Expected output with ID %#v, got: %#v", k, expected, output) - } - - output = nil - }) - } -} - -func TestEvalWriteState(t *testing.T) { - state := states.NewState() - ctx := new(MockEvalContext) - ctx.StateState = state.SyncWrapper() - ctx.PathPath = addrs.RootModuleInstance - - mockProvider := mockProviderWithResourceTypeSchema("aws_instance", &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": { - Type: cty.String, - Optional: true, - }, - }, - }) - providerSchema := mockProvider.GetSchemaReturn - - obj := &states.ResourceInstanceObject{ - Value: cty.ObjectVal(map[string]cty.Value{ - "id": cty.StringVal("i-abc123"), - }), - Status: states.ObjectReady, - } - node := &EvalWriteState{ - Addr: addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "aws_instance", - Name: "foo", - }.Instance(addrs.NoKey), - - State: &obj, - - ProviderSchema: &providerSchema, - ProviderAddr: addrs.RootModuleInstance.ProviderConfigDefault(addrs.NewDefaultProvider("aws")), - } - _, err := node.Eval(ctx) - if err != nil { - t.Fatalf("Got err: %#v", err) - } - - checkStateString(t, state, ` -aws_instance.foo: - ID = i-abc123 - provider = provider["registry.terraform.io/hashicorp/aws"] - `) -} - -func TestEvalWriteStateDeposed(t *testing.T) { - state := states.NewState() - ctx := new(MockEvalContext) - ctx.StateState = state.SyncWrapper() - ctx.PathPath = addrs.RootModuleInstance - - mockProvider := mockProviderWithResourceTypeSchema("aws_instance", &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "id": { - Type: cty.String, - Optional: true, - }, - }, - }) - providerSchema := mockProvider.GetSchemaReturn - - obj := &states.ResourceInstanceObject{ - Value: cty.ObjectVal(map[string]cty.Value{ - "id": cty.StringVal("i-abc123"), - }), - Status: states.ObjectReady, - } - node := &EvalWriteStateDeposed{ - Addr: addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "aws_instance", - Name: "foo", - }.Instance(addrs.NoKey), - Key: states.DeposedKey("deadbeef"), - - State: &obj, - - ProviderSchema: &providerSchema, - ProviderAddr: addrs.RootModuleInstance.ProviderConfigDefault(addrs.NewDefaultProvider("aws")), - } - _, err := node.Eval(ctx) - if err != nil { - t.Fatalf("Got err: %#v", err) - } - - checkStateString(t, state, ` -aws_instance.foo: (1 deposed) - ID = - provider = provider["registry.terraform.io/hashicorp/aws"] - Deposed ID 1 = i-abc123 - `) -} - -func TestUpdateStateHook(t *testing.T) { - mockHook := new(MockHook) - - state := states.NewState() - state.Module(addrs.RootModuleInstance).SetLocalValue("foo", cty.StringVal("hello")) - - ctx := new(MockEvalContext) - ctx.HookHook = mockHook - ctx.StateState = state.SyncWrapper() - - if err := UpdateStateHook(ctx); err != nil { - t.Fatalf("err: %s", err) - } - - if !mockHook.PostStateUpdateCalled { - t.Fatal("should call PostStateUpdate") - } - if mockHook.PostStateUpdateState.LocalValue(addrs.LocalValue{Name: "foo"}.Absolute(addrs.RootModuleInstance)) != cty.StringVal("hello") { - t.Fatalf("wrong state passed to hook: %s", spew.Sdump(mockHook.PostStateUpdateState)) - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_state_upgrade.go b/vendor/github.com/hashicorp/terraform/terraform/eval_state_upgrade.go deleted file mode 100644 index e03349c3..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_state_upgrade.go +++ /dev/null @@ -1,203 +0,0 @@ -package terraform - -import ( - "encoding/json" - "fmt" - "log" - - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/providers" - "github.com/hashicorp/terraform/states" - "github.com/hashicorp/terraform/tfdiags" - "github.com/zclconf/go-cty/cty" -) - -// UpgradeResourceState will, if necessary, run the provider-defined upgrade -// logic against the given state object to make it compliant with the -// current schema version. This is a no-op if the given state object is -// already at the latest version. -// -// If any errors occur during upgrade, error diagnostics are returned. In that -// case it is not safe to proceed with using the original state object. -func UpgradeResourceState(addr addrs.AbsResourceInstance, provider providers.Interface, src *states.ResourceInstanceObjectSrc, currentSchema *configschema.Block, currentVersion uint64) (*states.ResourceInstanceObjectSrc, tfdiags.Diagnostics) { - // Remove any attributes from state that are not present in the schema. - // This was previously taken care of by the provider, but data sources do - // not go through the UpgradeResourceState process. - // - // Legacy flatmap state is already taken care of during conversion. - // If the schema version is be changed, then allow the provider to handle - // removed attributes. - if len(src.AttrsJSON) > 0 && src.SchemaVersion == currentVersion { - src.AttrsJSON = stripRemovedStateAttributes(src.AttrsJSON, currentSchema.ImpliedType()) - } - - if addr.Resource.Resource.Mode != addrs.ManagedResourceMode { - // We only do state upgrading for managed resources. - return src, nil - } - - stateIsFlatmap := len(src.AttrsJSON) == 0 - - // TODO: This should eventually use a proper FQN. - providerType := addr.Resource.Resource.ImpliedProvider() - if src.SchemaVersion > currentVersion { - log.Printf("[TRACE] UpgradeResourceState: can't downgrade state for %s from version %d to %d", addr, src.SchemaVersion, currentVersion) - var diags tfdiags.Diagnostics - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Resource instance managed by newer provider version", - // This is not a very good error message, but we don't retain enough - // information in state to give good feedback on what provider - // version might be required here. :( - fmt.Sprintf("The current state of %s was created by a newer provider version than is currently selected. Upgrade the %s provider to work with this state.", addr, providerType), - )) - return nil, diags - } - - // If we get down here then we need to upgrade the state, with the - // provider's help. - // If this state was originally created by a version of Terraform prior to - // v0.12, this also includes translating from legacy flatmap to new-style - // representation, since only the provider has enough information to - // understand a flatmap built against an older schema. - if src.SchemaVersion != currentVersion { - log.Printf("[TRACE] UpgradeResourceState: upgrading state for %s from version %d to %d using provider %q", addr, src.SchemaVersion, currentVersion, providerType) - } else { - log.Printf("[TRACE] UpgradeResourceState: schema version of %s is still %d; calling provider %q for any other minor fixups", addr, currentVersion, providerType) - } - - req := providers.UpgradeResourceStateRequest{ - TypeName: addr.Resource.Resource.Type, - - // TODO: The internal schema version representations are all using - // uint64 instead of int64, but unsigned integers aren't friendly - // to all protobuf target languages so in practice we use int64 - // on the wire. In future we will change all of our internal - // representations to int64 too. - Version: int64(src.SchemaVersion), - } - - if stateIsFlatmap { - req.RawStateFlatmap = src.AttrsFlat - } else { - req.RawStateJSON = src.AttrsJSON - } - - resp := provider.UpgradeResourceState(req) - diags := resp.Diagnostics - if diags.HasErrors() { - return nil, diags - } - - // After upgrading, the new value must conform to the current schema. When - // going over RPC this is actually already ensured by the - // marshaling/unmarshaling of the new value, but we'll check it here - // anyway for robustness, e.g. for in-process providers. - newValue := resp.UpgradedState - if errs := newValue.Type().TestConformance(currentSchema.ImpliedType()); len(errs) > 0 { - for _, err := range errs { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid resource state upgrade", - fmt.Sprintf("The %s provider upgraded the state for %s from a previous version, but produced an invalid result: %s.", providerType, addr, tfdiags.FormatError(err)), - )) - } - return nil, diags - } - - new, err := src.CompleteUpgrade(newValue, currentSchema.ImpliedType(), uint64(currentVersion)) - if err != nil { - // We already checked for type conformance above, so getting into this - // codepath should be rare and is probably a bug somewhere under CompleteUpgrade. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Failed to encode result of resource state upgrade", - fmt.Sprintf("Failed to encode state for %s after resource schema upgrade: %s.", addr, tfdiags.FormatError(err)), - )) - } - return new, diags -} - -// stripRemovedStateAttributes deletes any attributes no longer present in the -// schema, so that the json can be correctly decoded. -func stripRemovedStateAttributes(state []byte, ty cty.Type) []byte { - jsonMap := map[string]interface{}{} - err := json.Unmarshal(state, &jsonMap) - if err != nil { - // we just log any errors here, and let the normal decode process catch - // invalid JSON. - log.Printf("[ERROR] UpgradeResourceState: %s", err) - return state - } - - // if no changes were made, we return the original state to ensure nothing - // was altered in the marshaling process. - if !removeRemovedAttrs(jsonMap, ty) { - return state - } - - js, err := json.Marshal(jsonMap) - if err != nil { - // if the json map was somehow mangled enough to not marhsal, something - // went horribly wrong - panic(err) - } - - return js -} - -// strip out the actual missing attributes, and return a bool indicating if any -// changes were made. -func removeRemovedAttrs(v interface{}, ty cty.Type) bool { - modified := false - // we're only concerned with finding maps that correspond to object - // attributes - switch v := v.(type) { - case []interface{}: - switch { - // If these aren't blocks the next call will be a noop - case ty.IsListType() || ty.IsSetType(): - eTy := ty.ElementType() - for _, eV := range v { - modified = removeRemovedAttrs(eV, eTy) || modified - } - } - return modified - case map[string]interface{}: - switch { - case ty.IsMapType(): - // map blocks aren't yet supported, but handle this just in case - eTy := ty.ElementType() - for _, eV := range v { - modified = removeRemovedAttrs(eV, eTy) || modified - } - return modified - - case ty == cty.DynamicPseudoType: - log.Printf("[DEBUG] UpgradeResourceState: ignoring dynamic block: %#v\n", v) - return false - - case ty.IsObjectType(): - attrTypes := ty.AttributeTypes() - for attr, attrV := range v { - attrTy, ok := attrTypes[attr] - if !ok { - log.Printf("[DEBUG] UpgradeResourceState: attribute %q no longer present in schema", attr) - delete(v, attr) - modified = true - continue - } - - modified = removeRemovedAttrs(attrV, attrTy) || modified - } - return modified - default: - // This shouldn't happen, and will fail to decode further on, so - // there's no need to handle it here. - log.Printf("[WARN] UpgradeResourceState: unexpected type %#v for map in json state", ty) - return false - } - } - return modified -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_validate.go b/vendor/github.com/hashicorp/terraform/terraform/eval_validate.go deleted file mode 100644 index c9f47b1b..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_validate.go +++ /dev/null @@ -1,609 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/configs" - "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/providers" - "github.com/hashicorp/terraform/provisioners" - "github.com/hashicorp/terraform/tfdiags" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" - "github.com/zclconf/go-cty/cty/gocty" -) - -// EvalValidateCount is an EvalNode implementation that validates -// the count of a resource. -type EvalValidateCount struct { - Resource *configs.Resource -} - -// TODO: test -func (n *EvalValidateCount) Eval(ctx EvalContext) (interface{}, error) { - var diags tfdiags.Diagnostics - var count int - var err error - - val, valDiags := ctx.EvaluateExpr(n.Resource.Count, cty.Number, nil) - diags = diags.Append(valDiags) - if valDiags.HasErrors() { - goto RETURN - } - if val.IsNull() || !val.IsKnown() { - goto RETURN - } - - err = gocty.FromCtyValue(val, &count) - if err != nil { - // The EvaluateExpr call above already guaranteed us a number value, - // so if we end up here then we have something that is out of range - // for an int, and the error message will include a description of - // the valid range. - rawVal := val.AsBigFloat() - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count value", - Detail: fmt.Sprintf("The number %s is not a valid count value: %s.", rawVal, err), - Subject: n.Resource.Count.Range().Ptr(), - }) - } else if count < 0 { - rawVal := val.AsBigFloat() - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count value", - Detail: fmt.Sprintf("The number %s is not a valid count value: count must not be negative.", rawVal), - Subject: n.Resource.Count.Range().Ptr(), - }) - } - -RETURN: - return nil, diags.NonFatalErr() -} - -// EvalValidateProvisioner validates the configuration of a provisioner -// belonging to a resource. The provisioner config is expected to contain the -// merged connection configurations. -type EvalValidateProvisioner struct { - ResourceAddr addrs.Resource - Provisioner *provisioners.Interface - Schema **configschema.Block - Config *configs.Provisioner - ResourceHasCount bool - ResourceHasForEach bool -} - -func (n *EvalValidateProvisioner) Validate(ctx EvalContext) error { - provisioner := *n.Provisioner - config := *n.Config - schema := *n.Schema - - var diags tfdiags.Diagnostics - - // Validate the provisioner's own config first - configVal, _, configDiags := n.evaluateBlock(ctx, config.Config, schema) - diags = diags.Append(configDiags) - if configDiags.HasErrors() { - return diags.Err() - } - - if configVal == cty.NilVal { - // Should never happen for a well-behaved EvaluateBlock implementation - return fmt.Errorf("EvaluateBlock returned nil value") - } - - req := provisioners.ValidateProvisionerConfigRequest{ - Config: configVal, - } - - resp := provisioner.ValidateProvisionerConfig(req) - diags = diags.Append(resp.Diagnostics) - - // Now validate the connection config, which contains the merged bodies - // of the resource and provisioner connection blocks. - connDiags := n.validateConnConfig(ctx, config.Connection, n.ResourceAddr) - diags = diags.Append(connDiags) - - return diags.NonFatalErr() -} - -func (n *EvalValidateProvisioner) validateConnConfig(ctx EvalContext, config *configs.Connection, self addrs.Referenceable) tfdiags.Diagnostics { - // We can't comprehensively validate the connection config since its - // final structure is decided by the communicator and we can't instantiate - // that until we have a complete instance state. However, we *can* catch - // configuration keys that are not valid for *any* communicator, catching - // typos early rather than waiting until we actually try to run one of - // the resource's provisioners. - - var diags tfdiags.Diagnostics - - if config == nil || config.Config == nil { - // No block to validate - return diags - } - - // We evaluate here just by evaluating the block and returning any - // diagnostics we get, since evaluation alone is enough to check for - // extraneous arguments and incorrectly-typed arguments. - _, _, configDiags := n.evaluateBlock(ctx, config.Config, connectionBlockSupersetSchema) - diags = diags.Append(configDiags) - - return diags -} - -func (n *EvalValidateProvisioner) evaluateBlock(ctx EvalContext, body hcl.Body, schema *configschema.Block) (cty.Value, hcl.Body, tfdiags.Diagnostics) { - keyData := EvalDataForNoInstanceKey - selfAddr := n.ResourceAddr.Instance(addrs.NoKey) - - if n.ResourceHasCount { - // For a resource that has count, we allow count.index but don't - // know at this stage what it will return. - keyData = InstanceKeyEvalData{ - CountIndex: cty.UnknownVal(cty.Number), - } - - // "self" can't point to an unknown key, but we'll force it to be - // key 0 here, which should return an unknown value of the - // expected type since none of these elements are known at this - // point anyway. - selfAddr = n.ResourceAddr.Instance(addrs.IntKey(0)) - } else if n.ResourceHasForEach { - // For a resource that has for_each, we allow each.value and each.key - // but don't know at this stage what it will return. - keyData = InstanceKeyEvalData{ - EachKey: cty.UnknownVal(cty.String), - EachValue: cty.DynamicVal, - } - - // "self" can't point to an unknown key, but we'll force it to be - // key "" here, which should return an unknown value of the - // expected type since none of these elements are known at - // this point anyway. - selfAddr = n.ResourceAddr.Instance(addrs.StringKey("")) - } - - return ctx.EvaluateBlock(body, schema, selfAddr, keyData) -} - -// connectionBlockSupersetSchema is a schema representing the superset of all -// possible arguments for "connection" blocks across all supported connection -// types. -// -// This currently lives here because we've not yet updated our communicator -// subsystem to be aware of schema itself. Once that is done, we can remove -// this and use a type-specific schema from the communicator to validate -// exactly what is expected for a given connection type. -var connectionBlockSupersetSchema = &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - // NOTE: "type" is not included here because it's treated special - // by the config loader and stored away in a separate field. - - // Common attributes for both connection types - "host": { - Type: cty.String, - Required: true, - }, - "type": { - Type: cty.String, - Optional: true, - }, - "user": { - Type: cty.String, - Optional: true, - }, - "password": { - Type: cty.String, - Optional: true, - }, - "port": { - Type: cty.String, - Optional: true, - }, - "timeout": { - Type: cty.String, - Optional: true, - }, - "script_path": { - Type: cty.String, - Optional: true, - }, - - // For type=ssh only (enforced in ssh communicator) - "private_key": { - Type: cty.String, - Optional: true, - }, - "certificate": { - Type: cty.String, - Optional: true, - }, - "host_key": { - Type: cty.String, - Optional: true, - }, - "agent": { - Type: cty.Bool, - Optional: true, - }, - "agent_identity": { - Type: cty.String, - Optional: true, - }, - "bastion_host": { - Type: cty.String, - Optional: true, - }, - "bastion_host_key": { - Type: cty.String, - Optional: true, - }, - "bastion_port": { - Type: cty.Number, - Optional: true, - }, - "bastion_user": { - Type: cty.String, - Optional: true, - }, - "bastion_password": { - Type: cty.String, - Optional: true, - }, - "bastion_private_key": { - Type: cty.String, - Optional: true, - }, - "bastion_certificate": { - Type: cty.String, - Optional: true, - }, - - // For type=winrm only (enforced in winrm communicator) - "https": { - Type: cty.Bool, - Optional: true, - }, - "insecure": { - Type: cty.Bool, - Optional: true, - }, - "cacert": { - Type: cty.String, - Optional: true, - }, - "use_ntlm": { - Type: cty.Bool, - Optional: true, - }, - }, -} - -// connectionBlockSupersetSchema is a schema representing the superset of all -// possible arguments for "connection" blocks across all supported connection -// types. -// -// This currently lives here because we've not yet updated our communicator -// subsystem to be aware of schema itself. It's exported only for use in the -// configs/configupgrade package and should not be used from anywhere else. -// The caller may not modify any part of the returned schema data structure. -func ConnectionBlockSupersetSchema() *configschema.Block { - return connectionBlockSupersetSchema -} - -// EvalValidateResource validates the configuration of a resource. -type EvalValidateResource struct { - Addr addrs.Resource - Provider *providers.Interface - ProviderSchema **ProviderSchema - Config *configs.Resource - ProviderMetas map[addrs.Provider]*configs.ProviderMeta - - // IgnoreWarnings means that warnings will not be passed through. This allows - // "just-in-time" passes of validation to continue execution through warnings. - IgnoreWarnings bool - - // ConfigVal, if non-nil, will be updated with the value resulting from - // evaluating the given configuration body. Since validation is performed - // very early, this value is likely to contain lots of unknown values, - // but its type will conform to the schema of the resource type associated - // with the resource instance being validated. - ConfigVal *cty.Value -} - -func (n *EvalValidateResource) Validate(ctx EvalContext) error { - if n.ProviderSchema == nil || *n.ProviderSchema == nil { - return fmt.Errorf("EvalValidateResource has nil schema for %s", n.Addr) - } - - var diags tfdiags.Diagnostics - provider := *n.Provider - cfg := *n.Config - schema := *n.ProviderSchema - mode := cfg.Mode - - keyData := EvalDataForNoInstanceKey - - switch { - case n.Config.Count != nil: - // If the config block has count, we'll evaluate with an unknown - // number as count.index so we can still type check even though - // we won't expand count until the plan phase. - keyData = InstanceKeyEvalData{ - CountIndex: cty.UnknownVal(cty.Number), - } - - // Basic type-checking of the count argument. More complete validation - // of this will happen when we DynamicExpand during the plan walk. - countDiags := n.validateCount(ctx, n.Config.Count) - diags = diags.Append(countDiags) - - case n.Config.ForEach != nil: - keyData = InstanceKeyEvalData{ - EachKey: cty.UnknownVal(cty.String), - EachValue: cty.UnknownVal(cty.DynamicPseudoType), - } - - // Evaluate the for_each expression here so we can expose the diagnostics - forEachDiags := n.validateForEach(ctx, n.Config.ForEach) - diags = diags.Append(forEachDiags) - } - - diags = diags.Append(validateDependsOn(ctx, n.Config.DependsOn)) - - // Validate the provider_meta block for the provider this resource - // belongs to, if there is one. - // - // Note: this will return an error for every resource a provider - // uses in a module, if the provider_meta for that module is - // incorrect. The only way to solve this that we've foudn is to - // insert a new ProviderMeta graph node in the graph, and make all - // that provider's resources in the module depend on the node. That's - // an awful heavy hammer to swing for this feature, which should be - // used only in limited cases with heavy coordination with the - // Terraform team, so we're going to defer that solution for a future - // enhancement to this functionality. - /* - if n.ProviderMetas != nil { - if m, ok := n.ProviderMetas[n.ProviderAddr.ProviderConfig.Type]; ok && m != nil { - // if the provider doesn't support this feature, throw an error - if (*n.ProviderSchema).ProviderMeta == nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: fmt.Sprintf("Provider %s doesn't support provider_meta", cfg.ProviderConfigAddr()), - Detail: fmt.Sprintf("The resource %s belongs to a provider that doesn't support provider_meta blocks", n.Addr), - Subject: &m.ProviderRange, - }) - } else { - _, _, metaDiags := ctx.EvaluateBlock(m.Config, (*n.ProviderSchema).ProviderMeta, nil, EvalDataForNoInstanceKey) - diags = diags.Append(metaDiags) - } - } - } - */ - // BUG(paddy): we're not validating provider_meta blocks on EvalValidate right now - // because the ProviderAddr for the resource isn't available on the EvalValidate - // struct. - - // Provider entry point varies depending on resource mode, because - // managed resources and data resources are two distinct concepts - // in the provider abstraction. - switch mode { - case addrs.ManagedResourceMode: - schema, _ := schema.SchemaForResourceType(mode, cfg.Type) - if schema == nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid resource type", - Detail: fmt.Sprintf("The provider %s does not support resource type %q.", cfg.ProviderConfigAddr(), cfg.Type), - Subject: &cfg.TypeRange, - }) - return diags.Err() - } - - configVal, _, valDiags := ctx.EvaluateBlock(cfg.Config, schema, nil, keyData) - diags = diags.Append(valDiags) - if valDiags.HasErrors() { - return diags.Err() - } - - if cfg.Managed != nil { // can be nil only in tests with poorly-configured mocks - for _, traversal := range cfg.Managed.IgnoreChanges { - // This will error out if the traversal contains an invalid - // index step. That is OK if we want users to be able to ignore - // a key that is no longer specified in the config. - moreDiags := schema.StaticValidateTraversal(traversal) - diags = diags.Append(moreDiags) - if diags.HasErrors() { - continue - } - - // first check to see if this assigned in the config - v, _ := traversal.TraverseRel(configVal) - if !v.IsNull() { - // it's assigned, so we can also assume it's not computed-only - continue - } - - // We can't ignore changes that don't exist in the configuration. - // We're not checking specifically if the traversal resolves to - // a computed-only value, but we can hint to the user that it - // might also be the case. - sourceRange := traversal.SourceRange() - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Cannot ignore argument not set in the configuration", - Detail: fmt.Sprintf("The ignore_changes argument is not set in the configuration.\n" + - "The ignore_changes mechanism only applies to changes " + - "within the configuration, and must be used with " + - "arguments set in the configuration and not computed by " + - "the provider.", - ), - Subject: &sourceRange, - }) - return diags.Err() - } - } - - req := providers.ValidateResourceTypeConfigRequest{ - TypeName: cfg.Type, - Config: configVal, - } - - resp := provider.ValidateResourceTypeConfig(req) - diags = diags.Append(resp.Diagnostics.InConfigBody(cfg.Config)) - - if n.ConfigVal != nil { - *n.ConfigVal = configVal - } - - case addrs.DataResourceMode: - schema, _ := schema.SchemaForResourceType(mode, cfg.Type) - if schema == nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid data source", - Detail: fmt.Sprintf("The provider %s does not support data source %q.", cfg.ProviderConfigAddr(), cfg.Type), - Subject: &cfg.TypeRange, - }) - return diags.Err() - } - - configVal, _, valDiags := ctx.EvaluateBlock(cfg.Config, schema, nil, keyData) - diags = diags.Append(valDiags) - if valDiags.HasErrors() { - return diags.Err() - } - - req := providers.ValidateDataSourceConfigRequest{ - TypeName: cfg.Type, - Config: configVal, - } - - resp := provider.ValidateDataSourceConfig(req) - diags = diags.Append(resp.Diagnostics.InConfigBody(cfg.Config)) - } - - if n.IgnoreWarnings { - // If we _only_ have warnings then we'll return nil. - if diags.HasErrors() { - return diags.NonFatalErr() - } - return nil - } else { - // We'll return an error if there are any diagnostics at all, even if - // some of them are warnings. - return diags.NonFatalErr() - } -} - -func (n *EvalValidateResource) validateCount(ctx EvalContext, expr hcl.Expression) tfdiags.Diagnostics { - if expr == nil { - return nil - } - - var diags tfdiags.Diagnostics - - countVal, countDiags := ctx.EvaluateExpr(expr, cty.Number, nil) - diags = diags.Append(countDiags) - if diags.HasErrors() { - return diags - } - - if countVal.IsNull() { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count argument", - Detail: `The given "count" argument value is null. An integer is required.`, - Subject: expr.Range().Ptr(), - }) - return diags - } - - var err error - countVal, err = convert.Convert(countVal, cty.Number) - if err != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count argument", - Detail: fmt.Sprintf(`The given "count" argument value is unsuitable: %s.`, err), - Subject: expr.Range().Ptr(), - }) - return diags - } - - // If the value isn't known then that's the best we can do for now, but - // we'll check more thoroughly during the plan walk. - if !countVal.IsKnown() { - return diags - } - - // If we _do_ know the value, then we can do a few more checks here. - var count int - err = gocty.FromCtyValue(countVal, &count) - if err != nil { - // Isn't a whole number, etc. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count argument", - Detail: fmt.Sprintf(`The given "count" argument value is unsuitable: %s.`, err), - Subject: expr.Range().Ptr(), - }) - return diags - } - - if count < 0 { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid count argument", - Detail: `The given "count" argument value is unsuitable: count cannot be negative.`, - Subject: expr.Range().Ptr(), - }) - return diags - } - - return diags -} - -func (n *EvalValidateResource) validateForEach(ctx EvalContext, expr hcl.Expression) (diags tfdiags.Diagnostics) { - val, forEachDiags := evaluateForEachExpressionValue(expr, ctx) - // If the value isn't known then that's the best we can do for now, but - // we'll check more thoroughly during the plan walk - if !val.IsKnown() { - return diags - } - - if forEachDiags.HasErrors() { - diags = diags.Append(forEachDiags) - } - - return diags -} - -func validateDependsOn(ctx EvalContext, dependsOn []hcl.Traversal) (diags tfdiags.Diagnostics) { - for _, traversal := range dependsOn { - ref, refDiags := addrs.ParseRef(traversal) - diags = diags.Append(refDiags) - if !refDiags.HasErrors() && len(ref.Remaining) != 0 { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid depends_on reference", - Detail: "References in depends_on must be to a whole object (resource, etc), not to an attribute of an object.", - Subject: ref.Remaining.SourceRange().Ptr(), - }) - } - - // The ref must also refer to something that exists. To test that, - // we'll just eval it and count on the fact that our evaluator will - // detect references to non-existent objects. - if !diags.HasErrors() { - scope := ctx.EvaluationScope(nil, EvalDataForNoInstanceKey) - if scope != nil { // sometimes nil in tests, due to incomplete mocks - _, refDiags = scope.EvalReference(ref, cty.DynamicPseudoType) - diags = diags.Append(refDiags) - } - } - } - return diags -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_validate_selfref.go b/vendor/github.com/hashicorp/terraform/terraform/eval_validate_selfref.go deleted file mode 100644 index dd5e4018..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_validate_selfref.go +++ /dev/null @@ -1,67 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" - - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/lang" - "github.com/hashicorp/terraform/tfdiags" -) - -// EvalValidateSelfRef is an EvalNode implementation that checks to ensure that -// expressions within a particular referencable block do not reference that -// same block. -type EvalValidateSelfRef struct { - Addr addrs.Referenceable - Config hcl.Body - ProviderSchema **ProviderSchema -} - -func (n *EvalValidateSelfRef) Eval(ctx EvalContext) (interface{}, error) { - var diags tfdiags.Diagnostics - addr := n.Addr - - addrStrs := make([]string, 0, 1) - addrStrs = append(addrStrs, addr.String()) - switch tAddr := addr.(type) { - case addrs.ResourceInstance: - // A resource instance may not refer to its containing resource either. - addrStrs = append(addrStrs, tAddr.ContainingResource().String()) - } - - if n.ProviderSchema == nil || *n.ProviderSchema == nil { - return nil, fmt.Errorf("provider schema unavailable while validating %s for self-references; this is a bug in Terraform and should be reported", addr) - } - - providerSchema := *n.ProviderSchema - var schema *configschema.Block - switch tAddr := addr.(type) { - case addrs.Resource: - schema, _ = providerSchema.SchemaForResourceAddr(tAddr) - case addrs.ResourceInstance: - schema, _ = providerSchema.SchemaForResourceAddr(tAddr.ContainingResource()) - } - - if schema == nil { - return nil, fmt.Errorf("no schema available for %s to validate for self-references; this is a bug in Terraform and should be reported", addr) - } - - refs, _ := lang.ReferencesInBlock(n.Config, schema) - for _, ref := range refs { - for _, addrStr := range addrStrs { - if ref.Subject.String() == addrStr { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Self-referential block", - Detail: fmt.Sprintf("Configuration for %s may not refer to itself.", addrStr), - Subject: ref.SourceRange.ToHCL().Ptr(), - }) - } - } - } - - return nil, diags.NonFatalErr() -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_validate_selfref_test.go b/vendor/github.com/hashicorp/terraform/terraform/eval_validate_selfref_test.go deleted file mode 100644 index de3b9a34..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_validate_selfref_test.go +++ /dev/null @@ -1,115 +0,0 @@ -package terraform - -import ( - "fmt" - "testing" - - "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/tfdiags" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hcltest" - "github.com/hashicorp/terraform/addrs" - "github.com/zclconf/go-cty/cty" -) - -func TestEvalValidateSelfRef(t *testing.T) { - rAddr := addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "aws_instance", - Name: "foo", - } - - tests := []struct { - Name string - Addr addrs.Referenceable - Expr hcl.Expression - Err bool - }{ - { - "no references at all", - rAddr, - hcltest.MockExprLiteral(cty.StringVal("bar")), - false, - }, - - { - "non self reference", - rAddr, - hcltest.MockExprTraversalSrc("aws_instance.bar.id"), - false, - }, - - { - "self reference", - rAddr, - hcltest.MockExprTraversalSrc("aws_instance.foo.id"), - true, - }, - - { - "self reference other index", - rAddr, - hcltest.MockExprTraversalSrc("aws_instance.foo[4].id"), - false, - }, - - { - "self reference same index", - rAddr.Instance(addrs.IntKey(4)), - hcltest.MockExprTraversalSrc("aws_instance.foo[4].id"), - true, - }, - - { - "self reference whole", - rAddr.Instance(addrs.IntKey(4)), - hcltest.MockExprTraversalSrc("aws_instance.foo"), - true, - }, - } - - for i, test := range tests { - t.Run(fmt.Sprintf("%d-%s", i, test.Name), func(t *testing.T) { - body := hcltest.MockBody(&hcl.BodyContent{ - Attributes: hcl.Attributes{ - "foo": { - Name: "foo", - Expr: test.Expr, - }, - }, - }) - - ps := &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ - "aws_instance": &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": { - Type: cty.String, - Required: true, - }, - }, - }, - }, - } - - n := &EvalValidateSelfRef{ - Addr: test.Addr, - Config: body, - ProviderSchema: &ps, - } - result, err := n.Eval(nil) - if result != nil { - t.Fatal("result should always be nil") - } - diags := tfdiags.Diagnostics(nil).Append(err) - if diags.HasErrors() != test.Err { - if test.Err { - t.Errorf("unexpected success; want error") - } else { - t.Errorf("unexpected error\n\n%s", diags.Err()) - } - } - }) - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_validate_test.go b/vendor/github.com/hashicorp/terraform/terraform/eval_validate_test.go deleted file mode 100644 index ce23b359..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_validate_test.go +++ /dev/null @@ -1,518 +0,0 @@ -package terraform - -import ( - "errors" - "strings" - "testing" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hcltest" - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/configs" - "github.com/hashicorp/terraform/configs/configschema" - "github.com/hashicorp/terraform/providers" - "github.com/hashicorp/terraform/provisioners" - "github.com/hashicorp/terraform/tfdiags" -) - -func TestEvalValidateResource_managedResource(t *testing.T) { - mp := simpleMockProvider() - mp.ValidateResourceTypeConfigFn = func(req providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse { - if got, want := req.TypeName, "test_object"; got != want { - t.Fatalf("wrong resource type\ngot: %#v\nwant: %#v", got, want) - } - if got, want := req.Config.GetAttr("test_string"), cty.StringVal("bar"); !got.RawEquals(want) { - t.Fatalf("wrong value for test_string\ngot: %#v\nwant: %#v", got, want) - } - return providers.ValidateResourceTypeConfigResponse{} - } - - p := providers.Interface(mp) - rc := &configs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test_object", - Name: "foo", - Config: configs.SynthBody("", map[string]cty.Value{ - "test_string": cty.StringVal("bar"), - }), - } - node := &EvalValidateResource{ - Addr: addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "aws_instance", - Name: "foo", - }, - Provider: &p, - Config: rc, - ProviderSchema: &mp.GetSchemaReturn, - } - - ctx := &MockEvalContext{} - ctx.installSimpleEval() - - err := node.Validate(ctx) - if err != nil { - t.Fatalf("err: %s", err) - } - - if !mp.ValidateResourceTypeConfigCalled { - t.Fatal("Expected ValidateResourceTypeConfig to be called, but it was not!") - } -} - -func TestEvalValidateResource_managedResourceCount(t *testing.T) { - mp := simpleMockProvider() - mp.ValidateResourceTypeConfigFn = func(req providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse { - if got, want := req.TypeName, "test_object"; got != want { - t.Fatalf("wrong resource type\ngot: %#v\nwant: %#v", got, want) - } - if got, want := req.Config.GetAttr("test_string"), cty.StringVal("bar"); !got.RawEquals(want) { - t.Fatalf("wrong value for test_string\ngot: %#v\nwant: %#v", got, want) - } - return providers.ValidateResourceTypeConfigResponse{} - } - - p := providers.Interface(mp) - rc := &configs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test_object", - Name: "foo", - Count: hcltest.MockExprLiteral(cty.NumberIntVal(2)), - Config: configs.SynthBody("", map[string]cty.Value{ - "test_string": cty.StringVal("bar"), - }), - } - node := &EvalValidateResource{ - Addr: addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "aws_instance", - Name: "foo", - }, - Provider: &p, - Config: rc, - ProviderSchema: &mp.GetSchemaReturn, - } - - ctx := &MockEvalContext{} - ctx.installSimpleEval() - - err := node.Validate(ctx) - if err != nil { - t.Fatalf("err: %s", err) - } - - if !mp.ValidateResourceTypeConfigCalled { - t.Fatal("Expected ValidateResourceTypeConfig to be called, but it was not!") - } -} - -func TestEvalValidateResource_dataSource(t *testing.T) { - mp := simpleMockProvider() - mp.ValidateDataSourceConfigFn = func(req providers.ValidateDataSourceConfigRequest) providers.ValidateDataSourceConfigResponse { - if got, want := req.TypeName, "test_object"; got != want { - t.Fatalf("wrong resource type\ngot: %#v\nwant: %#v", got, want) - } - if got, want := req.Config.GetAttr("test_string"), cty.StringVal("bar"); !got.RawEquals(want) { - t.Fatalf("wrong value for test_string\ngot: %#v\nwant: %#v", got, want) - } - return providers.ValidateDataSourceConfigResponse{} - } - - p := providers.Interface(mp) - rc := &configs.Resource{ - Mode: addrs.DataResourceMode, - Type: "test_object", - Name: "foo", - Config: configs.SynthBody("", map[string]cty.Value{ - "test_string": cty.StringVal("bar"), - }), - } - - node := &EvalValidateResource{ - Addr: addrs.Resource{ - Mode: addrs.DataResourceMode, - Type: "aws_ami", - Name: "foo", - }, - Provider: &p, - Config: rc, - ProviderSchema: &mp.GetSchemaReturn, - } - - ctx := &MockEvalContext{} - ctx.installSimpleEval() - - err := node.Validate(ctx) - if err != nil { - t.Fatalf("err: %s", err) - } - - if !mp.ValidateDataSourceConfigCalled { - t.Fatal("Expected ValidateDataSourceConfig to be called, but it was not!") - } -} - -func TestEvalValidateResource_validReturnsNilError(t *testing.T) { - mp := simpleMockProvider() - mp.ValidateResourceTypeConfigFn = func(req providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse { - return providers.ValidateResourceTypeConfigResponse{} - } - - p := providers.Interface(mp) - rc := &configs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test_object", - Name: "foo", - Config: configs.SynthBody("", map[string]cty.Value{}), - } - node := &EvalValidateResource{ - Addr: addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test_object", - Name: "foo", - }, - Provider: &p, - Config: rc, - ProviderSchema: &mp.GetSchemaReturn, - } - - ctx := &MockEvalContext{} - ctx.installSimpleEval() - - err := node.Validate(ctx) - if err != nil { - t.Fatalf("Expected nil error, got: %s", err) - } -} - -func TestEvalValidateResource_warningsAndErrorsPassedThrough(t *testing.T) { - mp := simpleMockProvider() - mp.ValidateResourceTypeConfigFn = func(req providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse { - var diags tfdiags.Diagnostics - diags = diags.Append(tfdiags.SimpleWarning("warn")) - diags = diags.Append(errors.New("err")) - return providers.ValidateResourceTypeConfigResponse{ - Diagnostics: diags, - } - } - - p := providers.Interface(mp) - rc := &configs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test_object", - Name: "foo", - Config: configs.SynthBody("", map[string]cty.Value{}), - } - node := &EvalValidateResource{ - Addr: addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test_object", - Name: "foo", - }, - Provider: &p, - Config: rc, - ProviderSchema: &mp.GetSchemaReturn, - } - - ctx := &MockEvalContext{} - ctx.installSimpleEval() - - err := node.Validate(ctx) - if err == nil { - t.Fatal("unexpected success; want error") - } - - var diags tfdiags.Diagnostics - diags = diags.Append(err) - bySeverity := map[tfdiags.Severity]tfdiags.Diagnostics{} - for _, diag := range diags { - bySeverity[diag.Severity()] = append(bySeverity[diag.Severity()], diag) - } - if len(bySeverity[tfdiags.Warning]) != 1 || bySeverity[tfdiags.Warning][0].Description().Summary != "warn" { - t.Errorf("Expected 1 warning 'warn', got: %s", bySeverity[tfdiags.Warning].ErrWithWarnings()) - } - if len(bySeverity[tfdiags.Error]) != 1 || bySeverity[tfdiags.Error][0].Description().Summary != "err" { - t.Errorf("Expected 1 error 'err', got: %s", bySeverity[tfdiags.Error].Err()) - } -} - -func TestEvalValidateResource_ignoreWarnings(t *testing.T) { - mp := simpleMockProvider() - mp.ValidateResourceTypeConfigFn = func(req providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse { - var diags tfdiags.Diagnostics - diags = diags.Append(tfdiags.SimpleWarning("warn")) - return providers.ValidateResourceTypeConfigResponse{ - Diagnostics: diags, - } - } - - p := providers.Interface(mp) - rc := &configs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test_object", - Name: "foo", - Config: configs.SynthBody("", map[string]cty.Value{}), - } - node := &EvalValidateResource{ - Addr: addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test-object", - Name: "foo", - }, - Provider: &p, - Config: rc, - ProviderSchema: &mp.GetSchemaReturn, - - IgnoreWarnings: true, - } - - ctx := &MockEvalContext{} - ctx.installSimpleEval() - - err := node.Validate(ctx) - if err != nil { - t.Fatalf("Expected no error, got: %s", err) - } -} - -func TestEvalValidateResource_invalidDependsOn(t *testing.T) { - mp := simpleMockProvider() - mp.ValidateResourceTypeConfigFn = func(req providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse { - return providers.ValidateResourceTypeConfigResponse{} - } - - // We'll check a _valid_ config first, to make sure we're not failing - // for some other reason, and then make it invalid. - p := providers.Interface(mp) - rc := &configs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "test_object", - Name: "foo", - Config: configs.SynthBody("", map[string]cty.Value{}), - DependsOn: []hcl.Traversal{ - // Depending on path.module is pointless, since it is immediately - // available, but we allow all of the referencable addrs here - // for consistency: referencing them is harmless, and avoids the - // need for us to document a different subset of addresses that - // are valid in depends_on. - // For the sake of this test, it's a valid address we can use that - // doesn't require something else to exist in the configuration. - { - hcl.TraverseRoot{ - Name: "path", - }, - hcl.TraverseAttr{ - Name: "module", - }, - }, - }, - } - node := &EvalValidateResource{ - Addr: addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "aws_instance", - Name: "foo", - }, - Provider: &p, - Config: rc, - ProviderSchema: &mp.GetSchemaReturn, - } - - ctx := &MockEvalContext{} - ctx.installSimpleEval() - - err := node.Validate(ctx) - if err != nil { - t.Fatalf("error for supposedly-valid config: %s", err) - } - - // Now we'll make it invalid by adding additional traversal steps at - // the end of what we're referencing. This is intended to catch the - // situation where the user tries to depend on e.g. a specific resource - // attribute, rather than the whole resource, like aws_instance.foo.id. - rc.DependsOn = append(rc.DependsOn, hcl.Traversal{ - hcl.TraverseRoot{ - Name: "path", - }, - hcl.TraverseAttr{ - Name: "module", - }, - hcl.TraverseAttr{ - Name: "extra", - }, - }) - - err = node.Validate(ctx) - if err == nil { - t.Fatal("no error for invalid depends_on") - } - if got, want := err.Error(), "Invalid depends_on reference"; !strings.Contains(got, want) { - t.Fatalf("wrong error\ngot: %s\nwant: Message containing %q", got, want) - } - - // Test for handling an unknown root without attribute, like a - // typo that omits the dot inbetween "path.module". - rc.DependsOn = append(rc.DependsOn, hcl.Traversal{ - hcl.TraverseRoot{ - Name: "pathmodule", - }, - }) - - err = node.Validate(ctx) - if err == nil { - t.Fatal("no error for invalid depends_on") - } - if got, want := err.Error(), "Invalid depends_on reference"; !strings.Contains(got, want) { - t.Fatalf("wrong error\ngot: %s\nwant: Message containing %q", got, want) - } -} - -func TestEvalValidateProvisioner_valid(t *testing.T) { - mp := &MockProvisioner{} - var p provisioners.Interface = mp - ctx := &MockEvalContext{} - ctx.installSimpleEval() - - schema := &configschema.Block{} - - node := &EvalValidateProvisioner{ - ResourceAddr: addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "foo", - Name: "bar", - }, - Provisioner: &p, - Schema: &schema, - Config: &configs.Provisioner{ - Type: "baz", - Config: hcl.EmptyBody(), - Connection: &configs.Connection{ - Config: configs.SynthBody("", map[string]cty.Value{ - "host": cty.StringVal("localhost"), - "type": cty.StringVal("ssh"), - }), - }, - }, - } - - err := node.Validate(ctx) - if err != nil { - t.Fatalf("node.Eval failed: %s", err) - } - if !mp.ValidateProvisionerConfigCalled { - t.Fatalf("p.ValidateProvisionerConfig not called") - } -} - -func TestEvalValidateProvisioner_warning(t *testing.T) { - mp := &MockProvisioner{} - var p provisioners.Interface = mp - ctx := &MockEvalContext{} - ctx.installSimpleEval() - - schema := &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "type": { - Type: cty.String, - Optional: true, - }, - }, - } - - node := &EvalValidateProvisioner{ - ResourceAddr: addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "foo", - Name: "bar", - }, - Provisioner: &p, - Schema: &schema, - Config: &configs.Provisioner{ - Type: "baz", - Config: hcl.EmptyBody(), - Connection: &configs.Connection{ - Config: configs.SynthBody("", map[string]cty.Value{ - "host": cty.StringVal("localhost"), - "type": cty.StringVal("ssh"), - }), - }, - }, - } - - { - var diags tfdiags.Diagnostics - diags = diags.Append(tfdiags.SimpleWarning("foo is deprecated")) - mp.ValidateProvisionerConfigResponse = provisioners.ValidateProvisionerConfigResponse{ - Diagnostics: diags, - } - } - - err := node.Validate(ctx) - if err == nil { - t.Fatalf("node.Eval succeeded; want error") - } - - var diags tfdiags.Diagnostics - diags = diags.Append(err) - if len(diags) != 1 { - t.Fatalf("wrong number of diagnostics in %s; want one warning", diags.ErrWithWarnings()) - } - - if got, want := diags[0].Description().Summary, mp.ValidateProvisionerConfigResponse.Diagnostics[0].Description().Summary; got != want { - t.Fatalf("wrong warning %q; want %q", got, want) - } -} - -func TestEvalValidateProvisioner_connectionInvalid(t *testing.T) { - var p provisioners.Interface = &MockProvisioner{} - ctx := &MockEvalContext{} - ctx.installSimpleEval() - - schema := &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "type": { - Type: cty.String, - Optional: true, - }, - }, - } - - node := &EvalValidateProvisioner{ - ResourceAddr: addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "foo", - Name: "bar", - }, - Provisioner: &p, - Schema: &schema, - Config: &configs.Provisioner{ - Type: "baz", - Config: hcl.EmptyBody(), - Connection: &configs.Connection{ - Config: configs.SynthBody("", map[string]cty.Value{ - "type": cty.StringVal("ssh"), - "bananananananana": cty.StringVal("foo"), - "bazaz": cty.StringVal("bar"), - }), - }, - }, - } - - err := node.Validate(ctx) - if err == nil { - t.Fatalf("node.Eval succeeded; want error") - } - - var diags tfdiags.Diagnostics - diags = diags.Append(err) - if len(diags) != 3 { - t.Fatalf("wrong number of diagnostics; want two errors\n\n%s", diags.Err()) - } - - errStr := diags.Err().Error() - if !(strings.Contains(errStr, "bananananananana") && strings.Contains(errStr, "bazaz")) { - t.Fatalf("wrong errors %q; want something about each of our invalid connInfo keys", errStr) - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_variable.go b/vendor/github.com/hashicorp/terraform/terraform/eval_variable.go index 72734b78..34669f96 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_variable.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_variable.go @@ -18,14 +18,12 @@ import ( // This must be used only after any side-effects that make the value of the // variable available for use in expression evaluation, such as // EvalModuleCallArgument for variables in descendent modules. -func evalVariableValidations(addr addrs.AbsInputVariableInstance, config *configs.Variable, expr hcl.Expression, ctx EvalContext) error { +func evalVariableValidations(addr addrs.AbsInputVariableInstance, config *configs.Variable, expr hcl.Expression, ctx EvalContext) (diags tfdiags.Diagnostics) { if config == nil || len(config.Validations) == 0 { log.Printf("[TRACE] evalVariableValidations: not active for %s, so skipping", addr) return nil } - var diags tfdiags.Diagnostics - // Variable nodes evaluate in the parent module to where they were declared // because the value expression (n.Expr, if set) comes from the calling // "module" block in the parent module. @@ -83,6 +81,11 @@ func evalVariableValidations(addr addrs.AbsInputVariableInstance, config *config continue } + // Validation condition may be marked if the input variable is bound to + // a sensitive value. This is irrelevant to the validation process, so + // we discard the marks now. + result, _ = result.Unmark() + if result.False() { if expr != nil { diags = diags.Append(&hcl.Diagnostic{ @@ -105,5 +108,5 @@ func evalVariableValidations(addr addrs.AbsInputVariableInstance, config *config } } - return diags.ErrWithWarnings() + return diags } diff --git a/vendor/github.com/hashicorp/terraform/terraform/evaluate.go b/vendor/github.com/hashicorp/terraform/terraform/evaluate.go index add559cb..282569ee 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/evaluate.go +++ b/vendor/github.com/hashicorp/terraform/terraform/evaluate.go @@ -144,7 +144,7 @@ func (d *evaluationStateData) GetCountAttr(addr addrs.CountAttr, rng tfdiags.Sou diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: `Reference to "count" in non-counted context`, - Detail: fmt.Sprintf(`The "count" object can only be used in "module", "resource", and "data" blocks, and only when the "count" argument is set.`), + Detail: `The "count" object can only be used in "module", "resource", and "data" blocks, and only when the "count" argument is set.`, Subject: rng.ToHCL().Ptr(), }) return cty.UnknownVal(cty.Number), diags @@ -176,7 +176,7 @@ func (d *evaluationStateData) GetForEachAttr(addr addrs.ForEachAttr, rng tfdiags diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: `each.value cannot be used in this context`, - Detail: fmt.Sprintf(`A reference to "each.value" has been used in a context in which it unavailable, such as when the configuration no longer contains the value in its "for_each" expression. Remove this reference to each.value in your configuration to work around this error.`), + Detail: `A reference to "each.value" has been used in a context in which it unavailable, such as when the configuration no longer contains the value in its "for_each" expression. Remove this reference to each.value in your configuration to work around this error.`, Subject: rng.ToHCL().Ptr(), }) return cty.UnknownVal(cty.DynamicPseudoType), diags @@ -195,7 +195,7 @@ func (d *evaluationStateData) GetForEachAttr(addr addrs.ForEachAttr, rng tfdiags diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, Summary: `Reference to "each" in context without for_each`, - Detail: fmt.Sprintf(`The "each" object can be used only in "module" or "resource" blocks, and only when the "for_each" argument is set.`), + Detail: `The "each" object can be used only in "module" or "resource" blocks, and only when the "for_each" argument is set.`, Subject: rng.ToHCL().Ptr(), }) return cty.UnknownVal(cty.DynamicPseudoType), diags @@ -259,6 +259,10 @@ func (d *evaluationStateData) GetInputVariable(addr addrs.InputVariable, rng tfd // being liberal in what it accepts because the subsequent plan walk has // more information available and so can be more conservative. if d.Operation == walkValidate { + // Ensure variable sensitivity is captured in the validate walk + if config.Sensitive { + return cty.UnknownVal(wantType).Mark("sensitive"), diags + } return cty.UnknownVal(wantType), diags } @@ -292,7 +296,8 @@ func (d *evaluationStateData) GetInputVariable(addr addrs.InputVariable, rng tfd val = cty.UnknownVal(wantType) } - if config.Sensitive { + // Mark if sensitive, and avoid double-marking if this has already been marked + if config.Sensitive && !val.HasMark("sensitive") { val = val.Mark("sensitive") } @@ -427,7 +432,7 @@ func (d *evaluationStateData) GetModule(addr addrs.ModuleCall, rng tfdiags.Sourc instance[cfg.Name] = outputState - if cfg.Sensitive { + if cfg.Sensitive && !outputState.HasMark("sensitive") { instance[cfg.Name] = outputState.Mark("sensitive") } } @@ -454,9 +459,9 @@ func (d *evaluationStateData) GetModule(addr addrs.ModuleCall, rng tfdiags.Sourc continue } - instance[cfg.Name] = change.After + instance[cfg.Name] = change.After.MarkWithPaths(changeSrc.AfterValMarks) - if change.Sensitive { + if change.Sensitive && !change.After.HasMark("sensitive") { instance[cfg.Name] = change.After.Mark("sensitive") } } @@ -752,11 +757,13 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc continue } - // If our schema contains sensitive values, mark those as sensitive + // If our provider schema contains sensitive values, mark those as sensitive + afterMarks := change.AfterValMarks if schema.ContainsSensitive() { - val = markProviderSensitiveAttributes(schema, val) + afterMarks = append(afterMarks, schema.ValueMarks(val, nil)...) } - instances[key] = val + + instances[key] = val.MarkWithPaths(afterMarks) continue } @@ -774,9 +781,16 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc } val := ios.Value - // If our schema contains sensitive values, mark those as sensitive + + // If our schema contains sensitive values, mark those as sensitive. + // Since decoding the instance object can also apply sensitivity marks, + // we must remove and combine those before remarking to avoid a double- + // mark error. if schema.ContainsSensitive() { - val = markProviderSensitiveAttributes(schema, val) + var marks []cty.PathValueMarks + val, marks = val.UnmarkDeepWithPaths() + marks = append(marks, schema.ValueMarks(val, nil)...) + val = val.MarkWithPaths(marks) } instances[key] = val } @@ -945,51 +959,3 @@ func moduleDisplayAddr(addr addrs.ModuleInstance) string { return addr.String() } } - -// markProviderSensitiveAttributes returns an updated value -// where attributes that are Sensitive are marked -func markProviderSensitiveAttributes(schema *configschema.Block, val cty.Value) cty.Value { - return val.MarkWithPaths(getValMarks(schema, val, nil)) -} - -func getValMarks(schema *configschema.Block, val cty.Value, path cty.Path) []cty.PathValueMarks { - var pvm []cty.PathValueMarks - for name, attrS := range schema.Attributes { - if attrS.Sensitive { - // Create a copy of the path, with this step added, to add to our PathValueMarks slice - attrPath := make(cty.Path, len(path), len(path)+1) - copy(attrPath, path) - attrPath = append(path, cty.GetAttrStep{Name: name}) - pvm = append(pvm, cty.PathValueMarks{ - Path: attrPath, - Marks: cty.NewValueMarks("sensitive"), - }) - } - } - - for name, blockS := range schema.BlockTypes { - // If our block doesn't contain any sensitive attributes, skip inspecting it - if !blockS.Block.ContainsSensitive() { - continue - } - // Create a copy of the path, with this step added, to add to our PathValueMarks slice - blockPath := make(cty.Path, len(path), len(path)+1) - copy(blockPath, path) - blockPath = append(path, cty.GetAttrStep{Name: name}) - - blockV := val.GetAttr(name) - switch blockS.Nesting { - case configschema.NestingSingle, configschema.NestingGroup: - pvm = append(pvm, getValMarks(&blockS.Block, blockV, blockPath)...) - case configschema.NestingList, configschema.NestingMap, configschema.NestingSet: - for it := blockV.ElementIterator(); it.Next(); { - idx, blockEV := it.Element() - morePaths := getValMarks(&blockS.Block, blockEV, append(blockPath, cty.IndexStep{Key: idx})) - pvm = append(pvm, morePaths...) - } - default: - panic(fmt.Sprintf("unsupported nesting mode %s", blockS.Nesting)) - } - } - return pvm -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/evaluate_test.go b/vendor/github.com/hashicorp/terraform/terraform/evaluate_test.go index eb07af31..8c57c8e6 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/evaluate_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/evaluate_test.go @@ -98,11 +98,20 @@ func TestEvaluatorGetInputVariable(t *testing.T) { Sensitive: true, Default: cty.StringVal("foo"), }, + // Avoid double marking a value + "some_other_var": { + Name: "some_other_var", + Sensitive: true, + Default: cty.StringVal("bar"), + }, }, }, }, VariableValues: map[string]map[string]cty.Value{ - "": {"some_var": cty.StringVal("bar")}, + "": { + "some_var": cty.StringVal("bar"), + "some_other_var": cty.StringVal("boop").Mark("sensitive"), + }, }, VariableValuesLock: &sync.Mutex{}, } @@ -123,6 +132,18 @@ func TestEvaluatorGetInputVariable(t *testing.T) { if !got.RawEquals(want) { t.Errorf("wrong result %#v; want %#v", got, want) } + + want = cty.StringVal("boop").Mark("sensitive") + got, diags = scope.Data.GetInputVariable(addrs.InputVariable{ + Name: "some_other_var", + }, tfdiags.SourceRange{}) + + if len(diags) != 0 { + t.Errorf("unexpected diagnostics %s", spew.Sdump(diags)) + } + if !got.RawEquals(want) { + t.Errorf("wrong result %#v; want %#v", got, want) + } } func TestEvaluatorGetResource(t *testing.T) { @@ -297,6 +318,145 @@ func TestEvaluatorGetResource(t *testing.T) { } } +// GetResource will return a planned object's After value +// if there is a change for that resource instance. +func TestEvaluatorGetResource_changes(t *testing.T) { + // Set up existing state + stateSync := states.BuildState(func(ss *states.SyncState) { + ss.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_resource", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectPlanned, + AttrsJSON: []byte(`{"id":"foo", "to_mark_val":"tacos", "sensitive_value":"abc"}`), + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + }).SyncWrapper() + + // Create a change for the existing state resource, + // to exercise retrieving the After value of the change + changesSync := plans.NewChanges().SyncWrapper() + change := &plans.ResourceInstanceChange{ + Addr: mustResourceInstanceAddr("test_resource.foo"), + ProviderAddr: addrs.AbsProviderConfig{ + Module: addrs.RootModule, + Provider: addrs.NewDefaultProvider("test"), + }, + Change: plans.Change{ + Action: plans.Update, + // Provide an After value that contains a marked value + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + "to_mark_val": cty.StringVal("pizza").Mark("sensitive"), + "sensitive_value": cty.StringVal("abc"), + "sensitive_collection": cty.MapVal(map[string]cty.Value{ + "boop": cty.StringVal("beep"), + }), + }), + }, + } + + // Set up our schemas + schemas := &Schemas{ + Providers: map[addrs.Provider]*ProviderSchema{ + addrs.NewDefaultProvider("test"): { + Provider: &configschema.Block{}, + ResourceTypes: map[string]*configschema.Block{ + "test_resource": { + Attributes: map[string]*configschema.Attribute{ + "id": { + Type: cty.String, + Computed: true, + }, + "to_mark_val": { + Type: cty.String, + Computed: true, + }, + "sensitive_value": { + Type: cty.String, + Computed: true, + Sensitive: true, + }, + "sensitive_collection": { + Type: cty.Map(cty.String), + Computed: true, + Sensitive: true, + }, + }, + }, + }, + }, + }, + } + + // The resource we'll inspect + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_resource", + Name: "foo", + } + schema, _ := schemas.ResourceTypeConfig(addrs.NewDefaultProvider("test"), addr.Mode, addr.Type) + // This encoding separates out the After's marks into its AfterValMarks + csrc, _ := change.Encode(schema.ImpliedType()) + changesSync.AppendResourceInstanceChange(csrc) + + evaluator := &Evaluator{ + Meta: &ContextMeta{ + Env: "foo", + }, + Changes: changesSync, + Config: &configs.Config{ + Module: &configs.Module{ + ManagedResources: map[string]*configs.Resource{ + "test_resource.foo": { + Mode: addrs.ManagedResourceMode, + Type: "test_resource", + Name: "foo", + Provider: addrs.Provider{ + Hostname: addrs.DefaultRegistryHost, + Namespace: "hashicorp", + Type: "test", + }, + }, + }, + }, + }, + State: stateSync, + Schemas: schemas, + } + + data := &evaluationStateData{ + Evaluator: evaluator, + } + scope := evaluator.Scope(data, nil) + + want := cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + "to_mark_val": cty.StringVal("pizza").Mark("sensitive"), + "sensitive_value": cty.StringVal("abc").Mark("sensitive"), + "sensitive_collection": cty.MapVal(map[string]cty.Value{ + "boop": cty.StringVal("beep"), + }).Mark("sensitive"), + }) + + got, diags := scope.Data.GetResource(addr, tfdiags.SourceRange{}) + + if len(diags) != 0 { + t.Errorf("unexpected diagnostics %s", spew.Sdump(diags)) + } + + if !got.RawEquals(want) { + t.Errorf("wrong result:\ngot: %#v\nwant: %#v", got, want) + } +} + func TestEvaluatorGetModule(t *testing.T) { // Create a new evaluator with an existing state stateSync := states.BuildState(func(ss *states.SyncState) { diff --git a/vendor/github.com/hashicorp/terraform/terraform/evaluate_valid_test.go b/vendor/github.com/hashicorp/terraform/terraform/evaluate_valid_test.go index 9be7b278..086ca303 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/evaluate_valid_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/evaluate_valid_test.go @@ -50,6 +50,14 @@ For example, to correlate with indices of a referring resource, use: aws_instance.count[count.index] - Unsupported attribute: This object has no argument, nested block, or exported attribute named "foo".`, }, + { + "boop_instance.yep", + ``, + }, + { + "boop_whatever.nope", + `Invalid resource type: A managed resource type "boop_whatever" is not supported by provider "registry.terraform.io/foobar/beep".`, + }, } cfg := testModule(t, "static-validate-refs") @@ -62,6 +70,12 @@ For example, to correlate with indices of a referring resource, use: "aws_instance": {}, }, }, + addrs.MustParseProviderSourceString("foobar/beep"): { + ResourceTypes: map[string]*configschema.Block{ + // intentional mismatch between resource type prefix and provider type + "boop_instance": {}, + }, + }, }, }, } diff --git a/vendor/github.com/hashicorp/terraform/terraform/execute.go b/vendor/github.com/hashicorp/terraform/terraform/execute.go index 5bf06c4d..6d038d9d 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/execute.go +++ b/vendor/github.com/hashicorp/terraform/terraform/execute.go @@ -1,9 +1,9 @@ package terraform +import "github.com/hashicorp/terraform/tfdiags" + // GraphNodeExecutable is the interface that graph nodes must implement to -// enable execution. This is an alternative to GraphNodeEvalable, which is in -// the process of being removed. A given graph node should _not_ implement both -// GraphNodeExecutable and GraphNodeEvalable. +// enable execution. type GraphNodeExecutable interface { - Execute(EvalContext, walkOperation) error + Execute(EvalContext, walkOperation) tfdiags.Diagnostics } diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph.go b/vendor/github.com/hashicorp/terraform/terraform/graph.go index c690b356..5fa1ff28 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graph.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graph.go @@ -38,8 +38,7 @@ func (g *Graph) walk(walker GraphWalker) tfdiags.Diagnostics { ctx := walker.EvalContext() // Walk the graph. - var walkFn dag.WalkFunc - walkFn = func(v dag.Vertex) (diags tfdiags.Diagnostics) { + walkFn := func(v dag.Vertex) (diags tfdiags.Diagnostics) { log.Printf("[TRACE] vertex %q: starting visit (%T)", dag.VertexName(v), v) defer func() { diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_builder.go b/vendor/github.com/hashicorp/terraform/terraform/graph_builder.go index 7d4bd88b..0f740fad 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graph_builder.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graph_builder.go @@ -1,9 +1,7 @@ package terraform import ( - "fmt" "log" - "strings" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/internal/logging" @@ -40,12 +38,6 @@ func (b *BasicGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Di } log.Printf("[TRACE] Executing graph transform %T", step) - stepName := fmt.Sprintf("%T", step) - dot := strings.LastIndex(stepName, ".") - if dot >= 0 { - stepName = stepName[dot+1:] - } - err := step.Transform(g) if thisStepStr := g.StringWithNodeTypes(); thisStepStr != lastStepStr { log.Printf("[TRACE] Completed graph transform %T with new graph:\n%s ------", step, logging.Indent(thisStepStr)) diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_apply.go b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_apply.go index 5501319d..d927cde1 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_apply.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_apply.go @@ -40,6 +40,12 @@ type ApplyGraphBuilder struct { // outputs should go into the diff so that this is unnecessary. Targets []addrs.Targetable + // ForceReplace are the resource instance addresses that the user + // requested to force replacement for when creating the plan, if any. + // The apply step refers to these as part of verifying that the planned + // actions remain consistent between plan and apply. + ForceReplace []addrs.AbsResourceInstance + // Validate will do structural validation of the graph. Validate bool } @@ -71,6 +77,7 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer { concreteResourceInstance := func(a *NodeAbstractResourceInstance) dag.Vertex { return &NodeApplyableResourceInstance{ NodeAbstractResourceInstance: a, + forceReplace: b.ForceReplace, } } @@ -108,10 +115,6 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer { // Attach the configuration to any resources &AttachResourceConfigTransformer{Config: b.Config}, - // Provisioner-related transformations - &MissingProvisionerTransformer{Provisioners: b.Components.ResourceProvisioners()}, - &ProvisionerTransformer{}, - // add providers TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config), @@ -162,7 +165,6 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer { // Close opened plugin connections &CloseProviderTransformer{}, - &CloseProvisionerTransformer{}, // close the root module &CloseRootModuleTransformer{}, diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_apply_test.go b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_apply_test.go index 3e1f6e29..cf707691 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_apply_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_apply_test.go @@ -423,70 +423,6 @@ func TestApplyGraphBuilder_moduleDestroy(t *testing.T) { ) } -func TestApplyGraphBuilder_provisioner(t *testing.T) { - changes := &plans.Changes{ - Resources: []*plans.ResourceInstanceChangeSrc{ - { - Addr: mustResourceInstanceAddr("test_object.foo"), - ChangeSrc: plans.ChangeSrc{ - Action: plans.Create, - }, - }, - }, - } - - b := &ApplyGraphBuilder{ - Config: testModule(t, "graph-builder-apply-provisioner"), - Changes: changes, - Components: simpleMockComponentFactory(), - Schemas: simpleTestSchemas(), - } - - g, err := b.Build(addrs.RootModuleInstance) - if err != nil { - t.Fatalf("err: %s", err) - } - - testGraphContains(t, g, "provisioner.test") - testGraphHappensBefore( - t, g, - "provisioner.test", - "test_object.foo", - ) -} - -func TestApplyGraphBuilder_provisionerDestroy(t *testing.T) { - changes := &plans.Changes{ - Resources: []*plans.ResourceInstanceChangeSrc{ - { - Addr: mustResourceInstanceAddr("test_object.foo"), - ChangeSrc: plans.ChangeSrc{ - Action: plans.Delete, - }, - }, - }, - } - - b := &ApplyGraphBuilder{ - Config: testModule(t, "graph-builder-apply-provisioner"), - Changes: changes, - Components: simpleMockComponentFactory(), - Schemas: simpleTestSchemas(), - } - - g, err := b.Build(addrs.RootModuleInstance) - if err != nil { - t.Fatalf("err: %s", err) - } - - testGraphContains(t, g, "provisioner.test") - testGraphHappensBefore( - t, g, - "provisioner.test", - "test_object.foo (destroy)", - ) -} - func TestApplyGraphBuilder_targetModule(t *testing.T) { changes := &plans.Changes{ Resources: []*plans.ResourceInstanceChangeSrc{ @@ -784,7 +720,6 @@ module.child.test_object.create module.child.test_object.create (expand) module.child (expand) provider["registry.terraform.io/hashicorp/test"] - provisioner.test module.child.test_object.other module.child.test_object.create module.child.test_object.other (expand) @@ -795,13 +730,9 @@ provider["registry.terraform.io/hashicorp/test"] provider["registry.terraform.io/hashicorp/test"] (close) module.child.test_object.other test_object.other -provisioner.test -provisioner.test (close) - module.child.test_object.create root meta.count-boundary (EachMode fixup) provider["registry.terraform.io/hashicorp/test"] (close) - provisioner.test (close) test_object.create test_object.create (expand) test_object.create (expand) diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_destroy_plan.go b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_destroy_plan.go index d8216228..c389a776 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_destroy_plan.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_destroy_plan.go @@ -12,7 +12,10 @@ import ( // planning a pure-destroy. // // Planning a pure destroy operation is simple because we can ignore most -// ordering configuration and simply reverse the state. +// ordering configuration and simply reverse the state. This graph mainly +// exists for targeting, because we need to walk the destroy dependencies to +// ensure we plan the required resources. Without the requirement for +// targeting, the plan could theoretically be created directly from the state. type DestroyPlanGraphBuilder struct { // Config is the configuration tree to build the plan from. Config *configs.Config @@ -33,6 +36,11 @@ type DestroyPlanGraphBuilder struct { // Validate will do structural validation of the graph. Validate bool + + // If set, skipRefresh will cause us stop skip refreshing any existing + // resource instances as part of our planning. This will cause us to fail + // to detect if an object has already been deleted outside of Terraform. + skipRefresh bool } // See GraphBuilder @@ -49,6 +57,7 @@ func (b *DestroyPlanGraphBuilder) Steps() []GraphTransformer { concreteResourceInstance := func(a *NodeAbstractResourceInstance) dag.Vertex { return &NodePlanDestroyableResourceInstance{ NodeAbstractResourceInstance: a, + skipRefresh: b.skipRefresh, } } concreteResourceInstanceDeposed := func(a *NodeAbstractResourceInstance, key states.DeposedKey) dag.Vertex { @@ -72,6 +81,7 @@ func (b *DestroyPlanGraphBuilder) Steps() []GraphTransformer { State: b.State, }, + // Create the delete changes for root module outputs. &OutputTransformer{ Config: b.Config, Destroy: true, @@ -93,8 +103,6 @@ func (b *DestroyPlanGraphBuilder) Steps() []GraphTransformer { Schemas: b.Schemas, }, - // Target. Note we don't set "Destroy: true" here since we already - // created proper destroy ordering. &TargetsTransformer{Targets: b.Targets}, // Close opened plugin connections diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_import.go b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_import.go index 8754f3eb..e2bd1788 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_import.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_import.go @@ -84,7 +84,7 @@ func (b *ImportGraphBuilder) Steps() []GraphTransformer { // Make sure data sources are aware of any depends_on from the // configuration - &attachDataResourceDependenciesTransformer{}, + &attachDataResourceDependsOnTransformer{}, // Close opened plugin connections &CloseProviderTransformer{}, diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_plan.go b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_plan.go index 503ce1b6..da471f6a 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_plan.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_plan.go @@ -39,12 +39,23 @@ type PlanGraphBuilder struct { // Targets are resources to target Targets []addrs.Targetable + // ForceReplace are resource instances where if we would normally have + // generated a NoOp or Update action then we'll force generating a replace + // action instead. Create and Delete actions are not affected. + ForceReplace []addrs.AbsResourceInstance + // Validate will do structural validation of the graph. Validate bool // skipRefresh indicates that we should skip refreshing managed resources skipRefresh bool + // skipPlanChanges indicates that we should skip the step of comparing + // prior state with configuration and generating planned changes to + // resource instances. (This is for the "refresh only" planning mode, + // where we _only_ do the refresh step.) + skipPlanChanges bool + // CustomConcrete can be set to customize the node types created // for various parts of the plan. This is useful in order to customize // the plan behavior. @@ -115,10 +126,6 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { // Attach the configuration to any resources &AttachResourceConfigTransformer{Config: b.Config}, - // Provisioner-related transformations - &MissingProvisionerTransformer{Provisioners: b.Components.ResourceProvisioners()}, - &ProvisionerTransformer{}, - // add providers TransformProviders(b.Components.ResourceProviders(), b.ConcreteProvider, b.Config), @@ -141,7 +148,7 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { // Make sure data sources are aware of any depends_on from the // configuration - &attachDataResourceDependenciesTransformer{}, + &attachDataResourceDependsOnTransformer{}, // Target &TargetsTransformer{Targets: b.Targets}, @@ -185,12 +192,16 @@ func (b *PlanGraphBuilder) init() { return &nodeExpandPlannableResource{ NodeAbstractResource: a, skipRefresh: b.skipRefresh, + skipPlanChanges: b.skipPlanChanges, + forceReplace: b.ForceReplace, } } b.ConcreteResourceOrphan = func(a *NodeAbstractResourceInstance) dag.Vertex { return &NodePlannableResourceInstanceOrphan{ NodeAbstractResourceInstance: a, + skipRefresh: b.skipRefresh, + skipPlanChanges: b.skipPlanChanges, } } } diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_plan_test.go b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_plan_test.go index 84fec874..62cd6978 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_plan_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_plan_test.go @@ -16,23 +16,16 @@ func TestPlanGraphBuilder_impl(t *testing.T) { func TestPlanGraphBuilder(t *testing.T) { awsProvider := &MockProvider{ - GetSchemaReturn: &ProviderSchema{ - Provider: simpleTestSchema(), - ResourceTypes: map[string]*configschema.Block{ - "aws_security_group": simpleTestSchema(), - "aws_instance": simpleTestSchema(), - "aws_load_balancer": simpleTestSchema(), - }, - }, - } - openstackProvider := &MockProvider{ - GetSchemaReturn: &ProviderSchema{ - Provider: simpleTestSchema(), - ResourceTypes: map[string]*configschema.Block{ - "openstack_floating_ip": simpleTestSchema(), + GetProviderSchemaResponse: &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{Block: simpleTestSchema()}, + ResourceTypes: map[string]providers.Schema{ + "aws_security_group": {Block: simpleTestSchema()}, + "aws_instance": {Block: simpleTestSchema()}, + "aws_load_balancer": {Block: simpleTestSchema()}, }, }, } + openstackProvider := mockProviderWithResourceTypeSchema("openstack_floating_ip", simpleTestSchema()) components := &basicComponentFactory{ providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("aws"): providers.FactoryFixed(awsProvider), @@ -45,8 +38,8 @@ func TestPlanGraphBuilder(t *testing.T) { Components: components, Schemas: &Schemas{ Providers: map[addrs.Provider]*ProviderSchema{ - addrs.NewDefaultProvider("aws"): awsProvider.GetSchemaReturn, - addrs.NewDefaultProvider("openstack"): openstackProvider.GetSchemaReturn, + addrs.NewDefaultProvider("aws"): awsProvider.ProviderSchema(), + addrs.NewDefaultProvider("openstack"): openstackProvider.ProviderSchema(), }, }, } @@ -68,28 +61,22 @@ func TestPlanGraphBuilder(t *testing.T) { } func TestPlanGraphBuilder_dynamicBlock(t *testing.T) { - provider := &MockProvider{ - GetSchemaReturn: &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ - "test_thing": { + provider := mockProviderWithResourceTypeSchema("test_thing", &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + "list": {Type: cty.List(cty.String), Computed: true}, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "nested": { + Nesting: configschema.NestingList, + Block: configschema.Block{ Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Computed: true}, - "list": {Type: cty.List(cty.String), Computed: true}, - }, - BlockTypes: map[string]*configschema.NestedBlock{ - "nested": { - Nesting: configschema.NestingList, - Block: configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "foo": {Type: cty.String, Optional: true}, - }, - }, - }, + "foo": {Type: cty.String, Optional: true}, }, }, }, }, - } + }) components := &basicComponentFactory{ providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("test"): providers.FactoryFixed(provider), @@ -101,7 +88,7 @@ func TestPlanGraphBuilder_dynamicBlock(t *testing.T) { Components: components, Schemas: &Schemas{ Providers: map[addrs.Provider]*ProviderSchema{ - addrs.NewDefaultProvider("test"): provider.GetSchemaReturn, + addrs.NewDefaultProvider("test"): provider.ProviderSchema(), }, }, } @@ -144,23 +131,17 @@ test_thing.c (expand) } func TestPlanGraphBuilder_attrAsBlocks(t *testing.T) { - provider := &MockProvider{ - GetSchemaReturn: &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ - "test_thing": { - Attributes: map[string]*configschema.Attribute{ - "id": {Type: cty.String, Computed: true}, - "nested": { - Type: cty.List(cty.Object(map[string]cty.Type{ - "foo": cty.String, - })), - Optional: true, - }, - }, - }, + provider := mockProviderWithResourceTypeSchema("test_thing", &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + "nested": { + Type: cty.List(cty.Object(map[string]cty.Type{ + "foo": cty.String, + })), + Optional: true, }, }, - } + }) components := &basicComponentFactory{ providers: map[addrs.Provider]providers.Factory{ addrs.NewDefaultProvider("test"): providers.FactoryFixed(provider), @@ -172,7 +153,7 @@ func TestPlanGraphBuilder_attrAsBlocks(t *testing.T) { Components: components, Schemas: &Schemas{ Providers: map[addrs.Provider]*ProviderSchema{ - addrs.NewDefaultProvider("test"): provider.GetSchemaReturn, + addrs.NewDefaultProvider("test"): provider.ProviderSchema(), }, }, } @@ -233,14 +214,7 @@ func TestPlanGraphBuilder_targetModule(t *testing.T) { } func TestPlanGraphBuilder_forEach(t *testing.T) { - awsProvider := &MockProvider{ - GetSchemaReturn: &ProviderSchema{ - Provider: simpleTestSchema(), - ResourceTypes: map[string]*configschema.Block{ - "aws_instance": simpleTestSchema(), - }, - }, - } + awsProvider := mockProviderWithResourceTypeSchema("aws_instance", simpleTestSchema()) components := &basicComponentFactory{ providers: map[addrs.Provider]providers.Factory{ @@ -253,7 +227,7 @@ func TestPlanGraphBuilder_forEach(t *testing.T) { Components: components, Schemas: &Schemas{ Providers: map[addrs.Provider]*ProviderSchema{ - addrs.NewDefaultProvider("aws"): awsProvider.GetSchemaReturn, + addrs.NewDefaultProvider("aws"): awsProvider.ProviderSchema(), }, }, } diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_test.go b/vendor/github.com/hashicorp/terraform/terraform/graph_test.go index 7dac808e..e61da987 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graph_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graph_test.go @@ -68,37 +68,3 @@ func testGraphHappensBefore(t *testing.T, g *Graph, A, B string) { "Expected %q before %q in:\n\n%s", A, B, g.String()) } - -type testGraphSubPath struct { - PathFn func() []string -} - -func (v *testGraphSubPath) Path() []string { return v.PathFn() } - -type testGraphDependable struct { - VertexName string - DependentOnMock []string -} - -func (v *testGraphDependable) Name() string { - return v.VertexName -} - -func (v *testGraphDependable) DependableName() []string { - return []string{v.VertexName} -} - -func (v *testGraphDependable) DependentOn() []string { - return v.DependentOnMock -} - -const testGraphAddStr = ` -42 -84 -` - -const testGraphConnectDepsStr = ` -a -b - a -` diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_walk_context.go b/vendor/github.com/hashicorp/terraform/terraform/graph_walk_context.go index c7492ed0..d7b24a18 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graph_walk_context.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graph_walk_context.go @@ -25,6 +25,7 @@ type ContextGraphWalker struct { Context *Context State *states.SyncState // Used for safe concurrent access to state RefreshState *states.SyncState // Used for safe concurrent access to state + PrevRunState *states.SyncState // Used for safe concurrent access to state Changes *plans.ChangesSync // Used for safe concurrent writes to changes InstanceExpander *instances.Expander // Tracks our gradual expansion of module and resource instances Operation walkOperation @@ -35,7 +36,6 @@ type ContextGraphWalker struct { // is in progress. NonFatalDiagnostics tfdiags.Diagnostics - errorLock sync.Mutex once sync.Once contexts map[string]*BuiltinEvalContext contextLock sync.Mutex @@ -96,6 +96,7 @@ func (w *ContextGraphWalker) EvalContext() EvalContext { ChangesValue: w.Changes, StateValue: w.State, RefreshStateValue: w.RefreshState, + PrevRunStateValue: w.PrevRunState, Evaluator: evaluator, VariableValues: w.variableValues, VariableValuesLock: &w.variableValuesLock, @@ -123,39 +124,7 @@ func (w *ContextGraphWalker) init() { func (w *ContextGraphWalker) Execute(ctx EvalContext, n GraphNodeExecutable) tfdiags.Diagnostics { // Acquire a lock on the semaphore w.Context.parallelSem.Acquire() + defer w.Context.parallelSem.Release() - err := n.Execute(ctx, w.Operation) - - // Release the semaphore - w.Context.parallelSem.Release() - - if err == nil { - return nil - } - - // Acquire the lock because anything is going to require a lock. - w.errorLock.Lock() - defer w.errorLock.Unlock() - - // If the error is non-fatal then we'll accumulate its diagnostics in our - // non-fatal list, rather than returning it directly, so that the graph - // walk can continue. - if nferr, ok := err.(tfdiags.NonFatalError); ok { - w.NonFatalDiagnostics = w.NonFatalDiagnostics.Append(nferr.Diagnostics) - return nil - } - - // If we early exit, it isn't an error. - if _, isEarlyExit := err.(EvalEarlyExitError); isEarlyExit { - return nil - } - - // Otherwise, we'll let our usual diagnostics machinery figure out how to - // unpack this as one or more diagnostic messages and return that. If we - // get down here then the returned diagnostics will contain at least one - // error, causing the graph walk to halt. - var diags tfdiags.Diagnostics - diags = diags.Append(err) - return diags - + return n.Execute(ctx, w.Operation) } diff --git a/vendor/github.com/hashicorp/terraform/terraform/graphtype_string.go b/vendor/github.com/hashicorp/terraform/terraform/graphtype_string.go index 2d4026e4..ac605cca 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graphtype_string.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graphtype_string.go @@ -11,14 +11,15 @@ func _() { _ = x[GraphTypeInvalid-0] _ = x[GraphTypePlan-1] _ = x[GraphTypePlanDestroy-2] - _ = x[GraphTypeApply-3] - _ = x[GraphTypeValidate-4] - _ = x[GraphTypeEval-5] + _ = x[GraphTypePlanRefreshOnly-3] + _ = x[GraphTypeApply-4] + _ = x[GraphTypeValidate-5] + _ = x[GraphTypeEval-6] } -const _GraphType_name = "GraphTypeInvalidGraphTypePlanGraphTypePlanDestroyGraphTypeApplyGraphTypeValidateGraphTypeEval" +const _GraphType_name = "GraphTypeInvalidGraphTypePlanGraphTypePlanDestroyGraphTypePlanRefreshOnlyGraphTypeApplyGraphTypeValidateGraphTypeEval" -var _GraphType_index = [...]uint8{0, 16, 29, 49, 63, 80, 93} +var _GraphType_index = [...]uint8{0, 16, 29, 49, 73, 87, 104, 117} func (i GraphType) String() string { if i >= GraphType(len(_GraphType_index)-1) { diff --git a/vendor/github.com/hashicorp/terraform/terraform/hook.go b/vendor/github.com/hashicorp/terraform/terraform/hook.go index c0bb23ab..1887c236 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/hook.go +++ b/vendor/github.com/hashicorp/terraform/terraform/hook.go @@ -143,19 +143,3 @@ func (*NilHook) PostImportState(addr addrs.AbsResourceInstance, imported []provi func (*NilHook) PostStateUpdate(new *states.State) (HookAction, error) { return HookActionContinue, nil } - -// handleHook turns hook actions into panics. This lets you use the -// panic/recover mechanism in Go as a flow control mechanism for hook -// actions. -func handleHook(a HookAction, err error) { - if err != nil { - // TODO: handle errors - } - - switch a { - case HookActionContinue: - return - case HookActionHalt: - panic(HookActionHalt) - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/hook_stop.go b/vendor/github.com/hashicorp/terraform/terraform/hook_stop.go index 811fb337..86f22114 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/hook_stop.go +++ b/vendor/github.com/hashicorp/terraform/terraform/hook_stop.go @@ -1,6 +1,7 @@ package terraform import ( + "errors" "sync/atomic" "github.com/zclconf/go-cty/cty" @@ -76,11 +77,7 @@ func (h *stopHook) PostStateUpdate(new *states.State) (HookAction, error) { func (h *stopHook) hook() (HookAction, error) { if h.Stopped() { - // FIXME: This should really return an error since stopping partway - // through is not a successful run-to-completion, but we'll need to - // introduce that cautiously since existing automation solutions may - // be depending on this behavior. - return HookActionHalt, nil + return HookActionHalt, errors.New("execution halted") } return HookActionContinue, nil diff --git a/vendor/github.com/hashicorp/terraform/terraform/marks.go b/vendor/github.com/hashicorp/terraform/terraform/marks.go new file mode 100644 index 00000000..8e2a3260 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/marks.go @@ -0,0 +1,39 @@ +package terraform + +import ( + "fmt" + "sort" + + "github.com/zclconf/go-cty/cty" +) + +// marksEqual compares 2 unordered sets of PathValue marks for equality, with +// the comparison using the cty.PathValueMarks.Equal method. +func marksEqual(a, b []cty.PathValueMarks) bool { + if len(a) == 0 && len(b) == 0 { + return true + } + + if len(a) != len(b) { + return false + } + + less := func(s []cty.PathValueMarks) func(i, j int) bool { + return func(i, j int) bool { + // the sort only needs to be consistent, so use the GoString format + // to get a comparable value + return fmt.Sprintf("%#v", s[i]) < fmt.Sprintf("%#v", s[j]) + } + } + + sort.Slice(a, less(a)) + sort.Slice(b, less(b)) + + for i := 0; i < len(a); i++ { + if !a[i].Equal(b[i]) { + return false + } + } + + return true +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/marks_test.go b/vendor/github.com/hashicorp/terraform/terraform/marks_test.go new file mode 100644 index 00000000..efb3b7e9 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/marks_test.go @@ -0,0 +1,104 @@ +package terraform + +import ( + "fmt" + "testing" + + "github.com/zclconf/go-cty/cty" +) + +func TestMarksEqual(t *testing.T) { + for i, tc := range []struct { + a, b []cty.PathValueMarks + equal bool + }{ + { + []cty.PathValueMarks{ + cty.PathValueMarks{Path: cty.Path{cty.GetAttrStep{Name: "a"}}, Marks: cty.NewValueMarks("sensitive")}, + }, + []cty.PathValueMarks{ + cty.PathValueMarks{Path: cty.Path{cty.GetAttrStep{Name: "a"}}, Marks: cty.NewValueMarks("sensitive")}, + }, + true, + }, + { + []cty.PathValueMarks{ + cty.PathValueMarks{Path: cty.Path{cty.GetAttrStep{Name: "a"}}, Marks: cty.NewValueMarks("sensitive")}, + }, + []cty.PathValueMarks{ + cty.PathValueMarks{Path: cty.Path{cty.GetAttrStep{Name: "A"}}, Marks: cty.NewValueMarks("sensitive")}, + }, + false, + }, + { + []cty.PathValueMarks{ + cty.PathValueMarks{Path: cty.Path{cty.GetAttrStep{Name: "a"}}, Marks: cty.NewValueMarks("sensitive")}, + cty.PathValueMarks{Path: cty.Path{cty.GetAttrStep{Name: "b"}}, Marks: cty.NewValueMarks("sensitive")}, + cty.PathValueMarks{Path: cty.Path{cty.GetAttrStep{Name: "c"}}, Marks: cty.NewValueMarks("sensitive")}, + }, + []cty.PathValueMarks{ + cty.PathValueMarks{Path: cty.Path{cty.GetAttrStep{Name: "b"}}, Marks: cty.NewValueMarks("sensitive")}, + cty.PathValueMarks{Path: cty.Path{cty.GetAttrStep{Name: "c"}}, Marks: cty.NewValueMarks("sensitive")}, + cty.PathValueMarks{Path: cty.Path{cty.GetAttrStep{Name: "a"}}, Marks: cty.NewValueMarks("sensitive")}, + }, + true, + }, + { + []cty.PathValueMarks{ + cty.PathValueMarks{ + Path: cty.Path{cty.GetAttrStep{Name: "a"}, cty.GetAttrStep{Name: "b"}}, + Marks: cty.NewValueMarks("sensitive"), + }, + cty.PathValueMarks{ + Path: cty.Path{cty.GetAttrStep{Name: "a"}, cty.GetAttrStep{Name: "c"}}, + Marks: cty.NewValueMarks("sensitive"), + }, + }, + []cty.PathValueMarks{ + cty.PathValueMarks{ + Path: cty.Path{cty.GetAttrStep{Name: "a"}, cty.GetAttrStep{Name: "c"}}, + Marks: cty.NewValueMarks("sensitive"), + }, + cty.PathValueMarks{ + Path: cty.Path{cty.GetAttrStep{Name: "a"}, cty.GetAttrStep{Name: "b"}}, + Marks: cty.NewValueMarks("sensitive"), + }, + }, + true, + }, + { + []cty.PathValueMarks{ + cty.PathValueMarks{Path: cty.Path{cty.GetAttrStep{Name: "a"}}, Marks: cty.NewValueMarks("sensitive")}, + }, + []cty.PathValueMarks{ + cty.PathValueMarks{Path: cty.Path{cty.GetAttrStep{Name: "b"}}, Marks: cty.NewValueMarks("sensitive")}, + }, + false, + }, + { + nil, + nil, + true, + }, + { + []cty.PathValueMarks{ + cty.PathValueMarks{Path: cty.Path{cty.GetAttrStep{Name: "a"}}, Marks: cty.NewValueMarks("sensitive")}, + }, + nil, + false, + }, + { + nil, + []cty.PathValueMarks{ + cty.PathValueMarks{Path: cty.Path{cty.GetAttrStep{Name: "a"}}, Marks: cty.NewValueMarks("sensitive")}, + }, + false, + }, + } { + t.Run(fmt.Sprint(i), func(t *testing.T) { + if marksEqual(tc.a, tc.b) != tc.equal { + t.Fatalf("marksEqual(\n%#v,\n%#v,\n) != %t\n", tc.a, tc.b, tc.equal) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_count_boundary.go b/vendor/github.com/hashicorp/terraform/terraform/node_count_boundary.go index bfdbd1ef..2e972ff2 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_count_boundary.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_count_boundary.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" + "github.com/hashicorp/terraform/tfdiags" ) // NodeCountBoundary fixes up any transitions between "each modes" in objects @@ -14,12 +15,14 @@ type NodeCountBoundary struct { Config *configs.Config } +var _ GraphNodeExecutable = (*NodeCountBoundary)(nil) + func (n *NodeCountBoundary) Name() string { return "meta.count-boundary (EachMode fixup)" } // GraphNodeExecutable -func (n *NodeCountBoundary) Execute(ctx EvalContext, op walkOperation) error { +func (n *NodeCountBoundary) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { // We'll temporarily lock the state to grab the modules, then work on each // one separately while taking a lock again for each separate resource. // This means that if another caller concurrently adds a module here while @@ -42,10 +45,11 @@ func (n *NodeCountBoundary) Execute(ctx EvalContext, op walkOperation) error { continue } if err := n.fixModule(ctx, addr); err != nil { - return err + diags = diags.Append(err) + return diags } } - return nil + return diags } func (n *NodeCountBoundary) fixModule(ctx EvalContext, moduleAddr addrs.ModuleInstance) error { diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_count_boundary_test.go b/vendor/github.com/hashicorp/terraform/terraform/node_count_boundary_test.go index 13c5fac2..497d4fc9 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_count_boundary_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_count_boundary_test.go @@ -53,9 +53,9 @@ func TestNodeCountBoundaryExecute(t *testing.T) { } node := NodeCountBoundary{Config: config} - err := node.Execute(ctx, walkApply) - if err != nil { - t.Fatalf("unexpected error: %s", err.Error()) + diags := node.Execute(ctx, walkApply) + if diags.HasErrors() { + t.Fatalf("unexpected error: %s", diags.Err()) } if !state.HasResources() { t.Fatal("resources missing from state") diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_data_destroy.go b/vendor/github.com/hashicorp/terraform/terraform/node_data_destroy.go index 73137192..14c06516 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_data_destroy.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_data_destroy.go @@ -1,6 +1,10 @@ package terraform -import "log" +import ( + "log" + + "github.com/hashicorp/terraform/tfdiags" +) // NodeDestroyableDataResourceInstance represents a resource that is "destroyable": // it is ready to be destroyed. @@ -13,7 +17,7 @@ var ( ) // GraphNodeExecutable -func (n *NodeDestroyableDataResourceInstance) Execute(ctx EvalContext, op walkOperation) error { +func (n *NodeDestroyableDataResourceInstance) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics { log.Printf("[TRACE] NodeDestroyableDataResourceInstance: removing state object for %s", n.Addr) ctx.State().SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider) return nil diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_data_destroy_test.go b/vendor/github.com/hashicorp/terraform/terraform/node_data_destroy_test.go index 24fb988d..32d07b14 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_data_destroy_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_data_destroy_test.go @@ -36,9 +36,9 @@ func TestNodeDataDestroyExecute(t *testing.T) { }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), }} - err := node.Execute(ctx, walkApply) - if err != nil { - t.Fatalf("unexpected error: %s", err.Error()) + diags := node.Execute(ctx, walkApply) + if diags.HasErrors() { + t.Fatalf("unexpected error: %v", diags.Err()) } // verify resource removed from state diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_local.go b/vendor/github.com/hashicorp/terraform/terraform/node_local.go index 055492fe..eb04b395 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_local.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_local.go @@ -128,10 +128,7 @@ func (n *NodeLocal) References() []*addrs.Reference { // NodeLocal.Execute is an Execute implementation that evaluates the // expression for a local value and writes it into a transient part of // the state. -func (n *NodeLocal) Execute(ctx EvalContext, op walkOperation) error { - - var diags tfdiags.Diagnostics - +func (n *NodeLocal) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { expr := n.Config.Expr addr := n.Addr.LocalValue @@ -150,23 +147,24 @@ func (n *NodeLocal) Execute(ctx EvalContext, op walkOperation) error { } } if diags.HasErrors() { - return diags.Err() + return diags } val, moreDiags := ctx.EvaluateExpr(expr, cty.DynamicPseudoType, nil) diags = diags.Append(moreDiags) if moreDiags.HasErrors() { - return diags.Err() + return diags } state := ctx.State() if state == nil { - return fmt.Errorf("cannot write local value to nil state") + diags = diags.Append(fmt.Errorf("cannot write local value to nil state")) + return diags } state.SetLocalValue(addr.Absolute(ctx.Path()), val) - return nil + return diags } // dag.GraphNodeDotter impl. diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_module_expand.go b/vendor/github.com/hashicorp/terraform/terraform/node_module_expand.go index 1e27fd27..07ff4545 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_module_expand.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_module_expand.go @@ -99,7 +99,7 @@ func (n *nodeExpandModule) ReferenceOutside() (selfPath, referencePath addrs.Mod } // GraphNodeExecutable -func (n *nodeExpandModule) Execute(ctx EvalContext, op walkOperation) error { +func (n *nodeExpandModule) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { expander := ctx.InstanceExpander() _, call := n.Addr.Call() @@ -110,16 +110,18 @@ func (n *nodeExpandModule) Execute(ctx EvalContext, op walkOperation) error { ctx = ctx.WithPath(module) switch { case n.ModuleCall.Count != nil: - count, diags := evaluateCountExpression(n.ModuleCall.Count, ctx) + count, ctDiags := evaluateCountExpression(n.ModuleCall.Count, ctx) + diags = diags.Append(ctDiags) if diags.HasErrors() { - return diags.Err() + return diags } expander.SetModuleCount(module, call, count) case n.ModuleCall.ForEach != nil: - forEach, diags := evaluateForEachExpression(n.ModuleCall.ForEach, ctx) + forEach, feDiags := evaluateForEachExpression(n.ModuleCall.ForEach, ctx) + diags = diags.Append(feDiags) if diags.HasErrors() { - return diags.Err() + return diags } expander.SetModuleForEach(module, call, forEach) @@ -128,7 +130,7 @@ func (n *nodeExpandModule) Execute(ctx EvalContext, op walkOperation) error { } } - return nil + return diags } @@ -139,6 +141,8 @@ func (n *nodeExpandModule) Execute(ctx EvalContext, op walkOperation) error { // Besides providing a root node for dependency ordering, nodeCloseModule also // cleans up state after all the module nodes have been evaluated, removing // empty resources and modules from the state. +// The root module instance also closes any remaining provisioner plugins which +// do not have a lifecycle controlled by individual graph nodes. type nodeCloseModule struct { Addr addrs.Module } @@ -146,6 +150,7 @@ type nodeCloseModule struct { var ( _ GraphNodeReferenceable = (*nodeCloseModule)(nil) _ GraphNodeReferenceOutside = (*nodeCloseModule)(nil) + _ GraphNodeExecutable = (*nodeCloseModule)(nil) ) func (n *nodeCloseModule) ModulePath() addrs.Module { @@ -170,7 +175,13 @@ func (n *nodeCloseModule) Name() string { return n.Addr.String() + " (close)" } -func (n *nodeCloseModule) Execute(ctx EvalContext, op walkOperation) error { +func (n *nodeCloseModule) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { + if n.Addr.IsRoot() { + // If this is the root module, we are cleaning up the walk, so close + // any running provisioners + diags = diags.Append(ctx.CloseProvisioners()) + } + switch op { case walkApply, walkDestroy: state := ctx.State().Lock() @@ -206,10 +217,11 @@ type nodeValidateModule struct { nodeExpandModule } +var _ GraphNodeExecutable = (*nodeValidateModule)(nil) + // GraphNodeEvalable -func (n *nodeValidateModule) Execute(ctx EvalContext, op walkOperation) error { +func (n *nodeValidateModule) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { _, call := n.Addr.Call() - var diags tfdiags.Diagnostics expander := ctx.InstanceExpander() // Modules all evaluate to single instances during validation, only to @@ -228,7 +240,7 @@ func (n *nodeValidateModule) Execute(ctx EvalContext, op walkOperation) error { diags = diags.Append(countDiags) case n.ModuleCall.ForEach != nil: - _, forEachDiags := evaluateForEachExpressionValue(n.ModuleCall.ForEach, ctx) + _, forEachDiags := evaluateForEachExpressionValue(n.ModuleCall.ForEach, ctx, true) diags = diags.Append(forEachDiags) } @@ -238,9 +250,5 @@ func (n *nodeValidateModule) Execute(ctx EvalContext, op walkOperation) error { expander.SetModuleSingle(module, call) } - if diags.HasErrors() { - return diags.ErrWithWarnings() - } - - return nil + return diags } diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_module_expand_test.go b/vendor/github.com/hashicorp/terraform/terraform/node_module_expand_test.go index 578b4276..c83d1693 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_module_expand_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_module_expand_test.go @@ -42,9 +42,9 @@ func TestNodeCloseModuleExecute(t *testing.T) { StateState: state.SyncWrapper(), } node := nodeCloseModule{addrs.Module{"child"}} - err := node.Execute(ctx, walkApply) - if err != nil { - t.Fatalf("unexpected error: %s", err.Error()) + diags := node.Execute(ctx, walkApply) + if diags.HasErrors() { + t.Fatalf("unexpected error: %s", diags.Err()) } // Since module.child has no resources, it should be removed @@ -62,9 +62,9 @@ func TestNodeCloseModuleExecute(t *testing.T) { } node := nodeCloseModule{addrs.Module{"child"}} - err := node.Execute(ctx, walkImport) - if err != nil { - t.Fatalf("unexpected error: %s", err.Error()) + diags := node.Execute(ctx, walkImport) + if diags.HasErrors() { + t.Fatalf("unexpected error: %s", diags.Err()) } if _, ok := state.Modules["module.child"]; !ok { t.Fatal("module.child was removed from state, expected no-op") @@ -87,9 +87,9 @@ func TestNodeValidateModuleExecute(t *testing.T) { }, } - err := node.Execute(ctx, walkApply) - if err != nil { - t.Fatalf("unexpected error: %s", err.Error()) + diags := node.Execute(ctx, walkApply) + if diags.HasErrors() { + t.Fatalf("unexpected error: %v", diags.Err()) } }) diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_module_variable.go b/vendor/github.com/hashicorp/terraform/terraform/node_module_variable.go index 78ddb6e8..675242d7 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_module_variable.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_module_variable.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform/dag" "github.com/hashicorp/terraform/instances" "github.com/hashicorp/terraform/lang" + "github.com/hashicorp/terraform/tfdiags" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/convert" ) @@ -141,7 +142,7 @@ func (n *nodeModuleVariable) ModulePath() addrs.Module { } // GraphNodeExecutable -func (n *nodeModuleVariable) Execute(ctx EvalContext, op walkOperation) error { +func (n *nodeModuleVariable) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { // If we have no value, do nothing if n.Expr == nil { return nil @@ -154,14 +155,16 @@ func (n *nodeModuleVariable) Execute(ctx EvalContext, op walkOperation) error { switch op { case walkValidate: - vals, err = n.EvalModuleCallArgument(ctx, true) - if err != nil { - return err + vals, err = n.evalModuleCallArgument(ctx, true) + diags = diags.Append(err) + if diags.HasErrors() { + return diags } default: - vals, err = n.EvalModuleCallArgument(ctx, false) - if err != nil { - return err + vals, err = n.evalModuleCallArgument(ctx, false) + diags = diags.Append(err) + if diags.HasErrors() { + return diags } } @@ -184,7 +187,7 @@ func (n *nodeModuleVariable) DotNode(name string, opts *dag.DotOpts) *dag.DotNod } } -// EvalModuleCallArgument produces the value for a particular variable as will +// evalModuleCallArgument produces the value for a particular variable as will // be used by a child module instance. // // The result is written into a map, with its key set to the local name of the @@ -196,7 +199,7 @@ func (n *nodeModuleVariable) DotNode(name string, opts *dag.DotOpts) *dag.DotNod // validateOnly indicates that this evaluation is only for config // validation, and we will not have any expansion module instance // repetition data. -func (n *nodeModuleVariable) EvalModuleCallArgument(ctx EvalContext, validateOnly bool) (map[string]cty.Value, error) { +func (n *nodeModuleVariable) evalModuleCallArgument(ctx EvalContext, validateOnly bool) (map[string]cty.Value, error) { wantType := n.Config.Type name := n.Addr.Variable.Name expr := n.Expr diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_output.go b/vendor/github.com/hashicorp/terraform/terraform/node_output.go index 00f39c80..4db2055c 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_output.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_output.go @@ -246,11 +246,10 @@ func (n *NodeApplyableOutput) References() []*addrs.Reference { } // GraphNodeExecutable -func (n *NodeApplyableOutput) Execute(ctx EvalContext, op walkOperation) error { - var diags tfdiags.Diagnostics +func (n *NodeApplyableOutput) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { state := ctx.State() if state == nil { - return nil + return } changes := ctx.Changes() // may be nil, if we're not working on a changeset @@ -276,16 +275,24 @@ func (n *NodeApplyableOutput) Execute(ctx EvalContext, op walkOperation) error { // depends_on expressions here too diags = diags.Append(validateDependsOn(ctx, n.Config.DependsOn)) - // Ensure that non-sensitive outputs don't include sensitive values + // For root module outputs in particular, an output value must be + // statically declared as sensitive in order to dynamically return + // a sensitive result, to help avoid accidental exposure in the state + // of a sensitive value that the user doesn't want to include there. _, marks := val.UnmarkDeep() _, hasSensitive := marks["sensitive"] - if !n.Config.Sensitive && hasSensitive { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Output refers to sensitive values", - Detail: "Expressions used in outputs can only refer to sensitive values if the sensitive attribute is true.", - Subject: n.Config.DeclRange.Ptr(), - }) + if n.Addr.Module.IsRoot() { + if !n.Config.Sensitive && hasSensitive { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Output refers to sensitive values", + Detail: `To reduce the risk of accidentally exporting sensitive data that was intended to be only internal, Terraform requires that any root module output containing sensitive data be explicitly marked as sensitive, to confirm your intent. + +If you do intend to export this data, annotate the output value as sensitive by adding the following argument: + sensitive = true`, + Subject: n.Config.DeclRange.Ptr(), + }) + } } } @@ -297,9 +304,24 @@ func (n *NodeApplyableOutput) Execute(ctx EvalContext, op walkOperation) error { // marked as unknown. If the evaluator was able to find a type // for the value in spite of the error then we'll use it. n.setValue(state, changes, cty.UnknownVal(val.Type())) - return EvalEarlyExitError{} + + // Keep existing warnings, while converting errors to warnings. + // This is not meant to be the normal path, so there no need to + // make the errors pretty. + var warnings tfdiags.Diagnostics + for _, d := range diags { + switch d.Severity() { + case tfdiags.Warning: + warnings = warnings.Append(d) + case tfdiags.Error: + desc := d.Description() + warnings = warnings.Append(tfdiags.SimpleWarning(fmt.Sprintf("%s:%s", desc.Summary, desc.Detail))) + } + } + + return warnings } - return diags.Err() + return diags } n.setValue(state, changes, val) @@ -309,7 +331,7 @@ func (n *NodeApplyableOutput) Execute(ctx EvalContext, op walkOperation) error { n.setValue(state, changes, val) } - return nil + return diags } // dag.GraphNodeDotter impl. @@ -350,7 +372,7 @@ func (n *NodeDestroyableOutput) temporaryValue() bool { } // GraphNodeExecutable -func (n *NodeDestroyableOutput) Execute(ctx EvalContext, op walkOperation) error { +func (n *NodeDestroyableOutput) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics { state := ctx.State() if state == nil { return nil @@ -418,12 +440,17 @@ func (n *NodeApplyableOutput) setValue(state *states.SyncState, changes *plans.C // the diff sensitiveBefore := false before := cty.NullVal(cty.DynamicPseudoType) + + // is this output new to our state? + newOutput := true + mod := state.Module(n.Addr.Module) if n.Addr.Module.IsRoot() && mod != nil { for name, o := range mod.OutputValues { if name == n.Addr.OutputValue.Name { before = o.Value sensitiveBefore = o.Sensitive + newOutput = false break } } @@ -435,27 +462,27 @@ func (n *NodeApplyableOutput) setValue(state *states.SyncState, changes *plans.C sensitiveChange := sensitiveBefore || n.Config.Sensitive // strip any marks here just to be sure we don't panic on the True comparison - val, _ = val.UnmarkDeep() + unmarkedVal, _ := val.UnmarkDeep() - var action plans.Action + action := plans.Update switch { - case val.IsNull(): - action = plans.Delete + case val.IsNull() && before.IsNull(): + // This is separate from the NoOp case below, since we can ignore + // sensitivity here when there are only null values. + action = plans.NoOp - case before.IsNull(): + case newOutput: + // This output was just added to the configuration action = plans.Create case val.IsWhollyKnown() && - val.Equals(before).True() && + unmarkedVal.Equals(before).True() && n.Config.Sensitive == sensitiveBefore: // Sensitivity must also match to be a NoOp. // Theoretically marks may not match here, but sensitivity is the // only one we can act on, and the state will have been loaded // without any marks to consider. action = plans.NoOp - - default: - action = plans.Update } change := &plans.OutputChange{ @@ -473,7 +500,7 @@ func (n *NodeApplyableOutput) setValue(state *states.SyncState, changes *plans.C // Should never happen, since we just constructed this right above panic(fmt.Sprintf("planned change for %s could not be encoded: %s", n.Addr, err)) } - log.Printf("[TRACE] ExecuteWriteOutput: Saving %s change for %s in changeset", change.Action, n.Addr) + log.Printf("[TRACE] setValue: Saving %s change for %s in changeset", change.Action, n.Addr) changes.RemoveOutputChange(n.Addr) // remove any existing planned change, if present changes.AppendOutputChange(cs) // add the new planned change } @@ -482,12 +509,12 @@ func (n *NodeApplyableOutput) setValue(state *states.SyncState, changes *plans.C // The state itself doesn't represent unknown values, so we null them // out here and then we'll save the real unknown value in the planned // changeset below, if we have one on this graph walk. - log.Printf("[TRACE] EvalWriteOutput: Saving value for %s in state", n.Addr) + log.Printf("[TRACE] setValue: Saving value for %s in state", n.Addr) unmarkedVal, _ := val.UnmarkDeep() stateVal := cty.UnknownAsNull(unmarkedVal) state.SetOutputValue(n.Addr, stateVal, n.Config.Sensitive) } else { - log.Printf("[TRACE] EvalWriteOutput: Removing %s from state (it is now null)", n.Addr) + log.Printf("[TRACE] setValue: Removing %s from state (it is now null)", n.Addr) state.RemoveOutputValue(n.Addr) } diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_output_test.go b/vendor/github.com/hashicorp/terraform/terraform/node_output_test.go index 63fd3521..3b518f04 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_output_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_output_test.go @@ -81,11 +81,11 @@ func TestNodeApplyableOutputExecute_invalidDependsOn(t *testing.T) { }) ctx.EvaluateExprResult = val - err := node.Execute(ctx, walkApply) - if err == nil { + diags := node.Execute(ctx, walkApply) + if !diags.HasErrors() { t.Fatal("expected execute error, but there was none") } - if got, want := err.Error(), "Invalid depends_on reference"; !strings.Contains(got, want) { + if got, want := diags.Err().Error(), "Invalid depends_on reference"; !strings.Contains(got, want) { t.Errorf("expected error to include %q, but was: %s", want, got) } } @@ -102,11 +102,11 @@ func TestNodeApplyableOutputExecute_sensitiveValueNotOutput(t *testing.T) { }) ctx.EvaluateExprResult = val - err := node.Execute(ctx, walkApply) - if err == nil { + diags := node.Execute(ctx, walkApply) + if !diags.HasErrors() { t.Fatal("expected execute error, but there was none") } - if got, want := err.Error(), "Output refers to sensitive values"; !strings.Contains(got, want) { + if got, want := diags.Err().Error(), "Output refers to sensitive values"; !strings.Contains(got, want) { t.Errorf("expected error to include %q, but was: %s", want, got) } } @@ -151,9 +151,9 @@ func TestNodeDestroyableOutputExecute(t *testing.T) { } node := NodeDestroyableOutput{Addr: outputAddr} - err := node.Execute(ctx, walkApply) - if err != nil { - t.Fatalf("Unexpected error: %s", err.Error()) + diags := node.Execute(ctx, walkApply) + if diags.HasErrors() { + t.Fatalf("Unexpected error: %s", diags.Err()) } if state.OutputValue(outputAddr) != nil { t.Fatal("Unexpected outputs in state after removal") diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_provider.go b/vendor/github.com/hashicorp/terraform/terraform/node_provider.go index c901eb41..5b06a25a 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_provider.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_provider.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/tfdiags" + "github.com/zclconf/go-cty/cty" ) // NodeApplyableProvider represents a provider during an apply. @@ -20,39 +21,48 @@ var ( ) // GraphNodeExecutable -func (n *NodeApplyableProvider) Execute(ctx EvalContext, op walkOperation) error { +func (n *NodeApplyableProvider) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { _, err := ctx.InitProvider(n.Addr) - if err != nil { - return err + diags = diags.Append(err) + if diags.HasErrors() { + return diags } - provider, _, err := GetProvider(ctx, n.Addr) - if err != nil { - return err + provider, _, err := getProvider(ctx, n.Addr) + diags = diags.Append(err) + if diags.HasErrors() { + return diags } switch op { case walkValidate: - return n.ValidateProvider(ctx, provider) + return diags.Append(n.ValidateProvider(ctx, provider)) case walkPlan, walkApply, walkDestroy: - return n.ConfigureProvider(ctx, provider, false) + return diags.Append(n.ConfigureProvider(ctx, provider, false)) case walkImport: - return n.ConfigureProvider(ctx, provider, true) + return diags.Append(n.ConfigureProvider(ctx, provider, true)) } - return nil + return diags } -func (n *NodeApplyableProvider) ValidateProvider(ctx EvalContext, provider providers.Interface) error { - var diags tfdiags.Diagnostics +func (n *NodeApplyableProvider) ValidateProvider(ctx EvalContext, provider providers.Interface) (diags tfdiags.Diagnostics) { configBody := buildProviderConfig(ctx, n.Addr, n.ProviderConfig()) - resp := provider.GetSchema() - diags = diags.Append(resp.Diagnostics) + // if a provider config is empty (only an alias), return early and don't continue + // validation. validate doesn't need to fully configure the provider itself, so + // skipping a provider with an implied configuration won't prevent other validation from completing. + _, noConfigDiags := configBody.Content(&hcl.BodySchema{}) + if !noConfigDiags.HasErrors() { + return nil + } + + schemaResp := provider.GetProviderSchema() + diags = diags.Append(schemaResp.Diagnostics.InConfigBody(configBody, n.Addr.String())) if diags.HasErrors() { - return diags.ErrWithWarnings() + return diags } - configSchema := resp.Provider.Block + configSchema := schemaResp.Provider.Block if configSchema == nil { // Should never happen in real code, but often comes up in tests where // mock schemas are being used that tend to be incomplete. @@ -60,42 +70,45 @@ func (n *NodeApplyableProvider) ValidateProvider(ctx EvalContext, provider provi configSchema = &configschema.Block{} } - configVal, configBody, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, EvalDataForNoInstanceKey) - diags = diags.Append(evalDiags) + configVal, _, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, EvalDataForNoInstanceKey) if evalDiags.HasErrors() { - return diags.ErrWithWarnings() + return diags.Append(evalDiags) } + diags = diags.Append(evalDiags) - req := providers.PrepareProviderConfigRequest{ - Config: configVal, + // If our config value contains any marked values, ensure those are + // stripped out before sending this to the provider + unmarkedConfigVal, _ := configVal.UnmarkDeep() + + req := providers.ValidateProviderConfigRequest{ + Config: unmarkedConfigVal, } - validateResp := provider.PrepareProviderConfig(req) - diags = diags.Append(validateResp.Diagnostics) + validateResp := provider.ValidateProviderConfig(req) + diags = diags.Append(validateResp.Diagnostics.InConfigBody(configBody, n.Addr.String())) - return diags.ErrWithWarnings() + return diags } // ConfigureProvider configures a provider that is already initialized and retrieved. // If verifyConfigIsKnown is true, ConfigureProvider will return an error if the // provider configVal is not wholly known and is meant only for use during import. -func (n *NodeApplyableProvider) ConfigureProvider(ctx EvalContext, provider providers.Interface, verifyConfigIsKnown bool) error { - var diags tfdiags.Diagnostics +func (n *NodeApplyableProvider) ConfigureProvider(ctx EvalContext, provider providers.Interface, verifyConfigIsKnown bool) (diags tfdiags.Diagnostics) { config := n.ProviderConfig() configBody := buildProviderConfig(ctx, n.Addr, config) - resp := provider.GetSchema() - diags = diags.Append(resp.Diagnostics) + resp := provider.GetProviderSchema() + diags = diags.Append(resp.Diagnostics.InConfigBody(configBody, n.Addr.String())) if diags.HasErrors() { - return diags.ErrWithWarnings() + return diags } configSchema := resp.Provider.Block configVal, configBody, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, EvalDataForNoInstanceKey) diags = diags.Append(evalDiags) if evalDiags.HasErrors() { - return diags.ErrWithWarnings() + return diags } if verifyConfigIsKnown && !configVal.IsWhollyKnown() { @@ -105,11 +118,59 @@ func (n *NodeApplyableProvider) ConfigureProvider(ctx EvalContext, provider prov Detail: fmt.Sprintf("The configuration for %s depends on values that cannot be determined until apply.", n.Addr), Subject: &config.DeclRange, }) - return diags.ErrWithWarnings() + return diags + } + + // If our config value contains any marked values, ensure those are + // stripped out before sending this to the provider + unmarkedConfigVal, _ := configVal.UnmarkDeep() + + // Allow the provider to validate and insert any defaults into the full + // configuration. + req := providers.ValidateProviderConfigRequest{ + Config: unmarkedConfigVal, + } + + // ValidateProviderConfig is only used for validation. We are intentionally + // ignoring the PreparedConfig field to maintain existing behavior. + validateResp := provider.ValidateProviderConfig(req) + diags = diags.Append(validateResp.Diagnostics.InConfigBody(configBody, n.Addr.String())) + if diags.HasErrors() && config == nil { + // If there isn't an explicit "provider" block in the configuration, + // this error message won't be very clear. Add some detail to the error + // message in this case. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Invalid provider configuration", + fmt.Sprintf(providerConfigErr, n.Addr.Provider), + )) + } + + if diags.HasErrors() { + return diags } - configDiags := ctx.ConfigureProvider(n.Addr, configVal) - configDiags = configDiags.InConfigBody(configBody) + // If the provider returns something different, log a warning to help + // indicate to provider developers that the value is not used. + preparedCfg := validateResp.PreparedConfig + if preparedCfg != cty.NilVal && !preparedCfg.IsNull() && !preparedCfg.RawEquals(unmarkedConfigVal) { + log.Printf("[WARN] ValidateProviderConfig from %q changed the config value, but that value is unused", n.Addr) + } - return configDiags.ErrWithWarnings() + configDiags := ctx.ConfigureProvider(n.Addr, unmarkedConfigVal) + diags = diags.Append(configDiags.InConfigBody(configBody, n.Addr.String())) + if diags.HasErrors() && config == nil { + // If there isn't an explicit "provider" block in the configuration, + // this error message won't be very clear. Add some detail to the error + // message in this case. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Invalid provider configuration", + fmt.Sprintf(providerConfigErr, n.Addr.Provider), + )) + } + return diags } + +const providerConfigErr = `Provider %q requires explicit configuration. Add a provider block to the root module and configure the provider's required arguments as described in the provider documentation. +` diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_provider_eval.go b/vendor/github.com/hashicorp/terraform/terraform/node_provider_eval.go index aa6ba2c5..a89583cf 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_provider_eval.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_provider_eval.go @@ -1,5 +1,7 @@ package terraform +import "github.com/hashicorp/terraform/tfdiags" + // NodeEvalableProvider represents a provider during an "eval" walk. // This special provider node type just initializes a provider and // fetches its schema, without configuring it or otherwise interacting @@ -8,8 +10,10 @@ type NodeEvalableProvider struct { *NodeAbstractProvider } +var _ GraphNodeExecutable = (*NodeEvalableProvider)(nil) + // GraphNodeExecutable -func (n *NodeEvalableProvider) Execute(ctx EvalContext, op walkOperation) error { +func (n *NodeEvalableProvider) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { _, err := ctx.InitProvider(n.Addr) - return err + return diags.Append(err) } diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_provider_test.go b/vendor/github.com/hashicorp/terraform/terraform/node_provider_test.go index 5c418bde..4eafe49f 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_provider_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_provider_test.go @@ -1,10 +1,16 @@ package terraform import ( + "fmt" + "strings" "testing" + "github.com/hashicorp/hcl/v2" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/tfdiags" "github.com/zclconf/go-cty/cty" ) @@ -65,13 +71,13 @@ func TestNodeApplyableProviderExecute_unknownImport(t *testing.T) { ctx := &MockEvalContext{ProviderProvider: provider} ctx.installSimpleEval() - err := n.Execute(ctx, walkImport) - if err == nil { + diags := n.Execute(ctx, walkImport) + if !diags.HasErrors() { t.Fatal("expected error, got success") } detail := `Invalid provider configuration: The configuration for provider["registry.terraform.io/hashicorp/foo"] depends on values that cannot be determined until apply.` - if got, want := err.Error(), detail; got != want { + if got, want := diags.Err().Error(), detail; got != want { t.Errorf("wrong diagnostic detail\n got: %q\nwant: %q", got, want) } @@ -115,3 +121,379 @@ func TestNodeApplyableProviderExecute_unknownApply(t *testing.T) { t.Errorf("wrong configuration value\ngot: %#v\nwant: %#v", got, want) } } + +func TestNodeApplyableProviderExecute_sensitive(t *testing.T) { + config := &configs.Provider{ + Name: "foo", + Config: configs.SynthBody("", map[string]cty.Value{ + "test_string": cty.StringVal("hello").Mark("sensitive"), + }), + } + provider := mockProviderWithConfigSchema(simpleTestSchema()) + providerAddr := addrs.AbsProviderConfig{ + Module: addrs.RootModule, + Provider: addrs.NewDefaultProvider("foo"), + } + + n := &NodeApplyableProvider{&NodeAbstractProvider{ + Addr: providerAddr, + Config: config, + }} + + ctx := &MockEvalContext{ProviderProvider: provider} + ctx.installSimpleEval() + if err := n.Execute(ctx, walkApply); err != nil { + t.Fatalf("err: %s", err) + } + + if !ctx.ConfigureProviderCalled { + t.Fatal("should be called") + } + + gotObj := ctx.ConfigureProviderConfig + if !gotObj.Type().HasAttribute("test_string") { + t.Fatal("configuration object does not have \"test_string\" attribute") + } + if got, want := gotObj.GetAttr("test_string"), cty.StringVal("hello"); !got.RawEquals(want) { + t.Errorf("wrong configuration value\ngot: %#v\nwant: %#v", got, want) + } +} + +func TestNodeApplyableProviderExecute_sensitiveValidate(t *testing.T) { + config := &configs.Provider{ + Name: "foo", + Config: configs.SynthBody("", map[string]cty.Value{ + "test_string": cty.StringVal("hello").Mark("sensitive"), + }), + } + provider := mockProviderWithConfigSchema(simpleTestSchema()) + providerAddr := addrs.AbsProviderConfig{ + Module: addrs.RootModule, + Provider: addrs.NewDefaultProvider("foo"), + } + + n := &NodeApplyableProvider{&NodeAbstractProvider{ + Addr: providerAddr, + Config: config, + }} + + ctx := &MockEvalContext{ProviderProvider: provider} + ctx.installSimpleEval() + if err := n.Execute(ctx, walkValidate); err != nil { + t.Fatalf("err: %s", err) + } + + if !provider.ValidateProviderConfigCalled { + t.Fatal("should be called") + } + + gotObj := provider.ValidateProviderConfigRequest.Config + if !gotObj.Type().HasAttribute("test_string") { + t.Fatal("configuration object does not have \"test_string\" attribute") + } + if got, want := gotObj.GetAttr("test_string"), cty.StringVal("hello"); !got.RawEquals(want) { + t.Errorf("wrong configuration value\ngot: %#v\nwant: %#v", got, want) + } +} + +func TestNodeApplyableProviderExecute_emptyValidate(t *testing.T) { + config := &configs.Provider{ + Name: "foo", + Config: configs.SynthBody("", map[string]cty.Value{}), + } + provider := mockProviderWithConfigSchema(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "test_string": { + Type: cty.String, + Required: true, + }, + }, + }) + providerAddr := addrs.AbsProviderConfig{ + Module: addrs.RootModule, + Provider: addrs.NewDefaultProvider("foo"), + } + + n := &NodeApplyableProvider{&NodeAbstractProvider{ + Addr: providerAddr, + Config: config, + }} + + ctx := &MockEvalContext{ProviderProvider: provider} + ctx.installSimpleEval() + if err := n.Execute(ctx, walkValidate); err != nil { + t.Fatalf("err: %s", err) + } + + if ctx.ConfigureProviderCalled { + t.Fatal("should not be called") + } +} + +func TestNodeApplyableProvider_Validate(t *testing.T) { + provider := mockProviderWithConfigSchema(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "region": { + Type: cty.String, + Required: true, + }, + }, + }) + ctx := &MockEvalContext{ProviderProvider: provider} + ctx.installSimpleEval() + + t.Run("valid", func(t *testing.T) { + config := &configs.Provider{ + Name: "test", + Config: configs.SynthBody("", map[string]cty.Value{ + "region": cty.StringVal("mars"), + }), + } + + node := NodeApplyableProvider{ + NodeAbstractProvider: &NodeAbstractProvider{ + Addr: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + Config: config, + }, + } + + diags := node.ValidateProvider(ctx, provider) + if diags.HasErrors() { + t.Errorf("unexpected error with valid config: %s", diags.Err()) + } + }) + + t.Run("invalid", func(t *testing.T) { + config := &configs.Provider{ + Name: "test", + Config: configs.SynthBody("", map[string]cty.Value{ + "region": cty.MapValEmpty(cty.String), + }), + } + + node := NodeApplyableProvider{ + NodeAbstractProvider: &NodeAbstractProvider{ + Addr: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + Config: config, + }, + } + + diags := node.ValidateProvider(ctx, provider) + if !diags.HasErrors() { + t.Error("missing expected error with invalid config") + } + }) + + t.Run("empty config", func(t *testing.T) { + node := NodeApplyableProvider{ + NodeAbstractProvider: &NodeAbstractProvider{ + Addr: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + }, + } + + diags := node.ValidateProvider(ctx, provider) + if diags.HasErrors() { + t.Errorf("unexpected error with empty config: %s", diags.Err()) + } + }) +} + +//This test specifically tests responses from the +//providers.ValidateProviderConfigFn. See +//TestNodeApplyableProvider_ConfigProvider_config_fn_err for +//providers.ConfigureProviderRequest responses. +func TestNodeApplyableProvider_ConfigProvider(t *testing.T) { + provider := mockProviderWithConfigSchema(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "region": { + Type: cty.String, + Optional: true, + }, + }, + }) + // For this test, we're returning an error for an optional argument. This + // can happen for example if an argument is only conditionally required. + provider.ValidateProviderConfigFn = func(req providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) { + region := req.Config.GetAttr("region") + if region.IsNull() { + resp.Diagnostics = resp.Diagnostics.Append( + tfdiags.WholeContainingBody(tfdiags.Error, "value is not found", "you did not supply a required value")) + } + return + } + ctx := &MockEvalContext{ProviderProvider: provider} + ctx.installSimpleEval() + + t.Run("valid", func(t *testing.T) { + config := &configs.Provider{ + Name: "test", + Config: configs.SynthBody("", map[string]cty.Value{ + "region": cty.StringVal("mars"), + }), + } + + node := NodeApplyableProvider{ + NodeAbstractProvider: &NodeAbstractProvider{ + Addr: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + Config: config, + }, + } + + diags := node.ConfigureProvider(ctx, provider, false) + if diags.HasErrors() { + t.Errorf("unexpected error with valid config: %s", diags.Err()) + } + }) + + t.Run("missing required config (no config at all)", func(t *testing.T) { + node := NodeApplyableProvider{ + NodeAbstractProvider: &NodeAbstractProvider{ + Addr: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + }, + } + + diags := node.ConfigureProvider(ctx, provider, false) + if !diags.HasErrors() { + t.Fatal("missing expected error with nil config") + } + if !strings.Contains(diags.Err().Error(), "requires explicit configuration") { + t.Errorf("diagnostic is missing \"requires explicit configuration\" message: %s", diags.Err()) + } + }) + + t.Run("missing required config", func(t *testing.T) { + config := &configs.Provider{ + Name: "test", + Config: hcl.EmptyBody(), + } + node := NodeApplyableProvider{ + NodeAbstractProvider: &NodeAbstractProvider{ + Addr: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + Config: config, + }, + } + + diags := node.ConfigureProvider(ctx, provider, false) + if !diags.HasErrors() { + t.Fatal("missing expected error with invalid config") + } + if !strings.Contains(diags.Err().Error(), "value is not found") { + t.Errorf("wrong diagnostic: %s", diags.Err()) + } + }) + +} + +//This test is similar to TestNodeApplyableProvider_ConfigProvider, but tests responses from the providers.ConfigureProviderRequest +func TestNodeApplyableProvider_ConfigProvider_config_fn_err(t *testing.T) { + provider := mockProviderWithConfigSchema(&configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "region": { + Type: cty.String, + Optional: true, + }, + }, + }) + ctx := &MockEvalContext{ProviderProvider: provider} + ctx.installSimpleEval() + // For this test, provider.PrepareConfigFn will succeed every time but the + // ctx.ConfigureProviderFn will return an error if a value is not found. + // + // This is an unlikely but real situation that occurs: + // https://github.com/hashicorp/terraform/issues/23087 + ctx.ConfigureProviderFn = func(addr addrs.AbsProviderConfig, cfg cty.Value) (diags tfdiags.Diagnostics) { + if cfg.IsNull() { + diags = diags.Append(fmt.Errorf("no config provided")) + } else { + region := cfg.GetAttr("region") + if region.IsNull() { + diags = diags.Append(fmt.Errorf("value is not found")) + } + } + return + } + + t.Run("valid", func(t *testing.T) { + config := &configs.Provider{ + Name: "test", + Config: configs.SynthBody("", map[string]cty.Value{ + "region": cty.StringVal("mars"), + }), + } + + node := NodeApplyableProvider{ + NodeAbstractProvider: &NodeAbstractProvider{ + Addr: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + Config: config, + }, + } + + diags := node.ConfigureProvider(ctx, provider, false) + if diags.HasErrors() { + t.Errorf("unexpected error with valid config: %s", diags.Err()) + } + }) + + t.Run("missing required config (no config at all)", func(t *testing.T) { + node := NodeApplyableProvider{ + NodeAbstractProvider: &NodeAbstractProvider{ + Addr: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + }, + } + + diags := node.ConfigureProvider(ctx, provider, false) + if !diags.HasErrors() { + t.Fatal("missing expected error with nil config") + } + if !strings.Contains(diags.Err().Error(), "requires explicit configuration") { + t.Errorf("diagnostic is missing \"requires explicit configuration\" message: %s", diags.Err()) + } + }) + + t.Run("missing required config", func(t *testing.T) { + config := &configs.Provider{ + Name: "test", + Config: hcl.EmptyBody(), + } + node := NodeApplyableProvider{ + NodeAbstractProvider: &NodeAbstractProvider{ + Addr: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + Config: config, + }, + } + + diags := node.ConfigureProvider(ctx, provider, false) + if !diags.HasErrors() { + t.Fatal("missing expected error with invalid config") + } + if diags.Err().Error() != "value is not found" { + t.Errorf("wrong diagnostic: %s", diags.Err()) + } + }) +} + +func TestGetSchemaError(t *testing.T) { + provider := &MockProvider{ + GetProviderSchemaResponse: &providers.GetProviderSchemaResponse{ + Diagnostics: tfdiags.Diagnostics.Append(nil, tfdiags.WholeContainingBody(tfdiags.Error, "oops", "error")), + }, + } + + providerAddr := mustProviderConfig(`provider["terraform.io/some/provider"]`) + ctx := &MockEvalContext{ProviderProvider: provider} + ctx.installSimpleEval() + node := NodeApplyableProvider{ + NodeAbstractProvider: &NodeAbstractProvider{ + Addr: providerAddr, + }, + } + + diags := node.ConfigureProvider(ctx, provider, false) + for _, d := range diags { + desc := d.Description() + if desc.Address != providerAddr.String() { + t.Fatalf("missing provider address from diagnostics: %#v", desc) + } + } + +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_provisioner.go b/vendor/github.com/hashicorp/terraform/terraform/node_provisioner.go deleted file mode 100644 index c4df9c62..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/node_provisioner.go +++ /dev/null @@ -1,44 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/hashicorp/terraform/addrs" -) - -// NodeProvisioner represents a provider that has no associated operations. -// It registers all the common interfaces across operations for providers. -type NodeProvisioner struct { - NameValue string - PathValue addrs.ModuleInstance -} - -var ( - _ GraphNodeModuleInstance = (*NodeProvisioner)(nil) - _ GraphNodeProvisioner = (*NodeProvisioner)(nil) - _ GraphNodeExecutable = (*NodeProvisioner)(nil) -) - -func (n *NodeProvisioner) Name() string { - result := fmt.Sprintf("provisioner.%s", n.NameValue) - if len(n.PathValue) > 0 { - result = fmt.Sprintf("%s.%s", n.PathValue.String(), result) - } - - return result -} - -// GraphNodeModuleInstance -func (n *NodeProvisioner) Path() addrs.ModuleInstance { - return n.PathValue -} - -// GraphNodeProvisioner -func (n *NodeProvisioner) ProvisionerName() string { - return n.NameValue -} - -// GraphNodeExecutable impl. -func (n *NodeProvisioner) Execute(ctx EvalContext, op walkOperation) error { - return ctx.InitProvisioner(n.NameValue) -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract.go index 4606d518..c03103b9 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract.go @@ -62,7 +62,7 @@ type NodeAbstractResource struct { // Set from GraphNodeTargetable Targets []addrs.Targetable - // Set from AttachResourceDependencies + // Set from AttachDataResourceDependsOn dependsOn []addrs.ConfigResource forceDependsOn bool @@ -71,18 +71,18 @@ type NodeAbstractResource struct { } var ( - _ GraphNodeReferenceable = (*NodeAbstractResource)(nil) - _ GraphNodeReferencer = (*NodeAbstractResource)(nil) - _ GraphNodeProviderConsumer = (*NodeAbstractResource)(nil) - _ GraphNodeProvisionerConsumer = (*NodeAbstractResource)(nil) - _ GraphNodeConfigResource = (*NodeAbstractResource)(nil) - _ GraphNodeAttachResourceConfig = (*NodeAbstractResource)(nil) - _ GraphNodeAttachResourceSchema = (*NodeAbstractResource)(nil) - _ GraphNodeAttachProvisionerSchema = (*NodeAbstractResource)(nil) - _ GraphNodeAttachProviderMetaConfigs = (*NodeAbstractResource)(nil) - _ GraphNodeTargetable = (*NodeAbstractResource)(nil) - _ graphNodeAttachResourceDependencies = (*NodeAbstractResource)(nil) - _ dag.GraphNodeDotter = (*NodeAbstractResource)(nil) + _ GraphNodeReferenceable = (*NodeAbstractResource)(nil) + _ GraphNodeReferencer = (*NodeAbstractResource)(nil) + _ GraphNodeProviderConsumer = (*NodeAbstractResource)(nil) + _ GraphNodeProvisionerConsumer = (*NodeAbstractResource)(nil) + _ GraphNodeConfigResource = (*NodeAbstractResource)(nil) + _ GraphNodeAttachResourceConfig = (*NodeAbstractResource)(nil) + _ GraphNodeAttachResourceSchema = (*NodeAbstractResource)(nil) + _ GraphNodeAttachProvisionerSchema = (*NodeAbstractResource)(nil) + _ GraphNodeAttachProviderMetaConfigs = (*NodeAbstractResource)(nil) + _ GraphNodeTargetable = (*NodeAbstractResource)(nil) + _ graphNodeAttachDataResourceDependsOn = (*NodeAbstractResource)(nil) + _ dag.GraphNodeDotter = (*NodeAbstractResource)(nil) ) // NewNodeAbstractResource creates an abstract resource graph node for @@ -264,8 +264,8 @@ func (n *NodeAbstractResource) SetTargets(targets []addrs.Targetable) { n.Targets = targets } -// graphNodeAttachResourceDependencies -func (n *NodeAbstractResource) AttachResourceDependencies(deps []addrs.ConfigResource, force bool) { +// graphNodeAttachDataResourceDependsOn +func (n *NodeAbstractResource) AttachDataResourceDependsOn(deps []addrs.ConfigResource, force bool) { n.dependsOn = deps n.forceDependsOn = force } @@ -305,8 +305,7 @@ func (n *NodeAbstractResource) DotNode(name string, opts *dag.DotOpts) *dag.DotN // eval is the only change we get to set the resource "each mode" to list // in that case, allowing expression evaluation to see it as a zero-element list // rather than as not set at all. -func (n *NodeAbstractResource) writeResourceState(ctx EvalContext, addr addrs.AbsResource) error { - var diags tfdiags.Diagnostics +func (n *NodeAbstractResource) writeResourceState(ctx EvalContext, addr addrs.AbsResource) (diags tfdiags.Diagnostics) { state := ctx.State() // We'll record our expansion decision in the shared "expander" object @@ -320,7 +319,7 @@ func (n *NodeAbstractResource) writeResourceState(ctx EvalContext, addr addrs.Ab count, countDiags := evaluateCountExpression(n.Config.Count, ctx) diags = diags.Append(countDiags) if countDiags.HasErrors() { - return diags.Err() + return diags } state.SetResourceProvider(addr, n.ResolvedProvider) @@ -330,7 +329,7 @@ func (n *NodeAbstractResource) writeResourceState(ctx EvalContext, addr addrs.Ab forEach, forEachDiags := evaluateForEachExpression(n.Config.ForEach, ctx) diags = diags.Append(forEachDiags) if forEachDiags.HasErrors() { - return diags.Err() + return diags } // This method takes care of all of the business logic of updating this @@ -343,51 +342,103 @@ func (n *NodeAbstractResource) writeResourceState(ctx EvalContext, addr addrs.Ab expander.SetResourceSingle(addr.Module, n.Addr.Resource) } - return nil + return diags } -// ReadResourceInstanceState reads the current object for a specific instance in +// readResourceInstanceState reads the current object for a specific instance in // the state. -func (n *NodeAbstractResource) ReadResourceInstanceState(ctx EvalContext, addr addrs.AbsResourceInstance) (*states.ResourceInstanceObject, error) { - provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider) - - if provider == nil { - panic("ReadResourceInstanceState used with no Provider object") - } - if providerSchema == nil { - panic("ReadResourceInstanceState used with no ProviderSchema object") +func (n *NodeAbstractResource) readResourceInstanceState(ctx EvalContext, addr addrs.AbsResourceInstance) (*states.ResourceInstanceObject, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + if err != nil { + diags = diags.Append(err) + return nil, diags } - log.Printf("[TRACE] ReadResourceInstanceState: reading state for %s", addr) + log.Printf("[TRACE] readResourceInstanceState: reading state for %s", addr) src := ctx.State().ResourceInstanceObject(addr, states.CurrentGen) if src == nil { // Presumably we only have deposed objects, then. - log.Printf("[TRACE] ReadResourceInstanceState: no state present for %s", addr) + log.Printf("[TRACE] readResourceInstanceState: no state present for %s", addr) return nil, nil } schema, currentVersion := (providerSchema).SchemaForResourceAddr(addr.Resource.ContainingResource()) if schema == nil { // Shouldn't happen since we should've failed long ago if no schema is present - return nil, fmt.Errorf("no schema available for %s while reading state; this is a bug in Terraform and should be reported", addr) + return nil, diags.Append(fmt.Errorf("no schema available for %s while reading state; this is a bug in Terraform and should be reported", addr)) + } + src, upgradeDiags := upgradeResourceState(addr, provider, src, schema, currentVersion) + if n.Config != nil { + upgradeDiags = upgradeDiags.InConfigBody(n.Config.Config, addr.String()) + } + diags = diags.Append(upgradeDiags) + if diags.HasErrors() { + // Note that we don't have any channel to return warnings here. We'll + // accept that for now since warnings during a schema upgrade would + // be pretty weird anyway, since this operation is supposed to seem + // invisible to the user. + return nil, diags + } + + obj, err := src.Decode(schema.ImpliedType()) + if err != nil { + diags = diags.Append(err) } + + return obj, diags +} + +// readResourceInstanceStateDeposed reads the deposed object for a specific +// instance in the state. +func (n *NodeAbstractResource) readResourceInstanceStateDeposed(ctx EvalContext, addr addrs.AbsResourceInstance, key states.DeposedKey) (*states.ResourceInstanceObject, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics - src, diags = UpgradeResourceState(addr, provider, src, schema, currentVersion) + provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + if err != nil { + diags = diags.Append(err) + return nil, diags + } + + if key == states.NotDeposed { + return nil, diags.Append(fmt.Errorf("readResourceInstanceStateDeposed used with no instance key; this is a bug in Terraform and should be reported")) + } + + log.Printf("[TRACE] readResourceInstanceStateDeposed: reading state for %s deposed object %s", addr, key) + + src := ctx.State().ResourceInstanceObject(addr, key) + if src == nil { + // Presumably we only have deposed objects, then. + log.Printf("[TRACE] readResourceInstanceStateDeposed: no state present for %s deposed object %s", addr, key) + return nil, diags + } + + schema, currentVersion := (providerSchema).SchemaForResourceAddr(addr.Resource.ContainingResource()) + if schema == nil { + // Shouldn't happen since we should've failed long ago if no schema is present + return nil, diags.Append(fmt.Errorf("no schema available for %s while reading state; this is a bug in Terraform and should be reported", addr)) + + } + + src, upgradeDiags := upgradeResourceState(addr, provider, src, schema, currentVersion) + if n.Config != nil { + upgradeDiags = upgradeDiags.InConfigBody(n.Config.Config, addr.String()) + } + diags = diags.Append(upgradeDiags) if diags.HasErrors() { // Note that we don't have any channel to return warnings here. We'll // accept that for now since warnings during a schema upgrade would // be pretty weird anyway, since this operation is supposed to seem // invisible to the user. - return nil, diags.Err() + return nil, diags } obj, err := src.Decode(schema.ImpliedType()) if err != nil { - return nil, err + diags = diags.Append(err) } - return obj, nil + return obj, diags } // graphNodesAreResourceInstancesInDifferentInstancesOfSameModule is an diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract_instance.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract_instance.go index 4739679c..5ecc709b 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract_instance.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract_instance.go @@ -3,12 +3,19 @@ package terraform import ( "fmt" "log" + "strings" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/configs" + "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/plans/objchange" + "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/provisioners" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/tfdiags" + "github.com/zclconf/go-cty/cty" ) // NodeAbstractResourceInstance represents a resource instance with no @@ -162,7 +169,7 @@ func (n *NodeAbstractResourceInstance) readDiff(ctx EvalContext, providerSchema gen := states.CurrentGen csrc := changes.GetResourceInstanceChange(addr, gen) if csrc == nil { - log.Printf("[TRACE] EvalReadDiff: No planned change recorded for %s", n.Addr) + log.Printf("[TRACE] readDiff: No planned change recorded for %s", n.Addr) return nil, nil } @@ -171,7 +178,7 @@ func (n *NodeAbstractResourceInstance) readDiff(ctx EvalContext, providerSchema return nil, fmt.Errorf("failed to decode planned changes for %s: %s", n.Addr, err) } - log.Printf("[TRACE] EvalReadDiff: Read %s change from plan for %s", change.Action, n.Addr) + log.Printf("[TRACE] readDiff: Read %s change from plan for %s", change.Action, n.Addr) return change, nil } @@ -199,3 +206,1990 @@ func (n *NodeAbstractResourceInstance) checkPreventDestroy(change *plans.Resourc return nil } + +// preApplyHook calls the pre-Apply hook +func (n *NodeAbstractResourceInstance) preApplyHook(ctx EvalContext, change *plans.ResourceInstanceChange) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + if change == nil { + panic(fmt.Sprintf("preApplyHook for %s called with nil Change", n.Addr)) + } + + // Only managed resources have user-visible apply actions. + if n.Addr.Resource.Resource.Mode == addrs.ManagedResourceMode { + priorState := change.Before + plannedNewState := change.After + + diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) { + return h.PreApply(n.Addr, change.DeposedKey.Generation(), change.Action, priorState, plannedNewState) + })) + if diags.HasErrors() { + return diags + } + } + + return nil +} + +// postApplyHook calls the post-Apply hook +func (n *NodeAbstractResourceInstance) postApplyHook(ctx EvalContext, state *states.ResourceInstanceObject, err error) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + // Only managed resources have user-visible apply actions. + if n.Addr.Resource.Resource.Mode == addrs.ManagedResourceMode { + var newState cty.Value + if state != nil { + newState = state.Value + } else { + newState = cty.NullVal(cty.DynamicPseudoType) + } + diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) { + return h.PostApply(n.Addr, nil, newState, err) + })) + } + + return diags +} + +type phaseState int + +const ( + workingState phaseState = iota + refreshState + prevRunState +) + +//go:generate go run golang.org/x/tools/cmd/stringer -type phaseState + +// writeResourceInstanceState saves the given object as the current object for +// the selected resource instance. +// +// dependencies is a parameter, instead of those directly attacted to the +// NodeAbstractResourceInstance, because we don't write dependencies for +// datasources. +// +// targetState determines which context state we're writing to during plan. The +// default is the global working state. +func (n *NodeAbstractResourceInstance) writeResourceInstanceState(ctx EvalContext, obj *states.ResourceInstanceObject, targetState phaseState) error { + absAddr := n.Addr + _, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + if err != nil { + return err + } + + var state *states.SyncState + switch targetState { + case workingState: + state = ctx.State() + case refreshState: + log.Printf("[TRACE] writeResourceInstanceState: using RefreshState for %s", absAddr) + state = ctx.RefreshState() + case prevRunState: + state = ctx.PrevRunState() + default: + panic(fmt.Sprintf("unsupported phaseState value %#v", targetState)) + } + if state == nil { + // Should not happen, because we shouldn't ever try to write to + // a state that isn't applicable to the current operation. + // (We can also get in here for unit tests which are using + // EvalContextMock but not populating PrevRunStateState with + // a suitable state object.) + return fmt.Errorf("state of type %s is not applicable to the current operation; this is a bug in Terraform", targetState) + } + + if obj == nil || obj.Value.IsNull() { + // No need to encode anything: we'll just write it directly. + state.SetResourceInstanceCurrent(absAddr, nil, n.ResolvedProvider) + log.Printf("[TRACE] writeResourceInstanceState: removing state object for %s", absAddr) + return nil + } + + if providerSchema == nil { + // Should never happen, unless our state object is nil + panic("writeResourceInstanceState used with nil ProviderSchema") + } + + if obj != nil { + log.Printf("[TRACE] writeResourceInstanceState: writing current state object for %s", absAddr) + } else { + log.Printf("[TRACE] writeResourceInstanceState: removing current state object for %s", absAddr) + } + + schema, currentVersion := (*providerSchema).SchemaForResourceAddr(absAddr.ContainingResource().Resource) + if schema == nil { + // It shouldn't be possible to get this far in any real scenario + // without a schema, but we might end up here in contrived tests that + // fail to set up their world properly. + return fmt.Errorf("failed to encode %s in state: no resource type schema available", absAddr) + } + + src, err := obj.Encode(schema.ImpliedType(), currentVersion) + if err != nil { + return fmt.Errorf("failed to encode %s in state: %s", absAddr, err) + } + + state.SetResourceInstanceCurrent(absAddr, src, n.ResolvedProvider) + return nil +} + +// planDestroy returns a plain destroy diff. +func (n *NodeAbstractResourceInstance) planDestroy(ctx EvalContext, currentState *states.ResourceInstanceObject, deposedKey states.DeposedKey) (*plans.ResourceInstanceChange, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + + absAddr := n.Addr + + if n.ResolvedProvider.Provider.Type == "" { + if deposedKey == "" { + panic(fmt.Sprintf("planDestroy for %s does not have ProviderAddr set", absAddr)) + } else { + panic(fmt.Sprintf("planDestroy for %s (deposed %s) does not have ProviderAddr set", absAddr, deposedKey)) + } + } + + // If there is no state or our attributes object is null then we're already + // destroyed. + if currentState == nil || currentState.Value.IsNull() { + // We still need to generate a NoOp change, because that allows + // outside consumers of the plan to distinguish between us affirming + // that we checked something and concluded no changes were needed + // vs. that something being entirely excluded e.g. due to -target. + noop := &plans.ResourceInstanceChange{ + Addr: absAddr, + DeposedKey: deposedKey, + Change: plans.Change{ + Action: plans.NoOp, + Before: cty.NullVal(cty.DynamicPseudoType), + After: cty.NullVal(cty.DynamicPseudoType), + }, + Private: currentState.Private, + ProviderAddr: n.ResolvedProvider, + } + return noop, nil + } + + // Call pre-diff hook + diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) { + return h.PreDiff( + absAddr, deposedKey.Generation(), + currentState.Value, + cty.NullVal(cty.DynamicPseudoType), + ) + })) + if diags.HasErrors() { + return nil, diags + } + + // Plan is always the same for a destroy. We don't need the provider's + // help for this one. + plan := &plans.ResourceInstanceChange{ + Addr: absAddr, + DeposedKey: deposedKey, + Change: plans.Change{ + Action: plans.Delete, + Before: currentState.Value, + After: cty.NullVal(cty.DynamicPseudoType), + }, + Private: currentState.Private, + ProviderAddr: n.ResolvedProvider, + } + + // Call post-diff hook + diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) { + return h.PostDiff( + absAddr, + deposedKey.Generation(), + plan.Action, + plan.Before, + plan.After, + ) + })) + + return plan, diags +} + +// writeChange saves a planned change for an instance object into the set of +// global planned changes. +func (n *NodeAbstractResourceInstance) writeChange(ctx EvalContext, change *plans.ResourceInstanceChange, deposedKey states.DeposedKey) error { + changes := ctx.Changes() + + if change == nil { + // Caller sets nil to indicate that we need to remove a change from + // the set of changes. + gen := states.CurrentGen + if deposedKey != states.NotDeposed { + gen = deposedKey + } + changes.RemoveResourceInstanceChange(n.Addr, gen) + return nil + } + + _, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + if err != nil { + return err + } + + if change.Addr.String() != n.Addr.String() || change.DeposedKey != deposedKey { + // Should never happen, and indicates a bug in the caller. + panic("inconsistent address and/or deposed key in writeChange") + } + + ri := n.Addr.Resource + schema, _ := providerSchema.SchemaForResourceAddr(ri.Resource) + if schema == nil { + // Should be caught during validation, so we don't bother with a pretty error here + return fmt.Errorf("provider does not support resource type %q", ri.Resource.Type) + } + + csrc, err := change.Encode(schema.ImpliedType()) + if err != nil { + return fmt.Errorf("failed to encode planned changes for %s: %s", n.Addr, err) + } + + changes.AppendResourceInstanceChange(csrc) + if deposedKey == states.NotDeposed { + log.Printf("[TRACE] writeChange: recorded %s change for %s", change.Action, n.Addr) + } else { + log.Printf("[TRACE] writeChange: recorded %s change for %s deposed object %s", change.Action, n.Addr, deposedKey) + } + + return nil +} + +// refresh does a refresh for a resource +func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, state *states.ResourceInstanceObject) (*states.ResourceInstanceObject, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + absAddr := n.Addr + log.Printf("[TRACE] NodeAbstractResourceInstance.refresh for %s", absAddr) + provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + if err != nil { + return state, diags.Append(err) + } + // If we have no state, we don't do any refreshing + if state == nil { + log.Printf("[DEBUG] refresh: %s: no state, so not refreshing", absAddr) + return state, diags + } + + schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.Resource.ContainingResource()) + if schema == nil { + // Should be caught during validation, so we don't bother with a pretty error here + diags = diags.Append(fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Resource.Type)) + return state, diags + } + + metaConfigVal, metaDiags := n.providerMetas(ctx) + diags = diags.Append(metaDiags) + if diags.HasErrors() { + return state, diags + } + + // Call pre-refresh hook + diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) { + return h.PreRefresh(absAddr, states.CurrentGen, state.Value) + })) + if diags.HasErrors() { + return state, diags + } + + // Refresh! + priorVal := state.Value + + // Unmarked before sending to provider + var priorPaths []cty.PathValueMarks + if priorVal.ContainsMarked() { + priorVal, priorPaths = priorVal.UnmarkDeepWithPaths() + } + + providerReq := providers.ReadResourceRequest{ + TypeName: n.Addr.Resource.Resource.Type, + PriorState: priorVal, + Private: state.Private, + ProviderMeta: metaConfigVal, + } + + resp := provider.ReadResource(providerReq) + if n.Config != nil { + resp.Diagnostics = resp.Diagnostics.InConfigBody(n.Config.Config, n.Addr.String()) + } + + diags = diags.Append(resp.Diagnostics) + if diags.HasErrors() { + return state, diags + } + + if resp.NewState == cty.NilVal { + // This ought not to happen in real cases since it's not possible to + // send NilVal over the plugin RPC channel, but it can come up in + // tests due to sloppy mocking. + panic("new state is cty.NilVal") + } + + for _, err := range resp.NewState.Type().TestConformance(schema.ImpliedType()) { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Provider produced invalid object", + fmt.Sprintf( + "Provider %q planned an invalid value for %s during refresh: %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", + n.ResolvedProvider.Provider.String(), absAddr, tfdiags.FormatError(err), + ), + )) + } + if diags.HasErrors() { + return state, diags + } + + // We have no way to exempt provider using the legacy SDK from this check, + // so we can only log inconsistencies with the updated state values. + // In most cases these are not errors anyway, and represent "drift" from + // external changes which will be handled by the subsequent plan. + if errs := objchange.AssertObjectCompatible(schema, priorVal, resp.NewState); len(errs) > 0 { + var buf strings.Builder + fmt.Fprintf(&buf, "[WARN] Provider %q produced an unexpected new value for %s during refresh.", n.ResolvedProvider.Provider.String(), absAddr) + for _, err := range errs { + fmt.Fprintf(&buf, "\n - %s", tfdiags.FormatError(err)) + } + log.Print(buf.String()) + } + + ret := state.DeepCopy() + ret.Value = resp.NewState + ret.Private = resp.Private + + // Call post-refresh hook + diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) { + return h.PostRefresh(absAddr, states.CurrentGen, priorVal, ret.Value) + })) + if diags.HasErrors() { + return ret, diags + } + + // Mark the value if necessary + if len(priorPaths) > 0 { + ret.Value = ret.Value.MarkWithPaths(priorPaths) + } + + return ret, diags +} + +func (n *NodeAbstractResourceInstance) plan( + ctx EvalContext, + plannedChange *plans.ResourceInstanceChange, + currentState *states.ResourceInstanceObject, + createBeforeDestroy bool, + forceReplace []addrs.AbsResourceInstance) (*plans.ResourceInstanceChange, *states.ResourceInstanceObject, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + var state *states.ResourceInstanceObject + var plan *plans.ResourceInstanceChange + + config := *n.Config + resource := n.Addr.Resource.Resource + provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + if err != nil { + return plan, state, diags.Append(err) + } + + if plannedChange != nil { + // If we already planned the action, we stick to that plan + createBeforeDestroy = plannedChange.Action == plans.CreateThenDelete + } + + if providerSchema == nil { + diags = diags.Append(fmt.Errorf("provider schema is unavailable for %s", n.Addr)) + return plan, state, diags + } + + // Evaluate the configuration + schema, _ := providerSchema.SchemaForResourceAddr(resource) + if schema == nil { + // Should be caught during validation, so we don't bother with a pretty error here + diags = diags.Append(fmt.Errorf("provider does not support resource type %q", resource.Type)) + return plan, state, diags + } + + forEach, _ := evaluateForEachExpression(n.Config.ForEach, ctx) + + keyData := EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach) + origConfigVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema, nil, keyData) + diags = diags.Append(configDiags) + if configDiags.HasErrors() { + return plan, state, diags + } + + metaConfigVal, metaDiags := n.providerMetas(ctx) + diags = diags.Append(metaDiags) + if diags.HasErrors() { + return plan, state, diags + } + + var priorVal cty.Value + var priorValTainted cty.Value + var priorPrivate []byte + if currentState != nil { + if currentState.Status != states.ObjectTainted { + priorVal = currentState.Value + priorPrivate = currentState.Private + } else { + // If the prior state is tainted then we'll proceed below like + // we're creating an entirely new object, but then turn it into + // a synthetic "Replace" change at the end, creating the same + // result as if the provider had marked at least one argument + // change as "requires replacement". + priorValTainted = currentState.Value + priorVal = cty.NullVal(schema.ImpliedType()) + } + } else { + priorVal = cty.NullVal(schema.ImpliedType()) + } + + // Create an unmarked version of our config val and our prior val. + // Store the paths for the config val to re-markafter + // we've sent things over the wire. + unmarkedConfigVal, unmarkedPaths := origConfigVal.UnmarkDeepWithPaths() + unmarkedPriorVal, priorPaths := priorVal.UnmarkDeepWithPaths() + + log.Printf("[TRACE] Re-validating config for %q", n.Addr) + // Allow the provider to validate the final set of values. + // The config was statically validated early on, but there may have been + // unknown values which the provider could not validate at the time. + // TODO: It would be more correct to validate the config after + // ignore_changes has been applied, but the current implementation cannot + // exclude computed-only attributes when given the `all` option. + validateResp := provider.ValidateResourceConfig( + providers.ValidateResourceConfigRequest{ + TypeName: n.Addr.Resource.Resource.Type, + Config: unmarkedConfigVal, + }, + ) + + diags = diags.Append(validateResp.Diagnostics.InConfigBody(config.Config, n.Addr.String())) + if diags.HasErrors() { + return plan, state, diags + } + + // ignore_changes is meant to only apply to the configuration, so it must + // be applied before we generate a plan. This ensures the config used for + // the proposed value, the proposed value itself, and the config presented + // to the provider in the PlanResourceChange request all agree on the + // starting values. + configValIgnored, ignoreChangeDiags := n.processIgnoreChanges(unmarkedPriorVal, unmarkedConfigVal) + diags = diags.Append(ignoreChangeDiags) + if ignoreChangeDiags.HasErrors() { + return plan, state, diags + } + + proposedNewVal := objchange.ProposedNew(schema, unmarkedPriorVal, configValIgnored) + + // Call pre-diff hook + diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) { + return h.PreDiff(n.Addr, states.CurrentGen, priorVal, proposedNewVal) + })) + if diags.HasErrors() { + return plan, state, diags + } + + resp := provider.PlanResourceChange(providers.PlanResourceChangeRequest{ + TypeName: n.Addr.Resource.Resource.Type, + Config: configValIgnored, + PriorState: unmarkedPriorVal, + ProposedNewState: proposedNewVal, + PriorPrivate: priorPrivate, + ProviderMeta: metaConfigVal, + }) + diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config, n.Addr.String())) + if diags.HasErrors() { + return plan, state, diags + } + + plannedNewVal := resp.PlannedState + plannedPrivate := resp.PlannedPrivate + + if plannedNewVal == cty.NilVal { + // Should never happen. Since real-world providers return via RPC a nil + // is always a bug in the client-side stub. This is more likely caused + // by an incompletely-configured mock provider in tests, though. + panic(fmt.Sprintf("PlanResourceChange of %s produced nil value", n.Addr)) + } + + // We allow the planned new value to disagree with configuration _values_ + // here, since that allows the provider to do special logic like a + // DiffSuppressFunc, but we still require that the provider produces + // a value whose type conforms to the schema. + for _, err := range plannedNewVal.Type().TestConformance(schema.ImpliedType()) { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Provider produced invalid plan", + fmt.Sprintf( + "Provider %q planned an invalid value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", + n.ResolvedProvider.Provider, tfdiags.FormatErrorPrefixed(err, n.Addr.String()), + ), + )) + } + if diags.HasErrors() { + return plan, state, diags + } + + if errs := objchange.AssertPlanValid(schema, unmarkedPriorVal, configValIgnored, plannedNewVal); len(errs) > 0 { + if resp.LegacyTypeSystem { + // The shimming of the old type system in the legacy SDK is not precise + // enough to pass this consistency check, so we'll give it a pass here, + // but we will generate a warning about it so that we are more likely + // to notice in the logs if an inconsistency beyond the type system + // leads to a downstream provider failure. + var buf strings.Builder + fmt.Fprintf(&buf, + "[WARN] Provider %q produced an invalid plan for %s, but we are tolerating it because it is using the legacy plugin SDK.\n The following problems may be the cause of any confusing errors from downstream operations:", + n.ResolvedProvider.Provider, n.Addr, + ) + for _, err := range errs { + fmt.Fprintf(&buf, "\n - %s", tfdiags.FormatError(err)) + } + log.Print(buf.String()) + } else { + for _, err := range errs { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Provider produced invalid plan", + fmt.Sprintf( + "Provider %q planned an invalid value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", + n.ResolvedProvider.Provider, tfdiags.FormatErrorPrefixed(err, n.Addr.String()), + ), + )) + } + return plan, state, diags + } + } + + if resp.LegacyTypeSystem { + // Because we allow legacy providers to depart from the contract and + // return changes to non-computed values, the plan response may have + // altered values that were already suppressed with ignore_changes. + // A prime example of this is where providers attempt to obfuscate + // config data by turning the config value into a hash and storing the + // hash value in the state. There are enough cases of this in existing + // providers that we must accommodate the behavior for now, so for + // ignore_changes to work at all on these values, we will revert the + // ignored values once more. + plannedNewVal, ignoreChangeDiags = n.processIgnoreChanges(unmarkedPriorVal, plannedNewVal) + diags = diags.Append(ignoreChangeDiags) + if ignoreChangeDiags.HasErrors() { + return plan, state, diags + } + } + + // Add the marks back to the planned new value -- this must happen after ignore changes + // have been processed + unmarkedPlannedNewVal := plannedNewVal + if len(unmarkedPaths) > 0 { + plannedNewVal = plannedNewVal.MarkWithPaths(unmarkedPaths) + } + + // The provider produces a list of paths to attributes whose changes mean + // that we must replace rather than update an existing remote object. + // However, we only need to do that if the identified attributes _have_ + // actually changed -- particularly after we may have undone some of the + // changes in processIgnoreChanges -- so now we'll filter that list to + // include only where changes are detected. + reqRep := cty.NewPathSet() + if len(resp.RequiresReplace) > 0 { + for _, path := range resp.RequiresReplace { + if priorVal.IsNull() { + // If prior is null then we don't expect any RequiresReplace at all, + // because this is a Create action. + continue + } + + priorChangedVal, priorPathDiags := hcl.ApplyPath(unmarkedPriorVal, path, nil) + plannedChangedVal, plannedPathDiags := hcl.ApplyPath(plannedNewVal, path, nil) + if plannedPathDiags.HasErrors() && priorPathDiags.HasErrors() { + // This means the path was invalid in both the prior and new + // values, which is an error with the provider itself. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Provider produced invalid plan", + fmt.Sprintf( + "Provider %q has indicated \"requires replacement\" on %s for a non-existent attribute path %#v.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", + n.ResolvedProvider.Provider, n.Addr, path, + ), + )) + continue + } + + // Make sure we have valid Values for both values. + // Note: if the opposing value was of the type + // cty.DynamicPseudoType, the type assigned here may not exactly + // match the schema. This is fine here, since we're only going to + // check for equality, but if the NullVal is to be used, we need to + // check the schema for th true type. + switch { + case priorChangedVal == cty.NilVal && plannedChangedVal == cty.NilVal: + // this should never happen without ApplyPath errors above + panic("requires replace path returned 2 nil values") + case priorChangedVal == cty.NilVal: + priorChangedVal = cty.NullVal(plannedChangedVal.Type()) + case plannedChangedVal == cty.NilVal: + plannedChangedVal = cty.NullVal(priorChangedVal.Type()) + } + + // Unmark for this value for the equality test. If only sensitivity has changed, + // this does not require an Update or Replace + unmarkedPlannedChangedVal, _ := plannedChangedVal.UnmarkDeep() + eqV := unmarkedPlannedChangedVal.Equals(priorChangedVal) + if !eqV.IsKnown() || eqV.False() { + reqRep.Add(path) + } + } + if diags.HasErrors() { + return plan, state, diags + } + } + + // The user might also ask us to force replacing a particular resource + // instance, regardless of whether the provider thinks it needs replacing. + // For example, users typically do this if they learn a particular object + // has become degraded in an immutable infrastructure scenario and so + // replacing it with a new object is a viable repair path. + matchedForceReplace := false + for _, candidateAddr := range forceReplace { + if candidateAddr.Equal(n.Addr) { + matchedForceReplace = true + break + } + + // For "force replace" purposes we require an exact resource instance + // address to match. If a user forgets to include the instance key + // for a multi-instance resource then it won't match here, but we + // have an earlier check in NodePlannableResource.Execute that should + // prevent us from getting here in that case. + } + + // Unmark for this test for value equality. + eqV := unmarkedPlannedNewVal.Equals(unmarkedPriorVal) + eq := eqV.IsKnown() && eqV.True() + + var action plans.Action + var actionReason plans.ResourceInstanceChangeActionReason + switch { + case priorVal.IsNull(): + action = plans.Create + case eq && !matchedForceReplace: + action = plans.NoOp + case matchedForceReplace || !reqRep.Empty(): + // If the user "forced replace" of this instance of if there are any + // "requires replace" paths left _after our filtering above_ then this + // is a replace action. + if createBeforeDestroy { + action = plans.CreateThenDelete + } else { + action = plans.DeleteThenCreate + } + switch { + case matchedForceReplace: + actionReason = plans.ResourceInstanceReplaceByRequest + case !reqRep.Empty(): + actionReason = plans.ResourceInstanceReplaceBecauseCannotUpdate + } + default: + action = plans.Update + // "Delete" is never chosen here, because deletion plans are always + // created more directly elsewhere, such as in "orphan" handling. + } + + if action.IsReplace() { + // In this strange situation we want to produce a change object that + // shows our real prior object but has a _new_ object that is built + // from a null prior object, since we're going to delete the one + // that has all the computed values on it. + // + // Therefore we'll ask the provider to plan again here, giving it + // a null object for the prior, and then we'll meld that with the + // _actual_ prior state to produce a correctly-shaped replace change. + // The resulting change should show any computed attributes changing + // from known prior values to unknown values, unless the provider is + // able to predict new values for any of these computed attributes. + nullPriorVal := cty.NullVal(schema.ImpliedType()) + + // Since there is no prior state to compare after replacement, we need + // a new unmarked config from our original with no ignored values. + unmarkedConfigVal := origConfigVal + if origConfigVal.ContainsMarked() { + unmarkedConfigVal, _ = origConfigVal.UnmarkDeep() + } + + // create a new proposed value from the null state and the config + proposedNewVal = objchange.ProposedNew(schema, nullPriorVal, unmarkedConfigVal) + + resp = provider.PlanResourceChange(providers.PlanResourceChangeRequest{ + TypeName: n.Addr.Resource.Resource.Type, + Config: unmarkedConfigVal, + PriorState: nullPriorVal, + ProposedNewState: proposedNewVal, + PriorPrivate: plannedPrivate, + ProviderMeta: metaConfigVal, + }) + // We need to tread carefully here, since if there are any warnings + // in here they probably also came out of our previous call to + // PlanResourceChange above, and so we don't want to repeat them. + // Consequently, we break from the usual pattern here and only + // append these new diagnostics if there's at least one error inside. + if resp.Diagnostics.HasErrors() { + diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config, n.Addr.String())) + return plan, state, diags + } + plannedNewVal = resp.PlannedState + plannedPrivate = resp.PlannedPrivate + + if len(unmarkedPaths) > 0 { + plannedNewVal = plannedNewVal.MarkWithPaths(unmarkedPaths) + } + + for _, err := range plannedNewVal.Type().TestConformance(schema.ImpliedType()) { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Provider produced invalid plan", + fmt.Sprintf( + "Provider %q planned an invalid value for %s%s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", + n.ResolvedProvider.Provider, n.Addr, tfdiags.FormatError(err), + ), + )) + } + if diags.HasErrors() { + return plan, state, diags + } + } + + // If our prior value was tainted then we actually want this to appear + // as a replace change, even though so far we've been treating it as a + // create. + if action == plans.Create && !priorValTainted.IsNull() { + if createBeforeDestroy { + action = plans.CreateThenDelete + } else { + action = plans.DeleteThenCreate + } + priorVal = priorValTainted + actionReason = plans.ResourceInstanceReplaceBecauseTainted + } + + // If we plan to write or delete sensitive paths from state, + // this is an Update action + if action == plans.NoOp && !marksEqual(unmarkedPaths, priorPaths) { + action = plans.Update + } + + // As a special case, if we have a previous diff (presumably from the plan + // phases, whereas we're now in the apply phase) and it was for a replace, + // we've already deleted the original object from state by the time we + // get here and so we would've ended up with a _create_ action this time, + // which we now need to paper over to get a result consistent with what + // we originally intended. + if plannedChange != nil { + prevChange := *plannedChange + if prevChange.Action.IsReplace() && action == plans.Create { + log.Printf("[TRACE] plan: %s treating Create change as %s change to match with earlier plan", n.Addr, prevChange.Action) + action = prevChange.Action + priorVal = prevChange.Before + } + } + + // Call post-refresh hook + diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) { + return h.PostDiff(n.Addr, states.CurrentGen, action, priorVal, plannedNewVal) + })) + if diags.HasErrors() { + return plan, state, diags + } + + // Update our return plan + plan = &plans.ResourceInstanceChange{ + Addr: n.Addr, + Private: plannedPrivate, + ProviderAddr: n.ResolvedProvider, + Change: plans.Change{ + Action: action, + Before: priorVal, + // Pass the marked planned value through in our change + // to propogate through evaluation. + // Marks will be removed when encoding. + After: plannedNewVal, + }, + ActionReason: actionReason, + RequiredReplace: reqRep, + } + + // Update our return state + state = &states.ResourceInstanceObject{ + // We use the special "planned" status here to note that this + // object's value is not yet complete. Objects with this status + // cannot be used during expression evaluation, so the caller + // must _also_ record the returned change in the active plan, + // which the expression evaluator will use in preference to this + // incomplete value recorded in the state. + Status: states.ObjectPlanned, + Value: plannedNewVal, + Private: plannedPrivate, + } + + return plan, state, diags +} + +func (n *NodeAbstractResource) processIgnoreChanges(prior, config cty.Value) (cty.Value, tfdiags.Diagnostics) { + // ignore_changes only applies when an object already exists, since we + // can't ignore changes to a thing we've not created yet. + if prior.IsNull() { + return config, nil + } + + ignoreChanges := n.Config.Managed.IgnoreChanges + ignoreAll := n.Config.Managed.IgnoreAllChanges + + if len(ignoreChanges) == 0 && !ignoreAll { + return config, nil + } + if ignoreAll { + return prior, nil + } + if prior.IsNull() || config.IsNull() { + // Ignore changes doesn't apply when we're creating for the first time. + // Proposed should never be null here, but if it is then we'll just let it be. + return config, nil + } + + return processIgnoreChangesIndividual(prior, config, ignoreChanges) +} + +func processIgnoreChangesIndividual(prior, config cty.Value, ignoreChanges []hcl.Traversal) (cty.Value, tfdiags.Diagnostics) { + // When we walk below we will be using cty.Path values for comparison, so + // we'll convert our traversals here so we can compare more easily. + ignoreChangesPath := make([]cty.Path, len(ignoreChanges)) + for i, traversal := range ignoreChanges { + path := make(cty.Path, len(traversal)) + for si, step := range traversal { + switch ts := step.(type) { + case hcl.TraverseRoot: + path[si] = cty.GetAttrStep{ + Name: ts.Name, + } + case hcl.TraverseAttr: + path[si] = cty.GetAttrStep{ + Name: ts.Name, + } + case hcl.TraverseIndex: + path[si] = cty.IndexStep{ + Key: ts.Key, + } + default: + panic(fmt.Sprintf("unsupported traversal step %#v", step)) + } + } + ignoreChangesPath[i] = path + } + + type ignoreChange struct { + // Path is the full path, minus any trailing map index + path cty.Path + // Value is the value we are to retain at the above path. If there is a + // key value, this must be a map and the desired value will be at the + // key index. + value cty.Value + // Key is the index key if the ignored path ends in a map index. + key cty.Value + } + var ignoredValues []ignoreChange + + // Find the actual changes first and store them in the ignoreChange struct. + // If the change was to a map value, and the key doesn't exist in the + // config, it would never be visited in the transform walk. + for _, icPath := range ignoreChangesPath { + key := cty.NullVal(cty.String) + // check for a map index, since maps are the only structure where we + // could have invalid path steps. + last, ok := icPath[len(icPath)-1].(cty.IndexStep) + if ok { + if last.Key.Type() == cty.String { + icPath = icPath[:len(icPath)-1] + key = last.Key + } + } + + // The structure should have been validated already, and we already + // trimmed the trailing map index. Any other intermediate index error + // means we wouldn't be able to apply the value below, so no need to + // record this. + p, err := icPath.Apply(prior) + if err != nil { + continue + } + c, err := icPath.Apply(config) + if err != nil { + continue + } + + // If this is a map, it is checking the entire map value for equality + // rather than the individual key. This means that the change is stored + // here even if our ignored key doesn't change. That is OK since it + // won't cause any changes in the transformation, but allows us to skip + // breaking up the maps and checking for key existence here too. + eq := p.Equals(c) + if !eq.IsKnown() || eq.False() { + // there a change to ignore at this path, store the prior value + ignoredValues = append(ignoredValues, ignoreChange{icPath, p, key}) + } + } + + if len(ignoredValues) == 0 { + return config, nil + } + + ret, _ := cty.Transform(config, func(path cty.Path, v cty.Value) (cty.Value, error) { + // Easy path for when we are only matching the entire value. The only + // values we break up for inspection are maps. + if !v.Type().IsMapType() { + for _, ignored := range ignoredValues { + if path.Equals(ignored.path) { + return ignored.value, nil + } + } + return v, nil + } + // We now know this must be a map, so we need to accumulate the values + // key-by-key. + + if !v.IsNull() && !v.IsKnown() { + // since v is not known, we cannot ignore individual keys + return v, nil + } + + // The configMap is the current configuration value, which we will + // mutate based on the ignored paths and the prior map value. + var configMap map[string]cty.Value + switch { + case v.IsNull() || v.LengthInt() == 0: + configMap = map[string]cty.Value{} + default: + configMap = v.AsValueMap() + } + + for _, ignored := range ignoredValues { + if !path.Equals(ignored.path) { + continue + } + + if ignored.key.IsNull() { + // The map address is confirmed to match at this point, + // so if there is no key, we want the entire map and can + // stop accumulating values. + return ignored.value, nil + } + // Now we know we are ignoring a specific index of this map, so get + // the config map and modify, add, or remove the desired key. + + // We also need to create a prior map, so we can check for + // existence while getting the value, because Value.Index will + // return null for a key with a null value and for a non-existent + // key. + var priorMap map[string]cty.Value + switch { + case ignored.value.IsNull() || ignored.value.LengthInt() == 0: + priorMap = map[string]cty.Value{} + default: + priorMap = ignored.value.AsValueMap() + } + + key := ignored.key.AsString() + priorElem, keep := priorMap[key] + + switch { + case !keep: + // this didn't exist in the old map value, so we're keeping the + // "absence" of the key by removing it from the config + delete(configMap, key) + default: + configMap[key] = priorElem + } + } + + if len(configMap) == 0 { + return cty.MapValEmpty(v.Type().ElementType()), nil + } + + return cty.MapVal(configMap), nil + }) + return ret, nil +} + +// readDataSource handles everything needed to call ReadDataSource on the provider. +// A previously evaluated configVal can be passed in, or a new one is generated +// from the resource configuration. +func (n *NodeAbstractResourceInstance) readDataSource(ctx EvalContext, configVal cty.Value) (cty.Value, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + var newVal cty.Value + + config := *n.Config + + provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + diags = diags.Append(err) + if diags.HasErrors() { + return newVal, diags + } + if providerSchema == nil { + diags = diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr)) + return newVal, diags + } + schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource) + if schema == nil { + // Should be caught during validation, so we don't bother with a pretty error here + diags = diags.Append(fmt.Errorf("provider %q does not support data source %q", n.ResolvedProvider, n.Addr.ContainingResource().Resource.Type)) + return newVal, diags + } + + metaConfigVal, metaDiags := n.providerMetas(ctx) + diags = diags.Append(metaDiags) + if diags.HasErrors() { + return newVal, diags + } + + // Unmark before sending to provider, will re-mark before returning + var pvm []cty.PathValueMarks + configVal, pvm = configVal.UnmarkDeepWithPaths() + + log.Printf("[TRACE] readDataSource: Re-validating config for %s", n.Addr) + validateResp := provider.ValidateDataResourceConfig( + providers.ValidateDataResourceConfigRequest{ + TypeName: n.Addr.ContainingResource().Resource.Type, + Config: configVal, + }, + ) + diags = diags.Append(validateResp.Diagnostics.InConfigBody(config.Config, n.Addr.String())) + if diags.HasErrors() { + return newVal, diags + } + + // If we get down here then our configuration is complete and we're read + // to actually call the provider to read the data. + log.Printf("[TRACE] readDataSource: %s configuration is complete, so reading from provider", n.Addr) + + resp := provider.ReadDataSource(providers.ReadDataSourceRequest{ + TypeName: n.Addr.ContainingResource().Resource.Type, + Config: configVal, + ProviderMeta: metaConfigVal, + }) + diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config, n.Addr.String())) + if diags.HasErrors() { + return newVal, diags + } + newVal = resp.State + if newVal == cty.NilVal { + // This can happen with incompletely-configured mocks. We'll allow it + // and treat it as an alias for a properly-typed null value. + newVal = cty.NullVal(schema.ImpliedType()) + } + + for _, err := range newVal.Type().TestConformance(schema.ImpliedType()) { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Provider produced invalid object", + fmt.Sprintf( + "Provider %q produced an invalid value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", + n.ResolvedProvider, tfdiags.FormatErrorPrefixed(err, n.Addr.String()), + ), + )) + } + if diags.HasErrors() { + return newVal, diags + } + + if newVal.IsNull() { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Provider produced null object", + fmt.Sprintf( + "Provider %q produced a null value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", + n.ResolvedProvider, n.Addr, + ), + )) + } + + if !newVal.IsNull() && !newVal.IsWhollyKnown() { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Provider produced invalid object", + fmt.Sprintf( + "Provider %q produced a value for %s that is not wholly known.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", + n.ResolvedProvider, n.Addr, + ), + )) + + // We'll still save the object, but we need to eliminate any unknown + // values first because we can't serialize them in the state file. + // Note that this may cause set elements to be coalesced if they + // differed only by having unknown values, but we don't worry about + // that here because we're saving the value only for inspection + // purposes; the error we added above will halt the graph walk. + newVal = cty.UnknownAsNull(newVal) + } + + if len(pvm) > 0 { + newVal = newVal.MarkWithPaths(pvm) + } + + return newVal, diags +} + +func (n *NodeAbstractResourceInstance) providerMetas(ctx EvalContext) (cty.Value, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + metaConfigVal := cty.NullVal(cty.DynamicPseudoType) + + _, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + if err != nil { + return metaConfigVal, diags.Append(err) + } + if providerSchema == nil { + return metaConfigVal, diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr)) + } + if n.ProviderMetas != nil { + if m, ok := n.ProviderMetas[n.ResolvedProvider.Provider]; ok && m != nil { + // if the provider doesn't support this feature, throw an error + if providerSchema.ProviderMeta == nil { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: fmt.Sprintf("Provider %s doesn't support provider_meta", n.ResolvedProvider.Provider.String()), + Detail: fmt.Sprintf("The resource %s belongs to a provider that doesn't support provider_meta blocks", n.Addr.Resource), + Subject: &m.ProviderRange, + }) + } else { + var configDiags tfdiags.Diagnostics + metaConfigVal, _, configDiags = ctx.EvaluateBlock(m.Config, providerSchema.ProviderMeta, nil, EvalDataForNoInstanceKey) + diags = diags.Append(configDiags) + } + } + } + return metaConfigVal, diags +} + +// planDataSource deals with the main part of the data resource lifecycle: +// either actually reading from the data source or generating a plan to do so. +// +// currentState is the current state for the data source, and the new state is +// returned. While data sources are read-only, we need to start with the prior +// state to determine if we have a change or not. If we needed to read a new +// value, but it still matches the previous state, then we can record a NoNop +// change. If the states don't match then we record a Read change so that the +// new value is applied to the state. +func (n *NodeAbstractResourceInstance) planDataSource(ctx EvalContext, currentState *states.ResourceInstanceObject) (*plans.ResourceInstanceChange, *states.ResourceInstanceObject, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + var configVal cty.Value + + _, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + if err != nil { + return nil, nil, diags.Append(err) + } + if providerSchema == nil { + return nil, nil, diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr)) + } + + config := *n.Config + schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource) + if schema == nil { + // Should be caught during validation, so we don't bother with a pretty error here + diags = diags.Append(fmt.Errorf("provider %q does not support data source %q", n.ResolvedProvider, n.Addr.ContainingResource().Resource.Type)) + return nil, nil, diags + } + + objTy := schema.ImpliedType() + priorVal := cty.NullVal(objTy) + if currentState != nil { + priorVal = currentState.Value + } + + forEach, _ := evaluateForEachExpression(config.ForEach, ctx) + keyData := EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach) + + var configDiags tfdiags.Diagnostics + configVal, _, configDiags = ctx.EvaluateBlock(config.Config, schema, nil, keyData) + diags = diags.Append(configDiags) + if configDiags.HasErrors() { + return nil, nil, diags + } + + unmarkedConfigVal, configMarkPaths := configVal.UnmarkDeepWithPaths() + // We drop marks on the values used here as the result is only + // temporarily used for validation. + unmarkedPriorVal, _ := priorVal.UnmarkDeep() + + configKnown := configVal.IsWhollyKnown() + // If our configuration contains any unknown values, or we depend on any + // unknown values then we must defer the read to the apply phase by + // producing a "Read" change for this resource, and a placeholder value for + // it in the state. + if n.forcePlanReadData(ctx) || !configKnown { + if configKnown { + log.Printf("[TRACE] planDataSource: %s configuration is fully known, but we're forcing a read plan to be created", n.Addr) + } else { + log.Printf("[TRACE] planDataSource: %s configuration not fully known yet, so deferring to apply phase", n.Addr) + } + + proposedNewVal := objchange.PlannedDataResourceObject(schema, unmarkedConfigVal) + + diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) { + return h.PreDiff(n.Addr, states.CurrentGen, priorVal, proposedNewVal) + })) + if diags.HasErrors() { + return nil, nil, diags + } + proposedNewVal = proposedNewVal.MarkWithPaths(configMarkPaths) + + // Apply detects that the data source will need to be read by the After + // value containing unknowns from PlanDataResourceObject. + plannedChange := &plans.ResourceInstanceChange{ + Addr: n.Addr, + ProviderAddr: n.ResolvedProvider, + Change: plans.Change{ + Action: plans.Read, + Before: priorVal, + After: proposedNewVal, + }, + } + + plannedNewState := &states.ResourceInstanceObject{ + Value: proposedNewVal, + Status: states.ObjectPlanned, + } + + diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) { + return h.PostDiff(n.Addr, states.CurrentGen, plans.Read, priorVal, proposedNewVal) + })) + + return plannedChange, plannedNewState, diags + } + + // While this isn't a "diff", continue to call this for data sources. + diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) { + return h.PreDiff(n.Addr, states.CurrentGen, priorVal, configVal) + })) + if diags.HasErrors() { + return nil, nil, diags + } + // We have a complete configuration with no dependencies to wait on, so we + // can read the data source into the state. + newVal, readDiags := n.readDataSource(ctx, configVal) + diags = diags.Append(readDiags) + if diags.HasErrors() { + return nil, nil, diags + } + + // if we have a prior value, we can check for any irregularities in the response + if !priorVal.IsNull() { + // While we don't propose planned changes for data sources, we can + // generate a proposed value for comparison to ensure the data source + // is returning a result following the rules of the provider contract. + proposedVal := objchange.ProposedNew(schema, unmarkedPriorVal, unmarkedConfigVal) + if errs := objchange.AssertObjectCompatible(schema, proposedVal, newVal); len(errs) > 0 { + // Resources have the LegacyTypeSystem field to signal when they are + // using an SDK which may not produce precise values. While data + // sources are read-only, they can still return a value which is not + // compatible with the config+schema. Since we can't detect the legacy + // type system, we can only warn about this for now. + var buf strings.Builder + fmt.Fprintf(&buf, "[WARN] Provider %q produced an unexpected new value for %s.", + n.ResolvedProvider, n.Addr) + for _, err := range errs { + fmt.Fprintf(&buf, "\n - %s", tfdiags.FormatError(err)) + } + log.Print(buf.String()) + } + } + + plannedNewState := &states.ResourceInstanceObject{ + Value: newVal, + Status: states.ObjectReady, + } + + diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) { + return h.PostDiff(n.Addr, states.CurrentGen, plans.Update, priorVal, newVal) + })) + return nil, plannedNewState, diags +} + +// forcePlanReadData determines if we need to override the usual behavior of +// immediately reading from the data source where possible, instead forcing us +// to generate a plan. +func (n *NodeAbstractResourceInstance) forcePlanReadData(ctx EvalContext) bool { + nModInst := n.Addr.Module + nMod := nModInst.Module() + + // Check and see if any depends_on dependencies have + // changes, since they won't show up as changes in the + // configuration. + changes := ctx.Changes() + for _, d := range n.dependsOn { + if d.Resource.Mode == addrs.DataResourceMode { + // Data sources have no external side effects, so they pose a need + // to delay this read. If they do have a change planned, it must be + // because of a dependency on a managed resource, in which case + // we'll also encounter it in this list of dependencies. + continue + } + + for _, change := range changes.GetChangesForConfigResource(d) { + changeModInst := change.Addr.Module + changeMod := changeModInst.Module() + + if changeMod.Equal(nMod) && !changeModInst.Equal(nModInst) { + // Dependencies are tracked by configuration address, which + // means we may have changes from other instances of parent + // modules. The actual reference can only take effect within + // the same module instance, so skip any that aren't an exact + // match + continue + } + + if change != nil && change.Action != plans.NoOp { + return true + } + } + } + return false +} + +// apply deals with the main part of the data resource lifecycle: either +// actually reading from the data source or generating a plan to do so. +func (n *NodeAbstractResourceInstance) applyDataSource(ctx EvalContext, planned *plans.ResourceInstanceChange) (*states.ResourceInstanceObject, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + + _, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + if err != nil { + return nil, diags.Append(err) + } + if providerSchema == nil { + return nil, diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr)) + } + + if planned != nil && planned.Action != plans.Read { + // If any other action gets in here then that's always a bug; this + // EvalNode only deals with reading. + diags = diags.Append(fmt.Errorf( + "invalid action %s for %s: only Read is supported (this is a bug in Terraform; please report it!)", + planned.Action, n.Addr, + )) + return nil, diags + } + + diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) { + return h.PreApply(n.Addr, states.CurrentGen, planned.Action, planned.Before, planned.After) + })) + if diags.HasErrors() { + return nil, diags + } + + config := *n.Config + schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource) + if schema == nil { + // Should be caught during validation, so we don't bother with a pretty error here + diags = diags.Append(fmt.Errorf("provider %q does not support data source %q", n.ResolvedProvider, n.Addr.ContainingResource().Resource.Type)) + return nil, diags + } + + forEach, _ := evaluateForEachExpression(config.ForEach, ctx) + keyData := EvalDataForInstanceKey(n.Addr.Resource.Key, forEach) + + configVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema, nil, keyData) + diags = diags.Append(configDiags) + if configDiags.HasErrors() { + return nil, diags + } + + newVal, readDiags := n.readDataSource(ctx, configVal) + diags = diags.Append(readDiags) + if diags.HasErrors() { + return nil, diags + } + + state := &states.ResourceInstanceObject{ + Value: newVal, + Status: states.ObjectReady, + } + + diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) { + return h.PostApply(n.Addr, states.CurrentGen, newVal, diags.Err()) + })) + + return state, diags +} + +// evalApplyProvisioners determines if provisioners need to be run, and if so +// executes the provisioners for a resource and returns an updated error if +// provisioning fails. +func (n *NodeAbstractResourceInstance) evalApplyProvisioners(ctx EvalContext, state *states.ResourceInstanceObject, createNew bool, when configs.ProvisionerWhen) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + if state == nil { + log.Printf("[TRACE] evalApplyProvisioners: %s has no state, so skipping provisioners", n.Addr) + return nil + } + if when == configs.ProvisionerWhenCreate && !createNew { + // If we're not creating a new resource, then don't run provisioners + log.Printf("[TRACE] evalApplyProvisioners: %s is not freshly-created, so no provisioning is required", n.Addr) + return nil + } + if state.Status == states.ObjectTainted { + // No point in provisioning an object that is already tainted, since + // it's going to get recreated on the next apply anyway. + log.Printf("[TRACE] evalApplyProvisioners: %s is tainted, so skipping provisioning", n.Addr) + return nil + } + + provs := filterProvisioners(n.Config, when) + if len(provs) == 0 { + // We have no provisioners, so don't do anything + return nil + } + + // Call pre hook + diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) { + return h.PreProvisionInstance(n.Addr, state.Value) + })) + if diags.HasErrors() { + return diags + } + + // If there are no errors, then we append it to our output error + // if we have one, otherwise we just output it. + err := n.applyProvisioners(ctx, state, when, provs) + if err != nil { + diags = diags.Append(err) + log.Printf("[TRACE] evalApplyProvisioners: %s provisioning failed, but we will continue anyway at the caller's request", n.Addr) + return diags + } + + // Call post hook + return diags.Append(ctx.Hook(func(h Hook) (HookAction, error) { + return h.PostProvisionInstance(n.Addr, state.Value) + })) +} + +// filterProvisioners filters the provisioners on the resource to only +// the provisioners specified by the "when" option. +func filterProvisioners(config *configs.Resource, when configs.ProvisionerWhen) []*configs.Provisioner { + // Fast path the zero case + if config == nil || config.Managed == nil { + return nil + } + + if len(config.Managed.Provisioners) == 0 { + return nil + } + + result := make([]*configs.Provisioner, 0, len(config.Managed.Provisioners)) + for _, p := range config.Managed.Provisioners { + if p.When == when { + result = append(result, p) + } + } + + return result +} + +// applyProvisioners executes the provisioners for a resource. +func (n *NodeAbstractResourceInstance) applyProvisioners(ctx EvalContext, state *states.ResourceInstanceObject, when configs.ProvisionerWhen, provs []*configs.Provisioner) error { + var diags tfdiags.Diagnostics + + // this self is only used for destroy provisioner evaluation, and must + // refer to the last known value of the resource. + self := state.Value + + var evalScope func(EvalContext, hcl.Body, cty.Value, *configschema.Block) (cty.Value, tfdiags.Diagnostics) + switch when { + case configs.ProvisionerWhenDestroy: + evalScope = n.evalDestroyProvisionerConfig + default: + evalScope = n.evalProvisionerConfig + } + + // If there's a connection block defined directly inside the resource block + // then it'll serve as a base connection configuration for all of the + // provisioners. + var baseConn hcl.Body + if n.Config.Managed != nil && n.Config.Managed.Connection != nil { + baseConn = n.Config.Managed.Connection.Config + } + + for _, prov := range provs { + log.Printf("[TRACE] applyProvisioners: provisioning %s with %q", n.Addr, prov.Type) + + // Get the provisioner + provisioner, err := ctx.Provisioner(prov.Type) + if err != nil { + diags = diags.Append(err) + return diags.Err() + } + + schema := ctx.ProvisionerSchema(prov.Type) + + config, configDiags := evalScope(ctx, prov.Config, self, schema) + diags = diags.Append(configDiags) + if diags.HasErrors() { + return diags.Err() + } + + // If the provisioner block contains a connection block of its own then + // it can override the base connection configuration, if any. + var localConn hcl.Body + if prov.Connection != nil { + localConn = prov.Connection.Config + } + + var connBody hcl.Body + switch { + case baseConn != nil && localConn != nil: + // Our standard merging logic applies here, similar to what we do + // with _override.tf configuration files: arguments from the + // base connection block will be masked by any arguments of the + // same name in the local connection block. + connBody = configs.MergeBodies(baseConn, localConn) + case baseConn != nil: + connBody = baseConn + case localConn != nil: + connBody = localConn + } + + // start with an empty connInfo + connInfo := cty.NullVal(connectionBlockSupersetSchema.ImpliedType()) + + if connBody != nil { + var connInfoDiags tfdiags.Diagnostics + connInfo, connInfoDiags = evalScope(ctx, connBody, self, connectionBlockSupersetSchema) + diags = diags.Append(connInfoDiags) + if diags.HasErrors() { + return diags.Err() + } + } + + { + // Call pre hook + err := ctx.Hook(func(h Hook) (HookAction, error) { + return h.PreProvisionInstanceStep(n.Addr, prov.Type) + }) + if err != nil { + return err + } + } + + // The output function + outputFn := func(msg string) { + ctx.Hook(func(h Hook) (HookAction, error) { + h.ProvisionOutput(n.Addr, prov.Type, msg) + return HookActionContinue, nil + }) + } + + // If our config or connection info contains any marked values, ensure + // those are stripped out before sending to the provisioner. Unlike + // resources, we have no need to capture the marked paths and reapply + // later. + unmarkedConfig, configMarks := config.UnmarkDeep() + unmarkedConnInfo, _ := connInfo.UnmarkDeep() + + // Marks on the config might result in leaking sensitive values through + // provisioner logging, so we conservatively suppress all output in + // this case. This should not apply to connection info values, which + // provisioners ought not to be logging anyway. + if len(configMarks) > 0 { + outputFn = func(msg string) { + ctx.Hook(func(h Hook) (HookAction, error) { + h.ProvisionOutput(n.Addr, prov.Type, "(output suppressed due to sensitive value in config)") + return HookActionContinue, nil + }) + } + } + + output := CallbackUIOutput{OutputFn: outputFn} + resp := provisioner.ProvisionResource(provisioners.ProvisionResourceRequest{ + Config: unmarkedConfig, + Connection: unmarkedConnInfo, + UIOutput: &output, + }) + applyDiags := resp.Diagnostics.InConfigBody(prov.Config, n.Addr.String()) + + // Call post hook + hookErr := ctx.Hook(func(h Hook) (HookAction, error) { + return h.PostProvisionInstanceStep(n.Addr, prov.Type, applyDiags.Err()) + }) + + switch prov.OnFailure { + case configs.ProvisionerOnFailureContinue: + if applyDiags.HasErrors() { + log.Printf("[WARN] Errors while provisioning %s with %q, but continuing as requested in configuration", n.Addr, prov.Type) + } else { + // Maybe there are warnings that we still want to see + diags = diags.Append(applyDiags) + } + default: + diags = diags.Append(applyDiags) + if applyDiags.HasErrors() { + log.Printf("[WARN] Errors while provisioning %s with %q, so aborting", n.Addr, prov.Type) + return diags.Err() + } + } + + // Deal with the hook + if hookErr != nil { + return hookErr + } + } + + // we have to drop warning-only diagnostics for now + if diags.HasErrors() { + return diags.ErrWithWarnings() + } + + // log any warnings since we can't return them + if e := diags.ErrWithWarnings(); e != nil { + log.Printf("[WARN] applyProvisioners %s: %v", n.Addr, e) + } + + return nil +} + +func (n *NodeAbstractResourceInstance) evalProvisionerConfig(ctx EvalContext, body hcl.Body, self cty.Value, schema *configschema.Block) (cty.Value, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + + forEach, forEachDiags := evaluateForEachExpression(n.Config.ForEach, ctx) + diags = diags.Append(forEachDiags) + + keyData := EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach) + + config, _, configDiags := ctx.EvaluateBlock(body, schema, n.ResourceInstanceAddr().Resource, keyData) + diags = diags.Append(configDiags) + + return config, diags +} + +// during destroy a provisioner can only evaluate within the scope of the parent resource +func (n *NodeAbstractResourceInstance) evalDestroyProvisionerConfig(ctx EvalContext, body hcl.Body, self cty.Value, schema *configschema.Block) (cty.Value, tfdiags.Diagnostics) { + var diags tfdiags.Diagnostics + + // For a destroy-time provisioner forEach is intentionally nil here, + // which EvalDataForInstanceKey responds to by not populating EachValue + // in its result. That's okay because each.value is prohibited for + // destroy-time provisioners. + keyData := EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, nil) + + evalScope := ctx.EvaluationScope(n.ResourceInstanceAddr().Resource, keyData) + config, evalDiags := evalScope.EvalSelfBlock(body, self, schema, keyData) + diags = diags.Append(evalDiags) + + return config, diags +} + +// apply accepts an applyConfig, instead of using n.Config, so destroy plans can +// send a nil config. Most of the errors generated in apply are returned as +// diagnostics, but if provider.ApplyResourceChange itself fails, that error is +// returned as an error and nil diags are returned. +func (n *NodeAbstractResourceInstance) apply( + ctx EvalContext, + state *states.ResourceInstanceObject, + change *plans.ResourceInstanceChange, + applyConfig *configs.Resource, + createBeforeDestroy bool) (*states.ResourceInstanceObject, tfdiags.Diagnostics) { + + var diags tfdiags.Diagnostics + if state == nil { + state = &states.ResourceInstanceObject{} + } + + provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + if err != nil { + return nil, diags.Append(err) + } + schema, _ := providerSchema.SchemaForResourceType(n.Addr.Resource.Resource.Mode, n.Addr.Resource.Resource.Type) + if schema == nil { + // Should be caught during validation, so we don't bother with a pretty error here + diags = diags.Append(fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Resource.Type)) + return nil, diags + } + + log.Printf("[INFO] Starting apply for %s", n.Addr) + + configVal := cty.NullVal(cty.DynamicPseudoType) + if applyConfig != nil { + var configDiags tfdiags.Diagnostics + forEach, _ := evaluateForEachExpression(applyConfig.ForEach, ctx) + keyData := EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach) + configVal, _, configDiags = ctx.EvaluateBlock(applyConfig.Config, schema, nil, keyData) + diags = diags.Append(configDiags) + if configDiags.HasErrors() { + return nil, diags + } + } + + if !configVal.IsWhollyKnown() { + diags = diags.Append(fmt.Errorf( + "configuration for %s still contains unknown values during apply (this is a bug in Terraform; please report it!)", + n.Addr, + )) + return nil, diags + } + + metaConfigVal, metaDiags := n.providerMetas(ctx) + diags = diags.Append(metaDiags) + if diags.HasErrors() { + return nil, diags + } + + log.Printf("[DEBUG] %s: applying the planned %s change", n.Addr, change.Action) + + // If our config, Before or After value contain any marked values, + // ensure those are stripped out before sending + // this to the provider + unmarkedConfigVal, _ := configVal.UnmarkDeep() + unmarkedBefore, beforePaths := change.Before.UnmarkDeepWithPaths() + unmarkedAfter, afterPaths := change.After.UnmarkDeepWithPaths() + + // If we have an Update action, our before and after values are equal, + // and only differ on their sensitivity, the newVal is the after val + // and we should not communicate with the provider. We do need to update + // the state with this new value, to ensure the sensitivity change is + // persisted. + eqV := unmarkedBefore.Equals(unmarkedAfter) + eq := eqV.IsKnown() && eqV.True() + if change.Action == plans.Update && eq && !marksEqual(beforePaths, afterPaths) { + // Copy the previous state, changing only the value + newState := &states.ResourceInstanceObject{ + CreateBeforeDestroy: state.CreateBeforeDestroy, + Dependencies: state.Dependencies, + Private: state.Private, + Status: state.Status, + Value: change.After, + } + return newState, diags + } + + resp := provider.ApplyResourceChange(providers.ApplyResourceChangeRequest{ + TypeName: n.Addr.Resource.Resource.Type, + PriorState: unmarkedBefore, + Config: unmarkedConfigVal, + PlannedState: unmarkedAfter, + PlannedPrivate: change.Private, + ProviderMeta: metaConfigVal, + }) + applyDiags := resp.Diagnostics + if applyConfig != nil { + applyDiags = applyDiags.InConfigBody(applyConfig.Config, n.Addr.String()) + } + diags = diags.Append(applyDiags) + + // Even if there are errors in the returned diagnostics, the provider may + // have returned a _partial_ state for an object that already exists but + // failed to fully configure, and so the remaining code must always run + // to completion but must be defensive against the new value being + // incomplete. + newVal := resp.NewState + + // If we have paths to mark, mark those on this new value + if len(afterPaths) > 0 { + newVal = newVal.MarkWithPaths(afterPaths) + } + + if newVal == cty.NilVal { + // Providers are supposed to return a partial new value even when errors + // occur, but sometimes they don't and so in that case we'll patch that up + // by just using the prior state, so we'll at least keep track of the + // object for the user to retry. + newVal = change.Before + + // As a special case, we'll set the new value to null if it looks like + // we were trying to execute a delete, because the provider in this case + // probably left the newVal unset intending it to be interpreted as "null". + if change.After.IsNull() { + newVal = cty.NullVal(schema.ImpliedType()) + } + + if !diags.HasErrors() { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Provider produced invalid object", + fmt.Sprintf( + "Provider %q produced an invalid nil value after apply for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", + n.ResolvedProvider.String(), n.Addr.String(), + ), + )) + } + } + + var conformDiags tfdiags.Diagnostics + for _, err := range newVal.Type().TestConformance(schema.ImpliedType()) { + conformDiags = conformDiags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Provider produced invalid object", + fmt.Sprintf( + "Provider %q produced an invalid value after apply for %s. The result cannot not be saved in the Terraform state.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", + n.ResolvedProvider.String(), tfdiags.FormatErrorPrefixed(err, n.Addr.String()), + ), + )) + } + diags = diags.Append(conformDiags) + if conformDiags.HasErrors() { + // Bail early in this particular case, because an object that doesn't + // conform to the schema can't be saved in the state anyway -- the + // serializer will reject it. + return nil, diags + } + + // After this point we have a type-conforming result object and so we + // must always run to completion to ensure it can be saved. If n.Error + // is set then we must not return a non-nil error, in order to allow + // evaluation to continue to a later point where our state object will + // be saved. + + // By this point there must not be any unknown values remaining in our + // object, because we've applied the change and we can't save unknowns + // in our persistent state. If any are present then we will indicate an + // error (which is always a bug in the provider) but we will also replace + // them with nulls so that we can successfully save the portions of the + // returned value that are known. + if !newVal.IsWhollyKnown() { + // To generate better error messages, we'll go for a walk through the + // value and make a separate diagnostic for each unknown value we + // find. + cty.Walk(newVal, func(path cty.Path, val cty.Value) (bool, error) { + if !val.IsKnown() { + pathStr := tfdiags.FormatCtyPath(path) + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Provider returned invalid result object after apply", + fmt.Sprintf( + "After the apply operation, the provider still indicated an unknown value for %s%s. All values must be known after apply, so this is always a bug in the provider and should be reported in the provider's own repository. Terraform will still save the other known object values in the state.", + n.Addr, pathStr, + ), + )) + } + return true, nil + }) + + // NOTE: This operation can potentially be lossy if there are multiple + // elements in a set that differ only by unknown values: after + // replacing with null these will be merged together into a single set + // element. Since we can only get here in the presence of a provider + // bug, we accept this because storing a result here is always a + // best-effort sort of thing. + newVal = cty.UnknownAsNull(newVal) + } + + if change.Action != plans.Delete && !diags.HasErrors() { + // Only values that were marked as unknown in the planned value are allowed + // to change during the apply operation. (We do this after the unknown-ness + // check above so that we also catch anything that became unknown after + // being known during plan.) + // + // If we are returning other errors anyway then we'll give this + // a pass since the other errors are usually the explanation for + // this one and so it's more helpful to let the user focus on the + // root cause rather than distract with this extra problem. + if errs := objchange.AssertObjectCompatible(schema, change.After, newVal); len(errs) > 0 { + if resp.LegacyTypeSystem { + // The shimming of the old type system in the legacy SDK is not precise + // enough to pass this consistency check, so we'll give it a pass here, + // but we will generate a warning about it so that we are more likely + // to notice in the logs if an inconsistency beyond the type system + // leads to a downstream provider failure. + var buf strings.Builder + fmt.Fprintf(&buf, "[WARN] Provider %q produced an unexpected new value for %s, but we are tolerating it because it is using the legacy plugin SDK.\n The following problems may be the cause of any confusing errors from downstream operations:", n.ResolvedProvider.String(), n.Addr) + for _, err := range errs { + fmt.Fprintf(&buf, "\n - %s", tfdiags.FormatError(err)) + } + log.Print(buf.String()) + + // The sort of inconsistency we won't catch here is if a known value + // in the plan is changed during apply. That can cause downstream + // problems because a dependent resource would make its own plan based + // on the planned value, and thus get a different result during the + // apply phase. This will usually lead to a "Provider produced invalid plan" + // error that incorrectly blames the downstream resource for the change. + + } else { + for _, err := range errs { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Provider produced inconsistent result after apply", + fmt.Sprintf( + "When applying changes to %s, provider %q produced an unexpected new value: %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", + n.Addr, n.ResolvedProvider.String(), tfdiags.FormatError(err), + ), + )) + } + } + } + } + + // If a provider returns a null or non-null object at the wrong time then + // we still want to save that but it often causes some confusing behaviors + // where it seems like Terraform is failing to take any action at all, + // so we'll generate some errors to draw attention to it. + if !diags.HasErrors() { + if change.Action == plans.Delete && !newVal.IsNull() { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Provider returned invalid result object after apply", + fmt.Sprintf( + "After applying a %s plan, the provider returned a non-null object for %s. Destroying should always produce a null value, so this is always a bug in the provider and should be reported in the provider's own repository. Terraform will still save this errant object in the state for debugging and recovery.", + change.Action, n.Addr, + ), + )) + } + if change.Action != plans.Delete && newVal.IsNull() { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Provider returned invalid result object after apply", + fmt.Sprintf( + "After applying a %s plan, the provider returned a null object for %s. Only destroying should always produce a null value, so this is always a bug in the provider and should be reported in the provider's own repository.", + change.Action, n.Addr, + ), + )) + } + } + + switch { + case diags.HasErrors() && newVal.IsNull(): + // Sometimes providers return a null value when an operation fails for + // some reason, but we'd rather keep the prior state so that the error + // can be corrected on a subsequent run. We must only do this for null + // new value though, or else we may discard partial updates the + // provider was able to complete. Otherwise, we'll continue using the + // prior state as the new value, making this effectively a no-op. If + // the item really _has_ been deleted then our next refresh will detect + // that and fix it up. + return state.DeepCopy(), diags + + case diags.HasErrors() && !newVal.IsNull(): + // if we have an error, make sure we restore the object status in the new state + newState := &states.ResourceInstanceObject{ + Status: state.Status, + Value: newVal, + Private: resp.Private, + CreateBeforeDestroy: createBeforeDestroy, + } + + // if the resource was being deleted, the dependencies are not going to + // be recalculated and we need to restore those as well. + if change.Action == plans.Delete { + newState.Dependencies = state.Dependencies + } + + return newState, diags + + case !newVal.IsNull(): + // Non error case with a new state + newState := &states.ResourceInstanceObject{ + Status: states.ObjectReady, + Value: newVal, + Private: resp.Private, + CreateBeforeDestroy: createBeforeDestroy, + } + return newState, diags + + default: + // Non error case, were the object was deleted + return nil, diags + } +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract_instance_test.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract_instance_test.go index 919e4bac..08561c46 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract_instance_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract_instance_test.go @@ -6,6 +6,9 @@ import ( "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/states" + "github.com/zclconf/go-cty/cty" ) func TestNodeAbstractResourceInstanceProvider(t *testing.T) { @@ -109,3 +112,47 @@ func TestNodeAbstractResourceInstanceProvider(t *testing.T) { }) } } + +func TestNodeAbstractResourceInstance_WriteResourceInstanceState(t *testing.T) { + state := states.NewState() + ctx := new(MockEvalContext) + ctx.StateState = state.SyncWrapper() + ctx.PathPath = addrs.RootModuleInstance + + mockProvider := mockProviderWithResourceTypeSchema("aws_instance", &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": { + Type: cty.String, + Optional: true, + }, + }, + }) + + obj := &states.ResourceInstanceObject{ + Value: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-abc123"), + }), + Status: states.ObjectReady, + } + + node := &NodeAbstractResourceInstance{ + Addr: mustResourceInstanceAddr("aws_instance.foo"), + // instanceState: obj, + NodeAbstractResource: NodeAbstractResource{ + ResolvedProvider: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + }, + } + ctx.ProviderProvider = mockProvider + ctx.ProviderSchemaSchema = mockProvider.ProviderSchema() + + err := node.writeResourceInstanceState(ctx, obj, workingState) + if err != nil { + t.Fatalf("unexpected error: %s", err.Error()) + } + + checkStateString(t, state, ` +aws_instance.foo: + ID = i-abc123 + provider = provider["registry.terraform.io/hashicorp/aws"] + `) +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract_test.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract_test.go index 6faafe1e..5c921f9c 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract_test.go @@ -6,6 +6,10 @@ import ( "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/states" + "github.com/zclconf/go-cty/cty" ) func TestNodeAbstractResourceProvider(t *testing.T) { @@ -107,3 +111,128 @@ func TestNodeAbstractResourceProvider(t *testing.T) { }) } } + +func TestNodeAbstractResource_ReadResourceInstanceState(t *testing.T) { + mockProvider := mockProviderWithResourceTypeSchema("aws_instance", &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": { + Type: cty.String, + Optional: true, + }, + }, + }) + + tests := map[string]struct { + State *states.State + Node *NodeAbstractResource + ExpectedInstanceId string + }{ + "ReadState gets primary instance state": { + State: states.BuildState(func(s *states.SyncState) { + providerAddr := addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("aws"), + Module: addrs.RootModule, + } + oneAddr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "aws_instance", + Name: "bar", + }.Absolute(addrs.RootModuleInstance) + s.SetResourceProvider(oneAddr, providerAddr) + s.SetResourceInstanceCurrent(oneAddr.Instance(addrs.NoKey), &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"i-abc123"}`), + }, providerAddr) + }), + Node: &NodeAbstractResource{ + Addr: mustConfigResourceAddr("aws_instance.bar"), + ResolvedProvider: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + }, + ExpectedInstanceId: "i-abc123", + }, + } + + for k, test := range tests { + t.Run(k, func(t *testing.T) { + ctx := new(MockEvalContext) + ctx.StateState = test.State.SyncWrapper() + ctx.PathPath = addrs.RootModuleInstance + ctx.ProviderSchemaSchema = mockProvider.ProviderSchema() + ctx.ProviderProvider = providers.Interface(mockProvider) + + got, readDiags := test.Node.readResourceInstanceState(ctx, test.Node.Addr.Resource.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)) + if readDiags.HasErrors() { + t.Fatalf("[%s] Got err: %#v", k, readDiags.Err()) + } + + expected := test.ExpectedInstanceId + + if !(got != nil && got.Value.GetAttr("id") == cty.StringVal(expected)) { + t.Fatalf("[%s] Expected output with ID %#v, got: %#v", k, expected, got) + } + }) + } +} + +func TestNodeAbstractResource_ReadResourceInstanceStateDeposed(t *testing.T) { + mockProvider := mockProviderWithResourceTypeSchema("aws_instance", &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": { + Type: cty.String, + Optional: true, + }, + }, + }) + + tests := map[string]struct { + State *states.State + Node *NodeAbstractResource + ExpectedInstanceId string + }{ + "ReadStateDeposed gets deposed instance": { + State: states.BuildState(func(s *states.SyncState) { + providerAddr := addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("aws"), + Module: addrs.RootModule, + } + oneAddr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "aws_instance", + Name: "bar", + }.Absolute(addrs.RootModuleInstance) + s.SetResourceProvider(oneAddr, providerAddr) + s.SetResourceInstanceDeposed(oneAddr.Instance(addrs.NoKey), states.DeposedKey("00000001"), &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"i-abc123"}`), + }, providerAddr) + }), + Node: &NodeAbstractResource{ + Addr: mustConfigResourceAddr("aws_instance.bar"), + ResolvedProvider: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + }, + ExpectedInstanceId: "i-abc123", + }, + } + for k, test := range tests { + t.Run(k, func(t *testing.T) { + ctx := new(MockEvalContext) + ctx.StateState = test.State.SyncWrapper() + ctx.PathPath = addrs.RootModuleInstance + ctx.ProviderSchemaSchema = mockProvider.ProviderSchema() + ctx.ProviderProvider = providers.Interface(mockProvider) + + key := states.DeposedKey("00000001") // shim from legacy state assigns 0th deposed index this key + + got, readDiags := test.Node.readResourceInstanceStateDeposed(ctx, test.Node.Addr.Resource.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), key) + if readDiags.HasErrors() { + t.Fatalf("[%s] Got err: %#v", k, readDiags.Err()) + } + + expected := test.ExpectedInstanceId + + if !(got != nil && got.Value.GetAttr("id") == cty.StringVal(expected)) { + t.Fatalf("[%s] Expected output with ID %#v, got: %#v", k, expected, got) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply.go index ff09ba61..7c23483b 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/dag" "github.com/hashicorp/terraform/lang" + "github.com/hashicorp/terraform/tfdiags" ) // nodeExpandApplyableResource handles the first layer of resource @@ -42,10 +43,7 @@ func (n *nodeExpandApplyableResource) DynamicExpand(ctx EvalContext) (*Graph, er expander := ctx.InstanceExpander() moduleInstances := expander.ExpandModule(n.Addr.Module) - var resources []addrs.AbsResource for _, module := range moduleInstances { - resAddr := n.Addr.Resource.Absolute(module) - resources = append(resources, resAddr) g.Add(&NodeApplyableResource{ NodeAbstractResource: n.NodeAbstractResource, Addr: n.Addr.Resource.Absolute(module), @@ -102,13 +100,12 @@ func (n *NodeApplyableResource) References() []*addrs.Reference { } // GraphNodeExecutable -func (n *NodeApplyableResource) Execute(ctx EvalContext, op walkOperation) error { +func (n *NodeApplyableResource) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics { if n.Config == nil { // Nothing to do, then. log.Printf("[TRACE] NodeApplyableResource: no configuration present for %s", n.Name()) return nil } - err := n.writeResourceState(ctx, n.Addr) - return err + return n.writeResourceState(ctx, n.Addr) } diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply_instance.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply_instance.go index a1d35b81..8db54970 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply_instance.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply_instance.go @@ -2,10 +2,12 @@ package terraform import ( "fmt" + "log" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/plans/objchange" "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/tfdiags" ) @@ -25,6 +27,12 @@ type NodeApplyableResourceInstance struct { // If this node is forced to be CreateBeforeDestroy, we need to record that // in the state to. ForceCreateBeforeDestroy bool + + // forceReplace are resource instance addresses where the user wants to + // force generating a replace action. This set isn't pre-filtered, so + // it might contain addresses that have nothing to do with the resource + // that this node represents, which the node itself must therefore ignore. + forceReplace []addrs.AbsResourceInstance } var ( @@ -101,7 +109,7 @@ func (n *NodeApplyableResourceInstance) AttachDependencies(deps []addrs.ConfigRe } // GraphNodeExecutable -func (n *NodeApplyableResourceInstance) Execute(ctx EvalContext, op walkOperation) error { +func (n *NodeApplyableResourceInstance) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { addr := n.ResourceInstanceAddr() if n.Config == nil { @@ -110,7 +118,6 @@ func (n *NodeApplyableResourceInstance) Execute(ctx EvalContext, op walkOperatio // https://github.com/hashicorp/terraform/issues/21258 // To avoid an outright crash here, we'll instead return an explicit // error. - var diags tfdiags.Diagnostics diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, "Resource node has no configuration attached", @@ -119,7 +126,7 @@ func (n *NodeApplyableResourceInstance) Execute(ctx EvalContext, op walkOperatio addr, ), )) - return diags.Err() + return diags } // Eval info is different depending on what kind of resource this is @@ -133,93 +140,68 @@ func (n *NodeApplyableResourceInstance) Execute(ctx EvalContext, op walkOperatio } } -func (n *NodeApplyableResourceInstance) dataResourceExecute(ctx EvalContext) error { - addr := n.ResourceInstanceAddr().Resource - - provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider) - if err != nil { - return err +func (n *NodeApplyableResourceInstance) dataResourceExecute(ctx EvalContext) (diags tfdiags.Diagnostics) { + _, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + diags = diags.Append(err) + if diags.HasErrors() { + return diags } change, err := n.readDiff(ctx, providerSchema) - if err != nil { - return err + diags = diags.Append(err) + if diags.HasErrors() { + return diags } // Stop early if we don't actually have a diff if change == nil { - return EvalEarlyExitError{} + return diags } - // In this particular call to EvalReadData we include our planned + // In this particular call to applyDataSource we include our planned // change, which signals that we expect this read to complete fully // with no unknown values; it'll produce an error if not. - var state *states.ResourceInstanceObject - readDataApply := &evalReadDataApply{ - evalReadData{ - Addr: addr, - Config: n.Config, - Planned: &change, - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderMetas: n.ProviderMetas, - ProviderSchema: &providerSchema, - State: &state, - }, - } - _, err = readDataApply.Eval(ctx) - if err != nil { - return err + state, applyDiags := n.applyDataSource(ctx, change) + diags = diags.Append(applyDiags) + if diags.HasErrors() { + return diags } - writeState := &EvalWriteState{ - Addr: addr, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - } - _, err = writeState.Eval(ctx) - if err != nil { - return err + diags = diags.Append(n.writeResourceInstanceState(ctx, state, workingState)) + if diags.HasErrors() { + return diags } - writeDiff := &EvalWriteDiff{ - Addr: addr, - ProviderSchema: &providerSchema, - Change: nil, - } - _, err = writeDiff.Eval(ctx) - if err != nil { - return err - } + diags = diags.Append(n.writeChange(ctx, nil, "")) - UpdateStateHook(ctx) - return nil + diags = diags.Append(updateStateHook(ctx)) + return diags } -func (n *NodeApplyableResourceInstance) managedResourceExecute(ctx EvalContext) error { +func (n *NodeApplyableResourceInstance) managedResourceExecute(ctx EvalContext) (diags tfdiags.Diagnostics) { // Declare a bunch of variables that are used for state during // evaluation. Most of this are written to by-address below. var state *states.ResourceInstanceObject - var createNew bool var createBeforeDestroyEnabled bool var deposedKey states.DeposedKey addr := n.ResourceInstanceAddr().Resource - provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider) - if err != nil { - return err + _, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + diags = diags.Append(err) + if diags.HasErrors() { + return diags } // Get the saved diff for apply diffApply, err := n.readDiff(ctx, providerSchema) - if err != nil { - return err + diags = diags.Append(err) + if diags.HasErrors() { + return diags } // We don't want to do any destroys // (these are handled by NodeDestroyResourceInstance instead) if diffApply == nil || diffApply.Action == plans.Delete { - return EvalEarlyExitError{} + return diags } destroy := (diffApply.Action == plans.Delete || diffApply.Action.IsReplace()) @@ -231,220 +213,225 @@ func (n *NodeApplyableResourceInstance) managedResourceExecute(ctx EvalContext) } if createBeforeDestroyEnabled { - deposeState := &EvalDeposeState{ - Addr: addr, - ForceKey: n.PreallocatedDeposedKey, - OutputKey: &deposedKey, - } - _, err = deposeState.Eval(ctx) - if err != nil { - return err + state := ctx.State() + if n.PreallocatedDeposedKey == states.NotDeposed { + deposedKey = state.DeposeResourceInstanceObject(n.Addr) + } else { + deposedKey = n.PreallocatedDeposedKey + state.DeposeResourceInstanceObjectForceKey(n.Addr, deposedKey) } + log.Printf("[TRACE] managedResourceExecute: prior object for %s now deposed with key %s", n.Addr, deposedKey) } - readState := &EvalReadState{ - Addr: addr, - Provider: &provider, - ProviderSchema: &providerSchema, - - Output: &state, - } - _, err = readState.Eval(ctx) - if err != nil { - return err + state, readDiags := n.readResourceInstanceState(ctx, n.ResourceInstanceAddr()) + diags = diags.Append(readDiags) + if diags.HasErrors() { + return diags } // Get the saved diff diff, err := n.readDiff(ctx, providerSchema) - if err != nil { - return err + diags = diags.Append(err) + if diags.HasErrors() { + return diags } // Make a new diff, in case we've learned new values in the state // during apply which we can now incorporate. - evalDiff := &EvalDiff{ - Addr: addr, - Config: n.Config, - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderMetas: n.ProviderMetas, - ProviderSchema: &providerSchema, - State: &state, - PreviousDiff: &diff, - OutputChange: &diffApply, - OutputState: &state, - } - _, err = evalDiff.Eval(ctx) - if err != nil { - return err + diffApply, _, planDiags := n.plan(ctx, diff, state, false, n.forceReplace) + diags = diags.Append(planDiags) + if diags.HasErrors() { + return diags } // Compare the diffs - checkPlannedChange := &EvalCheckPlannedChange{ - Addr: addr, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - Planned: &diff, - Actual: &diffApply, - } - _, err = checkPlannedChange.Eval(ctx) - if err != nil { - return err + diags = diags.Append(n.checkPlannedChange(ctx, diff, diffApply, providerSchema)) + if diags.HasErrors() { + return diags } - readState = &EvalReadState{ - Addr: addr, - Provider: &provider, - ProviderSchema: &providerSchema, - - Output: &state, - } - _, err = readState.Eval(ctx) - if err != nil { - return err + state, readDiags = n.readResourceInstanceState(ctx, n.ResourceInstanceAddr()) + diags = diags.Append(readDiags) + if diags.HasErrors() { + return diags } - reduceDiff := &EvalReduceDiff{ - Addr: addr, - InChange: &diffApply, - Destroy: false, - OutChange: &diffApply, - } - _, err = reduceDiff.Eval(ctx) - if err != nil { - return err - } - - // EvalReduceDiff may have simplified our planned change + diffApply = reducePlan(addr, diffApply, false) + // reducePlan may have simplified our planned change // into a NoOp if it only requires destroying, since destroying // is handled by NodeDestroyResourceInstance. if diffApply == nil || diffApply.Action == plans.NoOp { - return EvalEarlyExitError{} + return diags } - evalApplyPre := &EvalApplyPre{ - Addr: addr, - State: &state, - Change: &diffApply, - } - _, err = evalApplyPre.Eval(ctx) - if err != nil { - return err - } - - var applyError error - evalApply := &EvalApply{ - Addr: addr, - Config: n.Config, - State: &state, - Change: &diffApply, - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderMetas: n.ProviderMetas, - ProviderSchema: &providerSchema, - Output: &state, - Error: &applyError, - CreateNew: &createNew, - CreateBeforeDestroy: n.CreateBeforeDestroy(), - } - _, err = evalApply.Eval(ctx) - if err != nil { - return err + diags = diags.Append(n.preApplyHook(ctx, diffApply)) + if diags.HasErrors() { + return diags } + state, applyDiags := n.apply(ctx, state, diffApply, n.Config, n.CreateBeforeDestroy()) + diags = diags.Append(applyDiags) + // We clear the change out here so that future nodes don't see a change // that is already complete. - writeDiff := &EvalWriteDiff{ - Addr: addr, - ProviderSchema: &providerSchema, - Change: nil, - } - _, err = writeDiff.Eval(ctx) + err = n.writeChange(ctx, nil, "") if err != nil { - return err + return diags.Append(err) } - evalMaybeTainted := &EvalMaybeTainted{ - Addr: addr, - State: &state, - Change: &diffApply, - Error: &applyError, - } - _, err = evalMaybeTainted.Eval(ctx) - if err != nil { - return err - } + state = maybeTainted(addr.Absolute(ctx.Path()), state, diffApply, diags.Err()) - writeState := &EvalWriteState{ - Addr: addr, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - Dependencies: &n.Dependencies, + if state != nil { + // dependencies are always updated to match the configuration during apply + state.Dependencies = n.Dependencies } - _, err = writeState.Eval(ctx) + err = n.writeResourceInstanceState(ctx, state, workingState) if err != nil { - return err + return diags.Append(err) } - applyProvisioners := &EvalApplyProvisioners{ - Addr: addr, - State: &state, // EvalApplyProvisioners will skip if already tainted - ResourceConfig: n.Config, - CreateNew: &createNew, - Error: &applyError, - When: configs.ProvisionerWhenCreate, - } - _, err = applyProvisioners.Eval(ctx) - if err != nil { - return err - } + // Run Provisioners + createNew := (diffApply.Action == plans.Create || diffApply.Action.IsReplace()) + applyProvisionersDiags := n.evalApplyProvisioners(ctx, state, createNew, configs.ProvisionerWhenCreate) + // the provisioner errors count as port of the apply error, so we can bundle the diags + diags = diags.Append(applyProvisionersDiags) - evalMaybeTainted = &EvalMaybeTainted{ - Addr: addr, - State: &state, - Change: &diffApply, - Error: &applyError, - } - _, err = evalMaybeTainted.Eval(ctx) - if err != nil { - return err - } + state = maybeTainted(addr.Absolute(ctx.Path()), state, diffApply, diags.Err()) - writeState = &EvalWriteState{ - Addr: addr, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - Dependencies: &n.Dependencies, - } - _, err = writeState.Eval(ctx) + err = n.writeResourceInstanceState(ctx, state, workingState) if err != nil { - return err + return diags.Append(err) + } + + if createBeforeDestroyEnabled && diags.HasErrors() { + if deposedKey == states.NotDeposed { + // This should never happen, and so it always indicates a bug. + // We should evaluate this node only if we've previously deposed + // an object as part of the same operation. + if diffApply != nil { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Attempt to restore non-existent deposed object", + fmt.Sprintf( + "Terraform has encountered a bug where it would need to restore a deposed object for %s without knowing a deposed object key for that object. This occurred during a %s action. This is a bug in Terraform; please report it!", + addr, diffApply.Action, + ), + )) + } else { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Attempt to restore non-existent deposed object", + fmt.Sprintf( + "Terraform has encountered a bug where it would need to restore a deposed object for %s without knowing a deposed object key for that object. This is a bug in Terraform; please report it!", + addr, + ), + )) + } + } else { + restored := ctx.State().MaybeRestoreResourceInstanceDeposed(addr.Absolute(ctx.Path()), deposedKey) + if restored { + log.Printf("[TRACE] managedResourceExecute: %s deposed object %s was restored as the current object", addr, deposedKey) + } else { + log.Printf("[TRACE] managedResourceExecute: %s deposed object %s remains deposed", addr, deposedKey) + } + } } - if createBeforeDestroyEnabled && applyError != nil { - maybeRestoreDesposedObject := &EvalMaybeRestoreDeposedObject{ - Addr: addr, - PlannedChange: &diffApply, - Key: &deposedKey, - } - _, err := maybeRestoreDesposedObject.Eval(ctx) - if err != nil { - return err + diags = diags.Append(n.postApplyHook(ctx, state, diags.Err())) + diags = diags.Append(updateStateHook(ctx)) + return diags +} + +// checkPlannedChange produces errors if the _actual_ expected value is not +// compatible with what was recorded in the plan. +// +// Errors here are most often indicative of a bug in the provider, so our error +// messages will report with that in mind. It's also possible that there's a bug +// in Terraform's Core's own "proposed new value" code in EvalDiff. +func (n *NodeApplyableResourceInstance) checkPlannedChange(ctx EvalContext, plannedChange, actualChange *plans.ResourceInstanceChange, providerSchema *ProviderSchema) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + addr := n.ResourceInstanceAddr().Resource + + schema, _ := providerSchema.SchemaForResourceAddr(addr.ContainingResource()) + if schema == nil { + // Should be caught during validation, so we don't bother with a pretty error here + diags = diags.Append(fmt.Errorf("provider does not support %q", addr.Resource.Type)) + return diags + } + + absAddr := addr.Absolute(ctx.Path()) + + log.Printf("[TRACE] checkPlannedChange: Verifying that actual change (action %s) matches planned change (action %s)", actualChange.Action, plannedChange.Action) + + if plannedChange.Action != actualChange.Action { + switch { + case plannedChange.Action == plans.Update && actualChange.Action == plans.NoOp: + // It's okay for an update to become a NoOp once we've filled in + // all of the unknown values, since the final values might actually + // match what was there before after all. + log.Printf("[DEBUG] After incorporating new values learned so far during apply, %s change has become NoOp", absAddr) + + case (plannedChange.Action == plans.CreateThenDelete && actualChange.Action == plans.DeleteThenCreate) || + (plannedChange.Action == plans.DeleteThenCreate && actualChange.Action == plans.CreateThenDelete): + // If the order of replacement changed, then that is a bug in terraform + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Terraform produced inconsistent final plan", + fmt.Sprintf( + "When expanding the plan for %s to include new values learned so far during apply, the planned action changed from %s to %s.\n\nThis is a bug in Terraform and should be reported.", + absAddr, plannedChange.Action, actualChange.Action, + ), + )) + default: + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Provider produced inconsistent final plan", + fmt.Sprintf( + "When expanding the plan for %s to include new values learned so far during apply, provider %q changed the planned action from %s to %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", + absAddr, n.ResolvedProvider.Provider.String(), + plannedChange.Action, actualChange.Action, + ), + )) } } - applyPost := &EvalApplyPost{ - Addr: addr, - State: &state, - Error: &applyError, - } - _, err = applyPost.Eval(ctx) - if err != nil { - return err + errs := objchange.AssertObjectCompatible(schema, plannedChange.After, actualChange.After) + for _, err := range errs { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Provider produced inconsistent final plan", + fmt.Sprintf( + "When expanding the plan for %s to include new values learned so far during apply, provider %q produced an invalid new value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.", + absAddr, n.ResolvedProvider.Provider.String(), tfdiags.FormatError(err), + ), + )) } + return diags +} - UpdateStateHook(ctx) - return nil +// maybeTainted takes the resource addr, new value, planned change, and possible +// error from an apply operation and return a new instance object marked as +// tainted if it appears that a create operation has failed. +func maybeTainted(addr addrs.AbsResourceInstance, state *states.ResourceInstanceObject, change *plans.ResourceInstanceChange, err error) *states.ResourceInstanceObject { + if state == nil || change == nil || err == nil { + return state + } + if state.Status == states.ObjectTainted { + log.Printf("[TRACE] maybeTainted: %s was already tainted, so nothing to do", addr) + return state + } + if change.Action == plans.Create { + // If there are errors during a _create_ then the object is + // in an undefined state, and so we'll mark it as tainted so + // we can try again on the next run. + // + // We don't do this for other change actions because errors + // during updates will often not change the remote object at all. + // If there _were_ changes prior to the error, it's the provider's + // responsibility to record the effect of those changes in the + // object value it returned. + log.Printf("[TRACE] maybeTainted: %s encountered an error during creation, so it is now marked as tainted", addr) + return state.AsTainted() + } + return state } diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply_test.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply_test.go index a841a356..54b65ec4 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply_test.go @@ -23,9 +23,9 @@ func TestNodeApplyableResourceExecute(t *testing.T) { }, Addr: mustAbsResourceAddr("test_instance.foo"), } - err := node.Execute(ctx, walkApply) - if err != nil { - t.Fatalf("unexpected error: %s", err.Error()) + diags := node.Execute(ctx, walkApply) + if diags.HasErrors() { + t.Fatalf("unexpected error: %s", diags.Err()) } if !state.Empty() { t.Fatalf("expected no state, got:\n %s", state.String()) @@ -48,9 +48,9 @@ func TestNodeApplyableResourceExecute(t *testing.T) { }, Addr: mustAbsResourceAddr("test_instance.foo"), } - err := node.Execute(ctx, walkApply) - if err != nil { - t.Fatalf("unexpected error: %s", err.Error()) + diags := node.Execute(ctx, walkApply) + if diags.HasErrors() { + t.Fatalf("unexpected error: %s", diags.Err()) } if state.Empty() { t.Fatal("expected resources in state, got empty state") diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy.go index 872df365..1027b67f 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy.go @@ -5,6 +5,7 @@ import ( "log" "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/tfdiags" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" @@ -42,6 +43,14 @@ func (n *NodeDestroyResourceInstance) Name() string { return n.ResourceInstanceAddr().String() + " (destroy)" } +func (n *NodeDestroyResourceInstance) ProvidedBy() (addr addrs.ProviderConfig, exact bool) { + if n.Addr.Resource.Resource.Mode == addrs.DataResourceMode { + // indicate that this node does not require a configured provider + return nil, true + } + return n.NodeAbstractResourceInstance.ProvidedBy() +} + // GraphNodeDestroyer func (n *NodeDestroyResourceInstance) DestroyAddr() *addrs.AbsResourceInstance { addr := n.ResourceInstanceAddr() @@ -122,7 +131,21 @@ func (n *NodeDestroyResourceInstance) References() []*addrs.Reference { } // GraphNodeExecutable -func (n *NodeDestroyResourceInstance) Execute(ctx EvalContext, op walkOperation) error { +func (n *NodeDestroyResourceInstance) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { + addr := n.ResourceInstanceAddr() + + // Eval info is different depending on what kind of resource this is + switch addr.Resource.Resource.Mode { + case addrs.ManagedResourceMode: + return n.managedResourceExecute(ctx) + case addrs.DataResourceMode: + return n.dataResourceExecute(ctx) + default: + panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode)) + } +} + +func (n *NodeDestroyResourceInstance) managedResourceExecute(ctx EvalContext) (diags tfdiags.Diagnostics) { addr := n.ResourceInstanceAddr() // Get our state @@ -134,133 +157,77 @@ func (n *NodeDestroyResourceInstance) Execute(ctx EvalContext, op walkOperation) // These vars are updated through pointers at various stages below. var changeApply *plans.ResourceInstanceChange var state *states.ResourceInstanceObject - var provisionerErr error - provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider) - if err != nil { - return err + _, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + diags = diags.Append(err) + if diags.HasErrors() { + return diags } changeApply, err = n.readDiff(ctx, providerSchema) - if err != nil { - return err - } - - evalReduceDiff := &EvalReduceDiff{ - Addr: addr.Resource, - InChange: &changeApply, - Destroy: true, - OutChange: &changeApply, - } - _, err = evalReduceDiff.Eval(ctx) - if err != nil { - return err + diags = diags.Append(err) + if diags.HasErrors() { + return diags } - // EvalReduceDiff may have simplified our planned change + changeApply = reducePlan(addr.Resource, changeApply, true) + // reducePlan may have simplified our planned change // into a NoOp if it does not require destroying. if changeApply == nil || changeApply.Action == plans.NoOp { - return EvalEarlyExitError{} + return diags } - state, err = n.ReadResourceInstanceState(ctx, addr) - if err != nil { - return err + state, readDiags := n.readResourceInstanceState(ctx, addr) + diags = diags.Append(readDiags) + if diags.HasErrors() { + return diags } // Exit early if the state object is null after reading the state if state == nil || state.Value.IsNull() { - return EvalEarlyExitError{} + return diags } - evalApplyPre := &EvalApplyPre{ - Addr: addr.Resource, - State: &state, - Change: &changeApply, - } - _, err = evalApplyPre.Eval(ctx) - if err != nil { - return err + diags = diags.Append(n.preApplyHook(ctx, changeApply)) + if diags.HasErrors() { + return diags } // Run destroy provisioners if not tainted - if state != nil && state.Status != states.ObjectTainted { - evalApplyProvisioners := &EvalApplyProvisioners{ - Addr: addr.Resource, - State: &state, - ResourceConfig: n.Config, - Error: &provisionerErr, - When: configs.ProvisionerWhenDestroy, - } - _, err := evalApplyProvisioners.Eval(ctx) - if err != nil { - return err - } - if provisionerErr != nil { + if state.Status != states.ObjectTainted { + applyProvisionersDiags := n.evalApplyProvisioners(ctx, state, false, configs.ProvisionerWhenDestroy) + diags = diags.Append(applyProvisionersDiags) + // keep the diags separate from the main set until we handle the cleanup + + if diags.HasErrors() { // If we have a provisioning error, then we just call // the post-apply hook now. - evalApplyPost := &EvalApplyPost{ - Addr: addr.Resource, - State: &state, - Error: &provisionerErr, - } - _, err = evalApplyPost.Eval(ctx) - if err != nil { - return err - } + diags = diags.Append(n.postApplyHook(ctx, state, diags.Err())) + return diags } } // Managed resources need to be destroyed, while data sources // are only removed from state. - if addr.Resource.Resource.Mode == addrs.ManagedResourceMode { - evalApply := &EvalApply{ - Addr: addr.Resource, - Config: nil, // No configuration because we are destroying - State: &state, - Change: &changeApply, - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderMetas: n.ProviderMetas, - ProviderSchema: &providerSchema, - Output: &state, - Error: &provisionerErr, - } - _, err = evalApply.Eval(ctx) - if err != nil { - return err - } - - evalWriteState := &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - } - _, err = evalWriteState.Eval(ctx) - if err != nil { - return err - } - } else { - log.Printf("[TRACE] NodeDestroyResourceInstance: removing state object for %s", n.Addr) - state := ctx.State() - state.SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider) - } + // we pass a nil configuration to apply because we are destroying + s, d := n.apply(ctx, state, changeApply, nil, false) + state, diags = s, diags.Append(d) + // we don't return immediately here on error, so that the state can be + // finalized - evalApplyPost := &EvalApplyPost{ - Addr: addr.Resource, - State: &state, - Error: &provisionerErr, - } - _, err = evalApplyPost.Eval(ctx) + err = n.writeResourceInstanceState(ctx, state, workingState) if err != nil { - return err + return diags.Append(err) } - err = UpdateStateHook(ctx) - if err != nil { - return err - } + // create the err value for postApplyHook + diags = diags.Append(n.postApplyHook(ctx, state, diags.Err())) + diags = diags.Append(updateStateHook(ctx)) + return diags +} - return nil +func (n *NodeDestroyResourceInstance) dataResourceExecute(ctx EvalContext) (diags tfdiags.Diagnostics) { + log.Printf("[TRACE] NodeDestroyResourceInstance: removing state object for %s", n.Addr) + ctx.State().SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider) + return diags.Append(updateStateHook(ctx)) } diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy_deposed.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy_deposed.go index a77cdf47..a63d49bf 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy_deposed.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy_deposed.go @@ -2,11 +2,13 @@ package terraform import ( "fmt" + "log" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/dag" "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/states" + "github.com/hashicorp/terraform/tfdiags" ) // ConcreteResourceInstanceDeposedNodeFunc is a callback type used to convert @@ -63,55 +65,22 @@ func (n *NodePlanDeposedResourceInstanceObject) References() []*addrs.Reference } // GraphNodeEvalable impl. -func (n *NodePlanDeposedResourceInstanceObject) Execute(ctx EvalContext, op walkOperation) error { - addr := n.ResourceInstanceAddr() - - provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider) - if err != nil { - return err - } - - // During the plan walk we always produce a planned destroy change, because - // destroying is the only supported action for deposed objects. - var change *plans.ResourceInstanceChange - var state *states.ResourceInstanceObject - - readStateDeposed := &EvalReadStateDeposed{ - Addr: addr.Resource, - Output: &state, - Key: n.DeposedKey, - Provider: &provider, - ProviderSchema: &providerSchema, - } - _, err = readStateDeposed.Eval(ctx) - if err != nil { - return err +func (n *NodePlanDeposedResourceInstanceObject) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { + // Read the state for the deposed resource instance + state, err := n.readResourceInstanceStateDeposed(ctx, n.Addr, n.DeposedKey) + diags = diags.Append(err) + if diags.HasErrors() { + return diags } - diffDestroy := &EvalDiffDestroy{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - DeposedKey: n.DeposedKey, - State: &state, - Output: &change, - } - _, err = diffDestroy.Eval(ctx) - if err != nil { - return err + change, destroyPlanDiags := n.planDestroy(ctx, state, n.DeposedKey) + diags = diags.Append(destroyPlanDiags) + if diags.HasErrors() { + return diags } - writeDiff := &EvalWriteDiff{ - Addr: addr.Resource, - DeposedKey: n.DeposedKey, - ProviderSchema: &providerSchema, - Change: &change, - } - _, err = writeDiff.Eval(ctx) - if err != nil { - return err - } - - return nil + diags = diags.Append(n.writeChange(ctx, change, n.DeposedKey)) + return diags } // NodeDestroyDeposedResourceInstanceObject represents deposed resource @@ -181,97 +150,49 @@ func (n *NodeDestroyDeposedResourceInstanceObject) ModifyCreateBeforeDestroy(v b } // GraphNodeExecutable impl. -func (n *NodeDestroyDeposedResourceInstanceObject) Execute(ctx EvalContext, op walkOperation) error { - addr := n.ResourceInstanceAddr().Resource - - var state *states.ResourceInstanceObject +func (n *NodeDestroyDeposedResourceInstanceObject) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { var change *plans.ResourceInstanceChange - var applyError error - provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider) + // Read the state for the deposed resource instance + state, err := n.readResourceInstanceStateDeposed(ctx, n.Addr, n.DeposedKey) if err != nil { - return err + return diags.Append(err) } - readStateDeposed := &EvalReadStateDeposed{ - Addr: addr, - Output: &state, - Key: n.DeposedKey, - Provider: &provider, - ProviderSchema: &providerSchema, - } - _, err = readStateDeposed.Eval(ctx) - if err != nil { - return err + if state == nil { + diags = diags.Append(fmt.Errorf("missing deposed state for %s (%s)", n.Addr, n.DeposedKey)) + return diags } - diffDestroy := &EvalDiffDestroy{ - Addr: addr, - ProviderAddr: n.ResolvedProvider, - State: &state, - Output: &change, - } - _, err = diffDestroy.Eval(ctx) - if err != nil { - return err + change, destroyPlanDiags := n.planDestroy(ctx, state, n.DeposedKey) + diags = diags.Append(destroyPlanDiags) + if diags.HasErrors() { + return diags } // Call pre-apply hook - applyPre := &EvalApplyPre{ - Addr: addr, - State: &state, - Change: &change, - } - _, err = applyPre.Eval(ctx) - if err != nil { - return err + diags = diags.Append(n.preApplyHook(ctx, change)) + if diags.HasErrors() { + return diags } - apply := &EvalApply{ - Addr: addr, - Config: nil, // No configuration because we are destroying - State: &state, - Change: &change, - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - Output: &state, - Error: &applyError, - } - _, err = apply.Eval(ctx) - if err != nil { - return err - } + // we pass a nil configuration to apply because we are destroying + state, applyDiags := n.apply(ctx, state, change, nil, false) + diags = diags.Append(applyDiags) + // don't return immediately on errors, we need to handle the state // Always write the resource back to the state deposed. If it // was successfully destroyed it will be pruned. If it was not, it will // be caught on the next run. - writeStateDeposed := &EvalWriteStateDeposed{ - Addr: addr, - Key: n.DeposedKey, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - } - _, err = writeStateDeposed.Eval(ctx) - if err != nil { - return err + writeDiags := n.writeResourceInstanceState(ctx, state) + diags.Append(writeDiags) + if diags.HasErrors() { + return diags } - applyPost := &EvalApplyPost{ - Addr: addr, - State: &state, - Error: &applyError, - } - _, err = applyPost.Eval(ctx) - if err != nil { - return err - } - if applyError != nil { - return applyError - } - UpdateStateHook(ctx) - return nil + diags = diags.Append(n.postApplyHook(ctx, state, diags.Err())) + + return diags.Append(updateStateHook(ctx)) } // GraphNodeDeposer is an optional interface implemented by graph nodes that @@ -295,3 +216,46 @@ type graphNodeDeposer struct { func (n *graphNodeDeposer) SetPreallocatedDeposedKey(key states.DeposedKey) { n.PreallocatedDeposedKey = key } + +func (n *NodeDestroyDeposedResourceInstanceObject) writeResourceInstanceState(ctx EvalContext, obj *states.ResourceInstanceObject) error { + absAddr := n.Addr + key := n.DeposedKey + state := ctx.State() + + if key == states.NotDeposed { + // should never happen + return fmt.Errorf("can't save deposed object for %s without a deposed key; this is a bug in Terraform that should be reported", absAddr) + } + + if obj == nil { + // No need to encode anything: we'll just write it directly. + state.SetResourceInstanceDeposed(absAddr, key, nil, n.ResolvedProvider) + log.Printf("[TRACE] writeResourceInstanceStateDeposed: removing state object for %s deposed %s", absAddr, key) + return nil + } + + _, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + if err != nil { + return err + } + if providerSchema == nil { + // Should never happen, unless our state object is nil + panic("writeResourceInstanceStateDeposed used with no ProviderSchema object") + } + + schema, currentVersion := providerSchema.SchemaForResourceAddr(absAddr.ContainingResource().Resource) + if schema == nil { + // It shouldn't be possible to get this far in any real scenario + // without a schema, but we might end up here in contrived tests that + // fail to set up their world properly. + return fmt.Errorf("failed to encode %s in state: no resource type schema available", absAddr) + } + src, err := obj.Encode(schema.ImpliedType(), currentVersion) + if err != nil { + return fmt.Errorf("failed to encode %s in state: %s", absAddr, err) + } + + log.Printf("[TRACE] writeResourceInstanceStateDeposed: writing state object for %s deposed %s", absAddr, key) + state.SetResourceInstanceDeposed(absAddr, key, src, n.ResolvedProvider) + return nil +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy_deposed_test.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy_deposed_test.go index 584dab5a..1f3057bb 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy_deposed_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy_deposed_test.go @@ -26,7 +26,7 @@ func TestNodePlanDeposedResourceInstanceObject_Execute(t *testing.T) { ) p := testProvider("test") - p.UpgradeResourceStateResponse = providers.UpgradeResourceStateResponse{ + p.UpgradeResourceStateResponse = &providers.UpgradeResourceStateResponse{ UpgradedState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("bar"), }), @@ -85,28 +85,32 @@ func TestNodeDestroyDeposedResourceInstanceObject_Execute(t *testing.T) { mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), ) + schema := &ProviderSchema{ + ResourceTypes: map[string]*configschema.Block{ + "test_instance": { + Attributes: map[string]*configschema.Attribute{ + "id": { + Type: cty.String, + Computed: true, + }, + }, + }, + }, + } + p := testProvider("test") - p.UpgradeResourceStateResponse = providers.UpgradeResourceStateResponse{ + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema) + + p.UpgradeResourceStateResponse = &providers.UpgradeResourceStateResponse{ UpgradedState: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("bar"), }), } ctx := &MockEvalContext{ - StateState: state.SyncWrapper(), - ProviderProvider: p, - ProviderSchemaSchema: &ProviderSchema{ - ResourceTypes: map[string]*configschema.Block{ - "test_instance": { - Attributes: map[string]*configschema.Attribute{ - "id": { - Type: cty.String, - Computed: true, - }, - }, - }, - }, - }, - ChangesChanges: plans.NewChanges().SyncWrapper(), + StateState: state.SyncWrapper(), + ProviderProvider: p, + ProviderSchemaSchema: schema, + ChangesChanges: plans.NewChanges().SyncWrapper(), } node := NodeDestroyDeposedResourceInstanceObject{ @@ -128,3 +132,72 @@ func TestNodeDestroyDeposedResourceInstanceObject_Execute(t *testing.T) { t.Fatalf("resources left in state after destroy") } } + +func TestNodeDestroyDeposedResourceInstanceObject_WriteResourceInstanceState(t *testing.T) { + state := states.NewState() + ctx := new(MockEvalContext) + ctx.StateState = state.SyncWrapper() + ctx.PathPath = addrs.RootModuleInstance + mockProvider := mockProviderWithResourceTypeSchema("aws_instance", &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": { + Type: cty.String, + Optional: true, + }, + }, + }) + ctx.ProviderProvider = mockProvider + ctx.ProviderSchemaSchema = mockProvider.ProviderSchema() + + obj := &states.ResourceInstanceObject{ + Value: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-abc123"), + }), + Status: states.ObjectReady, + } + node := &NodeDestroyDeposedResourceInstanceObject{ + NodeAbstractResourceInstance: &NodeAbstractResourceInstance{ + NodeAbstractResource: NodeAbstractResource{ + ResolvedProvider: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + }, + Addr: mustResourceInstanceAddr("aws_instance.foo"), + }, + DeposedKey: states.NewDeposedKey(), + } + err := node.writeResourceInstanceState(ctx, obj) + if err != nil { + t.Fatalf("unexpected error: %s", err.Error()) + } + + checkStateString(t, state, ` +aws_instance.foo: (1 deposed) + ID = + provider = provider["registry.terraform.io/hashicorp/aws"] + Deposed ID 1 = i-abc123 + `) +} + +func TestNodeDestroyDeposedResourceInstanceObject_ExecuteMissingState(t *testing.T) { + p := simpleMockProvider() + ctx := &MockEvalContext{ + StateState: states.NewState().SyncWrapper(), + ProviderProvider: simpleMockProvider(), + ProviderSchemaSchema: p.ProviderSchema(), + ChangesChanges: plans.NewChanges().SyncWrapper(), + } + + node := NodeDestroyDeposedResourceInstanceObject{ + NodeAbstractResourceInstance: &NodeAbstractResourceInstance{ + Addr: mustResourceInstanceAddr("test_object.foo"), + NodeAbstractResource: NodeAbstractResource{ + ResolvedProvider: mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + }, + }, + DeposedKey: states.NewDeposedKey(), + } + err := node.Execute(ctx, walkApply) + + if err == nil { + t.Fatal("expected error") + } +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan.go index 466c3a87..a946527e 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan.go @@ -1,7 +1,9 @@ package terraform import ( + "fmt" "log" + "strings" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/dag" @@ -24,6 +26,16 @@ type nodeExpandPlannableResource struct { // skipRefresh indicates that we should skip refreshing individual instances skipRefresh bool + // skipPlanChanges indicates we should skip trying to plan change actions + // for any instances. + skipPlanChanges bool + + // forceReplace are resource instance addresses where the user wants to + // force generating a replace action. This set isn't pre-filtered, so + // it might contain addresses that have nothing to do with the resource + // that this node represents, which the node itself must therefore ignore. + forceReplace []addrs.AbsResourceInstance + // We attach dependencies to the Resource during refresh, since the // instances are instantiated during DynamicExpand. dependencies []addrs.ConfigResource @@ -73,19 +85,19 @@ func (n *nodeExpandPlannableResource) DynamicExpand(ctx EvalContext) (*Graph, er var g Graph expander := ctx.InstanceExpander() - var resources []addrs.AbsResource moduleInstances := expander.ExpandModule(n.Addr.Module) // Add the current expanded resource to the graph for _, module := range moduleInstances { resAddr := n.Addr.Resource.Absolute(module) - resources = append(resources, resAddr) g.Add(&NodePlannableResource{ NodeAbstractResource: n.NodeAbstractResource, Addr: resAddr, ForceCreateBeforeDestroy: n.ForceCreateBeforeDestroy, dependencies: n.dependencies, skipRefresh: n.skipRefresh, + skipPlanChanges: n.skipPlanChanges, + forceReplace: n.forceReplace, }) } @@ -151,6 +163,16 @@ type NodePlannableResource struct { // skipRefresh indicates that we should skip refreshing individual instances skipRefresh bool + // skipPlanChanges indicates we should skip trying to plan change actions + // for any instances. + skipPlanChanges bool + + // forceReplace are resource instance addresses where the user wants to + // force generating a replace action. This set isn't pre-filtered, so + // it might contain addresses that have nothing to do with the resource + // that this node represents, which the node itself must therefore ignore. + forceReplace []addrs.AbsResourceInstance + dependencies []addrs.ConfigResource } @@ -172,21 +194,90 @@ func (n *NodePlannableResource) Name() string { return n.Addr.String() } -// GraphNodeModuleInstance -func (n *NodePlannableResource) ModuleInstance() addrs.ModuleInstance { - return n.Addr.Module -} - // GraphNodeExecutable -func (n *NodePlannableResource) Execute(ctx EvalContext, op walkOperation) error { +func (n *NodePlannableResource) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + if n.Config == nil { // Nothing to do, then. log.Printf("[TRACE] NodeApplyableResource: no configuration present for %s", n.Name()) - return nil + return diags + } + + // writeResourceState is responsible for informing the expander of what + // repetition mode this resource has, which allows expander.ExpandResource + // to work below. + moreDiags := n.writeResourceState(ctx, n.Addr) + diags = diags.Append(moreDiags) + if moreDiags.HasErrors() { + return diags + } + + // Before we expand our resource into potentially many resource instances, + // we'll verify that any mention of this resource in n.forceReplace is + // consistent with the repetition mode of the resource. In other words, + // we're aiming to catch a situation where naming a particular resource + // instance would require an instance key but the given address has none. + expander := ctx.InstanceExpander() + instanceAddrs := expander.ExpandResource(n.ResourceAddr().Absolute(ctx.Path())) + + // If there's a number of instances other than 1 then we definitely need + // an index. + mustHaveIndex := len(instanceAddrs) != 1 + // If there's only one instance then we might still need an index, if the + // instance address has one. + if len(instanceAddrs) == 1 && instanceAddrs[0].Resource.Key != addrs.NoKey { + mustHaveIndex = true + } + if mustHaveIndex { + for _, candidateAddr := range n.forceReplace { + if candidateAddr.Resource.Key == addrs.NoKey { + if n.Addr.Resource.Equal(candidateAddr.Resource.Resource) { + switch { + case len(instanceAddrs) == 0: + // In this case there _are_ no instances to replace, so + // there isn't any alternative address for us to suggest. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Warning, + "Incompletely-matched force-replace resource instance", + fmt.Sprintf( + "Your force-replace request for %s doesn't match any resource instances because this resource doesn't have any instances.", + candidateAddr, + ), + )) + case len(instanceAddrs) == 1: + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Warning, + "Incompletely-matched force-replace resource instance", + fmt.Sprintf( + "Your force-replace request for %s doesn't match any resource instances because it lacks an instance key.\n\nTo force replacement of the single declared instance, use the following option instead:\n -replace=%q", + candidateAddr, instanceAddrs[0], + ), + )) + default: + var possibleValidOptions strings.Builder + for _, addr := range instanceAddrs { + fmt.Fprintf(&possibleValidOptions, "\n -replace=%q", addr) + } + + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Warning, + "Incompletely-matched force-replace resource instance", + fmt.Sprintf( + "Your force-replace request for %s doesn't match any resource instances because it lacks an instance key.\n\nTo force replacement of particular instances, use one or more of the following options instead:%s", + candidateAddr, possibleValidOptions.String(), + ), + )) + } + } + } + } } + // NOTE: The actual interpretation of n.forceReplace to produce replace + // actions is in NodeAbstractResourceInstance.plan, because we must do so + // on a per-instance basis rather than for the whole resource. - err := n.writeResourceState(ctx, n.Addr) - return err + return diags } // GraphNodeDestroyerCBD @@ -247,6 +338,8 @@ func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) { // nodes that have it. ForceCreateBeforeDestroy: n.CreateBeforeDestroy(), skipRefresh: n.skipRefresh, + skipPlanChanges: n.skipPlanChanges, + forceReplace: n.forceReplace, } } @@ -261,6 +354,7 @@ func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) { return &NodePlannableResourceInstanceOrphan{ NodeAbstractResourceInstance: a, + skipRefresh: n.skipRefresh, } } diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_destroy.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_destroy.go index 0118815b..bb731d8c 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_destroy.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_destroy.go @@ -4,12 +4,16 @@ import ( "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/states" + "github.com/hashicorp/terraform/tfdiags" ) // NodePlanDestroyableResourceInstance represents a resource that is ready // to be planned for destruction. type NodePlanDestroyableResourceInstance struct { *NodeAbstractResourceInstance + + // skipRefresh indicates that we should skip refreshing + skipRefresh bool } var ( @@ -32,7 +36,7 @@ func (n *NodePlanDestroyableResourceInstance) DestroyAddr() *addrs.AbsResourceIn } // GraphNodeEvalable -func (n *NodePlanDestroyableResourceInstance) Execute(ctx EvalContext, op walkOperation) error { +func (n *NodePlanDestroyableResourceInstance) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { addr := n.ResourceInstanceAddr() // Declare a bunch of variables that are used for state during @@ -41,37 +45,43 @@ func (n *NodePlanDestroyableResourceInstance) Execute(ctx EvalContext, op walkOp var change *plans.ResourceInstanceChange var state *states.ResourceInstanceObject - _, providerSchema, err := GetProvider(ctx, n.ResolvedProvider) - if err != nil { - return err + state, err := n.readResourceInstanceState(ctx, addr) + diags = diags.Append(err) + if diags.HasErrors() { + return diags } - state, err = n.ReadResourceInstanceState(ctx, addr) - if err != nil { - return err + // If we are in the "skip refresh" mode then we will have skipped over our + // usual opportunity to update the previous run state and refresh state + // with the result of any provider schema upgrades, so we'll compensate + // by doing that here. + // + // NOTE: this is coupled with logic in Context.destroyPlan which skips + // running a normal plan walk when refresh is enabled. These two + // conditionals must agree (be exactly opposite) in order to get the + // correct behavior in both cases. + if n.skipRefresh { + diags = diags.Append(n.writeResourceInstanceState(ctx, state, prevRunState)) + if diags.HasErrors() { + return diags + } + diags = diags.Append(n.writeResourceInstanceState(ctx, state, refreshState)) + if diags.HasErrors() { + return diags + } } - diffDestroy := &EvalDiffDestroy{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - State: &state, - Output: &change, - } - _, err = diffDestroy.Eval(ctx) - if err != nil { - return err + change, destroyPlanDiags := n.planDestroy(ctx, state, "") + diags = diags.Append(destroyPlanDiags) + if diags.HasErrors() { + return diags } - err = n.checkPreventDestroy(change) - if err != nil { - return err + diags = diags.Append(n.checkPreventDestroy(change)) + if diags.HasErrors() { + return diags } - writeDiff := &EvalWriteDiff{ - Addr: addr.Resource, - ProviderSchema: &providerSchema, - Change: &change, - } - _, err = writeDiff.Eval(ctx) - return err + diags = diags.Append(n.writeChange(ctx, change, "")) + return diags } diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_instance.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_instance.go index 3ffe9946..d9603326 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_instance.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_instance.go @@ -2,9 +2,12 @@ package terraform import ( "fmt" + "log" + "sort" "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/states" + "github.com/hashicorp/terraform/tfdiags" "github.com/hashicorp/terraform/addrs" ) @@ -15,7 +18,19 @@ import ( type NodePlannableResourceInstance struct { *NodeAbstractResourceInstance ForceCreateBeforeDestroy bool - skipRefresh bool + + // skipRefresh indicates that we should skip refreshing individual instances + skipRefresh bool + + // skipPlanChanges indicates we should skip trying to plan change actions + // for any instances. + skipPlanChanges bool + + // forceReplace are resource instance addresses where the user wants to + // force generating a replace action. This set isn't pre-filtered, so + // it might contain addresses that have nothing to do with the resource + // that this node represents, which the node itself must therefore ignore. + forceReplace []addrs.AbsResourceInstance } var ( @@ -30,13 +45,13 @@ var ( ) // GraphNodeEvalable -func (n *NodePlannableResourceInstance) Execute(ctx EvalContext, op walkOperation) error { +func (n *NodePlannableResourceInstance) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics { addr := n.ResourceInstanceAddr() // Eval info is different depending on what kind of resource this is switch addr.Resource.Resource.Mode { case addrs.ManagedResourceMode: - return n.managedResourceExecute(ctx, n.skipRefresh) + return n.managedResourceExecute(ctx) case addrs.DataResourceMode: return n.dataResourceExecute(ctx) default: @@ -44,192 +59,233 @@ func (n *NodePlannableResourceInstance) Execute(ctx EvalContext, op walkOperatio } } -func (n *NodePlannableResourceInstance) dataResourceExecute(ctx EvalContext) error { +func (n *NodePlannableResourceInstance) dataResourceExecute(ctx EvalContext) (diags tfdiags.Diagnostics) { config := n.Config addr := n.ResourceInstanceAddr() var change *plans.ResourceInstanceChange - var state *states.ResourceInstanceObject - provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider) - if err != nil { - return err + _, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + diags = diags.Append(err) + if diags.HasErrors() { + return diags } - state, err = n.ReadResourceInstanceState(ctx, addr) - if err != nil { - return err + state, readDiags := n.readResourceInstanceState(ctx, addr) + diags = diags.Append(readDiags) + if diags.HasErrors() { + return diags } - validateSelfRef := &EvalValidateSelfRef{ - Addr: addr.Resource, - Config: config.Config, - ProviderSchema: &providerSchema, - } - _, err = validateSelfRef.Eval(ctx) - if err != nil { - return err + // We'll save a snapshot of what we just read from the state into the + // prevRunState which will capture the result read in the previous + // run, possibly tweaked by any upgrade steps that + // readResourceInstanceState might've made. + // However, note that we don't have any explicit mechanism for upgrading + // data resource results as we do for managed resources, and so the + // prevRunState might not conform to the current schema if the + // previous run was with a different provider version. + diags = diags.Append(n.writeResourceInstanceState(ctx, state, prevRunState)) + if diags.HasErrors() { + return diags } - readDataPlan := &evalReadDataPlan{ - evalReadData: evalReadData{ - Addr: addr.Resource, - Config: n.Config, - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderMetas: n.ProviderMetas, - ProviderSchema: &providerSchema, - OutputChange: &change, - State: &state, - dependsOn: n.dependsOn, - }, + diags = diags.Append(validateSelfRef(addr.Resource, config.Config, providerSchema)) + if diags.HasErrors() { + return diags } - _, err = readDataPlan.Eval(ctx) - if err != nil { - return err + + change, state, planDiags := n.planDataSource(ctx, state) + diags = diags.Append(planDiags) + if diags.HasErrors() { + return diags } // write the data source into both the refresh state and the // working state - writeRefreshState := &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - targetState: refreshState, + diags = diags.Append(n.writeResourceInstanceState(ctx, state, refreshState)) + if diags.HasErrors() { + return diags } - _, err = writeRefreshState.Eval(ctx) - if err != nil { - return err + diags = diags.Append(n.writeResourceInstanceState(ctx, state, workingState)) + if diags.HasErrors() { + return diags } - writeState := &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - } - _, err = writeState.Eval(ctx) - if err != nil { - return err - } - - writeDiff := &EvalWriteDiff{ - Addr: addr.Resource, - ProviderSchema: &providerSchema, - Change: &change, - } - _, err = writeDiff.Eval(ctx) - return err + diags = diags.Append(n.writeChange(ctx, change, "")) + return diags } -func (n *NodePlannableResourceInstance) managedResourceExecute(ctx EvalContext, skipRefresh bool) error { +func (n *NodePlannableResourceInstance) managedResourceExecute(ctx EvalContext) (diags tfdiags.Diagnostics) { config := n.Config addr := n.ResourceInstanceAddr() var change *plans.ResourceInstanceChange var instanceRefreshState *states.ResourceInstanceObject - var instancePlanState *states.ResourceInstanceObject - provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider) - if err != nil { - return err + _, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + diags = diags.Append(err) + if diags.HasErrors() { + return diags } - validateSelfRef := &EvalValidateSelfRef{ - Addr: addr.Resource, - Config: config.Config, - ProviderSchema: &providerSchema, + diags = diags.Append(validateSelfRef(addr.Resource, config.Config, providerSchema)) + if diags.HasErrors() { + return diags } - _, err = validateSelfRef.Eval(ctx) - if err != nil { - return err + + instanceRefreshState, readDiags := n.readResourceInstanceState(ctx, addr) + diags = diags.Append(readDiags) + if diags.HasErrors() { + return diags } - instanceRefreshState, err = n.ReadResourceInstanceState(ctx, addr) - if err != nil { - return err + // We'll save a snapshot of what we just read from the state into the + // prevRunState before we do anything else, since this will capture the + // result of any schema upgrading that readResourceInstanceState just did, + // but not include any out-of-band changes we might detect in in the + // refresh step below. + diags = diags.Append(n.writeResourceInstanceState(ctx, instanceRefreshState, prevRunState)) + if diags.HasErrors() { + return diags } - refreshLifecycle := &EvalRefreshLifecycle{ - Addr: addr, - Config: n.Config, - State: &instanceRefreshState, - ForceCreateBeforeDestroy: n.ForceCreateBeforeDestroy, + // Also the refreshState, because that should still reflect schema upgrades + // even if it doesn't reflect upstream changes. + diags = diags.Append(n.writeResourceInstanceState(ctx, instanceRefreshState, refreshState)) + if diags.HasErrors() { + return diags } - _, err = refreshLifecycle.Eval(ctx) - if err != nil { - return err + + // In 0.13 we could be refreshing a resource with no config. + // We should be operating on managed resource, but check here to be certain + if n.Config == nil || n.Config.Managed == nil { + log.Printf("[WARN] managedResourceExecute: no Managed config value found in instance state for %q", n.Addr) + } else { + if instanceRefreshState != nil { + instanceRefreshState.CreateBeforeDestroy = n.Config.Managed.CreateBeforeDestroy || n.ForceCreateBeforeDestroy + } } // Refresh, maybe - if !skipRefresh { - refresh := &EvalRefresh{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - Provider: &provider, - ProviderMetas: n.ProviderMetas, - ProviderSchema: &providerSchema, - State: &instanceRefreshState, - Output: &instanceRefreshState, + if !n.skipRefresh { + s, refreshDiags := n.refresh(ctx, instanceRefreshState) + diags = diags.Append(refreshDiags) + if diags.HasErrors() { + return diags + } + + instanceRefreshState = s + + if instanceRefreshState != nil { + // When refreshing we start by merging the stored dependencies and + // the configured dependencies. The configured dependencies will be + // stored to state once the changes are applied. If the plan + // results in no changes, we will re-write these dependencies + // below. + instanceRefreshState.Dependencies = mergeDeps(n.Dependencies, instanceRefreshState.Dependencies) + } + + diags = diags.Append(n.writeResourceInstanceState(ctx, instanceRefreshState, refreshState)) + if diags.HasErrors() { + return diags + } + } + + // Plan the instance, unless we're in the refresh-only mode + if !n.skipPlanChanges { + change, instancePlanState, planDiags := n.plan( + ctx, change, instanceRefreshState, n.ForceCreateBeforeDestroy, n.forceReplace, + ) + diags = diags.Append(planDiags) + if diags.HasErrors() { + return diags } - _, err = refresh.Eval(ctx) - if err != nil { - return err + + diags = diags.Append(n.checkPreventDestroy(change)) + if diags.HasErrors() { + return diags } - writeRefreshState := &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &instanceRefreshState, - targetState: refreshState, - Dependencies: &n.Dependencies, + diags = diags.Append(n.writeResourceInstanceState(ctx, instancePlanState, workingState)) + if diags.HasErrors() { + return diags } - _, err = writeRefreshState.Eval(ctx) - if err != nil { - return err + + // If this plan resulted in a NoOp, then apply won't have a chance to make + // any changes to the stored dependencies. Since this is a NoOp we know + // that the stored dependencies will have no effect during apply, and we can + // write them out now. + if change.Action == plans.NoOp && !depsEqual(instanceRefreshState.Dependencies, n.Dependencies) { + // the refresh state will be the final state for this resource, so + // finalize the dependencies here if they need to be updated. + instanceRefreshState.Dependencies = n.Dependencies + diags = diags.Append(n.writeResourceInstanceState(ctx, instanceRefreshState, refreshState)) + if diags.HasErrors() { + return diags + } } + + diags = diags.Append(n.writeChange(ctx, change, "")) + } else { + // Even if we don't plan changes, we do still need to at least update + // the working state to reflect the refresh result. If not, then e.g. + // any output values refering to this will not react to the drift. + // (Even if we didn't actually refresh above, this will still save + // the result of any schema upgrading we did in readResourceInstanceState.) + diags = diags.Append(n.writeResourceInstanceState(ctx, instanceRefreshState, workingState)) + if diags.HasErrors() { + return diags + } + } + + return diags +} + +// mergeDeps returns the union of 2 sets of dependencies +func mergeDeps(a, b []addrs.ConfigResource) []addrs.ConfigResource { + switch { + case len(a) == 0: + return b + case len(b) == 0: + return a } - // Plan the instance - diff := &EvalDiff{ - Addr: addr.Resource, - Config: n.Config, - CreateBeforeDestroy: n.ForceCreateBeforeDestroy, - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderMetas: n.ProviderMetas, - ProviderSchema: &providerSchema, - State: &instanceRefreshState, - OutputChange: &change, - OutputState: &instancePlanState, + set := make(map[string]addrs.ConfigResource) + + for _, dep := range a { + set[dep.String()] = dep } - _, err = diff.Eval(ctx) - if err != nil { - return err + + for _, dep := range b { + set[dep.String()] = dep } - err = n.checkPreventDestroy(change) - if err != nil { - return err + newDeps := make([]addrs.ConfigResource, 0, len(set)) + for _, dep := range set { + newDeps = append(newDeps, dep) } - writeState := &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - State: &instancePlanState, - ProviderSchema: &providerSchema, + return newDeps +} + +func depsEqual(a, b []addrs.ConfigResource) bool { + if len(a) != len(b) { + return false } - _, err = writeState.Eval(ctx) - if err != nil { - return err + + less := func(s []addrs.ConfigResource) func(i, j int) bool { + return func(i, j int) bool { + return s[i].String() < s[j].String() + } } - writeDiff := &EvalWriteDiff{ - Addr: addr.Resource, - ProviderSchema: &providerSchema, - Change: &change, + sort.Slice(a, less(a)) + sort.Slice(b, less(b)) + + for i := range a { + if !a[i].Equal(b[i]) { + return false + } } - _, err = writeDiff.Eval(ctx) - return err + return true } diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_orphan.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_orphan.go index ca020005..74a4bbb7 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_orphan.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_orphan.go @@ -1,14 +1,25 @@ package terraform import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/plans" - "github.com/hashicorp/terraform/states" + "github.com/hashicorp/terraform/tfdiags" ) // NodePlannableResourceInstanceOrphan represents a resource that is "applyable": // it is ready to be applied and is represented by a diff. type NodePlannableResourceInstanceOrphan struct { *NodeAbstractResourceInstance + + // skipRefresh indicates that we should skip refreshing individual instances + skipRefresh bool + + // skipPlanChanges indicates we should skip trying to plan change actions + // for any instances. + skipPlanChanges bool } var ( @@ -20,6 +31,7 @@ var ( _ GraphNodeAttachResourceConfig = (*NodePlannableResourceInstanceOrphan)(nil) _ GraphNodeAttachResourceState = (*NodePlannableResourceInstanceOrphan)(nil) _ GraphNodeExecutable = (*NodePlannableResourceInstanceOrphan)(nil) + _ GraphNodeProviderConsumer = (*NodePlannableResourceInstanceOrphan)(nil) ) func (n *NodePlannableResourceInstanceOrphan) Name() string { @@ -27,60 +39,107 @@ func (n *NodePlannableResourceInstanceOrphan) Name() string { } // GraphNodeExecutable -func (n *NodePlannableResourceInstanceOrphan) Execute(ctx EvalContext, op walkOperation) error { +func (n *NodePlannableResourceInstanceOrphan) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics { addr := n.ResourceInstanceAddr() - // Declare a bunch of variables that are used for state during - // evaluation. These are written to by-address below. - var change *plans.ResourceInstanceChange - var state *states.ResourceInstanceObject - - _, providerSchema, err := GetProvider(ctx, n.ResolvedProvider) - if err != nil { - return err + // Eval info is different depending on what kind of resource this is + switch addr.Resource.Resource.Mode { + case addrs.ManagedResourceMode: + return n.managedResourceExecute(ctx) + case addrs.DataResourceMode: + return n.dataResourceExecute(ctx) + default: + panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode)) } +} - state, err = n.ReadResourceInstanceState(ctx, addr) - if err != nil { - return err +func (n *NodePlannableResourceInstanceOrphan) ProvidedBy() (addr addrs.ProviderConfig, exact bool) { + if n.Addr.Resource.Resource.Mode == addrs.DataResourceMode { + // indicate that this node does not require a configured provider + return nil, true } + return n.NodeAbstractResourceInstance.ProvidedBy() +} - diffDestroy := &EvalDiffDestroy{ - Addr: addr.Resource, - State: &state, - ProviderAddr: n.ResolvedProvider, - Output: &change, - OutputState: &state, // Will point to a nil state after this complete, signalling destroyed - } - _, err = diffDestroy.Eval(ctx) - if err != nil { - return err - } +func (n *NodePlannableResourceInstanceOrphan) dataResourceExecute(ctx EvalContext) tfdiags.Diagnostics { + // A data source that is no longer in the config is removed from the state + log.Printf("[TRACE] NodePlannableResourceInstanceOrphan: removing state object for %s", n.Addr) + + // we need to update both the refresh state to refresh the current data + // source, and the working state for plan-time evaluations. + refreshState := ctx.RefreshState() + refreshState.SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider) + + workingState := ctx.State() + workingState.SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider) + return nil +} + +func (n *NodePlannableResourceInstanceOrphan) managedResourceExecute(ctx EvalContext) (diags tfdiags.Diagnostics) { + addr := n.ResourceInstanceAddr() - err = n.checkPreventDestroy(change) - if err != nil { - return err + oldState, readDiags := n.readResourceInstanceState(ctx, addr) + diags = diags.Append(readDiags) + if diags.HasErrors() { + return diags } - writeDiff := &EvalWriteDiff{ - Addr: addr.Resource, - ProviderSchema: &providerSchema, - Change: &change, + // Note any upgrades that readResourceInstanceState might've done in the + // prevRunState, so that it'll conform to current schema. + diags = diags.Append(n.writeResourceInstanceState(ctx, oldState, prevRunState)) + if diags.HasErrors() { + return diags } - _, err = writeDiff.Eval(ctx) - if err != nil { - return err + // Also the refreshState, because that should still reflect schema upgrades + // even if not refreshing. + diags = diags.Append(n.writeResourceInstanceState(ctx, oldState, refreshState)) + if diags.HasErrors() { + return diags } - writeState := &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, + if !n.skipRefresh { + // Refresh this instance even though it is going to be destroyed, in + // order to catch missing resources. If this is a normal plan, + // providers expect a Read request to remove missing resources from the + // plan before apply, and may not handle a missing resource during + // Delete correctly. If this is a simple refresh, Terraform is + // expected to remove the missing resource from the state entirely + refreshedState, refreshDiags := n.refresh(ctx, oldState) + diags = diags.Append(refreshDiags) + if diags.HasErrors() { + return diags + } + + diags = diags.Append(n.writeResourceInstanceState(ctx, refreshedState, refreshState)) + if diags.HasErrors() { + return diags + } + + // If we refreshed then our subsequent planning should be in terms of + // the new object, not the original object. + oldState = refreshedState } - _, err = writeState.Eval(ctx) - if err != nil { - return err + + if !n.skipPlanChanges { + var change *plans.ResourceInstanceChange + change, destroyPlanDiags := n.planDestroy(ctx, oldState, "") + diags = diags.Append(destroyPlanDiags) + if diags.HasErrors() { + return diags + } + + diags = diags.Append(n.checkPreventDestroy(change)) + if diags.HasErrors() { + return diags + } + + diags = diags.Append(n.writeChange(ctx, change, "")) + if diags.HasErrors() { + return diags + } + + diags = diags.Append(n.writeResourceInstanceState(ctx, nil, workingState)) } - return nil + + return diags } diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_orphan_test.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_orphan_test.go index 45078cd4..df663aba 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_orphan_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_orphan_test.go @@ -7,7 +7,9 @@ import ( "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/instances" "github.com/hashicorp/terraform/plans" + "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/states" + "github.com/zclconf/go-cty/cty" ) func TestNodeResourcePlanOrphanExecute(t *testing.T) { @@ -33,6 +35,8 @@ func TestNodeResourcePlanOrphanExecute(t *testing.T) { p := simpleMockProvider() ctx := &MockEvalContext{ StateState: state.SyncWrapper(), + RefreshStateState: state.DeepCopy().SyncWrapper(), + PrevRunStateState: state.DeepCopy().SyncWrapper(), InstanceExpanderExpander: instances.NewExpander(), ProviderProvider: p, ProviderSchemaSchema: &ProviderSchema{ @@ -54,11 +58,89 @@ func TestNodeResourcePlanOrphanExecute(t *testing.T) { Addr: mustResourceInstanceAddr("test_object.foo"), }, } - err := node.Execute(ctx, walkApply) - if err != nil { - t.Fatalf("unexpected error: %s", err.Error()) + diags := node.Execute(ctx, walkApply) + if diags.HasErrors() { + t.Fatalf("unexpected error: %s", diags.Err()) } if !state.Empty() { t.Fatalf("expected empty state, got %s", state.String()) } } + +func TestNodeResourcePlanOrphanExecute_alreadyDeleted(t *testing.T) { + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_object", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + state := states.NewState() + state.Module(addrs.RootModuleInstance).SetResourceInstanceCurrent( + addr.Resource, + &states.ResourceInstanceObjectSrc{ + AttrsFlat: map[string]string{ + "test_string": "foo", + }, + Status: states.ObjectReady, + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + refreshState := state.DeepCopy() + prevRunState := state.DeepCopy() + changes := plans.NewChanges() + + p := simpleMockProvider() + p.ReadResourceResponse = &providers.ReadResourceResponse{ + NewState: cty.NullVal(p.GetProviderSchemaResponse.ResourceTypes["test_string"].Block.ImpliedType()), + } + ctx := &MockEvalContext{ + StateState: state.SyncWrapper(), + RefreshStateState: refreshState.SyncWrapper(), + PrevRunStateState: prevRunState.SyncWrapper(), + InstanceExpanderExpander: instances.NewExpander(), + ProviderProvider: p, + ProviderSchemaSchema: &ProviderSchema{ + ResourceTypes: map[string]*configschema.Block{ + "test_object": simpleTestSchema(), + }, + }, + ChangesChanges: changes.SyncWrapper(), + } + + node := NodePlannableResourceInstanceOrphan{ + NodeAbstractResourceInstance: &NodeAbstractResourceInstance{ + NodeAbstractResource: NodeAbstractResource{ + ResolvedProvider: addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + }, + Addr: mustResourceInstanceAddr("test_object.foo"), + }, + } + diags := node.Execute(ctx, walkPlan) + if diags.HasErrors() { + t.Fatalf("unexpected error: %s", diags.Err()) + } + if !state.Empty() { + t.Fatalf("expected empty state, got %s", state.String()) + } + + if got := prevRunState.ResourceInstance(addr); got == nil { + t.Errorf("no entry for %s in the prev run state; should still be present", addr) + } + if got := refreshState.ResourceInstance(addr); got != nil { + t.Errorf("refresh state has entry for %s; should've been removed", addr) + } + if got := changes.ResourceInstance(addr); got == nil { + t.Errorf("no entry for %s in the planned changes; should have a NoOp change", addr) + } else { + if got, want := got.Action, plans.NoOp; got != want { + t.Errorf("planned change for %s has wrong action\ngot: %s\nwant: %s", addr, got, want) + } + } + +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_test.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_test.go index c5ae144b..e3a8faa1 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_test.go @@ -23,9 +23,9 @@ func TestNodePlannableResourceExecute(t *testing.T) { }, Addr: mustAbsResourceAddr("test_instance.foo"), } - err := node.Execute(ctx, walkApply) - if err != nil { - t.Fatalf("unexpected error: %s", err.Error()) + diags := node.Execute(ctx, walkApply) + if diags.HasErrors() { + t.Fatalf("unexpected error: %s", diags.Err()) } if !state.Empty() { t.Fatalf("expected no state, got:\n %s", state.String()) @@ -48,9 +48,9 @@ func TestNodePlannableResourceExecute(t *testing.T) { }, Addr: mustAbsResourceAddr("test_instance.foo"), } - err := node.Execute(ctx, walkApply) - if err != nil { - t.Fatalf("unexpected error: %s", err.Error()) + diags := node.Execute(ctx, walkApply) + if diags.HasErrors() { + t.Fatalf("unexpected error: %s", diags.Err()) } if state.Empty() { t.Fatal("expected resources in state, got empty state") diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_validate.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_validate.go index 436d30a4..0f664084 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_validate.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_validate.go @@ -3,8 +3,13 @@ package terraform import ( "fmt" + "github.com/hashicorp/hcl/v2" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/provisioners" + "github.com/hashicorp/terraform/tfdiags" "github.com/zclconf/go-cty/cty" ) @@ -31,31 +36,8 @@ func (n *NodeValidatableResource) Path() addrs.ModuleInstance { } // GraphNodeEvalable -func (n *NodeValidatableResource) Execute(ctx EvalContext, op walkOperation) error { - addr := n.ResourceAddr() - config := n.Config - - // Declare the variables will be used are used to pass values along - // the evaluation sequence below. These are written to via pointers - // passed to the EvalNodes. - var configVal cty.Value - provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider) - if err != nil { - return err - } - - evalValidateResource := &EvalValidateResource{ - Addr: addr.Resource, - Provider: &provider, - ProviderMetas: n.ProviderMetas, - ProviderSchema: &providerSchema, - Config: config, - ConfigVal: &configVal, - } - err = evalValidateResource.Validate(ctx) - if err != nil { - return err - } +func (n *NodeValidatableResource) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { + diags = diags.Append(n.validateResource(ctx)) if managed := n.Config.Managed; managed != nil { hasCount := n.Config.Count != nil @@ -64,34 +46,428 @@ func (n *NodeValidatableResource) Execute(ctx EvalContext, op walkOperation) err // Validate all the provisioners for _, p := range managed.Provisioners { if p.Connection == nil { - p.Connection = config.Managed.Connection - } else if config.Managed.Connection != nil { - p.Connection.Config = configs.MergeBodies(config.Managed.Connection.Config, p.Connection.Config) + p.Connection = n.Config.Managed.Connection + } else if n.Config.Managed.Connection != nil { + p.Connection.Config = configs.MergeBodies(n.Config.Managed.Connection.Config, p.Connection.Config) } - provisioner := ctx.Provisioner(p.Type) - if provisioner == nil { - return fmt.Errorf("provisioner %s not initialized", p.Type) + // Validate Provisioner Config + diags = diags.Append(n.validateProvisioner(ctx, p, hasCount, hasForEach)) + if diags.HasErrors() { + return diags } - provisionerSchema := ctx.ProvisionerSchema(p.Type) - if provisionerSchema == nil { - return fmt.Errorf("provisioner %s not initialized", p.Type) + } + } + return diags +} + +// validateProvisioner validates the configuration of a provisioner belonging to +// a resource. The provisioner config is expected to contain the merged +// connection configurations. +func (n *NodeValidatableResource) validateProvisioner(ctx EvalContext, p *configs.Provisioner, hasCount, hasForEach bool) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + provisioner, err := ctx.Provisioner(p.Type) + if err != nil { + diags = diags.Append(err) + return diags + } + + if provisioner == nil { + return diags.Append(fmt.Errorf("provisioner %s not initialized", p.Type)) + } + provisionerSchema := ctx.ProvisionerSchema(p.Type) + if provisionerSchema == nil { + return diags.Append(fmt.Errorf("provisioner %s not initialized", p.Type)) + } + + // Validate the provisioner's own config first + configVal, _, configDiags := n.evaluateBlock(ctx, p.Config, provisionerSchema, hasCount, hasForEach) + diags = diags.Append(configDiags) + + if configVal == cty.NilVal { + // Should never happen for a well-behaved EvaluateBlock implementation + return diags.Append(fmt.Errorf("EvaluateBlock returned nil value")) + } + + // Use unmarked value for validate request + unmarkedConfigVal, _ := configVal.UnmarkDeep() + req := provisioners.ValidateProvisionerConfigRequest{ + Config: unmarkedConfigVal, + } + + resp := provisioner.ValidateProvisionerConfig(req) + diags = diags.Append(resp.Diagnostics) + + if p.Connection != nil { + // We can't comprehensively validate the connection config since its + // final structure is decided by the communicator and we can't instantiate + // that until we have a complete instance state. However, we *can* catch + // configuration keys that are not valid for *any* communicator, catching + // typos early rather than waiting until we actually try to run one of + // the resource's provisioners. + _, _, connDiags := n.evaluateBlock(ctx, p.Connection.Config, connectionBlockSupersetSchema, hasCount, hasForEach) + diags = diags.Append(connDiags) + } + return diags +} + +func (n *NodeValidatableResource) evaluateBlock(ctx EvalContext, body hcl.Body, schema *configschema.Block, hasCount, hasForEach bool) (cty.Value, hcl.Body, tfdiags.Diagnostics) { + keyData := EvalDataForNoInstanceKey + selfAddr := n.ResourceAddr().Resource.Instance(addrs.NoKey) + + if hasCount { + // For a resource that has count, we allow count.index but don't + // know at this stage what it will return. + keyData = InstanceKeyEvalData{ + CountIndex: cty.UnknownVal(cty.Number), + } + + // "self" can't point to an unknown key, but we'll force it to be + // key 0 here, which should return an unknown value of the + // expected type since none of these elements are known at this + // point anyway. + selfAddr = n.ResourceAddr().Resource.Instance(addrs.IntKey(0)) + } else if hasForEach { + // For a resource that has for_each, we allow each.value and each.key + // but don't know at this stage what it will return. + keyData = InstanceKeyEvalData{ + EachKey: cty.UnknownVal(cty.String), + EachValue: cty.DynamicVal, + } + + // "self" can't point to an unknown key, but we'll force it to be + // key "" here, which should return an unknown value of the + // expected type since none of these elements are known at + // this point anyway. + selfAddr = n.ResourceAddr().Resource.Instance(addrs.StringKey("")) + } + + return ctx.EvaluateBlock(body, schema, selfAddr, keyData) +} + +// connectionBlockSupersetSchema is a schema representing the superset of all +// possible arguments for "connection" blocks across all supported connection +// types. +// +// This currently lives here because we've not yet updated our communicator +// subsystem to be aware of schema itself. Once that is done, we can remove +// this and use a type-specific schema from the communicator to validate +// exactly what is expected for a given connection type. +var connectionBlockSupersetSchema = &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + // NOTE: "type" is not included here because it's treated special + // by the config loader and stored away in a separate field. + + // Common attributes for both connection types + "host": { + Type: cty.String, + Required: true, + }, + "type": { + Type: cty.String, + Optional: true, + }, + "user": { + Type: cty.String, + Optional: true, + }, + "password": { + Type: cty.String, + Optional: true, + }, + "port": { + Type: cty.String, + Optional: true, + }, + "timeout": { + Type: cty.String, + Optional: true, + }, + "script_path": { + Type: cty.String, + Optional: true, + }, + // For type=ssh only (enforced in ssh communicator) + "target_platform": { + Type: cty.String, + Optional: true, + }, + "private_key": { + Type: cty.String, + Optional: true, + }, + "certificate": { + Type: cty.String, + Optional: true, + }, + "host_key": { + Type: cty.String, + Optional: true, + }, + "agent": { + Type: cty.Bool, + Optional: true, + }, + "agent_identity": { + Type: cty.String, + Optional: true, + }, + "bastion_host": { + Type: cty.String, + Optional: true, + }, + "bastion_host_key": { + Type: cty.String, + Optional: true, + }, + "bastion_port": { + Type: cty.Number, + Optional: true, + }, + "bastion_user": { + Type: cty.String, + Optional: true, + }, + "bastion_password": { + Type: cty.String, + Optional: true, + }, + "bastion_private_key": { + Type: cty.String, + Optional: true, + }, + "bastion_certificate": { + Type: cty.String, + Optional: true, + }, + + // For type=winrm only (enforced in winrm communicator) + "https": { + Type: cty.Bool, + Optional: true, + }, + "insecure": { + Type: cty.Bool, + Optional: true, + }, + "cacert": { + Type: cty.String, + Optional: true, + }, + "use_ntlm": { + Type: cty.Bool, + Optional: true, + }, + }, +} + +func (n *NodeValidatableResource) validateResource(ctx EvalContext) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider) + diags = diags.Append(err) + if diags.HasErrors() { + return diags + } + if providerSchema == nil { + diags = diags.Append(fmt.Errorf("validateResource has nil schema for %s", n.Addr)) + return diags + } + + keyData := EvalDataForNoInstanceKey + + switch { + case n.Config.Count != nil: + // If the config block has count, we'll evaluate with an unknown + // number as count.index so we can still type check even though + // we won't expand count until the plan phase. + keyData = InstanceKeyEvalData{ + CountIndex: cty.UnknownVal(cty.Number), + } + + // Basic type-checking of the count argument. More complete validation + // of this will happen when we DynamicExpand during the plan walk. + countDiags := validateCount(ctx, n.Config.Count) + diags = diags.Append(countDiags) + + case n.Config.ForEach != nil: + keyData = InstanceKeyEvalData{ + EachKey: cty.UnknownVal(cty.String), + EachValue: cty.UnknownVal(cty.DynamicPseudoType), + } + + // Evaluate the for_each expression here so we can expose the diagnostics + forEachDiags := validateForEach(ctx, n.Config.ForEach) + diags = diags.Append(forEachDiags) + } + + diags = diags.Append(validateDependsOn(ctx, n.Config.DependsOn)) + + // Validate the provider_meta block for the provider this resource + // belongs to, if there is one. + // + // Note: this will return an error for every resource a provider + // uses in a module, if the provider_meta for that module is + // incorrect. The only way to solve this that we've found is to + // insert a new ProviderMeta graph node in the graph, and make all + // that provider's resources in the module depend on the node. That's + // an awful heavy hammer to swing for this feature, which should be + // used only in limited cases with heavy coordination with the + // Terraform team, so we're going to defer that solution for a future + // enhancement to this functionality. + /* + if n.ProviderMetas != nil { + if m, ok := n.ProviderMetas[n.ProviderAddr.ProviderConfig.Type]; ok && m != nil { + // if the provider doesn't support this feature, throw an error + if (*n.ProviderSchema).ProviderMeta == nil { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: fmt.Sprintf("Provider %s doesn't support provider_meta", cfg.ProviderConfigAddr()), + Detail: fmt.Sprintf("The resource %s belongs to a provider that doesn't support provider_meta blocks", n.Addr), + Subject: &m.ProviderRange, + }) + } else { + _, _, metaDiags := ctx.EvaluateBlock(m.Config, (*n.ProviderSchema).ProviderMeta, nil, EvalDataForNoInstanceKey) + diags = diags.Append(metaDiags) + } } + } + */ + // BUG(paddy): we're not validating provider_meta blocks on EvalValidate right now + // because the ProviderAddr for the resource isn't available on the EvalValidate + // struct. - // Validate Provisioner Config - validateProvisioner := &EvalValidateProvisioner{ - ResourceAddr: addr.Resource, - Provisioner: &provisioner, - Schema: &provisionerSchema, - Config: p, - ResourceHasCount: hasCount, - ResourceHasForEach: hasForEach, + // Provider entry point varies depending on resource mode, because + // managed resources and data resources are two distinct concepts + // in the provider abstraction. + switch n.Config.Mode { + case addrs.ManagedResourceMode: + schema, _ := providerSchema.SchemaForResourceType(n.Config.Mode, n.Config.Type) + if schema == nil { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid resource type", + Detail: fmt.Sprintf("The provider %s does not support resource type %q.", n.Config.ProviderConfigAddr(), n.Config.Type), + Subject: &n.Config.TypeRange, + }) + return diags + } + + configVal, _, valDiags := ctx.EvaluateBlock(n.Config.Config, schema, nil, keyData) + diags = diags.Append(valDiags) + if valDiags.HasErrors() { + return diags + } + + if n.Config.Managed != nil { // can be nil only in tests with poorly-configured mocks + for _, traversal := range n.Config.Managed.IgnoreChanges { + // validate the ignore_changes traversals apply. + moreDiags := schema.StaticValidateTraversal(traversal) + diags = diags.Append(moreDiags) + + // TODO: we want to notify users that they can't use + // ignore_changes for computed attributes, but we don't have an + // easy way to correlate the config value, schema and + // traversal together. } - err := validateProvisioner.Validate(ctx) - if err != nil { - return err + } + + // Use unmarked value for validate request + unmarkedConfigVal, _ := configVal.UnmarkDeep() + req := providers.ValidateResourceConfigRequest{ + TypeName: n.Config.Type, + Config: unmarkedConfigVal, + } + + resp := provider.ValidateResourceConfig(req) + diags = diags.Append(resp.Diagnostics.InConfigBody(n.Config.Config, n.Addr.String())) + + case addrs.DataResourceMode: + schema, _ := providerSchema.SchemaForResourceType(n.Config.Mode, n.Config.Type) + if schema == nil { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid data source", + Detail: fmt.Sprintf("The provider %s does not support data source %q.", n.Config.ProviderConfigAddr(), n.Config.Type), + Subject: &n.Config.TypeRange, + }) + return diags + } + + configVal, _, valDiags := ctx.EvaluateBlock(n.Config.Config, schema, nil, keyData) + diags = diags.Append(valDiags) + if valDiags.HasErrors() { + return diags + } + + // Use unmarked value for validate request + unmarkedConfigVal, _ := configVal.UnmarkDeep() + req := providers.ValidateDataResourceConfigRequest{ + TypeName: n.Config.Type, + Config: unmarkedConfigVal, + } + + resp := provider.ValidateDataResourceConfig(req) + diags = diags.Append(resp.Diagnostics.InConfigBody(n.Config.Config, n.Addr.String())) + } + + return diags +} + +func validateCount(ctx EvalContext, expr hcl.Expression) (diags tfdiags.Diagnostics) { + val, countDiags := evaluateCountExpressionValue(expr, ctx) + // If the value isn't known then that's the best we can do for now, but + // we'll check more thoroughly during the plan walk + if !val.IsKnown() { + return diags + } + + if countDiags.HasErrors() { + diags = diags.Append(countDiags) + } + + return diags +} + +func validateForEach(ctx EvalContext, expr hcl.Expression) (diags tfdiags.Diagnostics) { + val, forEachDiags := evaluateForEachExpressionValue(expr, ctx, true) + // If the value isn't known then that's the best we can do for now, but + // we'll check more thoroughly during the plan walk + if !val.IsKnown() { + return diags + } + + if forEachDiags.HasErrors() { + diags = diags.Append(forEachDiags) + } + + return diags +} + +func validateDependsOn(ctx EvalContext, dependsOn []hcl.Traversal) (diags tfdiags.Diagnostics) { + for _, traversal := range dependsOn { + ref, refDiags := addrs.ParseRef(traversal) + diags = diags.Append(refDiags) + if !refDiags.HasErrors() && len(ref.Remaining) != 0 { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid depends_on reference", + Detail: "References in depends_on must be to a whole object (resource, etc), not to an attribute of an object.", + Subject: ref.Remaining.SourceRange().Ptr(), + }) + } + + // The ref must also refer to something that exists. To test that, + // we'll just eval it and count on the fact that our evaluator will + // detect references to non-existent objects. + if !diags.HasErrors() { + scope := ctx.EvaluationScope(nil, EvalDataForNoInstanceKey) + if scope != nil { // sometimes nil in tests, due to incomplete mocks + _, refDiags = scope.EvalReference(ref, cty.DynamicPseudoType) + diags = diags.Append(refDiags) } } } - return nil + return diags } diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_validate_test.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_validate_test.go new file mode 100644 index 00000000..1c89d32e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_validate_test.go @@ -0,0 +1,488 @@ +package terraform + +import ( + "errors" + "strings" + "testing" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hcltest" + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/configs" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/provisioners" + "github.com/hashicorp/terraform/tfdiags" + "github.com/zclconf/go-cty/cty" +) + +func TestNodeValidatableResource_ValidateProvisioner_valid(t *testing.T) { + ctx := &MockEvalContext{} + ctx.installSimpleEval() + mp := &MockProvisioner{} + ps := &configschema.Block{} + ctx.ProvisionerSchemaSchema = ps + ctx.ProvisionerProvisioner = mp + + pc := &configs.Provisioner{ + Type: "baz", + Config: hcl.EmptyBody(), + Connection: &configs.Connection{ + Config: configs.SynthBody("", map[string]cty.Value{ + "host": cty.StringVal("localhost"), + "type": cty.StringVal("ssh"), + }), + }, + } + + rc := &configs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_foo", + Name: "bar", + Config: configs.SynthBody("", map[string]cty.Value{}), + } + + node := NodeValidatableResource{ + NodeAbstractResource: &NodeAbstractResource{ + Addr: mustConfigResourceAddr("test_foo.bar"), + Config: rc, + }, + } + + diags := node.validateProvisioner(ctx, pc, false, false) + if diags.HasErrors() { + t.Fatalf("node.Eval failed: %s", diags.Err()) + } + if !mp.ValidateProvisionerConfigCalled { + t.Fatalf("p.ValidateProvisionerConfig not called") + } +} + +func TestNodeValidatableResource_ValidateProvisioner__warning(t *testing.T) { + ctx := &MockEvalContext{} + ctx.installSimpleEval() + mp := &MockProvisioner{} + ps := &configschema.Block{} + ctx.ProvisionerSchemaSchema = ps + ctx.ProvisionerProvisioner = mp + + pc := &configs.Provisioner{ + Type: "baz", + Config: hcl.EmptyBody(), + } + + rc := &configs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_foo", + Name: "bar", + Config: configs.SynthBody("", map[string]cty.Value{}), + Managed: &configs.ManagedResource{}, + } + + node := NodeValidatableResource{ + NodeAbstractResource: &NodeAbstractResource{ + Addr: mustConfigResourceAddr("test_foo.bar"), + Config: rc, + }, + } + + { + var diags tfdiags.Diagnostics + diags = diags.Append(tfdiags.SimpleWarning("foo is deprecated")) + mp.ValidateProvisionerConfigResponse = provisioners.ValidateProvisionerConfigResponse{ + Diagnostics: diags, + } + } + + diags := node.validateProvisioner(ctx, pc, false, false) + if len(diags) != 1 { + t.Fatalf("wrong number of diagnostics in %s; want one warning", diags.ErrWithWarnings()) + } + + if got, want := diags[0].Description().Summary, mp.ValidateProvisionerConfigResponse.Diagnostics[0].Description().Summary; got != want { + t.Fatalf("wrong warning %q; want %q", got, want) + } +} + +func TestNodeValidatableResource_ValidateProvisioner__conntectionInvalid(t *testing.T) { + ctx := &MockEvalContext{} + ctx.installSimpleEval() + mp := &MockProvisioner{} + ps := &configschema.Block{} + ctx.ProvisionerSchemaSchema = ps + ctx.ProvisionerProvisioner = mp + + pc := &configs.Provisioner{ + Type: "baz", + Config: hcl.EmptyBody(), + Connection: &configs.Connection{ + Config: configs.SynthBody("", map[string]cty.Value{ + "type": cty.StringVal("ssh"), + "bananananananana": cty.StringVal("foo"), + "bazaz": cty.StringVal("bar"), + }), + }, + } + + rc := &configs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_foo", + Name: "bar", + Config: configs.SynthBody("", map[string]cty.Value{}), + Managed: &configs.ManagedResource{}, + } + + node := NodeValidatableResource{ + NodeAbstractResource: &NodeAbstractResource{ + Addr: mustConfigResourceAddr("test_foo.bar"), + Config: rc, + }, + } + + diags := node.validateProvisioner(ctx, pc, false, false) + if !diags.HasErrors() { + t.Fatalf("node.Eval succeeded; want error") + } + if len(diags) != 3 { + t.Fatalf("wrong number of diagnostics; want two errors\n\n%s", diags.Err()) + } + + errStr := diags.Err().Error() + if !(strings.Contains(errStr, "bananananananana") && strings.Contains(errStr, "bazaz")) { + t.Fatalf("wrong errors %q; want something about each of our invalid connInfo keys", errStr) + } +} + +func TestNodeValidatableResource_ValidateResource_managedResource(t *testing.T) { + mp := simpleMockProvider() + mp.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { + if got, want := req.TypeName, "test_object"; got != want { + t.Fatalf("wrong resource type\ngot: %#v\nwant: %#v", got, want) + } + if got, want := req.Config.GetAttr("test_string"), cty.StringVal("bar"); !got.RawEquals(want) { + t.Fatalf("wrong value for test_string\ngot: %#v\nwant: %#v", got, want) + } + if got, want := req.Config.GetAttr("test_number"), cty.NumberIntVal(2); !got.RawEquals(want) { + t.Fatalf("wrong value for test_number\ngot: %#v\nwant: %#v", got, want) + } + return providers.ValidateResourceConfigResponse{} + } + + p := providers.Interface(mp) + rc := &configs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_object", + Name: "foo", + Config: configs.SynthBody("", map[string]cty.Value{ + "test_string": cty.StringVal("bar"), + "test_number": cty.NumberIntVal(2).Mark("sensitive"), + }), + } + node := NodeValidatableResource{ + NodeAbstractResource: &NodeAbstractResource{ + Addr: mustConfigResourceAddr("test_foo.bar"), + Config: rc, + ResolvedProvider: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + }, + } + + ctx := &MockEvalContext{} + ctx.installSimpleEval() + ctx.ProviderSchemaSchema = mp.ProviderSchema() + ctx.ProviderProvider = p + + err := node.validateResource(ctx) + if err != nil { + t.Fatalf("err: %s", err) + } + + if !mp.ValidateResourceConfigCalled { + t.Fatal("Expected ValidateResourceConfig to be called, but it was not!") + } +} + +func TestNodeValidatableResource_ValidateResource_managedResourceCount(t *testing.T) { + // Setup + mp := simpleMockProvider() + mp.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { + if got, want := req.TypeName, "test_object"; got != want { + t.Fatalf("wrong resource type\ngot: %#v\nwant: %#v", got, want) + } + if got, want := req.Config.GetAttr("test_string"), cty.StringVal("bar"); !got.RawEquals(want) { + t.Fatalf("wrong value for test_string\ngot: %#v\nwant: %#v", got, want) + } + return providers.ValidateResourceConfigResponse{} + } + + p := providers.Interface(mp) + + ctx := &MockEvalContext{} + ctx.installSimpleEval() + ctx.ProviderSchemaSchema = mp.ProviderSchema() + ctx.ProviderProvider = p + + tests := []struct { + name string + count hcl.Expression + }{ + { + "simple count", + hcltest.MockExprLiteral(cty.NumberIntVal(2)), + }, + { + "marked count value", + hcltest.MockExprLiteral(cty.NumberIntVal(3).Mark("marked")), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + rc := &configs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_object", + Name: "foo", + Count: test.count, + Config: configs.SynthBody("", map[string]cty.Value{ + "test_string": cty.StringVal("bar"), + }), + } + node := NodeValidatableResource{ + NodeAbstractResource: &NodeAbstractResource{ + Addr: mustConfigResourceAddr("test_foo.bar"), + Config: rc, + ResolvedProvider: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + }, + } + + diags := node.validateResource(ctx) + if diags.HasErrors() { + t.Fatalf("err: %s", diags.Err()) + } + + if !mp.ValidateResourceConfigCalled { + t.Fatal("Expected ValidateResourceConfig to be called, but it was not!") + } + }) + } +} + +func TestNodeValidatableResource_ValidateResource_dataSource(t *testing.T) { + mp := simpleMockProvider() + mp.ValidateDataResourceConfigFn = func(req providers.ValidateDataResourceConfigRequest) providers.ValidateDataResourceConfigResponse { + if got, want := req.TypeName, "test_object"; got != want { + t.Fatalf("wrong resource type\ngot: %#v\nwant: %#v", got, want) + } + if got, want := req.Config.GetAttr("test_string"), cty.StringVal("bar"); !got.RawEquals(want) { + t.Fatalf("wrong value for test_string\ngot: %#v\nwant: %#v", got, want) + } + if got, want := req.Config.GetAttr("test_number"), cty.NumberIntVal(2); !got.RawEquals(want) { + t.Fatalf("wrong value for test_number\ngot: %#v\nwant: %#v", got, want) + } + return providers.ValidateDataResourceConfigResponse{} + } + + p := providers.Interface(mp) + rc := &configs.Resource{ + Mode: addrs.DataResourceMode, + Type: "test_object", + Name: "foo", + Config: configs.SynthBody("", map[string]cty.Value{ + "test_string": cty.StringVal("bar"), + "test_number": cty.NumberIntVal(2).Mark("sensitive"), + }), + } + + node := NodeValidatableResource{ + NodeAbstractResource: &NodeAbstractResource{ + Addr: mustConfigResourceAddr("test_foo.bar"), + Config: rc, + ResolvedProvider: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + }, + } + + ctx := &MockEvalContext{} + ctx.installSimpleEval() + ctx.ProviderSchemaSchema = mp.ProviderSchema() + ctx.ProviderProvider = p + + diags := node.validateResource(ctx) + if diags.HasErrors() { + t.Fatalf("err: %s", diags.Err()) + } + + if !mp.ValidateDataResourceConfigCalled { + t.Fatal("Expected ValidateDataSourceConfig to be called, but it was not!") + } +} + +func TestNodeValidatableResource_ValidateResource_valid(t *testing.T) { + mp := simpleMockProvider() + mp.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { + return providers.ValidateResourceConfigResponse{} + } + + p := providers.Interface(mp) + rc := &configs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_object", + Name: "foo", + Config: configs.SynthBody("", map[string]cty.Value{}), + } + node := NodeValidatableResource{ + NodeAbstractResource: &NodeAbstractResource{ + Addr: mustConfigResourceAddr("test_object.foo"), + Config: rc, + ResolvedProvider: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + }, + } + + ctx := &MockEvalContext{} + ctx.installSimpleEval() + ctx.ProviderSchemaSchema = mp.ProviderSchema() + ctx.ProviderProvider = p + + diags := node.validateResource(ctx) + if diags.HasErrors() { + t.Fatalf("err: %s", diags.Err()) + } +} + +func TestNodeValidatableResource_ValidateResource_warningsAndErrorsPassedThrough(t *testing.T) { + mp := simpleMockProvider() + mp.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { + var diags tfdiags.Diagnostics + diags = diags.Append(tfdiags.SimpleWarning("warn")) + diags = diags.Append(errors.New("err")) + return providers.ValidateResourceConfigResponse{ + Diagnostics: diags, + } + } + + p := providers.Interface(mp) + rc := &configs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_object", + Name: "foo", + Config: configs.SynthBody("", map[string]cty.Value{}), + } + node := NodeValidatableResource{ + NodeAbstractResource: &NodeAbstractResource{ + Addr: mustConfigResourceAddr("test_foo.bar"), + Config: rc, + ResolvedProvider: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + }, + } + + ctx := &MockEvalContext{} + ctx.installSimpleEval() + ctx.ProviderSchemaSchema = mp.ProviderSchema() + ctx.ProviderProvider = p + + diags := node.validateResource(ctx) + if !diags.HasErrors() { + t.Fatal("unexpected success; want error") + } + + bySeverity := map[tfdiags.Severity]tfdiags.Diagnostics{} + for _, diag := range diags { + bySeverity[diag.Severity()] = append(bySeverity[diag.Severity()], diag) + } + if len(bySeverity[tfdiags.Warning]) != 1 || bySeverity[tfdiags.Warning][0].Description().Summary != "warn" { + t.Errorf("Expected 1 warning 'warn', got: %s", bySeverity[tfdiags.Warning].ErrWithWarnings()) + } + if len(bySeverity[tfdiags.Error]) != 1 || bySeverity[tfdiags.Error][0].Description().Summary != "err" { + t.Errorf("Expected 1 error 'err', got: %s", bySeverity[tfdiags.Error].Err()) + } +} + +func TestNodeValidatableResource_ValidateResource_invalidDependsOn(t *testing.T) { + mp := simpleMockProvider() + mp.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse { + return providers.ValidateResourceConfigResponse{} + } + + // We'll check a _valid_ config first, to make sure we're not failing + // for some other reason, and then make it invalid. + p := providers.Interface(mp) + rc := &configs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_object", + Name: "foo", + Config: configs.SynthBody("", map[string]cty.Value{}), + DependsOn: []hcl.Traversal{ + // Depending on path.module is pointless, since it is immediately + // available, but we allow all of the referencable addrs here + // for consistency: referencing them is harmless, and avoids the + // need for us to document a different subset of addresses that + // are valid in depends_on. + // For the sake of this test, it's a valid address we can use that + // doesn't require something else to exist in the configuration. + { + hcl.TraverseRoot{ + Name: "path", + }, + hcl.TraverseAttr{ + Name: "module", + }, + }, + }, + } + node := NodeValidatableResource{ + NodeAbstractResource: &NodeAbstractResource{ + Addr: mustConfigResourceAddr("test_foo.bar"), + Config: rc, + ResolvedProvider: mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + }, + } + + ctx := &MockEvalContext{} + ctx.installSimpleEval() + + ctx.ProviderSchemaSchema = mp.ProviderSchema() + ctx.ProviderProvider = p + + diags := node.validateResource(ctx) + if diags.HasErrors() { + t.Fatalf("error for supposedly-valid config: %s", diags.ErrWithWarnings()) + } + + // Now we'll make it invalid by adding additional traversal steps at + // the end of what we're referencing. This is intended to catch the + // situation where the user tries to depend on e.g. a specific resource + // attribute, rather than the whole resource, like aws_instance.foo.id. + rc.DependsOn = append(rc.DependsOn, hcl.Traversal{ + hcl.TraverseRoot{ + Name: "path", + }, + hcl.TraverseAttr{ + Name: "module", + }, + hcl.TraverseAttr{ + Name: "extra", + }, + }) + + diags = node.validateResource(ctx) + if !diags.HasErrors() { + t.Fatal("no error for invalid depends_on") + } + if got, want := diags.Err().Error(), "Invalid depends_on reference"; !strings.Contains(got, want) { + t.Fatalf("wrong error\ngot: %s\nwant: Message containing %q", got, want) + } + + // Test for handling an unknown root without attribute, like a + // typo that omits the dot inbetween "path.module". + rc.DependsOn = append(rc.DependsOn, hcl.Traversal{ + hcl.TraverseRoot{ + Name: "pathmodule", + }, + }) + + diags = node.validateResource(ctx) + if !diags.HasErrors() { + t.Fatal("no error for invalid depends_on") + } + if got, want := diags.Err().Error(), "Invalid depends_on reference"; !strings.Contains(got, want) { + t.Fatalf("wrong error\ngot: %s\nwant: Message containing %q", got, want) + } +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_root_variable.go b/vendor/github.com/hashicorp/terraform/terraform/node_root_variable.go index 63fe8180..5e90e4ad 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_root_variable.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_root_variable.go @@ -4,6 +4,7 @@ import ( "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/dag" + "github.com/hashicorp/terraform/tfdiags" ) // NodeRootVariable represents a root variable input. @@ -36,7 +37,7 @@ func (n *NodeRootVariable) ReferenceableAddrs() []addrs.Referenceable { } // GraphNodeExecutable -func (n *NodeRootVariable) Execute(ctx EvalContext, op walkOperation) error { +func (n *NodeRootVariable) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics { // We don't actually need to _evaluate_ a root module variable, because // its value is always constant and already stashed away in our EvalContext. // However, we might need to run some user-defined validation rules against diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_root_variable_test.go b/vendor/github.com/hashicorp/terraform/terraform/node_root_variable_test.go index 2a410e21..6546395b 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_root_variable_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_root_variable_test.go @@ -17,9 +17,9 @@ func TestNodeRootVariableExecute(t *testing.T) { }, } - err := n.Execute(ctx, walkApply) - if err != nil { - t.Fatalf("unexpected error: %s", err.Error()) + diags := n.Execute(ctx, walkApply) + if diags.HasErrors() { + t.Fatalf("unexpected error: %s", diags.Err()) } } diff --git a/vendor/github.com/hashicorp/terraform/terraform/phasestate_string.go b/vendor/github.com/hashicorp/terraform/terraform/phasestate_string.go new file mode 100644 index 00000000..3c3b4f71 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/phasestate_string.go @@ -0,0 +1,25 @@ +// Code generated by "stringer -type phaseState"; DO NOT EDIT. + +package terraform + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[workingState-0] + _ = x[refreshState-1] + _ = x[prevRunState-2] +} + +const _phaseState_name = "workingStaterefreshStateprevRunState" + +var _phaseState_index = [...]uint8{0, 12, 24, 36} + +func (i phaseState) String() string { + if i < 0 || i >= phaseState(len(_phaseState_index)-1) { + return "phaseState(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _phaseState_name[_phaseState_index[i]:_phaseState_index[i+1]] +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/plan.go b/vendor/github.com/hashicorp/terraform/terraform/plan.go deleted file mode 100644 index af04c6cd..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/plan.go +++ /dev/null @@ -1,122 +0,0 @@ -package terraform - -import ( - "bytes" - "encoding/gob" - "fmt" - "io" - "sync" - - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/terraform/configs" -) - -func init() { - gob.Register(make([]interface{}, 0)) - gob.Register(make([]map[string]interface{}, 0)) - gob.Register(make(map[string]interface{})) - gob.Register(make(map[string]string)) -} - -// Plan represents a single Terraform execution plan, which contains -// all the information necessary to make an infrastructure change. -// -// A plan has to contain basically the entire state of the world -// necessary to make a change: the state, diff, config, backend config, etc. -// This is so that it can run alone without any other data. -type Plan struct { - // Diff describes the resource actions that must be taken when this - // plan is applied. - Diff *Diff - - // Config represents the entire configuration that was present when this - // plan was created. - Config *configs.Config - - // State is the Terraform state that was current when this plan was - // created. - // - // It is not allowed to apply a plan that has a stale state, since its - // diff could be outdated. - State *State - - // Vars retains the variables that were set when creating the plan, so - // that the same variables can be applied during apply. - Vars map[string]cty.Value - - // Targets, if non-empty, contains a set of resource address strings that - // identify graph nodes that were selected as targets for plan. - // - // When targets are set, any graph node that is not directly targeted or - // indirectly targeted via dependencies is excluded from the graph. - Targets []string - - // TerraformVersion is the version of Terraform that was used to create - // this plan. - // - // It is not allowed to apply a plan created with a different version of - // Terraform, since the other fields of this structure may be interpreted - // in different ways between versions. - TerraformVersion string - - // ProviderSHA256s is a map giving the SHA256 hashes of the exact binaries - // used as plugins for each provider during plan. - // - // These must match between plan and apply to ensure that the diff is - // correctly interpreted, since different provider versions may have - // different attributes or attribute value constraints. - ProviderSHA256s map[string][]byte - - // Backend is the backend that this plan should use and store data with. - Backend *BackendState - - // Destroy indicates that this plan was created for a full destroy operation - Destroy bool - - once sync.Once -} - -func (p *Plan) String() string { - buf := new(bytes.Buffer) - buf.WriteString("DIFF:\n\n") - buf.WriteString(p.Diff.String()) - buf.WriteString("\n\nSTATE:\n\n") - buf.WriteString(p.State.String()) - return buf.String() -} - -func (p *Plan) init() { - p.once.Do(func() { - if p.Diff == nil { - p.Diff = new(Diff) - p.Diff.init() - } - - if p.State == nil { - p.State = new(State) - p.State.init() - } - - if p.Vars == nil { - p.Vars = make(map[string]cty.Value) - } - }) -} - -// The format byte is prefixed into the plan file format so that we have -// the ability in the future to change the file format if we want for any -// reason. -const planFormatMagic = "tfplan" -const planFormatVersion byte = 2 - -// ReadPlan reads a plan structure out of a reader in the format that -// was written by WritePlan. -func ReadPlan(src io.Reader) (*Plan, error) { - return nil, fmt.Errorf("terraform.ReadPlan is no longer in use; use planfile.Open instead") -} - -// WritePlan writes a plan somewhere in a binary format. -func WritePlan(d *Plan, dst io.Writer) error { - return fmt.Errorf("terraform.WritePlan is no longer in use; use planfile.Create instead") -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/provider_mock.go b/vendor/github.com/hashicorp/terraform/terraform/provider_mock.go index 45397fe2..d8f9afcd 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/provider_mock.go +++ b/vendor/github.com/hashicorp/terraform/terraform/provider_mock.go @@ -1,12 +1,14 @@ package terraform import ( - "encoding/json" + "fmt" "sync" "github.com/zclconf/go-cty/cty" ctyjson "github.com/zclconf/go-cty/cty/json" + "github.com/zclconf/go-cty/cty/msgpack" + "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/configs/hcl2shim" "github.com/hashicorp/terraform/providers" ) @@ -21,66 +23,63 @@ type MockProvider struct { // Anything you want, in case you need to store extra data with the mock. Meta interface{} - GetSchemaCalled bool - GetSchemaReturn *ProviderSchema // This is using ProviderSchema directly rather than providers.GetSchemaResponse for compatibility with old tests + GetProviderSchemaCalled bool + GetProviderSchemaResponse *providers.GetProviderSchemaResponse - PrepareProviderConfigCalled bool - PrepareProviderConfigResponse providers.PrepareProviderConfigResponse - PrepareProviderConfigRequest providers.PrepareProviderConfigRequest - PrepareProviderConfigFn func(providers.PrepareProviderConfigRequest) providers.PrepareProviderConfigResponse + ValidateProviderConfigCalled bool + ValidateProviderConfigResponse *providers.ValidateProviderConfigResponse + ValidateProviderConfigRequest providers.ValidateProviderConfigRequest + ValidateProviderConfigFn func(providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse - ValidateResourceTypeConfigCalled bool - ValidateResourceTypeConfigTypeName string - ValidateResourceTypeConfigResponse providers.ValidateResourceTypeConfigResponse - ValidateResourceTypeConfigRequest providers.ValidateResourceTypeConfigRequest - ValidateResourceTypeConfigFn func(providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse + ValidateResourceConfigCalled bool + ValidateResourceConfigTypeName string + ValidateResourceConfigResponse *providers.ValidateResourceConfigResponse + ValidateResourceConfigRequest providers.ValidateResourceConfigRequest + ValidateResourceConfigFn func(providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse - ValidateDataSourceConfigCalled bool - ValidateDataSourceConfigTypeName string - ValidateDataSourceConfigResponse providers.ValidateDataSourceConfigResponse - ValidateDataSourceConfigRequest providers.ValidateDataSourceConfigRequest - ValidateDataSourceConfigFn func(providers.ValidateDataSourceConfigRequest) providers.ValidateDataSourceConfigResponse + ValidateDataResourceConfigCalled bool + ValidateDataResourceConfigTypeName string + ValidateDataResourceConfigResponse *providers.ValidateDataResourceConfigResponse + ValidateDataResourceConfigRequest providers.ValidateDataResourceConfigRequest + ValidateDataResourceConfigFn func(providers.ValidateDataResourceConfigRequest) providers.ValidateDataResourceConfigResponse UpgradeResourceStateCalled bool UpgradeResourceStateTypeName string - UpgradeResourceStateResponse providers.UpgradeResourceStateResponse + UpgradeResourceStateResponse *providers.UpgradeResourceStateResponse UpgradeResourceStateRequest providers.UpgradeResourceStateRequest UpgradeResourceStateFn func(providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse - ConfigureCalled bool - ConfigureResponse providers.ConfigureResponse - ConfigureRequest providers.ConfigureRequest - ConfigureFn func(providers.ConfigureRequest) providers.ConfigureResponse + ConfigureProviderCalled bool + ConfigureProviderResponse *providers.ConfigureProviderResponse + ConfigureProviderRequest providers.ConfigureProviderRequest + ConfigureProviderFn func(providers.ConfigureProviderRequest) providers.ConfigureProviderResponse StopCalled bool StopFn func() error StopResponse error ReadResourceCalled bool - ReadResourceResponse providers.ReadResourceResponse + ReadResourceResponse *providers.ReadResourceResponse ReadResourceRequest providers.ReadResourceRequest ReadResourceFn func(providers.ReadResourceRequest) providers.ReadResourceResponse PlanResourceChangeCalled bool - PlanResourceChangeResponse providers.PlanResourceChangeResponse + PlanResourceChangeResponse *providers.PlanResourceChangeResponse PlanResourceChangeRequest providers.PlanResourceChangeRequest PlanResourceChangeFn func(providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse ApplyResourceChangeCalled bool - ApplyResourceChangeResponse providers.ApplyResourceChangeResponse + ApplyResourceChangeResponse *providers.ApplyResourceChangeResponse ApplyResourceChangeRequest providers.ApplyResourceChangeRequest ApplyResourceChangeFn func(providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse ImportResourceStateCalled bool - ImportResourceStateResponse providers.ImportResourceStateResponse + ImportResourceStateResponse *providers.ImportResourceStateResponse ImportResourceStateRequest providers.ImportResourceStateRequest ImportResourceStateFn func(providers.ImportResourceStateRequest) providers.ImportResourceStateResponse - // Legacy return type for existing tests, which will be shimmed into an - // ImportResourceStateResponse if set - ImportStateReturn []*InstanceState ReadDataSourceCalled bool - ReadDataSourceResponse providers.ReadDataSourceResponse + ReadDataSourceResponse *providers.ReadDataSourceResponse ReadDataSourceRequest providers.ReadDataSourceRequest ReadDataSourceFn func(providers.ReadDataSourceRequest) providers.ReadDataSourceResponse @@ -88,88 +87,143 @@ type MockProvider struct { CloseError error } -func (p *MockProvider) GetSchema() providers.GetSchemaResponse { +func (p *MockProvider) GetProviderSchema() providers.GetProviderSchemaResponse { p.Lock() defer p.Unlock() - p.GetSchemaCalled = true - return p.getSchema() + p.GetProviderSchemaCalled = true + return p.getProviderSchema() } -func (p *MockProvider) getSchema() providers.GetSchemaResponse { - // This version of getSchema doesn't do any locking, so it's suitable to +func (p *MockProvider) getProviderSchema() providers.GetProviderSchemaResponse { + // This version of getProviderSchema doesn't do any locking, so it's suitable to // call from other methods of this mock as long as they are already // holding the lock. + if p.GetProviderSchemaResponse != nil { + return *p.GetProviderSchemaResponse + } - ret := providers.GetSchemaResponse{ + return providers.GetProviderSchemaResponse{ Provider: providers.Schema{}, DataSources: map[string]providers.Schema{}, ResourceTypes: map[string]providers.Schema{}, } - if p.GetSchemaReturn != nil { - ret.Provider.Block = p.GetSchemaReturn.Provider - ret.ProviderMeta.Block = p.GetSchemaReturn.ProviderMeta - for n, s := range p.GetSchemaReturn.DataSources { - ret.DataSources[n] = providers.Schema{ - Block: s, - } - } - for n, s := range p.GetSchemaReturn.ResourceTypes { - ret.ResourceTypes[n] = providers.Schema{ - Version: int64(p.GetSchemaReturn.ResourceTypeSchemaVersions[n]), - Block: s, - } - } +} + +// ProviderSchema is a helper to convert from the internal GetProviderSchemaResponse to +// a ProviderSchema. +func (p *MockProvider) ProviderSchema() *ProviderSchema { + resp := p.getProviderSchema() + + schema := &ProviderSchema{ + Provider: resp.Provider.Block, + ProviderMeta: resp.ProviderMeta.Block, + ResourceTypes: map[string]*configschema.Block{}, + DataSources: map[string]*configschema.Block{}, + ResourceTypeSchemaVersions: map[string]uint64{}, } - return ret + for resType, s := range resp.ResourceTypes { + schema.ResourceTypes[resType] = s.Block + schema.ResourceTypeSchemaVersions[resType] = uint64(s.Version) + } + + for dataSource, s := range resp.DataSources { + schema.DataSources[dataSource] = s.Block + } + + return schema } -func (p *MockProvider) PrepareProviderConfig(r providers.PrepareProviderConfigRequest) providers.PrepareProviderConfigResponse { +func (p *MockProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) { p.Lock() defer p.Unlock() - p.PrepareProviderConfigCalled = true - p.PrepareProviderConfigRequest = r - if p.PrepareProviderConfigFn != nil { - return p.PrepareProviderConfigFn(r) + p.ValidateProviderConfigCalled = true + p.ValidateProviderConfigRequest = r + if p.ValidateProviderConfigFn != nil { + return p.ValidateProviderConfigFn(r) } - return p.PrepareProviderConfigResponse + + if p.ValidateProviderConfigResponse != nil { + return *p.ValidateProviderConfigResponse + } + + resp.PreparedConfig = r.Config + return resp } -func (p *MockProvider) ValidateResourceTypeConfig(r providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse { +func (p *MockProvider) ValidateResourceConfig(r providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) { p.Lock() defer p.Unlock() - p.ValidateResourceTypeConfigCalled = true - p.ValidateResourceTypeConfigRequest = r + p.ValidateResourceConfigCalled = true + p.ValidateResourceConfigRequest = r + + // Marshall the value to replicate behavior by the GRPC protocol, + // and return any relevant errors + resourceSchema, ok := p.getProviderSchema().ResourceTypes[r.TypeName] + if !ok { + resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName)) + return resp + } + + _, err := msgpack.Marshal(r.Config, resourceSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + + if p.ValidateResourceConfigFn != nil { + return p.ValidateResourceConfigFn(r) + } - if p.ValidateResourceTypeConfigFn != nil { - return p.ValidateResourceTypeConfigFn(r) + if p.ValidateResourceConfigResponse != nil { + return *p.ValidateResourceConfigResponse } - return p.ValidateResourceTypeConfigResponse + return resp } -func (p *MockProvider) ValidateDataSourceConfig(r providers.ValidateDataSourceConfigRequest) providers.ValidateDataSourceConfigResponse { +func (p *MockProvider) ValidateDataResourceConfig(r providers.ValidateDataResourceConfigRequest) (resp providers.ValidateDataResourceConfigResponse) { p.Lock() defer p.Unlock() - p.ValidateDataSourceConfigCalled = true - p.ValidateDataSourceConfigRequest = r + p.ValidateDataResourceConfigCalled = true + p.ValidateDataResourceConfigRequest = r - if p.ValidateDataSourceConfigFn != nil { - return p.ValidateDataSourceConfigFn(r) + // Marshall the value to replicate behavior by the GRPC protocol + dataSchema, ok := p.getProviderSchema().DataSources[r.TypeName] + if !ok { + resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName)) + return resp + } + _, err := msgpack.Marshal(r.Config, dataSchema.Block.ImpliedType()) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp } - return p.ValidateDataSourceConfigResponse + if p.ValidateDataResourceConfigFn != nil { + return p.ValidateDataResourceConfigFn(r) + } + + if p.ValidateDataResourceConfigResponse != nil { + return *p.ValidateDataResourceConfigResponse + } + + return resp } -func (p *MockProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse { +func (p *MockProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) { p.Lock() defer p.Unlock() - schemas := p.getSchema() - schema := schemas.ResourceTypes[r.TypeName] + schema, ok := p.getProviderSchema().ResourceTypes[r.TypeName] + if !ok { + resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName)) + return resp + } + schemaType := schema.Block.ImpliedType() p.UpgradeResourceStateCalled = true @@ -179,42 +233,47 @@ func (p *MockProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequ return p.UpgradeResourceStateFn(r) } - resp := p.UpgradeResourceStateResponse + if p.UpgradeResourceStateResponse != nil { + return *p.UpgradeResourceStateResponse + } - if resp.UpgradedState == cty.NilVal { - switch { - case r.RawStateFlatmap != nil: - v, err := hcl2shim.HCL2ValueFromFlatmap(r.RawStateFlatmap, schemaType) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - resp.UpgradedState = v - case len(r.RawStateJSON) > 0: - v, err := ctyjson.Unmarshal(r.RawStateJSON, schemaType) + switch { + case r.RawStateFlatmap != nil: + v, err := hcl2shim.HCL2ValueFromFlatmap(r.RawStateFlatmap, schemaType) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + resp.UpgradedState = v + case len(r.RawStateJSON) > 0: + v, err := ctyjson.Unmarshal(r.RawStateJSON, schemaType) - if err != nil { - resp.Diagnostics = resp.Diagnostics.Append(err) - return resp - } - resp.UpgradedState = v + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp } + resp.UpgradedState = v } + return resp } -func (p *MockProvider) Configure(r providers.ConfigureRequest) providers.ConfigureResponse { +func (p *MockProvider) ConfigureProvider(r providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) { p.Lock() defer p.Unlock() - p.ConfigureCalled = true - p.ConfigureRequest = r + p.ConfigureProviderCalled = true + p.ConfigureProviderRequest = r + + if p.ConfigureProviderFn != nil { + return p.ConfigureProviderFn(r) + } - if p.ConfigureFn != nil { - return p.ConfigureFn(r) + if p.ConfigureProviderResponse != nil { + return *p.ConfigureProviderResponse } - return p.ConfigureResponse + return resp } func (p *MockProvider) Stop() error { @@ -231,7 +290,7 @@ func (p *MockProvider) Stop() error { return p.StopResponse } -func (p *MockProvider) ReadResource(r providers.ReadResourceRequest) providers.ReadResourceResponse { +func (p *MockProvider) ReadResource(r providers.ReadResourceRequest) (resp providers.ReadResourceResponse) { p.Lock() defer p.Unlock() @@ -242,18 +301,32 @@ func (p *MockProvider) ReadResource(r providers.ReadResourceRequest) providers.R return p.ReadResourceFn(r) } - // make sure the NewState fits the schema - newState, err := p.GetSchemaReturn.ResourceTypes[r.TypeName].CoerceValue(p.ReadResourceResponse.NewState) - if err != nil { - panic(err) + if p.ReadResourceResponse != nil { + resp = *p.ReadResourceResponse + + // Make sure the NewState conforms to the schema. + // This isn't always the case for the existing tests. + schema, ok := p.getProviderSchema().ResourceTypes[r.TypeName] + if !ok { + resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName)) + return resp + } + + newState, err := schema.Block.CoerceValue(resp.NewState) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + } + resp.NewState = newState + return resp } - resp := p.ReadResourceResponse - resp.NewState = newState + // otherwise just return the same state we received + resp.NewState = r.PriorState + resp.Private = r.Private return resp } -func (p *MockProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse { +func (p *MockProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) { p.Lock() defer p.Unlock() @@ -264,10 +337,64 @@ func (p *MockProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) return p.PlanResourceChangeFn(r) } - return p.PlanResourceChangeResponse + if p.PlanResourceChangeResponse != nil { + return *p.PlanResourceChangeResponse + } + + schema, ok := p.getProviderSchema().ResourceTypes[r.TypeName] + if !ok { + resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName)) + return resp + } + + // The default plan behavior is to accept the proposed value, and mark all + // nil computed attributes as unknown. + val, err := cty.Transform(r.ProposedNewState, func(path cty.Path, v cty.Value) (cty.Value, error) { + // We're only concerned with known null values, which can be computed + // by the provider. + if !v.IsKnown() { + return v, nil + } + + attrSchema := schema.Block.AttributeByPath(path) + if attrSchema == nil { + // this is an intermediate path which does not represent an attribute + return v, nil + } + + // get the current configuration value, to detect when a + // computed+optional attributes has become unset + configVal, err := path.Apply(r.Config) + if err != nil { + return v, err + } + + switch { + case attrSchema.Computed && !attrSchema.Optional && v.IsNull(): + // this is the easy path, this value is not yet set, and _must_ be computed + return cty.UnknownVal(v.Type()), nil + + case attrSchema.Computed && attrSchema.Optional && !v.IsNull() && configVal.IsNull(): + // If an optional+computed value has gone from set to unset, it + // becomes computed. (this was not possible to do with legacy + // providers) + return cty.UnknownVal(v.Type()), nil + } + + return v, nil + }) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + + resp.PlannedPrivate = r.PriorPrivate + resp.PlannedState = val + + return resp } -func (p *MockProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse { +func (p *MockProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) { p.Lock() p.ApplyResourceChangeCalled = true p.ApplyResourceChangeRequest = r @@ -277,56 +404,61 @@ func (p *MockProvider) ApplyResourceChange(r providers.ApplyResourceChangeReques return p.ApplyResourceChangeFn(r) } - return p.ApplyResourceChangeResponse -} - -func (p *MockProvider) ImportResourceState(r providers.ImportResourceStateRequest) providers.ImportResourceStateResponse { - p.Lock() - defer p.Unlock() + if p.ApplyResourceChangeResponse != nil { + return *p.ApplyResourceChangeResponse + } - if p.ImportStateReturn != nil { - for _, is := range p.ImportStateReturn { - if is.Attributes == nil { - is.Attributes = make(map[string]string) - } - is.Attributes["id"] = is.ID + schema, ok := p.getProviderSchema().ResourceTypes[r.TypeName] + if !ok { + resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName)) + return resp + } - typeName := is.Ephemeral.Type - // Use the requested type if the resource has no type of it's own. - // We still return the empty type, which will error, but this prevents a panic. - if typeName == "" { - typeName = r.TypeName - } + // if the value is nil, we return that directly to correspond to a delete + if r.PlannedState.IsNull() { + resp.NewState = cty.NullVal(schema.Block.ImpliedType()) + return resp + } - schema := p.GetSchemaReturn.ResourceTypes[typeName] - if schema == nil { - panic("no schema found for " + typeName) - } + val, err := schema.Block.CoerceValue(r.PlannedState) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } - private, err := json.Marshal(is.Meta) - if err != nil { - panic(err) + // the default behavior will be to create the minimal valid apply value by + // setting unknowns (which correspond to computed attributes) to a zero + // value. + val, _ = cty.Transform(val, func(path cty.Path, v cty.Value) (cty.Value, error) { + if !v.IsKnown() { + ty := v.Type() + switch { + case ty == cty.String: + return cty.StringVal(""), nil + case ty == cty.Number: + return cty.NumberIntVal(0), nil + case ty == cty.Bool: + return cty.False, nil + case ty.IsMapType(): + return cty.MapValEmpty(ty.ElementType()), nil + case ty.IsListType(): + return cty.ListValEmpty(ty.ElementType()), nil + default: + return cty.NullVal(ty), nil } + } + return v, nil + }) - state, err := hcl2shim.HCL2ValueFromFlatmap(is.Attributes, schema.ImpliedType()) - if err != nil { - panic(err) - } + resp.NewState = val + resp.Private = r.PlannedPrivate - state, err = schema.CoerceValue(state) - if err != nil { - panic(err) - } + return resp +} - p.ImportResourceStateResponse.ImportedResources = append( - p.ImportResourceStateResponse.ImportedResources, - providers.ImportedResource{ - TypeName: is.Ephemeral.Type, - State: state, - Private: private, - }) - } - } +func (p *MockProvider) ImportResourceState(r providers.ImportResourceStateRequest) (resp providers.ImportResourceStateResponse) { + p.Lock() + defer p.Unlock() p.ImportResourceStateCalled = true p.ImportResourceStateRequest = r @@ -334,10 +466,31 @@ func (p *MockProvider) ImportResourceState(r providers.ImportResourceStateReques return p.ImportResourceStateFn(r) } - return p.ImportResourceStateResponse + if p.ImportResourceStateResponse != nil { + resp = *p.ImportResourceStateResponse + // fixup the cty value to match the schema + for i, res := range resp.ImportedResources { + schema, ok := p.getProviderSchema().ResourceTypes[res.TypeName] + if !ok { + resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", res.TypeName)) + return resp + } + + var err error + res.State, err = schema.Block.CoerceValue(res.State) + if err != nil { + resp.Diagnostics = resp.Diagnostics.Append(err) + return resp + } + + resp.ImportedResources[i] = res + } + } + + return resp } -func (p *MockProvider) ReadDataSource(r providers.ReadDataSourceRequest) providers.ReadDataSourceResponse { +func (p *MockProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { p.Lock() defer p.Unlock() @@ -348,7 +501,11 @@ func (p *MockProvider) ReadDataSource(r providers.ReadDataSourceRequest) provide return p.ReadDataSourceFn(r) } - return p.ReadDataSourceResponse + if p.ReadDataSourceResponse != nil { + resp = *p.ReadDataSourceResponse + } + + return resp } func (p *MockProvider) Close() error { diff --git a/vendor/github.com/hashicorp/terraform/terraform/provisioner_mock_test.go b/vendor/github.com/hashicorp/terraform/terraform/provisioner_mock_test.go new file mode 100644 index 00000000..242c09b6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/provisioner_mock_test.go @@ -0,0 +1,27 @@ +package terraform + +import ( + "github.com/hashicorp/terraform/provisioners" +) + +// simpleMockProvisioner returns a MockProvisioner that is pre-configured +// with schema for its own config, with the same content as returned by +// function simpleTestSchema. +// +// For most reasonable uses the returned provisioner must be registered in a +// componentFactory under the name "test". Use simpleMockComponentFactory +// to obtain a pre-configured componentFactory containing the result of +// this function along with simpleMockProvider, both registered as "test". +// +// The returned provisioner has no other behaviors by default, but the caller +// may modify it in order to stub any other required functionality, or modify +// the default schema stored in the field GetSchemaReturn. Each new call to +// simpleTestProvisioner produces entirely new instances of all of the nested +// objects so that callers can mutate without affecting mock objects. +func simpleMockProvisioner() *MockProvisioner { + return &MockProvisioner{ + GetSchemaResponse: provisioners.GetSchemaResponse{ + Provisioner: simpleTestSchema(), + }, + } +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/reduce_plan.go b/vendor/github.com/hashicorp/terraform/terraform/reduce_plan.go new file mode 100644 index 00000000..097fe6aa --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/reduce_plan.go @@ -0,0 +1,32 @@ +package terraform + +import ( + "log" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/plans" +) + +// reducePlan takes a planned resource instance change as might be produced by +// Plan or PlanDestroy and "simplifies" it to a single atomic action to be +// performed by a specific graph node. +// +// Callers must specify whether they are a destroy node or a regular apply node. +// If the result is NoOp then the given change requires no action for the +// specific graph node calling this and so evaluation of the that graph node +// should exit early and take no action. +// +// The returned object may either be identical to the input change or a new +// change object derived from the input. Because of the former case, the caller +// must not mutate the object returned in OutChange. +func reducePlan(addr addrs.ResourceInstance, in *plans.ResourceInstanceChange, destroy bool) *plans.ResourceInstanceChange { + out := in.Simplify(destroy) + if out.Action != in.Action { + if destroy { + log.Printf("[TRACE] reducePlan: %s change simplified from %s to %s for destroy node", addr, in.Action, out.Action) + } else { + log.Printf("[TRACE] reducePlan: %s change simplified from %s to %s for apply node", addr, in.Action, out.Action) + } + } + return out +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/reduce_plan_test.go b/vendor/github.com/hashicorp/terraform/terraform/reduce_plan_test.go new file mode 100644 index 00000000..30d819aa --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/reduce_plan_test.go @@ -0,0 +1,395 @@ +package terraform + +import ( + "testing" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclsyntax" + "github.com/zclconf/go-cty/cty" +) + +func TestProcessIgnoreChangesIndividual(t *testing.T) { + tests := map[string]struct { + Old, New cty.Value + Ignore []string + Want cty.Value + }{ + "string": { + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("a value"), + "b": cty.StringVal("b value"), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("new a value"), + "b": cty.StringVal("new b value"), + }), + []string{"a"}, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("a value"), + "b": cty.StringVal("new b value"), + }), + }, + "changed type": { + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("a value"), + "b": cty.StringVal("b value"), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NumberIntVal(1), + "b": cty.StringVal("new b value"), + }), + []string{"a"}, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("a value"), + "b": cty.StringVal("new b value"), + }), + }, + "list": { + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.StringVal("a0 value"), + cty.StringVal("a1 value"), + }), + "b": cty.StringVal("b value"), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.StringVal("new a0 value"), + cty.StringVal("new a1 value"), + }), + "b": cty.StringVal("new b value"), + }), + []string{"a"}, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.StringVal("a0 value"), + cty.StringVal("a1 value"), + }), + "b": cty.StringVal("new b value"), + }), + }, + "list_index": { + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.StringVal("a0 value"), + cty.StringVal("a1 value"), + }), + "b": cty.StringVal("b value"), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.StringVal("new a0 value"), + cty.StringVal("new a1 value"), + }), + "b": cty.StringVal("new b value"), + }), + []string{"a[1]"}, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ListVal([]cty.Value{ + cty.StringVal("new a0 value"), + cty.StringVal("a1 value"), + }), + "b": cty.StringVal("new b value"), + }), + }, + "map": { + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a0": cty.StringVal("a0 value"), + "a1": cty.StringVal("a1 value"), + }), + "b": cty.StringVal("b value"), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a0": cty.StringVal("new a0 value"), + "a1": cty.UnknownVal(cty.String), + }), + "b": cty.StringVal("b value"), + }), + []string{`a`}, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a0": cty.StringVal("a0 value"), + "a1": cty.StringVal("a1 value"), + }), + "b": cty.StringVal("b value"), + }), + }, + "map_index": { + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a0": cty.StringVal("a0 value"), + "a1": cty.StringVal("a1 value"), + }), + "b": cty.StringVal("b value"), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a0": cty.StringVal("new a0 value"), + "a1": cty.StringVal("new a1 value"), + }), + "b": cty.StringVal("b value"), + }), + []string{`a["a1"]`}, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a0": cty.StringVal("new a0 value"), + "a1": cty.StringVal("a1 value"), + }), + "b": cty.StringVal("b value"), + }), + }, + "map_index_no_config": { + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a0": cty.StringVal("a0 value"), + "a1": cty.StringVal("a1 value"), + }), + "b": cty.StringVal("b value"), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.Map(cty.String)), + "b": cty.StringVal("b value"), + }), + []string{`a["a1"]`}, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a1": cty.StringVal("a1 value"), + }), + "b": cty.StringVal("b value"), + }), + }, + "map_index_unknown_value": { + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a0": cty.StringVal("a0 value"), + "a1": cty.StringVal("a1 value"), + }), + "b": cty.StringVal("b value"), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a0": cty.StringVal("a0 value"), + "a1": cty.UnknownVal(cty.String), + }), + "b": cty.StringVal("b value"), + }), + []string{`a["a1"]`}, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a0": cty.StringVal("a0 value"), + "a1": cty.StringVal("a1 value"), + }), + "b": cty.StringVal("b value"), + }), + }, + "map_index_multiple_keys": { + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a0": cty.StringVal("a0 value"), + "a1": cty.StringVal("a1 value"), + "a2": cty.StringVal("a2 value"), + "a3": cty.StringVal("a3 value"), + }), + "b": cty.StringVal("b value"), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.Map(cty.String)), + "b": cty.StringVal("new b value"), + }), + []string{`a["a1"]`, `a["a2"]`, `a["a3"]`, `b`}, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a1": cty.StringVal("a1 value"), + "a2": cty.StringVal("a2 value"), + "a3": cty.StringVal("a3 value"), + }), + "b": cty.StringVal("b value"), + }), + }, + "map_index_redundant": { + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a0": cty.StringVal("a0 value"), + "a1": cty.StringVal("a1 value"), + "a2": cty.StringVal("a2 value"), + }), + "b": cty.StringVal("b value"), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NullVal(cty.Map(cty.String)), + "b": cty.StringVal("new b value"), + }), + []string{`a["a1"]`, `a`, `b`}, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a0": cty.StringVal("a0 value"), + "a1": cty.StringVal("a1 value"), + "a2": cty.StringVal("a2 value"), + }), + "b": cty.StringVal("b value"), + }), + }, + "missing_map_index": { + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a0": cty.StringVal("a0 value"), + "a1": cty.StringVal("a1 value"), + }), + "b": cty.StringVal("b value"), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapValEmpty(cty.String), + "b": cty.StringVal("b value"), + }), + []string{`a["a1"]`}, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a1": cty.StringVal("a1 value"), + }), + "b": cty.StringVal("b value"), + }), + }, + "missing_map_index_empty": { + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapValEmpty(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a": cty.StringVal("a0 value"), + }), + }), + []string{`a["a"]`}, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapValEmpty(cty.String), + }), + }, + "missing_map_index_to_object": { + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("aa0"), + "b": cty.StringVal("ab0"), + }), + "b": cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("ba0"), + "b": cty.StringVal("bb0"), + }), + }), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapValEmpty( + cty.Object(map[string]cty.Type{ + "a": cty.String, + "b": cty.String, + }), + ), + }), + // we expect the config to be used here, as the ignore changes was + // `a["a"].b`, but the change was larger than that removing + // `a["a"]` entirely. + []string{`a["a"].b`}, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapValEmpty( + cty.Object(map[string]cty.Type{ + "a": cty.String, + "b": cty.String, + }), + ), + }), + }, + "missing_prior_map_index": { + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a0": cty.StringVal("a0 value"), + }), + "b": cty.StringVal("b value"), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a0": cty.StringVal("a0 value"), + "a1": cty.StringVal("new a1 value"), + }), + "b": cty.StringVal("b value"), + }), + []string{`a["a1"]`}, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{ + "a0": cty.StringVal("a0 value"), + }), + "b": cty.StringVal("b value"), + }), + }, + "object attribute": { + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("a.foo value"), + "bar": cty.StringVal("a.bar value"), + }), + "b": cty.StringVal("b value"), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("new a.foo value"), + "bar": cty.StringVal("new a.bar value"), + }), + "b": cty.StringVal("new b value"), + }), + []string{"a.bar"}, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("new a.foo value"), + "bar": cty.StringVal("a.bar value"), + }), + "b": cty.StringVal("new b value"), + }), + }, + "unknown_object_attribute": { + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("a.foo value"), + "bar": cty.StringVal("a.bar value"), + }), + "b": cty.StringVal("b value"), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("new a.foo value"), + "bar": cty.UnknownVal(cty.String), + }), + "b": cty.StringVal("new b value"), + }), + []string{"a.bar"}, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "foo": cty.StringVal("new a.foo value"), + "bar": cty.StringVal("a.bar value"), + }), + "b": cty.StringVal("new b value"), + }), + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + ignore := make([]hcl.Traversal, len(test.Ignore)) + for i, ignoreStr := range test.Ignore { + trav, diags := hclsyntax.ParseTraversalAbs([]byte(ignoreStr), "", hcl.Pos{Line: 1, Column: 1}) + if diags.HasErrors() { + t.Fatalf("failed to parse %q: %s", ignoreStr, diags.Error()) + } + ignore[i] = trav + } + + ret, diags := processIgnoreChangesIndividual(test.Old, test.New, ignore) + if diags.HasErrors() { + t.Fatal(diags.Err()) + } + + if got, want := ret, test.Want; !want.RawEquals(got) { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want) + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource_provider.go b/vendor/github.com/hashicorp/terraform/terraform/resource_provider.go index dccfec68..d4bdfcb6 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/resource_provider.go +++ b/vendor/github.com/hashicorp/terraform/terraform/resource_provider.go @@ -1,229 +1,6 @@ package terraform -// ResourceProvider is a legacy interface for providers. -// -// This is retained only for compatibility with legacy code. The current -// interface for providers is providers.Interface, in the sibling directory -// named "providers". -type ResourceProvider interface { - /********************************************************************* - * Functions related to the provider - *********************************************************************/ - - // ProviderSchema returns the config schema for the main provider - // configuration, as would appear in a "provider" block in the - // configuration files. - // - // Currently not all providers support schema. Callers must therefore - // first call Resources and DataSources and ensure that at least one - // resource or data source has the SchemaAvailable flag set. - GetSchema(*ProviderSchemaRequest) (*ProviderSchema, error) - - // Input was used prior to v0.12 to ask the provider to prompt the user - // for input to complete the configuration. - // - // From v0.12 onwards this method is never called because Terraform Core - // is able to handle the necessary input logic itself based on the - // schema returned from GetSchema. - Input(UIInput, *ResourceConfig) (*ResourceConfig, error) - - // Validate is called once at the beginning with the raw configuration - // (no interpolation done) and can return a list of warnings and/or - // errors. - // - // This is called once with the provider configuration only. It may not - // be called at all if no provider configuration is given. - // - // This should not assume that any values of the configurations are valid. - // The primary use case of this call is to check that required keys are - // set. - Validate(*ResourceConfig) ([]string, []error) - - // Configure configures the provider itself with the configuration - // given. This is useful for setting things like access keys. - // - // This won't be called at all if no provider configuration is given. - // - // Configure returns an error if it occurred. - Configure(*ResourceConfig) error - - // Resources returns all the available resource types that this provider - // knows how to manage. - Resources() []ResourceType - - // Stop is called when the provider should halt any in-flight actions. - // - // This can be used to make a nicer Ctrl-C experience for Terraform. - // Even if this isn't implemented to do anything (just returns nil), - // Terraform will still cleanly stop after the currently executing - // graph node is complete. However, this API can be used to make more - // efficient halts. - // - // Stop doesn't have to and shouldn't block waiting for in-flight actions - // to complete. It should take any action it wants and return immediately - // acknowledging it has received the stop request. Terraform core will - // automatically not make any further API calls to the provider soon - // after Stop is called (technically exactly once the currently executing - // graph nodes are complete). - // - // The error returned, if non-nil, is assumed to mean that signaling the - // stop somehow failed and that the user should expect potentially waiting - // a longer period of time. - Stop() error - - /********************************************************************* - * Functions related to individual resources - *********************************************************************/ - - // ValidateResource is called once at the beginning with the raw - // configuration (no interpolation done) and can return a list of warnings - // and/or errors. - // - // This is called once per resource. - // - // This should not assume any of the values in the resource configuration - // are valid since it is possible they have to be interpolated still. - // The primary use case of this call is to check that the required keys - // are set and that the general structure is correct. - ValidateResource(string, *ResourceConfig) ([]string, []error) - - // Apply applies a diff to a specific resource and returns the new - // resource state along with an error. - // - // If the resource state given has an empty ID, then a new resource - // is expected to be created. - Apply( - *InstanceInfo, - *InstanceState, - *InstanceDiff) (*InstanceState, error) - - // Diff diffs a resource versus a desired state and returns - // a diff. - Diff( - *InstanceInfo, - *InstanceState, - *ResourceConfig) (*InstanceDiff, error) - - // Refresh refreshes a resource and updates all of its attributes - // with the latest information. - Refresh(*InstanceInfo, *InstanceState) (*InstanceState, error) - - /********************************************************************* - * Functions related to importing - *********************************************************************/ - - // ImportState requests that the given resource be imported. - // - // The returned InstanceState only requires ID be set. Importing - // will always call Refresh after the state to complete it. - // - // IMPORTANT: InstanceState doesn't have the resource type attached - // to it. A type must be specified on the state via the Ephemeral - // field on the state. - // - // This function can return multiple states. Normally, an import - // will map 1:1 to a physical resource. However, some resources map - // to multiple. For example, an AWS security group may contain many rules. - // Each rule is represented by a separate resource in Terraform, - // therefore multiple states are returned. - ImportState(*InstanceInfo, string) ([]*InstanceState, error) - - /********************************************************************* - * Functions related to data resources - *********************************************************************/ - - // ValidateDataSource is called once at the beginning with the raw - // configuration (no interpolation done) and can return a list of warnings - // and/or errors. - // - // This is called once per data source instance. - // - // This should not assume any of the values in the resource configuration - // are valid since it is possible they have to be interpolated still. - // The primary use case of this call is to check that the required keys - // are set and that the general structure is correct. - ValidateDataSource(string, *ResourceConfig) ([]string, []error) - - // DataSources returns all of the available data sources that this - // provider implements. - DataSources() []DataSource - - // ReadDataDiff produces a diff that represents the state that will - // be produced when the given data source is read using a later call - // to ReadDataApply. - ReadDataDiff(*InstanceInfo, *ResourceConfig) (*InstanceDiff, error) - - // ReadDataApply initializes a data instance using the configuration - // in a diff produced by ReadDataDiff. - ReadDataApply(*InstanceInfo, *InstanceDiff) (*InstanceState, error) -} - -// ResourceProviderCloser is an interface that providers that can close -// connections that aren't needed anymore must implement. -type ResourceProviderCloser interface { - Close() error -} - -// ResourceType is a type of resource that a resource provider can manage. -type ResourceType struct { - Name string // Name of the resource, example "instance" (no provider prefix) - Importable bool // Whether this resource supports importing - - // SchemaAvailable is set if the provider supports the ProviderSchema, - // ResourceTypeSchema and DataSourceSchema methods. Although it is - // included on each resource type, it's actually a provider-wide setting - // that's smuggled here only because that avoids a breaking change to - // the plugin protocol. - SchemaAvailable bool -} - -// DataSource is a data source that a resource provider implements. -type DataSource struct { - Name string - - // SchemaAvailable is set if the provider supports the ProviderSchema, - // ResourceTypeSchema and DataSourceSchema methods. Although it is - // included on each resource type, it's actually a provider-wide setting - // that's smuggled here only because that avoids a breaking change to - // the plugin protocol. - SchemaAvailable bool -} - -// ResourceProviderFactory is a function type that creates a new instance -// of a resource provider. -type ResourceProviderFactory func() (ResourceProvider, error) - -// ResourceProviderFactoryFixed is a helper that creates a -// ResourceProviderFactory that just returns some fixed provider. -func ResourceProviderFactoryFixed(p ResourceProvider) ResourceProviderFactory { - return func() (ResourceProvider, error) { - return p, nil - } -} - -func ProviderHasResource(p ResourceProvider, n string) bool { - for _, rt := range p.Resources() { - if rt.Name == n { - return true - } - } - - return false -} - -func ProviderHasDataSource(p ResourceProvider, n string) bool { - for _, rt := range p.DataSources() { - if rt.Name == n { - return true - } - } - - return false -} - const errPluginInit = ` -Plugin reinitialization required. Please run "terraform init". - Plugins are external binaries that Terraform uses to access and manipulate resources. The configuration provided requires plugins which can't be located, don't satisfy the version constraints, or are otherwise incompatible. @@ -233,4 +10,7 @@ configuration, including providers used in child modules. To see the requirements and constraints, run "terraform providers". %s + +Plugin reinitialization required. Please address the above error(s) and run +"terraform init". ` diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource_provider_mock.go b/vendor/github.com/hashicorp/terraform/terraform/resource_provider_mock.go deleted file mode 100644 index 4000e3d2..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/resource_provider_mock.go +++ /dev/null @@ -1,315 +0,0 @@ -package terraform - -import ( - "sync" -) - -// MockResourceProvider implements ResourceProvider but mocks out all the -// calls for testing purposes. -type MockResourceProvider struct { - sync.Mutex - - // Anything you want, in case you need to store extra data with the mock. - Meta interface{} - - CloseCalled bool - CloseError error - GetSchemaCalled bool - GetSchemaRequest *ProviderSchemaRequest - GetSchemaReturn *ProviderSchema - GetSchemaReturnError error - InputCalled bool - InputInput UIInput - InputConfig *ResourceConfig - InputReturnConfig *ResourceConfig - InputReturnError error - InputFn func(UIInput, *ResourceConfig) (*ResourceConfig, error) - ApplyCalled bool - ApplyInfo *InstanceInfo - ApplyState *InstanceState - ApplyDiff *InstanceDiff - ApplyFn func(*InstanceInfo, *InstanceState, *InstanceDiff) (*InstanceState, error) - ApplyReturn *InstanceState - ApplyReturnError error - ConfigureCalled bool - ConfigureConfig *ResourceConfig - ConfigureFn func(*ResourceConfig) error - ConfigureReturnError error - DiffCalled bool - DiffInfo *InstanceInfo - DiffState *InstanceState - DiffDesired *ResourceConfig - DiffFn func(*InstanceInfo, *InstanceState, *ResourceConfig) (*InstanceDiff, error) - DiffReturn *InstanceDiff - DiffReturnError error - RefreshCalled bool - RefreshInfo *InstanceInfo - RefreshState *InstanceState - RefreshFn func(*InstanceInfo, *InstanceState) (*InstanceState, error) - RefreshReturn *InstanceState - RefreshReturnError error - ResourcesCalled bool - ResourcesReturn []ResourceType - ReadDataApplyCalled bool - ReadDataApplyInfo *InstanceInfo - ReadDataApplyDiff *InstanceDiff - ReadDataApplyFn func(*InstanceInfo, *InstanceDiff) (*InstanceState, error) - ReadDataApplyReturn *InstanceState - ReadDataApplyReturnError error - ReadDataDiffCalled bool - ReadDataDiffInfo *InstanceInfo - ReadDataDiffDesired *ResourceConfig - ReadDataDiffFn func(*InstanceInfo, *ResourceConfig) (*InstanceDiff, error) - ReadDataDiffReturn *InstanceDiff - ReadDataDiffReturnError error - StopCalled bool - StopFn func() error - StopReturnError error - DataSourcesCalled bool - DataSourcesReturn []DataSource - ValidateCalled bool - ValidateConfig *ResourceConfig - ValidateFn func(*ResourceConfig) ([]string, []error) - ValidateReturnWarns []string - ValidateReturnErrors []error - ValidateResourceFn func(string, *ResourceConfig) ([]string, []error) - ValidateResourceCalled bool - ValidateResourceType string - ValidateResourceConfig *ResourceConfig - ValidateResourceReturnWarns []string - ValidateResourceReturnErrors []error - ValidateDataSourceFn func(string, *ResourceConfig) ([]string, []error) - ValidateDataSourceCalled bool - ValidateDataSourceType string - ValidateDataSourceConfig *ResourceConfig - ValidateDataSourceReturnWarns []string - ValidateDataSourceReturnErrors []error - - ImportStateCalled bool - ImportStateInfo *InstanceInfo - ImportStateID string - ImportStateReturn []*InstanceState - ImportStateReturnError error - ImportStateFn func(*InstanceInfo, string) ([]*InstanceState, error) -} - -func (p *MockResourceProvider) Close() error { - p.CloseCalled = true - return p.CloseError -} - -func (p *MockResourceProvider) GetSchema(req *ProviderSchemaRequest) (*ProviderSchema, error) { - p.Lock() - defer p.Unlock() - - p.GetSchemaCalled = true - p.GetSchemaRequest = req - return p.GetSchemaReturn, p.GetSchemaReturnError -} - -func (p *MockResourceProvider) Input( - input UIInput, c *ResourceConfig) (*ResourceConfig, error) { - p.Lock() - defer p.Unlock() - p.InputCalled = true - p.InputInput = input - p.InputConfig = c - if p.InputFn != nil { - return p.InputFn(input, c) - } - return p.InputReturnConfig, p.InputReturnError -} - -func (p *MockResourceProvider) Validate(c *ResourceConfig) ([]string, []error) { - p.Lock() - defer p.Unlock() - - p.ValidateCalled = true - p.ValidateConfig = c - if p.ValidateFn != nil { - return p.ValidateFn(c) - } - return p.ValidateReturnWarns, p.ValidateReturnErrors -} - -func (p *MockResourceProvider) ValidateResource(t string, c *ResourceConfig) ([]string, []error) { - p.Lock() - defer p.Unlock() - - p.ValidateResourceCalled = true - p.ValidateResourceType = t - p.ValidateResourceConfig = c - - if p.ValidateResourceFn != nil { - return p.ValidateResourceFn(t, c) - } - - return p.ValidateResourceReturnWarns, p.ValidateResourceReturnErrors -} - -func (p *MockResourceProvider) Configure(c *ResourceConfig) error { - p.Lock() - defer p.Unlock() - - p.ConfigureCalled = true - p.ConfigureConfig = c - - if p.ConfigureFn != nil { - return p.ConfigureFn(c) - } - - return p.ConfigureReturnError -} - -func (p *MockResourceProvider) Stop() error { - p.Lock() - defer p.Unlock() - - p.StopCalled = true - if p.StopFn != nil { - return p.StopFn() - } - - return p.StopReturnError -} - -func (p *MockResourceProvider) Apply( - info *InstanceInfo, - state *InstanceState, - diff *InstanceDiff) (*InstanceState, error) { - // We only lock while writing data. Reading is fine - p.Lock() - p.ApplyCalled = true - p.ApplyInfo = info - p.ApplyState = state - p.ApplyDiff = diff - p.Unlock() - - if p.ApplyFn != nil { - return p.ApplyFn(info, state, diff) - } - - return p.ApplyReturn.DeepCopy(), p.ApplyReturnError -} - -func (p *MockResourceProvider) Diff( - info *InstanceInfo, - state *InstanceState, - desired *ResourceConfig) (*InstanceDiff, error) { - p.Lock() - defer p.Unlock() - - p.DiffCalled = true - p.DiffInfo = info - p.DiffState = state - p.DiffDesired = desired - - if p.DiffFn != nil { - return p.DiffFn(info, state, desired) - } - - return p.DiffReturn.DeepCopy(), p.DiffReturnError -} - -func (p *MockResourceProvider) Refresh( - info *InstanceInfo, - s *InstanceState) (*InstanceState, error) { - p.Lock() - defer p.Unlock() - - p.RefreshCalled = true - p.RefreshInfo = info - p.RefreshState = s - - if p.RefreshFn != nil { - return p.RefreshFn(info, s) - } - - return p.RefreshReturn.DeepCopy(), p.RefreshReturnError -} - -func (p *MockResourceProvider) Resources() []ResourceType { - p.Lock() - defer p.Unlock() - - p.ResourcesCalled = true - return p.ResourcesReturn -} - -func (p *MockResourceProvider) ImportState(info *InstanceInfo, id string) ([]*InstanceState, error) { - p.Lock() - defer p.Unlock() - - p.ImportStateCalled = true - p.ImportStateInfo = info - p.ImportStateID = id - if p.ImportStateFn != nil { - return p.ImportStateFn(info, id) - } - - var result []*InstanceState - if p.ImportStateReturn != nil { - result = make([]*InstanceState, len(p.ImportStateReturn)) - for i, v := range p.ImportStateReturn { - result[i] = v.DeepCopy() - } - } - - return result, p.ImportStateReturnError -} - -func (p *MockResourceProvider) ValidateDataSource(t string, c *ResourceConfig) ([]string, []error) { - p.Lock() - defer p.Unlock() - - p.ValidateDataSourceCalled = true - p.ValidateDataSourceType = t - p.ValidateDataSourceConfig = c - - if p.ValidateDataSourceFn != nil { - return p.ValidateDataSourceFn(t, c) - } - - return p.ValidateDataSourceReturnWarns, p.ValidateDataSourceReturnErrors -} - -func (p *MockResourceProvider) ReadDataDiff( - info *InstanceInfo, - desired *ResourceConfig) (*InstanceDiff, error) { - p.Lock() - defer p.Unlock() - - p.ReadDataDiffCalled = true - p.ReadDataDiffInfo = info - p.ReadDataDiffDesired = desired - if p.ReadDataDiffFn != nil { - return p.ReadDataDiffFn(info, desired) - } - - return p.ReadDataDiffReturn.DeepCopy(), p.ReadDataDiffReturnError -} - -func (p *MockResourceProvider) ReadDataApply( - info *InstanceInfo, - d *InstanceDiff) (*InstanceState, error) { - p.Lock() - defer p.Unlock() - - p.ReadDataApplyCalled = true - p.ReadDataApplyInfo = info - p.ReadDataApplyDiff = d - - if p.ReadDataApplyFn != nil { - return p.ReadDataApplyFn(info, d) - } - - return p.ReadDataApplyReturn.DeepCopy(), p.ReadDataApplyReturnError -} - -func (p *MockResourceProvider) DataSources() []DataSource { - p.Lock() - defer p.Unlock() - - p.DataSourcesCalled = true - return p.DataSourcesReturn -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource_provider_mock_test.go b/vendor/github.com/hashicorp/terraform/terraform/resource_provider_mock_test.go index 98134bdc..191d9df8 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/resource_provider_mock_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/resource_provider_mock_test.go @@ -1,35 +1,17 @@ package terraform import ( - "testing" - - "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/providers" "github.com/zclconf/go-cty/cty" ) -func TestMockResourceProvider_impl(t *testing.T) { - var _ ResourceProvider = new(MockResourceProvider) - var _ ResourceProviderCloser = new(MockResourceProvider) -} - -// testProviderComponentFactory creates a componentFactory that contains only -// a single given. -func testProviderComponentFactory(name string, provider providers.Interface) *basicComponentFactory { - return &basicComponentFactory{ - providers: map[addrs.Provider]providers.Factory{ - addrs.NewDefaultProvider(name): providers.FactoryFixed(provider), - }, - } -} - // mockProviderWithConfigSchema is a test helper to concisely create a mock // provider with the given schema for its own configuration. func mockProviderWithConfigSchema(schema *configschema.Block) *MockProvider { return &MockProvider{ - GetSchemaReturn: &ProviderSchema{ - Provider: schema, + GetProviderSchemaResponse: &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{Block: schema}, }, } } @@ -38,40 +20,81 @@ func mockProviderWithConfigSchema(schema *configschema.Block) *MockProvider { // provider with a schema containing a single resource type. func mockProviderWithResourceTypeSchema(name string, schema *configschema.Block) *MockProvider { return &MockProvider{ - GetSchemaReturn: &ProviderSchema{ - Provider: &configschema.Block{ - Attributes: map[string]*configschema.Attribute{ - "string": { - Type: cty.String, - Optional: true, - }, - "list": { - Type: cty.List(cty.String), - Optional: true, - }, - "root": { - Type: cty.Map(cty.String), - Optional: true, + GetProviderSchemaResponse: &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "string": { + Type: cty.String, + Optional: true, + }, + "list": { + Type: cty.List(cty.String), + Optional: true, + }, + "root": { + Type: cty.Map(cty.String), + Optional: true, + }, }, }, }, - ResourceTypes: map[string]*configschema.Block{ - name: schema, + ResourceTypes: map[string]providers.Schema{ + name: providers.Schema{Block: schema}, }, }, } } -// mockProviderWithDataSourceSchema is a test helper to concisely create a mock -// provider with a schema containing a single data source. -func mockProviderWithDataSourceSchema(name string, schema *configschema.Block) *MockResourceProvider { - return &MockResourceProvider{ - GetSchemaReturn: &ProviderSchema{ - DataSources: map[string]*configschema.Block{ - name: schema, +// mockProviderWithProviderSchema is a test helper to create a mock provider +// from an existing ProviderSchema. +func mockProviderWithProviderSchema(providerSchema ProviderSchema) *MockProvider { + p := &MockProvider{ + GetProviderSchemaResponse: &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{ + Block: providerSchema.Provider, }, + ResourceTypes: map[string]providers.Schema{}, + DataSources: map[string]providers.Schema{}, }, } + + for name, schema := range providerSchema.ResourceTypes { + p.GetProviderSchemaResponse.ResourceTypes[name] = providers.Schema{ + Block: schema, + Version: int64(providerSchema.ResourceTypeSchemaVersions[name]), + } + } + + for name, schema := range providerSchema.DataSources { + p.GetProviderSchemaResponse.DataSources[name] = providers.Schema{Block: schema} + } + + return p +} + +// getProviderSchemaResponseFromProviderSchema is a test helper to convert a +// ProviderSchema to a GetProviderSchemaResponse for use when building a mock provider. +func getProviderSchemaResponseFromProviderSchema(providerSchema *ProviderSchema) *providers.GetProviderSchemaResponse { + resp := &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{Block: providerSchema.Provider}, + ProviderMeta: providers.Schema{Block: providerSchema.ProviderMeta}, + ResourceTypes: map[string]providers.Schema{}, + DataSources: map[string]providers.Schema{}, + } + + for name, schema := range providerSchema.ResourceTypes { + resp.ResourceTypes[name] = providers.Schema{ + Block: schema, + Version: int64(providerSchema.ResourceTypeSchemaVersions[name]), + } + } + + for name, schema := range providerSchema.DataSources { + resp.DataSources[name] = providers.Schema{Block: schema} + } + + return resp } // simpleMockProvider returns a MockProvider that is pre-configured @@ -93,13 +116,13 @@ func mockProviderWithDataSourceSchema(name string, schema *configschema.Block) * // objects so that callers can mutate without affecting mock objects. func simpleMockProvider() *MockProvider { return &MockProvider{ - GetSchemaReturn: &ProviderSchema{ - Provider: simpleTestSchema(), - ResourceTypes: map[string]*configschema.Block{ - "test_object": simpleTestSchema(), + GetProviderSchemaResponse: &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{Block: simpleTestSchema()}, + ResourceTypes: map[string]providers.Schema{ + "test_object": providers.Schema{Block: simpleTestSchema()}, }, - DataSources: map[string]*configschema.Block{ - "test_object": simpleTestSchema(), + DataSources: map[string]providers.Schema{ + "test_object": providers.Schema{Block: simpleTestSchema()}, }, }, } diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource_provisioner_mock_test.go b/vendor/github.com/hashicorp/terraform/terraform/resource_provisioner_mock_test.go deleted file mode 100644 index 600a9825..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/resource_provisioner_mock_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package terraform - -import ( - "testing" - - "github.com/hashicorp/terraform/provisioners" -) - -func TestMockResourceProvisioner_impl(t *testing.T) { - var _ ResourceProvisioner = new(MockResourceProvisioner) -} - -// simpleMockProvisioner returns a MockProvisioner that is pre-configured -// with schema for its own config, with the same content as returned by -// function simpleTestSchema. -// -// For most reasonable uses the returned provisioner must be registered in a -// componentFactory under the name "test". Use simpleMockComponentFactory -// to obtain a pre-configured componentFactory containing the result of -// this function along with simpleMockProvider, both registered as "test". -// -// The returned provisioner has no other behaviors by default, but the caller -// may modify it in order to stub any other required functionality, or modify -// the default schema stored in the field GetSchemaReturn. Each new call to -// simpleTestProvisioner produces entirely new instances of all of the nested -// objects so that callers can mutate without affecting mock objects. -func simpleMockProvisioner() *MockProvisioner { - return &MockProvisioner{ - GetSchemaResponse: provisioners.GetSchemaResponse{ - Provisioner: simpleTestSchema(), - }, - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/schemas.go b/vendor/github.com/hashicorp/terraform/terraform/schemas.go index 15f6d5e7..017856ef 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/schemas.go +++ b/vendor/github.com/hashicorp/terraform/terraform/schemas.go @@ -106,7 +106,7 @@ func loadProviderSchemas(schemas map[addrs.Provider]*ProviderSchema, config *con // future calls. schemas[fqn] = &ProviderSchema{} diags = diags.Append( - fmt.Errorf("Failed to instantiate provider %q to obtain schema: %s", name, err), + fmt.Errorf("failed to instantiate provider %q to obtain schema: %s", name, err), ) return } @@ -114,13 +114,13 @@ func loadProviderSchemas(schemas map[addrs.Provider]*ProviderSchema, config *con provider.Close() }() - resp := provider.GetSchema() + resp := provider.GetProviderSchema() if resp.Diagnostics.HasErrors() { // We'll put a stub in the map so we won't re-attempt this on // future calls. schemas[fqn] = &ProviderSchema{} diags = diags.Append( - fmt.Errorf("Failed to retrieve schema from provider %q: %s", name, resp.Diagnostics.Err()), + fmt.Errorf("failed to retrieve schema from provider %q: %s", name, resp.Diagnostics.Err()), ) return } @@ -142,6 +142,9 @@ func loadProviderSchemas(schemas map[addrs.Provider]*ProviderSchema, config *con } for t, r := range resp.ResourceTypes { + if err := r.Block.InternalValidate(); err != nil { + diags = diags.Append(fmt.Errorf(errProviderSchemaInvalid, name, "resource", t, err)) + } s.ResourceTypes[t] = r.Block s.ResourceTypeSchemaVersions[t] = uint64(r.Version) if r.Version < 0 { @@ -152,6 +155,9 @@ func loadProviderSchemas(schemas map[addrs.Provider]*ProviderSchema, config *con } for t, d := range resp.DataSources { + if err := d.Block.InternalValidate(); err != nil { + diags = diags.Append(fmt.Errorf(errProviderSchemaInvalid, name, "data source", t, err)) + } s.DataSources[t] = d.Block if d.Version < 0 { // We're not using the version numbers here yet, but we'll check @@ -200,14 +206,12 @@ func loadProvisionerSchemas(schemas map[string]*configschema.Block, config *conf // future calls. schemas[name] = &configschema.Block{} diags = diags.Append( - fmt.Errorf("Failed to instantiate provisioner %q to obtain schema: %s", name, err), + fmt.Errorf("failed to instantiate provisioner %q to obtain schema: %s", name, err), ) return } defer func() { - if closer, ok := provisioner.(ResourceProvisionerCloser); ok { - closer.Close() - } + provisioner.Close() }() resp := provisioner.GetSchema() @@ -216,7 +220,7 @@ func loadProvisionerSchemas(schemas map[string]*configschema.Block, config *conf // future calls. schemas[name] = &configschema.Block{} diags = diags.Append( - fmt.Errorf("Failed to retrieve schema from provisioner %q: %s", name, resp.Diagnostics.Err()), + fmt.Errorf("failed to retrieve schema from provisioner %q: %s", name, resp.Diagnostics.Err()), ) return } @@ -277,9 +281,10 @@ func (ps *ProviderSchema) SchemaForResourceAddr(addr addrs.Resource) (schema *co return ps.SchemaForResourceType(addr.Mode, addr.Type) } -// ProviderSchemaRequest is used to describe to a ResourceProvider which -// aspects of schema are required, when calling the GetSchema method. -type ProviderSchemaRequest struct { - ResourceTypes []string - DataSources []string -} +const errProviderSchemaInvalid = ` +Internal validation of the provider failed! This is always a bug with the +provider itself, and not a user issue. Please report this bug to the +maintainers of the %q provider: + +%s %s: %s +` diff --git a/vendor/github.com/hashicorp/terraform/terraform/schemas_test.go b/vendor/github.com/hashicorp/terraform/terraform/schemas_test.go index 06b20f73..b871884a 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/schemas_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/schemas_test.go @@ -8,9 +8,10 @@ import ( func simpleTestSchemas() *Schemas { provider := simpleMockProvider() provisioner := simpleMockProvisioner() + return &Schemas{ Providers: map[addrs.Provider]*ProviderSchema{ - addrs.NewDefaultProvider("test"): provider.GetSchemaReturn, + addrs.NewDefaultProvider("test"): provider.ProviderSchema(), }, Provisioners: map[string]*configschema.Block{ "test": provisioner.GetSchemaResponse.Provisioner, diff --git a/vendor/github.com/hashicorp/terraform/terraform/state_upgrade_v1_to_v2.go b/vendor/github.com/hashicorp/terraform/terraform/state_upgrade_v1_to_v2.go deleted file mode 100644 index aa13cce8..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/state_upgrade_v1_to_v2.go +++ /dev/null @@ -1,189 +0,0 @@ -package terraform - -import ( - "fmt" - - "github.com/mitchellh/copystructure" -) - -// upgradeStateV1ToV2 is used to upgrade a V1 state representation -// into a V2 state representation -func upgradeStateV1ToV2(old *stateV1) (*State, error) { - if old == nil { - return nil, nil - } - - remote, err := old.Remote.upgradeToV2() - if err != nil { - return nil, fmt.Errorf("Error upgrading State V1: %v", err) - } - - modules := make([]*ModuleState, len(old.Modules)) - for i, module := range old.Modules { - upgraded, err := module.upgradeToV2() - if err != nil { - return nil, fmt.Errorf("Error upgrading State V1: %v", err) - } - modules[i] = upgraded - } - if len(modules) == 0 { - modules = nil - } - - newState := &State{ - Version: 2, - Serial: old.Serial, - Remote: remote, - Modules: modules, - } - - newState.sort() - newState.init() - - return newState, nil -} - -func (old *remoteStateV1) upgradeToV2() (*RemoteState, error) { - if old == nil { - return nil, nil - } - - config, err := copystructure.Copy(old.Config) - if err != nil { - return nil, fmt.Errorf("Error upgrading RemoteState V1: %v", err) - } - - return &RemoteState{ - Type: old.Type, - Config: config.(map[string]string), - }, nil -} - -func (old *moduleStateV1) upgradeToV2() (*ModuleState, error) { - if old == nil { - return nil, nil - } - - pathRaw, err := copystructure.Copy(old.Path) - if err != nil { - return nil, fmt.Errorf("Error upgrading ModuleState V1: %v", err) - } - path, ok := pathRaw.([]string) - if !ok { - return nil, fmt.Errorf("Error upgrading ModuleState V1: path is not a list of strings") - } - if len(path) == 0 { - // We found some V1 states with a nil path. Assume root and catch - // duplicate path errors later (as part of Validate). - path = rootModulePath - } - - // Outputs needs upgrading to use the new structure - outputs := make(map[string]*OutputState) - for key, output := range old.Outputs { - outputs[key] = &OutputState{ - Type: "string", - Value: output, - Sensitive: false, - } - } - - resources := make(map[string]*ResourceState) - for key, oldResource := range old.Resources { - upgraded, err := oldResource.upgradeToV2() - if err != nil { - return nil, fmt.Errorf("Error upgrading ModuleState V1: %v", err) - } - resources[key] = upgraded - } - - dependencies, err := copystructure.Copy(old.Dependencies) - if err != nil { - return nil, fmt.Errorf("Error upgrading ModuleState V1: %v", err) - } - - return &ModuleState{ - Path: path, - Outputs: outputs, - Resources: resources, - Dependencies: dependencies.([]string), - }, nil -} - -func (old *resourceStateV1) upgradeToV2() (*ResourceState, error) { - if old == nil { - return nil, nil - } - - dependencies, err := copystructure.Copy(old.Dependencies) - if err != nil { - return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err) - } - - primary, err := old.Primary.upgradeToV2() - if err != nil { - return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err) - } - - deposed := make([]*InstanceState, len(old.Deposed)) - for i, v := range old.Deposed { - upgraded, err := v.upgradeToV2() - if err != nil { - return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err) - } - deposed[i] = upgraded - } - if len(deposed) == 0 { - deposed = nil - } - - return &ResourceState{ - Type: old.Type, - Dependencies: dependencies.([]string), - Primary: primary, - Deposed: deposed, - Provider: old.Provider, - }, nil -} - -func (old *instanceStateV1) upgradeToV2() (*InstanceState, error) { - if old == nil { - return nil, nil - } - - attributes, err := copystructure.Copy(old.Attributes) - if err != nil { - return nil, fmt.Errorf("Error upgrading InstanceState V1: %v", err) - } - ephemeral, err := old.Ephemeral.upgradeToV2() - if err != nil { - return nil, fmt.Errorf("Error upgrading InstanceState V1: %v", err) - } - - meta, err := copystructure.Copy(old.Meta) - if err != nil { - return nil, fmt.Errorf("Error upgrading InstanceState V1: %v", err) - } - - newMeta := make(map[string]interface{}) - for k, v := range meta.(map[string]string) { - newMeta[k] = v - } - - return &InstanceState{ - ID: old.ID, - Attributes: attributes.(map[string]string), - Ephemeral: *ephemeral, - Meta: newMeta, - }, nil -} - -func (old *ephemeralStateV1) upgradeToV2() (*EphemeralState, error) { - connInfo, err := copystructure.Copy(old.ConnInfo) - if err != nil { - return nil, fmt.Errorf("Error upgrading EphemeralState V1: %v", err) - } - return &EphemeralState{ - ConnInfo: connInfo.(map[string]string), - }, nil -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/state_upgrade_v2_to_v3.go b/vendor/github.com/hashicorp/terraform/terraform/state_upgrade_v2_to_v3.go deleted file mode 100644 index e52d35fc..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/state_upgrade_v2_to_v3.go +++ /dev/null @@ -1,142 +0,0 @@ -package terraform - -import ( - "fmt" - "log" - "regexp" - "sort" - "strconv" - "strings" -) - -// The upgrade process from V2 to V3 state does not affect the structure, -// so we do not need to redeclare all of the structs involved - we just -// take a deep copy of the old structure and assert the version number is -// as we expect. -func upgradeStateV2ToV3(old *State) (*State, error) { - new := old.DeepCopy() - - // Ensure the copied version is v2 before attempting to upgrade - if new.Version != 2 { - return nil, fmt.Errorf("Cannot apply v2->v3 state upgrade to " + - "a state which is not version 2.") - } - - // Set the new version number - new.Version = 3 - - // Change the counts for things which look like maps to use the % - // syntax. Remove counts for empty collections - they will be added - // back in later. - for _, module := range new.Modules { - for _, resource := range module.Resources { - // Upgrade Primary - if resource.Primary != nil { - upgradeAttributesV2ToV3(resource.Primary) - } - - // Upgrade Deposed - if resource.Deposed != nil { - for _, deposed := range resource.Deposed { - upgradeAttributesV2ToV3(deposed) - } - } - } - } - - return new, nil -} - -func upgradeAttributesV2ToV3(instanceState *InstanceState) error { - collectionKeyRegexp := regexp.MustCompile(`^(.*\.)#$`) - collectionSubkeyRegexp := regexp.MustCompile(`^([^\.]+)\..*`) - - // Identify the key prefix of anything which is a collection - var collectionKeyPrefixes []string - for key := range instanceState.Attributes { - if submatches := collectionKeyRegexp.FindAllStringSubmatch(key, -1); len(submatches) > 0 { - collectionKeyPrefixes = append(collectionKeyPrefixes, submatches[0][1]) - } - } - sort.Strings(collectionKeyPrefixes) - - log.Printf("[STATE UPGRADE] Detected the following collections in state: %v", collectionKeyPrefixes) - - // This could be rolled into fewer loops, but it is somewhat clearer this way, and will not - // run very often. - for _, prefix := range collectionKeyPrefixes { - // First get the actual keys that belong to this prefix - var potentialKeysMatching []string - for key := range instanceState.Attributes { - if strings.HasPrefix(key, prefix) { - potentialKeysMatching = append(potentialKeysMatching, strings.TrimPrefix(key, prefix)) - } - } - sort.Strings(potentialKeysMatching) - - var actualKeysMatching []string - for _, key := range potentialKeysMatching { - if submatches := collectionSubkeyRegexp.FindAllStringSubmatch(key, -1); len(submatches) > 0 { - actualKeysMatching = append(actualKeysMatching, submatches[0][1]) - } else { - if key != "#" { - actualKeysMatching = append(actualKeysMatching, key) - } - } - } - actualKeysMatching = uniqueSortedStrings(actualKeysMatching) - - // Now inspect the keys in order to determine whether this is most likely to be - // a map, list or set. There is room for error here, so we log in each case. If - // there is no method of telling, we remove the key from the InstanceState in - // order that it will be recreated. Again, this could be rolled into fewer loops - // but we prefer clarity. - - oldCountKey := fmt.Sprintf("%s#", prefix) - - // First, detect "obvious" maps - which have non-numeric keys (mostly). - hasNonNumericKeys := false - for _, key := range actualKeysMatching { - if _, err := strconv.Atoi(key); err != nil { - hasNonNumericKeys = true - } - } - if hasNonNumericKeys { - newCountKey := fmt.Sprintf("%s%%", prefix) - - instanceState.Attributes[newCountKey] = instanceState.Attributes[oldCountKey] - delete(instanceState.Attributes, oldCountKey) - log.Printf("[STATE UPGRADE] Detected %s as a map. Replaced count = %s", - strings.TrimSuffix(prefix, "."), instanceState.Attributes[newCountKey]) - } - - // Now detect empty collections and remove them from state. - if len(actualKeysMatching) == 0 { - delete(instanceState.Attributes, oldCountKey) - log.Printf("[STATE UPGRADE] Detected %s as an empty collection. Removed from state.", - strings.TrimSuffix(prefix, ".")) - } - } - - return nil -} - -// uniqueSortedStrings removes duplicates from a slice of strings and returns -// a sorted slice of the unique strings. -func uniqueSortedStrings(input []string) []string { - uniquemap := make(map[string]struct{}) - for _, str := range input { - uniquemap[str] = struct{}{} - } - - output := make([]string, len(uniquemap)) - - i := 0 - for key := range uniquemap { - output[i] = key - i = i + 1 - } - - sort.Strings(output) - return output -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/state_v1.go b/vendor/github.com/hashicorp/terraform/terraform/state_v1.go deleted file mode 100644 index 68cffb41..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/state_v1.go +++ /dev/null @@ -1,145 +0,0 @@ -package terraform - -// stateV1 keeps track of a snapshot state-of-the-world that Terraform -// can use to keep track of what real world resources it is actually -// managing. -// -// stateV1 is _only used for the purposes of backwards compatibility -// and is no longer used in Terraform. -// -// For the upgrade process, see state_upgrade_v1_to_v2.go -type stateV1 struct { - // Version is the protocol version. "1" for a StateV1. - Version int `json:"version"` - - // Serial is incremented on any operation that modifies - // the State file. It is used to detect potentially conflicting - // updates. - Serial int64 `json:"serial"` - - // Remote is used to track the metadata required to - // pull and push state files from a remote storage endpoint. - Remote *remoteStateV1 `json:"remote,omitempty"` - - // Modules contains all the modules in a breadth-first order - Modules []*moduleStateV1 `json:"modules"` -} - -type remoteStateV1 struct { - // Type controls the client we use for the remote state - Type string `json:"type"` - - // Config is used to store arbitrary configuration that - // is type specific - Config map[string]string `json:"config"` -} - -type moduleStateV1 struct { - // Path is the import path from the root module. Modules imports are - // always disjoint, so the path represents amodule tree - Path []string `json:"path"` - - // Outputs declared by the module and maintained for each module - // even though only the root module technically needs to be kept. - // This allows operators to inspect values at the boundaries. - Outputs map[string]string `json:"outputs"` - - // Resources is a mapping of the logically named resource to - // the state of the resource. Each resource may actually have - // N instances underneath, although a user only needs to think - // about the 1:1 case. - Resources map[string]*resourceStateV1 `json:"resources"` - - // Dependencies are a list of things that this module relies on - // existing to remain intact. For example: an module may depend - // on a VPC ID given by an aws_vpc resource. - // - // Terraform uses this information to build valid destruction - // orders and to warn the user if they're destroying a module that - // another resource depends on. - // - // Things can be put into this list that may not be managed by - // Terraform. If Terraform doesn't find a matching ID in the - // overall state, then it assumes it isn't managed and doesn't - // worry about it. - Dependencies []string `json:"depends_on,omitempty"` -} - -type resourceStateV1 struct { - // This is filled in and managed by Terraform, and is the resource - // type itself such as "mycloud_instance". If a resource provider sets - // this value, it won't be persisted. - Type string `json:"type"` - - // Dependencies are a list of things that this resource relies on - // existing to remain intact. For example: an AWS instance might - // depend on a subnet (which itself might depend on a VPC, and so - // on). - // - // Terraform uses this information to build valid destruction - // orders and to warn the user if they're destroying a resource that - // another resource depends on. - // - // Things can be put into this list that may not be managed by - // Terraform. If Terraform doesn't find a matching ID in the - // overall state, then it assumes it isn't managed and doesn't - // worry about it. - Dependencies []string `json:"depends_on,omitempty"` - - // Primary is the current active instance for this resource. - // It can be replaced but only after a successful creation. - // This is the instances on which providers will act. - Primary *instanceStateV1 `json:"primary"` - - // Tainted is used to track any underlying instances that - // have been created but are in a bad or unknown state and - // need to be cleaned up subsequently. In the - // standard case, there is only at most a single instance. - // However, in pathological cases, it is possible for the number - // of instances to accumulate. - Tainted []*instanceStateV1 `json:"tainted,omitempty"` - - // Deposed is used in the mechanics of CreateBeforeDestroy: the existing - // Primary is Deposed to get it out of the way for the replacement Primary to - // be created by Apply. If the replacement Primary creates successfully, the - // Deposed instance is cleaned up. If there were problems creating the - // replacement, the instance remains in the Deposed list so it can be - // destroyed in a future run. Functionally, Deposed instances are very - // similar to Tainted instances in that Terraform is only tracking them in - // order to remember to destroy them. - Deposed []*instanceStateV1 `json:"deposed,omitempty"` - - // Provider is used when a resource is connected to a provider with an alias. - // If this string is empty, the resource is connected to the default provider, - // e.g. "aws_instance" goes with the "aws" provider. - // If the resource block contained a "provider" key, that value will be set here. - Provider string `json:"provider,omitempty"` -} - -type instanceStateV1 struct { - // A unique ID for this resource. This is opaque to Terraform - // and is only meant as a lookup mechanism for the providers. - ID string `json:"id"` - - // Attributes are basic information about the resource. Any keys here - // are accessible in variable format within Terraform configurations: - // ${resourcetype.name.attribute}. - Attributes map[string]string `json:"attributes,omitempty"` - - // Ephemeral is used to store any state associated with this instance - // that is necessary for the Terraform run to complete, but is not - // persisted to a state file. - Ephemeral ephemeralStateV1 `json:"-"` - - // Meta is a simple K/V map that is persisted to the State but otherwise - // ignored by Terraform core. It's meant to be used for accounting by - // external client code. - Meta map[string]string `json:"meta,omitempty"` -} - -type ephemeralStateV1 struct { - // ConnInfo is used for the providers to export information which is - // used to connect to the resource for provisioning. For example, - // this could contain SSH or WinRM credentials. - ConnInfo map[string]string `json:"-"` -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/terraform_test.go b/vendor/github.com/hashicorp/terraform/terraform/terraform_test.go index 96fbcdff..ffca0ff7 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/terraform_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/terraform_test.go @@ -12,12 +12,10 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/convert" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs/configload" - "github.com/hashicorp/terraform/helper/experiment" "github.com/hashicorp/terraform/internal/initwd" "github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/providers" @@ -32,19 +30,8 @@ import ( const fixtureDir = "./testdata" func TestMain(m *testing.M) { - // We want to shadow on tests just to make sure the shadow graph works - // in case we need it and to find any race issues. - experiment.SetEnabled(experiment.X_shadow, true) - - experiment.Flag(flag.CommandLine) flag.Parse() - // Make sure shadow operations fail our real tests - contextFailOnShadowError = true - - // Always DeepCopy the Diff on every Plan during a test - contextTestDeepCopyOnPlan = true - // We have fmt.Stringer implementations on lots of objects that hide // details that we very often want to see in tests, so we just disable // spew's use of String methods globally on the assumption that spew @@ -55,37 +42,6 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func tempDir(t *testing.T) string { - t.Helper() - - dir, err := ioutil.TempDir("", "tf") - if err != nil { - t.Fatalf("err: %s", err) - } - if err := os.RemoveAll(dir); err != nil { - t.Fatalf("err: %s", err) - } - - return dir -} - -// tempEnv lets you temporarily set an environment variable. It returns -// a function to defer to reset the old value. -// the old value that should be set via a defer. -func tempEnv(t *testing.T, k string, v string) func() { - t.Helper() - - old, oldOk := os.LookupEnv(k) - os.Setenv(k, v) - return func() { - if !oldOk { - os.Unsetenv(k) - } else { - os.Setenv(k, old) - } - } -} - func testModule(t *testing.T, name string) *configs.Config { t.Helper() c, _ := testModuleWithSnapshot(t, name) @@ -208,28 +164,16 @@ func testSetResourceInstanceTainted(module *states.Module, resource, attrsJson, ) } -// testSetResourceInstanceDeposed is a helper function for tests that sets a -// Deposed resource instance for the given module. -func testSetResourceInstanceDeposed(module *states.Module, resource, attrsJson, provider string, key states.DeposedKey) { - module.SetResourceInstanceDeposed( - mustResourceInstanceAddr(resource).Resource, - key, - &states.ResourceInstanceObjectSrc{ - Status: states.ObjectTainted, - AttrsJSON: []byte(attrsJson), - }, - mustProviderConfig(provider), - ) -} - func testProviderFuncFixed(rp providers.Interface) providers.Factory { return func() (providers.Interface, error) { return rp, nil } } -func testProvisionerFuncFixed(rp provisioners.Interface) ProvisionerFactory { +func testProvisionerFuncFixed(rp *MockProvisioner) provisioners.Factory { return func() (provisioners.Interface, error) { + // make sure this provisioner has has not been closed + rp.CloseCalled = false return rp, nil } } @@ -266,22 +210,6 @@ func mustProviderConfig(s string) addrs.AbsProviderConfig { return p } -func instanceObjectIdForTests(obj *states.ResourceInstanceObject) string { - v := obj.Value - if v.IsNull() || !v.IsKnown() { - return "" - } - idVal := v.GetAttr("id") - if idVal.IsNull() || !idVal.IsKnown() { - return "" - } - idVal, err := convert.Convert(idVal, cty.String) - if err != nil { - return "" // placeholder value - } - return idVal.AsString() -} - // HookRecordApplyOrder is a test hook that records the order of applies // by recording the PreApply event. type HookRecordApplyOrder struct { @@ -320,59 +248,12 @@ func (h *HookRecordApplyOrder) PreApply(addr addrs.AbsResourceInstance, gen stat // Below are all the constant strings that are the expected output for // various tests. -const testTerraformInputProviderStr = ` -aws_instance.bar: - ID = foo - provider = provider["registry.terraform.io/hashicorp/aws"] - bar = override - foo = us-east-1 - type = aws_instance -aws_instance.foo: - ID = foo - provider = provider["registry.terraform.io/hashicorp/aws"] - bar = baz - num = 2 - type = aws_instance -` - const testTerraformInputProviderOnlyStr = ` aws_instance.foo: - ID = foo + ID = provider = provider["registry.terraform.io/hashicorp/aws"] foo = us-west-2 - type = aws_instance -` - -const testTerraformInputVarOnlyStr = ` -aws_instance.foo: - ID = foo - provider = provider["registry.terraform.io/hashicorp/aws"] - foo = us-east-1 - type = aws_instance -` - -const testTerraformInputVarOnlyUnsetStr = ` -aws_instance.foo: - ID = foo - provider = provider["registry.terraform.io/hashicorp/aws"] - bar = baz - foo = foovalue - type = aws_instance -` - -const testTerraformInputVarsStr = ` -aws_instance.bar: - ID = foo - provider = provider["registry.terraform.io/hashicorp/aws"] - bar = override - foo = us-east-1 - type = aws_instance -aws_instance.foo: - ID = foo - provider = provider["registry.terraform.io/hashicorp/aws"] - bar = baz - num = 2 - type = aws_instance + type = ` const testTerraformApplyStr = ` @@ -1171,199 +1052,6 @@ aws_instance.bar: type = aws_instance ` -const testTerraformPlanStr = ` -DIFF: - -CREATE: aws_instance.bar - foo: "" => "2" - type: "" => "aws_instance" -CREATE: aws_instance.foo - num: "" => "2" - type: "" => "aws_instance" - -STATE: - - -` - -const testTerraformPlanComputedIdStr = ` -DIFF: - -CREATE: aws_instance.bar - foo: "" => "" - type: "" => "aws_instance" -CREATE: aws_instance.foo - foo: "" => "" - num: "" => "2" - type: "" => "aws_instance" - -STATE: - - -` - -const testTerraformPlanCountIndexZeroStr = ` -DIFF: - -CREATE: aws_instance.foo - foo: "" => "0" - type: "" => "aws_instance" - -STATE: - - -` - -const testTerraformPlanEmptyStr = ` -DIFF: - -CREATE: aws_instance.bar -CREATE: aws_instance.foo - -STATE: - - -` - -const testTerraformPlanEscapedVarStr = ` -DIFF: - -CREATE: aws_instance.foo - foo: "" => "bar-${baz}" - type: "" => "aws_instance" - -STATE: - - -` - -const testTerraformPlanModulesStr = ` -DIFF: - -CREATE: aws_instance.bar - foo: "" => "2" - type: "" => "aws_instance" -CREATE: aws_instance.foo - num: "" => "2" - type: "" => "aws_instance" - -module.child: - CREATE: aws_instance.foo - num: "" => "2" - type: "" => "aws_instance" - -STATE: - - -` - -const testTerraformPlanModuleCycleStr = ` -DIFF: - -CREATE: aws_instance.b -CREATE: aws_instance.c - some_input: "" => "" - type: "" => "aws_instance" - -STATE: - - -` - -const testTerraformPlanModuleInputStr = ` -DIFF: - -CREATE: aws_instance.bar - foo: "" => "2" - type: "" => "aws_instance" - -module.child: - CREATE: aws_instance.foo - foo: "" => "42" - type: "" => "aws_instance" - -STATE: - - -` - -const testTerraformPlanModuleInputComputedStr = ` -DIFF: - -CREATE: aws_instance.bar - compute: "" => "foo" - compute_value: "" => "" - foo: "" => "" - type: "" => "aws_instance" - -module.child: - CREATE: aws_instance.foo - foo: "" => "" - type: "" => "aws_instance" - -STATE: - - -` - -const testTerraformPlanModuleVarIntStr = ` -DIFF: - -module.child: - CREATE: aws_instance.foo - num: "" => "2" - type: "" => "aws_instance" - -STATE: - - -` - -const testTerraformPlanMultipleTaintStr = ` -DIFF: - -DESTROY/CREATE: aws_instance.bar - foo: "" => "2" - type: "" => "aws_instance" - -STATE: - -aws_instance.bar: (2 tainted) - ID = - Tainted ID 1 = baz - Tainted ID 2 = zip -aws_instance.foo: - ID = bar - num = 2 -` - -const testTerraformPlanVarMultiCountOneStr = ` -DIFF: - -CREATE: aws_instance.bar - foo: "" => "2" - type: "" => "aws_instance" -CREATE: aws_instance.foo - num: "" => "2" - type: "" => "aws_instance" - -STATE: - - -` - -const testTerraformInputHCL = ` -hcl_instance.hcltest: - ID = foo - provider = provider["registry.terraform.io/hashicorp/hcl"] - bar.w = z - bar.x = y - foo.# = 2 - foo.0 = a - foo.1 = b - type = hcl_instance -` - const testTerraformRefreshDataRefDataStr = ` data.null_data_source.bar: ID = foo diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-data-sensitive/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-data-sensitive/main.tf new file mode 100644 index 00000000..c248a7c3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-data-sensitive/main.tf @@ -0,0 +1,8 @@ +variable "foo" { + sensitive = true + default = "foo" +} + +data "null_data_source" "testing" { + foo = var.foo +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-destroy-data-cycle/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-destroy-data-cycle/main.tf index bd72a47e..591af820 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-destroy-data-cycle/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-destroy-data-cycle/main.tf @@ -8,3 +8,7 @@ data "null_data_source" "d" { resource "null_resource" "a" { count = local.l == "NONE" ? 1 : 0 } + +provider "test" { + foo = data.null_data_source.d.id +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-destroy-data-resource/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-destroy-data-resource/main.tf index cb16d9f3..0d941a70 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-destroy-data-resource/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-destroy-data-resource/main.tf @@ -1,5 +1,3 @@ data "null_data_source" "testing" { - inputs = { - test = "yes" - } + foo = "yes" } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-destroy-mod-var-and-count-nested/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-destroy-mod-var-and-count-nested/main.tf index 676ddc79..58600cdb 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-destroy-mod-var-and-count-nested/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-destroy-mod-var-and-count-nested/main.tf @@ -1,9 +1,9 @@ variable "mod_count_root" { - type = "string" + type = string default = "3" } module "child" { - source = "./child" - mod_count_child = "${var.mod_count_root}" + source = "./child" + mod_count_child = var.mod_count_root } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-good-create-before-count/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-good-create-before-count/main.tf deleted file mode 100644 index 324ad528..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-good-create-before-count/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -resource "aws_instance" "bar" { - count = 2 - require_new = "xyz" - lifecycle { - create_before_destroy = true - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-ignore-changes-all/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-ignore-changes-all/main.tf new file mode 100644 index 00000000..a89889a0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-ignore-changes-all/main.tf @@ -0,0 +1,7 @@ +resource "aws_instance" "foo" { + required_field = "set" + + lifecycle { + ignore_changes = all + } +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-ignore-changes-wildcard/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-ignore-changes-wildcard/main.tf deleted file mode 100644 index a2bc76fd..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-ignore-changes-wildcard/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -resource "aws_instance" "foo" { - required_field = "set" - - lifecycle { - ignore_changes = ["*"] - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-map-var-through-module/amodule/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-map-var-through-module/amodule/main.tf index ce2cc8c7..a5284966 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-map-var-through-module/amodule/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-map-var-through-module/amodule/main.tf @@ -1,9 +1,9 @@ variable "amis" { - type = "map" + type = map(string) } resource "null_resource" "noop" {} output "amis_out" { - value = "${var.amis}" + value = var.amis } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-map-var-through-module/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-map-var-through-module/main.tf index 991a0ecf..4cec4a67 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-map-var-through-module/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-map-var-through-module/main.tf @@ -1,19 +1,19 @@ variable "amis_in" { - type = "map" - default = { - "us-west-1" = "ami-123456" - "us-west-2" = "ami-456789" - "eu-west-1" = "ami-789012" - "eu-west-2" = "ami-989484" - } + type = map(string) + default = { + "us-west-1" = "ami-123456" + "us-west-2" = "ami-456789" + "eu-west-1" = "ami-789012" + "eu-west-2" = "ami-989484" + } } module "test" { - source = "./amodule" + source = "./amodule" - amis = "${var.amis_in}" + amis = var.amis_in } output "amis_from_module" { - value = "${module.test.amis_out}" + value = module.test.amis_out } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-multi-var-comprehensive/child/child.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-multi-var-comprehensive/child/child.tf index 5d925d93..8fe7df7c 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-multi-var-comprehensive/child/child.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-multi-var-comprehensive/child/child.tf @@ -2,11 +2,11 @@ variable "num" { } variable "source_ids" { - type = "list" + type = list(string) } variable "source_names" { - type = "list" + type = list(string) } resource "test_thing" "multi_count_var" { diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-provisioner-conninfo/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-provisioner-conninfo/main.tf deleted file mode 100644 index 5166d22b..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-provisioner-conninfo/main.tf +++ /dev/null @@ -1,29 +0,0 @@ -variable "pass" { -} - -variable "value" { -} - -resource "aws_instance" "foo" { - num = "2" - compute = "value" - compute_value = "${var.value}" -} - -resource "aws_instance" "bar" { - connection { - host = "localhost" - type = "telnet" - } - - provisioner "shell" { - foo = "${aws_instance.foo.value}" - connection { - host = "localhost" - type = "telnet" - user = "superuser" - port = 2222 - password = "${var.pass}" - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-provisioner-each/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-provisioner-each/main.tf deleted file mode 100644 index 29be7206..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-provisioner-each/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -resource "aws_instance" "bar" { - for_each = toset(["a"]) - provisioner "shell" { - when = "destroy" - command = "echo ${each.key}" - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-resource-scale-in/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-resource-scale-in/main.tf index 0363d89b..8cb38473 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-resource-scale-in/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/apply-resource-scale-in/main.tf @@ -5,7 +5,7 @@ resource "aws_instance" "one" { } locals { - one_id = element(concat(aws_instance.one.*.id, list("")), 0) + one_id = element(concat(aws_instance.one.*.id, [""]), 0) } resource "aws_instance" "two" { diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/empty-with-child-module/child/child.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/empty-with-child-module/child/child.tf deleted file mode 100644 index 05e29577..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/empty-with-child-module/child/child.tf +++ /dev/null @@ -1,3 +0,0 @@ -module "grandchild" { - source = "../grandchild" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/empty-with-child-module/grandchild/grandchild.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/empty-with-child-module/grandchild/grandchild.tf deleted file mode 100644 index 4b41c9fc..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/empty-with-child-module/grandchild/grandchild.tf +++ /dev/null @@ -1 +0,0 @@ -# Nothing here! diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-basic/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-basic/main.tf deleted file mode 100644 index add0dd43..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-basic/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -provider "aws" {} -resource "aws_instance" "db" {} -resource "aws_instance" "web" { - foo = "${aws_instance.db.id}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-cbd-non-cbd/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-cbd-non-cbd/main.tf deleted file mode 100644 index f478d4f3..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-cbd-non-cbd/main.tf +++ /dev/null @@ -1,9 +0,0 @@ -provider "aws" {} - -resource "aws_lc" "foo" {} - -resource "aws_asg" "foo" { - lc = "${aws_lc.foo.id}" - - lifecycle { create_before_destroy = true } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-modules/consul/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-modules/consul/main.tf deleted file mode 100644 index 8b0469b8..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-modules/consul/main.tf +++ /dev/null @@ -1,2 +0,0 @@ -provider "aws" {} -resource "aws_instance" "server" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-modules/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-modules/main.tf deleted file mode 100644 index 8e8b532d..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-modules/main.tf +++ /dev/null @@ -1,16 +0,0 @@ -module "consul" { - foo = "${aws_security_group.firewall.foo}" - source = "./consul" -} - -provider "aws" {} - -resource "aws_security_group" "firewall" {} - -resource "aws_instance" "web" { - security_groups = [ - "foo", - "${aws_security_group.firewall.foo}", - "${module.consul.security_group}" - ] -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-multi-level-module/foo/bar/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-multi-level-module/foo/bar/main.tf deleted file mode 100644 index 6ee0b288..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-multi-level-module/foo/bar/main.tf +++ /dev/null @@ -1,2 +0,0 @@ -variable "bar" {} -output "value" { value = "${var.bar}" } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-multi-level-module/foo/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-multi-level-module/foo/main.tf deleted file mode 100644 index dbe120fb..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-multi-level-module/foo/main.tf +++ /dev/null @@ -1,6 +0,0 @@ -module "bar" { - source = "./bar" - bar = "${var.foo}" -} - -variable "foo" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-multi-level-module/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-multi-level-module/main.tf deleted file mode 100644 index 3962c1d1..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-multi-level-module/main.tf +++ /dev/null @@ -1,4 +0,0 @@ -module "foo" { - source = "./foo" - foo = "bar" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-orphan-deps/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-orphan-deps/main.tf deleted file mode 100644 index b21d3b6a..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-builder-orphan-deps/main.tf +++ /dev/null @@ -1 +0,0 @@ -provider "aws" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-count-var-resource/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-count-var-resource/main.tf deleted file mode 100644 index 9c7407fa..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-count-var-resource/main.tf +++ /dev/null @@ -1,9 +0,0 @@ -resource "aws_instance" "foo" {} - -resource "aws_instance" "web" { - count = "${aws_instance.foo.bar}" -} - -resource "aws_load_balancer" "weblb" { - members = "${aws_instance.web.*.id}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-count/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-count/main.tf deleted file mode 100644 index c6fdf97e..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-count/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -resource "aws_instance" "web" { - count = 3 -} - -resource "aws_load_balancer" "weblb" { - members = "${aws_instance.web.*.id}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-cycle/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-cycle/main.tf deleted file mode 100644 index 1f7a3a76..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-cycle/main.tf +++ /dev/null @@ -1,18 +0,0 @@ -variable "foo" { - default = "bar" - description = "bar" -} - -provider "aws" { - foo = "${aws_security_group.firewall.value}" -} - -resource "aws_security_group" "firewall" {} - -resource "aws_instance" "web" { - ami = "${var.foo}" - security_groups = [ - "foo", - "${aws_security_group.firewall.foo}" - ] -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-depends-on-count/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-depends-on-count/main.tf deleted file mode 100644 index 7e005f17..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-depends-on-count/main.tf +++ /dev/null @@ -1,6 +0,0 @@ -resource "aws_instance" "web" {} - -resource "aws_instance" "db" { - depends_on = ["aws_instance.web"] - count = 2 -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-depends-on/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-depends-on/main.tf deleted file mode 100644 index 5a543091..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-depends-on/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -resource "aws_instance" "web" {} - -resource "aws_instance" "db" { - depends_on = ["aws_instance.web"] -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-create-before/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-create-before/main.tf deleted file mode 100644 index 2cfe794d..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-create-before/main.tf +++ /dev/null @@ -1,8 +0,0 @@ -provider "aws" {} - -resource "aws_instance" "bar" { - ami = "abc" - lifecycle { - create_before_destroy = true - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-destroy/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-destroy/main.tf deleted file mode 100644 index 1df2421f..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-destroy/main.tf +++ /dev/null @@ -1,8 +0,0 @@ -provider "aws" {} - -resource "aws_instance" "foo" { -} - -resource "aws_instance" "bar" { - foo = "${aws_instance.foo.id}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module-dep-module/bar/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module-dep-module/bar/main.tf deleted file mode 100644 index 6f71b621..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module-dep-module/bar/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -variable "in" {} - -aws_instance "foo" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module-dep-module/foo/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module-dep-module/foo/main.tf deleted file mode 100644 index 2bf29d59..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module-dep-module/foo/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -output "data" { - value = "foo" -} - -aws_instance "foo" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module-dep-module/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module-dep-module/main.tf deleted file mode 100644 index 656503f2..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module-dep-module/main.tf +++ /dev/null @@ -1,8 +0,0 @@ -module "foo" { - source = "./foo" -} - -module "bar" { - source = "./bar" - in = "${module.foo.data}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module-dep/child/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module-dep/child/main.tf deleted file mode 100644 index 84d1de90..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module-dep/child/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -resource "aws_instance" "foo" {} - -output "bar" { - value = "baz" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module-dep/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module-dep/main.tf deleted file mode 100644 index 2f61386b..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module-dep/main.tf +++ /dev/null @@ -1,8 +0,0 @@ -resource "aws_instance" "foo" {} - -module "child" { - source = "./child" - in = "${aws_instance.foo.id}" -} - - diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module/child/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module/child/main.tf deleted file mode 100644 index 84d1de90..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module/child/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -resource "aws_instance" "foo" {} - -output "bar" { - value = "baz" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module/main.tf deleted file mode 100644 index 2b823f11..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff-module/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -module "child" { - source = "./child" -} - -resource "aws_instance" "foo" { - value = "${module.child.bar}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff/main.tf deleted file mode 100644 index b626e60c..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-diff/main.tf +++ /dev/null @@ -1,2 +0,0 @@ -resource "aws_instance" "foo" { -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-missing-deps/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-missing-deps/main.tf deleted file mode 100644 index 44297f31..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-missing-deps/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -resource "aws_instance" "db" {} - -resource "aws_instance" "web" { - foo = "${aws_instance.lb.id}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-module-orphan/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-module-orphan/main.tf deleted file mode 100644 index 307463d3..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-module-orphan/main.tf +++ /dev/null @@ -1,10 +0,0 @@ -provider "aws" {} - -resource "aws_security_group" "firewall" {} - -resource "aws_instance" "web" { - security_groups = [ - "foo", - "${aws_security_group.firewall.foo}" - ] -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-modules/consul/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-modules/consul/main.tf deleted file mode 100644 index 9e22d04d..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-modules/consul/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -resource "aws_instance" "server" {} - -output "security_group" { value = "" } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-modules/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-modules/main.tf deleted file mode 100644 index 8e8b532d..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-modules/main.tf +++ /dev/null @@ -1,16 +0,0 @@ -module "consul" { - foo = "${aws_security_group.firewall.foo}" - source = "./consul" -} - -provider "aws" {} - -resource "aws_security_group" "firewall" {} - -resource "aws_instance" "web" { - security_groups = [ - "foo", - "${aws_security_group.firewall.foo}", - "${module.consul.security_group}" - ] -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-node-module-expand/child/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-node-module-expand/child/main.tf deleted file mode 100644 index f14f189b..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-node-module-expand/child/main.tf +++ /dev/null @@ -1,4 +0,0 @@ -resource "aws_instance" "foo" {} -resource "aws_instance" "bar" { - var = "${aws_instance.foo.whatever}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-node-module-expand/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-node-module-expand/main.tf deleted file mode 100644 index 0f6991c5..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-node-module-expand/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -module "child" { - source = "./child" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-node-module-flatten/child/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-node-module-flatten/child/main.tf deleted file mode 100644 index 919f140b..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-node-module-flatten/child/main.tf +++ /dev/null @@ -1 +0,0 @@ -resource "aws_instance" "foo" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-node-module-flatten/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-node-module-flatten/main.tf deleted file mode 100644 index 0f6991c5..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-node-module-flatten/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -module "child" { - source = "./child" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-outputs/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-outputs/main.tf deleted file mode 100644 index 92c4bf22..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-outputs/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -resource "aws_instance" "foo" {} - -output "foo" { - value = "${aws_instance.foo.value}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-provider-alias/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-provider-alias/main.tf deleted file mode 100644 index f7c319fc..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-provider-alias/main.tf +++ /dev/null @@ -1,10 +0,0 @@ -provider "aws" { -} - -provider "aws" { - alias = "foo" -} - -provider "aws" { - alias = "bar" -} \ No newline at end of file diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-provider-prune/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-provider-prune/main.tf deleted file mode 100644 index ac2f526f..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-provider-prune/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -provider "aws" {} -provider "digitalocean" {} -provider "openstack" {} - -resource "aws_load_balancer" "weblb" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-provisioners/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-provisioners/main.tf deleted file mode 100644 index 6e1e93aa..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-provisioners/main.tf +++ /dev/null @@ -1,33 +0,0 @@ -variable "foo" { - default = "bar" - description = "bar" -} - -provider "aws" {} - -resource "aws_security_group" "firewall" {} - -resource "aws_instance" "web" { - ami = "${var.foo}" - security_groups = [ - "foo", - "${aws_security_group.firewall.foo}" - ] - provisioner "winrm" { - cmd = "echo foo" - } - provisioner "winrm" { - cmd = "echo bar" - } -} - -resource "aws_load_balancer" "weblb" { - provisioner "shell" { - cmd = "add ${aws_instance.web.id}" - connection { - host = "localhost" - type = "magic" - user = "${aws_security_group.firewall.id}" - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-resource-expand-prov-deps/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-resource-expand-prov-deps/main.tf deleted file mode 100644 index a8c8efd8..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-resource-expand-prov-deps/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -resource "aws_instance" "web" { - count = 3 - - provisioner "remote-exec" { - inline = ["echo ${aws_instance.web.0.foo}"] - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-resource-expand/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-resource-expand/main.tf deleted file mode 100644 index b00b04ef..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-resource-expand/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -resource "aws_instance" "web" { - count = 3 -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-tainted/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-tainted/main.tf deleted file mode 100644 index da7eb0a7..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/graph-tainted/main.tf +++ /dev/null @@ -1,18 +0,0 @@ -variable "foo" { - default = "bar" - description = "bar" -} - -provider "aws" { - foo = "${openstack_floating_ip.random.value}" -} - -resource "aws_security_group" "firewall" {} - -resource "aws_instance" "web" { - ami = "${var.foo}" - security_groups = [ - "foo", - "${aws_security_group.firewall.foo}" - ] -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/import-provider-alias/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/import-provider-alias/main.tf deleted file mode 100644 index d145d088..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/import-provider-alias/main.tf +++ /dev/null @@ -1,4 +0,0 @@ -provider "aws" { - foo = "bar" - alias = "alias" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/input-bad-var-default/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/input-bad-var-default/main.tf deleted file mode 100644 index bc34bb8e..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/input-bad-var-default/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -variable "test" { - default = { - l = [1, 2, 3] - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/input-hcl/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/input-hcl/main.tf deleted file mode 100644 index ca46ee8e..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/input-hcl/main.tf +++ /dev/null @@ -1,12 +0,0 @@ -variable "mapped" { - type = "map" -} - -variable "listed" { - type = "list" -} - -resource "hcl_instance" "hcltest" { - foo = "${var.listed}" - bar = "${var.mapped}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/input-module-computed-output-element/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/input-module-computed-output-element/main.tf deleted file mode 100644 index bb96e24a..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/input-module-computed-output-element/main.tf +++ /dev/null @@ -1,9 +0,0 @@ -module "b" { - source = "./modb" -} - -module "a" { - source = "./moda" - - single_element = "${element(module.b.computed_list, 0)}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/input-module-computed-output-element/moda/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/input-module-computed-output-element/moda/main.tf deleted file mode 100644 index eb09eb19..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/input-module-computed-output-element/moda/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -variable "single_element" { - type = "string" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/input-module-computed-output-element/modb/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/input-module-computed-output-element/modb/main.tf deleted file mode 100644 index ebe4a7ef..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/input-module-computed-output-element/modb/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -resource "aws_instance" "test" { - count = 3 -} - -output "computed_list" { - value = ["${aws_instance.test.*.id}"] -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/input-submodule-count/mod/submod/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/input-submodule-count/mod/submod/main.tf index c0c8d15a..732ce43b 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/input-submodule-count/mod/submod/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/input-submodule-count/mod/submod/main.tf @@ -1,7 +1,7 @@ variable "list" { - type = "list" + type = list(string) } resource "aws_instance" "bar" { - count = "${var.list[0]}" + count = var.list[0] } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/input-variables-invalid/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/input-variables-invalid/main.tf deleted file mode 100644 index 9d6d49aa..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/input-variables-invalid/main.tf +++ /dev/null @@ -1,30 +0,0 @@ -# Required -variable "foo" { -} - -# Optional -variable "bar" { - default = "baz" -} - -# Mapping -variable "map" { - default = { - foo = "bar" - } -} - -# Complex Object Types -variable "object_map" { - type = map(object({ - foo = string, - bar = any - })) -} - -variable "object_list" { - type = list(object({ - foo = string, - bar = any - })) -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/input-variables-invalid/terraform.tfvars b/vendor/github.com/hashicorp/terraform/terraform/testdata/input-variables-invalid/terraform.tfvars deleted file mode 100644 index 4ebc8391..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/input-variables-invalid/terraform.tfvars +++ /dev/null @@ -1,13 +0,0 @@ -test = [ - { - foo = "blah1" - bar = {} - }, - { - foo = "blah2" - bar = { - var1 = "val1", - var2 = "var2" - } - } -] diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-local/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-local/main.tf deleted file mode 100644 index 699667a1..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-local/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -locals { - foo = "..." -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-multi-interp/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-multi-interp/main.tf deleted file mode 100644 index 1d475e2e..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-multi-interp/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -resource "aws_instance" "web" { - count = "${var.c}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-multi-vars/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-multi-vars/main.tf deleted file mode 100644 index b24d02f9..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-multi-vars/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -resource "aws_route53_zone" "yada" { - -} - -resource "aws_route53_zone" "terra" { - count = 2 -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-path-module/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-path-module/main.tf deleted file mode 100644 index 0f6991c5..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-path-module/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -module "child" { - source = "./child" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-resource-variable-multi/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-resource-variable-multi/main.tf deleted file mode 100644 index b00b04ef..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-resource-variable-multi/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -resource "aws_instance" "web" { - count = 3 -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-resource-variable/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-resource-variable/main.tf deleted file mode 100644 index 64cbf623..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/interpolate-resource-variable/main.tf +++ /dev/null @@ -1 +0,0 @@ -resource "aws_instance" "web" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/issue-7824/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/issue-7824/main.tf index 835254b6..ec76bc39 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/issue-7824/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/issue-7824/main.tf @@ -1,5 +1,5 @@ variable "test" { - type = "map" + type = map(string) default = { "test" = "1" } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-explicit-provider-resource/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-explicit-provider-resource/main.tf deleted file mode 100644 index d5990b09..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-explicit-provider-resource/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -provider "foo" { - version = ">=1.0.0" -} - -resource "foo_bar" "test1" { - -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-explicit-provider-unconstrained/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-explicit-provider-unconstrained/main.tf deleted file mode 100644 index 6144ff53..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-explicit-provider-unconstrained/main.tf +++ /dev/null @@ -1,2 +0,0 @@ -provider "foo" { -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-explicit-provider/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-explicit-provider/main.tf deleted file mode 100644 index 27d42375..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-explicit-provider/main.tf +++ /dev/null @@ -1,8 +0,0 @@ -provider "foo" { - version = ">=1.0.0" -} - -provider "foo" { - version = ">=2.0.0" - alias = "bar" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-implicit-provider/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-implicit-provider/main.tf deleted file mode 100644 index 15aa2cb7..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-implicit-provider/main.tf +++ /dev/null @@ -1,8 +0,0 @@ - -resource "foo_bar" "test1" { - -} - -resource "foo_bar" "test2" { - provider = "foo.baz" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-inherit-provider/child/child.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-inherit-provider/child/child.tf deleted file mode 100644 index 51e0950a..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-inherit-provider/child/child.tf +++ /dev/null @@ -1,17 +0,0 @@ - -# "foo" is inherited from the parent module -resource "foo_bar" "test" { - -} - -# but we don't use the "bar" provider inherited from the parent - -# "baz" is introduced here for the first time, so it's an implicit -# dependency -resource "baz_bar" "test" { - -} - -module "grandchild" { - source = "../grandchild" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-inherit-provider/grandchild/grandchild.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-inherit-provider/grandchild/grandchild.tf deleted file mode 100644 index c5a07249..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-inherit-provider/grandchild/grandchild.tf +++ /dev/null @@ -1,11 +0,0 @@ - -# Here we *override* the foo from the parent -provider "foo" { - -} - -# We also use the "bar" provider defined at the root, which was -# completely ignored by the child module in between. -resource "bar_thing" "test" { - -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-inherit-provider/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-inherit-provider/main.tf deleted file mode 100644 index 9842855b..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-inherit-provider/main.tf +++ /dev/null @@ -1,11 +0,0 @@ - -provider "foo" { -} - -provider "bar" { - -} - -module "child" { - source = "./child" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-required-providers/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-required-providers/main.tf deleted file mode 100644 index e39cc897..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/module-deps-required-providers/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -terraform { - required_providers { - foo = { - version = ">=1.0.0" - } - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/new-good/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/new-good/main.tf deleted file mode 100644 index 40ed1a89..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/new-good/main.tf +++ /dev/null @@ -1,6 +0,0 @@ -provider "aws" { - foo = "bar" -} - -resource "aws_instance" "foo" {} -resource "do_droplet" "bar" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/new-graph-cycle/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/new-graph-cycle/main.tf deleted file mode 100644 index b4285424..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/new-graph-cycle/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -resource "aws_instance" "foo" { - ami = "${aws_instance.bar.id}" -} - -resource "aws_instance" "bar" { - ami = "${aws_instance.foo.id}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/new-pc-cache/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/new-pc-cache/main.tf deleted file mode 100644 index 617fd43b..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/new-pc-cache/main.tf +++ /dev/null @@ -1,12 +0,0 @@ -provider "aws" { - foo = "bar" -} - -provider "aws_elb" { - foo = "baz" -} - -resource "aws_instance" "foo" {} -resource "aws_instance" "bar" {} -resource "aws_elb" "lb" {} -resource "do_droplet" "bar" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/new-provider-validate/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/new-provider-validate/main.tf deleted file mode 100644 index 9ba300ba..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/new-provider-validate/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -provider "aws" { - foo = "bar" -} - -resource "aws_instance" "foo" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/new-variables/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/new-variables/main.tf deleted file mode 100644 index 8a42d5cd..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/new-variables/main.tf +++ /dev/null @@ -1,4 +0,0 @@ -variable "foo" {} -variable "bar" { - default = "baz" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-data-source-type-mismatch/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-data-source-type-mismatch/main.tf deleted file mode 100644 index d0782f26..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-data-source-type-mismatch/main.tf +++ /dev/null @@ -1,4 +0,0 @@ -data "aws_availability_zones" "azs" {} -resource "aws_instance" "foo" { - ami = "${data.aws_availability_zones.azs.names}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-for-each/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-for-each/main.tf index bffb079c..94572e20 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-for-each/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-for-each/main.tf @@ -13,7 +13,7 @@ resource "aws_instance" "bar" { for_each = toset([]) } resource "aws_instance" "bar2" { - for_each = toset(list("z", "y", "x")) + for_each = toset(["z", "y", "x"]) } # an empty map should generate no resource diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-destroy-gh-1835/b/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-destroy-gh-1835/b/main.tf index c3b0270b..3b0cc666 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-destroy-gh-1835/b/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-destroy-gh-1835/b/main.tf @@ -1,5 +1,5 @@ variable "a_id" {} resource "aws_instance" "b" { - command = "echo ${var.a_id}" + foo = "echo ${var.a_id}" } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-var-with-default-value/inner/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-var-with-default-value/inner/main.tf index 8a089655..5b5cf6cd 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-var-with-default-value/inner/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-var-with-default-value/inner/main.tf @@ -1,12 +1,12 @@ variable "im_a_string" { - type = "string" + type = string } variable "service_region_ami" { - type = "map" - default = { - us-east-1 = "ami-e4c9db8e" - } + type = map(string) + default = { + us-east-1 = "ami-e4c9db8e" + } } resource "null_resource" "noop" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-variable-from-splat/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-variable-from-splat/main.tf index 7212325e..be900a3c 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-variable-from-splat/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-variable-from-splat/main.tf @@ -5,5 +5,5 @@ module "mod1" { module "mod2" { source = "./mod" - param = ["${module.mod1.out_from_splat[0]}"] + param = [module.mod1.out_from_splat[0]] } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-variable-from-splat/mod/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-variable-from-splat/mod/main.tf index 28d9175d..66127d36 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-variable-from-splat/mod/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-variable-from-splat/mod/main.tf @@ -1,12 +1,12 @@ variable "param" { - type = "list" + type = list(string) } resource "aws_instance" "test" { - count = "2" + count = "2" thing = "doesnt" } output "out_from_splat" { - value = ["${aws_instance.test.*.thing}"] + value = aws_instance.test.*.thing } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type-nested/inner/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type-nested/inner/main.tf index 88995119..dabe507f 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type-nested/inner/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type-nested/inner/main.tf @@ -1,13 +1,13 @@ variable "inner_in" { - type = "map" - default = { - us-west-1 = "ami-12345" - us-west-2 = "ami-67890" - } + type = map(string) + default = { + us-west-1 = "ami-12345" + us-west-2 = "ami-67890" + } } resource "null_resource" "inner_noop" {} output "inner_out" { - value = "${lookup(var.inner_in, "us-west-1")}" + value = lookup(var.inner_in, "us-west-1") } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type-nested/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type-nested/main.tf index fe63fc5f..8f9fdcc5 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type-nested/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type-nested/main.tf @@ -1,3 +1,3 @@ module "middle" { - source = "./middle" + source = "./middle" } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type-nested/middle/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type-nested/middle/main.tf index 1e823576..eb989fe9 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type-nested/middle/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type-nested/middle/main.tf @@ -1,19 +1,19 @@ variable "middle_in" { - type = "map" - default = { - eu-west-1 = "ami-12345" - eu-west-2 = "ami-67890" - } + type = map(string) + default = { + eu-west-1 = "ami-12345" + eu-west-2 = "ami-67890" + } } module "inner" { - source = "../inner" + source = "../inner" - inner_in = "hello" + inner_in = "hello" } resource "null_resource" "middle_noop" {} output "middle_out" { - value = "${lookup(var.middle_in, "us-west-1")}" + value = lookup(var.middle_in, "us-west-1") } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type/inner/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type/inner/main.tf index c36aef10..7782d1b8 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type/inner/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type/inner/main.tf @@ -1,5 +1,5 @@ variable "map_in" { - type = "map" + type = map(string) default = { us-west-1 = "ami-12345" @@ -9,5 +9,5 @@ variable "map_in" { // We have to reference it so it isn't pruned output "output" { - value = "${var.map_in}" + value = var.map_in } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type/main.tf index 4fc7f8a7..5a39cd5d 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-module-wrong-var-type/main.tf @@ -1,10 +1,10 @@ variable "input" { - type = "string" - default = "hello world" + type = string + default = "hello world" } module "test" { - source = "./inner" + source = "./inner" - map_in = "${var.input}" -} + map_in = var.input +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-provider-init/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-provider-init/main.tf deleted file mode 100644 index ca800ad7..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-provider-init/main.tf +++ /dev/null @@ -1,9 +0,0 @@ -provider "do" { - foo = "${aws_instance.foo.num}" -} - -resource "aws_instance" "foo" { - num = "2" -} - -resource "do_droplet" "bar" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-requires-replace/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-requires-replace/main.tf new file mode 100644 index 00000000..23cee56b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-requires-replace/main.tf @@ -0,0 +1,3 @@ +resource "test_thing" "foo" { + v = "goodbye" +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-targeted-with-tainted/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-targeted-with-tainted/main.tf deleted file mode 100644 index f17e0809..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-targeted-with-tainted/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -resource "aws_instance" "ifailedprovisioners" { -} - -resource "aws_instance" "iambeingadded" { -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-variable-sensitivity-module/child/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-variable-sensitivity-module/child/main.tf index 1e1a07b1..e34751aa 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-variable-sensitivity-module/child/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-variable-sensitivity-module/child/main.tf @@ -2,6 +2,12 @@ variable "foo" { type = string } +// "bar" is defined as sensitive by both the parent and the child +variable "bar" { + sensitive = true +} + resource "aws_instance" "foo" { - foo = var.foo + foo = var.foo + value = var.bar } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-variable-sensitivity-module/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-variable-sensitivity-module/main.tf index 28ac1dfb..69bdbb4c 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-variable-sensitivity-module/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/plan-variable-sensitivity-module/main.tf @@ -3,7 +3,12 @@ variable "sensitive_var" { sensitive = true } +variable "another_var" { + sensitive = true +} + module "child" { source = "./child" foo = var.sensitive_var + bar = var.another_var } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/refresh-config-orphan/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/refresh-config-orphan/main.tf deleted file mode 100644 index c1c8b23d..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/refresh-config-orphan/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -resource "test_object" "foo" { - count = 3 -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/refresh-data-scale-inout/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/refresh-data-scale-inout/main.tf deleted file mode 100644 index 480ba948..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/refresh-data-scale-inout/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -data "aws_instance" "foo" { - count = 3 -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/refresh-resource-scale-inout/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/refresh-resource-scale-inout/main.tf deleted file mode 100644 index acef373b..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/refresh-resource-scale-inout/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -resource "aws_instance" "foo" { - count = 3 -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/state-module-orphans/bar/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/state-module-orphans/bar/main.tf deleted file mode 100644 index c01ade29..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/state-module-orphans/bar/main.tf +++ /dev/null @@ -1 +0,0 @@ -# Nothing diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/state-module-orphans/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/state-module-orphans/main.tf deleted file mode 100644 index f009f192..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/state-module-orphans/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -module "bar" { - source = "./bar" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/state-upgrade/v1-to-v2-empty-path.tfstate b/vendor/github.com/hashicorp/terraform/terraform/testdata/state-upgrade/v1-to-v2-empty-path.tfstate deleted file mode 100644 index ee7c9d18..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/state-upgrade/v1-to-v2-empty-path.tfstate +++ /dev/null @@ -1,38 +0,0 @@ -{ - "version": 1, - "modules": [{ - "resources": { - "aws_instance.foo1": {"primary":{}}, - "cloudstack_instance.foo1": {"primary":{}}, - "cloudstack_instance.foo2": {"primary":{}}, - "digitalocean_droplet.foo1": {"primary":{}}, - "digitalocean_droplet.foo2": {"primary":{}}, - "digitalocean_droplet.foo3": {"primary":{}}, - "docker_container.foo1": {"primary":{}}, - "docker_container.foo2": {"primary":{}}, - "docker_container.foo3": {"primary":{}}, - "docker_container.foo4": {"primary":{}}, - "google_compute_instance.foo1": {"primary":{}}, - "google_compute_instance.foo2": {"primary":{}}, - "google_compute_instance.foo3": {"primary":{}}, - "google_compute_instance.foo4": {"primary":{}}, - "google_compute_instance.foo5": {"primary":{}}, - "heroku_app.foo1": {"primary":{}}, - "heroku_app.foo2": {"primary":{}}, - "heroku_app.foo3": {"primary":{}}, - "heroku_app.foo4": {"primary":{}}, - "heroku_app.foo5": {"primary":{}}, - "heroku_app.foo6": {"primary":{}}, - "openstack_compute_instance_v2.foo1": {"primary":{}}, - "openstack_compute_instance_v2.foo2": {"primary":{}}, - "openstack_compute_instance_v2.foo3": {"primary":{}}, - "openstack_compute_instance_v2.foo4": {"primary":{}}, - "openstack_compute_instance_v2.foo5": {"primary":{}}, - "openstack_compute_instance_v2.foo6": {"primary":{}}, - "openstack_compute_instance_v2.foo7": {"primary":{}}, - "bar": {"primary":{}}, - "baz": {"primary":{}}, - "zip": {"primary":{}} - } - }] -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/static-validate-refs/static-validate-refs.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/static-validate-refs/static-validate-refs.tf index 9d945279..e9f9344a 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/static-validate-refs/static-validate-refs.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/static-validate-refs/static-validate-refs.tf @@ -1,6 +1,20 @@ +terraform { + required_providers { + boop = { + source = "foobar/beep" # intentional mismatch between local name and type + } + } +} + resource "aws_instance" "no_count" { } resource "aws_instance" "count" { count = 1 } + +resource "boop_instance" "yep" { +} + +resource "boop_whatever" "nope" { +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-create-before-destroy-basic/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-create-before-destroy-basic/main.tf deleted file mode 100644 index 478c911c..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-create-before-destroy-basic/main.tf +++ /dev/null @@ -1,9 +0,0 @@ -resource "aws_instance" "web" { - lifecycle { - create_before_destroy = true - } -} - -resource "aws_load_balancer" "lb" { - member = "${aws_instance.web.id}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-create-before-destroy-twice/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-create-before-destroy-twice/main.tf deleted file mode 100644 index c84a7a67..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-create-before-destroy-twice/main.tf +++ /dev/null @@ -1,9 +0,0 @@ -resource "aws_lc" "foo" { - lifecycle { create_before_destroy = true } -} - -resource "aws_autoscale" "bar" { - lc = "${aws_lc.foo.id}" - - lifecycle { create_before_destroy = true } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-destroy-basic/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-destroy-basic/main.tf deleted file mode 100644 index 14bca3e8..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-destroy-basic/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -resource "aws_instance" "foo" {} - -resource "aws_instance" "bar" { - value = "${aws_instance.foo.value}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-destroy-depends-on/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-destroy-depends-on/main.tf deleted file mode 100644 index bb81ab86..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-destroy-depends-on/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -resource "aws_instance" "foo" {} - -resource "aws_instance" "bar" { - depends_on = ["aws_instance.foo"] -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-destroy-deps/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-destroy-deps/main.tf deleted file mode 100644 index 1419d893..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-destroy-deps/main.tf +++ /dev/null @@ -1,5 +0,0 @@ -resource "aws_lc" "foo" {} - -resource "aws_asg" "bar" { - lc = "${aws_lc.foo.id}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-destroy-edge-splat/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-destroy-edge-splat/main.tf deleted file mode 100644 index 88d8b840..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-destroy-edge-splat/main.tf +++ /dev/null @@ -1,6 +0,0 @@ -resource "test_object" "A" {} - -resource "test_object" "B" { - count = 2 - test_string = "${test_object.A.*.test_string}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-destroy-prefix/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-destroy-prefix/main.tf deleted file mode 100644 index dd85754d..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-destroy-prefix/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -resource "aws_instance" "foo" {} - -resource "aws_instance" "foo-bar" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-destroy-prune-count/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-destroy-prune-count/main.tf deleted file mode 100644 index 756ae10d..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-destroy-prune-count/main.tf +++ /dev/null @@ -1,6 +0,0 @@ -resource "aws_instance" "foo" {} - -resource "aws_instance" "bar" { - value = "${aws_instance.foo.value}" - count = "5" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-diff-basic/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-diff-basic/main.tf deleted file mode 100644 index 919f140b..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-diff-basic/main.tf +++ /dev/null @@ -1 +0,0 @@ -resource "aws_instance" "foo" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-flat-config-basic/child/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-flat-config-basic/child/main.tf deleted file mode 100644 index 0c70b1b5..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-flat-config-basic/child/main.tf +++ /dev/null @@ -1 +0,0 @@ -resource "aws_instance" "baz" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-flat-config-basic/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-flat-config-basic/main.tf deleted file mode 100644 index ffe0916f..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-flat-config-basic/main.tf +++ /dev/null @@ -1,9 +0,0 @@ -resource "aws_instance" "foo" {} - -resource "aws_instance" "bar" { - value = "${aws_instance.foo.value}" -} - -module "child" { - source = "./child" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-flatten/child/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-flatten/child/main.tf deleted file mode 100644 index 7371f826..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-flatten/child/main.tf +++ /dev/null @@ -1,9 +0,0 @@ -variable "var" {} - -resource "aws_instance" "child" { - value = "${var.var}" -} - -output "output" { - value = "${aws_instance.child.value}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-flatten/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-flatten/main.tf deleted file mode 100644 index 179e151a..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-flatten/main.tf +++ /dev/null @@ -1,12 +0,0 @@ -module "child" { - source = "./child" - var = "${aws_instance.parent.value}" -} - -resource "aws_instance" "parent" { - value = "foo" -} - -resource "aws_instance" "parent-output" { - value = "${module.child.output}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-orphan-output-basic/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-orphan-output-basic/main.tf deleted file mode 100644 index 70619c4e..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-orphan-output-basic/main.tf +++ /dev/null @@ -1 +0,0 @@ -output "foo" { value = "bar" } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-disable-keep/child/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-disable-keep/child/main.tf deleted file mode 100644 index 9d02c162..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-disable-keep/child/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -variable "value" {} - -provider "aws" { - value = "${var.value}" -} - -resource "aws_instance" "foo" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-disable-keep/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-disable-keep/main.tf deleted file mode 100644 index 7f9aa3f9..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-disable-keep/main.tf +++ /dev/null @@ -1,9 +0,0 @@ -variable "foo" {} - -module "child" { - source = "./child" - - value = "${var.foo}" -} - -resource "aws_instance" "foo" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-disable/child/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-disable/child/main.tf deleted file mode 100644 index 9d02c162..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-disable/child/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -variable "value" {} - -provider "aws" { - value = "${var.value}" -} - -resource "aws_instance" "foo" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-disable/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-disable/main.tf deleted file mode 100644 index a405f989..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-disable/main.tf +++ /dev/null @@ -1,7 +0,0 @@ -variable "foo" {} - -module "child" { - source = "./child" - - value = "${var.foo}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-implicit-module/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-implicit-module/main.tf deleted file mode 100644 index 141a04d3..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-implicit-module/main.tf +++ /dev/null @@ -1,10 +0,0 @@ -provider "aws" { - alias = "foo" -} - -module "mod" { - source = "./mod" - providers = { - aws = aws.foo - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-implicit-module/mod/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-implicit-module/mod/main.tf deleted file mode 100644 index 01cf0803..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-implicit-module/mod/main.tf +++ /dev/null @@ -1,2 +0,0 @@ -resource "aws_instance" "bar" { -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-invalid/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-invalid/main.tf deleted file mode 100644 index ec23232a..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-invalid/main.tf +++ /dev/null @@ -1,11 +0,0 @@ -provider "aws" { -} - -module "mod" { - source = "./mod" - - # aws.foo doesn't exist, and should report an error - providers = { - aws = aws.foo - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-invalid/mod/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-invalid/mod/main.tf deleted file mode 100644 index 03641197..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provider-invalid/mod/main.tf +++ /dev/null @@ -1,2 +0,0 @@ -resource "aws_resource" "foo" { -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provisioner-prune/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provisioner-prune/main.tf deleted file mode 100644 index c78a6eca..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-provisioner-prune/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -resource "aws_instance" "web" { - provisioner "foo" {} -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-resource-count-basic/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-resource-count-basic/main.tf deleted file mode 100644 index 782a9142..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-resource-count-basic/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -resource "aws_instance" "foo" { - count = 3 -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-resource-count-deps/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-resource-count-deps/main.tf deleted file mode 100644 index c6a683e6..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-resource-count-deps/main.tf +++ /dev/null @@ -1,8 +0,0 @@ -resource "aws_instance" "foo" { - count = 2 - - provisioner "local-exec" { - command = "echo ${aws_instance.foo.0.id}" - other = "echo ${aws_instance.foo.id}" - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-resource-count-negative/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-resource-count-negative/main.tf deleted file mode 100644 index 267e2008..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-resource-count-negative/main.tf +++ /dev/null @@ -1,4 +0,0 @@ -resource "aws_instance" "foo" { - count = -5 - value = "${aws_instance.foo.0.value}" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-tainted-basic/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-tainted-basic/main.tf deleted file mode 100644 index 64cbf623..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-tainted-basic/main.tf +++ /dev/null @@ -1 +0,0 @@ -resource "aws_instance" "web" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-targets-destroy/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-targets-destroy/main.tf deleted file mode 100644 index 5c1c285e..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/transform-targets-destroy/main.tf +++ /dev/null @@ -1,26 +0,0 @@ -resource "aws_vpc" "notme" {} - -resource "aws_subnet" "notme" { - depends_on = [ - aws_vpc.notme, - ] -} - -resource "aws_instance" "me" { - depends_on = [ - aws_subnet.notme, - ] -} - -resource "aws_instance" "notme" {} -resource "aws_instance" "metoo" { - depends_on = [ - aws_instance.me, - ] -} - -resource "aws_elb" "me" { - depends_on = [ - aws_instance.me, - ] -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/uservars-map/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/uservars-map/main.tf deleted file mode 100644 index ebe10634..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/uservars-map/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -variable "test_map" { - type = "map" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-cycle/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-cycle/main.tf deleted file mode 100644 index 3dc503aa..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-cycle/main.tf +++ /dev/null @@ -1,19 +0,0 @@ -provider "aws" { } - -/* - * When a CBD resource depends on a non-CBD resource, - * a cycle is formed that only shows up when Destroy - * nodes are included in the graph. - */ -resource "aws_security_group" "firewall" { -} - -resource "aws_instance" "web" { - security_groups = [ - "foo", - "${aws_security_group.firewall.foo}" - ] - lifecycle { - create_before_destroy = true - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-module-pc-inherit-orphan/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-module-pc-inherit-orphan/main.tf deleted file mode 100644 index a5c34f64..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-module-pc-inherit-orphan/main.tf +++ /dev/null @@ -1,9 +0,0 @@ -variable "foo" { - default = "bar" -} - -provider "aws" { - set = "${var.foo}" -} - -resource "aws_instance" "foo" {} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-resource-name-symbol/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-resource-name-symbol/main.tf deleted file mode 100644 index e89401f7..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-resource-name-symbol/main.tf +++ /dev/null @@ -1,3 +0,0 @@ -resource "aws_instance" "foo bar" { - num = "2" -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-sensitive-provisioner-config/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-sensitive-provisioner-config/main.tf new file mode 100644 index 00000000..88a37275 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-sensitive-provisioner-config/main.tf @@ -0,0 +1,11 @@ +variable "secret" { + type = string + default = " password123" + sensitive = true +} + +resource "aws_instance" "foo" { + provisioner "test" { + test_string = var.secret + } +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-bad-pc-empty/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-skipped-pc-empty/main.tf similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/testdata/validate-bad-pc-empty/main.tf rename to vendor/github.com/hashicorp/terraform/terraform/testdata/validate-skipped-pc-empty/main.tf diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-var-map-override-old/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-var-map-override-old/main.tf deleted file mode 100644 index 7fe646c8..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-var-map-override-old/main.tf +++ /dev/null @@ -1 +0,0 @@ -variable "foo" { default = { foo = "bar" } } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-var-no-default-explicit-type/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-var-no-default-explicit-type/main.tf index 6bd2bdd5..5953eab4 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-var-no-default-explicit-type/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-var-no-default-explicit-type/main.tf @@ -1,5 +1,5 @@ variable "maybe_a_map" { - type = "map" + type = map(string) - // No default + // No default } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-variable-custom-validations-child-sensitive/child/child.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-variable-custom-validations-child-sensitive/child/child.tf new file mode 100644 index 00000000..05027f75 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-variable-custom-validations-child-sensitive/child/child.tf @@ -0,0 +1,8 @@ +variable "test" { + type = string + + validation { + condition = var.test != "nope" + error_message = "Value must not be \"nope\"." + } +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-variable-custom-validations-child-sensitive/validate-variable-custom-validations.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-variable-custom-validations-child-sensitive/validate-variable-custom-validations.tf new file mode 100644 index 00000000..4f436db1 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/validate-variable-custom-validations-child-sensitive/validate-variable-custom-validations.tf @@ -0,0 +1,10 @@ +variable "test" { + sensitive = true + default = "nope" +} + +module "child" { + source = "./child" + + test = var.test +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/testdata/vars-basic/main.tf b/vendor/github.com/hashicorp/terraform/terraform/testdata/vars-basic/main.tf index 66fa77a8..af3ba5cc 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/testdata/vars-basic/main.tf +++ b/vendor/github.com/hashicorp/terraform/terraform/testdata/vars-basic/main.tf @@ -1,14 +1,14 @@ variable "a" { - default = "foo" - type = "string" + default = "foo" + type = string } variable "b" { - default = [] - type = "list" + default = [] + type = list(string) } variable "c" { - default = {} - type = "map" + default = {} + type = map(string) } diff --git a/vendor/github.com/hashicorp/terraform/terraform/testing.go b/vendor/github.com/hashicorp/terraform/terraform/testing.go deleted file mode 100644 index 3f0418d9..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/testing.go +++ /dev/null @@ -1,19 +0,0 @@ -package terraform - -import ( - "os" - "testing" -) - -// TestStateFile writes the given state to the path. -func TestStateFile(t *testing.T, path string, state *State) { - f, err := os.Create(path) - if err != nil { - t.Fatalf("err: %s", err) - } - defer f.Close() - - if err := WriteState(state, f); err != nil { - t.Fatalf("err: %s", err) - } -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform.go b/vendor/github.com/hashicorp/terraform/terraform/transform.go index dc615f2b..3b412190 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform.go @@ -23,17 +23,6 @@ type GraphVertexTransformer interface { Transform(dag.Vertex) (dag.Vertex, error) } -// GraphTransformIf is a helper function that conditionally returns a -// GraphTransformer given. This is useful for calling inline a sequence -// of transforms without having to split it up into multiple append() calls. -func GraphTransformIf(f func() bool, then GraphTransformer) GraphTransformer { - if f() { - return then - } - - return nil -} - type graphTransformerMulti struct { Transforms []GraphTransformer } diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go b/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go index c6ec449f..9a177cbe 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go @@ -59,7 +59,7 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error { // Record the creators, which will need to depend on the destroyers if they // are only being updated. - creators := make(map[string]GraphNodeCreator) + creators := make(map[string][]GraphNodeCreator) // destroyersByResource records each destroyer by the ConfigResource // address. We use this because dependencies are only referenced as @@ -83,8 +83,8 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error { resAddr := addr.ContainingResource().Config().String() destroyersByResource[resAddr] = append(destroyersByResource[resAddr], n) case GraphNodeCreator: - addr := n.CreateAddr() - creators[addr.String()] = n + addr := n.CreateAddr().ContainingResource().Config().String() + creators[addr] = append(creators[addr], n) } } @@ -111,24 +111,38 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error { log.Printf("[TRACE] DestroyEdgeTransformer: skipping %s => %s inter-module-instance dependency\n", dag.VertexName(desDep), dag.VertexName(des)) } } + + // We can have some create or update nodes which were + // dependents of the destroy node. If they have no destroyer + // themselves, make the connection directly from the creator. + for _, createDep := range creators[resAddr.String()] { + if !graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(createDep, des) { + log.Printf("[DEBUG] DestroyEdgeTransformer: %s has stored dependency of %s\n", dag.VertexName(createDep), dag.VertexName(des)) + g.Connect(dag.BasicEdge(createDep, des)) + } else { + log.Printf("[TRACE] DestroyEdgeTransformer: skipping %s => %s inter-module-instance dependency\n", dag.VertexName(createDep), dag.VertexName(des)) + } + } } } } // connect creators to any destroyers on which they may depend - for _, c := range creators { - ri, ok := c.(GraphNodeResourceInstance) - if !ok { - continue - } + for _, cs := range creators { + for _, c := range cs { + ri, ok := c.(GraphNodeResourceInstance) + if !ok { + continue + } - for _, resAddr := range ri.StateDependencies() { - for _, desDep := range destroyersByResource[resAddr.String()] { - if !graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(c, desDep) { - log.Printf("[TRACE] DestroyEdgeTransformer: %s has stored dependency of %s\n", dag.VertexName(c), dag.VertexName(desDep)) - g.Connect(dag.BasicEdge(c, desDep)) - } else { - log.Printf("[TRACE] DestroyEdgeTransformer: skipping %s => %s inter-module-instance dependency\n", dag.VertexName(c), dag.VertexName(desDep)) + for _, resAddr := range ri.StateDependencies() { + for _, desDep := range destroyersByResource[resAddr.String()] { + if !graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(c, desDep) { + log.Printf("[TRACE] DestroyEdgeTransformer: %s has stored dependency of %s\n", dag.VertexName(c), dag.VertexName(desDep)) + g.Connect(dag.BasicEdge(c, desDep)) + } else { + log.Printf("[TRACE] DestroyEdgeTransformer: skipping %s => %s inter-module-instance dependency\n", dag.VertexName(c), dag.VertexName(desDep)) + } } } } @@ -221,6 +235,13 @@ func (t *pruneUnusedNodesTransformer) Transform(g *Graph) error { } } + case GraphNodeProvider: + // Providers that may have been required by expansion nodes + // that we no longer need can also be removed. + if g.UpEdges(n).Len() > 0 { + return + } + default: return } diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge_test.go b/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge_test.go index 107f26a4..92b2448f 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge_test.go @@ -260,23 +260,75 @@ module.child[1].test_object.c (destroy) } } -func testDestroyNode(addrString string) GraphNodeDestroyer { - instAddr := mustResourceInstanceAddr(addrString) +func TestDestroyEdgeTransformer_destroyThenUpdate(t *testing.T) { + g := Graph{Path: addrs.RootModuleInstance} + g.Add(testUpdateNode("test_object.A")) + g.Add(testDestroyNode("test_object.B")) - inst := NewNodeAbstractResourceInstance(instAddr) + state := states.NewState() + root := state.EnsureModule(addrs.RootModuleInstance) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("test_object.A").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"A","test_string":"old"}`), + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("test_object.B").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"B","test_string":"x"}`), + Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.A")}, + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) - return &NodeDestroyResourceInstance{NodeAbstractResourceInstance: inst} + if err := (&AttachStateTransformer{State: state}).Transform(&g); err != nil { + t.Fatal(err) + } + + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_instance" "a" { + test_string = "udpated" } +`, + }) + tf := &DestroyEdgeTransformer{ + Config: m, + Schemas: simpleTestSchemas(), + } + if err := tf.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } -const testTransformDestroyEdgeBasicStr = ` -test_object.A (destroy) + expected := strings.TrimSpace(` +test_object.A test_object.B (destroy) test_object.B (destroy) -` +`) + actual := strings.TrimSpace(g.String()) -const testTransformDestroyEdgeCreatorStr = ` -test_object.A - test_object.A (destroy) + if actual != expected { + t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) + } +} + +func testDestroyNode(addrString string) GraphNodeDestroyer { + instAddr := mustResourceInstanceAddr(addrString) + inst := NewNodeAbstractResourceInstance(instAddr) + return &NodeDestroyResourceInstance{NodeAbstractResourceInstance: inst} +} + +func testUpdateNode(addrString string) GraphNodeCreator { + instAddr := mustResourceInstanceAddr(addrString) + inst := NewNodeAbstractResourceInstance(instAddr) + return &NodeApplyableResourceInstance{NodeAbstractResourceInstance: inst} +} + +const testTransformDestroyEdgeBasicStr = ` test_object.A (destroy) test_object.B (destroy) test_object.B (destroy) diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_import_state.go b/vendor/github.com/hashicorp/terraform/terraform/transform_import_state.go index bbeba7aa..92e3bbde 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_import_state.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_import_state.go @@ -22,13 +22,13 @@ func (t *ImportStateTransformer) Transform(g *Graph) error { // This is only likely to happen in misconfigured tests if t.Config == nil { - return fmt.Errorf("Cannot import into an empty configuration.") + return fmt.Errorf("cannot import into an empty configuration") } // Get the module config modCfg := t.Config.Descendent(target.Addr.Module.Module()) if modCfg == nil { - return fmt.Errorf("Module %s not found.", target.Addr.Module.Module()) + return fmt.Errorf("module %s not found", target.Addr.Module.Module()) } providerAddr := addrs.AbsProviderConfig{ @@ -116,25 +116,25 @@ func (n *graphNodeImportState) ModulePath() addrs.Module { } // GraphNodeExecutable impl. -func (n *graphNodeImportState) Execute(ctx EvalContext, op walkOperation) error { +func (n *graphNodeImportState) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { // Reset our states n.states = nil - provider, _, err := GetProvider(ctx, n.ResolvedProvider) - if err != nil { - return err + provider, _, err := getProvider(ctx, n.ResolvedProvider) + diags = diags.Append(err) + if diags.HasErrors() { + return diags } // import state absAddr := n.Addr.Resource.Absolute(ctx.Path()) - var diags tfdiags.Diagnostics // Call pre-import hook - err = ctx.Hook(func(h Hook) (HookAction, error) { + diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) { return h.PreImportState(absAddr, n.ID) - }) - if err != nil { - return err + })) + if diags.HasErrors() { + return diags } resp := provider.ImportResourceState(providers.ImportResourceStateRequest{ @@ -143,7 +143,7 @@ func (n *graphNodeImportState) Execute(ctx EvalContext, op walkOperation) error }) diags = diags.Append(resp.Diagnostics) if diags.HasErrors() { - return diags.Err() + return diags } imported := resp.ImportedResources @@ -153,10 +153,10 @@ func (n *graphNodeImportState) Execute(ctx EvalContext, op walkOperation) error n.states = imported // Call post-import hook - err = ctx.Hook(func(h Hook) (HookAction, error) { + diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) { return h.PostImportState(absAddr, imported) - }) - return err + })) + return diags } // GraphNodeDynamicExpandable impl. @@ -259,53 +259,28 @@ func (n *graphNodeImportStateSub) Path() addrs.ModuleInstance { } // GraphNodeExecutable impl. -func (n *graphNodeImportStateSub) Execute(ctx EvalContext, op walkOperation) error { +func (n *graphNodeImportStateSub) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { // If the Ephemeral type isn't set, then it is an error if n.State.TypeName == "" { - return fmt.Errorf("import of %s didn't set type", n.TargetAddr.String()) + diags = diags.Append(fmt.Errorf("import of %s didn't set type", n.TargetAddr.String())) + return diags } state := n.State.AsInstanceObject() - provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider) - if err != nil { - return err - } - // EvalRefresh - evalRefresh := &EvalRefresh{ - Addr: n.TargetAddr.Resource, - ProviderAddr: n.ResolvedProvider, - Provider: &provider, - ProviderSchema: &providerSchema, - State: &state, - Output: &state, - } - _, err = evalRefresh.Eval(ctx) - if err != nil { - return err + // Refresh + riNode := &NodeAbstractResourceInstance{ + Addr: n.TargetAddr, + NodeAbstractResource: NodeAbstractResource{ + ResolvedProvider: n.ResolvedProvider, + }, } - - // Verify the existance of the imported resource - if state.Value.IsNull() { - var diags tfdiags.Diagnostics - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Cannot import non-existent remote object", - fmt.Sprintf( - "While attempting to import an existing object to %s, the provider detected that no object exists with the given id. Only pre-existing objects can be imported; check that the id is correct and that it is associated with the provider's configured region or endpoint, or use \"terraform apply\" to create a new remote object for this resource.", - n.TargetAddr.Resource.String(), - ), - )) - return diags.Err() + state, refreshDiags := riNode.refresh(ctx, state) + diags = diags.Append(refreshDiags) + if diags.HasErrors() { + return diags } - //EvalWriteState - evalWriteState := &EvalWriteState{ - Addr: n.TargetAddr.Resource, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - } - _, err = evalWriteState.Eval(ctx) - return err + diags = diags.Append(riNode.writeResourceInstanceState(ctx, state, workingState)) + return diags } diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_import_state_test.go b/vendor/github.com/hashicorp/terraform/terraform/transform_import_state_test.go index 9068a365..84eb47f1 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_import_state_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_import_state_test.go @@ -14,12 +14,17 @@ import ( func TestGraphNodeImportStateExecute(t *testing.T) { state := states.NewState() provider := testProvider("aws") - provider.ImportStateReturn = []*InstanceState{ - &InstanceState{ - ID: "bar", - Ephemeral: EphemeralState{Type: "aws_instance"}, + provider.ImportResourceStateResponse = &providers.ImportResourceStateResponse{ + ImportedResources: []providers.ImportedResource{ + { + TypeName: "aws_instance", + State: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("bar"), + }), + }, }, } + ctx := &MockEvalContext{ StateState: state.SyncWrapper(), ProviderProvider: provider, @@ -41,9 +46,9 @@ func TestGraphNodeImportStateExecute(t *testing.T) { }, } - err := node.Execute(ctx, walkImport) - if err != nil { - t.Fatalf("Unexpected error: %s", err.Error()) + diags := node.Execute(ctx, walkImport) + if diags.HasErrors() { + t.Fatalf("Unexpected error: %s", diags.Err()) } if len(node.states) != 1 { @@ -93,9 +98,9 @@ func TestGraphNodeImportStateSubExecute(t *testing.T) { Module: addrs.RootModule, }, } - err := node.Execute(ctx, walkImport) - if err != nil { - t.Fatalf("Unexpected error: %s", err.Error()) + diags := node.Execute(ctx, walkImport) + if diags.HasErrors() { + t.Fatalf("Unexpected error: %s", diags.Err()) } // check for resource in state diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_orphan_count_test.go b/vendor/github.com/hashicorp/terraform/terraform/transform_orphan_count_test.go index ca823d8c..1ec725cd 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_orphan_count_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_orphan_count_test.go @@ -1,60 +1,51 @@ package terraform -// FIXME: Update these tests for the new OrphanResourceCountTransformer -// interface that expects to be given a list of instance addresses that -// exist in config. - -/* import ( "strings" "testing" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/states" - "github.com/zclconf/go-cty/cty" ) func TestOrphanResourceCountTransformer(t *testing.T) { - state := MustShimLegacyState(&State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: []string{"root"}, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - - "aws_instance.foo.2": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, + state := states.NewState() + root := state.RootModule() + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("aws_instance.web").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"foo"}`), }, - }) + mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + ) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("aws_instance.foo[0]").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"foo"}`), + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + ) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("aws_instance.foo[2]").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"foo"}`), + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + ) g := Graph{Path: addrs.RootModuleInstance} { - tf := &OrphanResourceCountTransformer{ + tf := &OrphanResourceInstanceCountTransformer{ Concrete: testOrphanResourceConcreteFunc, - Count: 1, Addr: addrs.RootModuleInstance.Resource( addrs.ManagedResourceMode, "aws_instance", "foo", ), - State: state, + InstanceAddrs: []addrs.AbsResourceInstance{mustResourceInstanceAddr("aws_instance.foo[0]")}, + State: state, } if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) @@ -69,100 +60,43 @@ func TestOrphanResourceCountTransformer(t *testing.T) { } func TestOrphanResourceCountTransformer_zero(t *testing.T) { - state := MustShimLegacyState(&State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: []string{"root"}, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - - "aws_instance.foo.2": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, + state := states.NewState() + root := state.RootModule() + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("aws_instance.web").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"foo"}`), }, - }) - - g := Graph{Path: addrs.RootModuleInstance} - - { - tf := &OrphanResourceCountTransformer{ - Concrete: testOrphanResourceConcreteFunc, - Count: 0, - Addr: addrs.RootModuleInstance.Resource( - addrs.ManagedResourceMode, "aws_instance", "foo", - ), - State: state, - } - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformOrphanResourceCountZeroStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestOrphanResourceCountTransformer_oneNoIndex(t *testing.T) { - state := MustShimLegacyState(&State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: []string{"root"}, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - - "aws_instance.foo.2": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + ) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("aws_instance.foo[0]").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"foo"}`), }, - }) + mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + ) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("aws_instance.foo[2]").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"foo"}`), + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + ) g := Graph{Path: addrs.RootModuleInstance} { - tf := &OrphanResourceCountTransformer{ + tf := &OrphanResourceInstanceCountTransformer{ Concrete: testOrphanResourceConcreteFunc, - Count: 1, Addr: addrs.RootModuleInstance.Resource( addrs.ManagedResourceMode, "aws_instance", "foo", ), - State: state, + InstanceAddrs: []addrs.AbsResourceInstance{}, + State: state, } if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) @@ -170,161 +104,50 @@ func TestOrphanResourceCountTransformer_oneNoIndex(t *testing.T) { } actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformOrphanResourceCountOneNoIndexStr) + expected := strings.TrimSpace(testTransformOrphanResourceCountZeroStr) if actual != expected { t.Fatalf("bad:\n\n%s", actual) } } func TestOrphanResourceCountTransformer_oneIndex(t *testing.T) { - state := MustShimLegacyState(&State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: []string{"root"}, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - - "aws_instance.foo.0": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - - "aws_instance.foo.1": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, + state := states.NewState() + root := state.RootModule() + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("aws_instance.web").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"foo"}`), }, - }) - - g := Graph{Path: addrs.RootModuleInstance} - - { - tf := &OrphanResourceCountTransformer{ - Concrete: testOrphanResourceConcreteFunc, - Count: 1, - Addr: addrs.RootModuleInstance.Resource( - addrs.ManagedResourceMode, "aws_instance", "foo", - ), - State: state, - } - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformOrphanResourceCountOneIndexStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestOrphanResourceCountTransformer_zeroAndNone(t *testing.T) { - state := MustShimLegacyState(&State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: []string{"root"}, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - - "aws_instance.foo.0": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + ) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("aws_instance.foo[0]").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"foo"}`), }, - }) - - g := Graph{Path: addrs.RootModuleInstance} - - { - tf := &OrphanResourceCountTransformer{ - Concrete: testOrphanResourceConcreteFunc, - Count: -1, - Addr: addrs.RootModuleInstance.Resource( - addrs.ManagedResourceMode, "aws_instance", "foo", - ), - State: state, - } - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformOrphanResourceCountZeroAndNoneStr) - if actual != expected { - t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) - } -} - -func TestOrphanResourceCountTransformer_zeroAndNoneCount(t *testing.T) { - state := MustShimLegacyState(&State{ - Modules: []*ModuleState{ - &ModuleState{ - Path: []string{"root"}, - Resources: map[string]*ResourceState{ - "aws_instance.web": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - - "aws_instance.foo": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - - "aws_instance.foo.0": &ResourceState{ - Type: "aws_instance", - Primary: &InstanceState{ - ID: "foo", - }, - }, - }, - }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + ) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("aws_instance.foo[1]").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"foo"}`), }, - }) + mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), + ) g := Graph{Path: addrs.RootModuleInstance} { - tf := &OrphanResourceCountTransformer{ + tf := &OrphanResourceInstanceCountTransformer{ Concrete: testOrphanResourceConcreteFunc, - Count: 2, Addr: addrs.RootModuleInstance.Resource( addrs.ManagedResourceMode, "aws_instance", "foo", ), - State: state, + InstanceAddrs: []addrs.AbsResourceInstance{mustResourceInstanceAddr("aws_instance.foo[0]")}, + State: state, } if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) @@ -332,7 +155,7 @@ func TestOrphanResourceCountTransformer_zeroAndNoneCount(t *testing.T) { } actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformOrphanResourceCountZeroAndNoneCountStr) + expected := strings.TrimSpace(testTransformOrphanResourceCountOneIndexStr) if actual != expected { t.Fatalf("bad:\n\n%s", actual) } @@ -357,10 +180,7 @@ func TestOrphanResourceCountTransformer_ForEachEdgesAdded(t *testing.T) { }, Status: states.ObjectReady, }, - addrs.AbsProviderConfig{ - Provider: addrs.NewDefaultProvider("aws"), - Module: addrs.RootModuleInstance, - }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), ) // NoKey'd resource @@ -376,25 +196,20 @@ func TestOrphanResourceCountTransformer_ForEachEdgesAdded(t *testing.T) { }, Status: states.ObjectReady, }, - addrs.AbsProviderConfig{ - Provider: addrs.NewDefaultProvider("aws"), - Module: addrs.RootModuleInstance, - }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`), ) }) g := Graph{Path: addrs.RootModuleInstance} { - tf := &OrphanResourceCountTransformer{ + tf := &OrphanResourceInstanceCountTransformer{ Concrete: testOrphanResourceConcreteFunc, - // No keys in this ForEach ensure both our resources end - // up orphaned in this test - ForEach: map[string]cty.Value{}, Addr: addrs.RootModuleInstance.Resource( addrs.ManagedResourceMode, "aws_instance", "foo", ), - State: state, + InstanceAddrs: []addrs.AbsResourceInstance{}, + State: state, } if err := tf.Transform(&g); err != nil { t.Fatalf("err: %s", err) @@ -413,11 +228,7 @@ aws_instance.foo[2] (orphan) ` const testTransformOrphanResourceCountZeroStr = ` -aws_instance.foo (orphan) -aws_instance.foo[2] (orphan) -` - -const testTransformOrphanResourceCountOneNoIndexStr = ` +aws_instance.foo[0] (orphan) aws_instance.foo[2] (orphan) ` @@ -425,17 +236,7 @@ const testTransformOrphanResourceCountOneIndexStr = ` aws_instance.foo[1] (orphan) ` -const testTransformOrphanResourceCountZeroAndNoneStr = ` -aws_instance.foo[0] (orphan) -` - -const testTransformOrphanResourceCountZeroAndNoneCountStr = ` -aws_instance.foo (orphan) -` - const testTransformOrphanResourceForEachStr = ` aws_instance.foo (orphan) aws_instance.foo["bar"] (orphan) - aws_instance.foo (orphan) ` -*/ diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_provider.go b/vendor/github.com/hashicorp/terraform/terraform/transform_provider.go index d3b2248d..0ce42e10 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_provider.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_provider.go @@ -31,8 +31,6 @@ func TransformProviders(providers []string, concrete ConcreteProviderNodeFunc, c }, // Remove unused providers and proxies &PruneProviderTransformer{}, - // Connect provider to their parent provider nodes - &ParentProviderTransformer{}, ) } @@ -67,6 +65,7 @@ type GraphNodeProviderConsumer interface { // ProvidedBy returns the address of the provider configuration the node // refers to, if available. The following value types may be returned: // + // nil + exact true: the node does not require a provider // * addrs.LocalProviderConfig: the provider was set in the resource config // * addrs.AbsProviderConfig + exact true: the provider configuration was // taken from the instance state. @@ -111,9 +110,14 @@ func (t *ProviderTransformer) Transform(g *Graph) error { for _, v := range g.Vertices() { // Does the vertex _directly_ use a provider? if pv, ok := v.(GraphNodeProviderConsumer); ok { + providerAddr, exact := pv.ProvidedBy() + if providerAddr == nil && exact { + // no provider is required + continue + } + requested[v] = make(map[string]ProviderRequest) - providerAddr, exact := pv.ProvidedBy() var absPc addrs.AbsProviderConfig switch p := providerAddr.(type) { @@ -221,11 +225,10 @@ func (t *ProviderTransformer) Transform(g *Graph) error { break } - // see if this in an inherited provider + // see if this is a proxy provider pointing to another concrete config if p, ok := target.(*graphNodeProxyProvider); ok { g.Remove(p) target = p.Target() - key = target.(GraphNodeProvider).ProviderAddr().String() } log.Printf("[DEBUG] ProviderTransformer: %q (%T) needs %s", dag.VertexName(v), v, dag.VertexName(target)) @@ -250,8 +253,7 @@ func (t *CloseProviderTransformer) Transform(g *Graph) error { cpm := make(map[string]*graphNodeCloseProvider) var err error - for _, v := range pm { - p := v.(GraphNodeProvider) + for _, p := range pm { key := p.ProviderAddr().String() // get the close provider of this type if we alread created it @@ -353,42 +355,6 @@ func (t *MissingProviderTransformer) Transform(g *Graph) error { return err } -// ParentProviderTransformer connects provider nodes to their parents. -// -// This works by finding nodes that are both GraphNodeProviders and -// GraphNodeModuleInstance. It then connects the providers to their parent -// path. The parent provider is always at the root level. -type ParentProviderTransformer struct{} - -func (t *ParentProviderTransformer) Transform(g *Graph) error { - pm := providerVertexMap(g) - for _, v := range g.Vertices() { - // Only care about providers - pn, ok := v.(GraphNodeProvider) - if !ok { - continue - } - - // Also require non-empty path, since otherwise we're in the root - // module and so cannot have a parent. - if len(pn.ModulePath()) <= 1 { - continue - } - - // this provider may be disabled, but we can only get it's name from - // the ProviderName string - addr := pn.ProviderAddr() - parentAddr, ok := addr.Inherited() - if ok { - parent := pm[parentAddr.String()] - if parent != nil { - g.Connect(dag.BasicEdge(v, parent)) - } - } - } - return nil -} - // PruneProviderTransformer removes any providers that are not actually used by // anything, and provider proxies. This avoids the provider being initialized // and configured. This both saves resources but also avoids errors since @@ -431,24 +397,13 @@ func providerVertexMap(g *Graph) map[string]GraphNodeProvider { return m } -func closeProviderVertexMap(g *Graph) map[string]GraphNodeCloseProvider { - m := make(map[string]GraphNodeCloseProvider) - for _, v := range g.Vertices() { - if pv, ok := v.(GraphNodeCloseProvider); ok { - addr := pv.CloseProviderAddr() - m[addr.String()] = pv - } - } - - return m -} - type graphNodeCloseProvider struct { Addr addrs.AbsProviderConfig } var ( _ GraphNodeCloseProvider = (*graphNodeCloseProvider)(nil) + _ GraphNodeExecutable = (*graphNodeCloseProvider)(nil) ) func (n *graphNodeCloseProvider) Name() string { @@ -461,13 +416,8 @@ func (n *graphNodeCloseProvider) ModulePath() addrs.Module { } // GraphNodeExecutable impl. -func (n *graphNodeCloseProvider) Execute(ctx EvalContext, op walkOperation) error { - return ctx.CloseProvider(n.Addr) -} - -// GraphNodeDependable impl. -func (n *graphNodeCloseProvider) DependableName() []string { - return []string{n.Name()} +func (n *graphNodeCloseProvider) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) { + return diags.Append(ctx.CloseProvider(n.Addr)) } func (n *graphNodeCloseProvider) CloseProviderAddr() addrs.AbsProviderConfig { @@ -584,6 +534,37 @@ func (t *ProviderConfigTransformer) transformSingle(g *Graph, c *configs.Config) mod := c.Module path := c.Path + // If this is the root module, we can add nodes for required providers that + // have no configuration, equivalent to having an empty configuration + // block. This will ensure that a provider node exists for modules to + // access when passing around configuration and inheritance. + if path.IsRoot() && c.Module.ProviderRequirements != nil { + for name, p := range c.Module.ProviderRequirements.RequiredProviders { + if _, configured := mod.ProviderConfigs[name]; configured { + continue + } + + addr := addrs.AbsProviderConfig{ + Provider: p.Type, + Module: path, + } + + abstract := &NodeAbstractProvider{ + Addr: addr, + } + + var v dag.Vertex + if t.Concrete != nil { + v = t.Concrete(abstract) + } else { + v = abstract + } + + g.Add(v) + t.providers[addr.String()] = v.(GraphNodeProvider) + } + } + // add all providers from the configuration for _, p := range mod.ProviderConfigs { fqn := mod.ProviderForLocalConfig(p.Addr()) @@ -608,13 +589,17 @@ func (t *ProviderConfigTransformer) transformSingle(g *Graph, c *configs.Config) key := addr.String() t.providers[key] = v.(GraphNodeProvider) - // A provider configuration is "proxyable" if its configuration is - // entirely empty. This means it's standing in for a provider - // configuration that must be passed in from the parent module. - // We decide this by evaluating the config with an empty schema; - // if this succeeds, then we know there's nothing in the body. - _, diags := p.Config.Content(&hcl.BodySchema{}) - t.proxiable[key] = !diags.HasErrors() + // While deprecated, we still accept empty configuration blocks within + // modules as being a possible proxy for passed configuration. + if !path.IsRoot() { + // A provider configuration is "proxyable" if its configuration is + // entirely empty. This means it's standing in for a provider + // configuration that must be passed in from the parent module. + // We decide this by evaluating the config with an empty schema; + // if this succeeds, then we know there's nothing in the body. + _, diags := p.Config.Content(&hcl.BodySchema{}) + t.proxiable[key] = !diags.HasErrors() + } } // Now replace the provider nodes with proxy nodes if a provider was being @@ -627,7 +612,7 @@ func (t *ProviderConfigTransformer) addProxyProviders(g *Graph, c *configs.Confi path := c.Path // can't add proxies at the root - if len(path) == 0 { + if path.IsRoot() { return nil } @@ -683,15 +668,13 @@ func (t *ProviderConfigTransformer) addProxyProviders(g *Graph, c *configs.Confi concreteProvider := t.providers[fullName] - // replace the concrete node with the provider passed in - if concreteProvider != nil && t.proxiable[fullName] { - g.Replace(concreteProvider, proxy) - t.providers[fullName] = proxy - continue - } - - // aliased configurations can't be implicitly passed in - if fullAddr.Alias != "" { + // replace the concrete node with the provider passed in only if it is + // proxyable + if concreteProvider != nil { + if t.proxiable[fullName] { + g.Replace(concreteProvider, proxy) + t.providers[fullName] = proxy + } continue } @@ -700,6 +683,7 @@ func (t *ProviderConfigTransformer) addProxyProviders(g *Graph, c *configs.Confi g.Add(proxy) t.providers[fullName] = proxy } + return nil } diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_provider_test.go b/vendor/github.com/hashicorp/terraform/terraform/transform_provider_test.go index 01ff63a0..dc1d0ad4 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_provider_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_provider_test.go @@ -6,36 +6,39 @@ import ( "testing" "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/dag" ) -func TestProviderTransformer(t *testing.T) { - mod := testModule(t, "transform-provider-basic") +func testProviderTransformerGraph(t *testing.T, cfg *configs.Config) *Graph { + t.Helper() - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } + g := &Graph{Path: addrs.RootModuleInstance} + ct := &ConfigTransformer{Config: cfg} + if err := ct.Transform(g); err != nil { + t.Fatal(err) } - - { - transform := &AttachResourceConfigTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } + arct := &AttachResourceConfigTransformer{Config: cfg} + if err := arct.Transform(g); err != nil { + t.Fatal(err) } + return g +} + +func TestProviderTransformer(t *testing.T) { + mod := testModule(t, "transform-provider-basic") + + g := testProviderTransformerGraph(t, mod) { transform := &MissingProviderTransformer{Providers: []string{"aws"}} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } transform := &ProviderTransformer{} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } @@ -49,23 +52,9 @@ func TestProviderTransformer(t *testing.T) { func TestProviderTransformer_ImportModuleChild(t *testing.T) { mod := testModule(t, "import-module") - g := Graph{Path: addrs.RootModuleInstance} + g := testProviderTransformerGraph(t, mod) { - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &AttachResourceConfigTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - tf := &ImportStateTransformer{ Config: mod, Targets: []*ImportTarget{ @@ -83,7 +72,7 @@ func TestProviderTransformer_ImportModuleChild(t *testing.T) { }, } - if err := tf.Transform(&g); err != nil { + if err := tf.Transform(g); err != nil { t.Fatalf("err: %s", err) } t.Logf("graph after ImportStateTransformer:\n%s", g.String()) @@ -91,7 +80,7 @@ func TestProviderTransformer_ImportModuleChild(t *testing.T) { { tf := &MissingProviderTransformer{Providers: []string{"foo", "bar"}} - if err := tf.Transform(&g); err != nil { + if err := tf.Transform(g); err != nil { t.Fatalf("err: %s", err) } t.Logf("graph after MissingProviderTransformer:\n%s", g.String()) @@ -99,7 +88,7 @@ func TestProviderTransformer_ImportModuleChild(t *testing.T) { { tf := &ProviderTransformer{} - if err := tf.Transform(&g); err != nil { + if err := tf.Transform(g); err != nil { t.Fatalf("err: %s", err) } t.Logf("graph after ProviderTransformer:\n%s", g.String()) @@ -117,30 +106,16 @@ func TestProviderTransformer_fqns(t *testing.T) { for _, mod := range []string{"fqns", "fqns-module"} { mod := testModule(t, fmt.Sprintf("transform-provider-%s", mod)) - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &AttachResourceConfigTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - + g := testProviderTransformerGraph(t, mod) { transform := &MissingProviderTransformer{Providers: []string{"aws"}, Config: mod} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } transform := &ProviderTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } @@ -154,39 +129,25 @@ func TestProviderTransformer_fqns(t *testing.T) { func TestCloseProviderTransformer(t *testing.T) { mod := testModule(t, "transform-provider-basic") - - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &AttachResourceConfigTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } + g := testProviderTransformerGraph(t, mod) { transform := &MissingProviderTransformer{Providers: []string{"aws"}} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } { transform := &ProviderTransformer{} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } { transform := &CloseProviderTransformer{} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } @@ -201,9 +162,8 @@ func TestCloseProviderTransformer(t *testing.T) { func TestCloseProviderTransformer_withTargets(t *testing.T) { mod := testModule(t, "transform-provider-basic") - g := Graph{Path: addrs.RootModuleInstance} + g := testProviderTransformerGraph(t, mod) transforms := []GraphTransformer{ - &ConfigTransformer{Config: mod}, &MissingProviderTransformer{Providers: []string{"aws"}}, &ProviderTransformer{}, &CloseProviderTransformer{}, @@ -217,7 +177,7 @@ func TestCloseProviderTransformer_withTargets(t *testing.T) { } for _, tr := range transforms { - if err := tr.Transform(&g); err != nil { + if err := tr.Transform(g); err != nil { t.Fatalf("err: %s", err) } } @@ -232,38 +192,24 @@ func TestCloseProviderTransformer_withTargets(t *testing.T) { func TestMissingProviderTransformer(t *testing.T) { mod := testModule(t, "transform-provider-missing") - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &AttachResourceConfigTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - + g := testProviderTransformerGraph(t, mod) { transform := &MissingProviderTransformer{Providers: []string{"aws", "foo", "bar"}} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } { transform := &ProviderTransformer{} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } { transform := &CloseProviderTransformer{} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } @@ -280,30 +226,16 @@ func TestMissingProviderTransformer_grandchildMissing(t *testing.T) { concrete := func(a *NodeAbstractProvider) dag.Vertex { return a } - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &AttachResourceConfigTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - + g := testProviderTransformerGraph(t, mod) { transform := TransformProviders([]string{"aws", "foo", "bar"}, concrete, mod) - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } { transform := &TransitiveReductionTransformer{} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } @@ -318,45 +250,31 @@ func TestMissingProviderTransformer_grandchildMissing(t *testing.T) { func TestPruneProviderTransformer(t *testing.T) { mod := testModule(t, "transform-provider-prune") - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &AttachResourceConfigTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - + g := testProviderTransformerGraph(t, mod) { transform := &MissingProviderTransformer{Providers: []string{"foo"}} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } { transform := &ProviderTransformer{} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } { transform := &CloseProviderTransformer{} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } { transform := &PruneProviderTransformer{} - if err := transform.Transform(&g); err != nil { + if err := transform.Transform(g); err != nil { t.Fatalf("err: %s", err) } } @@ -373,23 +291,10 @@ func TestProviderConfigTransformer_parentProviders(t *testing.T) { mod := testModule(t, "transform-provider-inherit") concrete := func(a *NodeAbstractProvider) dag.Vertex { return a } - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - { - tf := &AttachResourceConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - + g := testProviderTransformerGraph(t, mod) { tf := TransformProviders([]string{"aws"}, concrete, mod) - if err := tf.Transform(&g); err != nil { + if err := tf.Transform(g); err != nil { t.Fatalf("err: %s", err) } } @@ -406,23 +311,10 @@ func TestProviderConfigTransformer_grandparentProviders(t *testing.T) { mod := testModule(t, "transform-provider-grandchild-inherit") concrete := func(a *NodeAbstractProvider) dag.Vertex { return a } - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - { - tf := &AttachResourceConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - + g := testProviderTransformerGraph(t, mod) { tf := TransformProviders([]string{"aws"}, concrete, mod) - if err := tf.Transform(&g); err != nil { + if err := tf.Transform(g); err != nil { t.Fatalf("err: %s", err) } } @@ -434,66 +326,123 @@ func TestProviderConfigTransformer_grandparentProviders(t *testing.T) { } } -// pass a specific provider into a module using it implicitly -func TestProviderConfigTransformer_implicitModule(t *testing.T) { - mod := testModule(t, "transform-provider-implicit-module") +func TestProviderConfigTransformer_inheritOldSkool(t *testing.T) { + mod := testModuleInline(t, map[string]string{ + "main.tf": ` +provider "test" { + test_string = "config" +} + +module "moda" { + source = "./moda" +} +`, + + "moda/main.tf": ` +resource "test_object" "a" { +} +`, + }) concrete := func(a *NodeAbstractProvider) dag.Vertex { return a } - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - { - tf := &AttachResourceConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } + g := testProviderTransformerGraph(t, mod) { - tf := TransformProviders([]string{"aws"}, concrete, mod) - if err := tf.Transform(&g); err != nil { + tf := TransformProviders([]string{"registry.terraform.io/hashicorp/test"}, concrete, mod) + if err := tf.Transform(g); err != nil { t.Fatalf("err: %s", err) } } + expected := `module.moda.test_object.a + provider["registry.terraform.io/hashicorp/test"] +provider["registry.terraform.io/hashicorp/test"]` + actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(`module.mod.aws_instance.bar - provider["registry.terraform.io/hashicorp/aws"].foo -provider["registry.terraform.io/hashicorp/aws"].foo`) if actual != expected { - t.Fatalf("wrong result\n\nexpected:\n%s\n\ngot:\n%s", expected, actual) + t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) } } -// error out when a non-existent provider is named in a module providers map -func TestProviderConfigTransformer_invalidProvider(t *testing.T) { - mod := testModule(t, "transform-provider-invalid") - concrete := func(a *NodeAbstractProvider) dag.Vertex { return a } +// Verify that configurations which are not recommended yet supported still work +func TestProviderConfigTransformer_nestedModuleProviders(t *testing.T) { + mod := testModuleInline(t, map[string]string{ + "main.tf": ` +terraform { + required_providers { + test = { + source = "registry.terraform.io/hashicorp/test" + } + } +} - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } +provider "test" { + alias = "z" + test_string = "config" +} + +module "moda" { + source = "./moda" + providers = { + test.x = test.z + } +} +`, + + "moda/main.tf": ` +terraform { + required_providers { + test = { + source = "registry.terraform.io/hashicorp/test" + configuration_aliases = [ test.x ] } + } +} + +provider "test" { + test_string = "config" +} + +// this should connect to this module's provider +resource "test_object" "a" { +} + +resource "test_object" "x" { + provider = test.x +} + +module "modb" { + source = "./modb" +} +`, + + "moda/modb/main.tf": ` +# this should end up with the provider from the parent module +resource "test_object" "a" { +} +`, + }) + concrete := func(a *NodeAbstractProvider) dag.Vertex { return a } + + g := testProviderTransformerGraph(t, mod) { - tf := &AttachResourceConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { + tf := TransformProviders([]string{"registry.terraform.io/hashicorp/test"}, concrete, mod) + if err := tf.Transform(g); err != nil { t.Fatalf("err: %s", err) } } - tf := TransformProviders([]string{"aws"}, concrete, mod) - err := tf.Transform(&g) - if err == nil { - t.Fatal("expected missing provider error") - } - if !strings.Contains(err.Error(), `provider["registry.terraform.io/hashicorp/aws"].foo`) { - t.Fatalf("error should reference missing provider, got: %s", err) + expected := `module.moda.module.modb.test_object.a + module.moda.provider["registry.terraform.io/hashicorp/test"] +module.moda.provider["registry.terraform.io/hashicorp/test"] +module.moda.test_object.a + module.moda.provider["registry.terraform.io/hashicorp/test"] +module.moda.test_object.x + provider["registry.terraform.io/hashicorp/test"].z +provider["registry.terraform.io/hashicorp/test"].z` + + actual := strings.TrimSpace(g.String()) + if actual != expected { + t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual) } } @@ -545,31 +494,6 @@ provider["registry.terraform.io/hashicorp/foo"] (close) provider["registry.terraform.io/hashicorp/foo"] ` -const testTransformDisableProviderBasicStr = ` -module.child - provider["registry.terraform.io/hashicorp/aws"] (disabled) - var.foo -provider["registry.terraform.io/hashicorp/aws"] (close) - module.child - provider["registry.terraform.io/hashicorp/aws"] (disabled) -provider["registry.terraform.io/hashicorp/aws"] (disabled) -var.foo -` - -const testTransformDisableProviderKeepStr = ` -aws_instance.foo - provider["registry.terraform.io/hashicorp/aws"] -module.child - provider["registry.terraform.io/hashicorp/aws"] - var.foo -provider["registry.terraform.io/hashicorp/aws"] -provider["registry.terraform.io/hashicorp/aws"] (close) - aws_instance.foo - module.child - provider["registry.terraform.io/hashicorp/aws"] -var.foo -` - const testTransformModuleProviderConfigStr = ` module.child.aws_instance.thing provider["registry.terraform.io/hashicorp/aws"].foo diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_provisioner.go b/vendor/github.com/hashicorp/terraform/terraform/transform_provisioner.go index 638410d4..38e3a8ed 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_provisioner.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_provisioner.go @@ -1,179 +1,8 @@ package terraform -import ( - "fmt" - "log" - - "github.com/hashicorp/go-multierror" - "github.com/hashicorp/terraform/dag" -) - -// GraphNodeProvisioner is an interface that nodes that can be a provisioner -// must implement. The ProvisionerName returned is the name of the provisioner -// they satisfy. -type GraphNodeProvisioner interface { - ProvisionerName() string -} - -// GraphNodeCloseProvisioner is an interface that nodes that can be a close -// provisioner must implement. The CloseProvisionerName returned is the name -// of the provisioner they satisfy. -type GraphNodeCloseProvisioner interface { - CloseProvisionerName() string -} - // GraphNodeProvisionerConsumer is an interface that nodes that require // a provisioner must implement. ProvisionedBy must return the names of the // provisioners to use. type GraphNodeProvisionerConsumer interface { ProvisionedBy() []string } - -// ProvisionerTransformer is a GraphTransformer that maps resources to -// provisioners within the graph. This will error if there are any resources -// that don't map to proper resources. -type ProvisionerTransformer struct{} - -func (t *ProvisionerTransformer) Transform(g *Graph) error { - // Go through the other nodes and match them to provisioners they need - var err error - m := provisionerVertexMap(g) - for _, v := range g.Vertices() { - if pv, ok := v.(GraphNodeProvisionerConsumer); ok { - for _, p := range pv.ProvisionedBy() { - if m[p] == nil { - err = multierror.Append(err, fmt.Errorf( - "%s: provisioner %s couldn't be found", - dag.VertexName(v), p)) - continue - } - - log.Printf("[TRACE] ProvisionerTransformer: %s is provisioned by %s (%q)", dag.VertexName(v), p, dag.VertexName(m[p])) - g.Connect(dag.BasicEdge(v, m[p])) - } - } - } - - return err -} - -// MissingProvisionerTransformer is a GraphTransformer that adds nodes -// for missing provisioners into the graph. -type MissingProvisionerTransformer struct { - // Provisioners is the list of provisioners we support. - Provisioners []string -} - -func (t *MissingProvisionerTransformer) Transform(g *Graph) error { - // Create a set of our supported provisioners - supported := make(map[string]struct{}, len(t.Provisioners)) - for _, v := range t.Provisioners { - supported[v] = struct{}{} - } - - // Get the map of provisioners we already have in our graph - m := provisionerVertexMap(g) - - // Go through all the provisioner consumers and make sure we add - // that provisioner if it is missing. - for _, v := range g.Vertices() { - pv, ok := v.(GraphNodeProvisionerConsumer) - if !ok { - continue - } - - for _, p := range pv.ProvisionedBy() { - if _, ok := m[p]; ok { - // This provisioner already exists as a configure node - continue - } - - if _, ok := supported[p]; !ok { - // If we don't support the provisioner type, we skip it. - // Validation later will catch this as an error. - continue - } - - // Build the vertex - var newV dag.Vertex = &NodeProvisioner{ - NameValue: p, - } - - // Add the missing provisioner node to the graph - m[p] = g.Add(newV) - log.Printf("[TRACE] MissingProviderTransformer: added implicit provisioner %s, first implied by %s", p, dag.VertexName(v)) - } - } - - return nil -} - -// CloseProvisionerTransformer is a GraphTransformer that adds nodes to the -// graph that will close open provisioner connections that aren't needed -// anymore. A provisioner connection is not needed anymore once all depended -// resources in the graph are evaluated. -type CloseProvisionerTransformer struct{} - -func (t *CloseProvisionerTransformer) Transform(g *Graph) error { - m := closeProvisionerVertexMap(g) - for _, v := range g.Vertices() { - if pv, ok := v.(GraphNodeProvisionerConsumer); ok { - for _, p := range pv.ProvisionedBy() { - source := m[p] - - if source == nil { - // Create a new graphNodeCloseProvisioner and add it to the graph - source = &graphNodeCloseProvisioner{ProvisionerNameValue: p} - g.Add(source) - - // Make sure we also add the new graphNodeCloseProvisioner to the map - // so we don't create and add any duplicate graphNodeCloseProvisioners. - m[p] = source - } - - g.Connect(dag.BasicEdge(source, v)) - } - } - } - - return nil -} - -func provisionerVertexMap(g *Graph) map[string]dag.Vertex { - m := make(map[string]dag.Vertex) - for _, v := range g.Vertices() { - if pv, ok := v.(GraphNodeProvisioner); ok { - m[pv.ProvisionerName()] = v - } - } - - return m -} - -func closeProvisionerVertexMap(g *Graph) map[string]dag.Vertex { - m := make(map[string]dag.Vertex) - for _, v := range g.Vertices() { - if pv, ok := v.(GraphNodeCloseProvisioner); ok { - m[pv.CloseProvisionerName()] = v - } - } - - return m -} - -type graphNodeCloseProvisioner struct { - ProvisionerNameValue string -} - -func (n *graphNodeCloseProvisioner) Name() string { - return fmt.Sprintf("provisioner.%s (close)", n.ProvisionerNameValue) -} - -// GraphNodeExecutable impl. -func (n *graphNodeCloseProvisioner) Execute(ctx EvalContext, op walkOperation) error { - return ctx.CloseProvisioner(n.ProvisionerNameValue) -} - -func (n *graphNodeCloseProvisioner) CloseProvisionerName() string { - return n.ProvisionerNameValue -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_provisioner_test.go b/vendor/github.com/hashicorp/terraform/terraform/transform_provisioner_test.go deleted file mode 100644 index a6da10af..00000000 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_provisioner_test.go +++ /dev/null @@ -1,203 +0,0 @@ -package terraform - -import ( - "strings" - "testing" - - "github.com/hashicorp/terraform/addrs" - "github.com/hashicorp/terraform/dag" - "github.com/hashicorp/terraform/states" -) - -func TestMissingProvisionerTransformer(t *testing.T) { - mod := testModule(t, "transform-provisioner-basic") - - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &AttachResourceConfigTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &MissingProvisionerTransformer{Provisioners: []string{"shell"}} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &ProvisionerTransformer{} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformMissingProvisionerBasicStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -func TestMissingProvisionerTransformer_module(t *testing.T) { - mod := testModule(t, "transform-provisioner-module") - - g := Graph{Path: addrs.RootModuleInstance} - { - concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex { - return a - } - - state := states.BuildState(func(s *states.SyncState) { - s.SetResourceInstanceCurrent( - addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "aws_instance", - Name: "foo", - }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), - &states.ResourceInstanceObjectSrc{ - AttrsFlat: map[string]string{ - "id": "foo", - }, - Status: states.ObjectReady, - }, - addrs.AbsProviderConfig{ - Provider: addrs.NewDefaultProvider("aws"), - Module: addrs.RootModule, - }, - ) - s.SetResourceInstanceCurrent( - addrs.Resource{ - Mode: addrs.ManagedResourceMode, - Type: "aws_instance", - Name: "foo", - }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance.Child("child", addrs.NoKey)), - &states.ResourceInstanceObjectSrc{ - AttrsFlat: map[string]string{ - "id": "foo", - }, - Status: states.ObjectReady, - }, - addrs.AbsProviderConfig{ - Provider: addrs.NewDefaultProvider("aws"), - Module: addrs.RootModule, - }, - ) - }) - - tf := &StateTransformer{ - ConcreteCurrent: concreteResource, - State: state, - } - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - t.Logf("graph after StateTransformer:\n%s", g.StringWithNodeTypes()) - } - - { - transform := &AttachResourceConfigTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &MissingProvisionerTransformer{Provisioners: []string{"shell"}} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - t.Logf("graph after MissingProvisionerTransformer:\n%s", g.StringWithNodeTypes()) - } - - { - transform := &ProvisionerTransformer{} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - t.Logf("graph after ProvisionerTransformer:\n%s", g.StringWithNodeTypes()) - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformMissingProvisionerModuleStr) - if actual != expected { - t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) - } -} - -func TestCloseProvisionerTransformer(t *testing.T) { - mod := testModule(t, "transform-provisioner-basic") - - g := Graph{Path: addrs.RootModuleInstance} - { - tf := &ConfigTransformer{Config: mod} - if err := tf.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &AttachResourceConfigTransformer{Config: mod} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &MissingProvisionerTransformer{Provisioners: []string{"shell"}} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &ProvisionerTransformer{} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - { - transform := &CloseProvisionerTransformer{} - if err := transform.Transform(&g); err != nil { - t.Fatalf("err: %s", err) - } - } - - actual := strings.TrimSpace(g.String()) - expected := strings.TrimSpace(testTransformCloseProvisionerBasicStr) - if actual != expected { - t.Fatalf("bad:\n\n%s", actual) - } -} - -const testTransformMissingProvisionerBasicStr = ` -aws_instance.web - provisioner.shell -provisioner.shell -` - -const testTransformMissingProvisionerModuleStr = ` -aws_instance.foo - provisioner.shell -module.child.aws_instance.foo - provisioner.shell -provisioner.shell -` - -const testTransformCloseProvisionerBasicStr = ` -aws_instance.web - provisioner.shell -provisioner.shell -provisioner.shell (close) - aws_instance.web -` diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_reference.go b/vendor/github.com/hashicorp/terraform/terraform/transform_reference.go index 0b7d0e56..37937d05 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_reference.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_reference.go @@ -50,7 +50,7 @@ type graphNodeDependsOn interface { DependsOn() []*addrs.Reference } -// graphNodeAttachResourceDependencies records all resources that are transitively +// graphNodeAttachDataResourceDependsOn records all resources that are transitively // referenced through depends_on in the configuration. This is used by data // resources to determine if they can be read during the plan, or if they need // to be further delayed until apply. @@ -60,18 +60,18 @@ type graphNodeDependsOn interface { // unrelated module instances, the fact that it only happens when there are any // resource updates pending means we can still avoid the problem of the // "perpetual diff" -type graphNodeAttachResourceDependencies interface { +type graphNodeAttachDataResourceDependsOn interface { GraphNodeConfigResource graphNodeDependsOn - // AttachResourceDependencies stored the discovered dependencies in the + // AttachDataResourceDependsOn stores the discovered dependencies in the // resource node for evaluation later. // // The force parameter indicates that even if there are no dependencies, // force the data source to act as though there are for refresh purposes. // This is needed because yet-to-be-created resources won't be in the // initial refresh graph, but may still be referenced through depends_on. - AttachResourceDependencies(deps []addrs.ConfigResource, force bool) + AttachDataResourceDependsOn(deps []addrs.ConfigResource, force bool) } // GraphNodeReferenceOutside is an interface that can optionally be implemented. @@ -157,12 +157,12 @@ func (m depMap) add(v dag.Vertex) { } } -// attachDataResourceDependenciesTransformer records all resources transitively referenced -// through a configuration depends_on. -type attachDataResourceDependenciesTransformer struct { +// attachDataResourceDependsOnTransformer records all resources transitively +// referenced through a configuration depends_on. +type attachDataResourceDependsOnTransformer struct { } -func (t attachDataResourceDependenciesTransformer) Transform(g *Graph) error { +func (t attachDataResourceDependsOnTransformer) Transform(g *Graph) error { // First we need to make a map of referenceable addresses to their vertices. // This is very similar to what's done in ReferenceTransformer, but we keep // implementation separate as they may need to change independently. @@ -170,7 +170,7 @@ func (t attachDataResourceDependenciesTransformer) Transform(g *Graph) error { refMap := NewReferenceMap(vertices) for _, v := range vertices { - depender, ok := v.(graphNodeAttachResourceDependencies) + depender, ok := v.(graphNodeAttachDataResourceDependsOn) if !ok { continue } @@ -195,7 +195,7 @@ func (t attachDataResourceDependenciesTransformer) Transform(g *Graph) error { } log.Printf("[TRACE] attachDataDependenciesTransformer: %s depends on %s", depender.ResourceAddr(), res) - depender.AttachResourceDependencies(res, fromModule) + depender.AttachDataResourceDependsOn(res, fromModule) } return nil @@ -538,17 +538,3 @@ func ReferencesFromConfig(body hcl.Body, schema *configschema.Block) []*addrs.Re refs, _ := lang.ReferencesInBlock(body, schema) return refs } - -func modulePrefixStr(p addrs.ModuleInstance) string { - return p.String() -} - -func modulePrefixList(result []string, prefix string) []string { - if prefix != "" { - for i, v := range result { - result[i] = fmt.Sprintf("%s.%s", prefix, v) - } - } - - return result -} diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_reference_test.go b/vendor/github.com/hashicorp/terraform/terraform/transform_reference_test.go index 069645e9..e6bf1940 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_reference_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_reference_test.go @@ -68,12 +68,12 @@ func TestReferenceTransformer_path(t *testing.T) { }) g.Add(&graphNodeRefParentTest{ NameValue: "child.A", - PathValue: []string{"root", "child"}, + PathValue: addrs.ModuleInstance{addrs.ModuleInstanceStep{Name: "child"}}, Names: []string{"A"}, }) g.Add(&graphNodeRefChildTest{ NameValue: "child.B", - PathValue: []string{"root", "child"}, + PathValue: addrs.ModuleInstance{addrs.ModuleInstanceStep{Name: "child"}}, Refs: []string{"A"}, }) @@ -214,7 +214,7 @@ func TestReferenceMapReferences(t *testing.T) { type graphNodeRefParentTest struct { NameValue string - PathValue []string + PathValue addrs.ModuleInstance Names []string } @@ -233,16 +233,16 @@ func (n *graphNodeRefParentTest) ReferenceableAddrs() []addrs.Referenceable { } func (n *graphNodeRefParentTest) Path() addrs.ModuleInstance { - return normalizeModulePath(n.PathValue) + return n.PathValue } func (n *graphNodeRefParentTest) ModulePath() addrs.Module { - return normalizeModulePath(n.PathValue).Module() + return n.PathValue.Module() } type graphNodeRefChildTest struct { NameValue string - PathValue []string + PathValue addrs.ModuleInstance Refs []string } @@ -263,11 +263,11 @@ func (n *graphNodeRefChildTest) References() []*addrs.Reference { } func (n *graphNodeRefChildTest) Path() addrs.ModuleInstance { - return normalizeModulePath(n.PathValue) + return n.PathValue } func (n *graphNodeRefChildTest) ModulePath() addrs.Module { - return normalizeModulePath(n.PathValue).Module() + return n.PathValue.Module() } type graphNodeFakeResourceInstance struct { @@ -309,25 +309,6 @@ B A ` -const testTransformRefBackupStr = ` -A -B - A -` - -const testTransformRefBackupPrimaryStr = ` -A -B - C -C -` - -const testTransformRefModulePathStr = ` -A -B - A -` - const testTransformRefPathStr = ` A B diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_vertex.go b/vendor/github.com/hashicorp/terraform/terraform/transform_vertex.go index 6b1293fc..9620e6eb 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_vertex.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_vertex.go @@ -31,7 +31,7 @@ func (t *VertexTransformer) Transform(g *Graph) error { if ok := g.Replace(v, newV); !ok { // This should never happen, big problem return fmt.Errorf( - "Failed to replace %s with %s!\n\nSource: %#v\n\nTarget: %#v", + "failed to replace %s with %s!\n\nSource: %#v\n\nTarget: %#v", dag.VertexName(v), dag.VertexName(newV), v, newV) } diff --git a/vendor/github.com/hashicorp/terraform/terraform/update_state_hook.go b/vendor/github.com/hashicorp/terraform/terraform/update_state_hook.go new file mode 100644 index 00000000..c2ed76e8 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/update_state_hook.go @@ -0,0 +1,19 @@ +package terraform + +// updateStateHook calls the PostStateUpdate hook with the current state. +func updateStateHook(ctx EvalContext) error { + // In principle we could grab the lock here just long enough to take a + // deep copy and then pass that to our hooks below, but we'll instead + // hold the hook for the duration to avoid the potential confusing + // situation of us racing to call PostStateUpdate concurrently with + // different state snapshots. + stateSync := ctx.State() + state := stateSync.Lock().DeepCopy() + defer stateSync.Unlock() + + // Call the hook + err := ctx.Hook(func(h Hook) (HookAction, error) { + return h.PostStateUpdate(state) + }) + return err +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/update_state_hook_test.go b/vendor/github.com/hashicorp/terraform/terraform/update_state_hook_test.go new file mode 100644 index 00000000..71735627 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/update_state_hook_test.go @@ -0,0 +1,33 @@ +package terraform + +import ( + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/zclconf/go-cty/cty" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/states" +) + +func TestUpdateStateHook(t *testing.T) { + mockHook := new(MockHook) + + state := states.NewState() + state.Module(addrs.RootModuleInstance).SetLocalValue("foo", cty.StringVal("hello")) + + ctx := new(MockEvalContext) + ctx.HookHook = mockHook + ctx.StateState = state.SyncWrapper() + + if err := updateStateHook(ctx); err != nil { + t.Fatalf("err: %s", err) + } + + if !mockHook.PostStateUpdateCalled { + t.Fatal("should call PostStateUpdate") + } + if mockHook.PostStateUpdateState.LocalValue(addrs.LocalValue{Name: "foo"}.Absolute(addrs.RootModuleInstance)) != cty.StringVal("hello") { + t.Fatalf("wrong state passed to hook: %s", spew.Sdump(mockHook.PostStateUpdateState)) + } +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/upgrade_resource_state.go b/vendor/github.com/hashicorp/terraform/terraform/upgrade_resource_state.go new file mode 100644 index 00000000..924b79cc --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/upgrade_resource_state.go @@ -0,0 +1,203 @@ +package terraform + +import ( + "encoding/json" + "fmt" + "log" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/providers" + "github.com/hashicorp/terraform/states" + "github.com/hashicorp/terraform/tfdiags" + "github.com/zclconf/go-cty/cty" +) + +// upgradeResourceState will, if necessary, run the provider-defined upgrade +// logic against the given state object to make it compliant with the +// current schema version. This is a no-op if the given state object is +// already at the latest version. +// +// If any errors occur during upgrade, error diagnostics are returned. In that +// case it is not safe to proceed with using the original state object. +func upgradeResourceState(addr addrs.AbsResourceInstance, provider providers.Interface, src *states.ResourceInstanceObjectSrc, currentSchema *configschema.Block, currentVersion uint64) (*states.ResourceInstanceObjectSrc, tfdiags.Diagnostics) { + // Remove any attributes from state that are not present in the schema. + // This was previously taken care of by the provider, but data sources do + // not go through the UpgradeResourceState process. + // + // Legacy flatmap state is already taken care of during conversion. + // If the schema version is be changed, then allow the provider to handle + // removed attributes. + if len(src.AttrsJSON) > 0 && src.SchemaVersion == currentVersion { + src.AttrsJSON = stripRemovedStateAttributes(src.AttrsJSON, currentSchema.ImpliedType()) + } + + if addr.Resource.Resource.Mode != addrs.ManagedResourceMode { + // We only do state upgrading for managed resources. + return src, nil + } + + stateIsFlatmap := len(src.AttrsJSON) == 0 + + // TODO: This should eventually use a proper FQN. + providerType := addr.Resource.Resource.ImpliedProvider() + if src.SchemaVersion > currentVersion { + log.Printf("[TRACE] upgradeResourceState: can't downgrade state for %s from version %d to %d", addr, src.SchemaVersion, currentVersion) + var diags tfdiags.Diagnostics + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Resource instance managed by newer provider version", + // This is not a very good error message, but we don't retain enough + // information in state to give good feedback on what provider + // version might be required here. :( + fmt.Sprintf("The current state of %s was created by a newer provider version than is currently selected. Upgrade the %s provider to work with this state.", addr, providerType), + )) + return nil, diags + } + + // If we get down here then we need to upgrade the state, with the + // provider's help. + // If this state was originally created by a version of Terraform prior to + // v0.12, this also includes translating from legacy flatmap to new-style + // representation, since only the provider has enough information to + // understand a flatmap built against an older schema. + if src.SchemaVersion != currentVersion { + log.Printf("[TRACE] upgradeResourceState: upgrading state for %s from version %d to %d using provider %q", addr, src.SchemaVersion, currentVersion, providerType) + } else { + log.Printf("[TRACE] upgradeResourceState: schema version of %s is still %d; calling provider %q for any other minor fixups", addr, currentVersion, providerType) + } + + req := providers.UpgradeResourceStateRequest{ + TypeName: addr.Resource.Resource.Type, + + // TODO: The internal schema version representations are all using + // uint64 instead of int64, but unsigned integers aren't friendly + // to all protobuf target languages so in practice we use int64 + // on the wire. In future we will change all of our internal + // representations to int64 too. + Version: int64(src.SchemaVersion), + } + + if stateIsFlatmap { + req.RawStateFlatmap = src.AttrsFlat + } else { + req.RawStateJSON = src.AttrsJSON + } + + resp := provider.UpgradeResourceState(req) + diags := resp.Diagnostics + if diags.HasErrors() { + return nil, diags + } + + // After upgrading, the new value must conform to the current schema. When + // going over RPC this is actually already ensured by the + // marshaling/unmarshaling of the new value, but we'll check it here + // anyway for robustness, e.g. for in-process providers. + newValue := resp.UpgradedState + if errs := newValue.Type().TestConformance(currentSchema.ImpliedType()); len(errs) > 0 { + for _, err := range errs { + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Invalid resource state upgrade", + fmt.Sprintf("The %s provider upgraded the state for %s from a previous version, but produced an invalid result: %s.", providerType, addr, tfdiags.FormatError(err)), + )) + } + return nil, diags + } + + new, err := src.CompleteUpgrade(newValue, currentSchema.ImpliedType(), uint64(currentVersion)) + if err != nil { + // We already checked for type conformance above, so getting into this + // codepath should be rare and is probably a bug somewhere under CompleteUpgrade. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to encode result of resource state upgrade", + fmt.Sprintf("Failed to encode state for %s after resource schema upgrade: %s.", addr, tfdiags.FormatError(err)), + )) + } + return new, diags +} + +// stripRemovedStateAttributes deletes any attributes no longer present in the +// schema, so that the json can be correctly decoded. +func stripRemovedStateAttributes(state []byte, ty cty.Type) []byte { + jsonMap := map[string]interface{}{} + err := json.Unmarshal(state, &jsonMap) + if err != nil { + // we just log any errors here, and let the normal decode process catch + // invalid JSON. + log.Printf("[ERROR] UpgradeResourceState: %s", err) + return state + } + + // if no changes were made, we return the original state to ensure nothing + // was altered in the marshaling process. + if !removeRemovedAttrs(jsonMap, ty) { + return state + } + + js, err := json.Marshal(jsonMap) + if err != nil { + // if the json map was somehow mangled enough to not marhsal, something + // went horribly wrong + panic(err) + } + + return js +} + +// strip out the actual missing attributes, and return a bool indicating if any +// changes were made. +func removeRemovedAttrs(v interface{}, ty cty.Type) bool { + modified := false + // we're only concerned with finding maps that correspond to object + // attributes + switch v := v.(type) { + case []interface{}: + switch { + // If these aren't blocks the next call will be a noop + case ty.IsListType() || ty.IsSetType(): + eTy := ty.ElementType() + for _, eV := range v { + modified = removeRemovedAttrs(eV, eTy) || modified + } + } + return modified + case map[string]interface{}: + switch { + case ty.IsMapType(): + // map blocks aren't yet supported, but handle this just in case + eTy := ty.ElementType() + for _, eV := range v { + modified = removeRemovedAttrs(eV, eTy) || modified + } + return modified + + case ty == cty.DynamicPseudoType: + log.Printf("[DEBUG] UpgradeResourceState: ignoring dynamic block: %#v\n", v) + return false + + case ty.IsObjectType(): + attrTypes := ty.AttributeTypes() + for attr, attrV := range v { + attrTy, ok := attrTypes[attr] + if !ok { + log.Printf("[DEBUG] UpgradeResourceState: attribute %q no longer present in schema", attr) + delete(v, attr) + modified = true + continue + } + + modified = removeRemovedAttrs(attrV, attrTy) || modified + } + return modified + default: + // This shouldn't happen, and will fail to decode further on, so + // there's no need to handle it here. + log.Printf("[WARN] UpgradeResourceState: unexpected type %#v for map in json state", ty) + return false + } + } + return modified +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_state_upgrade_test.go b/vendor/github.com/hashicorp/terraform/terraform/upgrade_resource_state_test.go similarity index 100% rename from vendor/github.com/hashicorp/terraform/terraform/eval_state_upgrade_test.go rename to vendor/github.com/hashicorp/terraform/terraform/upgrade_resource_state_test.go diff --git a/vendor/github.com/hashicorp/terraform/terraform/validate_selfref.go b/vendor/github.com/hashicorp/terraform/terraform/validate_selfref.go new file mode 100644 index 00000000..d00b1975 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/validate_selfref.go @@ -0,0 +1,60 @@ +package terraform + +import ( + "fmt" + + "github.com/hashicorp/hcl/v2" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/configs/configschema" + "github.com/hashicorp/terraform/lang" + "github.com/hashicorp/terraform/tfdiags" +) + +// validateSelfRef checks to ensure that expressions within a particular +// referencable block do not reference that same block. +func validateSelfRef(addr addrs.Referenceable, config hcl.Body, providerSchema *ProviderSchema) tfdiags.Diagnostics { + var diags tfdiags.Diagnostics + + addrStrs := make([]string, 0, 1) + addrStrs = append(addrStrs, addr.String()) + switch tAddr := addr.(type) { + case addrs.ResourceInstance: + // A resource instance may not refer to its containing resource either. + addrStrs = append(addrStrs, tAddr.ContainingResource().String()) + } + + if providerSchema == nil { + diags = diags.Append(fmt.Errorf("provider schema unavailable while validating %s for self-references; this is a bug in Terraform and should be reported", addr)) + return diags + } + + var schema *configschema.Block + switch tAddr := addr.(type) { + case addrs.Resource: + schema, _ = providerSchema.SchemaForResourceAddr(tAddr) + case addrs.ResourceInstance: + schema, _ = providerSchema.SchemaForResourceAddr(tAddr.ContainingResource()) + } + + if schema == nil { + diags = diags.Append(fmt.Errorf("no schema available for %s to validate for self-references; this is a bug in Terraform and should be reported", addr)) + return diags + } + + refs, _ := lang.ReferencesInBlock(config, schema) + for _, ref := range refs { + for _, addrStr := range addrStrs { + if ref.Subject.String() == addrStr { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Self-referential block", + Detail: fmt.Sprintf("Configuration for %s may not refer to itself.", addrStr), + Subject: ref.SourceRange.ToHCL().Ptr(), + }) + } + } + } + + return diags +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/validate_selfref_test.go b/vendor/github.com/hashicorp/terraform/terraform/validate_selfref_test.go new file mode 100644 index 00000000..8359a947 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/validate_selfref_test.go @@ -0,0 +1,105 @@ +package terraform + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/configs/configschema" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hcltest" + "github.com/hashicorp/terraform/addrs" + "github.com/zclconf/go-cty/cty" +) + +func TestValidateSelfRef(t *testing.T) { + rAddr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "aws_instance", + Name: "foo", + } + + tests := []struct { + Name string + Addr addrs.Referenceable + Expr hcl.Expression + Err bool + }{ + { + "no references at all", + rAddr, + hcltest.MockExprLiteral(cty.StringVal("bar")), + false, + }, + + { + "non self reference", + rAddr, + hcltest.MockExprTraversalSrc("aws_instance.bar.id"), + false, + }, + + { + "self reference", + rAddr, + hcltest.MockExprTraversalSrc("aws_instance.foo.id"), + true, + }, + + { + "self reference other index", + rAddr, + hcltest.MockExprTraversalSrc("aws_instance.foo[4].id"), + false, + }, + + { + "self reference same index", + rAddr.Instance(addrs.IntKey(4)), + hcltest.MockExprTraversalSrc("aws_instance.foo[4].id"), + true, + }, + + { + "self reference whole", + rAddr.Instance(addrs.IntKey(4)), + hcltest.MockExprTraversalSrc("aws_instance.foo"), + true, + }, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("%d-%s", i, test.Name), func(t *testing.T) { + body := hcltest.MockBody(&hcl.BodyContent{ + Attributes: hcl.Attributes{ + "foo": { + Name: "foo", + Expr: test.Expr, + }, + }, + }) + + ps := &ProviderSchema{ + ResourceTypes: map[string]*configschema.Block{ + "aws_instance": &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "foo": { + Type: cty.String, + Required: true, + }, + }, + }, + }, + } + + diags := validateSelfRef(test.Addr, body, ps) + if diags.HasErrors() != test.Err { + if test.Err { + t.Errorf("unexpected success; want error") + } else { + t.Errorf("unexpected error\n\n%s", diags.Err()) + } + } + }) + } +} diff --git a/vendor/github.com/hashicorp/terraform/terraform/variables_test.go b/vendor/github.com/hashicorp/terraform/terraform/variables_test.go index 3ac112eb..320a6740 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/variables_test.go +++ b/vendor/github.com/hashicorp/terraform/terraform/variables_test.go @@ -30,21 +30,21 @@ func TestVariables(t *testing.T) { }, }, "b": &InputValue{ - Value: cty.ListValEmpty(cty.DynamicPseudoType), + Value: cty.ListValEmpty(cty.String), SourceType: ValueFromConfig, SourceRange: tfdiags.SourceRange{ Filename: "testdata/vars-basic/main.tf", - Start: tfdiags.SourcePos{Line: 6, Column: 1, Byte: 58}, - End: tfdiags.SourcePos{Line: 6, Column: 13, Byte: 70}, + Start: tfdiags.SourcePos{Line: 6, Column: 1, Byte: 55}, + End: tfdiags.SourcePos{Line: 6, Column: 13, Byte: 67}, }, }, "c": &InputValue{ - Value: cty.MapValEmpty(cty.DynamicPseudoType), + Value: cty.MapValEmpty(cty.String), SourceType: ValueFromConfig, SourceRange: tfdiags.SourceRange{ Filename: "testdata/vars-basic/main.tf", - Start: tfdiags.SourcePos{Line: 11, Column: 1, Byte: 111}, - End: tfdiags.SourcePos{Line: 11, Column: 13, Byte: 123}, + Start: tfdiags.SourcePos{Line: 11, Column: 1, Byte: 113}, + End: tfdiags.SourcePos{Line: 11, Column: 13, Byte: 125}, }, }, }, diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/contextual.go b/vendor/github.com/hashicorp/terraform/tfdiags/contextual.go index d55bc2f0..c59280f3 100644 --- a/vendor/github.com/hashicorp/terraform/tfdiags/contextual.go +++ b/vendor/github.com/hashicorp/terraform/tfdiags/contextual.go @@ -19,25 +19,28 @@ import ( // contextualFromConfig is an interface type implemented by diagnostic types // that can elaborate themselves when given information about the configuration -// body they are embedded in. +// body they are embedded in, as well as the runtime address associated with +// that configuration. // // Usually this entails extracting source location information in order to // populate the "Subject" range. type contextualFromConfigBody interface { - ElaborateFromConfigBody(hcl.Body) Diagnostic + ElaborateFromConfigBody(hcl.Body, string) Diagnostic } // InConfigBody returns a copy of the receiver with any config-contextual -// diagnostics elaborated in the context of the given body. -func (d Diagnostics) InConfigBody(body hcl.Body) Diagnostics { - if len(d) == 0 { +// diagnostics elaborated in the context of the given body. An optional address +// argument may be added to indicate which instance of the configuration the +// error related to. +func (diags Diagnostics) InConfigBody(body hcl.Body, addr string) Diagnostics { + if len(diags) == 0 { return nil } - ret := make(Diagnostics, len(d)) - for i, srcDiag := range d { + ret := make(Diagnostics, len(diags)) + for i, srcDiag := range diags { if cd, isCD := srcDiag.(contextualFromConfigBody); isCD { - ret[i] = cd.ElaborateFromConfigBody(body) + ret[i] = cd.ElaborateFromConfigBody(body, addr) } else { ret[i] = srcDiag } @@ -112,7 +115,12 @@ type attributeDiagnostic struct { // source location information is still available, for more accuracy. This // is not always possible due to system architecture, so this serves as a // "best effort" fallback behavior for such situations. -func (d *attributeDiagnostic) ElaborateFromConfigBody(body hcl.Body) Diagnostic { +func (d *attributeDiagnostic) ElaborateFromConfigBody(body hcl.Body, addr string) Diagnostic { + // don't change an existing address + if d.address == "" { + d.address = addr + } + if len(d.attrPath) < 1 { // Should never happen, but we'll allow it rather than crashing. return d @@ -353,7 +361,12 @@ type wholeBodyDiagnostic struct { subject *SourceRange // populated only after ElaborateFromConfigBody } -func (d *wholeBodyDiagnostic) ElaborateFromConfigBody(body hcl.Body) Diagnostic { +func (d *wholeBodyDiagnostic) ElaborateFromConfigBody(body hcl.Body, addr string) Diagnostic { + // don't change an existing address + if d.address == "" { + d.address = addr + } + if d.subject != nil { // Don't modify an already-elaborated diagnostic. return d diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/contextual_test.go b/vendor/github.com/hashicorp/terraform/tfdiags/contextual_test.go index 806d0c3a..e29712a7 100644 --- a/vendor/github.com/hashicorp/terraform/tfdiags/contextual_test.go +++ b/vendor/github.com/hashicorp/terraform/tfdiags/contextual_test.go @@ -185,6 +185,7 @@ simple_attr = "val" diagnosticBase: diagnosticBase{ summary: "preexisting", detail: "detail", + address: "original", }, subject: &SourceRange{ Filename: "somewhere_else.tf", @@ -535,9 +536,22 @@ simple_attr = "val" for i, tc := range testCases { t.Run(fmt.Sprintf("%d:%s", i, tc.Diag.Description()), func(t *testing.T) { var diags Diagnostics + + origAddr := tc.Diag.Description().Address diags = diags.Append(tc.Diag) - gotDiags := diags.InConfigBody(f.Body) + + gotDiags := diags.InConfigBody(f.Body, "test.addr") gotRange := gotDiags[0].Source().Subject + gotAddr := gotDiags[0].Description().Address + + switch { + case origAddr != "": + if gotAddr != origAddr { + t.Errorf("original diagnostic address modified from %s to %s", origAddr, gotAddr) + } + case gotAddr != "test.addr": + t.Error("missing detail address") + } for _, problem := range deep.Equal(gotRange, tc.ExpectedRange) { t.Error(problem) diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic.go b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic.go index a7699cf0..d84fa666 100644 --- a/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic.go +++ b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic.go @@ -25,6 +25,7 @@ const ( ) type Description struct { + Address string Summary string Detail string } diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic_base.go b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic_base.go index 50bf9d8e..04e56773 100644 --- a/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic_base.go +++ b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic_base.go @@ -9,6 +9,7 @@ type diagnosticBase struct { severity Severity summary string detail string + address string } func (d diagnosticBase) Severity() Severity { @@ -19,6 +20,7 @@ func (d diagnosticBase) Description() Description { return Description{ Summary: d.summary, Detail: d.detail, + Address: d.address, } } diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/hcl.go b/vendor/github.com/hashicorp/terraform/tfdiags/hcl.go index 37fb0d1a..66e3e425 100644 --- a/vendor/github.com/hashicorp/terraform/tfdiags/hcl.go +++ b/vendor/github.com/hashicorp/terraform/tfdiags/hcl.go @@ -98,12 +98,12 @@ func (r SourceRange) ToHCL() hcl.Range { // problem, but could produce an awkward result in some special cases such // as converting the result of ConsolidateWarnings, which will force the // resulting warning groups to be flattened early. -func (d Diagnostics) ToHCL() hcl.Diagnostics { - if len(d) == 0 { +func (diags Diagnostics) ToHCL() hcl.Diagnostics { + if len(diags) == 0 { return nil } - ret := make(hcl.Diagnostics, len(d)) - for i, diag := range d { + ret := make(hcl.Diagnostics, len(diags)) + for i, diag := range diags { severity := diag.Severity() desc := diag.Description() source := diag.Source() diff --git a/vendor/github.com/hashicorp/terraform/tools/terraform-bundle/CHANGELOG.md b/vendor/github.com/hashicorp/terraform/tools/terraform-bundle/CHANGELOG.md index cb3c0e7a..80d51204 100644 --- a/vendor/github.com/hashicorp/terraform/tools/terraform-bundle/CHANGELOG.md +++ b/vendor/github.com/hashicorp/terraform/tools/terraform-bundle/CHANGELOG.md @@ -8,4 +8,4 @@ BUG FIXES: > This is a list of changes relative to terraform-bundle tagged v0.12. Breaking Changes: -* Terraform v0.13.0 has introduced a new heirarchical namespace for providers. Terraform v0.13 requires a new directory layout in order to discover locally-installed provider plugins, and terraform-bundle has been updated to match. Please see the [README](README.md) to learn more about the new directory layout. +* Terraform v0.13.0 has introduced a new hierarchical namespace for providers. Terraform v0.13 requires a new directory layout in order to discover locally-installed provider plugins, and terraform-bundle has been updated to match. Please see the [README](README.md) to learn more about the new directory layout. diff --git a/vendor/github.com/hashicorp/terraform/tools/terraform-bundle/README.md b/vendor/github.com/hashicorp/terraform/tools/terraform-bundle/README.md index 916b33c1..dc387962 100644 --- a/vendor/github.com/hashicorp/terraform/tools/terraform-bundle/README.md +++ b/vendor/github.com/hashicorp/terraform/tools/terraform-bundle/README.md @@ -124,7 +124,7 @@ bundles contain the same core Terraform version. ## Custom Plugins To include custom plugins in the bundle file, create a local directory named -`./plugins` and put all the plugins you want to include there, under the +`./.plugins` and put all the plugins you want to include there, under the required [sub directory](#plugins-directory-layout). Optionally, you can use the `-plugin-dir` flag to specify a location where to find the plugins. To be recognized as a valid plugin, the file must have a name of the form @@ -135,10 +135,10 @@ Typically this will be `linux` and `amd64`. ### Plugins Directory Layout To include custom plugins in the bundle file, you must specify a "source" attribute in the configuration and place the plugin in the appropriate -subdirectory under `./plugins`. The directory must have the following layout: +subdirectory under `./.plugins`. The directory must have the following layout: ``` -./plugins/$SOURCEHOST/$SOURCENAMESPACE/$NAME/$VERSION/$OS_$ARCH/ +./.plugins/$SOURCEHOST/$SOURCENAMESPACE/$NAME/$VERSION/$OS_$ARCH/ ``` When installing custom plugins, you may choose any arbitrary identifier for the @@ -158,7 +158,7 @@ providers { The binary must be placed in the following directory: ``` -./plugins/example.com/myorg/customplugin/0.1/linux_amd64/ +./.plugins/example.com/myorg/customplugin/0.1/linux_amd64/ ``` ## Provider Resolution Behavior @@ -171,9 +171,7 @@ Therefore if automatic installation is not desired, it is important to ensure that version constraints within Terraform configurations do not exclude all of the versions available from the bundle. If a suitable version cannot be found in the bundle, Terraform _will_ attempt to satisfy that dependency by -automatic installation from the official repository. If you want -`terraform init` to explicitly fail instead of contacting the repository, pass -the `-get-plugins=false` option. +automatic installation from the official repository. For full details about provider resolution, see [How Terraform Works: Plugin Discovery](https://www.terraform.io/docs/extend/how-terraform-works.html#discovery). diff --git a/vendor/github.com/hashicorp/terraform/tools/terraform-bundle/config.go b/vendor/github.com/hashicorp/terraform/tools/terraform-bundle/config.go index 20dd61a3..268df65d 100644 --- a/vendor/github.com/hashicorp/terraform/tools/terraform-bundle/config.go +++ b/vendor/github.com/hashicorp/terraform/tools/terraform-bundle/config.go @@ -72,10 +72,14 @@ func (c *Config) validate() error { return fmt.Errorf("providers.%s: %s", k, diags.Err().Error()) } } - for _, c := range cs.Versions { - if _, err := getproviders.ParseVersionConstraints(c); err != nil { - return fmt.Errorf("providers.%s: %s", k, err) + if len(cs.Versions) > 0 { + for _, c := range cs.Versions { + if _, err := getproviders.ParseVersionConstraints(c); err != nil { + return fmt.Errorf("providers.%s: %s", k, err) + } } + } else { + return fmt.Errorf("provider.%s: required \"versions\" argument not found", k) } } diff --git a/vendor/github.com/hashicorp/terraform/tools/terraform-bundle/package.go b/vendor/github.com/hashicorp/terraform/tools/terraform-bundle/package.go index 21305996..35eb25bb 100644 --- a/vendor/github.com/hashicorp/terraform/tools/terraform-bundle/package.go +++ b/vendor/github.com/hashicorp/terraform/tools/terraform-bundle/package.go @@ -130,7 +130,7 @@ func (c *PackageCommand) Run(args []string) int { localSource := getproviders.NewFilesystemMirrorSource(absPluginDir) if available, err := localSource.AllAvailablePackages(); err == nil { for found := range available { - c.ui.Info(fmt.Sprintf("Found provider %q in %q. p", found.String(), pluginDir)) + c.ui.Info(fmt.Sprintf("Found provider %q in %q.", found.String(), pluginDir)) foundLocally[found] = struct{}{} } } diff --git a/vendor/github.com/hashicorp/terraform/version.go b/vendor/github.com/hashicorp/terraform/version.go index 36d16cb2..a5433164 100644 --- a/vendor/github.com/hashicorp/terraform/version.go +++ b/vendor/github.com/hashicorp/terraform/version.go @@ -4,9 +4,6 @@ import ( "github.com/hashicorp/terraform/version" ) -// The git commit that was compiled. This will be filled in by the compiler. -var GitCommit string - var Version = version.Version var VersionPrerelease = version.Prerelease diff --git a/vendor/github.com/hashicorp/terraform/version/version.go b/vendor/github.com/hashicorp/terraform/version/version.go index 8dd290be..cd586549 100644 --- a/vendor/github.com/hashicorp/terraform/version/version.go +++ b/vendor/github.com/hashicorp/terraform/version/version.go @@ -11,7 +11,7 @@ import ( ) // The main version number that is being run at the moment. -var Version = "0.14.0" +var Version = "0.15.0" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/config.html.md b/vendor/github.com/hashicorp/terraform/website/docs/backends/config.html.md deleted file mode 100644 index 4c7f0ea2..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/config.html.md +++ /dev/null @@ -1,147 +0,0 @@ ---- -layout: "docs" -page_title: "Backends: Configuration" -sidebar_current: "docs-backends-config" -description: |- - Backends are configured directly in Terraform files in the `terraform` section. ---- - -# Backend Configuration - -Backends are configured directly in Terraform files in the `terraform` -section. After configuring a backend, it has to be -[initialized](/docs/backends/init.html). - -Below, we show a complete example configuring the "consul" backend: - -```hcl -terraform { - backend "consul" { - address = "demo.consul.io" - scheme = "https" - path = "example_app/terraform_state" - } -} -``` - -You specify the backend type as a key to the `backend` stanza. Within the -stanza are backend-specific configuration keys. The list of supported backends -and their configuration is in the sidebar to the left. - -Only one backend may be specified and the configuration **may not contain -interpolations**. Terraform will validate this. - -## First Time Configuration - -When configuring a backend for the first time (moving from no defined backend -to explicitly configuring one), Terraform will give you the option to migrate -your state to the new backend. This lets you adopt backends without losing -any existing state. - -To be extra careful, we always recommend manually backing up your state -as well. You can do this by simply copying your `terraform.tfstate` file -to another location. The initialization process should create a backup -as well, but it never hurts to be safe! - -Configuring a backend for the first time is no different than changing -a configuration in the future: create the new configuration and run -`terraform init`. Terraform will guide you the rest of the way. - -## Partial Configuration - -You do not need to specify every required argument in the backend configuration. -Omitting certain arguments may be desirable to avoid storing secrets, such as -access keys, within the main configuration. When some or all of the arguments -are omitted, we call this a _partial configuration_. - -With a partial configuration, the remaining configuration arguments must be -provided as part of -[the initialization process](/docs/backends/init.html#backend-initialization). -There are several ways to supply the remaining arguments: - - * **Interactively**: Terraform will interactively ask you for the required - values, unless interactive input is disabled. Terraform will not prompt for - optional values. - - * **File**: A [backend configuration file](#backend-configuration-file) may be specified via the - `init` command line. To specify a file, use the `-backend-config=PATH` - option when running `terraform init`. If the file contains secrets it may be - kept in a secure data store, such as [Vault](https://www.vaultproject.io/), - in which case it must be downloaded to the local disk before running - Terraform. - - * **Command-line key/value pairs**: Key/value pairs can be specified via the - `init` command line. Note that many shells retain command-line flags in a - history file, so this isn't recommended for secrets. To specify a single - key/value pair, use the `-backend-config="KEY=VALUE"` option when running - `terraform init`. - -If backend settings are provided in multiple locations, the top-level -settings are merged such that any command-line options override the settings -in the main configuration and then the command-line options are processed -in order, with later options overriding values set by earlier options. - -The final, merged configuration is stored on disk in the `.terraform` -directory, which should be ignored from version control. This means that -sensitive information can be omitted from version control, but it will be -present in plain text on local disk when running Terraform. - -When using partial configuration, Terraform requires at a minimum that -an empty backend configuration is specified in one of the root Terraform -configuration files, to specify the backend type. For example: - -```hcl -terraform { - backend "consul" {} -} -``` - -## Backend Configuration File -A backend configuration file has the contents of the `backend` block as -top-level attributes, without the need to wrap it in another `terraform` -or `backend` block: - -```hcl -address = "demo.consul.io" -path = "example_app/terraform_state" -scheme = "https" -``` - -The same settings can alternatively be specified on the command line as -follows: - -``` -$ terraform init \ - -backend-config="address=demo.consul.io" \ - -backend-config="path=example_app/terraform_state" \ - -backend-config="scheme=https" -``` - -## Changing Configuration - -You can change your backend configuration at any time. You can change -both the configuration itself as well as the type of backend (for example -from "consul" to "s3"). - -Terraform will automatically detect any changes in your configuration -and request a [reinitialization](/docs/backends/init.html). As part of -the reinitialization process, Terraform will ask if you'd like to migrate -your existing state to the new configuration. This allows you to easily -switch from one backend to another. - -If you're using multiple [workspaces](/docs/state/workspaces.html), -Terraform can copy all workspaces to the destination. If Terraform detects -you have multiple workspaces, it will ask if this is what you want to do. - -If you're just reconfiguring the same backend, Terraform will still ask if you -want to migrate your state. You can respond "no" in this scenario. - -## Unconfiguring a Backend - -If you no longer want to use any backend, you can simply remove the -configuration from the file. Terraform will detect this like any other -change and prompt you to [reinitialize](/docs/backends/init.html). - -As part of the reinitialization, Terraform will ask if you'd like to migrate -your state back down to normal local state. Once this is complete then -Terraform is back to behaving as it does by default. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/backends/index.html.md deleted file mode 100644 index f8fb843c..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/index.html.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -layout: "docs" -page_title: "Backends" -sidebar_current: "docs-backends-index" -description: |- - A "backend" in Terraform determines how state is loaded and how an operation such as `apply` is executed. This abstraction enables non-local file state storage, remote execution, etc. ---- - -# Backends - -A "backend" in Terraform determines how state is loaded and how an operation -such as `apply` is executed. This abstraction enables non-local file state -storage, remote execution, etc. - -By default, Terraform uses the "local" backend, which is the normal behavior -of Terraform you're used to. This is the backend that was being invoked -throughout the [introduction](/intro/index.html). - -Here are some of the benefits of backends: - - * **Working in a team**: Backends can store their state remotely and - protect that state with locks to prevent corruption. Some backends - such as Terraform Cloud even automatically store a history of - all state revisions. - - * **Keeping sensitive information off disk**: State is retrieved from - backends on demand and only stored in memory. If you're using a backend - such as Amazon S3, the only location the state ever is persisted is in - S3. - - * **Remote operations**: For larger infrastructures or certain changes, - `terraform apply` can take a long, long time. Some backends support - remote operations which enable the operation to execute remotely. You can - then turn off your computer and your operation will still complete. Paired - with remote state storage and locking above, this also helps in team - environments. - -**Backends are completely optional**. You can successfully use Terraform without -ever having to learn or use backends. However, they do solve pain points that -afflict teams at a certain scale. If you're an individual, you can likely -get away with never using backends. - -Even if you only intend to use the "local" backend, it may be useful to -learn about backends since you can also change the behavior of the local -backend. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/init.html.md b/vendor/github.com/hashicorp/terraform/website/docs/backends/init.html.md deleted file mode 100644 index cbba0eb7..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/init.html.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -layout: "docs" -page_title: "Backends: Init" -sidebar_current: "docs-backends-init" -description: |- - Terraform must initialize any configured backend before use. This can be done by simply running `terraform init`. ---- - -# Backend Initialization - -Terraform must initialize any configured backend before use. This can be -done by simply running `terraform init`. - -The `terraform init` command should be run by any member of your team on -any Terraform configuration as a first step. It is safe to execute multiple -times and performs all the setup actions required for a Terraform environment, -including initializing the backend. - -The `init` command must be called: - - * On any new environment that configures a backend - * On any change of the backend configuration (including type of backend) - * On removing backend configuration completely - -You don't need to remember these exact cases. Terraform will detect when -initialization is required and error in that situation. Terraform doesn't -auto-initialize because it may require additional information from the user, -perform state migrations, etc. - -The `init` command will do more than just initialize the backend. Please see -the [init documentation](/docs/commands/init.html) for more information. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/legacy-0-8.html.md b/vendor/github.com/hashicorp/terraform/website/docs/backends/legacy-0-8.html.md deleted file mode 100644 index a5feee40..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/legacy-0-8.html.md +++ /dev/null @@ -1,135 +0,0 @@ ---- -layout: "docs" -page_title: "Backends: Migrating From 0.8.x and Earlier" -sidebar_current: "docs-backends-migrate" -description: |- - A "backend" in Terraform determines how state is loaded and how an operation such as `apply` is executed. This abstraction enables non-local file state storage, remote execution, etc. ---- - -# Backend & Legacy Remote State - -Prior to Terraform 0.9.0 backends didn't exist and remote state management -was done in a completely different way. This page documents how you can -migrate to the new backend system and any considerations along the way. - -Migrating to the new backends system is extremely simple. The only complex -case is if you had automation around configuring remote state. An existing -environment can be configured to use the new backend system after just -a few minutes of reading. - -For the remainder of this document, the remote state system prior to -Terraform 0.9.0 will be called "legacy remote state." - --> **Note:** This page is targeted at users who used remote state prior -to version 0.9.0 and need to upgrade their environments. If you didn't -use remote state, you can ignore this document. - -## Backwards Compatibility - -In version 0.9.0, Terraform knows how to load and continue working with -legacy remote state. A warning is shown guiding you to this page, but -otherwise everything continues to work without changing any configuration. - -Backwards compatibility with legacy remote state environments will be -removed in Terraform 0.11.0, or two major releases after 0.10.0. Starting -in 0.10.0, detection will remain but users will be _required_ to update -their configurations to use backends. In Terraform 0.11.0, detection and -loading will be completely removed. - -For the short term, you may continue using Terraform with version 0.9.0 -as you have been. However, you should begin planning to update your configuration -very soon. As you'll see, this process is very easy. - -## Migrating to Backends - -You should begin by reading the [complete backend documentation](/docs/backends) -section. This section covers in detail how you use and configure backends. - -Next, perform the following steps to migrate. These steps will also guide -you through backing up your existing remote state just in case things don't -go as planned. - -1. **With the older Terraform version (version 0.8.x),** run `terraform remote pull`. This -will cache the latest legacy remote state data locally. We'll use this for -a backup in case things go wrong. - -1. Backup your `.terraform/terraform.tfstate` file. This contains the -cache we just pulled. Please copy this file to a location outside of your -Terraform module. - -1. [Configure your backend](/docs/backends/config.html) in your Terraform -configuration. The backend type is the same backend type as you used with -your legacy remote state. The configuration should be setup to match the -same configuration you used with remote state. - -1. [Run the init command](/docs/backends/init.html). This is an interactive -process that will guide you through migrating your existing remote state -to the new backend system. During this step, Terraform may ask if you want -to copy your old remote state into the newly configured backend. If you -configured the identical backend location, you may say no since it should -already be there. - -1. Verify your state looks good by running `terraform plan` and seeing if -it detects your infrastructure. Advanced users may run `terraform state pull` -which will output the raw contents of your state file to your console. You -can compare this with the file you saved. There may be slight differences in -the serial number and version data, but the raw data should be almost identical. - -After the above steps, you're good to go! Everyone who uses the same -Terraform state should copy the same steps above. The only difference is they -may be able to skip the configuration step if you're sharing the configuration. - -At this point, **older Terraform versions will stop working.** Terraform -will prevent itself from working with state written with a higher version -of Terraform. This means that even other users using an older version of -Terraform with the same configured remote state location will no longer -be able to work with the environment. Everyone must upgrade. - -## Rolling Back - -If the migration fails for any reason: your states look different, your -plan isn't what you expect, you're getting errors, etc. then you may roll back. - -After rolling back, please [report an issue](https://github.com/hashicorp/terraform) -so that we may resolve anything that may have gone wrong for you. - -To roll back, follow the steps below using Terraform 0.8.x or earlier: - -1. Remove the backend configuration from your Terraform configuration file. - -2. Remove any "terraform.tfstate" files (including backups). If you believe -these may contain important data, you may back them up. Going with the assumption -that you started this migration guide with working remote state, these files -shouldn't contain anything of value. - -3. Copy the `.terraform/terraform.tfstate` file you backed up back into -the same location. - -And you're rolled back. If your backend migration worked properly and was -able to update your remote state, **then this will not work**. Terraform -prevents writing state that was written with a higher Terraform version -or a later serial number. - -**If you're absolutely certain you want to restore your state backup**, -then you can use `terraform remote push -force`. This is extremely dangerous -and you will lose any changes that were in the remote location. - -## Configuration Automation - -The `terraform remote config` command has been replaced with -`terraform init`. The new command is better in many ways by allowing file-based -configuration, automatic state migration, and more. - -You should be able to very easily migrate `terraform remote config` -scripting to the new `terraform init` command. - -The new `terraform init` command takes a `-backend-config` flag which is -either an HCL file or a string in the format of `key=value`. This configuration -is merged with the backend configuration in your Terraform files. -This lets you keep secrets out of your actual configuration. -We call this "partial configuration" and you can learn more in the -docs on [configuring backends](/docs/backends/config.html). - -This does introduce an extra step: your automation must generate a -JSON file (presumably JSON is easier to generate from a script than HCL -and HCL is compatible) to pass into `-backend-config`. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/operations.html.md b/vendor/github.com/hashicorp/terraform/website/docs/backends/operations.html.md deleted file mode 100644 index 0272c856..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/operations.html.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -layout: "docs" -page_title: "Backends: Remote Operations (plan, apply, etc.)" -sidebar_current: "docs-backends-operations" -description: |- - Some backends support the ability to run operations (`refresh`, `plan`, `apply`, etc.) remotely. Terraform will continue to look and behave as if they're running locally while they in fact run on a remote machine. ---- - -# Remote Operations (plan, apply, etc.) - -Most backends run all operations on the local system — although Terraform stores -its state remotely with these backends, it still executes its logic locally and -makes API requests directly from the system where it was invoked. - -This is simple to understand and work with, but when many people are -collaborating on the same Terraform configurations, it requires everyone's -execution environment to be similar. This includes sharing access to -infrastructure provider credentials, keeping Terraform versions in sync, -keeping Terraform variables in sync, and installing any extra software required -by Terraform providers. This becomes more burdensome as teams get larger. - -Some backends can run operations (`plan`, `apply`, etc.) on a remote machine, -while appearing to execute locally. This enables a more consistent execution -environment and more powerful access controls, without disrupting workflows -for users who are already comfortable with running Terraform. - -Currently, [the `remote` backend](./types/remote.html) is the only backend to -support remote operations, and [Terraform Cloud](/docs/cloud/index.html) -is the only remote execution environment that supports it. For more information, see: - -- [The `remote` backend](./types/remote.html) -- [Terraform Cloud's CLI-driven run workflow](/docs/cloud/run/cli.html) diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/state.html.md b/vendor/github.com/hashicorp/terraform/website/docs/backends/state.html.md deleted file mode 100644 index 93636720..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/state.html.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -layout: "docs" -page_title: "Backends: State Storage and Locking" -sidebar_current: "docs-backends-state" -description: |- - Backends are configured directly in Terraform files in the `terraform` section. ---- - -# State Storage and Locking - -Backends are responsible for storing state and providing an API for -[state locking](/docs/state/locking.html). State locking is optional. - -Despite the state being stored remotely, all Terraform commands such -as `terraform console`, the `terraform state` operations, `terraform taint`, -and more will continue to work as if the state was local. - -## State Storage - -Backends determine where state is stored. For example, the local (default) -backend stores state in a local JSON file on disk. The Consul backend stores -the state within Consul. Both of these backends happen to provide locking: -local via system APIs and Consul via locking APIs. - -When using a non-local backend, Terraform will not persist the state anywhere -on disk except in the case of a non-recoverable error where writing the state -to the backend failed. This behavior is a major benefit for backends: if -sensitive values are in your state, using a remote backend allows you to use -Terraform without that state ever being persisted to disk. - -In the case of an error persisting the state to the backend, Terraform will -write the state locally. This is to prevent data loss. If this happens the -end user must manually push the state to the remote backend once the error -is resolved. - -## Manual State Pull/Push - -You can still manually retrieve the state from the remote state using -the `terraform state pull` command. This will load your remote state and -output it to stdout. You can choose to save that to a file or perform any -other operations. - -You can also manually write state with `terraform state push`. **This -is extremely dangerous and should be avoided if possible.** This will -overwrite the remote state. This can be used to do manual fixups if necessary. - -When manually pushing state, Terraform will attempt to protect you from -some potentially dangerous situations: - - * **Differing lineage**: The "lineage" is a unique ID assigned to a state - when it is created. If a lineage is different, then it means the states - were created at different times and its very likely you're modifying a - different state. Terraform will not allow this. - - * **Higher serial**: Every state has a monotonically increasing "serial" - number. If the destination state has a higher serial, Terraform will - not allow you to write it since it means that changes have occurred since - the state you're attempting to write. - -Both of these protections can be bypassed with the `-force` flag if you're -confident you're making the right decision. Even if using the `-force` flag, -we recommend making a backup of the state with `terraform state pull` -prior to forcing the overwrite. - -## State Locking - -Backends are responsible for supporting [state locking](/docs/state/locking.html) -if possible. Not all backend types support state locking. In the -[list of supported backend types](/docs/backends/types) we explicitly note -whether locking is supported. - -For more information on state locking, view the -[page dedicated to state locking](/docs/state/locking.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/gcs.html.md b/vendor/github.com/hashicorp/terraform/website/docs/backends/types/gcs.html.md deleted file mode 100644 index 62ee24b4..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/gcs.html.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -layout: "backend-types" -page_title: "Backend Type: gcs" -sidebar_current: "docs-backends-types-standard-gcs" -description: |- - Terraform can store the state remotely, making it easier to version and work with in a team. ---- - -# gcs - -**Kind: Standard (with locking)** - -Stores the state as an object in a configurable prefix in a given bucket on [Google Cloud Storage](https://cloud.google.com/storage/) (GCS). -This backend also supports [state locking](/docs/state/locking.html). - -~> **Warning!** It is highly recommended that you enable -[Object Versioning](https://cloud.google.com/storage/docs/object-versioning) -on the GCS bucket to allow for state recovery in the case of accidental deletions and human error. - -## Example Configuration - -```hcl -terraform { - backend "gcs" { - bucket = "tf-state-prod" - prefix = "terraform/state" - } -} -``` - -## Data Source Configuration - -```hcl -data "terraform_remote_state" "foo" { - backend = "gcs" - config = { - bucket = "terraform-state" - prefix = "prod" - } -} - -resource "template_file" "bar" { - template = "${greeting}" - - vars { - greeting = "${data.terraform_remote_state.foo.greeting}" - } -} -``` - -## Configuration variables - -The following configuration options are supported: - - * `bucket` - (Required) The name of the GCS bucket. This name must be - globally unique. For more information, see [Bucket Naming - Guidelines](https://cloud.google.com/storage/docs/bucketnaming.html#requirements). - * `credentials` / `GOOGLE_BACKEND_CREDENTIALS` / `GOOGLE_CREDENTIALS` - - (Optional) Local path to Google Cloud Platform account credentials in JSON - format. If unset, [Google Application Default - Credentials](https://developers.google.com/identity/protocols/application-default-credentials) - are used. The provided credentials need to have the - `devstorage.read_write` scope and `WRITER` permissions on the bucket. - **Warning**: if using the Google Cloud Platform provider as well, it will - also pick up the `GOOGLE_CREDENTIALS` environment variable. - * `access_token` - (Optional) A temporary [OAuth 2.0 access token] obtained - from the Google Authorization server, i.e. the `Authorization: Bearer` token - used to authenticate HTTP requests to GCP APIs. This is an alternative to - `credentials`. If both are specified, `access_token` will be used over the - `credentials` field. - * `prefix` - (Optional) GCS prefix inside the bucket. Named states for - workspaces are stored in an object called `/.tfstate`. - * `path` - (Deprecated) GCS path to the state file of the default state. For - backwards compatibility only, use `prefix` instead. - * `encryption_key` / `GOOGLE_ENCRYPTION_KEY` - (Optional) A 32 byte base64 - encoded 'customer supplied encryption key' used to encrypt all state. For - more information see [Customer Supplied Encryption - Keys](https://cloud.google.com/storage/docs/encryption#customer-supplied). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/backends/types/index.html.md deleted file mode 100644 index 4034a0ae..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/index.html.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -layout: "backend-types" -page_title: "Backend: Supported Backend Types" -sidebar_current: "docs-backends-types-index" -description: |- - Terraform can store the state remotely, making it easier to version and work with in a team. ---- - -# Backend Types - -This section documents the various backend types supported by Terraform. -If you're not familiar with backends, please -[read the sections about backends](/docs/backends/index.html) first. - -Backends may support differing levels of features in Terraform. We differentiate -these by calling a backend either **standard** or **enhanced**. All backends -must implement **standard** functionality. These are defined below: - - * **Standard**: State management, functionality covered in - [State Storage & Locking](/docs/backends/state.html) - - * **Enhanced**: Everything in standard plus - [remote operations](/docs/backends/operations.html). - -The backends are separated in the left by standard and enhanced. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/local.html.md b/vendor/github.com/hashicorp/terraform/website/docs/backends/types/local.html.md deleted file mode 100644 index b8a690cb..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/local.html.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -layout: "backend-types" -page_title: "Backend Type: local" -sidebar_current: "docs-backends-types-enhanced-local" -description: |- - Terraform can store the state remotely, making it easier to version and work with in a team. ---- - -# local - -**Kind: Enhanced** - -The local backend stores state on the local filesystem, locks that -state using system APIs, and performs operations locally. - -## Example Configuration - -```hcl -terraform { - backend "local" { - path = "relative/path/to/terraform.tfstate" - } -} -``` - -## Data Source Configuration - -```hcl -data "terraform_remote_state" "foo" { - backend = "local" - - config = { - path = "${path.module}/../../terraform.tfstate" - } -} -``` - -## Configuration variables - -The following configuration options are supported: - - * `path` - (Optional) The path to the `tfstate` file. This defaults to - "terraform.tfstate" relative to the root module by default. - * `workspace_dir` - (Optional) The path to non-default workspaces. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/remote.html.md b/vendor/github.com/hashicorp/terraform/website/docs/backends/types/remote.html.md deleted file mode 100644 index 1af0313c..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/remote.html.md +++ /dev/null @@ -1,218 +0,0 @@ ---- -layout: "backend-types" -page_title: "Backend Type: remote" -sidebar_current: "docs-backends-types-enhanced-remote" -description: |- - Terraform can store the state and run operations remotely, making it easier to version and work with in a team. ---- - -# remote - -**Kind: Enhanced** - --> **Note:** We recommend using Terraform v0.11.13 or newer with this -backend. This backend requires either a Terraform Cloud account on -[app.terraform.io](https://app.terraform.io) or a Terraform Enterprise instance -(version v201809-1 or newer). - -The remote backend stores Terraform state and may be used to run operations in Terraform Cloud. - -When using full remote operations, operations like `terraform plan` or `terraform apply` can be executed in Terraform -Cloud's run environment, with log output streaming to the local terminal. Remote plans and applies use variable values from the associated Terraform Cloud workspace. - -Terraform Cloud can also be used with local operations, in which case only state is stored in the Terraform Cloud backend. - -## Command Support - -Currently the remote backend supports the following Terraform commands: - -- `apply` -- `console` (supported in Terraform >= v0.11.12) -- `destroy` (requires manually setting `CONFIRM_DESTROY=1` on the workspace) -- `fmt` -- `get` -- `graph` (supported in Terraform >= v0.11.12) -- `import` (supported in Terraform >= v0.11.12) -- `init` -- `output` -- `plan` -- `providers` -- `show` -- `state` (supports all sub-commands: list, mv, pull, push, rm, show) -- `taint` -- `untaint` -- `validate` -- `version` -- `workspace` - -## Workspaces - -The remote backend can work with either a single remote Terraform Cloud workspace, -or with multiple similarly-named remote workspaces (like `networking-dev` -and `networking-prod`). The `workspaces` block of the backend configuration -determines which mode it uses: - -- To use a single remote Terraform Cloud workspace, set `workspaces.name` to the - remote workspace's full name (like `networking`). - -- To use multiple remote workspaces, set `workspaces.prefix` to a prefix used in - all of the desired remote workspace names. For example, set - `prefix = "networking-"` to use Terraform cloud workspaces with - names like `networking-dev` and `networking-prod`. This is helpful when - mapping multiple Terraform CLI [workspaces](../../state/workspaces.html) - used in a single Terraform configuration to multiple Terraform Cloud - workspaces. - -When interacting with workspaces on the command line, Terraform uses -shortened names without the common prefix. For example, if -`prefix = "networking-"`, use `terraform workspace select prod` to switch to -the Terraform CLI workspace `prod` within the current configuration. Remote -Terraform operations such as `plan` and `apply` executed against that Terraform -CLI workspace will be executed in the Terraform Cloud workspace `networking-prod`. - -Additionally, the [`${terraform.workspace}`](../../state/workspaces.html#current-workspace-interpolation) -interpolation sequence should be removed from Terraform configurations that run -remote operations against Terraform Cloud workspaces. The reason for this is that -each Terraform Cloud workspace currently only uses the single `default` Terraform -CLI workspace internally. In other words, if your Terraform configuration -used `${terraform.workspace}` to return `dev` or `prod`, remote runs in Terraform Cloud -would always evaluate it as `default` regardless of -which workspace you had set with the `terraform workspace select` command. That -would most likely not be what you wanted. (It is ok to use `${terraform.workspace}` -in local operations.) - -The backend configuration requires either `name` or `prefix`. Omitting both or -setting both results in a configuration error. - -If previous state is present when you run `terraform init` and the corresponding -remote workspaces are empty or absent, Terraform will create workspaces and/or -update the remote state accordingly. However, if your workspace needs variables -set or requires a specific version of Terraform for remote operations, we -recommend that you create your remote workspaces on Terraform Cloud before -running any remote operations against them. - -## Example Configurations - --> **Note:** We recommend omitting the token from the configuration, and instead using - [`terraform login`](/docs/commands/login.html) or manually configuring - `credentials` in the [CLI config file](/docs/commands/cli-config.html#credentials). - -### Basic Configuration - -```hcl -# Using a single workspace: -terraform { - backend "remote" { - hostname = "app.terraform.io" - organization = "company" - - workspaces { - name = "my-app-prod" - } - } -} - -# Using multiple workspaces: -terraform { - backend "remote" { - hostname = "app.terraform.io" - organization = "company" - - workspaces { - prefix = "my-app-" - } - } -} -``` - -### Using CLI Input - -```hcl -# main.tf -terraform { - required_version = "~> 0.12.0" - - backend "remote" {} -} -``` - -Backend configuration file: - -```hcl -# backend.hcl -workspaces { name = "workspace" } -hostname = "app.terraform.io" -organization = "company" -``` - -Running `terraform init` with the backend file: - -```sh -terraform init -backend-config=backend.hcl -``` - -### Data Source Configuration - -```hcl -data "terraform_remote_state" "foo" { - backend = "remote" - - config = { - organization = "company" - - workspaces = { - name = "workspace" - } - } -} -``` - -## Configuration variables - -The following configuration options are supported: - -* `hostname` - (Optional) The remote backend hostname to connect to. Defaults - to app.terraform.io. -* `organization` - (Required) The name of the organization containing the - targeted workspace(s). -* `token` - (Optional) The token used to authenticate with the remote backend. - We recommend omitting the token from the configuration, and instead using - [`terraform login`](/docs/commands/login.html) or manually configuring - `credentials` in the - [CLI config file](/docs/commands/cli-config.html#credentials). -* `workspaces` - (Required) A block specifying which remote workspace(s) to use. - The `workspaces` block supports the following keys: - - * `name` - (Optional) The full name of one remote workspace. When configured, - only the default workspace can be used. This option conflicts with `prefix`. - * `prefix` - (Optional) A prefix used in the names of one or more remote - workspaces, all of which can be used with this configuration. The full - workspace names are used in Terraform Cloud, and the short names - (minus the prefix) are used on the command line for Terraform CLI workspaces. - If omitted, only the default workspace can be used. This option conflicts with `name`. - --> **Note:** You must use the `name` key when configuring a `terraform_remote_state` -data source that retrieves state from another Terraform Cloud workspace. The `prefix` key is only -intended for use when configuring an instance of the remote backend. - -## Excluding Files from Upload with .terraformignore - --> **Version note:** `.terraformignore` support was added in Terraform 0.12.11. - -When executing a remote `plan` or `apply` in a [CLI-driven run](/docs/cloud/run/cli.html), -an archive of your configuration directory is uploaded to Terraform Cloud. You can define -paths to ignore from upload via a `.terraformignore` file at the root of your configuration directory. If this file is not present, the archive will exclude the following by default: - -* .git/ directories -* .terraform/ directories (exclusive of .terraform/modules) - -The `.terraformignore` file can include rules as one would include in a -[.gitignore file](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository#Ignoring-Files) - - -* Comments (starting with `#`) or blank lines are ignored -* End a pattern with a forward slash / to specify a directory -* Negate a pattern by starting it with an exclamation point `!` - -Note that unlike `.gitignore`, only the `.terraformignore` at the root of the configuration -directory is considered. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/terraform-enterprise.html.md b/vendor/github.com/hashicorp/terraform/website/docs/backends/types/terraform-enterprise.html.md deleted file mode 100644 index b4b9ee36..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/terraform-enterprise.html.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -layout: "backend-types" -page_title: "Backend Type: terraform enterprise" -sidebar_current: "docs-backends-types-standard-terraform-enterprise" -description: |- - Terraform can store its state in Terraform Enterprise ---- - -# terraform enterprise - -!> **The `atlas` backend is deprecated.** Please use the new enhanced -[remote](/docs/backends/types/remote.html) backend for storing state and running -remote operations in Terraform Cloud. - -**Kind: Standard (with no locking)** - -Reads and writes state from a [Terraform Enterprise](/docs/cloud/index.html) -workspace. - --> **Why is this called "atlas"?** Before it was a standalone offering, -Terraform Enterprise was part of an integrated suite of enterprise products -called Atlas. This backend predates the current version Terraform Enterprise, so -it uses the old name. - -We no longer recommend using this backend, as it does not support collaboration -features like [workspace -locking](/docs/cloud/run/index.html). Please use the new enhanced -[remote](/docs/backends/types/remote.html) backend for storing state and running -remote operations in Terraform Cloud. - -## Example Configuration - -```hcl -terraform { - backend "atlas" { - name = "example_corp/networking-prod" - address = "https://app.terraform.io" # optional - } -} -``` - -We recommend using a [partial configuration](/docs/backends/config.html) and -omitting the access token, which can be provided as an environment variable. - -## Data Source Configuration - -```hcl -data "terraform_remote_state" "foo" { - backend = "atlas" - config = { - name = "example_corp/networking-prod" - } -} -``` - -## Configuration variables - -The following configuration options / environment variables are supported: - -* `name` - (Required) Full name of the workspace (`/`). -* `ATLAS_TOKEN`/ `access_token` - (Optional) A Terraform Enterprise [user API - token](/docs/cloud/users-teams-organizations/users.html#api-tokens). We - recommend using the `ATLAS_TOKEN` environment variable rather than setting - `access_token` in the configuration. If not set, the token will be requested - during a `terraform init` and saved locally. -* `address` - (Optional) The URL of a Terraform Enterprise instance. Defaults to - the SaaS version of Terraform Enterprise, at `"https://app.terraform.io"`; if - you use a private install, provide its URL here. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli-index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli-index.html.md deleted file mode 100644 index 9fc22207..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/cli-index.html.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -layout: "docs" -page_title: "Documentation" -sidebar_current: "docs-home" -description: |- - Documentation for Terraform's core open source features, including the - configuration language, the commands, and the main Terraform providers. ---- - -# Terraform CLI Documentation - -Welcome to the Terraform CLI documentation! - -## What's in This Section of the Docs? - -This section contains reference documentation for Terraform's core open source -features, including the -[configuration language](/docs/configuration/index.html), the -[command-line tools](/docs/commands/index.html), and the main -[Terraform providers](/docs/providers/index.html). Use the navigation sidebar -to browse the various subsections. - -## Who is This For? - -The Terraform CLI docs are relevant to _all Terraform users,_ including open -source users and Terraform Cloud users. - -Since these docs are reference material, they are mainly written for -_intermediate and advanced users,_ who need to find complete and detailed -information quickly. - -- **New user?** Try the - [Get Started collection](https://learn.hashicorp.com/collections/terraform/aws-get-started?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) - at HashiCorp Learn, then return - here once you've used Terraform to manage some simple resources. -- **Curious about Terraform?** See [Introduction to Terraform](/intro/index.html) - for a broad overview of what Terraform is and why people use it. - -## What's Elsewhere? - -This is not the only section of the Terraform docs! You can find out more at the -[Terraform docs home page](/docs/index.html), or you can jump between sections -using the "Other Docs" area of the navigation sidebar. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/auth/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/auth/index.html.md new file mode 100644 index 00000000..794f2311 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/auth/index.html.md @@ -0,0 +1,29 @@ +--- +layout: "docs" +page_title: "Authentication - Terraform CLI" +--- + +# CLI Authentication + +> **Hands-on:** Try the [Authenticate the CLI with Terraform Cloud](https://learn.hashicorp.com/tutorials/terraform/cloud-login?in=terraform/cloud&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +[Terraform Cloud](/docs/cloud/index.html) and +[Terraform Enterprise](/docs/enterprise/index.html) are platforms that perform +Terraform runs to provision infrastructure, offering a collaboration-focused +environment that makes it easier for teams to use Terraform together. (For +expediency, the content below refers to both products as "Terraform Cloud.") + +Terraform CLI integrates with Terraform Cloud in several ways — it can be a +front-end for [CLI-driven runs](/docs/cloud/run/cli.html) in Terraform Cloud, +and can also use Terraform Cloud as a state backend and a private module +registry. All of these integrations require you to authenticate Terraform CLI +with your Terraform Cloud account. + +The best way to handle CLI authentication is with the `login` and `logout` +commands, which help automate the process of getting an API token for your +Terraform Cloud user account. + +For details, see: + +- [The `terraform login` command](/docs/cli/commands/login.html) +- [The `terraform logout` command](/docs/cli/commands/logout.html) diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/code/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/code/index.html.md new file mode 100644 index 00000000..01dba636 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/code/index.html.md @@ -0,0 +1,43 @@ +--- +layout: "docs" +page_title: "Writing and Modifying Code - Terraform CLI" +--- + +# Writing and Modifying Terraform Code + +The [Terraform language](/docs/language/index.html) is Terraform's primary +user interface, and all of Terraform's workflows rely on configurations written +in the Terraform language. + +Terraform CLI includes several commands to make Terraform code more convenient +to work with. Integrating these commands into your editing workflow can +potentially save you time and effort. + +- [The `terraform console` command](/docs/cli/commands/console.html) starts an + interactive shell for evaluating Terraform + [expressions](/docs/language/expressions/index.html), which can be a faster way + to verify that a particular resource argument results in the value you expect. + + +- [The `terraform fmt` command](/docs/cli/commands/fmt.html) rewrites Terraform + configuration files to a canonical format and style, so you don't have to + waste time making minor adjustments for readability and consistency. It works + well as a pre-commit hook in your version control system. + +- [The `terraform validate` command](/docs/cli/commands/validate.html) validates the + syntax and arguments of the Terraform configuration files in a directory, + including argument and attribute names and types for resources and modules. + The `plan` and `apply` commands automatically validate a configuration before + performing any other work, so `validate` isn't a crucial part of the core + workflow, but it can be very useful as a pre-commit hook or as part of a + continuous integration pipeline. + +- [The `0.13upgrade` command](/docs/cli/commands/0.13upgrade.html) and + [the `0.12upgrade` command](/docs/cli/commands/0.12upgrade.html) can automatically + modify the configuration files in a Terraform module to help deal with major + syntax changes that occurred in the 0.13 and 0.12 releases of Terraform. Both + of these commands are only available in the Terraform version they are + associated with, and you are expected to upgrade older code to be compatible + with 0.12 before attempting to make it compatible with 0.13. For more detailed + information about updating code for new Terraform versions, see the [upgrade + guides](/upgrade-guides/index.html) in the Terraform language docs. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/0.12upgrade.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/0.12upgrade.html.md new file mode 100644 index 00000000..59ff0de4 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/0.12upgrade.html.md @@ -0,0 +1,118 @@ +--- +layout: "docs" +page_title: "Command: 0.12upgrade" +sidebar_current: "docs-commands-012upgrade" +description: |- + The 0.12upgrade subcommand automatically rewrites existing configurations for Terraform 0.12 compatibility. +--- + +# Command: 0.12upgrade + +The `terraform 0.12upgrade` command applies several automatic upgrade rules to +help prepare a module that was written for Terraform v0.11 to be used +with Terraform v0.12. + +-> **This command is available only in Terraform v0.12 releases.** For more information, see [the Terraform v0.12 upgrade guide](https://www.terraform.io/upgrade-guides/0-12.html). + +## Usage + +Usage: `terraform 0.12upgrade [options] [dir]` + +By default, `0.12upgrade` changes configuration files in the current working +directory. However, you can provide an explicit path to another directory if +desired, which may be useful for automating migrations of several modules in +the same repository. + +When run with no other options, the command will first explain what it is +going to do and prompt for confirmation: + +``` +$ terraform 0.12upgrade + +This command will rewrite the configuration files in the given directory so +that they use the new syntax features from Terraform v0.12, and will identify +any constructs that may need to be adjusted for correct operation with +Terraform v0.12. + +We recommend using this command in a clean version control work tree, so that +you can easily see the proposed changes as a diff against the latest commit. +If you have uncommitted changes already present, we recommend aborting this +command and dealing with them before running this command again. + +Would you like to upgrade the module in the current directory? + Only 'yes' will be accepted to confirm. + + Enter a value: yes +``` + +The `0.12upgrade` subcommand requires access to providers used in the +configuration in order to analyze their resource types, so it's important to +run `terraform init` first to install these. In some rare cases, a configuration +that worked in v0.11 may have syntax errors in v0.12, in which case +`terraform init` will run in a special mode where it installs only enough to +run the upgrade command, after which you can run `terraform init` again to +complete initialization. + +Many of the rewrite rules are completely automatic, but in some cases the +tool cannot determine enough information from the configuration alone to make +a decision, and so it will instead add a comment to the configuration for +user review. All such comments contain the string `TF-UPGRADE-TODO` to make +them easier to find. + +After upgrading, the configuration will also be reformatted into the standard +Terraform style and expressions rewritten to use the more-readable v0.12 syntax +features. + +We recommend running this command with a clean version control work tree so +that you can use VCS tools to review the proposed changes, including any +`TF-UPGRADE-TODO` comments, and make any revisions required before committing +the change. + +Once upgraded the configuration will no longer be compatible with Terraform +v0.11 and earlier. When upgrading a shared module that is called from multiple +configurations, you may need to +[fix existing configurations to a previous version](/docs/language/modules/syntax.html#version) +to allow for a gradual upgrade. If the module is published via +[a Terraform registry](/docs/registry/), assign a new _major_ version number +to the upgraded module source to represent the fact that this is a breaking +change for v0.11 callers. If a module is installed directly from a version +control system such as Git, +[use specific revisions](https://www.terraform.io/docs/language/modules/sources.html#selecting-a-revision) +to control which version is used by which caller. + +The command-line options are all optional. The available options are: + +* `-yes` - Skip the initial introduction messages and interactive confirmation. + Use this when running the command in batch from a script. + +* `-force` - Override the heuristic that attempts to detect if a configuration + is already written for v0.12 or later. Some of the transformations made by + this command are not idempotent, so re-running against the same module may + change the meanings of some expressions in the module. + +## Batch Usage + +After you've experimented with the `0.12upgrade` command in some confined +situations, if you have a repository containing multiple modules you may +wish to batch-upgrade them all and review them together. Recursive upgrades +are not supported by the tool itself, but if you are on a Unix-style system +you can achieve this using the `find` command as follows: + +``` +find . -name '*.tf' -printf "%h\n" | uniq | xargs -n1 terraform 0.12upgrade -yes +``` + +On Mac OS X, the `find` included with the system does not support the `-printf` argument. You can install GNU find using Homebrew in order to use that argument: + +``` +brew install findutils +``` +Once installed, run the above command line using `gfind` instead of `find`. + + +Note that the above includes the `-yes` option to override the interactive +prompt, so be sure you have a clean work tree before running it. + +Because upgrading requires access to the configuration's provider plugins, +all of the directories must be initialized with `terraform init` prior to +running the above. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/0.13upgrade.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/0.13upgrade.html.md new file mode 100644 index 00000000..52ff4206 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/0.13upgrade.html.md @@ -0,0 +1,90 @@ +--- +layout: "docs" +page_title: "Command: 0.13upgrade" +sidebar_current: "docs-commands-013upgrade" +description: |- + The 0.13upgrade subcommand updates existing configurations to use the new provider source features from Terraform 0.13. +--- + +# Command: 0.13upgrade + +The `terraform 0.13upgrade` command updates existing configuration to add an +explicit `source` attribute for each provider used in a given module. The +provider source settings are stored in a `required_providers` block. + +-> **This command is available only in Terraform v0.13 releases.** For more information, see [the Terraform v0.13 upgrade guide](https://www.terraform.io/upgrade-guides/0-13.html). + +## Usage + +Usage: `terraform 0.13upgrade [options] [dir]` + +The primary purpose of the `0.13upgrade` command is to determine which +providers are in use for a module, detect the source address for those +providers where possible, and record this information in a +[`required_providers` block][required-providers]. + +[required-providers]: /docs/language/providers/requirements.html + +~> Note: the command ignores `.tf.json` files and override files in the module. + +If the module already has a `required_providers` block, the command updates it +in-place. Otherwise, a new block is added to the `versions.tf` file. + +By default, `0.13upgrade` changes configuration files in the current working +directory. However, you can provide an explicit path to another directory if +desired, which may be useful for automating migrations of several modules in +the same repository. + +When run with no other options, the command will first explain what it is +going to do and prompt for confirmation: + +``` +$ terraform 0.13upgrade + +This command will update the configuration files in the given directory to use +the new provider source features from Terraform v0.13. It will also highlight +any providers for which the source cannot be detected, and advise how to +proceed. + +We recommend using this command in a clean version control work tree, so that +you can easily see the proposed changes as a diff against the latest commit. +If you have uncommited changes already present, we recommend aborting this +command and dealing with them before running this command again. + +Would you like to upgrade the module in the current directory? + Only 'yes' will be accepted to confirm. + + Enter a value: yes +``` + +We recommend running this command with a clean version control work tree so +that you can use VCS tools to review the proposed changes, including any +`TF-UPGRADE-TODO` comments, and make any revisions required before committing +the change. + +There is one command-line option: + +* `-yes` - Skip the initial introduction messages and interactive confirmation. + Use this when running the command in batch from a script. + +## Batch Usage + +After you've experimented with the `0.13upgrade` command in some confined +situations, if you have a repository containing multiple modules you may +wish to batch-upgrade them all and review them together. Recursive upgrades +are not supported by the tool itself, but if you are on a Unix-style system +you can achieve this using the `find` command as follows: + +``` +$ find . -name '*.tf' | xargs -n1 dirname | uniq | xargs -n1 terraform 0.13upgrade -yes +``` + +On a Windows system with PowerShell, you can use this command: + +``` +Get-Childitem -Recurse -Include *.tf | Split-Path | ` +Select-Object -Unique | ForEach-Object { terraform 0.13upgrade -yes $_.FullName } +``` + +Note that the above commands include the `-yes` option to override the +interactive prompt, so be sure you have a clean work tree before running it. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/apply.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/apply.html.md new file mode 100644 index 00000000..f5454bb7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/apply.html.md @@ -0,0 +1,111 @@ +--- +layout: "docs" +page_title: "Command: apply" +sidebar_current: "docs-commands-apply" +description: |- + The `terraform apply` command executes the actions proposed in a Terraform plan. +--- + +# Command: apply + +> **Hands-on:** Try the [Terraform: Get Started](https://learn.hashicorp.com/collections/terraform/aws-get-started?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. + +The `terraform apply` command executes the actions proposed in a Terraform +plan. + +The most straightforward way to use `terraform apply` is to run it without +any arguments at all, in which case it will automatically create a new +execution plan (as if you had run `terraform plan`) and then prompt you to +approve that plan, before taking the indicated actions. + +Another way to use `terraform apply` is to pass it the filename of a saved +plan file you created earlier with `terraform plan -out=...`, in which case +Terraform will apply the changes in the plan without any confirmation prompt. +This two-step workflow is primarily intended for when +[running Terraform in automation](https://learn.hashicorp.com/tutorials/terraform/automate-terraform?in=terraform/automation&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS). + +## Usage + +Usage: `terraform apply [options] [plan]` + +The behavior of `terraform apply` differs significantly depending on whether +you pass it the filename of a previously-saved plan file. + +In the default case, with no saved plan file, `terraform apply` effectively +runs [`terraform plan`](./plan.html) internally itself in order to propose a +new plan. In that case, `terraform apply` supports all of the same +[Planning Modes](./plan.html#planning-modes) and +[Planning Options](./plan.html#planning-options) that `terraform plan` +would accept, so you can customize how Terraform will create the plan. +Terraform will prompt you to approve the plan before taking the described +actions, unless you override that prompt using the `-auto-approve` option. + +If you pass the filename of a previously-saved plan file, none of the options +related to planning modes and planning options are supported, because Terraform +will instead use the options that you set on the earlier `terraform plan` call +that created the plan file. + +The following options allow you to change various details about how the +apply command executes and reports on the apply operation. If you are running +`terraform apply` _without_ a previously-saved plan file, these options are +_in addition to_ the planning modes and planning options described for +[`terraform plan`](./plan.html). + +* `-auto-approve` - Skips interactive approval of plan before applying. This + option is ignored when you pass a previously-saved plan file, because + Terraform considers you passing the plan file as the approval and so + will never prompt in that case. + +* `-compact-warnings` - Shows any warning messages in a compact form which + includes only the summary messages, unless the warnings are accompanied by + at least one error and thus the warning text might be useful context for + the errors. + +* `-input=false` - Disables all of Terraform's interactive prompts. Note that + this also prevents Terraform from prompting for interactive approval of a + plan, so Terraform will conservatively assume that you do not wish to + apply the plan, causing the operation to fail. If you wish to run Terraform + in a non-interactive context, see + [Running Terraform in Automation](https://learn.hashicorp.com/tutorials/terraform/automate-terraform?in=terraform/automation&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) for some + different approaches. + +* `-lock=false` - Disables Terraform's default behavior of attempting to take + a read/write lock on the state for the duration of the operation. + +* `-lock-timeout=DURATION` - Unless locking is disabled with `-lock=false`, + instructs Terraform to retry acquiring a lock for a period of time before + returning an error. The duration syntax is a number followed by a time + unit letter, such as "3s" for three seconds. + +* `-no-color` - Disables terminal formatting sequences in the output. Use this + if you are running Terraform in a context where its output will be + rendered by a system that cannot interpret terminal formatting. + +* `-parallelism=n` - Limit the number of concurrent operation as Terraform + [walks the graph](/docs/internals/graph.html#walking-the-graph). Defaults to + 10. + +For configurations using +[the `local` backend](/docs/language/settings/backends/local.html) only, +`terraform apply` also accepts the legacy options +[`-state`, `-state-out`, and `-backup`](/docs/language/settings/backends/local.html#command-line-arguments). + +## Passing a Different Configuration Directory + +Terraform v0.13 and earlier also accepted a directory path in place of the +plan file argument to `terraform apply`, in which case Terraform would use +that directory as the root module instead of the current working directory. + +That usage was deprecated in Terraform v0.14 and removed in Terraform v0.15. +If your workflow relies on overriding the root module directory, use +[the `-chdir` global option](./#switching-working-directory-with-chdir) +instead, which works across all commands and makes Terraform consistently look +in the given directory for all files it would normally read or write in the +current working directory. + +If your previous use of this legacy pattern was also relying on Terraform +writing the `.terraform` subdirectory into the current working directory even +though the root module directory was overridden, use +[the `TF_DATA_DIR` environment variable](/docs/cli/config/environment-variables.html#tf_data_dir) +to direct Terraform to write the `.terraform` directory to a location other +than the current working directory. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/console.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/console.html.md new file mode 100644 index 00000000..5d888ead --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/console.html.md @@ -0,0 +1,54 @@ +--- +layout: "docs" +page_title: "Command: console" +sidebar_current: "docs-commands-console" +description: |- + The `terraform console` command provides an interactive console for + evaluting expressions. +--- + +# Command: console + +The `terraform console` command provides an interactive console for +evaluating [expressions](/docs/language/expressions/index.html). + +## Usage + +Usage: `terraform console [options]` + +This command provides an interactive command-line console for evaluating and +experimenting with [expressions](/docs/language/expressions/index.html). +This is useful for testing interpolations before using them in configurations, +and for interacting with any values currently saved in +[state](/docs/language/state/index.html). + +If the current state is empty or has not yet been created, the console can be +used to experiment with the expression syntax and +[built-in functions](/docs/language/functions/index.html). + +You can close the console with the `exit` command or by pressing Control-C +or Control-D. + +For configurations using +[the `local` backend](/docs/language/settings/backends/local.html) only, +`terraform console` accepts the legacy command line option +[`-state`](/docs/language/settings/backends/local.html#command-line-arguments). + +## Scripting + +The `terraform console` command can be used in non-interactive scripts +by piping newline-separated commands to it. Only the output from the +final command is printed unless an error occurs earlier. + +For example: + +```shell +$ echo "1 + 5" | terraform console +6 +``` + +## Remote State + +If [remote state](/docs/language/state/remote.html) is used by the current backend, +Terraform will read the state for the current workspace from the backend +before evaluating any expressions. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/destroy.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/destroy.html.md new file mode 100644 index 00000000..fc82a455 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/destroy.html.md @@ -0,0 +1,48 @@ +--- +layout: "docs" +page_title: "Command: destroy" +sidebar_current: "docs-commands-destroy" +description: |- + The `terraform destroy` command is a convenient way to destroy all objects + managed by a particular Terraform configuration. +--- + +# Command: destroy + +The `terraform destroy` command is a convenient way to destroy all remote +objects managed by a particular Terraform configuration. + +While you will typically not want to destroy long-lived objects in a production +environment, Terraform is sometimes used to manage ephemeral infrastructure +for development purposes, in which case you can use `terraform destroy` to +conveniently clean up all of those temporary objects once you are finished +with your work. + +## Usage + +Usage: `terraform destroy [options]` + +This command is just a convenience alias for the following command: + +``` +terraform apply -destroy +``` + +For that reason, this command accepts most of the options that +[`terraform apply`](./apply.html) accepts, although it does +not accept a plan file argument and forces the selection of the "destroy" +planning mode. + +You can also create a speculative destroy plan, to see what the effect of +destroying would be, by running the following command: + +``` +terraform plan -destroy +``` + +This will run [`terraform plan`](./plan.html) in _destroy_ mode, showing +you the proposed destroy changes without executing them. + +-> **Note:** The `-destroy` option to `terraform apply` exists only in +Terraform v1.0 and later. For earlier versions, you _must_ use +`terraform destroy` to get the effect of `terraform apply -destroy`. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/env.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/env.html.md new file mode 100644 index 00000000..3d1183d2 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/env.html.md @@ -0,0 +1,13 @@ +--- +layout: "docs" +page_title: "Command: env" +sidebar_current: "docs-commands-envcmd" +description: |- + The terraform env command is a deprecated, legacy form of "terraform workspace". +--- + +# Command: env + +The `terraform env` command is deprecated. +[The `terraform workspace` command](/docs/cli/commands/workspace/index.html) +should be used instead. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/fmt.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/fmt.html.md new file mode 100644 index 00000000..b07a97df --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/fmt.html.md @@ -0,0 +1,63 @@ +--- +layout: "docs" +page_title: "Command: fmt" +sidebar_current: "docs-commands-fmt" +description: |- + The `terraform fmt` command is used to rewrite Terraform configuration files to a canonical format and style. +--- + +# Command: fmt + +The `terraform fmt` command is used to rewrite Terraform configuration files +to a canonical format and style. This command applies a subset of +the [Terraform language style conventions](/docs/language/syntax/style.html), +along with other minor adjustments for readability. + +Other Terraform commands that generate Terraform configuration will produce +configuration files that conform to the style imposed by `terraform fmt`, so +using this style in your own files will ensure consistency. + +The canonical format may change in minor ways between Terraform versions, so +after upgrading Terraform we recommend to proactively run `terraform fmt` +on your modules along with any other changes you are making to adopt the new +version. + +We don't consider new formatting rules in `terraform fmt` to be a breaking +change in new versions of Terraform, but we do aim to minimize changes for +configurations that are already following the style examples shown in the +Terraform documentation. When adding new formatting rules, they will usually +aim to apply more of the rules already shown in the configuration examples +in the documentation, and so we recommend following the documented style even +for decisions that `terraform fmt` doesn't yet apply automatically. + +Formatting decisions are always subjective and so you might disagree with the +decisions that `terraform fmt` makes. This command is intentionally opinionated +and has no customization options because its primary goal is to encourage +consistency of style between different Terraform codebases, even though the +chosen style can never be be everyone's favorite. + +We recommend that you follow the style conventions applied by `terraform fmt` +when writing Terraform modules, but if you find the results particularly +objectionable then you may choose not to use this command, and possibly choose +to use a third-party formatting tool instead. If you choose to use a +third-party tool then you should also run it on files that are generated +automatically by Terraform, to get consistency between your hand-written files +and the generated files. + +## Usage + +Usage: `terraform fmt [options] [DIR]` + +By default, `fmt` scans the current directory for configuration files. If +the `dir` argument is provided then it will scan that given directory +instead. If `dir` is a single dash (`-`) then `fmt` will read from standard +input (STDIN). + +The command-line flags are all optional. The list of available flags are: + +* `-list=false` - Don't list the files containing formatting inconsistencies. +* `-write=false` - Don't overwrite the input files. (This is implied by `-check` or when the input is STDIN.) +* `-diff` - Display diffs of formatting changes +* `-check` - Check if the input is formatted. Exit status will be 0 if + all input is properly formatted and non-zero otherwise. +* `-recursive` - Also process files in subdirectories. By default, only the given directory (or current directory) is processed. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/force-unlock.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/force-unlock.html.md similarity index 100% rename from vendor/github.com/hashicorp/terraform/website/docs/commands/force-unlock.html.markdown rename to vendor/github.com/hashicorp/terraform/website/docs/cli/commands/force-unlock.html.md diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/get.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/get.html.md new file mode 100644 index 00000000..b847fecd --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/get.html.md @@ -0,0 +1,27 @@ +--- +layout: "docs" +page_title: "Command: get" +sidebar_current: "docs-commands-get" +description: |- + The `terraform get` command is used to download and update modules. +--- + +# Command: get + +The `terraform get` command is used to download and update +[modules](/docs/language/modules/develop/index.html) mentioned in the root module. + +## Usage + +Usage: `terraform get [options] PATH` + +The modules are downloaded into a `.terraform` subdirectory of the current +working directory. Don't commit this directory to your version control +repository. + +The `get` command supports the following option: + +* `-update` - If specified, modules that are already downloaded will be + checked for updates and the updates will be downloaded if present. + +* `-no-color` - Disable text coloring in the output. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/graph.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/graph.html.md new file mode 100644 index 00000000..dea3d441 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/graph.html.md @@ -0,0 +1,59 @@ +--- +layout: "docs" +page_title: "Command: graph" +sidebar_current: "docs-commands-graph" +description: |- + The `terraform graph` command is used to generate a visual representation of either a configuration or execution plan. The output is in the DOT format, which can be used by GraphViz to generate charts. +--- + +# Command: graph + +The `terraform graph` command is used to generate a visual +representation of either a configuration or execution plan. +The output is in the DOT format, which can be used by +[GraphViz](http://www.graphviz.org) to generate charts. + + +## Usage + +Usage: `terraform graph [options]` + +Outputs the visual execution graph of Terraform resources according to +either the current configuration or an execution plan. + +The graph is outputted in DOT format. The typical program that can +read this format is GraphViz, but many web services are also available +to read this format. + +The `-type` flag can be used to control the type of graph shown. Terraform +creates different graphs for different operations. See the options below +for the list of types supported. The default type is "plan" if a +configuration is given, and "apply" if a plan file is passed as an +argument. + +Options: + +* `-plan=tfplan` - Render graph using the specified plan file instead of the + configuration in the current directory. + +* `-draw-cycles` - Highlight any cycles in the graph with colored edges. + This helps when diagnosing cycle errors. + +* `-type=plan` - Type of graph to output. Can be: `plan`, `plan-destroy`, `apply`, + `validate`, `input`, `refresh`. + +* `-module-depth=n` - (deprecated) In prior versions of Terraform, specified the + depth of modules to show in the output. + +## Generating Images + +The output of `terraform graph` is in the DOT format, which can +easily be converted to an image by making use of `dot` provided +by GraphViz: + +```shellsession +$ terraform graph | dot -Tsvg > graph.svg +``` + +Here is an example graph output: +![Graph Example](docs/graph-example.png) diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/import.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/import.html.md new file mode 100644 index 00000000..74b3ee9e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/import.html.md @@ -0,0 +1,163 @@ +--- +layout: "docs" +page_title: "Command: import" +sidebar_current: "docs-commands-import" +description: |- + The `terraform import` command is used to import existing resources into Terraform. +--- + +# Command: import + +> **Hands-on:** Try the [Import Terraform Configuration](https://learn.hashicorp.com/tutorials/terraform/state-import?in=terraform/state&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +The `terraform import` command is used to +[import existing resources](/docs/cli/import/index.html) +into Terraform. + +## Usage + +Usage: `terraform import [options] ADDRESS ID` + +Import will find the existing resource from ID and import it into your Terraform +state at the given ADDRESS. + +ADDRESS must be a valid [resource address](/docs/cli/state/resource-addressing.html). +Because any resource address is valid, the import command can import resources +into modules as well as directly into the root of your state. + +ID is dependent on the resource type being imported. For example, for AWS +instances it is the instance ID (`i-abcd1234`) but for AWS Route53 zones +it is the zone ID (`Z12ABC4UGMOZ2N`). Please reference the provider documentation for details +on the ID format. If you're unsure, feel free to just try an ID. If the ID +is invalid, you'll just receive an error message. + +~> Warning: Terraform expects that each remote object it is managing will be +bound to only one resource address, which is normally guaranteed by Terraform +itself having created all objects. If you import existing objects into Terraform, +be careful to import each remote object to only one Terraform resource address. +If you import the same object multiple times, Terraform may exhibit unwanted +behavior. For more information on this assumption, see +[the State section](/docs/language/state/index.html). + +The command-line flags are all optional. The list of available flags are: + +* `-config=path` - Path to directory of Terraform configuration files that + configure the provider for import. This defaults to your working directory. + If this directory contains no Terraform configuration files, the provider + must be configured via manual input or environmental variables. + +* `-input=true` - Whether to ask for input for provider configuration. + +* `-lock=true` - Lock the state file when locking is supported. + +* `-lock-timeout=0s` - Duration to retry a state lock. + +* `-no-color` - If specified, output won't contain any color. + +* `-parallelism=n` - Limit the number of concurrent operation as Terraform + [walks the graph](/docs/internals/graph.html#walking-the-graph). Defaults + to 10. + +* `-provider=provider` - **Deprecated** Override the provider configuration to +use when importing the object. By default, Terraform uses the provider specified +in the configuration for the target resource, and that is the best behavior in most cases. + +* `-var 'foo=bar'` - Set a variable in the Terraform configuration. This flag + can be set multiple times. Variable values are interpreted as + [literal expressions](/docs/language/expressions/types.html) in the + Terraform language, so list and map values can be specified via this flag. + This is only useful with the `-config` flag. + +* `-var-file=foo` - Set variables in the Terraform configuration from + a [variable file](/docs/language/values/variables.html#variable-definitions-tfvars-files). If + a `terraform.tfvars` or any `.auto.tfvars` files are present in the current + directory, they will be automatically loaded. `terraform.tfvars` is loaded + first and the `.auto.tfvars` files after in alphabetical order. Any files + specified by `-var-file` override any values set automatically from files in + the working directory. This flag can be used multiple times. This is only + useful with the `-config` flag. + +* `-ignore-remote-version` - When using the enhanced remote backend with + Terraform Cloud, continue even if remote and local Terraform versions differ. + This may result in an unusable Terraform Cloud workspace, and should be used + with extreme caution. + +For configurations using +[the `local` backend](/docs/language/settings/backends/local.html) only, +`terraform import` also accepts the legacy options +[`-state`, `-state-out`, and `-backup`](/docs/language/settings/backends/local.html#command-line-arguments). + +## Provider Configuration + +Terraform will attempt to load configuration files that configure the +provider being used for import. If no configuration files are present or +no configuration for that specific provider is present, Terraform will +prompt you for access credentials. You may also specify environmental variables +to configure the provider. + +The only limitation Terraform has when reading the configuration files +is that the import provider configurations must not depend on non-variable +inputs. For example, a provider configuration cannot depend on a data +source. + +As a working example, if you're importing AWS resources and you have a +configuration file with the contents below, then Terraform will configure +the AWS provider with this file. + +```hcl +variable "access_key" {} +variable "secret_key" {} + +provider "aws" { + access_key = "${var.access_key}" + secret_key = "${var.secret_key}" +} +``` + +## Example: Import into Resource + +This example will import an AWS instance into the `aws_instance` resource named `foo`: + +```shell +$ terraform import aws_instance.foo i-abcd1234 +``` + +## Example: Import into Module + +The example below will import an AWS instance into the `aws_instance` resource named `bar` into a module named `foo`: + +```shell +$ terraform import module.foo.aws_instance.bar i-abcd1234 +``` + +## Example: Import into Resource configured with count + +The example below will import an AWS instance into the first instance of the `aws_instance` resource named `baz` configured with +[`count`](/docs/language/meta-arguments/count.html): + +```shell +$ terraform import 'aws_instance.baz[0]' i-abcd1234 +``` + +## Example: Import into Resource configured with for_each + +The example below will import an AWS instance into the `"example"` instance of the `aws_instance` resource named `baz` configured with +[`for_each`](/docs/language/meta-arguments/for_each.html): + +Linux, Mac OS, and UNIX: + +```shell +$ terraform import 'aws_instance.baz["example"]' i-abcd1234 +``` + +PowerShell: + +```shell +$ terraform import 'aws_instance.baz[\"example\"]' i-abcd1234 +``` + +Windows `cmd.exe`: + +```shell +$ terraform import aws_instance.baz[\"example\"] i-abcd1234 +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/index.html.md new file mode 100644 index 00000000..d579a951 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/index.html.md @@ -0,0 +1,171 @@ +--- +layout: "docs" +page_title: "Basic CLI Features" +sidebar_current: "docs-commands" +description: |- + Main usage information for the Terraform CLI tool. +--- + +# Basic CLI Features + +> **Hands-on:** Try the [Terraform: Get Started](https://learn.hashicorp.com/collections/terraform/aws-get-started?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. + +The command line interface to Terraform is via the `terraform` command, which +accepts a variety of subcommands such as `terraform init` or `terraform plan`. +A full list of all of the supported subcommands is in the navigation section +of this page. + +We refer to the `terraform` command line tool as "Terraform CLI" elsewhere +in the documentation. This terminology is often used to distinguish it from +other components you might use in the Terraform product family, such as +[Terraform Cloud](/docs/cloud/) or +the various [Terraform providers](/docs/providers/), which are developed and +released separately from Terraform CLI. + +To view a list of the commands available in your current Terraform version, +run `terraform` with no additional arguments: + +```text +Usage: terraform [global options] [args] + +The available commands for execution are listed below. +The primary workflow commands are given first, followed by +less common or more advanced commands. + +Main commands: + init Prepare your working directory for other commands + validate Check whether the configuration is valid + plan Show changes required by the current configuration + apply Create or update infrastructure + destroy Destroy previously-created infrastructure + +All other commands: + console Try Terraform expressions at an interactive command prompt + fmt Reformat your configuration in the standard style + force-unlock Release a stuck lock on the current workspace + get Install or upgrade remote Terraform modules + graph Generate a Graphviz graph of the steps in an operation + import Associate existing infrastructure with a Terraform resource + login Obtain and save credentials for a remote host + logout Remove locally-stored credentials for a remote host + output Show output values from your root module + providers Show the providers required for this configuration + refresh Update the state to match remote systems + show Show the current state or a saved plan + state Advanced state management + taint Mark a resource instance as not fully functional + untaint Remove the 'tainted' state from a resource instance + version Show the current Terraform version + workspace Workspace management + +Global options (use these before the subcommand, if any): + -chdir=DIR Switch to a different working directory before executing the + given subcommand. + -help Show this help output, or the help for a specified subcommand. + -version An alias for the "version" subcommand. +``` + +(The output from your current Terraform version may be different than the +above example.) + +To get specific help for any specific command, use the `-help` option with the +relevant subcommand. For example, to see help about the "validate" subcommand +you can run `terraform validate -help`. + +The inline help built in to Terraform CLI describes the most important +characteristics of each command. For more detailed information, refer to each +command's section of this documentation, available in the navigation +section of this page. + +## Switching working directory with `-chdir` + +The usual way to run Terraform is to first switch to the directory containing +the `.tf` files for your root module (for example, using the `cd` command), so +that Terraform will find those files automatically without any extra arguments. + +In some cases though — particularly when wrapping Terraform in automation +scripts — it can be convenient to run Terraform from a different directory than +the root module directory. To allow that, Terraform supports a global option +`-chdir=...` which you can include before the name of the subcommand you intend +to run: + +``` +terraform -chdir=environments/production apply +``` + +The `chdir` option instructs Terraform to change its working directory to the +given directory before running the given subcommand. This means that any files +that Terraform would normally read or write in the current working directory +will be read or written in the given directory instead. + +There are two exceptions where Terraform will use the original working directory +even when you specify `-chdir=...`: + +* Settings in the [CLI Configuration](/docs/cli/config/config-file.html) are not for a specific + subcommand and Terraform processes them before acting on the `-chdir` + option. + +* In case you need to use files from the original working directory as part + of your configuration, a reference to `path.cwd` in the configuration will + produce the original working directory instead of the overridden working + directory. Use `path.root` to get the root module directory. + +## Shell Tab-completion + +If you use either `bash` or `zsh` as your command shell, Terraform can provide +tab-completion support for all command names and (at this time) _some_ command +arguments. + +To add the necessary commands to your shell profile, run the following command: + +```bash +terraform -install-autocomplete +``` + +After installation, it is necessary to restart your shell or to re-read its +profile script before completion will be activated. + +To uninstall the completion hook, assuming that it has not been modified +manually in the shell profile, run the following command: + +```bash +terraform -uninstall-autocomplete +``` + +Currently not all of Terraform's subcommands have full tab-completion support +for all arguments. We plan to improve tab-completion coverage over time. + +## Upgrade and Security Bulletin Checks + +The Terraform CLI commands interact with the HashiCorp service +[Checkpoint](https://checkpoint.hashicorp.com/) to check for the availability +of new versions and for critical security bulletins about the current version. + +One place where the effect of this can be seen is in `terraform version`, where +it is used by default to indicate in the output when a newer version is +available. + +Only anonymous information, which cannot be used to identify the user or host, +is sent to Checkpoint. An anonymous ID is sent which helps de-duplicate warning +messages. Both the anonymous id and the use of checkpoint itself are completely +optional and can be disabled. + +Checkpoint itself can be entirely disabled for all HashiCorp products by +setting the environment variable `CHECKPOINT_DISABLE` to any non-empty value. + +Alternatively, settings in +[the CLI configuration file](/docs/cli/config/config-file.html) can be used to +disable checkpoint features. The following checkpoint-related settings are +supported in this file: + +* `disable_checkpoint` - set to `true` to disable checkpoint calls + entirely. This is similar to the `CHECKPOINT_DISABLE` environment variable + described above. + +* `disable_checkpoint_signature` - set to `true` to disable the use of an + anonymous signature in checkpoint requests. This allows Terraform to check + for security bulletins but does not send the anonymous signature in these + requests. + +[The Checkpoint client code](https://github.com/hashicorp/go-checkpoint) used +by Terraform is available for review by any interested party. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/init.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/init.html.md new file mode 100644 index 00000000..04278636 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/init.html.md @@ -0,0 +1,200 @@ +--- +layout: "docs" +page_title: "Command: init" +sidebar_current: "docs-commands-init" +description: |- + The `terraform init` command is used to initialize a Terraform configuration. This is the first command that should be run for any new or existing Terraform configuration. It is safe to run this command multiple times. +--- + +# Command: init + +> **Hands-on:** Try the [Terraform: Get Started](https://learn.hashicorp.com/collections/terraform/aws-get-started?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. + +The `terraform init` command is used to initialize a working directory +containing Terraform configuration files. This is the first command that should +be run after writing a new Terraform configuration or cloning an existing one +from version control. It is safe to run this command multiple times. + +## Usage + +Usage: `terraform init [options]` + +This command performs several different initialization steps in order to +prepare the current working directory for use with Terraform. More details on +these are in the sections below, but in most cases it is not necessary to worry +about these individual steps. + +This command is always safe to run multiple times, to bring the working +directory up to date with changes in the configuration. Though subsequent runs +may give errors, this command will never delete your existing configuration or +state. + +## General Options + +The following options apply to all of (or several of) the initialization steps: + +* `-input=true` Ask for input if necessary. If false, will error if + input was required. + +* `-lock=false` Disable locking of state files during state-related operations. + +* `-lock-timeout=` Override the time Terraform will wait to acquire + a state lock. The default is `0s` (zero seconds), which causes immediate + failure if the lock is already held by another process. + +* `-no-color` Disable color codes in the command output. + +* `-upgrade` Opt to upgrade modules and plugins as part of their respective + installation steps. See the sections below for more details. + +## Copy a Source Module + +By default, `terraform init` assumes that the working directory already +contains a configuration and will attempt to initialize that configuration. + +Optionally, init can be run against an empty directory with the +`-from-module=MODULE-SOURCE` option, in which case the given module will be +copied into the target directory before any other initialization steps are +run. + +This special mode of operation supports two use-cases: + +* Given a version control source, it can serve as a shorthand for checking out + a configuration from version control and then initializing the working directory + for it. + +* If the source refers to an _example_ configuration, it can be copied into + a local directory to be used as a basis for a new configuration. + +For routine use it is recommended to check out configuration from version +control separately, using the version control system's own commands. This way +it is possible to pass extra flags to the version control system when necessary, +and to perform other preparation steps (such as configuration generation, or +activating credentials) before running `terraform init`. + +## Backend Initialization + +During init, the root configuration directory is consulted for +[backend configuration](/docs/language/settings/backends/configuration.html) and the chosen backend +is initialized using the given configuration settings. + +Re-running init with an already-initialized backend will update the working +directory to use the new backend settings. Depending on what changed, this +may result in interactive prompts to confirm migration of workspace states. +The `-force-copy` option suppresses these prompts and answers "yes" to the +migration questions. The `-reconfigure` option disregards any existing +configuration, preventing migration of any existing state. + +To skip backend configuration, use `-backend=false`. Note that some other init +steps require an initialized backend, so it is recommended to use this flag only +when the working directory was already previously initialized for a particular +backend. + +The `-backend-config=...` option can be used for +[partial backend configuration](/docs/language/settings/backends/configuration.html#partial-configuration), +in situations where the backend settings are dynamic or sensitive and so cannot +be statically specified in the configuration file. + +## Child Module Installation + +During init, the configuration is searched for `module` blocks, and the source +code for referenced [modules](/docs/language/modules/develop/index.html) is retrieved from the locations +given in their `source` arguments. + +Re-running init with modules already installed will install the sources for +any modules that were added to configuration since the last init, but will not +change any already-installed modules. Use `-upgrade` to override this behavior, +updating all modules to the latest available source code. + +To skip child module installation, use `-get=false`. Note that some other init +steps can complete only when the module tree is complete, so it's recommended +to use this flag only when the working directory was already previously +initialized with its child modules. + +## Plugin Installation + +Most Terraform providers are published separately from Terraform as plugins. +During init, Terraform searches the configuration for both direct and indirect +references to providers and attempts to install the plugins for those providers. + +For providers that are published in either +[the public Terraform Registry](https://registry.terraform.io/) or in a +third-party provider registry, `terraform init` will automatically find, +download, and install the necessary provider plugins. If you cannot or do not +wish to install providers from their origin registries, you can customize how +Terraform installs providers using +[the provider installation settings in the CLI configuration](/docs/cli/config/config-file.html#provider-installation). + +For more information about specifying which providers are required for each +of your modules, see [Provider Requirements](/docs/language/providers/requirements.html). + +After successful installation, Terraform writes information about the selected +providers to [the dependency lock file](/docs/language/dependency-lock.html). +You should commit this file to your version control system to ensure that +when you run `terraform init` again in future Terraform will select exactly +the same provider versions. Use the `-upgrade` option if you want Terraform +to ignore the dependency lock file and consider installing newer versions. + +You can modify `terraform init`'s plugin behavior with the following options: + +- `-upgrade` Upgrade all previously-selected plugins to the newest version + that complies with the configuration's version constraints. This will + cause Terraform to ignore any selections recorded in the dependency lock + file, and to take the newest available version matching the configured + version constraints. +- `-get-plugins=false` — Skip plugin installation. + + -> Note: Since Terraform 0.13, this option has been superseded by the + [`provider_installation`](/docs/cli/config/config-file.html#provider-installation) and + [`plugin_cache_dir`](/docs/cli/config/config-file.html#plugin_cache_dir) settings. + It should not be used in Terraform versions 0.13+, and this option + was removed in Terraform 0.15. +- `-plugin-dir=PATH` — Force plugin installation to read plugins _only_ from + the specified directory, as if it had been configured as a `filesystem_mirror` + in the CLI configuration. If you intend to routinely use a particular + filesystem mirror then we recommend + [configuring Terraform's installation methods globally](/docs/cli/config/config-file.html#provider-installation). + You can use `-plugin-dir` as a one-time override for exceptional situations, + such as if you are testing a local build of a provider plugin you are + currently developing. +- `-lockfile=MODE` Set a dependency lockfile mode. + +The valid values for the lockfile mode are as follows: + +- readonly: suppress the lockfile changes, but verify checksums against the + information already recorded. It conflicts with the `-upgrade` flag. If you + update the lockfile with third-party dependency management tools, it would be + useful to control when it changes explicitly. + +## Running `terraform init` in automation + +For teams that use Terraform as a key part of a change management and +deployment pipeline, it can be desirable to orchestrate Terraform runs in some +sort of automation in order to ensure consistency between runs, and provide +other interesting features such as integration with version control hooks. + +There are some special concerns when running `init` in such an environment, +including optionally making plugins available locally to avoid repeated +re-installation. For more information, see +the [Running Terraform in Automation](https://learn.hashicorp.com/tutorials/terraform/automate-terraform?in=terraform/automation&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +## Passing a Different Configuration Directory + +Terraform v0.13 and earlier also accepted a directory path in place of the +plan file argument to `terraform apply`, in which case Terraform would use +that directory as the root module instead of the current working directory. + +That usage is still supported in Terraform v0.14, but is now deprecated and we +plan to remove it in Terraform v0.15. If your workflow relies on overriding +the root module directory, use +[the `-chdir` global option](./#switching-working-directory-with-chdir) +instead, which works across all commands and makes Terraform consistently look +in the given directory for all files it would normally read or write in the +current working directory. + +If your previous use of this legacy pattern was also relying on Terraform +writing the `.terraform` subdirectory into the current working directory even +though the root module directory was overridden, use +[the `TF_DATA_DIR` environment variable](/docs/cli/config/environment-variables.html#tf_data_dir) +to direct Terraform to write the `.terraform` directory to a location other +than the current working directory. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/login.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/login.html.md new file mode 100644 index 00000000..8af1ef7a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/login.html.md @@ -0,0 +1,45 @@ +--- +layout: "docs" +page_title: "Command: login" +sidebar_current: "docs-commands-login" +description: |- + The terraform login command can be used to automatically obtain and save an API token for Terraform Cloud, Terraform Enterprise, or any other host that offers Terraform services. +--- + +# Command: login + +The `terraform login` command can be used to automatically obtain and save an +API token for Terraform Cloud, Terraform Enterprise, or any other host that offers Terraform services. + +-> **Note:** This command is suitable only for use in interactive scenarios +where it is possible to launch a web browser on the same host where Terraform +is running. If you are running Terraform in an unattended automation scenario, +you can +[configure credentials manually in the CLI configuration](https://www.terraform.io/docs/cli/config/config-file.html#credentials). + +## Usage + +Usage: `terraform login [hostname]` + +If you don't provide an explicit hostname, Terraform will assume you want to +log in to Terraform Cloud at `app.terraform.io`. + +## Credentials Storage + +By default, Terraform will obtain an API token and save it in plain text in a +local CLI configuration file called `credentials.tfrc.json`. When you run +`terraform login`, it will explain specifically where it intends to save +the API token and give you a chance to cancel if the current configuration is +not as desired. + +If you don't wish to store your API token in the default location, you can +optionally configure a +[credentials helper program](/docs/cli/config/config-file.html#credentials-helpers) which knows +how to store and later retrieve credentials in some other system, such as +your organization's existing secrets management system. + +## Login Server Support + +The `terraform login` command works with any server supporting the +[login protocol](/docs/internals/login-protocol.html), including Terraform Cloud +and Terraform Enterprise. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/logout.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/logout.html.md new file mode 100644 index 00000000..15656fba --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/logout.html.md @@ -0,0 +1,30 @@ +--- +layout: "docs" +page_title: "Command: logout" +sidebar_current: "docs-commands-logout" +description: |- + The terraform logout command is used to remove credentials stored by terraform login. +--- + +# Command: logout + +The `terraform logout` command is used to remove credentials stored by +`terraform login`. These credentials are API tokens for Terraform Cloud, +Terraform Enterprise, or any other host that offers Terraform services. + +## Usage + +Usage: `terraform logout [hostname]` + +If you don't provide an explicit hostname, Terraform will assume you want to +log out of Terraform Cloud at `app.terraform.io`. + +-> **Note:** the API token is only removed from local storage, not destroyed on +the remote server, so it will remain valid until manually revoked. + +## Credentials Storage + +By default, Terraform will remove the token stored in plain text in a local CLI +configuration file called `credentials.tfrc.json`. If you have configured a +[credentials helper program](/docs/cli/config/config-file.html#credentials-helpers), Terraform +will use the helper's `forget` command to remove it. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/output.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/output.html.md new file mode 100644 index 00000000..1a828612 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/output.html.md @@ -0,0 +1,123 @@ +--- +layout: "docs" +page_title: "Command: output" +sidebar_current: "docs-commands-output" +description: |- + The `terraform output` command is used to extract the value of an output variable from the state file. +--- + +# Command: output + +The `terraform output` command is used to extract the value of +an output variable from the state file. + +## Usage + +Usage: `terraform output [options] [NAME]` + +With no additional arguments, `output` will display all the outputs for +the root module. If an output `NAME` is specified, only the value of that +output is printed. + +The command-line flags are all optional. The list of available flags are: + +* `-json` - If specified, the outputs are formatted as a JSON object, with + a key per output. If `NAME` is specified, only the output specified will be + returned. This can be piped into tools such as `jq` for further processing. +* `-raw` - If specified, Terraform will convert the specified output value to a + string and print that string directly to the output, without any special + formatting. This can be convenient when working with shell scripts, but + it only supports string, number, and boolean values. Use `-json` instead + for processing complex data types. +* `-no-color` - If specified, output won't contain any color. +* `-state=path` - Path to the state file. Defaults to "terraform.tfstate". + Ignored when [remote state](/docs/language/state/remote.html) is used. + +## Examples + +These examples assume the following Terraform output snippet. + +```hcl +output "instance_ips" { + value = aws_instance.web.*.public_ip +} + +output "lb_address" { + value = aws_alb.web.public_dns +} + +output "password" { + sensitive = true + value = var.secret_password +} +``` + +To list all outputs: + +```shellsession +$ terraform output +instance_ips = [ + "54.43.114.12", + "52.122.13.4", + "52.4.116.53" +] +lb_address = "my-app-alb-1657023003.us-east-1.elb.amazonaws.com" +password = +``` + +Note that outputs with the `sensitive` attribute will be redacted: + +```shellsession +$ terraform output password +password = +``` + +To query for the DNS address of the load balancer: + +```shellsession +$ terraform output lb_address +"my-app-alb-1657023003.us-east-1.elb.amazonaws.com" +``` + +To query for all instance IP addresses: + +```shellsession +$ terraform output instance_ips +instance_ips = [ + "54.43.114.12", + "52.122.13.4", + "52.4.116.53" +] +``` + +## Use in automation + +The `terraform output` command by default displays in a human-readable format, +which can change over time to improve clarity. + +For scripting and automation, use `-json` to produce the stable JSON format. +You can parse the output using a JSON command-line parser such as +[jq](https://stedolan.github.io/jq/): + +```shellsession +$ terraform output -json instance_ips | jq -r '.[0]' +54.43.114.12 +``` + +For the common case of directly using a string value in a shell script, you +can use `-raw` instead, which will print the string directly with no extra +escaping or whitespace. + +```shellsession +$ terraform output -raw lb_address +my-app-alb-1657023003.us-east-1.elb.amazonaws.com +``` + +The `-raw` option works only with values that Terraform can automatically +convert to strings. Use `-json` instead, possibly combined with `jq`, to +work with complex-typed values such as objects. + +Terraform strings are sequences of Unicode characters rather than raw bytes, +so the `-raw` output will be UTF-8 encoded when it contains non-ASCII +characters. If you need a different character encoding, use a separate command +such as `iconv` to transcode Terraform's raw output. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/plan.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/plan.html.md new file mode 100644 index 00000000..5a87d728 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/plan.html.md @@ -0,0 +1,307 @@ +--- +layout: "docs" +page_title: "Command: plan" +sidebar_current: "docs-commands-plan" +description: |- + The terraform plan command creates an execution plan. +--- + +# Command: plan + +> **Hands-on:** Try the [Terraform: Get Started](https://learn.hashicorp.com/collections/terraform/aws-get-started?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. + +The `terraform plan` command creates an execution plan. By default, creating +a plan consists of: + +* Reading the current state of any already-existing remote objects to make sure + that the Terraform state is up-to-date. +* Comparing the current configuration to the prior state and noting any + differences. +* Proposing a set of change actions that should, if applied, make the remote + objects match the configuration. + +The plan command alone will not actually carry out the proposed changes, and +so you can use this command to check whether the proposed changes match what +you expected before you apply the changes or share your changes with your +team for broader review. + +If Terraform detects that no changes are needed to resource instances or to +root module output values, `terraform plan` will report that no actions need +to be taken. + +If you are using Terraform directly in an interactive terminal and you expect +to apply the changes Terraform proposes, you can alternatively run +[`terraform apply`](./apply.html) directly. By default, the "apply" command +automatically generates a new plan and prompts for you to approve it. + +You can use the optional `-out=FILE` option to save the generated plan to a +file on disk, which you can later execute by passing the file to +[`terraform apply`](./apply.html) as an extra argument. This two-step workflow +is primarily intended for when +[running Terraform in automation](https://learn.hashicorp.com/tutorials/terraform/automate-terraform?in=terraform/automation&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS). + +If you run `terraform plan` without the `-out=FILE` option then it will create +a _speculative plan_, which is a description of a the effect of the plan but +without any intent to actually apply it. + +In teams that use a version control and code review workflow for making changes +to real infrastructure, developers can use speculative plans to verify the +effect of their changes before submitting them for code review. However, it's +important to consider that other changes made to the target system in the +meantime might cause the final effect of a configuration change to be different +than what an earlier speculative plan indicated, so you should always re-check +the final non-speculative plan before applying to make sure that it still +matches your intent. + +## Usage + +Usage: `terraform plan [options]` + +The `plan` subcommand looks in the current working directory for the root module +configuration. + +Because the plan command is one of the main commands of Terraform, it has +a variety of different options, described in the following sections. However, +most of the time you should not need to set any of these options, because +a Terraform configuration should typically be designed to work with no special +additional options for routine work. + +The remaining sections on this page describe the various options: + +* **[Planning Modes](#planning-modes)**: There are some special alternative + planning modes that you can use for some special situations where your goal + is not just to change the remote system to match your configuration. +* **[Planning Options](#planning-options)**: Alongside the special planning + modes, there are also some options you can set in order to customize the + planning process for unusual needs. + * **[Resource Targeting](#resource-targeting)** is one particular + special planning option that has some important caveats associated + with it. +* **[Other Options](#other-options)**: These change the behavior of the planning + command itself, rather than customizing the content of the generated plan. + +## Planning Modes + +The section above described Terraform's default planning behavior, which is +intended for changing the remote system to match with changes you've made to +your configuration. + +Terraform has one alternative planning mode, which creates a plan with +a different intended outcome: + +* **Destroy mode:** creates a plan whose goal is to destroy all remote objects + that currently exist, leaving an empty Terraform state. This can be useful + for situations like transient development environments, where the managed + objects cease to be useful once the development task is complete. + + Activate destroy mode using the `-destroy` command line option. + +In situations where we need to discuss the default planning mode that Terraform +uses when none of the alternative modes are selected, we refer to it as +"Normal mode". Because these alternative modes are for specialized situations +only, some other Terraform documentation only discusses the normal planning +mode. + +The planning modes are all mutually-exclusive, so activating any non-default +planning mode disables the "normal" planning mode, and you can't use more than +one alternative mode at the same time. + +-> **Note:** In Terraform v0.15 and earlier, the `-destroy` option is +supported only by the `terraform plan` command, and not by the +`terraform apply` command. To create and apply a plan in destroy mode in +earlier versions you must run [`terraform destroy`](./destroy.html). + +## Planning Options + +In addition to the planning _modes_ described above, there are also several +additional options that can modify details of the behavior of the planning +step. + +When you use `terraform apply` without passing it a saved plan file, it +incorporates the `terraform plan` command functionality and so the planning +options in this section, along with the planning mode selection options in +the previous section, are also available with the same meanings on +`terraform apply`. + +* `-refresh=false` - Disables the default behavior of synchronizing the + Terraform state with remote objects before checking for configuration changes. + + This option can potentially make the planning operation faster by reducing + the number of remote API requests, but it comes at the expense of having + Terraform not take into account any changes that might've happened outside + of Terraform, and thus the resulting plan may not be complete or correct. + + This option is not available in the "refresh only" planning mode, because + it would effectively disable the entirety of the planning operation in that + case. + +* `-replace=ADDRESS` - Instructs Terraform to plan to replace the single + resource instance with the given address. If the given instance would + normally have caused only an "update" action, or no action at all, then + Terraform will choose a "replace" action instead. + + You can use this option if you have learned that a particular remote object + has become degraded in some way. If you are using immutable infrastructure + patterns then you may wish to respond to that by replacing the + malfunctioning object with a new object that has the same configuration. + + This option is allowed only in the normal planning mode, so this option + is incompatible with the `-destroy` option. + + The `-replace=...` option is available only from Terraform v1.0 onwards. + For earlier versions, you can achieve a similar effect (with some caveats) + using [`terraform taint`](./taint.html). + +* `-target=ADDRESS` - Instructs Terraform to focus its planning efforts only + on resource instances which match the given address and on any objects that + those instances depend on. + + This command is for exceptional use only. See + [Resource Targeting](#resource-targeting) + below for more information. + +* `-var 'NAME=VALUE` - Sets a value for a single + [input variable](/docs/language/values/variables.html) declared in the + root module of the configuration. Use this option multiple times to set + more than one variable. + +* `-var-file=FILENAME` - Sets values for potentially many + [input variables](/docs/language/values/variables.html) declared in the + root module of the configuration, using definitions from a + ["tfvars" file](/docs/language/values/variables.html#variable-definitions-tfvars-files). + Use this option multiple times to include values from more than one file. + +There are several other ways to set values for input variables in the root +module, aside from the `-var` and `-var-file` options. For more information, +see +[Assigning Values to Root Module Variables](/docs/language/values/variables.html#assigning-values-to-root-module-variables). + +### Resource Targeting + +> **Hands-on:** Try the [Target resources](https://learn.hashicorp.com/tutorials/terraform/resource-targeting?in=terraform/state&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +You can use the `-target` option to focus Terraform's attention on only a +subset of resources. +You can use [resource address syntax](/docs/cli/state/resource-addressing.html) +to specify the constraint. Terraform interprets the resource address as follows: + +* If the given address identifies one specific resource instance, Terraform + will select that instance alone. For resources with either `count` or + `for_each` set, a resource instance address must include the instance index + part, like `aws_instance.example[0]`. + +* If the given address identifies a resource as a whole, Terraform will select + all of the instances of that resource. For resources with either `count` + or `for_each` set, this means selecting _all_ instance indexes currently + associated with that resource. For single-instance resources (without + either `count` or `for_each`), the resource address and the resource instance + address are identical, so this possibility does not apply. + +* If the given address identifies an entire module instance, Terraform will + select all instances of all resources that belong to that module instance + and all of its child module instances. + +Once Terraform has selected one or more resource instances that you've directly +targeted, it will also then extend the selection to include all other objects +that those selections depend on either directly or indirectly. + +This targeting capability is provided for exceptional circumstances, such +as recovering from mistakes or working around Terraform limitations. It +is *not recommended* to use `-target` for routine operations, since this can +lead to undetected configuration drift and confusion about how the true state +of resources relates to configuration. + +Instead of using `-target` as a means to operate on isolated portions of very +large configurations, prefer instead to break large configurations into +several smaller configurations that can each be independently applied. +[Data sources](/docs/language/data-sources/index.html) can be used to access +information about resources created in other configurations, allowing +a complex system architecture to be broken down into more manageable parts +that can be updated independently. + +## Other Options + +The `terraform plan` command also has some other options that are related to +the input and output of the planning command, rather than customizing what +sort of plan Terraform will create. These commands are not necessarily also +available on `terraform apply`, unless otherwise stated in the documentation +for that command. + +The available options are: + +* `-compact-warnings` - Shows any warning messages in a compact form which + includes only the summary messages, unless the warnings are accompanied by + at least one error and thus the warning text might be useful context for + the errors. + +* `-detailed-exitcode` - Returns a detailed exit code when the command exits. + When provided, this argument changes the exit codes and their meanings to + provide more granular information about what the resulting plan contains: + * 0 = Succeeded with empty diff (no changes) + * 1 = Error + * 2 = Succeeded with non-empty diff (changes present) + +* `-input=false` - Disables Terraform's default behavior of prompting for + input for root module input variables that have not otherwise been assigned + a value. This option is particular useful when running Terraform in + non-interactive automation systems. + +* `-lock=false` - Disables Terraform's default behavior of attempting to take + a read/write lock on the state for the duration of the operation. + +* `-lock-timeout=DURATION` - Unless locking is disabled with `-lock=false`, + instructs Terraform to retry acquiring a lock for a period of time before + returning an error. The duration syntax is a number followed by a time + unit letter, such as "3s" for three seconds. + +* `-no-color` - Disables terminal formatting sequences in the output. Use this + if you are running Terraform in a context where its output will be + rendered by a system that cannot interpret terminal formatting. + +* `-out=FILENAME` - Writes the generated plan to the given filename in an + opaque file format that you can later pass to `terraform apply` to execute + the planned changes, and to some other Terraform commands that can work with + saved plan files. + + Terraform will allow any filename for the plan file, but a typical + convention is to name it `tfplan`. **Do not** name the file with a suffix + that Terraform recognizes as another file format; if you use a `.tf` suffix + then Terraform will try to interpret the file as a configuration source + file, which will then cause syntax errors for subsequent commands. + + The generated file is not in any standard format intended for consumption + by other software, but the file _does_ contain your full configuration, + all of the values associated with planned changes, and all of the plan + options including the input variables. If your plan includes any sort of + sensitive data, even if obscured in Terraform's terminal output, it will + be saved in cleartext in the plan file. You should therefore treat any + saved plan files as potentially-sensitive artifacts. + +* `-parallelism=n` - Limit the number of concurrent operation as Terraform + [walks the graph](/docs/internals/graph.html#walking-the-graph). Defaults + to 10. + +For configurations using +[the `local` backend](/docs/language/settings/backends/local.html) only, +`terraform plan` accepts the legacy command line option +[`-state`](/docs/language/settings/backends/local.html#command-line-arguments). + +### Passing a Different Configuration Directory + +Terraform v0.13 and earlier accepted an additional positional argument giving +a directory path, in which case Terraform would use that directory as the root +module instead of the current working directory. + +That usage was deprecated in Terraform v0.14 and removed in Terraform v0.15. +If your workflow relies on overriding the root module directory, use +[the `-chdir` global option](./#switching-working-directory-with-chdir) +instead, which works across all commands and makes Terraform consistently look +in the given directory for all files it would normally read or write in the +current working directory. + +If your previous use of this legacy pattern was also relying on Terraform +writing the `.terraform` subdirectory into the current working directory even +though the root module directory was overridden, use +[the `TF_DATA_DIR` environment variable](/docs/cli/config/environment-variables.html#tf_data_dir) +to direct Terraform to write the `.terraform` directory to a location other +than the current working directory. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/providers.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/providers.html.md new file mode 100644 index 00000000..3c3bb7d0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/providers.html.md @@ -0,0 +1,22 @@ +--- +layout: "docs" +page_title: "Command: providers" +sidebar_current: "docs-commands-providers" +description: |- + The `terraform providers` command prints information about the providers used + in the current configuration. +--- + +# Command: providers + +The `terraform providers` command shows information about the +[provider requirements](/docs/language/providers/requirements.html) of the +configuration in the current working directory, as an aid to understanding +where each requirement was detected from. + +This command also has several subcommands with different purposes, which +are listed in the navigation bar. + +## Usage + +Usage: `terraform providers` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/providers/lock.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/providers/lock.html.md similarity index 84% rename from vendor/github.com/hashicorp/terraform/website/docs/commands/providers/lock.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/cli/commands/providers/lock.html.md index 25dea21e..b9a48fae 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/providers/lock.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/providers/lock.html.md @@ -1,5 +1,5 @@ --- -layout: "commands-providers" +layout: "docs" page_title: "Command: providers lock" sidebar_current: "docs-commands-providers-lock" description: |- @@ -7,35 +7,35 @@ description: |- to the dependency lock file without initializing the referenced providers. --- -# Command: terraform providers mirror +# Command: terraform providers lock The `terraform providers lock` consults upstream registries (by default) in order to write provider dependency information into -[the dependency lock file](/docs/configuration/dependency-lock.html). +[the dependency lock file](/docs/language/dependency-lock.html). The common way to update the dependency lock file is as a side-effect of normal provider installation during -[`terraform init`](../init.html), but there are several situations where that +[`terraform init`](/docs/cli/commands/init.html), but there are several situations where that automatic approach may not be sufficient: * If you are running Terraform in an environment that uses - [alternative provider installation methods](../cli-config.html#provider-installation), + [alternative provider installation methods](/docs/cli/config/config-file.html#provider-installation), such as filesystem or network mirrors, normal provider installation will not access the origin registry for a provider and therefore Terraform will not be able to populate all of the possible package checksums for the selected provider versions. - If you use `terraform lock` to write the official release checksums for a - provider into the dependency lock file then future `terraform init` runs - will verify the packages available in your selected mirror against the - official checksums previously recorded, giving additional certainty that - the mirror is serving the provider packages it is claiming to. + If you use `terraform lock` to write the official release checksums for a + provider into the dependency lock file then future `terraform init` runs + will verify the packages available in your selected mirror against the + official checksums previously recorded, giving additional certainty that + the mirror is serving the provider packages it is claiming to. * If your team runs Terraform across a number of different platforms (e.g. on both Windows and Linux) and the upstream registry for a provider is unable to provide signed checksums using the latest hashing scheme, subsequent runs of Terraform on other platforms may - [add additional checksums to the lock file](/docs/configuration/dependency-lock.html#new-provider-package-checksums). + [add additional checksums to the lock file](/docs/language/dependency-lock.html#new-provider-package-checksums). You can avoid that by pre-populating hashes for all of the platforms you intend to use, using the `terraform providers lock` command. @@ -49,7 +49,7 @@ With no additional command line arguments, `terraform providers lock` will analyze the configuration in the current working directory to find all of the providers it depends on, and it will fetch the necessary data about those providers from their origin registries and then update -[the dependency lock file](/docs/configuration/dependency-lock.html) to +[the dependency lock file](/docs/language/dependency-lock.html) to include a selected version for each provider and all of the package checksums that are covered by the provider developer's cryptographic signature. @@ -81,14 +81,14 @@ You can customize the default behavior using the following additional option: available for the given platform and will save enough package checksums in the lock file to support _at least_ the specified platforms. - Use this option multiple times to include checksums for multiple target - systems. + Use this option multiple times to include checksums for multiple target + systems. - Target platform names consist of an operating system and a CPU - architecture. For example, `linux_amd64` selects the Linux operating system - running on an AMD64 or x86_64 CPU. + Target platform names consist of an operating system and a CPU + architecture. For example, `linux_amd64` selects the Linux operating system + running on an AMD64 or x86_64 CPU. - There is more detail on this option in the following section. + There is more detail on this option in the following section. ## Specifying Target Platforms @@ -150,7 +150,7 @@ multiple times and specify a different subset of your providers each time. The `-fs-mirror` and `-net-mirror` options have the same meaning as `filesystem_mirror` and `network_mirror` blocks in -[the provider installation methods configuration](../cli-config.html#provider-installation), +[the provider installation methods configuration](/docs/cli/config/config-file.html#provider-installation), but specify only a single method in order to be explicit about where you intend to derive the package checksum information from. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/providers/mirror.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/providers/mirror.html.md similarity index 95% rename from vendor/github.com/hashicorp/terraform/website/docs/commands/providers/mirror.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/cli/commands/providers/mirror.html.md index 39e7b183..b0827837 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/providers/mirror.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/providers/mirror.html.md @@ -1,5 +1,5 @@ --- -layout: "commands-providers" +layout: "docs" page_title: "Command: providers mirror" sidebar_current: "docs-commands-providers-mirror" description: |- @@ -19,7 +19,7 @@ from provider registries as part of initializing the current working directory. Sometimes Terraform is running in an environment where that isn't possible, such as on an isolated network without access to the Terraform Registry. In that case, -[explicit installation method configuration](../cli-config.html#explicit-installation-method-configuration) +[explicit installation method configuration](/docs/cli/config/config-file.html#explicit-installation-method-configuration) allows you to configure Terraform, when running on a particular system, to consult only a local filesystem directory where you've created a local mirror of the necessary plugins, and to skip accessing the upstream registry at all. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/providers/schema.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/providers/schema.html.md similarity index 99% rename from vendor/github.com/hashicorp/terraform/website/docs/commands/providers/schema.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/cli/commands/providers/schema.html.md index 717d463e..e97e50f2 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/providers/schema.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/providers/schema.html.md @@ -1,5 +1,5 @@ --- -layout: "commands-providers" +layout: "docs" page_title: "Command: providers schema" sidebar_current: "docs-commands-providers-schema" description: |- diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/push.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/push.html.md new file mode 100644 index 00000000..bc093a85 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/push.html.md @@ -0,0 +1,16 @@ +--- +layout: "docs" +page_title: "Command: push" +sidebar_current: "docs-commands-push" +description: |- + DISCONTINUED. Terraform Cloud and the modern "remote" backend have replaced the old `terraform push` command. +--- + +# Command: push + +!> **Important:** The `terraform push` command is no longer functional. Its functionality was replaced and surpassed by [the `remote` backend](/docs/language/settings/backends/remote.html), which works with current versions of Terraform Cloud. The `remote` backend allows you to run remote operations directly from the command line, and displays real-time output from the remote run environment. + +The `terraform push` command was an early implementation of remote Terraform runs. It allowed teams to push a configuration to a remote run environment in a discontinued version of Terraform Enterprise. + +The legacy Terraform Enterprise version that supported `terraform push` is no longer available, and there are no remaining instances of that version in operation. Without a service to push to, the command is now completely non-functional. + diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/refresh.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/refresh.html.md new file mode 100644 index 00000000..a171162e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/refresh.html.md @@ -0,0 +1,68 @@ +--- +layout: "docs" +page_title: "Command: refresh" +sidebar_current: "docs-commands-refresh" +description: |- + The `terraform refresh` command reads the current settings from all managed + remote objects and updates the Terraform state to match. +--- + +# Command: refresh + +The `terraform refresh` command reads the current settings from all managed +remote objects and updates the Terraform state to match. + +~> *Warning:* This command is deprecated, because its default behavior is +unsafe if you have misconfigured credentials for any of your providers. +See below for more information and recommended alternatives. + +This won't modify your real remote objects, but it will modify the +[the Terraform state](/docs/language/state/). + +You shouldn't typically need to use this command, because Terraform +automatically performs the same refreshing actions as a part of creating +a plan in both the +[`terraform plan`](./plan.html) +and +[`terraform apply`](./apply.html) +commands. This command is here primarily for backward compatibility, but +we don't recommend using it because it provides no opportunity to review +the effects of the operation before updating the state. + +## Usage + +Usage: `terraform refresh [options]` + +This command is effectively an alias for the following command: + +``` +terraform apply -refresh-only -auto-approve +``` + +Consequently, it supports all of the same options as +[`terraform apply`](./apply.html) except that it does not accept a saved +plan file, it doesn't allow selecting a planning mode other than "refresh only", +and `-auto-approve` is always enabled. + +Automatically applying the effect of a refresh is risky, because if you have +misconfigured credentials for one or more providers then the provider may +be misled into thinking that all of the managed objects have been deleted, +and thus remove all of the tracked objects without any confirmation prompt. + +Instead, we recommend using the following command in order to get the same +effect but with the opportunity to review the the changes that Terraform has +detected before committing them to the state: + +``` +terraform apply -refresh-only +``` + +This alternative command will present an interactive prompt for you to confirm +the detected changes. + +The `-refresh-only` option for `terraform plan` and `terraform apply` was +introduced in Terraform v1.0. For prior versions you must use +`terraform refresh` directly if you need this behavior, while taking into +account the warnings above. Wherever possible, avoid using `terraform refresh` +explicitly and instead rely on Terraform's behavior of automatically refreshing +existing objects as part of creating a normal plan. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/show.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/show.html.md new file mode 100644 index 00000000..45bc6fa3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/show.html.md @@ -0,0 +1,53 @@ +--- +layout: "docs" +page_title: "Command: show" +sidebar_current: "docs-commands-show" +description: |- + The `terraform show` command is used to provide human-readable output from a state or plan file. This can be used to inspect a plan to ensure that the planned operations are expected, or to inspect the current state as Terraform sees it. +--- + +# Command: show + +The `terraform show` command is used to provide human-readable output +from a state or plan file. This can be used to inspect a plan to ensure +that the planned operations are expected, or to inspect the current state +as Terraform sees it. + +Machine-readable output is generated by adding the `-json` command-line +flag. + +-> **Note:** When using the `-json` command-line flag, any sensitive values in +Terraform state will be displayed in plain text. For more information, see +[Sensitive Data in State](/docs/language/state/sensitive-data.html). + +## JSON Output + +For Terraform state files (including when no path is provided), +`terraform show -json` will show a JSON representation of the state. + +For Terraform plan files, `terraform show -json` will show a JSON representation +of the plan, configuration, and current state. + +If you've updated providers which contain new schema versions since the state +was written, the state needs to be upgraded before it can be displayed with +`show -json`. If you are viewing a plan, it must be created without +`-refresh=false`. If you are viewing a state file, run `terraform refresh` +first. + +The output format is covered in detail in [JSON Output Format](/docs/internals/json-format.html). + +## Usage + +Usage: `terraform show [options] [file]` + +You may use `show` with a path to either a Terraform state file or plan +file. If you don't specify a file path, Terraform will show the latest state +snapshot. + +This command accepts the following options: + +* `-no-color` - Disables output with coloring + +* `-json` - Displays machine-readable output from a state or plan file + +-> JSON output via the `-json` option requires **Terraform v0.12 or later**. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/index.html.md new file mode 100644 index 00000000..38e2028d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/index.html.md @@ -0,0 +1,53 @@ +--- +layout: "docs" +page_title: "Command: state" +sidebar_current: "docs-commands-state-index" +description: |- + The `terraform state` command is used for advanced state management. +--- + +# State Command + +The `terraform state` command is used for advanced state management. +As your Terraform usage becomes more advanced, there are some cases where +you may need to modify the [Terraform state](/docs/language/state/index.html). +Rather than modify the state directly, the `terraform state` commands can +be used in many cases instead. + +This command is a nested subcommand, meaning that it has further subcommands. +These subcommands are listed to the left. + +## Usage + +Usage: `terraform state [options] [args]` + +Please click a subcommand to the left for more information. + +## Remote State + +The Terraform state subcommands all work with remote state just as if it +was local state. Reads and writes may take longer than normal as each read +and each write do a full network roundtrip. Otherwise, backups are still +written to disk and the CLI usage is the same as if it were local state. + +## Backups + +All `terraform state` subcommands that modify the state write backup +files. The path of these backup file can be controlled with `-backup`. + +Subcommands that are read-only (such as [list](/docs/cli/commands/state/list.html)) +do not write any backup files since they aren't modifying the state. + +Note that backups for state modification _can not be disabled_. Due to +the sensitivity of the state file, Terraform forces every state modification +command to write a backup file. You'll have to remove these files manually +if you don't want to keep them around. + +## Command-Line Friendly + +The output and command-line structure of the state subcommands is +designed to be usable with Unix command-line tools such as grep, awk, +and similar PowerShell commands. + +For advanced filtering and modification, we recommend piping Terraform +state subcommands together with other command line tools. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/list.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/list.html.md new file mode 100644 index 00000000..a7b78c94 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/list.html.md @@ -0,0 +1,77 @@ +--- +layout: "docs" +page_title: "Command: state list" +sidebar_current: "docs-commands-state-sub-list" +description: |- + The terraform state list command is used to list resources within a Terraform state. +--- + +# Command: state list + +The `terraform state list` command is used to list resources within a +[Terraform state](/docs/language/state/index.html). + +## Usage + +Usage: `terraform state list [options] [address...]` + +The command will list all resources in the state file matching the given +addresses (if any). If no addresses are given, all resources are listed. + +The resources listed are sorted according to module depth order followed +by alphabetical. This means that resources that are in your immediate +configuration are listed first, and resources that are more deeply nested +within modules are listed last. + +For complex infrastructures, the state can contain thousands of resources. +To filter these, provide one or more patterns to the command. Patterns are +in [resource addressing format](/docs/cli/state/resource-addressing.html). + +The command-line flags are all optional. The list of available flags are: + +* `-state=path` - Path to the state file. Defaults to "terraform.tfstate". + Ignored when [remote state](/docs/language/state/remote.html) is used. +* `-id=id` - ID of resources to show. Ignored when unset. + +## Example: All Resources + +This example will list all resources, including modules: + +``` +$ terraform state list +aws_instance.foo +aws_instance.bar[0] +aws_instance.bar[1] +module.elb.aws_elb.main +``` + +## Example: Filtering by Resource + +This example will only list resources for the given name: + +``` +$ terraform state list aws_instance.bar +aws_instance.bar[0] +aws_instance.bar[1] +``` + +## Example: Filtering by Module + +This example will list resources in the given module and any submodules: + +``` +$ terraform state list module.elb +module.elb.aws_elb.main +module.elb.module.secgroups.aws_security_group.sg +``` + +## Example: Filtering by ID + +This example will only list the resource whose ID is specified on the +command line. This is useful to find where in your configuration a +specific resource is located. + +``` +$ terraform state list -id=sg-1234abcd +module.elb.aws_security_group.sg +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/state/mv.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/mv.html.md similarity index 88% rename from vendor/github.com/hashicorp/terraform/website/docs/commands/state/mv.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/mv.html.md index bb35b139..66fcbb76 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/state/mv.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/mv.html.md @@ -1,5 +1,5 @@ --- -layout: "commands-state" +layout: "docs" page_title: "Command: state mv" sidebar_current: "docs-commands-state-sub-mv" description: |- @@ -9,7 +9,7 @@ description: |- # Command: state mv The `terraform state mv` command is used to move items in a -[Terraform state](/docs/state/index.html). This command can move +[Terraform state](/docs/language/state/index.html). This command can move single resources, single instances of a resource, entire modules, and more. This command can also move items to a completely different state file, enabling efficient refactoring. @@ -36,7 +36,7 @@ for each state file. This command requires a source and destination address of the item to move. Addresses are -in [resource addressing format](/docs/commands/state/addressing.html). +in [resource addressing format](/docs/cli/state/resource-addressing.html). The command-line flags are all optional. The list of available flags are: @@ -57,6 +57,11 @@ The command-line flags are all optional. The list of available flags are: isn't specified the source state file will be used. This can be a new or existing path. +* `-ignore-remote-version` - When using the enhanced remote backend with + Terraform Cloud, continue even if remote and local Terraform versions differ. + This may result in an unusable Terraform Cloud workspace, and should be used + with extreme caution. + ## Example: Rename a Resource The example below renames the `packet_device` resource named `worker` to `helper`: @@ -95,7 +100,7 @@ $ terraform state mv -state-out=other.tfstate 'module.app' 'module.app' ## Example: Move a Resource configured with count The example below moves the first instance of a `packet_device` resource named `worker` configured with -[`count`](/docs/configuration/resources.html#count-multiple-resource-instances-by-count) to +[`count`](/docs/language/meta-arguments/count.html) to the first instance of a resource named `helper` also configured with `count`: ```shell @@ -105,7 +110,7 @@ $ terraform state mv 'packet_device.worker[0]' 'packet_device.helper[0]' ## Example: Move a Resource configured with for_each The example below moves the `"example123"` instance of a `packet_device` resource named `worker` configured with -[`for_each`](/docs/configuration/resources.html#for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings) +[`for_each`](/docs/language/meta-arguments/for_each.html) to the `"example456"` instance of a resource named `helper` also configuring `for_each`: Linux, Mac OS, and UNIX: diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/pull.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/pull.html.md new file mode 100644 index 00000000..ad52c96b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/pull.html.md @@ -0,0 +1,29 @@ +--- +layout: "docs" +page_title: "Command: state pull" +sidebar_current: "docs-commands-state-sub-pull" +description: |- + The `terraform state pull` command is used to manually download and output the state from remote state. +--- + +# Command: state pull + +The `terraform state pull` command is used to manually download and output +the state from [remote state](/docs/language/state/remote.html). This command also +works with local state. + +## Usage + +Usage: `terraform state pull` + +This command will download the state from its current location, upgrade the +local copy to the latest state file version that is compatible with +locally-installed Terraform, and output the raw format to stdout. + +This is useful for reading values out of state (potentially pairing this +command with something like [jq](https://stedolan.github.io/jq/)). It is +also useful if you need to make manual modifications to state. + +~> Note: This command cannot be used to inspect the Terraform version of +the remote state, as it will always be converted to the current Terraform +version before output. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/push.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/push.html.md new file mode 100644 index 00000000..1481e6bf --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/push.html.md @@ -0,0 +1,51 @@ +--- +layout: "docs" +page_title: "Command: state push" +sidebar_current: "docs-commands-state-sub-push" +description: |- + The `terraform state push` command pushes items to the Terraform state. +--- + +# Command: state push + +The `terraform state push` command is used to manually upload a local +state file to [remote state](/docs/language/state/remote.html). This command also +works with local state. + +This command should rarely be used. It is meant only as a utility in case +manual intervention is necessary with the remote state. + +## Usage + +Usage: `terraform state push [options] PATH` + +This command will push the state specified by PATH to the currently +configured [backend](/docs/language/settings/backends/index.html). + +If PATH is "-" then the state data to push is read from stdin. This data +is loaded completely into memory and verified prior to being written to +the destination state. + +Terraform will perform a number of safety checks to prevent you from +making changes that appear to be unsafe: + + * **Differing lineage**: If the "lineage" value in the state differs, + Terraform will not allow you to push the state. A differing lineage + suggests that the states are completely different and you may lose + data. + + * **Higher remote serial**: If the "serial" value in the destination state + is higher than the state being pushed, Terraform will prevent the push. + A higher serial suggests that data is in the destination state that isn't + accounted for in the local state being pushed. + +Both of these safety checks can be disabled with the `-force` flag. +**This is not recommended.** If you disable the safety checks and are +pushing state, the destination state will be overwritten. + +Other available flags: + +* `-ignore-remote-version` - When using the enhanced remote backend with + Terraform Cloud, continue even if remote and local Terraform versions differ. + This may result in an unusable Terraform Cloud workspace, and should be used + with extreme caution. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/state/replace-provider.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/replace-provider.html.md similarity index 82% rename from vendor/github.com/hashicorp/terraform/website/docs/commands/state/replace-provider.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/replace-provider.html.md index 1219c12a..e9837372 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/state/replace-provider.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/replace-provider.html.md @@ -1,5 +1,5 @@ --- -layout: "commands-state" +layout: "docs" page_title: "Command: state replace-provider" sidebar_current: "docs-commands-state-sub-replace-provider" description: |- @@ -9,7 +9,7 @@ description: |- # Command: state replace-provider The `terraform state replace-provider` command is used to replace the provider -for resources in a [Terraform state](/docs/state/index.html). +for resources in a [Terraform state](/docs/language/state/index.html). ## Usage @@ -38,6 +38,11 @@ The command-line flags are all optional. The list of available flags are: * `-state=path` - Path to the source state file to read from. Defaults to the configured backend, or "terraform.tfstate". +* `-ignore-remote-version` - When using the enhanced remote backend with + Terraform Cloud, continue even if remote and local Terraform versions differ. + This may result in an unusable Terraform Cloud workspace, and should be used + with extreme caution. + ## Example The example below replaces the `hashicorp/aws` provider with a fork by `acme`, hosted at a private registry at `registry.acme.corp`: diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/state/rm.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/rm.html.md similarity index 85% rename from vendor/github.com/hashicorp/terraform/website/docs/commands/state/rm.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/rm.html.md index cbdfc766..556a99d5 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/state/rm.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/rm.html.md @@ -1,5 +1,5 @@ --- -layout: "commands-state" +layout: "docs" page_title: "Command: state rm" sidebar_current: "docs-commands-state-sub-rm" description: |- @@ -9,7 +9,7 @@ description: |- # Command: state rm The `terraform state rm` command is used to remove items from the -[Terraform state](/docs/state/index.html). This command can remove +[Terraform state](/docs/language/state/index.html). This command can remove single resources, single instances of a resource, entire modules, and more. @@ -39,7 +39,7 @@ of this command, backups are required. This command requires one or more addresses that point to a resources in the state. Addresses are -in [resource addressing format](/docs/commands/state/addressing.html). +in [resource addressing format](/docs/cli/state/resource-addressing.html). The command-line flags are all optional. The list of available flags are: @@ -51,6 +51,11 @@ The command-line flags are all optional. The list of available flags are: Terraform-managed resources. By default it will use the configured backend, or the default "terraform.tfstate" if it exists. +* `-ignore-remote-version` - When using the enhanced remote backend with + Terraform Cloud, continue even if remote and local Terraform versions differ. + This may result in an unusable Terraform Cloud workspace, and should be used + with extreme caution. + ## Example: Remove a Resource The example below removes the `packet_device` resource named `worker`: @@ -78,7 +83,7 @@ $ terraform state rm 'module.foo.packet_device.worker' ## Example: Remove a Resource configured with count The example below removes the first instance of a `packet_device` resource named `worker` configured with -[`count`](/docs/configuration/resources.html#count-multiple-resource-instances-by-count): +[`count`](/docs/language/meta-arguments/count.html): ```shell $ terraform state rm 'packet_device.worker[0]' @@ -87,7 +92,7 @@ $ terraform state rm 'packet_device.worker[0]' ## Example: Remove a Resource configured with for_each The example below removes the `"example"` instance of a `packet_device` resource named `worker` configured with -[`for_each`](/docs/configuration/resources.html#for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings): +[`for_each`](/docs/language/meta-arguments/for_each.html): Linux, Mac OS, and UNIX: diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/show.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/show.html.md new file mode 100644 index 00000000..2c665ba6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/state/show.html.md @@ -0,0 +1,91 @@ +--- +layout: "docs" +page_title: "Command: state show" +sidebar_current: "docs-commands-state-sub-show" +description: |- + The `terraform state show` command is used to show the attributes of a single resource in the Terraform state. +--- + +# Command: state show + +The `terraform state show` command is used to show the attributes of a +single resource in the +[Terraform state](/docs/language/state/index.html). + +## Usage + +Usage: `terraform state show [options] ADDRESS` + +The command will show the attributes of a single resource in the +state file that matches the given address. + +This command requires an address that points to a single resource in the +state. Addresses are +in [resource addressing format](/docs/cli/state/resource-addressing.html). + +The command-line flags are all optional. The list of available flags are: + +* `-state=path` - Path to the state file. Defaults to "terraform.tfstate". + Ignored when [remote state](/docs/language/state/remote.html) is used. + +The output of `terraform state show` is intended for human consumption, not +programmatic consumption. To extract state data for use in other software, use +[`terraform show -json`](/docs/cli/commands/show.html#json-output) and decode the result +using the documented structure. + +## Example: Show a Resource + +The example below shows a `packet_device` resource named `worker`: + +``` +$ terraform state show 'packet_device.worker' +# packet_device.worker: +resource "packet_device" "worker" { + billing_cycle = "hourly" + created = "2015-12-17T00:06:56Z" + facility = "ewr1" + hostname = "prod-xyz01" + id = "6015bg2b-b8c4-4925-aad2-f0671d5d3b13" + locked = false +} +``` + +## Example: Show a Module Resource + +The example below shows a `packet_device` resource named `worker` inside a module named `foo`: + +```shell +$ terraform state show 'module.foo.packet_device.worker' +``` + +## Example: Show a Resource configured with count + +The example below shows the first instance of a `packet_device` resource named `worker` configured with +[`count`](/docs/language/meta-arguments/count.html): + +```shell +$ terraform state show 'packet_device.worker[0]' +``` + +## Example: Show a Resource configured with for_each + +The example below shows the `"example"` instance of a `packet_device` resource named `worker` configured with +[`for_each`](/docs/language/meta-arguments/for_each.html): + +Linux, Mac OS, and UNIX: + +```shell +$ terraform state show 'packet_device.worker["example"]' +``` + +PowerShell: + +```shell +$ terraform state show 'packet_device.worker[\"example\"]' +``` + +Windows `cmd.exe`: + +```shell +$ terraform state show packet_device.worker[\"example\"] +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/taint.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/taint.html.md new file mode 100644 index 00000000..84c2cbdb --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/taint.html.md @@ -0,0 +1,78 @@ +--- +layout: "docs" +page_title: "Command: taint" +sidebar_current: "docs-commands-taint" +description: |- + The `terraform taint` command informs Terraform that a particular object + is damaged or degraded. +--- + +# Command: taint + +The `terraform taint` command informs Terraform that a particular object has +become degraded or damaged. Terraform represents this by marking the +object as "tainted" in the Terraform state, in which case Terraform will +propose to replace it in the next plan you create. + +~> *Warning:* This command is deprecated, because there are better alternatives +available in Terraform v1.0 and later. See below for more details. + +If your intent is to force replacement of a particular object even though +there are no configuration changes that would require it, we recommend instead +to use the `-replace` option with [`terraform apply`](./apply.html). +For example: + +``` +terraform apply -replace="aws_instance.example[0]" +``` + +Creating a plan with the "replace" option is superior to using `terraform taint` +because it will allow you to see the full effect of that change before you take +any externally-visible action. When you use `terraform taint` to get a similar +effect, you risk someone else on your team creating a new plan against your +tainted object before you've had a chance to review the consequences of that +change yourself. + +The `-replace=...` option to `terraform apply` is only available from +Terraform v1.0 onwards, so if you are using an earlier version you will need to +use `terraform taint` to force object replacement, while considering the +caveats described above. + +## Usage + +Usage: `terraform taint [options] address` + +The `address` argument is the address of the resource to mark as tainted. +The address is in +[the resource address syntax](/docs/cli/state/resource-addressing.html) syntax, +as shown in the output from other commands, such as: + + * `aws_instance.foo` + * `aws_instance.bar[1]` + * `aws_instance.baz[\"key\"]` (quotes in resource addresses must be escaped on the command line, so that they will not be interpreted by your shell) + * `module.foo.module.bar.aws_instance.qux` + +This command accepts the following options: + +* `-allow-missing` - If specified, the command will succeed (exit code 0) + even if the resource is missing. The command might still return an error + for other situations, such as if there is a problem reading or writing + the state. + +* `-lock=false` - Disables Terraform's default behavior of attempting to take + a read/write lock on the state for the duration of the operation. + +* `-lock-timeout=DURATION` - Unless locking is disabled with `-lock=false`, + instructs Terraform to retry acquiring a lock for a period of time before + returning an error. The duration syntax is a number followed by a time + unit letter, such as "3s" for three seconds. + +* `-ignore-remote-version` - When using the enhanced remote backend with + Terraform Cloud, continue even if remote and local Terraform versions differ. + This may result in an unusable Terraform Cloud workspace, and should be used + with extreme caution. + +For configurations using +[the `local` backend](/docs/language/settings/backends/local.html) only, +`terraform taint` also accepts the legacy options +[`-state`, `-state-out`, and `-backup`](/docs/language/settings/backends/local.html#command-line-arguments). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/test.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/test.html.md new file mode 100644 index 00000000..7edd1fb1 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/test.html.md @@ -0,0 +1,16 @@ +--- +layout: "docs" +page_title: "Command: test" +sidebar_current: "docs-commands-test" +description: |- + Part of the ongoing design research for module integration testing. +--- + +# Command: test + +The `terraform test` command is currently serving as part of +[the module integration testing experiment](/docs/language/modules/testing-experiment.html). + +It's not ready for routine use, but if you'd be interested in trying the +prototype functionality then we'd love to hear your feedback. See the +experiment details page linked above for more information. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/untaint.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/untaint.html.md new file mode 100644 index 00000000..b505e7ed --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/untaint.html.md @@ -0,0 +1,73 @@ +--- +layout: "docs" +page_title: "Command: untaint" +sidebar_current: "docs-commands-untaint" +description: |- + The `terraform untaint` command tells Terraform that an object is functioning + correctly, even though its creation failed or it was previously manually + marked as degraded. +--- + +# Command: untaint + +Terraform has a marker called "tainted" which it uses to track that an object +might be damaged and so a future Terraform plan ought to replace it. + +Terraform automatically marks an object as "tainted" if an error occurs during +a multi-step "create" action, because Terraform can't be sure that the object +was left in a fully-functional state. + +You can also manually mark an object as "tainted" using the deprecated command +[`terraform taint`](./taint.html), although we no longer recommend that +workflow. + +If Terraform currently considers a particular object as tainted but you've +determined that it's actually functioning correctly and need _not_ be replaced, +you can use `terraform untaint` to remove the taint marker from that object. + +This command _will not_ modify any real remote objects, but will modify the +state in order to remove the tainted status. + +If you remove the taint marker from an object but then later discover that it +was degraded after all, you can create and apply a plan to replace it without +first re-tainting the object, by using a command like the following: + +``` +terraform apply -replace="aws_instance.example[0]" +``` + +## Usage + +Usage: `terraform untaint [options] address` + +The `address` argument is a [resource address](/docs/cli/state/resource-addressing.html) +identifying a particular resource instance which is currently tainted. + +This command also accepts the following options: + +* `-allow-missing` - If specified, the command will succeed (exit code 0) + even if the resource is missing. The command might still return an error + for other situations, such as if there is a problem reading or writing + the state. + +* `-lock=false` - Disables Terraform's default behavior of attempting to take + a read/write lock on the state for the duration of the operation. + +* `-lock-timeout=DURATION` - Unless locking is disabled with `-lock=false`, + instructs Terraform to retry acquiring a lock for a period of time before + returning an error. The duration syntax is a number followed by a time + unit letter, such as "3s" for three seconds. + +* `-no-color` - Disables terminal formatting sequences in the output. Use this + if you are running Terraform in a context where its output will be + rendered by a system that cannot interpret terminal formatting. + +* `-ignore-remote-version` - When using the enhanced remote backend with + Terraform Cloud, continue even if remote and local Terraform versions differ. + This may result in an unusable Terraform Cloud workspace, and should be used + with extreme caution. + +For configurations using +[the `local` backend](/docs/language/settings/backends/local.html) only, +`terraform taint` also accepts the legacy options +[`-state`, `-state-out`, and `-backup`](/docs/language/settings/backends/local.html#command-line-arguments). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/validate.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/validate.html.md new file mode 100644 index 00000000..b6210d69 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/validate.html.md @@ -0,0 +1,214 @@ +--- +layout: "docs" +page_title: "Command: validate" +sidebar_current: "docs-commands-validate" +description: |- + The `terraform validate` command is used to validate the syntax of the terraform files. +--- + +# Command: validate + +The `terraform validate` command validates the configuration files in a +directory, referring only to the configuration and not accessing any remote +services such as remote state, provider APIs, etc. + +Validate runs checks that verify whether a configuration is syntactically +valid and internally consistent, regardless of any provided variables or +existing state. It is thus primarily useful for general verification of +reusable modules, including correctness of attribute names and value types. + +It is safe to run this command automatically, for example as a post-save +check in a text editor or as a test step for a re-usable module in a CI +system. + +Validation requires an initialized working directory with any referenced +plugins and modules installed. To initialize a working directory for +validation without accessing any configured remote backend, use: + +``` +$ terraform init -backend=false +``` + +To verify configuration in the context of a particular run (a particular +target workspace, input variable values, etc), use the `terraform plan` +command instead, which includes an implied validation check. + +## Usage + +Usage: `terraform validate [options]` + +This command accepts the following options: + +- `-json` - Produce output in a machine-readable JSON format, suitable for + use in text editor integrations and other automated systems. Always disables + color. + +- `-no-color` - If specified, output won't contain any color. + +## JSON Output Format + +When you use the `-json` option, Terraform will produce validation results +in JSON format to allow using the validation result for tool integrations, such +as highlighting errors in a text editor. + +As with all JSON output options, it's possible that Terraform will encounter +an error prior to beginning the validation task that will thus not be subject +to the JSON output setting. For that reason, external software consuming +Terraform's output should be prepared to find data on stdout that _isn't_ valid +JSON, which it should then treat as a generic error case. + +**Note:** The output includes a `format_version` key, which currently has major +version zero to indicate that the format is experimental and subject to change. +A future version will assign a non-zero major version and make stronger +promises about compatibility. We do not anticipate any significant breaking +changes to the format before its first major version, however. + +In the normal case, Terraform will print a JSON object to the standard output +stream. The top-level JSON object will have the following properties: + +* `valid` (boolean): Summarizes the overall validation result, by indicating + `true` if Terraform considers the current configuration to be valid or + `false` if it detected any errors. + +* `error_count` (number): A zero or positive whole number giving the count + of errors Terraform detected. If `valid` is `false` then `error_count` will + always be zero, because it is the presence of errors that indicates that + a configuration is invalid. + +* `warning_count` (number): A zero or positive whole number giving the count + of warnings Terraform detected. Warnings do not cause Terraform to consider + a configuration to be invalid, but they do indicate potential caveats that + a user should consider and possibly resolve. + +* `diagnostics` (array of objects): A JSON array of nested objects that each + describe an error or warning from Terraform. + +The nested objects in `diagnostics` have the following properties: + +* `severity` (string): A string keyword, currently either `"error"` or + `"warning"`, indicating the diagnostic severity. + + The presence of errors causes Terraform to consider a configuration to be + invalid, while warnings are just advice or caveats to the user which do not + block working with the configuration. Later versions of Terraform may + introduce new severity keywords, so consumers should be prepared to accept + and ignore severity values they don't understand. + +* `summary` (string): A short description of the nature of the problem that + the diagnostic is reporting. + + In Terraform's usual human-oriented diagnostic messages, the summary serves + as a sort of "heading" for the diagnostic, printed after the "Error:" or + "Warning:" indicator. + + Summaries are typically short, single sentences, but can sometimes be longer + as a result of returning errors from subsystems that are not designed to + return full diagnostics, where the entire error message therefore becomes the + summary. In those cases, the summary might include newline characters which + a renderer should honor when presenting the message visually to a user. + +* `detail` (string): An optional additional message giving more detail about + the problem. + + In Terraform's usual human-oriented diagnostic messages, the detail provides + the paragraphs of text that appear after the heading and the source location + reference. + + Detail messages are often multiple paragraphs and possibly interspersed with + non-paragraph lines, so tools which aim to present detail messages to the + user should distinguish between lines without leading spaces, treating them + as paragraphs, and lines with leading spaces, treating them as preformatted + text. Renderers should then soft-wrap the paragraphs to fit the width of the + rendering container, but leave the preformatted lines unwrapped. + + Some Terraform detail messages currently contain an approximation of bullet + lists using ASCII characters to mark the bullets. This is not currently a + contractural formatting convention and so renderers should avoid depending on + it and should instead treat those lines as either paragraphs or preformatted + text per the rules above. A future version of this format may define some + additional rules for processing other text conventions, but will do so within + the bounds of the rules above to achieve backward-compatibility. + +* `range` (object): An optional object referencing a portion of the configuration + source code that the diagnostic message relates to. For errors, this will + typically indicate the bounds of the specific block header, attribute, or + expression which was detected as invalid. + + A source range is an object with a property `filename` which gives the + filename as a relative path from the current working directory, and then + two properties `start` and `end` which are both themselves objects + describing source positions, as described below. + + Not all diagnostic messages are connected with specific portions of the + configuration, so `range` will be omitted or `null` for diagnostic messages + where it isn't relevant. + +* `snippet` (object): An optional object including an excerpt of the + configuration source code that the diagnostic message relates to. + + The snippet information includes: + + * `context` (string): An optional summary of the root context of the + diagnostic. For example, this might be the resource block containing the + expression which triggered the diagnostic. For some diagnostics this + information is not available, and then this property will be `null`. + + * `code` (string): A snippet of Terraform configuration including the + source of the diagnostic. This can be multiple lines and may include + additional configuration source code around the expression which + triggered the diagnostic. + + * `start_line` (number): A one-based line count representing the position + in the source file at which the `code` excerpt begins. This is not + necessarily the same value as `range.start.line`, as it is possible for + `code` to include one or more lines of context before the source of the + diagnostic. + + * `highlight_start_offset` (number): A zero-based character offset into the + `code` string, pointing at the start of the expression which triggered + the diagnostic. + + * `highlight_end_offset` (number): A zero-based character offset into the + `code` string, pointing at the end of the expression which triggered the + diagnostic. + + * `values` (array of objects): Contains zero or more expression values + which may be useful in understanding the source of a diagnostic in a + complex expression. These expression value objects are described below. + +### Source Position + +A source position object, as used in the `range` property of a diagnostic +object, has the following properties: + +* `byte` (number): A zero-based byte offset into the indicated file. + +* `line` (number): A one-based line count for the line containing the relevant + position in the indicated file. + +* `column` (number): A one-based count of _Unicode characters_ from the start + of the line indicated in `line`. + +A `start` position is inclusive while an `end` position is exclusive. The +exact positions used for particular error messages are intended for human +interpretation only and subject to change in future versions of Terraform due +either to improvements to the error reporting or changes in implementation +details of the language parser/evaluator. + +### Expression Value + +An expression value object gives additional information about a value which is +part of the expression which triggered the diagnostic. This is especially +useful when using `for_each` or similar constructs, in order to identify +exactly which values are responsible for an error. The object has two properties: + +* `traversal` (string): An HCL-like traversal string, such as + `var.instance_count`. Complex index key values may be elided, so this will + not always be valid, parseable HCL. The contents of this string are intended + to be human-readable and are subject to change in future versions of + Terraform. + +* `statement` (string): A short English-language fragment describing the value + of the expression when the diagnostic was triggered. The contents of this + string are intended to be human-readable and are subject to change in future + versions of Terraform. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/version.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/version.html.md new file mode 100644 index 00000000..713bfb6e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/version.html.md @@ -0,0 +1,55 @@ +--- +layout: "docs" +page_title: "Command: version" +sidebar_current: "docs-commands-version" +description: |- + The `terraform version` command displays the version of Terraform and all installed plugins. +--- + +# Command: version + +The `terraform version` displays the current version of Terraform and all +installed plugins. + +## Usage + +Usage: `terraform version [options]` + +With no additional arguments, `version` will display the version of Terraform, +the platform it's installed on, installed providers, and the results of upgrade +and security checks [unless disabled](/docs/cli/commands/index.html#upgrade-and-security-bulletin-checks). + +This command has one optional flag: + +* `-json` - If specified, the version information is formatted as a JSON object, + and no upgrade or security information is included. + +-> **Note:** Platform information was added to the `version` command in Terraform 0.15. + +## Example + +Basic usage, with upgrade and security information shown if relevant: + +```shellsession +$ terraform version +Terraform v0.15.0 +on darwin_amd64 ++ provider registry.terraform.io/hashicorp/null v3.0.0 + +Your version of Terraform is out of date! The latest version +is X.Y.Z. You can update by downloading from https://www.terraform.io/downloads.html +``` + +As JSON: + +```shellsession +$ terraform version -json +{ + "terraform_version": "0.15.0", + "platform": "darwin_amd64", + "provider_selections": { + "registry.terraform.io/hashicorp/null": "3.0.0" + }, + "terraform_outdated": true +} +``` \ No newline at end of file diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/workspace/delete.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/workspace/delete.html.md similarity index 85% rename from vendor/github.com/hashicorp/terraform/website/docs/commands/workspace/delete.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/cli/commands/workspace/delete.html.md index f489009c..3c99cf85 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/workspace/delete.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/workspace/delete.html.md @@ -1,5 +1,5 @@ --- -layout: "commands-workspace" +layout: "docs" page_title: "Command: workspace delete" sidebar_current: "docs-commands-workspace-sub-delete" description: |- @@ -12,7 +12,7 @@ The `terraform workspace delete` command is used to delete an existing workspace ## Usage -Usage: `terraform workspace delete [NAME]` +Usage: `terraform workspace delete [OPTIONS] NAME [DIR]` This command will delete the specified workspace. @@ -30,6 +30,8 @@ from getting into this situation. The command-line flags are all optional. The only supported flag is: * `-force` - Delete the workspace even if its state is not empty. Defaults to false. +* `-lock` - Lock the state file when locking is supported. Defaults to true. +* `-lock-timeout` - Duration to retry a state lock. Default 0s. ## Example diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/workspace/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/workspace/index.html.md new file mode 100644 index 00000000..cf60ded6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/workspace/index.html.md @@ -0,0 +1,21 @@ +--- +layout: "docs" +page_title: "Command: workspace" +sidebar_current: "docs-commands-workspace-index" +description: |- + The terraform workspace command is used to manage workspaces. +--- + +# Command: workspace + +The `terraform workspace` command is used to manage +[workspaces](/docs/language/state/workspaces.html). + +This command is a container for further subcommands. These subcommands are +listed in the navigation bar. + +## Usage + +Usage: `terraform workspace [options] [args]` + +Please choose a subcommand from the navigation for more information. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/workspace/list.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/workspace/list.html.md new file mode 100644 index 00000000..2114a60c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/workspace/list.html.md @@ -0,0 +1,27 @@ +--- +layout: "docs" +page_title: "Command: workspace list" +sidebar_current: "docs-commands-workspace-sub-list" +description: |- + The terraform workspace list command is used to list all existing workspaces. +--- + +# Command: workspace list + +The `terraform workspace list` command is used to list all existing workspaces. + +## Usage + +Usage: `terraform workspace list [DIR]` + +The command will list all existing workspaces. The current workspace is +indicated using an asterisk (`*`) marker. + +## Example + +``` +$ terraform workspace list + default +* development + jsmith-test +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/workspace/new.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/workspace/new.html.md similarity index 75% rename from vendor/github.com/hashicorp/terraform/website/docs/commands/workspace/new.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/cli/commands/workspace/new.html.md index d4147344..0caac8b0 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/workspace/new.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/workspace/new.html.md @@ -1,5 +1,5 @@ --- -layout: "commands-workspace" +layout: "docs" page_title: "Command: workspace new" sidebar_current: "docs-commands-workspace-sub-new" description: |- @@ -12,7 +12,7 @@ The `terraform workspace new` command is used to create a new workspace. ## Usage -Usage: `terraform workspace new [NAME]` +Usage: `terraform workspace new [OPTIONS] NAME [DIR]` This command will create a new workspace with the given name. A workspace with this name must not already exist. @@ -20,9 +20,11 @@ this name must not already exist. If the `-state` flag is given, the state specified by the given path will be copied to initialize the state for this new workspace. -The command-line flags are all optional. The only supported flag is: +The command-line flags are all optional. The supported flags are: -* `-state=path` - Path to a state file to initialize the state of this environment. +* `-lock` - Lock the state file when locking is supported. Defaults to true. +* `-lock-timeout` - Duration to retry a state lock. Default 0s. +* `-state=path` - Path to an existing state file to initialize the state of this environment. ## Example: Create diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/workspace/select.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/workspace/select.html.md similarity index 89% rename from vendor/github.com/hashicorp/terraform/website/docs/commands/workspace/select.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/cli/commands/workspace/select.html.md index 197eb64d..08170a61 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/workspace/select.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/workspace/select.html.md @@ -1,5 +1,5 @@ --- -layout: "commands-workspace" +layout: "docs" page_title: "Command: workspace select" sidebar_current: "docs-commands-workspace-sub-select" description: |- @@ -13,7 +13,7 @@ workspace to use for further operations. ## Usage -Usage: `terraform workspace select [NAME]` +Usage: `terraform workspace select NAME [DIR]` This command will select another workspace. The named workspace must already exist. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/workspace/show.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/workspace/show.html.md new file mode 100644 index 00000000..7cd7d5b5 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/commands/workspace/show.html.md @@ -0,0 +1,24 @@ +--- +layout: "docs" +page_title: "Command: workspace show" +sidebar_current: "docs-commands-workspace-sub-show" +description: |- + The terraform workspace show command is used to output the current workspace. +--- + +# Command: workspace show + +The `terraform workspace show` command is used to output the current workspace. + +## Usage + +Usage: `terraform workspace show` + +The command will display the current workspace. + +## Example + +``` +$ terraform workspace show +development +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/config/config-file.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/config/config-file.html.md new file mode 100644 index 00000000..8dce0166 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/config/config-file.html.md @@ -0,0 +1,451 @@ +--- +layout: "docs" +page_title: "CLI Configuration" +sidebar_current: "docs-commands-cli-config" +description: |- + The general behavior of the Terraform CLI can be customized using the CLI + configuration file. +--- + +# CLI Configuration File (`.terraformrc` or `terraform.rc`) + +The CLI configuration file configures per-user settings for CLI behaviors, +which apply across all Terraform working directories. This is separate from +[your infrastructure configuration](/docs/language/index.html). + +## Location + +The configuration is placed in a single file whose location depends on the +host operating system: + +* On Windows, the file must be named `terraform.rc` and placed + in the relevant user's `%APPDATA%` directory. The physical location + of this directory depends on your Windows version and system configuration; + use `$env:APPDATA` in PowerShell to find its location on your system. +* On all other systems, the file must be named `.terraformrc` (note + the leading period) and placed directly in the home directory + of the relevant user. + +On Windows, beware of Windows Explorer's default behavior of hiding filename +extensions. Terraform will not recognize a file named `terraform.rc.txt` as a +CLI configuration file, even though Windows Explorer may _display_ its name +as just `terraform.rc`. Use `dir` from PowerShell or Command Prompt to +confirm the filename. + +The location of the Terraform CLI configuration file can also be specified +using the `TF_CLI_CONFIG_FILE` [environment variable](/docs/cli/config/environment-variables.html). + +## Configuration File Syntax + +The configuration file uses the same _HCL_ syntax as `.tf` files, but with +different attributes and blocks. The following example illustrates the +general syntax; see the following section for information on the meaning +of each of these settings: + +```hcl +plugin_cache_dir = "$HOME/.terraform.d/plugin-cache" +disable_checkpoint = true +``` + +## Available Settings + +The following settings can be set in the CLI configuration file: + +- `credentials` - configures credentials for use with Terraform Cloud or + Terraform Enterprise. See [Credentials](#credentials) below for more + information. + +- `credentials_helper` - configures an external helper program for the storage + and retrieval of credentials for Terraform Cloud or Terraform Enterprise. + See [Credentials Helpers](#credentials-helpers) below for more information. + +- `disable_checkpoint` — when set to `true`, disables + [upgrade and security bulletin checks](/docs/cli/commands/index.html#upgrade-and-security-bulletin-checks) + that require reaching out to HashiCorp-provided network services. + +- `disable_checkpoint_signature` — when set to `true`, allows the upgrade and + security bulletin checks described above but disables the use of an anonymous + id used to de-duplicate warning messages. + +- `plugin_cache_dir` — enables + [plugin caching](#provider-plugin-cache) + and specifies, as a string, the location of the plugin cache directory. + +- `provider_installation` - customizes the installation methods used by + `terraform init` when installing provider plugins. See + [Provider Installation](#provider-installation) below for more information. + +## Credentials + +[Terraform Cloud](/docs/cloud/index.html) provides a number of remote network +services for use with Terraform, and +[Terraform Enterprise](/docs/enterprise/index.html) allows hosting those +services inside your own infrastructure. For example, these systems offer both +[remote operations](/docs/cloud/run/cli.html) and a +[private module registry](/docs/cloud/registry/index.html). + +When interacting with Terraform-specific network services, Terraform expects +to find API tokens in CLI configuration files in `credentials` blocks: + +```hcl +credentials "app.terraform.io" { + token = "xxxxxx.atlasv1.zzzzzzzzzzzzz" +} +``` + +If you are running the Terraform CLI interactively on a computer with a web browser, you can use [the `terraform login` command](/docs/cli/commands/login.html) +to get credentials and automatically save them in the CLI configuration. If +not, you can manually write `credentials` blocks. + +You can have multiple `credentials` blocks if you regularly use services from +multiple hosts. Many users will configure only one, for either +Terraform Cloud (at `app.terraform.io`) or for their organization's own +Terraform Enterprise host. Each `credentials` block contains a `token` argument +giving the API token to use for that host. + +~> **Important:** If you are using Terraform Cloud or Terraform Enterprise, +the token provided must be either a +[user token](/docs/cloud/users-teams-organizations/users.html#api-tokens) +or a +[team token](/docs/cloud/users-teams-organizations/api-tokens.html#team-api-tokens); +organization tokens cannot be used for command-line Terraform actions. + +-> **Note:** The credentials hostname must match the hostname in your module +sources and/or backend configuration. If your Terraform Enterprise instance +is available at multiple hostnames, use only one of them consistently. +Terraform Cloud responds to API calls at both its current hostname +`app.terraform.io`, and its historical hostname `atlas.hashicorp.com`. + +### Credentials Helpers + +If you would prefer not to store your API tokens directly in the CLI +configuration as described in the previous section, you can optionally instruct +Terraform to use a different credentials storage mechanism by configuring a +special kind of plugin program called a _credentials helper_. + +```hcl +credentials_helper "example" { + args = [] +} +``` + +`credentials_helper` is a configuration block that can appear at most once +in the CLI configuration. Its label (`"example"` above) is the name of the +credentials helper to use. The `args` argument is optional and allows passing +additional arguments to the helper program, for example if it needs to be +configured with the address of a remote host to access for credentials. + +A configured credentials helper will be consulted only to retrieve credentials +for hosts that are _not_ explicitly configured in a `credentials` block as +described in the previous section. +Conversely, this means you can override the credentials returned by the helper +for a specific hostname by writing a `credentials` block alongside the +`credentials_helper` block. + +Terraform does not include any credentials helpers in the main distribution. +To learn how to write and install your own credentials helpers to integrate +with existing in-house credentials management systems, see +[the guide to Credentials Helper internals](/docs/internals/credentials-helpers.html). + +## Provider Installation + +The default way to install provider plugins is from a provider registry. The +origin registry for a provider is encoded in the provider's source address, +like `registry.terraform.io/hashicorp/aws`. For convenience in the common case, +Terraform allows omitting the hostname portion for providers on +`registry.terraform.io`, so you can write shorter public provider addresses like +`hashicorp/aws`. + +Downloading a plugin directly from its origin registry is not always +appropriate, though. For example, the system where you are running Terraform +may not be able to access an origin registry due to firewall restrictions +within your organization or your locality. + +To allow using Terraform providers in these situations, there are some +alternative options for making provider plugins available to Terraform which +we'll describe in the following sections. + +### Explicit Installation Method Configuration + +A `provider_installation` block in the CLI configuration allows overriding +Terraform's default installation behaviors, so you can force Terraform to use +a local mirror for some or all of the providers you intend to use. + +The general structure of a `provider_installation` block is as follows: + +```hcl +provider_installation { + filesystem_mirror { + path = "/usr/share/terraform/providers" + include = ["example.com/*/*"] + } + direct { + exclude = ["example.com/*/*"] + } +} +``` + +Each of the nested blocks inside the `provider_installation` block specifies +one installation method. Each installation method can take both `include` +and `exclude` patterns that specify which providers a particular installation +method can be used for. In the example above, we specify that any provider +whose origin registry is at `example.com` can be installed only from the +filesystem mirror at `/usr/share/terraform/providers`, while all other +providers can be installed only directly from their origin registries. + +If you set both `include` and `exclude` for a particular installation +method, the exclusion patterns take priority. For example, including +`registry.terraform.io/hashicorp/*` but also excluding +`registry.terraform.io/hashicorp/dns` will make that installation method apply +to everything in the `hashicorp` namespace with the exception of +`hashicorp/dns`. + +As with provider source addresses in the main configuration, you can omit +the `registry.terraform.io/` prefix for providers distributed through the +public Terraform registry, even when using wildcards. For example, +`registry.terraform.io/hashicorp/*` and `hashicorp/*` are equivalent. +`*/*` is a shorthand for `registry.terraform.io/*/*`, not for +`*/*/*`. + +The following are the two supported installation method types: + +* `direct`: request information about the provider directly from its origin + registry and download over the network from the location that registry + indicates. This method expects no additional arguments. + +* `filesystem_mirror`: consult a directory on the local disk for copies of + providers. This method requires the additional argument `path` to indicate + which directory to look in. + + Terraform expects the given directory to contain a nested directory structure + where the path segments together provide metadata about the available + providers. The following two directory structures are supported: + + * Packed layout: `HOSTNAME/NAMESPACE/TYPE/terraform-provider-TYPE_VERSION_TARGET.zip` + is the distribution zip file obtained from the provider's origin registry. + * Unpacked layout: `HOSTNAME/NAMESPACE/TYPE/VERSION/TARGET` is a directory + containing the result of extracting the provider's distribution zip file. + + In both layouts, the `VERSION` is a string like `2.0.0` and the `TARGET` + specifies a particular target platform using a format like `darwin_amd64`, + `linux_arm`, `windows_amd64`, etc. + + If you use the unpacked layout, Terraform will attempt to create a symbolic + link to the mirror directory when installing the provider, rather than + creating a deep copy of the directory. The packed layout prevents this + because Terraform must extract the zip file during installation. + + You can include multiple `filesystem_mirror` blocks in order to specify + several different directories to search. + +* `network_mirror`: consult a particular HTTPS server for copies of providers, + regardless of which registry host they belong to. This method requires the + additional argument `url` to indicate the mirror base URL, which should + use the `https:` scheme and end with a trailing slash. + + Terraform expects the given URL to be a base URL for an implementation of + [the provider network mirror protocol](/docs/internals/provider-network-mirror-protocol.html), + which is designed to be relatively easy to implement using typical static + website hosting mechanisms. + +~> **Warning:** Don't configure `network_mirror` URLs that you do not trust. +Provider mirror servers are subject to TLS certificate checks to verify +identity, but a network mirror with a TLS certificate can potentially serve +modified copies of upstream providers with malicious content. + +Terraform will try all of the specified methods whose include and exclude +patterns match a given provider, and select the newest version available across +all of those methods that matches the version constraint given in each +Terraform configuration. If you have a local mirror of a particular provider +and intend Terraform to use that local mirror exclusively, you must either +remove the `direct` installation method altogether or use its `exclude` +argument to disable its use for specific providers. + +### Implied Local Mirror Directories + +If your CLI configuration does not include a `provider_installation` block at +all, Terraform produces an _implied_ configuration. The implied configuration +includes a selection of `filesystem_mirror` methods and then the `direct` +method. + +The set of directories Terraform can select as filesystem mirrors depends on +the operating system where you are running Terraform: + +* **Windows:** `%APPDATA%/terraform.d/plugins` and `%APPDATA%/HashiCorp/Terraform/plugins` +* **Mac OS X:** `$HOME/.terraform.d/plugins/`, + `~/Library/Application Support/io.terraform/plugins`, and + `/Library/Application Support/io.terraform/plugins` +* **Linux and other Unix-like systems**:`$HOME/.terraform.d/plugins/`, and + [XDG Base Directory](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) + data directories as configured, after appending `terraform/plugins`. + Without any XDG environment variables set, Terraform will use + `~/.local/share/terraform/plugins`, + `/usr/local/share/terraform/plugins`, and `/usr/share/terraform/plugins`. + +If a `terraform.d/plugins` directory exists in the current working directory +then Terraform will also include that directory, regardless of your operating +system. + +Terraform will check each of the paths above to see if it exists, and if so +treat it as a filesystem mirror. The directory structure inside each one must +therefore match one of the two structures described for `filesystem_mirror` +blocks in [Explicit Installation Method Configuration](#explicit-installation-method-configuration). + +In addition to the zero or more implied `filesystem_mirror` blocks, Terraform +also creates an implied `direct` block. Terraform will scan all of the +filesystem mirror directories to see which providers are placed there and +automatically exclude all of those providers from the implied `direct` block. +(This automatic `exclude` behavior applies only to _implicit_ `direct` blocks; +if you use explicit `provider_installation` you will need to write the intended +exclusions out yourself.) + +### Provider Plugin Cache + +By default, `terraform init` downloads plugins into a subdirectory of the +working directory so that each working directory is self-contained. As a +consequence, if you have multiple configurations that use the same provider +then a separate copy of its plugin will be downloaded for each configuration. + +Given that provider plugins can be quite large (on the order of hundreds of +megabytes), this default behavior can be inconvenient for those with slow +or metered Internet connections. Therefore Terraform optionally allows the +use of a local directory as a shared plugin cache, which then allows each +distinct plugin binary to be downloaded only once. + +To enable the plugin cache, use the `plugin_cache_dir` setting in +the CLI configuration file. For example: + +```hcl +plugin_cache_dir = "$HOME/.terraform.d/plugin-cache" +``` + +This directory must already exist before Terraform will cache plugins; +Terraform will not create the directory itself. + +Please note that on Windows it is necessary to use forward slash separators +(`/`) rather than the conventional backslash (`\`) since the configuration +file parser considers a backslash to begin an escape sequence. + +Setting this in the configuration file is the recommended approach for a +persistent setting. Alternatively, the `TF_PLUGIN_CACHE_DIR` environment +variable can be used to enable caching or to override an existing cache +directory within a particular shell session: + +```bash +export TF_PLUGIN_CACHE_DIR="$HOME/.terraform.d/plugin-cache" +``` + +When a plugin cache directory is enabled, the `terraform init` command will +still use the configured or implied installation methods to obtain metadata +about which plugins are available, but once a suitable version has been +selected it will first check to see if the chosen plugin is already available +in the cache directory. If so, Terraform will use the previously-downloaded +copy. + +If the selected plugin is not already in the cache, Terraform will download +it into the cache first and then copy it from there into the correct location +under your current working directory. When possible Terraform will use +symbolic links to avoid storing a separate copy of a cached plugin in multiple +directories. + +The plugin cache directory _must not_ also be one of the configured or implied +filesystem mirror directories, since the cache management logic conflicts with +the filesystem mirror logic when operating on the same directory. + +Terraform will never itself delete a plugin from the plugin cache once it has +been placed there. Over time, as plugins are upgraded, the cache directory may +grow to contain several unused versions which you must delete manually. + +### Development Overrides for Provider Developers + +-> **Note:** Development overrides work only in Terraform v0.14 and later. +Using a `dev_overrides` block in your CLI configuration will cause Terraform +v0.13 to reject the configuration as invalid. + +Normally Terraform verifies version selections and checksums for providers +in order to help ensure that all operations are made with the intended version +of a provider, and that authors can gradually upgrade to newer provider versions +in a controlled manner. + +These version and checksum rules are inconvenient when developing a provider +though, because we often want to try a test configuration against a development +build of a provider that doesn't even have an associated version number yet, +and doesn't have an official set of checksums listed in a provider registry. + +As a convenience for provider development, Terraform supports a special +additional block `dev_overrides` in `provider_installation` blocks. The contents +of this block effectively override all of the other configured installation +methods, so a block of this type must always appear first in the sequence: + +```hcl +provider_installation { + + # Use /home/developer/tmp/terraform-null as an overridden package directory + # for the hashicorp/null provider. This disables the version and checksum + # verifications for this provider and forces Terraform to look for the + # null provider plugin in the given directory. + dev_overrides { + "hashicorp/null" = "/home/developer/tmp/terraform-null" + } + + # For all other providers, install them directly from their origin provider + # registries as normal. If you omit this, Terraform will _only_ use + # the dev_overrides block, and so no other providers will be available. + direct {} +} +``` + +With development overrides in effect, the `terraform init` command will still +attempt to select a suitable published version of your provider to install and +record in +[the dependency lock file](/docs/language/dependency-lock.html) +for future use, but other commands like +`terraform apply` will disregard the lock file's entry for `hashicorp/null` and +will use the given directory instead. Once your new changes are included in a +published release of the provider, you can use `terraform init -upgrade` to +select the new version in the dependency lock file and remove your development +override. + +The override path for a particular provider should be a directory similar to +what would be included in a `.zip` file when distributing the provider. At +minimum that includes an executable file named with a prefix like +`terraform-provider-null`, where `null` is the provider type. If your provider +makes use of other files in its distribution package then you can copy those +files into the override directory too. + +You may wish to enable a development override only for shell sessions where +you are actively working on provider development. If so, you can write a +local CLI configuration file with content like the above in your development +directory, perhaps called `dev.tfrc` for the sake of example, and then use the +`TF_CLI_CONFIG_FILE` environment variable to instruct Terraform to use that +localized CLI configuration instead of the default one: + +``` +export TF_CLI_CONFIG_FILE=/home/developer/tmp/dev.tfrc +``` + +Development overrides are not intended for general use as a way to have +Terraform look for providers on the local filesystem. If you wish to put +copies of _released_ providers in your local filesystem, see +[Implied Local Mirror Directories](#implied-local-mirror-directories) +or +[Explicit Installation Method Configuration](#explicit-installation-method-configuration) +instead. + +This development overrides mechanism is intended as a pragmatic way to enable +smoother provider development. The details of how it behaves, how to +configure it, and how it interacts with the dependency lock file may all evolve +in future Terraform releases, including possible breaking changes. We therefore +recommend using development overrides only temporarily during provider +development work. + +## Removed Settings + +The following settings are supported in Terraform 0.12 and earlier but are +no longer recommended for use: + +* `providers` - a configuration block that allows specifying the locations of + specific plugins for each named provider. This mechanism is deprecated + because it is unable to specify a version number and source for each provider. + See [Provider Installation](#provider-installation) above for the replacement + of this setting in Terraform 0.13 and later. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/environment-variables.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/config/environment-variables.html.md similarity index 90% rename from vendor/github.com/hashicorp/terraform/website/docs/commands/environment-variables.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/cli/config/environment-variables.html.md index e0784802..891f8d23 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/environment-variables.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/config/environment-variables.html.md @@ -16,16 +16,16 @@ for debugging. ## TF_LOG -If set to any value, enables detailed logs to appear on stderr which is useful for debugging. For example: +Enables detailed logs to appear on stderr which is useful for debugging. For example: ```shell -export TF_LOG=TRACE +export TF_LOG=trace ``` -To disable, either unset it or set it to empty. When unset, logging will default to stderr. For example: +To disable, either unset it, or set it to `off`. For example: ```shell -export TF_LOG= +export TF_LOG=off ``` For more on debugging Terraform, check out the section on [Debugging](/docs/internals/debugging.html). @@ -59,7 +59,7 @@ export TF_VAR_alist='[1,2,3]' export TF_VAR_amap='{ foo = "bar", baz = "qux" }' ``` -For more on how to use `TF_VAR_name` in context, check out the section on [Variable Configuration](/docs/configuration/variables.html). +For more on how to use `TF_VAR_name` in context, check out the section on [Variable Configuration](/docs/language/values/variables.html). ## TF_CLI_ARGS and TF_CLI_ARGS_name @@ -114,7 +114,7 @@ export TF_WORKSPACE=your_workspace Using this environment variable is recommended only for non-interactive usage, since in a local shell environment it can be easy to forget the variable is set and apply changes to the wrong state. For more information regarding workspaces, check out the section on [Using Workspaces] -(https://www.terraform.io/docs/state/workspaces.html). +(https://www.terraform.io/docs/language/state/workspaces.html). ## TF_IN_AUTOMATION @@ -145,7 +145,7 @@ export TF_REGISTRY_CLIENT_TIMEOUT=15 ## TF_CLI_CONFIG_FILE -The location of the [Terraform CLI configuration file](/docs/commands/cli-config.html). +The location of the [Terraform CLI configuration file](/docs/cli/config/config-file.html). ```shell export TF_CLI_CONFIG_FILE="$HOME/.terraformrc-custom" @@ -159,4 +159,4 @@ If `TF_IGNORE` is set to "trace", Terraform will output debug messages to displa export TF_IGNORE=trace ``` -For more details on `.terraformignore`, please see [Excluding Files from Upload with .terraformignore](/docs/backends/types/remote.html#excluding-files-from-upload-with-terraformignore). +For more details on `.terraformignore`, please see [Excluding Files from Upload with .terraformignore](/docs/language/settings/backends/remote.html#excluding-files-from-upload-with-terraformignore). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/config/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/config/index.html.md new file mode 100644 index 00000000..dbac33b5 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/config/index.html.md @@ -0,0 +1,22 @@ +--- +layout: "docs" +page_title: "CLI Configuration - Terraform CLI" +--- + +# CLI Configuration + +Terraform CLI can be configured with some global settings, which are separate +from any Terraform configuration and which apply across all working directories. + +We've designed Terraform such that an average user running Terraform CLI +interactively will not need to interact with any of these settings. As a result, +most of the global settings relate to advanced or automated workflows, or +unusual environmental conditions like running Terraform on an airgapped +instance. + +- The [CLI config file](/docs/cli/config/config-file.html) configures provider + installation and security features. +- Several [environment variables](/docs/cli/config/environment-variables.html) can + configure Terraform's inputs and outputs; this includes some alternate ways to + provide information that is usually passed on the command line or read from + the state of the shell. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/import/importability.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/import/importability.html.md similarity index 88% rename from vendor/github.com/hashicorp/terraform/website/docs/import/importability.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/cli/import/importability.html.md index dde7cb68..79d78d90 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/import/importability.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/import/importability.html.md @@ -20,5 +20,5 @@ Converting a resource to be importable is also relatively simple, so if you're interested in contributing that functionality, the Terraform team would be grateful. -To make a resource importable, please see the -[plugin documentation on writing a resource](/docs/plugins/provider.html). +To make a resource importable, please see +[Extending Terraform: Resources — Import](/docs/extend/resources/import.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/import/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/import/index.html.md new file mode 100644 index 00000000..10eb64fb --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/import/index.html.md @@ -0,0 +1,53 @@ +--- +layout: "docs" +page_title: "Import" +sidebar_current: "docs-import" +description: |- + Terraform is able to import existing infrastructure. This allows you take + resources you've created by some other means and bring it under Terraform + management. +--- + +# Import + +> **Hands-on:** Try the [Import Terraform Configuration](https://learn.hashicorp.com/tutorials/terraform/state-import?in=terraform/state&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +Terraform is able to import existing infrastructure. This allows you take +resources you've created by some other means and bring it under Terraform +management. + +This is a great way to slowly transition infrastructure to Terraform, or +to be able to be confident that you can use Terraform in the future if it +potentially doesn't support every feature you need today. + +~> Warning: Terraform expects that each remote object it is managing will be +bound to only one resource address, which is normally guaranteed by Terraform +itself having created all objects. If you import existing objects into Terraform, +be careful to import each remote object to only one Terraform resource address. +If you import the same object multiple times, Terraform may exhibit unwanted +behavior. For more information on this assumption, see +[the State section](/docs/language/state/index.html). + +## Currently State Only + +The current implementation of Terraform import can only import resources +into the [state](/docs/language/state/index.html). It does not generate configuration. A future +version of Terraform will also generate configuration. + +Because of this, prior to running `terraform import` it is necessary to write +manually a `resource` configuration block for the resource, to which the +imported object will be mapped. + +While this may seem tedious, it still gives Terraform users an avenue for +importing existing resources. + +## Remote Backends + +When using Terraform import on the command line with a [remote +backend](/docs/language/settings/backends/remote.html), such as Terraform Cloud, the import +command runs locally, unlike commands such as apply, which run inside your +Terraform Cloud environment. Because of this, the import command will not have +access to information from the remote backend, such as workspace variables. + +In order to use Terraform import with a remote state backend, you may need to +set local variables equivalent to the remote workspace variables. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/import/usage.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/import/usage.html.md similarity index 95% rename from vendor/github.com/hashicorp/terraform/website/docs/import/usage.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/cli/import/usage.html.md index 57fdb4d7..4fdc5452 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/import/usage.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/import/usage.html.md @@ -23,7 +23,7 @@ itself having created all objects. If you import existing objects into Terraform be careful to import each remote object to only one Terraform resource address. If you import the same object multiple times, Terraform may exhibit unwanted behavior. For more information on this assumption, see -[the State section](/docs/state/). +[the State section](/docs/language/state/index.html). To import a resource, first write a resource block for it in your configuration, establishing the name by which it will be known to Terraform: @@ -57,7 +57,7 @@ Terraform state. It is also possible to import to resources in child modules, using their paths, and to single instances of a resource with `count` or `for_each` set. See -[_Resource Addressing_](/docs/internals/resource-addressing.html) for more +[_Resource Addressing_](/docs/cli/state/resource-addressing.html) for more details on how to specify a target resource. The syntax of the given ID is dependent on the resource type being imported. @@ -83,4 +83,4 @@ a `resource` block in configuration for each secondary resource. If this is not done, Terraform will plan to destroy the imported objects on the next run. If you want to rename or otherwise move the imported resources, the -[state management commands](/docs/commands/state/index.html) can be used. +[state management commands](/docs/cli/commands/state/index.html) can be used. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/index.html.md new file mode 100644 index 00000000..6d66974e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/index.html.md @@ -0,0 +1,20 @@ +--- +layout: "docs" +page_title: "Terraform CLI Documentation" +sidebar_current: "docs-home" +description: |- + Documentation for Terraform's CLI-based workflows. +--- + +# Terraform CLI Documentation + +> **Hands-on:** Try the [Terraform: Get Started](https://learn.hashicorp.com/collections/terraform/aws-get-started?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. + +This is the documentation for Terraform CLI. It is relevant to anyone working +with Terraform's CLI-based workflows; this includes people who use Terraform CLI +by itself, as well as those who use Terraform CLI in conjunction with Terraform +Cloud or Terraform Enterprise. + +Notably, this documentation does not cover the syntax and usage of the Terraform +language. For that, see the +[Terraform Language Documentation](/docs/language/index.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/init/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/init/index.html.md new file mode 100644 index 00000000..ee6372b0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/init/index.html.md @@ -0,0 +1,71 @@ +--- +layout: "docs" +page_title: "Initializing Working Directories - Terraform CLI" +--- + +# Initializing Working Directories + +Terraform expects to be invoked from a working directory that contains +configuration files written in +[the Terraform language](/docs/language/index.html). Terraform uses +configuration content from this directory, and also uses the directory to store +settings, cached plugins and modules, and sometimes state data. + +A working directory must be initialized before Terraform can perform any +operations in it (like provisioning infrastructure or modifying state). + +## Working Directory Contents + +A Terraform working directory typically contains: + +- A Terraform configuration describing resources Terraform should manage. This + configuration is expected to change over time. +- A hidden `.terraform` directory, which Terraform uses to manage cached + provider plugins and modules, record which + [workspace](/docs/cli/workspaces/index.html) is currently active, and + record the last known backend configuration in case it needs to migrate state + on the next run. This directory is automatically managed by Terraform, and is + created during initialization. +- State data, if the configuration uses the default `local` backend. This is + managed by Terraform in a `terraform.tfstate` file (if the directory only uses + the default workspace) or a `terraform.tfstate.d` directory (if the directory + uses multiple workspaces). + +## Initialization + +Run the `terraform init` command to initialize a working directory that contains +a Terraform configuration. After initialization, you will be able to perform +other commands, like `terraform plan` and `terraform apply`. + +If you try to run a command that relies on initialization without first +initializing, the command will fail with an error and explain that you need to +run init. + +Initialization performs several tasks to prepare a directory, including +accessing state in the configured backend, downloading and installing provider +plugins, and downloading modules. Under some conditions (usually when changing +from one backend to another), it might ask the user for guidance or +confirmation. + +For details, see [the `terraform init` command](/docs/cli/commands/init.html). + +## Reinitialization + +Certain types of changes to a Terraform configuration can require +reinitialization before normal operations can continue. This includes changes to +provider requirements, module sources or version constraints, and backend +configurations. + +You can reinitialize a directory by running `terraform init` again. In fact, you +can reinitialize at any time; the init command is idempotent, and will have no +effect if no changes are required. + +If reinitialization is required, any commands that rely on initialization will +fail with an error and tell you so. + +## Reinitializing Only Modules + +The `terraform get` command will download modules referenced in the +configuration, but will not perform the other required initialization tasks. +This command is only useful for niche workflows, and most Terraform users can +ignore it in favor of `terraform init`. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/inspect/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/inspect/index.html.md new file mode 100644 index 00000000..f704c039 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/inspect/index.html.md @@ -0,0 +1,33 @@ +--- +layout: "docs" +page_title: "Inspecting Infrastructure - Terraform CLI" +--- + +# Inspecting Infrastructure + +Terraform configurations and state data include some highly structured +information about the resources they manage; this includes dependency +information, outputs (which are pieces of generated or discovered data that the +configuration's author considers important enough to surface to users), and +more. + +Terraform CLI includes some commands for inspecting or transforming this data. +You can use these to integrate other tools with Terraform's infrastructure data, +or just to gain a deeper or more holistic understanding of your infrastructure. + +- [The `terraform graph` command](/docs/cli/commands/graph.html) creates a visual + representation of a configuration or a set of planned changes. +- [The `terraform output` command](/docs/cli/commands/output.html) can get the + values for the top-level [output values](/docs/language/values/outputs.html) of + a configuration, which are often helpful when making use of the infrastructure + Terraform has provisioned. +- [The `terraform show` command](/docs/cli/commands/show.html) can generate + human-readable versions of a state file or plan file, or generate + machine-readable versions that can be integrated with other tools. +- [The `terraform state list` command](/docs/cli/commands/state/list.html) can list + the resources being managed by the current working directory and workspace, + providing a complete or filtered list. +- [The `terraform state show` command](/docs/cli/commands/state/show.html) can print + all of the attributes of a given resource being managed by the current working + directory and workspace, including generated read-only attributes like the + unique ID assigned by the cloud provider. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/install/apt.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/install/apt.html.md new file mode 100644 index 00000000..d55effb4 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/install/apt.html.md @@ -0,0 +1,134 @@ +--- +layout: "downloads" +page_title: "APT Packages for Debian and Ubuntu" +sidebar_current: "docs-cli-install-apt" +description: |- + The HashiCorp APT repositories contain distribution-specific Terraform packages for both Debian and Ubuntu systems. +--- + +# APT Packages for Debian and Ubuntu + +The primary distribution packages for Terraform are `.zip` archives containing +single executable files that you can extract anywhere on your system. However, +for easier integration with configuration management tools and other systematic +system configuration strategies, we also offer package repositories for +Debian and Ubuntu systems, which allow you to install Terraform using the +`apt install` command or any other APT frontend. + +If you are instead using Red Hat Enterprise Linux, CentOS, or Fedora, you +might prefer to [install Terraform from our Yum repositories](yum.html). + +-> **Note:** The APT repositories discussed on this page are generic HashiCorp +repositories that contain packages for a variety of different HashiCorp +products, rather than just Terraform. Adding these repositories to your +system will, by default, therefore make a number of other non-Terraform +packages available for installation. That might then mask some packages that +are available for some HashiCorp products in the main Debian and Ubuntu +package repositories. + +## Repository Configuration + +The Terraform packages are signed using a private key controlled by HashiCorp, +so in most situations the first step would be to configure your system to trust +that HashiCorp key for package authentication. For example: + +```bash +curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add - +``` + +After registering the key, you can add the official HashiCorp repository to +your system: + +```bash +sudo apt-add-repository "deb [arch=$(dpkg --print-architecture)] https://apt.releases.hashicorp.com $(lsb_release -cs) main" +``` + +The above command line uses the following sub-shell commands: + +* `dpkg --print-architecture` to determine your system's primary APT + architecture/ABI, such as `amd64`. +* `lsb_release -cs` to find the distribution release codename for your current + system, such as `buster`, `groovy`, or `sid`. + +`apt-add-repository` usually automatically runs `apt update` as part of its +work in order to fetch the new package indices, but if it does not then you +will need to so manually before the packages will be available. + +To install Terraform from the new repository: + +```bash +sudo apt install terraform +``` + +## Supported Architectures + +The HashiCorp APT server currently has packages only for the `amd64` +architecture, which is also sometimes known as `x86_64`. + +There are no official packages available for other architectures, such as +`arm64`. If you wish to use Terraform on a non-`amd64` system, +[download a normal release `.zip` file](/downloads.html) instead. + +## Supported Debian and Ubuntu Releases + +The HashiCorp APT server currently contains release repositories for the +following distribution releases: + +* Debian 8 (`jessie`) +* Debian 9 (`stretch`) +* Debian 10 (`buster`) +* Ubuntu 16.04 (`xenial`) +* Ubuntu 18.04 (`bionic`) +* Ubuntu 19.10 (`eoam`) +* Ubuntu 20.04 (`focal`) +* Ubuntu 20.10 (`groovy`) + +No repositories are available for other Debian or Ubuntu versions or for +any other APT-based Linux distributions. If you add the repository using +the above commands on other systems then `apt update` will report the +repository index as missing. + +Terraform executables are statically linked and so they depend only on the +Linux system call interface, not on any system libraries. Because of that, +you may be able to use one of the above release codenames when adding a +repository to your system, even if that codename doesn't match your current +distribution release. + +Over time we will change the set of supported distributions, including both +adding support for new releases and ceasing to publish new Terraform versions +under older releases. + +## Choosing Terraform Versions + +The HashiCorp APT repositories contain multiple versions of Terraform, but +because the packages are all named `terraform` it is impossible to install +more than one version at a time, and `apt install` will default to selecting +the latest version. + +It's often necessary to match your Terraform version with what a particular +configuration is currently expecting. You can use the following command to +see which versions are currently available in the repository index: + +```bash +apt policy terraform +``` + +There may be multiple package releases for a particular Terraform version if +we need to publish an updated package for any reason. In that case, the +subsequent releases will have an additional suffix, like `0.13.4-2`. In these +cases the Terraform executable inside the package should be unchanged, but its +metadata and other contents may be different. + +You can select a specific version to install by including it in the +`apt install` command line, as follows: + +```bash +sudo apt install terraform=0.14.0 +``` + +If your workflow requires using multiple versions of Terraform at the same +time, for example when working through a gradual upgrade where not all +of your configurations are upgraded yet, we recommend that you use the +official release `.zip` files instead of the APT packages, so you can install +multiple versions at once and then select which to use for each command you +run. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/install/yum.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/install/yum.html.md new file mode 100644 index 00000000..45e88e0e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/install/yum.html.md @@ -0,0 +1,121 @@ +--- +layout: "downloads" +page_title: "Yum Packages for Red Hat Enterprise Linux, Fedora, and Amazon Linux" +sidebar_current: "docs-cli-install-yum" +description: |- + The HashiCorp Yum repositories contain distribution-specific Terraform packages for Red Hat Enterprise Linux, Fedora, and Amazon Linux systems. +--- + +# Yum/DNF Packages for RHEL, CentOS, and Fedora + +The primary distribution packages for Terraform are `.zip` archives containing +single executable files that you can extract anywhere on your system. However, +for easier integration with configuration management tools and other systematic +system configuration strategies, we also offer package repositories for +RedHat Enterprise Linux, Fedora, and Amazon Linux systems, which allow you to +install Terraform using the `yum install` or `dnf install` commands. + +If you are instead using Debian or Ubuntu, you +might prefer to [install Terraform from our APT repositories](apt.html). + +-> **Note:** The Yum repositories discussed on this page are generic HashiCorp +repositories that contain packages for a variety of different HashiCorp +products, rather than just Terraform. Adding these repositories to your +system will, by default, therefore make a number of other non-Terraform +packages available for installation. That might then mask the packages that are +available for some HashiCorp products in the main distribution repositories. + +## Repository Configuration + +Before adding a repository you must determine which distribution you are using. +The following command lines refer to a placeholder variable `$release` which +you must replace with the appropriate value from the following list: + +* Red Hat Enterprise Linux: `RHEL` +* Fedora: `fedora` +* Amazon Linux: `AmazonLinux` + +If you are using a Yum-based distribution, add the repository using +`yum-config-manager` as follows: + +```bash +sudo yum install -y yum-utils +sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/$release/hashicorp.repo +``` + +If you are using a DNF-based distribution, add the repository using +`dnf config-manager` as follows: + +```bash +sudo dnf install -y dnf-plugins-core +sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/$release/hashicorp.repo +``` + +In both cases, the Terraform package name is `terraform`. For example: + +```bash +yum install terraform +``` + +## Supported Architectures + +The HashiCorp Yum/DNF server currently has packages only for the `x86_64` +architecture, which is also sometimes known as `amd64`. + +There are no official packages available for other architectures, such as +`aarch64`. If you wish to use Terraform on a non-`x86_64` system, +[download a normal release `.zip` file](/downloads.html) instead. + +## Supported Distribution Releases + +The HashiCorp Yum server currently contains release repositories for the +following distribution releases: + +* AmazonLinux 2 +* Fedora 29 +* Fedora 30 +* Fedora 31 +* Fedora 32 +* Fedora 33 +* RHEL 7 (and CentOS 7) +* RHEL 8 (and CentOS 8) + +No repositories are available for other versions of these distributions or for +any other RPM-based Linux distributions. If you add the repository using +the above commands on other systems then you will see a 404 Not Found error. + +Over time we will change the set of supported distributions, including both +adding support for new releases and ceasing to publish new Terraform versions +under older releases. + +## Choosing Terraform Versions + +The HashiCorp Yum repositories contain multiple versions of Terraform, but +because the packages are all named `terraform` it is impossible to install +more than one version at a time, and `yum install` or `dnf install` will +default to selecting the latest version. + +It's often necessary to match your Terraform version with what a particular +configuration is currently expecting. You can use the following command to +see which versions are currently available in the repository index: + +```bash +yum --showduplicate list terraform +``` + +You can select a specific version to install by including it in the +`yum install` command line, as follows: + +```bash +yum install terraform-0.14.0-2.x86_64 +``` + +If you are using a DNF-based distribution, similar use `dnf` instead of `yum` +when following the above steps. + +If your workflow requires using multiple versions of Terraform at the same +time, for example when working through a gradual upgrade where not all +of your configurations are upgraded yet, we recommend that you use the +official release `.zip` files instead of the Yum packages, so you can install +multiple versions at once and then select which to use for each command you +run. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/plugins/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/plugins/index.html.md new file mode 100644 index 00000000..c1ac5e1e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/plugins/index.html.md @@ -0,0 +1,58 @@ +--- +layout: "docs" +page_title: "Managing Plugins - Terraform CLI" +--- + +# Managing Plugins + +Terraform relies on plugins called "providers" in order to manage various types +of resources. (For more information about providers, see +[Providers](/docs/language/providers/index.html) in the Terraform +language docs.) + +-> **Note:** Providers are currently the only plugin type most Terraform users +will interact with. Terraform also supports third-party provisioner plugins, but +we discourage their use. + +Terraform downloads and/or installs any providers +[required](/docs/language/providers/requirements.html) by a configuration +when [initializing](/docs/cli/init/index.html) a working directory. By default, +this works without any additional interaction but requires network access to +download providers from their source registry. + +You can configure Terraform's provider installation behavior to limit or skip +network access, and to enable use of providers that aren't available via a +networked source. Terraform also includes some commands to show information +about providers and to reduce the effort of installing providers in airgapped +environments. + +## Configuring Plugin Installation + +Terraform's configuration file includes options for caching downloaded plugins, +or explicitly specifying a local or HTTPS mirror to install plugins from. For +more information, see [CLI Config File](/docs/cli/config/config-file.html). + +## Getting Plugin Information + +Use the [`terraform providers`](/docs/cli/commands/providers.html) command to get information +about the providers required by the current working directory's configuration. + +Use the [`terraform version`](/docs/cli/commands/version.html) command (or +`terraform -version`) to show the specific provider versions installed for the +current working directory. + +Use the [`terraform providers schema`](/docs/cli/commands/providers/schema.html) command to +get machine-readable information about the resources and configuration options +offered by each provider. + +## Managing Plugin Installation + +Use the [`terraform providers mirror`](/docs/cli/commands/providers/mirror.html) command to +download local copies of every provider required by the current working +directory's configuration. This directory will use the nested directory layout +that Terraform expects when installing plugins from a local source, so you can +transfer it directly to an airgapped system that runs Terraform. + +Use the [`terraform providers lock`](/docs/cli/commands/providers/lock.html) command +to update the lock file that Terraform uses to ensure predictable runs when +using ambiguous provider version constraints. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/plugins/signing.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/plugins/signing.html.md similarity index 92% rename from vendor/github.com/hashicorp/terraform/website/docs/plugins/signing.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/cli/plugins/signing.html.md index f1cfa7c6..95c8e935 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/plugins/signing.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/plugins/signing.html.md @@ -1,11 +1,12 @@ --- -layout: "registry" +layout: "docs" page_title: "Plugin Signing" -sidebar_current: "docs-plugins-signing" description: |- Terraform plugin signing trust levels --- + + # Plugin Signing ~> **Note** Currently only provider plugins fetched from a registry are authenticated. @@ -13,7 +14,7 @@ description: |- Terraform providers installed from the Registry are cryptographically signed, and the signature is verified at time of installation. There are three types of provider signatures, each with different trust implications: * **Signed by HashiCorp** - are built, signed, and supported by HashiCorp. -* **Signed by Trusted Partners** - are built, signed, and supported by a third party. HashiCorp has +* **Signed by Trusted Partners** - are built, signed, and supported by a third party. HashiCorp has verified the ownership of the private key and we provide a chain of trust to the CLI to verify this programatically. * **Self-signed** - are built, signed, and supported by a third party. HashiCorp does not provide a diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/run/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/run/index.html.md new file mode 100644 index 00000000..579c9d81 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/run/index.html.md @@ -0,0 +1,71 @@ +--- +layout: "docs" +page_title: "Provisioning Infrastructure - Terraform CLI" +--- + +# Provisioning Infrastructure with Terraform + +Terraform's primary function is to create, modify, and destroy infrastructure +resources to match the desired state described in a +[Terraform configuration](/docs/language/index.html). + +When people refer to "running Terraform," they generally mean performing these +provisioning actions in order to affect real infrastructure objects. The +Terraform binary has many other subcommands for a wide variety of administrative +actions, but these basic provisioning tasks are the core of Terraform. + +Terraform's provisioning workflow relies on three commands: `plan`, `apply`, and +`destroy`. All of these commands require an +[initialized](/docs/cli/init/index.html) working directory, and all of them act +only upon the currently selected [workspace](/docs/cli/workspaces/index.html). + +## Planning + +The `terraform plan` command evaluates a Terraform configuration to determine +the desired state of all the resources it declares, then compares that desired +state to the real infrastructure objects being managed with the current working +directory and workspace. It uses state data to determine which real objects +correspond to which declared resources, and checks the current state of each +resource using the relevant infrastructure provider's API. + +Once it has determined the difference between the current state and the desired +state, `terraform plan` presents a description of the changes necessary to +achieve the desired state. It _does not_ perform any actual changes to real +world infrastructure objects; it only presents a plan for making changes. + +Plans are usually run to validate configuration changes and confirm that the +resulting actions are as expected. However, `terraform plan` can also save its +plan as a runnable artifact, which `terraform apply` can use to carry out those +exact changes. + +For details, see [the `terraform plan` command](/docs/cli/commands/plan.html). + +## Applying + +The `terraform apply` command performs a plan just like `terraform plan` does, +but then actually carries out the planned changes to each resource using the +relevant infrastructure provider's API. It asks for confirmation from the user +before making any changes, unless it was explicitly told to skip approval. + +By default, `terraform apply` performs a fresh plan right before applying +changes, and displays the plan to the user when asking for confirmation. +However, it can also accept a plan file produced by `terraform plan` in lieu of +running a new plan. You can use this to reliably perform an exact set of +pre-approved changes, even if the configuration or the state of the real +infrastructure has changed in the minutes since the original plan was created. + +For details, see [the `terraform apply` command](/docs/cli/commands/apply.html). + +## Destroying + +The `terraform destroy` command destroys all of the resources being managed by +the current working directory and workspace, using state data to determine which +real world objects correspond to managed resources. Like `terraform apply`, it +asks for confirmation before proceeding. + +A destroy behaves exactly like deleting every resource from the configuration +and then running an apply, except that it doesn't require editing the +configuration. This is more convenient if you intend to provision similar +resources at a later date. + +For details, see [the `terraform destroy` command](/docs/cli/commands/destroy.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/state/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/state/index.html.md new file mode 100644 index 00000000..bb8b46f2 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/state/index.html.md @@ -0,0 +1,32 @@ +--- +layout: "docs" +page_title: "Manipulating State - Terraform CLI" +--- + +# Manipulating Terraform State + +> **Hands-on:** Try the [Manage Resources in Terraform State](https://learn.hashicorp.com/tutorials/terraform/state-cli?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +Terraform uses [state data](/docs/language/state/index.html) to remember which +real-world object corresponds to each resource in the configuration; +this allows it to modify an existing object when its resource declaration +changes. + +Terraform updates state automatically during plans and applies. However, it's +sometimes necessary to make deliberate adjustments to Terraform's state data, +usually to compensate for changes to the configuration or the real managed +infrastructure. + +Terraform CLI supports several workflows for interacting with state: + +- [Inspecting State](/docs/cli/state/inspect.html) +- [Forcing Re-creation (Tainting)](/docs/cli/state/taint.html) +- [Moving Resources](/docs/cli/state/move.html) +- Importing Pre-existing Resources (documented in the + [Importing Infrastructure](/docs/cli/import/index.html) section) +- [Disaster Recovery](/docs/cli/state/recover.html) + +~> **Important:** Modifying state data outside a normal plan or apply can cause +Terraform to lose track of managed resources, which might waste money, annoy +your colleagues, or even compromise the security of your operations. Make sure +to keep backups of your state data when modifying state out-of-band. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/state/inspect.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/state/inspect.html.md new file mode 100644 index 00000000..53ccc6f9 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/state/inspect.html.md @@ -0,0 +1,21 @@ +--- +layout: "docs" +page_title: "Inspecting State - Terraform CLI" +--- + +# Inspecting State + +Terraform includes some commands for reading and updating state without taking +any other actions. + +- [The `terraform state list` command](/docs/cli/commands/state/list.html) + shows the resource addresses for every resource Terraform knows about in a + configuration, optionally filtered by partial resource address. + +- [The `terraform state show` command](/docs/cli/commands/state/show.html) + displays detailed state data about one resource. + +- [The `terraform refresh` command](/docs/cli/commands/refresh.html) updates + state data to match the real-world condition of the managed resources. This is + done automatically during plans and applies, but not when interacting with + state directly. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/state/move.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/state/move.html.md new file mode 100644 index 00000000..87bb7198 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/state/move.html.md @@ -0,0 +1,35 @@ +--- +layout: "docs" +page_title: "Moving Resources - Terraform CLI" +--- + +# Moving Resources + +Terraform's state associates each real-world object with a configured resource +at a specific [resource address](/docs/cli/state/resource-addressing.html). This +is seamless when changing a resource's attributes, but Terraform will lose track +of a resource if you change its name, move it to a different module, or change +its provider. + +Usually that's fine: Terraform will destroy the old resource, replace it with a +new one (using the new resource address), and update any resources that rely on +its attributes. + +In cases where it's important to preserve an existing infrastructure object, you +can explicitly tell Terraform to associate it with a different configured +resource. + +- [The `terraform state mv` command](/docs/cli/commands/state/mv.html) changes + which resource address in your configuration is associated with a particular + real-world object. Use this to preserve an object when renaming a resource, or + when moving a resource into or out of a child module. + +- [The `terraform state rm` command](/docs/cli/commands/state/rm.html) tells + Terraform to stop managing a resource as part of the current working directory + and workspace, _without_ destroying the corresponding real-world object. (You + can later use `terraform import` to start managing that resource in a + different workspace or a different Terraform configuration.) + +- [The `terraform state replace-provider` command](/docs/cli/commands/state/replace-provider.html) + transfers existing resources to a new provider without requiring them to be + re-created. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/state/recover.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/state/recover.html.md new file mode 100644 index 00000000..5b45b8f6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/state/recover.html.md @@ -0,0 +1,24 @@ +--- +layout: "docs" +page_title: "Recovering from State Disasters - Terraform CLI" +--- + +# Recovering from State Disasters + +If something has gone horribly wrong (possibly due to accidents when performing +other state manipulation actions), you might need to take drastic actions with +your state data. + +- [The `terraform force-unlock` command](/docs/cli/commands/force-unlock.html) can + override the protections Terraform uses to prevent two processes from + modifying state at the same time. You might need this if a Terraform process + (like a normal apply) is unexpectedly terminated (like by the complete + destruction of the VM it's running in) before it can release its lock on the + state backend. Do not run this until you are completely certain what happened + to the process that caused the lock to get stuck. + +- [The `terraform state pull` command](/docs/cli/commands/state/pull.html) and + [the `terraform state push` command](/docs/cli/commands/state/push.html) can + directly read and write entire state files from and to the configured backend. + You might need this for obtaining or restoring a state backup. + diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/state/resource-addressing.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/state/resource-addressing.html.md new file mode 100644 index 00000000..72b987a8 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/state/resource-addressing.html.md @@ -0,0 +1,138 @@ +--- +layout: "docs" +page_title: "Internals: Resource Address" +sidebar_current: "docs-internals-resource-addressing" +description: |- + A resource address is a string that identifies zero or more resource + instances in your overall configuration. +--- + +# Resource Addressing + +A _resource address_ is a string that identifies zero or more resource +instances in your overall configuration. + +An address is made up of two parts: + +``` +[module path][resource spec] +``` + +In some contexts Terraform might allow for an incomplete resource address that +only refers to a module as a whole, or that omits the index for a +multi-instance resource. In those cases, the meaning depends on the context, +so you'll need to refer to the documentation for the specific feature you +are using which parses resource addresses. + +## Module path + +A module path addresses a module within the tree of modules. It takes the form: + +``` +module.module_name[module index] +``` + + * `module` - Module keyword indicating a child module (non-root). Multiple `module` + keywords in a path indicate nesting. + * `module_name` - User-defined name of the module. + * `[module index]` - (Optional) [Index](#index-values-for-modules-and-resources) + to select an instance from a module call that has multiple instances, + surrounded by square bracket characters (`[` and `]`). + +An address without a resource spec, i.e. `module.foo` applies to every resource within +the module if a single module, or all instances of a module if a module has multiple instances. +To address all resources of a particular module instance, include the module index in the address, +such as `module.foo[0]`. + +If the module path is omitted, the address applies to the root module. + +An example of the `module` keyword delineating between two modules that have multiple instances: + +``` +module.foo[0].module.bar["a"] +``` + +-> Module index only applies to modules in Terraform v0.13 or later. In earlier +versions of Terraform, a module could not have multiple instances. + +## Resource spec + +A resource spec addresses a specific resource instance in the selected module. +It has the following syntax: + +``` +resource_type.resource_name[instance index] +``` + + * `resource_type` - Type of the resource being addressed. + * `resource_name` - User-defined name of the resource. + * `[instance index]` - (Optional) [Index](#index-values-for-modules-and-resources) + to select an instance from a resource that has multiple instances, + surrounded by square bracket characters (`[` and `]`). + +-> In Terraform v0.12 and later, a resource spec without a module path prefix +matches only resources in the root module. In earlier versions, a resource spec +without a module path prefix would match resources with the same type and name +in any descendent module. + +## Index values for Modules and Resources + +The following specifications apply to index values on modules and resources with multiple instances: + + * `[N]` where `N` is a `0`-based numerical index into a resource with multiple + instances specified by the `count` meta-argument. Omitting an index when + addressing a resource where `count > 1` means that the address references + all instances. + * `["INDEX"]` where `INDEX` is a alphanumerical key index into a resource with + multiple instances specified by the `for_each` meta-argument. + +## Examples + +### count Example + +Given a Terraform config that includes: + +```hcl +resource "aws_instance" "web" { + # ... + count = 4 +} +``` + +An address like this: + +``` +aws_instance.web[3] +``` + +Refers to only the last instance in the config, and an address like this: + +``` +aws_instance.web +``` + +Refers to all four "web" instances. + +### for_each Example + +Given a Terraform config that includes: + +```hcl +resource "aws_instance" "web" { + # ... + for_each = { + "terraform": "value1", + "resource": "value2", + "indexing": "value3", + "example": "value4", + } +} +``` + +An address like this: + +``` +aws_instance.web["example"] +``` + +Refers to only the "example" instance in the config. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/state/taint.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/state/taint.html.md new file mode 100644 index 00000000..12162c36 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/state/taint.html.md @@ -0,0 +1,25 @@ +--- +layout: "docs" +page_title: "Forcing Re-creation of Resources (Tainting) - Terraform CLI" +--- + +# Forcing Re-creation of Resources (Tainting) + +When a resource declaration is modified, Terraform usually attempts to update +the existing resource in place (although some changes can require destruction +and re-creation, usually due to upstream API limitations). + +In some cases, you might want a resource to be destroyed and re-created even +when Terraform doesn't think it's necessary. This is usually for objects that +aren't fully described by their resource arguments due to side-effects that +happen during creation; for example, a virtual machine that configures itself +with `cloud-init` on startup might no longer meet your needs if the cloud-init +configuration changes. + +- [The `terraform taint` command](/docs/cli/commands/taint.html) tells Terraform to + destroy and re-create a particular resource during the next apply, regardless + of whether its resource arguments would normally require that. + +- [The `terraform untaint` command](/docs/cli/commands/untaint.html) undoes a + previous taint, or can preserve a resource that was automatically tainted due + to failed [provisioners](/docs/language/resources/provisioners/syntax.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/cli/workspaces/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/cli/workspaces/index.html.md new file mode 100644 index 00000000..f6a76a0e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/cli/workspaces/index.html.md @@ -0,0 +1,78 @@ +--- +layout: "docs" +page_title: "Managing Workspaces - Terraform CLI" +--- + +# Managing Workspaces + +In Terraform CLI, _workspaces_ are separate instances of +[state data](/docs/language/state/index.html) that can be used from the same working +directory. You can use workspaces to manage multiple non-overlapping groups of +resources with the same configuration. + +- Every [initialized working directory](/docs/cli/init/index.html) has at least + one workspace. (If you haven't created other workspaces, it is a workspace + named `default`.) +- For a given working directory, only one workspace can be _selected_ at a time. +- Most Terraform commands (including [provisioning](/docs/cli/run/index.html) + and [state manipulation](/docs/cli/state/index.html) commands) only interact + with the currently selected workspace. +- Use [the `terraform workspace select` command](/docs/cli/commands/workspace/select.html) + to change the currently selected workspace. +- Use the [`terraform workspace list`](/docs/cli/commands/workspace/list.html), + [`terraform workspace new`](/docs/cli/commands/workspace/new.html), and + [`terraform workspace delete`](/docs/cli/commands/workspace/delete.html) commands + to manage the available workspaces in the current working directory. + +-> **Note:** Terraform Cloud and Terraform CLI both have features called +"workspaces," but they're slightly different. Terraform Cloud's workspaces +behave more like completely separate working directories. + +## The Purpose of Workspaces + +Since most of the resources you can manage with Terraform don't include a unique +name as part of their configuration, it's common to use the same Terraform +configuration to provision multiple groups of similar resources. + +Terraform relies on [state](/docs/language/state/index.html) to associate resources with +real-world objects, so if you run the same configuration multiple times with +completely separate state data, Terraform can manage many non-overlapping groups +of resources. In some cases you'll want to change +[variable values](/docs/language/values/variables.html) for these different +resource collections (like when specifying differences between staging and +production deployments), and in other cases you might just want many instances +of a particular infrastructure pattern. + +The simplest way to maintain multiple instances of a configuration with +completely separate state data is to use multiple +[working directories](/docs/cli/init/index.html) (with different +[backend](/docs/language/settings/backends/configuration.html) configurations per directory, if you +aren't using the default `local` backend). + +However, this isn't always the most _convenient_ way to handle separate states. +Terraform installs a separate cache of plugins and modules for each working +directory, so maintaining multiple directories can waste bandwidth and disk +space. You must also update your configuration code from version control +separately for each directory, reinitialize each directory separately when +changing the configuration, etc. + +Workspaces allow you to use the same working copy of your configuration and the +same plugin and module caches, while still keeping separate states for each +collection of resources you manage. + +## Interactions with Terraform Cloud Workspaces + +Terraform Cloud organizes infrastructure using workspaces, but its workspaces +act more like completely separate working directories; each Terraform Cloud +workspace has its own Terraform configuration, set of variable values, state +data, run history, and settings. + +These two kinds of workspaces are different, but related. When using Terraform +CLI as a frontend for Terraform Cloud, you associate the current working +directory with one or more remote workspaces by configuring +[the `remote` backend](/docs/language/settings/backends/remote.html). If you associate the +directory with multiple workspaces (using a name prefix), you can use the +`terraform workspace` commands to select which remote workspace to use. + +For more information about using Terraform CLI with Terraform Cloud, see +[CLI-driven Runs](/docs/cloud/run/cli.html) in the Terraform Cloud docs. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/0.12upgrade.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/0.12upgrade.html.markdown deleted file mode 100644 index 1136599b..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/0.12upgrade.html.markdown +++ /dev/null @@ -1,118 +0,0 @@ ---- -layout: "docs" -page_title: "Command: 0.12upgrade" -sidebar_current: "docs-commands-012upgrade" -description: |- - The 0.12upgrade subcommand automatically rewrites existing configurations for Terraform 0.12 compatibility. ---- - -# Command: 0.12upgrade - -The `terraform 0.12upgrade` command applies several automatic upgrade rules to -help prepare a module that was written for Terraform v0.11 to be used -with Terraform v0.12. - --> **This command is available only in Terraform v0.12 releases.** For more information, see [the Terraform v0.12 upgrade guide](https://www.terraform.io/upgrade-guides/0-12.html). - -## Usage - -Usage: `terraform 0.12upgrade [options] [dir]` - -By default, `0.12upgrade` changes configuration files in the current working -directory. However, you can provide an explicit path to another directory if -desired, which may be useful for automating migrations of several modules in -the same repository. - -When run with no other options, the command will first explain what it is -going to do and prompt for confirmation: - -``` -$ terraform 0.12upgrade - -This command will rewrite the configuration files in the given directory so -that they use the new syntax features from Terraform v0.12, and will identify -any constructs that may need to be adjusted for correct operation with -Terraform v0.12. - -We recommend using this command in a clean version control work tree, so that -you can easily see the proposed changes as a diff against the latest commit. -If you have uncommitted changes already present, we recommend aborting this -command and dealing with them before running this command again. - -Would you like to upgrade the module in the current directory? - Only 'yes' will be accepted to confirm. - - Enter a value: yes -``` - -The `0.12upgrade` subcommand requires access to providers used in the -configuration in order to analyze their resource types, so it's important to -run `terraform init` first to install these. In some rare cases, a configuration -that worked in v0.11 may have syntax errors in v0.12, in which case -`terraform init` will run in a special mode where it installs only enough to -run the upgrade command, after which you can run `terraform init` again to -complete initialization. - -Many of the rewrite rules are completely automatic, but in some cases the -tool cannot determine enough information from the configuration alone to make -a decision, and so it will instead add a comment to the configuration for -user review. All such comments contain the string `TF-UPGRADE-TODO` to make -them easy to find. - -After upgrading, the configuration will also be reformatted into the standard -Terraform style and expressions rewritten to use the more-readable v0.12 syntax -features. - -We recommend running this command with a clean version control work tree so -that you can use VCS tools to review the proposed changes, including any -`TF-UPGRADE-TODO` comments, and make any revisions required before committing -the change. - -Once upgraded the configuration will no longer be compatible with Terraform -v0.11 and earlier. When upgrading a shared module that is called from multiple -configurations, you may need to -[fix existing configurations to a previous version](/docs/configuration/modules.html#module-versions) -to allow for a gradual upgrade. If the module is published via -[a Terraform registry](/docs/registry/), assign a new _major_ version number -to the upgraded module source to represent the fact that this is a breaking -change for v0.11 callers. If a module is installed directly from a version -control system such as Git, -[use specific revisions](https://www.terraform.io/docs/modules/sources.html#selecting-a-revision) -to control which version is used by which caller. - -The command-line options are all optional. The available options are: - -* `-yes` - Skip the initial introduction messages and interactive confirmation. - Use this when running the command in batch from a script. - -* `-force` - Override the heuristic that attempts to detect if a configuration - is already written for v0.12 or later. Some of the transformations made by - this command are not idempotent, so re-running against the same module may - change the meanings of some expressions in the module. - -## Batch Usage - -After you've experimented with the `0.12upgrade` command in some confined -situations, if you have a repository containing multiple modules you may -wish to batch-upgrade them all and review them together. Recursive upgrades -are not supported by the tool itself, but if you are on a Unix-style system -you can achieve this using the `find` command as follows: - -``` -find . -name '*.tf' -printf "%h\n" | uniq | xargs -n1 terraform 0.12upgrade -yes -``` - -On Mac OS X, the `find` included with the system does not support the `-printf` argument. You can install GNU find using Homebrew in order to use that argument: - -``` -brew install findutils -``` -Once installed, run the above command line using `gfind` instead of `find`. - - -Note that the above includes the `-yes` option to override the interactive -prompt, so be sure you have a clean work tree before running it. - -Because upgrading requires access to the configuration's provider plugins, -all of the directories must be initialized with `terraform init` prior to -running the above. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/0.13upgrade.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/0.13upgrade.html.markdown deleted file mode 100644 index 9340ebb4..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/0.13upgrade.html.markdown +++ /dev/null @@ -1,90 +0,0 @@ ---- -layout: "docs" -page_title: "Command: 0.13upgrade" -sidebar_current: "docs-commands-013upgrade" -description: |- - The 0.13upgrade subcommand updates existing configurations to use the new provider source features from Terraform 0.13. ---- - -# Command: 0.13upgrade - -The `terraform 0.13upgrade` command updates existing configuration to add an -explicit `source` attribute for each provider used in a given module. The -provider source settings are stored in a `required_providers` block. - --> **This command is available only in Terraform v0.13 releases.** For more information, see [the Terraform v0.13 upgrade guide](https://www.terraform.io/upgrade-guides/0-13.html). - -## Usage - -Usage: `terraform 0.13upgrade [options] [dir]` - -The primary purpose of the `0.13upgrade` command is to determine which -providers are in use for a module, detect the source address for those -providers where possible, and record this information in a -[`required_providers` block][required-providers]. - -[required-providers]: /docs/configuration/terraform.html#specifying-required-provider-versions - -~> Note: the command ignores `.tf.json` files and override files in the module. - -If the module already has a `required_providers` block, the command updates it -in-place. Otherwise, a new block is added to the `versions.tf` file. - -By default, `0.13upgrade` changes configuration files in the current working -directory. However, you can provide an explicit path to another directory if -desired, which may be useful for automating migrations of several modules in -the same repository. - -When run with no other options, the command will first explain what it is -going to do and prompt for confirmation: - -``` -$ terraform 0.13upgrade - -This command will update the configuration files in the given directory to use -the new provider source features from Terraform v0.13. It will also highlight -any providers for which the source cannot be detected, and advise how to -proceed. - -We recommend using this command in a clean version control work tree, so that -you can easily see the proposed changes as a diff against the latest commit. -If you have uncommited changes already present, we recommend aborting this -command and dealing with them before running this command again. - -Would you like to upgrade the module in the current directory? - Only 'yes' will be accepted to confirm. - - Enter a value: yes -``` - -We recommend running this command with a clean version control work tree so -that you can use VCS tools to review the proposed changes, including any -`TF-UPGRADE-TODO` comments, and make any revisions required before committing -the change. - -There is one command-line option: - -* `-yes` - Skip the initial introduction messages and interactive confirmation. - Use this when running the command in batch from a script. - -## Batch Usage - -After you've experimented with the `0.13upgrade` command in some confined -situations, if you have a repository containing multiple modules you may -wish to batch-upgrade them all and review them together. Recursive upgrades -are not supported by the tool itself, but if you are on a Unix-style system -you can achieve this using the `find` command as follows: - -``` -$ find . -name '*.tf' | xargs -n1 dirname | uniq | xargs -n1 terraform 0.13upgrade -yes -``` - -On a Windows system with PowerShell, you can use this command: - -``` -Get-Childitem -Recurse -Include *.tf | Split-Path | ` -Select-Object -Unique | ForEach-Object { terraform 0.13upgrade -yes $_.FullName } -``` - -Note that the above commands include the `-yes` option to override the -interactive prompt, so be sure you have a clean work tree before running it. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/apply.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/apply.html.markdown deleted file mode 100644 index ffcdc135..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/apply.html.markdown +++ /dev/null @@ -1,105 +0,0 @@ ---- -layout: "docs" -page_title: "Command: apply" -sidebar_current: "docs-commands-apply" -description: |- - The `terraform apply` command is used to apply the changes required to reach the desired state of the configuration, or the pre-determined set of actions generated by a `terraform plan` execution plan. ---- - -# Command: apply - -> **Hands-on:** Try the [Terraform: Get Started](https://learn.hashicorp.com/collections/terraform/aws-get-started?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. - -The `terraform apply` command is used to apply the changes required -to reach the desired state of the configuration, or the pre-determined -set of actions generated by a `terraform plan` execution plan. - -## Usage - -Usage: `terraform apply [options] [plan]` - -By default, `apply` scans the current directory for the configuration -and applies the changes appropriately. However, you can optionally give the -path to a saved plan file that was previously created with -[`terraform plan`](plan.html). - -If you don't give a plan file on the command line, `terraform apply` will -create a new plan automatically and then prompt for approval to apply it. If the -created plan does not include any changes to resources or to root module -output values then `terraform apply` will exit immediately, without prompting. - -The command-line flags are all optional. The list of available flags are: - -* `-backup=path` - Path to the backup file. Defaults to `-state-out` with - the ".backup" extension. Disabled by setting to "-". - -* `-compact-warnings` - If Terraform produces any warnings that are not - accompanied by errors, show them in a more compact form that includes only - the summary messages. - -* `-lock=true` - Lock the state file when locking is supported. - -* `-lock-timeout=0s` - Duration to retry a state lock. - -* `-input=true` - Ask for input for variables if not directly set. - -* `-auto-approve` - Skip interactive approval of plan before applying. - -* `-no-color` - Disables output with coloring. - -* `-parallelism=n` - Limit the number of concurrent operation as Terraform - [walks the graph](/docs/internals/graph.html#walking-the-graph). Defaults to - 10. - -* `-refresh=true` - Update the state for each resource prior to planning - and applying. This has no effect if a plan file is given directly to - apply. - -* `-state=path` - Path to the state file. Defaults to "terraform.tfstate". - Ignored when [remote state](/docs/state/remote.html) is used. This setting - does not persist and other commands, such as init, may not be aware of the - alternate statefile. To configure an alternate statefile path which is - available to all terraform commands, use the [local backend](/docs/backends/types/local.html). - -* `-state-out=path` - Path to write updated state file. By default, the - `-state` path will be used. Ignored when - [remote state](/docs/state/remote.html) is used. - -* `-target=resource` - A [Resource - Address](/docs/internals/resource-addressing.html) to target. For more - information, see - [the targeting docs from `terraform plan`](/docs/commands/plan.html#resource-targeting). - -* `-var 'foo=bar'` - Set a variable in the Terraform configuration. This flag - can be set multiple times. Variable values are interpreted as - [HCL](/docs/configuration/syntax.html#HCL), so list and map values can be - specified via this flag. - -* `-var-file=foo` - Set variables in the Terraform configuration from - a [variable file](/docs/configuration/variables.html#variable-files). If - a `terraform.tfvars` or any `.auto.tfvars` files are present in the current - directory, they will be automatically loaded. `terraform.tfvars` is loaded - first and the `.auto.tfvars` files after in alphabetical order. Any files - specified by `-var-file` override any values set automatically from files in - the working directory. This flag can be used multiple times. - -## Passing a Different Configuration Directory - -Terraform v0.13 and earlier also accepted a directory path in place of the -plan file argument to `terraform apply`, in which case Terraform would use -that directory as the root module instead of the current working directory. - -That usage is still supported in Terraform v0.14, but is now deprecated and we -plan to remove it in Terraform v0.15. If your workflow relies on overriding -the root module directory, use -[the `-chdir` global option](./#switching-working-directory-with--chdir) -instead, which works across all commands and makes Terraform consistently look -in the given directory for all files it would normaly read or write in the -current working directory. - -If your previous use of this legacy pattern was also relying on Terraform -writing the `.terraform` subdirectory into the current working directory even -though the root module directory was overridden, use -[the `TF_DATA_DIR` environment variable](environment-variables.html#TF_DATA_DIR) -to direct Terraform to write the `.terraform` directory to a location other -than the current working directory. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/cli-config.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/cli-config.html.markdown deleted file mode 100644 index bd83d7e9..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/cli-config.html.markdown +++ /dev/null @@ -1,447 +0,0 @@ ---- -layout: "docs" -page_title: "CLI Configuration" -sidebar_current: "docs-commands-cli-config" -description: |- - The general behavior of the Terraform CLI can be customized using the CLI - configuration file. ---- - -# CLI Configuration File (`.terraformrc` or `terraform.rc`) - -The CLI configuration file configures per-user settings for CLI behaviors, -which apply across all Terraform working directories. This is separate from -[your infrastructure configuration](/docs/configuration/index.html). - -## Location - -The configuration is placed in a single file whose location depends on the -host operating system: - -* On Windows, the file must be named named `terraform.rc` and placed - in the relevant user's `%APPDATA%` directory. The physical location - of this directory depends on your Windows version and system configuration; - use `$env:APPDATA` in PowerShell to find its location on your system. -* On all other systems, the file must be named `.terraformrc` (note - the leading period) and placed directly in the home directory - of the relevant user. - -On Windows, beware of Windows Explorer's default behavior of hiding filename -extensions. Terraform will not recognize a file named `terraform.rc.txt` as a -CLI configuration file, even though Windows Explorer may _display_ its name -as just `terraform.rc`. Use `dir` from PowerShell or Command Prompt to -confirm the filename. - -The location of the Terraform CLI configuration file can also be specified -using the `TF_CLI_CONFIG_FILE` [environment variable](/docs/commands/environment-variables.html). - -## Configuration File Syntax - -The configuration file uses the same _HCL_ syntax as `.tf` files, but with -different attributes and blocks. The following example illustrates the -general syntax; see the following section for information on the meaning -of each of these settings: - -```hcl -plugin_cache_dir = "$HOME/.terraform.d/plugin-cache" -disable_checkpoint = true -``` - -## Available Settings - -The following settings can be set in the CLI configuration file: - -- `credentials` - configures credentials for use with Terraform Cloud or - Terraform Enterprise. See [Credentials](#credentials) below for more - information. - -- `credentials_helper` - configures an external helper program for the storage - and retrieval of credentials for Terraform Cloud or Terraform Enterprise. - See [Credentials Helpers](#credentials-helpers) below for more information. - -- `disable_checkpoint` — when set to `true`, disables - [upgrade and security bulletin checks](/docs/commands/index.html#upgrade-and-security-bulletin-checks) - that require reaching out to HashiCorp-provided network services. - -- `disable_checkpoint_signature` — when set to `true`, allows the upgrade and - security bulletin checks described above but disables the use of an anonymous - id used to de-duplicate warning messages. - -- `plugin_cache_dir` — enables - [plugin caching](#provider-plugin-cache) - and specifies, as a string, the location of the plugin cache directory. - -- `provider_installation` - customizes the installation methods used by - `terraform init` when installing provider plugins. See - [Provider Installation](#provider-installation) below for more information. - -## Credentials - -[Terraform Cloud](/docs/cloud/index.html) provides a number of remote network -services for use with Terraform, and -[Terraform Enterprise](/docs/enterprise/index.html) allows hosting those -services inside your own infrastructure. For example, these systems offer both -[remote operations](/docs/cloud/run/cli.html) and a -[private module registry](/docs/cloud/registry/index.html). - -When interacting with Terraform-specific network services, Terraform expects -to find API tokens in CLI configuration files in `credentials` blocks: - -```hcl -credentials "app.terraform.io" { - token = "xxxxxx.atlasv1.zzzzzzzzzzzzz" -} -``` - -If you are running the Terraform CLI interactively on a computer with a web browser, you can use [the `terraform login` command](./login.html) -to get credentials and automatically save them in the CLI configuration. If -not, you can manually write `credentials` blocks. - -You can have multiple `credentials` blocks if you regularly use services from -multiple hosts. Many users will configure only one, for either -Terraform Cloud (at `app.terraform.io`) or for their organization's own -Terraform Enterprise host. Each `credentials` block contains a `token` argument -giving the API token to use for that host. - -~> **Important:** If you are using Terraform Cloud or Terraform Enterprise, -the token provided must be either a -[user token](/docs/cloud/users-teams-organizations/users.html#api-tokens) -or a -[team token](/docs/cloud/users-teams-organizations/api-tokens.html#team-api-tokens); -organization tokens cannot be used for command-line Terraform actions. - --> **Note:** The credentials hostname must match the hostname in your module -sources and/or backend configuration. If your Terraform Enterprise instance -is available at multiple hostnames, use only one of them consistently. -Terraform Cloud responds to API calls at both its current hostname -`app.terraform.io`, and its historical hostname `atlas.hashicorp.com`. - -### Credentials Helpers - -If you would prefer not to store your API tokens directly in the CLI -configuration as described in the previous section, you can optionally instruct -Terraform to use a different credentials storage mechanism by configuring a -special kind of plugin program called a _credentials helper_. - -```hcl -credentials_helper "example" { - args = [] -} -``` - -`credentials_helper` is a configuration block that can appear at most once -in the CLI configuration. Its label (`"example"` above) is the name of the -credentials helper to use. The `args` argument is optional and allows passing -additional arguments to the helper program, for example if it needs to be -configured with the address of a remote host to access for credentials. - -A configured credentials helper will be consulted only to retrieve credentials -for hosts that are _not_ explicitly configured in a `credentials` block as -described in the previous section. -Conversely, this means you can override the credentials returned by the helper -for a specific hostname by writing a `credentials` block alongside the -`credentials_helper` block. - -Terraform does not include any credentials helpers in the main distribution. -To learn how to write and install your own credentials helpers to integrate -with existing in-house credentials management systems, see -[the guide to Credentials Helper internals](/docs/internals/credentials-helpers.html). - -## Provider Installation - -The default way to install provider plugins is from a provider registry. The -origin registry for a provider is encoded in the provider's source address, -like `registry.terraform.io/hashicorp/aws`. For convenience in the common case, -Terraform allows omitting the hostname portion for providers on -`registry.terraform.io`, so you can write shorter public provider addresses like -`hashicorp/aws`. - -Downloading a plugin directly from its origin registry is not always -appropriate, though. For example, the system where you are running Terraform -may not be able to access an origin registry due to firewall restrictions -within your organization or your locality. - -To allow using Terraform providers in these situations, there are some -alternative options for making provider plugins available to Terraform which -we'll describe in the following sections. - -### Explicit Installation Method Configuration - -A `provider_installation` block in the CLI configuration allows overriding -Terraform's default installation behaviors, so you can force Terraform to use -a local mirror for some or all of the providers you intend to use. - -The general structure of a `provider_installation` block is as follows: - -```hcl -provider_installation { - filesystem_mirror { - path = "/usr/share/terraform/providers" - include = ["example.com/*/*"] - } - direct { - exclude = ["example.com/*/*"] - } -} -``` - -Each of the nested blocks inside the `provider_installation` block specifies -one installation method. Each installation method can take both `include` -and `exclude` patterns that specify which providers a particular installation -method can be used for. In the example above, we specify that any provider -whose origin registry is at `example.com` can be installed only from the -filesystem mirror at `/usr/share/terraform/providers`, while all other -providers can be installed only directly from their origin registries. - -If you set both both `include` and `exclude` for a particular installation -method, the exclusion patterns take priority. For example, including -`registry.terraform.io/hashicorp/*` but also excluding -`registry.terraform.io/hashicorp/dns` will make that installation method apply -to everything in the `hashicorp` namespace with the exception of -`hashicorp/dns`. - -As with provider source addresses in the main configuration, you can omit -the `registry.terraform.io/` prefix for providers distributed through the -public Terraform registry, even when using wildcards. For example, -`registry.terraform.io/hashicorp/*` and `hashicorp/*` are equivalent. -`*/*` is a shorthand for `registry.terraform.io/*/*`, not for -`*/*/*`. - -The following are the two supported installation method types: - -* `direct`: request information about the provider directly from its origin - registry and download over the network from the location that registry - indicates. This method expects no additional arguments. - -* `filesystem_mirror`: consult a directory on the local disk for copies of - providers. This method requires the additional argument `path` to indicate - which directory to look in. - - Terraform expects the given directory to contain a nested directory structure - where the path segments together provide metadata about the available - providers. The following two directory structures are supported: - - * Packed layout: `HOSTNAME/NAMESPACE/TYPE/terraform-provider-TYPE_VERSION_TARGET.zip` - is the distribution zip file obtained from the provider's origin registry. - * Unpacked layout: `HOSTNAME/NAMESPACE/TYPE/VERSION/TARGET` is a directory - containing the result of extracting the provider's distribution zip file. - - In both layouts, the `VERSION` is a string like `2.0.0` and the `TARGET` - specifies a particular target platform using a format like `darwin_amd64`, - `linux_arm`, `windows_amd64`, etc. - - If you use the unpacked layout, Terraform will attempt to create a symbolic - link to the mirror directory when installing the provider, rather than - creating a deep copy of the directory. The packed layout prevents this - because Terraform must extract the zip file during installation. - - You can include multiple `filesystem_mirror` blocks in order to specify - several different directories to search. - -* `network_mirror`: consult a particular HTTPS server for copies of providers, - regardless of which registry host they belong to. This method requires the - additional argument `url` to indicate the mirror base URL, which should - use the `https:` scheme and end with a trailing slash. - - Terraform expects the given URL to be a base URL for an implementation of - [the provider network mirror protocol](/docs/internals/provider-network-mirror-protocol.html), - which is designed to be relatively easy to implement using typical static - website hosting mechanisms. - -~> **Warning:** Don't configure `network_mirror` URLs that you do not trust. -Provider mirror servers are subject to TLS certificate checks to verify -identity, but a network mirror with a TLS certificate can potentially serve -modified copies of upstream providers with malicious content. - -Terraform will try all of the specified methods whose include and exclude -patterns match a given provider, and select the newest version available across -all of those methods that matches the version constraint given in each -Terraform configuration. If you have a local mirror of a particular provider -and intend Terraform to use that local mirror exclusively, you must either -remove the `direct` installation method altogether or use its `exclude` -argument to disable its use for specific providers. - -### Implied Local Mirror Directories - -If your CLI configuration does not include a `provider_installation` block at -all, Terraform produces an _implied_ configuration. The implied configuration -includes a selection of `filesystem_mirror` methods and then the `direct` -method. - -The set of directories Terraform can select as filesystem mirrors depends on -the operating system where you are running Terraform: - -* **Windows:** `%APPDATA%/terraform.d/plugins` and `%APPDATA%/HashiCorp/Terraform/plugins` -* **Mac OS X:** `$HOME/.terraform.d/plugins/`, - `~/Library/Application Support/io.terraform/plugins`, and - `/Library/Application Support/io.terraform/plugins` -* **Linux and other Unix-like systems**:`$HOME/.terraform.d/plugins/`, and - [XDG Base Directory](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) - data directories as configured, after appending `terraform/plugins`. - Without any XDG environment variables set, Terraform will use - `~/.local/share/terraform/plugins`, - `/usr/local/share/terraform/plugins`, and `/usr/share/terraform/plugins`. - -Terraform will create an implied `filesystem_mirror` method block for each of -the directories indicated above that exists when Terraform starts up. -In addition, if a `terraform.d/plugins` directory exists in the current working -directory, it will be added as a filesystem mirror. - -In addition to the zero or more implied `filesystem_mirror` blocks, Terraform -also creates an implied `direct` block. Terraform will scan all of the -filesystem mirror directories to see which providers are placed there and -automatically exclude all of those providers from the implied `direct` block. -(This automatic `exclude` behavior applies only to _implicit_ `direct` blocks; -if you use explicit `provider_installation` you will need to write the intended -exclusions out yourself.) - -### Provider Plugin Cache - -By default, `terraform init` downloads plugins into a subdirectory of the -working directory so that each working directory is self-contained. As a -consequence, if you have multiple configurations that use the same provider -then a separate copy of its plugin will be downloaded for each configuration. - -Given that provider plugins can be quite large (on the order of hundreds of -megabytes), this default behavior can be inconvenient for those with slow -or metered Internet connections. Therefore Terraform optionally allows the -use of a local directory as a shared plugin cache, which then allows each -distinct plugin binary to be downloaded only once. - -To enable the plugin cache, use the `plugin_cache_dir` setting in -the CLI configuration file. For example: - -```hcl -plugin_cache_dir = "$HOME/.terraform.d/plugin-cache" -``` - -This directory must already exist before Terraform will cache plugins; -Terraform will not create the directory itself. - -Please note that on Windows it is necessary to use forward slash separators -(`/`) rather than the conventional backslash (`\`) since the configuration -file parser considers a backslash to begin an escape sequence. - -Setting this in the configuration file is the recommended approach for a -persistent setting. Alternatively, the `TF_PLUGIN_CACHE_DIR` environment -variable can be used to enable caching or to override an existing cache -directory within a particular shell session: - -```bash -export TF_PLUGIN_CACHE_DIR="$HOME/.terraform.d/plugin-cache" -``` - -When a plugin cache directory is enabled, the `terraform init` command will -still use the configured or implied installation methods to obtain metadata -about which plugins are available, but once a suitable version has been -selected it will first check to see if the chosen plugin is already available -in the cache directory. If so, Terraform will use the previously-downloaded -copy. - -If the selected plugin is not already in the cache, Terraform will download -it into the cache first and then copy it from there into the correct location -under your current working directory. When possible Terraform will use -symbolic links to avoid storing a separate copy of a cached plugin in multiple -directories. - -The plugin cache directory _must not_ also be one of the configured or implied -filesystem mirror directories, since the cache management logic conflicts with -the filesystem mirror logic when operating on the same directory. - -Terraform will never itself delete a plugin from the plugin cache once it has -been placed there. Over time, as plugins are upgraded, the cache directory may -grow to contain several unused versions which you must delete manually. - -### Development Overrides for Provider Developers - --> **Note:** Development overrides work only in Terraform v0.14 and later. -Using a `dev_overrides` block in your CLI configuration will cause Terraform -v0.13 to reject the configuration as invalid. - -Normally Terraform verifies version selections and checksums for providers -in order to help ensure that all operations are made with the intended version -of a provider, and that authors can gradually upgrade to newer provider versions -in a controlled manner. - -These version and checksum rules are inconvenient when developing a provider -though, because we often want to try a test configuration against a development -build of a provider that doesn't even have an associated version number yet, -and doesn't have an official set of checksums listed in a provider registry. - -As a convenience for provider development, Terraform supports a special -additional block `dev_overrides` in `provider_installation` blocks. The contents -of this block effectively override all of the other configured installation -methods, so a block of this type must always appear first in the sequence: - -```hcl -provider_installation { - - # Use /home/developer/tmp/terraform-null as an overridden package directory - # for the hashicorp/null provider. This disables the version and checksum - # verifications for this provider and forces Terraform to look for the - # null provider plugin in the given directory. - dev_overrides { - "hashicorp/null" = "/home/developer/tmp/terraform-null" - } - - # For all other providers, install them directly from their origin provider - # registries as normal. If you omit this, Terraform will _only_ use - # the dev_overrides block, and so no other providers will be available. - direct {} -} -``` - -With development overrides in effect, the `terraform init` command will still -attempt to select a suitable published version of your provider to install and -record in -[the dependency lock file](/docs/configuration/dependency-lock.html) -for future use, but other commands like -`terraform apply` will disregard the lock file's entry for `hashicorp/null` and -will use the given directory instead. Once your new changes are included in a -published release of the provider, you can use `terraform init -upgrade` to -select the new version in the dependency lock file and remove your development -override. - -The override path for a particular provider should be a directory similar to -what would be included in a `.zip` file when distributing the provider. At -minimum that includes an executable file named with a prefix like -`terraform-provider-null`, where `null` is the provider type. If your provider -makes use of other files in its distribution package then you can copy those -files into the override directory too. - -You may wish to enable a development override only for shell sessions where -you are actively working on provider development. If so, you can write a -local CLI configuration file with content like the above in your development -directory, perhaps called `dev.tfrc` for the sake fo example, and then use the -`TF_CLI_CONFIG_FILE` environment variable to instruct Terraform to use that -localized CLI configuration instead of the default one: - -``` -export TF_CLI_CONFIG_FILE=/home/developer/tmp/dev.tfrc -``` - -Development overrides are not intended for general use as a way to have -Terraform look for providers on the local filesystem. If you wish to put -copies of _released_ providers in your local filesystem, see -[Implied Local Mirror Directories](#implied-local-mirror-directories) -or -[Explicit Installation Method Configuration](#explicit-installation-method-configuration) -instead. - -This development overrides mechanism is intended as a pragmatic way to enable -smoother provider development. The details of how it behaves, how to -configure it, and how it interacts with the dependency lock file may all evolve -in future Terraform releases, including possible breaking changes. We therefore -recommend using development overrides only temporarily during provider -development work. - -## Removed Settings - -The following settings are supported in Terraform 0.12 and earlier but are -no longer recommended for use: - -* `providers` - a configuration block that allows specifying the locations of - specific plugins for each named provider. This mechanism is deprecated - because it is unable to specify a version number and source for each provider. - See [Provider Installation](#provider-installation) above for the replacement - of this setting in Terraform 0.13 and later. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/console.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/console.html.markdown deleted file mode 100644 index 511f8516..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/console.html.markdown +++ /dev/null @@ -1,55 +0,0 @@ ---- -layout: "docs" -page_title: "Command: console" -sidebar_current: "docs-commands-console" -description: |- - The `terraform console` command provides an interactive console for - evaluting expressions. ---- - -# Command: console - -The `terraform console` command provides an interactive console for -evaluating [expressions](/docs/configuration/expressions.html). - -## Usage - -Usage: `terraform console [options]` - -This command provides an interactive command-line console for evaluating and -experimenting with [expressions](/docs/configuration/expressions.html). -This is useful for testing interpolations before using them in configurations, -and for interacting with any values currently saved in -[state](/docs/state/index.html). - -If the current state is empty or has not yet been created, the console can be -used to experiment with the expression syntax and -[built-in functions](/docs/configuration/functions.html). - -The supported options are: - -* `-state=path` - Path to a local state file. Expressions will be evaluated - using values from this state file. If not specified, the state associated - with the current [workspace](/docs/state/workspaces.html) is used. - -You can close the console with the `exit` command or by pressing Control-C -or Control-D. - -## Scripting - -The `terraform console` command can be used in non-interactive scripts -by piping newline-separated commands to it. Only the output from the -final command is printed unless an error occurs earlier. - -For example: - -```shell -$ echo "1 + 5" | terraform console -6 -``` - -## Remote State - -If [remote state](/docs/state/remote.html) is used by the current backend, -Terraform will read the state for the current workspace from the backend -before evaluating any expressions. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/destroy.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/destroy.html.markdown deleted file mode 100644 index f07c357a..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/destroy.html.markdown +++ /dev/null @@ -1,31 +0,0 @@ ---- -layout: "docs" -page_title: "Command: destroy" -sidebar_current: "docs-commands-destroy" -description: |- - The `terraform destroy` command is used to destroy the Terraform-managed infrastructure. ---- - -# Command: destroy - -The `terraform destroy` command is used to destroy the Terraform-managed -infrastructure. - -## Usage - -Usage: `terraform destroy [options]` - -Infrastructure managed by Terraform will be destroyed. This will ask for -confirmation before destroying. - -This command accepts all the arguments and options that the [apply -command](/docs/commands/apply.html) accepts, with the exception of a plan file -argument. - -If `-auto-approve` is set, then the destroy confirmation will not be shown. - -The `-target` flag, instead of affecting "dependencies" will instead also -destroy any resources that _depend on_ the target(s) specified. For more information, see [the targeting docs from `terraform plan`](/docs/commands/plan.html#resource-targeting). - -The behavior of any `terraform destroy` command can be previewed at any time -with an equivalent `terraform plan -destroy` command. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/env.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/env.html.markdown deleted file mode 100644 index e1ec830e..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/env.html.markdown +++ /dev/null @@ -1,13 +0,0 @@ ---- -layout: "docs" -page_title: "Command: env" -sidebar_current: "docs-commands-envcmd" -description: |- - The terraform env command is a deprecated, legacy form of "terraform workspace". ---- - -# Command: env - -The `terraform env` command is deprecated. -[The `terraform workspace` command](/docs/commands/workspace/) -should be used instead. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/fmt.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/fmt.html.markdown deleted file mode 100644 index 2a395019..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/fmt.html.markdown +++ /dev/null @@ -1,41 +0,0 @@ ---- -layout: "docs" -page_title: "Command: fmt" -sidebar_current: "docs-commands-fmt" -description: |- - The `terraform fmt` command is used to rewrite Terraform configuration files to a canonical format and style. ---- - -# Command: fmt - -The `terraform fmt` command is used to rewrite Terraform configuration files -to a canonical format and style. This command applies a subset of -the [Terraform language style conventions](/docs/configuration/style.html), -along with other minor adjustments for readability. - -Other Terraform commands that generate Terraform configuration will produce -configuration files that conform to the style imposed by `terraform fmt`, so -using this style in your own files will ensure consistency. - -The canonical format may change in minor ways between Terraform versions, so -after upgrading Terraform we recommend to proactively run `terraform fmt` -on your modules along with any other changes you are making to adopt the new -version. - -## Usage - -Usage: `terraform fmt [options] [DIR]` - -By default, `fmt` scans the current directory for configuration files. If -the `dir` argument is provided then it will scan that given directory -instead. If `dir` is a single dash (`-`) then `fmt` will read from standard -input (STDIN). - -The command-line flags are all optional. The list of available flags are: - -* `-list=false` - Don't list the files containing formatting inconsistencies. -* `-write=false` - Don't overwrite the input files. (This is implied by `-check` or when the input is STDIN.) -* `-diff` - Display diffs of formatting changes -* `-check` - Check if the input is formatted. Exit status will be 0 if - all input is properly formatted and non-zero otherwise. -* `-recursive` - Also process files in subdirectories. By default, only the given directory (or current directory) is processed. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/get.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/get.html.markdown deleted file mode 100644 index 80cbf4a5..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/get.html.markdown +++ /dev/null @@ -1,27 +0,0 @@ ---- -layout: "docs" -page_title: "Command: get" -sidebar_current: "docs-commands-get" -description: |- - The `terraform get` command is used to download and update modules. ---- - -# Command: get - -The `terraform get` command is used to download and update -[modules](/docs/modules/index.html) mentioned in the root module. - -## Usage - -Usage: `terraform get [options] PATH` - -The modules are downloaded into a `.terraform` subdirectory of the current -working directory. Don't commit this directory to your version control -repository. - -The `get` command supports the following option: - -* `-update` - If specified, modules that are already downloaded will be - checked for updates and the updates will be downloaded if present. - -* `-no-color` - Disable text coloring in the output. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/graph.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/graph.html.markdown deleted file mode 100644 index 04c286b0..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/graph.html.markdown +++ /dev/null @@ -1,56 +0,0 @@ ---- -layout: "docs" -page_title: "Command: graph" -sidebar_current: "docs-commands-graph" -description: |- - The `terraform graph` command is used to generate a visual representation of either a configuration or execution plan. The output is in the DOT format, which can be used by GraphViz to generate charts. ---- - -# Command: graph - -The `terraform graph` command is used to generate a visual -representation of either a configuration or execution plan. -The output is in the DOT format, which can be used by -[GraphViz](http://www.graphviz.org) to generate charts. - - -## Usage - -Usage: `terraform graph [options]` - -Outputs the visual dependency graph of Terraform resources represented by the -configuration in the current working directory. - -The graph is outputted in DOT format. The typical program that can -read this format is GraphViz, but many web services are also available -to read this format. - -The -type flag can be used to control the type of graph shown. Terraform -creates different graphs for different operations. See the options below -for the list of types supported. The default type is "plan" if a -configuration is given, and "apply" if a plan file is passed as an -argument. - -Options: - -* `-draw-cycles` - Highlight any cycles in the graph with colored edges. - This helps when diagnosing cycle errors. - -* `-type=plan` - Type of graph to output. Can be: `plan`, `plan-destroy`, `apply`, - `validate`, `input`, `refresh`. - -* `-module-depth=n` - (deprecated) In prior versions of Terraform, specified the - depth of modules to show in the output. - -## Generating Images - -The output of `terraform graph` is in the DOT format, which can -easily be converted to an image by making use of `dot` provided -by GraphViz: - -```shell -$ terraform graph | dot -Tsvg > graph.svg -``` - -Here is an example graph output: -![Graph Example](docs/graph-example.png) diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/import.html.md b/vendor/github.com/hashicorp/terraform/website/docs/commands/import.html.md deleted file mode 100644 index 7ae35681..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/import.html.md +++ /dev/null @@ -1,163 +0,0 @@ ---- -layout: "docs" -page_title: "Command: import" -sidebar_current: "docs-commands-import" -description: |- - The `terraform import` command is used to import existing resources into Terraform. ---- - -# Command: import - -> **Hands-on:** Try the [Import Terraform Configuration](https://learn.hashicorp.com/tutorials/terraform/state-import?in=terraform/state&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. - -The `terraform import` command is used to -[import existing resources](/docs/import/index.html) -into Terraform. - -## Usage - -Usage: `terraform import [options] ADDRESS ID` - -Import will find the existing resource from ID and import it into your Terraform -state at the given ADDRESS. - -ADDRESS must be a valid [resource address](/docs/internals/resource-addressing.html). -Because any resource address is valid, the import command can import resources -into modules as well as directly into the root of your state. - -ID is dependent on the resource type being imported. For example, for AWS -instances it is the instance ID (`i-abcd1234`) but for AWS Route53 zones -it is the zone ID (`Z12ABC4UGMOZ2N`). Please reference the provider documentation for details -on the ID format. If you're unsure, feel free to just try an ID. If the ID -is invalid, you'll just receive an error message. - -~> Warning: Terraform expects that each remote object it is managing will be -bound to only one resource address, which is normally guaranteed by Terraform -itself having created all objects. If you import existing objects into Terraform, -be careful to import each remote object to only one Terraform resource address. -If you import the same object multiple times, Terraform may exhibit unwanted -behavior. For more information on this assumption, see -[the State section](/docs/state/). - -The command-line flags are all optional. The list of available flags are: - -* `-backup=path` - Path to backup the existing state file. Defaults to - the `-state-out` path with the ".backup" extension. Set to "-" to disable - backups. - -* `-config=path` - Path to directory of Terraform configuration files that - configure the provider for import. This defaults to your working directory. - If this directory contains no Terraform configuration files, the provider - must be configured via manual input or environmental variables. - -* `-input=true` - Whether to ask for input for provider configuration. - -* `-lock=true` - Lock the state file when locking is supported. - -* `-lock-timeout=0s` - Duration to retry a state lock. - -* `-no-color` - If specified, output won't contain any color. - -* `-parallelism=n` - Limit the number of concurrent operation as Terraform - [walks the graph](/docs/internals/graph.html#walking-the-graph). Defaults - to 10. - -* `-provider=provider` - **Deprecated** Override the provider configuration to -use when importing the object. By default, Terraform uses the provider specified -in the configuration for the target resource, and that is the best behavior in most cases. - -* `-state=path` - Path to the source state file to read from. Defaults to the - configured backend, or "terraform.tfstate". - -* `-state-out=path` - Path to the destination state file to write to. If this - isn't specified the source state file will be used. This can be a new or - existing path. - -* `-var 'foo=bar'` - Set a variable in the Terraform configuration. This flag - can be set multiple times. Variable values are interpreted as - [HCL](/docs/configuration/syntax.html#HCL), so list and map values can be - specified via this flag. This is only useful with the `-config` flag. - -* `-var-file=foo` - Set variables in the Terraform configuration from - a [variable file](/docs/configuration/variables.html#variable-files). If - a `terraform.tfvars` or any `.auto.tfvars` files are present in the current - directory, they will be automatically loaded. `terraform.tfvars` is loaded - first and the `.auto.tfvars` files after in alphabetical order. Any files - specified by `-var-file` override any values set automatically from files in - the working directory. This flag can be used multiple times. This is only - useful with the `-config` flag. - -## Provider Configuration - -Terraform will attempt to load configuration files that configure the -provider being used for import. If no configuration files are present or -no configuration for that specific provider is present, Terraform will -prompt you for access credentials. You may also specify environmental variables -to configure the provider. - -The only limitation Terraform has when reading the configuration files -is that the import provider configurations must not depend on non-variable -inputs. For example, a provider configuration cannot depend on a data -source. - -As a working example, if you're importing AWS resources and you have a -configuration file with the contents below, then Terraform will configure -the AWS provider with this file. - -```hcl -variable "access_key" {} -variable "secret_key" {} - -provider "aws" { - access_key = "${var.access_key}" - secret_key = "${var.secret_key}" -} -``` - -## Example: Import into Resource - -This example will import an AWS instance into the `aws_instance` resource named `foo`: - -```shell -$ terraform import aws_instance.foo i-abcd1234 -``` - -## Example: Import into Module - -The example below will import an AWS instance into the `aws_instance` resource named `bar` into a module named `foo`: - -```shell -$ terraform import module.foo.aws_instance.bar i-abcd1234 -``` - -## Example: Import into Resource configured with count - -The example below will import an AWS instance into the first instance of the `aws_instance` resource named `baz` configured with -[`count`](/docs/configuration/resources.html#count-multiple-resource-instances-by-count): - -```shell -$ terraform import 'aws_instance.baz[0]' i-abcd1234 -``` - -## Example: Import into Resource configured with for_each - -The example below will import an AWS instance into the `"example"` instance of the `aws_instance` resource named `baz` configured with -[`for_each`](/docs/configuration/resources.html#for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings): - -Linux, Mac OS, and UNIX: - -```shell -$ terraform import 'aws_instance.baz["example"]' i-abcd1234 -``` - -PowerShell: - -```shell -$ terraform import 'aws_instance.baz[\"example\"]' i-abcd1234 -``` - -Windows `cmd.exe`: - -```shell -$ terraform import aws_instance.baz[\"example\"] i-abcd1234 -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/index.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/index.html.markdown deleted file mode 100644 index 4617ebb6..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/index.html.markdown +++ /dev/null @@ -1,178 +0,0 @@ ---- -layout: "docs" -page_title: "Commands" -sidebar_current: "docs-commands" -description: |- - Terraform is controlled via a very easy to use command-line interface (CLI). Terraform is only a single command-line application: terraform. This application then takes a subcommand such as "apply" or "plan". The complete list of subcommands is in the navigation to the left. ---- - -# Terraform Commands (CLI) - -> **Hands-on:** Try the [Terraform: Get Started](https://learn.hashicorp.com/collections/terraform/aws-get-started?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. - -Terraform is controlled via a very easy to use command-line interface (CLI). -Terraform is only a single command-line application: terraform. This application -then takes a subcommand such as "apply" or "plan". The complete list of subcommands -is in the navigation to the left. - -The terraform CLI is a well-behaved command line application. In erroneous cases, -a non-zero exit status will be returned. It also responds to -h and --help as you'd -most likely expect. - -To view a list of the available commands at any time, just run terraform with no arguments: - -```text -Usage: terraform [global options] [args] - -The available commands for execution are listed below. -The most common, useful commands are shown first, followed by -less common or more advanced commands. If you're just getting -started with Terraform, stick with the common commands. For the -other commands, please read the help and docs before usage. - -Common commands: - apply Builds or changes infrastructure - console Interactive console for Terraform interpolations - destroy Destroy Terraform-managed infrastructure - env Workspace management - fmt Rewrites config files to canonical format - get Download and install modules for the configuration - graph Create a visual graph of Terraform resources - import Import existing infrastructure into Terraform - init Initialize a Terraform working directory - login Obtain and save credentials for a remote host - logout Remove locally-stored credentials for a remote host - output Read an output from a state file - plan Generate and show an execution plan - providers Prints a tree of the providers used in the configuration - refresh Update local state file against real resources - show Inspect Terraform state or plan - taint Manually mark a resource for recreation - untaint Manually unmark a resource as tainted - validate Validates the Terraform files - version Prints the Terraform version - workspace Workspace management - -All other commands: - debug Debug output management (experimental) - force-unlock Manually unlock the terraform state - state Advanced state management - - -Global options (use these before the subcommand, if any): - -chdir=DIR Switch to a different working directory before executing - the given subcommand. - -help Show this help output, or the help for a specified - subcommand. - -version An alias for the "version" subcommand. -``` - -To get help for any specific command, use the -help option to the relevant -subcommand. For example, to see help about the graph subcommand: - -```text -$ terraform graph -help -Usage: terraform graph [options] PATH - - Outputs the visual graph of Terraform resources. If the path given is - the path to a configuration, the dependency graph of the resources are - shown. If the path is a plan file, then the dependency graph of the - plan itself is shown. - - The graph is outputted in DOT format. The typical program that can - read this format is GraphViz, but many web services are also available - to read this format. -``` - -## Switching working directory with `-chdir` - -The usual way to run Terraform is to first switch to the directory containing -the `.tf` files for your root module (for example, using the `cd` command), so -that Terraform will find those files automatically without any extra arguments. - -In some cases though — particularly when wrapping Terraform in automation -scripts — it can be convenient to run Terraform from a different directory than -the root module directory. To allow that, Terraform supports a global option -`-chdir=...` which you can include before the name of the subcommand you intend -to run: - -``` -terraform -chdir=environments/production apply -``` - -The `chdir` option instructs Terraform to change its working directory to the -given directory before running the given subcommand. This means that any files -that Terraform would normally read or write in the current working directory -will be read or written in the given directory instead. - -There are two exceptions where Terraform will use the original working directory -even when you specify `-chdir=...`: - -* Settings in the [CLI Configuration](cli-config.html) are not for a specific - subcommand and Terraform processes them before acting on the `-chdir` - option. - -* In case you need to use files from the original working directory as part - of your configuration, a reference to `path.cwd` in the configuration will - produce the original working directory instead of the overridden working - directory. Use `path.root` to get the root module directory. - -## Shell Tab-completion - -If you use either `bash` or `zsh` as your command shell, Terraform can provide -tab-completion support for all command names and (at this time) _some_ command -arguments. - -To add the necessary commands to your shell profile, run the following command: - -```bash -terraform -install-autocomplete -``` - -After installation, it is necessary to restart your shell or to re-read its -profile script before completion will be activated. - -To uninstall the completion hook, assuming that it has not been modified -manually in the shell profile, run the following command: - -```bash -terraform -uninstall-autocomplete -``` - -Currently not all of Terraform's subcommands have full tab-completion support -for all arguments. We plan to improve tab-completion coverage over time. - -## Upgrade and Security Bulletin Checks - -The Terraform CLI commands interact with the HashiCorp service -[Checkpoint](https://checkpoint.hashicorp.com/) to check for the availability -of new versions and for critical security bulletins about the current version. - -One place where the effect of this can be seen is in `terraform version`, where -it is used by default to indicate in the output when a newer version is -available. - -Only anonymous information, which cannot be used to identify the user or host, -is sent to Checkpoint. An anonymous ID is sent which helps de-duplicate warning -messages. Both the anonymous id and the use of checkpoint itself are completely -optional and can be disabled. - -Checkpoint itself can be entirely disabled for all HashiCorp products by -setting the environment variable `CHECKPOINT_DISABLE` to any non-empty value. - -Alternatively, settings in -[the CLI configuration file](/docs/commands/cli-config.html) can be used to -disable checkpoint features. The following checkpoint-related settings are -supported in this file: - -* `disable_checkpoint` - set to `true` to disable checkpoint calls - entirely. This is similar to the `CHECKPOINT_DISABLE` environment variable - described above. - -* `disable_checkpoint_signature` - set to `true` to disable the use of an - anonymous signature in checkpoint requests. This allows Terraform to check - for security bulletins but does not send the anonymous signature in these - requests. - -[The Checkpoint client code](https://github.com/hashicorp/go-checkpoint) used -by Terraform is available for review by any interested party. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/init.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/init.html.markdown deleted file mode 100644 index dfadce18..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/init.html.markdown +++ /dev/null @@ -1,190 +0,0 @@ ---- -layout: "docs" -page_title: "Command: init" -sidebar_current: "docs-commands-init" -description: |- - The `terraform init` command is used to initialize a Terraform configuration. This is the first command that should be run for any new or existing Terraform configuration. It is safe to run this command multiple times. ---- - -# Command: init - -> **Hands-on:** Try the [Terraform: Get Started](https://learn.hashicorp.com/collections/terraform/aws-get-started?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. - -The `terraform init` command is used to initialize a working directory -containing Terraform configuration files. This is the first command that should -be run after writing a new Terraform configuration or cloning an existing one -from version control. It is safe to run this command multiple times. - -## Usage - -Usage: `terraform init [options]` - -This command performs several different initialization steps in order to -prepare the current working directory for use with Terraform. More details on -these are in the sections below, but in most cases it is not necessary to worry -about these individual steps. - -This command is always safe to run multiple times, to bring the working -directory up to date with changes in the configuration. Though subsequent runs -may give errors, this command will never delete your existing configuration or -state. - -## General Options - -The following options apply to all of (or several of) the initialization steps: - -* `-input=true` Ask for input if necessary. If false, will error if - input was required. - -* `-lock=false` Disable locking of state files during state-related operations. - -* `-lock-timeout=` Override the time Terraform will wait to acquire - a state lock. The default is `0s` (zero seconds), which causes immediate - failure if the lock is already held by another process. - -* `-no-color` Disable color codes in the command output. - -* `-upgrade` Opt to upgrade modules and plugins as part of their respective - installation steps. See the sections below for more details. - -## Copy a Source Module - -By default, `terraform init` assumes that the working directory already -contains a configuration and will attempt to initialize that configuration. - -Optionally, init can be run against an empty directory with the -`-from-module=MODULE-SOURCE` option, in which case the given module will be -copied into the target directory before any other initialization steps are -run. - -This special mode of operation supports two use-cases: - -* Given a version control source, it can serve as a shorthand for checking out - a configuration from version control and then initializing the working directory - for it. - -* If the source refers to an _example_ configuration, it can be copied into - a local directory to be used as a basis for a new configuration. - -For routine use it is recommended to check out configuration from version -control separately, using the version control system's own commands. This way -it is possible to pass extra flags to the version control system when necessary, -and to perform other preparation steps (such as configuration generation, or -activating credentials) before running `terraform init`. - -## Backend Initialization - -During init, the root configuration directory is consulted for -[backend configuration](/docs/backends/config.html) and the chosen backend -is initialized using the given configuration settings. - -Re-running init with an already-initialized backend will update the working -directory to use the new backend settings. Depending on what changed, this -may result in interactive prompts to confirm migration of workspace states. -The `-force-copy` option suppresses these prompts and answers "yes" to the -migration questions. The `-reconfigure` option disregards any existing -configuration, preventing migration of any existing state. - -To skip backend configuration, use `-backend=false`. Note that some other init -steps require an initialized backend, so it is recommended to use this flag only -when the working directory was already previously initialized for a particular -backend. - -The `-backend-config=...` option can be used for -[partial backend configuration](/docs/backends/config.html#partial-configuration), -in situations where the backend settings are dynamic or sensitive and so cannot -be statically specified in the configuration file. - -## Child Module Installation - -During init, the configuration is searched for `module` blocks, and the source -code for referenced [modules](/docs/modules/) is retrieved from the locations -given in their `source` arguments. - -Re-running init with modules already installed will install the sources for -any modules that were added to configuration since the last init, but will not -change any already-installed modules. Use `-upgrade` to override this behavior, -updating all modules to the latest available source code. - -To skip child module installation, use `-get=false`. Note that some other init -steps can complete only when the module tree is complete, so it's recommended -to use this flag only when the working directory was already previously -initialized with its child modules. - -## Plugin Installation - -Most Terraform providers are published separately from Terraform as plugins. -During init, Terraform searches the configuration for both direct and indirect -references to providers and attempts to install the plugins for those providers. - -For providers that are published in either -[the public Terraform Registry](https://registry.terraform.io/) or in a -third-party provider registry, `terraform init` will automatically find, -download, and install the necessary provider plugins. If you cannot or do not -wish to install providers from their origin registries, you can customize how -Terraform installs providers using -[the provider installation settings in the CLI configuration](./cli-config.html#provider-installation). - -For more information about specifying which providers are required for each -of your modules, see [Provider Requirements](/docs/configuration/provider-requirements.html). - -After successful installation, Terraform writes information about the selected -providers to [the dependency lock file](/docs/configuration/dependency-lock.html). -You should commit this file to your version control system to ensure that -when you run `terraform init` again in future Terraform will select exactly -the same provider versions. Use the `-upgrade` option if you want Terraform -to ignore the dependency lock file and consider installing newer versions. - -You can modify `terraform init`'s plugin behavior with the following options: - -- `-upgrade` Upgrade all previously-selected plugins to the newest version - that complies with the configuration's version constraints. This will - cause Terraform to ignore any selections recorded in the dependency lock - file, and to take the newest available version matching the configured - version constraints. -- `-get-plugins=false` — Skip plugin installation. If you previously ran - `terraform init` without this option, the previously-installed plugins will - remain available in your current working directory. If you have not - previously run without this option, subsequent Terraform commands will - fail due to the needed provider plugins being unavailable. -- `-plugin-dir=PATH` — Force plugin installation to read plugins _only_ from - the specified directory, as if it had been configured as a `filesystem_mirror` - in the CLI configuration. If you intend to routinely use a particular - filesystem mirror then we recommend - [configuring Terraform's installation methods globally](./cli-config.html#provider-installation). - You can use `-plugin-dir` as a one-time override for exceptional situations, - such as if you are testing a local build of a provider plugin you are - currently developing. - -## Running `terraform init` in automation - -For teams that use Terraform as a key part of a change management and -deployment pipeline, it can be desirable to orchestrate Terraform runs in some -sort of automation in order to ensure consistency between runs, and provide -other interesting features such as integration with version control hooks. - -There are some special concerns when running `init` in such an environment, -including optionally making plugins available locally to avoid repeated -re-installation. For more information, see -the [Running Terraform in Automation](https://learn.hashicorp.com/tutorials/terraform/automate-terraform?in=terraform/automation&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. - -## Passing a Different Configuration Directory - -Terraform v0.13 and earlier also accepted a directory path in place of the -plan file argument to `terraform apply`, in which case Terraform would use -that directory as the root module instead of the current working directory. - -That usage is still supported in Terraform v0.14, but is now deprecated and we -plan to remove it in Terraform v0.15. If your workflow relies on overriding -the root module directory, use -[the `-chdir` global option](./#switching-working-directory-with--chdir) -instead, which works across all commands and makes Terraform consistently look -in the given directory for all files it would normaly read or write in the -current working directory. - -If your previous use of this legacy pattern was also relying on Terraform -writing the `.terraform` subdirectory into the current working directory even -though the root module directory was overridden, use -[the `TF_DATA_DIR` environment variable](environment-variables.html#TF_DATA_DIR) -to direct Terraform to write the `.terraform` directory to a location other -than the current working directory. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/login.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/login.html.markdown deleted file mode 100644 index 085781f5..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/login.html.markdown +++ /dev/null @@ -1,45 +0,0 @@ ---- -layout: "docs" -page_title: "Command: login" -sidebar_current: "docs-commands-login" -description: |- - The terraform login command can be used to automatically obtain and save an API token for Terraform Cloud, Terraform Enterprise, or any other host that offers Terraform services. ---- - -# Command: login - -The `terraform login` command can be used to automatically obtain and save an -API token for Terraform Cloud, Terraform Enterprise, or any other host that offers Terraform services. - --> **Note:** This command is suitable only for use in interactive scenarios -where it is possible to launch a web browser on the same host where Terraform -is running. If you are running Terraform in an unattended automation scenario, -you can -[configure credentials manually in the CLI configuration](https://www.terraform.io/docs/commands/cli-config.html#credentials). - -## Usage - -Usage: `terraform login [hostname]` - -If you don't provide an explicit hostname, Terraform will assume you want to -log in to Terraform Cloud at `app.terraform.io`. - -## Credentials Storage - -By default, Terraform will obtain an API token and save it in plain text in a -local CLI configuration file called `credentials.tfrc.json`. When you run -`terraform login`, it will explain specifically where it intends to save -the API token and give you a chance to cancel if the current configuration is -not as desired. - -If you don't wish to store your API token in the default location, you can -optionally configure a -[credentials helper program](cli-config.html#credentials-helpers) which knows -how to store and later retrieve credentials in some other system, such as -your organization's existing secrets management system. - -## Login Server Support - -The `terraform login` command works with any server supporting the -[login protocol](/docs/internals/login-protocol.html), including Terraform Cloud -and Terraform Enterprise. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/logout.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/logout.html.markdown deleted file mode 100644 index 644ff517..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/logout.html.markdown +++ /dev/null @@ -1,30 +0,0 @@ ---- -layout: "docs" -page_title: "Command: logout" -sidebar_current: "docs-commands-logout" -description: |- - The terraform logout command is used to remove credentials stored by terraform login. ---- - -# Command: logout - -The `terraform logout` command is used to remove credentials stored by -`terraform login`. These credentials are API tokens for Terraform Cloud, -Terraform Enterprise, or any other host that offers Terraform services. - -## Usage - -Usage: `terraform logout [hostname]` - -If you don't provide an explicit hostname, Terraform will assume you want to -log out of Terraform Cloud at `app.terraform.io`. - --> **Note:** the API token is only removed from local storage, not destroyed on -the remote server, so it will remain valid until manually revoked. - -## Credentials Storage - -By default, Terraform will remove the token stored in plain text in a local CLI -configuration file called `credentials.tfrc.json`. If you have configured a -[credentials helper program](cli-config.html#credentials-helpers), Terraform -will use the helper's `forget` command to remove it. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/output.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/output.html.markdown deleted file mode 100644 index 5b072eb1..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/output.html.markdown +++ /dev/null @@ -1,86 +0,0 @@ ---- -layout: "docs" -page_title: "Command: output" -sidebar_current: "docs-commands-output" -description: |- - The `terraform output` command is used to extract the value of an output variable from the state file. ---- - -# Command: output - -The `terraform output` command is used to extract the value of -an output variable from the state file. - -## Usage - -Usage: `terraform output [options] [NAME]` - -With no additional arguments, `output` will display all the outputs for -the root module. If an output `NAME` is specified, only the value of that -output is printed. - -The command-line flags are all optional. The list of available flags are: - -* `-json` - If specified, the outputs are formatted as a JSON object, with - a key per output. If `NAME` is specified, only the output specified will be - returned. This can be piped into tools such as `jq` for further processing. -* `-no-color` - If specified, output won't contain any color. -* `-state=path` - Path to the state file. Defaults to "terraform.tfstate". - Ignored when [remote state](/docs/state/remote.html) is used. - -## Examples - -These examples assume the following Terraform output snippet. - -```hcl -output "lb_address" { - value = "${aws_alb.web.public_dns}" -} - -output "instance_ips" { - value = ["${aws_instance.web.*.public_ip}"] -} - -output "password" { - sensitive = true - value = ["${var.secret_password}"] -} -``` - -To list all outputs: - -```shell -$ terraform output -``` - -Note that outputs with the `sensitive` attribute will be redacted: -```shell -$ terraform output password -password = -``` - -To query for the DNS address of the load balancer: - -```shell -$ terraform output lb_address -my-app-alb-1657023003.us-east-1.elb.amazonaws.com -``` - -To query for all instance IP addresses: - -```shell -$ terraform output instance_ips -test = [ - 54.43.114.12, - 52.122.13.4, - 52.4.116.53 -] -``` - -To query for a particular value in a list, use `-json` and a JSON -command-line parser such as [jq](https://stedolan.github.io/jq/). -For example, to query for the first instance's IP address: - -```shell -$ terraform output -json instance_ips | jq '.value[0]' -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/plan.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/plan.html.markdown deleted file mode 100644 index c6a1703d..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/plan.html.markdown +++ /dev/null @@ -1,155 +0,0 @@ ---- -layout: "docs" -page_title: "Command: plan" -sidebar_current: "docs-commands-plan" -description: |- - The `terraform plan` command is used to create an execution plan. Terraform performs a refresh, unless explicitly disabled, and then determines what actions are necessary to achieve the desired state specified in the configuration files. The plan can be saved using `-out`, and then provided to `terraform apply` to ensure only the pre-planned actions are executed. ---- - -# Command: plan - -> **Hands-on:** Try the [Terraform: Get Started](https://learn.hashicorp.com/collections/terraform/aws-get-started?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. - -The `terraform plan` command is used to create an execution plan. Terraform -performs a refresh, unless explicitly disabled, and then determines what -actions are necessary to achieve the desired state specified in the -configuration files. - -This command is a convenient way to check whether the execution plan for a -set of changes matches your expectations without making any changes to -real resources or to the state. For example, `terraform plan` might be run -before committing a change to version control, to create confidence that it -will behave as expected. - -The optional `-out` argument can be used to save the generated plan to a file -for later execution with `terraform apply`, which can be useful when -[running Terraform in automation](https://learn.hashicorp.com/tutorials/terraform/automate-terraform?in=terraform/automation&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS). - -If Terraform detects no changes to resource or to root module output values, -`terraform plan` will indicate that no changes are required. - -## Usage - -Usage: `terraform plan [options]` - -The `plan` subcommand looks in the current working directory for the root module -configuration. - -The available options are: - -* `-compact-warnings` - If Terraform produces any warnings that are not - accompanied by errors, show them in a more compact form that includes only - the summary messages. - -* `-destroy` - If set, generates a plan to destroy all the known resources. - -* `-detailed-exitcode` - Return a detailed exit code when the command exits. - When provided, this argument changes the exit codes and their meanings to - provide more granular information about what the resulting plan contains: - * 0 = Succeeded with empty diff (no changes) - * 1 = Error - * 2 = Succeeded with non-empty diff (changes present) - -* `-input=true` - Ask for input for variables if not directly set. - -* `-lock=true` - Lock the state file when locking is supported. - -* `-lock-timeout=0s` - Duration to retry a state lock. - -* `-no-color` - Disables output with coloring. - -* `-out=path` - The path to save the generated execution plan. This plan - can then be used with `terraform apply` to be certain that only the - changes shown in this plan are applied. Read the warning on saved - plans below. - -* `-parallelism=n` - Limit the number of concurrent operation as Terraform - [walks the graph](/docs/internals/graph.html#walking-the-graph). Defaults - to 10. - -* `-refresh=true` - Update the state prior to checking for differences. - -* `-state=path` - Path to the state file. Defaults to "terraform.tfstate". - Ignored when [remote state](/docs/state/remote.html) is used. - -* `-target=resource` - A [Resource - Address](/docs/internals/resource-addressing.html) to target. This flag can - be used multiple times. See below for more information. - -* `-var=foo=bar` - Set a variable in the Terraform configuration. This flag - can be set multiple times. Variable values are interpreted as - [HCL](/docs/configuration/syntax.html#HCL), so list and map values can be - specified via this flag. - -* `-var-file=foo` - Set variables in the Terraform configuration from - a [variable file](/docs/configuration/variables.html#variable-files). If - a `terraform.tfvars` or any `.auto.tfvars` files are present in the current - directory, they will be automatically loaded. `terraform.tfvars` is loaded - first and the `.auto.tfvars` files after in alphabetical order. Any files - specified by `-var-file` override any values set automatically from files in - the working directory. This flag can be used multiple times. - -## Resource Targeting - -The `-target` option can be used to focus Terraform's attention on only a -subset of resources. -[Resource Address](/docs/internals/resource-addressing.html) syntax is used -to specify the constraint. The resource address is interpreted as follows: - -* If the given address has a _resource spec_, only the specified resource - is targeted. If the named resource uses `count` and no explicit index - is specified in the address (i.e. aws_instance.example[3]), all of the instances sharing the given - resource name are targeted. - -* If the given address _does not_ have a resource spec, and instead just - specifies a module path, the target applies to all resources in the - specified module _and_ all of the descendent modules of the specified - module. - -This targeting capability is provided for exceptional circumstances, such -as recovering from mistakes or working around Terraform limitations. It -is *not recommended* to use `-target` for routine operations, since this can -lead to undetected configuration drift and confusion about how the true state -of resources relates to configuration. - -Instead of using `-target` as a means to operate on isolated portions of very -large configurations, prefer instead to break large configurations into -several smaller configurations that can each be independently applied. -[Data sources](/docs/configuration/data-sources.html) can be used to access -information about resources created in other configurations, allowing -a complex system architecture to be broken down into more manageable parts -that can be updated independently. - -## Security Warning - -Saved plan files (with the `-out` flag) encode the configuration, -state, diff, and _variables_. Variables are often used to store secrets. -Therefore, the plan file can potentially store secrets. - -Terraform itself does not encrypt the plan file. It is highly -recommended to encrypt the plan file if you intend to transfer it -or keep it at rest for an extended period of time. - -Future versions of Terraform will make plan files more -secure. - -## Passing a Different Configuration Directory - -Terraform v0.13 and earlier accepted an additional positional argument giving -a directory path, in which case Terraform would use that directory as the root -module instead of the current working directory. - -That usage is still supported in Terraform v0.14, but is now deprecated and we -plan to remove it in Terraform v0.15. If your workflow relies on overriding -the root module directory, use -[the `-chdir` global option](./#switching-working-directory-with--chdir) -instead, which works across all commands and makes Terraform consistently look -in the given directory for all files it would normaly read or write in the -current working directory. - -If your previous use of this legacy pattern was also relying on Terraform -writing the `.terraform` subdirectory into the current working directory even -though the root module directory was overridden, use -[the `TF_DATA_DIR` environment variable](environment-variables.html#TF_DATA_DIR) -to direct Terraform to write the `.terraform` directory to a location other -than the current working directory. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/providers.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/providers.html.markdown deleted file mode 100644 index d30c2130..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/providers.html.markdown +++ /dev/null @@ -1,22 +0,0 @@ ---- -layout: "commands-providers" -page_title: "Command: providers" -sidebar_current: "docs-commands-providers" -description: |- - The `terraform providers` command prints information about the providers used - in the current configuration. ---- - -# Command: providers - -The `terraform providers` command shows information about the -[provider requirements](/docs/configuration/provider-requirements.html) of the -configuration in the current working directory, as an aid to understanding -where each requirement was detected from. - -This command also has several subcommands with different purposes, which -are listed in the navigation bar. - -## Usage - -Usage: `terraform providers` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/push.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/push.html.markdown deleted file mode 100644 index 927920b3..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/push.html.markdown +++ /dev/null @@ -1,16 +0,0 @@ ---- -layout: "docs" -page_title: "Command: push" -sidebar_current: "docs-commands-push" -description: |- - DISCONTINUED. Terraform Cloud and the modern "remote" backend have replaced the old `terraform push` command. ---- - -# Command: push - -!> **Important:** The `terraform push` command is no longer functional. Its functionality was replaced and surpassed by [the `remote` backend](/docs/backends/types/remote.html), which works with current versions of Terraform Cloud. The `remote` backend allows you to run remote operations directly from the command line, and displays real-time output from the remote run environment. - -The `terraform push` command was an early implementation of remote Terraform runs. It allowed teams to push a configuration to a remote run environment in a discontinued version of Terraform Enterprise. - -The legacy Terraform Enterprise version that supported `terraform push` is no longer available, and there are no remaining instances of that version in operation. Without a service to push to, the command is now completely non-functional. - diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/refresh.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/refresh.html.markdown deleted file mode 100644 index 6506cc96..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/refresh.html.markdown +++ /dev/null @@ -1,68 +0,0 @@ ---- -layout: "docs" -page_title: "Command: refresh" -sidebar_current: "docs-commands-refresh" -description: |- - The `terraform refresh` command is used to reconcile the state Terraform knows about (via its state file) with the real-world infrastructure. This can be used to detect any drift from the last-known state, and to update the state file. ---- - -# Command: refresh - -The `terraform refresh` command is used to reconcile the state Terraform -knows about (via its state file) with the real-world infrastructure. -This can be used to detect any drift from the last-known state, and to -update the state file. - -This does not modify infrastructure, but does modify the state file. -If the state is changed, this may cause changes to occur during the next -plan or apply. - -## Usage - -Usage: `terraform refresh [options]` - -The `terraform refresh` command accepts the following options: - -* `-backup=path` - Path to the backup file. Defaults to `-state-out` with - the ".backup" extension. Disabled by setting to "-". - -* `-compact-warnings` - If Terraform produces any warnings that are not - accompanied by errors, show them in a more compact form that includes only - the summary messages. - -* `-input=true` - Ask for input for variables if not directly set. - -* `-lock=true` - Lock the state file when locking is supported. - -* `-lock-timeout=0s` - Duration to retry a state lock. - -* `-no-color` - If specified, output won't contain any color. - -* `-parallelism=n` - Limit the number of concurrent operation as Terraform - [walks the graph](/docs/internals/graph.html#walking-the-graph). Defaults - to 10. - -* `-state=path` - Path to read and write the state file to. Defaults to "terraform.tfstate". - Ignored when [remote state](/docs/state/remote.html) is used. - -* `-state-out=path` - Path to write updated state file. By default, the - `-state` path will be used. Ignored when - [remote state](/docs/state/remote.html) is used. - -* `-target=resource` - A [Resource - Address](/docs/internals/resource-addressing.html) to target. Operation will - be limited to this resource and its dependencies. This flag can be used - multiple times. - -* `-var 'foo=bar'` - Set a variable in the Terraform configuration. This flag - can be set multiple times. Variable values are interpreted as - [HCL](/docs/configuration/syntax.html#HCL), so list and map values can be - specified via this flag. - -* `-var-file=foo` - Set variables in the Terraform configuration from - a [variable file](/docs/configuration/variables.html#variable-files). If - a `terraform.tfvars` or any `.auto.tfvars` files are present in the current - directory, they will be automatically loaded. `terraform.tfvars` is loaded - first and the `.auto.tfvars` files after in alphabetical order. Any files - specified by `-var-file` override any values set automatically from files in - the working directory. This flag can be used multiple times. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/show.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/show.html.markdown deleted file mode 100644 index bb0cd7c4..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/show.html.markdown +++ /dev/null @@ -1,47 +0,0 @@ ---- -layout: "docs" -page_title: "Command: show" -sidebar_current: "docs-commands-show" -description: |- - The `terraform show` command is used to provide human-readable output from a state or plan file. This can be used to inspect a plan to ensure that the planned operations are expected, or to inspect the current state as Terraform sees it. ---- - -# Command: show - -The `terraform show` command is used to provide human-readable output -from a state or plan file. This can be used to inspect a plan to ensure -that the planned operations are expected, or to inspect the current state -as Terraform sees it. - -Machine-readable output is generated by adding the `-json` command-line -flag. - --> **Note:** When using the `-json` command-line flag, any sensitive values in -Terraform state will be displayed in plain text. For more information, see -[Sensitive Data in State](/docs/state/sensitive-data.html). - -## JSON Output - -For Terraform state files (including when no path is provided), -`terraform show -json` will show a JSON representation of the state. - -For Terraform plan files, `terraform show -json` will show a JSON representation -of the plan, configuration, and current state. - -The output format is covered in detail in [JSON Output Format](/docs/internals/json-format.html). - -## Usage - -Usage: `terraform show [options] [file]` - -You may use `show` with a path to either a Terraform state file or plan -file. If you don't specify a file path, Terraform will show the latest state -snapshot. - -This command accepts the following options: - -* `-no-color` - Disables output with coloring - -* `-json` - Displays machine-readable output from a state or plan file - --> JSON output via the `-json` option requires **Terraform v0.12 or later**. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/state/addressing.html.md b/vendor/github.com/hashicorp/terraform/website/docs/commands/state/addressing.html.md deleted file mode 100644 index 0df95118..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/state/addressing.html.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -layout: "commands-state" -page_title: "Command: state resource addressing" -sidebar_current: "docs-commands-state-address" -description: |- - The `terraform state` command is used for advanced state management. ---- - -# Resource Addressing - -The `terraform state` subcommands use -[standard address syntax](/docs/internals/resource-addressing.html) to refer -to individual resources, resource instances, and modules. This is the same -syntax used for the `-target` option to the `apply` and `plan` commands. - -Most state commands allow referring to individual resource instances, whole -resources (which may have multiple instances if `count` or `for_each` is used), -or even whole modules. - -For more information on the syntax, see [Resource Addressing](/docs/internals/resource-addressing.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/state/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/commands/state/index.html.md deleted file mode 100644 index 264dbdcd..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/state/index.html.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -layout: "commands-state" -page_title: "Command: state" -sidebar_current: "docs-commands-state-index" -description: |- - The `terraform state` command is used for advanced state management. ---- - -# State Command - -The `terraform state` command is used for advanced state management. -As your Terraform usage becomes more advanced, there are some cases where -you may need to modify the [Terraform state](/docs/state/index.html). -Rather than modify the state directly, the `terraform state` commands can -be used in many cases instead. - -This command is a nested subcommand, meaning that it has further subcommands. -These subcommands are listed to the left. - -## Usage - -Usage: `terraform state [options] [args]` - -Please click a subcommand to the left for more information. - -## Remote State - -The Terraform state subcommands all work with remote state just as if it -was local state. Reads and writes may take longer than normal as each read -and each write do a full network roundtrip. Otherwise, backups are still -written to disk and the CLI usage is the same as if it were local state. - -## Backups - -All `terraform state` subcommands that modify the state write backup -files. The path of these backup file can be controlled with `-backup`. - -Subcommands that are read-only (such as [list](/docs/commands/state/list.html)) -do not write any backup files since they aren't modifying the state. - -Note that backups for state modification _can not be disabled_. Due to -the sensitivity of the state file, Terraform forces every state modification -command to write a backup file. You'll have to remove these files manually -if you don't want to keep them around. - -## Command-Line Friendly - -The output and command-line structure of the state subcommands is -designed to be easy to use with Unix command-line tools such as grep, awk, -etc. Consequently, the output is also friendly to the equivalent PowerShell -commands within Windows. - -For advanced filtering and modification, we recommend piping Terraform -state subcommands together with other command line tools. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/state/list.html.md b/vendor/github.com/hashicorp/terraform/website/docs/commands/state/list.html.md deleted file mode 100644 index dea39e2c..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/state/list.html.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -layout: "commands-state" -page_title: "Command: state list" -sidebar_current: "docs-commands-state-sub-list" -description: |- - The terraform state list command is used to list resources within a Terraform state. ---- - -# Command: state list - -The `terraform state list` command is used to list resources within a -[Terraform state](/docs/state/index.html). - -## Usage - -Usage: `terraform state list [options] [address...]` - -The command will list all resources in the state file matching the given -addresses (if any). If no addresses are given, all resources are listed. - -The resources listed are sorted according to module depth order followed -by alphabetical. This means that resources that are in your immediate -configuration are listed first, and resources that are more deeply nested -within modules are listed last. - -For complex infrastructures, the state can contain thousands of resources. -To filter these, provide one or more patterns to the command. Patterns are -in [resource addressing format](/docs/commands/state/addressing.html). - -The command-line flags are all optional. The list of available flags are: - -* `-state=path` - Path to the state file. Defaults to "terraform.tfstate". - Ignored when [remote state](/docs/state/remote.html) is used. -* `-id=id` - ID of resources to show. Ignored when unset. - -## Example: All Resources - -This example will list all resources, including modules: - -``` -$ terraform state list -aws_instance.foo -aws_instance.bar[0] -aws_instance.bar[1] -module.elb.aws_elb.main -``` - -## Example: Filtering by Resource - -This example will only list resources for the given name: - -``` -$ terraform state list aws_instance.bar -aws_instance.bar[0] -aws_instance.bar[1] -``` - -## Example: Filtering by Module - -This example will only list resources in the given module: - -``` -$ terraform state list module.elb -module.elb.aws_elb.main -``` - -## Example: Filtering by ID - -This example will only list the resource whose ID is specified on the -command line. This is useful to find where in your configuration a -specific resource is located. - -``` -$ terraform state list -id=sg-1234abcd -module.elb.aws_security_group.sg -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/state/pull.html.md b/vendor/github.com/hashicorp/terraform/website/docs/commands/state/pull.html.md deleted file mode 100644 index 394881b4..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/state/pull.html.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -layout: "commands-state" -page_title: "Command: state pull" -sidebar_current: "docs-commands-state-sub-pull" -description: |- - The `terraform state pull` command is used to manually download and output the state from remote state. ---- - -# Command: state pull - -The `terraform state pull` command is used to manually download and output -the state from [remote state](/docs/state/remote.html). This command also -works with local state. - -## Usage - -Usage: `terraform state pull` - -This command will download the state from its current location and -output the raw format to stdout. - -This is useful for reading values out of state (potentially pairing this -command with something like [jq](https://stedolan.github.io/jq/)). It is -also useful if you need to make manual modifications to state. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/state/push.html.md b/vendor/github.com/hashicorp/terraform/website/docs/commands/state/push.html.md deleted file mode 100644 index 13303ad2..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/state/push.html.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -layout: "commands-state" -page_title: "Command: state push" -sidebar_current: "docs-commands-state-sub-push" -description: |- - The `terraform state push` command pushes items to the Terraform state. ---- - -# Command: state push - -The `terraform state push` command is used to manually upload a local -state file to [remote state](/docs/state/remote.html). This command also -works with local state. - -This command should rarely be used. It is meant only as a utility in case -manual intervention is necessary with the remote state. - -## Usage - -Usage: `terraform state push [options] PATH` - -This command will push the state specified by PATH to the currently -configured [backend](/docs/backends). - -If PATH is "-" then the state data to push is read from stdin. This data -is loaded completely into memory and verified prior to being written to -the destination state. - -Terraform will perform a number of safety checks to prevent you from -making changes that appear to be unsafe: - - * **Differing lineage**: If the "lineage" value in the state differs, - Terraform will not allow you to push the state. A differing lineage - suggests that the states are completely different and you may lose - data. - - * **Higher remote serial**: If the "serial" value in the destination state - is higher than the state being pushed, Terraform will prevent the push. - A higher serial suggests that data is in the destination state that isn't - accounted for in the local state being pushed. - -Both of these safety checks can be disabled with the `-force` flag. -**This is not recommended.** If you disable the safety checks and are -pushing state, the destination state will be overwritten. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/state/show.html.md b/vendor/github.com/hashicorp/terraform/website/docs/commands/state/show.html.md deleted file mode 100644 index 6cf8984a..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/state/show.html.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -layout: "commands-state" -page_title: "Command: state show" -sidebar_current: "docs-commands-state-sub-show" -description: |- - The `terraform state show` command is used to show the attributes of a single resource in the Terraform state. ---- - -# Command: state show - -The `terraform state show` command is used to show the attributes of a -single resource in the -[Terraform state](/docs/state/index.html). - -## Usage - -Usage: `terraform state show [options] ADDRESS` - -The command will show the attributes of a single resource in the -state file that matches the given address. - -This command requires an address that points to a single resource in the -state. Addresses are -in [resource addressing format](/docs/commands/state/addressing.html). - -The command-line flags are all optional. The list of available flags are: - -* `-state=path` - Path to the state file. Defaults to "terraform.tfstate". - Ignored when [remote state](/docs/state/remote.html) is used. - -The output of `terraform state show` is intended for human consumption, not -programmatic consumption. To extract state data for use in other software, use -[`terraform show -json`](../show.html#json-output) and decode the result -using the documented structure. - -## Example: Show a Resource - -The example below shows a `packet_device` resource named `worker`: - -``` -$ terraform state show 'packet_device.worker' -# packet_device.worker: -resource "packet_device" "worker" { - billing_cycle = "hourly" - created = "2015-12-17T00:06:56Z" - facility = "ewr1" - hostname = "prod-xyz01" - id = "6015bg2b-b8c4-4925-aad2-f0671d5d3b13" - locked = false -} -``` - -## Example: Show a Module Resource - -The example below shows a `packet_device` resource named `worker` inside a module named `foo`: - -```shell -$ terraform state show 'module.foo.packet_device.worker' -``` - -## Example: Show a Resource configured with count - -The example below shows the first instance of a `packet_device` resource named `worker` configured with -[`count`](/docs/configuration/resources.html#count-multiple-resource-instances-by-count): - -```shell -$ terraform state show 'packet_device.worker[0]' -``` - -## Example: Show a Resource configured with for_each - -The example below shows the `"example"` instance of a `packet_device` resource named `worker` configured with -[`for_each`](/docs/configuration/resources.html#for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings): - -Linux, Mac OS, and UNIX: - -```shell -$ terraform state show 'packet_device.worker["example"]' -``` - -PowerShell: - -```shell -$ terraform state show 'packet_device.worker[\"example\"]' -``` - -Windows `cmd.exe`: - -```shell -$ terraform state show packet_device.worker[\"example\"] -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/taint.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/taint.html.markdown deleted file mode 100644 index 65c758d9..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/taint.html.markdown +++ /dev/null @@ -1,110 +0,0 @@ ---- -layout: "docs" -page_title: "Command: taint" -sidebar_current: "docs-commands-taint" -description: |- - The `terraform taint` command manually marks a Terraform-managed resource as tainted, forcing it to be destroyed and recreated on the next apply. ---- - -# Command: taint - -The `terraform taint` command manually marks a Terraform-managed resource -as tainted, forcing it to be destroyed and recreated on the next apply. - -This command _will not_ modify infrastructure, but does modify the -state file in order to mark a resource as tainted. Once a resource is -marked as tainted, the next -[plan](/docs/commands/plan.html) will show that the resource will -be destroyed and recreated and the next -[apply](/docs/commands/apply.html) will implement this change. - -Forcing the recreation of a resource is useful when you want a certain -side effect of recreation that is not visible in the attributes of a resource. -For example: re-running provisioners will cause the node to be different -or rebooting the machine from a base image will cause new startup scripts -to run. - -Note that tainting a resource for recreation may affect resources that -depend on the newly tainted resource. For example, a DNS resource that -uses the IP address of a server may need to be modified to reflect -the potentially new IP address of a tainted server. The -[plan command](/docs/commands/plan.html) will show this if this is -the case. - -## Usage - -Usage: `terraform taint [options] address` - -The `address` argument is the address of the resource to mark as tainted. -The address is in -[the resource address syntax](/docs/internals/resource-addressing.html) syntax, -as shown in the output from other commands, such as: - - * `aws_instance.foo` - * `aws_instance.bar[1]` - * `aws_instance.baz``[\"key\"]` (quotes in resource addresses must be escaped on the command line, so that they are not interpreted by your shell) - * `module.foo.module.bar.aws_instance.qux` - -The command-line flags are all optional. The list of available flags are: - -* `-allow-missing` - If specified, the command will succeed (exit code 0) - even if the resource is missing. The command can still error, but only - in critically erroneous cases. - -* `-backup=path` - Path to the backup file. Defaults to `-state-out` with - the ".backup" extension. Disabled by setting to "-". - -* `-lock=true` - Lock the state file when locking is supported. - -* `-lock-timeout=0s` - Duration to retry a state lock. - -* `-state=path` - Path to read and write the state file to. Defaults to "terraform.tfstate". - Ignored when [remote state](/docs/state/remote.html) is used. - -* `-state-out=path` - Path to write updated state file. By default, the - `-state` path will be used. Ignored when - [remote state](/docs/state/remote.html) is used. - -## Example: Tainting a Single Resource - -This example will taint a single resource: - -``` -$ terraform taint aws_security_group.allow_all -The resource aws_security_group.allow_all in the module root has been marked as tainted. -``` - -## Example: Tainting a single resource created with for_each - -This example will taint a single resource created with for_each: - -``` -$ terraform taint 'module.route_tables.azurerm_route_table.rt["DefaultSubnet"]' -The resource module.route_tables.azurerm_route_table.rt["DefaultSubnet"] in the module root has been marked as tainted. -``` - -~> Note: In most `sh` compatible shells, double quotes and spaces can be -escaped by wrapping the argument in single quotes. This however varies between -other shells and operating systems, and users should use the appropriate escape -characters based on the applicable quoting rules for their shell to pass the -address string, including quotes, to Terraform. - -## Example: Tainting a Resource within a Module - -This example will only taint a resource within a module: - -``` -$ terraform taint "module.couchbase.aws_instance.cb_node[9]" -Resource instance module.couchbase.aws_instance.cb_node[9] has been marked as tainted. -``` - -Although we recommend that most configurations use only one level of nesting -and employ [module composition](/docs/modules/composition.html), it's possible -to have multiple levels of nested modules. In that case the resource instance -address must include all of the steps to the target instance, as in the -following example: - -``` -$ terraform taint "module.child.module.grandchild.aws_instance.example[2]" -Resource instance module.child.module.grandchild.aws_instance.example[2] has been marked as tainted. -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/untaint.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/untaint.html.markdown deleted file mode 100644 index c0bf081a..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/untaint.html.markdown +++ /dev/null @@ -1,59 +0,0 @@ ---- -layout: "docs" -page_title: "Command: untaint" -sidebar_current: "docs-commands-untaint" -description: |- - The `terraform untaint` command manually unmarks a Terraform-managed resource as tainted, restoring it as the primary instance in the state. ---- - -# Command: untaint - -The `terraform untaint` command manually unmarks a Terraform-managed resource -as tainted, restoring it as the primary instance in the state. This reverses -either a manual `terraform taint` or the result of provisioners failing on a -resource. - -This command _will not_ modify infrastructure, but does modify the state file -in order to unmark a resource as tainted. - -~> **NOTE on Tainted Indexes:** In certain edge cases, more than one tainted -instance can be present for a single resource. When this happens, you need to specify the index after the resources, e.g. `my-resource-example[2]`. You can use the `terraform show` command to inspect the state and -determine which index holds the instance you'd like to restore. In the vast -majority of cases, there will only be one tainted instance, and the `-index` -flag can be omitted. - -## Usage - -Usage: `terraform untaint [options] name` - -The `name` argument is the name of the resource to mark as untainted. The -format of this argument is `TYPE.NAME`, such as `aws_instance.foo`. - -The command-line flags are all optional (with the exception of `-index` in -certain cases, see above note). The list of available flags are: - -* `-allow-missing` - If specified, the command will succeed (exit code 0) - even if the resource is missing. The command can still error, but only - in critically erroneous cases. - -* `-backup=path` - Path to the backup file. Defaults to `-state-out` with - the ".backup" extension. Disabled by setting to "-". - -* `-lock=true` - Lock the state file when locking is supported. - -* `-lock-timeout=0s` - Duration to retry a state lock. - -* `-module=path` - The module path where the resource to untaint exists. - By default this is the root path. Other modules can be specified by - a period-separated list. Example: "foo" would reference the module - "foo" but "foo.bar" would reference the "bar" module in the "foo" - module. - -* `-no-color` - Disables output with coloring - -* `-state=path` - Path to read and write the state file to. Defaults to "terraform.tfstate". - Ignored when [remote state](/docs/state/remote.html) is used. - -* `-state-out=path` - Path to write updated state file. By default, the - `-state` path will be used. Ignored when - [remote state](/docs/state/remote.html) is used. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/validate.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/commands/validate.html.markdown deleted file mode 100644 index 344b094b..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/validate.html.markdown +++ /dev/null @@ -1,46 +0,0 @@ ---- -layout: "docs" -page_title: "Command: validate" -sidebar_current: "docs-commands-validate" -description: |- - The `terraform validate` command is used to validate the syntax of the terraform files. ---- - -# Command: validate - -The `terraform validate` command validates the configuration files in a -directory, referring only to the configuration and not accessing any remote -services such as remote state, provider APIs, etc. - -Validate runs checks that verify whether a configuration is syntactically -valid and internally consistent, regardless of any provided variables or -existing state. It is thus primarily useful for general verification of -reusable modules, including correctness of attribute names and value types. - -It is safe to run this command automatically, for example as a post-save -check in a text editor or as a test step for a re-usable module in a CI -system. - -Validation requires an initialized working directory with any referenced -plugins and modules installed. To initialize a working directory for -validation without accessing any configured remote backend, use: - -``` -$ terraform init -backend=false -``` - -To verify configuration in the context of a particular run (a particular -target workspace, input variable values, etc), use the `terraform plan` -command instead, which includes an implied validation check. - -## Usage - -Usage: `terraform validate [options]` - -This command accepts the following options: - -- `-json` - Produce output in a machine-readable JSON format, suitable for - use in text editor integrations and other automated systems. Always disables - color. - -- `-no-color` - If specified, output won't contain any color. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/workspace/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/commands/workspace/index.html.md deleted file mode 100644 index 6ec535d6..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/workspace/index.html.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -layout: "commands-workspace" -page_title: "Command: workspace" -sidebar_current: "docs-commands-workspace-index" -description: |- - The terraform workspace command is used to manage workspaces. ---- - -# Command: workspace - -The `terraform workspace` command is used to manage -[workspaces](/docs/state/workspaces.html). - -This command is a container for further subcommands. These subcommands are -listed in the navigation bar. - -## Usage - -Usage: `terraform workspace [options] [args]` - -Please choose a subcommand from the navigation for more information. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/workspace/list.html.md b/vendor/github.com/hashicorp/terraform/website/docs/commands/workspace/list.html.md deleted file mode 100644 index a44cab8a..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/workspace/list.html.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -layout: "commands-workspace" -page_title: "Command: workspace list" -sidebar_current: "docs-commands-workspace-sub-list" -description: |- - The terraform workspace list command is used to list all existing workspaces. ---- - -# Command: workspace list - -The `terraform workspace list` command is used to list all existing workspaces. - -## Usage - -Usage: `terraform workspace list` - -The command will list all existing workspaces. The current workspace is -indicated using an asterisk (`*`) marker. - -## Example - -``` -$ terraform workspace list - default -* development - jsmith-test -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/commands/workspace/show.html.md b/vendor/github.com/hashicorp/terraform/website/docs/commands/workspace/show.html.md deleted file mode 100644 index 228bc13c..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/commands/workspace/show.html.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -layout: "commands-workspace" -page_title: "Command: workspace show" -sidebar_current: "docs-commands-workspace-sub-show" -description: |- - The terraform workspace show command is used to output the current workspace. ---- - -# Command: workspace show - -The `terraform workspace show` command is used to output the current workspace. - -## Usage - -Usage: `terraform workspace show` - -The command will display the current workspace. - -## Example - -``` -$ terraform workspace show -development -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/data-sources.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/data-sources.html.md index 7f5c7e68..90f3463e 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/data-sources.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/data-sources.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Data Sources - 0.11 Configuration Language" sidebar_current: "docs-conf-old-data-sources" description: |- @@ -10,7 +10,7 @@ description: |- -> **Note:** This page is about Terraform 0.11 and earlier. For Terraform 0.12 and later, see -[Configuration Language: Configuring Data Sources](../configuration/data-sources.html). +[Configuration Language: Configuring Data Sources](/docs/language/data-sources/index.html). *Data sources* allow data to be fetched or computed for use elsewhere in Terraform configuration. Use of data sources allows a Terraform diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/environment-variables.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/environment-variables.html.md index 083d9504..97998088 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/environment-variables.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/environment-variables.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Environment Variables - 0.11 Configuration Language" sidebar_current: "docs-conf-old-environment-variables" description: |- @@ -10,7 +10,7 @@ description: |- -> **Note:** This page is about Terraform 0.11 and earlier. For Terraform 0.12 and later, see -[Commands: Environment Variables](../commands/environment-variables.html). +[Commands: Environment Variables](/docs/cli/config/environment-variables.html). ## TF_LOG @@ -48,7 +48,7 @@ export TF_INPUT=0 ## TF_MODULE_DEPTH -When given a value, causes terraform commands to behave as if the `-module-depth=VALUE` flag was specified. By setting this to 0, for example, you enable commands such as [plan](/docs/commands/plan.html) and [graph](/docs/commands/graph.html) to display more compressed information. +When given a value, causes terraform commands to behave as if the `-module-depth=VALUE` flag was specified. By setting this to 0, for example, you enable commands such as [plan](/docs/cli/commands/plan.html) and [graph](/docs/cli/commands/graph.html) to display more compressed information. ```shell export TF_MODULE_DEPTH=0 diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/index.html.md index eb4167aa..35982822 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/index.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/index.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "0.11 Configuration Language" sidebar_current: "docs-conf-old" description: |- @@ -10,7 +10,7 @@ description: |- -> **Note:** This page is about Terraform 0.11 and earlier. For Terraform 0.12 and later, see -[Configuration Language](../configuration/index.html). +[Configuration Language](/docs/language/index.html). Terraform uses text files to describe infrastructure and to set variables. These text files are called Terraform _configurations_ and end in diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/interpolation.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/interpolation.html.md index 73878362..9ec65d8a 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/interpolation.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/interpolation.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Interpolation Syntax - 0.11 Configuration Language" sidebar_current: "docs-conf-old-interpolation" description: |- @@ -10,8 +10,8 @@ description: |- -> **Note:** This page is about Terraform 0.11 and earlier. For Terraform 0.12 and later, see -[Configuration Language: Expressions](../configuration/expressions.html) and -[Configuration Language: Functions](../configuration/functions.html). +[Configuration Language: Expressions](/docs/language/expressions/index.html) and +[Configuration Language: Functions](/docs/language/functions/index.html). Embedded within strings in Terraform, whether you're using the Terraform syntax or JSON syntax, you can interpolate other values. These @@ -77,7 +77,7 @@ syntax to get a list of all the attributes: `${data.aws_subnet.example.*.cidr_bl The syntax is `module..`. For example `${module.foo.bar}` will interpolate the `bar` output from the `foo` -[module](/docs/modules/index.html). +[module](/docs/language/modules/develop/index.html). #### Count information @@ -98,7 +98,7 @@ path of the root module. In general, you probably want the The syntax is `terraform.`. This variable type contains metadata about the currently executing Terraform run. FIELD can currently only be `env` to -reference the currently active [state environment](/docs/state/environments.html). +reference the currently active workspace. ## Conditionals @@ -409,7 +409,7 @@ The supported built-in functions are: * `timestamp()` - Returns a UTC timestamp string in RFC 3339 format. This string will change with every invocation of the function, so in order to prevent diffs on every plan & apply, it must be used with the - [`ignore_changes`](./resources.html#ignore-changes) lifecycle attribute. + [`ignore_changes`](./resources.html#ignore_changes) lifecycle attribute. * `timeadd(time, duration)` - Returns a UTC timestamp string corresponding to adding a given `duration` to `time` in RFC 3339 format. For example, `timeadd("2017-11-22T00:00:00Z", "10m")` produces a value `"2017-11-22T00:10:00Z"`. @@ -424,7 +424,7 @@ The supported built-in functions are: * `urlencode(string)` - Returns an URL-safe copy of the string. - * `uuid()` - Returns a random UUID string. This string will change with every invocation of the function, so in order to prevent diffs on every plan & apply, it must be used with the [`ignore_changes`](./resources.html#ignore-changes) lifecycle attribute. + * `uuid()` - Returns a random UUID string. This string will change with every invocation of the function, so in order to prevent diffs on every plan & apply, it must be used with the [`ignore_changes`](./resources.html#ignore_changes) lifecycle attribute. * `values(map)` - Returns a list of the map values, in the order of the keys returned by the `keys` function. This function only works on flat maps and @@ -449,7 +449,7 @@ Terraform 0.12 and later. ## Templates Long strings can be managed using templates. -[Templates](/docs/providers/template/index.html) are +[Templates](https://registry.terraform.io/providers/hashicorp/template/latest/docs) are [data-sources](./data-sources.html) defined by a string with interpolation tokens (usually loaded from a file) and some variables to use during interpolation. They have a computed `rendered` attribute @@ -487,7 +487,7 @@ by the surrounding scope of the configuration. You may use any of the built-in functions in your template. For more details on template usage, please see the -[template_file documentation](/docs/providers/template/d/file.html). +[template_file documentation](https://registry.terraform.io/providers/hashicorp/template/latest/docs/data-sources/file). ### Using Templates with Count @@ -560,7 +560,7 @@ Operator precedences is the standard mathematical order of operations: "${2 * (4 + 3) * 3}" # computes to 42 ``` -You can use the [terraform console](/docs/commands/console.html) command to +You can use the [terraform console](/docs/cli/commands/console.html) command to try the math operations. -> **Note:** Since Terraform allows hyphens in resource and variable names, diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/load.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/load.html.md index 75ddcb19..b95adffa 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/load.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/load.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Load Order and Semantics - 0.11 Configuration Language" sidebar_current: "docs-conf-old-load" description: |- @@ -10,7 +10,7 @@ description: |- -> **Note:** This page is about Terraform 0.11 and earlier. For Terraform 0.12 and later, see -[Configuration Language](../configuration/index.html). +[Configuration Language](/docs/language/index.html). When invoking any command that loads the Terraform configuration, Terraform loads all configuration files within the directory diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/locals.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/locals.html.md index 74dc658a..b23a5b3a 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/locals.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/locals.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Local Values - 0.11 Configuration Language" sidebar_current: "docs-conf-old-locals" description: |- @@ -11,7 +11,7 @@ description: |- -> **Note:** This page is about Terraform 0.11 and earlier. For Terraform 0.12 and later, see -[Configuration Language: Configuring Local Values](../configuration/locals.html). +[Configuration Language: Configuring Local Values](/docs/language/values/locals.html). Local values assign a name to an expression, that can then be used multiple times within a module. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/modules.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/modules.html.md index 6ac2a0b3..25f2984d 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/modules.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/modules.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Modules - 0.11 Configuration Language" sidebar_current: "docs-conf-old-modules" description: |- @@ -10,7 +10,7 @@ description: |- -> **Note:** This page is about Terraform 0.11 and earlier. For Terraform 0.12 and later, see -[Configuration Language: Modules](../configuration/modules.html). +[Configuration Language: Modules](/docs/language/modules/index.html). A _module_ is a container for multiple resources that are used together. @@ -27,7 +27,7 @@ and re-used. This page describes how to call one module from another. Other pages in this section of the documentation describe the different elements that make up modules, and there is further information about how modules can be used, -created, and published in [the dedicated _Modules_ section](/docs/modules/index.html). +created, and published in [the dedicated _Modules_ section](/docs/language/modules/develop/index.html). ## Calling a Child Module @@ -62,7 +62,7 @@ Terraform CLI. Its value is either the path to a local directory of the module's configuration files, or a remote module source that Terraform should download and use. This value must be a literal string with no template sequences; interpolations are not allowed. For more information on -possible values for this argument, see [Module Sources](/docs/modules/sources.html). +possible values for this argument, see [Module Sources](/docs/language/modules/sources.html). The same source address can be specified in multiple `module` blocks to create multiple copies of the resources defined within, possibly with different @@ -161,7 +161,7 @@ future features. Since modules are a complex feature in their own right, further detail about how modules can be used, created, and published is included in -[the dedicated section on modules](/docs/modules/index.html). +[the dedicated section on modules](/docs/language/modules/develop/index.html). ## Providers within Modules @@ -233,7 +233,7 @@ resource "aws_s3_bucket" "example" { This approach is recommended in the common case where only a single configuration is needed for each provider across the entire configuration. -In more complex situations there may be [multiple provider instances](/docs/configuration/providers.html#multiple-provider-instances), +In more complex situations there may be [multiple provider instances](./providers.html#multiple-provider-instances), or a child module may need to use different provider settings than its parent. For such situations, it's necessary to pass providers explicitly as we will see in the next section. @@ -272,7 +272,7 @@ module "example" { The `providers` argument within a `module` block is similar to the `provider` argument within a resource as described for -[multiple provider instances](/docs/configuration/providers.html#multiple-provider-instances), +[multiple provider instances](./providers.html#multiple-provider-instances), but is a map rather than a single string because a module may contain resources from many different providers. @@ -386,7 +386,7 @@ giving each instance a unique name -- here `module "assets_bucket"` and Resources from child modules are prefixed with `module.` when displayed in plan output and elsewhere in the UI. For example, the `./publish_bucket` module contains `aws_s3_bucket.example`, and so the two -instances of this module produce S3 bucket resources with [_resource addresses_](/docs/internals/resource-addressing.html) +instances of this module produce S3 bucket resources with [_resource addresses_](/docs/cli/state/resource-addressing.html) `module.assets_bucket.aws_s3_bucket.example` and `module.media_bucket.aws_s3_bucket.example` respectively. These full addresses are used within the UI and on the command line, but are not valid within interpolation expressions due to the @@ -405,7 +405,7 @@ several regions or datacenters. ## Tainting resources within a module -The [taint command](/docs/commands/taint.html) can be used to _taint_ specific +The [taint command](/docs/cli/commands/taint.html) can be used to _taint_ specific resources within a module: ```shell diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/outputs.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/outputs.html.md index 7e43180c..cf9e8b43 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/outputs.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/outputs.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Output Values - 0.11 Configuration Language" sidebar_current: "docs-conf-old-outputs" description: |- @@ -10,11 +10,11 @@ description: |- -> **Note:** This page is about Terraform 0.11 and earlier. For Terraform 0.12 and later, see -[Configuration Language: Output Values](../configuration/outputs.html). +[Configuration Language: Output Values](/docs/language/values/outputs.html). Outputs define values that will be highlighted to the user when Terraform applies, and can be queried easily using the -[output command](/docs/commands/output.html). +[output command](/docs/cli/commands/output.html). Terraform knows a lot about the infrastructure it manages. Most resources have attributes associated with them, and diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/override.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/override.html.md index 6311443f..83411f59 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/override.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/override.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Override Files - 0.11 Configuration Language" sidebar_current: "docs-conf-old-override" description: |- @@ -10,7 +10,7 @@ description: |- -> **Note:** This page is about Terraform 0.11 and earlier. For Terraform 0.12 and later, see -[Configuration Language: Override Files](../configuration/override.html). +[Configuration Language: Override Files](/docs/language/files/override.html). Terraform loads all configuration files within a directory and appends them together. Terraform also has a concept of _overrides_, diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/providers.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/providers.html.md index d74aba63..2450f38e 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/providers.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/providers.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Providers - 0.11 Configuration Language" sidebar_current: "docs-conf-old-providers" description: |- @@ -10,7 +10,7 @@ description: |- -> **Note:** This page is about Terraform 0.11 and earlier. For Terraform 0.12 and later, see -[Configuration Language: Providers](../configuration/providers.html). +[Configuration Language: Providers](/docs/language/providers/index.html). Providers are responsible in Terraform for managing the lifecycle of a [resource](./resources.html): create, @@ -79,7 +79,7 @@ distributed by HashiCorp. See [Third-party Plugins](#third-party-plugins) below for installation instructions. For more information, see -[the `terraform init` command](/docs/commands/init.html). +[the `terraform init` command](/docs/cli/commands/init.html). ## Provider Versions @@ -118,7 +118,7 @@ provider "aws" { ``` This special argument applies to _all_ providers. -[`terraform providers`](/docs/commands/providers.html) can be used to +[`terraform providers`](/docs/cli/commands/providers.html) can be used to view the specified version constraints for all providers used in the current configuration. @@ -220,7 +220,7 @@ may also be used, but currently may cause errors when running `terraform destroy ## Third-party Plugins Anyone can develop and distribute their own Terraform providers. (See -[Writing Custom Providers](/docs/extend/writing-custom-providers.html) for more +[Writing Custom Providers](https://learn.hashicorp.com/collections/terraform/providers?utm_source=WEBSITE/docs/extend/writing-custom-providers.htmlutm_medium=WEB_IO/docs/extend/writing-custom-providers.htmlutm_offer=ARTICLE_PAGE/docs/extend/writing-custom-providers.htmlutm_content=DOCS) for more about provider development.) These third-party providers must be manually installed, since `terraform init` cannot automatically download them. @@ -295,7 +295,7 @@ use of a local directory as a shared plugin cache, which then allows each distinct plugin binary to be downloaded only once. To enable the plugin cache, use the `plugin_cache_dir` setting in -[the CLI configuration file](https://www.terraform.io/docs/commands/cli-config.html). +[the CLI configuration file](https://www.terraform.io/docs/cli/config/config-file.html). For example: ```hcl diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/resources.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/resources.html.md index d7bbed97..8b31603f 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/resources.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/resources.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Resources - 0.11 Configuration Language" sidebar_current: "docs-conf-old-resources" description: |- @@ -10,7 +10,7 @@ description: |- -> **Note:** This page is about Terraform 0.11 and earlier. For Terraform 0.12 and later, see -[Configuration Language: Resources](../configuration/resources.html). +[Configuration Language: Resources](/docs/language/resources/index.html). The most important thing you'll configure with Terraform are resources. Resources are a component of your infrastructure. @@ -98,7 +98,7 @@ There are **meta-parameters** available to all resources: Individual Resources may provide a `timeouts` block to enable users to configure the amount of time a specific operation is allowed to take before being considered an error. For example, the -[aws_db_instance](/docs/providers/aws/r/db_instance.html#timeouts) +[aws_db_instance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#timeouts) resource provides configurable timeouts for the `create`, `update`, and `delete` operations. Any Resource that provides Timeouts will document the default values for that operation, and users can overwrite @@ -176,7 +176,7 @@ could automatically do this a better way. Within a resource, you can optionally have a **connection block**. Connection blocks describe to Terraform how to connect to the resource for -[provisioning](/docs/provisioners/index.html). This block doesn't +[provisioning](/docs/language/resources/provisioners/syntax.html). This block doesn't need to be present if you're using only local provisioners, or if you're not provisioning at all. @@ -184,13 +184,13 @@ Resources provide some data on their own, such as an IP address, but other data must be specified by the user. The full list of settings that can be specified are listed on -the [provisioner connection page](/docs/provisioners/connection.html). +the [provisioner connection page](/docs/language/resources/provisioners/connection.html). ### Provisioners Within a resource, you can specify zero or more **provisioner blocks**. Provisioner blocks configure -[provisioners](/docs/provisioners/index.html). +[provisioners](/docs/language/resources/provisioners/syntax.html). Within the provisioner block is provisioner-specific configuration, much like resource-specific configuration. @@ -211,7 +211,7 @@ You can use the `${count.index}` [variable](./variables.html) to accomplish this. For example, here's how you could create three [AWS -Instances](/docs/providers/aws/r/instance.html) each with their own +Instances](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) each with their own static IP address: ```hcl @@ -232,7 +232,7 @@ resource "aws_instance" "app" { To reference a particular instance of a resource you can use `resource.foo.*.id[#]` where `#` is the index number of the instance. -For example, to create a list of all [AWS subnet](/docs/providers/aws/r/subnet.html) ids vs referencing a specific subnet in the list you can use this syntax: +For example, to create a list of all [AWS subnet](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) ids vs referencing a specific subnet in the list you can use this syntax: ```hcl resource "aws_vpc" "foo" { diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/syntax.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/syntax.html.md index d438e77a..8c0e57d3 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/syntax.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/syntax.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Syntax - 0.11 Configuration Language" sidebar_current: "docs-conf-old-syntax" description: |- @@ -14,7 +14,7 @@ description: |- -> **Note:** This page is about Terraform 0.11 and earlier. For Terraform 0.12 and later, see -[Configuration Language: Syntax](../configuration/syntax.html). +[Configuration Language: Syntax](/docs/language/syntax/configuration.html). The syntax of Terraform configurations is called [HashiCorp Configuration Language (HCL)](https://github.com/hashicorp/hcl). It is meant to strike a diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/terraform-enterprise.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/terraform-enterprise.html.md index 1b263688..8c5ee7f3 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/terraform-enterprise.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/terraform-enterprise.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Terraform Push - 0.11 Configuration Language" sidebar_current: "docs-conf-old-push" description: |- @@ -13,9 +13,9 @@ feature that was removed in Terraform 0.12. ~> **Important:** The `terraform push` command is deprecated, and only works with the legacy version of Terraform Enterprise. In the current version of Terraform Cloud, you can upload configurations using the API. See [the docs about API-driven runs](/docs/cloud/run/api.html) for more details. -The [`terraform push` command](/docs/commands/push.html) uploads a configuration to a Terraform Enterprise (legacy) environment. The name of the environment (and the organization it's in) can be specified on the command line, or as part of the Terraform configuration in an `atlas` block. +The [`terraform push` command](/docs/cli/commands/push.html) uploads a configuration to a Terraform Enterprise (legacy) environment. The name of the environment (and the organization it's in) can be specified on the command line, or as part of the Terraform configuration in an `atlas` block. -The `atlas` block does not configure remote state; it only configures the push command. For remote state, [use a `terraform { backend "" {...} }` block](/docs/backends/config.html). +The `atlas` block does not configure remote state; it only configures the push command. For remote state, use a `terraform { backend "" {...} }` block. This page assumes you're familiar with the [configuration syntax](./syntax.html) @@ -40,7 +40,7 @@ Enterprise**. While this transition is in progress, you may see references to ## Description The `atlas` block configures the settings when Terraform is -[pushed](/docs/commands/push.html) to Terraform Enterprise. Only one `atlas` block +[pushed](/docs/cli/commands/push.html) to Terraform Enterprise. Only one `atlas` block is allowed. Within the block (the `{ }`) is configuration for Atlas uploading. @@ -50,7 +50,7 @@ No keys are required, but the key typically set is `name`. to the nature of this configuration, interpolations are not possible. If you want to parameterize these settings, use the Atlas block to set defaults, then use the command-line flags of the -[push command](/docs/commands/push.html) to override. +[push command](/docs/cli/commands/push.html) to override. ## Syntax diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/terraform.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/terraform.html.md index ff75cbce..e7911ffe 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/terraform.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/terraform.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Terraform Settings - 0.11 Configuration Language" sidebar_current: "docs-conf-old-terraform" description: |- @@ -10,7 +10,7 @@ description: |- -> **Note:** This page is about Terraform 0.11 and earlier. For Terraform 0.12 and later, see -[Configuration Language: Terraform Settings](../configuration/terraform.html). +[Configuration Language: Terraform Settings](/docs/language/settings/index.html). The `terraform` configuration section is used to configure Terraform itself, such as requiring a minimum Terraform version to execute a configuration. @@ -41,7 +41,7 @@ that must be met to perform operations on this configuration. If the running Terraform version doesn't meet these constraints, an error is shown. See the section below dedicated to this option. -See [backends](/docs/backends/index.html) for more detail on the `backend` +See [backends](/docs/language/settings/backends/configuration.html) for more detail on the `backend` configuration. **No value within the `terraform` block can use interpolations.** The diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/variables.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/variables.html.md index 7201d642..6fbcf545 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/variables.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration-0-11/variables.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Input Variables - 0.11 Configuration Language" sidebar_current: "docs-conf-old-variables" description: |- @@ -11,7 +11,7 @@ description: |- -> **Note:** This page is about Terraform 0.11 and earlier. For Terraform 0.12 and later, see -[Configuration Language: Input Variables](../configuration/variables.html). +[Configuration Language: Input Variables](/docs/language/values/variables.html). Input variables serve as parameters for a Terraform module. @@ -211,7 +211,7 @@ $ TF_VAR_image=foo terraform apply ``` Maps and lists can be specified using environment variables as well using -[HCL](./syntax.html#HCL) syntax in the value. +[HCL](./syntax.html) syntax in the value. For a list variable like so: diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/backend.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/backend.html.md deleted file mode 100644 index 35e6b009..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/backend.html.md +++ /dev/null @@ -1,172 +0,0 @@ ---- -layout: "docs" -page_title: "Backend Configuration - Configuration Language" ---- - -# Backend Configuration - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Terraform Settings](../configuration-0-11/terraform.html). - - -Each Terraform configuration can specify a backend, which defines exactly where -and how operations are performed, where [state](/docs/state/index.html) -snapshots are stored, etc. Most non-trivial Terraform configurations configure -a remote backend so that multiple people can work with the same infrastructure. - -## Using a Backend Block - -Backends are configured with a nested `backend` block within the top-level -`terraform` block: - -```hcl -terraform { - backend "remote" { - organization = "example_corp" - - workspaces { - name = "my-app-prod" - } - } -} -``` - -There are some important limitations on backend configuration: - -- A configuration can only provide one backend block. -- A backend block cannot refer to named values (like input variables, locals, or data source attributes). - -### Backend Types - -The block label of the backend block (`"remote"`, in the example above) indicates which backend type to use. Terraform has a built-in selection of backends, and the configured backend must be available in the version of Terraform you are using. - -The arguments used in the block's body are specific to the chosen backend type; they configure where and how the backend will store the configuration's state, and in some cases configure other behavior. - -Some backends allow providing access credentials directly as part of the configuration for use in unusual situations, for pragmatic reasons. However, in normal use we _do not_ recommend including access credentials as part of the backend configuration. Instead, leave those arguments completely unset and provide credentials via the credentials files or environment variables that are conventional for the target system, as described in the documentation for each backend. - -See _[Backend Types](/docs/backends/types/index.html)_ for details about each supported backend type and its configuration arguments. - -### Default Backend - -If a configuration includes no backend block, Terraform defaults to using the `local` backend, which performs operations on the local system and stores state as a plain file in the current working directory. - -## Initialization - -Whenever a configuration's backend changes, you must run `terraform init` again -to validate and configure the backend before you can perform any plans, applies, -or state operations. - -When changing backends, Terraform will give you the option to migrate -your state to the new backend. This lets you adopt backends without losing -any existing state. - -To be extra careful, we always recommend manually backing up your state -as well. You can do this by simply copying your `terraform.tfstate` file -to another location. The initialization process should create a backup -as well, but it never hurts to be safe! - -## Partial Configuration - -You do not need to specify every required argument in the backend configuration. -Omitting certain arguments may be desirable if some arguments are provided -automatically by an automation script running Terraform. When some or all of -the arguments are omitted, we call this a _partial configuration_. - -With a partial configuration, the remaining configuration arguments must be -provided as part of -[the initialization process](/docs/backends/init.html#backend-initialization). -There are several ways to supply the remaining arguments: - - * **File**: A configuration file may be specified via the `init` command line. - To specify a file, use the `-backend-config=PATH` option when running - `terraform init`. If the file contains secrets it may be kept in - a secure data store, such as - [Vault](https://www.vaultproject.io/), in which case it must be downloaded - to the local disk before running Terraform. - - * **Command-line key/value pairs**: Key/value pairs can be specified via the - `init` command line. Note that many shells retain command-line flags in a - history file, so this isn't recommended for secrets. To specify a single - key/value pair, use the `-backend-config="KEY=VALUE"` option when running - `terraform init`. - - * **Interactively**: Terraform will interactively ask you for the required - values, unless interactive input is disabled. Terraform will not prompt for - optional values. - -If backend settings are provided in multiple locations, the top-level -settings are merged such that any command-line options override the settings -in the main configuration and then the command-line options are processed -in order, with later options overriding values set by earlier options. - -The final, merged configuration is stored on disk in the `.terraform` -directory, which should be ignored from version control. This means that -sensitive information can be omitted from version control, but it will be -present in plain text on local disk when running Terraform. - -When using partial configuration, Terraform requires at a minimum that -an empty backend configuration is specified in one of the root Terraform -configuration files, to specify the backend type. For example: - -```hcl -terraform { - backend "consul" {} -} -``` - -A backend configuration file has the contents of the `backend` block as -top-level attributes, without the need to wrap it in another `terraform` -or `backend` block: - -```hcl -address = "demo.consul.io" -path = "example_app/terraform_state" -scheme = "https" -``` - -The same settings can alternatively be specified on the command line as -follows: - -``` -$ terraform init \ - -backend-config="address=demo.consul.io" \ - -backend-config="path=example_app/terraform_state" \ - -backend-config="scheme=https" -``` - -The Consul backend also requires a Consul access token. Per the recommendation -above of omitting credentials from the configuration and using other mechanisms, -the Consul token would be provided by setting either the `CONSUL_HTTP_TOKEN` -or `CONSUL_HTTP_AUTH` environment variables. See the documentation of your -chosen backend to learn how to provide credentials to it outside of its main -configuration. - -## Changing Configuration - -You can change your backend configuration at any time. You can change -both the configuration itself as well as the type of backend (for example -from "consul" to "s3"). - -Terraform will automatically detect any changes in your configuration -and request a [reinitialization](/docs/backends/init.html). As part of -the reinitialization process, Terraform will ask if you'd like to migrate -your existing state to the new configuration. This allows you to easily -switch from one backend to another. - -If you're using multiple [workspaces](/docs/state/workspaces.html), -Terraform can copy all workspaces to the destination. If Terraform detects -you have multiple workspaces, it will ask if this is what you want to do. - -If you're just reconfiguring the same backend, Terraform will still ask if you -want to migrate your state. You can respond "no" in this scenario. - -## Unconfiguring a Backend - -If you no longer want to use any backend, you can simply remove the -configuration from the file. Terraform will detect this like any other -change and prompt you to [reinitialize](/docs/backends/init.html). - -As part of the reinitialization, Terraform will ask if you'd like to migrate -your state back down to normal local state. Once this is complete then -Terraform is back to behaving as it does by default. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/data-sources.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/data-sources.html.md deleted file mode 100644 index 7fa77d81..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/data-sources.html.md +++ /dev/null @@ -1,224 +0,0 @@ ---- -layout: "docs" -page_title: "Data Sources - Configuration Language" -sidebar_current: "docs-config-data-sources" -description: |- - Data sources allow data to be fetched or computed for use elsewhere in Terraform configuration. ---- - -# Data Sources - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Data Sources](../configuration-0-11/data-sources.html). - -_Data sources_ allow data to be fetched or computed for use elsewhere -in Terraform configuration. Use of data sources allows a Terraform -configuration to make use of information defined outside of Terraform, -or defined by another separate Terraform configuration. - -Each [provider](./providers.html) may offer data sources -alongside its set of [resource types](./resources.html#resource-types-and-arguments). - -## Using Data Sources - -A data source is accessed via a special kind of resource known as a -_data resource_, declared using a `data` block: - -```hcl -data "aws_ami" "example" { - most_recent = true - - owners = ["self"] - tags = { - Name = "app-server" - Tested = "true" - } -} -``` - -A `data` block requests that Terraform read from a given data source ("aws_ami") -and export the result under the given local name ("example"). The name is used -to refer to this resource from elsewhere in the same Terraform module, but has -no significance outside of the scope of a module. - -The data source and name together serve as an identifier for a given -resource and so must be unique within a module. - -Within the block body (between `{` and `}`) are query constraints defined by -the data source. Most arguments in this section depend on the -data source, and indeed in this example `most_recent`, `owners` and `tags` are -all arguments defined specifically for [the `aws_ami` data source](/docs/providers/aws/d/ami.html). - -When distinguishing from data resources, the primary kind of resource (as declared -by a `resource` block) is known as a _managed resource_. Both kinds of resources -take arguments and export attributes for use in configuration, but while -managed resources cause Terraform to create, update, and delete infrastructure -objects, data resources cause Terraform only to _read_ objects. For brevity, -managed resources are often referred to just as "resources" when the meaning -is clear from context. - -## Data Source Arguments - -Each data resource is associated with a single data source, which determines -the kind of object (or objects) it reads and what query constraint arguments -are available. - -Each data source in turn belongs to a [provider](./providers.html), -which is a plugin for Terraform that offers a collection of resource types and -data sources that most often belong to a single cloud or on-premises -infrastructure platform. - -Most of the items within the body of a `data` block are defined by and -specific to the selected data source, and these arguments can make full -use of [expressions](./expressions.html) and other dynamic -Terraform language features. - -However, there are some "meta-arguments" that are defined by Terraform itself -and apply across all data sources. These arguments often have additional -restrictions on what language features can be used with them, and are described -in more detail in the following sections. - -## Data Resource Behavior - -If the query constraint arguments for a data resource refer only to constant -values or values that are already known, the data resource will be read and its -state updated during Terraform's "refresh" phase, which runs prior to creating a plan. -This ensures that the retrieved data is available for use during planning and -so Terraform's plan will show the actual values obtained. - -Query constraint arguments may refer to values that cannot be determined until -after configuration is applied, such as the id of a managed resource that has -not been created yet. In this case, reading from the data source is deferred -until the apply phase, and any references to the results of the data resource -elsewhere in configuration will themselves be unknown until after the -configuration has been applied. - -## Local-only Data Sources - -While many data sources correspond to an infrastructure object type that -is accessed via a remote network API, some specialized data sources operate -only within Terraform itself, calculating some results and exposing them -for use elsewhere. - -For example, local-only data sources exist for -[rendering templates](/docs/providers/template/d/file.html), -[reading local files](/docs/providers/local/d/file.html), and -[rendering AWS IAM policies](/docs/providers/aws/d/iam_policy_document.html). - -The behavior of local-only data sources is the same as all other data -sources, but their result data exists only temporarily during a Terraform -operation, and is re-calculated each time a new plan is created. - -## Data Resource Dependencies - -Data resources have the same dependency resolution behavior -[as defined for managed resources](./resources.html#resource-dependencies). -Setting the `depends_on` meta-argument within `data` blocks defers reading of -the data source until after all changes to the dependencies have been applied. - -~> **NOTE:** **In Terraform 0.12 and earlier**, due to the data resource behavior of deferring the read until the apply phase when depending on values that are not yet known, using `depends_on` with `data` resources will force the read to always be deferred to the apply phase, and therefore a configuration that uses `depends_on` with a `data` resource can never converge. Due to this behavior, we do not recommend using `depends_on` with data resources. - - -## Multiple Resource Instances - -Data resources support [`count`](./resources.html#count-multiple-resource-instances-by-count) -and [`for_each`](./resources.html#for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings) -meta-arguments as defined for managed resources, with the same syntax and behavior. - -As with managed resources, when `count` or `for_each` is present it is important to -distinguish the resource itself from the multiple resource _instances_ it -creates. Each instance will separately read from its data source with its -own variant of the constraint arguments, producing an indexed result. - -## Selecting a Non-default Provider Configuration - -Data resources support [the `providers` meta-argument](./resources.html#provider-selecting-a-non-default-provider-configuration) -as defined for managed resources, with the same syntax and behavior. - -## Lifecycle Customizations - -Data resources do not currently have any customization settings available -for their lifecycle, but the `lifecycle` nested block is reserved in case -any are added in future versions. - -## Example - -A data source configuration looks like the following: - -```hcl -# Find the latest available AMI that is tagged with Component = web -data "aws_ami" "web" { - filter { - name = "state" - values = ["available"] - } - - filter { - name = "tag:Component" - values = ["web"] - } - - most_recent = true -} -``` - -## Description - -The `data` block creates a data instance of the given _type_ (first -block label) and _name_ (second block label). The combination of the type -and name must be unique. - -Within the block (the `{ }`) is configuration for the data instance. The -configuration is dependent on the type, and is documented for each -data source in the [providers section](/docs/providers/index.html). - -Each data instance will export one or more attributes, which can be -used in other resources as reference expressions of the form -`data...`. For example: - -```hcl -resource "aws_instance" "web" { - ami = data.aws_ami.web.id - instance_type = "t1.micro" -} -``` - -## Meta-Arguments - -As data sources are essentially a read only subset of resources, they also -support the same [meta-arguments](./resources.html#meta-arguments) of resources -with the exception of the -[`lifecycle` configuration block](./resources.html#lifecycle-lifecycle-customizations). - -### Non-Default Provider Configurations - -Similarly to [resources](./resources.html), when a module has multiple configurations for the same provider you can specify which configuration to use with the `provider` meta-argument: - -```hcl -data "aws_ami" "web" { - provider = aws.west - - # ... -} -``` - -See -[Resources: Selecting a Non-Default Provider Configuration](./resources.html#provider-selecting-a-non-default-provider-configuration) -for more information. - -## Data Source Lifecycle - -If the arguments of a data instance contain no references to computed values, -such as attributes of resources that have not yet been created, then the -data instance will be read and its state updated during Terraform's "refresh" -phase, which by default runs prior to creating a plan. This ensures that the -retrieved data is available for use during planning and the diff will show -the real values obtained. - -Data instance arguments may refer to computed values, in which case the -attributes of the instance itself cannot be resolved until all of its -arguments are defined. In this case, refreshing the data instance will be -deferred until the "apply" phase, and all interpolations of the data instance -attributes will show as "computed" in the plan since the values are not yet -known. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/expressions.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/expressions.html.md index 6b4db611..9aa59acd 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/expressions.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration/expressions.html.md @@ -1,918 +1,157 @@ --- -layout: "docs" -page_title: "Expressions - Configuration Language" +layout: "language" +page_title: "Expressions Landing Page - Configuration Language" sidebar_current: "docs-config-expressions" -description: |- - The Terraform language allows the use of expressions to access data exported - by resources and to transform and combine that data to produce other values. --- -# Expressions +# Expressions Landing Page --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../configuration-0-11/interpolation.html). - -_Expressions_ are used to refer to or compute values within a configuration. -The simplest expressions are just literal values, like `"hello"` or `5`, -but the Terraform language also allows more complex expressions such as -references to data exported by resources, arithmetic, conditional evaluation, -and a number of built-in functions. - -Expressions can be used in a number of places in the Terraform language, -but some contexts limit which expression constructs are allowed, -such as requiring a literal value of a particular type or forbidding -[references to resource attributes](/docs/configuration/expressions.html#references-to-resource-attributes). -Each language feature's documentation describes any restrictions it places on expressions. - -You can experiment with the behavior of Terraform's expressions from -the Terraform expression console, by running -[the `terraform console` command](/docs/commands/console.html). - -The rest of this page describes all of the features of Terraform's -expression syntax. - -## Types and Values - -The result of an expression is a _value_. All values have a _type_, which -dictates where that value can be used and what transformations can be -applied to it. - -The Terraform language uses the following types for its values: - -* `string`: a sequence of Unicode characters representing some text, like - `"hello"`. -* `number`: a numeric value. The `number` type can represent both whole - numbers like `15` and fractional values like `6.283185`. -* `bool`: either `true` or `false`. `bool` values can be used in conditional - logic. -* `list` (or `tuple`): a sequence of values, like - `["us-west-1a", "us-west-1c"]`. Elements in a list or tuple are identified by - consecutive whole numbers, starting with zero. -* `map` (or `object`): a group of values identified by named labels, like - `{name = "Mabel", age = 52}`. - -Strings, numbers, and bools are sometimes called _primitive types._ Lists/tuples and maps/objects are sometimes called _complex types,_ _structural types,_ or _collection types._ - -Finally, there is one special value that has _no_ type: - -* `null`: a value that represents _absence_ or _omission._ If you set an - argument of a resource or module to `null`, Terraform behaves as though you - had completely omitted it — it will use the argument's default value if it has - one, or raise an error if the argument is mandatory. `null` is most useful in - conditional expressions, so you can dynamically omit an argument if a - condition isn't met. - -### Advanced Type Details - -In most situations, lists and tuples behave identically, as do maps and objects. -Whenever the distinction isn't relevant, the Terraform documentation uses each -pair of terms interchangeably (with a historical preference for "list" and -"map"). - -However, module authors and provider developers should understand the -differences between these similar types (and the related `set` type), since they -offer different ways to restrict the allowed values for input variables and -resource arguments. - -For complete details about these types (and an explanation of why the difference -usually doesn't matter), see [Type Constraints](./types.html). - -### Type Conversion - -Expressions are most often used to set values for the arguments of resources and -child modules. In these cases, the argument has an expected type and the given -expression must produce a value of that type. - -Where possible, Terraform automatically converts values from one type to -another in order to produce the expected type. If this isn't possible, Terraform -will produce a type mismatch error and you must update the configuration with a -more suitable expression. - -Terraform automatically converts number and bool values to strings when needed. -It also converts strings to numbers or bools, as long as the string contains a -valid representation of a number or bool value. - -* `true` converts to `"true"`, and vice-versa -* `false` converts to `"false"`, and vice-versa -* `15` converts to `"15"`, and vice-versa - -## Literal Expressions - -A _literal expression_ is an expression that directly represents a particular -constant value. Terraform has a literal expression syntax for each of the value -types described above: - -* Strings are usually represented by a double-quoted sequence of Unicode - characters, `"like this"`. There is also a "heredoc" syntax for more complex - strings. String literals are the most complex kind of literal expression in - Terraform, and have additional documentation on this page: - * See [String Literals](#string-literals) below for information about escape - sequences and the heredoc syntax. - * See [String Templates](#string-templates) below for information about - interpolation and template directives. -* Numbers are represented by unquoted sequences of digits with or without a - decimal point, like `15` or `6.283185`. -* Bools are represented by the unquoted symbols `true` and `false`. -* The null value is represented by the unquoted symbol `null`. -* Lists/tuples are represented by a pair of square brackets containing a - comma-separated sequence of values, like `["a", 15, true]`. - - List literals can be split into multiple lines for readability, but always - require a comma between values. A comma after the final value is allowed, - but not required. Values in a list can be arbitrary expressions. -* Maps/objects are represented by a pair of curly braces containing a series of - ` = ` pairs: - - ```hcl - { - name = "John" - age = 52 - } - ``` - - Key/value pairs can be separated by either a comma or a line break. Values - can be arbitrary expressions. Keys are strings; they can be left unquoted if - they are a valid [identifier](./syntax.html#identifiers), but must be quoted - otherwise. You can use a non-literal expression as a key by wrapping it in - parentheses, like `(var.business_unit_tag_name) = "SRE"`. - -## Indices and Attributes - -[inpage-index]: #indices-and-attributes - -Elements of list/tuple and map/object values can be accessed using -the square-bracket index notation, like `local.list[3]`. The expression within -the brackets must be a whole number for list and tuple values or a string -for map and object values. - -Map/object attributes with names that are valid identifiers can also be accessed -using the dot-separated attribute notation, like `local.object.attrname`. -In cases where a map might contain arbitrary user-specified keys, we recommend -using only the square-bracket index notation (`local.map["keyname"]`). - -## References to Named Values - -Terraform makes several kinds of named values available. Each of these names is -an expression that references the associated value; you can use them as -standalone expressions, or combine them with other expressions to compute new -values. - -The following named values are available: - -* `.` is an object representing a - [managed resource](./resources.html) of the given type - and name. The attributes of the resource can be accessed using - [dot or square bracket notation][inpage-index]. - - Any named value that does not match another pattern listed below - will be interpreted by Terraform as a reference to a managed resource. - - If the resource has the `count` argument set, the value of this expression - is a _list_ of objects representing its instances. - - If the resource has the `for_each` argument set, the value of this expression - is a _map_ of objects representing its instances. - - For more information, see - [references to resource attributes](#references-to-resource-attributes) below. -* `var.` is the value of the - [input variable](./variables.html) of the given name. -* `local.` is the value of the - [local value](./locals.html) of the given name. -* `module..` is the value of the specified - [output value](./outputs.html) from a - [child module](./modules.html) called by the current module. -* `data..` is an object representing a - [data resource](./data-sources.html) of the given data - source type and name. If the resource has the `count` argument set, the value - is a list of objects representing its instances. If the resource has the `for_each` - argument set, the value is a map of objects representing its instances. -* `path.module` is the filesystem path of the module where the expression - is placed. -* `path.root` is the filesystem path of the root module of the configuration. -* `path.cwd` is the filesystem path of the current working directory. In - normal use of Terraform this is the same as `path.root`, but some advanced - uses of Terraform run it from a directory other than the root module - directory, causing these paths to be different. -* `terraform.workspace` is the name of the currently selected - [workspace](/docs/state/workspaces.html). - -Although many of these names use dot-separated paths that resemble -[attribute notation][inpage-index] for elements of object values, they are not -implemented as real objects. This means you must use them exactly as written: -you cannot use square-bracket notation to replace the dot-separated paths, and -you cannot iterate over the "parent object" of a named entity (for example, you -cannot use `aws_instance` in a `for` expression). - -### Local Named Values - -Within the bodies of certain expressions, or in some other specific contexts, -there are other named values available beyond the global values listed above. -These local names are described in the documentation for the specific contexts -where they appear. Some of most common local names are: - -- `count.index`, in resources that use - [the `count` meta-argument](./resources.html#count-multiple-resource-instances-by-count). -- `each.key` / `each.value`, in resources that use - [the `for_each` meta-argument](./resources.html#for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings). -- `self`, in [provisioner](../provisioners/index.html) and - [connection](../provisioners/connection.html) blocks. - --> **Note:** Local names are often referred to as _variables_ or -_temporary variables_ in their documentation. These are not [input -variables](./variables.html); they are just arbitrary names -that temporarily represent a value. - -### Named Values and Dependencies - -Constructs like resources and module calls often use references to named values -in their block bodies, and Terraform analyzes these expressions to automatically -infer dependencies between objects. For example, an expression in a resource -argument that refers to another managed resource creates an implicit dependency -between the two resources. - -### References to Resource Attributes - -The most common reference type is a reference to an attribute of a resource -which has been declared either with a `resource` or `data` block. Because -the contents of such blocks can be quite complicated themselves, expressions -referring to these contents can also be complicated. - -Consider the following example resource block: +To improve navigation, we've split the old Expressions page into several smaller +pages. -```hcl -resource "aws_instance" "example" { - ami = "ami-abc123" - instance_type = "t2.micro" - - ebs_block_device { - device_name = "sda2" - volume_size = 16 - } - ebs_block_device { - device_name = "sda3" - volume_size = 20 - } -} -``` + + + + + -The documentation for [`aws_instance`](/docs/providers/aws/r/instance.html) -lists all of the arguments and nested blocks supported for this resource type, -and also lists a number of attributes that are _exported_ by this resource -type. All of these different resource type schema constructs are available -for use in references, as follows: - -* The `ami` argument set in the configuration can be used elsewhere with - the reference expression `aws_instance.example.ami`. -* The `id` attribute exported by this resource type can be read using the - same syntax, giving `aws_instance.example.id`. -* The arguments of the `ebs_block_device` nested blocks can be accessed using - a [splat expression](#splat-expressions). For example, to obtain a list of - all of the `device_name` values, use - `aws_instance.example.ebs_block_device[*].device_name`. -* The nested blocks in this particular resource type do not have any exported - attributes, but if `ebs_block_device` were to have a documented `id` - attribute then a list of them could be accessed similarly as - `aws_instance.example.ebs_block_device[*].id`. -* Sometimes nested blocks are defined as taking a logical key to identify each - block, which serves a similar purpose as the resource's own name by providing - a convenient way to refer to that single block in expressions. If `aws_instance` - had a hypothetical nested block type `device` that accepted such a key, it - would look like this in configuration: - - ```hcl - device "foo" { - size = 2 - } - device "bar" { - size = 4 - } - ``` - - Arguments inside blocks with _keys_ can be accessed using index syntax, such - as `aws_instance.example.device["foo"].size`. - - To obtain a map of values of a particular argument for _labelled_ nested - block types, use a [`for` expression](#for-expressions): - `{for k, device in aws_instance.example.device : k => device.size}`. - -When a resource has the -[`count`](https://www.terraform.io/docs/configuration/resources.html#count-multiple-resource-instances-by-count) -argument set, the resource itself becomes a _list_ of instance objects rather than -a single object. In that case, access the attributes of the instances using -either [splat expressions](#splat-expressions) or index syntax: - -* `aws_instance.example[*].id` returns a list of all of the ids of each of the - instances. -* `aws_instance.example[0].id` returns just the id of the first instance. - -When a resource has the -[`for_each`](/docs/configuration/resources.html#for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings) -argument set, the resource itself becomes a _map_ of instance objects rather than -a single object, and attributes of instances must be specified by key, or can -be accessed using a [`for` expression](#for-expressions). - -* `aws_instance.example["a"].id` returns the id of the "a"-keyed resource. -* `[for value in aws_instance.example: value.id]` returns a list of all of the ids - of each of the instances. - -Note that unlike `count`, splat expressions are _not_ directly applicable to resources managed with `for_each`, as splat expressions are for lists only. You may apply a splat expression to values in a map like so: - -* `values(aws_instance.example)[*].id` - -### Local Named Values - -Within the bodies of certain expressions, or in some other specific contexts, -there are other named values available beyond the global values listed above. -(For example, the body of a resource block where `count` is set can use a -special `count.index` value.) These local names are described in the -documentation for the specific contexts where they appear. - --> **Note:** Local named values are often referred to as _variables_ or -_temporary variables_ in their documentation. These are not [input -variables](./variables.html); they are just arbitrary names -that temporarily represent a value. - -### Values Not Yet Known - -When Terraform is planning a set of changes that will apply your configuration, -some resource attribute values cannot be populated immediately because their -values are decided dynamically by the remote system. For example, if a -particular remote object type is assigned a generated unique id on creation, -Terraform cannot predict the value of this id until the object has been created. - -To allow expressions to still be evaluated during the plan phase, Terraform -uses special "unknown value" placeholders for these results. In most cases you -don't need to do anything special to deal with these, since the Terraform -language automatically handles unknown values during expressions, so that -for example adding a known value to an unknown value automatically produces -an unknown value as the result. - -However, there are some situations where unknown values _do_ have a significant -effect: - -* The `count` meta-argument for resources cannot be unknown, since it must - be evaluated during the plan phase to determine how many instances are to - be created. - -* If unknown values are used in the configuration of a data resource, that - data resource cannot be read during the plan phase and so it will be deferred - until the apply phase. In this case, the results of the data resource will - _also_ be unknown values. - -* If an unknown value is assigned to an argument inside a `module` block, - any references to the corresponding input variable within the child module - will use that unknown value. - -* If an unknown value is used in the `value` argument of an output value, - any references to that output value in the parent module will use that - unknown value. - -* Terraform will attempt to validate that unknown values are of suitable - types where possible, but incorrect use of such values may not be detected - until the apply phase, causing the apply to fail. - -Unknown values appear in the `terraform plan` output as `(not yet known)`. - -## Arithmetic and Logical Operators +## Types and Values, Literal Expressions, Indices and Attributes -An _operator_ is a type of expression that transforms or combines one or more -other expressions. Operators either combine two values in some way to -produce a third result value, or transform a single given value to -produce a single result. +Terraform's types are `string`, `number`, `bool`, `list`, `tuple`, `map`, +`object`, and `null`. -Operators that work on two values place an operator symbol between the two -values, similar to mathematical notation: `1 + 2`. Operators that work on -only one value place an operator symbol before that value, like -`!true`. +This information has moved to +[Types and Values](/docs/language/expressions/types.html). -The Terraform language has a set of operators for both arithmetic and logic, -which are similar to operators in programming languages such as JavaScript -or Ruby. +
-When multiple operators are used together in an expression, they are evaluated -in the following order of operations: -1. `!`, `-` (multiplication by `-1`) -1. `*`, `/`, `%` -1. `+`, `-` (subtraction) -1. `>`, `>=`, `<`, `<=` -1. `==`, `!=` -1. `&&` -1. `||` -Parentheses can be used to override the default order of operations. Without -parentheses, higher levels are evaluated first, so `1 + 2 * 3` is interpreted -as `1 + (2 * 3)` and _not_ as `(1 + 2) * 3`. + + + + + + -The different operators can be gathered into a few different groups with -similar behavior, as described below. Each group of operators expects its -given values to be of a particular type. Terraform will attempt to convert -values to the required type automatically, or will produce an error message -if this automatic conversion is not possible. +## References to Named Values (Resource Attributes, Variables, etc.) -### Arithmetic Operators +You can refer to certain values by name, like `var.some_variable` or +`aws_instance.example.ami`. -The arithmetic operators all expect number values and produce number values -as results: +This information has moved to +[References to Values](/docs/language/expressions/references.html). -* `a + b` returns the result of adding `a` and `b` together. -* `a - b` returns the result of subtracting `b` from `a`. -* `a * b` returns the result of multiplying `a` and `b`. -* `a / b` returns the result of dividing `a` by `b`. -* `a % b` returns the remainder of dividing `a` by `b`. This operator is - generally useful only when used with whole numbers. -* `-a` returns the result of multiplying `a` by `-1`. +
-### Equality Operators -The equality operators both take two values of any type and produce boolean -values as results. -* `a == b` returns `true` if `a` and `b` both have the same type and the same - value, or `false` otherwise. -* `a != b` is the opposite of `a == b`. + + + + -### Comparison Operators +## Arithmetic and Logical Operators -The comparison operators all expect number values and produce boolean values -as results. +Operators are expressions that transform other expressions, like adding two +numbers (`+`) or comparing two values to get a bool (`==`, `>=`, etc.). -* `a < b` returns `true` if `a` is less than `b`, or `false` otherwise. -* `a <= b` returns `true` if `a` is less than or equal to `b`, or `false` - otherwise. -* `a > b` returns `true` if `a` is greater than `b`, or `false` otherwise. -* `a >= b` returns `true` if `a` is greater than or equal to `b`, or `false` otherwise. +This information has moved to +[Operators](/docs/language/expressions/references.html). -### Logical Operators +
-The logical operators all expect bool values and produce bool values as results. -* `a || b` returns `true` if either `a` or `b` is `true`, or `false` if both are `false`. -* `a && b` returns `true` if both `a` and `b` are `true`, or `false` if either one is `false`. -* `!a` returns `true` if `a` is `false`, and `false` if `a` is `true`. ## Conditional Expressions -A _conditional expression_ uses the value of a bool expression to select one of -two values. - -The syntax of a conditional expression is as follows: - -```hcl -condition ? true_val : false_val -``` +The `condition ? true_val : false_val` expression chooses between two +expressions based on a bool condition. -If `condition` is `true` then the result is `true_val`. If `condition` is -`false` then the result is `false_val`. +This information has moved to +[Conditional Expressions](/docs/language/expressions/conditionals.html). -A common use of conditional expressions is to define defaults to replace -invalid values: +
-``` -var.a != "" ? var.a : "default-a" -``` -If `var.a` is an empty string then the result is `"default-a"`, but otherwise -it is the actual value of `var.a`. -Any of the equality, comparison, and logical operators can be used to define -the condition. The two result values may be of any type, but they must both -be of the _same_ type so that Terraform can determine what type the whole -conditional expression will return without knowing the condition value. + + ## Function Calls -The Terraform language has a number of -[built-in functions](./functions.html) that can be used -within expressions as another way to transform and combine values. These -are similar to the operators but all follow a common syntax: - -```hcl -(, ) -``` - -The function name specifies which function to call. Each defined function -expects a specific number of arguments with specific value types, and returns a -specific value type as a result. - -Some functions take an arbitrary number of arguments. For example, the `min` -function takes any amount of number arguments and returns the one that is -numerically smallest: +Terraform's functions can be called like `function_name(arg1, arg2)`. -```hcl -min(55, 3453, 2) -``` +This information has moved to +[Function Calls](/docs/language/expressions/function-calls.html). -### Expanding Function Arguments - -If the arguments to pass to a function are available in a list or tuple value, -that value can be _expanded_ into separate arguments. Provide the list value as -an argument and follow it with the `...` symbol: - -```hcl -min([55, 2453, 2]...) -``` +
-The expansion symbol is three periods (`...`), not a Unicode ellipsis character -(`…`). Expansion is a special syntax that is only available in function calls. -### Available Functions -For a full list of available functions, see -[the function reference](./functions.html). + ## `for` Expressions -A _`for` expression_ creates a complex type value by transforming -another complex type value. Each element in the input value -can correspond to either one or zero values in the result, and an arbitrary -expression can be used to transform each input element into an output element. - -For example, if `var.list` is a list of strings, then the following expression -produces a list of strings with all-uppercase letters: - -```hcl -[for s in var.list : upper(s)] -``` - -This `for` expression iterates over each element of `var.list`, and then -evaluates the expression `upper(s)` with `s` set to each respective element. -It then builds a new tuple value with all of the results of executing that -expression in the same order. - -The type of brackets around the `for` expression decide what type of result -it produces. The above example uses `[` and `]`, which produces a tuple. If -`{` and `}` are used instead, the result is an object, and two result -expressions must be provided separated by the `=>` symbol: +Expressions like `[for s in var.list : upper(s)]` can transform a complex type +value into another complex type value. -```hcl -{for s in var.list : s => upper(s)} -``` - -This expression produces an object whose attributes are the original elements -from `var.list` and their corresponding values are the uppercase versions. - -A `for` expression can also include an optional `if` clause to filter elements -from the source collection, which can produce a value with fewer elements than -the source: - -``` -[for s in var.list : upper(s) if s != ""] -``` +This information has moved to +[For Expressions](/docs/language/expressions/for.html). -The source value can also be an object or map value, in which case two -temporary variable names can be provided to access the keys and values -respectively: +
-``` -[for k, v in var.map : length(k) + length(v)] -``` -Finally, if the result type is an object (using `{` and `}` delimiters) then -the value result expression can be followed by the `...` symbol to group -together results that have a common key: -``` -{for s in var.list : substr(s, 0, 1) => s... if s != ""} -``` - -For expressions are particularly useful when combined with other language -features to combine collections together in various ways. For example, -the following two patterns are commonly used when constructing map values -to use with [resource `for_each`](./resources.html#for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings): - -* Transform a multi-level nested structure into a flat list by - [using nested `for` expressions with the `flatten` function](./functions/flatten.html#flattening-nested-structures-for-for_each). -* Produce an exhaustive list of combinations of elements from two or more - collections by - [using the `setproduct` function inside a `for` expression](./functions/setproduct.html#finding-combinations-for-for_each). + + ## Splat Expressions -A _splat expression_ provides a more concise way to express a common -operation that could otherwise be performed with a `for` expression. - -If `var.list` is a list of objects that all have an attribute `id`, then -a list of the ids could be produced with the following `for` expression: - -```hcl -[for o in var.list : o.id] -``` - -This is equivalent to the following _splat expression:_ - -```hcl -var.list[*].id -``` - -The special `[*]` symbol iterates over all of the elements of the list given -to its left and accesses from each one the attribute name given on its -right. A splat expression can also be used to access attributes and indexes -from lists of complex types by extending the sequence of operations to the -right of the symbol: - -```hcl -var.list[*].interfaces[0].name -``` - -The above expression is equivalent to the following `for` expression: - -```hcl -[for o in var.list : o.interfaces[0].name] -``` - -Splat expressions are for lists only (and thus cannot be used [to reference resources -created with `for_each`](/docs/configuration/resources.html#referring-to-instances-1), -which are represented as maps in Terraform). However, if a splat expression is applied -to a value that is _not_ a list or tuple then the value is automatically wrapped in -a single-element list before processing. - -For example, `var.single_object[*].id` is equivalent to `[var.single_object][*].id`, -or effectively `[var.single_object.id]`. This behavior is not interesting in most cases, -but it is particularly useful when referring to resources that may or may -not have `count` set, and thus may or may not produce a tuple value: - -```hcl -aws_instance.example[*].id -``` - -The above will produce a list of ids whether `aws_instance.example` has -`count` set or not, avoiding the need to revise various other expressions -in the configuration when a particular resource switches to and from -having `count` set. - -### Legacy (Attribute-only) Splat Expressions - -An older variant of the splat expression is available for compatibility with -code written in older versions of the Terraform language. This is a less useful -version of the splat expression, and should be avoided in new configurations. - -An "attribute-only" splat expression is indicated by the sequence `.*` (instead -of `[*]`): - -``` -var.list.*.interfaces[0].name -``` - -This form has a subtly different behavior, equivalent to the following -`for` expression: - -``` -[for o in var.list : o.interfaces][0].name -``` - -Notice that with the attribute-only splat expression the index operation -`[0]` is applied to the result of the iteration, rather than as part of -the iteration itself. - -## `dynamic` blocks - -Within top-level block constructs like resources, expressions can usually be -used only when assigning a value to an argument using the `name = expression` -form. This covers many uses, but some resource types include repeatable _nested -blocks_ in their arguments, which do not accept expressions: - -```hcl -resource "aws_elastic_beanstalk_environment" "tfenvtest" { - name = "tf-test-name" # can use expressions here - - setting { - # but the "setting" block is always a literal block - } -} -``` - -You can dynamically construct repeatable nested blocks like `setting` using a -special `dynamic` block type, which is supported inside `resource`, `data`, -`provider`, and `provisioner` blocks: - -```hcl -resource "aws_elastic_beanstalk_environment" "tfenvtest" { - name = "tf-test-name" - application = "${aws_elastic_beanstalk_application.tftest.name}" - solution_stack_name = "64bit Amazon Linux 2018.03 v2.11.4 running Go 1.12.6" - - dynamic "setting" { - for_each = var.settings - content { - namespace = setting.value["namespace"] - name = setting.value["name"] - value = setting.value["value"] - } - } -} -``` - -A `dynamic` block acts much like a `for` expression, but produces nested blocks -instead of a complex typed value. It iterates over a given complex value, and -generates a nested block for each element of that complex value. - -- The label of the dynamic block (`"setting"` in the example above) specifies - what kind of nested block to generate. -- The `for_each` argument provides the complex value to iterate over. -- The `iterator` argument (optional) sets the name of a temporary variable - that represents the current element of the complex value. If omitted, the name - of the variable defaults to the label of the `dynamic` block (`"setting"` in - the example above). -- The `labels` argument (optional) is a list of strings that specifies the block - labels, in order, to use for each generated block. You can use the temporary - iterator variable in this value. -- The nested `content` block defines the body of each generated block. You can - use the temporary iterator variable inside this block. - -Since the `for_each` argument accepts any collection or structural value, -you can use a `for` expression or splat expression to transform an existing -collection. - -The iterator object (`setting` in the example above) has two attributes: - -* `key` is the map key or list element index for the current element. If the - `for_each` expression produces a _set_ value then `key` is identical to - `value` and should not be used. -* `value` is the value of the current element. - -A `dynamic` block can only generate arguments that belong to the resource type, -data source, provider or provisioner being configured. It is _not_ possible -to generate meta-argument blocks such as `lifecycle` and `provisioner` -blocks, since Terraform must process these before it is safe to evaluate -expressions. - -The `for_each` value must be a map or set with one element per desired -nested block. If you need to declare resource instances based on a nested -data structure or combinations of elements from multiple data structures you -can use Terraform expressions and functions to derive a suitable value. -For some common examples of such situations, see the -[`flatten`](/docs/configuration/functions/flatten.html) -and -[`setproduct`](/docs/configuration/functions/setproduct.html) -functions. - -### Best Practices for `dynamic` Blocks - -Overuse of `dynamic` blocks can make configuration hard to read and maintain, so -we recommend using them only when you need to hide details in order to build a -clean user interface for a re-usable module. Always write nested blocks out -literally where possible. - -## String Literals - -The Terraform language has two different syntaxes for string literals. The -most common is to delimit the string with quote characters (`"`), like -`"hello"`. In quoted strings, the backslash character serves as an escape -sequence, with the following characters selecting the escape behavior: - -| Sequence | Replacement | -| ------------ | ----------------------------------------------------------------------------- | -| `\n` | Newline | -| `\r` | Carriage Return | -| `\t` | Tab | -| `\"` | Literal quote (without terminating the string) | -| `\\` | Literal backslash | -| `\uNNNN` | Unicode character from the basic multilingual plane (NNNN is four hex digits) | -| `\UNNNNNNNN` | Unicode character from supplementary planes (NNNNNNNN is eight hex digits) | - -The alternative syntax for string literals is the so-called "heredoc" style, -inspired by Unix shell languages. This style allows multi-line strings to -be expressed more clearly by using a custom delimiter word on a line of its -own to close the string: - -```hcl -< -Within quoted and heredoc string expressions, the sequences `${` and `%{` begin -_template sequences_. Templates let you directly embed expressions into a string -literal, to dynamically construct strings from other values. -### Interpolation -A `${ ... }` sequence is an _interpolation,_ which evaluates the expression -given between the markers, converts the result to a string if necessary, and -then inserts it into the final string: + + -```hcl -"Hello, ${var.name}!" -``` - -In the above example, the named object `var.name` is accessed and its value -inserted into the string, producing a result like "Hello, Juan!". - -### Directives +## `dynamic` Blocks -A `%{ ... }` sequence is a _directive_, which allows for conditional -results and iteration over collections, similar to conditional -and `for` expressions. +The special `dynamic` block type serves the same purpose as a `for` expression, +except it creates multiple repeatable nested blocks instead of a complex value. -The following directives are supported: +This information has moved to +[Dynamic Blocks](/docs/language/expressions/dynamic-blocks.html). -* The `if `/`else`/`endif` directive chooses between two templates based - on the value of a bool expression: +
- ```hcl - "Hello, %{ if var.name != "" }${var.name}%{ else }unnamed%{ endif }!" - ``` - The `else` portion may be omitted, in which case the result is an empty - string if the condition expression returns `false`. -* The `for in ` / `endfor` directive iterates over the - elements of a given collection or structural value and evaluates a given - template once for each element, concatenating the results together: + + + + - ```hcl - < diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions.html.md deleted file mode 100644 index 2b866e2f..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions.html.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -layout: "functions" -page_title: "Functions - Configuration Language" -sidebar_current: "docs-config-functions" -description: |- - The Terraform language has a number of built-in functions that can be called - from within expressions to transform and combine values. ---- - -# Built-in Functions - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../configuration-0-11/interpolation.html). - -The Terraform language includes a number of built-in functions that you can -call from within expressions to transform and combine values. The general -syntax for function calls is a function name followed by comma-separated -arguments in parentheses: - -```hcl -max(5, 12, 9) -``` - -For more details on syntax, see -[_Function Calls_](./expressions.html#function-calls) -on the Expressions page. - -The Terraform language does not support user-defined functions, and so only -the functions built in to the language are available for use. The navigation -for this section includes a list of all of the available built-in functions. - -You can experiment with the behavior of Terraform's built-in functions from -the Terraform expression console, by running -[the `terraform console` command](/docs/commands/console.html): - -``` -> max(5, 12, 9) -12 -``` - -The examples in the documentation for each function use console output to -illustrate the result of calling the function with different parameters. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/abs.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/abs.html.md deleted file mode 100644 index 012bd00b..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/abs.html.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -layout: "functions" -page_title: "abs - Functions - Configuration Language" -sidebar_current: "docs-funcs-numeric-abs" -description: |- - The abs function returns the absolute value of the given number. ---- - -# `abs` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`abs` returns the absolute value of the given number. In other words, if the -number is zero or positive then it is returned as-is, but if it is negative -then it is multiplied by -1 to make it positive before returning it. - -## Examples - -``` -> abs(23) -23 -> abs(0) -0 -> abs(-12.4) -12.4 -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/alltrue.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/alltrue.html.md deleted file mode 100644 index 173685c1..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/alltrue.html.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -layout: functions -page_title: alltrue - Functions - Configuration Language -sidebar_current: docs-funcs-collection-alltrue -description: |- - The alltrue function determines whether all elements of a collection - are true or "true". If the collection is empty, it returns true. ---- - -# `alltrue` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`alltrue` returns `true` if all elements in a given collection are `true` -or `"true"`. It also returns `true` if the collection is empty. - -```hcl -alltrue(list) -``` - -## Examples - -```command -> alltrue(["true", true]) -true -> alltrue([true, false]) -false -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/ceil.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/ceil.html.md deleted file mode 100644 index deb4dada..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/ceil.html.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -layout: "functions" -page_title: "ceil - Functions - Configuration Language" -sidebar_current: "docs-funcs-numeric-ceil" -description: |- - The ceil function returns the closest whole number greater than or equal to - the given value. ---- - -# `ceil` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`ceil` returns the closest whole number that is greater than or equal to the -given value, which may be a fraction. - -## Examples - -``` -> ceil(5) -5 -> ceil(5.1) -6 -``` - -## Related Functions - -* [`floor`](./floor.html), which rounds to the nearest whole number _less than_ - or equal. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/chomp.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/chomp.html.md deleted file mode 100644 index 1e8db54a..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/chomp.html.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -layout: "functions" -page_title: "chomp - Functions - Configuration Language" -sidebar_current: "docs-funcs-string-chomp" -description: |- - The chomp function removes newline characters at the end of a string. ---- - -# `chomp` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`chomp` removes newline characters at the end of a string. - -This can be useful if, for example, the string was read from a file that has -a newline character at the end. - -## Examples - -``` -> chomp("hello\n") -hello -> chomp("hello\r\n") -hello -> chomp("hello\n\n") -hello -``` - -## Related Functions - -* [`trimspace`](./trimspace.html), which removes all types of whitespace from - both the start and the end of a string. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/coalesce.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/coalesce.html.md deleted file mode 100644 index e38a0971..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/coalesce.html.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -layout: "functions" -page_title: "coalesce - Functions - Configuration Language" -sidebar_current: "docs-funcs-collection-coalesce-x" -description: |- - The coalesce function takes any number of arguments and returns the - first one that isn't null nor empty. ---- - -# `coalesce` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`coalesce` takes any number of arguments and returns the first one -that isn't null or an empty string. - -## Examples - -``` -> coalesce("a", "b") -a -> coalesce("", "b") -b -> coalesce(1,2) -1 -``` - -To perform the `coalesce` operation with a list of strings, use the `...` -symbol to expand the list as arguments: - -``` -> coalesce(["", "b"]...) -b -``` - -## Related Functions - -* [`coalescelist`](./coalescelist.html) performs a similar operation with - list arguments rather than individual arguments. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/compact.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/compact.html.md deleted file mode 100644 index 9659efa5..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/compact.html.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -layout: "functions" -page_title: "compact - Functions - Configuration Language" -sidebar_current: "docs-funcs-collection-compact" -description: |- - The compact function removes empty string elements from a list. ---- - -# `compact` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`compact` takes a list of strings and returns a new list with any empty string -elements removed. - -## Examples - -``` -> compact(["a", "", "b", "c"]) -[ - "a", - "b", - "c", -] -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/concat.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/concat.html.md deleted file mode 100644 index 032ec8c8..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/concat.html.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -layout: "functions" -page_title: "concat - Functions - Configuration Language" -sidebar_current: "docs-funcs-collection-concat" -description: |- - The concat function combines two or more lists into a single list. ---- - -# `concat` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`concat` takes two or more lists and combines them into a single list. - -## Examples - -``` -> concat(["a", ""], ["b", "c"]) -[ - "a", - "", - "b", - "c", -] -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/contains.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/contains.html.md deleted file mode 100644 index ff21003f..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/contains.html.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -layout: "functions" -page_title: "contains - Functions - Configuration Language" -sidebar_current: "docs-funcs-collection-contains" -description: |- - The contains function determines whether a list or set contains a given value. ---- - -# `contains` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`contains` determines whether a given list or set contains a given single value -as one of its elements. - -```hcl -contains(list, value) -``` - -## Examples - -``` -> contains(["a", "b", "c"], "a") -true -> contains(["a", "b", "c"], "d") -false -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/distinct.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/distinct.html.md deleted file mode 100644 index a28be11d..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/distinct.html.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -layout: "functions" -page_title: "distinct - Functions - Configuration Language" -sidebar_current: "docs-funcs-collection-distinct" -description: |- - The distinct function removes duplicate elements from a list. ---- - -# `distinct` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`distinct` takes a list and returns a new list with any duplicate elements -removed. - -The first occurrence of each value is retained and the relative ordering of -these elements is preserved. - -## Examples - -``` -> distinct(["a", "b", "a", "c", "d", "b"]) -[ - "a", - "b", - "c", - "d", -] -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/file.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/file.html.md deleted file mode 100644 index e34ada9f..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/file.html.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -layout: "functions" -page_title: "file - Functions - Configuration Language" -sidebar_current: "docs-funcs-file-file-x" -description: |- - The file function reads the contents of the file at the given path and - returns them as a string. ---- - -# `file` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`file` reads the contents of a file at the given path and returns them as -a string. - -```hcl -file(path) -``` - -Strings in the Terraform language are sequences of Unicode characters, so -this function will interpret the file contents as UTF-8 encoded text and -return the resulting Unicode characters. If the file contains invalid UTF-8 -sequences then this function will produce an error. - -This function can be used only with files that already exist on disk -at the beginning of a Terraform run. Functions do not participate in the -dependency graph, so this function cannot be used with files that are generated -dynamically during a Terraform operation. We do not recommend using dynamic -local files in Terraform configurations, but in rare situations where this is -necessary you can use -[the `local_file` data source](/docs/providers/local/d/file.html) -to read files while respecting resource dependencies. - -## Examples - -``` -> file("${path.module}/hello.txt") -Hello World -``` - -## Related Functions - -* [`filebase64`](./filebase64.html) also reads the contents of a given file, - but returns the raw bytes in that file Base64-encoded, rather than - interpreting the contents as UTF-8 text. -* [`fileexists`](./fileexists.html) determines whether a file exists - at a given path. -* [`templatefile`](./templatefile.html) renders using a file from disk as a - template. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filebase64sha256.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filebase64sha256.html.md deleted file mode 100644 index f9e29594..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filebase64sha256.html.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -layout: "functions" -page_title: "filebase64sha256 - Functions - Configuration Language" -sidebar_current: "docs-funcs-crypto-filebase64sha256" -description: |- - The filebase64sha256 function computes the SHA256 hash of the contents of - a given file and encodes it with Base64. ---- - -# `filebase64sha256` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`filebase64sha256` is a variant of [`base64sha256`](./base64sha256.html) -that hashes the contents of a given file rather than a literal string. - -This is similar to `base64sha256(file(filename))`, but -because [`file`](./file.html) accepts only UTF-8 text it cannot be used to -create hashes for binary files. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filebase64sha512.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filebase64sha512.html.md deleted file mode 100644 index 77de9fac..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filebase64sha512.html.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -layout: "functions" -page_title: "filebase64sha512 - Functions - Configuration Language" -sidebar_current: "docs-funcs-crypto-filebase64sha512" -description: |- - The filebase64sha512 function computes the SHA512 hash of the contents of - a given file and encodes it with Base64. ---- - -# `filebase64sha512` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`filebase64sha512` is a variant of [`base64sha512`](./base64sha512.html) -that hashes the contents of a given file rather than a literal string. - -This is similar to `base64sha512(file(filename))`, but -because [`file`](./file.html) accepts only UTF-8 text it cannot be used to -create hashes for binary files. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filemd5.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filemd5.html.md deleted file mode 100644 index e9c41c77..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filemd5.html.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -layout: "functions" -page_title: "filemd5 - Functions - Configuration Language" -sidebar_current: "docs-funcs-crypto-filemd5" -description: |- - The filemd5 function computes the MD5 hash of the contents of - a given file and encodes it as hex. ---- - -# `filemd5` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`filemd5` is a variant of [`md5`](./md5.html) -that hashes the contents of a given file rather than a literal string. - -This is similar to `md5(file(filename))`, but -because [`file`](./file.html) accepts only UTF-8 text it cannot be used to -create hashes for binary files. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filesha1.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filesha1.html.md deleted file mode 100644 index fbe9d620..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filesha1.html.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -layout: "functions" -page_title: "filesha1 - Functions - Configuration Language" -sidebar_current: "docs-funcs-crypto-filesha1" -description: |- - The filesha1 function computes the SHA1 hash of the contents of - a given file and encodes it as hex. ---- - -# `filesha1` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`filesha1` is a variant of [`sha1`](./sha1.html) -that hashes the contents of a given file rather than a literal string. - -This is similar to `sha1(file(filename))`, but -because [`file`](./file.html) accepts only UTF-8 text it cannot be used to -create hashes for binary files. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filesha256.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filesha256.html.md deleted file mode 100644 index ac8b2055..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filesha256.html.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -layout: "functions" -page_title: "filesha256 - Functions - Configuration Language" -sidebar_current: "docs-funcs-crypto-filesha256" -description: |- - The filesha256 function computes the SHA256 hash of the contents of - a given file and encodes it as hex. ---- - -# `filesha256` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`filesha256` is a variant of [`sha256`](./sha256.html) -that hashes the contents of a given file rather than a literal string. - -This is similar to `sha256(file(filename))`, but -because [`file`](./file.html) accepts only UTF-8 text it cannot be used to -create hashes for binary files. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filesha512.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filesha512.html.md deleted file mode 100644 index 6bf40195..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filesha512.html.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -layout: "functions" -page_title: "filesha512 - Functions - Configuration Language" -sidebar_current: "docs-funcs-crypto-filesha512" -description: |- - The filesha512 function computes the SHA512 hash of the contents of - a given file and encodes it as hex. ---- - -# `filesha512` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`filesha512` is a variant of [`sha512`](./sha512.html) -that hashes the contents of a given file rather than a literal string. - -This is similar to `sha512(file(filename))`, but -because [`file`](./file.html) accepts only UTF-8 text it cannot be used to -create hashes for binary files. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/floor.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/floor.html.md deleted file mode 100644 index eb0ad924..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/floor.html.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -layout: "functions" -page_title: "floor - Functions - Configuration Language" -sidebar_current: "docs-funcs-numeric-floor" -description: |- - The floor function returns the closest whole number less than or equal to - the given value. ---- - -# `floor` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`floor` returns the closest whole number that is less than or equal to the -given value, which may be a fraction. - -## Examples - -``` -> floor(5) -5 -> floor(4.9) -4 -``` - -## Related Functions - -* [`ceil`](./ceil.html), which rounds to the nearest whole number _greater than_ - or equal. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/index.html.md deleted file mode 100644 index 59575c91..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/index.html.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -layout: "functions" -page_title: "index - Functions - Configuration Language" -sidebar_current: "docs-funcs-collection-index" -description: |- - The index function finds the element index for a given value in a list. ---- - -# `index` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`index` finds the element index for a given value in a list. - -```hcl -index(list, value) -``` - -The returned index is zero-based. This function produces an error if the given -value is not present in the list. - -## Examples - -``` -> index(["a", "b", "c"], "b") -1 -``` - -## Related Functions - -* [`element`](./element.html) retrieves a particular element from a list given - its index. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/keys.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/keys.html.md deleted file mode 100644 index 87097a1d..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/keys.html.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -layout: "functions" -page_title: "keys - Functions - Configuration Language" -sidebar_current: "docs-funcs-collection-keys" -description: |- - The keys function returns a list of the keys in a given map. ---- - -# `keys` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`keys` takes a map and returns a list containing the keys from that map. - -The keys are returned in lexicographical order, ensuring that the result will -be identical as long as the keys in the map don't change. - -## Examples - -``` -> keys({a=1, c=2, d=3}) -[ - "a", - "c", - "d", -] -``` - -## Related Functions - -* [`values`](./values.html) returns a list of the _values_ from a map. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/list.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/list.html.md deleted file mode 100644 index 0313bac1..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/list.html.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -layout: "functions" -page_title: "list - Functions - Configuration Language" -sidebar_current: "docs-funcs-collection-list" -description: |- - The list function constructs a list from some given elements. ---- - -# `list` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -~> **This function is deprecated.** From Terraform v0.12, the Terraform -language has built-in syntax for creating lists using the `[` and `]` -delimiters. Use the built-in syntax instead. The `list` function will be -removed in a future version of Terraform. - -`list` takes an arbitrary number of arguments and returns a list containing -those values in the same order. - -## Examples - -``` -> list("a", "b", "c") -[ - "a", - "b", - "c", -] -``` - -Do not use the above form in Terraform v0.12 or above. Instead, use the -built-in list construction syntax, which achieves the same result: - -``` -> ["a", "b", "c"] -[ - "a", - "b", - "c", -] -``` - -## Related Functions - -* [`tolist`](./tolist.html) converts a set value to a list. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/log.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/log.html.md deleted file mode 100644 index 86976e51..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/log.html.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -layout: "functions" -page_title: "log - Functions - Configuration Language" -sidebar_current: "docs-funcs-numeric-log" -description: |- - The log function returns the logarithm of a given number in a given base. ---- - -# `log` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`log` returns the logarithm of a given number in a given base. - -```hcl -log(number, base) -``` - -## Examples - -``` -> log(50, 10) -1.6989700043360185 -> log(16, 2) -4 -``` - -`log` and `ceil` can be used together to find the minimum number of binary -digits required to represent a given number of distinct values: - -``` -> ceil(log(15, 2)) -4 -> ceil(log(16, 2)) -4 -> ceil(log(17, 2)) -5 -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/lookup.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/lookup.html.md deleted file mode 100644 index 9dae41af..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/lookup.html.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -layout: "functions" -page_title: "lookup - Functions - Configuration Language" -sidebar_current: "docs-funcs-collection-lookup" -description: |- - The lookup function retrieves an element value from a map given its key. ---- - -# `lookup` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`lookup` retrieves the value of a single element from a map, given its key. -If the given key does not exist, a the given default value is returned instead. - -``` -lookup(map, key, default) -``` - --> For historical reasons, the `default` parameter is actually optional. However, -omitting `default` is deprecated since v0.7 because that would then be -equivalent to the native index syntax, `map[key]`. - -## Examples - -``` -> lookup({a="ay", b="bee"}, "a", "what?") -ay -> lookup({a="ay", b="bee"}, "c", "what?") -what? -``` - -## Related Functions - -* [`element`](./element.html) retrieves a value from a _list_ given its _index_. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/lower.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/lower.html.md deleted file mode 100644 index 15dd95eb..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/lower.html.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -layout: "functions" -page_title: "lower - Functions - Configuration Language" -sidebar_current: "docs-funcs-string-lower" -description: |- - The lower function converts all cased letters in the given string to lowercase. ---- - -# `lower` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`lower` converts all cased letters in the given string to lowercase. - -## Examples - -``` -> lower("HELLO") -hello -> lower("АЛЛО!") -алло! -``` - -This function uses Unicode's definition of letters and of upper- and lowercase. - -## Related Functions - -* [`upper`](./upper.html) converts letters in a string to _uppercase_. -* [`title`](./title.html) converts the first letter of each word in a string to uppercase. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/map.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/map.html.md deleted file mode 100644 index 4735b878..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/map.html.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -layout: "functions" -page_title: "map - Functions - Configuration Language" -sidebar_current: "docs-funcs-collection-map" -description: |- - The map function constructs a map from some given elements. ---- - -# `map` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -~> **This function is deprecated.** From Terraform v0.12, the Terraform -language has built-in syntax for creating maps using the `{` and `}` -delimiters. Use the built-in syntax instead. The `map` function will be -removed in a future version of Terraform. - -`map` takes an even number of arguments and returns a map whose elements -are constructed from consecutive pairs of arguments. - -## Examples - -``` -> map("a", "b", "c", "d") -{ - "a" = "b" - "c" = "d" -} -``` - -Do not use the above form in Terraform v0.12 or above. Instead, use the -built-in map construction syntax, which achieves the same result: - -``` -> {"a" = "b", "c" = "d"} -{ - "a" = "b" - "c" = "d" -} -``` - -## Related Functions - -* [`tomap`](./tomap.html) performs a type conversion to a map type. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/max.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/max.html.md deleted file mode 100644 index 42e89d32..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/max.html.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -layout: "functions" -page_title: "max - Functions - Configuration Language" -sidebar_current: "docs-funcs-numeric-max" -description: |- - The max function takes one or more numbers and returns the greatest number. ---- - -# `max` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`max` takes one or more numbers and returns the greatest number from the set. - -## Examples - -``` -> max(12, 54, 3) -54 -``` - -If the numbers are in a list or set value, use `...` to expand the collection -to individual arguments: - -``` -> max([12, 54, 3]...) -54 -``` - -## Related Functions - -* [`min`](./min.html), which returns the _smallest_ number from a set. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/min.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/min.html.md deleted file mode 100644 index 5c1411a7..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/min.html.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -layout: "functions" -page_title: "min - Functions - Configuration Language" -sidebar_current: "docs-funcs-numeric-min" -description: |- - The min function takes one or more numbers and returns the smallest number. ---- - -# `min` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`min` takes one or more numbers and returns the smallest number from the set. - -## Examples - -``` -> min(12, 54, 3) -3 -``` - -If the numbers are in a list or set value, use `...` to expand the collection -to individual arguments: - -``` -> min([12, 54, 3]...) -3 -``` - -## Related Functions - -* [`max`](./max.html), which returns the _greatest_ number from a set. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/pow.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/pow.html.md deleted file mode 100644 index 154e2840..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/pow.html.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -layout: "functions" -page_title: "pow - Functions - Configuration Language" -sidebar_current: "docs-funcs-numeric-pow" -description: |- - The pow function raises a number to a power. ---- - -# `pow` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`pow` calculates an exponent, by raising its first argument to the power of the second argument. - -## Examples - -``` -> pow(3, 2) -9 -> pow(4, 0) -1 -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/reverse.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/reverse.html.md deleted file mode 100644 index d9febeca..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/reverse.html.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -layout: "functions" -page_title: "reverse - Functions - Configuration Language" -sidebar_current: "docs-funcs-collection-reverse" -description: |- - The reverse function reverses a sequence. ---- - -# `reverse` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`reverse` takes a sequence and produces a new sequence of the same length -with all of the same elements as the given sequence but in reverse order. - -## Examples - -``` -> reverse([1, 2, 3]) -[ - 3, - 2, - 1, -] -``` - -## Related Functions - -* [`strrev`](./strrev.html) reverses a string. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/signum.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/signum.html.md deleted file mode 100644 index 93d62506..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/signum.html.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -layout: "functions" -page_title: "signum - Functions - Configuration Language" -sidebar_current: "docs-funcs-numeric-signum" -description: |- - The signum function determines the sign of a number. ---- - -# `signum` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`signum` determines the sign of a number, returning a number between -1 and -1 to represent the sign. - -## Examples - -``` -> signum(-13) --1 -> signum(0) -0 -> signum(344) -1 -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/sort.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/sort.html.md deleted file mode 100644 index 598c035e..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/sort.html.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -layout: "functions" -page_title: "sort - Functions - Configuration Language" -sidebar_current: "docs-funcs-collection-sort" -description: |- - The sort function takes a list of strings and returns a new list with those - strings sorted lexicographically. ---- - -# `sort` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`sort` takes a list of strings and returns a new list with those strings -sorted lexicographically. - -The sort is in terms of Unicode codepoints, with higher codepoints appearing -after lower ones in the result. - -## Examples - -``` -> sort(["e", "d", "a", "x"]) -[ - "a", - "d", - "e", - "x", -] -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/substr.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/substr.html.md deleted file mode 100644 index d9579d12..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/substr.html.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -layout: "functions" -page_title: "substr - Functions - Configuration Language" -sidebar_current: "docs-funcs-string-substr" -description: |- - The substr function extracts a substring from a given string by offset and - length. ---- - -# `substr` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`substr` extracts a substring from a given string by offset and length. - -```hcl -substr(string, offset, length) -``` - -## Examples - -``` -> substr("hello world", 1, 4) -ello -``` - -The offset and length are both counted in _unicode characters_ rather than -bytes: - -``` -> substr("🤔🤷", 0, 1) -🤔 -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/sum.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/sum.html.md deleted file mode 100644 index 20958974..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/sum.html.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -layout: "functions" -page_title: "sum - Functions - Configuration Language" -sidebar_current: "docs-funcs-collection-sum" -description: |- - The sum function takes a list or set of numbers and returns the sum of those - numbers. ---- - -# `sum` Function - --> **Note:** This page is about Terraform 0.13 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`sum` takes a list or set of numbers and returns the sum of those numbers. - - -## Examples - -``` -> sum([10, 13, 6, 4.5]) -33.5 -``` \ No newline at end of file diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/title.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/title.html.md deleted file mode 100644 index 7027d48f..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/title.html.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -layout: "functions" -page_title: "title - Functions - Configuration Language" -sidebar_current: "docs-funcs-string-title" -description: |- - The title function converts the first letter of each word in a given string - to uppercase. ---- - -# `title` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`title` converts the first letter of each word in the given string to uppercase. - -## Examples - -``` -> title("hello world") -Hello World -``` - -This function uses Unicode's definition of letters and of upper- and lowercase. - -## Related Functions - -* [`upper`](./upper.html) converts _all_ letters in a string to uppercase. -* [`lower`](./lower.html) converts all letters in a string to lowercase. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/transpose.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/transpose.html.md deleted file mode 100644 index a95b49b2..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/transpose.html.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -layout: "functions" -page_title: "transpose - Functions - Configuration Language" -sidebar_current: "docs-funcs-collection-transpose" -description: |- - The transpose function takes a map of lists of strings and swaps the keys - and values. ---- - -# `transpose` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`transpose` takes a map of lists of strings and swaps the keys and values -to produce a new map of lists of strings. - -## Examples - -``` -> transpose({"a" = ["1", "2"], "b" = ["2", "3"]}) -{ - "1" = [ - "a", - ], - "2" = [ - "a", - "b", - ], - "3" = [ - "b", - ], -} -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/type.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/type.html.md new file mode 100644 index 00000000..ed4b0c35 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/type.html.md @@ -0,0 +1,82 @@ +--- +layout: "language" +page_title: "type - Functions - Configuration Language" +sidebar_current: "docs-funcs-conversion-type" +description: |- + The type function returns the type of a given value. +--- + +# `type` Function + +-> **Note:** This function is available only in Terraform 1.0 and later. + +`type` retuns the type of a given value. + +Sometimes a Terraform configuration can result in confusing errors regarding +inconsistent types. This function displays terraform's evaluation of a given +value's type, which is useful in understanding this error message. + +This is a special function which is only available in the `terraform console` command. + +## Examples + +Here we have a conditional `output` which prints either the value of `var.list` or a local named `default_list`: + +```hcl +variable "list" { + default = [] +} + +locals { + default_list = [ + { + foo = "bar" + map = { bleep = "bloop" } + }, + { + beep = "boop" + }, + ] +} + +output "list" { + value = var.list != [] ? var.list : local.default_list +} +``` + +Applying this configuration results in the following error: + +``` +Error: Inconsistent conditional result types + + on main.tf line 18, in output "list": + 18: value = var.list != [] ? var.list : local.default_list + |---------------- + | local.default_list is tuple with 2 elements + | var.list is empty tuple + +The true and false result expressions must have consistent types. The given +expressions are tuple and tuple, respectively. +``` + +While this error message does include some type information, it can be helpful +to inspect the exact type that Terraform has determined for each given input. +Examining both `var.list` and `local.default_list` using the `type` function +provides more context for the error message: + +``` +> type(var.list) +tuple +> type(local.default_list) +tuple([ + object({ + foo: string, + map: object({ + bleep: string, + }), + }), + object({ + beep: string, + }), +]) +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/upper.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/upper.html.md deleted file mode 100644 index 286bd0c8..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/upper.html.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -layout: "functions" -page_title: "upper - Functions - Configuration Language" -sidebar_current: "docs-funcs-string-upper" -description: |- - The upper function converts all cased letters in the given string to uppercase. ---- - -# `upper` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`upper` converts all cased letters in the given string to uppercase. - -## Examples - -``` -> upper("hello") -HELLO -> upper("алло!") -АЛЛО! -``` - -This function uses Unicode's definition of letters and of upper- and lowercase. - -## Related Functions - -* [`lower`](./lower.html) converts letters in a string to _lowercase_. -* [`title`](./title.html) converts the first letter of each word in a string to uppercase. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/uuid.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/uuid.html.md deleted file mode 100644 index 439ec823..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/uuid.html.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -layout: "functions" -page_title: "uuid - Functions - Configuration Language" -sidebar_current: "docs-funcs-crypto-uuid" -description: |- - The uuid function generates a unique id. ---- - -# `uuid` Function - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - -`uuid` generates a unique identifier string. - -The id is a generated and formatted as required by -[RFC 4122 section 4.4](https://tools.ietf.org/html/rfc4122#section-4.4), -producing a Version 4 UUID. The result is a UUID generated only from -pseudo-random numbers. - -This function produces a new value each time it is called, and so using it -directly in resource arguments will result in spurious diffs. We do not -recommend using the `uuid` function in resource configurations, but it can -be used with care in conjunction with -[the `ignore_changes` lifecycle meta-argument](../resources.html#ignore_changes). - -In most cases we recommend using [the `random` provider](/docs/providers/random/index.html) -instead, since it allows the one-time generation of random values that are -then retained in the Terraform [state](/docs/state/index.html) for use by -future operations. In particular, -[`random_id`](/docs/providers/random/r/id.html) can generate results with -equivalent randomness to the `uuid` function. - -## Examples - -``` -> uuid() -b5ee72a3-54dd-c4b8-551c-4bdc0204cedb -``` - -## Related Functions - -* [`uuidv5`](./uuidv5.html), which generates name-based UUIDs. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/index.html.md deleted file mode 100644 index 7cae418d..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/index.html.md +++ /dev/null @@ -1,182 +0,0 @@ ---- -layout: "docs" -page_title: "Configuration Language" -sidebar_current: "docs-config-index" -description: |- - Terraform uses text files to describe infrastructure and to set variables. - These text files are called Terraform _configurations_ and are - written in the Terraform language. ---- - -# Configuration Language - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language](../configuration-0-11/index.html). - -> **Hands-on:** Try the [Terraform: Get Started](https://learn.hashicorp.com/collections/terraform/aws-get-started?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. - -Terraform uses its own configuration language, designed to allow concise -descriptions of infrastructure. The Terraform language is declarative, -describing an intended goal rather than the steps to reach that goal. - -## Resources and Modules - -The main purpose of the Terraform language is declaring [resources](./resources.html). -All other language features exist only to make the definition of resources -more flexible and convenient. - -A group of resources can be gathered into a [module](./modules.html), -which creates a larger unit of configuration. A resource describes a single -infrastructure object, while a module might describe a set of objects and the -necessary relationships between them in order to create a higher-level system. - -A _Terraform configuration_ consists of a _root module_, where evaluation -begins, along with a tree of child modules created when one module calls -another. - -## Arguments, Blocks, and Expressions - -The syntax of the Terraform language consists of only a few basic elements: - -```hcl -resource "aws_vpc" "main" { - cidr_block = var.base_cidr_block -} - - "" "" { - # Block body - = # Argument -} -``` - -- _Blocks_ are containers for other content and usually represent the - configuration of some kind of object, like a resource. Blocks have a - _block type,_ can have zero or more _labels,_ and have a _body_ that contains - any number of arguments and nested blocks. Most of Terraform's features are - controlled by top-level blocks in a configuration file. -- _Arguments_ assign a value to a name. They appear within blocks. -- _Expressions_ represent a value, either literally or by referencing and - combining other values. They appear as values for arguments, or within other - expressions. - -For full details about Terraform's syntax, see: - -- [Configuration Syntax](./syntax.html) -- [Expressions](./expressions.html) - -## Code Organization - -The Terraform language uses configuration files that are named with the `.tf` -file extension. There is also [a JSON-based variant of the language](./syntax-json.html) -that is named with the `.tf.json` file extension. - -Configuration files must always use UTF-8 encoding, and by convention are -usually maintained with Unix-style line endings (LF) rather than Windows-style -line endings (CRLF), though both are accepted. - -A _module_ is a collection of `.tf` or `.tf.json` files kept together in a -directory. The root module is built from the configuration files in the -current working directory when Terraform is run, and this module may reference -child modules in other directories, which can in turn reference other modules, -etc. - -The simplest Terraform configuration is a single root module containing only -a single `.tf` file. A configuration can grow gradually as more resources -are added, either by creating new configuration files within the root module -or by organizing sets of resources into child modules. - -## Configuration Ordering - -Because Terraform's configuration language is declarative, the ordering of -blocks is generally not significant. (The order of `provisioner` blocks within a -resource is the only major feature where block order matters.) - -Terraform automatically processes resources in the correct order based on -relationships defined between them in configuration, and so you can organize -resources into source files in whatever way makes sense for your infrastructure. - -## Terraform CLI vs. Providers - -The Terraform command line interface (CLI) is a general engine for evaluating -and applying Terraform configurations. It defines the Terraform language syntax -and overall structure, and coordinates sequences of changes that must be made to -make remote infrastructure match the given configuration. - -This general engine has no knowledge about specific types of infrastructure -objects. Instead, Terraform uses plugins called -[providers](./providers.html) that each define and manage a -set of resource types. Most providers are associated with a particular cloud or -on-premises infrastructure service, allowing Terraform to manage infrastructure -objects within that service. - -Terraform doesn't have a concept of platform-independent resource types -— resources are always tied to a provider, since the features of similar -resources can vary greatly from provider to provider. But Terraform CLI's shared -configuration engine ensures that the same language constructs and syntax are -available across all services and allows resource types from different services -to be combined as needed. - -## Example - -The following simple example describes a simple network topology for Amazon Web -Services, just to give a sense of the overall structure and syntax of the -Terraform language. Similar configurations can be created for other virtual -network services, using resource types defined by other providers, and a -practical network configuration will often contain additional elements not -shown here. - -```hcl -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 1.0.4" - } - } -} - -variable "aws_region" {} - -variable "base_cidr_block" { - description = "A /16 CIDR range definition, such as 10.1.0.0/16, that the VPC will use" - default = "10.1.0.0/16" -} - -variable "availability_zones" { - description = "A list of availability zones in which to create subnets" - type = list(string) -} - -provider "aws" { - region = var.aws_region -} - -resource "aws_vpc" "main" { - # Referencing the base_cidr_block variable allows the network address - # to be changed without modifying the configuration. - cidr_block = var.base_cidr_block -} - -resource "aws_subnet" "az" { - # Create one subnet for each given availability zone. - count = length(var.availability_zones) - - # For each subnet, use one of the specified availability zones. - availability_zone = var.availability_zones[count.index] - - # By referencing the aws_vpc.main object, Terraform knows that the subnet - # must be created only after the VPC is created. - vpc_id = aws_vpc.main.id - - # Built-in functions and operators can be used for simple transformations of - # values, such as computing a subnet address. Here we create a /20 prefix for - # each subnet, using consecutive addresses for each availability zone, - # such as 10.1.16.0/20 . - cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 4, count.index+1) -} -``` - -For more information on the configuration elements shown here, use the -site navigation to explore the Terraform language documentation sub-sections. -To start, see [_Resource Configuration_](./resources.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/modules.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/modules.html.md index 397942ac..1dbcf06a 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/modules.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration/modules.html.md @@ -1,632 +1,54 @@ --- -layout: "docs" -page_title: "Modules - Configuration Language" -sidebar_current: "docs-config-modules" -description: |- - Modules allow multiple resources to be grouped together and encapsulated. +layout: "language" +page_title: "Modules Landing Page - Configuration Language" --- -# Modules +# Modules Landing Page --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Modules](../configuration-0-11/modules.html). +To improve navigation, we've split the old Modules page into several smaller +pages. -> **Hands-on:** Try the [Reuse Configuration with Modules](https://learn.hashicorp.com/collections/terraform/modules?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. + + + + + + -A _module_ is a container for multiple resources that are used together. +## Syntax and Elements of Module Blocks -Every Terraform configuration has at least one module, known as its -_root module_, which consists of the resources defined in the `.tf` files in -the main working directory. +This information has moved to +[Module Blocks](/docs/language/modules/syntax.html). -A module can call other modules, which lets you include the child module's -resources into the configuration in a concise way. Modules -can also be called multiple times, either within the same configuration or -in separate configurations, allowing resource configurations to be packaged -and re-used. +
-This page describes how to call one module from another. Other pages in this -section of the documentation describe the different elements that make up -modules, and there is further information about how modules can be used, -created, and published in [the dedicated _Modules_ -section](/docs/modules/index.html). -## Calling a Child Module -To _call_ a module means to include the contents of that module into the -configuration with specific values for its -[input variables](./variables.html). Modules are called -from within other modules using `module` blocks: + -```hcl -module "servers" { - source = "./app-cluster" +## Multiple Instances with `count` and `for_each` - servers = 5 -} -``` +This information has moved to +[`count`](/docs/language/meta-arguments/count.html) and +[`for_each`](/docs/language/meta-arguments/for_each.html). -A module that includes a `module` block like this is the _calling module_ of the -child module. +
-The label immediately after the `module` keyword is a local name, which the -calling module can use to refer to this instance of the module. -Within the block body (between `{` and `}`) are the arguments for the module. -Most of the arguments correspond to [input variables](./variables.html) -defined by the module, including the `servers` argument in the above example. -Terraform also defines a few meta-arguments that are reserved by Terraform -and used for its own purposes; we will discuss those throughout the rest of -this section. -All modules require a `source` argument, which is a meta-argument defined by -Terraform. Its value is either the path to a local directory containing the -module's configuration files, or a remote module source that Terraform should -download and use. This value must be a literal string with no template -sequences; arbitrary expressions are not allowed. For more information on -possible values for this argument, see [Module Sources](/docs/modules/sources.html). + + + + + + -The same source address can be specified in multiple `module` blocks to create -multiple copies of the resources defined within, possibly with different -variable values. +## Handling Provider Configurations in Re-usable Modules -After adding, removing, or modifying `module` blocks, you must re-run -`terraform init` to allow Terraform the opportunity to adjust the installed -modules. By default this command will not upgrade an already-installed module; -use the `-upgrade` option to instead upgrade to the newest available version. +This information has moved to +[The `providers` Meta-Argument](/docs/language/meta-arguments/module-providers.html) +(for users of re-usable modules) and +[Providers Within Modules](/docs/language/modules/develop/providers.html) +(for module developers). -## Accessing Module Output Values - -The resources defined in a module are encapsulated, so the calling module -cannot access their attributes directly. However, the child module can -declare [output values](./outputs.html) to selectively -export certain values to be accessed by the calling module. - -For example, if the `./app-cluster` module referenced in the example above -exported an output value named `instance_ids` then the calling module -can reference that result using the expression `module.servers.instance_ids`: - -```hcl -resource "aws_elb" "example" { - # ... - - instances = module.servers.instance_ids -} -``` - -For more information about referring to named values, see -[Expressions](./expressions.html). - -## Transferring Resource State Into Modules - -When refactoring an existing configuration to split code into child modules, -moving resource blocks between modules causes Terraform to see the new location -as an entirely different resource from the old. Always check the execution plan -after moving code across modules to ensure that no resources are deleted by -surprise. - -If you want to make sure an existing resource is preserved, use -[the `terraform state mv` command](/docs/commands/state/mv.html) to inform -Terraform that it has moved to a different module. - -When passing resource addresses to `terraform state mv`, resources within child -modules must be prefixed with `module..`. If a module was called -with `count` or `for_each` ([see below][inpage-multiple]), its resource -addresses must be prefixed with `module.[].` instead, where -`` matches the `count.index` or `each.key` value of a particular module -instance. - -Full resource addresses for module contents are used within the UI and on the -command line, but cannot be used within a Terraform configuration. Only -[outputs](./outputs.html) from a module can be referenced from -elsewhere in your configuration. - -## Other Meta-arguments - -Along with the `source` meta-argument described above, module blocks have -some optional meta-arguments that have special meaning across all modules, -described in more detail below: - -- `version` - A [version constraint string](./version-constraints.html) - that specifies acceptable versions of the module. Described in detail under - [Module Versions][inpage-versions] below. - -- `count` and `for_each` - Both of these arguments create multiple instances of a - module from a single `module` block. Described in detail under - [Multiple Instances of a Module][inpage-multiple] below. - -- `providers` - A map whose keys are provider configuration names - that are expected by child module and whose values are the corresponding - provider configurations in the calling module. This allows - [provider configurations to be passed explicitly to child modules](#passing-providers-explicitly). - If not specified, the child module inherits all of the default (un-aliased) - provider configurations from the calling module. Described in detail under - [Providers Within Modules][inpage-providers] - -- `depends_on` - Creates explicit dependencies between the entire - module and the listed targets. This will delay the final evaluation of the - module, and any sub-modules, until after the dependencies have been applied. - Modules have the same dependency resolution behavior - [as defined for managed resources](./resources.html#resource-dependencies). - -In addition to the above, the `lifecycle` argument is not currently used by -Terraform but is reserved for planned future features. - -Since modules are a complex feature in their own right, further detail -about how modules can be used, created, and published is included in -[the dedicated section on modules](/docs/modules/index.html). - -## Module Versions - -[inpage-versions]: #module-versions - -When using modules installed from a module registry, we recommend explicitly -constraining the acceptable version numbers to avoid unexpected or unwanted -changes. - -Use the `version` attribute in the `module` block to specify versions: - -```shell -module "consul" { - source = "hashicorp/consul/aws" - version = "0.0.5" - - servers = 3 -} -``` - -The `version` attribute accepts a [version constraint string](./version-constraints.html). -Terraform will use the newest installed version of the module that meets the -constraint; if no acceptable versions are installed, it will download the newest -version that meets the constraint. - -Version constraints are supported only for modules installed from a module -registry, such as the public [Terraform Registry](https://registry.terraform.io/) -or [Terraform Cloud's private module registry](/docs/cloud/registry/index.html). -Other module sources can provide their own versioning mechanisms within the -source string itself, or might not support versions at all. In particular, -modules sourced from local file paths do not support `version`; since -they're loaded from the same source repository, they always share the same -version as their caller. - -## Multiple Instances of a Module - -[inpage-multiple]: #multiple-instances-of-a-module - --> **Note:** Module support for the `for_each` and `count` meta-arguments was -added in Terraform 0.13. Previous versions can only use these arguments with -individual resources. - -Use the `for_each` or the `count` argument to create multiple instances of a -module from a single `module` block. These arguments have the same syntax and -type constraints as -[`for_each`](./resources.html#for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings) -and -[`count`](./resources.html#count-multiple-resource-instances-by-count) -when used with resources. - -```hcl -# my_buckets.tf -module "bucket" { - for_each = toset(["assets", "media"]) - source = "./publish_bucket" - name = "${each.key}_bucket" -} -``` - -```hcl -# publish_bucket/bucket-and-cloudfront.tf -variable "name" {} # this is the input parameter of the module - -resource "aws_s3_bucket" "example" { - # Because var.name includes each.key in the calling - # module block, its value will be different for - # each instance of this module. - bucket = var.name - - # ... -} - -resource "aws_iam_user" "deploy_user" { - # ... -} -``` - -This example defines a local child module in the `./publish_bucket` -subdirectory. That module has configuration to create an S3 bucket. The module -wraps the bucket and all the other implementation details required to configure -a bucket. - -We declare multiple module instances by using the `for_each` attribute, -which accepts a map (with string keys) or a set of strings as its value. Additionally, -we use the special `each.key` value in our module block, because the -[`each`](/docs/configuration/resources.html#the-each-object) object is available when -we have declared `for_each` on the module block. When using the `count` argument, the -[`count`](/docs/configuration/resources.html#the-count-object) object is available. - -Resources from child modules are prefixed with `module.module_name[module index]` -when displayed in plan output and elsewhere in the UI. For a module with without -`count` or `for_each`, the address will not contain the module index as the module's -name suffices to reference the module. - -In our example, the `./publish_bucket` module contains `aws_s3_bucket.example`, and so the two -instances of this module produce S3 bucket resources with [resource addresses](/docs/internals/resource-addressing.html) of `module.bucket["assets"].aws_s3_bucket.example` -and `module.bucket["media"].aws_s3_bucket.example` respectively. - -## Providers Within Modules - -[inpage-providers]: #providers-within-modules - -In a configuration with multiple modules, there are some special considerations -for how resources are associated with provider configurations. - -Each resource in the configuration must be associated with one provider -configuration. Provider configurations, unlike most other concepts in -Terraform, are global to an entire Terraform configuration and can be shared -across module boundaries. Provider configurations can be defined only in a -root Terraform module. - -Providers can be passed down to descendent modules in two ways: either -_implicitly_ through inheritance, or _explicitly_ via the `providers` argument -within a `module` block. These two options are discussed in more detail in the -following sections. - -A module intended to be called by one or more other modules must not contain -any `provider` blocks, with the exception of the special -"proxy provider blocks" discussed under -_[Passing Providers Explicitly](#passing-providers-explicitly)_ -below. - -For backward compatibility with configurations targeting Terraform v0.10 and -earlier Terraform does not produce an error for a `provider` block in a shared -module if the `module` block only uses features available in Terraform v0.10, -but that is a legacy usage pattern that is no longer recommended. A legacy -module containing its own provider configurations is not compatible with the -`for_each`, `count`, and `depends_on` arguments that were introduced in -Terraform v0.13. For more information, see -[Legacy Shared Modules with Provider Configurations](#legacy-shared-modules-with-provider-configurations). - -Provider configurations are used for all operations on associated resources, -including destroying remote objects and refreshing state. Terraform retains, as -part of its state, a reference to the provider configuration that was most -recently used to apply changes to each resource. When a `resource` block is -removed from the configuration, this record in the state will be used to locate -the appropriate configuration because the resource's `provider` argument -(if any) will no longer be present in the configuration. - -As a consequence, you must ensure that all resources that belong to a -particular provider configuration are destroyed before you can remove that -provider configuration's block from your configuration. If Terraform finds -a resource instance tracked in the state whose provider configuration block is -no longer available then it will return an error during planning, prompting you -to reintroduce the provider configuration. - -### Provider Version Constraints in Modules - -Although provider _configurations_ are shared between modules, each module must -declare its own [provider requirements](provider-requirements.html), so that -Terraform can ensure that there is a single version of the provider that is -compatible with all modules in the configuration and to specify the -[source address](provider-requirements.html#source-addresses) that serves as -the global (module-agnostic) identifier for a provider. - -To declare that a module requires particular versions of a specific provider, -use a `required_providers` block inside a `terraform` block: - -```hcl -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 2.7.0" - } - } -} -``` - -A provider requirement says, for example, "This module requires version v2.7.0 -of the provider `hashicorp/aws` and will refer to it as `aws`." It doesn't, -however, specify any of the configuration settings that determine what remote -endpoints the provider will access, such as an AWS region; configuration -settings come from provider _configurations_, and a particular overall Terraform -configuration can potentially have -[several different configurations for the same provider](providers.html#alias-multiple-provider-instances). - -If you are writing a shared Terraform module, constrain only the minimum -required provider version using a `>=` constraint. This should specify the -minimum version containing the features your module relies on, and thus allow a -user of your module to potentially select a newer provider version if other -features are needed by other parts of their overall configuration. - -### Implicit Provider Inheritance - -For convenience in simple configurations, a child module automatically inherits -default (un-aliased) provider configurations from its parent. This means that -explicit `provider` blocks appear only in the root module, and downstream -modules can simply declare resources for that provider and have them -automatically associated with the root provider configurations. - -For example, the root module might contain only a `provider` block and a -`module` block to instantiate a child module: - -```hcl -provider "aws" { - region = "us-west-1" -} - -module "child" { - source = "./child" -} -``` - -The child module can then use any resource from this provider with no further -provider configuration required: - -```hcl -resource "aws_s3_bucket" "example" { - bucket = "provider-inherit-example" -} -``` - -We recommend using this approach when a single configuration for each provider -is sufficient for an entire configuration. - -~> **Note:** Only provider configurations are inherited by child modules, not provider source or version requirements. Each module must [declare its own provider requirements](provider-requirements.html). This is especially important for non-HashiCorp providers. - -In more complex situations there may be -[multiple provider configurations](/docs/configuration/providers.html#alias-multiple-provider-configurations), -or a child module may need to use different provider settings than -its parent. For such situations, you must pass providers explicitly. - -### Passing Providers Explicitly - -When child modules each need a different configuration of a particular -provider, or where the child module requires a different provider configuration -than its parent, you can use the `providers` argument within a `module` block -to explicitly define which provider configurations are available to the -child module. For example: - -```hcl -# The default "aws" configuration is used for AWS resources in the root -# module where no explicit provider instance is selected. -provider "aws" { - region = "us-west-1" -} - -# An alternate configuration is also defined for a different -# region, using the alias "usw2". -provider "aws" { - alias = "usw2" - region = "us-west-2" -} - -# An example child module is instantiated with the alternate configuration, -# so any AWS resources it defines will use the us-west-2 region. -module "example" { - source = "./example" - providers = { - aws = aws.usw2 - } -} -``` - -The `providers` argument within a `module` block is similar to -[the `provider` argument](resources.html#provider-selecting-a-non-default-provider-configuration) -within a resource, but is a map rather than a single string because a module may -contain resources from many different providers. - -The keys of the `providers` map are provider configuration names as expected by -the child module, and the values are the names of corresponding configurations -in the _current_ module. - -Once the `providers` argument is used in a `module` block, it overrides all of -the default inheritance behavior, so it is necessary to enumerate mappings -for _all_ of the required providers. This is to avoid confusion and surprises -that may result when mixing both implicit and explicit provider passing. - -Additional provider configurations (those with the `alias` argument set) are -_never_ inherited automatically by child modules, and so must always be passed -explicitly using the `providers` map. For example, a module -that configures connectivity between networks in two AWS regions is likely -to need both a source and a destination region. In that case, the root module -may look something like this: - -```hcl -provider "aws" { - alias = "usw1" - region = "us-west-1" -} - -provider "aws" { - alias = "usw2" - region = "us-west-2" -} - -module "tunnel" { - source = "./tunnel" - providers = { - aws.src = aws.usw1 - aws.dst = aws.usw2 - } -} -``` - -The subdirectory `./tunnel` must then contain _proxy configuration blocks_ like -the following, to declare that it requires its calling module to pass -configurations with these names in its `providers` argument: - -```hcl -provider "aws" { - alias = "src" -} - -provider "aws" { - alias = "dst" -} -``` - -Each resource should then have its own `provider` attribute set to either -`aws.src` or `aws.dst` to choose which of the two provider configurations to -use. - -### Proxy Configuration Blocks - -A proxy configuration block is one that contains only the `alias` argument. It -serves as a placeholder for provider configurations passed between modules, and -declares that a module expects to be explicitly passed an additional (aliased) -provider configuration. - --> **Note:** Although a completely empty proxy configuration block is also -valid, it is not necessary: proxy configuration blocks are needed only to -establish which _aliased_ provider configurations a child module expects. -Don't use a proxy configuration block if a module only needs a single default -provider configuration, and don't use proxy configuration blocks only to imply -[provider requirements](./provider-requirements.html). - -## Legacy Shared Modules with Provider Configurations - -In Terraform v0.10 and earlier there was no explicit way to use different -configurations of a provider in different modules in the same configuration, -and so module authors commonly worked around this by writing `provider` blocks -directly inside their modules, making the module have its own separate -provider configurations separate from those declared in the root module. - -However, that pattern had a significant drawback: because a provider -configuration is required to destroy the remote object associated with a -resource instance as well as to create or update it, a provider configuration -must always stay present in the overall Terraform configuration for longer -than all of the resources it manages. If a particular module includes -both resources and the provider configurations for those resources then -removing the module from its caller would violate that constraint: both the -resources and their associated providers would, in effect, be removed -simultaneously. - -Terraform v0.11 introduced the mechanisms described in earlier sections to -allow passing provider configurations between modules in a structured way, and -thus we explicitly recommended against writing a child module with its own -provider configuration blocks. However, that legacy pattern continued to work -for compatibility purposes -- though with the same drawback -- until Terraform -v0.13. - -Terraform v0.13 introduced the possibility for a module itself to use the -`for_each`, `count`, and `depends_on` arguments, but the implementation of -those unfortunately conflicted with the support for the legacy pattern. - -To retain the backward compatibility as much as possible, Terraform v0.13 -continues to support the legacy pattern for module blocks that do not use these -new features, but a module with its own provider configurations is not -compatible with `for_each`, `count`, or `depends_on`. Terraform will produce an -error if you attempt to combine these features. For example: - -``` -Error: Module does not support count - - on main.tf line 15, in module "child": - 15: count = 2 - -Module "child" cannot be used with count because it contains a nested provider -configuration for "aws", at child/main.tf:2,10-15. - -This module can be made compatible with count by changing it to receive all of -its provider configurations from the calling module, by using the "providers" -argument in the calling module block. -``` - -To make a module compatible with the new features, you must either remove all -of the `provider` blocks from its definition or, if you need multiple -configurations for the same provider, replace them with -_proxy configuration blocks_ as described in -[Passing Providers Explicitly](#passing-providers-explicitly). - -If the new version of the module uses proxy configuration blocks, or if the -calling module needs the child module to use different provider configurations -than its own default provider configurations, the calling module must then -include an explicit `providers` argument to describe which provider -configurations the child module will use: - -```hcl -provider "aws" { - region = "us-west-1" -} - -provider "aws" { - region = "us-east-1" - alias = "east" -} - -module "child" { - count = 2 - providers = { - # By default, the child module would use the - # default (unaliased) AWS provider configuration - # using us-west-1, but this will override it - # to use the additional "east" configuration - # for its resources instead. - aws = aws.east - } -} -``` - -Since the association between resources and provider configurations is -static, module calls using `for_each` or `count` cannot pass different -provider configurations to different instances. If you need different -instances of your module to use different provider configurations then you -must use a separate `module` block for each distinct set of provider -configurations: - -```hcl -provider "aws" { - alias = "usw1" - region = "us-west-1" -} - -provider "aws" { - alias = "usw2" - region = "us-west-2" -} - -provider "google" { - alias = "usw1" - credentials = "${file("account.json")}" - project = "my-project-id" - region = "us-west1" - zone = "us-west1-a" -} - -provider "google" { - alias = "usw2" - credentials = "${file("account.json")}" - project = "my-project-id" - region = "us-west2" - zone = "us-west2-a" -} - -module "bucket_w1" { - source = "./publish_bucket" - providers = { - aws.src = aws.usw1 - google.src = google.usw2 - } -} - -module "bucket_w2" { - source = "./publish_bucket" - providers = { - aws.src = aws.usw2 - google.src = google.usw2 - } -} -``` - -## Tainting resources within a module - -The [taint command](/docs/commands/taint.html) can be used to _taint_ specific -resources within a module: - -```shell -$ terraform taint module.salt_master.aws_instance.salt_master -``` - -It is not possible to taint an entire module. Instead, each resource within -the module must be tainted separately. +
diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/outputs.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/outputs.html.md deleted file mode 100644 index 01c89acf..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/outputs.html.md +++ /dev/null @@ -1,187 +0,0 @@ ---- -layout: "docs" -page_title: "Output Values - Configuration Language" -sidebar_current: "docs-config-outputs" -description: |- - Output values are the return values of a Terraform module. ---- - -# Output Values - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Output Values](../configuration-0-11/outputs.html). - -Output values are like the return values of a Terraform module, and have several -uses: - -- A child module can use outputs to expose a subset of its resource attributes - to a parent module. -- A root module can use outputs to print certain values in the CLI output after - running `terraform apply`. -- When using [remote state](/docs/state/remote.html), root module outputs can be - accessed by other configurations via a - [`terraform_remote_state` data source](/docs/providers/terraform/d/remote_state.html). - -Resource instances managed by Terraform each export attributes whose values -can be used elsewhere in configuration. Output values are a way to expose some -of that information to the user of your module. - --> **Note:** For brevity, output values are often referred to as just "outputs" -when the meaning is clear from context. - -## Declaring an Output Value - -Each output value exported by a module must be declared using an `output` -block: - -```hcl -output "instance_ip_addr" { - value = aws_instance.server.private_ip -} -``` - -The label immediately after the `output` keyword is the name, which must be a -valid [identifier](./syntax.html#identifiers). In a root module, this name is -displayed to the user; in a child module, it can be used to access the output's -value. - -The `value` argument takes an [expression](./expressions.html) -whose result is to be returned to the user. In this example, the expression -refers to the `private_ip` attribute exposed by an `aws_instance` resource -defined elsewhere in this module (not shown). Any valid expression is allowed -as an output value. - --> **Note:** Outputs are only rendered when Terraform applies your plan. Running -`terraform plan` will not render outputs. - -## Accessing Child Module Outputs - -In a parent module, outputs of child modules are available in expressions as -`module..`. For example, if a child module named -`web_server` declared an output named `instance_ip_addr`, you could access that -value as `module.web_server.instance_ip_addr`. - -## Optional Arguments - -`output` blocks can optionally include `description`, `sensitive`, and `depends_on` arguments, which are described in the following sections. - -### `description` — Output Value Documentation - -Because the output values of a module are part of its user interface, you can -briefly describe the purpose of each value using the optional `description` -argument: - -```hcl -output "instance_ip_addr" { - value = aws_instance.server.private_ip - description = "The private IP address of the main server instance." -} -``` - -The description should concisely explain the -purpose of the output and what kind of value is expected. This description -string might be included in documentation about the module, and so it should be -written from the perspective of the user of the module rather than its -maintainer. For commentary for module maintainers, use comments. - -### `sensitive` — Suppressing Values in CLI Output - -An output can be marked as containing sensitive material using the optional -`sensitive` argument: - -```hcl -output "db_password" { - value = aws_db_instance.db.password - description = "The password for logging in to the database." - sensitive = true -} -``` - -Setting an output value as sensitive prevents Terraform from showing its value -in `plan` and `apply`. In the following scenario, our root module has an output declared as sensitive -and a module call with a sensitive output, which we then use in a resource attribute. - -```hcl -# main.tf - -module "foo" { - source = "./mod" -} - -resource "test_instance" "x" { - some_attribute = module.mod.a # resource attribute references a sensitive output -} - -output "out" { - value = "xyz" - sensitive = true -} - -# mod/main.tf, our module containing a sensitive output - -output "a" { - value = "secret" - sensitive = true" -} -``` - -When we run a `plan` or `apply`, the sensitive value is redacted from output: - -``` -# CLI output - -Terraform will perform the following actions: - - # test_instance.x will be created - + resource "test_instance" "x" { - + some_attribute = (sensitive) - } - -Plan: 1 to add, 0 to change, 0 to destroy. - -Changes to Outputs: - + out = (sensitive value) -``` - --> **Note:** In Terraform versions prior to Terraform 0.14, setting an output value in the root module as sensitive would prevent Terraform from showing its value in the list of outputs at the end of `terraform apply`. However, the value could still display in the CLI output for other reasons, like if the value is referenced in an expression for a resource argument. - -Sensitive output values are still recorded in the -[state](/docs/state/index.html), and so will be visible to anyone who is able -to access the state data. For more information, see -[_Sensitive Data in State_](/docs/state/sensitive-data.html). - -### `depends_on` — Explicit Output Dependencies - -Since output values are just a means for passing data out of a module, it is -usually not necessary to worry about their relationships with other nodes in -the dependency graph. - -However, when a parent module accesses an output value exported by one of its -child modules, the dependencies of that output value allow Terraform to -correctly determine the dependencies between resources defined in different -modules. - -Just as with -[resource dependencies](./resources.html#resource-dependencies), -Terraform analyzes the `value` expression for an output value and automatically -determines a set of dependencies, but in less-common cases there are -dependencies that cannot be recognized implicitly. In these rare cases, the -`depends_on` argument can be used to create additional explicit dependencies: - -```hcl -output "instance_ip_addr" { - value = aws_instance.server.private_ip - description = "The private IP address of the main server instance." - - depends_on = [ - # Security group rule must be created before this IP address could - # actually be used, otherwise the services will be unreachable. - aws_security_group_rule.local_access, - ] -} -``` - -The `depends_on` argument should be used only as a last resort. When using it, -always include a comment explaining why it is being used, to help future -maintainers understand the purpose of the additional dependency. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/provider-requirements.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/provider-requirements.html.md deleted file mode 100644 index b27d85c2..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/provider-requirements.html.md +++ /dev/null @@ -1,463 +0,0 @@ ---- -layout: "docs" -page_title: "Provider Requirements - Configuration Language" ---- - -# Provider Requirements - --> **Note:** This page is about a feature of Terraform 0.13 and later; it also -describes how to use the more limited version of that feature that was available -in Terraform 0.12. If you are using Terraform 0.11 or earlier, see -[0.11 Configuration Language: Provider Versions](../configuration-0-11/providers.html#provider-versions) instead. - -Terraform relies on plugins called "providers" to interact with remote systems. - -Terraform configurations must declare which providers they require, so that -Terraform can install and use them. Additionally, some providers require -configuration (like endpoint URLs or cloud regions) before they can be used. - -- This page documents how to declare providers so Terraform can install them. - -- The [Provider Configuration](./providers.html) page documents how to configure - settings for providers. - -## About Providers - -Providers are plugins. They are released on a separate rhythm from Terraform -itself, and each provider has its own series of version numbers. - -Each provider plugin offers a set of -[resource types](resources.html#resource-types-and-arguments), and defines for -each resource type which arguments it accepts, which attributes it exports, and -how changes to resources of that type are actually applied to remote APIs. - -Most providers configure a specific infrastructure platform (either cloud or -self-hosted). Providers can also offer local utilities for tasks like -generating random numbers for unique resource names. - -The [Terraform Registry](https://registry.terraform.io/browse/providers) -is the main directory of publicly available Terraform providers, and hosts -providers for most major infrastructure platforms. You can also write and -distribute your own Terraform providers, for public or private use. - -> **Hands-on:** If you're interested in developing your own Terraform providers, try the [Call APIs with Terraform Providers](https://learn.hashicorp.com/collections/terraform/providers?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. - -### Provider Installation - -Terraform finds and installs providers when -[initializing a working directory](/docs/commands/init.html). It can -automatically download providers from a Terraform registry, or load them from a -local mirror or cache. - -When you add a new provider to a configuration, Terraform must install the -provider in order to use it. If you are using a persistent working directory, -you can run `terraform init` again to install new providers. - -Providers downloaded by `terraform init` are only installed for the current -working directory; other working directories can have their own installed -provider plugins. To help ensure that each working directory will use the same -selected versions, `terraform init` records its version selections in -your configuration's [dependency lock file](dependency-lock.html), named -`.terraform.lock.hcl` and will always make those same selections unless -you run `terraform init -upgrade` to update them. - -To save time and bandwidth, Terraform supports an optional plugin cache. You can -enable the cache using the `plugin_cache_dir` setting in -[the CLI configuration file](/docs/commands/cli-config.html). - -For more information about provider installation, see -[the `terraform init` command](/docs/commands/init.html). - -## Requiring Providers - -Each Terraform module must declare which providers it requires, so that -Terraform can install and use them. Provider requirements are declared in a -`required_providers` block. - -A provider requirement consists of a local name, a source location, and a -version constraint: - -```hcl -terraform { - required_providers { - mycloud = { - source = "mycorp/mycloud" - version = "~> 1.0" - } - } -} -``` - -The `required_providers` block must be nested inside the top-level -[`terraform` block](terraform.html) (which can also contain other settings). - -Each argument in the `required_providers` block enables one provider. The key -determines the provider's [local name](#local-names) (its unique identifier -within this module), and the value is an object with the following elements: - -* `source` - the global [source address](#source-addresses) for the - provider you intend to use, such as `hashicorp/aws`. - -* `version` - a [version constraint](#version-constraints) specifying - which subset of available provider versions the module is compatible with. - --> **Note:** The `name = { source, version }` syntax for `required_providers` -was added in Terraform v0.13. Previous versions of Terraform used a version -constraint string instead of an object (like `mycloud = "~> 1.0"`), and had no -way to specify provider source addresses. If you want to write a module that -works with both Terraform v0.12 and v0.13, see [v0.12-Compatible Provider -Requirements](#v0-12-compatible-provider-requirements) below. - -## Names and Addresses - -Each provider has two identifiers: - -- A unique _source address,_ which is only used when requiring a provider. -- A _local name,_ which is used everywhere else in a Terraform module. - --> **Note:** Prior to Terraform 0.13, providers only had local names, since -Terraform could only automatically download providers distributed by HashiCorp. - -### Local Names - -Local names are module-specific, and are assigned when requiring a provider. -Local names must be unique per-module. - -Outside of the `required_providers` block, Terraform configurations always refer -to providers by their local names. For example, the following configuration -declares `mycloud` as the local name for `mycorp/mycloud`, then uses that local -name when [configuring the provider](./providers.html): - -```hcl -terraform { - required_providers { - mycloud = { - source = "mycorp/mycloud" - version = "~> 1.0" - } - } -} - -provider "mycloud" { - # ... -} -``` - -Users of a provider can choose any local name for it. However, nearly every -provider has a _preferred local name,_ which it uses as a prefix for all of its -resource types. (For example, resources from `hashicorp/aws` all begin with -`aws`, like `aws_instance` or `aws_security_group`.) - -Whenever possible, you should use a provider's preferred local name. This makes -your configurations easier to understand, and lets you omit the `provider` -meta-argument from most of your resources. (If a resource doesn't specify which -provider configuration to use, Terraform interprets the first word of the -resource type as a local provider name.) - -### Source Addresses - -A provider's source address is its global identifier. It also specifies the -primary location where Terraform can download it. - -Source addresses consist of three parts delimited by slashes (`/`), as -follows: - -`[/]/` - -* **Hostname** (optional): The hostname of the Terraform registry that - distributes the provider. If omitted, this defaults to - `registry.terraform.io`, the hostname of - [the public Terraform Registry](https://registry.terraform.io/). - -* **Namespace:** An organizational namespace within the specified registry. - For the public Terraform Registry and for Terraform Cloud's private registry, - this represents the organization that publishes the provider. This field - may have other meanings for other registry hosts. - -* **Type:** A short name for the platform or system the provider manages. Must - be unique within a particular namespace on a particular registry host. - - The type is usually the provider's preferred local name. (There are - exceptions; for example, - [`hashicorp/google-beta`](https://registry.terraform.io/providers/hashicorp/google-beta/latest) - is an alternate release channel for `hashicorp/google`, so its preferred - local name is `google`. If in doubt, check the provider's documentation.) - -For example, -[the official HTTP provider](https://registry.terraform.io/providers/hashicorp/http) -belongs to the `hashicorp` namespace on `registry.terraform.io`, so its -source address is `registry.terraform.io/hashicorp/http` or, more commonly, just -`hashicorp/http`. - -The source address with all three components given explicitly is called the -provider's _fully-qualified address_. You will see fully-qualified address in -various outputs, like error messages, but in most cases a simplified display -version is used. This display version omits the source host when it is the -public registry, so you may see the shortened version `"hashicorp/random"` instead -of `"registry.terraform.io/hashicorp/random"`. - - --> **Note:** If you omit the `source` argument when requiring a provider, -Terraform uses an implied source address of -`registry.terraform.io/hashicorp/`. This is a backward compatibility -feature to support the transition to Terraform 0.13; in modules that require -0.13 or later, we recommend using explicit source addresses for all providers. - -### Handling Local Name Conflicts - -Whenever possible, we recommend using a provider's preferred local name, which -is usually the same as the "type" portion of its source address. - -However, it's sometimes necessary to use two providers with the same preferred -local name in the same module, usually when the providers are named after a -generic infrastructure type. Terraform requires unique local names for each -provider in a module, so you'll need to use a non-preferred name for at least -one of them. - -When this happens, we recommend combining each provider's namespace with -its type name to produce compound local names: - -```hcl -terraform { - required_providers { - # In the rare situation of using two providers that - # have the same type name -- "http" in this example -- - # use a compound local name to distinguish them. - hashicorp_http = { - source = "hashicorp/http" - version = "~> 2.0" - } - mycorp_http = { - source = "mycorp/http" - version = "~> 1.0" - } - } -} - -# References to these providers elsewhere in the -# module will use these compound local names. -provider "mycorp_http" { - # ... -} - -data "http" "example" { - provider = hashicorp_http - #... -} -``` - -Terraform won't be able to guess either provider's name from its resource types, -so you'll need to specify a `provider` meta-argument for every affected -resource. However, readers and maintainers of your module will be able to easily -understand what's happening, and avoiding confusion is much more important than -avoiding typing. - -## Version Constraints - -Each provider plugin has its own set of available versions, allowing the -functionality of the provider to evolve over time. Each provider dependency you -declare should have a [version constraint](./version-constraints.html) given in -the `version` argument so Terraform can select a single version per provider -that all modules are compatible with. - -The `version` argument is optional; if omitted, Terraform will accept any -version of the provider as compatible. However, we strongly recommend specifying -a version constraint for every provider your module depends on. - -### Best Practices for Provider Versions - -Each module should at least declare the minimum provider version it is known -to work with, using the `>=` version constraint syntax: - -```hcl -terraform { - required_providers { - mycloud = { - source = "hashicorp/aws" - version = ">= 1.0" - } - } -} -``` - -A module intended to be used as the root of a configuration — that is, as the -directory where you'd run `terraform apply` — should also specify the -_maximum_ provider version it is intended to work with, to avoid accidental -upgrades to incompatible new versions. The `~>` operator is a convenient -shorthand for allowing only patch releases within a specific minor release: - -```hcl -terraform { - required_providers { - mycloud = { - source = "hashicorp/aws" - version = "~> 1.0.4" - } - } -} -``` - -Do not use `~>` (or other maximum-version constraints) for modules you intend to -reuse across many configurations, even if you know the module isn't compatible -with certain newer versions. Doing so can sometimes prevent errors, but more -often it forces users of the module to update many modules simultaneously when -performing routine upgrades. Specify a minimum version, document any known -incompatibilities, and let the root module manage the maximum version. - -## Built-in Providers - -While most Terraform providers are distributed separately as plugins, there -is currently one provider that is built in to Terraform itself, which -provides -[the `terraform_remote_state` data source](/docs/providers/terraform/d/remote_state.html). - -Because this provider is built in to Terraform, you don't need to declare it -in the `required_providers` block in order to use its features. However, for -consistency it _does_ have a special provider source address, which is -`terraform.io/builtin/terraform`. This address may sometimes appear in -Terraform's error messages and other output in order to unambiguously refer -to the built-in provider, as opposed to a hypothetical third-party provider -with the type name "terraform". - -There is also an existing provider with the source address -`hashicorp/terraform`, which is an older version of the now-built-in provider -that was used by older versions of Terraform. `hashicorp/terraform` is not -compatible with Terraform v0.11 or later and should never be declared in a -`required_providers` block. - -## In-house Providers - -Anyone can develop and distribute their own Terraform providers. See -the [Call APIs with Terraform Providers](https://learn.hashicorp.com/collections/terraform/providers?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) -collection on HashiCorp Learn for more -about provider development. - -Some organizations develop their own providers to configure -proprietary systems, and wish to use these providers from Terraform without -publishing them on the public Terraform Registry. - -One option for distributing such a provider is to run an in-house _private_ -registry, by implementing -[the provider registry protocol](/docs/internals/provider-registry-protocol.html). - -Running an additional service just to distribute a single provider internally -may be undesirable, so Terraform also supports -[other provider installation methods](/docs/commands/cli-config.html#provider-installation), -including placing provider plugins directly in specific directories in the -local filesystem, via _filesystem mirrors_. - -All providers must have a [source address](#source-addresses) that includes -(or implies) the hostname of a registry, but that hostname does not need to -provide an actual registry service. For in-house providers that you intend to -distribute from a local filesystem directory, you can use an arbitrary hostname -in a domain your organization controls. - -For example, if your corporate domain were `example.com` then you might choose -to use `terraform.example.com` as your placeholder hostname, even if that -hostname doesn't actually resolve in DNS. You can then choose any namespace and -type you wish to represent your in-house provider under that hostname, giving -a source address like `terraform.example.com/examplecorp/ourcloud`: - -```hcl -terraform { - required_providers { - mycloud = { - source = "terraform.example.com/examplecorp/ourcloud" - version = ">= 1.0" - } - } -} -``` - -To make version 1.0.0 of this provider available for installation from the -local filesystem, choose one of the -[implied local mirror directories](/docs/commands/cli-config.html#implied-local-mirror-directories) -and create a directory structure under it like this: - -``` -terraform.example.com/examplecorp/ourcloud/1.0.0 -``` - -Under that `1.0.0` directory, create one additional directory representing the -platform where you are running Terraform, such as `linux_amd64` for Linux on -an AMD64/x64 processor, and then place the provider plugin executable and any -other needed files in that directory. - -Thus, on a Windows system, the provider plugin executable file might be at the -following path: - -``` -terraform.example.com/examplecorp/ourcloud/1.0.0/windows_amd64/terraform-provider-ourcloud.exe -``` - -If you later decide to switch to using a real private provider registry rather -than distribute binaries out of band, you can deploy the registry server at -`terraform.example.com` and retain the same namespace and type names, in which -case your existing modules will require no changes to locate the same provider -using your registry server. - -## v0.12-Compatible Provider Requirements - -Explicit provider source addresses were introduced with Terraform v0.13, so the -full provider requirements syntax is not supported by Terraform v0.12. - -However, in order to allow writing modules that are compatible with both -Terraform v0.12 and v0.13, versions of Terraform between v0.12.26 and v0.13 -will accept but ignore the `source` argument in a `required_providers` block. - -Consider the following example written for Terraform v0.13: - -```hcl -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 1.0" - } - } -} -``` - -Terraform v0.12.26 will accept syntax like the above but will understand it -in the same way as the following v0.12-style syntax: - -```hcl -terraform { - required_providers { - aws = "~> 1.0" - } -} -``` - -In other words, Terraform v0.12.26 ignores the `source` argument and considers -only the `version` argument, using the given [local name](#local-names) as the -un-namespaced provider type to install. - -When writing a module that is compatible with both Terraform v0.12.26 and -Terraform v0.13.0 or later, you must follow the following additional rules so -that both versions will select the same provider to install: - -* Use only providers that can be automatically installed by Terraform v0.12. - Third-party providers, such as community providers in the Terraform Registry, - cannot be selected by Terraform v0.12 because it does not support the - hierarchical source address namespace. - -* Ensure that your chosen local name exactly matches the "type" portion of the - source address given in the `source` argument, such as both being "aws" in - the examples above, because Terraform v0.12 will use the local name to - determine which provider plugin to download and install. - -* If the provider belongs to the `hashicorp` namespace, as with the - `hashicorp/aws` provider shown above, omit the `source` argument and allow - Terraform v0.13 to select the `hashicorp` namespace by default. - -* Provider type names must always be written in lowercase. Terraform v0.13 - treats provider source addresses as case-insensitive, but Terraform v0.12 - considers its legacy-style provider names to be case-sensitive. Using - lowercase will ensure that the name is selectable by both Terraform major - versions. - -This compatibility mechanism is provided as a temporary transitional aid only. -When Terraform v0.12 detects a use of the new `source` argument it doesn't -understand, it will emit a warning to alert the user that it is disregarding -the source address given in that argument. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/providers.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/providers.html.md deleted file mode 100644 index 4a4771b4..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/providers.html.md +++ /dev/null @@ -1,188 +0,0 @@ ---- -layout: "docs" -page_title: "Provider Configuration - Configuration Language" -sidebar_current: "docs-config-providers" -description: |- - Providers are responsible in Terraform for managing the lifecycle of a resource: create, read, update, delete. ---- - -# Provider Configuration - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Providers](../configuration-0-11/providers.html). - -Terraform relies on plugins called "providers" to interact with remote systems. - -Terraform configurations must declare which providers they require, so that -Terraform can install and use them. Additionally, some providers require -configuration (like endpoint URLs or cloud regions) before they can be used. - -- This page documents how to configure settings for providers. - -- The [Provider Requirements](./provider-requirements.html) page documents how - to declare providers so Terraform can install them. - -## Provider Configuration - -Provider configurations belong in the root module of a Terraform configuration. -(Child modules receive their provider configurations from the root module; for -more information, see -[Providers Within Modules](./modules.html#providers-within-modules).) - -A provider configuration is created using a `provider` block: - -```hcl -provider "google" { - project = "acme-app" - region = "us-central1" -} -``` - -The name given in the block header (`"google"` in this example) is the -[local name](./provider-requirements.html#local-names) of the provider to -configure. This provider should already be included in a `required_providers` -block. - -The body of the block (between `{` and `}`) contains configuration arguments for -the provider. Most arguments in this section are defined by the provider itself; -in this example both `project` and `region` are specific to the `google` -provider. - -You can use [expressions](./expressions.html) in the values of these -configuration arguments, but can only reference values that are known before the -configuration is applied. This means you can safely reference input variables, -but not attributes exported by resources (with an exception for resource -arguments that are specified directly in the configuration). - -A provider's documentation should list which configuration arguments it expects. -For providers distributed on the -[Terraform Registry](https://registry.terraform.io), versioned documentation is -available on each provider's page, via the "Documentation" link in the -provider's header. - -Some providers can use shell environment variables (or other alternate sources, -like VM instance profiles) as values for some of their arguments; when -available, we recommend using this as a way to keep credentials out of your -version-controlled Terraform code. - -There are also two "meta-arguments" that are defined by Terraform itself -and available for all `provider` blocks: - -- [`alias`, for using the same provider with different configurations for different resources][inpage-alias] -- [`version`, which we no longer recommend][inpage-versions] (use - [provider requirements](./provider-requirements.html) instead) - -Unlike many other objects in the Terraform language, a `provider` block may -be omitted if its contents would otherwise be empty. Terraform assumes an -empty default configuration for any provider that is not explicitly configured. - -## `alias`: Multiple Provider Configurations - -[inpage-alias]: #alias-multiple-provider-instances - -You can optionally define multiple configurations for the same provider, and -select which one to use on a per-resource or per-module basis. The primary -reason for this is to support multiple regions for a cloud platform; other -examples include targeting multiple Docker hosts, multiple Consul hosts, etc. - -To create multiple configurations for a given provider, include multiple -`provider` blocks with the same provider name. For each additional non-default -configuration, use the `alias` meta-argument to provide an extra name segment. -For example: - -```hcl -# The default provider configuration; resources that begin with `aws_` will use -# it as the default, and it can be referenced as `aws`. -provider "aws" { - region = "us-east-1" -} - -# Additional provider configuration for west coast region; resources can -# reference this as `aws.west`. -provider "aws" { - alias = "west" - region = "us-west-2" -} -``` - -### Default Provider Configurations - -A `provider` block without an `alias` argument is the _default_ configuration -for that provider. Resources that don't set the `provider` meta-argument will -use the default provider configuration that matches the first word of the -resource type name. (For example, an `aws_instance` resource uses the default -`aws` provider configuration unless otherwise stated.) - -If every explicit configuration of a provider has an alias, Terraform uses the -implied empty configuration as that provider's default configuration. (If the -provider has any required configuration arguments, Terraform will raise an error -when resources default to the empty configuration.) - -### Referring to Alternate Provider Configurations - -When Terraform needs the name of a provider configuration, it expects a -reference of the form `.`. In the example above, -`aws.west` would refer to the provider with the `us-west-2` region. - -These references are special expressions. Like references to other named -entities (for example, `var.image_id`), they aren't strings and don't need to be -quoted. But they are only valid in specific meta-arguments of `resource`, -`data`, and `module` blocks, and can't be used in arbitrary expressions. - -### Selecting Alternate Provider Configurations - -By default, resources use a default provider configuration (one without an -`alias` argument) inferred from the first word of the resource type name. - -To use an alternate provider configuration for a resource or data source, set -its `provider` meta-argument to a `.` reference: - -```hcl -resource "aws_instance" "foo" { - provider = aws.west - - # ... -} -``` - -To select alternate provider configurations for a child module, use its -`providers` meta-argument to specify which provider configurations should be -mapped to which local provider names inside the module: - -```hcl -module "aws_vpc" { - source = "./aws_vpc" - providers = { - aws = aws.west - } -} -``` - -Modules have some special requirements when passing in providers; see -[Providers Within Modules](./modules.html#providers-within-modules) -for more details. In most cases, only _root modules_ should define provider -configurations, with all child modules obtaining their provider configurations -from their parents. - - - -## `version`: An Older Way to Manage Provider Versions - -[inpage-versions]: #provider-versions - -The `version` meta-argument specifies a version constraint for a provider, and -works the same way as the `version` argument in a -[`required_providers` block](./provider-requirements.html). The version -constraint in a provider configuration is only used if `required_providers` -does not include one for that provider. - -**The `version` argument in provider configurations is deprecated.** -In Terraform 0.13 and later, version constraints should always be declared in -[the `required_providers` block](./provider-requirements.html). The `version` -argument will be removed in a future version of Terraform. - --> **Note:** The `version` meta-argument made sense before Terraform 0.13, since -Terraform could only install providers that were distributed by HashiCorp. Now -that Terraform can install providers from multiple sources, it makes more sense -to keep version constraints and provider source addresses together. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/resources.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/resources.html.md index 42f71fcf..87ae1bf5 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/resources.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/configuration/resources.html.md @@ -1,760 +1,135 @@ --- -layout: "docs" -page_title: "Resources - Configuration Language" -sidebar_current: "docs-config-resources" -description: |- - Resources are the most important element in a Terraform configuration. - Each resource corresponds to an infrastructure object, such as a virtual - network or compute instance. +layout: "language" +page_title: "Resources Landing Page - Configuration Language" --- -# Resources +# Resources Landing Page --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Resources](../configuration-0-11/resources.html). +To improve navigation, we've split the old Resources page into several smaller +pages. -> **Hands-on:** Try the [Terraform: Get Started](https://learn.hashicorp.com/collections/terraform/aws-get-started?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. + + + + + + + + -_Resources_ are the most important element in the Terraform language. -Each resource block describes one or more infrastructure objects, such -as virtual networks, compute instances, or higher-level components such -as DNS records. +## Syntax and Elements of Resource Blocks -## Resource Syntax +This information has moved to +[Resource Blocks](/docs/language/resources/syntax.html). -Resource declarations can include a number of advanced features, but only -a small subset are required for initial use. More advanced syntax features, -such as single resource declarations that produce multiple similar remote -objects, are described later in this page. +
-```hcl -resource "aws_instance" "web" { - ami = "ami-a1b2c3d4" - instance_type = "t2.micro" -} -``` -A `resource` block declares a resource of a given type ("aws_instance") -with a given local name ("web"). The name is used to refer to this resource -from elsewhere in the same Terraform module, but has no significance outside -that module's scope. -The resource type and name together serve as an identifier for a given -resource and so must be unique within a module. + + + + -Within the block body (between `{` and `}`) are the configuration arguments -for the resource itself. Most arguments in this section depend on the -resource type, and indeed in this example both `ami` and `instance_type` are -arguments defined specifically for [the `aws_instance` resource type](/docs/providers/aws/r/instance.html). +## Details of Resource Behavior --> **Note:** Resource names must start with a letter or underscore, and may -contain only letters, digits, underscores, and dashes. +This information has moved to +[Resource Behavior](/docs/language/resources/behavior.html). -## Resource Types +
-Each resource is associated with a single _resource type_, which determines -the kind of infrastructure object it manages and what arguments and other -attributes the resource supports. -### Providers -Each resource type is implemented by a [provider](./provider-requirements.html), -which is a plugin for Terraform that offers a collection of resource types. A -provider usually provides resources to manage a single cloud or on-premises -infrastructure platform. Providers are distributed separately from Terraform -itself, but Terraform can automatically install most providers when initializing -a working directory. +## Resource Meta-Arguments -In order to manage resources, a Terraform module must specify which providers it -requires. Additionally, most providers need some configuration in order to -access their remote APIs, and the root module must provide that configuration. +Each resource meta-argument has moved to its own page: -For more information, see: +- [`depends_on`](/docs/language/meta-arguments/depends_on.html) +- [`count`](/docs/language/meta-arguments/count.html) +- [`for_each`](/docs/language/meta-arguments/for_each.html) +- [`provider`](/docs/language/meta-arguments/resource-provider.html) +- [`lifecycle`](/docs/language/meta-arguments/lifecycle.html) +- [Provisioners](/docs/language/resources/provisioners/index.html) -- [Provider Requirements](./provider-requirements.html), for declaring which - providers a module uses. -- [Provider Configuration](./providers.html), for configuring provider settings. +
-Terraform usually automatically determines which provider to use based on a -resource type's name. (By convention, resource type names start with their -provider's preferred local name.) When using multiple configurations of a -provider (or non-preferred local provider names), you must use the `provider` -meta-argument to manually choose an alternate provider configuration. See -[the section on `provider` below][inpage-provider] for more details. -### Resource Arguments -Most of the arguments within the body of a `resource` block are specific to the -selected resource type. The resource type's documentation lists which arguments -are available and how their values should be formatted. + -The values for resource arguments can make full use of -[expressions](./expressions.html) and other dynamic Terraform -language features. +### `depends_on` -There are also some _meta-arguments_ that are defined by Terraform itself -and apply across all resource types. (See [Meta-Arguments](#meta-arguments) below.) +This information has moved to +[`depends_on`](/docs/language/meta-arguments/depends_on.html). -### Documentation for Resource Types +
-Every Terraform provider has its own documentation, describing its resource -types and their arguments. -Most publicly available providers are distributed on the -[Terraform Registry](https://registry.terraform.io/browse/providers), which also -hosts their documentation. When viewing a provider's page on the Terraform -Registry, you can click the "Documentation" link in the header to browse its -documentation. Provider documentation on the registry is versioned, and you can -use the dropdown version menu in the header to switch which version's -documentation you are viewing. - -To browse the publicly available providers and their documentation, see -[the providers section of the Terraform Registry](https://registry.terraform.io/browse/providers). --> **Note:** Provider documentation used to be hosted directly on terraform.io, -as part of Terraform's core documentation. Although some provider documentation -might still be hosted here, the Terraform Registry is now the main home for all -public provider docs. (The exception is the built-in -[`terraform` provider](/docs/providers/terraform/index.html) for reading state -data, since it is not available on the Terraform Registry.) + + + + + + + -## Resource Behavior +### `count` -A `resource` block declares that you want a particular infrastructure object -to exist with the given settings. If you are writing a new configuration for -the first time, the resources it defines will exist _only_ in the configuration, -and will not yet represent real infrastructure objects in the target platform. +This information has moved to +[`count`](/docs/language/meta-arguments/count.html). -_Applying_ a Terraform configuration is the process of creating, updating, -and destroying real infrastructure objects in order to make their settings -match the configuration. - -When Terraform creates a new infrastructure object represented by a `resource` -block, the identifier for that real object is saved in Terraform's -[state](/docs/state/index.html), allowing it to be updated and destroyed -in response to future changes. For resource blocks that already have an -associated infrastructure object in the state, Terraform compares the -actual configuration of the object with the arguments given in the -configuration and, if necessary, updates the object to match the configuration. +
-This general behavior applies for all resources, regardless of type. The -details of what it means to create, update, or destroy a resource are different -for each resource type, but this standard set of verbs is common across them -all. - -The meta-arguments within `resource` blocks, documented in the -sections below, allow some details of this standard resource behavior to be -customized on a per-resource basis. - -### Accessing Resource Attributes - -[Expressions](./expressions.html) within a Terraform module can access -information about resources in the same module, and you can use that information -to help configure other resources. Use the `..` -syntax to reference a resource attribute in an expression. - -In addition to arguments specified in the configuration, resources often provide -read-only attributes with information obtained from the remote API; this often -includes things that can't be known until the resource is created, like the -resource's unique random ID. - -Many providers also include [data sources](./data-sources.html), which are a -special type of resource used only for looking up information. - -For a list of the attributes a resource or data source type provides, consult -its documentation; these are generally included in a second list below its list -of configurable arguments. - -For more information about referencing resource attributes in expressions, see -[Expressions: References to Resource Attributes](./expressions.html#references-to-resource-attributes). - -### Resource Dependencies - -Most resources in a configuration don't have any particular relationship, and -Terraform can make changes to several unrelated resources in parallel. - -However, some resources must be processed after other specific resources; -sometimes this is because of how the resource works, and sometimes the -resource's configuration just requires information generated by another -resource. - -Most resource dependencies are handled automatically. Terraform analyses any -[expressions](./expressions.html) within a `resource` block to find references -to other objects, and treats those references as implicit ordering requirements -when creating, updating, or destroying resources. Since most resources with -behavioral dependencies on other resources also refer to those resources' data, -it's usually not necessary to manually specify dependencies between resources. - -However, some dependencies cannot be recognized implicitly in configuration. For -example, if Terraform must manage access control policies _and_ take actions -that require those policies to be present, there is a hidden dependency between -the access policy and a resource whose creation depends on it. In these rare -cases, [the `depends_on` meta-argument][inpage-depend] can explicitly specify a -dependency. - -## Meta-Arguments - -Terraform CLI defines the following meta-arguments, which can be used with -any resource type to change the behavior of resources: - -- [`depends_on`, for specifying hidden dependencies][inpage-depend] -- [`count`, for creating multiple resource instances according to a count][inpage-count] -- [`for_each`, to create multiple instances according to a map, or set of strings][inpage-for_each] -- [`provider`, for selecting a non-default provider configuration][inpage-provider] -- [`lifecycle`, for lifecycle customizations][inpage-lifecycle] -- [`provisioner` and `connection`, for taking extra actions after resource creation][inpage-provisioner] - -These arguments often have additional restrictions on what language features can -be used with them, which are described in each - -### `depends_on`: Explicit Resource Dependencies - -[inpage-depend]: #depends_on-explicit-resource-dependencies - -Use the `depends_on` meta-argument to handle hidden resource dependencies that -Terraform can't automatically infer. - -Explicitly specifying a dependency is only necessary when a resource relies on -some other resource's behavior but _doesn't_ access any of that resource's data -in its arguments. - -This argument is available in all `resource` blocks, regardless of resource -type. For example: - -```hcl -resource "aws_iam_role" "example" { - name = "example" - - # assume_role_policy is omitted for brevity in this example. See the - # documentation for aws_iam_role for a complete example. - assume_role_policy = "..." -} - -resource "aws_iam_instance_profile" "example" { - # Because this expression refers to the role, Terraform can infer - # automatically that the role must be created first. - role = aws_iam_role.example.name -} - -resource "aws_iam_role_policy" "example" { - name = "example" - role = aws_iam_role.example.name - policy = jsonencode({ - "Statement" = [{ - # This policy allows software running on the EC2 instance to - # access the S3 API. - "Action" = "s3:*", - "Effect" = "Allow", - }], - }) -} - -resource "aws_instance" "example" { - ami = "ami-a1b2c3d4" - instance_type = "t2.micro" - # Terraform can infer from this that the instance profile must - # be created before the EC2 instance. - iam_instance_profile = aws_iam_instance_profile.example - - # However, if software running in this EC2 instance needs access - # to the S3 API in order to boot properly, there is also a "hidden" - # dependency on the aws_iam_role_policy that Terraform cannot - # automatically infer, so it must be declared explicitly: - depends_on = [ - aws_iam_role_policy.example, - ] -} -``` - -The `depends_on` meta-argument, if present, must be a list of references -to other resources in the same module. Arbitrary expressions are not allowed -in the `depends_on` argument value, because its value must be known before -Terraform knows resource relationships and thus before it can safely -evaluate expressions. - -The `depends_on` argument should be used only as a last resort. When using it, -always include a comment explaining why it is being used, to help future -maintainers understand the purpose of the additional dependency. - -### `count`: Multiple Resource Instances By Count - -[inpage-count]: #count-multiple-resource-instances-by-count - --> **Note:** A given resource block cannot use both `count` and `for_each`. - -By default, a `resource` block configures one real infrastructure object. -However, sometimes you want to manage several similar objects, such as a fixed -pool of compute instances. Terraform has two ways to do this: -`count` and [`for_each`][inpage-for_each]. -> **Hands-on:** Try the [Manage Similar Resources With Count](https://learn.hashicorp.com/tutorials/terraform/count?in=terraform/0-13&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + + + + + + + -The `count` meta-argument accepts a whole number, and creates that many -instances of the resource. Each instance has a distinct infrastructure object -associated with it (as described above in -[Resource Behavior](#resource-behavior)), and each is separately created, -updated, or destroyed when the configuration is applied. +### `for_each` -```hcl -resource "aws_instance" "server" { - count = 4 # create four similar EC2 instances +This information has moved to +[`for_each`](/docs/language/meta-arguments/for_each.html). - ami = "ami-a1b2c3d4" - instance_type = "t2.micro" +
- tags = { - Name = "Server ${count.index}" - } -} -``` -#### The `count` Object - -In resource blocks where `count` is set, an additional `count` object is -available in expressions, so you can modify the configuration of each instance. -This object has one attribute: - -- `count.index` — The distinct index number (starting with `0`) corresponding - to this instance. - -#### Referring to Instances - -When `count` is set, Terraform distinguishes between the resource block itself -and the multiple _resource instances_ associated with it. Instances are -identified by an index number, starting with `0`. - -- `.` (for example, `aws_instance.server`) refers to the resource block. -- `.[]` (for example, `aws_instance.server[0]`, - `aws_instance.server[1]`, etc.) refers to individual instances. - -This is different from resources without `count` or `for_each`, which can be -referenced without an index or key. - --> **Note:** Within nested `provisioner` or `connection` blocks, the special -`self` object refers to the current _resource instance,_ not the resource block -as a whole. - -#### Using Expressions in `count` - -The `count` meta-argument accepts numeric [expressions](./expressions.html). -However, unlike most resource arguments, the `count` value must be known -_before_ Terraform performs any remote resource actions. This means `count` -can't refer to any resource attributes that aren't known until after a -configuration is applied (such as a unique ID generated by the remote API when -an object is created). - -#### When to Use `for_each` Instead of `count` - -If your resource instances are almost identical, `count` is appropriate. If some -of their arguments need distinct values that can't be directly derived from an -integer, it's safer to use `for_each`. - -Before `for_each` was available, it was common to derive `count` from the -length of a list and use `count.index` to look up the original list value: - -```hcl -variable "subnet_ids" { - type = list(string) -} - -resource "aws_instance" "server" { - # Create one instance for each subnet - count = length(var.subnet_ids) - - ami = "ami-a1b2c3d4" - instance_type = "t2.micro" - subnet_id = var.subnet_ids[count.index] - tags = { - Name = "Server ${count.index}" - } -} -``` + -This was fragile, because the resource instances were still identified by their -_index_ instead of the string values in the list. If an element was removed from -the middle of the list, every instance _after_ that element would see its -`subnet_id` value change, resulting in more remote object changes than intended. -Using `for_each` gives the same flexibility without the extra churn. +### `provider` -### `for_each`: Multiple Resource Instances Defined By a Map, or Set of Strings +This information has moved to +[`provider`](/docs/language/meta-arguments/resource-provider.html). -[inpage-for_each]: #for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings - --> **Version note:** `for_each` was added in Terraform 0.12.6. - --> **Note:** A given resource block cannot use both `count` and `for_each`. - -By default, a `resource` block configures one real infrastructure object. -However, sometimes you want to manage several similar objects, such as a fixed -pool of compute instances. Terraform has two ways to do this: -[`count`][inpage-count] and `for_each`. - -> **Hands-on:** Try the [Manage Similar Resources With For Each](https://learn.hashicorp.com/tutorials/terraform/for-each?in=terraform/0-13&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. - -The `for_each` meta-argument accepts a map or a set of strings, and creates an -instance for each item in that map or set. Each instance has a distinct -infrastructure object associated with it (as described above in -[Resource Behavior](#resource-behavior)), and each is separately created, -updated, or destroyed when the configuration is applied. - --> **Note:** The keys of the map (or all the values in the case of a set of strings) must -be _known values_, or you will get an error message that `for_each` has dependencies -that cannot be determined before apply, and a `-target` may be needed. `for_each` keys -cannot be the result (or rely on the result of) of impure functions, including `uuid`, `bcrypt`, -or `timestamp`, as their evaluation is deferred resource during evaluation. - -Map: - -```hcl -resource "azurerm_resource_group" "rg" { - for_each = { - a_group = "eastus" - another_group = "westus2" - } - name = each.key - location = each.value -} -``` -Set of strings: - -```hcl -resource "aws_iam_user" "the-accounts" { - for_each = toset( ["Todd", "James", "Alice", "Dottie"] ) - name = each.key -} -``` -#### The `each` Object - -In resource blocks where `for_each` is set, an additional `each` object is -available in expressions, so you can modify the configuration of each instance. -This object has two attributes: - -- `each.key` — The map key (or set member) corresponding to this instance. -- `each.value` — The map value corresponding to this instance. (If a set was - provided, this is the same as `each.key`.) - -#### Using Expressions in `for_each` - -The `for_each` meta-argument accepts map or set [expressions](./expressions.html). -However, unlike most resource arguments, the `for_each` value must be known -_before_ Terraform performs any remote resource actions. This means `for_each` -can't refer to any resource attributes that aren't known until after a -configuration is applied (such as a unique ID generated by the remote API when -an object is created). - -The `for_each` value must be a map or set with one element per desired -resource instance. If you need to declare resource instances based on a nested -data structure or combinations of elements from multiple data structures you -can use Terraform expressions and functions to derive a suitable value. -For example: - -* Transform a multi-level nested structure into a flat list by - [using nested `for` expressions with the `flatten` function](./functions/flatten.html#flattening-nested-structures-for-for_each). -* Produce an exhaustive list of combinations of elements from two or more - collections by - [using the `setproduct` function inside a `for` expression](./functions/setproduct.html#finding-combinations-for-for_each). - -#### Referring to Instances - -When `for_each` is set, Terraform distinguishes between the resource block itself -and the multiple _resource instances_ associated with it. Instances are -identified by a map key (or set member) from the value provided to `for_each`. - -- `.` (for example, `azurerm_resource_group.rg`) refers to the resource block. -- `.[]` (for example, `azurerm_resource_group.rg["a_group"]`, - `azurerm_resource_group.rg["another_group"]`, etc.) refers to individual instances. - -This is different from resources without `count` or `for_each`, which can be -referenced without an index or key. - --> **Note:** Within nested `provisioner` or `connection` blocks, the special -`self` object refers to the current _resource instance,_ not the resource block -as a whole. - -#### Using Sets - -The Terraform language doesn't have a literal syntax for -[set values](./types.html#collection-types), but you can use the `toset` -function to explicitly convert a list of strings to a set: - -```hcl -locals { - subnet_ids = toset([ - "subnet-abcdef", - "subnet-012345", - ]) -} - -resource "aws_instance" "server" { - for_each = local.subnet_ids - - ami = "ami-a1b2c3d4" - instance_type = "t2.micro" - subnet_id = each.key # note: each.key and each.value are the same for a set - - tags = { - Name = "Server ${each.key}" - } -} -``` - -Conversion from list to set discards the ordering of the items in the list and -removes any duplicate elements. `toset(["b", "a", "b"])` will produce a set -containing only `"a"` and `"b"` in no particular order; the second `"b"` is -discarded. - -If you are writing a module with an [input variable](./variables.html) that -will be used as a set of strings for `for_each`, you can set its type to -`set(string)` to avoid the need for an explicit type conversion: - -``` -variable "subnet_ids" { - type = set(string) -} - -resource "aws_instance" "server" { - for_each = var.subnet_ids - - # (and the other arguments as above) -} -``` - -### `provider`: Selecting a Non-default Provider Configuration - -[inpage-provider]: #provider-selecting-a-non-default-provider-configuration - -The `provider` meta-argument specifies which provider configuration to use, -overriding Terraform's default behavior of selecting one based on the resource -type name. Its value should be an unquoted `.` reference. - -As described in [Provider Configuration](./providers.html), you can optionally -create multiple configurations for a single provider (usually to manage -resources in different regions of multi-region services). Each provider can have -one default configuration, and any number of alternate configurations that -include an extra name segment (or "alias"). - -By default, Terraform interprets the initial word in the resource type name -(separated by underscores) as the local name of a provider, and uses that -provider's default configuration. For example, the resource type -`google_compute_instance` is associated automatically with the default -configuration for the provider named `google`. - -By using the `provider` meta-argument, you can select an alternate provider -configuration for a resource: - -```hcl -# default configuration -provider "google" { - region = "us-central1" -} - -# alternate configuration, whose alias is "europe" -provider "google" { - alias = "europe" - region = "europe-west1" -} - -resource "google_compute_instance" "example" { - # This "provider" meta-argument selects the google provider - # configuration whose alias is "europe", rather than the - # default configuration. - provider = google.europe - - # ... -} -``` - -A resource always has an implicit dependency on its associated provider, to -ensure that the provider is fully configured before any resource actions -are taken. - -The `provider` meta-argument expects -[a `.` reference](./providers.html#referring-to-alternate-providers), -which does not need to be quoted. Arbitrary expressions are not permitted for -`provider` because it must be resolved while Terraform is constructing the -dependency graph, before it is safe to evaluate expressions. - -### `lifecycle`: Lifecycle Customizations - -[inpage-lifecycle]: #lifecycle-lifecycle-customizations - -The general lifecycle for resources is described above in the -[Resource Behavior](#resource-behavior) section. Some details of that behavior -can be customized using the special nested `lifecycle` block within a resource -block body: - -``` -resource "azurerm_resource_group" "example" { - # ... - - lifecycle { - create_before_destroy = true - } -} -``` - -The `lifecycle` block and its contents are meta-arguments, available -for all `resource` blocks regardless of type. The following lifecycle -meta-arguments are supported: - -* `create_before_destroy` (bool) - By default, when Terraform must make a - change to a resource argument that cannot be updated in-place due to - remote API limitations, Terraform will instead destroy the existing object - and then create a new replacement object with the new configured arguments. - - The `create_before_destroy` meta-argument changes this behavior so that - the new replacement object is created _first,_ and then the prior object - is destroyed only once the replacement is created. - - This is an opt-in behavior because many remote object types have unique - name requirements or other constraints that must be accommodated for - both a new and an old object to exist concurrently. Some resource types - offer special options to append a random suffix onto each object name to - avoid collisions, for example. Terraform CLI cannot automatically activate - such features, so you must understand the constraints for each resource - type before using `create_before_destroy` with it. - -* `prevent_destroy` (bool) - This meta-argument, when set to `true`, will - cause Terraform to reject with an error any plan that would destroy the - infrastructure object associated with the resource, as long as the argument - remains present in the configuration. - - This can be used as a measure of safety against the accidental replacement - of objects that may be costly to reproduce, such as database instances. - However, it will make certain configuration changes impossible to apply, - and will prevent the use of the `terraform destroy` command once such - objects are created, and so this option should be used sparingly. - - Since this argument must be present in configuration for the protection to - apply, note that this setting does not prevent the remote object from - being destroyed if the `resource` block were removed from configuration - entirely: in that case, the `prevent_destroy` setting is removed along - with it, and so Terraform will allow the destroy operation to succeed. - -* `ignore_changes` (list of attribute names) - By default, Terraform detects - any difference in the current settings of a real infrastructure object - and plans to update the remote object to match configuration. - - The `ignore_changes` feature is intended to be used when a resource is - created with references to data that may change in the future, but should - not effect said resource after its creation. In some rare cases, settings - of a remote object are modified by processes outside of Terraform, which - Terraform would then attempt to "fix" on the next run. In order to make - Terraform share management responsibilities of a single object with a - separate process, the `ignore_changes` meta-argument specifies resource - attributes that Terraform should ignore when planning updates to the - associated remote object. - - The arguments corresponding to the given attribute names are considered - when planning a _create_ operation, but are ignored when planning an - _update_. The arguments are the relative address of the attributes in the - resource. Map and list elements can be referenced using index notation, - like `tags["Name"]` and `list[0]` respectively. - - - ```hcl - resource "aws_instance" "example" { - # ... - - lifecycle { - ignore_changes = [ - # Ignore changes to tags, e.g. because a management agent - # updates these based on some ruleset managed elsewhere. - tags, - ] - } - } - ``` - - Instead of a list, the special keyword `all` may be used to instruct - Terraform to ignore _all_ attributes, which means that Terraform can - create and destroy the remote object but will never propose updates to it. - - Only attributes defined by the resource type can be ignored. - `ignore_changes` cannot be applied to itself or to any other meta-arguments. - -The `lifecycle` settings all effect how Terraform constructs and traverses -the dependency graph. As a result, only literal values can be used because -the processing happens too early for arbitrary expression evaluation. - -### `provisioner` and `connection`: Resource Provisioners - -[inpage-provisioner]: #provisioner-and-connection-resource-provisioners - -> **Hands-on:** To learn about more declarative ways to handle provisioning actions, try the [Provision Infrastructure Deployed with Terraform](https://learn.hashicorp.com/collections/terraform/provision?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. - -Some infrastructure objects require some special actions to be taken after they -are created before they can become fully functional. For example, compute -instances may require configuration to be uploaded or a configuration management -program to be run before they can begin their intended operation. - -Create-time actions like these can be described using _resource provisioners_. -A provisioner is another type of plugin supported by Terraform, and each -provisioner takes a different kind of action in the context of a resource -being created. - -Provisioning steps should be used sparingly, since they represent -non-declarative actions taken during the creation of a resource and so -Terraform is not able to model changes to them as it can for the declarative -portions of the Terraform language. - -Provisioners can also be defined to run when a resource is _destroyed_, with -certain limitations. - -The `provisioner` and `connection` block types within `resource` blocks are -meta-arguments available across all resource types. Provisioners and their -usage are described in more detail in -[the Provisioners section](/docs/provisioners/index.html). - -## Local-only Resources - -While most resource types correspond to an infrastructure object type that -is managed via a remote network API, there are certain specialized resource -types that operate only within Terraform itself, calculating some results and -saving those results in the state for future use. - -For example, local-only resource types exist for -[generating private keys](/docs/providers/tls/r/private_key.html), -[issuing self-signed TLS certificates](/docs/providers/tls/r/self_signed_cert.html), -and even [generating random ids](/docs/providers/random/r/id.html). -While these resource types often have a more marginal purpose than those -managing "real" infrastructure objects, they can be useful as glue to help -connect together other resources. - -The behavior of local-only resources is the same as all other resources, but -their result data exists only within the Terraform state. "Destroying" such -a resource means only to remove it from the state, discarding its data. - -## Operation Timeouts - -Some resource types provide a special `timeouts` nested block argument that -allows you to customize how long certain operations are allowed to take -before being considered to have failed. -For example, [`aws_db_instance`](/docs/providers/aws/r/db_instance.html) -allows configurable timeouts for `create`, `update` and `delete` operations. - -Timeouts are handled entirely by the resource type implementation in the -provider, but resource types offering these features follow the convention -of defining a child block called `timeouts` that has a nested argument -named after each operation that has a configurable timeout value. -Each of these arguments takes a string representation of a duration, such -as `"60m"` for 60 minutes, `"10s"` for ten seconds, or `"2h"` for two hours. - -```hcl -resource "aws_db_instance" "example" { - # ... - - timeouts { - create = "60m" - delete = "2h" - } -} -``` - -The set of configurable operations is chosen by each resource type. Most -resource types do not support the `timeouts` block at all. Consult the -documentation for each resource type to see which operations it offers -for configuration, if any. +
+ + + + + + + + +### `lifecycle` + +This information has moved to +[`lifecycle`](/docs/language/meta-arguments/lifecycle.html). + +
+ + + + + +### Provisioners + +This information has moved to +[Provisioners](/docs/language/resources/provisioners/index.html). + +
diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/syntax-json.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/syntax-json.html.md deleted file mode 100644 index ce00be96..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/syntax-json.html.md +++ /dev/null @@ -1,474 +0,0 @@ ---- -layout: "docs" -page_title: "JSON Configuration Syntax - Configuration Language" -sidebar_current: "docs-config-syntax-json" -description: |- - In addition to the native syntax that is most commonly used with Terraform, - the Terraform language can also be expressed in a JSON-compatible syntax. ---- - -# JSON Configuration Syntax - --> **Note:** This page is about Terraform 0.12 and later. The JSON configuration -syntax in 0.11 and earlier was never formally documented. For other information -about Terraform 0.11 and earlier, see -[0.11 Configuration Language](../configuration-0-11/index.html). - -Most Terraform configurations are written in -[the native Terraform language syntax](./syntax.html), which is designed to be -easy for humans to read and update. - -Terraform also supports an alternative syntax that is JSON-compatible. This -syntax is useful when generating portions of a configuration programmatically, -since existing JSON libraries can be used to prepare the generated -configuration files. - -The JSON syntax is defined in terms of the native syntax. Everything that can -be expressed in native syntax can also be expressed in JSON syntax, but some -constructs are more complex to represent in JSON due to limitations of the -JSON grammar. - -Terraform expects native syntax for files named with a `.tf` suffix, and -JSON syntax for files named with a `.tf.json` suffix. - -The low-level JSON syntax, just as with the native syntax, is defined in terms -of a specification called _HCL_. It is not necessary to know all of the details -of HCL syntax or its JSON mapping in order to use Terraform, and so this page -summarizes the most important differences between native and JSON syntax. -If you are interested, you can find a full definition of HCL's JSON syntax -in [its specification](https://github.com/hashicorp/hcl/blob/hcl2/json/spec.md). - -## JSON File Structure - -At the root of any JSON-based Terraform configuration is a JSON object. The -properties of this object correspond to the top-level block types of the -Terraform language. For example: - -```json -{ - "variable": { - "example": { - "default": "hello" - } - } -} -``` - -Each top-level object property must match the name of one of the expected -top-level block types. Block types that expect labels, such as `variable` -shown above, are represented by one nested object value for each level -of label. `resource` blocks expect two labels, so two levels of nesting -are required: - -```json -{ - "resource": { - "aws_instance": { - "example": { - "instance_type": "t2.micro", - "ami": "ami-abc123" - } - } - } -} -``` - -After any nested objects representing the labels, finally one more nested -object represents the body of the block itself. In the above examples, the -`default` argument for `variable "example"` and the `instance_type` and -`ami` arguments for `resource "aws_instance" "example"` are specified. - -Taken together, the above two configuration files are equivalent to the -following blocks in the native syntax: - -```hcl -variable "example" { - default = "hello" -} - -resource "aws_instance" "example" { - instance_type = "t2.micro" - ami = "ami-abc123" -} -``` - -Within each top-level block type the rules for mapping to JSON are slightly -different (see the [block-type-specific exceptions](#block-type-specific-exceptions) below), but the following general rules apply in most cases: - -* The JSON object representing the block body contains properties that - correspond either to argument names or to nested block type names. - -* Where a property corresponds to an argument that accepts - [arbitrary expressions](./expressions.html) in the native syntax, the - property value is mapped to an expression as described under - [_Expression Mapping_](#expression-mapping) below. For arguments that - do _not_ accept arbitrary expressions, the interpretation of the property - value depends on the argument, as described in the - [block-type-specific exceptions](#block-type-specific-exceptions) - given later in this page. - -* Where a property name corresponds to an expected nested block type name, - the value is interpreted as described under - [_Nested Block Mapping_](#nested-block-mapping) below, unless otherwise - stated in [the block-type-specific exceptions](#block-type-specific-exceptions) - given later in this page. - -## Expression Mapping - -Since JSON grammar is not able to represent all of the Terraform language -[expression syntax](./expressions.html), JSON values interpreted as expressions -are mapped as follows: - -| JSON | Terraform Language Interpretation | -| ------- | ------------------------------------------------------------------------------------------------------------- | -| Boolean | A literal `bool` value. | -| Number | A literal `number` value. | -| String | Parsed as a [string template](./expressions.html#string-templates) and then evaluated as described below. | -| Object | Each property value is mapped per this table, producing an `object(...)` value with suitable attribute types. | -| Array | Each element is mapped per this table, producing a `tuple(...)` value with suitable element types. | -| Null | A literal `null`. | - -When a JSON string is encountered in a location where arbitrary expressions are -expected, its value is first parsed as a [string template](./expressions.html#string-templates) -and then it is evaluated to produce the final result. - -If the given template consists _only_ of a single interpolation sequence, -the result of its expression is taken directly, without first converting it -to a string. This allows non-string expressions to be used within the -JSON syntax: - -```json -{ - "output": { - "example": { - "value": "${aws_instance.example}" - } - } -} -``` - -The `output "example"` declared above has the object value representing the -given `aws_instance` resource block as its value, rather than a string value. -This special behavior does not apply if any literal or control sequences appear -in the template; in these other situations, a string value is always produced. - -## Nested Block Mapping - -When a JSON object property is named after a nested block type, the value -of this property represents one or more blocks of that type. The value of -the property must be either a JSON object or a JSON array. - -The simplest situation is representing only a single block of the given type -when that type expects no labels, as with the `lifecycle` nested block used -within `resource` blocks: - -```json -{ - "resource": { - "aws_instance": { - "example": { - "lifecycle": { - "create_before_destroy": true - } - } - } - } -} -``` - -The above is equivalent to the following native syntax configuration: - -```hcl -resource "aws_instance" "example" { - lifecycle { - create_before_destroy = true - } -} -``` - -When the nested block type requires one or more labels, or when multiple -blocks of the same type can be given, the mapping gets a little more -complicated. For example, the `provisioner` nested block type used -within `resource` blocks expects a label giving the provisioner to use, -and the ordering of provisioner blocks is significant to decide the order -of operations. - -The following native syntax example shows a `resource` block with a number -of provisioners of different types: - -```hcl -resource "aws_instance" "example" { - # (resource configuration omitted for brevity) - - provisioner "local-exec" { - command = "echo 'Hello World' >example.txt" - } - provisioner "file" { - source = "example.txt" - destination = "/tmp/example.txt" - } - provisioner "remote-exec" { - inline = [ - "sudo install-something -f /tmp/example.txt", - ] - } -} -``` - -In order to preserve the order of these blocks, you must use a JSON array -as the direct value of the property representing this block type, as in -this JSON equivalent of the above: - -```json -{ - "resource": { - "aws_instance": { - "example": { - "provisioner": [ - { - "local-exec": { - "command": "echo 'Hello World' >example.txt" - } - }, - { - "file": { - "source": "example.txt", - "destination": "/tmp/example.txt" - } - }, - { - "remote-exec": { - "inline": ["sudo install-something -f /tmp/example.txt"] - } - } - ] - } - } - } -} -``` - -Each element of the `provisioner` array is an object with a single property -whose name represents the label for each `provisioner` block. For block types -that expect multiple labels, this pattern of alternating array and object -nesting can be used for each additional level. - -If a nested block type requires labels but the order does _not_ matter, you -may omit the array and provide just a single object whose property names -correspond to unique block labels. This is allowed as a shorthand for the above -for simple cases, but the alternating array and object approach is the most -general. We recommend using the most general form if systematically converting -from native syntax to JSON, to ensure that the meaning of the configuration is -preserved exactly. - -### Comment Properties - -Although we do not recommend hand-editing of JSON syntax configuration files --- this format is primarily intended for programmatic generation and consumption -- -a limited form of _comments_ are allowed inside JSON objects that represent -block bodies using a special property name: - -```json -{ - "resource": { - "aws_instance": { - "example": { - "//": "This instance runs the scheduled tasks for backup", - - "instance_type": "t2.micro", - "ami": "ami-abc123" - } - } - } -} -``` - -In any object that represents a block body, properties named `"//"` are -ignored by Terraform entirely. This exception does _not_ apply to objects -that are being [interpreted as expressions](#expression-mapping), where this -would be interpreted as an object type attribute named `"//"`. - -This special property name can also be used at the root of a JSON-based -configuration file. This can be useful to note which program created the file. - -```json -{ - "//": "This file is generated by generate-outputs.py. DO NOT HAND-EDIT!", - - "output": { - "example": { - "value": "${aws_instance.example}" - } - } -} -``` - -## Block-type-specific Exceptions - -[inpage-block]: #block-type-specific-exceptions - -Certain arguments within specific block types are processed in a special way -by Terraform, and so their mapping to the JSON syntax does not follow the -general rules described above. The following sub-sections describe the special -mapping rules that apply to each top-level block type. - -### `resource` and `data` blocks - -Some meta-arguments for the `resource` and `data` block types take direct -references to objects, or literal keywords. When represented in JSON, the -reference or keyword is given as a JSON string with no additional surrounding -spaces or symbols. - -For example, the `provider` meta-argument takes a `.` reference -to a provider configuration, which appears unquoted in the native syntax but -must be presented as a string in the JSON syntax: - -```json -{ - "resource": { - "aws_instance": { - "example": { - "provider": "aws.foo" - } - } - } -} -``` - -This special processing applies to the following meta-arguments: - -* `provider`: a single string, as shown above -* `depends_on`: an array of strings containing references to named entities, - like `["aws_instance.example"]`. -* `ignore_changes` within the `lifecycle` block: if set to `all`, a single - string `"all"` must be given. Otherwise, an array of JSON strings containing - property references must be used, like `["ami"]`. - -Special processing also applies to the `type` argument of any `connection` -blocks, whether directly inside the `resource` block or nested inside -`provisioner` blocks: the given string is interpreted literally, and not -parsed and evaluated as a string template. - -### `variable` blocks - -All arguments inside `variable` blocks have non-standard mappings to JSON: - -* `type`: a string containing a type expression, like `"string"` or `"list(string)"`. -* `default`: a literal JSON value that can be converted to the given type. - Strings within this value are taken literally and _not_ interpreted as - string templates. -* `description`: a literal JSON string, _not_ interpreted as a template. - -```json -{ - "variable": { - "example": { - "type": "string", - "default": "hello" - } - } -} -``` - -### `output` blocks - -The `description` and `sensitive` arguments are interpreted as literal JSON -values. The `description` string is not interpreted as a string template. - -The `value` argument is [interpreted as an expression](#expression-mapping). - -```json -{ - "output": { - "example": { - "value": "${aws_instance.example}" - } - } -} -``` - -### `locals` blocks - -The value of the JSON object property representing the locals block type -must be a JSON object whose property names are the local value names to -declare: - -```json -{ - "locals": { - "greeting": "Hello, ${var.name}" - } -} -``` - -The value of each of these nested properties is -[interpreted as an expression](#expression-mapping). - -### `module` blocks - -The `source` and `version` meta-arguments must be given as literal strings. The -values are not interpreted as string templates. - -The `providers` meta-argument must be given as a JSON object whose properties -are the compact provider addresses to expose into the child module and whose -values are the provider addresses to use from the current module, both -given as literal strings: - -```json -{ - "module": { - "example": { - "source": "hashicorp/consul/azurerm", - "version": "= 1.0.0", - "providers": { - "aws": "aws.usw1" - } - } - } -} -``` - -### `provider` blocks - -The `alias` and `version` meta-arguments must be given as literal strings. The -values are not interpreted as string templates. - -```json -{ - "provider": { - "aws": [ - { - "region": "us-east-1" - }, - { - "alias": "usw1", - "region": "us-west-1" - } - ] - } -} -``` - -### `terraform` blocks - -Since no settings within `terraform` blocks accept named object references or -function calls, all setting values are taken literally. String values are not -interpreted as string templates. - -Since only one `backend` block is allowed per `terraform` block, the compact -block mapping can be used to represent it, with a nested object containing -a single property whose name represents the backend type. - -```json -{ - "terraform": { - "required_version": ">= 0.12.0", - "backend": { - "s3": { - "region": "us-west-2", - "bucket": "acme-terraform-states" - } - } - } -} -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/syntax.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/syntax.html.md deleted file mode 100644 index 2b9e6b41..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/syntax.html.md +++ /dev/null @@ -1,135 +0,0 @@ ---- -layout: "docs" -page_title: "Syntax - Configuration Language" -sidebar_current: "docs-config-syntax" -description: |- - The Terraform language has its own syntax, intended to combine declarative - structure with expressions in a way that is easy for humans to read and - understand. ---- - -# Configuration Syntax - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Syntax](../configuration-0-11/syntax.html). - -Other pages in this section have described various configuration constructs -that can appear in the Terraform language. This page describes the lower-level -syntax of the language in more detail, revealing the building blocks that -those constructs are built from. - -This page describes the _native syntax_ of the Terraform language, which is -a rich language designed to be easy for humans to read and write. The -constructs in the Terraform language can also be expressed in -[JSON syntax](./syntax-json.html), which is harder for humans -to read and edit but easier to generate and parse programmatically. - -This low-level syntax of the Terraform language is defined in terms of a -syntax called _HCL_, which is also used by configuration languages in -other applications, and in particular other HashiCorp products. -It is not necessary to know all of the details of HCL syntax in -order to use Terraform, and so this page summarizes the most important -details. If you are interested, you can find a full definition of HCL -syntax in -[the HCL native syntax specification](https://github.com/hashicorp/hcl/blob/hcl2/hclsyntax/spec.md). - -## Arguments and Blocks - -The Terraform language syntax is built around two key syntax constructs: -arguments and blocks. - -### Arguments - -An _argument_ assigns a value to a particular name: - -```hcl -image_id = "abc123" -``` - -The identifier before the equals sign is the _argument name_, and the expression -after the equals sign is the argument's value. - -The context where the argument appears determines what value types are valid -(for example, each resource type has a schema that defines the types of its -arguments), but many arguments accept arbitrary -[expressions](./expressions.html), which allow the value to -either be specified literally or generated from other values programmatically. - --> **Note:** Terraform's configuration language is based on a more general -language called HCL, and HCL's documentation usually uses the word "attribute" -instead of "argument." These words are similar enough to be interchangeable in -this context, and experienced Terraform users might use either term in casual -conversation. But because Terraform also interacts with several _other_ things -called "attributes" (in particular, Terraform resources have attributes like -`id` that can be referenced from expressions but can't be assigned values in -configuration), we've chosen to use "argument" in the Terraform documentation -when referring to this syntax construct. - -### Blocks - -A _block_ is a container for other content: - -```hcl -resource "aws_instance" "example" { - ami = "abc123" - - network_interface { - # ... - } -} -``` - -A block has a _type_ (`resource` in this example). Each block type defines -how many _labels_ must follow the type keyword. The `resource` block type -expects two labels, which are `aws_instance` and `example` in the example above. -A particular block type may have any number of required labels, or it may -require none as with the nested `network_interface` block type. - -After the block type keyword and any labels, the block _body_ is delimited -by the `{` and `}` characters. Within the block body, further arguments -and blocks may be nested, creating a hierarchy of blocks and their associated -arguments. - -The Terraform language uses a limited number of _top-level block types,_ which -are blocks that can appear outside of any other block in a configuration file. -Most of Terraform's features (including resources, input variables, output -values, data sources, etc.) are implemented as top-level blocks. - -## Identifiers - -Argument names, block type names, and the names of most Terraform-specific -constructs like resources, input variables, etc. are all _identifiers_. - -Identifiers can contain letters, digits, underscores (`_`), and hyphens (`-`). -The first character of an identifier must not be a digit, to avoid ambiguity -with literal numbers. - -For complete identifier rules, Terraform implements -[the Unicode identifier syntax](http://unicode.org/reports/tr31/), extended to -include the ASCII hyphen character `-`. - -## Comments - -The Terraform language supports three different syntaxes for comments: - -* `#` begins a single-line comment, ending at the end of the line. -* `//` also begins a single-line comment, as an alternative to `#`. -* `/*` and `*/` are start and end delimiters for a comment that might span - over multiple lines. - -The `#` single-line comment style is the default comment style and should be -used in most cases. Automatic configuration formatting tools may automatically -transform `//` comments into `#` comments, since the double-slash style is -not idiomatic. - -## Character Encoding and Line Endings - -Terraform configuration files must always be UTF-8 encoded. While the -delimiters of the language are all ASCII characters, Terraform accepts -non-ASCII characters in identifiers, comments, and string values. - -Terraform accepts configuration files with either Unix-style line endings -(LF only) or Windows-style line endings (CR then LF), but the idiomatic style -is to use the Unix convention, and so automatic configuration formatting tools -may automatically transform CRLF endings to LF. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/terraform-enterprise.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/terraform-enterprise.html.md deleted file mode 100644 index b0ed7634..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/terraform-enterprise.html.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -layout: "docs" -page_title: "Configuring Terraform Push" -sidebar_current: "docs-config-push" -description: |- - Terraform's push command was a way to interact with the legacy version of Terraform Enterprise. It is not supported in the current version of Terraform Enterprise. ---- - -# Terraform Push Configuration - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Configuring Terraform Push](../configuration-0-11/terraform-enterprise.html). - -Prior to v0.12, Terraform included mechanisms to interact with a legacy version -of Terraform Enterprise, formerly known as "Atlas". - -These features relied on a special configuration block named `atlas`: - -```hcl -atlas { - name = "acme-corp/production" -} -``` - -These features are no longer available on Terraform Enterprise and so the -corresponding configuration elements and commands have been removed in -Terraform v0.12. - -After upgrading to the current version of Terraform Enterprise, -any `atlas` blocks in your configuration can be safely removed. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/terraform.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/terraform.html.md deleted file mode 100644 index 4367710b..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/terraform.html.md +++ /dev/null @@ -1,132 +0,0 @@ ---- -layout: "docs" -page_title: "Terraform Settings - Configuration Language" -sidebar_current: "docs-config-terraform" -description: |- - The "terraform" configuration section is used to configure some behaviors - of Terraform itself. ---- - -# Terraform Settings - --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Terraform Settings](../configuration-0-11/terraform.html). - -The special `terraform` configuration block type is used to configure some -behaviors of Terraform itself, such as requiring a minimum Terraform version to -apply your configuration. - -## Terraform Block Syntax - -Terraform settings are gathered together into `terraform` blocks: - -```hcl -terraform { - # ... -} -``` - -Each `terraform` block can contain a number of settings related to Terraform's -behavior. Within a `terraform` block, only constant values can be used; -arguments may not refer to named objects such as resources, input variables, -etc, and may not use any of the Terraform language built-in functions. - -The various options supported within a `terraform` block are described in the -following sections. - -## Configuring a Terraform Backend - -The nested `backend` block configures which backend Terraform should use. - -The syntax and behavior of the `backend` block is described in [Backend -Configuration](./backend.html). - -## Specifying a Required Terraform Version - -The `required_version` setting accepts a [version constraint -string,](./version-constraints.html) which specifies which versions of Terraform -can be used with your configuration. - -If the running version of Terraform doesn't match the constraints specified, -Terraform will produce an error and exit without taking any further actions. - -When you use [child modules](./modules.html), each module can specify its own -version requirements. The requirements of all modules in the tree must be -satisfied. - -Use Terraform version constraints in a collaborative environment to -ensure that everyone is using a specific Terraform version, or using at least -a minimum Terraform version that has behavior expected by the configuration. - -The `required_version` setting applies only to the version of Terraform CLI. -Terraform's resource types are implemented by provider plugins, -whose release cycles are independent of Terraform CLI and of each other. -Use [the `required_providers` block](./provider-requirements.html) to manage -the expected versions for each provider you use. - -## Specifying Provider Requirements - -[inpage-source]: #specifying-provider-requirements - -The `required_providers` block specifies all of the providers required by the -current module, mapping each local provider name to a source address and a -version constraint. - -```hcl -terraform { - required_providers { - aws = { - version = ">= 2.7.0" - source = "hashicorp/aws" - } - } -} -``` - -For more information, see [Provider Requirements](./provider-requirements.html). - -## Experimental Language Features - -The Terraform team will sometimes introduce new language features initially via -an opt-in experiment, so that the community can try the new feature and give -feedback on it prior to it becoming a backward-compatibility constraint. - -In releases where experimental features are available, you can enable them on -a per-module basis by setting the `experiments` argument inside a `terraform` -block: - -```hcl -terraform { - experiments = [example] -} -``` - -The above would opt in to an experiment named `example`, assuming such an -experiment were available in the current Terraform version. - -Experiments are subject to arbitrary changes in later releases and, depending on -the outcome of the experiment, may change drastically before final release or -may not be released in stable form at all. Such breaking changes may appear -even in minor and patch releases. We do not recommend using experimental -features in Terraform modules intended for production use. - -In order to make that explicit and to avoid module callers inadvertently -depending on an experimental feature, any module with experiments enabled will -generate a warning on every `terraform plan` or `terraform apply`. If you -want to try experimental features in a shared module, we recommend enabling the -experiment only in alpha or beta releases of the module. - -The introduction and completion of experiments is reported in -[Terraform's changelog](https://github.com/hashicorp/terraform/blob/master/CHANGELOG.md), -so you can watch the release notes there to discover which experiment keywords, -if any, are available in a particular Terraform release. - -## Passing Metadata to Providers - -The `terraform` block can have a nested `provider_meta` block for each -provider a module is using, if the provider defines a schema for it. This -allows the provider to receive module-specific information, and is primarily -intended for modules distributed by the same vendor as the associated provider. - -For more information, see [Provider Metadata](/docs/internals/provider-meta.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/types.html.md b/vendor/github.com/hashicorp/terraform/website/docs/configuration/types.html.md deleted file mode 100644 index 2c514b1f..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/types.html.md +++ /dev/null @@ -1,303 +0,0 @@ ---- -layout: "docs" -page_title: "Type Constraints - Configuration Language" -sidebar_current: "docs-config-types" -description: |- - Terraform module authors and provider developers can use detailed type - constraints to validate the inputs of their modules and resources. ---- - -# Type Constraints - --> **Note:** This page is about Terraform 0.12 and later, and documents a -feature that did not exist in older versions. For other information about -Terraform 0.11 and earlier, see -[0.11 Configuration Language](../configuration-0-11/index.html). - -Terraform module authors and provider developers can use detailed type -constraints to validate user-provided values for their input variables and -resource arguments. This requires some additional knowledge about Terraform's -type system, but allows you to build a more resilient user interface for your -modules and resources. - -## Type Keywords and Constructors - -Type constraints are expressed using a mixture of _type keywords_ and -function-like constructs called _type constructors._ - -* Type keywords are unquoted symbols that represent a static type. -* Type constructors are unquoted symbols followed by a pair of - parentheses, which contain an argument that specifies more information about - the type. Without its argument, a type constructor does not fully - represent a type; instead, it represents a _kind_ of similar types. - -Type constraints look like other kinds of Terraform -[expressions](./expressions.html), but are a special syntax. Within the -Terraform language, they are only valid in the `type` argument of an -[input variable](./variables.html). - -## Primitive Types - -A _primitive_ type is a simple type that isn't made from any other types. All -primitive types in Terraform are represented by a type keyword. The available -primitive types are: - -* `string`: a sequence of Unicode characters representing some text, such - as `"hello"`. -* `number`: a numeric value. The `number` type can represent both whole - numbers like `15` and fractional values such as `6.283185`. -* `bool`: either `true` or `false`. `bool` values can be used in conditional - logic. - -### Conversion of Primitive Types - -The Terraform language will automatically convert `number` and `bool` values -to `string` values when needed, and vice-versa as long as the string contains -a valid representation of a number or boolean value. - -* `true` converts to `"true"`, and vice-versa -* `false` converts to `"false"`, and vice-versa -* `15` converts to `"15"`, and vice-versa - -## Complex Types - -A _complex_ type is a type that groups multiple values into a single value. -Complex types are represented by type constructors, but several of them -also have shorthand keyword versions. - -There are two categories of complex types: collection types (for grouping -similar values), and structural types (for grouping potentially dissimilar -values). - -### Collection Types - -A _collection_ type allows multiple values of _one_ other type to be grouped -together as a single value. The type of value _within_ a collection is called -its _element type._ All collection types must have an element type, which is -provided as the argument to their constructor. - -For example, the type `list(string)` means "list of strings", which is a -different type than `list(number)`, a list of numbers. All elements of a -collection must always be of the same type. - -The three kinds of collection type in the Terraform language are: - -* `list(...)`: a sequence of values identified by consecutive whole numbers - starting with zero. - - The keyword `list` is a shorthand for `list(any)`, which accepts any - element type as long as every element is the same type. This is for - compatibility with older configurations; for new code, we recommend using - the full form. -* `map(...)`: a collection of values where each is identified by a string label. - - The keyword `map` is a shorthand for `map(any)`, which accepts any - element type as long as every element is the same type. This is for - compatibility with older configurations; for new code, we recommend using - the full form. - - Maps can be made with braces ({}) and colons (:) or equals signs (=): - { "foo": "bar", "bar": "baz" } OR { foo = "bar", bar = "baz" }. Quotes - may be omitted on keys, unless the key starts with a number, in which - case quotes are required. Commas are required between key/value pairs - for single line maps. A newline between key/value pairs is sufficient - in multi-line maps. - - Note: although colons are valid delimiters between keys and values, - they are currently ignored by `terraform fmt` (whereas `terraform fmt` - will attempt vertically align equals signs). -* `set(...)`: a collection of unique values that do not have any secondary - identifiers or ordering. - -### Structural Types - -A _structural_ type allows multiple values of _several distinct types_ to be -grouped together as a single value. Structural types require a _schema_ as an -argument, to specify which types are allowed for which elements. - -The two kinds of structural type in the Terraform language are: - -* `object(...)`: a collection of named attributes that each have their own type. - - The schema for object types is `{ = , = , ... }` — a - pair of curly braces containing a comma-separated series of ` = ` - pairs. Values that match the object type must contain _all_ of the specified - keys, and the value for each key must match its specified type. (Values with - _additional_ keys can still match an object type, but the extra attributes - are discarded during type conversion.) -* `tuple(...)`: a sequence of elements identified by consecutive whole - numbers starting with zero, where each element has its own type. - - The schema for tuple types is `[, , ...]` — a pair of square - brackets containing a comma-separated series of types. Values that match the - tuple type must have _exactly_ the same number of elements (no more and no - fewer), and the value in each position must match the specified type for - that position. - -For example: an object type of `object({ name=string, age=number })` would match -a value like the following: - -```hcl -{ - name = "John" - age = 52 -} -``` - -Also, an object type of `object({ id=string, cidr_block=string })` would match -the object produced by a reference to an `aws_vpc` resource, like -`aws_vpc.example_vpc`; although the resource has additional attributes, they -would be discarded during type conversion. - -Finally, a tuple type of `tuple([string, number, bool])` would match a value -like the following: - -```hcl -["a", 15, true] -``` - -### Complex Type Literals - -The Terraform language has literal expressions for creating tuple and object -values, which are described in -[Expressions: Literal Expressions](./expressions.html#literal-expressions) as -"list/tuple" literals and "map/object" literals, respectively. - -Terraform does _not_ provide any way to directly represent lists, maps, or sets. -However, due to the automatic conversion of complex types (described below), the -difference between similar complex types is almost never relevant to a normal -user, and most of the Terraform documentation conflates lists with tuples and -maps with objects. The distinctions are only useful when restricting input -values for a module or resource. - -### Conversion of Complex Types - -Similar kinds of complex types (list/tuple/set and map/object) can usually be -used interchangeably within the Terraform language, and most of Terraform's -documentation glosses over the differences between the kinds of complex type. -This is due to two conversion behaviors: - -* Whenever possible, Terraform converts values between similar kinds of complex - types if the provided value is not the exact type requested. "Similar kinds" - is defined as follows: - * Objects and maps are similar. - * A map (or a larger object) can be converted to an object if it has - _at least_ the keys required by the object schema. Any additional - attributes are discarded during conversion, which means map -> object - -> map conversions can be lossy. - * Tuples and lists are similar. - * A list can only be converted to a tuple if it has _exactly_ the - required number of elements. - * Sets are _almost_ similar to both tuples and lists: - * When a list or tuple is converted to a set, duplicate values are - discarded and the ordering of elements is lost. - * When a `set` is converted to a list or tuple, the elements will be - in an arbitrary order. If the set's elements were strings, they will - be in lexicographical order; sets of other element types do not - guarantee any particular order of elements. -* Whenever possible, Terraform converts _element values_ within a complex type, - either by converting complex-typed elements recursively or as described above - in [Conversion of Primitive Types](#conversion-of-primitive-types). - -For example: if a module argument requires a value of type `list(string)` and a -user provides the tuple `["a", 15, true]`, Terraform will internally transform -the value to `["a", "15", "true"]` by converting the elements to the required -`string` element type. Later, if the module uses those elements to set different -resource arguments that require a string, a number, and a bool (respectively), -Terraform will automatically convert the second and third strings back to the -required types at that time, since they contain valid representations of a -number and a bool. - -On the other hand, automatic conversion will fail if the provided value -(including any of its element values) is incompatible with the required type. If -an argument requires a type of `map(string)` and a user provides the object -`{name = ["Kristy", "Claudia", "Mary Anne", "Stacey"], age = 12}`, Terraform -will raise a type mismatch error, since a tuple cannot be converted to a string. - -## Dynamic Types: The "any" Constraint - -The keyword `any` is a special construct that serves as a placeholder for a -type yet to be decided. `any` is not _itself_ a type: when interpreting a -value against a type constraint containing `any`, Terraform will attempt to -find a single actual type that could replace the `any` keyword to produce -a valid result. - -For example, given the type constraint `list(any)`, Terraform will examine -the given value and try to choose a replacement for the `any` that would -make the result valid. - -If the given value were `["a", "b", "c"]` -- whose physical type is -`tuple([string, string, string])`, Terraform analyzes this as follows: - -* Tuple types and list types are _similar_ per the previous section, so the - tuple-to-list conversion rule applies. -* All of the elements in the tuple are strings, so the type constraint - `string` would be valid for all of the list elements. -* Therefore in this case the `any` argument is replaced with `string`, - and the final concrete value type is `list(string)`. - -All of the elements of a collection must have the same type, so conversion -to `list(any)` requires that all of the given elements must be convertible -to a common type. This implies some other behaviors that result from the -conversion rules described in earlier sections. - -* If the given value were instead `["a", 1, "b"]` then Terraform would still - select `list(string)`, because of the primitive type conversion rules, and - the resulting value would be `["a", "1", "b"]` due to the string conversion - implied by that type constraint. -* If the given value were instead `["a", [], "b"]` then the value cannot - conform to the type constraint: there is no single type that both a string - and an empty tuple can convert to. Terraform would reject this value, - complaining that all elements must have the same type. - -Although the above examples use `list(any)`, a similar principle applies to -`map(any)` and `set(any)`. - -If you wish to apply absolutely no constraint to the given value, the `any` -keyword can be used in isolation: - -```hcl -variable "no_type_constraint" { - type = any -} -``` - -In this case, Terraform will replace `any` with the exact type of the given -value and thus perform no type conversion whatsoever. - -## Experimental: Optional Object Type Attributes - -From Terraform v0.14 there is _experimental_ support for marking particular -attributes as optional in an object type constraint. - -To mark an attribute as optional, use the additional `optional(...)` modifier -around its type declaration: - -```hcl -variable "with_optional_attribute" { - type = object({ - a = string # a required attribute - b = optional(string) # an optional attribute - }) -} -``` - -By default, for required attributes, Terraform will return an error if the -source value has no matching attribute. Marking an attribute as optional -changes the behavior in that situation: Terraform will instead just silently -insert `null` as the value of the attribute, allowing the recieving module -to describe an appropriate fallback behavior. - -Because this feature is currently experimental, it requires an explicit -opt-in on a per-module basis. To use it, write a `terraform` block with the -`experiments` argument set as follows: - -```hcl -terraform { - experiments = [module_variable_optional_attrs] -} -``` - -Until the experiment is concluded, the behavior of this feature may see -breaking changes even in minor releases. We recommend using this feature -only in prerelease versions of modules as long as it remains experimental. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/import/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/import/index.html.md deleted file mode 100644 index 4be535bb..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/import/index.html.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -layout: "docs" -page_title: "Import" -sidebar_current: "docs-import" -description: |- - Terraform is able to import existing infrastructure. This allows you take - resources you've created by some other means and bring it under Terraform - management. ---- - -# Import - -> **Hands-on:** Try the [Import Terraform Configuration](https://learn.hashicorp.com/tutorials/terraform/state-import?in=terraform/state&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. - -Terraform is able to import existing infrastructure. This allows you take -resources you've created by some other means and bring it under Terraform -management. - -This is a great way to slowly transition infrastructure to Terraform, or -to be able to be confident that you can use Terraform in the future if it -potentially doesn't support every feature you need today. - -~> Warning: Terraform expects that each remote object it is managing will be -bound to only one resource address, which is normally guaranteed by Terraform -itself having created all objects. If you import existing objects into Terraform, -be careful to import each remote object to only one Terraform resource address. -If you import the same object multiple times, Terraform may exhibit unwanted -behavior. For more information on this assumption, see -[the State section](/docs/state/). - -## Currently State Only - -The current implementation of Terraform import can only import resources -into the [state](/docs/state). It does not generate configuration. A future -version of Terraform will also generate configuration. - -Because of this, prior to running `terraform import` it is necessary to write -manually a `resource` configuration block for the resource, to which the -imported object will be mapped. - -While this may seem tedious, it still gives Terraform users an avenue for -importing existing resources. - -## Remote Backends - -When using Terraform import on the command line with a [remote -backend](/docs/backends/types/remote.html), such as Terraform Cloud, the import -command runs locally, unlike commands such as apply, which run inside your -Terraform Cloud environment. Because of this, the import command will not have -access to information from the remote backend, such as workspace variables. - -In order to use Terraform import with a remote state backend, you may need to -set local variables equivalent to the remote workspace variables. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/internals/credentials-helpers.html.md b/vendor/github.com/hashicorp/terraform/website/docs/internals/credentials-helpers.html.md index c7572484..8e8b71aa 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/internals/credentials-helpers.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/internals/credentials-helpers.html.md @@ -12,7 +12,7 @@ For Terraform-specific features that interact with remote network services, such as [module registries](/docs/registry/) and [remote operations](/docs/cloud/run/cli.html), Terraform by default looks for API credentials to use in these calls in -[the CLI configuration](/docs/commands/cli-config.html). +[the CLI configuration](/docs/cli/config/config-file.html). Credentials helpers offer an alternative approach that allows you to customize how Terraform obtains credentials using an external program, which can then @@ -20,7 +20,7 @@ directly access an existing secrets management system in your organization. This page is about how to write and install a credentials helper. To learn how to configure a credentials helper that was already installed, see -[the CLI config Credentials Helpers section](/docs/commands/cli-config.html#credentials-helpers). +[the CLI config Credentials Helpers section](/docs/cli/config/config-file.html#credentials-helpers). ## How Terraform finds Credentials Helpers @@ -57,7 +57,7 @@ The current set of verbs are: To represent credentials, the credentials helper protocol uses a JSON object whose contents correspond with the contents of -[`credentials` blocks in the CLI configuration](/docs/commands/cli-config.html#credentials). +[`credentials` blocks in the CLI configuration](/docs/cli/config/config-file.html#credentials). To represent an API token, the object contains a property called "token" whose value is the token string: diff --git a/vendor/github.com/hashicorp/terraform/website/docs/internals/debugging.html.md b/vendor/github.com/hashicorp/terraform/website/docs/internals/debugging.html.md index 01c96474..5262c62b 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/internals/debugging.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/internals/debugging.html.md @@ -8,9 +8,20 @@ description: |- # Debugging Terraform +> **Hands-on:** Try the [Create Dynamic Expressions](https://learn.hashicorp.com/tutorials/terraform/troubleshooting-workflow#bug-reporting-best-practices?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + + Terraform has detailed logs which can be enabled by setting the `TF_LOG` environment variable to any value. This will cause detailed logs to appear on stderr. -You can set `TF_LOG` to one of the log levels `TRACE`, `DEBUG`, `INFO`, `WARN` or `ERROR` to change the verbosity of the logs. `TRACE` is the most verbose and it is the default if `TF_LOG` is set to something other than a log level name. +You can set `TF_LOG` to one of the log levels `TRACE`, `DEBUG`, `INFO`, `WARN` or `ERROR` to change the verbosity of the logs. + +Setting `TF_LOG` to `JSON` outputs logs at the `TRACE` level or higher, and uses a parseable JSON encoding as the formatting. + +~> **Warning:** The JSON encoding of log files is not considered a stable interface. It may change at any time, without warning. It is meant to support tooling that will be forthcoming, and that tooling is the only supported way to interact with JSON formatted logs. + +Logging can be enabled separately for terraform itself and the provider plugins +using the `TF_LOG_CORE` or `TF_LOG_PROVIDER` environment variables. These take +the same level arguments as `TF_LOG`, but only activate a subset of the logs. To persist logged output you can set `TF_LOG_PATH` in order to force the log to always be appended to a specific file when logging is enabled. Note that even when `TF_LOG_PATH` is set, `TF_LOG` must be set in order for any logging to be enabled. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/internals/graph.html.md b/vendor/github.com/hashicorp/terraform/website/docs/internals/graph.html.md index e2de628f..56ec892f 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/internals/graph.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/internals/graph.html.md @@ -108,8 +108,8 @@ The amount of parallelism is limited using a semaphore to prevent too many concurrent operations from overwhelming the resources of the machine running Terraform. By default, up to 10 nodes in the graph will be processed concurrently. This number can be set using the `-parallelism` flag on the -[plan](/docs/commands/plan.html), [apply](/docs/commands/apply.html), and -[destroy](/docs/commands/destroy.html) commands. +[plan](/docs/cli/commands/plan.html), [apply](/docs/cli/commands/apply.html), and +[destroy](/docs/cli/commands/destroy.html) commands. Setting `-parallelism` is considered an advanced operation and should not be necessary for normal usage of Terraform. It may be helpful in certain special diff --git a/vendor/github.com/hashicorp/terraform/website/docs/internals/internal-plugins.html.md b/vendor/github.com/hashicorp/terraform/website/docs/internals/internal-plugins.html.md deleted file mode 100644 index 5ed5ae8d..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/internals/internal-plugins.html.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -layout: "docs" -page_title: "Internal Plugins" -sidebar_current: "docs-internals-plugins" -description: |- - Terraform includes many popular plugins compiled into the main binary. ---- - -# Internal Plugins - -Terraform providers and provisioners are provided via plugins. Each plugin provides an implementation for a specific service, such as AWS, or provisioner, such as bash. Plugins are executed as a separate process and communicate with the main Terraform binary over an RPC interface. - -# Upgrading From Versions Earlier Than 0.7 - -In versions of Terraform prior to 0.7, each plugin shipped as a separate binary. In versions of Terraform >= 0.7, all of the official plugins are shipped as a single binary. This saves a lot of disk space and makes downloads faster for you! - -However, when you upgrade you will need to manually delete old plugins from disk. You can do this via something like this, depending on where you installed `terraform`: - - rm /usr/local/bin/terraform-* - -If you don't do this you will see an error message like the following: - -```text -[WARN] /usr/local/bin/terraform-provisioner-file overrides an internal plugin for file-provisioner. - If you did not expect to see this message you will need to remove the old plugin. - See https://www.terraform.io/docs/internals/plugins.html -Error configuring: 2 error(s) occurred: - -* Unrecognized remote plugin message: 2|unix|/var/folders/pj/66q7ztvd17v_vgfg8c99gm1m0000gn/T/tf-plugin604337945 - -This usually means that the plugin is either invalid or simply -needs to be recompiled to support the latest protocol. -* Unrecognized remote plugin message: 2|unix|/var/folders/pj/66q7ztvd17v_vgfg8c99gm1m0000gn/T/tf-plugin647987867 - -This usually means that the plugin is either invalid or simply -needs to be recompiled to support the latest protocol. -``` - -## Why Does This Happen? - -In previous versions of Terraform all of the plugins were included in a zip file. For example, when you upgraded from 0.6.12 to 0.6.15, the newer version of each plugin file would have replaced the older one on disk, and you would have ended up with the latest version of each plugin. - -Going forward there is only one file in the distribution so you will need to perform a one-time cleanup when upgrading from Terraform < 0.7 to Terraform 0.7 or higher. - -If you're curious about the low-level details, keep reading! - -## Go Plugin Architecture - -Terraform is written in the Go programming language. One of Go's interesting properties is that it produces statically-compiled binaries. This means that it does not need to find libraries on your computer to run, and in general only needs to be compatible with your operating system (to make system calls) and with your CPU architecture (so the assembly instructions match the CPU you're running on). - -Another property of Go is that it does not support dynamic libraries. It _only_ supports static binaries. This is part of Go's overall design and is the reason why it produces statically-compiled binaries in the first place -- once you have a Go binary for your platform it should _Just Work_. - -In other languages, plugins are built using dynamic libraries. Since this is not an option for us in Go we use a network RPC interface instead. This means that each plugin is an independent program, and instead of communicating via shared memory, the main process communicates with the plugin process over HTTP. When you start Terraform, it identifies the plugin you want to use, finds it on disk, runs the other binary, and does some handshaking to make sure they can talk to each other (the error you may see after upgrading is a handshake failure in the RPC code). - -### Downsides - -There is a significant downside to this approach. Statically compiled binaries are much larger than dynamically-linked binaries because they include everything they need to run. And because Terraform shares a lot of code with its plugins, there is a lot of binary data duplicated between each of these programs. - -In Terraform 0.6.15 there were 42 programs in total, using around 750MB on disk. And it turns out that about 600MB of this is duplicate data! This uses up a lot of space on your hard drive and a lot of bandwidth on our CDN. Fortunately, there is a way to resolve this problem. - -### Our Solution - -In Terraform 0.7 we merged all of the programs into the same binary. We do this by using a special command `terraform internal-plugin` which allows us to invoke a plugin just by calling the same Terraform binary with extra arguments. In essence, Terraform now just calls itself in order to activate the special behavior in each plugin. - -### Supporting our Community - -> Why would you do this? Why not just eliminate the network RPC interface and simplify everything? - -Terraform is an open source project with a large community, and while we maintain a wide range of plugins as part of the core distribution, we also want to make it easy for people anywhere to write and use their own plugins. - -By using the network RPC interface, you can build and distribute a plugin for Terraform without having to rebuild Terraform itself. This makes it easy for you to build a Terraform plugin for your organization's internal use, for a proprietary API that you don't want to open source, or to prototype something before contributing it back to the main project. - -In theory, because the plugin interface is HTTP, you could even develop a plugin using a completely different programming language! (Disclaimer, you would also have to re-implement the plugin API which is not a trivial amount of work.) - -So to conclude, with the RPC interface _and_ internal plugins, we get the best of all of these features: Binaries that _Just Work_, savings from shared code, and extensibility through plugins. We hope you enjoy using these features in Terraform. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/internals/json-format.html.md b/vendor/github.com/hashicorp/terraform/website/docs/internals/json-format.html.md index 4c345220..7741cd08 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/internals/json-format.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/internals/json-format.html.md @@ -14,7 +14,7 @@ When Terraform plans to make changes, it prints a human-readable summary to the Since the format of plan files isn't suited for use with external tools (and likely never will be), Terraform can output a machine-readable JSON representation of a plan file's changes. It can also convert state files to the same format, to simplify data loading and provide better long-term compatibility. -Use `terraform show -json ` to generate a JSON representation of a plan or state file. See [the `terraform show` documentation](/docs/commands/show.html) for more details. +Use `terraform show -json ` to generate a JSON representation of a plan or state file. See [the `terraform show` documentation](/docs/cli/commands/show.html) for more details. -> **Note:** The output includes a `format_version` key, which currently has major version zero to indicate that the format is experimental and subject to change. A future version will assign a non-zero major version and make stronger promises about compatibility. We do not anticipate any significant breaking changes to the format before its first major version, however. @@ -56,7 +56,7 @@ The extra wrapping object here will allow for any extension we may need to add i A plan consists of a prior state, the configuration that is being applied to that state, and the set of changes Terraform plans to make to achieve that. -For ease of consumption by callers, the plan representation includes a partial representation of the values in the final state (using a [value representation](#value-representation)), allowing callers to easily analyze the planned outcome using similar code as for analyzing the prior state. +For ease of consumption by callers, the plan representation includes a partial representation of the values in the final state (using a [value representation](#values-representation)), allowing callers to easily analyze the planned outcome using similar code as for analyzing the prior state. ```javascript { @@ -122,7 +122,31 @@ For ease of consumption by callers, the plan representation includes a partial r // "change" describes the change that will be made to the indicated // object. The is detailed in a section below. - "change": + "change": , + + // "action_reason" is some optional extra context about why the + // actions given inside "change" were selected. This is the JSON + // equivalent of annotations shown in the normal plan output like + // "is tainted, so must be replaced" as opposed to just "must be + // replaced". + // + // These reason codes are display hints only and the set of possible + // hints may change over time. Users of this must be prepared to + // encounter unrecognized reasons and treat them as unspecified reasons. + // + // The current set of possible values is: + // - "replace_because_tainted": the object in question is marked as + // "tainted" in the prior state, so Terraform planned to replace it. + // - "replace_because_cannot_update": the provider indicated that one + // of the requested changes isn't possible without replacing the + // existing object with a new object. + // - "replace_by_request": the user explicitly called for this object + // to be replaced as an option when creating the plan, which therefore + // overrode what would have been a "no-op" or "update" action otherwise. + // + // If there is no special reason to note, Terraform will omit this + // property altogether. + action_reason: "replace_because_tainted" } ], @@ -244,7 +268,7 @@ The following example illustrates the structure of a ``: } ``` -The translation of attribute and output values is the same intuitive mapping from HCL types to JSON types used by Terraform's [`jsonencode`](/docs/configuration/functions/jsonencode.html) function. This mapping does lose some information: lists, sets, and tuples all lower to JSON arrays while maps and objects both lower to JSON objects. Unknown values and null values are both treated as absent or null. +The translation of attribute and output values is the same intuitive mapping from HCL types to JSON types used by Terraform's [`jsonencode`](/docs/language/functions/jsonencode.html) function. This mapping does lose some information: lists, sets, and tuples all lower to JSON arrays while maps and objects both lower to JSON objects. Unknown values and null values are both treated as absent or null. Only the "current" object for each resource instance is described. "Deposed" objects are not reflected in this structure at all; in plan representations, you can refer to the change representations for further details. @@ -466,7 +490,7 @@ A `` describes the change that will be made to the indica // e.g. just scan the list for "delete" to recognize all three situations // where the object will be deleted, allowing for any new deletion // combinations that might be added in future. - "actions": ["update"] + "actions": ["update"], // "before" and "after" are representations of the object value both before // and after the action. For ["create"] and ["delete"] actions, either @@ -474,6 +498,35 @@ A `` describes the change that will be made to the indica // after values are identical. The "after" value will be incomplete if there // are values within it that won't be known until after apply. "before": , - "after": + "after": , + + // "after_unknown" is an object value with similar structure to "after", but + // with all unknown leaf values replaced with "true", and all known leaf + // values omitted. This can be combined with "after" to reconstruct a full + // value after the action, including values which will only be known after + // apply. + "after_unknown": { + "id": true + }, + + // "before_sensitive" and "after_sensitive" are object values with similar + // structure to "before" and "after", but with all sensitive leaf values + // replaced with true, and all non-sensitive leaf values omitted. These + // objects should be combined with "before" and "after" to prevent accidental + // display of sensitive values in user interfaces. + "before_sensitive": {}, + "after_sensitive": { + "triggers": { + "boop": true + } + }, + + // "replace_paths" is an array of arrays representing a set of paths into the + // object value which resulted in the action being "replace". This will be + // omitted if the action is not replace, or if no paths caused the + // replacement (for example, if the resource was tainted). Each path + // consists of one or more steps, each of which will be a number or a + // string. + "replace_paths": [["triggers"]] } ``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/internals/login-protocol.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/internals/login-protocol.html.markdown index 560a471b..59e3a39a 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/internals/login-protocol.html.markdown +++ b/vendor/github.com/hashicorp/terraform/website/docs/internals/login-protocol.html.markdown @@ -9,7 +9,7 @@ description: |- # Server-side Login Protocol ~> **Note:** You don't need to read these docs to _use_ -[`terraform login`](/docs/commands/login.html). The information below is for +[`terraform login`](/docs/cli/commands/login.html). The information below is for anyone intending to implement the server side of `terraform login` in order to offer Terraform-native services in a third-party system. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/internals/module-registry-protocol.html.md b/vendor/github.com/hashicorp/terraform/website/docs/internals/module-registry-protocol.html.md index 0369f112..4da94721 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/internals/module-registry-protocol.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/internals/module-registry-protocol.html.md @@ -10,7 +10,7 @@ description: |- # Module Registry Protocol --> Third-party provider registries are supported only in Terraform CLI 0.11 and later. Prior versions do not support this protocol. +-> Third-party module registries are supported only in Terraform CLI 0.11 and later. Prior versions do not support this protocol. The module registry protocol is what Terraform CLI uses to discover metadata about modules available for installation and to locate the distribution @@ -34,23 +34,25 @@ Terraform CLI itself does not use them. Each Terraform module has an associated address. A module address has the syntax `hostname/namespace/name/system`, where: -* `hostname` is the hostname of the provider registry that serves this module. +* `hostname` is the hostname of the module registry that serves this module. * `namespace` is the name of a namespace, unique on a particular hostname, that can contain one or more modules that are somehow related. On the public Terraform Registry the "namespace" represents the organization that is packaging and distributing the module. * `name` is the module name, which generally names the abstraction that the module is intending to create. -* `system` is the name of a system that the module is primarily written to - target. For multi-cloud abstractions, there can be multiple modules with - addresses that differ only in "system" to reflect system-specific +* `system` is the name of a remote system that the module is primarily written + to target. For multi-cloud abstractions, there can be multiple modules with + addresses that differ only in "system" to reflect provider-specific implementations of the abstraction, like `registry.terraform.io/hashicorp/consul/aws` vs. `registry.terraform.io/hashicorp/consul/azurerm`. The system name commonly - matches the type portion of the address of an official provider, but that - is not required. + matches the type portion of the address of an official provider, like `aws` + or `azurerm` in the above examples, but that is not required and so you can + use whichever system keywords make sense for the organization of your + particular registry. -The `hostname/` portion of a provider address (including its slash delimiter) +The `hostname/` portion of a module address (including its slash delimiter) is optional, and if omitted defaults to `registry.terraform.io/`. For example: @@ -61,12 +63,12 @@ For example: * `example.com/awesomecorp/consul/happycloud` is a hypothetical module published on a third-party registry. -If you intend only to share a module you've developed for use by all -Terraform users, please consider publishing it into the public -[Terraform Registry](https://registry.terraform.io/), which will make your -module discoverable. You only need to implement this module registry -protocol if you wish to publish modules whose addresses include a different -hostname that is under your control. +If you intend to share a module you've developed for use by all Terraform +users, please consider publishing it into the public +[Terraform Registry](https://registry.terraform.io/) to make your module more +discoverable. You only need to implement this module registry protocol if you +wish to publish modules whose addresses include a different hostname that is +under your control. ## Module Versions @@ -80,7 +82,7 @@ blocks have the same source address. ## Service Discovery -The providers protocol begins with Terraform CLI using +The module registry protocol begins with Terraform CLI using [Terraform's remote service discovery protocol](./remote-service-discovery.html), with the hostname in the module address acting as the "User-facing Hostname". @@ -123,7 +125,7 @@ available versions for a given fully-qualified module. | Method | Path | Produces | | ------ | ------------------------------------- | -------------------------- | -| `GET` | `:namespace/:name/:provider/versions` | `application/json` | +| `GET` | `:namespace/:name/:provider/versions` | `application/json` | ### Parameters @@ -177,7 +179,7 @@ This endpoint downloads the specified version of a module for a single provider. | Method | Path | Produces | | ------ | ------------------------------------------------------ | -------------------------- | -| `GET` | `:namespace/:name/:provider/:system/:version/download` | `application/json` | +| `GET` | `:namespace/:name/:system/:version/download` | `application/json` | ### Parameters @@ -187,10 +189,7 @@ This endpoint downloads the specified version of a module for a single provider. - `name` `(string: )` - The name of the module. This is required and is specified as part of the URL path. -- `provider` `(string: )` - The name of the provider. - This is required and is specified as part of the URL path. - -- `system` `(string: )` - The name of the target system. +- `provider` `(string: )` - The name of the target system. This is required and is specified as part of the URL path. - `version` `(string: )` - The version of the module. @@ -214,10 +213,10 @@ A successful response has no body, and includes the location from which the module version's source can be downloaded in the `X-Terraform-Get` header. The value of this header accepts the same values as the `source` argument in a `module` block in Terraform configuration, as described in -[Module Sources](https://www.terraform.io/docs/modules/sources.html), +[Module Sources](https://www.terraform.io/docs/language/modules/sources.html), except that it may not recursively refer to another module registry address. The value of `X-Terraform-Get` may instead be a relative URL, indicated by beginning with `/`, `./` or `../`, in which case it is resolved relative to the full URL of the download endpoint to produce -[an HTTP URL module source](/docs/modules/sources.html#http-urls). +[an HTTP URL module source](/docs/language/modules/sources.html#http-urls). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/internals/provider-network-mirror-protocol.html.md b/vendor/github.com/hashicorp/terraform/website/docs/internals/provider-network-mirror-protocol.html.md index 3ad4895b..9520f829 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/internals/provider-network-mirror-protocol.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/internals/provider-network-mirror-protocol.html.md @@ -18,7 +18,7 @@ implement to provide an alternative installation source for Terraform providers, regardless of their origin registries. Terraform uses network mirrors only if you activate them explicitly in -[the CLI configuration's `provider_installation` block](/docs/commands/cli-config.html#provider-installation). +[the CLI configuration's `provider_installation` block](/docs/cli/config/config-file.html#provider-installation). When enabled, a network mirror can serve providers belonging to any registry hostname, which can allow an organization to serve all of the Terraform providers they intend to use from an internal server, rather than from each @@ -36,7 +36,7 @@ instead. Each Terraform provider has an associated address which uniquely identifies it within Terraform. A provider address has the syntax `hostname/namespace/type`, which is described in more detail in -[the Provider Requirements documentation](/docs/configuration/provider-requirements.html). +[the Provider Requirements documentation](/docs/language/providers/requirements.html). By default, the `hostname` portion of a provider address serves both as part of its unique identifier _and_ as the location of the registry to retrieve it @@ -96,7 +96,7 @@ base URL from the above CLI configuration example. ### Authentication If the CLI configuration includes -[credentials](/docs/commands/cli-config.html#credentials) for the hostname +[credentials](/docs/cli/config/config-file.html#credentials) for the hostname given in the network mirror base URL, Terraform will include those credentials in its requests for operations described below. @@ -264,7 +264,7 @@ in the appropriate nested subdirectories, and ensure that your system is configured to serve `.json` files with the `application/json` media type. As a convenience, Terraform CLI includes -[the `terraform providers mirror` subcommand](https://www.terraform.io/docs/commands/providers/mirror.html), +[the `terraform providers mirror` subcommand](https://www.terraform.io/docs/cli/commands/providers/mirror.html), which will analyze the current configuration for the providers it requires, download the packages for those providers from their origin registries, and place them into a local directory suitable for use as a mirror. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/internals/provider-registry-protocol.html.md b/vendor/github.com/hashicorp/terraform/website/docs/internals/provider-registry-protocol.html.md index b81dead3..8dd4c543 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/internals/provider-registry-protocol.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/internals/provider-registry-protocol.html.md @@ -42,7 +42,7 @@ where: * `hostname` is the registry host that the provider is considered to have originated from, and the default location Terraform will consult for information about the provider - [unless overridden in the CLI configuration](/docs/commands/cli-config.html#provider-installation). + [unless overridden in the CLI configuration](/docs/cli/config/config-file.html#provider-installation). * `namespace` is the name of a namespace, unique on a particular hostname, that can contain one or more providers that are somehow related. On the public Terraform Registry the "namespace" represents the organization that is @@ -78,7 +78,7 @@ to see it as an entirely separate provider that will _not_ be usable by modules that declare a dependency on `hashicorp/azurerm`. If your goal is to create an alternative local distribution source for an existing provider -- that is, a _mirror_ of the provider -- refer to -[the provider installation method configuration](/docs/commands/cli-config.html#provider-installation) +[the provider installation method configuration](/docs/cli/config/config-file.html#provider-installation) instead. ## Provider Versions diff --git a/vendor/github.com/hashicorp/terraform/website/docs/internals/remote-service-discovery.html.md b/vendor/github.com/hashicorp/terraform/website/docs/internals/remote-service-discovery.html.md index 5a2ecb33..f82617e7 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/internals/remote-service-discovery.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/internals/remote-service-discovery.html.md @@ -85,14 +85,14 @@ version 1 of the module registry protocol: At present, the following service identifiers are in use: -* `login.v1`: [login protocol version 1](/docs/commands/login.html#protocol-v1) +* `login.v1`: [login protocol version 1](/docs/cli/commands/login.html) * `modules.v1`: [module registry API version 1](module-registry-protocol.html) * `providers.v1`: [provider registry API version 1](provider-registry-protocol.html) ## Authentication If credentials for the given hostname are available in -[the CLI config](/docs/commands/cli-config.html) then they will be included +[the CLI config](/docs/cli/config/config-file.html) then they will be included in the request for the discovery document. The credentials may also be provided to endpoints declared in the discovery diff --git a/vendor/github.com/hashicorp/terraform/website/docs/internals/resource-addressing.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/internals/resource-addressing.html.markdown deleted file mode 100644 index 51ff80c9..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/internals/resource-addressing.html.markdown +++ /dev/null @@ -1,127 +0,0 @@ ---- -layout: "docs" -page_title: "Internals: Resource Address" -sidebar_current: "docs-internals-resource-addressing" -description: |- - Resource addressing is used to target specific resources in a larger - infrastructure. ---- - -# Resource Addressing - -A __Resource Address__ is a string that references a specific resource in a -larger infrastructure. An address is made up of two parts: - -``` -[module path][resource spec] -``` - -## Module path - -A module path addresses a module within the tree of modules. It takes the form: - -``` -module.module_name[module index] -``` - - * `module` - Module keyword indicating a child module (non-root). Multiple `module` - keywords in a path indicate nesting. - * `module_name` - User-defined name of the module. - * `[module index]` - (Optional) [Index](#index-values-for-modules-and-resources) into a - module with multiple instances, surrounded by square brace characters (`[` and `]`). - -An address without a resource spec, i.e. `module.foo` applies to every resource within -the module if a single module, or all instances of a module if a module has multiple instances. -To address all resources of a particular module instance, include the module index in the address, -such as `module.foo[0]`. - -If the module path is omitted, the address applies to the root module. - -An example of the `module` keyword delineating between two modules that have multiple instances: - -``` -module.foo[0].module.bar["a"] -``` - --> Module index only applies to modules in Terraform v0.13 or later, as in earlier -versions of Terraform, a module could not have multiple instances. - -## Resource spec - -A resource spec addresses a specific resource in the config. It takes the form: - -``` -resource_type.resource_name[resource index] -``` - - * `resource_type` - Type of the resource being addressed. - * `resource_name` - User-defined name of the resource. - * `[resource index]` - (Optional) [Index](#index-values-for-modules-and-resources) - into a resource with multiple instances, surrounded by square brace characters (`[` and `]`). - --> In Terraform v0.12 and later, a resource spec without a module path prefix -matches only resources in the root module. In earlier versions, a resource spec -without a module path prefix will match resources with the same type and name -in any descendent module. - -## Index values for Modules and Resources - -The following specifications apply to index values on modules and resources with multiple instances: - - * `[N]` where `N` is a `0`-based numerical index into a resource with multiple - instances specified by the `count` meta-argument. Omitting an index when - addressing a resource where `count > 1` means that the address references - all instances. - * `["INDEX"]` where `INDEX` is a alphanumerical key index into a resource with - multiple instances specified by the `for_each` meta-argument. - -## Examples - -### count Example - -Given a Terraform config that includes: - -```hcl -resource "aws_instance" "web" { - # ... - count = 4 -} -``` - -An address like this: - -``` -aws_instance.web[3] -``` - -Refers to only the last instance in the config, and an address like this: - -``` -aws_instance.web -``` - -Refers to all four "web" instances. - -### for_each Example - -Given a Terraform config that includes: - -```hcl -resource "aws_instance" "web" { - # ... - for_each = { - "terraform": "value1", - "resource": "value2", - "indexing": "value3", - "example": "value4", - } -} -``` - -An address like this: - -``` -aws_instance.web["example"] -``` - -Refers to only the "example" instance in the config. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/attr-as-blocks.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/attr-as-blocks.html.md similarity index 93% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/attr-as-blocks.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/attr-as-blocks.html.md index 5a2dd37a..ad7759a2 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/attr-as-blocks.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/attr-as-blocks.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Attributes as Blocks - Configuration Language" sidebar_current: "docs-config-attr-as-blocks" description: |- @@ -25,14 +25,14 @@ is set to an empty list (` = []`). Most users do not need to know any further details of this "nested block or empty list" behavior. However, read further if you need to: -- Use Terraform's [JSON syntax](/docs/configuration/syntax-json.html) with this +- Use Terraform's [JSON syntax](/docs/language/syntax/json.html) with this type of resource. - Create a reusable module that wraps this type of resource. ## Details In Terraform v0.12 and later, the language makes a distinction between -[argument syntax and nested block syntax](/docs/configuration/syntax.html#arguments-and-blocks) +[argument syntax and nested block syntax](/docs/language/syntax/configuration.html#arguments-and-blocks) within blocks: * Argument syntax sets a named argument for the containing object. If the @@ -46,7 +46,7 @@ within blocks: merging in with any explicitly-defined arguments. The distinction between these is particularly important for -[JSON syntax](/docs/configuration/syntax-json.html) +[JSON syntax](/docs/language/syntax/json.html) because the same primitive JSON constructs (lists and objects) will be interpreted differently depending on whether a particular name is an argument or a nested block type. @@ -153,7 +153,7 @@ example = [ For the arguments that use the attributes-as-blocks usage mode, the above is a better pattern than using -[`dynamic` blocks](/docs/configuration/expressions.html#dynamic-blocks) +[`dynamic` blocks](/docs/language/expressions/dynamic-blocks.html) because the case where the caller provides an empty list will result in explicitly assigning an empty list value, rather than assigning no value at all and thus retaining and @@ -163,7 +163,7 @@ dynamically-generating _normal_ nested blocks, though. ## In JSON syntax Arguments that use this special mode are specified in JSON syntax always using -the [JSON expression mapping](/docs/configuration/syntax-json.html#expression-mapping) +the [JSON expression mapping](/docs/language/syntax/json.html#expression-mapping) to produce a list of objects. The interpretation of these values in JSON syntax is, therefore, equivalent diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/data-sources/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/data-sources/index.html.md new file mode 100644 index 00000000..589dfa3c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/data-sources/index.html.md @@ -0,0 +1,231 @@ +--- +layout: "language" +page_title: "Data Sources - Configuration Language" +sidebar_current: "docs-config-data-sources" +description: |- + Data sources allow data to be fetched or computed for use elsewhere in Terraform configuration. +--- + +# Data Sources + +> **Hands-on:** Try the [Query data sources](https://learn.hashicorp.com/tutorials/terraform/data-sources?in=terraform/configuration-language&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +_Data sources_ allow data to be fetched or computed for use elsewhere +in Terraform configuration. Use of data sources allows a Terraform +configuration to make use of information defined outside of Terraform, +or defined by another separate Terraform configuration. + +Each [provider](/docs/language/providers/index.html) may offer data sources +alongside its set of [resource](/docs/language/resources/index.html) +types. + +## Using Data Sources + +A data source is accessed via a special kind of resource known as a +_data resource_, declared using a `data` block: + +```hcl +data "aws_ami" "example" { + most_recent = true + + owners = ["self"] + tags = { + Name = "app-server" + Tested = "true" + } +} +``` + +A `data` block requests that Terraform read from a given data source ("aws_ami") +and export the result under the given local name ("example"). The name is used +to refer to this resource from elsewhere in the same Terraform module, but has +no significance outside of the scope of a module. + +The data source and name together serve as an identifier for a given +resource and so must be unique within a module. + +Within the block body (between `{` and `}`) are query constraints defined by +the data source. Most arguments in this section depend on the +data source, and indeed in this example `most_recent`, `owners` and `tags` are +all arguments defined specifically for [the `aws_ami` data source](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami). + +When distinguishing from data resources, the primary kind of resource (as declared +by a `resource` block) is known as a _managed resource_. Both kinds of resources +take arguments and export attributes for use in configuration, but while +managed resources cause Terraform to create, update, and delete infrastructure +objects, data resources cause Terraform only to _read_ objects. For brevity, +managed resources are often referred to just as "resources" when the meaning +is clear from context. + +## Data Source Arguments + +Each data resource is associated with a single data source, which determines +the kind of object (or objects) it reads and what query constraint arguments +are available. + +Each data source in turn belongs to a [provider](/docs/language/providers/index.html), +which is a plugin for Terraform that offers a collection of resource types and +data sources that most often belong to a single cloud or on-premises +infrastructure platform. + +Most of the items within the body of a `data` block are defined by and +specific to the selected data source, and these arguments can make full +use of [expressions](/docs/language/expressions/index.html) and other dynamic +Terraform language features. + +However, there are some "meta-arguments" that are defined by Terraform itself +and apply across all data sources. These arguments often have additional +restrictions on what language features can be used with them, and are described +in more detail in the following sections. + +## Data Resource Behavior + +If the query constraint arguments for a data resource refer only to constant +values or values that are already known, the data resource will be read and its +state updated during Terraform's "refresh" phase, which runs prior to creating a plan. +This ensures that the retrieved data is available for use during planning and +so Terraform's plan will show the actual values obtained. + +Query constraint arguments may refer to values that cannot be determined until +after configuration is applied, such as the id of a managed resource that has +not been created yet. In this case, reading from the data source is deferred +until the apply phase, and any references to the results of the data resource +elsewhere in configuration will themselves be unknown until after the +configuration has been applied. + +## Local-only Data Sources + +While many data sources correspond to an infrastructure object type that +is accessed via a remote network API, some specialized data sources operate +only within Terraform itself, calculating some results and exposing them +for use elsewhere. + +For example, local-only data sources exist for +[rendering templates](https://registry.terraform.io/providers/hashicorp/template/latest/docs/data-sources/file), +[reading local files](https://registry.terraform.io/providers/hashicorp/local/latest/docs/data-sources/file), and +[rendering AWS IAM policies](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document). + +The behavior of local-only data sources is the same as all other data +sources, but their result data exists only temporarily during a Terraform +operation, and is re-calculated each time a new plan is created. + +## Data Resource Dependencies + +Data resources have the same dependency resolution behavior +[as defined for managed resources](/docs/language/resources/behavior.html#resource-dependencies). +Setting the `depends_on` meta-argument within `data` blocks defers reading of +the data source until after all changes to the dependencies have been applied. + +In order to ensure that data sources are accessing the most up to date +information possible in a wide variety of use cases, arguments directly +referencing managed resources are treated the same as if the resource was +listed in `depends_on`. This behavior can be avoided when desired by indirectly +referencing the managed resource values through a `local` value. + +~> **NOTE:** **In Terraform 0.12 and earlier**, due to the data resource behavior of deferring the read until the apply phase when depending on values that are not yet known, using `depends_on` with `data` resources will force the read to always be deferred to the apply phase, and therefore a configuration that uses `depends_on` with a `data` resource can never converge. Due to this behavior, we do not recommend using `depends_on` with data resources. + + +## Multiple Resource Instances + +Data resources support [`count`](/docs/language/meta-arguments/count.html) +and [`for_each`](/docs/language/meta-arguments/for_each.html) +meta-arguments as defined for managed resources, with the same syntax and behavior. + +As with managed resources, when `count` or `for_each` is present it is important to +distinguish the resource itself from the multiple resource _instances_ it +creates. Each instance will separately read from its data source with its +own variant of the constraint arguments, producing an indexed result. + +## Selecting a Non-default Provider Configuration + +Data resources support [the `provider` meta-argument](/docs/language/meta-arguments/resource-provider.html) +as defined for managed resources, with the same syntax and behavior. + +## Lifecycle Customizations + +Data resources do not currently have any customization settings available +for their lifecycle, but the `lifecycle` nested block is reserved in case +any are added in future versions. + +## Example + +A data source configuration looks like the following: + +```hcl +# Find the latest available AMI that is tagged with Component = web +data "aws_ami" "web" { + filter { + name = "state" + values = ["available"] + } + + filter { + name = "tag:Component" + values = ["web"] + } + + most_recent = true +} +``` + +## Description + +The `data` block creates a data instance of the given _type_ (first +block label) and _name_ (second block label). The combination of the type +and name must be unique. + +Within the block (the `{ }`) is configuration for the data instance. The +configuration is dependent on the type, and is documented for each +data source in the [providers section](/docs/providers/index.html). + +Each data instance will export one or more attributes, which can be +used in other resources as reference expressions of the form +`data...`. For example: + +```hcl +resource "aws_instance" "web" { + ami = data.aws_ami.web.id + instance_type = "t1.micro" +} +``` + +## Meta-Arguments + +As data sources are essentially a read only subset of resources, they also +support the same [meta-arguments](/docs/language/resources/syntax.html#meta-arguments) of resources +with the exception of the +[`lifecycle` configuration block](/docs/language/meta-arguments/lifecycle.html). + +### Non-Default Provider Configurations + +Similarly to [resources](/docs/language/resources/index.html), when +a module has multiple configurations for the same provider you can specify which +configuration to use with the `provider` meta-argument: + +```hcl +data "aws_ami" "web" { + provider = aws.west + + # ... +} +``` + +See +[The Resource `provider` Meta-Argument](/docs/language/meta-arguments/resource-provider.html) +for more information. + +## Data Source Lifecycle + +If the arguments of a data instance contain no references to computed values, +such as attributes of resources that have not yet been created, then the +data instance will be read and its state updated during Terraform's "refresh" +phase, which by default runs prior to creating a plan. This ensures that the +retrieved data is available for use during planning and the diff will show +the real values obtained. + +Data instance arguments may refer to computed values, in which case the +attributes of the instance itself cannot be resolved until all of its +arguments are defined. In this case, refreshing the data instance will be +deferred until the "apply" phase, and all interpolations of the data instance +attributes will show as "computed" in the plan since the values are not yet +known. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/dependency-lock.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/dependency-lock.html.md similarity index 83% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/dependency-lock.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/dependency-lock.html.md index fc7a1413..2a3eda96 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/dependency-lock.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/dependency-lock.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Dependency Lock File (.terraform.lock.hcl) - Configuration Language" --- @@ -9,14 +9,16 @@ page_title: "Dependency Lock File (.terraform.lock.hcl) - Configuration Language versions of Terraform did not track dependency selections at all, so the information here is not relevant to those versions. +> **Hands-on:** Try the [Lock and Upgrade Provider Versions](https://learn.hashicorp.com/tutorials/terraform/provider-versioning?in=terraform/configuration-language&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + A Terraform configuration may refer to two different kinds of external dependency that come from outside of its own codebase: -* [Providers](./provider-requirements.html), which are plugins for Terraform +* [Providers](/docs/language/providers/requirements.html), which are plugins for Terraform that extend it with support for interacting with various external systems. -* [Modules](./modules.html), which allow splitting out groups of Terraform - configuration constructs (written in the Terraform language) into reusable - abstractions. +* [Modules](/docs/language/modules/index.html), which allow + splitting out groups of Terraform configuration constructs (written in the + Terraform language) into reusable abstractions. Both of these dependency types can be published and updated independently from Terraform itself and from the configurations that depend on them. For that @@ -24,7 +26,7 @@ reason, Terraform must determine which versions of those dependencies are potentially compatible with the current configuration and which versions are currently selected for use. -[Version constraints](./version-constraints.html) within the configuration +[Version constraints](/docs/language/expressions/version-constraints.html) within the configuration itself determine which versions of dependencies are _potentially_ compatible, but after selecting a specific version of each dependency Terraform remembers the decisions it made in a _dependency lock file_ so that it can (by default) @@ -49,7 +51,7 @@ to signify that it is a lock file for various items that Terraform caches in the `.terraform` subdirectory of your working directory. Terraform automatically creates or updates the dependency lock file each time -you run [the `terraform init` command](/docs/commands/init.html). You should +you run [the `terraform init` command](/docs/cli/commands/init.html). You should include this file in your version control repository so that you can discuss potential changes to your external dependencies via code review, just as you would discuss potential changes to your configuration itself. @@ -121,12 +123,12 @@ There are two special considerations with the "trust on first use" model: your current platform _and_ any other packages that might be available for other platforms. - In this case, the `terraform init` output will include the fingerprint of - the key that signed the checksums, with a message like - `(signed by a HashiCorp partner, key ID DC9FC6B1FCE47986)`. You may wish to - confirm that you trust the holder of the given key before committing the - lock file containing the signed checksums, or to retrieve and verify the - full set of available packages for the given provider version. + In this case, the `terraform init` output will include the fingerprint of + the key that signed the checksums, with a message like + `(signed by a HashiCorp partner, key ID DC9FC6B1FCE47986)`. You may wish to + confirm that you trust the holder of the given key before committing the + lock file containing the signed checksums, or to retrieve and verify the + full set of available packages for the given provider version. * If you install a provider for the first time using an alternative installation method, such as a filesystem or network mirror, Terraform will @@ -135,12 +137,12 @@ There are two special considerations with the "trust on first use" model: for other platforms and so the configuration will not be usable on any other platform. - To avoid this problem you can pre-populate checksums for a variety of - different platforms in your lock file using - [the `terraform providers lock` command](/docs/commands/providers/lock.html), - which will then allow future calls to `terraform init` to verify that the - packages available in your chosen mirror match the official packages from - the provider's origin registry. + To avoid this problem you can pre-populate checksums for a variety of + different platforms in your lock file using + [the `terraform providers lock` command](/docs/cli/commands/providers/lock.html), + which will then allow future calls to `terraform init` to verify that the + packages available in your chosen mirror match the official packages from + the provider's origin registry. ## Understanding Lock File Changes @@ -150,12 +152,12 @@ your version control system may show you that the file has changed. There are a few different types of changes that Terraform can potentially make to your lock file, which you may need to understand in order to review the -propsed changes. The following sections will describe these common situations. +proposed changes. The following sections will describe these common situations. ### Dependency on a new provider If you add a new entry to the -[provider requirements](./provider-requirements.html) for any module in your +[provider requirements](/docs/language/providers/requirements.html) for any module in your configuration, or if you add an external module that includes a new provider dependency itself, `terraform init` will respond to that by selecting the newest version of that provider which meets all of the version constraints @@ -168,7 +170,7 @@ block in the dependency lock file. @@ -6,6 +6,26 @@ ] } - + +provider "registry.terraform.io/hashicorp/azurerm" { + version = "2.30.0" + constraints = "~> 2.12" @@ -219,7 +221,7 @@ block to reflect that change. +++ .terraform.lock.hcl 2020-10-07 16:43:42.785665945 -0700 @@ -7,22 +7,22 @@ } - + provider "registry.terraform.io/hashicorp/azurerm" { - version = "2.1.0" - constraints = "~> 2.1.0" @@ -298,27 +300,27 @@ The two hashing schemes currently supported are: part of the Terraform provider registry protocol and is therefore used for providers that you install directly from an origin registry. - This hashing scheme captures a SHA256 hash of each of the official `.zip` - packages indexed in the origin registry. This is an effective scheme for - verifying the official release packages when installed from a registry, but - it's not suitable for verifying packages that come from other - [provider installation methods](/docs/commands/cli-config.html#provider-installation), - such as filesystem mirrors using the unpacked directory layout. + This hashing scheme captures a SHA256 hash of each of the official `.zip` + packages indexed in the origin registry. This is an effective scheme for + verifying the official release packages when installed from a registry, but + it's not suitable for verifying packages that come from other + [provider installation methods](/docs/cli/config/config-file.html#provider-installation), + such as filesystem mirrors using the unpacked directory layout. * `h1:`: a mnemonic for "hash scheme 1", which is the current preferred hashing scheme. - Hash scheme 1 is also a SHA256 hash, but is one computed from the _contents_ - of the provider distribution package, rather than of the `.zip` archive - it's contained within. This scheme therefore has the advantage that it can - be calculated for an official `.zip` file, an unpacked directory with the - same contents, or a recompressed `.zip` file which contains the same files - but potentially different metadata or compression schemes. + Hash scheme 1 is also a SHA256 hash, but is one computed from the _contents_ + of the provider distribution package, rather than of the `.zip` archive + it's contained within. This scheme therefore has the advantage that it can + be calculated for an official `.zip` file, an unpacked directory with the + same contents, or a recompressed `.zip` file which contains the same files + but potentially different metadata or compression schemes. - Due to the limited scope of the `zh:` scheme, Terraform will - opportunistically add in the corresponding `h1:` checksums as it learns - of them, which is what caused the addition of a second `h1:` checksum - in the example change shown above. + Due to the limited scope of the `zh:` scheme, Terraform will + opportunistically add in the corresponding `h1:` checksums as it learns + of them, which is what caused the addition of a second `h1:` checksum + in the example change shown above. Terraform will add a new hash to an existing provider only if the hash is calculated from a package that _also_ matches one of the existing hashes. In @@ -343,7 +345,7 @@ your configuration on new target platforms, or if you are installing providers from a mirror that therefore can't provide official signed checksums, you can ask Terraform to pre-populate hashes for a chosen set of platforms using -[the `terraform providers lock` command](/docs/commands/providers/lock.html): +[the `terraform providers lock` command](/docs/cli/commands/providers/lock.html): ``` terraform providers lock \ diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/conditionals.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/conditionals.html.md new file mode 100644 index 00000000..138e2b0e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/conditionals.html.md @@ -0,0 +1,68 @@ +--- +layout: "language" +page_title: "Conditional Expressions - Configuration Language" +--- + +# Conditional Expressions + +> **Hands-on:** Try the [Create Dynamic Expressions](https://learn.hashicorp.com/tutorials/terraform/expressions?in=terraform/configuration-language&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +A _conditional expression_ uses the value of a bool expression to select one of +two values. + +The syntax of a conditional expression is as follows: + +```hcl +condition ? true_val : false_val +``` + +If `condition` is `true` then the result is `true_val`. If `condition` is +`false` then the result is `false_val`. + +A common use of conditional expressions is to define defaults to replace +invalid values: + +``` +var.a != "" ? var.a : "default-a" +``` + +If `var.a` is an empty string then the result is `"default-a"`, but otherwise +it is the actual value of `var.a`. + +## Conditions + +The condition can be any expression that resolves to a boolean value. This will +usually be an expression that uses the equality, comparison, or logical +operators. + +## Result Types + +The two result values may be of any type, but they must both +be of the _same_ type so that Terraform can determine what type the whole +conditional expression will return without knowing the condition value. + +If the two result expressions don't produce the same type then Terraform will +attempt to find a type that they can both convert to, and make those +conversions automatically if so. + +For example, the following expression is valid and will always return a string, +because in Terraform all numbers can convert automatically to a string using +decimal digits: + +```hcl +var.example ? 12 : "hello" +``` + +Relying on this automatic conversion behavior can be confusing for those who +are not familiar with Terraform's conversion rules though, so we recommend +being explicit using type conversion functions in any situation where there may +be some uncertainty about the expected result type. + +The following example is contrived because it would be easier to write the +constant `"12"` instead of the type conversion in this case, but shows how to +use [`tostring`](/docs/language/functions/tostring.html) to explicitly convert a number to +a string. + +```hcl +var.example ? tostring(12) : "hello" +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/dynamic-blocks.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/dynamic-blocks.html.md new file mode 100644 index 00000000..83551060 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/dynamic-blocks.html.md @@ -0,0 +1,154 @@ +--- +layout: "language" +page_title: "Dynamic Blocks - Configuration Language" +--- + + +# `dynamic` Blocks + +Within top-level block constructs like resources, expressions can usually be +used only when assigning a value to an argument using the `name = expression` +form. This covers many uses, but some resource types include repeatable _nested +blocks_ in their arguments, which typically represent separate objects that +are related to (or embedded within) the containing object: + +```hcl +resource "aws_elastic_beanstalk_environment" "tfenvtest" { + name = "tf-test-name" # can use expressions here + + setting { + # but the "setting" block is always a literal block + } +} +``` + +You can dynamically construct repeatable nested blocks like `setting` using a +special `dynamic` block type, which is supported inside `resource`, `data`, +`provider`, and `provisioner` blocks: + +```hcl +resource "aws_elastic_beanstalk_environment" "tfenvtest" { + name = "tf-test-name" + application = "${aws_elastic_beanstalk_application.tftest.name}" + solution_stack_name = "64bit Amazon Linux 2018.03 v2.11.4 running Go 1.12.6" + + dynamic "setting" { + for_each = var.settings + content { + namespace = setting.value["namespace"] + name = setting.value["name"] + value = setting.value["value"] + } + } +} +``` + +A `dynamic` block acts much like a [`for` expression](for.html), but produces +nested blocks instead of a complex typed value. It iterates over a given +complex value, and generates a nested block for each element of that complex +value. + +- The label of the dynamic block (`"setting"` in the example above) specifies + what kind of nested block to generate. +- The `for_each` argument provides the complex value to iterate over. +- The `iterator` argument (optional) sets the name of a temporary variable + that represents the current element of the complex value. If omitted, the name + of the variable defaults to the label of the `dynamic` block (`"setting"` in + the example above). +- The `labels` argument (optional) is a list of strings that specifies the block + labels, in order, to use for each generated block. You can use the temporary + iterator variable in this value. +- The nested `content` block defines the body of each generated block. You can + use the temporary iterator variable inside this block. + +Since the `for_each` argument accepts any collection or structural value, +you can use a `for` expression or splat expression to transform an existing +collection. + +The iterator object (`setting` in the example above) has two attributes: + +* `key` is the map key or list element index for the current element. If the + `for_each` expression produces a _set_ value then `key` is identical to + `value` and should not be used. +* `value` is the value of the current element. + +A `dynamic` block can only generate arguments that belong to the resource type, +data source, provider or provisioner being configured. It is _not_ possible +to generate meta-argument blocks such as `lifecycle` and `provisioner` +blocks, since Terraform must process these before it is safe to evaluate +expressions. + +The `for_each` value must be a collection with one element per desired +nested block. If you need to declare resource instances based on a nested +data structure or combinations of elements from multiple data structures you +can use Terraform expressions and functions to derive a suitable value. +For some common examples of such situations, see the +[`flatten`](/docs/language/functions/flatten.html) +and +[`setproduct`](/docs/language/functions/setproduct.html) +functions. + +## Multi-level Nested Block Structures + +Some providers define resource types that include multiple levels of blocks +nested inside one another. You can generate these nested structures dynamically +when necessary by nesting `dynamic` blocks in the `content` portion of other +`dynamic` blocks. + +For example, a module might accept a complex data structure like the following: + +```hcl +variable "load_balancer_origin_groups" { + type = map(object({ + origins = set(object({ + hostname = string + })) + })) +} +``` + +If you were defining a resource whose type expects a block for each origin +group and then nested blocks for each origin within a group, you could ask +Terraform to generate that dynamically using the following nested `dynamic` +blocks: + +```hcl + dynamic "origin_group" { + for_each = var.load_balancer_origin_groups + content { + name = origin_group.key + + dynamic "origin" { + for_each = origin_group.value.origins + content { + hostname = origin.value.hostname + } + } + } + } +``` + +When using nested `dynamic` blocks it's particularly important to pay attention +to the iterator symbol for each block. In the above example, +`origin_group.value` refers to the current element of the outer block, while +`origin.value` refers to the current element of the inner block. + +If a particular resource type defines nested blocks that have the same type +name as one of their parents, you can use the `iterator` argument in each of +`dynamic` blocks to choose a different iterator symbol that makes the two +easier to distinguish. + +## Best Practices for `dynamic` Blocks + +Overuse of `dynamic` blocks can make configuration hard to read and maintain, so +we recommend using them only when you need to hide details in order to build a +clean user interface for a re-usable module. Always write nested blocks out +literally where possible. + +If you find yourself defining most or all of a `resource` block's arguments and +nested blocks using directly-corresponding attributes from an input variable +then that might suggest that your module is not creating a useful abstraction. +It may be better for the calling module to define the resource itself then +pass information about it into your module. For more information on this design +tradeoff, see [When to Write a Module](/docs/language/modules/develop/index.html#when-to-write-a-module) +and [Module Composition](/docs/language/modules/develop/composition.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/for.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/for.html.md new file mode 100644 index 00000000..77ba0a3c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/for.html.md @@ -0,0 +1,209 @@ +--- +layout: "language" +page_title: "For Expressions - Configuration Language" +--- + +# `for` Expressions + +A _`for` expression_ creates a complex type value by transforming +another complex type value. Each element in the input value +can correspond to either one or zero values in the result, and an arbitrary +expression can be used to transform each input element into an output element. + +For example, if `var.list` were a list of strings, then the following expression +would produce a tuple of strings with all-uppercase letters: + +```hcl +[for s in var.list : upper(s)] +``` + +This `for` expression iterates over each element of `var.list`, and then +evaluates the expression `upper(s)` with `s` set to each respective element. +It then builds a new tuple value with all of the results of executing that +expression in the same order. + +## Input Types + +A `for` expression's input (given after the `in` keyword) can be a list, +a set, a tuple, a map, or an object. + +The above example showed a `for` expression with only a single temporary +symbol `s`, but a `for` expression can optionally declare a pair of temporary +symbols in order to use the key or index of each item too: + +```hcl +[for k, v in var.map : length(k) + length(v)] +``` + +For a map or object type, like above, the `k` symbol refers to the key or +attribute name of the current element. You can also use the two-symbol form +with lists and tuples, in which case the additional symbol is the index +of each element starting from zero, which conventionally has the symbol name +`i` or `idx` unless it's helpful to choose a more specific name: + +```hcl +[for i, v in var.list : "${i} is ${v}"] +``` + +The index or key symbol is always optional. If you specify only a single +symbol after the `for` keyword then that symbol will always represent the +_value_ of each element of the input collection. + +## Result Types + +The type of brackets around the `for` expression decide what type of result +it produces. + +The above example uses `[` and `]`, which produces a tuple. If you use `{` and +`}` instead, the result is an object and you must provide two result +expressions that are separated by the `=>` symbol: + +```hcl +{for s in var.list : s => upper(s)} +``` + +This expression produces an object whose attributes are the original elements +from `var.list` and their corresponding values are the uppercase versions. +For example, the resulting value might be as follows: + +```hcl +{ + foo = "FOO" + bar = "BAR" + baz = "BAZ" +} +``` + +A `for` expression alone can only produce either an object value or a tuple +value, but Terraform's automatic type conversion rules mean that you can +typically use the results in locations where lists, maps, and sets are expected. + +## Filtering Elements + +A `for` expression can also include an optional `if` clause to filter elements +from the source collection, producing a value with fewer elements than +the source value: + +``` +[for s in var.list : upper(s) if s != ""] +``` + +One common reason for filtering collections in `for` expressions is to split +a single source collection into two separate collections based on some +criteria. For example, if the input `var.users` is a map of objects where the +objects each have an attribute `is_admin` then you may wish to produce separate +maps with admin vs non-admin objects: + +```hcl +variable "users" { + type = map(object({ + is_admin = boolean + })) +} + +locals { + admin_users = { + for name, user in var.users : name => user + if user.is_admin + } + regular_users = { + for name, user in var.users : name => user + if !user.is_admin + } +} +``` + +## Element Ordering + +Because `for` expressions can convert from unordered types (maps, objects, sets) +to unordered types (lists, tuples), Terraform must choose an implied ordering +for the elements of an unordered collection. + +For maps and objects, Terraform sorts the elements by key or attribute name, +using lexical sorting. + +For sets of strings, Terraform sorts the elements by their value, using +lexical sorting. + +For sets of other types, Terraform uses an arbitrary ordering that may change +in future versions of Terraform. For that reason, we recommend converting the +result of such an expression to itself be a set so that it's clear elsewhere +in the configuration that the result is unordered. You can use +[the `toset` function](/docs/language/functions/toset.html) +to concisely convert a `for` expression result to be of a set type. + +```hcl +toset([for e in var.set : e.example]) +``` + +## Grouping Results + +If the result type is an object (using `{` and `}` delimiters) then normally +the given key expression must be unique across all elements in the result, +or Terraform will return an error. + +Sometimes the resulting keys are _not_ unique, and so to support that situation +Terraform supports a special _grouping mode_ which changes the result to support +multiple elements per key. + +To activate grouping mode, add the symbol `...` after the value expression. +For example: + +```hcl +variable "users" { + type = map(object({ + role = string + })) +} + +locals { + users_by_role = { + for name, user in var.users : user.role => name... + } +} +``` + +The above represents a situation where a module expects a map describing +various users who each have a single "role", where the map keys are usernames. +The usernames are guaranteed unique because they are map keys in the input, +but many users may all share a single role name. + +The `local.users_by_role` expression inverts the input map so that the keys +are the role names and the values are usernames, but the expression is in +grouping mode (due to the `...` after `name`) and so the result will be a +map of lists of strings, such as the following: + +```hcl +{ + "admin": [ + "ps", + ], + "maintainer": [ + "am", + "jb", + "kl", + "ma", + ], + "viewer": [ + "st", + "zq", + ], +} +``` + +Due to [the element ordering rules](#element-ordering), Terraform will sort +the users lexically by username as part of evaluating the `for` expression, +and so the usernames associated with each role will be lexically sorted +after grouping. + +## Repeated Configuration Blocks + +The `for` expressions mechanism is for constructing collection values from +other collection values within expressions, which you can then assign to +individual resource arguments that expect complex values. + +Some resource types also define _nested block types_, which typically represent +separate objects that belong to the containing resource in some way. You can't +dynamically generated nested blocks using `for` expressions, but you _can_ +generate nested blocks for a resource dynamically using +[`dynamic` blocks](dynamic-blocks.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/function-calls.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/function-calls.html.md new file mode 100644 index 00000000..26e23035 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/function-calls.html.md @@ -0,0 +1,112 @@ +--- +layout: "language" +page_title: "Function Calls - Configuration Language" +--- + +# Function Calls + +> **Hands-on:** Try the [Perform Dynamic Operations with Functions](https://learn.hashicorp.com/tutorials/terraform/functions?in=terraform/configuration-language&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +The Terraform language has a number of +[built-in functions](/docs/language/functions/index.html) that can be used +in expressions to transform and combine values. These +are similar to the operators but all follow a common syntax: + +```hcl +(, ) +``` + +The function name specifies which function to call. Each defined function +expects a specific number of arguments with specific value types, and returns a +specific value type as a result. + +Some functions take an arbitrary number of arguments. For example, the `min` +function takes any amount of number arguments and returns the one that is +numerically smallest: + +```hcl +min(55, 3453, 2) +``` + +A function call expression evaluates to the function's return value. + +## Available Functions + +For a full list of available functions, see +[the function reference](/docs/language/functions/index.html). + +## Expanding Function Arguments + +If the arguments to pass to a function are available in a list or tuple value, +that value can be _expanded_ into separate arguments. Provide the list value as +an argument and follow it with the `...` symbol: + +```hcl +min([55, 2453, 2]...) +``` + +The expansion symbol is three periods (`...`), not a Unicode ellipsis character +(`…`). Expansion is a special syntax that is only available in function calls. + +## Using Sensitive Data as Function Arguments + +When using sensitive data, such as [an input variable](https://www.terraform.io/docs/language/values/variables.html#suppressing-values-in-cli-output) +or [an output defined](https://www.terraform.io/docs/language/values/outputs.html#sensitive-suppressing-values-in-cli-output) as sensitive +as function arguments, the result of the function call will be marked as sensitive. + +This is a conservative behavior that is true irrespective of the function being +called. For example, passing an object containing a sensitive input variable to +the `keys()` function will result in a list that is sensitive: + +```shell +> local.baz +{ + "a" = (sensitive) + "b" = "dog" +} +> keys(local.baz) +(sensitive) +``` + +## When Terraform Calls Functions + +Most of Terraform's built-in functions are, in programming language terms, +[pure functions](https://en.wikipedia.org/wiki/Pure_function). This means that +their result is based only on their arguments and so it doesn't make any +practical difference when Terraform would call them. + +However, a small subset of functions interact with outside state and so for +those it can be helpful to know when Terraform will call them in relation to +other events that occur in a Terraform run. + +The small set of special functions includes +[`file`](/docs/language/functions/file.html), +[`templatefile`](/docs/language/functions/templatefile.html), +[`timestamp`](/docs/language/functions/timestamp.html), +and [`uuid`](/docs/language/functions/uuid.html). +If you are not working with these functions then you don't need +to read this section, although the information here may still be interesting +background information. + +The `file` and `templatefile` functions are intended for reading files that +are included as a static part of the configuration and so Terraform will +execute these functions as part of initial configuration validation, before +taking any other actions with the configuration. That means you cannot use +either function to read files that your configuration might generate +dynamically on disk as part of the plan or apply steps. + +The `timestamp` function returns a representation of the current system time +at the point when Terraform calls it, and the `uuid` function returns a random +result which differs on each call. Without any special behavior these would +would both cause the final configuration during the apply step not to match the +actions shown in the plan, which violates the Terraform execution model. + +For that reason, Terraform arranges for both of those functions to produce +[unknown value](references.html#values-not-yet-known) results during the +plan step, with the real result being decided only during the apply step. +For `timestamp` in particular, this means that the recorded time will be +the instant when Terraform began applying the change, rather than when +Terraform _planned_ the change. + +For more details on the behavior of these functions, refer to their own +documentation pages. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/index.html.md new file mode 100644 index 00000000..5a6a18a0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/index.html.md @@ -0,0 +1,69 @@ +--- +layout: "language" +page_title: "Expressions - Configuration Language" +--- + +# Expressions + +> **Hands-on:** Try the [Create Dynamic Expressions](https://learn.hashicorp.com/tutorials/terraform/expressions?in=terraform/configuration-language&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +_Expressions_ are used to refer to or compute values within a configuration. +The simplest expressions are just literal values, like `"hello"` or `5`, +but the Terraform language also allows more complex expressions such as +references to data exported by resources, arithmetic, conditional evaluation, +and a number of built-in functions. + +Expressions can be used in a number of places in the Terraform language, +but some contexts limit which expression constructs are allowed, +such as requiring a literal value of a particular type or forbidding +[references to resource attributes](/docs/language/expressions/references.html#references-to-resource-attributes). +Each language feature's documentation describes any restrictions it places on +expressions. + +You can experiment with the behavior of Terraform's expressions from +the Terraform expression console, by running +[the `terraform console` command](/docs/cli/commands/console.html). + +The other pages in this section describe the features of Terraform's +expression syntax. + +- [Types and Values](/docs/language/expressions/types.html) + documents the data types that Terraform expressions can resolve to, and the + literal syntaxes for values of those types. + +- [Strings and Templates](/docs/language/expressions/strings.html) + documents the syntaxes for string literals, including interpolation sequences + and template directives. + +- [References to Values](/docs/language/expressions/references.html) + documents how to refer to named values like variables and resource attributes. + +- [Operators](/docs/language/expressions/operators.html) + documents the arithmetic, comparison, and logical operators. + +- [Function Calls](/docs/language/expressions/function-calls.html) + documents the syntax for calling Terraform's built-in functions. + +- [Conditional Expressions](/docs/language/expressions/conditionals.html) + documents the ` ? : ` expression, which + chooses between two values based on a bool condition. + +- [For Expressions](/docs/language/expressions/for.html) + documents expressions like `[for s in var.list : upper(s)]`, which can + transform a complex type value into another complex type value. + +- [Splat Expressions](/docs/language/expressions/splat.html) + documents expressions like `var.list[*].id`, which can extract simpler + collections from more complicated expressions. + +- [Dynamic Blocks](/docs/language/expressions/dynamic-blocks.html) + documents a way to create multiple repeatable nested blocks within a resource + or other construct. + +- [Type Constraints](/docs/language/expressions/type-constraints.html) + documents the syntax for referring to a type, rather than a value of that + type. Input variables expect this syntax in their `type` argument. + +- [Version Constraints](/docs/language/expressions/version-constraints.html) + documents the syntax of special strings that define a set of allowed software + versions. Terraform uses version constraints in several places. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/operators.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/operators.html.md new file mode 100644 index 00000000..54223268 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/operators.html.md @@ -0,0 +1,103 @@ +--- +layout: "language" +page_title: "Operators - Configuration Language" +--- + +# Arithmetic and Logical Operators + +An _operator_ is a type of expression that transforms or combines one or more +other expressions. Operators either combine two values in some way to +produce a third result value, or transform a single given value to +produce a single result. + +Operators that work on two values place an operator symbol between the two +values, similar to mathematical notation: `1 + 2`. Operators that work on +only one value place an operator symbol before that value, like +`!true`. + +The Terraform language has a set of operators for both arithmetic and logic, +which are similar to operators in programming languages such as JavaScript +or Ruby. + +When multiple operators are used together in an expression, they are evaluated +in the following order of operations: + +1. `!`, `-` (multiplication by `-1`) +1. `*`, `/`, `%` +1. `+`, `-` (subtraction) +1. `>`, `>=`, `<`, `<=` +1. `==`, `!=` +1. `&&` +1. `||` + +Use parentheses to override the default order of operations. Without +parentheses, higher levels will be evaluated first, so Terraform will interpret +`1 + 2 * 3` as `1 + (2 * 3)` and _not_ as `(1 + 2) * 3`. + +The different operators can be gathered into a few different groups with +similar behavior, as described below. Each group of operators expects its +given values to be of a particular type. Terraform will attempt to convert +values to the required type automatically, or will produce an error message +if automatic conversion is impossible. + +## Arithmetic Operators + +The arithmetic operators all expect number values and produce number values +as results: + +* `a + b` returns the result of adding `a` and `b` together. +* `a - b` returns the result of subtracting `b` from `a`. +* `a * b` returns the result of multiplying `a` and `b`. +* `a / b` returns the result of dividing `a` by `b`. +* `a % b` returns the remainder of dividing `a` by `b`. This operator is + generally useful only when used with whole numbers. +* `-a` returns the result of multiplying `a` by `-1`. + +Terraform supports some other less-common numeric operations as +[functions](function-calls.html). For example, you can calculate exponents +using +[the `pow` function](/docs/language/functions/pow.html). + +## Equality Operators + +The equality operators both take two values of any type and produce boolean +values as results. + +* `a == b` returns `true` if `a` and `b` both have the same type and the same + value, or `false` otherwise. +* `a != b` is the opposite of `a == b`. + +Because the equality operators require both arguments to be of exactly the +same type in order to decide equality, we recommend using these operators only +with values of primitive types or using explicit type conversion functions +to indicate which type you are intending to use for comparison. + +Comparisons between structural types may produce surprising results if you +are not sure about the types of each of the arguments. For example, +`var.list == []` may seem like it would return `true` if `var.list` were an +empty list, but `[]` actually builds a value of type `tuple([])` and so the +two values can never match. In this situation it's often clearer to write +`length(var.list) == 0` instead. + +## Comparison Operators + +The comparison operators all expect number values and produce boolean values +as results. + +* `a < b` returns `true` if `a` is less than `b`, or `false` otherwise. +* `a <= b` returns `true` if `a` is less than or equal to `b`, or `false` + otherwise. +* `a > b` returns `true` if `a` is greater than `b`, or `false` otherwise. +* `a >= b` returns `true` if `a` is greater than or equal to `b`, or `false` otherwise. + +## Logical Operators + +The logical operators all expect bool values and produce bool values as results. + +* `a || b` returns `true` if either `a` or `b` is `true`, or `false` if both are `false`. +* `a && b` returns `true` if both `a` and `b` are `true`, or `false` if either one is `false`. +* `!a` returns `true` if `a` is `false`, and `false` if `a` is `true`. + +Terraform does not have an operator for the "exclusive OR" operation. If you +know that both operators are boolean values then exclusive OR is equivalent +to the `!=` ("not equal") operator. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/references.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/references.html.md new file mode 100644 index 00000000..f69420f9 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/references.html.md @@ -0,0 +1,349 @@ +--- +layout: "language" +page_title: "References to Values - Configuration Language" +--- + +# References to Named Values + +> **Hands-on:** Try the [Create Dynamic Expressions](https://learn.hashicorp.com/tutorials/terraform/expressions?in=terraform/configuration-language&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +Terraform makes several kinds of named values available. Each of these names is +an expression that references the associated value; you can use them as +standalone expressions, or combine them with other expressions to compute new +values. + +## Types of Named Values + +The main kinds of named values available in Terraform are: + +- Resources +- Input variables +- Local values +- Child module outputs +- Data sources +- Filesystem and workspace info +- Block-local values + +The sections below explain each kind of named value in detail. + +Although many of these names use dot-separated paths that resemble +[attribute notation](./types.html#indices-and-attributes) for elements of object values, they are not +implemented as real objects. This means you must use them exactly as written: +you cannot use square-bracket notation to replace the dot-separated paths, and +you cannot iterate over the "parent object" of a named entity; for example, you +cannot use `aws_instance` in a `for` expression to iterate over every AWS +instance resource. + +### Resources + +`.` represents a [managed resource](/docs/language/resources/index.html) of +the given type and name. + +The value of a resource reference can vary, depending on whether the resource +uses `count` or `for_each`: + +- If the resource doesn't use `count` or `for_each`, the reference's value is an + object. The resource's attributes are elements of the object, and you can + access them using [dot or square bracket notation](./types.html#indices-and-attributes). +- If the resource has the `count` argument set, the reference's value is a + _list_ of objects representing its instances. +- If the resource has the `for_each` argument set, the reference's value is a + _map_ of objects representing its instances. + +Any named value that does not match another pattern listed below +will be interpreted by Terraform as a reference to a managed resource. + +For more information about how to use resource references, see +[references to resource attributes](#references-to-resource-attributes) below. + +### Input Variables + +`var.` is the value of the [input variable](/docs/language/values/variables.html) of the given name. + +If the variable has a type constraint (`type` argument) as part of its +declaration, Terraform will automatically convert the caller's given value +to conform to the type constraint. + +For that reason, you can safely assume that a reference using `var.` will +always produce a value that conforms to the type constraint, even if the caller +provided a value of a different type that was automatically converted. + +In particular, note that if you define a variable as being of an object type +with particular attributes then only _those specific attributes_ will be +available in expressions elsewhere in the module, even if the caller actually +passed in a value with additional attributes. You must define in the type +constraint all of the attributes you intend to use elsewhere in your module. + +### Local Values + +`local.` is the value of the [local value](/docs/language/values/locals.html) of the given name. + +Local values can refer to other local values, even within the same `locals` +block, as long as you don't introduce circular dependencies. + +### Child Module Outputs + +`module.` is an value representing the results of +[a `module` block](/docs/language/modules/syntax.html). + +If the corresponding `module` block does not have either `count` nor `for_each` +set then the value will be an object with one attribute for each output value +defined in the child module. To access one of the module's +[output values](/docs/language/values/outputs.html), use `module..`. + +If the corresponding `module` uses `for_each` then the value will be a map +of objects whose keys correspond with the keys in the `for_each` expression, +and whose values are each objects with one attribute for each output value +defined in the child module, each representing one module instance. + +If the corresponding module uses `count` then the result is similar to for +`for_each` except that the value is a _list_ with the requested number of +elements, each one representing one module instance. + +### Data Sources + +`data..` is an object representing a +[data resource](/docs/language/data-sources/index.html) of the given data +source type and name. If the resource has the `count` argument set, the value +is a list of objects representing its instances. If the resource has the `for_each` +argument set, the value is a map of objects representing its instances. + +For more information, see +[References to Resource Attributes](#references-to-resource-attributes), which +also applies to data resources aside from the addition of the `data.` prefix +to mark the reference as for a data resource. + +### Filesystem and Workspace Info + +* `path.module` is the filesystem path of the module where the expression + is placed. +* `path.root` is the filesystem path of the root module of the configuration. +* `path.cwd` is the filesystem path of the current working directory. In + normal use of Terraform this is the same as `path.root`, but some advanced + uses of Terraform run it from a directory other than the root module + directory, causing these paths to be different. +* `terraform.workspace` is the name of the currently selected + [workspace](/docs/language/state/workspaces.html). + +Use the values in this section carefully, because they include information +about the context in which a configuration is being applied and so may +inadvertently hurt the portability or composability of a module. + +For example, if you use `path.cwd` directly to populate a path into a resource +argument then later applying the same configuration from a different directory +or on a different computer with a different directory structure will cause +the provider to consider the change of path to be a change to be applied, even +if the path still refers to the same file. + +Similarly, if you use any of these values as a form of namespacing in a shared +module, such as using `terraform.workspace` as a prefix for globally-unique +object names, it may not be possible to call your module more than once in +the same configuration. + +Aside from `path.module`, we recommend using the values in this section only +in the root module of your configuration. If you are writing a shared module +which needs a prefix to help create unique names, define an input variable +for your module and allow the calling module to define the prefix. The +calling module can then use `terraform.workspace` to define it if appropriate, +or some other value if not: + +```hcl +module "example" { + # ... + + name_prefix = "app-${terraform-workspace}" +} +``` + +### Block-Local Values + +Within the bodies of certain blocks, or in some other specific contexts, +there are other named values available beyond the global values listed above. +These local names are described in the documentation for the specific contexts +where they appear. Some of most common local names are: + +- `count.index`, in resources that use + [the `count` meta-argument](/docs/language/meta-arguments/count.html). +- `each.key` / `each.value`, in resources that use + [the `for_each` meta-argument](/docs/language/meta-arguments/for_each.html). +- `self`, in [provisioner](/docs/language/resources/provisioners/syntax.html) and + [connection](/docs/language/resources/provisioners/connection.html) blocks. + +-> **Note:** Local names are often referred to as _variables_ or +_temporary variables_ in their documentation. These are not [input +variables](/docs/language/values/variables.html); they are just arbitrary names +that temporarily represent a value. + +The names in this section relate to top-level configuration blocks only. +If you use [`dynamic` blocks](dynamic-blocks.html) to dynamically generate +resource-type-specific _nested_ blocks within `resource` and `data` blocks then +you'll refer to the key and value of each element differently. See the +`dynamic` blocks documentation for details. + +## Named Values and Dependencies + +Constructs like resources and module calls often use references to named values +in their block bodies, and Terraform analyzes these expressions to automatically +infer dependencies between objects. For example, an expression in a resource +argument that refers to another managed resource creates an implicit dependency +between the two resources. + +## References to Resource Attributes + +The most common reference type is a reference to an attribute of a resource +which has been declared either with a `resource` or `data` block. Because +the contents of such blocks can be quite complicated themselves, expressions +referring to these contents can also be complicated. + +Consider the following example resource block: + +```hcl +resource "aws_instance" "example" { + ami = "ami-abc123" + instance_type = "t2.micro" + + ebs_block_device { + device_name = "sda2" + volume_size = 16 + } + ebs_block_device { + device_name = "sda3" + volume_size = 20 + } +} +``` + +The documentation for [`aws_instance`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) +lists all of the arguments and nested blocks supported for this resource type, +and also lists a number of attributes that are _exported_ by this resource +type. All of these different resource type schema constructs are available +for use in references, as follows: + +* The `ami` argument set in the configuration can be used elsewhere with + the reference expression `aws_instance.example.ami`. +* The `id` attribute exported by this resource type can be read using the + same syntax, giving `aws_instance.example.id`. +* The arguments of the `ebs_block_device` nested blocks can be accessed using + a [splat expression](./splat.html). For example, to obtain a list of + all of the `device_name` values, use + `aws_instance.example.ebs_block_device[*].device_name`. +* The nested blocks in this particular resource type do not have any exported + attributes, but if `ebs_block_device` were to have a documented `id` + attribute then a list of them could be accessed similarly as + `aws_instance.example.ebs_block_device[*].id`. +* Sometimes nested blocks are defined as taking a logical key to identify each + block, which serves a similar purpose as the resource's own name by providing + a convenient way to refer to that single block in expressions. If `aws_instance` + had a hypothetical nested block type `device` that accepted such a key, it + would look like this in configuration: + + ```hcl + device "foo" { + size = 2 + } + device "bar" { + size = 4 + } + ``` + + Arguments inside blocks with _keys_ can be accessed using index syntax, such + as `aws_instance.example.device["foo"].size`. + + To obtain a map of values of a particular argument for _labelled_ nested + block types, use a [`for` expression](./for.html): + `{for k, device in aws_instance.example.device : k => device.size}`. + +When a resource has the +[`count`](/docs/language/meta-arguments/count.html) +argument set, the resource itself becomes a _list_ of instance objects rather than +a single object. In that case, access the attributes of the instances using +either [splat expressions](./splat.html) or index syntax: + +* `aws_instance.example[*].id` returns a list of all of the ids of each of the + instances. +* `aws_instance.example[0].id` returns just the id of the first instance. + +When a resource has the +[`for_each`](/docs/language/meta-arguments/for_each.html) +argument set, the resource itself becomes a _map_ of instance objects rather than +a single object, and attributes of instances must be specified by key, or can +be accessed using a [`for` expression](./for.html). + +* `aws_instance.example["a"].id` returns the id of the "a"-keyed resource. +* `[for value in aws_instance.example: value.id]` returns a list of all of the ids + of each of the instances. + +Note that unlike `count`, splat expressions are _not_ directly applicable to resources managed with `for_each`, as splat expressions must act on a list value. However, you can use the `values()` function to extract the instances as a list and use that list value in a splat expression: + +* `values(aws_instance.example)[*].id` + +### Sensitive Resource Attributes + +When defining the schema for a resource type, a provider developer can mark +certain attributes as _sensitive_, in which case Terraform will show a +placeholder marker `(sensitive)` instead of the actual value when rendering +a plan involving that attribute. + +A provider attribute marked as sensitive behaves similarly to an +[an input variable declared as sensitive](/docs/language/values/variables.html#suppressing-values-in-cli-output), +where Terraform will hide the value in the plan and apply messages and will +also hide any other values you derive from it as sensitive. +However, there are some limitations to that behavior as described in +[Cases where Terraform may disclose a sensitive variable](/docs/language/values/variables.html#cases-where-terraform-may-disclose-a-sensitive-variable). + +If you use a sensitive value from a resource attribute as part of an +[output value](/docs/language/values/outputs.html) then Terraform will require +you to also mark the output value itself as sensitive, to confirm that you +intended to export it. + +Terraform will still record sensitive values in the [state](/docs/language/state/index.html), +and so anyone who can access the state data will have access to the sensitive +values in cleartext. For more information, see +[_Sensitive Data in State_](/docs/language/state/sensitive-data.html). + +-> **Note:** Treating values derived from a sensitive resource attribute as +sensitive themselves was introduced in Terraform v0.15. Earlier versions of +Terraform will obscure the direct value of a sensitive resource attribute, +but will _not_ automatically obscure other values derived from sensitive +resource attributes. + +### Values Not Yet Known + +When Terraform is planning a set of changes that will apply your configuration, +some resource attribute values cannot be populated immediately because their +values are decided dynamically by the remote system. For example, if a +particular remote object type is assigned a generated unique id on creation, +Terraform cannot predict the value of this id until the object has been created. + +To allow expressions to still be evaluated during the plan phase, Terraform +uses special "unknown value" placeholders for these results. In most cases you +don't need to do anything special to deal with these, since the Terraform +language automatically handles unknown values during expressions, so that +for example adding a known value to an unknown value automatically produces +an unknown value as the result. + +However, there are some situations where unknown values _do_ have a significant +effect: + +* The `count` meta-argument for resources cannot be unknown, since it must + be evaluated during the plan phase to determine how many instances are to + be created. + +* If unknown values are used in the configuration of a data resource, that + data resource cannot be read during the plan phase and so it will be deferred + until the apply phase. In this case, the results of the data resource will + _also_ be unknown values. + +* If an unknown value is assigned to an argument inside a `module` block, + any references to the corresponding input variable within the child module + will use that unknown value. + +* If an unknown value is used in the `value` argument of an output value, + any references to that output value in the parent module will use that + unknown value. + +* Terraform will attempt to validate that unknown values are of suitable + types where possible, but incorrect use of such values may not be detected + until the apply phase, causing the apply to fail. + +Unknown values appear in the `terraform plan` output as `(not yet known)`. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/splat.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/splat.html.md new file mode 100644 index 00000000..731aa767 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/splat.html.md @@ -0,0 +1,128 @@ +--- +layout: "language" +page_title: "Splat Expressions - Configuration Language" +--- + +# Splat Expressions + +> **Hands-on:** Try the [Create Dynamic Expressions](https://learn.hashicorp.com/tutorials/terraform/expressions?in=terraform/configuration-language&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +A _splat expression_ provides a more concise way to express a common +operation that could otherwise be performed with a `for` expression. + +If `var.list` is a list of objects that all have an attribute `id`, then +a list of the ids could be produced with the following `for` expression: + +```hcl +[for o in var.list : o.id] +``` + +This is equivalent to the following _splat expression:_ + +```hcl +var.list[*].id +``` + +The special `[*]` symbol iterates over all of the elements of the list given +to its left and accesses from each one the attribute name given on its +right. A splat expression can also be used to access attributes and indexes +from lists of complex types by extending the sequence of operations to the +right of the symbol: + +```hcl +var.list[*].interfaces[0].name +``` + +The above expression is equivalent to the following `for` expression: + +```hcl +[for o in var.list : o.interfaces[0].name] +``` + +## Splat Expressions with Maps + +The splat expression patterns shown above apply only to lists, sets, and +tuples. To get a similar result with a map or object value you must use +[`for` expressions](for.html). + +Resources that use the `for_each` argument will appear in expressions as a map +of objects, so you can't use splat expressions with those resources. +For more information, see +[Referring to Resource Instances](/docs/language/meta-arguments/for_each.html#referring-to-instances). + +## Single Values as Lists + +Splat expressions have a special behavior when you apply them to a value that +isn't a list, set, or tuple. + +If the value is anything other than a null value then the splat expression will +transform it into a single-element list, or more accurately a single-element +tuple value. If the value is _null_ then the splat expression will return an +empty tuple. + +This special behavior can be useful for modules that accept optional input +variables whose default value is `null` to represent the absense of any value, +to adapt the variable value to work with other Terraform language features that +are designed to work with collections. For example: + +``` +variable "website" { + type = object({ + index_document = string + error_document = string + }) + default = null +} + +resource "aws_s3_bucket" "example" { + # ... + + dynamic "website" { + for_each = var.website[*] + content { + index_document = website.value.index_document + error_document = website.value.error_document + } + } +} +``` + +The above example uses a [`dynamic` block](dynamic-blocks.html), which +generates zero or more nested blocks based on a collection value. The input +variable `var.website` is defined as a single object that might be null, +so the `dynamic` block's `for_each` expression uses `[*]` to ensure that +there will be one block if the module caller sets the website argument, or +zero blocks if the caller leaves it set to null. + +This special behavior of splat expressions is not obvious to an unfamiliar +reader, so we recommend using it only in `for_each` arguments and similar +situations where the context implies working with a collection. Otherwise, +the meaning of the expression may be unclear to future readers. + +## Legacy (Attribute-only) Splat Expressions + +Earlier versions of the Terraform language had a slightly different version +of splat expressions, which Terraform continues to support for backward +compatibility. This older variant is less useful than the modern form described +above, and so we recommend against using it in new configurations. + +The legacy "attribute-only" splat expressions use the sequence `.*`, instead of +`[*]`: + +``` +var.list.*.interfaces[0].name +``` + +This form has a subtly different behavior, equivalent to the following +`for` expression: + +``` +[for o in var.list : o.interfaces][0].name +``` + +Notice that with the attribute-only splat expression the index operation +`[0]` is applied to the result of the iteration, rather than as part of +the iteration itself. Only the attribute lookups apply to each element of +the input. This limitation was confusing some people using older versions of +Terraform and so we recommend always using the new-style splat expressions, +with `[*]`, to get the more consistent behavior. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/strings.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/strings.html.md new file mode 100644 index 00000000..72cd0bba --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/strings.html.md @@ -0,0 +1,222 @@ +--- +layout: "language" +page_title: "Strings and Templates - Configuration Language" +--- + +# Strings and Templates + +String literals are the most complex kind of literal expression in +Terraform, and also the most commonly used. + +Terraform supports both a quoted syntax and a "heredoc" syntax for strings. +Both of these syntaxes support template sequences for interpolating values and +manipulating text. + +## Quoted Strings + +A quoted string is a series of characters delimited by straight double-quote +characters (`"`). + +``` +"hello" +``` + +### Escape Sequences + +In quoted strings, the backslash character serves as an escape +sequence, with the following characters selecting the escape behavior: + +| Sequence | Replacement | +| ------------ | ----------------------------------------------------------------------------- | +| `\n` | Newline | +| `\r` | Carriage Return | +| `\t` | Tab | +| `\"` | Literal quote (without terminating the string) | +| `\\` | Literal backslash | +| `\uNNNN` | Unicode character from the basic multilingual plane (NNNN is four hex digits) | +| `\UNNNNNNNN` | Unicode character from supplementary planes (NNNNNNNN is eight hex digits) | + +There are also two special escape sequences that do not use backslashes: + +| Sequence | Replacement | +| --- | ---- | +| `$${` | Literal `${`, without beginning an interpolation sequence. | +| `%%{` | Literal `%{`, without beginning a template directive sequence. | + +## Heredoc Strings + +Terraform also supports a "heredoc" style of string literal inspired by Unix +shell languages, which allows multi-line strings to be expressed more clearly. + +```hcl +<}`/`%{else}`/`%{endif}` directive chooses between two templates based + on the value of a bool expression: + + ```hcl + "Hello, %{ if var.name != "" }${var.name}%{ else }unnamed%{ endif }!" + ``` + + The `else` portion may be omitted, in which case the result is an empty + string if the condition expression returns `false`. + +* The `%{for in }` / `%{endfor}` directive iterates over the + elements of a given collection or structural value and evaluates a given + template once for each element, concatenating the results together: + + ```hcl + < = , = , ... }` — a + pair of curly braces containing a comma-separated series of ` = ` + pairs. Values that match the object type must contain _all_ of the specified + keys, and the value for each key must match its specified type. (Values with + _additional_ keys can still match an object type, but the extra attributes + are discarded during type conversion.) +* `tuple(...)`: a sequence of elements identified by consecutive whole + numbers starting with zero, where each element has its own type. + + The schema for tuple types is `[, , ...]` — a pair of square + brackets containing a comma-separated series of types. Values that match the + tuple type must have _exactly_ the same number of elements (no more and no + fewer), and the value in each position must match the specified type for + that position. + +For example: an object type of `object({ name=string, age=number })` would match +a value like the following: + +```hcl +{ + name = "John" + age = 52 +} +``` + +Also, an object type of `object({ id=string, cidr_block=string })` would match +the object produced by a reference to an `aws_vpc` resource, like +`aws_vpc.example_vpc`; although the resource has additional attributes, they +would be discarded during type conversion. + +Finally, a tuple type of `tuple([string, number, bool])` would match a value +like the following: + +```hcl +["a", 15, true] +``` + +### Complex Type Literals + +The Terraform language has literal expressions for creating tuple and object +values, which are described in +[Expressions: Literal Expressions](/docs/language/expressions/types.html#literal-expressions) as +"list/tuple" literals and "map/object" literals, respectively. + +Terraform does _not_ provide any way to directly represent lists, maps, or sets. +However, due to the automatic conversion of complex types (described below), the +difference between similar complex types is almost never relevant to a normal +user, and most of the Terraform documentation conflates lists with tuples and +maps with objects. The distinctions are only useful when restricting input +values for a module or resource. + +### Conversion of Complex Types + +Similar kinds of complex types (list/tuple/set and map/object) can usually be +used interchangeably within the Terraform language, and most of Terraform's +documentation glosses over the differences between the kinds of complex type. +This is due to two conversion behaviors: + +* Whenever possible, Terraform converts values between similar kinds of complex + types if the provided value is not the exact type requested. "Similar kinds" + is defined as follows: + * Objects and maps are similar. + * A map (or a larger object) can be converted to an object if it has + _at least_ the keys required by the object schema. Any additional + attributes are discarded during conversion, which means map -> object + -> map conversions can be lossy. + * Tuples and lists are similar. + * A list can only be converted to a tuple if it has _exactly_ the + required number of elements. + * Sets are _almost_ similar to both tuples and lists: + * When a list or tuple is converted to a set, duplicate values are + discarded and the ordering of elements is lost. + * When a `set` is converted to a list or tuple, the elements will be + in an arbitrary order. If the set's elements were strings, they will + be in lexicographical order; sets of other element types do not + guarantee any particular order of elements. +* Whenever possible, Terraform converts _element values_ within a complex type, + either by converting complex-typed elements recursively or as described above + in [Conversion of Primitive Types](#conversion-of-primitive-types). + +For example: if a module argument requires a value of type `list(string)` and a +user provides the tuple `["a", 15, true]`, Terraform will internally transform +the value to `["a", "15", "true"]` by converting the elements to the required +`string` element type. Later, if the module uses those elements to set different +resource arguments that require a string, a number, and a bool (respectively), +Terraform will automatically convert the second and third strings back to the +required types at that time, since they contain valid representations of a +number and a bool. + +On the other hand, automatic conversion will fail if the provided value +(including any of its element values) is incompatible with the required type. If +an argument requires a type of `map(string)` and a user provides the object +`{name = ["Kristy", "Claudia", "Mary Anne", "Stacey"], age = 12}`, Terraform +will raise a type mismatch error, since a tuple cannot be converted to a string. + +## Dynamic Types: The "any" Constraint + +The keyword `any` is a special construct that serves as a placeholder for a +type yet to be decided. `any` is not _itself_ a type: when interpreting a +value against a type constraint containing `any`, Terraform will attempt to +find a single actual type that could replace the `any` keyword to produce +a valid result. + +For example, given the type constraint `list(any)`, Terraform will examine +the given value and try to choose a replacement for the `any` that would +make the result valid. + +If the given value were `["a", "b", "c"]` -- whose physical type is +`tuple([string, string, string])`, Terraform analyzes this as follows: + +* Tuple types and list types are _similar_ per the previous section, so the + tuple-to-list conversion rule applies. +* All of the elements in the tuple are strings, so the type constraint + `string` would be valid for all of the list elements. +* Therefore in this case the `any` argument is replaced with `string`, + and the final concrete value type is `list(string)`. + +All of the elements of a collection must have the same type, so conversion +to `list(any)` requires that all of the given elements must be convertible +to a common type. This implies some other behaviors that result from the +conversion rules described in earlier sections. + +* If the given value were instead `["a", 1, "b"]` then Terraform would still + select `list(string)`, because of the primitive type conversion rules, and + the resulting value would be `["a", "1", "b"]` due to the string conversion + implied by that type constraint. +* If the given value were instead `["a", [], "b"]` then the value cannot + conform to the type constraint: there is no single type that both a string + and an empty tuple can convert to. Terraform would reject this value, + complaining that all elements must have the same type. + +Although the above examples use `list(any)`, a similar principle applies to +`map(any)` and `set(any)`. + +If you wish to apply absolutely no constraint to the given value, the `any` +keyword can be used in isolation: + +```hcl +variable "no_type_constraint" { + type = any +} +``` + +In this case, Terraform will replace `any` with the exact type of the given +value and thus perform no type conversion whatsoever. + +## Experimental: Optional Object Type Attributes + +From Terraform v0.14 there is _experimental_ support for marking particular +attributes as optional in an object type constraint. + +To mark an attribute as optional, use the additional `optional(...)` modifier +around its type declaration: + +```hcl +variable "with_optional_attribute" { + type = object({ + a = string # a required attribute + b = optional(string) # an optional attribute + }) +} +``` + +By default, for required attributes, Terraform will return an error if the +source value has no matching attribute. Marking an attribute as optional +changes the behavior in that situation: Terraform will instead just silently +insert `null` as the value of the attribute, allowing the recieving module +to describe an appropriate fallback behavior. + +Because this feature is currently experimental, it requires an explicit +opt-in on a per-module basis. To use it, write a `terraform` block with the +`experiments` argument set as follows: + +```hcl +terraform { + experiments = [module_variable_optional_attrs] +} +``` + +Until the experiment is concluded, the behavior of this feature may see +breaking changes even in minor releases. We recommend using this feature +only in prerelease versions of modules as long as it remains experimental. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/types.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/types.html.md new file mode 100644 index 00000000..c197a7f8 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/types.html.md @@ -0,0 +1,147 @@ +--- +layout: "language" +page_title: "Types and Values - Configuration Language" +--- + +# Types and Values + +The result of an expression is a _value_. All values have a _type_, which +dictates where that value can be used and what transformations can be +applied to it. + +## Types + +The Terraform language uses the following types for its values: + +* `string`: a sequence of Unicode characters representing some text, like + `"hello"`. +* `number`: a numeric value. The `number` type can represent both whole + numbers like `15` and fractional values like `6.283185`. +* `bool`: a boolean value, either `true` or `false`. `bool` values can be used in conditional + logic. +* `list` (or `tuple`): a sequence of values, like + `["us-west-1a", "us-west-1c"]`. Elements in a list or tuple are identified by + consecutive whole numbers, starting with zero. +* `map` (or `object`): a group of values identified by named labels, like + `{name = "Mabel", age = 52}`. + +Strings, numbers, and bools are sometimes called _primitive types._ Lists/tuples and maps/objects are sometimes called _complex types,_ _structural types,_ or _collection types._ + +Finally, there is one special value that has _no_ type: + +* `null`: a value that represents _absence_ or _omission._ If you set an + argument of a resource or module to `null`, Terraform behaves as though you + had completely omitted it — it will use the argument's default value if it has + one, or raise an error if the argument is mandatory. `null` is most useful in + conditional expressions, so you can dynamically omit an argument if a + condition isn't met. + +## Literal Expressions + +A _literal expression_ is an expression that directly represents a particular +constant value. Terraform has a literal expression syntax for each of the value +types described above. + +### Strings + +Strings are usually represented by a double-quoted sequence of Unicode +characters, `"like this"`. There is also a "heredoc" syntax for more complex +strings. + +String literals are the most complex kind of literal expression in +Terraform, and have their own page of documentation. See [Strings](./strings.html) +for information about escape sequences, the heredoc syntax, interpolation, and +template directives. + +### Numbers + +Numbers are represented by unquoted sequences of digits with or without a +decimal point, like `15` or `6.283185`. + +### Bools + +Bools are represented by the unquoted symbols `true` and `false`. + +### Null + +The null value is represented by the unquoted symbol `null`. + +### Lists/Tuples + +Lists/tuples are represented by a pair of square brackets containing a +comma-separated sequence of values, like `["a", 15, true]`. + +List literals can be split into multiple lines for readability, but always +require a comma between values. A comma after the final value is allowed, +but not required. Values in a list can be arbitrary expressions. + +### Maps/Objects + +Maps/objects are represented by a pair of curly braces containing a series of +` = ` pairs: + +```hcl +{ + name = "John" + age = 52 +} +``` + +Key/value pairs can be separated by either a comma or a line break. + +The values in a map +can be arbitrary expressions. + +The keys in a map must be strings; they can be left unquoted if +they are a valid [identifier](/docs/language/syntax/configuration.html#identifiers), but must be quoted +otherwise. You can use a non-literal string expression as a key by wrapping it in +parentheses, like `(var.business_unit_tag_name) = "SRE"`. + +## Indices and Attributes + +[inpage-index]: #indices-and-attributes + +Elements of list/tuple and map/object values can be accessed using +the square-bracket index notation, like `local.list[3]`. The expression within +the brackets must be a whole number for list and tuple values or a string +for map and object values. + +Map/object attributes with names that are valid identifiers can also be accessed +using the dot-separated attribute notation, like `local.object.attrname`. +In cases where a map might contain arbitrary user-specified keys, we recommend +using only the square-bracket index notation (`local.map["keyname"]`). + +## More About Complex Types + +In most situations, lists and tuples behave identically, as do maps and objects. +Whenever the distinction isn't relevant, the Terraform documentation uses each +pair of terms interchangeably (with a historical preference for "list" and +"map"). + +However, module authors and provider developers should understand the +differences between these similar types (and the related `set` type), since they +offer different ways to restrict the allowed values for input variables and +resource arguments. + +For complete details about these types (and an explanation of why the difference +usually doesn't matter), see [Type Constraints](/docs/language/expressions/type-constraints.html). + +## Type Conversion + +Expressions are most often used to set values for the arguments of resources and +child modules. In these cases, the argument has an expected type and the given +expression must produce a value of that type. + +Where possible, Terraform automatically converts values from one type to +another in order to produce the expected type. If this isn't possible, Terraform +will produce a type mismatch error and you must update the configuration with a +more suitable expression. + +Terraform automatically converts number and bool values to strings when needed. +It also converts strings to numbers or bools, as long as the string contains a +valid representation of a number or bool value. + +* `true` converts to `"true"`, and vice-versa +* `false` converts to `"false"`, and vice-versa +* `15` converts to `"15"`, and vice-versa + diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/version-constraints.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/version-constraints.html.md similarity index 83% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/version-constraints.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/expressions/version-constraints.html.md index 69558015..0f7ebba5 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/version-constraints.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/expressions/version-constraints.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Version Constraints - Configuration Language" --- @@ -9,9 +9,9 @@ Anywhere that Terraform lets you specify a range of acceptable versions for something, it expects a specially formatted string known as a version constraint. Version constraints are used when configuring: -- [Modules](./modules.html) -- [Provider requirements](./provider-requirements.html) -- [The `required_version` setting](./terraform.html#specifying-a-required-terraform-version) in the `terraform` block. +- [Modules](/docs/language/modules/index.html) +- [Provider requirements](/docs/language/providers/requirements.html) +- [The `required_version` setting](/docs/language/settings/index.html#specifying-a-required-terraform-version) in the `terraform` block. ## Version Constraint Syntax @@ -22,7 +22,7 @@ other dependency management systems like Bundler and NPM. version = ">= 1.2.0, < 2.0.0" ``` -A version constraint is a [string literal](./expressions.html#string-literals) +A version constraint is a [string literal](/docs/language/expressions/strings.html) containing one or more conditions, which are separated by commas. Each condition consists of an operator and a version number. @@ -41,11 +41,10 @@ The following operators are valid: versions for which the comparison is true. "Greater-than" requests newer versions, and "less-than" requests older versions. -- `~>`: Allows the specified version, plus newer versions that only - increase the _most specific_ segment of the specified version number. For - example, `~> 0.9` is equivalent to `>= 0.9, < 1.0`, and `~> 0.8.4`, is - equivalent to `>= 0.8.4, < 0.9`. This is usually called the pessimistic - constraint operator. +- `~>`: Allows only the _rightmost_ version component to increment. For example, + to allow new patch releases within a specific minor release, use the full + version number: `~> 1.0.4` will allow installation of `1.0.5` and `1.0.10` + but not `1.1.0`. This is usually called the pessimistic constraint operator. ## Version Constraint Behavior diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/files/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/files/index.html.md new file mode 100644 index 00000000..109c60f7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/files/index.html.md @@ -0,0 +1,57 @@ +--- +layout: "language" +page_title: "Files and Directories - Configuration Language" +--- + +# Files and Directories + +## File Extension + +Code in the Terraform language is stored in plain text files with the `.tf` file +extension. There is also +[a JSON-based variant of the language](/docs/language/syntax/json.html) that is named with +the `.tf.json` file extension. + +Files containing Terraform code are often called _configuration files._ + +## Text Encoding + +Configuration files must always use UTF-8 encoding, and by convention +usually use Unix-style line endings (LF) rather than Windows-style +line endings (CRLF), though both are accepted. + +## Directories and Modules + +A _module_ is a collection of `.tf` and/or `.tf.json` files kept together in a +directory. + +A Terraform module only consists of the top-level configuration files in a +directory; nested directories are treated as completely separate modules, and +are not automatically included in the configuration. + +Terraform evaluates all of the configuration files in a module, effectively +treating the entire module as a single document. Separating various blocks into +different files is purely for the convenience of readers and maintainers, and +has no effect on the module's behavior. + +A Terraform module can use [module calls](/docs/language/modules/index.html) to +explicitly include other modules into the configuration. These child modules can +come from local directories (nested in the parent module's directory, or +anywhere else on disk), or from external sources like the +[Terraform Registry](https://registry.terraform.io). + +## The Root Module + +Terraform always runs in the context of a single _root module._ A complete +_Terraform configuration_ consists of a root module and the tree of child +modules (which includes the modules called by the root module, any modules +called by those modules, etc.). + +- In Terraform CLI, the root module is the working directory where Terraform is + invoked. (You can use command line options to specify a root module outside + the working directory, but in practice this is rare. ) +- In Terraform Cloud and Terraform Enterprise, the root module for a workspace + defaults to the top level of the configuration directory (supplied via version + control repository or direct upload), but the workspace settings can specify a + subdirectory to use instead. + diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/override.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/files/override.html.md similarity index 96% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/override.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/files/override.html.md index 10ab5467..334aa2eb 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/override.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/files/override.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Override Files - Configuration Language" sidebar_current: "docs-config-override" description: |- @@ -9,10 +9,6 @@ description: |- # Override Files --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Override Files](../configuration-0-11/override.html). - Terraform normally loads all of the `.tf` and `.tf.json` files within a directory and expects each one to define a distinct set of configuration objects. If two files attempt to define the same object, Terraform returns diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/abs.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/abs.html.md new file mode 100644 index 00000000..51d3bc01 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/abs.html.md @@ -0,0 +1,24 @@ +--- +layout: "language" +page_title: "abs - Functions - Configuration Language" +sidebar_current: "docs-funcs-numeric-abs" +description: |- + The abs function returns the absolute value of the given number. +--- + +# `abs` Function + +`abs` returns the absolute value of the given number. In other words, if the +number is zero or positive then it is returned as-is, but if it is negative +then it is multiplied by -1 to make it positive before returning it. + +## Examples + +``` +> abs(23) +23 +> abs(0) +0 +> abs(-12.4) +12.4 +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/abspath.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/abspath.html.md similarity index 80% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/abspath.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/abspath.html.md index cf5bceba..3da10ed7 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/abspath.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/abspath.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "abspath - Functions - Configuration Language" sidebar_current: "docs-funcs-file-abspath" description: |- @@ -8,10 +8,6 @@ description: |- # `abspath` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `abspath` takes a string containing a filesystem path and converts it to an absolute path. That is, if the path is not absolute, it will be joined with the current working directory. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/alltrue.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/alltrue.html.md new file mode 100644 index 00000000..c20dc3b3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/alltrue.html.md @@ -0,0 +1,28 @@ +--- +layout: "language" +page_title: alltrue - Functions - Configuration Language +sidebar_current: docs-funcs-collection-alltrue +description: |- + The alltrue function determines whether all elements of a collection + are true or "true". If the collection is empty, it returns true. +--- + +# `alltrue` Function + +-> **Note:** This function is available in Terraform 0.14 and later. + +`alltrue` returns `true` if all elements in a given collection are `true` +or `"true"`. It also returns `true` if the collection is empty. + +```hcl +alltrue(list) +``` + +## Examples + +```command +> alltrue(["true", true]) +true +> alltrue([true, false]) +false +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/anytrue.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/anytrue.html.md new file mode 100644 index 00000000..35e3c5de --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/anytrue.html.md @@ -0,0 +1,32 @@ +--- +layout: "language" +page_title: anytrue - Functions - Configuration Language +sidebar_current: docs-funcs-collection-anytrue +description: |- + The anytrue function determines whether any element of a collection + is true or "true". If the collection is empty, it returns false. +--- + +# `anytrue` Function + +-> **Note:** This function is available in Terraform 0.14 and later. + +`anytrue` returns `true` if any element in a given collection is `true` +or `"true"`. It also returns `false` if the collection is empty. + +```hcl +anytrue(list) +``` + +## Examples + +```command +> anytrue(["true"]) +true +> anytrue([true]) +true +> anytrue([true, false]) +true +> anytrue([]) +false +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/base64decode.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/base64decode.html.md similarity index 89% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/base64decode.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/base64decode.html.md index f81ed912..41feab12 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/base64decode.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/base64decode.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "base64decode - Functions - Configuration Language" sidebar_current: "docs-funcs-encoding-base64decode" description: |- @@ -8,10 +8,6 @@ description: |- # `base64decode` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `base64decode` takes a string containing a Base64 character sequence and returns the original string. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/base64encode.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/base64encode.html.md similarity index 90% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/base64encode.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/base64encode.html.md index e3e6bc5e..8a4ddb1c 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/base64encode.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/base64encode.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "base64encode - Functions - Configuration Language" sidebar_current: "docs-funcs-encoding-base64encode" description: |- @@ -8,10 +8,6 @@ description: |- # `base64encode` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `base64encode` applies Base64 encoding to a string. Terraform uses the "standard" Base64 alphabet as defined in diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/base64gzip.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/base64gzip.html.md similarity index 85% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/base64gzip.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/base64gzip.html.md index 5bd11434..f8d103a9 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/base64gzip.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/base64gzip.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "base64gzip - Functions - Configuration Language" sidebar_current: "docs-funcs-encoding-base64gzip" description: |- @@ -9,10 +9,6 @@ description: |- # `base64gzip` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `base64gzip` compresses a string with gzip and then encodes the result in Base64 encoding. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/base64sha256.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/base64sha256.html.md similarity index 84% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/base64sha256.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/base64sha256.html.md index 381f410b..1edfc98d 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/base64sha256.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/base64sha256.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "base64sha256 - Functions - Configuration Language" sidebar_current: "docs-funcs-crypto-base64sha256" description: |- @@ -9,10 +9,6 @@ description: |- # `base64sha256` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `base64sha256` computes the SHA256 hash of a given string and encodes it with Base64. This is not equivalent to `base64encode(sha256("test"))` since `sha256()` returns hexadecimal representation. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/base64sha512.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/base64sha512.html.md similarity index 85% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/base64sha512.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/base64sha512.html.md index b910d78f..87bd5213 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/base64sha512.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/base64sha512.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "base64sha512 - Functions - Configuration Language" sidebar_current: "docs-funcs-crypto-base64sha512" description: |- @@ -9,10 +9,6 @@ description: |- # `base64sha512` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `base64sha512` computes the SHA512 hash of a given string and encodes it with Base64. This is not equivalent to `base64encode(sha512("test"))` since `sha512()` returns hexadecimal representation. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/basename.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/basename.html.md similarity index 86% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/basename.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/basename.html.md index 8db5fa4b..e7869d64 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/basename.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/basename.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "basename - Functions - Configuration Language" sidebar_current: "docs-funcs-file-basename" description: |- @@ -9,10 +9,6 @@ description: |- # `basename` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `basename` takes a string containing a filesystem path and removes all except the last portion from it. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/bcrypt.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/bcrypt.html.md similarity index 84% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/bcrypt.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/bcrypt.html.md index 66852343..b1b0eb79 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/bcrypt.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/bcrypt.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "bcrypt - Functions - Configuration Language" sidebar_current: "docs-funcs-crypto-bcrypt" description: |- @@ -9,10 +9,6 @@ description: |- # `bcrypt` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `bcrypt` computes a hash of the given string using the Blowfish cipher, returning a string in [the _Modular Crypt Format_](https://passlib.readthedocs.io/en/stable/modular_crypt_format.html) diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/can.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/can.html.md similarity index 88% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/can.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/can.html.md index 5cb3abb6..8c05a304 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/can.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/can.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "can - Functions - Configuration Language" sidebar_current: "docs-funcs-conversion-can" description: |- @@ -9,10 +9,6 @@ description: |- # `can` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `can` evaluates the given expression and returns a boolean value indicating whether the expression produced a result without any errors. @@ -23,7 +19,7 @@ fallback values for failing expressions. The primary purpose of `can` is to turn an error condition into a boolean validation result when writing -[custom variable validation rules](../variables.html#custom-validation-rules). +[custom variable validation rules](/docs/language/values/variables.html#custom-validation-rules). For example: ``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/ceil.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/ceil.html.md new file mode 100644 index 00000000..0b68172c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/ceil.html.md @@ -0,0 +1,27 @@ +--- +layout: "language" +page_title: "ceil - Functions - Configuration Language" +sidebar_current: "docs-funcs-numeric-ceil" +description: |- + The ceil function returns the closest whole number greater than or equal to + the given value. +--- + +# `ceil` Function + +`ceil` returns the closest whole number that is greater than or equal to the +given value, which may be a fraction. + +## Examples + +``` +> ceil(5) +5 +> ceil(5.1) +6 +``` + +## Related Functions + +* [`floor`](./floor.html), which rounds to the nearest whole number _less than_ + or equal. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/chomp.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/chomp.html.md new file mode 100644 index 00000000..5ea67e81 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/chomp.html.md @@ -0,0 +1,30 @@ +--- +layout: "language" +page_title: "chomp - Functions - Configuration Language" +sidebar_current: "docs-funcs-string-chomp" +description: |- + The chomp function removes newline characters at the end of a string. +--- + +# `chomp` Function + +`chomp` removes newline characters at the end of a string. + +This can be useful if, for example, the string was read from a file that has +a newline character at the end. + +## Examples + +``` +> chomp("hello\n") +hello +> chomp("hello\r\n") +hello +> chomp("hello\n\n") +hello +``` + +## Related Functions + +* [`trimspace`](./trimspace.html), which removes all types of whitespace from + both the start and the end of a string. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/chunklist.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/chunklist.html.md similarity index 75% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/chunklist.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/chunklist.html.md index 63df0bc6..21823ab4 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/chunklist.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/chunklist.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "chunklist - Functions - Configuration Language" sidebar_current: "docs-funcs-collection-chunklist" description: |- @@ -9,10 +9,6 @@ description: |- # `chunklist` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `chunklist` splits a single list into fixed-size chunks, returning a list of lists. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/cidrhost.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/cidrhost.html.md similarity index 88% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/cidrhost.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/cidrhost.html.md index a7579312..e4354036 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/cidrhost.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/cidrhost.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "cidrhost - Functions - Configuration Language" sidebar_current: "docs-funcs-ipnet-cidrhost" description: |- @@ -9,10 +9,6 @@ description: |- # `cidrhost` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `cidrhost` calculates a full host IP address for a given host number within a given IP network address prefix. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/cidrnetmask.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/cidrnetmask.html.md similarity index 80% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/cidrnetmask.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/cidrnetmask.html.md index ce3fa8ad..bb3de4f7 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/cidrnetmask.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/cidrnetmask.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "cidrnetmask - Functions - Configuration Language" sidebar_current: "docs-funcs-ipnet-cidrnetmask" description: |- @@ -9,10 +9,6 @@ description: |- # `cidrnetmask` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `cidrnetmask` converts an IPv4 address prefix given in CIDR notation into a subnet mask address. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/cidrsubnet.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/cidrsubnet.html.md similarity index 96% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/cidrsubnet.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/cidrsubnet.html.md index 6988ce3f..e64c894f 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/cidrsubnet.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/cidrsubnet.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "cidrsubnet - Functions - Configuration Language" sidebar_current: "docs-funcs-ipnet-cidrsubnet" description: |- @@ -9,10 +9,6 @@ description: |- # `cidrsubnet` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `cidrsubnet` calculates a subnet address within given IP network address prefix. ```hcl diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/cidrsubnets.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/cidrsubnets.html.md similarity index 91% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/cidrsubnets.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/cidrsubnets.html.md index 7308cf5d..4f43635c 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/cidrsubnets.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/cidrsubnets.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "cidrsubnets - Functions - Configuration Language" sidebar_current: "docs-funcs-ipnet-cidrsubnets" description: |- @@ -9,10 +9,6 @@ description: |- # `cidrsubnets` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `cidrsubnets` calculates a sequence of consecutive IP address ranges within a particular CIDR prefix. @@ -70,7 +66,7 @@ platforms. ``` You can use nested `cidrsubnets` calls with -[`for` expressions](/docs/configuration/expressions.html#for-expressions) +[`for` expressions](/docs/language/expressions/for.html) to concisely allocate groups of network address blocks: ``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/coalesce.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/coalesce.html.md new file mode 100644 index 00000000..6458970e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/coalesce.html.md @@ -0,0 +1,58 @@ +--- +layout: "language" +page_title: "coalesce - Functions - Configuration Language" +sidebar_current: "docs-funcs-collection-coalesce-x" +description: |- + The coalesce function takes any number of arguments and returns the + first one that isn't null nor empty. +--- + +# `coalesce` Function + +`coalesce` takes any number of arguments and returns the first one +that isn't null or an empty string. + +All of the arguments must be of the same type. Terraform will try to +convert mismatched arguments to the most general of the types that all +arguments can convert to, or return an error if the types are incompatible. +The result type is the same as the type of all of the arguments. + +## Examples + +``` +> coalesce("a", "b") +a +> coalesce("", "b") +b +> coalesce(1,2) +1 +``` + +To perform the `coalesce` operation with a list of strings, use the `...` +symbol to expand the list as arguments: + +``` +> coalesce(["", "b"]...) +b +``` + +Terraform attempts to select a result type that all of the arguments can +convert to, so mixing argument types may produce surprising results due to +Terraform's automatic type conversion rules: + +``` +> coalesce(1, "hello") +"1" +> coalesce(true, "hello") +"true" +> coalesce({}, "hello") + +Error: Error in function call + +Call to function "coalesce" failed: all arguments must have the same type. +``` + +## Related Functions + +* [`coalescelist`](./coalescelist.html) performs a similar operation with + list arguments rather than individual arguments. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/coalescelist.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/coalescelist.html.md similarity index 79% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/coalescelist.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/coalescelist.html.md index aa265aa7..6508fa31 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/coalescelist.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/coalescelist.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "coalescelist - Functions - Configuration Language" sidebar_current: "docs-funcs-collection-coalescelist" description: |- @@ -9,10 +9,6 @@ description: |- # `coalescelist` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `coalescelist` takes any number of list arguments and returns the first one that isn't empty. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/compact.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/compact.html.md new file mode 100644 index 00000000..cd2d6379 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/compact.html.md @@ -0,0 +1,23 @@ +--- +layout: "language" +page_title: "compact - Functions - Configuration Language" +sidebar_current: "docs-funcs-collection-compact" +description: |- + The compact function removes empty string elements from a list. +--- + +# `compact` Function + +`compact` takes a list of strings and returns a new list with any empty string +elements removed. + +## Examples + +``` +> compact(["a", "", "b", "c"]) +[ + "a", + "b", + "c", +] +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/concat.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/concat.html.md new file mode 100644 index 00000000..47ef10ac --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/concat.html.md @@ -0,0 +1,23 @@ +--- +layout: "language" +page_title: "concat - Functions - Configuration Language" +sidebar_current: "docs-funcs-collection-concat" +description: |- + The concat function combines two or more lists into a single list. +--- + +# `concat` Function + +`concat` takes two or more lists and combines them into a single list. + +## Examples + +``` +> concat(["a", ""], ["b", "c"]) +[ + "a", + "", + "b", + "c", +] +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/contains.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/contains.html.md new file mode 100644 index 00000000..8ab835eb --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/contains.html.md @@ -0,0 +1,25 @@ +--- +layout: "language" +page_title: "contains - Functions - Configuration Language" +sidebar_current: "docs-funcs-collection-contains" +description: |- + The contains function determines whether a list or set contains a given value. +--- + +# `contains` Function + +`contains` determines whether a given list or set contains a given single value +as one of its elements. + +```hcl +contains(list, value) +``` + +## Examples + +``` +> contains(["a", "b", "c"], "a") +true +> contains(["a", "b", "c"], "d") +false +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/csvdecode.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/csvdecode.html.md similarity index 87% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/csvdecode.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/csvdecode.html.md index 50873744..be301628 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/csvdecode.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/csvdecode.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "csvdecode - Functions - Configuration Language" sidebar_current: "docs-funcs-encoding-csvdecode" description: |- @@ -8,10 +8,6 @@ description: |- # `csvdecode` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `csvdecode` decodes a string containing CSV-formatted data and produces a list of maps representing that data. @@ -46,7 +42,7 @@ number of fields, or this function will produce an error. ## Use with the `for_each` meta-argument You can use the result of `csvdecode` with -[the `for_each` meta-argument](/docs/configuration/resources.html#for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings) +[the `for_each` meta-argument](/docs/language/meta-arguments/for_each.html) to describe a collection of similar objects whose differences are described by the rows in the given CSV file. @@ -94,7 +90,7 @@ create or destroy associated instances as appropriate. If there is no reasonable value you can use as a unique identifier in your CSV then you could instead use -[the `count` meta-argument](/docs/configuration/resources.html#count-multiple-resource-instances-by-count) +[the `count` meta-argument](/docs/language/meta-arguments/count.html) to define an object for each CSV row, with each one identified by its index into the list returned by `csvdecode`. However, in that case any future updates to the CSV may be disruptive if they change the positions of particular objects in diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/defaults.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/defaults.html.md new file mode 100644 index 00000000..b4d68478 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/defaults.html.md @@ -0,0 +1,201 @@ +--- +layout: "language" +page_title: "defaults - Functions - Configuration Language" +sidebar_current: "docs-funcs-conversion-defaults" +description: |- + The defaults function can fill in default values in place of null values. +--- + +# `defaults` Function + +-> **Note:** This function is available only in Terraform 0.15 and later. + +~> **Experimental:** This function is part of +[the optional attributes experiment](/docs/language/expressions/type-constraints.html#experimental-optional-object-type-attributes) +and is only available in modules where the `module_variable_optional_attrs` +experiment is explicitly enabled. + +The `defaults` function is a specialized function intended for use with +input variables whose type constraints are object types or collections of +object types that include optional attributes. + +When you define an attribute as optional and the caller doesn't provide an +explicit value for it, Terraform will set the attribute to `null` to represent +that it was omitted. If you want to use a placeholder value other than `null` +when an attribute isn't set, you can use the `defaults` function to concisely +assign default values only where an attribute value was set to `null`. + +``` +defaults(input_value, defaults) +``` + +The `defaults` function expects that the `input_value` argument will be the +value of an input variable with an exact [type constraint](/docs/language/expressions/types.html) +(not containing `any`). The function will then visit every attribute in +the data structure, including attributes of nested objects, and apply the +default values given in the defaults object. + +The interpretation of attributes in the `defaults` argument depends on what +type an attribute has in the `input_value`: + +* **Primitive types** (`string`, `number`, `bool`): if a default value is given + then it will be used only if the `input_value`'s attribute of the same + name has the value `null`. The default value's type must match the input + value's type. +* **Structural types** (`object` and `tuple` types): Terraform will recursively + visit all of the attributes or elements of the nested value and repeat the + same defaults-merging logic one level deeper. The default value's type must + be of the same kind as the input value's type, and a default value for an + object type must only contain attribute names that appear in the input + value's type. +* **Collection types** (`list`, `map`, and `set` types): Terraform will visit + each of the collection elements in turn and apply defaults to them. In this + case the default value is only a single value to be applied to _all_ elements + of the collection, so it must have a type compatible with the collection's + element type rather than with the collection type itself. + +The above rules may be easier to follow with an example. Consider the following +Terraform configuration: + +```hcl +terraform { + # Optional attributes and the defaults function are + # both experimental, so we must opt in to the experiment. + experiments = [module_variable_optional_attrs] +} + +variable "storage" { + type = object({ + name = string + enabled = optional(bool) + website = object({ + index_document = optional(string) + error_document = optional(string) + }) + documents = map( + object({ + source_file = string + content_type = optional(string) + }) + ) + }) +} + +locals { + storage = defaults(var.storage, { + # If "enabled" isn't set then it will default + # to true. + enabled = true + + # The "website" attribute is required, but + # it's here to provide defaults for the + # optional attributes inside. + website = { + index_document = "index.html" + error_document = "error.html" + } + + # The "documents" attribute has a map type, + # so the default value represents defaults + # to be applied to all of the elements in + # the map, not for the map itself. Therefore + # it's a single object matching the map + # element type, not a map itself. + documents = { + # If _any_ of the map elements omit + # content_type then this default will be + # used instead. + content_type = "application/octet-stream" + } + }) +} + +output "storage" { + value = local.storage +} +``` + +To test this out, we can create a file `terraform.tfvars` to provide an example +value for `var.storage`: + +```hcl +storage = { + name = "example" + + website = { + error_document = "error.txt" + } + documents = { + "index.html" = { + source_file = "index.html.tmpl" + content_type = "text/html" + } + "error.txt" = { + source_file = "error.txt.tmpl" + content_type = "text/plain" + } + "terraform.exe" = { + source_file = "terraform.exe" + } + } +} +``` + +The above value conforms to the variable's type constraint because it only +omits attributes that are declared as optional. Terraform will automatically +populate those attributes with the value `null` before evaluating anything +else, and then the `defaults` function in `local.storage` will substitute +default values for each of them. + +The result of this `defaults` call would therefore be the following object: + +``` +storage = { + "documents" = tomap({ + "error.txt" = { + "content_type" = "text/plain" + "source_file" = "error.txt.tmpl" + } + "index.html" = { + "content_type" = "text/html" + "source_file" = "index.html.tmpl" + } + "terraform.exe" = { + "content_type" = "application/octet-stream" + "source_file" = "terraform.exe" + } + }) + "enabled" = true + "name" = "example" + "website" = { + "error_document" = "error.txt" + "index_document" = "index.html" + } +} +``` + +Notice that `enabled` and `website.index_document` were both populated directly +from the defaults. Notice also that the `"terraform.exe"` element of +`documents` had its `content_type` attribute populated from the `documents` +default, but the default value didn't need to predict that there would be an +element key `"terraform.exe"` because the default values apply equally to +all elements of the map where the optional attributes are `null`. + +## Using `defaults` elsewhere + +The design of the `defaults` function depends on input values having +well-specified type constraints, so it can reliably recognize the difference +between similar types: maps vs. objects, lists vs. tuples. The type constraint +causes Terraform to convert the caller's value to conform to the constraint +and thus `defaults` can rely on the input to conform. + +Elsewhere in the Terraform language it's typical to be less precise about +types, for example using the object construction syntax `{ ... }` to construct +values that will be used as if they are maps. Because `defaults` uses the +type information of `input_value`, an `input_value` that _doesn't_ originate +in an input variable will tend not to have an appropriate value type and will +thus not be interpreted as expected by `defaults`. + +We recommend using `defaults` only with fully-constrained input variable values +in the first argument, so you can use the variable's type constraint to +explicitly distinguish between collection and structural types. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/dirname.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/dirname.html.md similarity index 87% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/dirname.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/dirname.html.md index ddf07d91..e39150b3 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/dirname.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/dirname.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "dirname - Functions - Configuration Language" sidebar_current: "docs-funcs-file-dirname" description: |- @@ -8,10 +8,6 @@ description: |- # `dirname` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `dirname` takes a string containing a filesystem path and removes the last portion from it. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/distinct.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/distinct.html.md new file mode 100644 index 00000000..fc871471 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/distinct.html.md @@ -0,0 +1,27 @@ +--- +layout: "language" +page_title: "distinct - Functions - Configuration Language" +sidebar_current: "docs-funcs-collection-distinct" +description: |- + The distinct function removes duplicate elements from a list. +--- + +# `distinct` Function + +`distinct` takes a list and returns a new list with any duplicate elements +removed. + +The first occurrence of each value is retained and the relative ordering of +these elements is preserved. + +## Examples + +``` +> distinct(["a", "b", "a", "c", "d", "b"]) +[ + "a", + "b", + "c", + "d", +] +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/element.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/element.html.md similarity index 75% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/element.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/element.html.md index c4f85d9e..f0f6462c 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/element.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/element.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "element - Functions - Configuration Language" sidebar_current: "docs-funcs-collection-element" description: |- @@ -8,10 +8,6 @@ description: |- # `element` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `element` retrieves a single element from a list. ```hcl @@ -19,7 +15,7 @@ element(list, index) ``` The index is zero-based. This function produces an error if used with an -empty list. +empty list. The index must be a non-negative integer. Use the built-in index syntax `list[index]` in most cases. Use this function only for the special additional "wrap-around" behavior described below. @@ -39,6 +35,15 @@ If the given index is greater than the length of the list then the index is a ``` +To get the last element from the list use [`length`](./length.html) to find +the size of the list (minus 1 as the list is zero-based) and then pick the +last element: + +``` +> element(["a", "b", "c"], length(["a", "b", "c"])-1) +c +``` + ## Related Functions * [`index`](./index.html) finds the index for a particular element value. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/file.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/file.html.md new file mode 100644 index 00000000..48e96afa --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/file.html.md @@ -0,0 +1,48 @@ +--- +layout: "language" +page_title: "file - Functions - Configuration Language" +sidebar_current: "docs-funcs-file-file-x" +description: |- + The file function reads the contents of the file at the given path and + returns them as a string. +--- + +# `file` Function + +`file` reads the contents of a file at the given path and returns them as +a string. + +```hcl +file(path) +``` + +Strings in the Terraform language are sequences of Unicode characters, so +this function will interpret the file contents as UTF-8 encoded text and +return the resulting Unicode characters. If the file contains invalid UTF-8 +sequences then this function will produce an error. + +This function can be used only with files that already exist on disk +at the beginning of a Terraform run. Functions do not participate in the +dependency graph, so this function cannot be used with files that are generated +dynamically during a Terraform operation. We do not recommend using dynamic +local files in Terraform configurations, but in rare situations where this is +necessary you can use +[the `local_file` data source](https://registry.terraform.io/providers/hashicorp/local/latest/docs/data-sources/file) +to read files while respecting resource dependencies. + +## Examples + +``` +> file("${path.module}/hello.txt") +Hello World +``` + +## Related Functions + +* [`filebase64`](./filebase64.html) also reads the contents of a given file, + but returns the raw bytes in that file Base64-encoded, rather than + interpreting the contents as UTF-8 text. +* [`fileexists`](./fileexists.html) determines whether a file exists + at a given path. +* [`templatefile`](./templatefile.html) renders using a file from disk as a + template. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filebase64.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filebase64.html.md similarity index 89% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filebase64.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/filebase64.html.md index 77084051..1ae07b4f 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/filebase64.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filebase64.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "filebase64 - Functions - Configuration Language" sidebar_current: "docs-funcs-file-filebase64" description: |- @@ -9,10 +9,6 @@ description: |- # `filebase64` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `filebase64` reads the contents of a file at the given path and returns them as a base64-encoded string. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filebase64sha256.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filebase64sha256.html.md new file mode 100644 index 00000000..cedc5d3e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filebase64sha256.html.md @@ -0,0 +1,17 @@ +--- +layout: "language" +page_title: "filebase64sha256 - Functions - Configuration Language" +sidebar_current: "docs-funcs-crypto-filebase64sha256" +description: |- + The filebase64sha256 function computes the SHA256 hash of the contents of + a given file and encodes it with Base64. +--- + +# `filebase64sha256` Function + +`filebase64sha256` is a variant of [`base64sha256`](./base64sha256.html) +that hashes the contents of a given file rather than a literal string. + +This is similar to `base64sha256(file(filename))`, but +because [`file`](./file.html) accepts only UTF-8 text it cannot be used to +create hashes for binary files. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filebase64sha512.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filebase64sha512.html.md new file mode 100644 index 00000000..6844050e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filebase64sha512.html.md @@ -0,0 +1,17 @@ +--- +layout: "language" +page_title: "filebase64sha512 - Functions - Configuration Language" +sidebar_current: "docs-funcs-crypto-filebase64sha512" +description: |- + The filebase64sha512 function computes the SHA512 hash of the contents of + a given file and encodes it with Base64. +--- + +# `filebase64sha512` Function + +`filebase64sha512` is a variant of [`base64sha512`](./base64sha512.html) +that hashes the contents of a given file rather than a literal string. + +This is similar to `base64sha512(file(filename))`, but +because [`file`](./file.html) accepts only UTF-8 text it cannot be used to +create hashes for binary files. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/fileexists.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/fileexists.html.md similarity index 80% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/fileexists.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/fileexists.html.md index 0e076088..019b8e61 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/fileexists.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/fileexists.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "fileexists - Functions - Configuration Language" sidebar_current: "docs-funcs-file-file-exists" description: |- @@ -8,10 +8,6 @@ description: |- # `fileexists` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `fileexists` determines whether a file exists at a given path. ```hcl diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filemd5.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filemd5.html.md new file mode 100644 index 00000000..95658176 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filemd5.html.md @@ -0,0 +1,17 @@ +--- +layout: "language" +page_title: "filemd5 - Functions - Configuration Language" +sidebar_current: "docs-funcs-crypto-filemd5" +description: |- + The filemd5 function computes the MD5 hash of the contents of + a given file and encodes it as hex. +--- + +# `filemd5` Function + +`filemd5` is a variant of [`md5`](./md5.html) +that hashes the contents of a given file rather than a literal string. + +This is similar to `md5(file(filename))`, but +because [`file`](./file.html) accepts only UTF-8 text it cannot be used to +create hashes for binary files. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/fileset.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/fileset.html.md similarity index 85% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/fileset.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/fileset.html.md index c68234de..147ac08b 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/fileset.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/fileset.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "fileset - Functions - Configuration Language" sidebar_current: "docs-funcs-file-file-set" description: |- @@ -8,10 +8,6 @@ description: |- # `fileset` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `fileset` enumerates a set of regular file names given a path and pattern. The path is automatically removed from the resulting set of file names and any result still containing path separators always returns forward slash (`/`) as @@ -69,7 +65,7 @@ before Terraform takes any actions. ``` A common use of `fileset` is to create one resource instance per matched file, using -[the `for_each` meta-argument](/docs/configuration/resources.html#for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings): +[the `for_each` meta-argument](/docs/language/meta-arguments/for_each.html): ```hcl resource "example_thing" "example" { diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filesha1.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filesha1.html.md new file mode 100644 index 00000000..a1657638 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filesha1.html.md @@ -0,0 +1,17 @@ +--- +layout: "language" +page_title: "filesha1 - Functions - Configuration Language" +sidebar_current: "docs-funcs-crypto-filesha1" +description: |- + The filesha1 function computes the SHA1 hash of the contents of + a given file and encodes it as hex. +--- + +# `filesha1` Function + +`filesha1` is a variant of [`sha1`](./sha1.html) +that hashes the contents of a given file rather than a literal string. + +This is similar to `sha1(file(filename))`, but +because [`file`](./file.html) accepts only UTF-8 text it cannot be used to +create hashes for binary files. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filesha256.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filesha256.html.md new file mode 100644 index 00000000..2392b57f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filesha256.html.md @@ -0,0 +1,17 @@ +--- +layout: "language" +page_title: "filesha256 - Functions - Configuration Language" +sidebar_current: "docs-funcs-crypto-filesha256" +description: |- + The filesha256 function computes the SHA256 hash of the contents of + a given file and encodes it as hex. +--- + +# `filesha256` Function + +`filesha256` is a variant of [`sha256`](./sha256.html) +that hashes the contents of a given file rather than a literal string. + +This is similar to `sha256(file(filename))`, but +because [`file`](./file.html) accepts only UTF-8 text it cannot be used to +create hashes for binary files. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filesha512.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filesha512.html.md new file mode 100644 index 00000000..9786df3f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/filesha512.html.md @@ -0,0 +1,17 @@ +--- +layout: "language" +page_title: "filesha512 - Functions - Configuration Language" +sidebar_current: "docs-funcs-crypto-filesha512" +description: |- + The filesha512 function computes the SHA512 hash of the contents of + a given file and encodes it as hex. +--- + +# `filesha512` Function + +`filesha512` is a variant of [`sha512`](./sha512.html) +that hashes the contents of a given file rather than a literal string. + +This is similar to `sha512(file(filename))`, but +because [`file`](./file.html) accepts only UTF-8 text it cannot be used to +create hashes for binary files. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/flatten.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/flatten.html.md similarity index 88% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/flatten.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/flatten.html.md index 57211c41..57c81091 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/flatten.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/flatten.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "flatten - Functions - Configuration Language" sidebar_current: "docs-funcs-collection-flatten" description: |- @@ -8,10 +8,6 @@ description: |- # `flatten` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `flatten` takes a list and replaces any elements that are lists with a flattened sequence of the list contents. @@ -35,9 +31,9 @@ Indirectly-nested lists, such as those in maps, are _not_ flattened. ## Flattening nested structures for `for_each` The -[resource `for_each`](/docs/configuration/resources.html#for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings) +[resource `for_each`](/docs/language/meta-arguments/for_each.html) and -[`dynamic` block](/docs/configuration/expressions.html#dynamic-blocks) +[`dynamic` block](/docs/language/expressions/dynamic-blocks.html) language features both require a collection value that has one element for each repetition. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/floor.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/floor.html.md new file mode 100644 index 00000000..6a1ea180 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/floor.html.md @@ -0,0 +1,27 @@ +--- +layout: "language" +page_title: "floor - Functions - Configuration Language" +sidebar_current: "docs-funcs-numeric-floor" +description: |- + The floor function returns the closest whole number less than or equal to + the given value. +--- + +# `floor` Function + +`floor` returns the closest whole number that is less than or equal to the +given value, which may be a fraction. + +## Examples + +``` +> floor(5) +5 +> floor(4.9) +4 +``` + +## Related Functions + +* [`ceil`](./ceil.html), which rounds to the nearest whole number _greater than_ + or equal. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/format.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/format.html.md similarity index 96% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/format.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/format.html.md index 5d295d22..7fb8c9a8 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/format.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/format.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "format - Functions - Configuration Language" sidebar_current: "docs-funcs-string-format-x" description: |- @@ -9,10 +9,6 @@ description: |- # `format` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `format` produces a string by formatting a number of other values according to a specification string. It is similar to the `printf` function in C, and other similar functions in other programming languages. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/formatdate.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/formatdate.html.md similarity index 96% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/formatdate.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/formatdate.html.md index ee31f391..c519b6ef 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/formatdate.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/formatdate.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "formatdate - Functions - Configuration Language" sidebar_current: "docs-funcs-datetime-formatdate" description: |- @@ -8,10 +8,6 @@ description: |- # `formatdate` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `formatdate` converts a timestamp into a different time format. ```hcl diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/formatlist.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/formatlist.html.md similarity index 86% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/formatlist.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/formatlist.html.md index 8beae17d..16043611 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/formatlist.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/formatlist.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "formatlist - Functions - Configuration Language" sidebar_current: "docs-funcs-string-formatlist" description: |- @@ -9,10 +9,6 @@ description: |- # `formatlist` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `formatlist` produces a list of strings by formatting a number of other values according to a specification string. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/indent.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/indent.html.md similarity index 78% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/indent.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/indent.html.md index 54067213..40c99507 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/indent.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/indent.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "indent - Functions - Configuration Language" sidebar_current: "docs-funcs-string-indent" description: |- @@ -9,10 +9,6 @@ description: |- # `indent` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `indent` adds a given number of spaces to the beginnings of all but the first line in a given multi-line string. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/index.html.md new file mode 100644 index 00000000..f062b32a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/index.html.md @@ -0,0 +1,41 @@ +--- +layout: "language" +page_title: "Functions - Configuration Language" +sidebar_current: "docs-config-functions" +description: |- + The Terraform language has a number of built-in functions that can be called + from within expressions to transform and combine values. +--- + +# Built-in Functions + +> **Hands-on:** Try the [Perform Dynamic Operations with Functions](https://learn.hashicorp.com/tutorials/terraform/functions?in=terraform/configuration-language&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +The Terraform language includes a number of built-in functions that you can +call from within expressions to transform and combine values. The general +syntax for function calls is a function name followed by comma-separated +arguments in parentheses: + +```hcl +max(5, 12, 9) +``` + +For more details on syntax, see +[_Function Calls_](/docs/language/expressions/function-calls.html) +in the Expressions section. + +The Terraform language does not support user-defined functions, and so only +the functions built in to the language are available for use. The navigation +for this section includes a list of all of the available built-in functions. + +You can experiment with the behavior of Terraform's built-in functions from +the Terraform expression console, by running +[the `terraform console` command](/docs/cli/commands/console.html): + +``` +> max(5, 12, 9) +12 +``` + +The examples in the documentation for each function use console output to +illustrate the result of calling the function with different parameters. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/index_function.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/index_function.html.md new file mode 100644 index 00000000..6d8b3eb1 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/index_function.html.md @@ -0,0 +1,30 @@ +--- +layout: "language" +page_title: "index - Functions - Configuration Language" +sidebar_current: "docs-funcs-collection-index" +description: |- + The index function finds the element index for a given value in a list. +--- + +# `index` Function + +`index` finds the element index for a given value in a list. + +```hcl +index(list, value) +``` + +The returned index is zero-based. This function produces an error if the given +value is not present in the list. + +## Examples + +``` +> index(["a", "b", "c"], "b") +1 +``` + +## Related Functions + +* [`element`](./element.html) retrieves a particular element from a list given + its index. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/join.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/join.html.md similarity index 75% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/join.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/join.html.md index a2fc637f..e3c609fb 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/join.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/join.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "join - Functions - Configuration Language" sidebar_current: "docs-funcs-string-join" description: |- @@ -9,10 +9,6 @@ description: |- # `join` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `join` produces a string by concatenating together all elements of a given list of strings with the given delimiter. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/jsondecode.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/jsondecode.html.md similarity index 84% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/jsondecode.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/jsondecode.html.md index 6e569bb3..aacf2874 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/jsondecode.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/jsondecode.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "jsondecode - Functions - Configuration Language" sidebar_current: "docs-funcs-encoding-jsondecode" description: |- @@ -9,17 +9,13 @@ description: |- # `jsondecode` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `jsondecode` interprets a given string as JSON, returning a representation of the result of decoding that string. The JSON encoding is defined in [RFC 7159](https://tools.ietf.org/html/rfc7159). This function maps JSON values to -[Terraform language values](../expressions.html#types-and-values) +[Terraform language values](/docs/language/expressions/types.html) in the following way: | JSON type | Terraform type | diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/jsonencode.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/jsonencode.html.md similarity index 84% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/jsonencode.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/jsonencode.html.md index 6f9376ea..06f13fa8 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/jsonencode.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/jsonencode.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "jsonencode - Functions - Configuration Language" sidebar_current: "docs-funcs-encoding-jsonencode" description: |- @@ -8,16 +8,12 @@ description: |- # `jsonencode` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `jsonencode` encodes a given value to a string using JSON syntax. The JSON encoding is defined in [RFC 7159](https://tools.ietf.org/html/rfc7159). This function maps -[Terraform language values](../expressions.html#types-and-values) +[Terraform language values](/docs/language/expressions/types.html) to JSON values in the following way: | Terraform type | JSON type | diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/keys.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/keys.html.md new file mode 100644 index 00000000..46a65e48 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/keys.html.md @@ -0,0 +1,29 @@ +--- +layout: "language" +page_title: "keys - Functions - Configuration Language" +sidebar_current: "docs-funcs-collection-keys" +description: |- + The keys function returns a list of the keys in a given map. +--- + +# `keys` Function + +`keys` takes a map and returns a list containing the keys from that map. + +The keys are returned in lexicographical order, ensuring that the result will +be identical as long as the keys in the map don't change. + +## Examples + +``` +> keys({a=1, c=2, d=3}) +[ + "a", + "c", + "d", +] +``` + +## Related Functions + +* [`values`](./values.html) returns a list of the _values_ from a map. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/length.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/length.html.md similarity index 84% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/length.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/length.html.md index b4cde65c..5fd70ddb 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/length.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/length.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "length - Functions - Configuration Language" sidebar_current: "docs-funcs-collection-length" description: |- @@ -8,10 +8,6 @@ description: |- # `length` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `length` determines the length of a given list, map, or string. If given a list or map, the result is the number of elements in that collection. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/list.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/list.html.md new file mode 100644 index 00000000..3330d330 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/list.html.md @@ -0,0 +1,29 @@ +--- +layout: "language" +page_title: "list - Functions - Configuration Language" +sidebar_current: "docs-funcs-collection-list" +description: |- + The list function constructs a list from some given elements. +--- + +# `list` Function + +The `list` function is no longer available. Prior to Terraform v0.12 it was +the only available syntax for writing a literal list inside an expression, +but Terraform v0.12 introduced a new first-class syntax. + +To update an expression like `list(a, b, c)`, write the following instead: + +``` +tolist([a, b, c]) +``` + +The `[ ... ]` brackets construct a tuple value, and then the `tolist` function +then converts it to a list. For more information on the value types in the +Terraform language, see [Type Constraints](/docs/language/expressions/types.html). + +## Related Functions + +* [`concat`](./concat.html) produces a new list by concatenating together the + elements from other lists. +* [`tolist`](./tolist.html) converts a set or tuple value to a list. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/log.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/log.html.md new file mode 100644 index 00000000..28ade0a7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/log.html.md @@ -0,0 +1,36 @@ +--- +layout: "language" +page_title: "log - Functions - Configuration Language" +sidebar_current: "docs-funcs-numeric-log" +description: |- + The log function returns the logarithm of a given number in a given base. +--- + +# `log` Function + +`log` returns the logarithm of a given number in a given base. + +```hcl +log(number, base) +``` + +## Examples + +``` +> log(50, 10) +1.6989700043360185 +> log(16, 2) +4 +``` + +`log` and `ceil` can be used together to find the minimum number of binary +digits required to represent a given number of distinct values: + +``` +> ceil(log(15, 2)) +4 +> ceil(log(16, 2)) +4 +> ceil(log(17, 2)) +5 +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/lookup.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/lookup.html.md new file mode 100644 index 00000000..e41ece73 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/lookup.html.md @@ -0,0 +1,33 @@ +--- +layout: "language" +page_title: "lookup - Functions - Configuration Language" +sidebar_current: "docs-funcs-collection-lookup" +description: |- + The lookup function retrieves an element value from a map given its key. +--- + +# `lookup` Function + +`lookup` retrieves the value of a single element from a map, given its key. +If the given key does not exist, the given default value is returned instead. + +``` +lookup(map, key, default) +``` + +-> For historical reasons, the `default` parameter is actually optional. However, +omitting `default` is deprecated since v0.7 because that would then be +equivalent to the native index syntax, `map[key]`. + +## Examples + +``` +> lookup({a="ay", b="bee"}, "a", "what?") +ay +> lookup({a="ay", b="bee"}, "c", "what?") +what? +``` + +## Related Functions + +* [`element`](./element.html) retrieves a value from a _list_ given its _index_. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/lower.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/lower.html.md new file mode 100644 index 00000000..a767859f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/lower.html.md @@ -0,0 +1,27 @@ +--- +layout: "language" +page_title: "lower - Functions - Configuration Language" +sidebar_current: "docs-funcs-string-lower" +description: |- + The lower function converts all cased letters in the given string to lowercase. +--- + +# `lower` Function + +`lower` converts all cased letters in the given string to lowercase. + +## Examples + +``` +> lower("HELLO") +hello +> lower("АЛЛО!") +алло! +``` + +This function uses Unicode's definition of letters and of upper- and lowercase. + +## Related Functions + +* [`upper`](./upper.html) converts letters in a string to _uppercase_. +* [`title`](./title.html) converts the first letter of each word in a string to uppercase. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/map.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/map.html.md new file mode 100644 index 00000000..b05744d5 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/map.html.md @@ -0,0 +1,32 @@ +--- +layout: "language" +page_title: "map - Functions - Configuration Language" +sidebar_current: "docs-funcs-collection-map" +description: |- + The map function constructs a map from some given elements. +--- + +# `map` Function + +The `map` function is no longer available. Prior to Terraform v0.12 it was +the only available syntax for writing a literal map inside an expression, +but Terraform v0.12 introduced a new first-class syntax. + +To update an expression like `map("a", "b", "c", "d")`, write the following instead: + +``` +tomap({ + a = "b" + c = "d" +}) +``` + +The `{ ... }` braces construct an object value, and then the `tomap` function +then converts it to a map. For more information on the value types in the +Terraform language, see [Type Constraints](/docs/language/expressions/types.html). + +## Related Functions + +* [`tomap`](./tomap.html) converts an object value to a map. +* [`zipmap`](./zipmap.html) constructs a map dynamically, by taking keys from + one list and values from another list. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/matchkeys.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/matchkeys.html.md similarity index 90% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/matchkeys.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/matchkeys.html.md index 552fb042..390e24a2 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/matchkeys.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/matchkeys.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "matchkeys - Functions - Configuration Language" sidebar_current: "docs-funcs-collection-matchkeys" description: |- @@ -9,10 +9,6 @@ description: |- # `matchkeys` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `matchkeys` constructs a new list by taking a subset of elements from one list whose indexes match the corresponding indexes of values in another list. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/max.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/max.html.md new file mode 100644 index 00000000..33a45818 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/max.html.md @@ -0,0 +1,30 @@ +--- +layout: "language" +page_title: "max - Functions - Configuration Language" +sidebar_current: "docs-funcs-numeric-max" +description: |- + The max function takes one or more numbers and returns the greatest number. +--- + +# `max` Function + +`max` takes one or more numbers and returns the greatest number from the set. + +## Examples + +``` +> max(12, 54, 3) +54 +``` + +If the numbers are in a list or set value, use `...` to expand the collection +to individual arguments: + +``` +> max([12, 54, 3]...) +54 +``` + +## Related Functions + +* [`min`](./min.html), which returns the _smallest_ number from a set. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/md5.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/md5.html.md similarity index 81% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/md5.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/md5.html.md index ba3935d4..67c9330e 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/md5.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/md5.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "md5 - Functions - Configuration Language" sidebar_current: "docs-funcs-crypto-md5" description: |- @@ -9,10 +9,6 @@ description: |- # `md5` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `md5` computes the MD5 hash of a given string and encodes it with hexadecimal digits. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/merge.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/merge.html.md similarity index 82% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/merge.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/merge.html.md index edcc0a18..d01d551b 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/merge.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/merge.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "merge - Functions - Configuration Language" sidebar_current: "docs-funcs-collection-merge" description: |- @@ -10,10 +10,6 @@ description: |- # `merge` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `merge` takes an arbitrary number of maps or objects, and returns a single map or object that contains a merged set of elements from all arguments. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/min.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/min.html.md new file mode 100644 index 00000000..e9712dce --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/min.html.md @@ -0,0 +1,30 @@ +--- +layout: "language" +page_title: "min - Functions - Configuration Language" +sidebar_current: "docs-funcs-numeric-min" +description: |- + The min function takes one or more numbers and returns the smallest number. +--- + +# `min` Function + +`min` takes one or more numbers and returns the smallest number from the set. + +## Examples + +``` +> min(12, 54, 3) +3 +``` + +If the numbers are in a list or set value, use `...` to expand the collection +to individual arguments: + +``` +> min([12, 54, 3]...) +3 +``` + +## Related Functions + +* [`max`](./max.html), which returns the _greatest_ number from a set. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/nonsensitive.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/nonsensitive.html.md new file mode 100644 index 00000000..8458f4e7 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/nonsensitive.html.md @@ -0,0 +1,129 @@ +--- +layout: "language" +page_title: "nonsensitive - Functions - Configuration Language" +sidebar_current: "docs-funcs-conversion-sensitive" +description: |- + The nonsensitive function removes the sensitive marking from a value that Terraform considers to be sensitive. +--- + +# `nonsensitive` Function + +-> **Note:** This function is only available in Terraform v0.14 and later. + +`nonsensitive` takes a sensitive value and returns a copy of that value with +the sensitive marking removed, thereby exposing the sensitive value. + +~> **Warning:** Using this function indiscriminately will cause values that +Terraform would normally have considered as sensitive to be treated as normal +values and shown clearly in Terraform's output. Use this function only when +you've derived a new value from a sensitive value in a way that eliminates the +sensitive portions of the value. + +Normally Terraform tracks when you use expressions to derive a new value from +a value that is marked as sensitive, so that the result can also be marked +as sensitive. + +However, you may wish to write expressions that derive non-sensitive results +from sensitive values. For example, if you know based on details of your +particular system and its threat model that a SHA256 hash of a particular +sensitive value is safe to include clearly in Terraform output, you could use +the `nonsensitive` function to indicate that, overriding Terraform's normal +conservative behavior: + +```hcl +output "sensitive_example_hash" { + value = nonsensitive(sha256(var.sensitive_example)) +} +``` + +Another example might be if the original value is only partially sensitive and +you've written expressions to separate the sensitive and non-sensitive parts: + +```hcl +variable "mixed_content_json" { + description = "A JSON string containing a mixture of sensitive and non-sensitive values." + type = string + sensitive = true +} + +locals { + # mixed_content is derived from var.mixed_content_json, so it + # is also considered to be sensitive. + mixed_content = jsondecode(var.mixed_content_json) + + # password_from_json is derived from mixed_content, so it's + # also considered to be sensitive. + password_from_json = local.mixed_content["password"] + + # username_from_json would normally be considered to be + # sensitive too, but system-specific knowledge tells us + # that the username is a non-sensitive fragment of the + # original document, and so we can override Terraform's + # determination. + username_from_json = nonsensitive(local.mixed_content["username"]) +} +``` + +When you use this function, it's your responsibility to ensure that the +expression passed as its argument will remove all sensitive content from +the sensitive value it depends on. By passing a value to `nonsensitive` you are +declaring to Terraform that you have done all that is necessary to ensure that +the resulting value has no sensitive content, even though it was derived +from sensitive content. If a sensitive value appears in Terraform's output +due to an inappropriate call to `nonsensitive` in your module, that's a bug in +your module and not a bug in Terraform itself. +**Use this function sparingly and only with due care.** + +`nonsensitive` will return an error if you pass a value that isn't marked +as sensitive, because such a call would be redundant and potentially confusing +or misleading to a future maintainer of your module. Use `nonsensitive` only +after careful consideration and with definite intent. + +Consider including a comment adjacent to your call to explain to future +maintainers what makes the usage safe and thus what invariants they must take +care to preserve under future modifications. + +## Examples + +The following examples are from `terraform console` when running in the +context of the example above with `variable "mixed_content_json"` and +the local value `mixed_content`, with a valid JSON string assigned to +`var.mixed_content_json`. + +``` +> var.mixed_content_json +(sensitive) +> local.mixed_content +(sensitive) +> local.mixed_content["password"] +(sensitive) +> nonsensitive(local.mixed_content["username"]) +"zqb" +> nonsensitive("clear") + +Error: Invalid function argument + +Invalid value for "value" parameter: the given value is not sensitive, so this +call is redundant. +``` + +Note though that it's always your responsibility to use `nonsensitive` only +when it's safe to do so. If you use `nonsensitive` with content that +_ought to be_ considered sensitive then that content will be disclosed: + +``` +> nonsensitive(var.mixed_content_json) +< nonsensitive(local.mixed_content) +{ + "password" = "p4ssw0rd" + "username" = "zqb" +} +> nonsensitive(local.mixed_content["password"]) +"p4ssw0rd" +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/one.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/one.html.md new file mode 100644 index 00000000..0fc5918f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/one.html.md @@ -0,0 +1,121 @@ +--- +layout: "language" +page_title: "one - Functions - Configuration Language" +sidebar_current: "docs-funcs-collection-one" +description: |- + The 'one' function transforms a list with either zero or one elements into + either a null value or the value of the first element. +--- + +# `one` Function + +-> **Note:** This function is available only in Terraform v0.15 and later. + +`one` takes a list, set, or tuple value with either zero or one elements. +If the collection is empty, `one` returns `null`. Otherwise, `one` returns +the first element. If there are two or more elements then `one` will return +an error. + +This is a specialized function intended for the common situation where a +conditional item is represented as either a zero- or one-element list, where +a module author wishes to return a single value that might be null instead. + +For example: + +```hcl +variable "include_ec2_instance" { + type = bool + default = true +} + +resource "aws_instance" "example" { + count = var.include_ec2_instance ? 1 : 0 + + # (other resource arguments...) +} + +output "instance_ip_address" { + value = one(aws_instance.example[*].private_ip) +} +``` + +Because the `aws_instance` resource above has the `count` argument set to a +conditional that returns either zero or one, the value of +`aws_instance.example` is a list of either zero or one elements. The +`instance_ip_address` output value uses the `one` function as a concise way +to return either the private IP address of a single instance, or `null` if +no instances were created. + +## Relationship to the "Splat" Operator + +The Terraform language has a built-in operator `[*]`, known as +[the _splat_ operator](../expressions/splat.html), and one if its functions +is to translate a primitive value that might be null into a list of either +zero or one elements: + +```hcl +variable "ec2_instance_type" { + description = "The type of instance to create. If set to null, no instance will be created." + + type = string + default = null +} + +resource "aws_instance" "example" { + count = length(var.ec2_instance_type[*]) + + instance_type = var.ec2_instance_type + # (other resource arguments...) +} + +output "instance_ip_address" { + value = one(aws_instance.example[*].private_ip) +} +``` + +In this case we can see that the `one` function is, in a sense, the opposite +of applying `[*]` to a primitive-typed value. Splat can convert a possibly-null +value into a zero-or-one list, and `one` can reverse that to return to a +primitive value that might be null. + +## Examples + +``` +> one([]) +null +> one(["hello"]) +"hello" +> one(["hello", "goodbye"]) + +Error: Invalid function argument + +Invalid value for "list" parameter: must be a list, set, or tuple value with +either zero or one elements. +``` + +### Using `one` with sets + +The `one` function can be particularly helpful in situations where you have a +set that you know has only zero or one elements. Set values don't support +indexing, so it's not valid to write `var.set[0]` to extract the "first" +element of a set, but if you know that there's only one item then `one` can +isolate and return that single item: + +``` +> one(toset([])) +null +> one(toset(["hello"])) +"hello" +``` + +Don't use `one` with sets that might have more than one element. This function +will fail in that case: + +``` +> one(toset(["hello","goodbye"])) + +Error: Invalid function argument + +Invalid value for "list" parameter: must be a list, set, or tuple value with +either zero or one elements. +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/parseint.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/parseint.html.md similarity index 85% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/parseint.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/parseint.html.md index 44c7a9cf..f7ca35d5 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/parseint.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/parseint.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "parseint - Functions - Configuration Language" sidebar_current: "docs-funcs-numeric-parseint" description: |- @@ -8,10 +8,6 @@ description: |- # `parseint` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `parseint` parses the given string as a representation of an integer in the specified base and returns the resulting number. The base must be between 2 and 62 inclusive. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/pathexpand.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/pathexpand.html.md similarity index 89% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/pathexpand.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/pathexpand.html.md index 48be103a..d7fccc3b 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/pathexpand.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/pathexpand.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "pathexpand - Functions - Configuration Language" sidebar_current: "docs-funcs-file-pathexpand" description: |- @@ -9,10 +9,6 @@ description: |- # `pathexpand` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `pathexpand` takes a filesystem path that might begin with a `~` segment, and if so it replaces that segment with the current user's home directory path. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/pow.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/pow.html.md new file mode 100644 index 00000000..baf73014 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/pow.html.md @@ -0,0 +1,20 @@ +--- +layout: "language" +page_title: "pow - Functions - Configuration Language" +sidebar_current: "docs-funcs-numeric-pow" +description: |- + The pow function raises a number to a power. +--- + +# `pow` Function + +`pow` calculates an exponent, by raising its first argument to the power of the second argument. + +## Examples + +``` +> pow(3, 2) +9 +> pow(4, 0) +1 +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/range.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/range.html.md similarity index 92% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/range.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/range.html.md index 21bf4a3b..bdb538ec 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/range.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/range.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "range - Functions - Configuration Language" sidebar_current: "docs-funcs-collection-range" description: |- @@ -8,10 +8,6 @@ description: |- # `range` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `range` generates a list of numbers using a start value, a limit value, and a step value. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/regex.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/regex.html.md similarity index 97% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/regex.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/regex.html.md index 875ac65b..790870b9 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/regex.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/regex.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "regex - Functions - Configuration Language" sidebar_current: "docs-funcs-string-regex" description: |- @@ -9,10 +9,6 @@ description: |- # `regex` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `regex` applies a [regular expression](https://en.wikipedia.org/wiki/Regular_expression) to a string and returns the matching substrings. @@ -77,7 +73,7 @@ of the pattern must be escaped as `\\`. | `\PN` | The opposite of `\pN` | | `\P{Greek}` | The opposite of `\p{Greek}` | | `xy` | `x` followed immediately by `y` | -| `x|y` | either `x` or `y`, preferring `x` | +| x|y | either `x` or `y`, preferring `x` | | `x*` | zero or more `x`, preferring more | | `x*?` | zero or more `x`, preferring fewer | | `x+` | one or more `x`, preferring more | diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/regexall.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/regexall.html.md similarity index 88% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/regexall.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/regexall.html.md index fc8f495c..5b5928d8 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/regexall.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/regexall.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "regexall - Functions - Configuration Language" sidebar_current: "docs-funcs-string-regexall" description: |- @@ -8,10 +8,6 @@ description: |- # `regexall` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `regexall` applies a [regular expression](https://en.wikipedia.org/wiki/Regular_expression) to a string and returns a list of all matches. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/replace.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/replace.html.md similarity index 83% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/replace.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/replace.html.md index ea2dd711..8475e26a 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/replace.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/replace.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "replace - Functions - Configuration Language" sidebar_current: "docs-funcs-string-replace" description: |- @@ -9,10 +9,6 @@ description: |- # `replace` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `replace` searches a given string for another given substring, and replaces each occurrence with a given replacement string. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/reverse.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/reverse.html.md new file mode 100644 index 00000000..deeb3a17 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/reverse.html.md @@ -0,0 +1,27 @@ +--- +layout: "language" +page_title: "reverse - Functions - Configuration Language" +sidebar_current: "docs-funcs-collection-reverse" +description: |- + The reverse function reverses a sequence. +--- + +# `reverse` Function + +`reverse` takes a sequence and produces a new sequence of the same length +with all of the same elements as the given sequence but in reverse order. + +## Examples + +``` +> reverse([1, 2, 3]) +[ + 3, + 2, + 1, +] +``` + +## Related Functions + +* [`strrev`](./strrev.html) reverses a string. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/rsadecrypt.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/rsadecrypt.html.md similarity index 82% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/rsadecrypt.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/rsadecrypt.html.md index 6b09ffa6..1cf0b04b 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/rsadecrypt.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/rsadecrypt.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "rsadecrypt - Functions - Configuration Language" sidebar_current: "docs-funcs-crypto-rsadecrypt" description: |- @@ -8,10 +8,6 @@ description: |- # `rsadecrypt` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `rsadecrypt` decrypts an RSA-encrypted ciphertext, returning the corresponding cleartext. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/sensitive.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/sensitive.html.md new file mode 100644 index 00000000..bcc9bfec --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/sensitive.html.md @@ -0,0 +1,45 @@ +--- +layout: "language" +page_title: "sensitive - Functions - Configuration Language" +sidebar_current: "docs-funcs-conversion-sensitive" +description: |- + The sensitive function marks a value as being sensitive. +--- + +# `sensitive` Function + +-> **Note:** This function is only available in Terraform v0.14 and later. + +`sensitive` takes any value and returns a copy of it marked so that Terraform +will treat it as sensitive, with the same meaning and behavior as for +[sensitive input variables](/docs/language/values/variables.html#suppressing-values-in-cli-output). + +Whereever possible we recommend marking your input variable and/or output value +declarations as sensitive directly, instead of using this function, because +in that case you can be sure that there is no way to refer to those values +without Terraform automatically considering them as sensitive. + +The `sensitive` function might be useful in some less-common situations where a +sensitive value arises from a definition _within_ your module, such as if +you've loaded sensitive data from a file on disk as part of your configuration: + +``` +locals { + sensitive_content = sensitive(file("${path.module}/sensitive.txt")) +} +``` + +However, we generally don't recommend writing sensitive values directly within +your module any of the files you distribute statically as part of that module, +because they may be exposed in other ways outside of Terraform's control. + +## Examples + +``` +> sensitive(1) +(sensitive) +> sensitive("hello") +(sensitive) +> sensitive([]) +(sensitive) +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/setintersection.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/setintersection.html.md similarity index 85% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/setintersection.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/setintersection.html.md index 6444aeb8..c582cc0c 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/setintersection.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/setintersection.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "setintersection - Functions - Configuration Language" sidebar_current: "docs-funcs-collection-setintersection" description: |- @@ -9,10 +9,6 @@ description: |- # `setintersection` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - The `setintersection` function takes multiple sets and produces a single set containing only the elements that all of the given sets have in common. In other words, it computes the diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/setproduct.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/setproduct.html.md similarity index 92% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/setproduct.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/setproduct.html.md index 6829ce8e..030ab2c3 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/setproduct.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/setproduct.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "setproduct - Functions - Configuration Language" sidebar_current: "docs-funcs-collection-setproduct" description: |- @@ -9,10 +9,6 @@ description: |- # `setproduct` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - The `setproduct` function finds all of the possible combinations of elements from all of the given sets by computing the [Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product). @@ -121,9 +117,9 @@ elements all have a consistent type: ## Finding combinations for `for_each` The -[resource `for_each`](/docs/configuration/resources.html#for_each-multiple-resource-instances-defined-by-a-map-or-set-of-strings) +[resource `for_each`](/docs/language/meta-arguments/for_each.html) and -[`dynamic` block](/docs/configuration/expressions.html#dynamic-blocks) +[`dynamic` block](/docs/language/expressions/dynamic-blocks.html) language features both require a collection value that has one element for each repetition. @@ -208,7 +204,7 @@ resource "aws_subnet" "example" { vpc_id = each.value.network_id availability_zone = each.value.subnet_key - cidr_block = each.value_cidr_block + cidr_block = each.value.cidr_block } ``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/setsubtract.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/setsubtract.html.md similarity index 84% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/setsubtract.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/setsubtract.html.md index 0bf3b7ac..8b95b203 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/setsubtract.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/setsubtract.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "setsubtract - Functions - Configuration Language" sidebar_current: "docs-funcs-collection-setsubtract" description: |- @@ -9,10 +9,6 @@ description: |- # `setsubtract` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - The `setsubtract` function returns a new set containing the elements from the first set that are not present in the second set. In other words, it computes the [relative complement](https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement) of the first set in the second set. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/setunion.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/setunion.html.md similarity index 84% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/setunion.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/setunion.html.md index 41103e58..2784f5d8 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/setunion.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/setunion.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "setunion - Functions - Configuration Language" sidebar_current: "docs-funcs-collection-setunion" description: |- @@ -9,10 +9,6 @@ description: |- # `setunion` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - The `setunion` function takes multiple sets and produces a single set containing the elements from all of the given sets. In other words, it computes the [union](https://en.wikipedia.org/wiki/Union_(set_theory)) of diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/sha1.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/sha1.html.md similarity index 82% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/sha1.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/sha1.html.md index df44b43a..3b1b7593 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/sha1.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/sha1.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "sha1 - Functions - Configuration Language" sidebar_current: "docs-funcs-crypto-sha1" description: |- @@ -9,10 +9,6 @@ description: |- # `sha1` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `sha1` computes the SHA1 hash of a given string and encodes it with hexadecimal digits. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/sha256.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/sha256.html.md similarity index 82% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/sha256.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/sha256.html.md index e044e7d5..c71157d8 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/sha256.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/sha256.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "sha256 - Functions - Configuration Language" sidebar_current: "docs-funcs-crypto-sha256" description: |- @@ -9,10 +9,6 @@ description: |- # `sha256` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `sha256` computes the SHA256 hash of a given string and encodes it with hexadecimal digits. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/sha512.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/sha512.html.md similarity index 82% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/sha512.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/sha512.html.md index deb1ab3b..b36c6bf9 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/sha512.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/sha512.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "sha512 - Functions - Configuration Language" sidebar_current: "docs-funcs-crypto-sha512" description: |- @@ -9,10 +9,6 @@ description: |- # `sha512` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `sha512` computes the SHA512 hash of a given string and encodes it with hexadecimal digits. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/signum.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/signum.html.md new file mode 100644 index 00000000..e8136588 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/signum.html.md @@ -0,0 +1,23 @@ +--- +layout: "language" +page_title: "signum - Functions - Configuration Language" +sidebar_current: "docs-funcs-numeric-signum" +description: |- + The signum function determines the sign of a number. +--- + +# `signum` Function + +`signum` determines the sign of a number, returning a number between -1 and +1 to represent the sign. + +## Examples + +``` +> signum(-13) +-1 +> signum(0) +0 +> signum(344) +1 +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/slice.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/slice.html.md similarity index 77% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/slice.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/slice.html.md index 44c88f2c..d6672386 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/slice.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/slice.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "slice - Functions - Configuration Language" sidebar_current: "docs-funcs-collection-slice" description: |- @@ -8,10 +8,6 @@ description: |- # `slice` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `slice` extracts some consecutive elements from within a list. ```hcl diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/sort.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/sort.html.md new file mode 100644 index 00000000..6e50ae9f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/sort.html.md @@ -0,0 +1,28 @@ +--- +layout: "language" +page_title: "sort - Functions - Configuration Language" +sidebar_current: "docs-funcs-collection-sort" +description: |- + The sort function takes a list of strings and returns a new list with those + strings sorted lexicographically. +--- + +# `sort` Function + +`sort` takes a list of strings and returns a new list with those strings +sorted lexicographically. + +The sort is in terms of Unicode codepoints, with higher codepoints appearing +after lower ones in the result. + +## Examples + +``` +> sort(["e", "d", "a", "x"]) +[ + "a", + "d", + "e", + "x", +] +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/split.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/split.html.md similarity index 76% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/split.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/split.html.md index 49b5ece5..a777b7b5 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/split.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/split.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "split - Functions - Configuration Language" sidebar_current: "docs-funcs-string-split" description: |- @@ -9,10 +9,6 @@ description: |- # `split` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `split` produces a list by dividing a given string at all occurrences of a given separator. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/strrev.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/strrev.html.md similarity index 96% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/strrev.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/strrev.html.md index 630a6826..e2361bf5 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/strrev.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/strrev.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "strrev - Functions - Configuration Language" sidebar_current: "docs-funcs-string-strrev" description: |- diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/substr.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/substr.html.md new file mode 100644 index 00000000..21b3bbc3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/substr.html.md @@ -0,0 +1,31 @@ +--- +layout: "language" +page_title: "substr - Functions - Configuration Language" +sidebar_current: "docs-funcs-string-substr" +description: |- + The substr function extracts a substring from a given string by offset and + length. +--- + +# `substr` Function + +`substr` extracts a substring from a given string by offset and length. + +```hcl +substr(string, offset, length) +``` + +## Examples + +``` +> substr("hello world", 1, 4) +ello +``` + +The offset and length are both counted in _unicode characters_ rather than +bytes: + +``` +> substr("🤔🤷", 0, 1) +🤔 +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/sum.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/sum.html.md new file mode 100644 index 00000000..63c24184 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/sum.html.md @@ -0,0 +1,20 @@ +--- +layout: "language" +page_title: "sum - Functions - Configuration Language" +sidebar_current: "docs-funcs-collection-sum" +description: |- + The sum function takes a list or set of numbers and returns the sum of those + numbers. +--- + +# `sum` Function + +`sum` takes a list or set of numbers and returns the sum of those numbers. + + +## Examples + +``` +> sum([10, 13, 6, 4.5]) +33.5 +``` \ No newline at end of file diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/templatefile.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/templatefile.html.md similarity index 83% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/templatefile.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/templatefile.html.md index 64d7cf63..95bc2b04 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/templatefile.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/templatefile.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "templatefile - Functions - Configuration Language" sidebar_current: "docs-funcs-file-templatefile" description: |- @@ -9,10 +9,6 @@ description: |- # `templatefile` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `templatefile` reads the file at the given path and renders its content as a template using a supplied set of template variables. @@ -21,10 +17,10 @@ templatefile(path, vars) ``` The template syntax is the same as for -[string templates](../expressions.html#string-templates) in the main Terraform -language, including interpolation sequences delimited with `${` ... `}`. -This function just allows longer template sequences to be factored out -into a separate file for readability. +[string templates](/docs/language/expressions/strings.html#string-templates) +in the main Terraform language, including interpolation sequences delimited with +`${` ... `}`. This function just allows longer template sequences to be factored +out into a separate file for readability. The "vars" argument must be a map. Within the template file, each of the keys in the map is available as a variable for interpolation. The template may @@ -78,8 +74,8 @@ The `templatefile` function renders the template: ``` > templatefile( - "${path.module}/backends.tmpl", - { + "${path.module}/config.tmpl", + { config = { "x" = "y" "foo" = "bar" @@ -102,7 +98,7 @@ interpolation sequences and directives. Instead, you can write a template that consists only of a single interpolated call to either [`jsonencode`](./jsonencode.html) or [`yamlencode`](./yamlencode.html), specifying the value to encode using -[normal Terraform expression syntax](/docs/configuration/expressions.html) +[normal Terraform expression syntax](/docs/language/expressions/index.html) as in the following examples: ``` @@ -122,9 +118,9 @@ this will produce a valid JSON or YAML representation of the given data structure, without the need to manually handle escaping or delimiters. In the latest examples above, the repetition based on elements of `ip_addrs` is achieved by using a -[`for` expression](/docs/configuration/expressions.html#for-expressions) +[`for` expression](/docs/language/expressions/for.html) rather than by using -[template directives](/docs/configuration/expressions.html#directives). +[template directives](/docs/language/expressions/strings.html#directives). ```json {"backends":["10.0.0.1:8080","10.0.0.2:8080"]} diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/textdecodebase64.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/textdecodebase64.html.md similarity index 93% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/textdecodebase64.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/textdecodebase64.html.md index 6d04b30c..428dd5ca 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/textdecodebase64.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/textdecodebase64.html.md @@ -1,10 +1,10 @@ --- -layout: "functions" +layout: "language" page_title: "textdecodebase64 - Functions - Configuration Language" sidebar_current: "docs-funcs-encoding-textdecodebase64" description: |- The textdecodebase64 function decodes a string that was previously Base64-encoded, -and then interprets the result as characters in a specified character encoding. + and then interprets the result as characters in a specified character encoding. --- # `textdecodebase64` Function diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/textencodebase64.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/textencodebase64.html.md similarity index 95% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/textencodebase64.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/textencodebase64.html.md index 02e43669..3eec900b 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/textencodebase64.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/textencodebase64.html.md @@ -1,10 +1,10 @@ --- -layout: "functions" +layout: "language" page_title: "textencodebase64 - Functions - Configuration Language" sidebar_current: "docs-funcs-encoding-textencodebase64" description: |- The textencodebase64 function encodes the unicode characters in a given string using a -specified character encoding, returning the result base64 encoded. + specified character encoding, returning the result base64 encoded. --- # `textencodebase64` Function diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/timeadd.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/timeadd.html.md similarity index 83% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/timeadd.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/timeadd.html.md index 926806c6..5ba5800d 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/timeadd.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/timeadd.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "timeadd - Functions - Configuration Language" sidebar_current: "docs-funcs-datetime-timeadd" description: |- @@ -9,10 +9,6 @@ description: |- # `timeadd` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `timeadd` adds a duration to a timestamp, returning a new timestamp. ```hcl diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/timestamp.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/timestamp.html.md similarity index 82% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/timestamp.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/timestamp.html.md index e3365406..5ad73efd 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/timestamp.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/timestamp.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "timestamp - Functions - Configuration Language" sidebar_current: "docs-funcs-datetime-timestamp" description: |- @@ -9,10 +9,6 @@ description: |- # `timestamp` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `timestamp` returns a UTC timestamp string in [RFC 3339](https://tools.ietf.org/html/rfc3339) format. In the Terraform language, timestamps are conventionally represented as @@ -24,7 +20,7 @@ The result of this function will change every second, so using this function directly with resource attributes will cause a diff to be detected on every Terraform run. We do not recommend using this function in resource attributes, but in rare cases it can be used in conjunction with -[the `ignore_changes` lifecycle meta-argument](../resources.html#ignore_changes) +[the `ignore_changes` lifecycle meta-argument](/docs/language/meta-arguments/lifecycle.html#ignore_changes) to take the timestamp only on initial creation of the resource. For more stable time handling, see the [Time Provider](https://registry.terraform.io/providers/hashicorp/time/). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/title.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/title.html.md new file mode 100644 index 00000000..fef24ddd --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/title.html.md @@ -0,0 +1,26 @@ +--- +layout: "language" +page_title: "title - Functions - Configuration Language" +sidebar_current: "docs-funcs-string-title" +description: |- + The title function converts the first letter of each word in a given string + to uppercase. +--- + +# `title` Function + +`title` converts the first letter of each word in the given string to uppercase. + +## Examples + +``` +> title("hello world") +Hello World +``` + +This function uses Unicode's definition of letters and of upper- and lowercase. + +## Related Functions + +* [`upper`](./upper.html) converts _all_ letters in a string to uppercase. +* [`lower`](./lower.html) converts all letters in a string to lowercase. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/tobool.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/tobool.html.md similarity index 81% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/tobool.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/tobool.html.md index cfb8c83c..4ff16092 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/tobool.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/tobool.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "tobool - Functions - Configuration Language" sidebar_current: "docs-funcs-conversion-tobool" description: |- @@ -8,10 +8,6 @@ description: |- # `tobool` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `tobool` converts its argument to a boolean value. Explicit type conversions are rarely necessary in Terraform because it will diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/tolist.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/tolist.html.md similarity index 81% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/tolist.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/tolist.html.md index 3c114a76..682a7e53 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/tolist.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/tolist.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "tolist - Functions - Configuration Language" sidebar_current: "docs-funcs-conversion-tolist" description: |- @@ -8,10 +8,6 @@ description: |- # `tolist` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `tolist` converts its argument to a list value. Explicit type conversions are rarely necessary in Terraform because it will diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/tomap.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/tomap.html.md similarity index 78% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/tomap.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/tomap.html.md index 7adc8193..da28a4d8 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/tomap.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/tomap.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "tomap - Functions - Configuration Language" sidebar_current: "docs-funcs-conversion-tomap" description: |- @@ -8,10 +8,6 @@ description: |- # `tomap` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `tomap` converts its argument to a map value. Explicit type conversions are rarely necessary in Terraform because it will diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/tonumber.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/tonumber.html.md similarity index 79% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/tonumber.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/tonumber.html.md index d4e6927e..1b7e0236 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/tonumber.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/tonumber.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "tonumber - Functions - Configuration Language" sidebar_current: "docs-funcs-conversion-tonumber" description: |- @@ -8,10 +8,6 @@ description: |- # `tonumber` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `tonumber` converts its argument to a number value. Explicit type conversions are rarely necessary in Terraform because it will diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/toset.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/toset.html.md similarity index 83% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/toset.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/toset.html.md index 2a63f183..0dc2e73c 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/toset.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/toset.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "toset - Functions - Configuration Language" sidebar_current: "docs-funcs-conversion-toset" description: |- @@ -8,10 +8,6 @@ description: |- # `toset` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `toset` converts its argument to a set value. Explicit type conversions are rarely necessary in Terraform because it will diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/tostring.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/tostring.html.md similarity index 78% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/tostring.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/tostring.html.md index 2cec0090..667b3619 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/tostring.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/tostring.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "tostring - Functions - Configuration Language" sidebar_current: "docs-funcs-conversion-tostring" description: |- @@ -8,10 +8,6 @@ description: |- # `tostring` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `tostring` converts its argument to a string value. Explicit type conversions are rarely necessary in Terraform because it will diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/transpose.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/transpose.html.md new file mode 100644 index 00000000..dde38dac --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/transpose.html.md @@ -0,0 +1,31 @@ +--- +layout: "language" +page_title: "transpose - Functions - Configuration Language" +sidebar_current: "docs-funcs-collection-transpose" +description: |- + The transpose function takes a map of lists of strings and swaps the keys + and values. +--- + +# `transpose` Function + +`transpose` takes a map of lists of strings and swaps the keys and values +to produce a new map of lists of strings. + +## Examples + +``` +> transpose({"a" = ["1", "2"], "b" = ["2", "3"]}) +{ + "1" = [ + "a", + ], + "2" = [ + "a", + "b", + ], + "3" = [ + "b", + ], +} +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/trim.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/trim.html.md similarity index 75% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/trim.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/trim.html.md index f6402a83..43f12f2a 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/trim.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/trim.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "trim - Functions - Configuration Language" sidebar_current: "docs-funcs-string-trim" description: |- @@ -9,10 +9,6 @@ description: |- # `trim` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `trim` removes the specified characters from the start and end of the given string. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/trimprefix.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/trimprefix.html.md similarity index 79% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/trimprefix.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/trimprefix.html.md index cc9e8a0e..e1058ffb 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/trimprefix.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/trimprefix.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "trimprefix - Functions - Configuration Language" sidebar_current: "docs-funcs-string-trimprefix" description: |- @@ -9,10 +9,6 @@ description: |- # `trimprefix` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `trimprefix` removes the specified prefix from the start of the given string. If the string does not start with the prefix, the string is returned unchanged. ## Examples diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/trimspace.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/trimspace.html.md similarity index 75% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/trimspace.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/trimspace.html.md index a7f4ad38..b4f1a40d 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/trimspace.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/trimspace.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "trimspace - Functions - Configuration Language" sidebar_current: "docs-funcs-string-trimspace" description: |- @@ -9,10 +9,6 @@ description: |- # `trimspace` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `trimspace` removes any space characters from the start and end of the given string. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/trimsuffix.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/trimsuffix.html.md similarity index 76% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/trimsuffix.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/trimsuffix.html.md index aec89868..727c31b5 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/trimsuffix.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/trimsuffix.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "trimsuffix - Functions - Configuration Language" sidebar_current: "docs-funcs-string-trimsuffix" description: |- @@ -9,10 +9,6 @@ description: |- # `trimsuffix` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `trimsuffix` removes the specified suffix from the end of the given string. ## Examples diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/try.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/try.html.md similarity index 94% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/try.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/try.html.md index 57644bb5..d389a066 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/try.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/try.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "try - Functions - Configuration Language" sidebar_current: "docs-funcs-conversion-try" description: |- @@ -10,10 +10,6 @@ description: |- # `try` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `try` evaluates all of its argument expressions in turn and returns the result of the first one that does not produce any errors. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/upper.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/upper.html.md new file mode 100644 index 00000000..c2fe3757 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/upper.html.md @@ -0,0 +1,27 @@ +--- +layout: "language" +page_title: "upper - Functions - Configuration Language" +sidebar_current: "docs-funcs-string-upper" +description: |- + The upper function converts all cased letters in the given string to uppercase. +--- + +# `upper` Function + +`upper` converts all cased letters in the given string to uppercase. + +## Examples + +``` +> upper("hello") +HELLO +> upper("алло!") +АЛЛО! +``` + +This function uses Unicode's definition of letters and of upper- and lowercase. + +## Related Functions + +* [`lower`](./lower.html) converts letters in a string to _lowercase_. +* [`title`](./title.html) converts the first letter of each word in a string to uppercase. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/urlencode.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/urlencode.html.md similarity index 83% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/urlencode.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/urlencode.html.md index 1f6fda45..d5c39615 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/urlencode.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/urlencode.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "urlencode - Functions - Configuration Language" sidebar_current: "docs-funcs-encoding-urlencode" description: |- @@ -8,10 +8,6 @@ description: |- # `urlencode` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `urlencode` applies URL encoding to a given string. This function identifies characters in the given string that would have a diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/functions/uuid.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/uuid.html.md new file mode 100644 index 00000000..b801bf66 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/uuid.html.md @@ -0,0 +1,40 @@ +--- +layout: "language" +page_title: "uuid - Functions - Configuration Language" +sidebar_current: "docs-funcs-crypto-uuid" +description: |- + The uuid function generates a unique id. +--- + +# `uuid` Function + +`uuid` generates a unique identifier string. + +The id is a generated and formatted as required by +[RFC 4122 section 4.4](https://tools.ietf.org/html/rfc4122#section-4.4), +producing a Version 4 UUID. The result is a UUID generated only from +pseudo-random numbers. + +This function produces a new value each time it is called, and so using it +directly in resource arguments will result in spurious diffs. We do not +recommend using the `uuid` function in resource configurations, but it can +be used with care in conjunction with +[the `ignore_changes` lifecycle meta-argument](/docs/language/meta-arguments/lifecycle.html#ignore_changes). + +In most cases we recommend using [the `random` provider](https://registry.terraform.io/providers/hashicorp/random/latest/docs) +instead, since it allows the one-time generation of random values that are +then retained in the Terraform [state](/docs/language/state/index.html) for use by +future operations. In particular, +[`random_id`](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) can generate results with +equivalent randomness to the `uuid` function. + +## Examples + +``` +> uuid() +b5ee72a3-54dd-c4b8-551c-4bdc0204cedb +``` + +## Related Functions + +* [`uuidv5`](./uuidv5.html), which generates name-based UUIDs. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/uuidv5.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/uuidv5.html.md similarity index 92% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/uuidv5.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/uuidv5.html.md index d4883182..24efce01 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/uuidv5.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/uuidv5.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "uuidv5 - Functions - Configuration Language" sidebar_current: "docs-funcs-crypto-uuidv5" description: |- @@ -8,10 +8,6 @@ description: |- # `uuidv5` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `uuidv5` generates a _name-based_ UUID, as described in [RFC 4122 section 4.3](https://tools.ietf.org/html/rfc4122#section-4.3), also known as a "version 5" UUID. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/values.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/values.html.md similarity index 75% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/values.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/values.html.md index a74aa0b0..4acc3772 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/values.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/values.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "values - Functions - Configuration Language" sidebar_current: "docs-funcs-collection-values" description: |- @@ -8,10 +8,6 @@ description: |- # `values` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `values` takes a map and returns a list containing the values of the elements in that map. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/yamldecode.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/yamldecode.html.md similarity index 91% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/yamldecode.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/yamldecode.html.md index e276d858..0b900059 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/yamldecode.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/yamldecode.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "yamldecode - Functions - Configuration Language" sidebar_current: "docs-funcs-encoding-yamldecode" description: |- @@ -9,10 +9,6 @@ description: |- # `yamldecode` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `yamldecode` parses a string as a subset of YAML, and produces a representation of its value. @@ -20,7 +16,7 @@ This function supports a subset of [YAML 1.2](https://yaml.org/spec/1.2/spec.htm as described below. This function maps YAML values to -[Terraform language values](../expressions.html#types-and-values) +[Terraform language values](/docs/language/expressions/types.html) in the following way: | YAML type | Terraform type | @@ -61,7 +57,7 @@ supports only a subset of YAML 1.2, with restrictions including the following: ## Examples ``` -> yamldecode("{\"hello\": \"world\"}") +> yamldecode("hello: world") { "hello" = "world" } diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/yamlencode.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/yamlencode.html.md similarity index 91% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/yamlencode.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/yamlencode.html.md index c562f854..aa823dcd 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/yamlencode.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/yamlencode.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "yamlencode - Functions - Configuration Language" sidebar_current: "docs-funcs-encoding-yamlencode" description: |- @@ -8,10 +8,6 @@ description: |- # `yamlencode` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `yamlencode` encodes a given value to a string using [YAML 1.2](https://yaml.org/spec/1.2/spec.html) block syntax. @@ -33,7 +29,7 @@ results are also valid YAML because YAML is a JSON superset. --> This function maps -[Terraform language values](../expressions.html#types-and-values) +[Terraform language values](/docs/language/expressions/types.html) to YAML tags in the following way: | Terraform type | YAML type | diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/zipmap.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/zipmap.html.md similarity index 79% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/zipmap.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/functions/zipmap.html.md index 61b203e9..d37926f7 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/functions/zipmap.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/functions/zipmap.html.md @@ -1,5 +1,5 @@ --- -layout: "functions" +layout: "language" page_title: "zipmap - Functions - Configuration Language" sidebar_current: "docs-funcs-collection-zipmap" description: |- @@ -9,10 +9,6 @@ description: |- # `zipmap` Function --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). - `zipmap` constructs a map from a list of keys and a corresponding list of values. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/index.html.md new file mode 100644 index 00000000..49067f80 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/index.html.md @@ -0,0 +1,118 @@ +--- +layout: "language" +page_title: "Overview - Configuration Language" +--- + +# Terraform Language Documentation + +This is the documentation for Terraform's configuration language. It is relevant +to users of [Terraform CLI](/docs/cli/index.html), +[Terraform Cloud](/docs/cloud/index.html), and +[Terraform Enterprise](/docs/enterprise/index.html). + +> **Hands-on:** Try the [Terraform: Get Started](https://learn.hashicorp.com/collections/terraform/aws-get-started?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. + +_The Terraform language is Terraform's primary user interface._ In every edition +of Terraform, a configuration written in the Terraform language is always at the +heart of the workflow. + +## About the Terraform Language + +The main purpose of the Terraform language is declaring +[resources](/docs/language/resources/index.html), which represent infrastructure objects. All other +language features exist only to make the definition of resources more flexible +and convenient. + +A _Terraform configuration_ is a complete document in the Terraform language +that tells Terraform how to manage a given collection of infrastructure. A +configuration can consist of multiple files and directories. + +The syntax of the Terraform language consists of only a few basic elements: + +```hcl +resource "aws_vpc" "main" { + cidr_block = var.base_cidr_block +} + + "" "" { + # Block body + = # Argument +} +``` + +- _Blocks_ are containers for other content and usually represent the + configuration of some kind of object, like a resource. Blocks have a + _block type,_ can have zero or more _labels,_ and have a _body_ that contains + any number of arguments and nested blocks. Most of Terraform's features are + controlled by top-level blocks in a configuration file. +- _Arguments_ assign a value to a name. They appear within blocks. +- _Expressions_ represent a value, either literally or by referencing and + combining other values. They appear as values for arguments, or within other + expressions. + +The Terraform language is declarative, describing an intended goal rather than +the steps to reach that goal. The ordering of blocks and the files they are +organized into are generally not significant; Terraform only considers implicit +and explicit relationships between resources when determining an order of +operations. + +### Example + +The following example describes a simple network topology for Amazon Web +Services, just to give a sense of the overall structure and syntax of the +Terraform language. Similar configurations can be created for other virtual +network services, using resource types defined by other providers, and a +practical network configuration will often contain additional elements not +shown here. + +```hcl +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 1.0.4" + } + } +} + +variable "aws_region" {} + +variable "base_cidr_block" { + description = "A /16 CIDR range definition, such as 10.1.0.0/16, that the VPC will use" + default = "10.1.0.0/16" +} + +variable "availability_zones" { + description = "A list of availability zones in which to create subnets" + type = list(string) +} + +provider "aws" { + region = var.aws_region +} + +resource "aws_vpc" "main" { + # Referencing the base_cidr_block variable allows the network address + # to be changed without modifying the configuration. + cidr_block = var.base_cidr_block +} + +resource "aws_subnet" "az" { + # Create one subnet for each given availability zone. + count = length(var.availability_zones) + + # For each subnet, use one of the specified availability zones. + availability_zone = var.availability_zones[count.index] + + # By referencing the aws_vpc.main object, Terraform knows that the subnet + # must be created only after the VPC is created. + vpc_id = aws_vpc.main.id + + # Built-in functions and operators can be used for simple transformations of + # values, such as computing a subnet address. Here we create a /20 prefix for + # each subnet, using consecutive addresses for each availability zone, + # such as 10.1.16.0/20 . + cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 4, count.index+1) +} +``` + diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/meta-arguments/count.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/meta-arguments/count.html.md new file mode 100644 index 00000000..4e92bdb9 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/meta-arguments/count.html.md @@ -0,0 +1,122 @@ +--- +layout: "language" +page_title: "The count Meta-Argument - Configuration Language" +--- + +# The `count` Meta-Argument + +-> **Version note:** Module support for `count` was added in Terraform 0.13, and +previous versions can only use it with resources. + +-> **Note:** A given resource or module block cannot use both `count` and `for_each`. + +> **Hands-on:** Try the [Manage Similar Resources With Count](https://learn.hashicorp.com/tutorials/terraform/count?in=terraform/0-13&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +By default, a [resource block](/docs/language/resources/syntax.html) configures one real +infrastructure object. (Similarly, a +[module block](/docs/language/modules/syntax.html) includes a +child module's contents into the configuration one time.) +However, sometimes you want to manage several similar objects (like a fixed +pool of compute instances) without writing a separate block for each one. +Terraform has two ways to do this: +`count` and [`for_each`](/docs/language/meta-arguments/for_each.html). + +If a resource or module block includes a `count` argument whose value is a whole number, +Terraform will create that many instances. + +## Basic Syntax + +`count` is a meta-argument defined by the Terraform language. It can be used +with modules and with every resource type. + +The `count` meta-argument accepts a whole number, and creates that many +instances of the resource or module. Each instance has a distinct infrastructure object +associated with it, and each is separately created, +updated, or destroyed when the configuration is applied. + +```hcl +resource "aws_instance" "server" { + count = 4 # create four similar EC2 instances + + ami = "ami-a1b2c3d4" + instance_type = "t2.micro" + + tags = { + Name = "Server ${count.index}" + } +} +``` + +## The `count` Object + +In blocks where `count` is set, an additional `count` object is +available in expressions, so you can modify the configuration of each instance. +This object has one attribute: + +- `count.index` — The distinct index number (starting with `0`) corresponding + to this instance. + +## Using Expressions in `count` + +The `count` meta-argument accepts numeric [expressions](/docs/language/expressions/index.html). +However, unlike most arguments, the `count` value must be known +_before_ Terraform performs any remote resource actions. This means `count` +can't refer to any resource attributes that aren't known until after a +configuration is applied (such as a unique ID generated by the remote API when +an object is created). + +## Referring to Instances + +When `count` is set, Terraform distinguishes between the block itself +and the multiple _resource or module instances_ associated with it. Instances are +identified by an index number, starting with `0`. + +- `.` or `module.` (for example, `aws_instance.server`) refers to the resource block. +- `.[]` or `module.[]` (for example, `aws_instance.server[0]`, + `aws_instance.server[1]`, etc.) refers to individual instances. + +This is different from resources and modules without `count` or `for_each`, which can be +referenced without an index or key. + +Similarly, resources from child modules with multiple instances are prefixed +with `module.[]` when displayed in plan output and elsewhere in the UI. +For a module without `count` or `for_each`, the address will not contain +the module index as the module's name suffices to reference the module. + +-> **Note:** Within nested `provisioner` or `connection` blocks, the special +`self` object refers to the current _resource instance,_ not the resource block +as a whole. + +## When to Use `for_each` Instead of `count` + +If your instances are almost identical, `count` is appropriate. If some +of their arguments need distinct values that can't be directly derived from an +integer, it's safer to use `for_each`. + +Before `for_each` was available, it was common to derive `count` from the +length of a list and use `count.index` to look up the original list value: + +```hcl +variable "subnet_ids" { + type = list(string) +} + +resource "aws_instance" "server" { + # Create one instance for each subnet + count = length(var.subnet_ids) + + ami = "ami-a1b2c3d4" + instance_type = "t2.micro" + subnet_id = var.subnet_ids[count.index] + + tags = { + Name = "Server ${count.index}" + } +} +``` + +This was fragile, because the resource instances were still identified by their +_index_ instead of the string values in the list. If an element was removed from +the middle of the list, every instance _after_ that element would see its +`subnet_id` value change, resulting in more remote object changes than intended. +Using `for_each` gives the same flexibility without the extra churn. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/meta-arguments/depends_on.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/meta-arguments/depends_on.html.md new file mode 100644 index 00000000..fb8a35cc --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/meta-arguments/depends_on.html.md @@ -0,0 +1,76 @@ +--- +layout: "language" +page_title: "The depends_on Meta-Argument - Configuration Language" +--- + +# The `depends_on` Meta-Argument + +-> **Version note:** Module support for `depends_on` was added in Terraform 0.13, and +previous versions can only use it with resources. + +Use the `depends_on` meta-argument to handle hidden resource or module dependencies that +Terraform can't automatically infer. + +Explicitly specifying a dependency is only necessary when a resource or module relies on +some other resource's behavior but _doesn't_ access any of that resource's data +in its arguments. + +This argument is available in `module` blocks and in all `resource` blocks, +regardless of resource type. For example: + +```hcl +resource "aws_iam_role" "example" { + name = "example" + + # assume_role_policy is omitted for brevity in this example. See the + # documentation for aws_iam_role for a complete example. + assume_role_policy = "..." +} + +resource "aws_iam_instance_profile" "example" { + # Because this expression refers to the role, Terraform can infer + # automatically that the role must be created first. + role = aws_iam_role.example.name +} + +resource "aws_iam_role_policy" "example" { + name = "example" + role = aws_iam_role.example.name + policy = jsonencode({ + "Statement" = [{ + # This policy allows software running on the EC2 instance to + # access the S3 API. + "Action" = "s3:*", + "Effect" = "Allow", + }], + }) +} + +resource "aws_instance" "example" { + ami = "ami-a1b2c3d4" + instance_type = "t2.micro" + + # Terraform can infer from this that the instance profile must + # be created before the EC2 instance. + iam_instance_profile = aws_iam_instance_profile.example + + # However, if software running in this EC2 instance needs access + # to the S3 API in order to boot properly, there is also a "hidden" + # dependency on the aws_iam_role_policy that Terraform cannot + # automatically infer, so it must be declared explicitly: + depends_on = [ + aws_iam_role_policy.example, + ] +} +``` + +The `depends_on` meta-argument, if present, must be a list of references +to other resources or child modules in the same calling module. +Arbitrary expressions are not allowed in the `depends_on` argument value, +because its value must be known before Terraform knows resource relationships +and thus before it can safely evaluate expressions. + +The `depends_on` argument should be used only as a last resort. When using it, +always include a comment explaining why it is being used, to help future +maintainers understand the purpose of the additional dependency. + diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/meta-arguments/for_each.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/meta-arguments/for_each.html.md new file mode 100644 index 00000000..1a8bfe50 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/meta-arguments/for_each.html.md @@ -0,0 +1,271 @@ +--- +layout: "language" +page_title: "The for_each Meta-Argument - Configuration Language" +--- + +# The `for_each` Meta-Argument + +-> **Version note:** `for_each` was added in Terraform 0.12.6. Module support +for `for_each` was added in Terraform 0.13, and previous versions can only use +it with resources. + +-> **Note:** A given resource or module block cannot use both `count` and `for_each`. + +> **Hands-on:** Try the [Manage Similar Resources With For Each](https://learn.hashicorp.com/tutorials/terraform/for-each?in=terraform/0-13&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +By default, a [resource block](/docs/language/resources/syntax.html) configures one real +infrastructure object (and similarly, a +[module block](/docs/language/modules/syntax.html) includes a +child module's contents into the configuration one time). +However, sometimes you want to manage several similar objects (like a fixed +pool of compute instances) without writing a separate block for each one. +Terraform has two ways to do this: +[`count`](/docs/language/meta-arguments/count.html) and `for_each`. + +If a resource or module block includes a `for_each` argument whose value is a map or +a set of strings, Terraform will create one instance for each member of +that map or set. + +## Basic Syntax + +`for_each` is a meta-argument defined by the Terraform language. It can be used +with modules and with every resource type. + +The `for_each` meta-argument accepts a map or a set of strings, and creates an +instance for each item in that map or set. Each instance has a distinct +infrastructure object associated with it, and each is separately created, +updated, or destroyed when the configuration is applied. + +Map: + +```hcl +resource "azurerm_resource_group" "rg" { + for_each = { + a_group = "eastus" + another_group = "westus2" + } + name = each.key + location = each.value +} +``` + +Set of strings: + +```hcl +resource "aws_iam_user" "the-accounts" { + for_each = toset( ["Todd", "James", "Alice", "Dottie"] ) + name = each.key +} +``` + +Child module: + +```hcl +# my_buckets.tf +module "bucket" { + for_each = toset(["assets", "media"]) + source = "./publish_bucket" + name = "${each.key}_bucket" +} +``` + +```hcl +# publish_bucket/bucket-and-cloudfront.tf +variable "name" {} # this is the input parameter of the module + +resource "aws_s3_bucket" "example" { + # Because var.name includes each.key in the calling + # module block, its value will be different for + # each instance of this module. + bucket = var.name + + # ... +} + +resource "aws_iam_user" "deploy_user" { + # ... +} +``` + +## The `each` Object + +In blocks where `for_each` is set, an additional `each` object is +available in expressions, so you can modify the configuration of each instance. +This object has two attributes: + +- `each.key` — The map key (or set member) corresponding to this instance. +- `each.value` — The map value corresponding to this instance. (If a set was + provided, this is the same as `each.key`.) + +## Limitations on values used in `for_each` + +The keys of the map (or all the values in the case of a set of strings) must +be _known values_, or you will get an error message that `for_each` has dependencies +that cannot be determined before apply, and a `-target` may be needed. + +`for_each` keys cannot be the result (or rely on the result of) of impure functions, +including `uuid`, `bcrypt`, or `timestamp`, as their evaluation is deferred during the +main evaluation step. + +Sensitive values, such as [sensitive input variables](https://www.terraform.io/docs/language/values/variables.html#suppressing-values-in-cli-output), +[sensitive outputs](https://www.terraform.io/docs/language/values/outputs.html#sensitive-suppressing-values-in-cli-output), +or [sensitive resource attributes](https://www.terraform.io/docs/language/expressions/references.html#sensitive-resource-attributes), +cannot be used as arguments to `for_each`. The value used in `for_each` is used +to identify the resource instance and will always be disclosed in UI output, +which is why sensitive values are not allowed. +Attempts to use sensitive values as `for_each` arguments will result in an error. + +If you transform a value containing sensitive data into an argument to be used in `for_each`, be aware that +[most functions in Terraform will return a sensitive result if given an argument with any sensitive content](https://www.terraform.io/docs/language/expressions/function-calls.html#using-sensitive-data-as-function-arguments). +In many cases, you can achieve similar results to a function used for this purpose by +using a `for` expression. For example, if you would like to call `keys(local.map)`, where +`local.map` is an object with sensitive values (but non-sensitive keys), you can create a +value to pass to `for_each` with `toset([for k,v in local.map : k])`. + +## Using Expressions in `for_each` + +The `for_each` meta-argument accepts map or set [expressions](/docs/language/expressions/index.html). +However, unlike most arguments, the `for_each` value must be known +_before_ Terraform performs any remote resource actions. This means `for_each` +can't refer to any resource attributes that aren't known until after a +configuration is applied (such as a unique ID generated by the remote API when +an object is created). + +The `for_each` value must be a map or set with one element per desired +resource instance. When providing a set, you must use an expression that +explicitly returns a set value, like the [`toset`](/docs/language/functions/toset.html) +function; to prevent unwanted surprises during conversion, the `for_each` +argument does not implicitly convert lists or tuples to sets. +If you need to declare resource instances based on a nested +data structure or combinations of elements from multiple data structures you +can use Terraform expressions and functions to derive a suitable value. +For example: + +* Transform a multi-level nested structure into a flat list by + [using nested `for` expressions with the `flatten` function](/docs/language/functions/flatten.html#flattening-nested-structures-for-for_each). +* Produce an exhaustive list of combinations of elements from two or more + collections by + [using the `setproduct` function inside a `for` expression](/docs/language/functions/setproduct.html#finding-combinations-for-for_each). + +### Chaining `for_each` Between Resources + +Because a resource using `for_each` appears as a map of objects when used in +expressions elsewhere, you can directly use one resource as the `for_each` +of another in situations where there is a one-to-one relationship between +two sets of objects. + +For example, in AWS an `aws_vpc` object is commonly associated with a number +of other objects that provide additional services to that VPC, such as an +"internet gateway". If you are declaring multiple VPC instances using `for_each` +then you can chain that `for_each` into another resource to declare an +internet gateway for each VPC: + +```hcl +variable "vpcs" { + type = map(object({ + cidr_block = string + })) +} + +resource "aws_vpc" "example" { + # One VPC for each element of var.vpcs + for_each = var.vpcs + + # each.value here is a value from var.vpcs + cidr_block = each.value.cidr_block +} + +resource "aws_internet_gateway" "example" { + # One Internet Gateway per VPC + for_each = aws_vpc.example + + # each.value here is a full aws_vpc object + vpc_id = each.value.id +} + +output "vpc_ids" { + value = { + for k, v in aws_vpc.example : k => v.id + } + + # The VPCs aren't fully functional until their + # internet gateways are running. + depends_on = [aws_internet_gateway.example] +} +``` + +This chaining pattern explicitly and concisely declares the relationship +between the internet gateway instances and the VPC instances, which tells +Terraform to expect the instance keys for both to always change together, +and typically also makes the configuration easier to understand for human +maintainers. + +## Referring to Instances + +When `for_each` is set, Terraform distinguishes between the block itself +and the multiple _resource or module instances_ associated with it. Instances are +identified by a map key (or set member) from the value provided to `for_each`. + +- `.` or `module.` (for example, `azurerm_resource_group.rg`) refers to the block. +- `.[]` or `module.[]` (for example, `azurerm_resource_group.rg["a_group"]`, + `azurerm_resource_group.rg["another_group"]`, etc.) refers to individual instances. + +This is different from resources and modules without `count` or `for_each`, which can be +referenced without an index or key. + +Similarly, resources from child modules with multiple instances are prefixed +with `module.[]` when displayed in plan output and elsewhere in the UI. +For a module without `count` or `for_each`, the address will not contain +the module index as the module's name suffices to reference the module. + +-> **Note:** Within nested `provisioner` or `connection` blocks, the special +`self` object refers to the current _resource instance,_ not the resource block +as a whole. + +## Using Sets + +The Terraform language doesn't have a literal syntax for +[set values](/docs/language/expressions/type-constraints.html#collection-types), but you can use the `toset` +function to explicitly convert a list of strings to a set: + +```hcl +locals { + subnet_ids = toset([ + "subnet-abcdef", + "subnet-012345", + ]) +} + +resource "aws_instance" "server" { + for_each = local.subnet_ids + + ami = "ami-a1b2c3d4" + instance_type = "t2.micro" + subnet_id = each.key # note: each.key and each.value are the same for a set + + tags = { + Name = "Server ${each.key}" + } +} +``` + +Conversion from list to set discards the ordering of the items in the list and +removes any duplicate elements. `toset(["b", "a", "b"])` will produce a set +containing only `"a"` and `"b"` in no particular order; the second `"b"` is +discarded. + +If you are writing a module with an [input variable](/docs/language/values/variables.html) that +will be used as a set of strings for `for_each`, you can set its type to +`set(string)` to avoid the need for an explicit type conversion: + +```hcl +variable "subnet_ids" { + type = set(string) +} + +resource "aws_instance" "server" { + for_each = var.subnet_ids + + # (and the other arguments as above) +} +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/meta-arguments/lifecycle.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/meta-arguments/lifecycle.html.md new file mode 100644 index 00000000..4cbe0b78 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/meta-arguments/lifecycle.html.md @@ -0,0 +1,112 @@ +--- +layout: "language" +page_title: "The lifecycle Meta-Argument - Configuration Language" +--- + +# The `lifecycle` Meta-Argument + +> **Hands-on:** Try the [Lifecycle Management](https://learn.hashicorp.com/tutorials/terraform/resource-lifecycle?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +The general lifecycle for resources is described in the +[Resource Behavior](/docs/language/resources/behavior.html) page. Some details of +that behavior can be customized using the special nested `lifecycle` block +within a resource block body: + +```hcl +resource "azurerm_resource_group" "example" { + # ... + + lifecycle { + create_before_destroy = true + } +} +``` + +## Syntax and Arguments + +`lifecycle` is a nested block that can appear within a resource block. +The `lifecycle` block and its contents are meta-arguments, available +for all `resource` blocks regardless of type. + +The following arguments can be used within a `lifecycle` block: + +* `create_before_destroy` (bool) - By default, when Terraform must change + a resource argument that cannot be updated in-place due to + remote API limitations, Terraform will instead destroy the existing object + and then create a new replacement object with the new configured arguments. + + The `create_before_destroy` meta-argument changes this behavior so that + the new replacement object is created _first,_ and the prior object + is destroyed after the replacement is created. + + This is an opt-in behavior because many remote object types have unique + name requirements or other constraints that must be accommodated for + both a new and an old object to exist concurrently. Some resource types + offer special options to append a random suffix onto each object name to + avoid collisions, for example. Terraform CLI cannot automatically activate + such features, so you must understand the constraints for each resource + type before using `create_before_destroy` with it. + +* `prevent_destroy` (bool) - This meta-argument, when set to `true`, will + cause Terraform to reject with an error any plan that would destroy the + infrastructure object associated with the resource, as long as the argument + remains present in the configuration. + + This can be used as a measure of safety against the accidental replacement + of objects that may be costly to reproduce, such as database instances. + However, it will make certain configuration changes impossible to apply, + and will prevent the use of the `terraform destroy` command once such + objects are created, and so this option should be used sparingly. + + Since this argument must be present in configuration for the protection to + apply, note that this setting does not prevent the remote object from + being destroyed if the `resource` block were removed from configuration + entirely: in that case, the `prevent_destroy` setting is removed along + with it, and so Terraform will allow the destroy operation to succeed. + +* `ignore_changes` (list of attribute names) - By default, Terraform detects + any difference in the current settings of a real infrastructure object + and plans to update the remote object to match configuration. + + The `ignore_changes` feature is intended to be used when a resource is + created with references to data that may change in the future, but should + not affect said resource after its creation. In some rare cases, settings + of a remote object are modified by processes outside of Terraform, which + Terraform would then attempt to "fix" on the next run. In order to make + Terraform share management responsibilities of a single object with a + separate process, the `ignore_changes` meta-argument specifies resource + attributes that Terraform should ignore when planning updates to the + associated remote object. + + The arguments corresponding to the given attribute names are considered + when planning a _create_ operation, but are ignored when planning an + _update_. The arguments are the relative address of the attributes in the + resource. Map and list elements can be referenced using index notation, + like `tags["Name"]` and `list[0]` respectively. + + ```hcl + resource "aws_instance" "example" { + # ... + + lifecycle { + ignore_changes = [ + # Ignore changes to tags, e.g. because a management agent + # updates these based on some ruleset managed elsewhere. + tags, + ] + } + } + ``` + + Instead of a list, the special keyword `all` may be used to instruct + Terraform to ignore _all_ attributes, which means that Terraform can + create and destroy the remote object but will never propose updates to it. + + Only attributes defined by the resource type can be ignored. + `ignore_changes` cannot be applied to itself or to any other meta-arguments. + +## Literal Values Only + +The `lifecycle` settings all affect how Terraform constructs and traverses +the dependency graph. As a result, only literal values can be used because +the processing happens too early for arbitrary expression evaluation. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/meta-arguments/module-providers.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/meta-arguments/module-providers.html.md new file mode 100644 index 00000000..993b9f23 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/meta-arguments/module-providers.html.md @@ -0,0 +1,124 @@ +--- +layout: "language" +page_title: "The Module providers Meta-Argument - Configuration Language" +--- + +# The Module `providers` Meta-Argument + +In a [module call](/docs/language/modules/syntax.html) block, the +optional `providers` meta-argument specifies which +[provider configurations](/docs/language/providers/configuration.html) from the parent +module will be available inside the child module. + +```hcl +# The default "aws" configuration is used for AWS resources in the root +# module where no explicit provider instance is selected. +provider "aws" { + region = "us-west-1" +} + +# An alternate configuration is also defined for a different +# region, using the alias "usw2". +provider "aws" { + alias = "usw2" + region = "us-west-2" +} + +# An example child module is instantiated with the alternate configuration, +# so any AWS resources it defines will use the us-west-2 region. +module "example" { + source = "./example" + providers = { + aws = aws.usw2 + } +} +``` + +## Default Behavior: Inherit Default Providers + +If the child module does not declare any [configuration aliases](/docs/language/modules/develop/providers.html#provider-aliases-within-modules), +the `providers` argument is optional. If you omit it, a child module inherits +all of the _default_ provider configurations from its parent module. (Default +provider configurations are ones that don't use the `alias` argument.) + +If you specify a `providers` argument, it cancels this default behavior, and the +child module will _only_ have access to the provider configurations you specify. + +## Usage and Behavior + +The value of `providers` is a map, where: + +- The keys are the provider configuration names used inside the child module. +- The values are provider configuration names from the parent module. + +Both keys and values should be unquoted references to provider configurations. +For default configurations, this is the local name of the provider; for +alternate configurations, this is a `.` reference. + +Within a child module, resources are assigned to provider configurations as +normal — either Terraform chooses a default based on the name of the resource +type, or the resource specifies an alternate configuration with the `provider` +argument. If the module receives a `providers` map when it's called, the +provider configuration names used within the module are effectively remapped to +refer the specified configurations from the parent module. + +## When to Specify Providers + +There are two main reasons to use the `providers` argument: + +- Using different default provider configurations for a child module. +- Configuring a module that requires multiple configurations of the same provider. + +### Changing Default Provider Configurations + +Most re-usable modules only use default provider configurations, which they can +automatically inherit from their caller when `providers` is omitted. + +However, in Terraform configurations that use multiple configurations of the +same provider, you might want some child modules to use the default provider +configuration and other ones to use an alternate. (This usually happens when +using one configuration to manage resources in multiple different regions of the +same cloud provider.) + +By using the `providers` argument (like in the code example above), you can +accommodate this without needing to edit the child module. Although the code +within the child module always refers to the default provider configuration, the +actual configuration of that default can be different for each instance. + +### Modules With Alternate Provider Configurations + +In rare cases, a single re-usable module might require multiple configurations +of the same provider. For example, a module that configures connectivity between +networks in two AWS regions is likely to need both a source and a destination +region. In that case, the root module may look something like this: + +```hcl +provider "aws" { + alias = "usw1" + region = "us-west-1" +} + +provider "aws" { + alias = "usw2" + region = "us-west-2" +} + +module "tunnel" { + source = "./tunnel" + providers = { + aws.src = aws.usw1 + aws.dst = aws.usw2 + } +} +``` + +Non-default provider configurations are never automatically inherited, so any +module that works like this will always need a `providers` argument. The +documentation for the module should specify all of the provider configuration +names it needs. + +## More Information for Module Developers + +For more details and guidance about working with providers inside a re-usable +child module, see +[Module Development: Providers Within Modules](/docs/language/modules/develop/providers.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/meta-arguments/resource-provider.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/meta-arguments/resource-provider.html.md new file mode 100644 index 00000000..bdecf834 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/meta-arguments/resource-provider.html.md @@ -0,0 +1,58 @@ +--- +layout: "language" +page_title: "The Resource provider Meta-Argument - Configuration Language" +--- + +# The Resource `provider` Meta-Argument + +The `provider` meta-argument specifies which provider configuration to use for a resource, +overriding Terraform's default behavior of selecting one based on the resource +type name. Its value should be an unquoted `.` reference. + +As described in [Provider Configuration](/docs/language/providers/configuration.html), you can optionally +create multiple configurations for a single provider (usually to manage +resources in different regions of multi-region services). Each provider can have +one default configuration, and any number of alternate configurations that +include an extra name segment (or "alias"). + +By default, Terraform interprets the initial word in the resource type name +(separated by underscores) as the local name of a provider, and uses that +provider's default configuration. For example, the resource type +`google_compute_instance` is associated automatically with the default +configuration for the provider named `google`. + +By using the `provider` meta-argument, you can select an alternate provider +configuration for a resource: + +```hcl +# default configuration +provider "google" { + region = "us-central1" +} + +# alternate configuration, whose alias is "europe" +provider "google" { + alias = "europe" + region = "europe-west1" +} + +resource "google_compute_instance" "example" { + # This "provider" meta-argument selects the google provider + # configuration whose alias is "europe", rather than the + # default configuration. + provider = google.europe + + # ... +} +``` + +A resource always has an implicit dependency on its associated provider, to +ensure that the provider is fully configured before any resource actions +are taken. + +The `provider` meta-argument expects +[a `.` reference](/docs/language/providers/configuration.html#referring-to-alternate-provider-configurations), +which does not need to be quoted. Arbitrary expressions are not permitted for +`provider` because it must be resolved while Terraform is constructing the +dependency graph, before it is safe to evaluate expressions. + diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/modules/develop/composition.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/modules/develop/composition.html.md new file mode 100644 index 00000000..96c2da68 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/modules/develop/composition.html.md @@ -0,0 +1,355 @@ +--- +layout: "language" +page_title: "Module Composition" +sidebar_current: "docs-modules-composition" +description: |- + Module composition allows infrastructure to be described from modular + building blocks. +--- + +# Module Composition + +In a simple Terraform configuration with only one root module, we create a +flat set of resources and use Terraform's expression syntax to describe the +relationships between these resources: + +```hcl +resource "aws_vpc" "example" { + cidr_block = "10.1.0.0/16" +} + +resource "aws_subnet" "example" { + vpc_id = aws_vpc.example.id + + availability_zone = "us-west-2b" + cidr_block = cidrsubnet(aws_vpc.example.cidr_block, 4, 1) +} +``` + +When we introduce `module` blocks, our configuration becomes hierarchical +rather than flat: each module contains its own set of resources, and possibly +its own child modules, which can potentially create a deep, complex tree of +resource configurations. + +However, in most cases we strongly recommend keeping the module tree flat, +with only one level of child modules, and use a technique similar to the +above of using expressions to describe the relationships between the modules: + +```hcl +module "network" { + source = "./modules/aws-network" + + base_cidr_block = "10.0.0.0/8" +} + +module "consul_cluster" { + source = "./modules/aws-consul-cluster" + + vpc_id = module.network.vpc_id + subnet_ids = module.network.subnet_ids +} +``` + +We call this flat style of module usage _module composition_, because it +takes multiple [composable](https://en.wikipedia.org/wiki/Composability) +building-block modules and assembles them together to produce a larger system. +Instead of a module _embedding_ its dependencies, creating and managing its +own copy, the module _receives_ its dependencies from the root module, which +can therefore connect the same modules in different ways to produce different +results. + +The rest of this page discusses some more specific composition patterns that +may be useful when describing larger systems with Terraform. + +## Dependency Inversion + +In the example above, we saw a `consul_cluster` module that presumably describes +a cluster of [HashiCorp Consul](https://www.consul.io/) servers running in +an AWS VPC network, and thus it requires as arguments the identifiers of both +the VPC itself and of the subnets within that VPC. + +An alternative design would be to have the `consul_cluster` module describe +its _own_ network resources, but if we did that then it would be hard for +the Consul cluster to coexist with other infrastructure in the same network, +and so where possible we prefer to keep modules relatively small and pass in +their dependencies. + +This [dependency inversion](https://en.wikipedia.org/wiki/Dependency_inversion_principle) +approach also improves flexibility for future +refactoring, because the `consul_cluster` module doesn't know or care how +those identifiers are obtained by the calling module. A future refactor may +separate the network creation into its own configuration, and thus we may +pass those values into the module from data sources instead: + +```hcl +data "aws_vpc" "main" { + tags = { + Environment = "production" + } +} + +data "aws_subnet_ids" "main" { + vpc_id = data.aws_vpc.main.id +} + +module "consul_cluster" { + source = "./modules/aws-consul-cluster" + + vpc_id = data.aws_vpc.main.id + subnet_ids = data.aws_subnet_ids.main.ids +} +``` + +### Conditional Creation of Objects + +In situations where the same module is used across multiple environments, +it's common to see that some necessary object already exists in some +environments but needs to be created in other environments. + +For example, this can arise in development environment scenarios: for cost +reasons, certain infrastructure may be shared across multiple development +environments, while in production the infrastructure is unique and managed +directly by the production configuration. + +Rather than trying to write a module that itself tries to detect whether something +exists and create it if not, we recommend applying the dependency inversion +approach: making the module accept the object it needs as an argument, via +an input variable. + +For example, consider a situation where a Terraform module deploys compute +instances based on a disk image, and in some environments there is a +specialized disk image available while other environments share a common +base disk image. Rather than having the module itself handle both of these +scenarios, we can instead declare an input variable for an object representing +the disk image. Using AWS EC2 as an example, we might declare a common subtype +of the `aws_ami` resource type and data source schemas: + +```hcl +variable "ami" { + type = object({ + # Declare an object using only the subset of attributes the module + # needs. Terraform will allow any object that has at least these + # attributes. + id = string + architecture = string + }) +} +``` + +The caller of this module can now itself directly represent whether this is +an AMI to be created inline or an AMI to be retrieved from elsewhere: + +```hcl +# In situations where the AMI will be directly managed: + +resource "aws_ami_copy" "example" { + name = "local-copy-of-ami" + source_ami_id = "ami-abc123" + source_ami_region = "eu-west-1" +} + +module "example" { + source = "./modules/example" + + ami = aws_ami_copy.example +} +``` + +```hcl +# Or, in situations where the AMI already exists: + +data "aws_ami" "example" { + owner = "9999933333" + + tags = { + application = "example-app" + environment = "dev" + } +} + +module "example" { + source = "./modules/example" + + ami = data.aws_ami.example +} +``` + +This is consistent with Terraform's declarative style: rather than creating +modules with complex conditional branches, we directly describe what +should already exist and what we want Terraform to manage itself. + +By following this pattern, we can be explicit about in which situations we +expect the AMI to already be present and which we don't. A future reader +of the configuration can then directly understand what it is intending to do +without first needing to inspect the state of the remote system. + +In the above example, the object to be created or read is simple enough to +be given inline as a single resource, but we can also compose together multiple +modules as described elsewhere on this page in situations where the +dependencies themselves are complicated enough to benefit from abstractions. + +## Multi-cloud Abstractions + +Terraform itself intentionally does not attempt to abstract over similar +services offered by different vendors, because we want to expose the full +functionality in each offering and yet unifying multiple offerings behind a +single interface will tend to require a "lowest common denominator" approach. + +However, through composition of Terraform modules it is possible to create +your own lightweight multi-cloud abstractions by making your own tradeoffs +about which platform features are important to you. + +Opportunities for such abstractions arise in any situation where multiple +vendors implement the same concept, protocol, or open standard. For example, +the basic capabilities of the domain name system are common across all vendors, +and although some vendors differentiate themselves with unique features such +as geolocation and smart load balancing, you may conclude that in your use-case +you are willing to eschew those features in return for creating modules that +abstract the common DNS concepts across multiple vendors: + +```hcl +module "webserver" { + source = "./modules/webserver" +} + +locals { + fixed_recordsets = [ + { + name = "www" + type = "CNAME" + ttl = 3600 + records = [ + "webserver01", + "webserver02", + "webserver03", + ] + }, + ] + server_recordsets = [ + for i, addr in module.webserver.public_ip_addrs : { + name = format("webserver%02d", i) + type = "A" + records = [addr] + } + ] +} + +module "dns_records" { + source = "./modules/route53-dns-records" + + route53_zone_id = var.route53_zone_id + recordsets = concat(local.fixed_recordsets, local.server_recordsets) +} +``` + +In the above example, we've created a lightweight abstraction in the form of +a "recordset" object. This contains the attributes that describe the general +idea of a DNS recordset that should be mappable onto any DNS provider. + +We then instantiate one specific _implementation_ of that abstraction as a +module, in this case deploying our recordsets to Amazon Route53. + +If we later wanted to switch to a different DNS provider, we'd need only to +replace the `dns_records` module with a new implementation targeting that +provider, and all of the configuration that _produces_ the recordset +definitions can remain unchanged. + +We can create lightweight abstractions like these by defining Terraform object +types representing the concepts involved and then using these object types +for module input variables. In this case, all of our "DNS records" +implementations would have the following variable declared: + +```hcl +variable "recordsets" { + type = list(object({ + name = string + type = string + ttl = number + records = list(string) + })) +} +``` + +While DNS serves as a simple example, there are many more opportunities to +exploit common elements across vendors. A more complex example is Kubernetes, +where there are now many different vendors offering hosted Kubernetes clusters +and even more ways to run Kubernetes yourself. + +If the common functionality across all of these implementations is sufficient +for your needs, you may choose to implement a set of different modules that +describe a particular Kubernetes cluster implementation and all have the common +trait of exporting the hostname of the cluster as an output value: + +```hcl +output "hostname" { + value = azurerm_kubernetes_cluster.main.fqdn +} +``` + +You can then write _other_ modules that expect only a Kubernetes cluster +hostname as input and use them interchangeably with any of your Kubernetes +cluster modules: + +```hcl +module "k8s_cluster" { + source = "modules/azurerm-k8s-cluster" + + # (Azure-specific configuration arguments) +} + +module "monitoring_tools" { + source = "modules/monitoring_tools" + + cluster_hostname = module.k8s_cluster.hostname +} +``` + +## Data-only Modules + +Most modules contain `resource` blocks and thus describe infrastructure to be +created and managed. It may sometimes be useful to write modules that do not +describe any new infrastructure at all, but merely retrieve information about +existing infrastructure that was created elsewhere using +[data sources](/docs/language/data-sources/index.html). + +As with conventional modules, we suggest using this technique only when the +module raises the level of abstraction in some way, in this case by +encapsulating exactly how the data is retrieved. + +A common use of this technique is when a system has been decomposed into several +subsystem configurations but there is certain infrastructure that is shared +across all of the subsystems, such as a common IP network. In this situation, +we might write a shared module called `join-network-aws` which can be called +by any configuration that needs information about the shared network when +deployed in AWS: + +``` +module "network" { + source = "./modules/join-network-aws" + + environment = "production" +} + +module "k8s_cluster" { + source = "./modules/aws-k8s-cluster" + + subnet_ids = module.network.aws_subnet_ids +} +``` + +The `network` module itself could retrieve this data in a number of different +ways: it could query the AWS API directly using +[`aws_vpc`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc) +and +[`aws_subnet_ids`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet_ids) +data sources, or it could read saved information from a Consul cluster using +[`consul_keys`](https://registry.terraform.io/providers/hashicorp/consul/latest/docs/data-sources/keys), +or it might read the outputs directly from the state of the configuration that +manages the network using +[`terraform_remote_state`](https://www.terraform.io/docs/language/state/remote-state-data.html). + +The key benefit of this approach is that the source of this information can +change over time without updating every configuration that depends on it. +Furthermore, if you design your data-only module with a similar set of outputs +as a corresponding management module, you can swap between the two relatively +easily when refactoring. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/modules/develop/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/modules/develop/index.html.md new file mode 100644 index 00000000..5e71edd5 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/modules/develop/index.html.md @@ -0,0 +1,73 @@ +--- +layout: "language" +page_title: "Creating Modules" +sidebar_current: "docs-modules" +description: |- + A module is a container for multiple resources that are used together. +--- + +# Creating Modules + +> **Hands-on:** Try the [Reuse Configuration with Modules](https://learn.hashicorp.com/collections/terraform/modules?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. + +A _module_ is a container for multiple resources that are used together. +Modules can be used to create lightweight abstractions, so that you can +describe your infrastructure in terms of its architecture, rather than +directly in terms of physical objects. + +The `.tf` files in your working directory when you run [`terraform plan`](/docs/cli/commands/plan.html) +or [`terraform apply`](/docs/cli/commands/apply.html) together form the _root_ +module. That module may [call other modules](/docs/language/modules/syntax.html#calling-a-child-module) +and connect them together by passing output values from one to input values +of another. + +To learn how to _use_ modules, see [the Modules configuration section](/docs/language/modules/index.html). +This section is about _creating_ re-usable modules that other configurations +can include using `module` blocks. + +## Module structure + +Re-usable modules are defined using all of the same +[configuration language](/docs/language/index.html) concepts we use in root modules. +Most commonly, modules use: + +* [Input variables](/docs/language/values/variables.html) to accept values from + the calling module. +* [Output values](/docs/language/values/outputs.html) to return results to the + calling module, which it can then use to populate arguments elsewhere. +* [Resources](/docs/language/resources/index.html) to define one or more + infrastructure objects that the module will manage. + +To define a module, create a new directory for it and place one or more `.tf` +files inside just as you would do for a root module. Terraform can load modules +either from local relative paths or from remote repositories; if a module will +be re-used by lots of configurations you may wish to place it in its own +version control repository. + +Modules can also call other modules using a `module` block, but we recommend +keeping the module tree relatively flat and using [module composition](./composition.html) +as an alternative to a deeply-nested tree of modules, because this makes +the individual modules easier to re-use in different combinations. + +## When to write a module + +In principle any combination of resources and other constructs can be factored +out into a module, but over-using modules can make your overall Terraform +configuration harder to understand and maintain, so we recommend moderation. + +A good module should raise the level of abstraction by describing a new concept +in your architecture that is constructed from resource types offered by +providers. + +For example, `aws_instance` and `aws_elb` are both resource types belonging to +the AWS provider. You might use a module to represent the higher-level concept +"[HashiCorp Consul](https://www.consul.io/) cluster running in AWS" which +happens to be constructed from these and other AWS provider resources. + +We _do not_ recommend writing modules that are just thin wrappers around single +other resource types. If you have trouble finding a name for your module that +isn't the same as the main resource type inside it, that may be a sign that +your module is not creating any new abstraction and so the module is +adding unnecessary complexity. Just use the resource type directly in the +calling module instead. + diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/modules/develop/providers.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/modules/develop/providers.html.md new file mode 100644 index 00000000..fd5db189 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/modules/develop/providers.html.md @@ -0,0 +1,365 @@ +--- +layout: "language" +page_title: "Providers Within Modules - Configuration Language" +--- + +# Providers Within Modules + +[inpage-providers]: #providers-within-modules + +In a configuration with multiple modules, there are some special considerations +for how resources are associated with provider configurations. + +Each resource in the configuration must be associated with one provider +configuration. Provider configurations, unlike most other concepts in +Terraform, are global to an entire Terraform configuration and can be shared +across module boundaries. Provider configurations can be defined only in a +root Terraform module. + +Providers can be passed down to descendent modules in two ways: either +_implicitly_ through inheritance, or _explicitly_ via the `providers` argument +within a `module` block. These two options are discussed in more detail in the +following sections. + +A module intended to be called by one or more other modules must not contain +any `provider` blocks. A module containing its own provider configurations is +not compatible with the `for_each`, `count`, and `depends_on` arguments that +were introduced in Terraform v0.13. For more information, see +[Legacy Shared Modules with Provider Configurations](#legacy-shared-modules-with-provider-configurations). + +Provider configurations are used for all operations on associated resources, +including destroying remote objects and refreshing state. Terraform retains, as +part of its state, a reference to the provider configuration that was most +recently used to apply changes to each resource. When a `resource` block is +removed from the configuration, this record in the state will be used to locate +the appropriate configuration because the resource's `provider` argument +(if any) will no longer be present in the configuration. + +As a consequence, you must ensure that all resources that belong to a +particular provider configuration are destroyed before you can remove that +provider configuration's block from your configuration. If Terraform finds +a resource instance tracked in the state whose provider configuration block is +no longer available then it will return an error during planning, prompting you +to reintroduce the provider configuration. + +## Provider Version Constraints in Modules + +Although provider _configurations_ are shared between modules, each module must +declare its own [provider requirements](/docs/language/providers/requirements.html), so that +Terraform can ensure that there is a single version of the provider that is +compatible with all modules in the configuration and to specify the +[source address](/docs/language/providers/requirements.html#source-addresses) that serves as +the global (module-agnostic) identifier for a provider. + +To declare that a module requires particular versions of a specific provider, +use a `required_providers` block inside a `terraform` block: + +```hcl +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 2.7.0" + } + } +} +``` + +A provider requirement says, for example, "This module requires version v2.7.0 +of the provider `hashicorp/aws` and will refer to it as `aws`." It doesn't, +however, specify any of the configuration settings that determine what remote +endpoints the provider will access, such as an AWS region; configuration +settings come from provider _configurations_, and a particular overall Terraform +configuration can potentially have +[several different configurations for the same provider](/docs/language/providers/configuration.html#alias-multiple-provider-configurations). + +## Provider Aliases Within Modules + +To declare multiple configuration names for a provider within a module, add the +`configuration_aliases` argument: + +```hcl +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 2.7.0" + configuration_aliases = [ aws.alternate ] + } + } +} +``` + +The above requirements are identical to the previous, with the addition of the +alias provider configuration name `aws.alternate`, which can be referenced by +resources using the `provider` argument. + +If you are writing a shared Terraform module, constrain only the minimum +required provider version using a `>=` constraint. This should specify the +minimum version containing the features your module relies on, and thus allow a +user of your module to potentially select a newer provider version if other +features are needed by other parts of their overall configuration. + +## Implicit Provider Inheritance + +For convenience in simple configurations, a child module automatically inherits +default (un-aliased) provider configurations from its parent. This means that +explicit `provider` blocks appear only in the root module, and downstream +modules can simply declare resources for that provider and have them +automatically associated with the root provider configurations. + +For example, the root module might contain only a `provider` block and a +`module` block to instantiate a child module: + +```hcl +provider "aws" { + region = "us-west-1" +} + +module "child" { + source = "./child" +} +``` + +The child module can then use any resource from this provider with no further +provider configuration required: + +```hcl +resource "aws_s3_bucket" "example" { + bucket = "provider-inherit-example" +} +``` + +We recommend using this approach when a single configuration for each provider +is sufficient for an entire configuration. + +~> **Note:** Only provider configurations are inherited by child modules, not provider source or version requirements. Each module must [declare its own provider requirements](/docs/language/providers/requirements.html). This is especially important for non-HashiCorp providers. + +In more complex situations there may be +[multiple provider configurations](/docs/language/providers/configuration.html#alias-multiple-provider-configurations), +or a child module may need to use different provider settings than +its parent. For such situations, you must pass providers explicitly. + +## Passing Providers Explicitly + +When child modules each need a different configuration of a particular +provider, or where the child module requires a different provider configuration +than its parent, you can use the `providers` argument within a `module` block +to explicitly define which provider configurations are available to the +child module. For example: + +```hcl +# The default "aws" configuration is used for AWS resources in the root +# module where no explicit provider instance is selected. +provider "aws" { + region = "us-west-1" +} + +# An alternate configuration is also defined for a different +# region, using the alias "usw2". +provider "aws" { + alias = "usw2" + region = "us-west-2" +} + +# An example child module is instantiated with the alternate configuration, +# so any AWS resources it defines will use the us-west-2 region. +module "example" { + source = "./example" + providers = { + aws = aws.usw2 + } +} +``` + +The `providers` argument within a `module` block is similar to +[the `provider` argument](/docs/language/meta-arguments/resource-provider.html) +within a resource, but is a map rather than a single string because a module may +contain resources from many different providers. + +The keys of the `providers` map are provider configuration names as expected by +the child module, and the values are the names of corresponding configurations +in the _current_ module. + +Once the `providers` argument is used in a `module` block, it overrides all of +the default inheritance behavior, so it is necessary to enumerate mappings +for _all_ of the required providers. This is to avoid confusion and surprises +that may result when mixing both implicit and explicit provider passing. + +Additional provider configurations (those with the `alias` argument set) are +_never_ inherited automatically by child modules, and so must always be passed +explicitly using the `providers` map. For example, a module +that configures connectivity between networks in two AWS regions is likely +to need both a source and a destination region. In that case, the root module +may look something like this: + +```hcl +provider "aws" { + alias = "usw1" + region = "us-west-1" +} + +provider "aws" { + alias = "usw2" + region = "us-west-2" +} + +module "tunnel" { + source = "./tunnel" + providers = { + aws.src = aws.usw1 + aws.dst = aws.usw2 + } +} +``` + +The subdirectory `./tunnel` must then declare the configuration aliases for the +provider so the calling module can pass configurations with these names in its `providers` argument: + +```hcl +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 2.7.0" + configuration_aliases = [ aws.src, aws.dest ] + } + } +} +``` + +Each resource should then have its own `provider` attribute set to either +`aws.src` or `aws.dst` to choose which of the two provider configurations to +use. + +## Legacy Shared Modules with Provider Configurations + +In Terraform v0.10 and earlier there was no explicit way to use different +configurations of a provider in different modules in the same configuration, +and so module authors commonly worked around this by writing `provider` blocks +directly inside their modules, making the module have its own separate +provider configurations separate from those declared in the root module. + +However, that pattern had a significant drawback: because a provider +configuration is required to destroy the remote object associated with a +resource instance as well as to create or update it, a provider configuration +must always stay present in the overall Terraform configuration for longer +than all of the resources it manages. If a particular module includes +both resources and the provider configurations for those resources then +removing the module from its caller would violate that constraint: both the +resources and their associated providers would, in effect, be removed +simultaneously. + +Terraform v0.11 introduced the mechanisms described in earlier sections to +allow passing provider configurations between modules in a structured way, and +thus we explicitly recommended against writing a child module with its own +provider configuration blocks. However, that legacy pattern continued to work +for compatibility purposes -- though with the same drawback -- until Terraform +v0.13. + +Terraform v0.13 introduced the possibility for a module itself to use the +`for_each`, `count`, and `depends_on` arguments, but the implementation of +those unfortunately conflicted with the support for the legacy pattern. + +To retain the backward compatibility as much as possible, Terraform v0.13 +continues to support the legacy pattern for module blocks that do not use these +new features, but a module with its own provider configurations is not +compatible with `for_each`, `count`, or `depends_on`. Terraform will produce an +error if you attempt to combine these features. For example: + +``` +Error: Module does not support count + + on main.tf line 15, in module "child": + 15: count = 2 + +Module "child" cannot be used with count because it contains a nested provider +configuration for "aws", at child/main.tf:2,10-15. + +This module can be made compatible with count by changing it to receive all of +its provider configurations from the calling module, by using the "providers" +argument in the calling module block. +``` + +To make a module compatible with the new features, you must remove all of the +`provider` blocks from its definition. + +If the new version of the module declares `configuration_aliases`, or if the +calling module needs the child module to use different provider configurations +than its own default provider configurations, the calling module must then +include an explicit `providers` argument to describe which provider +configurations the child module will use: + +```hcl +provider "aws" { + region = "us-west-1" +} + +provider "aws" { + region = "us-east-1" + alias = "east" +} + +module "child" { + count = 2 + providers = { + # By default, the child module would use the + # default (unaliased) AWS provider configuration + # using us-west-1, but this will override it + # to use the additional "east" configuration + # for its resources instead. + aws = aws.east + } +} +``` + +Since the association between resources and provider configurations is +static, module calls using `for_each` or `count` cannot pass different +provider configurations to different instances. If you need different +instances of your module to use different provider configurations then you +must use a separate `module` block for each distinct set of provider +configurations: + +```hcl +provider "aws" { + alias = "usw1" + region = "us-west-1" +} + +provider "aws" { + alias = "usw2" + region = "us-west-2" +} + +provider "google" { + alias = "usw1" + credentials = "${file("account.json")}" + project = "my-project-id" + region = "us-west1" + zone = "us-west1-a" +} + +provider "google" { + alias = "usw2" + credentials = "${file("account.json")}" + project = "my-project-id" + region = "us-west2" + zone = "us-west2-a" +} + +module "bucket_w1" { + source = "./publish_bucket" + providers = { + aws.src = aws.usw1 + google.src = google.usw2 + } +} + +module "bucket_w2" { + source = "./publish_bucket" + providers = { + aws.src = aws.usw2 + google.src = google.usw2 + } +} +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/modules/develop/publish.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/modules/develop/publish.html.md new file mode 100644 index 00000000..93a17e3d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/modules/develop/publish.html.md @@ -0,0 +1,42 @@ +--- +layout: "language" +page_title: "Publishing Modules" +sidebar_current: "docs-modules-publish" +description: |- + A module is a container for multiple resources that are used together. +--- + +# Publishing Modules + +If you've built a module that you intend to be reused, we recommend +[publishing the module](/docs/registry/modules/publish.html) on the +[Terraform Registry](https://registry.terraform.io). This will version +your module, generate documentation, and more. + +Published modules can be easily consumed by Terraform, and users can +[constrain module versions](/docs/language/modules/syntax.html#version) +for safe and predictable updates. The following example shows how a caller +might use a module from the Terraform Registry: + +```hcl +module "consul" { + source = "hashicorp/consul/aws" +} +``` + +If you do not wish to publish your modules in the public registry, you can +instead use a [private registry](/docs/registry/private.html) to get +the same benefits. + +## Distribution via other sources + +Although the registry is the native mechanism for distributing re-usable +modules, Terraform can also install modules from +[various other sources](/docs/language/modules/sources.html). The alternative sources +do not support the first-class versioning mechanism, but some sources have +their own mechanisms for selecting particular VCS commits, etc. + +We recommend that modules distributed via other protocols still use the +[standard module structure](/docs/language/modules/develop/structure.html) so that they can +be used in a similar way as a registry module or be published on the registry +at a later time. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/modules/develop/structure.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/modules/develop/structure.html.md new file mode 100644 index 00000000..6aed2b26 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/modules/develop/structure.html.md @@ -0,0 +1,128 @@ +--- +layout: "language" +page_title: "Standard Module Structure" +--- + +# Standard Module Structure + +The standard module structure is a file and directory layout we recommend for +reusable modules distributed in separate repositories. Terraform tooling is +built to understand the standard module structure and use that structure to +generate documentation, index modules for the module registry, and more. + +The standard module structure expects the layout documented below. The list may +appear long, but everything is optional except for the root module. Most modules +don't need to do any extra work to follow the standard structure. + +* **Root module**. This is the **only required element** for the standard + module structure. Terraform files must exist in the root directory of + the repository. This should be the primary entrypoint for the module and is + expected to be opinionated. For the + [Consul module](https://registry.terraform.io/modules/hashicorp/consul) + the root module sets up a complete Consul cluster. It makes a lot of assumptions + however, and we expect that advanced users will use specific _nested modules_ + to more carefully control what they want. + +* **README**. The root module and any nested modules should have README + files. This file should be named `README` or `README.md`. The latter will + be treated as markdown. There should be a description of the module and + what it should be used for. If you want to include an example for how this + module can be used in combination with other resources, put it in an [examples + directory like this](https://github.com/hashicorp/terraform-aws-consul/tree/master/examples). + Consider including a visual diagram depicting the infrastructure resources + the module may create and their relationship. + + The README doesn't need to document inputs or outputs of the module because + tooling will automatically generate this. If you are linking to a file or + embedding an image contained in the repository itself, use a commit-specific + absolute URL so the link won't point to the wrong version of a resource in the + future. + +* **LICENSE**. The license under which this module is available. If you are + publishing a module publicly, many organizations will not adopt a module + unless a clear license is present. We recommend always having a license + file, even if it is not an open source license. + +* **`main.tf`, `variables.tf`, `outputs.tf`**. These are the recommended filenames for + a minimal module, even if they're empty. `main.tf` should be the primary + entrypoint. For a simple module, this may be where all the resources are + created. For a complex module, resource creation may be split into multiple + files but any nested module calls should be in the main file. `variables.tf` + and `outputs.tf` should contain the declarations for variables and outputs, + respectively. + +* **Variables and outputs should have descriptions.** All variables and + outputs should have one or two sentence descriptions that explain their + purpose. This is used for documentation. See the documentation for + [variable configuration](/docs/language/values/variables.html) and + [output configuration](/docs/language/values/outputs.html) for more details. + +* **Nested modules**. Nested modules should exist under the `modules/` + subdirectory. Any nested module with a `README.md` is considered usable + by an external user. If a README doesn't exist, it is considered for internal + use only. These are purely advisory; Terraform will not actively deny usage + of internal modules. Nested modules should be used to split complex behavior + into multiple small modules that advanced users can carefully pick and + choose. For example, the + [Consul module](https://registry.terraform.io/modules/hashicorp/consul) + has a nested module for creating the Cluster that is separate from the + module to setup necessary IAM policies. This allows a user to bring in their + own IAM policy choices. + + If the root module includes calls to nested modules, they should use relative + paths like `./modules/consul-cluster` so that Terraform will consider them + to be part of the same repository or package, rather than downloading them + again separately. + + If a repository or package contains multiple nested modules, they should + ideally be [composable](./composition.html) by the caller, rather than + calling directly to each other and creating a deeply-nested tree of modules. + +* **Examples**. Examples of using the module should exist under the + `examples/` subdirectory at the root of the repository. Each example may have + a README to explain the goal and usage of the example. Examples for + submodules should also be placed in the root `examples/` directory. + + Because examples will often be copied into other repositories for + customization, any `module` blocks should have their `source` set to the + address an external caller would use, not to a relative path. + +A minimal recommended module following the standard structure is shown below. +While the root module is the only required element, we recommend the structure +below as the minimum: + +```sh +$ tree minimal-module/ +. +├── README.md +├── main.tf +├── variables.tf +├── outputs.tf +``` + +A complete example of a module following the standard structure is shown below. +This example includes all optional elements and is therefore the most +complex a module can become: + +```sh +$ tree complete-module/ +. +├── README.md +├── main.tf +├── variables.tf +├── outputs.tf +├── ... +├── modules/ +│   ├── nestedA/ +│   │   ├── README.md +│   │   ├── variables.tf +│   │   ├── main.tf +│   │   ├── outputs.tf +│   ├── nestedB/ +│   ├── .../ +├── examples/ +│   ├── exampleA/ +│   │   ├── main.tf +│   ├── exampleB/ +│   ├── .../ +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/modules/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/modules/index.html.md new file mode 100644 index 00000000..2aef04ab --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/modules/index.html.md @@ -0,0 +1,68 @@ +--- +layout: "language" +page_title: "Modules Overview - Configuration Language" +--- + +# Modules + +> **Hands-on:** Try the [Reuse Configuration with Modules](https://learn.hashicorp.com/collections/terraform/modules?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. + +_Modules_ are containers for multiple resources that are used together. A module +consists of a collection of `.tf` and/or `.tf.json` files kept together in a +directory. + +Modules are the main way to package and reuse resource configurations with +Terraform. + +## The Root Module + +Every Terraform configuration has at least one module, known as its +_root module_, which consists of the resources defined in the `.tf` files in +the main working directory. + +## Child Modules + +A Terraform module (usually the root module of a configuration) can _call_ other +modules to include their resources into the configuration. A module that has +been called by another module is often referred to as a _child module._ + +Child modules can be called multiple times within the same configuration, and +multiple configurations can use the same child module. + +## Published Modules + +In addition to modules from the local filesystem, Terraform can load modules +from a public or private registry. This makes it possible to publish modules for +others to use, and to use modules that others have published. + +The [Terraform Registry](https://registry.terraform.io/browse/modules) hosts a +broad collection of publicly available Terraform modules for configuring many +kinds of common infrastructure. These modules are free to use, and Terraform can +download them automatically if you specify the appropriate source and version in +a module call block. + +Also, members of your organization might produce modules specifically crafted +for your own infrastructure needs. [Terraform Cloud](/docs/cloud/index.html) and +[Terraform Enterprise](/docs/enterprise/index.html) both include a private +module registry for sharing modules internally within your organization. + +## Using Modules + +- [Module Blocks](/docs/language/modules/syntax.html) documents the syntax for + calling a child module from a parent module, including meta-arguments like + `for_each`. + +- [Module Sources](/docs/language/modules/sources.html) documents what kinds of paths, + addresses, and URIs can be used in the `source` argument of a module block. + +- The Meta-Arguments section documents special arguments that can be used with + every module, including + [`providers`](/docs/language/meta-arguments/module-providers.html), + [`depends_on`](/docs/language/meta-arguments/depends_on.html), + [`count`](/docs/language/meta-arguments/count.html), + and [`for_each`](/docs/language/meta-arguments/for_each.html). + +## Developing Modules + +For information about developing reusable modules, see +[Module Development](/docs/language/modules/develop/index.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/modules/sources.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/modules/sources.html.md new file mode 100644 index 00000000..c8e23290 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/modules/sources.html.md @@ -0,0 +1,436 @@ +--- +layout: "language" +page_title: "Module Sources" +sidebar_current: "docs-modules-sources" +description: The source argument within a module block specifies the location of the source code of a child module. +--- + +# Module Sources + +The `source` argument in [a `module` block](/docs/language/modules/syntax.html) +tells Terraform where to find the source code for the desired child module. + +Terraform uses this during the module installation step of `terraform init` +to download the source code to a directory on local disk so that it can be +used by other Terraform commands. + +The module installer supports installation from a number of different source +types, as listed below. + + * [Local paths](#local-paths) + + * [Terraform Registry](#terraform-registry) + + * [GitHub](#github) + + * [Bitbucket](#bitbucket) + + * Generic [Git](#generic-git-repository), [Mercurial](#generic-mercurial-repository) repositories + + * [HTTP URLs](#http-urls) + + * [S3 buckets](#s3-bucket) + + * [GCS buckets](#gcs-bucket) + +Each of these is described in the following sections. Module source addresses +use a _URL-like_ syntax, but with extensions to support unambiguous selection +of sources and additional features. + +We recommend using local file paths for closely-related modules used primarily +for the purpose of factoring out repeated code elements, and using a native +Terraform module registry for modules intended to be shared by multiple calling +configurations. We support other sources so that you can potentially distribute +Terraform modules internally with existing infrastructure. + +Many of the source types will make use of "ambient" credentials available +when Terraform is run, such as from environment variables or credentials files +in your home directory. This is covered in more detail in each of the following +sections. + +We recommend placing each module that is intended to be re-usable in the root +of its own repository or archive file, but it is also possible to +[reference modules from subdirectories](#modules-in-package-sub-directories). + +## Local Paths + +Local path references allow for factoring out portions of a configuration +within a single source repository. + +```hcl +module "consul" { + source = "./consul" +} +``` + +A local path must begin with either `./` or `../` to indicate that a local +path is intended, to distinguish from +[a module registry address](#terraform-registry). + +Local paths are special in that they are not "installed" in the same sense +that other sources are: the files are already present on local disk (possibly +as a result of installing a parent module) and so can just be used directly. +Their source code is automatically updated if the parent module is upgraded. + +## Terraform Registry + +A module registry is the native way of distributing Terraform modules for use +across multiple configurations, using a Terraform-specific protocol that +has full support for module versioning. + +[Terraform Registry](https://registry.terraform.io/) is an index of modules +shared publicly using this protocol. This public registry is the easiest way +to get started with Terraform and find modules created by others in the +community. + +You can also use a +[private registry](/docs/registry/private.html), either +via the built-in feature from Terraform Cloud, or by running a custom +service that implements +[the module registry protocol](/docs/registry/api.html). + +Modules on the public Terraform Registry can be referenced using a registry +source address of the form `//`, with each +module's information page on the registry site including the exact address +to use. + +```hcl +module "consul" { + source = "hashicorp/consul/aws" + version = "0.1.0" +} +``` + +The above example will use the +[Consul module for AWS](https://registry.terraform.io/modules/hashicorp/consul/aws) +from the public registry. + +For modules hosted in other registries, prefix the source address with an +additional `/` portion, giving the hostname of the private registry: + +```hcl +module "consul" { + source = "app.terraform.io/example-corp/k8s-cluster/azurerm" + version = "1.1.0" +} +``` + +If you are using the SaaS version of Terraform Cloud, its private +registry hostname is `app.terraform.io`. If you use a self-hosted Terraform +Enterprise instance, its private registry hostname is the same as the host +where you'd access the web UI and the host you'd use when configuring +the `remote` backend. + +Registry modules support versioning. You can provide a specific version as shown +in the above examples, or use flexible +[version constraints](/docs/language/modules/syntax.html#version). + +You can learn more about the registry at the +[Terraform Registry documentation](/docs/registry/modules/use.html#using-modules). + +To access modules from a private registry, you may need to configure an access +token [in the CLI config](/docs/cli/config/config-file.html#credentials). Use the +same hostname as used in the module source string. For a private registry +within Terraform Cloud, use the same authentication token as you would +use with the Enterprise API or command-line clients. + +## GitHub + +Terraform will recognize unprefixed `github.com` URLs and interpret them +automatically as Git repository sources. + +```hcl +module "consul" { + source = "github.com/hashicorp/example" +} +``` + +The above address scheme will clone over HTTPS. To clone over SSH, use the +following form: + +```hcl +module "consul" { + source = "git@github.com:hashicorp/example.git" +} +``` + +These GitHub schemes are treated as convenient aliases for +[the general Git repository address scheme](#generic-git-repository), and so +they obtain credentials in the same way and support the `ref` argument for +selecting a specific revision. You will need to configure credentials in +particular to access private repositories. + +## Bitbucket + +Terraform will recognize unprefixed `bitbucket.org` URLs and interpret them +automatically as BitBucket repositories: + +```hcl +module "consul" { + source = "bitbucket.org/hashicorp/terraform-consul-aws" +} +``` + +This shorthand works only for public repositories, because Terraform must +access the BitBucket API to learn if the given repository uses Git or Mercurial. + +Terraform treats the result either as [a Git source](#generic-git-repository) +or [a Mercurial source](#generic-mercurial-repository) depending on the +repository type. See the sections on each version control type for information +on how to configure credentials for private repositories and how to specify +a specific revision to install. + +## Generic Git Repository + +Arbitrary Git repositories can be used by prefixing the address with the +special `git::` prefix. After this prefix, any valid +[Git URL](https://git-scm.com/docs/git-clone#_git_urls) +can be specified to select one of the protocols supported by Git. + +For example, to use HTTPS or SSH: + +```hcl +module "vpc" { + source = "git::https://example.com/vpc.git" +} + +module "storage" { + source = "git::ssh://username@example.com/storage.git" +} +``` + +Terraform installs modules from Git repositories by running `git clone`, and +so it will respect any local Git configuration set on your system, including +credentials. To access a non-public Git repository, configure Git with +suitable credentials for that repository. + +If you use the SSH protocol then any configured SSH keys will be used +automatically. This is the most common way to access non-public Git +repositories from automated systems because it allows access to private +repositories without interactive prompts. + +If using the HTTP/HTTPS protocol, or any other protocol that uses +username/password credentials, configure +[Git Credentials Storage](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage) +to select a suitable source of credentials for your environment. + +If your Terraform configuration will be used within [Terraform Cloud](https://www.hashicorp.com/products/terraform), +only SSH key authentication is supported, and +[keys can be configured on a per-workspace basis](/docs/cloud/workspaces/ssh-keys.html). + +### Selecting a Revision + +By default, Terraform will clone and use the default branch (referenced by +`HEAD`) in the selected repository. You can override this using the +`ref` argument: + +```hcl +module "vpc" { + source = "git::https://example.com/vpc.git?ref=v1.2.0" +} +``` + +The value of the `ref` argument can be any reference that would be accepted +by the `git checkout` command, including branch and tag names. + +### "scp-like" address syntax + +When using Git over SSH, we recommend using the `ssh://`-prefixed URL form +for consistency with all of the other URL-like git address forms. +You may opt to use the alternative "scp-like" syntax instead, in which case you +must omit the `ssh://` scheme part and include only the `git::` part. +For example: + +```hcl +module "storage" { + source = "git::username@example.com:storage.git" +} +``` + + +If you use the `ssh://` URL scheme then Terraform will assume that the colon +marks the beginning of a port number, rather than the beginning of the path. +This matches how Git itself interprets these different forms, aside from +the Terraform-specific `git::` selector prefix. + +## Generic Mercurial Repository + +You can use arbitrary Mercurial repositories by prefixing the address with the +special `hg::` prefix. After this prefix, any valid +[Mercurial URL](https://www.mercurial-scm.org/repo/hg/help/urls) +can be specified to select one of the protocols supported by Mercurial. + +```hcl +module "vpc" { + source = "hg::http://example.com/vpc.hg" +} +``` + +Terraform installs modules from Mercurial repositories by running `hg clone`, and +so it will respect any local Mercurial configuration set on your system, +including credentials. To access a non-public repository, configure Mercurial +with suitable credentials for that repository. + +If you use the SSH protocol then any configured SSH keys will be used +automatically. This is the most common way to access non-public Mercurial +repositories from automated systems because it allows access to private +repositories without interactive prompts. + +If your Terraform configuration will be used within [Terraform Cloud](https://www.hashicorp.com/products/terraform), +only SSH key authentication is supported, and +[keys can be configured on a per-workspace basis](/docs/cloud/workspaces/ssh-keys.html). + +### Selecting a Revision + +You can select a non-default branch or tag using the optional `ref` argument: + +```hcl +module "vpc" { + source = "hg::http://example.com/vpc.hg?ref=v1.2.0" +} +``` + +## HTTP URLs + +When you use an HTTP or HTTPS URL, Terraform will make a `GET` request to +the given URL, which can return _another_ source address. This indirection +allows using HTTP URLs as a sort of "vanity redirect" over a more complicated +module source address. + +Terraform will append an additional query string argument `terraform-get=1` to +the given URL before sending the `GET` request, allowing the server to +optionally return a different result when Terraform is requesting it. + +If the response is successful (`200`-range status code), Terraform looks in +the following locations in order for the next address to access: + +* The value of a response header field named `X-Terraform-Get`. + +* If the response is an HTML page, a `meta` element with the name `terraform-get`: + + ```html + + ``` + +In either case, the result is interpreted as another module source address +using one of the forms documented elsewhere on this page. + +If an HTTP/HTTPS URL requires authentication credentials, use a `.netrc` +file in your home directory to configure these. For information on this format, +see [the documentation for using it in `curl`](https://ec.haxx.se/usingcurl-netrc.html). + +### Fetching archives over HTTP + +As a special case, if Terraform detects that the URL has a common file +extension associated with an archive file format then it will bypass the +special `terraform-get=1` redirection described above and instead just use +the contents of the referenced archive as the module source code: + +```hcl +module "vpc" { + source = "https://example.com/vpc-module.zip" +} +``` + +The extensions that Terraform recognizes for this special behavior are: + +* `zip` +* `tar.bz2` and `tbz2` +* `tar.gz` and `tgz` +* `tar.xz` and `txz` + +If your URL _doesn't_ have one of these extensions but refers to an archive +anyway, use the `archive` argument to force this interpretation: + +```hcl +module "vpc" { + source = "https://example.com/vpc-module?archive=zip" +} +``` + +-> **Note:** If the content of the archive file is a directory, you will need to +include that directory in the module source. Read the section on +[Modules in Package Sub-directories](#modules-in-package-sub-directories) for more +information. + +## S3 Bucket + +You can use archives stored in S3 as module sources using the special `s3::` +prefix, followed by +[an S3 bucket object URL](http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html#access-bucket-intro). + +```hcl +module "consul" { + source = "s3::https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/vpc.zip" +} +``` + +-> **Note:** Buckets in AWS's us-east-1 region must use the hostname `s3.amazonaws.com` (instead of `s3-us-east-1.amazonaws.com`). + +The `s3::` prefix causes Terraform to use AWS-style authentication when +accessing the given URL. As a result, this scheme may also work for other +services that mimic the S3 API, as long as they handle authentication in the +same way as AWS. + +The resulting object must be an archive with one of the same file +extensions as for [archives over standard HTTP](#fetching-archives-over-http). +Terraform will extract the archive to obtain the module source tree. + +The module installer looks for AWS credentials in the following locations, +preferring those earlier in the list when multiple are available: + +* The `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables. +* The default profile in the `.aws/credentials` file in your home directory. +* If running on an EC2 instance, temporary credentials associated with the + instance's IAM Instance Profile. + +## GCS Bucket + +You can use archives stored in Google Cloud Storage as module sources using the special `gcs::` +prefix, followed by +[a GCS bucket object URL](https://cloud.google.com/storage/docs/request-endpoints#typical). + +For example + +* `gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH_TO_MODULE` +* `gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH/TO/module.zip` + +```hcl +module "consul" { + source = "gcs::https://www.googleapis.com/storage/v1/modules/foomodule.zip" +} +``` + +The module installer uses Google Cloud SDK to authenticate with GCS. To set credentials you can: + +* Enter the path of your service account key file in the GOOGLE_APPLICATION_CREDENTIALS environment variable, or; +* If you're running Terraform from a GCE instance, default credentials are automatically available. See [Creating and Enabling Service Accounts](https://cloud.google.com/compute/docs/authentication) for Instances for more details +* On your computer, you can make your Google identity available by running `gcloud auth application-default login`. + + +## Modules in Package Sub-directories + +When the source of a module is a version control repository or archive file +(generically, a "package"), the module itself may be in a sub-directory relative +to the root of the package. + +A special double-slash syntax is interpreted by Terraform to indicate that +the remaining path after that point is a sub-directory within the package. +For example: + +* `hashicorp/consul/aws//modules/consul-cluster` +* `git::https://example.com/network.git//modules/vpc` +* `https://example.com/network-module.zip//modules/vpc` +* `s3::https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/network.zip//modules/vpc` + +If the source address has arguments, such as the `ref` argument supported for +the version control sources, the sub-directory portion must be _before_ those +arguments: + +* `git::https://example.com/network.git//modules/vpc?ref=v1.2.0` + +Terraform will still extract the entire package to local disk, but will read +the module from the subdirectory. As a result, it is safe for a module in +a sub-directory of a package to use [a local path](#local-paths) to another +module as long as it is in the _same_ package. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/modules/syntax.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/modules/syntax.html.md new file mode 100644 index 00000000..9bbf78d9 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/modules/syntax.html.md @@ -0,0 +1,197 @@ +--- +layout: "language" +page_title: "Modules - Configuration Language" +sidebar_current: "docs-config-modules" +description: |- + Modules allow multiple resources to be grouped together and encapsulated. +--- + +# Module Blocks + +> **Hands-on:** Try the [Reuse Configuration with Modules](https://learn.hashicorp.com/collections/terraform/modules?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. + +A _module_ is a container for multiple resources that are used together. + +Every Terraform configuration has at least one module, known as its +_root module_, which consists of the resources defined in the `.tf` files in +the main working directory. + +A module can call other modules, which lets you include the child module's +resources into the configuration in a concise way. Modules +can also be called multiple times, either within the same configuration or +in separate configurations, allowing resource configurations to be packaged +and re-used. + +This page describes how to call one module from another. For more information +about creating re-usable child modules, see [Module Development](/docs/language/modules/develop/index.html). + +## Calling a Child Module + +To _call_ a module means to include the contents of that module into the +configuration with specific values for its +[input variables](/docs/language/values/variables.html). Modules are called +from within other modules using `module` blocks: + +```hcl +module "servers" { + source = "./app-cluster" + + servers = 5 +} +``` + +A module that includes a `module` block like this is the _calling module_ of the +child module. + +The label immediately after the `module` keyword is a local name, which the +calling module can use to refer to this instance of the module. + +Within the block body (between `{` and `}`) are the arguments for the module. +Module calls use the following kinds of arguments: + +- The `source` argument is mandatory for all modules. + +- The `version` argument is recommended for modules from a registry. + +- Most other arguments correspond to [input variables](/docs/language/values/variables.html) + defined by the module. (The `servers` argument in the example above is one of + these.) + +- Terraform defines a few other meta-arguments that can be used with all + modules, including `for_each` and `depends_on`. + +### Source + +All modules **require** a `source` argument, which is a meta-argument defined by +Terraform. Its value is either the path to a local directory containing the +module's configuration files, or a remote module source that Terraform should +download and use. This value must be a literal string with no template +sequences; arbitrary expressions are not allowed. For more information on +possible values for this argument, see [Module Sources](/docs/language/modules/sources.html). + +The same source address can be specified in multiple `module` blocks to create +multiple copies of the resources defined within, possibly with different +variable values. + +After adding, removing, or modifying `module` blocks, you must re-run +`terraform init` to allow Terraform the opportunity to adjust the installed +modules. By default this command will not upgrade an already-installed module; +use the `-upgrade` option to instead upgrade to the newest available version. + +### Version + +When using modules installed from a module registry, we recommend explicitly +constraining the acceptable version numbers to avoid unexpected or unwanted +changes. + +Use the `version` argument in the `module` block to specify versions: + +```shell +module "consul" { + source = "hashicorp/consul/aws" + version = "0.0.5" + + servers = 3 +} +``` + +The `version` argument accepts a [version constraint string](/docs/language/expressions/version-constraints.html). +Terraform will use the newest installed version of the module that meets the +constraint; if no acceptable versions are installed, it will download the newest +version that meets the constraint. + +Version constraints are supported only for modules installed from a module +registry, such as the public [Terraform Registry](https://registry.terraform.io/) +or [Terraform Cloud's private module registry](/docs/cloud/registry/index.html). +Other module sources can provide their own versioning mechanisms within the +source string itself, or might not support versions at all. In particular, +modules sourced from local file paths do not support `version`; since +they're loaded from the same source repository, they always share the same +version as their caller. + +### Meta-arguments + +Along with `source` and `version`, Terraform defines a few more +optional meta-arguments that have special meaning across all modules, +described in more detail in the following pages: + +- `count` - Creates multiple instances of a module from a single `module` block. + See [the `count` page](/docs/language/meta-arguments/count.html) + for details. + +- `for_each` - Creates multiple instances of a module from a single `module` + block. See + [the `for_each` page](/docs/language/meta-arguments/for_each.html) + for details. + +- `providers` - Passes provider configurations to a child module. See + [the `providers` page](/docs/language/meta-arguments/module-providers.html) + for details. If not specified, the child module inherits all of the default + (un-aliased) provider configurations from the calling module. + +- `depends_on` - Creates explicit dependencies between the entire + module and the listed targets. See + [the `depends_on` page](/docs/language/meta-arguments/depends_on.html) + for details. + +In addition to the above, the `lifecycle` argument is not currently used by +Terraform but is reserved for planned future features. + +## Accessing Module Output Values + +The resources defined in a module are encapsulated, so the calling module +cannot access their attributes directly. However, the child module can +declare [output values](/docs/language/values/outputs.html) to selectively +export certain values to be accessed by the calling module. + +For example, if the `./app-cluster` module referenced in the example above +exported an output value named `instance_ids` then the calling module +can reference that result using the expression `module.servers.instance_ids`: + +```hcl +resource "aws_elb" "example" { + # ... + + instances = module.servers.instance_ids +} +``` + +For more information about referring to named values, see +[Expressions](/docs/language/expressions/index.html). + +## Transferring Resource State Into Modules + +When refactoring an existing configuration to split code into child modules, +moving resource blocks between modules causes Terraform to see the new location +as an entirely different resource from the old. Always check the execution plan +after moving code across modules to ensure that no resources are deleted by +surprise. + +If you want to make sure an existing resource is preserved, use +[the `terraform state mv` command](/docs/cli/commands/state/mv.html) to inform +Terraform that it has moved to a different module. + +When passing resource addresses to `terraform state mv`, resources within child +modules must be prefixed with `module..`. If a module was called with +[`count`](/docs/language/meta-arguments/count.html) or +[`for_each`](/docs/language/meta-arguments/for_each.html), +its resource addresses must be prefixed with `module.[].` +instead, where `` matches the `count.index` or `each.key` value of a +particular module instance. + +Full resource addresses for module contents are used within the UI and on the +command line, but cannot be used within a Terraform configuration. Only +[outputs](/docs/language/values/outputs.html) from a module can be referenced from +elsewhere in your configuration. + +## Tainting resources within a module + +The [taint command](/docs/cli/commands/taint.html) can be used to _taint_ specific +resources within a module: + +```shell +$ terraform taint module.salt_master.aws_instance.salt_master +``` + +It is not possible to taint an entire module. Instead, each resource within +the module must be tainted separately. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/modules/testing-experiment.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/modules/testing-experiment.html.md new file mode 100644 index 00000000..6b2bb7fc --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/modules/testing-experiment.html.md @@ -0,0 +1,323 @@ +--- +layout: "language" +page_title: "Module Testing Experiment - Configuration Language" +--- + +# Module Testing Experiment + +This page is about some experimental features available in recent versions of +Terraform CLI related to integration testing of shared modules. + +The Terraform team is aiming to use these features to gather feedback as part +of ongoing research into different strategies for testing Terraform modules. +These features are likely to change significantly in future releases based on +feedback. + +## Current Research Goals + +Our initial area of research is into the question of whether it's helpful and +productive to write module integration tests in the Terraform language itself, +or whether it's better to handle that as a separate concern orchestrated by +code written in other languages. + +Some existing efforts have piloted both approaches: + +* [Terratest](https://terratest.gruntwork.io/) and + [kitchen-terraform](https://github.com/newcontext-oss/kitchen-terraform) + both pioneered the idea of writing tests for Terraform modules with explicit + orchestration written in the Go and Ruby programming languages, respectively. + +* The Terraform provider + [`apparentlymart/testing`](https://registry.terraform.io/providers/apparentlymart/testing/latest) + introduced the idea of writing Terraform module tests in the Terraform + language itself, using a special provider that can evaluate assertions + and fail `terraform apply` if they don't pass. + +Both of these approaches have both advantages and disadvantages, and so it's +likely that both will coexist for different situations, but the community +efforts have already explored the external-language testing model quite deeply +while the Terraform-integrated testing model has not yet been widely trialled. +For that reason, the current iteration of the module testing experiment is +aimed at trying to make the Terraform-integrated approach more accessible so +that more module authors can hopefully try it and share their experiences. + +## Current Experimental Features + +-> This page describes the incarnation of the experimental features introduced +in **Terraform CLI v0.15.0**. If you are using an earlier version of Terraform +then you'll need to upgrade to v0.15.0 or later to use the experimental features +described here, though you only need to use v0.15.0 or later for running tests; +your module itself can remain compatible with earlier Terraform versions, if +needed. + +Our current area of interest is in what sorts of tests can and cannot be +written using features integrated into the Terraform language itself. As a +means to investigate that without invasive, cross-cutting changes to Terraform +Core we're using a special built-in Terraform provider as a placeholder for +potential new features. + +If this experiment is successful then we expect to run a second round of +research and design about exactly what syntax is most ergonomic for writing +tests, but for the moment we're interested less in the specific syntax and more +in the capabilities of this approach. + +The temporary extensions to Terraform for this experiment consist of the +following parts: + +* A temporary experimental provider `terraform.io/builtin/test`, which acts as + a placeholder for potential new language features related to test assertions. + +* A `terraform test` command for more conveniently running multiple tests in + a single action. + +* An experimental convention of placing test configurations in subdirectories + of a `tests` directory within your module, which `terraform test` will then + discover and run. + +We would like to invite adventurous module authors to try writing integration +tests for their modules using these mechanisms, and ideally also share the +tests you write (in a temporary VCS branch, if necessary) so we can see what +you were able to test, along with anything you felt unable to test in this way. + +If you're interested in giving this a try, see the following sections for +usage details. Because these features are temporary experimental extensions, +there's some boilerplate required to activate and make use of it which would +likely not be required in a final design. + +### Writing Tests for a Module + +For the purposes of the current experiment, module tests are arranged into +_test suites_, each of which is a root Terraform module which includes a +`module` block calling the module under test, and ideally also a number of +test assertions to verify that the module outputs match expectations. + +In the same directory where you keep your module's `.tf` and/or `.tf.json` +source files, create a subdirectory called `tests`. Under that directory, +make another directory which will serve as your first test suite, with a +directory name that concisely describes what the suite is aiming to test. + +Here's an example directory structure of a typical module directory layout +with the addition of a test suite called `defaults`: + +``` +main.tf +outputs.tf +providers.tf +variables.tf +versions.tf +tests/ + defaults/ + test_defaults.tf +``` + +The `tests/defaults/test_defaults.tf` file will contain a call to the +main module with a suitable set of arguments and hopefully also one or more +resources that will, for the sake of the experiment, serve as the temporary +syntax for defining test assertions. For example: + +```hcl +terraform { + required_providers { + # Because we're currently using a built-in provider as + # a substitute for dedicated Terraform language syntax + # for now, test suite modules must always declare a + # dependency on this provider. This provider is only + # available when running tests, so you shouldn't use it + # in non-test modules. + test = { + source = "terraform.io/builtin/test" + } + + # This example also uses the "http" data source to + # verify the behavior of the hypothetical running + # service, so we should declare that too. + http = { + source = "hashicorp/http" + } + } +} + +module "main" { + # source is always ../.. for test suite configurations, + # because they are placed two subdirectories deep under + # the main module directory. + source = "../.." + + # This test suite is aiming to test the "defaults" for + # this module, so it doesn't set any input variables + # and just lets their default values be selected instead. +} + +# As with all Terraform modules, we can use local values +# to do any necessary post-processing of the results from +# the module in preparation for writing test assertions. +locals { + # This expression also serves as an implicit assertion + # that the base URL uses URL syntax; the test suite + # will fail if this function fails. + api_url_parts = regex( + "^(?:(?P[^:/?#]+):)?(?://(?P[^/?#]*))?", + module.main.api_url, + ) +} + +# The special test_assertions resource type, which belongs +# to the test provider we required above, is a temporary +# syntax for writing out explicit test assertions. +resource "test_assertions" "api_url" { + # "component" serves as a unique identifier for this + # particular set of assertions in the test results. + component = "api_url" + + # equal and check blocks serve as the test assertions. + # the labels on these blocks are unique identifiers for + # the assertions, to allow more easily tracking changes + # in success between runs. + + equal "scheme" { + description = "default scheme is https" + got = local.api_url_parts.scheme + want = "https" + } + + check "port_number" { + description = "default port number is 8080" + condition = can(regex(":8080$", local.api_url_parts.authority)) + } +} + +# We can also use data resources to respond to the +# behavior of the real remote system, rather than +# just to values within the Terraform configuration. +data "http" "api_response" { + depends_on = [ + # make sure the syntax assertions run first, so + # we'll be sure to see if it was URL syntax errors + # that let to this data resource also failing. + test_assertions.api_url, + ] + + url = module.main.api_url +} + +resource "test_assertions" "api_response" { + component = "api_response" + + check "valid_json" { + description = "base URL responds with valid JSON" + condition = can(jsondecode(data.http.api_response.body)) + } +} +``` + +If you like, you can create additional directories alongside +the `default` directory to define additional test suites that +pass different variable values into the main module, and +then include assertions that verify that the result has changed +in the expected way. + +### Running Your Tests + +The `terraform test` command aims to make it easier to exercise all of your +defined test suites at once, and see only the output related to any test +failures or errors. + +The current experimental incarnation of this command expects to be run from +your main module directory. In our example directory structure above, +that was the directory containing `main.tf` etc, and _not_ the specific test +suite directory containing `test_defaults.tf`. + +Because these test suites are integration tests rather than unit tests, you'll +need to set up any credentials files or environment variables needed by the +providers your module uses before running `terraform test`. The test command +will, for each suite: + +* Install the providers and any external modules the test configuration depends + on. +* Create an execution plan to create the objects declared in the module. +* Apply that execution plan to create the objects in the real remote system. +* Collect all of the test results from the apply step, which would also have + "created" the `test_assertions` resources. +* Destroy all of the objects recorded in the temporary test state, as if running + `terraform destroy` against the test configuration. + +```shellsession +$ terraform test +─── Failed: defaults.api_url.scheme (default scheme is https) ─────────────── +wrong value + got: "http" + want: "https" +───────────────────────────────────────────────────────────────────────────── +``` + +In this case, it seems like the module returned an `http` rather than an +`https` URL in the default case, and so the `defaults.api_url.scheme` +assertion failed, and the `terraform test` command detected and reported it. + +The `test_assertions` resource captures any assertion failures but does not +return an error, because that can then potentially allow downstream +assertions to also run and thus capture as much context as possible. +However, if Terraform encounters any _errors_ while processing the test +configuration it will halt processing, which may cause some of the test +assertions to be skipped. + +## Known Limitations + +The design above is very much a prototype aimed at gathering more experience +with the possibilities of testing inside the Terraform language. We know it's +currently somewhat non-ergonomic, and hope to improve on that in later phases +of research and design, but the main focus of this iteration is on available +functionality and so with that in mind there are some specific possibilities +that we know the current prototype doesn't support well: + +* Testing of subsequent updates to an existing deployment of a module. + Currently tests written in this way can only exercise the create and destroy + behaviors. + +* Assertions about expected errors. For a module that includes variable + validation rules and data resources that function as assertion checks, + the current prototype doesn't have any way to express that a particular + set of inputs is _expected_ to produce an error, and thus report a test + failure if it doesn't. We'll hopefully be able to improve on this in a future + iteration with the test assertions better integrated into the language. + +* Capturing context about failures. Due to this prototype using a provider as + an approximation for new assertion syntax, the `terraform test` command is + limited in how much context it's able to gather about failures. A design + more integrated into the language could potentially capture the source + expressions and input values to give better feedback about what went wrong, + similar to what Terraform typically returns from expression evaluation errors + in the main language. + +* Unit testing without creating real objects. Although we do hope to spend more + time researching possibilities for unit testing against fake test doubles in + the future, we've decided to focus on integration testing to start because + it feels like the better-defined problem. + +## Sending Feedback + +The sort of feedback we'd most like to see at this stage of the experiment is +to see the source code of any tests you've written against real modules using +the features described above, along with notes about anything that you +attempted to test but were blocked from doing so by limitations of the above +features. The most ideal way to share that would be to share a link to a +version control branch where you've added such tests, if your module is open +source. + +If you've previously written or attempted to write tests in an external +language, using a system like Terratest or kitchen-terraform, we'd also be +interested to hear about comparative differences between the two: what worked +well in each and what didn't work so well. + +Our ultimate goal is to work towards an integration testing methodology which +strikes the best compromise between the capabilities of these different +approaches, ideally avoiding a hard requirement on any particular external +language and fitting well into the Terraform workflow. + +Since this is still early work and likely to lead to unstructured discussion, +we'd like to gather feedback primarily via new topics in +[the community forum](https://discuss.hashicorp.com/c/terraform-core/27). That +way we can have some more freedom to explore different ideas and approaches +without the structural requirements we typically impose on GitHub issues. + +Any feedback you'd like to share would be very welcome! diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/providers/configuration.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/providers/configuration.html.md new file mode 100644 index 00000000..20cc28dc --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/providers/configuration.html.md @@ -0,0 +1,203 @@ +--- +layout: "language" +page_title: "Provider Configuration - Configuration Language" +sidebar_current: "docs-config-providers" +description: |- + Providers are responsible in Terraform for managing the lifecycle of a resource: create, read, update, delete. +--- + +# Provider Configuration + +Terraform relies on plugins called "providers" to interact with remote systems. + +Terraform configurations must declare which providers they require, so that +Terraform can install and use them. Additionally, some providers require +configuration (like endpoint URLs or cloud regions) before they can be used. + +- This page documents how to configure settings for providers. + +- The [Provider Requirements](/docs/language/providers/requirements.html) page documents how + to declare providers so Terraform can install them. + +## Provider Configuration + +Provider configurations belong in the root module of a Terraform configuration. +(Child modules receive their provider configurations from the root module; for +more information, see +[The Module `providers` Meta-Argument](/docs/language/meta-arguments/module-providers.html) +and [Module Development: Providers Within Modules](/docs/language/modules/develop/providers.html).) + +A provider configuration is created using a `provider` block: + +```hcl +provider "google" { + project = "acme-app" + region = "us-central1" +} +``` + +The name given in the block header (`"google"` in this example) is the +[local name](/docs/language/providers/requirements.html#local-names) of the provider to +configure. This provider should already be included in a `required_providers` +block. + +The body of the block (between `{` and `}`) contains configuration arguments for +the provider. Most arguments in this section are defined by the provider itself; +in this example both `project` and `region` are specific to the `google` +provider. + +You can use [expressions](/docs/language/expressions/index.html) in the values of these +configuration arguments, but can only reference values that are known before the +configuration is applied. This means you can safely reference input variables, +but not attributes exported by resources (with an exception for resource +arguments that are specified directly in the configuration). + +A provider's documentation should list which configuration arguments it expects. +For providers distributed on the +[Terraform Registry](https://registry.terraform.io), versioned documentation is +available on each provider's page, via the "Documentation" link in the +provider's header. + +Some providers can use shell environment variables (or other alternate sources, +like VM instance profiles) as values for some of their arguments; when +available, we recommend using this as a way to keep credentials out of your +version-controlled Terraform code. + +There are also two "meta-arguments" that are defined by Terraform itself +and available for all `provider` blocks: + +- [`alias`, for using the same provider with different configurations for different resources][inpage-alias] +- [`version`, which we no longer recommend][inpage-versions] (use + [provider requirements](/docs/language/providers/requirements.html) instead) + +Unlike many other objects in the Terraform language, a `provider` block may +be omitted if its contents would otherwise be empty. Terraform assumes an +empty default configuration for any provider that is not explicitly configured. + +## `alias`: Multiple Provider Configurations + +[inpage-alias]: #alias-multiple-provider-configurations + +You can optionally define multiple configurations for the same provider, and +select which one to use on a per-resource or per-module basis. The primary +reason for this is to support multiple regions for a cloud platform; other +examples include targeting multiple Docker hosts, multiple Consul hosts, etc. + +To create multiple configurations for a given provider, include multiple +`provider` blocks with the same provider name. For each additional non-default +configuration, use the `alias` meta-argument to provide an extra name segment. +For example: + +```hcl +# The default provider configuration; resources that begin with `aws_` will use +# it as the default, and it can be referenced as `aws`. +provider "aws" { + region = "us-east-1" +} + +# Additional provider configuration for west coast region; resources can +# reference this as `aws.west`. +provider "aws" { + alias = "west" + region = "us-west-2" +} +``` + +To declare a configuration alias within a module in order to receive an +alternate provider configuration from the parent module, add the +`configuration_aliases` argument to that provider's `required_providers` +entry. The following example declares both the `mycloud` and +`mycloud.alternate` provider configuration names within the containing module: + +```hcl +terraform { + required_providers { + mycloud = { + source = "mycorp/mycloud" + version = "~> 1.0" + configuration_aliases = [ mycloud.alternate ] + } + } +} +``` + +### Default Provider Configurations + +A `provider` block without an `alias` argument is the _default_ configuration +for that provider. Resources that don't set the `provider` meta-argument will +use the default provider configuration that matches the first word of the +resource type name. (For example, an `aws_instance` resource uses the default +`aws` provider configuration unless otherwise stated.) + +If every explicit configuration of a provider has an alias, Terraform uses the +implied empty configuration as that provider's default configuration. (If the +provider has any required configuration arguments, Terraform will raise an error +when resources default to the empty configuration.) + +### Referring to Alternate Provider Configurations + +When Terraform needs the name of a provider configuration, it expects a +reference of the form `.`. In the example above, +`aws.west` would refer to the provider with the `us-west-2` region. + +These references are special expressions. Like references to other named +entities (for example, `var.image_id`), they aren't strings and don't need to be +quoted. But they are only valid in specific meta-arguments of `resource`, +`data`, and `module` blocks, and can't be used in arbitrary expressions. + +### Selecting Alternate Provider Configurations + +By default, resources use a default provider configuration (one without an +`alias` argument) inferred from the first word of the resource type name. + +To use an alternate provider configuration for a resource or data source, set +its `provider` meta-argument to a `.` reference: + +```hcl +resource "aws_instance" "foo" { + provider = aws.west + + # ... +} +``` + +To select alternate provider configurations for a child module, use its +`providers` meta-argument to specify which provider configurations should be +mapped to which local provider names inside the module: + +```hcl +module "aws_vpc" { + source = "./aws_vpc" + providers = { + aws = aws.west + } +} +``` + +Modules have some special requirements when passing in providers; see +[The Module `providers` Meta-Argument](/docs/language/meta-arguments/module-providers.html) +for more details. In most cases, only _root modules_ should define provider +configurations, with all child modules obtaining their provider configurations +from their parents. + + + +## `version`: An Older Way to Manage Provider Versions + +[inpage-versions]: #provider-versions + +The `version` meta-argument specifies a version constraint for a provider, and +works the same way as the `version` argument in a +[`required_providers` block](/docs/language/providers/requirements.html). The version +constraint in a provider configuration is only used if `required_providers` +does not include one for that provider. + +**The `version` argument in provider configurations is deprecated.** +In Terraform 0.13 and later, version constraints should always be declared in +[the `required_providers` block](/docs/language/providers/requirements.html). The `version` +argument will be removed in a future version of Terraform. + +-> **Note:** The `version` meta-argument made sense before Terraform 0.13, since +Terraform could only install providers that were distributed by HashiCorp. Now +that Terraform can install providers from multiple sources, it makes more sense +to keep version constraints and provider source addresses together. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/providers/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/providers/index.html.md new file mode 100644 index 00000000..44f83010 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/providers/index.html.md @@ -0,0 +1,122 @@ +--- +layout: "language" +page_title: "Providers - Configuration Language" +--- + +# Providers + +Terraform relies on plugins called "providers" to interact with remote systems. + +Terraform configurations must declare which providers they require so that +Terraform can install and use them. Additionally, some providers require +configuration (like endpoint URLs or cloud regions) before they can be used. + +## What Providers Do + +Each provider adds a set of [resource types](/docs/language/resources/index.html) +and/or [data sources](/docs/language/data-sources/index.html) that Terraform can +manage. + +Every resource type is implemented by a provider; without providers, Terraform +can't manage any kind of infrastructure. + +Most providers configure a specific infrastructure platform (either cloud or +self-hosted). Providers can also offer local utilities for tasks like +generating random numbers for unique resource names. + +## Where Providers Come From + +Providers are distributed separately from Terraform itself, and each provider +has its own release cadence and version numbers. + +The [Terraform Registry](https://registry.terraform.io/browse/providers) +is the main directory of publicly available Terraform providers, and hosts +providers for most major infrastructure platforms. + +## How to Use Providers + +To use resources from a given provider, you need to include some information +about it in your configuration. See the following pages for details: + +- [Provider Requirements](/docs/language/providers/requirements.html) + documents how to declare providers so Terraform can install them. + +- [Provider Configuration](/docs/language/providers/configuration.html) + documents how to configure settings for providers. + +- [Dependency Lock File](/docs/language/dependency-lock.html) + documents an additional HCL file that can be included with a configuration, + which tells Terraform to always use a specific set of provider versions. + +## Provider Installation + +- Terraform Cloud and Terraform Enterprise install providers as part of every run. + +- Terraform CLI finds and installs providers when + [initializing a working directory](/docs/cli/init/index.html). It can + automatically download providers from a Terraform registry, or load them from + a local mirror or cache. If you are using a persistent working directory, you + must reinitialize whenever you change a configuration's providers. + + To save time and bandwidth, Terraform CLI supports an optional plugin + cache. You can enable the cache using the `plugin_cache_dir` setting in + [the CLI configuration file](/docs/cli/config/config-file.html). + +To ensure Terraform always installs the same provider versions for a given +configuration, you can use Terraform CLI to create a +[dependency lock file](/docs/language/dependency-lock.html) +and commit it to version control along with your configuration. If a lock file +is present, Terraform Cloud, CLI, and Enterprise will all obey it when +installing providers. + +> **Hands-on:** Try the [Lock and Upgrade Provider Versions](https://learn.hashicorp.com/tutorials/terraform/provider-versioning?in=terraform/configuration-language&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +## How to Find Providers + +To find providers for the infrastructure platforms you use, browse +[the providers section of the Terraform Registry](https://registry.terraform.io/browse/providers). + +Some providers on the Registry are developed and published by HashiCorp, some +are published by platform maintainers, and some are published by users and +volunteers. The provider listings use the following badges to indicate who +develops and maintains a given provider. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TierDescriptionNamespace
Official providers are owned and maintained by HashiCorp hashicorp
Verified providers are owned and maintained by third-party technology partners. Providers in this tier indicate HashiCorp has verified the authenticity of the Provider’s publisher, and that the partner is a member of the HashiCorp Technology Partner Program.Third-party organization, e.g. mongodb/mongodbatlas
Community providers are published to the Terraform Registry by individual maintainers, groups of maintainers, or other members of the Terraform community.
Maintainer’s individual or organization account, e.g. DeviaVir/gsuite
Archived Providers are Official or Verified Providers that are no longer maintained by HashiCorp or the community. This may occur if an API is deprecated or interest was low.hashicorp or third-party
+ + +## How to Develop Providers + +Providers are written in Go, using the Terraform Plugin SDK. For more +information on developing providers, see: + +- The [Extending Terraform](/docs/extend/index.html) documentation +- The [Call APIs with Terraform Providers](https://learn.hashicorp.com/collections/terraform/providers?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) + collection on HashiCorp Learn diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/providers/requirements.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/providers/requirements.html.md new file mode 100644 index 00000000..12819243 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/providers/requirements.html.md @@ -0,0 +1,424 @@ +--- +layout: "language" +page_title: "Provider Requirements - Configuration Language" +--- + +# Provider Requirements + +-> **Note:** This page is about a feature of Terraform 0.13 and later; it also +describes how to use the more limited version of that feature that was available +in Terraform 0.12. + +Terraform relies on plugins called "providers" to interact with remote systems. + +Terraform configurations must declare which providers they require, so that +Terraform can install and use them. Additionally, some providers require +configuration (like endpoint URLs or cloud regions) before they can be used. + +- This page documents how to declare providers so Terraform can install them. + +- The [Provider Configuration](/docs/language/providers/configuration.html) page documents how to configure + settings for providers. + +## Requiring Providers + +Each Terraform module must declare which providers it requires, so that +Terraform can install and use them. Provider requirements are declared in a +`required_providers` block. + +A provider requirement consists of a local name, a source location, and a +version constraint: + +```hcl +terraform { + required_providers { + mycloud = { + source = "mycorp/mycloud" + version = "~> 1.0" + } + } +} +``` + +The `required_providers` block must be nested inside the top-level +[`terraform` block](/docs/language/settings/index.html) (which can also contain other settings). + +Each argument in the `required_providers` block enables one provider. The key +determines the provider's [local name](#local-names) (its unique identifier +within this module), and the value is an object with the following elements: + +* `source` - the global [source address](#source-addresses) for the + provider you intend to use, such as `hashicorp/aws`. + +* `version` - a [version constraint](#version-constraints) specifying + which subset of available provider versions the module is compatible with. + +-> **Note:** The `name = { source, version }` syntax for `required_providers` +was added in Terraform v0.13. Previous versions of Terraform used a version +constraint string instead of an object (like `mycloud = "~> 1.0"`), and had no +way to specify provider source addresses. If you want to write a module that +works with both Terraform v0.12 and v0.13, see [v0.12-Compatible Provider +Requirements](#v0-12-compatible-provider-requirements) below. + +## Names and Addresses + +Each provider has two identifiers: + +- A unique _source address,_ which is only used when requiring a provider. +- A _local name,_ which is used everywhere else in a Terraform module. + +-> **Note:** Prior to Terraform 0.13, providers only had local names, since +Terraform could only automatically download providers distributed by HashiCorp. + +### Local Names + +Local names are module-specific, and are assigned when requiring a provider. +Local names must be unique per-module. + +Outside of the `required_providers` block, Terraform configurations always refer +to providers by their local names. For example, the following configuration +declares `mycloud` as the local name for `mycorp/mycloud`, then uses that local +name when [configuring the provider](/docs/language/providers/configuration.html): + +```hcl +terraform { + required_providers { + mycloud = { + source = "mycorp/mycloud" + version = "~> 1.0" + } + } +} + +provider "mycloud" { + # ... +} +``` + +Users of a provider can choose any local name for it. However, nearly every +provider has a _preferred local name,_ which it uses as a prefix for all of its +resource types. (For example, resources from `hashicorp/aws` all begin with +`aws`, like `aws_instance` or `aws_security_group`.) + +Whenever possible, you should use a provider's preferred local name. This makes +your configurations easier to understand, and lets you omit the `provider` +meta-argument from most of your resources. (If a resource doesn't specify which +provider configuration to use, Terraform interprets the first word of the +resource type as a local provider name.) + +### Source Addresses + +A provider's source address is its global identifier. It also specifies the +primary location where Terraform can download it. + +Source addresses consist of three parts delimited by slashes (`/`), as +follows: + +`[/]/` + +* **Hostname** (optional): The hostname of the Terraform registry that + distributes the provider. If omitted, this defaults to + `registry.terraform.io`, the hostname of + [the public Terraform Registry](https://registry.terraform.io/). + +* **Namespace:** An organizational namespace within the specified registry. + For the public Terraform Registry and for Terraform Cloud's private registry, + this represents the organization that publishes the provider. This field + may have other meanings for other registry hosts. + +* **Type:** A short name for the platform or system the provider manages. Must + be unique within a particular namespace on a particular registry host. + + The type is usually the provider's preferred local name. (There are + exceptions; for example, + [`hashicorp/google-beta`](https://registry.terraform.io/providers/hashicorp/google-beta/latest) + is an alternate release channel for `hashicorp/google`, so its preferred + local name is `google`. If in doubt, check the provider's documentation.) + +For example, +[the official HTTP provider](https://registry.terraform.io/providers/hashicorp/http) +belongs to the `hashicorp` namespace on `registry.terraform.io`, so its +source address is `registry.terraform.io/hashicorp/http` or, more commonly, just +`hashicorp/http`. + +The source address with all three components given explicitly is called the +provider's _fully-qualified address_. You will see fully-qualified address in +various outputs, like error messages, but in most cases a simplified display +version is used. This display version omits the source host when it is the +public registry, so you may see the shortened version `"hashicorp/random"` instead +of `"registry.terraform.io/hashicorp/random"`. + + +-> **Note:** If you omit the `source` argument when requiring a provider, +Terraform uses an implied source address of +`registry.terraform.io/hashicorp/`. This is a backward compatibility +feature to support the transition to Terraform 0.13; in modules that require +0.13 or later, we recommend using explicit source addresses for all providers. + +### Handling Local Name Conflicts + +Whenever possible, we recommend using a provider's preferred local name, which +is usually the same as the "type" portion of its source address. + +However, it's sometimes necessary to use two providers with the same preferred +local name in the same module, usually when the providers are named after a +generic infrastructure type. Terraform requires unique local names for each +provider in a module, so you'll need to use a non-preferred name for at least +one of them. + +When this happens, we recommend combining each provider's namespace with +its type name to produce compound local names with a dash: + +```hcl +terraform { + required_providers { + # In the rare situation of using two providers that + # have the same type name -- "http" in this example -- + # use a compound local name to distinguish them. + hashicorp-http = { + source = "hashicorp/http" + version = "~> 2.0" + } + mycorp-http = { + source = "mycorp/http" + version = "~> 1.0" + } + } +} + +# References to these providers elsewhere in the +# module will use these compound local names. +provider "mycorp-http" { + # ... +} + +data "http" "example" { + provider = hashicorp-http + #... +} +``` + +Terraform won't be able to guess either provider's name from its resource types, +so you'll need to specify a `provider` meta-argument for every affected +resource. However, readers and maintainers of your module will be able to easily +understand what's happening, and avoiding confusion is much more important than +avoiding typing. + +## Version Constraints + +Each provider plugin has its own set of available versions, allowing the +functionality of the provider to evolve over time. Each provider dependency you +declare should have a [version constraint](/docs/language/expressions/version-constraints.html) given in +the `version` argument so Terraform can select a single version per provider +that all modules are compatible with. + +The `version` argument is optional; if omitted, Terraform will accept any +version of the provider as compatible. However, we strongly recommend specifying +a version constraint for every provider your module depends on. + +To ensure Terraform always installs the same provider versions for a given +configuration, you can use Terraform CLI to create a +[dependency lock file](/docs/language/dependency-lock.html) +and commit it to version control along with your configuration. If a lock file +is present, Terraform Cloud, CLI, and Enterprise will all obey it when +installing providers. + +> **Hands-on:** Try the [Lock and Upgrade Provider Versions](https://learn.hashicorp.com/tutorials/terraform/provider-versioning?in=terraform/configuration-language&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +### Best Practices for Provider Versions + +Each module should at least declare the minimum provider version it is known +to work with, using the `>=` version constraint syntax: + +```hcl +terraform { + required_providers { + mycloud = { + source = "hashicorp/aws" + version = ">= 1.0" + } + } +} +``` + +A module intended to be used as the root of a configuration — that is, as the +directory where you'd run `terraform apply` — should also specify the +_maximum_ provider version it is intended to work with, to avoid accidental +upgrades to incompatible new versions. The `~>` operator is a convenient +shorthand for allowing only patch releases within a specific minor release: + +```hcl +terraform { + required_providers { + mycloud = { + source = "hashicorp/aws" + version = "~> 1.0.4" + } + } +} +``` + +Do not use `~>` (or other maximum-version constraints) for modules you intend to +reuse across many configurations, even if you know the module isn't compatible +with certain newer versions. Doing so can sometimes prevent errors, but more +often it forces users of the module to update many modules simultaneously when +performing routine upgrades. Specify a minimum version, document any known +incompatibilities, and let the root module manage the maximum version. + +## Built-in Providers + +While most Terraform providers are distributed separately as plugins, there +is currently one provider that is built in to Terraform itself, which +provides +[the `terraform_remote_state` data source](/docs/language/state/remote-state-data.html). + +Because this provider is built in to Terraform, you don't need to declare it +in the `required_providers` block in order to use its features. However, for +consistency it _does_ have a special provider source address, which is +`terraform.io/builtin/terraform`. This address may sometimes appear in +Terraform's error messages and other output in order to unambiguously refer +to the built-in provider, as opposed to a hypothetical third-party provider +with the type name "terraform". + +There is also an existing provider with the source address +`hashicorp/terraform`, which is an older version of the now-built-in provider +that was used by older versions of Terraform. `hashicorp/terraform` is not +compatible with Terraform v0.11 or later and should never be declared in a +`required_providers` block. + +## In-house Providers + +Anyone can develop and distribute their own Terraform providers. See +the [Call APIs with Terraform Providers](https://learn.hashicorp.com/collections/terraform/providers?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) +collection on HashiCorp Learn for more +about provider development. + +Some organizations develop their own providers to configure +proprietary systems, and wish to use these providers from Terraform without +publishing them on the public Terraform Registry. + +One option for distributing such a provider is to run an in-house _private_ +registry, by implementing +[the provider registry protocol](/docs/internals/provider-registry-protocol.html). + +Running an additional service just to distribute a single provider internally +may be undesirable, so Terraform also supports +[other provider installation methods](/docs/cli/config/config-file.html#provider-installation), +including placing provider plugins directly in specific directories in the +local filesystem, via _filesystem mirrors_. + +All providers must have a [source address](#source-addresses) that includes +(or implies) the hostname of a registry, but that hostname does not need to +provide an actual registry service. For in-house providers that you intend to +distribute from a local filesystem directory, you can use an arbitrary hostname +in a domain your organization controls. + +For example, if your corporate domain were `example.com` then you might choose +to use `terraform.example.com` as your placeholder hostname, even if that +hostname doesn't actually resolve in DNS. You can then choose any namespace and +type you wish to represent your in-house provider under that hostname, giving +a source address like `terraform.example.com/examplecorp/ourcloud`: + +```hcl +terraform { + required_providers { + mycloud = { + source = "terraform.example.com/examplecorp/ourcloud" + version = ">= 1.0" + } + } +} +``` + +To make version 1.0.0 of this provider available for installation from the +local filesystem, choose one of the +[implied local mirror directories](/docs/cli/config/config-file.html#implied-local-mirror-directories) +and create a directory structure under it like this: + +``` +terraform.example.com/examplecorp/ourcloud/1.0.0 +``` + +Under that `1.0.0` directory, create one additional directory representing the +platform where you are running Terraform, such as `linux_amd64` for Linux on +an AMD64/x64 processor, and then place the provider plugin executable and any +other needed files in that directory. + +Thus, on a Windows system, the provider plugin executable file might be at the +following path: + +``` +terraform.example.com/examplecorp/ourcloud/1.0.0/windows_amd64/terraform-provider-ourcloud.exe +``` + +If you later decide to switch to using a real private provider registry rather +than distribute binaries out of band, you can deploy the registry server at +`terraform.example.com` and retain the same namespace and type names, in which +case your existing modules will require no changes to locate the same provider +using your registry server. + +## v0.12-Compatible Provider Requirements + +Explicit provider source addresses were introduced with Terraform v0.13, so the +full provider requirements syntax is not supported by Terraform v0.12. + +However, in order to allow writing modules that are compatible with both +Terraform v0.12 and v0.13, versions of Terraform between v0.12.26 and v0.13 +will accept but ignore the `source` argument in a `required_providers` block. + +Consider the following example written for Terraform v0.13: + +```hcl +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 1.0" + } + } +} +``` + +Terraform v0.12.26 will accept syntax like the above but will understand it +in the same way as the following v0.12-style syntax: + +```hcl +terraform { + required_providers { + aws = "~> 1.0" + } +} +``` + +In other words, Terraform v0.12.26 ignores the `source` argument and considers +only the `version` argument, using the given [local name](#local-names) as the +un-namespaced provider type to install. + +When writing a module that is compatible with both Terraform v0.12.26 and +Terraform v0.13.0 or later, you must follow the following additional rules so +that both versions will select the same provider to install: + +* Use only providers that can be automatically installed by Terraform v0.12. + Third-party providers, such as community providers in the Terraform Registry, + cannot be selected by Terraform v0.12 because it does not support the + hierarchical source address namespace. + +* Ensure that your chosen local name exactly matches the "type" portion of the + source address given in the `source` argument, such as both being "aws" in + the examples above, because Terraform v0.12 will use the local name to + determine which provider plugin to download and install. + +* If the provider belongs to the `hashicorp` namespace, as with the + `hashicorp/aws` provider shown above, omit the `source` argument and allow + Terraform v0.13 to select the `hashicorp` namespace by default. + +* Provider type names must always be written in lowercase. Terraform v0.13 + treats provider source addresses as case-insensitive, but Terraform v0.12 + considers its legacy-style provider names to be case-sensitive. Using + lowercase will ensure that the name is selectable by both Terraform major + versions. + +This compatibility mechanism is provided as a temporary transitional aid only. +When Terraform v0.12 detects a use of the new `source` argument it doesn't +understand, it will emit a warning to alert the user that it is disregarding +the source address given in that argument. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/resources/behavior.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/behavior.html.md new file mode 100644 index 00000000..70c0e2da --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/behavior.html.md @@ -0,0 +1,108 @@ +--- +layout: "language" +page_title: "Resource Behavior - Configuration Language" +--- + +# Resource Behavior + +A `resource` block declares that you want a particular infrastructure object +to exist with the given settings. If you are writing a new configuration for +the first time, the resources it defines will exist _only_ in the configuration, +and will not yet represent real infrastructure objects in the target platform. + +_Applying_ a Terraform configuration is the process of creating, updating, +and destroying real infrastructure objects in order to make their settings +match the configuration. + +## How Terraform Applies a Configuration + +When Terraform creates a new infrastructure object represented by a `resource` +block, the identifier for that real object is saved in Terraform's +[state](/docs/language/state/index.html), allowing it to be updated and destroyed +in response to future changes. For resource blocks that already have an +associated infrastructure object in the state, Terraform compares the +actual configuration of the object with the arguments given in the +configuration and, if necessary, updates the object to match the configuration. + +In summary, applying a Terraform configuration will: + +- _Create_ resources that exist in the configuration but are not associated with a real infrastructure object in the state. +- _Destroy_ resources that exist in the state but no longer exist in the configuration. +- _Update in-place_ resources whose arguments have changed. +- _Destroy and re-create_ resources whose arguments have changed but which cannot be updated in-place due to remote API limitations. + +This general behavior applies for all resources, regardless of type. The +details of what it means to create, update, or destroy a resource are different +for each resource type, but this standard set of verbs is common across them +all. + +The meta-arguments within `resource` blocks, documented in the +sections below, allow some details of this standard resource behavior to be +customized on a per-resource basis. + +## Accessing Resource Attributes + +[Expressions](/docs/language/expressions/index.html) within a Terraform module can access +information about resources in the same module, and you can use that information +to help configure other resources. Use the `..` +syntax to reference a resource attribute in an expression. + +In addition to arguments specified in the configuration, resources often provide +read-only attributes with information obtained from the remote API; this often +includes things that can't be known until the resource is created, like the +resource's unique random ID. + +Many providers also include [data sources](/docs/language/data-sources/index.html), +which are a special type of resource used only for looking up information. + +For a list of the attributes a resource or data source type provides, consult +its documentation; these are generally included in a second list below its list +of configurable arguments. + +For more information about referencing resource attributes in expressions, see +[Expressions: References to Resource Attributes](/docs/language/expressions/references.html#references-to-resource-attributes). + +## Resource Dependencies + +Most resources in a configuration don't have any particular relationship, and +Terraform can make changes to several unrelated resources in parallel. + +However, some resources must be processed after other specific resources; +sometimes this is because of how the resource works, and sometimes the +resource's configuration just requires information generated by another +resource. + +Most resource dependencies are handled automatically. Terraform analyses any +[expressions](/docs/language/expressions/index.html) within a `resource` block to find references +to other objects, and treats those references as implicit ordering requirements +when creating, updating, or destroying resources. Since most resources with +behavioral dependencies on other resources also refer to those resources' data, +it's usually not necessary to manually specify dependencies between resources. + +However, some dependencies cannot be recognized implicitly in configuration. For +example, if Terraform must manage access control policies _and_ take actions +that require those policies to be present, there is a hidden dependency between +the access policy and a resource whose creation depends on it. In these rare +cases, +[the `depends_on` meta-argument](/docs/language/meta-arguments/depends_on.html) +can explicitly specify a dependency. + +## Local-only Resources + +While most resource types correspond to an infrastructure object type that +is managed via a remote network API, there are certain specialized resource +types that operate only within Terraform itself, calculating some results and +saving those results in the state for future use. + +For example, local-only resource types exist for +[generating private keys](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/private_key), +[issuing self-signed TLS certificates](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/self_signed_cert), +and even [generating random ids](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id). +While these resource types often have a more marginal purpose than those +managing "real" infrastructure objects, they can be useful as glue to help +connect together other resources. + +The behavior of local-only resources is the same as all other resources, but +their result data exists only within the Terraform state. "Destroying" such +a resource means only to remove it from the state, discarding its data. + diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/resources/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/index.html.md new file mode 100644 index 00000000..891202d3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/index.html.md @@ -0,0 +1,34 @@ +--- +layout: "language" +page_title: "Resources Overview - Configuration Language" +--- + +# Resources + +> **Hands-on:** Try the [Terraform: Get Started](https://learn.hashicorp.com/collections/terraform/aws-get-started?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. + +_Resources_ are the most important element in the Terraform language. +Each resource block describes one or more infrastructure objects, such +as virtual networks, compute instances, or higher-level components such +as DNS records. + +- [Resource Blocks](/docs/language/resources/syntax.html) documents + the syntax for declaring resources. + +- [Resource Behavior](/docs/language/resources/behavior.html) explains in + more detail how Terraform handles resource declarations when applying a + configuration. + +- The Meta-Arguments section documents special arguments that can be used with + every resource type, including + [`depends_on`](/docs/language/meta-arguments/depends_on.html), + [`count`](/docs/language/meta-arguments/count.html), + [`for_each`](/docs/language/meta-arguments/for_each.html), + [`provider`](/docs/language/meta-arguments/resource-provider.html), + and [`lifecycle`](/docs/language/meta-arguments/lifecycle.html). + +- [Provisioners](/docs/language/resources/provisioners/index.html) + documents configuring post-creation actions for a resource using the + `provisioner` and `connection` blocks. Since provisioners are non-declarative + and potentially unpredictable, we strongly recommend that you treat them as a + last resort. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/chef.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/chef.html.md new file mode 100644 index 00000000..e420b9a3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/chef.html.md @@ -0,0 +1,181 @@ +--- +layout: "language" +page_title: "Provisioner: chef" +sidebar_current: "docs-provisioners-chef" +description: |- + The `chef` provisioner installs, configures and runs the Chef client on a resource. +--- + +# Chef Provisioner + +The `chef` provisioner installs, configures and runs the Chef Client on a remote +resource. The `chef` provisioner supports both `ssh` and `winrm` type +[connections](/docs/language/resources/provisioners/connection.html). + +!> **Note:** This provisioner was removed in the 0.15.0 version of Terraform after being deprecated as of Terraform 0.13.4. For most common situations there are better alternatives to using provisioners. For more information, see [the main Provisioners page](./). + +## Requirements + +The `chef` provisioner has some prerequisites for specific connection types: + +* For `ssh` type connections, `cURL` must be available on the remote host. +* For `winrm` connections, `PowerShell 2.0` must be available on the remote host. + +[Chef end user license agreement](https://www.chef.io/end-user-license-agreement/) must be accepted by setting `chef_license` to `accept` in `client_options` argument unless you are installing an old version of Chef client. + +Without these prerequisites, your provisioning execution will fail. + +## Example usage + +```hcl +resource "aws_instance" "web" { + # ... + + provisioner "chef" { + attributes_json = < **Note:** Provisioners should only be used as a last resort. For most +common situations there are better alternatives. For more information, see +[the main Provisioners page](./). + +-> **Note:** In Terraform 0.11 and earlier, providers could set default values +for some connection settings, so that `connection` blocks could sometimes be +omitted. This feature was removed in 0.12 in order to make Terraform's behavior +more predictable. + +-> **Note:** Since the SSH connection type is most often used with +newly-created remote resources, validation of SSH host keys is disabled by +default. In scenarios where this is not acceptable, a separate mechanism for +key distribution could be established and the `host_key` directive documented +below explicitly set to verify against a specific key or signing CA. + +Connection blocks don't take a block label, and can be nested within either a +`resource` or a `provisioner`. + +- A `connection` block nested directly within a `resource` affects all of + that resource's provisioners. +- A `connection` block nested in a `provisioner` block only affects that + provisioner, and overrides any resource-level connection settings. + +One use case for providing multiple connections is to have an initial +provisioner connect as the `root` user to set up user accounts, and have +subsequent provisioners connect as a user with more limited permissions. + +## Example usage + +```hcl +# Copies the file as the root user using SSH +provisioner "file" { + source = "conf/myapp.conf" + destination = "/etc/myapp.conf" + + connection { + type = "ssh" + user = "root" + password = "${var.root_password}" + host = "${var.host}" + } +} + +# Copies the file as the Administrator user using WinRM +provisioner "file" { + source = "conf/myapp.conf" + destination = "C:/App/myapp.conf" + + connection { + type = "winrm" + user = "Administrator" + password = "${var.admin_password}" + host = "${var.host}" + } +} +``` + +## The `self` Object + +Expressions in `connection` blocks cannot refer to their parent resource by +name. Instead, they can use the special `self` object. + +The `self` object represents the connection's parent resource, and has all of +that resource's attributes. For example, use `self.public_ip` to reference an +`aws_instance`'s `public_ip` attribute. + +-> **Technical note:** Resource references are restricted here because +references create dependencies. Referring to a resource by name within its own +block would create a dependency cycle. + +## Argument Reference + +**The following arguments are supported by all connection types:** + +* `type` - The connection type that should be used. Valid types are `ssh` and `winrm`. + Defaults to `ssh`. + +* `user` - The user that we should use for the connection. + Defaults to `root` when using type `ssh` and defaults to `Administrator` when using type `winrm`. + +* `password` - The password we should use for the connection. In some cases this is + specified by the provider. + +* `host` - (Required) The address of the resource to connect to. + +* `port` - The port to connect to. + Defaults to `22` when using type `ssh` and defaults to `5985` when using type `winrm`. + +* `timeout` - The timeout to wait for the connection to become available. Should be provided as a string like `30s` or `5m`. + Defaults to 5 minutes. + +* `script_path` - The path used to copy scripts meant for remote execution. + +**Additional arguments only supported by the `ssh` connection type:** + +* `private_key` - The contents of an SSH key to use for the connection. These can + be loaded from a file on disk using + [the `file` function](/docs/language/functions/file.html). This takes + preference over the password if provided. + +* `certificate` - The contents of a signed CA Certificate. The certificate argument must be + used in conjunction with a `private_key`. These can + be loaded from a file on disk using the [the `file` function](/docs/language/functions/file.html). + +* `agent` - Set to `false` to disable using `ssh-agent` to authenticate. On Windows the + only supported SSH authentication agent is + [Pageant](http://the.earth.li/~sgtatham/putty/0.66/htmldoc/Chapter9.html#pageant). + +* `agent_identity` - The preferred identity from the ssh agent for authentication. + +* `host_key` - The public key from the remote host or the signing CA, used to + verify the connection. +* `target_platform` - The target platform to connect to. Valid values are `windows` and `unix`. Defaults to `unix` if not set. + + If the platform is set to `windows`, the default `script_path` is `c:\windows\temp\terraform_%RAND%.cmd`, assuming [the SSH default shell](https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_server_configuration#configuring-the-default-shell-for-openssh-in-windows) is `cmd.exe`. If the SSH default shell is PowerShell, set `script_path` to `"c:/windows/temp/terraform_%RAND%.ps1"` + +**Additional arguments only supported by the `winrm` connection type:** + +* `https` - Set to `true` to connect using HTTPS instead of HTTP. + +* `insecure` - Set to `true` to not validate the HTTPS certificate chain. + +* `use_ntlm` - Set to `true` to use NTLM authentication, rather than default (basic authentication), removing the requirement for basic authentication to be enabled within the target guest. Further reading for remote connection authentication can be found [here](https://msdn.microsoft.com/en-us/library/aa384295(v=vs.85).aspx). + +* `cacert` - The CA certificate to validate against. + + + +## Connecting through a Bastion Host with SSH + +The `ssh` connection also supports the following fields to facilitate connnections via a +[bastion host](https://en.wikipedia.org/wiki/Bastion_host). + +* `bastion_host` - Setting this enables the bastion Host connection. This host + will be connected to first, and then the `host` connection will be made from there. + +* `bastion_host_key` - The public key from the remote host or the signing CA, + used to verify the host connection. + +* `bastion_port` - The port to use connect to the bastion host. Defaults to the + value of the `port` field. + +* `bastion_user` - The user for the connection to the bastion host. Defaults to + the value of the `user` field. + +* `bastion_password` - The password we should use for the bastion host. + Defaults to the value of the `password` field. + +* `bastion_private_key` - The contents of an SSH key file to use for the bastion + host. These can be loaded from a file on disk using + [the `file` function](/docs/language/functions/file.html). + Defaults to the value of the `private_key` field. + +* `bastion_certificate` - The contents of a signed CA Certificate. The certificate argument + must be used in conjunction with a `bastion_private_key`. These can be loaded from + a file on disk using the [the `file` function](/docs/language/functions/file.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/file.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/file.html.md new file mode 100644 index 00000000..05b494e5 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/file.html.md @@ -0,0 +1,92 @@ +--- +layout: "language" +page_title: "Provisioner: file" +sidebar_current: "docs-provisioners-file" +description: |- + The `file` provisioner is used to copy files or directories from the machine executing Terraform to the newly created resource. The `file` provisioner supports both `ssh` and `winrm` type connections. +--- + +# File Provisioner + +The `file` provisioner is used to copy files or directories from the machine +executing Terraform to the newly created resource. The `file` provisioner +supports both `ssh` and `winrm` type [connections](/docs/language/resources/provisioners/connection.html). + +-> **Note:** Provisioners should only be used as a last resort. For most +common situations there are better alternatives. For more information, see +[the main Provisioners page](./). + +## Example usage + +```hcl +resource "aws_instance" "web" { + # ... + + # Copies the myapp.conf file to /etc/myapp.conf + provisioner "file" { + source = "conf/myapp.conf" + destination = "/etc/myapp.conf" + } + + # Copies the string in content into /tmp/file.log + provisioner "file" { + content = "ami used: ${self.ami}" + destination = "/tmp/file.log" + } + + # Copies the configs.d folder to /etc/configs.d + provisioner "file" { + source = "conf/configs.d" + destination = "/etc" + } + + # Copies all files and folders in apps/app1 to D:/IIS/webapp1 + provisioner "file" { + source = "apps/app1/" + destination = "D:/IIS/webapp1" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `source` - This is the source file or folder. It can be specified as + relative to the current working directory or as an absolute path. This + attribute cannot be specified with `content`. + +* `content` - This is the content to copy on the destination. If destination is a file, + the content will be written on that file, in case of a directory a file named + `tf-file-content` is created. It's recommended to use a file as the destination. A + [`template_file`](https://registry.terraform.io/providers/hashicorp/template/latest/docs/data-sources/file) might be referenced in here, or + any interpolation syntax. This attribute cannot be specified with `source`. + +* `destination` - (Required) This is the destination path. It must be specified as an + absolute path. + +## Directory Uploads + +The file provisioner is also able to upload a complete directory to the remote machine. +When uploading a directory, there are a few important things you should know. + +First, when using the `ssh` connection type the destination directory must already exist. +If you need to create it, use a remote-exec provisioner just prior to the file provisioner +in order to create the directory. When using the `winrm` connection type the destination +directory will be created for you if it doesn't already exist. + +Next, the existence of a trailing slash on the source path will determine whether the +directory name will be embedded within the destination, or whether the destination will +be created. An example explains this best: + +If the source is `/foo` (no trailing slash), and the destination is `/tmp`, then the contents +of `/foo` on the local machine will be uploaded to `/tmp/foo` on the remote machine. The +`foo` directory on the remote machine will be created by Terraform. + +If the source, however, is `/foo/` (a trailing slash is present), and the destination is +`/tmp`, then the contents of `/foo` will be uploaded directly into `/tmp`. + +This behavior was adopted from the standard behavior of +[rsync](https://linux.die.net/man/1/rsync). + +-> **Note:** Under the covers, rsync may or may not be used. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/habitat.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/habitat.html.md new file mode 100644 index 00000000..66af5a65 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/habitat.html.md @@ -0,0 +1,96 @@ +--- +layout: "language" +page_title: "Provisioner: habitat" +sidebar_current: "docs-provisioners-habitat" +description: |- + The `habitat` provisioner installs the Habitat supervisor, and loads configured services. +--- + +# Habitat Provisioner + +The `habitat` provisioner installs the [Habitat](https://habitat.sh) supervisor and loads configured services. This provisioner only supports Linux targets using the `ssh` connection type at this time. + +!> **Note:** This provisioner was removed in the 0.15.0 version of Terraform after being deprecated as of Terraform 0.13.4. For most common situations there are better alternatives to using provisioners. For more information, see [the main Provisioners page](./). + +## Requirements + +The `habitat` provisioner has some prerequisites for specific connection types: + +- For `ssh` type connections, we assume a few tools to be available on the remote host: + * `curl` + * `tee` + * `setsid` - Only if using the `unmanaged` service type. + +Without these prerequisites, your provisioning execution will fail. + +## Example usage + +```hcl +resource "aws_instance" "redis" { + count = 3 + + provisioner "habitat" { + peers = [aws_instance.redis[0].private_ip] + use_sudo = true + service_type = "systemd" + accept_license = true + + service { + name = "core/redis" + topology = "leader" + user_toml = file("conf/redis.toml") + } + } +} + +``` + +## Argument Reference + +There are 2 configuration levels, `supervisor` and `service`. Configuration placed directly within the `provisioner` block are supervisor configurations, and a provisioner can define zero or more services to run, and each service will have a `service` block within the `provisioner`. A `service` block can also contain zero or more `bind` blocks to create service group bindings. + +### Supervisor Arguments +* `accept_license (bool)` - (Required) Set to true to accept [Habitat end user license agreement](https://www.chef.io/end-user-license-agreement/) +* `version (string)` - (Optional) The Habitat version to install on the remote machine. If not specified, the latest available version is used. +* `auto_update (bool)` - (Optional) If set to `true`, the supervisor will auto-update itself as soon as new releases are available on the specified `channel`. +* `http_disable (bool)` - (Optional) If set to `true`, disables the supervisor HTTP listener entirely. +* `peer (string)` - (Optional, deprecated) IP addresses or FQDN's for other Habitat supervisors to peer with, like: `--peer 1.2.3.4 --peer 5.6.7.8`. (Defaults to none) +* `peers (array)` - (Optional) A list of IP or FQDN's of other supervisor instance(s) to peer with. (Defaults to none) +* `service_type (string)` - (Optional) Method used to run the Habitat supervisor. Valid options are `unmanaged` and `systemd`. (Defaults to `systemd`) +* `service_name (string)` - (Optional) The name of the Habitat supervisor service, if using an init system such as `systemd`. (Defaults to `hab-supervisor`) +* `use_sudo (bool)` - (Optional) Use `sudo` when executing remote commands. Required when the user specified in the `connection` block is not `root`. (Defaults to `true`) +* `permanent_peer (bool)` - (Optional) Marks this supervisor as a permanent peer. (Defaults to false) +* `listen_ctl (string)` - (Optional) The listen address for the countrol gateway system (Defaults to 127.0.0.1:9632) +* `listen_gossip (string)` - (Optional) The listen address for the gossip system (Defaults to 0.0.0.0:9638) +* `listen_http (string)` - (Optional) The listen address for the HTTP gateway (Defaults to 0.0.0.0:9631) +* `ring_key (string)` - (Optional) The name of the ring key for encrypting gossip ring communication (Defaults to no encryption) +* `ring_key_content (string)` - (Optional) The key content. Only needed if using ring encryption and want the provisioner to take care of uploading and importing it. Easiest to source from a file (eg `ring_key_content = "${file("conf/foo-123456789.sym.key")}"`) (Defaults to none) +* `ctl_secret (string)` - (Optional) Specify a secret to use (from `hab sup secret generate`) for control gateway communication between hab client(s) and the supervisor. (Defaults to none) +* `url (string)` - (Optional) The URL of a Builder service to download packages and receive updates from. (Defaults to https://bldr.habitat.sh) +* `channel (string)` - (Optional) The release channel in the Builder service to use. (Defaults to `stable`) +* `events (string)` - (Optional) Name of the service group running a Habitat EventSrv to forward Supervisor and service event data to. (Defaults to none) +* `organization (string)` - (Optional) The organization that the Supervisor and it's subsequent services are part of. (Defaults to `default`) +* `gateway_auth_token (string)` - (Optional) The http gateway authorization token (Defaults to none) +* `builder_auth_token (string)` - (Optional) The builder authorization token when using a private origin. (Defaults to none) + +### Service Arguments +* `name (string)` - (Required) The Habitat package identifier of the service to run. (ie `core/haproxy` or `core/redis/3.2.4/20171002182640`) +* `binds (array)` - (Optional) An array of bind specifications. (ie `binds = ["backend:nginx.default"]`) +* `bind` - (Optional) An alternative way of declaring binds. This method can be easier to deal with when populating values from other values or variable inputs without having to do string interpolation. The following example is equivalent to `binds = ["backend:nginx.default"]`: + +```hcl +bind { + alias = "backend" + service = "nginx" + group = "default" +} +``` +* `topology (string)` - (Optional) Topology to start service in. Possible values `standalone` or `leader`. (Defaults to `standalone`) +* `strategy (string)` - (Optional) Update strategy to use. Possible values `at-once`, `rolling` or `none`. (Defaults to `none`) +* `user_toml (string)` - (Optional) TOML formatted user configuration for the service. Easiest to source from a file (eg `user_toml = "${file("conf/redis.toml")}"`). (Defaults to none) +* `channel (string)` - (Optional) The release channel in the Builder service to use. (Defaults to `stable`) +* `group (string)` - (Optional) The service group to join. (Defaults to `default`) +* `url (string)` - (Optional) The URL of a Builder service to download packages and receive updates from. (Defaults to https://bldr.habitat.sh) +* `application (string)` - (Optional) The application name. (Defaults to none) +* `environment (string)` - (Optional) The environment name. (Defaults to none) +* `service_key (string)` - (Optional) The key content of a service private key, if using service group encryption. Easiest to source from a file (eg `service_key = "${file("conf/redis.default@org-123456789.box.key")}"`) (Defaults to none) diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/index.html.md new file mode 100644 index 00000000..c9fef64b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/index.html.md @@ -0,0 +1,11 @@ +--- +layout: "language" +page_title: "Provisioners Overview - Configuration Language" +--- + +# Provisioners + +Provisioners can be used to model specific actions on the local machine or on a +remote machine in order to prepare servers or other infrastructure objects for +service. + diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/local-exec.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/local-exec.html.md new file mode 100644 index 00000000..f0d463ea --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/local-exec.html.md @@ -0,0 +1,95 @@ +--- +layout: "language" +page_title: "Provisioner: local-exec" +sidebar_current: "docs-provisioners-local" +description: |- + The `local-exec` provisioner invokes a local executable after a resource is created. This invokes a process on the machine running Terraform, not on the resource. See the `remote-exec` provisioner to run commands on the resource. +--- + +# local-exec Provisioner + +The `local-exec` provisioner invokes a local executable after a resource is +created. This invokes a process on the machine running Terraform, not on the +resource. See the `remote-exec` +[provisioner](/docs/language/resources/provisioners/remote-exec.html) to run commands on the +resource. + +Note that even though the resource will be fully created when the provisioner is +run, there is no guarantee that it will be in an operable state - for example +system services such as `sshd` may not be started yet on compute resources. + +-> **Note:** Provisioners should only be used as a last resort. For most +common situations there are better alternatives. For more information, see +[the main Provisioners page](./). + +## Example usage + +```hcl +resource "aws_instance" "web" { + # ... + + provisioner "local-exec" { + command = "echo ${self.private_ip} >> private_ips.txt" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `command` - (Required) This is the command to execute. It can be provided + as a relative path to the current working directory or as an absolute path. + It is evaluated in a shell, and can use environment variables or Terraform + variables. + +* `working_dir` - (Optional) If provided, specifies the working directory where + `command` will be executed. It can be provided as as a relative path to the + current working directory or as an absolute path. The directory must exist. + +* `interpreter` - (Optional) If provided, this is a list of interpreter + arguments used to execute the command. The first argument is the + interpreter itself. It can be provided as a relative path to the current + working directory or as an absolute path. The remaining arguments are + appended prior to the command. This allows building command lines of the + form "/bin/bash", "-c", "echo foo". If `interpreter` is unspecified, + sensible defaults will be chosen based on the system OS. + +* `environment` - (Optional) block of key value pairs representing the + environment of the executed command. inherits the current process environment. + +### Interpreter Examples + +```hcl +resource "null_resource" "example1" { + provisioner "local-exec" { + command = "open WFH, '>completed.txt' and print WFH scalar localtime" + interpreter = ["perl", "-e"] + } +} +``` + +```hcl +resource "null_resource" "example2" { + provisioner "local-exec" { + command = "Get-Date > completed.txt" + interpreter = ["PowerShell", "-Command"] + } +} +``` + +```hcl +resource "aws_instance" "web" { + # ... + + provisioner "local-exec" { + command = "echo $FOO $BAR $BAZ >> env_vars.txt" + + environment = { + FOO = "bar" + BAR = 1 + BAZ = "true" + } + } +} +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/null_resource.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/null_resource.html.md new file mode 100644 index 00000000..7fb94a4e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/null_resource.html.md @@ -0,0 +1,61 @@ +--- +layout: "language" +page_title: "Provisioners Without a Resource" +sidebar_current: "docs-provisioners-null-resource" +description: |- + The `null_resource` is a resource allows you to configure provisioners that + are not directly associated with a single existing resource. +--- + +# Provisioners Without a Resource + +[null]: https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource + +If you need to run provisioners that aren't directly associated with a specific +resource, you can associate them with a `null_resource`. + +Instances of [`null_resource`][null] are treated like normal resources, but they +don't do anything. Like with any other resource, you can configure +[provisioners](/docs/language/resources/provisioners/syntax.html) and [connection +details](/docs/language/resources/provisioners/connection.html) on a `null_resource`. You can also +use its `triggers` argument and any meta-arguments to control exactly where in +the dependency graph its provisioners will run. + +## Example usage + +```hcl +resource "aws_instance" "cluster" { + count = 3 + + # ... +} + +resource "null_resource" "cluster" { + # Changes to any instance of the cluster requires re-provisioning + triggers = { + cluster_instance_ids = "${join(",", aws_instance.cluster.*.id)}" + } + + # Bootstrap script can run on any instance of the cluster + # So we just choose the first in this case + connection { + host = "${element(aws_instance.cluster.*.public_ip, 0)}" + } + + provisioner "remote-exec" { + # Bootstrap script called with private_ip of each node in the cluster + inline = [ + "bootstrap-cluster.sh ${join(" ", aws_instance.cluster.*.private_ip)}", + ] + } +} +``` + +## Argument Reference + +In addition to meta-arguments supported by all resources, `null_resource` +supports the following specific arguments: + + * `triggers` - A map of values which should cause this set of provisioners to + re-run. Values are meant to be interpolated references to variables or + attributes of other resources. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/puppet.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/puppet.html.md new file mode 100644 index 00000000..0e500525 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/puppet.html.md @@ -0,0 +1,95 @@ +--- +layout: "language" +page_title: "Provisioner: puppet" +sidebar_current: "docs-provisioners-puppet" +description: |- + The `puppet` provisioner installs, configures and runs the Puppet agent on a resource. +--- + +# Puppet Provisioner + +The `puppet` provisioner installs, configures and runs the Puppet agent on a +remote resource. The `puppet` provisioner supports both `ssh` and `winrm` type +[connections](/docs/language/resources/provisioners/connection.html). + +!> **Note:** This provisioner was removed in the 0.15.0 version of Terraform after being deprecated as of Terraform 0.13.4. For most common situations there are better alternatives to using provisioners. For more information, see [the main Provisioners page](./). + +## Requirements + +The `puppet` provisioner has some prerequisites for specific connection types: + +* For `ssh` type connections, `cURL` must be available on the remote host. +* For `winrm` connections, `PowerShell 2.0` must be available on the remote host. + +Without these prerequisites, your provisioning execution will fail. + +Additionally, the `puppet` provisioner requires +[Bolt](https://puppet.com/docs/bolt/latest/bolt.html) to be installed on your workstation +with the following [modules +installed](https://puppet.com/docs/bolt/latest/bolt_installing_modules.html#install-modules) + +* `danieldreier/autosign` +* `puppetlabs/puppet_agent` + +## Example usage + +```hcl +resource "aws_instance" "web" { + # ... + + provisioner "puppet" { + server = aws_instance.puppetmaster.public_dns + server_user = "ubuntu" + extension_requests = { + pp_role = "webserver" + } + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `server (string)` - (Required) The FQDN of the Puppet master that the agent + is to connect to. + +* `server_user (string)` - (Optional) The user that Bolt should connect to the + server as (defaults to `root`). + +* `os_type (string)` - (Optional) The OS type of the resource. Valid options + are: `linux` and `windows`. If not supplied, the connection type will be used + to determine the OS type (`ssh` will assume `linux` and `winrm` will assume + `windows`). + +* `use_sudo (boolean)` - (Optional) If `true`, commands run on the resource + will have their privileges elevated with sudo (defaults to `true` when the OS + type is `linux` and `false` when the OS type is `windows`). + +* `autosign (boolean)` - (Optional) Set to `true` if the Puppet master is using an autosigner such as + [Daniel Dreier's policy-based autosigning + tool](https://danieldreier.github.io/autosign). If `false` new agent certificate requests will have to be signed manually (defaults to `true`). + +* `open_source (boolean)` - (Optional) If `true` the provisioner uses an open source Puppet compatible agent install method (push via the Bolt agent install task). If `false` the simplified Puppet Enterprise installer will pull the agent from the Puppet master (defaults to `true`). + +* `certname (string)` - (Optional) The Subject CN used when requesting + a certificate from the Puppet master CA (defaults to the FQDN of the + resource). + +* `extension_requests (map)` - (Optional) A map of [extension + requests](https://puppet.com/docs/puppet/latest/ssl_attributes_extensions.html#concept-932) + to be embedded in the certificate signing request before it is sent to the + Puppet master CA and then transferred to the final certificate when the CSR + is signed. These become available during Puppet agent runs as [trusted facts](https://puppet.com/docs/puppet/latest/lang_facts_and_builtin_vars.html#trusted-facts). Friendly names for common extensions such as pp_role and pp_environment have [been predefined](https://puppet.com/docs/puppet/latest/ssl_attributes_extensions.html#recommended-oids-for-extensions). + +* `custom_attributes (map)` - (Optional) A map of [custom + attributes](https://puppet.com/docs/puppet/latest/ssl_attributes_extensions.html#concept-5488) + to be embedded in the certificate signing request before it is sent to the + Puppet master CA. + +* `environment (string)` - (Optional) The name of the Puppet environment that the + Puppet agent will be running in (defaults to `production`). + +* `bolt_timeout (string)` - (Optional) The timeout to wait for Bolt tasks to + complete. This should be specified as a string like `30s` or `5m` (defaults + to `5m` - 5 minutes). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/remote-exec.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/remote-exec.html.md new file mode 100644 index 00000000..31272830 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/remote-exec.html.md @@ -0,0 +1,77 @@ +--- +layout: "language" +page_title: "Provisioner: remote-exec" +sidebar_current: "docs-provisioners-remote" +description: |- + The `remote-exec` provisioner invokes a script on a remote resource after it is created. This can be used to run a configuration management tool, bootstrap into a cluster, etc. To invoke a local process, see the `local-exec` provisioner instead. The `remote-exec` provisioner supports both `ssh` and `winrm` type connections. +--- + +# remote-exec Provisioner + +The `remote-exec` provisioner invokes a script on a remote resource after it +is created. This can be used to run a configuration management tool, bootstrap +into a cluster, etc. To invoke a local process, see the `local-exec` +[provisioner](/docs/language/resources/provisioners/local-exec.html) instead. The `remote-exec` +provisioner supports both `ssh` and `winrm` type [connections](/docs/language/resources/provisioners/connection.html). + +-> **Note:** Provisioners should only be used as a last resort. For most +common situations there are better alternatives. For more information, see +[the main Provisioners page](./). + +## Example usage + +```hcl +resource "aws_instance" "web" { + # ... + + provisioner "remote-exec" { + inline = [ + "puppet apply", + "consul join ${aws_instance.web.private_ip}", + ] + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `inline` - This is a list of command strings. They are executed in the order + they are provided. This cannot be provided with `script` or `scripts`. + +* `script` - This is a path (relative or absolute) to a local script that will + be copied to the remote resource and then executed. This cannot be provided + with `inline` or `scripts`. + +* `scripts` - This is a list of paths (relative or absolute) to local scripts + that will be copied to the remote resource and then executed. They are executed + in the order they are provided. This cannot be provided with `inline` or `script`. + +-> **Note:** Since `inline` is implemented by concatenating commands into a script, [`on_failure`](/docs/language/resources/provisioners/syntax.html#failure-behavior) applies only to the final command in the list. In particular, with `on_failure = fail` (the default behaviour) earlier commands will be allowed to fail, and later commands will also execute. If this behaviour is not desired, consider using `"set -o errexit"` as the first command. + +## Script Arguments + +You cannot pass any arguments to scripts using the `script` or +`scripts` arguments to this provisioner. If you want to specify arguments, +upload the script with the +[file provisioner](/docs/language/resources/provisioners/file.html) +and then use `inline` to call it. Example: + +```hcl +resource "aws_instance" "web" { + # ... + + provisioner "file" { + source = "script.sh" + destination = "/tmp/script.sh" + } + + provisioner "remote-exec" { + inline = [ + "chmod +x /tmp/script.sh", + "/tmp/script.sh args", + ] + } +} +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/provisioners/salt-masterless.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/salt-masterless.html.md similarity index 91% rename from vendor/github.com/hashicorp/terraform/website/docs/provisioners/salt-masterless.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/salt-masterless.html.md index 0e16265b..6cdb4f32 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/provisioners/salt-masterless.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/salt-masterless.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Provisioner: salt-masterless" sidebar_current: "docs-provisioners-salt-masterless" description: |- @@ -11,12 +11,9 @@ description: |- Type: `salt-masterless` The `salt-masterless` Terraform provisioner provisions machines built by Terraform -using [Salt](http://saltstack.com/) states, without connecting to a Salt master. The `salt-masterless` provisioner supports `ssh` [connections](/docs/provisioners/connection.html). +using [Salt](http://saltstack.com/) states, without connecting to a Salt master. The `salt-masterless` provisioner supports `ssh` [connections](/docs/language/resources/provisioners/connection.html). --> **Note:** This provisioner has been deprecated as of Terraform 0.13.4 and will be -removed in a future version of Terraform. For most common situations there are better -alternatives to using provisioners. For more information, see -[the main Provisioners page](./). +!> **Note:** This provisioner was removed in the 0.15.0 version of Terraform after being deprecated as of Terraform 0.13.4. For most common situations there are better alternatives to using provisioners. For more information, see [the main Provisioners page](./). ## Requirements diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/syntax.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/syntax.html.md new file mode 100644 index 00000000..3a8eb95e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/provisioners/syntax.html.md @@ -0,0 +1,305 @@ +--- +layout: "language" +page_title: "Provisioners" +sidebar_current: "docs-provisioners" +description: |- + Provisioners are used to execute scripts on a local or remote machine as part of resource creation or destruction. +--- + +# Provisioners + +Provisioners can be used to model specific actions on the local machine or on +a remote machine in order to prepare servers or other infrastructure objects +for service. + +## Provisioners are a Last Resort + +> **Hands-on:** To learn about more declarative ways to handle provisioning actions, try the [Provision Infrastructure Deployed with Terraform](https://learn.hashicorp.com/collections/terraform/provision?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. + +Terraform includes the concept of provisioners as a measure of pragmatism, +knowing that there will always be certain behaviors that can't be directly +represented in Terraform's declarative model. + +However, they also add a considerable amount of complexity and uncertainty to +Terraform usage. Firstly, Terraform cannot model the actions of provisioners +as part of a plan because they can in principle take any action. Secondly, +successful use of provisioners requires coordinating many more details than +Terraform usage usually requires: direct network access to your servers, +issuing Terraform credentials to log in, making sure that all of the necessary +external software is installed, etc. + +The following sections describe some situations which can be solved with +provisioners in principle, but where better solutions are also available. We do +not recommend using provisioners for any of the use-cases described in the +following sections. + +Even if your specific use-case is not described in the following sections, we +still recommend attempting to solve it using other techniques first, and use +provisioners only if there is no other option. + +### Passing data into virtual machines and other compute resources + +When deploying virtual machines or other similar compute resources, we often +need to pass in data about other related infrastructure that the software on +that server will need to do its job. + +The various provisioners that interact with remote servers over SSH or WinRM +can potentially be used to pass such data by logging in to the server and +providing it directly, but most cloud computing platforms provide mechanisms +to pass data to instances at the time of their creation such that the data +is immediately available on system boot. For example: + +* Alibaba Cloud: `user_data` on + [`alicloud_instance`](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/instance) + or [`alicloud_launch_template`](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/launch_template). +* Amazon EC2: `user_data` or `user_data_base64` on + [`aws_instance`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance), + [`aws_launch_template`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_template), + and [`aws_launch_configuration`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_configuration). +* Amazon Lightsail: `user_data` on + [`aws_lightsail_instance`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lightsail_instance). +* Microsoft Azure: `custom_data` on + [`azurerm_virtual_machine`](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_machine) + or [`azurerm_virtual_machine_scale_set`](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_machine_scale_set). +* Google Cloud Platform: `metadata` on + [`google_compute_instance`](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance) + or [`google_compute_instance_group`](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance_group). +* Oracle Cloud Infrastructure: `metadata` or `extended_metadata` on + [`oci_core_instance`](https://registry.terraform.io/providers/hashicorp/oci/latest/docs/resources/core_instance) + or [`oci_core_instance_configuration`](https://registry.terraform.io/providers/hashicorp/oci/latest/docs/resources/core_instance_configuration). +* VMware vSphere: Attach a virtual CDROM to + [`vsphere_virtual_machine`](https://registry.terraform.io/providers/hashicorp/vsphere/latest/docs/resources/virtual_machine) + using the `cdrom` block, containing a file called `user-data.txt`. + +Many official Linux distribution disk images include software called +[cloud-init](https://cloudinit.readthedocs.io/en/latest/) that can automatically +process in various ways data passed via the means described above, allowing +you to run arbitrary scripts and do basic system configuration immediately +during the boot process and without the need to access the machine over SSH. + +> **Hands-on:** Try the [Provision Infrastructure with Cloud-Init](https://learn.hashicorp.com/tutorials/terraform/cloud-init?in=terraform/provision&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +If you are building custom machine images, you can make use of the "user data" +or "metadata" passed by the above means in whatever way makes sense to your +application, by referring to your vendor's documentation on how to access the +data at runtime. + +This approach is _required_ if you intend to use any mechanism in your cloud +provider for automatically launching and destroying servers in a group, +because in that case individual servers will launch unattended while Terraform +is not around to provision them. + +Even if you're deploying individual servers directly with Terraform, passing +data this way will allow faster boot times and simplify deployment by avoiding +the need for direct network access from Terraform to the new server and for +remote access credentials to be provided. + +### Running configuration management software + +As a convenience to users who are forced to use generic operating system +distribution images, Terraform includes a number of specialized provisioners +for launching specific configuration management products. + +We strongly recommend not using these, and instead running system configuration +steps during a custom image build process. For example, +[HashiCorp Packer](https://packer.io/) offers a similar complement of +configuration management provisioners and can run their installation steps +during a separate build process, before creating a system disk image that you +can deploy many times. + +> **Hands-on:** Try the [Provision Infrastructure with Packer](https://learn.hashicorp.com/tutorials/terraform/packer?in=terraform/provision&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. + +If you are using configuration management software that has a centralized server +component, you will need to delay the _registration_ step until the final +system is booted from your custom image. To achieve that, use one of the +mechanisms described above to pass the necessary information into each instance +so that it can register itself with the configuration management server +immediately on boot, without the need to accept commands from Terraform over +SSH or WinRM. + +### First-class Terraform provider functionality may be available + +It is technically possible to use the `local-exec` provisioner to run the CLI +for your target system in order to create, update, or otherwise interact with +remote objects in that system. + +If you are trying to use a new feature of the remote system that isn't yet +supported in its Terraform provider, that might be the only option. However, +if there _is_ provider support for the feature you intend to use, prefer to +use that provider functionality rather than a provisioner so that Terraform +can be fully aware of the object and properly manage ongoing changes to it. + +Even if the functionality you need is not available in a provider today, we +suggest to consider `local-exec` usage a temporary workaround and to also +open an issue in the relevant provider's repository to discuss adding +first-class provider support. Provider development teams often prioritize +features based on interest, so opening an issue is a way to record your +interest in the feature. + +Provisioners are used to execute scripts on a local or remote machine +as part of resource creation or destruction. Provisioners can be used to +bootstrap a resource, cleanup before destroy, run configuration management, etc. + +## How to use Provisioners + +-> **Note:** Provisioners should only be used as a last resort. For most +common situations there are better alternatives. For more information, see +the sections above. + +If you are certain that provisioners are the best way to solve your problem +after considering the advice in the sections above, you can add a +`provisioner` block inside the `resource` block of a compute instance. + +```hcl +resource "aws_instance" "web" { + # ... + + provisioner "local-exec" { + command = "echo The server's IP address is ${self.private_ip}" + } +} +``` + +The `local-exec` provisioner requires no other configuration, but most other +provisioners must connect to the remote system using SSH or WinRM. +You must include [a `connection` block](./connection.html) so that Terraform +will know how to communicate with the server. + +Terraform includes several built-in provisioners; use the navigation sidebar to +view their documentation. + +It's also possible to use third-party provisioners as plugins, by placing them +in `%APPDATA%\terraform.d\plugins`, `~/.terraform.d/plugins`, or the same +directory where the Terraform binary is installed. However, we do not recommend +using any provisioners except the built-in `file`, `local-exec`, and +`remote-exec` provisioners. + +All provisioners support the `when` and `on_failure` meta-arguments, which +are described below (see [Destroy-Time Provisioners](#destroy-time-provisioners) +and [Failure Behavior](#failure-behavior)). + +### The `self` Object + +Expressions in `provisioner` blocks cannot refer to their parent resource by +name. Instead, they can use the special `self` object. + +The `self` object represents the provisioner's parent resource, and has all of +that resource's attributes. For example, use `self.public_ip` to reference an +`aws_instance`'s `public_ip` attribute. + +-> **Technical note:** Resource references are restricted here because +references create dependencies. Referring to a resource by name within its own +block would create a dependency cycle. + +## Suppressing Provisioner Logs in CLI Output + +The configuration for a `provisioner` block may use sensitive values, such as +[`sensitive` variables](/docs/language/values/variables.html#suppressing-values-in-cli-output) or +[`sensitive` output values](/docs/language/values/outputs.html#sensitive-suppressing-values-in-cli-output). +In this case, all log output from the provisioner is automatically suppressed to +prevent the sensitive values from being displayed. + +## Creation-Time Provisioners + +By default, provisioners run when the resource they are defined within is +created. Creation-time provisioners are only run during _creation_, not +during updating or any other lifecycle. They are meant as a means to perform +bootstrapping of a system. + +If a creation-time provisioner fails, the resource is marked as **tainted**. +A tainted resource will be planned for destruction and recreation upon the +next `terraform apply`. Terraform does this because a failed provisioner +can leave a resource in a semi-configured state. Because Terraform cannot +reason about what the provisioner does, the only way to ensure proper creation +of a resource is to recreate it. This is tainting. + +You can change this behavior by setting the `on_failure` attribute, +which is covered in detail below. + +## Destroy-Time Provisioners + +If `when = destroy` is specified, the provisioner will run when the +resource it is defined within is _destroyed_. + +```hcl +resource "aws_instance" "web" { + # ... + + provisioner "local-exec" { + when = destroy + command = "echo 'Destroy-time provisioner'" + } +} +``` + +Destroy provisioners are run before the resource is destroyed. If they +fail, Terraform will error and rerun the provisioners again on the next +`terraform apply`. Due to this behavior, care should be taken for destroy +provisioners to be safe to run multiple times. + +Destroy-time provisioners can only run if they remain in the configuration +at the time a resource is destroyed. If a resource block with a destroy-time +provisioner is removed entirely from the configuration, its provisioner +configurations are removed along with it and thus the destroy provisioner +won't run. To work around this, a multi-step process can be used to safely +remove a resource with a destroy-time provisioner: + +* Update the resource configuration to include `count = 0`. +* Apply the configuration to destroy any existing instances of the resource, including running the destroy provisioner. +* Remove the resource block entirely from configuration, along with its `provisioner` blocks. +* Apply again, at which point no further action should be taken since the resources were already destroyed. + +This limitation may be addressed in future versions of Terraform. For now, +destroy-time provisioners must be used sparingly and with care. + +~> **NOTE:** A destroy-time provisioner within a resource that is tainted _will not_ run. This includes resources that are marked tainted from a failed creation-time provisioner or tainted manually using `terraform taint`. + +## Multiple Provisioners + +Multiple provisioners can be specified within a resource. Multiple provisioners +are executed in the order they're defined in the configuration file. + +You may also mix and match creation and destruction provisioners. Only +the provisioners that are valid for a given operation will be run. Those +valid provisioners will be run in the order they're defined in the configuration +file. + +Example of multiple provisioners: + +```hcl +resource "aws_instance" "web" { + # ... + + provisioner "local-exec" { + command = "echo first" + } + + provisioner "local-exec" { + command = "echo second" + } +} +``` + +## Failure Behavior + +By default, provisioners that fail will also cause the Terraform apply +itself to fail. The `on_failure` setting can be used to change this. The +allowed values are: + +- `continue` - Ignore the error and continue with creation or destruction. + +- `fail` - Raise an error and stop applying (the default behavior). If this is a creation provisioner, + taint the resource. + +Example: + +```hcl +resource "aws_instance" "web" { + # ... + + provisioner "local-exec" { + command = "echo The server's IP address is ${self.private_ip}" + on_failure = continue + } +} +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/resources/syntax.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/syntax.html.md new file mode 100644 index 00000000..65d52537 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/resources/syntax.html.md @@ -0,0 +1,165 @@ +--- +layout: "language" +page_title: "Resources - Configuration Language" +sidebar_current: "docs-config-resources" +description: |- + Resources are the most important element in a Terraform configuration. + Each resource corresponds to an infrastructure object, such as a virtual + network or compute instance. +--- + +# Resource Blocks + +> **Hands-on:** Try the [Terraform: Get Started](https://learn.hashicorp.com/collections/terraform/aws-get-started?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. + +_Resources_ are the most important element in the Terraform language. +Each resource block describes one or more infrastructure objects, such +as virtual networks, compute instances, or higher-level components such +as DNS records. + +## Resource Syntax + +Resource declarations can include a number of advanced features, but only +a small subset are required for initial use. More advanced syntax features, +such as single resource declarations that produce multiple similar remote +objects, are described later in this page. + +```hcl +resource "aws_instance" "web" { + ami = "ami-a1b2c3d4" + instance_type = "t2.micro" +} +``` + +A `resource` block declares a resource of a given type ("aws_instance") +with a given local name ("web"). The name is used to refer to this resource +from elsewhere in the same Terraform module, but has no significance outside +that module's scope. + +The resource type and name together serve as an identifier for a given +resource and so must be unique within a module. + +Within the block body (between `{` and `}`) are the configuration arguments +for the resource itself. Most arguments in this section depend on the +resource type, and indeed in this example both `ami` and `instance_type` are +arguments defined specifically for [the `aws_instance` resource type](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance). + +-> **Note:** Resource names must start with a letter or underscore, and may +contain only letters, digits, underscores, and dashes. + +## Resource Types + +Each resource is associated with a single _resource type_, which determines +the kind of infrastructure object it manages and what arguments and other +attributes the resource supports. + +### Providers + +Each resource type is implemented by a [provider](/docs/language/providers/requirements.html), +which is a plugin for Terraform that offers a collection of resource types. A +provider usually provides resources to manage a single cloud or on-premises +infrastructure platform. Providers are distributed separately from Terraform +itself, but Terraform can automatically install most providers when initializing +a working directory. + +In order to manage resources, a Terraform module must specify which providers it +requires. Additionally, most providers need some configuration in order to +access their remote APIs, and the root module must provide that configuration. + +For more information, see: + +- [Provider Requirements](/docs/language/providers/requirements.html), for declaring which + providers a module uses. +- [Provider Configuration](/docs/language/providers/configuration.html), for configuring provider settings. + +Terraform usually automatically determines which provider to use based on a +resource type's name. (By convention, resource type names start with their +provider's preferred local name.) When using multiple configurations of a +provider (or non-preferred local provider names), you must use the `provider` +meta-argument to manually choose an alternate provider configuration. See +[the `provider` meta-argument](/docs/language/meta-arguments/resource-provider.html) for more details. + +### Resource Arguments + +Most of the arguments within the body of a `resource` block are specific to the +selected resource type. The resource type's documentation lists which arguments +are available and how their values should be formatted. + +The values for resource arguments can make full use of +[expressions](/docs/language/expressions/index.html) and other dynamic Terraform +language features. + +There are also some _meta-arguments_ that are defined by Terraform itself +and apply across all resource types. (See [Meta-Arguments](#meta-arguments) below.) + +### Documentation for Resource Types + +Every Terraform provider has its own documentation, describing its resource +types and their arguments. + +Most publicly available providers are distributed on the +[Terraform Registry](https://registry.terraform.io/browse/providers), which also +hosts their documentation. When viewing a provider's page on the Terraform +Registry, you can click the "Documentation" link in the header to browse its +documentation. Provider documentation on the registry is versioned, and you can +use the dropdown version menu in the header to switch which version's +documentation you are viewing. + +To browse the publicly available providers and their documentation, see +[the providers section of the Terraform Registry](https://registry.terraform.io/browse/providers). + +-> **Note:** Provider documentation used to be hosted directly on terraform.io, +as part of Terraform's core documentation. Although some provider documentation +might still be hosted here, the Terraform Registry is now the main home for all +public provider docs. + +## Resource Behavior + +For more information about how Terraform manages resources when applying a +configuration, see +[Resource Behavior](/docs/language/resources/behavior.html). + +## Meta-Arguments + +The Terraform language defines several meta-arguments, which can be used with +any resource type to change the behavior of resources. + +The following meta-arguments are documented on separate pages: + +- [`depends_on`, for specifying hidden dependencies](/docs/language/meta-arguments/depends_on.html) +- [`count`, for creating multiple resource instances according to a count](/docs/language/meta-arguments/count.html) +- [`for_each`, to create multiple instances according to a map, or set of strings](/docs/language/meta-arguments/for_each.html) +- [`provider`, for selecting a non-default provider configuration](/docs/language/meta-arguments/resource-provider.html) +- [`lifecycle`, for lifecycle customizations](/docs/language/meta-arguments/lifecycle.html) +- [`provisioner` and `connection`, for taking extra actions after resource creation](/docs/language/resources/provisioners/index.html) + +## Operation Timeouts + +Some resource types provide a special `timeouts` nested block argument that +allows you to customize how long certain operations are allowed to take +before being considered to have failed. +For example, [`aws_db_instance`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance) +allows configurable timeouts for `create`, `update` and `delete` operations. + +Timeouts are handled entirely by the resource type implementation in the +provider, but resource types offering these features follow the convention +of defining a child block called `timeouts` that has a nested argument +named after each operation that has a configurable timeout value. +Each of these arguments takes a string representation of a duration, such +as `"60m"` for 60 minutes, `"10s"` for ten seconds, or `"2h"` for two hours. + +```hcl +resource "aws_db_instance" "example" { + # ... + + timeouts { + create = "60m" + delete = "2h" + } +} +``` + +The set of configurable operations is chosen by each resource type. Most +resource types do not support the `timeouts` block at all. Consult the +documentation for each resource type to see which operations it offers +for configuration, if any. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/artifactory.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/artifactory.html.md similarity index 98% rename from vendor/github.com/hashicorp/terraform/website/docs/backends/types/artifactory.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/artifactory.html.md index ce30ae71..2e19dea8 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/artifactory.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/artifactory.html.md @@ -1,5 +1,5 @@ --- -layout: "backend-types" +layout: "language" page_title: "Backend Type: artifactory" sidebar_current: "docs-backends-types-standard-artifactory" description: |- diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/azurerm.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/azurerm.html.md similarity index 77% rename from vendor/github.com/hashicorp/terraform/website/docs/backends/types/azurerm.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/azurerm.html.md index 72da9c09..d5796a34 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/azurerm.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/azurerm.html.md @@ -1,5 +1,5 @@ --- -layout: "backend-types" +layout: "language" page_title: "Backend Type: azurerm" sidebar_current: "docs-backends-types-standard-azurerm" description: |- @@ -11,7 +11,7 @@ description: |- **Kind: Standard (with state locking)** -Stores the state as a Blob with the given Key within the Blob Container within [the Blob Storage Account](https://docs.microsoft.com/azure/storage/common/storage-introduction). This backend also supports state locking and consistency checking via native capabilities of Azure Blob Storage. +Stores the state as a Blob with the given Key within the Blob Container within [the Blob Storage Account](https://docs.microsoft.com/en-us/azure/storage/common/storage-introduction). This backend also supports state locking and consistency checking via native capabilities of Azure Blob Storage. ## Example Configuration @@ -28,6 +28,8 @@ terraform { } ``` +--- + When authenticating using Managed Service Identity (MSI): ```hcl @@ -43,6 +45,27 @@ terraform { } ``` +--- + +When authenticating using Azure AD Authentication: + +```hcl +terraform { + backend "azurerm" { + storage_account_name = "abcd1234" + container_name = "tfstate" + key = "prod.terraform.tfstate" + use_azuread_auth = true + subscription_id = "00000000-0000-0000-0000-000000000000" + tenant_id = "00000000-0000-0000-0000-000000000000" + } +} +``` + +-> **Note:** When using AzureAD for Authentication to Storage you also need to ensure the `Storage Blob Data Owner` role is assigned. + +--- + When authenticating using the Access Key associated with the Storage Account: ```hcl @@ -59,6 +82,8 @@ terraform { } ``` +--- + When authenticating using a SAS Token associated with the Storage Account: ```hcl @@ -75,11 +100,11 @@ terraform { } ``` --> **NOTE:** When using a Service Principal or an Access Key - we recommend using a [Partial Configuration](/docs/backends/config.html) for the credentials. +-> **NOTE:** When using a Service Principal or an Access Key - we recommend using a [Partial Configuration](/docs/language/settings/backends/configuration.html#partial-configuration) for the credentials. ## Data Source Configuration -When authenticating using a Service Principall (either with a Client Certificate or a Client Secret): +When authenticating using a Service Principal (either with a Client Certificate or a Client Secret): ```hcl data "terraform_remote_state" "foo" { @@ -92,6 +117,8 @@ data "terraform_remote_state" "foo" { } ``` +--- + When authenticating using Managed Service Identity (MSI): ```hcl @@ -102,12 +129,34 @@ data "terraform_remote_state" "foo" { container_name = "terraform-state" key = "prod.terraform.tfstate" use_msi = true - subscription_id = "00000000-0000-0000-0000-000000000000" - tenant_id = "00000000-0000-0000-0000-000000000000" + subscription_id = "00000000-0000-0000-0000-000000000000" + tenant_id = "00000000-0000-0000-0000-000000000000" } } ``` +--- + +When authenticating using AzureAD Authentication: + +```hcl +data "terraform_remote_state" "foo" { + backend = "azurerm" + config = { + storage_account_name = "terraform123abc" + container_name = "terraform-state" + key = "prod.terraform.tfstate" + use_azuread_auth = true + subscription_id = "00000000-0000-0000-0000-000000000000" + tenant_id = "00000000-0000-0000-0000-000000000000" + } +} +``` + +-> **Note:** When using AzureAD for Authentication to Storage you also need to ensure the `Storage Blob Data Owner` role is assigned. + +--- + When authenticating using the Access Key associated with the Storage Account: ```hcl @@ -125,6 +174,8 @@ data "terraform_remote_state" "foo" { } ``` +--- + When authenticating using a SAS Token associated with the Storage Account: ```hcl @@ -146,9 +197,9 @@ data "terraform_remote_state" "foo" { The following configuration options are supported: -* `storage_account_name` - (Required) The Name of [the Storage Account](https://www.terraform.io/docs/providers/azurerm/r/storage_account.html). +* `storage_account_name` - (Required) The Name of [the Storage Account](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_account). -* `container_name` - (Required) The Name of [the Storage Container](https://www.terraform.io/docs/providers/azurerm/r/storage_container.html) within the Storage Account. +* `container_name` - (Required) The Name of [the Storage Container](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_container) within the Storage Account. * `key` - (Required) The name of the Blob used to retrieve/store Terraform's State file inside the Storage Container. @@ -186,6 +237,14 @@ When authenticating using the Storage Account's Access Key - the following field --- +When authenticating using AzureAD Authentication - the following fields are also supported: + +* `use_azuread_auth` - (Optional) Should AzureAD Authentication be used to access the Blob Storage Account. This can also be sourced from the `ARM_USE_AZUREAD` environment variable. + +-> **Note:** When using AzureAD for Authentication to Storage you also need to ensure the `Storage Blob Data Owner` role is assigned. + +--- + When authenticating using a Service Principal with a Client Certificate - the following fields are also supported: * `resource_group_name` - (Required) The Name of the Resource Group in which the Storage Account exists. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/configuration.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/configuration.html.md new file mode 100644 index 00000000..23bd91b0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/configuration.html.md @@ -0,0 +1,168 @@ +--- +layout: "language" +page_title: "Backend Configuration - Configuration Language" +--- + +# Backend Configuration + + +Each Terraform configuration can specify a backend, which defines exactly where +and how operations are performed, where [state](/docs/language/state/index.html) +snapshots are stored, etc. Most non-trivial Terraform configurations configure +a remote backend so that multiple people can work with the same infrastructure. + +## Using a Backend Block + +Backends are configured with a nested `backend` block within the top-level +`terraform` block: + +```hcl +terraform { + backend "remote" { + organization = "example_corp" + + workspaces { + name = "my-app-prod" + } + } +} +``` + +There are some important limitations on backend configuration: + +- A configuration can only provide one backend block. +- A backend block cannot refer to named values (like input variables, locals, or data source attributes). + +### Backend Types + +The block label of the backend block (`"remote"`, in the example above) indicates which backend type to use. Terraform has a built-in selection of backends, and the configured backend must be available in the version of Terraform you are using. + +The arguments used in the block's body are specific to the chosen backend type; they configure where and how the backend will store the configuration's state, and in some cases configure other behavior. + +Some backends allow providing access credentials directly as part of the configuration for use in unusual situations, for pragmatic reasons. However, in normal use we _do not_ recommend including access credentials as part of the backend configuration. Instead, leave those arguments completely unset and provide credentials via the credentials files or environment variables that are conventional for the target system, as described in the documentation for each backend. + +See the list of backend types in the navigation sidebar for details about each supported backend type and its configuration arguments. + +### Default Backend + +If a configuration includes no backend block, Terraform defaults to using the `local` backend, which performs operations on the local system and stores state as a plain file in the current working directory. + +## Initialization + +Whenever a configuration's backend changes, you must run `terraform init` again +to validate and configure the backend before you can perform any plans, applies, +or state operations. + +When changing backends, Terraform will give you the option to migrate +your state to the new backend. This lets you adopt backends without losing +any existing state. + +To be extra careful, we always recommend manually backing up your state +as well. You can do this by simply copying your `terraform.tfstate` file +to another location. The initialization process should create a backup +as well, but it never hurts to be safe! + +## Partial Configuration + +You do not need to specify every required argument in the backend configuration. +Omitting certain arguments may be desirable if some arguments are provided +automatically by an automation script running Terraform. When some or all of +the arguments are omitted, we call this a _partial configuration_. + +With a partial configuration, the remaining configuration arguments must be +provided as part of +[the initialization process](/docs/cli/init/index.html). +There are several ways to supply the remaining arguments: + + * **File**: A configuration file may be specified via the `init` command line. + To specify a file, use the `-backend-config=PATH` option when running + `terraform init`. If the file contains secrets it may be kept in + a secure data store, such as + [Vault](https://www.vaultproject.io/), in which case it must be downloaded + to the local disk before running Terraform. + + * **Command-line key/value pairs**: Key/value pairs can be specified via the + `init` command line. Note that many shells retain command-line flags in a + history file, so this isn't recommended for secrets. To specify a single + key/value pair, use the `-backend-config="KEY=VALUE"` option when running + `terraform init`. + + * **Interactively**: Terraform will interactively ask you for the required + values, unless interactive input is disabled. Terraform will not prompt for + optional values. + +If backend settings are provided in multiple locations, the top-level +settings are merged such that any command-line options override the settings +in the main configuration and then the command-line options are processed +in order, with later options overriding values set by earlier options. + +The final, merged configuration is stored on disk in the `.terraform` +directory, which should be ignored from version control. This means that +sensitive information can be omitted from version control, but it will be +present in plain text on local disk when running Terraform. + +When using partial configuration, Terraform requires at a minimum that +an empty backend configuration is specified in one of the root Terraform +configuration files, to specify the backend type. For example: + +```hcl +terraform { + backend "consul" {} +} +``` + +A backend configuration file has the contents of the `backend` block as +top-level attributes, without the need to wrap it in another `terraform` +or `backend` block: + +```hcl +address = "demo.consul.io" +path = "example_app/terraform_state" +scheme = "https" +``` + +The same settings can alternatively be specified on the command line as +follows: + +``` +$ terraform init \ + -backend-config="address=demo.consul.io" \ + -backend-config="path=example_app/terraform_state" \ + -backend-config="scheme=https" +``` + +The Consul backend also requires a Consul access token. Per the recommendation +above of omitting credentials from the configuration and using other mechanisms, +the Consul token would be provided by setting either the `CONSUL_HTTP_TOKEN` +or `CONSUL_HTTP_AUTH` environment variables. See the documentation of your +chosen backend to learn how to provide credentials to it outside of its main +configuration. + +## Changing Configuration + +You can change your backend configuration at any time. You can change +both the configuration itself as well as the type of backend (for example +from "consul" to "s3"). + +Terraform will automatically detect any changes in your configuration +and request a [reinitialization](/docs/cli/init/index.html). As part of +the reinitialization process, Terraform will ask if you'd like to migrate +your existing state to the new configuration. This allows you to easily +switch from one backend to another. + +If you're using multiple [workspaces](/docs/language/state/workspaces.html), +Terraform can copy all workspaces to the destination. If Terraform detects +you have multiple workspaces, it will ask if this is what you want to do. + +If you're just reconfiguring the same backend, Terraform will still ask if you +want to migrate your state. You can respond "no" in this scenario. + +## Unconfiguring a Backend + +If you no longer want to use any backend, you can simply remove the +configuration from the file. Terraform will detect this like any other +change and prompt you to [reinitialize](/docs/cli/init/index.html). + +As part of the reinitialization, Terraform will ask if you'd like to migrate +your state back down to normal local state. Once this is complete then +Terraform is back to behaving as it does by default. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/consul.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/consul.html.md similarity index 92% rename from vendor/github.com/hashicorp/terraform/website/docs/backends/types/consul.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/consul.html.md index 740d9750..d2f92145 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/consul.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/consul.html.md @@ -1,5 +1,5 @@ --- -layout: "backend-types" +layout: "language" page_title: "Backend Type: consul" sidebar_current: "docs-backends-types-standard-consul" description: |- @@ -12,7 +12,7 @@ description: |- Stores the state in the [Consul](https://www.consul.io/) KV store at a given path. -This backend supports [state locking](/docs/state/locking.html). +This backend supports [state locking](/docs/language/state/locking.html). ## Example Configuration @@ -27,7 +27,7 @@ terraform { ``` Note that for the access credentials we recommend using a -[partial configuration](/docs/backends/config.html). +[partial configuration](/docs/language/settings/backends/configuration.html#partial-configuration). ## Data Source Configuration diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/cos.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/cos.html.md similarity index 85% rename from vendor/github.com/hashicorp/terraform/website/docs/backends/types/cos.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/cos.html.md index bf1d0994..cef18670 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/cos.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/cos.html.md @@ -1,5 +1,5 @@ --- -layout: "backend-types" +layout: "language" page_title: "Backend Type: cos" sidebar_current: "docs-backends-types-standard-cos" description: |- @@ -11,7 +11,7 @@ description: |- **Kind: Standard (with locking)** Stores the state as an object in a configurable prefix in a given bucket on [Tencent Cloud Object Storage](https://intl.cloud.tencent.com/product/cos) (COS). -This backend also supports [state locking](/docs/state/locking.html). +This backend also supports [state locking](/docs/language/state/locking.html). ~> **Warning!** It is highly recommended that you enable [Object Versioning](https://intl.cloud.tencent.com/document/product/436/19883) on the COS bucket to allow for state recovery in the case of accidental deletions and human error. @@ -28,12 +28,12 @@ terraform { } ``` -This assumes we have a [COS Bucket](https://www.terraform.io/docs/providers/tencentcloud/r/cos_bucket.html) created named `bucket-for-terraform-state-1258798060`, +This assumes we have a [COS Bucket](https://registry.terraform.io/providers/tencentcloudstack/tencentcloud/latest/docs/resources/cos_bucket) created named `bucket-for-terraform-state-1258798060`, Terraform state will be written into the file `terraform/state/terraform.tfstate`. ## Data Source Configuration -To make use of the COS remote state in another configuration, use the [`terraform_remote_state` data source](/docs/providers/terraform/d/remote_state.html). +To make use of the COS remote state in another configuration, use the [`terraform_remote_state` data source](/docs/language/state/remote-state-data.html). ```hcl data "terraform_remote_state" "foo" { diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/etcd.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/etcd.html.md similarity index 97% rename from vendor/github.com/hashicorp/terraform/website/docs/backends/types/etcd.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/etcd.html.md index 302d5486..792b7354 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/etcd.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/etcd.html.md @@ -1,5 +1,5 @@ --- -layout: "backend-types" +layout: "language" page_title: "Backend Type: etcd" sidebar_current: "docs-backends-types-standard-etcdv2" description: |- diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/etcdv3.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/etcdv3.html.md similarity index 89% rename from vendor/github.com/hashicorp/terraform/website/docs/backends/types/etcdv3.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/etcdv3.html.md index 43257c8c..7c31d512 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/etcdv3.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/etcdv3.html.md @@ -1,5 +1,5 @@ --- -layout: "backend-types" +layout: "language" page_title: "Backend Type: etcdv3" sidebar_current: "docs-backends-types-standard-etcdv3" description: |- @@ -12,7 +12,7 @@ description: |- Stores the state in the [etcd](https://coreos.com/etcd/) KV store with a given prefix. -This backend supports [state locking](/docs/state/locking.html). +This backend supports [state locking](/docs/language/state/locking.html). ## Example Configuration @@ -27,7 +27,7 @@ terraform { ``` Note that for the access credentials we recommend using a -[partial configuration](/docs/backends/config.html). +[partial configuration](/docs/language/settings/backends/configuration.html#partial-configuration). ## Data Source Configuration diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/gcs.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/gcs.html.md new file mode 100644 index 00000000..1d869579 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/gcs.html.md @@ -0,0 +1,107 @@ +--- +layout: "language" +page_title: "Backend Type: gcs" +sidebar_current: "docs-backends-types-standard-gcs" +description: |- + Terraform can store the state remotely, making it easier to version and work with in a team. +--- + +# gcs + +**Kind: Standard (with locking)** + +Stores the state as an object in a configurable prefix in a pre-existing bucket on [Google Cloud Storage](https://cloud.google.com/storage/) (GCS). +This backend also supports [state locking](/docs/language/state/locking.html). The bucket must exist prior to configuring the backend. + +~> **Warning!** It is highly recommended that you enable +[Object Versioning](https://cloud.google.com/storage/docs/object-versioning) +on the GCS bucket to allow for state recovery in the case of accidental deletions and human error. + +## Example Configuration + +```hcl +terraform { + backend "gcs" { + bucket = "tf-state-prod" + prefix = "terraform/state" + } +} +``` + +## Data Source Configuration + +```hcl +data "terraform_remote_state" "foo" { + backend = "gcs" + config = { + bucket = "terraform-state" + prefix = "prod" + } +} + +resource "template_file" "bar" { + template = "${greeting}" + + vars { + greeting = "${data.terraform_remote_state.foo.greeting}" + } +} +``` + +## Authentication + +IAM Changes to buckets are [eventually consistent](https://cloud.google.com/storage/docs/consistency#eventually_consistent_operations) and may take upto a few minutes to take effect. Terraform will return 403 errors till it is eventually consistent. + +### Running Terraform on your workstation. + +If you are using terraform on your workstation, you will need to install the Google Cloud SDK and authenticate using [User Application Default +Credentials](https://cloud.google.com/sdk/gcloud/reference/auth/application-default). + +User ADCs do [expire](https://developers.google.com/identity/protocols/oauth2#expiration) and you can refresh them by running `gcloud auth application-default login`. + +### Running Terraform on Google Cloud + +If you are running terraform on Google Cloud, you can configure that instance or cluster to use a [Google Service +Account](https://cloud.google.com/compute/docs/authentication). This will allow Terraform to authenticate to Google Cloud without having to bake in a separate +credential/authentication file. Make sure that the scope of the VM/Cluster is set to cloud-platform. + +### Running Terraform outside of Google Cloud + +If you are running terraform outside of Google Cloud, generate a service account key and set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to +the path of the service account key. Terraform will use that key for authentication. + +### Impersonating Service Accounts + +Terraform can impersonate a Google Service Account as described [here](https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials). A valid credential must be provided as mentioned in the earlier section and that identity must have the `roles/iam.serviceAccountTokenCreator` role on the service account you are impersonating. + +## Configuration variables + +The following configuration options are supported: + + * `bucket` - (Required) The name of the GCS bucket. This name must be + globally unique. For more information, see [Bucket Naming + Guidelines](https://cloud.google.com/storage/docs/bucketnaming.html#requirements). + * `credentials` / `GOOGLE_BACKEND_CREDENTIALS` / `GOOGLE_CREDENTIALS` - + (Optional) Local path to Google Cloud Platform account credentials in JSON + format. If unset, [Google Application Default + Credentials](https://developers.google.com/identity/protocols/application-default-credentials) + are used. The provided credentials must have Storage Object Admin role on the bucket. + **Warning**: if using the Google Cloud Platform provider as well, it will + also pick up the `GOOGLE_CREDENTIALS` environment variable. + * `impersonate_service_account` - (Optional) The service account to impersonate for accessing the State Bucket. + You must have `roles/iam.serviceAccountTokenCreator` role on that account for the impersonation to succeed. + If you are using a delegation chain, you can specify that using the `impersonate_service_account_delegates` field. + Alternatively, this can be specified using the `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT` environment + variable. + * `impersonate_service_account_delegates` - (Optional) The delegation chain for an impersonating a service account as described [here](https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials#sa-credentials-delegated). + * `access_token` - (Optional) A temporary [OAuth 2.0 access token] obtained + from the Google Authorization server, i.e. the `Authorization: Bearer` token + used to authenticate HTTP requests to GCP APIs. This is an alternative to + `credentials`. If both are specified, `access_token` will be used over the + `credentials` field. + * `prefix` - (Optional) GCS prefix inside the bucket. Named states for + workspaces are stored in an object called `/.tfstate`. + * `encryption_key` / `GOOGLE_ENCRYPTION_KEY` - (Optional) A 32 byte base64 + encoded 'customer supplied encryption key' used to encrypt all state. For + more information see [Customer Supplied Encryption + Keys](https://cloud.google.com/storage/docs/encryption#customer-supplied). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/http.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/http.html.md similarity index 99% rename from vendor/github.com/hashicorp/terraform/website/docs/backends/types/http.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/http.html.md index e0f3e748..e87903c4 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/http.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/http.html.md @@ -1,5 +1,5 @@ --- -layout: "backend-types" +layout: "language" page_title: "Backend Type: http" sidebar_current: "docs-backends-types-standard-http" description: |- diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/index.html.md new file mode 100644 index 00000000..6e477c5e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/index.html.md @@ -0,0 +1,110 @@ +--- +layout: "language" +page_title: "Backend Overview - Configuration Language" +--- + +# Backends + +Each Terraform configuration can specify a backend, which defines where +and how operations are performed, where [state](/docs/language/state/index.html) +snapshots are stored, etc. + +The rest of this page introduces the concept of backends; the other pages in +this section document how to configure and use backends. + +- [Backend Configuration](/docs/language/settings/backends/configuration.html) documents the form + of a `backend` block, which selects and configures a backend for a + Terraform configuration. +- This section also includes a page for each of Terraform's built-in backends, + documenting its behavior and available settings. See the navigation sidebar + for a complete list. + +## Recommended Backends + +- If you are still learning how to use Terraform, we recommend using the default + `local` backend, which requires no configuration. +- If you and your team are using Terraform to manage meaningful infrastructure, + we recommend using the `remote` backend with [Terraform Cloud](/docs/cloud/index.html) + or [Terraform Enterprise](/docs/enterprise/index.html). + +## Where Backends are Used + +Backend configuration is only used by [Terraform CLI](/docs/cli/index.html). +Terraform Cloud and Terraform Enterprise always use their own state storage when +performing Terraform runs, so they ignore any backend block in the +configuration. + +But since it's common to +[use Terraform CLI alongside Terraform Cloud](/docs/cloud/run/cli.html) +(and since certain state operations, like [tainting](/docs/cli/commands/taint.html), +can only be performed on the CLI), we recommend that Terraform Cloud users +include a backend block in their configurations and configure the `remote` +backend to use the relevant Terraform Cloud workspace(s). + +## Where Backends Come From + +Terraform includes a built-in selection of backends; this selection has changed +over time, but does not change very often. + +The built-in backends are the only backends. You cannot load additional backends +as plugins. + +## What Backends Do + +There are two areas of Terraform's behavior that are determined by the backend: + +- Where state is stored. +- Where operations are performed. + +### State + +Terraform uses persistent [state](/docs/language/state/index.html) data to keep track of +the resources it manages. Since it needs the state in order to know which +real-world infrastructure objects correspond to the resources in a +configuration, everyone working with a given collection of infrastructure +resources must be able to access the same state data. + +The `local` backend stores state as a local file on disk, but every other +backend stores state in a remote service of some kind, which allows multiple +people to access it. Accessing state in a remote service generally requires some +kind of access credentials, since state data contains extremely sensitive +information. + +Some backends act like plain "remote disks" for state files; others support +_locking_ the state while operations are being performed, which helps prevent +conflicts and inconsistencies. + +### Operations + +"Operations" refers to performing API requests against infrastructure services +in order to create, read, update, or destroy resources. Not every `terraform` +subcommand performs API operations; many of them only operate on state data. + +Only two backends actually perform operations: `local` and `remote`. + +The `local` backend performs API operations directly from the machine where the +`terraform` command is run. Whenever you use a backend other than `local` or +`remote`, Terraform uses the `local` backend for operations; it only uses the +configured backend for state storage. + +The `remote` backend can perform API operations remotely, using Terraform Cloud +or Terraform Enterprise. When running remote operations, the local `terraform` +command displays the output of the remote actions as though they were being +performed locally, but only the remote system requires cloud credentials or +network access to the resources being managed. + +Remote operations are optional for the `remote` backend; the settings for the +target Terraform Cloud workspace determine whether operations run remotely or +locally. If local operations are configured, Terraform uses the `remote` backend +for state and the `local` backend for operations, like with the other state +backends. + +### Backend Types + +Terraform's backends are divided into two main types, according to how they +handle state and operations: + +- **Enhanced** backends can both store state and perform operations. There are + only two enhanced backends: `local` and `remote`. +- **Standard** backends only store state, and rely on the `local` backend for + performing operations. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/kubernetes.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/kubernetes.html.md similarity index 85% rename from vendor/github.com/hashicorp/terraform/website/docs/backends/types/kubernetes.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/kubernetes.html.md index 80ef0876..71d80e24 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/kubernetes.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/kubernetes.html.md @@ -1,5 +1,5 @@ --- -layout: "backend-types" +layout: "language" page_title: "Backend Type: Kubernetes" sidebar_current: "docs-backends-types-standard-kubernetes" description: |- @@ -20,20 +20,20 @@ Stores the state in a [Kubernetes secret](https://kubernetes.io/docs/concepts/co terraform { backend "kubernetes" { secret_suffix = "state" - load_config_file = true + config_path = "~/.kube/config" } } ``` This assumes the user/service account running terraform has [permissions](https://kubernetes.io/docs/reference/access-authn-authz/authorization/) to read/write secrets in the [namespace](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/) used to store the secret. -If the `load_config_file` flag is set the backend will attempt to use a [kubeconfig file](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/) to gain access to the cluster. +If the `config_path` or `config_paths` attribute is set the backend will attempt to use a [kubeconfig file](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/) to gain access to the cluster. If the `in_cluster_config` flag is set the backend will attempt to use a [service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) to access the cluster. This can be used if Terraform is being run from within a pod running in the Kubernetes cluster. -For most use cases either `in_cluster_config` or `load_config_file` will need to be set to `true`. If both flags are set the configuration from `load_config_file` will be used. +For most use cases either `in_cluster_config`, `config_path`, or `config_paths` will need to be set. If all flags are set the configuration at `config_path` will be used. -Note that for the access credentials we recommend using a [partial configuration](/docs/backends/config.html#partial-configuration). +Note that for the access credentials we recommend using a [partial configuration](/docs/language/settings/backends/configuration.html#partial-configuration). ## Example Referencing @@ -56,7 +56,6 @@ The following configuration options are supported: * `labels` - (Optional) Map of additional labels to be applied to the secret and lease. * `namespace` - (Optional) Namespace to store the secret and lease in. Can be sourced from `KUBE_NAMESPACE`. * `in_cluster_config` - (Optional) Used to authenticate to the cluster from inside a pod. Can be sourced from `KUBE_IN_CLUSTER_CONFIG`. -* `load_config_file` - (Optional) Use a kubeconfig file to access the cluster. Can be sourced from `KUBE_LOAD_CONFIG_FILE`. * `host` - (Optional) The hostname (in form of URI) of Kubernetes master. Can be sourced from `KUBE_HOST`. Defaults to `https://localhost`. * `username` - (Optional) The username to use for HTTP basic authentication when accessing the Kubernetes master endpoint. Can be sourced from `KUBE_USER`. * `password` - (Optional) The password to use for HTTP basic authentication when accessing the Kubernetes master endpoint. Can be sourced from `KUBE_PASSWORD`. @@ -64,7 +63,8 @@ The following configuration options are supported: * `client_certificate` - (Optional) PEM-encoded client certificate for TLS authentication. Can be sourced from `KUBE_CLIENT_CERT_DATA`. * `client_key` - (Optional) PEM-encoded client certificate key for TLS authentication. Can be sourced from `KUBE_CLIENT_KEY_DATA`. * `cluster_ca_certificate` - (Optional) PEM-encoded root certificates bundle for TLS authentication. Can be sourced from `KUBE_CLUSTER_CA_CERT_DATA`. -* `config_path` - (Optional) Path to the kube config file. Can be sourced from `KUBE_CONFIG` or `KUBECONFIG`. Defaults to `~/.kube/config`. +* `config_path` - (Optional) Path to the kube config file. Can be sourced from `KUBE_CONFIG_PATH`. +* `config_paths` - (Optional) List of paths to kube config files. Can be sourced from `KUBE_CONFIG_PATHS`. * `config_context` - (Optional) Context to choose from the config file. Can be sourced from `KUBE_CTX`. * `config_context_auth_info` - (Optional) Authentication info context of the kube config (name of the kubeconfig user, `--user` flag in `kubectl`). Can be sourced from `KUBE_CTX_AUTH_INFO`. * `config_context_cluster` - (Optional) Cluster context of the kube config (name of the kubeconfig cluster, `--cluster` flag in `kubectl`). Can be sourced from `KUBE_CTX_CLUSTER`. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/local.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/local.html.md new file mode 100644 index 00000000..f2829d3a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/local.html.md @@ -0,0 +1,96 @@ +--- +layout: "language" +page_title: "Backend Type: local" +sidebar_current: "docs-backends-types-enhanced-local" +description: |- + Terraform can store the state remotely, making it easier to version and work with in a team. +--- + +# local + +**Kind: Enhanced** + +The local backend stores state on the local filesystem, locks that +state using system APIs, and performs operations locally. + +## Example Configuration + +```hcl +terraform { + backend "local" { + path = "relative/path/to/terraform.tfstate" + } +} +``` + +## Data Source Configuration + +```hcl +data "terraform_remote_state" "foo" { + backend = "local" + + config = { + path = "${path.module}/../../terraform.tfstate" + } +} +``` + +## Configuration variables + +The following configuration options are supported: + + * `path` - (Optional) The path to the `tfstate` file. This defaults to + "terraform.tfstate" relative to the root module by default. + * `workspace_dir` - (Optional) The path to non-default workspaces. + +## Command Line Arguments + +~> This section describes legacy features that we've preserved for backward +compatibility but that we no longer recommend. See below for more details. + +For configurations that include a `backend "local"` block or that default to +the local backend by not specifying a backend at all, most commands that either +read or write state snapshots from the backend accept the following +additional arguments: + +* `-state=FILENAME` - overrides the state filename when _reading_ the prior + state snapshot. +* `-state-out=FILENAME` - overrides the state filename when _writing_ new state + snapshots. + + If you use `-state` without also using `-state-out` then Terraform will + use the `-state` filename for both `-state` and `-state-out`, which means + Terraform will overwrite the input file if it creates a new state snapshot. +* `-backup=FILENAME` - overrides the default filename that the local backend + would normally choose dynamically to create backup files when it writes new + state. + + If you use `-state` without also using `-backup` then Terraform will use + the `-state` filename as a filename prefix for generating a backup filename. + You can use `-backup=-` (that is, set the filename to just the ASCII + dash character) to disable the creation of backup files altogether. + +These three options are preserved for backward-compatibility with earlier +workflows that predated the introduction of built-in remote state, where +users would write wrapper scripts that fetch prior state before running +Terraform and then save the new state after Terraform exits, in which case +the three arguments would typically all be paths within a temporary +directory used just for one operation. + +Because these old workflows predate the introduction of the possibility of +[multiple workspaces](/docs/language/state/workspaces.html), setting them +overrides Terraform's usual behavior of selecting a different state filename +based on the selected workspace. If you use all three of these options then +the selected workspace has no effect on which filenames Terraform will select +for state files, and so you'll need to select different filenames yourself if +you wish to keep workspace state files distinct from one another. + +These three options have no effect for configurations that have a different +backend type selected. + +We do not recommend using these options in new systems, even if you are running +Terraform in automation. Instead, +[select a different backend which supports remote state](./) and configure it +within your root module, which ensures that everyone working on your +configuration will automatically retrieve and store state in the correct shared +location without any special command line options. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/manta.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/manta.html.md similarity index 95% rename from vendor/github.com/hashicorp/terraform/website/docs/backends/types/manta.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/manta.html.md index 92689161..ef82e560 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/manta.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/manta.html.md @@ -1,5 +1,5 @@ --- -layout: "backend-types" +layout: "language" page_title: "Backend Type: manta" sidebar_current: "docs-backends-types-standard-manta" description: |- @@ -24,7 +24,7 @@ terraform { ``` Note that for the access credentials we recommend using a -[partial configuration](/docs/backends/config.html). +[partial configuration](/docs/language/settings/backends/configuration.html#partial-configuration). ## Data Source Configuration diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/oss.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/oss.html.md similarity index 91% rename from vendor/github.com/hashicorp/terraform/website/docs/backends/types/oss.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/oss.html.md index c15ebd06..6acc16af 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/oss.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/oss.html.md @@ -1,5 +1,5 @@ --- -layout: "backend-types" +layout: "language" page_title: "Backend Type: oss" sidebar_current: "docs-backends-types-standard-oss" description: |- @@ -33,9 +33,9 @@ terraform { } ``` -This assumes we have a [OSS Bucket](https://www.terraform.io/docs/providers/alicloud/r/oss_bucket.html) created called `bucket-for-terraform-state`, -a [OTS Instance](https://www.terraform.io/docs/providers/alicloud/r/ots_instance.html) called `terraform-remote` and -a [OTS TableStore](https://www.terraform.io/docs/providers/alicloud/r/ots_table.html) called `statelock`. The +This assumes we have a [OSS Bucket](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/oss_bucket) created called `bucket-for-terraform-state`, +a [OTS Instance](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/ots_instance) called `terraform-remote` and +a [OTS TableStore](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/ots_table) called `statelock`. The Terraform state will be written into the file `path/mystate/version-1.tfstate`. The `TableStore` must have a primary key named `LockID` of type `String`. @@ -43,7 +43,7 @@ Terraform state will be written into the file `path/mystate/version-1.tfstate`. To make use of the OSS remote state in another configuration, use the [`terraform_remote_state` data -source](/docs/providers/terraform/d/remote_state.html). +source](/docs/language/state/remote-state-data.html). ```hcl terraform { diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/pg.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/pg.html.md similarity index 76% rename from vendor/github.com/hashicorp/terraform/website/docs/backends/types/pg.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/pg.html.md index 7dd60e58..362443cf 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/pg.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/pg.html.md @@ -1,5 +1,5 @@ --- -layout: "backend-types" +layout: "language" page_title: "Backend Type: pg" sidebar_current: "docs-backends-types-standard-pg" description: |- @@ -12,7 +12,7 @@ description: |- Stores the state in a [Postgres database](https://www.postgresql.org) version 9.5 or newer. -This backend supports [state locking](/docs/state/locking.html). +This backend supports [state locking](/docs/language/state/locking.html). ## Example Configuration @@ -32,7 +32,9 @@ createdb terraform_backend This `createdb` command is found in [Postgres client applications](https://www.postgresql.org/docs/9.5/reference-client.html) which are installed along with the database server. -We recommend using a [partial configuration](/docs/backends/config.html#partial-configuration) for the `conn_str` variable, because it typically contains access credentials that should not be committed to source control: +We recommend using a +[partial configuration](/docs/language/settings/backends/configuration.html#partial-configuration) +for the `conn_str` variable, because it typically contains access credentials that should not be committed to source control: ```hcl terraform { @@ -54,7 +56,7 @@ terraform init -backend-config="conn_str=postgres://localhost/terraform_backend? ## Data Source Configuration -To make use of the pg remote state in another configuration, use the [`terraform_remote_state` data source](/docs/providers/terraform/d/remote_state.html). +To make use of the pg remote state in another configuration, use the [`terraform_remote_state` data source](/docs/language/state/remote-state-data.html). ```hcl data "terraform_remote_state" "network" { @@ -81,9 +83,9 @@ Postgres version 9.5 or newer is required to support advisory locks and the "ON This backend creates one table **states** in the automatically-managed Postgres schema configured by the `schema_name` variable. -The table is keyed by the [workspace](/docs/state/workspaces.html) name. If workspaces are not in use, the name `default` is used. +The table is keyed by the [workspace](/docs/language/state/workspaces.html) name. If workspaces are not in use, the name `default` is used. -Locking is supported using [Postgres advisory locks](https://www.postgresql.org/docs/9.5/explicit-locking.html#ADVISORY-LOCKS). [`force-unlock`](https://www.terraform.io/docs/commands/force-unlock.html) is not supported, because these database-native locks will automatically unlock when the session is aborted or the connection fails. To see outstanding locks in a Postgres server, use the [`pg_locks` system view](https://www.postgresql.org/docs/9.5/view-pg-locks.html). +Locking is supported using [Postgres advisory locks](https://www.postgresql.org/docs/9.5/explicit-locking.html#ADVISORY-LOCKS). [`force-unlock`](https://www.terraform.io/docs/cli/commands/force-unlock.html) is not supported, because these database-native locks will automatically unlock when the session is aborted or the connection fails. To see outstanding locks in a Postgres server, use the [`pg_locks` system view](https://www.postgresql.org/docs/9.5/view-pg-locks.html). The **states** table contains: diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/remote.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/remote.html.md new file mode 100644 index 00000000..83350eae --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/remote.html.md @@ -0,0 +1,218 @@ +--- +layout: "language" +page_title: "Backend Type: remote" +sidebar_current: "docs-backends-types-enhanced-remote" +description: |- + Terraform can store the state and run operations remotely, making it easier to version and work with in a team. +--- + +# remote + +**Kind: Enhanced** + +-> **Note:** We recommend using Terraform v0.11.13 or newer with this +backend. This backend requires either a Terraform Cloud account on +[app.terraform.io](https://app.terraform.io) or a Terraform Enterprise instance +(version v201809-1 or newer). + +The remote backend stores Terraform state and may be used to run operations in Terraform Cloud. + +When using full remote operations, operations like `terraform plan` or `terraform apply` can be executed in Terraform +Cloud's run environment, with log output streaming to the local terminal. Remote plans and applies use variable values from the associated Terraform Cloud workspace. + +Terraform Cloud can also be used with local operations, in which case only state is stored in the Terraform Cloud backend. + +## Command Support + +Currently the remote backend supports the following Terraform commands: + +- `apply` +- `console` (supported in Terraform >= v0.11.12) +- `destroy` +- `fmt` +- `get` +- `graph` (supported in Terraform >= v0.11.12) +- `import` (supported in Terraform >= v0.11.12) +- `init` +- `output` +- `plan` +- `providers` +- `show` +- `state` (supports all sub-commands: list, mv, pull, push, rm, show) +- `taint` +- `untaint` +- `validate` +- `version` +- `workspace` + +## Workspaces + +The remote backend can work with either a single remote Terraform Cloud workspace, +or with multiple similarly-named remote workspaces (like `networking-dev` +and `networking-prod`). The `workspaces` block of the backend configuration +determines which mode it uses: + +- To use a single remote Terraform Cloud workspace, set `workspaces.name` to the + remote workspace's full name (like `networking`). + +- To use multiple remote workspaces, set `workspaces.prefix` to a prefix used in + all of the desired remote workspace names. For example, set + `prefix = "networking-"` to use Terraform cloud workspaces with + names like `networking-dev` and `networking-prod`. This is helpful when + mapping multiple Terraform CLI [workspaces](/docs/language/state/workspaces.html) + used in a single Terraform configuration to multiple Terraform Cloud + workspaces. + +When interacting with workspaces on the command line, Terraform uses +shortened names without the common prefix. For example, if +`prefix = "networking-"`, use `terraform workspace select prod` to switch to +the Terraform CLI workspace `prod` within the current configuration. Remote +Terraform operations such as `plan` and `apply` executed against that Terraform +CLI workspace will be executed in the Terraform Cloud workspace `networking-prod`. + +Additionally, the [`${terraform.workspace}`](/docs/language/state/workspaces.html#current-workspace-interpolation) +interpolation sequence should be removed from Terraform configurations that run +remote operations against Terraform Cloud workspaces. The reason for this is that +each Terraform Cloud workspace currently only uses the single `default` Terraform +CLI workspace internally. In other words, if your Terraform configuration +used `${terraform.workspace}` to return `dev` or `prod`, remote runs in Terraform Cloud +would always evaluate it as `default` regardless of +which workspace you had set with the `terraform workspace select` command. That +would most likely not be what you wanted. (It is ok to use `${terraform.workspace}` +in local operations.) + +The backend configuration requires either `name` or `prefix`. Omitting both or +setting both results in a configuration error. + +If previous state is present when you run `terraform init` and the corresponding +remote workspaces are empty or absent, Terraform will create workspaces and/or +update the remote state accordingly. However, if your workspace needs variables +set or requires a specific version of Terraform for remote operations, we +recommend that you create your remote workspaces on Terraform Cloud before +running any remote operations against them. + +## Example Configurations + +-> **Note:** We recommend omitting the token from the configuration, and instead using + [`terraform login`](/docs/cli/commands/login.html) or manually configuring + `credentials` in the [CLI config file](/docs/cli/config/config-file.html#credentials). + +### Basic Configuration + +```hcl +# Using a single workspace: +terraform { + backend "remote" { + hostname = "app.terraform.io" + organization = "company" + + workspaces { + name = "my-app-prod" + } + } +} + +# Using multiple workspaces: +terraform { + backend "remote" { + hostname = "app.terraform.io" + organization = "company" + + workspaces { + prefix = "my-app-" + } + } +} +``` + +### Using CLI Input + +```hcl +# main.tf +terraform { + required_version = "~> 0.12.0" + + backend "remote" {} +} +``` + +Backend configuration file: + +```hcl +# backend.hcl +workspaces { name = "workspace" } +hostname = "app.terraform.io" +organization = "company" +``` + +Running `terraform init` with the backend file: + +```sh +terraform init -backend-config=backend.hcl +``` + +### Data Source Configuration + +```hcl +data "terraform_remote_state" "foo" { + backend = "remote" + + config = { + organization = "company" + + workspaces = { + name = "workspace" + } + } +} +``` + +## Configuration variables + +The following configuration options are supported: + +* `hostname` - (Optional) The remote backend hostname to connect to. Defaults + to app.terraform.io. +* `organization` - (Required) The name of the organization containing the + targeted workspace(s). +* `token` - (Optional) The token used to authenticate with the remote backend. + We recommend omitting the token from the configuration, and instead using + [`terraform login`](/docs/cli/commands/login.html) or manually configuring + `credentials` in the + [CLI config file](/docs/cli/config/config-file.html#credentials). +* `workspaces` - (Required) A block specifying which remote workspace(s) to use. + The `workspaces` block supports the following keys: + + * `name` - (Optional) The full name of one remote workspace. When configured, + only the default workspace can be used. This option conflicts with `prefix`. + * `prefix` - (Optional) A prefix used in the names of one or more remote + workspaces, all of which can be used with this configuration. The full + workspace names are used in Terraform Cloud, and the short names + (minus the prefix) are used on the command line for Terraform CLI workspaces. + If omitted, only the default workspace can be used. This option conflicts with `name`. + +-> **Note:** You must use the `name` key when configuring a `terraform_remote_state` +data source that retrieves state from another Terraform Cloud workspace. The `prefix` key is only +intended for use when configuring an instance of the remote backend. + +## Excluding Files from Upload with .terraformignore + +-> **Version note:** `.terraformignore` support was added in Terraform 0.12.11. + +When executing a remote `plan` or `apply` in a [CLI-driven run](/docs/cloud/run/cli.html), +an archive of your configuration directory is uploaded to Terraform Cloud. You can define +paths to ignore from upload via a `.terraformignore` file at the root of your configuration directory. If this file is not present, the archive will exclude the following by default: + +* .git/ directories +* .terraform/ directories (exclusive of .terraform/modules) + +The `.terraformignore` file can include rules as one would include in a +[.gitignore file](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository#Ignoring-Files) + + +* Comments (starting with `#`) or blank lines are ignored +* End a pattern with a forward slash / to specify a directory +* Negate a pattern by starting it with an exclamation point `!` + +Note that unlike `.gitignore`, only the `.terraformignore` at the root of the configuration +directory is considered. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/s3.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/s3.html.md similarity index 97% rename from vendor/github.com/hashicorp/terraform/website/docs/backends/types/s3.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/s3.html.md index 31b3a7ac..678cb24b 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/s3.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/s3.html.md @@ -1,5 +1,5 @@ --- -layout: "backend-types" +layout: "language" page_title: "Backend Type: s3" sidebar_current: "docs-backends-types-standard-s3" description: |- @@ -37,7 +37,7 @@ This assumes we have a bucket created called `mybucket`. The Terraform state is written to the key `path/to/my/key`. Note that for the access credentials we recommend using a -[partial configuration](/docs/backends/config.html). +[partial configuration](/docs/language/settings/backends/configuration.html#partial-configuration). ### S3 Bucket Permissions @@ -107,7 +107,7 @@ This is seen in the following AWS IAM Statement: To make use of the S3 remote state in another configuration, use the [`terraform_remote_state` data -source](/docs/providers/terraform/d/remote_state.html). +source](/docs/language/state/remote-state-data.html). ```hcl data "terraform_remote_state" "network" { @@ -182,7 +182,7 @@ The following configuration is optional: The following configuration is required: * `bucket` - (Required) Name of the S3 Bucket. -* `key` - (Required) Path to the state file inside the S3 Bucket. When using a non-default [workspace](/docs/state/workspaces.html), the state path will be `/workspace_key_prefix/workspace_name/key` (see also the `workspace_key_prefix` configuration). +* `key` - (Required) Path to the state file inside the S3 Bucket. When using a non-default [workspace](/docs/language/state/workspaces.html), the state path will be `/workspace_key_prefix/workspace_name/key` (see also the `workspace_key_prefix` configuration). The following configuration is optional: @@ -214,7 +214,7 @@ The S3 backend can be used in a number of different ways that make different tradeoffs between convenience, security, and isolation in such an organization. This section describes one such approach that aims to find a good compromise between these tradeoffs, allowing use of -[Terraform's workspaces feature](/docs/state/workspaces.html) to switch +[Terraform's workspaces feature](/docs/language/state/workspaces.html) to switch conveniently between multiple isolated deployments of the same configuration. Use this section as a starting-point for your approach, but note that @@ -320,7 +320,7 @@ provider "aws" { If workspace IAM roles are centrally managed and shared across many separate Terraform configurations, the role ARNs could also be obtained via a data -source such as [`terraform_remote_state`](/docs/providers/terraform/d/remote_state.html) +source such as [`terraform_remote_state`](/docs/language/state/remote-state-data.html) to avoid repeating these values. ### Creating and Selecting Workspaces diff --git a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/swift.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/swift.html.md similarity index 96% rename from vendor/github.com/hashicorp/terraform/website/docs/backends/types/swift.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/swift.html.md index 57ab752e..1d8241ae 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/backends/types/swift.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/backends/swift.html.md @@ -1,5 +1,5 @@ --- -layout: "backend-types" +layout: "language" page_title: "Backend Type: swift" sidebar_current: "docs-backends-types-standard-swift" description: |- @@ -12,7 +12,7 @@ description: |- Stores the state as an artifact in [Swift](http://docs.openstack.org/developer/swift/latest/). -~> Warning! It is highly recommended that you enable [Object Versioning](https://docs.openstack.org/developer/swift/latest/overview_object_versioning.html) by setting the [`archive_container`](https://www.terraform.io/docs/backends/types/swift.html#archive_container) configuration. This allows for state recovery in the case of accidental deletions and human error. +~> Warning! It is highly recommended that you enable [Object Versioning](https://docs.openstack.org/developer/swift/latest/overview_object_versioning.html) by setting the [`archive_container`](https://www.terraform.io/docs/language/settings/backends/swift.html#archive_container) configuration. This allows for state recovery in the case of accidental deletions and human error. ## Example Configuration @@ -27,7 +27,7 @@ terraform { This will create a container called `terraform-state` and an object within that container called `tfstate.tf`. It will enable versioning using the `terraform-state-archive` container to contain the older version. For the access credentials we recommend using a -[partial configuration](/docs/backends/config.html). +[partial configuration](/docs/language/settings/backends/configuration.html#partial-configuration). ## Data Source Configuration diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/settings/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/index.html.md new file mode 100644 index 00000000..51c051a3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/settings/index.html.md @@ -0,0 +1,128 @@ +--- +layout: "language" +page_title: "Terraform Settings - Configuration Language" +sidebar_current: "docs-config-terraform" +description: |- + The "terraform" configuration section is used to configure some behaviors + of Terraform itself. +--- + +# Terraform Settings + +The special `terraform` configuration block type is used to configure some +behaviors of Terraform itself, such as requiring a minimum Terraform version to +apply your configuration. + +## Terraform Block Syntax + +Terraform settings are gathered together into `terraform` blocks: + +```hcl +terraform { + # ... +} +``` + +Each `terraform` block can contain a number of settings related to Terraform's +behavior. Within a `terraform` block, only constant values can be used; +arguments may not refer to named objects such as resources, input variables, +etc, and may not use any of the Terraform language built-in functions. + +The various options supported within a `terraform` block are described in the +following sections. + +## Configuring a Terraform Backend + +The nested `backend` block configures which backend Terraform should use. + +The syntax and behavior of the `backend` block is described in [Backend +Configuration](/docs/language/settings/backends/configuration.html). + +## Specifying a Required Terraform Version + +The `required_version` setting accepts a [version constraint +string,](/docs/language/expressions/version-constraints.html) which specifies which versions of Terraform +can be used with your configuration. + +If the running version of Terraform doesn't match the constraints specified, +Terraform will produce an error and exit without taking any further actions. + +When you use [child modules](/docs/language/modules/index.html), each module can specify its own +version requirements. The requirements of all modules in the tree must be +satisfied. + +Use Terraform version constraints in a collaborative environment to +ensure that everyone is using a specific Terraform version, or using at least +a minimum Terraform version that has behavior expected by the configuration. + +The `required_version` setting applies only to the version of Terraform CLI. +Terraform's resource types are implemented by provider plugins, +whose release cycles are independent of Terraform CLI and of each other. +Use [the `required_providers` block](/docs/language/providers/requirements.html) to manage +the expected versions for each provider you use. + +## Specifying Provider Requirements + +[inpage-source]: #specifying-provider-requirements + +The `required_providers` block specifies all of the providers required by the +current module, mapping each local provider name to a source address and a +version constraint. + +```hcl +terraform { + required_providers { + aws = { + version = ">= 2.7.0" + source = "hashicorp/aws" + } + } +} +``` + +For more information, see [Provider Requirements](/docs/language/providers/requirements.html). + +## Experimental Language Features + +The Terraform team will sometimes introduce new language features initially via +an opt-in experiment, so that the community can try the new feature and give +feedback on it prior to it becoming a backward-compatibility constraint. + +In releases where experimental features are available, you can enable them on +a per-module basis by setting the `experiments` argument inside a `terraform` +block: + +```hcl +terraform { + experiments = [example] +} +``` + +The above would opt in to an experiment named `example`, assuming such an +experiment were available in the current Terraform version. + +Experiments are subject to arbitrary changes in later releases and, depending on +the outcome of the experiment, may change drastically before final release or +may not be released in stable form at all. Such breaking changes may appear +even in minor and patch releases. We do not recommend using experimental +features in Terraform modules intended for production use. + +In order to make that explicit and to avoid module callers inadvertently +depending on an experimental feature, any module with experiments enabled will +generate a warning on every `terraform plan` or `terraform apply`. If you +want to try experimental features in a shared module, we recommend enabling the +experiment only in alpha or beta releases of the module. + +The introduction and completion of experiments is reported in +[Terraform's changelog](https://github.com/hashicorp/terraform/blob/main/CHANGELOG.md), +so you can watch the release notes there to discover which experiment keywords, +if any, are available in a particular Terraform release. + +## Passing Metadata to Providers + +The `terraform` block can have a nested `provider_meta` block for each +provider a module is using, if the provider defines a schema for it. This +allows the provider to receive module-specific information, and is primarily +intended for modules distributed by the same vendor as the associated provider. + +For more information, see [Provider Metadata](/docs/internals/provider-meta.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/state/backends.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/state/backends.html.md new file mode 100644 index 00000000..da957173 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/state/backends.html.md @@ -0,0 +1,75 @@ +--- +layout: "language" +page_title: "Backends: State Storage and Locking" +sidebar_current: "docs-backends-state" +description: |- + Backends are configured directly in Terraform files in the `terraform` section. +--- + +# State Storage and Locking + +Backends are responsible for storing state and providing an API for +[state locking](/docs/language/state/locking.html). State locking is optional. + +Despite the state being stored remotely, all Terraform commands such +as `terraform console`, the `terraform state` operations, `terraform taint`, +and more will continue to work as if the state was local. + +## State Storage + +Backends determine where state is stored. For example, the local (default) +backend stores state in a local JSON file on disk. The Consul backend stores +the state within Consul. Both of these backends happen to provide locking: +local via system APIs and Consul via locking APIs. + +When using a non-local backend, Terraform will not persist the state anywhere +on disk except in the case of a non-recoverable error where writing the state +to the backend failed. This behavior is a major benefit for backends: if +sensitive values are in your state, using a remote backend allows you to use +Terraform without that state ever being persisted to disk. + +In the case of an error persisting the state to the backend, Terraform will +write the state locally. This is to prevent data loss. If this happens the +end user must manually push the state to the remote backend once the error +is resolved. + +## Manual State Pull/Push + +You can still manually retrieve the state from the remote state using +the `terraform state pull` command. This will load your remote state and +output it to stdout. You can choose to save that to a file or perform any +other operations. + +You can also manually write state with `terraform state push`. **This +is extremely dangerous and should be avoided if possible.** This will +overwrite the remote state. This can be used to do manual fixups if necessary. + +When manually pushing state, Terraform will attempt to protect you from +some potentially dangerous situations: + + * **Differing lineage**: The "lineage" is a unique ID assigned to a state + when it is created. If a lineage is different, then it means the states + were created at different times and its very likely you're modifying a + different state. Terraform will not allow this. + + * **Higher serial**: Every state has a monotonically increasing "serial" + number. If the destination state has a higher serial, Terraform will + not allow you to write it since it means that changes have occurred since + the state you're attempting to write. + +Both of these protections can be bypassed with the `-force` flag if you're +confident you're making the right decision. Even if using the `-force` flag, +we recommend making a backup of the state with `terraform state pull` +prior to forcing the overwrite. + +## State Locking + +Backends are responsible for supporting [state locking](/docs/language/state/locking.html) +if possible. + +Not all backends support locking. The +[documentation for each backend](/docs/language/settings/backends/index.html) +includes details on whether it supports locking or not. + +For more information on state locking, view the +[page dedicated to state locking](/docs/language/state/locking.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/state/import.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/state/import.html.md new file mode 100644 index 00000000..1334269e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/state/import.html.md @@ -0,0 +1,15 @@ +--- +layout: "language" +page_title: "State: Import Existing Resources" +sidebar_current: "docs-state-import" +description: |- + Terraform stores state which caches the known state of the world the last time Terraform ran. +--- + +# Import Existing Resources + +Terraform is able to import existing infrastructure. This allows you take +resources you've created by some other means and bring it under Terraform management. + +To learn more about this, please visit the +[pages dedicated to import](/docs/cli/import/index.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/state/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/state/index.html.md new file mode 100644 index 00000000..071cee81 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/state/index.html.md @@ -0,0 +1,83 @@ +--- +layout: "language" +page_title: "State" +sidebar_current: "docs-state" +description: |- + Terraform must store state about your managed infrastructure and configuration. This state is used by Terraform to map real world resources to your configuration, keep track of metadata, and to improve performance for large infrastructures. +--- + +# State + +Terraform must store state about your managed infrastructure and +configuration. This state is used by Terraform to map real world +resources to your configuration, keep track of metadata, and to improve +performance for large infrastructures. + +This state is stored by default in a local file named "terraform.tfstate", +but it can also be stored remotely, which works better in a team environment. + +Terraform uses this local state to create plans and make changes to your +infrastructure. Prior to any operation, Terraform does a +[refresh](/docs/cli/commands/refresh.html) to update the state with the +real infrastructure. + +The primary purpose of Terraform state is to store bindings between objects in +a remote system and resource instances declared in your configuration. +When Terraform creates a remote object in response to a change of configuration, +it will record the identity of that remote object against a particular +resource instance, and then potentially update or delete that object in +response to future configuration changes. + +For more information on why Terraform requires state and why Terraform cannot +function without state, please see the page [state purpose](/docs/language/state/purpose.html). + +## Inspection and Modification + +While the format of the state files are just JSON, direct file editing +of the state is discouraged. Terraform provides the +[terraform state](/docs/cli/commands/state/index.html) command to perform +basic modifications of the state using the CLI. + +The CLI usage and output of the state commands is structured to be +friendly for Unix tools such as grep, awk, etc. Additionally, the CLI +insulates users from any format changes within the state itself. The Terraform +project will keep the CLI working while the state format underneath it may +shift. + +Terraform expects a one-to-one mapping between configured resource instances +and remote objects. Normally that is guaranteed by Terraform being the one +to create each object and record its identity in the state, or to destroy +an object and then remove the binding for it. + +If you add or remove bindings in the state by other means, such as by importing +externally-created objects with `terraform import`, or by asking Terraform to +"forget" an existing object with `terraform state rm`, you'll then need to +ensure for yourself that this one-to-one rule is followed, such as by manually +deleting an object that you asked Terraform to "forget", or by re-importing it +to bind it to some other resource instance. + +## Format + +State snapshots are stored in JSON format and new Terraform versions are +generally backward compatible with state snapshots produced by earlier versions. +However, the state format is subject to change in new Terraform versions, so +if you build software that parses or modifies it directly you should expect +to perform ongoing maintenance of that software as the state format evolves +in new versions. + +Alternatively, there are several integration points which produce JSON output +that is specifically intended for consumption by external software: + +* [The `terraform output` command](/docs/cli/commands/output.html) +has a `-json` option, for obtaining either the full set of root module output +values or a specific named output value from the latest state snapshot. +* [The `terraform show` command](/docs/cli/commands/show.html) has a `-json` +option for inspecting the latest state snapshot in full, and also for +inspecting saved plan files which include a copy of the prior state at the +time the plan was made. + +A typical way to use these in situations where Terraform is running in +automation is to run them immediately after a successful `terraform apply` +to obtain a representation of the latest state snapshot, and then store that +result as an artifact associated with the automated run so that other software +can potentially consume it without needing to run Terraform itself. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/state/locking.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/state/locking.html.md similarity index 78% rename from vendor/github.com/hashicorp/terraform/website/docs/state/locking.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/state/locking.html.md index 6a4b8648..739a4b8c 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/state/locking.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/state/locking.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "State: Locking" sidebar_current: "docs-state-locking" description: |- @@ -8,7 +8,7 @@ description: |- # State Locking -If supported by your [backend](/docs/backends), Terraform will lock your +If supported by your [backend](/docs/language/settings/backends/index.html), Terraform will lock your state for all operations that could write state. This prevents others from acquiring the lock and potentially corrupting your state. @@ -21,13 +21,13 @@ If acquiring the lock is taking longer than expected, Terraform will output a status message. If Terraform doesn't output a message, state locking is still occurring if your backend supports it. -Not all [backends](/docs/backends) support locking. Please view the list -of [backend types](/docs/backends/types) for details on whether a backend -supports locking or not. +Not all backends support locking. The +[documentation for each backend](/docs/language/settings/backends/index.html) +includes details on whether it supports locking or not. ## Force Unlock -Terraform has a [force-unlock command](/docs/commands/force-unlock.html) +Terraform has a [force-unlock command](/docs/cli/commands/force-unlock.html) to manually unlock the state if unlocking failed. **Be very careful with this command.** If you unlock the state when someone diff --git a/vendor/github.com/hashicorp/terraform/website/docs/state/purpose.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/state/purpose.html.md similarity index 98% rename from vendor/github.com/hashicorp/terraform/website/docs/state/purpose.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/state/purpose.html.md index fd0a5c91..44bfef6d 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/state/purpose.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/state/purpose.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "State" sidebar_current: "docs-state-purpose" description: |- @@ -105,7 +105,7 @@ started, but when using Terraform in a team it is important for everyone to be working with the same state so that operations will be applied to the same remote objects. -[Remote state](/docs/state/remote.html) is the recommended solution +[Remote state](/docs/language/state/remote.html) is the recommended solution to this problem. With a fully-featured state backend, Terraform can use remote locking as a measure to avoid two or more different users accidentally running Terraform at the same time, and thus ensure that each Terraform run diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/state/remote-state-data.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/state/remote-state-data.html.md new file mode 100644 index 00000000..717d8fb3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/state/remote-state-data.html.md @@ -0,0 +1,213 @@ +--- +layout: "language" +page_title: "The terraform_remote_state Data Source" +sidebar_current: "docs-terraform-datasource-remote-state" +description: |- + Retrieves the root module output values from a Terraform state snapshot stored in a remote backend. +--- + +# The `terraform_remote_state` Data Source + +[backends]: /docs/backends/index.html + +The `terraform_remote_state` data source retrieves the root module output values +from some other Terraform configuration, using the latest state snapshot from +the remote backend. + +This data source is built into Terraform, and is always available; you do not +need to require or configure a provider in order to use it. + +-> **Note:** This data source is implemented by a built-in provider, whose +[source address](/docs/language/providers/requirements.html#source-addresses) +is `terraform.io/builtin/terraform`. That provider does not include any other +resources or data sources. + +## Alternative Ways to Share Data Between Configurations + +Sharing data with root module outputs is convenient, but it has drawbacks. +Although `terraform_remote_state` only exposes output values, its user must have +access to the entire state snapshot, which often includes some sensitive +information. + +When possible, we recommend explicitly publishing data for external consumption +to a separate location instead of accessing it via remote state. This lets you +apply different access controls for shared information and state snapshots. + +To share data explicitly between configurations, you can use pairs of managed +resource types and data sources in various providers, including (but not +limited to) the following: + +| System | Publish with... | Read with... | +|--|--|--| +| Alibaba Cloud DNS
(for IP addresses and hostnames) | [`alicloud_alidns_record` resource type](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/alidns_record) | Normal DNS lookups, or [the `dns` provider](https://registry.terraform.io/providers/hashicorp/dns/latest/docs) | +| Amazon Route53
(for IP addresses and hostnames) | [`aws_route53_record` resource type](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | Normal DNS lookups, or [the `dns` provider](https://registry.terraform.io/providers/hashicorp/dns/latest/docs) | +| Amazon S3 | [`aws_s3_bucket_object` resource type](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_object) | [`aws_s3_bucket_object` data source](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/s3_bucket_object) | +| Amazon SSM Parameter Store | [`aws_ssm_parameter` resource type](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | [`aws_ssm_parameter` data source](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | +| Azure Automation | [`azurerm_automation_variable_string` resource type](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/automation_variable_string) | [`azurerm_automation_variable_string` data source](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/automation_variable_string) | +| Azure DNS
(for IP addresses and hostnames) | [`azurerm_dns_a_record` resource type](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/dns_a_record), etc | Normal DNS lookups, or [the `dns` provider](https://registry.terraform.io/providers/hashicorp/dns/latest/docs) | +| Google Cloud DNS
(for IP addresses and hostnames) | [`google_dns_record_set` resource type](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | Normal DNS lookups, or [the `dns` provider](https://registry.terraform.io/providers/hashicorp/dns/latest/docs) | +| Google Cloud Storage | [`google_storage_bucket_object` resource type](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket_object) | [`google_storage_bucket_object` data source](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/storage_bucket_object) and [`http` data source](https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http) | +| HashiCorp Consul | [`consul_key_prefix` resource type](https://registry.terraform.io/providers/hashicorp/consul/latest/docs/resources/key_prefix) | [`consul_key_prefix` data source](https://registry.terraform.io/providers/hashicorp/consul/latest/docs/data-sources/key_prefix) | +| Kubernetes | [`kubernetes_config_map` resource type](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/config_map) | [`kubernetes_config_map` data source](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/data-sources/config_map) | +| OCI Object Storage | [`oci_objectstorage_bucket` resource type](https://registry.terraform.io/providers/hashicorp/oci/latest/docs/resources/objectstorage_object) | [`oci_objectstorage_bucket` data source](https://registry.terraform.io/providers/hashicorp/oci/latest/docs/data-sources/objectstorage_object) | + +-> These are some common options from the Official Terraform providers, but +there are too many configuration storage options for us to list them all +here, including some in partner and community providers. +Any pair of managed resource type and corresponding data source can potentially +be used to share data between Terraform configurations. See individual provider +documentation to find other possibilities. + +A key advantage of using a separate explicit configuration store instead of +`terraform_remote_state` is that the data can potentially also be read by +systems other than Terraform, such as configuration management or scheduler +systems within your compute instances. For that reason, we recommend selecting +a configuration store that your other infrastructure could potentially make +use of. For example: + +* If you wish to share IP addresses and hostnames, you could publish them as +normal DNS `A`, `AAAA`, `CNAME`, and `SRV` records in a private DNS zone and +then configure your other infrastructure to refer to that zone so you can +find infrastructure objects via your system's built-in DNS resolver. +* If you use HashiCorp Consul then publishing data to the Consul key/value +store or Consul service catalog can make that data also accessible via +[Consul Template](https://github.com/hashicorp/consul-template) +or the +[HashiCorp Nomad](https://www.nomadproject.io/docs/job-specification/template) +`template` stanza. +* If you use Kubernetes then you can +[make Config Maps available to your Pods](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/). + +Some of the data stores listed above are specifically designed for storing +small configuration values, while others are generic blob storage systems. For +those generic systems, you can use +[the `jsonencode` function](https://www.terraform.io/docs/language/functions/jsonencode.html) +and +[the `jsondecode` function](https://www.terraform.io/docs/language/functions/jsondecode.html) respectively +to store and retrieve structured data. + +You can encapsulate the implementation details of retrieving your published +configuration data by writing a +[data-only module](/docs/language/modules/develop/composition.html#data-only-modules) +containing the necessary data source configuration and any necessary +post-processing such as JSON decoding. You can then change that module later +if you switch to a different strategy for sharing data between multiple +Terraform configurations. + +## Example Usage (`remote` Backend) + +```hcl +data "terraform_remote_state" "vpc" { + backend = "remote" + + config = { + organization = "hashicorp" + workspaces = { + name = "vpc-prod" + } + } +} + +# Terraform >= 0.12 +resource "aws_instance" "foo" { + # ... + subnet_id = data.terraform_remote_state.vpc.outputs.subnet_id +} + +# Terraform <= 0.11 +resource "aws_instance" "foo" { + # ... + subnet_id = "${data.terraform_remote_state.vpc.subnet_id}" +} +``` + +## Example Usage (`local` Backend) + +```hcl +data "terraform_remote_state" "vpc" { + backend = "local" + + config = { + path = "..." + } +} + +# Terraform >= 0.12 +resource "aws_instance" "foo" { + # ... + subnet_id = data.terraform_remote_state.vpc.outputs.subnet_id +} + +# Terraform <= 0.11 +resource "aws_instance" "foo" { + # ... + subnet_id = "${data.terraform_remote_state.vpc.subnet_id}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `backend` - (Required) The remote backend to use. +* `workspace` - (Optional) The Terraform workspace to use, if the backend + supports workspaces. +* `config` - (Optional; object) The configuration of the remote backend. + Although this argument is listed as optional, most backends require + some configuration. + + The `config` object can use any arguments that would be valid in the + equivalent `terraform { backend "" { ... } }` block. See + [the documentation of your chosen backend](/docs/language/settings/backends/index.html) + for details. + + -> **Note:** If the backend configuration requires a nested block, specify + it here as a normal attribute with an object value. (For example, + `workspaces = { ... }` instead of `workspaces { ... }`.) +* `defaults` - (Optional; object) Default values for outputs, in case the state + file is empty or lacks a required output. + +## Attributes Reference + +In addition to the above, the following attributes are exported: + +* (v0.12+) `outputs` - An object containing every root-level + [output](/docs/language/values/outputs.html) in the remote state. +* (<= v0.11) `` - Each root-level [output](/docs/language/values/outputs.html) + in the remote state appears as a top level attribute on the data source. + +## Root Outputs Only + +Only the root-level output values from the remote state snapshot are exposed +for use elsewhere in your module. Resource data and output values from nested +modules are not accessible. + +If you wish to make a nested module output value accessible as a root module +output value, you must explicitly configure a passthrough in the root module. +For example: + +For example: + +```hcl +module "app" { + source = "..." +} + +output "app_value" { + # This syntax is for Terraform 0.12 or later. + value = module.app.example +} +``` + +In this example, the output value named `example` from the "app" module is +available as the `app_value` root module output value. If this configuration +didn't include the `output "app_value"` block then the data would not be +accessible via `terraform_remote_state`. + +~> **Warning:** Although `terraform_remote_state` doesn't expose any other +state snapshot information for use in configuration, the state snapshot data +is a single object and so any user or server which has enough access to read +the root module output values will also always have access to the full state +snapshot data by direct network requests. Don't use `terraform_remote_state` +if any of the resources in your configuration work with data that you consider +sensitive. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/state/remote.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/state/remote.html.md new file mode 100644 index 00000000..998b4794 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/state/remote.html.md @@ -0,0 +1,64 @@ +--- +layout: "language" +page_title: "State: Remote Storage" +sidebar_current: "docs-state-remote" +description: |- + Terraform can store the state remotely, making it easier to version and work with in a team. +--- + +# Remote State + +By default, Terraform stores state locally in a file named `terraform.tfstate`. +When working with Terraform in a team, use of a local file makes Terraform +usage complicated because each user must make sure they always have the latest +state data before running Terraform and make sure that nobody else runs +Terraform at the same time. + +With _remote_ state, Terraform writes the state data to a remote data store, +which can then be shared between all members of a team. Terraform supports +storing state in [Terraform Cloud](https://www.hashicorp.com/products/terraform/), +[HashiCorp Consul](https://www.consul.io/), Amazon S3, Azure Blob Storage, Google Cloud Storage, Alibaba Cloud OSS, and more. + +Remote state is implemented by a [backend](/docs/language/settings/backends/index.html), +which you can configure in your configuration's root module. + +## Delegation and Teamwork + +Remote state allows you to share +[output values](/docs/language/values/outputs.html) with other configurations. +This allows your infrastructure to be decomposed into smaller components. + +Put another way, remote state also allows teams to share infrastructure +resources in a read-only way without relying on any additional configuration +store. + +For example, a core infrastructure team can handle building the core +machines, networking, etc. and can expose some information to other +teams to run their own infrastructure. As a more specific example with AWS: +you can expose things such as VPC IDs, subnets, NAT instance IDs, etc. through +remote state and have other Terraform states consume that. + +For example usage, see +[the `terraform_remote_state` data source](/docs/language/state/remote-state-data.html). + +While remote state can be a convenient, built-in mechanism for sharing data +between configurations, you may prefer to use more general stores to +pass settings both to other configurations and to other consumers. For example, +if your environment has [HashiCorp Consul](https://www.consul.io/) then you +can have one Terraform configuration that writes to Consul using +[`consul_key_prefix`](https://registry.terraform.io/providers/hashicorp/consul/latest/docs/resources/key_prefix) and then +another that consumes those values using +[the `consul_keys` data source](https://registry.terraform.io/providers/hashicorp/consul/latest/docs/data-sources/keys). + +## Locking and Teamwork + +For fully-featured remote backends, Terraform can also use +[state locking](/docs/language/state/locking.html) to prevent concurrent runs of +Terraform against the same state. + +[Terraform Cloud by HashiCorp](https://www.hashicorp.com/products/terraform/) +is a commercial offering that supports an even stronger locking concept that +can also detect attempts to create a new plan when an existing plan is already +awaiting approval, by queuing Terraform operations in a central location. +This allows teams to more easily coordinate and communicate about changes to +infrastructure. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/state/sensitive-data.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/state/sensitive-data.html.md similarity index 93% rename from vendor/github.com/hashicorp/terraform/website/docs/state/sensitive-data.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/state/sensitive-data.html.md index f5ccaa11..49f5c127 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/state/sensitive-data.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/state/sensitive-data.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "State: Sensitive Data" sidebar_current: "docs-state-sensitive-data" description: |- @@ -15,7 +15,7 @@ passwords. When using local state, state is stored in plain-text JSON files. -When using [remote state](/docs/state/remote.html), state is only ever held in +When using [remote state](/docs/language/state/remote.html), state is only ever held in memory when used by Terraform. It may be encrypted at rest, but this depends on the specific remote state backend. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/state/workspaces.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/state/workspaces.html.md similarity index 79% rename from vendor/github.com/hashicorp/terraform/website/docs/state/workspaces.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/state/workspaces.html.md index e2a51c4b..a675d8ed 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/state/workspaces.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/state/workspaces.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "State: Workspaces" sidebar_current: "docs-state-workspaces" description: |- @@ -8,9 +8,9 @@ description: |- # Workspaces -Each Terraform configuration has an associated [backend](/docs/backends/index.html) +Each Terraform configuration has an associated [backend](/docs/language/settings/backends/index.html) that defines how operations are executed and where persistent data such as -[the Terraform state](https://www.terraform.io/docs/state/purpose.html) are +[the Terraform state](https://www.terraform.io/docs/language/state/purpose.html) are stored. The persistent data stored in the backend belongs to a _workspace_. Initially @@ -25,16 +25,17 @@ credentials. Multiple workspaces are currently supported by the following backends: - * [AzureRM](/docs/backends/types/azurerm.html) - * [Consul](/docs/backends/types/consul.html) - * [COS](/docs/backends/types/cos.html) - * [GCS](/docs/backends/types/gcs.html) - * [Kubernetes](/docs/backends/types/kubernetes.html) - * [Local](/docs/backends/types/local.html) - * [Manta](/docs/backends/types/manta.html) - * [Postgres](/docs/backends/types/pg.html) - * [Remote](/docs/backends/types/remote.html) - * [S3](/docs/backends/types/s3.html) + * [AzureRM](/docs/language/settings/backends/azurerm.html) + * [Consul](/docs/language/settings/backends/consul.html) + * [COS](/docs/language/settings/backends/cos.html) + * [etcdv3](/docs/language/settings/backends/etcdv3.html) + * [GCS](/docs/language/settings/backends/gcs.html) + * [Kubernetes](/docs/language/settings/backends/kubernetes.html) + * [Local](/docs/language/settings/backends/local.html) + * [Manta](/docs/language/settings/backends/manta.html) + * [Postgres](/docs/language/settings/backends/pg.html) + * [Remote](/docs/language/settings/backends/remote.html) + * [S3](/docs/language/settings/backends/s3.html) In the 0.9 line of Terraform releases, this concept was known as "environment". It was renamed in 0.10 based on feedback about confusion caused by the @@ -81,7 +82,7 @@ Within your Terraform configuration, you may include the name of the current workspace using the `${terraform.workspace}` interpolation sequence. This can be used anywhere interpolations are allowed. However, it should **not** be used in remote operations against Terraform Cloud workspaces. For an -explanation, see the [remote backend](../backends/types/remote.html#workspaces) +explanation, see the [remote backend](/docs/language/settings/backends/remote.html#workspaces) document. Referencing the current workspace is useful for changing behavior based @@ -122,7 +123,7 @@ set of infrastructure changes might create a new temporary workspace in order to freely experiment with changes without affecting the default workspace. Non-default workspaces are often related to feature branches in version control. -The default workspace might correspond to the "master" or "trunk" branch, +The default workspace might correspond to the "main" or "trunk" branch, which describes the intended state of production infrastructure. When a feature branch is created to develop a change, the developer of that feature might create a corresponding workspace and deploy into it a temporary "copy" @@ -146,7 +147,7 @@ In this case, the backend used for each deployment often belongs to that deployment, with different credentials and access controls. Named workspaces are _not_ a suitable isolation mechanism for this scenario. -Instead, use one or more [re-usable modules](/docs/modules/index.html) to +Instead, use one or more [re-usable modules](/docs/language/modules/develop/index.html) to represent the common elements, and then represent each instance as a separate configuration that instantiates those common elements in the context of a different backend. In that case, the root module of each configuration will @@ -157,26 +158,26 @@ Where multiple configurations are representing distinct system components rather than multiple deployments, data can be passed from one component to another using paired resources types and data sources. For example: -* Where a shared [Consul](https://consul.io/) cluster is available, use - [`consul_key_prefix`](/docs/providers/consul/r/key_prefix.html) to - publish to the key/value store and [`consul_keys`](/docs/providers/consul/d/keys.html) +* Where a shared [Consul](https://www.consul.io/) cluster is available, use + [`consul_key_prefix`](https://registry.terraform.io/providers/hashicorp/consul/latest/docs/resources/key_prefix) to + publish to the key/value store and [`consul_keys`](https://registry.terraform.io/providers/hashicorp/consul/latest/docs/data-sources/keys) to retrieve those values in other configurations. * In systems that support user-defined labels or tags, use a tagging convention to make resources automatically discoverable. For example, use - [the `aws_vpc` resource type](/docs/providers/aws/r/vpc.html) + [the `aws_vpc` resource type](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc) to assign suitable tags and then - [the `aws_vpc` data source](/docs/providers/aws/d/vpc.html) + [the `aws_vpc` data source](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc) to query by those tags in other configurations. * For server addresses, use a provider-specific resource to create a DNS record with a predictable name and then either use that name directly or - use [the `dns` provider](/docs/providers/dns/index.html) to retrieve + use [the `dns` provider](https://registry.terraform.io/providers/hashicorp/dns/latest/docs) to retrieve the published addresses in other configurations. * If a Terraform state for one configuration is stored in a remote backend that is accessible to other configurations then - [`terraform_remote_state`](/docs/providers/terraform/d/remote_state.html) + [`terraform_remote_state`](/docs/language/state/remote-state-data.html) can be used to directly consume its root module outputs from those other configurations. This creates a tighter coupling between configurations, but avoids the need for the "producer" configuration to explicitly @@ -194,9 +195,9 @@ local-only `terraform.tfstate`; some teams commit these files to version control, although using a remote backend instead is recommended when there are multiple collaborators. -For [remote state](/docs/state/remote.html), the workspaces are stored -directly in the configured [backend](/docs/backends). For example, if you -use [Consul](/docs/backends/types/consul.html), the workspaces are stored +For [remote state](/docs/language/state/remote.html), the workspaces are stored +directly in the configured [backend](/docs/language/settings/backends/index.html). For example, if you +use [Consul](/docs/language/settings/backends/consul.html), the workspaces are stored by appending the workspace name to the state path. To ensure that workspace names are stored correctly and safely in all backends, the name must be valid to use in a URL path segment without escaping. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/syntax/configuration.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/syntax/configuration.html.md new file mode 100644 index 00000000..e080ed54 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/syntax/configuration.html.md @@ -0,0 +1,131 @@ +--- +layout: "language" +page_title: "Syntax - Configuration Language" +sidebar_current: "docs-config-syntax" +description: |- + The Terraform language has its own syntax, intended to combine declarative + structure with expressions in a way that is easy for humans to read and + understand. +--- + +# Configuration Syntax + +Other pages in this section have described various configuration constructs +that can appear in the Terraform language. This page describes the lower-level +syntax of the language in more detail, revealing the building blocks that +those constructs are built from. + +This page describes the _native syntax_ of the Terraform language, which is +a rich language designed to be relatively easy for humans to read and write. +The constructs in the Terraform language can also be expressed in +[JSON syntax](/docs/language/syntax/json.html), which is harder for humans +to read and edit but easier to generate and parse programmatically. + +This low-level syntax of the Terraform language is defined in terms of a +syntax called _HCL_, which is also used by configuration languages in +other applications, and in particular other HashiCorp products. +It is not necessary to know all of the details of HCL syntax in +order to use Terraform, and so this page summarizes the most important +details. If you are interested, you can find a full definition of HCL +syntax in +[the HCL native syntax specification](https://github.com/hashicorp/hcl/blob/main/hclsyntax/spec.md). + +## Arguments and Blocks + +The Terraform language syntax is built around two key syntax constructs: +arguments and blocks. + +### Arguments + +An _argument_ assigns a value to a particular name: + +```hcl +image_id = "abc123" +``` + +The identifier before the equals sign is the _argument name_, and the expression +after the equals sign is the argument's value. + +The context where the argument appears determines what value types are valid +(for example, each resource type has a schema that defines the types of its +arguments), but many arguments accept arbitrary +[expressions](/docs/language/expressions/index.html), which allow the value to +either be specified literally or generated from other values programmatically. + +-> **Note:** Terraform's configuration language is based on a more general +language called HCL, and HCL's documentation usually uses the word "attribute" +instead of "argument." These words are similar enough to be interchangeable in +this context, and experienced Terraform users might use either term in casual +conversation. But because Terraform also interacts with several _other_ things +called "attributes" (in particular, Terraform resources have attributes like +`id` that can be referenced from expressions but can't be assigned values in +configuration), we've chosen to use "argument" in the Terraform documentation +when referring to this syntax construct. + +### Blocks + +A _block_ is a container for other content: + +```hcl +resource "aws_instance" "example" { + ami = "abc123" + + network_interface { + # ... + } +} +``` + +A block has a _type_ (`resource` in this example). Each block type defines +how many _labels_ must follow the type keyword. The `resource` block type +expects two labels, which are `aws_instance` and `example` in the example above. +A particular block type may have any number of required labels, or it may +require none as with the nested `network_interface` block type. + +After the block type keyword and any labels, the block _body_ is delimited +by the `{` and `}` characters. Within the block body, further arguments +and blocks may be nested, creating a hierarchy of blocks and their associated +arguments. + +The Terraform language uses a limited number of _top-level block types,_ which +are blocks that can appear outside of any other block in a configuration file. +Most of Terraform's features (including resources, input variables, output +values, data sources, etc.) are implemented as top-level blocks. + +## Identifiers + +Argument names, block type names, and the names of most Terraform-specific +constructs like resources, input variables, etc. are all _identifiers_. + +Identifiers can contain letters, digits, underscores (`_`), and hyphens (`-`). +The first character of an identifier must not be a digit, to avoid ambiguity +with literal numbers. + +For complete identifier rules, Terraform implements +[the Unicode identifier syntax](http://unicode.org/reports/tr31/), extended to +include the ASCII hyphen character `-`. + +## Comments + +The Terraform language supports three different syntaxes for comments: + +* `#` begins a single-line comment, ending at the end of the line. +* `//` also begins a single-line comment, as an alternative to `#`. +* `/*` and `*/` are start and end delimiters for a comment that might span + over multiple lines. + +The `#` single-line comment style is the default comment style and should be +used in most cases. Automatic configuration formatting tools may automatically +transform `//` comments into `#` comments, since the double-slash style is +not idiomatic. + +## Character Encoding and Line Endings + +Terraform configuration files must always be UTF-8 encoded. While the +delimiters of the language are all ASCII characters, Terraform accepts +non-ASCII characters in identifiers, comments, and string values. + +Terraform accepts configuration files with either Unix-style line endings +(LF only) or Windows-style line endings (CR then LF), but the idiomatic style +is to use the Unix convention, and so automatic configuration formatting tools +may automatically transform CRLF endings to LF. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/syntax/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/syntax/index.html.md new file mode 100644 index 00000000..83de5faa --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/syntax/index.html.md @@ -0,0 +1,21 @@ +--- +layout: "language" +page_title: "Syntax Overview - Configuration Language" +--- + +# Syntax + +The majority of the Terraform language documentation focuses on the practical +uses of the language and the specific constructs it uses. The pages in this +section offer a more abstract view of the Terraform language. + +- [Configuration Syntax](/docs/language/syntax/configuration.html) describes the native + grammar of the Terraform language. +- [JSON Configuration Syntax](/docs/language/syntax/json.html) documents + how to represent Terraform language constructs in the pure JSON variant of the + Terraform language. Terraform's JSON syntax is unfriendly to humans, but can + be very useful when generating infrastructure as code with other systems that + don't have a readily available HCL library. +- [Style Conventions](/docs/language/syntax/style.html) documents some commonly + accepted formatting guidelines for Terraform code. These conventions can be + enforced automatically with [`terraform fmt`](/docs/cli/commands/fmt.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/syntax/json.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/syntax/json.html.md new file mode 100644 index 00000000..ea991702 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/syntax/json.html.md @@ -0,0 +1,471 @@ +--- +layout: "language" +page_title: "JSON Configuration Syntax - Configuration Language" +sidebar_current: "docs-config-syntax-json" +description: |- + In addition to the native syntax that is most commonly used with Terraform, + the Terraform language can also be expressed in a JSON-compatible syntax. +--- + +# JSON Configuration Syntax + +Most Terraform configurations are written in +[the native Terraform language syntax](/docs/language/syntax/configuration.html), which is designed to be +relatively easy for humans to read and update. + +Terraform also supports an alternative syntax that is JSON-compatible. This +syntax is useful when generating portions of a configuration programmatically, +since existing JSON libraries can be used to prepare the generated +configuration files. + +The JSON syntax is defined in terms of the native syntax. Everything that can +be expressed in native syntax can also be expressed in JSON syntax, but some +constructs are more complex to represent in JSON due to limitations of the +JSON grammar. + +Terraform expects native syntax for files named with a `.tf` suffix, and +JSON syntax for files named with a `.tf.json` suffix. + +The low-level JSON syntax, just as with the native syntax, is defined in terms +of a specification called _HCL_. It is not necessary to know all of the details +of HCL syntax or its JSON mapping in order to use Terraform, and so this page +summarizes the most important differences between native and JSON syntax. +If you are interested, you can find a full definition of HCL's JSON syntax +in [its specification](https://github.com/hashicorp/hcl/blob/main/json/spec.md). + +## JSON File Structure + +At the root of any JSON-based Terraform configuration is a JSON object. The +properties of this object correspond to the top-level block types of the +Terraform language. For example: + +```json +{ + "variable": { + "example": { + "default": "hello" + } + } +} +``` + +Each top-level object property must match the name of one of the expected +top-level block types. Block types that expect labels, such as `variable` +shown above, are represented by one nested object value for each level +of label. `resource` blocks expect two labels, so two levels of nesting +are required: + +```json +{ + "resource": { + "aws_instance": { + "example": { + "instance_type": "t2.micro", + "ami": "ami-abc123" + } + } + } +} +``` + +After any nested objects representing the labels, finally one more nested +object represents the body of the block itself. In the above examples, the +`default` argument for `variable "example"` and the `instance_type` and +`ami` arguments for `resource "aws_instance" "example"` are specified. + +Taken together, the above two configuration files are equivalent to the +following blocks in the native syntax: + +```hcl +variable "example" { + default = "hello" +} + +resource "aws_instance" "example" { + instance_type = "t2.micro" + ami = "ami-abc123" +} +``` + +Within each top-level block type the rules for mapping to JSON are slightly +different (see the [block-type-specific exceptions](#block-type-specific-exceptions) below), but the following general rules apply in most cases: + +* The JSON object representing the block body contains properties that + correspond either to argument names or to nested block type names. + +* Where a property corresponds to an argument that accepts + [arbitrary expressions](/docs/language/expressions/index.html) in the native syntax, the + property value is mapped to an expression as described under + [_Expression Mapping_](#expression-mapping) below. For arguments that + do _not_ accept arbitrary expressions, the interpretation of the property + value depends on the argument, as described in the + [block-type-specific exceptions](#block-type-specific-exceptions) + given later in this page. + +* Where a property name corresponds to an expected nested block type name, + the value is interpreted as described under + [_Nested Block Mapping_](#nested-block-mapping) below, unless otherwise + stated in [the block-type-specific exceptions](#block-type-specific-exceptions) + given later in this page. + +## Expression Mapping + +Since JSON grammar is not able to represent all of the Terraform language +[expression syntax](/docs/language/expressions/index.html), JSON values interpreted as expressions +are mapped as follows: + +| JSON | Terraform Language Interpretation | +| ------- | ------------------------------------------------------------------------------------------------------------- | +| Boolean | A literal `bool` value. | +| Number | A literal `number` value. | +| String | Parsed as a [string template][] and then evaluated as described below. | +| Object | Each property value is mapped per this table, producing an `object(...)` value with suitable attribute types. | +| Array | Each element is mapped per this table, producing a `tuple(...)` value with suitable element types. | +| Null | A literal `null`. | + +[string template]: /docs/language/expressions/strings.html#string-templates + +When a JSON string is encountered in a location where arbitrary expressions are +expected, its value is first parsed as a [string template][] +and then it is evaluated to produce the final result. + +If the given template consists _only_ of a single interpolation sequence, +the result of its expression is taken directly, without first converting it +to a string. This allows non-string expressions to be used within the +JSON syntax: + +```json +{ + "output": { + "example": { + "value": "${aws_instance.example}" + } + } +} +``` + +The `output "example"` declared above has the object value representing the +given `aws_instance` resource block as its value, rather than a string value. +This special behavior does not apply if any literal or control sequences appear +in the template; in these other situations, a string value is always produced. + +## Nested Block Mapping + +When a JSON object property is named after a nested block type, the value +of this property represents one or more blocks of that type. The value of +the property must be either a JSON object or a JSON array. + +The simplest situation is representing only a single block of the given type +when that type expects no labels, as with the `lifecycle` nested block used +within `resource` blocks: + +```json +{ + "resource": { + "aws_instance": { + "example": { + "lifecycle": { + "create_before_destroy": true + } + } + } + } +} +``` + +The above is equivalent to the following native syntax configuration: + +```hcl +resource "aws_instance" "example" { + lifecycle { + create_before_destroy = true + } +} +``` + +When the nested block type requires one or more labels, or when multiple +blocks of the same type can be given, the mapping gets a little more +complicated. For example, the `provisioner` nested block type used +within `resource` blocks expects a label giving the provisioner to use, +and the ordering of provisioner blocks is significant to decide the order +of operations. + +The following native syntax example shows a `resource` block with a number +of provisioners of different types: + +```hcl +resource "aws_instance" "example" { + # (resource configuration omitted for brevity) + + provisioner "local-exec" { + command = "echo 'Hello World' >example.txt" + } + provisioner "file" { + source = "example.txt" + destination = "/tmp/example.txt" + } + provisioner "remote-exec" { + inline = [ + "sudo install-something -f /tmp/example.txt", + ] + } +} +``` + +In order to preserve the order of these blocks, you must use a JSON array +as the direct value of the property representing this block type, as in +this JSON equivalent of the above: + +```json +{ + "resource": { + "aws_instance": { + "example": { + "provisioner": [ + { + "local-exec": { + "command": "echo 'Hello World' >example.txt" + } + }, + { + "file": { + "source": "example.txt", + "destination": "/tmp/example.txt" + } + }, + { + "remote-exec": { + "inline": ["sudo install-something -f /tmp/example.txt"] + } + } + ] + } + } + } +} +``` + +Each element of the `provisioner` array is an object with a single property +whose name represents the label for each `provisioner` block. For block types +that expect multiple labels, this pattern of alternating array and object +nesting can be used for each additional level. + +If a nested block type requires labels but the order does _not_ matter, you +may omit the array and provide just a single object whose property names +correspond to unique block labels. This is allowed as a shorthand for the above +for simple cases, but the alternating array and object approach is the most +general. We recommend using the most general form if systematically converting +from native syntax to JSON, to ensure that the meaning of the configuration is +preserved exactly. + +### Comment Properties + +Although we do not recommend hand-editing of JSON syntax configuration files +-- this format is primarily intended for programmatic generation and consumption -- +a limited form of _comments_ are allowed inside JSON objects that represent +block bodies using a special property name: + +```json +{ + "resource": { + "aws_instance": { + "example": { + "//": "This instance runs the scheduled tasks for backup", + + "instance_type": "t2.micro", + "ami": "ami-abc123" + } + } + } +} +``` + +In any object that represents a block body, properties named `"//"` are +ignored by Terraform entirely. This exception does _not_ apply to objects +that are being [interpreted as expressions](#expression-mapping), where this +would be interpreted as an object type attribute named `"//"`. + +This special property name can also be used at the root of a JSON-based +configuration file. This can be useful to note which program created the file. + +```json +{ + "//": "This file is generated by generate-outputs.py. DO NOT HAND-EDIT!", + + "output": { + "example": { + "value": "${aws_instance.example}" + } + } +} +``` + +## Block-type-specific Exceptions + +[inpage-block]: #block-type-specific-exceptions + +Certain arguments within specific block types are processed in a special way +by Terraform, and so their mapping to the JSON syntax does not follow the +general rules described above. The following sub-sections describe the special +mapping rules that apply to each top-level block type. + +### `resource` and `data` blocks + +Some meta-arguments for the `resource` and `data` block types take direct +references to objects, or literal keywords. When represented in JSON, the +reference or keyword is given as a JSON string with no additional surrounding +spaces or symbols. + +For example, the `provider` meta-argument takes a `.` reference +to a provider configuration, which appears unquoted in the native syntax but +must be presented as a string in the JSON syntax: + +```json +{ + "resource": { + "aws_instance": { + "example": { + "provider": "aws.foo" + } + } + } +} +``` + +This special processing applies to the following meta-arguments: + +* `provider`: a single string, as shown above +* `depends_on`: an array of strings containing references to named entities, + like `["aws_instance.example"]`. +* `ignore_changes` within the `lifecycle` block: if set to `all`, a single + string `"all"` must be given. Otherwise, an array of JSON strings containing + property references must be used, like `["ami"]`. + +Special processing also applies to the `type` argument of any `connection` +blocks, whether directly inside the `resource` block or nested inside +`provisioner` blocks: the given string is interpreted literally, and not +parsed and evaluated as a string template. + +### `variable` blocks + +All arguments inside `variable` blocks have non-standard mappings to JSON: + +* `type`: a string containing a type expression, like `"string"` or `"list(string)"`. +* `default`: a literal JSON value that can be converted to the given type. + Strings within this value are taken literally and _not_ interpreted as + string templates. +* `description`: a literal JSON string, _not_ interpreted as a template. + +```json +{ + "variable": { + "example": { + "type": "string", + "default": "hello" + } + } +} +``` + +### `output` blocks + +The `description` and `sensitive` arguments are interpreted as literal JSON +values. The `description` string is not interpreted as a string template. + +The `value` argument is [interpreted as an expression](#expression-mapping). + +```json +{ + "output": { + "example": { + "value": "${aws_instance.example}" + } + } +} +``` + +### `locals` blocks + +The value of the JSON object property representing the locals block type +must be a JSON object whose property names are the local value names to +declare: + +```json +{ + "locals": { + "greeting": "Hello, ${var.name}" + } +} +``` + +The value of each of these nested properties is +[interpreted as an expression](#expression-mapping). + +### `module` blocks + +The `source` and `version` meta-arguments must be given as literal strings. The +values are not interpreted as string templates. + +The `providers` meta-argument must be given as a JSON object whose properties +are the compact provider addresses to expose into the child module and whose +values are the provider addresses to use from the current module, both +given as literal strings: + +```json +{ + "module": { + "example": { + "source": "hashicorp/consul/azurerm", + "version": "= 1.0.0", + "providers": { + "aws": "aws.usw1" + } + } + } +} +``` + +### `provider` blocks + +The `alias` and `version` meta-arguments must be given as literal strings. The +values are not interpreted as string templates. + +```json +{ + "provider": { + "aws": [ + { + "region": "us-east-1" + }, + { + "alias": "usw1", + "region": "us-west-1" + } + ] + } +} +``` + +### `terraform` blocks + +Since no settings within `terraform` blocks accept named object references or +function calls, all setting values are taken literally. String values are not +interpreted as string templates. + +Since only one `backend` block is allowed per `terraform` block, the compact +block mapping can be used to represent it, with a nested object containing +a single property whose name represents the backend type. + +```json +{ + "terraform": { + "required_version": ">= 0.12.0", + "backend": { + "s3": { + "region": "us-west-2", + "bucket": "acme-terraform-states" + } + } + } +} +``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/style.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/syntax/style.html.md similarity index 93% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/style.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/syntax/style.html.md index 2ca6e732..c7599bd0 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/style.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/syntax/style.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Style Conventions - Configuration Language" sidebar_current: "docs-config-style" description: |- @@ -10,10 +10,6 @@ description: |- # Style Conventions --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language](../configuration-0-11/index.html). - The Terraform parser allows you some flexibility in how you lay out the elements in your configuration files, but the Terraform language also has some idiomatic style conventions which we recommend users always follow diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/values/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/values/index.html.md new file mode 100644 index 00000000..e73972c4 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/values/index.html.md @@ -0,0 +1,18 @@ +--- +layout: "language" +page_title: "Variables and Outputs" +--- + +# Variables and Outputs + +The Terraform language includes a few kinds of blocks for requesting or +publishing named values. + +- [Input Variables](/docs/language/values/variables.html) serve as parameters for + a Terraform module, so users can customize behavior without editing the source. + +- [Output Values](/docs/language/values/outputs.html) are like return values for a + Terraform module. + +- [Local Values](/docs/language/values/locals.html) are a convenience feature for + assigning a short name to an expression. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/locals.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/values/locals.html.md similarity index 84% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/locals.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/values/locals.html.md index 54502a28..ad755b26 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/locals.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/values/locals.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Local Values - Configuration Language" sidebar_current: "docs-config-locals" description: |- @@ -9,11 +9,11 @@ description: |- # Local Values --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Local Values](../configuration-0-11/locals.html). +> **Hands-on:** Try the [Simplify Terraform Configuration with +Locals](https://learn.hashicorp.com/tutorials/terraform/locals?in=terraform/configuration-language&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) +tutorial on HashiCorp Learn. -A local value assigns a name to an [expression](./expressions.html), +A local value assigns a name to an [expression](/docs/language/expressions/index.html), so you can use it multiple times within a module without repeating it. @@ -61,7 +61,7 @@ locals { ## Using Local Values Once a local value is declared, you can reference it in -[expressions](./expressions.html) as `local.`. +[expressions](/docs/language/expressions/index.html) as `local.`. -> **Note:** Local values are _created_ by a `locals` block (plural), but you _reference_ them as attributes on an object named `local` (singular). Make sure diff --git a/vendor/github.com/hashicorp/terraform/website/docs/language/values/outputs.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/values/outputs.html.md new file mode 100644 index 00000000..35b8aa44 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/values/outputs.html.md @@ -0,0 +1,196 @@ +--- +layout: "language" +page_title: "Output Values - Configuration Language" +sidebar_current: "docs-config-outputs" +description: |- + Output values are the return values of a Terraform module. +--- + +# Output Values + +> **Hands-on:** Try the [Output Data From +Terraform](https://learn.hashicorp.com/tutorials/terraform/outputs?in=terraform/configuration-language&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) +tutorial on HashiCorp Learn. + +Output values are like the return values of a Terraform module, and have several +uses: + +- A child module can use outputs to expose a subset of its resource attributes + to a parent module. +- A root module can use outputs to print certain values in the CLI output after + running `terraform apply`. +- When using [remote state](/docs/language/state/remote.html), root module outputs can be + accessed by other configurations via a + [`terraform_remote_state` data source](/docs/language/state/remote-state-data.html). + +Resource instances managed by Terraform each export attributes whose values +can be used elsewhere in configuration. Output values are a way to expose some +of that information to the user of your module. + +-> **Note:** For brevity, output values are often referred to as just "outputs" +when the meaning is clear from context. + +## Declaring an Output Value + +Each output value exported by a module must be declared using an `output` +block: + +```hcl +output "instance_ip_addr" { + value = aws_instance.server.private_ip +} +``` + +The label immediately after the `output` keyword is the name, which must be a +valid [identifier](/docs/language/syntax/configuration.html#identifiers). In a root module, this name is +displayed to the user; in a child module, it can be used to access the output's +value. + +The `value` argument takes an [expression](/docs/language/expressions/index.html) +whose result is to be returned to the user. In this example, the expression +refers to the `private_ip` attribute exposed by an `aws_instance` resource +defined elsewhere in this module (not shown). Any valid expression is allowed +as an output value. + +-> **Note:** Outputs are only rendered when Terraform applies your plan. Running +`terraform plan` will not render outputs. + +## Accessing Child Module Outputs + +In a parent module, outputs of child modules are available in expressions as +`module..`. For example, if a child module named +`web_server` declared an output named `instance_ip_addr`, you could access that +value as `module.web_server.instance_ip_addr`. + +## Optional Arguments + +`output` blocks can optionally include `description`, `sensitive`, and `depends_on` arguments, which are described in the following sections. + + + +### `description` — Output Value Documentation + +Because the output values of a module are part of its user interface, you can +briefly describe the purpose of each value using the optional `description` +argument: + +```hcl +output "instance_ip_addr" { + value = aws_instance.server.private_ip + description = "The private IP address of the main server instance." +} +``` + +The description should concisely explain the +purpose of the output and what kind of value is expected. This description +string might be included in documentation about the module, and so it should be +written from the perspective of the user of the module rather than its +maintainer. For commentary for module maintainers, use comments. + + + +### `sensitive` — Suppressing Values in CLI Output + +An output can be marked as containing sensitive material using the optional +`sensitive` argument: + +```hcl +output "db_password" { + value = aws_db_instance.db.password + description = "The password for logging in to the database." + sensitive = true +} +``` + +Terraform will hide values marked as sensitive in the messages from +`terraform plan` and `terraform apply`. In the following scenario, our root +module has an output declared as sensitive and a module call with a +sensitive output, which we then use in a resource attribute. + +```hcl +# main.tf + +module "foo" { + source = "./mod" +} + +resource "test_instance" "x" { + some_attribute = module.mod.a # resource attribute references a sensitive output +} + +output "out" { + value = "xyz" + sensitive = true +} + +# mod/main.tf, our module containing a sensitive output + +output "a" { + value = "secret" + sensitive = true +} +``` + +When we run a plan or apply, the sensitive value is redacted from output: + +``` +Terraform will perform the following actions: + + # test_instance.x will be created + + resource "test_instance" "x" { + + some_attribute = (sensitive) + } + +Plan: 1 to add, 0 to change, 0 to destroy. + +Changes to Outputs: + + out = (sensitive value) +``` + +-> **Note:** In Terraform versions prior to Terraform 0.14, setting an output +value in the root module as sensitive would prevent Terraform from showing its +value in the list of outputs at the end of `terraform apply`. However, the +value could still display in the CLI output for other reasons, like if the +value is referenced in an expression for a resource argument. + +Terraform will still record sensitive values in the [state](/docs/language/state/index.html), +and so anyone who can access the state data will have access to the sensitive +values in cleartext. For more information, see +[_Sensitive Data in State_](/docs/language/state/sensitive-data.html). + + + +### `depends_on` — Explicit Output Dependencies + +Since output values are just a means for passing data out of a module, it is +usually not necessary to worry about their relationships with other nodes in +the dependency graph. + +However, when a parent module accesses an output value exported by one of its +child modules, the dependencies of that output value allow Terraform to +correctly determine the dependencies between resources defined in different +modules. + +Just as with +[resource dependencies](/docs/language/resources/behavior.html#resource-dependencies), +Terraform analyzes the `value` expression for an output value and automatically +determines a set of dependencies, but in less-common cases there are +dependencies that cannot be recognized implicitly. In these rare cases, the +`depends_on` argument can be used to create additional explicit dependencies: + +```hcl +output "instance_ip_addr" { + value = aws_instance.server.private_ip + description = "The private IP address of the main server instance." + + depends_on = [ + # Security group rule must be created before this IP address could + # actually be used, otherwise the services will be unreachable. + aws_security_group_rule.local_access, + ] +} +``` + +The `depends_on` argument should be used only as a last resort. When using it, +always include a comment explaining why it is being used, to help future +maintainers understand the purpose of the additional dependency. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/configuration/variables.html.md b/vendor/github.com/hashicorp/terraform/website/docs/language/values/variables.html.md similarity index 75% rename from vendor/github.com/hashicorp/terraform/website/docs/configuration/variables.html.md rename to vendor/github.com/hashicorp/terraform/website/docs/language/values/variables.html.md index 239e1a2d..3c167eb1 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/configuration/variables.html.md +++ b/vendor/github.com/hashicorp/terraform/website/docs/language/values/variables.html.md @@ -1,5 +1,5 @@ --- -layout: "docs" +layout: "language" page_title: "Input Variables - Configuration Language" sidebar_current: "docs-config-variables" description: |- @@ -9,11 +9,7 @@ description: |- # Input Variables --> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and -earlier, see -[0.11 Configuration Language: Input Variables](../configuration-0-11/variables.html). - -> **Hands-on:** Try the [Define Input Variables](https://learn.hashicorp.com/tutorials/terraform/aws-variables?in=terraform/aws-get-started&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. +> **Hands-on:** Try the [Customize Terraform Configuration with Variables](https://learn.hashicorp.com/tutorials/terraform/variables?in=terraform/configuration-language&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. Input variables serve as parameters for a Terraform module, allowing aspects of the module to be customized without altering the module's own source code, @@ -21,7 +17,7 @@ and allowing modules to be shared between different configurations. When you declare variables in the root module of your configuration, you can set their values using CLI options and environment variables. -When you declare them in [child modules](./modules.html), +When you declare them in [child modules](/docs/language/modules/index.html), the calling module should pass values in the `module` block. If you're familiar with traditional programming languages, it can be useful to @@ -36,7 +32,7 @@ compare Terraform modules to function definitions: variable is being discussed. Other kinds of variables in Terraform include _environment variables_ (set by the shell where Terraform runs) and _expression variables_ (used to indirectly represent a value in an -[expression](./expressions.html)). +[expression](/docs/language/expressions/index.html)). ## Declaring an Input Variable @@ -74,11 +70,11 @@ be unique among all variables in the same module. This name is used to assign a value to the variable from outside and to reference the variable's value from within the module. -The name of a variable can be any valid [identifier](./syntax.html#identifiers) +The name of a variable can be any valid [identifier](/docs/language/syntax/configuration.html#identifiers) _except_ the following: `source`, `version`, `providers`, `count`, `for_each`, `lifecycle`, `depends_on`, `locals`. These names are reserved for meta-arguments in -[module configuration blocks](./modules.html), and cannot be +[module configuration blocks](/docs/language/modules/syntax.html), and cannot be declared as variable names. ## Arguments @@ -106,12 +102,12 @@ configuration. [inpage-type]: #type-constraints The `type` argument in a `variable` block allows you to restrict the -[type of value](./expressions.html#types-and-values) that will be accepted as +[type of value](/docs/language/expressions/types.html) that will be accepted as the value for a variable. If no type constraint is set then a value of any type is accepted. While type constraints are optional, we recommend specifying them; they -serve as easy reminders for users of the module, and +can serve as helpful reminders for users of the module, and they allow Terraform to return a helpful error message if the wrong type is used. Type constraints are created from a mixture of type keywords and type @@ -133,7 +129,7 @@ collections: The keyword `any` may be used to indicate that any type is acceptable. For more information on the meaning and behavior of these different types, as well as detailed information about automatic conversion of complex types, see -[Type Constraints](./types.html). +[Type Constraints](/docs/language/expressions/types.html). If both the `type` and `default` arguments are specified, the given default value must be convertible to the specified type. @@ -187,7 +183,7 @@ The expression can refer only to the variable that the condition applies to, and _must not_ produce errors. If the failure of an expression is the basis of the validation decision, use -[the `can` function](./functions/can.html) to detect such errors. For example: +[the `can` function](/docs/language/functions/can.html) to detect such errors. For example: ```hcl variable "image_id" { @@ -211,15 +207,20 @@ using a sentence structure similar to the above examples. [inpage-sensitive]: #suppressing-values-in-cli-output --> This feature was introduced in Terraform CLI v0.14.0. +-> This feature was introduced in Terraform v0.14.0. -Setting a variable as `sensitive` prevents Terraform from showing its value in the `plan` or `apply` output, when that variable is used within a configuration. +> **Hands-on:** Try the [Protect Sensitive Input Variables](https://learn.hashicorp.com/tutorials/terraform/sensitive-variables?in=terraform/configuration-language&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. -Sensitive values are still recorded in the [state](/docs/state/index.html), and so will be visible to anyone who is able to access the state data. For more information, see [_Sensitive Data in State_](/docs/state/sensitive-data.html). +Setting a variable as `sensitive` prevents Terraform from showing its value in +the `plan` or `apply` output, when you use that variable elsewhere in your +configuration. -A provider can define [an attribute as sensitive](/docs/extend/best-practices/sensitive-state.html#using-the-sensitive-flag), which prevents the value of that attribute from being displayed in logs or regular output. The `sensitive` argument on variables allows users to replicate this behavior for values in their configuration, by defining a variable as `sensitive`. +Terraform will still record sensitive values in the [state](/docs/language/state/index.html), +and so anyone who can access the state data will have access to the sensitive +values in cleartext. For more information, see +[_Sensitive Data in State_](/docs/language/state/sensitive-data.html). -Define a variable as sensitive by setting the `sensitive` argument to `true`: +Declare a variable as sensitive by setting the `sensitive` argument to `true`: ``` variable "user_information" { @@ -236,7 +237,9 @@ resource "some_resource" "a" { } ``` -Using this variable throughout your configuration will obfuscate the value from display in `plan` or `apply` output: +Any expressions whose result depends on the sensitive variable will be treated +as sensitive themselves, and so in the above example the two arguments of +`resource "some_resource" "a"` will also be hidden in the plan output: ``` Terraform will perform the following actions: @@ -250,22 +253,12 @@ Terraform will perform the following actions: Plan: 1 to add, 0 to change, 0 to destroy. ``` -In some cases where a sensitive variable is used in a nested block, the whole block can be redacted. This happens with resources which can have multiple blocks of the same type, where the values must be unique. This looks like: +In some cases where you use a sensitive variable inside a nested block, Terraform +may treat the entire block as redacted. This happens for resource types where +all of the blocks of a particular type are required to be unique, and so +disclosing the content of one block might imply the content of a sibling block. ``` -# main.tf - -resource "some_resource" "a" { - nested_block { - user_information = var.user_information # a sensitive variable - other_information = "not sensitive data" - } -} - -# CLI output - -Terraform will perform the following actions: - # some_resource.a will be updated in-place ~ resource "some_resource" "a" { ~ nested_block { @@ -273,9 +266,19 @@ Terraform will perform the following actions: # so its contents will not be displayed. } } - ``` +A provider can also +[declare an attribute as sensitive](/docs/extend/best-practices/sensitive-state.html#using-the-sensitive-flag), +which will cause Terraform to hide it from regular output regardless of how +you assign it a value. For more information, see +[Sensitive Resource Attributes](/docs/language/expressions/references.html#sensitive-resource-attributes). + +If you use a sensitive value from as part of an +[output value](/docs/language/values/outputs.html) then Terraform will require +you to also mark the output value itself as sensitive, to confirm that you +intended to export it. + #### Cases where Terraform may disclose a sensitive variable A `sensitive` variable is a configuration-centered concept, and values are sent to providers without any obfuscation. A provider error could disclose a value if that value is included in the error message. For example, a provider might return the following error even if "foo" is a sensitive value: `"Invalid value 'foo' for field"` @@ -302,7 +305,7 @@ random_pet.animal: Creation complete after 0s [id=jae-known-mongoose] ## Using Input Variable Values Within the module that declared a variable, its value can be accessed from -within [expressions](./expressions.html) as `var.`, +within [expressions](/docs/language/expressions/index.html) as `var.`, where `` matches the label given in the declaration block: -> **Note:** Input variables are _created_ by a `variable` block, but you @@ -332,7 +335,7 @@ can be set in a number of ways: The following sections describe these options in more detail. This section does not apply to _child_ modules, where values for input variables are instead assigned in the configuration of their parent module, as described in -[_Modules_](./modules.html). +[_Modules_](/docs/language/modules/index.html). ### Variables on the Command Line @@ -341,12 +344,14 @@ when running the `terraform plan` and `terraform apply` commands: ``` terraform apply -var="image_id=ami-abc123" -terraform apply -var='image_id_list=["ami-abc123","ami-def456"]' +terraform apply -var='image_id_list=["ami-abc123","ami-def456"]' -var="instance_type=t2.micro" terraform apply -var='image_id_map={"us-east-1":"ami-abc123","us-east-2":"ami-def456"}' ``` The `-var` option can be used any number of times in a single command. + + ### Variable Definitions (`.tfvars`) Files To set lots of variables, it is more convenient to specify their values in @@ -411,9 +416,10 @@ and lower case letters as in the above example. ### Complex-typed Values -When variable values are provided in a variable definitions file, Terraform's -[usual syntax](./expressions.html#structural-types) can be used to assign -complex-typed values, like lists and maps. +When variable values are provided in a variable definitions file, you can use +Terraform's usual syntax for +[literal expressions](/docs/language/expressions/types.html#literal-expressions) +to assign complex-typed values, like lists and maps. Some special rules apply to the `-var` command line option and to environment variables. For convenience, Terraform defaults to interpreting `-var` and @@ -436,6 +442,44 @@ $ export TF_VAR_availability_zone_names='["us-west-1b","us-west-1d"]' For readability, and to avoid the need to worry about shell escaping, we recommend always setting complex variable values via variable definitions files. +### Values for Undeclared Variables + +If you have defined a variable value, but not its corresponding `variable {}` +definition, you may get an error or warning depending on how you have provided +that value. + +If you provide values for undeclared variables defined as [environment variables](#environment-variables) +you will not get an error or warning. This is because environment variables may +be declared but not used in all configurations that might be run. + +If you provide values for undeclared variables defined [in a file](#variable-definitions-tfvars-files) +you will get a warning. This is to help in cases where you have provided a variable +value _meant_ for a variable declaration, but perhaps there is a mistake in the +value definition. For example, the following configuration: + +```terraform +variable "moose" { + type = string +} +``` + +And the following `.tfvars` file: + +```hcl +mosse = "Moose" +``` + +Will cause Terraform to warn you that there is no variable declared `"mosse"`, which can help +you spot this mistake. + +If you use `.tfvars` files across multiple configurations and expect to continue to see this warning, +you can use the [`-compact-warnings`](https://www.terraform.io/docs/cli/commands/plan.html#compact-warnings) +option to simplify your output. + +If you provide values for undeclared variables on the [command line](#variables-on-the-command-line), +Terraform will error. To avoid this error, either declare a variable block for the value, or remove +the variable value from your Terraform call. + ### Variable Definition Precedence The above mechanisms for setting variables can be used together in any diff --git a/vendor/github.com/hashicorp/terraform/website/docs/modules/composition.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/modules/composition.html.markdown deleted file mode 100644 index 3a17deca..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/modules/composition.html.markdown +++ /dev/null @@ -1,361 +0,0 @@ ---- -layout: "docs" -page_title: "Module Composition" -sidebar_current: "docs-modules-composition" -description: |- - Module composition allows infrastructure to be described from modular - building blocks. ---- - -# Module Composition - --> This section is written for **Terraform v0.12 or later**. The general patterns - described in this section _do_ apply to earlier versions, but the examples - shown are using v0.12-only syntax and features. For general information - on module usage in prior versions, see - [the v0.11 documentation about modules](/docs/configuration-0-11/modules.html). - -In a simple Terraform configuration with only one root module, we create a -flat set of resources and use Terraform's expression syntax to describe the -relationships between these resources: - -```hcl -resource "aws_vpc" "example" { - cidr_block = "10.1.0.0/16" -} - -resource "aws_subnet" "example" { - vpc_id = aws_vpc.example.id - - availability_zone = "us-west-2b" - cidr_block = cidrsubnet(aws_vpc.example.cidr_block, 4, 1) -} -``` - -When we introduce `module` blocks, our configuration becomes hierarchical -rather than flat: each module contains its own set of resources, and possibly -its own child modules, which can potentially create a deep, complex tree of -resource configurations. - -However, in most cases we strongly recommend keeping the module tree flat, -with only one level of child modules, and use a technique similar to the -above of using expressions to describe the relationships between the modules: - -```hcl -module "network" { - source = "./modules/aws-network" - - base_cidr_block = "10.0.0.0/8" -} - -module "consul_cluster" { - source = "./modules/aws-consul-cluster" - - vpc_id = module.network.vpc_id - subnet_ids = module.network.subnet_ids -} -``` - -We call this flat style of module usage _module composition_, because it -takes multiple [composable](https://en.wikipedia.org/wiki/Composability) -building-block modules and assembles them together to produce a larger system. -Instead of a module _embedding_ its dependencies, creating and managing its -own copy, the module _receives_ its dependencies from the root module, which -can therefore connect the same modules in different ways to produce different -results. - -The rest of this page discusses some more specific composition patterns that -may be useful when describing larger systems with Terraform. - -## Dependency Inversion - -In the example above, we saw a `consul_cluster` module that presumably describes -a cluster of [HashiCorp Consul](https://www.consul.io/) servers running in -an AWS VPC network, and thus it requires as arguments the identifiers of both -the VPC itself and of the subnets within that VPC. - -An alternative design would be to have the `consul_cluster` module describe -its _own_ network resources, but if we did that then it would be hard for -the Consul cluster to coexist with other infrastructure in the same network, -and so where possible we prefer to keep modules relatively small and pass in -their dependencies. - -This [dependency inversion](https://en.wikipedia.org/wiki/Dependency_inversion_principle) -approach also improves flexibility for future -refactoring, because the `consul_cluster` module doesn't know or care how -those identifiers are obtained by the calling module. A future refactor may -separate the network creation into its own configuration, and thus we may -pass those values into the module from data sources instead: - -```hcl -data "aws_vpc" "main" { - tags = { - Environment = "production" - } -} - -data "aws_subnet_ids" "main" { - vpc_id = data.aws_vpc.main.id -} - -module "consul_cluster" { - source = "./modules/aws-consul-cluster" - - vpc_id = data.aws_vpc.main.id - subnet_ids = data.aws_subnet_ids.main.ids -} -``` - -### Conditional Creation of Objects - -In situations where the same module is used across multiple environments, -it's common to see that some necessary object already exists in some -environments but needs to be created in other environments. - -For example, this can arise in development environment scenarios: for cost -reasons, certain infrastructure may be shared across multiple development -environments, while in production the infrastructure is unique and managed -directly by the production configuration. - -Rather than trying to write a module that itself tries to detect whether something -exists and create it if not, we recommend applying the dependency inversion -approach: making the module accept the object it needs as an argument, via -an input variable. - -For example, consider a situation where a Terraform module deploys compute -instances based on a disk image, and in some environments there is a -specialized disk image available while other environments share a common -base disk image. Rather than having the module itself handle both of these -scenarios, we can instead declare an input variable for an object representing -the disk image. Using AWS EC2 as an example, we might declare a common subtype -of the `aws_ami` resource type and data source schemas: - -```hcl -variable "ami" { - type = object({ - # Declare an object using only the subset of attributes the module - # needs. Terraform will allow any object that has at least these - # attributes. - id = string - architecture = string - }) -} -``` - -The caller of this module can now itself directly represent whether this is -an AMI to be created inline or an AMI to be retrieved from elsewhere: - -```hcl -# In situations where the AMI will be directly managed: - -resource "aws_ami_copy" "example" { - name = "local-copy-of-ami" - source_ami_id = "ami-abc123" - source_ami_region = "eu-west-1" -} - -module "example" { - source = "./modules/example" - - ami = aws_ami_copy.example -} -``` - -```hcl -# Or, in situations where the AMI already exists: - -data "aws_ami" "example" { - owner = "9999933333" - - tags = { - application = "example-app" - environment = "dev" - } -} - -module "example" { - source = "./modules/example" - - ami = data.aws_ami.example -} -``` - -This is consistent with Terraform's declarative style: rather than creating -modules with complex conditional branches, we directly describe what -should already exist and what we want Terraform to manage itself. - -By following this pattern, we can be explicit about in which situations we -expect the AMI to already be present and which we don't. A future reader -of the configuration can then directly understand what it is intending to do -without first needing to inspect the state of the remote system. - -In the above example, the object to be created or read is simple enough to -be given inline as a single resource, but we can also compose together multiple -modules as described elsewhere on this page in situations where the -dependencies themselves are complicated enough to benefit from abstractions. - -## Multi-cloud Abstractions - -Terraform itself intentionally does not attempt to abstract over similar -services offered by different vendors, because we want to expose the full -functionality in each offering and yet unifying multiple offerings behind a -single interface will tend to require a "lowest common denominator" approach. - -However, through composition of Terraform modules it is possible to create -your own lightweight multi-cloud abstractions by making your own tradeoffs -about which platform features are important to you. - -Opportunities for such abstractions arise in any situation where multiple -vendors implement the same concept, protocol, or open standard. For example, -the basic capabilities of the domain name system are common across all vendors, -and although some vendors differentiate themselves with unique features such -as geolocation and smart load balancing, you may conclude that in your use-case -you are willing to eschew those features in return for creating modules that -abstract the common DNS concepts across multiple vendors: - -```hcl -module "webserver" { - source = "./modules/webserver" -} - -locals { - fixed_recordsets = [ - { - name = "www" - type = "CNAME" - ttl = 3600 - records = [ - "webserver01", - "webserver02", - "webserver03", - ] - }, - ] - server_recordsets = [ - for i, addr in module.webserver.public_ip_addrs : { - name = format("webserver%02d", i) - type = "A" - records = [addr] - } - ] -} - -module "dns_records" { - source = "./modules/route53-dns-records" - - route53_zone_id = var.route53_zone_id - recordsets = concat(local.fixed_recordsets, local.server_recordsets) -} -``` - -In the above example, we've created a lightweight abstraction in the form of -a "recordset" object. This contains the attributes that describe the general -idea of a DNS recordset that should be mappable onto any DNS provider. - -We then instantiate one specific _implementation_ of that abstraction as a -module, in this case deploying our recordsets to Amazon Route53. - -If we later wanted to switch to a different DNS provider, we'd need only to -replace the `dns_records` module with a new implementation targeting that -provider, and all of the configuration that _produces_ the recordset -definitions can remain unchanged. - -We can create lightweight abstractions like these by defining Terraform object -types representing the concepts involved and then using these object types -for module input variables. In this case, all of our "DNS records" -implementations would have the following variable declared: - -```hcl -variable "recordsets" { - type = list(object({ - name = string - type = string - ttl = number - records = list(string) - })) -} -``` - -While DNS serves as a simple example, there are many more opportunities to -exploit common elements across vendors. A more complex example is Kubernetes, -where there are now many different vendors offering hosted Kubernetes clusters -and even more ways to run Kubernetes yourself. - -If the common functionality across all of these implementations is sufficient -for your needs, you may choose to implement a set of different modules that -describe a particular Kubernetes cluster implementation and all have the common -trait of exporting the hostname of the cluster as an output value: - -```hcl -output "hostname" { - value = azurerm_kubernetes_cluster.main.fqdn -} -``` - -You can then write _other_ modules that expect only a Kubernetes cluster -hostname as input and use them interchangeably with any of your Kubernetes -cluster modules: - -```hcl -module "k8s_cluster" { - source = "modules/azurerm-k8s-cluster" - - # (Azure-specific configuration arguments) -} - -module "monitoring_tools" { - source = "modules/monitoring_tools" - - cluster_hostname = module.k8s_cluster.hostname -} -``` - -## Data-only Modules - -Most modules contain `resource` blocks and thus describe infrastructure to be -created and managed. It may sometimes be useful to write modules that do not -describe any new infrastructure at all, but merely retrieve information about -existing infrastructure that was created elsewhere using -[data sources](/docs/configuration/data-sources.html). - -As with conventional modules, we suggest using this technique only when the -module raises the level of abstraction in some way, in this case by -encapsulating exactly how the data is retrieved. - -A common use of this technique is when a system has been decomposed into several -subsystem configurations but there is certain infrastructure that is shared -across all of the subsystems, such as a common IP network. In this situation, -we might write a shared module called `join-network-aws` which can be called -by any configuration that needs information about the shared network when -deployed in AWS: - -``` -module "network" { - source = "./modules/join-network-aws" - - environment = "production" -} - -module "k8s_cluster" { - source = "./modules/aws-k8s-cluster" - - subnet_ids = module.network.aws_subnet_ids -} -``` - -The `network` module itself could retrieve this data in a number of different -ways: it could query the AWS API directly using -[`aws_vpc`](/docs/providers/aws/d/vpc.html) -and -[`aws_subnet_ids`](/docs/providers/aws/d/subnet_ids.html) -data sources, or it could read saved information from a Consul cluster using -[`consul_keys`](https://www.terraform.io/docs/providers/consul/d/keys.html), -or it might read the outputs directly from the state of the configuration that -manages the network using -[`terraform_remote_state`](https://www.terraform.io/docs/providers/terraform/d/remote_state.html). - -The key benefit of this approach is that the source of this information can -change over time without updating every configuration that depends on it. -Furthermore, if you design your data-only module with a similar set of outputs -as a corresponding management module, you can swap between the two relatively -easily when refactoring. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/modules/index.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/modules/index.html.markdown deleted file mode 100644 index c677d1fb..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/modules/index.html.markdown +++ /dev/null @@ -1,196 +0,0 @@ ---- -layout: "docs" -page_title: "Creating Modules" -sidebar_current: "docs-modules" -description: |- - A module is a container for multiple resources that are used together. ---- - -# Creating Modules - -> **Hands-on:** Try the [Reuse Configuration with Modules](https://learn.hashicorp.com/collections/terraform/modules?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. - -A _module_ is a container for multiple resources that are used together. -Modules can be used to create lightweight abstractions, so that you can -describe your infrastructure in terms of its architecture, rather than -directly in terms of physical objects. - -The `.tf` files in your working directory when you run [`terraform plan`](/docs/commands/plan.html) -or [`terraform apply`](/docs/commands/apply.html) together form the _root_ -module. That module may [call other modules](/docs/configuration/modules.html#calling-a-child-module) -and connect them together by passing output values from one to input values -of another. - -To learn how to _use_ modules, see [the Modules configuration section](/docs/configuration/modules.html). -This section is about _creating_ re-usable modules that other configurations -can include using `module` blocks. - -## Module structure - -Re-usable modules are defined using all of the same -[configuration language](/docs/configuration/) concepts we use in root modules. -Most commonly, modules use: - -* [Input variables](/docs/configuration/variables.html) to accept values from - the calling module. -* [Output values](/docs/configuration/outputs.html) to return results to the - calling module, which it can then use to populate arguments elsewhere. -* [Resources](/docs/configuration/resources.html) to define one or more - infrastructure objects that the module will manage. - -To define a module, create a new directory for it and place one or more `.tf` -files inside just as you would do for a root module. Terraform can load modules -either from local relative paths or from remote repositories; if a module will -be re-used by lots of configurations you may wish to place it in its own -version control repository. - -Modules can also call other modules using a `module` block, but we recommend -keeping the module tree relatively flat and using [module composition](./composition.html) -as an alternative to a deeply-nested tree of modules, because this makes -the individual modules easier to re-use in different combinations. - -## When to write a module - -In principle any combination of resources and other constructs can be factored -out into a module, but over-using modules can make your overall Terraform -configuration harder to understand and maintain, so we recommend moderation. - -A good module should raise the level of abstraction by describing a new concept -in your architecture that is constructed from resource types offered by -providers. - -For example, `aws_instance` and `aws_elb` are both resource types belonging to -the AWS provider. You might use a module to represent the higher-level concept -"[HashiCorp Consul](https://www.consul.io/) cluster running in AWS" which -happens to be constructed from these and other AWS provider resources. - -We _do not_ recommend writing modules that are just thin wrappers around single -other resource types. If you have trouble finding a name for your module that -isn't the same as the main resource type inside it, that may be a sign that -your module is not creating any new abstraction and so the module is -adding unnecessary complexity. Just use the resource type directly in the -calling module instead. - -## Standard Module Structure - -The standard module structure is a file and directory layout we recommend for -reusable modules distributed in separate repositories. Terraform tooling is -built to understand the standard module structure and use that structure to -generate documentation, index modules for the module registry, and more. - -The standard module structure expects the layout documented below. The list may -appear long, but everything is optional except for the root module. Most modules -don't need to do any extra work to follow the standard structure. - -* **Root module**. This is the **only required element** for the standard - module structure. Terraform files must exist in the root directory of - the repository. This should be the primary entrypoint for the module and is - expected to be opinionated. For the - [Consul module](https://registry.terraform.io/modules/hashicorp/consul) - the root module sets up a complete Consul cluster. It makes a lot of assumptions - however, and we expect that advanced users will use specific _nested modules_ - to more carefully control what they want. - -* **README**. The root module and any nested modules should have README - files. This file should be named `README` or `README.md`. The latter will - be treated as markdown. There should be a description of the module and - what it should be used for. If you want to include an example for how this - module can be used in combination with other resources, put it in an [examples - directory like this](https://github.com/hashicorp/terraform-aws-consul/tree/master/examples). - Consider including a visual diagram depicting the infrastructure resources - the module may create and their relationship. - - The README doesn't need to document inputs or outputs of the module because - tooling will automatically generate this. If you are linking to a file or - embedding an image contained in the repository itself, use a commit-specific - absolute URL so the link won't point to the wrong version of a resource in the - future. - -* **LICENSE**. The license under which this module is available. If you are - publishing a module publicly, many organizations will not adopt a module - unless a clear license is present. We recommend always having a license - file, even if it is not an open source license. - -* **`main.tf`, `variables.tf`, `outputs.tf`**. These are the recommended filenames for - a minimal module, even if they're empty. `main.tf` should be the primary - entrypoint. For a simple module, this may be where all the resources are - created. For a complex module, resource creation may be split into multiple - files but any nested module calls should be in the main file. `variables.tf` - and `outputs.tf` should contain the declarations for variables and outputs, - respectively. - -* **Variables and outputs should have descriptions.** All variables and - outputs should have one or two sentence descriptions that explain their - purpose. This is used for documentation. See the documentation for - [variable configuration](/docs/configuration/variables.html) and - [output configuration](/docs/configuration/outputs.html) for more details. - -* **Nested modules**. Nested modules should exist under the `modules/` - subdirectory. Any nested module with a `README.md` is considered usable - by an external user. If a README doesn't exist, it is considered for internal - use only. These are purely advisory; Terraform will not actively deny usage - of internal modules. Nested modules should be used to split complex behavior - into multiple small modules that advanced users can carefully pick and - choose. For example, the - [Consul module](https://registry.terraform.io/modules/hashicorp/consul) - has a nested module for creating the Cluster that is separate from the - module to setup necessary IAM policies. This allows a user to bring in their - own IAM policy choices. - - If the root module includes calls to nested modules, they should use relative - paths like `./modules/consul-cluster` so that Terraform will consider them - to be part of the same repository or package, rather than downloading them - again separately. - - If a repository or package contains multiple nested modules, they should - ideally be [composable](./composition.html) by the caller, rather than - calling directly to each other and creating a deeply-nested tree of modules. - -* **Examples**. Examples of using the module should exist under the - `examples/` subdirectory at the root of the repository. Each example may have - a README to explain the goal and usage of the example. Examples for - submodules should also be placed in the root `examples/` directory. - - Because examples will often be copied into other repositories for - customization, any `module` blocks should have their `source` set to the - address an external caller would use, not to a relative path. - -A minimal recommended module following the standard structure is shown below. -While the root module is the only required element, we recommend the structure -below as the minimum: - -```sh -$ tree minimal-module/ -. -├── README.md -├── main.tf -├── variables.tf -├── outputs.tf -``` - -A complete example of a module following the standard structure is shown below. -This example includes all optional elements and is therefore the most -complex a module can become: - -```sh -$ tree complete-module/ -. -├── README.md -├── main.tf -├── variables.tf -├── outputs.tf -├── ... -├── modules/ -│   ├── nestedA/ -│   │   ├── README.md -│   │   ├── variables.tf -│   │   ├── main.tf -│   │   ├── outputs.tf -│   ├── nestedB/ -│   ├── .../ -├── examples/ -│   ├── exampleA/ -│   │   ├── main.tf -│   ├── exampleB/ -│   ├── .../ -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/modules/publish.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/modules/publish.html.markdown deleted file mode 100644 index 1768b09d..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/modules/publish.html.markdown +++ /dev/null @@ -1,42 +0,0 @@ ---- -layout: "docs" -page_title: "Publishing Modules" -sidebar_current: "docs-modules-publish" -description: |- - A module is a container for multiple resources that are used together. ---- - -# Publishing Modules - -If you've built a module that you intend to be reused, we recommend -[publishing the module](/docs/registry/modules/publish.html) on the -[Terraform Registry](https://registry.terraform.io). This will version -your module, generate documentation, and more. - -Published modules can be easily consumed by Terraform, and users can -[constrain module versions](/docs/configuration/modules.html#module-versions) -for safe and predictable updates. The following example shows how a caller -might use a module from the Terraform Registry: - -```hcl -module "consul" { - source = "hashicorp/consul/aws" -} -``` - -If you do not wish to publish your modules in the public registry, you can -instead use a [private registry](/docs/registry/private.html) to get -the same benefits. - -## Distribution via other sources - -Although the registry is the native mechanism for distributing re-usable -modules, Terraform can also install modules from -[various other sources](/docs/modules/sources.html). The alternative sources -do not support the first-class versioning mechanism, but some sources have -their own mechanisms for selecting particular VCS commits, etc. - -We recommend that modules distributed via other protocols still use the -[standard module structure](./#standard-module-structure) so that it can -be used in a similar way to a registry module, or even _become_ a registry -module at a later time. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/modules/sources.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/modules/sources.html.markdown deleted file mode 100644 index 020fe09b..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/modules/sources.html.markdown +++ /dev/null @@ -1,435 +0,0 @@ ---- -layout: "docs" -page_title: "Module Sources" -sidebar_current: "docs-modules-sources" -description: The source argument within a module block specifies the location of the source code of a child module. ---- - -# Module Sources - -The `source` argument in [a `module` block](/docs/configuration/modules.html) -tells Terraform where to find the source code for the desired child module. - -Terraform uses this during the module installation step of `terraform init` -to download the source code to a directory on local disk so that it can be -used by other Terraform commands. - -The module installer supports installation from a number of different source -types, as listed below. - - * [Local paths](#local-paths) - - * [Terraform Registry](#terraform-registry) - - * [GitHub](#github) - - * [Bitbucket](#bitbucket) - - * Generic [Git](#generic-git-repository), [Mercurial](#generic-mercurial-repository) repositories - - * [HTTP URLs](#http-urls) - - * [S3 buckets](#s3-bucket) - - * [GCS buckets](#gcs-bucket) - -Each of these is described in the following sections. Module source addresses -use a _URL-like_ syntax, but with extensions to support unambiguous selection -of sources and additional features. - -We recommend using local file paths for closely-related modules used primarily -for the purpose of factoring out repeated code elements, and using a native -Terraform module registry for modules intended to be shared by multiple calling -configurations. We support other sources so that you can potentially distribute -Terraform modules internally with existing infrastructure. - -Many of the source types will make use of "ambient" credentials available -when Terraform is run, such as from environment variables or credentials files -in your home directory. This is covered in more detail in each of the following -sections. - -We recommend placing each module that is intended to be re-usable in the root -of its own repository or archive file, but it is also possible to -[reference modules from subdirectories](#modules-in-package-sub-directories). - -## Local Paths - -Local path references allow for factoring out portions of a configuration -within a single source repository. - -```hcl -module "consul" { - source = "./consul" -} -``` - -A local path must begin with either `./` or `../` to indicate that a local -path is intended, to distinguish from -[a module registry address](#terraform-registry). - -Local paths are special in that they are not "installed" in the same sense -that other sources are: the files are already present on local disk (possibly -as a result of installing a parent module) and so can just be used directly. -Their source code is automatically updated if the parent module is upgraded. - -## Terraform Registry - -A module registry is the native way of distributing Terraform modules for use -across multiple configurations, using a Terraform-specific protocol that -has full support for module versioning. - -[Terraform Registry](https://registry.terraform.io/) is an index of modules -shared publicly using this protocol. This public registry is the easiest way -to get started with Terraform and find modules created by others in the -community. - -You can also use a -[private registry](/docs/registry/private.html), either -via the built-in feature from Terraform Cloud, or by running a custom -service that implements -[the module registry protocol](/docs/registry/api.html). - -Modules on the public Terraform Registry can be referenced using a registry -source address of the form `//`, with each -module's information page on the registry site including the exact address -to use. - -```hcl -module "consul" { - source = "hashicorp/consul/aws" - version = "0.1.0" -} -``` - -The above example will use the -[Consul module for AWS](https://registry.terraform.io/modules/hashicorp/consul/aws) -from the public registry. - -For modules hosted in other registries, prefix the source address with an -additional `/` portion, giving the hostname of the private registry: - -```hcl -module "consul" { - source = "app.terraform.io/example-corp/k8s-cluster/azurerm" - version = "1.1.0" -} -``` - -If you are using the SaaS version of Terraform Cloud, its private -registry hostname is `app.terraform.io`. If you are using a Terraform Enterprise -instance, its private registry hostname is the same hostname you use to -access the Terraform Cloud application. - -Registry modules support versioning. You can provide a specific version as shown -in the above examples, or use flexible -[version constraints](/docs/configuration/modules.html#module-versions). - -You can learn more about the registry at the -[Terraform Registry documentation](/docs/registry/modules/use.html#using-modules). - -To access modules from a private registry, you may need to configure an access -token [in the CLI config](/docs/commands/cli-config.html#credentials). Use the -same hostname as used in the module source string. For a private registry -within Terraform Cloud, use the same authentication token as you would -use with the Enterprise API or command-line clients. - -## GitHub - -Terraform will recognize unprefixed `github.com` URLs and interpret them -automatically as Git repository sources. - -```hcl -module "consul" { - source = "github.com/hashicorp/example" -} -``` - -The above address scheme will clone over HTTPS. To clone over SSH, use the -following form: - -```hcl -module "consul" { - source = "git@github.com:hashicorp/example.git" -} -``` - -These GitHub schemes are treated as convenient aliases for -[the general Git repository address scheme](#generic-git-repository), and so -they obtain credentials in the same way and support the `ref` argument for -selecting a specific revision. You will need to configure credentials in -particular to access private repositories. - -## Bitbucket - -Terraform will recognize unprefixed `bitbucket.org` URLs and interpret them -automatically as BitBucket repositories: - -```hcl -module "consul" { - source = "bitbucket.org/hashicorp/terraform-consul-aws" -} -``` - -This shorthand works only for public repositories, because Terraform must -access the BitBucket API to learn if the given repository uses Git or Mercurial. - -Terraform treats the result either as [a Git source](#generic-git-repository) -or [a Mercurial source](#generic-mercurial-repository) depending on the -repository type. See the sections on each version control type for information -on how to configure credentials for private repositories and how to specify -a specific revision to install. - -## Generic Git Repository - -Arbitrary Git repositories can be used by prefixing the address with the -special `git::` prefix. After this prefix, any valid -[Git URL](https://git-scm.com/docs/git-clone#_git_urls_a_id_urls_a) -can be specified to select one of the protocols supported by Git. - -For example, to use HTTPS or SSH: - -```hcl -module "vpc" { - source = "git::https://example.com/vpc.git" -} - -module "storage" { - source = "git::ssh://username@example.com/storage.git" -} -``` - -Terraform installs modules from Git repositories by running `git clone`, and -so it will respect any local Git configuration set on your system, including -credentials. To access a non-public Git repository, configure Git with -suitable credentials for that repository. - -If you use the SSH protocol then any configured SSH keys will be used -automatically. This is the most common way to access non-public Git -repositories from automated systems because it is easy to configure -and allows access to private repositories without interactive prompts. - -If using the HTTP/HTTPS protocol, or any other protocol that uses -username/password credentials, configure -[Git Credentials Storage](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage) -to select a suitable source of credentials for your environment. - -If your Terraform configuration will be used within [Terraform Cloud](https://www.hashicorp.com/products/terraform), -only SSH key authentication is supported, and -[keys can be configured on a per-workspace basis](/docs/cloud/workspaces/ssh-keys.html). - -### Selecting a Revision - -By default, Terraform will clone and use the default branch (referenced by -`HEAD`) in the selected repository. You can override this using the -`ref` argument: - -```hcl -module "vpc" { - source = "git::https://example.com/vpc.git?ref=v1.2.0" -} -``` - -The value of the `ref` argument can be any reference that would be accepted -by the `git checkout` command, including branch and tag names. - -### "scp-like" address syntax - -When using Git over SSH, we recommend using the `ssh://`-prefixed URL form -for consistency with all of the other URL-like git address forms. -You may opt to use the alternative "scp-like" syntax instead, in which case you -must omit the `ssh://` scheme part and include only the `git::` part. -For example: - -```hcl -module "storage" { - source = "git::username@example.com:storage.git" -} -``` - - -If you use the `ssh://` URL scheme then Terraform will assume that the colon -marks the beginning of a port number, rather than the beginning of the path. -This matches how Git itself interprets these different forms, aside from -the Terraform-specific `git::` selector prefix. - -## Generic Mercurial Repository - -You can use arbitrary Mercurial repositories by prefixing the address with the -special `hg::` prefix. After this prefix, any valid -[Mercurial URL](https://www.mercurial-scm.org/repo/hg/help/urls) -can be specified to select one of the protocols supported by Mercurial. - -```hcl -module "vpc" { - source = "hg::http://example.com/vpc.hg" -} -``` - -Terraform installs modules from Mercurial repositories by running `hg clone`, and -so it will respect any local Mercurial configuration set on your system, -including credentials. To access a non-public repository, configure Mercurial -with suitable credentials for that repository. - -If you use the SSH protocol then any configured SSH keys will be used -automatically. This is the most common way to access non-public Mercurial -repositories from automated systems because it is easy to configure -and allows access to private repositories without interactive prompts. - -If your Terraform configuration will be used within [Terraform Cloud](https://www.hashicorp.com/products/terraform), -only SSH key authentication is supported, and -[keys can be configured on a per-workspace basis](/docs/cloud/workspaces/ssh-keys.html). - -### Selecting a Revision - -You can select a non-default branch or tag using the optional `ref` argument: - -```hcl -module "vpc" { - source = "hg::http://example.com/vpc.hg?ref=v1.2.0" -} -``` - -## HTTP URLs - -When you use an HTTP or HTTPS URL, Terraform will make a `GET` request to -the given URL, which can return _another_ source address. This indirection -allows using HTTP URLs as a sort of "vanity redirect" over a more complicated -module source address. - -Terraform will append an additional query string argument `terraform-get=1` to -the given URL before sending the `GET` request, allowing the server to -optionally return a different result when Terraform is requesting it. - -If the response is successful (`200`-range status code), Terraform looks in -the following locations in order for the next address to access: - -* The value of a response header field named `X-Terraform-Get`. - -* If the response is an HTML page, a `meta` element with the name `terraform-get`: - - ```html - - ``` - -In either case, the result is interpreted as another module source address -using one of the forms documented elsewhere on this page. - -If an HTTP/HTTPS URL requires authentication credentials, use a `.netrc` -file in your home directory to configure these. For information on this format, -see [the documentation for using it in `curl`](https://ec.haxx.se/usingcurl-netrc.html). - -### Fetching archives over HTTP - -As a special case, if Terraform detects that the URL has a common file -extension associated with an archive file format then it will bypass the -special `terraform-get=1` redirection described above and instead just use -the contents of the referenced archive as the module source code: - -```hcl -module "vpc" { - source = "https://example.com/vpc-module.zip" -} -``` - -The extensions that Terraform recognizes for this special behavior are: - -* `zip` -* `tar.bz2` and `tbz2` -* `tar.gz` and `tgz` -* `tar.xz` and `txz` - -If your URL _doesn't_ have one of these extensions but refers to an archive -anyway, use the `archive` argument to force this interpretation: - -```hcl -module "vpc" { - source = "https://example.com/vpc-module?archive=zip" -} -``` - --> **Note:** If the content of the archive file is a directory, you will need to -include that directory in the module source. Read the section on -[Modules in Package Sub-directories](#modules-in-package-sub-directories) for more -information. - -## S3 Bucket - -You can use archives stored in S3 as module sources using the special `s3::` -prefix, followed by -[a path-style S3 bucket object URL](http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html#access-bucket-intro). - -```hcl -module "consul" { - source = "s3::https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/vpc.zip" -} -``` - --> **Note:** Buckets in AWS's us-east-1 region must use the hostname `s3.amazonaws.com` (instead of `s3-us-east-1.amazonaws.com`). - -The `s3::` prefix causes Terraform to use AWS-style authentication when -accessing the given URL. As a result, this scheme may also work for other -services that mimic the S3 API, as long as they handle authentication in the -same way as AWS. - -The resulting object must be an archive with one of the same file -extensions as for [archives over standard HTTP](#fetching-archives-over-http). -Terraform will extract the archive to obtain the module source tree. - -The module installer looks for AWS credentials in the following locations, -preferring those earlier in the list when multiple are available: - -* The `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables. -* The default profile in the `.aws/credentials` file in your home directory. -* If running on an EC2 instance, temporary credentials associated with the - instance's IAM Instance Profile. - -## GCS Bucket - -You can use archives stored in Google Cloud Storage as module sources using the special `gcs::` -prefix, followed by -[a GCS bucket object URL](https://cloud.google.com/storage/docs/request-endpoints#typical). - -For example - -* `gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH_TO_MODULE` -* `gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH/TO/module.zip` - -```hcl -module "consul" { - source = "gcs::https://www.googleapis.com/storage/v1/modules/foomodule.zip" -} -``` - -The module installer uses Google Cloud SDK to authenticate with GCS. To set credentials you can: - -* Enter the path of your service account key file in the GOOGLE_APPLICATION_CREDENTIALS environment variable, or; -* If you're running Terraform from a GCE instance, default credentials are automatically available. See [Creating and Enabling Service Accounts](https://cloud.google.com/compute/docs/authentication) for Instances for more details -* On your computer, you can make your Google identity available by running `gcloud auth application-default login`. - - -## Modules in Package Sub-directories - -When the source of a module is a version control repository or archive file -(generically, a "package"), the module itself may be in a sub-directory relative -to the root of the package. - -A special double-slash syntax is interpreted by Terraform to indicate that -the remaining path after that point is a sub-directory within the package. -For example: - -* `hashicorp/consul/aws//modules/consul-cluster` -* `git::https://example.com/network.git//modules/vpc` -* `https://example.com/network-module.zip//modules/vpc` -* `s3::https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/network.zip//modules/vpc` - -If the source address has arguments, such as the `ref` argument supported for -the version control sources, the sub-directory portion must be _before_ those -arguments: - -* `git::https://example.com/network.git//modules/vpc?ref=v1.2.0` - -Terraform will still extract the entire package to local disk, but will read -the module from the subdirectory. As a result, it is safe for a module in -a sub-directory of a package to use [a local path](#local-paths) to another -module as long as it is in the _same_ package. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/plugins/basics.html.md b/vendor/github.com/hashicorp/terraform/website/docs/plugins/basics.html.md deleted file mode 100644 index 55103f20..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/plugins/basics.html.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -layout: "docs" -page_title: "Plugin Basics" -sidebar_current: "docs-plugins-basics" -description: |- - This page documents the basics of how the plugin system in Terraform works, and how to setup a basic development environment for plugin development if you're writing a Terraform plugin. ---- - -# Plugin Basics - -~> **Advanced topic!** Plugin development is a highly advanced -topic in Terraform, and is not required knowledge for day-to-day usage. -If you don't plan on writing any plugins, this section of the documentation is -not necessary to read. For general use of Terraform, please see -[Intro to Terraform](/intro/index.html) or the -[Terraform: Get Started](https://learn.hashicorp.com/collections/terraform/aws-get-started?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) -collection on HashiCorp Learn. - -This page documents the basics of how the plugin system in Terraform -works, and how to setup a basic development environment for plugin development -if you're writing a Terraform plugin. - -## How it Works - -Terraform providers and provisioners are provided via plugins. Each plugin -exposes an implementation for a specific service, such as AWS, or provisioner, -such as bash. Plugins are executed as a separate process and communicate with -the main Terraform binary over an RPC interface. - -More details are available in -_[Plugin Internals](/docs/internals/internal-plugins.html)_. - -The code within the binaries must adhere to certain interfaces. -The network communication and RPC is handled automatically by higher-level -Terraform libraries. The exact interface to implement is documented -in its respective documentation section. - -## Installing Plugins - -The [provider plugins distributed by HashiCorp](/docs/providers/index.html) are -automatically installed by `terraform init`. Third-party plugins (both -providers and provisioners) can be manually installed into the user plugins -directory, located at `%APPDATA%\terraform.d\plugins` on Windows and -`~/.terraform.d/plugins` on other systems. - -For more information, see: - -- [Configuring Providers](/docs/configuration/providers.html) -- [Configuring Providers: Third-party Plugins](/docs/configuration/providers.html#third-party-plugins) - -For developer-centric documentation, see: - -- [How Terraform Works: Plugin Discovery](/docs/extend/how-terraform-works.html#discovery) - -## Developing a Plugin - -Developing a plugin is simple. The only knowledge necessary to write -a plugin is basic command-line skills and basic knowledge of the -[Go programming language](http://golang.org). - --> **Note:** A common pitfall is not properly setting up a -$GOPATH. This can lead to strange errors. You can read more about -this [here](https://golang.org/doc/code.html) to familiarize -yourself. - -Create a new Go project somewhere in your `$GOPATH`. If you're a -GitHub user, we recommend creating the project in the directory -`$GOPATH/src/github.com/USERNAME/terraform-NAME`, where `USERNAME` -is your GitHub username and `NAME` is the name of the plugin you're -developing. This structure is what Go expects and simplifies things down -the road. - -The `NAME` should either begin with `provider-` or `provisioner-`, -depending on what kind of plugin it will be. The repository name will, -by default, be the name of the binary produced by `go install` for -your plugin package. - -With the package directory made, create a `main.go` file. This project will -be a binary so the package is "main": - -```golang -package main - -import ( - "github.com/hashicorp/terraform/plugin" -) - -func main() { - plugin.Serve(new(MyPlugin)) -} -``` - -The name `MyPlugin` is a placeholder for the struct type that represents -your plugin's implementation. This must implement either -`terraform.ResourceProvider` or `terraform.ResourceProvisioner`, depending -on the plugin type. - -To test your plugin, the easiest method is to copy your `terraform` binary -to `$GOPATH/bin` and ensure that this copy is the one being used for testing. -`terraform init` will search for plugins within the same directory as the -`terraform` binary, and `$GOPATH/bin` is the directory into which `go install` -will place the plugin executable. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/plugins/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/plugins/index.html.md deleted file mode 100644 index 7970e704..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/plugins/index.html.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -layout: "docs" -page_title: "Plugins" -sidebar_current: "docs-plugins" -description: |- - Terraform is built on a plugin-based architecture. All providers and provisioners that are used in Terraform configurations are plugins, even the core types such as AWS and Heroku. Users of Terraform are able to write new plugins in order to support new functionality in Terraform. ---- - -# Plugins - -Terraform is built on a plugin-based architecture. All providers and -provisioners that are used in Terraform configurations are plugins, even -the core types such as AWS and Heroku. Users of Terraform are able to -write new plugins in order to support new functionality in Terraform. - -This section of the documentation gives a high-level overview of how -to write plugins for Terraform. It does not hold your hand through the -process, however, and expects a relatively high level of understanding -of Go, provider semantics, Unix, etc. - -~> **Advanced topic!** Plugin development is a highly advanced -topic in Terraform, and is not required knowledge for day-to-day usage. -If you don't plan on writing any plugins, we recommend not reading -this section of the documentation. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/plugins/provider.html.md b/vendor/github.com/hashicorp/terraform/website/docs/plugins/provider.html.md deleted file mode 100644 index fef7056d..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/plugins/provider.html.md +++ /dev/null @@ -1,320 +0,0 @@ ---- -layout: "docs" -page_title: "Provider Plugins" -sidebar_current: "docs-plugins-provider" -description: |- - A provider in Terraform is responsible for the lifecycle of a resource: create, read, update, delete. An example of a provider is AWS, which can manage resources of type `aws_instance`, `aws_eip`, `aws_elb`, etc. ---- - -# Provider Plugins - -~> **Advanced topic!** Plugin development is a highly advanced -topic in Terraform, and is not required knowledge for day-to-day usage. -If you don't plan on writing any plugins, this section of the documentation is -not necessary to read. For general use of Terraform, please see -[Intro to Terraform](/intro/index.html) or the -[Terraform: Get Started](https://learn.hashicorp.com/collections/terraform/aws-get-started?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) -collection on HashiCorp Learn. - -> **Hands-on:** Try the [Call APIs with Terraform Providers](https://learn.hashicorp.com/collections/terraform/providers?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. - -A provider in Terraform is responsible for the lifecycle of a resource: -create, read, update, delete. An example of a provider is AWS, which -can manage resources of type `aws_instance`, `aws_eip`, `aws_elb`, etc. - -The primary reasons to care about provider plugins are: - - * You want to add a new resource type to an existing provider. - - * You want to write a completely new provider for managing resource - types in a system not yet supported. - - * You want to write a completely new provider for custom, internal - systems such as a private inventory management system. - -If you're interested in provider development, then read on. The remainder -of this page will assume you're familiar with -[plugin basics](/docs/plugins/basics.html) and that you already have -a basic development environment setup. - -## Provider Plugin Codebases - -Provider plugins live outside of the Terraform core codebase in their own -source code repositories. The official set of provider plugins released by -HashiCorp (developed by both HashiCorp staff and community contributors) -all live in repositories in -[the `terraform-providers` organization](https://github.com/terraform-providers) -on GitHub, but third-party plugins can be maintained in any source code -repository. - -When developing a provider plugin, it is recommended to use a common `GOPATH` -that includes both the core Terraform repository and the repositories of any -providers being changed. This makes it easier to use a locally-built -`terraform` executable and a set of locally-built provider plugins together -without further configuration. - -For example, to download both Terraform and the `template` provider into -`GOPATH`: - -``` -$ go get github.com/hashicorp/terraform -$ go get github.com/terraform-providers/terraform-provider-template -``` - -These two packages are both "main" packages that can be built into separate -executables with `go install`: - -``` -$ go install github.com/hashicorp/terraform -$ go install github.com/terraform-providers/terraform-provider-template -``` - -After running the above commands, both Terraform core and the `template` -provider will both be installed in the current `GOPATH` and `$GOPATH/bin` -will contain both `terraform` and `terraform-provider-template` executables. -This `terraform` executable will find and use the `template` provider plugin -alongside it in the `bin` directory in preference to downloading and installing -an official release. - -When constructing a new provider from scratch, it's recommended to follow -a similar repository structure as for the existing providers, with the main -package in the repository root and a library package in a subdirectory named -after the provider. For more information, see -[Writing Custom Providers](/docs/extend/writing-custom-providers.html) in the -[Extending Terraform section](/docs/extend/index.html). - -When making changes only to files within the provider repository, it is _not_ -necessary to re-build the main Terraform executable. Note that some packages -from the Terraform repository are used as library dependencies by providers, -such as `github.com/hashicorp/terraform/helper/schema`; it is recommended to -use `govendor` to create a local vendor copy of the relevant packages in the -provider repository, as can be seen in the repositories within the -`terraform-providers` GitHub organization. - -## Low-Level Interface - -The interface you must implement for providers is -[ResourceProvider](https://github.com/hashicorp/terraform/blob/master/terraform/resource_provider.go). - -This interface is extremely low level, however, and we don't recommend -you implement it directly. Implementing the interface directly is error -prone, complicated, and difficult. - -Instead, we've developed some higher level libraries to help you out -with developing providers. These are the same libraries we use in our -own core providers. - -## helper/schema - -The `helper/schema` library is a framework we've built to make creating -providers extremely easy. This is the same library we use to build most -of the core providers. - -To give you an idea of how productive you can become with this framework: -we implemented the Google Cloud provider in about 6 hours of coding work. -This isn't a simple provider, and we did have knowledge of -the framework beforehand, but it goes to show how expressive the framework -can be. - -The GoDoc for `helper/schema` can be -[found here](https://godoc.org/github.com/hashicorp/terraform/helper/schema). -This is API-level documentation but will be extremely important -for you going forward. - -## Provider - -The first thing to do in your plugin is to create the -[schema.Provider](https://godoc.org/github.com/hashicorp/terraform/helper/schema#Provider) structure. -This structure implements the `ResourceProvider` interface. We -recommend creating this structure in a function to make testing easier -later. Example: - -```golang -func Provider() *schema.Provider { - return &schema.Provider{ - ... - } -} -``` - -Within the `schema.Provider`, you should initialize all the fields. They -are documented within the godoc, but a brief overview is here as well: - - * `Schema` - This is the configuration schema for the provider itself. - You should define any API keys, etc. here. Schemas are covered below. - - * `ResourcesMap` - The map of resources that this provider supports. - All keys are resource names and the values are the - [schema.Resource](https://godoc.org/github.com/hashicorp/terraform/helper/schema#Resource) structures implementing this resource. - - * `ConfigureFunc` - This function callback is used to configure the - provider. This function should do things such as initialize any API - clients, validate API keys, etc. The `interface{}` return value of - this function is the `meta` parameter that will be passed into all - resource [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) - functions. In general, the returned value is a configuration structure - or a client. - -As part of the unit tests, you should call `InternalValidate`. This is used -to verify the structure of the provider and all of the resources, and reports -an error if it is invalid. An example test is shown below: - -```golang -func TestProvider(t *testing.T) { - if err := Provider().(*schema.Provider).InternalValidate(); err != nil { - t.Fatalf("err: %s", err) - } -} -``` - -Having this unit test will catch a lot of beginner mistakes as you build -your provider. - -## Resources - -Next, you'll want to create the resources that the provider can manage. -These resources are put into the `ResourcesMap` field of the provider -structure. Again, we recommend creating functions to instantiate these. -An example is shown below. - -```golang -func resourceComputeAddress() *schema.Resource { - return &schema.Resource { - ... - } -} -``` - -Resources are described using the -[schema.Resource](https://godoc.org/github.com/hashicorp/terraform/helper/schema#Resource) -structure. This structure has the following fields: - - * `Schema` - The configuration schema for this resource. Schemas are - covered in more detail below. - - * `Create`, `Read`, `Update`, and `Delete` - These are the callback - functions that implement CRUD operations for the resource. The only - optional field is `Update`. If your resource doesn't support update, then - you may keep that field nil. - - * `Importer` - If this is non-nil, then this resource is - [importable](/docs/import/importability.html). It is recommended to - implement this. - -The CRUD operations in more detail, along with their contracts: - - * `Create` - This is called to create a new instance of the resource. - Terraform guarantees that an existing ID is not set on the resource - data. That is, you're working with a new resource. Therefore, you are - responsible for calling `SetId` on your `schema.ResourceData` using a - value suitable for your resource. This ensures whatever resource - state you set on `schema.ResourceData` will be persisted in local state. - If you neglect to `SetId`, no resource state will be persisted. - - * `Read` - This is called to resync the local state with the remote state. - Terraform guarantees that an existing ID will be set. This ID should be - used to look up the resource. Any remote data should be updated into - the local data. **No changes to the remote resource are to be made.** - If the resource is no longer present, calling `SetId` - with an empty string will signal its removal. - - * `Update` - This is called to update properties of an existing resource. - Terraform guarantees that an existing ID will be set. Additionally, - the only changed attributes are guaranteed to be those that support - update, as specified by the schema. Be careful to read about partial - states below. - - * `Delete` - This is called to delete the resource. Terraform guarantees - an existing ID will be set. - - * `Exists` - This is called to verify a resource still exists. It is - called prior to `Read`, and lowers the burden of `Read` to be able - to assume the resource exists. `false` should be returned if - the resources is no longer present, which has the same effect - as calling `SetId("")` from `Read` (i.e. removal of the resource data - from state). - -## Schemas - -Both providers and resources require a schema to be specified. The schema -is used to define the structure of the configuration, the types, etc. It is -very important to get correct. - -In both provider and resource, the schema is a `map[string]*schema.Schema`. -The key of this map is the configuration key, and the value is a schema for -the value of that key. - -Schemas are incredibly powerful, so this documentation page won't attempt -to cover the full power of them. Instead, the API docs should be referenced -which cover all available settings. - -We recommend viewing schemas of existing or similar providers to learn -best practices. A good starting place is the -[core Terraform providers](https://github.com/terraform-providers). - -## Resource Data - -The parameter to provider configuration as well as all the CRUD operations -on a resource is a -[schema.ResourceData](https://godoc.org/github.com/hashicorp/terraform/helper/schema#ResourceData). -This structure is used to query configurations as well as to set information -about the resource such as its ID, connection information, and computed -attributes. - -The API documentation covers ResourceData well, as well as the core providers -in Terraform. - -**Partial state** deserves a special mention. Occasionally in Terraform, create or -update operations are not atomic; they can fail halfway through. As an example, -when creating an AWS security group, creating the group may succeed, -but creating all the initial rules may fail. In this case, it is incredibly -important that Terraform record the correct _partial state_ so that a -subsequent `terraform apply` fixes this resource. - -Most of the time, partial state is not required. When it is, it must be -specifically enabled. An example is shown below: - -```golang -func resourceUpdate(d *schema.ResourceData, meta interface{}) error { - // Enable partial state mode - d.Partial(true) - - if d.HasChange("tags") { - // If an error occurs, return with an error, - // we didn't finish updating - if err := updateTags(d, meta); err != nil { - return err - } - - d.SetPartial("tags") - } - - if d.HasChange("name") { - if err := updateName(d, meta); err != nil { - return err - } - - d.SetPartial("name") - } - - // We succeeded, disable partial mode - d.Partial(false) - - return nil -} -``` - -In the example above, it is possible that setting the `tags` succeeds, -but setting the `name` fails. In this scenario, we want to make sure -that only the state of the `tags` is updated. To do this the -`Partial` and `SetPartial` functions are used. - -`Partial` toggles partial-state mode. When disabled, all changes are merged -into the state upon result of the operation. When enabled, only changes -enabled with `SetPartial` are merged in. - -`SetPartial` tells Terraform what state changes to adopt upon completion -of an operation. You should call `SetPartial` with every key that is safe -to merge into the state. The parameter to `SetPartial` is a prefix, so -if you have a nested structure and want to accept the whole thing, -you can just specify the prefix. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/providers/index.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/providers/index.html.markdown index 8ab5cf29..2656c978 100644 --- a/vendor/github.com/hashicorp/terraform/website/docs/providers/index.html.markdown +++ b/vendor/github.com/hashicorp/terraform/website/docs/providers/index.html.markdown @@ -1,223 +1,44 @@ --- -layout: "docs" -page_title: "Providers" +layout: "language" +page_title: "Provider Documentation" sidebar_current: "docs-providers" description: |- - Terraform is used to create, manage, and manipulate infrastructure resources. Examples of resources include physical machines, VMs, network switches, containers, etc. Almost any infrastructure noun can be represented as a resource in Terraform. + Terraform's resources are implemented by provider plugins. The Terraform + Registry is the main directory of publicly available Terraform providers. --- -# Providers - -Terraform is used to create, manage, and update infrastructure resources such -as physical machines, VMs, network switches, containers, and more. Almost any -infrastructure type can be represented as a resource in Terraform. - -A provider is responsible for understanding API interactions and exposing -resources. Most providers configure a specific infrastructure platform (either -cloud or self-hosted). Providers can also offer local utilities for tasks like -generating random numbers for unique resource names. - -## Providers in the Terraform Registry - -The [Terraform Registry](https://registry.terraform.io/browse/providers) -is the main directory of publicly available Terraform providers, and hosts -providers for most major infrastructure platforms. - -Once you've found a provider you want to use, you can require it in your -Terraform configuration and start using the resource types it provides. -Terraform can automatically install providers from the Terraform Registry when -you run `terraform init`. - -- To find providers for the infrastructure platforms you use, browse - [the providers section of the Terraform Registry](https://registry.terraform.io/browse/providers). -- For details about how to use providers in your Terraform configurations, see - [Provider Requirements](../configuration/provider-requirements.html) and - [Provider Configuration](../configuration/providers.html). - -### Provider Documentation +# Provider Documentation Every Terraform provider has its own documentation, describing its resource types and their arguments. -The Terraform Registry is also the main home for provider documentation. -When viewing a provider's page on the Terraform Registry, you can click the -"Documentation" link in the header to browse its documentation. Provider -documentation in the registry is versioned, and you can use the dropdown version -menu in the header to switch which version's documentation you are viewing. - -## Lists of Terraform Providers - -Provider documentation used to be hosted directly on terraform.io, as part of -Terraform's core documentation. Although some provider documentation might still -be hosted here, the Terraform Registry is now the main home for all public -provider docs. (The exception is the built-in -[`terraform` provider](/docs/providers/terraform/index.html) for reading state -data, since it is not available on the Terraform Registry.) +The [Terraform Registry](https://registry.terraform.io/browse/providers) is the +main home for provider documentation. When viewing a provider's page on the +Terraform Registry, you can click the "Documentation" link in the header to +browse its documentation. -As part of the old provider documentation, this section of the site included -categorized lists of all of the providers that could be automatically installed -by older versions of Terraform, plus a supplemental list of community providers -that needed to be manually installed. Many of these providers have already moved -to the Terraform Registry, but we will continue to host these lists for a while -as part of the transition. Links to provider documentation URLs on terraform.io -should still work, but will now redirect to the equivalent page in the Terraform -Registry. +Provider documentation in the Registry is versioned; you can use the version +menu in the header to change which version you're viewing. -Use the navigation to the left to browse the categorized lists, or see the main -list of historical providers below. +Learn more about writing, generating, and rendering provider documentation +in the [provider publishing documentation](/docs/registry/providers/docs.html). -
+## Temporary Provider Documentation +The following providers will be published on the Terraform Registry soon, but +aren't quite ready. Until they're published, their documentation is available at +the links below: -- [ACME](/docs/providers/acme/index.html) -- [Akamai](/docs/providers/akamai/index.html) -- [Alibaba Cloud](/docs/providers/alicloud/index.html) -- [Archive](/docs/providers/archive/index.html) -- [Arukas](/docs/providers/arukas/index.html) -- [Auth0](/docs/providers/auth0/index.html) - [Avi Vantage](/docs/providers/avi/index.html) -- [Aviatrix](/docs/providers/aviatrix/index.html) -- [AWS](/docs/providers/aws/index.html) -- [Azure](/docs/providers/azurerm/index.html) -- [Azure Active Directory](/docs/providers/azuread/index.html) -- [Azure DevOps](/docs/providers/azuredevops/index.html) -- [Azure Stack](/docs/providers/azurestack/index.html) -- [A10 Networks](/docs/providers/vthunder/index.html) -- [BaiduCloud](/docs/providers/baiducloud/index.html) -- [Bitbucket](/docs/providers/bitbucket/index.html) -- [Brightbox](/docs/providers/brightbox/index.html) -- [CenturyLinkCloud](/docs/providers/clc/index.html) -- [Check Point](/docs/providers/checkpoint/index.html) - [Chef](/docs/providers/chef/index.html) -- [CherryServers](/docs/providers/cherryservers/index.html) -- [Circonus](/docs/providers/circonus/index.html) -- [Cisco ASA](/docs/providers/ciscoasa/index.html) -- [Cisco ACI](/docs/providers/aci/index.html) -- [Cisco MSO](/docs/providers/mso/index.html) -- [CloudAMQP](/docs/providers/cloudamqp/index.html) -- [Cloudflare](/docs/providers/cloudflare/index.html) -- [Cloud-init](/docs/providers/cloudinit/index.html) -- [CloudScale.ch](/docs/providers/cloudscale/index.html) -- [CloudStack](/docs/providers/cloudstack/index.html) - [Cobbler](/docs/providers/cobbler/index.html) -- [Cohesity](/docs/providers/cohesity/index.html) -- [Constellix](/docs/providers/constellix/index.html) -- [Consul](/docs/providers/consul/index.html) -- [Datadog](/docs/providers/datadog/index.html) -- [DigitalOcean](/docs/providers/do/index.html) -- [DNS](/docs/providers/dns/index.html) -- [DNSimple](/docs/providers/dnsimple/index.html) -- [DNSMadeEasy](/docs/providers/dme/index.html) -- [Docker](/docs/providers/docker/index.html) -- [Dome9](/docs/providers/dome9/index.html) -- [Dyn](/docs/providers/dyn/index.html) -- [EnterpriseCloud](/docs/providers/ecl/index.html) -- [Exoscale](/docs/providers/exoscale/index.html) -- [External](/docs/providers/external/index.html) -- [F5 BIG-IP](/docs/providers/bigip/index.html) -- [Fastly](/docs/providers/fastly/index.html) -- [FlexibleEngine](/docs/providers/flexibleengine/index.html) -- [FortiOS](/docs/providers/fortios/index.html) - [Genymotion](/docs/providers/genymotion/index.html) -- [GitHub](/docs/providers/github/index.html) -- [GitLab](/docs/providers/gitlab/index.html) -- [Google Cloud Platform](/docs/providers/google/index.html) -- [Grafana](/docs/providers/grafana/index.html) -- [Gridscale](/docs/providers/gridscale) -- [Hedvig](/docs/providers/hedvig/index.html) -- [Helm](/docs/providers/helm/index.html) -- [Heroku](/docs/providers/heroku/index.html) -- [Hetzner Cloud](/docs/providers/hcloud/index.html) -- [HTTP](/docs/providers/http/index.html) -- [HuaweiCloud](/docs/providers/huaweicloud/index.html) -- [HuaweiCloudStack](/docs/providers/huaweicloudstack/index.html) -- [Icinga2](/docs/providers/icinga2/index.html) -- [Ignition](/docs/providers/ignition/index.html) -- [Incapsula](/docs/providers/incapsula/index.html) -- [InfluxDB](/docs/providers/influxdb/index.html) - [Infoblox](/docs/providers/infoblox/index.html) -- [JDCloud](/docs/providers/jdcloud/index.html) -- [KingsoftCloud](/docs/providers/ksyun/index.html) -- [Kubernetes](/docs/providers/kubernetes/index.html) -- [Lacework](/docs/providers/lacework/index.html) -- [LaunchDarkly](/docs/providers/launchdarkly/index.html) -- [Librato](/docs/providers/librato/index.html) -- [Linode](/docs/providers/linode/index.html) -- [Local](/docs/providers/local/index.html) -- [Logentries](/docs/providers/logentries/index.html) -- [LogicMonitor](/docs/providers/logicmonitor/index.html) -- [Mailgun](/docs/providers/mailgun/index.html) -- [MetalCloud](/docs/providers/metalcloud/index.html) -- [MongoDB Atlas](/docs/providers/mongodbatlas/index.html) - [MySQL](/docs/providers/mysql/index.html) -- [Naver Cloud](/docs/providers/ncloud/index.html) -- [Netlify](/docs/providers/netlify/index.html) -- [New Relic](https://registry.terraform.io/providers/newrelic/newrelic/latest/docs) -- [Nomad](/docs/providers/nomad/index.html) -- [NS1](/docs/providers/ns1/index.html) -- [Null](https://registry.terraform.io/providers/hashicorp/null/latest/docs) -- [Nutanix](/docs/providers/nutanix/index.html) -- [1&1](/docs/providers/oneandone/index.html) -- [Okta](/docs/providers/okta/index.html) -- [Okta Advanced Server Access](/docs/providers/oktaasa/index.html) -- [OpenNebula](/docs/providers/opennebula/index.html) -- [OpenStack](/docs/providers/openstack/index.html) -- [OpenTelekomCloud](/docs/providers/opentelekomcloud/index.html) -- [OpsGenie](/docs/providers/opsgenie/index.html) -- [Oracle Cloud Infrastructure](/docs/providers/oci/index.html) -- [Oracle Cloud Platform](/docs/providers/oraclepaas/index.html) -- [Oracle Public Cloud](/docs/providers/opc/index.html) -- [OVH](/docs/providers/ovh/index.html) -- [Packet](/docs/providers/packet/index.html) -- [PagerDuty](/docs/providers/pagerduty/index.html) -- [Palo Alto Networks PANOS](/docs/providers/panos/index.html) -- [Palo Alto Networks PrismaCloud](/docs/providers/prismacloud/index.html) -- [PostgreSQL](/docs/providers/postgresql/index.html) -- [PowerDNS](/docs/providers/powerdns/index.html) -- [ProfitBricks](/docs/providers/profitbricks/index.html) -- [Pureport](/docs/providers/pureport/index.html) -- [RabbitMQ](/docs/providers/rabbitmq/index.html) -- [Rancher](/docs/providers/rancher/index.html) -- [Rancher2](/docs/providers/rancher2/index.html) -- [Random](https://registry.terraform.io/providers/hashicorp/random/latest/docs) -- [RightScale](/docs/providers/rightscale/index.html) - [Rubrik](/docs/providers/rubrik/index.html) - [Rundeck](/docs/providers/rundeck/index.html) -- [RunScope](/docs/providers/runscope/index.html) -- [Scaleway](/docs/providers/scaleway/index.html) -- [Selectel](/docs/providers/selectel/index.html) -- [SignalFx](/docs/providers/signalfx/index.html) -- [Skytap](/docs/providers/skytap/index.html) -- [SoftLayer](/docs/providers/softlayer/index.html) -- [Spotinst](/docs/providers/spotinst/index.html) -- [StackPath](/docs/providers/stackpath/index.html) -- [StatusCake](/docs/providers/statuscake/index.html) -- [Sumo Logic](/docs/providers/sumologic/index.html) -- [TelefonicaOpenCloud](/docs/providers/telefonicaopencloud/index.html) -- [Template](/docs/providers/template/index.html) -- [TencentCloud](/docs/providers/tencentcloud/index.html) -- [Terraform](/docs/providers/terraform/index.html) -- [Terraform Cloud](/docs/providers/tfe/index.html) -- [Time](/docs/providers/time/index.html) -- [TLS](/docs/providers/tls/index.html) -- [Triton](/docs/providers/triton/index.html) -- [Turbot](/docs/providers/turbot/index.html) -- [UCloud](/docs/providers/ucloud/index.html) -- [UltraDNS](/docs/providers/ultradns/index.html) -- [Vault](/docs/providers/vault/index.html) -- [Venafi](/docs/providers/venafi/index.html) -- [VMware Cloud](/docs/providers/vmc/index.html) -- [VMware NSX-T](/docs/providers/nsxt/index.html) -- [VMware vCloud Director](/docs/providers/vcd/index.html) -- [VMware vRA7](/docs/providers/vra7/index.html) -- [VMware vSphere](/docs/providers/vsphere/index.html) -- [Vultr](/docs/providers/vultr/index.html) -- [Wavefront](/docs/providers/wavefront/index.html) -- [Yandex](/docs/providers/yandex/index.html) - - -
------ +## Useful tools -More providers can be found on our [Community Providers](/docs/providers/type/community-index.html) page. +- [Doc preview tool](https://registry.terraform.io/tools/doc-preview) +- [terraform-plugin-docs](https://github.com/hashicorp/terraform-plugin-docs) diff --git a/vendor/github.com/hashicorp/terraform/website/docs/providers/terraform/d/remote_state.html.md b/vendor/github.com/hashicorp/terraform/website/docs/providers/terraform/d/remote_state.html.md deleted file mode 100644 index 96cfdc4f..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/providers/terraform/d/remote_state.html.md +++ /dev/null @@ -1,125 +0,0 @@ ---- -layout: "terraform" -page_title: "Terraform: terraform_remote_state" -sidebar_current: "docs-terraform-datasource-remote-state" -description: |- - Accesses state meta data from a remote backend. ---- - -# remote_state - -[backends]: /docs/backends/index.html - -Retrieves state data from a [Terraform backend][backends]. This allows you to -use the root-level outputs of one or more Terraform configurations as input data -for another configuration. - -Although this data source uses Terraform's [backends][], it doesn't have the -same limitations as the main backend configuration. You can use any number of -`remote_state` data sources with differently configured backends, and you can -use interpolations when configuring them. - -## Example Usage (`remote` Backend) - -```hcl -data "terraform_remote_state" "vpc" { - backend = "remote" - - config = { - organization = "hashicorp" - workspaces = { - name = "vpc-prod" - } - } -} - -# Terraform >= 0.12 -resource "aws_instance" "foo" { - # ... - subnet_id = data.terraform_remote_state.vpc.outputs.subnet_id -} - -# Terraform <= 0.11 -resource "aws_instance" "foo" { - # ... - subnet_id = "${data.terraform_remote_state.vpc.subnet_id}" -} -``` - -## Example Usage (`local` Backend) - -```hcl -data "terraform_remote_state" "vpc" { - backend = "local" - - config = { - path = "..." - } -} - -# Terraform >= 0.12 -resource "aws_instance" "foo" { - # ... - subnet_id = data.terraform_remote_state.vpc.outputs.subnet_id -} - -# Terraform <= 0.11 -resource "aws_instance" "foo" { - # ... - subnet_id = "${data.terraform_remote_state.vpc.subnet_id}" -} -``` - -## Argument Reference - -The following arguments are supported: - -* `backend` - (Required) The remote backend to use. -* `workspace` - (Optional) The Terraform workspace to use, if the backend - supports workspaces. -* `config` - (Optional; object) The configuration of the remote backend. - Although this argument is listed as optional, most backends require - some configuration. - - The `config` object can use any arguments that would be valid in the - equivalent `terraform { backend "" { ... } }` block. See - [the documentation of your chosen backend](/docs/backends/types/index.html) - for details. - - -> **Note:** If the backend configuration requires a nested block, specify - it here as a normal attribute with an object value. (For example, - `workspaces = { ... }` instead of `workspaces { ... }`.) -* `defaults` - (Optional; object) Default values for outputs, in case the state - file is empty or lacks a required output. - -## Attributes Reference - -In addition to the above, the following attributes are exported: - -* (v0.12+) `outputs` - An object containing every root-level - [output](/docs/configuration/outputs.html) in the remote state. -* (<= v0.11) `` - Each root-level [output](/docs/configuration/outputs.html) - in the remote state appears as a top level attribute on the data source. - -## Root Outputs Only - -Only the root-level outputs from the remote state are accessible. Outputs from -modules within the state cannot be accessed. If you want a module output or a -resource attribute to be accessible via a remote state, you must thread the -output through to a root output. - -For example: - -```hcl -module "app" { - source = "..." -} - -output "app_value" { - value = "${module.app.value}" -} -``` - -In this example, the output `value` from the "app" module is available as -`app_value`. If this root level output hadn't been created, then a remote state -resource wouldn't be able to access the `value` output on the module. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/providers/terraform/index.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/providers/terraform/index.html.markdown deleted file mode 100644 index 8327958b..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/providers/terraform/index.html.markdown +++ /dev/null @@ -1,42 +0,0 @@ ---- -layout: "terraform" -page_title: "Provider: Terraform" -sidebar_current: "docs-terraform-index" -description: |- - The Terraform provider is used to access meta data from shared infrastructure. ---- - -# Terraform Provider - -The terraform provider provides access to outputs from the Terraform state -of shared infrastructure. - -Use the navigation to the left to read about the available data sources. - -## Example Usage - -```hcl -# Shared infrastructure state stored in Atlas -data "terraform_remote_state" "vpc" { - backend = "remote" - - config { - organization = "hashicorp" - workspaces = { - name = "vpc-prod" - } - } -} - -# Terraform >= 0.12 -resource "aws_instance" "foo" { - # ... - subnet_id = data.terraform_remote_state.vpc.outputs.subnet_id -} - -# Terraform <= 0.11 -resource "aws_instance" "foo" { - # ... - subnet_id = "${data.terraform_remote_state.vpc.subnet_id}" -} -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/providers/type/cloud-index.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/providers/type/cloud-index.html.markdown deleted file mode 100644 index 7e241643..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/providers/type/cloud-index.html.markdown +++ /dev/null @@ -1,62 +0,0 @@ ---- -layout: "docs" -page_title: "Cloud Providers" -sidebar_current: "docs-providers-cloud" -description: |- - Category for standard cloud vendors. ---- - -#Cloud Providers - -This group includes cloud providers offering a range of services including IaaS, -SaaS, and PaaS offerings. This group of cloud providers includes some smaller -scale clouds or ones with more specialized offerings. The Terraform provider -and associated resources for these clouds are primarily supported by the cloud -vendor in close collaboration with HashiCorp, and are tested by HashiCorp. - ---- - - -- [Arukas](/docs/providers/arukas/index.html) -- [BaiduCloud](/docs/providers/baiducloud/index.html) -- [Brightbox](/docs/providers/brightbox/index.html) -- [CenturyLinkCloud](/docs/providers/clc/index.html) -- [CherryServers](/docs/providers/cherryservers/index.html) -- [Cisco ACI](/docs/providers/aci/index.html) -- [CloudScale.ch](/docs/providers/cloudscale/index.html) -- [CloudStack](/docs/providers/cloudstack/index.html) -- [DigitalOcean](/docs/providers/do/index.html) -- [EnterpriseCloud](/docs/providers/ecl/index.html) -- [Exoscale](/docs/providers/exoscale/index.html) -- [Fastly](/docs/providers/fastly/index.html) -- [FlexibleEngine](/docs/providers/flexibleengine/index.html) -- [Gridscale](/docs/providers/gridscale/index.html) -- [Hedvig](/docs/providers/hedvig/index.html) -- [Heroku](/docs/providers/heroku/index.html) -- [Hetzner Cloud](/docs/providers/hcloud/index.html) -- [HuaweiCloud](/docs/providers/huaweicloud/index.html) -- [HuaweiCloudStack](/docs/providers/huaweicloudstack/index.html) -- [JDCloud](/docs/providers/jdcloud/index.html) -- [KingsoftCloud](/docs/providers/ksyun/index.html) -- [Linode](/docs/providers/linode/index.html) -- [MetalCloud](/docs/providers/metalcloud/index.html) -- [Naver Cloud](/docs/providers/ncloud/index.html) -- [Nutanix](/docs/providers/nutanix/index.html) -- [OpenNebula](/docs/providers/opennebula/index.html) -- [OpenStack](/docs/providers/openstack/index.html) -- [OpenTelekomCloud](/docs/providers/opentelekomcloud/index.html) -- [OVH](/docs/providers/ovh/index.html) -- [Packet](/docs/providers/packet/index.html) -- [ProfitBricks](/docs/providers/profitbricks/index.html) -- [Scaleway](/docs/providers/scaleway/index.html) -- [Skytap](/docs/providers/skytap/index.html) -- [Selectel](/docs/providers/selectel/index.html) -- [SoftLayer](/docs/providers/softlayer/index.html) -- [StackPath](/docs/providers/stackpath/index.html) -- [TelefonicaOpenCloud](/docs/providers/telefonicaopencloud/index.html) -- [TencentCloud](/docs/providers/tencentcloud/index.html) -- [Triton](/docs/providers/triton/index.html) -- [UCloud](/docs/providers/ucloud/index.html) -- [Vultr](/docs/providers/vultr/index.html) -- [Yandex.Cloud](/docs/providers/yandex/index.html) -- [1&1](/docs/providers/oneandone/index.html) diff --git a/vendor/github.com/hashicorp/terraform/website/docs/providers/type/community-index.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/providers/type/community-index.html.markdown deleted file mode 100644 index 2c498ed2..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/providers/type/community-index.html.markdown +++ /dev/null @@ -1,15 +0,0 @@ ---- -layout: "docs" -page_title: "Community Providers" -sidebar_current: "docs-providers-community" -description: |- - Category for community-built providers. ---- - -# Community Providers - -Discovering and sharing Terraform providers is now available directly in the Terraform Registry. Please visit [registry.terraform.io](https://registry.terraform.io/browse/providers) to get started. - --> **Note:** Use the “community” filter on the left to view providers published and maintained by community members. - -If you have created a new provider and would like to share it on the Registry, please see our [publishing instructions](https://www.terraform.io/docs/registry/providers/publishing.html) to learn how you can easily share it to other Terraform users. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/providers/type/database-index.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/providers/type/database-index.html.markdown deleted file mode 100644 index 7a676c6d..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/providers/type/database-index.html.markdown +++ /dev/null @@ -1,23 +0,0 @@ ---- -layout: "docs" -page_title: "Database Providers" -sidebar_current: "docs-providers-database" -description: |- - Category for database vendors. ---- - -# Database Providers - -This is a group of database providers offer specific capabilities to provision -and configure your database resources. Terraform integrates with these -database services using the specific provider to provision and manages database -resources. These providers are primarily supported by the vendor in close -collaboration with HashiCorp, and are tested by HashiCorp. - ---- - - -- [InfluxDB](/docs/providers/influxdb/index.html) -- [MongoDB Atlas](/docs/providers/mongodbatlas/index.html) -- [MySQL](/docs/providers/mysql/index.html) -- [PostgreSQL](/docs/providers/postgresql/index.html) diff --git a/vendor/github.com/hashicorp/terraform/website/docs/providers/type/infra-index.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/providers/type/infra-index.html.markdown deleted file mode 100644 index 43989a3d..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/providers/type/infra-index.html.markdown +++ /dev/null @@ -1,44 +0,0 @@ ---- -layout: "docs" -page_title: "Infrastructure Software Providers" -sidebar_current: "docs-providers-infra" -description: |- - Category for infrastructure management vendors. ---- - -# Infrastructure Software Providers - -This is a group of software providers offering specialized infrastructure -management capabilities such as configuration management. Terraform integrates -with these tools using the specific providers to enable these specialized tools -to execute tasks during the provisioning of infrastructure. These providers -are primarily supported by the vendor in close collaboration with HashiCorp, -and are tested by HashiCorp. - ---- - -- [Chef](/docs/providers/chef/index.html) -- [CloudAMQP](/docs/providers/cloudamqp/index.html) -- [Cohesity](/docs/providers/cohesity/index.html) -- [Consul](/docs/providers/consul/index.html) -- [Docker](/docs/providers/docker/index.html) -- [Dome9](/docs/providers/dome9/index.html) -- [Helm](/docs/providers/helm/index.html) -- [Kubernetes](/docs/providers/kubernetes/index.html) -- [Lacework](/docs/providers/lacework/index.html) -- [Mailgun](/docs/providers/mailgun/index.html) -- [Nomad](/docs/providers/nomad/index.html) -- [Okta](/docs/providers/okta/index.html) -- [Okta Advanced Server Access](/docs/providers/oktaasa/index.html) -- [RabbitMQ](/docs/providers/rabbitmq/index.html) -- [Rancher](/docs/providers/rancher/index.html) -- [Rancher2](/docs/providers/rancher2/index.html) -- [RightScale](/docs/providers/rightscale/index.html) -- [Rubrik](/docs/providers/rubrik/index.html) -- [Rundeck](/docs/providers/rundeck/index.html) -- [Spotinst](/docs/providers/spotinst/index.html) -- [Terraform](/docs/providers/terraform/index.html) -- [Terraform Cloud](/docs/providers/tfe/index.html) -- [Turbot](/docs/providers/turbot/index.html) -- [Vault](/docs/providers/vault/index.html) -- [Venafi](/docs/providers/venafi/index.html) diff --git a/vendor/github.com/hashicorp/terraform/website/docs/providers/type/major-index.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/providers/type/major-index.html.markdown deleted file mode 100644 index bde80127..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/providers/type/major-index.html.markdown +++ /dev/null @@ -1,35 +0,0 @@ ---- -layout: "docs" -page_title: "Major Cloud Providers" -sidebar_current: "docs-providers-major" -description: |- - Category for major cloud vendors. ---- - -# Major Cloud Providers - -This group includes hyper-scale cloud providers that offer a range of services -including IaaS, SaaS, and PaaS. A large percentage of Terraform users provision -their infrastructure on these major cloud providers. HashiCorp closely partners -with these cloud providers to offer best-in-class integration to provision and -manage the majority of the services offered. These providers are primarily -supported by the cloud vendor in close collaboration with HashiCorp, and are -tested by HashiCorp. - ---- - - -- [Alibaba Cloud](/docs/providers/alicloud/index.html) -- [AWS](/docs/providers/aws/index.html) -- [Azure](/docs/providers/azurerm/index.html) -- [Azure DevOps](/docs/providers/azuredevops/index.html) -- [Azure Stack](/docs/providers/azurestack/index.html) -- [Google Cloud Platform](/docs/providers/google/index.html) -- [Oracle Cloud Infrastructure](/docs/providers/oci/index.html) -- [Oracle Cloud Platform](/docs/providers/oraclepaas/index.html) -- [Oracle Public Cloud](/docs/providers/opc/index.html) -- [VMware Cloud](/docs/providers/vmc/index.html) -- [VMware NSX-T](/docs/providers/nsxt/index.html) -- [vCloud Director](/docs/providers/vcd/index.html) -- [VMware vRA7](/docs/providers/vra7/index.html) -- [VMware vSphere](/docs/providers/vsphere/index.html) diff --git a/vendor/github.com/hashicorp/terraform/website/docs/providers/type/misc-index.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/providers/type/misc-index.html.markdown deleted file mode 100644 index 1efc87d5..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/providers/type/misc-index.html.markdown +++ /dev/null @@ -1,29 +0,0 @@ ---- -layout: "docs" -page_title: "Misc Providers" -sidebar_current: "docs-providers-misc" -description: |- - Category for miscellaneous vendors. ---- - -# Miscellaneous Providers - -This is a group of miscellaneous providers offer specific capabilities that can -be useful when working with Terraform. These providers are primarily supported -by the vendors and the Terraform community, and are tested by HashiCorp. - ---- - -- [ACME](/docs/providers/acme/index.html) -- [Archive](/docs/providers/archive/index.html) -- [Cobbler](/docs/providers/cobbler/index.html) -- [External](/docs/providers/external/index.html) -- [Genymotion](/docs/providers/genymotion/index.html) -- [Ignition](/docs/providers/ignition/index.html) -- [Local](/docs/providers/local/index.html) -- [Netlify](/docs/providers/netlify/index.html) -- [Null](/docs/providers/null/index.html) -- [Random](/docs/providers/random/index.html) -- [Template](/docs/providers/template/index.html) -- [TLS](/docs/providers/tls/index.html) -- [Time](/docs/providers/time/index.html) diff --git a/vendor/github.com/hashicorp/terraform/website/docs/providers/type/monitor-index.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/providers/type/monitor-index.html.markdown deleted file mode 100644 index d28efc76..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/providers/type/monitor-index.html.markdown +++ /dev/null @@ -1,39 +0,0 @@ ---- -layout: "docs" -page_title: "Monitor & Sys Management Providers" -sidebar_current: "docs-providers-monitor" -description: |- - Category for monitoring and system management vendors. ---- - -# Monitoring & System Management Providers - -This is a group of monitoring & system management providers that offer the -capability to configure and manage services such as loggers, metric tools, -and monitoring services. Terraform integrates with these services using the -specific provider to enable these specialized monitoring capabilities. These -providers are primarily supported by the vendor in close collaboration with -HashiCorp, and are tested by HashiCorp. - - ---- - - -- [Auth0](/docs/providers/auth0/index.html) -- [Circonus](/docs/providers/circonus/index.html) -- [Datadog](/docs/providers/datadog/index.html) -- [Dyn](/docs/providers/dyn/index.html) -- [Grafana](/docs/providers/grafana/index.html) -- [Icinga2](/docs/providers/icinga2/index.html) -- [LaunchDarkly](/docs/providers/launchdarkly/index.html) -- [Librato](/docs/providers/librato/index.html) -- [Logentries](/docs/providers/logentries/index.html) -- [LogicMonitor](/docs/providers/logicmonitor/index.html) -- [New Relic](https://registry.terraform.io/providers/newrelic/newrelic/latest/docs) -- [OpsGenie](/docs/providers/opsgenie/index.html) -- [PagerDuty](/docs/providers/pagerduty/index.html) -- [Runscope](/docs/providers/runscope/index.html) -- [SignalFx](/docs/providers/signalfx/index.html) -- [StatusCake](/docs/providers/statuscake/index.html) -- [Sumo Logic](/docs/providers/sumologic/index.html) -- [Wavefront](/docs/providers/wavefront/index.html) diff --git a/vendor/github.com/hashicorp/terraform/website/docs/providers/type/network-index.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/providers/type/network-index.html.markdown deleted file mode 100644 index d035eb84..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/providers/type/network-index.html.markdown +++ /dev/null @@ -1,42 +0,0 @@ ---- -layout: "docs" -page_title: "Network Providers" -sidebar_current: "docs-providers-network" -description: |- - Category for network vendors. ---- - -# Network Providers - -This is a group of network providers that offer specific network capabilities -such as DNS, routing, and firewall configuration. The providers generally -offer a cloud-based service and Terraform integrates with these services using -the specific providers. These providers are primarily supported by the vendor -in close collaboration with HashiCorp, and are tested by HashiCorp. - ---- - - -- [Akamai](/docs/providers/akamai/index.html) -- [Avi Vantage](/docs/providers/avi/index.html) -- [Aviatrix](/docs/providers/aviatrix/index.html) -- [A10 Networks](/docs/providers/vthunder/index.html) -- [Check Point](/docs/providers/checkpoint/index.html) -- [Cloudflare](/docs/providers/cloudflare/index.html) -- [Cisco ASA](/docs/providers/ciscoasa/index.html) -- [Cisco MSO](/docs/providers/mso/index.html) -- [Constellix](/docs/providers/constellix/index.html) -- [DNS](/docs/providers/dns/index.html) -- [DNSimple](/docs/providers/dnsimple/index.html) -- [DNSMadeEasy](/docs/providers/dme/index.html) -- [F5 BIG-IP](/docs/providers/bigip/index.html) -- [FortiOS](/docs/providers/fortios/index.html) -- [HTTP](/docs/providers/http/index.html) -- [Incapsula](/docs/providers/incapsula/index.html) -- [Infoblox](/docs/providers/infoblox/index.html) -- [NS1](/docs/providers/ns1/index.html) -- [Palo Alto Networks PANOS](/docs/providers/panos/index.html) -- [Palo Alto Networks Prisma Cloud](/docs/providers/prismacloud/index.html) -- [PowerDNS](/docs/providers/powerdns/index.html) -- [Pureport](/docs/providers/pureport/index.html) -- [UltraDNS](/docs/providers/ultradns/index.html) diff --git a/vendor/github.com/hashicorp/terraform/website/docs/providers/type/vcs-index.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/providers/type/vcs-index.html.markdown deleted file mode 100644 index 6c1f77a8..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/providers/type/vcs-index.html.markdown +++ /dev/null @@ -1,22 +0,0 @@ ---- -layout: "docs" -page_title: "VCS Providers" -sidebar_current: "docs-providers-vcs" -description: |- - Category for version control vendors. ---- - -# Version Control Providers - -This is a group of Version Control System (VCS) providers that offer -capabilities of using Terraform to manage your VCS projects, teams and -repositories. Terraform integrates with these services to create and manage -resources provided by the VCS. These providers are primarily supported by the -vendor in close collaboration with HashiCorp, and are tested by HashiCorp. - ---- - - -- [Bitbucket](/docs/providers/bitbucket/index.html) -- [GitHub](/docs/providers/github/index.html) -- [GitLab](/docs/providers/gitlab/index.html) diff --git a/vendor/github.com/hashicorp/terraform/website/docs/provisioners/chef.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/provisioners/chef.html.markdown deleted file mode 100644 index 1248f363..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/provisioners/chef.html.markdown +++ /dev/null @@ -1,184 +0,0 @@ ---- -layout: "docs" -page_title: "Provisioner: chef" -sidebar_current: "docs-provisioners-chef" -description: |- - The `chef` provisioner installs, configures and runs the Chef client on a resource. ---- - -# Chef Provisioner - -The `chef` provisioner installs, configures and runs the Chef Client on a remote -resource. The `chef` provisioner supports both `ssh` and `winrm` type -[connections](/docs/provisioners/connection.html). - --> **Note:** This provisioner has been deprecated as of Terraform 0.13.4 and will be -removed in a future version of Terraform. For most common situations there are better -alternatives to using provisioners. For more information, see -[the main Provisioners page](./). - -## Requirements - -The `chef` provisioner has some prerequisites for specific connection types: - -* For `ssh` type connections, `cURL` must be available on the remote host. -* For `winrm` connections, `PowerShell 2.0` must be available on the remote host. - -[Chef end user license agreement](https://www.chef.io/end-user-license-agreement/) must be accepted by setting `chef_license` to `accept` in `client_options` argument unless you are installing an old version of Chef client. - -Without these prerequisites, your provisioning execution will fail. - -## Example usage - -```hcl -resource "aws_instance" "web" { - # ... - - provisioner "chef" { - attributes_json = < **Note:** Provisioners should only be used as a last resort. For most -common situations there are better alternatives. For more information, see -[the main Provisioners page](./). - --> **Note:** In Terraform 0.11 and earlier, providers could set default values -for some connection settings, so that `connection` blocks could sometimes be -omitted. This feature was removed in 0.12 in order to make Terraform's behavior -more predictable. - --> **Note:** Since the SSH connection type is most often used with -newly-created remote resources, validation of SSH host keys is disabled by -default. In scenarios where this is not acceptable, a separate mechanism for -key distribution could be established and the `host_key` directive documented -below explicitly set to verify against a specific key or signing CA. - -Connection blocks don't take a block label, and can be nested within either a -`resource` or a `provisioner`. - -- A `connection` block nested directly within a `resource` affects all of - that resource's provisioners. -- A `connection` block nested in a `provisioner` block only affects that - provisioner, and overrides any resource-level connection settings. - -One use case for providing multiple connections is to have an initial -provisioner connect as the `root` user to set up user accounts, and have -subsequent provisioners connect as a user with more limited permissions. - -## Example usage - -```hcl -# Copies the file as the root user using SSH -provisioner "file" { - source = "conf/myapp.conf" - destination = "/etc/myapp.conf" - - connection { - type = "ssh" - user = "root" - password = "${var.root_password}" - host = "${var.host}" - } -} - -# Copies the file as the Administrator user using WinRM -provisioner "file" { - source = "conf/myapp.conf" - destination = "C:/App/myapp.conf" - - connection { - type = "winrm" - user = "Administrator" - password = "${var.admin_password}" - host = "${var.host}" - } -} -``` - -## The `self` Object - -Expressions in `connection` blocks cannot refer to their parent resource by -name. Instead, they can use the special `self` object. - -The `self` object represents the connection's parent resource, and has all of -that resource's attributes. For example, use `self.public_ip` to reference an -`aws_instance`'s `public_ip` attribute. - --> **Technical note:** Resource references are restricted here because -references create dependencies. Referring to a resource by name within its own -block would create a dependency cycle. - -## Argument Reference - -**The following arguments are supported by all connection types:** - -* `type` - The connection type that should be used. Valid types are `ssh` and `winrm`. - Defaults to `ssh`. - -* `user` - The user that we should use for the connection. - Defaults to `root` when using type `ssh` and defaults to `Administrator` when using type `winrm`. - -* `password` - The password we should use for the connection. In some cases this is - specified by the provider. - -* `host` - (Required) The address of the resource to connect to. - -* `port` - The port to connect to. - Defaults to `22` when using type `ssh` and defaults to `5985` when using type `winrm`. - -* `timeout` - The timeout to wait for the connection to become available. Should be provided as a string like `30s` or `5m`. - Defaults to 5 minutes. - -* `script_path` - The path used to copy scripts meant for remote execution. - -**Additional arguments only supported by the `ssh` connection type:** - -* `private_key` - The contents of an SSH key to use for the connection. These can - be loaded from a file on disk using - [the `file` function](/docs/configuration/functions/file.html). This takes - preference over the password if provided. - -* `certificate` - The contents of a signed CA Certificate. The certificate argument must be - used in conjunction with a `private_key`. These can - be loaded from a file on disk using the [the `file` function](/docs/configuration/functions/file.html). - -* `agent` - Set to `false` to disable using `ssh-agent` to authenticate. On Windows the - only supported SSH authentication agent is - [Pageant](http://the.earth.li/~sgtatham/putty/0.66/htmldoc/Chapter9.html#pageant). - -* `agent_identity` - The preferred identity from the ssh agent for authentication. - -* `host_key` - The public key from the remote host or the signing CA, used to - verify the connection. - -**Additional arguments only supported by the `winrm` connection type:** - -* `https` - Set to `true` to connect using HTTPS instead of HTTP. - -* `insecure` - Set to `true` to not validate the HTTPS certificate chain. - -* `use_ntlm` - Set to `true` to use NTLM authentication, rather than default (basic authentication), removing the requirement for basic authentication to be enabled within the target guest. Further reading for remote connection authentication can be found [here](https://msdn.microsoft.com/en-us/library/aa384295(v=vs.85).aspx). - -* `cacert` - The CA certificate to validate against. - - - -## Connecting through a Bastion Host with SSH - -The `ssh` connection also supports the following fields to facilitate connnections via a -[bastion host](https://en.wikipedia.org/wiki/Bastion_host). - -* `bastion_host` - Setting this enables the bastion Host connection. This host - will be connected to first, and then the `host` connection will be made from there. - -* `bastion_host_key` - The public key from the remote host or the signing CA, - used to verify the host connection. - -* `bastion_port` - The port to use connect to the bastion host. Defaults to the - value of the `port` field. - -* `bastion_user` - The user for the connection to the bastion host. Defaults to - the value of the `user` field. - -* `bastion_password` - The password we should use for the bastion host. - Defaults to the value of the `password` field. - -* `bastion_private_key` - The contents of an SSH key file to use for the bastion - host. These can be loaded from a file on disk using - [the `file` function](/docs/configuration/functions/file.html). - Defaults to the value of the `private_key` field. - -* `bastion_certificate` - The contents of a signed CA Certificate. The certificate argument - must be used in conjunction with a `bastion_private_key`. These can be loaded from - a file on disk using the [the `file` function](/docs/configuration/functions/file.html). \ No newline at end of file diff --git a/vendor/github.com/hashicorp/terraform/website/docs/provisioners/file.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/provisioners/file.html.markdown deleted file mode 100644 index 5b3c80b0..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/provisioners/file.html.markdown +++ /dev/null @@ -1,92 +0,0 @@ ---- -layout: "docs" -page_title: "Provisioner: file" -sidebar_current: "docs-provisioners-file" -description: |- - The `file` provisioner is used to copy files or directories from the machine executing Terraform to the newly created resource. The `file` provisioner supports both `ssh` and `winrm` type connections. ---- - -# File Provisioner - -The `file` provisioner is used to copy files or directories from the machine -executing Terraform to the newly created resource. The `file` provisioner -supports both `ssh` and `winrm` type [connections](/docs/provisioners/connection.html). - --> **Note:** Provisioners should only be used as a last resort. For most -common situations there are better alternatives. For more information, see -[the main Provisioners page](./). - -## Example usage - -```hcl -resource "aws_instance" "web" { - # ... - - # Copies the myapp.conf file to /etc/myapp.conf - provisioner "file" { - source = "conf/myapp.conf" - destination = "/etc/myapp.conf" - } - - # Copies the string in content into /tmp/file.log - provisioner "file" { - content = "ami used: ${self.ami}" - destination = "/tmp/file.log" - } - - # Copies the configs.d folder to /etc/configs.d - provisioner "file" { - source = "conf/configs.d" - destination = "/etc" - } - - # Copies all files and folders in apps/app1 to D:/IIS/webapp1 - provisioner "file" { - source = "apps/app1/" - destination = "D:/IIS/webapp1" - } -} -``` - -## Argument Reference - -The following arguments are supported: - -* `source` - This is the source file or folder. It can be specified as - relative to the current working directory or as an absolute path. This - attribute cannot be specified with `content`. - -* `content` - This is the content to copy on the destination. If destination is a file, - the content will be written on that file, in case of a directory a file named - `tf-file-content` is created. It's recommended to use a file as the destination. A - [`template_file`](/docs/providers/template/d/file.html) might be referenced in here, or - any interpolation syntax. This attribute cannot be specified with `source`. - -* `destination` - (Required) This is the destination path. It must be specified as an - absolute path. - -## Directory Uploads - -The file provisioner is also able to upload a complete directory to the remote machine. -When uploading a directory, there are a few important things you should know. - -First, when using the `ssh` connection type the destination directory must already exist. -If you need to create it, use a remote-exec provisioner just prior to the file provisioner -in order to create the directory. When using the `winrm` connection type the destination -directory will be created for you if it doesn't already exist. - -Next, the existence of a trailing slash on the source path will determine whether the -directory name will be embedded within the destination, or whether the destination will -be created. An example explains this best: - -If the source is `/foo` (no trailing slash), and the destination is `/tmp`, then the contents -of `/foo` on the local machine will be uploaded to `/tmp/foo` on the remote machine. The -`foo` directory on the remote machine will be created by Terraform. - -If the source, however, is `/foo/` (a trailing slash is present), and the destination is -`/tmp`, then the contents of `/foo` will be uploaded directly into `/tmp`. - -This behavior was adopted from the standard behavior of -[rsync](https://linux.die.net/man/1/rsync). - --> **Note:** Under the covers, rsync may or may not be used. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/provisioners/habitat.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/provisioners/habitat.html.markdown deleted file mode 100644 index d35ab7dd..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/provisioners/habitat.html.markdown +++ /dev/null @@ -1,99 +0,0 @@ ---- -layout: "docs" -page_title: "Provisioner: habitat" -sidebar_current: "docs-provisioners-habitat" -description: |- - The `habitat` provisioner installs the Habitat supervisor, and loads configured services. ---- - -# Habitat Provisioner - -The `habitat` provisioner installs the [Habitat](https://habitat.sh) supervisor and loads configured services. This provisioner only supports Linux targets using the `ssh` connection type at this time. - --> **Note:** This provisioner has been deprecated as of Terraform 0.13.4 and will be -removed in a future version of Terraform. For most common situations there are better -alternatives to using provisioners. For more information, see -[the main Provisioners page](./). - -## Requirements - -The `habitat` provisioner has some prerequisites for specific connection types: - -- For `ssh` type connections, we assume a few tools to be available on the remote host: - * `curl` - * `tee` - * `setsid` - Only if using the `unmanaged` service type. - -Without these prerequisites, your provisioning execution will fail. - -## Example usage - -```hcl -resource "aws_instance" "redis" { - count = 3 - - provisioner "habitat" { - peers = [aws_instance.redis[0].private_ip] - use_sudo = true - service_type = "systemd" - accept_license = true - - service { - name = "core/redis" - topology = "leader" - user_toml = file("conf/redis.toml") - } - } -} - -``` - -## Argument Reference - -There are 2 configuration levels, `supervisor` and `service`. Configuration placed directly within the `provisioner` block are supervisor configurations, and a provisioner can define zero or more services to run, and each service will have a `service` block within the `provisioner`. A `service` block can also contain zero or more `bind` blocks to create service group bindings. - -### Supervisor Arguments -* `accept_license (bool)` - (Required) Set to true to accept [Habitat end user license agreement](https://www.chef.io/end-user-license-agreement/) -* `version (string)` - (Optional) The Habitat version to install on the remote machine. If not specified, the latest available version is used. -* `auto_update (bool)` - (Optional) If set to `true`, the supervisor will auto-update itself as soon as new releases are available on the specified `channel`. -* `http_disable (bool)` - (Optional) If set to `true`, disables the supervisor HTTP listener entirely. -* `peer (string)` - (Optional, deprecated) IP addresses or FQDN's for other Habitat supervisors to peer with, like: `--peer 1.2.3.4 --peer 5.6.7.8`. (Defaults to none) -* `peers (array)` - (Optional) A list of IP or FQDN's of other supervisor instance(s) to peer with. (Defaults to none) -* `service_type (string)` - (Optional) Method used to run the Habitat supervisor. Valid options are `unmanaged` and `systemd`. (Defaults to `systemd`) -* `service_name (string)` - (Optional) The name of the Habitat supervisor service, if using an init system such as `systemd`. (Defaults to `hab-supervisor`) -* `use_sudo (bool)` - (Optional) Use `sudo` when executing remote commands. Required when the user specified in the `connection` block is not `root`. (Defaults to `true`) -* `permanent_peer (bool)` - (Optional) Marks this supervisor as a permanent peer. (Defaults to false) -* `listen_ctl (string)` - (Optional) The listen address for the countrol gateway system (Defaults to 127.0.0.1:9632) -* `listen_gossip (string)` - (Optional) The listen address for the gossip system (Defaults to 0.0.0.0:9638) -* `listen_http (string)` - (Optional) The listen address for the HTTP gateway (Defaults to 0.0.0.0:9631) -* `ring_key (string)` - (Optional) The name of the ring key for encrypting gossip ring communication (Defaults to no encryption) -* `ring_key_content (string)` - (Optional) The key content. Only needed if using ring encryption and want the provisioner to take care of uploading and importing it. Easiest to source from a file (eg `ring_key_content = "${file("conf/foo-123456789.sym.key")}"`) (Defaults to none) -* `ctl_secret (string)` - (Optional) Specify a secret to use (from `hab sup secret generate`) for control gateway communication between hab client(s) and the supervisor. (Defaults to none) -* `url (string)` - (Optional) The URL of a Builder service to download packages and receive updates from. (Defaults to https://bldr.habitat.sh) -* `channel (string)` - (Optional) The release channel in the Builder service to use. (Defaults to `stable`) -* `events (string)` - (Optional) Name of the service group running a Habitat EventSrv to forward Supervisor and service event data to. (Defaults to none) -* `organization (string)` - (Optional) The organization that the Supervisor and it's subsequent services are part of. (Defaults to `default`) -* `gateway_auth_token (string)` - (Optional) The http gateway authorization token (Defaults to none) -* `builder_auth_token (string)` - (Optional) The builder authorization token when using a private origin. (Defaults to none) - -### Service Arguments -* `name (string)` - (Required) The Habitat package identifier of the service to run. (ie `core/haproxy` or `core/redis/3.2.4/20171002182640`) -* `binds (array)` - (Optional) An array of bind specifications. (ie `binds = ["backend:nginx.default"]`) -* `bind` - (Optional) An alternative way of declaring binds. This method can be easier to deal with when populating values from other values or variable inputs without having to do string interpolation. The following example is equivalent to `binds = ["backend:nginx.default"]`: - -```hcl -bind { - alias = "backend" - service = "nginx" - group = "default" -} -``` -* `topology (string)` - (Optional) Topology to start service in. Possible values `standalone` or `leader`. (Defaults to `standalone`) -* `strategy (string)` - (Optional) Update strategy to use. Possible values `at-once`, `rolling` or `none`. (Defaults to `none`) -* `user_toml (string)` - (Optional) TOML formatted user configuration for the service. Easiest to source from a file (eg `user_toml = "${file("conf/redis.toml")}"`). (Defaults to none) -* `channel (string)` - (Optional) The release channel in the Builder service to use. (Defaults to `stable`) -* `group (string)` - (Optional) The service group to join. (Defaults to `default`) -* `url (string)` - (Optional) The URL of a Builder service to download packages and receive updates from. (Defaults to https://bldr.habitat.sh) -* `application (string)` - (Optional) The application name. (Defaults to none) -* `environment (string)` - (Optional) The environment name. (Defaults to none) -* `service_key (string)` - (Optional) The key content of a service private key, if using service group encryption. Easiest to source from a file (eg `service_key = "${file("conf/redis.default@org-123456789.box.key")}"`) (Defaults to none) diff --git a/vendor/github.com/hashicorp/terraform/website/docs/provisioners/index.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/provisioners/index.html.markdown deleted file mode 100644 index d604b6f8..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/provisioners/index.html.markdown +++ /dev/null @@ -1,296 +0,0 @@ ---- -layout: "docs" -page_title: "Provisioners" -sidebar_current: "docs-provisioners" -description: |- - Provisioners are used to execute scripts on a local or remote machine as part of resource creation or destruction. ---- - -# Provisioners - -Provisioners can be used to model specific actions on the local machine or on -a remote machine in order to prepare servers or other infrastructure objects -for service. - -## Provisioners are a Last Resort - -> **Hands-on:** To learn about more declarative ways to handle provisioning actions, try the [Provision Infrastructure Deployed with Terraform](https://learn.hashicorp.com/collections/terraform/provision?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn. - -Terraform includes the concept of provisioners as a measure of pragmatism, -knowing that there will always be certain behaviors that can't be directly -represented in Terraform's declarative model. - -However, they also add a considerable amount of complexity and uncertainty to -Terraform usage. Firstly, Terraform cannot model the actions of provisioners -as part of a plan because they can in principle take any action. Secondly, -successful use of provisioners requires coordinating many more details than -Terraform usage usually requires: direct network access to your servers, -issuing Terraform credentials to log in, making sure that all of the necessary -external software is installed, etc. - -The following sections describe some situations which can be solved with -provisioners in principle, but where better solutions are also available. We do -not recommend using provisioners for any of the use-cases described in the -following sections. - -Even if your specific use-case is not described in the following sections, we -still recommend attempting to solve it using other techniques first, and use -provisioners only if there is no other option. - -### Passing data into virtual machines and other compute resources - -When deploying virtual machines or other similar compute resources, we often -need to pass in data about other related infrastructure that the software on -that server will need to do its job. - -The various provisioners that interact with remote servers over SSH or WinRM -can potentially be used to pass such data by logging in to the server and -providing it directly, but most cloud computing platforms provide mechanisms -to pass data to instances at the time of their creation such that the data -is immediately available on system boot. For example: - -* Alibaba Cloud: `user_data` on - [`alicloud_instance`](/docs/providers/alicloud/r/instance.html) - or [`alicloud_launch_template`](/docs/providers/alicloud/r/launch_template.html). -* Amazon EC2: `user_data` or `user_data_base64` on - [`aws_instance`](/docs/providers/aws/r/instance.html), - [`aws_launch_template`](/docs/providers/aws/r/launch_template.html), - and [`aws_launch_configuration`](/docs/providers/aws/r/launch_configuration.html). -* Amazon Lightsail: `user_data` on - [`aws_lightsail_instance`](/docs/providers/aws/r/lightsail_instance.html). -* Microsoft Azure: `custom_data` on - [`azurerm_virtual_machine`](/docs/providers/azurerm/r/virtual_machine.html) - or [`azurerm_virtual_machine_scale_set`](/docs/providers/azurerm/r/virtual_machine_scale_set.html). -* Google Cloud Platform: `metadata` on - [`google_compute_instance`](/docs/providers/google/r/compute_instance.html) - or [`google_compute_instance_group`](/docs/providers/google/r/compute_instance_group.html). -* Oracle Cloud Infrastructure: `metadata` or `extended_metadata` on - [`oci_core_instance`](/docs/providers/oci/r/core_instance.html) - or [`oci_core_instance_configuration`](/docs/providers/oci/r/core_instance_configuration.html). -* VMware vSphere: Attach a virtual CDROM to - [`vsphere_virtual_machine`](/docs/providers/vsphere/r/virtual_machine.html) - using the `cdrom` block, containing a file called `user-data.txt`. - -Many official Linux distribution disk images include software called -[cloud-init](https://cloudinit.readthedocs.io/en/latest/) that can automatically -process in various ways data passed via the means described above, allowing -you to run arbitrary scripts and do basic system configuration immediately -during the boot process and without the need to access the machine over SSH. - -> **Hands-on:** Try the [Provision Infrastructure with Cloud-Init](https://learn.hashicorp.com/tutorials/terraform/cloud-init?in=terraform/provision&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. - -If you are building custom machine images, you can make use of the "user data" -or "metadata" passed by the above means in whatever way makes sense to your -application, by referring to your vendor's documentation on how to access the -data at runtime. - -This approach is _required_ if you intend to use any mechanism in your cloud -provider for automatically launching and destroying servers in a group, -because in that case individual servers will launch unattended while Terraform -is not around to provision them. - -Even if you're deploying individual servers directly with Terraform, passing -data this way will allow faster boot times and simplify deployment by avoiding -the need for direct network access from Terraform to the new server and for -remote access credentials to be provided. - -### Running configuration management software - -As a convenience to users who are forced to use generic operating system -distribution images, Terraform includes a number of specialized provisioners -for launching specific configuration management products. - -We strongly recommend not using these, and instead running system configuration -steps during a custom image build process. For example, -[HashiCorp Packer](https://packer.io/) offers a similar complement of -configuration management provisioners and can run their installation steps -during a separate build process, before creating a system disk image that you -can deploy many times. - -> **Hands-on:** Try the [Provision Infrastructure with Packer](https://learn.hashicorp.com/tutorials/terraform/packer?in=terraform/provision&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) tutorial on HashiCorp Learn. - -If you are using configuration management software that has a centralized server -component, you will need to delay the _registration_ step until the final -system is booted from your custom image. To achieve that, use one of the -mechanisms described above to pass the necessary information into each instance -so that it can register itself with the configuration management server -immediately on boot, without the need to accept commands from Terraform over -SSH or WinRM. - -### First-class Terraform provider functionality may be available - -It is technically possible to use the `local-exec` provisioner to run the CLI -for your target system in order to create, update, or otherwise interact with -remote objects in that system. - -If you are trying to use a new feature of the remote system that isn't yet -supported in its Terraform provider, that might be the only option. However, -if there _is_ provider support for the feature you intend to use, prefer to -use that provider functionality rather than a provisioner so that Terraform -can be fully aware of the object and properly manage ongoing changes to it. - -Even if the functionality you need is not available in a provider today, we -suggest to consider `local-exec` usage a temporary workaround and to also -open an issue in the relevant provider's repository to discuss adding -first-class provider support. Provider development teams often prioritize -features based on interest, so opening an issue is a way to record your -interest in the feature. - -Provisioners are used to execute scripts on a local or remote machine -as part of resource creation or destruction. Provisioners can be used to -bootstrap a resource, cleanup before destroy, run configuration management, etc. - -## How to use Provisioners - --> **Note:** Provisioners should only be used as a last resort. For most -common situations there are better alternatives. For more information, see -the sections above. - -If you are certain that provisioners are the best way to solve your problem -after considering the advice in the sections above, you can add a -`provisioner` block inside the `resource` block of a compute instance. - -```hcl -resource "aws_instance" "web" { - # ... - - provisioner "local-exec" { - command = "echo The server's IP address is ${self.private_ip}" - } -} -``` - -The `local-exec` provisioner requires no other configuration, but most other -provisioners must connect to the remote system using SSH or WinRM. -You must include [a `connection` block](./connection.html) so that Terraform -will know how to communicate with the server. - -Terraform includes several built-in provisioners; use the navigation sidebar to -view their documentation. You can also install third-party provisioners in -[the user plugins directory](../configuration/providers.html#third-party-plugins). - -All provisioners support the `when` and `on_failure` meta-arguments, which -are described below (see [Destroy-Time Provisioners](#destroy-time-provisioners) -and [Failure Behavior](#failure-behavior)). - -### The `self` Object - -Expressions in `provisioner` blocks cannot refer to their parent resource by -name. Instead, they can use the special `self` object. - -The `self` object represents the provisioner's parent resource, and has all of -that resource's attributes. For example, use `self.public_ip` to reference an -`aws_instance`'s `public_ip` attribute. - --> **Technical note:** Resource references are restricted here because -references create dependencies. Referring to a resource by name within its own -block would create a dependency cycle. - -## Suppressing Provisioner Logs in CLI Output - -The configuration for a `provisioner` block may use sensitive values, such as [`sensitive` variables](../configuration/variables.html#suppressing-values-in-cli-output) or [`sensitive` output values](../outputs.html#sensitive-suppressing-values-in-cli-output). In this case, all log output from the provider is automatically suppressed to prevent the sensitive values from being displayed. - -## Creation-Time Provisioners - -By default, provisioners run when the resource they are defined within is -created. Creation-time provisioners are only run during _creation_, not -during updating or any other lifecycle. They are meant as a means to perform -bootstrapping of a system. - -If a creation-time provisioner fails, the resource is marked as **tainted**. -A tainted resource will be planned for destruction and recreation upon the -next `terraform apply`. Terraform does this because a failed provisioner -can leave a resource in a semi-configured state. Because Terraform cannot -reason about what the provisioner does, the only way to ensure proper creation -of a resource is to recreate it. This is tainting. - -You can change this behavior by setting the `on_failure` attribute, -which is covered in detail below. - -## Destroy-Time Provisioners - -If `when = destroy` is specified, the provisioner will run when the -resource it is defined within is _destroyed_. - -```hcl -resource "aws_instance" "web" { - # ... - - provisioner "local-exec" { - when = destroy - command = "echo 'Destroy-time provisioner'" - } -} -``` - -Destroy provisioners are run before the resource is destroyed. If they -fail, Terraform will error and rerun the provisioners again on the next -`terraform apply`. Due to this behavior, care should be taken for destroy -provisioners to be safe to run multiple times. - -Destroy-time provisioners can only run if they remain in the configuration -at the time a resource is destroyed. If a resource block with a destroy-time -provisioner is removed entirely from the configuration, its provisioner -configurations are removed along with it and thus the destroy provisioner -won't run. To work around this, a multi-step process can be used to safely -remove a resource with a destroy-time provisioner: - -* Update the resource configuration to include `count = 0`. -* Apply the configuration to destroy any existing instances of the resource, including running the destroy provisioner. -* Remove the resource block entirely from configuration, along with its `provisioner` blocks. -* Apply again, at which point no further action should be taken since the resources were already destroyed. - -This limitation may be addressed in future versions of Terraform. For now, -destroy-time provisioners must be used sparingly and with care. - -~> **NOTE:** A destroy-time provisioner within a resource that is tainted _will not_ run. This includes resources that are marked tainted from a failed creation-time provisioner or tainted manually using `terraform taint`. - -## Multiple Provisioners - -Multiple provisioners can be specified within a resource. Multiple provisioners -are executed in the order they're defined in the configuration file. - -You may also mix and match creation and destruction provisioners. Only -the provisioners that are valid for a given operation will be run. Those -valid provisioners will be run in the order they're defined in the configuration -file. - -Example of multiple provisioners: - -```hcl -resource "aws_instance" "web" { - # ... - - provisioner "local-exec" { - command = "echo first" - } - - provisioner "local-exec" { - command = "echo second" - } -} -``` - -## Failure Behavior - -By default, provisioners that fail will also cause the Terraform apply -itself to fail. The `on_failure` setting can be used to change this. The -allowed values are: - -- `continue` - Ignore the error and continue with creation or destruction. - -- `fail` - Raise an error and stop applying (the default behavior). If this is a creation provisioner, - taint the resource. - -Example: - -```hcl -resource "aws_instance" "web" { - # ... - - provisioner "local-exec" { - command = "echo The server's IP address is ${self.private_ip}" - on_failure = continue - } -} -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/provisioners/local-exec.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/provisioners/local-exec.html.markdown deleted file mode 100644 index 2f8cf628..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/provisioners/local-exec.html.markdown +++ /dev/null @@ -1,95 +0,0 @@ ---- -layout: "docs" -page_title: "Provisioner: local-exec" -sidebar_current: "docs-provisioners-local" -description: |- - The `local-exec` provisioner invokes a local executable after a resource is created. This invokes a process on the machine running Terraform, not on the resource. See the `remote-exec` provisioner to run commands on the resource. ---- - -# local-exec Provisioner - -The `local-exec` provisioner invokes a local executable after a resource is -created. This invokes a process on the machine running Terraform, not on the -resource. See the `remote-exec` -[provisioner](/docs/provisioners/remote-exec.html) to run commands on the -resource. - -Note that even though the resource will be fully created when the provisioner is -run, there is no guarantee that it will be in an operable state - for example -system services such as `sshd` may not be started yet on compute resources. - --> **Note:** Provisioners should only be used as a last resort. For most -common situations there are better alternatives. For more information, see -[the main Provisioners page](./). - -## Example usage - -```hcl -resource "aws_instance" "web" { - # ... - - provisioner "local-exec" { - command = "echo ${aws_instance.web.private_ip} >> private_ips.txt" - } -} -``` - -## Argument Reference - -The following arguments are supported: - -* `command` - (Required) This is the command to execute. It can be provided - as a relative path to the current working directory or as an absolute path. - It is evaluated in a shell, and can use environment variables or Terraform - variables. - -* `working_dir` - (Optional) If provided, specifies the working directory where - `command` will be executed. It can be provided as as a relative path to the - current working directory or as an absolute path. The directory must exist. - -* `interpreter` - (Optional) If provided, this is a list of interpreter - arguments used to execute the command. The first argument is the - interpreter itself. It can be provided as a relative path to the current - working directory or as an absolute path. The remaining arguments are - appended prior to the command. This allows building command lines of the - form "/bin/bash", "-c", "echo foo". If `interpreter` is unspecified, - sensible defaults will be chosen based on the system OS. - -* `environment` - (Optional) block of key value pairs representing the - environment of the executed command. inherits the current process environment. - -### Interpreter Examples - -```hcl -resource "null_resource" "example1" { - provisioner "local-exec" { - command = "open WFH, '>completed.txt' and print WFH scalar localtime" - interpreter = ["perl", "-e"] - } -} -``` - -```hcl -resource "null_resource" "example2" { - provisioner "local-exec" { - command = "Get-Date > completed.txt" - interpreter = ["PowerShell", "-Command"] - } -} -``` - -```hcl -resource "aws_instance" "web" { - # ... - - provisioner "local-exec" { - command = "echo $FOO $BAR $BAZ >> env_vars.txt" - - environment = { - FOO = "bar" - BAR = 1 - BAZ = "true" - } - } -} -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/provisioners/null_resource.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/provisioners/null_resource.html.markdown deleted file mode 100644 index f7609ab6..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/provisioners/null_resource.html.markdown +++ /dev/null @@ -1,61 +0,0 @@ ---- -layout: "docs" -page_title: "Provisioners Without a Resource" -sidebar_current: "docs-provisioners-null-resource" -description: |- - The `null_resource` is a resource allows you to configure provisioners that - are not directly associated with a single existing resource. ---- - -# Provisioners Without a Resource - -[null]: /docs/providers/null/resource.html - -If you need to run provisioners that aren't directly associated with a specific -resource, you can associate them with a `null_resource`. - -Instances of [`null_resource`][null] are treated like normal resources, but they -don't do anything. Like with any other resource, you can configure -[provisioners](/docs/provisioners/index.html) and [connection -details](/docs/provisioners/connection.html) on a `null_resource`. You can also -use its `triggers` argument and any meta-arguments to control exactly where in -the dependency graph its provisioners will run. - -## Example usage - -```hcl -resource "aws_instance" "cluster" { - count = 3 - - # ... -} - -resource "null_resource" "cluster" { - # Changes to any instance of the cluster requires re-provisioning - triggers = { - cluster_instance_ids = "${join(",", aws_instance.cluster.*.id)}" - } - - # Bootstrap script can run on any instance of the cluster - # So we just choose the first in this case - connection { - host = "${element(aws_instance.cluster.*.public_ip, 0)}" - } - - provisioner "remote-exec" { - # Bootstrap script called with private_ip of each node in the cluster - inline = [ - "bootstrap-cluster.sh ${join(" ", aws_instance.cluster.*.private_ip)}", - ] - } -} -``` - -## Argument Reference - -In addition to meta-arguments supported by all resources, `null_resource` -supports the following specific arguments: - - * `triggers` - A map of values which should cause this set of provisioners to - re-run. Values are meant to be interpolated references to variables or - attributes of other resources. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/provisioners/puppet.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/provisioners/puppet.html.markdown deleted file mode 100644 index 4a88bd63..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/provisioners/puppet.html.markdown +++ /dev/null @@ -1,98 +0,0 @@ ---- -layout: "docs" -page_title: "Provisioner: puppet" -sidebar_current: "docs-provisioners-puppet" -description: |- - The `puppet` provisioner installs, configures and runs the Puppet agent on a resource. ---- - -# Puppet Provisioner - -The `puppet` provisioner installs, configures and runs the Puppet agent on a -remote resource. The `puppet` provisioner supports both `ssh` and `winrm` type -[connections](/docs/provisioners/connection.html). - --> **Note:** This provisioner has been deprecated as of Terraform 0.13.4 and will be -removed in a future version of Terraform. For most common situations there are better -alternatives to using provisioners. For more information, see -[the main Provisioners page](./). - -## Requirements - -The `puppet` provisioner has some prerequisites for specific connection types: - -* For `ssh` type connections, `cURL` must be available on the remote host. -* For `winrm` connections, `PowerShell 2.0` must be available on the remote host. - -Without these prerequisites, your provisioning execution will fail. - -Additionally, the `puppet` provisioner requires -[Bolt](https://puppet.com/docs/bolt/latest/bolt.html) to be installed on your workstation -with the following [modules -installed](https://puppet.com/docs/bolt/latest/bolt_installing_modules.html#install-modules) - -* `danieldreier/autosign` -* `puppetlabs/puppet_agent` - -## Example usage - -```hcl -resource "aws_instance" "web" { - # ... - - provisioner "puppet" { - server = aws_instance.puppetmaster.public_dns - server_user = "ubuntu" - extension_requests = { - pp_role = "webserver" - } - } -} -``` - -## Argument Reference - -The following arguments are supported: - -* `server (string)` - (Required) The FQDN of the Puppet master that the agent - is to connect to. - -* `server_user (string)` - (Optional) The user that Bolt should connect to the - server as (defaults to `root`). - -* `os_type (string)` - (Optional) The OS type of the resource. Valid options - are: `linux` and `windows`. If not supplied, the connection type will be used - to determine the OS type (`ssh` will assume `linux` and `winrm` will assume - `windows`). - -* `use_sudo (boolean)` - (Optional) If `true`, commands run on the resource - will have their privileges elevated with sudo (defaults to `true` when the OS - type is `linux` and `false` when the OS type is `windows`). - -* `autosign (boolean)` - (Optional) Set to `true` if the Puppet master is using an autosigner such as - [Daniel Dreier's policy-based autosigning - tool](https://danieldreier.github.io/autosign). If `false` new agent certificate requests will have to be signed manually (defaults to `true`). - -* `open_source (boolean)` - (Optional) If `true` the provisioner uses an open source Puppet compatible agent install method (push via the Bolt agent install task). If `false` the simplified Puppet Enterprise installer will pull the agent from the Puppet master (defaults to `true`). - -* `certname (string)` - (Optional) The Subject CN used when requesting - a certificate from the Puppet master CA (defaults to the FQDN of the - resource). - -* `extension_requests (map)` - (Optional) A map of [extension - requests](https://puppet.com/docs/puppet/latest/ssl_attributes_extensions.html#concept-932) - to be embedded in the certificate signing request before it is sent to the - Puppet master CA and then transferred to the final certificate when the CSR - is signed. These become available during Puppet agent runs as [trusted facts](https://puppet.com/docs/puppet/latest/lang_facts_and_builtin_vars.html#trusted-facts). Friendly names for common extensions such as pp_role and pp_environment have [been predefined](https://puppet.com/docs/puppet/latest/ssl_attributes_extensions.html#recommended-oids-for-extensions). - -* `custom_attributes (map)` - (Optional) A map of [custom - attributes](https://puppet.com/docs/puppet/latest/ssl_attributes_extensions.html#concept-5488) - to be embedded in the certificate signing request before it is sent to the - Puppet master CA. - -* `environment (string)` - (Optional) The name of the Puppet environment that the - Puppet agent will be running in (defaults to `production`). - -* `bolt_timeout (string)` - (Optional) The timeout to wait for Bolt tasks to - complete. This should be specified as a string like `30s` or `5m` (defaults - to `5m` - 5 minutes). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/provisioners/remote-exec.html.markdown b/vendor/github.com/hashicorp/terraform/website/docs/provisioners/remote-exec.html.markdown deleted file mode 100644 index 3085bb33..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/provisioners/remote-exec.html.markdown +++ /dev/null @@ -1,75 +0,0 @@ ---- -layout: "docs" -page_title: "Provisioner: remote-exec" -sidebar_current: "docs-provisioners-remote" -description: |- - The `remote-exec` provisioner invokes a script on a remote resource after it is created. This can be used to run a configuration management tool, bootstrap into a cluster, etc. To invoke a local process, see the `local-exec` provisioner instead. The `remote-exec` provisioner supports both `ssh` and `winrm` type connections. ---- - -# remote-exec Provisioner - -The `remote-exec` provisioner invokes a script on a remote resource after it -is created. This can be used to run a configuration management tool, bootstrap -into a cluster, etc. To invoke a local process, see the `local-exec` -[provisioner](/docs/provisioners/local-exec.html) instead. The `remote-exec` -provisioner supports both `ssh` and `winrm` type [connections](/docs/provisioners/connection.html). - --> **Note:** Provisioners should only be used as a last resort. For most -common situations there are better alternatives. For more information, see -[the main Provisioners page](./). - -## Example usage - -```hcl -resource "aws_instance" "web" { - # ... - - provisioner "remote-exec" { - inline = [ - "puppet apply", - "consul join ${aws_instance.web.private_ip}", - ] - } -} -``` - -## Argument Reference - -The following arguments are supported: - -* `inline` - This is a list of command strings. They are executed in the order - they are provided. This cannot be provided with `script` or `scripts`. - -* `script` - This is a path (relative or absolute) to a local script that will - be copied to the remote resource and then executed. This cannot be provided - with `inline` or `scripts`. - -* `scripts` - This is a list of paths (relative or absolute) to local scripts - that will be copied to the remote resource and then executed. They are executed - in the order they are provided. This cannot be provided with `inline` or `script`. - -## Script Arguments - -You cannot pass any arguments to scripts using the `script` or -`scripts` arguments to this provisioner. If you want to specify arguments, -upload the script with the -[file provisioner](/docs/provisioners/file.html) -and then use `inline` to call it. Example: - -```hcl -resource "aws_instance" "web" { - # ... - - provisioner "file" { - source = "script.sh" - destination = "/tmp/script.sh" - } - - provisioner "remote-exec" { - inline = [ - "chmod +x /tmp/script.sh", - "/tmp/script.sh args", - ] - } -} -``` diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/api.html.md b/vendor/github.com/hashicorp/terraform/website/docs/registry/api.html.md deleted file mode 100644 index 18d2644e..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/registry/api.html.md +++ /dev/null @@ -1,771 +0,0 @@ ---- -layout: "registry" -page_title: "Terraform Registry - HTTP API" -sidebar_current: "docs-registry-api" -description: |- - The /acl endpoints create, update, destroy, and query ACL tokens in Consul. ---- - -# HTTP API - -When downloading modules from registry sources such as the public -[Terraform Registry](https://registry.terraform.io/), Terraform CLI expects -the given hostname to support -[the module registry protocol](/docs/internals/module-registry-protocol.html), -which is the minimal API required for Terraform CLI to successfully retrieve -a module. - -The public Terraform Registry and the private registry included in Terraform -Cloud and Terraform Enterprise implement a superset of that minimal module -registry API to support additional use-cases such as searching for modules -across the whole registry, retrieving documentation and schemas for modules, -and so on. - -This page describes the extended API implemented by the official module -registry implementations, and is aimed at those intending to build clients -to work with registry data. Third-party implementations of the registry -protocol are not required to implement these extensions. If you intend to -implement your own module registry, please refer to -[the module registry protocol](/docs/internals/module-registry-protocol.html) -instead. - -Terraform Registry also has some additional internal API endpoints used to -support its UI. Any endpoints or properties not documented on this page are -subject to change over time. - -## Service Discovery - -The hostname portion of a module source address is first passed to -[the service discovery protocol](/docs/internals/remote-service-discovery.html) -to determine if the given host has a module registry and, if so, the base -URL for its module registry endpoints. - -The service identifier for this protocol is `modules.v1`, and the declared -URL should always end with a slash such that the paths shown in the following -sections can be appended to it. - -For example, if discovery produces the URL `https://modules.example.com/v1/` -then this API would use full endpoint URLs like -`https://modules.example.com/v1/{namespace}/{name}/{provider}/versions`. - -A module source address with no hostname is a shorthand for an address -on `registry.terraform.io`. You can perform service discovery on that hostname -to find the public Terraform Registry's module API endpoints. - -## Base URL - -The example request URLs shown in this document are for the public [Terraform -Registry](https://registry.terraform.io), and use its API `` of -`https://registry.terraform.io/v1/modules/`. Note that although the base URL in -the [discovery document](#service-discovery) _may include_ a trailing slash, we -include a slash after the placeholder in the `Path`s below for clarity. - -## List Modules - -These endpoints list modules according to some criteria. - -| Method | Path | Produces | -| ------ | ------------------------------------- | -------------------------- | -| `GET` | `` | `application/json` | -| `GET` | `/:namespace` | `application/json` | - -### Parameters - -- `namespace` `(string: )` - Restricts listing to modules published by - this user or organization. This is optionally specified as part of the URL - path. - -### Query Parameters - -- `offset`, `limit` `(int: )` - See [Pagination](#pagination) for details. -- `provider` `(string: )` - Limits modules to a specific provider. -- `verified` `(bool: )` - If `true`, limits results to only verified - modules. Any other value including none returns all modules _including_ - verified ones. - -### Sample Request - -```text -$ curl 'https://registry.terraform.io/v1/modules?limit=2&verified=true' -``` - -### Sample Response - -```json -{ - "meta": { - "limit": 2, - "current_offset": 0, - "next_offset": 2, - "next_url": "/v1/modules?limit=2&offset=2&verified=true" - }, - "modules": [ - { - "id": "GoogleCloudPlatform/lb-http/google/1.0.4", - "owner": "", - "namespace": "GoogleCloudPlatform", - "name": "lb-http", - "version": "1.0.4", - "provider": "google", - "description": "Modular Global HTTP Load Balancer for GCE using forwarding rules.", - "source": "https://github.com/GoogleCloudPlatform/terraform-google-lb-http", - "published_at": "2017-10-17T01:22:17.792066Z", - "downloads": 213, - "verified": true - }, - { - "id": "terraform-aws-modules/vpc/aws/1.5.1", - "owner": "", - "namespace": "terraform-aws-modules", - "name": "vpc", - "version": "1.5.1", - "provider": "aws", - "description": "Terraform module which creates VPC resources on AWS", - "source": "https://github.com/terraform-aws-modules/terraform-aws-vpc", - "published_at": "2017-11-23T10:48:09.400166Z", - "downloads": 29714, - "verified": true - } - ] -} -``` - -## Search Modules - -This endpoint allows searching modules. - -| Method | Path | Produces | -| ------ | ------------------------------------- | -------------------------- | -| `GET` | `/search` | `application/json` | - -### Query Parameters - -- `q` `(string: )` - The search string. Search syntax understood - depends on registry implementation. The public registry supports basic keyword - or phrase searches. -- `offset`, `limit` `(int: )` - See [Pagination](#pagination) for details. -- `provider` `(string: )` - Limits results to a specific provider. -- `namespace` `(string: )` - Limits results to a specific namespace. -- `verified` `(bool: )` - If `true`, limits results to only verified - modules. Any other value including none returns all modules _including_ - verified ones. - -### Sample Request - -```text -$ curl 'https://registry.terraform.io/v1/modules/search?q=network&limit=2' -``` - -### Sample Response - -```json -{ - "meta": { - "limit": 2, - "current_offset": 0, - "next_offset": 2, - "next_url": "/v1/modules/search?limit=2&offset=2&q=network" - }, - "modules": [ - { - "id": "zoitech/network/aws/0.0.3", - "owner": "", - "namespace": "zoitech", - "name": "network", - "version": "0.0.3", - "provider": "aws", - "description": "This module is intended to be used for configuring an AWS network.", - "source": "https://github.com/zoitech/terraform-aws-network", - "published_at": "2017-11-23T15:12:06.620059Z", - "downloads": 39, - "verified": false - }, - { - "id": "Azure/network/azurerm/1.1.1", - "owner": "", - "namespace": "Azure", - "name": "network", - "version": "1.1.1", - "provider": "azurerm", - "description": "Terraform Azure RM Module for Network", - "source": "https://github.com/Azure/terraform-azurerm-network", - "published_at": "2017-11-22T17:15:34.325436Z", - "downloads": 1033, - "verified": true - } - ] -} -``` - -## List Available Versions for a Specific Module - -This is the primary endpoint for resolving module sources, returning the -available versions for a given fully-qualified module. - -| Method | Path | Produces | -| ------ | ------------------------------------- | -------------------------- | -| `GET` | `/:namespace/:name/:provider/versions` | `application/json` | - -### Parameters - -- `namespace` `(string: )` - The user or organization the module is - owned by. This is required and is specified as part of the URL path. - -- `name` `(string: )` - The name of the module. - This is required and is specified as part of the URL path. - -- `provider` `(string: )` - The name of the provider. - This is required and is specified as part of the URL path. - -### Sample Request - -```text -$ curl https://registry.terraform.io/v1/modules/hashicorp/consul/aws/versions -``` - -### Sample Response - -The `modules` array in the response always includes the requested module as the -first element. Other elements of this list, if present, are dependencies of the -requested module that are provided to potentially avoid additional requests to -resolve these modules. - -Additional modules are not required to be provided but, when present, can be -used by Terraform to optimize the module installation process. - -Each returned module has an array of available versions, which Terraform -matches against any version constraints given in configuration. - -```json -{ - "modules": [ - { - "source": "hashicorp/consul/aws", - "versions": [ - { - "version": "0.0.1", - "submodules" : [ - { - "path": "modules/consul-cluster", - "providers": [ - { - "name": "aws", - "version": "" - } - ], - "dependencies": [] - }, - { - "path": "modules/consul-security-group-rules", - "providers": [ - { - "name": "aws", - "version": "" - } - ], - "dependencies": [] - }, - { - "providers": [ - { - "name": "aws", - "version": "" - } - ], - "dependencies": [], - "path": "modules/consul-iam-policies" - } - ], - "root": { - "dependencies": [], - "providers": [ - { - "name": "template", - "version": "" - }, - { - "name": "aws", - "version": "" - } - ] - } - } - ] - } - ] -} -``` - -## Download Source Code for a Specific Module Version - -This endpoint downloads the specified version of a module for a single provider. - -A successful response has no body, and includes the location from which the module -version's source can be downloaded in the `X-Terraform-Get` header. Note that -this string may contain special syntax interpreted by Terraform via -[`go-getter`](https://github.com/hashicorp/go-getter). See the [`go-getter` -documentation](https://github.com/hashicorp/go-getter#url-format) for details. - -The value of `X-Terraform-Get` may instead be a relative URL, indicated by -beginning with `/`, `./` or `../`, in which case it is resolved relative to -the full URL of the download endpoint. - -| Method | Path | Produces | -| ------ | ---------------------------- | -------------------------- | -| `GET` | `/:namespace/:name/:provider/:version/download` | `application/json` | - -### Parameters - -- `namespace` `(string: )` - The user the module is owned by. - This is required and is specified as part of the URL path. - -- `name` `(string: )` - The name of the module. - This is required and is specified as part of the URL path. - -- `provider` `(string: )` - The name of the provider. - This is required and is specified as part of the URL path. - -- `version` `(string: )` - The version of the module. - This is required and is specified as part of the URL path. - -### Sample Request - -```text -$ curl -i \ - https://registry.terraform.io/v1/modules/hashicorp/consul/aws/0.0.1/download -``` - -### Sample Response - -```text -HTTP/1.1 204 No Content -Content-Length: 0 -X-Terraform-Get: https://api.github.com/repos/hashicorp/terraform-aws-consul/tarball/v0.0.1//*?archive=tar.gz -``` - -## List Latest Version of Module for All Providers - -This endpoint returns the latest version of each provider for a module. - -| Method | Path | Produces | -| ------ | ---------------------------- | -------------------------- | -| `GET` | `/:namespace/:name` | `application/json` | - -### Parameters - -- `namespace` `(string: )` - The user or organization the module is - owned by. This is required and is specified as part of the URL path. - -- `name` `(string: )` - The name of the module. - This is required and is specified as part of the URL path. - -### Query Parameters - -- `offset`, `limit` `(int: )` - See [Pagination](#pagination) for details. - -### Sample Request - -```text -$ curl \ - https://registry.terraform.io/v1/modules/hashicorp/consul -``` - -### Sample Response - -```json -{ - "meta": { - "limit": 15, - "current_offset": 0 - }, - "modules": [ - { - "id": "hashicorp/consul/azurerm/0.0.1", - "owner": "gruntwork-team", - "namespace": "hashicorp", - "name": "consul", - "version": "0.0.1", - "provider": "azurerm", - "description": "A Terraform Module for how to run Consul on AzureRM using Terraform and Packer", - "source": "https://github.com/hashicorp/terraform-azurerm-consul", - "published_at": "2017-09-14T23:22:59.923047Z", - "downloads": 100, - "verified": false - }, - { - "id": "hashicorp/consul/aws/0.0.1", - "owner": "gruntwork-team", - "namespace": "hashicorp", - "name": "consul", - "version": "0.0.1", - "provider": "aws", - "description": "A Terraform Module for how to run Consul on AWS using Terraform and Packer", - "source": "https://github.com/hashicorp/terraform-aws-consul", - "published_at": "2017-09-14T23:22:44.793647Z", - "downloads": 113, - "verified": false - } - ] -} -``` - -## Latest Version for a Specific Module Provider - -This endpoint returns the latest version of a module for a single provider. - -| Method | Path | Produces | -| ------ | ---------------------------- | -------------------------- | -| `GET` | `/:namespace/:name/:provider` | `application/json` | - -### Parameters - -- `namespace` `(string: )` - The user the module is owned by. - This is required and is specified as part of the URL path. - -- `name` `(string: )` - The name of the module. - This is required and is specified as part of the URL path. - -- `provider` `(string: )` - The name of the provider. - This is required and is specified as part of the URL path. - -### Sample Request - -```text -$ curl \ - https://registry.terraform.io/v1/modules/hashicorp/consul/aws -``` - -### Sample Response - -Note this response has has some fields trimmed for clarity. - -```json -{ - "id": "hashicorp/consul/aws/0.0.1", - "owner": "gruntwork-team", - "namespace": "hashicorp", - "name": "consul", - "version": "0.0.1", - "provider": "aws", - "description": "A Terraform Module for how to run Consul on AWS using Terraform and Packer", - "source": "https://github.com/hashicorp/terraform-aws-consul", - "published_at": "2017-09-14T23:22:44.793647Z", - "downloads": 113, - "verified": false, - "root": { - "path": "", - "readme": "# Consul AWS Module\n\nThis repo contains a Module for how to deploy a [Consul]...", - "empty": false, - "inputs": [ - { - "name": "ami_id", - "description": "The ID of the AMI to run in the cluster. ...", - "default": "\"\"" - }, - { - "name": "aws_region", - "description": "The AWS region to deploy into (e.g. us-east-1).", - "default": "\"us-east-1\"" - } - ], - "outputs": [ - { - "name": "num_servers", - "description": "" - }, - { - "name": "asg_name_servers", - "description": "" - } - ], - "dependencies": [], - "resources": [] - }, - "submodules": [ - { - "path": "modules/consul-cluster", - "readme": "# Consul Cluster\n\nThis folder contains a [Terraform](https://www.terraform.io/) ...", - "empty": false, - "inputs": [ - { - "name": "cluster_name", - "description": "The name of the Consul cluster (e.g. consul-stage). This variable is used to namespace all resources created by this module.", - "default": "" - }, - { - "name": "ami_id", - "description": "The ID of the AMI to run in this cluster. Should be an AMI that had Consul installed and configured by the install-consul module.", - "default": "" - } - ], - "outputs": [ - { - "name": "asg_name", - "description": "" - }, - { - "name": "cluster_size", - "description": "" - } - ], - "dependencies": [], - "resources": [ - { - "name": "autoscaling_group", - "type": "aws_autoscaling_group" - }, - { - "name": "launch_configuration", - "type": "aws_launch_configuration" - } - ] - } - ], - "providers": [ - "aws", - "azurerm" - ], - "versions": [ - "0.0.1" - ] -} -``` - -## Get a Specific Module - -This endpoint returns the specified version of a module for a single provider. - -| Method | Path | Produces | -| ------ | ---------------------------- | -------------------------- | -| `GET` | `/:namespace/:name/:provider/:version` | `application/json` | - -### Parameters - -- `namespace` `(string: )` - The user the module is owned by. - This is required and is specified as part of the URL path. - -- `name` `(string: )` - The name of the module. - This is required and is specified as part of the URL path. - -- `provider` `(string: )` - The name of the provider. - This is required and is specified as part of the URL path. - -- `version` `(string: )` - The version of the module. - This is required and is specified as part of the URL path. - -### Sample Request - -```text -$ curl \ - https://registry.terraform.io/v1/modules/hashicorp/consul/aws/0.0.1 -``` - -### Sample Response - -Note this response has has some fields trimmed for clarity. - - -```json -{ - "id": "hashicorp/consul/aws/0.0.1", - "owner": "gruntwork-team", - "namespace": "hashicorp", - "name": "consul", - "version": "0.0.1", - "provider": "aws", - "description": "A Terraform Module for how to run Consul on AWS using Terraform and Packer", - "source": "https://github.com/hashicorp/terraform-aws-consul", - "published_at": "2017-09-14T23:22:44.793647Z", - "downloads": 113, - "verified": false, - "root": { - "path": "", - "readme": "# Consul AWS Module\n\nThis repo contains a Module for how to deploy a [Consul]...", - "empty": false, - "inputs": [ - { - "name": "ami_id", - "description": "The ID of the AMI to run in the cluster. ...", - "default": "\"\"" - }, - { - "name": "aws_region", - "description": "The AWS region to deploy into (e.g. us-east-1).", - "default": "\"us-east-1\"" - } - ], - "outputs": [ - { - "name": "num_servers", - "description": "" - }, - { - "name": "asg_name_servers", - "description": "" - } - ], - "dependencies": [], - "resources": [] - }, - "submodules": [ - { - "path": "modules/consul-cluster", - "readme": "# Consul Cluster\n\nThis folder contains a [Terraform](https://www.terraform.io/) ...", - "empty": false, - "inputs": [ - { - "name": "cluster_name", - "description": "The name of the Consul cluster (e.g. consul-stage). This variable is used to namespace all resources created by this module.", - "default": "" - }, - { - "name": "ami_id", - "description": "The ID of the AMI to run in this cluster. Should be an AMI that had Consul installed and configured by the install-consul module.", - "default": "" - } - ], - "outputs": [ - { - "name": "asg_name", - "description": "" - }, - { - "name": "cluster_size", - "description": "" - } - ], - "dependencies": [], - "resources": [ - { - "name": "autoscaling_group", - "type": "aws_autoscaling_group" - }, - { - "name": "launch_configuration", - "type": "aws_launch_configuration" - } - ] - } - ], - "providers": [ - "aws", - "azurerm" - ], - "versions": [ - "0.0.1" - ] -} -``` - -## Download the Latest Version of a Module - -This endpoint downloads the latest version of a module for a single provider. - -It returns a 302 redirect whose `Location` header redirects the client to the -download endpoint (above) for the latest version. - -| Method | Path | Produces | -| ------ | ---------------------------- | -------------------------- | -| `GET` | `/:namespace/:name/:provider/download` | `application/json` | - -### Parameters - -- `namespace` `(string: )` - The user the module is owned by. - This is required and is specified as part of the URL path. - -- `name` `(string: )` - The name of the module. - This is required and is specified as part of the URL path. - -- `provider` `(string: )` - The name of the provider. - This is required and is specified as part of the URL path. - -### Sample Request - -```text -$ curl -i \ - https://registry.terraform.io/v1/modules/hashicorp/consul/aws/download -``` - -### Sample Response - -```text -HTTP/1.1 302 Found -Location: /v1/modules/hashicorp/consul/aws/0.0.1/download -Content-Length: 70 -Content-Type: text/html; charset=utf-8 - -Found. -``` - -## HTTP Status Codes - -The API follows regular HTTP status semantics. To make implementing a complete -client easier, some details on our policy and potential future status codes are -listed below. A robust client should consider how to handle all of the -following. - - - **Success:** Return status is `200` on success with a body or `204` if there - is no body data to return. - - **Redirects:** Moved or aliased endpoints redirect with a `301`. Endpoints - redirecting to the latest version of a module may redirect with `302` or - `307` to indicate that they logically point to different resources over time. - - **Client Errors:** Invalid requests will receive the relevant `4xx` status. - Except where noted below, the request should not be retried. - - **Rate Limiting:** Clients placing excessive load on the service might be - rate-limited and receive a `429` code. This should be interpreted as a sign - to slow down, and wait some time before retrying the request. - - **Service Errors:** The usual `5xx` errors will be returned for service - failures. In all cases it is safe to retry the request after receiving a - `5xx` response. - - **Load Shedding:** A `503` response indicates that the service is under load - and can't process your request immediately. As with other `5xx` errors you - may retry after some delay, although clients should consider being more - lenient with retry schedule in this case. - -## Error Responses - -When a `4xx` or `5xx` status code is returned. The response payload will look -like the following example: - -```json -{ - "errors": [ - "something bad happened" - ] -} -``` - -The `errors` key is a list containing one or more strings where each string -describes an error that has occurred. - -Note that it is possible that some `5xx` errors might result in a response that -is not in JSON format above due to being returned by an intermediate proxy. - -## Pagination - -Endpoints that return lists of results use a common pagination format. - -They accept positive integer query variables `offset` and `limit` which have the -usual SQL-like semantics. Each endpoint will have a default limit and a -default offset of `0`. Each endpoint will also apply a maximum limit, -requesting more results will just result in the maximum limit being used. - -The response for a paginated result set will look like: - -```json -{ - "meta": { - "limit": 15, - "current_offset": 15, - "next_offset": 30, - "prev_offset": 0, - }, - "": [] -} -``` -Note that: - - `next_offset` will only be present if there are more results available. - - `prev_offset` will only be present if not at `offset = 0`. - - `limit` is the actual limit that was applied, it may be lower than the requested limit param. - - The key for the result array varies based on the endpoint and will be the - type of result pluralized, for example `modules`. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/images/registry-issue.png b/vendor/github.com/hashicorp/terraform/website/docs/registry/images/registry-issue.png deleted file mode 100644 index 6d7c5fff..00000000 Binary files a/vendor/github.com/hashicorp/terraform/website/docs/registry/images/registry-issue.png and /dev/null differ diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/images/registry1.png b/vendor/github.com/hashicorp/terraform/website/docs/registry/images/registry1.png deleted file mode 100644 index 3bae1463..00000000 Binary files a/vendor/github.com/hashicorp/terraform/website/docs/registry/images/registry1.png and /dev/null differ diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/images/registry2.png b/vendor/github.com/hashicorp/terraform/website/docs/registry/images/registry2.png deleted file mode 100644 index e1e56f03..00000000 Binary files a/vendor/github.com/hashicorp/terraform/website/docs/registry/images/registry2.png and /dev/null differ diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/images/user-account.png b/vendor/github.com/hashicorp/terraform/website/docs/registry/images/user-account.png deleted file mode 100644 index 06d4cfab..00000000 Binary files a/vendor/github.com/hashicorp/terraform/website/docs/registry/images/user-account.png and /dev/null differ diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/registry/index.html.md deleted file mode 100644 index 863063d4..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/registry/index.html.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -layout: "registry" -page_title: "Terraform Registry" -sidebar_current: "docs-registry-home" -description: |- - The Terraform Registry is a repository of providers and modules written by the Terraform community. ---- - -# Terraform Registry - -The [Terraform Registry](https://registry.terraform.io) is an interactive resource for discovering a wide selection of integrations (providers) and configuration packages (modules) for use with Terraform. The Registry includes solutions developed by HashiCorp, third-party vendors, and our Terraform community. Our goal with the Registry is to provide plugins to manage any infrastructure API, pre-made modules to quickly configure common infrastructure components, and examples of how to write quality Terraform code. - -![screenshot: terraform registry landing page](./images/registry1.png) - -The Terraform Registry is integrated [directly into Terraform](/docs/configuration/provider-requirements.html) to make it easy to use providers and modules. Anyone can publish and consume providers and modules on the public [Terraform Registry](https://registry.terraform.io). (To publish private modules within your organization, you can use a [private registry](/docs/registry/private.html) or [reference repositories and other sources directly](/docs/modules/sources.html).) - -Use the navigation to the left to learn more about using the Terraform Registry. - -## Navigating the Registry - -As the Terraform ecosystem continues to grow, the Registry is designed to make it easy to discover integrations and solutions across dozens of categories. Select a provider or module card to learn more, filter results to a [specific tier](./providers/index.html#provider-tiers-amp-namespaces), or use the search field at the top of the Registry to find what you’re looking for. (Note that search supports keyboard navigation.) - -![screenshot: terraform registry browse](./images/registry2.png) - -## User Account - -Anyone interested in publishing a provider or module can create an account and sign in to the Terraform Registry using a GitHub account. Click the "Sign-in" button, and follow the login prompts. Once you have authorized the use of your GitHub account and are signed in, you can publish both providers and modules directly from one of the repositories you manage. To learn more, see [Publishing to the Registry](/docs/registry/providers/publishing.html). - -![screenshot: terraform registry sign in](./images/user-account.png) - -## Getting Help - -We welcome any feedback about using or publishing to the Registry. Please reach out if you have any questions or issues with the Terraform Registry by sending us an [email](mailto:terraform-registry-beta@hashicorp.com). The providers and modules in The Terraform Registry are published and maintained either directly by HashiCorp, by trusted HashiCorp partners, or by members of the Terraform community ([see tiers & namespaces](./providers/index.html#provider-tiers-amp-namespaces)). If you run into issues or have additional contributions to make to a provider or module, you can submit a GitHub issue by selecting the "Report an issue" link on the detail view: - -![Provider report issue link](./images/registry-issue.png) diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/modules/images/registry-verified.png b/vendor/github.com/hashicorp/terraform/website/docs/registry/modules/images/registry-verified.png deleted file mode 100644 index 806eb71a..00000000 Binary files a/vendor/github.com/hashicorp/terraform/website/docs/registry/modules/images/registry-verified.png and /dev/null differ diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/modules/publish.html.md b/vendor/github.com/hashicorp/terraform/website/docs/registry/modules/publish.html.md deleted file mode 100644 index 9c343284..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/registry/modules/publish.html.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -layout: "registry" -page_title: "Terraform Registry - Publishing Modules" -sidebar_current: "docs-registry-publish" -description: |- - Anyone can publish and share modules on the Terraform Registry. ---- - -# Publishing Modules - -Anyone can publish and share modules on the [Terraform Registry](https://registry.terraform.io). - -Published modules support versioning, automatically generate documentation, -allow browsing version histories, show examples and READMEs, and more. We -recommend publishing reusable modules to a registry. - -Public modules are managed via Git and GitHub. Publishing a module takes only -a few minutes. Once a module is published, you can release a new version of -a module by simply pushing a properly formed Git tag. - -The registry extracts information about the module from the module's source. -The module name, provider, documentation, inputs/outputs, and dependencies are -all parsed and available via the UI or API, as well as the same information for -any submodules or examples in the module's source repository. - -## Requirements - -The list below contains all the requirements for publishing a module. -Meeting the requirements for publishing a module is extremely easy. The -list may appear long only to ensure we're detailed, but adhering to the -requirements should happen naturally. - -- **GitHub.** The module must be on GitHub and must be a public repo. -This is only a requirement for the [public registry](https://registry.terraform.io). -If you're using a private registry, you may ignore this requirement. - -- **Named `terraform--`.** Module repositories must use this -three-part name format, where `` reflects the type of infrastructure the -module manages and `` is the main provider where it creates that -infrastructure. The `` segment can contain additional hyphens. Examples: -`terraform-google-vault` or `terraform-aws-ec2-instance`. - -- **Repository description.** The GitHub repository description is used -to populate the short description of the module. This should be a simple -one sentence description of the module. - -- **Standard module structure.** The module must adhere to the -[standard module structure](/docs/modules/index.html#standard-module-structure). -This allows the registry to inspect your module and generate documentation, -track resource usage, parse submodules and examples, and more. - -- **`x.y.z` tags for releases.** The registry uses tags to identify module -versions. Release tag names must be a [semantic version](http://semver.org), -which can optionally be prefixed with a `v`. For example, `v1.0.4` and `0.9.2`. -To publish a module initially, at least one release tag must be present. Tags -that don't look like version numbers are ignored. - -## Publishing a Public Module - -With the requirements met, you can publish a public module by going to -the [Terraform Registry](https://registry.terraform.io) and clicking the -"Upload" link in the top navigation. - -If you're not signed in, this will ask you to connect with GitHub. We only -ask for access to public repositories, since the public registry may only -publish public modules. We require access to hooks so we can register a webhook -with your repository. We require access to your email address so that we can -email you alerts about your module. We will not spam you. - -The upload page will list your available repositories, filtered to those that -match the [naming convention described above](#Requirements). This is shown in -the screenshot below. Select the repository of the module you want to add and -click "Publish Module." - -In a few seconds, your module will be created. - -![Publish Module flow animation](/assets/images/docs/registry-publish.gif) - -## Releasing New Versions - -The Terraform Registry uses tags to detect releases. - -Tag names must be a valid [semantic version](http://semver.org), optionally -prefixed with a `v`. Example of valid tags are: `v1.0.1` and `0.9.4`. To publish -a new module, you must already have at least one tag created. - -To release a new version, create and push a new tag with the proper format. -The webhook will notify the registry of the new version and it will appear -on the registry usually in less than a minute. - -If your version doesn't appear properly, you may force a sync with GitHub -by viewing your module on the registry and clicking "Resync Module" -under the "Manage Module" dropdown. This process may take a few minutes. -Please only do this if you do not see the version appear, since it will -cause the registry to resync _all versions_ of your module. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/modules/use.html.md b/vendor/github.com/hashicorp/terraform/website/docs/registry/modules/use.html.md deleted file mode 100644 index dcd60211..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/registry/modules/use.html.md +++ /dev/null @@ -1,84 +0,0 @@ ---- -layout: "registry" -page_title: "Finding and Using Modules from the Terraform Registry" -sidebar_current: "docs-registry-use" -description: |- - The Terraform Registry makes it simple to find and use modules. ---- - -# Finding and Using Modules - -The [Terraform Registry](https://registry.terraform.io) makes it simple to -find and use modules. - -## Finding Modules - -Every page on the registry has a search field for finding -modules. Enter any type of module you're looking for (examples: "vault", -"vpc", "database") and resulting modules will be listed. The search query -will look at module name, provider, and description to match your search -terms. On the results page, filters can be used further refine search results. - -By default, only [verified modules](/docs/registry/modules/verified.html) -are shown in search results. Verified modules are reviewed by HashiCorp to -ensure stability and compatibility. By using the filters, you can view unverified -modules as well. - -## Using Modules - -The Terraform Registry is integrated directly into Terraform. This makes -it easy to reference any module in the registry. The syntax for referencing -a registry module is `//`. For example: -`hashicorp/consul/aws`. - -~> **Note:** Module registry integration was added in Terraform v0.10.6, and full versioning support in v0.11.0. - -When viewing a module on the registry on a tablet or desktop, usage instructions -are shown on the right side. -You can copy and paste this to get started with any module. Some modules -have required inputs you must set before being able to use the module. - -```hcl -module "consul" { - source = "hashicorp/consul/aws" - version = "0.1.0" -} -``` - -The `terraform init` command will download and cache any modules referenced by -a configuration. - -### Private Registry Module Sources - -You can also use modules from a private registry, like the one provided by -Terraform Cloud. Private registry modules have source strings of the form -`///`. This is the same format as the -public registry, but with an added hostname prefix. - -```hcl -module "vpc" { - source = "app.terraform.io/example_corp/vpc/aws" - version = "0.9.3" -} -``` - -Depending on the registry you're using, you might also need to configure -credentials to access modules. See your registry's documentation for details. -[Terraform Cloud's private registry is documented here.](/docs/cloud/registry/index.html) - -Private registry module sources are supported in Terraform v0.11.0 and -newer. - -## Module Versions - -Each module in the registry is versioned. These versions syntactically must -follow [semantic versioning](http://semver.org/). In addition to pure syntax, -we encourage all modules to follow the full guidelines of semantic versioning. - -Terraform since version 0.11 will resolve any provided -[module version constraints](/docs/configuration/modules.html#module-versions) and -using them is highly recommended to avoid pulling in breaking changes. - -Terraform versions after 0.10.6 but before 0.11 have partial support for the registry -protocol, but always download the latest version instead of honoring version -constraints. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/modules/verified.html.md b/vendor/github.com/hashicorp/terraform/website/docs/registry/modules/verified.html.md deleted file mode 100644 index 357770f9..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/registry/modules/verified.html.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -layout: "registry" -page_title: "Terraform Registry - Verified Modules" -sidebar_current: "docs-registry-verified" -description: |- - Verified modules are reviewed by HashiCorp and actively maintained by contributors to stay up-to-date and compatible with both Terraform and their respective providers. ---- - -# Verified Modules - -Verified modules are reviewed by HashiCorp and actively maintained by contributors to stay up-to-date and compatible with both Terraform and their respective providers. - -The verified badge appears next to modules that are published by a verified source. - -![Verified module listing](./images/registry-verified.png) - -Verified modules are expected to be actively maintained by HashiCorp partners. -The verified badge isn’t indicative of flexibility or feature support; very -simple modules can be verified just because they're great examples of modules. -Likewise, an unverified module could be extremely high quality and actively -maintained. An unverified module shouldn't be assumed to be poor quality, it -only means it hasn't been created by a HashiCorp partner. - -When [using registry modules](/docs/registry/modules/use.html), there is no -difference between a verified and unverified module; they are used the same -way. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/private.html.md b/vendor/github.com/hashicorp/terraform/website/docs/registry/private.html.md deleted file mode 100644 index 80a8b750..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/registry/private.html.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -layout: "registry" -page_title: "Terraform Registry - Private Registry" -sidebar_current: "docs-registry-private" -description: |- - Terraform can load private modules from private registries via Terraform Cloud. ---- - -# Private Registries - -The registry at [registry.terraform.io](https://registry.terraform.io) -only hosts public modules, but most organizations have some modules that -can't, shouldn't, or don't need to be public. - -You can load private modules [directly from version control and other -sources](/docs/modules/sources.html), but those sources don't support [version -constraints](/docs/configuration/modules.html#module-versions) or a browsable -marketplace of modules, both of which are important for enabling a -producers-and-consumers content model in a large organization. - -If your organization is specialized enough that teams frequently use modules -created by other teams, you will benefit from a private module registry. - -## Terraform Cloud's Private Registry - -[Terraform Cloud](https://www.hashicorp.com/products/terraform) -includes a private module registry. It is available to all accounts, including free organizations. - -It uses the same VCS-backed tagged release workflow as the Terraform Registry, -but imports modules from your private VCS repos (on any of Terraform Cloud's supported VCS -providers) instead of requiring public GitHub repos. You can seamlessly -reference private modules in your Terraform configurations (just include a -hostname in the module source), and Terraform Cloud's UI provides a searchable marketplace -of private modules to help your users find the code they need. - -[Terraform Cloud's private module registry is documented here.](/docs/cloud/registry/index.html) - -## Other Private Registries - -Terraform can use versioned modules from any service that implements -[the registry API](/docs/registry/api.html). -The Terraform open source project does not provide a server implementation, but -we welcome community members to create their own private registries by following -the published protocol. - diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/docs.html.md b/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/docs.html.md deleted file mode 100644 index cdbad83f..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/docs.html.md +++ /dev/null @@ -1,252 +0,0 @@ ---- -layout: "registry" -page_title: "Terraform Registry - Provider Documentation" -sidebar_current: "docs-registry-provider-docs" -description: |- - Expected document structure for publishing providers to the Terraform Registry. ---- - -# Provider Documentation - -The [Terraform Registry][terraform-registry] displays documentation for the providers it hosts. This page describes the expected format for provider documentation. - --> In order to test how documents will render in the Terraform Registry, you can use the [Terraform Registry Doc Preview Tool](https://registry.terraform.io/tools/doc-preview). - -## Publishing - -The Terraform Registry publishes providers from their Git repositories, creating a version for each Git tag that matches the [Semver](https://semver.org/) versioning format. Provider documentation is published automatically as part of the provider release process. - -Provider documentation is always tied to a provider version. A given version always displays the documentation from that version's Git commit, and the only way to publish updated documentation is to release a new version of the provider. - -### Storage Limits - -The maximum number of documents allowed for a single provider version is 1000. - -Each document can contain no more than 500KB of data. Documents which exceed this limit will be truncated, and a note will be displayed in the Terraform Registry. - -## Format - -Provider documentation should be a directory of Markdown documents in the provider repository. Each Markdown document is rendered as a separate page. The directory should include a document for the provider index, a document for each resource and data source, and optional documents for any guides. - -### Directory Structure - -| Location | Filename | Description | -|-|-|-| -| `docs/` | `index.md` | Index page for the provider. | -| `docs/guides/` | `.md` | Additional documentation for guides. | -| `docs/resources/` | `.md` | Information for a Resource. Filename should not include a `_` prefix. | -| `docs/data-sources/` | `.md` | Information on a provider data source. | - --> **Note:** In order to support provider docs which have already been formatted for publishing to [terraform.io][terraform-io-providers], the Terraform Registry also supports docs in a `website/docs/` legacy directory with file extensions of `.html.markdown` or `.html.md`. - -### Headers - -We strongly suggest that provider docs include the following sections to help users understand how to use the provider. Create additional sections if they would enhance usability of the resource (for example, “Imports” or “Customizable Timeouts”). - -#### Index Headers - - # Provider - - Summary of what the provider is for, including use cases and links to - app/service documentation. - - ## Example Usage - - ```hcl - // Code block with an example of how to use this provider. - ``` - - ## Argument Reference - - * List any arguments for the provider block. - -#### Resource/Data Source Headers - - # Resource/Data Source - - Description of what this resource does, with links to official - app/service documentation. - - ## Example Usage - - ```hcl - // Code block with an example of how to use this resource. - ``` - - ## Argument Reference - - * `attribute_name` - (Optional/Required) List arguments this resource takes. - - ## Attribute Reference - - * `attribute_name` - List attributes that this resource exports. - -### YAML Frontmatter - -Markdown source files may contain YAML frontmatter, which provides organizational information and display hints. Frontmatter can be omitted for resources and data sources that don't require a subcategory. - -Frontmatter is not rendered in the Terraform Registry web UI. - -#### Example - -```markdown ---- -page_title: "Authenticating with Foo Service via OAuth" -subcategory: "Authentication" ---- -``` - -#### Supported Attributes - -The following frontmatter attributes are supported by the Terraform Registry: - -* **page_title** - The title of this document, which will display in the docs navigation. This is only required for documents in the `guides/` folder. -* **subcategory** - An optional additional layer of grouping that affects the display of the docs navigation; [see Subcategories below](#subcategories) for more details. Resources and data sources should be organized into subcategories if the number of resources would be difficult to quickly scan for a user. Guides should be separated into subcategories if there are multiple guides which fit into 2 or more distinct groupings. - -### Callouts - -If you start a paragraph with a special arrow-like sigil, it will become a colored callout box. You can't make multi-paragraph callouts. For colorblind users (and for clarity in general), callouts will automatically start with a strong-emphasized word to indicate their function. - -Sigil | Text prefix | Color -------|-------------------|------- -`->` | `**Note**` | blue -`~>` | `**Note**` | yellow -`!>` | `**Warning**` | red - -## Navigation Hierarchy - -Provider docs are organized by category: resources, data sources, and guides. At a minimum, a provider must contain an index (`docs/index.md`) and at least one resource or data source. - -### Typical Structure - -A provider named `example` with a resource and data source for `instance` would have these 3 files: - -``` -docs/ - index.md - data-sources/ - instance.md - resources/ - instance.md -``` - -After publishing this provider version, its page on the Terraform Registry would display a navigation which resembles this hierarchy: - -* example Provider -* Resources - * example_instance -* Data Sources - * example_instance - -### Subcategories - -To group these resources by a service or other dimension, add the optional `subcategory` field to the YAML frontmatter of the resource and data source: - -```markdown ---- -subcategory: "Compute" ---- -``` - -This would change the navigation hierarchy to the following: - -* example Provider -* Compute - * Resources - * example_instance - * Data Sources - * example_instance - -Resources and data sources without a subcategory will be rendered before any subcategories. - -The following subcategories will be rendered at the bottom of the list: - -* Beta -* Deprecated - -### Guides - -Providers can optionally include 1 or more guides. These can assist users in using the provider for certain scenarios. - -``` -docs/ - index.md - guides/ - authenticating.md - data-sources/ - instance.md - resources/ - instance.md -``` - -The title for guides is controlled with the `page_title` attribute in the YAML frontmatter: - -```markdown ---- -page_title: "Authenticating with Example Cloud" ---- -``` - -The `page_title` is used (instead of the filename) for rendering the link to this guide in the navigation: - -* example Provider -* Guides - * Authenticating with Example Cloud -* Resources - * example_instance -* Data Sources - * example_instance - -Guides are always rendered before resources, data sources, and any subcategories. - -If a `page_title` attribute is not found, the title will default to the filename without the extension. - -### Guides Subcategories - -If a provider has many guides, you can use subcategories to group them into separate top-level sections. For example, given the following directory structure: - -``` -docs/ - index.md - guides/ - authenticating-basic.md - authenticating-oauth.md - setup.md - data-sources/ - instance.md - resources/ - instance.md -``` - -Assuming that these three guides have titles similar to their filenames, and the first two include `subcategory: "Authentication"` in their frontmatter, the Terraform Registry would display this navigation structure: - -* example Provider -* Guides - * Initial Setup -* Authentication - * Authenticating with Basic Authentication - * Authenticating with OAuth -* Resources - * example_instance -* Data Sources - * example_instance - -Guides without a subcategory are always rendered before guides with subcategories. Both are always rendered before resources and data sources. - -## Migrating Legacy Providers Docs - -For most provider docs already published to [terraform.io][terraform-io-providers], no changes are required to publish them to the Terraform Registry. - -~> **Important:** The only exceptions are providers which organize resources, data sources, or guides into subcategories. See the [Subcategories](#subcategories) section above for more information. - -If you want to publish docs on the Terraform Registry that are not currently published to terraform.io, take the following steps to migrate to the newer format: - -1. Move the `website/docs/` folder to `docs/` -2. Expand the folder names to match the Terraform Registry's expected format: - * Rename `docs/d/` to `docs/data-sources/` - * Rename `docs/r/` to `docs/resources/` -3. Change file suffixes from `.html.markdown` or `.html.md` to `.md`. - -[terraform-registry]: https://registry.terraform.io -[terraform-registry-providers]: https://registry.terraform.io/browse/providers -[terraform-io-providers]: https://www.terraform.io/docs/providers/ diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/images/archived-tier.png b/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/images/archived-tier.png deleted file mode 100644 index 2849da32..00000000 Binary files a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/images/archived-tier.png and /dev/null differ diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/images/community-tier.png b/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/images/community-tier.png deleted file mode 100644 index fe5d1000..00000000 Binary files a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/images/community-tier.png and /dev/null differ diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/images/github-oauth-permissions.png b/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/images/github-oauth-permissions.png deleted file mode 100644 index 279ac4cf..00000000 Binary files a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/images/github-oauth-permissions.png and /dev/null differ diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/images/official-tier.png b/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/images/official-tier.png deleted file mode 100644 index 0110f8f4..00000000 Binary files a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/images/official-tier.png and /dev/null differ diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/images/publishing.png b/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/images/publishing.png deleted file mode 100644 index df1786b6..00000000 Binary files a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/images/publishing.png and /dev/null differ diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/images/verified-tier.png b/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/images/verified-tier.png deleted file mode 100644 index 201e243f..00000000 Binary files a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/images/verified-tier.png and /dev/null differ diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/index.html.md deleted file mode 100644 index 411f44f2..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/index.html.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -layout: "registry" -page_title: "Terraform Registry - Providers Overview" -description: |- - Overview of providers in the Terraform Registry ---- - -# Overview - -Providers are how Terraform integrates with any upstream API. - -The Terraform Registry is the main source for publicly available Terraform providers. It offers a browsable and searchable interface for finding providers, and makes it possible for Terraform CLI to automatically install any of the providers it hosts. - -If you want Terraform to support a new infrastructure service, you can create your own provider using Terraform's Go SDK. Once you've developed a provider, you can use the Registry to share it with the rest of the community. - -## Using Providers From the Registry - -The Registry is directly integrated with Terraform. To use any provider from the Registry, all you need to do is require it within your Terraform configuration; Terraform can then automatically install that provider when initializing a working directory, and your configuration can take advantage of any resources implemented by that provider. - -For more information, see: - -- [Configuration Language: Provider Requirements](/docs/configuration/provider-requirements.html) - -## Provider Tiers & Namespaces - -Terraform providers are published and maintained by a variety of sources, including HashiCorp, HashiCorp Technology Partners, and the Terraform community. The Registry uses tiers and badges to denote the source of a provider. Additionally, namespaces are used to help users identify the organization or publisher responsible for the integration, as shown in the table below. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TierDescriptionNamespace
Official providers are owned and maintained by HashiCorp hashicorp
Verified providers are owned and maintained by third-party technology partners. Providers in this tier indicate HashiCorp has verified the authenticity of the Provider’s publisher, and that the partner is a member of the HashiCorp Technology Partner Program.Third-party organization, e.g. mongodb/mongodbatlas
Community providers are published to the Terraform Registry by individual maintainers, groups of maintainers, or other members of the Terraform community.
Maintainer’s individual or organization account, e.g. DeviaVir/gsuite
Archived Providers are Official or Verified Providers that are no longer maintained by HashiCorp or the community. This may occur if an API is deprecated or interest was low.hashicorp or third-party
-

- -## Verified Provider Development Program - -If your organization is interested in joining our Provider Development Program (which sets the standards for publishing providers and modules with a `Verified` badge), please take a look at our [Program Details](/guides/terraform-provider-development-program.html) for further information. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/os-arch.html.md b/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/os-arch.html.md deleted file mode 100644 index 930e9902..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/os-arch.html.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -layout: "registry" -page_title: "Recommended Provider Binary Operating Systems and Architectures - Terraform Registry" -sidebar_current: "docs-registry-provider-os-arch" -description: |- - Recommended Provider Binary Operating Systems and Architectures ---- - -# Recommended Provider Binary Operating Systems and Architectures - -We recommend the following operating system / architecture combinations for compiled binaries available in the registry (this list is already satisfied by our [recommended **.goreleaser.yml** configuration file](https://github.com/hashicorp/terraform-provider-scaffolding/blob/master/.goreleaser.yml)): - -* Darwin / AMD64 -* Linux / AMD64 (this is **required** for usage in Terraform Cloud, see below) -* Linux / ARMv8 (sometimes referred to as AArch64 or ARM64) -* Linux / ARMv6 -* Windows / AMD64 - -We also recommend shipping binaries for the following combinations, but we typically do not prioritize fixes for these: - -* Linux / 386 -* Windows / 386 -* FreeBSD / 386 -* FreeBSD / AMD64 - -## Terraform Cloud Compatibility - -To ensure your provider can run in Terraform Cloud, please include a Linux / AMD64 binary. This binary should also not have CGO enabled and should not depend on command line execution of any external tools or binaries. We cannot guaruntee availibility of any package/library/binary within the Terraform Cloud images. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/publishing.html.md b/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/publishing.html.md deleted file mode 100644 index aeec49b2..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/registry/providers/publishing.html.md +++ /dev/null @@ -1,123 +0,0 @@ ---- -layout: "registry" -page_title: "Terraform Registry - Publishing Providers" -sidebar_current: "docs-registry-provider-publishing" -description: |- - Publishing Providers to the Terraform Registry ---- - -# Publishing Providers - -Anyone can publish and share a provider by signing into the Registry using their GitHub account and following a few easy steps. - -This page describes how to prepare a [Terraform Provider](/docs/plugins/provider.html) for publishing, and how to publish a prepared provider using the Registry's interface. - -## Preparing your Provider - -### Writing a Provider - -Providers published to the Terraform Registry are written and built in the same way as other Terraform providers. A variety of resources are available to help our contributors build a quality integration: - -- The [Call APIs with Terraform Providers](https://learn.hashicorp.com/collections/terraform/providers?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn -- [How to build a provider – Video](https://www.youtube.com/watch?v=2BvpqmFpchI) -- [Sample provider developed by a HashiCorp partner](https://blog.container-solutions.com/write-terraform-provider-part-1) -- Example providers for reference: - - [AWS](https://github.com/terraform-providers/terraform-provider-aws) - - [AzureRM](https://github.com/terraform-providers/terraform-provider-azurerm) -- [Contributing to Terraform guidelines](/docs/extend/community/contributing.html) - -~> **Important:** In order to be detected by the Terraform Registry, all provider repositories on GitHub must match the pattern `terraform-provider-{NAME}`, and the repository must be public. - -### Documenting your Provider - -Your provider should contain an overview document (index.md), as well as a doc for each resource and data-source. See [Documenting Providers](./docs.html) for details about how to ensure your provider documentation renders properly on the Terraform Registry. - --> **Note:** In order to test how documents will render in the Terraform Registry, you can use the [Terraform Registry Doc Preview Tool](https://registry.terraform.io/tools/doc-preview). - -### Creating a GitHub Release - -Publishing a provider requires at least one version be available on GitHub Releases. The tag must be a valid [Semantic Version](https://semver.org/) preceded with a `v` (for example, `v1.2.3`). - -Terraform CLI and the Terraform Registry follow the Semantic Versioning specification when detecting a valid version, sorting versions, solving version constraints, and choosing the latest version. Prerelease versions are supported (available if explicitly defined but not chosen automatically) with a hyphen (-) delimiter, such as `v1.2.3-pre`. - -We have a list of [recommend OS / architecture combinations](/docs/registry/providers/os-arch.html) for which we suggest most providers create binaries. - -~> **Important:** Avoid modifying or replacing an already-released version of a provider, as this will cause checksum errors for users when attempting to download the plugin. Instead, if changes are necessary, please release as a new version. - -#### GitHub Actions (Preferred) - -[GitHub Actions](https://docs.github.com/en/actions) allow you to execute workflows when events on your repository occur. You can use this to publish provider releases to the Terraform Registry whenever a new version tag is created on your repository. - -To use GitHub Actions to publish new provider releases to the Terraform Registry: - -1. Create and export a signing key that you plan on using to sign your provider releases. See [Preparing and Adding a Signing Key](#preparing-and-adding-a-signing-key) for more information. -1. Copy the [GoReleaser configuration from the terraform-provider-scaffolding repository](https://github.com/hashicorp/terraform-provider-scaffolding/blob/master/.goreleaser.yml) to the root of your repository. -1. Copy the [GitHub Actions workflow from the terraform-provider-scaffolding repository](https://github.com/hashicorp/terraform-provider-scaffolding/blob/master/.github/workflows/release.yml) to `.github/workflows/release.yml` in your repository. -1. Go to *Settings > Secrets* in your repository, and add the following secrets: - * `GPG_PRIVATE_KEY` - Your ASCII-armored GPG private key. You can export this with `gpg --armor --export-secret-keys [key ID or email]`. - * `PASSPHRASE` - The passphrase for your GPG private key. -1. Push a new valid version tag (e.g. `v1.2.3`) to test that the GitHub Actions releaser is working. - -Once a release is created, you can move on to [Publishing to the Registry](#publishing-to-the-registry). - -#### Using GoReleaser locally - -GoReleaser is a tool for building Go projects for multiple platforms, creating a checksums file, and signing the release. It can also upload your release to GitHub Releases. - -1. Install [GoReleaser](https://goreleaser.com) using the [installation instructions](https://goreleaser.com/install/). -1. Copy the [.goreleaser.yml file](https://github.com/hashicorp/terraform-provider-scaffolding/blob/master/.goreleaser.yml) from the [hashicorp/terraform-provider-scaffolding](https://github.com/hashicorp/terraform-provider-scaffolding) repository. -1. Cache the password for your GPG private key with `gpg --armor --detach-sign` (see note below). -1. Set your `GITHUB_TOKEN` to a [Personal Access Token](https://github.com/settings/tokens/new?scopes=public_repo) that has the **public_repo** scope. -1. Tag your version with `git tag v1.2.3`. -1. Build, sign, and upload your release with `goreleaser release --rm-dist`. - --> GoReleaser does not support signing binaries with a GPG key that requires a passphrase. Some systems may cache your GPG passphrase for a few minutes. If you are unable to cache the passphrase for GoReleaser, please use the manual release preparation process below, or remove the signature step from GoReleaser and sign it prior to moving the GitHub release from draft to published. - -#### Manually Preparing a Release - -If for some reason you're not able to use GoReleaser to build, sign, and upload your release, you can create the required assets by following these steps, or encode them into a Makefile or shell script. - -The release must meet the following criteria: - -* There are 1 or more zip files containing the built provider binary for a single architecture - * The binary name is `terraform-provider-{NAME}_v{VERSION}` - * The archive name is `terraform-provider-{NAME}_{VERSION}_{OS}_{ARCH}.zip` -* There is a `terraform-provider-{NAME}_{VERSION}_SHA256SUMS` file, which contains a sha256 sum for each zip file in the release. - * `shasum -a 256 *.zip > terraform-provider-{NAME}_{VERSION}_SHA256SUMS` -* There is a `terraform-provider-{NAME}_{VERSION}_SHA256SUMS.sig` file, which is a valid GPG signature of the `terraform-provider-{NAME}_{VERSION}_SHA256SUMS` file using the keypair. - * `gpg --detach-sign terraform-provider-{NAME}_{VERSION}_SHA256SUMS` -* Release is finalized (not a private draft). - -## Publishing to the Registry - -### Signing in - -Before publishing a provider, you must first sign in to the Terraform Registry with a GitHub account (see [Signing into the Registry](/docs/registry/index.html#creating-an-account)). The GitHub account used must have the following permission scopes on the provider repository you’d like to publish. Permissions can be verified by going to your [GitHub Settings](https://github.com/settings/connections/applications/) and selecting the Terraform Registry Application under Authorized OAuth Apps. - -![screenshot: terraform registry github oauth required permissions](./images/github-oauth-permissions.png) - -### Preparing and Adding a Signing Key - -All provider releases are required to be signed, thus you must provide HashiCorp with the public key for the GPG keypair that you will be signing releases with. The Terraform Registry will validate that the release is signed with this key when publishing each version, and Terraform will verify this during `terraform init`. - -- Generate a GPG key to be used when signing releases (See [GitHub's detailed instructions](https://docs.github.com/en/github/authenticating-to-github/generating-a-new-gpg-key) for help with this step, but you do not need to add the key to GitHub) -- Export your public key in ASCII-armor format using the following command, substituting the GPG key ID created in the step above: - -```console -$ gpg --armor --export "{Key ID or email address}" -``` - -The ASCII-armored public key to the Terraform Registry by going to [User Settings > Signing Keys](https://registry.terraform.io/settings/gpg-keys). You can add keys for your personal namespace, or any organization which you are an admin of. - - -### Publishing Your Provider - -In the top-right navigation, select [Publish > Provider](https://registry.terraform.io/publish/provider) to begin the publishing process. Follow the prompts to select the organization and repository you would like to publish. - -#### Terms of Use - -Anything published to the Terraform Registry is subject to our terms of use. A copy of the terms are available for viewing at https://registry.terraform.io/terms - -### Support - -If you experience issues publishing your provider to the Terraform Registry, please contact us at [terraform-registry@hashicorp.com](mailto:terraform-registry@hashicorp.com). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/state/environments.html.md b/vendor/github.com/hashicorp/terraform/website/docs/state/environments.html.md deleted file mode 100644 index dfc8f761..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/state/environments.html.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -layout: "docs" -page_title: "State: Environments" -sidebar_current: "docs-state-env" -description: |- - Legacy terminology for "Workspaces". ---- - -# State Environments - -The term _state environment_, or just _environment_, was used within the -Terraform 0.9 releases to refer to the idea of having multiple distinct, -named states associated with a single configuration directory. - -After this concept was implemented, we received feedback that this terminology -caused confusion due to other uses of the word "environment", both within -Terraform itself and within organizations using Terraform. - -As of 0.10, the preferred term is "workspace". For more information on -workspaces, see [the main Workspaces page](/docs/state/workspaces.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/state/import.html.md b/vendor/github.com/hashicorp/terraform/website/docs/state/import.html.md deleted file mode 100644 index 166614b6..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/state/import.html.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -layout: "docs" -page_title: "State: Import Existing Resources" -sidebar_current: "docs-state-import" -description: |- - Terraform stores state which caches the known state of the world the last time Terraform ran. ---- - -# Import Existing Resources - -Terraform is able to import existing infrastructure. This allows you take -resources you've created by some other means and bring it under Terraform management. - -To learn more about this, please visit the -[pages dedicated to import](/docs/import/index.html). diff --git a/vendor/github.com/hashicorp/terraform/website/docs/state/index.html.md b/vendor/github.com/hashicorp/terraform/website/docs/state/index.html.md deleted file mode 100644 index cfc70c88..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/state/index.html.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -layout: "docs" -page_title: "State" -sidebar_current: "docs-state" -description: |- - Terraform must store state about your managed infrastructure and configuration. This state is used by Terraform to map real world resources to your configuration, keep track of metadata, and to improve performance for large infrastructures. ---- - -# State - -Terraform must store state about your managed infrastructure and -configuration. This state is used by Terraform to map real world -resources to your configuration, keep track of metadata, and to improve -performance for large infrastructures. - -This state is stored by default in a local file named "terraform.tfstate", -but it can also be stored remotely, which works better in a team environment. - -Terraform uses this local state to create plans and make changes to your -infrastructure. Prior to any operation, Terraform does a -[refresh](/docs/commands/refresh.html) to update the state with the -real infrastructure. - -The primary purpose of Terraform state is to store bindings between objects in -a remote system and resource instances declared in your configuration. -When Terraform creates a remote object in response to a change of configuration, -it will record the identity of that remote object against a particular -resource instance, and then potentially update or delete that object in -response to future configuration changes. - -For more information on why Terraform requires state and why Terraform cannot -function without state, please see the page [state purpose](/docs/state/purpose.html). - -## Inspection and Modification - -While the format of the state files are just JSON, direct file editing -of the state is discouraged. Terraform provides the -[terraform state](/docs/commands/state/index.html) command to perform -basic modifications of the state using the CLI. - -The CLI usage and output of the state commands is structured to be -friendly for Unix tools such as grep, awk, etc. Additionally, the CLI -insulates users from any format changes within the state itself. The Terraform -project will keep the CLI working while the state format underneath it may -shift. - -Terraform expects a one-to-one mapping between configured resource instances -and remote objects. Normally that is guaranteed by Terraform being the one -to create each object and record its identity in the state, or to destroy -an object and then remove the binding for it. - -If you add or remove bindings in the state by other means, such as by importing -externally-created objects with `terraform import`, or by asking Terraform to -"forget" an existing object with `terraform state rm`, you'll then need to -ensure for yourself that this one-to-one rule is followed, such as by manually -deleting an object that you asked Terraform to "forget", or by re-importing it -to bind it to some other resource instance. - -## Format - -State snapshots are stored in JSON format and new Terraform versions are -generally backward compatible with state snapshots produced by earlier versions. -However, the state format is subject to change in new Terraform versions, so -if you build software that parses or modifies it directly you should expect -to perform ongoing maintenence of that software as the state format evolves -in new versions. - -Alternatively, there are several integration points which produce JSON output -that is specifically intended for consumption by external software: - -* [The `terraform output` command](/docs/commands/output.html) -has a `-json` option, for obtaining either the full set of root module output -values or a specific named output value from the latest state snapshot. -* [The `terraform show` command](/docs/commands/show.html) has a `-json` -option for inspecting the latest state snapshot in full, and also for -inspecting saved plan files which include a copy of the prior state at the -time the plan was made. - -A typical way to use these in situations where Terraform is running in -automation is to run them immediately after a successful `terraform apply` -to obtain a representation of the latest state snapshot, and then store that -result as an artifact associated with the automated run so that other software -can potentially consume it without needing to run Terraform itself. diff --git a/vendor/github.com/hashicorp/terraform/website/docs/state/remote.html.md b/vendor/github.com/hashicorp/terraform/website/docs/state/remote.html.md deleted file mode 100644 index 8e4039b6..00000000 --- a/vendor/github.com/hashicorp/terraform/website/docs/state/remote.html.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -layout: "docs" -page_title: "State: Remote Storage" -sidebar_current: "docs-state-remote" -description: |- - Terraform can store the state remotely, making it easier to version and work with in a team. ---- - -# Remote State - -By default, Terraform stores state locally in a file named `terraform.tfstate`. -When working with Terraform in a team, use of a local file makes Terraform -usage complicated because each user must make sure they always have the latest -state data before running Terraform and make sure that nobody else runs -Terraform at the same time. - -With _remote_ state, Terraform writes the state data to a remote data store, -which can then be shared between all members of a team. Terraform supports -storing state in [Terraform Cloud](https://www.hashicorp.com/products/terraform/), -[HashiCorp Consul](https://www.consul.io/), Amazon S3, Azure Blob Storage, Google Cloud Storage, Alibaba Cloud OSS, and more. - -Remote state is a feature of [backends](/docs/backends). Configuring and -using remote backends is easy and you can get started with remote state -quickly. If you then want to migrate back to using local state, backends make -that easy as well. - -## Delegation and Teamwork - -Remote state gives you more than just easier version control and -safer storage. It also allows you to delegate the -[outputs](/docs/configuration/outputs.html) to other teams. This allows -your infrastructure to be more easily broken down into components that -multiple teams can access. - -Put another way, remote state also allows teams to share infrastructure -resources in a read-only way without relying on any additional configuration -store. - -For example, a core infrastructure team can handle building the core -machines, networking, etc. and can expose some information to other -teams to run their own infrastructure. As a more specific example with AWS: -you can expose things such as VPC IDs, subnets, NAT instance IDs, etc. through -remote state and have other Terraform states consume that. - -For example usage, see -[the `terraform_remote_state` data source](/docs/providers/terraform/d/remote_state.html). - -While remote state is a convenient, built-in mechanism for sharing data -between configurations, it is also possible to use more general stores to -pass settings both to other configurations and to other consumers. For example, -if your environment has [HashiCorp Consul](https://www.consul.io/) then you -can have one Terraform configuration that writes to Consul using -[`consul_key_prefix`](/docs/providers/consul/r/key_prefix.html) and then -another that consumes those values using -[the `consul_keys` data source](/docs/providers/consul/d/keys.html). - -## Locking and Teamwork - -For fully-featured remote backends, Terraform can also use -[state locking](/docs/state/locking.html) to prevent concurrent runs of -Terraform against the same state. - -[Terraform Cloud by HashiCorp](https://www.hashicorp.com/products/terraform/) -is a commercial offering that supports an even stronger locking concept that -can also detect attempts to create a new plan when an existing plan is already -awaiting approval, by queuing Terraform operations in a central location. -This allows teams to more easily coordinate and communicate about changes to -infrastructure. diff --git a/vendor/github.com/hashicorp/terraform/website/guides/core-workflow.html.md b/vendor/github.com/hashicorp/terraform/website/guides/core-workflow.html.md index c7f50a6f..eb3112a1 100644 --- a/vendor/github.com/hashicorp/terraform/website/guides/core-workflow.html.md +++ b/vendor/github.com/hashicorp/terraform/website/guides/core-workflow.html.md @@ -1,5 +1,5 @@ --- -layout: "guides" +layout: "intro" page_title: "The Core Terraform Workflow - Guides" sidebar_current: "guides-core-workflow" description: |- @@ -76,7 +76,7 @@ it's time to commit your work and review the final plan. $ git add main.tf $ git commit -m 'Managing infrastructure as code!' -[master (root-commit) f735520] Managing infrastructure as code! +[main (root-commit) f735520] Managing infrastructure as code! 1 file changed, 1 insertion(+) ``` @@ -113,7 +113,7 @@ location for safekeeping. ```sh $ git remote add origin https://github.com/*user*/*repo*.git -$ git push origin master +$ git push origin main ``` This core workflow is a loop; the next time you want to make changes, you start @@ -235,7 +235,7 @@ for a better experience at each step. Terraform Cloud provides a centralized and secure location for storing input variables and state while also bringing back a tight feedback loop for speculative plans for config authors. Terraform configuration interacts with -Terraform Cloud via the ["remote" backend](/docs/backends/types/remote.html). +Terraform Cloud via the ["remote" backend](/docs/language/settings/backends/remote.html). ``` terraform { diff --git a/vendor/github.com/hashicorp/terraform/website/guides/index.html.md b/vendor/github.com/hashicorp/terraform/website/guides/index.html.md index 8eb70738..987d3853 100644 --- a/vendor/github.com/hashicorp/terraform/website/guides/index.html.md +++ b/vendor/github.com/hashicorp/terraform/website/guides/index.html.md @@ -1,5 +1,5 @@ --- -layout: "guides" +layout: "intro" page_title: "Guides" sidebar_current: "guides-home" description: |- diff --git a/vendor/github.com/hashicorp/terraform/website/guides/terraform-provider-development-program.html.md b/vendor/github.com/hashicorp/terraform/website/guides/terraform-provider-development-program.html.md index 9acba17a..59d5ffc6 100644 --- a/vendor/github.com/hashicorp/terraform/website/guides/terraform-provider-development-program.html.md +++ b/vendor/github.com/hashicorp/terraform/website/guides/terraform-provider-development-program.html.md @@ -13,7 +13,7 @@ The Verified badge helps users easily identify and discover integrations develop ![Verified Provider Card](/assets/images/docs/verified-card.png) --> **Building your own provider?** If you're building your own provider and aren't interested in having HashiCorp officially verify and regularly monitor your provider, please refer to the [Writing Custom Providers guide](https://www.terraform.io/docs/extend/writing-custom-providers.html) and the [Extending Terraform](https://www.terraform.io/docs/extend/index.html) section. +-> **Building your own provider?** If you're building your own provider and aren't interested in having HashiCorp officially verify and regularly monitor your provider, please refer to the [Call APIs with Terraform Providers](https://learn.hashicorp.com/collections/terraform/providers?utm_source=WEBSITEhttps://www.terraform.io/docs/extend/writing-custom-providers.htmlutm_medium=WEB_IOhttps://www.terraform.io/docs/extend/writing-custom-providers.htmlutm_offer=ARTICLE_PAGEhttps://www.terraform.io/docs/extend/writing-custom-providers.htmlutm_content=DOCS) collection on HashiCorp Learn and the [Extending Terraform](https://www.terraform.io/docs/extend/index.html) section of the documentation. ## What is a Terraform Provider? @@ -66,7 +66,7 @@ The provider development process is divided into five steps below. By following ![Provider Development Process](/assets/images/docs/program-steps.png) 1. **Apply**: Initial contact between vendor and HashiCorp -2. **Prepare**: Follow documentation while developing the provider +2. **Prepare**: Follow documentation while developing the provider 3. **Verify**: Share public GPG key with HashiCorp 4. **Publish**: Release the provider on the Registry 5. **Support**: Ongoing maintenance and support of the provider by the vendor. @@ -112,7 +112,7 @@ We’ve found the provider development process to be fairly straightforward and ### 3. Verify -At this stage, it is expected that the provider is fully developed, all tests and documentation are in place, and your provider is ready for publishing. In this step, HashiCorp will verify the source and authenticity of the namespace being used to publish the provider by signing your GPG key with a trust signature. +At this stage, it is expected that the provider is fully developed, all tests and documentation are in place, and your provider is ready for publishing. In this step, HashiCorp will verify the source and authenticity of the namespace being used to publish the provider by signing your GPG key with a trust signature. -> **Important:** This step requires that you have signed and accepted our Technology Partner Agreement. If you have not received this, please see step #1 above. @@ -128,7 +128,7 @@ $ gpg --armor --export "{Key ID or email address}" Once the verification step is complete please follow the steps on [Publishing a Provider](https://www.terraform.io/docs/registry/providers/publishing.html). This step does not require additional involvement from HashiCorp as publishing is a fully self-service process in the [Terraform Registry](https://registry.terraform.io). -Once completed, your provider should be visible in the Terraform Registry and usable in Terraform. Please confirm that everything looks good, and that documentation is rendering properly. +Once completed, your provider should be visible in the Terraform Registry and usable in Terraform. Please confirm that everything looks good, and that documentation is rendering properly. ### 5. Maintain & Support diff --git a/vendor/github.com/hashicorp/terraform/website/intro/examples/aws.html.markdown b/vendor/github.com/hashicorp/terraform/website/intro/examples/aws.html.markdown deleted file mode 100644 index 0a9449e8..00000000 --- a/vendor/github.com/hashicorp/terraform/website/intro/examples/aws.html.markdown +++ /dev/null @@ -1,28 +0,0 @@ ---- -layout: "intro" -page_title: "Two-Tier AWS Architecture" -sidebar_current: "examples-aws" -description: |- - This provides a template for running a simple two-tier architecture on Amazon Web services. The premise is that you have stateless app servers running behind an ELB serving traffic. ---- - -# Two-Tier AWS Architecture - -[**Example Source Code**](https://github.com/terraform-providers/terraform-provider-aws/tree/master/examples/two-tier) - -This provides a template for running a simple two-tier architecture on Amazon -Web Services. The premise is that you have stateless app servers running behind -an ELB serving traffic. - -To simplify the example, it intentionally ignores deploying and -getting your application onto the servers. However, you could do so either via -[provisioners](/docs/provisioners/index.html) and a configuration -management tool, or by pre-baking configured AMIs with -[Packer](https://www.packer.io). - -After you run `terraform apply` on this configuration, it will -automatically output the DNS address of the ELB. After your instance -registers, this should respond with the default Nginx web page. - -As with all the examples, just copy and paste the example and run -`terraform apply` to see it work. diff --git a/vendor/github.com/hashicorp/terraform/website/intro/examples/consul.html.markdown b/vendor/github.com/hashicorp/terraform/website/intro/examples/consul.html.markdown deleted file mode 100644 index 3f8223ff..00000000 --- a/vendor/github.com/hashicorp/terraform/website/intro/examples/consul.html.markdown +++ /dev/null @@ -1,57 +0,0 @@ ---- -layout: "intro" -page_title: "Consul Example" -sidebar_current: "examples-consul" -description: |- - Consul is a tool for service discovery, configuration and orchestration. The Key/Value store it provides is often used to store application configuration and information about the infrastructure necessary to process requests. ---- - -# Consul Example - -[**Example Source Code**](https://github.com/terraform-providers/terraform-provider-consul/tree/master/examples/kv) - -[Consul](https://www.consul.io) is a tool for service discovery, configuration -and orchestration. The Key/Value store it provides is often used to store -application configuration and information about the infrastructure necessary -to process requests. - -Terraform provides a [Consul provider](/docs/providers/consul/index.html) which -can be used to interface with Consul from inside a Terraform configuration. - -For our example, we use the [Consul demo cluster](https://demo.consul.io/) -to both read configuration and store information about a newly created EC2 instance. -The size of the EC2 instance will be determined by the `tf_test/size` key in Consul, -and will default to `m1.small` if that key does not exist. Once the instance is created -the `tf_test/id` and `tf_test/public_dns` keys will be set with the computed -values for the instance. - -Before we run the example, use the [Web UI](https://demo.consul.io/ui/dc1/kv/) -to set the `tf_test/size` key to `t1.micro`. Once that is done, -copy the configuration into a configuration file (`consul.tf` works fine). -Either provide the AWS credentials as a default value in the configuration -or invoke `apply` with the appropriate variables set. - -Once the `apply` has completed, we can see the keys in Consul by -visiting the [Web UI](https://demo.consul.io/ui/dc1/kv/). We can see -that the `tf_test/id` and `tf_test/public_dns` values have been -set. - -You can now [tear down the infrastructure](https://learn.hashicorp.com/tutorials/terraform/aws-destroy?in=terraform/aws-get-started&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS). -Because we set the `delete` property of two of the Consul keys, Terraform -will clean up those keys on destroy. We can verify this by using -the Web UI. - -This example has shown that Consul can be used with Terraform both to read -existing values and to store generated results. - -Inputs like AMI name, security groups, Puppet roles, bootstrap scripts, -etc can all be loaded from Consul. This allows the specifics of an -infrastructure to be decoupled from its overall architecture. This enables -details to be changed without updating the Terraform configuration. - -Outputs from Terraform can also be easily stored in Consul. One powerful -feature this enables is using Consul for inventory management. If an -application relies on ELB for routing, Terraform can update the application's -configuration directly by setting the ELB address into Consul. Any resource -attribute can be stored in Consul, allowing an operator to capture anything -useful. diff --git a/vendor/github.com/hashicorp/terraform/website/intro/examples/count.markdown b/vendor/github.com/hashicorp/terraform/website/intro/examples/count.markdown deleted file mode 100644 index e6064924..00000000 --- a/vendor/github.com/hashicorp/terraform/website/intro/examples/count.markdown +++ /dev/null @@ -1,20 +0,0 @@ ---- -layout: "intro" -page_title: "Count" -sidebar_current: "examples-count" -description: |- - The count parameter on resources can simplify configurations and let you scale resources by simply incrementing a number. ---- - -# Count Example - -[**Example Source Code**](https://github.com/terraform-providers/terraform-provider-aws/tree/master/examples/count) - -The `count` parameter on resources can simplify configurations -and let you scale resources by simply incrementing a number. - -Additionally, variables can be used to expand a list of resources -for use elsewhere. - -As with all the examples, just copy and paste the example and run -`terraform apply` to see it work. diff --git a/vendor/github.com/hashicorp/terraform/website/intro/examples/cross-provider.markdown b/vendor/github.com/hashicorp/terraform/website/intro/examples/cross-provider.markdown deleted file mode 100644 index 381d4c49..00000000 --- a/vendor/github.com/hashicorp/terraform/website/intro/examples/cross-provider.markdown +++ /dev/null @@ -1,21 +0,0 @@ ---- -layout: "intro" -page_title: "Cross Provider" -sidebar_current: "examples-cross-provider" -description: |- - An example of the cross-provider capabilities of Terraform. ---- - -# Cross Provider Example - -[**Example Source Code**](https://github.com/hashicorp/terraform/tree/master/examples/cross-provider) - -This is a simple example of the cross-provider capabilities of -Terraform. - -This creates a Heroku application and points a DNS -CNAME record at the result via DNSimple. A `host` query to the outputted -hostname should reveal the correct DNS configuration. - -As with all the examples, just copy and paste the example and run -`terraform apply` to see it work. diff --git a/vendor/github.com/hashicorp/terraform/website/intro/examples/index.html.markdown b/vendor/github.com/hashicorp/terraform/website/intro/examples/index.html.markdown deleted file mode 100644 index 8a0318a9..00000000 --- a/vendor/github.com/hashicorp/terraform/website/intro/examples/index.html.markdown +++ /dev/null @@ -1,60 +0,0 @@ ---- -layout: "intro" -page_title: "Example Configurations" -sidebar_current: "examples" -description: |- - These examples are designed to help you understand some of the ways Terraform can be used. ---- - -# Example Configurations - -The examples in this section illustrate some -of the ways Terraform can be used. - -All examples are ready to run as-is. Terraform will -ask for input of things such as variables and API keys. If you want to -continue using the example, you should save those parameters in a -"terraform.tfvars" file or in a `provider` config block. - -~> **Warning!** The examples use real providers that launch _real_ resources. -That means they can cost money to experiment with. To avoid unexpected charges, -be sure to understand the price of resources before launching them, and verify -any unneeded resources are cleaned up afterwards. - -Experimenting in this way can help you learn how the Terraform lifecycle -works, as well as how to repeatedly create and destroy infrastructure. - -If you're completely new to Terraform, we recommend reading the -[Terraform: Get Started](https://learn.hashicorp.com/collections/terraform/aws-get-started?utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) collection on HashiCorp Learn before diving into -the examples. However, due to the intuitive configuration Terraform -uses it isn't required. - -## Examples - -Our examples are distributed across several repos. [This README file in the Terraform repo has links to all of them.](https://github.com/hashicorp/terraform/tree/master/examples) - -To use these examples, Terraform must first be installed on your machine. -You can install Terraform from the [downloads page](/downloads.html). -Once installed, you can download, view, and run the examples. - -To use an example, clone the repository that contains it and navigate to its directory. For example, to try the AWS two-tier architecture example: - -``` -git clone https://github.com/terraform-providers/terraform-provider-aws.git -cd terraform-provider-aws/examples/two-tier -``` - -You can then use your preferred code editor to browse and read the configurations. -To try out an example, run Terraform's init and apply commands while in the example's directory: - -``` -$ terraform init -... -$ terraform apply -... -``` - -Terraform will interactively ask for variable input and potentially -provider configuration, and will start executing. - -When you're done with the example, run `terraform destroy` to clean up. diff --git a/vendor/github.com/hashicorp/terraform/website/intro/use-cases.html.markdown b/vendor/github.com/hashicorp/terraform/website/intro/use-cases.html.markdown index 0c545cf5..e2d3f26f 100644 --- a/vendor/github.com/hashicorp/terraform/website/intro/use-cases.html.markdown +++ b/vendor/github.com/hashicorp/terraform/website/intro/use-cases.html.markdown @@ -91,7 +91,7 @@ This configuration can then be used by Terraform to automatically setup and modi settings by interfacing with the control layer. This allows configuration to be versioned and changes to be automated. As an example, [AWS VPC](https://aws.amazon.com/vpc/) is one of the most commonly used SDN implementations, and [can be configured by -Terraform](/docs/providers/aws/r/vpc.html). +Terraform](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc). ## Resource Schedulers diff --git a/vendor/github.com/hashicorp/terraform/website/intro/vs/chef-puppet.html.markdown b/vendor/github.com/hashicorp/terraform/website/intro/vs/chef-puppet.html.markdown index 9e5a820f..dd2c5cb4 100644 --- a/vendor/github.com/hashicorp/terraform/website/intro/vs/chef-puppet.html.markdown +++ b/vendor/github.com/hashicorp/terraform/website/intro/vs/chef-puppet.html.markdown @@ -13,11 +13,13 @@ that already exists. Terraform is not a configuration management tool, and it allows existing tooling to focus on their strengths: bootstrapping and initializing resources. -Using provisioners, Terraform enables any configuration management tool -to be used to setup a resource once it has been created. Terraform -focuses on the higher-level abstraction of the datacenter and associated -services, without sacrificing the ability to use configuration management -tools to do what they do best. It also embraces the same codification that -is responsible for the success of those tools, making entire infrastructure -deployments easy and reliable. +Terraform focuses on the higher-level abstraction of the datacenter and +associated services, while allowing you to use configuration management +tools on individual systems. It also aims to bring the same benefits of +codification of your system configuration to infrastructure management. + +If you are using traditional configuration management within your compute +instances, you can use Terraform to configure bootstrapping software like +cloud-init to activate your configuration management software on first +system boot. diff --git a/vendor/github.com/hashicorp/terraform/website/layouts/backend-types.erb b/vendor/github.com/hashicorp/terraform/website/layouts/backend-types.erb deleted file mode 100644 index 14dacac6..00000000 --- a/vendor/github.com/hashicorp/terraform/website/layouts/backend-types.erb +++ /dev/null @@ -1,88 +0,0 @@ -<% wrap_layout :inner do %> - <% content_for :sidebar do %> -

Terraform CLI

- - - - <%= partial("layouts/otherdocs", :locals => { :skip => "Terraform CLI" }) %> - <% end %> - - <%= yield %> -<% end %> diff --git a/vendor/github.com/hashicorp/terraform/website/layouts/commands-providers.erb b/vendor/github.com/hashicorp/terraform/website/layouts/commands-providers.erb deleted file mode 100644 index a0587364..00000000 --- a/vendor/github.com/hashicorp/terraform/website/layouts/commands-providers.erb +++ /dev/null @@ -1,35 +0,0 @@ -<% wrap_layout :inner do %> - <% content_for :sidebar do %> -

Terraform CLI

- - - - <%= partial("layouts/otherdocs", :locals => { :skip => "Terraform CLI" }) %> - - <% end %> - - <%= yield %> -<% end %> diff --git a/vendor/github.com/hashicorp/terraform/website/layouts/commands-state.erb b/vendor/github.com/hashicorp/terraform/website/layouts/commands-state.erb deleted file mode 100644 index f5a98d32..00000000 --- a/vendor/github.com/hashicorp/terraform/website/layouts/commands-state.erb +++ /dev/null @@ -1,57 +0,0 @@ -<% wrap_layout :inner do %> - <% content_for :sidebar do %> -

Terraform CLI

- - - - <%= partial("layouts/otherdocs", :locals => { :skip => "Terraform CLI" }) %> - - <% end %> - - <%= yield %> -<% end %> diff --git a/vendor/github.com/hashicorp/terraform/website/layouts/commands-workspace.erb b/vendor/github.com/hashicorp/terraform/website/layouts/commands-workspace.erb deleted file mode 100644 index 624026cb..00000000 --- a/vendor/github.com/hashicorp/terraform/website/layouts/commands-workspace.erb +++ /dev/null @@ -1,45 +0,0 @@ -<% wrap_layout :inner do %> - <% content_for :sidebar do %> -

Terraform CLI

- - - - <%= partial("layouts/otherdocs", :locals => { :skip => "Terraform CLI" }) %> - - <% end %> - - <%= yield %> -<% end %> diff --git a/vendor/github.com/hashicorp/terraform/website/layouts/docs.erb b/vendor/github.com/hashicorp/terraform/website/layouts/docs.erb index 3f5d733b..ce0570f6 100644 --- a/vendor/github.com/hashicorp/terraform/website/layouts/docs.erb +++ b/vendor/github.com/hashicorp/terraform/website/layouts/docs.erb @@ -1,541 +1,572 @@ <% wrap_layout :inner do %> <% content_for :sidebar do %> -

Terraform CLI

+

Terraform CLI